Skip to content

[SYCL][FPGA] Implementing __builtin_intel_fpga_mem() #1033

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 23, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions clang/include/clang/Basic/Builtins.def
Original file line number Diff line number Diff line change
Expand Up @@ -1569,6 +1569,7 @@ BUILTIN(__builtin_ms_va_copy, "vc*&c*&", "n")

// Builtins for Intel FPGA
BUILTIN(__builtin_intel_fpga_reg, "v.", "nt")
BUILTIN(__builtin_intel_fpga_mem, "v.", "nt")

#undef BUILTIN
#undef LIBBUILTIN
Expand Down
9 changes: 9 additions & 0 deletions clang/include/clang/Basic/DiagnosticSemaKinds.td
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,15 @@ def err_bankbits_numbanks_conflicting : Error<
"the number of bank_bits must be equal to ceil(log2(numbanks))">;
def err_bankbits_non_consecutive : Error<
"bank_bits must be consecutive">;
def err_intel_fpga_mem_limitations
: Error<
"illegal %select{pointer argument of type %1 |field in type pointed "
"to by pointer argument}0 to __builtin_intel_fpga_mem. Only pointers "
"to a first class lvalue or to an rvalue are allowed">;
def err_intel_fpga_mem_arg_mismatch
: Error<"builtin parameter must be %select{"
"a pointer"
"|a non-negative integer constant}0">;

// C99 variable-length arrays
def ext_vla : Extension<"variable length arrays are a C99 feature">,
Expand Down
3 changes: 2 additions & 1 deletion clang/include/clang/Sema/Sema.h
Original file line number Diff line number Diff line change
Expand Up @@ -11701,7 +11701,8 @@ class Sema final {
bool CheckX86BuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);
bool CheckPPCBuiltinFunctionCall(unsigned BuiltinID, CallExpr *TheCall);

bool CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID, CallExpr *Call);
bool CheckIntelFPGARegBuiltinFunctionCall(unsigned BuiltinID, CallExpr *Call);
bool CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *Call);

bool SemaBuiltinVAStart(unsigned BuiltinID, CallExpr *TheCall);
bool SemaBuiltinVAStartARMMicrosoft(CallExpr *Call);
Expand Down
31 changes: 31 additions & 0 deletions clang/lib/CodeGen/CGBuiltin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4223,6 +4223,8 @@ RValue CodeGenFunction::EmitBuiltinExpr(const GlobalDecl GD, unsigned BuiltinID,
// SYCL
case Builtin::BI__builtin_intel_fpga_reg:
return EmitIntelFPGARegBuiltin(E, ReturnValue);
case Builtin::BI__builtin_intel_fpga_mem:
return EmitIntelFPGAMemBuiltin(E);
}

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

return RValue::get(AnnotatedV);
}

RValue CodeGenFunction::EmitIntelFPGAMemBuiltin(const CallExpr *E) {
// Arguments
const Expr *PtrArg = E->getArg(0);
Value *PtrVal = EmitScalarExpr(PtrArg);

// Create the pointer annotation
Function *F =
CGM.getIntrinsic(llvm::Intrinsic::ptr_annotation, PtrVal->getType());
SmallString<256> AnnotStr;
llvm::raw_svector_ostream Out(AnnotStr);

llvm::APSInt Params;
bool IsConst = E->getArg(1)->isIntegerConstantExpr(Params, getContext());
assert(IsConst && "Constant arg isn't actually constant?"); (void)IsConst;
Out << "{params:" << Params.toString(10) << "}";

llvm::APSInt CacheSize;
IsConst = E->getArg(2)->isIntegerConstantExpr(CacheSize, getContext());
assert(IsConst && "Constant arg isn't actually constant?"); (void)IsConst;
Out << "{cache-size:" << CacheSize.toString(10) << "}";

llvm::Value *Ann = EmitAnnotationCall(F, PtrVal, AnnotStr, SourceLocation());

cast<CallBase>(Ann)->addAttribute(llvm::AttributeList::FunctionIndex,
llvm::Attribute::ReadNone);

return RValue::get(Ann);
}
4 changes: 3 additions & 1 deletion clang/lib/CodeGen/CodeGenFunction.h
Original file line number Diff line number Diff line change
Expand Up @@ -3790,7 +3790,9 @@ class CodeGenFunction : public CodeGenTypeCache {
const CallExpr *E);
llvm::Value *EmitHexagonBuiltinExpr(unsigned BuiltinID, const CallExpr *E);

RValue EmitIntelFPGARegBuiltin(const CallExpr *E, ReturnValueSlot ReturnValue);
RValue EmitIntelFPGARegBuiltin(const CallExpr *E,
ReturnValueSlot ReturnValue);
RValue EmitIntelFPGAMemBuiltin(const CallExpr *E);

private:
enum class MSVCIntrin;
Expand Down
61 changes: 58 additions & 3 deletions clang/lib/Sema/SemaChecking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1532,7 +1532,17 @@ Sema::CheckBuiltinFunctionCall(FunctionDecl *FDecl, unsigned BuiltinID,
<< "SYCL device";
return ExprError();
}
if (CheckIntelFPGABuiltinFunctionCall(BuiltinID, TheCall))
if (CheckIntelFPGARegBuiltinFunctionCall(BuiltinID, TheCall))
return ExprError();
break;
case Builtin::BI__builtin_intel_fpga_mem:
if (!Context.getLangOpts().SYCLIsDevice) {
Diag(TheCall->getBeginLoc(), diag::err_builtin_requires_language)
<< "__builtin_intel_fpga_mem"
<< "SYCL device";
return ExprError();
}
if (CheckIntelFPGAMemBuiltinFunctionCall(TheCall))
return ExprError();
break;
}
Expand Down Expand Up @@ -4156,8 +4166,8 @@ static bool checkIntelFPGARegArgument(Sema &S, QualType ArgType,
return false;
}

bool Sema::CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID,
CallExpr *TheCall) {
bool Sema::CheckIntelFPGARegBuiltinFunctionCall(unsigned BuiltinID,
CallExpr *TheCall) {
switch (BuiltinID) {
case Builtin::BI__builtin_intel_fpga_reg: {
if (checkArgCount(*this, TheCall, 1))
Expand Down Expand Up @@ -4185,6 +4195,51 @@ bool Sema::CheckIntelFPGABuiltinFunctionCall(unsigned BuiltinID,
}
}

bool Sema::CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *TheCall) {
// Make sure we have exactly 3 arguments
if (checkArgCount(*this, TheCall, 3))
return true;

Expr *PointerArg = TheCall->getArg(0);
QualType PointerArgType = PointerArg->getType();

// Make sure that the first argument is a pointer
if (!isa<PointerType>(PointerArgType))
return Diag(PointerArg->getBeginLoc(),
diag::err_intel_fpga_mem_arg_mismatch)
<< 0;

// Make sure that the pointer points to a legal type
// We use the same argument checks used for __builtin_intel_fpga_reg
QualType PointeeType = PointerArgType->getPointeeType();
SourceLocation Loc;
if (checkIntelFPGARegArgument(*this, PointeeType, Loc)) {
Diag(TheCall->getBeginLoc(), diag::err_intel_fpga_mem_limitations)
<< (PointeeType->isRecordType() ? 1 : 0) << PointerArgType
<< TheCall->getSourceRange();
if (PointeeType->isRecordType())
Diag(Loc, diag::illegal_type_declared_here);
return true;
}

// Second argument must be a constant integer
llvm::APSInt Result;
if (SemaBuiltinConstantArg(TheCall, 1, Result))
return true;

// Third argument (CacheSize) must be a non-negative constant integer
if (SemaBuiltinConstantArg(TheCall, 2, Result))
return true;
if (Result < 0)
return Diag(TheCall->getArg(2)->getBeginLoc(),
diag::err_intel_fpga_mem_arg_mismatch) << 1;

// Set the return type to be the same as the type of the first argument
// (pointer argument)
TheCall->setType(PointerArgType);
return false;
}

/// Given a FunctionDecl's FormatAttr, attempts to populate the FomatStringInfo
/// parameter with the FormatAttr's correct format_idx and firstDataArg.
/// Returns true when the format fits the function and the FormatStringInfo has
Expand Down
86 changes: 86 additions & 0 deletions clang/test/CodeGenSYCL/intel-fpga-mem-builtin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// 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

#define PARAM_1 1U << 7
#define PARAM_2 1U << 8

// CHECK: [[STRUCT:%.*]] = type { i32, float }
struct State {
int x;
float y;
};

// CHECK: [[ANN1:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:0}
// CHECK: [[ANN2:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}

// CHECK: define spir_func void @{{.*}}(float addrspace(4)* %A, i32 addrspace(4)* %B, [[STRUCT]] addrspace(4)* %C, [[STRUCT]] addrspace(4)*{{.*}}%D)
void foo(float *A, int *B, State *C, State &D) {
float *x;
int *y;
State *z;
double F = 0.0;
double *f;

// CHECK-DAG: [[Aaddr:%.*]] = alloca float addrspace(4)*
// CHECK-DAG: [[Baddr:%.*]] = alloca i32 addrspace(4)*
// CHECK-DAG: [[Caddr:%.*]] = alloca [[STRUCT]] addrspace(4)*
// CHECK-DAG: [[Daddr:%.*]] = alloca [[STRUCT]] addrspace(4)*
// CHECK-DAG: [[F:%.*]] = alloca double
// CHECK-DAG: [[f:%.*]] = alloca double addrspace(4)*

// CHECK-DAG: [[A:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)** [[Aaddr]]
// CHECK-DAG: [[PTR1:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store float addrspace(4)* [[PTR1]], float addrspace(4)** %x
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 0);

// CHECK-DAG: [[B:%[0-9]+]] = load i32 addrspace(4)*, i32 addrspace(4)** [[Baddr]]
// CHECK-DAG: [[PTR2:%[0-9]+]] = call i32 addrspace(4)* @llvm.ptr.annotation{{.*}}[[B]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store i32 addrspace(4)* [[PTR2]], i32 addrspace(4)** %y
y = __builtin_intel_fpga_mem(B, PARAM_1 | PARAM_2, 0);

// CHECK-DAG: [[C:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Caddr]]
// CHECK-DAG: [[PTR3:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[C]]{{.*}}[[ANN1]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR3]], [[STRUCT]] addrspace(4)** %z
z = __builtin_intel_fpga_mem(C, PARAM_1 | PARAM_2, 0);

// CHECK-DAG: [[A2:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)** [[Aaddr]]
// CHECK-DAG: [[PTR4:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store float addrspace(4)* [[PTR4]], float addrspace(4)** %x
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127);

// CHECK-DAG: [[B2:%[0-9]+]] = load i32 addrspace(4)*, i32 addrspace(4)** [[Baddr]]
// CHECK-DAG: [[PTR5:%[0-9]+]] = call i32 addrspace(4)* @llvm.ptr.annotation{{.*}}[[B2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store i32 addrspace(4)* [[PTR5]], i32 addrspace(4)** %y
y = __builtin_intel_fpga_mem(B, PARAM_1 | PARAM_2, 127);

// CHECK-DAG: [[C2:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Caddr]]
// CHECK-DAG: [[PTR6:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[C2]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR6]], [[STRUCT]] addrspace(4)** %z
z = __builtin_intel_fpga_mem(C, PARAM_1 | PARAM_2, 127);

// CHECK-DAG: [[D:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)** [[Daddr]]
// CHECK-DAG: [[PTR7:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[D]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR7]], [[STRUCT]] addrspace(4)** %z
z = __builtin_intel_fpga_mem(&D, PARAM_1 | PARAM_2, 127);

// CHECK-DAG: [[PTR8:%[0-9]+]] = call double* @llvm.ptr.annotation{{.*}}[[F]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]]
// CHECK-DAG: [[ASCAST:%[0-9]+]] = addrspacecast double* [[PTR8]] to double addrspace(4)*
// CHECK-DAG: store double addrspace(4)* [[ASCAST]], double addrspace(4)** [[f]]
f = __builtin_intel_fpga_mem(&F, PARAM_1 | PARAM_2, 127);
}

// CHECK-DAG: attributes [[ATT]] = { readnone }

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
kernelFunc();
}

int main() {
kernel_single_task<class fake_kernel>([]() {
float *A;
int *B;
State *C;
State D;
foo(A, B, C, D); });
return 0;
}
82 changes: 82 additions & 0 deletions clang/test/SemaSYCL/intel-fpga-mem-builtin.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// RUN: %clang_cc1 -fsycl-is-device -verify -pedantic -fsyntax-only -x c++ %s
// RUN: %clang_cc1 -verify -pedantic -fsyntax-only -x c++ %s

#define PARAM_1 1U << 7
#define PARAM_2 1U << 8

#ifdef __SYCL_DEVICE_ONLY__
static_assert(__has_builtin(__builtin_intel_fpga_mem), "");
struct State {
int x;
float y;
};

struct inner {
void (*fp)(); // expected-note {{Field with illegal type declared here}}
};

struct outer {
inner A;
};

void foo(float *A, int *B, State *C) {
float *x;
int *y;
State *z;
int i = 0;
void *U;

x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, -1);
// expected-error@-1{{builtin parameter must be a non-negative integer constant}}
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2);
// expected-error@-1{{too few arguments to function call, expected 3, have 2}}
x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1);
// expected-error@-1{{too many arguments to function call, expected 3, have 4}}
y = __builtin_intel_fpga_mem(B, 0, i);
// expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}}
z = __builtin_intel_fpga_mem(C, i, 0);
// expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}}
z = __builtin_intel_fpga_mem(U, 0, 0);
// 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}}

int intArr[10] = {0};
int *k1 = __builtin_intel_fpga_mem(intArr, 0, 0);
// expected-error@-1{{builtin parameter must be a pointer}}

int **k2 = __builtin_intel_fpga_mem(&intArr, 0, 0);
// 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}}

void (*fp1)();
void (*fp2)() = __builtin_intel_fpga_mem(fp1, 0, 0);
// 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}}

struct outer *iii;
struct outer *iv = __builtin_intel_fpga_mem(iii, 0, 0);
// 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}}
}

template <typename name, typename Func>
__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) {
kernelFunc();
}
int main() {
kernel_single_task<class fake_kernel>([]() {
float *A;
int *B;
State *C;
foo(A, B, C); });
return 0;
}

#else
void bar(float *A) {
float *x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127);
// expected-error@-1{{'__builtin_intel_fpga_mem' is only available in SYCL device}}
}

int main() {
float *A;
bar(A);
return 0;
}
#endif