Code:
------------------------------------------
import std.stdio;
void main() {
void loop(void delegate(int x) dg) {
foreach (i; 0..10) {
dg(i);
}
}
loop((x) {
writeln(x);
});
}
------------------------------------------
Compile with: dmd -O -inline -release -noboundscheck
Here's the disassembly:
------------------------------------------
0000000000454f80 <_Dmain>:
454f80: 55 push %rbp
454f81: 48 8b ec mov %rsp,%rbp
454f84: 48 83 ec 18 sub $0x18,%rsp
454f88: 53 push %rbx
454f89: 48 b9 e8 4f 45 00 00 movabs $0x454fe8,%rcx
454f90: 00 00 00
454f93: 48 31 c0 xor %rax,%rax
454f96: 48 89 45 f0 mov %rax,-0x10(%rbp)
454f9a: 48 89 4d f8 mov %rcx,-0x8(%rbp)
454f9e: 31 db xor %ebx,%ebx
454fa0: 8b f3 mov %ebx,%esi
454fa2: 48 8b 7d f0 mov -0x10(%rbp),%rdi
454fa6: 48 ff 55 f8 rex.W callq *-0x8(%rbp)
454faa: ff c3 inc %ebx
454fac: 83 fb 0a cmp $0xa,%ebx
454faf: 72 ef jb 454fa0 <_Dmain+0x20>
454fb1: 31 c0 xor %eax,%eax
454fb3: 5b pop %rbx
454fb4: 48 8b e5 mov %rbp,%rsp
454fb7: 5d pop %rbp
454fb8: c3 retq
454fb9: 90 nop
454fba: 90 nop
454fbb: 90 nop
0000000000454fbc <_D4test4mainFZv4loopMFDFiZvZv>:
454fbc: 55 push %rbp
454fbd: 48 8b ec mov %rsp,%rbp
454fc0: 48 83 ec 28 sub $0x28,%rsp
454fc4: 53 push %rbx
454fc5: 48 89 75 f0 mov %rsi,-0x10(%rbp)
454fc9: 48 89 55 f8 mov %rdx,-0x8(%rbp)
454fcd: 31 db xor %ebx,%ebx
454fcf: 8b f3 mov %ebx,%esi
454fd1: 48 8b 7d f0 mov -0x10(%rbp),%rdi
454fd5: 48 ff 55 f8 rex.W callq *-0x8(%rbp)
454fd9: ff c3 inc %ebx
454fdb: 83 fb 0a cmp $0xa,%ebx
454fde: 72 ef jb 454fcf <_D4test4mainFZv4loopMFDFiZvZv+0x13>
454fe0: 5b pop %rbx
454fe1: 48 8b e5 mov %rbp,%rsp
454fe4: 5d pop %rbp
454fe5: c3 retq
454fe6: 90 nop
454fe7: 90 nop
0000000000454fe8 <_D4test4mainFZv16__T9__lambda2TiZ9__lambda2MFiZv>:
454fe8: 55 push %rbp
454fe9: 48 8b ec mov %rsp,%rbp
454fec: 48 83 ec 10 sub $0x10,%rsp
454ff0: 48 89 f2 mov %rsi,%rdx
454ff3: be 0a 00 00 00 mov $0xa,%esi
454ff8: 48 bf 00 af 6c 00 00 movabs $0x6caf00,%rdi
454fff: 00 00 00
455002: e8 59 02 00 00 callq 455260 <_D3std5stdio4File14__T5writeTiTaZ5writeMFiaZv>
455007: 48 8b e5 mov %rbp,%rsp
45500a: 5d pop %rbp
45500b: c3 retq
------------------------------------------
There are two enhancements (optimizations) possible here:
1) Since loop() is inside main(), and there are no escaping references to it, it can't possibly be called from anywhere else. Since it's already inlined in main, there is no point in emitting the function body as a separate function. So the function body should be omitted.
2) Since the lambda delegate body is known at compile-time, and this is the only place it's used, the lambda should be inlined as well. Currently it's not (main still takes the address of the lambda and calls it from the inlined loop).
Comment #1 by monarchdodra — 2013-11-11T12:58:56Z
Is this a duplicate of:
https://d.puremagic.com/issues/show_bug.cgi?id=10848
"Issue 10848 - Compiler should always try to inlining a direct lambda call"
?
Or is the "delegate" something specific that's not covered in 10848?
(10848 has a lot more detail, so it seems smarter to mark this one as duplicate...)
Comment #2 by public — 2014-08-09T12:10:31Z
It is related but somewhat different. Issue 10848 is about frontend-based inlining that will make it a hard language spec guarantee. This one about dmd backend deficiency.
Comment #3 by bugzilla — 2015-08-22T05:26:18Z
The reason the call to loop() gets inlined and the call to dg(i) does not is because dg is a function pointer. It is not until the optimizer is run that dg's value is discovered to be constant, and thus can be inlined.
Unfortunately, fixing this would require moving data flow analysis into the front end, or inlining into the optimizer, neither of which is simple.
Huh, just re-read Walter's comment, and it suddenly struck me: inlining is currently done in the front-end? Not in the optimizer? That strikes me as rather odd, since isn't inlining usually the domain of the optimizer?
Comment #6 by robert.schadek — 2024-12-13T18:05:07Z