Skip to content

Conversation

@9rnsr
Copy link
Contributor

@9rnsr 9rnsr commented Aug 24, 2015

This is same with #4784, excepting the base branch is 'stable'.


https://issues.dlang.org/show_bug.cgi?id=14431

In the pull request #4384, all instance has been changed to invoke
semantic3(). It was for the link-failure issue in specific case, but it
was too excessive.

  1. Semantic analysis strategy for template instances:
    We cannot determine which instance does not need to be placed in object
    file until semantic analysis completed. Therefore, for all templates
    instantiated in root module, compiler should invoke their semantic3 --
    regardless of whether those are also instantiated in non-root module. If
    a template is only instantiated in non-root module, we can elide its
    semantic3 (and for the compilation speed we should do that).
  2. Code generation storategy for template instances:
    If a template is instantiated in non-root module, compiler usually does
    not have to put it in object file. But if a template is instantiated in
    both of root and non-root modules which mutually import each other, it
    needs to placed in objfile.

By fixing excessive semantic3 for template instances, we can also fix few regressions which come from the same root.

  • Issue 14508 - [REG2.067.0] compiling with -unittest instantiates templates in non-root modules
  • Issue 14564 - [REG2.067] dmd -property -unittest combination causes compiler error
  • Issue 14901 - [reg 2.067/2.068] template static shared this() run multiple times with separate compilation

src/struct.c Outdated
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Makes sense b/c non-root instances no longer run semantic3.
In the longer run the TypeInfo should only be generated where the struct is instantiated.

9rnsr added 2 commits August 25, 2015 00:20
In the pull request dlang#4384, all instance has been changed to invoke
semantic3(). It was for the link-failure issue in specific case, but it
was too excessive.

1. Semantic analysis strategy for template instances:
  We cannot determine which instance does not need to be placed in object
file until semantic analysis completed. Therefore, for all templates
instantiated in root module, compiler should invoke their semantic3 --
regardless of whether those are also instantiated in non-root module. If
a template is _only_ instantiated in non-root module, we can elide its
semantic3 (and for the compilation speed we should do that).

2. Code generation strategy for template instances:
  If a template is instantiated in non-root module, compiler usually does
not have to put it in object file. But if a template is instantiated in
both of root and non-root modules which mutually import each other, it
needs to placed in objfile.
@MartinNowak
Copy link
Member

Unfortunately this causes a linker error with Higgs' test.

higgs-test.o:ffi/tests.d:function _D3std5range14__T6strideTAkZ6strideFAkmZ6Result7popBackMFNaNbNiNfZv: error: undefined reference to '_D3std5range10primitives16__T8popBackNTAkZ8popBackNFNaNbNiNfKAkmZm'
higgs-test.o:ffi/tests.d:function _D3std5range14__T6strideTAkZ6strideFAkmZ6Result6moveAtMFNaNbNiNfmZk: error: undefined reference to '_D3std5range10primitives16__T6moveAtTAkTmZ6moveAtFNaNbNiNfAkmZk'

I'll try to provide more details, but it seems that some phobos unittest (std.uni) gets compiled into the binary but some templates instantiated by it are not. Might be the -debug, -unittest issues mentioned in needsCodegen.

@MartinNowak
Copy link
Member

Here is code from Higgs that instantiates the right templates to trigger the issue.

module bug;
void foo()
{
    import std.regex;
    enum hexRegex = ctRegex!("");
}
void main() {}

dmd -debug bug

The problem itself seems to come from dbgVerifySorted.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 24, 2015

@MartinNowak If a source file is conditionally compiled from two places by using different conditions (debug and version, including version(unittest)), the generate object files are essentially incompatible and link may fail. Currently it's unavoidable, and this PR is completely unrelated to that issue.

OT: In #4626, I had tried to reduce such the link-failure problem for instantiated code...

@MartinNowak
Copy link
Member

From a practical standpoint we cannot do something that breaks programs using ctRegex though.

The problem seems to be a bit more complex. dbgVerifySorted actually does instantiate stride, it's stride.Result.moveAt instantiating std.range.moveAt which is a problem here. For some reason no code is generated for the latter.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 24, 2015

It's not so complex. dbgVerifySorted function will have different attributes depending on -debug switch. By that, its direct/indirect callers will also have different attributes. If one of the callers is already instantiated in the library (e.g. SortedRange), compiler may elide its codegen to use the code in lib file, then it may cause link failure.

When we try to speed up compilation by avoiding more codegen for the instances, it's equivalent that we try to use more many pre-compiled code in the library file. In there, an implicit assumption exists: "the library and the currently compiled code are analyzed under the same conditional compilation".
The excessive semantic analysis and codegen since 2.067 would have been hide the issue - because it's equivalent that more many code will be compiled under the "current condition".

So, this is unavoidable. By taking the compilation speed, we will have to strictly follow the rule of conditional compilation.

@MartinNowak
Copy link
Member

When we try to speed up compilation by avoiding more codegen for the instances, it's equivalent that we try to use more many pre-compiled code in the library file. In there, an implicit assumption exists: "the library and the currently compiled code are analyzed under the same conditional compilation".

That is understood, but not exactly the problem we're seeing here.

template_instance

Both stride and moveAt are only present b/c the non-root module was analyzed with -debug.
If we had a top to bottom instantiation graph (we already have a bottom to top one with tinst) we could "taint" all the templates reachable from roots.
It also seems that the children of CodepointSet should be treated as speculative b/c we don't generate code for CodepointSet and therefor don't know it's real children.

Taking into account the possibly different attributes inferred we apparently have 2 solutions. Generating all instances by default or only linking code that was compiled with the same arguments. The latter required debug/unittest/release phobos libraries or careful avoidance of differences.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 25, 2015

It also seems that the children of CodepointSet should be treated as speculative b/c we don't generate code for CodepointSet and therefor don't know it's real children.

CTFE will invoke semantic3 pass for most of the children of CodepointSet. Therefore currently, compiler can know that SortedRange is its real children.

In other words, for the compilation speed, compiler maximally uses the assumption of the conditional compilation (== pre-compiled library and user code are compiled under the same condition).
It comes from the original design in #2550.

If we adjust the balance of compile speed and link-ability, it should go into future enhancement.

@MartinNowak
Copy link
Member

Interestingly enough, we only generate code for stride b/c of this "hack" https://github.com/D-Programming-Language/dmd/blob/97449e602b346f2dd46a06f74cec74ce3e82d173/src/dtemplate.d#L6329.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 25, 2015

@MartinNowak Ah, I finally got the issue you've mentioned. Today, when -debug or -unittest is specified, the instantiation strategy is switched to the old thing for the -allinst. However, as we already know, it's problematic.
That we should do is: if -allinst is not actually specified, we should always use the new strategy even if -debug or -unittest is specified; distinguish root and non-root instances, and have to change some non-root instances to the root ones.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 25, 2015

Please give me some days. I'll start to work for that.

@9rnsr 9rnsr force-pushed the fix14431 branch 8 times, most recently from 68bd9a5 to 83b5428 Compare August 28, 2015 08:40
9rnsr and others added 5 commits August 28, 2015 18:32
Even if a struct is defined as non-root symbol, some built-in operations
request its TypeInfo. For those, today TypeInfo_Struct is generated in COMDAT.

On the other hand, TypeInfoDeclaration is always generated when the TypeInfo is requested,
even if the code exists in speculative scope. By that, some unneeded
TypeInfo objects had excessively generated.
If a TypeInfo is not actually used, we should stop its generation.

To satisfy two requirements, I added "speculative TypeInfo" concept
associated with template instances.

By this change, when a TypeInfo is *requested*, the codegen pass still
visit it always. But if the corresponding type is instantiated struct or
its derived, and is definitely speculative, the TypeInfo code also won't
be emit into object file.
1. When a non-root function is succeeded to expand for inlining,
the additional TypeInfo requests should be handled properly.

2. When a function's semantic3 pass will get called from `canInline`, it
would generate more speculative instances when -debug/-unittest is specified.
If the additionally instantiated function succeeds to expand, it should be
changed to non-speculative, because its code is reachable from the final executable.
Issue 14541 - "duplicate COMDAT" linker error with the template forward reference in Tuple.opAssign
@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 28, 2015

OK, ready to review.

@MartinNowak
Copy link
Member

I tested this with a bunch of the most downloaded packages and couldn't find any issue.
It does improve build times a bit for all packages and a lot for projects using single file compilation.
The solution makes the template instantiation slightly more complex, which is unfortunate, but at least we can fix recent regressions.

@MartinNowak
Copy link
Member

Auto-merge toggled on

MartinNowak added a commit that referenced this pull request Aug 30, 2015
[REG 2.067.0] Issue 14431 - huge slowdown of compilation speed
@MartinNowak MartinNowak merged commit 92fcc9d into dlang:stable Aug 30, 2015
@MartinNowak
Copy link
Member

Unfortunately I hit a linking issue when building chmgen.
Here is a "reduced" test-case.

import std.file;

struct Only(T)
{
    private T _val;
}

auto only(V)(V v)
{
    return Only!V(v);
}

static struct Chain(R...)
{
    R source;
}

auto chain(R...)(R rs)
{
    return Chain!R(rs);
}

void main()
{
    string docRoot;

    chain(
        dirEntries(docRoot , "*.*"   , SpanMode.shallow),
        only(docRoot)
    );
}

Seems like TypeInfo for const(FilterResult!(DirIterator))) is missing. The TypeInfo for the non-const type is in libphobos2.a.

@9rnsr
Copy link
Contributor Author

9rnsr commented Aug 31, 2015

@MartinNowak

Issue 14985 - [REG2.068.1-b1] Link failure for const TypeInfo of speculative instantiated struct
#4995

@9rnsr 9rnsr deleted the fix14431 branch August 31, 2015 14:38
@MartinNowak
Copy link
Member

@MartinNowak
Copy link
Member

@MartinNowak
Copy link
Member

9rnsr added a commit to 9rnsr/dmd that referenced this pull request Oct 13, 2015
Even if a function is expanded by inlining, the function itself don't have to put in object file. In short, adjusting `ti.minst` in `inline.d` is not correct.

The unspeculation in `expandInline` was added in the PR dlang#4944, but sadly it was just a hack and inherently unneeded. The changes in `TemplateInstance.appendToModuleMember` and `needsCodegen` those were added since 2.068.1-b1, now correctly handle codegen of nested template instances.
@CyberShadow
Copy link
Member

This pull request introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=15985

@CyberShadow
Copy link
Member

This pull request introduced a regression:
https://issues.dlang.org/show_bug.cgi?id=17398

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants