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,114 @@ 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
151+ const char *mangle = mangleExact (fd);
152+ const auto &triple = *global. params . targetTriple ;
149153
154+ // Clear the nakedAsm stream and collect the function body
150155 std::ostringstream &asmstr = gIR ->nakedAsm ;
156+ asmstr.str (" " );
151157
152- // build function header
153-
154- // FIXME: could we perhaps use llvm asmwriter to give us these details ?
158+ // Use the visitor to collect asm statements into nakedAsm
159+ ToNakedIRVisitor visitor ( gIR );
160+ fd-> fbody -> accept (&visitor);
155161
156- const char *mangle = mangleExact (fd);
157- std::string fullmangle; // buffer only
162+ if (global.errors ) {
163+ fatal ();
164+ }
158165
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;
173- if (fd->isInstantiated ()) {
174- asmstr << " \t .weak_definition\t " << mangle << std::endl;
166+ // Get the collected asm string and escape $ characters for LLVM inline asm.
167+ // In LLVM inline asm, $N refers to operand N, so literal $ must be escaped as $$.
168+ std::string asmBody;
169+ {
170+ std::string raw = asmstr.str ();
171+ asmBody.reserve (raw.size () * 2 ); // Worst case: all $ characters
172+ for (char c : raw) {
173+ if (c == ' $' ) {
174+ asmBody += " $$" ;
175+ } else {
176+ asmBody += c;
177+ }
175178 }
176- asmstr << " \t .p2align\t 4, 0x90" << std::endl;
177- asmstr << mangle << " :" << std::endl;
178179 }
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- }
180+ asmstr.str (" " ); // Clear for potential reuse
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+ // Get or create the LLVM function
183+ llvm::Module &module = gIR ->module ;
184+ llvm::Function *func = module .getFunction (mangle);
200185
186+ if (!func) {
187+ // Create function type using the existing infrastructure
188+ llvm::FunctionType *funcType = DtoFunctionType (fd);
189+
190+ // Create function with appropriate linkage
191+ llvm::GlobalValue::LinkageTypes linkage;
201192 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;
193+ linkage = llvm::GlobalValue::LinkOnceODRLinkage;
214194 } else {
215- asmstr << " \t .text" << std::endl;
216- asmstr << " \t .globl\t " << mangle << std::endl;
195+ linkage = llvm::GlobalValue::ExternalLinkage;
217196 }
218- asmstr << " \t .p2align\t 4, 0x90" << std::endl;
219- asmstr << " \t .type\t " << mangle << " ,@function" << std::endl;
220- asmstr << mangle << " :" << std::endl;
197+
198+ func = llvm::Function::Create (funcType, linkage, mangle, &module );
199+ } else if (!func->empty ()) {
200+ // Function already has a body - this can happen if the function was
201+ // already defined (e.g., template instantiation in another module).
202+ // Don't add another body.
203+ return ;
204+ } else if (func->hasFnAttribute (llvm::Attribute::Naked)) {
205+ // Function already has naked attribute - it was already processed
206+ return ;
221207 }
222208
223- // emit body
224- ToNakedIRVisitor v (gIR );
225- fd->fbody ->accept (&v);
209+ // Set naked attribute - this tells LLVM not to generate prologue/epilogue
210+ func->addFnAttr (llvm::Attribute::Naked);
226211
227- // We could have generated new errors in toNakedIR(), but we are in codegen
228- // already so we have to abort here.
229- if (global.errors ) {
230- fatal ();
231- }
212+ // Prevent optimizations that might clone or modify the function.
213+ // The inline asm contains labels that would conflict if duplicated.
214+ func->addFnAttr (llvm::Attribute::OptimizeNone);
215+ func->addFnAttr (llvm::Attribute::NoInline);
232216
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;
217+ // For template instantiations, set up COMDAT for deduplication
218+ if (fd->isInstantiated ()) {
219+ func->setComdat (module .getOrInsertComdat (mangle));
238220 }
239221
240- gIR ->module .appendModuleInlineAsm (asmstr.str ());
241- asmstr.str (" " );
222+ // Set other common attributes
223+ func->addFnAttr (llvm::Attribute::NoUnwind);
224+
225+ // Create entry basic block
226+ llvm::BasicBlock *entryBB =
227+ llvm::BasicBlock::Create (gIR ->context (), " entry" , func);
242228
229+ // Save current insert point and switch to new function
230+ llvm::IRBuilderBase::InsertPoint savedIP = gIR ->ir ->saveIP ();
231+ gIR ->ir ->SetInsertPoint (entryBB);
232+
233+ // Create inline asm - the entire function body is a single asm block
234+ // No constraints needed since naked functions handle everything in asm
235+ llvm::FunctionType *asmFuncType =
236+ llvm::FunctionType::get (llvm::Type::getVoidTy (gIR ->context ()), false );
237+
238+ llvm::InlineAsm *inlineAsm = llvm::InlineAsm::get (
239+ asmFuncType,
240+ asmBody,
241+ " " , // No constraints
242+ true , // Has side effects
243+ false , // Not align stack
244+ llvm::InlineAsm::AD_ATT // AT&T syntax
245+ );
246+
247+ gIR ->ir ->CreateCall (inlineAsm);
248+
249+ // Naked functions don't return normally through LLVM IR
250+ gIR ->ir ->CreateUnreachable ();
251+
252+ // Restore insert point
253+ gIR ->ir ->restoreIP (savedIP);
254+
255+ // Handle DLL export on Windows
243256 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));
257+ (triple.isOSWindows () && fd->isExport ())) {
258+ func->setDLLStorageClass (llvm::GlobalValue::DLLExportStorageClass);
249259 }
250260}
251261
@@ -436,7 +446,7 @@ DValue *DtoInlineAsmExpr(Loc loc, FuncDeclaration *fd,
436446 LLSmallVector<LLValue *, 8 > operands;
437447 LLSmallVector<LLType *, 8 > indirectTypes;
438448 operands.reserve (n);
439-
449+
440450 Type *returnType = fd->type ->nextOf ();
441451 const size_t cisize = constraintInfo.size ();
442452 const size_t minRequired = n + (returnType->ty == TY::Tvoid ? 0 : 1 );
0 commit comments