Created attachment 1560
involed project
I have this project that compiles fine with 2.069-rc2 but doesn't work anymore.
So far I didn't manage to reduce the issue because several factors are mixed:
What happens now:
=================
nothing is written to stdout.
what should happens:
====================
stdout filled.
when does this work anyway:
===========================
1/ if I use the 'new' operator instead of my custom 'construct' template.
2/ if I disable the inline switch.
3/ if I create a File with "w" flag before writing stdout.
with the 3rd workaround being totally WTF. So maybe something in File __ctor fix the issue ?!.
code:
=====
see attachment
shell commands:
===============
dmd
/home/basile/Dev/pasproj/Coedit/cesyms/cesyms.d
/home/basile/Dev/metad/libs/libdparse.a
-I/home/basile/Dev/metad/repos/libdparse/src
-w
-inline
-O
-release
-boundscheck=off
-of../lazproj/cesyms
Attempt to reduce:
==================
So far I'm here but it doesn't capture the essence of the problem.
---
//#!runnable-flags: -O -inline
module runnable;
import core.memory;
import std.stdio, std.array, std.conv;
import std.experimental.allocator.mallocator;
ST* construct(ST, Allocator = Mallocator, A...)(A a)
if(is(ST==struct))
{
auto memory = Allocator.instance.allocate(ST.sizeof)[0 .. ST.sizeof];
//GC.addRange(memory.ptr, ST.sizeof);
return emplace!(ST, A)(memory, a);
}
struct Foo
{
Foo*[] children;
void get(ref Appender!string app)
{
app.put("whatever");
foreach(child; children) child.get(app);
}
}
void main(string[] args)
{
Appender!string app;
Foo* foo = construct!Foo;
foreach(i; 0..100)
{
foo.children ~= construct!Foo;
foreach(j; 0..100) foo.children[i].children ~= construct!Foo;
}
foo.get(app);
auto s = app.data;
assert(0, s);
}
---
the assertion failure doesn't happen at all when GC.addRange() is commented. Then in the real project project I've added GC.addRange() too to the struct/class allocators but the bug still happens (empty stdout). It looks like actually there's a crash before reaching 'write(stuff.serialize)'.
Comment #1 by ag0aep6g — 2015-11-01T18:46:34Z
(In reply to bb.temp from comment #0)
> So far I'm here but it doesn't capture the essence of the problem.
>
> ---
[...]
> ---
That code segfaults for me. Is that what you see, too?
Comment #2 by ag0aep6g — 2015-11-01T19:16:21Z
(In reply to bb.temp from comment #0)
> So far I'm here but it doesn't capture the essence of the problem.
>
> ---
[...]
> ---
As far as I can tell, everything works as expected in that code.
The different `children` arrays are GC allocated (via ~=), but the `Foo`s themselves are not. So a parent `Foo` doesn't keep its `children` alive, and at some point the GC collects the seemingly dead arrays.
Comment #3 by b2.temp — 2015-11-01T20:05:29Z
(In reply to ag0aep6g from comment #2)
> (In reply to bb.temp from comment #0)
> > So far I'm here but it doesn't capture the essence of the problem.
> >
> > ---
> [...]
> > ---
>
> As far as I can tell, everything works as expected in that code.
>
> The different `children` arrays are GC allocated (via ~=), but the `Foo`s
> themselves are not. So a parent `Foo` doesn't keep its `children` alive, and
> at some point the GC collects the seemingly dead arrays.
Take care with the small sample. I've written that <<it does not reproduce the problem>>, it was just an attempt. So far it just helped to show that it's necessary to add the ranges to the GC, which I've done to the real program.
Despite of this in the real program it's still the same, if I call 'write(slb.serialize)':
- with '-inline': stdout is empty.
- without: stdout is filled
When you consider those two facts it really looks that the inliner does something wrong.
(In reply to ag0aep6g from comment #4)
> I think this is a bug in libdparse.
>
> Its `StringCache` allocates space for `buckets`, but doesn't null the
> pointers. On destruction it then tries to `free` the garbage pointers in
> there.
Forget that. It's using calloc, not malloc, so it should be all zeros already.
Comment #6 by b2.temp — 2015-11-01T21:56:37Z
(In reply to ag0aep6g from comment #4)
> I think this is a bug in libdparse.
>
> Its `StringCache` allocates space for `buckets`, but doesn't null the
> pointers. On destruction it then tries to `free` the garbage pointers in
> there.
>
> https://github.com/Hackerpilot/libdparse/blob/
> 8230f207912b40a46e5eae84e50ee59215b7c67f/src/dparse/lexer.d#L2009
>
> Could you try adding `buckets[] = null;` after that line, then rebuild
> libdparse, and see if your project still crashes?
Yes. I confirm that the fix you suggested in libdparse works.
But now I don't know whom the bug belongs to...
Was it accidental that the program worked without -inline and without the libdparse fix ?!
Comment #7 by ag0aep6g — 2015-11-01T22:26:46Z
Here's a reduction that segfaults reliably for me with -release -O -inline:
----
extern(C) void* calloc(size_t, size_t) nothrow pure;
void main()
{
{
File file_;
nop();
}
auto scache = StringCache(1);
foreach (nodePointer; scache.buckets)
{
if (nodePointer !is null) new Object;
}
}
struct File
{
~this() {}
}
void nop() {}
struct StringCache
{
this(size_t bucketCount)
{
buckets = (cast(void**) calloc(8, bucketCount))[0 .. bucketCount];
}
void*[] buckets;
}
----
(In reply to bb.temp from comment #6)
> Yes. I confirm that the fix you suggested in libdparse works.
>
> But now I don't know whom the bug belongs to...
> Was it accidental that the program worked without -inline and without the
> libdparse fix ?!
I was mistaken. The bug I saw in libdparse is not there. My "fix" probably just masks the issue somehow.
Comment #8 by b2.temp — 2015-11-01T22:41:55Z
(In reply to ag0aep6g from comment #7)
> Here's a reduction that segfaults reliably for me with -release -O -inline:
> ----
> extern(C) void* calloc(size_t, size_t) nothrow pure;
>
> void main()
> {
> {
> File file_;
> nop();
> }
>
> auto scache = StringCache(1);
> foreach (nodePointer; scache.buckets)
> {
> if (nodePointer !is null) new Object;
> }
> }
>
> struct File
> {
> ~this() {}
> }
>
> void nop() {}
>
> struct StringCache
> {
> this(size_t bucketCount)
> {
> buckets = (cast(void**) calloc(8, bucketCount))[0 .. bucketCount];
> }
>
> void*[] buckets;
> }
> ----
Yes that's it. Thx much for your patience.
Comment #13 by safety0ff.bugz — 2015-11-02T20:11:23Z
(In reply to Martin Nowak from comment #12)
> I diggered and found https://github.com/D-Programming-Language/dmd/pull/4909
> to be the culprit.
FWIW, the following patch fixes it for me:
--- a/src/backend/cgelem.c
+++ b/src/backend/cgelem.c
@@ -3545,8 +3545,8 @@ STATIC elem * eleq(elem *e, goal_t goal)
{
e->E2 = e2->E1;
eb = el_bin(OPeq,ty,eb,e2->E2);
- e2->E1 = eb;
- e2->E2 = e;
+ e2->E1 = e;
+ e2->E2 = eb;
}
else
{