mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-18 03:12:10 +00:00
deinterleave -> shuffle
This commit is contained in:
@@ -1601,7 +1601,6 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
|
||||
case BuiltinProc_simd_deinterleave:
|
||||
{
|
||||
lbValue arg0 = lb_build_expr(p, ce->args[0]);
|
||||
LLVMTypeRef types[1] = {lb_type(m, arg0.type)};
|
||||
|
||||
GB_ASSERT(ce->args[1]->tav.value.kind == ExactValue_Integer);
|
||||
int n = cast(int)exact_value_to_i64(ce->args[1]->tav.value);
|
||||
@@ -1611,11 +1610,27 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn
|
||||
return res;
|
||||
}
|
||||
|
||||
gbString name = gb_string_make(heap_allocator(), "");
|
||||
name = gb_string_append_fmt(name, "llvm.vector.deinterleave%d", n);
|
||||
defer (gb_string_free(name));
|
||||
// `llvm.vector.deinterleave<N>` for N > 2 cannot be selected or legalized on most
|
||||
// targets, only arm64 takes it. The split is done with shuffles, same as
|
||||
// `simd_interleave`. Output `j` is the input strided by N starting at lane `j`.
|
||||
LLVMTypeRef llvm_u32 = lb_type(m, t_u32);
|
||||
LLVMTypeRef vector_type = LLVMTypeOf(arg0.value);
|
||||
LLVMValueRef undef = LLVMGetUndef(vector_type);
|
||||
|
||||
res.value = lb_call_intrinsic(p, name, &arg0.value, 1, types, gb_count_of(types));
|
||||
unsigned width = LLVMGetVectorSize(vector_type);
|
||||
unsigned part = width/cast(unsigned)n;
|
||||
|
||||
LLVMValueRef agg = LLVMGetUndef(lb_type(m, tv.type));
|
||||
LLVMValueRef *mask = temporary_alloc_array<LLVMValueRef>(part);
|
||||
for (int j = 0; j < n; j++) {
|
||||
for (unsigned i = 0; i < part; i++) {
|
||||
mask[i] = LLVMConstInt(llvm_u32, i*cast(unsigned)n + cast(unsigned)j, false);
|
||||
}
|
||||
LLVMValueRef lanes = LLVMBuildShuffleVector(p->builder, arg0.value, undef, LLVMConstVector(mask, part), "");
|
||||
agg = LLVMBuildInsertValue(p->builder, agg, lanes, cast(unsigned)j, "");
|
||||
}
|
||||
|
||||
res.value = agg;
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,6 +47,41 @@ simd_interleave_power_of_two_widths :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, simd_extract_i32(y, 0), 5)
|
||||
}
|
||||
|
||||
// `simd_deinterleave` splits by stride, so output `j` is the input taken every N lanes from
|
||||
// lane `j`. N > 2 lowers to shuffles for the same reason interleave does, only arm64 could
|
||||
// select the intrinsic. The round trip is asserted, not just the widths.
|
||||
|
||||
@(test)
|
||||
simd_deinterleave_splits_by_stride :: proc(t: ^testing.T) {
|
||||
v: #simd[16]i32
|
||||
for i in 0..<16 {
|
||||
v = intrinsics.simd_replace(v, i, i32(i))
|
||||
}
|
||||
|
||||
a2, b2 := intrinsics.simd_deinterleave(v, 2)
|
||||
testing.expect_value(t, len(a2), 8)
|
||||
testing.expect_value(t, simd_extract_i32(a2, 0), 0)
|
||||
testing.expect_value(t, simd_extract_i32(a2, 1), 2)
|
||||
testing.expect_value(t, simd_extract_i32(b2, 0), 1)
|
||||
testing.expect_value(t, simd_extract_i32(b2, 7), 15)
|
||||
|
||||
a4, b4, c4, d4 := intrinsics.simd_deinterleave(v, 4)
|
||||
testing.expect_value(t, len(a4), 4)
|
||||
testing.expect_value(t, simd_extract_i32(a4, 0), 0)
|
||||
testing.expect_value(t, simd_extract_i32(b4, 0), 1)
|
||||
testing.expect_value(t, simd_extract_i32(c4, 0), 2)
|
||||
testing.expect_value(t, simd_extract_i32(d4, 0), 3)
|
||||
testing.expect_value(t, simd_extract_i32(a4, 3), 12)
|
||||
testing.expect_value(t, simd_extract_i32(d4, 3), 15)
|
||||
|
||||
// interleave is the inverse, so the pair must round trip
|
||||
r := intrinsics.simd_interleave(a4, b4, c4, d4)
|
||||
testing.expect_value(t, len(r), 16)
|
||||
testing.expect_value(t, simd_extract_i32(r, 0), 0)
|
||||
testing.expect_value(t, simd_extract_i32(r, 7), 7)
|
||||
testing.expect_value(t, simd_extract_i32(r, 15), 15)
|
||||
}
|
||||
|
||||
simd_extract_i32 :: #force_inline proc(v: $V/#simd[$N]i32, $I: int) -> i32 {
|
||||
return intrinsics.simd_extract(v, I)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user