Subversion Repositories Kolibri OS

Rev

Blame | Last modification | View Log | Download | RSS feed

  1. /*
  2.  *  GAS like assembler for TCC
  3.  *
  4.  *  Copyright (c) 2001-2004 Fabrice Bellard
  5.  *
  6.  * This library is free software; you can redistribute it and/or
  7.  * modify it under the terms of the GNU Lesser General Public
  8.  * License as published by the Free Software Foundation; either
  9.  * version 2 of the License, or (at your option) any later version.
  10.  *
  11.  * This library is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  14.  * Lesser General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU Lesser General Public
  17.  * License along with this library; if not, write to the Free Software
  18.  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  19.  */
  20.  
  21. static int asm_get_local_label_name(TCCState *s1, unsigned int n)
  22. {
  23.     char buf[64];
  24.     TokenSym *ts;
  25.  
  26.     snprintf(buf, sizeof(buf), "L..%u", n);
  27.     ts = tok_alloc(buf, strlen(buf));
  28.     return ts->tok;
  29. }
  30.  
  31. static void asm_expr(TCCState *s1, ExprValue *pe);
  32.  
  33. /* We do not use the C expression parser to handle symbols. Maybe the
  34.    C expression parser could be tweaked to do so. */
  35.  
  36. static void asm_expr_unary(TCCState *s1, ExprValue *pe)
  37. {
  38.     Sym *sym;
  39.     int op, n, label;
  40.     const char *p;
  41.  
  42.     switch(tok) {
  43.     case TOK_PPNUM:
  44.         p = tokc.cstr->data;
  45.         n = strtoul(p, (char **)&p, 0);
  46.         if (*p == 'b' || *p == 'f') {
  47.             /* backward or forward label */
  48.             label = asm_get_local_label_name(s1, n);
  49.             sym = label_find(label);
  50.             if (*p == 'b') {
  51.                 /* backward : find the last corresponding defined label */
  52.                 if (sym && sym->r == 0)
  53.                     sym = sym->prev_tok;
  54.                 if (!sym)
  55.                     error("local label '%d' not found backward", n);
  56.             } else {
  57.                 /* forward */
  58.                 if (!sym || sym->r) {
  59.                     /* if the last label is defined, then define a new one */
  60.                     sym = label_push(&s1->asm_labels, label, 0);
  61.                     sym->type.t = VT_STATIC | VT_VOID;
  62.                 }
  63.             }
  64.             pe->v = 0;
  65.             pe->sym = sym;
  66.         } else if (*p == '\0') {
  67.             pe->v = n;
  68.             pe->sym = NULL;
  69.         } else {
  70.             error("invalid number syntax");
  71.         }
  72.         next();
  73.         break;
  74.     case '+':
  75.         next();
  76.         asm_expr_unary(s1, pe);
  77.         break;
  78.     case '-':
  79.     case '~':
  80.         op = tok;
  81.         next();
  82.         asm_expr_unary(s1, pe);
  83.         if (pe->sym)
  84.             error("invalid operation with label");
  85.         if (op == '-')
  86.             pe->v = -pe->v;
  87.         else
  88.             pe->v = ~pe->v;
  89.         break;
  90.     case TOK_CCHAR:
  91.     case TOK_LCHAR:
  92.         pe->v = tokc.i;
  93.         pe->sym = NULL;
  94.         next();
  95.         break;
  96.     case '(':
  97.         next();
  98.         asm_expr(s1, pe);
  99.         skip(')');
  100.         break;
  101.     default:
  102.         if (tok >= TOK_IDENT) {
  103.             /* label case : if the label was not found, add one */
  104.             sym = label_find(tok);
  105.             if (!sym) {
  106.                 sym = label_push(&s1->asm_labels, tok, 0);
  107.                 /* NOTE: by default, the symbol is global */
  108.                 sym->type.t = VT_VOID;
  109.             }
  110.             if (sym->r == SHN_ABS) {
  111.                 /* if absolute symbol, no need to put a symbol value */
  112.                 pe->v = (long)sym->next;
  113.                 pe->sym = NULL;
  114.             } else {
  115.                 pe->v = 0;
  116.                 pe->sym = sym;
  117.             }
  118.             next();
  119.         } else {
  120.             error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
  121.         }
  122.         break;
  123.     }
  124. }
  125.    
  126. static void asm_expr_prod(TCCState *s1, ExprValue *pe)
  127. {
  128.     int op;
  129.     ExprValue e2;
  130.  
  131.     asm_expr_unary(s1, pe);
  132.     for(;;) {
  133.         op = tok;
  134.         if (op != '*' && op != '/' && op != '%' &&
  135.             op != TOK_SHL && op != TOK_SAR)
  136.             break;
  137.         next();
  138.         asm_expr_unary(s1, &e2);
  139.         if (pe->sym || e2.sym)
  140.             error("invalid operation with label");
  141.         switch(op) {
  142.         case '*':
  143.             pe->v *= e2.v;
  144.             break;
  145.         case '/':  
  146.             if (e2.v == 0) {
  147.             div_error:
  148.                 error("division by zero");
  149.             }
  150.             pe->v /= e2.v;
  151.             break;
  152.         case '%':  
  153.             if (e2.v == 0)
  154.                 goto div_error;
  155.             pe->v %= e2.v;
  156.             break;
  157.         case TOK_SHL:
  158.             pe->v <<= e2.v;
  159.             break;
  160.         default:
  161.         case TOK_SAR:
  162.             pe->v >>= e2.v;
  163.             break;
  164.         }
  165.     }
  166. }
  167.  
  168. static void asm_expr_logic(TCCState *s1, ExprValue *pe)
  169. {
  170.     int op;
  171.     ExprValue e2;
  172.  
  173.     asm_expr_prod(s1, pe);
  174.     for(;;) {
  175.         op = tok;
  176.         if (op != '&' && op != '|' && op != '^')
  177.             break;
  178.         next();
  179.         asm_expr_prod(s1, &e2);
  180.         if (pe->sym || e2.sym)
  181.             error("invalid operation with label");
  182.         switch(op) {
  183.         case '&':
  184.             pe->v &= e2.v;
  185.             break;
  186.         case '|':  
  187.             pe->v |= e2.v;
  188.             break;
  189.         default:
  190.         case '^':
  191.             pe->v ^= e2.v;
  192.             break;
  193.         }
  194.     }
  195. }
  196.  
  197. static inline void asm_expr_sum(TCCState *s1, ExprValue *pe)
  198. {
  199.     int op;
  200.     ExprValue e2;
  201.  
  202.     asm_expr_logic(s1, pe);
  203.     for(;;) {
  204.         op = tok;
  205.         if (op != '+' && op != '-')
  206.             break;
  207.         next();
  208.         asm_expr_logic(s1, &e2);
  209.         if (op == '+') {
  210.             if (pe->sym != NULL && e2.sym != NULL)
  211.                 goto cannot_relocate;
  212.             pe->v += e2.v;
  213.             if (pe->sym == NULL && e2.sym != NULL)
  214.                 pe->sym = e2.sym;
  215.         } else {
  216.             pe->v -= e2.v;
  217.             /* NOTE: we are less powerful than gas in that case
  218.                because we store only one symbol in the expression */
  219.             if (!pe->sym && !e2.sym) {
  220.                 /* OK */
  221.             } else if (pe->sym && !e2.sym) {
  222.                 /* OK */
  223.             } else if (pe->sym && e2.sym) {
  224.                 if (pe->sym == e2.sym) {
  225.                     /* OK */
  226.                 } else if (pe->sym->r == e2.sym->r && pe->sym->r != 0) {
  227.                     /* we also accept defined symbols in the same section */
  228.                     pe->v += (long)pe->sym->next - (long)e2.sym->next;
  229.                 } else {
  230.                     goto cannot_relocate;
  231.                 }
  232.                 pe->sym = NULL; /* same symbols can be substracted to NULL */
  233.             } else {
  234.             cannot_relocate:
  235.                 error("invalid operation with label");
  236.             }
  237.         }
  238.     }
  239. }
  240.  
  241. static void asm_expr(TCCState *s1, ExprValue *pe)
  242. {
  243.     asm_expr_sum(s1, pe);
  244. }
  245.  
  246. static int asm_int_expr(TCCState *s1)
  247. {
  248.     ExprValue e;
  249.     asm_expr(s1, &e);
  250.     if (e.sym)
  251.         expect("constant");
  252.     return e.v;
  253. }
  254.  
  255. /* NOTE: the same name space as C labels is used to avoid using too
  256.    much memory when storing labels in TokenStrings */
  257. static void asm_new_label1(TCCState *s1, int label, int is_local,
  258.                            int sh_num, int value)
  259. {
  260.     Sym *sym;
  261.  
  262.     sym = label_find(label);
  263.     if (sym) {
  264.         if (sym->r) {
  265.             /* the label is already defined */
  266.             if (!is_local) {
  267.                 error("assembler label '%s' already defined",
  268.                       get_tok_str(label, NULL));
  269.             } else {
  270.                 /* redefinition of local labels is possible */
  271.                 goto new_label;
  272.             }
  273.         }
  274.     } else {
  275.     new_label:
  276.         sym = label_push(&s1->asm_labels, label, 0);
  277.         sym->type.t = VT_STATIC | VT_VOID;
  278.     }
  279.     sym->r = sh_num;
  280.     sym->next = (void *)value;
  281. }
  282.  
  283. static void asm_new_label(TCCState *s1, int label, int is_local)
  284. {
  285.     asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
  286. }
  287.  
  288. static void asm_free_labels(TCCState *st)
  289. {
  290.     Sym *s, *s1;
  291.     Section *sec;
  292.    
  293.     for(s = st->asm_labels; s != NULL; s = s1) {
  294.         s1 = s->prev;
  295.         /* define symbol value in object file */
  296.         if (s->r) {
  297.             if (s->r == SHN_ABS)
  298.                 sec = SECTION_ABS;
  299.             else
  300.                 sec = st->sections[s->r];
  301.             put_extern_sym2(s, sec, (long)s->next, 0, 0);
  302.         }
  303.         /* remove label */
  304.         table_ident[s->v - TOK_IDENT]->sym_label = NULL;
  305.         sym_free(s);
  306.     }
  307.     st->asm_labels = NULL;
  308. }
  309.  
  310. static void use_section1(TCCState *s1, Section *sec)
  311. {
  312.     cur_text_section->data_offset = ind;
  313.     cur_text_section = sec;
  314.     ind = cur_text_section->data_offset;
  315. }
  316.  
  317. static void use_section(TCCState *s1, const char *name)
  318. {
  319.     Section *sec;
  320.     sec = find_section(s1, name);
  321.     use_section1(s1, sec);
  322. }
  323.  
  324. static void asm_parse_directive(TCCState *s1)
  325. {
  326.     int n, offset, v, size, tok1;
  327.     Section *sec;
  328.     uint8_t *ptr;
  329.  
  330.     /* assembler directive */
  331.     next();
  332.     sec = cur_text_section;
  333.     switch(tok) {
  334.     case TOK_ASM_align:
  335.     case TOK_ASM_skip:
  336.     case TOK_ASM_space:
  337.         tok1 = tok;
  338.         next();
  339.         n = asm_int_expr(s1);
  340.         if (tok1 == TOK_ASM_align) {
  341.             if (n < 0 || (n & (n-1)) != 0)
  342.                 error("alignment must be a positive power of two");
  343.             offset = (ind + n - 1) & -n;
  344.             size = offset - ind;
  345.             /* the section must have a compatible alignment */
  346.             if (sec->sh_addralign < n)
  347.                 sec->sh_addralign = n;
  348.         } else {
  349.             size = n;
  350.         }
  351.         v = 0;
  352.         if (tok == ',') {
  353.             next();
  354.             v = asm_int_expr(s1);
  355.         }
  356.     zero_pad:
  357.         if (sec->sh_type != SHT_NOBITS) {
  358.             sec->data_offset = ind;
  359.             ptr = section_ptr_add(sec, size);
  360.             memset(ptr, v, size);
  361.         }
  362.         ind += size;
  363.         break;
  364.     case TOK_ASM_quad:
  365.         next();
  366.         for(;;) {
  367.             uint64_t vl;
  368.             const char *p;
  369.  
  370.             p = tokc.cstr->data;
  371.             if (tok != TOK_PPNUM) {
  372.             error_constant:
  373.                 error("64 bit constant");
  374.             }
  375.             vl = strtoll(p, (char **)&p, 0);
  376.             if (*p != '\0')
  377.                 goto error_constant;
  378.             next();
  379.             if (sec->sh_type != SHT_NOBITS) {
  380.                 /* XXX: endianness */
  381.                 gen_le32(vl);
  382.                 gen_le32(vl >> 32);
  383.             } else {
  384.                 ind += 8;
  385.             }
  386.             if (tok != ',')
  387.                 break;
  388.             next();
  389.         }
  390.         break;
  391.     case TOK_ASM_byte:
  392.         size = 1;
  393.         goto asm_data;
  394.     case TOK_ASM_word:
  395.     case TOK_SHORT:
  396.         size = 2;
  397.         goto asm_data;
  398.     case TOK_LONG:
  399.     case TOK_INT:
  400.         size = 4;
  401.     asm_data:
  402.         next();
  403.         for(;;) {
  404.             ExprValue e;
  405.             asm_expr(s1, &e);
  406.             if (sec->sh_type != SHT_NOBITS) {
  407.                 if (size == 4) {
  408.                     gen_expr32(&e);
  409.                 } else {
  410.                     if (e.sym)
  411.                         expect("constant");
  412.                     if (size == 1)
  413.                         g(e.v);
  414.                     else
  415.                         gen_le16(e.v);
  416.                 }
  417.             } else {
  418.                 ind += size;
  419.             }
  420.             if (tok != ',')
  421.                 break;
  422.             next();
  423.         }
  424.         break;
  425.     case TOK_ASM_fill:
  426.         {
  427.             int repeat, size, val, i, j;
  428.             uint8_t repeat_buf[8];
  429.             next();
  430.             repeat = asm_int_expr(s1);
  431.             if (repeat < 0) {
  432.                 error("repeat < 0; .fill ignored");
  433.                 break;
  434.             }
  435.             size = 1;
  436.             val = 0;
  437.             if (tok == ',') {
  438.                 next();
  439.                 size = asm_int_expr(s1);
  440.                 if (size < 0) {
  441.                     error("size < 0; .fill ignored");
  442.                     break;
  443.                 }
  444.                 if (size > 8)
  445.                     size = 8;
  446.                 if (tok == ',') {
  447.                     next();
  448.                     val = asm_int_expr(s1);
  449.                 }
  450.             }
  451.             /* XXX: endianness */
  452.             repeat_buf[0] = val;
  453.             repeat_buf[1] = val >> 8;
  454.             repeat_buf[2] = val >> 16;
  455.             repeat_buf[3] = val >> 24;
  456.             repeat_buf[4] = 0;
  457.             repeat_buf[5] = 0;
  458.             repeat_buf[6] = 0;
  459.             repeat_buf[7] = 0;
  460.             for(i = 0; i < repeat; i++) {
  461.                 for(j = 0; j < size; j++) {
  462.                     g(repeat_buf[j]);
  463.                 }
  464.             }
  465.         }
  466.         break;
  467.     case TOK_ASM_org:
  468.         {
  469.             unsigned long n;
  470.             next();
  471.             /* XXX: handle section symbols too */
  472.             n = asm_int_expr(s1);
  473.             if (n < ind)
  474.                 error("attempt to .org backwards");
  475.             v = 0;
  476.             size = n - ind;
  477.             goto zero_pad;
  478.         }
  479.         break;
  480.     case TOK_ASM_globl:
  481.     case TOK_ASM_global:
  482.         {
  483.             Sym *sym;
  484.  
  485.             next();
  486.             sym = label_find(tok);
  487.             if (!sym) {
  488.                 sym = label_push(&s1->asm_labels, tok, 0);
  489.                 sym->type.t = VT_VOID;
  490.             }
  491.             sym->type.t &= ~VT_STATIC;
  492.             next();
  493.         }
  494.         break;
  495.     case TOK_ASM_string:
  496.     case TOK_ASM_ascii:
  497.     case TOK_ASM_asciz:
  498.         {
  499.             const uint8_t *p;
  500.             int i, size, t;
  501.  
  502.             t = tok;
  503.             next();
  504.             for(;;) {
  505.                 if (tok != TOK_STR)
  506.                     expect("string constant");
  507.                 p = tokc.cstr->data;
  508.                 size = tokc.cstr->size;
  509.                 if (t == TOK_ASM_ascii && size > 0)
  510.                     size--;
  511.                 for(i = 0; i < size; i++)
  512.                     g(p[i]);
  513.                 next();
  514.                 if (tok == ',') {
  515.                     next();
  516.                 } else if (tok != TOK_STR) {
  517.                     break;
  518.                 }
  519.             }
  520.         }
  521.         break;
  522.     case TOK_ASM_text:
  523.     case TOK_ASM_data:
  524.     case TOK_ASM_bss:
  525.         {
  526.             char sname[64];
  527.             tok1 = tok;
  528.             n = 0;
  529.             next();
  530.             if (tok != ';' && tok != TOK_LINEFEED) {
  531.                 n = asm_int_expr(s1);
  532.                 next();
  533.             }
  534.             sprintf(sname, (n?".%s%d":".%s"), get_tok_str(tok1, NULL), n);
  535.             use_section(s1, sname);
  536.         }
  537.         break;
  538.     case TOK_SECTION1:
  539.         {
  540.             char sname[256];
  541.  
  542.             /* XXX: support more options */
  543.             next();
  544.             sname[0] = '\0';
  545.             while (tok != ';' && tok != TOK_LINEFEED && tok != ',') {
  546.                 if (tok == TOK_STR)
  547.                     pstrcat(sname, sizeof(sname), tokc.cstr->data);
  548.                 else
  549.                     pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
  550.                 next();
  551.             }
  552.             if (tok == ',') {
  553.                 /* skip section options */
  554.                 next();
  555.                 if (tok != TOK_STR)
  556.                     expect("string constant");
  557.                 next();
  558.             }
  559.             last_text_section = cur_text_section;
  560.             use_section(s1, sname);
  561.         }
  562.         break;
  563.     case TOK_ASM_previous:
  564.         {
  565.             Section *sec;
  566.             next();
  567.             if (!last_text_section)
  568.                 error("no previous section referenced");
  569.             sec = cur_text_section;
  570.             use_section1(s1, last_text_section);
  571.             last_text_section = sec;
  572.         }
  573.         break;
  574.     default:
  575.         error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
  576.         break;
  577.     }
  578. }
  579.  
  580.  
  581. /* assemble a file */
  582. static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
  583. {
  584.     int opcode;
  585.  
  586. #if 0
  587.     /* print stats about opcodes */
  588.     {
  589.         const ASMInstr *pa;
  590.         int freq[4];
  591.         int op_vals[500];
  592.         int nb_op_vals, i, j;
  593.  
  594.         nb_op_vals = 0;
  595.         memset(freq, 0, sizeof(freq));
  596.         for(pa = asm_instrs; pa->sym != 0; pa++) {
  597.             freq[pa->nb_ops]++;
  598.             for(i=0;i<pa->nb_ops;i++) {
  599.                 for(j=0;j<nb_op_vals;j++) {
  600.                     if (pa->op_type[i] == op_vals[j])
  601.                         goto found;
  602.                 }
  603.                 op_vals[nb_op_vals++] = pa->op_type[i];
  604.             found: ;
  605.             }
  606.         }
  607.         for(i=0;i<nb_op_vals;i++) {
  608.             int v = op_vals[i];
  609.             if ((v & (v - 1)) != 0)
  610.                 printf("%3d: %08x\n", i, v);
  611.         }
  612.         printf("size=%d nb=%d f0=%d f1=%d f2=%d f3=%d\n",
  613.                sizeof(asm_instrs), sizeof(asm_instrs) / sizeof(ASMInstr),
  614.                freq[0], freq[1], freq[2], freq[3]);
  615.     }
  616. #endif
  617.  
  618.     /* XXX: undefine C labels */
  619.  
  620.     ch = file->buf_ptr[0];
  621.     tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
  622.     parse_flags = PARSE_FLAG_ASM_COMMENTS;
  623.     if (do_preprocess)
  624.         parse_flags |= PARSE_FLAG_PREPROCESS;
  625.     next();
  626.     for(;;) {
  627.         if (tok == TOK_EOF)
  628.             break;
  629.         parse_flags |= PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
  630.     redo:
  631.         if (tok == '#') {
  632.             /* horrible gas comment */
  633.             while (tok != TOK_LINEFEED)
  634.                 next();
  635.         } else if (tok == '.') {
  636.             asm_parse_directive(s1);
  637.         } else if (tok == TOK_PPNUM) {
  638.             const char *p;
  639.             int n;
  640.             p = tokc.cstr->data;
  641.             n = strtoul(p, (char **)&p, 10);
  642.             if (*p != '\0')
  643.                 expect("':'");
  644.             /* new local label */
  645.             asm_new_label(s1, asm_get_local_label_name(s1, n), 1);
  646.             next();
  647.             skip(':');
  648.             goto redo;
  649.         } else if (tok >= TOK_IDENT) {
  650.             /* instruction or label */
  651.             opcode = tok;
  652.             next();
  653.             if (tok == ':') {
  654.                 /* new label */
  655.                 asm_new_label(s1, opcode, 0);
  656.                 next();
  657.                 goto redo;
  658.             } else if (tok == '=') {
  659.                 int n;
  660.                 next();
  661.                 n = asm_int_expr(s1);
  662.                 asm_new_label1(s1, opcode, 0, SHN_ABS, n);
  663.                 goto redo;
  664.             } else {
  665.                 asm_opcode(s1, opcode);
  666.             }
  667.         }
  668.         /* end of line */
  669.         if (tok != ';' && tok != TOK_LINEFEED){
  670.             expect("end of line");
  671.         }
  672.         parse_flags &= ~PARSE_FLAG_LINEFEED; /* XXX: suppress that hack */
  673.         next();
  674.     }
  675.  
  676.     asm_free_labels(s1);
  677.  
  678.     return 0;
  679. }
  680.  
  681. /* Assemble the current file */
  682. static int tcc_assemble(TCCState *s1, int do_preprocess)
  683. {
  684.     Sym *define_start;
  685.     int ret;
  686.  
  687.     preprocess_init(s1);
  688.  
  689.     /* default section is text */
  690.     cur_text_section = text_section;
  691.     ind = cur_text_section->data_offset;
  692.  
  693.     define_start = define_stack;
  694.  
  695.     ret = tcc_assemble_internal(s1, do_preprocess);
  696.  
  697.     cur_text_section->data_offset = ind;
  698.  
  699.     free_defines(define_start);
  700.  
  701.     return ret;
  702. }
  703.  
  704. /********************************************************************/
  705. /* GCC inline asm support */
  706.  
  707. /* assemble the string 'str' in the current C compilation unit without
  708.    C preprocessing. NOTE: str is modified by modifying the '\0' at the
  709.    end */
  710. static void tcc_assemble_inline(TCCState *s1, char *str, int len)
  711. {
  712.     BufferedFile *bf, *saved_file;
  713.     int saved_parse_flags, *saved_macro_ptr;
  714.  
  715.     bf = tcc_malloc(sizeof(BufferedFile));
  716.     memset(bf, 0, sizeof(BufferedFile));
  717.     bf->fd = -1;
  718.     bf->buf_ptr = str;
  719.     bf->buf_end = str + len;
  720.     str[len] = CH_EOB;
  721.     /* same name as current file so that errors are correctly
  722.        reported */
  723.     pstrcpy(bf->filename, sizeof(bf->filename), file->filename);
  724.     bf->line_num = file->line_num;
  725.     saved_file = file;
  726.     file = bf;
  727.     saved_parse_flags = parse_flags;
  728.     saved_macro_ptr = macro_ptr;
  729.     macro_ptr = NULL;
  730.    
  731.     tcc_assemble_internal(s1, 0);
  732.  
  733.     parse_flags = saved_parse_flags;
  734.     macro_ptr = saved_macro_ptr;
  735.     file = saved_file;
  736.     tcc_free(bf);
  737. }
  738.  
  739. /* find a constraint by its number or id (gcc 3 extended
  740.    syntax). return -1 if not found. Return in *pp in char after the
  741.    constraint */
  742. static int find_constraint(ASMOperand *operands, int nb_operands,
  743.                            const char *name, const char **pp)
  744. {
  745.     int index;
  746.     TokenSym *ts;
  747.     const char *p;
  748.  
  749.     if (isnum(*name)) {
  750.         index = 0;
  751.         while (isnum(*name)) {
  752.             index = (index * 10) + (*name) - '0';
  753.             name++;
  754.         }
  755.         if ((unsigned)index >= nb_operands)
  756.             index = -1;
  757.     } else if (*name == '[') {
  758.         name++;
  759.         p = strchr(name, ']');
  760.         if (p) {
  761.             ts = tok_alloc(name, p - name);
  762.             for(index = 0; index < nb_operands; index++) {
  763.                 if (operands[index].id == ts->tok)
  764.                     goto found;
  765.             }
  766.             index = -1;
  767.         found:
  768.             name = p + 1;
  769.         } else {
  770.             index = -1;
  771.         }
  772.     } else {
  773.         index = -1;
  774.     }
  775.     if (pp)
  776.         *pp = name;
  777.     return index;
  778. }
  779.  
  780. static void subst_asm_operands(ASMOperand *operands, int nb_operands,
  781.                                int nb_outputs,
  782.                                CString *out_str, CString *in_str)
  783. {
  784.     int c, index, modifier;
  785.     const char *str;
  786.     ASMOperand *op;
  787.     SValue sv;
  788.  
  789.     cstr_new(out_str);
  790.     str = in_str->data;
  791.     for(;;) {
  792.         c = *str++;
  793.         if (c == '%') {
  794.             if (*str == '%') {
  795.                 str++;
  796.                 goto add_char;
  797.             }
  798.             modifier = 0;
  799.             if (*str == 'c' || *str == 'n' ||
  800.                 *str == 'b' || *str == 'w' || *str == 'h')
  801.                 modifier = *str++;
  802.             index = find_constraint(operands, nb_operands, str, &str);
  803.             if (index < 0)
  804.                 error("invalid operand reference after %%");
  805.             op = &operands[index];
  806.             sv = *op->vt;
  807.             if (op->reg >= 0) {
  808.                 sv.r = op->reg;
  809.                 if ((op->vt->r & VT_VALMASK) == VT_LLOCAL)
  810.                     sv.r |= VT_LVAL;
  811.             }
  812.             subst_asm_operand(out_str, &sv, modifier);
  813.         } else {
  814.         add_char:
  815.             cstr_ccat(out_str, c);
  816.             if (c == '\0')
  817.                 break;
  818.         }
  819.     }
  820. }
  821.  
  822.  
  823. static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
  824.                                int is_output)
  825. {
  826.     ASMOperand *op;
  827.     int nb_operands;
  828.  
  829.     if (tok != ':') {
  830.         nb_operands = *nb_operands_ptr;
  831.         for(;;) {
  832.             if (nb_operands >= MAX_ASM_OPERANDS)
  833.                 error("too many asm operands");
  834.             op = &operands[nb_operands++];
  835.             op->id = 0;
  836.             if (tok == '[') {
  837.                 next();
  838.                 if (tok < TOK_IDENT)
  839.                     expect("identifier");
  840.                 op->id = tok;
  841.                 next();
  842.                 skip(']');
  843.             }
  844.             if (tok != TOK_STR)
  845.                 expect("string constant");
  846.             op->constraint = tcc_malloc(tokc.cstr->size);
  847.             strcpy(op->constraint, tokc.cstr->data);
  848.             next();
  849.             skip('(');
  850.             gexpr();
  851.             if (is_output) {
  852.                 test_lvalue();
  853.             } else {
  854.                 /* we want to avoid LLOCAL case, except when the 'm'
  855.                    constraint is used. Note that it may come from
  856.                    register storage, so we need to convert (reg)
  857.                    case */
  858.                 if ((vtop->r & VT_LVAL) &&
  859.                     ((vtop->r & VT_VALMASK) == VT_LLOCAL ||
  860.                      (vtop->r & VT_VALMASK) < VT_CONST) &&
  861.                     !strchr(op->constraint, 'm')) {
  862.                     gv(RC_INT);
  863.                 }
  864.             }
  865.             op->vt = vtop;
  866.             skip(')');
  867.             if (tok == ',') {
  868.                 next();
  869.             } else {
  870.                 break;
  871.             }
  872.         }
  873.         *nb_operands_ptr = nb_operands;
  874.     }
  875. }
  876.  
  877. static void parse_asm_str(CString *astr)
  878. {
  879.     skip('(');
  880.     /* read the string */
  881.     if (tok != TOK_STR)
  882.         expect("string constant");
  883.     cstr_new(astr);
  884.     while (tok == TOK_STR) {
  885.         /* XXX: add \0 handling too ? */
  886.         cstr_cat(astr, tokc.cstr->data);
  887.         next();
  888.     }
  889.     cstr_ccat(astr, '\0');
  890. }
  891.  
  892. /* parse the GCC asm() instruction */
  893. static void asm_instr(void)
  894. {
  895.     CString astr, astr1;
  896.     ASMOperand operands[MAX_ASM_OPERANDS];
  897.     int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
  898.     uint8_t clobber_regs[NB_ASM_REGS];
  899.  
  900.     next();
  901.     /* since we always generate the asm() instruction, we can ignore
  902.        volatile */
  903.     if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
  904.         next();
  905.     }
  906.     parse_asm_str(&astr);
  907.     nb_operands = 0;
  908.     nb_outputs = 0;
  909.     must_subst = 0;
  910.     memset(clobber_regs, 0, sizeof(clobber_regs));
  911.     if (tok == ':') {
  912.         next();
  913.         must_subst = 1;
  914.         /* output args */
  915.         parse_asm_operands(operands, &nb_operands, 1);
  916.         nb_outputs = nb_operands;
  917.         if (tok == ':') {
  918.             next();
  919.             /* input args */
  920.             parse_asm_operands(operands, &nb_operands, 0);
  921.             if (tok == ':') {
  922.                 /* clobber list */
  923.                 /* XXX: handle registers */
  924.                 next();
  925.                 for(;;) {
  926.                     if (tok != TOK_STR)
  927.                         expect("string constant");
  928.                     asm_clobber(clobber_regs, tokc.cstr->data);
  929.                     next();
  930.                     if (tok == ',') {
  931.                         next();
  932.                     } else {
  933.                         break;
  934.                     }
  935.                 }
  936.             }
  937.         }
  938.     }
  939.     skip(')');
  940.     /* NOTE: we do not eat the ';' so that we can restore the current
  941.        token after the assembler parsing */
  942.     if (tok != ';')
  943.         expect("';'");
  944.     nb_inputs = nb_operands - nb_outputs;
  945.    
  946.     /* save all values in the memory */
  947.     save_regs(0);
  948.  
  949.     /* compute constraints */
  950.     asm_compute_constraints(operands, nb_operands, nb_outputs,
  951.                             clobber_regs, &out_reg);
  952.  
  953.     /* substitute the operands in the asm string. No substitution is
  954.        done if no operands (GCC behaviour) */
  955. #ifdef ASM_DEBUG
  956.     printf("asm: \"%s\"\n", (char *)astr.data);
  957. #endif
  958.     if (must_subst) {
  959.         subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
  960.         cstr_free(&astr);
  961.     } else {
  962.         astr1 = astr;
  963.     }
  964. #ifdef ASM_DEBUG
  965.     printf("subst_asm: \"%s\"\n", (char *)astr1.data);
  966. #endif
  967.  
  968.     /* generate loads */
  969.     asm_gen_code(operands, nb_operands, nb_outputs, 0,
  970.                  clobber_regs, out_reg);    
  971.  
  972.     /* assemble the string with tcc internal assembler */
  973.     tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
  974.  
  975.     /* restore the current C token */
  976.     next();
  977.  
  978.     /* store the output values if needed */
  979.     asm_gen_code(operands, nb_operands, nb_outputs, 1,
  980.                  clobber_regs, out_reg);
  981.    
  982.     /* free everything */
  983.     for(i=0;i<nb_operands;i++) {
  984.         ASMOperand *op;
  985.         op = &operands[i];
  986.         tcc_free(op->constraint);
  987.         vpop();
  988.     }
  989.     cstr_free(&astr1);
  990. }
  991.  
  992. static void asm_global_instr(void)
  993. {
  994.     CString astr;
  995.  
  996.     next();
  997.     parse_asm_str(&astr);
  998.     skip(')');
  999.     /* NOTE: we do not eat the ';' so that we can restore the current
  1000.        token after the assembler parsing */
  1001.     if (tok != ';')
  1002.         expect("';'");
  1003.    
  1004. #ifdef ASM_DEBUG
  1005.     printf("asm_global: \"%s\"\n", (char *)astr.data);
  1006. #endif
  1007.     cur_text_section = text_section;
  1008.     ind = cur_text_section->data_offset;
  1009.  
  1010.     /* assemble the string with tcc internal assembler */
  1011.     tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
  1012.    
  1013.     cur_text_section->data_offset = ind;
  1014.  
  1015.     /* restore the current C token */
  1016.     next();
  1017.  
  1018.     cstr_free(&astr);
  1019. }
  1020.