From a91725f305285d5dd9bd62b60e6e64142362f1a0 Mon Sep 17 00:00:00 2001 From: gingerBill Date: Tue, 19 May 2026 21:32:09 +0100 Subject: [PATCH] Add `simd.interleave` + `simd.deinterleave` --- base/intrinsics/intrinsics.odin | 7 ++- core/simd/simd.odin | 3 ++ src/check_builtin.cpp | 84 +++++++++++++++++++++++++++++++++ src/checker_builtin_procs.hpp | 6 +++ src/llvm_backend_proc.cpp | 51 +++++++++++++++++++- 5 files changed, 148 insertions(+), 3 deletions(-) diff --git a/base/intrinsics/intrinsics.odin b/base/intrinsics/intrinsics.odin index e89036932..635b31193 100644 --- a/base/intrinsics/intrinsics.odin +++ b/base/intrinsics/intrinsics.odin @@ -367,8 +367,11 @@ simd_odd_even :: proc(a, b: #simd[N]T) -> #simd[N]T --- // Returns the sums of N consecutive lanes simd_sums_of_n :: proc(a: #simd[LANES]T, $N: uint) -> #simd[LANES/N]T where is_power_of_two(N) --- -simd_pairwise_add :: proc(a, b: #simd[LANES]T) -> #simd[LANES/N]T --- -simd_pairwise_sub :: proc(a, b: #simd[LANES]T) -> #simd[LANES/N]T --- +simd_pairwise_add :: proc(a, b: #simd[LANES]T) -> #simd[LANES]T where LANES % 2 == 0 --- +simd_pairwise_sub :: proc(a, b: #simd[LANES]T) -> #simd[LANES]T where LANES % 2 == 0 --- + +simd_interleave :: proc(a, ..#simd[LANES/N]T) -> #simd[LANES]T where N >= 1 --- +simd_deinterleave :: proc(a: #simd[LANES]T, $N: uint) -> (#simd[LANES/N]T, #simd[LANES/N]T, ..., #simd[LANES/N]T) where N >= 1, LANES % N == 0 --- // Checks if the current target supports the given target features. diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 11dc8129d..7313babcd 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -2886,3 +2886,6 @@ abs_diff :: #force_inline proc "contextless" (a, b: $T/#simd[$LANES]$E) -> T whe pairwise_add :: intrinsics.simd_pairwise_add pairwise_sub :: intrinsics.simd_pairwise_add + +interleave :: intrinsics.simd_interleave +deinterleave :: intrinsics.simd_deinterleave \ No newline at end of file diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index 1a6b97b49..584264f10 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -1868,6 +1868,90 @@ gb_internal bool check_builtin_simd_operation(CheckerContext *c, Operand *operan return true; } + case BuiltinProc_simd_interleave: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + + for (isize i = 1; i < ce->args.count; i++) { + Operand y = {}; + check_expr_with_type_hint(c, &y, ce->args[i], x.type); if (y.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(y.type)) { + error(y.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + if (!are_types_identical(x.type, y.type)) { + gbString a = type_to_string(x.type); + gbString b = type_to_string(y.type); + error(y.expr, "'%.*s' all argument types must match, expected %s, got %s", LIT(builtin_name), a, b); + gb_string_free(b); + gb_string_free(a); + return false; + } + } + + Type *elem = base_array_type(x.type); + i64 base_count = get_array_type_count(x.type); + i64 count = base_count * cast(i64)ce->args.count; + + i64 max_count = 64; + if (count > max_count) { + error(ce->proc, "'%.*s' exceeds the maximum #simd count %lld, got %lld", cast(long long)max_count, cast(long long)count); + return false; + } + + operand->type = alloc_type_simd_vector(count, elem); + operand->mode = Addressing_Value; + return true; + } + + case BuiltinProc_simd_deinterleave: + { + Operand x = {}; + check_expr(c, &x, ce->args[0]); if (x.mode == Addressing_Invalid) return false; + if (!is_type_simd_vector(x.type)) { + error(x.expr, "'%.*s' expected a simd vector type", LIT(builtin_name)); + return false; + } + + Type *elem = base_array_type(x.type); + i64 max_count = get_array_type_count(x.type); + + Operand n = {}; + check_expr(c, &n, ce->args[1]); if (n.mode == Addressing_Invalid) return false; + if (n.mode != Addressing_Constant) { + error(n.expr, "'%.*s' expected a constant integer divisible by the count of the #simd vector", LIT(builtin_name)); + return false; + } + if (n.value.kind != ExactValue_Integer) { + error(n.expr, "'%.*s' expected a constant integer divisible by the count of the #simd vector", LIT(builtin_name)); + return false; + } + i64 divisor = exact_value_to_i64(n.value); + if (divisor < 1 || divisor > max_count || (max_count % divisor != 0)) { + error(n.expr, "'%.*s' expected a constant integer divisible by the count of the #simd vector , got %lld, which must have been divisible by %lld", LIT(builtin_name), cast(long long)divisor, cast(long long)max_count); + return false; + } + + Type *base_vector = alloc_type_simd_vector(max_count / divisor, elem); + + Type **field_types = temporary_alloc_array(cast(isize)divisor); + for (i64 i = 0; i < divisor; i++) { + field_types[i] = base_vector; + } + + operand->type = alloc_type_tuple_from_field_types(field_types, divisor, false, false); + operand->mode = Addressing_Value; + return true; + } + break; + + case BuiltinProc_simd_x86__MM_SHUFFLE: { Operand x[4] = {}; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 8baa92e58..049d29a2a 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -235,6 +235,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_indices, + BuiltinProc_simd_interleave, + BuiltinProc_simd_deinterleave, + // Platform specific SIMD intrinsics BuiltinProc_simd_x86__MM_SHUFFLE, @@ -641,6 +644,9 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_indices"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_interleave"), 1, true, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_deinterleave"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_x86__MM_SHUFFLE"), 4, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 2840cfd3e..bed7ad6ac 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1445,7 +1445,7 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn Type *elem = type->SimdVector.elem; i64 count = type->SimdVector.count; - LLVMValueRef *scalars = gb_alloc_array(temporary_allocator(), LLVMValueRef, count); + LLVMValueRef *scalars = temporary_alloc_array(count); for (i64 i = 0; i < count; i++) { scalars[i] = lb_const_value(m, elem, exact_value_i64(i)).value; } @@ -1453,6 +1453,55 @@ gb_internal lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAn res.value = LLVMConstVector(scalars, cast(unsigned)count); return res; } + + case BuiltinProc_simd_interleave: + { + int n = cast(int)ce->args.count; + + if (n == 1) { + lbValue arg = lb_build_expr(p, ce->args[0]); + res.value = arg.value; + return res; + } + + Type *vector_type = type_of_expr(ce->args[0]); + + LLVMValueRef *args = temporary_alloc_array(n); + for (int i = 0; i < n; i++) { + lbValue arg = lb_build_expr(p, ce->args[i]); + arg = lb_emit_conv(p, arg, vector_type); + args[i] = arg.value; + } + + gbString name = gb_string_make(heap_allocator(), ""); + name = gb_string_append_fmt(name, "llvm.vector.interleave%d", n); + defer (gb_string_free(name)); + + LLVMTypeRef types[1] = {lb_type(m, tv.type)}; + res.value = lb_call_intrinsic(p, name, args, n, types, gb_count_of(types)); + return res; + } + + 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); + + if (n == 1) { + res.value = arg0.value; + 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)); + + res.value = lb_call_intrinsic(p, name, &arg0.value, 1, types, gb_count_of(types)); + return res; + } } lbValue arg0 = {}; if (ce->args.count > 0) arg0 = lb_build_expr(p, ce->args[0]);