digitalmars.D - Dead Code Elimination
- libxmoc (6/6) Oct 18 I noticed that dmd doesn't do DCE, my executable is quite large
- Richard (Rikki) Andrew Cattermole (7/14) Oct 18 Doesn't surprise me.
- Walter Bright (2/3) Oct 18 Yes, it does. Use the -O switch.
- Richard (Rikki) Andrew Cattermole (27/31) Oct 18 dmd -O
- Walter Bright (7/43) Oct 21 Current version of dmd:
- libxmoc (33/36) Oct 21 Even with -O it doesn't work:
- Walter Bright (4/5) Oct 21 It's the job of the linker to remove uncalled functions.
- Walter Bright (25/25) Oct 18 ```d
- Alexandru Ermicioi (2/3) Oct 19 So is it just 32 bit only?
- Richard (Rikki) Andrew Cattermole (11/15) Oct 19 For instruction/block DCE, no.
- Walter Bright (9/13) Oct 19 See https://github.com/dlang/dmd/pull/24074
- Adam Wilson (3/14) Oct 21 Has something doesn't mean it works very well. See Rikki's
- Richard (Rikki) Andrew Cattermole (5/21) Oct 21 In this case I understand it to have been out of scope.
- Walter Bright (2/3) Oct 21 Rikki's example is not a flow analysis issue. Besides, it's been correct...
- user1234 (13/19) Oct 21 It's hard to say when something is used, simple example:
- user1234 (3/13) Oct 21 I should have said, more briefly, that this brings the question
- Paolo Invernizzi (24/44) Oct 21 Not an answer on the compiler side, but related, since today
- user1234 (8/25) Oct 21 I see what you mean. One thing however: you can forgive an error
I noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.
Oct 18
On 19/10/2026 4:33 AM, libxmoc wrote:I noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.Doesn't surprise me. DCE can only work on shared libraries, executables, and local to object file symbols (mostly private but also not limited to that i.e. ModuleInfo must emit). The way dmd has traditionally handled it is to emit static libraries and let the linker deal with it.
Oct 18
On 10/18/2026 8:33 AM, libxmoc wrote:I noticed that dmd doesn't do DCEYes, it does. Use the -O switch.
Oct 18
On 19/10/2026 7:36 PM, Walter Bright wrote:On 10/18/2026 8:33 AM, libxmoc wrote:dmd -O ```d void main() { } void foo() { } ``` ```asm.text._Dmain segment assume CS:.text._Dmain _Dmain: push RBP mov RBP,RSP xor EAX,EAX pop RBP ret .text._Dmain ends .text.void onlineapp.foo() segment assume CS:.text.void onlineapp.foo() void onlineapp.foo(): push RBP mov RBP,RSP pop RBP ret add [RAX],AL .text.void onlineapp.foo() ends ```I noticed that dmd doesn't do DCEYes, it does. Use the -O switch.
Oct 18
On 10/18/2026 11:45 PM, Richard (Rikki) Andrew Cattermole wrote:On 19/10/2026 7:36 PM, Walter Bright wrote:Current version of dmd: _Dmain: 0000: 31 C0 xor EAX,EAX 0002: C3 ret _D4dead3fooFZv: 0000: C3 retOn 10/18/2026 8:33 AM, libxmoc wrote:dmd -O ```d void main() { } void foo() { } ``` ```asm.text._Dmain segment assume CS:.text._Dmain _Dmain: push RBP mov RBP,RSP xor EAX,EAX pop RBP ret .text._Dmain ends .text.void onlineapp.foo() segment assume CS:.text.void onlineapp.foo() void onlineapp.foo(): push RBP mov RBP,RSP pop RBP ret add [RAX],AL .text.void onlineapp.foo() ends ```I noticed that dmd doesn't do DCEYes, it does. Use the -O switch.
Oct 21
On Monday, 19 October 2026 at 06:36:33 UTC, Walter Bright wrote:On 10/18/2026 8:33 AM, libxmoc wrote:Even with -O it doesn't work: ```D import bar; extern(C) void main() { assert(1); } ``` ```D module bar; void removeme() { assert(0); } ``` ``` $ dmd -O -i -betterC -vasm app.d main: 0000: 31 C0 xor EAX,EAX 0002: C3 ret _D3bar8removemeFZv: 0000: 55 push RBP 0001: 48 8B EC mov RBP,RSP 0004: 48 8D 0D FC FF FF FF lea RCX,[0FFFFFFFCh][RIP] 000b: BA 05 00 00 00 mov EDX,5 0010: 48 8D 35 FC FF FF FF lea RSI,[0FFFFFFFCh][RIP] 0017: 48 8D 3D FC FF FF FF lea RDI,[0FFFFFFFCh][RIP] 001e: E8 00 00 00 00 call L0 0023: 5D pop RBP 0024: C3 ret ``` I disassembled both app and app.o, and `removeme` exists in bothI noticed that dmd doesn't do DCEYes, it does. Use the -O switch.
Oct 21
On 10/21/2026 12:10 AM, libxmoc wrote:I disassembled both app and app.o, and `removeme` exists in bothIt's the job of the linker to remove uncalled functions. The compiler does not know what other code the result may be linked with, and what functions that other code calls.
Oct 21
```d
int foo() {
int x = 3;
if (x == 2)
return 4;
return 0;
}
```
dmd -c -vasm -m32 dead.d:
```
_D4dead3fooFZi:
0000: B8 03 00 00 00 mov EAX,3
0005: 83 F8 02 cmp EAX,2
0008: 75 02 jne Lc
000a: 40 inc EAX
000b: C3 ret
000c: 31 C0 xor EAX,EAX
000e: C3 ret
```
dmd -c -vasm -O -m32 dead.d:
```
_D4dead3fooFZi:
0000: 31 C0 xor EAX,EAX
0002: C3 ret
```
Oct 18
On Monday, 19 October 2026 at 06:46:13 UTC, Walter Bright wrote:dmd -c -vasm -O -m32 dead.d:So is it just 32 bit only?
Oct 19
On 19/10/2026 8:04 PM, Alexandru Ermicioi wrote:On Monday, 19 October 2026 at 06:46:13 UTC, Walter Bright wrote:For instruction/block DCE, no. dmd -O -m64 ```asm int onlineapp.foo(): push RBP mov RBP,RSP xor EAX,EAX pop RBP ret ```dmd -c -vasm -O -m32 dead.d:So is it just 32 bit only?
Oct 19
On 10/19/2026 12:04 AM, Alexandru Ermicioi wrote:On Monday, 19 October 2026 at 06:46:13 UTC, Walter Bright wrote:See https://github.com/dlang/dmd/pull/24074 The alwaysframe is specific to OSX64. But it does eliminate the dead code due to a DFA optimization called "constant propagation". There's a persistent belief that DMD does not do data flow analysis optimizations. DMD does rely on the linker to remove unused functions, because the separate compilation model means one doesn't not know if a function gets called or not. Only the linker has the full information.dmd -c -vasm -O -m32 dead.d:So is it just 32 bit only?
Oct 19
On Monday, 19 October 2026 at 07:50:10 UTC, Walter Bright wrote:On 10/19/2026 12:04 AM, Alexandru Ermicioi wrote:Has something doesn't mean it works very well. See Rikki's example.On Monday, 19 October 2026 at 06:46:13 UTC, Walter Bright wrote:See https://github.com/dlang/dmd/pull/24074 The alwaysframe is specific to OSX64. But it does eliminate the dead code due to a DFA optimization called "constant propagation". There's a persistent belief that DMD does not do data flow analysis optimizations.dmd -c -vasm -O -m32 dead.d:So is it just 32 bit only?
Oct 21
On 21/10/2026 8:24 PM, Adam Wilson wrote:On Monday, 19 October 2026 at 07:50:10 UTC, Walter Bright wrote:In this case I understand it to have been out of scope. DCE'ing symbols has requirements that C doesn't provide. In this case there is no harm in it being missing, but there is harm if it wasn't designed correctly (see -allinst).On 10/19/2026 12:04 AM, Alexandru Ermicioi wrote:Has something doesn't mean it works very well. See Rikki's example.On Monday, 19 October 2026 at 06:46:13 UTC, Walter Bright wrote:See https://github.com/dlang/dmd/pull/24074 The alwaysframe is specific to OSX64. But it does eliminate the dead code due to a DFA optimization called "constant propagation". There's a persistent belief that DMD does not do data flow analysis optimizations.dmd -c -vasm -O -m32 dead.d:So is it just 32 bit only?
Oct 21
On 10/21/2026 12:24 AM, Adam Wilson wrote:Has something doesn't mean it works very well. See Rikki's example.Rikki's example is not a flow analysis issue. Besides, it's been corrected.
Oct 21
On Sunday, 18 October 2026 at 15:33:17 UTC, libxmoc wrote:I noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.It's hard to say when something is used, simple example: ```d void unused(){} void main() { import core.stdc.stdio; printf("%p\n",&unused); } ``` same problem with virtual functions. Otherwise what you call DCE is rather WPO (whole program optimization).
Oct 21
On Wednesday, 21 October 2026 at 09:32:25 UTC, user1234 wrote:On Sunday, 18 October 2026 at 15:33:17 UTC, libxmoc wrote:I should have said, more briefly, that this brings the question of aliasing.I noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.It's hard to say when something is used, simple example: [...]
Oct 21
On Wednesday, 21 October 2026 at 09:32:25 UTC, user1234 wrote:On Sunday, 18 October 2026 at 15:33:17 UTC, libxmoc wrote:Not an answer on the compiler side, but related, since today djs-vscode (0.2.0) shows unused code in the editor. It walks the semantic AST of the whole program from `main` (the same thing `-i` compiles) and greys out every declaration it cannot reach: functions, methods, templates, aggregates, enums, mutable globals. Hovering tells you why ("unreferenced", "only referenced from unreachable code", ...). Because the walk runs after semantic analysis, string mixins are already expanded and `__traits` resolved, so a function named only inside a mixin counts as used. And user1234's example works as it should: `&unused` is a use, so `unused` is not greyed. Screenshot: https://raw.githubusercontent.com/DeepGlance/djs-vscode/main/extension/media/unused.png Hover: https://raw.githubusercontent.com/DeepGlance/djs-vscode/main/extension/media/unused-hover.png Caveats: it is "unused in this build", so code used only under a `version` that is not compiled, or only in a `unittest` when you compile without `-unittest`, is kept rather than greyed; `extern(C)` and exported symbols count as roots; separately compiled libraries are out of sight. It does not touch the binary, it only shows you what the linker would not need. PaoloI noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.It's hard to say when something is used, simple example: ```d void unused(){} void main() { import core.stdc.stdio; printf("%p\n",&unused); } ``` same problem with virtual functions. Otherwise what you call DCE is rather WPO (whole program optimization).
Oct 21
On Wednesday, 21 October 2026 at 14:59:20 UTC, Paolo Invernizzi wrote:On Wednesday, 21 October 2026 at 09:32:25 UTC, user1234 wrote:I see what you mean. One thing however: you can forgive an error from your your code editor. But an error in the backend production will be way more frustrating. I have nothing against the idea of libxmoc, I wanted to note that this should be made with care. Maybe in a first time this could be tried with a compiler warning.On Sunday, 18 October 2026 at 15:33:17 UTC, libxmoc wrote:Not an answer on the compiler side, but related, since today djs-vscode (0.2.0) shows unused code in the editor. [...] PaoloI noticed that dmd doesn't do DCE, my executable is quite large for what it does. Any plan to add that? I tried to do it myself, a simple bool in DSymbol + a visitor (yoinked it from the unused import PR) to scan things, and it shrank build time by ~15% and executable went down to 3.5mb from 5.5mb. Surely someone smarter than me could do even better.It's hard to say when something is used, simple example: [...]
Oct 21









"Richard (Rikki) Andrew Cattermole" <richard cattermole.co.nz> 