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