www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Schrodinger's CTFE

reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jul 15 2020
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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; }
Jul 15 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jul 15 2020
next sibling parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jul 15 2020
prev sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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
Jul 15 2020
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jul 16 2020
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
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.
Jul 16 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
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
Jul 16 2020
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
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.
Jul 16 2020
parent Steven Schveighoffer <schveiguy gmail.com> writes:
On 7/16/20 6:12 PM, Paul Backus wrote:
 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?
Am I the only one who thinks that this is completely insane?
It's not just you.
 
 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.
I would be fine if the compiler just didn't generate unreachable code, and didn't tell me about it. -Steve
Jul 16 2020
prev sibling parent reply Paul Backus <snarwin gmail.com> writes:
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.
Jul 16 2020
parent Ben Jones <fake fake.fake> writes:
On Thursday, 16 July 2020 at 22:15:05 UTC, Paul Backus wrote:
 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.
I'm taking a look into trying to get rid of "false positive" warnings. I think considering whether a return statement comes from a static if when doing stuff in blockexit.d. Would love to hear suggestions for this/other approaches. My test case is basically: static foreach(whatever){ static if(something){ return false; } } return true; which currently warns whenever one of the early returns is triggered.
Jul 17 2020