From 98bab41cba27a7da9ebf40389212e811ee2277ac Mon Sep 17 00:00:00 2001 From: Michael Matz Date: Thu, 7 Jul 2022 16:45:16 +0200 Subject: [PATCH] Support asm goto this adds generic support for asm goto, but I've tested only on x86. --- tcc.h | 3 +- tccasm.c | 73 ++++++++++++++++++++++++++------ tccgen.c | 2 +- tccpp.c | 2 +- tests/tests2/127_asm_goto.c | 62 +++++++++++++++++++++++++++ tests/tests2/127_asm_goto.expect | 5 +++ tests/tests2/Makefile | 1 + 7 files changed, 132 insertions(+), 16 deletions(-) create mode 100644 tests/tests2/127_asm_goto.c create mode 100644 tests/tests2/127_asm_goto.expect diff --git a/tcc.h b/tcc.h index 9605a9db..bfac0638 100644 --- a/tcc.h +++ b/tcc.h @@ -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 diff --git a/tccasm.c b/tccasm.c index 2dd8921b..65aae62f 100644 --- a/tccasm.c +++ b/tccasm.c @@ -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,13 +1092,17 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands, if (index < 0) tcc_error("invalid operand reference after %%"); op = &operands[index]; - sv = *op->vt; - if (op->reg >= 0) { - sv.r = op->reg; - if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) - sv.r |= VT_LVAL; + 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; + if ((op->vt->r & VT_VALMASK) == VT_LLOCAL && op->is_memory) + sv.r |= VT_LVAL; + } + subst_asm_operand(out_str, &sv, modifier); } - 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; diff --git a/tccgen.c b/tccgen.c index 2d1829fc..cc7cc9eb 100644 --- a/tccgen.c +++ b/tccgen.c @@ -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; diff --git a/tccpp.c b/tccpp.c index c2026faa..a5f6d7af 100644 --- a/tccpp.c +++ b/tccpp.c @@ -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) { diff --git a/tests/tests2/127_asm_goto.c b/tests/tests2/127_asm_goto.c new file mode 100644 index 00000000..de631aa3 --- /dev/null +++ b/tests/tests2/127_asm_goto.c @@ -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; +} diff --git a/tests/tests2/127_asm_goto.expect b/tests/tests2/127_asm_goto.expect new file mode 100644 index 00000000..e1b71699 --- /dev/null +++ b/tests/tests2/127_asm_goto.expect @@ -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 diff --git a/tests/tests2/Makefile b/tests/tests2/Makefile index 44c28fe3..fbbf0ba5 100644 --- a/tests/tests2/Makefile +++ b/tests/tests2/Makefile @@ -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