From a639a0b7d217c307bc9504cbbe2d5776b159bbba Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 22 Sep 2025 10:44:44 -0700 Subject: [PATCH 1/4] Pre-commit tests --- llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll | 121 +++++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll index 55b0d1f0bf7be..898bb2bc69050 100644 --- a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll +++ b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll @@ -155,3 +155,124 @@ define i1 @test9(i64 %x) { %b = icmp eq i64 %a, u0x08000000 ret i1 %b } + +; Make sure the and constant doesn't get converted to an opaque constant by +; ConstantHoisting. If it's an opaque constant, we'll have addi -16 and addi 15. +define i64 @test10(i64 %0) #0 { +; RV32-LABEL: test10: +; RV32: # %bb.0: # %entry +; RV32-NEXT: addi a0, a0, -16 +; RV32-NEXT: addi a0, a0, 15 +; RV32-NEXT: andi a0, a0, -16 +; RV32-NEXT: snez a0, a0 +; RV32-NEXT: li a1, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test10: +; RV64: # %bb.0: # %entry +; RV64-NEXT: addi a0, a0, -16 +; RV64-NEXT: addi a0, a0, 15 +; RV64-NEXT: sraiw a0, a0, 4 +; RV64-NEXT: snez a0, a0 +; RV64-NEXT: ret +entry: + %1 = add nuw nsw i64 %0, u0xffffffff + %2 = and i64 %1, u0xfffffff0 + %3 = icmp ne i64 %2, 0 + %4 = zext i1 %3 to i64 + ret i64 %4 +} + +; Make sure the and constant doesn't get converted to an opaque constant by +; ConstantHoisting. If it's an opaque constant, we'll have addi -16 and addi 15. +define i64 @test11(i64 %0) #0 { +; RV32-LABEL: test11: +; RV32: # %bb.0: # %entry +; RV32-NEXT: addi a0, a0, -16 +; RV32-NEXT: addi a0, a0, 15 +; RV32-NEXT: srai a0, a0, 4 +; RV32-NEXT: addi a0, a0, 1621 +; RV32-NEXT: seqz a0, a0 +; RV32-NEXT: li a1, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test11: +; RV64: # %bb.0: # %entry +; RV64-NEXT: addi a0, a0, -16 +; RV64-NEXT: addi a0, a0, 15 +; RV64-NEXT: sraiw a0, a0, 4 +; RV64-NEXT: addi a0, a0, 1621 +; RV64-NEXT: seqz a0, a0 +; RV64-NEXT: ret +entry: + %1 = add nuw nsw i64 %0, u0xffffffff + %2 = and i64 %1, u0xfffffff0 + %3 = icmp eq i64 %2, u0xffff9ab0 + %4 = zext i1 %3 to i64 + ret i64 %4 +} + +; Make sure the and constant doesn't get converted to an opaque constant by +; ConstantHoisting. If it's an opaque constant we'll end up with constant +; materialization sequences on RV64. +define i64 @test12(i64 %0) #0 { +; RV32-LABEL: test12: +; RV32: # %bb.0: # %entry +; RV32-NEXT: addi a0, a0, -3 +; RV32-NEXT: seqz a0, a0 +; RV32-NEXT: li a1, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test12: +; RV64: # %bb.0: # %entry +; RV64-NEXT: li a1, 1 +; RV64-NEXT: slli a1, a1, 32 +; RV64-NEXT: addi a2, a1, -16 +; RV64-NEXT: add a0, a0, a2 +; RV64-NEXT: addi a2, a2, 15 +; RV64-NEXT: and a0, a0, a2 +; RV64-NEXT: addi a1, a1, -13 +; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: seqz a0, a0 +; RV64-NEXT: ret +entry: + %1 = add nuw nsw i64 %0, u0xfffffff0 + %2 = and i64 %1, u0xffffffff + %3 = icmp eq i64 %2, u0xfffffff3 + %4 = zext i1 %3 to i64 + ret i64 %4 +} + +; Make sure the and constant doesn't get converted to an opaque constant by +; ConstantHoisting. +define i64 @test13(i64 %0) #0 { +; RV32-LABEL: test13: +; RV32: # %bb.0: # %entry +; RV32-NEXT: lui a1, 524288 +; RV32-NEXT: addi a1, a1, 15 +; RV32-NEXT: add a0, a0, a1 +; RV32-NEXT: addi a1, a1, -15 +; RV32-NEXT: and a0, a0, a1 +; RV32-NEXT: seqz a0, a0 +; RV32-NEXT: li a1, 0 +; RV32-NEXT: ret +; +; RV64-LABEL: test13: +; RV64: # %bb.0: # %entry +; RV64-NEXT: lui a1, 524288 +; RV64-NEXT: addi a1, a1, -15 +; RV64-NEXT: sub a0, a0, a1 +; RV64-NEXT: li a1, 1 +; RV64-NEXT: slli a1, a1, 31 +; RV64-NEXT: addi a1, a1, 15 +; RV64-NEXT: addi a1, a1, -15 +; RV64-NEXT: and a0, a0, a1 +; RV64-NEXT: seqz a0, a0 +; RV64-NEXT: ret +entry: + %1 = add nuw nsw i64 %0, u0x8000000f + %2 = and i64 %1, u0x80000000 + %3 = icmp eq i64 %2, 0 + %4 = zext i1 %3 to i64 + ret i64 %4 +} From 78dddfa65a254f91b720925264ab2362bd8dad54 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Mon, 22 Sep 2025 10:41:47 -0700 Subject: [PATCH 2/4] [RISCV] Teach getIntImmCostInst about X & -(1 << C1) & 0xffffffff) == C2 << C1 We can rewrite this to (sraiw X, C1) == C2 so the AND immediate is free. This fixes the opaque constant case mentioned in #157416. --- .../Target/RISCV/RISCVTargetTransformInfo.cpp | 38 +++++++++++++++++++ llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll | 31 ++++----------- 2 files changed, 46 insertions(+), 23 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index a06faa414a2ef..73edfbf28f3d2 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -166,6 +166,41 @@ static bool canUseShiftPair(Instruction *Inst, const APInt &Imm) { return false; } +// If this is a 64-bit AND with a mask of the form -(1 << C) in the lower 32 +// bits and the only user is an equality comparison, we might be able to use a +// sraiw instead. This avoids the need to materialize the AND constant. +static bool canUseSRAIWCmp(Instruction *Inst, const APInt &Imm) { + if (!Inst->hasOneUse()) + return false; + + // Look for equality comparison. + auto *Cmp = dyn_cast(*Inst->user_begin()); + if (!Cmp || !Cmp->isEquality()) + return false; + + // Right hand side of comparison should be a constant. + auto *C = dyn_cast(Cmp->getOperand(1)); + if (!C) + return false; + + uint64_t Mask = Imm.getZExtValue(); + + // Mask should be of the form -(1 << C) in the lower 32 bits. + if (!isUInt<32>(Mask) || !isPowerOf2_32(-uint32_t(Mask))) + return false; + + // Comparison constant should be a subset of Mask. + uint64_t CmpC = C->getZExtValue(); + if ((CmpC & Mask) != CmpC) + return false; + + // We'll need to sign extend the comparison constant and shift it right. Make + // sure the new constant can use addi/xori+seqz/snez. + unsigned ShiftBits = llvm::countr_zero(Mask); + int64_t NewCmpC = SignExtend64<32>(CmpC) >> ShiftBits; + return NewCmpC >= -2048 && NewCmpC <= 2048; +} + InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, const APInt &Imm, Type *Ty, TTI::TargetCostKind CostKind, @@ -223,6 +258,9 @@ InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, if (Inst && Idx == 1 && Imm.getBitWidth() <= ST->getXLen() && canUseShiftPair(Inst, Imm)) return TTI::TCC_Free; + if (Inst && Idx == 1 && Imm.getBitWidth() == 64 && + canUseSRAIWCmp(Inst, Imm)) + return TTI::TCC_Free; Takes12BitImm = true; break; case Instruction::Add: diff --git a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll index 898bb2bc69050..2a46a59e90535 100644 --- a/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll +++ b/llvm/test/CodeGen/RISCV/and-negpow2-cmp.ll @@ -161,8 +161,7 @@ define i1 @test9(i64 %x) { define i64 @test10(i64 %0) #0 { ; RV32-LABEL: test10: ; RV32: # %bb.0: # %entry -; RV32-NEXT: addi a0, a0, -16 -; RV32-NEXT: addi a0, a0, 15 +; RV32-NEXT: addi a0, a0, -1 ; RV32-NEXT: andi a0, a0, -16 ; RV32-NEXT: snez a0, a0 ; RV32-NEXT: li a1, 0 @@ -170,8 +169,7 @@ define i64 @test10(i64 %0) #0 { ; ; RV64-LABEL: test10: ; RV64: # %bb.0: # %entry -; RV64-NEXT: addi a0, a0, -16 -; RV64-NEXT: addi a0, a0, 15 +; RV64-NEXT: addi a0, a0, -1 ; RV64-NEXT: sraiw a0, a0, 4 ; RV64-NEXT: snez a0, a0 ; RV64-NEXT: ret @@ -188,8 +186,7 @@ entry: define i64 @test11(i64 %0) #0 { ; RV32-LABEL: test11: ; RV32: # %bb.0: # %entry -; RV32-NEXT: addi a0, a0, -16 -; RV32-NEXT: addi a0, a0, 15 +; RV32-NEXT: addi a0, a0, -1 ; RV32-NEXT: srai a0, a0, 4 ; RV32-NEXT: addi a0, a0, 1621 ; RV32-NEXT: seqz a0, a0 @@ -198,8 +195,7 @@ define i64 @test11(i64 %0) #0 { ; ; RV64-LABEL: test11: ; RV64: # %bb.0: # %entry -; RV64-NEXT: addi a0, a0, -16 -; RV64-NEXT: addi a0, a0, 15 +; RV64-NEXT: addi a0, a0, -1 ; RV64-NEXT: sraiw a0, a0, 4 ; RV64-NEXT: addi a0, a0, 1621 ; RV64-NEXT: seqz a0, a0 @@ -225,14 +221,8 @@ define i64 @test12(i64 %0) #0 { ; ; RV64-LABEL: test12: ; RV64: # %bb.0: # %entry -; RV64-NEXT: li a1, 1 -; RV64-NEXT: slli a1, a1, 32 -; RV64-NEXT: addi a2, a1, -16 -; RV64-NEXT: add a0, a0, a2 -; RV64-NEXT: addi a2, a2, 15 -; RV64-NEXT: and a0, a0, a2 -; RV64-NEXT: addi a1, a1, -13 -; RV64-NEXT: xor a0, a0, a1 +; RV64-NEXT: addiw a0, a0, -16 +; RV64-NEXT: addi a0, a0, 13 ; RV64-NEXT: seqz a0, a0 ; RV64-NEXT: ret entry: @@ -251,8 +241,7 @@ define i64 @test13(i64 %0) #0 { ; RV32-NEXT: lui a1, 524288 ; RV32-NEXT: addi a1, a1, 15 ; RV32-NEXT: add a0, a0, a1 -; RV32-NEXT: addi a1, a1, -15 -; RV32-NEXT: and a0, a0, a1 +; RV32-NEXT: srli a0, a0, 31 ; RV32-NEXT: seqz a0, a0 ; RV32-NEXT: li a1, 0 ; RV32-NEXT: ret @@ -262,11 +251,7 @@ define i64 @test13(i64 %0) #0 { ; RV64-NEXT: lui a1, 524288 ; RV64-NEXT: addi a1, a1, -15 ; RV64-NEXT: sub a0, a0, a1 -; RV64-NEXT: li a1, 1 -; RV64-NEXT: slli a1, a1, 31 -; RV64-NEXT: addi a1, a1, 15 -; RV64-NEXT: addi a1, a1, -15 -; RV64-NEXT: and a0, a0, a1 +; RV64-NEXT: sraiw a0, a0, 31 ; RV64-NEXT: seqz a0, a0 ; RV64-NEXT: ret entry: From 82f426c00d022cb309ee8e349b5519aacf240ac8 Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Tue, 23 Sep 2025 08:34:35 -0700 Subject: [PATCH 3/4] fixup! Update comment --- llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index 73edfbf28f3d2..aef5175518e65 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -166,10 +166,11 @@ static bool canUseShiftPair(Instruction *Inst, const APInt &Imm) { return false; } -// If this is a 64-bit AND with a mask of the form -(1 << C) in the lower 32 -// bits and the only user is an equality comparison, we might be able to use a -// sraiw instead. This avoids the need to materialize the AND constant. -static bool canUseSRAIWCmp(Instruction *Inst, const APInt &Imm) { +// If this is i64 AND is part of (X & -(1 << C1) & 0xffffffff) == C2 << C1), +// DAGCombiner can convert this to (sraiw X, C1) == sext(C2) for RV64. On RV32, +// the type will be split so only the lower 32 bits need to be compared using +// (srai/srli X, C) == C2. +static bool canUseShiftCmp(Instruction *Inst, const APInt &Imm) { if (!Inst->hasOneUse()) return false; From afb9f1be337a753e6e569027e0d628918ea3dc3c Mon Sep 17 00:00:00 2001 From: Craig Topper Date: Tue, 23 Sep 2025 22:40:03 -0700 Subject: [PATCH 4/4] Update llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp --- llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp index aef5175518e65..38e14b096cd2a 100644 --- a/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVTargetTransformInfo.cpp @@ -260,7 +260,7 @@ InstructionCost RISCVTTIImpl::getIntImmCostInst(unsigned Opcode, unsigned Idx, canUseShiftPair(Inst, Imm)) return TTI::TCC_Free; if (Inst && Idx == 1 && Imm.getBitWidth() == 64 && - canUseSRAIWCmp(Inst, Imm)) + canUseShiftCmp(Inst, Imm)) return TTI::TCC_Free; Takes12BitImm = true; break;