blob: c06161ad3d696e1e62b3b776877cc5eb164d5cc9 [file] [edit]
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 5
; RUN: llc < %s -mtriple=wasm32-unknown-unknown -O3 -filetype=asm | FileCheck %s --check-prefix=W32
; RUN: llc < %s -mtriple=wasm64-unknown-unknown -O3 -filetype=asm | FileCheck %s --check-prefix=W64
; Test smin(x, 0) pattern
define i32 @test_ctselect_smin_zero(i32 %x) {
; W32-LABEL: test_ctselect_smin_zero:
; W32: .functype test_ctselect_smin_zero (i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.lt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_smin_zero:
; W64: .functype test_ctselect_smin_zero (i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.lt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: # fallthrough-return
%cmp = icmp slt i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 0)
ret i32 %result
}
; Test smax(x, 0) pattern
define i32 @test_ctselect_smax_zero(i32 %x) {
; W32-LABEL: test_ctselect_smax_zero:
; W32: .functype test_ctselect_smax_zero (i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.gt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_smax_zero:
; W64: .functype test_ctselect_smax_zero (i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.gt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: # fallthrough-return
%cmp = icmp sgt i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 0)
ret i32 %result
}
; Test generic smin pattern
define i32 @test_ctselect_smin_generic(i32 %x, i32 %y) {
; W32-LABEL: test_ctselect_smin_generic:
; W32: .functype test_ctselect_smin_generic (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.lt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_smin_generic:
; W64: .functype test_ctselect_smin_generic (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.lt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%cmp = icmp slt i32 %x, %y
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 %y)
ret i32 %result
}
; Test generic smax pattern
define i32 @test_ctselect_smax_generic(i32 %x, i32 %y) {
; W32-LABEL: test_ctselect_smax_generic:
; W32: .functype test_ctselect_smax_generic (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.gt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_smax_generic:
; W64: .functype test_ctselect_smax_generic (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.gt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%cmp = icmp sgt i32 %x, %y
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 %y)
ret i32 %result
}
; Test umin pattern
define i32 @test_ctselect_umin_generic(i32 %x, i32 %y) {
; W32-LABEL: test_ctselect_umin_generic:
; W32: .functype test_ctselect_umin_generic (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.lt_u
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_umin_generic:
; W64: .functype test_ctselect_umin_generic (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.lt_u
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%cmp = icmp ult i32 %x, %y
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 %y)
ret i32 %result
}
; Test umax pattern
define i32 @test_ctselect_umax_generic(i32 %x, i32 %y) {
; W32-LABEL: test_ctselect_umax_generic:
; W32: .functype test_ctselect_umax_generic (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.gt_u
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_umax_generic:
; W64: .functype test_ctselect_umax_generic (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.gt_u
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%cmp = icmp ugt i32 %x, %y
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 %y)
ret i32 %result
}
; Test abs pattern
define i32 @test_ctselect_abs(i32 %x) {
; W32-LABEL: test_ctselect_abs:
; W32: .functype test_ctselect_abs (i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.sub
; W32-NEXT: local.get 0
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.lt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_abs:
; W64: .functype test_ctselect_abs (i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.sub
; W64-NEXT: local.get 0
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.lt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%neg = sub i32 0, %x
%cmp = icmp slt i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %neg, i32 %x)
ret i32 %result
}
; Test nabs pattern (negative abs)
define i32 @test_ctselect_nabs(i32 %x) {
; W32-LABEL: test_ctselect_nabs:
; W32: .functype test_ctselect_nabs (i32) -> (i32)
; W32-NEXT: .local i32
; W32-NEXT: # %bb.0:
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.sub
; W32-NEXT: local.tee 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.lt_s
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_nabs:
; W64: .functype test_ctselect_nabs (i32) -> (i32)
; W64-NEXT: .local i32
; W64-NEXT: # %bb.0:
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.sub
; W64-NEXT: local.tee 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.lt_s
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%neg = sub i32 0, %x
%cmp = icmp slt i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 %x, i32 %neg)
ret i32 %result
}
; Test sign extension pattern
define i32 @test_ctselect_sign_extend(i32 %x) {
; W32-LABEL: test_ctselect_sign_extend:
; W32: .functype test_ctselect_sign_extend (i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.lt_s
; W32-NEXT: i32.sub
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_sign_extend:
; W64: .functype test_ctselect_sign_extend (i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.lt_s
; W64-NEXT: i32.sub
; W64-NEXT: # fallthrough-return
%cmp = icmp slt i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 -1, i32 0)
ret i32 %result
}
; Test zero extension pattern
define i32 @test_ctselect_zero_extend(i32 %x) {
; W32-LABEL: test_ctselect_zero_extend:
; W32: .functype test_ctselect_zero_extend (i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 0
; W32-NEXT: i32.ne
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_zero_extend:
; W64: .functype test_ctselect_zero_extend (i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 0
; W64-NEXT: i32.ne
; W64-NEXT: # fallthrough-return
%cmp = icmp ne i32 %x, 0
%result = call i32 @llvm.ct.select.i32(i1 %cmp, i32 1, i32 0)
ret i32 %result
}
; Test constant folding with known condition
define i32 @test_ctselect_constant_folding_true(i32 %a, i32 %b) {
; W32-LABEL: test_ctselect_constant_folding_true:
; W32: .functype test_ctselect_constant_folding_true (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.xor
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_constant_folding_true:
; W64: .functype test_ctselect_constant_folding_true (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.xor
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%result = call i32 @llvm.ct.select.i32(i1 true, i32 %a, i32 %b)
ret i32 %result
}
define i32 @test_ctselect_constant_folding_false(i32 %a, i32 %b) {
; W32-LABEL: test_ctselect_constant_folding_false:
; W32: .functype test_ctselect_constant_folding_false (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: i32.const 0
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_constant_folding_false:
; W64: .functype test_ctselect_constant_folding_false (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: i32.const 0
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%result = call i32 @llvm.ct.select.i32(i1 false, i32 %a, i32 %b)
ret i32 %result
}
; Test with identical operands
define i32 @test_ctselect_identical_operands(i1 %cond, i32 %x) {
; W32-LABEL: test_ctselect_identical_operands:
; W32: .functype test_ctselect_identical_operands (i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 1
; W32-NEXT: i32.const 0
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_identical_operands:
; W64: .functype test_ctselect_identical_operands (i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 1
; W64-NEXT: i32.const 0
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%result = call i32 @llvm.ct.select.i32(i1 %cond, i32 %x, i32 %x)
ret i32 %result
}
; Test with inverted condition
define i32 @test_ctselect_inverted_condition(i32 %x, i32 %y, i32 %a, i32 %b) {
; W32-LABEL: test_ctselect_inverted_condition:
; W32: .functype test_ctselect_inverted_condition (i32, i32, i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 2
; W32-NEXT: local.get 3
; W32-NEXT: local.get 2
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.eq
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_inverted_condition:
; W64: .functype test_ctselect_inverted_condition (i32, i32, i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 2
; W64-NEXT: local.get 3
; W64-NEXT: local.get 2
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.eq
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%cmp = icmp eq i32 %x, %y
%not_cmp = xor i1 %cmp, true
%result = call i32 @llvm.ct.select.i32(i1 %not_cmp, i32 %a, i32 %b)
ret i32 %result
}
; Test chain of ct.select operations
define i32 @test_ctselect_chain(i1 %c1, i1 %c2, i1 %c3, i32 %a, i32 %b, i32 %c, i32 %d) {
; W32-LABEL: test_ctselect_chain:
; W32: .functype test_ctselect_chain (i32, i32, i32, i32, i32, i32, i32) -> (i32)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 6
; W32-NEXT: local.get 5
; W32-NEXT: local.get 4
; W32-NEXT: local.get 3
; W32-NEXT: local.get 4
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 0
; W32-NEXT: i32.const 1
; W32-NEXT: i32.and
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: local.get 5
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 1
; W32-NEXT: i32.const 1
; W32-NEXT: i32.and
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: local.get 6
; W32-NEXT: i32.xor
; W32-NEXT: i32.const 0
; W32-NEXT: local.get 2
; W32-NEXT: i32.const 1
; W32-NEXT: i32.and
; W32-NEXT: i32.sub
; W32-NEXT: i32.and
; W32-NEXT: i32.xor
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_chain:
; W64: .functype test_ctselect_chain (i32, i32, i32, i32, i32, i32, i32) -> (i32)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 6
; W64-NEXT: local.get 5
; W64-NEXT: local.get 4
; W64-NEXT: local.get 3
; W64-NEXT: local.get 4
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 0
; W64-NEXT: i32.const 1
; W64-NEXT: i32.and
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: local.get 5
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 1
; W64-NEXT: i32.const 1
; W64-NEXT: i32.and
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: local.get 6
; W64-NEXT: i32.xor
; W64-NEXT: i32.const 0
; W64-NEXT: local.get 2
; W64-NEXT: i32.const 1
; W64-NEXT: i32.and
; W64-NEXT: i32.sub
; W64-NEXT: i32.and
; W64-NEXT: i32.xor
; W64-NEXT: # fallthrough-return
%sel1 = call i32 @llvm.ct.select.i32(i1 %c1, i32 %a, i32 %b)
%sel2 = call i32 @llvm.ct.select.i32(i1 %c2, i32 %sel1, i32 %c)
%sel3 = call i32 @llvm.ct.select.i32(i1 %c3, i32 %sel2, i32 %d)
ret i32 %sel3
}
; Test for 64-bit operations (supported on all 64-bit architectures)
define i64 @test_ctselect_i64_smin_zero(i64 %x) {
; W32-LABEL: test_ctselect_i64_smin_zero:
; W32: .functype test_ctselect_i64_smin_zero (i64) -> (i64)
; W32-NEXT: # %bb.0:
; W32-NEXT: local.get 0
; W32-NEXT: local.get 0
; W32-NEXT: i64.const 63
; W32-NEXT: i64.shr_s
; W32-NEXT: i64.and
; W32-NEXT: # fallthrough-return
;
; W64-LABEL: test_ctselect_i64_smin_zero:
; W64: .functype test_ctselect_i64_smin_zero (i64) -> (i64)
; W64-NEXT: # %bb.0:
; W64-NEXT: local.get 0
; W64-NEXT: local.get 0
; W64-NEXT: i64.const 63
; W64-NEXT: i64.shr_s
; W64-NEXT: i64.and
; W64-NEXT: # fallthrough-return
%cmp = icmp slt i64 %x, 0
%result = call i64 @llvm.ct.select.i64(i1 %cmp, i64 %x, i64 0)
ret i64 %result
}
; Declare the intrinsics
declare i32 @llvm.ct.select.i32(i1, i32, i32)
declare i64 @llvm.ct.select.i64(i1, i64, i64)