// Private LLVM 18 / LLD bridge. Inputs are borrowed only for the call; outputs // use malloc/free through one ABI and never expose LLVM ownership to Rust. #include "lld/Common/Driver.h" #include "lld/Common/ErrorHandler.h" #include "llvm/IR/LegacyPassManager.h" #include "llvm/IR/Constants.h" #include "llvm/IR/Verifier.h" #include "llvm/IRReader/IRReader.h" #include "llvm/MC/TargetRegistry.h" #include "llvm/Passes/PassBuilder.h" #include "llvm/Support/SourceMgr.h" #include "llvm/Support/TargetSelect.h" #include "llvm/Target/TargetMachine.h" #include #include #include LLD_HAS_DRIVER(elf) LLD_HAS_DRIVER(coff) LLD_HAS_DRIVER(macho) struct Reply { unsigned char *bytes; size_t len; uint32_t status; }; struct Resource { const unsigned char *symbol; size_t symbol_len; const unsigned char *bytes; size_t len; }; static void answer(Reply *reply, llvm::StringRef bytes, uint32_t status) { constexpr size_t limit = 320 * 1024 * 1024; if (bytes.size() > limit) { bytes = "compiler bridge output exceeds 320 MiB"; status = 1; } reply->status = status; reply->len = bytes.size(); reply->bytes = static_cast(std::malloc(bytes.size())); if (!reply->bytes && !bytes.empty()) { reply->len = 0; reply->status = 1; return; } if (!bytes.empty()) std::memcpy(reply->bytes, bytes.data(), bytes.size()); } extern "C" void dever_backend_free(unsigned char *bytes) { std::free(bytes); } extern "C" void dever_backend_object(const unsigned char *ir, size_t len, const char *triple, const Resource *resources, size_t resource_count, Reply *reply) { try { static std::once_flag initialized; std::call_once(initialized, [] { llvm::InitializeAllTargetInfos(); llvm::InitializeAllTargets(); llvm::InitializeAllTargetMCs(); llvm::InitializeAllAsmPrinters(); llvm::InitializeAllAsmParsers(); }); llvm::LLVMContext context; llvm::SMDiagnostic diagnostic; auto input = llvm::MemoryBuffer::getMemBufferCopy( llvm::StringRef(reinterpret_cast(ir), len), "dever.ir"); auto module = llvm::parseIR(input->getMemBufferRef(), diagnostic, context); std::string error; llvm::raw_string_ostream errors(error); if (!module) { diagnostic.print("dever", errors); return answer(reply, error, 1); } // Do not silently compile a module for a target different from its ABI. if (!module->getTargetTriple().empty() && module->getTargetTriple() != triple) return answer(reply, "LLVM module target disagrees with selected target", 1); for (size_t index = 0; index < resource_count; ++index) { const auto &resource = resources[index]; auto *global = module->getNamedGlobal(llvm::StringRef( reinterpret_cast(resource.symbol), resource.symbol_len)); auto *array = global ? llvm::dyn_cast(global->getValueType()) : nullptr; if (!global || !global->isDeclaration() || !global->isConstant() || !global->hasExternalLinkage() || global->isThreadLocal() || global->getAddressSpace() != 0 || !array || !array->getElementType()->isIntegerTy(8) || array->getNumElements() != resource.len || global->use_empty()) return answer(reply, "LLVM resource must match a used external constant byte-array declaration", 1); global->setInitializer(llvm::ConstantDataArray::get( context, llvm::ArrayRef(resource.bytes, resource.len))); global->setLinkage(llvm::GlobalValue::PrivateLinkage); } if (llvm::verifyModule(*module, &errors)) return answer(reply, error, 1); auto target = llvm::TargetRegistry::lookupTarget(triple, error); if (!target) return answer(reply, error, 1); llvm::TargetOptions options; std::unique_ptr machine(target->createTargetMachine( triple, "generic", "", options, llvm::Reloc::PIC_, std::nullopt, llvm::CodeGenOptLevel::Default)); if (!machine) return answer(reply, "LLVM target machine is unavailable", 1); auto layout = machine->createDataLayout(); if (!module->getDataLayoutStr().empty() && module->getDataLayout() != layout) return answer(reply, "LLVM module layout disagrees with selected target", 1); module->setTargetTriple(triple); module->setDataLayout(layout); llvm::PassBuilder passes(machine.get()); llvm::LoopAnalysisManager loops; llvm::FunctionAnalysisManager functions; llvm::CGSCCAnalysisManager calls; llvm::ModuleAnalysisManager modules; passes.registerModuleAnalyses(modules); passes.registerCGSCCAnalyses(calls); passes.registerFunctionAnalyses(functions); passes.registerLoopAnalyses(loops); passes.crossRegisterProxies(loops, functions, calls, modules); auto pipeline = passes.buildPerModuleDefaultPipeline(llvm::OptimizationLevel::O2); pipeline.run(*module, modules); if (llvm::verifyModule(*module, &errors)) return answer(reply, error, 1); llvm::SmallVector output; llvm::raw_svector_ostream stream(output); llvm::legacy::PassManager emission; if (machine->addPassesToEmitFile(emission, stream, nullptr, llvm::CodeGenFileType::ObjectFile)) return answer(reply, "LLVM target cannot emit native objects", 1); emission.run(*module); answer(reply, llvm::StringRef(output.data(), output.size()), 0); } catch (const std::exception &error) { answer(reply, error.what(), 1); } catch (...) { answer(reply, "LLVM bridge failed", 1); } } extern "C" void dever_backend_link(const char *const *args, size_t count, Reply *reply) { try { std::string output, error; llvm::raw_string_ostream out(output), err(error); const lld::DriverDef drivers[] = {{lld::Gnu, lld::elf::link}, {lld::WinLink, lld::coff::link}, {lld::Darwin, lld::macho::link}}; auto result = lld::lldMain(llvm::ArrayRef(args, count), out, err, drivers); // LLD explicitly forbids cleanup/re-entry after failed crash recovery. // This bridge belongs to a compiler worker, never an application process. if (!result.canRunAgain) lld::exitLld(result.retCode ? result.retCode : 1); answer(reply, error, result.retCode == 0 ? 0 : 1); } catch (const std::exception &) { lld::exitLld(1); } catch (...) { lld::exitLld(1); } }