tccgen: introduce TOK_NEG for unary minus

for floats (currently only).  On x86_64 uses built-in fp
constants (in libtcc1.c) to avoid multiple anonymous
instances.

Also: win32/i386: use __alloca for big struct stack store
- use new function int tok_alloc_const(const char*);
- change alloca86.S to preserve EDX

tccelf.c: fix a warning with 'roinf_use'
This commit is contained in:
grischka 2021-01-04 13:16:05 +01:00
parent a5865fab22
commit aeb8f427e2
11 changed files with 145 additions and 100 deletions

View file

@ -1207,7 +1207,7 @@ ST_FUNC int asm_parse_regvar (int t)
s = table_ident[t - TOK_IDENT]->str; s = table_ident[t - TOK_IDENT]->str;
if (s[0] != '%') if (s[0] != '%')
return -1; return -1;
t = tok_alloc(s+1, strlen(s)-1)->tok; t = tok_alloc_const(s + 1);
unget_tok(t); unget_tok(t);
unget_tok('%'); unget_tok('%');
parse_operand(tcc_state, &op); parse_operand(tcc_state, &op);
@ -1488,7 +1488,7 @@ ST_FUNC void subst_asm_operand(CString *add_str,
in the C symbol table when later looking up in the C symbol table when later looking up
this name. So enter them now into the asm label this name. So enter them now into the asm label
list when we still know the symbol. */ list when we still know the symbol. */
get_asm_sym(tok_alloc(name, strlen(name))->tok, sv->sym); get_asm_sym(tok_alloc_const(name), sv->sym);
} }
if (tcc_state->leading_underscore) if (tcc_state->leading_underscore)
cstr_ccat(add_str, '_'); cstr_ccat(add_str, '_');
@ -1698,7 +1698,6 @@ ST_FUNC void asm_gen_code(ASMOperand *operands, int nb_operands,
ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str) ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
{ {
int reg; int reg;
TokenSym *ts;
#ifdef TCC_TARGET_X86_64 #ifdef TCC_TARGET_X86_64
unsigned int type; unsigned int type;
#endif #endif
@ -1707,8 +1706,7 @@ ST_FUNC void asm_clobber(uint8_t *clobber_regs, const char *str)
!strcmp(str, "cc") || !strcmp(str, "cc") ||
!strcmp(str, "flags")) !strcmp(str, "flags"))
return; return;
ts = tok_alloc(str, strlen(str)); reg = tok_alloc_const(str);
reg = ts->tok;
if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) { if (reg >= TOK_ASM_eax && reg <= TOK_ASM_edi) {
reg -= TOK_ASM_eax; reg -= TOK_ASM_eax;
} else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) { } else if (reg >= TOK_ASM_ax && reg <= TOK_ASM_di) {

View file

@ -423,26 +423,20 @@ ST_FUNC void gfunc_call(int nb_args)
size = (size + 3) & ~3; size = (size + 3) & ~3;
/* allocate the necessary size on stack */ /* allocate the necessary size on stack */
#ifdef TCC_TARGET_PE #ifdef TCC_TARGET_PE
if (size >= 0x4096) { if (size >= 4096) {
/* cannot call alloca with bound checking. Do stack probing. */ r = get_reg(RC_EAX);
o(0x50); // push %eax oad(0x68, size); // push size
oad(0xb8, size - 4); // mov size-4,%eax /* cannot call normal 'alloca' with bound checking */
oad(0x3d, 4096); // p1: cmp $4096,%eax gen_static_call(tok_alloc_const("__alloca"));
o(0x1476); // jbe <p2> gadd_sp(4);
oad(0x248485,-4096); // test %eax,-4096(%esp) } else
oad(0xec81, 4096); // sub $4096,%esp
oad(0x2d, 4096); // sub $4096,%eax
o(0xe5eb); // jmp <p1>
o(0xc429); // p2: sub %eax,%esp
oad(0xc481, size - 4); // add size-4,%esp
o(0x58); // pop %eax
}
#endif #endif
oad(0xec81, size); /* sub $xxx, %esp */ {
/* generate structure store */ oad(0xec81, size); /* sub $xxx, %esp */
r = get_reg(RC_INT); /* generate structure store */
o(0x89); /* mov %esp, r */ r = get_reg(RC_INT);
o(0xe0 + r); o(0xe089 + (r << 8)); /* mov %esp, r */
}
vset(&vtop->type, r | VT_LVAL, 0); vset(&vtop->type, r | VT_LVAL, 0);
vswap(); vswap();
vstore(); vstore();
@ -844,6 +838,12 @@ ST_FUNC void gen_opf(int op)
{ {
int a, ft, fc, swapped, r; int a, ft, fc, swapped, r;
if (op == TOK_NEG) { /* unary minus */
gv(RC_FLOAT);
o(0xe0d9); /* fchs */
return;
}
/* convert constants to memory references */ /* convert constants to memory references */
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
vswap(); vswap();

View file

@ -7,30 +7,30 @@
# define _(s) s # define _(s) s
#endif #endif
.globl _(alloca) .globl _(alloca), _(__alloca)
_(alloca): _(alloca):
pop %edx _(__alloca):
pop %eax push %ebp
mov %esp,%ebp
mov 8(%ebp),%eax
add $3,%eax add $3,%eax
and $-4,%eax and $-4,%eax
jz p3
#ifdef _WIN32 #ifdef _WIN32
jmp .+16 #p2
p1: p1:
cmp $4096,%eax
jbe p2
test %eax,-4096(%esp)
sub $4096,%esp sub $4096,%esp
sub $4096,%eax sub $4096,%eax
jmp p1 test %eax,(%esp)
p2: p2:
cmp $4096,%eax
jae p1
#endif #endif
sub %eax,%esp sub %eax,%esp
mov %esp,%eax mov 4(%ebp),%eax
p3: mov 0(%ebp),%ebp
push %edx add $8,%esp
push %edx push %eax
lea 8(%esp),%eax
ret ret
/* ---------------------------------------------- */ /* ---------------------------------------------- */

View file

@ -625,3 +625,9 @@ long long __fixxfdi (long double a1)
return s ? ret : -ret; return s ? ret : -ret;
} }
#endif /* !ARM */ #endif /* !ARM */
#if defined __x86_64__
/* float constants used for unary minus operation */
const float __mzerosf = -0.0;
const double __mzerodf = -0.0;
#endif

2
tcc.h
View file

@ -1090,6 +1090,7 @@ struct filespec {
#define TOK_SHL '<' /* shift left */ #define TOK_SHL '<' /* shift left */
#define TOK_SAR '>' /* signed shift right */ #define TOK_SAR '>' /* signed shift right */
#define TOK_SHR 0x8b /* unsigned shift right */ #define TOK_SHR 0x8b /* unsigned shift right */
#define TOK_NEG TOK_MID /* unary minus operation (for floats) */
#define TOK_ARROW 0xa0 /* -> */ #define TOK_ARROW 0xa0 /* -> */
#define TOK_DOTS 0xa1 /* three dots */ #define TOK_DOTS 0xa1 /* three dots */
@ -1378,6 +1379,7 @@ ST_DATA TokenSym **table_ident;
#define IS_NUM 4 #define IS_NUM 4
ST_FUNC TokenSym *tok_alloc(const char *str, int len); ST_FUNC TokenSym *tok_alloc(const char *str, int len);
ST_FUNC int tok_alloc_const(const char *str);
ST_FUNC const char *get_tok_str(int v, CValue *cv); ST_FUNC const char *get_tok_str(int v, CValue *cv);
ST_FUNC void begin_macro(TokenString *str, int alloc); ST_FUNC void begin_macro(TokenString *str, int alloc);
ST_FUNC void end_macro(void); ST_FUNC void end_macro(void);

View file

@ -27,11 +27,8 @@ static Section *last_text_section; /* to handle .previous asm directive */
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]; char buf[64];
TokenSym *ts;
snprintf(buf, sizeof(buf), "L..%u", n); snprintf(buf, sizeof(buf), "L..%u", n);
ts = tok_alloc(buf, strlen(buf)); return tok_alloc_const(buf);
return ts->tok;
} }
static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global); static int tcc_assemble_internal(TCCState *s1, int do_preprocess, int global);
@ -54,12 +51,11 @@ static int asm2cname(int v, int *addeddot)
if (!name) if (!name)
return v; return v;
if (name[0] == '_') { if (name[0] == '_') {
v = tok_alloc(name + 1, strlen(name) - 1)->tok; v = tok_alloc_const(name + 1);
} else if (!strchr(name, '.')) { } else if (!strchr(name, '.')) {
int n = strlen(name) + 2;
char newname[256]; char newname[256];
snprintf(newname, sizeof newname, ".%s", name); snprintf(newname, sizeof newname, ".%s", name);
v = tok_alloc(newname, n - 1)->tok; v = tok_alloc_const(newname);
*addeddot = 1; *addeddot = 1;
} }
return v; return v;
@ -111,11 +107,10 @@ ST_FUNC Sym* get_asm_sym(int name, Sym *csym)
static Sym* asm_section_sym(TCCState *s1, Section *sec) static Sym* asm_section_sym(TCCState *s1, Section *sec)
{ {
char buf[100]; char buf[100]; int label; Sym *sym;
int label = tok_alloc(buf, snprintf(buf, sizeof buf, "L.%s", sec->name);
snprintf(buf, sizeof buf, "L.%s", sec->name) label = tok_alloc_const(buf);
)->tok; sym = asm_label_find(label);
Sym *sym = asm_label_find(label);
return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0); return sym ? sym : asm_new_label1(s1, label, 1, sec->sh_num, 0);
} }

View file

@ -2511,9 +2511,12 @@ static int elf_output_file(TCCState *s1, const char *filename)
{ {
int i, ret, phnum, phfill, shnum, file_type, file_offset, *sec_order; int i, ret, phnum, phfill, shnum, file_type, file_offset, *sec_order;
struct dyn_inf dyninf = {0}; struct dyn_inf dyninf = {0};
struct ro_inf roinf, *roinf_use = &roinf; struct ro_inf roinf;
ElfW(Phdr) *phdr; ElfW(Phdr) *phdr;
Section *strsec, *interp, *dynamic, *dynstr, *note = NULL; Section *strsec, *interp, *dynamic, *dynstr, *note = NULL;
#ifndef ELF_OBJ_ONLY
struct ro_inf *roinf_use = NULL;
#endif
file_type = s1->output_type; file_type = s1->output_type;
@ -2648,10 +2651,8 @@ static int elf_output_file(TCCState *s1, const char *filename)
#if !TARGETOS_FreeBSD && !TARGETOS_NetBSD && !defined(__APPLE__) && !defined(_WIN32) #if !TARGETOS_FreeBSD && !TARGETOS_NetBSD && !defined(__APPLE__) && !defined(_WIN32)
/* GNU_RELRO */ /* GNU_RELRO */
if (file_type != TCC_OUTPUT_OBJ) if (file_type != TCC_OUTPUT_OBJ)
phnum++; phnum++, roinf_use = &roinf;
else
#endif #endif
roinf_use = NULL;
/* allocate program segment headers */ /* allocate program segment headers */
phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr))); phdr = tcc_mallocz(phnum * sizeof(ElfW(Phdr)));

View file

@ -2790,6 +2790,33 @@ static void gen_opic(int op)
} }
} }
#if defined TCC_TARGET_X86_64 || defined TCC_TARGET_I386
# define gen_negf gen_opf
#else
/* XXX: implement in gen_opf() for other backends too */
void gen_negf(int op)
{
/* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's
subtract(-0, x), but with them it's really a sign flip
operation. We implement this with bit manipulation and have
to do some type reinterpretation for this, which TCC can do
only via memory. */
int align, size, bt;
size = type_size(&vtop->type, &align);
bt = vtop->type.t & VT_BTYPE;
save_reg(gv(RC_TYPE(bt)));
vdup();
incr_bf_adr(size - 1);
vdup();
vpushi(0x80); /* flip sign */
gen_op('^');
vstore();
vpop();
}
#endif
/* generate a floating point operation with constant propagation */ /* generate a floating point operation with constant propagation */
static void gen_opif(int op) static void gen_opif(int op)
{ {
@ -2803,6 +2830,9 @@ static void gen_opif(int op)
v1 = vtop - 1; v1 = vtop - 1;
v2 = vtop; v2 = vtop;
if (op == TOK_NEG)
v1 = v2;
/* currently, we cannot do computations with forward symbols */ /* currently, we cannot do computations with forward symbols */
c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; c1 = (v1->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST; c2 = (v2->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST;
@ -2817,29 +2847,43 @@ static void gen_opif(int op)
f1 = v1->c.ld; f1 = v1->c.ld;
f2 = v2->c.ld; f2 = v2->c.ld;
} }
/* NOTE: we only do constant propagation if finite number (not /* NOTE: we only do constant propagation if finite number (not
NaN or infinity) (ANSI spec) */ NaN or infinity) (ANSI spec) */
if (!ieee_finite(f1) || !ieee_finite(f2)) if (!(ieee_finite(f1) || !ieee_finite(f2)) && !const_wanted)
goto general_case; goto general_case;
switch(op) { switch(op) {
case '+': f1 += f2; break; case '+': f1 += f2; break;
case '-': f1 -= f2; break; case '-': f1 -= f2; break;
case '*': f1 *= f2; break; case '*': f1 *= f2; break;
case '/': case '/':
if (f2 == 0.0) { if (f2 == 0.0) {
union { float f; unsigned u; } x1, x2, y;
/* If not in initializer we need to potentially generate /* If not in initializer we need to potentially generate
FP exceptions at runtime, otherwise we want to fold. */ FP exceptions at runtime, otherwise we want to fold. */
if (!const_wanted) if (!const_wanted)
goto general_case; goto general_case;
/* the run-time result of 0.0/0.0 on x87, also of other compilers
when used to compile the f1 /= f2 below, would be -nan */
x1.f = f1, x2.f = f2;
if (f1 == 0.0)
y.u = 0x7fc00000; /* nan */
else
y.u = 0x7f800000; /* infinity */
y.u |= (x1.u ^ x2.u) & 0x80000000; /* set sign */
f1 = y.f;
break;
} }
f1 /= f2; f1 /= f2;
break; break;
case TOK_NEG:
f1 = -f1;
goto unary_result;
/* XXX: also handles tests ? */ /* XXX: also handles tests ? */
default: default:
goto general_case; goto general_case;
} }
vtop--;
unary_result:
/* XXX: overflow test ? */ /* XXX: overflow test ? */
if (v1->type.t == VT_FLOAT) { if (v1->type.t == VT_FLOAT) {
v1->c.f = f1; v1->c.f = f1;
@ -2848,10 +2892,13 @@ static void gen_opif(int op)
} else { } else {
v1->c.ld = f1; v1->c.ld = f1;
} }
vtop--;
} else { } else {
general_case: general_case:
gen_opf(op); if (op == TOK_NEG) {
gen_negf(op);
} else {
gen_opf(op);
}
} }
} }
@ -5878,44 +5925,8 @@ ST_FUNC void unary(void)
case '-': case '-':
next(); next();
unary(); unary();
t = vtop->type.t & VT_BTYPE; if (is_float(vtop->type.t)) {
if (is_float(t)) { gen_opif(TOK_NEG);
if ((vtop->r & VT_VALMASK) == VT_CONST) {
/* This is what gen_opif would do if we had a NEG operation. */
if (t == VT_FLOAT)
vtop->c.f = -vtop->c.f;
else if (t == VT_DOUBLE)
vtop->c.d = -vtop->c.d;
else
vtop->c.ld = -vtop->c.ld;
} else {
/* In IEEE negate(x) isn't subtract(0,x). Without NaNs it's
subtract(-0, x), but with them it's really a sign flip
operation. We implement this with bit manipulation and have
to do some type reinterpretation for this, which TCC can do
only via memory. */
int align, size = type_size(&vtop->type, &align);
save_reg(gv(RC_TYPE(t)));
vdup();
gaddrof();
vtop->type = char_pointer_type;
/* Byte of sign bit. For big endian, this would have to
add zero always. */
#if defined(TCC_TARGET_X86_64) || defined(TCC_TARGET_I386)
/* sizeof long double is 12 or 16 here, but it's
really the 80bit extended float format. */
if (t == VT_LDOUBLE)
size = 10;
#endif
vpushi(size - 1);
gen_op('+');
indir();
vdup();
vpushi(0x80); /* flip sign */
gen_op('^');
vstore();
vpop();
}
} else { } else {
vpushi(0); vpushi(0);
vswap(); vswap();

View file

@ -487,6 +487,12 @@ ST_FUNC TokenSym *tok_alloc(const char *str, int len)
return tok_alloc_new(pts, str, len); return tok_alloc_new(pts, str, len);
} }
ST_FUNC int tok_alloc_const(const char *str)
{
return tok_alloc(str, strlen(str))->tok;
}
/* XXX: buffer overflow */ /* XXX: buffer overflow */
/* XXX: float tokens */ /* XXX: float tokens */
ST_FUNC const char *get_tok_str(int v, CValue *cv) ST_FUNC const char *get_tok_str(int v, CValue *cv)

View file

@ -100,6 +100,8 @@
DEF(TOK___NAN__, "__nan__") DEF(TOK___NAN__, "__nan__")
DEF(TOK___SNAN__, "__snan__") DEF(TOK___SNAN__, "__snan__")
DEF(TOK___INF__, "__inf__") DEF(TOK___INF__, "__inf__")
DEF(TOK___mzerosf, "__mzerosf")
DEF(TOK___mzerodf, "__mzerodf")
/* attribute identifiers */ /* attribute identifiers */
/* XXX: handle all tokens generically since speed is not critical */ /* XXX: handle all tokens generically since speed is not critical */

View file

@ -1813,14 +1813,38 @@ void gen_opl(int op)
gen_opi(op); gen_opi(op);
} }
void vpush_const(int t, int v)
{
CType ctype = { t | VT_CONSTANT, 0 };
vpushsym(&ctype, external_global_sym(v, &ctype));
vtop->r |= VT_LVAL;
}
/* generate a floating point operation 'v = t1 op t2' instruction. The /* generate a floating point operation 'v = t1 op t2' instruction. The
two operands are guaranteed to have the same floating point type */ two operands are guaranteed to have the same floating point type */
/* XXX: need to use ST1 too */ /* XXX: need to use ST1 too */
void gen_opf(int op) void gen_opf(int op)
{ {
int a, ft, fc, swapped, r; int a, ft, fc, swapped, r;
int float_type = int bt = vtop->type.t & VT_BTYPE;
(vtop->type.t & VT_BTYPE) == VT_LDOUBLE ? RC_ST0 : RC_FLOAT; int float_type = bt == VT_LDOUBLE ? RC_ST0 : RC_FLOAT;
if (op == TOK_NEG) { /* unary minus */
gv(float_type);
if (float_type == RC_ST0) {
o(0xe0d9); /* fchs */
} else {
/* -0.0, in libtcc1.c */
vpush_const(bt, bt == VT_FLOAT ? TOK___mzerosf : TOK___mzerodf);
gv(RC_FLOAT);
if (bt == VT_DOUBLE)
o(0x66);
/* xorp[sd] %xmm1, %xmm0 */
o(0xc0570f | (REG_VALUE(vtop[0].r) + REG_VALUE(vtop[-1].r)*8) << 16);
vtop--;
}
return;
}
/* convert constants to memory references */ /* convert constants to memory references */
if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) { if ((vtop[-1].r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {