1616#include " dmd/template.h"
1717#include " gen/dvalue.h"
1818#include " gen/funcgenstate.h"
19+ #include " gen/functions.h"
1920#include " gen/irstate.h"
2021#include " gen/llvm.h"
2122#include " gen/llvmhelpers.h"
2425#include " ir/irfunction.h"
2526#include " llvm/IR/InlineAsm.h"
2627#include < cassert>
28+ #include < sstream>
2729
2830using namespace dmd ;
2931
@@ -128,9 +130,11 @@ class ToNakedIRVisitor : public Visitor {
128130 stmt->loc .toChars ());
129131 LOG_SCOPE;
130132
133+ // Use printLabelName to match how label references are generated in asm-x86.h.
134+ // This ensures label definitions match the quoted format used in jump instructions.
131135 printLabelName (irs->nakedAsm , mangleExact (irs->func ()->decl ),
132136 stmt->ident ->toChars ());
133- irs->nakedAsm << " :" ;
137+ irs->nakedAsm << " :\n " ;
134138
135139 if (stmt->statement ) {
136140 stmt->statement ->accept (this );
@@ -144,108 +148,117 @@ void DtoDefineNakedFunction(FuncDeclaration *fd) {
144148 IF_LOG Logger::println (" DtoDefineNakedFunction(%s)" , mangleExact (fd));
145149 LOG_SCOPE;
146150
147- // we need to do special processing on the body, since we only want
148- // to allow actual inline asm blocks to reach the final asm output
149-
150- std::ostringstream &asmstr = gIR ->nakedAsm ;
151-
152- // build function header
151+ const char *mangle = mangleExact (fd);
152+ const auto &triple = *global.params .targetTriple ;
153153
154- // FIXME: could we perhaps use llvm asmwriter to give us these details ?
154+ // Get or create the LLVM function first, before visiting the body.
155+ // The visitor may call Declaration_codegen which needs an IR insert point.
156+ llvm::Module &module = gIR ->module ;
157+ llvm::Function *func = module .getFunction (mangle);
155158
156- const char *mangle = mangleExact (fd);
157- std::string fullmangle; // buffer only
159+ if (!func) {
160+ // Create function type using the existing infrastructure
161+ llvm::FunctionType *funcType = DtoFunctionType (fd);
158162
159- const auto &triple = *global.params .targetTriple ;
160- bool const isWin = triple.isOSWindows ();
161- bool const isDarwin = triple.isOSDarwin ();
162-
163- // osx is different
164- // also mangling has an extra underscore prefixed
165- if (isDarwin) {
166- fullmangle += ' _' ;
167- fullmangle += mangle;
168- mangle = fullmangle.c_str ();
169-
170- asmstr << " \t .section\t __TEXT,__text,regular,pure_instructions"
171- << std::endl;
172- asmstr << " \t .globl\t " << mangle << std::endl;
163+ // Create function with appropriate linkage
164+ llvm::GlobalValue::LinkageTypes linkage;
173165 if (fd->isInstantiated ()) {
174- asmstr << " \t .weak_definition\t " << mangle << std::endl;
166+ linkage = llvm::GlobalValue::LinkOnceODRLinkage;
167+ } else {
168+ linkage = llvm::GlobalValue::ExternalLinkage;
175169 }
176- asmstr << " \t .p2align\t 4, 0x90" << std::endl;
177- asmstr << mangle << " :" << std::endl;
170+
171+ func = llvm::Function::Create (funcType, linkage, mangle, &module );
172+ } else if (!func->empty ()) {
173+ // Function already has a body - this can happen if the function was
174+ // already defined (e.g., template instantiation in another module).
175+ // Don't add another body.
176+ return ;
177+ } else if (func->hasFnAttribute (llvm::Attribute::Naked)) {
178+ // Function already has naked attribute - it was already processed
179+ return ;
178180 }
179- // Windows is different
180- else if (isWin) {
181- // mangled names starting with '?' (MSVC++ symbols) apparently need quoting
182- if (mangle[0 ] == ' ?' ) {
183- fullmangle += ' "' ;
184- fullmangle += mangle;
185- fullmangle += ' "' ;
186- mangle = fullmangle.c_str ();
187- } else if (triple.isArch32Bit ()) {
188- // prepend extra underscore for Windows x86
189- fullmangle += ' _' ;
190- fullmangle += mangle;
191- mangle = fullmangle.c_str ();
192- }
193181
194- asmstr << " \t .def\t " << mangle << " ;" << std::endl;
195- // hard code these two numbers for now since gas ignores .scl and llvm
196- // is defaulting to .type 32 for everything I have seen
197- asmstr << " \t .scl\t 2;" << std::endl;
198- asmstr << " \t .type\t 32;" << std::endl;
199- asmstr << " \t .endef" << std::endl;
182+ // Set naked attribute - this tells LLVM not to generate prologue/epilogue
183+ func->addFnAttr (llvm::Attribute::Naked);
200184
201- if (fd->isInstantiated ()) {
202- asmstr << " \t .section\t .text,\" xr\" ,discard," << mangle << std::endl;
203- } else {
204- asmstr << " \t .text" << std::endl;
205- }
206- asmstr << " \t .globl\t " << mangle << std::endl;
207- asmstr << " \t .p2align\t 4, 0x90" << std::endl;
208- asmstr << mangle << " :" << std::endl;
209- } else {
210- if (fd->isInstantiated ()) {
211- asmstr << " \t .section\t .text." << mangle << " ,\" axG\" ,@progbits,"
212- << mangle << " ,comdat" << std::endl;
213- asmstr << " \t .weak\t " << mangle << std::endl;
214- } else {
215- asmstr << " \t .text" << std::endl;
216- asmstr << " \t .globl\t " << mangle << std::endl;
217- }
218- asmstr << " \t .p2align\t 4, 0x90" << std::endl;
219- asmstr << " \t .type\t " << mangle << " ,@function" << std::endl;
220- asmstr << mangle << " :" << std::endl;
185+ // Prevent optimizations that might clone or modify the function.
186+ // The inline asm contains labels that would conflict if duplicated.
187+ func->addFnAttr (llvm::Attribute::OptimizeNone);
188+ func->addFnAttr (llvm::Attribute::NoInline);
189+
190+ // For template instantiations, set up COMDAT for deduplication
191+ if (fd->isInstantiated ()) {
192+ func->setComdat (module .getOrInsertComdat (mangle));
221193 }
222194
223- // emit body
224- ToNakedIRVisitor v (gIR );
225- fd->fbody ->accept (&v);
195+ // Set other common attributes
196+ func->addFnAttr (llvm::Attribute::NoUnwind);
197+
198+ // Create entry basic block and set insert point before visiting body.
199+ // The visitor's ExpStatement::visit may call Declaration_codegen for
200+ // static symbols, which may need an active IR insert point.
201+ llvm::BasicBlock *entryBB =
202+ llvm::BasicBlock::Create (gIR ->context (), " entry" , func);
203+
204+ // Save current insert point and switch to new function
205+ llvm::IRBuilderBase::InsertPoint savedIP = gIR ->ir ->saveIP ();
206+ gIR ->ir ->SetInsertPoint (entryBB);
207+
208+ // Clear the nakedAsm stream and collect the function body
209+ std::ostringstream &asmstr = gIR ->nakedAsm ;
210+ asmstr.str (" " );
211+
212+ // Use the visitor to collect asm statements into nakedAsm
213+ ToNakedIRVisitor visitor (gIR );
214+ fd->fbody ->accept (&visitor);
226215
227- // We could have generated new errors in toNakedIR(), but we are in codegen
228- // already so we have to abort here.
229216 if (global.errors ) {
230217 fatal ();
231218 }
232219
233- // emit size after body
234- // llvm does this on linux, but not on osx or Win
235- if (!(isWin || isDarwin)) {
236- asmstr << " \t .size\t " << mangle << " , .-" << mangle << std::endl
237- << std::endl;
220+ // Get the collected asm string and escape $ characters for LLVM inline asm.
221+ // In LLVM inline asm, $N refers to operand N, so literal $ must be escaped as $$.
222+ std::string asmBody;
223+ {
224+ std::string raw = asmstr.str ();
225+ asmBody.reserve (raw.size () * 2 ); // Worst case: all $ characters
226+ for (char c : raw) {
227+ if (c == ' $' ) {
228+ asmBody += " $$" ;
229+ } else {
230+ asmBody += c;
231+ }
232+ }
238233 }
234+ asmstr.str (" " ); // Clear for potential reuse
239235
240- gIR ->module .appendModuleInlineAsm (asmstr.str ());
241- asmstr.str (" " );
236+ // Create inline asm - the entire function body is a single asm block
237+ // No constraints needed since naked functions handle everything in asm
238+ llvm::FunctionType *asmFuncType =
239+ llvm::FunctionType::get (llvm::Type::getVoidTy (gIR ->context ()), false );
240+
241+ llvm::InlineAsm *inlineAsm = llvm::InlineAsm::get (
242+ asmFuncType,
243+ asmBody,
244+ " " , // No constraints
245+ true , // Has side effects
246+ false , // Not align stack
247+ llvm::InlineAsm::AD_ATT // AT&T syntax
248+ );
242249
250+ gIR ->ir ->CreateCall (inlineAsm);
251+
252+ // Naked functions don't return normally through LLVM IR
253+ gIR ->ir ->CreateUnreachable ();
254+
255+ // Restore insert point
256+ gIR ->ir ->restoreIP (savedIP);
257+
258+ // Handle DLL export on Windows
243259 if (global.params .dllexport ||
244- (global.params .targetTriple ->isOSWindows () && fd->isExport ())) {
245- // Embed a linker switch telling the MS linker to export the naked function.
246- // This mimics the effect of the dllexport attribute for regular functions.
247- const auto linkerSwitch = std::string (" /EXPORT:" ) + mangle;
248- gIR ->addLinkerOption (llvm::StringRef (linkerSwitch));
260+ (triple.isOSWindows () && fd->isExport ())) {
261+ func->setDLLStorageClass (llvm::GlobalValue::DLLExportStorageClass);
249262 }
250263}
251264
@@ -436,7 +449,7 @@ DValue *DtoInlineAsmExpr(Loc loc, FuncDeclaration *fd,
436449 LLSmallVector<LLValue *, 8 > operands;
437450 LLSmallVector<LLType *, 8 > indirectTypes;
438451 operands.reserve (n);
439-
452+
440453 Type *returnType = fd->type ->nextOf ();
441454 const size_t cisize = constraintInfo.size ();
442455 const size_t minRequired = n + (returnType->ty == TY::Tvoid ? 0 : 1 );
0 commit comments