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