diff --git a/i386-asm.c b/i386-asm.c index 16b2e915..a62a4cfa 100644 --- a/i386-asm.c +++ b/i386-asm.c @@ -1185,6 +1185,28 @@ static const char *skip_constraint_modifiers(const char *p) return p; } +/* If T (a token) is of the form "%reg" returns the register + number and type, otherwise return -1. */ +ST_FUNC int asm_parse_regvar (int t) +{ + const char *s; + Operand op; + if (t < TOK_IDENT) + return -1; + s = table_ident[t - TOK_IDENT]->str; + if (s[0] != '%') + return -1; + t = tok_alloc(s+1, strlen(s)-1)->tok; + unget_tok(t); + unget_tok('%'); + parse_operand(tcc_state, &op); + /* Accept only integer regs for now. */ + if (op.type & OP_REG) + return op.reg; + else + return -1; +} + #define REG_OUT_MASK 0x01 #define REG_IN_MASK 0x02 @@ -1227,6 +1249,11 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, tcc_error("cannot reference twice the same operand"); operands[k].input_index = i; op->priority = 5; + } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL + && op->vt->sym + && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) { + op->priority = 1; + op->reg = reg; } else { op->priority = constraint_priority(str); } @@ -1274,6 +1301,12 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, } else { reg_mask = REG_IN_MASK; } + if (op->reg >= 0) { + if (is_reg_allocated(op->reg)) + tcc_error("asm regvar requests register that's taken already"); + reg = op->reg; + goto reg_found; + } try_next: c = *str++; switch(c) { diff --git a/tcc.h b/tcc.h index 2434b944..fb104fa5 100644 --- a/tcc.h +++ b/tcc.h @@ -372,7 +372,8 @@ typedef struct SValue { unsigned short r2; /* second register, used for 'long long' type. If not used, set to VT_CONST */ CValue c; /* constant, if VT_CONST */ - struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */ + struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST), or if + result of unary() for an identifier. */ } SValue; struct Attribute { @@ -407,7 +408,7 @@ typedef struct Sym { int scope; /* scope level for locals */ }; union { - long r; /* associated register */ + long r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */ struct Attribute a; }; union { @@ -416,7 +417,7 @@ typedef struct Sym { }; CType type; /* associated type */ union { - struct Sym *next; /* next related symbol */ + struct Sym *next; /* next related symbol (for fields and anoms) */ long jnext; /* next jump label */ }; struct Sym *prev; /* prev symbol in stack */ @@ -1542,6 +1543,7 @@ ST_FUNC void gen_expr32(ExprValue *pe); ST_FUNC void gen_expr64(ExprValue *pe); #endif ST_FUNC void asm_opcode(TCCState *s1, int opcode); +ST_FUNC int asm_parse_regvar(int t); ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg); diff --git a/tccgen.c b/tccgen.c index f7b7c2d9..5c2acf8b 100644 --- a/tccgen.c +++ b/tccgen.c @@ -386,7 +386,7 @@ ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c) Sym *s; s = sym_malloc(); - s->asm_label = 0; + s->scope = 0; s->v = v; s->type.t = t; s->type.ref = NULL; @@ -577,6 +577,7 @@ static void vsetc(CType *type, int r, CValue *vc) vtop->r = r; vtop->r2 = VT_CONST; vtop->c = *vc; + vtop->sym = NULL; } /* push constant of type "type" with useless value */ @@ -4599,11 +4600,17 @@ ST_FUNC void unary(void) r = VT_SYM | VT_CONST; } else { r = s->r; + /* A symbol that has a register is a local register variable, + which starts out as VT_LOCAL value. */ + if ((r & VT_VALMASK) < VT_CONST) + r = (r & ~VT_VALMASK) | VT_LOCAL; } vset(&s->type, r, s->c); - /* if forward reference, we must point to s */ + /* Point to s as backpointer (even without r&VT_SYM). + Will be used by at least the x86 inline asm parser for + regvars. */ + vtop->sym = s; if (vtop->r & VT_SYM) { - vtop->sym = s; vtop->c.i = 0; } break; @@ -6462,6 +6469,13 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, #endif if (v) { /* local variable */ +#ifdef CONFIG_TCC_ASM + if (ad->asm_label) { + int reg = asm_parse_regvar(ad->asm_label); + if (reg >= 0) + r = (r & ~VT_VALMASK) | reg; + } +#endif sym_push(v, type, r, addr); } else { /* push local reference */ diff --git a/tests/tcctest.c b/tests/tcctest.c index 5190523d..d4a6a5a5 100644 --- a/tests/tcctest.c +++ b/tests/tcctest.c @@ -2939,6 +2939,7 @@ void asm_test(void) #ifdef BOOL_ISOC99 _Bool somebool; #endif + register int regvar asm("%esi"); printf("inline asm:\n"); @@ -3001,6 +3002,8 @@ void asm_test(void) val = 44; fancy_copy2 (&val, &val2); printf ("fancycpy2(%d)=%d\n", val, val2); + asm volatile ("mov $0x4243, %%esi" : "=r" (regvar)); + printf ("regvar=%x\n", regvar); return; label1: goto label2;