Support asm goto

this adds generic support for asm goto, but I've tested only
on x86.
This commit is contained in:
Michael Matz 2022-07-07 16:45:16 +02:00
parent 154c3e7450
commit 98bab41cba
7 changed files with 132 additions and 16 deletions

3
tcc.h
View file

@ -727,7 +727,7 @@ typedef struct ExprValue {
#define MAX_ASM_OPERANDS 30
typedef struct ASMOperand {
int id; /* GCC 3 optional identifier (0 if number only supported */
int id; /* GCC 3 optional identifier (0 if number only supported) */
char *constraint;
char asm_str[16]; /* computed asm string for operand */
SValue *vt; /* C value of the expression */
@ -738,6 +738,7 @@ typedef struct ASMOperand {
int is_llong; /* true if double register value */
int is_memory; /* true if memory operand */
int is_rw; /* for '+' modifier */
int is_label; /* for asm goto */
} ASMOperand;
#endif

View file

@ -23,12 +23,18 @@
#ifdef CONFIG_TCC_ASM
static Section *last_text_section; /* to handle .previous asm directive */
static int asmgoto_n;
static int asm_get_prefix_name(TCCState *s1, const char *prefix, unsigned int n)
{
char buf[64];
snprintf(buf, sizeof(buf), "%s%u", prefix, n);
return tok_alloc_const(buf);
}
ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
{
char buf[64];
snprintf(buf, sizeof(buf), "L..%u", n);
return tok_alloc_const(buf);
return asm_get_prefix_name(s1, "L..", n);
}
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
@ -1077,7 +1083,7 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
modifier = 0;
if (*str == 'c' || *str == 'n' ||
*str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
*str == 'q' ||
*str == 'q' || *str == 'l' ||
/* P in GCC would add "@PLT" to symbol refs in PIC mode,
and make literal operands not be decorated with '$'. */
*str == 'P')
@ -1086,6 +1092,9 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
if (index < 0)
tcc_error("invalid operand reference after %%");
op = &operands[index];
if (modifier == 'l') {
cstr_cat(out_str, get_tok_str(op->is_label, NULL), -1);
} else {
sv = *op->vt;
if (op->reg >= 0) {
sv.r = op->reg;
@ -1093,6 +1102,7 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
sv.r |= VT_LVAL;
}
subst_asm_operand(out_str, &sv, modifier);
}
} else {
add_char:
cstr_ccat(out_str, c);
@ -1163,18 +1173,20 @@ ST_FUNC void asm_instr(void)
{
CString astr, astr1;
ASMOperand operands[MAX_ASM_OPERANDS];
int nb_outputs, nb_operands, i, must_subst, out_reg;
int nb_outputs, nb_operands, i, must_subst, out_reg, nb_labels;
uint8_t clobber_regs[NB_ASM_REGS];
Section *sec;
/* since we always generate the asm() instruction, we can ignore
volatile */
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
while (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3
|| tok == TOK_GOTO) {
next();
}
parse_asm_str(&astr);
nb_operands = 0;
nb_outputs = 0;
nb_labels = 0;
must_subst = 0;
memset(clobber_regs, 0, sizeof(clobber_regs));
if (tok == ':') {
@ -1193,6 +1205,8 @@ ST_FUNC void asm_instr(void)
/* XXX: handle registers */
next();
for(;;) {
if (tok == ':')
break;
if (tok != TOK_STR)
expect("string constant");
asm_clobber(clobber_regs, tokc.str.data);
@ -1204,6 +1218,39 @@ ST_FUNC void asm_instr(void)
}
}
}
if (tok == ':') {
/* goto labels */
next();
for (;;) {
Sym *csym;
int asmname;
if (nb_operands + nb_labels >= MAX_ASM_OPERANDS)
tcc_error("too many asm operands");
if (tok < TOK_UIDENT)
expect("label identifier");
operands[nb_operands + nb_labels++].id = tok;
csym = label_find(tok);
if (!csym) {
csym = label_push(&global_label_stack, tok,
LABEL_FORWARD);
} else {
if (csym->r == LABEL_DECLARED)
csym->r = LABEL_FORWARD;
}
next();
asmname = asm_get_prefix_name(tcc_state, "LG.",
++asmgoto_n);
if (!csym->c)
put_extern_sym2(csym, SHN_UNDEF, 0, 0, 1);
get_asm_sym(asmname, csym);
operands[nb_operands + nb_labels - 1].is_label = asmname;
if (tok != ',')
break;
next();
}
}
}
}
}
@ -1226,7 +1273,7 @@ ST_FUNC void asm_instr(void)
printf("asm: \"%s\"\n", (char *)astr.data);
#endif
if (must_subst) {
subst_asm_operands(operands, nb_operands, &astr1, &astr);
subst_asm_operands(operands, nb_operands + nb_labels, &astr1, &astr);
cstr_free(&astr);
} else {
astr1 = astr;

View file

@ -5677,7 +5677,7 @@ ST_FUNC void unary(void)
if (s->r == LABEL_DECLARED)
s->r = LABEL_FORWARD;
}
if (!s->type.t) {
if ((s->type.t & VT_BTYPE) != VT_PTR) {
s->type.t = VT_VOID;
mk_pointer(&s->type);
s->type.t |= VT_STATIC;

View file

@ -1434,7 +1434,7 @@ ST_FUNC Sym *label_find(int v)
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
{
Sym *s, **ps;
s = sym_push2(ptop, v, 0, 0);
s = sym_push2(ptop, v, VT_STATIC, 0);
s->r = flags;
ps = &table_ident[v - TOK_IDENT]->sym_label;
if (ptop == &global_label_stack) {

View file

@ -0,0 +1,62 @@
static int simple_jump(void)
{
asm goto ("jmp %l[label]" : : : : label);
return 0;
label:
return 1;
}
static int three_way_jump(int val, int *addr)
{
*addr = 42;
asm goto ("cmp $0, %1\n\t"
"jg %l[larger]\n\t"
"jl %l[smaller]\n\t"
"incl %0\n\t"
: "=m" (*addr)
: "r" (val)
:
: smaller, larger);
return 1;
smaller:
return 2;
larger:
return 3;
}
static int another_jump(void)
{
asm goto ("jmp %l[label]" : : : : label);
return 70;
/* Use the same label name as in simple_jump to check that
that doesn't confuse our C/ASM symbol tables */
label:
return 71;
}
extern int printf (const char *, ...);
int main(void)
{
int i;
if (simple_jump () == 1)
printf ("simple_jump: okay\n");
else
printf ("simple_jump: wrong\n");
if (another_jump () == 71)
printf ("another_jump: okay\n");
else
printf ("another_jump: wrong\n");
if (three_way_jump(0, &i) == 1 && i == 43)
printf ("three_way_jump(0): okay\n");
else
printf ("three_way_jump(0): wrong (i=%d)\n", i);
if (three_way_jump(1, &i) == 3 && i == 42)
printf ("three_way_jump(1): okay\n");
else
printf ("three_way_jump(1): wrong (i=%d)\n", i);
if (three_way_jump(-1, &i) == 2 && i == 42)
printf ("three_way_jump(-1): okay\n");
else
printf ("three_way_jump(-1): wrong (i=%d)\n", i);
return 0;
}

View file

@ -0,0 +1,5 @@
simple_jump: okay
another_jump: okay
three_way_jump(0): okay
three_way_jump(1): okay
three_way_jump(-1): okay

View file

@ -25,6 +25,7 @@ ifeq (,$(filter i386 x86_64,$(ARCH)))
SKIP += 85_asm-outside-function.test # x86 asm
SKIP += 124_atomic_counter.test
SKIP += 125_atomic_misc.test # currently only x86 supported
SKIP += 127_asm_goto.text # hardcodes x86 asm
endif
ifeq ($(CONFIG_backtrace),no)
SKIP += 113_btdll.test