diff --git a/llvm/lib/Target/RISCV/CMakeLists.txt b/llvm/lib/Target/RISCV/CMakeLists.txt --- a/llvm/lib/Target/RISCV/CMakeLists.txt +++ b/llvm/lib/Target/RISCV/CMakeLists.txt @@ -27,6 +27,7 @@ RISCVSubtarget.cpp RISCVTargetMachine.cpp RISCVTargetObjectFile.cpp + RISCVTargetTransformInfo.cpp ) add_subdirectory(AsmParser) diff --git a/llvm/lib/Target/RISCV/LLVMBuild.txt b/llvm/lib/Target/RISCV/LLVMBuild.txt --- a/llvm/lib/Target/RISCV/LLVMBuild.txt +++ b/llvm/lib/Target/RISCV/LLVMBuild.txt @@ -29,6 +29,6 @@ type = Library name = RISCVCodeGen parent = RISCV -required_libraries = AsmPrinter Core CodeGen MC RISCVDesc +required_libraries = Analysis AsmPrinter Core CodeGen MC RISCVDesc RISCVInfo RISCVUtils SelectionDAG Support Target add_to_library_groups = RISCV diff --git a/llvm/lib/Target/RISCV/RISCVTargetMachine.h b/llvm/lib/Target/RISCV/RISCVTargetMachine.h --- a/llvm/lib/Target/RISCV/RISCVTargetMachine.h +++ b/llvm/lib/Target/RISCV/RISCVTargetMachine.h @@ -39,6 +39,8 @@ TargetLoweringObjectFile *getObjFileLowering() const override { return TLOF.get(); } + + TargetTransformInfo getTargetTransformInfo(const Function &F) override; }; } 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 @@ -10,11 +10,13 @@ // //===----------------------------------------------------------------------===// -#include "RISCV.h" #include "RISCVTargetMachine.h" +#include "RISCV.h" #include "RISCVTargetObjectFile.h" +#include "RISCVTargetTransformInfo.h" #include "TargetInfo/RISCVTargetInfo.h" #include "llvm/ADT/STLExtras.h" +#include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/Passes.h" #include "llvm/CodeGen/TargetLoweringObjectFileImpl.h" #include "llvm/CodeGen/TargetPassConfig.h" @@ -61,6 +63,11 @@ initAsmInfo(); } +TargetTransformInfo +RISCVTargetMachine::getTargetTransformInfo(const Function &F) { + return TargetTransformInfo(RISCVTTIImpl(this, F)); +} + namespace { class RISCVPassConfig : public TargetPassConfig { public: diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.h @@ -0,0 +1,52 @@ +//===- RISCVTargetTransformInfo.h - RISC-V specific TTI ---------*- C++ -*-===// +// +// 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 defines a TargetTransformInfo::Concept conforming object specific +/// to the RISC-V target machine. It uses the target's detailed information to +/// provide more precise answers to certain TTI queries, while letting the +/// target independent and default TTI implementations handle the rest. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_LIB_TARGET_RISCV_RISCVTARGETTRANSFORMINFO_H +#define LLVM_LIB_TARGET_RISCV_RISCVTARGETTRANSFORMINFO_H + +#include "RISCVSubtarget.h" +#include "RISCVTargetMachine.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/IR/Function.h" + +namespace llvm { + +class RISCVTTIImpl : public BasicTTIImplBase { + using BaseT = BasicTTIImplBase; + using TTI = TargetTransformInfo; + + friend BaseT; + + const RISCVSubtarget *ST; + const RISCVTargetLowering *TLI; + + const RISCVSubtarget *getST() const { return ST; } + const RISCVTargetLowering *getTLI() const { return TLI; } + +public: + explicit RISCVTTIImpl(const RISCVTargetMachine *TM, const Function &F) + : BaseT(TM, F.getParent()->getDataLayout()), ST(TM->getSubtargetImpl(F)), + TLI(ST->getTargetLowering()) {} + + int getIntImmCost(const APInt &Imm, Type *Ty); + int getIntImmCost(unsigned Opcode, unsigned Idx, const APInt &Imm, Type *Ty); + int getIntImmCost(Intrinsic::ID IID, unsigned Idx, const APInt &Imm, + Type *Ty); +}; + +} // end namespace llvm + +#endif // LLVM_LIB_TARGET_RISCV_RISCVTARGETTRANSFORMINFO_H \ No newline at end of file diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp new file mode 100644 --- /dev/null +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -0,0 +1,67 @@ +//===-- RISCVTargetTransformInfo.cpp - RISC-V specific TTI ----------------===// +// +// 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 +// +//===----------------------------------------------------------------------===// + +#include "RISCVTargetTransformInfo.h" +#include "Utils/RISCVMatInt.h" +#include "llvm/Analysis/TargetTransformInfo.h" +#include "llvm/CodeGen/BasicTTIImpl.h" +#include "llvm/CodeGen/TargetLowering.h" +using namespace llvm; + +#define DEBUG_TYPE "riscvtti" + +int RISCVTTIImpl::getIntImmCost(const APInt &Imm, Type *Ty) { + assert(Ty->isIntegerTy() && + "getIntImmCost can only estimate cost of materialising integers"); + + // We have a Zero register, so 0 is always free. + if (Imm == 0) + return TTI::TCC_Free; + + const DataLayout &DL = getDataLayout(); + return RISCVMatInt::getIntMatCost(Imm, DL.getTypeSizeInBits(Ty), + getST()->is64Bit()); +} + +int RISCVTTIImpl::getIntImmCost(unsigned Opcode, unsigned Idx, const APInt &Imm, + Type *Ty) { + assert(Ty->isIntegerTy() && + "getIntImmCost can only estimate cost of materialising integers"); + + // We have a Zero register, so 0 is always free. + if (Imm == 0) + return TTI::TCC_Free; + + // Most arithmetic instructions in RISC-V can take a 12-bit immediate. + bool isAddLike = Instruction::isBinaryOp(Opcode); + if (isAddLike) { + // Fits into the 12-bit immediate. + if (getTLI()->isLegalAddImmediate(Imm.getSExtValue())) + return TTI::TCC_Free; + + // Otherwise, use the full materialisation cost. + return getIntImmCost(Imm, Ty); + } + + switch (Opcode) { + default: + // By default, no hoisting. + return TTI::TCC_Free; + case Instruction::GetElementPtr: + // Never hoist any arguments to a GetElementPtr. CodeGenPrepare will + // split up large offsets in GEP into better parts than ConstantHoisting + // can. + return TTI::TCC_Free; + } +} + +int RISCVTTIImpl::getIntImmCost(Intrinsic::ID IID, unsigned Idx, + const APInt &Imm, Type *Ty) { + // Use default hoisting behaviour + return BaseT::getIntImmCost(IID, Idx, Imm, Ty); +} diff --git a/llvm/test/CodeGen/RISCV/imm-cse.ll b/llvm/test/CodeGen/RISCV/imm-cse.ll --- a/llvm/test/CodeGen/RISCV/imm-cse.ll +++ b/llvm/test/CodeGen/RISCV/imm-cse.ll @@ -11,19 +11,19 @@ ; RV32I-LABEL: imm32_cse: ; RV32I: # %bb.0: ; RV32I-NEXT: lui a0, 1 -; RV32I-NEXT: addi a1, a0, 1 -; RV32I-NEXT: lui a2, %hi(src) -; RV32I-NEXT: lw a3, %lo(src)(a2) -; RV32I-NEXT: add a1, a3, a1 +; RV32I-NEXT: addi a0, a0, 1 +; RV32I-NEXT: lui a1, %hi(src) +; RV32I-NEXT: lw a2, %lo(src)(a1) +; RV32I-NEXT: add a2, a2, a0 ; RV32I-NEXT: lui a3, %hi(dst) -; RV32I-NEXT: sw a1, %lo(dst)(a3) -; RV32I-NEXT: addi a1, a0, 2 -; RV32I-NEXT: lw a4, %lo(src)(a2) -; RV32I-NEXT: add a1, a4, a1 -; RV32I-NEXT: sw a1, %lo(dst)(a3) -; RV32I-NEXT: addi a0, a0, 3 -; RV32I-NEXT: lw a1, %lo(src)(a2) +; RV32I-NEXT: sw a2, %lo(dst)(a3) +; RV32I-NEXT: lw a2, %lo(src)(a1) +; RV32I-NEXT: add a2, a2, a0 +; RV32I-NEXT: addi a2, a2, 1 +; RV32I-NEXT: sw a2, %lo(dst)(a3) +; RV32I-NEXT: lw a1, %lo(src)(a1) ; RV32I-NEXT: add a0, a1, a0 +; RV32I-NEXT: addi a0, a0, 2 ; RV32I-NEXT: sw a0, %lo(dst)(a3) ; RV32I-NEXT: ret %1 = load volatile i32, i32* @src diff --git a/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll b/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll @@ -0,0 +1,29 @@ +; RUN: opt -mtriple=riscv32-unknown-elf -S -consthoist < %s | FileCheck %s +; RUN: opt -mtriple=riscv64-unknown-elf -S -consthoist < %s | FileCheck %s + +; Check that we don't hoist immediates with small values. +define i64 @test1(i64 %a) nounwind { +; CHECK-LABEL: test1 +; CHECK-NOT: %const = bitcast i64 2 to i64 + %1 = mul i64 %a, 2 + %2 = add i64 %1, 2 + ret i64 %2 +} + +; Check that we don't hoist immediates with small values. +define i64 @test2(i64 %a) nounwind { +; CHECK-LABEL: test2 +; CHECK-NOT: %const = bitcast i64 2047 to i64 + %1 = mul i64 %a, 2047 + %2 = add i64 %1, 2047 + ret i64 %2 +} + +; Check that we hoist immediates with large values. +define i64 @test3(i64 %a) nounwind { +; CHECK-LABEL: test3 +; CHECK: %const = bitcast i64 32767 to i64 + %1 = mul i64 %a, 32767 + %2 = add i64 %1, 32767 + ret i64 %2 +} \ No newline at end of file diff --git a/llvm/test/Transforms/ConstantHoisting/RISCV/lit.local.cfg b/llvm/test/Transforms/ConstantHoisting/RISCV/lit.local.cfg new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/ConstantHoisting/RISCV/lit.local.cfg @@ -0,0 +1,2 @@ +if not 'RISCV' in config.root.targets: + config.unsupported = True \ No newline at end of file