Skip to content

Give IRGenModule Ownership of its LLVMContext #31080

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

Merged
merged 5 commits into from
Apr 17, 2020

Conversation

CodaFi
Copy link
Contributor

@CodaFi CodaFi commented Apr 16, 2020

Formalize the ownership contract between code generation, the LLVM Modules it generates, and the LLVMContext those modules are allocated in. Previously, the context to generate code in was under caller control. In practice, most callers passed around the global context. With David's patch, IRGenModule now allocates and destroys the context itself after code generation has completed. This was mostly harmless. Mostly.

The first place this disrupted was ORCJIT, which needed ownership of the context the generated module was allocated in. This necessitated the introduction of a new move-only type that manages the lifetime of the Module-Context pairs used by IRGenModule. I've called it swift::GeneratedModule. Its API contract is simple: The module and context live and die together, and ownership of a GeneratedModule is exclusive by construction. There are also a very limited set of ownership transfer operations on a GeneratedModule value. GeneratedModule::intoThreadSafeContext consumes a GeneratedModule and produces an orc::ThreadSafeModule suitable for use by the JIT. The remaining release operation mirrors the release operation on std::unique_ptr, except it consumes the GeneratedModule value in the process. This supports the second part of the story:

The second place this disrupted was the legacy integrated REPL. The REPL generates a fresh module for each "line", links the generated modules together, then passes them to its ExecutionEngine. The LLVM Linker has never handled linking modules allocated in separate contexts. Thus, the REPL used to just allocate everything into Swift's ad-hoc global context. Given that it is no longer under caller control, one solution would be to clone the module into the global context and link that. However, cloning LLVM Modules across contexts is also not a first-class operation. Instead, introduce an egregious hack that round-trips the module through one of LLVM's serialization paths. I have chosen the IR printer, but the bitcode printer would work equally well.

We need to delete the integrated REPL.

The third place this disrupted was LLDB. There, it seems like they just need to extract the module and context in order to form an IRExecutionUnit, so I've cleaned them up under that assumption. The Clang REPL code seems built in a very similar manner, which gives me some hope.

In theory, this patchset is NFC, but given the linker hack in the REPL - and that LLVM does not test round-tripping at a semantic level very well - I am not confident in that claim.

Even if we don't wind up going through with the parts of this patch that sequester the ownership of the LLVM Context, I can still break off the little cleanup pieces from this patchset - there's some const-qualification and other little mechanical improvements that would be nice to have.

David Ungar and others added 5 commits April 16, 2020 11:57
swift::GeneratedModule encapsulates an llvm::Module, llvm::LLVMContext
pair that must live and die together. It has  convenient accessors for
projecting the module and context components. The meat of this type is
the two conversion functions, which transfer ownership of either the
module component to the caller or the module and context to ORCJIT.

This is because ORC enforces an ownership contract that is distinct from
LLVM's rather wild ownership story for modules and their associated
contexts. See http://llvm.org/docs/ORCv2.html#how-to-use-threadsafemodule-and-threadsafecontext
All of this is in service of working around a pile of deficiencies in LLVM's Module Linker, and LLVMContext abstractions. And because we're just gonna scrap this code soon anyways, it's probably not worth the effort to push on these bugs to block the broader cleanup here.

The LLVM Linker currently does not support linking modules allocated in different contexts. This appears to be motivated in part by LLVM's lack of a facility to clone a module from one context to another. This, in turn, appears to be motivated in part by LLVMContext's lack of a robust notion of identity - which makes it harder than it needs to be to detect the mismatch.

However, it is not impossible to clone a module across contexts. We need to get creative and round-trip the module through some serialization layer. Out of convenience, that layer is currently textual IR, though bitcode would work equally well.

Given that it is no longer under the caller's control which LLVMContext we generate code in, put all the above together to arrive at an egregious hack that clones the module into the LLVMContext the REPL expects.
@CodaFi CodaFi marked this pull request as draft April 16, 2020 19:28
Copy link
Contributor

@davidungar davidungar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only had time to give it a very quick look right away but it looks wonderful to me! Very nice to see it passes the consistency test. Way to go, @CodaFi !

Copy link
Contributor

@davidungar davidungar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Only had time to give it a very quick look right away but it looks wonderful to me! Very nice to see it passes the consistency test. Way to go, @CodaFi !

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 16, 2020

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 16, 2020

Screwed up LLDB. This first round is going to fail.

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 16, 2020

@lhames
Copy link
Contributor

lhames commented Apr 16, 2020

This looks good to me. I like the clarity of ownership in GenerateModule.

Since re-using contexts came up on the REPL use case I'll just mention this: orc::ThreadSafeModule and orc::ThreadSafeContext were designed to account for this situation if you want to use them directly. They invert the usual Context/Module ownership relationship: Instead of Contexts uniquely owning multiple Modules, ThreadSafeModules share ownership of a ThreadSafeContext. That allows you to write APIs that optionally take a Context: If the client has one they want to use then they can (re)use it, if not the function can create a new one. E.g.

  ThreadSafeModule createModule(StringRef FileName,
                                Optional<ThreadSafeModule> Ctx = None) {
    // If the user didn't supply a context then create one.
    if (!Ctx)
      Ctx.emplace(std::make_unique<llvm::LLVMContext>());

    // Lock the ThreadSafeContext wrapper.
    auto ContextLock = Ctx->getLock();

    // Create the module.
    std::unique_ptr<llvm::Module> M = loadModuleOnContext(FileName, Ctx->getContext());

    // Wrap the Module in a ThreadSafeModule and return.
    // The return value assumes shared ownership of Ctx.
    return ThreadSafeModule(std::move(M), std::move(*Ctx));
  }

Though since the REPL is the only client that would benefit from this right now and you're thinking of removing that anyway the complexity may not be worth it.

@swift-ci
Copy link
Contributor

Build failed
Swift Test OS X Platform
Git Sha - 03b19f3

@swift-ci
Copy link
Contributor

Build failed
Swift Test Linux Platform
Git Sha - 03b19f3

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 16, 2020

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 17, 2020

swiftlang/llvm-project#1088

@swift-ci asan test

@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 17, 2020

swiftlang/llvm-project#1088

@swift-ci test source compatibility

@CodaFi CodaFi marked this pull request as ready for review April 17, 2020 14:59
@CodaFi
Copy link
Contributor Author

CodaFi commented Apr 17, 2020

@CodaFi CodaFi merged commit 85fdc7a into swiftlang:master Apr 17, 2020
@CodaFi CodaFi deleted the why-I-R-ta branch April 17, 2020 17:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants