Skip to content

Commit 1f72fa2

Browse files
authored
[X86Backend][M68KBackend] Make Ctx in X86MCInstLower (M68KInstLower) the same as AsmPrinter.OutContext (#133352)
In `X86MCInstLower::LowerMachineOperand`, a new `MCSymbol` can be created in `GetSymbolFromOperand(MO)` where `MO.getType()` is `MachineOperand::MO_ExternalSymbol` ``` case MachineOperand::MO_ExternalSymbol: return LowerSymbolOperand(MO, GetSymbolFromOperand(MO)); ``` at https://github.com/llvm/llvm-project/blob/725a7b664b92cd2e884806de5a08900b43d43cce/llvm/lib/Target/X86/X86MCInstLower.cpp#L196 However, this newly created symbol will not be marked properly with its `IsExternal` field since `Ctx.getOrCreateSymbol(Name)` doesn't know if the newly created `MCSymbol` is for `MachineOperand::MO_ExternalSymbol`. Looking at other backends, for example `Arch64MCInstLower` is doing for handling `MC_ExternalSymbol` https://github.com/llvm/llvm-project/blob/14c36db16fc090ef494ff6d8207562c414b40e30/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp#L366-L367 https://github.com/llvm/llvm-project/blob/14c36db16fc090ef494ff6d8207562c414b40e30/llvm/lib/Target/AArch64/AArch64MCInstLower.cpp#L145-L148 It creates/gets the MCSymbol from `AsmPrinter.OutContext` instead of from `Ctx`. Moreover, `Ctx` for `AArch64MCLower` is the same as `AsmPrinter.OutContext`. https://github.com/llvm/llvm-project/blob/8e7d6baf0e013408be932758b4a5334c14a34086/llvm/lib/Target/AArch64/AArch64AsmPrinter.cpp#L100. This applies to almost all the other backends except X86 and M68k. ``` $git grep "MCInstLowering(" lib/Target/AArch64/AArch64AsmPrinter.cpp:100: : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this), lib/Target/AMDGPU/AMDGPUMCInstLower.cpp:223: AMDGPUMCInstLower MCInstLowering(OutContext, STI, *this); lib/Target/AMDGPU/AMDGPUMCInstLower.cpp:257: AMDGPUMCInstLower MCInstLowering(OutContext, STI, *this); lib/Target/AMDGPU/R600MCInstLower.cpp:52: R600MCInstLower MCInstLowering(OutContext, STI, *this); lib/Target/ARC/ARCAsmPrinter.cpp:41: MCInstLowering(&OutContext, *this) {} lib/Target/AVR/AVRAsmPrinter.cpp:196: AVRMCInstLower MCInstLowering(OutContext, *this); lib/Target/BPF/BPFAsmPrinter.cpp:144: BPFMCInstLower MCInstLowering(OutContext, *this); lib/Target/CSKY/CSKYAsmPrinter.cpp:41: : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(OutContext, *this) {} lib/Target/Lanai/LanaiAsmPrinter.cpp:147: LanaiMCInstLower MCInstLowering(OutContext, *this); lib/Target/Lanai/LanaiAsmPrinter.cpp:184: LanaiMCInstLower MCInstLowering(OutContext, *this); lib/Target/MSP430/MSP430AsmPrinter.cpp:149: MSP430MCInstLower MCInstLowering(OutContext, *this); lib/Target/Mips/MipsAsmPrinter.h:126: : AsmPrinter(TM, std::move(Streamer)), MCInstLowering(*this) {} lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp:695: WebAssemblyMCInstLower MCInstLowering(OutContext, *this); lib/Target/X86/X86MCInstLower.cpp:2200: X86MCInstLower MCInstLowering(*MF, *this); ``` This patch makes `X86MCInstLower` and `M68KInstLower` to have their `Ctx` from `AsmPrinter.OutContext` instead of getting it from `MF.getContext()` to be consistent with all the other backends. I think since normal use case (probably anything other than our un-conventional case) only handles one llvm module all the way through in the codegen pipeline till the end of code emission (AsmPrint), `AsmPrinter.OutContext` is the same as MachineFunction's MCContext, so this change is an NFC. ---- This fixes an error while running the generated code in ORC JIT for our use case with [MCLinker](https://youtu.be/yuSBEXkjfEA?si=HjgjkxJ9hLfnSvBj&t=813) (see more details below): #133291 (comment) We (Mojo) are trying to do a MC level linking so that we break llvm module into multiple submodules to compile and codegen in parallel (technically into *.o files with symbol linkage type change), but instead of archive all of them into one `.a` file, we want to fix the symbol linkage type and still produce one *.o file. The parallel codegen pipeline generates the codegen data structures in their own `MCContext` (which is `Ctx` here). So if function `f` and `g` got split into different submodules, they will have different `Ctx`. And when we try to create an external symbol with the same name for each of them with `Ctx.getOrCreate(SymName)`, we will get two different `MCSymbol*` because `f` and `g`'s `MCContext` are different and they can't see each other. This is unfortunately not what we want for external symbols. Using `AsmPrinter.OutContext` helps, since it is shared, if we try to get or create the `MCSymbol` there, we'll be able to deduplicate.
1 parent 6272e1f commit 1f72fa2

File tree

4 files changed

+178
-3
lines changed

4 files changed

+178
-3
lines changed

llvm/lib/Target/M68k/M68kMCInstLower.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ using namespace llvm;
3333
#define DEBUG_TYPE "m68k-mc-inst-lower"
3434

3535
M68kMCInstLower::M68kMCInstLower(MachineFunction &MF, M68kAsmPrinter &AP)
36-
: Ctx(MF.getContext()), MF(MF), TM(MF.getTarget()), MAI(*TM.getMCAsmInfo()),
36+
: Ctx(AP.OutContext), MF(MF), TM(MF.getTarget()), MAI(*TM.getMCAsmInfo()),
3737
AsmPrinter(AP) {}
3838

3939
MCSymbol *

llvm/lib/Target/X86/X86MCInstLower.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -142,8 +142,8 @@ void X86AsmPrinter::EmitAndCountInstruction(MCInst &Inst) {
142142

143143
X86MCInstLower::X86MCInstLower(const MachineFunction &mf,
144144
X86AsmPrinter &asmprinter)
145-
: Ctx(mf.getContext()), MF(mf), TM(mf.getTarget()), MAI(*TM.getMCAsmInfo()),
146-
AsmPrinter(asmprinter) {}
145+
: Ctx(asmprinter.OutContext), MF(mf), TM(mf.getTarget()),
146+
MAI(*TM.getMCAsmInfo()), AsmPrinter(asmprinter) {}
147147

148148
MachineModuleInfoMachO &X86MCInstLower::getMachOMMI() const {
149149
return AsmPrinter.MMI->getObjFileInfo<MachineModuleInfoMachO>();

llvm/unittests/CodeGen/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,7 @@ add_llvm_unittest(CodeGenTests
4747
TargetOptionsTest.cpp
4848
TestAsmPrinter.cpp
4949
MLRegAllocDevelopmentFeatures.cpp
50+
X86MCInstLowerTest.cpp
5051
)
5152

5253
add_subdirectory(GlobalISel)
Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
//===- llvm/unittest/CodeGen/X86MCInstLowerTest.cpp
2+
//-------------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "TestAsmPrinter.h"
11+
#include "llvm/AsmParser/Parser.h"
12+
#include "llvm/CodeGen/AsmPrinter.h"
13+
#include "llvm/CodeGen/MachineModuleInfo.h"
14+
#include "llvm/CodeGen/TargetLowering.h"
15+
#include "llvm/CodeGen/TargetPassConfig.h"
16+
#include "llvm/IR/LegacyPassManager.h"
17+
#include "llvm/IR/MDBuilder.h"
18+
#include "llvm/IR/Module.h"
19+
#include "llvm/MC/MCStreamer.h"
20+
#include "llvm/MC/TargetRegistry.h"
21+
#include "llvm/Support/SourceMgr.h"
22+
#include "llvm/Support/TargetSelect.h"
23+
#include "llvm/Target/TargetLoweringObjectFile.h"
24+
#include "llvm/Target/TargetMachine.h"
25+
#include "gtest/gtest.h"
26+
27+
namespace llvm {
28+
29+
class X86MCInstLowerTest : public testing::Test {
30+
protected:
31+
static void SetUpTestCase() {
32+
InitializeAllTargetMCs();
33+
InitializeAllTargetInfos();
34+
InitializeAllTargets();
35+
InitializeAllAsmPrinters();
36+
}
37+
38+
// Function to setup codegen pipeline and returns the AsmPrinter.
39+
AsmPrinter *addPassesToEmitFile(llvm::legacy::PassManagerBase &PM,
40+
llvm::raw_pwrite_stream &Out,
41+
llvm::CodeGenFileType FileType,
42+
llvm::MachineModuleInfoWrapperPass *MMIWP) {
43+
TargetPassConfig *PassConfig = TM->createPassConfig(PM);
44+
45+
PassConfig->setDisableVerify(true);
46+
PM.add(PassConfig);
47+
PM.add(MMIWP);
48+
49+
if (PassConfig->addISelPasses())
50+
return nullptr;
51+
52+
PassConfig->addMachinePasses();
53+
PassConfig->setInitialized();
54+
55+
MC.reset(new MCContext(TM->getTargetTriple(), TM->getMCAsmInfo(),
56+
TM->getMCRegisterInfo(), TM->getMCSubtargetInfo()));
57+
MC->setObjectFileInfo(TM->getObjFileLowering());
58+
TM->getObjFileLowering()->Initialize(*MC, *TM);
59+
MC->setObjectFileInfo(TM->getObjFileLowering());
60+
61+
// Use a new MCContext for AsmPrinter for testing.
62+
// AsmPrinter.OutContext will be different from
63+
// MachineFunction's MCContext in MMIWP.
64+
Expected<std::unique_ptr<MCStreamer>> MCStreamerOrErr =
65+
TM->createMCStreamer(Out, nullptr, FileType, *MC);
66+
67+
if (auto Err = MCStreamerOrErr.takeError())
68+
return nullptr;
69+
70+
AsmPrinter *Printer =
71+
TM->getTarget().createAsmPrinter(*TM, std::move(*MCStreamerOrErr));
72+
73+
if (!Printer)
74+
return nullptr;
75+
76+
PM.add(Printer);
77+
78+
return Printer;
79+
}
80+
81+
void SetUp() override {
82+
// Module to compile.
83+
const char *FooStr = R""""(
84+
@G = external global i32
85+
86+
define i32 @foo() {
87+
%1 = load i32, i32* @G; load the global variable
88+
%2 = call i32 @f()
89+
%3 = mul i32 %1, %2
90+
ret i32 %3
91+
}
92+
93+
declare i32 @f() #0
94+
)"""";
95+
StringRef AssemblyF(FooStr);
96+
97+
// Get target triple for X86_64
98+
Triple TargetTriple("x86_64--");
99+
std::string Error;
100+
const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
101+
// Skip the test if target is not built.
102+
if (!T)
103+
GTEST_SKIP();
104+
105+
// Get TargetMachine.
106+
// Use Reloc::Model::PIC_ and CodeModel::Model::Large
107+
// to get GOT during codegen as MO_ExternalSymbol.
108+
TargetOptions Options;
109+
TM = std::unique_ptr<TargetMachine>(T->createTargetMachine(
110+
TargetTriple, "", "", Options, Reloc::Model::PIC_,
111+
CodeModel::Model::Large, CodeGenOptLevel::Default));
112+
if (!TM)
113+
GTEST_SKIP();
114+
115+
SMDiagnostic SMError;
116+
117+
// Parse the module.
118+
M = parseAssemblyString(AssemblyF, SMError, Context);
119+
if (!M)
120+
report_fatal_error(SMError.getMessage());
121+
M->setDataLayout(TM->createDataLayout());
122+
123+
// Get llvm::Function from M
124+
Foo = M->getFunction("foo");
125+
if (!Foo)
126+
report_fatal_error("foo?");
127+
128+
// Prepare the MCContext for codegen M.
129+
// MachineFunction for Foo will have this MCContext.
130+
MCFoo.reset(new MCContext(TargetTriple, TM->getMCAsmInfo(),
131+
TM->getMCRegisterInfo(),
132+
TM->getMCSubtargetInfo()));
133+
MCFoo->setObjectFileInfo(TM->getObjFileLowering());
134+
TM->getObjFileLowering()->Initialize(*MCFoo, *TM);
135+
MCFoo->setObjectFileInfo(TM->getObjFileLowering());
136+
}
137+
138+
LLVMContext Context;
139+
std::unique_ptr<TargetMachine> TM;
140+
std::unique_ptr<Module> M;
141+
142+
std::unique_ptr<MCContext> MC;
143+
std::unique_ptr<MCContext> MCFoo;
144+
145+
Function *Foo;
146+
std::unique_ptr<MachineFunction> MFFoo;
147+
};
148+
149+
TEST_F(X86MCInstLowerTest, moExternalSymbol_MCSYMBOL) {
150+
151+
MachineModuleInfoWrapperPass *MMIWP =
152+
new MachineModuleInfoWrapperPass(TM.get(), &*MCFoo);
153+
154+
legacy::PassManager PassMgrF;
155+
SmallString<1024> Buf;
156+
llvm::raw_svector_ostream OS(Buf);
157+
AsmPrinter *Printer =
158+
addPassesToEmitFile(PassMgrF, OS, CodeGenFileType::AssemblyFile, MMIWP);
159+
PassMgrF.run(*M);
160+
161+
// Check GOT MCSymbol is from Printer.OutContext.
162+
MCSymbol *GOTPrinterPtr =
163+
Printer->OutContext.lookupSymbol("_GLOBAL_OFFSET_TABLE_");
164+
165+
// Check GOT MCSymbol is NOT from MachineFunction's MCContext.
166+
MCSymbol *GOTMFCtxPtr =
167+
MMIWP->getMMI().getMachineFunction(*Foo)->getContext().lookupSymbol(
168+
"_GLOBAL_OFFSET_TABLE_");
169+
170+
EXPECT_NE(GOTPrinterPtr, nullptr);
171+
EXPECT_EQ(GOTMFCtxPtr, nullptr);
172+
}
173+
174+
} // end namespace llvm

0 commit comments

Comments
 (0)