From 1e87a39e7be5a0c4316a37b3f72e2d952c1581a2 Mon Sep 17 00:00:00 2001 From: Daniel Hoekwater Date: Thu, 24 Oct 2024 23:05:46 +0000 Subject: [PATCH] [CFIFixup] Add frame info to the first block of each section Now that `-fbasic-block-sections=list` is enabled for Arm, functions may be split aross multiple sections, and CFI information must be handled independently for each section. On x86, this is handled in `llvm/lib/CodeGen/CFIInstrInserter.cpp`. However, this pass does not run on Arm, so we must add logic for it to `llvm/lib/CodeGen/CFIFixup.cpp`. --- llvm/lib/CodeGen/CFIFixup.cpp | 69 +++++- .../AArch64/cfi-fixup-multi-section.mir | 200 ++++++++++++++++++ 2 files changed, 258 insertions(+), 11 deletions(-) create mode 100644 llvm/test/CodeGen/AArch64/cfi-fixup-multi-section.mir diff --git a/llvm/lib/CodeGen/CFIFixup.cpp b/llvm/lib/CodeGen/CFIFixup.cpp index 0edffc0beef8e..02152a136a225 100644 --- a/llvm/lib/CodeGen/CFIFixup.cpp +++ b/llvm/lib/CodeGen/CFIFixup.cpp @@ -67,7 +67,12 @@ #include "llvm/CodeGen/CFIFixup.h" +#include "llvm/ADT/DenseMap.h" #include "llvm/ADT/PostOrderIterator.h" +#include "llvm/ADT/STLExtras.h" +#include "llvm/ADT/iterator_range.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineFunction.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetFrameLowering.h" #include "llvm/CodeGen/TargetInstrInfo.h" @@ -76,6 +81,8 @@ #include "llvm/MC/MCDwarf.h" #include "llvm/Target/TargetMachine.h" +#include + using namespace llvm; #define DEBUG_TYPE "cfi-fixup" @@ -120,7 +127,7 @@ findPrologueEnd(MachineFunction &MF, MachineBasicBlock::iterator &PrologueEnd) { // iterator can point to the end of the block. Instructions are inserted // *before* the iterator. struct InsertionPoint { - MachineBasicBlock *MBB; + MachineBasicBlock *MBB = nullptr; MachineBasicBlock::iterator Iterator; }; @@ -150,6 +157,30 @@ insertRememberRestorePair(const InsertionPoint &RememberInsertPt, ->getIterator())}; } +// Copies all CFI instructions before PrologueEnd and inserts them before +// DstInsertPt. Returns the iterator to the first instruction after the +// inserted instructions. +static InsertionPoint cloneCfiPrologue(const InsertionPoint &PrologueEnd, + const InsertionPoint &DstInsertPt) { + MachineFunction &MF = *DstInsertPt.MBB->getParent(); + + auto cloneCfiInstructions = [&](MachineBasicBlock::iterator Begin, + MachineBasicBlock::iterator End) { + auto ToClone = map_range( + make_filter_range(make_range(Begin, End), isPrologueCFIInstruction), + [&](const MachineInstr &MI) { return MF.CloneMachineInstr(&MI); }); + DstInsertPt.MBB->insert(DstInsertPt.Iterator, ToClone.begin(), + ToClone.end()); + }; + + // Clone all CFI instructions from previous blocks. + for (auto &MBB : make_range(MF.begin(), PrologueEnd.MBB->getIterator())) + cloneCfiInstructions(MBB.begin(), MBB.end()); + // Clone all CFI instructions from the final prologue block. + cloneCfiInstructions(PrologueEnd.MBB->begin(), PrologueEnd.Iterator); + return DstInsertPt; +} + bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { const TargetFrameLowering &TFL = *MF.getSubtarget().getFrameLowering(); if (!TFL.enableCFIFixup(MF)) @@ -172,7 +203,8 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { bool HasFrameOnEntry : 1; bool HasFrameOnExit : 1; }; - SmallVector BlockInfo(NumBlocks, {false, false, false, false}); + SmallVector BlockInfo(NumBlocks, + {false, false, false, false}); BlockInfo[0].Reachable = true; BlockInfo[0].StrongNoFrameOnEntry = true; @@ -209,10 +241,11 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { // of the previous block. If the intended frame state is different, insert // compensating CFI instructions. 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`. - InsertionPoint InsertPt = {PrologueBlock, PrologueEnd}; + // `InsertPt[sectionID]` 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`. + SmallDenseMap InsertionPts; + InsertionPts[PrologueBlock->getSectionID()] = {PrologueBlock, PrologueEnd}; assert(PrologueEnd != PrologueBlock->begin() && "Inconsistent notion of \"prologue block\""); @@ -239,14 +272,28 @@ bool CFIFixup::runOnMachineFunction(MachineFunction &MF) { } } #endif + + // If the block is the first block in its section, then it doesn't have a + // frame on entry. + HasFrame &= !CurrBB->isBeginSection(); if (!Info.StrongNoFrameOnEntry && Info.HasFrameOnEntry && !HasFrame) { // Reset to the "after prologue" state. - // There's an earlier block known to have a stack frame. Insert a - // `.cfi_remember_state` instruction into that block and a - // `.cfi_restore_state` instruction at the beginning of the current block. - InsertPt = insertRememberRestorePair( - InsertPt, InsertionPoint{&*CurrBB, CurrBB->begin()}); + InsertionPoint &InsertPt = InsertionPts[CurrBB->getSectionID()]; + if (InsertPt.MBB == nullptr) { + // CurBB is the first block in its section, so there is no "after + // prologue" state. Clone the CFI instructions from the prologue block + // to create it. + InsertPt = cloneCfiPrologue({PrologueBlock, PrologueEnd}, + {&*CurrBB, CurrBB->begin()}); + } else { + // There's an earlier block known to have a stack frame. Insert a + // `.cfi_remember_state` instruction into that block and a + // `.cfi_restore_state` instruction at the beginning of the current + // block. + InsertPt = + insertRememberRestorePair(InsertPt, {&*CurrBB, CurrBB->begin()}); + } Change = true; } else if ((Info.StrongNoFrameOnEntry || !Info.HasFrameOnEntry) && HasFrame) { diff --git a/llvm/test/CodeGen/AArch64/cfi-fixup-multi-section.mir b/llvm/test/CodeGen/AArch64/cfi-fixup-multi-section.mir new file mode 100644 index 0000000000000..a24972d138832 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/cfi-fixup-multi-section.mir @@ -0,0 +1,200 @@ +# 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 + } + + 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 (bbsections 1): + ; CHECK-NEXT: successors: %bb.5(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION escape 0x16, 0x12, 0x02, 0x82, 0x78 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION negate_ra_sign_state + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION def_cfa_offset 16 + ; CHECK-NEXT: frame-setup CFI_INSTRUCTION offset $w30, -16 + ; 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: RET undef $lr, implicit killed $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.5.return: + ; CHECK-NEXT: successors: %bb.7(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CFI_INSTRUCTION restore_state + ; CHECK-NEXT: CFI_INSTRUCTION remember_state + ; CHECK-NEXT: B %bb.7 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: bb.6.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.7.return: + ; CHECK-NEXT: successors: %bb.6(0x80000000) + ; CHECK-NEXT: liveins: $w0 + ; CHECK-NEXT: {{ $}} + ; CHECK-NEXT: CFI_INSTRUCTION restore_state + ; CHECK-NEXT: B %bb.6 + 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 (bbsections 1): + 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 + B %bb.6 + + bb.7.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.6.return: + liveins: $w0 + B %bb.7 + + +...