mirror of
https://github.com/frida/tinycc
synced 2025-01-12 06:39:51 +03:00
Support local register variables
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.
This commit is contained in:
parent
3bc9c325c5
commit
f081acbfba
33
i386-asm.c
33
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) {
|
||||
|
8
tcc.h
8
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);
|
||||
|
20
tccgen.c
20
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 */
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user