mirror of
https://github.com/roc-lang/roc.git
synced 2025-09-26 13:29:12 +00:00
a proper fix for llvm wasm checked arithmetic
This commit is contained in:
parent
fd7a7ba1e6
commit
e850f94d05
4 changed files with 95 additions and 22 deletions
|
@ -1154,6 +1154,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
|
||||
build_int_binop(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
int_width,
|
||||
lhs_arg.into_int_value(),
|
||||
|
@ -1183,6 +1184,7 @@ pub(crate) fn run_low_level<'a, 'ctx>(
|
|||
|
||||
build_int_binop(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
int_width,
|
||||
lhs_arg.into_int_value(),
|
||||
|
@ -1429,6 +1431,7 @@ fn intwidth_from_layout(layout: InLayout) -> IntWidth {
|
|||
|
||||
fn build_int_binop<'ctx>(
|
||||
env: &Env<'_, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'_>,
|
||||
parent: FunctionValue<'ctx>,
|
||||
int_width: IntWidth,
|
||||
lhs: IntValue<'ctx>,
|
||||
|
@ -1452,10 +1455,23 @@ fn build_int_binop<'ctx>(
|
|||
throw_on_overflow(env, parent, result, "integer addition overflowed!")
|
||||
}
|
||||
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
|
||||
NumAddChecked => env.call_intrinsic(
|
||||
&LLVM_ADD_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
),
|
||||
NumAddChecked => {
|
||||
let with_overflow = env.call_intrinsic(
|
||||
&LLVM_ADD_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
);
|
||||
|
||||
let layout = Layout::from_int_width(int_width);
|
||||
let layout_repr = LayoutRepr::Struct(env.arena.alloc([layout, Layout::BOOL]));
|
||||
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_repr,
|
||||
with_overflow,
|
||||
"num_add_with_overflow",
|
||||
)
|
||||
}
|
||||
NumAddSaturated => {
|
||||
env.call_intrinsic(&LLVM_ADD_SATURATED[int_width], &[lhs.into(), rhs.into()])
|
||||
}
|
||||
|
@ -1470,10 +1486,23 @@ fn build_int_binop<'ctx>(
|
|||
throw_on_overflow(env, parent, result, "integer subtraction overflowed!")
|
||||
}
|
||||
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
|
||||
NumSubChecked => env.call_intrinsic(
|
||||
&LLVM_SUB_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
),
|
||||
NumSubChecked => {
|
||||
let with_overflow = env.call_intrinsic(
|
||||
&LLVM_SUB_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
);
|
||||
|
||||
let layout = Layout::from_int_width(int_width);
|
||||
let layout_repr = LayoutRepr::Struct(env.arena.alloc([layout, Layout::BOOL]));
|
||||
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_repr,
|
||||
with_overflow,
|
||||
"num_sub_with_overflow",
|
||||
)
|
||||
}
|
||||
NumSubSaturated => {
|
||||
env.call_intrinsic(&LLVM_SUB_SATURATED[int_width], &[lhs.into(), rhs.into()])
|
||||
}
|
||||
|
@ -1493,10 +1522,23 @@ fn build_int_binop<'ctx>(
|
|||
&[lhs.into(), rhs.into()],
|
||||
&bitcode::NUM_MUL_SATURATED_INT[int_width],
|
||||
),
|
||||
NumMulChecked => env.call_intrinsic(
|
||||
&LLVM_MUL_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
),
|
||||
NumMulChecked => {
|
||||
let with_overflow = env.call_intrinsic(
|
||||
&LLVM_MUL_WITH_OVERFLOW[int_width],
|
||||
&[lhs.into(), rhs.into()],
|
||||
);
|
||||
|
||||
let layout = Layout::from_int_width(int_width);
|
||||
let layout_repr = LayoutRepr::Struct(env.arena.alloc([layout, Layout::BOOL]));
|
||||
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
layout_repr,
|
||||
with_overflow,
|
||||
"num_mul_with_overflow",
|
||||
)
|
||||
}
|
||||
NumGt => {
|
||||
if int_width.is_signed() {
|
||||
bd.build_int_compare(SGT, lhs, rhs, "gt_int").into()
|
||||
|
@ -1671,6 +1713,7 @@ pub fn build_num_binop<'a, 'ctx>(
|
|||
match lhs_builtin {
|
||||
Int(int_width) => build_int_binop(
|
||||
env,
|
||||
layout_interner,
|
||||
parent,
|
||||
int_width,
|
||||
lhs_arg.into_int_value(),
|
||||
|
@ -2088,10 +2131,10 @@ pub(crate) fn dec_binop_with_unchecked<'ctx>(
|
|||
/// Zig returns a nominal `WithOverflow(Dec)` struct (see [zig_with_overflow_roc_dec]),
|
||||
/// but the Roc side may flatten the overflow struct. LLVM does not admit comparisons
|
||||
/// between the two representations, so always cast to the Roc representation.
|
||||
fn change_with_overflow_dec_to_roc_type<'a, 'ctx>(
|
||||
fn change_with_overflow_to_roc_type<'a, 'ctx>(
|
||||
env: &Env<'a, 'ctx, '_>,
|
||||
layout_interner: &STLayoutInterner<'a>,
|
||||
val: StructValue<'ctx>,
|
||||
val: impl BasicValue<'ctx>,
|
||||
return_layout: InLayout<'a>,
|
||||
) -> BasicValueEnum<'ctx> {
|
||||
let return_type = convert::basic_type_from_layout(
|
||||
|
@ -2099,7 +2142,8 @@ fn change_with_overflow_dec_to_roc_type<'a, 'ctx>(
|
|||
layout_interner,
|
||||
layout_interner.get_repr(return_layout),
|
||||
);
|
||||
let casted = cast_basic_basic(env.builder, val.into(), return_type);
|
||||
let casted = cast_basic_basic(env.builder, val.as_basic_value_enum(), return_type);
|
||||
|
||||
use_roc_value(
|
||||
env,
|
||||
layout_interner,
|
||||
|
@ -2123,15 +2167,15 @@ fn build_dec_binop<'a, 'ctx>(
|
|||
match op {
|
||||
NumAddChecked => {
|
||||
let val = dec_binop_with_overflow(env, bitcode::DEC_ADD_WITH_OVERFLOW, lhs, rhs);
|
||||
change_with_overflow_dec_to_roc_type(env, layout_interner, val, return_layout)
|
||||
change_with_overflow_to_roc_type(env, layout_interner, val, return_layout)
|
||||
}
|
||||
NumSubChecked => {
|
||||
let val = dec_binop_with_overflow(env, bitcode::DEC_SUB_WITH_OVERFLOW, lhs, rhs);
|
||||
change_with_overflow_dec_to_roc_type(env, layout_interner, val, return_layout)
|
||||
change_with_overflow_to_roc_type(env, layout_interner, val, return_layout)
|
||||
}
|
||||
NumMulChecked => {
|
||||
let val = dec_binop_with_overflow(env, bitcode::DEC_MUL_WITH_OVERFLOW, lhs, rhs);
|
||||
change_with_overflow_dec_to_roc_type(env, layout_interner, val, return_layout)
|
||||
change_with_overflow_to_roc_type(env, layout_interner, val, return_layout)
|
||||
}
|
||||
NumAdd => build_dec_binop_throw_on_overflow(
|
||||
env,
|
||||
|
|
|
@ -2534,6 +2534,21 @@ impl<'a> Layout<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
pub const fn from_int_width(int_width: IntWidth) -> InLayout<'static> {
|
||||
match int_width {
|
||||
IntWidth::U8 => Layout::U8,
|
||||
IntWidth::U16 => Layout::U16,
|
||||
IntWidth::U32 => Layout::U32,
|
||||
IntWidth::U64 => Layout::U64,
|
||||
IntWidth::U128 => Layout::U128,
|
||||
IntWidth::I8 => Layout::I8,
|
||||
IntWidth::I16 => Layout::I16,
|
||||
IntWidth::I32 => Layout::I32,
|
||||
IntWidth::I64 => Layout::I64,
|
||||
IntWidth::I128 => Layout::I128,
|
||||
}
|
||||
}
|
||||
|
||||
fn layout_from_ranged_number(
|
||||
env: &mut Env<'a, '_>,
|
||||
range: NumericRange,
|
||||
|
|
|
@ -73,10 +73,7 @@ impl TargetInfo {
|
|||
// This is a reasonable default for most architectures. We want to pass large values by
|
||||
// reference because it's more efficient than copying them around on the stack, and puts
|
||||
// less pressure on CPU registers.
|
||||
|
||||
// TODO: change this back to `self.ptr_size() * 4`. We make some assumptions about how
|
||||
// return valus are passed somewhere that make that not quite work for the llvm wasm backend.
|
||||
8 * 4
|
||||
self.ptr_size() * 4
|
||||
}
|
||||
|
||||
pub const fn ptr_alignment_bytes(&self) -> usize {
|
||||
|
|
|
@ -3995,6 +3995,23 @@ fn mul_checked_u128() {
|
|||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm", feature = "gen-dev"))]
|
||||
fn add_checked_u128() {
|
||||
assert_evals_to!(
|
||||
indoc!(
|
||||
r#"
|
||||
x : Result U128 [ Overflow ]
|
||||
x = Num.addChecked 5u128 2u128
|
||||
|
||||
x
|
||||
"#
|
||||
),
|
||||
RocResult::ok(5u128 + 2u128),
|
||||
RocResult<u128, ()>
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(any(feature = "gen-llvm", feature = "gen-dev", feature = "gen-wasm"))]
|
||||
fn num_min() {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue