Skip to content

Commit

Permalink
Translate Intel FPGA attributes for static variables
Browse files Browse the repository at this point in the history
Attributes applied to static variables are generated with
@llvm.global.annotations, not with intrinsics as for "usual" variables.
This patch introduces converting of @llvm.global.annotations to correct SPIR-V
decorations and back.
  • Loading branch information
vmaksimo authored and AlexeySotkin committed Sep 9, 2019
1 parent ff61820 commit 1a0c7b5
Show file tree
Hide file tree
Showing 5 changed files with 267 additions and 38 deletions.
114 changes: 77 additions & 37 deletions lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2403,6 +2403,16 @@ bool SPIRVToLLVM::translate() {
if (BV->getStorageClass() != StorageClassFunction)
transValue(BV, nullptr, nullptr);
}
if (!GlobalAnnotations.empty()) {
Constant *Array =
ConstantArray::get(ArrayType::get(GlobalAnnotations[0]->getType(),
GlobalAnnotations.size()),
GlobalAnnotations);
auto *GV = new GlobalVariable(*M, Array->getType(), /*IsConstant*/ false,
GlobalValue::AppendingLinkage, Array,
"llvm.global.annotations");
GV->setSection("llvm.metadata");
}
// Compile unit might be needed during translation of debug intrinsics.
for (SPIRVExtInst *EI : BM->getDebugInstVec()) {
// Translate Compile Unit first.
Expand Down Expand Up @@ -2542,57 +2552,87 @@ void generateIntelFPGAAnnotationForStructMember(
}

void SPIRVToLLVM::transIntelFPGADecorations(SPIRVValue *BV, Value *V) {
if (BV->isVariable()) {
if (auto AL = dyn_cast<AllocaInst>(V)) {
IRBuilder<> Builder(AL->getParent());
if (!BV->isVariable())
return;

SPIRVType *ST = BV->getType()->getPointerElementType();
if (auto AL = dyn_cast<AllocaInst>(V)) {
IRBuilder<> Builder(AL->getParent());

Type *Int8PtrTyPrivate = Type::getInt8PtrTy(*Context, SPIRAS_Private);
IntegerType *Int32Ty = IntegerType::get(*Context, 32);
SPIRVType *ST = BV->getType()->getPointerElementType();

Type *Int8PtrTyPrivate = Type::getInt8PtrTy(*Context, SPIRAS_Private);
IntegerType *Int32Ty = IntegerType::get(*Context, 32);

Value *UndefInt8Ptr = UndefValue::get(Int8PtrTyPrivate);
Value *UndefInt32 = UndefValue::get(Int32Ty);
Value *UndefInt8Ptr = UndefValue::get(Int8PtrTyPrivate);
Value *UndefInt32 = UndefValue::get(Int32Ty);

if (ST->isTypeStruct()) {
SPIRVTypeStruct *STS = static_cast<SPIRVTypeStruct *>(ST);
if (ST->isTypeStruct()) {
SPIRVTypeStruct *STS = static_cast<SPIRVTypeStruct *>(ST);

for (SPIRVWord I = 0; I < STS->getMemberCount(); ++I) {
SmallString<256> AnnotStr;
generateIntelFPGAAnnotationForStructMember(ST, I, AnnotStr);
if (!AnnotStr.empty()) {
auto *GS = Builder.CreateGlobalStringPtr(AnnotStr);
for (SPIRVWord I = 0; I < STS->getMemberCount(); ++I) {
SmallString<256> AnnotStr;
generateIntelFPGAAnnotationForStructMember(ST, I, AnnotStr);
if (!AnnotStr.empty()) {
auto *GS = Builder.CreateGlobalStringPtr(AnnotStr);

auto AnnotationFn = llvm::Intrinsic::getDeclaration(
M, Intrinsic::ptr_annotation, Int8PtrTyPrivate);
auto AnnotationFn = llvm::Intrinsic::getDeclaration(
M, Intrinsic::ptr_annotation, Int8PtrTyPrivate);

auto GEP = Builder.CreateConstInBoundsGEP2_32(
AL->getAllocatedType(), AL, 0, I);
auto GEP = Builder.CreateConstInBoundsGEP2_32(AL->getAllocatedType(),
AL, 0, I);

llvm::Value *Args[] = {
Builder.CreateBitCast(GEP, Int8PtrTyPrivate, GEP->getName()),
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
UndefInt32};
Builder.CreateCall(AnnotationFn, Args);
}
llvm::Value *Args[] = {
Builder.CreateBitCast(GEP, Int8PtrTyPrivate, GEP->getName()),
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
UndefInt32};
Builder.CreateCall(AnnotationFn, Args);
}
}
}

SmallString<256> AnnotStr;
generateIntelFPGAAnnotation(BV, AnnotStr);
if (!AnnotStr.empty()) {
auto *GS = Builder.CreateGlobalStringPtr(AnnotStr);
SmallString<256> AnnotStr;
generateIntelFPGAAnnotation(BV, AnnotStr);
if (!AnnotStr.empty()) {
auto *GS = Builder.CreateGlobalStringPtr(AnnotStr);

auto AnnotationFn =
llvm::Intrinsic::getDeclaration(M, Intrinsic::var_annotation);
auto AnnotationFn =
llvm::Intrinsic::getDeclaration(M, Intrinsic::var_annotation);

llvm::Value *Args[] = {
Builder.CreateBitCast(V, Int8PtrTyPrivate, V->getName()),
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
UndefInt32};
Builder.CreateCall(AnnotationFn, Args);
}
llvm::Value *Args[] = {
Builder.CreateBitCast(V, Int8PtrTyPrivate, V->getName()),
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
UndefInt32};
Builder.CreateCall(AnnotationFn, Args);
}
} else if (auto *GV = dyn_cast<GlobalVariable>(V)) {
SmallString<256> AnnotStr;
generateIntelFPGAAnnotation(BV, AnnotStr);

if (AnnotStr.empty())
return;

Constant *StrConstant =
ConstantDataArray::getString(*Context, StringRef(AnnotStr));

auto *GS = new GlobalVariable(*GV->getParent(), StrConstant->getType(),
/*IsConstant*/ true,
GlobalValue::PrivateLinkage, StrConstant, "");

GS->setUnnamedAddr(GlobalValue::UnnamedAddr::Global);
GS->setSection("llvm.metadata");

Type *ResType = PointerType::getInt8PtrTy(
GV->getContext(), GV->getType()->getPointerAddressSpace());
Constant *C = ConstantExpr::getPointerBitCastOrAddrSpaceCast(GV, ResType);

Type *Int8PtrTyPrivate = Type::getInt8PtrTy(*Context, SPIRAS_Private);
IntegerType *Int32Ty = Type::getInt32Ty(*Context);

llvm::Constant *Fields[4] = {
C, ConstantExpr::getBitCast(GS, Int8PtrTyPrivate),
UndefValue::get(Int8PtrTyPrivate), UndefValue::get(Int32Ty)};

GlobalAnnotations.push_back(ConstantStruct::getAnon(Fields));
}
}

Expand Down
1 change: 1 addition & 0 deletions lib/SPIRV/SPIRVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ class SPIRVToLLVM {
SPIRVBlockToLLVMStructMap BlockMap;
SPIRVToLLVMPlaceholderMap PlaceholderMap;
std::unique_ptr<SPIRVToLLVMDbgTran> DbgTran;
std::vector<Constant *> GlobalAnnotations;

Type *mapType(SPIRVType *BT, Type *T);

Expand Down
37 changes: 36 additions & 1 deletion lib/SPIRV/SPIRVWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1606,9 +1606,44 @@ SPIRVWord LLVMToSPIRV::transFunctionControlMask(Function *F) {
return FCM;
}

void LLVMToSPIRV::transGlobalAnnotation(GlobalVariable *V) {
SPIRVDBG(dbgs() << "[transGlobalAnnotation] " << *V << '\n');

// @llvm.global.annotations is an array that contains structs with 4 fields.
// Get the array of structs with metadata
ConstantArray *CA = cast<ConstantArray>(V->getOperand(0));
for (Value *Op : CA->operands()) {
ConstantStruct *CS = cast<ConstantStruct>(Op);
// The first field of the struct contains a pointer to annotated variable
Value *AnnotatedVar = CS->getOperand(0)->getOperand(0);
SPIRVValue *SV = transValue(AnnotatedVar, nullptr);

// The second field contains a pointer to a global annotation string
GlobalVariable *GV = cast<GlobalVariable>(CS->getOperand(1)->getOperand(0));
// TODO: Refactor to use getConstantStringInfo()
StringRef AnnotationString =
cast<ConstantDataArray>(GV->getOperand(0))->getAsCString();

std::vector<std::pair<Decoration, std::string>> Decorations;
if (BM->isAllowedToUseExtension(
ExtensionID::SPV_INTEL_fpga_memory_attributes))
Decorations = tryParseIntelFPGAAnnotationString(AnnotationString);

// If we didn't find any IntelFPGA-specific decorations, let's
// add the whole annotation string as UserSemantic Decoration
if (Decorations.empty()) {
SV->addDecorate(new SPIRVDecorateUserSemanticAttr(SV, AnnotationString));
} else {
addIntelFPGADecorations(SV, Decorations);
}
}
}

bool LLVMToSPIRV::transGlobalVariables() {
for (auto I = M->global_begin(), E = M->global_end(); I != E; ++I) {
if (!transValue(&(*I), nullptr))
if ((&(*I))->getName() == "llvm.global.annotations")
transGlobalAnnotation(&(*I));
else if (!transValue(&(*I), nullptr))
return false;
}
return true;
Expand Down
1 change: 1 addition & 0 deletions lib/SPIRV/SPIRVWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class LLVMToSPIRV : public ModulePass {
SPIRVValue *transConstant(Value *V);
SPIRVValue *transValue(Value *V, SPIRVBasicBlock *BB,
bool CreateForward = true);
void transGlobalAnnotation(GlobalVariable *V);
SPIRVValue *transValueWithoutDecoration(Value *V, SPIRVBasicBlock *BB,
bool CreateForward = true);

Expand Down
152 changes: 152 additions & 0 deletions test/IntelFPGAMemoryAttributesForStaticVar.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
; Source
; void foo(int a) {
; static int a_one [[intelfpga::numbanks(2)]];
; a_one = a_one + a;
; }

; void bar(int b) {
; static int b_one [[intelfpga::memory("MLAB"), intelfpga::bankwidth(4)]];
; b_one = b_one + b;
; }

; void baz(int c) {
; static int c_one[[clang::annotate("foobarbaz")]];
; c_one = c_one + c;
; }

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

; int main() {
; kernel_single_task<class kernel_function>([]() {
; foo(128);
; bar(42);
; baz(16);
; });
; return 0;
; }

; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc --spirv-ext=+SPV_INTEL_fpga_memory_attributes -o %t.spv
; RUN: llvm-spirv %t.spv -to-text -o - | FileCheck %s --check-prefix=CHECK-SPIRV

; RUN: llvm-spirv -r %t.spv -o %t.rev.bc
; RUN: llvm-dis < %t.rev.bc | FileCheck %s --check-prefix=CHECK-LLVM

; CHECK-SPIRV: Capability FPGAMemoryAttributesINTEL
; CHECK-SPIRV: Extension "SPV_INTEL_fpga_memory_attributes"
; CHECK-SPIRV: Decorate {{[0-9]+}} UserSemantic "foobarbaz"
; CHECK-SPIRV: Decorate {{[0-9]+}} MemoryINTEL "DEFAULT"
; CHECK-SPIRV: Decorate {{[0-9]+}} MemoryINTEL "MLAB"
; CHECK-SPIRV: Decorate {{[0-9]+}} NumbanksINTEL 2

target datalayout = "e-i64:64-v16:16-v24:32-v32:32-v48:64-v96:128-v192:256-v256:256-v512:512-v1024:1024"
target triple = "spir64-unknown-linux-sycldevice"

%"class._ZTSZ4mainE3$_0.anon" = type { i8 }

; CHECK-LLVM: [[STR:@[0-9_.]+]] = {{.*}}{memory:DEFAULT}{numbanks:2}
; CHECK-LLVM: [[STR2:@[0-9_.]+]] = {{.*}}{memory:MLAB}
; CHECK-LLVM: [[STR3:@[0-9_.]+]] = {{.*}}foobarbaz
; CHECK-LLVM: @llvm.global.annotations
; CHECK-SAME: _ZZ3fooiE5a_one{{.*}}[[STR]]{{.*}}_ZZ3bariE5b_one{{.*}}[[STR2]]{{.*}}_ZZ3baziE5c_one{{.*}}[[STR3]]
@_ZZ3fooiE5a_one = internal addrspace(1) global i32 0, align 4
@.str = private unnamed_addr constant [29 x i8] c"{memory:DEFAULT}{numbanks:2}\00", section "llvm.metadata"
@.str.1 = private unnamed_addr constant [9 x i8] c"test.cpp\00", section "llvm.metadata"
@_ZZ3bariE5b_one = internal addrspace(1) global i32 0, align 4
@.str.2 = private unnamed_addr constant [14 x i8] c"{memory:MLAB}\00", section "llvm.metadata"
@_ZZ3baziE5c_one = internal addrspace(1) global i32 0, align 4
@.str.3 = private unnamed_addr constant [10 x i8] c"foobarbaz\00", section "llvm.metadata"
@llvm.global.annotations = appending global [3 x { i8 addrspace(1)*, i8*, i8*, i32 }] [{ i8 addrspace(1)*, i8*, i8*, i32 } { i8 addrspace(1)* bitcast (i32 addrspace(1)* @_ZZ3fooiE5a_one to i8 addrspace(1)*), i8* getelementptr inbounds ([29 x i8], [29 x i8]* @.str, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0), i32 2 }, { i8 addrspace(1)*, i8*, i8*, i32 } { i8 addrspace(1)* bitcast (i32 addrspace(1)* @_ZZ3bariE5b_one to i8 addrspace(1)*), i8* getelementptr inbounds ([14 x i8], [14 x i8]* @.str.2, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0), i32 7 }, { i8 addrspace(1)*, i8*, i8*, i32 } { i8 addrspace(1)* bitcast (i32 addrspace(1)* @_ZZ3baziE5c_one to i8 addrspace(1)*), i8* getelementptr inbounds ([10 x i8], [10 x i8]* @.str.3, i32 0, i32 0), i8* getelementptr inbounds ([9 x i8], [9 x i8]* @.str.1, i32 0, i32 0), i32 12 }], section "llvm.metadata"

; Function Attrs: nounwind
define spir_kernel void @_ZTSZ4mainE15kernel_function() #0 !kernel_arg_addr_space !4 !kernel_arg_access_qual !4 !kernel_arg_type !4 !kernel_arg_base_type !4 !kernel_arg_type_qual !4 {
entry:
%0 = alloca %"class._ZTSZ4mainE3$_0.anon", align 1
%1 = bitcast %"class._ZTSZ4mainE3$_0.anon"* %0 to i8*
call void @llvm.lifetime.start.p0i8(i64 1, i8* %1) #4
%2 = addrspacecast %"class._ZTSZ4mainE3$_0.anon"* %0 to %"class._ZTSZ4mainE3$_0.anon" addrspace(4)*
call spir_func void @"_ZZ4mainENK3$_0clEv"(%"class._ZTSZ4mainE3$_0.anon" addrspace(4)* %2)
%3 = bitcast %"class._ZTSZ4mainE3$_0.anon"* %0 to i8*
call void @llvm.lifetime.end.p0i8(i64 1, i8* %3) #4
ret void
}

; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.start.p0i8(i64 immarg, i8* nocapture) #1

; Function Attrs: inlinehint nounwind
define internal spir_func void @"_ZZ4mainENK3$_0clEv"(%"class._ZTSZ4mainE3$_0.anon" addrspace(4)* %this) #2 align 2 {
entry:
%this.addr = alloca %"class._ZTSZ4mainE3$_0.anon" addrspace(4)*, align 8
store %"class._ZTSZ4mainE3$_0.anon" addrspace(4)* %this, %"class._ZTSZ4mainE3$_0.anon" addrspace(4)** %this.addr, align 8, !tbaa !5
%this1 = load %"class._ZTSZ4mainE3$_0.anon" addrspace(4)*, %"class._ZTSZ4mainE3$_0.anon" addrspace(4)** %this.addr, align 8
call spir_func void @_Z3fooi(i32 128)
call spir_func void @_Z3bari(i32 42)
call spir_func void @_Z3bazi(i32 16)
ret void
}

; Function Attrs: argmemonly nounwind
declare void @llvm.lifetime.end.p0i8(i64 immarg, i8* nocapture) #1

; Function Attrs: nounwind
define spir_func void @_Z3fooi(i32 %a) #3 {
entry:
%a.addr = alloca i32, align 4
store i32 %a, i32* %a.addr, align 4, !tbaa !9
%0 = load i32, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3fooiE5a_one to i32 addrspace(4)*), align 4, !tbaa !9
%1 = load i32, i32* %a.addr, align 4, !tbaa !9
%add = add nsw i32 %0, %1
store i32 %add, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3fooiE5a_one to i32 addrspace(4)*), align 4, !tbaa !9
ret void
}

; Function Attrs: nounwind
define spir_func void @_Z3bari(i32 %b) #3 {
entry:
%b.addr = alloca i32, align 4
store i32 %b, i32* %b.addr, align 4, !tbaa !9
%0 = load i32, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3bariE5b_one to i32 addrspace(4)*), align 4, !tbaa !9
%1 = load i32, i32* %b.addr, align 4, !tbaa !9
%add = add nsw i32 %0, %1
store i32 %add, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3bariE5b_one to i32 addrspace(4)*), align 4, !tbaa !9
ret void
}

; Function Attrs: nounwind
define spir_func void @_Z3bazi(i32 %c) #3 {
entry:
%c.addr = alloca i32, align 4
store i32 %c, i32* %c.addr, align 4, !tbaa !9
%0 = load i32, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3baziE5c_one to i32 addrspace(4)*), align 4, !tbaa !9
%1 = load i32, i32* %c.addr, align 4, !tbaa !9
%add = add nsw i32 %0, %1
store i32 %add, i32 addrspace(4)* addrspacecast (i32 addrspace(1)* @_ZZ3baziE5c_one to i32 addrspace(4)*), align 4, !tbaa !9
ret void
}

attributes #0 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "uniform-work-group-size"="true" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #1 = { argmemonly nounwind }
attributes #2 = { inlinehint nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #3 = { nounwind "correctly-rounded-divide-sqrt-fp-math"="false" "disable-tail-calls"="false" "less-precise-fpmad"="false" "min-legal-vector-width"="0" "no-frame-pointer-elim"="false" "no-infs-fp-math"="false" "no-jump-tables"="false" "no-nans-fp-math"="false" "no-signed-zeros-fp-math"="false" "no-trapping-math"="false" "stack-protector-buffer-size"="8" "unsafe-fp-math"="false" "use-soft-float"="false" }
attributes #4 = { nounwind }

!llvm.module.flags = !{!0}
!opencl.spir.version = !{!1}
!spirv.Source = !{!2}
!llvm.ident = !{!3}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 1, i32 2}
!2 = !{i32 4, i32 100000}
!3 = !{!"clang version 9.0.0"}
!4 = !{}
!5 = !{!6, !6, i64 0}
!6 = !{!"any pointer", !7, i64 0}
!7 = !{!"omnipotent char", !8, i64 0}
!8 = !{!"Simple C++ TBAA"}
!9 = !{!10, !10, i64 0}
!10 = !{!"int", !7, i64 0}

0 comments on commit 1a0c7b5

Please sign in to comment.