Index: llvm/include/llvm/CodeGen/CFIFixup.h =================================================================== --- /dev/null +++ llvm/include/llvm/CodeGen/CFIFixup.h @@ -0,0 +1,38 @@ +//===------ CFIFixup.h - Insert CFI remember/restore instructions ---------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// +/// \file +/// Contains definition of the base CFIFixup pass. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_CODEGEN_CFIFIXUP_H +#define LLVM_CODEGEN_CFIFIXUP_H + +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/InitializePasses.h" + +namespace llvm { +class CFIFixup : public MachineFunctionPass { +public: + static char ID; + + CFIFixup() : MachineFunctionPass(ID) { + initializeCFIFixupPass(*PassRegistry::getPassRegistry()); + } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesAll(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; +} // namespace llvm + +#endif // LLVM_CODEGEN_CFIFIXUP_H Index: llvm/include/llvm/CodeGen/Passes.h =================================================================== --- llvm/include/llvm/CodeGen/Passes.h +++ llvm/include/llvm/CodeGen/Passes.h @@ -494,6 +494,9 @@ // This pass expands indirectbr instructions. FunctionPass *createIndirectBrExpandPass(); + /// Creates CFI Fixup pass. \see CFIFixup.cpp + FunctionPass *createCFIFixup(); + /// Creates CFI Instruction Inserter pass. \see CFIInstrInserter.cpp FunctionPass *createCFIInstrInserter(); Index: llvm/include/llvm/CodeGen/TargetFrameLowering.h =================================================================== --- llvm/include/llvm/CodeGen/TargetFrameLowering.h +++ llvm/include/llvm/CodeGen/TargetFrameLowering.h @@ -223,6 +223,14 @@ emitCalleeSavedFrameMovesFullCFA(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const {} + /// Returns true if we may need to fix the unwind infportmation for the + /// function. + virtual bool enableCFIFixup(MachineFunction &MF) const; + + /// Emit CFI instructions that recreate the state of the unwind information + /// upon fucntion entry. + virtual void resetCFIToInitialState(MachineBasicBlock &MBB) const {} + /// Replace a StackProbe stub (if any) with the actual probe code inline virtual void inlineStackProbe(MachineFunction &MF, MachineBasicBlock &PrologueMBB) const {} Index: llvm/include/llvm/InitializePasses.h =================================================================== --- llvm/include/llvm/InitializePasses.h +++ llvm/include/llvm/InitializePasses.h @@ -103,6 +103,7 @@ void initializeCFGuardPass(PassRegistry&); void initializeCFGuardLongjmpPass(PassRegistry&); void initializeCFGViewerLegacyPassPass(PassRegistry&); +void initializeCFIFixupPass(PassRegistry&); void initializeCFIInstrInserterPass(PassRegistry&); void initializeCFLAndersAAWrapperPassPass(PassRegistry&); void initializeCFLSteensAAWrapperPassPass(PassRegistry&); Index: llvm/include/llvm/Target/TargetMachine.h =================================================================== --- llvm/include/llvm/Target/TargetMachine.h +++ llvm/include/llvm/Target/TargetMachine.h @@ -260,6 +260,8 @@ Options.SupportsDebugEntryValues = Enable; } + void setCFIFixup(bool Enable) { Options.EnableCFIFixup = Enable; } + bool getAIXExtendedAltivecABI() const { return Options.EnableAIXExtendedAltivecABI; } Index: llvm/include/llvm/Target/TargetOptions.h =================================================================== --- llvm/include/llvm/Target/TargetOptions.h +++ llvm/include/llvm/Target/TargetOptions.h @@ -143,6 +143,7 @@ ValueTrackingVariableLocations(false), ForceDwarfFrameSection(false), XRayOmitFunctionIndex(false), DebugStrictDwarf(false), Hotpatch(false), PPCGenScalarMASSEntries(false), JMCInstrument(false), + EnableCFIFixup(false), FPDenormalMode(DenormalMode::IEEE, DenormalMode::IEEE) {} /// DisableFramePointerElim - This returns true if frame pointer elimination @@ -351,6 +352,9 @@ /// Enable JustMyCode instrumentation. unsigned JMCInstrument : 1; + /// Enable the CFIFixup pass. + unsigned EnableCFIFixup : 1; + /// Name of the stack usage file (i.e., .su file) if user passes /// -fstack-usage. If empty, it can be implied that -fstack-usage is not /// passed on the command line. Index: llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp =================================================================== --- llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp +++ llvm/lib/CodeGen/AsmPrinter/AsmPrinterDwarf.cpp @@ -277,6 +277,12 @@ case MCCFIInstruction::OpUndefined: OutStreamer->emitCFIUndefined(Inst.getRegister()); break; + case MCCFIInstruction::OpRememberState: + OutStreamer->emitCFIRememberState(); + break; + case MCCFIInstruction::OpRestoreState: + OutStreamer->emitCFIRestoreState(); + break; } } Index: llvm/lib/CodeGen/CFIFixup.cpp =================================================================== --- /dev/null +++ llvm/lib/CodeGen/CFIFixup.cpp @@ -0,0 +1,210 @@ +//===------ CFIFixup.cpp - Insert CFI remember/restore instructions -------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// This pass inserts the necessary .cfi_remember_state and +// .cfi_restore_state CFI instructions to adjust for the inconsistency of +// the call-frame information caused by final machine basic block layout. +// This pass relies in constraints LLVM imposes on the placement of +// save/restore points (cf. ShrinkWrap): +// * there is a single basic block, containing the function prologue +// * possibly multiple epilogue blocks, where each epilogue block is +// complete and self-contained, i.e. CSR restore instructions (and the +// corresponding CFI instructions are not split across two or more blocks. +// * prologue and epilogue blocks are outside of any loops +// Thus, during execution, at the beginning and at the end of each basic block +// the function can be in one of two states: +// - "has a call frame", if the function has executed the prologue, or +// has not executed any epilogue +// - "does not have a call frame", if the function has not executed the +// prologue, or has executed an epilogue +// which can be computed by a single RPO traversal. + +// In order to acommodate backends, which do not generate unwind info in +// epilogues we compute an additional property "strong no call frame", which +// is set for the entry point of the function and for every block reachable +// from the entry along a path that does not execute the prologue. If this +// property holds, it takes precedence over the "has a call frame" property. + +// From the point of view of the unwind tables, the "has/does not have call +// frame" state at beginning of each block is determined by the state at the end +// of the previous block, in layout order. Where these states differ, we insert +// compensating CFI instructions, which come in two flavours: + +// - CFI instructions, which reset the unwind table state to the initial one. +// This is done by a target specific hook and is expected to be trivial +// to implement, for example it could be: +// .cfi_def_cfa , 0 +// .cfi_same_value +// .cfi_same_value +// ... +// where are the callee-saved registers. +// - CFI instructions, which reset the unwind table state to the one +// created by the function prologue. These are +// .cfi_restore_state +// .cfi_remember_state +// In this case we also insert a `.cfi_remember_state` after the last CFI +// instruction in the function prologue. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/CFIFixup.h" + +#include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/SmallBitVector.h" +#include "llvm/CodeGen/Passes.h" +#include "llvm/CodeGen/TargetFrameLowering.h" +#include "llvm/CodeGen/TargetInstrInfo.h" +#include "llvm/CodeGen/TargetSubtargetInfo.h" +#include "llvm/MC/MCAsmInfo.h" +#include "llvm/MC/MCDwarf.h" +#include "llvm/Target/TargetMachine.h" + +using namespace llvm; + +#define DEBUG_TYPE "cfi-fixup" + +char CFIFixup::ID = 0; + +INITIALIZE_PASS(CFIFixup, "cfi-fixup", + "Insert CFI remember/restore state instructions", false, false) +FunctionPass *llvm::createCFIFixup() { return new CFIFixup(); } + +static bool isPrologueCFIInstruction(const MachineInstr &MI) { + return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && + MI.getFlag(MachineInstr::FrameSetup); +} + +static bool containsPrologue(const MachineBasicBlock &MBB) { + return llvm::any_of(MBB.instrs(), isPrologueCFIInstruction); +} + +static bool containsEpilogue(const MachineBasicBlock &MBB) { + return llvm::any_of(make_range(MBB.rbegin(), MBB.rend()), [](const auto &MI) { + return MI.getOpcode() == TargetOpcode::CFI_INSTRUCTION && + MI.getFlag(MachineInstr::FrameDestroy); + }); +} + +bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { + const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering(); + if (!TFL.enableCFIFixup(MF)) + return false; + + unsigned NumBlocks = MF.size(); + if (NumBlocks < 2) + return false; + + struct BlockFlags { + bool StrongNoFrameOnEntry : 1; + bool HasFrameOnEntry : 1; + bool HasFrameOnExit : 1; + }; + SmallVector BlockInfo(NumBlocks, {false, false, false}); + BlockInfo[0].StrongNoFrameOnEntry = true; + + // Compute the presence/absence of frame at each basic block. + MachineBasicBlock *PrologueBlock = nullptr; + ReversePostOrderTraversal RPOT(&*MF.begin()); + for (auto *MBB : RPOT) { + auto &Info = BlockInfo[MBB->getNumber()]; + + // Set to true if the current block contains the prologue or the epilogue, + // respectively. + bool HasPrologue = false; + bool HasEpilogue = false; + + if (!PrologueBlock && !Info.HasFrameOnEntry && containsPrologue(*MBB)) { + PrologueBlock = MBB; + HasPrologue = true; + } + + if (Info.HasFrameOnEntry || HasPrologue) + HasEpilogue = containsEpilogue(*MBB); + + // If the function has a call frame at the entry of the current block or the + // current block contains the prologue, then the function has a call frame + // at the exit of the block, unless the block contains the epilogue. + Info.HasFrameOnExit = (Info.HasFrameOnEntry || HasPrologue) && !HasEpilogue; + + // Set the successors' state on entry. + for (auto *Succ : MBB->successors()) { + auto &SuccInfo = BlockInfo[Succ->getNumber()]; + SuccInfo.StrongNoFrameOnEntry |= + Info.StrongNoFrameOnEntry && !HasPrologue && !HasEpilogue; + SuccInfo.HasFrameOnEntry = Info.HasFrameOnExit; + } + } + + if (!PrologueBlock) + return false; + + // Walk the blocks of the function in "physical" order. After each block, the + // current frame state (as recorded in the unwind tables) is the same as + // at the end of that block. + // At each block, where the current frame state differs from what it + // should be at the beginning of that block, insert compensating CFI + // instructions. + const TargetInstrInfo &TII = *MF.getSubtarget().getInstrInfo(); + bool Change = false; + // `InsertPt` always points to the point in a preceding block where we have to + // insert a `.cfi_remember_state`, in the case that the current block needs a + // `.cfi_restore_state`. + MachineBasicBlock *InsertMBB = PrologueBlock; + MachineBasicBlock::iterator InsertPt = PrologueBlock->begin(); + for (MachineInstr &MI : PrologueBlock->instrs()) { + if (isPrologueCFIInstruction(MI)) + InsertPt = std::next(MI.getIterator()); + } + assert(InsertPt != PrologueBlock->begin() && + "Inconsistent notion of \"prologue block\""); + + // No point starting before the prologue block. + // TODO: the unwind tables will still be incorrect if an epilogue physcally + // preceeds the prologue. + MachineFunction::iterator CurrBB = std::next(PrologueBlock->getIterator()); + bool HasFrame = BlockInfo[PrologueBlock->getNumber()].HasFrameOnExit; + while (CurrBB != MF.end()) { + auto &Info = BlockInfo[CurrBB->getNumber()]; +#ifndef NDEBUG + if (!Info.StrongNoFrameOnEntry) { + for (auto *Pred : CurrBB->predecessors()) + assert(Info.HasFrameOnEntry == + BlockInfo[Pred->getNumber()].HasFrameOnExit && + "Inconsistent call frame state"); + } +#endif + if (!Info.StrongNoFrameOnEntry && Info.HasFrameOnEntry && !HasFrame) { + // Reset to the "after prologue" state. + + // Insert a `.cfi_remember_state` into the last block known to have a + // stack frame. + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createRememberState(nullptr)); + BuildMI(*InsertMBB, InsertPt, DebugLoc(), + TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + // Insert a `.cfi_restore_state` at the beginning of the current block. + CFIIndex = MF.addFrameInst(MCCFIInstruction::createRestoreState(nullptr)); + InsertPt = BuildMI(*CurrBB, CurrBB->begin(), DebugLoc(), + TII.get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(CFIIndex); + ++InsertPt; + InsertMBB = &*CurrBB; + Change = true; + } else if (!Info.HasFrameOnEntry && HasFrame) { + // Reset to the state upon function entry. + TFL.resetCFIToInitialState(*CurrBB); + Change = true; + } + + HasFrame = Info.HasFrameOnExit; + ++CurrBB; + } + + return Change; +} Index: llvm/lib/CodeGen/CMakeLists.txt =================================================================== --- llvm/lib/CodeGen/CMakeLists.txt +++ llvm/lib/CodeGen/CMakeLists.txt @@ -39,6 +39,7 @@ CallingConvLower.cpp CFGuardLongjmp.cpp CFIInstrInserter.cpp + CFIFixup.cpp CodeGen.cpp CodeGenCommonISel.cpp CodeGenPassBuilder.cpp Index: llvm/lib/CodeGen/CodeGen.cpp =================================================================== --- llvm/lib/CodeGen/CodeGen.cpp +++ llvm/lib/CodeGen/CodeGen.cpp @@ -24,6 +24,7 @@ initializeBranchFolderPassPass(Registry); initializeBranchRelaxationPass(Registry); initializeCFGuardLongjmpPass(Registry); + initializeCFIFixupPass(Registry); initializeCFIInstrInserterPass(Registry); initializeCheckDebugMachineModulePass(Registry); initializeCodeGenPreparePass(Registry); Index: llvm/lib/CodeGen/TailDuplicator.cpp =================================================================== --- llvm/lib/CodeGen/TailDuplicator.cpp +++ llvm/lib/CodeGen/TailDuplicator.cpp @@ -385,8 +385,9 @@ // Allow duplication of CFI instructions. if (MI->isCFIInstruction()) { BuildMI(*PredBB, PredBB->end(), PredBB->findDebugLoc(PredBB->begin()), - TII->get(TargetOpcode::CFI_INSTRUCTION)).addCFIIndex( - MI->getOperand(0).getCFIIndex()); + TII->get(TargetOpcode::CFI_INSTRUCTION)) + .addCFIIndex(MI->getOperand(0).getCFIIndex()) + .setMIFlags(MI->getFlags()); return; } MachineInstr &NewMI = TII->duplicate(*PredBB, PredBB->end(), *MI); Index: llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp =================================================================== --- llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp +++ llvm/lib/CodeGen/TargetFrameLoweringImpl.cpp @@ -21,6 +21,7 @@ #include "llvm/IR/CallingConv.h" #include "llvm/IR/Function.h" #include "llvm/IR/InstrTypes.h" +#include "llvm/MC/MCAsmInfo.h" #include "llvm/MC/MCRegisterInfo.h" #include "llvm/Support/Compiler.h" #include "llvm/Target/TargetMachine.h" @@ -37,6 +38,11 @@ return false; } +bool TargetFrameLowering::enableCFIFixup(MachineFunction &MF) const { + return MF.needsFrameMoves() && + !MF.getTarget().getMCAsmInfo()->usesWindowsCFI(); +} + /// Returns the displacement from the frame register to the stack /// frame of the specified index, along with the frame register used /// (in output arg FrameReg). This is the default implementation which Index: llvm/lib/CodeGen/TargetPassConfig.cpp =================================================================== --- llvm/lib/CodeGen/TargetPassConfig.cpp +++ llvm/lib/CodeGen/TargetPassConfig.cpp @@ -139,6 +139,12 @@ "Disable all outlining"), // Sentinel value for unspecified option. clEnumValN(RunOutliner::AlwaysOutline, "", ""))); +// Disable the pass to fix unwind information. Whether the pass is included in +// the pipeline is controlled via the target options, this option serves as +// manual override. +static cl::opt DisableCFIFixup("disable-cfi-fixup", cl::init(false), + cl::Hidden, + cl::desc("Disable the CFI fixup pass")); // Enable or disable FastISel. Both options are needed, because // FastISel is enabled by default with -fast, and we wish to be // able to enable or disable fast-isel independently from -O0. @@ -1267,6 +1273,9 @@ addPass(createMachineFunctionSplitterPass()); } + if (!DisableCFIFixup && TM->Options.EnableCFIFixup) + addPass(createCFIFixup()); + // Add passes that directly emit MI after all other MI passes. addPreEmitPass2(); Index: llvm/lib/Target/AArch64/AArch64.h =================================================================== --- llvm/lib/Target/AArch64/AArch64.h +++ llvm/lib/Target/AArch64/AArch64.h @@ -71,6 +71,7 @@ void initializeAArch64A57FPLoadBalancingPass(PassRegistry&); void initializeAArch64AdvSIMDScalarPass(PassRegistry&); void initializeAArch64BranchTargetsPass(PassRegistry&); +void initializeAArch64CFIFixupPass(PassRegistry&); void initializeAArch64CollectLOHPass(PassRegistry&); void initializeAArch64CondBrTuningPass(PassRegistry &); void initializeAArch64CompressJumpTablesPass(PassRegistry&); Index: llvm/lib/Target/AArch64/AArch64FrameLowering.h =================================================================== --- llvm/lib/Target/AArch64/AArch64FrameLowering.h +++ llvm/lib/Target/AArch64/AArch64FrameLowering.h @@ -29,6 +29,8 @@ void emitCalleeSavedFrameMoves(MachineBasicBlock &MBB, MachineBasicBlock::iterator MBBI) const; + void resetCFIToInitialState(MachineBasicBlock &MBB) const override; + MachineBasicBlock::iterator eliminateCallFramePseudoInstr(MachineFunction &MF, MachineBasicBlock &MBB, MachineBasicBlock::iterator I) const override; Index: llvm/lib/Target/AArch64/AArch64FrameLowering.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64FrameLowering.cpp +++ llvm/lib/Target/AArch64/AArch64FrameLowering.cpp @@ -580,6 +580,57 @@ emitCalleeSavedSVELocations(MBB, MBBI); } +static void insertCFISameValue(const MCInstrDesc &Desc, MachineFunction &MF, + MachineBasicBlock &MBB, + MachineBasicBlock::iterator InsertPt, + unsigned DwarfReg) { + unsigned CFIIndex = + MF.addFrameInst(MCCFIInstruction::createSameValue(nullptr, DwarfReg)); + BuildMI(MBB, InsertPt, DebugLoc(), Desc).addCFIIndex(CFIIndex); +} + +void AArch64FrameLowering::resetCFIToInitialState( + MachineBasicBlock &MBB) const { + + MachineFunction &MF = *MBB.getParent(); + const auto &Subtarget = MF.getSubtarget(); + const TargetInstrInfo &TII = *Subtarget.getInstrInfo(); + const auto &TRI = + static_cast(*Subtarget.getRegisterInfo()); + const auto &MFI = *MF.getInfo(); + + const MCInstrDesc &CFIDesc = TII.get(TargetOpcode::CFI_INSTRUCTION); + DebugLoc DL; + + // Reset the CFA to `SP + 0`. + MachineBasicBlock::iterator InsertPt = MBB.begin(); + unsigned CFIIndex = MF.addFrameInst(MCCFIInstruction::cfiDefCfa( + nullptr, TRI.getDwarfRegNum(AArch64::SP, true), 0)); + BuildMI(MBB, InsertPt, DL, CFIDesc).addCFIIndex(CFIIndex); + + // Flip the RA sign state. + if (MFI.shouldSignReturnAddress()) { + CFIIndex = MF.addFrameInst(MCCFIInstruction::createNegateRAState(nullptr)); + BuildMI(MBB, InsertPt, DL, CFIDesc).addCFIIndex(CFIIndex); + } + + // X18 could have been used for the shadow call stack, reset it. + if (Subtarget.isXRegisterReserved(18)) + insertCFISameValue(CFIDesc, MF, MBB, InsertPt, + TRI.getDwarfRegNum(AArch64::X18, true)); + + // Emit .cfi_same_value for callee-saved registers. + const std::vector &CSI = + MF.getFrameInfo().getCalleeSavedInfo(); + for (const auto &Info : CSI) { + unsigned Reg = Info.getReg(); + if (!TRI.regNeedsCFI(Reg, Reg)) + continue; + insertCFISameValue(CFIDesc, MF, MBB, InsertPt, + TRI.getDwarfRegNum(Reg, true)); + } +} + // Find a scratch register that we can use at the start of the prologue to // re-align the stack pointer. We avoid using callee-save registers since they // may appear to be free when this is called from canUseAsPrologue (during Index: llvm/lib/Target/AArch64/AArch64TargetMachine.cpp =================================================================== --- llvm/lib/Target/AArch64/AArch64TargetMachine.cpp +++ llvm/lib/Target/AArch64/AArch64TargetMachine.cpp @@ -21,6 +21,7 @@ #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/CFIFixup.h" #include "llvm/CodeGen/CSEConfigBase.h" #include "llvm/CodeGen/GlobalISel/IRTranslator.h" #include "llvm/CodeGen/GlobalISel/InstructionSelect.h" @@ -354,6 +355,11 @@ // AArch64 supports the debug entry values. setSupportsDebugEntryValues(true); + + // AArch64 supports fixing up the DWARF unwind information. + if (!getMCAsmInfo()->usesWindowsCFI() && + !getTargetTriple().isOSBinFormatMachO()) + setCFIFixup(true); } AArch64TargetMachine::~AArch64TargetMachine() = default; Index: llvm/test/CodeGen/AArch64/O0-pipeline.ll =================================================================== --- llvm/test/CodeGen/AArch64/O0-pipeline.ll +++ llvm/test/CodeGen/AArch64/O0-pipeline.ll @@ -67,6 +67,7 @@ ; CHECK-NEXT: Contiguously Lay Out Funclets ; CHECK-NEXT: StackMap Liveness Analysis ; CHECK-NEXT: Live DEBUG_VALUE analysis +; CHECK-NEXT: Insert CFI remember/restore state instructions ; CHECK-NEXT: Unpack machine instruction bundles ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter Index: llvm/test/CodeGen/AArch64/O3-pipeline.ll =================================================================== --- llvm/test/CodeGen/AArch64/O3-pipeline.ll +++ llvm/test/CodeGen/AArch64/O3-pipeline.ll @@ -206,6 +206,7 @@ ; CHECK-NEXT: Live DEBUG_VALUE analysis ; CHECK-NEXT: Machine Outliner ; CHECK-NEXT: FunctionPass Manager +; CHECK-NEXT: Insert CFI remember/restore state instructions ; CHECK-NEXT: Unpack machine instruction bundles ; CHECK-NEXT: Lazy Machine Block Frequency Analysis ; CHECK-NEXT: Machine Optimization Remark Emitter Index: llvm/test/CodeGen/AArch64/arm64-fp128.ll =================================================================== --- llvm/test/CodeGen/AArch64/arm64-fp128.ll +++ llvm/test/CodeGen/AArch64/arm64-fp128.ll @@ -277,7 +277,7 @@ } -define dso_local i32 @test_br_cc() { +define dso_local i32 @test_br_cc() uwtable { ; CHECK-LABEL: test_br_cc: ; CHECK: // %bb.0: ; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill Index: llvm/test/CodeGen/AArch64/cfi-fixup.mir =================================================================== --- /dev/null +++ llvm/test/CodeGen/AArch64/cfi-fixup.mir @@ -0,0 +1,502 @@ +# NOTE: Assertions have been autogenerated by utils/update_mir_test_checks.py +# RUN: llc -mtriple=aarch64 -run-pass=cfi-fixup %s -o - | FileCheck %s +--- | + define i32 @f0(i32 %x) #0 { + entry: br label %return + if.end: br label %return + if.then2: br label %return + if.else: br label %return + return: + ret i32 0 + } + + define i32 @f1(i32 %x) #0 { + entry: br label %return + if.end: br label %return + if.then2: br label %return + if.else: br label %return + return: + ret i32 0 + } + + define i32 @f2(i32 %x) #0 { + entry: br label %return + if.end: br label %return + if.then2: br label %return + if.else: br label %return + return: + ret i32 0 + } + + declare i32 @g(i32) + + attributes #0 = { nounwind shadowcallstack uwtable "sign-return-address"="non-leaf" "target-features"="+reserve-x18" } + +... +--- +name: f0 +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +failsVerification: false +registers: [] +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 16 + offsetAdjustment: 0 + maxAlignment: 16 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, + stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + hasRedZone: false +body: | + ; CHECK-LABEL: name: f0 + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.4(0x30000000), %bb.1(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CBZW renamable $w0, %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.if.end: + ; CHECK-NEXT: successors: %bb.3(0x30000000), %bb.2(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + ; CHECK-NEXT: frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 + ; CHECK-NEXT: CFI_INSTRUCTION remember_state + ; CHECK-NEXT: TBNZW renamable $w0, 31, %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.if.else: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w8 = MOVZWi 1, 0 + ; CHECK-NEXT: $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + ; CHECK-NEXT: B %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.if.then2: + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: B %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CFI_INSTRUCTION def_cfa $wsp, 0 + ; CHECK-NEXT: CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: CFI_INSTRUCTION same_value $w18 + ; CHECK-NEXT: CFI_INSTRUCTION same_value $w30 + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CFI_INSTRUCTION restore_state + ; CHECK-NEXT: early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; CHECK-NEXT: frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $w18 + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $w30 + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + bb.0.entry: + successors: %bb.4(0x30000000), %bb.1(0x50000000) + liveins: $w0, $lr, $x18 + + CBZW renamable $w0, %bb.4 + + bb.1.if.end: + successors: %bb.3(0x30000000), %bb.2(0x50000000) + liveins: $w0, $lr, $x18 + + early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + frame-setup CFI_INSTRUCTION negate_ra_sign_state + early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + frame-setup CFI_INSTRUCTION offset $w30, -16 + TBNZW renamable $w0, 31, %bb.3 + + bb.2.if.else: + successors: %bb.5(0x80000000) + liveins: $w0 + + renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w8 = MOVZWi 1, 0 + $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + B %bb.5 + + bb.3.if.then2: + successors: %bb.5(0x80000000) + liveins: $w0 + + renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + B %bb.5 + + bb.4.return: + liveins: $w0 + RET undef $lr, implicit killed $w0 + + bb.5.return: + liveins: $w0 + + early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + frame-destroy CFI_INSTRUCTION negate_ra_sign_state + early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + frame-destroy CFI_INSTRUCTION restore $w18 + frame-destroy CFI_INSTRUCTION restore $w30 + RET undef $lr, implicit killed $w0 + +... +--- +name: f1 +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +failsVerification: false +registers: [] +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 16 + offsetAdjustment: 0 + maxAlignment: 16 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, + stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + hasRedZone: false +body: | + ; CHECK-LABEL: name: f1 + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.5(0x30000000), %bb.1(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CBZW renamable $w0, %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.if.end: + ; CHECK-NEXT: successors: %bb.3(0x30000000), %bb.2(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + ; CHECK-NEXT: frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 + ; CHECK-NEXT: TBNZW renamable $w0, 31, %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.if.else: + ; CHECK-NEXT: successors: %bb.4(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w8 = MOVZWi 1, 0 + ; CHECK-NEXT: $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + ; CHECK-NEXT: B %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.if.then2: + ; CHECK-NEXT: successors: %bb.4(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: B %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + ; CHECK-NEXT: frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $w18 + ; CHECK-NEXT: frame-destroy CFI_INSTRUCTION restore $w30 + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + bb.0.entry: + successors: %bb.5(0x30000000), %bb.1(0x50000000) + liveins: $w0, $lr, $x18 + + CBZW renamable $w0, %bb.5 + + bb.1.if.end: + successors: %bb.3(0x30000000), %bb.2(0x50000000) + liveins: $w0, $lr, $x18 + + early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + frame-setup CFI_INSTRUCTION negate_ra_sign_state + early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + frame-setup CFI_INSTRUCTION offset $w30, -16 + TBNZW renamable $w0, 31, %bb.3 + + bb.2.if.else: + successors: %bb.4(0x80000000) + liveins: $w0 + + renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w8 = MOVZWi 1, 0 + $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + B %bb.4 + + bb.3.if.then2: + successors: %bb.4(0x80000000) + liveins: $w0 + + renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + B %bb.4 + + bb.4.return: + liveins: $w0 + + early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + frame-destroy CFI_INSTRUCTION def_cfa_offset 0 + frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + frame-destroy CFI_INSTRUCTION negate_ra_sign_state + early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + frame-destroy CFI_INSTRUCTION restore $w18 + frame-destroy CFI_INSTRUCTION restore $w30 + RET undef $lr, implicit killed $w0 + + bb.5.return: + liveins: $w0 + RET undef $lr, implicit killed $w0 + +... +--- +name: f2 +alignment: 4 +exposesReturnsTwice: false +legalized: false +regBankSelected: false +selected: false +failedISel: false +tracksRegLiveness: true +hasWinCFI: false +failsVerification: false +registers: [] +liveins: + - { reg: '$w0', virtual-reg: '' } +frameInfo: + isFrameAddressTaken: false + isReturnAddressTaken: false + hasStackMap: false + hasPatchPoint: false + stackSize: 16 + offsetAdjustment: 0 + maxAlignment: 16 + adjustsStack: true + hasCalls: true + stackProtector: '' + maxCallFrameSize: 0 + cvBytesOfCalleeSavedRegisters: 0 + hasOpaqueSPAdjustment: false + hasVAStart: false + hasMustTailInVarArgFunc: false + hasTailCall: false + localFrameSize: 0 + savePoint: '' + restorePoint: '' +fixedStack: [] +stack: + - { id: 0, name: '', type: spill-slot, offset: -16, size: 8, alignment: 16, + stack-id: default, callee-saved-register: '$lr', callee-saved-restored: true, + debug-info-variable: '', debug-info-expression: '', debug-info-location: '' } +callSites: [] +debugValueSubstitutions: [] +constants: [] +machineFunctionInfo: + hasRedZone: false +body: | + ; CHECK-LABEL: name: f2 + ; CHECK: bb.0.entry: + ; CHECK-NEXT: successors: %bb.5(0x30000000), %bb.1(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CBZW renamable $w0, %bb.5 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.1.if.end: + ; CHECK-NEXT: successors: %bb.3(0x30000000), %bb.2(0x50000000) + ; CHECK-NEXT: liveins: $w0, $lr, $x18 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + ; CHECK-NEXT: frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 + ; CHECK-NEXT: TBNZW renamable $w0, 31, %bb.3 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.2.if.else: + ; CHECK-NEXT: successors: %bb.4(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w8 = MOVZWi 1, 0 + ; CHECK-NEXT: $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + ; CHECK-NEXT: B %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.3.if.then2: + ; CHECK-NEXT: successors: %bb.4(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + ; CHECK-NEXT: renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + ; CHECK-NEXT: B %bb.4 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.4.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + ; CHECK-NEXT: frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + ; CHECK-NEXT: early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.return: + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CFI_INSTRUCTION def_cfa $wsp, 0 + ; CHECK-NEXT: CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: CFI_INSTRUCTION same_value $w18 + ; CHECK-NEXT: CFI_INSTRUCTION same_value $w30 + ; CHECK-NEXT: RET undef $lr, implicit killed $w0 + bb.0.entry: + successors: %bb.5(0x30000000), %bb.1(0x50000000) + liveins: $w0, $lr, $x18 + + CBZW renamable $w0, %bb.5 + + bb.1.if.end: + successors: %bb.3(0x30000000), %bb.2(0x50000000) + liveins: $w0, $lr, $x18 + + early-clobber $x18 = frame-setup STRXpost $lr, $x18, 8 + frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + frame-setup PACIASP implicit-def $lr, implicit killed $lr, implicit $sp + frame-setup CFI_INSTRUCTION negate_ra_sign_state + early-clobber $sp = frame-setup STRXpre killed $lr, $sp, -16 :: (store (s64) into %stack.0) + frame-setup CFI_INSTRUCTION def_cfa_offset 16 + frame-setup CFI_INSTRUCTION offset $w30, -16 + TBNZW renamable $w0, 31, %bb.3 + + bb.2.if.else: + successors: %bb.4(0x80000000) + liveins: $w0 + + renamable $w0 = nuw nsw ADDWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w8 = MOVZWi 1, 0 + $w0 = SUBWrs killed renamable $w8, killed renamable $w0, 0 + B %bb.4 + + bb.3.if.then2: + successors: %bb.4(0x80000000) + liveins: $w0 + + renamable $w0 = nsw SUBWri killed renamable $w0, 1, 0 + BL @g, csr_aarch64_aapcs_scs, implicit-def dead $lr, implicit $sp, implicit killed $w0, implicit-def $sp, implicit-def $w0 + renamable $w0 = nsw ADDWri killed renamable $w0, 1, 0 + B %bb.4 + + bb.4.return: + liveins: $w0 + + early-clobber $sp, $lr = frame-destroy LDRXpost $sp, 16 :: (load (s64) from %stack.0) + frame-destroy AUTIASP implicit-def $lr, implicit killed $lr, implicit $sp + early-clobber $x18, $lr = frame-destroy LDRXpre $x18, -8 + RET undef $lr, implicit killed $w0 + + bb.5.return: + liveins: $w0 + RET undef $lr, implicit killed $w0 + +... Index: llvm/test/CodeGen/AArch64/nomerge.ll =================================================================== --- llvm/test/CodeGen/AArch64/nomerge.ll +++ llvm/test/CodeGen/AArch64/nomerge.ll @@ -1,6 +1,26 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py ; RUN: llc < %s -mtriple=aarch64 -o - | FileCheck %s -define void @foo(i32 %i) { +define void @foo(i32 %i) uwtable { +; CHECK-LABEL: foo: +; CHECK: // %bb.0: // %entry +; CHECK-NEXT: str x30, [sp, #-16]! // 8-byte Folded Spill +; CHECK-NEXT: .cfi_def_cfa_offset 16 +; CHECK-NEXT: .cfi_offset w30, -16 +; CHECK-NEXT: cmp w0, #7 +; CHECK-NEXT: b.eq .LBB0_3 +; CHECK-NEXT: // %bb.1: // %entry +; CHECK-NEXT: cmp w0, #5 +; CHECK-NEXT: b.ne .LBB0_4 +; CHECK-NEXT: // %bb.2: // %if.then +; CHECK-NEXT: bl bar +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: b bar +; CHECK-NEXT: .LBB0_3: // %if.then2 +; CHECK-NEXT: bl bar +; CHECK-NEXT: .LBB0_4: // %if.end3 +; CHECK-NEXT: ldr x30, [sp], #16 // 8-byte Folded Reload +; CHECK-NEXT: b bar entry: switch i32 %i, label %if.end3 [ i32 5, label %if.then @@ -23,14 +43,3 @@ declare void @bar() attributes #0 = { nomerge } - -; CHECK-LABEL: foo: -; CHECK: // %bb.0: // %entry -; CHECK: // %bb.1: // %entry -; CHECK: // %bb.2: // %if.then -; CHECK-NEXT: bl bar -; CHECK: b bar -; CHECK: .LBB0_3: // %if.then2 -; CHECK-NEXT: bl bar -; CHECK: .LBB0_4: // %if.end3 -; CHECK: b bar