diff --git a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp --- a/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp +++ b/llvm/lib/Target/ARM/ARMBaseInstrInfo.cpp @@ -5678,6 +5678,7 @@ const int FrameRegSave; const int CallDefault; const int FrameDefault; + const int SaveRestoreLROnStack; OutlinerCosts(const ARMSubtarget &target) : CallTailCall(target.isThumb() ? 4 : 4), @@ -5689,7 +5690,8 @@ CallRegSave(target.isThumb() ? 8 : 12), FrameRegSave(target.isThumb() ? 2 : 4), CallDefault(target.isThumb() ? 8 : 12), - FrameDefault(target.isThumb() ? 2 : 4) {} + FrameDefault(target.isThumb() ? 2 : 4), + SaveRestoreLROnStack(target.isThumb() ? 8 : 8) {} }; unsigned @@ -5830,10 +5832,28 @@ C.setCallInfo(MachineOutlinerDefault, Costs.CallDefault); SetCandidateCallInfo(MachineOutlinerDefault, Costs.CallDefault); CandidatesWithoutStackFixups.push_back(C); - } - else + } else return outliner::OutlinedFunction(); } + + // Does every candidate's MBB contain a call? If so, then we might have a + // call in the range. + if (FlagsSetInAll & MachineOutlinerMBBFlags::HasCalls) { + // check if the range contains a call. These require a save + restore of + // the link register. + if (std::any_of(FirstCand.front(), FirstCand.back(), + [](const MachineInstr &MI) { return MI.isCall(); })) + NumBytesToCreateFrame += Costs.SaveRestoreLROnStack; + + // Handle the last instruction separately. If it is tail call, then the + // last instruction is a call, we don't want to save + restore in this + // case. However, it could be possible that the last instruction is a + // call without it being valid to tail call this sequence. We should + // consider this as well. + else if (FrameID != MachineOutlinerThunk && + FrameID != MachineOutlinerTailCall && FirstCand.back()->isCall()) + NumBytesToCreateFrame += Costs.SaveRestoreLROnStack; + } RepeatedSequenceLocs = CandidatesWithoutStackFixups; } @@ -5973,6 +5993,23 @@ return outliner::InstrType::Illegal; if (MI.isCall()) { + // Get the function associated with the call. Look at each operand and find + // the one that represents the calle and get its name. + const Function *Callee = nullptr; + for (const MachineOperand &MOP : MI.operands()) { + if (MOP.isGlobal()) { + Callee = dyn_cast(MOP.getGlobal()); + break; + } + } + + // Dont't outline calls to "mcount" like functions, in particular Linux + // kernel function tracing relies on it. + if (Callee && + (Callee->getName() == "\01__gnu_mcount_nc" || + Callee->getName() == "\01mcount" || Callee->getName() == "__mcount")) + return outliner::InstrType::Illegal; + // If we don't know anything about the callee, assume it depends on the // stack layout of the caller. In that case, it's only legal to outline // as a tail-call. Explicitly list the call instructions we know about so @@ -5982,7 +6019,29 @@ Opc == ARM::tBLXr || Opc == ARM::tBLXi) UnknownCallOutlineType = outliner::InstrType::LegalTerminator; - return UnknownCallOutlineType; + if (!Callee) + return UnknownCallOutlineType; + + // We have a function we have information about. Check if it's something we + // can safely outline. + MachineFunction *MF = MI.getParent()->getParent(); + MachineFunction *CalleeMF = MF->getMMI().getMachineFunction(*Callee); + + // We don't know what's going on with the callee at all. Don't touch it. + if (!CalleeMF) + return UnknownCallOutlineType; + + // Check if we know anything about the callee saves on the function. If we + // don't, then don't touch it, since that implies that we haven't computed + // anything about its stack frame yet. + MachineFrameInfo &MFI = CalleeMF->getFrameInfo(); + if (!MFI.isCalleeSavedInfoValid() || MFI.getStackSize() > 0 || + MFI.getNumObjects() > 0) + return UnknownCallOutlineType; + + // At this point, we can say that CalleeMF ought to not pass anything on the + // stack. Therefore, we can outline it. + return outliner::InstrType::Legal; } // Since calls are handled, don't touch LR or PC @@ -6045,10 +6104,6 @@ void ARMBaseInstrInfo::buildOutlinedFrame( MachineBasicBlock &MBB, MachineFunction &MF, const outliner::OutlinedFunction &OF) const { - // Nothing is needed for tail-calls. - if (OF.FrameConstructionID == MachineOutlinerTailCall) - return; - // For thunk outlining, rewrite the last instruction from a call to a // tail-call. if (OF.FrameConstructionID == MachineOutlinerThunk) { @@ -6065,9 +6120,57 @@ if (isThumb && !Call->getOperand(FuncOp).isReg()) MIB.add(predOps(ARMCC::AL)); Call->eraseFromParent(); - return; } + // Is there a call in the outlined range? + auto IsNonTailCall = [](MachineInstr &MI) { + return MI.isCall() && !MI.isReturn(); + }; + if (std::any_of(MBB.instr_begin(), MBB.instr_end(), IsNonTailCall)) { + MachineBasicBlock::iterator It = MBB.begin(); + MachineBasicBlock::iterator Et = MBB.end(); + + if (OF.FrameConstructionID == MachineOutlinerTailCall || + OF.FrameConstructionID == MachineOutlinerThunk) + Et = std::prev(MBB.end()); + + // We have to save and restore LR, we need to add it to the liveins if it + // is not already part of the set. This is suffient since outlined + // functions only have one block. + if (!MBB.isLiveIn(ARM::LR)) + MBB.addLiveIn(ARM::LR); + + // 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); + + // Insert a restore before the terminator for the function. Restore LR. + restoreLRFromStack(MBB, Et); + } + + // If this is a tail call outlined function, then there's already a return. + if (OF.FrameConstructionID == MachineOutlinerTailCall || + OF.FrameConstructionID == MachineOutlinerThunk) + return; + // Here we have to insert the return ourselves. Get the correct opcode from // current feature set. BuildMI(MBB, MBB.end(), DebugLoc(), get(Subtarget.getReturnOpcode())) diff --git a/llvm/test/CodeGen/ARM/machine-outliner-default.mir b/llvm/test/CodeGen/ARM/machine-outliner-default.mir --- a/llvm/test/CodeGen/ARM/machine-outliner-default.mir +++ b/llvm/test/CodeGen/ARM/machine-outliner-default.mir @@ -5,8 +5,6 @@ --- | define void @outline_default_arm() #0 { ret void } define void @outline_default_thumb() #1 { ret void } - define void @outline_default_KO_call_arm() #0 { ret void } - define void @outline_default_KO_call_thumb() #1 { ret void } define void @outline_default_KO_stack_arm() #0 { ret void } define void @outline_default_KO_stack_thumb() #0 { ret void } declare void @bar() @@ -118,120 +116,6 @@ ... --- -name: outline_default_KO_call_arm -tracksRegLiveness: true -body: | - ; CHECK-LABEL: name: outline_default_KO_call_arm - ; CHECK: bb.0: - ; CHECK: liveins: $lr - ; 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 - ; CHECK: $r2 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r3 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r4 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.1: - ; CHECK: liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; 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 - ; CHECK: $r2 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r3 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r4 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.2: - ; CHECK: liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; 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 - ; CHECK: $r2 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r3 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: $r4 = MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.3: - ; CHECK: liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; CHECK: $r2 = MOVr $lr, 14 /* CC::al */, $noreg, $noreg - ; CHECK: BX_RET 14 /* CC::al */, $noreg - bb.0: - liveins: $lr - BL @bar, implicit-def dead $lr, implicit $sp - $r0 = MOVi 2, 14, $noreg, $noreg - $r1 = MOVi 2, 14, $noreg, $noreg - $r2 = MOVi 2, 14, $noreg, $noreg - $r3 = MOVi 2, 14, $noreg, $noreg - $r4 = MOVi 2, 14, $noreg, $noreg - bb.1: - liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - BL @bar, implicit-def dead $lr, implicit $sp - $r0 = MOVi 2, 14, $noreg, $noreg - $r1 = MOVi 2, 14, $noreg, $noreg - $r2 = MOVi 2, 14, $noreg, $noreg - $r3 = MOVi 2, 14, $noreg, $noreg - $r4 = MOVi 2, 14, $noreg, $noreg - bb.2: - liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - BL @bar, implicit-def dead $lr, implicit $sp - $r0 = MOVi 2, 14, $noreg, $noreg - $r1 = MOVi 2, 14, $noreg, $noreg - $r2 = MOVi 2, 14, $noreg, $noreg - $r3 = MOVi 2, 14, $noreg, $noreg - $r4 = MOVi 2, 14, $noreg, $noreg - bb.3: - liveins: $lr, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - $r2 = MOVr $lr, 14, $noreg, $noreg - BX_RET 14, $noreg -... ---- - -name: outline_default_KO_call_thumb -tracksRegLiveness: true -body: | - ; CHECK-LABEL: name: outline_default_KO_call_thumb - ; CHECK: bb.0: - ; CHECK: liveins: $lr - ; 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 - ; CHECK: $r2 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.1: - ; CHECK: liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; 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 - ; CHECK: $r2 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.2: - ; CHECK: liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; 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 - ; CHECK: $r2 = t2MOVi 2, 14 /* CC::al */, $noreg, $noreg - ; CHECK: bb.3: - ; CHECK: liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - ; CHECK: $r2 = tMOVr $lr, 14 /* CC::al */, $noreg - ; CHECK: tBX_RET 14 /* CC::al */, $noreg - bb.0: - liveins: $lr - tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp - $r0 = t2MOVi 2, 14, $noreg, $noreg - $r1 = t2MOVi 2, 14, $noreg, $noreg - $r2 = t2MOVi 2, 14, $noreg, $noreg - bb.1: - liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp - $r0 = t2MOVi 2, 14, $noreg, $noreg - $r1 = t2MOVi 2, 14, $noreg, $noreg - $r2 = t2MOVi 2, 14, $noreg, $noreg - bb.2: - liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - tBL 14, $noreg, @bar, implicit-def dead $lr, implicit $sp - $r0 = t2MOVi 2, 14, $noreg, $noreg - $r1 = t2MOVi 2, 14, $noreg, $noreg - $r2 = t2MOVi 2, 14, $noreg, $noreg - bb.3: - liveins: $lr, $r3, $r4, $r5, $r6, $r7, $r8, $r9, $r10, $r11 - $r2 = tMOVr $lr, 14, $noreg - tBX_RET 14, $noreg -... ---- - name: outline_default_KO_stack_arm tracksRegLiveness: true body: |