1111#include " dmd/dsymbol.h"
1212#include " dmd/errors.h"
1313#include " dmd/ldcbindings.h"
14+ #include " dmd/module.h"
1415#include " dmd/scope.h"
1516#include " dmd/statement.h"
17+ #include " dmd/template.h"
1618#include " gen/dvalue.h"
1719#include " gen/functions.h"
1820#include " gen/irstate.h"
@@ -97,6 +99,12 @@ static void replace_func_name(IRState *p, std::string &insnt) {
9799 }
98100}
99101
102+ // Global counter for generating unique asm label IDs per function.
103+ // This prevents duplicate symbol errors when LTO merges multiple
104+ // instantiations of template functions with inline asm.
105+ // See: https://github.com/ldc-developers/ldc/issues/4294
106+ static unsigned nextAsmLabelId = 1 ;
107+
100108Statement *asmSemantic (AsmStatement *s, Scope *sc) {
101109 if (!s->tokens ) {
102110 return nullptr ;
@@ -106,10 +114,50 @@ Statement *asmSemantic(AsmStatement *s, Scope *sc) {
106114 if (s->tokens ->value == TOK::string_ ||
107115 s->tokens ->value == TOK::leftParenthesis) {
108116 auto gas = createGccAsmStatement (s->loc , s->tokens );
109- return gccAsmSemantic (gas, sc);
117+ return dmd:: gccAsmSemantic (gas, sc);
110118 }
111119
112120 // this is DMD-style asm
121+
122+ // Assign a unique asm label ID to this function if it doesn't have one yet.
123+ // This ID is used to make labels unique across different compilation units
124+ // when LTO merges template instantiations.
125+ //
126+ // For template functions, we use the instantiation module's name to create
127+ // a deterministic hash that differs across compilation units. This ensures:
128+ // 1. Same compilation → same hash → reproducible builds
129+ // 2. Different compilation units → different hash → no LTO conflicts
130+ if (sc->func ->asmLabelId == 0 ) {
131+ unsigned moduleHash = 0 ;
132+
133+ // For template instantiations, use the instantiation module's name
134+ // This is the module that caused this template to be instantiated,
135+ // which differs between compilation units even for the same template.
136+ if (auto ti = sc->func ->isInstantiated ()) {
137+ if (ti->minst && ti->minst ->ident ) {
138+ const char * modName = ti->minst ->ident ->toChars ();
139+ for (const char * p = modName; *p; ++p) {
140+ moduleHash = moduleHash * 31 + static_cast <unsigned char >(*p);
141+ }
142+ }
143+ }
144+
145+ // If not a template or no instantiation module, use the current module
146+ if (moduleHash == 0 && sc->_module && sc->_module ->ident ) {
147+ const char * modName = sc->_module ->ident ->toChars ();
148+ for (const char * p = modName; *p; ++p) {
149+ moduleHash = moduleHash * 31 + static_cast <unsigned char >(*p);
150+ }
151+ }
152+
153+ // Combine module hash with counter for uniqueness within a compilation
154+ // Use golden ratio constant for better bit mixing
155+ sc->func ->asmLabelId = (moduleHash * 2654435761u ) ^ (nextAsmLabelId++ * 31 );
156+ // Ensure non-zero
157+ if (sc->func ->asmLabelId == 0 ) {
158+ sc->func ->asmLabelId = 1 ;
159+ }
160+ }
113161 sc->func ->hasInlineAsm (true );
114162
115163 const auto caseSensitive = s->caseSensitive ();
@@ -513,7 +561,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
513561 static size_t uniqueLabelsId = 0 ;
514562 std::string suffix = " _llvm_asm_end" ;
515563 suffix += std::to_string (uniqueLabelsId++);
516- printLabelName (asmGotoEndLabel, fdmangle, suffix.c_str ());
564+ printLabelName (asmGotoEndLabel, fdmangle, suffix.c_str (), fd-> asmLabelId );
517565 }
518566
519567 // initialize the setter statement we're going to build
@@ -550,7 +598,7 @@ void CompoundAsmStatement_toIR(CompoundAsmStatement *stmt, IRState *p) {
550598 IF_LOG Logger::println (
551599 " statement '%s' references outer label '%s': creating forwarder" ,
552600 a->code .c_str (), ident->toChars ());
553- printLabelName (code, fdmangle, ident->toChars ());
601+ printLabelName (code, fdmangle, ident->toChars (), fd-> asmLabelId );
554602 code << " :\n\t " ;
555603 code << " movl $<<in" << n_goto << " >>, $<<out0>>\n " ;
556604 // FIXME: Store the value -> label mapping somewhere, so it can be
0 commit comments