mirror of
https://github.com/mirror/tinycc.git
synced 2024-12-28 04:00:06 +08:00
absolute symbols support - .org, .fill and .previous directives
This commit is contained in:
parent
9668499b9f
commit
0bd402d2eb
221
tccasm.c
221
tccasm.c
@ -28,6 +28,8 @@ static int asm_get_local_label_name(TCCState *s1, unsigned int n)
|
|||||||
return ts->tok;
|
return ts->tok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void asm_expr(TCCState *s1, ExprValue *pe);
|
||||||
|
|
||||||
/* We do not use the C expression parser to handle symbols. Maybe the
|
/* We do not use the C expression parser to handle symbols. Maybe the
|
||||||
C expression parser could be tweaked to do so. */
|
C expression parser could be tweaked to do so. */
|
||||||
|
|
||||||
@ -91,6 +93,11 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
|
|||||||
pe->sym = NULL;
|
pe->sym = NULL;
|
||||||
next();
|
next();
|
||||||
break;
|
break;
|
||||||
|
case '(':
|
||||||
|
next();
|
||||||
|
asm_expr(s1, pe);
|
||||||
|
skip(')');
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
if (tok >= TOK_IDENT) {
|
if (tok >= TOK_IDENT) {
|
||||||
/* label case : if the label was not found, add one */
|
/* label case : if the label was not found, add one */
|
||||||
@ -100,8 +107,14 @@ static void asm_expr_unary(TCCState *s1, ExprValue *pe)
|
|||||||
/* NOTE: by default, the symbol is global */
|
/* NOTE: by default, the symbol is global */
|
||||||
sym->type.t = VT_VOID;
|
sym->type.t = VT_VOID;
|
||||||
}
|
}
|
||||||
|
if (sym->r == SHN_ABS) {
|
||||||
|
/* if absolute symbol, no need to put a symbol value */
|
||||||
|
pe->v = (long)sym->next;
|
||||||
|
pe->sym = NULL;
|
||||||
|
} else {
|
||||||
pe->v = 0;
|
pe->v = 0;
|
||||||
pe->sym = sym;
|
pe->sym = sym;
|
||||||
|
}
|
||||||
next();
|
next();
|
||||||
} else {
|
} else {
|
||||||
error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
|
error("bad expression syntax [%s]", get_tok_str(tok, &tokc));
|
||||||
@ -241,7 +254,8 @@ static int asm_int_expr(TCCState *s1)
|
|||||||
|
|
||||||
/* NOTE: the same name space as C labels is used to avoid using too
|
/* NOTE: the same name space as C labels is used to avoid using too
|
||||||
much memory when storing labels in TokenStrings */
|
much memory when storing labels in TokenStrings */
|
||||||
static void asm_new_label(TCCState *s1, int label, int is_local)
|
static void asm_new_label1(TCCState *s1, int label, int is_local,
|
||||||
|
int sh_num, int value)
|
||||||
{
|
{
|
||||||
Sym *sym;
|
Sym *sym;
|
||||||
|
|
||||||
@ -262,18 +276,29 @@ static void asm_new_label(TCCState *s1, int label, int is_local)
|
|||||||
sym = label_push(&s1->asm_labels, label, 0);
|
sym = label_push(&s1->asm_labels, label, 0);
|
||||||
sym->type.t = VT_STATIC | VT_VOID;
|
sym->type.t = VT_STATIC | VT_VOID;
|
||||||
}
|
}
|
||||||
sym->r = cur_text_section->sh_num;
|
sym->r = sh_num;
|
||||||
sym->next = (void *)ind;
|
sym->next = (void *)value;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void asm_new_label(TCCState *s1, int label, int is_local)
|
||||||
|
{
|
||||||
|
asm_new_label1(s1, label, is_local, cur_text_section->sh_num, ind);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asm_free_labels(TCCState *st)
|
static void asm_free_labels(TCCState *st)
|
||||||
{
|
{
|
||||||
Sym *s, *s1;
|
Sym *s, *s1;
|
||||||
|
Section *sec;
|
||||||
|
|
||||||
for(s = st->asm_labels; s != NULL; s = s1) {
|
for(s = st->asm_labels; s != NULL; s = s1) {
|
||||||
s1 = s->prev;
|
s1 = s->prev;
|
||||||
/* define symbol value in object file */
|
/* define symbol value in object file */
|
||||||
if (s->r) {
|
if (s->r) {
|
||||||
put_extern_sym(s, st->sections[s->r], (long)s->next, 0);
|
if (s->r == SHN_ABS)
|
||||||
|
sec = SECTION_ABS;
|
||||||
|
else
|
||||||
|
sec = st->sections[s->r];
|
||||||
|
put_extern_sym(s, sec, (long)s->next, 0);
|
||||||
}
|
}
|
||||||
/* remove label */
|
/* remove label */
|
||||||
table_ident[s->v - TOK_IDENT]->sym_label = NULL;
|
table_ident[s->v - TOK_IDENT]->sym_label = NULL;
|
||||||
@ -282,13 +307,18 @@ static void asm_free_labels(TCCState *st)
|
|||||||
st->asm_labels = NULL;
|
st->asm_labels = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void use_section1(TCCState *s1, Section *sec)
|
||||||
|
{
|
||||||
|
cur_text_section->data_offset = ind;
|
||||||
|
cur_text_section = sec;
|
||||||
|
ind = cur_text_section->data_offset;
|
||||||
|
}
|
||||||
|
|
||||||
static void use_section(TCCState *s1, const char *name)
|
static void use_section(TCCState *s1, const char *name)
|
||||||
{
|
{
|
||||||
Section *sec;
|
Section *sec;
|
||||||
sec = find_section(s1, name);
|
sec = find_section(s1, name);
|
||||||
cur_text_section->data_offset = ind;
|
use_section1(s1, sec);
|
||||||
cur_text_section = sec;
|
|
||||||
ind = cur_text_section->data_offset;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void asm_parse_directive(TCCState *s1)
|
static void asm_parse_directive(TCCState *s1)
|
||||||
@ -312,6 +342,9 @@ static void asm_parse_directive(TCCState *s1)
|
|||||||
error("alignment must be a positive power of two");
|
error("alignment must be a positive power of two");
|
||||||
offset = (ind + n - 1) & -n;
|
offset = (ind + n - 1) & -n;
|
||||||
size = offset - ind;
|
size = offset - ind;
|
||||||
|
/* the section must have a compatible alignment */
|
||||||
|
if (sec->sh_addralign < n)
|
||||||
|
sec->sh_addralign = n;
|
||||||
} else {
|
} else {
|
||||||
size = n;
|
size = n;
|
||||||
}
|
}
|
||||||
@ -320,6 +353,7 @@ static void asm_parse_directive(TCCState *s1)
|
|||||||
next();
|
next();
|
||||||
v = asm_int_expr(s1);
|
v = asm_int_expr(s1);
|
||||||
}
|
}
|
||||||
|
zero_pad:
|
||||||
if (sec->sh_type != SHT_NOBITS) {
|
if (sec->sh_type != SHT_NOBITS) {
|
||||||
sec->data_offset = ind;
|
sec->data_offset = ind;
|
||||||
ptr = section_ptr_add(sec, size);
|
ptr = section_ptr_add(sec, size);
|
||||||
@ -361,6 +395,61 @@ static void asm_parse_directive(TCCState *s1)
|
|||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TOK_ASM_fill:
|
||||||
|
{
|
||||||
|
int repeat, size, val, i, j;
|
||||||
|
uint8_t repeat_buf[8];
|
||||||
|
next();
|
||||||
|
repeat = asm_int_expr(s1);
|
||||||
|
if (repeat < 0) {
|
||||||
|
error("repeat < 0; .fill ignored");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
size = 1;
|
||||||
|
val = 0;
|
||||||
|
if (tok == ',') {
|
||||||
|
next();
|
||||||
|
size = asm_int_expr(s1);
|
||||||
|
if (size < 0) {
|
||||||
|
error("size < 0; .fill ignored");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (size > 8)
|
||||||
|
size = 8;
|
||||||
|
if (tok == ',') {
|
||||||
|
next();
|
||||||
|
val = asm_int_expr(s1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* XXX: endianness */
|
||||||
|
repeat_buf[0] = val;
|
||||||
|
repeat_buf[1] = val >> 8;
|
||||||
|
repeat_buf[2] = val >> 16;
|
||||||
|
repeat_buf[3] = val >> 24;
|
||||||
|
repeat_buf[4] = 0;
|
||||||
|
repeat_buf[5] = 0;
|
||||||
|
repeat_buf[6] = 0;
|
||||||
|
repeat_buf[7] = 0;
|
||||||
|
for(i = 0; i < repeat; i++) {
|
||||||
|
for(j = 0; j < size; j++) {
|
||||||
|
g(repeat_buf[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case TOK_ASM_org:
|
||||||
|
{
|
||||||
|
unsigned long n;
|
||||||
|
next();
|
||||||
|
/* XXX: handle section symbols too */
|
||||||
|
n = asm_int_expr(s1);
|
||||||
|
if (n < ind)
|
||||||
|
error("attempt to .org backwards");
|
||||||
|
v = 0;
|
||||||
|
size = n - ind;
|
||||||
|
goto zero_pad;
|
||||||
|
}
|
||||||
|
break;
|
||||||
case TOK_ASM_globl:
|
case TOK_ASM_globl:
|
||||||
case TOK_ASM_global:
|
case TOK_ASM_global:
|
||||||
{
|
{
|
||||||
@ -420,9 +509,28 @@ static void asm_parse_directive(TCCState *s1)
|
|||||||
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
|
pstrcat(sname, sizeof(sname), get_tok_str(tok, NULL));
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
if (tok == ',') {
|
||||||
|
/* skip section options */
|
||||||
|
next();
|
||||||
|
if (tok != TOK_STR)
|
||||||
|
expect("string constant");
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
last_text_section = cur_text_section;
|
||||||
use_section(s1, sname);
|
use_section(s1, sname);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case TOK_ASM_previous:
|
||||||
|
{
|
||||||
|
Section *sec;
|
||||||
|
next();
|
||||||
|
if (!last_text_section)
|
||||||
|
error("no previous section referenced");
|
||||||
|
sec = cur_text_section;
|
||||||
|
use_section1(s1, last_text_section);
|
||||||
|
last_text_section = sec;
|
||||||
|
}
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
|
error("unknown assembler directive '.%s'", get_tok_str(tok, NULL));
|
||||||
break;
|
break;
|
||||||
@ -471,7 +579,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
|
|||||||
|
|
||||||
ch = file->buf_ptr[0];
|
ch = file->buf_ptr[0];
|
||||||
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
tok_flags = TOK_FLAG_BOL | TOK_FLAG_BOF;
|
||||||
parse_flags = 0;
|
parse_flags = PARSE_FLAG_ASM_COMMENTS;
|
||||||
if (do_preprocess)
|
if (do_preprocess)
|
||||||
parse_flags |= PARSE_FLAG_PREPROCESS;
|
parse_flags |= PARSE_FLAG_PREPROCESS;
|
||||||
next();
|
next();
|
||||||
@ -507,6 +615,12 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
|
|||||||
asm_new_label(s1, opcode, 0);
|
asm_new_label(s1, opcode, 0);
|
||||||
next();
|
next();
|
||||||
goto redo;
|
goto redo;
|
||||||
|
} else if (tok == '=') {
|
||||||
|
int n;
|
||||||
|
next();
|
||||||
|
n = asm_int_expr(s1);
|
||||||
|
asm_new_label1(s1, opcode, 0, SHN_ABS, n);
|
||||||
|
goto redo;
|
||||||
} else {
|
} else {
|
||||||
asm_opcode(s1, opcode);
|
asm_opcode(s1, opcode);
|
||||||
}
|
}
|
||||||
@ -527,6 +641,7 @@ static int tcc_assemble_internal(TCCState *s1, int do_preprocess)
|
|||||||
/* Assemble the current file */
|
/* Assemble the current file */
|
||||||
static int tcc_assemble(TCCState *s1, int do_preprocess)
|
static int tcc_assemble(TCCState *s1, int do_preprocess)
|
||||||
{
|
{
|
||||||
|
Sym *define_start;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
preprocess_init(s1);
|
preprocess_init(s1);
|
||||||
@ -535,9 +650,14 @@ static int tcc_assemble(TCCState *s1, int do_preprocess)
|
|||||||
cur_text_section = text_section;
|
cur_text_section = text_section;
|
||||||
ind = cur_text_section->data_offset;
|
ind = cur_text_section->data_offset;
|
||||||
|
|
||||||
|
define_start = define_stack;
|
||||||
|
|
||||||
ret = tcc_assemble_internal(s1, do_preprocess);
|
ret = tcc_assemble_internal(s1, do_preprocess);
|
||||||
|
|
||||||
cur_text_section->data_offset = ind;
|
cur_text_section->data_offset = ind;
|
||||||
|
|
||||||
|
free_defines(define_start);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -691,12 +811,14 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
|
|||||||
if (is_output) {
|
if (is_output) {
|
||||||
test_lvalue();
|
test_lvalue();
|
||||||
} else {
|
} else {
|
||||||
/* we want to avoid LLOCAL case. note that it may come
|
/* we want to avoid LLOCAL case, except when the 'm'
|
||||||
from register storage, so we need to convert (reg)
|
constraint is used. Note that it may come from
|
||||||
|
register storage, so we need to convert (reg)
|
||||||
case */
|
case */
|
||||||
if ((vtop->r & VT_LVAL) &&
|
if ((vtop->r & VT_LVAL) &&
|
||||||
((vtop->r & VT_VALMASK) == VT_LLOCAL ||
|
((vtop->r & VT_VALMASK) == VT_LLOCAL ||
|
||||||
(vtop->r & VT_VALMASK) < VT_CONST)) {
|
(vtop->r & VT_VALMASK) < VT_CONST) &&
|
||||||
|
!strchr(op->constraint, 'm')) {
|
||||||
gv(RC_INT);
|
gv(RC_INT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -712,14 +834,27 @@ static void parse_asm_operands(ASMOperand *operands, int *nb_operands_ptr,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void parse_asm_str(CString *astr)
|
||||||
|
{
|
||||||
|
skip('(');
|
||||||
|
/* read the string */
|
||||||
|
if (tok != TOK_STR)
|
||||||
|
expect("string constant");
|
||||||
|
cstr_new(astr);
|
||||||
|
while (tok == TOK_STR) {
|
||||||
|
/* XXX: add \0 handling too ? */
|
||||||
|
cstr_cat(astr, tokc.cstr->data);
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
cstr_ccat(astr, '\0');
|
||||||
|
}
|
||||||
|
|
||||||
/* parse the GCC asm() instruction */
|
/* parse the GCC asm() instruction */
|
||||||
static void asm_instr(void)
|
static void asm_instr(void)
|
||||||
{
|
{
|
||||||
CString astr, astr1;
|
CString astr, astr1;
|
||||||
ASMOperand operands[MAX_ASM_OPERANDS];
|
ASMOperand operands[MAX_ASM_OPERANDS];
|
||||||
int nb_inputs, nb_outputs, nb_operands, i;
|
int nb_inputs, nb_outputs, nb_operands, i, must_subst, out_reg;
|
||||||
uint8_t input_regs_allocated[NB_ASM_REGS];
|
|
||||||
uint8_t output_regs_allocated[NB_ASM_REGS];
|
|
||||||
uint8_t clobber_regs[NB_ASM_REGS];
|
uint8_t clobber_regs[NB_ASM_REGS];
|
||||||
|
|
||||||
next();
|
next();
|
||||||
@ -728,22 +863,14 @@ static void asm_instr(void)
|
|||||||
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
|
if (tok == TOK_VOLATILE1 || tok == TOK_VOLATILE2 || tok == TOK_VOLATILE3) {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
skip('(');
|
parse_asm_str(&astr);
|
||||||
/* read the string */
|
|
||||||
if (tok != TOK_STR)
|
|
||||||
expect("string constant");
|
|
||||||
cstr_new(&astr);
|
|
||||||
while (tok == TOK_STR) {
|
|
||||||
/* XXX: add \0 handling too ? */
|
|
||||||
cstr_cat(&astr, tokc.cstr->data);
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
cstr_ccat(&astr, '\0');
|
|
||||||
nb_operands = 0;
|
nb_operands = 0;
|
||||||
nb_outputs = 0;
|
nb_outputs = 0;
|
||||||
|
must_subst = 0;
|
||||||
memset(clobber_regs, 0, sizeof(clobber_regs));
|
memset(clobber_regs, 0, sizeof(clobber_regs));
|
||||||
if (tok == ':') {
|
if (tok == ':') {
|
||||||
next();
|
next();
|
||||||
|
must_subst = 1;
|
||||||
/* output args */
|
/* output args */
|
||||||
parse_asm_operands(operands, &nb_operands, 1);
|
parse_asm_operands(operands, &nb_operands, 1);
|
||||||
nb_outputs = nb_operands;
|
nb_outputs = nb_operands;
|
||||||
@ -780,19 +907,15 @@ static void asm_instr(void)
|
|||||||
save_regs(0);
|
save_regs(0);
|
||||||
|
|
||||||
/* compute constraints */
|
/* compute constraints */
|
||||||
asm_compute_constraints(input_regs_allocated,
|
asm_compute_constraints(operands, nb_operands, nb_outputs,
|
||||||
operands, nb_operands, nb_outputs, 0,
|
clobber_regs, &out_reg);
|
||||||
NULL);
|
|
||||||
asm_compute_constraints(output_regs_allocated,
|
|
||||||
operands, nb_operands, nb_outputs, 1,
|
|
||||||
input_regs_allocated);
|
|
||||||
|
|
||||||
/* substitute the operands in the asm string. No substitution is
|
/* substitute the operands in the asm string. No substitution is
|
||||||
done if no operands (GCC behaviour) */
|
done if no operands (GCC behaviour) */
|
||||||
#ifdef ASM_DEBUG
|
#ifdef ASM_DEBUG
|
||||||
printf("asm: \"%s\"\n", (char *)astr.data);
|
printf("asm: \"%s\"\n", (char *)astr.data);
|
||||||
#endif
|
#endif
|
||||||
if (nb_operands > 0) {
|
if (must_subst) {
|
||||||
subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
|
subst_asm_operands(operands, nb_operands, nb_outputs, &astr1, &astr);
|
||||||
cstr_free(&astr);
|
cstr_free(&astr);
|
||||||
} else {
|
} else {
|
||||||
@ -803,7 +926,8 @@ static void asm_instr(void)
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* generate loads */
|
/* generate loads */
|
||||||
asm_gen_code(operands, nb_operands, nb_outputs, 0, clobber_regs);
|
asm_gen_code(operands, nb_operands, nb_outputs, 0,
|
||||||
|
clobber_regs, out_reg);
|
||||||
|
|
||||||
/* assemble the string with tcc internal assembler */
|
/* assemble the string with tcc internal assembler */
|
||||||
tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
|
tcc_assemble_inline(tcc_state, astr1.data, astr1.size - 1);
|
||||||
@ -812,7 +936,8 @@ static void asm_instr(void)
|
|||||||
next();
|
next();
|
||||||
|
|
||||||
/* store the output values if needed */
|
/* store the output values if needed */
|
||||||
asm_gen_code(operands, nb_operands, nb_outputs, 1, clobber_regs);
|
asm_gen_code(operands, nb_operands, nb_outputs, 1,
|
||||||
|
clobber_regs, out_reg);
|
||||||
|
|
||||||
/* free everything */
|
/* free everything */
|
||||||
for(i=0;i<nb_operands;i++) {
|
for(i=0;i<nb_operands;i++) {
|
||||||
@ -824,3 +949,31 @@ static void asm_instr(void)
|
|||||||
cstr_free(&astr1);
|
cstr_free(&astr1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void asm_global_instr(void)
|
||||||
|
{
|
||||||
|
CString astr;
|
||||||
|
|
||||||
|
next();
|
||||||
|
parse_asm_str(&astr);
|
||||||
|
skip(')');
|
||||||
|
/* NOTE: we do not eat the ';' so that we can restore the current
|
||||||
|
token after the assembler parsing */
|
||||||
|
if (tok != ';')
|
||||||
|
expect("';'");
|
||||||
|
|
||||||
|
#ifdef ASM_DEBUG
|
||||||
|
printf("asm_global: \"%s\"\n", (char *)astr.data);
|
||||||
|
#endif
|
||||||
|
cur_text_section = text_section;
|
||||||
|
ind = cur_text_section->data_offset;
|
||||||
|
|
||||||
|
/* assemble the string with tcc internal assembler */
|
||||||
|
tcc_assemble_inline(tcc_state, astr.data, astr.size - 1);
|
||||||
|
|
||||||
|
cur_text_section->data_offset = ind;
|
||||||
|
|
||||||
|
/* restore the current C token */
|
||||||
|
next();
|
||||||
|
|
||||||
|
cstr_free(&astr);
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user