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;
 | 
					    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_OUT_MASK 0x01
 | 
				
			||||||
#define REG_IN_MASK  0x02
 | 
					#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");
 | 
					                tcc_error("cannot reference twice the same operand");
 | 
				
			||||||
            operands[k].input_index = i;
 | 
					            operands[k].input_index = i;
 | 
				
			||||||
            op->priority = 5;
 | 
					            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 {
 | 
					        } else {
 | 
				
			||||||
            op->priority = constraint_priority(str);
 | 
					            op->priority = constraint_priority(str);
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
| 
						 | 
					@ -1274,6 +1301,12 @@ ST_FUNC void asm_compute_constraints(ASMOperand *operands,
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            reg_mask = REG_IN_MASK;
 | 
					            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:
 | 
					    try_next:
 | 
				
			||||||
        c = *str++;
 | 
					        c = *str++;
 | 
				
			||||||
        switch(c) {
 | 
					        switch(c) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										8
									
								
								tcc.h
									
										
									
									
									
								
							
							
						
						
									
										8
									
								
								tcc.h
									
										
									
									
									
								
							| 
						 | 
					@ -372,7 +372,8 @@ typedef struct SValue {
 | 
				
			||||||
    unsigned short r2;     /* second register, used for 'long long'
 | 
					    unsigned short r2;     /* second register, used for 'long long'
 | 
				
			||||||
                              type. If not used, set to VT_CONST */
 | 
					                              type. If not used, set to VT_CONST */
 | 
				
			||||||
    CValue c;              /* constant, if 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;
 | 
					} SValue;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct Attribute {
 | 
					struct Attribute {
 | 
				
			||||||
| 
						 | 
					@ -407,7 +408,7 @@ typedef struct Sym {
 | 
				
			||||||
        int scope;  /* scope level for locals */
 | 
					        int scope;  /* scope level for locals */
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
        long r;    /* associated register */
 | 
					        long r;    /* associated register or VT_CONST/VT_LOCAL and LVAL type */
 | 
				
			||||||
        struct Attribute a;
 | 
					        struct Attribute a;
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
| 
						 | 
					@ -416,7 +417,7 @@ typedef struct Sym {
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    CType type;    /* associated type */
 | 
					    CType type;    /* associated type */
 | 
				
			||||||
    union {
 | 
					    union {
 | 
				
			||||||
        struct Sym *next; /* next related symbol */
 | 
					        struct Sym *next; /* next related symbol (for fields and anoms) */
 | 
				
			||||||
        long jnext; /* next jump label */
 | 
					        long jnext; /* next jump label */
 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
    struct Sym *prev; /* prev symbol in stack */
 | 
					    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);
 | 
					ST_FUNC void gen_expr64(ExprValue *pe);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
ST_FUNC void asm_opcode(TCCState *s1, int opcode);
 | 
					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 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 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);
 | 
					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;
 | 
					    Sym *s;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    s = sym_malloc();
 | 
					    s = sym_malloc();
 | 
				
			||||||
    s->asm_label = 0;
 | 
					    s->scope = 0;
 | 
				
			||||||
    s->v = v;
 | 
					    s->v = v;
 | 
				
			||||||
    s->type.t = t;
 | 
					    s->type.t = t;
 | 
				
			||||||
    s->type.ref = NULL;
 | 
					    s->type.ref = NULL;
 | 
				
			||||||
| 
						 | 
					@ -577,6 +577,7 @@ static void vsetc(CType *type, int r, CValue *vc)
 | 
				
			||||||
    vtop->r = r;
 | 
					    vtop->r = r;
 | 
				
			||||||
    vtop->r2 = VT_CONST;
 | 
					    vtop->r2 = VT_CONST;
 | 
				
			||||||
    vtop->c = *vc;
 | 
					    vtop->c = *vc;
 | 
				
			||||||
 | 
					    vtop->sym = NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* push constant of type "type" with useless value */
 | 
					/* push constant of type "type" with useless value */
 | 
				
			||||||
| 
						 | 
					@ -4599,11 +4600,17 @@ ST_FUNC void unary(void)
 | 
				
			||||||
            r = VT_SYM | VT_CONST;
 | 
					            r = VT_SYM | VT_CONST;
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            r = s->r;
 | 
					            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);
 | 
					        vset(&s->type, r, s->c);
 | 
				
			||||||
        /* if forward reference, we must point to s */
 | 
					        /* Point to s as backpointer (even without r&VT_SYM).
 | 
				
			||||||
        if (vtop->r & VT_SYM) {
 | 
						   Will be used by at least the x86 inline asm parser for
 | 
				
			||||||
 | 
						   regvars.  */
 | 
				
			||||||
	vtop->sym = s;
 | 
						vtop->sym = s;
 | 
				
			||||||
 | 
					        if (vtop->r & VT_SYM) {
 | 
				
			||||||
            vtop->c.i = 0;
 | 
					            vtop->c.i = 0;
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        break;
 | 
					        break;
 | 
				
			||||||
| 
						 | 
					@ -6462,6 +6469,13 @@ static void decl_initializer_alloc(CType *type, AttributeDef *ad, int r,
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
        if (v) {
 | 
					        if (v) {
 | 
				
			||||||
            /* local variable */
 | 
					            /* 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);
 | 
					            sym_push(v, type, r, addr);
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            /* push local reference */
 | 
					            /* push local reference */
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -2939,6 +2939,7 @@ void asm_test(void)
 | 
				
			||||||
#ifdef BOOL_ISOC99
 | 
					#ifdef BOOL_ISOC99
 | 
				
			||||||
    _Bool somebool;
 | 
					    _Bool somebool;
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					    register int regvar asm("%esi");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    printf("inline asm:\n");
 | 
					    printf("inline asm:\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3001,6 +3002,8 @@ void asm_test(void)
 | 
				
			||||||
    val = 44;
 | 
					    val = 44;
 | 
				
			||||||
    fancy_copy2 (&val, &val2);
 | 
					    fancy_copy2 (&val, &val2);
 | 
				
			||||||
    printf ("fancycpy2(%d)=%d\n", val, val2);
 | 
					    printf ("fancycpy2(%d)=%d\n", val, val2);
 | 
				
			||||||
 | 
					    asm volatile ("mov $0x4243, %%esi" : "=r" (regvar));
 | 
				
			||||||
 | 
					    printf ("regvar=%x\n", regvar);
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
 label1:
 | 
					 label1:
 | 
				
			||||||
    goto label2;
 | 
					    goto label2;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		
		Reference in a new issue