Repository navigation
Conversation
…tead of __cxa_allocate_exception call
illuhad
left a comment
There was a problem hiding this comment.
I've only read the beginning of the pass -- looks like this needs some general cleanups first :)
Let me know once it's ready to review.
| */ | ||
| // SPDX-License-Identifier: BSD-2-Clause | ||
| #ifndef ACPP_REFLECTION_EXCEPTION_TO_ASSERTION_PASS_HPP | ||
| #define ACPP_REFLECTION_EXCEPTION_TO_ASSERTION_PASS_HPP |
There was a problem hiding this comment.
Why is it framed as part of reflection and in reflection subdirectory? I don't think it has anything to do with those. Probably it should go into compiler/sscp?
|
|
||
| static const char* CXAThrow = "__cxa_throw"; | ||
| static const char* ACPPSSCPAssertFail = "__acpp_sscp_assert_fail"; | ||
| //static const char* GlibcxxAssertFailBuiltinName = "__acpp_sscp_glibcxx_assert_fail"; |
There was a problem hiding this comment.
I don't think we need this -> remove
| #ifdef HIPSYCL_WITH_DEVICE_EXCEPTIONS | ||
| #include "hipSYCL/compiler/reflection/ExceptionToAssertionPass.hpp" | ||
| // #include "hipSYCL/compiler/reflection/FunctionNameExtractionPass.hpp" | ||
| #endif |
There was a problem hiding this comment.
Why do we need this include here? The pass is only used inside TargetSeparationPass, right?
| if(NOT DEFINED HIPSYCL_DEBUG_LEVEL) | ||
| message(FATAL_ERROR "HIPSYCL_DEBUG_LEVEL was not defined by the top-level CMakeLists.txt.") | ||
| if(NOT HIPSYCL_DEBUG_LEVEL) | ||
| set(HIPSYCL_DEBUG_LEVEL 2 CACHE INTEGER) |
There was a problem hiding this comment.
This seems like a change for debugging purposes - please remove from this PR!
|
|
||
| set(DEVICE_EXCEPTIONS | ||
| reflection/ExceptionToAssertionPass.cpp) | ||
| add_definitions(-DHIPSYCL_WITH_DEVICE_EXCEPTIONS) |
There was a problem hiding this comment.
Just add it to the existing SSCP_COMPILER sources above. No need to create something new and even have a macro for it.
| // detect if C++ throws occur in source code | ||
| llvm::Function* oldcxaThrow = nullptr; | ||
| for(auto& F : M) | ||
| if(F.getName().contains(CXAThrow)) { |
There was a problem hiding this comment.
Do we need contains or can we just compare for equality?
| oldcxaThrow = &F; | ||
| } | ||
| if (!oldcxaThrow) { | ||
| llvm::errs() << "Function " << CXAThrow << " not found. Assuming there is no use of exceptions in source code.\n"; |
There was a problem hiding this comment.
Error messages should be prefixed with the pass that produces them. E.g. llvm::errs() << "ExceptionToAssertionPas: ..."
Also, why would we even error in this case? Isn't it expected that in most cases there's no exception in device code?
| for(auto& F : M) | ||
| if(F.getName().contains(CXAThrow)) { | ||
| oldcxaThrow = &F; | ||
| } |
There was a problem hiding this comment.
That whole segment I think can be replaced with M.getFunction(CXAThrow)
| #if LLVM_VERSION_MAJOR >= 16 | ||
| llvmCharType = llvm::PointerType::get(M.getContext(), 0); | ||
| #else | ||
| llvmCharType = llvm::PointerType::get(llvm::Type::getInt8Ty(M.getContext()), 0); |
There was a problem hiding this comment.
No indentation inside preprocessor if/else
| llvmCharType = llvm::PointerType::get(llvm::Type::getInt8Ty(M.getContext()), 0); | ||
| #endif | ||
| if(auto* F = M.getFunction(ACPPSSCPAssertFail)) | ||
| ACPPSSCPAssertFailDeclaration=F; |
There was a problem hiding this comment.
That if seems unnecessary.... why not llvm::Function* ACPPSSCPAssertFailDeclaration = M.getFunction(...) ?
|
Also, please add some LIT test cases for this feature :-) |
…unction now to avoid code replica across passes touching device assertions, ADDED: LIT tests for ExceptionToAssertionPass
Update this feature branch with changes from main development branch.
…ce of __cxa_throw symbols directly using the hcf-tool, backend-independently
|
Code has been slightly restructured. PR is ready for review. |
| } | ||
| } | ||
| } | ||
| if(auto *Call = llvm::dyn_cast<llvm::CallInst>(&Inst)) { |
There was a problem hiding this comment.
This indentation is misleading. It looks like the CallInst check is inside the InvokeInst check (which would not make any sense), but I think it's actually outside.
| } | ||
| // replace all uses of invoke @__cxa_throw with unreachable instruction and erase the invoke instruction | ||
| for (int i=0; i<cxaThrowInvokes.size(); i++) { | ||
| UnreachableForCXAInvokes[i]->replaceAllUsesWith(cxaThrowInvokes[i]); |
There was a problem hiding this comment.
Isn't this the wrong way around? This looks like you are replacing the unreachables with the cxa throw invokes.
Also, it feels a little overcomplicated to store the unreachables in an array. Couldn't you just:
auto *Unreachable = new llvm::UnreachableInst(M.getContext(), cxaThrowInvokes[i]);
cxaThrowInvokes[i]->replaceAllUsesWith(Unreachable);| ACPPSSCPAssertFailArgs.push_back(builder.CreateGlobalStringPtr(functionName)); | ||
|
|
||
| auto *Call = llvm::CallInst::Create(DeviceAssertPass::getAssertFailBuiltin(M), ACPPSSCPAssertFailArgs); | ||
| Call->insertBefore(cxaThrowCalls[i]); |
There was a problem hiding this comment.
This section is duplicated from the invoke case...can't you create a function with this code to share the code between both cases?
|
|
||
|
|
||
|
|
||
|
|
There was a problem hiding this comment.
Do we need all those empty lines? :D
| cxaAllocExcArgs.push_back(*cxaAllocExcIt); | ||
| i++; | ||
| } | ||
| cxaAllocExcCallsArgs.push_back(cxaAllocExcArgs); |
There was a problem hiding this comment.
Instead of copying out all arguments, can't you just directly access the first argument with callInst->getArgOperand(0)?
| static_cast<int>(ConstInt->getZExtValue())); | ||
| } | ||
| } | ||
| auto* allocainstruction = new llvm::AllocaInst(cxaAllocExcArgs[0]->getType(), 0, allocSize, "exception_alloca", Call); |
There was a problem hiding this comment.
This identifier name does not look like LLVM coding style to me.
| //remove all calls to __cxa_allocate_exception | ||
| for (auto *Call : cxaAllocExcCalls2) { | ||
| if(Call!=nullptr) | ||
| Call->eraseFromParent(); |
There was a problem hiding this comment.
Do we really need a separate iteration to erase? Can't we combine that with the RAUW step?
| set(REFLECTION_BUILTINS "") | ||
| endif() |
There was a problem hiding this comment.
This block seems to be copy-pasted from above....please remove!
| ${SSCP_COMPILER} | ||
| ${STDPAR_COMPILER} | ||
| ${REFLECTION_BUILTINS} | ||
| ${DEVICE_EXCEPTIONS} |
| }); | ||
| q.wait(); | ||
| std::cout << *data << std::endl; | ||
| //CHECK-NOT: CXATHROWHIT |
There was a problem hiding this comment.
It would be good if we could also check for ACPPASSERTFAILHIT here, which I think you're not actually using anywhere?
Added compatibility for code possibly throwing exceptions on device. In LLVM C++ thrown exceptions are handled with the functions
__cxa_throwand__cxa_allocate_exceptionin LLVM IR. However if we run such code on GPU devices, we may not have access to appropriate implementations of these exception functions.This PR suggests a compilation pass, which parses device IR for calls and invokes to
__cxa_throwand replacing them with an assertion__acpp_sscp_assert_fail. Then, the function__cxa_allocate_exceptionis removed altogether as any allocation of exception structures would no longer be required.Additionally, the fix of a typo in one of the CI workflow files is suggested.