diff --git a/clang/lib/CodeGen/CGBuiltin.cpp b/clang/lib/CodeGen/CGBuiltin.cpp index bcc50df17da76..d0e7516343032 100644 --- a/clang/lib/CodeGen/CGBuiltin.cpp +++ b/clang/lib/CodeGen/CGBuiltin.cpp @@ -20449,6 +20449,7 @@ RValue CodeGenFunction::EmitIntelFPGAMemBuiltin(const CallExpr *E) { // Arguments const Expr *PtrArg = E->getArg(0); Value *PtrVal = EmitScalarExpr(PtrArg); + ASTContext &Ctx = getContext(); // Create the pointer annotation Function *F = @@ -20456,16 +20457,36 @@ RValue CodeGenFunction::EmitIntelFPGAMemBuiltin(const CallExpr *E) { SmallString<256> AnnotStr; llvm::raw_svector_ostream Out(AnnotStr); - Optional Params = - E->getArg(1)->getIntegerConstantExpr(getContext()); + Optional Params = E->getArg(1)->getIntegerConstantExpr(Ctx); assert(Params.hasValue() && "Constant arg isn't actually constant?"); Out << "{params:" << toString(*Params, 10) << "}"; - Optional CacheSize = - E->getArg(2)->getIntegerConstantExpr(getContext()); + Optional CacheSize = E->getArg(2)->getIntegerConstantExpr(Ctx); assert(CacheSize.hasValue() && "Constant arg isn't actually constant?"); Out << "{cache-size:" << toString(*CacheSize, 10) << "}"; + // There are four optional arguments with the following default values: + // const int32_t AnchorID = -1 + // const int32_t TargetAnchor = 0 + // const int32_t Type = 0 + // const int32_t Cycle = 0 + // Emit default values or use provided. + auto AddOptionalArgValue = [&E, &Ctx, &Out](int DefaultValue, + unsigned NumOfArg, + StringRef StringToAdd) { + Optional IntVal = + (E->getNumArgs() > NumOfArg) + ? E->getArg(NumOfArg)->getIntegerConstantExpr(Ctx) + : APSInt::get(DefaultValue); + assert(IntVal.hasValue() && "Constant arg isn't actually constant?"); + Out << "{" << StringToAdd << ":" << toString(*IntVal, 10) << "}"; + }; + + AddOptionalArgValue(-1, 3, "anchor-id"); + AddOptionalArgValue(0, 4, "target-anchor"); + AddOptionalArgValue(0, 5, "type"); + AddOptionalArgValue(0, 6, "cycle"); + llvm::Value *Ann = EmitAnnotationCall(F, PtrVal, AnnotStr, SourceLocation()); cast(Ann)->addFnAttr(llvm::Attribute::ReadNone); diff --git a/clang/lib/Sema/SemaChecking.cpp b/clang/lib/Sema/SemaChecking.cpp index e65e4a1c4019d..f87c1778ec444 100644 --- a/clang/lib/Sema/SemaChecking.cpp +++ b/clang/lib/Sema/SemaChecking.cpp @@ -4859,9 +4859,23 @@ bool Sema::CheckIntelFPGARegBuiltinFunctionCall(unsigned BuiltinID, } bool Sema::CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *TheCall) { - // Make sure we have exactly 3 arguments - if (checkArgCount(*this, TheCall, 3)) - return true; + const unsigned MinNumArgs = 3; + const unsigned MaxNumArgs = 7; + unsigned NumArgs = TheCall->getNumArgs(); + + // Make sure we have the minimum number of provided arguments. + if (NumArgs < MinNumArgs) + return Diag(TheCall->getEndLoc(), + diag::err_typecheck_call_too_few_args_at_least) + << 0 /* function call */ << MinNumArgs << NumArgs + << TheCall->getSourceRange(); + + // Make sure we don't have too many arguments. + if (NumArgs > MaxNumArgs) + return Diag(TheCall->getEndLoc(), + diag::err_typecheck_call_too_many_args_at_most) + << 0 /*function call*/ << MaxNumArgs << NumArgs + << TheCall->getSourceRange(); Expr *PointerArg = TheCall->getArg(0); QualType PointerArgType = PointerArg->getType(); @@ -4897,6 +4911,12 @@ bool Sema::CheckIntelFPGAMemBuiltinFunctionCall(CallExpr *TheCall) { return Diag(TheCall->getArg(2)->getBeginLoc(), diag::err_intel_fpga_mem_arg_mismatch) << 1; + // The last four optional arguments must be signed constant integers. + for (unsigned I = MinNumArgs; I != NumArgs; ++I) { + if (SemaBuiltinConstantArg(TheCall, I, Result)) + return true; + } + // Set the return type to be the same as the type of the first argument // (pointer argument) TheCall->setType(PointerArgType); diff --git a/clang/test/CodeGenSYCL/intel-fpga-mem-builtin.cpp b/clang/test/CodeGenSYCL/intel-fpga-mem-builtin.cpp index fe6833e189d6f..3cd793c19f482 100644 --- a/clang/test/CodeGenSYCL/intel-fpga-mem-builtin.cpp +++ b/clang/test/CodeGenSYCL/intel-fpga-mem-builtin.cpp @@ -3,14 +3,21 @@ #define PARAM_1 1U << 7 #define PARAM_2 1U << 8 +// This test checks that using of __builtin_intel_fpga_mem results in correct +// generation of annotations in LLVM IR. + // 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: [[ANN1:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:0}{anchor-id:-1}{target-anchor:0}{type:0}{cycle:0} +// CHECK: [[ANN2:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}{anchor-id:-1}{target-anchor:0}{type:0}{cycle:0} +// CHECK: [[ANN3:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}{anchor-id:10}{target-anchor:20}{type:30}{cycle:40} +// CHECK: [[ANN4:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}{anchor-id:11}{target-anchor:12}{type:0}{cycle:0} +// CHECK: [[ANN5:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:127}{anchor-id:100}{target-anchor:0}{type:0}{cycle:0} +// CHECK: [[ANN6:@.str[\.]*[0-9]*]] = {{.*}}{params:384}{cache-size:128}{anchor-id:4}{target-anchor:7}{type:8}{cycle:0} // 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) { @@ -65,6 +72,29 @@ void foo(float *A, int *B, State *C, State &D) { // CHECK-DAG: [[PTR8:%[0-9]+]] = call double addrspace(4)* @llvm.ptr.annotation{{.*}}[[F]]{{.*}}[[ANN2]]{{.*}}[[ATT:#[0-9]+]] // CHECK-DAG: store double addrspace(4)* [[PTR8]], double addrspace(4)* addrspace(4)* [[f]] f = __builtin_intel_fpga_mem(&F, PARAM_1 | PARAM_2, 127); + + // CHECK-DAG: [[A3:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)* addrspace(4)* [[Aaddr]] + // CHECK-DAG: [[PTR9:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A3]]{{.*}}[[ANN3]]{{.*}}[[ATT:#[0-9]+]] + // CHECK-DAG: store float addrspace(4)* [[PTR9]], float addrspace(4)* addrspace(4)* %x + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127, 10, 20, 30, 40); + + // CHECK-DAG: [[A4:%[0-9]+]] = load float addrspace(4)*, float addrspace(4)* addrspace(4)* [[Aaddr]] + // CHECK-DAG: [[PTR10:%[0-9]+]] = call float addrspace(4)* @llvm.ptr.annotation{{.*}}[[A4]]{{.*}}[[ANN4]]{{.*}}[[ATT:#[0-9]+]] + // CHECK-DAG: store float addrspace(4)* [[PTR10]], float addrspace(4)* addrspace(4)* %x + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 127, 11, 12); + + // CHECK-DAG: [[B3:%[0-9]+]] = load i32 addrspace(4)*, i32 addrspace(4)* addrspace(4)* [[Baddr]] + // CHECK-DAG: [[PTR11:%[0-9]+]] = call i32 addrspace(4)* @llvm.ptr.annotation{{.*}}[[B3]]{{.*}}[[ANN5]]{{.*}}[[ATT:#[0-9]+]] + // CHECK-DAG: store i32 addrspace(4)* [[PTR11]], i32 addrspace(4)* addrspace(4)* %y + y = __builtin_intel_fpga_mem(B, PARAM_1 | PARAM_2, 127, 100); + + constexpr int TestVal1 = 7; + constexpr int TestVal2 = 8; + + // CHECK-DAG: [[D1:%[0-9]+]] = load [[STRUCT]] addrspace(4)*, [[STRUCT]] addrspace(4)* addrspace(4)* [[Daddr]] + // CHECK-DAG: [[PTR12:%[0-9]+]] = call [[STRUCT]] addrspace(4)* @llvm.ptr.annotation{{.*}}[[D1]]{{.*}}[[ANN6]]{{.*}}[[ATT:#[0-9]+]] + // CHECK-DAG: store [[STRUCT]] addrspace(4)* [[PTR12]], [[STRUCT]] addrspace(4)* addrspace(4)* %z + z = __builtin_intel_fpga_mem(&D, PARAM_1 | PARAM_2, 128, 4, TestVal1, TestVal2); } // CHECK-DAG: attributes [[ATT]] = { readnone } diff --git a/clang/test/SemaSYCL/intel-fpga-mem-builtin.cpp b/clang/test/SemaSYCL/intel-fpga-mem-builtin.cpp index 77d7ad6920865..11afe6fc20989 100644 --- a/clang/test/SemaSYCL/intel-fpga-mem-builtin.cpp +++ b/clang/test/SemaSYCL/intel-fpga-mem-builtin.cpp @@ -4,6 +4,9 @@ #define PARAM_1 1U << 7 #define PARAM_2 1U << 8 +// This test makes sure that the compiler checks the semantics of +// __builtin_intel_fpga_mem built-in function arguments correctly. + #ifdef __SYCL_DEVICE_ONLY__ static_assert(__has_builtin(__builtin_intel_fpga_mem), ""); struct State { @@ -29,9 +32,7 @@ void foo(float *A, int *B, State *C) { 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}} + // expected-error@-1{{too few arguments to function call, expected at least 3, have 2}} 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); @@ -53,6 +54,24 @@ void foo(float *A, int *B, State *C) { 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}} + + // Up to 7 parameters is ok. + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1); + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, 10); + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, 10, 20); + x = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, -1, 10, 20); + + z = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, B, -1, 1, 1); + // expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}} + z = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, U, 10, 20); + // expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}} + z = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, -1, C, 300); + // expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}} + z = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, -1, 1, i); + // expected-error@-1{{argument to '__builtin_intel_fpga_mem' must be a constant integer}} + + y = __builtin_intel_fpga_mem(A, PARAM_1 | PARAM_2, 1, 1, -1, 10, 20, 30); + // expected-error@-1{{too many arguments to function call, expected at most 7, have 8}} } template