diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -11,6 +11,7 @@ #include "llvm/Analysis/TargetTransformInfo.h" #include "llvm/CodeGen/BasicTTIImpl.h" #include "llvm/CodeGen/TargetLowering.h" +#include "llvm/IR/PatternMatch.h" #include using namespace llvm; @@ -38,6 +39,34 @@ getST()->getFeatureBits()); } +// Recognize (ashr (add (shl X, 32), C << 32), 32) which DAGCombine can turn +// into (addw X, C). If we hoist the constant, DAGCombine can't see the pattern. +// The shl could also be a mul with -(1 << 32). The add can also be a sub with +// a constant left hand side. We assume InstCombine canonicalized the add/sub. +static bool isShiftedADDWOrSUBW(Instruction *Inst, const APInt &Imm) { + using namespace PatternMatch; + if (Inst->getOpcode() != Instruction::Add && + Inst->getOpcode() != Instruction::Sub) + return false; + // Needs to be a 64-bit immediate with at least 32 trailing zeros. + if (Imm.getBitWidth() != 64 || Imm.countTrailingZeros() < 32) + return false; + unsigned ShlIdx = Inst->getOpcode() == Instruction::Add ? 0 : 1; + Value *ShlOp = Inst->getOperand(ShlIdx); + // Non-immediate source should be a shl by 32 or a mul by -(1 << 32). + if (!match(ShlOp, m_Shl(m_Value(), m_SpecificInt(32))) && + !match(ShlOp, m_Mul(m_Value(), m_SpecificInt(-1ULL << 32)))) + return false; + BasicBlock *BB = Inst->getParent(); + // Need to be in the same basic block. + if (cast(ShlOp)->getParent() != BB) + return false; + // User should be an ashr by 32. + return Inst->hasOneUse() && + match(*Inst->user_begin(), m_AShr(m_Value(), m_SpecificInt(32))) && + cast(*Inst->user_begin())->getParent() == BB; +} + InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt &Imm, Type *Ty, TTI::TargetCostKind CostKind, @@ -91,6 +120,9 @@ break; } + if (Inst && ST->is64Bit() && isShiftedADDWOrSUBW(Inst, Imm)) + return TTI::TCC_Free; + if (Takes12BitImm) { // Check immediate is the correct argument... if (Instruction::isCommutative(Opcode) || Idx == ImmArgIdx) { diff --git a/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll b/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll --- a/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll +++ b/llvm/test/Transforms/ConstantHoisting/RISCV/immediates.ll @@ -1,5 +1,5 @@ ; RUN: opt -mtriple=riscv32-unknown-elf -S -consthoist < %s | FileCheck %s -; RUN: opt -mtriple=riscv64-unknown-elf -S -consthoist < %s | FileCheck %s +; RUN: opt -mtriple=riscv64-unknown-elf -S -consthoist < %s | FileCheck %s --check-prefixes=CHECK,RV64 ; Check that we don't hoist immediates with small values. define i64 @test1(i64 %a) nounwind { @@ -81,3 +81,61 @@ %2 = mul i64 %1, -4294967296 ret i64 %2 } + +; The mul is equivalent to (neg (shl X, 32)). The mul+add+ashr can be replaced +; with subw using a much smaller constant. +define i64 @test10(i64 %a) nounwind { +; RV64-LABEL: test10 +; RV64: add i64 %1, 4294967296 +; RV64: add i64 %4, 4294967296 + %1 = mul i64 %a, -4294967296 + %2 = add i64 %1, 4294967296 + %3 = ashr i64 %1, 32 + %4 = mul i64 %3, -4294967296 + %5 = add i64 %4, 4294967296 + %6 = ashr i64 %5, 32 + ret i64 %6 +} + +; The mul is equivalent to (neg (shl X, 32)). The mul+sub+ashr can be replaced +; with addw using a much smaller constant. +define i64 @test11(i64 %a) nounwind { +; RV64-LABEL: test11 +; RV64: sub i64 4294967296, %1 +; RV64: sub i64 4294967296, %4 + %1 = mul i64 %a, -4294967296 + %2 = sub i64 4294967296, %1 + %3 = ashr i64 %1, 32 + %4 = mul i64 %3, -4294967296 + %5 = sub i64 4294967296, %4 + %6 = ashr i64 %5, 32 + ret i64 %6 +} + +; The shl+add+ashr can be replaced with subw using a much smaller constant. +define i64 @test12(i64 %a) nounwind { +; RV64-LABEL: test12 +; RV64: add i64 %1, 4294967296 +; RV64: add i64 %4, 4294967296 + %1 = shl i64 %a, 32 + %2 = add i64 %1, 4294967296 + %3 = ashr i64 %1, 32 + %4 = shl i64 %3, 32 + %5 = add i64 %4, 4294967296 + %6 = ashr i64 %5, 32 + ret i64 %6 +} + +; The shl+shl+ashr can be replaced with addw using a much smaller constant. +define i64 @test13(i64 %a) nounwind { +; RV64-LABEL: test13 +; RV64: sub i64 4294967296, %1 +; RV64: sub i64 4294967296, %4 + %1 = shl i64 %a, 32 + %2 = sub i64 4294967296, %1 + %3 = ashr i64 %1, 32 + %4 = shl i64 %3, 32 + %5 = sub i64 4294967296, %4 + %6 = ashr i64 %5, 32 + ret i64 %6 +}