Selects of the form `cond ? 1 : 0` are created during unrolling of setcc+vselect. Currently these are not optimized away post-legalization even if fully redundant. Having these extra selects sitting between things can prevent other folds from applying. Enabling this requires some mitigations in the ARM backend, in particular in the interaction with MVE support. There's two changes here: * Form CSINV/CSNEG/CSINC from CMOV, rather than only creating it during SELECT_CC lowering. (After this change, the lowering in SELECT_CC can be dropped without test changes, let me know if I should do that.) * Support pushing negations through CMOV in more cases, in particular if the operands are constant or the negation can be handled by flipping lshr/ashr. Additionally, in the X86 backend, try to simplify CMOV to SETCC if only the low bit is demanded.
142 lines
4.4 KiB
LLVM
142 lines
4.4 KiB
LLVM
; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py UTC_ARGS: --version 6
|
|
; RUN: llc < %s | FileCheck %s
|
|
|
|
target triple = "wasm32-unknown-unknown"
|
|
|
|
define i32 @extract_last_active_v4i32(<4 x i32> %a, <4 x i1> %c) {
|
|
; CHECK-LABEL: extract_last_active_v4i32:
|
|
; CHECK: .functype extract_last_active_v4i32 (i32, i32, i32, i32, i32, i32, i32, i32) -> (i32)
|
|
; CHECK-NEXT: .local i32
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get __stack_pointer
|
|
; CHECK-NEXT: i32.const 16
|
|
; CHECK-NEXT: i32.sub
|
|
; CHECK-NEXT: local.tee 8
|
|
; CHECK-NEXT: local.get 3
|
|
; CHECK-NEXT: i32.store 12
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 2
|
|
; CHECK-NEXT: i32.store 8
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 1
|
|
; CHECK-NEXT: i32.store 4
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: i32.store 0
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 7
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.const 2
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: local.get 5
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: local.get 6
|
|
; CHECK-NEXT: local.get 7
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.select
|
|
; CHECK-NEXT: i32.const 2
|
|
; CHECK-NEXT: i32.shl
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.load 0
|
|
; CHECK-NEXT: i32.const -1
|
|
; CHECK-NEXT: local.get 4
|
|
; CHECK-NEXT: local.get 6
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: local.get 5
|
|
; CHECK-NEXT: local.get 7
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.select
|
|
; CHECK-NEXT: # fallthrough-return
|
|
%res = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> %a, <4 x i1> %c, i32 -1)
|
|
ret i32 %res
|
|
}
|
|
|
|
define i32 @extract_last_active_v4i32_no_default(<4 x i32> %a, <4 x i1> %c) {
|
|
; CHECK-LABEL: extract_last_active_v4i32_no_default:
|
|
; CHECK: .functype extract_last_active_v4i32_no_default (i32, i32, i32, i32, i32, i32, i32, i32) -> (i32)
|
|
; CHECK-NEXT: .local i32
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get __stack_pointer
|
|
; CHECK-NEXT: i32.const 16
|
|
; CHECK-NEXT: i32.sub
|
|
; CHECK-NEXT: local.tee 8
|
|
; CHECK-NEXT: local.get 3
|
|
; CHECK-NEXT: i32.store 12
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 2
|
|
; CHECK-NEXT: i32.store 8
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 1
|
|
; CHECK-NEXT: i32.store 4
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: i32.store 0
|
|
; CHECK-NEXT: local.get 8
|
|
; CHECK-NEXT: local.get 7
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.const 2
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: local.get 5
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: local.get 6
|
|
; CHECK-NEXT: local.get 7
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.select
|
|
; CHECK-NEXT: i32.const 2
|
|
; CHECK-NEXT: i32.shl
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.load 0
|
|
; CHECK-NEXT: # fallthrough-return
|
|
%res = call i32 @llvm.experimental.vector.extract.last.active.v4i32(<4 x i32> %a, <4 x i1> %c, i32 poison)
|
|
ret i32 %res
|
|
}
|
|
|
|
; Test v2i32 - smaller vector.
|
|
define i32 @extract_last_active_v2i32(<2 x i32> %a, <2 x i1> %c) {
|
|
; CHECK-LABEL: extract_last_active_v2i32:
|
|
; CHECK: .functype extract_last_active_v2i32 (i32, i32, i32, i32) -> (i32)
|
|
; CHECK-NEXT: .local i32
|
|
; CHECK-NEXT: # %bb.0:
|
|
; CHECK-NEXT: global.get __stack_pointer
|
|
; CHECK-NEXT: i32.const 16
|
|
; CHECK-NEXT: i32.sub
|
|
; CHECK-NEXT: local.tee 4
|
|
; CHECK-NEXT: local.get 1
|
|
; CHECK-NEXT: i32.store 12
|
|
; CHECK-NEXT: local.get 4
|
|
; CHECK-NEXT: local.get 0
|
|
; CHECK-NEXT: i32.store 8
|
|
; CHECK-NEXT: local.get 4
|
|
; CHECK-NEXT: i32.const 8
|
|
; CHECK-NEXT: i32.add
|
|
; CHECK-NEXT: i32.const 4
|
|
; CHECK-NEXT: i32.const 0
|
|
; CHECK-NEXT: local.get 3
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.select
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.load 0
|
|
; CHECK-NEXT: i32.const -1
|
|
; CHECK-NEXT: local.get 2
|
|
; CHECK-NEXT: local.get 3
|
|
; CHECK-NEXT: i32.or
|
|
; CHECK-NEXT: i32.const 1
|
|
; CHECK-NEXT: i32.and
|
|
; CHECK-NEXT: i32.select
|
|
; CHECK-NEXT: # fallthrough-return
|
|
%res = call i32 @llvm.experimental.vector.extract.last.active.v2i32(<2 x i32> %a, <2 x i1> %c, i32 -1)
|
|
ret i32 %res
|
|
}
|