www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - assert and static assert and code generation

reply Cecil Ward <cecil cecilward.com> writes:
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?
Jun 14 2023
next sibling parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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.
```d 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: ```d 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
Jun 14 2023
parent reply Cecil Ward <cecil cecilward.com> writes:
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.
Jun 14 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jun 14 2023
next sibling parent reply FeepingCreature <feepingcreature gmail.com> writes:
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.
Jun 15 2023
parent Rene Zwanenburg <renezwanenburg gmail.com> writes:
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
Jun 15 2023
prev sibling parent reply Cecil Ward <cecil cecilward.com> writes:
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.
Jun 15 2023
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jun 15 2023
parent max haughton <maxhaton gmail.com> writes:
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.
Jun 16 2023
prev sibling parent Quirin Schroll <qs.il.paperinik gmail.com> writes:
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`](https://en.cppreference.com/w/cpp/language/attributes/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
Jun 16 2023