Skip to content

Commit a04b0d5

Browse files
authored
Implement Move-assignment for llvm::Module (NFC) (#117270)
Move-assignment is quite convenient in various situation, and work-around having it available is very convoluted.
1 parent f170f5f commit a04b0d5

File tree

3 files changed

+118
-3
lines changed

3 files changed

+118
-3
lines changed

llvm/include/llvm/IR/Module.h

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -256,9 +256,12 @@ class LLVM_ABI Module {
256256
/// The module destructor. This will dropAllReferences.
257257
~Module();
258258

259-
/// @}
260-
/// @name Module Level Accessors
261-
/// @{
259+
/// Move assignment.
260+
Module &operator=(Module &&Other);
261+
262+
/// @}
263+
/// @name Module Level Accessors
264+
/// @{
262265

263266
/// Get the module identifier which is, essentially, the name of the module.
264267
/// @returns the module identifier as a string

llvm/lib/IR/Module.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,41 @@ Module::Module(StringRef MID, LLVMContext &C)
7777
Context.addModule(this);
7878
}
7979

80+
Module &Module::operator=(Module &&Other) {
81+
assert(&Context == &Other.Context && "Module must be in the same Context");
82+
83+
dropAllReferences();
84+
85+
ModuleID = std::move(Other.ModuleID);
86+
SourceFileName = std::move(Other.SourceFileName);
87+
IsNewDbgInfoFormat = std::move(Other.IsNewDbgInfoFormat);
88+
89+
GlobalList.clear();
90+
GlobalList.splice(GlobalList.begin(), Other.GlobalList);
91+
92+
FunctionList.clear();
93+
FunctionList.splice(FunctionList.begin(), Other.FunctionList);
94+
95+
AliasList.clear();
96+
AliasList.splice(AliasList.begin(), Other.AliasList);
97+
98+
IFuncList.clear();
99+
IFuncList.splice(IFuncList.begin(), Other.IFuncList);
100+
101+
NamedMDList.clear();
102+
NamedMDList.splice(NamedMDList.begin(), Other.NamedMDList);
103+
GlobalScopeAsm = std::move(Other.GlobalScopeAsm);
104+
OwnedMemoryBuffer = std::move(Other.OwnedMemoryBuffer);
105+
Materializer = std::move(Other.Materializer);
106+
TargetTriple = std::move(Other.TargetTriple);
107+
DL = std::move(Other.DL);
108+
CurrentIntrinsicIds = std::move(Other.CurrentIntrinsicIds);
109+
UniquedIntrinsicNames = std::move(Other.UniquedIntrinsicNames);
110+
ModuleFlags = std::move(Other.ModuleFlags);
111+
Context.addModule(this);
112+
return *this;
113+
}
114+
80115
Module::~Module() {
81116
Context.removeModule(this);
82117
dropAllReferences();

llvm/unittests/IR/ModuleTest.cpp

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
#include "llvm/Pass.h"
1515
#include "llvm/Support/RandomNumberGenerator.h"
1616
#include "llvm/Support/SourceMgr.h"
17+
#include "llvm/Support/raw_ostream.h"
1718
#include "gtest/gtest.h"
1819

1920
#include <random>
@@ -326,4 +327,80 @@ TEST(ModuleTest, GlobalList) {
326327
EXPECT_EQ(M->global_size(), 1u);
327328
}
328329

330+
TEST(ModuleTest, MoveAssign) {
331+
// This tests that we can move-assign modules, we parse two modules and
332+
// move assign the second one to the first one, and check that the print
333+
// is equal to what we loaded.
334+
LLVMContext C;
335+
SMDiagnostic Err;
336+
LLVMContext Context;
337+
std::unique_ptr<Module> M1 = parseAssemblyString(R"(
338+
; ModuleID = '<string>'
339+
source_filename = "<string1>"
340+
341+
@GV1 = external global i32
342+
343+
@GA1 = alias void (), ptr @Foo1
344+
345+
define void @Foo1() {
346+
ret void
347+
}
348+
349+
!llvm.module.flags = !{!0}
350+
!llvm.dbg.cu = !{!1}
351+
!foo1 = !{!3}
352+
!bar1 = !{!4}
353+
354+
!0 = !{i32 2, !"Debug Info Version", i32 3}
355+
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang1", isOptimized: true, flags: "-O2", runtimeVersion: 0, splitDebugFilename: "abc.debug", emissionKind: LineTablesOnly)
356+
!2 = !DIFile(filename: "path/to/file1", directory: "/path/to/dir1")
357+
!3 = !DILocation(line: 12, column: 34, scope: !4)
358+
!4 = distinct !DISubprogram(name: "foo1", scope: null, spFlags: DISPFlagDefinition, unit: !1)
359+
)",
360+
Err, Context);
361+
ASSERT_TRUE(M1.get());
362+
363+
StringLiteral M2Str = R"(
364+
; ModuleID = '<string>'
365+
source_filename = "<string2>"
366+
367+
@GV2 = external global i32
368+
369+
@GA2 = alias void (), ptr @Foo2
370+
371+
define void @Foo2() {
372+
ret void
373+
}
374+
375+
!llvm.module.flags = !{!0}
376+
!llvm.dbg.cu = !{!1}
377+
!foo2 = !{!3}
378+
!bar2 = !{!4}
379+
380+
!0 = !{i32 2, !"Debug Info Version", i32 3}
381+
!1 = distinct !DICompileUnit(language: DW_LANG_C99, file: !2, producer: "clang2", isOptimized: true, flags: "-O2", runtimeVersion: 0, splitDebugFilename: "abc.debug", emissionKind: LineTablesOnly)
382+
!2 = !DIFile(filename: "path/to/file2", directory: "/path/to/dir2")
383+
!3 = !DILocation(line: 1234, column: 56, scope: !4)
384+
!4 = distinct !DISubprogram(name: "foo2", scope: null, spFlags: DISPFlagDefinition, unit: !1)
385+
)";
386+
{
387+
std::unique_ptr<Module> M2 = parseAssemblyString(M2Str, Err, Context);
388+
ASSERT_TRUE(M2.get());
389+
auto *GV1 = M1->getNamedValue("GV1");
390+
ASSERT_TRUE(GV1);
391+
auto *GV2 = M2->getNamedValue("GV2");
392+
ASSERT_TRUE(GV2);
393+
ASSERT_EQ(GV2->getParent(), &*M2);
394+
*M1 = std::move(*M2);
395+
ASSERT_EQ(GV2->getParent(), &*M1);
396+
}
397+
398+
std::string M1Print;
399+
{
400+
llvm::raw_string_ostream Os(M1Print);
401+
Os << "\n" << *M1;
402+
}
403+
ASSERT_EQ(M2Str, M1Print);
404+
}
405+
329406
} // end namespace

0 commit comments

Comments
 (0)