| //==-- AArch64MCInstLower.cpp - Convert AArch64 MachineInstr to an MCInst --==// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This file contains code to lower AArch64 MachineInstrs to their corresponding | |
| // MCInst records. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "AArch64MCInstLower.h" | |
| #include "AArch64MachineFunctionInfo.h" | |
| #include "AArch64Subtarget.h" | |
| #include "MCTargetDesc/AArch64MCAsmInfo.h" | |
| #include "Utils/AArch64BaseInfo.h" | |
| #include "llvm/CodeGen/AsmPrinter.h" | |
| #include "llvm/CodeGen/MachineBasicBlock.h" | |
| #include "llvm/CodeGen/MachineInstr.h" | |
| #include "llvm/CodeGen/MachineModuleInfoImpls.h" | |
| #include "llvm/IR/Function.h" | |
| #include "llvm/IR/Mangler.h" | |
| #include "llvm/IR/Module.h" | |
| #include "llvm/MC/MCContext.h" | |
| #include "llvm/MC/MCExpr.h" | |
| #include "llvm/MC/MCInst.h" | |
| #include "llvm/MC/MCStreamer.h" | |
| #include "llvm/Object/COFF.h" | |
| #include "llvm/Support/CodeGen.h" | |
| #include "llvm/Target/TargetLoweringObjectFile.h" | |
| #include "llvm/Target/TargetMachine.h" | |
| using namespace llvm; | |
| using namespace llvm::object; | |
| AArch64MCInstLower::AArch64MCInstLower(MCContext &ctx, AsmPrinter &printer) | |
| : Ctx(ctx), Printer(printer) {} | |
| MCSymbol * | |
| AArch64MCInstLower::GetGlobalAddressSymbol(const MachineOperand &MO) const { | |
| return GetGlobalValueSymbol(MO.getGlobal(), MO.getTargetFlags()); | |
| } | |
| MCSymbol *AArch64MCInstLower::GetGlobalValueSymbol(const GlobalValue *GV, | |
| unsigned TargetFlags) const { | |
| const Triple &TheTriple = GV->getParent()->getTargetTriple(); | |
| if (!TheTriple.isOSBinFormatCOFF()) | |
| return Printer.getSymbolPreferLocal(*GV); | |
| assert(TheTriple.isOSWindows() && | |
| "Windows is the only supported COFF target"); | |
| bool IsIndirect = | |
| (TargetFlags & (AArch64II::MO_DLLIMPORT | AArch64II::MO_COFFSTUB)); | |
| if (!IsIndirect) { | |
| // For ARM64EC, symbol lookup in the MSVC linker has limited awareness | |
| // of ARM64EC mangling ("#"/"$$h"). So object files need to refer to both | |
| // the mangled and unmangled names of ARM64EC symbols, even if they aren't | |
| // actually used by any relocations. Emit the necessary references here. | |
| if (!TheTriple.isWindowsArm64EC() || !isa<Function>(GV) || | |
| !GV->hasExternalLinkage()) | |
| return Printer.getSymbol(GV); | |
| StringRef Name = Printer.getSymbol(GV)->getName(); | |
| // Don't mangle ARM64EC runtime functions. | |
| static constexpr StringLiteral ExcludedFns[] = { | |
| "__os_arm64x_check_icall_cfg", "__os_arm64x_dispatch_call_no_redirect", | |
| "__os_arm64x_check_icall"}; | |
| if (is_contained(ExcludedFns, Name)) | |
| return Printer.getSymbol(GV); | |
| if (std::optional<std::string> MangledName = | |
| getArm64ECMangledFunctionName(Name.str())) { | |
| MCSymbol *MangledSym = Ctx.getOrCreateSymbol(MangledName.value()); | |
| if (!cast<Function>(GV)->hasMetadata("arm64ec_hasguestexit")) { | |
| Printer.OutStreamer->emitSymbolAttribute(Printer.getSymbol(GV), | |
| MCSA_WeakAntiDep); | |
| Printer.OutStreamer->emitAssignment( | |
| Printer.getSymbol(GV), MCSymbolRefExpr::create(MangledSym, Ctx)); | |
| Printer.OutStreamer->emitSymbolAttribute(MangledSym, MCSA_WeakAntiDep); | |
| Printer.OutStreamer->emitAssignment( | |
| MangledSym, MCSymbolRefExpr::create(Printer.getSymbol(GV), Ctx)); | |
| } | |
| if (TargetFlags & AArch64II::MO_ARM64EC_CALLMANGLE) | |
| return MangledSym; | |
| } | |
| return Printer.getSymbol(GV); | |
| } | |
| SmallString<128> Name; | |
| if ((TargetFlags & AArch64II::MO_DLLIMPORT) && | |
| TheTriple.isWindowsArm64EC() && | |
| !(TargetFlags & AArch64II::MO_ARM64EC_CALLMANGLE) && | |
| isa<Function>(GV)) { | |
| // __imp_aux is specific to arm64EC; it represents the actual address of | |
| // an imported function without any thunks. | |
| // | |
| // If we see a reference to an "aux" symbol, also emit a reference to the | |
| // corresponding non-aux symbol. Otherwise, the Microsoft linker behaves | |
| // strangely when linking against x64 import libraries. | |
| // | |
| // emitSymbolAttribute() doesn't have any real effect here; it just | |
| // ensures the symbol name appears in the assembly without any | |
| // side-effects. It might make sense to design a cleaner way to express | |
| // this. | |
| Name = "__imp_"; | |
| Printer.TM.getNameWithPrefix(Name, GV, | |
| Printer.getObjFileLowering().getMangler()); | |
| MCSymbol *ExtraSym = Ctx.getOrCreateSymbol(Name); | |
| Printer.OutStreamer->emitSymbolAttribute(ExtraSym, MCSA_Global); | |
| Name = "__imp_aux_"; | |
| } else if (TargetFlags & AArch64II::MO_DLLIMPORT) { | |
| Name = "__imp_"; | |
| } else if (TargetFlags & AArch64II::MO_COFFSTUB) { | |
| Name = ".refptr."; | |
| } | |
| Printer.TM.getNameWithPrefix(Name, GV, | |
| Printer.getObjFileLowering().getMangler()); | |
| MCSymbol *MCSym = Ctx.getOrCreateSymbol(Name); | |
| if (TargetFlags & AArch64II::MO_COFFSTUB) { | |
| MachineModuleInfoCOFF &MMICOFF = | |
| Printer.MMI->getObjFileInfo<MachineModuleInfoCOFF>(); | |
| MachineModuleInfoImpl::StubValueTy &StubSym = | |
| MMICOFF.getGVStubEntry(MCSym); | |
| if (!StubSym.getPointer()) | |
| StubSym = MachineModuleInfoImpl::StubValueTy(Printer.getSymbol(GV), true); | |
| } | |
| return MCSym; | |
| } | |
| MCSymbol * | |
| AArch64MCInstLower::GetExternalSymbolSymbol(const MachineOperand &MO) const { | |
| return Printer.GetExternalSymbolSymbol(MO.getSymbolName()); | |
| } | |
| MCOperand AArch64MCInstLower::lowerSymbolOperandMachO(const MachineOperand &MO, | |
| MCSymbol *Sym) const { | |
| // FIXME: We would like an efficient form for this, so we don't have to do a | |
| // lot of extra uniquing. | |
| auto Spec = AArch64::S_None; | |
| if ((MO.getTargetFlags() & AArch64II::MO_GOT) != 0) { | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE) | |
| Spec = AArch64::S_MACHO_GOTPAGE; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_PAGEOFF) | |
| Spec = AArch64::S_MACHO_GOTPAGEOFF; | |
| else | |
| llvm_unreachable("Unexpected target flags with MO_GOT on GV operand"); | |
| } else if ((MO.getTargetFlags() & AArch64II::MO_TLS) != 0) { | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE) | |
| Spec = AArch64::S_MACHO_TLVPPAGE; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_PAGEOFF) | |
| Spec = AArch64::S_MACHO_TLVPPAGEOFF; | |
| else | |
| llvm_unreachable("Unexpected target flags with MO_TLS on GV operand"); | |
| } else { | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE) | |
| Spec = AArch64::S_MACHO_PAGE; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_PAGEOFF) | |
| Spec = AArch64::S_MACHO_PAGEOFF; | |
| } | |
| // TODO: Migrate to MCSpecifierExpr::create like ELF. | |
| const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Spec, Ctx); | |
| if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) | |
| Expr = MCBinaryExpr::createAdd( | |
| Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); | |
| return MCOperand::createExpr(Expr); | |
| } | |
| MCOperand AArch64MCInstLower::lowerSymbolOperandELF(const MachineOperand &MO, | |
| MCSymbol *Sym) const { | |
| uint32_t RefFlags = 0; | |
| if (MO.getTargetFlags() & AArch64II::MO_GOT) { | |
| const MachineFunction *MF = Printer.MF; | |
| RefFlags |= (MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT() | |
| ? AArch64::S_GOT_AUTH | |
| : AArch64::S_GOT); | |
| } else if (MO.getTargetFlags() & AArch64II::MO_TLS) { | |
| TLSModel::Model Model; | |
| if (MO.isGlobal()) { | |
| const MachineFunction *MF = Printer.MF; | |
| if (MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT()) { | |
| Model = TLSModel::GeneralDynamic; | |
| } else { | |
| const GlobalValue *GV = MO.getGlobal(); | |
| Model = Printer.TM.getTLSModel(GV); | |
| if (!MF->getSubtarget<AArch64Subtarget>() | |
| .getCLOpts() | |
| .elf_ldtls_generation && | |
| Model == TLSModel::LocalDynamic) | |
| Model = TLSModel::GeneralDynamic; | |
| } | |
| } else { | |
| assert(MO.isSymbol() && | |
| StringRef(MO.getSymbolName()) == "_TLS_MODULE_BASE_" && | |
| "unexpected external TLS symbol"); | |
| // The general dynamic access sequence is used to get the | |
| // address of _TLS_MODULE_BASE_. | |
| Model = TLSModel::GeneralDynamic; | |
| } | |
| switch (Model) { | |
| case TLSModel::InitialExec: | |
| RefFlags |= AArch64::S_GOTTPREL; | |
| break; | |
| case TLSModel::LocalExec: | |
| RefFlags |= AArch64::S_TPREL; | |
| break; | |
| case TLSModel::LocalDynamic: | |
| RefFlags |= AArch64::S_DTPREL; | |
| break; | |
| case TLSModel::GeneralDynamic: { | |
| // TODO: it's probably better to introduce MO_TLS_AUTH or smth and avoid | |
| // running hasELFSignedGOT() every time, but existing flags already | |
| // cover all 12 bits of SubReg_TargetFlags field in MachineOperand, and | |
| // making the field wider breaks static assertions. | |
| const MachineFunction *MF = Printer.MF; | |
| RefFlags |= MF->getInfo<AArch64FunctionInfo>()->hasELFSignedGOT() | |
| ? AArch64::S_TLSDESC_AUTH | |
| : AArch64::S_TLSDESC; | |
| break; | |
| } | |
| } | |
| } else if (MO.getTargetFlags() & AArch64II::MO_PREL) { | |
| RefFlags |= AArch64::S_PREL; | |
| } else { | |
| // No modifier means this is a generic reference, classified as absolute for | |
| // the cases where it matters (:abs_g0: etc). | |
| RefFlags |= AArch64::S_ABS; | |
| } | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE) | |
| RefFlags |= AArch64::S_PAGE; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_PAGEOFF) | |
| RefFlags |= AArch64::S_PAGEOFF; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G3) | |
| RefFlags |= AArch64::S_G3; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G2) | |
| RefFlags |= AArch64::S_G2; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G1) | |
| RefFlags |= AArch64::S_G1; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G0) | |
| RefFlags |= AArch64::S_G0; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_HI12) | |
| RefFlags |= AArch64::S_HI12; | |
| if (MO.getTargetFlags() & AArch64II::MO_NC) | |
| RefFlags |= AArch64::S_NC; | |
| const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); | |
| if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) | |
| Expr = MCBinaryExpr::createAdd( | |
| Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); | |
| Expr = MCSpecifierExpr::create(Expr, RefFlags, Ctx); | |
| return MCOperand::createExpr(Expr); | |
| } | |
| MCOperand AArch64MCInstLower::lowerSymbolOperandCOFF(const MachineOperand &MO, | |
| MCSymbol *Sym) const { | |
| uint32_t RefFlags = 0; | |
| if (MO.getTargetFlags() & AArch64II::MO_TLS) { | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGEOFF) | |
| RefFlags |= AArch64::S_SECREL_LO12; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_HI12) | |
| RefFlags |= AArch64::S_SECREL_HI12; | |
| } else if (MO.getTargetFlags() & AArch64II::MO_S) { | |
| RefFlags |= AArch64::S_SABS; | |
| } else { | |
| RefFlags |= AArch64::S_ABS; | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_PAGE) | |
| RefFlags |= AArch64::S_PAGE; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == | |
| AArch64II::MO_PAGEOFF) | |
| RefFlags |= AArch64::S_PAGEOFF | AArch64::S_NC; | |
| } | |
| if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G3) | |
| RefFlags |= AArch64::S_G3; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G2) | |
| RefFlags |= AArch64::S_G2; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G1) | |
| RefFlags |= AArch64::S_G1; | |
| else if ((MO.getTargetFlags() & AArch64II::MO_FRAGMENT) == AArch64II::MO_G0) | |
| RefFlags |= AArch64::S_G0; | |
| // FIXME: Currently we only set VK_NC for MO_G3/MO_G2/MO_G1/MO_G0. This is | |
| // because setting VK_NC for others would mean setting their respective | |
| // RefFlags correctly. We should do this in a separate patch. | |
| if (MO.getTargetFlags() & AArch64II::MO_NC) { | |
| auto MOFrag = (MO.getTargetFlags() & AArch64II::MO_FRAGMENT); | |
| if (MOFrag == AArch64II::MO_G3 || MOFrag == AArch64II::MO_G2 || | |
| MOFrag == AArch64II::MO_G1 || MOFrag == AArch64II::MO_G0) | |
| RefFlags |= AArch64::S_NC; | |
| } | |
| const MCExpr *Expr = MCSymbolRefExpr::create(Sym, Ctx); | |
| if (!MO.isJTI() && !MO.isMBB() && MO.getOffset()) | |
| Expr = MCBinaryExpr::createAdd( | |
| Expr, MCConstantExpr::create(MO.getOffset(), Ctx), Ctx); | |
| Expr = MCSpecifierExpr::create(Expr, RefFlags, Ctx); | |
| return MCOperand::createExpr(Expr); | |
| } | |
| MCOperand AArch64MCInstLower::LowerSymbolOperand(const MachineOperand &MO, | |
| MCSymbol *Sym) const { | |
| if (Printer.TM.getTargetTriple().isOSBinFormatMachO()) | |
| return lowerSymbolOperandMachO(MO, Sym); | |
| if (Printer.TM.getTargetTriple().isOSBinFormatCOFF()) | |
| return lowerSymbolOperandCOFF(MO, Sym); | |
| assert(Printer.TM.getTargetTriple().isOSBinFormatELF() && "Invalid target"); | |
| return lowerSymbolOperandELF(MO, Sym); | |
| } | |
| bool AArch64MCInstLower::lowerOperand(const MachineOperand &MO, | |
| MCOperand &MCOp) const { | |
| switch (MO.getType()) { | |
| default: | |
| llvm_unreachable("unknown operand type"); | |
| case MachineOperand::MO_Register: | |
| // Ignore all implicit register operands. | |
| if (MO.isImplicit()) | |
| return false; | |
| MCOp = MCOperand::createReg(MO.getReg()); | |
| break; | |
| case MachineOperand::MO_RegisterMask: | |
| // Regmasks are like implicit defs. | |
| return false; | |
| case MachineOperand::MO_Immediate: | |
| MCOp = MCOperand::createImm(MO.getImm()); | |
| break; | |
| case MachineOperand::MO_MachineBasicBlock: | |
| MCOp = LowerSymbolOperand(MO, MO.getMBB()->getSymbol()); | |
| break; | |
| case MachineOperand::MO_GlobalAddress: | |
| MCOp = LowerSymbolOperand(MO, GetGlobalAddressSymbol(MO)); | |
| break; | |
| case MachineOperand::MO_ExternalSymbol: | |
| MCOp = LowerSymbolOperand(MO, GetExternalSymbolSymbol(MO)); | |
| break; | |
| case MachineOperand::MO_MCSymbol: | |
| MCOp = LowerSymbolOperand(MO, MO.getMCSymbol()); | |
| break; | |
| case MachineOperand::MO_JumpTableIndex: | |
| MCOp = LowerSymbolOperand(MO, Printer.GetJTISymbol(MO.getIndex())); | |
| break; | |
| case MachineOperand::MO_ConstantPoolIndex: | |
| MCOp = LowerSymbolOperand(MO, Printer.GetCPISymbol(MO.getIndex())); | |
| break; | |
| case MachineOperand::MO_BlockAddress: | |
| MCOp = LowerSymbolOperand( | |
| MO, Printer.GetBlockAddressSymbol(MO.getBlockAddress())); | |
| break; | |
| } | |
| return true; | |
| } | |
| void AArch64MCInstLower::Lower(const MachineInstr *MI, MCInst &OutMI) const { | |
| OutMI.setOpcode(MI->getOpcode()); | |
| for (const MachineOperand &MO : MI->operands()) { | |
| MCOperand MCOp; | |
| if (lowerOperand(MO, MCOp)) | |
| OutMI.addOperand(MCOp); | |
| } | |
| switch (OutMI.getOpcode()) { | |
| case AArch64::CATCHRET: | |
| OutMI = MCInst(); | |
| OutMI.setOpcode(AArch64::RET); | |
| OutMI.addOperand(MCOperand::createReg(AArch64::LR)); | |
| break; | |
| case AArch64::CLEANUPRET: | |
| OutMI = MCInst(); | |
| OutMI.setOpcode(AArch64::RET); | |
| OutMI.addOperand(MCOperand::createReg(AArch64::LR)); | |
| break; | |
| } | |
| } |