David Blaikie via llvm-dev
2018-Sep-16 23:55 UTC
[llvm-dev] LLVMContext: Threads and Ownership.
In the most basic case, I'd imagine something like this: auto C = std::make_shared<LLVMContext>(); struct ModuleAndSharedContextDeleter { std::shared_ptr<LLVMContext> C; operator()(Module *M) { delete M; } /* ctor to init C */}; std::unique_ptr<Module, ModuleAndSharedDeleter> M(new Module(C.get()), ModuleAndSharedContextDeleter(C)); (or invert this and traffic in structs that have a unique_ptr<Module> and a shared_ptr<LLVMContext> as members - though the above example has the advantage that it mostly looks like unique_ptr, and you can change ownership (pass the unique_ptr<Module, CustomDeleter> to a shared_ptr and it'll correctly propagate the deleter, etc)) ... But it sounds like you're concerned with a situation in which there are a wide variety of things making Modules that are maybe outside the purview of Orc but need this ownership model? That would seem unfortunate & I'm not quite sure I'm picturing the situation you have in mind. On Sun, Sep 16, 2018 at 4:27 PM Lang Hames <lhames at gmail.com> wrote:> I'd think/suggest ref-counted LLVMContext ownership would be done by >> wrapping/external tracking in this use case. > > > What kind of wrapping are you imagining? I'm worried that maintaining an > external context ref-count is going to be awkward and error prone, but > perhaps there are idioms that would make this easier than I am imagining. > > I think it may be simpler and safer to expose the number of Modules > managed by an LLVMContext. That way the "ref-count" always reflects the > number of modules using the context, and tracking can be set up and managed > entirely from the LLVMContext creation site (by adding the context to an > 'automatically managed' set), rather than intruding to every Module > creation point. > > We would still need to add a mutex to LLVMContext to make this thread safe > though. > > -- Lang. > > On Sun, Sep 16, 2018 at 10:22 AM David Blaikie <dblaikie at gmail.com> wrote: > >> Agreed, the existing ownership seems sub-optimal. I wouldn't say broken, >> but subtle at least - looks like you get the choice to either manage the >> ownership of the Module object yourself, or let the context handle it (eg: >> currently it'd be valid to just do "{ LLVMContext C; new Module(C); new >> Module(C); }" - Modules end up owned by the context and cleaned up there). >> >> Might be hard to migrate existing users away from this without silently >> introducing memory leaks... maybe with some significant API breakage - move >> Module construction to a factory/helper that returns a >> std::unique_ptr<Module> - requiring every Module construction to be >> revisited, and users relying on LLVMContext based ownership/cleanup to >> redesign their code. >> >> As to the original question - gut reaction: this doesn't seem like >> something that's general-purpose enough to be implemented in the >> LLVMContext/Module itself. I think a reasonable ownership model for >> LLVMContext/Module is that the user is required to ensure the LLVMContext >> outlives all Modules created within it (same way a user of std::vector is >> required to ensure that the vector is not reallocated so long as they're >> keeping pointers/references to elements in it). I'd think/suggest >> ref-counted LLVMContext ownership would be done by wrapping/external >> tracking in this use case. >> >> - Dave >> >> On Sat, Sep 15, 2018 at 9:30 PM Lang Hames via llvm-dev < >> llvm-dev at lists.llvm.org> wrote: >> >>> Actually, looking at the destructors for LLVMContext and Module I do not >>> think the current ownership scheme makes sense, so this might be a good >>> opportunity to re-think it. >>> >>> Right now an LLVMContext owns a list of modules (see >>> LLVMContextImpl::OwnedModules) that it destroys when its destructor is >>> called. Modules remove themselves from this list if they are destructed >>> before the context: >>> >>> Module::~Module() { >>> Context.removeModule(this); >>> ... >>> >>> LLVMContextImpl::~LLVMContextImpl() { >>> // NOTE: We need to delete the contents of OwnedModules, but Module's >>> dtor >>> // will call LLVMContextImpl::removeModule, thus invalidating >>> iterators into >>> // the container. Avoid iterators during this operation: >>> while (!OwnedModules.empty()) >>> delete *OwnedModules.begin(); >>> ... >>> >>> This makes it unsafe to hold a unique_ptr to a Module: If any Module is >>> still alive when its context goes out of scope it will be double freed, >>> first by the LLVMContextImpl destructor and then again by the unique ptr. >>> Idiomatic scoping means that we tend not to see this in practice (Module >>> takes an LLVMContext reference, meaning we always declare the context >>> first, so it goes out of scope last), but makes the context ownership >>> redundant: the modules are always freed first via their unique_ptr's. >>> >>> I don't think it makes sense for LLVMContext to own Modules. I think >>> that Modules should share ownership of their LLVMContext via a shared_ptr. >>> >>> Thoughts? >>> >>> Cheers, >>> Lang. >>> >>> On Sat, Sep 15, 2018 at 4:14 PM Lang Hames <lhames at gmail.com> wrote: >>> >>>> Hi All, >>>> >>>> ORC's new concurrent compilation model generates some interesting >>>> lifetime and thread safety questions around LLVMContext: We need multiple >>>> LLVMContexts (one per module in the simplest case, but at least one per >>>> thread), and the lifetime of each context depends on the execution path of >>>> the JIT'd code. We would like to deallocate contexts once all modules >>>> associated with them have been compiled, but there is no safe or easy way >>>> to check that condition at the moment as LLVMContext does not expose how >>>> many modules are associated with it. >>>> >>>> One way to fix this would be to add a mutex to LLVMContext, and expose >>>> this and the module count. Then in the IR-compiling layer of the JIT we >>>> could have something like: >>>> >>>> // Compile finished, time to deallocate the module. >>>> // Explicit deletes used for clarity, we would use unique_ptrs in >>>> practice. >>>> auto &Ctx = Mod->getContext(); >>>> delete Mod; >>>> std::lock_guard<std::mutex> Lock(Ctx->getMutex()); >>>> if (Ctx.getNumModules()) >>>> delete Ctx; >>>> >>>> Another option would be to invert the ownership model and say that each >>>> Module shares ownership of its LLVMContext. That way LLVMContexts would be >>>> automatically deallocated when the last module using them is destructed >>>> (providing no other shared_ptrs to the context are held elsewhere). >>>> >>>> There are other possible approaches (e.g. side tables for the mutex and >>>> module count) but before I spent too much time on it I wanted to see >>>> whether anyone else has encountered these issues or has opinions on >>>> solutions. >>>> >>>> Cheers, >>>> Lang. >>>> >>> _______________________________________________ >>> LLVM Developers mailing list >>> llvm-dev at lists.llvm.org >>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >>> >>-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180916/7f21bf6e/attachment.html>
Lang Hames via llvm-dev
2018-Sep-17 00:38 UTC
[llvm-dev] LLVMContext: Threads and Ownership.
> auto C = std::make_shared<LLVMContext>(); > struct ModuleAndSharedContextDeleter { std::shared_ptr<LLVMContext> C; > operator()(Module *M) { delete M; } /* ctor to init C */}; > std::unique_ptr<Module, ModuleAndSharedDeleter> M(new Module(C.get()), > ModuleAndSharedContextDeleter(C));> (or invert this and traffic in structs that have a unique_ptr<Module> and > a shared_ptr<LLVMContext> as members - though the above example has the > advantage that it mostly looks like unique_ptr, and you can change > ownership (pass the unique_ptr<Module, CustomDeleter> to a shared_ptr and > it'll correctly propagate the deleter, etc))I believe this would work, but it certainly has a suboptimal feel to it. For what it is worth, the C API contains some helpful documentation (see include/llvm-c/LLVMContext.h): /** * Destroy a module instance. * * This must be called for every created module or memory will be * leaked. */ void LLVMDisposeModule(LLVMModuleRef M); So based on the C-API's documentation we are at least free to remove this strange fallback-ownership model of LLVMContext. Cheers, Lang. On Sun, Sep 16, 2018 at 4:55 PM David Blaikie <dblaikie at gmail.com> wrote:> In the most basic case, I'd imagine something like this: > > auto C = std::make_shared<LLVMContext>(); > struct ModuleAndSharedContextDeleter { std::shared_ptr<LLVMContext> C; > operator()(Module *M) { delete M; } /* ctor to init C */}; > std::unique_ptr<Module, ModuleAndSharedDeleter> M(new Module(C.get()), > ModuleAndSharedContextDeleter(C)); > > (or invert this and traffic in structs that have a unique_ptr<Module> and > a shared_ptr<LLVMContext> as members - though the above example has the > advantage that it mostly looks like unique_ptr, and you can change > ownership (pass the unique_ptr<Module, CustomDeleter> to a shared_ptr and > it'll correctly propagate the deleter, etc)) > ... > > But it sounds like you're concerned with a situation in which there are a > wide variety of things making Modules that are maybe outside the purview of > Orc but need this ownership model? That would seem unfortunate & I'm not > quite sure I'm picturing the situation you have in mind. > > On Sun, Sep 16, 2018 at 4:27 PM Lang Hames <lhames at gmail.com> wrote: > >> I'd think/suggest ref-counted LLVMContext ownership would be done by >>> wrapping/external tracking in this use case. >> >> >> What kind of wrapping are you imagining? I'm worried that maintaining an >> external context ref-count is going to be awkward and error prone, but >> perhaps there are idioms that would make this easier than I am imagining. >> >> I think it may be simpler and safer to expose the number of Modules >> managed by an LLVMContext. That way the "ref-count" always reflects the >> number of modules using the context, and tracking can be set up and managed >> entirely from the LLVMContext creation site (by adding the context to an >> 'automatically managed' set), rather than intruding to every Module >> creation point. >> >> We would still need to add a mutex to LLVMContext to make this thread >> safe though. >> >> -- Lang. >> >> On Sun, Sep 16, 2018 at 10:22 AM David Blaikie <dblaikie at gmail.com> >> wrote: >> >>> Agreed, the existing ownership seems sub-optimal. I wouldn't say broken, >>> but subtle at least - looks like you get the choice to either manage the >>> ownership of the Module object yourself, or let the context handle it (eg: >>> currently it'd be valid to just do "{ LLVMContext C; new Module(C); new >>> Module(C); }" - Modules end up owned by the context and cleaned up there). >>> >>> Might be hard to migrate existing users away from this without silently >>> introducing memory leaks... maybe with some significant API breakage - move >>> Module construction to a factory/helper that returns a >>> std::unique_ptr<Module> - requiring every Module construction to be >>> revisited, and users relying on LLVMContext based ownership/cleanup to >>> redesign their code. >>> >>> As to the original question - gut reaction: this doesn't seem like >>> something that's general-purpose enough to be implemented in the >>> LLVMContext/Module itself. I think a reasonable ownership model for >>> LLVMContext/Module is that the user is required to ensure the LLVMContext >>> outlives all Modules created within it (same way a user of std::vector is >>> required to ensure that the vector is not reallocated so long as they're >>> keeping pointers/references to elements in it). I'd think/suggest >>> ref-counted LLVMContext ownership would be done by wrapping/external >>> tracking in this use case. >>> >>> - Dave >>> >>> On Sat, Sep 15, 2018 at 9:30 PM Lang Hames via llvm-dev < >>> llvm-dev at lists.llvm.org> wrote: >>> >>>> Actually, looking at the destructors for LLVMContext and Module I do >>>> not think the current ownership scheme makes sense, so this might be a good >>>> opportunity to re-think it. >>>> >>>> Right now an LLVMContext owns a list of modules (see >>>> LLVMContextImpl::OwnedModules) that it destroys when its destructor is >>>> called. Modules remove themselves from this list if they are destructed >>>> before the context: >>>> >>>> Module::~Module() { >>>> Context.removeModule(this); >>>> ... >>>> >>>> LLVMContextImpl::~LLVMContextImpl() { >>>> // NOTE: We need to delete the contents of OwnedModules, but Module's >>>> dtor >>>> // will call LLVMContextImpl::removeModule, thus invalidating >>>> iterators into >>>> // the container. Avoid iterators during this operation: >>>> while (!OwnedModules.empty()) >>>> delete *OwnedModules.begin(); >>>> ... >>>> >>>> This makes it unsafe to hold a unique_ptr to a Module: If any Module is >>>> still alive when its context goes out of scope it will be double freed, >>>> first by the LLVMContextImpl destructor and then again by the unique ptr. >>>> Idiomatic scoping means that we tend not to see this in practice (Module >>>> takes an LLVMContext reference, meaning we always declare the context >>>> first, so it goes out of scope last), but makes the context ownership >>>> redundant: the modules are always freed first via their unique_ptr's. >>>> >>>> I don't think it makes sense for LLVMContext to own Modules. I think >>>> that Modules should share ownership of their LLVMContext via a shared_ptr. >>>> >>>> Thoughts? >>>> >>>> Cheers, >>>> Lang. >>>> >>>> On Sat, Sep 15, 2018 at 4:14 PM Lang Hames <lhames at gmail.com> wrote: >>>> >>>>> Hi All, >>>>> >>>>> ORC's new concurrent compilation model generates some interesting >>>>> lifetime and thread safety questions around LLVMContext: We need multiple >>>>> LLVMContexts (one per module in the simplest case, but at least one per >>>>> thread), and the lifetime of each context depends on the execution path of >>>>> the JIT'd code. We would like to deallocate contexts once all modules >>>>> associated with them have been compiled, but there is no safe or easy way >>>>> to check that condition at the moment as LLVMContext does not expose how >>>>> many modules are associated with it. >>>>> >>>>> One way to fix this would be to add a mutex to LLVMContext, and expose >>>>> this and the module count. Then in the IR-compiling layer of the JIT we >>>>> could have something like: >>>>> >>>>> // Compile finished, time to deallocate the module. >>>>> // Explicit deletes used for clarity, we would use unique_ptrs in >>>>> practice. >>>>> auto &Ctx = Mod->getContext(); >>>>> delete Mod; >>>>> std::lock_guard<std::mutex> Lock(Ctx->getMutex()); >>>>> if (Ctx.getNumModules()) >>>>> delete Ctx; >>>>> >>>>> Another option would be to invert the ownership model and say that >>>>> each Module shares ownership of its LLVMContext. That way LLVMContexts >>>>> would be automatically deallocated when the last module using them is >>>>> destructed (providing no other shared_ptrs to the context are held >>>>> elsewhere). >>>>> >>>>> There are other possible approaches (e.g. side tables for the mutex >>>>> and module count) but before I spent too much time on it I wanted to see >>>>> whether anyone else has encountered these issues or has opinions on >>>>> solutions. >>>>> >>>>> Cheers, >>>>> Lang. >>>>> >>>> _______________________________________________ >>>> LLVM Developers mailing list >>>> llvm-dev at lists.llvm.org >>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >>>> >>>-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180916/ee631597/attachment.html>
David Blaikie via llvm-dev
2018-Sep-17 00:52 UTC
[llvm-dev] LLVMContext: Threads and Ownership.
On Sun, Sep 16, 2018 at 5:38 PM Lang Hames <lhames at gmail.com> wrote:> > auto C = std::make_shared<LLVMContext>(); >> struct ModuleAndSharedContextDeleter { std::shared_ptr<LLVMContext> C; >> operator()(Module *M) { delete M; } /* ctor to init C */}; >> std::unique_ptr<Module, ModuleAndSharedDeleter> M(new Module(C.get()), >> ModuleAndSharedContextDeleter(C)); > > >> (or invert this and traffic in structs that have a unique_ptr<Module> and >> a shared_ptr<LLVMContext> as members - though the above example has the >> advantage that it mostly looks like unique_ptr, and you can change >> ownership (pass the unique_ptr<Module, CustomDeleter> to a shared_ptr and >> it'll correctly propagate the deleter, etc)) > > > I believe this would work, but it certainly has a suboptimal feel to it. >If you just want to use shared_ptr everywhere (for both the context and the modules), the complexity would leak into fewer APIs, because shared_ptr type erases its deleter. But yeah, feels weird to me to change the ownership of LLVMContext for this one use case - if there were more users that wanted to have it, I could see building it in in a more convenient way (but maybe still in the form of a typedef common for unique_ptr<Module, CustomDeleter>, built-in factory functions/helpers/etc to make that more mainstream). Also would come back to some discussion (maybe easier in person or over IRC, etc, not sure) about whether there's a way to design things so shared ownership isn't needed even for ORC - I don't have enough info to fully understand where/how it'd be used here/whether there's some good alternatives.> For what it is worth, the C API contains some helpful documentation (see > include/llvm-c/LLVMContext.h): > > /** > * Destroy a module instance. > * > * This must be called for every created module or memory will be > * leaked. > */ > void LLVMDisposeModule(LLVMModuleRef M); > > So based on the C-API's documentation we are at least free to remove this > strange fallback-ownership model of LLVMContext. >Based on the documentation, yeah - though I'd expect it'd still break some folks - and it's the C++ API users I'd expect we have more of at this level & likely to introduce some quiet leaks. Be nice to fail hard (though we don't have much way to do that - assert fail, but that's only in assert builds, etc) - could keep the list of modules around for a while & assert if it's non-empty when the context is destroyed. Not perfect, but at least it's something. - Dave> > Cheers, > Lang. > > > > > On Sun, Sep 16, 2018 at 4:55 PM David Blaikie <dblaikie at gmail.com> wrote: > >> In the most basic case, I'd imagine something like this: >> >> auto C = std::make_shared<LLVMContext>(); >> struct ModuleAndSharedContextDeleter { std::shared_ptr<LLVMContext> C; >> operator()(Module *M) { delete M; } /* ctor to init C */}; >> std::unique_ptr<Module, ModuleAndSharedDeleter> M(new Module(C.get()), >> ModuleAndSharedContextDeleter(C)); >> >> (or invert this and traffic in structs that have a unique_ptr<Module> and >> a shared_ptr<LLVMContext> as members - though the above example has the >> advantage that it mostly looks like unique_ptr, and you can change >> ownership (pass the unique_ptr<Module, CustomDeleter> to a shared_ptr and >> it'll correctly propagate the deleter, etc)) >> ... >> >> But it sounds like you're concerned with a situation in which there are a >> wide variety of things making Modules that are maybe outside the purview of >> Orc but need this ownership model? That would seem unfortunate & I'm not >> quite sure I'm picturing the situation you have in mind. >> >> On Sun, Sep 16, 2018 at 4:27 PM Lang Hames <lhames at gmail.com> wrote: >> >>> I'd think/suggest ref-counted LLVMContext ownership would be done by >>>> wrapping/external tracking in this use case. >>> >>> >>> What kind of wrapping are you imagining? I'm worried that maintaining an >>> external context ref-count is going to be awkward and error prone, but >>> perhaps there are idioms that would make this easier than I am imagining. >>> >>> I think it may be simpler and safer to expose the number of Modules >>> managed by an LLVMContext. That way the "ref-count" always reflects the >>> number of modules using the context, and tracking can be set up and managed >>> entirely from the LLVMContext creation site (by adding the context to an >>> 'automatically managed' set), rather than intruding to every Module >>> creation point. >>> >>> We would still need to add a mutex to LLVMContext to make this thread >>> safe though. >>> >>> -- Lang. >>> >>> On Sun, Sep 16, 2018 at 10:22 AM David Blaikie <dblaikie at gmail.com> >>> wrote: >>> >>>> Agreed, the existing ownership seems sub-optimal. I wouldn't say >>>> broken, but subtle at least - looks like you get the choice to either >>>> manage the ownership of the Module object yourself, or let the context >>>> handle it (eg: currently it'd be valid to just do "{ LLVMContext C; new >>>> Module(C); new Module(C); }" - Modules end up owned by the context and >>>> cleaned up there). >>>> >>>> Might be hard to migrate existing users away from this without silently >>>> introducing memory leaks... maybe with some significant API breakage - move >>>> Module construction to a factory/helper that returns a >>>> std::unique_ptr<Module> - requiring every Module construction to be >>>> revisited, and users relying on LLVMContext based ownership/cleanup to >>>> redesign their code. >>>> >>>> As to the original question - gut reaction: this doesn't seem like >>>> something that's general-purpose enough to be implemented in the >>>> LLVMContext/Module itself. I think a reasonable ownership model for >>>> LLVMContext/Module is that the user is required to ensure the LLVMContext >>>> outlives all Modules created within it (same way a user of std::vector is >>>> required to ensure that the vector is not reallocated so long as they're >>>> keeping pointers/references to elements in it). I'd think/suggest >>>> ref-counted LLVMContext ownership would be done by wrapping/external >>>> tracking in this use case. >>>> >>>> - Dave >>>> >>>> On Sat, Sep 15, 2018 at 9:30 PM Lang Hames via llvm-dev < >>>> llvm-dev at lists.llvm.org> wrote: >>>> >>>>> Actually, looking at the destructors for LLVMContext and Module I do >>>>> not think the current ownership scheme makes sense, so this might be a good >>>>> opportunity to re-think it. >>>>> >>>>> Right now an LLVMContext owns a list of modules (see >>>>> LLVMContextImpl::OwnedModules) that it destroys when its destructor is >>>>> called. Modules remove themselves from this list if they are destructed >>>>> before the context: >>>>> >>>>> Module::~Module() { >>>>> Context.removeModule(this); >>>>> ... >>>>> >>>>> LLVMContextImpl::~LLVMContextImpl() { >>>>> // NOTE: We need to delete the contents of OwnedModules, but >>>>> Module's dtor >>>>> // will call LLVMContextImpl::removeModule, thus invalidating >>>>> iterators into >>>>> // the container. Avoid iterators during this operation: >>>>> while (!OwnedModules.empty()) >>>>> delete *OwnedModules.begin(); >>>>> ... >>>>> >>>>> This makes it unsafe to hold a unique_ptr to a Module: If any Module >>>>> is still alive when its context goes out of scope it will be double freed, >>>>> first by the LLVMContextImpl destructor and then again by the unique ptr. >>>>> Idiomatic scoping means that we tend not to see this in practice (Module >>>>> takes an LLVMContext reference, meaning we always declare the context >>>>> first, so it goes out of scope last), but makes the context ownership >>>>> redundant: the modules are always freed first via their unique_ptr's. >>>>> >>>>> I don't think it makes sense for LLVMContext to own Modules. I think >>>>> that Modules should share ownership of their LLVMContext via a shared_ptr. >>>>> >>>>> Thoughts? >>>>> >>>>> Cheers, >>>>> Lang. >>>>> >>>>> On Sat, Sep 15, 2018 at 4:14 PM Lang Hames <lhames at gmail.com> wrote: >>>>> >>>>>> Hi All, >>>>>> >>>>>> ORC's new concurrent compilation model generates some interesting >>>>>> lifetime and thread safety questions around LLVMContext: We need multiple >>>>>> LLVMContexts (one per module in the simplest case, but at least one per >>>>>> thread), and the lifetime of each context depends on the execution path of >>>>>> the JIT'd code. We would like to deallocate contexts once all modules >>>>>> associated with them have been compiled, but there is no safe or easy way >>>>>> to check that condition at the moment as LLVMContext does not expose how >>>>>> many modules are associated with it. >>>>>> >>>>>> One way to fix this would be to add a mutex to LLVMContext, and >>>>>> expose this and the module count. Then in the IR-compiling layer of the JIT >>>>>> we could have something like: >>>>>> >>>>>> // Compile finished, time to deallocate the module. >>>>>> // Explicit deletes used for clarity, we would use unique_ptrs in >>>>>> practice. >>>>>> auto &Ctx = Mod->getContext(); >>>>>> delete Mod; >>>>>> std::lock_guard<std::mutex> Lock(Ctx->getMutex()); >>>>>> if (Ctx.getNumModules()) >>>>>> delete Ctx; >>>>>> >>>>>> Another option would be to invert the ownership model and say that >>>>>> each Module shares ownership of its LLVMContext. That way LLVMContexts >>>>>> would be automatically deallocated when the last module using them is >>>>>> destructed (providing no other shared_ptrs to the context are held >>>>>> elsewhere). >>>>>> >>>>>> There are other possible approaches (e.g. side tables for the mutex >>>>>> and module count) but before I spent too much time on it I wanted to see >>>>>> whether anyone else has encountered these issues or has opinions on >>>>>> solutions. >>>>>> >>>>>> Cheers, >>>>>> Lang. >>>>>> >>>>> _______________________________________________ >>>>> LLVM Developers mailing list >>>>> llvm-dev at lists.llvm.org >>>>> http://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-dev >>>>> >>>>-------------- next part -------------- An HTML attachment was scrubbed... URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180916/e6580b2d/attachment.html>