1 //==-- AArch64MCInstLower.cpp - Convert AArch64 MachineInstr to an MCInst --==//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file contains code to lower AArch64 MachineInstrs to their corresponding
10 // MCInst records.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "AArch64MCInstLower.h"
15 #include "AArch64MachineFunctionInfo.h"
16 #include "MCTargetDesc/AArch64MCAsmInfo.h"
17 #include "Utils/AArch64BaseInfo.h"
18 #include "llvm/CodeGen/AsmPrinter.h"
19 #include "llvm/CodeGen/MachineBasicBlock.h"
20 #include "llvm/CodeGen/MachineInstr.h"
21 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
22 #include "llvm/IR/Function.h"
23 #include "llvm/IR/Mangler.h"
24 #include "llvm/MC/MCContext.h"
25 #include "llvm/MC/MCExpr.h"
26 #include "llvm/MC/MCInst.h"
27 #include "llvm/MC/MCStreamer.h"
28 #include "llvm/Object/COFF.h"
29 #include "llvm/Support/CodeGen.h"
30 #include "llvm/Support/CommandLine.h"
31 #include "llvm/Target/TargetLoweringObjectFile.h"
32 #include "llvm/Target/TargetMachine.h"
33 using namespace llvm;
34 using namespace llvm::object;
35
36 extern cl::opt<bool> EnableAArch64ELFLocalDynamicTLSGeneration;
37
AArch64MCInstLower(MCContext & ctx,AsmPrinter & printer)38 AArch64MCInstLower::AArch64MCInstLower(MCContext &ctx, AsmPrinter &printer)
39 : Ctx(ctx), Printer(printer) {}
40
41 MCSymbol *
GetGlobalAddressSymbol(const MachineOperand & MO) const42 AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const {
43 return GetGlobalValueSymbol(MO.getGlobal(), MO.getTargetFlags());
44 }
45
GetGlobalValueSymbol(const GlobalValue * GV,unsigned TargetFlags) const46 MCSymbol *AArch64MCInstLower::GetGlobalValueSymbol(const GlobalValue *GV,
47 unsigned TargetFlags) const {
48 const Triple &TheTriple = Printer.TM.getTargetTriple();
49 if (!TheTriple.isOSBinFormatCOFF())
50 return Printer.getSymbolPreferLocal(*GV);
51
52 assert(TheTriple.isOSWindows() &&
53 "Windows is the only supported COFF target");
54
55 bool IsIndirect =
56 (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB));
57 if (!IsIndirect) {
58 // For ARM64EC, symbol lookup in the MSVC linker has limited awareness
59 // of ARM64EC mangling ("#"/"$$h"). So object files need to refer to both
60 // the mangled and unmangled names of ARM64EC symbols, even if they aren't
61 // actually used by any relocations. Emit the necessary references here.
62 if (!TheTriple.isWindowsArm64EC() || !isa<Function>(GV) ||
63 !GV->hasExternalLinkage())
64 return Printer.getSymbol(GV);
65
66 StringRef Name = Printer.getSymbol(GV)->getName();
67 // Don't mangle ARM64EC runtime functions.
68 static constexpr StringLiteral ExcludedFns[] = {
69 "__os_arm64x_check_icall_cfg", "__os_arm64x_dispatch_call_no_redirect",
70 "__os_arm64x_check_icall"};
71 if (is_contained(ExcludedFns, Name))
72 return Printer.getSymbol(GV);
73
74 if (std::optional<std::string> MangledName =
75 getArm64ECMangledFunctionName(Name.str())) {
76 MCSymbol *MangledSym = Ctx.getOrCreateSymbol(MangledName.value());
77 if (!cast<Function>(GV)->hasMetadata("arm64ec_hasguestexit")) {
78 Printer.OutStreamer->emitSymbolAttribute(Printer.getSymbol(GV),
79 MCSA_WeakAntiDep);
80 Printer.OutStreamer->emitAssignment(
81 Printer.getSymbol(GV), MCSymbolRefExpr::create(MangledSym, Ctx));
82 Printer.OutStreamer->emitSymbolAttribute(MangledSym, MCSA_WeakAntiDep);
83 Printer.OutStreamer->emitAssignment(
84 MangledSym, MCSymbolRefExpr::create(Printer.getSymbol(GV), Ctx));
85 }
86
87 if (TargetFlags & AArch64II::MO_ARM64EC_CALLMANGLE)
88 return MangledSym;
89 }
90
91 return Printer.getSymbol(GV);
92 }
93
94 SmallString<128> Name;
95
96 if ((TargetFlags & AArch64II::MO_DLLIMPORT) &&
97 TheTriple.isWindowsArm64EC() &&
98 !(TargetFlags & AArch64II::MO_ARM64EC_CALLMANGLE) &&
99 isa<Function>(GV)) {
100 // __imp_aux is specific to arm64EC; it represents the actual address of
101 // an imported function without any thunks.
102 //
103 // If we see a reference to an "aux" symbol, also emit a reference to the
104 // corresponding non-aux symbol. Otherwise, the Microsoft linker behaves
105 // strangely when linking against x64 import libararies.
106 //
107 // emitSymbolAttribute() doesn't have any real effect here; it just
108 // ensures the symbol name appears in the assembly without any
109 // side-effects. It might make sense to design a cleaner way to express
110 // this.
111 Name = "__imp_";
112 Printer.TM.getNameWithPrefix(Name, GV,
113 Printer.getObjFileLowering().getMangler());
114 MCSymbol *ExtraSym = Ctx.getOrCreateSymbol(Name);
115 Printer.OutStreamer->emitSymbolAttribute(ExtraSym, MCSA_Global);
116
117 Name = "__imp_aux_";
118 } else if (TargetFlags & AArch64II::MO_DLLIMPORT) {
119 Name = "__imp_";
120 } else if (TargetFlags & AArch64II::MO_COFFSTUB) {
121 Name = ".refptr.";
122 }
123 Printer.TM.getNameWithPrefix(Name, GV,
124 Printer.getObjFileLowering().getMangler());
125
126 MCSymbol *MCSym = Ctx.getOrCreateSymbol(Name);
127
128 if (TargetFlags & AArch64II::MO_COFFSTUB) {
129 MachineModuleInfoCOFF &MMICOFF =
130 Printer.MMI->getObjFileInfo<MachineModuleInfoCOFF>();
131 MachineModuleInfoImpl::StubValueTy &StubSym =
132 MMICOFF.getGVStubEntry(MCSym);
133
134 if (!StubSym.getPointer())
135 StubSym = MachineModuleInfoImpl::StubValueTy(Printer.getSymbol(GV), true);
136 }
137
138 return MCSym;
139 }
140
141 MCSymbol *
GetExternalSymbolSymbol(const MachineOperand & MO) const142 AArch64MCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const {
143 return Printer.GetExternalSymbolSymbol(MO.getSymbolName());
144 }
145
lowerSymbolOperandMachO(const MachineOperand & MO,MCSymbol * Sym) const146 MCOperand AArch64MCInstLower::lowerSymbolOperandMachO(const MachineOperand &MO,
147 MCSymbol *Sym) const {
148 // FIXME: We would like an efficient form for this, so we don't have to do a
149 // lot of extra uniquing.
150 auto Spec = AArch64::S_None;
151 if ((MO.getTargetFlags() & AArch64II::MO_GOT) != 0) {
152 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE)
153 Spec = AArch64::S_MACHO_GOTPAGE;
154 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
155 AArch64II::MO_PAGEOFF)
156 Spec = AArch64::S_MACHO_GOTPAGEOFF;
157 else
158 llvm_unreachable("Unexpected target flags with MO_GOT on GV operand");
159 } else if ((MO.getTargetFlags() & AArch64II::MO_TLS) != 0) {
160 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE)
161 Spec = AArch64::S_MACHO_TLVPPAGE;
162 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
163 AArch64II::MO_PAGEOFF)
164 Spec = AArch64::S_MACHO_TLVPPAGEOFF;
165 else
166 llvm_unreachable("Unexpected target flags with MO_TLS on GV operand");
167 } else {
168 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE)
169 Spec = AArch64::S_MACHO_PAGE;
170 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
171 AArch64II::MO_PAGEOFF)
172 Spec = AArch64::S_MACHO_PAGEOFF;
173 }
174 // TODO: Migrate to MCSpecifierExpr::create like ELF.
175 const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Spec, Ctx);
176 if (!MO.isJTI() && MO.getOffset())
177 Expr = MCBinaryExpr::createAdd(
178 Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
179 return MCOperand::createExpr(Expr);
180 }
181
lowerSymbolOperandELF(const MachineOperand & MO,MCSymbol * Sym) const182 MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO,
183 MCSymbol *Sym) const {
184 uint32_t RefFlags = 0;
185
186 if (MO.getTargetFlags() & AArch64II::MO_GOT) {
187 const MachineFunction *MF = MO.getParent()->getParent()->getParent();
188 RefFlags |= (MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()
189 ? AArch64::S_GOT_AUTH
190 : AArch64::S_GOT);
191 } else if (MO.getTargetFlags() & AArch64II::MO_TLS) {
192 TLSModel::Model Model;
193 if (MO.isGlobal()) {
194 const MachineFunction *MF = MO.getParent()->getParent()->getParent();
195 if (MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()) {
196 Model = TLSModel::GeneralDynamic;
197 } else {
198 const GlobalValue *GV = MO.getGlobal();
199 Model = Printer.TM.getTLSModel(GV);
200 if (!EnableAArch64ELFLocalDynamicTLSGeneration &&
201 Model == TLSModel::LocalDynamic)
202 Model = TLSModel::GeneralDynamic;
203 }
204 } else {
205 assert(MO.isSymbol() &&
206 StringRef(MO.getSymbolName()) == "_TLS_MODULE_BASE_" &&
207 "unexpected external TLS symbol");
208 // The general dynamic access sequence is used to get the
209 // address of _TLS_MODULE_BASE_.
210 Model = TLSModel::GeneralDynamic;
211 }
212 switch (Model) {
213 case TLSModel::InitialExec:
214 RefFlags |= AArch64::S_GOTTPREL;
215 break;
216 case TLSModel::LocalExec:
217 RefFlags |= AArch64::S_TPREL;
218 break;
219 case TLSModel::LocalDynamic:
220 RefFlags |= AArch64::S_DTPREL;
221 break;
222 case TLSModel::GeneralDynamic: {
223 // TODO: it's probably better to introduce MO_TLS_AUTH or smth and avoid
224 // running hasELFSignedGOT() every time, but existing flags already
225 // cover all 12 bits of SubReg_TargetFlags field in MachineOperand, and
226 // making the field wider breaks static assertions.
227 const MachineFunction *MF = MO.getParent()->getParent()->getParent();
228 RefFlags |= MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()
229 ? AArch64::S_TLSDESC_AUTH
230 : AArch64::S_TLSDESC;
231 break;
232 }
233 }
234 } else if (MO.getTargetFlags() & AArch64II::MO_PREL) {
235 RefFlags |= AArch64::S_PREL;
236 } else {
237 // No modifier means this is a generic reference, classified as absolute for
238 // the cases where it matters (:abs_g0: etc).
239 RefFlags |= AArch64::S_ABS;
240 }
241
242 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE)
243 RefFlags |= AArch64::S_PAGE;
244 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
245 AArch64II::MO_PAGEOFF)
246 RefFlags |= AArch64::S_PAGEOFF;
247 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G3)
248 RefFlags |= AArch64::S_G3;
249 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G2)
250 RefFlags |= AArch64::S_G2;
251 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G1)
252 RefFlags |= AArch64::S_G1;
253 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G0)
254 RefFlags |= AArch64::S_G0;
255 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_HI12)
256 RefFlags |= AArch64::S_HI12;
257
258 if (MO.getTargetFlags() & AArch64II::MO_NC)
259 RefFlags |= AArch64::S_NC;
260
261 const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
262 if (!MO.isJTI() && MO.getOffset())
263 Expr = MCBinaryExpr::createAdd(
264 Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
265
266 Expr = MCSpecifierExpr::create(Expr, RefFlags, Ctx);
267 return MCOperand::createExpr(Expr);
268 }
269
lowerSymbolOperandCOFF(const MachineOperand & MO,MCSymbol * Sym) const270 MCOperand AArch64MCInstLower::lowerSymbolOperandCOFF(const MachineOperand &MO,
271 MCSymbol *Sym) const {
272 uint32_t RefFlags = 0;
273
274 if (MO.getTargetFlags() & AArch64II::MO_TLS) {
275 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGEOFF)
276 RefFlags |= AArch64::S_SECREL_LO12;
277 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
278 AArch64II::MO_HI12)
279 RefFlags |= AArch64::S_SECREL_HI12;
280
281 } else if (MO.getTargetFlags() & AArch64II::MO_S) {
282 RefFlags |= AArch64::S_SABS;
283 } else {
284 RefFlags |= AArch64::S_ABS;
285
286 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE)
287 RefFlags |= AArch64::S_PAGE;
288 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) ==
289 AArch64II::MO_PAGEOFF)
290 RefFlags |= AArch64::S_PAGEOFF | AArch64::S_NC;
291 }
292
293 if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G3)
294 RefFlags |= AArch64::S_G3;
295 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G2)
296 RefFlags |= AArch64::S_G2;
297 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G1)
298 RefFlags |= AArch64::S_G1;
299 else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G0)
300 RefFlags |= AArch64::S_G0;
301
302 // FIXME: Currently we only set VK_NC for MO_G3/MO_G2/MO_G1/MO_G0. This is
303 // because setting VK_NC for others would mean setting their respective
304 // RefFlags correctly. We should do this in a separate patch.
305 if (MO.getTargetFlags() & AArch64II::MO_NC) {
306 auto MOFrag = (MO.getTargetFlags() & AArch64II::MO_FRAGMENT);
307 if (MOFrag == AArch64II::MO_G3 || MOFrag == AArch64II::MO_G2 ||
308 MOFrag == AArch64II::MO_G1 || MOFrag == AArch64II::MO_G0)
309 RefFlags |= AArch64::S_NC;
310 }
311
312 const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx);
313 if (!MO.isJTI() && MO.getOffset())
314 Expr = MCBinaryExpr::createAdd(
315 Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx);
316
317 Expr = MCSpecifierExpr::create(Expr, RefFlags, Ctx);
318 return MCOperand::createExpr(Expr);
319 }
320
LowerSymbolOperand(const MachineOperand & MO,MCSymbol * Sym) const321 MCOperand AArch64MCInstLower::LowerSymbolOperand(const MachineOperand &MO,
322 MCSymbol *Sym) const {
323 if (Printer.TM.getTargetTriple().isOSBinFormatMachO())
324 return lowerSymbolOperandMachO(MO, Sym);
325 if (Printer.TM.getTargetTriple().isOSBinFormatCOFF())
326 return lowerSymbolOperandCOFF(MO, Sym);
327
328 assert(Printer.TM.getTargetTriple().isOSBinFormatELF() && "Invalid target");
329 return lowerSymbolOperandELF(MO, Sym);
330 }
331
lowerOperand(const MachineOperand & MO,MCOperand & MCOp) const332 bool AArch64MCInstLower::lowerOperand(const MachineOperand &MO,
333 MCOperand &MCOp) const {
334 switch (MO.getType()) {
335 default:
336 llvm_unreachable("unknown operand type");
337 case MachineOperand::MO_Register:
338 // Ignore all implicit register operands.
339 if (MO.isImplicit())
340 return false;
341 MCOp = MCOperand::createReg(MO.getReg());
342 break;
343 case MachineOperand::MO_RegisterMask:
344 // Regmasks are like implicit defs.
345 return false;
346 case MachineOperand::MO_Immediate:
347 MCOp = MCOperand::createImm(MO.getImm());
348 break;
349 case MachineOperand::MO_MachineBasicBlock:
350 MCOp = MCOperand::createExpr(
351 MCSymbolRefExpr::create(MO.getMBB()->getSymbol(), Ctx));
352 break;
353 case MachineOperand::MO_GlobalAddress:
354 MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO));
355 break;
356 case MachineOperand::MO_ExternalSymbol:
357 MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO));
358 break;
359 case MachineOperand::MO_MCSymbol:
360 MCOp = LowerSymbolOperand(MO, MO.getMCSymbol());
361 break;
362 case MachineOperand::MO_JumpTableIndex:
363 MCOp = LowerSymbolOperand(MO, Printer.GetJTISymbol(MO.getIndex()));
364 break;
365 case MachineOperand::MO_ConstantPoolIndex:
366 MCOp = LowerSymbolOperand(MO, Printer.GetCPISymbol(MO.getIndex()));
367 break;
368 case MachineOperand::MO_BlockAddress:
369 MCOp = LowerSymbolOperand(
370 MO, Printer.GetBlockAddressSymbol(MO.getBlockAddress()));
371 break;
372 }
373 return true;
374 }
375
Lower(const MachineInstr * MI,MCInst & OutMI) const376 void AArch64MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const {
377 OutMI.setOpcode(MI->getOpcode());
378
379 for (const MachineOperand &MO : MI->operands()) {
380 MCOperand MCOp;
381 if (lowerOperand(MO, MCOp))
382 OutMI.addOperand(MCOp);
383 }
384
385 switch (OutMI.getOpcode()) {
386 case AArch64::CATCHRET:
387 OutMI = MCInst();
388 OutMI.setOpcode(AArch64::RET);
389 OutMI.addOperand(MCOperand::createReg(AArch64::LR));
390 break;
391 case AArch64::CLEANUPRET:
392 OutMI = MCInst();
393 OutMI.setOpcode(AArch64::RET);
394 OutMI.addOperand(MCOperand::createReg(AArch64::LR));
395 break;
396 }
397 }
398