checkedint call removal

bearophile via Digitalmars-d digitalmars-d at puremagic.com
Wed Jul 30 02:12:55 PDT 2014


Walter Bright:

> Data flow analysis can figure that example out.

Sorry, my mistake, what I am discussing about should not need 
much flow analysis. Here x and y are immutable:


void main(in string[] args) {
     import std.stdio, std.conv;

     assert(args.length == 3);
     immutable ubyte ux = args[1].to!ubyte;
     immutable ubyte uy = args[2].to!ubyte;
     immutable int x = ux;
     immutable int y = uy;

     immutable int result = x * y;
     writeln("Product: ", result);
}


In the current D compiler x and y keep a value range of ubytes 
(despite they are ints), so here D knows the expression "x * y" 
can't overflow, so even this is accepted with no casts needed:

immutable ushort result = x * y;



Now let's convert that code with SInt (x and y are still 
immutable, unlike in my precedent post):


void main(in string[] args) {
     import std.stdio, std.conv, std.experimental.safeintegral;

     assert(args.length == 3);
     immutable ubyte ux = args[1].to!ubyte;
     immutable ubyte uy = args[2].to!ubyte;
     immutable SInt x = ux;
     immutable SInt y = uy;

     immutable SInt result = x * y;
     assert(!result.overflow);
     writeln("Product: ", result);
}


What's I'd like is a way for x and y (that are of the 
library-defined type SInt) to keep the value range of an ubyte. 
So the compiler is able to rewrite this:

immutable SInt result = x * y;

removing the call to muls() and replacing it with a regular 
multiplication, that is faster, even when no inlining happens. 
(To do this SInt needs two different "instantiations" of its 
opBinary("*") ).


> If it can't for a more complex one, you can do:
>
>   assert(x >= 0 && x <= 255);
>   assert(y >= 0 && y <= 255);

For such simple situations I don't want user annotations. The 
only code needed to make this request work should be already 
written inside the implementation of SInt and similar 
user-defined types.


And assume() and assert() are two different things, used for 
different purposes. Do not give the same name to two so different 
features, if you want to keep a language sane.

Info about assume in Microsoft C++:
http://msdn.microsoft.com/en-us/library/1b3fsfxw.aspx


> The optimizer can certainly use asserts to provide semantic 
> information (even though the dmd one doesn't at the moment).

This is not a good idea. That's the job for assume(), not for 
assert.

In general assert() verifies something is true, and if it's 
false, the program just raises an assert error, that is even 
recoverable. An assert leaves a condition testing inside the 
binary in debug builds.

An assume() doesn't need to leave a test condition inside the 
binary, even in debug builds. It doesn't raise run-time errors. 
It's just a way to tell the compiler that some predicate is true, 
and the optimization stages of the compiler have to try to use 
this information to optimize the code.

You can't mix or replace the two things, because a mistake in an 
assert() doesn't cause your program to burn, it just raises a 
compile-time error. A programmer mistake in an assume() burns 
your house.

assert() can be used freely in your code, to make sure you have 
not done a mistake. assume() is only for special situations where 
you know something is true, that the compiler can't prove by 
itself.


> Again, I know you like reading about new languages and language 
> features. I think you'd enjoy that even more supplemented with 
> a book on how compilers work internally, in particular, how 
> data flow analysis works and what it can do.

I will read more about that topic.

Bye,
bearophile


More information about the Digitalmars-d mailing list