|
31 | 31 | #include "llvm/ExecutionEngine/MCJIT.h"
|
32 | 32 | #include "llvm/IR/Module.h"
|
33 | 33 | #include "llvm/IR/Constants.h"
|
| 34 | +#include "llvm/IR/DiagnosticInfo.h" |
| 35 | +#include "llvm/IR/DiagnosticPrinter.h" |
34 | 36 | #include "llvm/Transforms/Utils/Cloning.h"
|
35 | 37 | #include "llvm/ADT/StringSet.h"
|
| 38 | +#include "llvm/IRReader/IRReader.h" |
| 39 | +#include "llvm/Linker/Linker.h" |
36 | 40 | #include "llvm/Support/ConvertUTF.h"
|
37 | 41 | #include "llvm/Support/PrettyStackTrace.h"
|
38 | 42 | #include "llvm/Support/Process.h"
|
@@ -226,6 +230,34 @@ class PrettyStackTraceREPL : public llvm::PrettyStackTraceEntry {
|
226 | 230 | };
|
227 | 231 | } // end anonymous namespace
|
228 | 232 |
|
| 233 | +namespace { |
| 234 | +static void linkerDiagnosticHandlerNoCtx(const llvm::DiagnosticInfo &DI) { |
| 235 | + if (DI.getSeverity() != llvm::DS_Error) |
| 236 | + return; |
| 237 | + |
| 238 | + std::string MsgStorage; |
| 239 | + { |
| 240 | + llvm::raw_string_ostream Stream(MsgStorage); |
| 241 | + llvm::DiagnosticPrinterRawOStream DP(Stream); |
| 242 | + DI.print(DP); |
| 243 | + } |
| 244 | + llvm::errs() << "Error linking swift modules\n"; |
| 245 | + llvm::errs() << MsgStorage << "\n"; |
| 246 | +} |
| 247 | + |
| 248 | + |
| 249 | + |
| 250 | +static void linkerDiagnosticHandler(const llvm::DiagnosticInfo &DI, |
| 251 | + void *Context) { |
| 252 | + // This assert self documents our precondition that Context is always |
| 253 | + // nullptr. It seems that parts of LLVM are using the flexibility of having a |
| 254 | + // context. We don't really care about this. |
| 255 | + assert(Context == nullptr && "We assume Context is always a nullptr"); |
| 256 | + |
| 257 | + return linkerDiagnosticHandlerNoCtx(DI); |
| 258 | +} |
| 259 | +} // end anonymous namespace |
| 260 | + |
229 | 261 | /// EditLine wrapper that implements the user interface behavior for reading
|
230 | 262 | /// user input to the REPL. All of its methods must be usable from a separate
|
231 | 263 | /// thread and so shouldn't touch anything outside of the EditLine, History,
|
@@ -849,6 +881,44 @@ class REPLEnvironment {
|
849 | 881 | }
|
850 | 882 | }
|
851 | 883 |
|
| 884 | + bool linkLLVMModules(llvm::Module *Module, |
| 885 | + std::unique_ptr<llvm::Module> &&ModuleToLink) { |
| 886 | + // EGREGIOUS HACKS AHEAD |
| 887 | + // |
| 888 | + // 1) LLVMContext does not support robust notions of identity rdar://61895075 |
| 889 | + // 2) llvm::Linker does not support modules allocated in separate contexts |
| 890 | + // rdar://61894890 |
| 891 | + // 3) Cloning modules across contexts is a known deficiency in LLVM |
| 892 | + // rdar://61896692 |
| 893 | + // 4) Round-tripping through the IR printer and parser is not guaranteed to |
| 894 | + // result in the same IR. It is heavily tested and implied that this is |
| 895 | + // the case, but LLVM makes no formal guarantees. |
| 896 | + // |
| 897 | + // So, work around 1) and do a naive pointer comparison to see if we |
| 898 | + // allocated the module we're about to link in a separate context. |
| 899 | + if (&Module->getContext() != &ModuleToLink->getContext()) { |
| 900 | + // If so, work around 2) by round-tripping the IR and parsing back it |
| 901 | + // into the original context. With module in-hand, we have successfully |
| 902 | + // worked around 3). |
| 903 | + llvm::SmallString<1024> scratch; |
| 904 | + llvm::raw_svector_ostream PrintBuffer(scratch); |
| 905 | + ModuleToLink->print(PrintBuffer, nullptr); |
| 906 | + auto Buffer = llvm::MemoryBuffer::getMemBufferCopy(PrintBuffer.str()); |
| 907 | + llvm::SMDiagnostic Err; |
| 908 | + // Finally, hope that 4) doesn't come back to bite us in the long run. |
| 909 | + ModuleToLink = llvm::parseIR(Buffer->getMemBufferRef(), Err, |
| 910 | + Module->getContext()); |
| 911 | + } |
| 912 | + |
| 913 | + llvm::LLVMContext &Ctx = Module->getContext(); |
| 914 | + auto OldHandler = Ctx.getDiagnosticHandlerCallBack(); |
| 915 | + void *OldDiagnosticContext = Ctx.getDiagnosticContext(); |
| 916 | + Ctx.setDiagnosticHandlerCallBack(linkerDiagnosticHandler, nullptr); |
| 917 | + bool Failed = llvm::Linker::linkModules(*Module, std::move(ModuleToLink)); |
| 918 | + Ctx.setDiagnosticHandlerCallBack(OldHandler, OldDiagnosticContext); |
| 919 | + return !Failed; |
| 920 | + } |
| 921 | + |
852 | 922 | bool executeSwiftSource(llvm::StringRef Line, const ProcessCmdLine &CmdLine) {
|
853 | 923 | SWIFT_DEFER {
|
854 | 924 | // Always flush diagnostic consumers after executing a line.
|
|
0 commit comments