D issues are now tracked on GitHub. This Bugzilla instance remains as a read-only archive.
Issue 10664 - Win64: exception handling does not work with COMDAT folding
Summary: Win64: exception handling does not work with COMDAT folding
Status: RESOLVED FIXED
Alias: None
Product: D
Classification: Unclassified
Component: dmd (show other issues)
Version: D2
Hardware: x86_64 Windows
: P2 normal
Assignee: No Owner
URL:
Keywords: backend, pull, wrong-code
Depends on:
Blocks:
 
Reported: 2013-07-18 00:17 UTC by Rainer Schuetze
Modified: 2020-09-15 10:43 UTC (History)
2 users (show)

See Also:


Attachments

Note You need to log in before you can comment on or make changes to this issue.
Description Rainer Schuetze 2013-07-18 00:17:26 UTC
Another excerpt from the phobos unittests:

import core.exception;

T collectException(T = Exception, E)(lazy E expression, ref E result)
{
    try
    {
        result = expression();
    }
    catch (T e)
    {
        return e;
    }
    return null;
}

void main()
{
    int b;
    int foo() { throw new Exception("blah"); }
    assert(collectException(foo(), b));

    int[] a = new int[3];
    collectException!RangeError(a[4], b);
}

compile with "dmd -m64 test.d" and run:

core.exception.RangeError@test(23): Range violation
----------------
0x000007F627E337DF in rt.lifetime.__setArrayAllocLength at M:\s\d\rainers\drunti
me\src\rt\lifetime.d(283)
0x000007F627E31508 in rt.lifetime._d_newarrayT at M:\s\d\rainers\druntime\src\rt
\lifetime.d(781)
0x000007F627E31237 in test.main.__dgliteral12
0x000007F627E31197 in test.main.foo at M:\s\d\rainers\bugs\rs144\test.d(19)
0x000007F627E311E8 in test.main.foo
0x000007F627E310AA in D main at M:\s\d\rainers\bugs\rs144\test.d(20)
0x000007F627E32FDE in rt.dmain2._d_run_main.tryExec.printInfoBlock at M:\s\d\rai
ners\druntime\src\rt\dmain2.d(556)
0x000007F627E3222E in rt.dmain2._d_run_main at M:\s\d\rainers\druntime\src\rt\dm
ain2.d(469)
0x000007F627E330D6 in rt.dmain2._d_run_main.tryExec.print at M:\s\d\rainers\drun
time\src\rt\dmain2.d(567)
0x000007F627E3222E in rt.dmain2._d_run_main at M:\s\d\rainers\druntime\src\rt\dm
ain2.d(469)
0x000007F627E321CE in rt.dmain2._d_run_main at M:\s\d\rainers\druntime\src\rt\dm
ain2.d(463)
0x000007F627E31276 in test.main.__dgliteral13 at M:\s\d\rainers\bugs\rs144\test.
d(23)
0x000007F627E4AD0B in core.demangle.Demangle.parseType at M:\s\d\rainers\druntim
e\src\core\demangle.d(862)
0x000007FB2EDF1832 in BaseThreadInitThunk
0x000007FB2EF8D609 in RtlUserThreadStart
----------------

It works with -g or more specifically with

dmd -m64 -L/OPT:NOICF test.d

So it seems exception information for the two collectException instantiations are merged.
Comment 1 Temtaime 2015-03-19 10:53:25 UTC
Is there any fix not workaround ?
It increases .exe's size significantly.
Comment 2 Walter Bright 2020-08-09 09:24:18 UTC
It would be easier to understand without the templates, lambdas, lazy parameters, etc. :-/
Comment 3 Rainer Schuetze 2020-08-09 19:45:24 UTC
Here's a version without lazy and templates:

import core.exception;

Exception collectExceptionE(int delegate () expression, ref int result)
{
    try
    {
        result = expression();
    }
    catch (Exception e)
    {
        return e;
    }
    return null;
}

RangeError collectExceptionR(int delegate () expression, ref int result)
{
    try
    {
        result = expression();
    }
    catch (RangeError e)
    {
        return e;
    }
    return null;
}

void main()
{
    int b;
    int foo() { throw new Exception("blah"); }
    assert(collectExceptionE(&foo, b));

    int[] a = new int[3];
    int goo() { return a[4]; }
    collectExceptionR(&goo, b);
}

I suspect that the functions need to be the exact same code, and only the associated exception data differs. That's also why you cannot easily eliminate the delegate call.

Please note that you have to remove /OPT:NOICF from sc.ini to reproduce, as it was added as a workaround.
Comment 4 Walter Bright 2020-09-15 08:43:29 UTC
You're right, it's the handler table that is different. The handler table is all based on offsets from the start of the function, so that works. The only pointer in it is the pointer to the catch type (the `*__ClassZ` symbols), and hence the only actual difference in the handler table.

The best fix I can think of for this is to embed in the catch blocks a reference to the catch type. Then functions that differ only in catch type won't be merged, and /OPT:NOICF will no longer be needed.
Comment 5 Walter Bright 2020-09-15 09:33:00 UTC
I.e. this works:

import core.exception;
import core.bitop;

Exception collectExceptionE(int delegate () expression, ref int result)
{
    try
    {
        result = expression();
    }
    catch (Exception e)
    {
	core.bitop.volatileLoad(cast(size_t*)Exception.classinfo); <*****
        return e;
    }
    return null;
}

RangeError collectExceptionR(int delegate () expression, ref int result)
{
    try
    {
        result = expression();
    }
    catch (RangeError e)
    {
	core.bitop.volatileLoad(cast(size_t*)RangeError.classinfo); <*****
        return e;
    }
    return null;
}

void main()
{
    int b;
    int foo() { throw new Exception("blah"); }
    assert(collectExceptionE(&foo, b));

    int[] a = new int[3];
    int goo() { return a[4]; }
    collectExceptionR(&goo, b);
}
Comment 6 Dlang Bot 2020-09-15 10:00:16 UTC
@WalterBright created dlang/dmd pull request #11736 "fix Issue 10664 - Win64: exception handling does not work with COMDAT…" fixing this issue:

- fix Issue 10664 - Win64: exception handling does not work with COMDAT folding

https://github.com/dlang/dmd/pull/11736
Comment 7 Dlang Bot 2020-09-15 10:43:05 UTC
dlang/dmd pull request #11736 "fix Issue 10664 - Win64: exception handling does not work with COMDAT…" was merged into master:

- f3d96a38deee69fdfaabe49cd1a6c5a2b53563ce by Walter Bright:
  fix Issue 10664 - Win64: exception handling does not work with COMDAT folding

https://github.com/dlang/dmd/pull/11736