Skip to content
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
54 changes: 47 additions & 7 deletions lib/SPIRV/SPIRVReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,28 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F,
Index.insert(Index.begin(), getInt32(M, 0));
auto IsInbound = AC->isInBounds();
Value *V = nullptr;

if (GEPOrUseMap.count(Base)) {
auto IdxToInstMap = GEPOrUseMap[Base];
auto Idx = AC->getIndices();

// In transIntelFPGADecorations we generated GEPs only for the fields of
// structure, meaning that GEP to `0` accesses the Structure itself, and
// the second `Id` is a Key in the map.
if (Idx.size() == 2) {
unsigned Idx1 = static_cast<ConstantInt *>(getTranslatedValue(Idx[0]))
->getZExtValue();
if (Idx1 == 0) {
unsigned Idx2 = static_cast<ConstantInt *>(getTranslatedValue(Idx[1]))
->getZExtValue();

// If we already have the instruction in a map, use it.
if (IdxToInstMap.count(Idx2))
return mapValue(BV, IdxToInstMap[Idx2]);
}
}
}

if (BB) {
auto GEP =
GetElementPtrInst::Create(BaseTy, Base, Index, BV->getName(), BB);
Expand Down Expand Up @@ -3475,21 +3497,39 @@ void SPIRVToLLVM::transIntelFPGADecorations(SPIRVValue *BV, Value *V) {
for (const auto &AnnotStr : AnnotStrVec) {
auto *GS = Builder.CreateGlobalStringPtr(AnnotStr);

auto *GEP = cast<GetElementPtrInst>(
Builder.CreateConstInBoundsGEP2_32(AllocatedTy, AL, 0, I));
Instruction *PtrAnnFirstArg = nullptr;

Type *IntTy = GEP->getResultElementType()->isIntegerTy()
? GEP->getType()
: Int8PtrTyPrivate;
if (GEPOrUseMap.count(AL)) {
auto IdxToInstMap = GEPOrUseMap[AL];
if (IdxToInstMap.count(I)) {
PtrAnnFirstArg = IdxToInstMap[I];
}
}

Type *IntTy = nullptr;

if (!PtrAnnFirstArg) {
GetElementPtrInst *GEP = cast<GetElementPtrInst>(
Builder.CreateConstInBoundsGEP2_32(AllocatedTy, AL, 0, I));

IntTy = GEP->getResultElementType()->isIntegerTy()
? GEP->getType()
: Int8PtrTyPrivate;
PtrAnnFirstArg = GEP;
} else {
IntTy = PtrAnnFirstArg->getType();
}

auto *AnnotationFn = llvm::Intrinsic::getDeclaration(
M, Intrinsic::ptr_annotation, {IntTy, Int8PtrTyPrivate});

llvm::Value *Args[] = {
Builder.CreateBitCast(GEP, IntTy, GEP->getName()),
Builder.CreateBitCast(PtrAnnFirstArg, IntTy,
PtrAnnFirstArg->getName()),
Builder.CreateBitCast(GS, Int8PtrTyPrivate), UndefInt8Ptr,
UndefInt32, UndefInt8Ptr};
Builder.CreateCall(AnnotationFn, Args);
auto *PtrAnnotationCall = Builder.CreateCall(AnnotationFn, Args);
GEPOrUseMap[AL][I] = PtrAnnotationCall;
}
}
}
Expand Down
10 changes: 10 additions & 0 deletions lib/SPIRV/SPIRVReader.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,14 @@ class SPIRVToLLVM : private BuiltinCallHelper {
typedef std::map<const BasicBlock *, const SPIRVValue *>
SPIRVToLLVMLoopMetadataMap;

// Store all the allocations to Struct Types that are further
// accessed inside GetElementPtr instruction or in ptr.annotation intrinsics.
// For every structure we save the accessed structure field index and the
// last corresponding translated LLVM instruction.
typedef std::unordered_map<Value *,
std::unordered_map<SPIRVWord, Instruction *>>
TypeToGEPOrUseMap;

private:
Module *M;
LLVMContext *Context;
Expand Down Expand Up @@ -186,6 +194,8 @@ class SPIRVToLLVM : private BuiltinCallHelper {
SPIRVToLLVMMDAliasInstMap MDAliasScopeMap;
SPIRVToLLVMMDAliasInstMap MDAliasListMap;

TypeToGEPOrUseMap GEPOrUseMap;

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

// If a value is mapped twice, the existing mapped value is a placeholder,
Expand Down
11 changes: 8 additions & 3 deletions test/transcoding/multiple_user_semantic.ll
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,19 @@
; CHECK-LLVM: @[[StrB:[0-9_.]+]] = {{.*}}"var_annotation_b2\00"
; CHECK-LLVM: %[[#StructMember:]] = alloca %class.Sample, align 4
; CHECK-LLVM: %[[#GEP1:]] = getelementptr inbounds %class.Sample, %class.Sample* %[[#StructMember]], i32 0, i32 0
; CHECK-LLVM: call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP1:]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructA]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#GEP2:]] = getelementptr inbounds %class.Sample, %class.Sample* %[[#StructMember]], i32 0, i32 0
; CHECK-LLVM: call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP2]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @[[StrStructB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#PtrAnn1:]] = call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP1:]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructA]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#PtrAnn2:]] = call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#PtrAnn1]], i8* getelementptr inbounds ([20 x i8], [20 x i8]* @[[StrStructB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: [[#Var:]] = alloca i32, align 4
; CHECK-LLVM: [[#Bitcast1:]] = bitcast i32* %[[#Var]] to i8*
; CHECK-LLVM: call void @llvm.var.annotation.p0i8.p0i8(i8* %[[#Bitcast1]], i8* getelementptr inbounds ([17 x i8], [17 x i8]* @[[StrA]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: [[#Bitcast2:]] = bitcast i32* %[[#Var]] to i8*
; CHECK-LLVM: call void @llvm.var.annotation.p0i8.p0i8(i8* %[[#Bitcast2]], i8* getelementptr inbounds ([18 x i8], [18 x i8]* @[[StrB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: [[#Bitcast3:]] = bitcast i32* %[[#PtrAnn2]] to i8*
; CHECK-LLVM: [[#Bitcast4:]] = bitcast i8* %[[#Bitcast3]] to i32*
; CHECK-LLVM: [[#Load1:]] = load i32, i32* %[[#Bitcast4]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#Load1]])
; CHECK-LLVM: [[#Load2:]] = load i32, i32* %[[#Var]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#Load2]])


source_filename = "llvm-link"
Expand Down
5 changes: 2 additions & 3 deletions test/transcoding/multiple_user_semantic_nonopaque.ll
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,8 @@
; CHECK-LLVM: call void @llvm.var.annotation.p0i8.p0i8(i8* %[[#Bitcast2]], i8* getelementptr inbounds ([17 x i8], [17 x i8]* @[[StrB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#StructMember:]] = alloca %class.Sample, align 4
; CHECK-LLVM: %[[#GEP1:]] = getelementptr inbounds %class.Sample, %class.Sample* %[[#StructMember]], i32 0, i32 0
; CHECK-LLVM: call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP1:]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructA]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#GEP2:]] = getelementptr inbounds %class.Sample, %class.Sample* %[[#StructMember]], i32 0, i32 0
; CHECK-LLVM: call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP2]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: %[[#PtrAnn:]] = call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#GEP1:]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructA]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)
; CHECK-LLVM: call i32* @llvm.ptr.annotation.p0i32.p0i8(i32* %[[#PtrAnn]], i8* getelementptr inbounds ([19 x i8], [19 x i8]* @[[StrStructB]], i32 0, i32 0), i8* undef, i32 undef, i8* undef)


source_filename = "llvm-link"
Expand Down
170 changes: 170 additions & 0 deletions test/transcoding/multiple_user_semantic_on_struct.ll
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
; This test is intended to check that we are actually using the
; ptr.annotation intrinsic call results during the reverse translation.

; Source (https://godbolt.org/z/qzhsKfPeq):
; class B {
; public:
; int Val [[clang::annotate("ClassB")]];
; };
; class A {
; public:
; int Val [[clang::annotate("ClassA")]];
; int MultiDec [[clang::annotate("Dec1"), clang::annotate("Dec2"), clang::annotate("Dec3")]];
; [[clang::annotate("ClassAfieldB")]]class B b;
; };
; void foo(int);
; int main() {
; A a;
; B b;
; A c;
; foo(a.Val); // ClassA
; foo(c.Val); // Obj2ClassA
; foo(a.MultiDec); // ClassAMultiDec
; foo(a.b.Val); // ClassAFieldB
; foo(b.Val); // ClassB
; return 0;
; }


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

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

; Check that even when FPGA memory extensions are enabled - yet we have
; UserSemantic decoration be generated
; RUN: llvm-as %s -o %t.bc
; RUN: llvm-spirv %t.bc --spirv-ext=+SPV_INTEL_fpga_memory_accesses,+SPV_INTEL_fpga_memory_attributes -spirv-text -o - | FileCheck %s --check-prefix=CHECK-SPIRV

; CHECK-SPIRV: Name [[#ClassA:]] "class.A"
; CHECK-SPIRV: Name [[#ClassB:]] "class.B"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 0 UserSemantic "ClassA"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 0 UserSemantic "ClassA"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 1 UserSemantic "Dec1"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 1 UserSemantic "Dec2"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 1 UserSemantic "Dec3"
; CHECK-SPIRV: MemberDecorate [[#ClassA]] 2 UserSemantic "ClassAfieldB"
; CHECK-SPIRV: MemberDecorate [[#ClassB]] 0 UserSemantic "ClassB"
; CHECK-SPIRV: MemberDecorate [[#ClassB]] 0 UserSemantic "ClassB"

; CHECK-LLVM: @[[#StrStructA:]] = {{.*}}"ClassA\00"
; CHECK-LLVM: @[[#StrStructA2:]] = {{.*}}"ClassA\00"
; CHECK-LLVM: @[[#Dec1:]] = {{.*}}"Dec1\00"
; CHECK-LLVM: @[[#Dec2:]] = {{.*}}"Dec2\00"
; CHECK-LLVM: @[[#Dec3:]] = {{.*}}"Dec3\00"
; CHECK-LLVM: @[[#StrAfieldB:]] = {{.*}}"ClassAfieldB\00"
; CHECK-LLVM: @[[#StrStructB:]] = {{.*}}"ClassB\00"
; CHECK-LLVM: @[[#StrStructB2:]] = {{.*}}"ClassB\00"
; CHECK-LLVM: @[[#StrObj2StructA:]] = {{.*}}"ClassA\00"
; CHECK-LLVM: @[[#StrObj2StructA2:]] = {{.*}}"ClassA\00"

; CHECK-LLVM: %[[#ObjClassA:]] = alloca %class.A, align 4
; CHECK-LLVM: %[[#GepClassAVal:]] = getelementptr inbounds %class.A, ptr %[[#ObjClassA]], i32 0, i32 0
; CHECK-LLVM: %[[#PtrAnnClassAVal:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#GepClassAVal]], ptr @[[#StrStructA]], ptr undef, i32 undef, ptr undef)
; CHECK-LLVM: %[[#PtrAnn2ClassAVal:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#PtrAnnClassAVal]], ptr @[[#StrStructA2]], ptr undef, i32 undef, ptr undef)

; CHECK-LLVM: %[[#GepMultiDec:]] = getelementptr inbounds %class.A, ptr %[[#ObjClassA]], i32 0, i32 1
; CHECK-LLVM: %[[#PtrAnnMultiDec:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#GepMultiDec]], ptr @[[#Dec1]], ptr undef, i32 undef, ptr undef)
; CHECK-LLVM: %[[#PtrAnn2MultiDec:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#PtrAnnMultiDec]], ptr @[[#Dec2]], ptr undef, i32 undef, ptr undef)
; CHECK-LLVM: %[[#PtrAnn3MultiDec:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#PtrAnn2MultiDec]], ptr @[[#Dec3]], ptr undef, i32 undef, ptr undef)

; CHECK-LLVM: %[[#GepClassAFieldB:]] = getelementptr inbounds %class.A, ptr %[[#ObjClassA]], i32 0, i32 2
; CHECK-LLVM: %[[#CastClassAFieldB:]] = bitcast ptr %[[#GepClassAFieldB]] to ptr
; CHECK-LLVM: %[[#PtrAnnClassAFieldB:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#CastClassAFieldB]], ptr @[[#StrAfieldB]], ptr undef, i32 undef, ptr undef)

; CHECK-LLVM: %[[#ObjClassB:]] = alloca %class.B, align 4
; CHECK-LLVM: %[[#GEPClassB:]] = getelementptr inbounds %class.B, ptr %[[#ObjClassB]], i32 0, i32 0
; CHECK-LLVM: %[[#PtrAnnClassB:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#GEPClassB]], ptr @[[#StrStructB]], ptr undef, i32 undef, ptr undef)
; CHECK-LLVM: %[[#PtrAnn2ClassB:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#PtrAnnClassB]], ptr @[[#StrStructB2]], ptr undef, i32 undef, ptr undef)

; CHECK-LLVM: %[[#Obj2ClassA:]] = alloca %class.A, align 4
; CHECK-LLVM: %[[#GepObj2ClassA:]] = getelementptr inbounds %class.A, ptr %[[#Obj2ClassA]], i32 0, i32 0
; CHECK-LLVM: %[[#PtrAnnObj2ClassA:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#GepObj2ClassA]], ptr @[[#StrObj2StructA]], ptr undef, i32 undef, ptr undef)
; CHECK-LLVM: %[[#PtrAnn2Obj2ClassA:]] = call ptr @llvm.ptr.annotation.p0.p0(ptr %[[#PtrAnnObj2ClassA]], ptr @[[#StrObj2StructA2]], ptr undef, i32 undef, ptr undef)


; CHECK-LLVM: %[[#CastClassAVal:]] = bitcast ptr %[[#PtrAnn2ClassAVal]] to ptr
; CHECK-LLVM: %[[#LoadClassAVal:]] = bitcast ptr %[[#CastClassAVal]] to ptr
; CHECK-LLVM: %[[#CallClassA:]] = load i32, ptr %[[#LoadClassAVal]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#CallClassA]])

; CHECK-LLVM: %[[#CastObj2ClassA:]] = bitcast ptr %[[#PtrAnn2Obj2ClassA]] to ptr
; CHECK-LLVM: %[[#LoadObj2ClassA:]] = bitcast ptr %[[#CastObj2ClassA]] to ptr
; CHECK-LLVM: %[[#CallObj2ClassA:]] = load i32, ptr %[[#LoadObj2ClassA]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#CallObj2ClassA]])

; CHECK-LLVM: %[[#CastMultiDec:]] = bitcast ptr %[[#PtrAnn3MultiDec]] to ptr
; CHECK-LLVM: %[[#LoadMultiDec:]] = bitcast ptr %[[#CastMultiDec]] to ptr
; CHECK-LLVM: %[[#CallClassAMultiDec:]] = load i32, ptr %[[#LoadMultiDec]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#CallClassAMultiDec]])

; CHECK-LLVM: %[[#CastClassAFieldB:]] = bitcast ptr %[[#PtrAnnClassAFieldB]] to ptr
; CHECK-LLVM: %[[#Cast2ClassAFieldB:]] = bitcast ptr %[[#CastClassAFieldB]] to ptr
; CHECK-LLVM: %[[#GEPClassB:]] = getelementptr inbounds %class.B, ptr %[[#Cast2ClassAFieldB]], i32 0, i32 0
; CHECK-LLVM: %[[#CastClassB:]] = bitcast ptr %[[#GEPClassB]] to ptr
; CHECK-LLVM: %[[#Cast2ClassB:]] = bitcast ptr %[[#CastClassB]] to ptr
; CHECK-LLVM: %[[#CallClassAFieldB:]] = load i32, ptr %[[#Cast2ClassB]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#CallClassAFieldB]])

; CHECK-LLVM: %[[#CastClassB:]] = bitcast ptr %[[#PtrAnn2ClassB]] to ptr
; CHECK-LLVM: %[[#Cast2ClassB:]] = bitcast ptr %[[#CastClassB]] to ptr
; CHECK-LLVM: %[[#CallClassB:]] = load i32, ptr %[[#Cast2ClassB]], align 4
; CHECK-LLVM: call spir_func void @_Z3fooi(i32 %[[#CallClassB]])

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

%class.A = type { i32, i32, %class.B }
%class.B = type { i32 }

@.str = private unnamed_addr constant [7 x i8] c"ClassA\00", section "llvm.metadata"
@.str.1 = private unnamed_addr constant [17 x i8] c"/app/example.cpp\00", section "llvm.metadata"
@.str.2 = private unnamed_addr constant [5 x i8] c"Dec1\00", section "llvm.metadata"
@.str.3 = private unnamed_addr constant [5 x i8] c"Dec2\00", section "llvm.metadata"
@.str.4 = private unnamed_addr constant [5 x i8] c"Dec3\00", section "llvm.metadata"
@.str.5 = private unnamed_addr constant [13 x i8] c"ClassAfieldB\00", section "llvm.metadata"
@.str.6 = private unnamed_addr constant [7 x i8] c"ClassB\00", section "llvm.metadata"

define dso_local noundef i32 @main() #0 {
%1 = alloca i32, align 4
%2 = alloca %class.A, align 4
%3 = alloca %class.B, align 4
%4 = alloca %class.A, align 4
store i32 0, ptr %1, align 4
%5 = getelementptr inbounds %class.A, ptr %2, i32 0, i32 0
%6 = call ptr @llvm.ptr.annotation.p0.p0(ptr %5, ptr @.str, ptr @.str.1, i32 11, ptr null)
%7 = load i32, ptr %6, align 4
call void @_Z3fooi(i32 noundef %7)
%8 = getelementptr inbounds %class.A, ptr %4, i32 0, i32 0
%9 = call ptr @llvm.ptr.annotation.p0.p0(ptr %8, ptr @.str, ptr @.str.1, i32 11, ptr null)
%10 = load i32, ptr %9, align 4
call void @_Z3fooi(i32 noundef %10)
%11 = getelementptr inbounds %class.A, ptr %2, i32 0, i32 1
%12 = call ptr @llvm.ptr.annotation.p0.p0(ptr %11, ptr @.str.2, ptr @.str.1, i32 12, ptr null)
%13 = call ptr @llvm.ptr.annotation.p0.p0(ptr %12, ptr @.str.3, ptr @.str.1, i32 12, ptr null)
%14 = call ptr @llvm.ptr.annotation.p0.p0(ptr %13, ptr @.str.4, ptr @.str.1, i32 12, ptr null)
%15 = load i32, ptr %14, align 4
call void @_Z3fooi(i32 noundef %15)
%16 = getelementptr inbounds %class.A, ptr %2, i32 0, i32 2
%17 = call ptr @llvm.ptr.annotation.p0.p0(ptr %16, ptr @.str.5, ptr @.str.1, i32 13, ptr null)
%18 = getelementptr inbounds %class.B, ptr %17, i32 0, i32 0
%19 = call ptr @llvm.ptr.annotation.p0.p0(ptr %18, ptr @.str.6, ptr @.str.1, i32 5, ptr null)
%20 = load i32, ptr %19, align 4
call void @_Z3fooi(i32 noundef %20)
%21 = getelementptr inbounds %class.B, ptr %3, i32 0, i32 0
%22 = call ptr @llvm.ptr.annotation.p0.p0(ptr %21, ptr @.str.6, ptr @.str.1, i32 5, ptr null)
%23 = load i32, ptr %22, align 4
call void @_Z3fooi(i32 noundef %23)
ret i32 0
}

declare void @_Z3fooi(i32 noundef) #2

declare ptr @llvm.ptr.annotation.p0.p0(ptr, ptr, ptr, i32, ptr) #3

attributes #0 = { mustprogress noinline norecurse optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nocallback nofree nosync nounwind speculatable willreturn memory(none) }
attributes #2 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #3 = { nocallback nofree nosync nounwind willreturn memory(inaccessiblemem: readwrite) }