Bug 13381 – Two cases of array literal that can be stack-allocated

Status
NEW
Severity
enhancement
Priority
P4
Component
dmd
Product
D
Version
D2
Platform
All
OS
All
Creation time
2014-08-27T09:39:19Z
Last change time
2024-12-13T18:24:57Z
Assigned to
No Owner
Creator
bearophile_hugs
See also
https://issues.dlang.org/show_bug.cgi?id=22306
Moved to GitHub: dmd#18874 →

Comments

Comment #0 by bearophile_hugs — 2014-08-27T09:39:19Z
I think this is an array literal usage pattern where the compiler can allocate the array on the stack, allowing the function foo to be @nogc: void foo(in uint[] a) @nogc { if (a == [1]) {} } void main() {} Currently dmd 2.067alpha gives: temp.d(2,14): Error: array literal in @nogc function foo may cause GC allocation
Comment #1 by yebblies — 2014-08-31T09:05:58Z
I agree.
Comment #2 by bearophile_hugs — 2014-08-31T10:07:39Z
Given this code: void foo(uint[] a) @nogc { if (a == [1, 2]) {} } One way to rewrite it is (the immutable can't be always used): void foo(uint[] a) @nogc { immutable static uint[2] __foo_aux0 == [1, 2]; if (a == __foo_aux0) {} } Or more efficient: void foo(uint[] a) @nogc { if (a.length == 2 && a[0] == 1 && a[1] == 2) {} }
Comment #3 by yebblies — 2014-08-31T10:12:33Z
(In reply to bearophile_hugs from comment #2) > > One way to rewrite it is (the immutable can't be always used): > While re-writes are certainly the simplest way to explain it, using them in the compiler often leads to complications, especially with error messages referring to the re-written code instead of the original. For this case, and similar cases, the compiler can simply type the array literal as a static array, because it knows that == can't escape any references. It already does something like this for indexing array literals.
Comment #4 by bearophile_hugs — 2014-08-31T10:42:32Z
(In reply to yebblies from comment #3) > For this case, and similar cases, the compiler can simply type the array > literal as a static array, because it knows that == can't escape any > references. It already does something like this for indexing array literals. I see. I have suggested a rewrite like this: if (a.length == 2 && a[0] == 1 && a[1] == 2) {} Because most array literals used for equality or comparison are short or very short, so calling == or < on few single items is much more efficient than calling a runtime function that works on whole arrays.
Comment #5 by yebblies — 2014-08-31T10:50:13Z
(In reply to bearophile_hugs from comment #4) > (In reply to yebblies from comment #3) > > > For this case, and similar cases, the compiler can simply type the array > > literal as a static array, because it knows that == can't escape any > > references. It already does something like this for indexing array literals. > > I see. I have suggested a rewrite like this: > > if (a.length == 2 && a[0] == 1 && a[1] == 2) {} > > Because most array literals used for equality or comparison are short or > very short, so calling == or < on few single items is much more efficient > than calling a runtime function that works on whole arrays. Ideally the optimizer would do this by itself, once the literal is stack-allocated. I suspect it already does in some cases for gdc/ldc.
Comment #6 by bearophile_hugs — 2014-08-31T11:00:06Z
(In reply to yebblies from comment #5) > Ideally the optimizer would do this by itself, once the literal is > stack-allocated. I suspect it already does in some cases for gdc/ldc. If I compile this program: bool foo(in int[] a) { return a == [1, 2, 3]; } void main() {} With the latest ldc2 with: ldmd2 -O -release -inline -noboundscheck -output-s test.d The asm of foo is: __D4test3fooFxAiZb: subl $20, %esp movl $3, 4(%esp) movl $__D12TypeInfo_xAi6__initZ, (%esp) calll __d_newarrayvT movl $3, 8(%edx) movl $2, 4(%edx) movl $1, (%edx) movl %edx, 12(%esp) movl %eax, 8(%esp) movl 28(%esp), %eax movl %eax, 4(%esp) movl 24(%esp), %eax movl %eax, (%esp) movl $__D12TypeInfo_Axi6__initZ, 16(%esp) calll __adEq2 testl %eax, %eax setne %al addl $20, %esp ret $8
Comment #7 by yebblies — 2014-08-31T11:02:05Z
(In reply to bearophile_hugs from comment #6) > (In reply to yebblies from comment #5) > > > Ideally the optimizer would do this by itself, once the literal is > > stack-allocated. I suspect it already does in some cases for gdc/ldc. > > If I compile this program: > Like I said, once the literal is stack-allocated it's more likely to work.
Comment #8 by bearophile_hugs — 2014-08-31T11:04:34Z
(In reply to yebblies from comment #7) > Like I said, once the literal is stack-allocated it's more likely to work. OK, another experiment: bool foo(in int[] a) { immutable static int[3] aux = [1, 2, 3]; return a == aux; } void main() {} __D4test3fooFxAiZb: subl $20, %esp movl 28(%esp), %eax movl %eax, 4(%esp) movl 24(%esp), %eax movl %eax, (%esp) movl $__D12TypeInfo_Axi6__initZ, 16(%esp) movl $__D4test3fooFxAiZb3auxyG3i, 12(%esp) movl $3, 8(%esp) calll __adEq2 testl %eax, %eax setne %al addl $20, %esp ret $8
Comment #9 by yebblies — 2014-08-31T11:10:39Z
(In reply to bearophile_hugs from comment #8) > (In reply to yebblies from comment #7) > > > Like I said, once the literal is stack-allocated it's more likely to work. > > OK, another experiment: > > > bool foo(in int[] a) { > immutable static int[3] aux = [1, 2, 3]; > return a == aux; > } > void main() {} > > > __D4test3fooFxAiZb: > subl $20, %esp > movl 28(%esp), %eax > movl %eax, 4(%esp) > movl 24(%esp), %eax > movl %eax, (%esp) > movl $__D12TypeInfo_Axi6__initZ, 16(%esp) > movl $__D4test3fooFxAiZb3auxyG3i, 12(%esp) > movl $3, 8(%esp) > calll __adEq2 > testl %eax, %eax > setne %al > addl $20, %esp > ret $8 Which compiler? I'd say it's worth filing an optimizer enhancement.
Comment #10 by bearophile_hugs — 2014-08-31T11:24:40Z
(In reply to yebblies from comment #9) > Which compiler? The latest ldc2, version 0.14.0 (DMD v2.065, LLVM 3.4.2). dmd is not doing better. I don't know what gdc does in this case. > I'd say it's worth filing an optimizer enhancement. I don't know.
Comment #11 by yebblies — 2014-08-31T11:40:24Z
(In reply to bearophile_hugs from comment #10) > > dmd is not doing better. Haha no surprise there. > > > I'd say it's worth filing an optimizer enhancement. > > I don't know. Actually this is the same thing as general inlining and expanding of array ops, which I think you've already filed.
Comment #12 by bearophile_hugs — 2014-09-16T10:00:37Z
Another case worth optimizing: void main() @nogc { import std.traits: EnumMembers; enum Foo { A, B, C, D } size_t i = 2; Foo[4] foos = [EnumMembers!Foo]; // OK auto r1 = foos[i]; auto r2 = [EnumMembers!Foo][i]; // Error: array literal in @nogc }
Comment #13 by bearophile_hugs — 2014-10-06T17:26:58Z
A case (from Issue 13576 ): immutable int[] a; static this() @nogc { a = [1]; } void main() {} dmd 2.067alpha gives: temp.d(3,9): Error: array literal in @nogc function _staticCtor1 may cause GC allocation
Comment #14 by bearophile_hugs — 2014-10-06T17:27:40Z
*** Issue 13576 has been marked as a duplicate of this issue. ***
Comment #15 by bearophile_hugs — 2014-10-31T10:27:14Z
Another common case where I'd like the compiler to avoid GC-allocations: void main() @nogc { immutable int[4] a1 = [1, 2, 4, 5]; size_t i = 2; immutable int[5] a2 = a1[0 .. i] ~ 3 ~ a1[i .. $]; } Currently you have to write this more bug-prone code and the 'a2' variable can't be immutable: void main() @nogc { immutable int[4] a1 = [1, 2, 4, 5]; size_t i = 2; int[5] a2 = void; a2[0 .. i] = a1[0 .. i]; a2[2] = 3; a2[i + 1 .. $] = a1[i .. $]; }
Comment #16 by nick — 2024-07-22T13:33:15Z
Code in comment #0 and comment #12 now compile. Comment #13 and comment #19 do not.
Comment #17 by nick — 2024-11-29T13:47:56Z
Allowing comment #13 code depends on fixing Issue 20712. > and comment #19 do not. I meant comment #15 does not: > immutable int[5] a2 = a1[0 .. i] ~ 3 ~ a1[i .. $]; I don't see how that could be allowed because `i` is a runtime value, so the compiler has to make a GC array allocation (at least in the general case).
Comment #18 by robert.schadek — 2024-12-13T18:24:57Z
THIS ISSUE HAS BEEN MOVED TO GITHUB https://github.com/dlang/dmd/issues/18874 DO NOT COMMENT HERE ANYMORE, NOBODY WILL SEE IT, THIS ISSUE HAS BEEN MOVED TO GITHUB