<div dir="ltr"><div><div><div><div>I've got some code using the LLVM linker. When I link one module into another, the linker fails to correctly represent all the aspects of the source module. Specifically, I've observed that types whch are structurally equivalent get merged together, even though they're explicitly named types and not unnamed structural types.<br><br></div>Here's my reproducing case. I have the source and the output IR.<br><br><div class="">#pragma warning(push, 0)</div>
<div class="">#include <llvm/ExecutionEngine/GenericValue.h></div>
<div class="">#include <llvm/ExecutionEngine/MCJIT.h></div>
<div class="">#include <llvm/ExecutionEngine/ExecutionEngine.h></div>
<div class="">#include <llvm/Support/Program.h></div>
<div class="">#include <llvm/Support/FileSystem.h></div>
<div class="">#include <llvm/Support/DynamicLibrary.h></div>
<div class="">#include <llvm/IR/Verifier.h></div>
<div class="">#include <llvm/IR/Type.h></div>
<div class="">#include <llvm/IR/DerivedTypes.h></div>
<div class="">#include <llvm/IR/IRBuilder.h></div>
<div class="">#include <llvm/Transforms/Utils/Cloning.h></div>
<div class="">#include <llvm/Linker/Linker.h></div>
<div class="">#include <llvm/Support/raw_ostream.h></div>
<div class="">#pragma warning(pop)</div>
<div class=""> </div>
<div class="">std::string printModule(llvm::Module& module) {</div>
<div class="">    std::string mod_ir;</div>
<div class="">    llvm::raw_string_ostream stream(mod_ir);</div>
<div class="">    module.print(stream, nullptr);</div>
<div class="">    stream.flush();</div>
<div class="">    return mod_ir;</div>
<div class="">}</div>
<div class="">int main() {</div>
<div class="">    llvm::LLVMContext con;</div>
<div class="">    llvm::Module src("in", con);</div>
<div class="">    llvm::Module dest("out", con);</div>
<div class="">    auto srcb1 = llvm::StructType::create(con, std::vector<llvm::Type*>{ llvm::PointerType::getInt8PtrTy(con) }, "srcb1");</div>
<div class="">    auto srcb2 = llvm::StructType::create(con, std::vector<llvm::Type*>{ llvm::PointerType::getInt8PtrTy(con) }, "srcb2");</div>
<div class="">    auto srcty = llvm::StructType::create(con, std::vector<llvm::Type*>{ srcb1, srcb2 }, "srcty");</div>
<div class="">    auto func = 
llvm::Function::Create(llvm::FunctionType::get(srcty, {}, false), 
llvm::GlobalValue::LinkageTypes::ExternalLinkage, "srcfunc", &src);</div>
<div class="">    llvm::BasicBlock* entries = llvm::BasicBlock::Create(func->getParent()->getContext(), "entry", func);</div>
<div class="">    llvm::IRBuilder<> allocabuilder(entries);</div>
<div class="">    auto insert = 
allocabuilder.CreateInsertValue(llvm::ConstantAggregateZero::get(srcty),
 llvm::ConstantAggregateZero::get(srcb1), { 0 });</div>
<div class="">    allocabuilder.CreateRet(insert);</div>
<div class="">    </div>
<div class="">    auto before = printModule(src);</div>
<div class="">    auto clone = std::unique_ptr<llvm::Module>(llvm::CloneModule(&src));</div>
<div class="">    llvm::Linker::LinkModules(&dest, clone.get());</div>
<div class="">    auto after = printModule(dest);</div>
<div class="">    if (before != after)</div>
<div class="">        __debugbreak();</div>
<div class="">}</div>
<div class=""> </div>
<div class="">// Before:</div>
<div class=""> </div>
<div class="">; ModuleID = 'in'</div>
<div class=""> </div>
<div class="">%srcty = type { %srcb1, %srcb2 }</div>
<div class="">%srcb1 = type { i8* }</div>
<div class="">%srcb2 = type { i8* }</div>
<div class=""> </div>
<div class="">define %srcty @srcfunc() {</div>
<div class="">entry:</div>
<div class="">  ret %srcty zeroinitializer</div>
<div class="">}</div>
<div class=""> </div>
<div class="">// After:</div>
<div class=""> </div>
<div class="">; ModuleID = 'out'</div>
<div class=""> </div>
<div class="">%srcty = type { %srcb1, %srcb1 }</div>
<div class="">%srcb1 = type { i8* }</div>
<div class=""> </div>
<div class="">define %srcty @srcfunc() {</div>
<div class="">entry:</div>
<div class="">  ret %srcty zeroinitializer</div>
}<br><br></div>You can see in before and after that the two structurally equivalent but distinct named types, srcb1 and srcb2, were merged. After a bit of discussion on #llvm, it was suggested that this is intended behaviour. If so, this is terribly broken. <br><br>For one thing, my code depends on looking up types from the module by name. So far it just so happens that I don't have any test cases that look up structurally equivalent types after linking by name, but it certainly could occur for some user inputs for my compiler.<br><br></div>Secondly, it's much more difficult for me to determine what is going on in this IR. In my compiler then I strictly generate one LLVM type for various types in the source code. If the compiler is broken for any reason, and I look at the IR output, then I expect to see this. If I don't see this, then I think the compiler is broken. I just spent three days trying to figure out why on earth my compiler was not generating the types correctly, when it was all along. And it's much more difficult to interpret the outcome when the IR no longer distinguishes between the two logically completely distinct types that just happen to have the same IR representation.<br><br></div>Fundamentally, LLVM should never mutate the contents of the module unless it's explicitly requested, because the programmer depends on properties of the IR that are more than just binary equivalence. Moving the contents of one module into another module is no exception.<br></div>