Mercurial > projects > ldc
comparison gen/asmstmt.cpp @ 219:761c8352f494 trunk
[svn r235] rough port of GDC's inline assembler code, unfinished
author | ChristianK |
---|---|
date | Thu, 05 Jun 2008 19:22:37 +0200 |
parents | |
children | ccc2e6898a78 |
comparison
equal
deleted
inserted
replaced
218:df641a27e9fd | 219:761c8352f494 |
---|---|
1 // Taken from GDC source tree, licence unclear? | |
2 // | |
3 // Taken from an earlier version of DMD -- why is it missing from 0.79? | |
4 | |
5 //#include "d-gcc-includes.h" | |
6 //#include "total.h" | |
7 #include "dmd/statement.h" | |
8 #include "dmd/scope.h" | |
9 #include "dmd/declaration.h" | |
10 #include "dmd/dsymbol.h" | |
11 | |
12 #include "llvm/InlineAsm.h" | |
13 | |
14 #include <cassert> | |
15 #include <deque> | |
16 #include <iostream> | |
17 #include <sstream> | |
18 | |
19 //#include "d-lang.h" | |
20 //#include "d-codegen.h" | |
21 | |
22 typedef enum { | |
23 Arg_Integer, | |
24 Arg_Pointer, | |
25 Arg_Memory, | |
26 Arg_FrameRelative, | |
27 Arg_LocalSize, | |
28 Arg_Dollar | |
29 } AsmArgType; | |
30 | |
31 typedef enum { | |
32 Mode_Input, | |
33 Mode_Output, | |
34 Mode_Update | |
35 } AsmArgMode; | |
36 | |
37 struct AsmArg { | |
38 AsmArgType type; | |
39 Expression * expr; | |
40 AsmArgMode mode; | |
41 AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) { | |
42 this->type = type; | |
43 this->expr = expr; | |
44 this->mode = mode; | |
45 } | |
46 }; | |
47 | |
48 struct AsmCode { | |
49 char * insnTemplate; | |
50 unsigned insnTemplateLen; | |
51 Array args; // of AsmArg | |
52 unsigned moreRegs; | |
53 unsigned dollarLabel; | |
54 int clobbersMemory; | |
55 AsmCode() { | |
56 insnTemplate = NULL; | |
57 insnTemplateLen = 0; | |
58 moreRegs = 0; | |
59 dollarLabel = 0; | |
60 clobbersMemory = 0; | |
61 } | |
62 }; | |
63 | |
64 llvm::InlineAsm* | |
65 d_build_asm_stmt(std::string code, std::deque<DValue*> const& output_values, std::deque<DValue*> const& input_values, std::string constraints) | |
66 { | |
67 //FIXME: Return InlineAsm::get(..) here. | |
68 return NULL; | |
69 } | |
70 | |
71 AsmStatement::AsmStatement(Loc loc, Token *tokens) : | |
72 Statement(loc) | |
73 { | |
74 this->tokens = tokens; // Do I need to copy these? | |
75 asmcode = 0; | |
76 asmalign = 0; | |
77 refparam = 0; | |
78 naked = 0; | |
79 regs = 0; | |
80 } | |
81 | |
82 Statement *AsmStatement::syntaxCopy() | |
83 { | |
84 // copy tokens? copy 'code'? | |
85 AsmStatement * a_s = new AsmStatement(loc,tokens); | |
86 a_s->asmcode = asmcode; | |
87 a_s->refparam = refparam; | |
88 a_s->naked = naked; | |
89 a_s->regs = a_s->regs; | |
90 return a_s; | |
91 } | |
92 | |
93 void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs) | |
94 { | |
95 bool sep = 0, nsep = 0; | |
96 buf->writestring("asm { "); | |
97 | |
98 for (Token * t = tokens; t; t = t->next) { | |
99 switch (t->value) { | |
100 case TOKlparen: | |
101 case TOKrparen: | |
102 case TOKlbracket: | |
103 case TOKrbracket: | |
104 case TOKcolon: | |
105 case TOKsemicolon: | |
106 case TOKcomma: | |
107 case TOKstring: | |
108 case TOKcharv: | |
109 case TOKwcharv: | |
110 case TOKdcharv: | |
111 nsep = 0; | |
112 break; | |
113 default: | |
114 nsep = 1; | |
115 } | |
116 if (sep + nsep == 2) | |
117 buf->writeByte(' '); | |
118 sep = nsep; | |
119 buf->writestring(t->toChars()); | |
120 } | |
121 buf->writestring("; }"); | |
122 buf->writenl(); | |
123 } | |
124 | |
125 int AsmStatement::comeFrom() | |
126 { | |
127 return FALSE; | |
128 } | |
129 | |
130 /* GCC does not support jumps from asm statements. When optimization | |
131 is turned on, labels referenced only from asm statements will not | |
132 be output at the correct location. There are ways around this: | |
133 | |
134 1) Reference the label with a reachable goto statement | |
135 2) Have reachable computed goto in the function | |
136 3) Hack cfgbuild.c to act as though there is a computed goto. | |
137 | |
138 These are all pretty bad, but if would be nice to be able to tell | |
139 GCC not to optimize in this case (even on per label/block basis). | |
140 | |
141 The current solution is output our own private labels (as asm | |
142 statements) along with the "real" label. If the label happens to | |
143 be referred to by a goto statement, the "real" label will also be | |
144 output in the correct location. | |
145 | |
146 Also had to add 'asmLabelNum' to LabelDsymbol to indicate it needs | |
147 special processing. | |
148 | |
149 (junk) d-lang.cc:916:case LABEL_DECL: // C doesn't do this. D needs this for referencing labels in inline assembler since there may be not goto referencing it. | |
150 | |
151 */ | |
152 | |
153 static unsigned d_priv_asm_label_serial = 0; | |
154 | |
155 // may need to make this target-specific | |
156 static void d_format_priv_asm_label(char * buf, unsigned n) | |
157 { | |
158 //ASM_GENERATE_INTERNAL_LABEL(buf, "LDASM", n);//inserts a '*' for use with assemble_name | |
159 sprintf(buf, ".LDASM%u", n); | |
160 } | |
161 | |
162 void | |
163 d_expand_priv_asm_label(IRState * irs, unsigned n) | |
164 { | |
165 /* char buf[64]; | |
166 d_format_priv_asm_label(buf, n); | |
167 strcat(buf, ":"); | |
168 tree insnt = build_string(strlen(buf), buf); | |
169 #if D_GCC_VER < 40 | |
170 expand_asm(insnt, 1); | |
171 #else | |
172 tree t = d_build_asm_stmt(insnt, NULL_TREE, NULL_TREE, NULL_TREE); | |
173 ASM_VOLATILE_P( t ) = 1; | |
174 ASM_INPUT_P( t) = 1; // what is this doing? | |
175 irs->addExp(t); | |
176 #endif*/ | |
177 } | |
178 | |
179 | |
180 // StringExp::toIR usually adds a NULL. We don't want that... | |
181 | |
182 /*static tree | |
183 naturalString(Expression * e) | |
184 { | |
185 // don't fail, just an error? | |
186 assert(e->op == TOKstring); | |
187 StringExp * s = (StringExp *) e; | |
188 assert(s->sz == 1); | |
189 return build_string(s->len, (char *) s->string); | |
190 }*/ | |
191 | |
192 | |
193 #include "d-asm-i386.h" | |
194 | |
195 bool d_have_inline_asm() { return true; } | |
196 | |
197 Statement *AsmStatement::semantic(Scope *sc) | |
198 { | |
199 | |
200 sc->func->inlineAsm = 1; | |
201 sc->func->inlineStatus = ILSno; // %% not sure | |
202 // %% need to set DECL_UNINLINABLE too? | |
203 sc->func->hasReturnExp = 1; // %% DMD does this, apparently... | |
204 | |
205 // empty statement -- still do the above things because they might be expected? | |
206 if (! tokens) | |
207 return this; | |
208 | |
209 AsmProcessor ap(sc, this); | |
210 ap.run(); | |
211 return this; | |
212 } | |
213 | |
214 void | |
215 AsmStatement::toIR(IRState * irs) | |
216 { | |
217 // FIXME | |
218 // gen.doLineNote( loc ); | |
219 | |
220 if (! asmcode) | |
221 return; | |
222 | |
223 static std::string i_cns = "i"; | |
224 static std::string p_cns = "p"; | |
225 static std::string m_cns = "m"; | |
226 static std::string mw_cns = "=m"; | |
227 static std::string mrw_cns = "+m"; | |
228 static std::string memory_name = "memory"; | |
229 | |
230 AsmCode * code = (AsmCode *) asmcode; | |
231 std::deque<DValue*> input_values; | |
232 std::deque<std::string> input_constraints; | |
233 std::deque<DValue*> output_values; | |
234 std::deque<std::string> output_constraints; | |
235 std::deque<std::string> clobbers; | |
236 | |
237 // FIXME | |
238 #define HOST_WIDE_INT long | |
239 HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro | |
240 bool clobbers_mem = code->clobbersMemory; | |
241 int input_idx = 0; | |
242 int n_outputs = 0; | |
243 int arg_map[10]; | |
244 | |
245 assert(code->args.dim <= 10); | |
246 | |
247 for (unsigned i = 0; i < code->args.dim; i++) { | |
248 AsmArg * arg = (AsmArg *) code->args.data[i]; | |
249 | |
250 bool is_input = true; | |
251 DValue* arg_val; | |
252 std::string cns; | |
253 | |
254 switch (arg->type) { | |
255 case Arg_Integer: | |
256 arg_val = arg->expr->toElem(irs); | |
257 do_integer: | |
258 cns = i_cns; | |
259 break; | |
260 case Arg_Pointer: | |
261 // FIXME | |
262 /* if (arg->expr->op == TOKvar) | |
263 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; | |
264 else if (arg->expr->op == TOKdsymbol) { | |
265 arg_val = irs->getLabelTree( (LabelDsymbol *) ((DsymbolExp *) arg->expr)->s ); | |
266 } else | |
267 assert(0); | |
268 arg_val = irs->addressOf(arg_val);*/ | |
269 cns = p_cns; | |
270 break; | |
271 case Arg_Memory: | |
272 // FIXME | |
273 /* if (arg->expr->op == TOKvar) | |
274 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; | |
275 else | |
276 arg_val = arg->expr->toElem(irs); | |
277 if (DECL_P( arg_val )) | |
278 TREE_ADDRESSABLE( arg_val ) = 1;*/ | |
279 switch (arg->mode) { | |
280 case Mode_Input: cns = m_cns; break; | |
281 case Mode_Output: cns = mw_cns; is_input = false; break; | |
282 case Mode_Update: cns = mrw_cns; is_input = false; break; | |
283 default: assert(0); break; | |
284 } | |
285 break; | |
286 case Arg_FrameRelative: | |
287 // FIXME | |
288 /* if (arg->expr->op == TOKvar) | |
289 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree; | |
290 else | |
291 assert(0);*/ | |
292 if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) { | |
293 // arg_val = irs->integerConstant(var_frame_offset); | |
294 cns = i_cns; | |
295 } else { | |
296 this->error("%s", "argument not frame relative"); | |
297 return; | |
298 } | |
299 if (arg->mode != Mode_Input) | |
300 clobbers_mem = true; | |
301 break; | |
302 case Arg_LocalSize: | |
303 // FIXME | |
304 /* var_frame_offset = cfun->x_frame_offset; | |
305 if (var_frame_offset < 0) | |
306 var_frame_offset = - var_frame_offset; | |
307 arg_val = irs->integerConstant( var_frame_offset );*/ | |
308 goto do_integer; | |
309 default: | |
310 assert(0); | |
311 } | |
312 | |
313 if (is_input) { | |
314 arg_map[i] = --input_idx; | |
315 //inputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val); | |
316 input_values.push_back(arg_val); | |
317 input_constraints.push_back(cns); | |
318 } else { | |
319 arg_map[i] = n_outputs++; | |
320 //outputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val); | |
321 output_values.push_back(arg_val); | |
322 output_constraints.push_back(cns); | |
323 } | |
324 } | |
325 | |
326 // Telling GCC that callee-saved registers are clobbered makes it preserve | |
327 // those registers. This changes the stack from what a naked function | |
328 // expects. | |
329 | |
330 // FIXME | |
331 // if (! irs->func->naked) { | |
332 for (int i = 0; i < 32; i++) { | |
333 if (regs & (1 << i)) { | |
334 //clobbers.cons(NULL_TREE, regInfo[i].gccName); | |
335 clobbers.push_back(regInfo[i].gccName); | |
336 } | |
337 } | |
338 for (int i = 0; i < 32; i++) { | |
339 if (code->moreRegs & (1 << (i-32))) { | |
340 //clobbers.cons(NULL_TREE, regInfo[i].gccName); | |
341 clobbers.push_back(regInfo[i].gccName); | |
342 } | |
343 } | |
344 if (clobbers_mem) | |
345 clobbers.push_back(memory_name); | |
346 //clobbers.cons(NULL_TREE, memory_name); | |
347 // } | |
348 | |
349 | |
350 // Remap argument numbers | |
351 for (unsigned i = 0; i < code->args.dim; i++) { | |
352 if (arg_map[i] < 0) | |
353 arg_map[i] = -arg_map[i] - 1 + n_outputs; | |
354 } | |
355 | |
356 bool pct = false; | |
357 char * p = code->insnTemplate; | |
358 char * q = p + code->insnTemplateLen; | |
359 //printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate); | |
360 while (p < q) { | |
361 if (pct) { | |
362 if (*p >= '0' && *p <= '9') { | |
363 // %% doesn't check against nargs | |
364 *p = '0' + arg_map[*p - '0']; | |
365 pct = false; | |
366 } else if (*p == '$') { | |
367 pct = false; | |
368 } | |
369 //assert(*p == '%');// could be 'a', etc. so forget it.. | |
370 } else if (*p == '$') | |
371 pct = true; | |
372 ++p; | |
373 } | |
374 | |
375 //printf("final: %.*s\n", code->insnTemplateLen, code->insnTemplate); | |
376 | |
377 std::string insnt(code->insnTemplate, code->insnTemplateLen); | |
378 | |
379 // rewrite GCC-style constraints to LLVM-style constraints | |
380 std::string llvmConstraints; | |
381 int n = 0; | |
382 typedef std::deque<std::string>::iterator it; | |
383 for(it i = output_constraints.begin(), e = output_constraints.end(); i != e; ++i, ++n) { | |
384 // rewrite update constraint to in and out constraints | |
385 if((*i)[0] == '+') { | |
386 (*i)[0] = '='; | |
387 std::string input_constraint; | |
388 std::stringstream ss; | |
389 ss << n; | |
390 ss >> input_constraint; | |
391 //FIXME: I think multiple inout constraints will mess up the order! | |
392 input_constraints.push_front(input_constraint); | |
393 input_values.push_front(output_values[n]); | |
394 } | |
395 llvmConstraints += *i; | |
396 llvmConstraints += ","; | |
397 } | |
398 for(it i = input_constraints.begin(), e = input_constraints.end(); i != e; ++i) { | |
399 llvmConstraints += *i; | |
400 llvmConstraints += ","; | |
401 } | |
402 for(it i = clobbers.begin(), e = clobbers.end(); i != e; ++i) { | |
403 llvmConstraints += "~{" + *i + "},"; | |
404 } | |
405 llvmConstraints.resize(llvmConstraints.size()-1); | |
406 | |
407 std::cout << "Inline Asm code: " << std::endl; | |
408 std::cout << insnt << std::endl; | |
409 std::cout << "LLVM constraints: " << llvmConstraints << std::endl; | |
410 std::cout << std::endl; | |
411 | |
412 llvm::InlineAsm* t = d_build_asm_stmt(insnt, output_values, input_values, llvmConstraints); | |
413 // FIXME | |
414 //ASM_VOLATILE_P( t ) = 1; | |
415 //irs->addExp( t ); | |
416 if (code->dollarLabel) | |
417 d_expand_priv_asm_label(irs, code->dollarLabel); | |
418 } |