diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp index 6844b02b85289c..12a04c5e9f31d5 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -6071,7 +6071,7 @@ ARMBaseInstrInfo::getOutliningType(MachineBasicBlock::iterator &MIT, } void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB, - MachineBasicBlock::iterator &It) const { + MachineBasicBlock::iterator It) const { unsigned Opc = Subtarget.isThumb() ? ARM::t2STR_PRE : ARM::STR_PRE_IMM; int Align = -Subtarget.getStackAlignment().value(); BuildMI(MBB, It, DebugLoc(), get(Opc), ARM::SP) @@ -6081,8 +6081,45 @@ void ARMBaseInstrInfo::saveLROnStack(MachineBasicBlock &MBB, .add(predOps(ARMCC::AL)); } +void ARMBaseInstrInfo::emitCFIForLRSaveOnStack( + MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const { + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = Subtarget.getRegisterInfo(); + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + int Align = Subtarget.getStackAlignment().value(); + // Add a CFI saying the stack was moved down. + int64_t StackPosEntry = + MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, Align)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(StackPosEntry) + .setMIFlags(MachineInstr::FrameSetup); + + // Add a CFI saying that the LR that we want to find is now higher than + // before. + int64_t LRPosEntry = + MF.addFrameInst(MCCFIInstruction::createOffset(nullptr, DwarfLR, -Align)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameSetup); +} + +void ARMBaseInstrInfo::emitCFIForLRSaveToReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It, + Register Reg) const { + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = Subtarget.getRegisterInfo(); + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + unsigned DwarfReg = MRI->getDwarfRegNum(Reg, true); + + int64_t LRPosEntry = MF.addFrameInst( + MCCFIInstruction::createRegister(nullptr, DwarfLR, DwarfReg)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameSetup); +} + void ARMBaseInstrInfo::restoreLRFromStack( - MachineBasicBlock &MBB, MachineBasicBlock::iterator &It) const { + MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const { unsigned Opc = Subtarget.isThumb() ? ARM::t2LDR_POST : ARM::LDR_POST_IMM; MachineInstrBuilder MIB = BuildMI(MBB, It, DebugLoc(), get(Opc), ARM::LR) .addReg(ARM::SP, RegState::Define) @@ -6092,6 +6129,39 @@ void ARMBaseInstrInfo::restoreLRFromStack( MIB.addImm(Subtarget.getStackAlignment().value()).add(predOps(ARMCC::AL)); } +void ARMBaseInstrInfo::emitCFIForLRRestoreFromStack( + MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const { + // Now stack has moved back up... + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = Subtarget.getRegisterInfo(); + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + int64_t StackPosEntry = + MF.addFrameInst(MCCFIInstruction::cfiDefCfaOffset(nullptr, 0)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(StackPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); + + // ... and we have restored LR. + int64_t LRPosEntry = + MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, DwarfLR)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); +} + +void ARMBaseInstrInfo::emitCFIForLRRestoreFromReg( + MachineBasicBlock &MBB, MachineBasicBlock::iterator It) const { + MachineFunction &MF = *MBB.getParent(); + const MCRegisterInfo *MRI = Subtarget.getRegisterInfo(); + unsigned DwarfLR = MRI->getDwarfRegNum(ARM::LR, true); + + int64_t LRPosEntry = + MF.addFrameInst(MCCFIInstruction::createRestore(nullptr, DwarfLR)); + BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) + .addCFIIndex(LRPosEntry) + .setMIFlags(MachineInstr::FrameDestroy); +} + void ARMBaseInstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { @@ -6133,28 +6203,11 @@ void ARMBaseInstrInfo::buildOutlinedFrame( // Insert a save before the outlined region saveLROnStack(MBB, It); - - unsigned StackAlignment = Subtarget.getStackAlignment().value(); - const TargetSubtargetInfo &STI = MF.getSubtarget(); - const MCRegisterInfo *MRI = STI.getRegisterInfo(); - unsigned DwarfReg = MRI->getDwarfRegNum(ARM::LR, true); - // Add a CFI saying the stack was moved down. - int64_t StackPosEntry = MF.addFrameInst( - MCCFIInstruction::cfiDefCfaOffset(nullptr, StackAlignment)); - BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) - .addCFIIndex(StackPosEntry) - .setMIFlags(MachineInstr::FrameSetup); - - // Add a CFI saying that the LR that we want to find is now higher than - // before. - int64_t LRPosEntry = MF.addFrameInst( - MCCFIInstruction::createOffset(nullptr, DwarfReg, StackAlignment)); - BuildMI(MBB, It, DebugLoc(), get(ARM::CFI_INSTRUCTION)) - .addCFIIndex(LRPosEntry) - .setMIFlags(MachineInstr::FrameSetup); + emitCFIForLRSaveOnStack(MBB, It); // Insert a restore before the terminator for the function. Restore LR. restoreLRFromStack(MBB, Et); + emitCFIForLRRestoreFromStack(MBB, Et); } // If this is a tail call outlined function, then there's already a return. @@ -6204,6 +6257,7 @@ MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall( return It; } + const ARMFunctionInfo &AFI = *C.getMF()->getInfo(); // Can we save to a register? if (C.CallConstructionID == MachineOutlinerRegSave) { unsigned Reg = findRegisterToSaveLRTo(C); @@ -6211,15 +6265,23 @@ MachineBasicBlock::iterator ARMBaseInstrInfo::insertOutlinedCall( // Save and restore LR from that register. copyPhysReg(MBB, It, DebugLoc(), Reg, ARM::LR, true); + if (!AFI.isLRSpilled()) + emitCFIForLRSaveToReg(MBB, It, Reg); CallPt = MBB.insert(It, CallMIB); copyPhysReg(MBB, It, DebugLoc(), ARM::LR, Reg, true); + if (!AFI.isLRSpilled()) + emitCFIForLRRestoreFromReg(MBB, It); It--; return CallPt; } // We have the default case. Save and restore from SP. saveLROnStack(MBB, It); + if (!AFI.isLRSpilled()) + emitCFIForLRSaveOnStack(MBB, It); CallPt = MBB.insert(It, CallMIB); restoreLRFromStack(MBB, It); + if (!AFI.isLRSpilled()) + emitCFIForLRRestoreFromStack(MBB, It); It--; return CallPt; } diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h index 4132906d8bfacd..bfcbe8e5a4f5d6 100644 --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.h +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.h @@ -368,12 +368,33 @@ class ARMBaseInstrInfo : public ARMGenInstrInfo { // Adds an instruction which saves the link register on top of the stack into /// the MachineBasicBlock \p MBB at position \p It. void saveLROnStack(MachineBasicBlock &MBB, - MachineBasicBlock::iterator &It) const; + MachineBasicBlock::iterator It) const; /// Adds an instruction which restores the link register from the top the /// stack into the MachineBasicBlock \p MBB at position \p It. void restoreLRFromStack(MachineBasicBlock &MBB, - MachineBasicBlock::iterator &It) const; + MachineBasicBlock::iterator It) const; + + /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It, + /// for the case when the LR is saved on the stack. + void emitCFIForLRSaveOnStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It) const; + + /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It, + /// for the case when the LR is saved in the register \p Reg. + void emitCFIForLRSaveToReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It, + Register Reg) const; + + /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It, + /// after the LR is was restored from the stack. + void emitCFIForLRRestoreFromStack(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It) const; + + /// Emit CFI instructions into the MachineBasicBlock \p MBB at position \p It, + /// after the LR is was restored from a register. + void emitCFIForLRRestoreFromReg(MachineBasicBlock &MBB, + MachineBasicBlock::iterator It) const; unsigned getInstBundleLength(const MachineInstr &MI) const; diff --git a/llvm/test/CodeGen/ARM/machine-outliner-calls.mir b/llvm/test/CodeGen/ARM/machine-outliner-calls.mir index 7880ddfb0051cf..a9a2a1357e1041 100644 --- a/llvm/test/CodeGen/ARM/machine-outliner-calls.mir +++ b/llvm/test/CodeGen/ARM/machine-outliner-calls.mir @@ -295,7 +295,7 @@ body: | ; CHECK: liveins: $r11, $r10, $r9, $r8, $r7, $r6, $r5, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr ; CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg ; CHECK: frame-setup CFI_INSTRUCTION def_cfa_offset 8 - ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, 8 + ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, -8 ; CHECK: BL @bar, implicit-def dead $lr, implicit $sp ; CHECK: $r0 = MOVi 1, 14 /* CC::al */, $noreg, $noreg ; CHECK: $r1 = MOVi 1, 14 /* CC::al */, $noreg, $noreg @@ -320,7 +320,7 @@ body: | ; CHECK: liveins: $r11, $r10, $r9, $r8, $r7, $r6, $r5, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr ; CHECK: early-clobber $sp = STR_PRE_IMM killed $lr, $sp, -8, 14 /* CC::al */, $noreg ; CHECK: frame-setup CFI_INSTRUCTION def_cfa_offset 8 - ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, 8 + ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, -8 ; CHECK: BL @bar, implicit-def dead $lr, implicit $sp ; CHECK: $r0 = MOVi 2, 14 /* CC::al */, $noreg, $noreg ; CHECK: $r1 = MOVi 2, 14 /* CC::al */, $noreg, $noreg @@ -335,7 +335,7 @@ body: | ; CHECK: liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr ; CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg ; CHECK: frame-setup CFI_INSTRUCTION def_cfa_offset 8 - ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, 8 + ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, -8 ; CHECK: tBL 14 /* CC::al */, $noreg, @bar, implicit-def dead $lr, implicit $sp ; CHECK: $r0 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg ; CHECK: $r1 = t2MOVi 1, 14 /* CC::al */, $noreg, $noreg @@ -348,7 +348,7 @@ body: | ; CHECK: liveins: $r11, $r10, $r9, $r8, $r6, $r5, $r4, $d15, $d14, $d13, $d12, $d11, $d10, $d9, $d8, $lr ; CHECK: early-clobber $sp = t2STR_PRE killed $lr, $sp, -8, 14 /* CC::al */, $noreg ; CHECK: frame-setup CFI_INSTRUCTION def_cfa_offset 8 - ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, 8 + ; CHECK: frame-setup CFI_INSTRUCTION offset $lr, -8 ; CHECK: tBL 14 /* CC::al */, $noreg, @bar, implicit-def dead $lr, implicit $sp ; CHECK: $r0 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg ; CHECK: $r1 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll new file mode 100644 index 00000000000000..67c859c4bea8db --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-cfi-1.ll @@ -0,0 +1,78 @@ +; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv7m-unknown-unknown-eabi" + +; Derived from +; volatile int a, b, c, d, e, f, g, h; +; +; int x() { +; int r = (a + b) / (c + d) + e + f; +; return r + 1; +; } +; +; +; int y() { +; int r = (a + b) / (c + d) + e + f; +; return r + 2; +; } +; Checks that CFI instruction are emitted around saves/restores of +; the LR on stack. + +@a = dso_local global i32 0, align 4 +@b = dso_local global i32 0, align 4 +@c = dso_local global i32 0, align 4 +@d = dso_local global i32 0, align 4 +@e = dso_local global i32 0, align 4 +@f = dso_local global i32 0, align 4 + +define dso_local i32 @x() local_unnamed_addr #0 { +entry: + %0 = load volatile i32, i32* @a, align 4 + %1 = load volatile i32, i32* @b, align 4 + %add = add nsw i32 %1, %0 + %2 = load volatile i32, i32* @c, align 4 + %3 = load volatile i32, i32* @d, align 4 + %add1 = add nsw i32 %3, %2 + %div = sdiv i32 %add, %add1 + %4 = load volatile i32, i32* @e, align 4 + %5 = load volatile i32, i32* @f, align 4 + %add2 = add i32 %div, 1 + %add3 = add i32 %add2, %4 + %add4 = add i32 %add3, %5 + ret i32 %add4 +} +; CHECK-LABEL: x: +; CHECK: str lr, [sp, #-8]! +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: .cfi_offset lr, -8 +; CHECK-NEXT: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: ldr lr, [sp], #8 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: .cfi_restore lr + +define dso_local i32 @y() local_unnamed_addr #0 { +entry: + %0 = load volatile i32, i32* @a, align 4 + %1 = load volatile i32, i32* @b, align 4 + %add = add nsw i32 %1, %0 + %2 = load volatile i32, i32* @c, align 4 + %3 = load volatile i32, i32* @d, align 4 + %add1 = add nsw i32 %3, %2 + %div = sdiv i32 %add, %add1 + %4 = load volatile i32, i32* @e, align 4 + %5 = load volatile i32, i32* @f, align 4 + %add2 = add i32 %div, 2 + %add3 = add i32 %add2, %4 + %add4 = add i32 %add3, %5 + ret i32 %add4 +} +; CHECK-LABEL: y: +; CHECK: str lr, [sp, #-8]! +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: .cfi_offset lr, -8 +; CHECK-NEXT: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: ldr lr, [sp], #8 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: .cfi_restore lr + +attributes #0 = { minsize nofree norecurse nounwind optsize } diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll new file mode 100644 index 00000000000000..e5dee8fd2938cf --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-cfi-2.ll @@ -0,0 +1,73 @@ +; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv7m-unknown-unknown-eabi" + +; Derived from +; volatile int a, b, c, d, e, f, g, h; +; +; int x() { +; int r = a + b + c + d + e + f; +; return r + 1; +; } +; +; +; int y() { +; int r = a + b + c + d + e + f; +; return r + 2; +; } +; Check CFI instructions when LR is saved/restored to/from a register. + +@a = dso_local global i32 0, align 4 +@b = dso_local global i32 0, align 4 +@c = dso_local global i32 0, align 4 +@d = dso_local global i32 0, align 4 +@e = dso_local global i32 0, align 4 +@f = dso_local global i32 0, align 4 + +define dso_local i32 @x() local_unnamed_addr #0 { +entry: + %0 = load volatile i32, i32* @a, align 4 + %1 = load volatile i32, i32* @b, align 4 + %2 = load volatile i32, i32* @c, align 4 + %3 = load volatile i32, i32* @d, align 4 + %4 = load volatile i32, i32* @e, align 4 + %5 = load volatile i32, i32* @f, align 4 + %add = add i32 %0, 1 + %add1 = add i32 %add, %1 + %add2 = add i32 %add1, %2 + %add3 = add i32 %add2, %3 + %add4 = add i32 %add3, %4 + %add5 = add i32 %add4, %5 + ret i32 %add5 +} +; CHECK-LABEL: x: +; CHECK: mov r3, lr +; CHECK-NEXT: .cfi_register lr, r3 +; CHECK-NEXT: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: mov lr, r3 +; CHECK-NEXT: .cfi_restore lr + +define dso_local i32 @y() local_unnamed_addr #0 { +entry: + %0 = load volatile i32, i32* @a, align 4 + %1 = load volatile i32, i32* @b, align 4 + %2 = load volatile i32, i32* @c, align 4 + %3 = load volatile i32, i32* @d, align 4 + %4 = load volatile i32, i32* @e, align 4 + %5 = load volatile i32, i32* @f, align 4 + %add = add i32 %0, 2 + %add1 = add i32 %add, %1 + %add2 = add i32 %add1, %2 + %add3 = add i32 %add2, %3 + %add4 = add i32 %add3, %4 + %add5 = add i32 %add4, %5 + ret i32 %add5 +} +; CHECK-LABEL: y: +; CHECK: mov r3, lr +; CHECK-NEXT: .cfi_register lr, r3 +; CHECK-NEXT: bl OUTLINED_FUNCTION_0 +; CHECK-NEXT: mov lr, r3 +; CHECK-NEXT: .cfi_restore lr + +attributes #0 = { minsize nofree norecurse nounwind optsize } diff --git a/llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll b/llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll new file mode 100644 index 00000000000000..13c055f811b753 --- /dev/null +++ b/llvm/test/CodeGen/ARM/machine-outliner-cfi-3.ll @@ -0,0 +1,78 @@ +; RUN: llc --verify-machineinstrs --force-dwarf-frame-section %s -o - | FileCheck %s +target datalayout = "e-m:e-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64" +target triple = "thumbv7m-unknown-unknown-eabi" + +; Derived from +; __attribute__((noinline)) int h(int a, int b) { return a + b; } +; +; int f(int a, int b, int c, int d) { +; if (a < 0) +; return -1; +; a = h(a, b); +; return 2 + a * (a + b) / (c + d); +; } +; +; int g(int a, int b, int c, int d) { +; if (a < 0) +; return -1; +; a = h(a, b); +; return 1 + a * (a + b) / (c + d); +; } +; Check CFI instructions inside the outlined function. + +define dso_local i32 @h(i32 %a, i32 %b) local_unnamed_addr #0 { +entry: + %add = add nsw i32 %b, %a + ret i32 %add +} + +define dso_local i32 @f(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr #1 { +entry: + %cmp = icmp slt i32 %a, 0 + br i1 %cmp, label %return, label %if.end + +if.end: ; preds = %entry + %call = tail call i32 @h(i32 %a, i32 %b) #2 + %add = add nsw i32 %call, %b + %mul = mul nsw i32 %add, %call + %add1 = add nsw i32 %d, %c + %div = sdiv i32 %mul, %add1 + %add2 = add nsw i32 %div, 2 + br label %return + +return: ; preds = %entry, %if.end + %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ] + ret i32 %retval.0 +} + +define dso_local i32 @g(i32 %a, i32 %b, i32 %c, i32 %d) local_unnamed_addr #1 { +entry: + %cmp = icmp slt i32 %a, 0 + br i1 %cmp, label %return, label %if.end + +if.end: ; preds = %entry + %call = tail call i32 @h(i32 %a, i32 %b) #2 + %add = add nsw i32 %call, %b + %mul = mul nsw i32 %add, %call + %add1 = add nsw i32 %d, %c + %div = sdiv i32 %mul, %add1 + %add2 = add nsw i32 %div, 1 + br label %return + +return: ; preds = %entry, %if.end + %retval.0 = phi i32 [ %add2, %if.end ], [ -1, %entry ] + ret i32 %retval.0 +} + +; CHECK-LABEL: OUTLINED_FUNCTION_0: +; CHECK: str lr, [sp, #-8]! +; CHECK-NEXT: .cfi_def_cfa_offset 8 +; CHECK-NEXT: .cfi_offset lr, -8 +; CHECK: ldr lr, [sp], #8 +; CHECK-NEXT: .cfi_def_cfa_offset 0 +; CHECK-NEXT: .cfi_restore lr +; CHECK-NEXT: bx lr + +attributes #0 = { minsize noinline norecurse nounwind optsize readnone } +attributes #1 = { minsize norecurse nounwind optsize readnone } +attributes #2 = { minsize optsize }