diff --git a/core/intrinsics/intrinsics.odin b/core/intrinsics/intrinsics.odin index a14488210..7bbbc1efd 100644 --- a/core/intrinsics/intrinsics.odin +++ b/core/intrinsics/intrinsics.odin @@ -202,6 +202,9 @@ simd_shr :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shl_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- simd_shr_masked :: proc(a: #simd[N]T, b: #simd[N]Unsigned_Integer) -> #simd[N]T --- +simd_add_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- +simd_sub_sat :: proc(a, b: #simd[N]T) -> #simd[N]T --- + simd_and :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_or :: proc(a, b: #simd[N]T) -> #simd[N]T --- simd_xor :: proc(a, b: #simd[N]T) -> #simd[N]T --- @@ -238,13 +241,15 @@ simd_reduce_xor :: proc(a: #simd[N]T) -> T --- simd_shuffle :: proc(a, b: #simd[N]T, indices: ..int) -> #simd[len(indices)]T --- simd_select :: proc(cond: #simd[N]boolean_or_integer, true, false: #simd[N]T) -> #simd[N]T --- - -simd_sqrt :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +// Lane-wise operations +simd_sqrt :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- // IEEE sqrt simd_ceil :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- simd_floor :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- simd_trunc :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +// rounding to the nearest integral value; if two values are equally near, rounds to the even one simd_nearest :: proc(a: #simd[N]any_float) -> #simd[N]any_float --- +// equivalent a swizzle with descending indices, e.g. reserve(a, 3, 2, 1, 0) simd_reverse :: proc(a: #simd[N]T) -> #simd[N]T --- // WASM targets only diff --git a/core/simd/simd.odin b/core/simd/simd.odin index 060e32323..2ce487d13 100644 --- a/core/simd/simd.odin +++ b/core/simd/simd.odin @@ -39,6 +39,10 @@ shr :: intrinsics.simd_shr shl_masked :: intrinsics.simd_shl_masked shr_masked :: intrinsics.simd_shr_masked +// Saturation Arithmetic +add_sat :: intrinsics.simd_add_sat +sub_sat :: intrinsics.simd_sub_sat + and :: intrinsics.simd_and or :: intrinsics.simd_or xor :: intrinsics.simd_xor diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp index edf84b152..682667972 100644 --- a/src/check_builtin.cpp +++ b/src/check_builtin.cpp @@ -458,6 +458,8 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } // Integer only + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: case BuiltinProc_simd_rem: case BuiltinProc_simd_and: case BuiltinProc_simd_or: @@ -486,20 +488,25 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call } Type *elem = base_array_type(x.type); - if (id == BuiltinProc_simd_rem) { + switch (id) { + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + case BuiltinProc_simd_rem: if (!is_type_integer(elem)) { gbString xs = type_to_string(x.type); error(x.expr, "'%.*s' expected a #simd type with an integer element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } - } else { + break; + default: if (!is_type_integer(elem) && !is_type_boolean(elem)) { gbString xs = type_to_string(x.type); error(x.expr, "'%.*s' expected a #simd type with an integer or boolean element, got '%s'", LIT(builtin_name), xs); gb_string_free(xs); return false; } + break; } operand->mode = Addressing_Value; diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp index 22ee3d141..07c425cfa 100644 --- a/src/checker_builtin_procs.hpp +++ b/src/checker_builtin_procs.hpp @@ -129,6 +129,9 @@ BuiltinProc__simd_begin, BuiltinProc_simd_shl_masked, // C logic BuiltinProc_simd_shr_masked, // C logic + BuiltinProc_simd_add_sat, // saturation arithmetic + BuiltinProc_simd_sub_sat, // saturation arithmetic + BuiltinProc_simd_and, BuiltinProc_simd_or, BuiltinProc_simd_xor, @@ -402,6 +405,10 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = { {STR_LIT("simd_shr"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shl_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_shr_masked"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + + {STR_LIT("simd_add_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_sub_sat"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, + {STR_LIT("simd_and"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_or"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("simd_xor"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp index 42f5a60fa..dbdc51078 100644 --- a/src/llvm_backend_proc.cpp +++ b/src/llvm_backend_proc.cpp @@ -1353,6 +1353,30 @@ lbValue lb_build_builtin_simd_proc(lbProcedure *p, Ast *expr, TypeAndValue const return res; } + case BuiltinProc_simd_add_sat: + case BuiltinProc_simd_sub_sat: + { + arg1 = lb_build_expr(p, ce->args[1]); + + char const *name = nullptr; + switch (builtin_id) { + case BuiltinProc_simd_add_sat: name = is_signed ? "llvm.sadd.sat" : "llvm.uadd.sat"; break; + case BuiltinProc_simd_sub_sat: name = is_signed ? "llvm.ssub.sat" : "llvm.usub.sat"; break; + } + + LLVMTypeRef types[1] = {lb_type(p->module, arg0.type)}; + unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name)); + GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0])); + LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types)); + + LLVMValueRef args[2] = {}; + args[0] = arg0.value; + args[1] = arg1.value; + + res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), ""); + return res; + } + } GB_PANIC("Unhandled simd intrinsic: '%.*s'", LIT(builtin_procs[builtin_id].name));