mirror of
https://github.com/odin-lang/Odin.git
synced 2026-06-08 11:34:20 +00:00
Merge pull request #3934 from laytan/fix-saturating-intrinsics
fix `add_sat` and `sub_sat` intrinsics
This commit is contained in:
@@ -38,9 +38,12 @@ count_leading_zeros :: proc(x: $T) -> T where type_is_integer(T) || type_is_sim
|
||||
reverse_bits :: proc(x: $T) -> T where type_is_integer(T) || type_is_simd_vector(T) ---
|
||||
byte_swap :: proc(x: $T) -> T where type_is_integer(T) || type_is_float(T) ---
|
||||
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) ---
|
||||
overflow_add :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) where type_is_integer(T) #optional_ok ---
|
||||
|
||||
add_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||
sub_sat :: proc(lhs, rhs: $T) -> T where type_is_integer(T) ---
|
||||
|
||||
sqrt :: proc(x: $T) -> T where type_is_float(T) || (type_is_simd_vector(T) && type_is_float(type_elem_type(T))) ---
|
||||
|
||||
|
||||
@@ -4261,6 +4261,47 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case BuiltinProc_overflow_add:
|
||||
case BuiltinProc_overflow_sub:
|
||||
case BuiltinProc_overflow_mul:
|
||||
{
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
check_expr(c, &x, ce->args[0]);
|
||||
check_expr(c, &y, ce->args[1]);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
return false;
|
||||
}
|
||||
convert_to_typed(c, &y, x.type); if (y.mode == Addressing_Invalid) return false;
|
||||
convert_to_typed(c, &x, y.type);
|
||||
if (is_type_untyped(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected a typed integer for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_integer(x.type)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
Type *ct = core_type(x.type);
|
||||
if (is_type_different_to_arch_endianness(ct)) {
|
||||
GB_ASSERT(ct->kind == Type_Basic);
|
||||
if (ct->Basic.flags & (BasicFlag_EndianLittle|BasicFlag_EndianBig)) {
|
||||
gbString xts = type_to_string(x.type);
|
||||
error(x.expr, "Expected an integer which does not specify the explicit endianness for '%.*s', got %s", LIT(builtin_name), xts);
|
||||
gb_string_free(xts);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = make_optional_ok_type(default_type(x.type));
|
||||
}
|
||||
break;
|
||||
|
||||
case BuiltinProc_add_sat:
|
||||
case BuiltinProc_sub_sat:
|
||||
{
|
||||
@@ -4300,7 +4341,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
}
|
||||
|
||||
operand->mode = Addressing_Value;
|
||||
operand->type = make_optional_ok_type(default_type(x.type));
|
||||
operand->type = default_type(x.type);
|
||||
}
|
||||
break;
|
||||
|
||||
|
||||
@@ -2236,8 +2236,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
case BuiltinProc_overflow_add:
|
||||
case BuiltinProc_overflow_sub:
|
||||
case BuiltinProc_overflow_mul:
|
||||
case BuiltinProc_add_sat:
|
||||
case BuiltinProc_sub_sat:
|
||||
{
|
||||
Type *main_type = tv.type;
|
||||
Type *type = main_type;
|
||||
@@ -2256,16 +2254,12 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
case BuiltinProc_overflow_add: name = "llvm.uadd.with.overflow"; break;
|
||||
case BuiltinProc_overflow_sub: name = "llvm.usub.with.overflow"; break;
|
||||
case BuiltinProc_overflow_mul: name = "llvm.umul.with.overflow"; break;
|
||||
case BuiltinProc_add_sat: name = "llvm.uadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.usub.sat"; break;
|
||||
}
|
||||
} else {
|
||||
switch (id) {
|
||||
case BuiltinProc_overflow_add: name = "llvm.sadd.with.overflow"; break;
|
||||
case BuiltinProc_overflow_sub: name = "llvm.ssub.with.overflow"; break;
|
||||
case BuiltinProc_overflow_mul: name = "llvm.smul.with.overflow"; break;
|
||||
case BuiltinProc_add_sat: name = "llvm.sadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.ssub.sat"; break;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, type)};
|
||||
@@ -2291,6 +2285,39 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_add_sat:
|
||||
case BuiltinProc_sub_sat:
|
||||
{
|
||||
Type *main_type = tv.type;
|
||||
Type *type = main_type;
|
||||
|
||||
lbValue x = lb_build_expr(p, ce->args[0]);
|
||||
lbValue y = lb_build_expr(p, ce->args[1]);
|
||||
x = lb_emit_conv(p, x, type);
|
||||
y = lb_emit_conv(p, y, type);
|
||||
|
||||
char const *name = nullptr;
|
||||
if (is_type_unsigned(type)) {
|
||||
switch (id) {
|
||||
case BuiltinProc_add_sat: name = "llvm.uadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.usub.sat"; break;
|
||||
}
|
||||
} else {
|
||||
switch (id) {
|
||||
case BuiltinProc_add_sat: name = "llvm.sadd.sat"; break;
|
||||
case BuiltinProc_sub_sat: name = "llvm.ssub.sat"; break;
|
||||
}
|
||||
}
|
||||
LLVMTypeRef types[1] = {lb_type(p->module, type)};
|
||||
|
||||
LLVMValueRef args[2] = { x.value, y.value };
|
||||
|
||||
lbValue res = {};
|
||||
res.value = lb_call_intrinsic(p, name, args, gb_count_of(args), types, gb_count_of(types));
|
||||
res.type = type;
|
||||
return res;
|
||||
}
|
||||
|
||||
case BuiltinProc_sqrt:
|
||||
{
|
||||
Type *type = tv.type;
|
||||
|
||||
Reference in New Issue
Block a user