Skip to content

Commit

Permalink
add intrinsics for checked overflow add/sub/mul
Browse files Browse the repository at this point in the history
  • Loading branch information
thestinger committed Aug 9, 2013
1 parent cdba212 commit f9b4228
Show file tree
Hide file tree
Showing 10 changed files with 663 additions and 8 deletions.
54 changes: 54 additions & 0 deletions src/librustc/middle/trans/base.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2721,6 +2721,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 @@ -237,7 +237,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 @@ -3648,6 +3648,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

4 comments on commit f9b4228

@bors
Copy link
Contributor

@bors bors commented on f9b4228 Aug 11, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bors
Copy link
Contributor

@bors bors commented on f9b4228 Aug 11, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

merging thestinger/rust/checked = f9b4228 into auto

@bors
Copy link
Contributor

@bors bors commented on f9b4228 Aug 11, 2013

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

thestinger/rust/checked = f9b4228 merged ok, testing candidate = 87474339

Please sign in to comment.