LLVM 24.0.0git
AArch64AsmPrinter.cpp
Go to the documentation of this file.
1//===- AArch64AsmPrinter.cpp - AArch64 LLVM assembly writer ---------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains a printer that converts from our internal representation
10// of machine-dependent LLVM code to the AArch64 assembly language.
11//
12//===----------------------------------------------------------------------===//
13
14#include "AArch64AsmPrinter.h"
15#include "AArch64.h"
16#include "AArch64MCInstLower.h"
18#include "AArch64RegisterInfo.h"
19#include "AArch64Subtarget.h"
28#include "llvm/ADT/ScopeExit.h"
31#include "llvm/ADT/Statistic.h"
32#include "llvm/ADT/StringRef.h"
33#include "llvm/ADT/Twine.h"
48#include "llvm/IR/Analysis.h"
49#include "llvm/IR/DataLayout.h"
51#include "llvm/IR/Mangler.h"
52#include "llvm/IR/Module.h"
53#include "llvm/IR/PassManager.h"
54#include "llvm/MC/MCAsmInfo.h"
55#include "llvm/MC/MCContext.h"
56#include "llvm/MC/MCExpr.h"
57#include "llvm/MC/MCInst.h"
61#include "llvm/MC/MCStreamer.h"
62#include "llvm/MC/MCSymbol.h"
63#include "llvm/MC/MCValue.h"
73#include <cassert>
74#include <cstdint>
75#include <map>
76#include <memory>
77
78using namespace llvm;
79
80#define DEBUG_TYPE "AArch64AsmPrinter"
81
82// Doesn't count FPR128 ZCZ instructions which are handled
83// by TableGen pattern matching
84STATISTIC(NumZCZeroingInstrsFPR,
85 "Number of zero-cycle FPR zeroing instructions expanded from "
86 "canonical pseudo instructions");
87
90 "aarch64-ptrauth-auth-checks", cl::Hidden,
91 cl::values(clEnumValN(Unchecked, "none", "don't test for failure"),
92 clEnumValN(Poison, "poison", "poison on failure"),
93 clEnumValN(Trap, "trap", "trap on failure")),
94 cl::desc("Check pointer authentication auth/resign failures"));
95
96namespace {
97
98class AArch64AsmPrinter : public AsmPrinter {
99 AArch64MCInstLower MCInstLowering;
100 FaultMaps FM;
101 const AArch64Subtarget *STI;
102 bool ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = false;
103 bool PtrauthInitFini = false;
104 bool PtrauthInitFiniAddressDisc = false;
105#ifndef NDEBUG
106 unsigned InstsEmitted;
107#endif
108 bool EnableImportCallOptimization = false;
110 SectionToImportedFunctionCalls;
111 unsigned PAuthIFuncNextUniqueID = 1;
112
113public:
114 static char ID;
115
116 AArch64AsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
117 : AsmPrinter(TM, std::move(Streamer), ID),
118 MCInstLowering(OutContext, *this), FM(*this) {}
119
120 StringRef getPassName() const override { return "AArch64 Assembly Printer"; }
121
122 /// Wrapper for MCInstLowering.lowerOperand() for the
123 /// tblgen'erated pseudo lowering.
124 bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const {
125 return MCInstLowering.lowerOperand(MO, MCOp);
126 }
127
128 const MCExpr *lowerConstantPtrAuth(const ConstantPtrAuth &CPA) override;
129
130 const MCExpr *lowerBlockAddressConstant(const BlockAddress &BA) override;
131
132 void emitStartOfAsmFile(Module &M) override;
133 void emitJumpTableImpl(const MachineJumpTableInfo &MJTI,
134 ArrayRef<unsigned> JumpTableIndices) override;
135 std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
137 getCodeViewJumpTableInfo(int JTI, const MachineInstr *BranchInstr,
138 const MCSymbol *BranchLabel) const override;
139
140 void emitFunctionEntryLabel() override;
141
142 void emitXXStructor(const DataLayout &DL, const Constant *CV) override;
143
144 void LowerJumpTableDest(MCStreamer &OutStreamer, const MachineInstr &MI);
145
146 void LowerHardenedBRJumpTable(const MachineInstr &MI);
147
148 void LowerMOPS(MCStreamer &OutStreamer, const MachineInstr &MI);
149
150 void LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
151 const MachineInstr &MI);
152 void LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
153 const MachineInstr &MI);
154 void LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
155 const MachineInstr &MI);
156 void LowerFAULTING_OP(const MachineInstr &MI);
157
158 void LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI);
159 void LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI);
160 void LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI);
161 void LowerPATCHABLE_EVENT_CALL(const MachineInstr &MI, bool Typed);
162
163 typedef std::tuple<unsigned, bool, uint32_t, bool, uint64_t>
164 HwasanMemaccessTuple;
165 std::map<HwasanMemaccessTuple, MCSymbol *> HwasanMemaccessSymbols;
166 void LowerKCFI_CHECK(const MachineInstr &MI);
167 void LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI);
168 void emitHwasanMemaccessSymbols(Module &M);
169
170 void emitSled(const MachineInstr &MI, SledKind Kind);
171
172 // Returns whether Reg may be used to store sensitive temporary values when
173 // expanding PtrAuth pseudos. Some OSes may take extra care to protect a
174 // small subset of GPRs on context switches - use these registers then.
175 //
176 // If there are no preferred registers, returns true for any Reg.
177 bool isPtrauthRegSafe(Register Reg) const {
178 if (STI->isX16X17Safer())
179 return Reg == AArch64::X16 || Reg == AArch64::X17;
180
181 return true;
182 }
183
184 // Emit the sequence for BRA/BLRA (authenticate + branch/call).
185 void emitPtrauthBranch(const MachineInstr *MI);
186
187 void emitPtrauthCheckAuthenticatedValue(Register TestedReg,
188 Register ScratchReg,
191 const MCSymbol *OnFailure = nullptr);
192
193 // Check authenticated LR before tail calling.
194 void emitPtrauthTailCallHardening(const MachineInstr *TC);
195
196 struct PtrAuthSchema {
197 static PtrAuthSchema CreateImmReg(AArch64PACKey::ID Key, uint64_t IntDisc,
198 const MachineOperand &AddrDiscOp);
199 static PtrAuthSchema CreateRegReg(AArch64PACKey::ID Key, Register AddrDisc,
200 Register PCDisc);
201
203 uint64_t IntDisc;
204 Register AddrDisc;
205 bool AddrDiscIsKilled;
206 Register PCDisc;
207
208 bool addrDiscIsKilledAndNoneOf(std::initializer_list<Register> Regs) {
209 return AddrDiscIsKilled && !llvm::is_contained(Regs, AddrDisc);
210 }
211 };
212
213 // Helper for emitting AUTRELLOADPAC: increment Pointer by Addend and then by
214 // a 32-bit signed value loaded from memory. The instructions emitted are
215 //
216 // ldrsw Scratch, [Pointer, #Addend]!
217 // add Pointer, Pointer, Scratch
218 //
219 // for small Addend value, with longer sequences required for wider Addend.
220 void emitPtrauthApplyIndirectAddend(Register Pointer, Register Scratch,
221 int64_t Addend);
222
223 // Emit the sequence for AUT or AUTPAC (or their PC-blending variants).
224 // Addend is only used for AUTRELLOADPAC.
225 void emitPtrauthAuthResign(Register Pointer, Register Scratch,
226 PtrAuthSchema AuthSchema,
227 std::optional<PtrAuthSchema> SignSchema,
228 std::optional<int64_t> Addend, Value *DS);
229
230 // Emit R_AARCH64_PATCHINST, the deactivation symbol relocation. Returns true
231 // if no instruction should be emitted because the deactivation symbol is
232 // defined in the current module so this function emitted a NOP instead.
233 bool emitDeactivationSymbolRelocation(Value *DS);
234
235 // Emit the sequence for PAC.
236 void emitPtrauthSign(const MachineInstr *MI);
237
238 // Emit the sequence to compute the discriminator.
239 //
240 // The Scratch register passed to this function must be safe, as returned by
241 // isPtrauthRegSafe(ScratchReg).
242 //
243 // The returned register is either ScratchReg, AddrDisc, or XZR. Furthermore,
244 // it is guaranteed to be safe (or XZR), with the only exception of
245 // passing-through an *unmodified* unsafe AddrDisc register.
246 //
247 // If the expanded pseudo is allowed to clobber AddrDisc register, setting
248 // MayClobberAddrDisc may save one MOV instruction, provided
249 // isPtrauthRegSafe(AddrDisc) is true:
250 //
251 // mov x17, x16
252 // movk x17, #1234, lsl #48
253 // ; x16 is not used anymore
254 //
255 // can be replaced by
256 //
257 // movk x16, #1234, lsl #48
258 Register emitPtrauthDiscriminator(uint64_t Disc, Register AddrDisc,
259 Register ScratchReg,
260 bool MayClobberAddrDisc = false);
261
262 // Emit the sequence for LOADauthptrstatic
263 void LowerLOADauthptrstatic(const MachineInstr &MI);
264
265 // Emit the sequence for LOADgotPAC/MOVaddrPAC (either GOT adrp-ldr or
266 // adrp-add followed by PAC sign)
267 void LowerMOVaddrPAC(const MachineInstr &MI);
268
269 // Emit the sequence for LOADgotAUTH (load signed pointer from signed ELF GOT
270 // and authenticate it with, if FPAC bit is not set, check+trap sequence after
271 // authenticating)
272 void LowerLOADgotAUTH(const MachineInstr &MI);
273
274 void emitAddImm(MCRegister Val, int64_t Addend, MCRegister Tmp);
275 void emitAddress(MCRegister Reg, const MCExpr *Expr, MCRegister Tmp,
276 bool DSOLocal, const MCSubtargetInfo &STI);
277
278 const MCExpr *emitPAuthRelocationAsIRelative(
279 const MCExpr *Target, uint64_t Disc, AArch64PACKey::ID KeyID,
280 bool HasAddressDiversity, bool IsDSOLocal, const MCExpr *DSExpr);
281
282 /// tblgen'erated driver function for lowering simple MI->MC
283 /// pseudo instructions.
284 bool lowerPseudoInstExpansion(const MachineInstr *MI, MCInst &Inst);
285
286 // Emit Build Attributes
287 void emitAttributes(unsigned Flags, uint64_t PAuthABIPlatform,
288 uint64_t PAuthABIVersion, AArch64TargetStreamer *TS);
289
290 // Emit expansion of Compare-and-branch pseudo instructions
291 void emitCBPseudoExpansion(const MachineInstr *MI);
292
293 void EmitToStreamer(MCStreamer &S, const MCInst &Inst);
294 void EmitToStreamer(const MCInst &Inst) {
295 EmitToStreamer(*OutStreamer, Inst);
296 }
297
298 void emitInstruction(const MachineInstr *MI) override;
299
300 void emitFunctionHeaderComment() override;
301
302 void getAnalysisUsage(AnalysisUsage &AU) const override {
304 AU.setPreservesAll();
305 }
306
307 bool runOnMachineFunction(MachineFunction &MF) override {
308 if (auto *PSIW = getAnalysisIfAvailable<ProfileSummaryInfoWrapperPass>())
309 PSI = &PSIW->getPSI();
310 if (auto *SDPIW =
311 getAnalysisIfAvailable<StaticDataProfileInfoWrapperPass>())
312 SDPI = &SDPIW->getStaticDataProfileInfo();
313
314 AArch64FI = MF.getInfo<AArch64FunctionInfo>();
315 STI = &MF.getSubtarget<AArch64Subtarget>();
316
317 SetupMachineFunction(MF);
318
319 if (STI->isTargetCOFF()) {
320 bool Local = MF.getFunction().hasLocalLinkage();
323 int Type =
325
326 OutStreamer->beginCOFFSymbolDef(CurrentFnSym);
327 OutStreamer->emitCOFFSymbolStorageClass(Scl);
328 OutStreamer->emitCOFFSymbolType(Type);
329 OutStreamer->endCOFFSymbolDef();
330 }
331
332 // Emit the rest of the function body.
333 emitFunctionBody();
334
335 // Emit the XRay table for this function.
336 emitXRayTable();
337
338 // We didn't modify anything.
339 return false;
340 }
341
342 const MCExpr *lowerConstant(const Constant *CV,
343 const Constant *BaseCV = nullptr,
344 uint64_t Offset = 0) override;
345
346private:
347 void printOperand(const MachineInstr *MI, unsigned OpNum, raw_ostream &O);
348 bool printAsmMRegister(const MachineOperand &MO, char Mode, raw_ostream &O);
349 bool printAsmRegInClass(const MachineOperand &MO,
350 const TargetRegisterClass *RC, unsigned AltName,
351 raw_ostream &O);
352
353 bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
354 const char *ExtraCode, raw_ostream &O) override;
355 bool PrintAsmMemoryOperand(const MachineInstr *MI, unsigned OpNum,
356 const char *ExtraCode, raw_ostream &O) override;
357
358 void PrintDebugValueComment(const MachineInstr *MI, raw_ostream &OS);
359
360 void emitFunctionBodyEnd() override;
361 void emitGlobalAlias(const Module &M, const GlobalAlias &GA) override;
362
363 MCSymbol *GetCPISymbol(unsigned CPID) const override;
364 void emitEndOfAsmFile(Module &M) override;
365
366 AArch64FunctionInfo *AArch64FI = nullptr;
367
368 /// Emit the LOHs contained in AArch64FI.
369 void emitLOHs();
370
371 void emitMovXReg(Register Dest, Register Src);
372 void emitMOVZ(Register Dest, uint64_t Imm, unsigned Shift);
373 void emitMOVK(Register Dest, uint64_t Imm, unsigned Shift);
374
375 void emitAUT(AArch64PACKey::ID Key, Register Pointer, Register Disc);
376 void emitPAC(AArch64PACKey::ID Key, Register Pointer, Register Disc);
377 void emitBLRA(bool IsCall, AArch64PACKey::ID Key, Register Target,
378 Register Disc);
379
380 /// Emit instruction to set float register to zero.
381 void emitFMov0(const MachineInstr &MI);
382 void emitFMov0AsFMov(const MachineInstr &MI, Register DestReg);
383
384 using MInstToMCSymbol = std::map<const MachineInstr *, MCSymbol *>;
385
386 MInstToMCSymbol LOHInstToLabel;
387
388 bool shouldEmitWeakSwiftAsyncExtendedFramePointerFlags() const override {
389 return ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags;
390 }
391
392 const MCSubtargetInfo *getIFuncMCSubtargetInfo() const override {
393 assert(STI);
394 return STI;
395 }
396 void emitMachOIFuncStubBody(Module &M, const GlobalIFunc &GI,
397 MCSymbol *LazyPointer) override;
398 void emitMachOIFuncStubHelperBody(Module &M, const GlobalIFunc &GI,
399 MCSymbol *LazyPointer) override;
400
401 /// Checks if this instruction is part of a sequence that is eligle for import
402 /// call optimization and, if so, records it to be emitted in the import call
403 /// section.
404 void recordIfImportCall(const MachineInstr *BranchInst);
405};
406
407} // end anonymous namespace
408
409// Get boolean module flag (0 or 1), treating absent flag as having value 0.
411 Metadata *Flag = M.getModuleFlag(Name);
412 if (!Flag)
413 return false;
414
415 uint64_t Value = mdconst::extract<ConstantInt>(Flag)->getZExtValue();
416 assert((Value == 0 || Value == 1) && "Boolean flag is expected, if present");
417 return Value;
418}
419
420void AArch64AsmPrinter::emitStartOfAsmFile(Module &M) {
421 const Triple &TT = TM.getTargetTriple();
422
423 if (TT.isOSBinFormatCOFF()) {
424 emitCOFFFeatureSymbol(M);
425 emitCOFFReplaceableFunctionData(M);
426
427 if (M.getModuleFlag("import-call-optimization"))
428 EnableImportCallOptimization = true;
429 }
430
431 PtrauthInitFini = getOptionalBooleanModuleFlag(M, "ptrauth-init-fini");
432 PtrauthInitFiniAddressDisc = getOptionalBooleanModuleFlag(
433 M, "ptrauth-init-fini-address-discrimination");
434
435 if (!TT.isOSBinFormatELF())
436 return;
437
438 // For emitting build attributes and .note.gnu.property section
439 auto *TS =
440 static_cast<AArch64TargetStreamer *>(OutStreamer->getTargetStreamer());
441 // Assemble feature flags that may require creation of build attributes and a
442 // note section.
443 unsigned BAFlags = 0;
444 unsigned GNUFlags = 0;
445 if (const auto *BTE = mdconst::extract_or_null<ConstantInt>(
446 M.getModuleFlag("branch-target-enforcement"))) {
447 if (!BTE->isZero()) {
448 BAFlags |= AArch64BuildAttributes::FeatureAndBitsFlag::Feature_BTI_Flag;
450 }
451 }
452
453 if (const auto *GCS = mdconst::extract_or_null<ConstantInt>(
454 M.getModuleFlag("guarded-control-stack"))) {
455 if (!GCS->isZero()) {
456 BAFlags |= AArch64BuildAttributes::FeatureAndBitsFlag::Feature_GCS_Flag;
458 }
459 }
460
461 if (const auto *Sign = mdconst::extract_or_null<ConstantInt>(
462 M.getModuleFlag("sign-return-address"))) {
463 if (!Sign->isZero()) {
464 BAFlags |= AArch64BuildAttributes::FeatureAndBitsFlag::Feature_PAC_Flag;
466 }
467 }
468
469 uint64_t PAuthABIPlatform = -1;
470 if (const auto *PAP = mdconst::extract_or_null<ConstantInt>(
471 M.getModuleFlag("aarch64-elf-pauthabi-platform"))) {
472 PAuthABIPlatform = PAP->getZExtValue();
473 }
474
475 uint64_t PAuthABIVersion = -1;
476 if (const auto *PAV = mdconst::extract_or_null<ConstantInt>(
477 M.getModuleFlag("aarch64-elf-pauthabi-version"))) {
478 PAuthABIVersion = PAV->getZExtValue();
479 }
480
481 // For LLVM_LINUX experimental platform, version value of 0 means no PAuth
482 // support. Do not emit corresponding PAuthABI GNU property note and AArch64
483 // build attributes for this case to keep Linux binaries not using PAuth
484 // unaffected.
485 if (PAuthABIPlatform == ELF::AARCH64_PAUTH_PLATFORM_LLVM_LINUX &&
486 PAuthABIVersion == 0) {
487 PAuthABIPlatform = uint64_t(-1);
488 PAuthABIVersion = uint64_t(-1);
489 }
490
491 // Emit AArch64 Build Attributes
492 emitAttributes(BAFlags, PAuthABIPlatform, PAuthABIVersion, TS);
493 // Emit a .note.gnu.property section with the flags.
494 TS->emitNoteSection(GNUFlags, PAuthABIPlatform, PAuthABIVersion);
495}
496
497void AArch64AsmPrinter::emitFunctionHeaderComment() {
498 const AArch64FunctionInfo *FI = MF->getInfo<AArch64FunctionInfo>();
499 std::optional<std::string> OutlinerString = FI->getOutliningStyle();
500 if (OutlinerString != std::nullopt)
501 OutStreamer->getCommentOS() << ' ' << OutlinerString;
502}
503
504void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_ENTER(const MachineInstr &MI)
505{
506 const Function &F = MF->getFunction();
507 if (F.hasFnAttribute("patchable-function-entry")) {
508 unsigned Num;
509 if (F.getFnAttribute("patchable-function-entry")
510 .getValueAsString()
511 .getAsInteger(10, Num))
512 return;
513 emitNops(Num);
514 return;
515 }
516
517 emitSled(MI, SledKind::FUNCTION_ENTER);
518}
519
520void AArch64AsmPrinter::LowerPATCHABLE_FUNCTION_EXIT(const MachineInstr &MI) {
521 emitSled(MI, SledKind::FUNCTION_EXIT);
522}
523
524void AArch64AsmPrinter::LowerPATCHABLE_TAIL_CALL(const MachineInstr &MI) {
525 emitSled(MI, SledKind::TAIL_CALL);
526}
527
528void AArch64AsmPrinter::emitSled(const MachineInstr &MI, SledKind Kind) {
529 static const int8_t NoopsInSledCount = 7;
530 // We want to emit the following pattern:
531 //
532 // .Lxray_sled_N:
533 // ALIGN
534 // B #32
535 // ; 7 NOP instructions (28 bytes)
536 // .tmpN
537 //
538 // We need the 28 bytes (7 instructions) because at runtime, we'd be patching
539 // over the full 32 bytes (8 instructions) with the following pattern:
540 //
541 // STP X0, X30, [SP, #-16]! ; push X0 and the link register to the stack
542 // LDR W17, #12 ; W17 := function ID
543 // LDR X16,#12 ; X16 := addr of __xray_FunctionEntry or __xray_FunctionExit
544 // BLR X16 ; call the tracing trampoline
545 // ;DATA: 32 bits of function ID
546 // ;DATA: lower 32 bits of the address of the trampoline
547 // ;DATA: higher 32 bits of the address of the trampoline
548 // LDP X0, X30, [SP], #16 ; pop X0 and the link register from the stack
549 //
550 OutStreamer->emitCodeAlignment(Align(4), getSubtargetInfo());
551 auto CurSled = OutContext.createTempSymbol("xray_sled_", true);
552 OutStreamer->emitLabel(CurSled);
553 auto Target = OutContext.createTempSymbol();
554
555 // Emit "B #32" instruction, which jumps over the next 28 bytes.
556 // The operand has to be the number of 4-byte instructions to jump over,
557 // including the current instruction.
558 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::B).addImm(8));
559
560 for (int8_t I = 0; I < NoopsInSledCount; I++)
561 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::NOP));
562
563 OutStreamer->emitLabel(Target);
564 recordSled(CurSled, MI, Kind, 2);
565}
566
567void AArch64AsmPrinter::emitAttributes(unsigned Flags,
568 uint64_t PAuthABIPlatform,
569 uint64_t PAuthABIVersion,
570 AArch64TargetStreamer *TS) {
571
572 PAuthABIPlatform = (uint64_t(-1) == PAuthABIPlatform) ? 0 : PAuthABIPlatform;
573 PAuthABIVersion = (uint64_t(-1) == PAuthABIVersion) ? 0 : PAuthABIVersion;
574
575 if (PAuthABIPlatform || PAuthABIVersion) {
579 AArch64BuildAttributes::SubsectionOptional::REQUIRED,
580 AArch64BuildAttributes::SubsectionType::ULEB128);
584 PAuthABIPlatform, "");
588 "");
589 }
590
591 unsigned BTIValue =
593 unsigned PACValue =
595 unsigned GCSValue =
597
598 if (BTIValue || PACValue || GCSValue) {
602 AArch64BuildAttributes::SubsectionOptional::OPTIONAL,
603 AArch64BuildAttributes::SubsectionType::ULEB128);
613 }
614}
615
616// Emit the following code for Intrinsic::{xray_customevent,xray_typedevent}
617// (built-in functions __xray_customevent/__xray_typedevent).
618//
619// .Lxray_event_sled_N:
620// b 1f
621// save x0 and x1 (and also x2 for TYPED_EVENT_CALL)
622// set up x0 and x1 (and also x2 for TYPED_EVENT_CALL)
623// bl __xray_CustomEvent or __xray_TypedEvent
624// restore x0 and x1 (and also x2 for TYPED_EVENT_CALL)
625// 1:
626//
627// There are 6 instructions for EVENT_CALL and 9 for TYPED_EVENT_CALL.
628//
629// Then record a sled of kind CUSTOM_EVENT or TYPED_EVENT.
630// After patching, b .+N will become a nop.
631void AArch64AsmPrinter::LowerPATCHABLE_EVENT_CALL(const MachineInstr &MI,
632 bool Typed) {
633 auto &O = *OutStreamer;
634 MCSymbol *CurSled = OutContext.createTempSymbol("xray_sled_", true);
635 O.emitLabel(CurSled);
636 bool MachO = TM.getTargetTriple().isOSBinFormatMachO();
637 auto *Sym = MCSymbolRefExpr::create(
638 OutContext.getOrCreateSymbol(
639 Twine(MachO ? "_" : "") +
640 (Typed ? "__xray_TypedEvent" : "__xray_CustomEvent")),
641 OutContext);
642 if (Typed) {
643 O.AddComment("Begin XRay typed event");
644 EmitToStreamer(O, MCInstBuilder(AArch64::B).addImm(9));
645 EmitToStreamer(O, MCInstBuilder(AArch64::STPXpre)
646 .addReg(AArch64::SP)
647 .addReg(AArch64::X0)
648 .addReg(AArch64::X1)
649 .addReg(AArch64::SP)
650 .addImm(-4));
651 EmitToStreamer(O, MCInstBuilder(AArch64::STRXui)
652 .addReg(AArch64::X2)
653 .addReg(AArch64::SP)
654 .addImm(2));
655 emitMovXReg(AArch64::X0, MI.getOperand(0).getReg());
656 emitMovXReg(AArch64::X1, MI.getOperand(1).getReg());
657 emitMovXReg(AArch64::X2, MI.getOperand(2).getReg());
658 EmitToStreamer(O, MCInstBuilder(AArch64::BL).addExpr(Sym));
659 EmitToStreamer(O, MCInstBuilder(AArch64::LDRXui)
660 .addReg(AArch64::X2)
661 .addReg(AArch64::SP)
662 .addImm(2));
663 O.AddComment("End XRay typed event");
664 EmitToStreamer(O, MCInstBuilder(AArch64::LDPXpost)
665 .addReg(AArch64::SP)
666 .addReg(AArch64::X0)
667 .addReg(AArch64::X1)
668 .addReg(AArch64::SP)
669 .addImm(4));
670
671 recordSled(CurSled, MI, SledKind::TYPED_EVENT, 2);
672 } else {
673 O.AddComment("Begin XRay custom event");
674 EmitToStreamer(O, MCInstBuilder(AArch64::B).addImm(6));
675 EmitToStreamer(O, MCInstBuilder(AArch64::STPXpre)
676 .addReg(AArch64::SP)
677 .addReg(AArch64::X0)
678 .addReg(AArch64::X1)
679 .addReg(AArch64::SP)
680 .addImm(-2));
681 emitMovXReg(AArch64::X0, MI.getOperand(0).getReg());
682 emitMovXReg(AArch64::X1, MI.getOperand(1).getReg());
683 EmitToStreamer(O, MCInstBuilder(AArch64::BL).addExpr(Sym));
684 O.AddComment("End XRay custom event");
685 EmitToStreamer(O, MCInstBuilder(AArch64::LDPXpost)
686 .addReg(AArch64::SP)
687 .addReg(AArch64::X0)
688 .addReg(AArch64::X1)
689 .addReg(AArch64::SP)
690 .addImm(2));
691
692 recordSled(CurSled, MI, SledKind::CUSTOM_EVENT, 2);
693 }
694}
695
696void AArch64AsmPrinter::LowerKCFI_CHECK(const MachineInstr &MI) {
697 Register AddrReg = MI.getOperand(0).getReg();
698 assert(std::next(MI.getIterator())->isCall() &&
699 "KCFI_CHECK not followed by a call instruction");
700 assert(std::next(MI.getIterator())->getOperand(0).getReg() == AddrReg &&
701 "KCFI_CHECK call target doesn't match call operand");
702
703 // Default to using the intra-procedure-call temporary registers for
704 // comparing the hashes.
705 unsigned ScratchRegs[] = {AArch64::W16, AArch64::W17};
706 if (AddrReg == AArch64::XZR) {
707 // Checking XZR makes no sense. Instead of emitting a load, zero
708 // ScratchRegs[0] and use it for the ESR AddrIndex below.
709 AddrReg = getXRegFromWReg(ScratchRegs[0]);
710 emitMovXReg(AddrReg, AArch64::XZR);
711 } else {
712 // If one of the scratch registers is used for the call target (e.g.
713 // with AArch64::TCRETURNriBTI), we can clobber another caller-saved
714 // temporary register instead (in this case, AArch64::W9) as the check
715 // is immediately followed by the call instruction.
716 for (auto &Reg : ScratchRegs) {
717 if (Reg == getWRegFromXReg(AddrReg)) {
718 Reg = AArch64::W9;
719 break;
720 }
721 }
722 assert(ScratchRegs[0] != AddrReg && ScratchRegs[1] != AddrReg &&
723 "Invalid scratch registers for KCFI_CHECK");
724
725 // Adjust the offset for patchable-function-prefix. This assumes that
726 // patchable-function-prefix is the same for all functions.
727 int64_t PrefixNops =
728 MI.getMF()->getFunction().getFnAttributeAsParsedInteger(
729 "patchable-function-prefix");
730
731 // Load the target function type hash.
732 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDURWi)
733 .addReg(ScratchRegs[0])
734 .addReg(AddrReg)
735 .addImm(-(PrefixNops * 4 + 4)));
736 }
737
738 // Load the expected type hash.
739 const int64_t Type = MI.getOperand(1).getImm();
740 emitMOVK(ScratchRegs[1], Type & 0xFFFF, 0);
741 emitMOVK(ScratchRegs[1], (Type >> 16) & 0xFFFF, 16);
742
743 // Compare the hashes and trap if there's a mismatch.
744 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSWrs)
745 .addReg(AArch64::WZR)
746 .addReg(ScratchRegs[0])
747 .addReg(ScratchRegs[1])
748 .addImm(0));
749
750 MCSymbol *Pass = OutContext.createTempSymbol();
751 EmitToStreamer(*OutStreamer,
752 MCInstBuilder(AArch64::Bcc)
753 .addImm(AArch64CC::EQ)
754 .addExpr(MCSymbolRefExpr::create(Pass, OutContext)));
755
756 // The base ESR is 0x8000 and the register information is encoded in bits
757 // 0-9 as follows:
758 // - 0-4: n, where the register Xn contains the target address
759 // - 5-9: m, where the register Wm contains the expected type hash
760 // Where n, m are in [0, 30].
761 unsigned TypeIndex = ScratchRegs[1] - AArch64::W0;
762 unsigned AddrIndex;
763 switch (AddrReg) {
764 default:
765 AddrIndex = AddrReg - AArch64::X0;
766 break;
767 case AArch64::FP:
768 AddrIndex = 29;
769 break;
770 case AArch64::LR:
771 AddrIndex = 30;
772 break;
773 }
774
775 assert(AddrIndex < 31 && TypeIndex < 31);
776
777 unsigned ESR = 0x8000 | ((TypeIndex & 31) << 5) | (AddrIndex & 31);
778 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::BRK).addImm(ESR));
779 OutStreamer->emitLabel(Pass);
780}
781
782void AArch64AsmPrinter::LowerHWASAN_CHECK_MEMACCESS(const MachineInstr &MI) {
783 Register Reg = MI.getOperand(0).getReg();
784
785 // The HWASan pass won't emit a CHECK_MEMACCESS intrinsic with a pointer
786 // statically known to be zero. However, conceivably, the HWASan pass may
787 // encounter a "cannot currently statically prove to be null" pointer (and is
788 // therefore unable to omit the intrinsic) that later optimization passes
789 // convert into a statically known-null pointer.
790 if (Reg == AArch64::XZR)
791 return;
792
793 bool IsShort =
794 ((MI.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES) ||
795 (MI.getOpcode() ==
796 AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW));
797 uint32_t AccessInfo = MI.getOperand(1).getImm();
798 bool IsFixedShadow =
799 ((MI.getOpcode() == AArch64::HWASAN_CHECK_MEMACCESS_FIXEDSHADOW) ||
800 (MI.getOpcode() ==
801 AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW));
802 uint64_t FixedShadowOffset = IsFixedShadow ? MI.getOperand(2).getImm() : 0;
803
804 MCSymbol *&Sym = HwasanMemaccessSymbols[HwasanMemaccessTuple(
805 Reg, IsShort, AccessInfo, IsFixedShadow, FixedShadowOffset)];
806 if (!Sym) {
807 // FIXME: Make this work on non-ELF.
808 if (!TM.getTargetTriple().isOSBinFormatELF())
809 report_fatal_error("llvm.hwasan.check.memaccess only supported on ELF");
810
811 std::string SymName = "__hwasan_check_x" + utostr(Reg - AArch64::X0) + "_" +
812 utostr(AccessInfo);
813 if (IsFixedShadow)
814 SymName += "_fixed_" + utostr(FixedShadowOffset);
815 if (IsShort)
816 SymName += "_short_v2";
817 Sym = OutContext.getOrCreateSymbol(SymName);
818 }
819
820 EmitToStreamer(*OutStreamer,
821 MCInstBuilder(AArch64::BL)
822 .addExpr(MCSymbolRefExpr::create(Sym, OutContext)));
823}
824
825void AArch64AsmPrinter::emitHwasanMemaccessSymbols(Module &M) {
826 if (HwasanMemaccessSymbols.empty())
827 return;
828
829 const Triple &TT = TM.getTargetTriple();
830 assert(TT.isOSBinFormatELF());
831 // AArch64Subtarget is huge, so heap allocate it so we don't run out of stack
832 // space.
833 auto STI = std::make_unique<AArch64Subtarget>(
834 TT, TM.getTargetCPU(), TM.getTargetCPU(), TM.getTargetFeatureString(), TM,
835 true);
836 this->STI = STI.get();
837
838 MCSymbol *HwasanTagMismatchV1Sym =
839 OutContext.getOrCreateSymbol("__hwasan_tag_mismatch");
840 MCSymbol *HwasanTagMismatchV2Sym =
841 OutContext.getOrCreateSymbol("__hwasan_tag_mismatch_v2");
842
843 const MCSymbolRefExpr *HwasanTagMismatchV1Ref =
844 MCSymbolRefExpr::create(HwasanTagMismatchV1Sym, OutContext);
845 const MCSymbolRefExpr *HwasanTagMismatchV2Ref =
846 MCSymbolRefExpr::create(HwasanTagMismatchV2Sym, OutContext);
847
848 for (auto &P : HwasanMemaccessSymbols) {
849 unsigned Reg = std::get<0>(P.first);
850 bool IsShort = std::get<1>(P.first);
851 uint32_t AccessInfo = std::get<2>(P.first);
852 bool IsFixedShadow = std::get<3>(P.first);
853 uint64_t FixedShadowOffset = std::get<4>(P.first);
854 const MCSymbolRefExpr *HwasanTagMismatchRef =
855 IsShort ? HwasanTagMismatchV2Ref : HwasanTagMismatchV1Ref;
856 MCSymbol *Sym = P.second;
857
858 bool HasMatchAllTag =
859 (AccessInfo >> HWASanAccessInfo::HasMatchAllShift) & 1;
860 uint8_t MatchAllTag =
861 (AccessInfo >> HWASanAccessInfo::MatchAllShift) & 0xff;
862 unsigned Size =
863 1 << ((AccessInfo >> HWASanAccessInfo::AccessSizeShift) & 0xf);
864 bool CompileKernel =
865 (AccessInfo >> HWASanAccessInfo::CompileKernelShift) & 1;
866
867 OutStreamer->switchSection(OutContext.getELFSection(
868 ".text.hot", ELF::SHT_PROGBITS,
870 /*IsComdat=*/true));
871
872 OutStreamer->emitSymbolAttribute(Sym, MCSA_ELF_TypeFunction);
873 OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
874 OutStreamer->emitSymbolAttribute(Sym, MCSA_Hidden);
875 OutStreamer->emitLabel(Sym);
876
877 EmitToStreamer(MCInstBuilder(AArch64::SBFMXri)
878 .addReg(AArch64::X16)
879 .addReg(Reg)
880 .addImm(4)
881 .addImm(55));
882
883 if (IsFixedShadow) {
884 // Aarch64 makes it difficult to embed large constants in the code.
885 // Fortuitously, kShadowBaseAlignment == 32, so we use the 32-bit
886 // left-shift option in the MOV instruction. Combined with the 16-bit
887 // immediate, this is enough to represent any offset up to 2**48.
888 emitMOVZ(AArch64::X17, FixedShadowOffset >> 32, 32);
889 EmitToStreamer(MCInstBuilder(AArch64::LDRBBroX)
890 .addReg(AArch64::W16)
891 .addReg(AArch64::X17)
892 .addReg(AArch64::X16)
893 .addImm(0)
894 .addImm(0));
895 } else {
896 EmitToStreamer(MCInstBuilder(AArch64::LDRBBroX)
897 .addReg(AArch64::W16)
898 .addReg(IsShort ? AArch64::X20 : AArch64::X9)
899 .addReg(AArch64::X16)
900 .addImm(0)
901 .addImm(0));
902 }
903
904 EmitToStreamer(MCInstBuilder(AArch64::SUBSXrs)
905 .addReg(AArch64::XZR)
906 .addReg(AArch64::X16)
907 .addReg(Reg)
909 MCSymbol *HandleMismatchOrPartialSym = OutContext.createTempSymbol();
910 EmitToStreamer(MCInstBuilder(AArch64::Bcc)
911 .addImm(AArch64CC::NE)
913 HandleMismatchOrPartialSym, OutContext)));
914 MCSymbol *ReturnSym = OutContext.createTempSymbol();
915 OutStreamer->emitLabel(ReturnSym);
916 EmitToStreamer(MCInstBuilder(AArch64::RET).addReg(AArch64::LR));
917 OutStreamer->emitLabel(HandleMismatchOrPartialSym);
918
919 if (HasMatchAllTag) {
920 EmitToStreamer(MCInstBuilder(AArch64::UBFMXri)
921 .addReg(AArch64::X17)
922 .addReg(Reg)
923 .addImm(56)
924 .addImm(63));
925 EmitToStreamer(MCInstBuilder(AArch64::SUBSXri)
926 .addReg(AArch64::XZR)
927 .addReg(AArch64::X17)
928 .addImm(MatchAllTag)
929 .addImm(0));
930 EmitToStreamer(
931 MCInstBuilder(AArch64::Bcc)
932 .addImm(AArch64CC::EQ)
933 .addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)));
934 }
935
936 if (IsShort) {
937 EmitToStreamer(MCInstBuilder(AArch64::SUBSWri)
938 .addReg(AArch64::WZR)
939 .addReg(AArch64::W16)
940 .addImm(15)
941 .addImm(0));
942 MCSymbol *HandleMismatchSym = OutContext.createTempSymbol();
943 EmitToStreamer(
944 MCInstBuilder(AArch64::Bcc)
945 .addImm(AArch64CC::HI)
946 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)));
947
948 EmitToStreamer(MCInstBuilder(AArch64::ANDXri)
949 .addReg(AArch64::X17)
950 .addReg(Reg)
951 .addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)));
952 if (Size != 1)
953 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
954 .addReg(AArch64::X17)
955 .addReg(AArch64::X17)
956 .addImm(Size - 1)
957 .addImm(0));
958 EmitToStreamer(MCInstBuilder(AArch64::SUBSWrs)
959 .addReg(AArch64::WZR)
960 .addReg(AArch64::W16)
961 .addReg(AArch64::W17)
962 .addImm(0));
963 EmitToStreamer(
964 MCInstBuilder(AArch64::Bcc)
965 .addImm(AArch64CC::LS)
966 .addExpr(MCSymbolRefExpr::create(HandleMismatchSym, OutContext)));
967
968 EmitToStreamer(MCInstBuilder(AArch64::ORRXri)
969 .addReg(AArch64::X16)
970 .addReg(Reg)
971 .addImm(AArch64_AM::encodeLogicalImmediate(0xf, 64)));
972 EmitToStreamer(MCInstBuilder(AArch64::LDRBBui)
973 .addReg(AArch64::W16)
974 .addReg(AArch64::X16)
975 .addImm(0));
976 EmitToStreamer(
977 MCInstBuilder(AArch64::SUBSXrs)
978 .addReg(AArch64::XZR)
979 .addReg(AArch64::X16)
980 .addReg(Reg)
982 EmitToStreamer(
983 MCInstBuilder(AArch64::Bcc)
984 .addImm(AArch64CC::EQ)
985 .addExpr(MCSymbolRefExpr::create(ReturnSym, OutContext)));
986
987 OutStreamer->emitLabel(HandleMismatchSym);
988 }
989
990 EmitToStreamer(MCInstBuilder(AArch64::STPXpre)
991 .addReg(AArch64::SP)
992 .addReg(AArch64::X0)
993 .addReg(AArch64::X1)
994 .addReg(AArch64::SP)
995 .addImm(-32));
996 EmitToStreamer(MCInstBuilder(AArch64::STPXi)
997 .addReg(AArch64::FP)
998 .addReg(AArch64::LR)
999 .addReg(AArch64::SP)
1000 .addImm(29));
1001
1002 if (Reg != AArch64::X0)
1003 emitMovXReg(AArch64::X0, Reg);
1004 emitMOVZ(AArch64::X1, AccessInfo & HWASanAccessInfo::RuntimeMask, 0);
1005
1006 if (CompileKernel) {
1007 // The Linux kernel's dynamic loader doesn't support GOT relative
1008 // relocations, but it doesn't support late binding either, so just call
1009 // the function directly.
1010 EmitToStreamer(MCInstBuilder(AArch64::B).addExpr(HwasanTagMismatchRef));
1011 } else {
1012 // Intentionally load the GOT entry and branch to it, rather than possibly
1013 // late binding the function, which may clobber the registers before we
1014 // have a chance to save them.
1015 EmitToStreamer(MCInstBuilder(AArch64::ADRP)
1016 .addReg(AArch64::X16)
1017 .addExpr(MCSpecifierExpr::create(HwasanTagMismatchRef,
1019 OutContext)));
1020 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
1021 .addReg(AArch64::X16)
1022 .addReg(AArch64::X16)
1023 .addExpr(MCSpecifierExpr::create(HwasanTagMismatchRef,
1025 OutContext)));
1026 EmitToStreamer(MCInstBuilder(AArch64::BR).addReg(AArch64::X16));
1027 }
1028 }
1029 this->STI = nullptr;
1030}
1031
1032static void emitAuthenticatedPointer(MCStreamer &OutStreamer,
1033 MCSymbol *StubLabel,
1034 const MCExpr *StubAuthPtrRef) {
1035 // sym$auth_ptr$key$disc:
1036 OutStreamer.emitLabel(StubLabel);
1037 OutStreamer.emitValue(StubAuthPtrRef, /*size=*/8);
1038}
1039
1040void AArch64AsmPrinter::emitEndOfAsmFile(Module &M) {
1041 emitHwasanMemaccessSymbols(M);
1042
1043 const Triple &TT = TM.getTargetTriple();
1044 if (TT.isOSBinFormatMachO()) {
1045 // Output authenticated pointers as indirect symbols, if we have any.
1046 MachineModuleInfoMachO &MMIMacho =
1047 MMI->getObjFileInfo<MachineModuleInfoMachO>();
1048
1049 auto Stubs = MMIMacho.getAuthGVStubList();
1050
1051 if (!Stubs.empty()) {
1052 // Switch to the "__auth_ptr" section.
1053 OutStreamer->switchSection(
1054 OutContext.getMachOSection("__DATA", "__auth_ptr", MachO::S_REGULAR,
1056 emitAlignment(Align(8));
1057
1058 for (const auto &Stub : Stubs)
1059 emitAuthenticatedPointer(*OutStreamer, Stub.first, Stub.second);
1060
1061 OutStreamer->addBlankLine();
1062 }
1063
1064 // Funny Darwin hack: This flag tells the linker that no global symbols
1065 // contain code that falls through to other global symbols (e.g. the obvious
1066 // implementation of multiple entry points). If this doesn't occur, the
1067 // linker can safely perform dead code stripping. Since LLVM never
1068 // generates code that does this, it is always safe to set.
1069 OutStreamer->emitSubsectionsViaSymbols();
1070 }
1071
1072 if (TT.isOSBinFormatELF()) {
1073 // Output authenticated pointers as indirect symbols, if we have any.
1074 MachineModuleInfoELF &MMIELF = MMI->getObjFileInfo<MachineModuleInfoELF>();
1075
1076 auto Stubs = MMIELF.getAuthGVStubList();
1077
1078 if (!Stubs.empty()) {
1079 const TargetLoweringObjectFile &TLOF = getObjFileLowering();
1080 OutStreamer->switchSection(TLOF.getDataSection());
1081 emitAlignment(Align(8));
1082
1083 for (const auto &Stub : Stubs)
1084 emitAuthenticatedPointer(*OutStreamer, Stub.first, Stub.second);
1085
1086 OutStreamer->addBlankLine();
1087 }
1088
1089 // With signed ELF GOT enabled, the linker looks at the symbol type to
1090 // choose between keys IA (for STT_FUNC) and DA (for other types). Symbols
1091 // for functions not defined in the module have STT_NOTYPE type by default.
1092 // This makes linker to emit signing schema with DA key (instead of IA) for
1093 // corresponding R_AARCH64_AUTH_GLOB_DAT dynamic reloc. To avoid that, force
1094 // all function symbols used in the module to have STT_FUNC type. See
1095 // https://github.com/ARM-software/abi-aa/blob/main/pauthabielf64/pauthabielf64.rst#default-signing-schema
1096 const auto *PtrAuthELFGOTFlag = mdconst::extract_or_null<ConstantInt>(
1097 M.getModuleFlag("ptrauth-elf-got"));
1098 if (PtrAuthELFGOTFlag && PtrAuthELFGOTFlag->getZExtValue() == 1)
1099 for (const GlobalValue &GV : M.global_values())
1100 if (!GV.use_empty() && isa<Function>(GV) &&
1101 !GV.getName().starts_with("llvm."))
1102 OutStreamer->emitSymbolAttribute(getSymbol(&GV),
1104 }
1105
1106 // Emit stack and fault map information.
1108
1109 // If import call optimization is enabled, emit the appropriate section.
1110 // We do this whether or not we recorded any import calls.
1111 if (EnableImportCallOptimization && TT.isOSBinFormatCOFF()) {
1112 OutStreamer->switchSection(getObjFileLowering().getImportCallSection());
1113
1114 // Section always starts with some magic.
1115 constexpr char ImpCallMagic[12] = "Imp_Call_V1";
1116 OutStreamer->emitBytes(StringRef{ImpCallMagic, sizeof(ImpCallMagic)});
1117
1118 // Layout of this section is:
1119 // Per section that contains calls to imported functions:
1120 // uint32_t SectionSize: Size in bytes for information in this section.
1121 // uint32_t Section Number
1122 // Per call to imported function in section:
1123 // uint32_t Kind: the kind of imported function.
1124 // uint32_t BranchOffset: the offset of the branch instruction in its
1125 // parent section.
1126 // uint32_t TargetSymbolId: the symbol id of the called function.
1127 for (auto &[Section, CallsToImportedFuncs] :
1128 SectionToImportedFunctionCalls) {
1129 unsigned SectionSize =
1130 sizeof(uint32_t) * (2 + 3 * CallsToImportedFuncs.size());
1131 OutStreamer->emitInt32(SectionSize);
1132 OutStreamer->emitCOFFSecNumber(Section->getBeginSymbol());
1133 for (auto &[CallsiteSymbol, CalledSymbol] : CallsToImportedFuncs) {
1134 // Kind is always IMAGE_REL_ARM64_DYNAMIC_IMPORT_CALL (0x13).
1135 OutStreamer->emitInt32(0x13);
1136 OutStreamer->emitCOFFSecOffset(CallsiteSymbol);
1137 OutStreamer->emitCOFFSymbolIndex(CalledSymbol);
1138 }
1139 }
1140 }
1141}
1142
1143void AArch64AsmPrinter::emitLOHs() {
1145
1146 for (const auto &D : AArch64FI->getLOHContainer()) {
1147 for (const MachineInstr *MI : D.getArgs()) {
1148 MInstToMCSymbol::iterator LabelIt = LOHInstToLabel.find(MI);
1149 assert(LabelIt != LOHInstToLabel.end() &&
1150 "Label hasn't been inserted for LOH related instruction");
1151 MCArgs.push_back(LabelIt->second);
1152 }
1153 OutStreamer->emitLOHDirective(D.getKind(), MCArgs);
1154 MCArgs.clear();
1155 }
1156}
1157
1158void AArch64AsmPrinter::emitFunctionBodyEnd() {
1159 if (!AArch64FI->getLOHRelated().empty())
1160 emitLOHs();
1161}
1162
1163/// GetCPISymbol - Return the symbol for the specified constant pool entry.
1164MCSymbol *AArch64AsmPrinter::GetCPISymbol(unsigned CPID) const {
1165 // Darwin uses a linker-private symbol name for constant-pools (to
1166 // avoid addends on the relocation?), ELF has no such concept and
1167 // uses a normal private symbol.
1168 if (!getDataLayout().getLinkerPrivateGlobalPrefix().empty())
1169 return OutContext.getOrCreateSymbol(
1170 Twine(getDataLayout().getLinkerPrivateGlobalPrefix()) + "CPI" +
1171 Twine(getFunctionNumber()) + "_" + Twine(CPID));
1172
1173 return AsmPrinter::GetCPISymbol(CPID);
1174}
1175
1176void AArch64AsmPrinter::printOperand(const MachineInstr *MI, unsigned OpNum,
1177 raw_ostream &O) {
1178 const MachineOperand &MO = MI->getOperand(OpNum);
1179 switch (MO.getType()) {
1180 default:
1181 llvm_unreachable("<unknown operand type>");
1183 Register Reg = MO.getReg();
1185 assert(!MO.getSubReg() && "Subregs should be eliminated!");
1187 break;
1188 }
1190 O << MO.getImm();
1191 break;
1192 }
1194 PrintSymbolOperand(MO, O);
1195 break;
1196 }
1198 MCSymbol *Sym = GetBlockAddressSymbol(MO.getBlockAddress());
1199 Sym->print(O, MAI);
1200 break;
1201 }
1202 }
1203}
1204
1205bool AArch64AsmPrinter::printAsmMRegister(const MachineOperand &MO, char Mode,
1206 raw_ostream &O) {
1207 Register Reg = MO.getReg();
1208 switch (Mode) {
1209 default:
1210 return true; // Unknown mode.
1211 case 'w':
1213 break;
1214 case 'x':
1216 break;
1217 case 't':
1219 break;
1220 }
1221
1223 return false;
1224}
1225
1226// Prints the register in MO using class RC using the offset in the
1227// new register class. This should not be used for cross class
1228// printing.
1229bool AArch64AsmPrinter::printAsmRegInClass(const MachineOperand &MO,
1230 const TargetRegisterClass *RC,
1231 unsigned AltName, raw_ostream &O) {
1232 assert(MO.isReg() && "Should only get here with a register!");
1233 const TargetRegisterInfo *RI = STI->getRegisterInfo();
1234 Register Reg = MO.getReg();
1235 MCRegister RegToPrint = RC->getRegister(RI->getEncodingValue(Reg));
1236 if (!RI->regsOverlap(RegToPrint, Reg))
1237 return true;
1238 O << AArch64InstPrinter::getRegisterName(RegToPrint, AltName);
1239 return false;
1240}
1241
1242bool AArch64AsmPrinter::PrintAsmOperand(const MachineInstr *MI, unsigned OpNum,
1243 const char *ExtraCode, raw_ostream &O) {
1244 const MachineOperand &MO = MI->getOperand(OpNum);
1245
1246 // First try the generic code, which knows about modifiers like 'c' and 'n'.
1247 if (!AsmPrinter::PrintAsmOperand(MI, OpNum, ExtraCode, O))
1248 return false;
1249
1250 // Does this asm operand have a single letter operand modifier?
1251 if (ExtraCode && ExtraCode[0]) {
1252 if (ExtraCode[1] != 0)
1253 return true; // Unknown modifier.
1254
1255 switch (ExtraCode[0]) {
1256 default:
1257 return true; // Unknown modifier.
1258 case 'w': // Print W register
1259 case 'x': // Print X register
1260 if (MO.isReg())
1261 return printAsmMRegister(MO, ExtraCode[0], O);
1262 if (MO.isImm() && MO.getImm() == 0) {
1263 unsigned Reg = ExtraCode[0] == 'w' ? AArch64::WZR : AArch64::XZR;
1265 return false;
1266 }
1267 printOperand(MI, OpNum, O);
1268 return false;
1269 case 'b': // Print B register.
1270 case 'h': // Print H register.
1271 case 's': // Print S register.
1272 case 'd': // Print D register.
1273 case 'q': // Print Q register.
1274 case 'z': // Print Z register.
1275 if (MO.isReg()) {
1276 const TargetRegisterClass *RC;
1277 switch (ExtraCode[0]) {
1278 case 'b':
1279 RC = &AArch64::FPR8RegClass;
1280 break;
1281 case 'h':
1282 RC = &AArch64::FPR16RegClass;
1283 break;
1284 case 's':
1285 RC = &AArch64::FPR32RegClass;
1286 break;
1287 case 'd':
1288 RC = &AArch64::FPR64RegClass;
1289 break;
1290 case 'q':
1291 RC = &AArch64::FPR128RegClass;
1292 break;
1293 case 'z':
1294 RC = &AArch64::ZPRRegClass;
1295 break;
1296 default:
1297 return true;
1298 }
1299 return printAsmRegInClass(MO, RC, AArch64::NoRegAltName, O);
1300 }
1301 printOperand(MI, OpNum, O);
1302 return false;
1303 }
1304 }
1305
1306 // According to ARM, we should emit x and v registers unless we have a
1307 // modifier.
1308 if (MO.isReg()) {
1309 Register Reg = MO.getReg();
1310
1311 // If this is a w or x register, print an x register.
1312 if (AArch64::GPR32allRegClass.contains(Reg) ||
1313 AArch64::GPR64allRegClass.contains(Reg))
1314 return printAsmMRegister(MO, 'x', O);
1315
1316 // If this is an x register tuple, print an x register.
1317 if (AArch64::GPR64x8ClassRegClass.contains(Reg))
1318 return printAsmMRegister(MO, 't', O);
1319
1320 unsigned AltName = AArch64::NoRegAltName;
1321 const TargetRegisterClass *RegClass;
1322 if (AArch64::ZPRRegClass.contains(Reg)) {
1323 RegClass = &AArch64::ZPRRegClass;
1324 } else if (AArch64::PPRRegClass.contains(Reg)) {
1325 RegClass = &AArch64::PPRRegClass;
1326 } else if (AArch64::PNRRegClass.contains(Reg)) {
1327 RegClass = &AArch64::PNRRegClass;
1328 } else {
1329 RegClass = &AArch64::FPR128RegClass;
1330 AltName = AArch64::vreg;
1331 }
1332
1333 // If this is a b, h, s, d, or q register, print it as a v register.
1334 return printAsmRegInClass(MO, RegClass, AltName, O);
1335 }
1336
1337 printOperand(MI, OpNum, O);
1338 return false;
1339}
1340
1341bool AArch64AsmPrinter::PrintAsmMemoryOperand(const MachineInstr *MI,
1342 unsigned OpNum,
1343 const char *ExtraCode,
1344 raw_ostream &O) {
1345 if (ExtraCode && ExtraCode[0] && ExtraCode[0] != 'a')
1346 return true; // Unknown modifier.
1347
1348 const MachineOperand &MO = MI->getOperand(OpNum);
1349 assert(MO.isReg() && "unexpected inline asm memory operand");
1350 O << "[" << AArch64InstPrinter::getRegisterName(MO.getReg()) << "]";
1351 return false;
1352}
1353
1354void AArch64AsmPrinter::PrintDebugValueComment(const MachineInstr *MI,
1355 raw_ostream &OS) {
1356 unsigned NOps = MI->getNumOperands();
1357 assert(NOps == 4);
1358 OS << '\t' << MAI.getCommentString() << "DEBUG_VALUE: ";
1359 // cast away const; DIetc do not take const operands for some reason.
1360 OS << MI->getDebugVariable()->getName();
1361 OS << " <- ";
1362 // Frame address. Currently handles register +- offset only.
1363 assert(MI->isIndirectDebugValue());
1364 OS << '[';
1365 for (unsigned I = 0, E = llvm::size(MI->debug_operands()); I < E; ++I) {
1366 if (I != 0)
1367 OS << ", ";
1368 printOperand(MI, I, OS);
1369 }
1370 OS << ']';
1371 OS << "+";
1372 printOperand(MI, NOps - 2, OS);
1373}
1374
1375void AArch64AsmPrinter::emitJumpTableImpl(const MachineJumpTableInfo &MJTI,
1376 ArrayRef<unsigned> JumpTableIndices) {
1377 // Fast return if there is nothing to emit to avoid creating empty sections.
1378 if (JumpTableIndices.empty())
1379 return;
1380 const TargetLoweringObjectFile &TLOF = getObjFileLowering();
1381 const auto &F = MF->getFunction();
1383
1384 MCSection *ReadOnlySec = nullptr;
1385 if (TM.Options.EnableStaticDataPartitioning) {
1386 ReadOnlySec =
1387 TLOF.getSectionForJumpTable(F, TM, &JT[JumpTableIndices.front()]);
1388 } else {
1389 ReadOnlySec = TLOF.getSectionForJumpTable(F, TM);
1390 }
1391 OutStreamer->switchSection(ReadOnlySec);
1392
1393 auto AFI = MF->getInfo<AArch64FunctionInfo>();
1394 for (unsigned JTI : JumpTableIndices) {
1395 const std::vector<MachineBasicBlock*> &JTBBs = JT[JTI].MBBs;
1396
1397 // If this jump table was deleted, ignore it.
1398 if (JTBBs.empty()) continue;
1399
1400 unsigned Size = AFI->getJumpTableEntrySize(JTI);
1401 emitAlignment(Align(Size));
1402 OutStreamer->emitLabel(GetJTISymbol(JTI));
1403
1404 const MCSymbol *BaseSym = AArch64FI->getJumpTableEntryPCRelSymbol(JTI);
1405 const MCExpr *Base = MCSymbolRefExpr::create(BaseSym, OutContext);
1406
1407 for (auto *JTBB : JTBBs) {
1408 const MCExpr *Value =
1409 MCSymbolRefExpr::create(JTBB->getSymbol(), OutContext);
1410
1411 // Each entry is:
1412 // .byte/.hword (LBB - Lbase)>>2
1413 // or plain:
1414 // .word LBB - Lbase
1415 Value = MCBinaryExpr::createSub(Value, Base, OutContext);
1416 if (Size != 4)
1418 Value, MCConstantExpr::create(2, OutContext), OutContext);
1419
1420 OutStreamer->emitValue(Value, Size);
1421 }
1422 }
1423}
1424
1425std::tuple<const MCSymbol *, uint64_t, const MCSymbol *,
1427AArch64AsmPrinter::getCodeViewJumpTableInfo(int JTI,
1428 const MachineInstr *BranchInstr,
1429 const MCSymbol *BranchLabel) const {
1430 const auto AFI = MF->getInfo<AArch64FunctionInfo>();
1431 const auto Base = AArch64FI->getJumpTableEntryPCRelSymbol(JTI);
1433 switch (AFI->getJumpTableEntrySize(JTI)) {
1434 case 1:
1435 EntrySize = codeview::JumpTableEntrySize::UInt8ShiftLeft;
1436 break;
1437 case 2:
1438 EntrySize = codeview::JumpTableEntrySize::UInt16ShiftLeft;
1439 break;
1440 case 4:
1441 EntrySize = codeview::JumpTableEntrySize::Int32;
1442 break;
1443 default:
1444 llvm_unreachable("Unexpected jump table entry size");
1445 }
1446 return std::make_tuple(Base, 0, BranchLabel, EntrySize);
1447}
1448
1449void AArch64AsmPrinter::emitFunctionEntryLabel() {
1450 const Triple &TT = TM.getTargetTriple();
1451 if (TT.isOSBinFormatELF() &&
1452 (MF->getFunction().getCallingConv() == CallingConv::AArch64_VectorCall ||
1453 MF->getFunction().getCallingConv() ==
1454 CallingConv::AArch64_SVE_VectorCall ||
1455 MF->getInfo<AArch64FunctionInfo>()->isSVECC())) {
1456 auto *TS =
1457 static_cast<AArch64TargetStreamer *>(OutStreamer->getTargetStreamer());
1458 TS->emitDirectiveVariantPCS(CurrentFnSym);
1459 }
1460
1462
1463 if (TT.isWindowsArm64EC() && !MF->getFunction().hasLocalLinkage()) {
1464 // For ARM64EC targets, a function definition's name is mangled differently
1465 // from the normal symbol, emit required aliases here.
1466 auto emitFunctionAlias = [&](MCSymbol *Src, MCSymbol *Dst) {
1467 OutStreamer->emitSymbolAttribute(Src, MCSA_WeakAntiDep);
1468 OutStreamer->emitAssignment(
1469 Src, MCSymbolRefExpr::create(Dst, MMI->getContext()));
1470 };
1471
1472 auto getSymbolFromMetadata = [&](StringRef Name) {
1473 MCSymbol *Sym = nullptr;
1474 if (MDNode *Node = MF->getFunction().getMetadata(Name)) {
1475 StringRef NameStr = cast<MDString>(Node->getOperand(0))->getString();
1476 Sym = MMI->getContext().getOrCreateSymbol(NameStr);
1477 }
1478 return Sym;
1479 };
1480
1481 SmallVector<MDNode *> UnmangledNames;
1482 MF->getFunction().getMetadata("arm64ec_unmangled_name", UnmangledNames);
1483 for (MDNode *Node : UnmangledNames) {
1484 StringRef NameStr = cast<MDString>(Node->getOperand(0))->getString();
1485 MCSymbol *UnmangledSym = MMI->getContext().getOrCreateSymbol(NameStr);
1486 if (std::optional<std::string> MangledName =
1487 getArm64ECMangledFunctionName(UnmangledSym->getName())) {
1488 MCSymbol *ECMangledSym =
1489 MMI->getContext().getOrCreateSymbol(*MangledName);
1490 emitFunctionAlias(UnmangledSym, ECMangledSym);
1491 }
1492 }
1493 if (MCSymbol *ECMangledSym =
1494 getSymbolFromMetadata("arm64ec_ecmangled_name"))
1495 emitFunctionAlias(ECMangledSym, CurrentFnSym);
1496 }
1497}
1498
1499void AArch64AsmPrinter::emitXXStructor(const DataLayout &DL,
1500 const Constant *CV) {
1501 LLVMContext &C = CV->getContext();
1503 "ctors/dtors are to be signed by asm printer");
1504
1505 if (PtrauthInitFini) {
1506 IntegerType *Int32Ty = IntegerType::get(C, 32);
1507 IntegerType *Int64Ty = IntegerType::get(C, 64);
1508 PointerType *PtrTy = PointerType::get(C, 0);
1509
1510 ConstantInt *Key = ConstantInt::get(Int32Ty, AArch64PAuth::InitFiniKey);
1511 ConstantInt *IntDisc = ConstantInt::get(
1514 Constant *AddressDisc = Null;
1515 if (PtrauthInitFiniAddressDisc) {
1517 AddressDisc =
1518 ConstantExpr::getIntToPtr(ConstantInt::get(Int64Ty, Marker), PtrTy);
1519 }
1520
1521 CV = ConstantPtrAuth::get(const_cast<Constant *>(CV), Key, IntDisc,
1522 AddressDisc, /*DeactivationSymbol=*/Null);
1523 }
1524
1525 // Signed pointers will be lowered by AArch64AsmPrinter::lowerConstantPtrAuth.
1527}
1528
1529void AArch64AsmPrinter::emitGlobalAlias(const Module &M,
1530 const GlobalAlias &GA) {
1531 if (auto F = dyn_cast_or_null<Function>(GA.getAliasee())) {
1532 // Global aliases must point to a definition, but unmangled patchable
1533 // symbols are special and need to point to an undefined symbol with "EXP+"
1534 // prefix. Such undefined symbol is resolved by the linker by creating
1535 // x86 thunk that jumps back to the actual EC target.
1536 if (MDNode *Node = F->getMetadata("arm64ec_exp_name")) {
1537 StringRef ExpStr = cast<MDString>(Node->getOperand(0))->getString();
1538 MCSymbol *ExpSym = MMI->getContext().getOrCreateSymbol(ExpStr);
1539 MCSymbol *Sym = MMI->getContext().getOrCreateSymbol(GA.getName());
1540
1541 OutStreamer->beginCOFFSymbolDef(ExpSym);
1542 OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
1543 OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_FUNCTION
1545 OutStreamer->endCOFFSymbolDef();
1546
1547 OutStreamer->beginCOFFSymbolDef(Sym);
1548 OutStreamer->emitCOFFSymbolStorageClass(COFF::IMAGE_SYM_CLASS_EXTERNAL);
1549 OutStreamer->emitCOFFSymbolType(COFF::IMAGE_SYM_DTYPE_FUNCTION
1551 OutStreamer->endCOFFSymbolDef();
1552 OutStreamer->emitSymbolAttribute(Sym, MCSA_Weak);
1553 OutStreamer->emitAssignment(
1554 Sym, MCSymbolRefExpr::create(ExpSym, MMI->getContext()));
1555 return;
1556 }
1557 }
1559}
1560
1561/// Small jump tables contain an unsigned byte or half, representing the offset
1562/// from the lowest-addressed possible destination to the desired basic
1563/// block. Since all instructions are 4-byte aligned, this is further compressed
1564/// by counting in instructions rather than bytes (i.e. divided by 4). So, to
1565/// materialize the correct destination we need:
1566///
1567/// adr xDest, .LBB0_0
1568/// ldrb wScratch, [xTable, xEntry] (with "lsl #1" for ldrh).
1569/// add xDest, xDest, xScratch (with "lsl #2" for smaller entries)
1570void AArch64AsmPrinter::LowerJumpTableDest(llvm::MCStreamer &OutStreamer,
1571 const llvm::MachineInstr &MI) {
1572 Register DestReg = MI.getOperand(0).getReg();
1573 Register ScratchReg = MI.getOperand(1).getReg();
1574 Register ScratchRegW =
1575 STI->getRegisterInfo()->getSubReg(ScratchReg, AArch64::sub_32);
1576 Register TableReg = MI.getOperand(2).getReg();
1577 Register EntryReg = MI.getOperand(3).getReg();
1578 int JTIdx = MI.getOperand(4).getIndex();
1579 int Size = AArch64FI->getJumpTableEntrySize(JTIdx);
1580
1581 // This has to be first because the compression pass based its reachability
1582 // calculations on the start of the JumpTableDest instruction.
1583 auto Label =
1584 MF->getInfo<AArch64FunctionInfo>()->getJumpTableEntryPCRelSymbol(JTIdx);
1585
1586 // If we don't already have a symbol to use as the base, use the ADR
1587 // instruction itself.
1588 if (!Label) {
1590 AArch64FI->setJumpTableEntryInfo(JTIdx, Size, Label);
1591 OutStreamer.emitLabel(Label);
1592 }
1593
1594 auto LabelExpr = MCSymbolRefExpr::create(Label, MF->getContext());
1595 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::ADR)
1596 .addReg(DestReg)
1597 .addExpr(LabelExpr));
1598
1599 // Load the number of instruction-steps to offset from the label.
1600 unsigned LdrOpcode;
1601 switch (Size) {
1602 case 1: LdrOpcode = AArch64::LDRBBroX; break;
1603 case 2: LdrOpcode = AArch64::LDRHHroX; break;
1604 case 4: LdrOpcode = AArch64::LDRSWroX; break;
1605 default:
1606 llvm_unreachable("Unknown jump table size");
1607 }
1608
1609 EmitToStreamer(OutStreamer, MCInstBuilder(LdrOpcode)
1610 .addReg(Size == 4 ? ScratchReg : ScratchRegW)
1611 .addReg(TableReg)
1612 .addReg(EntryReg)
1613 .addImm(0)
1614 .addImm(Size == 1 ? 0 : 1));
1615
1616 // Add to the already materialized base label address, multiplying by 4 if
1617 // compressed.
1618 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::ADDXrs)
1619 .addReg(DestReg)
1620 .addReg(DestReg)
1621 .addReg(ScratchReg)
1622 .addImm(Size == 4 ? 0 : 2));
1623}
1624
1625void AArch64AsmPrinter::LowerHardenedBRJumpTable(const MachineInstr &MI) {
1626 const MachineJumpTableInfo *MJTI = MF->getJumpTableInfo();
1627 assert(MJTI && "Can't lower jump-table dispatch without JTI");
1628
1629 const std::vector<MachineJumpTableEntry> &JTs = MJTI->getJumpTables();
1630 assert(!JTs.empty() && "Invalid JT index for jump-table dispatch");
1631
1632 // Emit:
1633 // mov x17, #<size of table> ; depending on table size, with MOVKs
1634 // cmp x16, x17 ; or #imm if table size fits in 12-bit
1635 // csel x16, x16, xzr, ls ; check for index overflow
1636 //
1637 // adrp x17, Ltable@PAGE ; materialize table address
1638 // add x17, Ltable@PAGEOFF
1639 // ldrsw x16, [x17, x16, lsl #2] ; load table entry
1640 //
1641 // Lanchor:
1642 // adr x17, Lanchor ; compute target address
1643 // add x16, x17, x16
1644 // br x16 ; branch to target
1645
1646 MachineOperand JTOp = MI.getOperand(0);
1647
1648 unsigned JTI = JTOp.getIndex();
1649 assert(!AArch64FI->getJumpTableEntryPCRelSymbol(JTI) &&
1650 "unsupported compressed jump table");
1651
1652 const uint64_t NumTableEntries = JTs[JTI].MBBs.size();
1653
1654 // cmp only supports a 12-bit immediate. If we need more, materialize the
1655 // immediate, using x17 as a scratch register.
1656 uint64_t MaxTableEntry = NumTableEntries - 1;
1657 if (isUInt<12>(MaxTableEntry)) {
1658 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXri)
1659 .addReg(AArch64::XZR)
1660 .addReg(AArch64::X16)
1661 .addImm(MaxTableEntry)
1662 .addImm(0));
1663 } else {
1664 emitMOVZ(AArch64::X17, static_cast<uint16_t>(MaxTableEntry), 0);
1665 // It's sad that we have to manually materialize instructions, but we can't
1666 // trivially reuse the main pseudo expansion logic.
1667 // A MOVK sequence is easy enough to generate and handles the general case.
1668 for (int Offset = 16; Offset < 64; Offset += 16) {
1669 if ((MaxTableEntry >> Offset) == 0)
1670 break;
1671 emitMOVK(AArch64::X17, static_cast<uint16_t>(MaxTableEntry >> Offset),
1672 Offset);
1673 }
1674 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::SUBSXrs)
1675 .addReg(AArch64::XZR)
1676 .addReg(AArch64::X16)
1677 .addReg(AArch64::X17)
1678 .addImm(0));
1679 }
1680
1681 // This picks entry #0 on failure.
1682 // We might want to trap instead.
1683 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::CSELXr)
1684 .addReg(AArch64::X16)
1685 .addReg(AArch64::X16)
1686 .addReg(AArch64::XZR)
1687 .addImm(AArch64CC::LS));
1688
1689 // Prepare the @PAGE/@PAGEOFF low/high operands.
1690 MachineOperand JTMOHi(JTOp), JTMOLo(JTOp);
1691 MCOperand JTMCHi, JTMCLo;
1692
1693 JTMOHi.setTargetFlags(AArch64II::MO_PAGE);
1694 JTMOLo.setTargetFlags(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
1695
1696 MCInstLowering.lowerOperand(JTMOHi, JTMCHi);
1697 MCInstLowering.lowerOperand(JTMOLo, JTMCLo);
1698
1699 EmitToStreamer(
1700 *OutStreamer,
1701 MCInstBuilder(AArch64::ADRP).addReg(AArch64::X17).addOperand(JTMCHi));
1702
1703 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXri)
1704 .addReg(AArch64::X17)
1705 .addReg(AArch64::X17)
1706 .addOperand(JTMCLo)
1707 .addImm(0));
1708
1709 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRSWroX)
1710 .addReg(AArch64::X16)
1711 .addReg(AArch64::X17)
1712 .addReg(AArch64::X16)
1713 .addImm(0)
1714 .addImm(1));
1715
1716 MCSymbol *AdrLabel = MF->getContext().createTempSymbol();
1717 const auto *AdrLabelE = MCSymbolRefExpr::create(AdrLabel, MF->getContext());
1718 AArch64FI->setJumpTableEntryInfo(JTI, 4, AdrLabel);
1719
1720 OutStreamer->emitLabel(AdrLabel);
1721 EmitToStreamer(
1722 *OutStreamer,
1723 MCInstBuilder(AArch64::ADR).addReg(AArch64::X17).addExpr(AdrLabelE));
1724
1725 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ADDXrs)
1726 .addReg(AArch64::X16)
1727 .addReg(AArch64::X17)
1728 .addReg(AArch64::X16)
1729 .addImm(0));
1730
1731 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::BR).addReg(AArch64::X16));
1732}
1733
1734void AArch64AsmPrinter::LowerMOPS(llvm::MCStreamer &OutStreamer,
1735 const llvm::MachineInstr &MI) {
1736 unsigned Opcode = MI.getOpcode();
1737 assert(STI->hasMOPS());
1738 assert(STI->hasMTE() || Opcode != AArch64::MOPSMemorySetTaggingPseudo);
1739
1740 const auto Ops = [Opcode]() -> std::array<unsigned, 3> {
1741 if (Opcode == AArch64::MOPSMemoryCopyPseudo)
1742 return {AArch64::CPYFP, AArch64::CPYFM, AArch64::CPYFE};
1743 if (Opcode == AArch64::MOPSMemoryMovePseudo)
1744 return {AArch64::CPYP, AArch64::CPYM, AArch64::CPYE};
1745 if (Opcode == AArch64::MOPSMemorySetPseudo)
1746 return {AArch64::SETP, AArch64::SETM, AArch64::SETE};
1747 if (Opcode == AArch64::MOPSMemorySetTaggingPseudo)
1748 return {AArch64::SETGP, AArch64::SETGM, AArch64::MOPSSETGE};
1749 llvm_unreachable("Unhandled memory operation pseudo");
1750 }();
1751 const bool IsSet = Opcode == AArch64::MOPSMemorySetPseudo ||
1752 Opcode == AArch64::MOPSMemorySetTaggingPseudo;
1753
1754 for (auto Op : Ops) {
1755 int i = 0;
1756 auto MCIB = MCInstBuilder(Op);
1757 // Destination registers
1758 MCIB.addReg(MI.getOperand(i++).getReg());
1759 MCIB.addReg(MI.getOperand(i++).getReg());
1760 if (!IsSet)
1761 MCIB.addReg(MI.getOperand(i++).getReg());
1762 // Input registers
1763 MCIB.addReg(MI.getOperand(i++).getReg());
1764 MCIB.addReg(MI.getOperand(i++).getReg());
1765 MCIB.addReg(MI.getOperand(i++).getReg());
1766
1767 EmitToStreamer(OutStreamer, MCIB);
1768 }
1769}
1770
1771void AArch64AsmPrinter::LowerSTACKMAP(MCStreamer &OutStreamer, StackMaps &SM,
1772 const MachineInstr &MI) {
1773 unsigned NumNOPBytes = StackMapOpers(&MI).getNumPatchBytes();
1774
1775 auto &Ctx = OutStreamer.getContext();
1776 MCSymbol *MILabel = Ctx.createTempSymbol();
1777 OutStreamer.emitLabel(MILabel);
1778
1779 SM.recordStackMap(*MILabel, MI);
1780 assert(NumNOPBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
1781
1782 // Scan ahead to trim the shadow.
1783 const MachineBasicBlock &MBB = *MI.getParent();
1785 ++MII;
1786 while (NumNOPBytes > 0) {
1787 if (MII == MBB.end() || MII->isCall() ||
1788 MII->getOpcode() == AArch64::DBG_VALUE ||
1789 MII->getOpcode() == TargetOpcode::PATCHPOINT ||
1790 MII->getOpcode() == TargetOpcode::STACKMAP)
1791 break;
1792 ++MII;
1793 NumNOPBytes -= 4;
1794 }
1795
1796 // Emit nops.
1797 for (unsigned i = 0; i < NumNOPBytes; i += 4)
1798 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::NOP));
1799}
1800
1801// Lower a patchpoint of the form:
1802// [<def>], <id>, <numBytes>, <target>, <numArgs>
1803void AArch64AsmPrinter::LowerPATCHPOINT(MCStreamer &OutStreamer, StackMaps &SM,
1804 const MachineInstr &MI) {
1805 auto &Ctx = OutStreamer.getContext();
1806 MCSymbol *MILabel = Ctx.createTempSymbol();
1807 OutStreamer.emitLabel(MILabel);
1808 SM.recordPatchPoint(*MILabel, MI);
1809
1810 PatchPointOpers Opers(&MI);
1811
1812 int64_t CallTarget = Opers.getCallTarget().getImm();
1813 unsigned EncodedBytes = 0;
1814 if (CallTarget) {
1815 assert((CallTarget & 0xFFFFFFFFFFFF) == CallTarget &&
1816 "High 16 bits of call target should be zero.");
1817 Register ScratchReg = MI.getOperand(Opers.getNextScratchIdx()).getReg();
1818 EncodedBytes = 16;
1819 // Materialize the jump address:
1820 emitMOVZ(ScratchReg, (CallTarget >> 32) & 0xFFFF, 32);
1821 emitMOVK(ScratchReg, (CallTarget >> 16) & 0xFFFF, 16);
1822 emitMOVK(ScratchReg, CallTarget & 0xFFFF, 0);
1823 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::BLR).addReg(ScratchReg));
1824 }
1825 // Emit padding.
1826 unsigned NumBytes = Opers.getNumPatchBytes();
1827 assert(NumBytes >= EncodedBytes &&
1828 "Patchpoint can't request size less than the length of a call.");
1829 assert((NumBytes - EncodedBytes) % 4 == 0 &&
1830 "Invalid number of NOP bytes requested!");
1831 for (unsigned i = EncodedBytes; i < NumBytes; i += 4)
1832 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::NOP));
1833}
1834
1835void AArch64AsmPrinter::LowerSTATEPOINT(MCStreamer &OutStreamer, StackMaps &SM,
1836 const MachineInstr &MI) {
1837 StatepointOpers SOpers(&MI);
1838 if (unsigned PatchBytes = SOpers.getNumPatchBytes()) {
1839 assert(PatchBytes % 4 == 0 && "Invalid number of NOP bytes requested!");
1840 for (unsigned i = 0; i < PatchBytes; i += 4)
1841 EmitToStreamer(OutStreamer, MCInstBuilder(AArch64::NOP));
1842 } else {
1843 // Lower call target and choose correct opcode
1844 const MachineOperand &CallTarget = SOpers.getCallTarget();
1845 MCOperand CallTargetMCOp;
1846 unsigned CallOpcode;
1847 switch (CallTarget.getType()) {
1850 MCInstLowering.lowerOperand(CallTarget, CallTargetMCOp);
1851 CallOpcode = AArch64::BL;
1852 break;
1854 CallTargetMCOp = MCOperand::createImm(CallTarget.getImm());
1855 CallOpcode = AArch64::BL;
1856 break;
1858 CallTargetMCOp = MCOperand::createReg(CallTarget.getReg());
1859 CallOpcode = AArch64::BLR;
1860 break;
1861 default:
1862 llvm_unreachable("Unsupported operand type in statepoint call target");
1863 break;
1864 }
1865
1866 EmitToStreamer(OutStreamer,
1867 MCInstBuilder(CallOpcode).addOperand(CallTargetMCOp));
1868 }
1869
1870 auto &Ctx = OutStreamer.getContext();
1871 MCSymbol *MILabel = Ctx.createTempSymbol();
1872 OutStreamer.emitLabel(MILabel);
1873 SM.recordStatepoint(*MILabel, MI);
1874}
1875
1876void AArch64AsmPrinter::LowerFAULTING_OP(const MachineInstr &FaultingMI) {
1877 // FAULTING_LOAD_OP <def>, <faltinf type>, <MBB handler>,
1878 // <opcode>, <operands>
1879
1880 Register DefRegister = FaultingMI.getOperand(0).getReg();
1882 static_cast<FaultMaps::FaultKind>(FaultingMI.getOperand(1).getImm());
1883 MCSymbol *HandlerLabel = FaultingMI.getOperand(2).getMBB()->getSymbol();
1884 unsigned Opcode = FaultingMI.getOperand(3).getImm();
1885 unsigned OperandsBeginIdx = 4;
1886
1887 auto &Ctx = OutStreamer->getContext();
1888 MCSymbol *FaultingLabel = Ctx.createTempSymbol();
1889 OutStreamer->emitLabel(FaultingLabel);
1890
1891 assert(FK < FaultMaps::FaultKindMax && "Invalid Faulting Kind!");
1892 FM.recordFaultingOp(FK, FaultingLabel, HandlerLabel);
1893
1894 MCInst MI;
1895 MI.setOpcode(Opcode);
1896
1897 if (DefRegister != (Register)0)
1898 MI.addOperand(MCOperand::createReg(DefRegister));
1899
1900 for (const MachineOperand &MO :
1901 llvm::drop_begin(FaultingMI.operands(), OperandsBeginIdx)) {
1902 MCOperand Dest;
1903 lowerOperand(MO, Dest);
1904 MI.addOperand(Dest);
1905 }
1906
1907 OutStreamer->AddComment("on-fault: " + HandlerLabel->getName());
1908 EmitToStreamer(MI);
1909}
1910
1911void AArch64AsmPrinter::emitMovXReg(Register Dest, Register Src) {
1912 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ORRXrs)
1913 .addReg(Dest)
1914 .addReg(AArch64::XZR)
1915 .addReg(Src)
1916 .addImm(0));
1917}
1918
1919void AArch64AsmPrinter::emitMOVZ(Register Dest, uint64_t Imm, unsigned Shift) {
1920 bool Is64Bit = AArch64::GPR64RegClass.contains(Dest);
1921 EmitToStreamer(*OutStreamer,
1922 MCInstBuilder(Is64Bit ? AArch64::MOVZXi : AArch64::MOVZWi)
1923 .addReg(Dest)
1924 .addImm(Imm)
1925 .addImm(Shift));
1926}
1927
1928void AArch64AsmPrinter::emitMOVK(Register Dest, uint64_t Imm, unsigned Shift) {
1929 bool Is64Bit = AArch64::GPR64RegClass.contains(Dest);
1930 EmitToStreamer(*OutStreamer,
1931 MCInstBuilder(Is64Bit ? AArch64::MOVKXi : AArch64::MOVKWi)
1932 .addReg(Dest)
1933 .addReg(Dest)
1934 .addImm(Imm)
1935 .addImm(Shift));
1936}
1937
1938void AArch64AsmPrinter::emitAUT(AArch64PACKey::ID Key, Register Pointer,
1939 Register Disc) {
1940 bool IsZeroDisc = Disc == AArch64::XZR;
1941 unsigned Opcode = getAUTOpcodeForKey(Key, IsZeroDisc);
1942
1943 // autiza x16 ; if IsZeroDisc
1944 // autia x16, x17 ; if !IsZeroDisc
1945 MCInst AUTInst;
1946 AUTInst.setOpcode(Opcode);
1947 AUTInst.addOperand(MCOperand::createReg(Pointer));
1948 AUTInst.addOperand(MCOperand::createReg(Pointer));
1949 if (!IsZeroDisc)
1950 AUTInst.addOperand(MCOperand::createReg(Disc));
1951
1952 EmitToStreamer(AUTInst);
1953}
1954
1955void AArch64AsmPrinter::emitPAC(AArch64PACKey::ID Key, Register Pointer,
1956 Register Disc) {
1957 bool IsZeroDisc = Disc == AArch64::XZR;
1958 unsigned Opcode = getPACOpcodeForKey(Key, IsZeroDisc);
1959
1960 // paciza x16 ; if IsZeroDisc
1961 // pacia x16, x17 ; if !IsZeroDisc
1962 MCInst PACInst;
1963 PACInst.setOpcode(Opcode);
1964 PACInst.addOperand(MCOperand::createReg(Pointer));
1965 PACInst.addOperand(MCOperand::createReg(Pointer));
1966 if (!IsZeroDisc)
1967 PACInst.addOperand(MCOperand::createReg(Disc));
1968
1969 EmitToStreamer(PACInst);
1970}
1971
1972void AArch64AsmPrinter::emitBLRA(bool IsCall, AArch64PACKey::ID Key,
1973 Register Target, Register Disc) {
1974 bool IsZeroDisc = Disc == AArch64::XZR;
1975 unsigned Opcode = getBranchOpcodeForKey(IsCall, Key, IsZeroDisc);
1976
1977 // blraaz x16 ; if IsZeroDisc
1978 // blraa x16, x17 ; if !IsZeroDisc
1979 MCInst Inst;
1980 Inst.setOpcode(Opcode);
1981 Inst.addOperand(MCOperand::createReg(Target));
1982 if (!IsZeroDisc)
1983 Inst.addOperand(MCOperand::createReg(Disc));
1984 EmitToStreamer(Inst);
1985}
1986
1987void AArch64AsmPrinter::emitFMov0(const MachineInstr &MI) {
1988 Register DestReg = MI.getOperand(0).getReg();
1989 if (!STI->hasZeroCycleZeroingFPWorkaround() && STI->isNeonAvailable()) {
1990 if (STI->hasZeroCycleZeroingFPR64()) {
1991 // Convert H/S register to corresponding D register
1992 const AArch64RegisterInfo *TRI = STI->getRegisterInfo();
1993 if (AArch64::FPR16RegClass.contains(DestReg))
1994 DestReg = TRI->getMatchingSuperReg(DestReg, AArch64::hsub,
1995 &AArch64::FPR64RegClass);
1996 else if (AArch64::FPR32RegClass.contains(DestReg))
1997 DestReg = TRI->getMatchingSuperReg(DestReg, AArch64::ssub,
1998 &AArch64::FPR64RegClass);
1999 else
2000 assert(AArch64::FPR64RegClass.contains(DestReg));
2001
2002 MCInst MOVI;
2003 MOVI.setOpcode(AArch64::MOVID);
2004 MOVI.addOperand(MCOperand::createReg(DestReg));
2006 EmitToStreamer(*OutStreamer, MOVI);
2007 ++NumZCZeroingInstrsFPR;
2008 } else if (STI->hasZeroCycleZeroingFPR128()) {
2009 // Convert H/S/D register to corresponding Q register
2010 const AArch64RegisterInfo *TRI = STI->getRegisterInfo();
2011 if (AArch64::FPR16RegClass.contains(DestReg)) {
2012 DestReg = TRI->getMatchingSuperReg(DestReg, AArch64::hsub,
2013 &AArch64::FPR128RegClass);
2014 } else if (AArch64::FPR32RegClass.contains(DestReg)) {
2015 DestReg = TRI->getMatchingSuperReg(DestReg, AArch64::ssub,
2016 &AArch64::FPR128RegClass);
2017 } else {
2018 assert(AArch64::FPR64RegClass.contains(DestReg));
2019 DestReg = TRI->getMatchingSuperReg(DestReg, AArch64::dsub,
2020 &AArch64::FPR128RegClass);
2021 }
2022
2023 MCInst MOVI;
2024 MOVI.setOpcode(AArch64::MOVIv2d_ns);
2025 MOVI.addOperand(MCOperand::createReg(DestReg));
2027 EmitToStreamer(*OutStreamer, MOVI);
2028 ++NumZCZeroingInstrsFPR;
2029 } else {
2030 emitFMov0AsFMov(MI, DestReg);
2031 }
2032 } else {
2033 emitFMov0AsFMov(MI, DestReg);
2034 }
2035}
2036
2037void AArch64AsmPrinter::emitFMov0AsFMov(const MachineInstr &MI,
2038 Register DestReg) {
2039 MCInst FMov;
2040 switch (MI.getOpcode()) {
2041 default:
2042 llvm_unreachable("Unexpected opcode");
2043 case AArch64::FMOVH0:
2044 FMov.setOpcode(STI->hasFullFP16() ? AArch64::FMOVWHr : AArch64::FMOVWSr);
2045 if (!STI->hasFullFP16())
2046 DestReg = (AArch64::S0 + (DestReg - AArch64::H0));
2047 FMov.addOperand(MCOperand::createReg(DestReg));
2048 FMov.addOperand(MCOperand::createReg(AArch64::WZR));
2049 break;
2050 case AArch64::FMOVS0:
2051 FMov.setOpcode(AArch64::FMOVWSr);
2052 FMov.addOperand(MCOperand::createReg(DestReg));
2053 FMov.addOperand(MCOperand::createReg(AArch64::WZR));
2054 break;
2055 case AArch64::FMOVD0:
2056 FMov.setOpcode(AArch64::FMOVXDr);
2057 FMov.addOperand(MCOperand::createReg(DestReg));
2058 FMov.addOperand(MCOperand::createReg(AArch64::XZR));
2059 break;
2060 }
2061 EmitToStreamer(*OutStreamer, FMov);
2062}
2063
2064Register AArch64AsmPrinter::emitPtrauthDiscriminator(uint64_t Disc,
2065 Register AddrDisc,
2066 Register ScratchReg,
2067 bool MayClobberAddrDisc) {
2068 assert(isPtrauthRegSafe(ScratchReg) &&
2069 "Safe scratch register must be provided by the caller");
2070 assert(isUInt<16>(Disc) && "Constant discriminator is too wide");
2071
2072 // So far we've used NoRegister in pseudos. Now we need real encodings.
2073 if (AddrDisc == AArch64::NoRegister)
2074 AddrDisc = AArch64::XZR;
2075
2076 // If there is no constant discriminator, there's no blend involved:
2077 // just use the address discriminator register as-is (XZR or not).
2078 if (!Disc)
2079 return AddrDisc;
2080
2081 // If there's only a constant discriminator, MOV it into the scratch register.
2082 if (AddrDisc == AArch64::XZR) {
2083 emitMOVZ(ScratchReg, Disc, 0);
2084 return ScratchReg;
2085 }
2086
2087 // If there are both, emit a blend into the scratch register.
2088
2089 // Check if we can save one MOV instruction.
2090 if (MayClobberAddrDisc && isPtrauthRegSafe(AddrDisc)) {
2091 ScratchReg = AddrDisc;
2092 } else {
2093 emitMovXReg(ScratchReg, AddrDisc);
2094 assert(ScratchReg != AddrDisc &&
2095 "Forbidden to clobber AddrDisc, but have to");
2096 }
2097
2098 emitMOVK(ScratchReg, Disc, 48);
2099 return ScratchReg;
2100}
2101
2102/// Emit a code sequence to check an authenticated pointer value.
2103///
2104/// This function emits a sequence of instructions that checks if TestedReg was
2105/// authenticated successfully. On success, execution continues at the next
2106/// instruction after the sequence.
2107///
2108/// The action performed on failure depends on the OnFailure argument:
2109/// * if OnFailure is not nullptr, control is transferred to that label after
2110/// clearing the PAC field
2111/// * otherwise, BRK instruction is emitted to generate an error
2112void AArch64AsmPrinter::emitPtrauthCheckAuthenticatedValue(
2113 Register TestedReg, Register ScratchReg, AArch64PACKey::ID Key,
2114 AArch64PAuth::AuthCheckMethod Method, const MCSymbol *OnFailure) {
2115 // Insert a sequence to check if authentication of TestedReg succeeded,
2116 // such as:
2117 //
2118 // - checked and clearing:
2119 // ; x16 is TestedReg, x17 is ScratchReg
2120 // mov x17, x16
2121 // xpaci x17
2122 // cmp x16, x17
2123 // b.eq Lsuccess
2124 // mov x16, x17
2125 // b Lend
2126 // Lsuccess:
2127 // ; skipped if authentication failed
2128 // Lend:
2129 // ...
2130 //
2131 // - checked and trapping:
2132 // mov x17, x16
2133 // xpaci x17
2134 // cmp x16, x17
2135 // b.eq Lsuccess
2136 // brk #<0xc470 + aut key>
2137 // Lsuccess:
2138 // ...
2139 //
2140 // See the documentation on AuthCheckMethod enumeration constants for
2141 // the specific code sequences that can be used to perform the check.
2143
2144 if (Method == AuthCheckMethod::None)
2145 return;
2146 if (Method == AuthCheckMethod::DummyLoad) {
2147 EmitToStreamer(MCInstBuilder(AArch64::LDRWui)
2148 .addReg(getWRegFromXReg(ScratchReg))
2149 .addReg(TestedReg)
2150 .addImm(0));
2151 assert(!OnFailure && "DummyLoad always traps on error");
2152 return;
2153 }
2154
2155 MCSymbol *SuccessSym = createTempSymbol("auth_success_");
2156 if (Method == AuthCheckMethod::XPAC || Method == AuthCheckMethod::XPACHint) {
2157 // mov Xscratch, Xtested
2158 emitMovXReg(ScratchReg, TestedReg);
2159
2160 if (Method == AuthCheckMethod::XPAC) {
2161 // xpac(i|d) Xscratch
2162 unsigned XPACOpc = getXPACOpcodeForKey(Key);
2163 EmitToStreamer(
2164 MCInstBuilder(XPACOpc).addReg(ScratchReg).addReg(ScratchReg));
2165 } else {
2166 // xpaclri
2167
2168 // Note that this method applies XPAC to TestedReg instead of ScratchReg.
2169 assert(TestedReg == AArch64::LR &&
2170 "XPACHint mode is only compatible with checking the LR register");
2172 "XPACHint mode is only compatible with I-keys");
2173 EmitToStreamer(MCInstBuilder(AArch64::XPACLRI));
2174 }
2175
2176 // cmp Xtested, Xscratch
2177 EmitToStreamer(MCInstBuilder(AArch64::SUBSXrs)
2178 .addReg(AArch64::XZR)
2179 .addReg(TestedReg)
2180 .addReg(ScratchReg)
2181 .addImm(0));
2182
2183 // b.eq Lsuccess
2184 EmitToStreamer(
2185 MCInstBuilder(AArch64::Bcc)
2186 .addImm(AArch64CC::EQ)
2187 .addExpr(MCSymbolRefExpr::create(SuccessSym, OutContext)));
2188 } else if (Method == AuthCheckMethod::HighBitsNoTBI) {
2189 // eor Xscratch, Xtested, Xtested, lsl #1
2190 EmitToStreamer(MCInstBuilder(AArch64::EORXrs)
2191 .addReg(ScratchReg)
2192 .addReg(TestedReg)
2193 .addReg(TestedReg)
2194 .addImm(1));
2195 // tbz Xscratch, #62, Lsuccess
2196 EmitToStreamer(
2197 MCInstBuilder(AArch64::TBZX)
2198 .addReg(ScratchReg)
2199 .addImm(62)
2200 .addExpr(MCSymbolRefExpr::create(SuccessSym, OutContext)));
2201 } else {
2202 llvm_unreachable("Unsupported check method");
2203 }
2204
2205 if (!OnFailure) {
2206 // Trapping sequences do a 'brk'.
2207 // brk #<0xc470 + aut key>
2208 EmitToStreamer(MCInstBuilder(AArch64::BRK).addImm(0xc470 | Key));
2209 } else {
2210 // Non-trapping checked sequences return the stripped result in TestedReg,
2211 // skipping over success-only code (such as re-signing the pointer) by
2212 // jumping to OnFailure label.
2213 // Note that this can introduce an authentication oracle (such as based on
2214 // the high bits of the re-signed value).
2215
2216 // FIXME: The XPAC method can be optimized by applying XPAC to TestedReg
2217 // instead of ScratchReg, thus eliminating one `mov` instruction.
2218 // Both XPAC and XPACHint can be further optimized by not using a
2219 // conditional branch jumping over an unconditional one.
2220
2221 switch (Method) {
2222 case AuthCheckMethod::XPACHint:
2223 // LR is already XPAC-ed at this point.
2224 break;
2225 case AuthCheckMethod::XPAC:
2226 // mov Xtested, Xscratch
2227 emitMovXReg(TestedReg, ScratchReg);
2228 break;
2229 default:
2230 // If Xtested was not XPAC-ed so far, emit XPAC here.
2231 // xpac(i|d) Xtested
2232 unsigned XPACOpc = getXPACOpcodeForKey(Key);
2233 EmitToStreamer(
2234 MCInstBuilder(XPACOpc).addReg(TestedReg).addReg(TestedReg));
2235 }
2236
2237 // b Lend
2238 const auto *OnFailureExpr = MCSymbolRefExpr::create(OnFailure, OutContext);
2239 EmitToStreamer(MCInstBuilder(AArch64::B).addExpr(OnFailureExpr));
2240 }
2241
2242 // If the auth check succeeds, we can continue.
2243 // Lsuccess:
2244 OutStreamer->emitLabel(SuccessSym);
2245}
2246
2247// With Pointer Authentication, it may be needed to explicitly check the
2248// authenticated value in LR before performing a tail call.
2249// Otherwise, the callee may re-sign the invalid return address,
2250// introducing a signing oracle.
2251void AArch64AsmPrinter::emitPtrauthTailCallHardening(const MachineInstr *TC) {
2252 if (!AArch64FI->shouldSignReturnAddress(*MF))
2253 return;
2254
2255 auto LRCheckMethod = STI->getAuthenticatedLRCheckMethod(*MF);
2256 if (LRCheckMethod == AArch64PAuth::AuthCheckMethod::None)
2257 return;
2258
2259 const AArch64RegisterInfo *TRI = STI->getRegisterInfo();
2260 Register ScratchReg =
2261 TC->readsRegister(AArch64::X16, TRI) ? AArch64::X17 : AArch64::X16;
2262 assert(!TC->readsRegister(ScratchReg, TRI) &&
2263 "Neither x16 nor x17 is available as a scratch register");
2266 emitPtrauthCheckAuthenticatedValue(AArch64::LR, ScratchReg, Key,
2267 LRCheckMethod);
2268}
2269
2270bool AArch64AsmPrinter::emitDeactivationSymbolRelocation(Value *DS) {
2271 if (!DS)
2272 return false;
2273
2274 if (isa<GlobalAlias>(DS)) {
2275 // Just emit the nop directly.
2276 EmitToStreamer(MCInstBuilder(AArch64::NOP));
2277 return true;
2278 }
2279 MCSymbol *Dot = OutContext.createTempSymbol();
2280 OutStreamer->emitLabel(Dot);
2281 const MCExpr *DeactDotExpr = MCSymbolRefExpr::create(Dot, OutContext);
2282
2283 const MCExpr *DSExpr = MCSymbolRefExpr::create(
2284 OutContext.getOrCreateSymbol(DS->getName()), OutContext);
2285 OutStreamer->emitRelocDirective(*DeactDotExpr, "R_AARCH64_PATCHINST", DSExpr,
2286 SMLoc());
2287 return false;
2288}
2289
2290AArch64AsmPrinter::PtrAuthSchema AArch64AsmPrinter::PtrAuthSchema::CreateImmReg(
2291 AArch64PACKey::ID Key, uint64_t IntDisc, const MachineOperand &AddrDiscOp) {
2292 PtrAuthSchema Schema;
2293 Schema.Key = Key;
2294 Schema.IntDisc = IntDisc;
2295 Schema.AddrDisc = AddrDiscOp.getReg();
2296 Schema.AddrDiscIsKilled = AddrDiscOp.isKill();
2297 Schema.PCDisc = AArch64::NoRegister;
2298 return Schema;
2299}
2300
2301AArch64AsmPrinter::PtrAuthSchema AArch64AsmPrinter::PtrAuthSchema::CreateRegReg(
2302 AArch64PACKey::ID Key, Register AddrDisc, Register PCDisc) {
2303 assert(PCDisc != AArch64::NoRegister &&
2304 "Use CreateImmReg for non-PC schemas");
2305 PtrAuthSchema Schema;
2306 Schema.Key = Key;
2307 Schema.IntDisc = 0;
2308 Schema.AddrDisc = AddrDisc;
2309 Schema.AddrDiscIsKilled = false;
2310 Schema.PCDisc = PCDisc;
2311 return Schema;
2312}
2313
2314void AArch64AsmPrinter::emitPtrauthApplyIndirectAddend(Register Pointer,
2315 Register Scratch,
2316 int64_t Addend) {
2317 if (isInt<9>(Addend)) {
2318 // ldrsw Scratch, [Pointer, #Addend]! ; note: Pointer+Addend is used later.
2319 EmitToStreamer(MCInstBuilder(AArch64::LDRSWpre)
2320 .addReg(Pointer)
2321 .addReg(Scratch)
2322 .addReg(Pointer)
2323 .addImm(/*simm9:*/ Addend));
2324 } else {
2325 // Pointer += Addend computation has 2 variants
2326 if (isUInt<24>(Addend)) {
2327 // Variant 1: add Pointer, Pointer, (Addend >> shift12) lsl shift12
2328 // This can take up to 2 instructions.
2329 for (int BitPos = 0; BitPos != 24 && (Addend >> BitPos); BitPos += 12) {
2330 EmitToStreamer(
2331 MCInstBuilder(AArch64::ADDXri)
2332 .addReg(Pointer)
2333 .addReg(Pointer)
2334 .addImm((Addend >> BitPos) & 0xfff)
2335 .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, BitPos)));
2336 }
2337 } else {
2338 // Variant 2: accumulate constant in Scratch 16 bits at a time,
2339 // and add it to Pointer. This can take 2-5 instructions.
2340 emitMOVZ(Scratch, Addend & 0xffff, 0);
2341 for (int Offset = 16; Offset < 64; Offset += 16) {
2342 if (unsigned Fragment = (Addend >> Offset) & 0xffff)
2343 emitMOVK(Scratch, Fragment, Offset);
2344 }
2345
2346 // add Pointer, Pointer, Scratch
2347 EmitToStreamer(MCInstBuilder(AArch64::ADDXrs)
2348 .addReg(Pointer)
2349 .addReg(Pointer)
2350 .addReg(Scratch)
2351 .addImm(0));
2352 }
2353 // ldrsw Scratch, [Pointer]
2354 EmitToStreamer(MCInstBuilder(AArch64::LDRSWui)
2355 .addReg(Scratch)
2356 .addReg(Pointer)
2357 .addImm(0));
2358 }
2359 // add Pointer, Pointer, Scratch
2360 EmitToStreamer(MCInstBuilder(AArch64::ADDXrs)
2361 .addReg(Pointer)
2362 .addReg(Pointer)
2363 .addReg(Scratch)
2364 .addImm(0));
2365}
2366
2369
2370 // If an override is passed via command line argument, just use that value.
2371 if (PtrauthAuthChecks.getNumOccurrences())
2372 return PtrauthAuthChecks;
2373
2374 // Otherwise, on an FPAC CPU, you get traps whether you want them or not:
2375 // there's no point in emitting checks or traps.
2376 if (STI.hasFPAC())
2378
2379 bool ShouldTrap = MF->getFunction().hasFnAttribute("ptrauth-auth-traps");
2381}
2382
2383// We expand non-signing AUT* pseudo instructions into a sequence of the form
2384//
2385// ; 1. Authenticate Pointer
2386//
2387// or
2388//
2389// ; 1. Authenticate Pointer
2390// ; 2. Check that Pointer is valid, trap otherwise
2391//
2392// We expand AUT*PAC pseudo instructions into a sequence of the form
2393// (with addend only applied if Addend argument is given):
2394//
2395// ; 1. Authenticate Pointer
2396// ; 3. Apply addend and sign Pointer
2397//
2398// or
2399//
2400// ; 1. Authenticate Pointer
2401// ; 2. Check that Pointer is valid, trap otherwise
2402// ; 3. Apply addend and sign Pointer
2403//
2404// or
2405//
2406// ; 1. Authenticate Pointer
2407// ; 2. Check that Pointer is valid, jump to .Lon_failure otherwise
2408// ; 3. Apply addend and sign Pointer
2409// .Lon_failure:
2410//
2411void AArch64AsmPrinter::emitPtrauthAuthResign(
2412 Register Pointer, Register Scratch, PtrAuthSchema AuthSchema,
2413 std::optional<PtrAuthSchema> SignSchema, std::optional<int64_t> Addend,
2414 Value *DS) {
2415 const PtrauthCheckMode CheckMode = getCheckMode(MF);
2416 const bool IsAuthWithPC = AuthSchema.PCDisc != AArch64::NoRegister;
2417 assert(!SignSchema || SignSchema->PCDisc == AArch64::NoRegister);
2418
2419 Register SignAddrDiscOrNone =
2420 SignSchema ? SignSchema->AddrDisc : AArch64::NoRegister;
2421
2422 // 1. Authenticate Pointer - this is the only common step.
2423 // It is more complex than signing because AUTI[AB]171615 may be used.
2424
2425 if (IsAuthWithPC) {
2426 assert(Pointer == AArch64::X17 && Scratch == AArch64::X16 &&
2427 "AUTPCPAC must use x17/x16 as Pointer/Scratch");
2428
2429 assert(AuthSchema.AddrDisc == AArch64::X16 &&
2430 "AUTPCPAC requires address discriminator in X16");
2431
2432 assert(AuthSchema.PCDisc == AArch64::X15 &&
2433 "AUTPCPAC requires PC discriminator in X15");
2434
2435 assert(AuthSchema.IntDisc == 0 && "AUTPCPAC does not support IntDisc");
2436
2437 assert((AuthSchema.Key == AArch64PACKey::IB ||
2438 AuthSchema.Key == AArch64PACKey::IA) &&
2439 "AUTPCPAC only supports AUT-ing with IA/IB");
2440
2441 if (!emitDeactivationSymbolRelocation(DS)) {
2442 unsigned AutOpc = (AuthSchema.Key == AArch64PACKey::IB)
2443 ? AArch64::AUTIB171615
2444 : AArch64::AUTIA171615;
2445 EmitToStreamer(MCInstBuilder(AutOpc));
2446 }
2447 } else {
2448 // emitPtrauthDiscriminator is allowed to clobber AuthSchema.AddrDisc as
2449 // long as it is not used past this point neither externally (the register
2450 // operand is "killed"), nor internally (it does not alias anything being
2451 // used later by this pseudo instruction).
2452 //
2453 // Note that, while rather unlikely, it is technically possible to use the
2454 // Pointer to compute its own discriminator.
2455 Register AUTDiscReg = emitPtrauthDiscriminator(
2456 AuthSchema.IntDisc, AuthSchema.AddrDisc, Scratch,
2457 AuthSchema.addrDiscIsKilledAndNoneOf({Pointer, SignAddrDiscOrNone}));
2458 if (!emitDeactivationSymbolRelocation(DS))
2459 emitAUT(AuthSchema.Key, Pointer, AUTDiscReg);
2460 }
2461
2462 // The other two steps are optional, define lambdas for them:
2463 // 2. Check that Pointer is valid, on failure jump to label or trap.
2464 auto EmitCheck = [&](MCSymbol *OnFailure = nullptr) {
2465 emitPtrauthCheckAuthenticatedValue(Pointer, Scratch, AuthSchema.Key,
2466 AArch64PAuth::AuthCheckMethod::XPAC,
2467 OnFailure);
2468 };
2469 // 3. Apply addend and sign Pointer.
2470 auto EmitResignOnSuccess = [&]() {
2471 if (Addend.has_value())
2472 emitPtrauthApplyIndirectAddend(Pointer, Scratch, *Addend);
2473
2474 assert(Pointer != SignSchema->AddrDisc && "Pointer is early-clobbered");
2475 Register PACDiscReg =
2476 emitPtrauthDiscriminator(SignSchema->IntDisc, SignSchema->AddrDisc,
2477 Scratch, SignSchema->AddrDiscIsKilled);
2478 emitPAC(SignSchema->Key, Pointer, PACDiscReg);
2479 };
2480
2481 // Emit checking and resigning as needed.
2482
2483 if (!SignSchema) {
2484 if (CheckMode == PtrauthCheckMode::Trap)
2485 EmitCheck();
2486 // For authentication-only pseudos, Poison is demoted to Unchecked.
2487 return;
2488 }
2489
2490 switch (CheckMode) {
2491 case Unchecked:
2492 EmitResignOnSuccess();
2493 break;
2494 case Trap:
2495 EmitCheck();
2496 EmitResignOnSuccess();
2497 break;
2498 case Poison:
2499 MCSymbol *OnFailure = createTempSymbol("resign_end_");
2500 EmitCheck(OnFailure);
2501 EmitResignOnSuccess();
2502 OutStreamer->emitLabel(OnFailure);
2503 break;
2504 }
2505}
2506
2507void AArch64AsmPrinter::emitPtrauthSign(const MachineInstr *MI) {
2508 Register Val = MI->getOperand(1).getReg();
2509 auto Key = (AArch64PACKey::ID)MI->getOperand(2).getImm();
2510 uint64_t Disc = MI->getOperand(3).getImm();
2511 Register AddrDisc = MI->getOperand(4).getReg();
2512 bool AddrDiscKilled = MI->getOperand(4).isKill();
2513
2514 // As long as at least one of Val and AddrDisc is in GPR64noip, a scratch
2515 // register is available.
2516 Register ScratchReg = Val == AArch64::X16 ? AArch64::X17 : AArch64::X16;
2517 assert(ScratchReg != AddrDisc &&
2518 "Neither X16 nor X17 is available as a scratch register");
2519
2520 // Compute pac discriminator
2521 Register DiscReg = emitPtrauthDiscriminator(
2522 Disc, AddrDisc, ScratchReg, /*MayClobberAddrDisc=*/AddrDiscKilled);
2523
2524 if (emitDeactivationSymbolRelocation(MI->getDeactivationSymbol()))
2525 return;
2526
2527 emitPAC(Key, Val, DiscReg);
2528}
2529
2530void AArch64AsmPrinter::emitPtrauthBranch(const MachineInstr *MI) {
2531 bool IsCall = MI->getOpcode() == AArch64::BLRA;
2532 unsigned BrTarget = MI->getOperand(0).getReg();
2533
2534 auto Key = (AArch64PACKey::ID)MI->getOperand(1).getImm();
2535 uint64_t Disc = MI->getOperand(2).getImm();
2536
2537 unsigned AddrDisc = MI->getOperand(3).getReg();
2538
2539 // Make sure AddrDisc is solely used to compute the discriminator.
2540 // While hardly meaningful, it is still possible to describe an authentication
2541 // of a pointer against its own value (instead of storage address) with
2542 // intrinsics, so use report_fatal_error instead of assert.
2543 if (BrTarget == AddrDisc)
2544 report_fatal_error("Branch target is signed with its own value");
2545
2546 // If we are printing BLRA pseudo, try to save one MOV by making use of the
2547 // fact that x16 and x17 are described as clobbered by the MI instruction and
2548 // AddrDisc is not used as any other input.
2549 //
2550 // Back in the day, emitPtrauthDiscriminator was restricted to only returning
2551 // either x16 or x17, meaning the returned register is always among the
2552 // implicit-def'ed registers of BLRA pseudo. Now this property can be violated
2553 // if isX16X17Safer predicate is false, thus manually check if AddrDisc is
2554 // among x16 and x17 to prevent clobbering unexpected registers.
2555 //
2556 // Unlike BLRA, BRA pseudo is used to perform computed goto, and thus not
2557 // declared as clobbering x16/x17.
2558 //
2559 // FIXME: Make use of `killed` flags and register masks instead.
2560 bool AddrDiscIsImplicitDef =
2561 IsCall && (AddrDisc == AArch64::X16 || AddrDisc == AArch64::X17);
2562 Register DiscReg = emitPtrauthDiscriminator(Disc, AddrDisc, AArch64::X17,
2563 AddrDiscIsImplicitDef);
2564 emitBLRA(IsCall, Key, BrTarget, DiscReg);
2565}
2566
2567void AArch64AsmPrinter::emitAddImm(MCRegister Reg, int64_t Addend,
2568 MCRegister Tmp) {
2569 if (Addend != 0) {
2570 const uint64_t AbsOffset = (Addend > 0 ? Addend : -((uint64_t)Addend));
2571 const bool IsNeg = Addend < 0;
2572 if (isUInt<24>(AbsOffset)) {
2573 for (int BitPos = 0; BitPos != 24 && (AbsOffset >> BitPos);
2574 BitPos += 12) {
2575 EmitToStreamer(
2576 MCInstBuilder(IsNeg ? AArch64::SUBXri : AArch64::ADDXri)
2577 .addReg(Reg)
2578 .addReg(Reg)
2579 .addImm((AbsOffset >> BitPos) & 0xfff)
2580 .addImm(AArch64_AM::getShifterImm(AArch64_AM::LSL, BitPos)));
2581 }
2582 } else {
2583 const uint64_t UAddend = Addend;
2584 EmitToStreamer(MCInstBuilder(IsNeg ? AArch64::MOVNXi : AArch64::MOVZXi)
2585 .addReg(Tmp)
2586 .addImm((IsNeg ? ~UAddend : UAddend) & 0xffff)
2587 .addImm(/*shift=*/0));
2588 auto NeedMovk = [IsNeg, UAddend](int BitPos) -> bool {
2589 assert(BitPos == 16 || BitPos == 32 || BitPos == 48);
2590 uint64_t Shifted = UAddend >> BitPos;
2591 if (!IsNeg)
2592 return Shifted != 0;
2593 for (int I = 0; I != 64 - BitPos; I += 16)
2594 if (((Shifted >> I) & 0xffff) != 0xffff)
2595 return true;
2596 return false;
2597 };
2598 for (int BitPos = 16; BitPos != 64 && NeedMovk(BitPos); BitPos += 16)
2599 emitMOVK(Tmp, (UAddend >> BitPos) & 0xffff, BitPos);
2600
2601 EmitToStreamer(MCInstBuilder(AArch64::ADDXrs)
2602 .addReg(Reg)
2603 .addReg(Reg)
2604 .addReg(Tmp)
2605 .addImm(/*shift=*/0));
2606 }
2607 }
2608}
2609
2610void AArch64AsmPrinter::emitAddress(MCRegister Reg, const MCExpr *Expr,
2611 MCRegister Tmp, bool DSOLocal,
2612 const MCSubtargetInfo &STI) {
2613 MCValue Val;
2614 if (!Expr->evaluateAsRelocatable(Val, nullptr))
2615 report_fatal_error("emitAddress could not evaluate");
2616 if (DSOLocal) {
2617 EmitToStreamer(
2618 MCInstBuilder(AArch64::ADRP)
2619 .addReg(Reg)
2621 OutStreamer->getContext())));
2622 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
2623 .addReg(Reg)
2624 .addReg(Reg)
2625 .addExpr(MCSpecifierExpr::create(
2626 Expr, AArch64::S_LO12, OutStreamer->getContext()))
2627 .addImm(0));
2628 } else {
2629 auto *SymRef =
2630 MCSymbolRefExpr::create(Val.getAddSym(), OutStreamer->getContext());
2631 EmitToStreamer(
2632 MCInstBuilder(AArch64::ADRP)
2633 .addReg(Reg)
2635 OutStreamer->getContext())));
2636 EmitToStreamer(
2637 MCInstBuilder(AArch64::LDRXui)
2638 .addReg(Reg)
2639 .addReg(Reg)
2641 OutStreamer->getContext())));
2642 emitAddImm(Reg, Val.getConstant(), Tmp);
2643 }
2644}
2645
2647 // IFUNCs are ELF-only.
2648 if (!TT.isOSBinFormatELF())
2649 return false;
2650
2651 // IFUNCs are supported on glibc, bionic, and some but not all of the BSDs.
2652 return TT.isOSGlibc() || TT.isAndroid() || TT.isOSFreeBSD() ||
2653 TT.isOSDragonFly() || TT.isOSNetBSD();
2654}
2655
2656// Emit an ifunc resolver that returns a signed pointer to the specified target,
2657// and return a FUNCINIT reference to the resolver. In the linked binary, this
2658// function becomes the target of an IRELATIVE relocation. This resolver is used
2659// to relocate signed pointers in global variable initializers in special cases
2660// where the standard R_AARCH64_AUTH_ABS64 relocation would not work.
2661//
2662// Example (signed null pointer, not address discriminated):
2663//
2664// .8byte .Lpauth_ifunc0
2665// .pushsection .text.startup,"ax",@progbits
2666// .Lpauth_ifunc0:
2667// mov x0, #0
2668// mov x1, #12345
2669// b __emupac_pacda
2670//
2671// Example (signed null pointer, address discriminated):
2672//
2673// .Ltmp:
2674// .8byte .Lpauth_ifunc0
2675// .pushsection .text.startup,"ax",@progbits
2676// .Lpauth_ifunc0:
2677// mov x0, #0
2678// adrp x1, .Ltmp
2679// add x1, x1, :lo12:.Ltmp
2680// b __emupac_pacda
2681// .popsection
2682//
2683// Example (signed pointer to symbol, not address discriminated):
2684//
2685// .Ltmp:
2686// .8byte .Lpauth_ifunc0
2687// .pushsection .text.startup,"ax",@progbits
2688// .Lpauth_ifunc0:
2689// adrp x0, symbol
2690// add x0, x0, :lo12:symbol
2691// mov x1, #12345
2692// b __emupac_pacda
2693// .popsection
2694//
2695// Example (signed null pointer, not address discriminated, with deactivation
2696// symbol ds):
2697//
2698// .8byte .Lpauth_ifunc0
2699// .pushsection .text.startup,"ax",@progbits
2700// .Lpauth_ifunc0:
2701// mov x0, #0
2702// mov x1, #12345
2703// .reloc ., R_AARCH64_PATCHINST, ds
2704// b __emupac_pacda
2705// ret
2706// .popsection
2707const MCExpr *AArch64AsmPrinter::emitPAuthRelocationAsIRelative(
2708 const MCExpr *Target, uint64_t Disc, AArch64PACKey::ID KeyID,
2709 bool HasAddressDiversity, bool IsDSOLocal, const MCExpr *DSExpr) {
2710 const Triple &TT = TM.getTargetTriple();
2711
2712 // We only emit an IRELATIVE relocation if the target supports IRELATIVE.
2714 return nullptr;
2715
2716 // For now, only the DA key is supported.
2717 if (KeyID != AArch64PACKey::DA)
2718 return nullptr;
2719
2720 // AArch64Subtarget is huge, so heap allocate it so we don't run out of stack
2721 // space.
2722 auto STI = std::make_unique<AArch64Subtarget>(
2723 TT, TM.getTargetCPU(), TM.getTargetCPU(), TM.getTargetFeatureString(), TM,
2724 true);
2725 this->STI = STI.get();
2726
2727 MCSymbol *Place = OutStreamer->getContext().createTempSymbol();
2728 OutStreamer->emitLabel(Place);
2729 OutStreamer->pushSection();
2730
2731 const MCSymbolELF *Group =
2732 static_cast<MCSectionELF *>(OutStreamer->getCurrentSectionOnly())
2733 ->getGroup();
2735 if (Group)
2737 OutStreamer->switchSection(OutStreamer->getContext().getELFSection(
2738 ".text.startup", ELF::SHT_PROGBITS, Flags, 0, Group, true,
2739 Group ? MCSection::NonUniqueID : PAuthIFuncNextUniqueID++, nullptr));
2740
2741 MCSymbol *IRelativeSym =
2742 OutStreamer->getContext().createLinkerPrivateSymbol("pauth_ifunc");
2743 OutStreamer->emitLabel(IRelativeSym);
2744 if (isa<MCConstantExpr>(Target)) {
2745 OutStreamer->emitInstruction(MCInstBuilder(AArch64::MOVZXi)
2746 .addReg(AArch64::X0)
2747 .addExpr(Target)
2748 .addImm(0),
2749 *STI);
2750 } else {
2751 emitAddress(AArch64::X0, Target, AArch64::X16, IsDSOLocal, *STI);
2752 }
2753 if (HasAddressDiversity) {
2754 auto *PlacePlusDisc = MCBinaryExpr::createAdd(
2755 MCSymbolRefExpr::create(Place, OutStreamer->getContext()),
2756 MCConstantExpr::create(Disc, OutStreamer->getContext()),
2757 OutStreamer->getContext());
2758 emitAddress(AArch64::X1, PlacePlusDisc, AArch64::X16, /*IsDSOLocal=*/true,
2759 *STI);
2760 } else {
2761 if (!isUInt<16>(Disc)) {
2762 OutContext.reportError(SMLoc(), "AArch64 PAC Discriminator '" +
2763 Twine(Disc) +
2764 "' out of range [0, 0xFFFF]");
2765 }
2766 emitMOVZ(AArch64::X1, Disc, 0);
2767 }
2768
2769 if (DSExpr) {
2770 MCSymbol *PrePACInst = OutStreamer->getContext().createTempSymbol();
2771 OutStreamer->emitLabel(PrePACInst);
2772
2773 auto *PrePACInstExpr =
2774 MCSymbolRefExpr::create(PrePACInst, OutStreamer->getContext());
2775 OutStreamer->emitRelocDirective(*PrePACInstExpr, "R_AARCH64_PATCHINST",
2776 DSExpr, SMLoc());
2777 }
2778
2779 // We don't know the subtarget because this is being emitted for a global
2780 // initializer. Because the performance of IFUNC resolvers is unimportant, we
2781 // always call the EmuPAC runtime, which will end up using the PAC instruction
2782 // if the target supports PAC.
2783 MCSymbol *EmuPAC =
2784 OutStreamer->getContext().getOrCreateSymbol("__emupac_pacda");
2785 const MCSymbolRefExpr *EmuPACRef =
2786 MCSymbolRefExpr::create(EmuPAC, OutStreamer->getContext());
2787 OutStreamer->emitInstruction(MCInstBuilder(AArch64::B).addExpr(EmuPACRef),
2788 *STI);
2789
2790 // We need a RET despite the above tail call because the deactivation symbol
2791 // may replace the tail call with a NOP.
2792 if (DSExpr)
2793 OutStreamer->emitInstruction(
2794 MCInstBuilder(AArch64::RET).addReg(AArch64::LR), *STI);
2795 OutStreamer->popSection();
2796
2798 MCSymbolRefExpr::create(IRelativeSym, OutStreamer->getContext()),
2799 AArch64::S_FUNCINIT, OutStreamer->getContext());
2800}
2801
2802const MCExpr *
2803AArch64AsmPrinter::lowerConstantPtrAuth(const ConstantPtrAuth &CPA) {
2804 MCContext &Ctx = OutContext;
2805
2806 // Figure out the base symbol and the addend, if any.
2807 APInt Offset(64, 0);
2808 const Value *BaseGV = CPA.getPointer()->stripAndAccumulateConstantOffsets(
2809 getDataLayout(), Offset, /*AllowNonInbounds=*/true);
2810
2811 auto *BaseGVB = dyn_cast<GlobalValue>(BaseGV);
2812
2813 const MCExpr *Sym;
2814 if (BaseGVB) {
2815 // If there is an addend, turn that into the appropriate MCExpr.
2816 Sym = MCSymbolRefExpr::create(getSymbol(BaseGVB), Ctx);
2817 if (Offset.sgt(0))
2819 Sym, MCConstantExpr::create(Offset.getSExtValue(), Ctx), Ctx);
2820 else if (Offset.slt(0))
2822 Sym, MCConstantExpr::create((-Offset).getSExtValue(), Ctx), Ctx);
2823 } else if (isa<ConstantPointerNull>(BaseGV)) {
2824 Sym = MCConstantExpr::create(Offset.getSExtValue(), Ctx);
2825 } else {
2826 reportFatalUsageError("unsupported constant expression in ptrauth pointer");
2827 }
2828
2829 const MCExpr *DSExpr = nullptr;
2830 if (auto *DS = dyn_cast<GlobalValue>(CPA.getDeactivationSymbol())) {
2831 if (isa<GlobalAlias>(DS))
2832 return Sym;
2833 DSExpr = MCSymbolRefExpr::create(getSymbol(DS), Ctx);
2834 }
2835
2836 uint64_t KeyID = CPA.getKey()->getZExtValue();
2837 // We later rely on valid KeyID value in AArch64PACKeyIDToString call from
2838 // AArch64AuthMCExpr::printImpl, so fail fast.
2839 if (KeyID > AArch64PACKey::LAST) {
2840 CPA.getContext().emitError("AArch64 PAC Key ID '" + Twine(KeyID) +
2841 "' out of range [0, " +
2842 Twine((unsigned)AArch64PACKey::LAST) + "]");
2843 KeyID = 0;
2844 }
2845
2846 uint64_t Disc = CPA.getDiscriminator()->getZExtValue();
2847
2848 // Check if we can represent this with an IRELATIVE and emit it if so.
2849 if (auto *IFuncSym = emitPAuthRelocationAsIRelative(
2850 Sym, Disc, AArch64PACKey::ID(KeyID), CPA.hasAddressDiscriminator(),
2851 BaseGVB && BaseGVB->isDSOLocal(), DSExpr))
2852 return IFuncSym;
2853
2854 if (!isUInt<16>(Disc)) {
2855 CPA.getContext().emitError("AArch64 PAC Discriminator '" + Twine(Disc) +
2856 "' out of range [0, 0xFFFF]");
2857 Disc = 0;
2858 }
2859
2860 if (DSExpr)
2861 report_fatal_error("deactivation symbols unsupported in constant "
2862 "expressions on this target");
2863
2864 // Finally build the complete @AUTH expr.
2865 return AArch64AuthMCExpr::create(Sym, Disc, AArch64PACKey::ID(KeyID),
2866 CPA.hasAddressDiscriminator(), Ctx);
2867}
2868
2869void AArch64AsmPrinter::LowerLOADauthptrstatic(const MachineInstr &MI) {
2870 unsigned DstReg = MI.getOperand(0).getReg();
2871 const MachineOperand &GAOp = MI.getOperand(1);
2872 const uint64_t KeyC = MI.getOperand(2).getImm();
2873 assert(KeyC <= AArch64PACKey::LAST &&
2874 "key is out of range [0, AArch64PACKey::LAST]");
2875 const auto Key = (AArch64PACKey::ID)KeyC;
2876 const uint64_t Disc = MI.getOperand(3).getImm();
2877 assert(isUInt<16>(Disc) &&
2878 "constant discriminator is out of range [0, 0xffff]");
2879
2880 // Emit instruction sequence like the following:
2881 // ADRP x16, symbol$auth_ptr$key$disc
2882 // LDR x16, [x16, :lo12:symbol$auth_ptr$key$disc]
2883 //
2884 // Where the $auth_ptr$ symbol is the stub slot containing the signed pointer
2885 // to symbol.
2886 MCSymbol *AuthPtrStubSym;
2887 if (TM.getTargetTriple().isOSBinFormatELF()) {
2888 const auto &TLOF =
2889 static_cast<const AArch64_ELFTargetObjectFile &>(getObjFileLowering());
2890
2891 assert(GAOp.getOffset() == 0 &&
2892 "non-zero offset for $auth_ptr$ stub slots is not supported");
2893 const MCSymbol *GASym = TM.getSymbol(GAOp.getGlobal());
2894 AuthPtrStubSym = TLOF.getAuthPtrSlotSymbol(TM, MMI, GASym, Key, Disc);
2895 } else {
2896 assert(TM.getTargetTriple().isOSBinFormatMachO() &&
2897 "LOADauthptrstatic is implemented only for MachO/ELF");
2898
2899 const auto &TLOF = static_cast<const AArch64_MachoTargetObjectFile &>(
2900 getObjFileLowering());
2901
2902 assert(GAOp.getOffset() == 0 &&
2903 "non-zero offset for $auth_ptr$ stub slots is not supported");
2904 const MCSymbol *GASym = TM.getSymbol(GAOp.getGlobal());
2905 AuthPtrStubSym = TLOF.getAuthPtrSlotSymbol(TM, MMI, GASym, Key, Disc);
2906 }
2907
2908 MachineOperand StubMOHi =
2910 MachineOperand StubMOLo = MachineOperand::CreateMCSymbol(
2911 AuthPtrStubSym, AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
2912 MCOperand StubMCHi, StubMCLo;
2913
2914 MCInstLowering.lowerOperand(StubMOHi, StubMCHi);
2915 MCInstLowering.lowerOperand(StubMOLo, StubMCLo);
2916
2917 EmitToStreamer(
2918 *OutStreamer,
2919 MCInstBuilder(AArch64::ADRP).addReg(DstReg).addOperand(StubMCHi));
2920
2921 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::LDRXui)
2922 .addReg(DstReg)
2923 .addReg(DstReg)
2924 .addOperand(StubMCLo));
2925}
2926
2927void AArch64AsmPrinter::LowerMOVaddrPAC(const MachineInstr &MI) {
2928 const bool IsGOTLoad = MI.getOpcode() == AArch64::LOADgotPAC;
2929 const bool IsELFSignedGOT = MI.getParent()
2930 ->getParent()
2931 ->getInfo<AArch64FunctionInfo>()
2932 ->hasELFSignedGOT();
2933 MachineOperand GAOp = MI.getOperand(0);
2934 const uint64_t KeyC = MI.getOperand(1).getImm();
2935 assert(KeyC <= AArch64PACKey::LAST &&
2936 "key is out of range [0, AArch64PACKey::LAST]");
2937 const auto Key = (AArch64PACKey::ID)KeyC;
2938 const unsigned AddrDisc = MI.getOperand(2).getReg();
2939 const uint64_t Disc = MI.getOperand(3).getImm();
2940
2941 const int64_t Offset = GAOp.getOffset();
2942 GAOp.setOffset(0);
2943
2944 // Emit:
2945 // target materialization:
2946 // - via GOT:
2947 // - unsigned GOT:
2948 // adrp x16, :got:target
2949 // ldr x16, [x16, :got_lo12:target]
2950 // add offset to x16 if offset != 0
2951 // - ELF signed GOT:
2952 // adrp x17, :got:target
2953 // add x17, x17, :got_auth_lo12:target
2954 // ldr x16, [x17]
2955 // aut{i|d}a x16, x17
2956 // check+trap sequence (if no FPAC)
2957 // add offset to x16 if offset != 0
2958 //
2959 // - direct:
2960 // adrp x16, target
2961 // add x16, x16, :lo12:target
2962 // add offset to x16 if offset != 0
2963 //
2964 // add offset to x16:
2965 // - abs(offset) fits 24 bits:
2966 // add/sub x16, x16, #<offset>[, #lsl 12] (up to 2 instructions)
2967 // - abs(offset) does not fit 24 bits:
2968 // - offset < 0:
2969 // movn+movk sequence filling x17 register with the offset (up to 4
2970 // instructions)
2971 // add x16, x16, x17
2972 // - offset > 0:
2973 // movz+movk sequence filling x17 register with the offset (up to 4
2974 // instructions)
2975 // add x16, x16, x17
2976 //
2977 // signing:
2978 // - 0 discriminator:
2979 // paciza x16
2980 // - Non-0 discriminator, no address discriminator:
2981 // mov x17, #Disc
2982 // pacia x16, x17
2983 // - address discriminator (with potentially folded immediate discriminator):
2984 // pacia x16, xAddrDisc
2985
2986 MachineOperand GAMOHi(GAOp), GAMOLo(GAOp);
2987 MCOperand GAMCHi, GAMCLo;
2988
2989 GAMOHi.setTargetFlags(AArch64II::MO_PAGE);
2990 GAMOLo.setTargetFlags(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
2991 if (IsGOTLoad) {
2992 GAMOHi.addTargetFlag(AArch64II::MO_GOT);
2993 GAMOLo.addTargetFlag(AArch64II::MO_GOT);
2994 }
2995
2996 MCInstLowering.lowerOperand(GAMOHi, GAMCHi);
2997 MCInstLowering.lowerOperand(GAMOLo, GAMCLo);
2998
2999 EmitToStreamer(
3000 MCInstBuilder(AArch64::ADRP)
3001 .addReg(IsGOTLoad && IsELFSignedGOT ? AArch64::X17 : AArch64::X16)
3002 .addOperand(GAMCHi));
3003
3004 if (IsGOTLoad) {
3005 if (IsELFSignedGOT) {
3006 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
3007 .addReg(AArch64::X17)
3008 .addReg(AArch64::X17)
3009 .addOperand(GAMCLo)
3010 .addImm(0));
3011
3012 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
3013 .addReg(AArch64::X16)
3014 .addReg(AArch64::X17)
3015 .addImm(0));
3016
3017 assert(GAOp.isGlobal());
3018 assert(GAOp.getGlobal()->getValueType() != nullptr);
3019
3020 bool IsFunctionTy = GAOp.getGlobal()->getValueType()->isFunctionTy();
3021 auto AuthKey = IsFunctionTy ? AArch64PACKey::IA : AArch64PACKey::DA;
3022 emitAUT(AuthKey, AArch64::X16, AArch64::X17);
3023
3024 if (!STI->hasFPAC())
3025 emitPtrauthCheckAuthenticatedValue(AArch64::X16, AArch64::X17, AuthKey,
3026 AArch64PAuth::AuthCheckMethod::XPAC);
3027 } else {
3028 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
3029 .addReg(AArch64::X16)
3030 .addReg(AArch64::X16)
3031 .addOperand(GAMCLo));
3032 }
3033 } else {
3034 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
3035 .addReg(AArch64::X16)
3036 .addReg(AArch64::X16)
3037 .addOperand(GAMCLo)
3038 .addImm(0));
3039 }
3040
3041 emitAddImm(AArch64::X16, Offset, AArch64::X17);
3042 Register DiscReg = emitPtrauthDiscriminator(Disc, AddrDisc, AArch64::X17);
3043
3044 emitPAC(Key, AArch64::X16, DiscReg);
3045}
3046
3047void AArch64AsmPrinter::LowerLOADgotAUTH(const MachineInstr &MI) {
3048 Register DstReg = MI.getOperand(0).getReg();
3049 Register AuthResultReg = STI->hasFPAC() ? DstReg : AArch64::X16;
3050 const MachineOperand &GAMO = MI.getOperand(1);
3051 assert(GAMO.getOffset() == 0);
3052
3053 if (MI.getMF()->getTarget().getCodeModel() == CodeModel::Tiny) {
3054 MCOperand GAMC;
3055 MCInstLowering.lowerOperand(GAMO, GAMC);
3056 EmitToStreamer(
3057 MCInstBuilder(AArch64::ADR).addReg(AArch64::X17).addOperand(GAMC));
3058 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
3059 .addReg(AuthResultReg)
3060 .addReg(AArch64::X17)
3061 .addImm(0));
3062 } else {
3063 MachineOperand GAHiOp(GAMO);
3064 MachineOperand GALoOp(GAMO);
3065 GAHiOp.addTargetFlag(AArch64II::MO_PAGE);
3066 GALoOp.addTargetFlag(AArch64II::MO_PAGEOFF | AArch64II::MO_NC);
3067
3068 MCOperand GAMCHi, GAMCLo;
3069 MCInstLowering.lowerOperand(GAHiOp, GAMCHi);
3070 MCInstLowering.lowerOperand(GALoOp, GAMCLo);
3071
3072 EmitToStreamer(
3073 MCInstBuilder(AArch64::ADRP).addReg(AArch64::X17).addOperand(GAMCHi));
3074
3075 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
3076 .addReg(AArch64::X17)
3077 .addReg(AArch64::X17)
3078 .addOperand(GAMCLo)
3079 .addImm(0));
3080
3081 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
3082 .addReg(AuthResultReg)
3083 .addReg(AArch64::X17)
3084 .addImm(0));
3085 }
3086
3087 assert(GAMO.isGlobal());
3088 MCSymbol *UndefWeakSym;
3089 if (GAMO.getGlobal()->hasExternalWeakLinkage()) {
3090 UndefWeakSym = createTempSymbol("undef_weak");
3091 EmitToStreamer(
3092 MCInstBuilder(AArch64::CBZX)
3093 .addReg(AuthResultReg)
3094 .addExpr(MCSymbolRefExpr::create(UndefWeakSym, OutContext)));
3095 }
3096
3097 assert(GAMO.getGlobal()->getValueType() != nullptr);
3098
3099 bool IsFunctionTy = GAMO.getGlobal()->getValueType()->isFunctionTy();
3100 auto AuthKey = IsFunctionTy ? AArch64PACKey::IA : AArch64PACKey::DA;
3101 emitAUT(AuthKey, AuthResultReg, AArch64::X17);
3102
3103 if (GAMO.getGlobal()->hasExternalWeakLinkage())
3104 OutStreamer->emitLabel(UndefWeakSym);
3105
3106 if (!STI->hasFPAC()) {
3107 emitPtrauthCheckAuthenticatedValue(AuthResultReg, AArch64::X17, AuthKey,
3108 AArch64PAuth::AuthCheckMethod::XPAC);
3109
3110 emitMovXReg(DstReg, AuthResultReg);
3111 }
3112}
3113
3114const MCExpr *
3115AArch64AsmPrinter::lowerBlockAddressConstant(const BlockAddress &BA) {
3116 const MCExpr *BAE = AsmPrinter::lowerBlockAddressConstant(BA);
3117 const Function &Fn = *BA.getFunction();
3118
3119 if (std::optional<uint16_t> BADisc =
3120 STI->getPtrAuthBlockAddressDiscriminatorIfEnabled(Fn))
3121 return AArch64AuthMCExpr::create(BAE, *BADisc, AArch64PACKey::IA,
3122 /*HasAddressDiversity=*/false, OutContext);
3123
3124 return BAE;
3125}
3126
3127void AArch64AsmPrinter::emitCBPseudoExpansion(const MachineInstr *MI) {
3128 bool IsImm = false;
3129 unsigned Width = 0;
3130
3131 switch (MI->getOpcode()) {
3132 default:
3133 llvm_unreachable("This is not a CB pseudo instruction");
3134 case AArch64::CBBAssertExt:
3135 IsImm = false;
3136 Width = 8;
3137 break;
3138 case AArch64::CBHAssertExt:
3139 IsImm = false;
3140 Width = 16;
3141 break;
3142 case AArch64::CBWPrr:
3143 Width = 32;
3144 break;
3145 case AArch64::CBXPrr:
3146 Width = 64;
3147 break;
3148 case AArch64::CBWPri:
3149 IsImm = true;
3150 Width = 32;
3151 break;
3152 case AArch64::CBXPri:
3153 IsImm = true;
3154 Width = 64;
3155 break;
3156 }
3157
3159 static_cast<AArch64CC::CondCode>(MI->getOperand(0).getImm());
3160 bool NeedsRegSwap = false;
3161 bool NeedsImmDec = false;
3162 bool NeedsImmInc = false;
3163
3164#define GET_CB_OPC(IsImm, Width, ImmCond, RegCond) \
3165 (IsImm \
3166 ? (Width == 32 ? AArch64::CB##ImmCond##Wri : AArch64::CB##ImmCond##Xri) \
3167 : (Width == 8 \
3168 ? AArch64::CBB##RegCond##Wrr \
3169 : (Width == 16 ? AArch64::CBH##RegCond##Wrr \
3170 : (Width == 32 ? AArch64::CB##RegCond##Wrr \
3171 : AArch64::CB##RegCond##Xrr))))
3172 unsigned MCOpC;
3173
3174 // Decide if we need to either swap register operands or increment/decrement
3175 // immediate operands
3176 switch (CC) {
3177 default:
3178 llvm_unreachable("Invalid CB condition code");
3179 case AArch64CC::EQ:
3180 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ EQ, /* Reg-Reg */ EQ);
3181 break;
3182 case AArch64CC::NE:
3183 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ NE, /* Reg-Reg */ NE);
3184 break;
3185 case AArch64CC::HS:
3186 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ HI, /* Reg-Reg */ HS);
3187 NeedsImmDec = IsImm;
3188 break;
3189 case AArch64CC::LO:
3190 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ LO, /* Reg-Reg */ HI);
3191 NeedsRegSwap = !IsImm;
3192 break;
3193 case AArch64CC::HI:
3194 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ HI, /* Reg-Reg */ HI);
3195 break;
3196 case AArch64CC::LS:
3197 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ LO, /* Reg-Reg */ HS);
3198 NeedsRegSwap = !IsImm;
3199 NeedsImmInc = IsImm;
3200 break;
3201 case AArch64CC::GE:
3202 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ GT, /* Reg-Reg */ GE);
3203 NeedsImmDec = IsImm;
3204 break;
3205 case AArch64CC::LT:
3206 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ LT, /* Reg-Reg */ GT);
3207 NeedsRegSwap = !IsImm;
3208 break;
3209 case AArch64CC::GT:
3210 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ GT, /* Reg-Reg */ GT);
3211 break;
3212 case AArch64CC::LE:
3213 MCOpC = GET_CB_OPC(IsImm, Width, /* Reg-Imm */ LT, /* Reg-Reg */ GE);
3214 NeedsRegSwap = !IsImm;
3215 NeedsImmInc = IsImm;
3216 break;
3217 }
3218#undef GET_CB_OPC
3219
3220 MCInst Inst;
3221 Inst.setOpcode(MCOpC);
3222
3223 MCOperand Lhs, Rhs, Trgt;
3224 lowerOperand(MI->getOperand(1), Lhs);
3225 lowerOperand(MI->getOperand(2), Rhs);
3226 lowerOperand(MI->getOperand(3), Trgt);
3227
3228 // Now swap, increment or decrement
3229 if (NeedsRegSwap) {
3230 assert(Lhs.isReg() && "Expected register operand for CB");
3231 assert(Rhs.isReg() && "Expected register operand for CB");
3232 Inst.addOperand(Rhs);
3233 Inst.addOperand(Lhs);
3234 } else if (NeedsImmDec) {
3235 Rhs.setImm(Rhs.getImm() - 1);
3236 Inst.addOperand(Lhs);
3237 Inst.addOperand(Rhs);
3238 } else if (NeedsImmInc) {
3239 Rhs.setImm(Rhs.getImm() + 1);
3240 Inst.addOperand(Lhs);
3241 Inst.addOperand(Rhs);
3242 } else {
3243 Inst.addOperand(Lhs);
3244 Inst.addOperand(Rhs);
3245 }
3246
3247 assert((!IsImm || (Rhs.getImm() >= 0 && Rhs.getImm() < 64)) &&
3248 "CB immediate operand out-of-bounds");
3249
3250 Inst.addOperand(Trgt);
3251 EmitToStreamer(*OutStreamer, Inst);
3252}
3253
3254// Simple pseudo-instructions have their lowering (with expansion to real
3255// instructions) auto-generated.
3256#include "AArch64GenMCPseudoLowering.inc"
3257
3258void AArch64AsmPrinter::EmitToStreamer(MCStreamer &S, const MCInst &Inst) {
3259 S.emitInstruction(Inst, *STI);
3260#ifndef NDEBUG
3261 ++InstsEmitted;
3262#endif
3263}
3264
3265void AArch64AsmPrinter::emitInstruction(const MachineInstr *MI) {
3266 AArch64_MC::verifyInstructionPredicates(MI->getOpcode(), STI->getFeatureBits());
3267
3268#ifndef NDEBUG
3269 InstsEmitted = 0;
3270 llvm::scope_exit CheckMISize([&]() {
3271 assert(STI->getInstrInfo()->getInstSizeInBytes(*MI) >= InstsEmitted * 4);
3272 });
3273#endif
3274
3275 // Do any auto-generated pseudo lowerings.
3276 if (MCInst OutInst; lowerPseudoInstExpansion(MI, OutInst)) {
3277 EmitToStreamer(*OutStreamer, OutInst);
3278 return;
3279 }
3280
3281 if (MI->getOpcode() == AArch64::ADRP) {
3282 for (auto &Opd : MI->operands()) {
3283 if (Opd.isSymbol() && StringRef(Opd.getSymbolName()) ==
3284 "swift_async_extendedFramePointerFlags") {
3285 ShouldEmitWeakSwiftAsyncExtendedFramePointerFlags = true;
3286 }
3287 }
3288 }
3289
3290 if (AArch64FI->getLOHRelated().count(MI)) {
3291 // Generate a label for LOH related instruction
3292 MCSymbol *LOHLabel = createTempSymbol("loh");
3293 // Associate the instruction with the label
3294 LOHInstToLabel[MI] = LOHLabel;
3295 OutStreamer->emitLabel(LOHLabel);
3296 }
3297
3298 AArch64TargetStreamer *TS =
3299 static_cast<AArch64TargetStreamer *>(OutStreamer->getTargetStreamer());
3300 // Do any manual lowerings.
3301 switch (MI->getOpcode()) {
3302 default:
3304 "Unhandled tail call instruction");
3305 break;
3306 case AArch64::READ_REGISTER_GPR64:
3307 // Read of a named GPR: emit "mov Xt, Xn" (ORR Xt, XZR, Xn). The source
3308 // register is encoded as an immediate operand so that earlier passes do not
3309 // see a use of an undefined physical register.
3310 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::ORRXrs)
3311 .addReg(MI->getOperand(0).getReg())
3312 .addReg(AArch64::XZR)
3313 .addReg(MI->getOperand(1).getImm())
3314 .addImm(0));
3315 return;
3316 case AArch64::READ_REGISTER_FPR64:
3317 // Read of a named FP/SIMD d-register: emit "fmov Dt, Dn".
3318 EmitToStreamer(*OutStreamer, MCInstBuilder(AArch64::FMOVDr)
3319 .addReg(MI->getOperand(0).getReg())
3320 .addReg(MI->getOperand(1).getImm()));
3321 return;
3322 case AArch64::HINT: {
3323 // CurrentPatchableFunctionEntrySym can be CurrentFnBegin only for
3324 // -fpatchable-function-entry=N,0. The entry MBB is guaranteed to be
3325 // non-empty. If MI is the initial BTI, place the
3326 // __patchable_function_entries label after BTI.
3327 if (CurrentPatchableFunctionEntrySym &&
3328 CurrentPatchableFunctionEntrySym == CurrentFnBegin &&
3329 MI == &MF->front().front()) {
3330 int64_t Imm = MI->getOperand(0).getImm();
3331 if (Imm == 32 || Imm == 34 || Imm == 36 || Imm == 38) {
3332 MCInst Inst;
3333 MCInstLowering.Lower(MI, Inst);
3334 EmitToStreamer(*OutStreamer, Inst);
3335 CurrentPatchableFunctionEntrySym = createTempSymbol("patch");
3336 OutStreamer->emitLabel(CurrentPatchableFunctionEntrySym);
3337 return;
3338 }
3339 }
3340 break;
3341 }
3342 case AArch64::MOVMCSym: {
3343 Register DestReg = MI->getOperand(0).getReg();
3344 const MachineOperand &MO_Sym = MI->getOperand(1);
3345 MachineOperand Hi_MOSym(MO_Sym), Lo_MOSym(MO_Sym);
3346 MCOperand Hi_MCSym, Lo_MCSym;
3347
3348 Hi_MOSym.setTargetFlags(AArch64II::MO_G1 | AArch64II::MO_S);
3349 Lo_MOSym.setTargetFlags(AArch64II::MO_G0 | AArch64II::MO_NC);
3350
3351 MCInstLowering.lowerOperand(Hi_MOSym, Hi_MCSym);
3352 MCInstLowering.lowerOperand(Lo_MOSym, Lo_MCSym);
3353
3354 MCInst MovZ;
3355 MovZ.setOpcode(AArch64::MOVZXi);
3356 MovZ.addOperand(MCOperand::createReg(DestReg));
3357 MovZ.addOperand(Hi_MCSym);
3359 EmitToStreamer(*OutStreamer, MovZ);
3360
3361 MCInst MovK;
3362 MovK.setOpcode(AArch64::MOVKXi);
3363 MovK.addOperand(MCOperand::createReg(DestReg));
3364 MovK.addOperand(MCOperand::createReg(DestReg));
3365 MovK.addOperand(Lo_MCSym);
3367 EmitToStreamer(*OutStreamer, MovK);
3368 return;
3369 }
3370 case AArch64::MOVIv2d_ns:
3371 // It is generally beneficial to rewrite "fmov s0, wzr" to "movi d0, #0".
3372 // as movi is more efficient across all cores. Newer cores can eliminate
3373 // fmovs early and there is no difference with movi, but this not true for
3374 // all implementations.
3375 //
3376 // The floating-point version doesn't quite work in rare cases on older
3377 // CPUs, so on those targets we lower this instruction to movi.16b instead.
3378 if (STI->hasZeroCycleZeroingFPWorkaround() &&
3379 MI->getOperand(1).getImm() == 0) {
3380 MCInst TmpInst;
3381 TmpInst.setOpcode(AArch64::MOVIv16b_ns);
3382 TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
3383 TmpInst.addOperand(MCOperand::createImm(0));
3384 EmitToStreamer(*OutStreamer, TmpInst);
3385 return;
3386 }
3387 break;
3388
3389 case AArch64::DBG_VALUE:
3390 case AArch64::DBG_VALUE_LIST:
3391 if (isVerbose() && OutStreamer->hasRawTextSupport()) {
3392 SmallString<128> TmpStr;
3393 raw_svector_ostream OS(TmpStr);
3394 PrintDebugValueComment(MI, OS);
3395 OutStreamer->emitRawText(StringRef(OS.str()));
3396 }
3397 return;
3398
3399 case AArch64::EMITBKEY: {
3400 ExceptionHandling ExceptionHandlingType = MAI.getExceptionHandlingType();
3401 if (ExceptionHandlingType != ExceptionHandling::DwarfCFI &&
3402 ExceptionHandlingType != ExceptionHandling::ARM)
3403 return;
3404
3405 if (getFunctionCFISectionType(*MF) == CFISection::None)
3406 return;
3407
3408 OutStreamer->emitCFIBKeyFrame();
3409 return;
3410 }
3411
3412 case AArch64::EMITMTETAGGED: {
3413 ExceptionHandling ExceptionHandlingType = MAI.getExceptionHandlingType();
3414 if (ExceptionHandlingType != ExceptionHandling::DwarfCFI &&
3415 ExceptionHandlingType != ExceptionHandling::ARM)
3416 return;
3417
3418 if (getFunctionCFISectionType(*MF) != CFISection::None)
3419 OutStreamer->emitCFIMTETaggedFrame();
3420 return;
3421 }
3422
3423 case AArch64::AUTx16x17: {
3424 const Register Pointer = AArch64::X16;
3425 const Register Scratch = AArch64::X17;
3426
3427 auto AuthSchema = PtrAuthSchema::CreateImmReg(
3428 (AArch64PACKey::ID)MI->getOperand(0).getImm(),
3429 MI->getOperand(1).getImm(), MI->getOperand(2));
3430
3431 emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, std::nullopt,
3432 std::nullopt, MI->getDeactivationSymbol());
3433 return;
3434 }
3435
3436 case AArch64::AUTxMxN: {
3437 const Register Pointer = MI->getOperand(0).getReg();
3438 const Register Scratch = MI->getOperand(1).getReg();
3439
3440 auto AuthSchema = PtrAuthSchema::CreateImmReg(
3441 (AArch64PACKey::ID)MI->getOperand(3).getImm(),
3442 MI->getOperand(4).getImm(), MI->getOperand(5));
3443
3444 emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, std::nullopt,
3445 std::nullopt, MI->getDeactivationSymbol());
3446 return;
3447 }
3448
3449 case AArch64::AUTPAC: {
3450 const Register Pointer = AArch64::X16;
3451 const Register Scratch = AArch64::X17;
3452
3453 auto AuthSchema = PtrAuthSchema::CreateImmReg(
3454 (AArch64PACKey::ID)MI->getOperand(0).getImm(),
3455 MI->getOperand(1).getImm(), MI->getOperand(2));
3456
3457 auto SignSchema = PtrAuthSchema::CreateImmReg(
3458 (AArch64PACKey::ID)MI->getOperand(3).getImm(),
3459 MI->getOperand(4).getImm(), MI->getOperand(5));
3460
3461 emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, SignSchema,
3462 std::nullopt, MI->getDeactivationSymbol());
3463 return;
3464 }
3465
3466 case AArch64::AUTPCPAC: {
3467 auto AuthSchema = PtrAuthSchema::CreateRegReg(
3468 (AArch64PACKey::ID)MI->getOperand(0).getImm(), AArch64::X16,
3469 AArch64::X15);
3470
3471 auto SignSchema = PtrAuthSchema::CreateImmReg(
3472 (AArch64PACKey::ID)MI->getOperand(1).getImm(),
3473 MI->getOperand(2).getImm(), MI->getOperand(3));
3474
3475 emitPtrauthAuthResign(/*Pointer=*/AArch64::X17, /*Scratch=*/AArch64::X16,
3476 AuthSchema, SignSchema, std::nullopt,
3477 MI->getDeactivationSymbol());
3478 return;
3479 }
3480
3481 case AArch64::AUTRELLOADPAC: {
3482 const Register Pointer = AArch64::X16;
3483 const Register Scratch = AArch64::X17;
3484
3485 auto AuthSchema = PtrAuthSchema::CreateImmReg(
3486 (AArch64PACKey::ID)MI->getOperand(0).getImm(),
3487 MI->getOperand(1).getImm(), MI->getOperand(2));
3488
3489 auto SignSchema = PtrAuthSchema::CreateImmReg(
3490 (AArch64PACKey::ID)MI->getOperand(3).getImm(),
3491 MI->getOperand(4).getImm(), MI->getOperand(5));
3492
3493 emitPtrauthAuthResign(Pointer, Scratch, AuthSchema, SignSchema,
3494 MI->getOperand(6).getImm(),
3495 MI->getDeactivationSymbol());
3496
3497 return;
3498 }
3499
3500 case AArch64::PAC:
3501 emitPtrauthSign(MI);
3502 return;
3503
3504 case AArch64::LOADauthptrstatic:
3505 LowerLOADauthptrstatic(*MI);
3506 return;
3507
3508 case AArch64::LOADgotPAC:
3509 case AArch64::MOVaddrPAC:
3510 LowerMOVaddrPAC(*MI);
3511 return;
3512
3513 case AArch64::LOADgotAUTH:
3514 LowerLOADgotAUTH(*MI);
3515 return;
3516
3517 case AArch64::BRA:
3518 case AArch64::BLRA:
3519 emitPtrauthBranch(MI);
3520 return;
3521
3522 // Tail calls use pseudo instructions so they have the proper code-gen
3523 // attributes (isCall, isReturn, etc.). We lower them to the real
3524 // instruction here.
3525 case AArch64::AUTH_TCRETURN:
3526 case AArch64::AUTH_TCRETURN_BTI: {
3527 Register Callee = MI->getOperand(0).getReg();
3528 const auto Key = (AArch64PACKey::ID)MI->getOperand(2).getImm();
3529 const uint64_t Disc = MI->getOperand(3).getImm();
3530
3531 Register AddrDisc = MI->getOperand(4).getReg();
3532
3533 Register ScratchReg = Callee == AArch64::X16 ? AArch64::X17 : AArch64::X16;
3534
3535 emitPtrauthTailCallHardening(MI);
3536
3537 // See the comments in emitPtrauthBranch.
3538 if (Callee == AddrDisc)
3539 report_fatal_error("Call target is signed with its own value");
3540
3541 // After isX16X17Safer predicate was introduced, emitPtrauthDiscriminator is
3542 // no longer restricted to only reusing AddrDisc when it is X16 or X17
3543 // (which are implicit-def'ed by AUTH_TCRETURN pseudos), thus impose this
3544 // restriction manually not to clobber an unexpected register.
3545 bool AddrDiscIsImplicitDef =
3546 AddrDisc == AArch64::X16 || AddrDisc == AArch64::X17;
3547 Register DiscReg = emitPtrauthDiscriminator(Disc, AddrDisc, ScratchReg,
3548 AddrDiscIsImplicitDef);
3549 emitBLRA(/*IsCall*/ false, Key, Callee, DiscReg);
3550 return;
3551 }
3552
3553 case AArch64::TCRETURNri:
3554 case AArch64::TCRETURNrix16x17:
3555 case AArch64::TCRETURNrix17:
3556 case AArch64::TCRETURNrinotx16:
3557 case AArch64::TCRETURNriALL: {
3558 emitPtrauthTailCallHardening(MI);
3559
3560 recordIfImportCall(MI);
3561 MCInst TmpInst;
3562 TmpInst.setOpcode(AArch64::BR);
3563 TmpInst.addOperand(MCOperand::createReg(MI->getOperand(0).getReg()));
3564 EmitToStreamer(*OutStreamer, TmpInst);
3565 return;
3566 }
3567 case AArch64::TCRETURNdi: {
3568 emitPtrauthTailCallHardening(MI);
3569
3570 MCOperand Dest;
3571 MCInstLowering.lowerOperand(MI->getOperand(0), Dest);
3572 recordIfImportCall(MI);
3573 MCInst TmpInst;
3574 TmpInst.setOpcode(AArch64::B);
3575 TmpInst.addOperand(Dest);
3576 EmitToStreamer(*OutStreamer, TmpInst);
3577 return;
3578 }
3579 case AArch64::SpeculationBarrierISBDSBEndBB: {
3580 // Print DSB SYS + ISB
3581 MCInst TmpInstDSB;
3582 TmpInstDSB.setOpcode(AArch64::DSB);
3583 TmpInstDSB.addOperand(MCOperand::createImm(0xf));
3584 EmitToStreamer(*OutStreamer, TmpInstDSB);
3585 MCInst TmpInstISB;
3586 TmpInstISB.setOpcode(AArch64::ISB);
3587 TmpInstISB.addOperand(MCOperand::createImm(0xf));
3588 EmitToStreamer(*OutStreamer, TmpInstISB);
3589 return;
3590 }
3591 case AArch64::SpeculationBarrierSBEndBB: {
3592 // Print SB
3593 MCInst TmpInstSB;
3594 TmpInstSB.setOpcode(AArch64::SB);
3595 EmitToStreamer(*OutStreamer, TmpInstSB);
3596 return;
3597 }
3598 case AArch64::TLSDESC_AUTH_CALLSEQ: {
3599 /// lower this to:
3600 /// adrp x0, :tlsdesc_auth:var
3601 /// ldr x16, [x0, #:tlsdesc_auth_lo12:var]
3602 /// add x0, x0, #:tlsdesc_auth_lo12:var
3603 /// blraa x16, x0
3604 /// (TPIDR_EL0 offset now in x0)
3605 const MachineOperand &MO_Sym = MI->getOperand(0);
3606 MachineOperand MO_TLSDESC_LO12(MO_Sym), MO_TLSDESC(MO_Sym);
3607 MCOperand SymTLSDescLo12, SymTLSDesc;
3608 MO_TLSDESC_LO12.setTargetFlags(AArch64II::MO_TLS | AArch64II::MO_PAGEOFF);
3609 MO_TLSDESC.setTargetFlags(AArch64II::MO_TLS | AArch64II::MO_PAGE);
3610 MCInstLowering.lowerOperand(MO_TLSDESC_LO12, SymTLSDescLo12);
3611 MCInstLowering.lowerOperand(MO_TLSDESC, SymTLSDesc);
3612
3613 MCInst Adrp;
3614 Adrp.setOpcode(AArch64::ADRP);
3615 Adrp.addOperand(MCOperand::createReg(AArch64::X0));
3616 Adrp.addOperand(SymTLSDesc);
3617 EmitToStreamer(*OutStreamer, Adrp);
3618
3619 MCInst Ldr;
3620 Ldr.setOpcode(AArch64::LDRXui);
3621 Ldr.addOperand(MCOperand::createReg(AArch64::X16));
3622 Ldr.addOperand(MCOperand::createReg(AArch64::X0));
3623 Ldr.addOperand(SymTLSDescLo12);
3625 EmitToStreamer(*OutStreamer, Ldr);
3626
3627 MCInst Add;
3628 Add.setOpcode(AArch64::ADDXri);
3629 Add.addOperand(MCOperand::createReg(AArch64::X0));
3630 Add.addOperand(MCOperand::createReg(AArch64::X0));
3631 Add.addOperand(SymTLSDescLo12);
3633 EmitToStreamer(*OutStreamer, Add);
3634
3635 // Authenticated TLSDESC accesses are not relaxed.
3636 // Thus, do not emit .tlsdesccall for AUTH TLSDESC.
3637
3638 MCInst Blraa;
3639 Blraa.setOpcode(AArch64::BLRAA);
3640 Blraa.addOperand(MCOperand::createReg(AArch64::X16));
3641 Blraa.addOperand(MCOperand::createReg(AArch64::X0));
3642 EmitToStreamer(*OutStreamer, Blraa);
3643
3644 return;
3645 }
3646 case AArch64::TLSDESC_CALLSEQ: {
3647 /// lower this to:
3648 /// adrp x0, :tlsdesc:var
3649 /// ldr x1, [x0, #:tlsdesc_lo12:var]
3650 /// add x0, x0, #:tlsdesc_lo12:var
3651 /// .tlsdesccall var
3652 /// blr x1
3653 /// (TPIDR_EL0 offset now in x0)
3654 const MachineOperand &MO_Sym = MI->getOperand(0);
3655 MachineOperand MO_TLSDESC_LO12(MO_Sym), MO_TLSDESC(MO_Sym);
3656 MCOperand Sym, SymTLSDescLo12, SymTLSDesc;
3657 MO_TLSDESC_LO12.setTargetFlags(AArch64II::MO_TLS | AArch64II::MO_PAGEOFF);
3658 MO_TLSDESC.setTargetFlags(AArch64II::MO_TLS | AArch64II::MO_PAGE);
3659 MCInstLowering.lowerOperand(MO_Sym, Sym);
3660 MCInstLowering.lowerOperand(MO_TLSDESC_LO12, SymTLSDescLo12);
3661 MCInstLowering.lowerOperand(MO_TLSDESC, SymTLSDesc);
3662
3663 MCInst Adrp;
3664 Adrp.setOpcode(AArch64::ADRP);
3665 Adrp.addOperand(MCOperand::createReg(AArch64::X0));
3666 Adrp.addOperand(SymTLSDesc);
3667 EmitToStreamer(*OutStreamer, Adrp);
3668
3669 MCInst Ldr;
3670 if (STI->isTargetILP32()) {
3671 Ldr.setOpcode(AArch64::LDRWui);
3672 Ldr.addOperand(MCOperand::createReg(AArch64::W1));
3673 } else {
3674 Ldr.setOpcode(AArch64::LDRXui);
3675 Ldr.addOperand(MCOperand::createReg(AArch64::X1));
3676 }
3677 Ldr.addOperand(MCOperand::createReg(AArch64::X0));
3678 Ldr.addOperand(SymTLSDescLo12);
3680 EmitToStreamer(*OutStreamer, Ldr);
3681
3682 MCInst Add;
3683 if (STI->isTargetILP32()) {
3684 Add.setOpcode(AArch64::ADDWri);
3685 Add.addOperand(MCOperand::createReg(AArch64::W0));
3686 Add.addOperand(MCOperand::createReg(AArch64::W0));
3687 } else {
3688 Add.setOpcode(AArch64::ADDXri);
3689 Add.addOperand(MCOperand::createReg(AArch64::X0));
3690 Add.addOperand(MCOperand::createReg(AArch64::X0));
3691 }
3692 Add.addOperand(SymTLSDescLo12);
3694 EmitToStreamer(*OutStreamer, Add);
3695
3696 // Emit a relocation-annotation. This expands to no code, but requests
3697 // the following instruction gets an R_AARCH64_TLSDESC_CALL.
3698 MCInst TLSDescCall;
3699 TLSDescCall.setOpcode(AArch64::TLSDESCCALL);
3700 TLSDescCall.addOperand(Sym);
3701 EmitToStreamer(*OutStreamer, TLSDescCall);
3702#ifndef NDEBUG
3703 --InstsEmitted; // no code emitted
3704#endif
3705
3706 MCInst Blr;
3707 Blr.setOpcode(AArch64::BLR);
3708 Blr.addOperand(MCOperand::createReg(AArch64::X1));
3709 EmitToStreamer(*OutStreamer, Blr);
3710
3711 return;
3712 }
3713
3714 case AArch64::JumpTableDest32:
3715 case AArch64::JumpTableDest16:
3716 case AArch64::JumpTableDest8:
3717 LowerJumpTableDest(*OutStreamer, *MI);
3718 return;
3719
3720 case AArch64::BR_JumpTable:
3721 LowerHardenedBRJumpTable(*MI);
3722 return;
3723
3724 case AArch64::FMOVH0:
3725 case AArch64::FMOVS0:
3726 case AArch64::FMOVD0:
3727 emitFMov0(*MI);
3728 return;
3729
3730 case AArch64::MOPSMemoryCopyPseudo:
3731 case AArch64::MOPSMemoryMovePseudo:
3732 case AArch64::MOPSMemorySetPseudo:
3733 case AArch64::MOPSMemorySetTaggingPseudo:
3734 LowerMOPS(*OutStreamer, *MI);
3735 return;
3736
3737 case TargetOpcode::STACKMAP:
3738 return LowerSTACKMAP(*OutStreamer, SM, *MI);
3739
3740 case TargetOpcode::PATCHPOINT:
3741 return LowerPATCHPOINT(*OutStreamer, SM, *MI);
3742
3743 case TargetOpcode::STATEPOINT:
3744 return LowerSTATEPOINT(*OutStreamer, SM, *MI);
3745
3746 case TargetOpcode::FAULTING_OP:
3747 return LowerFAULTING_OP(*MI);
3748
3749 case TargetOpcode::PATCHABLE_FUNCTION_ENTER:
3750 LowerPATCHABLE_FUNCTION_ENTER(*MI);
3751 return;
3752
3753 case TargetOpcode::PATCHABLE_FUNCTION_EXIT:
3754 LowerPATCHABLE_FUNCTION_EXIT(*MI);
3755 return;
3756
3757 case TargetOpcode::PATCHABLE_TAIL_CALL:
3758 LowerPATCHABLE_TAIL_CALL(*MI);
3759 return;
3760 case TargetOpcode::PATCHABLE_EVENT_CALL:
3761 return LowerPATCHABLE_EVENT_CALL(*MI, false);
3762 case TargetOpcode::PATCHABLE_TYPED_EVENT_CALL:
3763 return LowerPATCHABLE_EVENT_CALL(*MI, true);
3764
3765 case AArch64::KCFI_CHECK:
3766 LowerKCFI_CHECK(*MI);
3767 return;
3768
3769 case AArch64::HWASAN_CHECK_MEMACCESS:
3770 case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES:
3771 case AArch64::HWASAN_CHECK_MEMACCESS_FIXEDSHADOW:
3772 case AArch64::HWASAN_CHECK_MEMACCESS_SHORTGRANULES_FIXEDSHADOW:
3773 LowerHWASAN_CHECK_MEMACCESS(*MI);
3774 return;
3775
3776 case AArch64::SEH_StackAlloc:
3777 TS->emitARM64WinCFIAllocStack(MI->getOperand(0).getImm());
3778 return;
3779
3780 case AArch64::SEH_SaveFPLR:
3781 TS->emitARM64WinCFISaveFPLR(MI->getOperand(0).getImm());
3782 return;
3783
3784 case AArch64::SEH_SaveFPLR_X:
3785 assert(MI->getOperand(0).getImm() < 0 &&
3786 "Pre increment SEH opcode must have a negative offset");
3787 TS->emitARM64WinCFISaveFPLRX(-MI->getOperand(0).getImm());
3788 return;
3789
3790 case AArch64::SEH_SaveReg:
3791 TS->emitARM64WinCFISaveReg(MI->getOperand(0).getImm(),
3792 MI->getOperand(1).getImm());
3793 return;
3794
3795 case AArch64::SEH_SaveReg_X:
3796 assert(MI->getOperand(1).getImm() < 0 &&
3797 "Pre increment SEH opcode must have a negative offset");
3798 TS->emitARM64WinCFISaveRegX(MI->getOperand(0).getImm(),
3799 -MI->getOperand(1).getImm());
3800 return;
3801
3802 case AArch64::SEH_SaveRegP:
3803 if (MI->getOperand(1).getImm() == 30 && MI->getOperand(0).getImm() >= 19 &&
3804 MI->getOperand(0).getImm() <= 28) {
3805 assert((MI->getOperand(0).getImm() - 19) % 2 == 0 &&
3806 "Register paired with LR must be odd");
3807 TS->emitARM64WinCFISaveLRPair(MI->getOperand(0).getImm(),
3808 MI->getOperand(2).getImm());
3809 return;
3810 }
3811 assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) &&
3812 "Non-consecutive registers not allowed for save_regp");
3813 TS->emitARM64WinCFISaveRegP(MI->getOperand(0).getImm(),
3814 MI->getOperand(2).getImm());
3815 return;
3816
3817 case AArch64::SEH_SaveRegP_X:
3818 assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) &&
3819 "Non-consecutive registers not allowed for save_regp_x");
3820 assert(MI->getOperand(2).getImm() < 0 &&
3821 "Pre increment SEH opcode must have a negative offset");
3822 TS->emitARM64WinCFISaveRegPX(MI->getOperand(0).getImm(),
3823 -MI->getOperand(2).getImm());
3824 return;
3825
3826 case AArch64::SEH_SaveFReg:
3827 TS->emitARM64WinCFISaveFReg(MI->getOperand(0).getImm(),
3828 MI->getOperand(1).getImm());
3829 return;
3830
3831 case AArch64::SEH_SaveFReg_X:
3832 assert(MI->getOperand(1).getImm() < 0 &&
3833 "Pre increment SEH opcode must have a negative offset");
3834 TS->emitARM64WinCFISaveFRegX(MI->getOperand(0).getImm(),
3835 -MI->getOperand(1).getImm());
3836 return;
3837
3838 case AArch64::SEH_SaveFRegP:
3839 assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) &&
3840 "Non-consecutive registers not allowed for save_regp");
3841 TS->emitARM64WinCFISaveFRegP(MI->getOperand(0).getImm(),
3842 MI->getOperand(2).getImm());
3843 return;
3844
3845 case AArch64::SEH_SaveFRegP_X:
3846 assert((MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1) &&
3847 "Non-consecutive registers not allowed for save_regp_x");
3848 assert(MI->getOperand(2).getImm() < 0 &&
3849 "Pre increment SEH opcode must have a negative offset");
3850 TS->emitARM64WinCFISaveFRegPX(MI->getOperand(0).getImm(),
3851 -MI->getOperand(2).getImm());
3852 return;
3853
3854 case AArch64::SEH_SetFP:
3856 return;
3857
3858 case AArch64::SEH_AddFP:
3859 TS->emitARM64WinCFIAddFP(MI->getOperand(0).getImm());
3860 return;
3861
3862 case AArch64::SEH_Nop:
3863 TS->emitARM64WinCFINop();
3864 return;
3865
3866 case AArch64::SEH_PrologEnd:
3868 return;
3869
3870 case AArch64::SEH_EpilogStart:
3872 return;
3873
3874 case AArch64::SEH_EpilogEnd:
3876 return;
3877
3878 case AArch64::SEH_PACSignLR:
3880 return;
3881
3882 case AArch64::SEH_SaveAnyRegI:
3883 assert(MI->getOperand(1).getImm() <= 1008 &&
3884 "SaveAnyRegQP SEH opcode offset must fit into 6 bits");
3885 TS->emitARM64WinCFISaveAnyRegI(MI->getOperand(0).getImm(),
3886 MI->getOperand(1).getImm());
3887 return;
3888
3889 case AArch64::SEH_SaveAnyRegIP:
3890 assert(MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1 &&
3891 "Non-consecutive registers not allowed for save_any_reg");
3892 assert(MI->getOperand(2).getImm() <= 1008 &&
3893 "SaveAnyRegQP SEH opcode offset must fit into 6 bits");
3894 TS->emitARM64WinCFISaveAnyRegIP(MI->getOperand(0).getImm(),
3895 MI->getOperand(2).getImm());
3896 return;
3897
3898 case AArch64::SEH_SaveAnyRegQP:
3899 assert(MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1 &&
3900 "Non-consecutive registers not allowed for save_any_reg");
3901 assert(MI->getOperand(2).getImm() >= 0 &&
3902 "SaveAnyRegQP SEH opcode offset must be non-negative");
3903 assert(MI->getOperand(2).getImm() <= 1008 &&
3904 "SaveAnyRegQP SEH opcode offset must fit into 6 bits");
3905 TS->emitARM64WinCFISaveAnyRegQP(MI->getOperand(0).getImm(),
3906 MI->getOperand(2).getImm());
3907 return;
3908
3909 case AArch64::SEH_SaveAnyRegQPX:
3910 assert(MI->getOperand(1).getImm() - MI->getOperand(0).getImm() == 1 &&
3911 "Non-consecutive registers not allowed for save_any_reg");
3912 assert(MI->getOperand(2).getImm() < 0 &&
3913 "SaveAnyRegQPX SEH opcode offset must be negative");
3914 assert(MI->getOperand(2).getImm() >= -1008 &&
3915 "SaveAnyRegQPX SEH opcode offset must fit into 6 bits");
3916 TS->emitARM64WinCFISaveAnyRegQPX(MI->getOperand(0).getImm(),
3917 -MI->getOperand(2).getImm());
3918 return;
3919
3920 case AArch64::SEH_AllocZ:
3921 assert(MI->getOperand(0).getImm() >= 0 &&
3922 "AllocZ SEH opcode offset must be non-negative");
3923 assert(MI->getOperand(0).getImm() <= 255 &&
3924 "AllocZ SEH opcode offset must fit into 8 bits");
3925 TS->emitARM64WinCFIAllocZ(MI->getOperand(0).getImm());
3926 return;
3927
3928 case AArch64::SEH_SaveZReg:
3929 assert(MI->getOperand(1).getImm() >= 0 &&
3930 "SaveZReg SEH opcode offset must be non-negative");
3931 assert(MI->getOperand(1).getImm() <= 255 &&
3932 "SaveZReg SEH opcode offset must fit into 8 bits");
3933 TS->emitARM64WinCFISaveZReg(MI->getOperand(0).getImm(),
3934 MI->getOperand(1).getImm());
3935 return;
3936
3937 case AArch64::SEH_SavePReg:
3938 assert(MI->getOperand(1).getImm() >= 0 &&
3939 "SavePReg SEH opcode offset must be non-negative");
3940 assert(MI->getOperand(1).getImm() <= 255 &&
3941 "SavePReg SEH opcode offset must fit into 8 bits");
3942 TS->emitARM64WinCFISavePReg(MI->getOperand(0).getImm(),
3943 MI->getOperand(1).getImm());
3944 return;
3945
3946 case AArch64::BLR:
3947 case AArch64::BR: {
3948 recordIfImportCall(MI);
3949 MCInst TmpInst;
3950 MCInstLowering.Lower(MI, TmpInst);
3951 EmitToStreamer(*OutStreamer, TmpInst);
3952 return;
3953 }
3954 case AArch64::CBWPri:
3955 case AArch64::CBXPri:
3956 case AArch64::CBBAssertExt:
3957 case AArch64::CBHAssertExt:
3958 case AArch64::CBWPrr:
3959 case AArch64::CBXPrr:
3960 emitCBPseudoExpansion(MI);
3961 return;
3962 }
3963
3964 if (emitDeactivationSymbolRelocation(MI->getDeactivationSymbol()))
3965 return;
3966
3967 // Finally, do the automated lowerings for everything else.
3968 MCInst TmpInst;
3969 MCInstLowering.Lower(MI, TmpInst);
3970 EmitToStreamer(*OutStreamer, TmpInst);
3971}
3972
3973void AArch64AsmPrinter::recordIfImportCall(
3974 const llvm::MachineInstr *BranchInst) {
3975 if (!EnableImportCallOptimization)
3976 return;
3977
3978 auto [GV, OpFlags] = BranchInst->getMF()->tryGetCalledGlobal(BranchInst);
3979 if (GV && GV->hasDLLImportStorageClass()) {
3980 auto *CallSiteSymbol = MMI->getContext().createNamedTempSymbol("impcall");
3981 OutStreamer->emitLabel(CallSiteSymbol);
3982
3983 auto *CalledSymbol = MCInstLowering.GetGlobalValueSymbol(GV, OpFlags);
3984 SectionToImportedFunctionCalls[OutStreamer->getCurrentSectionOnly()]
3985 .push_back({CallSiteSymbol, CalledSymbol});
3986 }
3987}
3988
3989void AArch64AsmPrinter::emitMachOIFuncStubBody(Module &M, const GlobalIFunc &GI,
3990 MCSymbol *LazyPointer) {
3991 // _ifunc:
3992 // adrp x16, lazy_pointer@GOTPAGE
3993 // ldr x16, [x16, lazy_pointer@GOTPAGEOFF]
3994 // ldr x16, [x16]
3995 // br x16
3996
3997 {
3998 MCInst Adrp;
3999 Adrp.setOpcode(AArch64::ADRP);
4000 Adrp.addOperand(MCOperand::createReg(AArch64::X16));
4001 MCOperand SymPage;
4002 MCInstLowering.lowerOperand(
4005 SymPage);
4006 Adrp.addOperand(SymPage);
4007 EmitToStreamer(Adrp);
4008 }
4009
4010 {
4011 MCInst Ldr;
4012 Ldr.setOpcode(AArch64::LDRXui);
4013 Ldr.addOperand(MCOperand::createReg(AArch64::X16));
4014 Ldr.addOperand(MCOperand::createReg(AArch64::X16));
4015 MCOperand SymPageOff;
4016 MCInstLowering.lowerOperand(
4019 SymPageOff);
4020 Ldr.addOperand(SymPageOff);
4022 EmitToStreamer(Ldr);
4023 }
4024
4025 EmitToStreamer(MCInstBuilder(AArch64::LDRXui)
4026 .addReg(AArch64::X16)
4027 .addReg(AArch64::X16)
4028 .addImm(0));
4029
4030 EmitToStreamer(MCInstBuilder(TM.getTargetTriple().isArm64e() ? AArch64::BRAAZ
4031 : AArch64::BR)
4032 .addReg(AArch64::X16));
4033}
4034
4035void AArch64AsmPrinter::emitMachOIFuncStubHelperBody(Module &M,
4036 const GlobalIFunc &GI,
4037 MCSymbol *LazyPointer) {
4038 // These stub helpers are only ever called once, so here we're optimizing for
4039 // minimum size by using the pre-indexed store variants, which saves a few
4040 // bytes of instructions to bump & restore sp.
4041
4042 // _ifunc.stub_helper:
4043 // stp fp, lr, [sp, #-16]!
4044 // mov fp, sp
4045 // stp x1, x0, [sp, #-16]!
4046 // stp x3, x2, [sp, #-16]!
4047 // stp x5, x4, [sp, #-16]!
4048 // stp x7, x6, [sp, #-16]!
4049 // stp d1, d0, [sp, #-16]!
4050 // stp d3, d2, [sp, #-16]!
4051 // stp d5, d4, [sp, #-16]!
4052 // stp d7, d6, [sp, #-16]!
4053 // bl _resolver
4054 // adrp x16, lazy_pointer@GOTPAGE
4055 // ldr x16, [x16, lazy_pointer@GOTPAGEOFF]
4056 // str x0, [x16]
4057 // mov x16, x0
4058 // ldp d7, d6, [sp], #16
4059 // ldp d5, d4, [sp], #16
4060 // ldp d3, d2, [sp], #16
4061 // ldp d1, d0, [sp], #16
4062 // ldp x7, x6, [sp], #16
4063 // ldp x5, x4, [sp], #16
4064 // ldp x3, x2, [sp], #16
4065 // ldp x1, x0, [sp], #16
4066 // ldp fp, lr, [sp], #16
4067 // br x16
4068
4069 EmitToStreamer(MCInstBuilder(AArch64::STPXpre)
4070 .addReg(AArch64::SP)
4071 .addReg(AArch64::FP)
4072 .addReg(AArch64::LR)
4073 .addReg(AArch64::SP)
4074 .addImm(-2));
4075
4076 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
4077 .addReg(AArch64::FP)
4078 .addReg(AArch64::SP)
4079 .addImm(0)
4080 .addImm(0));
4081
4082 for (int I = 0; I != 4; ++I)
4083 EmitToStreamer(MCInstBuilder(AArch64::STPXpre)
4084 .addReg(AArch64::SP)
4085 .addReg(AArch64::X1 + 2 * I)
4086 .addReg(AArch64::X0 + 2 * I)
4087 .addReg(AArch64::SP)
4088 .addImm(-2));
4089
4090 for (int I = 0; I != 4; ++I)
4091 EmitToStreamer(MCInstBuilder(AArch64::STPDpre)
4092 .addReg(AArch64::SP)
4093 .addReg(AArch64::D1 + 2 * I)
4094 .addReg(AArch64::D0 + 2 * I)
4095 .addReg(AArch64::SP)
4096 .addImm(-2));
4097
4098 EmitToStreamer(
4099 MCInstBuilder(AArch64::BL)
4101
4102 {
4103 MCInst Adrp;
4104 Adrp.setOpcode(AArch64::ADRP);
4105 Adrp.addOperand(MCOperand::createReg(AArch64::X16));
4106 MCOperand SymPage;
4107 MCInstLowering.lowerOperand(
4108 MachineOperand::CreateES(LazyPointer->getName().data() + 1,
4110 SymPage);
4111 Adrp.addOperand(SymPage);
4112 EmitToStreamer(Adrp);
4113 }
4114
4115 {
4116 MCInst Ldr;
4117 Ldr.setOpcode(AArch64::LDRXui);
4118 Ldr.addOperand(MCOperand::createReg(AArch64::X16));
4119 Ldr.addOperand(MCOperand::createReg(AArch64::X16));
4120 MCOperand SymPageOff;
4121 MCInstLowering.lowerOperand(
4122 MachineOperand::CreateES(LazyPointer->getName().data() + 1,
4124 SymPageOff);
4125 Ldr.addOperand(SymPageOff);
4127 EmitToStreamer(Ldr);
4128 }
4129
4130 EmitToStreamer(MCInstBuilder(AArch64::STRXui)
4131 .addReg(AArch64::X0)
4132 .addReg(AArch64::X16)
4133 .addImm(0));
4134
4135 EmitToStreamer(MCInstBuilder(AArch64::ADDXri)
4136 .addReg(AArch64::X16)
4137 .addReg(AArch64::X0)
4138 .addImm(0)
4139 .addImm(0));
4140
4141 for (int I = 3; I != -1; --I)
4142 EmitToStreamer(MCInstBuilder(AArch64::LDPDpost)
4143 .addReg(AArch64::SP)
4144 .addReg(AArch64::D1 + 2 * I)
4145 .addReg(AArch64::D0 + 2 * I)
4146 .addReg(AArch64::SP)
4147 .addImm(2));
4148
4149 for (int I = 3; I != -1; --I)
4150 EmitToStreamer(MCInstBuilder(AArch64::LDPXpost)
4151 .addReg(AArch64::SP)
4152 .addReg(AArch64::X1 + 2 * I)
4153 .addReg(AArch64::X0 + 2 * I)
4154 .addReg(AArch64::SP)
4155 .addImm(2));
4156
4157 EmitToStreamer(MCInstBuilder(AArch64::LDPXpost)
4158 .addReg(AArch64::SP)
4159 .addReg(AArch64::FP)
4160 .addReg(AArch64::LR)
4161 .addReg(AArch64::SP)
4162 .addImm(2));
4163
4164 EmitToStreamer(MCInstBuilder(TM.getTargetTriple().isArm64e() ? AArch64::BRAAZ
4165 : AArch64::BR)
4166 .addReg(AArch64::X16));
4167}
4168
4169const MCExpr *AArch64AsmPrinter::lowerConstant(const Constant *CV,
4170 const Constant *BaseCV,
4171 uint64_t Offset) {
4172 if (const GlobalValue *GV = dyn_cast<GlobalValue>(CV)) {
4173 return MCSymbolRefExpr::create(MCInstLowering.GetGlobalValueSymbol(GV, 0),
4174 OutContext);
4175 }
4176
4177 return AsmPrinter::lowerConstant(CV, BaseCV, Offset);
4178}
4179
4180char AArch64AsmPrinter::ID = 0;
4181
4182INITIALIZE_PASS(AArch64AsmPrinter, "aarch64-asm-printer",
4183 "AArch64 Assembly Printer", false, false)
4184
4185// Force static initialization.
4186extern "C" LLVM_ABI LLVM_EXTERNAL_VISIBILITY void
4187LLVMInitializeAArch64AsmPrinter() {
4193}
4194
4197 AArch64AsmPrinter &AsmPrinter = static_cast<AArch64AsmPrinter &>(
4198 MAM.getResult<AsmPrinterAnalysis>(M).getPrinter());
4201 return PreservedAnalyses::all();
4202}
4203
4207 AArch64AsmPrinter &AsmPrinter = static_cast<AArch64AsmPrinter &>(
4209 .getCachedResult<AsmPrinterAnalysis>(*MF.getFunction().getParent())
4210 ->getPrinter());
4213 return PreservedAnalyses::all();
4214}
4215
4218 AArch64AsmPrinter &AsmPrinter = static_cast<AArch64AsmPrinter &>(
4219 MAM.getResult<AsmPrinterAnalysis>(M).getPrinter());
4222 return PreservedAnalyses::all();
4223}
PtrauthCheckMode
@ Unchecked
#define GET_CB_OPC(IsImm, Width, ImmCond, RegCond)
static void emitAuthenticatedPointer(MCStreamer &OutStreamer, MCSymbol *StubLabel, const MCExpr *StubAuthPtrRef)
static bool getOptionalBooleanModuleFlag(Module &M, StringRef Name)
static cl::opt< PtrauthCheckMode > PtrauthAuthChecks("aarch64-ptrauth-auth-checks", cl::Hidden, cl::values(clEnumValN(Unchecked, "none", "don't test for failure"), clEnumValN(Poison, "poison", "poison on failure"), clEnumValN(Trap, "trap", "trap on failure")), cl::desc("Check pointer authentication auth/resign failures"))
static bool targetSupportsIRelativeRelocation(const Triple &TT)
static PtrauthCheckMode getCheckMode(const MachineFunction *MF)
assert(UImm &&(UImm !=~static_cast< T >(0)) &&"Invalid immediate!")
unsigned Imm
unsigned uint64_t
static MCDisassembler::DecodeStatus addOperand(MCInst &Inst, const MCOperand &Opnd)
MachineBasicBlock & MBB
MachineBasicBlock MachineBasicBlock::iterator DebugLoc DL
static const Function * getParent(const Value *V)
#define X(NUM, ENUM, NAME)
Definition ELF.h:857
static GCRegistry::Add< ShadowStackGC > C("shadow-stack", "Very portable GC for uncooperative code generators")
static GCRegistry::Add< StatepointGC > D("statepoint-example", "an example strategy for statepoint")
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define clEnumValN(ENUMVAL, FLAGNAME, DESC)
#define LLVM_ABI
Definition Compiler.h:215
#define LLVM_EXTERNAL_VISIBILITY
Definition Compiler.h:132
IRTranslator LLVM IR MI
Module.h This file contains the declarations for the Module class.
This header defines various interfaces for pass management in LLVM.
const AbstractManglingParser< Derived, Alloc >::OperatorInfo AbstractManglingParser< Derived, Alloc >::Ops[]
#define F(x, y, z)
Definition MD5.cpp:54
#define I(x, y, z)
Definition MD5.cpp:57
print mir2vec MIR2Vec Vocabulary Printer Pass
Definition MIR2Vec.cpp:621
Machine Check Debug Module
Register Reg
Register const TargetRegisterInfo * TRI
Promote Memory to Register
Definition Mem2Reg.cpp:110
#define P(N)
ModuleAnalysisManager MAM
#define INITIALIZE_PASS(passName, arg, name, cfg, analysis)
Definition PassSupport.h:56
static SDValue lowerConstant(SDValue Op, SelectionDAG &DAG, const RISCVSubtarget &Subtarget)
static cl::opt< RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode > Mode("regalloc-enable-advisor", cl::Hidden, cl::init(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default), cl::desc("Enable regalloc advisor mode"), cl::values(clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Default, "default", "Default"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Release, "release", "precompiled"), clEnumValN(RegAllocEvictionAdvisorAnalysisLegacy::AdvisorMode::Development, "development", "for training")))
static bool contains(SmallPtrSetImpl< ConstantExpr * > &Cache, ConstantExpr *Expr, Constant *C)
Definition Value.cpp:484
This file defines the scope_exit class, which executes user-defined cleanup logic at scope exit.
static bool printOperand(raw_ostream &OS, const SelectionDAG *G, const SDValue Value)
This file defines the SmallString class.
This file defines the SmallVector class.
This file defines the 'Statistic' class, which is designed to be an easy way to expose various metric...
#define STATISTIC(VARNAME, DESC)
Definition Statistic.h:171
std::unique_ptr< MCStreamer > && Streamer
static TableGen::Emitter::Opt Y("gen-skeleton-entry", EmitSkeleton, "Generate example skeleton entry")
static bool printAsmMRegister(const X86AsmPrinter &P, const MachineInstr &MI, const MachineOperand &MO, char Mode, raw_ostream &O)
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
PreservedAnalyses run(Module &M, ModuleAnalysisManager &MAM)
PreservedAnalyses run(MachineFunction &MF, MachineFunctionAnalysisManager &MFAM)
static const AArch64AuthMCExpr * create(const MCExpr *Expr, uint16_t Discriminator, AArch64PACKey::ID Key, bool HasAddressDiversity, MCContext &Ctx, SMLoc Loc=SMLoc())
const SetOfInstructions & getLOHRelated() const
unsigned getJumpTableEntrySize(int Idx) const
MCSymbol * getJumpTableEntryPCRelSymbol(int Idx) const
static bool shouldSignReturnAddress(SignReturnAddress Condition, bool IsLRSpilled)
std::optional< std::string > getOutliningStyle() const
const MILOHContainer & getLOHContainer() const
void setJumpTableEntryInfo(int Idx, unsigned Size, MCSymbol *PCRelSym)
static const char * getRegisterName(MCRegister Reg, unsigned AltIdx=AArch64::NoRegAltName)
static bool isTailCallReturnInst(const MachineInstr &MI)
Returns true if MI is one of the TCRETURN* instructions.
AArch64MCInstLower - This class is used to lower an MachineInstr into an MCInst.
MCSymbol * GetGlobalValueSymbol(const GlobalValue *GV, unsigned TargetFlags) const
void Lower(const MachineInstr *MI, MCInst &OutMI) const
bool lowerOperand(const MachineOperand &MO, MCOperand &MCOp) const
virtual void emitARM64WinCFISaveRegP(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveRegPX(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveAnyRegQP(unsigned Reg, int Offset)
virtual void emitAttributesSubsection(StringRef VendorName, AArch64BuildAttributes::SubsectionOptional IsOptional, AArch64BuildAttributes::SubsectionType ParameterType)
Build attributes implementation.
virtual void emitARM64WinCFISavePReg(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveFReg(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveAnyRegI(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveFRegPX(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveRegX(unsigned Reg, int Offset)
virtual void emitARM64WinCFIAllocStack(unsigned Size)
virtual void emitARM64WinCFISaveFPLRX(int Offset)
virtual void emitARM64WinCFIAllocZ(int Offset)
virtual void emitDirectiveVariantPCS(MCSymbol *Symbol)
Callback used to implement the .variant_pcs directive.
virtual void emitARM64WinCFIAddFP(unsigned Size)
virtual void emitARM64WinCFISaveFPLR(int Offset)
virtual void emitARM64WinCFISaveFRegP(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveAnyRegQPX(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveFRegX(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveZReg(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveReg(unsigned Reg, int Offset)
virtual void emitARM64WinCFISaveLRPair(unsigned Reg, int Offset)
virtual void emitAttribute(StringRef VendorName, unsigned Tag, unsigned Value, std::string String)
virtual void emitARM64WinCFISaveAnyRegIP(unsigned Reg, int Offset)
PassT::Result & getResult(IRUnitT &IR, ExtraArgTs... ExtraArgs)
Get the result of an analysis pass for a given IR unit.
void setPreservesAll()
Set by analyses that do not transform their input at all.
const T & front() const
Get the first element.
Definition ArrayRef.h:144
bool empty() const
Check if the array is empty.
Definition ArrayRef.h:136
This class is intended to be used as a driving class for all asm writers.
Definition AsmPrinter.h:91
virtual void emitGlobalAlias(const Module &M, const GlobalAlias &GA)
virtual MCSymbol * GetCPISymbol(unsigned CPID) const
Return the symbol for the specified constant pool entry.
virtual const MCExpr * lowerConstant(const Constant *CV, const Constant *BaseCV=nullptr, uint64_t Offset=0)
Lower the specified LLVM Constant to an MCExpr.
bool doInitialization(Module &M) override
Set up the AsmPrinter when we are working on a new module.
void getAnalysisUsage(AnalysisUsage &AU) const override
Record analysis usage.
bool doFinalization(Module &M) override
Shut down the asmprinter.
bool runOnMachineFunction(MachineFunction &MF) override
Emit the specified function out to the OutStreamer.
Definition AsmPrinter.h:453
virtual void emitXXStructor(const DataLayout &DL, const Constant *CV)
Targets can override this to change how global constants that are part of a C++ static/global constru...
Definition AsmPrinter.h:660
virtual void emitFunctionEntryLabel()
EmitFunctionEntryLabel - Emit the label that is the entrypoint for the function.
virtual bool PrintAsmOperand(const MachineInstr *MI, unsigned OpNo, const char *ExtraCode, raw_ostream &OS)
Print the specified operand of MI, an INLINEASM instruction, using the specified assembler variant.
virtual const MCExpr * lowerBlockAddressConstant(const BlockAddress &BA)
Lower the specified BlockAddress to an MCExpr.
Function * getFunction() const
Definition Constants.h:1126
static LLVM_ABI Constant * getIntToPtr(Constant *C, Type *Ty, bool OnlyIfReduced=false)
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition Constants.h:168
static LLVM_ABI ConstantPointerNull * get(PointerType *T)
Static factory methods - Return objects of the specified value.
Constant * getPointer() const
The pointer that is signed in this ptrauth signed pointer.
Definition Constants.h:1251
static LLVM_ABI ConstantPtrAuth * get(Constant *Ptr, ConstantInt *Key, ConstantInt *Disc, Constant *AddrDisc, Constant *DeactivationSymbol)
Return a pointer signed with the specified parameters.
ConstantInt * getKey() const
The Key ID, an i32 constant.
Definition Constants.h:1254
Constant * getDeactivationSymbol() const
Definition Constants.h:1273
bool hasAddressDiscriminator() const
Whether there is any non-null address discriminator.
Definition Constants.h:1269
ConstantInt * getDiscriminator() const
The integer discriminator, an i64 constant, or 0.
Definition Constants.h:1257
LLVM_ABI void recordFaultingOp(FaultKind FaultTy, const MCSymbol *FaultingLabel, const MCSymbol *HandlerLabel)
Definition FaultMaps.cpp:28
LLVM_ABI void serializeToFaultMapSection()
Definition FaultMaps.cpp:45
CallingConv::ID getCallingConv() const
getCallingConv()/setCallingConv(CC) - These method get and set the calling convention of this functio...
Definition Function.h:273
bool hasFnAttribute(Attribute::AttrKind Kind) const
Return true if the function has the attribute.
Definition Function.cpp:730
const Constant * getAliasee() const
Definition GlobalAlias.h:87
const Constant * getResolver() const
Definition GlobalIFunc.h:73
MDNode * getMetadata(unsigned KindID) const
Get the metadata of given kind attached to this GlobalObject.
bool hasLocalLinkage() const
bool hasExternalWeakLinkage() const
Type * getValueType() const
static LLVM_ABI IntegerType * get(LLVMContext &C, unsigned NumBits)
This static method is the primary way of constructing an IntegerType.
Definition Type.cpp:348
LLVM_ABI void emitError(const Instruction *I, const Twine &ErrorStr)
emitError - Emit an error message to the currently installed error handler with optional location inf...
static const MCBinaryExpr * createLShr(const MCExpr *LHS, const MCExpr *RHS, MCContext &Ctx)
Definition MCExpr.h:422
static const MCBinaryExpr * createAdd(const MCExpr *LHS, const MCExpr *RHS, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:342
static const MCBinaryExpr * createSub(const MCExpr *LHS, const MCExpr *RHS, MCContext &Ctx)
Definition MCExpr.h:427
static LLVM_ABI const MCConstantExpr * create(int64_t Value, MCContext &Ctx, bool PrintInHex=false, unsigned SizeInBytes=0)
Definition MCExpr.cpp:212
LLVM_ABI MCSymbol * createTempSymbol()
Create a temporary symbol with a unique name.
MCSectionELF * getELFSection(const Twine &Section, unsigned Type, unsigned Flags)
Definition MCContext.h:550
LLVM_ABI MCSymbol * getOrCreateSymbol(const Twine &Name)
Lookup the symbol inside with the specified Name.
LLVM_ABI MCSymbol * createLinkerPrivateSymbol(const Twine &Name)
Base class for the full range of assembler expressions which are needed for parsing.
Definition MCExpr.h:34
LLVM_ABI bool evaluateAsRelocatable(MCValue &Res, const MCAssembler *Asm) const
Try to evaluate the expression to a relocatable value, i.e.
Definition MCExpr.cpp:450
void addOperand(const MCOperand Op)
Definition MCInst.h:215
void setOpcode(unsigned Op)
Definition MCInst.h:201
MCSection * getDataSection() const
void setImm(int64_t Val)
Definition MCInst.h:89
static MCOperand createExpr(const MCExpr *Val)
Definition MCInst.h:166
int64_t getImm() const
Definition MCInst.h:84
static MCOperand createReg(MCRegister Reg)
Definition MCInst.h:138
static MCOperand createImm(int64_t Val)
Definition MCInst.h:145
bool isReg() const
Definition MCInst.h:65
MCRegister getRegister(unsigned i) const
getRegister - Return the specified register in the class.
uint16_t getEncodingValue(MCRegister Reg) const
Returns the encoding for Reg.
static constexpr unsigned NonUniqueID
Definition MCSection.h:585
static const MCSpecifierExpr * create(const MCExpr *Expr, Spec S, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.cpp:743
Streaming machine code generation interface.
Definition MCStreamer.h:222
virtual void emitCFIBKeyFrame()
virtual bool popSection()
Restore the current and previous section from the section stack.
virtual void emitInstruction(const MCInst &Inst, const MCSubtargetInfo &STI)
Emit the given Instruction into the current section.
virtual void emitRelocDirective(const MCExpr &Offset, StringRef Name, const MCExpr *Expr, SMLoc Loc={})
Record a relocation described by the .reloc directive.
virtual bool hasRawTextSupport() const
Return true if this asm streamer supports emitting unformatted text to the .s file with EmitRawText.
Definition MCStreamer.h:385
MCContext & getContext() const
Definition MCStreamer.h:326
virtual void AddComment(const Twine &T, bool EOL=true)
Add a textual comment.
Definition MCStreamer.h:404
virtual void emitCFIMTETaggedFrame()
void emitValue(const MCExpr *Value, unsigned Size, SMLoc Loc=SMLoc())
virtual void emitLabel(MCSymbol *Symbol, SMLoc Loc=SMLoc())
Emit a label for Symbol into the current section.
MCTargetStreamer * getTargetStreamer()
Definition MCStreamer.h:336
void pushSection()
Save the current and previous section on the section stack.
Definition MCStreamer.h:460
virtual void switchSection(MCSection *Section, uint32_t Subsec=0)
Set the current section where code is being emitted to Section.
MCSection * getCurrentSectionOnly() const
Definition MCStreamer.h:438
void emitRawText(const Twine &String)
If this file is backed by a assembly streamer, this dumps the specified string in the output ....
const FeatureBitset & getFeatureBits() const
static const MCSymbolRefExpr * create(const MCSymbol *Symbol, MCContext &Ctx, SMLoc Loc=SMLoc())
Definition MCExpr.h:213
MCSymbol - Instances of this class represent a symbol name in the MC file, and MCSymbols are created ...
Definition MCSymbol.h:42
LLVM_ABI void print(raw_ostream &OS, const MCAsmInfo *MAI) const
print - Print the value to the stream OS.
Definition MCSymbol.cpp:59
StringRef getName() const
getName - Get the symbol name.
Definition MCSymbol.h:188
const MCSymbol * getAddSym() const
Definition MCValue.h:49
int64_t getConstant() const
Definition MCValue.h:44
MachineInstrBundleIterator< const MachineInstr > const_iterator
LLVM_ABI MCSymbol * getSymbol() const
Return the MCSymbol for this basic block.
const MachineFunction * getParent() const
Return the MachineFunction containing this basic block.
CalledGlobalInfo tryGetCalledGlobal(const MachineInstr *MI) const
Tries to get the global and target flags for a call site, if the instruction is a call to a global.
const TargetSubtargetInfo & getSubtarget() const
getSubtarget - Return the subtarget for which this machine code is being compiled.
MCContext & getContext() const
Function & getFunction()
Return the LLVM function that this machine code represents.
Ty * getInfo()
getInfo - Keep track of various per-function pieces of information for backends that would like to do...
const MachineBasicBlock & front() const
const MachineJumpTableInfo * getJumpTableInfo() const
getJumpTableInfo - Return the jump table info object for the current function.
bool readsRegister(Register Reg, const TargetRegisterInfo *TRI) const
Return true if the MachineInstr reads the specified register.
mop_range operands()
LLVM_ABI const MachineFunction * getMF() const
Return the function that contains the basic block that this instruction belongs to.
const MachineOperand & getOperand(unsigned i) const
const std::vector< MachineJumpTableEntry > & getJumpTables() const
unsigned getSubReg() const
static MachineOperand CreateMCSymbol(MCSymbol *Sym, unsigned TargetFlags=0)
const GlobalValue * getGlobal() const
static MachineOperand CreateES(const char *SymName, unsigned TargetFlags=0)
int64_t getImm() const
bool isReg() const
isReg - Tests if this is a MO_Register operand.
MachineBasicBlock * getMBB() const
bool isImm() const
isImm - Tests if this is a MO_Immediate operand.
const BlockAddress * getBlockAddress() const
void setOffset(int64_t Offset)
bool isGlobal() const
isGlobal - Tests if this is a MO_GlobalAddress operand.
MachineOperandType getType() const
getType - Returns the MachineOperandType for this operand.
Register getReg() const
getReg - Returns the register number.
@ MO_Immediate
Immediate operand.
@ MO_GlobalAddress
Address of a global value.
@ MO_BlockAddress
Address of a basic block.
@ MO_Register
Register operand.
@ MO_ExternalSymbol
Name of external global symbol.
int64_t getOffset() const
Return the offset from the symbol in this operand.
This class implements a map that also provides access to all stored values in a deterministic order.
Definition MapVector.h:38
Root of the metadata hierarchy.
Definition Metadata.h:64
A Module instance is used to store all the information related to an LLVM module.
Definition Module.h:68
A set of analyses that are preserved following a run of a transformation pass.
Definition Analysis.h:112
static PreservedAnalyses all()
Construct a special preserved set that preserves all passes.
Definition Analysis.h:118
constexpr bool isPhysical() const
Return true if the specified register number is in the physical register namespace.
Definition Register.h:83
static SectionKind getMetadata()
size_type count(ConstPtrType Ptr) const
count - Return 1 if the specified pointer is in the set, 0 otherwise.
void push_back(const T &Elt)
LLVM_ABI void recordStatepoint(const MCSymbol &L, const MachineInstr &MI)
Generate a stackmap record for a statepoint instruction.
LLVM_ABI void recordPatchPoint(const MCSymbol &L, const MachineInstr &MI)
Generate a stackmap record for a patchpoint instruction.
LLVM_ABI void recordStackMap(const MCSymbol &L, const MachineInstr &MI)
Generate a stackmap record for a stackmap instruction.
Represent a constant reference to a string, i.e.
Definition StringRef.h:56
constexpr const char * data() const
Get a pointer to the start of the string (which may not be null terminated).
Definition StringRef.h:138
virtual MCSection * getSectionForJumpTable(const Function &F, const TargetMachine &TM) const
Primary interface to the complete machine description for the target machine.
bool regsOverlap(Register RegA, Register RegB) const
Returns true if the two registers are equal or alias each other.
Triple - Helper class for working with autoconf configuration names.
Definition Triple.h:48
bool isFunctionTy() const
True if this is an instance of FunctionType.
Definition Type.h:273
LLVM Value Representation.
Definition Value.h:75
LLVMContext & getContext() const
All values hold a context through their type.
Definition Value.h:258
LLVM_ABI const Value * stripAndAccumulateConstantOffsets(const DataLayout &DL, APInt &Offset, bool AllowNonInbounds, bool AllowInvariantGroup=false, function_ref< bool(Value &Value, APInt &Offset)> ExternalAnalysis=nullptr, bool LookThroughIntToPtr=false) const
Accumulate the constant offset this value has compared to a base pointer.
LLVM_ABI StringRef getName() const
Return a constant reference to the value's name.
Definition Value.cpp:319
Pass manager infrastructure for declaring and invalidating analyses.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
LLVM_ABI StringRef getVendorName(unsigned const Vendor)
@ MO_NC
MO_NC - Indicates whether the linker is expected to check the symbol reference for overflow.
@ MO_G1
MO_G1 - A symbol operand with this flag (granule 1) represents the bits 16-31 of a 64-bit address,...
@ MO_S
MO_S - Indicates that the bits of the symbol operand represented by MO_G0 etc are signed.
@ MO_PAGEOFF
MO_PAGEOFF - A symbol operand with this flag represents the offset of that symbol within a 4K page.
@ MO_GOT
MO_GOT - This flag indicates that a symbol operand represents the address of the GOT entry for the sy...
@ MO_G0
MO_G0 - A symbol operand with this flag (granule 0) represents the bits 0-15 of a 64-bit address,...
@ MO_PAGE
MO_PAGE - A symbol operand with this flag represents the pc-relative offset of the 4K page containing...
@ MO_TLS
MO_TLS - Indicates that the operand being accessed is some kind of thread-local symbol.
constexpr AArch64PACKey::ID InitFiniKey
PAuth key to be used with function pointers in .init_array and .fini_array.
AuthCheckMethod
Variants of check performed on an authenticated pointer.
constexpr unsigned InitFiniPointerConstantDiscriminator
Constant discriminator to be used with function pointers in .init_array and .fini_array.
static unsigned getShiftValue(unsigned Imm)
getShiftValue - Extract the shift value.
static uint64_t encodeLogicalImmediate(uint64_t imm, unsigned regSize)
encodeLogicalImmediate - Return the encoded immediate value for a logical immediate instruction of th...
static unsigned getShifterImm(AArch64_AM::ShiftExtendType ST, unsigned Imm)
getShifterImm - Encode the shift type and amount: imm: 6-bit shift amount shifter: 000 ==> lsl 001 ==...
constexpr char Align[]
Key for Kernel::Arg::Metadata::mAlign.
@ SectionSize
Definition COFF.h:61
SymbolStorageClass
Storage class tells where and what the symbol represents.
Definition COFF.h:218
@ IMAGE_SYM_CLASS_EXTERNAL
External symbol.
Definition COFF.h:224
@ IMAGE_SYM_CLASS_STATIC
Static.
Definition COFF.h:225
@ IMAGE_SYM_DTYPE_FUNCTION
A function that returns a base type.
Definition COFF.h:276
@ SCT_COMPLEX_TYPE_SHIFT
Type is formed as (base + (derived << SCT_COMPLEX_TYPE_SHIFT))
Definition COFF.h:280
@ AARCH64_PAUTH_PLATFORM_LLVM_LINUX
Definition ELF.h:1879
@ SHF_ALLOC
Definition ELF.h:1259
@ SHF_GROUP
Definition ELF.h:1281
@ SHF_EXECINSTR
Definition ELF.h:1262
@ GNU_PROPERTY_AARCH64_FEATURE_1_BTI
Definition ELF.h:1870
@ GNU_PROPERTY_AARCH64_FEATURE_1_PAC
Definition ELF.h:1871
@ GNU_PROPERTY_AARCH64_FEATURE_1_GCS
Definition ELF.h:1872
@ SHT_PROGBITS
Definition ELF.h:1157
@ S_REGULAR
S_REGULAR - Regular section.
Definition MachO.h:127
void emitInstruction(MCObjectStreamer &, const MCInst &Inst, const MCSubtargetInfo &STI)
ValuesClass values(OptsTy... Options)
Helper to build a ValuesClass by forwarding a variable number of arguments as an initializer list to ...
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > extract_or_null(Y &&MD)
Extract a Value from Metadata, allowing null.
Definition Metadata.h:683
std::enable_if_t< detail::IsValidPointer< X, Y >::value, X * > extract(Y &&MD)
Extract a Value from Metadata.
Definition Metadata.h:668
NodeAddr< NodeBase * > Node
Definition RDFGraph.h:381
bool empty() const
Definition BasicBlock.h:101
This is an optimization pass for GlobalISel generic memory operations.
auto drop_begin(T &&RangeOrContainer, size_t N=1)
Return a range covering RangeOrContainer with the first N elements excluded.
Definition STLExtras.h:315
@ Offset
Definition DWP.cpp:577
LLVM_ABI std::optional< std::string > getArm64ECMangledFunctionName(StringRef Name)
Returns the ARM64EC mangled function name unless the input is already mangled.
Definition Mangler.cpp:292
auto size(R &&Range, std::enable_if_t< std::is_base_of< std::random_access_iterator_tag, typename std::iterator_traits< decltype(Range.begin())>::iterator_category >::value, void > *=nullptr)
Get the size of a range.
Definition STLExtras.h:1669
OuterAnalysisManagerProxy< ModuleAnalysisManager, MachineFunction > ModuleAnalysisManagerMachineFunctionProxy
Provide the ModuleAnalysisManager to Function proxy.
constexpr bool isInt(int64_t x)
Checks if an integer fits into the given bit width.
Definition MathExtras.h:166
decltype(auto) dyn_cast(const From &Val)
dyn_cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:643
scope_exit(Callable) -> scope_exit< Callable >
static unsigned getXPACOpcodeForKey(AArch64PACKey::ID K)
Return XPAC opcode to be used for a ptrauth strip using the given key.
Target & getTheAArch64beTarget()
std::string utostr(uint64_t X, bool isNeg=false)
AnalysisManager< MachineFunction > MachineFunctionAnalysisManager
static unsigned getBranchOpcodeForKey(bool IsCall, AArch64PACKey::ID K, bool Zero)
Return B(L)RA opcode to be used for an authenticated branch or call using the given key,...
RelativeUniformCounterPtr ValuesPtrExpr VTableAddr Value
Definition InstrProf.h:143
Target & getTheAArch64leTarget()
auto dyn_cast_or_null(const Y &Val)
Definition Casting.h:753
Target & getTheAArch64_32Target()
MachineInstr * getImm(const MachineOperand &MO, const MachineRegisterInfo *MRI)
LLVM_ABI void setupModuleAsmPrinter(Module &M, ModuleAnalysisManager &MAM, AsmPrinter &AsmPrinter)
LLVM_ABI void report_fatal_error(Error Err, bool gen_crash_diag=true)
Definition Error.cpp:163
constexpr bool isUInt(uint64_t x)
Checks if an unsigned integer fits into the given bit width.
Definition MathExtras.h:190
class LLVM_GSL_OWNER SmallVector
Forward declaration of SmallVector so that calculateSmallVectorDefaultInlinedElements can reference s...
bool isa(const From &Val)
isa<X> - Return true if the parameter to the template is an instance of one of the template type argu...
Definition Casting.h:547
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
Target & getTheARM64_32Target()
static MCRegister getXRegFromWReg(MCRegister Reg)
@ Add
Sum of integers.
Target & getTheARM64Target()
DWARFExpression::Operation Op
ArrayRef(const T &OneElt) -> ArrayRef< T >
static MCRegister getXRegFromXRegTuple(MCRegister RegTuple)
static unsigned getPACOpcodeForKey(AArch64PACKey::ID K, bool Zero)
Return PAC opcode to be used for a ptrauth sign using the given key, or its PAC*Z variant that doesn'...
static MCRegister getWRegFromXReg(MCRegister Reg)
OutputIt move(R &&Range, OutputIt Out)
Provide wrappers to std::move which take ranges instead of having to pass begin/end explicitly.
Definition STLExtras.h:1917
ExceptionHandling
Definition CodeGen.h:54
LLVM_ABI void setupMachineFunctionAsmPrinter(MachineFunctionAnalysisManager &MFAM, MachineFunction &MF, AsmPrinter &AsmPrinter)
decltype(auto) cast(const From &Val)
cast<X> - Return the argument parameter cast to the specified type.
Definition Casting.h:559
bool is_contained(R &&Range, const E &Element)
Returns true if Element is found in Range.
Definition STLExtras.h:1947
static unsigned getAUTOpcodeForKey(AArch64PACKey::ID K, bool Zero)
Return AUT opcode to be used for a ptrauth auth using the given key, or its AUT*Z variant that doesn'...
@ MCSA_Weak
.weak
@ MCSA_WeakAntiDep
.weak_anti_dep (COFF)
@ MCSA_ELF_TypeFunction
.type _foo, STT_FUNC # aka @function
@ MCSA_Hidden
.hidden (ELF)
AnalysisManager< Module > ModuleAnalysisManager
Convenience typedef for the Module analysis manager.
Definition MIRParser.h:39
MCRegisterClass TargetRegisterClass
Definition FastISel.h:58
LLVM_ABI void reportFatalUsageError(Error Err)
Report a fatal error that does not indicate a bug in LLVM.
Definition Error.cpp:177
Implement std::hash so that hash_code can be used in STL containers.
Definition BitVector.h:878
#define EQ(a, b)
Definition regexec.c:65
RegisterAsmPrinter - Helper template for registering a target specific assembly printer,...