Use unsigned LLVM intrinsic arithmetic for unsigned integers

Closes #2331
This commit is contained in:
ayazhafiz 2022-01-10 18:58:48 -05:00
parent 2f3919c870
commit abe42781d5
3 changed files with 86 additions and 25 deletions

View file

@ -201,24 +201,28 @@ macro_rules! float_intrinsic {
#[macro_export] #[macro_export]
macro_rules! int_intrinsic { macro_rules! int_intrinsic {
($name:literal) => {{ ($signed_name:literal, $unsigned_name:literal) => {{
let mut output = IntrinsicName::default(); let mut output = IntrinsicName::default();
// These are LLVM types which don't include // The indeces align with the `Index` impl for `IntrinsicName`.
// u64 for example output.options[4] = concat!($unsigned_name, ".i8");
output.options[4] = concat!($name, ".i8"); output.options[5] = concat!($unsigned_name, ".i16");
output.options[5] = concat!($name, ".i16"); output.options[6] = concat!($unsigned_name, ".i32");
output.options[6] = concat!($name, ".i32"); output.options[7] = concat!($unsigned_name, ".i64");
output.options[7] = concat!($name, ".i64"); output.options[8] = concat!($unsigned_name, ".i128");
output.options[8] = concat!($name, ".i128");
output.options[9] = concat!($name, ".i8"); output.options[9] = concat!($signed_name, ".i8");
output.options[10] = concat!($name, ".i16"); output.options[10] = concat!($signed_name, ".i16");
output.options[11] = concat!($name, ".i32"); output.options[11] = concat!($signed_name, ".i32");
output.options[12] = concat!($name, ".i64"); output.options[12] = concat!($signed_name, ".i64");
output.options[13] = concat!($name, ".i128"); output.options[13] = concat!($signed_name, ".i128");
output output
}}; }};
($name:literal) => {
int_intrinsic!($name, $name)
};
} }
pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin"); pub const NUM_ASIN: IntrinsicName = float_intrinsic!("roc_builtins.num.asin");

View file

@ -562,19 +562,19 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
}); });
add_float_intrinsic(ctx, module, &LLVM_FLOOR, |t| t.fn_type(&[t.into()], false)); add_float_intrinsic(ctx, module, &LLVM_FLOOR, |t| t.fn_type(&[t.into()], false));
add_int_intrinsic(ctx, module, &LLVM_SADD_WITH_OVERFLOW, |t| { add_int_intrinsic(ctx, module, &LLVM_ADD_WITH_OVERFLOW, |t| {
let fields = [t.into(), i1_type.into()]; let fields = [t.into(), i1_type.into()];
ctx.struct_type(&fields, false) ctx.struct_type(&fields, false)
.fn_type(&[t.into(), t.into()], false) .fn_type(&[t.into(), t.into()], false)
}); });
add_int_intrinsic(ctx, module, &LLVM_SSUB_WITH_OVERFLOW, |t| { add_int_intrinsic(ctx, module, &LLVM_SUB_WITH_OVERFLOW, |t| {
let fields = [t.into(), i1_type.into()]; let fields = [t.into(), i1_type.into()];
ctx.struct_type(&fields, false) ctx.struct_type(&fields, false)
.fn_type(&[t.into(), t.into()], false) .fn_type(&[t.into(), t.into()], false)
}); });
add_int_intrinsic(ctx, module, &LLVM_SMUL_WITH_OVERFLOW, |t| { add_int_intrinsic(ctx, module, &LLVM_MUL_WITH_OVERFLOW, |t| {
let fields = [t.into(), i1_type.into()]; let fields = [t.into(), i1_type.into()];
ctx.struct_type(&fields, false) ctx.struct_type(&fields, false)
.fn_type(&[t.into(), t.into()], false) .fn_type(&[t.into(), t.into()], false)
@ -602,9 +602,12 @@ static LLVM_STACK_SAVE: &str = "llvm.stacksave";
static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp"; static LLVM_SETJMP: &str = "llvm.eh.sjlj.setjmp";
pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp"; pub static LLVM_LONGJMP: &str = "llvm.eh.sjlj.longjmp";
const LLVM_SADD_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.sadd.with.overflow"); const LLVM_ADD_WITH_OVERFLOW: IntrinsicName =
const LLVM_SSUB_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.ssub.with.overflow"); int_intrinsic!("llvm.sadd.with.overflow", "llvm.uadd.with.overflow");
const LLVM_SMUL_WITH_OVERFLOW: IntrinsicName = int_intrinsic!("llvm.smul.with.overflow"); const LLVM_SUB_WITH_OVERFLOW: IntrinsicName =
int_intrinsic!("llvm.ssub.with.overflow", "llvm.usub.with.overflow");
const LLVM_MUL_WITH_OVERFLOW: IntrinsicName =
int_intrinsic!("llvm.smul.with.overflow", "llvm.umul.with.overflow");
fn add_intrinsic<'ctx>( fn add_intrinsic<'ctx>(
module: &Module<'ctx>, module: &Module<'ctx>,
@ -6366,7 +6369,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
NumAdd => { NumAdd => {
let result = env let result = env
.call_intrinsic( .call_intrinsic(
&LLVM_SADD_WITH_OVERFLOW[int_width], &LLVM_ADD_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
) )
.into_struct_value(); .into_struct_value();
@ -6375,13 +6378,13 @@ fn build_int_binop<'a, 'ctx, 'env>(
} }
NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(), NumAddWrap => bd.build_int_add(lhs, rhs, "add_int_wrap").into(),
NumAddChecked => env.call_intrinsic( NumAddChecked => env.call_intrinsic(
&LLVM_SADD_WITH_OVERFLOW[int_width], &LLVM_ADD_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
), ),
NumSub => { NumSub => {
let result = env let result = env
.call_intrinsic( .call_intrinsic(
&LLVM_SSUB_WITH_OVERFLOW[int_width], &LLVM_SUB_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
) )
.into_struct_value(); .into_struct_value();
@ -6390,13 +6393,13 @@ fn build_int_binop<'a, 'ctx, 'env>(
} }
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(), NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
NumSubChecked => env.call_intrinsic( NumSubChecked => env.call_intrinsic(
&LLVM_SSUB_WITH_OVERFLOW[int_width], &LLVM_SUB_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
), ),
NumMul => { NumMul => {
let result = env let result = env
.call_intrinsic( .call_intrinsic(
&LLVM_SMUL_WITH_OVERFLOW[int_width], &LLVM_MUL_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
) )
.into_struct_value(); .into_struct_value();
@ -6405,7 +6408,7 @@ fn build_int_binop<'a, 'ctx, 'env>(
} }
NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(), NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
NumMulChecked => env.call_intrinsic( NumMulChecked => env.call_intrinsic(
&LLVM_SMUL_WITH_OVERFLOW[int_width], &LLVM_MUL_WITH_OVERFLOW[int_width],
&[lhs.into(), rhs.into()], &[lhs.into(), rhs.into()],
), ),
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(), NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),

View file

@ -2101,3 +2101,57 @@ fn num_to_str() {
RocStr RocStr
); );
} }
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn u8_addition_greater_than_i8() {
assert_evals_to!(
indoc!(
r#"
x : U8
x = 100
y : U8
y = 100
x + y
"#
),
200,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn u8_sub_greater_than_i8() {
assert_evals_to!(
indoc!(
r#"
x : U8
x = 255
y : U8
y = 55
x - y
"#
),
200,
u8
)
}
#[test]
#[cfg(any(feature = "gen-llvm", feature = "gen-wasm"))]
fn u8_mul_greater_than_i8() {
assert_evals_to!(
indoc!(
r#"
x : U8
x = 40
y : U8
y = 5
x * y
"#
),
200,
u8
)
}