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
					
				
					 4 changed files with 58 additions and 6 deletions
				
			
		
							
								
								
									
										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 */
 | 
			
		||||
        if (vtop->r & VT_SYM) {
 | 
			
		||||
        /* 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->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…
	
	Add table
		
		Reference in a new issue