Jump to page: 1 2
Thread overview
Schrodinger's CTFE
Jul 16, 2020
Paul Backus
Jul 16, 2020
Paul Backus
Jul 17, 2020
Ben Jones
July 15, 2020
I found a weird workaround to the "Unreachable code" problem.

In a nutshell, code that is sometimes reachable given the correct template parameters can be flagged as unreachable when instantiated with different ones.

An example (compile with -w):

int foo(bool x)()
{
   if(x) return 1;
   return 2;
}

void main()
{
   auto x = foo!false(); // ok
   auto y = foo!true(); // Warning: "return 2;" is unreachable
}

A weird solution is to declare a boolean value somewhere as always true, and use it to "break" free from the constant folding:

int foo(bool x)()
{
   auto b = true;
   if(x && b) return 1;
   return 2;
}

The boolean will likely be removed by the optimizer, but it's kind of a clunky solution.

However, we also have a special boolean __ctfe that is treated as a runtime variable for things like checking reachability, but treated as a compile-time constant for code generation (so truly unreachable code is simply removed instead of being flagged for unreachability).

This also works, but is only good for compile time or runtime (depending on your intentions for foo):

int foo(bool x)()
{
   if(x && __ctfe) return 1; // or !__ctfe for runtime
   return 2;
}

I was going to ask if we could have another special variable that is like __ctfe in these respects, but is *always* true for both runtime and compile time, but I actually found out a weird trick. This works, and does the same thing at both runtime and compile time, plus will only generate one branch for the runtime (without even the optimizer turned on):

int foo(bool x)()
{
   if(x && (__ctfe || !__ctfe)) return 1;
   return 2;
}

I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?

-Steve
July 15, 2020
On 7/15/20 4:33 PM, Steven Schveighoffer wrote:
> 
> int foo(bool x)()
> {
>     if(x && (__ctfe || !__ctfe)) return 1;
>     return 2;
> }
> 
> I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?

Would a function work?

@property
bool __schro() pure nothrow @nogc { return __ctfe || !__ctfe; }

July 15, 2020
On 7/15/20 6:44 PM, Andrei Alexandrescu wrote:
> On 7/15/20 4:33 PM, Steven Schveighoffer wrote:
>>
>> int foo(bool x)()
>> {
>>     if(x && (__ctfe || !__ctfe)) return 1;
>>     return 2;
>> }
>>
>> I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?
> 
> Would a function work?
> 
> @property
> bool __schro() pure nothrow @nogc { return __ctfe || !__ctfe; }
> 

I actually tried that, and it does prevent the "Unreachable statement" problem, but the runtime code contains a call to that function where it is used, which means that it's not as effective in all cases.

With a straight usage of the expression, you get (without -inline or -O):

pure nothrow @nogc @safe int onlineapp.foo!(false).foo():
		push	RBP
		mov	RBP,RSP
		mov	EAX,2
		pop	RBP
		ret
		add	[RAX],AL

pure nothrow @nogc @safe int onlineapp.foo!(true).foo():
		push	RBP
		mov	RBP,RSP
		mov	EAX,1
		pop	RBP
		ret
		add	[RAX],AL

With the schro function, you get the same result for foo!false (as the short circuiting of the if statement prunes out the check of __ctfe), but you get this for foo!true:

pure nothrow @nogc int onlineapp.foo!(true).foo():
		push	RBP
		mov	RBP,RSP
		call	  pure nothrow @property @nogc bool onlineapp.schro()@PLT32
		test	AL,AL
		je	L14
		mov	EAX,1
		pop	RBP
		ret
L14:		mov	EAX,2
		pop	RBP
		ret
		add	[RAX],AL

With -inline, it results as the same thing.

Note that if you put it in a function, you can just return true, you don't need the __ctfe || !__ctfe.

-Steve
July 15, 2020
On 7/15/20 6:58 PM, Steven Schveighoffer wrote:
> With -inline, it results as the same thing.

I should clarify, I meant it results as the same thing as the hand-written check.

-Steve
July 16, 2020
On 7/15/20 6:58 PM, Steven Schveighoffer wrote:
> On 7/15/20 6:44 PM, Andrei Alexandrescu wrote:
>> On 7/15/20 4:33 PM, Steven Schveighoffer wrote:
>>>
>>> int foo(bool x)()
>>> {
>>>     if(x && (__ctfe || !__ctfe)) return 1;
>>>     return 2;
>>> }
>>>
>>> I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?
>>
>> Would a function work?
>>
>> @property
>> bool __schro() pure nothrow @nogc { return __ctfe || !__ctfe; }
>>
> 
> I actually tried that, and it does prevent the "Unreachable statement" problem, but the runtime code contains a call to that function where it is used, which means that it's not as effective in all cases.
> 
> With a straight usage of the expression, you get (without -inline or -O):
> 
> pure nothrow @nogc @safe int onlineapp.foo!(false).foo():
>          push    RBP
>          mov    RBP,RSP
>          mov    EAX,2
>          pop    RBP
>          ret
>          add    [RAX],AL
> 
> pure nothrow @nogc @safe int onlineapp.foo!(true).foo():
>          push    RBP
>          mov    RBP,RSP
>          mov    EAX,1
>          pop    RBP
>          ret
>          add    [RAX],AL
> 
> With the schro function, you get the same result for foo!false (as the short circuiting of the if statement prunes out the check of __ctfe), but you get this for foo!true:
> 
> pure nothrow @nogc int onlineapp.foo!(true).foo():
>          push    RBP
>          mov    RBP,RSP
>          call      pure nothrow @property @nogc bool onlineapp.schro()@PLT32
>          test    AL,AL
>          je    L14
>          mov    EAX,1
>          pop    RBP
>          ret
> L14:        mov    EAX,2
>          pop    RBP
>          ret
>          add    [RAX],AL
> 
> With -inline, it results as the same thing.
> 
> Note that if you put it in a function, you can just return true, you don't need the __ctfe || !__ctfe.
> 
> -Steve

I got good news for you then: https://github.com/dlang/dmd/pull/11236
July 16, 2020
On 7/16/20 2:32 AM, Andrei Alexandrescu wrote:
> I got good news for you then: https://github.com/dlang/dmd/pull/11236

That is good news, and will help here!

This should suffice:

@property pure @safe nothrow bool reachable() {
   pragma(inline, true);
   return true;
}

Though I don't know if this is something we should include in Phobos?

-Steve
July 16, 2020
On 7/16/20 12:04 PM, Steven Schveighoffer wrote:
> On 7/16/20 2:32 AM, Andrei Alexandrescu wrote:
>> I got good news for you then: https://github.com/dlang/dmd/pull/11236
> 
> That is good news, and will help here!
> 
> This should suffice:
> 
> @property pure @safe nothrow bool reachable() {
>     pragma(inline, true);
>     return true;
> }
> 
> Though I don't know if this is something we should include in Phobos?

Perhaps in std.benchmark. This function is loosely related to DoNotOptimize in https://github.com/google/benchmark.
July 16, 2020
On 7/16/20 4:51 PM, Andrei Alexandrescu wrote:
> On 7/16/20 12:04 PM, Steven Schveighoffer wrote:
>> On 7/16/20 2:32 AM, Andrei Alexandrescu wrote:
>>> I got good news for you then: https://github.com/dlang/dmd/pull/11236
>>
>> That is good news, and will help here!
>>
>> This should suffice:
>>
>> @property pure @safe nothrow bool reachable() {
>>     pragma(inline, true);
>>     return true;
>> }
>>
>> Though I don't know if this is something we should include in Phobos?
> 
> Perhaps in std.benchmark. This function is loosely related to DoNotOptimize in https://github.com/google/benchmark.

Possibly. But this is not exactly for benchmarking, it's for making something compile when it normally doesn't.

I don't know if it's related to DoNotOptimize. In fact, we want it to be optimized away completely, and be replaced with `true`.

I was thinking std.meta, since it's a tool for compile-time branches/loops.

-Steve
July 16, 2020
On Wednesday, 15 July 2020 at 20:33:23 UTC, Steven Schveighoffer wrote:
> I found a weird workaround to the "Unreachable code" problem.
>
[...]
>
> int foo(bool x)()
> {
>    if(x && (__ctfe || !__ctfe)) return 1;
>    return 2;
> }
>
> I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?
>
> -Steve

Am I the only one who thinks that this is completely insane?

Maybe if the unreachable code warning is causing us this much trouble, we should just get rid of it. Personally, the number of times it's done anything helpful for me are far outweighed by the number of times it's gotten in my way.
July 16, 2020
On Wednesday, 15 July 2020 at 20:33:23 UTC, Steven Schveighoffer wrote:
> I found a weird workaround to the "Unreachable code" problem.
>
[...]
>
> int foo(bool x)()
> {
>    if(x && (__ctfe || !__ctfe)) return 1;
>    return 2;
> }
>
> I think this is a way to fix some of those static foreach loops with returns which can have these reachability problems. Only, it's really verbose and ugly. Maybe we still should have that other variable?
>
> -Steve

Is it just me, or does anyone else think that this is completely insane?

Maybe if the unreachable code warning is causing us this much trouble, we should just get rid of it. Personally, the number of times it's done anything helpful for me are far outweighed by the number of times it's gotten in my way.
« First   ‹ Prev
1 2