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 | return p; | 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 | #define REG_OUT_MASK 0x01 | 1210 | #define REG_OUT_MASK 0x01 |
| 1189 | #define REG_IN_MASK 0x02 | 1211 | #define REG_IN_MASK 0x02 |
| 1190 | 1212 | ||
| @@ -1227,6 +1249,11 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, · 36 unmodified lines | |||
| 1227 | tcc_error("cannot reference twice the same operand"); | 1249 | tcc_error("cannot reference twice the same operand"); |
| 1228 | operands[k].input_index = i; | 1250 | operands[k].input_index = i; |
| 1229 | op->priority = 5; | 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 | } else { | 1257 | } else { |
| 1231 | op->priority = constraint_priority(str); | 1258 | op->priority = constraint_priority(str); |
| 1232 | } | 1259 | } |
| @@ -1274,6 +1301,12 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands, · 41 unmodified lines | |||
| 1274 | } else { | 1301 | } else { |
| 1275 | reg_mask = REG_IN_MASK; | 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 | try_next: | 1310 | try_next: |
| 1278 | c = *str++; | 1311 | c = *str++; |
| 1279 | switch(c) { | 1312 | switch(c) { |
| @@ -372,7 +372,8 @@ typedef struct SValue { · 371 unmodified lines | |||
| 372 | unsigned short r2; /* second register, used for 'long long' | 372 | unsigned short r2; /* second register, used for 'long long' |
| 373 | type. If not used, set to VT_CONST */ | 373 | type. If not used, set to VT_CONST */ |
| 374 | CValue c; /* constant, if VT_CONST */ | 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 | } SValue; | 377 | } SValue; |
| 377 | 378 | ||
| 378 | struct Attribute { | 379 | struct Attribute { |
| @@ -407,7 +408,7 @@ typedef struct Sym { · 28 unmodified lines | |||
| 407 | int scope; /* scope level for locals */ | 408 | int scope; /* scope level for locals */ |
| 408 | }; | 409 | }; |
| 409 | union { | 410 | union { |
| 410 | long r; /* associated register */ | 411 | long r; /* associated register or VT_CONST/VT_LOCAL and LVAL type */ |
| 411 | struct Attribute a; | 412 | struct Attribute a; |
| 412 | }; | 413 | }; |
| 413 | union { | 414 | union { |
| @@ -416,7 +417,7 @@ typedef struct Sym { · 2 unmodified lines | |||
| 416 | }; | 417 | }; |
| 417 | CType type; /* associated type */ | 418 | CType type; /* associated type */ |
| 418 | union { | 419 | union { |
| 419 | struct Sym *next; /* next related symbol */ | 420 | struct Sym *next; /* next related symbol (for fields and anoms) */ |
| 420 | long jnext; /* next jump label */ | 421 | long jnext; /* next jump label */ |
| 421 | }; | 422 | }; |
| 422 | struct Sym *prev; /* prev symbol in stack */ | 423 | struct Sym *prev; /* prev symbol in stack */ |
| @@ -1542,6 +1543,7 @@ ST_FUNC void gen_expr32(ExprValue *pe); · 1119 unmodified lines | |||
| 1542 | ST_FUNC void gen_expr64(ExprValue *pe); | 1543 | ST_FUNC void gen_expr64(ExprValue *pe); |
| 1543 | #endif | 1544 | #endif |
| 1544 | ST_FUNC void asm_opcode(TCCState *s1, int opcode); | 1545 | ST_FUNC void asm_opcode(TCCState *s1, int opcode); |
| 1546 | ST_FUNC int asm_parse_regvar(int t); | ||
| 1545 | ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); | 1547 | ST_FUNC void asm_compute_constraints(ASMOperand *operands, int nb_operands, int nb_outputs, const uint8_t *clobber_regs, int *pout_reg); |
| 1546 | ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); | 1548 | ST_FUNC void subst_asm_operand(CString *add_str, SValue *sv, int modifier); |
| 1547 | ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands, int nb_outputs, int is_output, uint8_t *clobber_regs, int out_reg); | 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 | Sym *s; | 386 | Sym *s; |
| 387 | 387 | ||
| 388 | s = sym_malloc(); | 388 | s = sym_malloc(); |
| 389 | s->asm_label = 0; | 389 | s->scope = 0; |
| 390 | s->v = v; | 390 | s->v = v; |
| 391 | s->type.t = t; | 391 | s->type.t = t; |
| 392 | s->type.ref = NULL; | 392 | s->type.ref = NULL; |
| @@ -577,6 +577,7 @@ static void vsetc(CType *type, int r, CValue *vc) · 184 unmodified lines | |||
| 577 | vtop->r = r; | 577 | vtop->r = r; |
| 578 | vtop->r2 = VT_CONST; | 578 | vtop->r2 = VT_CONST; |
| 579 | vtop->c = *vc; | 579 | vtop->c = *vc; |
| 580 | vtop->sym = NULL; | ||
| 580 | } | 581 | } |
| 581 | 582 | ||
| 582 | /* push constant of type "type" with useless value */ | 583 | /* push constant of type "type" with useless value */ |
| @@ -4599,11 +4600,17 @@ ST_FUNC void unary(void) · 4016 unmodified lines | |||
| 4599 | r = VT_SYM | VT_CONST; | 4600 | r = VT_SYM | VT_CONST; |
| 4600 | } else { | 4601 | } else { |
| 4601 | r = s->r; | 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 | vset(&s->type, r, s->c); | 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 | if (vtop->r & VT_SYM) { | 4613 | if (vtop->r & VT_SYM) { |
| 4606 | vtop->sym = s; | ||
| 4607 | vtop->c.i = 0; | 4614 | vtop->c.i = 0; |
| 4608 | } | 4615 | } |
| 4609 | break; | 4616 | break; |
| @@ -6462,6 +6469,13 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r, · 1852 unmodified lines | |||
| 6462 | #endif | 6469 | #endif |
| 6463 | if (v) { | 6470 | if (v) { |
| 6464 | /* local variable */ | 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 | sym_push(v, type, r, addr); | 6479 | sym_push(v, type, r, addr); |
| 6466 | } else { | 6480 | } else { |
| 6467 | /* push local reference */ | 6481 | /* push local reference */ |
| @@ -2939,6 +2939,7 @@ void asm_test(void) · 2938 unmodified lines | |||
| 2939 | #ifdef BOOL_ISOC99 | 2939 | #ifdef BOOL_ISOC99 |
| 2940 | _Bool somebool; | 2940 | _Bool somebool; |
| 2941 | #endif | 2941 | #endif |
| 2942 | register int regvar asm("%esi"); | ||
| 2942 | 2943 | ||
| 2943 | printf("inline asm:\n"); | 2944 | printf("inline asm:\n"); |
| 2944 | 2945 | ||
| @@ -3001,6 +3002,8 @@ void asm_test(void) · 56 unmodified lines | |||
| 3001 | val = 44; | 3002 | val = 44; |
| 3002 | fancy_copy2 (&val, &val2); | 3003 | fancy_copy2 (&val, &val2); |
| 3003 | printf ("fancycpy2(%d)=%d\n", val, val2); | 3004 | printf ("fancycpy2(%d)=%d\n", val, val2); |
| 3005 | asm volatile ("mov $0x4243, %%esi" : "=r" (regvar)); | ||
| 3006 | printf ("regvar=%x\n", regvar); | ||
| 3004 | return; | 3007 | return; |
| 3005 | label1: | 3008 | label1: |
| 3006 | goto label2; | 3009 | goto label2; |