From fbffaab85dae36a6157dd40f4995b0d1e959c60a Mon Sep 17 00:00:00 2001 From: Viktoria Maksimova Date: Tue, 19 Mar 2019 21:56:51 +0300 Subject: [PATCH] [SYCL] Enable FPGA memory attributes This patch introduces the following FPGA memory attributes: * register * memory * numbanks * bankwidth Signed-off-by: Vladimir Lazarev Signed-off-by: Viktoria Maksimova --- clang/include/clang/Basic/Attr.td | 87 +++++++ clang/include/clang/Basic/AttrDocs.td | 41 ++++ .../clang/Basic/DiagnosticSemaKinds.td | 6 + clang/include/clang/Sema/Sema.h | 7 + clang/lib/CodeGen/CGDecl.cpp | 12 + clang/lib/CodeGen/CGExpr.cpp | 8 + clang/lib/CodeGen/CodeGenFunction.cpp | 17 ++ clang/lib/CodeGen/CodeGenFunction.h | 4 + clang/lib/CodeGen/CodeGenModule.cpp | 64 +++++ clang/lib/CodeGen/CodeGenModule.h | 4 + clang/lib/Sema/SemaDeclAttr.cpp | 165 +++++++++++++ clang/test/CodeGenSYCL/intel-fpga-local.cpp | 96 ++++++++ clang/test/SemaSYCL/intel-fpga-local.cpp | 222 ++++++++++++++++++ 13 files changed, 733 insertions(+) create mode 100644 clang/test/CodeGenSYCL/intel-fpga-local.cpp create mode 100644 clang/test/SemaSYCL/intel-fpga-local.cpp diff --git a/clang/include/clang/Basic/Attr.td b/clang/include/clang/Basic/Attr.td index 1e6ce42114575..4fb4fb4a0744a 100644 --- a/clang/include/clang/Basic/Attr.td +++ b/clang/include/clang/Basic/Attr.td @@ -1387,6 +1387,93 @@ def Mode : Attr { let PragmaAttributeSupport = 0; } +def IntelFPGAConstVar : SubsetSubjectgetKind() != Decl::ImplicitParam && + S->getKind() != Decl::ParmVar && + S->getKind() != Decl::NonTypeTemplateParm && + (S->getType().isConstQualified() || + S->getType().getAddressSpace() == + LangAS::opencl_constant)}], + "constant variables">; + +def IntelFPGALocalStaticSlaveMemVar : SubsetSubjectgetKind() != Decl::ImplicitParam && + S->getKind() != Decl::NonTypeTemplateParm && + (S->getStorageClass() == SC_Static || + S->hasLocalStorage())}], + "local variables, static variables, slave memory arguments">; + +def IntelFPGALocalOrStaticVar : SubsetSubjectgetKind() != Decl::ImplicitParam && + S->getKind() != Decl::ParmVar && + S->getKind() != Decl::NonTypeTemplateParm && + (S->getStorageClass() == SC_Static || + S->hasLocalStorage())}], + "local variables, static variables">; + +def IntelFPGAMemory : Attr { + let Spellings = [GNU<"memory">, CXX11<"intelfpga", "memory">]; + let Args = [EnumArgument<"Kind", "MemoryKind", + ["MLAB", "BLOCK_RAM", ""], + ["MLAB", "BlockRAM", "Default"], 1>]; + let AdditionalMembers = [{ + static void generateValidStrings(SmallString<256> &Str) { + auto Last = BlockRAM; + for (int I = 0; I <= Last; ++I) { + Str += ConvertMemoryKindToStr(static_cast(I)); + if (I != Last) Str += " "; + } + } + }]; + let Subjects = SubjectList<[IntelFPGAConstVar, IntelFPGALocalStaticSlaveMemVar, + Field], ErrorDiag>; + let LangOpts = [SYCL]; + let Documentation = [IntelFPGAMemoryAttrDocs]; +} + +def IntelFPGARegister : Attr { + let Spellings = [GNU<"register">, CXX11<"intelfpga", "register">]; + let Subjects = SubjectList<[IntelFPGAConstVar, IntelFPGALocalOrStaticVar, + Field], ErrorDiag>; + let LangOpts = [SYCL]; + let Documentation = [IntelFPGARegisterAttrDocs]; +} + +// One integral argument. +def IntelFPGABankWidth : Attr { + let Spellings = [GNU<"bankwidth">, CXX11<"intelfpga","bankwidth">]; + let Args = [ExprArgument<"Value">]; + let Subjects = SubjectList<[IntelFPGAConstVar, IntelFPGALocalStaticSlaveMemVar, + Field], ErrorDiag>; + let LangOpts = [SYCL]; + let Documentation = [IntelFPGABankWidthAttrDocs]; + let AdditionalMembers = [{ + static unsigned getMinValue() { + return 1; + } + static unsigned getMaxValue() { + return 1024*1024; + } + }]; +} + +def IntelFPGANumBanks : Attr { + let Spellings = [GNU<"numbanks">, CXX11<"intelfpga","numbanks">]; + let Args = [ExprArgument<"Value">]; + let Subjects = SubjectList<[IntelFPGAConstVar, IntelFPGALocalStaticSlaveMemVar, + Field], ErrorDiag>; + let LangOpts = [SYCL]; + let Documentation = [IntelFPGANumBanksAttrDocs]; + let AdditionalMembers = [{ + static unsigned getMinValue() { + return 1; + } + static unsigned getMaxValue() { + return 1024*1024; + } + }]; +} + def Naked : InheritableAttr { let Spellings = [GCC<"naked">, Declspec<"naked">]; let Subjects = SubjectList<[Function]>; diff --git a/clang/include/clang/Basic/AttrDocs.td b/clang/include/clang/Basic/AttrDocs.td index a9835cbeeedff..b38462b53ad40 100644 --- a/clang/include/clang/Basic/AttrDocs.td +++ b/clang/include/clang/Basic/AttrDocs.td @@ -1668,6 +1668,47 @@ as ``-mlong-calls`` and ``-mno-long-calls``. }]; } +def IntelFPGAMemoryAttrDocs : Documentation { + let Category = DocCatVariable; + let Heading = "memory (IntelFGPA)"; + let Content = [{ +This attribute may be attached to a variable or struct member declaration and +instructs the backend to implement the variable or struct member in memory +rather than promoting to register(s). If the optional parameter is specified +it indicates what type of memory to use. + }]; +} + +def IntelFPGARegisterAttrDocs : Documentation { + let Category = DocCatVariable; + let Heading = "register (IntelFGPA)"; + let Content = [{ +This attribute may be attached to a variable or struct member declaration and +instructs the backend to promote the variable or struct member to register(s) +if possible. + }]; +} + +def IntelFPGABankWidthAttrDocs : Documentation { + let Category = DocCatVariable; + let Heading = "bankwidth (IntelFGPA)"; + let Content = [{ +This attribute may be attached to a variable or struct member declaration and +instructs the backend to implement the variable or struct member in a memory +with banks that are N bytes wide. + }]; +} + +def IntelFPGANumBanksAttrDocs : Documentation { + let Category = DocCatVariable; + let Heading = "numbanks (IntelFGPA)"; + let Content = [{ +This attribute may be attached to a variable or struct member declaration and +instructs the backend to implement the variable or struct member in a memory +with N banks. + }]; +} + def RISCVInterruptDocs : Documentation { let Category = DocCatFunction; let Heading = "interrupt (RISCV)"; diff --git a/clang/include/clang/Basic/DiagnosticSemaKinds.td b/clang/include/clang/Basic/DiagnosticSemaKinds.td index 8dede7cb2e77d..49d476f16df68 100644 --- a/clang/include/clang/Basic/DiagnosticSemaKinds.td +++ b/clang/include/clang/Basic/DiagnosticSemaKinds.td @@ -123,6 +123,12 @@ def warn_double_const_requires_fp64 : Warning< def err_half_const_requires_fp16 : Error< "half precision constant requires cl_khr_fp16">; +// Intel FPGA extensions +def err_attribute_argument_not_power_of_two : Error< + "%0 attribute argument must be a constant power of two greater than zero">; +def err_intel_fpga_memory_arg_invalid : Error< + "%0 attribute requires either no argument or one of: %1">; + // C99 variable-length arrays def ext_vla : Extension<"variable length arrays are a C99 feature">, InGroup; diff --git a/clang/include/clang/Sema/Sema.h b/clang/include/clang/Sema/Sema.h index d91940444320c..b4288f487bfc6 100644 --- a/clang/include/clang/Sema/Sema.h +++ b/clang/include/clang/Sema/Sema.h @@ -8751,6 +8751,13 @@ class Sema { /// attribute to be added (usually because of a pragma). void AddOptnoneAttributeIfNoConflicts(FunctionDecl *FD, SourceLocation Loc); + template + bool checkRangedIntegralArgument(Expr *E, const AttrType *TmpAttr, + ExprResult &Result); + template + void AddOneConstantPowerTwoValueAttr(SourceRange AttrRange, Decl *D, Expr *E, + unsigned SpellingListIndex); + /// AddAlignedAttr - Adds an aligned attribute to a particular declaration. void AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, unsigned SpellingListIndex, bool IsPackExpansion); diff --git a/clang/lib/CodeGen/CGDecl.cpp b/clang/lib/CodeGen/CGDecl.cpp index a008ad022bd59..d9809db1c652f 100644 --- a/clang/lib/CodeGen/CGDecl.cpp +++ b/clang/lib/CodeGen/CGDecl.cpp @@ -1607,6 +1607,18 @@ CodeGenFunction::EmitAutoVarAlloca(const VarDecl &D) { (void)DI->EmitDeclareOfAutoVariable(&D, address.getPointer(), Builder); } + // Emit Intel FPGA attribute annotation for a local variable. + if (getLangOpts().SYCLIsDevice) { + SmallString<256> AnnotStr; + CGM.generateIntelFPGAAnnotation(&D, AnnotStr); + if (!AnnotStr.empty()) { + llvm::Value *V = address.getPointer(); + EmitAnnotationCall(CGM.getIntrinsic(llvm::Intrinsic::var_annotation), + Builder.CreateBitCast(V, CGM.Int8PtrTy, V->getName()), + AnnotStr, D.getLocation()); + } + } + if (D.hasAttr() && HaveInsertPoint()) EmitVarAnnotations(&D, address.getPointer()); diff --git a/clang/lib/CodeGen/CGExpr.cpp b/clang/lib/CodeGen/CGExpr.cpp index 33fbb48bc5b84..6b559455fde30 100644 --- a/clang/lib/CodeGen/CGExpr.cpp +++ b/clang/lib/CodeGen/CGExpr.cpp @@ -3958,6 +3958,14 @@ LValue CodeGenFunction::EmitLValueForField(LValue base, if (field->hasAttr()) addr = EmitFieldAnnotations(field, addr); + // Emit attribute annotation for a field. + if (getLangOpts().SYCLIsDevice) { + SmallString<256> AnnotStr; + CGM.generateIntelFPGAAnnotation(field, AnnotStr); + if (!AnnotStr.empty()) + addr = EmitIntelFPGAFieldAnnotations(field, addr, AnnotStr); + } + LValue LV = MakeAddrLValue(addr, FieldType, FieldBaseInfo, FieldTBAAInfo); LV.getQuals().addCVRQualifiers(RecordCVR); diff --git a/clang/lib/CodeGen/CodeGenFunction.cpp b/clang/lib/CodeGen/CodeGenFunction.cpp index d20c78236a61f..a598977526278 100644 --- a/clang/lib/CodeGen/CodeGenFunction.cpp +++ b/clang/lib/CodeGen/CodeGenFunction.cpp @@ -2293,6 +2293,23 @@ Address CodeGenFunction::EmitFieldAnnotations(const FieldDecl *D, return Address(V, Addr.getAlignment()); } +Address CodeGenFunction::EmitIntelFPGAFieldAnnotations(const FieldDecl *D, + Address Addr, + StringRef AnnotStr) { + llvm::Value *V = Addr.getPointer(); + llvm::Type *VTy = V->getType(); + llvm::Function *F = + CGM.getIntrinsic(llvm::Intrinsic::ptr_annotation, CGM.Int8PtrTy); + // FIXME Always emit the cast inst so we can differentiate between + // annotation on the first field of a struct and annotation on the struct + // itself. + if (VTy != CGM.Int8PtrTy) + V = Builder.CreateBitCast(V, CGM.Int8PtrTy); + V = EmitAnnotationCall(F, V, AnnotStr, D->getLocation()); + V = Builder.CreateBitCast(V, VTy); + return Address(V, Addr.getAlignment()); +} + CodeGenFunction::CGCapturedStmtInfo::~CGCapturedStmtInfo() { } CodeGenFunction::SanitizerScope::SanitizerScope(CodeGenFunction *CGF) diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 37b62470aff95..0f62ed4f7afe6 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -4009,6 +4009,10 @@ class CodeGenFunction : public CodeGenTypeCache { /// annotation result. Address EmitFieldAnnotations(const FieldDecl *D, Address V); + /// Emit Intel FPGA field annotations for the given field and value. Returns + /// the annotation result. + Address EmitIntelFPGAFieldAnnotations(const FieldDecl *D, Address V, + StringRef AnnotStr); //===--------------------------------------------------------------------===// // Internal Helpers //===--------------------------------------------------------------------===// diff --git a/clang/lib/CodeGen/CodeGenModule.cpp b/clang/lib/CodeGen/CodeGenModule.cpp index 2fc2b6be1c461..d2bc525f22b8a 100644 --- a/clang/lib/CodeGen/CodeGenModule.cpp +++ b/clang/lib/CodeGen/CodeGenModule.cpp @@ -3512,6 +3512,55 @@ void CodeGenModule::maybeSetTrivialComdat(const Decl &D, GO.setComdat(TheModule.getOrInsertComdat(GO.getName())); } +void CodeGenModule::generateIntelFPGAAnnotation( + const Decl *D, llvm::SmallString<256> &AnnotStr) { + llvm::raw_svector_ostream Out(AnnotStr); + if (D->hasAttr()) + Out << "{register:1}"; + if (auto const *MA = D->getAttr()) { + IntelFPGAMemoryAttr::MemoryKind Kind = MA->getKind(); + Out << "{memory:"; + switch (Kind) { + case IntelFPGAMemoryAttr::MLAB: + case IntelFPGAMemoryAttr::BlockRAM: + Out << IntelFPGAMemoryAttr::ConvertMemoryKindToStr(Kind); + break; + case IntelFPGAMemoryAttr::Default: + Out << "DEFAULT"; + break; + } + Out << '}'; + } + if (const auto *BWA = D->getAttr()) { + llvm::APSInt BWAInt = BWA->getValue()->EvaluateKnownConstInt(getContext()); + Out << '{' << BWA->getSpelling() << ':' << BWAInt << '}'; + } + if (const auto *NBA = D->getAttr()) { + llvm::APSInt BWAInt = NBA->getValue()->EvaluateKnownConstInt(getContext()); + Out << '{' << NBA->getSpelling() << ':' << BWAInt << '}'; + } +} + +void CodeGenModule::addGlobalIntelFPGAAnnotation(const VarDecl *VD, + llvm::GlobalValue *GV) { + SmallString<256> AnnotStr; + generateIntelFPGAAnnotation(VD, AnnotStr); + if (!AnnotStr.empty()) { + // Get the globals for file name, annotation, and the line number. + llvm::Constant *AnnoGV = EmitAnnotationString(AnnotStr), + *UnitGV = EmitAnnotationUnit(VD->getLocation()), + *LineNoCst = EmitAnnotationLineNo(VD->getLocation()); + + llvm::Constant *C = + llvm::ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, Int8PtrTy); + // Create the ConstantStruct for the global annotation. + llvm::Constant *Fields[4] = { + C, llvm::ConstantExpr::getBitCast(AnnoGV, Int8PtrTy), + llvm::ConstantExpr::getBitCast(UnitGV, Int8PtrTy), LineNoCst}; + Annotations.push_back(llvm::ConstantStruct::getAnon(Fields)); + } +} + /// Pass IsTentative as true if you want to create a tentative definition. void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, bool IsTentative) { @@ -3638,6 +3687,21 @@ void CodeGenModule::EmitGlobalVarDefinition(const VarDecl *D, if (D->hasAttr()) AddGlobalAnnotations(D, GV); + // Emit Intel FPGA attribute annotation for a file-scope static variable. + if (getLangOpts().SYCLIsDevice) + addGlobalIntelFPGAAnnotation(D, GV); + + if (D->getType().isRestrictQualified()) { + llvm::LLVMContext &Context = getLLVMContext(); + + // Common metadata nodes. + llvm::NamedMDNode *GlobalsRestrict = + getModule().getOrInsertNamedMetadata("globals.restrict"); + llvm::Metadata *Args[] = {llvm::ValueAsMetadata::get(GV)}; + llvm::MDNode *Node = llvm::MDNode::get(Context, Args); + GlobalsRestrict->addOperand(Node); + } + // Set the llvm linkage type as appropriate. llvm::GlobalValue::LinkageTypes Linkage = getLLVMLinkageVarDefinition(D, GV->isConstant()); diff --git a/clang/lib/CodeGen/CodeGenModule.h b/clang/lib/CodeGen/CodeGenModule.h index a652f143d0f01..ff246e4196fb1 100644 --- a/clang/lib/CodeGen/CodeGenModule.h +++ b/clang/lib/CodeGen/CodeGenModule.h @@ -976,6 +976,10 @@ class CodeGenModule : public CodeGenTypeCache { llvm::FunctionType *FnType = nullptr, bool DontDefer = false, ForDefinition_t IsForDefinition = NotForDefinition); + void generateIntelFPGAAnnotation(const Decl *D, + llvm::SmallString<256> &AnnotStr); + void addGlobalIntelFPGAAnnotation(const VarDecl *VD, llvm::GlobalValue *GV); + /// Given a builtin id for a function like "__builtin_fabsf", return a /// Function* for "fabsf". llvm::Constant *getBuiltinLibFunction(const FunctionDecl *FD, diff --git a/clang/lib/Sema/SemaDeclAttr.cpp b/clang/lib/Sema/SemaDeclAttr.cpp index 945b5157a2fa6..6814f1c7e38b8 100644 --- a/clang/lib/Sema/SemaDeclAttr.cpp +++ b/clang/lib/Sema/SemaDeclAttr.cpp @@ -3763,6 +3763,60 @@ static void handleAlignedAttr(Sema &S, Decl *D, const ParsedAttr &AL) { AL.isPackExpansion()); } +template +bool Sema::checkRangedIntegralArgument(Expr *E, const AttrType *TmpAttr, + ExprResult &Result) { + llvm::APSInt Value; + Result = VerifyIntegerConstantExpression(E, &Value); + if (Result.isInvalid()) + return true; + + if (Value < AttrType::getMinValue() || Value > AttrType::getMaxValue()) { + Diag(TmpAttr->getRange().getBegin(), + diag::err_attribute_argument_out_of_range) + << TmpAttr << AttrType::getMinValue() << AttrType::getMaxValue() + << E->getSourceRange(); + return true; + } + return false; +} + +template +void Sema::AddOneConstantPowerTwoValueAttr(SourceRange AttrRange, Decl *D, + Expr *E, + unsigned SpellingListIndex) { + AttrType TmpAttr(AttrRange, Context, E, SpellingListIndex); + + if (!E->isValueDependent()) { + ExprResult ICE; + if (checkRangedIntegralArgument(E, &TmpAttr, ICE)) + return; + Expr::EvalResult Result; + E->EvaluateAsInt(Result, Context); + llvm::APSInt Value = Result.Val.getInt(); + if (!Value.isPowerOf2()) { + Diag(AttrRange.getBegin(), diag::err_attribute_argument_not_power_of_two) + << &TmpAttr; + return; + } + E = ICE.get(); + } + + if (!D->hasAttr()) + D->addAttr(IntelFPGAMemoryAttr::CreateImplicit( + Context, IntelFPGAMemoryAttr::Default)); + + // We are adding a user NumBanks, drop any implicit default. + if (IntelFPGANumBanksAttr::classof(&TmpAttr)) { + if (auto *NBA = D->getAttr()) + if (NBA->isImplicit()) + D->dropAttr(); + } + + D->addAttr(::new (Context) + AttrType(AttrRange, Context, E, SpellingListIndex)); +} + void Sema::AddAlignedAttr(SourceRange AttrRange, Decl *D, Expr *E, unsigned SpellingListIndex, bool IsPackExpansion) { AlignedAttr TmpAttr(AttrRange, Context, true, E, SpellingListIndex); @@ -4924,6 +4978,102 @@ static void handleTypeTagForDatatypeAttr(Sema &S, Decl *D, AL.getAttributeSpellingListIndex())); } +/// Give a warning for duplicate attributes, return true if duplicate. +template +static bool checkForDuplicateAttribute(Sema &S, Decl *D, + const ParsedAttr &Attr) { + // Give a warning for duplicates but not if it's one we've implicitly added. + auto *A = D->getAttr(); + if (A && !A->isImplicit()) { + S.Diag(Attr.getLoc(), diag::warn_duplicate_attribute_exact) << A; + return true; + } + return false; +} + +/// Handle the __memory__ attribute. +/// This is incompatible with the __register__ attribute. +static void handleIntelFPGAMemoryAttr(Sema &S, Decl *D, + const ParsedAttr &Attr) { + + checkForDuplicateAttribute(S, D, Attr); + if (checkAttrMutualExclusion(S, D, Attr)) + return; + + IntelFPGAMemoryAttr::MemoryKind Kind; + if (Attr.getNumArgs() == 0) + Kind = IntelFPGAMemoryAttr::Default; + else { + StringRef Str; + if (!S.checkStringLiteralArgumentAttr(Attr, 0, Str)) + return; + if (Str.empty() || + !IntelFPGAMemoryAttr::ConvertStrToMemoryKind(Str, Kind)) { + SmallString<256> ValidStrings; + IntelFPGAMemoryAttr::generateValidStrings(ValidStrings); + S.Diag(Attr.getLoc(), diag::err_intel_fpga_memory_arg_invalid) + << Attr << ValidStrings; + return; + } + } + + // We are adding a user memory attribute, drop any implicit default. + if (auto *MA = D->getAttr()) + if (MA->isImplicit()) + D->dropAttr(); + + D->addAttr(::new (S.Context) IntelFPGAMemoryAttr( + Attr.getRange(), S.Context, Kind, Attr.getAttributeSpellingListIndex())); +} + +/// Check for and diagnose attributes incompatible with register. +/// return true if any incompatible attributes exist. +static bool checkIntelFPGARegisterAttrCompatibility(Sema &S, Decl *D, + const ParsedAttr &Attr) { + bool InCompat = false; + if (auto *MA = D->getAttr()) + if (!MA->isImplicit() && + checkAttrMutualExclusion(S, D, Attr)) + InCompat = true; + if (checkAttrMutualExclusion(S, D, Attr)) + InCompat = true; + if (auto *NBA = D->getAttr()) + if (!NBA->isImplicit() && + checkAttrMutualExclusion(S, D, Attr)) + InCompat = true; + + return InCompat; +} + +/// Handle the __register__ attribute. +/// This is incompatible with most of the other memory attributes. +static void handleIntelFPGARegisterAttr(Sema &S, Decl *D, + const ParsedAttr &Attr) { + + checkForDuplicateAttribute(S, D, Attr); + if (checkIntelFPGARegisterAttrCompatibility(S, D, Attr)) + return; + + handleSimpleAttribute(S, D, Attr); +} + +/// Handle the bankwidth and numbanks attributes. +/// These require a single constant power of two greater than zero. +/// These are incompatible with the register attribute. +/// The numbanks and bank_bits attributes are related. If bank_bits exists +/// when handling numbanks they are checked for consistency. +template +static void handleOneConstantPowerTwoValueAttr(Sema &S, Decl *D, + const ParsedAttr &Attr) { + checkForDuplicateAttribute(S, D, Attr); + if (checkAttrMutualExclusion(S, D, Attr)) + return; + + S.AddOneConstantPowerTwoValueAttr( + Attr.getRange(), D, Attr.getArgAsExpr(0), + Attr.getAttributeSpellingListIndex()); +} + static void handleXRayLogArgsAttr(Sema &S, Decl *D, const ParsedAttr &AL) { ParamIdx ArgCount; @@ -7272,6 +7422,21 @@ static void ProcessDeclAttribute(Sema &S, Scope *scope, Decl *D, case ParsedAttr::AT_TypeTagForDatatype: handleTypeTagForDatatypeAttr(S, D, AL); break; + + // Intel FPGA specific attributes + case ParsedAttr::AT_IntelFPGAMemory: + handleIntelFPGAMemoryAttr(S, D, AL); + break; + case ParsedAttr::AT_IntelFPGARegister: + handleIntelFPGARegisterAttr(S, D, AL); + break; + case ParsedAttr::AT_IntelFPGABankWidth: + handleOneConstantPowerTwoValueAttr(S, D, AL); + break; + case ParsedAttr::AT_IntelFPGANumBanks: + handleOneConstantPowerTwoValueAttr(S, D, AL); + break; + case ParsedAttr::AT_AnyX86NoCallerSavedRegisters: handleSimpleAttribute(S, D, AL); break; diff --git a/clang/test/CodeGenSYCL/intel-fpga-local.cpp b/clang/test/CodeGenSYCL/intel-fpga-local.cpp new file mode 100644 index 0000000000000..9d6c624e07a08 --- /dev/null +++ b/clang/test/CodeGenSYCL/intel-fpga-local.cpp @@ -0,0 +1,96 @@ +// RUN: %clang_cc1 -x c++ -triple spir64-unknown-linux-sycldevice -std=c++11 -disable-llvm-passes -fsycl-is-device -emit-llvm %s -o - | FileCheck %s + +//CHECK: [[ANN1:@.str[\.]*[0-9]*]] = {{.*}}{memory:DEFAULT}{numbanks:4} +//CHECK: [[ANN2:@.str[\.]*[0-9]*]] = {{.*}}{register:1} +//CHECK: [[ANN3:@.str[\.]*[0-9]*]] = {{.*}}{memory:DEFAULT} +//CHECK: [[ANN4:@.str[\.]*[0-9]*]] = {{.*}}{memory:DEFAULT}{bankwidth:4} +//CHECK: [[ANN5:@.str[\.]*[0-9]*]] = {{.*}}{memory:BLOCK_RAM} +//CHECK: [[ANN6:@.str[\.]*[0-9]*]] = {{.*}}{memory:MLAB} +//CHECK: [[ANN7:@.str[\.]*[0-9]*]] = {{.*}}{memory:DEFAULT}{bankwidth:8} + +void foo() { + //CHECK: %[[VAR_ONE:[0-9]+]] = bitcast{{.*}}var_one + //CHECK: %[[VAR_ONE1:var_one[0-9]+]] = bitcast{{.*}}var_one + //CHECK: llvm.var.annotation{{.*}}%[[VAR_ONE1]],{{.*}}[[ANN1]] + int __attribute__((numbanks(4))) var_one; + //CHECK: %[[VAR_TWO:[0-9]+]] = bitcast{{.*}}var_two + //CHECK: %[[VAR_TWO1:var_two[0-9]+]] = bitcast{{.*}}var_two + //CHECK: llvm.var.annotation{{.*}}%[[VAR_TWO1]],{{.*}}[[ANN2]] + int __attribute__((register)) var_two; + //CHECK: %[[VAR_THREE:[0-9]+]] = bitcast{{.*}}var_three + //CHECK: %[[VAR_THREE1:var_three[0-9]+]] = bitcast{{.*}}var_three + //CHECK: llvm.var.annotation{{.*}}%[[VAR_THREE1]],{{.*}}[[ANN3]] + int __attribute__((__memory__)) var_three; + //CHECK: %[[VAR_FOUR:[0-9]+]] = bitcast{{.*}}var_four + //CHECK: %[[VAR_FOUR1:var_four[0-9]+]] = bitcast{{.*}}var_four + //CHECK: llvm.var.annotation{{.*}}%[[VAR_FOUR1]],{{.*}}[[ANN4]] + int __attribute__((__bankwidth__(4))) var_four; +} + +struct foo_two { + int __attribute__((numbanks(4))) f1; + int __attribute__((register)) f2; + int __attribute__((__memory__)) f3; + int __attribute__((__bankwidth__(4))) f4; +}; + +void bar() { + struct foo_two s1; + //CHECK: %[[FIELD1:.*]] = getelementptr inbounds %struct.foo_two{{.*}} + //CHECK: %[[CAST:.*]] = bitcast{{.*}}%[[FIELD1]] + //CHECK: call i8* @llvm.ptr.annotation.p0i8{{.*}}%[[CAST]]{{.*}}[[ANN1]] + s1.f1 = 0; + //CHECK: %[[FIELD2:.*]] = getelementptr inbounds %struct.foo_two{{.*}} + //CHECK: %[[CAST:.*]] = bitcast{{.*}}%[[FIELD2]] + //CHECK: call i8* @llvm.ptr.annotation.p0i8{{.*}}%[[CAST]]{{.*}}[[ANN2]] + s1.f2 = 0; + //CHECK: %[[FIELD3:.*]] = getelementptr inbounds %struct.foo_two{{.*}} + //CHECK: %[[CAST:.*]] = bitcast{{.*}}%[[FIELD3]] + //CHECK: call i8* @llvm.ptr.annotation.p0i8{{.*}}%[[CAST]]{{.*}}[[ANN3]] + s1.f3 = 0; + //CHECK: %[[FIELD4:.*]] = getelementptr inbounds %struct.foo_two{{.*}} + //CHECK: %[[CAST:.*]] = bitcast{{.*}}%[[FIELD4]] + //CHECK: call i8* @llvm.ptr.annotation.p0i8{{.*}}%[[CAST]]{{.*}}[[ANN4]] + s1.f4 = 0; +} + +void baz() { + //CHECK: %[[V_ONE:[0-9]+]] = bitcast{{.*}}v_one + //CHECK: %[[V_ONE1:v_one[0-9]+]] = bitcast{{.*}}v_one + //CHECK: llvm.var.annotation{{.*}}%[[V_ONE1]],{{.*}}[[ANN1]] + int v_one [[intelfpga::numbanks(4)]]; + //CHECK: %[[V_TWO:[0-9]+]] = bitcast{{.*}}v_two + //CHECK: %[[V_TWO1:v_two[0-9]+]] = bitcast{{.*}}v_two + //CHECK: llvm.var.annotation{{.*}}%[[V_TWO1]],{{.*}}[[ANN2]] + int v_two [[intelfpga::register]]; + //CHECK: %[[V_THREE:[0-9]+]] = bitcast{{.*}}v_three + //CHECK: %[[V_THREE1:v_three[0-9]+]] = bitcast{{.*}}v_three + //CHECK: llvm.var.annotation{{.*}}%[[V_THREE1]],{{.*}}[[ANN3]] + int v_three [[intelfpga::memory]]; + //CHECK: %[[V_FOUR:[0-9]+]] = bitcast{{.*}}v_four + //CHECK: %[[V_FOUR1:v_four[0-9]+]] = bitcast{{.*}}v_four + //CHECK: llvm.var.annotation{{.*}}%[[V_FOUR1]],{{.*}}[[ANN5]] + int v_four [[intelfpga::memory("BLOCK_RAM")]]; + //CHECK: %[[V_FIVE:[0-9]+]] = bitcast{{.*}}v_five + //CHECK: %[[V_FIVE1:v_five[0-9]+]] = bitcast{{.*}}v_five + //CHECK: llvm.var.annotation{{.*}}%[[V_FIVE1]],{{.*}}[[ANN6]] + int v_five [[intelfpga::memory("MLAB")]]; + //CHECK: %[[V_SIX:[0-9]+]] = bitcast{{.*}}v_six + //CHECK: %[[V_SIX1:v_six[0-9]+]] = bitcast{{.*}}v_six + //CHECK: llvm.var.annotation{{.*}}%[[V_SIX1]],{{.*}}[[ANN7]] + int v_six [[intelfpga::bankwidth(8)]]; +} + +template +__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) { + kernelFunc(); +} + +int main() { + kernel_single_task([]() { + foo(); + bar(); + baz(); + }); + return 0; +} \ No newline at end of file diff --git a/clang/test/SemaSYCL/intel-fpga-local.cpp b/clang/test/SemaSYCL/intel-fpga-local.cpp new file mode 100644 index 0000000000000..29ad19aabbe43 --- /dev/null +++ b/clang/test/SemaSYCL/intel-fpga-local.cpp @@ -0,0 +1,222 @@ +// RUN: %clang_cc1 -x c++ -Wno-return-type -fsycl-is-device -std=c++11 -fcxx-exceptions -fsyntax-only -ast-dump -verify -pedantic %s | FileCheck %s + +//CHECK: FunctionDecl{{.*}}foo1 +void foo1() +{ + //CHECK: VarDecl{{.*}}v_two + //CHECK: IntelFPGAMemoryAttr + __attribute__((__memory__)) + unsigned int v_two[64]; + + //CHECK: VarDecl{{.*}}v_two2 + //CHECK: IntelFPGAMemoryAttr{{.*}}MLAB + [[intelfpga::memory("MLAB")]] unsigned int v_two2[64]; + + //CHECK: VarDecl{{.*}}v_two3 + //CHECK: IntelFPGAMemoryAttr{{.*}}BlockRAM + [[intelfpga::memory("BLOCK_RAM")]] unsigned int v_two3[32]; + + //CHECK: VarDecl{{.*}}v_three + //CHECK: IntelFPGARegisterAttr + __attribute__((__register__)) + unsigned int v_three[64]; + + //CHECK: VarDecl{{.*}}v_three2 + //CHECK: IntelFPGARegisterAttr + [[intelfpga::register]] unsigned int v_three2[32]; + + //CHECK: VarDecl{{.*}}v_five + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGABankWidthAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}4{{$}} + __attribute__((__bankwidth__(4))) + unsigned int v_five[64]; + + //CHECK: VarDecl{{.*}}v_five2 + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGABankWidthAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + [[intelfpga::bankwidth(8)]] unsigned int v_five2[32]; + + //CHECK: VarDecl{{.*}}v_six + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGANumBanksAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + __attribute__((__numbanks__(8))) + unsigned int v_six[64]; + + //CHECK: VarDecl{{.*}}v_six2 + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGANumBanksAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}4{{$}} + __attribute__((__numbanks__(4))) unsigned int v_six2[32]; + + int __attribute__((__register__)) A; + int __attribute__((__numbanks__(4), __bankwidth__(16))) E; + + // diagnostics + + // **register + //expected-warning@+1{{attribute 'register' is already applied}} + __attribute__((register)) __attribute__((__register__)) + unsigned int reg_one[64]; + + //expected-error@+2{{attributes are not compatible}} + __attribute__((__register__)) + __attribute__((__memory__)) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int reg_four[64]; + + //expected-error@+2{{attributes are not compatible}} + __attribute__((__register__)) + __attribute__((__bankwidth__(16))) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int reg_six[64]; + + //expected-error@+2{{attributes are not compatible}} + __attribute__((__register__)) + __attribute__((__numbanks__(8))) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int reg_seven[64]; + + // **memory + //expected-error@+2{{attributes are not compatible}} + __attribute__((__memory__)) + __attribute__((__register__)) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int mem_one[64]; + + //expected-warning@+1{{attribute 'memory' is already applied}} + __attribute__((memory)) __attribute__((__memory__)) + unsigned int mem_two[64]; + + // bankwidth + //expected-error@+2{{attributes are not compatible}} + __attribute__((__bankwidth__(16))) + __attribute__((__register__)) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int bw_one[64]; + + //CHECK: VarDecl{{.*}}bw_two + //CHECK: IntelFPGABankWidthAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + //CHECK: IntelFPGABankWidthAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}16{{$}} + //expected-warning@+2{{attribute 'bankwidth' is already applied}} + __attribute__((__bankwidth__(8))) + __attribute__((__bankwidth__(16))) + unsigned int bw_two[64]; + + //expected-error@+1{{must be a constant power of two greater than zero}} + __attribute__((__bankwidth__(3))) + unsigned int bw_three[64]; + + //expected-error@+1{{requires integer constant between 1 and 1048576}} + __attribute__((__bankwidth__(-4))) + unsigned int bw_four[64]; + + int i_bankwidth = 32; // expected-note {{declared here}} + //expected-error@+1{{is not an integral constant expression}} + __attribute__((__bankwidth__(i_bankwidth))) + //expected-note@-1{{read of non-const variable 'i_bankwidth' is not allowed in a constant expression}} + unsigned int bw_five[64]; + + //expected-error@+1{{'__bankwidth__' attribute takes one argument}} + __attribute__((__bankwidth__(4,8))) + unsigned int bw_six[64]; + + //expected-error@+1{{requires integer constant between 1 and 1048576}} + __attribute__((__bankwidth__(0))) + unsigned int bw_seven[64]; + + // numbanks + //expected-error@+2{{attributes are not compatible}} + __attribute__((__numbanks__(16))) + __attribute__((__register__)) + //expected-note@-2 {{conflicting attribute is here}} + unsigned int nb_one[64]; + + //CHECK: VarDecl{{.*}}nb_two + //CHECK: IntelFPGANumBanksAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + //CHECK: IntelFPGANumBanksAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}16{{$}} + //expected-warning@+2{{attribute 'numbanks' is already applied}} + __attribute__((__numbanks__(8))) + __attribute__((__numbanks__(16))) + unsigned int nb_two[64]; + + //expected-error@+1{{must be a constant power of two greater than zero}} + __attribute__((__numbanks__(15))) + unsigned int nb_three[64]; + + //expected-error@+1{{requires integer constant between 1 and 1048576}} + __attribute__((__numbanks__(-4))) + unsigned int nb_four[64]; + + int i_numbanks = 32; // expected-note {{declared here}} + //expected-error@+1{{is not an integral constant expression}} + __attribute__((__numbanks__(i_numbanks))) + //expected-note@-1{{read of non-const variable 'i_numbanks' is not allowed in a constant expression}} + unsigned int nb_five[64]; + + //expected-error@+1{{'__numbanks__' attribute takes one argument}} + __attribute__((__numbanks__(4,8))) + unsigned int nb_six[64]; + + //expected-error@+1{{requires integer constant between 1 and 1048576}} + __attribute__((__numbanks__(0))) + unsigned int nb_seven[64]; +} + +struct foo { + //CHECK: FieldDecl{{.*}}v_two + //CHECK: IntelFPGAMemoryAttr + __attribute__((__memory__)) unsigned int v_two[64]; + + //CHECK: FieldDecl{{.*}}v_two_A + //CHECK: IntelFPGAMemoryAttr{{.*}}MLAB{{$}} + __attribute__((__memory__("MLAB"))) unsigned int v_two_A[64]; + + //CHECK: FieldDecl{{.*}}v_two_B + //CHECK: IntelFPGAMemoryAttr{{.*}}BlockRAM{{$}} + __attribute__((__memory__("BLOCK_RAM"))) unsigned int v_two_B[64]; + + //CHECK: FieldDecl{{.*}}v_three + //CHECK: IntelFPGARegisterAttr + __attribute__((__register__)) unsigned int v_three[64]; + + //CHECK: FieldDecl{{.*}}v_five + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGABankWidthAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}4{{$}} + __attribute__((__bankwidth__(4))) unsigned int v_five[64]; + + //CHECK: FieldDecl{{.*}}v_six + //CHECK: IntelFPGAMemoryAttr{{.*}}Implicit + //CHECK: IntelFPGANumBanksAttr + //CHECK-NEXT: ConstantExpr + //CHECK-NEXT: IntegerLiteral{{.*}}8{{$}} + __attribute__((__numbanks__(8))) unsigned int v_six[64]; +}; + +template +__attribute__((sycl_kernel)) void kernel_single_task(Func kernelFunc) { + kernelFunc(); +} + +int main() { + kernel_single_task([]() { + foo1(); + }); + return 0; +}