Index: llvm/lib/Target/RISCV/CMakeLists.txt =================================================================== --- llvm/lib/Target/RISCV/CMakeLists.txt +++ llvm/lib/Target/RISCV/CMakeLists.txt @@ -31,6 +31,7 @@ RISCVRegisterBankInfo.cpp RISCVRegisterInfo.cpp RISCVSubtarget.cpp + RISCVMacroFusion.cpp RISCVTargetMachine.cpp RISCVTargetObjectFile.cpp RISCVTargetTransformInfo.cpp Index: llvm/lib/Target/RISCV/RISCV.td =================================================================== --- llvm/lib/Target/RISCV/RISCV.td +++ llvm/lib/Target/RISCV/RISCV.td @@ -67,6 +67,10 @@ def RV64 : HwMode<"+64bit">; def RV32 : HwMode<"-64bit">; +def FeatureMacroFusion + : SubtargetFeature<"macrofusion", "HasMacroFusion", "true", + "Various pair of instructions can be fused">; + def FeatureRV32E : SubtargetFeature<"e", "IsRV32E", "true", "Implements RV32E (provides 16 rather than 32 GPRs)">; @@ -112,7 +116,7 @@ def : ProcessorModel<"rocket-rv32", Rocket32Model, [FeatureRVCHints]>; def : ProcessorModel<"rocket-rv64", Rocket64Model, [Feature64Bit, - FeatureRVCHints]>; + FeatureRVCHints, FeatureMacroFusion]>; //===----------------------------------------------------------------------===// Index: llvm/lib/Target/RISCV/RISCVMacroFusion.h =================================================================== --- /dev/null +++ llvm/lib/Target/RISCV/RISCVMacroFusion.h @@ -0,0 +1,28 @@ +//===- RISCVMacroFusion.h - RISCV Macro Fusion ------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file This file contains the RISCV definition of the DAG scheduling mutation +/// to pair instructions back to back. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVMACROFUSION_H +#define LLVM_LIB_TARGET_RISCV_RISCVMACROFUSION_H + +#include "llvm/CodeGen/MachineScheduler.h" + +namespace llvm { + +/// Note that you have to add: +/// DAG.addMutation(createRISCVMacroFusionDAGMutation()); +/// to RISCVPassConfig::createMachineScheduler() to have an effect. +std::unique_ptr createRISCVMacroFusionDAGMutation(); + +} // llvm + +#endif Index: llvm/lib/Target/RISCV/RISCVMacroFusion.cpp =================================================================== --- /dev/null +++ llvm/lib/Target/RISCV/RISCVMacroFusion.cpp @@ -0,0 +1,122 @@ +//===- RISCVMacroFusion.cpp - RISCV Macro Fusion ----------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +/// \file This file contains the RISCV implementation of the DAG scheduling +/// mutation to pair instructions back to back. +// +//===----------------------------------------------------------------------===// + +#include "RISCVMacroFusion.h" +#include "RISCVSubtarget.h" +#include "llvm/CodeGen/MacroFusion.h" +#include "llvm/CodeGen/TargetInstrInfo.h" + +namespace llvm { + +// Fuse Indexed Load +static bool isIndexedLoad(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + return FirstMI->getOpcode() == RISCV::ADD && + SecondMI.getOpcode() == RISCV::LD; +} + +// Fuse Global Load and Far Jumps +static bool isGLoadOrFJump(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + if (FirstMI->getOpcode() == RISCV::AUIPC) { + switch (SecondMI.getOpcode()) { + // Global Load + case RISCV::LW: + return true; + // Far Jumps + case RISCV::LD: + return true; + } + } + return false; +} + +// Fuse Load Effective Address or Clear Upper Words Idioms +static bool isLEAPair(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + if (FirstMI->getOpcode() == RISCV::SLLI) { + switch (SecondMI.getOpcode()) { + // Load Effective Address + case RISCV::ADD: + return true; + // Clear Upper Word + case RISCV::SRLI: + return true; + } + } + return false; +} + +// Fuse Load Immediate Idioms +static bool isClearUpWord(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + if (FirstMI->getOpcode() == RISCV::LUI) { + switch (SecondMI.getOpcode()) { + // LUI based immediate Add + case RISCV::ADDI: + return true; + // LUI based Loads + case RISCV::LD: + return true; + } + } + return false; +} + +// Load Pair Idioms +static bool isLoadPair(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + return FirstMI->getOpcode() == RISCV::LD && + SecondMI.getOpcode() == RISCV::LD; +} + +// Store Pair Idioms +static bool isStorePair(const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + return FirstMI->getOpcode() == RISCV::SD && + SecondMI.getOpcode() == RISCV::SD; +} + +/// Check if the instr pair, FirstMI and SecondMI, should be fused +/// together. Given SecondMI, when FirstMI is unspecified, then check if +/// SecondMI may be part of a fused pair at all. +static bool shouldScheduleAdjacent(const TargetInstrInfo &TII, + const TargetSubtargetInfo &TSI, + const MachineInstr *FirstMI, + const MachineInstr &SecondMI) { + const RISCVSubtarget &ST = static_cast(TSI); + if (!ST.hasMacroFusion()) + return false; + + // We're only checking whether Second can be fused at all. + if (FirstMI == nullptr) + return true; + + if (isLEAPair(FirstMI, SecondMI) || + isClearUpWord(FirstMI, SecondMI) || + isIndexedLoad(FirstMI, SecondMI)) + return true; + + if (isStorePair(FirstMI, SecondMI) || isLoadPair(FirstMI, SecondMI)) + return true; + + if (isGLoadOrFJump(FirstMI, SecondMI)) + return true; + return false; +} + +std::unique_ptr createRISCVMacroFusionDAGMutation () { + return createMacroFusionDAGMutation(shouldScheduleAdjacent); +} + +} // end namespace llvm Index: llvm/lib/Target/RISCV/RISCVSubtarget.h =================================================================== --- llvm/lib/Target/RISCV/RISCVSubtarget.h +++ llvm/lib/Target/RISCV/RISCVSubtarget.h @@ -87,6 +87,7 @@ bool hasStdExtF() const { return HasStdExtF; } bool hasStdExtD() const { return HasStdExtD; } bool hasStdExtC() const { return HasStdExtC; } + bool hasMacroFusion() const { return HasMacroFusion; } bool is64Bit() const { return HasRV64; } bool isRV32E() const { return IsRV32E; } bool enableLinkerRelax() const { return EnableLinkerRelax; } @@ -106,6 +107,9 @@ std::unique_ptr Legalizer; std::unique_ptr RegBankInfo; + /// True if the processor supports macrofusion. + bool HasMacroFusion = false; + public: const CallLowering *getCallLowering() const override; InstructionSelector *getInstructionSelector() const override; Index: llvm/lib/Target/RISCV/RISCVTargetMachine.cpp =================================================================== --- llvm/lib/Target/RISCV/RISCVTargetMachine.cpp +++ llvm/lib/Target/RISCV/RISCVTargetMachine.cpp @@ -12,6 +12,7 @@ #include "RISCVTargetMachine.h" #include "RISCV.h" +#include "RISCVMacroFusion.h" #include "RISCVTargetObjectFile.h" #include "RISCVTargetTransformInfo.h" #include "TargetInfo/RISCVTargetInfo.h" @@ -119,7 +120,24 @@ RISCVTargetMachine &getRISCVTargetMachine() const { return getTM(); } - + ScheduleDAGInstrs * + createMachineScheduler(MachineSchedContext *C) const override { + ScheduleDAGMILive *DAG = createGenericSchedLive(C); + const RISCVSubtarget &ST = C->MF->getSubtarget(); + if (ST.hasMacroFusion()) + DAG->addMutation(createRISCVMacroFusionDAGMutation()); + return DAG; + } + ScheduleDAGInstrs * + createPostMachineScheduler(MachineSchedContext *C) const override { + const RISCVSubtarget &ST = C->MF->getSubtarget(); + if (ST.hasMacroFusion()) { + ScheduleDAGMI *DAG = createGenericSchedPostRA(C); + DAG->addMutation(createRISCVMacroFusionDAGMutation()); + return DAG; + } + return nullptr; + } void addIRPasses() override; bool addInstSelector() override; bool addIRTranslator() override;