-
Notifications
You must be signed in to change notification settings - Fork 223
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[SPV->LLVM] Fix global c/dtors type if SPV is from opaque type LLVM #2280
[SPV->LLVM] Fix global c/dtors type if SPV is from opaque type LLVM #2280
Conversation
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.
The PR is needed for both llvm_release_140 and llvm_release_150 branches since opaque pointer isn't enabled by default until LLVM 16. |
Error in CI in-tree windows build isn't related to this PR |
lib/SPIRV/SPIRVReader.cpp
Outdated
@@ -1331,6 +1331,39 @@ void SPIRVToLLVM::transFunctionPointerCallArgumentAttributes( | |||
} | |||
} | |||
|
|||
/// When spirv is generated from LLVM IR with opapque pointer enabled and then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
typo: opaque
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/SPIRV/SPIRVReader.cpp
Outdated
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to better understand the scope of the problem when we get an opaque pointer type rather than function
type, and so interpret it further like a i8* pointer when reverse translate it back to LLVM. Does this behavior manifest itself for ctor/dtor only, and that is caused by special treatment of ctor/dtor case in other places of the source code base? Or this is rather a wider problem that is not only about ctor/dtor?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree with your concern that this behavior could be a wider problem.
I was also thinking why this issue is exposed so late. My guess is that IGC who uses llvm-14 branch switched to use the open-source SPIRV-LLVM-Translator not long time ago, so some issues related to opaque types are not exposed yet.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctor/dtor use function pointer. This problem here is kind of special that LLVM IR verifier specifically requires that the type of the second element should be a function type rather than i8* type, see https://github.com/llvm/llvm-project/blob/248fba0cd806a0f6bf4b0f12979f2185f2bed111/llvm/lib/IR/Verifier.cpp#L798
Indeed, the issue this PR addresses might also be related to cases that a function pointer is used in the code, e.g. as an indirect function call. With the second commit of this PR, I verified that SPV from opaque pointer in tests in https://github.com/KhronosGroup/SPIRV-LLVM-Translator/tree/main/test/extensions/INTEL/SPV_INTEL_function_pointers could be translated to LLVM IR with typed pointer using SPIRV-LLVM-Translator llvm_release_140 branch. Therefore, function pointer handling seems good.
Other than function pointer, I don't know if opaque types handling in SPIRV-LLVM-Translator llvm_release_140 branch has more issues. As @LU-JOHN mentioned, we might need the whole SPIRVTypeScavenger.
lib/SPIRV/SPIRVReader.cpp
Outdated
unsigned NumEltsInArr = InitArr->getType()->getNumElements(); | ||
assert(NumEltsInArr && "array is empty"); | ||
auto *CS = cast<ConstantStruct>(InitArr->getAggregateElement(0u)); | ||
assert(CS->getType()->getNumElements() == 3 && "expect 3 elements in struct"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This new function is not added to validate, so I'm not sure you need new asserts here at all. It looks like the following logic can be just protected by if conditions, for example, to avoid adding implicit layer of validity checks into a new ad-hoc function that addresses ctor/dtor case. However, if other reviewers think that asserts are ok here and may remain as it is, I'd strongly suggest to consider better error messages, adding more context to the message to facilitate possible future testing/debugging efforts.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assert is to make sure StTy->getElementType(2)
and CS->getAggregateElement(2)
below are valid and the assert aligns with assert at https://github.com/llvm/llvm-project/blob/248fba0cd806a0f6bf4b0f12979f2185f2bed111/llvm/lib/IR/Verifier.cpp#L799
I'd strongly suggest to consider better error messages
done
lib/SPIRV/SPIRVReader.cpp
Outdated
if (Init && IsCtorOrDtor) { | ||
Initializer = dyn_cast<Constant>(transValue(Init, F, BB, false)); | ||
postProcGlobalCtorDtorTypeInit(Ty, Initializer); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's confusing to have this check separated from if-statement below, and to repeat the check for IsCtorOrDtor
twice, here and below.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the code into transGlobalCtorDtors
lib/SPIRV/SPIRVReader.cpp
Outdated
/// 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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function is defined to be void, as if it would cause a side effect rather than convert one statement into another, as it actually does by reassigning an argument Initializer
. It would require more time to understand the underlying logic comparing to line 1595 style of
Initializer = dyn_cast<Constant>(transValue(Init, F, BB, false));
.
In my opinion, it's better to return a pointer than to change the *&
argument.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/SPIRV/SPIRVReader.cpp
Outdated
BV->getName() == "llvm.global_dtors"); | ||
if (Init && IsCtorOrDtor) { | ||
Initializer = dyn_cast<Constant>(transValue(Init, F, BB, false)); | ||
postProcGlobalCtorDtorTypeInit(Ty, Initializer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As in the comment to line 1340, it's not obvious that the function changes Initializer value. Initializer =
would work better.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
We may want to look at lib/SPIRV/SPIRVTypeScavenger.cpp, which was written to:
It would be better if the SPIRVTypeScavenger was able to get the correct type rather than adding custom code for global c/dtors. @jcranmer-intel can you comment if this issue is something that is better handled in the type scavenger? |
Yeah we probably need to back-port SPIRVTypeScavenger to llvm_release_140 and llvm_release_150 branches. I'm not familiar with the code in SPIRVTypeScavenger. @jcranmer-intel what's your opinion? edit: it seems @jcranmer-intel is on holiday now. |
lib/SPIRV/SPIRVReader.cpp
Outdated
/// 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. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ctor/dtor use function pointer. This problem here is kind of special that LLVM IR verifier specifically requires that the type of the second element should be a function type rather than i8* type, see https://github.com/llvm/llvm-project/blob/248fba0cd806a0f6bf4b0f12979f2185f2bed111/llvm/lib/IR/Verifier.cpp#L798
Indeed, the issue this PR addresses might also be related to cases that a function pointer is used in the code, e.g. as an indirect function call. With the second commit of this PR, I verified that SPV from opaque pointer in tests in https://github.com/KhronosGroup/SPIRV-LLVM-Translator/tree/main/test/extensions/INTEL/SPV_INTEL_function_pointers could be translated to LLVM IR with typed pointer using SPIRV-LLVM-Translator llvm_release_140 branch. Therefore, function pointer handling seems good.
Other than function pointer, I don't know if opaque types handling in SPIRV-LLVM-Translator llvm_release_140 branch has more issues. As @LU-JOHN mentioned, we might need the whole SPIRVTypeScavenger.
lib/SPIRV/SPIRVReader.cpp
Outdated
/// 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) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/SPIRV/SPIRVReader.cpp
Outdated
@@ -1331,6 +1331,39 @@ void SPIRVToLLVM::transFunctionPointerCallArgumentAttributes( | |||
} | |||
} | |||
|
|||
/// When spirv is generated from LLVM IR with opapque pointer enabled and then |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/SPIRV/SPIRVReader.cpp
Outdated
unsigned NumEltsInArr = InitArr->getType()->getNumElements(); | ||
assert(NumEltsInArr && "array is empty"); | ||
auto *CS = cast<ConstantStruct>(InitArr->getAggregateElement(0u)); | ||
assert(CS->getType()->getNumElements() == 3 && "expect 3 elements in struct"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The assert is to make sure StTy->getElementType(2)
and CS->getAggregateElement(2)
below are valid and the assert aligns with assert at https://github.com/llvm/llvm-project/blob/248fba0cd806a0f6bf4b0f12979f2185f2bed111/llvm/lib/IR/Verifier.cpp#L799
I'd strongly suggest to consider better error messages
done
lib/SPIRV/SPIRVReader.cpp
Outdated
BV->getName() == "llvm.global_dtors"); | ||
if (Init && IsCtorOrDtor) { | ||
Initializer = dyn_cast<Constant>(transValue(Init, F, BB, false)); | ||
postProcGlobalCtorDtorTypeInit(Ty, Initializer); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
done
lib/SPIRV/SPIRVReader.cpp
Outdated
if (Init && IsCtorOrDtor) { | ||
Initializer = dyn_cast<Constant>(transValue(Init, F, BB, false)); | ||
postProcGlobalCtorDtorTypeInit(Ty, Initializer); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I moved the code into transGlobalCtorDtors
transGlobalCtorDtors(BV); | ||
else if (BV->getStorageClass() != StorageClassFunction) | ||
transValue(BV, nullptr, nullptr); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the change of order here is to fix https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/test/extensions/INTEL/SPV_INTEL_function_pointers/global_ctor_dtor.ll and https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/main/test/extensions/INTEL/SPV_INTEL_function_pointers/global_ctor_dtor_addrspace.ll in the case SPV from opaque pointer LLVM IR is translated to typed pointer LLVM IR.
@LU-JOHN back-porting SPIRVTypeScavenger requires some efforts that probably can't be done in a short time. Can we proceed with PR at the moment since the fix is urgently needed? I can file an issue for back-porting SPIRVTypeScavenger . |
@VyacheslavLevytskyy @LU-JOHN please also note that in main branch SPIRVTypeScavenger is only used for LLVM->SPIRV translation. The scope here is SPIRV->LLVM. So it needs efforts to evaluate if SPIRVTypeScavenger can be modified to suite this scope. |
Hi @wenju-he . I see you have addressed all of @VyacheslavLevytskyy concerns. Can you look at while "In-tree build & tests / Windows" is failing? |
@LU-JOHN my local windows in-tree build is successful.
Linux in-tree build is checking out correct commit:
It isn't obvious to me which part of https://github.com/KhronosGroup/SPIRV-LLVM-Translator/blob/llvm_release_140/.github/workflows/check-in-tree-build.yml is wrong. |
submitted an issue: #2284 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
@wenju-he wants it to be merged asap. @VyacheslavLevytskyy, @jcranmer-intel, please, review post-commit. @wenju-he, be ready to address post-commit comments in follow-up PRs. |
Thank you @bader |
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.