Skip to content

add intrinsics for checked overflow add/sub/mul #8408

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 54 additions & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2740,6 +2740,60 @@ pub fn declare_intrinsics(llmod: ModuleRef) -> HashMap<&'static str, ValueRef> {
ifn!("llvm.bswap.i32",[Type::i32()], Type::i32());
ifn!("llvm.bswap.i64",[Type::i64()], Type::i64());

ifn!("llvm.sadd.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.sadd.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.sadd.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.sadd.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

ifn!("llvm.uadd.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.uadd.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.uadd.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.uadd.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

ifn!("llvm.ssub.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.ssub.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.ssub.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.ssub.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

ifn!("llvm.usub.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.usub.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.usub.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.usub.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

ifn!("llvm.smul.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.smul.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.smul.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.smul.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

ifn!("llvm.umul.with.overflow.i8",
[Type::i8(), Type::i8()], Type::struct_([Type::i8(), Type::i1()], false));
ifn!("llvm.umul.with.overflow.i16",
[Type::i16(), Type::i16()], Type::struct_([Type::i16(), Type::i1()], false));
ifn!("llvm.umul.with.overflow.i32",
[Type::i32(), Type::i32()], Type::struct_([Type::i32(), Type::i1()], false));
ifn!("llvm.umul.with.overflow.i64",
[Type::i64(), Type::i64()], Type::struct_([Type::i64(), Type::i1()], false));

return intrinsics;
}

Expand Down
8 changes: 5 additions & 3 deletions src/librustc/middle/trans/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -714,9 +714,11 @@ pub fn ExtractValue(cx: @mut Block, AggVal: ValueRef, Index: uint) -> ValueRef {
}
}

pub fn InsertValue(cx: @mut Block, AggVal: ValueRef, EltVal: ValueRef, Index: uint) {
if cx.unreachable { return; }
B(cx).insert_value(AggVal, EltVal, Index)
pub fn InsertValue(cx: @mut Block, AggVal: ValueRef, EltVal: ValueRef, Index: uint) -> ValueRef {
unsafe {
if cx.unreachable { return llvm::LLVMGetUndef(Type::nil().to_ref()); }
B(cx).insert_value(AggVal, EltVal, Index)
}
}

pub fn IsNull(cx: @mut Block, Val: ValueRef) -> ValueRef {
Expand Down
4 changes: 2 additions & 2 deletions src/librustc/middle/trans/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -861,11 +861,11 @@ impl Builder {
}

pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef,
idx: uint) {
idx: uint) -> ValueRef {
self.count_insn("insertvalue");
unsafe {
llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint,
noname());
noname())
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/librustc/middle/trans/common.rs
Original file line number Diff line number Diff line change
Expand Up @@ -238,7 +238,7 @@ impl FunctionContext {
}

pub fn out_arg_pos(&self) -> uint {
assert!(self.has_immediate_return_value);
assert!(!self.has_immediate_return_value);
0u
}

Expand Down
49 changes: 49 additions & 0 deletions src/librustc/middle/trans/foreign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -550,6 +550,24 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
Ret(bcx, Call(bcx, llfn, args.slice(0, num_args)));
}

fn with_overflow_instrinsic(bcx: @mut Block, name: &'static str) {
let first_real_arg = bcx.fcx.arg_pos(0u);
let a = get_param(bcx.fcx.llfn, first_real_arg);
let b = get_param(bcx.fcx.llfn, first_real_arg + 1);
let llfn = bcx.ccx().intrinsics.get_copy(&name);

// convert `i1` to a `bool`, and write to the out parameter
let val = Call(bcx, llfn, [a, b]);
let result = ExtractValue(bcx, val, 0);
let overflow = ZExt(bcx, ExtractValue(bcx, val, 1), Type::bool());
let retptr = get_param(bcx.fcx.llfn, bcx.fcx.out_arg_pos());
let ret = Load(bcx, retptr);
let ret = InsertValue(bcx, ret, result, 0);
let ret = InsertValue(bcx, ret, overflow, 1);
Store(bcx, ret, retptr);
RetVoid(bcx)
}

fn memcpy_intrinsic(bcx: @mut Block, name: &'static str, tp_ty: ty::t, sizebits: u8) {
let ccx = bcx.ccx();
let lltp_ty = type_of::type_of(ccx, tp_ty);
Expand Down Expand Up @@ -944,6 +962,37 @@ pub fn trans_intrinsic(ccx: @mut CrateContext,
"bswap16" => simple_llvm_intrinsic(bcx, "llvm.bswap.i16", 1),
"bswap32" => simple_llvm_intrinsic(bcx, "llvm.bswap.i32", 1),
"bswap64" => simple_llvm_intrinsic(bcx, "llvm.bswap.i64", 1),

"i8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i8"),
"i16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i16"),
"i32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i32"),
"i64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.sadd.with.overflow.i64"),

"u8_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i8"),
"u16_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i16"),
"u32_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i32"),
"u64_add_with_overflow" => with_overflow_instrinsic(bcx, "llvm.uadd.with.overflow.i64"),

"i8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i8"),
"i16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i16"),
"i32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i32"),
"i64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.ssub.with.overflow.i64"),

"u8_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i8"),
"u16_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i16"),
"u32_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i32"),
"u64_sub_with_overflow" => with_overflow_instrinsic(bcx, "llvm.usub.with.overflow.i64"),

"i8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i8"),
"i16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i16"),
"i32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i32"),
"i64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.smul.with.overflow.i64"),

"u8_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i8"),
"u16_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i16"),
"u32_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i32"),
"u64_mul_with_overflow" => with_overflow_instrinsic(bcx, "llvm.umul.with.overflow.i64"),

_ => {
// Could we make this an enum rather than a string? does it get
// checked earlier?
Expand Down
16 changes: 16 additions & 0 deletions src/librustc/middle/trans/type_use.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,22 @@ pub fn type_uses_for(ccx: @mut CrateContext, fn_id: def_id, n_tps: uint)

"bswap16" | "bswap32" | "bswap64" => 0,


"i8_add_with_overflow" | "u8_add_with_overflow" |
"i16_add_with_overflow" | "u16_add_with_overflow" |
"i32_add_with_overflow" | "u32_add_with_overflow" |
"i64_add_with_overflow" | "u64_add_with_overflow" => 0,

"i8_sub_with_overflow" | "u8_sub_with_overflow" |
"i16_sub_with_overflow" | "u16_sub_with_overflow" |
"i32_sub_with_overflow" | "u32_sub_with_overflow" |
"i64_sub_with_overflow" | "u64_sub_with_overflow" => 0,

"i8_mul_with_overflow" | "u8_mul_with_overflow" |
"i16_mul_with_overflow" | "u16_mul_with_overflow" |
"i32_mul_with_overflow" | "u32_mul_with_overflow" |
"i64_mul_with_overflow" | "u64_mul_with_overflow" => 0,

// would be cool to make these an enum instead of
// strings!
_ => fail!("unknown intrinsic in type_use")
Expand Down
33 changes: 33 additions & 0 deletions src/librustc/middle/typeck/check/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3647,6 +3647,39 @@ pub fn check_intrinsic_type(ccx: @mut CrateCtxt, it: @ast::foreign_item) {
"bswap16" => (0, ~[ ty::mk_i16() ], ty::mk_i16()),
"bswap32" => (0, ~[ ty::mk_i32() ], ty::mk_i32()),
"bswap64" => (0, ~[ ty::mk_i64() ], ty::mk_i64()),

"i8_add_with_overflow" | "i8_sub_with_overflow" | "i8_mul_with_overflow" =>
(0, ~[ty::mk_i8(), ty::mk_i8()],
ty::mk_tup(tcx, ~[ty::mk_i8(), ty::mk_bool()])),

"i16_add_with_overflow" | "i16_sub_with_overflow" | "i16_mul_with_overflow" =>
(0, ~[ty::mk_i16(), ty::mk_i16()],
ty::mk_tup(tcx, ~[ty::mk_i16(), ty::mk_bool()])),

"i32_add_with_overflow" | "i32_sub_with_overflow" | "i32_mul_with_overflow" =>
(0, ~[ty::mk_i32(), ty::mk_i32()],
ty::mk_tup(tcx, ~[ty::mk_i32(), ty::mk_bool()])),

"i64_add_with_overflow" | "i64_sub_with_overflow" | "i64_mul_with_overflow" =>
(0, ~[ty::mk_i64(), ty::mk_i64()],
ty::mk_tup(tcx, ~[ty::mk_i64(), ty::mk_bool()])),

"u8_add_with_overflow" | "u8_sub_with_overflow" | "u8_mul_with_overflow" =>
(0, ~[ty::mk_u8(), ty::mk_u8()],
ty::mk_tup(tcx, ~[ty::mk_u8(), ty::mk_bool()])),

"u16_add_with_overflow" | "u16_sub_with_overflow" | "u16_mul_with_overflow" =>
(0, ~[ty::mk_u16(), ty::mk_u16()],
ty::mk_tup(tcx, ~[ty::mk_u16(), ty::mk_bool()])),

"u32_add_with_overflow" | "u32_sub_with_overflow" | "u32_mul_with_overflow"=>
(0, ~[ty::mk_u32(), ty::mk_u32()],
ty::mk_tup(tcx, ~[ty::mk_u32(), ty::mk_bool()])),

"u64_add_with_overflow" | "u64_sub_with_overflow" | "u64_mul_with_overflow" =>
(0, ~[ty::mk_u64(), ty::mk_u64()],
ty::mk_tup(tcx, ~[ty::mk_u64(), ty::mk_bool()])),

ref other => {
tcx.sess.span_err(it.span,
fmt!("unrecognized intrinsic function: `%s`",
Expand Down
Loading