Uh oh!
There was an error while loading. Please reload this page.
[MIR] Implement overflow checking - #33255
Conversation
Add, Sub, Mul, Shl, and Shr are checked using a new Rvalue: CheckedBinaryOp, while Div, Rem and Neg are handled with explicit checks in the MIR.
Factors out the common pattern across the several places that do arithmetic checks
rust-highfive
commented
Apr 28, 2016
(rust_highfive has picked a reviewer for you, use r? to override) |
Aatch
commented
Apr 28, 2016
This currently breaks documentation generation for libc, as it makes the panic lang item required during MIR construction. /cc @alexcrichton |
| ast::IntTy::I64 => ConstInt::I64(std::i64::MIN), | ||
| ast::IntTy::Is => { | ||
| let int_ty = self.hir.tcx().sess.target.int_type; | ||
| let min = match int_ty { |
There was a problem hiding this comment.
You can generate the correct ConstIsize variant directly, without casting to i64 and going through ConstIsize::new.
let val = match int_ty {
ast::IntTy::I32 => ConstIsize::Is32(std::i32::MIN),
ast::IntTy::I64 => ConstIsize::Is64(std::i64::MIN),
_ => bug!(),}alexcrichton
commented
Apr 28, 2016
Ah that's fine, I can fix that at some point upstream for libc |
| let after = f(self, then_block); | ||
| // If the returned block isn't terminated, add a branch to the "else" | ||
| // block |
There was a problem hiding this comment.
What is the purpose of this hack? Does it always result in correct code in case of bugs (when somebody forgets to set a terminator)? If not, then lack of the terminator should just stay a bug, as it currently is.
Have you considered a ? Well, I guess there isn’t much difference, the current approach also looks fine. |
This is simpler to work with than `with_cond`.
This branch shouldn't be hit so if it is, it's probably a mistake.
| }); | ||
| block.and(Rvalue::Use(Operand::Consume(val))) | ||
| } else { |
There was a problem hiding this comment.
Doesn’t this branch ignore the check_overflow? Are we checking for division unconditionally in current trans?
There was a problem hiding this comment.
To me it seems like it would be cleaner to have something like
if !self.is_overflow_checked() || !op.is_checkable() || !ty.is_integral() { return block.and(Rvalue::BinaryOp(...) }
at the top, and then only do the branching based on the operator elsewhere in this function.
DemiMarie
commented
Apr 30, 2016
Does this generate the LLVM intrinsics or explicit checks? The LLVM documentation for frontends warns against using the intrinsics unless necessary. |
nagisa
commented
Apr 30, 2016
@drbo it does indeed emit intrinsics. I do not see how one would check for overflow after the fact without intrinsics, and I’m doubtful implementing overflow detection for, say multiplication would be easy without intrinsics or inline assembly. Any ideas? |
Aatch
commented
May 1, 2016
@drbo that's almost certainly referring to the fact that you shouldn't use the intrinsics unless you actually need to check the result, which is the case here. If we don't check the result, we don't use the intrinsics, it's not like we emit the intrinsics for all arithmetic, just checked arithmetic. Also, the impact on optimisation isn't a strong factor here, since most of the time chceked arithmetic is used because optimisations haven't been enabled. |
| let (of_block, ok_block) = this.build_cond_br(block, expr_span, | ||
| Operand::Consume(is_min)); | ||
| this.panic(of_block, "attempted to negate with overflow", expr_span); |
There was a problem hiding this comment.
I imagine we might want some more structured form of terminators here for these special checks (but that needn't derail this PR).
nikomatsakis
commented
May 3, 2016
@Aatch ok I reviewed the PR. The code all makes sense and basically looks good to me. I have one question though, I thought you had mentioned some kind of change to MIR to make something analogous to LLVM's "extract-value"? I didn't see anything like that? Otherwise, it seems like we have to settle the question of what to do about inlined code. I keep going back and forth. Finally, I suspect we are going to want mildly higher-level terminators that are clearly "automatically inserted" so that things like constant propagation and so forth can recognize them, but I'm not sure about that. |
nikomatsakis
commented
May 3, 2016
Ah, just saw this comment saying that the codegen part was deferred. |
alexcrichton
commented
May 4, 2016
The libs team discussed the ramifications of this on the standard library today, and the conclusion was generally positive. We felt that if the standard library wants overflow checks it should write those overflow checks (e.g. with @eddyb, could you clarify where this is relied on for We did have some hesitation, however, at the consideration of this for the I personally interpret that as expecting code like this to panic: fnadd<T:Add>(a:T,b:T) -> T{
a + b
}fnmain(){add(200_u8,200_u8);}I think with this PR, however, it will not panic? That'll get dispatched to the |
eddyb
commented
May 4, 2016
@alexcrichton |
arielb1
commented
May 5, 2016
Debugopt builds are a thing, you know. I would not expect anyone to run their big testsuite on a non-opt build. The We may want a |
alexcrichton
commented
May 5, 2016
eddyb
commented
May 5, 2016
@alexcrichton Well, the comments indicate that the behavior of @arielb1 The "short-cutting" only works when the types are known, though. If trait selection picks the impls in |
Implements overflow checking in the MIR. Also adds checking for
x/0,x%0,MIN/-1andMIN%-1.Fixes#29769