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 }