Similar to GCC a local asm register variable enforces the use of a specified register in asm operands (and doesn't otherwise matter). Works only if the variable is directly mentioned as operand. For that we now generally store a backpointer from an SValue to a Sym when the SValue was the result of unary() parsing a symbol identifier.
Unified · Split
| @@ -1185,6 +1185,28 @@ static const char *skip_constraint_modifiers(const char *p) · 1184 unmodified lines | ||
| 1185 | 1185 | return p; |
| 1186 | 1186 | } |
| 1187 | 1187 | |
| 1188 | +/* If T (a token) is of the form "%reg" returns the register | |
| 1189 | + number and type, otherwise return -1. */ | |
| 1190 | +ST_FUNC int asm_parse_regvar (int t) | |
| 1191 | +{ | |
| 1192 | + const char *s; | |
| 1193 | + Operand op; | |
| 1194 | + if (t < TOK_IDENT) | |
| 1195 | + return -1; | |
| 1196 | + s = table_ident[t - TOK_IDENT]->str; | |
| 1197 | + if (s[0] != '%') | |
| 1198 | + return -1; | |
| 1199 | + t = tok_alloc(s+1, strlen(s)-1)->tok; | |
| 1200 | + unget_tok(t); | |
| 1201 | + unget_tok('%'); | |
| 1202 | + parse_operand(tcc_state, &op); | |
| 1203 | + /* Accept only integer regs for now. */ | |
| 1204 | + if (op.type & OP_REG) | |
| 1205 | + return op.reg; | |
| 1206 | + else | |
| 1207 | + return -1; | |
| 1208 | +} | |
| 1209 | + | |
| 1188 | 1210 | #define REG_OUT_MASK 0x01 |
| 1189 | 1211 | #define REG_IN_MASK 0x02 |
| 1190 | 1212 | |
| @@ -1227,6 +1249,11 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, · 36 unmodified lines | ||
| 1227 | 1249 | tcc_error("cannot reference twice the same operand"); |
| 1228 | 1250 | operands[k].input_index = i; |
| 1229 | 1251 | op->priority = 5; |
| 1252 | + } else if ((op->vt->r & VT_VALMASK) == VT_LOCAL | |
| 1253 | + && op->vt->sym | |
| 1254 | + && (reg = op->vt->sym->r & VT_VALMASK) < VT_CONST) { | |
| 1255 | + op->priority = 1; | |
| 1256 | + op->reg = reg; | |
| 1230 | 1257 | } else { |
| 1231 | 1258 | op->priority = constraint_priority(str); |
| 1232 | 1259 | } |
| @@ -1274,6 +1301,12 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, · 41 unmodified lines | ||
| 1274 | 1301 | } else { |
| 1275 | 1302 | reg_mask = REG_IN_MASK; |
| 1276 | 1303 | } |
| 1304 | + if (op->reg >= 0) { | |
| 1305 | + if (is_reg_allocated(op->reg)) | |
| 1306 | + tcc_error("asm regvar requests register that's taken already"); | |
| 1307 | + reg = op->reg; | |
| 1308 | + goto reg_found; | |
| 1309 | + } | |
| 1277 | 1310 | try_next: |
| 1278 | 1311 | c = *str++; |
| 1279 | 1312 | switch(c) { |
| @@ -372,7 +372,8 @@ typedef struct SValue { · 371 unmodified lines | ||
| 372 | 372 | unsigned short r2; /* second register, used for 'long long' |
| 373 | 373 | type. If not used, set to VT_CONST */ |
| 374 | 374 | CValue c; /* constant, if VT_CONST */ |
| 375 | − struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST) */ | |
| 375 | + struct Sym *sym; /* symbol, if (VT_SYM | VT_CONST), or if | |
| 376 | + result of unary() for an identifier. */ | |
| 376 | 377 | } SValue; |
| 377 | 378 | |
| 378 | 379 | struct Attribute { |
| @@ -407,7 +408,7 @@ typedef struct Sym { · 28 unmodified lines | ||
| 407 | 408 | int scope; /* scope level for locals */ |
| 408 | 409 | }; |
| 409 | 410 | union { |
| 410 | − long r; /* associated register */ | |
| 411 | + long r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */ | |
| 411 | 412 | struct Attribute a; |
| 412 | 413 | }; |
| 413 | 414 | union { |
| @@ -416,7 +417,7 @@ typedef struct Sym { · 2 unmodified lines | ||
| 416 | 417 | }; |
| 417 | 418 | CType type; /* associated type */ |
| 418 | 419 | union { |
| 419 | − struct Sym *next; /* next related symbol */ | |
| 420 | + struct Sym *next; /* next related symbol (for fields and anoms) */ | |
| 420 | 421 | long jnext; /* next jump label */ |
| 421 | 422 | }; |
| 422 | 423 | struct Sym *prev; /* prev symbol in stack */ |
| @@ -1542,6 +1543,7 @@ ST_FUNC void gen_expr32(ExprValue *pe); · 1119 unmodified lines | ||
| 1542 | 1543 | ST_FUNC void gen_expr64(ExprValue *pe); |
| 1543 | 1544 | #endif |
| 1544 | 1545 | ST_FUNC void asm_opcode(TCCState *s1, int opcode); |
| 1546 | +ST_FUNC int asm_parse_regvar(int t); | |
| 1545 | 1547 | ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); |
| 1546 | 1548 | ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); |
| 1547 | 1549 | ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg); |
| @@ -386,7 +386,7 @@ ST_FUNC Sym *sym_push2(Sym **ps, int v, int t, long c) · 385 unmodified lines | ||
| 386 | 386 | Sym *s; |
| 387 | 387 | |
| 388 | 388 | s = sym_malloc(); |
| 389 | − s->asm_label = 0; | |
| 389 | + s->scope = 0; | |
| 390 | 390 | s->v = v; |
| 391 | 391 | s->type.t = t; |
| 392 | 392 | s->type.ref = NULL; |
| @@ -577,6 +577,7 @@ static void vsetc(CType *type, int r, CValue *vc) · 184 unmodified lines | ||
| 577 | 577 | vtop->r = r; |
| 578 | 578 | vtop->r2 = VT_CONST; |
| 579 | 579 | vtop->c = *vc; |
| 580 | + vtop->sym = NULL; | |
| 580 | 581 | } |
| 581 | 582 | |
| 582 | 583 | /* push constant of type "type" with useless value */ |
| @@ -4599,11 +4600,17 @@ ST_FUNC void unary(void) · 4016 unmodified lines | ||
| 4599 | 4600 | r = VT_SYM | VT_CONST; |
| 4600 | 4601 | } else { |
| 4601 | 4602 | r = s->r; |
| 4603 | + /* A symbol that has a register is a local register variable, | |
| 4604 | + which starts out as VT_LOCAL value. */ | |
| 4605 | + if ((r & VT_VALMASK) < VT_CONST) | |
| 4606 | + r = (r & ~VT_VALMASK) | VT_LOCAL; | |
| 4602 | 4607 | } |
| 4603 | 4608 | vset(&s->type, r, s->c); |
| 4604 | − /* if forward reference, we must point to s */ | |
| 4609 | + /* Point to s as backpointer (even without r&VT_SYM). | |
| 4610 | + Will be used by at least the x86 inline asm parser for | |
| 4611 | + regvars. */ | |
| 4612 | + vtop->sym = s; | |
| 4605 | 4613 | if (vtop->r & VT_SYM) { |
| 4606 | − vtop->sym = s; | |
| 4607 | 4614 | vtop->c.i = 0; |
| 4608 | 4615 | } |
| 4609 | 4616 | break; |
| @@ -6462,6 +6469,13 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 1852 unmodified lines | ||
| 6462 | 6469 | #endif |
| 6463 | 6470 | if (v) { |
| 6464 | 6471 | /* local variable */ |
| 6472 | +#ifdef CONFIG_TCC_ASM | |
| 6473 | + if (ad->asm_label) { | |
| 6474 | + int reg = asm_parse_regvar(ad->asm_label); | |
| 6475 | + if (reg >= 0) | |
| 6476 | + r = (r & ~VT_VALMASK) | reg; | |
| 6477 | + } | |
| 6478 | +#endif | |
| 6465 | 6479 | sym_push(v, type, r, addr); |
| 6466 | 6480 | } else { |
| 6467 | 6481 | /* push local reference */ |
| @@ -2939,6 +2939,7 @@ void asm_test(void) · 2938 unmodified lines | ||
| 2939 | 2939 | #ifdef BOOL_ISOC99 |
| 2940 | 2940 | _Bool somebool; |
| 2941 | 2941 | #endif |
| 2942 | + register int regvar asm("%esi"); | |
| 2942 | 2943 | |
| 2943 | 2944 | printf("inline asm:\n"); |
| 2944 | 2945 | |
| @@ -3001,6 +3002,8 @@ void asm_test(void) · 56 unmodified lines | ||
| 3001 | 3002 | val = 44; |
| 3002 | 3003 | fancy_copy2 (&val, &val2); |
| 3003 | 3004 | printf ("fancycpy2(%d)=%d\n", val, val2); |
| 3005 | + asm volatile ("mov $0x4243, %%esi" : "=r" (regvar)); | |
| 3006 | + printf ("regvar=%x\n", regvar); | |
| 3004 | 3007 | return; |
| 3005 | 3008 | label1: |
| 3006 | 3009 | goto label2; |