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 @@ -61,6 +61,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.ll b/llvm/test/CodeGen/RISCV/machineoutliner.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/machineoutliner.ll @@ -0,0 +1,66 @@ +; RUN: llc -march riscv32 < %s | FileCheck -check-prefix=RV32I %s +; RUN: llc -march riscv32 -enable-machine-outliner < %s \ +; RUN: | FileCheck -check-prefix=RV32I-MO %s + +define i32 @baz() nounwind { +; RV32I-LABEL: baz: +; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 +; +; RV32I-MO-LABEL: baz: +; RV32I-MO: call t0, OUTLINED_FUNCTION_0 +entry: + %x = alloca i32, align 4 + %y = alloca i32, align 4 + store i32 4, i32* %x, align 4 + store i32 43, i32* %y, align 4 + %0 = load i32, i32* %y, align 4 + %1 = load i32, i32* %x, align 4 + %mul = mul nsw i32 %0, %1 + %add = add nsw i32 %mul, 51 + store i32 %add, i32* %x, align 4 + %2 = load i32, i32* %x, align 4 + ret i32 %2 +} + +define i32 @bar() nounwind { +; RV32I-LABEL: bar: +; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 +; +; RV32I-MO-LABEL: bar: +; RV32I-MO: call t0, OUTLINED_FUNCTION_0 +entry: + %x = alloca i32, align 4 + %y = alloca i32, align 4 + store i32 4, i32* %x, align 4 + store i32 43, i32* %y, align 4 + %0 = load i32, i32* %y, align 4 + %1 = load i32, i32* %x, align 4 + %mul = mul nsw i32 %0, %1 + %add = add nsw i32 %mul, 51 + store i32 %add, i32* %x, align 4 + %2 = load i32, i32* %x, align 4 + ret i32 %2 +} + +define i32 @foo() nounwind { +; RV32I-LABEL: foo: +; RV32I-NOT: call t0, OUTLINED_FUNCTION_0 +; +; RV32I-MO-LABEL: foo: +; RV32I-MO: call t0, OUTLINED_FUNCTION_0 +entry: + %x = alloca i32, align 4 + %y = alloca i32, align 4 + store i32 4, i32* %x, align 4 + store i32 43, i32* %y, align 4 + %0 = load i32, i32* %y, align 4 + %1 = load i32, i32* %x, align 4 + %mul = mul nsw i32 %0, %1 + %add = add nsw i32 %mul, 51 + store i32 %add, i32* %x, align 4 + %2 = load i32, i32* %x, align 4 + ret i32 %2 +} + +; RV32I-LABEL-NOT: OUTLINED_FUNCTION_0: +; RV32I-MO-LABEL: OUTLINED_FUNCTION_0: