Bug 11506 – pure evaluation should be shortcircuited

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2013-11-12T13:44:14Z
Last change time
2024-12-13T18:13:56Z
Assigned to
No Owner
Creator
Andrei Alexandrescu
Moved to GitHub: dmd#18712 →

Comments

Comment #0 by andrei — 2013-11-12T13:44:14Z
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.
Comment #1 by temtaime — 2013-11-12T15:04:07Z
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.
Comment #2 by bearophile_hugs — 2013-11-12T15:26:47Z
(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.
Comment #3 by andrei — 2013-11-12T15:45:13Z
Very interesting, thanks bearophile.
Comment #4 by bugzilla — 2013-11-13T12:22:31Z
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.
Comment #5 by robert.schadek — 2024-12-13T18:13:56Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18712 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB