mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-28 22:34:45 +00:00
raise exception on Num.abs overflow
This commit is contained in:
parent
2bcaf3921f
commit
51cf54e367
2 changed files with 128 additions and 36 deletions
|
@ -2902,7 +2902,7 @@ fn run_low_level<'a, 'ctx, 'env>(
|
|||
|
||||
match arg_builtin {
|
||||
Int128 | Int64 | Int32 | Int16 | Int8 => {
|
||||
build_int_unary_op(env, arg.into_int_value(), arg_layout, op)
|
||||
build_int_unary_op(env, arg.into_int_value(), arg_builtin, op)
|
||||
}
|
||||
Float128 | Float64 | Float32 | Float16 => {
|
||||
build_float_unary_op(env, arg.into_float_value(), op)
|
||||
|
@ -3492,51 +3492,52 @@ fn build_float_binop<'a, 'ctx, 'env>(
|
|||
fn build_int_unary_op<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: IntValue<'ctx>,
|
||||
arg_layout: &Layout<'a>,
|
||||
arg_layout: &Builtin<'a>,
|
||||
op: LowLevel,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
use roc_module::low_level::LowLevel::*;
|
||||
|
||||
let bd = env.builder;
|
||||
let ctx = env.context;
|
||||
|
||||
match op {
|
||||
NumNeg => bd.build_int_neg(arg, "negate_int").into(),
|
||||
NumAbs => {
|
||||
// This is how libc's abs() is implemented - it uses no branching!
|
||||
//
|
||||
// abs = \arg ->
|
||||
// shifted = arg >>> 63
|
||||
//
|
||||
// (xor arg shifted) - shifted
|
||||
use Builtin::*;
|
||||
// integer abs overflows when applied to the minimum value of a signed type
|
||||
match arg_layout {
|
||||
Int128 => {
|
||||
let a = i128::MIN as u64;
|
||||
let b = (i128::MIN >> 64) as u64;
|
||||
|
||||
let ctx = env.context;
|
||||
let shifted_name = "abs_shift_right";
|
||||
let shifted_alloca = {
|
||||
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
|
||||
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
|
||||
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
|
||||
let alloca = bd.build_alloca(
|
||||
basic_type_from_layout(env.arena, ctx, arg_layout, env.ptr_bytes),
|
||||
"#int_abs_help",
|
||||
);
|
||||
|
||||
// shifted = arg >>> 63
|
||||
bd.build_store(alloca, shifted);
|
||||
|
||||
alloca
|
||||
};
|
||||
|
||||
let xored_arg = bd.build_xor(
|
||||
arg,
|
||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||
"xor_arg_shifted",
|
||||
);
|
||||
|
||||
BasicValueEnum::IntValue(bd.build_int_sub(
|
||||
xored_arg,
|
||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||
"sub_xored_shifted",
|
||||
))
|
||||
int_abs_raise_on_overflow(
|
||||
env,
|
||||
arg,
|
||||
ctx.i128_type().const_int_arbitrary_precision(&[b, a]),
|
||||
)
|
||||
}
|
||||
Int64 => int_abs_raise_on_overflow(
|
||||
env,
|
||||
arg,
|
||||
ctx.i64_type().const_int(i64::MIN as u64, false),
|
||||
),
|
||||
Int32 => int_abs_raise_on_overflow(
|
||||
env,
|
||||
arg,
|
||||
ctx.i32_type().const_int(i32::MIN as u64, false),
|
||||
),
|
||||
Int16 => int_abs_raise_on_overflow(
|
||||
env,
|
||||
arg,
|
||||
ctx.i16_type().const_int(i16::MIN as u64, false),
|
||||
),
|
||||
Int8 => int_abs_raise_on_overflow(
|
||||
env,
|
||||
arg,
|
||||
ctx.i8_type().const_int(i8::MIN as u64, false),
|
||||
),
|
||||
_ => unreachable!("not an integer type"),
|
||||
}
|
||||
}
|
||||
NumToFloat => {
|
||||
// This is an Int, so we need to convert it.
|
||||
|
@ -3553,6 +3554,77 @@ fn build_int_unary_op<'a, 'ctx, 'env>(
|
|||
}
|
||||
}
|
||||
|
||||
fn int_abs_raise_on_overflow<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: IntValue<'ctx>,
|
||||
min_val: IntValue<'ctx>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let builder = env.builder;
|
||||
let condition = builder.build_int_compare(IntPredicate::EQ, arg, min_val, "is_min_val");
|
||||
|
||||
let block = env.builder.get_insert_block().expect("to be in a function");
|
||||
let parent = block.get_parent().expect("to be in a function");
|
||||
let then_block = env.context.append_basic_block(parent, "then");
|
||||
let else_block = env.context.append_basic_block(parent, "else");
|
||||
|
||||
env.builder
|
||||
.build_conditional_branch(condition, then_block, else_block);
|
||||
|
||||
builder.position_at_end(then_block);
|
||||
|
||||
throw_exception(
|
||||
env,
|
||||
"integer absolute overflowed because its argument is the minimum value",
|
||||
);
|
||||
|
||||
builder.position_at_end(else_block);
|
||||
|
||||
int_abs_with_overflow(env, arg, Layout::Builtin(Builtin::Int64))
|
||||
}
|
||||
|
||||
fn int_abs_with_overflow<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: IntValue<'ctx>,
|
||||
arg_layout: Layout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
// This is how libc's abs() is implemented - it uses no branching!
|
||||
//
|
||||
// abs = \arg ->
|
||||
// shifted = arg >>> 63
|
||||
//
|
||||
// (xor arg shifted) - shifted
|
||||
|
||||
let bd = env.builder;
|
||||
let ctx = env.context;
|
||||
let shifted_name = "abs_shift_right";
|
||||
let shifted_alloca = {
|
||||
let bits_to_shift = ((arg_layout.stack_size(env.ptr_bytes) as u64) * 8) - 1;
|
||||
let shift_val = ctx.i64_type().const_int(bits_to_shift, false);
|
||||
let shifted = bd.build_right_shift(arg, shift_val, true, shifted_name);
|
||||
let alloca = bd.build_alloca(
|
||||
basic_type_from_layout(env.arena, ctx, &arg_layout, env.ptr_bytes),
|
||||
"#int_abs_help",
|
||||
);
|
||||
|
||||
// shifted = arg >>> 63
|
||||
bd.build_store(alloca, shifted);
|
||||
|
||||
alloca
|
||||
};
|
||||
|
||||
let xored_arg = bd.build_xor(
|
||||
arg,
|
||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||
"xor_arg_shifted",
|
||||
);
|
||||
|
||||
BasicValueEnum::IntValue(bd.build_int_sub(
|
||||
xored_arg,
|
||||
bd.build_load(shifted_alloca, shifted_name).into_int_value(),
|
||||
"sub_xored_shifted",
|
||||
))
|
||||
}
|
||||
|
||||
fn build_float_unary_op<'a, 'ctx, 'env>(
|
||||
env: &Env<'a, 'ctx, 'env>,
|
||||
arg: FloatValue<'ctx>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue