Disabling arithmetic overflow checking


#1

Hi everyone,

I am currently writing a Game Boy emulator in Rust and I am currently implementing the CPU instructions. Most, if not all, require that arithmetic operations overflow. My Googlefoo has failed me and I cannot seem to find a way to turn this off.

One test cases for the CPU is to overflow a register and make sure it sets the appropriate CPU flags. When I run this case, the program panics and I get the error: panicked at ‘arithmetic operation overflowed’

How do I turn overflow checking off?

Thanks very much!


#2

Can you not emulate overflow? I believe non-debug builds don’t check overflow but, IIRC, overflow (and underflow) behavior is undefined in rust.


#3

I mean, I can emulate the overflow, but putting in extra overhead for basically every single arithmetic operation seems a bit insane to me (especially when this overhead is emulating what any computer can do natively).

Overflow and underflow is seriously undefined in Rust? Is this documented anywhere? What type of weird behavior can occur if an integer is overflowed/underflowed in Rust? Maybe I’m wrong but it seems a bit inexcusable for a systems programming language.


#4

You want the WrappingOps traits. See PR 22020 for progress on related stuff.


#5

Thanks for this! I guess I can make a wrapper struct for an integer type and then use operator overloading which calls wrapping_add(). Though, I’d imagine this would add overhead per arithmetic operation… A bit tad dissappointed about this :confused:

Hmm it seems according to the PR, it says: “Optional error checking on +, -, *”. Is the “optional” part not done yet?


#6

This already exists in the standard library – it’s called Wrapping. e.g.:

use std::num::wrapping::Wrapping;
fn main() {
    let x = Wrapping(0xffffffff_u32);
    let y = Wrapping(2);
    println!("{}", (x + y).0);  // The ".0" accesses the u32 in the Wrapping struct
}

It’s also possible to turn overflow checking off using --cfg ndebug or -Z force-overflow-checks=off, but --cfg ndebug is likely to change.

When checking is turned off, I think underflow/overflow is guaranteed to wrap, just as it did before overflow checking was added. The relevant section from RFC 560 seems to be:

The error conditions that can arise, and their defined results, are as follows. The intention is that the defined results are the same as the defined results today. The only change is that now a panic may result.

  • The operations +, -, *, /, % can underflow and overflow. Shift operations (<<, >>) can shift a value of width N by more than N bits. In these cases, the result is the same as the pre-existing, wrapping semantics.

  • When truncating, the casting operation as can overflow if the truncated bits contain non-zero values. If no panic occurs, the result of such an operation is defined to be the same as wrapping.


#7

Also, it’s not so clear that using Wrapping will have any impact on efficiency – the wrapping functions are marked #[inline(always)], so the compiler ought to optimize all the overhead away.

On a simple case, it produces optimal code:

pub fn sum_wrap(x: u32, y: u32) -> u32 {
    (Wrapping(x) + Wrapping(y)).0
}

x86_64 assembly output:

_ZN8sum_wrap20hfc5947ed1ee1c5bdsaaE:
    addl    %esi, %edi
    movl    %edi, %eax
    retq

#8

Newtypes are ideally transparent, but they do cost cpu and memory time in the compiler. Also optimizing compilers are very complicated systems, so it’s hard to guarantee that they will find the same optimizations in every case.


#9

Thanks very much for this!

I can’t seem to find it, but how would you pass -Z force-overflow-checks=off into cargo?


#10

-Z force-overflow-checks=off is for performance. The result of integer overflow is unspecified - future versions of rustc may behave differently.

However, newtype’d scalars should codegen the same as non-newtype’d scalars, so there should be no harm in using wrapping ops.


#11

RFC 560 seems fairly clear that integer overflow results in the same wrapping result it did before RFC 560, except in the case where the overflow panics. See the section I quoted from it above. e.g.:

If no panic occurs, the result of such an operation is defined to be the same as wrapping.

There is some hinting in the “Alternatives and possible future directions” section of unspecified results, but that section is describing designs that were rejected.


#12

AFAIK, there’s no way to pass arbitrary rustc options when you’re using Cargo. It has come up before – e.g. #60, #313, #544.


#13

Ah okay thanks for this.

One more question:

It seems that I cannot use operators (e.g. +, -, etc) between a Wrapping and a number literal. Therefore I tried to do this:

impl<T> Add<T> for Wrapping<T> {

    type Output = Wrapping<T>;

    pub fn add(self, rhs: T) -> Self {
        self.0 += rhs ;
        self
    }
}

When I try to compile this I get this error:

type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter [E0210]

I found this problem: https://github.com/rust-lang/rust/issues/19470
I am not sure if what I am compiling is recreate of this problem. Question is: am I doing something wrong or is this feature simply not done yet (i.e. I’ll have to define my own wrapping struct if I want to have operator overloading with literals)?


#14

This is by design: neither Add nor Wrapping are traits or types local to your crate, so you can’t impl that. Either the type or the trait needs to be local (That’s the simple version, then it comes down to which type parameters you use too).