Thread overview
[Issue 11506] New: pure evaluation should be shortcircuited
Nov 12, 2013
Temtaime
Nov 13, 2013
Walter Bright
November 12, 2013
https://d.puremagic.com/issues/show_bug.cgi?id=11506

           Summary: pure evaluation should be shortcircuited
           Product: D
           Version: D2
          Platform: All
        OS/Version: All
            Status: NEW
          Severity: enhancement
          Priority: P2
         Component: DMD
        AssignedTo: nobody@puremagic.com
        ReportedBy: andrei@erdani.com


--- Comment #0 from Andrei Alexandrescu <andrei@erdani.com> 2013-11-12 13:44:14 PST ---
Consider:

pure int fun()
{
    for (;;) {}
    return 42;
}

int main(string[] args)
{
    return fun(), cast(int) args.length;
}

This program never ends. It should not evaluate fun() at all. Also:

pure int fun();

int main(string[] args)
{
    return fun() + fun();
}

This program doesn't link, but the generated code reveals that fun() is called twice. It should only called once.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 12, 2013
https://d.puremagic.com/issues/show_bug.cgi?id=11506


Temtaime <temtaime@gmail.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |temtaime@gmail.com


--- Comment #1 from Temtaime <temtaime@gmail.com> 2013-11-12 15:04:07 PST ---
Hi Andrei.

I disagree with first example.

Sometimes it's useful to use comma operator. And it must evaluate all the expressions.

Changing the behavior can break some code that rely on it and surprise newbies as for example in c++ it always evaluate all exprs.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 12, 2013
https://d.puremagic.com/issues/show_bug.cgi?id=11506


bearophile_hugs@eml.cc changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bearophile_hugs@eml.cc


--- Comment #2 from bearophile_hugs@eml.cc 2013-11-12 15:26:47 PST ---
(In reply to comment #0)
> Also:
> 
> pure int fun();
> 
> int main(string[] args)
> {
>     return fun() + fun();
> }
> 
> This program doesn't link, but the generated code reveals that fun() is called twice. It should only called once.

If I compile this code with:
dmd -O -release -noboundscheeck temp.d


int fun() pure nothrow { return 0; }
int main() {
   return fun + fun;
}


It contains only one call to fun (and add EAX,EAX doubles its result):

_D4temp3funFNaNbZi:
        xor EAX,EAX
        ret

__Dmain:
L0:     push    EAX
        call    near ptr _D4temp3funFNaNbZi
        add EAX,EAX
        pop ECX
        ret

------------------------

But if I remove the nothrow:

int fun() pure { return 0; }
int main() {
   return fun + fun;
}


It shows both calls to fun:

_D4temp3funFNaZi:
        xor EAX,EAX
        ret

__Dmain:
L0:     push    EAX
        call    near ptr _D4temp3funFNaZi
        push    EAX
        sub ESP,4
        call    near ptr _D4temp3funFNaZi
        add ESP,4
        mov ECX,EAX
        pop EAX
        add EAX,ECX
        pop ECX
        ret


The ability to throw is an effect (a side effect) and it can't be ignored. On the other hand fun takes no arguments, so I think the two calls to fun fun can't decide to throw or not throw independently. So I think calling fun only once is OK even if it doesn't have a nothrow annotation.

On the other hand two calls to a a function foo like this:


int foo(in size_t x) pure {
    if (x & 1) throw new Exception("");
    return 10;
}
int main(in string[] args) {
   return fun(args.length) + fun(args.length);
}


Can't be replaced with a single call.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 12, 2013
https://d.puremagic.com/issues/show_bug.cgi?id=11506



--- Comment #3 from Andrei Alexandrescu <andrei@erdani.com> 2013-11-12 15:45:13 PST ---
Very interesting, thanks bearophile.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------
November 13, 2013
https://d.puremagic.com/issues/show_bug.cgi?id=11506


Walter Bright <bugzilla@digitalmars.com> changed:

           What    |Removed                     |Added
----------------------------------------------------------------------------
                 CC|                            |bugzilla@digitalmars.com


--- Comment #4 from Walter Bright <bugzilla@digitalmars.com> 2013-11-13 12:22:31 PST ---
Yes, bearophile's right, I forgot about nothrow.

But the compiler is a bit conservative by requiring nothrow. For throwing pure
functions, the foo()+foo() case can still be replaced with 2*foo() if the
arguments to foo are identical, even if foo throws. The obvious case of this is
the no-argument case, which bearophile mentioned.

Also, if foo() returns memory that it new'd, it cannot be elided:

    pure nothrow string foo();
    return foo() ~ foo();

so things are a bit complicated, but there's still optimization opportunity.

-- 
Configure issuemail: https://d.puremagic.com/issues/userprefs.cgi?tab=email
------- You are receiving this mail because: -------