diff --git a/llvm/include/llvm/IR/Module.h b/llvm/include/llvm/IR/Module.h index 528e19af5518d..12b50fc506516 100644 --- a/llvm/include/llvm/IR/Module.h +++ b/llvm/include/llvm/IR/Module.h @@ -256,9 +256,12 @@ class LLVM_ABI Module { /// The module destructor. This will dropAllReferences. ~Module(); -/// @} -/// @name Module Level Accessors -/// @{ + /// Move assignment. + Module &operator=(Module &&Other); + + /// @} + /// @name Module Level Accessors + /// @{ /// Get the module identifier which is, essentially, the name of the module. /// @returns the module identifier as a string diff --git a/llvm/lib/IR/Module.cpp b/llvm/lib/IR/Module.cpp index ab48d3e4101b7..ff95f123d4878 100644 --- a/llvm/lib/IR/Module.cpp +++ b/llvm/lib/IR/Module.cpp @@ -78,6 +78,41 @@ Module::Module(StringRef MID, LLVMContext &C) Context.addModule(this); } +Module &Module::operator=(Module &&Other) { + assert(&Context == &Other.Context && "Module must be in the same Context"); + + dropAllReferences(); + + ModuleID = std::move(Other.ModuleID); + SourceFileName = std::move(Other.SourceFileName); + IsNewDbgInfoFormat = std::move(Other.IsNewDbgInfoFormat); + + GlobalList.clear(); + GlobalList.splice(GlobalList.begin(), Other.GlobalList); + + FunctionList.clear(); + FunctionList.splice(FunctionList.begin(), Other.FunctionList); + + AliasList.clear(); + AliasList.splice(AliasList.begin(), Other.AliasList); + + IFuncList.clear(); + IFuncList.splice(IFuncList.begin(), Other.IFuncList); + + NamedMDList.clear(); + NamedMDList.splice(NamedMDList.begin(), Other.NamedMDList); + GlobalScopeAsm = std::move(Other.GlobalScopeAsm); + OwnedMemoryBuffer = std::move(Other.OwnedMemoryBuffer); + Materializer = std::move(Other.Materializer); + TargetTriple = std::move(Other.TargetTriple); + DL = std::move(Other.DL); + CurrentIntrinsicIds = std::move(Other.CurrentIntrinsicIds); + UniquedIntrinsicNames = std::move(Other.UniquedIntrinsicNames); + ModuleFlags = std::move(Other.ModuleFlags); + Context.addModule(this); + return *this; +} + Module::~Module() { Context.removeModule(this); dropAllReferences(); diff --git a/llvm/unittests/IR/ModuleTest.cpp b/llvm/unittests/IR/ModuleTest.cpp index c18301d5e6d75..36c356730d27a 100644 --- a/llvm/unittests/IR/ModuleTest.cpp +++ b/llvm/unittests/IR/ModuleTest.cpp @@ -14,6 +14,7 @@ #include "llvm/Pass.h" #include "llvm/Support/RandomNumberGenerator.h" #include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" #include "gtest/gtest.h" #include @@ -326,4 +327,80 @@ TEST(ModuleTest, GlobalList) { EXPECT_EQ(M->global_size(), 1u); } +TEST(ModuleTest, MoveAssign) { + // This tests that we can move-assign modules, we parse two modules and + // move assign the second one to the first one, and check that the print + // is equal to what we loaded. + LLVMContext C; + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr M1 = parseAssemblyString(R"( +; ModuleID = '' +source_filename = "" + +@GV1 = external global i32 + +@GA1 = alias void (), ptr @Foo1 + +define void @Foo1() { + ret void +} + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} +!foo1 = !{!3} +!bar1 = !{!4} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang1", isOptimized: true, flags: "-O2", runtimeVersion: 0, splitDebugFilename: "abc.debug", emissionKind: LineTablesOnly) +!2 = !DIFile(filename: "path/to/file1", directory: "/path/to/dir1") +!3 = !DILocation(line: 12, column: 34, scope: !4) +!4 = distinct !DISubprogram(name: "foo1", scope: null, spFlags: DISPFlagDefinition, unit: !1) +)", + Err, Context); + ASSERT_TRUE(M1.get()); + + StringLiteral M2Str = R"( +; ModuleID = '' +source_filename = "" + +@GV2 = external global i32 + +@GA2 = alias void (), ptr @Foo2 + +define void @Foo2() { + ret void +} + +!llvm.module.flags = !{!0} +!llvm.dbg.cu = !{!1} +!foo2 = !{!3} +!bar2 = !{!4} + +!0 = !{i32 2, !"Debug Info Version", i32 3} +!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang2", isOptimized: true, flags: "-O2", runtimeVersion: 0, splitDebugFilename: "abc.debug", emissionKind: LineTablesOnly) +!2 = !DIFile(filename: "path/to/file2", directory: "/path/to/dir2") +!3 = !DILocation(line: 1234, column: 56, scope: !4) +!4 = distinct !DISubprogram(name: "foo2", scope: null, spFlags: DISPFlagDefinition, unit: !1) +)"; + { + std::unique_ptr M2 = parseAssemblyString(M2Str, Err, Context); + ASSERT_TRUE(M2.get()); + auto *GV1 = M1->getNamedValue("GV1"); + ASSERT_TRUE(GV1); + auto *GV2 = M2->getNamedValue("GV2"); + ASSERT_TRUE(GV2); + ASSERT_EQ(GV2->getParent(), &*M2); + *M1 = std::move(*M2); + ASSERT_EQ(GV2->getParent(), &*M1); + } + + std::string M1Print; + { + llvm::raw_string_ostream Os(M1Print); + Os << "\n" << *M1; + } + ASSERT_EQ(M2Str, M1Print); +} + } // end namespace