feat: throws exception on multiplication overflow

This commit is contained in:
Celso Bonutti Filho 2020-12-23 10:17:16 -03:00
parent 2608be3df9
commit cfbc4d1c54
2 changed files with 116 additions and 3 deletions

View file

@ -355,6 +355,12 @@ fn add_intrinsics<'ctx>(ctx: &'ctx Context, module: &Module<'ctx>) {
ctx.struct_type(&fields, false)
.fn_type(&[i64_type.into(), i64_type.into()], false)
});
add_intrinsic(module, LLVM_SMUL_WITH_OVERFLOW_I64, {
let fields = [i64_type.into(), i1_type.into()];
ctx.struct_type(&fields, false)
.fn_type(&[i64_type.into(), i64_type.into()], false)
});
}
static LLVM_MEMSET_I64: &str = "llvm.memset.p0i8.i64";
@ -369,6 +375,7 @@ static LLVM_CEILING_F64: &str = "llvm.ceil.f64";
static LLVM_FLOOR_F64: &str = "llvm.floor.f64";
pub static LLVM_SADD_WITH_OVERFLOW_I64: &str = "llvm.sadd.with.overflow.i64";
pub static LLVM_SSUB_WITH_OVERFLOW_I64: &str = "llvm.ssub.with.overflow.i64";
pub static LLVM_SMUL_WITH_OVERFLOW_I64: &str = "llvm.smul.with.overflow.i64";
fn add_intrinsic<'ctx>(
module: &Module<'ctx>,
@ -3259,7 +3266,36 @@ fn build_int_binop<'a, 'ctx, 'env>(
}
NumSubWrap => bd.build_int_sub(lhs, rhs, "sub_int").into(),
NumSubChecked => env.call_intrinsic(LLVM_SSUB_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()]),
NumMul => bd.build_int_mul(lhs, rhs, "mul_int").into(),
NumMul => {
let context = env.context;
let result = env
.call_intrinsic(LLVM_SMUL_WITH_OVERFLOW_I64, &[lhs.into(), rhs.into()])
.into_struct_value();
let mul_result = bd.build_extract_value(result, 0, "mul_result").unwrap();
let has_overflowed = bd.build_extract_value(result, 1, "has_overflowed").unwrap();
let condition = bd.build_int_compare(
IntPredicate::EQ,
has_overflowed.into_int_value(),
context.bool_type().const_zero(),
"has_not_overflowed",
);
let then_block = context.append_basic_block(parent, "then_block");
let throw_block = context.append_basic_block(parent, "throw_block");
bd.build_conditional_branch(condition, then_block, throw_block);
bd.position_at_end(throw_block);
throw_exception(env, "integer multiplication overflowed!");
bd.position_at_end(then_block);
mul_result
}
// NumMulWrap => bd.build_int_mul(lhs, rhs, "mul_int").into(),
NumGt => bd.build_int_compare(SGT, lhs, rhs, "int_gt").into(),
NumGte => bd.build_int_compare(SGE, lhs, rhs, "int_gte").into(),
NumLt => bd.build_int_compare(SLT, lhs, rhs, "int_lt").into(),
@ -3475,7 +3511,28 @@ fn build_float_binop<'a, 'ctx, 'env>(
struct_value.into()
}
NumSubWrap => unreachable!("wrapping subtraction is not defined on floats"),
NumMul => bd.build_float_mul(lhs, rhs, "mul_float").into(),
NumMul => {
let builder = env.builder;
let context = env.context;
let result = bd.build_float_mul(lhs, rhs, "mul_float");
let is_finite =
call_bitcode_fn(env, &[result.into()], &bitcode::NUM_IS_FINITE).into_int_value();
let then_block = context.append_basic_block(parent, "then_block");
let throw_block = context.append_basic_block(parent, "throw_block");
builder.build_conditional_branch(is_finite, then_block, throw_block);
builder.position_at_end(throw_block);
throw_exception(env, "float multiplication overflowed!");
builder.position_at_end(then_block);
result.into()
}
NumGt => bd.build_float_compare(OGT, lhs, rhs, "float_gt").into(),
NumGte => bd.build_float_compare(OGE, lhs, rhs, "float_gte").into(),
NumLt => bd.build_float_compare(OLT, lhs, rhs, "float_lt").into(),