Skip to content

Commit 1e33c01

Browse files
mohammadfawazbader
authored andcommitted
[SYCL][FPGA] Implementing __builtin_intel_fpga_mem() (#1033)
This patch creates and implements the __builtin_intel_fpga_mem builtin for FPGA SYCL device. The builtin is used to indicate the characteristics of the load-store unit (LSU) to be used when de-referencing the pointer. The builtin accepts 3 arguments: 1. A pointer to a first class lvalue or to an rvalue. 2. A integer bitmask that encodes various LSU parameters. These will be added for each language in separate libraries. 3. An integer value indicating the size of the LSU cache. The effect of this is observable only through the FPGA hardware generated. It lowers to an assignment in all other contexts. Implementation strategy: In CodeGen, we generate a pointer annotation: T* llvm.ptr.annotation.*(T*, "{params:..}{cachesize:..}", ...) Signed-off-by: Mohammad Fawaz <[email protected]>
1 parent 33f6ea4 commit 1e33c01

File tree

8 files changed

+272
-5
lines changed

8 files changed

+272
-5
lines changed

clang/include/clang/Basic/Builtins.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,6 +1569,7 @@ BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n")
15691569

15701570
// Builtins for Intel FPGA
15711571
BUILTIN(__builtin_intel_fpga_reg, "v.", "nt")
1572+
BUILTIN(__builtin_intel_fpga_mem, "v.", "nt")
15721573

15731574
#undef BUILTIN
15741575
#undef LIBBUILTIN

clang/include/clang/Basic/DiagnosticSemaKinds.td

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,15 @@ def err_bankbits_numbanks_conflicting : Error<
143143
"the number of bank_bits must be equal to ceil(log2(numbanks))">;
144144
def err_bankbits_non_consecutive : Error<
145145
"bank_bits must be consecutive">;
146+
def err_intel_fpga_mem_limitations
147+
: Error<
148+
"illegal %select{pointer argument of type %1 |field in type pointed "
149+
"to by pointer argument}0 to __builtin_intel_fpga_mem. Only pointers "
150+
"to a first class lvalue or to an rvalue are allowed">;
151+
def err_intel_fpga_mem_arg_mismatch
152+
: Error<"builtin parameter must be %select{"
153+
"a pointer"
154+
"|a non-negative integer constant}0">;
146155

147156
// C99 variable-length arrays
148157
def ext_vla : Extension<"variable length arrays are a C99 feature">,

clang/include/clang/Sema/Sema.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11701,7 +11701,8 @@ class Sema final {
1170111701
bool CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
1170211702
bool CheckPPCBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
1170311703

11704-
bool CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID, CallExpr *Call);
11704+
bool CheckIntelFPGARegBuiltinFunctionCall(unsigned BuiltinID, CallExpr *Call);
11705+
bool CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *Call);
1170511706

1170611707
bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall);
1170711708
bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call);

clang/lib/CodeGen/CGBuiltin.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4223,6 +4223,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
42234223
// SYCL
42244224
case Builtin::BI__builtin_intel_fpga_reg:
42254225
return EmitIntelFPGARegBuiltin(E, ReturnValue);
4226+
case Builtin::BI__builtin_intel_fpga_mem:
4227+
return EmitIntelFPGAMemBuiltin(E);
42264228
}
42274229

42284230
// If this is an alias for a lib function (e.g. __builtin_sin), emit
@@ -14892,3 +14894,32 @@ RValue CodeGenFunction::EmitIntelFPGARegBuiltin(const CallExpr *E,
1489214894

1489314895
return RValue::get(AnnotatedV);
1489414896
}
14897+
14898+
RValue CodeGenFunction::EmitIntelFPGAMemBuiltin(const CallExpr *E) {
14899+
// Arguments
14900+
const Expr *PtrArg = E->getArg(0);
14901+
Value *PtrVal = EmitScalarExpr(PtrArg);
14902+
14903+
// Create the pointer annotation
14904+
Function *F =
14905+
CGM.getIntrinsic(llvm::Intrinsic::ptr_annotation, PtrVal->getType());
14906+
SmallString<256> AnnotStr;
14907+
llvm::raw_svector_ostream Out(AnnotStr);
14908+
14909+
llvm::APSInt Params;
14910+
bool IsConst = E->getArg(1)->isIntegerConstantExpr(Params, getContext());
14911+
assert(IsConst && "Constant arg isn't actually constant?"); (void)IsConst;
14912+
Out << "{params:" << Params.toString(10) << "}";
14913+
14914+
llvm::APSInt CacheSize;
14915+
IsConst = E->getArg(2)->isIntegerConstantExpr(CacheSize, getContext());
14916+
assert(IsConst && "Constant arg isn't actually constant?"); (void)IsConst;
14917+
Out << "{cache-size:" << CacheSize.toString(10) << "}";
14918+
14919+
llvm::Value *Ann = EmitAnnotationCall(F, PtrVal, AnnotStr, SourceLocation());
14920+
14921+
cast<CallBase>(Ann)->addAttribute(llvm::AttributeList::FunctionIndex,
14922+
llvm::Attribute::ReadNone);
14923+
14924+
return RValue::get(Ann);
14925+
}

clang/lib/CodeGen/CodeGenFunction.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3790,7 +3790,9 @@ class CodeGenFunction : public CodeGenTypeCache {
37903790
const CallExpr *E);
37913791
llvm::Value *EmitHexagonBuiltinExpr(unsigned BuiltinID, const CallExpr *E);
37923792

3793-
RValue EmitIntelFPGARegBuiltin(const CallExpr *E, ReturnValueSlot ReturnValue);
3793+
RValue EmitIntelFPGARegBuiltin(const CallExpr *E,
3794+
ReturnValueSlot ReturnValue);
3795+
RValue EmitIntelFPGAMemBuiltin(const CallExpr *E);
37943796

37953797
private:
37963798
enum class MSVCIntrin;

clang/lib/Sema/SemaChecking.cpp

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1532,7 +1532,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
15321532
<< "SYCL device";
15331533
return ExprError();
15341534
}
1535-
if (CheckIntelFPGABuiltinFunctionCall(BuiltinID, TheCall))
1535+
if (CheckIntelFPGARegBuiltinFunctionCall(BuiltinID, TheCall))
1536+
return ExprError();
1537+
break;
1538+
case Builtin::BI__builtin_intel_fpga_mem:
1539+
if (!Context.getLangOpts().SYCLIsDevice) {
1540+
Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_language)
1541+
<< "__builtin_intel_fpga_mem"
1542+
<< "SYCL device";
1543+
return ExprError();
1544+
}
1545+
if (CheckIntelFPGAMemBuiltinFunctionCall(TheCall))
15361546
return ExprError();
15371547
break;
15381548
}
@@ -4156,8 +4166,8 @@ static bool checkIntelFPGARegArgument(Sema &S, QualType ArgType,
41564166
return false;
41574167
}
41584168

4159-
bool Sema::CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID,
4160-
CallExpr *TheCall) {
4169+
bool Sema::CheckIntelFPGARegBuiltinFunctionCall(unsigned BuiltinID,
4170+
CallExpr *TheCall) {
41614171
switch (BuiltinID) {
41624172
case Builtin::BI__builtin_intel_fpga_reg: {
41634173
if (checkArgCount(*this, TheCall, 1))
@@ -4185,6 +4195,51 @@ bool Sema::CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID,
41854195
}
41864196
}
41874197

4198+
bool Sema::CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *TheCall) {
4199+
// Make sure we have exactly 3 arguments
4200+
if (checkArgCount(*this, TheCall, 3))
4201+
return true;
4202+
4203+
Expr *PointerArg = TheCall->getArg(0);
4204+
QualType PointerArgType = PointerArg->getType();
4205+
4206+
// Make sure that the first argument is a pointer
4207+
if (!isa<PointerType>(PointerArgType))
4208+
return Diag(PointerArg->getBeginLoc(),
4209+
diag::err_intel_fpga_mem_arg_mismatch)
4210+
<< 0;
4211+
4212+
// Make sure that the pointer points to a legal type
4213+
// We use the same argument checks used for __builtin_intel_fpga_reg
4214+
QualType PointeeType = PointerArgType->getPointeeType();
4215+
SourceLocation Loc;
4216+
if (checkIntelFPGARegArgument(*this, PointeeType, Loc)) {
4217+
Diag(TheCall->getBeginLoc(), diag::err_intel_fpga_mem_limitations)
4218+
<< (PointeeType->isRecordType() ? 1 : 0) << PointerArgType
4219+
<< TheCall->getSourceRange();
4220+
if (PointeeType->isRecordType())
4221+
Diag(Loc, diag::illegal_type_declared_here);
4222+
return true;
4223+
}
4224+
4225+
// Second argument must be a constant integer
4226+
llvm::APSInt Result;
4227+
if (SemaBuiltinConstantArg(TheCall, 1, Result))
4228+
return true;
4229+
4230+
// Third argument (CacheSize) must be a non-negative constant integer
4231+
if (SemaBuiltinConstantArg(TheCall, 2, Result))
4232+
return true;
4233+
if (Result < 0)
4234+
return Diag(TheCall->getArg(2)->getBeginLoc(),
4235+
diag::err_intel_fpga_mem_arg_mismatch) << 1;
4236+
4237+
// Set the return type to be the same as the type of the first argument
4238+
// (pointer argument)
4239+
TheCall->setType(PointerArgType);
4240+
return false;
4241+
}
4242+
41884243
/// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo
41894244
/// parameter with the FormatAttr's correct format_idx and firstDataArg.
41904245
/// Returns true when the format fits the function and the FormatStringInfo has
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// RUN: %clang_cc1 -triple spir64-unknown-linux-sycldevice -std=c++11 -fsycl-is-device -disable-llvm-passes -S -emit-llvm -x c++ %s -o - | FileCheck %s
2+
3+
#define PARAM_1 1U << 7
4+
#define PARAM_2 1U << 8
5+
6+
// CHECK: [[STRUCT:%.*]] = type { i32, float }
7+
struct State {
8+
int x;
9+
float y;
10+
};
11+
12+
// CHECK: [[ANN1:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:0}
13+
// CHECK: [[ANN2:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}
14+
15+
// CHECK: define spir_func void @{{.*}}(float addrspace(4)* %A, i32 addrspace(4)* %B, [[STRUCT]] addrspace(4)* %C, [[STRUCT]] addrspace(4)*{{.*}}%D)
16+
void foo(float *A, int *B, State *C, State &D) {
17+
float *x;
18+
int *y;
19+
State *z;
20+
double F = 0.0;
21+
double *f;
22+
23+
// CHECK-DAG: [[Aaddr:%.*]] = alloca float addrspace(4)*
24+
// CHECK-DAG: [[Baddr:%.*]] = alloca i32 addrspace(4)*
25+
// CHECK-DAG: [[Caddr:%.*]] = alloca [[STRUCT]] addrspace(4)*
26+
// CHECK-DAG: [[Daddr:%.*]] = alloca [[STRUCT]] addrspace(4)*
27+
// CHECK-DAG: [[F:%.*]] = alloca double
28+
// CHECK-DAG: [[f:%.*]] = alloca double addrspace(4)*
29+
30+
// CHECK-DAG: [[A:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)** [[Aaddr]]
31+
// CHECK-DAG: [[PTR1:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
32+
// CHECK-DAG: store float addrspace(4)* [[PTR1]], float addrspace(4)** %x
33+
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 0);
34+
35+
// CHECK-DAG: [[B:%[0-9]+]] = load i32 addrspace(4)*, i32 addrspace(4)** [[Baddr]]
36+
// CHECK-DAG: [[PTR2:%[0-9]+]] = call i32 addrspace(4)* @llvm.ptr.annotation{{.*}}[[B]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
37+
// CHECK-DAG: store i32 addrspace(4)* [[PTR2]], i32 addrspace(4)** %y
38+
y = __builtin_intel_fpga_mem(B, PARAM_1 | PARAM_2, 0);
39+
40+
// CHECK-DAG: [[C:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Caddr]]
41+
// CHECK-DAG: [[PTR3:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[C]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
42+
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR3]], [[STRUCT]] addrspace(4)** %z
43+
z = __builtin_intel_fpga_mem(C, PARAM_1 | PARAM_2, 0);
44+
45+
// CHECK-DAG: [[A2:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)** [[Aaddr]]
46+
// CHECK-DAG: [[PTR4:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
47+
// CHECK-DAG: store float addrspace(4)* [[PTR4]], float addrspace(4)** %x
48+
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127);
49+
50+
// CHECK-DAG: [[B2:%[0-9]+]] = load i32 addrspace(4)*, i32 addrspace(4)** [[Baddr]]
51+
// CHECK-DAG: [[PTR5:%[0-9]+]] = call i32 addrspace(4)* @llvm.ptr.annotation{{.*}}[[B2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
52+
// CHECK-DAG: store i32 addrspace(4)* [[PTR5]], i32 addrspace(4)** %y
53+
y = __builtin_intel_fpga_mem(B, PARAM_1 | PARAM_2, 127);
54+
55+
// CHECK-DAG: [[C2:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Caddr]]
56+
// CHECK-DAG: [[PTR6:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[C2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
57+
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR6]], [[STRUCT]] addrspace(4)** %z
58+
z = __builtin_intel_fpga_mem(C, PARAM_1 | PARAM_2, 127);
59+
60+
// CHECK-DAG: [[D:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Daddr]]
61+
// CHECK-DAG: [[PTR7:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[D]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
62+
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR7]], [[STRUCT]] addrspace(4)** %z
63+
z = __builtin_intel_fpga_mem(&D, PARAM_1 | PARAM_2, 127);
64+
65+
// CHECK-DAG: [[PTR8:%[0-9]+]] = call double* @llvm.ptr.annotation{{.*}}[[F]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
66+
// CHECK-DAG: [[ASCAST:%[0-9]+]] = addrspacecast double* [[PTR8]] to double addrspace(4)*
67+
// CHECK-DAG: store double addrspace(4)* [[ASCAST]], double addrspace(4)** [[f]]
68+
f = __builtin_intel_fpga_mem(&F, PARAM_1 | PARAM_2, 127);
69+
}
70+
71+
// CHECK-DAG: attributes [[ATT]] = { readnone }
72+
73+
template <typename name, typename Func>
74+
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
75+
kernelFunc();
76+
}
77+
78+
int main() {
79+
kernel_single_task<class fake_kernel>([]() {
80+
float *A;
81+
int *B;
82+
State *C;
83+
State D;
84+
foo(A, B, C, D); });
85+
return 0;
86+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
// RUN: %clang_cc1 -fsycl-is-device -verify -pedantic -fsyntax-only -x c++ %s
2+
// RUN: %clang_cc1 -verify -pedantic -fsyntax-only -x c++ %s
3+
4+
#define PARAM_1 1U << 7
5+
#define PARAM_2 1U << 8
6+
7+
#ifdef __SYCL_DEVICE_ONLY__
8+
static_assert(__has_builtin(__builtin_intel_fpga_mem), "");
9+
struct State {
10+
int x;
11+
float y;
12+
};
13+
14+
struct inner {
15+
void (*fp)(); // expected-note {{Field with illegal type declared here}}
16+
};
17+
18+
struct outer {
19+
inner A;
20+
};
21+
22+
void foo(float *A, int *B, State *C) {
23+
float *x;
24+
int *y;
25+
State *z;
26+
int i = 0;
27+
void *U;
28+
29+
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, -1);
30+
// expected-error@-1{{builtin parameter must be a non-negative integer constant}}
31+
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2);
32+
// expected-error@-1{{too few arguments to function call, expected 3, have 2}}
33+
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1);
34+
// expected-error@-1{{too many arguments to function call, expected 3, have 4}}
35+
y = __builtin_intel_fpga_mem(B, 0, i);
36+
// expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}}
37+
z = __builtin_intel_fpga_mem(C, i, 0);
38+
// expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}}
39+
z = __builtin_intel_fpga_mem(U, 0, 0);
40+
// expected-error@-1{{illegal pointer argument of type 'void *' to __builtin_intel_fpga_mem. Only pointers to a first class lvalue or to an rvalue are allowed}}
41+
42+
int intArr[10] = {0};
43+
int *k1 = __builtin_intel_fpga_mem(intArr, 0, 0);
44+
// expected-error@-1{{builtin parameter must be a pointer}}
45+
46+
int **k2 = __builtin_intel_fpga_mem(&intArr, 0, 0);
47+
// expected-error@-1{{illegal pointer argument of type 'int (*)[10]' to __builtin_intel_fpga_mem. Only pointers to a first class lvalue or to an rvalue are allowed}}
48+
49+
void (*fp1)();
50+
void (*fp2)() = __builtin_intel_fpga_mem(fp1, 0, 0);
51+
// expected-error@-1{{illegal pointer argument of type 'void (*)()' to __builtin_intel_fpga_mem. Only pointers to a first class lvalue or to an rvalue are allowed}}
52+
53+
struct outer *iii;
54+
struct outer *iv = __builtin_intel_fpga_mem(iii, 0, 0);
55+
// expected-error@-1{{illegal field in type pointed to by pointer argument to __builtin_intel_fpga_mem. Only pointers to a first class lvalue or to an rvalue are allowed}}
56+
}
57+
58+
template <typename name, typename Func>
59+
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
60+
kernelFunc();
61+
}
62+
int main() {
63+
kernel_single_task<class fake_kernel>([]() {
64+
float *A;
65+
int *B;
66+
State *C;
67+
foo(A, B, C); });
68+
return 0;
69+
}
70+
71+
#else
72+
void bar(float *A) {
73+
float *x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127);
74+
// expected-error@-1{{'__builtin_intel_fpga_mem' is only available in SYCL device}}
75+
}
76+
77+
int main() {
78+
float *A;
79+
bar(A);
80+
return 0;
81+
}
82+
#endif

0 commit comments

Comments
 (0)