1 //===- AArch64AsmPrinter.cpp - AArch64 LLVM assembly writer ---------------===//
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
7 //===----------------------------------------------------------------------===//
9 // This file contains a printer that converts from our internal representation
10 // of machine-dependent LLVM code to the AArch64 assembly language.
12 //===----------------------------------------------------------------------===//
15 #include "AArch64MCInstLower.h"
16 #include "AArch64MachineFunctionInfo.h"
17 #include "AArch64RegisterInfo.h"
18 #include "AArch64Subtarget.h"
19 #include "AArch64TargetObjectFile.h"
20 #include "MCTargetDesc/AArch64AddressingModes.h"
21 #include "MCTargetDesc/AArch64InstPrinter.h"
22 #include "MCTargetDesc/AArch64MCExpr.h"
23 #include "MCTargetDesc/AArch64MCTargetDesc.h"
24 #include "MCTargetDesc/AArch64TargetStreamer.h"
25 #include "TargetInfo/AArch64TargetInfo.h"
26 #include "Utils/AArch64BaseInfo.h"
27 #include "llvm/ADT/DenseMap.h"
28 #include "llvm/ADT/ScopeExit.h"
29 #include "llvm/ADT/SmallString.h"
30 #include "llvm/ADT/SmallVector.h"
31 #include "llvm/ADT/StringRef.h"
32 #include "llvm/ADT/Twine.h"
33 #include "llvm/BinaryFormat/COFF.h"
34 #include "llvm/BinaryFormat/ELF.h"
35 #include "llvm/BinaryFormat/MachO.h"
36 #include "llvm/CodeGen/AsmPrinter.h"
37 #include "llvm/CodeGen/FaultMaps.h"
38 #include "llvm/CodeGen/MachineBasicBlock.h"
39 #include "llvm/CodeGen/MachineFunction.h"
40 #include "llvm/CodeGen/MachineInstr.h"
41 #include "llvm/CodeGen/MachineJumpTableInfo.h"
42 #include "llvm/CodeGen/MachineModuleInfoImpls.h"
43 #include "llvm/CodeGen/MachineOperand.h"
44 #include "llvm/CodeGen/StackMaps.h"
45 #include "llvm/CodeGen/TargetRegisterInfo.h"
46 #include "llvm/IR/DataLayout.h"
47 #include "llvm/IR/DebugInfoMetadata.h"
48 #include "llvm/IR/Module.h"
49 #include "llvm/MC/MCAsmInfo.h"
50 #include "llvm/MC/MCContext.h"
51 #include "llvm/MC/MCInst.h"
52 #include "llvm/MC/MCInstBuilder.h"
53 #include "llvm/MC/MCSectionELF.h"
54 #include "llvm/MC/MCSectionMachO.h"
55 #include "llvm/MC/MCStreamer.h"
56 #include "llvm/MC/MCSymbol.h"
57 #include "llvm/MC/TargetRegistry.h"
58 #include "llvm/Support/Casting.h"
59 #include "llvm/Support/CommandLine.h"
60 #include "llvm/Support/ErrorHandling.h"
61 #include "llvm/Support/raw_ostream.h"
62 #include "llvm/Target/TargetMachine.h"
63 #include "llvm/TargetParser/Triple.h"
64 #include "llvm/Transforms/Instrumentation/HWAddressSanitizer.h"
72 enum PtrauthCheckMode
{ Default
, Unchecked
, Poison
, Trap
};
73 static cl::opt
<PtrauthCheckMode
> PtrauthAuthChecks(
74 "aarch64-ptrauth-auth-checks", cl::Hidden
,
75 cl::values(clEnumValN(Unchecked
, "none", "don't test for failure"),
76 clEnumValN(Poison
, "poison", "poison on failure"),
77 clEnumValN(Trap
, "trap", "trap on failure")),
78 cl::desc("Check pointer authentication auth/resign failures"),
81 static cl::opt
<bool> EnableImportCallOptimization(
82 "aarch64-win-import-call-optimization", cl::Hidden
,
83 cl::desc("Enable import call optimization for AArch64 Windows"),
86 #define DEBUG_TYPE "asm-printer"
90 class AArch64AsmPrinter
: public AsmPrinter
{
91 AArch64MCInstLower MCInstLowering
;
93 const AArch64Subtarget
*STI
;
94 bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags
= false;
96 unsigned InstsEmitted
;
98 DenseMap
<MCSection
*, std::vector
<std::pair
<MCSymbol
*, MCSymbol
*>>>
99 SectionToImportedFunctionCalls
;
102 AArch64AsmPrinter(TargetMachine
&TM
, std::unique_ptr
<MCStreamer
> Streamer
)
103 : AsmPrinter(TM
, std::move(Streamer
)), MCInstLowering(OutContext
, *this),
106 StringRef
getPassName() const override
{ return "AArch64 Assembly Printer"; }
108 /// Wrapper for MCInstLowering.lowerOperand() for the
109 /// tblgen'erated pseudo lowering.
110 bool lowerOperand(const MachineOperand
&MO
, MCOperand
&MCOp
) const {
111 return MCInstLowering
.lowerOperand(MO
, MCOp
);
114 const MCExpr
*lowerConstantPtrAuth(const ConstantPtrAuth
&CPA
) override
;
116 const MCExpr
*lowerBlockAddressConstant(const BlockAddress
&BA
) override
;
118 void emitStartOfAsmFile(Module
&M
) override
;
119 void emitJumpTableInfo() override
;
120 std::tuple
<const MCSymbol
*, uint64_t, const MCSymbol
*,
121 codeview::JumpTableEntrySize
>
122 getCodeViewJumpTableInfo(int JTI
, const MachineInstr
*BranchInstr
,
123 const MCSymbol
*BranchLabel
) const override
;
125 void emitFunctionEntryLabel() override
;
127 void emitXXStructor(const DataLayout
&DL
, const Constant
*CV
) override
;
129 void LowerJumpTableDest(MCStreamer
&OutStreamer
, const MachineInstr
&MI
);
131 void LowerHardenedBRJumpTable(const MachineInstr
&MI
);
133 void LowerMOPS(MCStreamer
&OutStreamer
, const MachineInstr
&MI
);
135 void LowerSTACKMAP(MCStreamer
&OutStreamer
, StackMaps
&SM
,
136 const MachineInstr
&MI
);
137 void LowerPATCHPOINT(MCStreamer
&OutStreamer
, StackMaps
&SM
,
138 const MachineInstr
&MI
);
139 void LowerSTATEPOINT(MCStreamer
&OutStreamer
, StackMaps
&SM
,
140 const MachineInstr
&MI
);
141 void LowerFAULTING_OP(const MachineInstr
&MI
);
143 void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr
&MI
);
144 void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr
&MI
);
145 void LowerPATCHABLE_TAIL_CALL(const MachineInstr
&MI
);
146 void LowerPATCHABLE_EVENT_CALL(const MachineInstr
&MI
, bool Typed
);
148 typedef std::tuple
<unsigned, bool, uint32_t, bool, uint64_t>
149 HwasanMemaccessTuple
;
150 std::map
<HwasanMemaccessTuple
, MCSymbol
*> HwasanMemaccessSymbols
;
151 void LowerKCFI_CHECK(const MachineInstr
&MI
);
152 void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr
&MI
);
153 void emitHwasanMemaccessSymbols(Module
&M
);
155 void emitSled(const MachineInstr
&MI
, SledKind Kind
);
157 // Emit the sequence for BRA/BLRA (authenticate + branch/call).
158 void emitPtrauthBranch(const MachineInstr
*MI
);
160 void emitPtrauthCheckAuthenticatedValue(Register TestedReg
,
162 AArch64PACKey::ID Key
,
163 AArch64PAuth::AuthCheckMethod Method
,
165 const MCSymbol
*OnFailure
);
167 // Check authenticated LR before tail calling.
168 void emitPtrauthTailCallHardening(const MachineInstr
*TC
);
170 // Emit the sequence for AUT or AUTPAC.
171 void emitPtrauthAuthResign(const MachineInstr
*MI
);
173 // Emit the sequence to compute the discriminator.
175 // ScratchReg should be x16/x17.
177 // The returned register is either unmodified AddrDisc or x16/x17.
179 // If the expanded pseudo is allowed to clobber AddrDisc register, setting
180 // MayUseAddrAsScratch may save one MOV instruction, provided the address
181 // is already in x16/x17 (i.e. return x16/x17 which is the *modified* AddrDisc
182 // register at the same time):
185 // movk x17, #1234, lsl #48
186 // ; x16 is not used anymore
188 // can be replaced by
190 // movk x16, #1234, lsl #48
191 Register
emitPtrauthDiscriminator(uint16_t Disc
, Register AddrDisc
,
193 bool MayUseAddrAsScratch
= false);
195 // Emit the sequence for LOADauthptrstatic
196 void LowerLOADauthptrstatic(const MachineInstr
&MI
);
198 // Emit the sequence for LOADgotPAC/MOVaddrPAC (either GOT adrp-ldr or
199 // adrp-add followed by PAC sign)
200 void LowerMOVaddrPAC(const MachineInstr
&MI
);
202 // Emit the sequence for LOADgotAUTH (load signed pointer from signed ELF GOT
203 // and authenticate it with, if FPAC bit is not set, check+trap sequence after
205 void LowerLOADgotAUTH(const MachineInstr
&MI
);
207 /// tblgen'erated driver function for lowering simple MI->MC
208 /// pseudo instructions.
209 bool lowerPseudoInstExpansion(const MachineInstr
*MI
, MCInst
&Inst
);
211 void EmitToStreamer(MCStreamer
&S
, const MCInst
&Inst
);
212 void EmitToStreamer(const MCInst
&Inst
) {
213 EmitToStreamer(*OutStreamer
, Inst
);
216 void emitInstruction(const MachineInstr
*MI
) override
;
218 void emitFunctionHeaderComment() override
;
220 void getAnalysisUsage(AnalysisUsage
&AU
) const override
{
221 AsmPrinter::getAnalysisUsage(AU
);
222 AU
.setPreservesAll();
225 bool runOnMachineFunction(MachineFunction
&MF
) override
{
226 AArch64FI
= MF
.getInfo
<AArch64FunctionInfo
>();
227 STI
= &MF
.getSubtarget
<AArch64Subtarget
>();
229 SetupMachineFunction(MF
);
231 if (STI
->isTargetCOFF()) {
232 bool Local
= MF
.getFunction().hasLocalLinkage();
233 COFF::SymbolStorageClass Scl
=
234 Local
? COFF::IMAGE_SYM_CLASS_STATIC
: COFF::IMAGE_SYM_CLASS_EXTERNAL
;
236 COFF::IMAGE_SYM_DTYPE_FUNCTION
<< COFF::SCT_COMPLEX_TYPE_SHIFT
;
238 OutStreamer
->beginCOFFSymbolDef(CurrentFnSym
);
239 OutStreamer
->emitCOFFSymbolStorageClass(Scl
);
240 OutStreamer
->emitCOFFSymbolType(Type
);
241 OutStreamer
->endCOFFSymbolDef();
244 // Emit the rest of the function body.
247 // Emit the XRay table for this function.
250 // We didn't modify anything.
254 const MCExpr
*lowerConstant(const Constant
*CV
) override
;
257 void printOperand(const MachineInstr
*MI
, unsigned OpNum
, raw_ostream
&O
);
258 bool printAsmMRegister(const MachineOperand
&MO
, char Mode
, raw_ostream
&O
);
259 bool printAsmRegInClass(const MachineOperand
&MO
,
260 const TargetRegisterClass
*RC
, unsigned AltName
,
263 bool PrintAsmOperand(const MachineInstr
*MI
, unsigned OpNum
,
264 const char *ExtraCode
, raw_ostream
&O
) override
;
265 bool PrintAsmMemoryOperand(const MachineInstr
*MI
, unsigned OpNum
,
266 const char *ExtraCode
, raw_ostream
&O
) override
;
268 void PrintDebugValueComment(const MachineInstr
*MI
, raw_ostream
&OS
);
270 void emitFunctionBodyEnd() override
;
271 void emitGlobalAlias(const Module
&M
, const GlobalAlias
&GA
) override
;
273 MCSymbol
*GetCPISymbol(unsigned CPID
) const override
;
274 void emitEndOfAsmFile(Module
&M
) override
;
276 AArch64FunctionInfo
*AArch64FI
= nullptr;
278 /// Emit the LOHs contained in AArch64FI.
281 void emitMovXReg(Register Dest
, Register Src
);
282 void emitMOVZ(Register Dest
, uint64_t Imm
, unsigned Shift
);
283 void emitMOVK(Register Dest
, uint64_t Imm
, unsigned Shift
);
285 /// Emit instruction to set float register to zero.
286 void emitFMov0(const MachineInstr
&MI
);
288 using MInstToMCSymbol
= std::map
<const MachineInstr
*, MCSymbol
*>;
290 MInstToMCSymbol LOHInstToLabel
;
292 bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const override
{
293 return ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags
;
296 const MCSubtargetInfo
*getIFuncMCSubtargetInfo() const override
{
300 void emitMachOIFuncStubBody(Module
&M
, const GlobalIFunc
&GI
,
301 MCSymbol
*LazyPointer
) override
;
302 void emitMachOIFuncStubHelperBody(Module
&M
, const GlobalIFunc
&GI
,
303 MCSymbol
*LazyPointer
) override
;
305 /// Checks if this instruction is part of a sequence that is eligle for import
306 /// call optimization and, if so, records it to be emitted in the import call
308 void recordIfImportCall(const MachineInstr
*BranchInst
);
311 } // end anonymous namespace
313 void AArch64AsmPrinter::emitStartOfAsmFile(Module
&M
) {
314 const Triple
&TT
= TM
.getTargetTriple();
316 if (TT
.isOSBinFormatCOFF()) {
317 // Emit an absolute @feat.00 symbol
318 MCSymbol
*S
= MMI
->getContext().getOrCreateSymbol(StringRef("@feat.00"));
319 OutStreamer
->beginCOFFSymbolDef(S
);
320 OutStreamer
->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_STATIC
);
321 OutStreamer
->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_NULL
);
322 OutStreamer
->endCOFFSymbolDef();
323 int64_t Feat00Value
= 0;
325 if (M
.getModuleFlag("cfguard")) {
326 // Object is CFG-aware.
327 Feat00Value
|= COFF::Feat00Flags::GuardCF
;
330 if (M
.getModuleFlag("ehcontguard")) {
331 // Object also has EHCont.
332 Feat00Value
|= COFF::Feat00Flags::GuardEHCont
;
335 if (M
.getModuleFlag("ms-kernel")) {
336 // Object is compiled with /kernel.
337 Feat00Value
|= COFF::Feat00Flags::Kernel
;
340 OutStreamer
->emitSymbolAttribute(S
, MCSA_Global
);
341 OutStreamer
->emitAssignment(
342 S
, MCConstantExpr::create(Feat00Value
, MMI
->getContext()));
345 if (!TT
.isOSBinFormatELF())
348 // Assemble feature flags that may require creation of a note section.
350 if (const auto *BTE
= mdconst::extract_or_null
<ConstantInt
>(
351 M
.getModuleFlag("branch-target-enforcement")))
353 Flags
|= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_BTI
;
355 if (const auto *GCS
= mdconst::extract_or_null
<ConstantInt
>(
356 M
.getModuleFlag("guarded-control-stack")))
358 Flags
|= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_GCS
;
360 if (const auto *Sign
= mdconst::extract_or_null
<ConstantInt
>(
361 M
.getModuleFlag("sign-return-address")))
363 Flags
|= ELF::GNU_PROPERTY_AARCH64_FEATURE_1_PAC
;
365 uint64_t PAuthABIPlatform
= -1;
366 if (const auto *PAP
= mdconst::extract_or_null
<ConstantInt
>(
367 M
.getModuleFlag("aarch64-elf-pauthabi-platform")))
368 PAuthABIPlatform
= PAP
->getZExtValue();
369 uint64_t PAuthABIVersion
= -1;
370 if (const auto *PAV
= mdconst::extract_or_null
<ConstantInt
>(
371 M
.getModuleFlag("aarch64-elf-pauthabi-version")))
372 PAuthABIVersion
= PAV
->getZExtValue();
374 // Emit a .note.gnu.property section with the flags.
376 static_cast<AArch64TargetStreamer
*>(OutStreamer
->getTargetStreamer());
377 TS
->emitNoteSection(Flags
, PAuthABIPlatform
, PAuthABIVersion
);
380 void AArch64AsmPrinter::emitFunctionHeaderComment() {
381 const AArch64FunctionInfo
*FI
= MF
->getInfo
<AArch64FunctionInfo
>();
382 std::optional
<std::string
> OutlinerString
= FI
->getOutliningStyle();
383 if (OutlinerString
!= std::nullopt
)
384 OutStreamer
->getCommentOS() << ' ' << OutlinerString
;
387 void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr
&MI
)
389 const Function
&F
= MF
->getFunction();
390 if (F
.hasFnAttribute("patchable-function-entry")) {
392 if (F
.getFnAttribute("patchable-function-entry")
394 .getAsInteger(10, Num
))
400 emitSled(MI
, SledKind::FUNCTION_ENTER
);
403 void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr
&MI
) {
404 emitSled(MI
, SledKind::FUNCTION_EXIT
);
407 void AArch64AsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr
&MI
) {
408 emitSled(MI
, SledKind::TAIL_CALL
);
411 void AArch64AsmPrinter::emitSled(const MachineInstr
&MI
, SledKind Kind
) {
412 static const int8_t NoopsInSledCount
= 7;
413 // We want to emit the following pattern:
418 // ; 7 NOP instructions (28 bytes)
421 // We need the 28 bytes (7 instructions) because at runtime, we'd be patching
422 // over the full 32 bytes (8 instructions) with the following pattern:
424 // STP X0, X30, [SP, #-16]! ; push X0 and the link register to the stack
425 // LDR W17, #12 ; W17 := function ID
426 // LDR X16,#12 ; X16 := addr of __xray_FunctionEntry or __xray_FunctionExit
427 // BLR X16 ; call the tracing trampoline
428 // ;DATA: 32 bits of function ID
429 // ;DATA: lower 32 bits of the address of the trampoline
430 // ;DATA: higher 32 bits of the address of the trampoline
431 // LDP X0, X30, [SP], #16 ; pop X0 and the link register from the stack
433 OutStreamer
->emitCodeAlignment(Align(4), &getSubtargetInfo());
434 auto CurSled
= OutContext
.createTempSymbol("xray_sled_", true);
435 OutStreamer
->emitLabel(CurSled
);
436 auto Target
= OutContext
.createTempSymbol();
438 // Emit "B #32" instruction, which jumps over the next 28 bytes.
439 // The operand has to be the number of 4-byte instructions to jump over,
440 // including the current instruction.
441 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::B
).addImm(8));
443 for (int8_t I
= 0; I
< NoopsInSledCount
; I
++)
444 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::HINT
).addImm(0));
446 OutStreamer
->emitLabel(Target
);
447 recordSled(CurSled
, MI
, Kind
, 2);
450 // Emit the following code for Intrinsic::{xray_customevent,xray_typedevent}
451 // (built-in functions __xray_customevent/__xray_typedevent).
453 // .Lxray_event_sled_N:
455 // save x0 and x1 (and also x2 for TYPED_EVENT_CALL)
456 // set up x0 and x1 (and also x2 for TYPED_EVENT_CALL)
457 // bl __xray_CustomEvent or __xray_TypedEvent
458 // restore x0 and x1 (and also x2 for TYPED_EVENT_CALL)
461 // There are 6 instructions for EVENT_CALL and 9 for TYPED_EVENT_CALL.
463 // Then record a sled of kind CUSTOM_EVENT or TYPED_EVENT.
464 // After patching, b .+N will become a nop.
465 void AArch64AsmPrinter::LowerPATCHABLE_EVENT_CALL(const MachineInstr
&MI
,
467 auto &O
= *OutStreamer
;
468 MCSymbol
*CurSled
= OutContext
.createTempSymbol("xray_sled_", true);
469 O
.emitLabel(CurSled
);
470 bool MachO
= TM
.getTargetTriple().isOSBinFormatMachO();
471 auto *Sym
= MCSymbolRefExpr::create(
472 OutContext
.getOrCreateSymbol(
473 Twine(MachO
? "_" : "") +
474 (Typed
? "__xray_TypedEvent" : "__xray_CustomEvent")),
477 O
.AddComment("Begin XRay typed event");
478 EmitToStreamer(O
, MCInstBuilder(AArch64::B
).addImm(9));
479 EmitToStreamer(O
, MCInstBuilder(AArch64::STPXpre
)
485 EmitToStreamer(O
, MCInstBuilder(AArch64::STRXui
)
489 emitMovXReg(AArch64::X0
, MI
.getOperand(0).getReg());
490 emitMovXReg(AArch64::X1
, MI
.getOperand(1).getReg());
491 emitMovXReg(AArch64::X2
, MI
.getOperand(2).getReg());
492 EmitToStreamer(O
, MCInstBuilder(AArch64::BL
).addExpr(Sym
));
493 EmitToStreamer(O
, MCInstBuilder(AArch64::LDRXui
)
497 O
.AddComment("End XRay typed event");
498 EmitToStreamer(O
, MCInstBuilder(AArch64::LDPXpost
)
505 recordSled(CurSled
, MI
, SledKind::TYPED_EVENT
, 2);
507 O
.AddComment("Begin XRay custom event");
508 EmitToStreamer(O
, MCInstBuilder(AArch64::B
).addImm(6));
509 EmitToStreamer(O
, MCInstBuilder(AArch64::STPXpre
)
515 emitMovXReg(AArch64::X0
, MI
.getOperand(0).getReg());
516 emitMovXReg(AArch64::X1
, MI
.getOperand(1).getReg());
517 EmitToStreamer(O
, MCInstBuilder(AArch64::BL
).addExpr(Sym
));
518 O
.AddComment("End XRay custom event");
519 EmitToStreamer(O
, MCInstBuilder(AArch64::LDPXpost
)
526 recordSled(CurSled
, MI
, SledKind::CUSTOM_EVENT
, 2);
530 void AArch64AsmPrinter::LowerKCFI_CHECK(const MachineInstr
&MI
) {
531 Register AddrReg
= MI
.getOperand(0).getReg();
532 assert(std::next(MI
.getIterator())->isCall() &&
533 "KCFI_CHECK not followed by a call instruction");
534 assert(std::next(MI
.getIterator())->getOperand(0).getReg() == AddrReg
&&
535 "KCFI_CHECK call target doesn't match call operand");
537 // Default to using the intra-procedure-call temporary registers for
538 // comparing the hashes.
539 unsigned ScratchRegs
[] = {AArch64::W16
, AArch64::W17
};
540 if (AddrReg
== AArch64::XZR
) {
541 // Checking XZR makes no sense. Instead of emitting a load, zero
542 // ScratchRegs[0] and use it for the ESR AddrIndex below.
543 AddrReg
= getXRegFromWReg(ScratchRegs
[0]);
544 emitMovXReg(AddrReg
, AArch64::XZR
);
546 // If one of the scratch registers is used for the call target (e.g.
547 // with AArch64::TCRETURNriBTI), we can clobber another caller-saved
548 // temporary register instead (in this case, AArch64::W9) as the check
549 // is immediately followed by the call instruction.
550 for (auto &Reg
: ScratchRegs
) {
551 if (Reg
== getWRegFromXReg(AddrReg
)) {
556 assert(ScratchRegs
[0] != AddrReg
&& ScratchRegs
[1] != AddrReg
&&
557 "Invalid scratch registers for KCFI_CHECK");
559 // Adjust the offset for patchable-function-prefix. This assumes that
560 // patchable-function-prefix is the same for all functions.
561 int64_t PrefixNops
= 0;
564 .getFnAttribute("patchable-function-prefix")
566 .getAsInteger(10, PrefixNops
);
568 // Load the target function type hash.
569 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::LDURWi
)
570 .addReg(ScratchRegs
[0])
572 .addImm(-(PrefixNops
* 4 + 4)));
575 // Load the expected type hash.
576 const int64_t Type
= MI
.getOperand(1).getImm();
577 emitMOVK(ScratchRegs
[1], Type
& 0xFFFF, 0);
578 emitMOVK(ScratchRegs
[1], (Type
>> 16) & 0xFFFF, 16);
580 // Compare the hashes and trap if there's a mismatch.
581 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::SUBSWrs
)
582 .addReg(AArch64::WZR
)
583 .addReg(ScratchRegs
[0])
584 .addReg(ScratchRegs
[1])
587 MCSymbol
*Pass
= OutContext
.createTempSymbol();
588 EmitToStreamer(*OutStreamer
,
589 MCInstBuilder(AArch64::Bcc
)
590 .addImm(AArch64CC::EQ
)
591 .addExpr(MCSymbolRefExpr::create(Pass
, OutContext
)));
593 // The base ESR is 0x8000 and the register information is encoded in bits
595 // - 0-4: n, where the register Xn contains the target address
596 // - 5-9: m, where the register Wm contains the expected type hash
597 // Where n, m are in [0, 30].
598 unsigned TypeIndex
= ScratchRegs
[1] - AArch64::W0
;
602 AddrIndex
= AddrReg
- AArch64::X0
;
612 assert(AddrIndex
< 31 && TypeIndex
< 31);
614 unsigned ESR
= 0x8000 | ((TypeIndex
& 31) << 5) | (AddrIndex
& 31);
615 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::BRK
).addImm(ESR
));
616 OutStreamer
->emitLabel(Pass
);
619 void AArch64AsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr
&MI
) {
620 Register Reg
= MI
.getOperand(0).getReg();
622 // The HWASan pass won't emit a CHECK_MEMACCESS intrinsic with a pointer
623 // statically known to be zero. However, conceivably, the HWASan pass may
624 // encounter a "cannot currently statically prove to be null" pointer (and is
625 // therefore unable to omit the intrinsic) that later optimization passes
626 // convert into a statically known-null pointer.
627 if (Reg
== AArch64::XZR
)
631 ((MI
.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES
) ||
633 AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW
));
634 uint32_t AccessInfo
= MI
.getOperand(1).getImm();
636 ((MI
.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_FIXEDSHADOW
) ||
638 AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW
));
639 uint64_t FixedShadowOffset
= IsFixedShadow
? MI
.getOperand(2).getImm() : 0;
641 MCSymbol
*&Sym
= HwasanMemaccessSymbols
[HwasanMemaccessTuple(
642 Reg
, IsShort
, AccessInfo
, IsFixedShadow
, FixedShadowOffset
)];
644 // FIXME: Make this work on non-ELF.
645 if (!TM
.getTargetTriple().isOSBinFormatELF())
646 report_fatal_error("llvm.hwasan.check.memaccess only supported on ELF");
648 std::string SymName
= "__hwasan_check_x" + utostr(Reg
- AArch64::X0
) + "_" +
651 SymName
+= "_fixed_" + utostr(FixedShadowOffset
);
653 SymName
+= "_short_v2";
654 Sym
= OutContext
.getOrCreateSymbol(SymName
);
657 EmitToStreamer(*OutStreamer
,
658 MCInstBuilder(AArch64::BL
)
659 .addExpr(MCSymbolRefExpr::create(Sym
, OutContext
)));
662 void AArch64AsmPrinter::emitHwasanMemaccessSymbols(Module
&M
) {
663 if (HwasanMemaccessSymbols
.empty())
666 const Triple
&TT
= TM
.getTargetTriple();
667 assert(TT
.isOSBinFormatELF());
668 std::unique_ptr
<MCSubtargetInfo
> STI(
669 TM
.getTarget().createMCSubtargetInfo(TT
.str(), "", ""));
670 assert(STI
&& "Unable to create subtarget info");
671 this->STI
= static_cast<const AArch64Subtarget
*>(&*STI
);
673 MCSymbol
*HwasanTagMismatchV1Sym
=
674 OutContext
.getOrCreateSymbol("__hwasan_tag_mismatch");
675 MCSymbol
*HwasanTagMismatchV2Sym
=
676 OutContext
.getOrCreateSymbol("__hwasan_tag_mismatch_v2");
678 const MCSymbolRefExpr
*HwasanTagMismatchV1Ref
=
679 MCSymbolRefExpr::create(HwasanTagMismatchV1Sym
, OutContext
);
680 const MCSymbolRefExpr
*HwasanTagMismatchV2Ref
=
681 MCSymbolRefExpr::create(HwasanTagMismatchV2Sym
, OutContext
);
683 for (auto &P
: HwasanMemaccessSymbols
) {
684 unsigned Reg
= std::get
<0>(P
.first
);
685 bool IsShort
= std::get
<1>(P
.first
);
686 uint32_t AccessInfo
= std::get
<2>(P
.first
);
687 bool IsFixedShadow
= std::get
<3>(P
.first
);
688 uint64_t FixedShadowOffset
= std::get
<4>(P
.first
);
689 const MCSymbolRefExpr
*HwasanTagMismatchRef
=
690 IsShort
? HwasanTagMismatchV2Ref
: HwasanTagMismatchV1Ref
;
691 MCSymbol
*Sym
= P
.second
;
693 bool HasMatchAllTag
=
694 (AccessInfo
>> HWASanAccessInfo::HasMatchAllShift
) & 1;
695 uint8_t MatchAllTag
=
696 (AccessInfo
>> HWASanAccessInfo::MatchAllShift
) & 0xff;
698 1 << ((AccessInfo
>> HWASanAccessInfo::AccessSizeShift
) & 0xf);
700 (AccessInfo
>> HWASanAccessInfo::CompileKernelShift
) & 1;
702 OutStreamer
->switchSection(OutContext
.getELFSection(
703 ".text.hot", ELF::SHT_PROGBITS
,
704 ELF::SHF_EXECINSTR
| ELF::SHF_ALLOC
| ELF::SHF_GROUP
, 0, Sym
->getName(),
707 OutStreamer
->emitSymbolAttribute(Sym
, MCSA_ELF_TypeFunction
);
708 OutStreamer
->emitSymbolAttribute(Sym
, MCSA_Weak
);
709 OutStreamer
->emitSymbolAttribute(Sym
, MCSA_Hidden
);
710 OutStreamer
->emitLabel(Sym
);
712 EmitToStreamer(MCInstBuilder(AArch64::SBFMXri
)
713 .addReg(AArch64::X16
)
719 // Aarch64 makes it difficult to embed large constants in the code.
720 // Fortuitously, kShadowBaseAlignment == 32, so we use the 32-bit
721 // left-shift option in the MOV instruction. Combined with the 16-bit
722 // immediate, this is enough to represent any offset up to 2**48.
723 emitMOVZ(AArch64::X17
, FixedShadowOffset
>> 32, 32);
724 EmitToStreamer(MCInstBuilder(AArch64::LDRBBroX
)
725 .addReg(AArch64::W16
)
726 .addReg(AArch64::X17
)
727 .addReg(AArch64::X16
)
731 EmitToStreamer(MCInstBuilder(AArch64::LDRBBroX
)
732 .addReg(AArch64::W16
)
733 .addReg(IsShort
? AArch64::X20
: AArch64::X9
)
734 .addReg(AArch64::X16
)
739 EmitToStreamer(MCInstBuilder(AArch64::SUBSXrs
)
740 .addReg(AArch64::XZR
)
741 .addReg(AArch64::X16
)
743 .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR
, 56)));
744 MCSymbol
*HandleMismatchOrPartialSym
= OutContext
.createTempSymbol();
745 EmitToStreamer(MCInstBuilder(AArch64::Bcc
)
746 .addImm(AArch64CC::NE
)
747 .addExpr(MCSymbolRefExpr::create(
748 HandleMismatchOrPartialSym
, OutContext
)));
749 MCSymbol
*ReturnSym
= OutContext
.createTempSymbol();
750 OutStreamer
->emitLabel(ReturnSym
);
751 EmitToStreamer(MCInstBuilder(AArch64::RET
).addReg(AArch64::LR
));
752 OutStreamer
->emitLabel(HandleMismatchOrPartialSym
);
754 if (HasMatchAllTag
) {
755 EmitToStreamer(MCInstBuilder(AArch64::UBFMXri
)
756 .addReg(AArch64::X17
)
760 EmitToStreamer(MCInstBuilder(AArch64::SUBSXri
)
761 .addReg(AArch64::XZR
)
762 .addReg(AArch64::X17
)
766 MCInstBuilder(AArch64::Bcc
)
767 .addImm(AArch64CC::EQ
)
768 .addExpr(MCSymbolRefExpr::create(ReturnSym
, OutContext
)));
772 EmitToStreamer(MCInstBuilder(AArch64::SUBSWri
)
773 .addReg(AArch64::WZR
)
774 .addReg(AArch64::W16
)
777 MCSymbol
*HandleMismatchSym
= OutContext
.createTempSymbol();
779 MCInstBuilder(AArch64::Bcc
)
780 .addImm(AArch64CC::HI
)
781 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym
, OutContext
)));
783 EmitToStreamer(MCInstBuilder(AArch64::ANDXri
)
784 .addReg(AArch64::X17
)
786 .addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)));
788 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
789 .addReg(AArch64::X17
)
790 .addReg(AArch64::X17
)
793 EmitToStreamer(MCInstBuilder(AArch64::SUBSWrs
)
794 .addReg(AArch64::WZR
)
795 .addReg(AArch64::W16
)
796 .addReg(AArch64::W17
)
799 MCInstBuilder(AArch64::Bcc
)
800 .addImm(AArch64CC::LS
)
801 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym
, OutContext
)));
803 EmitToStreamer(MCInstBuilder(AArch64::ORRXri
)
804 .addReg(AArch64::X16
)
806 .addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)));
807 EmitToStreamer(MCInstBuilder(AArch64::LDRBBui
)
808 .addReg(AArch64::W16
)
809 .addReg(AArch64::X16
)
812 MCInstBuilder(AArch64::SUBSXrs
)
813 .addReg(AArch64::XZR
)
814 .addReg(AArch64::X16
)
816 .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSR
, 56)));
818 MCInstBuilder(AArch64::Bcc
)
819 .addImm(AArch64CC::EQ
)
820 .addExpr(MCSymbolRefExpr::create(ReturnSym
, OutContext
)));
822 OutStreamer
->emitLabel(HandleMismatchSym
);
825 EmitToStreamer(MCInstBuilder(AArch64::STPXpre
)
831 EmitToStreamer(MCInstBuilder(AArch64::STPXi
)
837 if (Reg
!= AArch64::X0
)
838 emitMovXReg(AArch64::X0
, Reg
);
839 emitMOVZ(AArch64::X1
, AccessInfo
& HWASanAccessInfo::RuntimeMask
, 0);
842 // The Linux kernel's dynamic loader doesn't support GOT relative
843 // relocations, but it doesn't support late binding either, so just call
844 // the function directly.
845 EmitToStreamer(MCInstBuilder(AArch64::B
).addExpr(HwasanTagMismatchRef
));
847 // Intentionally load the GOT entry and branch to it, rather than possibly
848 // late binding the function, which may clobber the registers before we
849 // have a chance to save them.
851 MCInstBuilder(AArch64::ADRP
)
852 .addReg(AArch64::X16
)
853 .addExpr(AArch64MCExpr::create(
854 HwasanTagMismatchRef
, AArch64MCExpr::VariantKind::VK_GOT_PAGE
,
857 MCInstBuilder(AArch64::LDRXui
)
858 .addReg(AArch64::X16
)
859 .addReg(AArch64::X16
)
860 .addExpr(AArch64MCExpr::create(
861 HwasanTagMismatchRef
, AArch64MCExpr::VariantKind::VK_GOT_LO12
,
863 EmitToStreamer(MCInstBuilder(AArch64::BR
).addReg(AArch64::X16
));
869 static void emitAuthenticatedPointer(MCStreamer
&OutStreamer
,
871 const MCExpr
*StubAuthPtrRef
) {
872 // sym$auth_ptr$key$disc:
873 OutStreamer
.emitLabel(StubLabel
);
874 OutStreamer
.emitValue(StubAuthPtrRef
, /*size=*/8);
877 void AArch64AsmPrinter::emitEndOfAsmFile(Module
&M
) {
878 emitHwasanMemaccessSymbols(M
);
880 const Triple
&TT
= TM
.getTargetTriple();
881 if (TT
.isOSBinFormatMachO()) {
882 // Output authenticated pointers as indirect symbols, if we have any.
883 MachineModuleInfoMachO
&MMIMacho
=
884 MMI
->getObjFileInfo
<MachineModuleInfoMachO
>();
886 auto Stubs
= MMIMacho
.getAuthGVStubList();
888 if (!Stubs
.empty()) {
889 // Switch to the "__auth_ptr" section.
890 OutStreamer
->switchSection(
891 OutContext
.getMachOSection("__DATA", "__auth_ptr", MachO::S_REGULAR
,
892 SectionKind::getMetadata()));
893 emitAlignment(Align(8));
895 for (const auto &Stub
: Stubs
)
896 emitAuthenticatedPointer(*OutStreamer
, Stub
.first
, Stub
.second
);
898 OutStreamer
->addBlankLine();
901 // Funny Darwin hack: This flag tells the linker that no global symbols
902 // contain code that falls through to other global symbols (e.g. the obvious
903 // implementation of multiple entry points). If this doesn't occur, the
904 // linker can safely perform dead code stripping. Since LLVM never
905 // generates code that does this, it is always safe to set.
906 OutStreamer
->emitAssemblerFlag(MCAF_SubsectionsViaSymbols
);
909 if (TT
.isOSBinFormatELF()) {
910 // Output authenticated pointers as indirect symbols, if we have any.
911 MachineModuleInfoELF
&MMIELF
= MMI
->getObjFileInfo
<MachineModuleInfoELF
>();
913 auto Stubs
= MMIELF
.getAuthGVStubList();
915 if (!Stubs
.empty()) {
916 const TargetLoweringObjectFile
&TLOF
= getObjFileLowering();
917 OutStreamer
->switchSection(TLOF
.getDataSection());
918 emitAlignment(Align(8));
920 for (const auto &Stub
: Stubs
)
921 emitAuthenticatedPointer(*OutStreamer
, Stub
.first
, Stub
.second
);
923 OutStreamer
->addBlankLine();
926 // With signed ELF GOT enabled, the linker looks at the symbol type to
927 // choose between keys IA (for STT_FUNC) and DA (for other types). Symbols
928 // for functions not defined in the module have STT_NOTYPE type by default.
929 // This makes linker to emit signing schema with DA key (instead of IA) for
930 // corresponding R_AARCH64_AUTH_GLOB_DAT dynamic reloc. To avoid that, force
931 // all function symbols used in the module to have STT_FUNC type. See
932 // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#default-signing-schema
933 const auto *PtrAuthELFGOTFlag
= mdconst::extract_or_null
<ConstantInt
>(
934 M
.getModuleFlag("ptrauth-elf-got"));
935 if (PtrAuthELFGOTFlag
&& PtrAuthELFGOTFlag
->getZExtValue() == 1)
936 for (const GlobalValue
&GV
: M
.global_values())
937 if (!GV
.use_empty() && isa
<Function
>(GV
) &&
938 !GV
.getName().starts_with("llvm."))
939 OutStreamer
->emitSymbolAttribute(getSymbol(&GV
),
940 MCSA_ELF_TypeFunction
);
943 // Emit stack and fault map information.
944 FM
.serializeToFaultMapSection();
946 // If import call optimization is enabled, emit the appropriate section.
947 // We do this whether or not we recorded any import calls.
948 if (EnableImportCallOptimization
&& TT
.isOSBinFormatCOFF()) {
949 OutStreamer
->switchSection(getObjFileLowering().getImportCallSection());
951 // Section always starts with some magic.
952 constexpr char ImpCallMagic
[12] = "Imp_Call_V1";
953 OutStreamer
->emitBytes(StringRef
{ImpCallMagic
, sizeof(ImpCallMagic
)});
955 // Layout of this section is:
956 // Per section that contains calls to imported functions:
957 // uint32_t SectionSize: Size in bytes for information in this section.
958 // uint32_t Section Number
959 // Per call to imported function in section:
960 // uint32_t Kind: the kind of imported function.
961 // uint32_t BranchOffset: the offset of the branch instruction in its
963 // uint32_t TargetSymbolId: the symbol id of the called function.
964 for (auto &[Section
, CallsToImportedFuncs
] :
965 SectionToImportedFunctionCalls
) {
966 unsigned SectionSize
=
967 sizeof(uint32_t) * (2 + 3 * CallsToImportedFuncs
.size());
968 OutStreamer
->emitInt32(SectionSize
);
969 OutStreamer
->emitCOFFSecNumber(Section
->getBeginSymbol());
970 for (auto &[CallsiteSymbol
, CalledSymbol
] : CallsToImportedFuncs
) {
971 // Kind is always IMAGE_REL_ARM64_DYNAMIC_IMPORT_CALL (0x13).
972 OutStreamer
->emitInt32(0x13);
973 OutStreamer
->emitCOFFSecOffset(CallsiteSymbol
);
974 OutStreamer
->emitCOFFSymbolIndex(CalledSymbol
);
980 void AArch64AsmPrinter::emitLOHs() {
981 SmallVector
<MCSymbol
*, 3> MCArgs
;
983 for (const auto &D
: AArch64FI
->getLOHContainer()) {
984 for (const MachineInstr
*MI
: D
.getArgs()) {
985 MInstToMCSymbol::iterator LabelIt
= LOHInstToLabel
.find(MI
);
986 assert(LabelIt
!= LOHInstToLabel
.end() &&
987 "Label hasn't been inserted for LOH related instruction");
988 MCArgs
.push_back(LabelIt
->second
);
990 OutStreamer
->emitLOHDirective(D
.getKind(), MCArgs
);
995 void AArch64AsmPrinter::emitFunctionBodyEnd() {
996 if (!AArch64FI
->getLOHRelated().empty())
1000 /// GetCPISymbol - Return the symbol for the specified constant pool entry.
1001 MCSymbol
*AArch64AsmPrinter::GetCPISymbol(unsigned CPID
) const {
1002 // Darwin uses a linker-private symbol name for constant-pools (to
1003 // avoid addends on the relocation?), ELF has no such concept and
1004 // uses a normal private symbol.
1005 if (!getDataLayout().getLinkerPrivateGlobalPrefix().empty())
1006 return OutContext
.getOrCreateSymbol(
1007 Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
1008 Twine(getFunctionNumber()) + "_" + Twine(CPID
));
1010 return AsmPrinter::GetCPISymbol(CPID
);
1013 void AArch64AsmPrinter::printOperand(const MachineInstr
*MI
, unsigned OpNum
,
1015 const MachineOperand
&MO
= MI
->getOperand(OpNum
);
1016 switch (MO
.getType()) {
1018 llvm_unreachable("<unknown operand type>");
1019 case MachineOperand::MO_Register
: {
1020 Register Reg
= MO
.getReg();
1021 assert(Reg
.isPhysical());
1022 assert(!MO
.getSubReg() && "Subregs should be eliminated!");
1023 O
<< AArch64InstPrinter::getRegisterName(Reg
);
1026 case MachineOperand::MO_Immediate
: {
1030 case MachineOperand::MO_GlobalAddress
: {
1031 PrintSymbolOperand(MO
, O
);
1034 case MachineOperand::MO_BlockAddress
: {
1035 MCSymbol
*Sym
= GetBlockAddressSymbol(MO
.getBlockAddress());
1042 bool AArch64AsmPrinter::printAsmMRegister(const MachineOperand
&MO
, char Mode
,
1044 Register Reg
= MO
.getReg();
1047 return true; // Unknown mode.
1049 Reg
= getWRegFromXReg(Reg
);
1052 Reg
= getXRegFromWReg(Reg
);
1055 Reg
= getXRegFromXRegTuple(Reg
);
1059 O
<< AArch64InstPrinter::getRegisterName(Reg
);
1063 // Prints the register in MO using class RC using the offset in the
1064 // new register class. This should not be used for cross class
1066 bool AArch64AsmPrinter::printAsmRegInClass(const MachineOperand
&MO
,
1067 const TargetRegisterClass
*RC
,
1068 unsigned AltName
, raw_ostream
&O
) {
1069 assert(MO
.isReg() && "Should only get here with a register!");
1070 const TargetRegisterInfo
*RI
= STI
->getRegisterInfo();
1071 Register Reg
= MO
.getReg();
1072 unsigned RegToPrint
= RC
->getRegister(RI
->getEncodingValue(Reg
));
1073 if (!RI
->regsOverlap(RegToPrint
, Reg
))
1075 O
<< AArch64InstPrinter::getRegisterName(RegToPrint
, AltName
);
1079 bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr
*MI
, unsigned OpNum
,
1080 const char *ExtraCode
, raw_ostream
&O
) {
1081 const MachineOperand
&MO
= MI
->getOperand(OpNum
);
1083 // First try the generic code, which knows about modifiers like 'c' and 'n'.
1084 if (!AsmPrinter::PrintAsmOperand(MI
, OpNum
, ExtraCode
, O
))
1087 // Does this asm operand have a single letter operand modifier?
1088 if (ExtraCode
&& ExtraCode
[0]) {
1089 if (ExtraCode
[1] != 0)
1090 return true; // Unknown modifier.
1092 switch (ExtraCode
[0]) {
1094 return true; // Unknown modifier.
1095 case 'w': // Print W register
1096 case 'x': // Print X register
1098 return printAsmMRegister(MO
, ExtraCode
[0], O
);
1099 if (MO
.isImm() && MO
.getImm() == 0) {
1100 unsigned Reg
= ExtraCode
[0] == 'w' ? AArch64::WZR
: AArch64::XZR
;
1101 O
<< AArch64InstPrinter::getRegisterName(Reg
);
1104 printOperand(MI
, OpNum
, O
);
1106 case 'b': // Print B register.
1107 case 'h': // Print H register.
1108 case 's': // Print S register.
1109 case 'd': // Print D register.
1110 case 'q': // Print Q register.
1111 case 'z': // Print Z register.
1113 const TargetRegisterClass
*RC
;
1114 switch (ExtraCode
[0]) {
1116 RC
= &AArch64::FPR8RegClass
;
1119 RC
= &AArch64::FPR16RegClass
;
1122 RC
= &AArch64::FPR32RegClass
;
1125 RC
= &AArch64::FPR64RegClass
;
1128 RC
= &AArch64::FPR128RegClass
;
1131 RC
= &AArch64::ZPRRegClass
;
1136 return printAsmRegInClass(MO
, RC
, AArch64::NoRegAltName
, O
);
1138 printOperand(MI
, OpNum
, O
);
1143 // According to ARM, we should emit x and v registers unless we have a
1146 Register Reg
= MO
.getReg();
1148 // If this is a w or x register, print an x register.
1149 if (AArch64::GPR32allRegClass
.contains(Reg
) ||
1150 AArch64::GPR64allRegClass
.contains(Reg
))
1151 return printAsmMRegister(MO
, 'x', O
);
1153 // If this is an x register tuple, print an x register.
1154 if (AArch64::GPR64x8ClassRegClass
.contains(Reg
))
1155 return printAsmMRegister(MO
, 't', O
);
1157 unsigned AltName
= AArch64::NoRegAltName
;
1158 const TargetRegisterClass
*RegClass
;
1159 if (AArch64::ZPRRegClass
.contains(Reg
)) {
1160 RegClass
= &AArch64::ZPRRegClass
;
1161 } else if (AArch64::PPRRegClass
.contains(Reg
)) {
1162 RegClass
= &AArch64::PPRRegClass
;
1163 } else if (AArch64::PNRRegClass
.contains(Reg
)) {
1164 RegClass
= &AArch64::PNRRegClass
;
1166 RegClass
= &AArch64::FPR128RegClass
;
1167 AltName
= AArch64::vreg
;
1170 // If this is a b, h, s, d, or q register, print it as a v register.
1171 return printAsmRegInClass(MO
, RegClass
, AltName
, O
);
1174 printOperand(MI
, OpNum
, O
);
1178 bool AArch64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr
*MI
,
1180 const char *ExtraCode
,
1182 if (ExtraCode
&& ExtraCode
[0] && ExtraCode
[0] != 'a')
1183 return true; // Unknown modifier.
1185 const MachineOperand
&MO
= MI
->getOperand(OpNum
);
1186 assert(MO
.isReg() && "unexpected inline asm memory operand");
1187 O
<< "[" << AArch64InstPrinter::getRegisterName(MO
.getReg()) << "]";
1191 void AArch64AsmPrinter::PrintDebugValueComment(const MachineInstr
*MI
,
1193 unsigned NOps
= MI
->getNumOperands();
1195 OS
<< '\t' << MAI
->getCommentString() << "DEBUG_VALUE: ";
1196 // cast away const; DIetc do not take const operands for some reason.
1197 OS
<< MI
->getDebugVariable()->getName();
1199 // Frame address. Currently handles register +- offset only.
1200 assert(MI
->isIndirectDebugValue());
1202 for (unsigned I
= 0, E
= std::distance(MI
->debug_operands().begin(),
1203 MI
->debug_operands().end());
1207 printOperand(MI
, I
, OS
);
1211 printOperand(MI
, NOps
- 2, OS
);
1214 void AArch64AsmPrinter::emitJumpTableInfo() {
1215 const MachineJumpTableInfo
*MJTI
= MF
->getJumpTableInfo();
1218 const std::vector
<MachineJumpTableEntry
> &JT
= MJTI
->getJumpTables();
1219 if (JT
.empty()) return;
1221 const TargetLoweringObjectFile
&TLOF
= getObjFileLowering();
1222 MCSection
*ReadOnlySec
= TLOF
.getSectionForJumpTable(MF
->getFunction(), TM
);
1223 OutStreamer
->switchSection(ReadOnlySec
);
1225 auto AFI
= MF
->getInfo
<AArch64FunctionInfo
>();
1226 for (unsigned JTI
= 0, e
= JT
.size(); JTI
!= e
; ++JTI
) {
1227 const std::vector
<MachineBasicBlock
*> &JTBBs
= JT
[JTI
].MBBs
;
1229 // If this jump table was deleted, ignore it.
1230 if (JTBBs
.empty()) continue;
1232 unsigned Size
= AFI
->getJumpTableEntrySize(JTI
);
1233 emitAlignment(Align(Size
));
1234 OutStreamer
->emitLabel(GetJTISymbol(JTI
));
1236 const MCSymbol
*BaseSym
= AArch64FI
->getJumpTableEntryPCRelSymbol(JTI
);
1237 const MCExpr
*Base
= MCSymbolRefExpr::create(BaseSym
, OutContext
);
1239 for (auto *JTBB
: JTBBs
) {
1240 const MCExpr
*Value
=
1241 MCSymbolRefExpr::create(JTBB
->getSymbol(), OutContext
);
1244 // .byte/.hword (LBB - Lbase)>>2
1246 // .word LBB - Lbase
1247 Value
= MCBinaryExpr::createSub(Value
, Base
, OutContext
);
1249 Value
= MCBinaryExpr::createLShr(
1250 Value
, MCConstantExpr::create(2, OutContext
), OutContext
);
1252 OutStreamer
->emitValue(Value
, Size
);
1257 std::tuple
<const MCSymbol
*, uint64_t, const MCSymbol
*,
1258 codeview::JumpTableEntrySize
>
1259 AArch64AsmPrinter::getCodeViewJumpTableInfo(int JTI
,
1260 const MachineInstr
*BranchInstr
,
1261 const MCSymbol
*BranchLabel
) const {
1262 const auto AFI
= MF
->getInfo
<AArch64FunctionInfo
>();
1263 const auto Base
= AArch64FI
->getJumpTableEntryPCRelSymbol(JTI
);
1264 codeview::JumpTableEntrySize EntrySize
;
1265 switch (AFI
->getJumpTableEntrySize(JTI
)) {
1267 EntrySize
= codeview::JumpTableEntrySize::UInt8ShiftLeft
;
1270 EntrySize
= codeview::JumpTableEntrySize::UInt16ShiftLeft
;
1273 EntrySize
= codeview::JumpTableEntrySize::Int32
;
1276 llvm_unreachable("Unexpected jump table entry size");
1278 return std::make_tuple(Base
, 0, BranchLabel
, EntrySize
);
1281 void AArch64AsmPrinter::emitFunctionEntryLabel() {
1282 if (MF
->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall
||
1283 MF
->getFunction().getCallingConv() ==
1284 CallingConv::AArch64_SVE_VectorCall
||
1285 MF
->getInfo
<AArch64FunctionInfo
>()->isSVECC()) {
1287 static_cast<AArch64TargetStreamer
*>(OutStreamer
->getTargetStreamer());
1288 TS
->emitDirectiveVariantPCS(CurrentFnSym
);
1291 AsmPrinter::emitFunctionEntryLabel();
1293 if (TM
.getTargetTriple().isWindowsArm64EC() &&
1294 !MF
->getFunction().hasLocalLinkage()) {
1295 // For ARM64EC targets, a function definition's name is mangled differently
1296 // from the normal symbol, emit required aliases here.
1297 auto emitFunctionAlias
= [&](MCSymbol
*Src
, MCSymbol
*Dst
) {
1298 OutStreamer
->emitSymbolAttribute(Src
, MCSA_WeakAntiDep
);
1299 OutStreamer
->emitAssignment(
1300 Src
, MCSymbolRefExpr::create(Dst
, MCSymbolRefExpr::VK_None
,
1301 MMI
->getContext()));
1304 auto getSymbolFromMetadata
= [&](StringRef Name
) {
1305 MCSymbol
*Sym
= nullptr;
1306 if (MDNode
*Node
= MF
->getFunction().getMetadata(Name
)) {
1307 StringRef NameStr
= cast
<MDString
>(Node
->getOperand(0))->getString();
1308 Sym
= MMI
->getContext().getOrCreateSymbol(NameStr
);
1313 if (MCSymbol
*UnmangledSym
=
1314 getSymbolFromMetadata("arm64ec_unmangled_name")) {
1315 MCSymbol
*ECMangledSym
= getSymbolFromMetadata("arm64ec_ecmangled_name");
1318 // An external function, emit the alias from the unmangled symbol to
1319 // mangled symbol name and the alias from the mangled symbol to guest
1321 emitFunctionAlias(UnmangledSym
, ECMangledSym
);
1322 emitFunctionAlias(ECMangledSym
, CurrentFnSym
);
1324 // A function implementation, emit the alias from the unmangled symbol
1325 // to mangled symbol name.
1326 emitFunctionAlias(UnmangledSym
, CurrentFnSym
);
1332 void AArch64AsmPrinter::emitXXStructor(const DataLayout
&DL
,
1333 const Constant
*CV
) {
1334 if (const auto *CPA
= dyn_cast
<ConstantPtrAuth
>(CV
))
1335 if (CPA
->hasAddressDiscriminator() &&
1336 !CPA
->hasSpecialAddressDiscriminator(
1337 ConstantPtrAuth::AddrDiscriminator_CtorsDtors
))
1339 "unexpected address discrimination value for ctors/dtors entry, only "
1340 "'ptr inttoptr (i64 1 to ptr)' is allowed");
1341 // If we have signed pointers in xxstructors list, they'll be lowered to @AUTH
1342 // MCExpr's via AArch64AsmPrinter::lowerConstantPtrAuth. It does not look at
1343 // actual address discrimination value and only checks
1344 // hasAddressDiscriminator(), so it's OK to leave special address
1345 // discrimination value here.
1346 AsmPrinter::emitXXStructor(DL
, CV
);
1349 void AArch64AsmPrinter::emitGlobalAlias(const Module
&M
,
1350 const GlobalAlias
&GA
) {
1351 if (auto F
= dyn_cast_or_null
<Function
>(GA
.getAliasee())) {
1352 // Global aliases must point to a definition, but unmangled patchable
1353 // symbols are special and need to point to an undefined symbol with "EXP+"
1354 // prefix. Such undefined symbol is resolved by the linker by creating
1355 // x86 thunk that jumps back to the actual EC target.
1356 if (MDNode
*Node
= F
->getMetadata("arm64ec_exp_name")) {
1357 StringRef ExpStr
= cast
<MDString
>(Node
->getOperand(0))->getString();
1358 MCSymbol
*ExpSym
= MMI
->getContext().getOrCreateSymbol(ExpStr
);
1359 MCSymbol
*Sym
= MMI
->getContext().getOrCreateSymbol(GA
.getName());
1361 OutStreamer
->beginCOFFSymbolDef(ExpSym
);
1362 OutStreamer
->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL
);
1363 OutStreamer
->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_FUNCTION
1364 << COFF::SCT_COMPLEX_TYPE_SHIFT
);
1365 OutStreamer
->endCOFFSymbolDef();
1367 OutStreamer
->beginCOFFSymbolDef(Sym
);
1368 OutStreamer
->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL
);
1369 OutStreamer
->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_FUNCTION
1370 << COFF::SCT_COMPLEX_TYPE_SHIFT
);
1371 OutStreamer
->endCOFFSymbolDef();
1372 OutStreamer
->emitSymbolAttribute(Sym
, MCSA_Weak
);
1373 OutStreamer
->emitAssignment(
1374 Sym
, MCSymbolRefExpr::create(ExpSym
, MCSymbolRefExpr::VK_None
,
1375 MMI
->getContext()));
1379 AsmPrinter::emitGlobalAlias(M
, GA
);
1382 /// Small jump tables contain an unsigned byte or half, representing the offset
1383 /// from the lowest-addressed possible destination to the desired basic
1384 /// block. Since all instructions are 4-byte aligned, this is further compressed
1385 /// by counting in instructions rather than bytes (i.e. divided by 4). So, to
1386 /// materialize the correct destination we need:
1388 /// adr xDest, .LBB0_0
1389 /// ldrb wScratch, [xTable, xEntry] (with "lsl #1" for ldrh).
1390 /// add xDest, xDest, xScratch (with "lsl #2" for smaller entries)
1391 void AArch64AsmPrinter::LowerJumpTableDest(llvm::MCStreamer
&OutStreamer
,
1392 const llvm::MachineInstr
&MI
) {
1393 Register DestReg
= MI
.getOperand(0).getReg();
1394 Register ScratchReg
= MI
.getOperand(1).getReg();
1395 Register ScratchRegW
=
1396 STI
->getRegisterInfo()->getSubReg(ScratchReg
, AArch64::sub_32
);
1397 Register TableReg
= MI
.getOperand(2).getReg();
1398 Register EntryReg
= MI
.getOperand(3).getReg();
1399 int JTIdx
= MI
.getOperand(4).getIndex();
1400 int Size
= AArch64FI
->getJumpTableEntrySize(JTIdx
);
1402 // This has to be first because the compression pass based its reachability
1403 // calculations on the start of the JumpTableDest instruction.
1405 MF
->getInfo
<AArch64FunctionInfo
>()->getJumpTableEntryPCRelSymbol(JTIdx
);
1407 // If we don't already have a symbol to use as the base, use the ADR
1408 // instruction itself.
1410 Label
= MF
->getContext().createTempSymbol();
1411 AArch64FI
->setJumpTableEntryInfo(JTIdx
, Size
, Label
);
1412 OutStreamer
.emitLabel(Label
);
1415 auto LabelExpr
= MCSymbolRefExpr::create(Label
, MF
->getContext());
1416 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::ADR
)
1418 .addExpr(LabelExpr
));
1420 // Load the number of instruction-steps to offset from the label.
1423 case 1: LdrOpcode
= AArch64::LDRBBroX
; break;
1424 case 2: LdrOpcode
= AArch64::LDRHHroX
; break;
1425 case 4: LdrOpcode
= AArch64::LDRSWroX
; break;
1427 llvm_unreachable("Unknown jump table size");
1430 EmitToStreamer(OutStreamer
, MCInstBuilder(LdrOpcode
)
1431 .addReg(Size
== 4 ? ScratchReg
: ScratchRegW
)
1435 .addImm(Size
== 1 ? 0 : 1));
1437 // Add to the already materialized base label address, multiplying by 4 if
1439 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::ADDXrs
)
1443 .addImm(Size
== 4 ? 0 : 2));
1446 void AArch64AsmPrinter::LowerHardenedBRJumpTable(const MachineInstr
&MI
) {
1447 const MachineJumpTableInfo
*MJTI
= MF
->getJumpTableInfo();
1448 assert(MJTI
&& "Can't lower jump-table dispatch without JTI");
1450 const std::vector
<MachineJumpTableEntry
> &JTs
= MJTI
->getJumpTables();
1451 assert(!JTs
.empty() && "Invalid JT index for jump-table dispatch");
1454 // mov x17, #<size of table> ; depending on table size, with MOVKs
1455 // cmp x16, x17 ; or #imm if table size fits in 12-bit
1456 // csel x16, x16, xzr, ls ; check for index overflow
1458 // adrp x17, Ltable@PAGE ; materialize table address
1459 // add x17, Ltable@PAGEOFF
1460 // ldrsw x16, [x17, x16, lsl #2] ; load table entry
1463 // adr x17, Lanchor ; compute target address
1464 // add x16, x17, x16
1465 // br x16 ; branch to target
1467 MachineOperand JTOp
= MI
.getOperand(0);
1469 unsigned JTI
= JTOp
.getIndex();
1470 assert(!AArch64FI
->getJumpTableEntryPCRelSymbol(JTI
) &&
1471 "unsupported compressed jump table");
1473 const uint64_t NumTableEntries
= JTs
[JTI
].MBBs
.size();
1475 // cmp only supports a 12-bit immediate. If we need more, materialize the
1476 // immediate, using x17 as a scratch register.
1477 uint64_t MaxTableEntry
= NumTableEntries
- 1;
1478 if (isUInt
<12>(MaxTableEntry
)) {
1479 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::SUBSXri
)
1480 .addReg(AArch64::XZR
)
1481 .addReg(AArch64::X16
)
1482 .addImm(MaxTableEntry
)
1485 emitMOVZ(AArch64::X17
, static_cast<uint16_t>(MaxTableEntry
), 0);
1486 // It's sad that we have to manually materialize instructions, but we can't
1487 // trivially reuse the main pseudo expansion logic.
1488 // A MOVK sequence is easy enough to generate and handles the general case.
1489 for (int Offset
= 16; Offset
< 64; Offset
+= 16) {
1490 if ((MaxTableEntry
>> Offset
) == 0)
1492 emitMOVK(AArch64::X17
, static_cast<uint16_t>(MaxTableEntry
>> Offset
),
1495 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::SUBSXrs
)
1496 .addReg(AArch64::XZR
)
1497 .addReg(AArch64::X16
)
1498 .addReg(AArch64::X17
)
1502 // This picks entry #0 on failure.
1503 // We might want to trap instead.
1504 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::CSELXr
)
1505 .addReg(AArch64::X16
)
1506 .addReg(AArch64::X16
)
1507 .addReg(AArch64::XZR
)
1508 .addImm(AArch64CC::LS
));
1510 // Prepare the @PAGE/@PAGEOFF low/high operands.
1511 MachineOperand
JTMOHi(JTOp
), JTMOLo(JTOp
);
1512 MCOperand JTMCHi
, JTMCLo
;
1514 JTMOHi
.setTargetFlags(AArch64II::MO_PAGE
);
1515 JTMOLo
.setTargetFlags(AArch64II::MO_PAGEOFF
| AArch64II::MO_NC
);
1517 MCInstLowering
.lowerOperand(JTMOHi
, JTMCHi
);
1518 MCInstLowering
.lowerOperand(JTMOLo
, JTMCLo
);
1522 MCInstBuilder(AArch64::ADRP
).addReg(AArch64::X17
).addOperand(JTMCHi
));
1524 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::ADDXri
)
1525 .addReg(AArch64::X17
)
1526 .addReg(AArch64::X17
)
1530 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::LDRSWroX
)
1531 .addReg(AArch64::X16
)
1532 .addReg(AArch64::X17
)
1533 .addReg(AArch64::X16
)
1537 MCSymbol
*AdrLabel
= MF
->getContext().createTempSymbol();
1538 const auto *AdrLabelE
= MCSymbolRefExpr::create(AdrLabel
, MF
->getContext());
1539 AArch64FI
->setJumpTableEntryInfo(JTI
, 4, AdrLabel
);
1541 OutStreamer
->emitLabel(AdrLabel
);
1544 MCInstBuilder(AArch64::ADR
).addReg(AArch64::X17
).addExpr(AdrLabelE
));
1546 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::ADDXrs
)
1547 .addReg(AArch64::X16
)
1548 .addReg(AArch64::X17
)
1549 .addReg(AArch64::X16
)
1552 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::BR
).addReg(AArch64::X16
));
1555 void AArch64AsmPrinter::LowerMOPS(llvm::MCStreamer
&OutStreamer
,
1556 const llvm::MachineInstr
&MI
) {
1557 unsigned Opcode
= MI
.getOpcode();
1558 assert(STI
->hasMOPS());
1559 assert(STI
->hasMTE() || Opcode
!= AArch64::MOPSMemorySetTaggingPseudo
);
1561 const auto Ops
= [Opcode
]() -> std::array
<unsigned, 3> {
1562 if (Opcode
== AArch64::MOPSMemoryCopyPseudo
)
1563 return {AArch64::CPYFP
, AArch64::CPYFM
, AArch64::CPYFE
};
1564 if (Opcode
== AArch64::MOPSMemoryMovePseudo
)
1565 return {AArch64::CPYP
, AArch64::CPYM
, AArch64::CPYE
};
1566 if (Opcode
== AArch64::MOPSMemorySetPseudo
)
1567 return {AArch64::SETP
, AArch64::SETM
, AArch64::SETE
};
1568 if (Opcode
== AArch64::MOPSMemorySetTaggingPseudo
)
1569 return {AArch64::SETGP
, AArch64::SETGM
, AArch64::MOPSSETGE
};
1570 llvm_unreachable("Unhandled memory operation pseudo");
1572 const bool IsSet
= Opcode
== AArch64::MOPSMemorySetPseudo
||
1573 Opcode
== AArch64::MOPSMemorySetTaggingPseudo
;
1575 for (auto Op
: Ops
) {
1577 auto MCIB
= MCInstBuilder(Op
);
1578 // Destination registers
1579 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1580 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1582 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1584 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1585 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1586 MCIB
.addReg(MI
.getOperand(i
++).getReg());
1588 EmitToStreamer(OutStreamer
, MCIB
);
1592 void AArch64AsmPrinter::LowerSTACKMAP(MCStreamer
&OutStreamer
, StackMaps
&SM
,
1593 const MachineInstr
&MI
) {
1594 unsigned NumNOPBytes
= StackMapOpers(&MI
).getNumPatchBytes();
1596 auto &Ctx
= OutStreamer
.getContext();
1597 MCSymbol
*MILabel
= Ctx
.createTempSymbol();
1598 OutStreamer
.emitLabel(MILabel
);
1600 SM
.recordStackMap(*MILabel
, MI
);
1601 assert(NumNOPBytes
% 4 == 0 && "Invalid number of NOP bytes requested!");
1603 // Scan ahead to trim the shadow.
1604 const MachineBasicBlock
&MBB
= *MI
.getParent();
1605 MachineBasicBlock::const_iterator
MII(MI
);
1607 while (NumNOPBytes
> 0) {
1608 if (MII
== MBB
.end() || MII
->isCall() ||
1609 MII
->getOpcode() == AArch64::DBG_VALUE
||
1610 MII
->getOpcode() == TargetOpcode::PATCHPOINT
||
1611 MII
->getOpcode() == TargetOpcode::STACKMAP
)
1618 for (unsigned i
= 0; i
< NumNOPBytes
; i
+= 4)
1619 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::HINT
).addImm(0));
1622 // Lower a patchpoint of the form:
1623 // [<def>], <id>, <numBytes>, <target>, <numArgs>
1624 void AArch64AsmPrinter::LowerPATCHPOINT(MCStreamer
&OutStreamer
, StackMaps
&SM
,
1625 const MachineInstr
&MI
) {
1626 auto &Ctx
= OutStreamer
.getContext();
1627 MCSymbol
*MILabel
= Ctx
.createTempSymbol();
1628 OutStreamer
.emitLabel(MILabel
);
1629 SM
.recordPatchPoint(*MILabel
, MI
);
1631 PatchPointOpers
Opers(&MI
);
1633 int64_t CallTarget
= Opers
.getCallTarget().getImm();
1634 unsigned EncodedBytes
= 0;
1636 assert((CallTarget
& 0xFFFFFFFFFFFF) == CallTarget
&&
1637 "High 16 bits of call target should be zero.");
1638 Register ScratchReg
= MI
.getOperand(Opers
.getNextScratchIdx()).getReg();
1640 // Materialize the jump address:
1641 emitMOVZ(ScratchReg
, (CallTarget
>> 32) & 0xFFFF, 32);
1642 emitMOVK(ScratchReg
, (CallTarget
>> 16) & 0xFFFF, 16);
1643 emitMOVK(ScratchReg
, CallTarget
& 0xFFFF, 0);
1644 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::BLR
).addReg(ScratchReg
));
1647 unsigned NumBytes
= Opers
.getNumPatchBytes();
1648 assert(NumBytes
>= EncodedBytes
&&
1649 "Patchpoint can't request size less than the length of a call.");
1650 assert((NumBytes
- EncodedBytes
) % 4 == 0 &&
1651 "Invalid number of NOP bytes requested!");
1652 for (unsigned i
= EncodedBytes
; i
< NumBytes
; i
+= 4)
1653 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::HINT
).addImm(0));
1656 void AArch64AsmPrinter::LowerSTATEPOINT(MCStreamer
&OutStreamer
, StackMaps
&SM
,
1657 const MachineInstr
&MI
) {
1658 StatepointOpers
SOpers(&MI
);
1659 if (unsigned PatchBytes
= SOpers
.getNumPatchBytes()) {
1660 assert(PatchBytes
% 4 == 0 && "Invalid number of NOP bytes requested!");
1661 for (unsigned i
= 0; i
< PatchBytes
; i
+= 4)
1662 EmitToStreamer(OutStreamer
, MCInstBuilder(AArch64::HINT
).addImm(0));
1664 // Lower call target and choose correct opcode
1665 const MachineOperand
&CallTarget
= SOpers
.getCallTarget();
1666 MCOperand CallTargetMCOp
;
1667 unsigned CallOpcode
;
1668 switch (CallTarget
.getType()) {
1669 case MachineOperand::MO_GlobalAddress
:
1670 case MachineOperand::MO_ExternalSymbol
:
1671 MCInstLowering
.lowerOperand(CallTarget
, CallTargetMCOp
);
1672 CallOpcode
= AArch64::BL
;
1674 case MachineOperand::MO_Immediate
:
1675 CallTargetMCOp
= MCOperand::createImm(CallTarget
.getImm());
1676 CallOpcode
= AArch64::BL
;
1678 case MachineOperand::MO_Register
:
1679 CallTargetMCOp
= MCOperand::createReg(CallTarget
.getReg());
1680 CallOpcode
= AArch64::BLR
;
1683 llvm_unreachable("Unsupported operand type in statepoint call target");
1687 EmitToStreamer(OutStreamer
,
1688 MCInstBuilder(CallOpcode
).addOperand(CallTargetMCOp
));
1691 auto &Ctx
= OutStreamer
.getContext();
1692 MCSymbol
*MILabel
= Ctx
.createTempSymbol();
1693 OutStreamer
.emitLabel(MILabel
);
1694 SM
.recordStatepoint(*MILabel
, MI
);
1697 void AArch64AsmPrinter::LowerFAULTING_OP(const MachineInstr
&FaultingMI
) {
1698 // FAULTING_LOAD_OP <def>, <faltinf type>, <MBB handler>,
1699 // <opcode>, <operands>
1701 Register DefRegister
= FaultingMI
.getOperand(0).getReg();
1702 FaultMaps::FaultKind FK
=
1703 static_cast<FaultMaps::FaultKind
>(FaultingMI
.getOperand(1).getImm());
1704 MCSymbol
*HandlerLabel
= FaultingMI
.getOperand(2).getMBB()->getSymbol();
1705 unsigned Opcode
= FaultingMI
.getOperand(3).getImm();
1706 unsigned OperandsBeginIdx
= 4;
1708 auto &Ctx
= OutStreamer
->getContext();
1709 MCSymbol
*FaultingLabel
= Ctx
.createTempSymbol();
1710 OutStreamer
->emitLabel(FaultingLabel
);
1712 assert(FK
< FaultMaps::FaultKindMax
&& "Invalid Faulting Kind!");
1713 FM
.recordFaultingOp(FK
, FaultingLabel
, HandlerLabel
);
1716 MI
.setOpcode(Opcode
);
1718 if (DefRegister
!= (Register
)0)
1719 MI
.addOperand(MCOperand::createReg(DefRegister
));
1721 for (const MachineOperand
&MO
:
1722 llvm::drop_begin(FaultingMI
.operands(), OperandsBeginIdx
)) {
1724 lowerOperand(MO
, Dest
);
1725 MI
.addOperand(Dest
);
1728 OutStreamer
->AddComment("on-fault: " + HandlerLabel
->getName());
1732 void AArch64AsmPrinter::emitMovXReg(Register Dest
, Register Src
) {
1733 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::ORRXrs
)
1735 .addReg(AArch64::XZR
)
1740 void AArch64AsmPrinter::emitMOVZ(Register Dest
, uint64_t Imm
, unsigned Shift
) {
1741 bool Is64Bit
= AArch64::GPR64RegClass
.contains(Dest
);
1742 EmitToStreamer(*OutStreamer
,
1743 MCInstBuilder(Is64Bit
? AArch64::MOVZXi
: AArch64::MOVZWi
)
1749 void AArch64AsmPrinter::emitMOVK(Register Dest
, uint64_t Imm
, unsigned Shift
) {
1750 bool Is64Bit
= AArch64::GPR64RegClass
.contains(Dest
);
1751 EmitToStreamer(*OutStreamer
,
1752 MCInstBuilder(Is64Bit
? AArch64::MOVKXi
: AArch64::MOVKWi
)
1759 void AArch64AsmPrinter::emitFMov0(const MachineInstr
&MI
) {
1760 Register DestReg
= MI
.getOperand(0).getReg();
1761 if (STI
->hasZeroCycleZeroingFP() && !STI
->hasZeroCycleZeroingFPWorkaround() &&
1762 STI
->isNeonAvailable()) {
1763 // Convert H/S register to corresponding D register
1764 if (AArch64::H0
<= DestReg
&& DestReg
<= AArch64::H31
)
1765 DestReg
= AArch64::D0
+ (DestReg
- AArch64::H0
);
1766 else if (AArch64::S0
<= DestReg
&& DestReg
<= AArch64::S31
)
1767 DestReg
= AArch64::D0
+ (DestReg
- AArch64::S0
);
1769 assert(AArch64::D0
<= DestReg
&& DestReg
<= AArch64::D31
);
1772 MOVI
.setOpcode(AArch64::MOVID
);
1773 MOVI
.addOperand(MCOperand::createReg(DestReg
));
1774 MOVI
.addOperand(MCOperand::createImm(0));
1775 EmitToStreamer(*OutStreamer
, MOVI
);
1778 switch (MI
.getOpcode()) {
1779 default: llvm_unreachable("Unexpected opcode");
1780 case AArch64::FMOVH0
:
1781 FMov
.setOpcode(STI
->hasFullFP16() ? AArch64::FMOVWHr
: AArch64::FMOVWSr
);
1782 if (!STI
->hasFullFP16())
1783 DestReg
= (AArch64::S0
+ (DestReg
- AArch64::H0
));
1784 FMov
.addOperand(MCOperand::createReg(DestReg
));
1785 FMov
.addOperand(MCOperand::createReg(AArch64::WZR
));
1787 case AArch64::FMOVS0
:
1788 FMov
.setOpcode(AArch64::FMOVWSr
);
1789 FMov
.addOperand(MCOperand::createReg(DestReg
));
1790 FMov
.addOperand(MCOperand::createReg(AArch64::WZR
));
1792 case AArch64::FMOVD0
:
1793 FMov
.setOpcode(AArch64::FMOVXDr
);
1794 FMov
.addOperand(MCOperand::createReg(DestReg
));
1795 FMov
.addOperand(MCOperand::createReg(AArch64::XZR
));
1798 EmitToStreamer(*OutStreamer
, FMov
);
1802 Register
AArch64AsmPrinter::emitPtrauthDiscriminator(uint16_t Disc
,
1804 Register ScratchReg
,
1805 bool MayUseAddrAsScratch
) {
1806 assert(ScratchReg
== AArch64::X16
|| ScratchReg
== AArch64::X17
);
1807 // So far we've used NoRegister in pseudos. Now we need real encodings.
1808 if (AddrDisc
== AArch64::NoRegister
)
1809 AddrDisc
= AArch64::XZR
;
1811 // If there is no constant discriminator, there's no blend involved:
1812 // just use the address discriminator register as-is (XZR or not).
1816 // If there's only a constant discriminator, MOV it into the scratch register.
1817 if (AddrDisc
== AArch64::XZR
) {
1818 emitMOVZ(ScratchReg
, Disc
, 0);
1822 // If there are both, emit a blend into the scratch register.
1824 // Check if we can save one MOV instruction.
1825 assert(MayUseAddrAsScratch
|| ScratchReg
!= AddrDisc
);
1826 bool AddrDiscIsSafe
= AddrDisc
== AArch64::X16
|| AddrDisc
== AArch64::X17
;
1827 if (MayUseAddrAsScratch
&& AddrDiscIsSafe
)
1828 ScratchReg
= AddrDisc
;
1830 emitMovXReg(ScratchReg
, AddrDisc
);
1832 emitMOVK(ScratchReg
, Disc
, 48);
1836 /// Emits a code sequence to check an authenticated pointer value.
1838 /// If OnFailure argument is passed, jump there on check failure instead
1839 /// of proceeding to the next instruction (only if ShouldTrap is false).
1840 void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
1841 Register TestedReg
, Register ScratchReg
, AArch64PACKey::ID Key
,
1842 AArch64PAuth::AuthCheckMethod Method
, bool ShouldTrap
,
1843 const MCSymbol
*OnFailure
) {
1844 // Insert a sequence to check if authentication of TestedReg succeeded,
1847 // - checked and clearing:
1848 // ; x16 is TestedReg, x17 is ScratchReg
1856 // ; skipped if authentication failed
1860 // - checked and trapping:
1865 // brk #<0xc470 + aut key>
1869 // See the documentation on AuthCheckMethod enumeration constants for
1870 // the specific code sequences that can be used to perform the check.
1871 using AArch64PAuth::AuthCheckMethod
;
1873 if (Method
== AuthCheckMethod::None
)
1875 if (Method
== AuthCheckMethod::DummyLoad
) {
1876 EmitToStreamer(MCInstBuilder(AArch64::LDRWui
)
1877 .addReg(getWRegFromXReg(ScratchReg
))
1880 assert(ShouldTrap
&& !OnFailure
&& "DummyLoad always traps on error");
1884 MCSymbol
*SuccessSym
= createTempSymbol("auth_success_");
1885 if (Method
== AuthCheckMethod::XPAC
|| Method
== AuthCheckMethod::XPACHint
) {
1886 // mov Xscratch, Xtested
1887 emitMovXReg(ScratchReg
, TestedReg
);
1889 if (Method
== AuthCheckMethod::XPAC
) {
1890 // xpac(i|d) Xscratch
1891 unsigned XPACOpc
= getXPACOpcodeForKey(Key
);
1893 MCInstBuilder(XPACOpc
).addReg(ScratchReg
).addReg(ScratchReg
));
1897 // Note that this method applies XPAC to TestedReg instead of ScratchReg.
1898 assert(TestedReg
== AArch64::LR
&&
1899 "XPACHint mode is only compatible with checking the LR register");
1900 assert((Key
== AArch64PACKey::IA
|| Key
== AArch64PACKey::IB
) &&
1901 "XPACHint mode is only compatible with I-keys");
1902 EmitToStreamer(MCInstBuilder(AArch64::XPACLRI
));
1905 // cmp Xtested, Xscratch
1906 EmitToStreamer(MCInstBuilder(AArch64::SUBSXrs
)
1907 .addReg(AArch64::XZR
)
1914 MCInstBuilder(AArch64::Bcc
)
1915 .addImm(AArch64CC::EQ
)
1916 .addExpr(MCSymbolRefExpr::create(SuccessSym
, OutContext
)));
1917 } else if (Method
== AuthCheckMethod::HighBitsNoTBI
) {
1918 // eor Xscratch, Xtested, Xtested, lsl #1
1919 EmitToStreamer(MCInstBuilder(AArch64::EORXrs
)
1924 // tbz Xscratch, #62, Lsuccess
1926 MCInstBuilder(AArch64::TBZX
)
1929 .addExpr(MCSymbolRefExpr::create(SuccessSym
, OutContext
)));
1931 llvm_unreachable("Unsupported check method");
1935 assert(!OnFailure
&& "Cannot specify OnFailure with ShouldTrap");
1936 // Trapping sequences do a 'brk'.
1937 // brk #<0xc470 + aut key>
1938 EmitToStreamer(MCInstBuilder(AArch64::BRK
).addImm(0xc470 | Key
));
1940 // Non-trapping checked sequences return the stripped result in TestedReg,
1941 // skipping over success-only code (such as re-signing the pointer) if
1943 // Note that this can introduce an authentication oracle (such as based on
1944 // the high bits of the re-signed value).
1946 // FIXME: The XPAC method can be optimized by applying XPAC to TestedReg
1947 // instead of ScratchReg, thus eliminating one `mov` instruction.
1948 // Both XPAC and XPACHint can be further optimized by not using a
1949 // conditional branch jumping over an unconditional one.
1952 case AuthCheckMethod::XPACHint
:
1953 // LR is already XPAC-ed at this point.
1955 case AuthCheckMethod::XPAC
:
1956 // mov Xtested, Xscratch
1957 emitMovXReg(TestedReg
, ScratchReg
);
1960 // If Xtested was not XPAC-ed so far, emit XPAC here.
1961 // xpac(i|d) Xtested
1962 unsigned XPACOpc
= getXPACOpcodeForKey(Key
);
1964 MCInstBuilder(XPACOpc
).addReg(TestedReg
).addReg(TestedReg
));
1970 MCInstBuilder(AArch64::B
)
1971 .addExpr(MCSymbolRefExpr::create(OnFailure
, OutContext
)));
1975 // If the auth check succeeds, we can continue.
1977 OutStreamer
->emitLabel(SuccessSym
);
1980 // With Pointer Authentication, it may be needed to explicitly check the
1981 // authenticated value in LR before performing a tail call.
1982 // Otherwise, the callee may re-sign the invalid return address,
1983 // introducing a signing oracle.
1984 void AArch64AsmPrinter::emitPtrauthTailCallHardening(const MachineInstr
*TC
) {
1985 if (!AArch64FI
->shouldSignReturnAddress(*MF
))
1988 auto LRCheckMethod
= STI
->getAuthenticatedLRCheckMethod(*MF
);
1989 if (LRCheckMethod
== AArch64PAuth::AuthCheckMethod::None
)
1992 const AArch64RegisterInfo
*TRI
= STI
->getRegisterInfo();
1993 Register ScratchReg
=
1994 TC
->readsRegister(AArch64::X16
, TRI
) ? AArch64::X17
: AArch64::X16
;
1995 assert(!TC
->readsRegister(ScratchReg
, TRI
) &&
1996 "Neither x16 nor x17 is available as a scratch register");
1997 AArch64PACKey::ID Key
=
1998 AArch64FI
->shouldSignWithBKey() ? AArch64PACKey::IB
: AArch64PACKey::IA
;
1999 emitPtrauthCheckAuthenticatedValue(
2000 AArch64::LR
, ScratchReg
, Key
, LRCheckMethod
,
2001 /*ShouldTrap=*/true, /*OnFailure=*/nullptr);
2004 void AArch64AsmPrinter::emitPtrauthAuthResign(const MachineInstr
*MI
) {
2005 const bool IsAUTPAC
= MI
->getOpcode() == AArch64::AUTPAC
;
2007 // We expand AUT/AUTPAC into a sequence of the form
2009 // ; authenticate x16
2010 // ; check pointer in x16
2012 // ; sign x16 (if AUTPAC)
2013 // Lend: ; if not trapping on failure
2015 // with the checking sequence chosen depending on whether/how we should check
2016 // the pointer and whether we should trap on failure.
2018 // By default, auth/resign sequences check for auth failures.
2019 bool ShouldCheck
= true;
2020 // In the checked sequence, we only trap if explicitly requested.
2021 bool ShouldTrap
= MF
->getFunction().hasFnAttribute("ptrauth-auth-traps");
2023 // On an FPAC CPU, you get traps whether you want them or not: there's
2024 // no point in emitting checks or traps.
2026 ShouldCheck
= ShouldTrap
= false;
2028 // However, command-line flags can override this, for experimentation.
2029 switch (PtrauthAuthChecks
) {
2030 case PtrauthCheckMode::Default
:
2032 case PtrauthCheckMode::Unchecked
:
2033 ShouldCheck
= ShouldTrap
= false;
2035 case PtrauthCheckMode::Poison
:
2039 case PtrauthCheckMode::Trap
:
2040 ShouldCheck
= ShouldTrap
= true;
2044 auto AUTKey
= (AArch64PACKey::ID
)MI
->getOperand(0).getImm();
2045 uint64_t AUTDisc
= MI
->getOperand(1).getImm();
2046 unsigned AUTAddrDisc
= MI
->getOperand(2).getReg();
2048 // Compute aut discriminator into x17
2049 assert(isUInt
<16>(AUTDisc
));
2050 Register AUTDiscReg
=
2051 emitPtrauthDiscriminator(AUTDisc
, AUTAddrDisc
, AArch64::X17
);
2052 bool AUTZero
= AUTDiscReg
== AArch64::XZR
;
2053 unsigned AUTOpc
= getAUTOpcodeForKey(AUTKey
, AUTZero
);
2055 // autiza x16 ; if AUTZero
2056 // autia x16, x17 ; if !AUTZero
2058 AUTInst
.setOpcode(AUTOpc
);
2059 AUTInst
.addOperand(MCOperand::createReg(AArch64::X16
));
2060 AUTInst
.addOperand(MCOperand::createReg(AArch64::X16
));
2062 AUTInst
.addOperand(MCOperand::createReg(AUTDiscReg
));
2063 EmitToStreamer(*OutStreamer
, AUTInst
);
2065 // Unchecked or checked-but-non-trapping AUT is just an "AUT": we're done.
2066 if (!IsAUTPAC
&& (!ShouldCheck
|| !ShouldTrap
))
2069 MCSymbol
*EndSym
= nullptr;
2072 if (IsAUTPAC
&& !ShouldTrap
)
2073 EndSym
= createTempSymbol("resign_end_");
2075 emitPtrauthCheckAuthenticatedValue(AArch64::X16
, AArch64::X17
, AUTKey
,
2076 AArch64PAuth::AuthCheckMethod::XPAC
,
2077 ShouldTrap
, EndSym
);
2080 // We already emitted unchecked and checked-but-non-trapping AUTs.
2081 // That left us with trapping AUTs, and AUTPACs.
2082 // Trapping AUTs don't need PAC: we're done.
2086 auto PACKey
= (AArch64PACKey::ID
)MI
->getOperand(3).getImm();
2087 uint64_t PACDisc
= MI
->getOperand(4).getImm();
2088 unsigned PACAddrDisc
= MI
->getOperand(5).getReg();
2090 // Compute pac discriminator into x17
2091 assert(isUInt
<16>(PACDisc
));
2092 Register PACDiscReg
=
2093 emitPtrauthDiscriminator(PACDisc
, PACAddrDisc
, AArch64::X17
);
2094 bool PACZero
= PACDiscReg
== AArch64::XZR
;
2095 unsigned PACOpc
= getPACOpcodeForKey(PACKey
, PACZero
);
2097 // pacizb x16 ; if PACZero
2098 // pacib x16, x17 ; if !PACZero
2100 PACInst
.setOpcode(PACOpc
);
2101 PACInst
.addOperand(MCOperand::createReg(AArch64::X16
));
2102 PACInst
.addOperand(MCOperand::createReg(AArch64::X16
));
2104 PACInst
.addOperand(MCOperand::createReg(PACDiscReg
));
2105 EmitToStreamer(*OutStreamer
, PACInst
);
2109 OutStreamer
->emitLabel(EndSym
);
2112 void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr
*MI
) {
2113 bool IsCall
= MI
->getOpcode() == AArch64::BLRA
;
2114 unsigned BrTarget
= MI
->getOperand(0).getReg();
2116 auto Key
= (AArch64PACKey::ID
)MI
->getOperand(1).getImm();
2117 assert((Key
== AArch64PACKey::IA
|| Key
== AArch64PACKey::IB
) &&
2118 "Invalid auth call key");
2120 uint64_t Disc
= MI
->getOperand(2).getImm();
2121 assert(isUInt
<16>(Disc
));
2123 unsigned AddrDisc
= MI
->getOperand(3).getReg();
2125 // Make sure AddrDisc is solely used to compute the discriminator.
2126 // While hardly meaningful, it is still possible to describe an authentication
2127 // of a pointer against its own value (instead of storage address) with
2128 // intrinsics, so use report_fatal_error instead of assert.
2129 if (BrTarget
== AddrDisc
)
2130 report_fatal_error("Branch target is signed with its own value");
2132 // If we are printing BLRA pseudo instruction, then x16 and x17 are
2133 // implicit-def'ed by the MI and AddrDisc is not used as any other input, so
2134 // try to save one MOV by setting MayUseAddrAsScratch.
2135 // Unlike BLRA, BRA pseudo is used to perform computed goto, and thus not
2136 // declared as clobbering x16/x17.
2137 Register DiscReg
= emitPtrauthDiscriminator(Disc
, AddrDisc
, AArch64::X17
,
2138 /*MayUseAddrAsScratch=*/IsCall
);
2139 bool IsZeroDisc
= DiscReg
== AArch64::XZR
;
2143 if (Key
== AArch64PACKey::IA
)
2144 Opc
= IsZeroDisc
? AArch64::BLRAAZ
: AArch64::BLRAA
;
2146 Opc
= IsZeroDisc
? AArch64::BLRABZ
: AArch64::BLRAB
;
2148 if (Key
== AArch64PACKey::IA
)
2149 Opc
= IsZeroDisc
? AArch64::BRAAZ
: AArch64::BRAA
;
2151 Opc
= IsZeroDisc
? AArch64::BRABZ
: AArch64::BRAB
;
2155 BRInst
.setOpcode(Opc
);
2156 BRInst
.addOperand(MCOperand::createReg(BrTarget
));
2158 BRInst
.addOperand(MCOperand::createReg(DiscReg
));
2159 EmitToStreamer(*OutStreamer
, BRInst
);
2163 AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth
&CPA
) {
2164 MCContext
&Ctx
= OutContext
;
2166 // Figure out the base symbol and the addend, if any.
2167 APInt
Offset(64, 0);
2168 const Value
*BaseGV
= CPA
.getPointer()->stripAndAccumulateConstantOffsets(
2169 getDataLayout(), Offset
, /*AllowNonInbounds=*/true);
2171 auto *BaseGVB
= dyn_cast
<GlobalValue
>(BaseGV
);
2173 // If we can't understand the referenced ConstantExpr, there's nothing
2174 // else we can do: emit an error.
2176 BaseGV
->getContext().emitError(
2177 "cannot resolve target base/addend of ptrauth constant");
2181 // If there is an addend, turn that into the appropriate MCExpr.
2182 const MCExpr
*Sym
= MCSymbolRefExpr::create(getSymbol(BaseGVB
), Ctx
);
2184 Sym
= MCBinaryExpr::createAdd(
2185 Sym
, MCConstantExpr::create(Offset
.getSExtValue(), Ctx
), Ctx
);
2186 else if (Offset
.slt(0))
2187 Sym
= MCBinaryExpr::createSub(
2188 Sym
, MCConstantExpr::create((-Offset
).getSExtValue(), Ctx
), Ctx
);
2190 uint64_t KeyID
= CPA
.getKey()->getZExtValue();
2191 // We later rely on valid KeyID value in AArch64PACKeyIDToString call from
2192 // AArch64AuthMCExpr::printImpl, so fail fast.
2193 if (KeyID
> AArch64PACKey::LAST
)
2194 report_fatal_error("AArch64 PAC Key ID '" + Twine(KeyID
) +
2195 "' out of range [0, " +
2196 Twine((unsigned)AArch64PACKey::LAST
) + "]");
2198 uint64_t Disc
= CPA
.getDiscriminator()->getZExtValue();
2199 if (!isUInt
<16>(Disc
))
2200 report_fatal_error("AArch64 PAC Discriminator '" + Twine(Disc
) +
2201 "' out of range [0, 0xFFFF]");
2203 // Finally build the complete @AUTH expr.
2204 return AArch64AuthMCExpr::create(Sym
, Disc
, AArch64PACKey::ID(KeyID
),
2205 CPA
.hasAddressDiscriminator(), Ctx
);
2208 void AArch64AsmPrinter::LowerLOADauthptrstatic(const MachineInstr
&MI
) {
2209 unsigned DstReg
= MI
.getOperand(0).getReg();
2210 const MachineOperand
&GAOp
= MI
.getOperand(1);
2211 const uint64_t KeyC
= MI
.getOperand(2).getImm();
2212 assert(KeyC
<= AArch64PACKey::LAST
&&
2213 "key is out of range [0, AArch64PACKey::LAST]");
2214 const auto Key
= (AArch64PACKey::ID
)KeyC
;
2215 const uint64_t Disc
= MI
.getOperand(3).getImm();
2216 assert(isUInt
<16>(Disc
) &&
2217 "constant discriminator is out of range [0, 0xffff]");
2219 // Emit instruction sequence like the following:
2220 // ADRP x16, symbol$auth_ptr$key$disc
2221 // LDR x16, [x16, :lo12:symbol$auth_ptr$key$disc]
2223 // Where the $auth_ptr$ symbol is the stub slot containing the signed pointer
2225 MCSymbol
*AuthPtrStubSym
;
2226 if (TM
.getTargetTriple().isOSBinFormatELF()) {
2228 static_cast<const AArch64_ELFTargetObjectFile
&>(getObjFileLowering());
2230 assert(GAOp
.getOffset() == 0 &&
2231 "non-zero offset for $auth_ptr$ stub slots is not supported");
2232 const MCSymbol
*GASym
= TM
.getSymbol(GAOp
.getGlobal());
2233 AuthPtrStubSym
= TLOF
.getAuthPtrSlotSymbol(TM
, MMI
, GASym
, Key
, Disc
);
2235 assert(TM
.getTargetTriple().isOSBinFormatMachO() &&
2236 "LOADauthptrstatic is implemented only for MachO/ELF");
2238 const auto &TLOF
= static_cast<const AArch64_MachoTargetObjectFile
&>(
2239 getObjFileLowering());
2241 assert(GAOp
.getOffset() == 0 &&
2242 "non-zero offset for $auth_ptr$ stub slots is not supported");
2243 const MCSymbol
*GASym
= TM
.getSymbol(GAOp
.getGlobal());
2244 AuthPtrStubSym
= TLOF
.getAuthPtrSlotSymbol(TM
, MMI
, GASym
, Key
, Disc
);
2247 MachineOperand StubMOHi
=
2248 MachineOperand::CreateMCSymbol(AuthPtrStubSym
, AArch64II::MO_PAGE
);
2249 MachineOperand StubMOLo
= MachineOperand::CreateMCSymbol(
2250 AuthPtrStubSym
, AArch64II::MO_PAGEOFF
| AArch64II::MO_NC
);
2251 MCOperand StubMCHi
, StubMCLo
;
2253 MCInstLowering
.lowerOperand(StubMOHi
, StubMCHi
);
2254 MCInstLowering
.lowerOperand(StubMOLo
, StubMCLo
);
2258 MCInstBuilder(AArch64::ADRP
).addReg(DstReg
).addOperand(StubMCHi
));
2260 EmitToStreamer(*OutStreamer
, MCInstBuilder(AArch64::LDRXui
)
2263 .addOperand(StubMCLo
));
2266 void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr
&MI
) {
2267 const bool IsGOTLoad
= MI
.getOpcode() == AArch64::LOADgotPAC
;
2268 const bool IsELFSignedGOT
= MI
.getParent()
2270 ->getInfo
<AArch64FunctionInfo
>()
2271 ->hasELFSignedGOT();
2272 MachineOperand GAOp
= MI
.getOperand(0);
2273 const uint64_t KeyC
= MI
.getOperand(1).getImm();
2274 assert(KeyC
<= AArch64PACKey::LAST
&&
2275 "key is out of range [0, AArch64PACKey::LAST]");
2276 const auto Key
= (AArch64PACKey::ID
)KeyC
;
2277 const unsigned AddrDisc
= MI
.getOperand(2).getReg();
2278 const uint64_t Disc
= MI
.getOperand(3).getImm();
2279 assert(isUInt
<16>(Disc
) &&
2280 "constant discriminator is out of range [0, 0xffff]");
2282 const int64_t Offset
= GAOp
.getOffset();
2286 // target materialization:
2289 // adrp x16, :got:target
2290 // ldr x16, [x16, :got_lo12:target]
2291 // add offset to x16 if offset != 0
2292 // - ELF signed GOT:
2293 // adrp x17, :got:target
2294 // add x17, x17, :got_auth_lo12:target
2296 // aut{i|d}a x16, x17
2297 // check+trap sequence (if no FPAC)
2298 // add offset to x16 if offset != 0
2302 // add x16, x16, :lo12:target
2303 // add offset to x16 if offset != 0
2305 // add offset to x16:
2306 // - abs(offset) fits 24 bits:
2307 // add/sub x16, x16, #<offset>[, #lsl 12] (up to 2 instructions)
2308 // - abs(offset) does not fit 24 bits:
2310 // movn+movk sequence filling x17 register with the offset (up to 4
2312 // add x16, x16, x17
2314 // movz+movk sequence filling x17 register with the offset (up to 4
2316 // add x16, x16, x17
2319 // - 0 discriminator:
2321 // - Non-0 discriminator, no address discriminator:
2324 // - address discriminator (with potentially folded immediate discriminator):
2325 // pacia x16, xAddrDisc
2327 MachineOperand
GAMOHi(GAOp
), GAMOLo(GAOp
);
2328 MCOperand GAMCHi
, GAMCLo
;
2330 GAMOHi
.setTargetFlags(AArch64II::MO_PAGE
);
2331 GAMOLo
.setTargetFlags(AArch64II::MO_PAGEOFF
| AArch64II::MO_NC
);
2333 GAMOHi
.addTargetFlag(AArch64II::MO_GOT
);
2334 GAMOLo
.addTargetFlag(AArch64II::MO_GOT
);
2337 MCInstLowering
.lowerOperand(GAMOHi
, GAMCHi
);
2338 MCInstLowering
.lowerOperand(GAMOLo
, GAMCLo
);
2341 MCInstBuilder(AArch64::ADRP
)
2342 .addReg(IsGOTLoad
&& IsELFSignedGOT
? AArch64::X17
: AArch64::X16
)
2343 .addOperand(GAMCHi
));
2346 if (IsELFSignedGOT
) {
2347 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
2348 .addReg(AArch64::X17
)
2349 .addReg(AArch64::X17
)
2353 EmitToStreamer(MCInstBuilder(AArch64::LDRXui
)
2354 .addReg(AArch64::X16
)
2355 .addReg(AArch64::X17
)
2358 assert(GAOp
.isGlobal());
2359 assert(GAOp
.getGlobal()->getValueType() != nullptr);
2360 unsigned AuthOpcode
= GAOp
.getGlobal()->getValueType()->isFunctionTy()
2364 EmitToStreamer(MCInstBuilder(AuthOpcode
)
2365 .addReg(AArch64::X16
)
2366 .addReg(AArch64::X16
)
2367 .addReg(AArch64::X17
));
2369 if (!STI
->hasFPAC()) {
2370 auto AuthKey
= (AuthOpcode
== AArch64::AUTIA
? AArch64PACKey::IA
2371 : AArch64PACKey::DA
);
2373 emitPtrauthCheckAuthenticatedValue(AArch64::X16
, AArch64::X17
, AuthKey
,
2374 AArch64PAuth::AuthCheckMethod::XPAC
,
2375 /*ShouldTrap=*/true,
2376 /*OnFailure=*/nullptr);
2379 EmitToStreamer(MCInstBuilder(AArch64::LDRXui
)
2380 .addReg(AArch64::X16
)
2381 .addReg(AArch64::X16
)
2382 .addOperand(GAMCLo
));
2385 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
2386 .addReg(AArch64::X16
)
2387 .addReg(AArch64::X16
)
2393 const uint64_t AbsOffset
= (Offset
> 0 ? Offset
: -((uint64_t)Offset
));
2394 const bool IsNeg
= Offset
< 0;
2395 if (isUInt
<24>(AbsOffset
)) {
2396 for (int BitPos
= 0; BitPos
!= 24 && (AbsOffset
>> BitPos
);
2399 MCInstBuilder(IsNeg
? AArch64::SUBXri
: AArch64::ADDXri
)
2400 .addReg(AArch64::X16
)
2401 .addReg(AArch64::X16
)
2402 .addImm((AbsOffset
>> BitPos
) & 0xfff)
2403 .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL
, BitPos
)));
2406 const uint64_t UOffset
= Offset
;
2407 EmitToStreamer(MCInstBuilder(IsNeg
? AArch64::MOVNXi
: AArch64::MOVZXi
)
2408 .addReg(AArch64::X17
)
2409 .addImm((IsNeg
? ~UOffset
: UOffset
) & 0xffff)
2410 .addImm(/*shift=*/0));
2411 auto NeedMovk
= [IsNeg
, UOffset
](int BitPos
) -> bool {
2412 assert(BitPos
== 16 || BitPos
== 32 || BitPos
== 48);
2413 uint64_t Shifted
= UOffset
>> BitPos
;
2415 return Shifted
!= 0;
2416 for (int I
= 0; I
!= 64 - BitPos
; I
+= 16)
2417 if (((Shifted
>> I
) & 0xffff) != 0xffff)
2421 for (int BitPos
= 16; BitPos
!= 64 && NeedMovk(BitPos
); BitPos
+= 16)
2422 emitMOVK(AArch64::X17
, (UOffset
>> BitPos
) & 0xffff, BitPos
);
2424 EmitToStreamer(MCInstBuilder(AArch64::ADDXrs
)
2425 .addReg(AArch64::X16
)
2426 .addReg(AArch64::X16
)
2427 .addReg(AArch64::X17
)
2428 .addImm(/*shift=*/0));
2432 Register DiscReg
= emitPtrauthDiscriminator(Disc
, AddrDisc
, AArch64::X17
);
2434 auto MIB
= MCInstBuilder(getPACOpcodeForKey(Key
, DiscReg
== AArch64::XZR
))
2435 .addReg(AArch64::X16
)
2436 .addReg(AArch64::X16
);
2437 if (DiscReg
!= AArch64::XZR
)
2438 MIB
.addReg(DiscReg
);
2439 EmitToStreamer(MIB
);
2442 void AArch64AsmPrinter::LowerLOADgotAUTH(const MachineInstr
&MI
) {
2443 Register DstReg
= MI
.getOperand(0).getReg();
2444 Register AuthResultReg
= STI
->hasFPAC() ? DstReg
: AArch64::X16
;
2445 const MachineOperand
&GAMO
= MI
.getOperand(1);
2446 assert(GAMO
.getOffset() == 0);
2448 if (MI
.getMF()->getTarget().getCodeModel() == CodeModel::Tiny
) {
2450 MCInstLowering
.lowerOperand(GAMO
, GAMC
);
2452 MCInstBuilder(AArch64::ADR
).addReg(AArch64::X17
).addOperand(GAMC
));
2453 EmitToStreamer(MCInstBuilder(AArch64::LDRXui
)
2454 .addReg(AuthResultReg
)
2455 .addReg(AArch64::X17
)
2458 MachineOperand
GAHiOp(GAMO
);
2459 MachineOperand
GALoOp(GAMO
);
2460 GAHiOp
.addTargetFlag(AArch64II::MO_PAGE
);
2461 GALoOp
.addTargetFlag(AArch64II::MO_PAGEOFF
| AArch64II::MO_NC
);
2463 MCOperand GAMCHi
, GAMCLo
;
2464 MCInstLowering
.lowerOperand(GAHiOp
, GAMCHi
);
2465 MCInstLowering
.lowerOperand(GALoOp
, GAMCLo
);
2468 MCInstBuilder(AArch64::ADRP
).addReg(AArch64::X17
).addOperand(GAMCHi
));
2470 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
2471 .addReg(AArch64::X17
)
2472 .addReg(AArch64::X17
)
2476 EmitToStreamer(MCInstBuilder(AArch64::LDRXui
)
2477 .addReg(AuthResultReg
)
2478 .addReg(AArch64::X17
)
2482 assert(GAMO
.isGlobal());
2483 MCSymbol
*UndefWeakSym
;
2484 if (GAMO
.getGlobal()->hasExternalWeakLinkage()) {
2485 UndefWeakSym
= createTempSymbol("undef_weak");
2487 MCInstBuilder(AArch64::CBZX
)
2488 .addReg(AuthResultReg
)
2489 .addExpr(MCSymbolRefExpr::create(UndefWeakSym
, OutContext
)));
2492 assert(GAMO
.getGlobal()->getValueType() != nullptr);
2493 unsigned AuthOpcode
= GAMO
.getGlobal()->getValueType()->isFunctionTy()
2496 EmitToStreamer(MCInstBuilder(AuthOpcode
)
2497 .addReg(AuthResultReg
)
2498 .addReg(AuthResultReg
)
2499 .addReg(AArch64::X17
));
2501 if (GAMO
.getGlobal()->hasExternalWeakLinkage())
2502 OutStreamer
->emitLabel(UndefWeakSym
);
2504 if (!STI
->hasFPAC()) {
2506 (AuthOpcode
== AArch64::AUTIA
? AArch64PACKey::IA
: AArch64PACKey::DA
);
2508 emitPtrauthCheckAuthenticatedValue(AuthResultReg
, AArch64::X17
, AuthKey
,
2509 AArch64PAuth::AuthCheckMethod::XPAC
,
2510 /*ShouldTrap=*/true,
2511 /*OnFailure=*/nullptr);
2513 emitMovXReg(DstReg
, AuthResultReg
);
2518 AArch64AsmPrinter::lowerBlockAddressConstant(const BlockAddress
&BA
) {
2519 const MCExpr
*BAE
= AsmPrinter::lowerBlockAddressConstant(BA
);
2520 const Function
&Fn
= *BA
.getFunction();
2522 if (std::optional
<uint16_t> BADisc
=
2523 STI
->getPtrAuthBlockAddressDiscriminatorIfEnabled(Fn
))
2524 return AArch64AuthMCExpr::create(BAE
, *BADisc
, AArch64PACKey::IA
,
2525 /*HasAddressDiversity=*/false, OutContext
);
2530 // Simple pseudo-instructions have their lowering (with expansion to real
2531 // instructions) auto-generated.
2532 #include "AArch64GenMCPseudoLowering.inc"
2534 void AArch64AsmPrinter::EmitToStreamer(MCStreamer
&S
, const MCInst
&Inst
) {
2535 S
.emitInstruction(Inst
, *STI
);
2541 void AArch64AsmPrinter::emitInstruction(const MachineInstr
*MI
) {
2542 AArch64_MC::verifyInstructionPredicates(MI
->getOpcode(), STI
->getFeatureBits());
2546 auto CheckMISize
= make_scope_exit([&]() {
2547 assert(STI
->getInstrInfo()->getInstSizeInBytes(*MI
) >= InstsEmitted
* 4);
2551 // Do any auto-generated pseudo lowerings.
2552 if (MCInst OutInst
; lowerPseudoInstExpansion(MI
, OutInst
)) {
2553 EmitToStreamer(*OutStreamer
, OutInst
);
2557 if (MI
->getOpcode() == AArch64::ADRP
) {
2558 for (auto &Opd
: MI
->operands()) {
2559 if (Opd
.isSymbol() && StringRef(Opd
.getSymbolName()) ==
2560 "swift_async_extendedFramePointerFlags") {
2561 ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags
= true;
2566 if (AArch64FI
->getLOHRelated().count(MI
)) {
2567 // Generate a label for LOH related instruction
2568 MCSymbol
*LOHLabel
= createTempSymbol("loh");
2569 // Associate the instruction with the label
2570 LOHInstToLabel
[MI
] = LOHLabel
;
2571 OutStreamer
->emitLabel(LOHLabel
);
2574 AArch64TargetStreamer
*TS
=
2575 static_cast<AArch64TargetStreamer
*>(OutStreamer
->getTargetStreamer());
2576 // Do any manual lowerings.
2577 switch (MI
->getOpcode()) {
2579 assert(!AArch64InstrInfo::isTailCallReturnInst(*MI
) &&
2580 "Unhandled tail call instruction");
2582 case AArch64::HINT
: {
2583 // CurrentPatchableFunctionEntrySym can be CurrentFnBegin only for
2584 // -fpatchable-function-entry=N,0. The entry MBB is guaranteed to be
2585 // non-empty. If MI is the initial BTI, place the
2586 // __patchable_function_entries label after BTI.
2587 if (CurrentPatchableFunctionEntrySym
&&
2588 CurrentPatchableFunctionEntrySym
== CurrentFnBegin
&&
2589 MI
== &MF
->front().front()) {
2590 int64_t Imm
= MI
->getOperand(0).getImm();
2591 if ((Imm
& 32) && (Imm
& 6)) {
2593 MCInstLowering
.Lower(MI
, Inst
);
2594 EmitToStreamer(*OutStreamer
, Inst
);
2595 CurrentPatchableFunctionEntrySym
= createTempSymbol("patch");
2596 OutStreamer
->emitLabel(CurrentPatchableFunctionEntrySym
);
2602 case AArch64::MOVMCSym
: {
2603 Register DestReg
= MI
->getOperand(0).getReg();
2604 const MachineOperand
&MO_Sym
= MI
->getOperand(1);
2605 MachineOperand
Hi_MOSym(MO_Sym
), Lo_MOSym(MO_Sym
);
2606 MCOperand Hi_MCSym
, Lo_MCSym
;
2608 Hi_MOSym
.setTargetFlags(AArch64II::MO_G1
| AArch64II::MO_S
);
2609 Lo_MOSym
.setTargetFlags(AArch64II::MO_G0
| AArch64II::MO_NC
);
2611 MCInstLowering
.lowerOperand(Hi_MOSym
, Hi_MCSym
);
2612 MCInstLowering
.lowerOperand(Lo_MOSym
, Lo_MCSym
);
2615 MovZ
.setOpcode(AArch64::MOVZXi
);
2616 MovZ
.addOperand(MCOperand::createReg(DestReg
));
2617 MovZ
.addOperand(Hi_MCSym
);
2618 MovZ
.addOperand(MCOperand::createImm(16));
2619 EmitToStreamer(*OutStreamer
, MovZ
);
2622 MovK
.setOpcode(AArch64::MOVKXi
);
2623 MovK
.addOperand(MCOperand::createReg(DestReg
));
2624 MovK
.addOperand(MCOperand::createReg(DestReg
));
2625 MovK
.addOperand(Lo_MCSym
);
2626 MovK
.addOperand(MCOperand::createImm(0));
2627 EmitToStreamer(*OutStreamer
, MovK
);
2630 case AArch64::MOVIv2d_ns
:
2631 // It is generally beneficial to rewrite "fmov s0, wzr" to "movi d0, #0".
2632 // as movi is more efficient across all cores. Newer cores can eliminate
2633 // fmovs early and there is no difference with movi, but this not true for
2634 // all implementations.
2636 // The floating-point version doesn't quite work in rare cases on older
2637 // CPUs, so on those targets we lower this instruction to movi.16b instead.
2638 if (STI
->hasZeroCycleZeroingFPWorkaround() &&
2639 MI
->getOperand(1).getImm() == 0) {
2641 TmpInst
.setOpcode(AArch64::MOVIv16b_ns
);
2642 TmpInst
.addOperand(MCOperand::createReg(MI
->getOperand(0).getReg()));
2643 TmpInst
.addOperand(MCOperand::createImm(MI
->getOperand(1).getImm()));
2644 EmitToStreamer(*OutStreamer
, TmpInst
);
2649 case AArch64::DBG_VALUE
:
2650 case AArch64::DBG_VALUE_LIST
:
2651 if (isVerbose() && OutStreamer
->hasRawTextSupport()) {
2652 SmallString
<128> TmpStr
;
2653 raw_svector_ostream
OS(TmpStr
);
2654 PrintDebugValueComment(MI
, OS
);
2655 OutStreamer
->emitRawText(StringRef(OS
.str()));
2659 case AArch64::EMITBKEY
: {
2660 ExceptionHandling ExceptionHandlingType
= MAI
->getExceptionHandlingType();
2661 if (ExceptionHandlingType
!= ExceptionHandling::DwarfCFI
&&
2662 ExceptionHandlingType
!= ExceptionHandling::ARM
)
2665 if (getFunctionCFISectionType(*MF
) == CFISection::None
)
2668 OutStreamer
->emitCFIBKeyFrame();
2672 case AArch64::EMITMTETAGGED
: {
2673 ExceptionHandling ExceptionHandlingType
= MAI
->getExceptionHandlingType();
2674 if (ExceptionHandlingType
!= ExceptionHandling::DwarfCFI
&&
2675 ExceptionHandlingType
!= ExceptionHandling::ARM
)
2678 if (getFunctionCFISectionType(*MF
) != CFISection::None
)
2679 OutStreamer
->emitCFIMTETaggedFrame();
2684 case AArch64::AUTPAC
:
2685 emitPtrauthAuthResign(MI
);
2688 case AArch64::LOADauthptrstatic
:
2689 LowerLOADauthptrstatic(*MI
);
2692 case AArch64::LOADgotPAC
:
2693 case AArch64::MOVaddrPAC
:
2694 LowerMOVaddrPAC(*MI
);
2697 case AArch64::LOADgotAUTH
:
2698 LowerLOADgotAUTH(*MI
);
2703 emitPtrauthBranch(MI
);
2706 // Tail calls use pseudo instructions so they have the proper code-gen
2707 // attributes (isCall, isReturn, etc.). We lower them to the real
2708 // instruction here.
2709 case AArch64::AUTH_TCRETURN
:
2710 case AArch64::AUTH_TCRETURN_BTI
: {
2711 Register Callee
= MI
->getOperand(0).getReg();
2712 const uint64_t Key
= MI
->getOperand(2).getImm();
2713 assert((Key
== AArch64PACKey::IA
|| Key
== AArch64PACKey::IB
) &&
2714 "Invalid auth key for tail-call return");
2716 const uint64_t Disc
= MI
->getOperand(3).getImm();
2717 assert(isUInt
<16>(Disc
) && "Integer discriminator is too wide");
2719 Register AddrDisc
= MI
->getOperand(4).getReg();
2721 Register ScratchReg
= Callee
== AArch64::X16
? AArch64::X17
: AArch64::X16
;
2723 emitPtrauthTailCallHardening(MI
);
2725 // See the comments in emitPtrauthBranch.
2726 if (Callee
== AddrDisc
)
2727 report_fatal_error("Call target is signed with its own value");
2728 Register DiscReg
= emitPtrauthDiscriminator(Disc
, AddrDisc
, ScratchReg
,
2729 /*MayUseAddrAsScratch=*/true);
2731 const bool IsZero
= DiscReg
== AArch64::XZR
;
2732 const unsigned Opcodes
[2][2] = {{AArch64::BRAA
, AArch64::BRAAZ
},
2733 {AArch64::BRAB
, AArch64::BRABZ
}};
2736 TmpInst
.setOpcode(Opcodes
[Key
][IsZero
]);
2737 TmpInst
.addOperand(MCOperand::createReg(Callee
));
2739 TmpInst
.addOperand(MCOperand::createReg(DiscReg
));
2740 EmitToStreamer(*OutStreamer
, TmpInst
);
2744 case AArch64::TCRETURNri
:
2745 case AArch64::TCRETURNrix16x17
:
2746 case AArch64::TCRETURNrix17
:
2747 case AArch64::TCRETURNrinotx16
:
2748 case AArch64::TCRETURNriALL
: {
2749 emitPtrauthTailCallHardening(MI
);
2751 recordIfImportCall(MI
);
2753 TmpInst
.setOpcode(AArch64::BR
);
2754 TmpInst
.addOperand(MCOperand::createReg(MI
->getOperand(0).getReg()));
2755 EmitToStreamer(*OutStreamer
, TmpInst
);
2758 case AArch64::TCRETURNdi
: {
2759 emitPtrauthTailCallHardening(MI
);
2762 MCInstLowering
.lowerOperand(MI
->getOperand(0), Dest
);
2763 recordIfImportCall(MI
);
2765 TmpInst
.setOpcode(AArch64::B
);
2766 TmpInst
.addOperand(Dest
);
2767 EmitToStreamer(*OutStreamer
, TmpInst
);
2770 case AArch64::SpeculationBarrierISBDSBEndBB
: {
2771 // Print DSB SYS + ISB
2773 TmpInstDSB
.setOpcode(AArch64::DSB
);
2774 TmpInstDSB
.addOperand(MCOperand::createImm(0xf));
2775 EmitToStreamer(*OutStreamer
, TmpInstDSB
);
2777 TmpInstISB
.setOpcode(AArch64::ISB
);
2778 TmpInstISB
.addOperand(MCOperand::createImm(0xf));
2779 EmitToStreamer(*OutStreamer
, TmpInstISB
);
2782 case AArch64::SpeculationBarrierSBEndBB
: {
2785 TmpInstSB
.setOpcode(AArch64::SB
);
2786 EmitToStreamer(*OutStreamer
, TmpInstSB
);
2789 case AArch64::TLSDESC_AUTH_CALLSEQ
: {
2791 /// adrp x0, :tlsdesc_auth:var
2792 /// ldr x16, [x0, #:tlsdesc_auth_lo12:var]
2793 /// add x0, x0, #:tlsdesc_auth_lo12:var
2795 /// (TPIDR_EL0 offset now in x0)
2796 const MachineOperand
&MO_Sym
= MI
->getOperand(0);
2797 MachineOperand
MO_TLSDESC_LO12(MO_Sym
), MO_TLSDESC(MO_Sym
);
2798 MCOperand SymTLSDescLo12
, SymTLSDesc
;
2799 MO_TLSDESC_LO12
.setTargetFlags(AArch64II::MO_TLS
| AArch64II::MO_PAGEOFF
);
2800 MO_TLSDESC
.setTargetFlags(AArch64II::MO_TLS
| AArch64II::MO_PAGE
);
2801 MCInstLowering
.lowerOperand(MO_TLSDESC_LO12
, SymTLSDescLo12
);
2802 MCInstLowering
.lowerOperand(MO_TLSDESC
, SymTLSDesc
);
2805 Adrp
.setOpcode(AArch64::ADRP
);
2806 Adrp
.addOperand(MCOperand::createReg(AArch64::X0
));
2807 Adrp
.addOperand(SymTLSDesc
);
2808 EmitToStreamer(*OutStreamer
, Adrp
);
2811 Ldr
.setOpcode(AArch64::LDRXui
);
2812 Ldr
.addOperand(MCOperand::createReg(AArch64::X16
));
2813 Ldr
.addOperand(MCOperand::createReg(AArch64::X0
));
2814 Ldr
.addOperand(SymTLSDescLo12
);
2815 Ldr
.addOperand(MCOperand::createImm(0));
2816 EmitToStreamer(*OutStreamer
, Ldr
);
2819 Add
.setOpcode(AArch64::ADDXri
);
2820 Add
.addOperand(MCOperand::createReg(AArch64::X0
));
2821 Add
.addOperand(MCOperand::createReg(AArch64::X0
));
2822 Add
.addOperand(SymTLSDescLo12
);
2823 Add
.addOperand(MCOperand::createImm(AArch64_AM::getShiftValue(0)));
2824 EmitToStreamer(*OutStreamer
, Add
);
2826 // Authenticated TLSDESC accesses are not relaxed.
2827 // Thus, do not emit .tlsdesccall for AUTH TLSDESC.
2830 Blraa
.setOpcode(AArch64::BLRAA
);
2831 Blraa
.addOperand(MCOperand::createReg(AArch64::X16
));
2832 Blraa
.addOperand(MCOperand::createReg(AArch64::X0
));
2833 EmitToStreamer(*OutStreamer
, Blraa
);
2837 case AArch64::TLSDESC_CALLSEQ
: {
2839 /// adrp x0, :tlsdesc:var
2840 /// ldr x1, [x0, #:tlsdesc_lo12:var]
2841 /// add x0, x0, #:tlsdesc_lo12:var
2842 /// .tlsdesccall var
2844 /// (TPIDR_EL0 offset now in x0)
2845 const MachineOperand
&MO_Sym
= MI
->getOperand(0);
2846 MachineOperand
MO_TLSDESC_LO12(MO_Sym
), MO_TLSDESC(MO_Sym
);
2847 MCOperand Sym
, SymTLSDescLo12
, SymTLSDesc
;
2848 MO_TLSDESC_LO12
.setTargetFlags(AArch64II::MO_TLS
| AArch64II::MO_PAGEOFF
);
2849 MO_TLSDESC
.setTargetFlags(AArch64II::MO_TLS
| AArch64II::MO_PAGE
);
2850 MCInstLowering
.lowerOperand(MO_Sym
, Sym
);
2851 MCInstLowering
.lowerOperand(MO_TLSDESC_LO12
, SymTLSDescLo12
);
2852 MCInstLowering
.lowerOperand(MO_TLSDESC
, SymTLSDesc
);
2855 Adrp
.setOpcode(AArch64::ADRP
);
2856 Adrp
.addOperand(MCOperand::createReg(AArch64::X0
));
2857 Adrp
.addOperand(SymTLSDesc
);
2858 EmitToStreamer(*OutStreamer
, Adrp
);
2861 if (STI
->isTargetILP32()) {
2862 Ldr
.setOpcode(AArch64::LDRWui
);
2863 Ldr
.addOperand(MCOperand::createReg(AArch64::W1
));
2865 Ldr
.setOpcode(AArch64::LDRXui
);
2866 Ldr
.addOperand(MCOperand::createReg(AArch64::X1
));
2868 Ldr
.addOperand(MCOperand::createReg(AArch64::X0
));
2869 Ldr
.addOperand(SymTLSDescLo12
);
2870 Ldr
.addOperand(MCOperand::createImm(0));
2871 EmitToStreamer(*OutStreamer
, Ldr
);
2874 if (STI
->isTargetILP32()) {
2875 Add
.setOpcode(AArch64::ADDWri
);
2876 Add
.addOperand(MCOperand::createReg(AArch64::W0
));
2877 Add
.addOperand(MCOperand::createReg(AArch64::W0
));
2879 Add
.setOpcode(AArch64::ADDXri
);
2880 Add
.addOperand(MCOperand::createReg(AArch64::X0
));
2881 Add
.addOperand(MCOperand::createReg(AArch64::X0
));
2883 Add
.addOperand(SymTLSDescLo12
);
2884 Add
.addOperand(MCOperand::createImm(AArch64_AM::getShiftValue(0)));
2885 EmitToStreamer(*OutStreamer
, Add
);
2887 // Emit a relocation-annotation. This expands to no code, but requests
2888 // the following instruction gets an R_AARCH64_TLSDESC_CALL.
2890 TLSDescCall
.setOpcode(AArch64::TLSDESCCALL
);
2891 TLSDescCall
.addOperand(Sym
);
2892 EmitToStreamer(*OutStreamer
, TLSDescCall
);
2894 --InstsEmitted
; // no code emitted
2898 Blr
.setOpcode(AArch64::BLR
);
2899 Blr
.addOperand(MCOperand::createReg(AArch64::X1
));
2900 EmitToStreamer(*OutStreamer
, Blr
);
2905 case AArch64::JumpTableDest32
:
2906 case AArch64::JumpTableDest16
:
2907 case AArch64::JumpTableDest8
:
2908 LowerJumpTableDest(*OutStreamer
, *MI
);
2911 case AArch64::BR_JumpTable
:
2912 LowerHardenedBRJumpTable(*MI
);
2915 case AArch64::FMOVH0
:
2916 case AArch64::FMOVS0
:
2917 case AArch64::FMOVD0
:
2921 case AArch64::MOPSMemoryCopyPseudo
:
2922 case AArch64::MOPSMemoryMovePseudo
:
2923 case AArch64::MOPSMemorySetPseudo
:
2924 case AArch64::MOPSMemorySetTaggingPseudo
:
2925 LowerMOPS(*OutStreamer
, *MI
);
2928 case TargetOpcode::STACKMAP
:
2929 return LowerSTACKMAP(*OutStreamer
, SM
, *MI
);
2931 case TargetOpcode::PATCHPOINT
:
2932 return LowerPATCHPOINT(*OutStreamer
, SM
, *MI
);
2934 case TargetOpcode::STATEPOINT
:
2935 return LowerSTATEPOINT(*OutStreamer
, SM
, *MI
);
2937 case TargetOpcode::FAULTING_OP
:
2938 return LowerFAULTING_OP(*MI
);
2940 case TargetOpcode::PATCHABLE_FUNCTION_ENTER
:
2941 LowerPATCHABLE_FUNCTION_ENTER(*MI
);
2944 case TargetOpcode::PATCHABLE_FUNCTION_EXIT
:
2945 LowerPATCHABLE_FUNCTION_EXIT(*MI
);
2948 case TargetOpcode::PATCHABLE_TAIL_CALL
:
2949 LowerPATCHABLE_TAIL_CALL(*MI
);
2951 case TargetOpcode::PATCHABLE_EVENT_CALL
:
2952 return LowerPATCHABLE_EVENT_CALL(*MI
, false);
2953 case TargetOpcode::PATCHABLE_TYPED_EVENT_CALL
:
2954 return LowerPATCHABLE_EVENT_CALL(*MI
, true);
2956 case AArch64::KCFI_CHECK
:
2957 LowerKCFI_CHECK(*MI
);
2960 case AArch64::HWASAN_CHECK_MEMACCESS
:
2961 case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES
:
2962 case AArch64::HWASAN_CHECK_MEMACCESS_FIXEDSHADOW
:
2963 case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW
:
2964 LowerHWASAN_CHECK_MEMACCESS(*MI
);
2967 case AArch64::SEH_StackAlloc
:
2968 TS
->emitARM64WinCFIAllocStack(MI
->getOperand(0).getImm());
2971 case AArch64::SEH_SaveFPLR
:
2972 TS
->emitARM64WinCFISaveFPLR(MI
->getOperand(0).getImm());
2975 case AArch64::SEH_SaveFPLR_X
:
2976 assert(MI
->getOperand(0).getImm() < 0 &&
2977 "Pre increment SEH opcode must have a negative offset");
2978 TS
->emitARM64WinCFISaveFPLRX(-MI
->getOperand(0).getImm());
2981 case AArch64::SEH_SaveReg
:
2982 TS
->emitARM64WinCFISaveReg(MI
->getOperand(0).getImm(),
2983 MI
->getOperand(1).getImm());
2986 case AArch64::SEH_SaveReg_X
:
2987 assert(MI
->getOperand(1).getImm() < 0 &&
2988 "Pre increment SEH opcode must have a negative offset");
2989 TS
->emitARM64WinCFISaveRegX(MI
->getOperand(0).getImm(),
2990 -MI
->getOperand(1).getImm());
2993 case AArch64::SEH_SaveRegP
:
2994 if (MI
->getOperand(1).getImm() == 30 && MI
->getOperand(0).getImm() >= 19 &&
2995 MI
->getOperand(0).getImm() <= 28) {
2996 assert((MI
->getOperand(0).getImm() - 19) % 2 == 0 &&
2997 "Register paired with LR must be odd");
2998 TS
->emitARM64WinCFISaveLRPair(MI
->getOperand(0).getImm(),
2999 MI
->getOperand(2).getImm());
3002 assert((MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1) &&
3003 "Non-consecutive registers not allowed for save_regp");
3004 TS
->emitARM64WinCFISaveRegP(MI
->getOperand(0).getImm(),
3005 MI
->getOperand(2).getImm());
3008 case AArch64::SEH_SaveRegP_X
:
3009 assert((MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1) &&
3010 "Non-consecutive registers not allowed for save_regp_x");
3011 assert(MI
->getOperand(2).getImm() < 0 &&
3012 "Pre increment SEH opcode must have a negative offset");
3013 TS
->emitARM64WinCFISaveRegPX(MI
->getOperand(0).getImm(),
3014 -MI
->getOperand(2).getImm());
3017 case AArch64::SEH_SaveFReg
:
3018 TS
->emitARM64WinCFISaveFReg(MI
->getOperand(0).getImm(),
3019 MI
->getOperand(1).getImm());
3022 case AArch64::SEH_SaveFReg_X
:
3023 assert(MI
->getOperand(1).getImm() < 0 &&
3024 "Pre increment SEH opcode must have a negative offset");
3025 TS
->emitARM64WinCFISaveFRegX(MI
->getOperand(0).getImm(),
3026 -MI
->getOperand(1).getImm());
3029 case AArch64::SEH_SaveFRegP
:
3030 assert((MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1) &&
3031 "Non-consecutive registers not allowed for save_regp");
3032 TS
->emitARM64WinCFISaveFRegP(MI
->getOperand(0).getImm(),
3033 MI
->getOperand(2).getImm());
3036 case AArch64::SEH_SaveFRegP_X
:
3037 assert((MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1) &&
3038 "Non-consecutive registers not allowed for save_regp_x");
3039 assert(MI
->getOperand(2).getImm() < 0 &&
3040 "Pre increment SEH opcode must have a negative offset");
3041 TS
->emitARM64WinCFISaveFRegPX(MI
->getOperand(0).getImm(),
3042 -MI
->getOperand(2).getImm());
3045 case AArch64::SEH_SetFP
:
3046 TS
->emitARM64WinCFISetFP();
3049 case AArch64::SEH_AddFP
:
3050 TS
->emitARM64WinCFIAddFP(MI
->getOperand(0).getImm());
3053 case AArch64::SEH_Nop
:
3054 TS
->emitARM64WinCFINop();
3057 case AArch64::SEH_PrologEnd
:
3058 TS
->emitARM64WinCFIPrologEnd();
3061 case AArch64::SEH_EpilogStart
:
3062 TS
->emitARM64WinCFIEpilogStart();
3065 case AArch64::SEH_EpilogEnd
:
3066 TS
->emitARM64WinCFIEpilogEnd();
3069 case AArch64::SEH_PACSignLR
:
3070 TS
->emitARM64WinCFIPACSignLR();
3073 case AArch64::SEH_SaveAnyRegQP
:
3074 assert(MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1 &&
3075 "Non-consecutive registers not allowed for save_any_reg");
3076 assert(MI
->getOperand(2).getImm() >= 0 &&
3077 "SaveAnyRegQP SEH opcode offset must be non-negative");
3078 assert(MI
->getOperand(2).getImm() <= 1008 &&
3079 "SaveAnyRegQP SEH opcode offset must fit into 6 bits");
3080 TS
->emitARM64WinCFISaveAnyRegQP(MI
->getOperand(0).getImm(),
3081 MI
->getOperand(2).getImm());
3084 case AArch64::SEH_SaveAnyRegQPX
:
3085 assert(MI
->getOperand(1).getImm() - MI
->getOperand(0).getImm() == 1 &&
3086 "Non-consecutive registers not allowed for save_any_reg");
3087 assert(MI
->getOperand(2).getImm() < 0 &&
3088 "SaveAnyRegQPX SEH opcode offset must be negative");
3089 assert(MI
->getOperand(2).getImm() >= -1008 &&
3090 "SaveAnyRegQPX SEH opcode offset must fit into 6 bits");
3091 TS
->emitARM64WinCFISaveAnyRegQPX(MI
->getOperand(0).getImm(),
3092 -MI
->getOperand(2).getImm());
3097 recordIfImportCall(MI
);
3099 MCInstLowering
.Lower(MI
, TmpInst
);
3100 EmitToStreamer(*OutStreamer
, TmpInst
);
3104 // Finally, do the automated lowerings for everything else.
3106 MCInstLowering
.Lower(MI
, TmpInst
);
3107 EmitToStreamer(*OutStreamer
, TmpInst
);
3110 void AArch64AsmPrinter::recordIfImportCall(
3111 const llvm::MachineInstr
*BranchInst
) {
3112 if (!EnableImportCallOptimization
||
3113 !TM
.getTargetTriple().isOSBinFormatCOFF())
3116 auto [GV
, OpFlags
] = BranchInst
->getMF()->tryGetCalledGlobal(BranchInst
);
3117 if (GV
&& GV
->hasDLLImportStorageClass()) {
3118 auto *CallSiteSymbol
= MMI
->getContext().createNamedTempSymbol("impcall");
3119 OutStreamer
->emitLabel(CallSiteSymbol
);
3121 auto *CalledSymbol
= MCInstLowering
.GetGlobalValueSymbol(GV
, OpFlags
);
3122 SectionToImportedFunctionCalls
[OutStreamer
->getCurrentSectionOnly()]
3123 .push_back({CallSiteSymbol
, CalledSymbol
});
3127 void AArch64AsmPrinter::emitMachOIFuncStubBody(Module
&M
, const GlobalIFunc
&GI
,
3128 MCSymbol
*LazyPointer
) {
3130 // adrp x16, lazy_pointer@GOTPAGE
3131 // ldr x16, [x16, lazy_pointer@GOTPAGEOFF]
3137 Adrp
.setOpcode(AArch64::ADRP
);
3138 Adrp
.addOperand(MCOperand::createReg(AArch64::X16
));
3140 MCInstLowering
.lowerOperand(
3141 MachineOperand::CreateMCSymbol(LazyPointer
,
3142 AArch64II::MO_GOT
| AArch64II::MO_PAGE
),
3144 Adrp
.addOperand(SymPage
);
3145 EmitToStreamer(Adrp
);
3150 Ldr
.setOpcode(AArch64::LDRXui
);
3151 Ldr
.addOperand(MCOperand::createReg(AArch64::X16
));
3152 Ldr
.addOperand(MCOperand::createReg(AArch64::X16
));
3153 MCOperand SymPageOff
;
3154 MCInstLowering
.lowerOperand(
3155 MachineOperand::CreateMCSymbol(LazyPointer
, AArch64II::MO_GOT
|
3156 AArch64II::MO_PAGEOFF
),
3158 Ldr
.addOperand(SymPageOff
);
3159 Ldr
.addOperand(MCOperand::createImm(0));
3160 EmitToStreamer(Ldr
);
3163 EmitToStreamer(MCInstBuilder(AArch64::LDRXui
)
3164 .addReg(AArch64::X16
)
3165 .addReg(AArch64::X16
)
3168 EmitToStreamer(MCInstBuilder(TM
.getTargetTriple().isArm64e() ? AArch64::BRAAZ
3170 .addReg(AArch64::X16
));
3173 void AArch64AsmPrinter::emitMachOIFuncStubHelperBody(Module
&M
,
3174 const GlobalIFunc
&GI
,
3175 MCSymbol
*LazyPointer
) {
3176 // These stub helpers are only ever called once, so here we're optimizing for
3177 // minimum size by using the pre-indexed store variants, which saves a few
3178 // bytes of instructions to bump & restore sp.
3180 // _ifunc.stub_helper:
3181 // stp fp, lr, [sp, #-16]!
3183 // stp x1, x0, [sp, #-16]!
3184 // stp x3, x2, [sp, #-16]!
3185 // stp x5, x4, [sp, #-16]!
3186 // stp x7, x6, [sp, #-16]!
3187 // stp d1, d0, [sp, #-16]!
3188 // stp d3, d2, [sp, #-16]!
3189 // stp d5, d4, [sp, #-16]!
3190 // stp d7, d6, [sp, #-16]!
3192 // adrp x16, lazy_pointer@GOTPAGE
3193 // ldr x16, [x16, lazy_pointer@GOTPAGEOFF]
3196 // ldp d7, d6, [sp], #16
3197 // ldp d5, d4, [sp], #16
3198 // ldp d3, d2, [sp], #16
3199 // ldp d1, d0, [sp], #16
3200 // ldp x7, x6, [sp], #16
3201 // ldp x5, x4, [sp], #16
3202 // ldp x3, x2, [sp], #16
3203 // ldp x1, x0, [sp], #16
3204 // ldp fp, lr, [sp], #16
3207 EmitToStreamer(MCInstBuilder(AArch64::STPXpre
)
3208 .addReg(AArch64::SP
)
3209 .addReg(AArch64::FP
)
3210 .addReg(AArch64::LR
)
3211 .addReg(AArch64::SP
)
3214 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
3215 .addReg(AArch64::FP
)
3216 .addReg(AArch64::SP
)
3220 for (int I
= 0; I
!= 4; ++I
)
3221 EmitToStreamer(MCInstBuilder(AArch64::STPXpre
)
3222 .addReg(AArch64::SP
)
3223 .addReg(AArch64::X1
+ 2 * I
)
3224 .addReg(AArch64::X0
+ 2 * I
)
3225 .addReg(AArch64::SP
)
3228 for (int I
= 0; I
!= 4; ++I
)
3229 EmitToStreamer(MCInstBuilder(AArch64::STPDpre
)
3230 .addReg(AArch64::SP
)
3231 .addReg(AArch64::D1
+ 2 * I
)
3232 .addReg(AArch64::D0
+ 2 * I
)
3233 .addReg(AArch64::SP
)
3237 MCInstBuilder(AArch64::BL
)
3238 .addOperand(MCOperand::createExpr(lowerConstant(GI
.getResolver()))));
3242 Adrp
.setOpcode(AArch64::ADRP
);
3243 Adrp
.addOperand(MCOperand::createReg(AArch64::X16
));
3245 MCInstLowering
.lowerOperand(
3246 MachineOperand::CreateES(LazyPointer
->getName().data() + 1,
3247 AArch64II::MO_GOT
| AArch64II::MO_PAGE
),
3249 Adrp
.addOperand(SymPage
);
3250 EmitToStreamer(Adrp
);
3255 Ldr
.setOpcode(AArch64::LDRXui
);
3256 Ldr
.addOperand(MCOperand::createReg(AArch64::X16
));
3257 Ldr
.addOperand(MCOperand::createReg(AArch64::X16
));
3258 MCOperand SymPageOff
;
3259 MCInstLowering
.lowerOperand(
3260 MachineOperand::CreateES(LazyPointer
->getName().data() + 1,
3261 AArch64II::MO_GOT
| AArch64II::MO_PAGEOFF
),
3263 Ldr
.addOperand(SymPageOff
);
3264 Ldr
.addOperand(MCOperand::createImm(0));
3265 EmitToStreamer(Ldr
);
3268 EmitToStreamer(MCInstBuilder(AArch64::STRXui
)
3269 .addReg(AArch64::X0
)
3270 .addReg(AArch64::X16
)
3273 EmitToStreamer(MCInstBuilder(AArch64::ADDXri
)
3274 .addReg(AArch64::X16
)
3275 .addReg(AArch64::X0
)
3279 for (int I
= 3; I
!= -1; --I
)
3280 EmitToStreamer(MCInstBuilder(AArch64::LDPDpost
)
3281 .addReg(AArch64::SP
)
3282 .addReg(AArch64::D1
+ 2 * I
)
3283 .addReg(AArch64::D0
+ 2 * I
)
3284 .addReg(AArch64::SP
)
3287 for (int I
= 3; I
!= -1; --I
)
3288 EmitToStreamer(MCInstBuilder(AArch64::LDPXpost
)
3289 .addReg(AArch64::SP
)
3290 .addReg(AArch64::X1
+ 2 * I
)
3291 .addReg(AArch64::X0
+ 2 * I
)
3292 .addReg(AArch64::SP
)
3295 EmitToStreamer(MCInstBuilder(AArch64::LDPXpost
)
3296 .addReg(AArch64::SP
)
3297 .addReg(AArch64::FP
)
3298 .addReg(AArch64::LR
)
3299 .addReg(AArch64::SP
)
3302 EmitToStreamer(MCInstBuilder(TM
.getTargetTriple().isArm64e() ? AArch64::BRAAZ
3304 .addReg(AArch64::X16
));
3307 const MCExpr
*AArch64AsmPrinter::lowerConstant(const Constant
*CV
) {
3308 if (const GlobalValue
*GV
= dyn_cast
<GlobalValue
>(CV
)) {
3309 return MCSymbolRefExpr::create(MCInstLowering
.GetGlobalValueSymbol(GV
, 0),
3313 return AsmPrinter::lowerConstant(CV
);
3316 // Force static initialization.
3317 extern "C" LLVM_EXTERNAL_VISIBILITY
void LLVMInitializeAArch64AsmPrinter() {
3318 RegisterAsmPrinter
<AArch64AsmPrinter
> X(getTheAArch64leTarget());
3319 RegisterAsmPrinter
<AArch64AsmPrinter
> Y(getTheAArch64beTarget());
3320 RegisterAsmPrinter
<AArch64AsmPrinter
> Z(getTheARM64Target());
3321 RegisterAsmPrinter
<AArch64AsmPrinter
> W(getTheARM64_32Target());
3322 RegisterAsmPrinter
<AArch64AsmPrinter
> V(getTheAArch64_32Target());