Add simd.interleave + simd.deinterleave

This commit is contained in:
gingerBill
2026-05-19 21:32:09 +01:00
parent c66de5ee3c
commit a91725f305
5 changed files with 148 additions and 3 deletions

View File

@@ -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.

View File

@@ -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

View File

@@ -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<Type *>(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] = {};

View File

@@ -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},

View File

@@ -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<LLVMValueRef>(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<LLVMValueRef>(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]);