Wiki Roadmap Timeline Tickets New Ticket Source Search Help / Guide About Trac Login

root/gen/naked.cpp

Revision 1571:8d086d552909, 13.8 kB (checked in by Benjamin Kramer <benny.kra@gmail.com>, 3 years ago)

IntegerType? is now contextifed.

Requires llvm >= 78969. resistor says this will be the last context API change :)

Line 
1 #include "gen/llvm.h"
2 #include "llvm/InlineAsm.h"
3
4 #include "expression.h"
5 #include "statement.h"
6 #include "declaration.h"
7 #include "template.h"
8
9 #include <cassert>
10
11 #include "gen/logger.h"
12 #include "gen/irstate.h"
13 #include "gen/llvmhelpers.h"
14 #include "gen/tollvm.h"
15 #include "gen/dvalue.h"
16
17 //////////////////////////////////////////////////////////////////////////////////////////
18
19 void Statement::toNakedIR(IRState *p)
20 {
21     error("not allowed in naked function");
22 }
23
24 //////////////////////////////////////////////////////////////////////////////////////////
25
26 void CompoundStatement::toNakedIR(IRState *p)
27 {
28     Logger::println("CompoundStatement::toNakedIR(): %s", loc.toChars());
29     LOG_SCOPE;
30
31     if (statements)
32     for (unsigned i = 0; i < statements->dim; i++)
33     {
34         Statement* s = (Statement*)statements->data[i];
35         if (s) s->toNakedIR(p);
36     }
37 }
38
39 //////////////////////////////////////////////////////////////////////////////////////////
40
41 void ExpStatement::toNakedIR(IRState *p)
42 {
43     Logger::println("ExpStatement::toNakedIR(): %s", loc.toChars());
44     LOG_SCOPE;
45
46     // only expstmt supported in declarations
47     if (exp->op != TOKdeclaration)
48     {
49         Statement::toNakedIR(p);
50         return;
51     }
52
53     DeclarationExp* d = (DeclarationExp*)exp;
54     VarDeclaration* vd = d->declaration->isVarDeclaration();
55     FuncDeclaration* fd = d->declaration->isFuncDeclaration();
56     EnumDeclaration* ed = d->declaration->isEnumDeclaration();
57
58     // and only static variable/function declaration
59     // no locals or nested stuffies!
60     if (!vd && !fd && !ed)
61     {
62         Statement::toNakedIR(p);
63         return;
64     }
65     else if (vd && !vd->isDataseg())
66     {
67         error("non-static variable '%s' not allowed in naked function", vd->toChars());
68         return;
69     }
70     else if (fd && !fd->isStatic())
71     {
72         error("non-static nested function '%s' not allowed in naked function", fd->toChars());
73         return;
74     }
75     // enum decls should always be safe
76
77     // make sure the symbols gets processed
78     d->declaration->codegen(Type::sir);
79 }
80
81 //////////////////////////////////////////////////////////////////////////////////////////
82
83 void LabelStatement::toNakedIR(IRState *p)
84 {
85     Logger::println("LabelStatement::toNakedIR(): %s", loc.toChars());
86     LOG_SCOPE;
87
88     p->nakedAsm << p->func()->decl->mangle() << "_" << ident->toChars() << ":";
89
90     if (statement)
91         statement->toNakedIR(p);
92 }
93
94 //////////////////////////////////////////////////////////////////////////////////////////
95
96 void DtoDefineNakedFunction(FuncDeclaration* fd)
97 {
98     Logger::println("DtoDefineNakedFunction(%s)", fd->mangle());
99     LOG_SCOPE;
100
101     assert(fd->ir.irFunc);
102     gIR->functions.push_back(fd->ir.irFunc);
103
104     // we need to do special processing on the body, since we only want
105     // to allow actual inline asm blocks to reach the final asm output
106
107     std::ostringstream& asmstr = gIR->nakedAsm;
108
109     // build function header
110
111     // FIXME: could we perhaps use llvm asmwriter to give us these details ?
112
113     const char* mangle = fd->mangle();
114     std::ostringstream tmpstr;
115
116     // osx is different
117     // also mangling has an extra underscore prefixed
118     if (global.params.os == OSMacOSX)
119     {
120         std::string section = "text";
121         bool weak = false;
122         if (DtoIsTemplateInstance(fd))
123         {
124             tmpstr << "section\t__TEXT,__textcoal_nt,coalesced,pure_instructions";
125             section = tmpstr.str();
126             weak = true;
127         }
128         asmstr << "\t." << section << std::endl;
129         asmstr << "\t.align\t4,0x90" << std::endl;
130         asmstr << "\t.globl\t_" << mangle << std::endl;
131         if (weak)
132         {
133             asmstr << "\t.weak_definition\t_" << mangle << std::endl;
134         }
135         asmstr << "_" << mangle << ":" << std::endl;
136     }
137     // this works on linux x86 32 and 64 bit
138     // assume it works everywhere else as well for now
139     // this needed a slight modification for Win
140     else
141     {
142         const char* linkage = "globl";
143         std::string section = "text";
144         if (DtoIsTemplateInstance(fd))
145         {
146             linkage = "weak";
147             tmpstr << "section\t.gnu.linkonce.t.";
148             if (global.params.os != OSWindows)
149             {
150                 tmpstr << mangle << ",\"ax\",@progbits";
151             } else
152             {
153                 tmpstr << "_" << mangle << ",\"ax\"";
154             }
155             section = tmpstr.str();
156         }
157         asmstr << "\t." << section << std::endl;
158         asmstr << "\t.align\t16" << std::endl;
159        
160         if (global.params.os == OSWindows)
161         {
162             std::string def = "def";
163             std::string endef = "endef";
164             asmstr << "\t." << def << "\t_" << mangle << ";";
165             // hard code these two numbers for now since gas ignores .scl and llvm
166             // is defaulting to .type 32 for everything I have seen
167             asmstr << "\t.scl 2; .type 32;\t" << "." << endef << std::endl;
168             asmstr << "_";
169         } else
170         {
171             asmstr << "\t." << linkage << "\t" << mangle << std::endl;
172             asmstr << "\t.type\t" << mangle << ",@function" << std::endl;
173         }
174        
175         asmstr << mangle << ":" << std::endl;
176            
177     }
178
179     // emit body
180     fd->fbody->toNakedIR(gIR);
181
182     // emit size after body
183     // llvm does this on linux, but not on osx or Win
184     if (global.params.os != OSMacOSX && global.params.os != OSWindows)
185     {
186         asmstr << "\t.size\t" << mangle << ", .-" << mangle << std::endl << std::endl;
187     }
188
189     gIR->module->appendModuleInlineAsm(asmstr.str());
190     asmstr.str("");
191
192     gIR->functions.pop_back();
193 }
194
195 //////////////////////////////////////////////////////////////////////////////////////////
196
197 void emitABIReturnAsmStmt(IRAsmBlock* asmblock, Loc loc, FuncDeclaration* fdecl)
198 {
199     Logger::println("emitABIReturnAsmStmt(%s)", fdecl->mangle());
200     LOG_SCOPE;
201
202     IRAsmStmt* as = new IRAsmStmt;
203
204     const LLType* llretTy = DtoType(fdecl->type->nextOf());
205     asmblock->retty = llretTy;
206     asmblock->retn = 1;
207
208     // FIXME: This should probably be handled by the TargetABI somehow.
209     //        It should be able to do this for a greater variety of types.
210
211     // x86
212     if (global.params.cpu == ARCHx86)
213     {
214         LINK l = fdecl->linkage;
215         assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return");
216
217         Type* rt = fdecl->type->nextOf()->toBasetype();
218         if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray)
219         {
220             if (rt->size() == 8) {
221                 as->out_c = "=A,";
222             } else {
223                 as->out_c = "={ax},";
224             }
225         }
226         else if (rt->isfloating())
227         {
228             if (rt->iscomplex()) {
229                 if (fdecl->linkage == LINKd) {
230                     // extern(D) always returns on the FPU stack
231                     as->out_c = "={st},={st(1)},";
232                     asmblock->retn = 2;
233                 } else if (rt->ty == Tcomplex32) {
234                     // extern(C) cfloat is return as i64
235                     as->out_c = "=A,";
236                     asmblock->retty = LLType::getInt64Ty(gIR->context());
237                 } else {
238                     // cdouble and creal extern(C) are returned in pointer
239                     // don't add anything!
240                     asmblock->retty = LLType::getVoidTy(gIR->context());
241                     asmblock->retn = 0;
242                     return;
243                 }
244             } else {
245                 as->out_c = "={st},";
246             }
247         }
248         else if (rt->ty == Tarray || rt->ty == Tdelegate)
249         {
250             as->out_c = "={ax},={dx},";
251             asmblock->retn = 2;
252         #if 0
253             // this is to show how to allocate a temporary for the return value
254             // in case the appropriate multi register constraint isn't supported.
255             // this way abi return from inline asm can still be emulated.
256             // note that "$<<out0>>" etc in the asm will translate to the correct
257             // numbered output when the asm block in finalized
258
259             // generate asm
260             as->out_c = "=*m,=*m,";
261             LLValue* tmp = DtoRawAlloca(llretTy, 0, ".tmp_asm_ret");
262             as->out.push_back( tmp );
263             as->out.push_back( DtoGEPi(tmp, 0,1) );
264             as->code = "movd %eax, $<<out0>>" "\n\t" "mov %edx, $<<out1>>";
265
266             // fix asmblock
267             asmblock->retn = 0;
268             asmblock->retemu = true;
269             asmblock->asmBlock->abiret = tmp;
270
271             // add "ret" stmt at the end of the block
272             asmblock->s.push_back(as);
273
274             // done, we don't want anything pushed in the front of the block
275             return;
276         #endif
277         }
278         else
279         {
280             error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars());
281             fatal();
282         }
283     }
284
285     // x86_64
286     else if (global.params.cpu == ARCHx86_64)
287     {
288         LINK l = fdecl->linkage;
289         /* TODO: Check if this works with extern(Windows), completely untested.
290          *       In particular, returning cdouble may not work with
291          *       extern(Windows) since according to X86CallingConv.td it
292          *       doesn't allow XMM1 to be used.
293          * (So is extern(C), but that should be fine as the calling convention
294          * is identical to that of extern(D))
295          */
296         assert((l == LINKd || l == LINKc || l == LINKwindows) && "invalid linkage for asm implicit return");
297
298         Type* rt = fdecl->type->nextOf()->toBasetype();
299         if (rt->isintegral() || rt->ty == Tpointer || rt->ty == Tclass || rt->ty == Taarray)
300         {
301             as->out_c = "={ax},";
302         }
303         else if (rt->isfloating())
304         {
305             if (rt == Type::tcomplex80) {
306                 // On x87 stack, re=st, im=st(1)
307                 as->out_c = "={st},={st(1)},";
308                 asmblock->retn = 2;
309             } else if (rt == Type::tfloat80 || rt == Type::timaginary80) {
310                 // On x87 stack
311                 as->out_c = "={st},";
312             } else if (l != LINKd && rt == Type::tcomplex32) {
313                 // LLVM and GCC disagree on how to return {float, float}.
314                 // For compatibility, use the GCC/LLVM-GCC way for extern(C/Windows)
315                 // extern(C) cfloat -> %xmm0 (extract two floats)
316                 as->out_c = "={xmm0},";
317                 asmblock->retty = LLType::getDoubleTy(gIR->context());
318             } else if (rt->iscomplex()) {
319                 // cdouble and extern(D) cfloat -> re=%xmm0, im=%xmm1
320                 as->out_c = "={xmm0},={xmm1},";
321                 asmblock->retn = 2;
322             } else {
323                 // Plain float/double/ifloat/idouble
324                 as->out_c = "={xmm0},";
325             }
326         }
327         else if (rt->ty == Tarray || rt->ty == Tdelegate)
328         {
329             as->out_c = "={ax},={dx},";
330             asmblock->retn = 2;
331         }
332         else
333         {
334             error(loc, "unimplemented return type '%s' for implicit abi return", rt->toChars());
335             fatal();
336         }
337     }
338
339     // unsupported
340     else
341     {
342         error(loc, "this target (%s) does not implement inline asm falling off the end of the function", global.params.targetTriple);
343         fatal();
344     }
345
346     // return values always go in the front
347     asmblock->s.push_front(as);
348 }
349
350 //////////////////////////////////////////////////////////////////////////////////////////
351
352 // sort of kinda related to naked ...
353
354 DValue * DtoInlineAsmExpr(Loc loc, FuncDeclaration * fd, Expressions * arguments)
355 {
356     Logger::println("DtoInlineAsmExpr @ %s", loc.toChars());
357     LOG_SCOPE;
358
359     TemplateInstance* ti = fd->toParent()->isTemplateInstance();
360     assert(ti && "invalid inline __asm expr");
361
362     assert(arguments->dim >= 2 && "invalid __asm call");
363
364     // get code param
365     Expression* e = (Expression*)arguments->data[0];
366     Logger::println("code exp: %s", e->toChars());
367     StringExp* se = (StringExp*)e;
368     if (e->op != TOKstring || se->sz != 1)
369     {
370         e->error("__asm code argument is not a char[] string literal");
371         fatal();
372     }
373     std::string code((char*)se->string, se->len);
374
375     // get constraints param
376     e = (Expression*)arguments->data[1];
377     Logger::println("constraint exp: %s", e->toChars());
378     se = (StringExp*)e;
379     if (e->op != TOKstring || se->sz != 1)
380     {
381         e->error("__asm constraints argument is not a char[] string literal");
382         fatal();
383     }
384     std::string constraints((char*)se->string, se->len);
385
386     // build runtime arguments
387     size_t n = arguments->dim;
388
389     LLSmallVector<llvm::Value*, 8> args;
390     args.reserve(n-2);
391     std::vector<const llvm::Type*> argtypes;
392     argtypes.reserve(n-2);
393
394     for (size_t i = 2; i < n; i++)
395     {
396         e = (Expression*)arguments->data[i];
397         args.push_back(e->toElem(gIR)->getRVal());
398         argtypes.push_back(args.back()->getType());
399     }
400
401     // build asm function type
402     Type* type = fd->type->nextOf()->toBasetype();
403     const llvm::Type* ret_type = DtoType(type);
404     llvm::FunctionType* FT = llvm::FunctionType::get(ret_type, argtypes, false);
405
406     // build asm call
407     bool sideeffect = true;
408     llvm::InlineAsm* ia = llvm::InlineAsm::get(FT, code, constraints, sideeffect);
409
410     llvm::Value* rv = gIR->ir->CreateCall(ia, args.begin(), args.end(), "");
411
412     // work around missing tuple support for users of the return value
413     if (type->ty == Tstruct)
414     {
415         // make a copy
416         llvm::Value* mem = DtoAlloca(type, ".__asm_tuple_ret");
417
418         TypeStruct* ts = (TypeStruct*)type;
419         size_t n = ts->sym->fields.dim;
420         for (size_t i = 0; i < n; i++)
421         {
422             llvm::Value* v = gIR->ir->CreateExtractValue(rv, i, "");
423             llvm::Value* gep = DtoGEPi(mem, 0, i);
424             DtoStore(v, gep);
425         }
426
427         return new DVarValue(fd->type->nextOf(), mem);
428     }
429
430     // return call as im value
431     return new DImValue(fd->type->nextOf(), rv);
432 }
Note: See TracBrowser for help on using the browser.
Copyright © 2008, LDC Development Team.