From e7bfb375d2a14eaa8ad2dc9aa1cfb46fe4e9f4c3 Mon Sep 17 00:00:00 2001 From: Wenju He Date: Thu, 21 Dec 2023 13:30:56 +0800 Subject: [PATCH 1/3] [SPV->LLVM] Fix global c/dtors type if SPV is from opaque type LLVM When SPV is generated from LLVM IR with opaque pointer enabled, ctor function in global ctors has opaque pointer type rather than function type. When translating the SPV back to LLVM IR with typed pointer like in LLVM 14, ctor function type is casted to i8* pointer in the global ctors initializer. This results in error in LLVM verifier. This PR fixes the issue by removing the cast. --- lib/SPIRV/SPIRVReader.cpp | 53 +++++++++++++++--- .../global_ctor_dtor_opaque.spt | 54 +++++++++++++++++++ 2 files changed, 101 insertions(+), 6 deletions(-) create mode 100644 test/transcoding/SPV_INTEL_function_pointers/global_ctor_dtor_opaque.spt diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 22c3203cda..2d0b5a24a6 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -1331,6 +1331,39 @@ void SPIRVToLLVM::transFunctionPointerCallArgumentAttributes( } } +/// When spirv is generated from LLVM IR with opapque pointer enabled and then +/// the spirv is translated to LLVM IR with typed pointer, function pointer is +/// casted to i8* type in \p Ty and \p Initializer, which causes error in LLVM +/// IR verifier. E.g. +/// [1 x %0][%0 { i32 1, i8* bitcast (void ()* @ctor to i8*), i8* null }] +/// This function removes the cast so that LLVM IR is valid. +static void postProcGlobalCtorDtorTypeInit(Type *&Ty, Constant *&Initializer) { + auto *InitArr = cast(Initializer); + unsigned NumEltsInArr = InitArr->getType()->getNumElements(); + assert(NumEltsInArr && "array is empty"); + auto *CS = cast(InitArr->getAggregateElement(0u)); + assert(CS->getType()->getNumElements() == 3 && "expect 3 elements in struct"); + auto *Elt1 = CS->getAggregateElement(1); + if (!isa(Elt1)) + return; + auto *StTy = CS->getType(); + auto *NewStTy = StructType::create(Ty->getContext(), + {StTy->getElementType(0), + Elt1->stripPointerCasts()->getType(), + StTy->getElementType(2)}, + StTy->getName(), StTy->isPacked()); + Ty = ArrayType::get(NewStTy, NumEltsInArr); + SmallVector ArrElts; + for (unsigned I = 0; I < NumEltsInArr; ++I) { + auto *CS = cast(InitArr->getAggregateElement(I)); + ArrElts.push_back(ConstantStruct::get( + NewStTy, {CS->getAggregateElement(0u), + CS->getAggregateElement(1)->stripPointerCasts(), + CS->getAggregateElement(2)})); + } + Initializer = ConstantArray::get(cast(Ty), ArrElts); +} + /// For instructions, this function assumes they are created in order /// and appended to the given basic block. An instruction may use a /// instruction from another BB which has not been translated. Such @@ -1556,20 +1589,28 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, SPIRVBuiltinVariableKind BVKind; if (BVar->isBuiltin(&BVKind)) BV->setName(prefixSPIRVName(SPIRVBuiltInNameMap::map(BVKind))); + bool IsCtorOrDtor = (BV->getName() == "llvm.global_ctors" || + BV->getName() == "llvm.global_dtors"); + if (Init && IsCtorOrDtor) { + Initializer = dyn_cast(transValue(Init, F, BB, false)); + postProcGlobalCtorDtorTypeInit(Ty, Initializer); + } auto LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy, /*Initializer=*/nullptr, BV->getName(), 0, GlobalVariable::NotThreadLocal, AddrSpace); auto Res = mapValue(BV, LVar); - if (Init) - Initializer = dyn_cast(transValue(Init, F, BB, false)); - else if (LinkageTy == GlobalValue::CommonLinkage) + if (Init) { + if (!IsCtorOrDtor) + Initializer = dyn_cast(transValue(Init, F, BB, false)); + } else if (LinkageTy == GlobalValue::CommonLinkage) { // In LLVM, variables with common linkage type must be initialized to 0. Initializer = Constant::getNullValue(Ty); - else if (BS == SPIRVStorageClassKind::StorageClassWorkgroup) + } else if (BS == SPIRVStorageClassKind::StorageClassWorkgroup) { Initializer = dyn_cast(UndefValue::get(Ty)); - else if ((LinkageTy != GlobalValue::ExternalLinkage) && - (BS == SPIRVStorageClassKind::StorageClassCrossWorkgroup)) + } else if ((LinkageTy != GlobalValue::ExternalLinkage) && + (BS == SPIRVStorageClassKind::StorageClassCrossWorkgroup)) { Initializer = Constant::getNullValue(Ty); + } LVar->setUnnamedAddr((IsConst && Ty->isArrayTy() && Ty->getArrayElementType()->isIntegerTy(8)) diff --git a/test/transcoding/SPV_INTEL_function_pointers/global_ctor_dtor_opaque.spt b/test/transcoding/SPV_INTEL_function_pointers/global_ctor_dtor_opaque.spt new file mode 100644 index 0000000000..ddf4bd1ca9 --- /dev/null +++ b/test/transcoding/SPV_INTEL_function_pointers/global_ctor_dtor_opaque.spt @@ -0,0 +1,54 @@ +119734787 65792 393230 22 0 +2 Capability Addresses +2 Capability Linkage +2 Capability Kernel +2 Capability Int64 +2 Capability Int8 +2 Capability FunctionPointersINTEL +8 Extension "SPV_INTEL_function_pointers" +5 ExtInstImport 1 "OpenCL.std" +3 MemoryModel 2 2 +3 Source 0 0 +7 Name 14 "asan.module_ctor" +7 Name 15 "asan.module_ctor" +7 Name 20 "llvm.global_ctors" + +9 Decorate 20 LinkageAttributes "llvm.global_ctors" Export +4 TypeInt 3 32 0 +4 TypeInt 4 8 0 +4 TypeInt 6 64 0 +5 Constant 6 7 1 0 +4 Constant 3 10 1 +4 TypePointer 5 7 4 +5 TypeStruct 2 3 5 5 + +4 TypeArray 8 2 7 +4 TypePointer 9 7 8 +2 TypeVoid 11 +3 TypeFunction 12 11 +4 TypePointer 13 7 12 +4 ConstantFunctionPointerINTEL 13 15 14 +5 SpecConstantOp 5 16 124 15 +3 ConstantNull 5 17 +6 ConstantComposite 2 18 10 16 17 + +4 ConstantComposite 8 19 18 + +5 Variable 9 20 7 19 + + + +5 Function 11 14 0 12 + +2 Label 21 +1 Return + +1 FunctionEnd + +; RUN: llvm-spirv %s --to-binary -o %t.spv +; RUN: llvm-spirv -r %t.spv -o %t.bc +; RUN: llvm-dis %t.bc -o %t.ll +; RUN: FileCheck --input-file=%t.ll %s --check-prefix=CHECK-LLVM + +; CHECK-LLVM: [[TY:%.*]] = type { i32, void ()*, i8* } +; CHECK-LLVM: @llvm.global_ctors = appending global [1 x [[TY]]] [[[TY]] { i32 1, void ()* @asan.module_ctor, i8* null }] From fa4372c608ceac6162ab47f2fa9603768af97db8 Mon Sep 17 00:00:00 2001 From: Wenju He Date: Fri, 22 Dec 2023 15:07:09 +0800 Subject: [PATCH 2/3] move the new code into transGlobalCtorDtors --- lib/SPIRV/SPIRVReader.cpp | 115 +++++++++++++++++++------------------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 2d0b5a24a6..69f238192e 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -1331,39 +1331,6 @@ void SPIRVToLLVM::transFunctionPointerCallArgumentAttributes( } } -/// When spirv is generated from LLVM IR with opapque pointer enabled and then -/// the spirv is translated to LLVM IR with typed pointer, function pointer is -/// casted to i8* type in \p Ty and \p Initializer, which causes error in LLVM -/// IR verifier. E.g. -/// [1 x %0][%0 { i32 1, i8* bitcast (void ()* @ctor to i8*), i8* null }] -/// This function removes the cast so that LLVM IR is valid. -static void postProcGlobalCtorDtorTypeInit(Type *&Ty, Constant *&Initializer) { - auto *InitArr = cast(Initializer); - unsigned NumEltsInArr = InitArr->getType()->getNumElements(); - assert(NumEltsInArr && "array is empty"); - auto *CS = cast(InitArr->getAggregateElement(0u)); - assert(CS->getType()->getNumElements() == 3 && "expect 3 elements in struct"); - auto *Elt1 = CS->getAggregateElement(1); - if (!isa(Elt1)) - return; - auto *StTy = CS->getType(); - auto *NewStTy = StructType::create(Ty->getContext(), - {StTy->getElementType(0), - Elt1->stripPointerCasts()->getType(), - StTy->getElementType(2)}, - StTy->getName(), StTy->isPacked()); - Ty = ArrayType::get(NewStTy, NumEltsInArr); - SmallVector ArrElts; - for (unsigned I = 0; I < NumEltsInArr; ++I) { - auto *CS = cast(InitArr->getAggregateElement(I)); - ArrElts.push_back(ConstantStruct::get( - NewStTy, {CS->getAggregateElement(0u), - CS->getAggregateElement(1)->stripPointerCasts(), - CS->getAggregateElement(2)})); - } - Initializer = ConstantArray::get(cast(Ty), ArrElts); -} - /// For instructions, this function assumes they are created in order /// and appended to the given basic block. An instruction may use a /// instruction from another BB which has not been translated. Such @@ -1589,28 +1556,20 @@ Value *SPIRVToLLVM::transValueWithoutDecoration(SPIRVValue *BV, Function *F, SPIRVBuiltinVariableKind BVKind; if (BVar->isBuiltin(&BVKind)) BV->setName(prefixSPIRVName(SPIRVBuiltInNameMap::map(BVKind))); - bool IsCtorOrDtor = (BV->getName() == "llvm.global_ctors" || - BV->getName() == "llvm.global_dtors"); - if (Init && IsCtorOrDtor) { - Initializer = dyn_cast(transValue(Init, F, BB, false)); - postProcGlobalCtorDtorTypeInit(Ty, Initializer); - } auto LVar = new GlobalVariable(*M, Ty, IsConst, LinkageTy, /*Initializer=*/nullptr, BV->getName(), 0, GlobalVariable::NotThreadLocal, AddrSpace); auto Res = mapValue(BV, LVar); - if (Init) { - if (!IsCtorOrDtor) - Initializer = dyn_cast(transValue(Init, F, BB, false)); - } else if (LinkageTy == GlobalValue::CommonLinkage) { + if (Init) + Initializer = dyn_cast(transValue(Init, F, BB, false)); + else if (LinkageTy == GlobalValue::CommonLinkage) // In LLVM, variables with common linkage type must be initialized to 0. Initializer = Constant::getNullValue(Ty); - } else if (BS == SPIRVStorageClassKind::StorageClassWorkgroup) { + else if (BS == SPIRVStorageClassKind::StorageClassWorkgroup) Initializer = dyn_cast(UndefValue::get(Ty)); - } else if ((LinkageTy != GlobalValue::ExternalLinkage) && - (BS == SPIRVStorageClassKind::StorageClassCrossWorkgroup)) { + else if ((LinkageTy != GlobalValue::ExternalLinkage) && + (BS == SPIRVStorageClassKind::StorageClassCrossWorkgroup)) Initializer = Constant::getNullValue(Ty); - } LVar->setUnnamedAddr((IsConst && Ty->isArrayTy() && Ty->getArrayElementType()->isIntegerTy(8)) @@ -3327,10 +3286,11 @@ bool SPIRVToLLVM::translate() { for (unsigned I = 0, E = BM->getNumVariables(); I != E; ++I) { auto *BV = BM->getVariable(I); - if (BV->getStorageClass() != StorageClassFunction) - transValue(BV, nullptr, nullptr); - else + if (BV->getName() == "llvm.global_ctors" || + BV->getName() == "llvm.global_dtors") transGlobalCtorDtors(BV); + else if (BV->getStorageClass() != StorageClassFunction) + transValue(BV, nullptr, nullptr); } // Then translate all debug instructions. @@ -3809,13 +3769,56 @@ bool SPIRVToLLVM::transDecoration(SPIRVValue *BV, Value *V) { return true; } -void SPIRVToLLVM::transGlobalCtorDtors(SPIRVVariable *BV) { - if (BV->getName() != "llvm.global_ctors" && - BV->getName() != "llvm.global_dtors") - return; +/// When spirv is generated from LLVM IR with opaque pointer enabled and then +/// the spirv is translated to LLVM IR with typed pointer, function pointer is +/// casted to i8* type in GlobalVariable \p GV, which causes error in LLVM IR +/// verifier. E.g. +/// [1 x %0][%0 { i32 1, i8* bitcast (void ()* @ctor to i8*), i8* null }] +/// This function removes the cast so that LLVM IR is valid. +static GlobalVariable *mutateGlobalCtorDtors(GlobalVariable *GV) { + if (!GV->hasInitializer()) + return GV; + auto *InitArr = cast(GV->getInitializer()); + unsigned NumEltsInArr = InitArr->getType()->getNumElements(); + if (NumEltsInArr == 0) + return GV; + auto *CS = cast(InitArr->getAggregateElement(0u)); + auto *Elt1 = CS->getAggregateElement(1); + if (!isa(Elt1)) + return GV; + + auto *STy = CS->getType(); + assert(STy->getNumElements() == 3 && + "expect 3 fields in global variable element struct type"); + auto *NewSTy = StructType::create(GV->getContext(), + {STy->getElementType(0), + Elt1->stripPointerCasts()->getType(), + STy->getElementType(2)}, + STy->getName(), STy->isPacked()); + auto *NewTy = ArrayType::get(NewSTy, NumEltsInArr); + SmallVector ArrElts; + for (unsigned I = 0; I < NumEltsInArr; ++I) { + auto *CS = cast(InitArr->getAggregateElement(I)); + ArrElts.push_back(ConstantStruct::get( + NewSTy, {CS->getAggregateElement(0u), + CS->getAggregateElement(1)->stripPointerCasts(), + CS->getAggregateElement(2)})); + } + auto *NewInitializer = ConstantArray::get(NewTy, ArrElts); + auto *NewGV = new GlobalVariable( + *GV->getParent(), NewTy, GV->isConstant(), GV->getLinkage(), + NewInitializer, "", 0, GV->getThreadLocalMode(), GV->getAddressSpace(), + GV->isExternallyInitialized()); + NewGV->copyAttributesFrom(GV); + NewGV->takeName(GV); + GV->eraseFromParent(); + return NewGV; +} - Value *V = transValue(BV, nullptr, nullptr); - cast(V)->setLinkage(GlobalValue::AppendingLinkage); +void SPIRVToLLVM::transGlobalCtorDtors(SPIRVVariable *BV) { + GlobalVariable *V = cast(transValue(BV, nullptr, nullptr)); + V = mutateGlobalCtorDtors(V); + V->setLinkage(GlobalVariable::AppendingLinkage); } void SPIRVToLLVM::createCXXStructor(const char *ListName, From 050a9c77bfdc00b65ba39a9cdf2392d391155a5d Mon Sep 17 00:00:00 2001 From: Wenju He Date: Fri, 22 Dec 2023 15:16:13 +0800 Subject: [PATCH 3/3] auto --- lib/SPIRV/SPIRVReader.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/SPIRV/SPIRVReader.cpp b/lib/SPIRV/SPIRVReader.cpp index 69f238192e..e1f50b6854 100644 --- a/lib/SPIRV/SPIRVReader.cpp +++ b/lib/SPIRV/SPIRVReader.cpp @@ -3816,7 +3816,7 @@ static GlobalVariable *mutateGlobalCtorDtors(GlobalVariable *GV) { } void SPIRVToLLVM::transGlobalCtorDtors(SPIRVVariable *BV) { - GlobalVariable *V = cast(transValue(BV, nullptr, nullptr)); + auto *V = cast(transValue(BV, nullptr, nullptr)); V = mutateGlobalCtorDtors(V); V->setLinkage(GlobalVariable::AppendingLinkage); }