Support asm goto
this adds generic support for asm goto, but I've tested only on x86.
This commit is contained in:
parent
154c3e7450
commit
98bab41cba
7 changed files with 132 additions and 16 deletions
3
tcc.h
3
tcc.h
|
@ -727,7 +727,7 @@ typedef struct ExprValue {
|
||||||
|
|
||||||
#define MAX_ASM_OPERANDS 30
|
#define MAX_ASM_OPERANDS 30
|
||||||
typedef struct ASMOperand {
|
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 *constraint;
|
||||||
char asm_str[16]; /* computed asm string for operand */
|
char asm_str[16]; /* computed asm string for operand */
|
||||||
SValue *vt; /* C value of the expression */
|
SValue *vt; /* C value of the expression */
|
||||||
|
@ -738,6 +738,7 @@ typedef struct ASMOperand {
|
||||||
int is_llong; /* true if double register value */
|
int is_llong; /* true if double register value */
|
||||||
int is_memory; /* true if memory operand */
|
int is_memory; /* true if memory operand */
|
||||||
int is_rw; /* for '+' modifier */
|
int is_rw; /* for '+' modifier */
|
||||||
|
int is_label; /* for asm goto */
|
||||||
} ASMOperand;
|
} ASMOperand;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
61
tccasm.c
61
tccasm.c
|
@ -23,12 +23,18 @@
|
||||||
#ifdef CONFIG_TCC_ASM
|
#ifdef CONFIG_TCC_ASM
|
||||||
|
|
||||||
static Section *last_text_section; /* to handle .previous asm directive */
|
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)
|
ST_FUNC int asm_get_local_label_name(TCCState *s1, unsigned int n)
|
||||||
{
|
{
|
||||||
char buf[64];
|
return asm_get_prefix_name(s1, "L..", n);
|
||||||
snprintf(buf, sizeof(buf), "L..%u", n);
|
|
||||||
return tok_alloc_const(buf);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
|
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;
|
modifier = 0;
|
||||||
if (*str == 'c' || *str == 'n' ||
|
if (*str == 'c' || *str == 'n' ||
|
||||||
*str == 'b' || *str == 'w' || *str == 'h' || *str == 'k' ||
|
*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,
|
/* P in GCC would add "@PLT" to symbol refs in PIC mode,
|
||||||
and make literal operands not be decorated with '$'. */
|
and make literal operands not be decorated with '$'. */
|
||||||
*str == 'P')
|
*str == 'P')
|
||||||
|
@ -1086,6 +1092,9 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
|
||||||
if (index < 0)
|
if (index < 0)
|
||||||
tcc_error("invalid operand reference after %%");
|
tcc_error("invalid operand reference after %%");
|
||||||
op = &operands[index];
|
op = &operands[index];
|
||||||
|
if (modifier == 'l') {
|
||||||
|
cstr_cat(out_str, get_tok_str(op->is_label, NULL), -1);
|
||||||
|
} else {
|
||||||
sv = *op->vt;
|
sv = *op->vt;
|
||||||
if (op->reg >= 0) {
|
if (op->reg >= 0) {
|
||||||
sv.r = op->reg;
|
sv.r = op->reg;
|
||||||
|
@ -1093,6 +1102,7 @@ static void subst_asm_operands(ASMOperand *operands, int nb_operands,
|
||||||
sv.r |= VT_LVAL;
|
sv.r |= VT_LVAL;
|
||||||
}
|
}
|
||||||
subst_asm_operand(out_str, &sv, modifier);
|
subst_asm_operand(out_str, &sv, modifier);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
add_char:
|
add_char:
|
||||||
cstr_ccat(out_str, c);
|
cstr_ccat(out_str, c);
|
||||||
|
@ -1163,18 +1173,20 @@ ST_FUNC void asm_instr(void)
|
||||||
{
|
{
|
||||||
CString astr, astr1;
|
CString astr, astr1;
|
||||||
ASMOperand operands[MAX_ASM_OPERANDS];
|
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];
|
uint8_t clobber_regs[NB_ASM_REGS];
|
||||||
Section *sec;
|
Section *sec;
|
||||||
|
|
||||||
/* since we always generate the asm() instruction, we can ignore
|
/* since we always generate the asm() instruction, we can ignore
|
||||||
volatile */
|
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();
|
next();
|
||||||
}
|
}
|
||||||
parse_asm_str(&astr);
|
parse_asm_str(&astr);
|
||||||
nb_operands = 0;
|
nb_operands = 0;
|
||||||
nb_outputs = 0;
|
nb_outputs = 0;
|
||||||
|
nb_labels = 0;
|
||||||
must_subst = 0;
|
must_subst = 0;
|
||||||
memset(clobber_regs, 0, sizeof(clobber_regs));
|
memset(clobber_regs, 0, sizeof(clobber_regs));
|
||||||
if (tok == ':') {
|
if (tok == ':') {
|
||||||
|
@ -1193,6 +1205,8 @@ ST_FUNC void asm_instr(void)
|
||||||
/* XXX: handle registers */
|
/* XXX: handle registers */
|
||||||
next();
|
next();
|
||||||
for(;;) {
|
for(;;) {
|
||||||
|
if (tok == ':')
|
||||||
|
break;
|
||||||
if (tok != TOK_STR)
|
if (tok != TOK_STR)
|
||||||
expect("string constant");
|
expect("string constant");
|
||||||
asm_clobber(clobber_regs, tokc.str.data);
|
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);
|
printf("asm: \"%s\"\n", (char *)astr.data);
|
||||||
#endif
|
#endif
|
||||||
if (must_subst) {
|
if (must_subst) {
|
||||||
subst_asm_operands(operands, nb_operands, &astr1, &astr);
|
subst_asm_operands(operands, nb_operands + nb_labels, &astr1, &astr);
|
||||||
cstr_free(&astr);
|
cstr_free(&astr);
|
||||||
} else {
|
} else {
|
||||||
astr1 = astr;
|
astr1 = astr;
|
||||||
|
|
2
tccgen.c
2
tccgen.c
|
@ -5677,7 +5677,7 @@ ST_FUNC void unary(void)
|
||||||
if (s->r == LABEL_DECLARED)
|
if (s->r == LABEL_DECLARED)
|
||||||
s->r = LABEL_FORWARD;
|
s->r = LABEL_FORWARD;
|
||||||
}
|
}
|
||||||
if (!s->type.t) {
|
if ((s->type.t & VT_BTYPE) != VT_PTR) {
|
||||||
s->type.t = VT_VOID;
|
s->type.t = VT_VOID;
|
||||||
mk_pointer(&s->type);
|
mk_pointer(&s->type);
|
||||||
s->type.t |= VT_STATIC;
|
s->type.t |= VT_STATIC;
|
||||||
|
|
2
tccpp.c
2
tccpp.c
|
@ -1434,7 +1434,7 @@ ST_FUNC Sym *label_find(int v)
|
||||||
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
|
ST_FUNC Sym *label_push(Sym **ptop, int v, int flags)
|
||||||
{
|
{
|
||||||
Sym *s, **ps;
|
Sym *s, **ps;
|
||||||
s = sym_push2(ptop, v, 0, 0);
|
s = sym_push2(ptop, v, VT_STATIC, 0);
|
||||||
s->r = flags;
|
s->r = flags;
|
||||||
ps = &table_ident[v - TOK_IDENT]->sym_label;
|
ps = &table_ident[v - TOK_IDENT]->sym_label;
|
||||||
if (ptop == &global_label_stack) {
|
if (ptop == &global_label_stack) {
|
||||||
|
|
62
tests/tests2/127_asm_goto.c
Normal file
62
tests/tests2/127_asm_goto.c
Normal 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;
|
||||||
|
}
|
5
tests/tests2/127_asm_goto.expect
Normal file
5
tests/tests2/127_asm_goto.expect
Normal 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
|
|
@ -25,6 +25,7 @@ ifeq (,$(filter i386 x86_64,$(ARCH)))
|
||||||
SKIP += 85_asm-outside-function.test # x86 asm
|
SKIP += 85_asm-outside-function.test # x86 asm
|
||||||
SKIP += 124_atomic_counter.test
|
SKIP += 124_atomic_counter.test
|
||||||
SKIP += 125_atomic_misc.test # currently only x86 supported
|
SKIP += 125_atomic_misc.test # currently only x86 supported
|
||||||
|
SKIP += 127_asm_goto.text # hardcodes x86 asm
|
||||||
endif
|
endif
|
||||||
ifeq ($(CONFIG_backtrace),no)
|
ifeq ($(CONFIG_backtrace),no)
|
||||||
SKIP += 113_btdll.test
|
SKIP += 113_btdll.test
|
||||||
|
|
Loading…
Reference in a new issue