diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -6162,6 +6162,65 @@ SetCandidateCallInfo(MachineOutlinerDefault, 12); } + // Bugzilla ID: 46767 + // TODO: Check if fixing up twice is safe so we can outline these. + // + // Fixing up the stack more than once is not yet implemented, as a result + // the following cases are not yet supported: + // + // Case 1: Outline resulting in a noreturn caller of an outlined callee that + // has other calls requiring stack fixups. + // + // Case 2: Outline resulting in a caller that requires stack fixups at the + // callsite to a callee that also requires stack fixups. This is the + // case when there are no available registers at the callsite. + // + // In other words if function_containing_sequence in the following pseudo + // assembly is either: + // - noreturn + // - Requires that we save LR at the point of the call, but there are no + // available registers. In this case, we save using SP. + // + // function_containing_sequence: + // ... + // save LR to SP <- Requires stack instr fixups in OUTLINED_FUNCTION_N + // call OUTLINED_FUNCTION_N + // restore LR from SP + // ... + // + // OUTLINED_FUNCTION_N: + // save LR to SP <- Requires stack instr fixups in OUTLINED_FUNCTION_N + // ... + // bl foo + // restore LR from SP + // ret + // + // Because the code to handle multiple stack fixups does not currently have + // the proper checks for legality, these cases will assert in the AArch64 + // MachineOutliner. + // + // The assert happens in AArch64InstrInfo::buildOutlinedFrame to catch these + // cases until they can be properly tested and handled. Bugzilla 46767 is + // referenced in comments at the assert site. + // + // To avoid asserting (or potentially generating non-legal code on + // noassert builds) we remove all candidates which would require multiple + // stack fixups. + // + if (FlagsSetInAll & MachineOutlinerMBBFlags::HasCalls) { + erase_if(RepeatedSequenceLocs, [](outliner::Candidate &C) { + if (std::any_of(C.front(), C.back(), + [](const MachineInstr &MI) { return MI.isCall(); })) { + if (!C.LRU.available(AArch64::LR)) + return true; + if (C.getMF()->getFunction().hasFnAttribute(Attribute::NoReturn)) + return true; + } + + return false; + }); + } + // If we dropped all of the candidates, bail out here. if (RepeatedSequenceLocs.size() < 2) { RepeatedSequenceLocs.clear(); @@ -6583,6 +6642,9 @@ if (std::any_of(MBB.instr_begin(), MBB.instr_end(), IsNonTailCall)) { // Fix up the instructions in the range, since we're going to modify the // stack. + + // Bugzilla ID: 46767 + // TODO: Check if fixing up twice is safe so we can outline these. assert(OF.FrameConstructionID != MachineOutlinerDefault && "Can only fix up stack references once"); fixupPostOutline(MBB); diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-noreturn-no-stack.mir b/llvm/test/CodeGen/AArch64/machine-outliner-noreturn-no-stack.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/machine-outliner-noreturn-no-stack.mir @@ -0,0 +1,69 @@ +# RUN: llc -mtriple=arm64-apple-ios -run-pass=prologepilog -run-pass=machine-outliner -verify-machineinstrs %s -o - | FileCheck %s + +# Noreturn functions conservatively need to save and restore lr. +# When there is no available register, the stack is used at call site. +# If the stack also needs to be set up for a call in the outlined function, +# bail-out this case since we do not handle adjusting the stack twice. + +# CHECK-NOT: OUTLINED_FUNCTION + +--- | + @g = external global i32 + define void @stack_1() #0 { ret void } + define void @stack_2() #0 { ret void } + define void @baz() { + ret void + } + attributes #0 = { noredzone noreturn } +... +--- +name: stack_1 +tracksRegLiveness: true +body: | + bb.0: + liveins: $x4, $x0, $x1, $x2, $x3 + $w8 = MOVZWi 259, 0 + $x9 = ADRP target-flags(aarch64-page) @g + $x9 = ADDXri $x9, target-flags(aarch64-pageoff, aarch64-nc) @g, 0 + STRXui $x9, $x1, 0 + STRHHui $w8, $x1, 8 + $w8 = ORRWrs $wzr, $w4, 0, implicit-def $x8 + STRXui $x8, $x3, 0 + STPXi $x3, $xzr, $x2, 0 + $w8 = MOVZWi 271, 0 + STRHHui $w8, $x2, 8 + $x8 = ORRXrs $xzr, $x0, 0 + $x0 = ORRXrs $xzr, $x1, 0 + $x1 = ORRXrs $xzr, $x2, 0 + BL @baz, implicit-def dead $lr, implicit $sp, implicit $x8, implicit $x0, implicit $x1, implicit $x3, implicit $x4, implicit-def $sp, implicit-def $x5, implicit-def $x6, implicit-def $x7, implicit-def $x8, implicit-def $x9, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit-def $x13, implicit-def $x14, implicit-def $x15, implicit-def $x18 + BRK 1 +... +--- +name: stack_2 +tracksRegLiveness: true +body: | + bb.0: + liveins: $x4, $x0, $x1, $x2, $x3 + $w8 = MOVZWi 259, 0 + $x9 = ADRP target-flags(aarch64-page) @g + $x9 = ADDXri $x9, target-flags(aarch64-pageoff, aarch64-nc) @g, 0 + STRXui $x9, $x1, 0 + STRHHui $w8, $x1, 8 + $w8 = ORRWrs $wzr, $w4, 0, implicit-def $x8 + STRXui $x8, $x3, 0 + STPXi $x3, $xzr, $x2, 0 + $w8 = MOVZWi 271, 0 + STRHHui $w8, $x2, 8 + $x8 = ORRXrs $xzr, $x0, 0 + $x0 = ORRXrs $xzr, $x1, 0 + $x1 = ORRXrs $xzr, $x2, 0 + BL @baz, implicit-def dead $lr, implicit $sp, implicit $x8, implicit $x0, implicit $x1, implicit $x3, implicit $x4, implicit-def $sp, implicit-def $x5, implicit-def $x6, implicit-def $x7, implicit-def $x8, implicit-def $x9, implicit-def $x10, implicit-def $x11, implicit-def $x12, implicit-def $x13, implicit-def $x14, implicit-def $x15, implicit-def $x18 + BRK 1 +... +--- +name: baz +tracksRegLiveness: true +body: | + bb.0: + liveins: $w0, $lr, $w8 + RET undef $lr