-
-
Notifications
You must be signed in to change notification settings - Fork 699
[REG2.067] Issue 14828 - duplicate symbol __ModuleInfoZ depending on ordering of files passed to dmd #4851
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[REG2.067] Issue 14828 - duplicate symbol __ModuleInfoZ depending on ordering of files passed to dmd #4851
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -70,9 +70,10 @@ void parseConfFile(StringTable *environment, const char *path, size_t len, unsig | |
|
|
||
| void genObjFile(Module *m, bool multiobj); | ||
| void genhelpers(Module *m, bool iscomdat); | ||
| void genHelpersObjFile(Module *m); | ||
|
|
||
| /** Normalize path by turning forward slashes into backslashes */ | ||
| const char * toWinPath(const char *src) | ||
| const char *toWinPath(const char *src) | ||
| { | ||
| if (src == NULL) | ||
| return NULL; | ||
|
|
@@ -1658,57 +1659,127 @@ Language changes listed by -transition=id:\n\ | |
| } | ||
| } | ||
|
|
||
| if (!global.params.obj) | ||
| //printf("global.params.multiobj = %d, oneobj = %d, lib = %d\n", | ||
| // global.params.multiobj, global.params.oneobj, global.params.lib); | ||
| if (!global.params.obj || !modules.dim) | ||
| { | ||
| } | ||
| else if (global.params.oneobj) | ||
| { | ||
| if (modules.dim) | ||
| obj_start(modules[0]->srcfile->toChars()); | ||
| /* global.params.oneobj == true: | ||
| * Just only one object file is generated for the final link. | ||
| * e.g. | ||
| * dmd -ofout main.d // main.obj is generated | ||
| */ | ||
| obj_start(modules[0]->srcfile->toChars()); | ||
|
|
||
| for (size_t i = 0; i < modules.dim; i++) | ||
| { | ||
| Module *m = modules[i]; | ||
| if (global.params.verbose) | ||
| fprintf(global.stdmsg, "code %s\n", m->toChars()); | ||
|
|
||
| genObjFile(m, false); | ||
| if (entrypoint && m == rootHasMain) | ||
| genObjFile(entrypoint, false); | ||
| } | ||
| for (size_t i = 0; i < Module::amodules.dim; i++) | ||
| for (size_t j = 0; j < Module::amodules.dim; j++) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In below code blocks, |
||
| { | ||
| Module *m = Module::amodules[i]; | ||
| if (!m->isRoot() && (m->marray || m->massert || m->munittest)) | ||
| genhelpers(m, true); | ||
| Module *mx = Module::amodules[j]; | ||
| if (mx->isRoot()) | ||
| continue; | ||
| if (!mx->marray && !mx->massert && !mx->munittest) | ||
| continue; | ||
| genhelpers(mx, true); | ||
| } | ||
| if (!global.errors && modules.dim) | ||
| { | ||
|
|
||
| if (!global.errors) | ||
| obj_end(library, modules[0]->objfile); | ||
| } | ||
| } | ||
| else | ||
| else if (!global.params.multiobj) | ||
| { | ||
| /* global.params.multiobj == false: | ||
| * The object files are generated per source files. | ||
| * e.g. | ||
| * dmd -c main.d code.d // main.obj and code.obj generated | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
@WalterBright Here was the problematic part. If Both
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The stdio module helpers should be there with the stdio.obj file, along with stdio's moduleinfo. There should be no reason for any other module to generate either.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes, it's still correct. When we compile stdio.d, stdio.obj will contain both non-COMDAT moduleinfo and three helper functions.
No, it's necessary to fix issue 846. When a user instantiate writeln!(), it will implicitly use the helper
If you want to know the details, please also check the change in #3552. Honestly this PR is a supplemental change of that. Honestly, when I wrote that PR, I didn't think well about the library generation. So the COMDAT helpers had placed in incorrect places. This PR will fix that issue.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, so #3552 is where it came from. Thanks for pointing it out. I did not review that PR. I believe it is the wrong solution, however. A very simple solution is to compile the helpers and moduleinfo into stdio.obj. Then, you have to link with stdio.obj. I do not think it should be a surprise to anyone that if they import stdio, they have to link with stdio.obj. (The stdio.obj should be in the library, too, meaning it will get pulled in automatically.) This solution resolves https://issues.dlang.org/show_bug.cgi?id=846 in a simple, easy to understand, and easy to implement manner. Furthermore, it eliminates all that .obj file bloat I've noticed where the helper functions for every import get generated into every .obj file.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Bloat is an exaggeration, each of those functions only adds about 50 bytes.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There are 3 per import, and then repeated endlessly for each object file. For: the following are generated: and: used only by those helper functions. The object file is 2,229 bytes, although the generated code for
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Generating |
||
| */ | ||
| for (size_t i = 0; i < modules.dim; i++) | ||
| { | ||
| Module *m = modules[i]; | ||
| if (global.params.verbose) | ||
| fprintf(global.stdmsg, "code %s\n", m->toChars()); | ||
|
|
||
| obj_start(m->srcfile->toChars()); | ||
| genObjFile(m, global.params.multiobj); | ||
| genObjFile(m, false); | ||
| if (entrypoint && m == rootHasMain) | ||
| genObjFile(entrypoint, global.params.multiobj); | ||
| genObjFile(entrypoint, false); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| for (size_t j = 0; j < Module::amodules.dim; j++) | ||
| { | ||
| // todo: This part is not the best for the total size of object files. | ||
| // For example: | ||
| // When both of main.d and code.d import mx, even if main.obj | ||
| // already contains mx->marray, it is stored in code.obj again. | ||
| Module *mx = Module::amodules[j]; | ||
| if (mx != m && mx->importedFrom == m && (mx->marray || mx->massert || mx->munittest)) | ||
| genhelpers(mx, true); | ||
| if (mx == m || mx->importedFrom != m) | ||
| continue; | ||
| if (!mx->marray && !mx->massert && !mx->munittest) | ||
| continue; | ||
| genhelpers(mx, true); | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still emitting weak duplicates of those helper functions into other modules, means it won't solve Issue 14748 for separate compilation.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But then again, other than libraries, objects passed to the linker aren't optional by default, so it might not be a problem.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. |
||
| } | ||
| obj_end(library, m->objfile); | ||
|
|
||
| if (global.errors && !global.params.lib) | ||
| m->deleteObjFile(); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| /* global.params.multiobj == true: | ||
| * Each compiled symbols are stored in separated doppelganger modules. | ||
| * The generated object files have numbered names. | ||
| * e.g. | ||
| * dmd -lib main.d code.d | ||
| * dmd -multiobj main.d code.d | ||
| * // main.foo() --> main_1_<hash>.obj | ||
| * // main.bar() --> main_2_<hash>.obj | ||
| * // code.baz() --> code_3_<hash>.obj | ||
| * // std.stdio.writeln!()() --> stdio_4_<hash>.obj | ||
| * // std.stdio.__array() --> stdio_5_<hash>.obj (Bugzilla 14828) | ||
| * // --> with -lib, all *.obj will be stored in one .lib file. | ||
| */ | ||
| for (size_t i = 0; i < modules.dim; i++) | ||
| { | ||
| Module *m = modules[i]; | ||
| if (global.params.verbose) | ||
| fprintf(global.stdmsg, "code %s\n", m->toChars()); | ||
|
|
||
| obj_start(m->srcfile->toChars()); | ||
| genObjFile(m, true); | ||
| if (entrypoint && m == rootHasMain) | ||
| genObjFile(entrypoint, true); | ||
| obj_end(library, m->objfile); | ||
|
|
||
| obj_write_deferred(library); | ||
|
|
||
| if (global.errors && !global.params.lib) | ||
| m->deleteObjFile(); | ||
| } | ||
|
|
||
| for (size_t j = 0; j < Module::amodules.dim; j++) | ||
| { | ||
| Module *mx = Module::amodules[j]; | ||
| if (mx->isRoot()) | ||
| continue; | ||
| if (!mx->marray && !mx->massert && !mx->munittest) | ||
| continue; | ||
|
|
||
| obj_start(mx->srcfile->toChars()); | ||
| genHelpersObjFile(mx); | ||
| obj_end(library, mx->objfile); | ||
|
|
||
| if (global.errors && !global.params.lib) | ||
| mx->deleteObjFile(); | ||
| } | ||
| } | ||
|
|
||
| if (global.params.lib && !global.errors) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| module link14828a; | ||
|
|
||
| void func1() | ||
| { | ||
| import link14828stdio; | ||
|
|
||
| writeln("ok1"); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| module link14828b; | ||
|
|
||
| void func2() | ||
| { | ||
| import link14828stdio; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| module link14828c; | ||
|
|
||
| import link14828a; | ||
| import link14828b; | ||
|
|
||
| void test() | ||
| { | ||
| func1(); | ||
| func2(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| module link14828d; | ||
|
|
||
| import link14828c; | ||
|
|
||
| void main(string[] args) | ||
| { | ||
| test(); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| module link14828stdio; | ||
|
|
||
| void writeln()(string s) | ||
| { | ||
| int[4] sa; | ||
|
|
||
| sa[s.length] = 1; | ||
| // bounds check function (link14828stdio._array) | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| src=runnable${SEP}extra-files | ||
| dir=${RESULTS_DIR}${SEP}runnable | ||
| output_file=${dir}${SEP}link14828.sh.out | ||
|
|
||
| rm -f ${output_file} | ||
|
|
||
| if [ $OS == "win32" -o $OS == "win64" ]; then | ||
| LIBEXT=.lib | ||
| else | ||
| LIBEXT=.a | ||
| fi | ||
|
|
||
| srcname=${src}${SEP}link14828 | ||
| outname=${dir}${SEP}link14828 | ||
|
|
||
| libname=${outname}x${LIBEXT} | ||
| exename=${outname}y${EXE} | ||
|
|
||
| # all0_order_flipped: | ||
| $DMD -m${MODEL} -I${src} -of${libname} -lib -g ${srcname}c.d ${srcname}a.d ${srcname}b.d || exit 1 | ||
| $DMD -m${MODEL} -I${src} -of${exename} -g ${libname} ${srcname}a.d ${srcname}d.d || exit 1 | ||
| ${dir}/link14828y || exit 1 | ||
|
|
||
| # all0: | ||
| $DMD -m${MODEL} -I${src} -of${libname} -lib -g ${srcname}c.d ${srcname}b.d ${srcname}a.d || exit 1 | ||
| $DMD -m${MODEL} -I${src} -of${exename} -g ${libname} ${srcname}a.d ${srcname}d.d || exit 1 | ||
| ${dir}/link14828y || exit 1 | ||
|
|
||
| # all1: | ||
| $DMD -m${MODEL} -I${src} -of${libname} -lib -g ${srcname}c.d ${srcname}a.d ${srcname}b.d || exit 1 | ||
| $DMD -m${MODEL} -I${src} -of${exename} -g ${libname} ${srcname}b.d ${srcname}d.d || exit 1 | ||
| ${dir}/link14828y || exit 1 | ||
|
|
||
| # all1_order_flipped: | ||
| $DMD -m${MODEL} -I${src} -of${libname} -lib -g ${srcname}c.d ${srcname}b.d ${srcname}a.d || exit 1 | ||
| $DMD -m${MODEL} -I${src} -of${exename} -g ${libname} ${srcname}b.d ${srcname}d.d || exit 1 | ||
| ${dir}/link14828y || exit 1 | ||
|
|
||
| rm ${libname} ${exename} ${outname}y${OBJ} | ||
|
|
||
| echo Success > ${output_file} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You canmake this an if-else to spare the s->getModule.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done.