Alien-TinyCC
view release on metacpan or search on metacpan
src/tccgen.c view on Meta::CPAN
vtop->c.tab[3] = 0;
#endif
}
#endif
ptr = section_ptr_add(data_section, size);
size = size >> 2;
#if defined(TCC_TARGET_ARM) && !defined(TCC_ARM_VFP)
check.d = 1;
if(check.tab[0])
for(i=0;i<size;i++)
ptr[i] = vtop->c.tab[size-1-i];
else
#endif
for(i=0;i<size;i++)
ptr[i] = vtop->c.tab[i];
sym = get_sym_ref(&vtop->type, data_section, offset, size << 2);
vtop->r |= VT_LVAL | VT_SYM;
vtop->sym = sym;
vtop->c.ul = 0;
}
#ifdef CONFIG_TCC_BCHECK
if (vtop->r & VT_MUSTBOUND)
gbound();
#endif
r = vtop->r & VT_VALMASK;
rc2 = (rc & RC_FLOAT) ? RC_FLOAT : RC_INT;
if (rc == RC_IRET)
rc2 = RC_LRET;
#ifdef TCC_TARGET_X86_64
else if (rc == RC_FRET)
rc2 = RC_QRET;
#endif
/* need to reload if:
- constant
- lvalue (need to dereference pointer)
- already a register, but not in the right class */
if (r >= VT_CONST
|| (vtop->r & VT_LVAL)
|| !(reg_classes[r] & rc)
#ifdef TCC_TARGET_X86_64
|| ((vtop->type.t & VT_BTYPE) == VT_QLONG && !(reg_classes[vtop->r2] & rc2))
|| ((vtop->type.t & VT_BTYPE) == VT_QFLOAT && !(reg_classes[vtop->r2] & rc2))
#else
|| ((vtop->type.t & VT_BTYPE) == VT_LLONG && !(reg_classes[vtop->r2] & rc2))
#endif
)
{
r = get_reg(rc);
#ifdef TCC_TARGET_X86_64
if (((vtop->type.t & VT_BTYPE) == VT_QLONG) || ((vtop->type.t & VT_BTYPE) == VT_QFLOAT)) {
int addr_type = VT_LLONG, load_size = 8, load_type = ((vtop->type.t & VT_BTYPE) == VT_QLONG) ? VT_LLONG : VT_DOUBLE;
#else
if ((vtop->type.t & VT_BTYPE) == VT_LLONG) {
int addr_type = VT_INT, load_size = 4, load_type = VT_INT;
unsigned long long ll;
#endif
int r2, original_type;
original_type = vtop->type.t;
/* two register type load : expand to two words
temporarily */
#ifndef TCC_TARGET_X86_64
if ((vtop->r & (VT_VALMASK | VT_LVAL)) == VT_CONST) {
/* load constant */
ll = vtop->c.ull;
vtop->c.ui = ll; /* first word */
load(r, vtop);
vtop->r = r; /* save register value */
vpushi(ll >> 32); /* second word */
} else
#endif
if (r >= VT_CONST || /* XXX: test to VT_CONST incorrect ? */
(vtop->r & VT_LVAL)) {
/* We do not want to modifier the long long
pointer here, so the safest (and less
efficient) is to save all the other registers
in the stack. XXX: totally inefficient. */
save_regs(1);
/* load from memory */
vtop->type.t = load_type;
load(r, vtop);
vdup();
vtop[-1].r = r; /* save register value */
/* increment pointer to get second word */
vtop->type.t = addr_type;
gaddrof();
vpushi(load_size);
gen_op('+');
vtop->r |= VT_LVAL;
vtop->type.t = load_type;
} else {
/* move registers */
load(r, vtop);
vdup();
vtop[-1].r = r; /* save register value */
vtop->r = vtop[-1].r2;
}
/* Allocate second register. Here we rely on the fact that
get_reg() tries first to free r2 of an SValue. */
r2 = get_reg(rc2);
load(r2, vtop);
vpop();
/* write second register */
vtop->r2 = r2;
vtop->type.t = original_type;
} else if ((vtop->r & VT_LVAL) && !is_float(vtop->type.t)) {
int t1, t;
/* lvalue of scalar type : need to use lvalue type
because of possible cast */
t = vtop->type.t;
t1 = t;
/* compute memory access type */
if (vtop->r & VT_REF)
#ifdef TCC_TARGET_X86_64
t = VT_PTR;
#else
t = VT_INT;
#endif
else if (vtop->r & VT_LVAL_BYTE)
t = VT_BYTE;
src/tccgen.c view on Meta::CPAN
vtop->r2 = r+1;
#endif
}
return r;
}
/* generate vtop[-1] and vtop[0] in resp. classes rc1 and rc2 */
ST_FUNC void gv2(int rc1, int rc2)
{
int v;
/* generate more generic register first. But VT_JMP or VT_CMP
values must be generated first in all cases to avoid possible
reload errors */
v = vtop[0].r & VT_VALMASK;
if (v != VT_CMP && (v & ~1) != VT_JMP && rc1 <= rc2) {
vswap();
gv(rc1);
vswap();
gv(rc2);
/* test if reload is needed for first register */
if ((vtop[-1].r & VT_VALMASK) >= VT_CONST) {
vswap();
gv(rc1);
vswap();
}
} else {
gv(rc2);
vswap();
gv(rc1);
vswap();
/* test if reload is needed for first register */
if ((vtop[0].r & VT_VALMASK) >= VT_CONST) {
gv(rc2);
}
}
}
/* wrapper around RC_FRET to return a register by type */
static int rc_fret(int t)
{
#ifdef TCC_TARGET_X86_64
if (t == VT_LDOUBLE) {
return RC_ST0;
}
#endif
return RC_FRET;
}
/* wrapper around REG_FRET to return a register by type */
static int reg_fret(int t)
{
#ifdef TCC_TARGET_X86_64
if (t == VT_LDOUBLE) {
return TREG_ST0;
}
#endif
return REG_FRET;
}
/* expand long long on stack in two int registers */
static void lexpand(void)
{
int u;
u = vtop->type.t & VT_UNSIGNED;
gv(RC_INT);
vdup();
vtop[0].r = vtop[-1].r2;
vtop[0].r2 = VT_CONST;
vtop[-1].r2 = VT_CONST;
vtop[0].type.t = VT_INT | u;
vtop[-1].type.t = VT_INT | u;
}
#ifdef TCC_TARGET_ARM
/* expand long long on stack */
ST_FUNC void lexpand_nr(void)
{
int u,v;
u = vtop->type.t & VT_UNSIGNED;
vdup();
vtop->r2 = VT_CONST;
vtop->type.t = VT_INT | u;
v=vtop[-1].r & (VT_VALMASK | VT_LVAL);
if (v == VT_CONST) {
vtop[-1].c.ui = vtop->c.ull;
vtop->c.ui = vtop->c.ull >> 32;
vtop->r = VT_CONST;
} else if (v == (VT_LVAL|VT_CONST) || v == (VT_LVAL|VT_LOCAL)) {
vtop->c.ui += 4;
vtop->r = vtop[-1].r;
} else if (v > VT_CONST) {
vtop--;
lexpand();
} else
vtop->r = vtop[-1].r2;
vtop[-1].r2 = VT_CONST;
vtop[-1].type.t = VT_INT | u;
}
#endif
/* build a long long from two ints */
static void lbuild(int t)
{
gv2(RC_INT, RC_INT);
vtop[-1].r2 = vtop[0].r;
vtop[-1].type.t = t;
vpop();
}
/* rotate n first stack elements to the bottom
I1 ... In -> I2 ... In I1 [top is right]
*/
ST_FUNC void vrotb(int n)
{
int i;
SValue tmp;
tmp = vtop[-n + 1];
for(i=-n+1;i!=0;i++)
vtop[i] = vtop[i+1];
vtop[0] = tmp;
}
/* rotate the n elements before entry e towards the top
I1 ... In ... -> In I1 ... I(n-1) ... [top is right]
*/
ST_FUNC void vrote(SValue *e, int n)
{
int i;
SValue tmp;
tmp = *e;
for(i = 0;i < n - 1; i++)
e[-i] = e[-i - 1];
e[-n + 1] = tmp;
}
/* rotate n first stack elements to the top
I1 ... In -> In I1 ... I(n-1) [top is right]
*/
ST_FUNC void vrott(int n)
{
vrote(vtop, n);
}
/* pop stack value */
ST_FUNC void vpop(void)
{
int v;
v = vtop->r & VT_VALMASK;
#if defined(TCC_TARGET_I386) || defined(TCC_TARGET_X86_64)
/* for x86, we need to pop the FP stack */
if (v == TREG_ST0 && !nocode_wanted) {
o(0xd8dd); /* fstp %st(0) */
} else
#endif
if (v == VT_JMP || v == VT_JMPI) {
/* need to put correct jump if && or || without test */
gsym(vtop->c.ul);
}
vtop--;
}
/* convert stack entry to register and duplicate its value in another
register */
static void gv_dup(void)
{
int rc, t, r, r1;
SValue sv;
t = vtop->type.t;
if ((t & VT_BTYPE) == VT_LLONG) {
lexpand();
gv_dup();
vswap();
vrotb(3);
gv_dup();
vrotb(4);
/* stack: H L L1 H1 */
lbuild(t);
vrotb(3);
vrotb(3);
vswap();
lbuild(t);
vswap();
} else {
/* duplicate value */
rc = RC_INT;
sv.type.t = VT_INT;
if (is_float(t)) {
rc = RC_FLOAT;
#ifdef TCC_TARGET_X86_64
if ((t & VT_BTYPE) == VT_LDOUBLE) {
rc = RC_ST0;
}
#endif
sv.type.t = t;
}
r = gv(rc);
r1 = get_reg(rc);
sv.r = r;
sv.c.ul = 0;
load(r1, &sv); /* move r to r1 */
vdup();
/* duplicates value */
if (r != r1)
vtop->r = r1;
}
}
#ifndef TCC_TARGET_X86_64
/* generate CPU independent (unsigned) long long operations */
static void gen_opl(int op)
{
int t, a, b, op1, c, i;
int func;
unsigned short reg_iret = REG_IRET;
unsigned short reg_lret = REG_LRET;
SValue tmp;
switch(op) {
case '/':
case TOK_PDIV:
func = TOK___divdi3;
goto gen_func;
case TOK_UDIV:
func = TOK___udivdi3;
goto gen_func;
case '%':
func = TOK___moddi3;
goto gen_mod_func;
case TOK_UMOD:
func = TOK___umoddi3;
gen_mod_func:
#ifdef TCC_ARM_EABI
reg_iret = TREG_R2;
reg_lret = TREG_R3;
#endif
gen_func:
/* call generic long long function */
vpush_global_sym(&func_old_type, func);
vrott(3);
gfunc_call(2);
vpushi(0);
vtop->r = reg_iret;
vtop->r2 = reg_lret;
break;
case '^':
case '&':
case '|':
case '*':
case '+':
case '-':
t = vtop->type.t;
vswap();
lexpand();
vrotb(3);
lexpand();
/* stack: L1 H1 L2 H2 */
tmp = vtop[0];
vtop[0] = vtop[-3];
vtop[-3] = tmp;
tmp = vtop[-2];
vtop[-2] = vtop[-3];
vtop[-3] = tmp;
vswap();
/* stack: H1 H2 L1 L2 */
if (op == '*') {
vpushv(vtop - 1);
vpushv(vtop - 1);
gen_op(TOK_UMULL);
lexpand();
/* stack: H1 H2 L1 L2 ML MH */
for(i=0;i<4;i++)
vrotb(6);
/* stack: ML MH H1 H2 L1 L2 */
tmp = vtop[0];
vtop[0] = vtop[-2];
vtop[-2] = tmp;
/* stack: ML MH H1 L2 H2 L1 */
gen_op('*');
vrotb(3);
vrotb(3);
gen_op('*');
/* stack: ML MH M1 M2 */
gen_op('+');
gen_op('+');
} else if (op == '+' || op == '-') {
/* XXX: add non carry method too (for MIPS or alpha) */
if (op == '+')
op1 = TOK_ADDC1;
else
op1 = TOK_SUBC1;
gen_op(op1);
/* stack: H1 H2 (L1 op L2) */
vrotb(3);
vrotb(3);
gen_op(op1 + 1); /* TOK_xxxC2 */
} else {
gen_op(op);
/* stack: H1 H2 (L1 op L2) */
vrotb(3);
vrotb(3);
/* stack: (L1 op L2) H1 H2 */
gen_op(op);
/* stack: (L1 op L2) (H1 op H2) */
}
/* stack: L H */
lbuild(t);
break;
case TOK_SAR:
case TOK_SHR:
case TOK_SHL:
if ((vtop->r & (VT_VALMASK | VT_LVAL | VT_SYM)) == VT_CONST) {
t = vtop[-1].type.t;
vswap();
lexpand();
vrotb(3);
/* stack: L H shift */
c = (int)vtop->c.i;
/* constant: simpler */
/* NOTE: all comments are for SHL. the other cases are
done by swaping words */
vpop();
if (op != TOK_SHL)
vswap();
if (c >= 32) {
/* stack: L H */
vpop();
if (c > 32) {
vpushi(c - 32);
gen_op(op);
}
if (op != TOK_SAR) {
vpushi(0);
} else {
gv_dup();
vpushi(31);
gen_op(TOK_SAR);
}
vswap();
} else {
vswap();
gv_dup();
/* stack: H L L */
vpushi(c);
gen_op(op);
vswap();
vpushi(32 - c);
if (op == TOK_SHL)
gen_op(TOK_SHR);
else
gen_op(TOK_SHL);
vrotb(3);
/* stack: L L H */
vpushi(c);
if (op == TOK_SHL)
gen_op(TOK_SHL);
else
gen_op(TOK_SHR);
gen_op('|');
}
if (op != TOK_SHL)
vswap();
lbuild(t);
} else {
/* XXX: should provide a faster fallback on x86 ? */
switch(op) {
case TOK_SAR:
func = TOK___ashrdi3;
goto gen_func;
case TOK_SHR:
func = TOK___lshrdi3;
goto gen_func;
case TOK_SHL:
func = TOK___ashldi3;
goto gen_func;
}
}
break;
default:
/* compare operations */
t = vtop->type.t;
vswap();
lexpand();
vrotb(3);
lexpand();
/* stack: L1 H1 L2 H2 */
tmp = vtop[-1];
vtop[-1] = vtop[-2];
vtop[-2] = tmp;
/* stack: L1 L2 H1 H2 */
/* compare high */
op1 = op;
/* when values are equal, we need to compare low words. since
the jump is inverted, we invert the test too. */
if (op1 == TOK_LT)
op1 = TOK_LE;
else if (op1 == TOK_GT)
op1 = TOK_GE;
else if (op1 == TOK_ULT)
op1 = TOK_ULE;
else if (op1 == TOK_UGT)
op1 = TOK_UGE;
a = 0;
b = 0;
gen_op(op1);
if (op1 != TOK_NE) {
a = gtst(1, 0);
}
if (op != TOK_EQ) {
/* generate non equal test */
/* XXX: NOT PORTABLE yet */
if (a == 0) {
b = gtst(0, 0);
} else {
#if defined(TCC_TARGET_I386)
b = psym(0x850f, 0);
#elif defined(TCC_TARGET_ARM)
b = ind;
o(0x1A000000 | encbranch(ind, 0, 1));
#elif defined(TCC_TARGET_C67)
tcc_error("not implemented");
#else
#error not supported
#endif
}
}
/* compare low. Always unsigned */
op1 = op;
if (op1 == TOK_LT)
op1 = TOK_ULT;
else if (op1 == TOK_LE)
op1 = TOK_ULE;
else if (op1 == TOK_GT)
op1 = TOK_UGT;
else if (op1 == TOK_GE)
op1 = TOK_UGE;
gen_op(op1);
a = gtst(1, a);
gsym(b);
vseti(VT_JMPI, a);
break;
}
}
#endif
src/tccgen.c view on Meta::CPAN
/* additional cast for char/short... */
vtop->type.t = dbt;
gen_cast(type);
}
}
#ifndef TCC_TARGET_X86_64
} else if ((dbt & VT_BTYPE) == VT_LLONG) {
if ((sbt & VT_BTYPE) != VT_LLONG) {
/* scalar to long long */
/* machine independent conversion */
gv(RC_INT);
/* generate high word */
if (sbt == (VT_INT | VT_UNSIGNED)) {
vpushi(0);
gv(RC_INT);
} else {
if (sbt == VT_PTR) {
/* cast from pointer to int before we apply
shift operation, which pointers don't support*/
gen_cast(&int_type);
}
gv_dup();
vpushi(31);
gen_op(TOK_SAR);
}
/* patch second register */
vtop[-1].r2 = vtop->r;
vpop();
}
#else
} else if ((dbt & VT_BTYPE) == VT_LLONG ||
(dbt & VT_BTYPE) == VT_PTR ||
(dbt & VT_BTYPE) == VT_FUNC) {
if ((sbt & VT_BTYPE) != VT_LLONG &&
(sbt & VT_BTYPE) != VT_PTR &&
(sbt & VT_BTYPE) != VT_FUNC) {
/* need to convert from 32bit to 64bit */
int r = gv(RC_INT);
if (sbt != (VT_INT | VT_UNSIGNED)) {
/* x86_64 specific: movslq */
o(0x6348);
o(0xc0 + (REG_VALUE(r) << 3) + REG_VALUE(r));
}
}
#endif
} else if (dbt == VT_BOOL) {
/* scalar to bool */
vpushi(0);
gen_op(TOK_NE);
} else if ((dbt & VT_BTYPE) == VT_BYTE ||
(dbt & VT_BTYPE) == VT_SHORT) {
if (sbt == VT_PTR) {
vtop->type.t = VT_INT;
tcc_warning("nonportable conversion from pointer to char/short");
}
force_charshort_cast(dbt);
} else if ((dbt & VT_BTYPE) == VT_INT) {
/* scalar to int */
if (sbt == VT_LLONG) {
/* from long long: just take low order word */
lexpand();
vpop();
}
/* if lvalue and single word type, nothing to do because
the lvalue already contains the real type size (see
VT_LVAL_xxx constants) */
}
}
} else if ((dbt & VT_BTYPE) == VT_PTR && !(vtop->r & VT_LVAL)) {
/* if we are casting between pointer types,
we must update the VT_LVAL_xxx size */
vtop->r = (vtop->r & ~VT_LVAL_TYPE)
| (lvalue_type(type->ref->type.t) & VT_LVAL_TYPE);
}
vtop->type = *type;
}
/* return type size as known at compile time. Put alignment at 'a' */
ST_FUNC int type_size(CType *type, int *a)
{
Sym *s;
int bt;
bt = type->t & VT_BTYPE;
if (bt == VT_STRUCT) {
/* struct/union */
s = type->ref;
*a = s->r;
return s->c;
} else if (bt == VT_PTR) {
if (type->t & VT_ARRAY) {
int ts;
s = type->ref;
ts = type_size(&s->type, a);
if (ts < 0 && s->c < 0)
ts = -ts;
return ts * s->c;
} else {
*a = PTR_SIZE;
return PTR_SIZE;
}
} else if (bt == VT_LDOUBLE) {
*a = LDOUBLE_ALIGN;
return LDOUBLE_SIZE;
} else if (bt == VT_DOUBLE || bt == VT_LLONG) {
#ifdef TCC_TARGET_I386
#ifdef TCC_TARGET_PE
*a = 8;
#else
*a = 4;
#endif
#elif defined(TCC_TARGET_ARM)
#ifdef TCC_ARM_EABI
*a = 8;
#else
*a = 4;
#endif
#else
( run in 0.815 second using v1.01-cache-2.11-cpan-5623c5533a1 )