/* $Id: code.c,v 1.8 2007/11/22 16:52:47 gmcgarry Exp $ */
/*
* Copyright (c) 2003 Anders Magnusson (ragge@ludd.luth.se).
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <assert.h>
#include <stdlib.h>
#include "pass1.h"
#include "pass2.h"
#if 0
static void genswitch_simple(int num, struct swents **p, int n);
static void genswitch_bintree(int num, struct swents **p, int n);
static void genswitch_table(int num, struct swents **p, int n);
static void genswitch_mrst(int num, struct swents **p, int n);
#endif
/*
* cause the alignment to become a multiple of n
* never called for text segment.
*/
void
defalign(int n)
{
if ((n = ispow2(n / SZCHAR)) == -1)
cerror("defalign: n != 2^i");
printf(" .p2align %d\n", n);
}
/*
* define the current location as the name p->sname
* never called for text segment.
*/
void
defnam(struct symtab *p)
{
char *c = p->sname;
#ifdef GCC_COMPAT
c = gcc_findname(p);
#endif
if (p->sclass == EXTDEF)
printf(" .globl %s\n", exname(c));
printf("%s:\n", exname(c));
}
/*
* code for the end of a function
* deals with struct return here
*/
void
efcode()
{
#if 0
NODE *p, *q;
int sz;
#endif
#if 0
printf("EFCODE:\n");
#endif
if (cftnsp->stype != STRTY+FTN && cftnsp->stype != UNIONTY+FTN)
return;
assert(0);
#if 0
/* address of return struct is in eax */
/* create a call to memcpy() */
/* will get the result in eax */
p = block(REG, NIL, NIL, CHAR+PTR, 0, MKSUE(CHAR+PTR));
p->n_rval = EAX;
q = block(OREG, NIL, NIL, CHAR+PTR, 0, MKSUE(CHAR+PTR));
q->n_rval = EBP;
q->n_lval = 8; /* return buffer offset */
p = block(CM, q, p, INT, 0, MKSUE(INT));
sz = (tsize(STRTY, cftnsp->sdf, cftnsp->ssue)+SZCHAR-1)/SZCHAR;
p = block(CM, p, bcon(sz), INT, 0, MKSUE(INT));
p->n_right->n_name = "";
p = block(CALL, bcon(0), p, CHAR+PTR, 0, MKSUE(CHAR+PTR));
p->n_left->n_name = "memcpy";
p = clocal(p);
send_passt(IP_NODE, p);
#endif
}
/*
* code for the beginning of a function; a is an array of
* indices in symtab for the arguments; n is the number
*/
void
bfcode(struct symtab **a, int n)
{
int i, m;
#if 0
printf("BFCODE start with %d arguments\n", n);
#endif
if (cftnsp->stype == STRTY+FTN && cftnsp->stype == UNIONTY+FTN) {
/* Function returns struct, adjust arg offset */
for (i = 0; i < n; i++)
a[i]->soffset += SZPOINT(INT);
}
m = n <= 8 ? n : 8;
#if 0
/* if optimised, assign parameters to registers */
/* XXX consider the size of the types */
for (i=0; i < m; i++) {
a[i]->hdr.h_sclass = REGISTER;
a[i]->hdr.h_offset = R3 + i;
}
#endif
/* if not optimised, */
/* save the register arguments (R3-R10) onto the stack */
int passedargoff = ARGINIT + FIXEDSTACKSIZE*8; // XXX must add the size of the stack frame
int reg = R3;
for (i=0; i < m; i++) {
NODE *r, *p;
a[i]->hdr.h_sclass = PARAM;
a[i]->hdr.h_offset = NOOFFSET;
oalloc(a[i], &passedargoff);
spname = a[i];
p = buildtree(NAME, NIL, NIL);
r = bcon(0);
r->n_op = REG;
if (BTYPE(p->n_type) == LONGLONG || BTYPE(p->n_type) == ULONGLONG) {
r->n_rval = R3R4+(reg-R3);
reg += 2;
} else {
r->n_rval = reg++;
}
r->n_type = p->n_type;
r->n_sue = p->n_sue;
r->n_df = p->n_df;
ecode(buildtree(ASSIGN, p, r));
}
#if 1
/* XXXHACK save the rest of the registers too, for varargs */
for (; reg < R11; reg++) {
NODE *r, *l;
l = bcon(0);
l->n_op = OREG;
l->n_lval = (passedargoff/SZCHAR);
l->n_rval = FPREG;
l->n_type = INT;
passedargoff += SZINT;
r = bcon(0);
r->n_op = REG;
r->n_rval = reg;
r->n_type = INT;
ecode(buildtree(ASSIGN, l, r));
}
#endif
#if 0
printf("BFCODE end\n");
#endif
}
/*
* by now, the automatics and register variables are allocated
*/
void
bccode()
{
#if 0
printf("BCCODE: autooff=%d, SZINT=%d\n", autooff, SZINT);
#endif
SETOFF(autooff, SZINT);
}
struct stub stublist;
struct stub nlplist;
/* called just before final exit */
/* flag is 1 if errors, 0 if none */
void
ejobcode(int flag )
{
#if 0
printf("EJOBCODE:\n");
#endif
if (kflag) {
// iterate over the stublist and output the PIC stubs
struct stub *p;
DLIST_FOREACH(p, &stublist, link) {
printf("\t.section __TEXT, __picsymbolstub1,symbol_stubs,pure_instructions,32\n");
printf("\t.align 5\n");
printf("%s$stub:\n", p->name);
printf("\t.indirect_symbol %s\n", p->name);
printf("\tmflr r0\n");
printf("\tbcl 20,31,L%s$stub$spb\n", p->name);
printf("L%s$stub$spb:\n", p->name);
printf("\tmflr r11\n");
printf("\taddis r11,r11,ha16(L%s$lazy_ptr-L%s$stub$spb)\n", p->name, p->name);
printf("\tmtlr r0\n");
printf("\tlwzu r12,lo16(L%s$lazy_ptr-L%s$stub$spb)(r11)\n", p->name, p->name);
printf("\tmtctr r12\n");
printf("\tbctr\n");
printf("\t.lazy_symbol_pointer\n");
printf("L%s$lazy_ptr:\n", p->name);
printf("\t.indirect_symbol %s\n", p->name);
printf("\t.long dyld_stub_binding_helper\n");
printf("\t.subsections_via_symbols\n");
}
printf("\t.non_lazy_symbol_pointer\n");
DLIST_FOREACH(p, &nlplist, link) {
printf("L%s$non_lazy_ptr:\n", p->name);
printf("\t.indirect_symbol %s\n", p->name);
printf("\t.long 0\n");
}
// memory leak here
}
}
void
bjobcode()
{
#if 0
printf("BJOBCODE:\n");
#endif
DLIST_INIT(&stublist, link);
DLIST_INIT(&nlplist, link);
}
/*
* Print character t at position i in one string, until t == -1.
* Locctr & label is already defined.
*/
void
bycode(int t, int i)
{
static int lastoctal = 0;
/* put byte i+1 in a string */
if (t < 0) {
if (i != 0)
puts("\"");
} else {
if (i == 0)
printf("\t.ascii \"");
if (t == '\\' || t == '"') {
lastoctal = 0;
putchar('\\');
putchar(t);
} else if (t < 040 || t >= 0177) {
lastoctal++;
printf("\\%o",t);
} else if (lastoctal && '0' <= t && t <= '9') {
lastoctal = 0;
printf("\"\n\t.ascii \"%c", t);
} else {
lastoctal = 0;
putchar(t);
}
}
}
/*
* return the alignment of field of type t
*/
int
fldal(unsigned int t)
{
uerror("illegal field type");
return(ALINT);
}
/* fix up type of field p */
void
fldty(struct symtab *p)
{
}
/*
* XXX - fix genswitch.
*/
int
mygenswitch(int num, TWORD type, struct swents **p, int n)
{
return 0;
#if 0
if (n == 0) {
if (p[0]->sval != 0)
branch(p[0]->sval);
return;
}
#ifdef PCC_DEBUG
if (xdebug) {
int i;
for (i = 1; i <= n; i++)
printf("%d: %llu\n", i, p[i]->sval);
}
#endif
if (0)
genswitch_table(num, p, n);
if (0)
genswitch_bintree(num, p, n);
genswitch_mrst(num, p, n);
#endif
}
#if 0
static void
genswitch_simple(int num, struct swents **p, int n)
{
NODE *r;
int i;
for (i = 1; i <= n; ++i) {
r = tempnode(num, INT, 0, MKSUE(INT));
r = buildtree(NE, r, bcon(p[i]->sval));
cbranch(buildtree(NOT, r, NIL), bcon(p[i]->slab));
}
if (p[0]->slab > 0)
branch(p[0]->slab);
}
static void bintree_rec(int num, struct swents **p, int n, int s, int e);
static void
genswitch_bintree(int num, struct swents **p, int n)
{
int lab = getlab();
if (p[0]->slab == 0)
p[0]->slab = lab;
bintree_rec(num, p, n, 1, n);
plabel(lab);
}
static void
bintree_rec(int num, struct swents **p, int n, int s, int e)
{
NODE *r;
int rlabel;
int h;
if (s == e) {
r = tempnode(num, INT, 0, MKSUE(INT));
r = buildtree(NE, r, bcon(p[s]->sval));
cbranch(buildtree(NOT, r, NIL), bcon(p[s]->slab));
branch(p[0]->slab);
return;
}
rlabel = getlab();
h = s + (e - s) / 2;
r = tempnode(num, INT, 0, MKSUE(INT));
r = buildtree(GT, r, bcon(p[h]->sval));
cbranch(r, bcon(rlabel));
bintree_rec(num, p, n, s, h);
plabel(rlabel);
bintree_rec(num, p, n, h+1, e);
}
static void
genswitch_table(int num, struct swents **p, int n)
{
NODE *r, *t;
int tval;
int minval, maxval, range;
int deflabel, tbllabel;
int i, j;
minval = p[1]->sval;
maxval = p[n]->sval;
range = maxval - minval + 1;
if (n < 10 || range > 3 * n) {
/* too small or too sparse for jump table */
genswitch_simple(num, p, n);
return;
}
r = tempnode(num, UNSIGNED, 0, MKSUE(UNSIGNED));
r = buildtree(MINUS, r, bcon(minval));
t = tempnode(0, UNSIGNED, 0, MKSUE(UNSIGNED));
tval = t->n_lval;
r = buildtree(ASSIGN, t, r);
ecomp(r);
deflabel = p[0]->slab;
if (deflabel == 0)
deflabel = getlab();
t = tempnode(tval, UNSIGNED, 0, MKSUE(UNSIGNED));
cbranch(buildtree(GT, t, bcon(maxval-minval)), bcon(deflabel));
tbllabel = getlab();
struct symtab *strtbl = lookup("__switch_table", SLBLNAME|STEMP);
strtbl->soffset = tbllabel;
strtbl->sclass = ILABEL;
strtbl->stype = INCREF(UCHAR);
t = block(NAME, NIL, NIL, UNSIGNED, 0, MKSUE(UNSIGNED));
t->n_sp = strtbl;
t = buildtree(ADDROF, t, NIL);
r = tempnode(tval, UNSIGNED, 0, MKSUE(INT));
r = buildtree(PLUS, t, r);
t = tempnode(0, INCREF(UNSIGNED), 0, MKSUE(UNSIGNED));
r = buildtree(ASSIGN, t, r);
ecomp(r);
r = tempnode(t->n_lval, INCREF(UNSIGNED), 0, MKSUE(UNSIGNED));
r = buildtree(UMUL, r, NIL);
t = block(NAME, NIL, NIL, UCHAR, 0, MKSUE(UCHAR));
t->n_sp = strtbl;
t = buildtree(ADDROF, t, NIL);
r = buildtree(PLUS, t, r);
r = block(GOTO, r, NIL, 0, 0, 0);
ecomp(r);
plabel(tbllabel);
for (i = minval, j=1; i <= maxval; i++) {
char *entry = tmpalloc(20);
int lab = deflabel;
//printf("; minval=%d, maxval=%d, i=%d, j=%d p[j]=%lld\n", minval, maxval, i, j, p[j]->sval);
if (p[j]->sval == i) {
lab = p[j]->slab;
j++;
}
snprintf(entry, 20, ".long " LABFMT "-" LABFMT, lab, tbllabel);
send_passt(IP_ASM, entry);
}
if (p[0]->slab <= 0)
plabel(deflabel);
}
#define DPRINTF(x) if (xdebug) printf x
//#define DPRINTF(x) do { } while(0)
#define MIN_TABLE_SIZE 8
/*
* Multi-way Radix Search Tree (MRST)
*/
static void mrst_rec(int num, struct swents **p, int n, int *state, int lab);
static unsigned long mrst_find_window(struct swents **p, int n, int *state, int lab, int *len, int *lowbit);
void mrst_put_entry_and_recurse(int num, struct swents **p, int n, int *state, int tbllabel, int lab, unsigned long j, unsigned long tblsize, unsigned long Wmax, int lowbit);
static void
genswitch_mrst(int num, struct swents **p, int n)
{
int *state;
int i;
int putlabel = 0;
if (n < 10) {
/* too small for MRST */
genswitch_simple(num, p, n);
return;
}
state = tmpalloc((n+1)*sizeof(int));
for (i = 0; i <= n; i++)
state[i] = 0;
if (p[0]->slab == 0) {
p[0]->slab = getlab();
putlabel = 1;
}
mrst_rec(num, p, n, state, 0);
if (putlabel)
plabel(p[0]->slab);
}
/*
* Look through the cases and generate a table or
* list of simple comparisons. If generating a table,
* invoke mrst_put_entry_and_recurse() to put
* an entry in the table and recurse.
*/
static void
mrst_rec(int num, struct swents **p, int n, int *state, int lab)
{
int len, lowbit;
unsigned long Wmax;
unsigned int tblsize;
NODE *t;
NODE *r;
int tval;
int i;
DPRINTF(("mrst_rec: num=%d, n=%d, lab=%d\n", num, n, lab));
/* find best window to cover set*/
Wmax = mrst_find_window(p, n, state, lab, &len, &lowbit);
tblsize = (1 << len);
assert(len > 0 && tblsize > 0);
DPRINTF(("mrst_rec: Wmax=%lu, lowbit=%d, tblsize=%u\n",
Wmax, lowbit, tblsize));
if (lab)
plabel(lab);
if (tblsize <= MIN_TABLE_SIZE) {
DPRINTF(("msrt_rec: break the recursion\n"));
for (i = 1; i <= n; i++) {
if (state[i] == lab) {
t = tempnode(num, UNSIGNED, 0, MKSUE(UNSIGNED));
cbranch(buildtree(EQ, t, bcon(p[i]->sval)),
bcon(p[i]->slab));
}
}
branch(p[0]->slab);
return;
}
DPRINTF(("generating table with %d elements\n", tblsize));
// AND with Wmax
t = tempnode(num, UNSIGNED, 0, MKSUE(UNSIGNED));
r = buildtree(AND, t, bcon(Wmax));
// RS lowbits
r = buildtree(RS, r, bcon(lowbit));
t = tempnode(0, UNSIGNED, 0, MKSUE(UNSIGNED));
tval = t->n_lval;
r = buildtree(ASSIGN, t, r);
ecomp(r);
int tbllabel = getlab();
struct symtab *strtbl = lookup("__switch_table", SLBLNAME|STEMP);
strtbl->soffset = tbllabel;
strtbl->sclass = ILABEL;
strtbl->stype = INCREF(UCHAR);
t = block(NAME, NIL, NIL, UNSIGNED, 0, MKSUE(UNSIGNED));
t->n_sp = strtbl;
t = buildtree(ADDROF, t, NIL);
r = tempnode(tval, UNSIGNED, 0, MKSUE(INT));
r = buildtree(PLUS, t, r);
t = tempnode(0, INCREF(UNSIGNED), 0, MKSUE(UNSIGNED));
r = buildtree(ASSIGN, t, r);
ecomp(r);
r = tempnode(t->n_lval, INCREF(UNSIGNED), 0, MKSUE(UNSIGNED));
r = buildtree(UMUL, r, NIL);
t = block(NAME, NIL, NIL, UCHAR, 0, MKSUE(UCHAR));
t->n_sp = strtbl;
t = buildtree(ADDROF, t, NIL);
r = buildtree(PLUS, t, r);
r = block(GOTO, r, NIL, 0, 0, 0);
ecomp(r);
plabel(tbllabel);
mrst_put_entry_and_recurse(num, p, n, state, tbllabel, lab,
0, tblsize, Wmax, lowbit);
}
/*
* Put an entry into the table and recurse to the next entry
* in the table. On the way back through the recursion, invoke
* mrst_rec() to check to see if we should generate another
* table.
*/
void
mrst_put_entry_and_recurse(int num, struct swents **p, int n, int *state,
int tbllabel, int labval,
unsigned long j, unsigned long tblsize, unsigned long Wmax, int lowbit)
{
int i;
int found = 0;
int lab = getlab();
/*
* Look for labels which map to this table entry.
* Mark each one in "state" that they fall inside this table.
*/
for (i = 1; i <= n; i++) {
unsigned int val = (p[i]->sval & Wmax) >> lowbit;
if (val == j && state[i] == labval) {
found = 1;
state[i] = lab;
}
}
/* couldn't find any labels? goto the default label */
if (!found)
lab = p[0]->slab;
/* generate the table entry */
char *entry = tmpalloc(20);
snprintf(entry, 20, ".long " LABFMT "-" LABFMT, lab, tbllabel);
send_passt(IP_ASM, entry);
DPRINTF(("mrst_put_entry: table=%d, pos=%lu/%lu, label=%d\n",
tbllabel, j, tblsize, lab));
/* go to the next table entry */
if (j+1 < tblsize) {
mrst_put_entry_and_recurse(num, p, n, state, tbllabel, labval,
j+1, tblsize, Wmax, lowbit);
}
/* if we are going to the default label, bail now */
if (!found)
return;
#ifdef PCC_DEBUG
if (xdebug) {
printf("state: ");
for (i = 1; i <= n; i++)
printf("%d ", state[i]);
printf("\n");
}
#endif
/* build another table */
mrst_rec(num, p, n, state, lab);
}
/*
* counts the number of entries in a table of size (1 << L) which would
* be used given the cases and the mask (W, lowbit).
*/
static unsigned int
mrst_cardinality(struct swents **p, int n, int *state, int step, unsigned long W, int L, int lowbit)
{
unsigned int count = 0;
int i;
if (W == 0)
return 0;
int *vals = (int *)calloc(1 << L, sizeof(int));
assert(vals);
DPRINTF(("mrst_cardinality: "));
for (i = 1; i <= n; i++) {
int idx;
if (state[i] != step)
continue;
idx = (p[i]->sval & W) >> lowbit;
DPRINTF(("%llu->%d, ", p[i]->sval, idx));
if (!vals[idx]) {
count++;
}
vals[idx] = 1;
}
DPRINTF((": found %d entries\n", count));
free(vals);
return count;
}
/*
* Find the maximum window (table size) which would best cover
* the set of labels. Algorithm explained in:
*
* Ulfar Erlingsson, Mukkai Krishnamoorthy and T.V. Raman.
* Efficient Multiway Radix Search Trees.
* Information Processing Letters 60:3 115-120 (November 1996)
*/
static unsigned long
mrst_find_window(struct swents **p, int n, int *state, int lab, int *len, int *lowbit)
{
unsigned int tblsize;
unsigned long W = 0;
unsigned long Wmax = 0;
unsigned long Wleft = (1 << (SZLONG-1));
unsigned int C = 0;
unsigned int Cmax = 0;
int L = 0;
int Lmax = 0;
int lowmax = 0;
int no_b = SZLONG-1;
unsigned long b = (1 << (SZLONG-1));
DPRINTF(("mrst_find_window: n=%d, lab=%d\n", n, lab));
for (; b > 0; b >>= 1, no_b--) {
// select the next bit
W |= b;
L += 1;
tblsize = 1 << L;
assert(tblsize > 0);
DPRINTF(("no_b=%d, b=0x%lx, Wleft=0x%lx, W=0x%lx, Wmax=0x%lx, L=%d, Lmax=%d, Cmax=%u, lowmax=%d, tblsize=%u\n", no_b, b, Wleft, W, Wmax, L, Lmax, Cmax, lowmax, tblsize));
C = mrst_cardinality(p, n, state, lab, W, L, no_b);
DPRINTF((" -> cardinality is %d\n", C));
if (2*C >= tblsize) {
DPRINTF(("(found good match, keep adding to table)\n"));
Wmax = W;
Lmax = L;
lowmax = no_b;
Cmax = C;
} else {
DPRINTF(("(too sparse)\n"));
assert((W & Wleft) != 0);
/* flip the MSB and see if we get a better match */
W ^= Wleft;
Wleft >>= 1;
L -= 1;
DPRINTF((" --> trying W=0x%lx and L=%d and Cmax=%u\n", W, L, Cmax));
C = mrst_cardinality(p, n, state, lab, W, L, no_b);
DPRINTF((" --> C=%u\n", C));
if (C > Cmax) {
Wmax = W;
Lmax = L;
lowmax = no_b;
Cmax = C;
DPRINTF((" --> better!\n"));
} else {
DPRINTF((" --> no better\n"));
}
}
}
#ifdef PCC_DEBUG
if (xdebug) {
int i;
int hibit = lowmax + Lmax;
printf("msrt_find_window: Wmax=0x%lx, lowbit=%d, result=", Wmax, lowmax);
for (i = 31; i >= 0; i--) {
int mask = (1 << i);
if (i == hibit)
printf("[");
if (Wmax & mask)
printf("1");
else
printf("0");
if (i == lowmax)
printf("]");
}
printf("\n");
}
#endif
assert(Lmax > 0);
*len = Lmax;
*lowbit = lowmax;
DPRINTF(("msrt_find_window: returning Wmax=%lu, len=%d, lowbit=%d [tblsize=%u, entries=%u]\n", Wmax, Lmax, lowmax, tblsize, C));
return Wmax;
}
#endif
/*
* Called with a function call with arguments as argument.
* This is done early in buildtree() and only done once.
*/
NODE *
funcode(NODE *p)
{
return p;
}
syntax highlighted by Code2HTML, v. 0.9.1