Allow returning something of type void in a function that returns void
[delight/core.git] / asmstmt.cc
blobd921ee590877ea557e48b9d23dab53f40a55934b
1 #include "d-gcc-includes.h"
2 #include "total.h"
3 #include "statement.h"
5 #include "d-lang.h"
6 #include "d-codegen.h"
8 typedef enum {
9 Arg_Integer,
10 Arg_Pointer,
11 Arg_Memory,
12 Arg_FrameRelative,
13 Arg_LocalSize,
14 Arg_Dollar
15 } AsmArgType;
17 typedef enum {
18 Mode_Input,
19 Mode_Output,
20 Mode_Update
21 } AsmArgMode;
23 struct AsmArg {
24 AsmArgType type;
25 Expression * expr;
26 AsmArgMode mode;
27 AsmArg(AsmArgType type, Expression * expr, AsmArgMode mode) {
28 this->type = type;
29 this->expr = expr;
30 this->mode = mode;
34 struct AsmCode {
35 char * insnTemplate;
36 unsigned insnTemplateLen;
37 Array args; // of AsmArg
38 unsigned moreRegs;
39 unsigned dollarLabel;
40 int clobbersMemory;
41 AsmCode() {
42 insnTemplate = NULL;
43 insnTemplateLen = 0;
44 moreRegs = 0;
45 dollarLabel = 0;
46 clobbersMemory = 0;
50 #if D_GCC_VER >= 40
51 /* Apple GCC extends ASM_EXPR to five operands; cannot use build4. */
52 tree
53 d_build_asm_stmt(tree t1, tree t2, tree t3, tree t4)
55 tree t = make_node(ASM_EXPR);
56 TREE_TYPE(t) = void_type_node;
57 SET_EXPR_LOCATION(t, input_location);
58 TREE_OPERAND(t,0) = t1;
59 TREE_OPERAND(t,1) = t2;
60 TREE_OPERAND(t,2) = t3;
61 TREE_OPERAND(t,3) = t4;
62 TREE_SIDE_EFFECTS(t) = 1;
63 return t;
65 #endif
67 AsmStatement::AsmStatement(Loc loc, Token *tokens) :
68 Statement(loc)
70 this->tokens = tokens; // Do I need to copy these?
71 asmcode = 0;
72 asmalign = 0;
73 refparam = 0;
74 naked = 0;
75 regs = 0;
78 Statement *AsmStatement::syntaxCopy()
80 // copy tokens? copy 'code'?
81 AsmStatement * a_s = new AsmStatement(loc,tokens);
82 a_s->asmcode = asmcode;
83 a_s->refparam = refparam;
84 a_s->naked = naked;
85 a_s->regs = a_s->regs;
86 return a_s;
89 void AsmStatement::toCBuffer(OutBuffer *buf, HdrGenState *hgs)
91 bool sep = 0, nsep = 0;
92 buf->writestring("asm { ");
94 for (Token * t = tokens; t; t = t->next) {
95 switch (t->value) {
96 case TOKlparen:
97 case TOKrparen:
98 case TOKlbracket:
99 case TOKrbracket:
100 case TOKcolon:
101 case TOKsemicolon:
102 case TOKcomma:
103 case TOKstring:
104 case TOKcharv:
105 case TOKwcharv:
106 case TOKdcharv:
107 nsep = 0;
108 break;
109 default:
110 nsep = 1;
112 if (sep + nsep == 2)
113 buf->writeByte(' ');
114 sep = nsep;
115 buf->writestring(t->toChars());
117 buf->writestring("; }");
118 buf->writenl();
121 int AsmStatement::comeFrom()
123 return FALSE;
126 #if V2
129 AsmStatement::blockExit()
131 // TODO: Be smarter about this
132 return BEany;
135 #endif
137 /* GCC does not support jumps from asm statements. When optimization
138 is turned on, labels referenced only from asm statements will not
139 be output at the correct location. There are ways around this:
141 1) Reference the label with a reachable goto statement
142 2) Have reachable computed goto in the function
143 3) Hack cfgbuild.c to act as though there is a computed goto.
145 These are all pretty bad, but if would be nice to be able to tell
146 GCC not to optimize in this case (even on per label/block basis).
148 The current solution is output our own private labels (as asm
149 statements) along with the "real" label. If the label happens to
150 be referred to by a goto statement, the "real" label will also be
151 output in the correct location.
153 Also had to add 'asmLabelNum' to LabelDsymbol to indicate it needs
154 special processing.
156 (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.
160 static unsigned d_priv_asm_label_serial = 0;
162 // may need to make this target-specific
163 static void d_format_priv_asm_label(char * buf, unsigned n)
165 //ASM_GENERATE_INTERNAL_LABEL(buf, "LDASM", n);//inserts a '*' for use with assemble_name
166 sprintf(buf, ".LDASM%u", n);
169 void
170 d_expand_priv_asm_label(IRState * irs, unsigned n)
172 char buf[64];
173 d_format_priv_asm_label(buf, n);
174 strcat(buf, ":");
175 tree insnt = build_string(strlen(buf), buf);
176 #if D_GCC_VER < 40
177 expand_asm(insnt, 1);
178 #else
179 tree t = d_build_asm_stmt(insnt, NULL_TREE, NULL_TREE, NULL_TREE);
180 ASM_VOLATILE_P( t ) = 1;
181 ASM_INPUT_P( t) = 1; // what is this doing?
182 irs->addExp(t);
183 #endif
186 ExtAsmStatement::ExtAsmStatement(Loc loc, Expression *insnTemplate, Expressions *args, Array *argNames,
187 Expressions *argConstraints, int nOutputArgs, Expressions *clobbers)
188 : Statement(loc)
190 this->insnTemplate = insnTemplate;
191 this->args = args;
192 this->argNames = argNames;
193 this->argConstraints = argConstraints;
194 this->nOutputArgs = nOutputArgs;
195 this->clobbers = clobbers;
198 Statement *ExtAsmStatement::syntaxCopy()
200 /* insnTemplate, argConstraints, and clobbers would be
201 semantically static in GNU C. */
202 Expression *insnTemplate = this->insnTemplate->syntaxCopy();
203 Expressions * args = Expression::arraySyntaxCopy(this->args);
204 // argNames is an array of identifiers
205 Expressions * argConstraints = Expression::arraySyntaxCopy(this->argConstraints);
206 Expressions * clobbers = Expression::arraySyntaxCopy(this->clobbers);
207 return new ExtAsmStatement(loc, insnTemplate, args, argNames,
208 argConstraints, nOutputArgs, clobbers);
211 Statement *ExtAsmStatement::semantic(Scope *sc)
213 insnTemplate = insnTemplate->semantic(sc);
214 insnTemplate = insnTemplate->optimize(WANTvalue);
215 if (insnTemplate->op != TOKstring || ((StringExp *)insnTemplate)->sz != 1)
216 error("instruction template must be a constant char string");
217 if (args)
218 for (unsigned i = 0; i < args->dim; i++) {
219 Expression * e = (Expression *) args->data[i];
220 e = e->semantic(sc);
221 if (i < nOutputArgs)
222 e = e->modifiableLvalue(sc, NULL);
223 else
224 e = e->optimize(WANTvalue|WANTinterpret);
225 args->data[i] = e;
227 e = (Expression *) argConstraints->data[i];
228 e = e->semantic(sc);
229 e = e->optimize(WANTvalue);
230 if (e->op != TOKstring || ((StringExp *)e)->sz != 1)
231 error("constraint must be a constant char string");
232 argConstraints->data[i] = e;
234 if (clobbers)
235 for (unsigned i = 0; i < clobbers->dim; i++) {
236 Expression * e = (Expression *) clobbers->data[i];
237 e = e->semantic(sc);
238 e = e->optimize(WANTvalue);
239 if (e->op != TOKstring || ((StringExp *)e)->sz != 1)
240 error("clobber specification must be a constant char string");
241 clobbers->data[i] = e;
243 return this;
246 #if V2
249 ExtAsmStatement::blockExit()
251 // TODO: Be smarter about this
252 return BEany;
255 #endif
257 // StringExp::toIR usually adds a NULL. We don't want that...
259 static tree
260 naturalString(Expression * e)
262 // don't fail, just an error?
263 assert(e->op == TOKstring);
264 StringExp * s = (StringExp *) e;
265 assert(s->sz == 1);
266 return build_string(s->len, (char *) s->string);
269 void ExtAsmStatement::toIR(IRState *irs)
271 ListMaker outputs;
272 ListMaker inputs;
273 ListMaker tree_clobbers;
275 gen.doLineNote( loc );
277 if (this->args)
278 for (unsigned i = 0; i < args->dim; i++)
280 Identifier * name = argNames->data[i] ? (Identifier *) argNames->data[i] : NULL;
281 Expression * constr = (Expression *) argConstraints->data[i];
282 tree p = tree_cons(name ? build_string(name->len, name->string) : NULL_TREE,
283 naturalString(constr), NULL_TREE);
284 tree v = ((Expression *) args->data[i])->toElem(irs);
286 if (i < nOutputArgs)
287 outputs.cons(p, v);
288 else
289 inputs.cons(p, v);
291 if (clobbers)
292 for (unsigned i = 0; i < clobbers->dim; i++) {
293 Expression * clobber = (Expression *) clobbers->data[i];
294 tree_clobbers.cons(NULL_TREE, naturalString(clobber));
297 irs->doAsm(naturalString(insnTemplate), outputs.head, inputs.head, tree_clobbers.head);
300 #ifdef TARGET_80387
301 #include "d-asm-i386.h"
302 #else
303 #define D_NO_INLINE_ASM_AT_ALL
304 #endif
306 #ifndef D_NO_INLINE_ASM_AT_ALL
308 bool d_have_inline_asm() { return true; }
310 Statement *AsmStatement::semantic(Scope *sc)
313 sc->func->inlineAsm = 1;
314 sc->func->inlineStatus = ILSno; // %% not sure
315 // %% need to set DECL_UNINLINABLE too?
316 sc->func->hasReturnExp = 1; // %% DMD does this, apparently...
318 // empty statement -- still do the above things because they might be expected?
319 if (! tokens)
320 return this;
322 AsmProcessor ap(sc, this);
323 ap.run();
324 return this;
327 void
328 AsmStatement::toIR(IRState * irs)
330 gen.doLineNote( loc );
332 if (! asmcode)
333 return;
335 static tree i_cns = 0;
336 static tree p_cns = 0;
337 static tree m_cns = 0;
338 static tree mw_cns = 0;
339 static tree mrw_cns = 0;
340 static tree memory_name = 0;
342 if (! i_cns) {
343 i_cns = build_string(1, "i");
344 p_cns = build_string(1, "p");
345 m_cns = build_string(1, "m");
346 mw_cns = build_string(2, "=m");
347 mrw_cns = build_string(2, "+m");
348 memory_name = build_string(6, "memory");
349 dkeep(i_cns);
350 dkeep(p_cns);
351 dkeep(m_cns);
354 AsmCode * code = (AsmCode *) asmcode;
355 ListMaker inputs;
356 ListMaker outputs;
357 ListMaker clobbers;
358 //tree dollar_label = NULL_TREE;//OLD
359 HOST_WIDE_INT var_frame_offset; // "frame_offset" is a macro
360 bool clobbers_mem = code->clobbersMemory;
361 int input_idx = 0;
362 int n_outputs = 0;
363 int arg_map[10];
365 assert(code->args.dim <= 10);
367 for (unsigned i = 0; i < code->args.dim; i++) {
368 AsmArg * arg = (AsmArg *) code->args.data[i];
370 bool is_input = true;
371 tree arg_val = NULL_TREE;
372 tree cns = NULL_TREE;
374 switch (arg->type) {
375 case Arg_Integer:
376 arg_val = arg->expr->toElem(irs);
377 do_integer:
378 cns = i_cns;
379 break;
380 case Arg_Pointer:
381 if (arg->expr->op == TOKvar)
382 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
383 else if (arg->expr->op == TOKdsymbol) {
384 arg_val = irs->getLabelTree( (LabelDsymbol *) ((DsymbolExp *) arg->expr)->s );
385 } else
386 assert(0);
387 arg_val = irs->addressOf(arg_val);
388 cns = p_cns;
389 break;
390 case Arg_Memory:
391 if (arg->expr->op == TOKvar)
392 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
393 else if (arg->expr->op == TOKfloat64)
395 /* Constant scalar value. In order to reference it as memory,
396 create an anonymous static var. */
397 tree cnst = build_decl(VAR_DECL, NULL_TREE, arg->expr->type->toCtype());
398 g.ofile->giveDeclUniqueName(cnst);
399 DECL_INITIAL(cnst) = arg->expr->toElem(irs);
400 TREE_STATIC(cnst) = TREE_CONSTANT(cnst) = TREE_READONLY(cnst) =
401 TREE_PRIVATE(cnst) = DECL_ARTIFICIAL(cnst) = DECL_IGNORED_P(cnst) = 1;
402 g.ofile->rodc(cnst, 1);
403 arg_val = cnst;
405 else
406 arg_val = arg->expr->toElem(irs);
407 if (DECL_P( arg_val ))
408 TREE_ADDRESSABLE( arg_val ) = 1;
409 switch (arg->mode) {
410 case Mode_Input: cns = m_cns; break;
411 case Mode_Output: cns = mw_cns; is_input = false; break;
412 case Mode_Update: cns = mrw_cns; is_input = false; break;
413 default: assert(0); break;
415 break;
416 case Arg_FrameRelative:
417 if (arg->expr->op == TOKvar)
418 arg_val = ((VarExp *) arg->expr)->var->toSymbol()->Stree;
419 else
420 assert(0);
421 if ( getFrameRelativeValue(arg_val, & var_frame_offset) ) {
422 arg_val = irs->integerConstant(var_frame_offset);
423 cns = i_cns;
424 } else {
425 this->error("%s", "argument not frame relative");
426 return;
428 if (arg->mode != Mode_Input)
429 clobbers_mem = true;
430 break;
431 case Arg_LocalSize:
432 var_frame_offset = cfun->x_frame_offset;
433 if (var_frame_offset < 0)
434 var_frame_offset = - var_frame_offset;
435 arg_val = irs->integerConstant( var_frame_offset );
436 goto do_integer;
437 /* OLD
438 case Arg_Dollar:
439 if (! dollar_label)
440 dollar_label = build_decl(LABEL_DECL, NULL_TREE, void_type_node);
441 arg_val = dollar_label;
442 goto do_pointer;
444 default:
445 assert(0);
448 if (is_input) {
449 arg_map[i] = --input_idx;
450 inputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val);
451 } else {
452 arg_map[i] = n_outputs++;
453 outputs.cons(tree_cons(NULL_TREE, cns, NULL_TREE), arg_val);
457 // Telling GCC that callee-saved registers are clobbered makes it preserve
458 // those registers. This changes the stack from what a naked function
459 // expects.
461 if (! irs->func->naked) {
462 for (int i = 0; i < 32; i++) {
463 if (regs & (1 << i)) {
464 clobbers.cons(NULL_TREE, regInfo[i].gccName);
467 for (int i = 0; i < 32; i++) {
468 if (code->moreRegs & (1 << (i-32))) {
469 clobbers.cons(NULL_TREE, regInfo[i].gccName);
472 if (clobbers_mem)
473 clobbers.cons(NULL_TREE, memory_name);
477 // Remap argument numbers
478 for (unsigned i = 0; i < code->args.dim; i++) {
479 if (arg_map[i] < 0)
480 arg_map[i] = -arg_map[i] - 1 + n_outputs;
483 bool pct = false;
484 char * p = code->insnTemplate;
485 char * q = p + code->insnTemplateLen;
486 //printf("start: %.*s\n", code->insnTemplateLen, code->insnTemplate);
487 while (p < q) {
488 if (pct) {
489 if (*p >= '0' && *p <= '9') {
490 // %% doesn't check against nargs
491 *p = '0' + arg_map[*p - '0'];
492 pct = false;
493 } else if (*p == '%') {
494 pct = false;
496 //assert(*p == '%');// could be 'a', etc. so forget it..
497 } else if (*p == '%')
498 pct = true;
499 ++p;
502 //printf("final: %.*s\n", code->insnTemplateLen, code->insnTemplate);
504 tree insnt = build_string(code->insnTemplateLen, code->insnTemplate);
505 #if D_GCC_VER == 34
506 location_t gcc_loc = { loc.filename, loc.linnum };
507 expand_asm_operands(insnt, outputs.head, inputs.head, clobbers.head, 1, gcc_loc);
508 #else
509 tree t = d_build_asm_stmt(insnt, outputs.head, inputs.head, clobbers.head);
510 ASM_VOLATILE_P( t ) = 1;
511 irs->addExp( t );
512 #endif
513 //if (dollar_label)//OLD
514 // expand_label(dollar_label);
515 if (code->dollarLabel)
516 d_expand_priv_asm_label(irs, code->dollarLabel);
519 #else
521 bool d_have_inline_asm() { return false; }
523 Statement *
524 AsmStatement::semantic(Scope *sc)
526 sc->func->inlineAsm = 1;
527 return Statement::semantic(sc);
530 void
531 AsmStatement::toIR(IRState *)
533 sorry("assembler statements are not supported on this target");
536 #endif