From 21fb19f3b5d572f608e959af895d781b9b24fbbd Mon Sep 17 00:00:00 2001 From: Anutosh Bhat Date: Sat, 26 Apr 2025 12:16:26 +0530 Subject: [clang-repl] : Fix clang-repl crash with --cuda flag (#136404) `clang-repl --cuda` was previously crashing with a segmentation fault, instead of reporting a clean error ``` (base) anutosh491@Anutoshs-MacBook-Air bin % ./clang-repl --cuda #0 0x0000000111da4fbc llvm::sys::PrintStackTrace(llvm::raw_ostream&, int) (/opt/local/libexec/llvm-20/lib/libLLVM.dylib+0x150fbc) #1 0x0000000111da31dc llvm::sys::RunSignalHandlers() (/opt/local/libexec/llvm-20/lib/libLLVM.dylib+0x14f1dc) #2 0x0000000111da5628 SignalHandler(int) (/opt/local/libexec/llvm-20/lib/libLLVM.dylib+0x151628) #3 0x000000019b242de4 (/usr/lib/system/libsystem_platform.dylib+0x180482de4) #4 0x0000000107f638d0 clang::IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(std::__1::unique_ptr>, clang::CompilerInstance&, llvm::IntrusiveRefCntPtr, llvm::Error&, std::__1::list> const&) (/opt/local/libexec/llvm-20/lib/libclang-cpp.dylib+0x216b8d0) #5 0x0000000107f638d0 clang::IncrementalCUDADeviceParser::IncrementalCUDADeviceParser(std::__1::unique_ptr>, clang::CompilerInstance&, llvm::IntrusiveRefCntPtr, llvm::Error&, std::__1::list> const&) (/opt/local/libexec/llvm-20/lib/libclang-cpp.dylib+0x216b8d0) #6 0x0000000107f6bac8 clang::Interpreter::createWithCUDA(std::__1::unique_ptr>, std::__1::unique_ptr>) (/opt/local/libexec/llvm-20/lib/libclang-cpp.dylib+0x2173ac8) #7 0x000000010206f8a8 main (/opt/local/libexec/llvm-20/bin/clang-repl+0x1000038a8) #8 0x000000019ae8c274 Segmentation fault: 11 ``` The underlying issue was that the `DeviceCompilerInstance` (used for device-side CUDA compilation) was never initialized with a `Sema`, which is required before constructing the `IncrementalCUDADeviceParser`. https://github.com/llvm/llvm-project/blob/89687e6f383b742a3c6542dc673a84d9f82d02de/clang/lib/Interpreter/DeviceOffload.cpp#L32 https://github.com/llvm/llvm-project/blob/89687e6f383b742a3c6542dc673a84d9f82d02de/clang/lib/Interpreter/IncrementalParser.cpp#L31 Unlike the host-side `CompilerInstance` which runs `ExecuteAction` inside the Interpreter constructor (thereby setting up Sema), the device-side CI was passed into the parser uninitialized, leading to an assertion or crash when accessing its internals. To fix this, I refactored the `Interpreter::create` method to include an optional `DeviceCI` parameter. If provided, we know we need to take care of this instance too. Only then do we construct the `IncrementalCUDADeviceParser`. --- clang/lib/Interpreter/Interpreter.cpp | 59 ++++++++++++++++++++++++++--------- 1 file changed, 44 insertions(+), 15 deletions(-) (limited to 'clang/lib/Interpreter/Interpreter.cpp') diff --git a/clang/lib/Interpreter/Interpreter.cpp b/clang/lib/Interpreter/Interpreter.cpp index 45fa583..e2950e17 100644 --- a/clang/lib/Interpreter/Interpreter.cpp +++ b/clang/lib/Interpreter/Interpreter.cpp @@ -481,20 +481,34 @@ Interpreter::createWithCUDA(std::unique_ptr CI, OverlayVFS->pushOverlay(IMVFS); CI->createFileManager(OverlayVFS); - auto Interp = Interpreter::create(std::move(CI)); - if (auto E = Interp.takeError()) - return std::move(E); + llvm::Expected> InterpOrErr = + Interpreter::create(std::move(CI)); + if (!InterpOrErr) + return InterpOrErr; + + std::unique_ptr Interp = std::move(*InterpOrErr); llvm::Error Err = llvm::Error::success(); - auto DeviceParser = std::make_unique( - std::move(DCI), *(*Interp)->getCompilerInstance(), IMVFS, Err, - (*Interp)->PTUs); + llvm::LLVMContext &LLVMCtx = *Interp->TSCtx->getContext(); + + auto DeviceAct = + std::make_unique(*DCI, LLVMCtx, Err, *Interp); + if (Err) return std::move(Err); - (*Interp)->DeviceParser = std::move(DeviceParser); + Interp->DeviceAct = std::move(DeviceAct); + + DCI->ExecuteAction(*Interp->DeviceAct); + + auto DeviceParser = std::make_unique( + std::move(DCI), *Interp->getCompilerInstance(), IMVFS, Err, Interp->PTUs); + + if (Err) + return std::move(Err); - return Interp; + Interp->DeviceParser = std::move(DeviceParser); + return std::move(Interp); } const CompilerInstance *Interpreter::getCompilerInstance() const { @@ -532,15 +546,17 @@ size_t Interpreter::getEffectivePTUSize() const { PartialTranslationUnit & Interpreter::RegisterPTU(TranslationUnitDecl *TU, - std::unique_ptr M /*={}*/) { + std::unique_ptr M /*={}*/, + IncrementalAction *Action) { PTUs.emplace_back(PartialTranslationUnit()); PartialTranslationUnit &LastPTU = PTUs.back(); LastPTU.TUPart = TU; if (!M) - M = GenModule(); + M = GenModule(Action); - assert((!getCodeGen() || M) && "Must have a llvm::Module at this point"); + assert((!getCodeGen(Action) || M) && + "Must have a llvm::Module at this point"); LastPTU.TheModule = std::move(M); LLVM_DEBUG(llvm::dbgs() << "compile-ptu " << PTUs.size() - 1 @@ -560,6 +576,16 @@ Interpreter::Parse(llvm::StringRef Code) { llvm::Expected DeviceTU = DeviceParser->Parse(Code); if (auto E = DeviceTU.takeError()) return std::move(E); + + RegisterPTU(*DeviceTU, nullptr, DeviceAct.get()); + + llvm::Expected PTX = DeviceParser->GeneratePTX(); + if (!PTX) + return PTX.takeError(); + + llvm::Error Err = DeviceParser->GenerateFatbinary(); + if (Err) + return std::move(Err); } // Tell the interpreter sliently ignore unused expressions since value @@ -736,9 +762,10 @@ llvm::Error Interpreter::LoadDynamicLibrary(const char *name) { return llvm::Error::success(); } -std::unique_ptr Interpreter::GenModule() { +std::unique_ptr +Interpreter::GenModule(IncrementalAction *Action) { static unsigned ID = 0; - if (CodeGenerator *CG = getCodeGen()) { + if (CodeGenerator *CG = getCodeGen(Action)) { // Clang's CodeGen is designed to work with a single llvm::Module. In many // cases for convenience various CodeGen parts have a reference to the // llvm::Module (TheModule or Module) which does not change when a new @@ -760,8 +787,10 @@ std::unique_ptr Interpreter::GenModule() { return nullptr; } -CodeGenerator *Interpreter::getCodeGen() const { - FrontendAction *WrappedAct = Act->getWrapped(); +CodeGenerator *Interpreter::getCodeGen(IncrementalAction *Action) const { + if (!Action) + Action = Act.get(); + FrontendAction *WrappedAct = Action->getWrapped(); if (!WrappedAct->hasIRSupport()) return nullptr; return static_cast(WrappedAct)->getCodeGenerator(); -- cgit v1.1