Thread overview
assert and static assert and code generation
Jun 14, 2023
Cecil Ward
Re: assert and static assert and code generatio
Jun 14, 2023
Cecil Ward
Jun 15, 2023
FeepingCreature
Jun 15, 2023
Rene Zwanenburg
Jun 15, 2023
Cecil Ward
Jun 16, 2023
max haughton
Jun 16, 2023
Quirin Schroll
June 14, 2023
The compilers should surely be able to use the presence of static asserts to enable better code generation since the direction of some later conditional branches can be known, value ranges can be known, all kinds of potential good stuff. Is that correct?

If assert without the static is implemented as debug assert then unless the compiler makes an exception and does not all the ‘debug’-conditional to simply wipe out the assert entirely the compiler won’t gain the benefit of the information available from an assert.

There is of course the difficulty that the non-static assert can not (necessarily) be evaluated at compile-time. But of course the _bool value_ of the assert text-expression is always known.

Is there a way of testing whether or not something can be evaluated at compile-time or really requires run-time evaluation? If so, is it something we could use routinely? I could really use this in certain templates, for the case where there are several specialisations that use parameters with special constant values and then there is the ‘otherwise’ default expansion of the template, which has an argument that is not a known compile-time constant.

So could non-static asserts that do not in fact require run-time evaluation be convertible to an effective status assert, with all the benefits possible in code generation?
June 14, 2023

On 6/14/23 9:00 AM, Cecil Ward wrote:

>

The compilers should surely be able to use the presence of static asserts to enable better code generation since the direction of some later conditional branches can be known, value ranges can be known, all kinds of potential good stuff. Is that correct?

A static assert is telling the compiler if the condition isn't true at compile time, to halt compilation. That is, static asserts have no bearing on semantic meaning other than to say "this isn't compilable". By definition, the compiler knows whether the assert is true or not, so there is no "hint" here for better code generation that it couldn't already gain from it's own knowledge.

In contrast, an assert is saying a condition should be true at runtime. This can testify for something that the compiler can't know at compile time. In this case, the compiler can take hints from the assert to alter code generation (i.e. skip some code that is redundant given the assert).

>

If assert without the static is implemented as debug assert then unless the compiler makes an exception and does not all the ‘debug’-conditional to simply wipe out the assert entirely the compiler won’t gain the benefit of the information available from an assert.

The compiler can use the hint of the assert whether it generates code to perform the assert or not.

>

There is of course the difficulty that the non-static assert can not (necessarily) be evaluated at compile-time. But of course the bool value of the assert text-expression is always known.

I'm not sure what you mean here.

>

Is there a way of testing whether or not something can be evaluated at compile-time or really requires run-time evaluation? If so, is it something we could use routinely? I could really use this in certain templates, for the case where there are several specialisations that use parameters with special constant values and then there is the ‘otherwise’ default expansion of the template, which has an argument that is not a known compile-time constant.

static if(__traits(compiles, () {enum x = someExprToEvalAtCompileTime;})) {...}
>

So could non-static asserts that do not in fact require run-time evaluation be convertible to an effective status assert, with all the benefits possible in code generation?

A static assert is not evaluated the same as a runtime assert. They aren't interchangeable because they aren't solving the same problem.

To give you an example:

void foo1()
{
   if(condThatShouldAlwaysBeTrue)
   {
       performSomeWork();
       return;
   }
   assert(false, "Should not get here");
}

void foo2()
{
   if(condThatShouldAlwaysBeTrue)
   {
       performSomeWork();
       return;
   }
   static assert(false, "Should not get here");
}

foo1 will compile and run just fine, as long as the asserted condition is always true.

foo2 will not compile. Why? Because even to compile static assert(false) means to halt compilation. It doesn't matter if at runtime it will never get there.

-Steve

June 14, 2023
On Wednesday, 14 June 2023 at 14:53:21 UTC, Steven Schveighoffer wrote:
> On 6/14/23 9:00 AM, Cecil Ward wrote:
>> [...]
>
> A static assert is telling the compiler if the condition isn't true at compile time, to *halt compilation*. That is, static asserts have no bearing on semantic meaning other than to say "this isn't compilable". By definition, the compiler knows whether the assert is true or not, so there is no "hint" here for better code generation that it couldn't already gain from it's own knowledge.
>
> [...]

Many thanks for the tip about traits. I had used that or something similar - memory fails me - to check on whether some expression was legal in some sense, in a condition on a template, but I have not used it like this, so very valuable thanks.

What if I say something like assert( x < 100 ); and let’s say I’m giving the compiler information in the maximum conceivable range of values, 0…99, so the compiler could generate better code as it now knows that the expression x < 200 is false and let’s say that the assert is the only source of information about the value range.
June 14, 2023

On 6/14/23 11:23 AM, Cecil Ward wrote:

>

What if I say something like assert( x < 100 ); and let’s say I’m giving the compiler information in the maximum conceivable range of values, 0…99, so the compiler could generate better code as it now knows that the expression x < 200 is false and let’s say that the assert is the only source of information about the value range.

Yes, err.... if x < 100, then x is surely < 200? But I think that's just a typo. That's the kind of thing that asserts can do, even if the assert is not generated into code.

-Steve

June 15, 2023

On Wednesday, 14 June 2023 at 15:53:01 UTC, Steven Schveighoffer wrote:

>

On 6/14/23 11:23 AM, Cecil Ward wrote:

>

What if I say something like assert( x < 100 ); and let’s say I’m giving the compiler information in the maximum conceivable range of values, 0…99, so the compiler could generate better code as it now knows that the expression x < 200 is false and let’s say that the assert is the only source of information about the value range.

Yes, err.... if x < 100, then x is surely < 200? But I think that's just a typo. That's the kind of thing that asserts can do, even if the assert is not generated into code.

-Steve

I think the idea is more that:

assert(x < 100);
...
if (x < 200) {

And the if should be optimized into a constant if (true).
In other words, if DMD does value range propagation based on asserts.

June 15, 2023

On Thursday, 15 June 2023 at 08:55:49 UTC, FeepingCreature wrote:

>

I think the idea is more that:

assert(x < 100);
...
if (x < 200) {

And the if should be optimized into a constant if (true).
In other words, if DMD does value range propagation based on asserts.

There were some big wars about this almost a decade ago. It was Walter's intention to indeed use asserts for optimization, even if the asserts themselves weren't compiled in. IIRC it was decided to not do this in the end.

Here's one of the threads about the subject from back then in case you have some time to kill:
https://forum.dlang.org/post/lrbpvj$mih$1@digitalmars.com

June 15, 2023
On Wednesday, 14 June 2023 at 15:53:01 UTC, Steven Schveighoffer wrote:
> On 6/14/23 11:23 AM, Cecil Ward wrote:
>
>> What if I say something like assert( x < 100 ); and let’s say I’m giving the compiler information in the maximum conceivable range of values, 0…99, so the compiler could generate better code as it now knows that the expression x < 200 is false and let’s say that the assert is the only source of information about the value range.
>
> Yes, err.... if x < 100, then x is surely < 200? But I think that's just a typo. That's the kind of thing that asserts can do, even if the assert is not generated into code.
>
> -Steve

It wasn’t a typo. I intended to give an second expression that is trivially always true. The point was that later if statements, even if their test is not identical to the test in the assert, can be removed sometimes.

To test things out, I wrote an assume() routine which takes an argument that is a bool. It uses a special GCC builtin and can make later if statements go away. It worked well and seems sufficiently valuable that I will try to use it a lot in future. The assume statement is like an assert.
June 15, 2023

On 6/15/23 5:32 PM, Cecil Ward wrote:

>

On Wednesday, 14 June 2023 at 15:53:01 UTC, Steven Schveighoffer wrote:

>

On 6/14/23 11:23 AM, Cecil Ward wrote:

>

What if I say something like assert( x < 100 ); and let’s say I’m giving the compiler information in the maximum conceivable range of values, 0…99, so the compiler could generate better code as it now knows that the expression x < 200 is false and let’s say that the assert is the only source of information about the value range.

Yes, err.... if x < 100, then x is surely < 200? But I think that's just a typo. That's the kind of thing that asserts can do, even if the assert is not generated into code.

It wasn’t a typo. I intended to give an second expression that is trivially always true. The point was that later if statements, even if their test is not identical to the test in the assert, can be removed sometimes.

Quote: "it now knows that the expression x < 200 is false"

That's what I assumed was the typo.

>

To test things out, I wrote an assume() routine which takes an argument that is a bool. It uses a special GCC builtin and can make later if statements go away. It worked well and seems sufficiently valuable that I will try to use it a lot in future. The assume statement is like an assert.

It appears that even ldc doesn't use the assert to make any deductions when the assert is not compiled-in. So maybe not implemented, but it would be to spec.

-Steve

June 16, 2023

On Friday, 16 June 2023 at 00:46:28 UTC, Steven Schveighoffer wrote:

>

On 6/15/23 5:32 PM, Cecil Ward wrote:

>

[...]

Quote: "it now knows that the expression x < 200 is false"

That's what I assumed was the typo.

>

[...]

It appears that even ldc doesn't use the assert to make any deductions when the assert is not compiled-in. So maybe not implemented, but it would be to spec.

-Steve
LDC can't see the assertion at all, it's gone by the time the backend is in play.

June 16, 2023

On Wednesday, 14 June 2023 at 13:00:39 UTC, Cecil Ward wrote:

>

The compilers should surely be able to use the presence of static asserts to enable better code generation since the direction of some later conditional branches can be known, value ranges can be known, all kinds of potential good stuff. Is that correct?

“the presence of static asserts to enable better code generation” → the presence of [non-static] asserts to enable better code generation

In principle, yes. IMO it’s a big issue that D doesn’t actually require the expression and message in an assert to be pure and const. That would alleviate the probably rare, but annoying-to-debug case where the side-effect of an assert does do things.

What you suggest is implemented in C++23: C++ attribute: assume

C and C++ had assert as a macro for ages. The C++ committee came to the conclusion that validating contracts and giving the compiler optimization hints are two different intentions and in fact so different that they warrant separate syntax.

The cppreference page says:

>

Since assumptions cause undefined behavior if they do not hold, they should be used sparingly. They are not intended as a mechanism to document the preconditions of a function or to diagnose violations of preconditions. Also, it should not be presumed, without checking, that the compiler actually makes use of any particular assumption.

For D, the catch would be: If an assert incurred undefined behavior, which it has to for the condition to meaningfully affect the optimization, assert would be invalid to use in @safe code. Consider that in @safe functions, array bounds checking is enabled even in release mode, because it wouldn’t be @safe if it wasn’t.

We could add to D a construct that does what C++23’s assume does; making assert mean that was decided against in the past. A pragma is ideal because a compiler is free to ignore it and ignoring an optimization hint is factually perfectly okay. Also, a pragma can be applied to individual statements and blocks of statements.

I filed an enhancement issue: https://issues.dlang.org/show_bug.cgi?id=23996