diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.h b/llvm/lib/Target/RISCV/RISCVInstrInfo.h --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.h +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.h @@ -80,6 +80,36 @@ int64_t BrOffset) const override; bool isAsCheapAsAMove(const MachineInstr &MI) const override; + + // Return true if the function can safely be outlined from. + virtual bool + isFunctionSafeToOutlineFrom(MachineFunction &MF, + bool OutlineFromLinkOnceODRs) const override; + + // Return true if MBB is safe to outline from, and return any target-specific + // information in Flags. + virtual bool isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, + unsigned &Flags) const override; + + // Calculate target-specific information for a set of outlining candidates. + outliner::OutlinedFunction getOutliningCandidateInfo( + std::vector &RepeatedSequenceLocs) const override; + + // Return if/how a given MachineInstr should be outlined. + virtual outliner::InstrType + getOutliningType(MachineBasicBlock::iterator &MBBI, + unsigned Flags) const override; + + // Insert a custom frame for outlined functions. + virtual void + buildOutlinedFrame(MachineBasicBlock &MBB, MachineFunction &MF, + const outliner::OutlinedFunction &OF) const override; + + // Insert a call to an outlined function into a given basic block. + virtual MachineBasicBlock::iterator + insertOutlinedCall(Module &M, MachineBasicBlock &MBB, + MachineBasicBlock::iterator &It, MachineFunction &MF, + const outliner::Candidate &C) const override; }; } #endif diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -466,3 +466,140 @@ } return MI.isAsCheapAsAMove(); } + +bool RISCVInstrInfo::isFunctionSafeToOutlineFrom( + MachineFunction &MF, bool OutlineFromLinkOnceODRs) const { + const Function &F = MF.getFunction(); + + // Can F be deduplicated by the linker? If it can, don't outline from it. + if (!OutlineFromLinkOnceODRs && F.hasLinkOnceODRLinkage()) + return false; + + // Don't outline from functions with section markings; the program could + // expect that all the code is in the named section. + if (F.hasSection()) + return false; + + // It's safe to outline from MF. + return true; +} + +bool RISCVInstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, + unsigned &Flags) const { + // Check if we will be able to insert a call through the X5 register. + RegScavenger RS; + RS.enterBasicBlock(MBB); + return !RS.isRegUsed(RISCV::X5); +} + +// Enum values indicating how an outlined call should be constructed. +enum MachineOutlinerConstructionID { + MachineOutlinerDefault +}; + +outliner::OutlinedFunction RISCVInstrInfo::getOutliningCandidateInfo( + std::vector &RepeatedSequenceLocs) const { + unsigned SequenceSize = 0; + + auto I = RepeatedSequenceLocs[0].front(); + auto E = std::next(RepeatedSequenceLocs[0].back()); + for (; I != E; ++I) + SequenceSize += getInstSizeInBytes(*I); + + // RISCV::PseudoCALL = 8 bytes. + unsigned CallOverhead = 8; + for (auto &C : RepeatedSequenceLocs) + C.setCallInfo(MachineOutlinerDefault, CallOverhead); + + // RISCV::PseudoRET = 4 bytes. + unsigned FrameOverhead = 4; + return outliner::OutlinedFunction(RepeatedSequenceLocs, SequenceSize, + FrameOverhead, MachineOutlinerDefault); +} + +outliner::InstrType +RISCVInstrInfo::getOutliningType(MachineBasicBlock::iterator &MBBI, + unsigned Flags) const { + MachineInstr &MI = *MBBI; + MachineBasicBlock *MBB = MI.getParent(); + const TargetRegisterInfo *TRI = + MBB->getParent()->getSubtarget().getRegisterInfo(); + + // Positions generally can't safely be outlined. + if (MI.isPosition()) { + // We can manually strip out CFI instructions later. + if (MI.isCFIInstruction()) + return outliner::InstrType::Invisible; + + return outliner::InstrType::Illegal; + } + + // Don't trust the user to write safe inline assembly. + if (MI.isInlineAsm()) + return outliner::InstrType::Illegal; + + // We can't outline branches to other basic blocks. + if (MI.isTerminator() && !MBB->succ_empty()) + return outliner::InstrType::Illegal; + + // Don't allow modifying the X5 register which we use for return addresses for + // these outlined functions. + if (MI.modifiesRegister(RISCV::X5, TRI) || + MI.getDesc().hasImplicitDefOfPhysReg(RISCV::X5)) + return outliner::InstrType::Illegal; + + // Make sure the operands don't reference something unsafe. + for (const auto &MO : MI.operands()) + if (MO.isMBB() || MO.isBlockAddress()) + return outliner::InstrType::Illegal; + + // Don't allow instructions which won't be materialized to impact outlining + // analysis. + if (MI.isMetaInstruction()) + return outliner::InstrType::Invisible; + + return outliner::InstrType::Legal; +} + +void RISCVInstrInfo::buildOutlinedFrame( + MachineBasicBlock &MBB, MachineFunction &MF, + const outliner::OutlinedFunction &OF) const { + + // Strip out any CFI instructions, and any return statements. + bool Changed = true; + while (Changed) { + Changed = false; + auto I = MBB.begin(); + auto E = MBB.end(); + for (; I != E; ++I) { + if (I->isCFIInstruction()) { + I->removeFromParent(); + Changed = true; + break; + } + if (I->isReturn()) { + I->removeFromParent(); + Changed = true; + break; + } + } + } + + // Add in a return instruction to the end of the outlined frame. + MBB.insert(MBB.end(), BuildMI(MF, DebugLoc(), get(RISCV::JALR)) + .addReg(RISCV::X0) + .addReg(RISCV::X5) + .addImm(0)); +} + +MachineBasicBlock::iterator RISCVInstrInfo::insertOutlinedCall( + Module &M, MachineBasicBlock &MBB, MachineBasicBlock::iterator &It, + MachineFunction &MF, const outliner::Candidate &C) const { + + // Add in a call instruction to the outlined function at the given location. + It = MBB.insert(It, + BuildMI(MF, DebugLoc(), get(RISCV::PseudoCALLReg), RISCV::X5) + .addGlobalAddress(M.getNamedValue(MF.getName()), 0, + RISCVII::MO_CALL)); + return It; +} diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -66,6 +66,9 @@ TLOF(std::make_unique()), Subtarget(TT, CPU, FS, Options.MCOptions.getABIName(), *this) { initAsmInfo(); + + // RISC-V supports the MachineOutliner. + setMachineOutliner(true); } TargetTransformInfo diff --git a/llvm/test/CodeGen/RISCV/machineoutliner.mir b/llvm/test/CodeGen/RISCV/machineoutliner.mir new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/machineoutliner.mir @@ -0,0 +1,226 @@ +# RUN: llc -march riscv32 -x mir < %s | FileCheck -check-prefix=RV32I %s +# RUN: llc -march riscv32 -x mir -enable-machine-outliner < %s \ +# RUN: | FileCheck -check-prefix=RV32I-MO %s + +--- | + define i32 @outline_0(i32 %a, i32 %b) #0 { + entry: + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + define i32 @outline_1(i32 %a, i32 %b) #0 { + entry: + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + define i32 @outline_2(i32 %a, i32 %b) #0 { + entry: + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + ; Should not outline linkonce_odr functions which could be + ; deduplicated by the linker. + define linkonce_odr i32 @dont_outline_0(i32 %a, i32 %b) #0 { + entry: + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + ; Should not outline functions with named linker sections + define i32 @dont_outline_1(i32 %a, i32 %b) #0 section "named" { + entry: + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + ; Cannot outline if the X5 (t0) register is not free + define i32 @dont_outline_2(i32 %a, i32 %b) #0 { + entry: + tail call void asm sideeffect "", "~{x5}"() + %0 = add i32 %a, 17 + %1 = or i32 %b, 1023 + %2 = and i32 %0, %1 + ret i32 %2 + } + + declare void @llvm.stackprotector(i8*, i8**) #0 + + attributes #0 = { nounwind } + +... +--- +name: outline_0 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: outline_0: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: outline_0: + ; RV32I-MO: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +... +--- +name: outline_1 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: outline_1: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: outline_1: + ; RV32I-MO: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +... +--- +name: outline_2 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: outline_2: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: outline_2: + ; RV32I-MO: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +... +--- +name: dont_outline_0 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: dont_outline_0: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: dont_outline_0: + ; RV32I-MO-NOT: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +... +--- +name: dont_outline_1 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: dont_outline_1: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: dont_outline_1: + ; RV32I-MO-NOT: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +... +--- +name: dont_outline_2 +alignment: 2 +tracksRegLiveness: true +liveins: + - { reg: '$x10' } + - { reg: '$x11' } +frameInfo: + maxAlignment: 1 + maxCallFrameSize: 0 +machineFunctionInfo: {} +body: | + bb.0.entry: + ; RV32I-LABEL: dont_outline_2: + ; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 + ; + ; RV32I-MO-LABEL: dont_outline_2: + ; RV32I-MO-NOT: call t0, OUTLINED_FUNCTION_0 + liveins: $x10, $x11 + + renamable $x11 = ORI killed renamable $x11, 1023 + renamable $x10 = ADDI killed renamable $x10, 17 + renamable $x10 = AND killed renamable $x10, killed renamable $x11 + INLINEASM &"", 1, 12, implicit-def dead early-clobber $x5 + CFI_INSTRUCTION def_cfa_offset 0 + PseudoRET implicit $x10 + +...