Files
clang-p2996/llvm/test/CodeGen/RISCV/select-const.ll
Philip Reames 6657d4bd70 [TTI][RISCV] Unconditionally break critical edges to sink ADDI (#108889)
This looks like a rather weird change, so let me explain why this isn't
as unreasonable as it looks. Let's start with the problem it's solving.

```
define signext i32 @overlap_live_ranges(ptr %arg, i32 signext %arg1) { bb:
  %i = icmp eq i32 %arg1, 1
  br i1 %i, label %bb2, label %bb5

bb2:                                              ; preds = %bb
  %i3 = getelementptr inbounds nuw i8, ptr %arg, i64 4
  %i4 = load i32, ptr %i3, align 4
  br label %bb5

bb5:                                              ; preds = %bb2, %bb
  %i6 = phi i32 [ %i4, %bb2 ], [ 13, %bb ]
  ret i32 %i6
}
```

Right now, we codegen this as:

```
	li	a3, 1
	li	a2, 13
	bne	a1, a3, .LBB0_2
	lw	a2, 4(a0)
.LBB0_2:
	mv	a0, a2
	ret
```

In this example, we have two values which must be assigned to a0 per the
ABI (%arg, and the return value). SelectionDAG ensures that all values
used in a successor phi are defined before exit the predecessor block.
This creates an ADDI to materialize the immediate in the entry block.

Currently, this ADDI is not sunk into the tail block because we'd have
to split a critical edges to do so. Note that if our immediate was
anything large enough to require two instructions we *would* split this
critical edge.

Looking at other targets, we notice that they don't seem to have this
problem. They perform the sinking, and tail duplication that we don't.
Why? Well, it turns out for AArch64 that this is entirely an accident of
the existance of the gpr32all register class. The immediate is
materialized into the gpr32 class, and then copied into the gpr32all
register class. The existance of that copy puts us right back into the
two instruction case noted above.

This change essentially just bypasses this emergent behavior aspect of
the aarch64 behavior, and implements the same "always sink immediates"
behavior for RISCV as well.
2024-11-25 18:59:31 -08:00

430 lines
12 KiB
LLVM

; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py
; RUN: llc -mtriple=riscv32 -target-abi=ilp32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefixes=RV32,RV32I %s
; RUN: llc -mtriple=riscv32 -mattr=+f -target-abi=ilp32 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefixes=RV32,RV32IF %s
; RUN: llc -mtriple=riscv64 -target-abi=lp64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefixes=RV64,RV64I %s
; RUN: llc -mtriple=riscv64 -mattr=+f,+d -target-abi=lp64 -verify-machineinstrs < %s \
; RUN: | FileCheck -check-prefixes=RV64,RV64IFD %s
;; This tests how good we are at materialising constants using `select`. The aim
;; is that we do so without a branch if possible (at the moment our lowering of
;; select always introduces a branch).
;;
;; Currently the hook `convertSelectOfConstantsToMath` only is useful when the
;; constants are either 1 away from each other, or one is a power of two and
;; the other is zero.
define signext i32 @select_const_int_easy(i1 zeroext %a) nounwind {
; RV32-LABEL: select_const_int_easy:
; RV32: # %bb.0:
; RV32-NEXT: ret
;
; RV64-LABEL: select_const_int_easy:
; RV64: # %bb.0:
; RV64-NEXT: ret
%1 = select i1 %a, i32 1, i32 0
ret i32 %1
}
define signext i32 @select_const_int_one_away(i1 zeroext %a) nounwind {
; RV32-LABEL: select_const_int_one_away:
; RV32: # %bb.0:
; RV32-NEXT: li a1, 4
; RV32-NEXT: sub a0, a1, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_const_int_one_away:
; RV64: # %bb.0:
; RV64-NEXT: li a1, 4
; RV64-NEXT: sub a0, a1, a0
; RV64-NEXT: ret
%1 = select i1 %a, i32 3, i32 4
ret i32 %1
}
define signext i32 @select_const_int_pow2_zero(i1 zeroext %a) nounwind {
; RV32-LABEL: select_const_int_pow2_zero:
; RV32: # %bb.0:
; RV32-NEXT: slli a0, a0, 2
; RV32-NEXT: ret
;
; RV64-LABEL: select_const_int_pow2_zero:
; RV64: # %bb.0:
; RV64-NEXT: slli a0, a0, 2
; RV64-NEXT: ret
%1 = select i1 %a, i32 4, i32 0
ret i32 %1
}
define signext i32 @select_const_int_harder(i1 zeroext %a) nounwind {
; RV32-LABEL: select_const_int_harder:
; RV32: # %bb.0:
; RV32-NEXT: bnez a0, .LBB3_2
; RV32-NEXT: # %bb.1:
; RV32-NEXT: li a0, 38
; RV32-NEXT: ret
; RV32-NEXT: .LBB3_2:
; RV32-NEXT: li a0, 6
; RV32-NEXT: ret
;
; RV64-LABEL: select_const_int_harder:
; RV64: # %bb.0:
; RV64-NEXT: bnez a0, .LBB3_2
; RV64-NEXT: # %bb.1:
; RV64-NEXT: li a0, 38
; RV64-NEXT: ret
; RV64-NEXT: .LBB3_2:
; RV64-NEXT: li a0, 6
; RV64-NEXT: ret
%1 = select i1 %a, i32 6, i32 38
ret i32 %1
}
define float @select_const_fp(i1 zeroext %a) nounwind {
; RV32I-LABEL: select_const_fp:
; RV32I: # %bb.0:
; RV32I-NEXT: mv a1, a0
; RV32I-NEXT: lui a0, 263168
; RV32I-NEXT: bnez a1, .LBB4_2
; RV32I-NEXT: # %bb.1:
; RV32I-NEXT: lui a0, 264192
; RV32I-NEXT: .LBB4_2:
; RV32I-NEXT: ret
;
; RV32IF-LABEL: select_const_fp:
; RV32IF: # %bb.0:
; RV32IF-NEXT: bnez a0, .LBB4_2
; RV32IF-NEXT: # %bb.1:
; RV32IF-NEXT: lui a0, 264192
; RV32IF-NEXT: j .LBB4_3
; RV32IF-NEXT: .LBB4_2:
; RV32IF-NEXT: lui a0, 263168
; RV32IF-NEXT: .LBB4_3:
; RV32IF-NEXT: fmv.w.x fa5, a0
; RV32IF-NEXT: fmv.x.w a0, fa5
; RV32IF-NEXT: ret
;
; RV64I-LABEL: select_const_fp:
; RV64I: # %bb.0:
; RV64I-NEXT: mv a1, a0
; RV64I-NEXT: lui a0, 263168
; RV64I-NEXT: bnez a1, .LBB4_2
; RV64I-NEXT: # %bb.1:
; RV64I-NEXT: lui a0, 264192
; RV64I-NEXT: .LBB4_2:
; RV64I-NEXT: ret
;
; RV64IFD-LABEL: select_const_fp:
; RV64IFD: # %bb.0:
; RV64IFD-NEXT: bnez a0, .LBB4_2
; RV64IFD-NEXT: # %bb.1:
; RV64IFD-NEXT: lui a0, 264192
; RV64IFD-NEXT: j .LBB4_3
; RV64IFD-NEXT: .LBB4_2:
; RV64IFD-NEXT: lui a0, 263168
; RV64IFD-NEXT: .LBB4_3:
; RV64IFD-NEXT: fmv.w.x fa5, a0
; RV64IFD-NEXT: fmv.x.w a0, fa5
; RV64IFD-NEXT: ret
%1 = select i1 %a, float 3.0, float 4.0
ret float %1
}
define signext i32 @select_eq_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_eq_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: snez a0, a0
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_eq_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: snez a0, a0
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp eq i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_ne_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_ne_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: seqz a0, a0
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_ne_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: seqz a0, a0
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp ne i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_sgt_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_sgt_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: slt a0, a1, a0
; RV32-NEXT: neg a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_sgt_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: slt a0, a1, a0
; RV64-NEXT: neg a0, a0
; RV64-NEXT: ret
%1 = icmp sgt i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_slt_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_slt_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: slt a0, a0, a1
; RV32-NEXT: neg a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_slt_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: slt a0, a0, a1
; RV64-NEXT: neg a0, a0
; RV64-NEXT: ret
%1 = icmp slt i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_sge_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_sge_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: slt a0, a0, a1
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_sge_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: slt a0, a0, a1
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp sge i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_sle_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_sle_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: slt a0, a1, a0
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_sle_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: slt a0, a1, a0
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp sle i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_ugt_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_ugt_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: sltu a0, a1, a0
; RV32-NEXT: neg a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_ugt_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: sltu a0, a1, a0
; RV64-NEXT: neg a0, a0
; RV64-NEXT: ret
%1 = icmp ugt i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_ult_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_ult_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: sltu a0, a0, a1
; RV32-NEXT: neg a0, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_ult_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: sltu a0, a0, a1
; RV64-NEXT: neg a0, a0
; RV64-NEXT: ret
%1 = icmp ult i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_uge_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_uge_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: sltu a0, a0, a1
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_uge_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: sltu a0, a0, a1
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp uge i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define signext i32 @select_ule_zero_negone(i32 signext %a, i32 signext %b) nounwind {
; RV32-LABEL: select_ule_zero_negone:
; RV32: # %bb.0:
; RV32-NEXT: sltu a0, a1, a0
; RV32-NEXT: addi a0, a0, -1
; RV32-NEXT: ret
;
; RV64-LABEL: select_ule_zero_negone:
; RV64: # %bb.0:
; RV64-NEXT: sltu a0, a1, a0
; RV64-NEXT: addi a0, a0, -1
; RV64-NEXT: ret
%1 = icmp ule i32 %a, %b
%2 = select i1 %1, i32 -1, i32 0
ret i32 %2
}
define i32 @select_eq_1_2(i32 signext %a, i32 signext %b) {
; RV32-LABEL: select_eq_1_2:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: snez a0, a0
; RV32-NEXT: addi a0, a0, 1
; RV32-NEXT: ret
;
; RV64-LABEL: select_eq_1_2:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: snez a0, a0
; RV64-NEXT: addi a0, a0, 1
; RV64-NEXT: ret
%1 = icmp eq i32 %a, %b
%2 = select i1 %1, i32 1, i32 2
ret i32 %2
}
define i32 @select_ne_1_2(i32 signext %a, i32 signext %b) {
; RV32-LABEL: select_ne_1_2:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: seqz a0, a0
; RV32-NEXT: addi a0, a0, 1
; RV32-NEXT: ret
;
; RV64-LABEL: select_ne_1_2:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: seqz a0, a0
; RV64-NEXT: addi a0, a0, 1
; RV64-NEXT: ret
%1 = icmp ne i32 %a, %b
%2 = select i1 %1, i32 1, i32 2
ret i32 %2
}
define i32 @select_eq_10000_10001(i32 signext %a, i32 signext %b) {
; RV32-LABEL: select_eq_10000_10001:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: lui a1, 2
; RV32-NEXT: seqz a0, a0
; RV32-NEXT: addi a1, a1, 1810
; RV32-NEXT: sub a0, a1, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_eq_10000_10001:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: lui a1, 2
; RV64-NEXT: seqz a0, a0
; RV64-NEXT: addiw a1, a1, 1810
; RV64-NEXT: sub a0, a1, a0
; RV64-NEXT: ret
%1 = icmp eq i32 %a, %b
%2 = select i1 %1, i32 10001, i32 10002
ret i32 %2
}
define i32 @select_ne_10001_10002(i32 signext %a, i32 signext %b) {
; RV32-LABEL: select_ne_10001_10002:
; RV32: # %bb.0:
; RV32-NEXT: xor a0, a0, a1
; RV32-NEXT: lui a1, 2
; RV32-NEXT: snez a0, a0
; RV32-NEXT: addi a1, a1, 1810
; RV32-NEXT: sub a0, a1, a0
; RV32-NEXT: ret
;
; RV64-LABEL: select_ne_10001_10002:
; RV64: # %bb.0:
; RV64-NEXT: xor a0, a0, a1
; RV64-NEXT: lui a1, 2
; RV64-NEXT: snez a0, a0
; RV64-NEXT: addiw a1, a1, 1810
; RV64-NEXT: sub a0, a1, a0
; RV64-NEXT: ret
%1 = icmp ne i32 %a, %b
%2 = select i1 %1, i32 10001, i32 10002
ret i32 %2
}
define i32 @select_slt_zero_constant1_constant2(i32 signext %x) {
; RV32-LABEL: select_slt_zero_constant1_constant2:
; RV32: # %bb.0:
; RV32-NEXT: srai a0, a0, 31
; RV32-NEXT: andi a0, a0, 10
; RV32-NEXT: addi a0, a0, -3
; RV32-NEXT: ret
;
; RV64-LABEL: select_slt_zero_constant1_constant2:
; RV64: # %bb.0:
; RV64-NEXT: srai a0, a0, 63
; RV64-NEXT: andi a0, a0, 10
; RV64-NEXT: addi a0, a0, -3
; RV64-NEXT: ret
%cmp = icmp slt i32 %x, 0
%cond = select i1 %cmp, i32 7, i32 -3
ret i32 %cond
}
define i32 @select_sgt_negative_one_constant1_constant2(i32 signext %x) {
; RV32-LABEL: select_sgt_negative_one_constant1_constant2:
; RV32: # %bb.0:
; RV32-NEXT: srai a0, a0, 31
; RV32-NEXT: andi a0, a0, -10
; RV32-NEXT: addi a0, a0, 7
; RV32-NEXT: ret
;
; RV64-LABEL: select_sgt_negative_one_constant1_constant2:
; RV64: # %bb.0:
; RV64-NEXT: srai a0, a0, 63
; RV64-NEXT: andi a0, a0, -10
; RV64-NEXT: addi a0, a0, 7
; RV64-NEXT: ret
%cmp = icmp sgt i32 %x, -1
%cond = select i1 %cmp, i32 7, i32 -3
ret i32 %cond
}