Bug 14639 – Assigning init value to struct uses stack, causing segfault

Status
RESOLVED
Resolution
FIXED
Severity
normal
Priority
P1
Component
dmd
Product
D
Version
D2
Platform
x86_64
OS
Linux
Creation time
2015-06-01T06:49:30Z
Last change time
2020-03-28T09:29:24Z
Keywords
pull
Assigned to
No Owner
Creator
Tomer Filiba (weka)

Comments

Comment #0 by tomer — 2015-06-01T06:49:30Z
Consider the following snippet: struct Biggy { ulong[50000] a; } __gshared Biggy biggy; void main() { biggy = Biggy.init; } produces the following code Dump of assembler code for function _Dmain: ... => 0x000000000047fa04 <+4>: movabs $0x6faef0,%rsi 0x000000000047fa0e <+14>: movabs $0x7d6a90,%rdi 0x000000000047fa18 <+24>: mov $0x1b774,%ecx 0x000000000047fa1d <+29>: rep movsq %ds:(%rsi),%es:(%rdi) which is fine. However, adding a @disable this(this) to Biggy, i.e., struct Biggy { ulong[50000] a; @disable this(this); } produces Dump of assembler code for function _Dmain: ... => 0x000000000047fba4 <+4>: movabs $0x7d6a88,%rsi 0x000000000047fbae <+14>: mov $0x1b774,%ecx 0x000000000047fbb3 <+19>: pushq (%rsi) 0x000000000047fbb5 <+21>: sub $0x8,%rsi 0x000000000047fbb9 <+25>: loop 0x47fbb3 <_Dmain+19> 0x000000000047fbbb <+27>: movabs $0x7d6a90,%rdi 0x000000000047fbc5 <+37>: callq 0x47fb70 <_D7themain5Biggy8opAssignMFNaNbNcNiNjNfS7themain5BiggyZS7themain5Biggy> 0x000000000047fbca <+42>: add $0xdbba0,%rsp which, as you can see, copies Biggy.init onto the *stack* and then calls opAssign on it. Notice that Biggy does not define opAssign, and even if it did, it doesn't make sense to *copy* the type's init to a temp location. Now since Biggy is big (~400KB), and the function is run on a stack-constrained fiber, we got a very peculiar segfault. Is there any reason for this odd behavior? I would have used memcpy, but &Biggy.init won't compile. -tomer (weka.io)
Comment #1 by bugzilla — 2017-05-10T09:51:20Z
What's happening is the existence of the postblit, even though disabled, causes the compiler to do the assignment by calling opAssign, where the argument is passed by value. Passing by value means pushing it to the stack.
Comment #2 by bugzilla — 2017-05-10T10:19:19Z
Comment #3 by bugzilla — 2017-05-10T15:50:27Z
The code: biggy = Biggy.init; gets rewritten to be: biggy = Biggy([0LU, ...]); which is a construction. The postblit caused an opAssign() to be created, and the expression is further rewritten to: biggy.opAssign(Biggy([0LU, ...])); which blows up the parameter stack because Biggy([0LU, ...]) is too big for it. The operation is not disabled because it gets constructed in place - a copy is not being made. A possible compiler fix is to figure out that the generated opAssign is trivial and can be replaced with a bit copy. The code in opover.d: if (sd && !sd.hasIdentityAssign) { /* This is bitwise struct assignment. */ return; } can be modified to test for triviality of identity assign, and use a bitwise copy.
Comment #4 by bugzilla — 2020-03-27T06:11:14Z
This: https://github.com/dlang/dmd/pull/10967 changes the biggy = Biggy.init; implementation into a memset.
Comment #5 by dlang-bot — 2020-03-27T08:22:31Z
@WalterBright updated dlang/dmd pull request #6766 "fix Issue 14639 - Assigning init value to struct uses stack, causing …" fixing this issue: - fix Issue 14639 - Assigning init value to struct uses stack, causing segfault https://github.com/dlang/dmd/pull/6766
Comment #6 by dlang-bot — 2020-03-28T09:29:24Z
dlang/dmd pull request #6766 "fix Issue 14639 - Assigning init value to struct uses stack, causing …" was merged into master: - a0b0b5783bffd806a45870d71aaa29c3883e5d7e by Walter Bright: fix Issue 14639 - Assigning init value to struct uses stack, causing segfault https://github.com/dlang/dmd/pull/6766