cranelift-interpreter: Implement a bunch of SIMD arithmetic ops (#5991)
* cranelift: Add function name to tests * cranelift: Move simd-ineg tests to separate file * cranelift: Move `avg_round` tests to separate file * cranelift: Move SIMD `fmin`/`fmax` tests to separate files * cranelift-interpreter: Implement a bunch of SIMD arithmetic ops Most of these are quite easy to adapt to be polymorphic * cranelift: Move shift tests from `simd-arithmetic.clif` into shift files
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
; the interpreter does not currently support some of these instructions
|
test interpret
|
||||||
; such as `avg_round` on SIMD values.
|
|
||||||
test run
|
test run
|
||||||
target aarch64
|
target aarch64
|
||||||
target s390x
|
target s390x
|
||||||
@@ -30,12 +29,6 @@ block0(v0: i32x4, v1: i32x4):
|
|||||||
}
|
}
|
||||||
; run: %isub_i32x4([1 1 1 1], [1 2 3 4]) == [0 -1 -2 -3]
|
; run: %isub_i32x4([1 1 1 1], [1 2 3 4]) == [0 -1 -2 -3]
|
||||||
|
|
||||||
function %ineg_i32x4(i32x4) -> i32x4 {
|
|
||||||
block0(v0: i32x4):
|
|
||||||
v1 = ineg v0
|
|
||||||
return v1
|
|
||||||
}
|
|
||||||
; run: %ineg_i32x4([1 1 1 1]) == [-1 -1 -1 -1]
|
|
||||||
|
|
||||||
function %imul_i64x2(i64x2, i64x2) -> i64x2 {
|
function %imul_i64x2(i64x2, i64x2) -> i64x2 {
|
||||||
block0(v0: i64x2, v1: i64x2):
|
block0(v0: i64x2, v1: i64x2):
|
||||||
@@ -105,7 +98,7 @@ block0:
|
|||||||
v9 = vall_true v8
|
v9 = vall_true v8
|
||||||
return v9
|
return v9
|
||||||
}
|
}
|
||||||
; run
|
; run: %add_sub_f32x4() == 1
|
||||||
|
|
||||||
function %mul_div_f32x4() -> i8 {
|
function %mul_div_f32x4() -> i8 {
|
||||||
block0:
|
block0:
|
||||||
@@ -123,7 +116,7 @@ block0:
|
|||||||
v9 = vall_true v8
|
v9 = vall_true v8
|
||||||
return v9
|
return v9
|
||||||
}
|
}
|
||||||
; run
|
; run: %mul_div_f32x4() == 1
|
||||||
|
|
||||||
function %sqrt_f64x2(f64x2) -> f64x2 {
|
function %sqrt_f64x2(f64x2) -> f64x2 {
|
||||||
block0(v0: f64x2):
|
block0(v0: f64x2):
|
||||||
@@ -132,28 +125,6 @@ block0(v0: f64x2):
|
|||||||
}
|
}
|
||||||
; run: %sqrt_f64x2([0x9.0 0x1.0]) == [0x3.0 0x1.0]
|
; run: %sqrt_f64x2([0x9.0 0x1.0]) == [0x3.0 0x1.0]
|
||||||
|
|
||||||
function %fmax_f64x2(f64x2, f64x2) -> f64x2 {
|
|
||||||
block0(v0: f64x2, v1: f64x2):
|
|
||||||
v2 = fmax v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; This operation exhibits non-deterministic behaviour for some input NaN values;
|
|
||||||
; refer to the simd-arithmetic-nondeterministic*.clif files for the respective tests.
|
|
||||||
; run: %fmax_f64x2([-0x0.0 -0x1.0], [+0x0.0 0x1.0]) == [+0x0.0 0x1.0]
|
|
||||||
; run: %fmax_f64x2([-NaN NaN], [0x0.0 0x100.0]) == [-NaN NaN]
|
|
||||||
; run: %fmax_f64x2([NaN 0.0], [0.0 0.0]) == [NaN 0.0]
|
|
||||||
; run: %fmax_f64x2([-NaN 0.0], [0x1.0 0.0]) == [-NaN 0.0]
|
|
||||||
|
|
||||||
function %fmin_f64x2(f64x2, f64x2) -> f64x2 {
|
|
||||||
block0(v0: f64x2, v1: f64x2):
|
|
||||||
v2 = fmin v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; This operation exhibits non-deterministic behaviour for some input NaN values;
|
|
||||||
; refer to the simd-arithmetic-nondeterministic*.clif files for the respective tests.
|
|
||||||
; run: %fmin_f64x2([-0x0.0 -0x1.0], [+0x0.0 0x1.0]) == [-0x0.0 -0x1.0]
|
|
||||||
; run: %fmin_f64x2([-NaN 0.0], [0x1.0 0.0]) == [-NaN 0.0]
|
|
||||||
|
|
||||||
function %fneg_f64x2(f64x2) -> f64x2 {
|
function %fneg_f64x2(f64x2) -> f64x2 {
|
||||||
block0(v0: f64x2):
|
block0(v0: f64x2):
|
||||||
v1 = fneg v0
|
v1 = fneg v0
|
||||||
@@ -175,128 +146,9 @@ block0(v0: f32x4):
|
|||||||
}
|
}
|
||||||
; run: %fabs_f32x4([0x0.0 -0x1.0 0x2.0 -0x3.0]) == [0x0.0 0x1.0 0x2.0 0x3.0]
|
; run: %fabs_f32x4([0x0.0 -0x1.0 0x2.0 -0x3.0]) == [0x0.0 0x1.0 0x2.0 0x3.0]
|
||||||
|
|
||||||
function %average_rounding_i8x16(i8x16, i8x16) -> i8x16 {
|
|
||||||
block0(v0: i8x16, v1: i8x16):
|
|
||||||
v2 = avg_round v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %average_rounding_i8x16([0 0 0 1 42 19 -1 0xff 5 0 0 0 1 42 19 -1], [0 1 2 4 42 18 -1 0 10 0 1 2 4 42 18 -1]) == [0 1 1 3 42 19 -1 0x80 8 0 1 1 3 42 19 -1]
|
|
||||||
|
|
||||||
function %average_rounding_i16x8(i16x8, i16x8) -> i16x8 {
|
|
||||||
block0(v0: i16x8, v1: i16x8):
|
|
||||||
v2 = avg_round v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %average_rounding_i16x8([0 0 0 1 42 19 -1 0xffff], [0 1 2 4 42 18 -1 0]) == [0 1 1 3 42 19 -1 0x8000]
|
|
||||||
|
|
||||||
function %iabs(i32x4) -> i32x4 {
|
function %iabs(i32x4) -> i32x4 {
|
||||||
block0(v0: i32x4):
|
block0(v0: i32x4):
|
||||||
v1 = iabs v0
|
v1 = iabs v0
|
||||||
return v1
|
return v1
|
||||||
}
|
}
|
||||||
; run: %iabs([-42 -1 0 1]) == [42 1 0 1]
|
; run: %iabs([-42 -1 0 1]) == [42 1 0 1]
|
||||||
|
|
||||||
function %i8x16_shl_imm(i8x16) -> i8x16 {
|
|
||||||
block0(v0: i8x16):
|
|
||||||
v1 = iconst.i32 2
|
|
||||||
v2 = ishl v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i8x16_shl_imm([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0 0 0]
|
|
||||||
|
|
||||||
function %i16x8_shl_imm(i16x8) -> i16x8 {
|
|
||||||
block0(v0: i16x8):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = ishl v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i16x8_shl_imm([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0x0010 0x0020 0x0040 0x0080 0x0100 0x0200 0x0400 0x0800]
|
|
||||||
; run: %i16x8_shl_imm([0x0100 0x0200 0x0400 0x0800 0x1000 0x2000 0x4000 0x8000]) == [0x1000 0x2000 0x4000 0x8000 0 0 0 0]
|
|
||||||
|
|
||||||
function %i32x4_shl_imm(i32x4) -> i32x4 {
|
|
||||||
block0(v0: i32x4):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = ishl v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i32x4_shl_imm([0x00000001 0x00000002 0x00000004 0x00000008]) == [0x00000010 0x00000020 0x00000040 0x00000080]
|
|
||||||
; run: %i32x4_shl_imm([0x10000000 0x00010000 0xf0000000 0x02000000]) == [0 0x00100000 0 0x20000000]
|
|
||||||
|
|
||||||
function %i64x2_shl_imm(i64x2) -> i64x2 {
|
|
||||||
block0(v0: i64x2):
|
|
||||||
v1 = iconst.i32 32
|
|
||||||
v2 = ishl v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i64x2_shl_imm([0x1 0xf]) == [0x100000000 0xf00000000]
|
|
||||||
; run: %i64x2_shl_imm([0x100000000 0]) == [0 0]
|
|
||||||
|
|
||||||
function %i8x16_sshr_imm(i8x16) -> i8x16 {
|
|
||||||
block0(v0: i8x16):
|
|
||||||
v1 = iconst.i32 2
|
|
||||||
v2 = sshr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i8x16_shl_imm([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0xe0 0 0 0 0 0 0 0 0]
|
|
||||||
|
|
||||||
function %i16x8_sshr_imm(i16x8) -> i16x8 {
|
|
||||||
block0(v0: i16x8):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = sshr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i16x8_sshr_imm([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0 0 0 0 0x1 0x2 0x4 0x8]
|
|
||||||
; run: %i16x8_sshr_imm([-1 -2 -4 -8 -16 16 0x8000 0x80f3]) == [-1 -1 -1 -1 -1 1 0xf800 0xf80f]
|
|
||||||
|
|
||||||
function %i32x4_sshr_imm(i32x4) -> i32x4 {
|
|
||||||
block0(v0: i32x4):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = sshr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i32x4_sshr_imm([1 0xfc 0x80000000 0xf83f3000]) == [0 0xf 0xf8000000 0xff83f300]
|
|
||||||
|
|
||||||
function %i64x2_sshr_imm(i64x2) -> i64x2 {
|
|
||||||
block0(v0: i64x2):
|
|
||||||
v1 = iconst.i32 32
|
|
||||||
v2 = sshr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i64x2_sshr_imm([0x1 0xf]) == [0 0]
|
|
||||||
; run: %i64x2_sshr_imm([0x100000000 0]) == [1 0]
|
|
||||||
; run: %i64x2_sshr_imm([-1 -1]) == [-1 -1]
|
|
||||||
|
|
||||||
function %i8x16_ushr_imm(i8x16) -> i8x16 {
|
|
||||||
block0(v0: i8x16):
|
|
||||||
v1 = iconst.i32 2
|
|
||||||
v2 = ushr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i8x16_shl_imm([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0x20 0 0 0 0 0 0 0 0]
|
|
||||||
|
|
||||||
function %i16x8_ushr_imm(i16x8) -> i16x8 {
|
|
||||||
block0(v0: i16x8):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = ushr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i16x8_ushr_imm([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0 0 0 0 0x1 0x2 0x4 0x8]
|
|
||||||
; run: %i16x8_ushr_imm([-1 -2 -4 -8 -16 16 0x8000 0x80f3]) == [0x0fff 0x0fff 0x0fff 0x0fff 0x0fff 1 0x0800 0x080f]
|
|
||||||
|
|
||||||
function %i32x4_ushr_imm(i32x4) -> i32x4 {
|
|
||||||
block0(v0: i32x4):
|
|
||||||
v1 = iconst.i32 4
|
|
||||||
v2 = ushr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i32x4_ushr_imm([1 0xfc 0x80000000 0xf83f3000]) == [0 0xf 0x08000000 0x0f83f300]
|
|
||||||
|
|
||||||
function %i64x2_ushr_imm(i64x2) -> i64x2 {
|
|
||||||
block0(v0: i64x2):
|
|
||||||
v1 = iconst.i32 32
|
|
||||||
v2 = ushr v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %i64x2_ushr_imm([0x1 0xf]) == [0 0]
|
|
||||||
; run: %i64x2_ushr_imm([0x100000000 0]) == [1 0]
|
|
||||||
; run: %i64x2_ushr_imm([-1 -1]) == [0xffffffff 0xffffffff]
|
|
||||||
|
|||||||
@@ -0,0 +1,52 @@
|
|||||||
|
; the interpreter does not currently support SIMD `avg_round`.
|
||||||
|
test run
|
||||||
|
target aarch64
|
||||||
|
; x86_64 and s390x do not currently support 64-bit vectors, or
|
||||||
|
; `avg_round` on `i64x2` values.
|
||||||
|
; x86_64 also does not currently support `avg_round.i32x4`.
|
||||||
|
|
||||||
|
function %average_rounding_i8x8(i8x8, i8x8) -> i8x8 {
|
||||||
|
block0(v0: i8x8, v1: i8x8):
|
||||||
|
v2 = avg_round v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %average_rounding_i8x8([0 0 0 1 42 19 -1 0xff], [0 1 2 4 42 18 -1 0]) == [0 1 1 3 42 19 -1 0x80]
|
||||||
|
|
||||||
|
function %average_rounding_i16x4(i16x4, i16x4) -> i16x4 {
|
||||||
|
block0(v0: i16x4, v1: i16x4):
|
||||||
|
v2 = avg_round v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %average_rounding_i16x4([0 0 0 1], [0 1 2 4]) == [0 1 1 3]
|
||||||
|
; run: %average_rounding_i16x4([42 19 -1 0xffff], [42 18 -1 0]) == [42 19 -1 0x8000]
|
||||||
|
|
||||||
|
function %average_rounding_i32x2(i32x2, i32x2) -> i32x2 {
|
||||||
|
block0(v0: i32x2, v1: i32x2):
|
||||||
|
v2 = avg_round v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %average_rounding_i32x2([0 0], [0 1]) == [0 1]
|
||||||
|
; run: %average_rounding_i32x2([0 1], [2 4]) == [1 3]
|
||||||
|
; run: %average_rounding_i32x2([42 19], [42 18]) == [42 19]
|
||||||
|
; run: %average_rounding_i32x2([-1 0xffffffff], [-1 0]) == [-1 0x80000000]
|
||||||
|
; run: %average_rounding_i32x2([0xffffffff 0xfffffffd], [10 0xffffffff]) == [0x80000005 0xfffffffe]
|
||||||
|
|
||||||
|
function %average_rounding_i32x4(i32x4, i32x4) -> i32x4 {
|
||||||
|
block0(v0: i32x4, v1: i32x4):
|
||||||
|
v2 = avg_round v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %average_rounding_i32x4([0 0 0 0xffffffff], [0 1 2 0]) == [0 1 1 0x80000000]
|
||||||
|
; run: %average_rounding_i32x4([1 42 19 -1], [4 42 18 -1]) == [3 42 19 -1]
|
||||||
|
|
||||||
|
function %average_rounding_i64x2(i64x2, i64x2) -> i64x2 {
|
||||||
|
block0(v0: i64x2, v1: i64x2):
|
||||||
|
v2 = avg_round v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %average_rounding_i64x2([0 0], [0 1]) == [0 1]
|
||||||
|
; run: %average_rounding_i64x2([0 1], [2 4]) == [1 3]
|
||||||
|
; run: %average_rounding_i64x2([42 19], [42 18]) == [42 19]
|
||||||
|
; run: %average_rounding_i64x2([-1 0xffffffffffffffff], [-1 0]) == [-1 0x8000000000000000]
|
||||||
|
; run: %average_rounding_i64x2([0xffffffffffffffff 0xfffffffffffffffd], [10 0xffffffffffffffff]) == [0x8000000000000005 0xfffffffffffffffe]
|
||||||
|
|
||||||
@@ -1,51 +1,20 @@
|
|||||||
; the interpreter does not currently support SIMD `avg_round`.
|
|
||||||
test run
|
test run
|
||||||
target aarch64
|
target aarch64
|
||||||
; x86_64 and s390x do not currently support 64-bit vectors, or
|
target s390x
|
||||||
; `avg_round` on `i64x2` values.
|
set enable_simd
|
||||||
; x86_64 also does not currently support `avg_round.i32x4`.
|
target x86_64
|
||||||
|
target x86_64 skylake
|
||||||
|
|
||||||
function %average_rounding_i8x8(i8x8, i8x8) -> i8x8 {
|
function %average_rounding_i8x16(i8x16, i8x16) -> i8x16 {
|
||||||
block0(v0: i8x8, v1: i8x8):
|
block0(v0: i8x16, v1: i8x16):
|
||||||
v2 = avg_round v0, v1
|
v2 = avg_round v0, v1
|
||||||
return v2
|
return v2
|
||||||
}
|
}
|
||||||
; run: %average_rounding_i8x8([0 0 0 1 42 19 -1 0xff], [0 1 2 4 42 18 -1 0]) == [0 1 1 3 42 19 -1 0x80]
|
; run: %average_rounding_i8x16([0 0 0 1 42 19 -1 0xff 5 0 0 0 1 42 19 -1], [0 1 2 4 42 18 -1 0 10 0 1 2 4 42 18 -1]) == [0 1 1 3 42 19 -1 0x80 8 0 1 1 3 42 19 -1]
|
||||||
|
|
||||||
function %average_rounding_i16x4(i16x4, i16x4) -> i16x4 {
|
function %average_rounding_i16x8(i16x8, i16x8) -> i16x8 {
|
||||||
block0(v0: i16x4, v1: i16x4):
|
block0(v0: i16x8, v1: i16x8):
|
||||||
v2 = avg_round v0, v1
|
v2 = avg_round v0, v1
|
||||||
return v2
|
return v2
|
||||||
}
|
}
|
||||||
; run: %average_rounding_i16x4([0 0 0 1], [0 1 2 4]) == [0 1 1 3]
|
; run: %average_rounding_i16x8([0 0 0 1 42 19 -1 0xffff], [0 1 2 4 42 18 -1 0]) == [0 1 1 3 42 19 -1 0x8000]
|
||||||
; run: %average_rounding_i16x4([42 19 -1 0xffff], [42 18 -1 0]) == [42 19 -1 0x8000]
|
|
||||||
|
|
||||||
function %average_rounding_i32x2(i32x2, i32x2) -> i32x2 {
|
|
||||||
block0(v0: i32x2, v1: i32x2):
|
|
||||||
v2 = avg_round v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %average_rounding_i32x2([0 0], [0 1]) == [0 1]
|
|
||||||
; run: %average_rounding_i32x2([0 1], [2 4]) == [1 3]
|
|
||||||
; run: %average_rounding_i32x2([42 19], [42 18]) == [42 19]
|
|
||||||
; run: %average_rounding_i32x2([-1 0xffffffff], [-1 0]) == [-1 0x80000000]
|
|
||||||
; run: %average_rounding_i32x2([0xffffffff 0xfffffffd], [10 0xffffffff]) == [0x80000005 0xfffffffe]
|
|
||||||
|
|
||||||
function %average_rounding_i32x4(i32x4, i32x4) -> i32x4 {
|
|
||||||
block0(v0: i32x4, v1: i32x4):
|
|
||||||
v2 = avg_round v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %average_rounding_i32x4([0 0 0 0xffffffff], [0 1 2 0]) == [0 1 1 0x80000000]
|
|
||||||
; run: %average_rounding_i32x4([1 42 19 -1], [4 42 18 -1]) == [3 42 19 -1]
|
|
||||||
|
|
||||||
function %average_rounding_i64x2(i64x2, i64x2) -> i64x2 {
|
|
||||||
block0(v0: i64x2, v1: i64x2):
|
|
||||||
v2 = avg_round v0, v1
|
|
||||||
return v2
|
|
||||||
}
|
|
||||||
; run: %average_rounding_i64x2([0 0], [0 1]) == [0 1]
|
|
||||||
; run: %average_rounding_i64x2([0 1], [2 4]) == [1 3]
|
|
||||||
; run: %average_rounding_i64x2([42 19], [42 18]) == [42 19]
|
|
||||||
; run: %average_rounding_i64x2([-1 0xffffffffffffffff], [-1 0]) == [-1 0x8000000000000000]
|
|
||||||
; run: %average_rounding_i64x2([0xffffffffffffffff 0xfffffffffffffffd], [10 0xffffffffffffffff]) == [0x8000000000000005 0xfffffffffffffffe]
|
|
||||||
|
|||||||
28
cranelift/filetests/filetests/runtests/simd-fmax-fmin.clif
Normal file
28
cranelift/filetests/filetests/runtests/simd-fmax-fmin.clif
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
test run
|
||||||
|
target aarch64
|
||||||
|
target s390x
|
||||||
|
set enable_simd
|
||||||
|
target x86_64
|
||||||
|
target x86_64 skylake
|
||||||
|
|
||||||
|
function %fmax_f64x2(f64x2, f64x2) -> f64x2 {
|
||||||
|
block0(v0: f64x2, v1: f64x2):
|
||||||
|
v2 = fmax v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; This operation exhibits non-deterministic behaviour for some input NaN values;
|
||||||
|
; refer to the simd-fmax-fmin-nondeterministic*.clif files for the respective tests.
|
||||||
|
; run: %fmax_f64x2([-0x0.0 -0x1.0], [+0x0.0 0x1.0]) == [+0x0.0 0x1.0]
|
||||||
|
; run: %fmax_f64x2([-NaN NaN], [0x0.0 0x100.0]) == [-NaN NaN]
|
||||||
|
; run: %fmax_f64x2([NaN 0.0], [0.0 0.0]) == [NaN 0.0]
|
||||||
|
; run: %fmax_f64x2([-NaN 0.0], [0x1.0 0.0]) == [-NaN 0.0]
|
||||||
|
|
||||||
|
function %fmin_f64x2(f64x2, f64x2) -> f64x2 {
|
||||||
|
block0(v0: f64x2, v1: f64x2):
|
||||||
|
v2 = fmin v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; This operation exhibits non-deterministic behaviour for some input NaN values;
|
||||||
|
; refer to the simd-fmax-fmin-nondeterministic*.clif files for the respective tests.
|
||||||
|
; run: %fmin_f64x2([-0x0.0 -0x1.0], [+0x0.0 0x1.0]) == [-0x0.0 -0x1.0]
|
||||||
|
; run: %fmin_f64x2([-NaN 0.0], [0x1.0 0.0]) == [-NaN 0.0]
|
||||||
13
cranelift/filetests/filetests/runtests/simd-ineg.clif
Normal file
13
cranelift/filetests/filetests/runtests/simd-ineg.clif
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
test run
|
||||||
|
target aarch64
|
||||||
|
target s390x
|
||||||
|
set enable_simd
|
||||||
|
target x86_64
|
||||||
|
target x86_64 skylake
|
||||||
|
|
||||||
|
function %ineg_i32x4(i32x4) -> i32x4 {
|
||||||
|
block0(v0: i32x4):
|
||||||
|
v1 = ineg v0
|
||||||
|
return v1
|
||||||
|
}
|
||||||
|
; run: %ineg_i32x4([1 1 1 1]) == [-1 -1 -1 -1]
|
||||||
@@ -44,3 +44,39 @@ block0(v0: i64x2):
|
|||||||
return v2
|
return v2
|
||||||
}
|
}
|
||||||
; run: %ishl_imm_i64x2([1 0]) == [2 0]
|
; run: %ishl_imm_i64x2([1 0]) == [2 0]
|
||||||
|
|
||||||
|
|
||||||
|
function %i8x16_shl_const(i8x16) -> i8x16 {
|
||||||
|
block0(v0: i8x16):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = ishl v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i8x16_shl_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0 0 0]
|
||||||
|
|
||||||
|
function %i16x8_shl_const(i16x8) -> i16x8 {
|
||||||
|
block0(v0: i16x8):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = ishl v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i16x8_shl_const([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0x0010 0x0020 0x0040 0x0080 0x0100 0x0200 0x0400 0x0800]
|
||||||
|
; run: %i16x8_shl_const([0x0100 0x0200 0x0400 0x0800 0x1000 0x2000 0x4000 0x8000]) == [0x1000 0x2000 0x4000 0x8000 0 0 0 0]
|
||||||
|
|
||||||
|
function %i32x4_shl_const(i32x4) -> i32x4 {
|
||||||
|
block0(v0: i32x4):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = ishl v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i32x4_shl_const([0x00000001 0x00000002 0x00000004 0x00000008]) == [0x00000010 0x00000020 0x00000040 0x00000080]
|
||||||
|
; run: %i32x4_shl_const([0x10000000 0x00010000 0xf0000000 0x02000000]) == [0 0x00100000 0 0x20000000]
|
||||||
|
|
||||||
|
function %i64x2_shl_const(i64x2) -> i64x2 {
|
||||||
|
block0(v0: i64x2):
|
||||||
|
v1 = iconst.i32 32
|
||||||
|
v2 = ishl v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i64x2_shl_const([0x1 0xf]) == [0x100000000 0xf00000000]
|
||||||
|
; run: %i64x2_shl_const([0x100000000 0]) == [0 0]
|
||||||
|
|||||||
@@ -56,3 +56,39 @@ block0(v0: i16x8):
|
|||||||
return v1
|
return v1
|
||||||
}
|
}
|
||||||
; run: %sshr_imm_i16x8([1 2 4 -8 0 0 0 0]) == [0 1 2 -4 0 0 0 0]
|
; run: %sshr_imm_i16x8([1 2 4 -8 0 0 0 0]) == [0 1 2 -4 0 0 0 0]
|
||||||
|
|
||||||
|
|
||||||
|
function %i8x16_sshr_const(i8x16) -> i8x16 {
|
||||||
|
block0(v0: i8x16):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = sshr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i8x16_shl_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0xe0 0 0 0 0 0 0 0 0]
|
||||||
|
|
||||||
|
function %i16x8_sshr_const(i16x8) -> i16x8 {
|
||||||
|
block0(v0: i16x8):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = sshr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i16x8_sshr_const([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0 0 0 0 0x1 0x2 0x4 0x8]
|
||||||
|
; run: %i16x8_sshr_const([-1 -2 -4 -8 -16 16 0x8000 0x80f3]) == [-1 -1 -1 -1 -1 1 0xf800 0xf80f]
|
||||||
|
|
||||||
|
function %i32x4_sshr_const(i32x4) -> i32x4 {
|
||||||
|
block0(v0: i32x4):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = sshr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i32x4_sshr_const([1 0xfc 0x80000000 0xf83f3000]) == [0 0xf 0xf8000000 0xff83f300]
|
||||||
|
|
||||||
|
function %i64x2_sshr_const(i64x2) -> i64x2 {
|
||||||
|
block0(v0: i64x2):
|
||||||
|
v1 = iconst.i32 32
|
||||||
|
v2 = sshr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i64x2_sshr_const([0x1 0xf]) == [0 0]
|
||||||
|
; run: %i64x2_sshr_const([0x100000000 0]) == [1 0]
|
||||||
|
; run: %i64x2_sshr_const([-1 -1]) == [-1 -1]
|
||||||
|
|||||||
@@ -50,3 +50,39 @@ block0:
|
|||||||
return v5
|
return v5
|
||||||
}
|
}
|
||||||
; run
|
; run
|
||||||
|
|
||||||
|
|
||||||
|
function %i8x16_ushr_const(i8x16) -> i8x16 {
|
||||||
|
block0(v0: i8x16):
|
||||||
|
v1 = iconst.i32 2
|
||||||
|
v2 = ushr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i8x16_shl_const([0x01 0x02 0x04 0x08 0x10 0x20 0x40 0x80 0 0 0 0 0 0 0 0]) == [0 0 0x01 0x02 0x04 0x08 0x10 0x20 0 0 0 0 0 0 0 0]
|
||||||
|
|
||||||
|
function %i16x8_ushr_const(i16x8) -> i16x8 {
|
||||||
|
block0(v0: i16x8):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = ushr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i16x8_ushr_const([0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 0x0080]) == [0 0 0 0 0x1 0x2 0x4 0x8]
|
||||||
|
; run: %i16x8_ushr_const([-1 -2 -4 -8 -16 16 0x8000 0x80f3]) == [0x0fff 0x0fff 0x0fff 0x0fff 0x0fff 1 0x0800 0x080f]
|
||||||
|
|
||||||
|
function %i32x4_ushr_const(i32x4) -> i32x4 {
|
||||||
|
block0(v0: i32x4):
|
||||||
|
v1 = iconst.i32 4
|
||||||
|
v2 = ushr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i32x4_ushr_const([1 0xfc 0x80000000 0xf83f3000]) == [0 0xf 0x08000000 0x0f83f300]
|
||||||
|
|
||||||
|
function %i64x2_ushr_const(i64x2) -> i64x2 {
|
||||||
|
block0(v0: i64x2):
|
||||||
|
v1 = iconst.i32 32
|
||||||
|
v2 = ushr v0, v1
|
||||||
|
return v2
|
||||||
|
}
|
||||||
|
; run: %i64x2_ushr_const([0x1 0xf]) == [0 0]
|
||||||
|
; run: %i64x2_ushr_const([0x100000000 0]) == [1 0]
|
||||||
|
; run: %i64x2_ushr_const([-1 -1]) == [0xffffffff 0xffffffff]
|
||||||
|
|||||||
@@ -199,45 +199,51 @@ where
|
|||||||
Ok(sum(imm, args)? as u64)
|
Ok(sum(imm, args)? as u64)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Interpret a unary instruction with the given `op`, assigning the resulting value to the
|
||||||
|
// instruction's results.
|
||||||
|
let unary = |op: fn(V) -> ValueResult<V>, arg: V| -> ValueResult<ControlFlow<V>> {
|
||||||
|
let ctrl_ty = inst_context.controlling_type().unwrap();
|
||||||
|
let res = unary_arith(arg, ctrl_ty, op, false)?;
|
||||||
|
Ok(assign(res))
|
||||||
|
};
|
||||||
|
|
||||||
// Interpret a binary instruction with the given `op`, assigning the resulting value to the
|
// Interpret a binary instruction with the given `op`, assigning the resulting value to the
|
||||||
// instruction's results.
|
// instruction's results.
|
||||||
let binary = |op: fn(V, V) -> ValueResult<V>,
|
let binary =
|
||||||
left: V,
|
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
||||||
right: V|
|
let ctrl_ty = inst_context.controlling_type().unwrap();
|
||||||
-> ValueResult<ControlFlow<V>> { Ok(assign(op(left, right)?)) };
|
let res = binary_arith(left, right, ctrl_ty, op, false)?;
|
||||||
|
Ok(assign(res))
|
||||||
|
};
|
||||||
|
|
||||||
// Same as `binary_unsigned`, but converts the values to their unsigned form before the
|
// Same as `binary_unsigned`, but converts the values to their unsigned form before the
|
||||||
// operation and back to signed form afterwards. Since Cranelift types have no notion of
|
// operation and back to signed form afterwards. Since Cranelift types have no notion of
|
||||||
// signedness, this enables operations that depend on sign.
|
// signedness, this enables operations that depend on sign.
|
||||||
let binary_unsigned =
|
let binary_unsigned =
|
||||||
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
||||||
Ok(assign(
|
let ctrl_ty = inst_context.controlling_type().unwrap();
|
||||||
op(
|
let res = binary_arith(left, right, ctrl_ty, op, true)
|
||||||
left.convert(ValueConversionKind::ToUnsigned)?,
|
.and_then(|v| v.convert(ValueConversionKind::ToSigned))?;
|
||||||
right.convert(ValueConversionKind::ToUnsigned)?,
|
Ok(assign(res))
|
||||||
)
|
|
||||||
.and_then(|v| v.convert(ValueConversionKind::ToSigned))?,
|
|
||||||
))
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Similar to `binary` but converts select `ValueError`'s into trap `ControlFlow`'s
|
// Similar to `binary` but converts select `ValueError`'s into trap `ControlFlow`'s
|
||||||
let binary_can_trap = |op: fn(V, V) -> ValueResult<V>,
|
let binary_can_trap =
|
||||||
left: V,
|
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
||||||
right: V|
|
let ctrl_ty = inst_context.controlling_type().unwrap();
|
||||||
-> ValueResult<ControlFlow<V>> { assign_or_trap(op(left, right)) };
|
let res = binary_arith(left, right, ctrl_ty, op, false);
|
||||||
|
assign_or_trap(res)
|
||||||
|
};
|
||||||
|
|
||||||
// Same as `binary_can_trap`, but converts the values to their unsigned form before the
|
// Same as `binary_can_trap`, but converts the values to their unsigned form before the
|
||||||
// operation and back to signed form afterwards. Since Cranelift types have no notion of
|
// operation and back to signed form afterwards. Since Cranelift types have no notion of
|
||||||
// signedness, this enables operations that depend on sign.
|
// signedness, this enables operations that depend on sign.
|
||||||
let binary_unsigned_can_trap =
|
let binary_unsigned_can_trap =
|
||||||
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
|op: fn(V, V) -> ValueResult<V>, left: V, right: V| -> ValueResult<ControlFlow<V>> {
|
||||||
assign_or_trap(
|
let ctrl_ty = inst_context.controlling_type().unwrap();
|
||||||
op(
|
let res = binary_arith(left, right, ctrl_ty, op, true)
|
||||||
left.convert(ValueConversionKind::ToUnsigned)?,
|
.and_then(|v| v.convert(ValueConversionKind::ToSigned));
|
||||||
right.convert(ValueConversionKind::ToUnsigned)?,
|
assign_or_trap(res)
|
||||||
)
|
|
||||||
.and_then(|v| v.convert(ValueConversionKind::ToSigned)),
|
|
||||||
)
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Choose whether to assign `left` or `right` to the instruction's result based on a `condition`.
|
// Choose whether to assign `left` or `right` to the instruction's result based on a `condition`.
|
||||||
@@ -811,7 +817,7 @@ where
|
|||||||
Opcode::Band => binary(Value::and, arg(0)?, arg(1)?)?,
|
Opcode::Band => binary(Value::and, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Bor => binary(Value::or, arg(0)?, arg(1)?)?,
|
Opcode::Bor => binary(Value::or, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Bxor => binary(Value::xor, arg(0)?, arg(1)?)?,
|
Opcode::Bxor => binary(Value::xor, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Bnot => assign(Value::not(arg(0)?)?),
|
Opcode::Bnot => unary(Value::not, arg(0)?)?,
|
||||||
Opcode::BandNot => binary(Value::and, arg(0)?, Value::not(arg(1)?)?)?,
|
Opcode::BandNot => binary(Value::and, arg(0)?, Value::not(arg(1)?)?)?,
|
||||||
Opcode::BorNot => binary(Value::or, arg(0)?, Value::not(arg(1)?)?)?,
|
Opcode::BorNot => binary(Value::or, arg(0)?, Value::not(arg(1)?)?)?,
|
||||||
Opcode::BxorNot => binary(Value::xor, arg(0)?, Value::not(arg(1)?)?)?,
|
Opcode::BxorNot => binary(Value::xor, arg(0)?, Value::not(arg(1)?)?)?,
|
||||||
@@ -828,9 +834,9 @@ where
|
|||||||
Opcode::IshlImm => binary(Value::shl, arg(0)?, imm_as_ctrl_ty()?)?,
|
Opcode::IshlImm => binary(Value::shl, arg(0)?, imm_as_ctrl_ty()?)?,
|
||||||
Opcode::UshrImm => binary_unsigned(Value::ushr, arg(0)?, imm_as_ctrl_ty()?)?,
|
Opcode::UshrImm => binary_unsigned(Value::ushr, arg(0)?, imm_as_ctrl_ty()?)?,
|
||||||
Opcode::SshrImm => binary(Value::ishr, arg(0)?, imm_as_ctrl_ty()?)?,
|
Opcode::SshrImm => binary(Value::ishr, arg(0)?, imm_as_ctrl_ty()?)?,
|
||||||
Opcode::Bitrev => assign(Value::reverse_bits(arg(0)?)?),
|
Opcode::Bitrev => unary(Value::reverse_bits, arg(0)?)?,
|
||||||
Opcode::Bswap => assign(Value::swap_bytes(arg(0)?)?),
|
Opcode::Bswap => unary(Value::swap_bytes, arg(0)?)?,
|
||||||
Opcode::Clz => assign(arg(0)?.leading_zeros()?),
|
Opcode::Clz => unary(Value::leading_zeros, arg(0)?)?,
|
||||||
Opcode::Cls => {
|
Opcode::Cls => {
|
||||||
let count = if Value::lt(&arg(0)?, &Value::int(0, ctrl_ty)?)? {
|
let count = if Value::lt(&arg(0)?, &Value::int(0, ctrl_ty)?)? {
|
||||||
arg(0)?.leading_ones()?
|
arg(0)?.leading_ones()?
|
||||||
@@ -839,7 +845,7 @@ where
|
|||||||
};
|
};
|
||||||
assign(Value::sub(count, Value::int(1, ctrl_ty)?)?)
|
assign(Value::sub(count, Value::int(1, ctrl_ty)?)?)
|
||||||
}
|
}
|
||||||
Opcode::Ctz => assign(arg(0)?.trailing_zeros()?),
|
Opcode::Ctz => unary(Value::trailing_zeros, arg(0)?)?,
|
||||||
Opcode::Popcnt => {
|
Opcode::Popcnt => {
|
||||||
let count = if arg(0)?.ty().is_int() {
|
let count = if arg(0)?.ty().is_int() {
|
||||||
arg(0)?.count_ones()?
|
arg(0)?.count_ones()?
|
||||||
@@ -876,7 +882,7 @@ where
|
|||||||
Opcode::Fsub => binary(Value::sub, arg(0)?, arg(1)?)?,
|
Opcode::Fsub => binary(Value::sub, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Fmul => binary(Value::mul, arg(0)?, arg(1)?)?,
|
Opcode::Fmul => binary(Value::mul, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Fdiv => binary(Value::div, arg(0)?, arg(1)?)?,
|
Opcode::Fdiv => binary(Value::div, arg(0)?, arg(1)?)?,
|
||||||
Opcode::Sqrt => assign(Value::sqrt(arg(0)?)?),
|
Opcode::Sqrt => unary(Value::sqrt, arg(0)?)?,
|
||||||
Opcode::Fma => {
|
Opcode::Fma => {
|
||||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
||||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
||||||
@@ -892,21 +898,9 @@ where
|
|||||||
ctrl_ty,
|
ctrl_ty,
|
||||||
)?)
|
)?)
|
||||||
}
|
}
|
||||||
Opcode::Fneg => assign(Value::neg(arg(0)?)?),
|
Opcode::Fneg => unary(Value::neg, arg(0)?)?,
|
||||||
Opcode::Fabs => assign(Value::abs(arg(0)?)?),
|
Opcode::Fabs => unary(Value::abs, arg(0)?)?,
|
||||||
Opcode::Fcopysign => {
|
Opcode::Fcopysign => binary(Value::copysign, arg(0)?, arg(1)?)?,
|
||||||
let arg0 = extractlanes(&arg(0)?, ctrl_ty)?;
|
|
||||||
let arg1 = extractlanes(&arg(1)?, ctrl_ty)?;
|
|
||||||
|
|
||||||
assign(vectorizelanes(
|
|
||||||
&arg0
|
|
||||||
.into_iter()
|
|
||||||
.zip(arg1.into_iter())
|
|
||||||
.map(|(x, y)| V::copysign(x, y))
|
|
||||||
.collect::<ValueResult<SimdVec<V>>>()?,
|
|
||||||
ctrl_ty,
|
|
||||||
)?)
|
|
||||||
}
|
|
||||||
Opcode::Fmin => assign(match (arg(0)?, arg(1)?) {
|
Opcode::Fmin => assign(match (arg(0)?, arg(1)?) {
|
||||||
(a, _) if a.is_nan()? => a,
|
(a, _) if a.is_nan()? => a,
|
||||||
(_, b) if b.is_nan()? => b,
|
(_, b) if b.is_nan()? => b,
|
||||||
@@ -931,10 +925,10 @@ where
|
|||||||
(a, b) if a.is_zero()? && b.is_zero()? => a,
|
(a, b) if a.is_zero()? && b.is_zero()? => a,
|
||||||
(a, b) => a.max(b)?,
|
(a, b) => a.max(b)?,
|
||||||
}),
|
}),
|
||||||
Opcode::Ceil => assign(Value::ceil(arg(0)?)?),
|
Opcode::Ceil => unary(Value::ceil, arg(0)?)?,
|
||||||
Opcode::Floor => assign(Value::floor(arg(0)?)?),
|
Opcode::Floor => unary(Value::floor, arg(0)?)?,
|
||||||
Opcode::Trunc => assign(Value::trunc(arg(0)?)?),
|
Opcode::Trunc => unary(Value::trunc, arg(0)?)?,
|
||||||
Opcode::Nearest => assign(Value::nearest(arg(0)?)?),
|
Opcode::Nearest => unary(Value::nearest, arg(0)?)?,
|
||||||
Opcode::IsNull => unimplemented!("IsNull"),
|
Opcode::IsNull => unimplemented!("IsNull"),
|
||||||
Opcode::IsInvalid => unimplemented!("IsInvalid"),
|
Opcode::IsInvalid => unimplemented!("IsInvalid"),
|
||||||
Opcode::Bitcast | Opcode::ScalarToVector => {
|
Opcode::Bitcast | Opcode::ScalarToVector => {
|
||||||
@@ -1592,7 +1586,28 @@ where
|
|||||||
extractlanes(&v, ty)?.into_iter().try_fold(init, op)
|
extractlanes(&v, ty)?.into_iter().try_fold(init, op)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Performs the supplied binary arithmetic `op` on two SIMD vectors.
|
/// Performs the supplied unary arithmetic `op` on a Value, either Vector or Scalar.
|
||||||
|
fn unary_arith<V, F>(x: V, vector_type: types::Type, op: F, unsigned: bool) -> ValueResult<V>
|
||||||
|
where
|
||||||
|
V: Value,
|
||||||
|
F: Fn(V) -> ValueResult<V>,
|
||||||
|
{
|
||||||
|
let arg = extractlanes(&x, vector_type)?;
|
||||||
|
|
||||||
|
let result = arg
|
||||||
|
.into_iter()
|
||||||
|
.map(|mut arg| {
|
||||||
|
if unsigned {
|
||||||
|
arg = arg.convert(ValueConversionKind::ToUnsigned)?;
|
||||||
|
}
|
||||||
|
Ok(op(arg)?)
|
||||||
|
})
|
||||||
|
.collect::<ValueResult<SimdVec<V>>>()?;
|
||||||
|
|
||||||
|
vectorizelanes(&result, vector_type)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the supplied binary arithmetic `op` on two values, either vector or scalar.
|
||||||
fn binary_arith<V, F>(x: V, y: V, vector_type: types::Type, op: F, unsigned: bool) -> ValueResult<V>
|
fn binary_arith<V, F>(x: V, y: V, vector_type: types::Type, op: F, unsigned: bool) -> ValueResult<V>
|
||||||
where
|
where
|
||||||
V: Value,
|
V: Value,
|
||||||
|
|||||||
Reference in New Issue
Block a user