1 /*
2 * Copyright (c) 2006 - 2010, Nils R. Weller
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 *
15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
19 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
20 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25 * POSSIBILITY OF SUCH DAMAGE.
26 *
27 * Parses gas inline assembly language statements and converts them to NASM
28 * code
29 */
30 #include "inlineasm.h"
31 #include <stdio.h>
32 #include <string.h>
33 #include <stdlib.h>
34 #include <stdarg.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #include <errno.h>
38 #include "defs.h"
39 #include "token.h"
40 #include "expr.h"
41 #include "backend.h"
42 #include "error.h"
43 #include "x86_emit_nasm.h"
44 #include "x86_emit_gas.h"
45 #include "x86_gen.h"
46 #include "n_libc.h"
47
48
49 static int lineno;
50 static struct token *asm_tok;
51
52 /*
53 * XXX the store_char() stuff bothers me, this adhoc interface should've been
54 * trashed long ago
55 */
56
57 static int
get_char(char ** code)58 get_char(char **code) {
59 int ret;
60
61 if (**code == 0) {
62 return EOF;
63 }
64 ret = (unsigned char)**code;
65 ++*code;
66 return ret;
67 }
68
69 static void
unget_char(int ch,char ** code)70 unget_char(int ch, char **code) {
71 (void) ch;
72 --*code;
73 }
74
75 #if 0
76 struct gas_token {
77 int type;
78 #define GAS_SEPARATOR 1001
79 #define GAS_DOTSTRING 1002
80 #define GAS_STRING 1003
81 #define GAS_DOLLAR 1004
82 #define GAS_OCTAL 1020
83 #define GAS_HEXA 1021
84 #define GAS_DECIMAL 1022
85 #define GAS_NUMBER(x) (x == GAS_OCTAL || x == GAS_HEXA || x == GAS_DECIMAL)
86 int lineno;
87 int idata;
88 void *data;
89 char *ascii;
90 struct gas_token *next;
91 };
92 #endif
93 #define ALLOCATED_TOKEN(type) \
94 (type == GAS_DOTIDENT || type == GAS_IDENT || \
95 type == GAS_OCTAL || type == GAS_HEXA || type == GAS_DECIMAL)
96
97 static struct gas_token *
make_gas_token(int type,void * data)98 make_gas_token(int type, void *data) {
99 struct gas_token *ret;
100 static struct gas_token nulltok;
101
102 ret = n_xmalloc(sizeof *ret);
103 *ret = nulltok;
104 ret->type = type;
105 ret->data = data;
106 ret->lineno = lineno;
107 switch (ret->type) {
108 case TOK_PAREN_OPEN:
109 ret->ascii = "(";
110 break;
111 case TOK_PAREN_CLOSE:
112 ret->ascii = ")";
113 break;
114 case GAS_IDENT:
115 case GAS_DOTIDENT:
116 case GAS_OCTAL:
117 case GAS_HEXA:
118 case GAS_DECIMAL:
119 default:
120 ret->ascii = ret->data;
121 break;
122 }
123
124 return ret;
125 }
126
127
128 static int gas_errors;
129
130 static void
gas_errorfl(struct gas_token * gt,const char * fmt,...)131 gas_errorfl(struct gas_token *gt, const char *fmt, ...) {
132 va_list va;
133
134 ++gas_errors;
135 ++errors;
136 fprintf(stderr, "%s:%d+%d: ", asm_tok->file, asm_tok->line, gt->lineno);
137 va_start(va, fmt);
138 vfprintf(stderr, fmt, va);
139 va_end(va);
140 fputc('\n', stderr);
141 print_source_line(asm_tok->line_ptr, asm_tok->tok_ptr, asm_tok->ascii,
142 1);
143 }
144
145 static struct gas_token *
get_string(char ** code)146 get_string(char **code) {
147 char *buf = NULL;
148 int ch;
149
150 store_char(NULL, 0);
151 while ((ch = get_char(code)) != EOF) {
152 if (ch == '"') {
153 break;
154 }
155 store_char(&buf, ch);
156 }
157 store_char(&buf, 0);
158 return make_gas_token(GAS_STRING, buf);
159 }
160
161 static struct gas_token *
get_ident(int firstch,char ** code)162 get_ident(int firstch, char **code) {
163 char *buf = NULL;
164 int toktype;
165 int ch;
166
167 store_char(NULL, 0);
168
169 if (firstch == '.') {
170 toktype = GAS_DOTIDENT;
171 } else {
172 toktype = GAS_IDENT;
173 store_char(&buf, firstch);
174 }
175
176 while ((ch = get_char(code)) != EOF) {
177 if (isalnum(ch) || ch == '_') {
178 store_char(&buf, ch);
179 } else {
180 unget_char(ch, code);
181 break;
182 }
183 }
184 store_char(&buf, 0);
185 return make_gas_token(toktype, buf);
186 }
187
188
189 static struct gas_token *
get_number(int firstch,char ** code,int sign)190 get_number(int firstch, char **code, int sign) {
191 char *buf = NULL;
192 struct gas_token *ret;
193 unsigned long *val;
194 int hexa = 0;
195 int octal = 0;
196 int ch;
197
198 store_char(NULL, 0);
199 store_char(&buf, firstch);
200 if (firstch == '0') {
201 /* May be hexadecimal */
202 if ((ch = get_char(code)) == EOF) {
203 goto out;
204 }
205 if (ch == 'x') {
206 store_char(&buf, 'x');
207 hexa = 1;
208 } else if (!isdigit(ch)) {
209 store_char(&buf, '0');
210 unget_char(ch, code);
211 goto out;
212 } else {
213 octal = 1;
214 store_char(&buf, ch);
215 }
216 }
217
218 while ((ch = get_char(code)) != EOF) {
219 ch = tolower(ch);
220 if (isdigit(ch)
221 || (hexa && ch && strchr("abcdef", ch))) {
222 store_char(&buf, ch);
223 #if 0
224 } else if ((ch == 'f' || ch == 'b') && !hexa) {
225 /*
226 * 02/28/09: This isn't really a number but a
227 * numeric label like ``1f'' or ``1b'';
228 *
229 * jne 1f
230 * 1:
231 */
232 store_char(&buf, ch);
233 if (ch == 'f') {
234 return make_gas_token(GAS_FORWARD_LABEL, buf);
235 } else {
236 return make_gas_token(GAS_BACKWARD_LABEL, buf);
237 }
238 #endif
239 } else {
240 unget_char(ch, code);
241 break;
242 }
243 }
244 out:
245 store_char(&buf, 0);
246 val = n_xmalloc(sizeof *val);
247 if (octal) {
248 sscanf(buf, "%lo", val);
249 ret = make_gas_token(GAS_OCTAL, buf);
250 } else if (hexa) {
251 sscanf(buf, "%lx", val);
252 ret = make_gas_token(GAS_HEXA, buf);
253 } else {
254 sscanf(buf, "%ld", val);
255 ret = make_gas_token(GAS_DECIMAL, buf);
256 }
257 ret->data2 = val;
258 if (sign) {
259 ret->idata = 1;
260 }
261 return ret;
262 }
263
264
265
266 static void
append_gas_list(struct gas_token ** dest,struct gas_token ** dest_tail,struct gas_token * src)267 append_gas_list(
268 struct gas_token **dest,
269 struct gas_token **dest_tail,
270 struct gas_token *src) {
271 if (*dest == NULL) {
272 *dest = *dest_tail = src;
273 } else {
274 (*dest_tail)->next = src;
275 *dest_tail = (*dest_tail)->next;
276 }
277 }
278
279 static void
free_gas_token(struct gas_token * t)280 free_gas_token(struct gas_token *t) {
281 if (t == NULL) return;
282 if (t->data != NULL) {
283 if (ALLOCATED_TOKEN(t->type)) {
284 free(t->data);
285 }
286 }
287 free(t);
288 }
289
290 static void
free_gas_token_list(struct gas_token * t)291 free_gas_token_list(struct gas_token *t) {
292 while (t != NULL) {
293 struct gas_token *next = t->next;
294 free_gas_token(t);
295 t = next;
296 }
297 }
298
299 static struct gas_token *
tokenize_gas_code(char * code)300 tokenize_gas_code(char *code) {
301 int ch;
302 struct gas_token *gt;
303 struct gas_token *glist = NULL;
304 struct gas_token *glist_tail = NULL;
305 struct gas_token dummytok;
306
307 lineno = 0;
308
309 while ((ch = get_char(&code)) != EOF) {
310 gt = NULL;
311 switch (ch) {
312 case '\t':
313 case ' ':
314 case '\n':
315 if (ch == '\n') {
316 ++lineno;
317 gt = make_gas_token(GAS_SEPARATOR, NULL);
318 }
319 break;
320 case ';':
321 gt = make_gas_token(GAS_SEPARATOR, NULL);
322 break;
323 case '(':
324 gt = make_gas_token(TOK_PAREN_OPEN, NULL);
325 break;
326 case ')':
327 gt = make_gas_token(TOK_PAREN_CLOSE, NULL);
328 break;
329 case '-':
330 while ((ch = get_char(&code)) != EOF
331 && isspace(ch))
332 ;
333
334 if (ch == EOF || !isdigit(ch)) {
335 dummytok.lineno = lineno;
336 gas_errorfl(&dummytok,
337 "Parse error at `%c' (%d)",
338 ch == EOF? '-': ch, __LINE__);
339 free_gas_token_list(glist);
340 return NULL;
341 }
342
343 gt = get_number(ch, &code, 1);
344 break;
345 case ',':
346 gt = make_gas_token(TOK_OP_COMMA, NULL);
347 break;
348 case '"':
349 gt = get_string(&code);
350 break;
351 case ':':
352 gt = make_gas_token(TOK_OP_AMB_COND2, ":");
353 break;
354 case '$':
355 gt = make_gas_token(GAS_DOLLAR, "$");
356 break;
357 case '%':
358 gt = make_gas_token(TOK_OP_MOD, "%");
359 break;
360 case '.':
361 #if 0
362 if ((ch = get_char(&code)) == EOF
363 || !isalnum((unsigned char)ch)) {
364 dummytok.lineno = lineno;
365 gas_errorfl(&dummytok,
366 "Parse error at `%c' (%d)",
367 ch, __LINE__);
368 free_gas_token_list(glist);
369 return NULL;
370 }
371 #endif
372 gt = get_ident(ch, &code);
373 break;
374 default:
375 if (isdigit((unsigned char)ch)) {
376 gt = get_number(ch, &code, 0);
377 } else if (isalpha((unsigned char)ch) || ch == '_') {
378 gt = get_ident(ch, &code);
379 } else {
380 dummytok.lineno = lineno;
381 gas_errorfl(&dummytok,
382 "Parse error at `%c' (%d)",
383 ch, __LINE__);
384 free_gas_token_list(glist);
385 return NULL;
386 }
387 }
388 if (gt != NULL) {
389 append_gas_list(&glist, &glist_tail, gt);
390 }
391 }
392
393 #if 0
394 printf("tokens:\n");
395 for (gt = glist; gt != NULL; gt = gt->next) {
396 printf("\t%d\n", gt->type);
397 }
398 #endif
399 return glist;
400 }
401
402 #if 0
403 struct gas_operand {
404 #define ITEM_REG 1
405 #define ITEM_NUMBER 2
406 #define ITEM_VARIABLE 3
407 #define ITEM_IO 4
408 int addr_mode;
409 #define ADDR_ABSOLUTE 1
410 #define ADDR_INDIRECT 2
411 #define ADDR_SCALED 3
412 #define ADDR_DISPLACE 4
413 #define ADDR_SCALED_DISPLACE 5
414 void *items[4];
415 int item_types[4];
416 };
417 #endif
418
419 static int
next_gas_token(struct gas_token ** tok)420 next_gas_token(struct gas_token **tok) {
421 if ((*tok)->next == NULL) {
422 struct gas_token dummy;
423 dummy.lineno = lineno;
424 gas_errorfl(&dummy, "Unexpected end of assembler statement");
425 return -1;
426 }
427 *tok = (*tok)->next;
428 return 0;
429 }
430
431 static int
is_number(const char * str)432 is_number(const char *str) {
433 for (; isdigit((unsigned char)*str); ++str);
434 ;
435 return *str == 0;
436 }
437
438 static struct inline_asm_io *
get_operand_by_idx(struct inline_asm_stmt * st,struct gas_token * t,int idx,int * curitem_type)439 get_operand_by_idx(
440 struct inline_asm_stmt *st,
441 struct gas_token *t,
442 int idx,
443 int *curitem_type) {
444
445 struct inline_asm_io *io;
446 int n_ios = st->n_inputs + st->n_outputs;
447
448 if (t != NULL) {
449 idx = (int)*(unsigned long *)t->data2;
450 }
451
452 if (idx >= n_ios) {
453 gas_errorfl(t, "I/O operand %d does "
454 "not exist, highest is %lu "
455 "(first one is %0!)", idx, n_ios);
456 return NULL;
457 }
458 ++idx;
459 if ((st->output != NULL
460 && idx > st->n_outputs)
461 || (st->output == NULL)) {
462 /* Must be input */
463 idx -= st->n_outputs;
464 if (curitem_type != NULL) {
465 *curitem_type = ITEM_INPUT;
466 }
467 io = st->input;
468 } else {
469 /* Output */
470 if (curitem_type != NULL) {
471 *curitem_type = ITEM_OUTPUT;
472 }
473 io = st->output;
474 }
475 while (--idx > 0) {
476 io = io->next;
477 }
478 return io;
479 }
480
481 static char *
get_constraint_by_idx(struct inline_asm_stmt * data,int idx)482 get_constraint_by_idx(struct inline_asm_stmt *data, int idx) {
483 int i;
484 struct inline_asm_io *io = data->output;
485 int input_done = 0;
486
487 for (i = 0; i < idx; ++i) {
488 if (io == NULL) {
489 if (input_done) {
490 return NULL;
491 } else {
492 io = data->input;
493 }
494 }
495 if (io == NULL) {
496 return NULL;
497 }
498 io = io->next;
499 }
500 if (io != NULL) {
501 return io->constraints;
502 }
503 return NULL;
504 }
505
506 /*
507 * Parses an operand to an instruction. This resembles a state machine,
508 * which is why it is hard to read. Basically, it just always reads either
509 * a parentheses or an ``item'' (a register, number or variable.)
510 * Parentheses may dictate some sort of indirect addressing mode. The items
511 * and their types are stored in the items and item_types arrays in the
512 * order in which they appear;
513 * -0x20(%eax) -> -0x20=items[0], %eax=items[1]
514 * 0x80(%eax,%ebx,0x4) -> 0x80=items[0], %eax=items[1], ....
515 */
516 static struct gas_operand *
parse_operand(struct gas_token ** tok,struct inline_asm_stmt * stmt)517 parse_operand(struct gas_token **tok, struct inline_asm_stmt *stmt) {
518 struct gas_token *t;
519 struct gas_operand *ret;
520 static struct gas_operand nullop;
521 void **curitem;
522 int *curitem_type;
523 int index = 0;
524
525 ret = n_xmalloc(sizeof *ret);
526 *ret = nullop;
527 curitem = &ret->items[0];
528 curitem_type = &ret->item_types[0];
529
530 for (t = *tok; t != NULL; t = t->next) {
531 if (t->type == GAS_DOLLAR) {
532 /* Should be immediate - $1234 */
533 if (next_gas_token(&t) != 0) {
534 return NULL;
535 }
536 if (t->type == GAS_IDENT) {
537 *curitem_type = ITEM_VARIABLE;
538 *curitem = t;
539 } else if (GAS_NUMBER(t->type)) {
540 *curitem_type = ITEM_NUMBER;
541 *curitem = t;
542 } else {
543 gas_errorfl(t, "Parse error at `%s' (%d)",
544 t->ascii, __LINE__);
545 return NULL;
546 }
547 } else if (GAS_NUMBER(t->type)) {
548 if (t->next
549 && t->next->type == GAS_IDENT
550 && (strcmp(t->next->data, "b") == 0
551 || strcmp(t->next->data, "f") == 0)) {
552 *curitem_type = ITEM_LABEL;
553 *curitem = /* XXX */
554 backend->get_inlineasm_label(t->data);
555 t = t->next;
556 } else {
557 *curitem_type = ITEM_NUMBER;
558 *curitem = t;
559 if (ret->addr_mode == 0) {
560 ret->addr_mode = ADDR_ABSOLUTE;
561 } else {
562 gas_errorfl(t,
563 "Parse error at `%s' (%d)",
564 t->ascii, __LINE__);
565 return NULL;
566 }
567 }
568 ++index;
569 curitem = &ret->items[index];
570 curitem_type = &ret->item_types[index];
571 } else if (t->type == GAS_IDENT) {
572 /*
573 * XXX can this possibly be right, and made work with
574 * NASM?!?
575 */
576 *curitem_type = ITEM_VARIABLE;
577 *curitem = t;
578 if (ret->addr_mode == 0) {
579 ret->addr_mode = ADDR_INDIRECT;
580 } else {
581 gas_errorfl(t, "Parse error at `%s' (%d)",
582 t->ascii, __LINE__);
583 return NULL;
584 }
585 #if 0
586 } else if (t->type == GAS_FORWARD_LABEL
587 || t->type == GAS_BACKWARD_LABEL) {
588 *curitem_type = ITEM_FW_BW;
589 if (ret->addr_mode == 0) {
590 ret->addr_mode = ADDR_
591 #endif
592 } else if (t->type == TOK_OP_MOD) {
593 if (next_gas_token(&t) != 0) {
594 return NULL;
595 }
596 if (t->type == TOK_OP_MOD) {
597 /* Should be %%reg */
598 if (next_gas_token(&t) != 0) {
599 return NULL;
600 }
601 if (t->type != GAS_IDENT) {
602 gas_errorfl(t,
603 "Parse error at `%s' (%d)",
604 t->ascii, __LINE__);
605 return NULL;
606 } else if (!stmt->extended) {
607 gas_errorfl(t, "Second %% in %%%%reg "
608 "is only allowed for "
609 "extended asm statements "
610 "(use %reg instead)");
611 return NULL;
612 }
613 *curitem_type = ITEM_REG;
614 *curitem = t;
615 } else if (GAS_NUMBER(t->type)) {
616 struct inline_asm_io *io;
617
618 /* i/o operand */
619 if (!stmt->extended) {
620 gas_errorfl(t, "`%%number' is only "
621 "allowed in extended asm "
622 "statements");
623 return NULL;
624 }
625 io = get_operand_by_idx(stmt, t, 0,
626 curitem_type);
627
628 if (io == NULL) {
629 return NULL;
630 }
631 *curitem = io;
632 ++index;
633 curitem = &ret->items[index];
634 curitem_type = &ret->item_types[index];
635 } else if (t->type == GAS_IDENT) {
636 if (stmt->extended) {
637 if ((t->ascii[0] == 'h'
638 || t->ascii[0] == 'b'
639 || t->ascii[0] == 'w')
640 && is_number(t->ascii+1)) {
641 char *p;
642 long rc;
643 struct inline_asm_io *io;
644
645 errno = 0;
646 rc = strtol(t->ascii+1, &p, 0);
647 if (errno
648 || rc == LONG_MIN
649 || rc == LONG_MAX
650 || *p) {
651 gas_errorfl(t,
652 "Parse error at `%s' (%d)",
653 t->ascii, __LINE__);
654 return NULL;
655 }
656 io = get_operand_by_idx(stmt,
657 NULL, rc, curitem_type);
658 if (io == NULL) {
659 return NULL;
660 }
661
662 *curitem = io;
663 if (t->ascii[0] == 'w') {
664 *curitem_type = ITEM_SUBREG_W;
665 } else {
666 *curitem_type =
667 t->ascii[0] == 'b'?
668 ITEM_SUBREG_B
669 : ITEM_SUBREG_H;
670 }
671 } else {
672 gas_errorfl(t, "%%reg isn't "
673 "allowed in extended asm statements (use %%%%reg instead)");
674 return NULL;
675 }
676 } else {
677 *curitem_type = ITEM_REG;
678 *curitem = t;
679 }
680 ++index;
681 curitem = &ret->items[index];
682 curitem_type = &ret->item_types[index];
683 } else {
684 gas_errorfl(t, "Parse error at `%s' (%d)",
685 t->ascii, __LINE__);
686 return NULL;
687 }
688 } else if (t->type == TOK_PAREN_OPEN) {
689 if (t == *tok) {
690 /* Scaled or indirect addressing */
691 ret->addr_mode = ADDR_INDIRECT;
692 } else if (ret->item_types[0] != 0) {
693 /*
694 * Displacement - $123(stuff)
695 */
696 if (ret->item_types[0] != ITEM_NUMBER) {
697 gas_errorfl(t, "Invalid "
698 "displacement");
699 return NULL;
700 }
701 ret->addr_mode = ADDR_DISPLACE;
702 curitem = &ret->items[1];
703 curitem_type = &ret->item_types[1];
704 ++index;
705 } else {
706 gas_errorfl(t, "Parse error at `%s' (%d)",
707 t->ascii, __LINE__);
708 return NULL;
709 }
710 } else if (t->type == TOK_PAREN_CLOSE) {
711 if (ret->addr_mode != ADDR_INDIRECT
712 && ret->addr_mode != ADDR_DISPLACE
713 && ret->addr_mode != ADDR_SCALED
714 && ret->addr_mode != ADDR_SCALED_DISPLACE) {
715 gas_errorfl(t, "Parse error at `%s' (%d)",
716 t->ascii, __LINE__);
717 return NULL;
718 }
719 if (t->next) {
720 if (t->next->type == TOK_OP_COMMA
721 || t->next->type == GAS_SEPARATOR) {
722 t = t->next;
723 break;
724 } else {
725 /* XXX wut to do?! */
726 }
727 } else {
728 t = t->next;
729 break;
730 }
731 } else if (t->type == TOK_OP_COMMA) {
732 if (ret->addr_mode != ADDR_INDIRECT
733 && ret->addr_mode != ADDR_SCALED
734 && ret->addr_mode != ADDR_DISPLACE
735 && ret->addr_mode != ADDR_SCALED_DISPLACE) {
736 /* ok, operand ends here */
737 break;
738 } else if (ret->addr_mode == ADDR_SCALED
739 || ret->addr_mode == ADDR_SCALED_DISPLACE) {
740 int max = ret->addr_mode == ADDR_SCALED?
741 2: 3;
742 if (index >= max) {
743 gas_errorfl(t, "Too many operands"
744 " for scaled addressing");
745 } else {
746 curitem = &ret->items[++index];
747 curitem_type = &ret->item_types[index];
748 }
749 } else {
750 if (ret->addr_mode == ADDR_DISPLACE) {
751 /* 0x123(foo,bar,baz) */
752 ret->addr_mode = ADDR_SCALED_DISPLACE;
753 } else {
754 ret->addr_mode = ADDR_SCALED;
755 }
756 ++index;
757 curitem = &ret->items[index];
758 curitem_type = &ret->item_types[index];
759 }
760 } else if (t->type == GAS_SEPARATOR) {
761 /* Instruction ends here */
762 break;
763 } else {
764 gas_errorfl(t, "Parse error at `%s' (%d)",
765 t->ascii, __LINE__);
766 return NULL;
767 }
768 }
769 *tok = t;
770 return ret;
771 }
772
773 #if 0
774 static void
775 free_gas_operand(struct gas_operand *t) {
776 (void)t;
777 }
778 #endif
779
780
781 static void
782 store_plusminus_num(FILE *out, void *item) {
783 struct gas_token *nt;
784
785 x_fputc(' ', out);
786 nt = item;
787 if (!nt->idata) {
788 /* Not signed */
789 x_fputc('+', out);
790 } else {
791 x_fputc('-', out);
792 }
793 x_fputc(' ', out);
794
795 print_item_nasm(out, item, ITEM_NUMBER, 0);
796 }
797
798 static void
799 print_operand_nasm(FILE *out, struct gas_operand *op, int postfix) {
800 char *p;
801 int i;
802
803 if (op->addr_mode != 0 && postfix) {
804 if (postfix == 'b') {
805 p = "byte";
806 } else if (postfix == 'w') {
807 p = "word";
808 } else if (postfix == 'l') {
809 p = "dword";
810 } else if (postfix == 'q') {
811 p = "qword";
812 } else if (postfix == 't') {
813 p = "tword";
814 } else {
815 abort();
816 }
817 x_fprintf(out, "%s ", p);
818 }
819
820 switch (op->addr_mode) {
821 case 0:
822 print_item_nasm(out, op->items[0], op->item_types[0], postfix);
823 break;
824 case ADDR_INDIRECT:
825 x_fputc('[', out);
826 print_item_nasm(out, op->items[0], op->item_types[0], postfix);
827 x_fputc(']', out);
828 break;
829 case ADDR_SCALED:
830 case ADDR_SCALED_DISPLACE:
831 if (op->addr_mode == ADDR_SCALED) {
832 i = 0;
833 } else {
834 i = 1;
835 }
836
837 x_fputc('[', out);
838 print_item_nasm(out, op->items[i], op->item_types[i], postfix);
839 x_fprintf(out, " + ");
840 ++i;
841 print_item_nasm(out, op->items[i], op->item_types[i], postfix);
842
843 ++i;
844 if (op->item_types[i] != 0) {
845 x_fprintf(out, " * ");
846 print_item_nasm(out, op->items[i],
847 op->item_types[i], postfix);
848 }
849 if (op->addr_mode == ADDR_SCALED_DISPLACE) {
850 store_plusminus_num(out, op->items[0]);
851 }
852 x_fputc(']', out);
853 break;
854 case ADDR_DISPLACE:
855 x_fputc('[', out);
856 print_item_nasm(out, op->items[1], op->item_types[1], postfix);
857 store_plusminus_num(out, op->items[0]);
858 x_fputc(']', out);
859 break;
860 default:
861 printf("UNKNOWN ADDRESSING MODE %d\n", op->addr_mode);
862 abort();
863 }
864 }
865
866 static void
867 print_operand_gas(FILE *out, struct gas_operand *op, int postfix) {
868 int i;
869
870 switch (op->addr_mode) {
871 case 0:
872 print_item_gas_x86(out, op->items[0], op->item_types[0], postfix);
873 break;
874 case ADDR_INDIRECT:
875 x_fputc('(', out);
876 print_item_gas_x86(out, op->items[0], op->item_types[0], postfix);
877 x_fputc(')', out);
878 break;
879 case ADDR_SCALED:
880 case ADDR_SCALED_DISPLACE:
881 if (op->addr_mode == ADDR_SCALED) {
882 i = 0;
883 } else {
884 i = 1;
885 }
886
887 x_fputc('(', out);
888 print_item_gas_x86(out, op->items[i], op->item_types[i], postfix);
889 x_fprintf(out, ",");
890 ++i;
891 print_item_gas_x86(out, op->items[i], op->item_types[i], postfix);
892
893 ++i;
894 if (op->item_types[i] != 0) {
895 x_fprintf(out, ",");
896 print_item_gas_x86(out, op->items[i],
897 op->item_types[i], postfix);
898 }
899 if (op->addr_mode == ADDR_SCALED_DISPLACE) {
900 store_plusminus_num(out, op->items[0]);
901 }
902 x_fputc(')', out);
903 break;
904 case ADDR_DISPLACE:
905 store_plusminus_num(out, op->items[0]);
906 x_fputc('(', out);
907 print_item_gas_x86(out, op->items[1], op->item_types[1], postfix);
908 x_fputc(')', out);
909 break;
910 default:
911 printf("UNKNOWN ADDRESSING MODE %d\n", op->addr_mode);
912 abort();
913 }
914 }
915
916 static void
917 do_xlat_nasm(
918 FILE *out,
919 /*s truct gas_token *instr_tok,*/ char *name,
920 struct gas_operand *op,
921 struct gas_operand *op2,
922 struct gas_operand *op3,
923 int postfix) {
924
925 x_fprintf(out, "%s", name);
926 if (op == NULL && postfix == 'l') {
927 /* stosd, lodsd, etc */
928 x_fputc('d', out);
929 }
930 if (op != NULL) x_fputc(' ', out);
931 if (op3 != NULL) {
932 print_operand_nasm(out, op3, postfix);
933 x_fprintf(out, ", ");
934 }
935 if (op2 != NULL) {
936 print_operand_nasm(out, op2, postfix);
937 x_fprintf(out, ", ");
938 }
939 if (op != NULL) {
940 print_operand_nasm(out, op, postfix);
941 }
942 }
943
944
945 static void
946 do_xlat_gas(
947 FILE *out,
948 /*s truct gas_token *instr_tok,*/ char *name,
949 struct gas_operand *op,
950 struct gas_operand *op2,
951 struct gas_operand *op3,
952 int postfix) {
953
954 x_fprintf(out, "%s", name);
955 if (postfix) {
956 x_fputc(postfix, out);
957 }
958 if (op != NULL) x_fputc(' ', out);
959 if (op != NULL) {
960 print_operand_gas(out, op, postfix);
961 }
962 if (op2 != NULL) {
963 x_fprintf(out, ", ");
964 print_operand_gas(out, op2, postfix);
965 }
966 if (op3 != NULL) {
967 x_fprintf(out, ", ");
968 print_operand_gas(out, op3, postfix);
969 }
970 }
971
972 /*
973 * Returns instruction operand size (b for byte, w=word, l=longword,
974 * q=qword, t=tword) or zero, in which case no size is specified.
975 *
976 * The operand size is sometimes encoded by the last character and
977 * sometimes not. For example, ``int'' ends with t but doesn't take
978 * a tword argument. If the return value is nonzero, the encoding
979 * last character will be removed from the string.
980 *
981 * XXX This stuff is quite probably incorrect for a vast number of
982 * instructions :-(
983 */
984 static int
985 get_instr_postfix(char *instr, struct gas_operand *op) {
986 char *p;
987 int postfix = 0;
988
989 p = strchr(instr, 0);
990 --p;
991 if (instr[0] == 'f') {
992 /* Floating point */
993 if (strchr("slt", *p) != NULL) {
994 /* short (float), long (double) or tword (ldouble) */
995 postfix = *p;
996 *p = 0;
997 }
998 } else if (op != NULL) { /* XXX correct? */
999 if (strchr("bwlq", *p) != NULL) {
1000 /* byte/word/longword(dword)/qword */
1001 if (strcmp(instr, "leal") == 0) {
1002 /*
1003 * For some reason,
1004 * lea eax, dword [eax]
1005 * doesn't work in NASM, so let's
1006 * omit the size
1007 */
1008 ;
1009 } else {
1010 postfix = *p;
1011 }
1012 *p = 0;
1013 }
1014 } else if (op == NULL && *p == 'l') {
1015 /*
1016 * stosd, lodsd, etc are written as stosl, etc
1017 */
1018 postfix = *p;
1019 *p = 0;
1020 }
1021 return postfix;
1022 }
1023
1024 static void
1025 append_instr(
1026 struct inline_instr **dest,
1027 struct inline_instr **dest_tail,
1028 struct inline_instr *instr) {
1029
1030 if (*dest == NULL) {
1031 *dest = *dest_tail = instr;
1032 } else {
1033 (*dest_tail)->next = instr;
1034 *dest_tail = (*dest_tail)->next;
1035 }
1036 }
1037
1038 static struct inline_instr *
1039 do_parse_asm(struct gas_token *tok, struct inline_asm_stmt *stmt) {
1040 struct gas_token *t;
1041 struct gas_token *nexttok;
1042 struct inline_instr *ret = NULL;
1043 struct inline_instr *ret_tail = NULL;
1044 struct inline_instr *tmp;
1045 static struct inline_instr nullinstr;
1046
1047 for (t = tok; t != NULL;) {
1048 if (t->type == GAS_DOTIDENT) {
1049 /* Assembler directive */
1050 warningfl(asm_tok,
1051 "Ignoring assembler directive `%s'",
1052 t->ascii);
1053 do {
1054 t = t->next;
1055 } while (t && t->type != GAS_SEPARATOR);
1056 } else if (t->type == GAS_SEPARATOR) {
1057 ;
1058 } else if ((t->type == GAS_IDENT
1059 || GAS_NUMBER(t->type))
1060 && t->next
1061 && t->next->type == TOK_OP_AMB_COND2) {
1062 /* Label */
1063 tmp = n_xmalloc(sizeof *tmp);
1064 *tmp = nullinstr;
1065 tmp->type = INLINSTR_LABEL;
1066 if (t->type == GAS_IDENT) {
1067 tmp->name = t->data;
1068 } else {
1069 tmp->name
1070 = backend->get_inlineasm_label(t->data);
1071 }
1072 t = t->next;
1073 append_instr(&ret, &ret_tail, tmp);
1074 } else if (t->type == GAS_IDENT) {
1075 /* Has to be instruction!? */
1076 struct gas_operand *op = NULL;
1077 struct gas_operand *op2 = NULL;
1078 struct gas_token *t2;
1079 struct gas_token *instr_tok = t;
1080
1081 t2 = t->next;
1082 if (t2
1083 && t2->type != GAS_SEPARATOR
1084 && (op = parse_operand(&t2, stmt)) != NULL) {
1085 t = t2;
1086 if (t2 && t2->type == TOK_OP_COMMA) {
1087 if ((t2 = t2->next) &&
1088 (op2 = parse_operand(&t2, stmt))
1089 != NULL) {
1090 t = t2;
1091 }
1092 }
1093 }
1094 if (gas_errors) {
1095 break;
1096 }
1097
1098 tmp = n_xmalloc(sizeof *tmp);
1099 *tmp = nullinstr;
1100 tmp->type = INLINSTR_REAL;
1101 tmp->name = instr_tok->data;
1102 tmp->postfix = get_instr_postfix(instr_tok->data, op);
1103 tmp->operands[0] = op;
1104 tmp->operands[1] = op2;
1105 tmp->operands[2] = NULL;
1106 append_instr(&ret, &ret_tail, tmp);
1107 } else {
1108 gas_errorfl(t, "Parse error at `%s' (%d)",
1109 t->ascii, __LINE__);
1110 }
1111 if (t == NULL) {
1112 break;
1113 }
1114 nexttok = t->next;
1115 /*free_gas_token(t);*/
1116 t = nexttok;
1117 }
1118
1119 if (gas_errors) {
1120 free(ret);
1121 return NULL;
1122 }
1123 return ret;
1124 }
1125
1126
1127 static void
1128 inline_instr_to_gas_or_nasm(FILE *out, struct inline_instr *code, int to) {
1129 struct inline_instr *tmp;
1130
1131 for (tmp = code; tmp != NULL; tmp = tmp->next) {
1132 if (tmp->type == INLINSTR_REAL) {
1133 if (to == TO_NASM) {
1134 do_xlat_nasm(out, tmp->name, tmp->operands[0],
1135 tmp->operands[1], NULL, tmp->postfix);
1136 } else {
1137 do_xlat_gas(out, tmp->name, tmp->operands[0],
1138 tmp->operands[1], NULL, tmp->postfix);
1139 }
1140 } else if (tmp->type == INLINSTR_LABEL) {
1141 emit->label(tmp->name, 0);
1142 } else if (tmp->type == INLINSTR_ASM) {
1143 }
1144 x_fputc('\n', out);
1145 }
1146 }
1147
1148 void
1149 inline_instr_to_nasm(FILE *out, struct inline_instr *code) {
1150 inline_instr_to_gas_or_nasm(out, code, TO_NASM);
1151 }
1152
1153 void
1154 inline_instr_to_gas(FILE *out, struct inline_instr *code) {
1155 inline_instr_to_gas_or_nasm(out, code, TO_GAS);
1156 }
1157
1158 static struct inline_asm_io *
1159 get_iolist(struct token **tok, int is_output, int *err, int *n_items,
1160 struct inline_asm_stmt *data) {
1161
1162 struct token *t;
1163 struct inline_asm_io *ret = NULL;
1164 struct inline_asm_io *ret_tail = NULL;
1165 struct expr *ex;
1166
1167 *n_items = 0;
1168 *err = 1;
1169 if (next_token(tok) != 0) {
1170 return NULL;
1171 }
1172 for (t = *tok; t != NULL; t = t->next) {
1173 if (t->type == TOK_OPERATOR
1174 && *(int *)t->data == TOK_OP_AMB_COND2) {
1175 /* Terminating : */
1176 break;
1177 } else if (t->type == TOK_PAREN_CLOSE) {
1178 /* Entire asm statement ends here */
1179 break;
1180 } else if (t->type == TOK_STRING_LITERAL) {
1181 struct inline_asm_io *tmp;
1182 char *constraints = t->ascii;
1183
1184 if (is_output) {
1185 char *p;
1186
1187 if ((p = strchr(constraints, '=')) == NULL) {
1188 if (strchr(constraints, '+') == NULL) {
1189 errorfl(t, "Output operand "
1190 "constraints lack `='");
1191 return NULL;
1192 }
1193 } else if (constraints[0] != '=') {
1194 warningfl(t, "Output operand "
1195 "constraints don't begin "
1196 "with `='");
1197 memmove(p, p + 1, strlen(p));
1198 }
1199 }
1200
1201 /*
1202 * "constraints" (expr)
1203 */
1204 if (expect_token(&t, TOK_PAREN_OPEN, 1) != 0) {
1205 return NULL;
1206 }
1207 ex = parse_expr(&t, TOK_PAREN_CLOSE, 0, 0, 1);
1208 if (ex == NULL) {
1209 return NULL;
1210 }
1211 tmp = n_xmalloc(sizeof *tmp);
1212 if (is_output && constraints[0] == '=') {
1213 tmp->constraints = constraints + 1;
1214 } else {
1215 tmp->constraints = constraints;
1216 }
1217 if (isdigit((unsigned char)*tmp->constraints)) {
1218 tmp->constraints = get_constraint_by_idx(
1219 data, *tmp->constraints - '0');
1220 if (tmp->constraints == NULL) {
1221 return NULL;
1222 }
1223 }
1224 tmp->expr = ex; /* XXX list order ok? */
1225 tmp->next = NULL;
1226 tmp->index = *n_items;
1227 if (ret == NULL) {
1228 ret = ret_tail = tmp;
1229 } else {
1230 ret_tail->next = tmp;
1231 ret_tail = tmp;
1232 }
1233 ++*n_items;
1234 if (next_token(&t) != 0) {
1235 return NULL;
1236 }
1237 if (t->type == TOK_OPERATOR
1238 && *(int *)t->data == TOK_OP_COMMA) {
1239 ;
1240 } else if (t->type == TOK_PAREN_CLOSE) {
1241 break;
1242 } else if (t->type == TOK_OPERATOR
1243 && *(int *)t->data == TOK_OP_AMB_COND2) {
1244 break;
1245 } else {
1246 errorfl(t, "Parse error at `%s'", t->ascii);
1247 return NULL;
1248 }
1249 } else {
1250 errorfl(t, "Parse error at `%s'", t->ascii);
1251 return NULL;
1252 }
1253 }
1254 *err = 0;
1255 *tok = t;
1256
1257 return ret;
1258 }
1259
1260 static void
1261 free_iolist(struct inline_asm_io *io) {
1262 struct inline_asm_io *tmp;
1263
1264 while (io != NULL) {
1265 tmp = io->next;
1266 free(io);
1267 io = tmp;
1268 }
1269 }
1270
1271 static void
1272 resolve_constraint_refs(struct inline_asm_stmt *stmt,
1273 struct inline_asm_io *iotmp,
1274 int is_output) {
1275
1276 struct inline_asm_io *mapped;
1277
1278 if (iotmp->constraints[0] != '%'
1279 || !isdigit((unsigned char)iotmp->constraints[1])) {
1280 /* Nothing to do */
1281 return;
1282 }
1283 mapped = get_operand_by_idx(stmt, NULL,
1284 strtol(iotmp->constraints+1, NULL, 10),
1285 NULL);
1286 if (mapped == NULL) {
1287 errorfl(NULL, "Referenced constraint `%s' doesn't exist",
1288 iotmp->constraints);
1289 return;
1290 }
1291 iotmp->constraints = n_xmalloc(strlen(mapped->constraints + 2));
1292 if (is_output) {
1293 *iotmp->constraints = '=';
1294 strcpy(iotmp->constraints+1, mapped->constraints);
1295 } else {
1296 char *p;
1297 char *dest = iotmp->constraints;
1298
1299 for (p = mapped->constraints; *p != 0; ++p) {
1300 if (*p != '=') {
1301 *dest++ = *p;
1302 }
1303 }
1304 *dest = 0;
1305 }
1306 }
1307
1308 struct inline_asm_stmt *
1309 parse_inline_asm(struct token **tok) {
1310 struct token *t;
1311 struct inline_asm_stmt *ret;
1312 static struct inline_asm_stmt nullstmt;
1313 struct gas_token *gt = NULL;
1314 static int warned = 0;
1315 int err;
1316 struct inline_asm_io *iotmp;
1317
1318 #if !defined(__FreeBSD__) && !defined(__DragonFly__) /* FreeBSD uses inline asm heavily in the headers ... */
1319 if (!warned) {
1320 warningfl(*tok, "nwcc inline assembly support is new, "
1321 "incomplete and unproven");
1322 warned = 1;
1323 }
1324 #endif
1325
1326 gas_errors = 0;
1327
1328 if ((*tok)->next && (*tok)->next->type == TOK_KEY_VOLATILE) {
1329 /*
1330 * __asm__ __volatile__("...");
1331 */
1332 if (next_token(tok) != 0) {
1333 return NULL;
1334 }
1335 }
1336 if (expect_token(tok, TOK_PAREN_OPEN, 1) != 0) {
1337 return NULL;
1338 }
1339
1340 asm_tok = t = *tok;
1341 if (t->type != TOK_STRING_LITERAL) {
1342 errorfl(t, "Parse error at `%s' (%d)",
1343 t->ascii, __LINE__);
1344 return NULL;
1345 }
1346 /*
1347 * Syntax:
1348 * "code" : output : input : clobbered
1349 * ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ optional
1350 */
1351
1352 ret = n_xmalloc(sizeof *ret);
1353 *ret = nullstmt;
1354 if (*t->ascii != 0) {
1355 if ((gt = tokenize_gas_code(t->ascii)) == NULL) {
1356 recover(&t, TOK_PAREN_CLOSE, 0);
1357 return NULL;
1358 }
1359 }
1360
1361 if (next_token(&t) != 0) {
1362 return NULL;
1363 }
1364 err = 0;
1365 if (t->type == TOK_OPERATOR
1366 && *(int *)t->data == TOK_OP_AMB_COND2) {
1367 int n;
1368
1369 ret->extended = 1;
1370 /* Output specified? */
1371 if ((ret->output = get_iolist(&t, 1, &err, &n, ret)) == NULL
1372 && err) {
1373 } else if (t->type == TOK_OPERATOR
1374 && *(int *)t->data == TOK_OP_AMB_COND2) {
1375 ret->n_outputs = n;
1376 /* Input specified? */
1377 if ((ret->input = get_iolist(&t, 0, &err, &n, ret)) == NULL
1378 && err) {
1379 free_iolist(ret->output);
1380 } else {
1381 ret->n_inputs = n;
1382 }
1383 } else {
1384 ret->n_outputs = n;
1385 }
1386 if (err == 0
1387 && t->type == TOK_OPERATOR
1388 && *(int *)t->data == TOK_OP_AMB_COND2) {
1389 /* Clobbered register list */
1390 for (t = t->next; t != NULL; t = t->next) {
1391 if (t->type == TOK_STRING_LITERAL) {
1392 struct clobbered_reg *cr;
1393
1394 cr = n_xmalloc(sizeof *cr);
1395 cr->reg = backend->
1396 name_to_reg(t->ascii);
1397 if (cr->reg == NULL
1398 && strcmp(t->ascii, "memory")
1399 != 0
1400 && strcmp(t->ascii, "cc") != 0) {
1401 errorfl(t,
1402 "Unknown register `%s'",
1403 t->ascii);
1404 return NULL;
1405 }
1406 cr->next = ret->clobbered;
1407 ret->clobbered = cr;
1408 if ((t = t->next) == NULL) {
1409 break;
1410 } else if (t->type == TOK_OPERATOR
1411 && *(int *)t->data
1412 == TOK_OP_COMMA) {
1413 ; /* ok */
1414 } else if (t->type
1415 == TOK_PAREN_CLOSE) {
1416 break;
1417 }
1418 } else {
1419 errorfl(t, "Parse error at `%s'",
1420 t->ascii);
1421 return NULL;
1422 }
1423 }
1424 if (t == NULL) {
1425 errorfl(NULL, "Premature end of file");
1426 return NULL;
1427 }
1428 }
1429 } else if (t->type == TOK_PAREN_CLOSE) {
1430 ; /* done */
1431
1432 }
1433
1434 if (t->type != TOK_PAREN_CLOSE) {
1435 errorfl(t, "Parse error at `%s' (%d)",
1436 t->ascii, __LINE__);
1437 recover(&t, TOK_PAREN_CLOSE, 0);
1438 return NULL;
1439 }
1440 *tok = t;
1441
1442 if (!err && gt != NULL) {
1443 ret->code = do_parse_asm(gt, ret);
1444 }
1445 /*free_gas_token_list(gt);*/
1446 if (err) {
1447 free(ret);
1448 return NULL;
1449 }
1450 ret->toklist = gt;
1451
1452 /*
1453 * 07/11/09: Resolve references to other I/O constraints. E.g.
1454 *
1455 * __asm__("..." : "=r" (x) : "%0" (y));
1456 *
1457 * ... will copy the %0 constraint "=r" for for the %1
1458 * constraint (except it removes = because %1 is an input
1459 * operand)
1460 */
1461 for (iotmp = ret->input; iotmp != NULL; iotmp = iotmp->next) {
1462 resolve_constraint_refs(ret, iotmp, 0);
1463 }
1464 for (iotmp = ret->output; iotmp != NULL; iotmp = iotmp->next) {
1465 resolve_constraint_refs(ret, iotmp, 1);
1466 }
1467
1468 return ret;
1469 }
1470
1471
1472 char *
1473 parse_asm_varname(struct token **curtok) {
1474 char *asmname;
1475
1476 if (expect_token(curtok, TOK_PAREN_OPEN, 1) != 0) {
1477 return NULL;
1478 }
1479 if ((*curtok)->type != TOK_STRING_LITERAL) {
1480 errorfl(*curtok, "Parse error at `%s'", (*curtok)->ascii);
1481 return NULL;
1482 }
1483 asmname = (*curtok)->ascii;
1484 if (expect_token(curtok, TOK_PAREN_CLOSE, 1) != 0) {
1485 return NULL;
1486 }
1487 return asmname;
1488 }
1489
1490