Compare commits

..

No commits in common. "master" and "v1.2" have entirely different histories.
master ... v1.2

71 changed files with 1329 additions and 7000 deletions

1
.gitignore vendored
View File

@ -3,4 +3,3 @@ qbe
config.h
.comfile
*.out
*~

View File

@ -1,4 +1,4 @@
© 2015-2026 Quentin Carbonneaux <quentin@c9x.me>
© 2015-2023 Quentin Carbonneaux <quentin@c9x.me>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),

View File

@ -5,9 +5,8 @@ PREFIX = /usr/local
BINDIR = $(PREFIX)/bin
COMMOBJ = main.o util.o parse.o abi.o cfg.o mem.o ssa.o alias.o load.o \
copy.o fold.o gvn.o gcm.o simpl.o ifopt.o live.o spill.o rega.o \
emit.o
AMD64OBJ = amd64/targ.o amd64/sysv.o amd64/isel.o amd64/emit.o amd64/winabi.o
copy.o fold.o simpl.o live.o spill.o rega.o emit.o
AMD64OBJ = amd64/targ.o amd64/sysv.o amd64/isel.o amd64/emit.o
ARM64OBJ = arm64/targ.o arm64/abi.o arm64/isel.o arm64/emit.o
RV64OBJ = rv64/targ.o rv64/abi.o rv64/isel.o rv64/emit.o
OBJ = $(COMMOBJ) $(AMD64OBJ) $(ARM64OBJ) $(RV64OBJ)
@ -72,18 +71,12 @@ clean-gen: clean
check: qbe
tools/test.sh all
check-x86_64: qbe
TARGET=x86_64 tools/test.sh all
check-arm64: qbe
TARGET=arm64 tools/test.sh all
check-rv64: qbe
TARGET=rv64 tools/test.sh all
check-amd64_win: qbe
TARGET=amd64_win tools/test.sh all
src:
@echo $(SRCALL)
@ -97,7 +90,4 @@ src:
}" < $$F; \
done
wc:
@wc -l $(SRCALL)
.PHONY: clean clean-gen check check-arm64 check-rv64 src 80 wc install uninstall
.PHONY: clean clean-gen check check-arm64 src 80 install uninstall

2
README
View File

@ -15,4 +15,4 @@ The LICENSE file applies to all files distributed.
Invoke make in this directory to create the executable
file qbe. Install using 'make install', the standard
DESTDIR and PREFIX environment variables are supported.
Alternatively, you may simply copy the qbe binary.
Alternatively, you may simply copy the qbe binary manually.

View File

@ -132,7 +132,7 @@ store(Ref r, int sz, Fn *fn)
void
fillalias(Fn *fn)
{
uint n;
uint n, m;
int t, sz;
int64_t x;
Blk *b;
@ -146,6 +146,8 @@ fillalias(Fn *fn)
for (n=0; n<fn->nblk; ++n) {
b = fn->rpo[n];
for (p=b->phi; p; p=p->link) {
for (m=0; m<p->narg; m++)
esc(p->arg[m], fn);
assert(rtype(p->to) == RTmp);
a = &fn->tmp[p->to.val].alias;
assert(a->type == ABot);
@ -215,8 +217,4 @@ fillalias(Fn *fn)
if (b->jmp.type != Jretc)
esc(b->jmp.arg, fn);
}
for (b=fn->start; b; b=b->link)
for (p=b->phi; p; p=p->link)
for (n=0; n<p->narg; n++)
esc(p->arg[n], fn);
}

81
all.h
View File

@ -21,7 +21,6 @@ typedef struct Phi Phi;
typedef struct Blk Blk;
typedef struct Use Use;
typedef struct Sym Sym;
typedef struct Num Num;
typedef struct Alias Alias;
typedef struct Tmp Tmp;
typedef struct Con Con;
@ -44,7 +43,6 @@ enum {
struct Target {
char name[16];
char apple;
char windows;
int gpr0; /* first general purpose reg */
int ngpr;
int fpr0; /* first floating point reg */
@ -63,7 +61,6 @@ struct Target {
void (*emitfin)(FILE *);
char asloc[4];
char assym[4];
uint cansel:1;
};
#define BIT(n) ((bits)1 << (n))
@ -118,7 +115,7 @@ static inline int rtype(Ref r)
static inline int rsval(Ref r)
{
return ((int)r.val ^ 0x10000000) - 0x10000000;
return (int32_t)((int64_t)r.val << 3) >> 3;
}
enum CmpI {
@ -185,8 +182,6 @@ enum {
Oalloc1 = Oalloc16,
Oflag = Oflagieq,
Oflag1 = Oflagfuo,
Oxsel = Oxselieq,
Oxsel1 = Oxselfuo,
NPubOp = Onop,
Jjf = Jjfieq,
Jjf1 = Jjffuo,
@ -195,7 +190,6 @@ enum {
#define INRANGE(x, l, u) ((unsigned)(x) - l <= u - l) /* linear in x */
#define isstore(o) INRANGE(o, Ostoreb, Ostored)
#define isload(o) INRANGE(o, Oloadsb, Oload)
#define isalloc(o) INRANGE(o, Oalloc4, Oalloc16)
#define isext(o) INRANGE(o, Oextsb, Oextuw)
#define ispar(o) INRANGE(o, Opar, Opare)
#define isarg(o) INRANGE(o, Oarg, Oargv)
@ -203,7 +197,6 @@ enum {
#define isparbh(o) INRANGE(o, Oparsb, Oparuh)
#define isargbh(o) INRANGE(o, Oargsb, Oarguh)
#define isretbh(j) INRANGE(j, Jretsb, Jretuh)
#define isxsel(o) INRANGE(o, Oxsel, Oxsel1)
enum {
Kx = -1, /* "top" class (see usecheck() and clsmerge()) */
@ -219,16 +212,7 @@ enum {
struct Op {
char *name;
short argcls[2][4];
uint canfold:1;
uint hasid:1; /* op identity value? */
uint idval:1; /* identity value 0/1 */
uint commutes:1; /* commutative op? */
uint assoc:1; /* associative op? */
uint idemp:1; /* idempotent op? */
uint cmpeqwl:1; /* Kl/Kw cmp eq/ne? */
uint cmplgtewl:1; /* Kl/Kw cmp lt/gt/le/ge? */
uint eqval:1; /* 1 for eq; 0 for ne */
uint pinned:1; /* GCM pinned op? */
int canfold;
};
struct Ins {
@ -243,8 +227,7 @@ struct Phi {
Ref *arg;
Blk **blk;
uint narg;
short cls;
uint visit:1;
int cls;
Phi *link;
};
@ -267,7 +250,6 @@ struct Blk {
Blk *dom, *dlink;
Blk **fron;
uint nfron;
int depth;
Blk **pred;
uint npred;
@ -299,12 +281,6 @@ struct Sym {
uint32_t id;
};
struct Num {
uchar n;
uchar nl, nr;
Ref l, r;
};
enum {
NoAlias,
MayAlias,
@ -359,7 +335,6 @@ struct Tmp {
Wuw
} width;
int visit;
uint gcmbid;
};
struct Con {
@ -389,7 +364,6 @@ struct Addr { /* amd64 addressing */
struct Lnk {
char export;
char thread;
char common;
char align;
char *sec;
char *secf;
@ -409,10 +383,8 @@ struct Fn {
Blk **rpo;
bits reg;
int slot;
int salign;
char vararg;
char dynalloc;
char leaf;
char name[NString];
Lnk lnk;
};
@ -486,36 +458,29 @@ void freeall(void);
void *vnew(ulong, size_t, Pool);
void vfree(void *);
void vgrow(void *, ulong);
void addins(Ins **, uint *, Ins *);
void addbins(Ins **, uint *, Blk *);
void strf(char[NString], char *, ...);
uint32_t intern(char *);
char *str(uint32_t);
int argcls(Ins *, int);
int isreg(Ref);
int iscmp(int, int *, int *);
void igroup(Blk *, Ins *, Ins **, Ins **);
void emit(int, int, Ref, Ref, Ref);
void emiti(Ins);
void idup(Blk *, Ins *, ulong);
void idup(Ins **, Ins *, ulong);
Ins *icpy(Ins *, Ins *, ulong);
int cmpop(int);
int cmpneg(int);
int cmpwlneg(int);
int clsmerge(short *, short);
int phicls(int, Tmp *);
uint phiargn(Phi *, Blk *);
Ref phiarg(Phi *, Blk *);
Ref newtmp(char *, int, Fn *);
void chuse(Ref, int, Fn *);
int symeq(Sym, Sym);
Ref newcon(Con *, Fn *);
Ref getcon(int64_t, Fn *);
int addcon(Con *, Con *, int);
int isconbits(Fn *fn, Ref r, int64_t *v);
int addcon(Con *, Con *);
void salloc(Ref, Ref, Fn *);
void dumpts(BSet *, Tmp *, FILE *);
void runmatch(uchar *, Num *, Ref, Ref *);
void bsinit(BSet *, uint);
void bszero(BSet *);
uint bscount(BSet *);
@ -547,21 +512,16 @@ void elimsb(Fn *);
/* cfg.c */
Blk *newblk(void);
void edgedel(Blk *, Blk **);
void fillpreds(Fn *);
void fillcfg(Fn *);
void fillrpo(Fn *);
void filldom(Fn *);
int sdom(Blk *, Blk *);
int dom(Blk *, Blk *);
void fillfron(Fn *);
void loopiter(Fn *, void (*)(Blk *, Blk *));
void filldepth(Fn *);
Blk *lca(Blk *, Blk *);
void fillloop(Fn *);
void simpljmp(Fn *);
int reaches(Fn *, Blk *, Blk *);
int reachesnotvia(Fn *, Blk *, Blk *, Blk *);
int ifgraph(Blk *, Blk **, Blk **, Blk **);
void simplcfg(Fn *);
/* mem.c */
void promote(Fn *);
@ -579,31 +539,17 @@ int storesz(Ins *);
void loadopt(Fn *);
/* ssa.c */
void adduse(Tmp *, int, Blk *, ...);
void filluse(Fn *);
void fillpreds(Fn *);
void fillrpo(Fn *);
void ssa(Fn *);
void ssacheck(Fn *);
/* copy.c */
void narrowpars(Fn *fn);
Ref copyref(Fn *, Blk *, Ins *);
Ref phicopyref(Fn *, Blk *, Phi *);
void copy(Fn *);
/* fold.c */
int foldint(Con *, int, int, Con *, Con *);
Ref foldref(Fn *, Ins *);
/* gvn.c */
extern Ref con01[2]; /* 0 and 1 */
int zeroval(Fn *, Blk *, Ref, int, int *);
void gvn(Fn *);
/* gcm.c */
int pinned(Ins *);
void gcm(Fn *);
/* ifopt.c */
void ifconvert(Fn *fn);
void fold(Fn *);
/* simpl.c */
void simpl(Fn *);
@ -624,8 +570,7 @@ void emitfnlnk(char *, Lnk *, FILE *);
void emitdat(Dat *, FILE *);
void emitdbgfile(char *, FILE *);
void emitdbgloc(uint, uint, FILE *);
int stashbits(bits, int);
int stashbits(void *, int);
void elf_emitfnfin(char *, FILE *);
void elf_emitfin(FILE *);
void macho_emitfin(FILE *);
void pe_emitfin(FILE *);

View File

@ -4,14 +4,14 @@ typedef struct Amd64Op Amd64Op;
enum Amd64Reg {
RAX = RXX+1, /* caller-save */
RCX, /* caller-save */
RDX, /* caller-save */
RSI, /* caller-save on sysv, callee-save on win */
RDI, /* caller-save on sysv, callee-save on win */
R8, /* caller-save */
R9, /* caller-save */
R10, /* caller-save */
R11, /* caller-save */
RCX,
RDX,
RSI,
RDI,
R8,
R9,
R10,
R11,
RBX, /* callee-save */
R12,
@ -41,13 +41,9 @@ enum Amd64Reg {
NFPR = XMM14 - XMM0 + 1, /* reserve XMM15 */
NGPR = RSP - RAX + 1,
NGPS = R11 - RAX + 1,
NFPS = NFPR,
NGPS_SYSV = R11 - RAX + 1,
NCLR_SYSV = R15 - RBX + 1,
NGPS_WIN = R11 - RAX + 1 - 2, /* -2 for RDI/RDI */
NCLR_WIN = R15 - RBX + 1 + 2, /* +2 for RDI/RDI */
NCLR = R15 - RBX + 1,
};
MAKESURE(reg_not_tmp, XMM15 < (int)Tmp0);
@ -67,16 +63,8 @@ bits amd64_sysv_retregs(Ref, int[2]);
bits amd64_sysv_argregs(Ref, int[2]);
void amd64_sysv_abi(Fn *);
/* winabi.c */
extern int amd64_winabi_rsave[];
extern int amd64_winabi_rclob[];
bits amd64_winabi_retregs(Ref, int[2]);
bits amd64_winabi_argregs(Ref, int[2]);
void amd64_winabi_abi(Fn *);
/* isel.c */
void amd64_isel(Fn *);
/* emit.c */
void amd64_sysv_emitfn(Fn *, FILE *);
void amd64_winabi_emitfn(Fn *, FILE *);
void amd64_emitfn(Fn *, FILE *);

View File

@ -1,33 +1,25 @@
#include "all.h"
typedef struct E E;
struct E {
FILE *f;
Fn *fn;
int fp;
uint64_t fsz;
int nclob;
};
#define CMP(X) \
X(Ciule, "be", "a") \
X(Ciult, "b", "ae") \
X(Cisle, "le", "g") \
X(Cislt, "l", "ge") \
X(Cisgt, "g", "le") \
X(Cisge, "ge", "l") \
X(Ciugt, "a", "be") \
X(Ciuge, "ae", "b") \
X(Cieq, "z", "nz") \
X(Cine, "nz", "z") \
X(NCmpI+Cfle, "be", "a") \
X(NCmpI+Cflt, "b", "ae") \
X(NCmpI+Cfgt, "a", "be") \
X(NCmpI+Cfge, "ae", "b") \
X(NCmpI+Cfo, "np", "p") \
X(NCmpI+Cfuo, "p", "np")
X(Ciule, "be") \
X(Ciult, "b") \
X(Cisle, "le") \
X(Cislt, "l") \
X(Cisgt, "g") \
X(Cisge, "ge") \
X(Ciugt, "a") \
X(Ciuge, "ae") \
X(Cieq, "z") \
X(Cine, "nz") \
X(NCmpI+Cfle, "be") \
X(NCmpI+Cflt, "b") \
X(NCmpI+Cfgt, "a") \
X(NCmpI+Cfge, "ae") \
X(NCmpI+Cfeq, "z") \
X(NCmpI+Cfne, "nz") \
X(NCmpI+Cfo, "np") \
X(NCmpI+Cfuo, "p")
enum {
SLong = 0,
@ -68,7 +60,7 @@ enum {
static struct {
short op;
short cls;
char *fmt;
char *asm;
} omap[] = {
{ Oadd, Ka, "+add%k %1, %=" },
{ Osub, Ka, "-sub%k %1, %=" },
@ -122,25 +114,13 @@ static struct {
{ Oxcmp, Kd, "ucomisd %D0, %D1" },
{ Oxcmp, Ki, "cmp%k %0, %1" },
{ Oxtest, Ki, "test%k %0, %1" },
#define X(c, s, _) \
#define X(c, s) \
{ Oflag+c, Ki, "set" s " %B=\n\tmovzb%k %B=, %=" },
CMP(X)
#undef X
{ Oflagfeq, Ki, "setz %B=\n\tmovzb%k %B=, %=" },
{ Oflagfne, Ki, "setnz %B=\n\tmovzb%k %B=, %=" },
{ NOp, 0, 0 }
};
static char cmov[][2][16] = {
#define X(c, s0, s1) \
[c] = { \
"cmov" s0 " %0, %=", \
"cmov" s1 " %1, %=", \
},
CMP(X)
#undef X
};
static char *rname[][4] = {
[RAX] = {"rax", "eax", "ax", "al"},
[RBX] = {"rbx", "ebx", "bx", "bl"},
@ -162,32 +142,23 @@ static char *rname[][4] = {
static int
slot(Ref r, E *e)
slot(Ref r, Fn *fn)
{
int s;
s = rsval(r);
assert(s <= e->fn->slot);
assert(s <= fn->slot);
/* specific to NAlign == 3 */
if (s < 0) {
if (e->fp == RSP)
return 4*-s - 8 + e->fsz + e->nclob*8;
if (s < 0)
return -4 * s;
else if (fn->vararg)
return -176 + -4 * (fn->slot - s);
else
return 4*-s;
}
else if (e->fp == RSP)
return 4*s + e->nclob*8;
else if (e->fn->vararg) {
if (T.windows)
return -4 * (e->fn->slot - s);
else
return -176 + -4 * (e->fn->slot - s);
} else
return -4 * (e->fn->slot - s);
return -4 * (fn->slot - s);
}
static void
emitcon(Con *con, E *e)
emitcon(Con *con, FILE *f)
{
char *p, *l;
@ -197,16 +168,16 @@ emitcon(Con *con, E *e)
p = l[0] == '"' ? "" : T.assym;
if (con->sym.type == SThr) {
if (T.apple)
fprintf(e->f, "%s%s@TLVP", p, l);
fprintf(f, "%s%s@TLVP", p, l);
else
fprintf(e->f, "%%fs:%s%s@tpoff", p, l);
fprintf(f, "%%fs:%s%s@tpoff", p, l);
} else
fprintf(e->f, "%s%s", p, l);
fprintf(f, "%s%s", p, l);
if (con->bits.i)
fprintf(e->f, "%+"PRId64, con->bits.i);
fprintf(f, "%+"PRId64, con->bits.i);
break;
case CBits:
fprintf(e->f, "%"PRId64, con->bits.i);
fprintf(f, "%"PRId64, con->bits.i);
break;
default:
die("unreachable");
@ -241,10 +212,10 @@ getarg(char c, Ins *i)
}
}
static void emitins(Ins, E *);
static void emitins(Ins, Fn *, FILE *);
static void
emitcopy(Ref r1, Ref r2, int k, E *e)
emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
{
Ins icp;
@ -252,11 +223,11 @@ emitcopy(Ref r1, Ref r2, int k, E *e)
icp.arg[0] = r2;
icp.to = r1;
icp.cls = k;
emitins(icp, e);
emitins(icp, fn, f);
}
static void
emitf(char *s, Ins *i, E *e)
emitf(char *s, Ins *i, Fn *fn, FILE *f)
{
static char clstoa[][3] = {"l", "q", "ss", "sd"};
char c;
@ -276,25 +247,25 @@ emitf(char *s, Ins *i, E *e)
case '-':
assert((!req(i->arg[1], i->to) || req(i->arg[0], i->to)) &&
"cannot convert to 2-address");
emitcopy(i->to, i->arg[0], i->cls, e);
emitcopy(i->to, i->arg[0], i->cls, fn, f);
s++;
break;
}
fputc('\t', e->f);
fputc('\t', f);
Next:
while ((c = *s++) != '%')
if (!c) {
fputc('\n', e->f);
fputc('\n', f);
return;
} else
fputc(c, e->f);
fputc(c, f);
switch ((c = *s++)) {
case '%':
fputc('%', e->f);
fputc('%', f);
break;
case 'k':
fputs(clstoa[i->cls], e->f);
fputs(clstoa[i->cls], f);
break;
case '0':
case '1':
@ -311,42 +282,37 @@ Next:
switch (rtype(ref)) {
case RTmp:
assert(isreg(ref));
fprintf(e->f, "%%%s", regtoa(ref.val, sz));
fprintf(f, "%%%s", regtoa(ref.val, sz));
break;
case RSlot:
fprintf(e->f, "%d(%%%s)",
slot(ref, e),
regtoa(e->fp, SLong)
);
fprintf(f, "%d(%%rbp)", slot(ref, fn));
break;
case RMem:
Mem:
m = &e->fn->mem[ref.val];
m = &fn->mem[ref.val];
if (rtype(m->base) == RSlot) {
off.type = CBits;
off.bits.i = slot(m->base, e);
addcon(&m->offset, &off, 1);
m->base = TMP(e->fp);
off.bits.i = slot(m->base, fn);
addcon(&m->offset, &off);
m->base = TMP(RBP);
}
if (m->offset.type != CUndef)
emitcon(&m->offset, e);
fputc('(', e->f);
emitcon(&m->offset, f);
fputc('(', f);
if (!req(m->base, R))
fprintf(e->f, "%%%s",
regtoa(m->base.val, SLong)
);
fprintf(f, "%%%s", regtoa(m->base.val, SLong));
else if (m->offset.type == CAddr)
fprintf(e->f, "%%rip");
fprintf(f, "%%rip");
if (!req(m->index, R))
fprintf(e->f, ", %%%s, %d",
fprintf(f, ", %%%s, %d",
regtoa(m->index.val, SLong),
m->scale
);
fputc(')', e->f);
fputc(')', f);
break;
case RCon:
fputc('$', e->f);
emitcon(&e->fn->con[ref.val], e);
fputc('$', f);
emitcon(&fn->con[ref.val], f);
break;
default:
die("unreachable");
@ -371,21 +337,18 @@ Next:
case RMem:
goto Mem;
case RSlot:
fprintf(e->f, "%d(%%%s)",
slot(ref, e),
regtoa(e->fp, SLong)
);
fprintf(f, "%d(%%rbp)", slot(ref, fn));
break;
case RCon:
off = e->fn->con[ref.val];
emitcon(&off, e);
off = fn->con[ref.val];
emitcon(&off, f);
if (off.type == CAddr)
if (off.sym.type != SThr || T.apple)
fprintf(e->f, "(%%rip)");
fprintf(f, "(%%rip)");
break;
case RTmp:
assert(isreg(ref));
fprintf(e->f, "(%%%s)", regtoa(ref.val, SLong));
fprintf(f, "(%%%s)", regtoa(ref.val, SLong));
break;
default:
die("unreachable");
@ -397,13 +360,13 @@ Next:
goto Next;
}
static bits negmask[4] = {
[Ks] = 0x80000000,
[Kd] = 0x8000000000000000,
static void *negmask[4] = {
[Ks] = (uint32_t[4]){ 0x80000000 },
[Kd] = (uint64_t[2]){ 0x8000000000000000 },
};
static void
emitins(Ins i, E *e)
emitins(Ins i, Fn *fn, FILE *f)
{
Ref r;
int64_t val;
@ -414,8 +377,6 @@ emitins(Ins i, E *e)
switch (i.op) {
default:
if (isxsel(i.op))
goto case_Oxsel;
Table:
/* most instructions are just pulled out of
* the table omap[], some special cases are
@ -432,7 +393,7 @@ emitins(Ins i, E *e)
|| (omap[o].cls == Ka))
break;
}
emitf(omap[o].fmt, &i, e);
emitf(omap[o].asm, &i, fn, f);
break;
case Onop:
/* just do nothing for nops, they are inserted
@ -449,7 +410,7 @@ emitins(Ins i, E *e)
if (KBASE(i.cls) == 0 /* only available for ints */
&& rtype(i.arg[0]) == RCon
&& rtype(i.arg[1]) == RTmp) {
emitf("imul%k %0, %1, %=", &i, e);
emitf("imul%k %0, %1, %=", &i, fn, f);
break;
}
goto Table;
@ -458,18 +419,18 @@ emitins(Ins i, E *e)
* some 3-address subtractions */
if (req(i.to, i.arg[1]) && !req(i.arg[0], i.to)) {
ineg = (Ins){Oneg, i.cls, i.to, {i.to}};
emitins(ineg, e);
emitf("add%k %0, %=", &i, e);
emitins(ineg, fn, f);
emitf("add%k %0, %=", &i, fn, f);
break;
}
goto Table;
case Oneg:
if (!req(i.to, i.arg[0]))
emitf("mov%k %0, %=", &i, e);
emitf("mov%k %0, %=", &i, fn, f);
if (KBASE(i.cls) == 0)
emitf("neg%k %=", &i, e);
emitf("neg%k %=", &i, fn, f);
else
fprintf(e->f,
fprintf(f,
"\txorp%c %sfp%d(%%rip), %%%s\n",
"xxsd"[i.cls],
T.asloc,
@ -482,8 +443,8 @@ emitins(Ins i, E *e)
* conversion to 2-address in emitf() would fail */
if (req(i.to, i.arg[1])) {
i.arg[1] = TMP(XMM0+15);
emitf("mov%k %=, %1", &i, e);
emitf("mov%k %0, %=", &i, e);
emitf("mov%k %=, %1", &i, fn, f);
emitf("mov%k %0, %=", &i, fn, f);
i.arg[0] = i.to;
}
goto Table;
@ -499,54 +460,53 @@ emitins(Ins i, E *e)
t0 = rtype(i.arg[0]);
if (i.cls == Kl
&& t0 == RCon
&& e->fn->con[i.arg[0].val].type == CBits) {
val = e->fn->con[i.arg[0].val].bits.i;
&& fn->con[i.arg[0].val].type == CBits) {
val = fn->con[i.arg[0].val].bits.i;
if (isreg(i.to))
if (val >= 0 && val <= UINT32_MAX) {
emitf("movl %W0, %W=", &i, e);
emitf("movl %W0, %W=", &i, fn, f);
break;
}
if (rtype(i.to) == RSlot)
if (val < INT32_MIN || val > INT32_MAX) {
emitf("movl %0, %=", &i, e);
emitf("movl %0>>32, 4+%=", &i, e);
emitf("movl %0, %=", &i, fn, f);
emitf("movl %0>>32, 4+%=", &i, fn, f);
break;
}
}
if (isreg(i.to)
&& t0 == RCon
&& e->fn->con[i.arg[0].val].type == CAddr) {
emitf("lea%k %M0, %=", &i, e);
&& fn->con[i.arg[0].val].type == CAddr) {
emitf("lea%k %M0, %=", &i, fn, f);
break;
}
if (rtype(i.to) == RSlot
&& (t0 == RSlot || t0 == RMem)) {
i.cls = KWIDE(i.cls) ? Kd : Ks;
i.arg[1] = TMP(XMM0+15);
emitf("mov%k %0, %1", &i, e);
emitf("mov%k %1, %=", &i, e);
emitf("mov%k %0, %1", &i, fn, f);
emitf("mov%k %1, %=", &i, fn, f);
break;
}
/* conveniently, the assembler knows if it
* should use movabsq when reading movq */
emitf("mov%k %0, %=", &i, e);
emitf("mov%k %0, %=", &i, fn, f);
break;
case Oaddr:
if (!T.apple
&& rtype(i.arg[0]) == RCon
&& e->fn->con[i.arg[0].val].sym.type == SThr) {
&& fn->con[i.arg[0].val].sym.type == SThr) {
/* derive the symbol address from the TCB
* address at offset 0 of %fs */
assert(isreg(i.to));
con = &e->fn->con[i.arg[0].val];
con = &fn->con[i.arg[0].val];
sym = str(con->sym.id);
emitf("movq %%fs:0, %L=", &i, e);
fprintf(e->f, "\tleaq %s%s@tpoff",
emitf("movq %%fs:0, %L=", &i, fn, f);
fprintf(f, "\tleaq %s%s@tpoff",
sym[0] == '"' ? "" : T.assym, sym);
if (con->bits.i)
fprintf(e->f, "%+"PRId64,
con->bits.i);
fprintf(e->f, "(%%%s), %%%s\n",
fprintf(f, "%+"PRId64, con->bits.i);
fprintf(f, "(%%%s), %%%s\n",
regtoa(i.to.val, SLong),
regtoa(i.to.val, SLong));
break;
@ -557,12 +517,12 @@ emitins(Ins i, E *e)
* assembly... */
switch (rtype(i.arg[0])) {
case RCon:
fprintf(e->f, "\tcallq ");
emitcon(&e->fn->con[i.arg[0].val], e);
fprintf(e->f, "\n");
fprintf(f, "\tcallq ");
emitcon(&fn->con[i.arg[0].val], f);
fprintf(f, "\n");
break;
case RTmp:
emitf("callq *%L0", &i, e);
emitf("callq *%L0", &i, fn, f);
break;
default:
die("invalid call argument");
@ -573,10 +533,9 @@ emitins(Ins i, E *e)
* maybe we should split Osalloc in 2 different
* instructions depending on the result
*/
assert(e->fp == RBP);
emitf("subq %L0, %%rsp", &i, e);
emitf("subq %L0, %%rsp", &i, fn, f);
if (!req(i.to, R))
emitcopy(i.to, TMP(RSP), Kl, e);
emitcopy(i.to, TMP(RSP), Kl, fn, f);
break;
case Oswap:
if (KBASE(i.cls) == 0)
@ -584,51 +543,34 @@ emitins(Ins i, E *e)
/* for floats, there is no swap instruction
* so we use xmm15 as a temporary
*/
emitcopy(TMP(XMM0+15), i.arg[0], i.cls, e);
emitcopy(i.arg[0], i.arg[1], i.cls, e);
emitcopy(i.arg[1], TMP(XMM0+15), i.cls, e);
emitcopy(TMP(XMM0+15), i.arg[0], i.cls, fn, f);
emitcopy(i.arg[0], i.arg[1], i.cls, fn, f);
emitcopy(i.arg[1], TMP(XMM0+15), i.cls, fn, f);
break;
case Odbgloc:
emitdbgloc(i.arg[0].val, i.arg[1].val, e->f);
break;
case_Oxsel:
if (req(i.to, i.arg[1]))
emitf(cmov[i.op-Oxsel][0], &i, e);
else {
if (!req(i.to, i.arg[0]))
emitf("mov %0, %=", &i, e);
emitf(cmov[i.op-Oxsel][1], &i, e);
}
emitdbgloc(i.arg[0].val, i.arg[1].val, f);
break;
}
}
static void
sysv_framesz(E *e)
static uint64_t
framesz(Fn *fn)
{
uint64_t i, o, f;
/* specific to NAlign == 3 */
o = 0;
if (!e->fn->leaf) {
for (i=0, o=0; i<NCLR_SYSV; i++)
o ^= e->fn->reg >> amd64_sysv_rclob[i];
o &= 1;
}
f = e->fn->slot;
for (i=0, o=0; i<NCLR; i++)
o ^= 1 & (fn->reg >> amd64_sysv_rclob[i]);
f = fn->slot;
f = (f + 3) & -4;
if (f > 0
&& e->fp == RSP
&& e->fn->salign == 4)
f += 2;
e->fsz = 4*f + 8*o + 176*e->fn->vararg;
return 4*f + 8*o + 176*fn->vararg;
}
void
amd64_sysv_emitfn(Fn *fn, FILE *f)
amd64_emitfn(Fn *fn, FILE *f)
{
static char *ctoa[] = {
#define X(c, s, _) [c] = s,
#define X(c, s) [c] = s,
CMP(X)
#undef X
};
@ -636,20 +578,13 @@ amd64_sysv_emitfn(Fn *fn, FILE *f)
Blk *b, *s;
Ins *i, itmp;
int *r, c, o, n, lbl;
uint p;
E *e;
uint64_t fs;
e = &(E){.f = f, .fn = fn};
emitfnlnk(fn->name, &fn->lnk, f);
fputs("\tendbr64\n", f);
if (!fn->leaf || fn->vararg || fn->dynalloc) {
e->fp = RBP;
fputs("\tpushq %rbp\n\tmovq %rsp, %rbp\n", f);
} else
e->fp = RSP;
sysv_framesz(e);
if (e->fsz)
fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", e->fsz);
fs = framesz(fn);
if (fs)
fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);
if (fn->vararg) {
o = -176;
for (r=amd64_sysv_rsave; r<&amd64_sysv_rsave[6]; r++, o+=8)
@ -657,24 +592,18 @@ amd64_sysv_emitfn(Fn *fn, FILE *f)
for (n=0; n<8; ++n, o+=16)
fprintf(f, "\tmovaps %%xmm%d, %d(%%rbp)\n", n, o);
}
for (r=amd64_sysv_rclob; r<&amd64_sysv_rclob[NCLR_SYSV]; r++)
for (r=amd64_sysv_rclob; r<&amd64_sysv_rclob[NCLR]; r++)
if (fn->reg & BIT(*r)) {
itmp.arg[0] = TMP(*r);
emitf("pushq %L0", &itmp, e);
e->nclob++;
emitf("pushq %L0", &itmp, fn, f);
fs += 8;
}
for (lbl=0, b=fn->start; b; b=b->link) {
if (lbl || b->npred > 1) {
for (p=0; p<b->npred; p++)
if (b->pred[p]->id >= b->id)
break;
if (p != b->npred)
fprintf(f, ".p2align 4\n");
if (lbl || b->npred > 1)
fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
}
for (i=b->ins; i!=&b->ins[b->nins]; i++)
emitins(*i, e);
emitins(*i, fn, f);
lbl = 1;
switch (b->jmp.type) {
case Jhlt:
@ -685,19 +614,17 @@ amd64_sysv_emitfn(Fn *fn, FILE *f)
fprintf(f,
"\tmovq %%rbp, %%rsp\n"
"\tsubq $%"PRIu64", %%rsp\n",
e->fsz + e->nclob * 8);
for (r=&amd64_sysv_rclob[NCLR_SYSV]; r>amd64_sysv_rclob;)
fs
);
for (r=&amd64_sysv_rclob[NCLR]; r>amd64_sysv_rclob;)
if (fn->reg & BIT(*--r)) {
itmp.arg[0] = TMP(*r);
emitf("popq %L0", &itmp, e);
emitf("popq %L0", &itmp, fn, f);
}
if (e->fp == RBP)
fputs("\tleave\n", f);
else if (e->fsz)
fprintf(f,
"\taddq $%"PRIu64", %%rsp\n",
e->fsz);
fputs("\tret\n", f);
"\tleave\n"
"\tret\n"
);
break;
case Jjmp:
Jmp:
@ -727,118 +654,3 @@ amd64_sysv_emitfn(Fn *fn, FILE *f)
if (!T.apple)
elf_emitfnfin(fn->name, f);
}
static void
winabi_framesz(E *e)
{
uint64_t i, o, f;
/* specific to NAlign == 3 */
o = 0;
if (!e->fn->leaf) {
for (i=0, o=0; i<NCLR_WIN; i++)
o ^= e->fn->reg >> amd64_winabi_rclob[i];
o &= 1;
}
f = e->fn->slot;
f = (f + 3) & -4;
if (f > 0
&& e->fp == RSP
&& e->fn->salign == 4)
f += 2;
e->fsz = 4*f + 8*o;
}
void
amd64_winabi_emitfn(Fn *fn, FILE *f)
{
static char *ctoa[] = {
#define X(c, s, _) [c] = s,
CMP(X)
#undef X
};
static int id0;
Blk *b, *s;
Ins *i, itmp;
int *r, c, lbl;
E *e;
e = &(E){.f = f, .fn = fn};
emitfnlnk(fn->name, &fn->lnk, f);
fputs("\tendbr64\n", f);
if (fn->vararg) {
fprintf(f, "\tmovq %%rcx, 0x8(%%rsp)\n");
fprintf(f, "\tmovq %%rdx, 0x10(%%rsp)\n");
fprintf(f, "\tmovq %%r8, 0x18(%%rsp)\n");
fprintf(f, "\tmovq %%r9, 0x20(%%rsp)\n");
}
if (!fn->leaf || fn->vararg || fn->dynalloc) {
e->fp = RBP;
fputs("\tpushq %rbp\n\tmovq %rsp, %rbp\n", f);
} else
e->fp = RSP;
winabi_framesz(e);
if (e->fsz)
fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", e->fsz);
for (r=amd64_winabi_rclob; r<&amd64_winabi_rclob[NCLR_WIN]; r++)
if (fn->reg & BIT(*r)) {
itmp.arg[0] = TMP(*r);
emitf("pushq %L0", &itmp, e);
e->nclob++;
}
for (lbl=0, b=fn->start; b; b=b->link) {
if (lbl || b->npred > 1)
fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
for (i=b->ins; i!=&b->ins[b->nins]; i++)
emitins(*i, e);
lbl = 1;
switch (b->jmp.type) {
case Jhlt:
fprintf(f, "\tud2\n");
break;
case Jret0:
if (fn->dynalloc)
fprintf(f,
"\tmovq %%rbp, %%rsp\n"
"\tsubq $%"PRIu64", %%rsp\n",
e->fsz + e->nclob * 8);
for (r=&amd64_winabi_rclob[NCLR_WIN]; r>amd64_winabi_rclob;)
if (fn->reg & BIT(*--r)) {
itmp.arg[0] = TMP(*r);
emitf("popq %L0", &itmp, e);
}
if (e->fp == RBP)
fputs("\tleave\n", f);
else if (e->fsz)
fprintf(f,
"\taddq $%"PRIu64", %%rsp\n",
e->fsz);
fputs("\tret\n", f);
break;
case Jjmp:
Jmp:
if (b->s1 != b->link)
fprintf(f, "\tjmp %sbb%d\n",
T.asloc, id0+b->s1->id);
else
lbl = 0;
break;
default:
c = b->jmp.type - Jjf;
if (0 <= c && c <= NCmp) {
if (b->link == b->s2) {
s = b->s1;
b->s1 = b->s2;
b->s2 = s;
} else
c = cmpneg(c);
fprintf(f, "\tj%s %sbb%d\n", ctoa[c],
T.asloc, id0+b->s2->id);
goto Jmp;
}
die("unhandled jump %d", b->jmp.type);
}
}
id0 += fn->nblk;
}

View File

@ -18,7 +18,14 @@
* dce should be moved out...
*/
static int amatch(Addr *, Num *, Ref, Fn *);
typedef struct ANum ANum;
struct ANum {
char n, l, r;
Ins *i;
};
static int amatch(Addr *, Ref, int, ANum *, Fn *);
static int
noimm(Ref r, Fn *fn)
@ -87,23 +94,11 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
vgrow(&fn->mem, ++fn->nmem);
memset(&a, 0, sizeof a);
a.offset.type = CAddr;
n = stashbits(fn->con[r0.val].bits.i, KWIDE(k) ? 8 : 4);
/* quote the name so that we do not
* add symbol prefixes on the apple
* target variant
*/
n = stashbits(&fn->con[r0.val].bits, KWIDE(k) ? 8 : 4);
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
a.offset.sym.id = intern(buf);
fn->mem[fn->nmem-1] = a;
}
else if (op == Ocall && r == &i->arg[0]
&& rtype(r0) == RCon && fn->con[r0.val].type != CAddr) {
/* use a temporary register so that we
* produce an indirect call
*/
r1 = newtmp("isel", Kl, fn);
emit(Ocopy, Kl, r1, r0, R);
}
else if (op != Ocopy && k == Kl && noimm(r0, fn)) {
/* load constants that do not fit in
* a 32bit signed integer into a
@ -171,15 +166,11 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
m->base = r0;
}
}
else if (isxsel(op) && rtype(*r) == RCon) {
r1 = newtmp("isel", i->cls, fn);
emit(Ocopy, i->cls, r1, *r, R);
}
*r = r1;
}
static void
seladdr(Ref *r, Num *tn, Fn *fn)
seladdr(Ref *r, ANum *an, Fn *fn)
{
Addr a;
Ref r0;
@ -187,7 +178,7 @@ seladdr(Ref *r, Num *tn, Fn *fn)
r0 = *r;
if (rtype(r0) == RTmp) {
memset(&a, 0, sizeof a);
if (!amatch(&a, tn, r0, fn))
if (!amatch(&a, r0, an[r0.val].n, an, fn))
return;
if (!req(a.base, R))
if (a.offset.type == CAddr) {
@ -251,7 +242,7 @@ selcmp(Ref arg[2], int k, int swap, Fn *fn)
}
static void
sel(Ins i, Num *tn, Fn *fn)
sel(Ins i, ANum *an, Fn *fn)
{
Ref r0, r1, tmp[7];
int x, j, k, kc, sh, swap;
@ -413,10 +404,10 @@ sel(Ins i, Num *tn, Fn *fn)
if (i.op == Ostores)
i.op = Ostorew;
}
seladdr(&i.arg[1], tn, fn);
seladdr(&i.arg[1], an, fn);
goto Emit;
case_Oload:
seladdr(&i.arg[0], tn, fn);
seladdr(&i.arg[0], an, fn);
goto Emit;
case Odbgloc:
case Ocall:
@ -437,8 +428,7 @@ sel(Ins i, Num *tn, Fn *fn)
case Oexts:
case Otruncd:
case Ocast:
case_Oxsel:
case_Oext:
case_OExt:
Emit:
emiti(i);
i1 = curi; /* fixarg() can change curi */
@ -452,9 +442,7 @@ Emit:
break;
default:
if (isext(i.op))
goto case_Oext;
if (isxsel(i.op))
goto case_Oxsel;
goto case_OExt;
if (isload(i.op))
goto case_Oload;
if (iscmp(i.op, &kc, &x)) {
@ -508,88 +496,6 @@ flagi(Ins *i0, Ins *i)
return 0;
}
static Ins*
selsel(Fn *fn, Blk *b, Ins *i, Num *tn)
{
Ref r, cr[2];
int c, k, swap, gencmp, gencpy;
Ins *isel0, *isel1, *fi;
Tmp *t;
assert(i->op == Osel1);
for (isel0=i; b->ins<isel0; isel0--) {
if (isel0->op == Osel0)
break;
assert(isel0->op == Osel1);
}
assert(isel0->op == Osel0);
r = isel0->arg[0];
assert(rtype(r) == RTmp);
t = &fn->tmp[r.val];
fi = flagi(b->ins, isel0);
cr[0] = cr[1] = R;
gencmp = gencpy = swap = 0;
k = Kw;
c = Cine;
if (!fi || !req(fi->to, r)) {
gencmp = 1;
cr[0] = r;
cr[1] = CON_Z;
}
else if (iscmp(fi->op, &k, &c)) {
if (c == NCmpI+Cfeq
|| c == NCmpI+Cfne) {
/* these are selected as 'and'
* or 'or', so we check their
* result with Cine
*/
c = Cine;
goto Other;
}
swap = cmpswap(fi->arg, c);
if (swap)
c = cmpop(c);
if (t->nuse == 1) {
gencmp = 1;
cr[0] = fi->arg[0];
cr[1] = fi->arg[1];
*fi = (Ins){.op = Onop};
}
}
else if (fi->op == Oand && t->nuse == 1
&& (rtype(fi->arg[0]) == RTmp ||
rtype(fi->arg[1]) == RTmp)) {
fi->op = Oxtest;
fi->to = R;
if (rtype(fi->arg[1]) == RCon) {
r = fi->arg[1];
fi->arg[1] = fi->arg[0];
fi->arg[0] = r;
}
}
else {
Other:
/* since flags are not tracked in liveness,
* the result of the flag-setting instruction
* has to be marked as live
*/
if (t->nuse == 1)
gencpy = 1;
}
/* generate conditional moves */
for (isel1=i; isel0<isel1; --isel1) {
isel1->op = Oxsel+c;
sel(*isel1, tn, fn);
}
assert(!gencmp || !gencpy);
if (gencmp)
selcmp(cr, k, swap, fn);
if (gencpy)
emit(Ocopy, Kw, R, r, R);
*isel0 = (Ins){.op = Onop};
return isel0;
}
static void
seljmp(Blk *b, Fn *fn)
{
@ -619,7 +525,7 @@ seljmp(Blk *b, Fn *fn)
b->jmp.type = Jjf + Cine;
}
else if (iscmp(fi->op, &k, &c)
&& c != NCmpI+Cfeq /* see sel(), selsel() */
&& c != NCmpI+Cfeq /* see sel() */
&& c != NCmpI+Cfne) {
swap = cmpswap(fi->arg, c);
if (swap)
@ -653,225 +559,155 @@ seljmp(Blk *b, Fn *fn)
}
}
enum {
Pob,
Pbis,
Pois,
Pobis,
Pbi1,
Pobi1,
};
/* mgen generated code
*
* (with-vars (o b i s)
* (patterns
* (ob (add (con o) (tmp b)))
* (bis (add (tmp b) (mul (tmp i) (con s 1 2 4 8))))
* (ois (add (con o) (mul (tmp i) (con s 1 2 4 8))))
* (obis (add (con o) (tmp b) (mul (tmp i) (con s 1 2 4 8))))
* (bi1 (add (tmp b) (tmp i)))
* (obi1 (add (con o) (tmp b) (tmp i)))
* ))
*/
static int
opn(int op, int l, int r)
aref(Ref r, ANum *ai)
{
static uchar Oaddtbl[91] = {
2,
2,2,
4,4,5,
6,6,8,8,
4,4,9,10,9,
7,7,5,8,9,5,
4,4,12,10,12,12,12,
4,4,9,10,9,9,12,9,
11,11,5,8,9,5,12,9,5,
7,7,5,8,9,5,12,9,5,5,
11,11,5,8,9,5,12,9,5,5,5,
4,4,9,10,9,9,12,9,9,9,9,9,
7,7,5,8,9,5,12,9,5,5,5,9,5,
};
int t;
if (l < r)
t = l, l = r, r = t;
switch (op) {
case Omul:
if (2 <= l)
if (r == 0) {
return 3;
}
switch (rtype(r)) {
case RCon:
return 2;
case Oadd:
return Oaddtbl[(l + l*l)/2 + r];
case RTmp:
return ai[r.val].n;
default:
return 2;
die("constant or temporary expected");
}
}
static int
refn(Ref r, Num *tn, Con *con)
ascale(Ref r, Con *con)
{
int64_t n;
switch (rtype(r)) {
case RTmp:
if (!tn[r.val].n)
tn[r.val].n = 2;
return tn[r.val].n;
case RCon:
if (con[r.val].type != CBits)
return 1;
n = con[r.val].bits.i;
if (n == 8 || n == 4 || n == 2 || n == 1)
if (rtype(r) != RCon)
return 0;
return 1;
default:
return INT_MIN;
if (con[r.val].type != CBits)
return 0;
n = con[r.val].bits.i;
return n == 1 || n == 2 || n == 4 || n == 8;
}
}
static bits match[13] = {
[4] = BIT(Pob),
[5] = BIT(Pbi1),
[6] = BIT(Pob) | BIT(Pois),
[7] = BIT(Pob) | BIT(Pobi1),
[8] = BIT(Pbi1) | BIT(Pbis),
[9] = BIT(Pbi1) | BIT(Pobi1),
[10] = BIT(Pbi1) | BIT(Pbis) | BIT(Pobi1) | BIT(Pobis),
[11] = BIT(Pob) | BIT(Pobi1) | BIT(Pobis),
[12] = BIT(Pbi1) | BIT(Pobi1) | BIT(Pobis),
};
static uchar *matcher[] = {
[Pbi1] = (uchar[]){
1,3,1,3,2,0
},
[Pbis] = (uchar[]){
5,1,8,5,27,1,5,1,2,5,13,3,1,1,3,3,3,2,0,1,
3,3,3,2,3,1,0,1,29
},
[Pob] = (uchar[]){
1,3,0,3,1,0
},
[Pobi1] = (uchar[]){
5,3,9,9,10,33,12,35,45,1,5,3,11,9,7,9,4,9,
17,1,3,0,3,1,3,2,0,3,1,1,3,0,34,1,37,1,5,2,
5,7,2,7,8,37,29,1,3,0,1,32
},
[Pobis] = (uchar[]){
5,2,10,7,11,19,49,1,1,3,3,3,2,1,3,0,3,1,0,
1,3,0,5,1,8,5,25,1,5,1,2,5,13,3,1,1,3,3,3,
2,0,1,3,3,3,2,26,1,51,1,5,1,6,5,9,1,3,0,51,
3,1,1,3,0,45
},
[Pois] = (uchar[]){
1,3,0,1,3,3,3,2,0
},
};
/* end of generated code */
static void
anumber(Num *tn, Blk *b, Con *con)
anumber(ANum *ai, Blk *b, Con *con)
{
/* This should be made obsolete by a proper
* reassoc pass.
*
* Rules:
*
* RTmp(_) -> 0 tmp
* ( RTmp(_) -> 1 slot )
* RCon(_) -> 2 con
* 0 * 2 -> 3 s * i (when constant is 1,2,4,8)
*/
static char add[10][10] = {
[2] [4] = 4, [4] [2] = 4,
[2] [6] = 6, [6] [2] = 6,
[2] [7] = 7, [7] [2] = 7,
[0] [2] = 4, [2] [0] = 4, /* 4: o + b */
[0] [0] = 5, /* 5: b + s * i */
[0] [3] = 5, [3] [0] = 5,
[2] [3] = 6, [3] [2] = 6, /* 6: o + s * i */
[2] [5] = 7, [5] [2] = 7, /* 7: o + b + s * i */
[0] [6] = 7, [6] [0] = 7,
[4] [3] = 7, [3] [4] = 7,
};
int a, a1, a2, n1, n2, t1, t2;
Ins *i;
Num *n;
for (i=b->ins; i<&b->ins[b->nins]; i++) {
if (rtype(i->to) != RTmp)
if (rtype(i->to) == RTmp)
ai[i->to.val].i = i;
if (i->op != Oadd && i->op != Omul)
continue;
n = &tn[i->to.val];
n->l = i->arg[0];
n->r = i->arg[1];
n->nl = refn(n->l, tn, con);
n->nr = refn(n->r, tn, con);
n->n = opn(i->op, n->nl, n->nr);
a1 = aref(i->arg[0], ai);
a2 = aref(i->arg[1], ai);
t1 = a1 != 1 && a1 != 2;
t2 = a2 != 1 && a2 != 2;
if (i->op == Oadd) {
a = add[n1 = a1][n2 = a2];
if (t1 && a < add[0][a2])
a = add[n1 = 0][n2 = a2];
if (t2 && a < add[a1][0])
a = add[n1 = a1][n2 = 0];
if (t1 && t2 && a < add[0][0])
a = add[n1 = 0][n2 = 0];
} else {
n1 = n2 = a = 0;
if (ascale(i->arg[0], con) && t2)
a = 3, n1 = 2, n2 = 0;
if (t1 && ascale(i->arg[1], con))
a = 3, n1 = 0, n2 = 2;
}
ai[i->to.val].n = a;
ai[i->to.val].l = n1;
ai[i->to.val].r = n2;
}
static Ref
adisp(Con *c, Num *tn, Ref r, Fn *fn, int s)
{
Ref v[2];
int n;
while (!req(r, R)) {
assert(rtype(r) == RTmp);
n = refn(r, tn, fn->con);
if (!(match[n] & BIT(Pob)))
break;
runmatch(matcher[Pob], tn, r, v);
assert(rtype(v[0]) == RCon);
addcon(c, &fn->con[v[0].val], s);
r = v[1];
}
return r;
}
static int
amatch(Addr *a, Num *tn, Ref r, Fn *fn)
amatch(Addr *a, Ref r, int n, ANum *ai, Fn *fn)
{
static int pat[] = {Pobis, Pobi1, Pbis, Pois, Pbi1, -1};
Ref ro, rb, ri, rs, v[4];
Con *c, co;
int s, n, *p;
Ins *i;
int nl, nr, t, s;
Ref al, ar;
if (rtype(r) != RTmp)
if (rtype(r) == RCon) {
if (!addcon(&a->offset, &fn->con[r.val]))
err("unlikely sum of $%s and $%s",
str(a->offset.sym.id),
str(fn->con[r.val].sym.id));
return 1;
}
assert(rtype(r) == RTmp);
i = ai[r.val].i;
nl = ai[r.val].l;
nr = ai[r.val].r;
if (i) {
if (nl > nr) {
al = i->arg[1];
ar = i->arg[0];
t = nl, nl = nr, nr = t;
} else {
al = i->arg[0];
ar = i->arg[1];
}
}
switch (n) {
case 3: /* s * i */
a->index = al;
a->scale = fn->con[ar.val].bits.i;
return 0;
n = refn(r, tn, fn->con);
memset(v, 0, sizeof v);
for (p=pat; *p>=0; p++)
if (match[n] & BIT(*p)) {
runmatch(matcher[*p], tn, r, v);
case 5: /* b + s * i */
switch (nr) {
case 0:
if (fn->tmp[ar.val].slot != -1) {
al = i->arg[1];
ar = i->arg[0];
}
a->index = ar;
a->scale = 1;
break;
case 3:
amatch(a, ar, nr, ai, fn);
break;
}
if (*p < 0)
v[1] = r;
memset(&co, 0, sizeof co);
ro = v[0];
rb = adisp(&co, tn, v[1], fn, 1);
ri = v[2];
rs = v[3];
s = 1;
if (*p < 0 && co.type != CUndef)
if (amatch(a, tn, rb, fn))
return addcon(&a->offset, &co, 1);
if (!req(ro, R)) {
assert(rtype(ro) == RCon);
c = &fn->con[ro.val];
if (!addcon(&co, c, 1))
return 0;
}
if (!req(rs, R)) {
assert(rtype(rs) == RCon);
c = &fn->con[rs.val];
assert(c->type == CBits);
s = c->bits.i;
}
ri = adisp(&co, tn, ri, fn, s);
*a = (Addr){co, rb, ri, s};
if (rtype(ri) == RTmp)
if (fn->tmp[ri.val].slot != -1) {
if (a->scale != 1
|| fn->tmp[rb.val].slot != -1)
return 0;
a->base = ri;
a->index = rb;
}
if (!req(a->base, R)) {
assert(rtype(a->base) == RTmp);
s = fn->tmp[a->base.val].slot;
r = al;
/* fall through */
case 0:
s = fn->tmp[r.val].slot;
if (s != -1)
a->base = SLOT(s);
}
r = SLOT(s);
a->base = r;
return n || s != -1;
case 2: /* constants */
case 4: /* o + b */
case 6: /* o + s * i */
case 7: /* o + b + s * i */
amatch(a, ar, nr, ai, fn);
amatch(a, al, nl, ai, fn);
return 1;
default:
die("unreachable");
}
}
/* instruction selection
@ -886,7 +722,7 @@ amd64_isel(Fn *fn)
uint a;
int n, al;
int64_t sz;
Num *num;
ANum *ainfo;
/* assign slots to fast allocs */
b = fn->start;
@ -905,13 +741,12 @@ amd64_isel(Fn *fn)
die("alloc too large");
fn->tmp[i->to.val].slot = fn->slot;
fn->slot += sz;
fn->salign = 2 + al - Oalloc;
*i = (Ins){.op = Onop};
}
/* process basic blocks */
n = fn->ntmp;
num = emalloc(n * sizeof num[0]);
ainfo = emalloc(n * sizeof ainfo[0]);
for (b=fn->start; b; b=b->link) {
curi = &insb[NIns];
for (sb=(Blk*[3]){b->s1, b->s2, 0}; *sb; sb++)
@ -920,20 +755,15 @@ amd64_isel(Fn *fn)
assert(a+1 < p->narg);
fixarg(&p->arg[a], p->cls, 0, fn);
}
memset(num, 0, n * sizeof num[0]);
anumber(num, b, fn->con);
memset(ainfo, 0, n * sizeof ainfo[0]);
anumber(ainfo, b, fn->con);
seljmp(b, fn);
for (i=&b->ins[b->nins]; i!=b->ins;) {
--i;
assert(i->op != Osel0);
if (i->op == Osel1)
i = selsel(fn, b, i, num);
else
sel(*i, num, fn);
for (i=&b->ins[b->nins]; i!=b->ins;)
sel(*--i, ainfo, fn);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
idup(b, curi, &insb[NIns]-curi);
}
free(num);
free(ainfo);
if (debug['I']) {
fprintf(stderr, "\n> After instruction selection:\n");

View File

@ -228,8 +228,8 @@ int amd64_sysv_rsave[] = {
int amd64_sysv_rclob[] = {RBX, R12, R13, R14, R15, -1};
MAKESURE(sysv_arrays_ok,
sizeof amd64_sysv_rsave == (NGPS_SYSV+NFPS+1) * sizeof(int) &&
sizeof amd64_sysv_rclob == (NCLR_SYSV+1) * sizeof(int)
sizeof amd64_sysv_rsave == (NGPS+NFPS+1) * sizeof(int) &&
sizeof amd64_sysv_rclob == (NCLR+1) * sizeof(int)
);
/* layout of call's second argument (RCall)
@ -508,7 +508,8 @@ split(Fn *fn, Blk *b)
++fn->nblk;
bn = newblk();
idup(bn, curi, &insb[NIns]-curi);
bn->nins = &insb[NIns] - curi;
idup(&bn->ins, curi, bn->nins);
curi = &insb[NIns];
bn->visit = ++b->visit;
strf(bn->name, "%s.%d", b->name, b->visit);
@ -656,9 +657,9 @@ void
amd64_sysv_abi(Fn *fn)
{
Blk *b;
Ins *i, *i0;
Ins *i, *i0, *ip;
RAlloc *ral;
int n0, n1, ioff, fa;
int n, fa;
for (b=fn->start; b; b=b->link)
b->visit = 0;
@ -668,13 +669,12 @@ amd64_sysv_abi(Fn *fn)
if (!ispar(i->op))
break;
fa = selpar(fn, b->ins, i);
n0 = &insb[NIns] - curi;
ioff = i - b->ins;
n1 = b->nins - ioff;
vgrow(&b->ins, n0+n1);
icpy(b->ins+n0, b->ins+ioff, n1);
icpy(b->ins, curi, n0);
b->nins = n0+n1;
n = b->nins - (i - b->ins) + (&insb[NIns] - curi);
i0 = alloc(n * sizeof(Ins));
ip = icpy(ip = i0, curi, &insb[NIns] - curi);
ip = icpy(ip, i, &b->ins[b->nins] - i);
b->nins = n;
b->ins = i0;
/* lower calls, returns, and vararg instructions */
ral = 0;
@ -711,7 +711,8 @@ amd64_sysv_abi(Fn *fn)
if (b == fn->start)
for (; ral; ral=ral->link)
emiti(ral->i);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
} while (b != fn->start);
if (debug['A']) {

View File

@ -19,21 +19,20 @@ amd64_memargs(int op)
.nfpr = NFPR, \
.rglob = BIT(RBP) | BIT(RSP), \
.nrglob = 2, \
.rsave = amd64_sysv_rsave, \
.nrsave = {NGPS, NFPS}, \
.retregs = amd64_sysv_retregs, \
.argregs = amd64_sysv_argregs, \
.memargs = amd64_memargs, \
.abi0 = elimsb, \
.abi1 = amd64_sysv_abi, \
.isel = amd64_isel, \
.cansel = 1,
.emitfn = amd64_emitfn, \
Target T_amd64_sysv = {
.name = "amd64_sysv",
.emitfin = elf_emitfin,
.asloc = ".L",
.abi1 = amd64_sysv_abi,
.rsave = amd64_sysv_rsave,
.nrsave = {NGPS_SYSV, NFPS},
.retregs = amd64_sysv_retregs,
.argregs = amd64_sysv_argregs,
.emitfn = amd64_sysv_emitfn,
AMD64_COMMON
};
@ -43,25 +42,5 @@ Target T_amd64_apple = {
.emitfin = macho_emitfin,
.asloc = "L",
.assym = "_",
.abi1 = amd64_sysv_abi,
.rsave = amd64_sysv_rsave,
.nrsave = {NGPS_SYSV, NFPS},
.retregs = amd64_sysv_retregs,
.argregs = amd64_sysv_argregs,
.emitfn = amd64_sysv_emitfn,
AMD64_COMMON
};
Target T_amd64_win = {
.name = "amd64_win",
.windows = 1,
.emitfin = pe_emitfin,
.asloc = "L",
.abi1 = amd64_winabi_abi,
.rsave = amd64_winabi_rsave,
.nrsave = {NGPS_WIN, NFPS},
.retregs = amd64_winabi_retregs,
.argregs = amd64_winabi_argregs,
.emitfn = amd64_winabi_emitfn,
AMD64_COMMON
};

View File

@ -1,763 +0,0 @@
#include "all.h"
#include <stdbool.h>
typedef enum ArgPassStyle {
APS_Invalid = 0,
APS_Register,
APS_InlineOnStack,
APS_CopyAndPointerInRegister,
APS_CopyAndPointerOnStack,
APS_VarargsTag,
APS_EnvTag,
} ArgPassStyle;
typedef struct ArgClass {
Typ* type;
ArgPassStyle style;
int align;
uint size;
int cls;
Ref ref;
} ArgClass;
typedef struct ExtraAlloc ExtraAlloc;
struct ExtraAlloc {
Ins instr;
ExtraAlloc* link;
};
#define ALIGN_DOWN(n, a) ((n) & ~((a)-1))
#define ALIGN_UP(n, a) ALIGN_DOWN((n) + (a)-1, (a))
// Number of stack bytes required be reserved for the callee.
#define SHADOW_SPACE_SIZE 32
int amd64_winabi_rsave[] = {RCX, RDX, R8, R9, R10, R11, RAX, XMM0,
XMM1, XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, XMM8,
XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, -1};
int amd64_winabi_rclob[] = {RBX, R12, R13, R14, R15, RSI, RDI, -1};
MAKESURE(winabi_arrays_ok,
sizeof amd64_winabi_rsave == (NGPS_WIN + NFPS + 1) * sizeof(int) &&
sizeof amd64_winabi_rclob == (NCLR_WIN + 1) * sizeof(int));
// layout of call's second argument (RCall)
//
// bit 0: rax returned
// bit 1: xmm0 returned
// bits 23: 0
// bits 4567: rcx, rdx, r8, r9 passed
// bits 89ab: xmm0,1,2,3 passed
// bit c: env call (rax passed)
// bits d..1f: 0
bits amd64_winabi_retregs(Ref r, int p[2]) {
assert(rtype(r) == RCall);
bits b = 0;
int num_int_returns = r.val & 1;
int num_float_returns = r.val & 2;
if (num_int_returns == 1) {
b |= BIT(RAX);
} else {
b |= BIT(XMM0);
}
if (p) {
p[0] = num_int_returns;
p[1] = num_float_returns;
}
return b;
}
static uint popcnt(bits b) {
b = (b & 0x5555555555555555) + ((b >> 1) & 0x5555555555555555);
b = (b & 0x3333333333333333) + ((b >> 2) & 0x3333333333333333);
b = (b & 0x0f0f0f0f0f0f0f0f) + ((b >> 4) & 0x0f0f0f0f0f0f0f0f);
b += (b >> 8);
b += (b >> 16);
b += (b >> 32);
return b & 0xff;
}
bits amd64_winabi_argregs(Ref r, int p[2]) {
assert(rtype(r) == RCall);
// On SysV, these are counts. Here, a count isn't sufficient, we actually need
// to know which ones are in use because they're not necessarily contiguous.
int int_passed = (r.val >> 4) & 15;
int float_passed = (r.val >> 8) & 15;
bool env_param = (r.val >> 12) & 1;
bits b = 0;
b |= (int_passed & 1) ? BIT(RCX) : 0;
b |= (int_passed & 2) ? BIT(RDX) : 0;
b |= (int_passed & 4) ? BIT(R8) : 0;
b |= (int_passed & 8) ? BIT(R9) : 0;
b |= (float_passed & 1) ? BIT(XMM0) : 0;
b |= (float_passed & 2) ? BIT(XMM1) : 0;
b |= (float_passed & 4) ? BIT(XMM2) : 0;
b |= (float_passed & 8) ? BIT(XMM3) : 0;
b |= env_param ? BIT(RAX) : 0;
if (p) {
// TODO: The only place this is used is live.c. I'm not sure what should be
// returned here wrt to using the same counter for int/float regs on win.
// For now, try the number of registers in use even though they're not
// contiguous.
p[0] = popcnt(int_passed);
p[1] = popcnt(float_passed);
}
return b;
}
typedef struct RegisterUsage {
// Counter for both int/float as they're counted together. Only if the bool's
// set in regs_passed is the given register *actually* needed for a value
// (i.e. needs to be saved, etc.).
int num_regs_passed;
// Indexed first by 0=int, 1=float, use KBASE(cls).
// Indexed second by register index in calling convention, so for integer,
// 0=RCX, 1=RDX, 2=R8, 3=R9, and for float XMM0, XMM1, XMM2, XMM3.
bool regs_passed[2][4];
bool rax_returned;
bool xmm0_returned;
// This is also used as where the va_start will start for varargs functions
// (there's no 'Oparv', so we need to keep track of a count here.)
int num_named_args_passed;
// This is set when classifying the arguments for a call (but not when
// classifying the parameters of a function definition).
bool is_varargs_call;
bool has_env;
} RegisterUsage;
static int register_usage_to_call_arg_value(RegisterUsage reg_usage) {
return (reg_usage.rax_returned << 0) | //
(reg_usage.xmm0_returned << 1) | //
(reg_usage.regs_passed[0][0] << 4) | //
(reg_usage.regs_passed[0][1] << 5) | //
(reg_usage.regs_passed[0][2] << 6) | //
(reg_usage.regs_passed[0][3] << 7) | //
(reg_usage.regs_passed[1][0] << 8) | //
(reg_usage.regs_passed[1][1] << 9) | //
(reg_usage.regs_passed[1][2] << 10) | //
(reg_usage.regs_passed[1][3] << 11) | //
(reg_usage.has_env << 12);
}
// Assigns the argument to a register if there's any left according to the
// calling convention, and updates the regs_passed bools. Otherwise marks the
// value as needing stack space to be passed.
static void assign_register_or_stack(RegisterUsage* reg_usage,
ArgClass* arg,
bool is_float,
bool by_copy) {
if (reg_usage->num_regs_passed == 4) {
arg->style = by_copy ? APS_CopyAndPointerOnStack : APS_InlineOnStack;
} else {
reg_usage->regs_passed[is_float][reg_usage->num_regs_passed] = true;
++reg_usage->num_regs_passed;
arg->style = by_copy ? APS_CopyAndPointerInRegister : APS_Register;
}
++reg_usage->num_named_args_passed;
}
static bool type_is_by_copy(Typ* type) {
// Note that only these sizes are passed by register, even though e.g. a
// 5 byte struct would "fit", it still is passed by copy-and-pointer.
return type->isdark || (type->size != 1 && type->size != 2 &&
type->size != 4 && type->size != 8);
}
// This function is used for both arguments and parameters.
// begin_instr should either point at the first Oarg or Opar, and end_instr
// should point past the last one (so to the Ocall for arguments, or to the
// first 'real' instruction of the function for parameters).
static void classify_arguments(RegisterUsage* reg_usage,
Ins* begin_instr,
Ins* end_instr,
ArgClass* arg_classes,
Ref* env) {
ArgClass* arg = arg_classes;
// For each argument, determine how it will be passed (int, float, stack)
// and update the `reg_usage` counts. Additionally, fill out arg_classes for
// each argument.
for (Ins* instr = begin_instr; instr < end_instr; ++instr, ++arg) {
switch (instr->op) {
case Oarg:
case Opar:
assign_register_or_stack(reg_usage, arg, KBASE(instr->cls),
/*by_copy=*/false);
arg->cls = instr->cls;
arg->align = 3;
arg->size = 8;
break;
case Oargc:
case Oparc: {
int typ_index = instr->arg[0].val;
Typ* type = &typ[typ_index];
bool by_copy = type_is_by_copy(type);
assign_register_or_stack(reg_usage, arg, /*is_float=*/false, by_copy);
arg->cls = Kl;
if (!by_copy && type->size <= 4) {
arg->cls = Kw;
}
arg->align = 3;
arg->size = type->size;
break;
}
case Oarge:
*env = instr->arg[0];
arg->style = APS_EnvTag;
reg_usage->has_env = true;
break;
case Opare:
*env = instr->to;
arg->style = APS_EnvTag;
reg_usage->has_env = true;
break;
case Oargv:
reg_usage->is_varargs_call = true;
arg->style = APS_VarargsTag;
break;
}
}
if (reg_usage->has_env && reg_usage->is_varargs_call) {
die("can't use env with varargs");
}
// During a varargs call, float arguments have to be duplicated to their
// associated integer register, so mark them as in-use too.
if (reg_usage->is_varargs_call) {
for (int i = 0; i < 4; ++i) {
if (reg_usage->regs_passed[/*float*/ 1][i]) {
reg_usage->regs_passed[/*int*/ 0][i] = true;
}
}
}
}
static bool is_integer_type(int ty) {
assert(ty >= 0 && ty < 4 && "expecting Kw Kl Ks Kd");
return KBASE(ty) == 0;
}
static Ref register_for_arg(int cls, int counter) {
assert(counter < 4);
if (is_integer_type(cls)) {
return TMP(amd64_winabi_rsave[counter]);
} else {
return TMP(XMM0 + counter);
}
}
static Ins* lower_call(Fn* func,
Blk* block,
Ins* call_instr,
ExtraAlloc** pextra_alloc) {
// Call arguments are instructions. Walk through them to find the end of the
// call+args that we need to process (and return the instruction past the body
// of the instruction for continuing processing).
Ins* instr_past_args = call_instr - 1;
for (; instr_past_args >= block->ins; --instr_past_args) {
if (!isarg(instr_past_args->op)) {
break;
}
}
Ins* earliest_arg_instr = instr_past_args + 1;
// Don't need an ArgClass for the call itself, so one less than the total
// number of instructions we're dealing with.
uint num_args = call_instr - earliest_arg_instr;
ArgClass* arg_classes = alloc(num_args * sizeof(ArgClass));
RegisterUsage reg_usage = {0};
ArgClass ret_arg_class = {0};
// Ocall's two arguments are the the function to be called in 0, and, if the
// the function returns a non-basic type, then arg[1] is a reference to the
// type of the return. req checks if Refs are equal; `R` is 0.
bool il_has_struct_return = !req(call_instr->arg[1], R);
bool is_struct_return = false;
if (il_has_struct_return) {
Typ* ret_type = &typ[call_instr->arg[1].val];
is_struct_return = type_is_by_copy(ret_type);
if (is_struct_return) {
assign_register_or_stack(&reg_usage, &ret_arg_class, /*is_float=*/false,
/*by_copy=*/true);
}
ret_arg_class.size = ret_type->size;
}
Ref env = R;
classify_arguments(&reg_usage, earliest_arg_instr, call_instr, arg_classes,
&env);
// We now know which arguments are on the stack and which are in registers, so
// we can allocate the correct amount of space to stash the stack-located ones
// into.
uint stack_usage = 0;
for (uint i = 0; i < num_args; ++i) {
ArgClass* arg = &arg_classes[i];
// stack_usage only accounts for pushes that are for values that don't have
// enough registers. Large struct copies are alloca'd separately, and then
// only have (potentially) 8 bytes to add to stack_usage here.
if (arg->style == APS_InlineOnStack) {
if (arg->align > 4) {
err("win abi cannot pass alignments > 16");
}
stack_usage += arg->size;
} else if (arg->style == APS_CopyAndPointerOnStack) {
stack_usage += 8;
}
}
stack_usage = ALIGN_UP(stack_usage, 16);
// Note that here we're logically 'after' the call (due to emitting
// instructions in reverse order), so we're doing a negative stack
// allocation to clean up after the call.
Ref stack_size_ref =
getcon(-(int64_t)(stack_usage + SHADOW_SPACE_SIZE), func);
emit(Osalloc, Kl, R, stack_size_ref, R);
ExtraAlloc* return_pad = NULL;
if (is_struct_return) {
return_pad = alloc(sizeof(ExtraAlloc));
Ref ret_pad_ref = newtmp("abi.ret_pad", Kl, func);
return_pad->instr =
(Ins){Oalloc8, Kl, ret_pad_ref, {getcon(ret_arg_class.size, func)}};
return_pad->link = (*pextra_alloc);
*pextra_alloc = return_pad;
reg_usage.rax_returned = true;
emit(Ocopy, call_instr->cls, call_instr->to, TMP(RAX), R);
} else {
if (il_has_struct_return) {
// In the case that at the IL level, a struct return was specified, but as
// far as the calling convention is concerned it's not actually by
// pointer, we need to store the return value into an alloca because
// subsequent IL will still be treating the function return as a pointer.
ExtraAlloc* return_copy = alloc(sizeof(ExtraAlloc));
return_copy->instr =
(Ins){Oalloc8, Kl, call_instr->to, {getcon(8, func)}};
return_copy->link = (*pextra_alloc);
*pextra_alloc = return_copy;
Ref copy = newtmp("abi.copy", Kl, func);
emit(Ostorel, 0, R, copy, call_instr->to);
emit(Ocopy, Kl, copy, TMP(RAX), R);
reg_usage.rax_returned = true;
} else if (is_integer_type(call_instr->cls)) {
// Only a basic type returned from the call, integer.
emit(Ocopy, call_instr->cls, call_instr->to, TMP(RAX), R);
reg_usage.rax_returned = true;
} else {
// Basic type, floating point.
emit(Ocopy, call_instr->cls, call_instr->to, TMP(XMM0), R);
reg_usage.xmm0_returned = true;
}
}
// Emit the actual call instruction. There's no 'to' value by this point
// because we've lowered it into register manipulation (that's the `R`),
// arg[0] of the call is the function, and arg[1] is register usage is
// documented as above (copied from SysV).
emit(Ocall, call_instr->cls, R, call_instr->arg[0],
CALL(register_usage_to_call_arg_value(reg_usage)));
if (!req(R, env)) {
// If there's an env arg to be passed, it gets stashed in RAX.
emit(Ocopy, Kl, TMP(RAX), env, R);
}
if (reg_usage.is_varargs_call) {
// Any float arguments need to be duplicated to integer registers. This is
// required by the calling convention so that dumping to shadow space can be
// done without a prototype and for varargs.
#define DUP_IF_USED(index, floatreg, intreg) \
if (reg_usage.regs_passed[/*float*/ 1][index]) { \
emit(Ocast, Kl, TMP(intreg), TMP(floatreg), R); \
}
DUP_IF_USED(0, XMM0, RCX);
DUP_IF_USED(1, XMM1, RDX);
DUP_IF_USED(2, XMM2, R8);
DUP_IF_USED(3, XMM3, R9);
#undef DUP_IF_USED
}
int reg_counter = 0;
if (is_struct_return) {
Ref first_reg = register_for_arg(Kl, reg_counter++);
emit(Ocopy, Kl, first_reg, return_pad->instr.to, R);
}
// This is where we actually do the load of values into registers or into
// stack slots.
Ref arg_stack_slots = newtmp("abi.args", Kl, func);
uint slot_offset = SHADOW_SPACE_SIZE;
ArgClass* arg = arg_classes;
for (Ins* instr = earliest_arg_instr; instr != call_instr; ++instr, ++arg) {
switch (arg->style) {
case APS_Register: {
Ref into = register_for_arg(arg->cls, reg_counter++);
if (instr->op == Oargc) {
// If this is a small struct being passed by value. The value in the
// instruction in this case is a pointer, but it needs to be loaded
// into the register.
emit(Oload, arg->cls, into, instr->arg[1], R);
} else {
// Otherwise, a normal value passed in a register.
emit(Ocopy, instr->cls, into, instr->arg[0], R);
}
break;
}
case APS_InlineOnStack: {
Ref slot = newtmp("abi.off", Kl, func);
if (instr->op == Oargc) {
// This is a small struct, so it's not passed by copy, but the
// instruction is a pointer. So we need to copy it into the stack
// slot. (And, remember that these are emitted backwards, so store,
// then load.)
Ref smalltmp = newtmp("abi.smalltmp", arg->cls, func);
emit(Ostorel, 0, R, smalltmp, slot);
emit(Oload, arg->cls, smalltmp, instr->arg[1], R);
} else {
// Stash the value into the stack slot.
emit(Ostorel, 0, R, instr->arg[0], slot);
}
emit(Oadd, Kl, slot, arg_stack_slots, getcon(slot_offset, func));
slot_offset += arg->size;
break;
}
case APS_CopyAndPointerInRegister:
case APS_CopyAndPointerOnStack: {
// Alloca a space to copy into, and blit the value from the instr to the
// copied location.
ExtraAlloc* arg_copy = alloc(sizeof(ExtraAlloc));
Ref copy_ref = newtmp("abi.copy", Kl, func);
arg_copy->instr =
(Ins){Oalloc8, Kl, copy_ref, {getcon(arg->size, func)}};
arg_copy->link = (*pextra_alloc);
*pextra_alloc = arg_copy;
emit(Oblit1, 0, R, INT(arg->size), R);
emit(Oblit0, 0, R, instr->arg[1], copy_ref);
// Now load the pointer into the correct register or stack slot.
if (arg->style == APS_CopyAndPointerInRegister) {
Ref into = register_for_arg(arg->cls, reg_counter++);
emit(Ocopy, Kl, into, copy_ref, R);
} else {
assert(arg->style == APS_CopyAndPointerOnStack);
Ref slot = newtmp("abi.off", Kl, func);
emit(Ostorel, 0, R, copy_ref, slot);
emit(Oadd, Kl, slot, arg_stack_slots, getcon(slot_offset, func));
slot_offset += 8;
}
break;
}
case APS_EnvTag:
case APS_VarargsTag:
// Nothing to do here, see right before the call for reg dupe.
break;
case APS_Invalid:
die("unreachable");
}
}
if (stack_usage) {
// The last (first in call order) thing we do is allocate the the stack
// space we're going to fill with temporaries.
emit(Osalloc, Kl, arg_stack_slots,
getcon(stack_usage + SHADOW_SPACE_SIZE, func), R);
} else {
// When there's no usage for temporaries, we can add this into the other
// alloca, but otherwise emit it separately (not storing into a reference)
// so that it doesn't get removed later for being useless.
emit(Osalloc, Kl, R, getcon(SHADOW_SPACE_SIZE, func), R);
}
return instr_past_args;
}
static void lower_block_return(Fn* func, Blk* block) {
int jmp_type = block->jmp.type;
if (!isret(jmp_type) || jmp_type == Jret0) {
return;
}
// Save the argument, and set the block to be a void return because once it's
// lowered it's handled by the the register/stack manipulation.
Ref ret_arg = block->jmp.arg;
block->jmp.type = Jret0;
RegisterUsage reg_usage = {0};
if (jmp_type == Jretc) {
Typ* type = &typ[func->retty];
if (type_is_by_copy(type)) {
assert(rtype(func->retr) == RTmp);
emit(Ocopy, Kl, TMP(RAX), func->retr, R);
emit(Oblit1, 0, R, INT(type->size), R);
emit(Oblit0, 0, R, ret_arg, func->retr);
} else {
emit(Oload, Kl, TMP(RAX), ret_arg, R);
}
reg_usage.rax_returned = true;
} else {
int k = jmp_type - Jretw;
if (is_integer_type(k)) {
emit(Ocopy, k, TMP(RAX), ret_arg, R);
reg_usage.rax_returned = true;
} else {
emit(Ocopy, k, TMP(XMM0), ret_arg, R);
reg_usage.xmm0_returned = true;
}
}
block->jmp.arg = CALL(register_usage_to_call_arg_value(reg_usage));
}
static void lower_vastart(Fn* func,
RegisterUsage* param_reg_usage,
Ref valist) {
assert(func->vararg);
// In varargs functions:
// 1. the int registers are already dumped to the shadow stack space;
// 2. any parameters passed in floating point registers have
// been duplicated to the integer registers
// 3. we ensure (later) that for varargs functions we're always using an rbp
// frame pointer.
// So, the ... argument is just indexed past rbp by the number of named values
// that were actually passed.
Ref offset = newtmp("abi.vastart", Kl, func);
emit(Ostorel, 0, R, offset, valist);
// *8 for sizeof(u64), +16 because the return address and rbp have been pushed
// by the time we get to the body of the function.
emit(Oadd, Kl, offset, TMP(RBP),
getcon(param_reg_usage->num_named_args_passed * 8 + 16, func));
}
static void lower_vaarg(Fn* func, Ins* vaarg_instr) {
// va_list is just a void** on winx64, so load the pointer, then load the
// argument from that pointer, then increment the pointer to the next arg.
// (All emitted backwards as usual.)
Ref inc = newtmp("abi.vaarg.inc", Kl, func);
Ref ptr = newtmp("abi.vaarg.ptr", Kl, func);
emit(Ostorel, 0, R, inc, vaarg_instr->arg[0]);
emit(Oadd, Kl, inc, ptr, getcon(8, func));
emit(Oload, vaarg_instr->cls, vaarg_instr->to, ptr, R);
emit(Oload, Kl, ptr, vaarg_instr->arg[0], R);
}
static void lower_args_for_block(Fn* func,
Blk* block,
RegisterUsage* param_reg_usage,
ExtraAlloc** pextra_alloc) {
// global temporary buffer used by emit. Reset to the end, and predecremented
// when adding to it.
curi = &insb[NIns];
lower_block_return(func, block);
if (block->nins) {
// Work backwards through the instructions, either copying them unchanged,
// or modifying as necessary.
for (Ins* instr = &block->ins[block->nins - 1]; instr >= block->ins;) {
switch (instr->op) {
case Ocall:
instr = lower_call(func, block, instr, pextra_alloc);
break;
case Ovastart:
lower_vastart(func, param_reg_usage, instr->arg[0]);
--instr;
break;
case Ovaarg:
lower_vaarg(func, instr);
--instr;
break;
case Oarg:
case Oargc:
die("unreachable");
default:
emiti(*instr);
--instr;
break;
}
}
}
// This it the start block, which is processed last. Add any allocas that
// other blocks needed.
bool is_start_block = block == func->start;
if (is_start_block) {
for (ExtraAlloc* ea = *pextra_alloc; ea; ea = ea->link) {
emiti(ea->instr);
}
}
// emit/emiti add instructions from the end to the beginning of the temporary
// global buffer. dup the final version into the final block storage.
block->nins = &insb[NIns] - curi;
idup(block, curi, block->nins);
}
static Ins* find_end_of_func_parameters(Blk* start_block) {
Ins* i;
for (i = start_block->ins; i < &start_block->ins[start_block->nins]; ++i) {
if (!ispar(i->op)) {
break;
}
}
return i;
}
// Copy from registers/stack into values.
static RegisterUsage lower_func_parameters(Fn* func) {
// This is half-open, so end points after the last Opar.
Blk* start_block = func->start;
Ins* start_of_params = start_block->ins;
Ins* end_of_params = find_end_of_func_parameters(start_block);
size_t num_params = end_of_params - start_of_params;
ArgClass* arg_classes = alloc(num_params * sizeof(ArgClass));
ArgClass arg_ret = {0};
// global temporary buffer used by emit. Reset to the end, and predecremented
// when adding to it.
curi = &insb[NIns];
int reg_counter = 0;
RegisterUsage reg_usage = {0};
if (func->retty >= 0) {
bool by_copy = type_is_by_copy(&typ[func->retty]);
if (by_copy) {
assign_register_or_stack(&reg_usage, &arg_ret, /*is_float=*/false,
by_copy);
Ref ret_ref = newtmp("abi.ret", Kl, func);
emit(Ocopy, Kl, ret_ref, TMP(RCX), R);
func->retr = ret_ref;
++reg_counter;
}
}
Ref env = R;
classify_arguments(&reg_usage, start_of_params, end_of_params, arg_classes,
&env);
func->reg = amd64_winabi_argregs(
CALL(register_usage_to_call_arg_value(reg_usage)), NULL);
// Copy from the registers or stack slots into the named parameters. Depending
// on how they're passed, they either need to be copied or loaded.
ArgClass* arg = arg_classes;
uint slot_offset = SHADOW_SPACE_SIZE / 4 + 4;
for (Ins* instr = start_of_params; instr < end_of_params; ++instr, ++arg) {
switch (arg->style) {
case APS_Register: {
Ref from = register_for_arg(arg->cls, reg_counter++);
// If it's a struct at the IL level, we need to copy the register into
// an alloca so we have something to point at (same for InlineOnStack).
if (instr->op == Oparc) {
arg->ref = newtmp("abi", Kl, func);
emit(Ostorel, 0, R, arg->ref, instr->to);
emit(Ocopy, instr->cls, arg->ref, from, R);
emit(Oalloc8, Kl, instr->to, getcon(arg->size, func), R);
} else {
emit(Ocopy, instr->cls, instr->to, from, R);
}
break;
}
case APS_InlineOnStack:
if (instr->op == Oparc) {
arg->ref = newtmp("abi", Kl, func);
emit(Ostorel, 0, R, arg->ref, instr->to);
emit(Ocopy, instr->cls, arg->ref, SLOT(-slot_offset), R);
emit(Oalloc8, Kl, instr->to, getcon(arg->size, func), R);
} else {
emit(Ocopy, Kl, instr->to, SLOT(-slot_offset), R);
}
slot_offset += 2;
break;
case APS_CopyAndPointerOnStack:
emit(Oload, Kl, instr->to, SLOT(-slot_offset), R);
slot_offset += 2;
break;
case APS_CopyAndPointerInRegister: {
// Because this has to be a copy (that we own), it is sufficient to just
// copy the register to the target.
Ref from = register_for_arg(Kl, reg_counter++);
emit(Ocopy, Kl, instr->to, from, R);
break;
}
case APS_EnvTag:
break;
case APS_VarargsTag:
case APS_Invalid:
die("unreachable");
}
}
// If there was an `env`, it was passed in RAX, so copy it into the env ref.
if (!req(R, env)) {
emit(Ocopy, Kl, env, TMP(RAX), R);
}
int num_created_instrs = &insb[NIns] - curi;
int num_other_after_instrs = (int)(start_block->nins - num_params);
int new_total_instrs = num_other_after_instrs + num_created_instrs;
Ins* new_instrs = vnew(new_total_instrs, sizeof(Ins), PFn);
Ins* instr_p = icpy(new_instrs, curi, num_created_instrs);
icpy(instr_p, end_of_params, num_other_after_instrs);
start_block->nins = new_total_instrs;
start_block->ins = new_instrs;
return reg_usage;
}
// The main job of this function is to lower generic instructions into the
// specific details of how arguments are passed, and parameters are
// interpreted for win x64. A useful reference is
// https://learn.microsoft.com/en-us/cpp/build/x64-calling-convention .
//
// Some of the major differences from SysV if you're comparing the code
// (non-exhaustive):
// - only 4 int and 4 float regs are used
// - when an int register is assigned a value, its associated float register is
// left unused (and vice versa). i.e. there's only one counter as you assign
// arguments to registers.
// - any structs that aren't 1/2/4/8 bytes in size are passed by pointer, not
// by copying them into the stack. So e.g. if you pass something like
// `struct { void*, int64_t }` by value, it first needs to be copied to
// another alloca (in order to maintain value semantics at the language
// level), then the pointer to that copy is treated as a regular integer
// argument (which then itself may *also* be copied to the stack in the case
// there's no integer register remaining.)
// - when calling a varargs functions, floating point values must be duplicated
// integer registers. Along with the above restrictions, this makes varargs
// handling simpler for the callee than SysV.
void amd64_winabi_abi(Fn* func) {
// The first thing to do is lower incoming parameters to this function.
RegisterUsage param_reg_usage = lower_func_parameters(func);
// This is the second larger part of the job. We walk all blocks, and rewrite
// instructions returns, calls, and handling of varargs into their win x64
// specific versions. Any other instructions are just passed through unchanged
// by using `emiti`.
// Skip over the entry block, and do it at the end so that our later
// modifications can add allocations to the start block. In particular, we
// need to add stack allocas for copies when structs are passed or returned by
// value.
ExtraAlloc* extra_alloc = NULL;
for (Blk* block = func->start->link; block; block = block->link) {
lower_args_for_block(func, block, &param_reg_usage, &extra_alloc);
}
lower_args_for_block(func, func->start, &param_reg_usage, &extra_alloc);
if (debug['A']) {
fprintf(stderr, "\n> After ABI lowering:\n");
printfn(func, stderr);
}
}

View File

@ -90,7 +90,7 @@ isfloatv(Typ *t, char *cls)
static void
typclass(Class *c, Typ *t, int *gp, int *fp)
{
uint64_t sz, hfasz;
uint64_t sz;
uint n;
sz = (t->size + 7) & -8;
@ -103,21 +103,7 @@ typclass(Class *c, Typ *t, int *gp, int *fp)
if (t->align > 3)
err("alignments larger than 8 are not supported");
c->size = sz;
c->hfa.base = Kx;
c->ishfa = isfloatv(t, &c->hfa.base);
hfasz = t->size/(KWIDE(c->hfa.base) ? 8 : 4);
c->ishfa &= !t->isdark && hfasz <= 4;
c->hfa.size = hfasz;
if (c->ishfa) {
for (n=0; n<hfasz; n++, c->nfp++) {
c->reg[n] = *fp++;
c->cls[n] = c->hfa.base;
}
c->nreg = n;
}
else if (t->isdark || sz > 16 || sz == 0) {
if (t->isdark || sz > 16 || sz == 0) {
/* large structs are replaced by a
* pointer to some caller-allocated
* memory */
@ -126,15 +112,27 @@ typclass(Class *c, Typ *t, int *gp, int *fp)
c->ngp = 1;
*c->reg = *gp;
*c->cls = Kl;
return;
}
else {
c->size = sz;
c->hfa.base = Kx;
c->ishfa = isfloatv(t, &c->hfa.base);
c->hfa.size = t->size/(KWIDE(c->hfa.base) ? 8 : 4);
if (c->ishfa)
for (n=0; n<c->hfa.size; n++, c->nfp++) {
c->reg[n] = *fp++;
c->cls[n] = c->hfa.base;
}
else
for (n=0; n<sz/8; n++, c->ngp++) {
c->reg[n] = *gp++;
c->cls[n] = Kl;
}
c->nreg = n;
}
}
static void
sttmps(Ref tmp[], int cls[], uint nreg, Ref mem, Fn *fn)
@ -429,7 +427,7 @@ selcall(Fn *fn, Ins *i0, Ins *i1, Insl **ilp)
for (i=i0, c=ca; i<i1; i++, c++) {
if ((c->class & Cstk) != 0)
continue;
if (i->op == Oarg || i->op == Oarge || isargbh(i->op))
if (i->op == Oarg || i->op == Oarge)
emit(Ocopy, *c->cls, TMP(*c->reg), i->arg[0], R);
if (i->op == Oargc)
ldregs(c->reg, c->cls, c->nreg, i->arg[1], fn);
@ -545,7 +543,8 @@ split(Fn *fn, Blk *b)
++fn->nblk;
bn = newblk();
idup(bn, curi, &insb[NIns]-curi);
bn->nins = &insb[NIns] - curi;
idup(&bn->ins, curi, bn->nins);
curi = &insb[NIns];
bn->visit = ++b->visit;
strf(bn->name, "%s.%d", b->name, b->visit);
@ -729,9 +728,9 @@ void
arm64_abi(Fn *fn)
{
Blk *b;
Ins *i, *i0;
Ins *i, *i0, *ip;
Insl *il;
int n0, n1, ioff;
int n;
Params p;
for (b=fn->start; b; b=b->link)
@ -742,13 +741,12 @@ arm64_abi(Fn *fn)
if (!ispar(i->op))
break;
p = selpar(fn, b->ins, i);
n0 = &insb[NIns] - curi;
ioff = i - b->ins;
n1 = b->nins - ioff;
vgrow(&b->ins, n0+n1);
icpy(b->ins+n0, b->ins+ioff, n1);
icpy(b->ins, curi, n0);
b->nins = n0+n1;
n = b->nins - (i - b->ins) + (&insb[NIns] - curi);
i0 = alloc(n * sizeof(Ins));
ip = icpy(ip = i0, curi, &insb[NIns] - curi);
ip = icpy(ip, i, &b->ins[b->nins] - i);
b->nins = n;
b->ins = i0;
/* lower calls, returns, and vararg instructions */
il = 0;
@ -791,7 +789,8 @@ arm64_abi(Fn *fn)
if (b == fn->start)
for (; il; il=il->link)
emiti(il->i);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
} while (b != fn->start);
if (debug['A']) {
@ -842,7 +841,8 @@ apple_extsb(Fn *fn)
emit(op, Kw, i->to, i->arg[0], R);
}
}
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
if (debug['A']) {

View File

@ -37,7 +37,7 @@ enum {
static struct {
short op;
short cls;
char *fmt;
char *asm;
} omap[] = {
{ Oadd, Ki, "add %=, %0, %1" },
{ Oadd, Ka, "fadd %=, %0, %1" },
@ -109,10 +109,6 @@ static struct {
{ NOp, 0, 0 }
};
enum {
V31 = 0x1fffffff, /* local name for V31 */
};
static char *
rname(int r, int k)
{
@ -136,12 +132,6 @@ rname(int r, int k)
case Kx:
case Kd: sprintf(buf, "d%d", r-V0); break;
}
else if (r == V31)
switch (k) {
default: die("invalid class");
case Ks: sprintf(buf, "s31"); break;
case Kd: sprintf(buf, "d31"); break;
}
else
die("invalid register");
return buf;
@ -170,8 +160,7 @@ emitf(char *s, Ins *i, E *e)
Ref r;
int k, c;
Con *pc;
uint64_t n;
uint sp;
uint n, sp;
fputc('\t', e->f);
@ -205,14 +194,14 @@ emitf(char *s, Ins *i, E *e)
goto Switch;
case '?':
if (KBASE(k) == 0)
fputs(rname(IP1, k), e->f);
fputs(rname(R18, k), e->f);
else
fputs(rname(V31, k), e->f);
fputs(k==Ks ? "s31" : "d31", e->f);
break;
case '=':
case '0':
r = c == '=' ? i->to : i->arg[0];
assert(isreg(r) || req(r, TMP(V31)));
assert(isreg(r));
fputs(rname(r.val, k), e->f);
break;
case '1':
@ -228,17 +217,10 @@ emitf(char *s, Ins *i, E *e)
pc = &e->fn->con[r.val];
n = pc->bits.i;
assert(pc->type == CBits);
if (n >> 24) {
assert(arm64_logimm(n, k));
fprintf(e->f, "#%"PRIu64, n);
} else if (n & 0xfff000) {
assert(!(n & ~0xfff000ull));
fprintf(e->f, "#%"PRIu64", lsl #12",
n>>12);
} else {
assert(!(n & ~0xfffull));
fprintf(e->f, "#%"PRIu64, n);
}
if (n & 0xfff000)
fprintf(e->f, "#%u, lsl #12", n>>12);
else
fprintf(e->f, "#%u", n);
break;
}
break;
@ -322,7 +304,6 @@ loadcon(Con *c, int r, int k, E *e)
rn = rname(r, k);
n = c->bits.i;
if (c->type == CAddr) {
rn = rname(r, Kl);
loadaddr(c, rn, e);
return;
}
@ -345,8 +326,8 @@ loadcon(Con *c, int r, int k, E *e)
static void emitins(Ins *, E *);
static int
fixarg(Ref *pr, int sz, int t, E *e)
static void
fixarg(Ref *pr, int sz, E *e)
{
Ins *i;
Ref r;
@ -356,14 +337,11 @@ fixarg(Ref *pr, int sz, int t, E *e)
if (rtype(r) == RSlot) {
s = slot(r, e);
if (s > sz * 4095u) {
if (t < 0)
return 1;
i = &(Ins){Oaddr, Kl, TMP(t), {r}};
i = &(Ins){Oaddr, Kl, TMP(IP0), {r}};
emitins(i, e);
*pr = TMP(t);
*pr = TMP(IP0);
}
}
return 0;
}
static void
@ -371,28 +349,16 @@ emitins(Ins *i, E *e)
{
char *l, *p, *rn;
uint64_t s;
int o, t;
int o;
Ref r;
Con *c;
switch (i->op) {
default:
if (isload(i->op))
fixarg(&i->arg[0], loadsz(i), IP1, e);
if (isstore(i->op)) {
t = T.apple ? -1 : R18;
if (fixarg(&i->arg[1], storesz(i), t, e)) {
if (req(i->arg[0], TMP(IP1))) {
fprintf(e->f,
"\tfmov\t%c31, %c17\n",
"ds"[i->cls == Kw],
"xw"[i->cls == Kw]);
i->arg[0] = TMP(V31);
i->op = Ostores + (i->cls-Kw);
}
fixarg(&i->arg[1], storesz(i), IP1, e);
}
}
fixarg(&i->arg[0], loadsz(i), e);
if (isstore(i->op))
fixarg(&i->arg[1], storesz(i), e);
Table:
/* most instructions are just pulled out of
* the table omap[], some special cases are
@ -408,7 +374,7 @@ emitins(Ins *i, E *e)
|| (omap[o].cls == Ki && KBASE(i->cls) == 0))
break;
}
emitf(omap[o].fmt, i, e);
emitf(omap[o].asm, i, e);
break;
case Onop:
break;
@ -418,7 +384,7 @@ emitins(Ins *i, E *e)
if (rtype(i->to) == RSlot) {
r = i->to;
if (!isreg(i->arg[0])) {
i->to = TMP(IP1);
i->to = TMP(R18);
emitins(i, e);
i->arg[0] = i->to;
}
@ -439,7 +405,7 @@ emitins(Ins *i, E *e)
emitins(i, e);
break;
default:
assert(i->to.val != IP1);
assert(i->to.val != R18);
goto Table;
}
break;
@ -548,7 +514,6 @@ arm64_emitfn(Fn *fn, FILE *out)
if (T.apple)
e->fn->lnk.align = 4;
emitfnlnk(e->fn->name, &e->fn->lnk, e->f);
fputs("\thint\t#34\n", e->f);
framelayout(e);
if (e->fn->vararg && !T.apple) {

View File

@ -24,7 +24,7 @@ imm(Con *c, int k, int64_t *pn)
i = Iplo12;
if (n < 0) {
i = Inlo12;
n = -(uint64_t)n;
n = -n;
}
*pn = n;
if ((n & 0x000fff) == n)
@ -109,7 +109,7 @@ fixarg(Ref *pr, int k, int phi, Fn *fn)
if (KBASE(k) == 0) {
emit(Ocopy, k, r1, r0, R);
} else {
n = stashbits(c->bits.i, KWIDE(k) ? 8 : 4);
n = stashbits(&c->bits, KWIDE(k) ? 8 : 4);
vgrow(&fn->con, ++fn->ncon);
c = &fn->con[fn->ncon-1];
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
@ -306,7 +306,8 @@ arm64_isel(Fn *fn)
seljmp(b, fn);
for (i=&b->ins[b->nins]; i!=b->ins;)
sel(*--i, fn);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
if (debug['I']) {

View File

@ -16,7 +16,7 @@ int arm64_rclob[] = {
-1
};
#define RGLOB (BIT(FP) | BIT(SP) | BIT(IP1) | BIT(R18))
#define RGLOB (BIT(FP) | BIT(SP) | BIT(R18))
static int
arm64_memargs(int op)
@ -31,7 +31,7 @@ arm64_memargs(int op)
.fpr0 = V0, \
.nfpr = NFPR, \
.rglob = RGLOB, \
.nrglob = 4, \
.nrglob = 3, \
.rsave = arm64_rsave, \
.nrsave = {NGPS, NFPS}, \
.retregs = arm64_retregs, \
@ -40,7 +40,6 @@ arm64_memargs(int op)
.isel = arm64_isel, \
.abi1 = arm64_abi, \
.emitfn = arm64_emitfn, \
.cansel = 0, \
Target T_arm64 = {
.name = "arm64",

352
cfg.c
View File

@ -8,50 +8,66 @@ newblk()
b = alloc(sizeof *b);
*b = z;
b->ins = vnew(0, sizeof b->ins[0], PFn);
b->pred = vnew(0, sizeof b->pred[0], PFn);
return b;
}
static void
fixphis(Fn *f)
void
edgedel(Blk *bs, Blk **pbd)
{
Blk *b, *bp;
Blk *bd;
Phi *p;
uint n, n0;
uint a;
int mult;
for (b=f->start; b; b=b->link) {
assert(b->id < f->nblk);
for (p=b->phi; p; p=p->link) {
for (n=n0=0; n<p->narg; n++) {
bp = p->blk[n];
if (bp->id != -1u)
if (bp->s1 == b || bp->s2 == b) {
p->blk[n0] = bp;
p->arg[n0] = p->arg[n];
n0++;
}
}
assert(n0 > 0);
p->narg = n0;
bd = *pbd;
mult = 1 + (bs->s1 == bs->s2);
*pbd = 0;
if (!bd || mult > 1)
return;
for (p=bd->phi; p; p=p->link) {
for (a=0; p->blk[a]!=bs; a++)
assert(a+1<p->narg);
p->narg--;
memmove(&p->blk[a], &p->blk[a+1],
sizeof p->blk[0] * (p->narg-a));
memmove(&p->arg[a], &p->arg[a+1],
sizeof p->arg[0] * (p->narg-a));
}
if (bd->npred != 0) {
for (a=0; bd->pred[a]!=bs; a++)
assert(a+1<bd->npred);
bd->npred--;
memmove(&bd->pred[a], &bd->pred[a+1],
sizeof bd->pred[0] * (bd->npred-a));
}
}
static void
addpred(Blk *bp, Blk *b)
addpred(Blk *bp, Blk *bc)
{
vgrow(&b->pred, ++b->npred);
b->pred[b->npred-1] = bp;
if (!bc->pred) {
bc->pred = alloc(bc->npred * sizeof bc->pred[0]);
bc->visit = 0;
}
bc->pred[bc->visit++] = bp;
}
/* fill predecessors information in blocks */
void
fillpreds(Fn *f)
{
Blk *b;
for (b=f->start; b; b=b->link)
for (b=f->start; b; b=b->link) {
b->npred = 0;
b->pred = 0;
}
for (b=f->start; b; b=b->link) {
if (b->s1)
b->s1->npred++;
if (b->s2 && b->s2 != b->s1)
b->s2->npred++;
}
for (b=f->start; b; b=b->link) {
if (b->s1)
addpred(b, b->s1);
@ -60,55 +76,52 @@ fillpreds(Fn *f)
}
}
static void
porec(Blk *b, uint *npo)
static int
rporec(Blk *b, uint x)
{
Blk *s1, *s2;
if (!b || b->id != -1u)
return;
b->id = 0; /* marker */
return x;
b->id = 1;
s1 = b->s1;
s2 = b->s2;
if (s1 && s2 && s1->loop > s2->loop) {
s1 = b->s2;
s2 = b->s1;
}
porec(s1, npo);
porec(s2, npo);
b->id = (*npo)++;
x = rporec(s1, x);
x = rporec(s2, x);
b->id = x;
assert(x != -1u);
return x - 1;
}
static void
/* fill the rpo information */
void
fillrpo(Fn *f)
{
uint n;
Blk *b, **p;
for (b=f->start; b; b=b->link)
b->id = -1u;
f->nblk = 0;
porec(f->start, &f->nblk);
vgrow(&f->rpo, f->nblk);
n = 1 + rporec(f->start, f->nblk-1);
f->nblk -= n;
f->rpo = alloc(f->nblk * sizeof f->rpo[0]);
for (p=&f->start; (b=*p);) {
if (b->id == -1u) {
edgedel(b, &b->s1);
edgedel(b, &b->s2);
*p = b->link;
} else {
b->id = f->nblk-b->id-1;
b->id -= n;
f->rpo[b->id] = b;
p = &b->link;
}
}
}
/* fill rpo, preds; prune dead blks */
void
fillcfg(Fn *f)
{
fillrpo(f);
fillpreds(f);
fixphis(f);
}
/* for dominators computation, read
* "A Simple, Fast Dominance Algorithm"
* by K. Cooper, T. Harvey, and K. Kennedy.
@ -249,50 +262,6 @@ loopiter(Fn *fn, void f(Blk *, Blk *))
}
}
/* dominator tree depth */
void
filldepth(Fn *fn)
{
Blk *b, *d;
int depth;
for (b=fn->start; b; b=b->link)
b->depth = -1;
fn->start->depth = 0;
for (b=fn->start; b; b=b->link) {
if (b->depth != -1)
continue;
depth = 1;
for (d=b->idom; d->depth==-1; d=d->idom)
depth++;
depth += d->depth;
b->depth = depth;
for (d=b->idom; d->depth==-1; d=d->idom)
d->depth = --depth;
}
}
/* least common ancestor in dom tree */
Blk *
lca(Blk *b1, Blk *b2)
{
if (!b1)
return b2;
if (!b2)
return b1;
while (b1->depth > b2->depth)
b1 = b1->idom;
while (b2->depth > b1->depth)
b2 = b2->idom;
while (b1 != b2) {
b1 = b1->idom;
b2 = b2->idom;
}
return b1;
}
void
multloop(Blk *hd, Blk *b)
{
@ -360,208 +329,3 @@ simpljmp(Fn *fn)
*p = ret;
free(uf);
}
static int
reachrec(Blk *b, Blk *to)
{
if (b == to)
return 1;
if (!b || b->visit)
return 0;
b->visit = 1;
if (reachrec(b->s1, to))
return 1;
if (reachrec(b->s2, to))
return 1;
return 0;
}
/* Blk.visit needs to be clear at entry */
int
reaches(Fn *fn, Blk *b, Blk *to)
{
int r;
assert(to);
r = reachrec(b, to);
for (b=fn->start; b; b=b->link)
b->visit = 0;
return r;
}
/* can b reach 'to' not through excl
* Blk.visit needs to be clear at entry */
int
reachesnotvia(Fn *fn, Blk *b, Blk *to, Blk *excl)
{
excl->visit = 1;
return reaches(fn, b, to);
}
int
ifgraph(Blk *ifb, Blk **pthenb, Blk **pelseb, Blk **pjoinb)
{
Blk *s1, *s2, **t;
if (ifb->jmp.type != Jjnz)
return 0;
s1 = ifb->s1;
s2 = ifb->s2;
if (s1->id > s2->id) {
s1 = ifb->s2;
s2 = ifb->s1;
t = pthenb;
pthenb = pelseb;
pelseb = t;
}
if (s1 == s2)
return 0;
if (s1->jmp.type != Jjmp || s1->npred != 1)
return 0;
if (s1->s1 == s2) {
/* if-then / if-else */
if (s2->npred != 2)
return 0;
*pthenb = s1;
*pelseb = ifb;
*pjoinb = s2;
return 1;
}
if (s2->jmp.type != Jjmp || s2->npred != 1)
return 0;
if (s1->s1 != s2->s1 || s1->s1->npred != 2)
return 0;
assert(s1->s1 != ifb);
*pthenb = s1;
*pelseb = s2;
*pjoinb = s1->s1;
return 1;
}
typedef struct Jmp Jmp;
struct Jmp {
int type;
Ref arg;
Blk *s1, *s2;
};
static int
jmpeq(Jmp *a, Jmp *b)
{
return a->type == b->type && req(a->arg, b->arg)
&& a->s1 == b->s1 && a->s2 == b->s2;
}
static int
jmpnophi(Jmp *j)
{
if (j->s1 && j->s1->phi)
return 0;
if (j->s2 && j->s2->phi)
return 0;
return 1;
}
/* require cfg rpo, breaks use */
void
simplcfg(Fn *fn)
{
Ins cpy, *i;
Blk *b, *bb, **pb;
Jmp *jmp, *j, *jj;
Phi *p;
int *empty, done;
uint n;
if (debug['C']) {
fprintf(stderr, "\n> Before CFG simplification:\n");
printfn(fn, stderr);
}
cpy = (Ins){.op = Ocopy};
for (b=fn->start; b; b=b->link)
if (b->npred == 1) {
bb = b->pred[0];
for (p=b->phi; p; p=p->link) {
cpy.cls = p->cls;
cpy.to = p->to;
cpy.arg[0] = phiarg(p, bb);
addins(&bb->ins, &bb->nins, &cpy);
}
b->phi = 0;
}
jmp = emalloc(fn->nblk * sizeof jmp[0]);
empty = emalloc(fn->nblk * sizeof empty[0]);
for (b=fn->start; b; b=b->link) {
jmp[b->id].type = b->jmp.type;
jmp[b->id].arg = b->jmp.arg;
jmp[b->id].s1 = b->s1;
jmp[b->id].s2 = b->s2;
empty[b->id] = !b->phi;
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (i->op != Onop && i->op != Odbgloc) {
empty[b->id] = 0;
break;
}
}
do {
done = 1;
for (b=fn->start; b; b=b->link) {
if (b->id == -1u)
continue;
j = &jmp[b->id];
if (j->type == Jjmp && j->s1->npred == 1) {
assert(!j->s1->phi);
addbins(&b->ins, &b->nins, j->s1);
empty[b->id] &= empty[j->s1->id];
jj = &jmp[j->s1->id];
pb = (Blk*[]){jj->s1, jj->s2, 0};
for (; (bb=*pb); pb++)
for (p=bb->phi; p; p=p->link) {
n = phiargn(p, j->s1);
p->blk[n] = b;
}
j->s1->id = -1u;
*j = *jj;
done = 0;
}
else if (j->type == Jjnz
&& empty[j->s1->id] && empty[j->s2->id]
&& jmpeq(&jmp[j->s1->id], &jmp[j->s2->id])
&& jmpnophi(&jmp[j->s1->id])) {
*j = jmp[j->s1->id];
done = 0;
}
}
} while (!done);
for (b=fn->start; b; b=b->link)
if (b->id != -1u) {
j = &jmp[b->id];
b->jmp.type = j->type;
b->jmp.arg = j->arg;
b->s1 = j->s1;
b->s2 = j->s2;
assert(!j->s1 || j->s1->id != -1u);
assert(!j->s2 || j->s2->id != -1u);
}
fillcfg(fn);
free(empty);
free(jmp);
if (debug['C']) {
fprintf(stderr, "\n> After CFG simplification:\n");
printfn(fn, stderr);
}
}

587
copy.c
View File

@ -1,272 +1,16 @@
#include "all.h"
typedef struct Ext Ext;
struct Ext {
char zext;
char nopw; /* is a no-op if arg width is <= nopw */
char usew; /* uses only the low usew bits of arg */
};
static int
ext(Ins *i, Ext *e)
iscon(Ref r, int64_t bits, Fn *fn)
{
static Ext tbl[] = {
/*extsb*/ {0, 7, 8},
/*extub*/ {1, 8, 8},
/*extsh*/ {0, 15, 16},
/*extuh*/ {1, 16, 16},
/*extsw*/ {0, 31, 32},
/*extuw*/ {1, 32, 32},
};
if (!isext(i->op))
return 0;
*e = tbl[i->op - Oextsb];
return 1;
return rtype(r) == RCon
&& fn->con[r.val].type == CBits
&& fn->con[r.val].bits.i == bits;
}
static int
bitwidth(uint64_t v)
iscopy(Ins *i, Ref r, Fn *fn)
{
int n;
n = 0;
if (v >> 32) { n += 32; v >>= 32; }
if (v >> 16) { n += 16; v >>= 16; }
if (v >> 8) { n += 8; v >>= 8; }
if (v >> 4) { n += 4; v >>= 4; }
if (v >> 2) { n += 2; v >>= 2; }
if (v >> 1) { n += 1; v >>= 1; }
return n+v;
}
/* no more than w bits are used */
static int
usewidthle(Fn *fn, Ref r, int w)
{
Ext e;
Tmp *t;
Use *u;
Phi *p;
Ins *i;
Ref rc;
int64_t v;
int b;
assert(rtype(r) == RTmp);
t = &fn->tmp[r.val];
for (u=t->use; u<&t->use[t->nuse]; u++) {
switch (u->type) {
case UPhi:
p = u->u.phi;
/* during gvn, phi nodes may be
* replaced by other temps; in
* this case, the replaced phi
* uses are added to the
* replacement temp uses and
* Phi.to is set to R */
if (p->visit || req(p->to, R))
continue;
p->visit = 1;
b = usewidthle(fn, p->to, w);
p->visit = 0;
if (b)
continue;
break;
case UIns:
i = u->u.ins;
assert(i != 0);
if (i->op == Ocopy)
if (usewidthle(fn, i->to, w))
continue;
if (ext(i, &e)) {
if (e.usew <= w)
continue;
if (usewidthle(fn, i->to, w))
continue;
}
if (i->op == Oand) {
if (req(r, i->arg[0]))
rc = i->arg[1];
else {
assert(req(r, i->arg[1]));
rc = i->arg[0];
}
if (isconbits(fn, rc, &v)
&& bitwidth(v) <= w)
continue;
break;
}
break;
default:
break;
}
return 0;
}
return 1;
}
static int
min(int v1, int v2)
{
return v1 < v2 ? v1 : v2;
}
/* is the ref narrower than w bits */
static int
defwidthle(Fn *fn, Ref r, int w)
{
Ext e;
Tmp *t;
Phi *p;
Ins *i;
uint n;
int64_t v;
int x;
if (isconbits(fn, r, &v)
&& bitwidth(v) <= w)
return 1;
if (rtype(r) != RTmp)
return 0;
t = &fn->tmp[r.val];
if (t->cls != Kw)
return 0;
if (!t->def) {
/* phi def */
for (p=fn->rpo[t->bid]->phi; p; p=p->link)
if (req(p->to, r))
break;
assert(p);
if (p->visit)
return 1;
p->visit = 1;
for (n=0; n<p->narg; n++)
if (!defwidthle(fn, p->arg[n], w)) {
p->visit = 0;
return 0;
}
p->visit = 0;
return 1;
}
i = t->def;
if (i->op == Ocopy)
return defwidthle(fn, i->arg[0], w);
if (i->op == Oshr || i->op == Osar) {
if (isconbits(fn, i->arg[1], &v))
if (0 < v && v <= 32) {
if (i->op == Oshr && w+v >= 32)
return 1;
if (w < 32) {
if (i->op == Osar)
w = min(31, w+v);
else
w = min(32, w+v);
}
}
return defwidthle(fn, i->arg[0], w);
}
if (iscmp(i->op, &x, &x))
return w >= 1;
if (i->op == Oand) {
if (defwidthle(fn, i->arg[0], w)
|| defwidthle(fn, i->arg[1], w))
return 1;
return 0;
}
if (i->op == Oor || i->op == Oxor) {
if (defwidthle(fn, i->arg[0], w)
&& defwidthle(fn, i->arg[1], w))
return 1;
return 0;
}
if (ext(i, &e)) {
if (e.zext && e.usew <= w)
return 1;
w = min(w, e.nopw);
return defwidthle(fn, i->arg[0], w);
}
return 0;
}
static int
isw1(Fn *fn, Ref r)
{
return defwidthle(fn, r, 1);
}
/* insert early extub/extuh instructions
* for pars used only narrowly; this
* helps factoring extensions out of
* loops
*
* needs use; breaks use
*/
void
narrowpars(Fn *fn)
{
Blk *b;
int loop;
Ins ext, *i, *ins;
uint npar, nins;
Ref r;
/* only useful for functions with loops */
loop = 0;
for (b=fn->start; b; b=b->link)
if (b->loop > 1) {
loop = 1;
break;
}
if (!loop)
return;
b = fn->start;
npar = 0;
for (i=b->ins; i<&b->ins[b->nins]; i++) {
if (!ispar(i->op))
break;
npar++;
}
if (npar == 0)
return;
nins = b->nins + npar;
ins = vnew(nins, sizeof ins[0], PFn);
icpy(ins, b->ins, npar);
icpy(ins + 2*npar, b->ins+npar, b->nins-npar);
b->ins = ins;
b->nins = nins;
for (i=b->ins; i<&b->ins[b->nins]; i++) {
if (!ispar(i->op))
break;
ext = (Ins){.op = Onop};
if (i->cls == Kw)
if (usewidthle(fn, i->to, 16)) {
ext.op = Oextuh;
if (usewidthle(fn, i->to, 8))
ext.op = Oextub;
r = newtmp("vw", i->cls, fn);
ext.cls = i->cls;
ext.to = i->to;
ext.arg[0] = r;
i->to = r;
}
*(i+npar) = ext;
}
}
Ref
copyref(Fn *fn, Blk *b, Ins *i)
{
/* which extensions are copies for a given
* argument width */
static bits extcpy[] = {
[WFull] = 0,
[Wsb] = BIT(Wsb) | BIT(Wsh) | BIT(Wsw),
@ -276,133 +20,210 @@ copyref(Fn *fn, Blk *b, Ins *i)
[Wsw] = BIT(Wsw),
[Wuw] = BIT(Wuw),
};
Ext e;
bits b;
Tmp *t;
int64_t v;
int w, z;
if (i->op == Ocopy)
return i->arg[0];
/* op identity value */
if (optab[i->op].hasid
&& KBASE(i->cls) == 0 /* integer only - fp NaN! */
&& req(i->arg[1], con01[optab[i->op].idval])
&& (!optab[i->op].cmpeqwl || isw1(fn, i->arg[0])))
return i->arg[0];
/* idempotent op with identical args */
if (optab[i->op].idemp
&& req(i->arg[0], i->arg[1]))
return i->arg[0];
/* integer cmp with identical args */
if ((optab[i->op].cmpeqwl || optab[i->op].cmplgtewl)
&& req(i->arg[0], i->arg[1]))
return con01[optab[i->op].eqval];
/* cmpeq/ne 0 with 0/non-0 inference */
if (optab[i->op].cmpeqwl
&& req(i->arg[1], CON_Z)
&& zeroval(fn, b, i->arg[0], argcls(i, 0), &z))
return con01[optab[i->op].eqval^z^1];
/* redundant and mask */
if (i->op == Oand
&& isconbits(fn, i->arg[1], &v)
&& (v > 0 && ((v+1) & v) == 0)
&& defwidthle(fn, i->arg[0], bitwidth(v)))
return i->arg[0];
if (i->cls == Kw
&& (i->op == Oextsw || i->op == Oextuw))
return i->arg[0];
if (ext(i, &e) && rtype(i->arg[0]) == RTmp) {
t = &fn->tmp[i->arg[0].val];
assert(KBASE(t->cls) == 0);
/* do not break typing by returning
* a narrower temp */
if (KWIDE(i->cls) > KWIDE(t->cls))
return R;
w = Wsb + (i->op - Oextsb);
if (BIT(w) & extcpy[t->width])
return i->arg[0];
/* avoid eliding extensions of params
* inserted in the start block; their
* point is to make further extensions
* redundant */
if ((!t->def || !ispar(t->def->op))
&& usewidthle(fn, i->to, e.usew))
return i->arg[0];
if (defwidthle(fn, i->arg[0], e.nopw))
return i->arg[0];
}
return R;
}
static int
phieq(Phi *pa, Phi *pb)
{
Ref r;
uint n;
assert(pa->narg == pb->narg);
for (n=0; n<pa->narg; n++) {
r = phiarg(pb, pa->blk[n]);
if (!req(pa->arg[n], r))
return 0;
}
switch (i->op) {
case Ocopy:
return 1;
}
Ref
phicopyref(Fn *fn, Blk *b, Phi *p)
{
Blk *d, **s;
Phi *p1;
uint n, c;
/* identical args */
for (n=0; n<p->narg-1; n++)
if (!req(p->arg[n], p->arg[n+1]))
case Omul:
case Odiv:
case Oudiv:
return iscon(i->arg[1], 1, fn);
case Oadd:
case Osub:
case Oor:
case Oxor:
case Osar:
case Oshl:
case Oshr:
return iscon(i->arg[1], 0, fn);
default:
break;
if (n == p->narg-1)
return p->arg[n];
}
if (!isext(i->op) || rtype(r) != RTmp)
return 0;
if (i->op == Oextsw || i->op == Oextuw)
if (i->cls == Kw)
return 1;
/* same as a previous phi */
for (p1=b->phi; p1!=p; p1=p1->link) {
assert(p1);
if (phieq(p1, p))
return p1->to;
t = &fn->tmp[r.val];
assert(KBASE(t->cls) == 0);
if (i->cls == Kl && t->cls == Kw)
return 0;
b = extcpy[t->width];
return (BIT(Wsb + (i->op-Oextsb)) & b) != 0;
}
/* can be replaced by a
* dominating jnz arg */
d = b->idom;
if (p->narg != 2
|| d->jmp.type != Jjnz
|| !isw1(fn, d->jmp.arg))
return R;
s = (Blk*[]){0, 0};
for (n=0; n<2; n++)
for (c=0; c<2; c++)
if (req(p->arg[n], con01[c]))
s[c] = p->blk[n];
/* if s1 ends with a jnz on either b
* or s2; the inference below is wrong
* without the jump type checks */
if (d->s1 == s[1] && d->s2 == s[0]
&& d->s1->jmp.type == Jjmp
&& d->s2->jmp.type == Jjmp)
return d->jmp.arg;
return R;
static Ref
copyof(Ref r, Ref *cpy)
{
if (rtype(r) == RTmp && !req(cpy[r.val], R))
return cpy[r.val];
return r;
}
/* detects a cluster of phis/copies redundant with 'r';
* the algorithm is inspired by Section 3.2 of "Simple
* and Efficient SSA Construction" by Braun M. et al.
*/
static void
phisimpl(Phi *p, Ref r, Ref *cpy, Use ***pstk, BSet *ts, BSet *as, Fn *fn)
{
Use **stk, *u, *u1;
uint nstk, a;
int t;
Ref r1;
Phi *p0;
bszero(ts);
bszero(as);
p0 = &(Phi){.narg = 0};
stk = *pstk;
nstk = 1;
stk[0] = &(Use){.type = UPhi, .u.phi = p};
while (nstk) {
u = stk[--nstk];
if (u->type == UIns && iscopy(u->u.ins, r, fn)) {
p = p0;
t = u->u.ins->to.val;
}
else if (u->type == UPhi) {
p = u->u.phi;
t = p->to.val;
}
else
continue;
if (bshas(ts, t))
continue;
bsset(ts, t);
for (a=0; a<p->narg; a++) {
r1 = copyof(p->arg[a], cpy);
if (req(r1, r))
continue;
if (rtype(r1) != RTmp)
return;
bsset(as, r1.val);
}
u = fn->tmp[t].use;
u1 = &u[fn->tmp[t].nuse];
vgrow(pstk, nstk+(u1-u));
stk = *pstk;
for (; u<u1; u++)
stk[nstk++] = u;
}
bsdiff(as, ts);
if (!bscount(as))
for (t=0; bsiter(ts, &t); t++)
cpy[t] = r;
}
static void
subst(Ref *pr, Ref *cpy)
{
assert(rtype(*pr) != RTmp || !req(cpy[pr->val], R));
*pr = copyof(*pr, cpy);
}
/* requires use and dom, breaks use */
void
copy(Fn *fn)
{
BSet ts[1], as[1];
Use **stk;
Phi *p, **pp;
Ins *i;
Blk *b;
uint n, a, eq;
Ref *cpy, r, r1;
int t;
bsinit(ts, fn->ntmp);
bsinit(as, fn->ntmp);
cpy = emalloc(fn->ntmp * sizeof cpy[0]);
stk = vnew(10, sizeof stk[0], PHeap);
/* 1. build the copy-of map */
for (n=0; n<fn->nblk; n++) {
b = fn->rpo[n];
for (p=b->phi; p; p=p->link) {
assert(rtype(p->to) == RTmp);
if (!req(cpy[p->to.val], R))
continue;
eq = 0;
r = R;
for (a=0; a<p->narg; a++)
if (p->blk[a]->id < n) {
r1 = copyof(p->arg[a], cpy);
if (req(r, R) || req(r, UNDEF))
r = r1;
if (req(r1, r) || req(r1, UNDEF))
eq++;
}
assert(!req(r, R));
if (rtype(r) == RTmp
&& !dom(fn->rpo[fn->tmp[r.val].bid], b))
cpy[p->to.val] = p->to;
else if (eq == p->narg)
cpy[p->to.val] = r;
else {
cpy[p->to.val] = p->to;
phisimpl(p, r, cpy, &stk, ts, as, fn);
}
}
for (i=b->ins; i<&b->ins[b->nins]; i++) {
assert(rtype(i->to) <= RTmp);
if (!req(cpy[i->to.val], R))
continue;
r = copyof(i->arg[0], cpy);
if (iscopy(i, r, fn))
cpy[i->to.val] = r;
else
cpy[i->to.val] = i->to;
}
}
/* 2. remove redundant phis/copies
* and rewrite their uses */
for (b=fn->start; b; b=b->link) {
for (pp=&b->phi; (p=*pp);) {
r = cpy[p->to.val];
if (!req(r, p->to)) {
*pp = p->link;
continue;
}
for (a=0; a<p->narg; a++)
subst(&p->arg[a], cpy);
pp=&p->link;
}
for (i=b->ins; i<&b->ins[b->nins]; i++) {
r = cpy[i->to.val];
if (!req(r, i->to)) {
*i = (Ins){.op = Onop};
continue;
}
subst(&i->arg[0], cpy);
subst(&i->arg[1], cpy);
}
subst(&b->jmp.arg, cpy);
}
if (debug['C']) {
fprintf(stderr, "\n> Copy information:");
for (t=Tmp0; t<fn->ntmp; t++) {
if (req(cpy[t], R)) {
fprintf(stderr, "\n%10s not seen!",
fn->tmp[t].name);
}
else if (!req(cpy[t], TMP(t))) {
fprintf(stderr, "\n%10s copy of ",
fn->tmp[t].name);
printref(cpy[t], fn, stderr);
}
}
fprintf(stderr, "\n\n> After copy elimination:\n");
printfn(fn, stderr);
}
vfree(stk);
free(cpy);
}

View File

@ -439,7 +439,7 @@ return type of the function. All return values of this
function must have this return type. If the return
type is missing, the function must not return any value.
The parameter list is a comma separated list of distinct
The parameter list is a comma separated list of
temporary names prefixed by types. The types are used
to correctly implement C compatibility. When an argument
has an aggregate type, a pointer to the aggregate is passed
@ -976,7 +976,6 @@ is possible to conservatively use the maximum size and
alignment required by all the targets.
type :valist = align 8 { 24 } # For amd64_sysv
type :valist = align 8 { 8 } # For amd64_win
type :valist = align 8 { 32 } # For arm64
type :valist = align 8 { 8 } # For rv64

View File

@ -1,15 +0,0 @@
There is an experimental amd64_win (native Windows ABI and calling
convention).
In tree, this is currently only tested via cross-compilation from a
Linux host, and using wine to run the tests.
You'll need something like:
sudo apt install mingw64-w64 dos2unix wine
and then
make check-amd64_win
should pass.

79
emit.c
View File

@ -61,14 +61,11 @@ emitfnlnk(char *n, Lnk *l, FILE *f)
void
emitdat(Dat *d, FILE *f)
{
static struct {
char decl[8];
int64_t mask;
} di[] = {
[DB] = {"\t.byte", 0xffL},
[DH] = {"\t.short", 0xffffL},
[DW] = {"\t.int", 0xffffffffL},
[DL] = {"\t.quad", -1L},
static char *dtoa[] = {
[DB] = "\t.byte",
[DH] = "\t.short",
[DW] = "\t.int",
[DL] = "\t.quad"
};
static int64_t zero;
char *p;
@ -78,17 +75,7 @@ emitdat(Dat *d, FILE *f)
zero = 0;
break;
case DEnd:
if (d->lnk->common) {
if (zero == -1)
die("invalid common data definition");
p = d->name[0] == '"' ? "" : T.assym;
fprintf(f, ".comm %s%s,%"PRId64,
p, d->name, zero);
if (d->lnk->align)
fprintf(f, ",%d", d->lnk->align);
fputc('\n', f);
}
else if (zero != -1) {
if (zero != -1) {
emitlnk(d->name, d->lnk, SecBss, f);
fprintf(f, "\t.fill %"PRId64",1,0\n", zero);
}
@ -114,13 +101,12 @@ emitdat(Dat *d, FILE *f)
else if (d->isref) {
p = d->u.ref.name[0] == '"' ? "" : T.assym;
fprintf(f, "%s %s%s%+"PRId64"\n",
di[d->type].decl, p, d->u.ref.name,
dtoa[d->type], p, d->u.ref.name,
d->u.ref.off);
}
else {
fprintf(f, "%s %"PRId64"\n",
di[d->type].decl,
d->u.num & di[d->type].mask);
dtoa[d->type], d->u.num);
}
break;
}
@ -129,7 +115,7 @@ emitdat(Dat *d, FILE *f)
typedef struct Asmbits Asmbits;
struct Asmbits {
bits n;
char bits[16];
int size;
Asmbits *link;
};
@ -137,17 +123,18 @@ struct Asmbits {
static Asmbits *stash;
int
stashbits(bits n, int size)
stashbits(void *bits, int size)
{
Asmbits **pb, *b;
int i;
assert(size == 4 || size == 8 || size == 16);
for (pb=&stash, i=0; (b=*pb); pb=&b->link, i++)
if (size <= b->size && b->n == n)
if (size <= b->size)
if (memcmp(bits, b->bits, size) == 0)
return i;
b = emalloc(sizeof *b);
b->n = n;
memcpy(b->bits, bits, size);
b->size = size;
b->link = 0;
*pb = b;
@ -158,8 +145,9 @@ static void
emitfin(FILE *f, char *sec[3])
{
Asmbits *b;
char *p;
int lg, i;
union { int32_t i; float f; } u;
double d;
if (!stash)
return;
@ -173,24 +161,17 @@ emitfin(FILE *f, char *sec[3])
"%sfp%d:",
sec[lg-2], lg, T.asloc, i
);
if (lg == 4)
fprintf(f,
"\n\t.quad %"PRId64
"\n\t.quad 0\n\n",
(int64_t)b->n);
else if (lg == 3)
fprintf(f,
"\n\t.quad %"PRId64
" /* %f */\n\n",
(int64_t)b->n,
*(double *)&b->n);
else if (lg == 2) {
u.i = b->n;
fprintf(f,
"\n\t.int %"PRId32
" /* %f */\n\n",
u.i, (double)u.f);
}
for (p=b->bits; p<&b->bits[b->size]; p+=4)
fprintf(f, "\n\t.int %"PRId32,
*(int32_t *)p);
if (lg <= 3) {
if (lg == 2)
d = *(float *)b->bits;
else
d = *(double *)b->bits;
fprintf(f, " /* %f */\n\n", d);
} else
fprintf(f, "\n\n");
}
}
while ((b=stash)) {
@ -227,14 +208,6 @@ macho_emitfin(FILE *f)
emitfin(f, sec);
}
void
pe_emitfin(FILE *f)
{
static char *sec[3] = { ".rodata", ".rodata", ".rodata" };
emitfin(f, sec);
}
static uint32_t *file;
static uint nfile;
static uint curfile;

349
fold.c
View File

@ -1,6 +1,22 @@
#include "all.h"
/* boring folding code */
enum {
Bot = -1, /* lattice bottom */
Top = 0, /* lattice top (matches UNDEF) */
};
typedef struct Edge Edge;
struct Edge {
int dest;
int dead;
Edge *work;
};
static int *val;
static Edge *flowrk, (*edge)[2];
static Use **usewrk;
static uint nuse;
static int
iscon(Con *c, int w, uint64_t k)
@ -13,7 +29,305 @@ iscon(Con *c, int w, uint64_t k)
return (uint32_t)c->bits.i == (uint32_t)k;
}
int
static int
latval(Ref r)
{
switch (rtype(r)) {
case RTmp:
return val[r.val];
case RCon:
return r.val;
default:
die("unreachable");
}
}
static int
latmerge(int v, int m)
{
return m == Top ? v : (v == Top || v == m) ? m : Bot;
}
static void
update(int t, int m, Fn *fn)
{
Tmp *tmp;
uint u;
m = latmerge(val[t], m);
if (m != val[t]) {
tmp = &fn->tmp[t];
for (u=0; u<tmp->nuse; u++) {
vgrow(&usewrk, ++nuse);
usewrk[nuse-1] = &tmp->use[u];
}
val[t] = m;
}
}
static int
deadedge(int s, int d)
{
Edge *e;
e = edge[s];
if (e[0].dest == d && !e[0].dead)
return 0;
if (e[1].dest == d && !e[1].dead)
return 0;
return 1;
}
static void
visitphi(Phi *p, int n, Fn *fn)
{
int v;
uint a;
v = Top;
for (a=0; a<p->narg; a++)
if (!deadedge(p->blk[a]->id, n))
v = latmerge(v, latval(p->arg[a]));
update(p->to.val, v, fn);
}
static int opfold(int, int, Con *, Con *, Fn *);
static void
visitins(Ins *i, Fn *fn)
{
int v, l, r;
if (rtype(i->to) != RTmp)
return;
if (optab[i->op].canfold) {
l = latval(i->arg[0]);
if (!req(i->arg[1], R))
r = latval(i->arg[1]);
else
r = CON_Z.val;
if (l == Bot || r == Bot)
v = Bot;
else if (l == Top || r == Top)
v = Top;
else
v = opfold(i->op, i->cls, &fn->con[l], &fn->con[r], fn);
} else
v = Bot;
/* fprintf(stderr, "\nvisiting %s (%p)", optab[i->op].name, (void *)i); */
update(i->to.val, v, fn);
}
static void
visitjmp(Blk *b, int n, Fn *fn)
{
int l;
switch (b->jmp.type) {
case Jjnz:
l = latval(b->jmp.arg);
if (l == Bot) {
edge[n][1].work = flowrk;
edge[n][0].work = &edge[n][1];
flowrk = &edge[n][0];
}
else if (iscon(&fn->con[l], 0, 0)) {
assert(edge[n][0].dead);
edge[n][1].work = flowrk;
flowrk = &edge[n][1];
}
else {
assert(edge[n][1].dead);
edge[n][0].work = flowrk;
flowrk = &edge[n][0];
}
break;
case Jjmp:
edge[n][0].work = flowrk;
flowrk = &edge[n][0];
break;
case Jhlt:
break;
default:
if (isret(b->jmp.type))
break;
die("unreachable");
}
}
static void
initedge(Edge *e, Blk *s)
{
if (s)
e->dest = s->id;
else
e->dest = -1;
e->dead = 1;
e->work = 0;
}
static int
renref(Ref *r)
{
int l;
if (rtype(*r) == RTmp)
if ((l=val[r->val]) != Bot) {
*r = CON(l);
return 1;
}
return 0;
}
/* require rpo, use, pred */
void
fold(Fn *fn)
{
Edge *e, start;
Use *u;
Blk *b, **pb;
Phi *p, **pp;
Ins *i;
int t, d;
uint n, a;
val = emalloc(fn->ntmp * sizeof val[0]);
edge = emalloc(fn->nblk * sizeof edge[0]);
usewrk = vnew(0, sizeof usewrk[0], PHeap);
for (t=0; t<fn->ntmp; t++)
val[t] = Top;
for (n=0; n<fn->nblk; n++) {
b = fn->rpo[n];
b->visit = 0;
initedge(&edge[n][0], b->s1);
initedge(&edge[n][1], b->s2);
}
initedge(&start, fn->start);
flowrk = &start;
nuse = 0;
/* 1. find out constants and dead cfg edges */
for (;;) {
e = flowrk;
if (e) {
flowrk = e->work;
e->work = 0;
if (e->dest == -1 || !e->dead)
continue;
e->dead = 0;
n = e->dest;
b = fn->rpo[n];
for (p=b->phi; p; p=p->link)
visitphi(p, n, fn);
if (b->visit == 0) {
for (i=b->ins; i<&b->ins[b->nins]; i++)
visitins(i, fn);
visitjmp(b, n, fn);
}
b->visit++;
assert(b->jmp.type != Jjmp
|| !edge[n][0].dead
|| flowrk == &edge[n][0]);
}
else if (nuse) {
u = usewrk[--nuse];
n = u->bid;
b = fn->rpo[n];
if (b->visit == 0)
continue;
switch (u->type) {
case UPhi:
visitphi(u->u.phi, u->bid, fn);
break;
case UIns:
visitins(u->u.ins, fn);
break;
case UJmp:
visitjmp(b, n, fn);
break;
default:
die("unreachable");
}
}
else
break;
}
if (debug['F']) {
fprintf(stderr, "\n> SCCP findings:");
for (t=Tmp0; t<fn->ntmp; t++) {
if (val[t] == Bot)
continue;
fprintf(stderr, "\n%10s: ", fn->tmp[t].name);
if (val[t] == Top)
fprintf(stderr, "Top");
else
printref(CON(val[t]), fn, stderr);
}
fprintf(stderr, "\n dead code: ");
}
/* 2. trim dead code, replace constants */
d = 0;
for (pb=&fn->start; (b=*pb);) {
if (b->visit == 0) {
d = 1;
if (debug['F'])
fprintf(stderr, "%s ", b->name);
edgedel(b, &b->s1);
edgedel(b, &b->s2);
*pb = b->link;
continue;
}
for (pp=&b->phi; (p=*pp);)
if (val[p->to.val] != Bot)
*pp = p->link;
else {
for (a=0; a<p->narg; a++)
if (!deadedge(p->blk[a]->id, b->id))
renref(&p->arg[a]);
pp = &p->link;
}
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (renref(&i->to))
*i = (Ins){.op = Onop};
else {
for (n=0; n<2; n++)
renref(&i->arg[n]);
if (isstore(i->op))
if (req(i->arg[0], UNDEF))
*i = (Ins){.op = Onop};
}
renref(&b->jmp.arg);
if (b->jmp.type == Jjnz && rtype(b->jmp.arg) == RCon) {
if (iscon(&fn->con[b->jmp.arg.val], 0, 0)) {
edgedel(b, &b->s1);
b->s1 = b->s2;
b->s2 = 0;
} else
edgedel(b, &b->s2);
b->jmp.type = Jjmp;
b->jmp.arg = R;
}
pb = &b->link;
}
if (debug['F']) {
if (!d)
fprintf(stderr, "(none)");
fprintf(stderr, "\n\n> After constant folding:\n");
printfn(fn, stderr);
}
free(val);
free(edge);
vfree(usewrk);
}
/* boring folding code */
static int
foldint(Con *res, int op, int w, Con *cl, Con *cr)
{
union {
@ -202,7 +516,7 @@ foldflt(Con *res, int op, int w, Con *cl, Con *cr)
}
}
static Ref
static int
opfold(int op, int cls, Con *cl, Con *cr, Fn *fn)
{
Ref r;
@ -210,37 +524,12 @@ opfold(int op, int cls, Con *cl, Con *cr, Fn *fn)
if (cls == Kw || cls == Kl) {
if (foldint(&c, op, cls == Kl, cl, cr))
return R;
return Bot;
} else
foldflt(&c, op, cls == Kd, cl, cr);
if (!KWIDE(cls))
c.bits.i &= 0xffffffff;
r = newcon(&c, fn);
assert(!(cls == Ks || cls == Kd) || c.flt);
return r;
}
/* used by GVN */
Ref
foldref(Fn *fn, Ins *i)
{
Ref rr;
Con *cl, *cr;
if (rtype(i->to) != RTmp)
return R;
if (optab[i->op].canfold) {
if (rtype(i->arg[0]) != RCon)
return R;
cl = &fn->con[i->arg[0].val];
rr = i->arg[1];
if (req(rr, R))
rr = CON_Z;
if (rtype(rr) != RCon)
return R;
cr = &fn->con[rr.val];
return opfold(i->op, i->cls, cl, cr, fn);
}
return R;
return r.val;
}

460
gcm.c
View File

@ -1,460 +0,0 @@
#include "all.h"
#define NOBID (-1u)
static int
isdivwl(Ins *i)
{
switch (i->op) {
case Odiv:
case Orem:
case Oudiv:
case Ourem:
return KBASE(i->cls) == 0;
default:
return 0;
}
}
int
pinned(Ins *i)
{
return optab[i->op].pinned || isdivwl(i);
}
/* pinned ins that can be eliminated if unused */
static int
canelim(Ins *i)
{
return isload(i->op) || isalloc(i->op) || isdivwl(i);
}
static uint earlyins(Fn *, Blk *, Ins *);
static uint
schedearly(Fn *fn, Ref r)
{
Tmp *t;
Blk *b;
if (rtype(r) != RTmp)
return 0;
t = &fn->tmp[r.val];
if (t->gcmbid != NOBID)
return t->gcmbid;
b = fn->rpo[t->bid];
if (t->def) {
assert(b->ins <= t->def && t->def < &b->ins[b->nins]);
t->gcmbid = 0; /* mark as visiting */
t->gcmbid = earlyins(fn, b, t->def);
} else {
/* phis do not move */
t->gcmbid = t->bid;
}
return t->gcmbid;
}
static uint
earlyins(Fn *fn, Blk *b, Ins *i)
{
uint b0, b1;
b0 = schedearly(fn, i->arg[0]);
assert(b0 != NOBID);
b1 = schedearly(fn, i->arg[1]);
assert(b1 != NOBID);
if (fn->rpo[b0]->depth < fn->rpo[b1]->depth) {
assert(dom(fn->rpo[b0], fn->rpo[b1]));
b0 = b1;
}
return pinned(i) ? b->id : b0;
}
static void
earlyblk(Fn *fn, uint bid)
{
Blk *b;
Phi *p;
Ins *i;
uint n;
b = fn->rpo[bid];
for (p=b->phi; p; p=p->link)
for (n=0; n<p->narg; n++)
schedearly(fn, p->arg[n]);
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (pinned(i)) {
schedearly(fn, i->arg[0]);
schedearly(fn, i->arg[1]);
}
schedearly(fn, b->jmp.arg);
}
/* least common ancestor in dom tree */
static uint
lcabid(Fn *fn, uint bid1, uint bid2)
{
Blk *b;
if (bid1 == NOBID)
return bid2;
if (bid2 == NOBID)
return bid1;
b = lca(fn->rpo[bid1], fn->rpo[bid2]);
assert(b);
return b->id;
}
static uint
bestbid(Fn *fn, uint earlybid, uint latebid)
{
Blk *curb, *earlyb, *bestb;
if (latebid == NOBID)
return NOBID; /* unused */
assert(earlybid != NOBID);
earlyb = fn->rpo[earlybid];
bestb = curb = fn->rpo[latebid];
assert(dom(earlyb, curb));
while (curb != earlyb) {
curb = curb->idom;
if (curb->loop < bestb->loop)
bestb = curb;
}
return bestb->id;
}
static uint lateins(Fn *, Blk *, Ins *, Ref r);
static uint latephi(Fn *, Phi *, Ref r);
static uint latejmp(Blk *, Ref r);
/* return lca bid of ref uses */
static uint
schedlate(Fn *fn, Ref r)
{
Tmp *t;
Blk *b;
Use *u;
uint earlybid;
uint latebid;
uint uselatebid;
if (rtype(r) != RTmp)
return NOBID;
t = &fn->tmp[r.val];
if (t->visit)
return t->gcmbid;
t->visit = 1;
earlybid = t->gcmbid;
if (earlybid == NOBID)
return NOBID; /* not used */
/* reuse gcmbid for late bid */
t->gcmbid = t->bid;
latebid = NOBID;
for (u=t->use; u<&t->use[t->nuse]; u++) {
assert(u->bid < fn->nblk);
b = fn->rpo[u->bid];
switch (u->type) {
case UXXX:
die("unreachable");
break;
case UPhi:
uselatebid = latephi(fn, u->u.phi, r);
break;
case UIns:
uselatebid = lateins(fn, b, u->u.ins, r);
break;
case UJmp:
uselatebid = latejmp(b, r);
break;
}
latebid = lcabid(fn, latebid, uselatebid);
}
/* latebid may be NOBID if the temp is used
* in fixed instructions that may be eliminated
* and are themselves unused transitively */
if (t->def && !pinned(t->def))
t->gcmbid = bestbid(fn, earlybid, latebid);
/* else, keep the early one */
/* now, gcmbid is the best bid */
return t->gcmbid;
}
/* returns lca bid of uses or NOBID if
* the definition can be eliminated */
static uint
lateins(Fn *fn, Blk *b, Ins *i, Ref r)
{
uint latebid;
assert(b->ins <= i && i < &b->ins[b->nins]);
assert(req(i->arg[0], r) || req(i->arg[1], r));
latebid = schedlate(fn, i->to);
if (pinned(i)) {
if (latebid == NOBID)
if (canelim(i))
return NOBID;
return b->id;
}
return latebid;
}
static uint
latephi(Fn *fn, Phi *p, Ref r)
{
uint n;
uint latebid;
if (!p->narg)
return NOBID; /* marked as unused */
latebid = NOBID;
for (n = 0; n < p->narg; n++)
if (req(p->arg[n], r))
latebid = lcabid(fn, latebid, p->blk[n]->id);
assert(latebid != NOBID);
return latebid;
}
static uint
latejmp(Blk *b, Ref r)
{
if (req(b->jmp.arg, R))
return NOBID;
else {
assert(req(b->jmp.arg, r));
return b->id;
}
}
static void
lateblk(Fn *fn, uint bid)
{
Blk *b;
Phi **pp;
Ins *i;
b = fn->rpo[bid];
for (pp=&b->phi; *(pp);)
if (schedlate(fn, (*pp)->to) == NOBID) {
(*pp)->narg = 0; /* mark unused */
*pp = (*pp)->link; /* remove phi */
} else
pp = &(*pp)->link;
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (pinned(i))
schedlate(fn, i->to);
}
static void
addgcmins(Fn *fn, Ins *vins, uint nins)
{
Ins *i;
Tmp *t;
Blk *b;
for (i=vins; i<&vins[nins]; i++) {
assert(rtype(i->to) == RTmp);
t = &fn->tmp[i->to.val];
b = fn->rpo[t->gcmbid];
addins(&b->ins, &b->nins, i);
}
}
/* move live instructions to the
* end of their target block; use-
* before-def errors are fixed by
* schedblk */
static void
gcmmove(Fn *fn)
{
Tmp *t;
Ins *vins, *i;
uint nins;
nins = 0;
vins = vnew(nins, sizeof vins[0], PFn);
for (t=fn->tmp; t<&fn->tmp[fn->ntmp]; t++) {
if (t->def == 0)
continue;
if (t->bid == t->gcmbid)
continue;
i = t->def;
if (pinned(i) && !canelim(i))
continue;
assert(rtype(i->to) == RTmp);
assert(t == &fn->tmp[i->to.val]);
if (t->gcmbid != NOBID)
addins(&vins, &nins, i);
*i = (Ins){.op = Onop};
}
addgcmins(fn, vins, nins);
}
/* dfs ordering */
static Ins *
schedins(Fn *fn, Blk *b, Ins *i, Ins **pvins, uint *pnins)
{
Ins *i0, *i1;
Tmp *t;
uint n;
igroup(b, i, &i0, &i1);
for (i=i0; i<i1; i++)
for (n=0; n<2; n++) {
if (rtype(i->arg[n]) != RTmp)
continue;
t = &fn->tmp[i->arg[n].val];
if (t->bid != b->id || !t->def)
continue;
schedins(fn, b, t->def, pvins, pnins);
}
for (i=i0; i<i1; i++) {
addins(pvins, pnins, i);
*i = (Ins){.op = Onop};
}
return i1;
}
/* order ins within a block */
static void
schedblk(Fn *fn)
{
Blk *b;
Ins *i, *vins;
uint nins;
vins = vnew(0, sizeof vins[0], PHeap);
for (b=fn->start; b; b=b->link) {
nins = 0;
for (i=b->ins; i<&b->ins[b->nins];)
i = schedins(fn, b, i, &vins, &nins);
idup(b, vins, nins);
}
vfree(vins);
}
static int
cheap(Ins *i)
{
int x;
if (KBASE(i->cls) != 0)
return 0;
switch (i->op) {
case Oneg:
case Oadd:
case Osub:
case Omul:
case Oand:
case Oor:
case Oxor:
case Osar:
case Oshr:
case Oshl:
return 1;
default:
return iscmp(i->op, &x, &x);
}
}
static void
sinkref(Fn *fn, Blk *b, Ref *pr)
{
Ins i;
Tmp *t;
Ref r;
if (rtype(*pr) != RTmp)
return;
t = &fn->tmp[pr->val];
if (!t->def
|| t->bid == b->id
|| pinned(t->def)
|| !cheap(t->def))
return;
/* sink t->def to b */
i = *t->def;
r = newtmp("snk", t->cls, fn);
t = 0; /* invalidated */
*pr = r;
i.to = r;
fn->tmp[r.val].gcmbid = b->id;
emiti(i);
sinkref(fn, b, &i.arg[0]);
sinkref(fn, b, &i.arg[1]);
}
/* redistribute trivial ops to point of
* use to reduce register pressure
* requires rpo, use; breaks use
*/
static void
sink(Fn *fn)
{
Blk *b;
Ins *i;
for (b=fn->start; b; b=b->link) {
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (isload(i->op))
sinkref(fn, b, &i->arg[0]);
else if (isstore(i->op))
sinkref(fn, b, &i->arg[1]);
sinkref(fn, b, &b->jmp.arg);
}
addgcmins(fn, curi, &insb[NIns] - curi);
}
/* requires use dom
* maintains rpo pred dom
* breaks use
*/
void
gcm(Fn *fn)
{
Tmp *t;
uint bid;
filldepth(fn);
fillloop(fn);
for (t=fn->tmp; t<&fn->tmp[fn->ntmp]; t++) {
t->visit = 0;
t->gcmbid = NOBID;
}
for (bid=0; bid<fn->nblk; bid++)
earlyblk(fn, bid);
for (bid=0; bid<fn->nblk; bid++)
lateblk(fn, bid);
gcmmove(fn);
filluse(fn);
curi = &insb[NIns];
sink(fn);
filluse(fn);
schedblk(fn);
if (debug['G']) {
fprintf(stderr, "\n> After GCM:\n");
printfn(fn, stderr);
}
}

508
gvn.c
View File

@ -1,508 +0,0 @@
#include "all.h"
Ref con01[2];
static inline uint
mix(uint x0, uint x1)
{
return x0 + 17*x1;
}
static inline uint
rhash(Ref r)
{
return mix(r.type, r.val);
}
static uint
ihash(Ins *i)
{
uint h;
h = mix(i->op, i->cls);
h = mix(h, rhash(i->arg[0]));
h = mix(h, rhash(i->arg[1]));
return h;
}
static int
ieq(Ins *ia, Ins *ib)
{
if (ia->op == ib->op)
if (ia->cls == ib->cls)
if (req(ia->arg[0], ib->arg[0]))
if (req(ia->arg[1], ib->arg[1]))
return 1;
return 0;
}
static Ins **gvntbl;
static uint gvntbln;
static Ins *
gvndup(Ins *i, int insert)
{
uint idx;
Ins *ii;
idx = ihash(i) % gvntbln;
for (;;) {
ii = gvntbl[idx];
if (!ii)
break;
if (ieq(i, ii))
return ii;
idx++;
if (gvntbln <= idx)
idx = 0;
}
if (insert)
gvntbl[idx] = i;
return 0;
}
static void
replaceuse(Fn *fn, Use *u, Ref r1, Ref r2)
{
Blk *b;
Ins *i;
Phi *p;
Ref *pr;
Tmp *t2;
int n;
t2 = 0;
if (rtype(r2) == RTmp)
t2 = &fn->tmp[r2.val];
b = fn->rpo[u->bid];
switch (u->type) {
case UPhi:
p = u->u.phi;
for (pr=p->arg; pr<&p->arg[p->narg]; pr++)
if (req(*pr, r1))
*pr = r2;
if (t2)
adduse(t2, UPhi, b, p);
break;
case UIns:
i = u->u.ins;
for (n=0; n<2; n++)
if (req(i->arg[n], r1))
i->arg[n] = r2;
if (t2)
adduse(t2, UIns, b, i);
break;
case UJmp:
if (req(b->jmp.arg, r1))
b->jmp.arg = r2;
if (t2)
adduse(t2, UJmp, b);
break;
case UXXX:
die("unreachable");
}
}
static void
replaceuses(Fn *fn, Ref r1, Ref r2)
{
Tmp *t1;
Use *u;
assert(rtype(r1) == RTmp);
t1 = &fn->tmp[r1.val];
for (u=t1->use; u<&t1->use[t1->nuse]; u++)
replaceuse(fn, u, r1, r2);
t1->nuse = 0;
}
static void
dedupphi(Fn *fn, Blk *b)
{
Phi *p, **pp;
Ref r;
for (pp=&b->phi; (p=*pp);) {
r = phicopyref(fn, b, p);
if (!req(r, R)) {
replaceuses(fn, p->to, r);
p->to = R;
*pp = p->link;
} else
pp = &p->link;
}
}
static int
rcmp(Ref a, Ref b)
{
if (rtype(a) != rtype(b))
return rtype(a) - rtype(b);
return a.val - b.val;
}
static void
normins(Fn *fn, Ins *i)
{
uint n;
int64_t v;
Ref r;
/* truncate constant bits to
* 32 bits for s/w uses */
for (n=0; n<2; n++) {
if (!KWIDE(argcls(i, n)))
if (isconbits(fn, i->arg[n], &v))
if ((v & 0xffffffff) != v)
i->arg[n] = getcon(v & 0xffffffff, fn);
}
/* order arg[0] <= arg[1] for
* commutative ops, preferring
* RTmp in arg[0] */
if (optab[i->op].commutes)
if (rcmp(i->arg[0], i->arg[1]) > 0) {
r = i->arg[1];
i->arg[1] = i->arg[0];
i->arg[0] = r;
}
}
static int
negcon(int cls, Con *c)
{
static Con z = {.type = CBits, .bits.i = 0};
return foldint(c, Osub, cls, &z, c);
}
static void
assoccon(Fn *fn, Blk *b, Ins *i1)
{
Tmp *t2;
Ins *i2;
int op, fail;
Con c, c1, c2;
op = i1->op;
if (op == Osub)
op = Oadd;
if (!optab[op].assoc
|| KBASE(i1->cls) != 0
|| rtype(i1->arg[0]) != RTmp
|| rtype(i1->arg[1]) != RCon)
return;
c1 = fn->con[i1->arg[1].val];
t2 = &fn->tmp[i1->arg[0].val];
if (t2->def == 0)
return;
i2 = t2->def;
if (op != (i2->op == Osub ? Oadd : i2->op)
|| rtype(i2->arg[1]) != RCon)
return;
c2 = fn->con[i2->arg[1].val];
assert(KBASE(i2->cls) == 0);
assert(KWIDE(i2->cls) >= KWIDE(i1->cls));
if (i1->op == Osub && negcon(i1->cls, &c1))
return;
if (i2->op == Osub && negcon(i2->cls, &c2))
return;
if (foldint(&c, op, i1->cls, &c1, &c2))
return;
if (op == Oadd && c.type == CBits)
if ((i1->cls == Kl && c.bits.i < 0)
|| (i1->cls == Kw && (int32_t)c.bits.i < 0)) {
fail = negcon(i1->cls, &c);
assert(fail == 0);
op = Osub;
}
i1->op = op;
i1->arg[0] = i2->arg[0];
i1->arg[1] = newcon(&c, fn);
adduse(&fn->tmp[i1->arg[0].val], UIns, b, i1);
}
static void
killins(Fn *fn, Ins *i, Ref r)
{
replaceuses(fn, i->to, r);
*i = (Ins){.op = Onop};
}
static void
dedupins(Fn *fn, Blk *b, Ins *i)
{
Ref r;
Ins *i1;
normins(fn, i);
if (i->op == Onop || pinned(i))
return;
/* when sel instructions are inserted
* before gvn, we may want to optimize
* them here */
assert(i->op != Osel0);
assert(!req(i->to, R));
assoccon(fn, b, i);
r = copyref(fn, b, i);
if (!req(r, R)) {
killins(fn, i, r);
return;
}
r = foldref(fn, i);
if (!req(r, R)) {
killins(fn, i, r);
return;
}
i1 = gvndup(i, 1);
if (i1) {
killins(fn, i, i1->to);
return;
}
}
int
cmpeqz(Fn *fn, Ref r, Ref *arg, int *cls, int *eqval)
{
Ins *i;
if (rtype(r) != RTmp)
return 0;
i = fn->tmp[r.val].def;
if (i)
if (optab[i->op].cmpeqwl)
if (req(i->arg[1], CON_Z)) {
*arg = i->arg[0];
*cls = argcls(i, 0);
*eqval = optab[i->op].eqval;
return 1;
}
return 0;
}
static int
branchdom(Fn *fn, Blk *bif, Blk *bbr1, Blk *bbr2, Blk *b)
{
assert(bif->jmp.type == Jjnz);
if (b != bif
&& dom(bbr1, b)
&& !reachesnotvia(fn, bbr2, b, bif))
return 1;
return 0;
}
static int
domzero(Fn *fn, Blk *d, Blk *b, int *z)
{
if (branchdom(fn, d, d->s1, d->s2, b)) {
*z = 0;
return 1;
}
if (branchdom(fn, d, d->s2, d->s1, b)) {
*z = 1;
return 1;
}
return 0;
}
/* infer 0/non-0 value from dominating jnz */
int
zeroval(Fn *fn, Blk *b, Ref r, int cls, int *z)
{
Blk *d;
Ref arg;
int cls1, eqval;
for (d=b->idom; d; d=d->idom) {
if (d->jmp.type != Jjnz)
continue;
if (req(r, d->jmp.arg)
&& cls == Kw
&& domzero(fn, d, b, z)) {
return 1;
}
if (cmpeqz(fn, d->jmp.arg, &arg, &cls1, &eqval)
&& req(r, arg)
&& cls == cls1
&& domzero(fn, d, b, z)) {
*z ^= eqval;
return 1;
}
}
return 0;
}
static int
usecls(Use *u, Ref r, int cls)
{
int k;
switch (u->type) {
case UIns:
k = Kx; /* widest use */
if (req(u->u.ins->arg[0], r))
k = argcls(u->u.ins, 0);
if (req(u->u.ins->arg[1], r))
if (k == Kx || !KWIDE(k))
k = argcls(u->u.ins, 1);
return k == Kx ? cls : k;
case UPhi:
if (req(u->u.phi->to, R))
return cls; /* eliminated */
return u->u.phi->cls;
case UJmp:
return Kw;
default:
break;
}
die("unreachable");
}
static void
propjnz0(Fn *fn, Blk *bif, Blk *s0, Blk *snon0, Ref r, int cls)
{
Blk *b;
Tmp *t;
Use *u;
if (s0->npred != 1 || rtype(r) != RTmp)
return;
t = &fn->tmp[r.val];
for (u=t->use; u<&t->use[t->nuse]; u++) {
b = fn->rpo[u->bid];
/* we may compare an l temp with a w
* comparison; so check that the use
* does not involve high bits */
if (usecls(u, r, cls) == cls)
if (branchdom(fn, bif, s0, snon0, b))
replaceuse(fn, u, r, CON_Z);
}
}
static void
dedupjmp(Fn *fn, Blk *b)
{
Blk **ps;
int64_t v;
Ref arg;
int cls, eqval, z;
if (b->jmp.type != Jjnz)
return;
/* propagate jmp arg as 0 through s2 */
propjnz0(fn, b, b->s2, b->s1, b->jmp.arg, Kw);
/* propagate cmp eq/ne 0 def of jmp arg as 0 */
if (cmpeqz(fn, b->jmp.arg, &arg, &cls, &eqval)) {
ps = (Blk*[]){b->s1, b->s2};
propjnz0(fn, b, ps[eqval^1], ps[eqval], arg, cls);
}
/* collapse trivial/constant jnz to jmp */
v = 1;
z = 0;
if (b->s1 == b->s2
|| isconbits(fn, b->jmp.arg, &v)
|| zeroval(fn, b, b->jmp.arg, Kw, &z)) {
if (v == 0 || z)
b->s1 = b->s2;
/* we later move active ins out of dead blks */
b->s2 = 0;
b->jmp.type = Jjmp;
b->jmp.arg = R;
}
}
static void
rebuildcfg(Fn *fn)
{
uint n, nblk;
Blk *b, *s, **rpo;
Ins *i;
nblk = fn->nblk;
rpo = emalloc(nblk * sizeof rpo[0]);
memcpy(rpo, fn->rpo, nblk * sizeof rpo[0]);
fillcfg(fn);
/* move instructions that were in
* killed blocks and may be active
* in the computation in the start
* block */
s = fn->start;
for (n=0; n<nblk; n++) {
b = rpo[n];
if (b->id != -1u)
continue;
/* blk unreachable after GVN */
assert(b != s);
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (!optab[i->op].pinned)
if (gvndup(i, 0) == i)
addins(&s->ins, &s->nins, i);
}
free(rpo);
}
/* requires rpo pred ssa use
* recreates rpo preds
* breaks pred use dom ssa (GCM fixes ssa)
*/
void
gvn(Fn *fn)
{
Blk *b;
Phi *p;
Ins *i;
uint n, nins;
con01[0] = getcon(0, fn);
con01[1] = getcon(1, fn);
/* copy.c uses the visit bit */
for (b=fn->start; b; b=b->link)
for (p=b->phi; p; p=p->link)
p->visit = 0;
fillloop(fn);
narrowpars(fn);
filluse(fn);
ssacheck(fn);
nins = 0;
for (b=fn->start; b; b=b->link) {
b->visit = 0;
nins += b->nins;
}
gvntbln = nins + nins/2;
gvntbl = emalloc(gvntbln * sizeof gvntbl[0]);
for (n=0; n<fn->nblk; n++) {
b = fn->rpo[n];
dedupphi(fn, b);
for (i=b->ins; i<&b->ins[b->nins]; i++)
dedupins(fn, b, i);
dedupjmp(fn, b);
}
rebuildcfg(fn);
free(gvntbl);
gvntbl = 0;
if (debug['G']) {
fprintf(stderr, "\n> After GVN:\n");
printfn(fn, stderr);
}
}

121
ifopt.c
View File

@ -1,121 +0,0 @@
#include "all.h"
enum {
MaxIns = 2,
MaxPhis = 2,
};
static int
okbranch(Blk *b)
{
Ins *i;
int n;
n = 0;
for (i=b->ins; i<&b->ins[b->nins]; i++)
if (i->op != Odbgloc) {
if (pinned(i))
return 0;
if (i->op != Onop)
n++;
}
return n <= MaxIns;
}
static int
okjoin(Blk *b)
{
Phi *p;
int n;
n = 0;
for (p=b->phi; p; p=p->link) {
if (KBASE(p->cls) != 0)
return 0;
n++;
}
return n <= MaxPhis;
}
static int
okgraph(Blk *ifb, Blk *thenb, Blk *elseb, Blk *joinb)
{
if (joinb->npred != 2 || !okjoin(joinb))
return 0;
assert(thenb != elseb);
if (thenb != ifb && !okbranch(thenb))
return 0;
if (elseb != ifb && !okbranch(elseb))
return 0;
return 1;
}
static void
convert(Blk *ifb, Blk *thenb, Blk *elseb, Blk *joinb)
{
Ins *ins, sel;
Phi *p;
uint nins;
ins = vnew(0, sizeof ins[0], PHeap);
nins = 0;
addbins(&ins, &nins, ifb);
if (thenb != ifb)
addbins(&ins, &nins, thenb);
if (elseb != ifb)
addbins(&ins, &nins, elseb);
assert(joinb->npred == 2);
if (joinb->phi) {
sel = (Ins){
.op = Osel0, .cls = Kw,
.arg = {ifb->jmp.arg},
};
addins(&ins, &nins, &sel);
}
sel = (Ins){.op = Osel1};
for (p=joinb->phi; p; p=p->link) {
sel.to = p->to;
sel.cls = p->cls;
sel.arg[0] = phiarg(p, thenb);
sel.arg[1] = phiarg(p, elseb);
addins(&ins, &nins, &sel);
}
idup(ifb, ins, nins);
ifb->jmp.type = Jjmp;
ifb->jmp.arg = R;
ifb->s1 = joinb;
ifb->s2 = 0;
joinb->npred = 1;
joinb->pred[0] = ifb;
joinb->phi = 0;
vfree(ins);
}
/* eliminate if-then[-else] graphlets
* using sel instructions
* needs rpo pred use; breaks cfg use
*/
void
ifconvert(Fn *fn)
{
Blk *ifb, *thenb, *elseb, *joinb;
if (debug['K'])
fputs("\n> If-conversion:\n", stderr);
for (ifb=fn->start; ifb; ifb=ifb->link)
if (ifgraph(ifb, &thenb, &elseb, &joinb))
if (okgraph(ifb, thenb, elseb, joinb)) {
if (debug['K'])
fprintf(stderr,
" @%s -> @%s, @%s -> @%s\n",
ifb->name, thenb->name, elseb->name,
joinb->name);
convert(ifb, thenb, elseb, joinb);
}
if (debug['K']) {
fprintf(stderr, "\n> After if-conversion:\n");
printfn(fn, stderr);
}
}

4
load.c
View File

@ -380,7 +380,6 @@ def(Slice sl, bits msk, Blk *b, Ins *i, Loc *il)
goto Load;
p->arg[np] = r1;
p->blk[np] = bp;
/* XXX - multiplicity in predecessors!!! */
}
if (msk != msks)
mask(cls, &r, msk, il);
@ -482,7 +481,8 @@ loadopt(Fn *fn)
vgrow(&ib, ++nt);
ib[nt-1] = *i;
}
idup(b, ib, nt);
b->nins = nt;
idup(&b->ins, ib, nt);
}
vfree(ib);
vfree(ilog);

32
main.c
View File

@ -11,7 +11,6 @@ char debug['Z'+1] = {
['N'] = 0, /* ssa construction */
['C'] = 0, /* copy elimination */
['F'] = 0, /* constant folding */
['K'] = 0, /* if-conversion */
['A'] = 0, /* abi lowering */
['I'] = 0, /* instruction selection */
['L'] = 0, /* liveness */
@ -21,7 +20,6 @@ char debug['Z'+1] = {
extern Target T_amd64_sysv;
extern Target T_amd64_apple;
extern Target T_amd64_win;
extern Target T_arm64;
extern Target T_arm64_apple;
extern Target T_rv64;
@ -29,7 +27,6 @@ extern Target T_rv64;
static Target *tlist[] = {
&T_amd64_sysv,
&T_amd64_apple,
&T_amd64_win,
&T_arm64,
&T_arm64_apple,
&T_rv64,
@ -62,7 +59,8 @@ func(Fn *fn)
printfn(fn, stderr);
}
T.abi0(fn);
fillcfg(fn);
fillrpo(fn);
fillpreds(fn);
filluse(fn);
promote(fn);
filluse(fn);
@ -75,37 +73,25 @@ func(Fn *fn)
fillalias(fn);
coalesce(fn);
filluse(fn);
filldom(fn);
ssacheck(fn);
gvn(fn);
fillcfg(fn);
simplcfg(fn);
copy(fn);
filluse(fn);
filldom(fn);
gcm(fn);
filluse(fn);
ssacheck(fn);
if (T.cansel) {
ifconvert(fn);
fillcfg(fn);
filluse(fn);
filldom(fn);
ssacheck(fn);
}
fold(fn);
T.abi1(fn);
simpl(fn);
fillcfg(fn);
fillpreds(fn);
filluse(fn);
T.isel(fn);
fillcfg(fn);
fillrpo(fn);
filllive(fn);
fillloop(fn);
fillcost(fn);
spill(fn);
rega(fn);
fillcfg(fn);
fillrpo(fn);
simpljmp(fn);
fillcfg(fn);
fillpreds(fn);
fillrpo(fn);
assert(fn->rpo[0] == fn->start);
for (n=0;; n++)
if (n == fn->nblk-1) {

9
mem.c
View File

@ -296,7 +296,7 @@ coalesce(Fn *fn)
if (s->l) {
radd(&s->r, ip);
if (b->loop != -1) {
assert(b->loop >= n);
assert(b->loop > n);
radd(&s->r, br[b->loop].b - 1);
}
}
@ -447,15 +447,10 @@ coalesce(Fn *fn)
if (i->op == Oblit0)
if (slot(&s, &off0, i->arg[0], fn, sl))
if (slot(&s0, &off1, i->arg[1], fn, sl))
if (s->s == s0->s) {
if (off0 < off1) {
if (s->s == s0->s && off0 < off1) {
sz = rsval((i+1)->arg[0]);
assert(sz >= 0);
(i+1)->arg[0] = INT(-sz);
} else if (off0 == off1) {
*i = (Ins){.op = Onop};
*(i+1) = (Ins){.op = Onop};
}
}
}
vfree(bl);

View File

@ -70,7 +70,7 @@ struct Stmt {
int yylex(void), yyerror(char *);
Symb expr(Node *), lval(Node *);
void branch(Node *, int, int);
void bool(Node *, int, int);
FILE *of;
int line;
@ -329,7 +329,7 @@ expr(Node *n)
case 'a':
l = lbl;
lbl += 3;
branch(n, l, l+1);
bool(n, l, l+1);
fprintf(of, "@l%d\n", l);
fprintf(of, "\tjmp @l%d\n", l+2);
fprintf(of, "@l%d\n", l+1);
@ -468,7 +468,7 @@ lval(Node *n)
}
void
branch(Node *n, int lt, int lf)
bool(Node *n, int lt, int lf)
{
Symb s;
int l;
@ -483,16 +483,16 @@ branch(Node *n, int lt, int lf)
case 'o':
l = lbl;
lbl += 1;
branch(n->l, lt, l);
bool(n->l, lt, l);
fprintf(of, "@l%d\n", l);
branch(n->r, lt, lf);
bool(n->r, lt, lf);
break;
case 'a':
l = lbl;
lbl += 1;
branch(n->l, l, lf);
bool(n->l, l, lf);
fprintf(of, "@l%d\n", l);
branch(n->r, lt, lf);
bool(n->r, lt, lf);
break;
}
}
@ -526,7 +526,7 @@ stmt(Stmt *s, int b)
case If:
l = lbl;
lbl += 3;
branch(s->p1, l, l+1);
bool(s->p1, l, l+1);
fprintf(of, "@l%d\n", l);
if (!(r=stmt(s->p2, b)))
if (s->p3)
@ -540,7 +540,7 @@ stmt(Stmt *s, int b)
l = lbl;
lbl += 3;
fprintf(of, "@l%d\n", l);
branch(s->p1, l+1, l+2);
bool(s->p1, l+1, l+2);
fprintf(of, "@l%d\n", l+1);
if (!stmt(s->p2, l+2))
fprintf(of, "\tjmp @l%d\n", l);

311
ops.h
View File

@ -6,217 +6,184 @@
#define V(Imm)
#endif
#ifndef F
#define F(a,b,c,d,e,f,g,h,i,j)
#endif
#define T(a,b,c,d,e,f,g,h) { \
{[Kw]=K##a, [Kl]=K##b, [Ks]=K##c, [Kd]=K##d}, \
{[Kw]=K##e, [Kl]=K##f, [Ks]=K##g, [Kd]=K##h} \
}
/*********************/
/* PUBLIC OPERATIONS */
/*********************/
/* can fold */
/* | has identity */
/* | | identity value for arg[1] */
/* | | | commutative */
/* | | | | associative */
/* | | | | | idempotent */
/* | | | | | | c{eq,ne}[wl] */
/* | | | | | | | c[us][gl][et][wl] */
/* | | | | | | | | value if = args */
/* | | | | | | | | | pinned */
/* Arithmetic and Bits v v v v v v v v v v */
O(add, T(w,l,s,d, w,l,s,d), F(1,1,0,1,1,0,0,0,0,0)) X(2,1,0) V(1)
O(sub, T(w,l,s,d, w,l,s,d), F(1,1,0,0,0,0,0,0,0,0)) X(2,1,0) V(0)
O(neg, T(w,l,s,d, x,x,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(1,1,0) V(0)
O(div, T(w,l,s,d, w,l,s,d), F(1,1,1,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(rem, T(w,l,e,e, w,l,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(udiv, T(w,l,e,e, w,l,e,e), F(1,1,1,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(urem, T(w,l,e,e, w,l,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(mul, T(w,l,s,d, w,l,s,d), F(1,1,1,1,0,0,0,0,0,0)) X(2,0,0) V(0)
O(and, T(w,l,e,e, w,l,e,e), F(1,0,0,1,1,1,0,0,0,0)) X(2,1,0) V(1)
O(or, T(w,l,e,e, w,l,e,e), F(1,1,0,1,1,1,0,0,0,0)) X(2,1,0) V(1)
O(xor, T(w,l,e,e, w,l,e,e), F(1,1,0,1,1,0,0,0,0,0)) X(2,1,0) V(1)
O(sar, T(w,l,e,e, w,w,e,e), F(1,1,0,0,0,0,0,0,0,0)) X(1,1,0) V(1)
O(shr, T(w,l,e,e, w,w,e,e), F(1,1,0,0,0,0,0,0,0,0)) X(1,1,0) V(1)
O(shl, T(w,l,e,e, w,w,e,e), F(1,1,0,0,0,0,0,0,0,0)) X(1,1,0) V(1)
/* Arithmetic and Bits */
O(add, T(w,l,s,d, w,l,s,d), 1) X(2, 1, 0) V(1)
O(sub, T(w,l,s,d, w,l,s,d), 1) X(2, 1, 0) V(0)
O(neg, T(w,l,s,d, x,x,x,x), 1) X(1, 1, 0) V(0)
O(div, T(w,l,s,d, w,l,s,d), 1) X(0, 0, 0) V(0)
O(rem, T(w,l,e,e, w,l,e,e), 1) X(0, 0, 0) V(0)
O(udiv, T(w,l,e,e, w,l,e,e), 1) X(0, 0, 0) V(0)
O(urem, T(w,l,e,e, w,l,e,e), 1) X(0, 0, 0) V(0)
O(mul, T(w,l,s,d, w,l,s,d), 1) X(2, 0, 0) V(0)
O(and, T(w,l,e,e, w,l,e,e), 1) X(2, 1, 0) V(1)
O(or, T(w,l,e,e, w,l,e,e), 1) X(2, 1, 0) V(1)
O(xor, T(w,l,e,e, w,l,e,e), 1) X(2, 1, 0) V(1)
O(sar, T(w,l,e,e, w,w,e,e), 1) X(1, 1, 0) V(1)
O(shr, T(w,l,e,e, w,w,e,e), 1) X(1, 1, 0) V(1)
O(shl, T(w,l,e,e, w,w,e,e), 1) X(1, 1, 0) V(1)
/* Comparisons */
O(ceqw, T(w,w,e,e, w,w,e,e), F(1,1,1,1,0,0,1,0,1,0)) X(0,1,0) V(0)
O(cnew, T(w,w,e,e, w,w,e,e), F(1,1,0,1,0,0,1,0,0,0)) X(0,1,0) V(0)
O(csgew, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(csgtw, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(0)
O(cslew, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(csltw, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(1)
O(cugew, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(cugtw, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(0)
O(culew, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(cultw, T(w,w,e,e, w,w,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(1)
O(ceqw, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(cnew, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(csgew, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(csgtw, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(cslew, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(csltw, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(1)
O(cugew, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(cugtw, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(culew, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(0)
O(cultw, T(w,w,e,e, w,w,e,e), 1) X(0, 1, 0) V(1)
O(ceql, T(l,l,e,e, l,l,e,e), F(1,0,0,1,0,0,1,0,1,0)) X(0,1,0) V(0)
O(cnel, T(l,l,e,e, l,l,e,e), F(1,0,0,1,0,0,1,0,0,0)) X(0,1,0) V(0)
O(csgel, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(csgtl, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(0)
O(cslel, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(csltl, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(1)
O(cugel, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(cugtl, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(0)
O(culel, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,1,0)) X(0,1,0) V(0)
O(cultl, T(l,l,e,e, l,l,e,e), F(1,0,0,0,0,0,0,1,0,0)) X(0,1,0) V(1)
O(ceql, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(cnel, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(csgel, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(csgtl, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(cslel, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(csltl, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(1)
O(cugel, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(cugtl, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(culel, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(0)
O(cultl, T(l,l,e,e, l,l,e,e), 1) X(0, 1, 0) V(1)
O(ceqs, T(s,s,e,e, s,s,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cges, T(s,s,e,e, s,s,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cgts, T(s,s,e,e, s,s,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cles, T(s,s,e,e, s,s,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(clts, T(s,s,e,e, s,s,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cnes, T(s,s,e,e, s,s,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cos, T(s,s,e,e, s,s,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cuos, T(s,s,e,e, s,s,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(ceqs, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cges, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cgts, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cles, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(clts, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cnes, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cos, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(cuos, T(s,s,e,e, s,s,e,e), 1) X(0, 1, 0) V(0)
O(ceqd, T(d,d,e,e, d,d,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cged, T(d,d,e,e, d,d,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cgtd, T(d,d,e,e, d,d,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cled, T(d,d,e,e, d,d,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cltd, T(d,d,e,e, d,d,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cned, T(d,d,e,e, d,d,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cod, T(d,d,e,e, d,d,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(cuod, T(d,d,e,e, d,d,e,e), F(1,0,0,1,0,0,0,0,0,0)) X(0,1,0) V(0)
O(ceqd, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cged, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cgtd, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cled, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cltd, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cned, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cod, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
O(cuod, T(d,d,e,e, d,d,e,e), 1) X(0, 1, 0) V(0)
/* Memory */
O(storeb, T(w,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(storeh, T(w,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(storew, T(w,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(storel, T(l,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(stores, T(s,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(stored, T(d,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(storeb, T(w,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(storeh, T(w,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(storew, T(w,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(storel, T(l,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(stores, T(s,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(stored, T(d,e,e,e, m,e,e,e), 0) X(0, 0, 1) V(0)
O(loadsb, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loadub, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loadsh, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loaduh, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loadsw, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loaduw, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(load, T(m,m,m,m, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(loadsb, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(loadub, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(loadsh, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(loaduh, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(loadsw, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(loaduw, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(load, T(m,m,m,m, x,x,x,x), 0) X(0, 0, 1) V(0)
/* Extensions and Truncations */
O(extsb, T(w,w,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extub, T(w,w,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extsh, T(w,w,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extuh, T(w,w,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extsw, T(e,w,e,e, e,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extuw, T(e,w,e,e, e,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(extsb, T(w,w,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(extub, T(w,w,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(extsh, T(w,w,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(extuh, T(w,w,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(extsw, T(e,w,e,e, e,x,e,e), 1) X(0, 0, 1) V(0)
O(extuw, T(e,w,e,e, e,x,e,e), 1) X(0, 0, 1) V(0)
O(exts, T(e,e,e,s, e,e,e,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(truncd, T(e,e,d,e, e,e,x,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(stosi, T(s,s,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(stoui, T(s,s,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(dtosi, T(d,d,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(dtoui, T(d,d,e,e, x,x,e,e), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(swtof, T(e,e,w,w, e,e,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(uwtof, T(e,e,w,w, e,e,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(sltof, T(e,e,l,l, e,e,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(ultof, T(e,e,l,l, e,e,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(cast, T(s,d,w,l, x,x,x,x), F(1,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(exts, T(e,e,e,s, e,e,e,x), 1) X(0, 0, 1) V(0)
O(truncd, T(e,e,d,e, e,e,x,e), 1) X(0, 0, 1) V(0)
O(stosi, T(s,s,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(stoui, T(s,s,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(dtosi, T(d,d,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(dtoui, T(d,d,e,e, x,x,e,e), 1) X(0, 0, 1) V(0)
O(swtof, T(e,e,w,w, e,e,x,x), 1) X(0, 0, 1) V(0)
O(uwtof, T(e,e,w,w, e,e,x,x), 1) X(0, 0, 1) V(0)
O(sltof, T(e,e,l,l, e,e,x,x), 1) X(0, 0, 1) V(0)
O(ultof, T(e,e,l,l, e,e,x,x), 1) X(0, 0, 1) V(0)
O(cast, T(s,d,w,l, x,x,x,x), 1) X(0, 0, 1) V(0)
/* Stack Allocation */
O(alloc4, T(e,l,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(alloc8, T(e,l,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(alloc16, T(e,l,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(alloc4, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(alloc8, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(alloc16, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
/* Variadic Function Helpers */
O(vaarg, T(m,m,m,m, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(vastart, T(m,e,e,e, x,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(vaarg, T(m,m,m,m, x,x,x,x), 0) X(0, 0, 0) V(0)
O(vastart, T(m,e,e,e, x,e,e,e), 0) X(0, 0, 0) V(0)
O(copy, T(w,l,s,d, x,x,x,x), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(copy, T(w,l,s,d, x,x,x,x), 0) X(0, 0, 1) V(0)
/* Debug */
O(dbgloc, T(w,e,e,e, w,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,1) V(0)
O(dbgloc, T(w,e,e,e, w,e,e,e), 0) X(0, 0, 1) V(0)
/****************************************/
/* INTERNAL OPERATIONS (keep nop first) */
/****************************************/
/* Miscellaneous and Architecture-Specific Operations */
O(nop, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(addr, T(m,m,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(blit0, T(m,e,e,e, m,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,1,0) V(0)
O(blit1, T(w,e,e,e, x,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,1,0) V(0)
O(sel0, T(w,e,e,e, x,e,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(sel1, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(swap, T(w,l,s,d, w,l,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(1,0,0) V(0)
O(sign, T(w,l,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(salloc, T(e,l,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xidiv, T(w,l,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(1,0,0) V(0)
O(xdiv, T(w,l,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(1,0,0) V(0)
O(xcmp, T(w,l,s,d, w,l,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(1,1,0) V(0)
O(xtest, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(1,1,0) V(0)
O(acmp, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(acmn, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(afcmp, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(reqz, T(w,l,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(rnez, T(w,l,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(nop, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 1) V(0)
O(addr, T(m,m,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(blit0, T(m,e,e,e, m,e,e,e), 0) X(0, 1, 0) V(0)
O(blit1, T(w,e,e,e, x,e,e,e), 0) X(0, 1, 0) V(0)
O(swap, T(w,l,s,d, w,l,s,d), 0) X(1, 0, 0) V(0)
O(sign, T(w,l,e,e, x,x,e,e), 0) X(0, 0, 0) V(0)
O(salloc, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(xidiv, T(w,l,e,e, x,x,e,e), 0) X(1, 0, 0) V(0)
O(xdiv, T(w,l,e,e, x,x,e,e), 0) X(1, 0, 0) V(0)
O(xcmp, T(w,l,s,d, w,l,s,d), 0) X(1, 1, 0) V(0)
O(xtest, T(w,l,e,e, w,l,e,e), 0) X(1, 1, 0) V(0)
O(acmp, T(w,l,e,e, w,l,e,e), 0) X(0, 0, 0) V(0)
O(acmn, T(w,l,e,e, w,l,e,e), 0) X(0, 0, 0) V(0)
O(afcmp, T(e,e,s,d, e,e,s,d), 0) X(0, 0, 0) V(0)
O(reqz, T(w,l,e,e, x,x,e,e), 0) X(0, 0, 0) V(0)
O(rnez, T(w,l,e,e, x,x,e,e), 0) X(0, 0, 0) V(0)
/* Arguments, Parameters, and Calls */
O(par, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(parsb, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(parub, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(parsh, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(paruh, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(parc, T(e,x,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(pare, T(e,x,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(arg, T(w,l,s,d, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(argsb, T(w,e,e,e, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(argub, T(w,e,e,e, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(argsh, T(w,e,e,e, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(arguh, T(w,e,e,e, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(argc, T(e,x,e,e, e,l,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(arge, T(e,l,e,e, e,x,e,e), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(argv, T(x,x,x,x, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(call, T(m,m,m,m, x,x,x,x), F(0,0,0,0,0,0,0,0,0,1)) X(0,0,0) V(0)
O(par, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(parsb, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(parub, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(parsh, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(paruh, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(parc, T(e,x,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(pare, T(e,x,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(arg, T(w,l,s,d, x,x,x,x), 0) X(0, 0, 0) V(0)
O(argsb, T(w,e,e,e, x,x,x,x), 0) X(0, 0, 0) V(0)
O(argub, T(w,e,e,e, x,x,x,x), 0) X(0, 0, 0) V(0)
O(argsh, T(w,e,e,e, x,x,x,x), 0) X(0, 0, 0) V(0)
O(arguh, T(w,e,e,e, x,x,x,x), 0) X(0, 0, 0) V(0)
O(argc, T(e,x,e,e, e,l,e,e), 0) X(0, 0, 0) V(0)
O(arge, T(e,l,e,e, e,x,e,e), 0) X(0, 0, 0) V(0)
O(argv, T(x,x,x,x, x,x,x,x), 0) X(0, 0, 0) V(0)
O(call, T(m,m,m,m, x,x,x,x), 0) X(0, 0, 0) V(0)
/* Flags Setting */
O(flagieq, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagine, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagisge, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagisgt, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagisle, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagislt, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagiuge, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagiugt, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagiule, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagiult, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfeq, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfge, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfgt, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfle, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagflt, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfne, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfo, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagfuo, T(x,x,e,e, x,x,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,1) V(0)
O(flagieq, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagine, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagisge, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagisgt, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagisle, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagislt, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagiuge, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagiugt, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagiule, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagiult, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfeq, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfge, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfgt, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfle, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagflt, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfne, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfo, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
O(flagfuo, T(x,x,e,e, x,x,e,e), 0) X(0, 0, 1) V(0)
/* Backend Flag Select (Condition Move) */
O(xselieq, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xseline, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselisge, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselisgt, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselisle, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselislt, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xseliuge, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xseliugt, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xseliule, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xseliult, T(w,l,e,e, w,l,e,e), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfeq, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfge, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfgt, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfle, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselflt, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfne, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfo, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
O(xselfuo, T(e,e,s,d, e,e,s,d), F(0,0,0,0,0,0,0,0,0,0)) X(0,0,0) V(0)
#undef T
#undef X

72
parse.c
View File

@ -15,17 +15,8 @@ enum {
};
Op optab[NOp] = {
#undef F
#define F(cf, hi, id, co, as, im, ic, lg, cv, pn) \
.canfold = cf, \
.hasid = hi, .idval = id, \
.commutes = co, .assoc = as, \
.idemp = im, \
.cmpeqwl = ic, .cmplgtewl = lg, .eqval = cv, \
.pinned = pn
#define O(op, k, flags) [O##op]={.name = #op, .argcls = k, flags},
#define O(op, t, cf) [O##op]={#op, t, cf},
#include "ops.h"
#undef F
};
typedef enum {
@ -57,7 +48,6 @@ enum Token {
Thlt,
Texport,
Tthread,
Tcommon,
Tfunc,
Ttype,
Tdata,
@ -116,7 +106,6 @@ static char *kwmap[Ntok] = {
[Thlt] = "hlt",
[Texport] = "export",
[Tthread] = "thread",
[Tcommon] = "common",
[Tfunc] = "function",
[Ttype] = "type",
[Tdata] = "data",
@ -143,7 +132,7 @@ enum {
TMask = 16383, /* for temps hash */
BMask = 8191, /* for blocks hash */
K = 11183273, /* found using tools/lexh.c */
K = 9583425, /* found using tools/lexh.c */
M = 23,
};
@ -161,8 +150,7 @@ static struct {
static int lnum;
static Fn *curf;
static int *tmph;
static int tmphcap;
static int tmph[TMask+1];
static Phi **plink;
static Blk *curb;
static Blk **blink;
@ -215,15 +203,12 @@ getint()
n = 0;
c = fgetc(inf);
m = (c == '-');
if (m) {
if (m || c == '+')
c = fgetc(inf);
if (!isdigit(c))
err("integer expected");
}
do {
n = 10*n + (c - '0');
c = fgetc(inf);
} while (isdigit(c));
} while ('0' <= c && c <= '9');
ungetc(c, inf);
if (m)
n = 1 + ~n;
@ -292,7 +277,7 @@ lex()
lnum++;
return Tnl;
}
if (isdigit(c) || c == '-') {
if (isdigit(c) || c == '-' || c == '+') {
ungetc(c, inf);
tokval.num = getint();
return Tint;
@ -397,27 +382,19 @@ expect(int t)
static Ref
tmpref(char *v)
{
int t, i;
int t, *h;
if (tmphcap/2 <= curf->ntmp-Tmp0) {
free(tmph);
tmphcap = tmphcap ? tmphcap*2 : TMask+1;
tmph = emalloc(tmphcap * sizeof tmph[0]);
for (t=Tmp0; t<curf->ntmp; t++) {
i = hash(curf->tmp[t].name) & (tmphcap-1);
for (; tmph[i]; i=(i+1) & (tmphcap-1))
;
tmph[i] = t;
}
}
i = hash(v) & (tmphcap-1);
for (; tmph[i]; i=(i+1) & (tmphcap-1)) {
t = tmph[i];
h = &tmph[hash(v) & TMask];
t = *h;
if (t) {
if (strcmp(curf->tmp[t].name, v) == 0)
return TMP(t);
for (t=curf->ntmp-1; t>=Tmp0; t--)
if (strcmp(curf->tmp[t].name, v) == 0)
return TMP(t);
}
t = curf->ntmp;
tmph[i] = t;
*h = t;
newtmp(0, Kx, curf);
strcpy(curf->tmp[t].name, v);
return TMP(t);
@ -591,7 +568,8 @@ findblk(char *name)
static void
closeblk()
{
idup(curb, insb, curi-insb);
curb->nins = curi - insb;
idup(&curb->ins, insb, curb->nins);
blink = &curb->link;
curi = insb;
}
@ -702,7 +680,6 @@ parseline(PState ps)
goto Ins;
}
if (op == Tcall) {
curf->leaf = 0;
arg[0] = parseref();
parserefl(1);
op = Ocall;
@ -919,7 +896,6 @@ parsefn(Lnk *lnk)
curf->con[0].bits.i = 0xdeaddead; /* UNDEF */
curf->con[1].type = CBits;
curf->lnk = *lnk;
curf->leaf = 1;
blink = &curf->start;
curf->retty = Kx;
if (peek() != Tglo)
@ -943,12 +919,12 @@ parsefn(Lnk *lnk)
curf->mem = vnew(0, sizeof curf->mem[0], PFn);
curf->nmem = 0;
curf->nblk = nblk;
curf->rpo = vnew(nblk, sizeof curf->rpo[0], PFn);
for (b=curf->start; b; b=b->link)
curf->rpo = 0;
for (b=0; b; b=b->link)
b->dlink = 0; /* was trashed by findblk() */
for (i=0; i<BMask+1; ++i)
blkh[i] = 0;
memset(tmph, 0, tmphcap * sizeof tmph[0]);
memset(tmph, 0, sizeof tmph);
typecheck(curf);
return curf;
}
@ -1115,9 +1091,6 @@ parsedat(void cb(Dat *), Lnk *lnk)
if (t == Talign) {
if (nextnl() != Tint)
err("alignment expected");
if (tokval.num <= 0 || tokval.num > CHAR_MAX
|| (tokval.num & (tokval.num-1)) != 0)
err("invalid alignment");
lnk->align = tokval.num;
t = nextnl();
}
@ -1183,9 +1156,6 @@ parselnk(Lnk *lnk)
case Tthread:
lnk->thread = 1;
break;
case Tcommon:
lnk->common = 1;
break;
case Tsection:
if (lnk->sec)
err("only one section allowed");
@ -1229,7 +1199,6 @@ parse(FILE *f, char *path, void dbgfile(char *), void data(Dat *), void func(Fn
dbgfile(tokval.str);
break;
case Tfunc:
lnk.align = 16;
func(parsefn(&lnk));
break;
case Tdata:
@ -1325,9 +1294,6 @@ printref(Ref r, Fn *fn, FILE *f)
case RInt:
fprintf(f, "%d", rsval(r));
break;
case -1:
fprintf(f, "R");
break;
}
}

8
rega.c
View File

@ -439,7 +439,8 @@ doblk(Blk *b, RMap *cur)
* the above loop must be changed */
}
}
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
/* qsort() comparison function to peel
@ -670,9 +671,10 @@ rega(Fn *fn)
blist = b1;
fn->nblk++;
strf(b1->name, "%s_%s", b->name, s->name);
stmov += &insb[NIns]-curi;
b1->nins = &insb[NIns] - curi;
stmov += b1->nins;
stblk += 1;
idup(b1, curi, &insb[NIns]-curi);
idup(&b1->ins, curi, b1->nins);
b1->jmp.type = Jjmp;
b1->s1 = s;
**ps = b1;

View File

@ -587,9 +587,9 @@ void
rv64_abi(Fn *fn)
{
Blk *b;
Ins *i, *i0;
Ins *i, *i0, *ip;
Insl *il;
int n0, n1, ioff;
int n;
Params p;
for (b=fn->start; b; b=b->link)
@ -600,13 +600,12 @@ rv64_abi(Fn *fn)
if (!ispar(i->op))
break;
p = selpar(fn, b->ins, i);
n0 = &insb[NIns] - curi;
ioff = i - b->ins;
n1 = b->nins - ioff;
vgrow(&b->ins, n0+n1);
icpy(b->ins+n0, b->ins+ioff, n1);
icpy(b->ins, curi, n0);
b->nins = n0+n1;
n = b->nins - (i - b->ins) + (&insb[NIns] - curi);
i0 = alloc(n * sizeof(Ins));
ip = icpy(ip = i0, curi, &insb[NIns] - curi);
ip = icpy(ip, i, &b->ins[b->nins] - i);
b->nins = n;
b->ins = i0;
/* lower calls, returns, and vararg instructions */
il = 0;
@ -643,7 +642,8 @@ rv64_abi(Fn *fn)
if (b == fn->start)
for (; il; il=il->link)
emiti(il->i);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
} while (b != fn->start);
if (debug['A']) {

View File

@ -8,7 +8,7 @@ enum {
static struct {
short op;
short cls;
char *fmt;
char *asm;
} omap[] = {
{ Oadd, Ki, "add%k %=, %0, %1" },
{ Oadd, Ka, "fadd.%k %=, %0, %1" },
@ -326,7 +326,7 @@ emitins(Ins *i, Fn *fn, FILE *f)
|| (omap[o].cls == Ki && KBASE(i->cls) == 0))
break;
}
emitf(omap[o].fmt, i, fn, f);
emitf(omap[o].asm, i, fn, f);
break;
case Ocopy:
if (req(i->to, i->arg[0]))
@ -444,7 +444,7 @@ rv64_emitfn(Fn *fn, FILE *f)
static int id0;
int lbl, neg, off, frame, *pr, r;
Blk *b, *s;
Ins *i, ii;
Ins *i;
emitfnlnk(fn->name, &fn->lnk, f);
@ -549,11 +549,6 @@ rv64_emitfn(Fn *fn, FILE *f)
b->s2 = s;
neg = 1;
}
if (rtype(b->jmp.arg) == RSlot) {
ii.arg[0] = b->jmp.arg;
emitf("lw t6, %M0", &ii, fn, f);
b->jmp.arg = TMP(T6);
}
assert(isreg(b->jmp.arg));
fprintf(f,
"\tb%sz %s, .L%d\n",

View File

@ -31,7 +31,6 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
c = &fn->con[r0.val];
if (c->type == CAddr && memarg(r, op, i))
break;
if (KBASE(k) == 0)
if (c->type == CBits && immarg(r, op, i))
if (-2048 <= c->bits.i && c->bits.i < 2048)
break;
@ -42,7 +41,7 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
* immediates
*/
assert(c->type == CBits);
n = stashbits(c->bits.i, KWIDE(k) ? 8 : 4);
n = stashbits(&c->bits, KWIDE(k) ? 8 : 4);
vgrow(&fn->con, ++fn->ncon);
c = &fn->con[fn->ncon-1];
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
@ -245,7 +244,8 @@ rv64_isel(Fn *fn)
seljmp(b, fn);
for (i=&b->ins[b->nins]; i!=b->ins;)
sel(*--i, fn);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
if (debug['I']) {

View File

@ -50,7 +50,6 @@ Target T_rv64 = {
.emitfn = rv64_emitfn,
.emitfin = elf_emitfin,
.asloc = ".L",
.cansel = 0,
};
MAKESURE(rsave_size_ok, sizeof rv64_rsave == (NGPS+NFPS+1) * sizeof(int));

60
simpl.c
View File

@ -30,43 +30,16 @@ blit(Ref sd[2], int sz, Fn *fn)
}
}
static int
ulog2_tab64[64] = {
63, 0, 1, 41, 37, 2, 16, 42,
38, 29, 32, 3, 12, 17, 43, 55,
39, 35, 30, 53, 33, 21, 4, 23,
13, 9, 18, 6, 25, 44, 48, 56,
62, 40, 36, 15, 28, 31, 11, 54,
34, 52, 20, 22, 8, 5, 24, 47,
61, 14, 27, 10, 51, 19, 7, 46,
60, 26, 50, 45, 59, 49, 58, 57,
};
static int
ulog2(uint64_t pow2)
{
return ulog2_tab64[(pow2 * 0x5b31ab928877a7e) >> 58];
}
static int
ispow2(uint64_t v)
{
return v && (v & (v - 1)) == 0;
}
static void
ins(Ins **pi, int *new, Blk *b, Fn *fn)
{
ulong ni;
Con *c;
Ins *i;
Ref r;
int n;
i = *pi;
/* simplify more instructions here;
* copy 0 into xor, bit rotations,
* etc. */
* copy 0 into xor, mul 2^n into shift,
* bit rotations, ... */
switch (i->op) {
case Oblit1:
assert(i > b->ins);
@ -80,29 +53,12 @@ ins(Ins **pi, int *new, Blk *b, Fn *fn)
}
blit((i-1)->arg, rsval(i->arg[0]), fn);
*pi = i-1;
return;
case Oudiv:
case Ourem:
r = i->arg[1];
if (KBASE(i->cls) == 0)
if (rtype(r) == RCon) {
c = &fn->con[r.val];
if (c->type == CBits)
if (ispow2(c->bits.i)) {
n = ulog2(c->bits.i);
if (i->op == Ourem) {
i->op = Oand;
i->arg[1] = getcon((1ull<<n) - 1, fn);
} else {
i->op = Oshr;
i->arg[1] = getcon(n, fn);
}
}
}
break;
}
default:
if (*new)
emiti(*i);
break;
}
}
void
@ -118,7 +74,9 @@ simpl(Fn *fn)
--i;
ins(&i, &new, b, fn);
}
if (new)
idup(b, curi, &insb[NIns]-curi);
if (new) {
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
}
}

25
spill.c
View File

@ -406,20 +406,26 @@ spill(Fn *fn)
if (rtype(b->jmp.arg) == RCall)
v->t[0] |= T.retregs(b->jmp.arg, 0);
}
if (rtype(b->jmp.arg) == RTmp) {
t = b->jmp.arg.val;
assert(KBASE(tmp[t].cls) == 0);
bsset(v, t);
limit2(v, 0, 0, NULL);
if (!bshas(v, t))
b->jmp.arg = slot(t);
}
for (t=Tmp0; bsiter(b->out, &t); t++)
if (!bshas(v, t))
slot(t);
bscopy(b->out, v);
/* 2. process the block instructions */
if (rtype(b->jmp.arg) == RTmp) {
t = b->jmp.arg.val;
assert(KBASE(tmp[t].cls) == 0);
lvarg[0] = bshas(v, t);
bsset(v, t);
bscopy(u, v);
limit2(v, 0, 0, NULL);
if (!bshas(v, t)) {
if (!lvarg[0])
bsclr(u, t);
b->jmp.arg = slot(t);
}
reloads(u, v);
}
curi = &insb[NIns];
for (i=&b->ins[b->nins]; i!=b->ins;) {
i--;
@ -511,7 +517,8 @@ spill(Fn *fn)
p->to = slot(p->to.val);
}
bscopy(b->in, v);
idup(b, curi, &insb[NIns]-curi);
b->nins = &insb[NIns] - curi;
idup(&b->ins, curi, b->nins);
}
/* align the locals to a 16 byte boundary */

9
ssa.c
View File

@ -1,7 +1,7 @@
#include "all.h"
#include <stdarg.h>
void
static void
adduse(Tmp *tmp, int ty, Blk *b, ...)
{
Use *u;
@ -40,10 +40,11 @@ filluse(Fn *fn)
Blk *b;
Phi *p;
Ins *i;
int m, t, tp, w, x;
int m, t, tp, w;
uint a;
Tmp *tmp;
/* todo, is this the correct file? */
tmp = fn->tmp;
for (t=Tmp0; t<fn->ntmp; t++) {
tmp[t].def = 0;
@ -83,8 +84,6 @@ filluse(Fn *fn)
w = Wsb + (i->op - Oloadsb);
if (isext(i->op))
w = Wsb + (i->op - Oextsb);
if (iscmp(i->op, &x, &x))
w = Wub;
if (w == Wsw || w == Wuw)
if (i->cls == Kw)
w = WFull;
@ -144,7 +143,7 @@ phiins(Fn *fn)
continue;
}
bszero(u);
k = Kx;
k = -1;
bp = be;
for (b=fn->start; b; b=b->link) {
b->visit = 0;

View File

@ -1,48 +0,0 @@
export
function w $ifmv(w %p1, w %p2, w %p3) {
@start
@entry
%rt =w add %p2, %p3 # gcm moves to @true
%rf =w sub %p2, %p3 # gcm moves to @false
jnz %p1, @true, @false
@true
%r =w copy %rt
jmp @exit
@false
%r =w copy %rf
jmp @exit
@exit
ret %r
}
export
function w $hoist1(w %p1, w %p2, w %p3) {
@start
@entry
%n =w copy 0
%i =w copy %p1
@loop
%base =w add %p2, %p3 # gcm moves to @exit
%i =w sub %i, 1
%n =w add %n, 1
jnz %i, @loop, @exit
@exit
%r =w add %base, %n
ret %r
}
export
function w $hoist2(w %p1, w %p2, w %p3) {
@start
@entry
%n =w copy 0
%i =w copy %p1
@loop
%base =w add %p2, %p3 # gcm moves to @entry
%i =w sub %i, 1
%n =w add %n, %base
jnz %i, @loop, @exit
@exit
%r =w add %base, %n
ret %r
}

View File

@ -1,43 +0,0 @@
# Programs from "Global Code Motion Global Value Numbering" by Cliff Click
# https://courses.cs.washington.edu/courses/cse501/06wi/reading/click-pldi95.pdf
# GCM program in Figure 1
function w $gcm_test(w %a){
@start
%i.0 =w copy 0
@loop
%i.1 =w phi @start %i.0, @loop %i.2
%b =w add %a, 1 # early schedule moves to @start
%i.2 =w add %i.1, %b
%c =w mul %i.2, 2 # late schedule moves to @end
%x =w csltw %i.2, 10
jnz %x, @loop, @end
@end
ret %c
}
# GCM program in "Figure 3 x's definition does not dominate it's use"
#
# SSA contruction will insert phi instruction for "x" in @if_false
# preventing the "add" in @if_false from being moved to @if_true
function $gcm_test2 (w %a){
@start
%f =w copy 1
%x =w copy 0
%s.0 =w copy 0
@loop
%s.1 = w phi @start %s.0, @if_false %s.2
jnz %a, @if, @end
@if
jnz %f, @if_true, @if_false
@if_true
%f =w copy 0
%x =w add %x, 1
@if_false
%s.2 =w add %s.1, %x
jmp @loop
@end
ret
}

View File

@ -1,17 +0,0 @@
# GCM can eliminate unused add/load instructions
export
function w $f(l %p, w %c) {
@start
jnz %c, @true, @false
@true
%p1 =l add %p, 4
%v1 =w loaduw %p1
jmp @end
@false
%p2 =l add %p, 4
%v2 =w loaduw %p2
jmp @end
@end
ret 0
}

View File

@ -28,7 +28,7 @@ function $test() {
# >>> driver
# #include <stdio.h>
# struct four { long long l; char c; int i; };
# struct four { long l; char c; int i; };
# extern void test(void);
# int F(int a0, int a1, int a2, int a3, struct four s, int a6) {
# printf("%d %d %d %d %d %d %d\n",

View File

@ -107,7 +107,7 @@ function $test() {
# typedef struct { int i; } st2;
# typedef struct { float f; int i; } st3;
# typedef struct { int i; double d; } st4;
# typedef struct { float f; long long l; } st5;
# typedef struct { float f; long l; } st5;
# typedef struct { char t[16]; } st6;
# typedef struct { float f; double d; } st7;
# typedef struct { int i[4]; } st8;

View File

@ -10,7 +10,6 @@ type :sd = { s, d } # ditto
type :ww = { w, w } # in a single gp reg
type :lb = { l, b } # in two gp regs
type :big = { b 17 } # by reference
type :ddd = { d, d, d} # big hfa on arm64
data $ctoqbestr = { b "c->qbe(%d)", b 0 }
data $emptystr = { b 0 }
@ -113,14 +112,6 @@ function $qfn10(w %p0, w %p1, w %p2, w %p3, w %p4, w %p5, w %p6, w %p7, :big %p8
%r1 =w call $puts(l $emptystr)
ret
}
export
function $qfn11(:ddd %p0) {
@start
%r0 =w call $printf(l $ctoqbestr, ..., w 11)
call $pddd(l %p0)
%r1 =w call $puts(l $emptystr)
ret
}
export
function w $main() {
@ -137,7 +128,6 @@ function w $main() {
call $cfn8(w 0, w 0, w 0, w 0, w 0, w 0, w 0, w 0, :lb $lb)
call $cfn9(:big $big)
call $cfn10(w 0, w 0, w 0, w 0, w 0, w 0, w 0, w 0, :big $big, s s_10.10, l 11)
call $cfn11(:ddd $ddd)
ret 0
}
@ -150,9 +140,8 @@ function w $main() {
# typedef struct { float s0, s1; } Sss;
# typedef struct { float s; double d; } Ssd;
# typedef struct { int w0, w1; } Sww;
# typedef struct { long long l; char b; } Slb;
# typedef struct { long l; char b; } Slb;
# typedef struct { char b[17]; } Sbig;
# typedef struct { double d0, d1, d2; } Sddd;
# Sfi1 zfi1, fi1 = { -123, 4.56 };
# Sfi2 zfi2, fi2 = { 1.23, 456 };
# Sfi3 zfi3, fi3 = { 3.45, 567 };
@ -161,18 +150,16 @@ function w $main() {
# Sww zww, ww = { -123, -456 };
# Slb zlb, lb = { 123, 'z' };
# Sbig zbig, big = { "abcdefhijklmnopqr" };
# Sddd zddd, ddd = { 1.23, 45.6, 7.89 };
# void pfi1(Sfi1 *s) { printf(" { %d, %g }", s->h, s->s); }
# void pfi2(Sfi2 *s) { printf(" { %g, %d }", s->s, s->w); }
# void pfi3(Sfi3 *s) { printf(" { %g, %d }", s->s, s->u.w); }
# void pss(Sss *s) { printf(" { %g, %g }", s->s0, s->s1); }
# void psd(Ssd *s) { printf(" { %g, %g }", s->s, s->d); }
# void pww(Sww *s) { printf(" { %d, %d }", s->w0, s->w1); }
# void plb(Slb *s) { printf(" { %lld, '%c' }", s->l, s->b); }
# void plb(Slb *s) { printf(" { %ld, '%c' }", s->l, s->b); }
# void pbig(Sbig *s) { printf(" \"%.17s\"", s->b); }
# void pddd(Sddd *s) { printf(" { %g, %g, %g }", s->d0, s->d1, s->d2); }
# void pw(int w) { printf(" %d", w); }
# void pl(long long l) { printf(" %lld", l); }
# void pl(long l) { printf(" %ld", l); }
# void ps(float s) { printf(" %g", s); }
# void pd(double d) { printf(" %g", d); }
# /* --------------------------- */
@ -206,8 +193,8 @@ function w $main() {
# pss(&p0); puts("");
# qfn4(p0);
# }
# extern void qfn5(double, double, double, double, double, double, double, Sss, float, long long);
# void cfn5(double p0, double p1, double p2, double p3, double p4, double p5, double p6, Sss p7, float p8, long long p9) {
# extern void qfn5(double, double, double, double, double, double, double, Sss, float, long);
# void cfn5(double p0, double p1, double p2, double p3, double p4, double p5, double p6, Sss p7, float p8, long p9) {
# printf("qbe->c(%d)", 5);
# pss(&p7); ps(p8); pl(p9); puts("");
# qfn5(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
@ -236,18 +223,12 @@ function w $main() {
# pbig(&p0); puts("");
# qfn9(p0);
# }
# extern void qfn10(int, int, int, int, int, int, int, int, Sbig, float, long long);
# void cfn10(int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, Sbig p8, float p9, long long p10) {
# extern void qfn10(int, int, int, int, int, int, int, int, Sbig, float, long);
# void cfn10(int p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, Sbig p8, float p9, long p10) {
# printf("qbe->c(%d)", 10);
# pbig(&p8); ps(p9); pl(p10); puts("");
# qfn10(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9, p10);
# }
# extern void qfn11(Sddd);
# void cfn11(Sddd p0) {
# printf("qbe->c(%d)", 11);
# pddd(&p0); puts("");
# qfn11(p0);
# }
# <<<
# >>> output
@ -273,6 +254,4 @@ function w $main() {
# c->qbe(9) "abcdefhijklmnopqr"
# qbe->c(10) "abcdefhijklmnopqr" 10.1 11
# c->qbe(10) "abcdefhijklmnopqr" 10.1 11
# qbe->c(11) { 1.23, 45.6, 7.89 }
# c->qbe(11) { 1.23, 45.6, 7.89 }
# <<<

View File

@ -1,20 +0,0 @@
type :obj = { l, l, l, l }
export
function :obj $f(l %self) {
@_0
%_1 =l alloc8 16
storel 77, %_1
ret %_1
}
# >>> driver
# #include <stdio.h>
# typedef struct { long long a, b, c, d; } obj;
# extern obj f();
# int main() { obj ret = f(); printf("%lld\n", ret.a); return 0; }
# <<<
# >>> output
# 77
# <<<

View File

@ -1,21 +0,0 @@
export function w $main() {
@start
%a =l alloc4 4
%b =l alloc4 4
storew 4, %a
storew 5, %b
@loop
# %mem will be %a and %b successively,
# but we do not know it when processing
# the phi because %b goes through a cpy
%mem =l phi @start %a, @loop %bcpy
%w =w load %mem
%eq5 =w ceqw %w, 5
%bcpy =l copy %b
jnz %eq5, @exit, @loop
@exit
ret 0
}

View File

@ -1,4 +1,3 @@
# skip amd64_win (no signals on win32)
# test amd64 addressing modes
export

View File

@ -1,15 +0,0 @@
export function w $f() {
@start
%x0 =w loadsb $a
# the extension must not be eliminated
# even though the load already extended
%x1 =l extsb %x0
%c =w ceql %x1, -1
ret %c
}
# >>> driver
# char a = -1;
# extern int f();
# int main() { return !(f() == 1); }
# <<<

View File

@ -1,4 +1,4 @@
# skip arm64 arm64_apple rv64 amd64_win
# skip arm64 arm64_apple rv64
# a hack example,
# we use a dark type to get
# a pointer to the stack.

View File

@ -72,7 +72,6 @@ function l $dtol(d %f) {
# >>> driver
# #include <float.h>
# #include <limits.h>
#
# extern float fneg(float);
@ -114,19 +113,15 @@ function l $dtol(d %f) {
# return 7;
# }
# for (i=0; i<sizeof(fin)/sizeof(fin[0]); i++) {
# if (fin[i] >= 1LL << DBL_MANT_DIG)
# break;
# if (dtol(fin[i]) != (unsigned long long)fin[i])
# if (stol((float)fin[i]) != (unsigned long long)(float)fin[i])
# return 8;
# if (dtol(fin[i]) != (unsigned long long)fin[i])
# return 9;
# if((unsigned long long)fin[i] > UINT_MAX)
# continue;
# if (dtow(fin[i]) != (unsigned int)fin[i])
# return 9;
# if (fin[i] >= 1LL << FLT_MANT_DIG)
# continue;
# if (stol((float)fin[i]) != (unsigned long long)(float)fin[i])
# return 10;
# if (stow((float)fin[i]) != (unsigned int)(float)fin[i])
# return 10;
# if (dtow(fin[i]) != (unsigned int)fin[i])
# return 11;
# }
# return 0;

View File

@ -1,19 +0,0 @@
export
function w $test(w %p1, w %p2) {
@start
@entry
%t1 =w copy 1
jnz %t1, @live, @dead1
@live
%t2 =w add %p1, %p2
ret %t2
@dead1
%t2 =w add %p1, %p2 # live ins in dead blk
@dead2
jnz %t1, @live, @dead1
}
# >>> driver
# extern int test(int p1, int p2);
# int main() { return test(1, 2) != 3; }
# <<<

View File

@ -1,31 +0,0 @@
# GVN 0/non-0 inference removes @yesyes, @yesno, @noyes, @nono
export
function w $test(w %c) {
@start
jnz %c, @yes, @no
@yes
%c0 =w cnew %c, 0
jnz %c0, @yesyes, @yesno
@yesyes
%rc =w copy 1
jmp @end
@yesno
%rc =w copy 111
jmp @end
@no
%c1 =w cnew %c, 0
jnz %c1, @noyes, @nono
@noyes
%rc =w copy 222
jmp @end
@nono
%rc =w copy 0
@end
ret %rc
}
# >>> driver
# int test(int);
# int main(void) { return test(0); }
# <<<

View File

@ -1,238 +0,0 @@
export
function l $ifc1(l %v0, l %v1, w %c) {
@start
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifc2(l %v0, l %v1, w %p) {
@start
%c =w cnew %p, 42
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifc3(l %v0, l %v1, w %p) {
@start
%c =w cugtw %p, 42
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifclts(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w clts %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcles(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cles %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcgts(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cgts %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcges(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cges %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifceqs(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w ceqs %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcnes(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cnes %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcos(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cos %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
export
function l $ifcuos(s %s0, s %s1, l %v0, l %v1) {
@start
%c =w cuos %s0, %s1
jnz %c, @true, @false
@true
%v =l copy %v1
jmp @end
@false
%v =l copy %v0
jmp @end
@end
ret %v
}
# >>> driver
# extern long ifc1(long, long, int);
# extern long ifc2(long, long, int);
# extern long ifc3(long, long, int);
# extern long ifclts(float, float, long, long);
# extern long ifcles(float, float, long, long);
# extern long ifcgts(float, float, long, long);
# extern long ifcges(float, float, long, long);
# extern long ifceqs(float, float, long, long);
# extern long ifcnes(float, float, long, long);
# extern long ifcos(float, float, long, long);
# extern long ifcuos(float, float, long, long);
# int main() {
# return
# ifc1(7, 5, 0) != 7
# || ifc1(7, 5, 1) != 5
# || ifc1(7, 5, 33) != 5
# || ifc2(7, 5, 42) != 7
# || ifc2(7, 5, 41) != 5
# || ifc2(7, 5, 43) != 5
# || ifc3(7, 5, 42) != 7
# || ifc3(7, 5, 41) != 7
# || ifc3(7, 5, 43) != 5
# || ifclts(5.0f, 6.0f, 7, 5) != 5
# || ifclts(5.0f, 5.0f, 7, 5) != 7
# || ifclts(5.0f, 4.0f, 7, 5) != 7
# || ifclts(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifclts(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifclts(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcles(5.0f, 6.0f, 7, 5) != 5
# || ifcles(5.0f, 5.0f, 7, 5) != 5
# || ifcles(5.0f, 4.0f, 7, 5) != 7
# || ifcles(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcles(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifcles(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcgts(5.0f, 6.0f, 7, 5) != 7
# || ifcgts(5.0f, 5.0f, 7, 5) != 7
# || ifcgts(5.0f, 4.0f, 7, 5) != 5
# || ifcgts(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcgts(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifcgts(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcges(5.0f, 6.0f, 7, 5) != 7
# || ifcges(5.0f, 5.0f, 7, 5) != 5
# || ifcges(5.0f, 4.0f, 7, 5) != 5
# || ifcges(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcges(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifcges(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifceqs(5.0f, 6.0f, 7, 5) != 7
# || ifceqs(5.0f, 5.0f, 7, 5) != 5
# || ifceqs(5.0f, 4.0f, 7, 5) != 7
# || ifceqs(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifceqs(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifceqs(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcnes(5.0f, 6.0f, 7, 5) != 5
# || ifcnes(5.0f, 5.0f, 7, 5) != 7
# || ifcnes(5.0f, 4.0f, 7, 5) != 5
# || ifcnes(5.0f, 0.0f/0.0f, 7, 5) != 5
# || ifcnes(0.0f/0.0f, 5.0f, 7, 5) != 5
# || ifcnes(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 5
# || ifcos(5.0f, 6.0f, 7, 5) != 5
# || ifcos(5.0f, 5.0f, 7, 5) != 5
# || ifcos(5.0f, 4.0f, 7, 5) != 5
# || ifcos(5.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcos(0.0f/0.0f, 5.0f, 7, 5) != 7
# || ifcos(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 7
# || ifcuos(5.0f, 6.0f, 7, 5) != 7
# || ifcuos(5.0f, 5.0f, 7, 5) != 7
# || ifcuos(5.0f, 4.0f, 7, 5) != 7
# || ifcuos(5.0f, 0.0f/0.0f, 7, 5) != 5
# || ifcuos(0.0f/0.0f, 5.0f, 7, 5) != 5
# || ifcuos(0.0f/0.0f, 0.0f/0.0f, 7, 5) != 5
# ;
# }
# <<<

View File

@ -1,64 +0,0 @@
# amd64 address-folding stress
export function w $f0(l %a, l %b) {
@start
%c =l add %b, 2
%d =l mul %c, 4
%e =l add %a, %d
%q =l loadw %e
ret %q
}
export function w $f1(l %a, l %b) {
@start
%c =l add 1, %b
%f =l add %c, 1
%d =l mul %f, 4
%e =l add %d, %a
%q =l loadw %e
ret %q
}
export function w $f2(l %a, l %b) {
@start
%l =l mul %b, 4
%d =l add 8, %l
%e =l add %a, %d
%q =l loadw %e
ret %q
}
# fixme: folding is not good here
export function w $f3(l %a, l %b) {
@start
%l =l mul %b, 4
%d =l add 4, %l
%f =l add 4, %d
%e =l add %a, %f
%q =l loadw %e
ret %q
}
export function w $f4(l %a, l %b) {
@start
%c =l add 1, %b
%d =l mul %c, 4
%e =l add 4, %d
%f =l add %e, %a
%q =l loadw %f
ret %q
}
# >>> driver
# int a[] = {1, 2, 3, 4};
# typedef int loadf(int *, long long);
# extern loadf f0, f1, f2, f3, f4;
# loadf *fns[] = {&f0, &f1, &f2, &f3, &f4, 0};
# int main() {
# loadf **f;
# int n;
# for (n=1,f=fns; *f; f++,n++)
# if ((*f)(a, 1) != 4) return n;
# return 0;
# }
# <<<

View File

@ -1,16 +0,0 @@
# make sure the local symbols used for
# fp constants do not get a _ prefix
# on apple arm hardware
export function w $main() {
@start
%r =d copy d_1.2
%x =w call $printf(l $fmt, ..., d %r)
ret 0
}
data $fmt = { b "%.06f\n", b 0 }
# >>> output
# 1.200000
# <<<

View File

@ -1,38 +0,0 @@
# make sure large consts are lowered
# without an offset
# i.e. not movq $9223372036854775807, 64(%rax)
export function w $main() {
@_0
%_1 =w call $myfunc(l 1, l 2, l 3, l 4, l 5, l 6, l 7, l 8, l 9223372036854775807)
ret 0
}
# >>> driver
# #include <stdio.h>
# #include <stdint.h>
# #include <inttypes.h>
# void myfunc(int64_t a, int64_t b, int64_t c, int64_t d, int64_t e, int64_t f, int64_t g, int64_t h, int64_t i) {
# printf("%" PRId64 "\n", a);
# printf("%" PRId64 "\n", b);
# printf("%" PRId64 "\n", c);
# printf("%" PRId64 "\n", d);
# printf("%" PRId64 "\n", e);
# printf("%" PRId64 "\n", f);
# printf("%" PRId64 "\n", g);
# printf("%" PRId64 "\n", h);
# printf("%" PRId64 "\n", i);
# }
# <<<
# >>> output
# 1
# 2
# 3
# 4
# 5
# 6
# 7
# 8
# 9223372036854775807
# <<<

View File

@ -1,4 +1,3 @@
# skip amd64_win (pthread and tls not implemented)
thread data $i = align 4 {w 42}
data $fmti = align 1 {b "i%d==%d\n", b 0}

View File

@ -29,7 +29,7 @@ char *tok[] = {
"function", "type", "data", "section", "align", "dbgfile",
"blit", "l", "w", "sh", "uh", "h", "sb", "ub", "b",
"d", "s", "z", "loadw", "loadl", "loads", "loadd",
"alloc1", "alloc2", "thread", "common",
"alloc1", "alloc2",
};
enum {

View File

@ -1,64 +0,0 @@
#include <assert.h>
#include <stdio.h>
typedef unsigned long long ullong;
char seen[64];
ullong rbg = 0x1e0298f7a7e;
int
bit()
{
int bit;
bit = rbg & 1;
rbg >>= 1;
return bit;
}
int
search(ullong n, int b, ullong *out)
{
int i, x;
ullong y, z;
if (b == 64) {
*out = n;
return 1;
}
x = 63 & ((n << (63 - b)) >> 58);
assert(!(x & 0) && x <= 62);
y = bit();
for (i=0; i<2; i++) {
z = x | (y << 5);
if (!seen[z]) {
seen[z] = (63-b)+1;
if (search(n | (y << b), b+1, out))
return 1;
seen[z] = 0;
}
y ^= 1;
}
return 0;
}
int
main()
{
ullong out;
int i;
if (search(0, 0, &out)) {
printf("0x%llx\n", out);
for (i=0; i<64; i++) {
printf((i&7) == 0 ? "\t" : " ");
printf("%2d,", seen[i]-1);
if ((i&7) == 7)
printf("\n");
}
} else
puts("not found");
}

View File

@ -1,3 +0,0 @@
*.cm[iox]
*.o
mgen

View File

@ -1 +0,0 @@
match_clause=4

View File

@ -1,16 +0,0 @@
BIN = mgen
SRC = \
match.ml \
fuzz.ml \
cgen.ml \
sexp.ml \
test.ml \
main.ml
$(BIN): $(SRC)
ocamlopt -o $(BIN) -g str.cmxa $(SRC)
clean:
rm -f *.cm? *.o $(BIN)
.PHONY: clean

View File

@ -1,420 +0,0 @@
open Match
type options =
{ pfx: string
; static: bool
; oc: out_channel }
type side = L | R
type id_pred =
| InBitSet of Int64.t
| Ge of int
| Eq of int
and id_test =
| Pred of (side * id_pred)
| And of id_test * id_test
type case_code =
| Table of ((int * int) * int) list
| IfThen of
{ test: id_test
; cif: case_code
; cthen: case_code option }
| Return of int
type case =
{ swap: bool
; code: case_code }
let cgen_case tmp nstates map =
let cgen_test ids =
match ids with
| [id] -> Eq id
| _ ->
let min_id =
List.fold_left min max_int ids in
if List.length ids = nstates - min_id
then Ge min_id
else begin
assert (nstates <= 64);
InBitSet
(List.fold_left (fun bs id ->
Int64.logor bs
(Int64.shift_left 1L id))
0L ids)
end
in
let symmetric =
let inverse ((l, r), x) = ((r, l), x) in
setify map = setify (List.map inverse map) in
let map =
let ordered ((l, r), _) = r <= l in
if symmetric then
List.filter ordered map
else map
in
let exception BailToTable in
try
let st =
match setify (List.map snd map) with
| [st] -> st
| _ -> raise BailToTable
in
(* the operation considered can only
* generate a single state *)
let pairs = List.map fst map in
let ls, rs = List.split pairs in
let ls = setify ls and rs = setify rs in
if List.length ls > 1 && List.length rs > 1 then
raise BailToTable;
{ swap = symmetric
; code =
let pl = Pred (L, cgen_test ls)
and pr = Pred (R, cgen_test rs) in
IfThen
{ test = And (pl, pr)
; cif = Return st
; cthen = Some (Return tmp) } }
with BailToTable ->
{ swap = symmetric
; code = Table map }
let show_op (_cls, op) =
"O" ^ show_op_base op
let indent oc i =
Printf.fprintf oc "%s" (String.sub "\t\t\t\t\t" 0 i)
let emit_swap oc i =
let pf m = Printf.fprintf oc m in
let pfi n m = indent oc n; pf m in
pfi i "if (l < r)\n";
pfi (i+1) "t = l, l = r, r = t;\n"
let gen_tables oc tmp pfx nstates (op, c) =
let i = 1 in
let pf m = Printf.fprintf oc m in
let pfi n m = indent oc n; pf m in
let ntables = ref 0 in
(* we must follow the order in which
* we visit code in emit_case, or
* else ntables goes out of sync *)
let base = pfx ^ show_op op in
let swap = c.swap in
let rec gen c =
match c with
| Table map ->
let name =
if !ntables = 0 then base else
base ^ string_of_int !ntables
in
assert (nstates <= 256);
if swap then
let n = nstates * (nstates + 1) / 2 in
pfi i "static uchar %stbl[%d] = {\n" name n
else
pfi i "static uchar %stbl[%d][%d] = {\n"
name nstates nstates;
for l = 0 to nstates - 1 do
pfi (i+1) "";
for r = 0 to nstates - 1 do
if not swap || r <= l then
begin
pf "%d"
(try List.assoc (l,r) map
with Not_found -> tmp);
pf ",";
end
done;
pf "\n";
done;
pfi i "};\n"
| IfThen {cif; cthen} ->
gen cif;
Option.iter gen cthen
| Return _ -> ()
in
gen c.code
let emit_case oc pfx no_swap (op, c) =
let fpf = Printf.fprintf in
let pf m = fpf oc m in
let pfi n m = indent oc n; pf m in
let rec side oc = function
| L -> fpf oc "l"
| R -> fpf oc "r"
in
let pred oc (s, pred) =
match pred with
| InBitSet bs -> fpf oc "BIT(%a) & %#Lx" side s bs
| Eq id -> fpf oc "%a == %d" side s id
| Ge id -> fpf oc "%d <= %a" id side s
in
let base = pfx ^ show_op op in
let swap = c.swap in
let ntables = ref 0 in
let rec code i c =
match c with
| Return id -> pfi i "return %d;\n" id
| Table map ->
let name =
if !ntables = 0 then base else
base ^ string_of_int !ntables
in
incr ntables;
if swap then
pfi i "return %stbl[(l + l*l)/2 + r];\n" name
else pfi i "return %stbl[l][r];\n" name
| IfThen ({test = And (And (t1, t2), t3)} as r) ->
code i @@ IfThen
{r with test = And (t1, And (t2, t3))}
| IfThen {test = And (Pred p, t); cif; cthen} ->
pfi i "if (%a)\n" pred p;
code i (IfThen {test = t; cif; cthen})
| IfThen {test = Pred p; cif; cthen} ->
pfi i "if (%a) {\n" pred p;
code (i+1) cif;
pfi i "}\n";
Option.iter (code i) cthen
in
pfi 1 "case %s:\n" (show_op op);
if not no_swap && c.swap then
emit_swap oc 2;
code 2 c.code
let emit_list
?(limit=60) ?(cut_before_sep=false)
~col ~indent:i ~sep ~f oc l =
let sl = String.length sep in
let rstripped_sep, rssl =
if sep.[sl - 1] = ' ' then
String.sub sep 0 (sl - 1), sl - 1
else sep, sl
in
let lstripped_sep, lssl =
if sep.[0] = ' ' then
String.sub sep 1 (sl - 1), sl - 1
else sep, sl
in
let rec line col acc = function
| [] -> (List.rev acc, [])
| s :: l ->
let col = col + sl + String.length s in
let no_space =
if cut_before_sep || l = [] then
col > limit
else
col + rssl > limit
in
if no_space then
(List.rev acc, s :: l)
else
line col (s :: acc) l
in
let rec go col l =
if l = [] then () else
let ll, l = line col [] l in
Printf.fprintf oc "%s" (String.concat sep ll);
if l <> [] && cut_before_sep then begin
Printf.fprintf oc "\n";
indent oc i;
Printf.fprintf oc "%s" lstripped_sep;
go (8*i + lssl) l
end else if l <> [] then begin
Printf.fprintf oc "%s\n" rstripped_sep;
indent oc i;
go (8*i) l
end else ()
in
go col (List.map f l)
let emit_numberer opts n =
let pf m = Printf.fprintf opts.oc m in
let tmp = (atom_state n Tmp).id in
let con = (atom_state n AnyCon).id in
let nst = Array.length n.states in
let cases =
StateMap.by_ops n.statemap |>
List.map (fun (op, map) ->
(op, cgen_case tmp nst map))
in
let all_swap =
List.for_all (fun (_, c) -> c.swap) cases in
(* opn() *)
if opts.static then pf "static ";
pf "int\n";
pf "%sopn(int op, int l, int r)\n" opts.pfx;
pf "{\n";
cases |> List.iter
(gen_tables opts.oc tmp opts.pfx nst);
if List.exists (fun (_, c) -> c.swap) cases then
pf "\tint t;\n\n";
if all_swap then emit_swap opts.oc 1;
pf "\tswitch (op) {\n";
cases |> List.iter
(emit_case opts.oc opts.pfx all_swap);
pf "\tdefault:\n";
pf "\t\treturn %d;\n" tmp;
pf "\t}\n";
pf "}\n\n";
(* refn() *)
if opts.static then pf "static ";
pf "int\n";
pf "%srefn(Ref r, Num *tn, Con *con)\n" opts.pfx;
pf "{\n";
let cons =
List.filter_map (function
| (Con c, s) -> Some (c, s.id)
| _ -> None)
n.atoms
in
if cons <> [] then
pf "\tint64_t n;\n\n";
pf "\tswitch (rtype(r)) {\n";
pf "\tcase RTmp:\n";
if tmp <> 0 then begin
assert
(List.exists (fun (_, s) ->
s.id = 0
) n.atoms &&
(* no temp should ever get state 0 *)
List.for_all (fun (a, s) ->
s.id <> 0 ||
match a with
| AnyCon | Con _ -> true
| _ -> false
) n.atoms);
pf "\t\tif (!tn[r.val].n)\n";
pf "\t\t\ttn[r.val].n = %d;\n" tmp;
end;
pf "\t\treturn tn[r.val].n;\n";
pf "\tcase RCon:\n";
if cons <> [] then begin
pf "\t\tif (con[r.val].type != CBits)\n";
pf "\t\t\treturn %d;\n" con;
pf "\t\tn = con[r.val].bits.i;\n";
cons |> inverse |> group_by_fst
|> List.iter (fun (id, cs) ->
pf "\t\tif (";
emit_list ~cut_before_sep:true
~col:20 ~indent:2 ~sep:" || "
~f:(fun c -> "n == " ^ Int64.to_string c)
opts.oc cs;
pf ")\n";
pf "\t\t\treturn %d;\n" id
);
end;
pf "\t\treturn %d;\n" con;
pf "\tdefault:\n";
pf "\t\treturn INT_MIN;\n";
pf "\t}\n";
pf "}\n\n";
(* match[]: patterns per state *)
if opts.static then pf "static ";
pf "bits %smatch[%d] = {\n" opts.pfx nst;
n.states |> Array.iteri (fun sn s ->
let tops =
List.filter_map (function
| Top ("$" | "%") -> None
| Top r -> Some ("BIT(P" ^ r ^ ")")
| _ -> None) s.point |> setify
in
if tops <> [] then
pf "\t[%d] = %s,\n"
sn (String.concat " | " tops);
);
pf "};\n\n"
let var_id vars f =
List.mapi (fun i x -> (x, i)) vars |>
List.assoc f
let compile_action vars act =
let pcs = Hashtbl.create 100 in
let rec gen pc (act: Action.t) =
try
[10 + Hashtbl.find pcs act.id]
with Not_found ->
let code =
match act.node with
| Action.Stop ->
[0]
| Action.Push (sym, k) ->
let c = if sym then 1 else 2 in
[c] @ gen (pc + 1) k
| Action.Set (v, {node = Action.Pop k; _})
| Action.Set (v, ({node = Action.Stop; _} as k)) ->
let v = var_id vars v in
[3; v] @ gen (pc + 2) k
| Action.Set _ ->
(* for now, only atomic patterns can be
* tied to a variable, so Set must be
* followed by either Pop or Stop *)
assert false
| Action.Pop k ->
[4] @ gen (pc + 1) k
| Action.Switch cases ->
let cases =
inverse cases |> group_by_fst |>
List.sort (fun (_, cs1) (_, cs2) ->
let n1 = List.length cs1
and n2 = List.length cs2 in
compare n2 n1)
in
(* the last case is the one with
* the max number of entries *)
let cases = List.rev (List.tl cases)
and last = fst (List.hd cases) in
let ncases =
List.fold_left (fun n (_, cs) ->
List.length cs + n)
0 cases
in
let body_off = 2 + 2 * ncases + 1 in
let pc, tbl, body =
List.fold_left
(fun (pc, tbl, body) (a, cs) ->
let ofs = body_off + List.length body in
let case = gen pc a in
let pc = pc + List.length case in
let body = body @ case in
let tbl =
List.fold_left (fun tbl c ->
tbl @ [c; ofs]
) tbl cs
in
(pc, tbl, body))
(pc + body_off, [], [])
cases
in
let ofs = body_off + List.length body in
let tbl = tbl @ [ofs] in
assert (2 + List.length tbl = body_off);
[5; ncases] @ tbl @ body @ gen pc last
in
if act.node <> Action.Stop then
Hashtbl.replace pcs act.id pc;
code
in
gen 0 act
let emit_matchers opts ms =
let pf m = Printf.fprintf opts.oc m in
if opts.static then pf "static ";
pf "uchar *%smatcher[] = {\n" opts.pfx;
List.iter (fun (vars, pname, m) ->
pf "\t[P%s] = (uchar[]){\n" pname;
pf "\t\t";
let bytes = compile_action vars m in
emit_list
~col:16 ~indent:2 ~sep:","
~f:string_of_int opts.oc bytes;
pf "\n";
pf "\t},\n")
ms;
pf "};\n\n"
let emit_c opts n =
emit_numberer opts n

View File

@ -1,413 +0,0 @@
(* fuzz the tables and matchers generated *)
open Match
module Buffer: sig
type 'a t
val create: ?capacity:int -> unit -> 'a t
val reset: 'a t -> unit
val size: 'a t -> int
val get: 'a t -> int -> 'a
val set: 'a t -> int -> 'a -> unit
val push: 'a t -> 'a -> unit
end = struct
type 'a t =
{ mutable size: int
; mutable data: 'a array }
let mk_array n = Array.make n (Obj.magic 0)
let create ?(capacity = 10) () =
if capacity < 0 then invalid_arg "Buffer.make";
{size = 0; data = mk_array capacity}
let reset b = b.size <- 0
let size b = b.size
let get b n =
if n >= size b then invalid_arg "Buffer.get";
b.data.(n)
let set b n x =
if n >= size b then invalid_arg "Buffer.set";
b.data.(n) <- x
let push b x =
let cap = Array.length b.data in
if size b = cap then begin
let data = mk_array (2 * cap + 1) in
Array.blit b.data 0 data 0 cap;
b.data <- data
end;
let sz = size b in
b.size <- sz + 1;
set b sz x
end
let binop_state n op s1 s2 =
let key = K (op, s1, s2) in
try StateMap.find key n.statemap
with Not_found -> atom_state n Tmp
type id = int
type term_data =
| Binop of op * id * id
| Leaf of atomic_pattern
type term =
{ id: id
; data: term_data
; state: p state }
let pp_term fmt (ta, id) =
let fpf x = Format.fprintf fmt x in
let rec pp _fmt id =
match ta.(id).data with
| Leaf (Con c) -> fpf "%Ld" c
| Leaf AnyCon -> fpf "$%d" id
| Leaf Tmp -> fpf "%%%d" id
| Binop (op, id1, id2) ->
fpf "@[(%s@%d:%d @[<hov>%a@ %a@])@]"
(show_op op) id ta.(id).state.id
pp id1 pp id2
in pp fmt id
(* A term pool is a deduplicated set of term
* that maintains nodes numbering using the
* statemap passed at creation time *)
module TermPool = struct
type t =
{ terms: term Buffer.t
; hcons: (term_data, id) Hashtbl.t
; numbr: numberer }
let create numbr =
{ terms = Buffer.create ()
; hcons = Hashtbl.create 100
; numbr }
let reset tp =
Buffer.reset tp.terms;
Hashtbl.clear tp.hcons
let size tp = Buffer.size tp.terms
let term tp id = Buffer.get tp.terms id
let mk_leaf tp atm =
let data = Leaf atm in
match Hashtbl.find tp.hcons data with
| id -> term tp id
| exception Not_found ->
let id = Buffer.size tp.terms in
let state = atom_state tp.numbr atm in
Buffer.push tp.terms {id; data; state};
Hashtbl.add tp.hcons data id;
term tp id
let mk_binop tp op t1 t2 =
let data = Binop (op, t1.id, t2.id) in
match Hashtbl.find tp.hcons data with
| id -> term tp id
| exception Not_found ->
let id = Buffer.size tp.terms in
let state =
binop_state tp.numbr op t1.state t2.state
in
Buffer.push tp.terms {id; data; state};
Hashtbl.add tp.hcons data id;
term tp id
let rec add_pattern tp = function
| Bnr (op, p1, p2) ->
let t1 = add_pattern tp p1 in
let t2 = add_pattern tp p2 in
mk_binop tp op t1 t2
| Atm atm -> mk_leaf tp atm
| Var (_, atm) -> add_pattern tp (Atm atm)
let explode_term tp id =
let rec aux tms n id =
let t = term tp id in
match t.data with
| Leaf _ -> (n, {t with id = n} :: tms)
| Binop (op, id1, id2) ->
let n1, tms = aux tms n id1 in
let n = n1 + 1 in
let n2, tms = aux tms n id2 in
let n = n2 + 1 in
(n, { t with data = Binop (op, n1, n2)
; id = n } :: tms)
in
let n, tms = aux [] 0 id in
Array.of_list (List.rev tms), n
end
module R = Random
(* uniform pick in a list *)
let list_pick l =
let rec aux n l x =
match l with
| [] -> x
| y :: l ->
if R.int (n + 1) = 0 then
aux (n + 1) l y
else
aux (n + 1) l x
in
match l with
| [] -> invalid_arg "list_pick"
| x :: l -> aux 1 l x
let term_pick ~numbr =
let ops =
if numbr.ops = [] then
numbr.ops <-
(StateMap.fold (fun k _ ops ->
match k with
| K (op, _, _) -> op :: ops)
numbr.statemap [] |> setify);
numbr.ops
in
let rec gen depth =
(* exponential probability for leaves to
* avoid skewing towards shallow terms *)
let atm_prob = 0.75 ** float_of_int depth in
if R.float 1.0 <= atm_prob || ops = [] then
let atom, st = list_pick numbr.atoms in
(st, Atm atom)
else
let op = list_pick ops in
let s1, t1 = gen (depth - 1) in
let s2, t2 = gen (depth - 1) in
( binop_state numbr op s1 s2
, Bnr (op, t1, t2) )
in fun ~depth -> gen depth
exception FuzzError
let rec pattern_depth = function
| Bnr (_, p1, p2) ->
1 + max (pattern_depth p1) (pattern_depth p2)
| Atm _ -> 0
| Var (_, atm) -> pattern_depth (Atm atm)
let ( %% ) a b =
1e2 *. float_of_int a /. float_of_int b
let progress ?(width = 50) msg pct =
Format.eprintf "\x1b[2K\r%!";
let progress_bar fmt =
let n =
let fwidth = float_of_int width in
1 + int_of_float (pct *. fwidth /. 1e2)
in
Format.fprintf fmt " %s%s %.0f%%@?"
(String.concat "" (List.init n (fun _ -> "")))
(String.make (max 0 (width - n)) '-')
pct
in
Format.kfprintf progress_bar
Format.err_formatter msg
let fuzz_numberer rules numbr =
(* pick twice the max pattern depth so we
* have a chance to find non-trivial numbers
* for the atomic patterns in the rules *)
let depth =
List.fold_left (fun depth r ->
max depth (pattern_depth r.pattern))
0 rules * 2
in
(* fuzz until the term pool we are constructing
* is no longer growing fast enough; or we just
* went through sufficiently many iterations *)
let max_iter = 1_000_000 in
let low_insert_rate = 1e-2 in
let tp = TermPool.create numbr in
let rec loop new_stats i =
let (_, _, insert_rate) = new_stats in
if insert_rate <= low_insert_rate then () else
if i >= max_iter then () else
(* periodically update stats *)
let new_stats =
let (num, cnt, rate) = new_stats in
if num land 1023 = 0 then
let rate =
0.5 *. (rate +. float_of_int cnt /. 1023.)
in
progress " insert_rate=%.1f%%"
(i %% max_iter) (rate *. 1e2);
(num + 1, 0, rate)
else new_stats
in
(* create a term and check that its number is
* accurate wrt the rules *)
let st, term = term_pick ~numbr ~depth in
let state_matched =
List.filter_map (fun cu ->
match cu with
| Top ("$" | "%") -> None
| Top name -> Some name
| _ -> None)
st.point |> setify
in
let rule_matched =
List.filter_map (fun r ->
if pattern_match r.pattern term then
Some r.name
else None)
rules |> setify
in
if state_matched <> rule_matched then begin
let open Format in
let pp_str_list =
let pp_sep fmt () = fprintf fmt ",@ " in
pp_print_list ~pp_sep pp_print_string
in
eprintf "@.@[<v2>fuzz error for %s"
(show_pattern term);
eprintf "@ @[state matched: %a@]"
pp_str_list state_matched;
eprintf "@ @[rule matched: %a@]"
pp_str_list rule_matched;
eprintf "@]@.";
raise FuzzError;
end;
if state_matched = [] then
loop new_stats (i + 1)
else
(* add to the term pool *)
let old_size = TermPool.size tp in
let _ = TermPool.add_pattern tp term in
let new_stats =
let (num, cnt, rate) = new_stats in
if TermPool.size tp <> old_size then
(num + 1, cnt + 1, rate)
else
(num + 1, cnt, rate)
in
loop new_stats (i + 1)
in
loop (1, 0, 1.0) 0;
Format.eprintf
"@.@[ generated %.3fMiB of test terms@]@."
(float_of_int (Obj.reachable_words (Obj.repr tp))
/. 128. /. 1024.);
tp
let rec run_matcher stk m (ta, id as t) =
let state id = ta.(id).state.id in
match m.Action.node with
| Action.Switch cases ->
let m =
try List.assoc (state id) cases
with Not_found -> failwith "no switch case"
in
run_matcher stk m t
| Action.Push (sym, m) ->
let l, r =
match ta.(id).data with
| Leaf _ -> failwith "push on leaf"
| Binop (_, l, r) -> (l, r)
in
if sym && state l > state r
then run_matcher (l :: stk) m (ta, r)
else run_matcher (r :: stk) m (ta, l)
| Action.Pop m -> begin
match stk with
| id :: stk -> run_matcher stk m (ta, id)
| [] -> failwith "pop on empty stack"
end
| Action.Set (v, m) ->
(v, id) :: run_matcher stk m t
| Action.Stop -> []
let rec term_match p (ta, id) =
let (|>>) x f =
match x with None -> None | Some x -> f x
in
let atom_match a =
match ta.(id).data with
| Leaf a' -> pattern_match (Atm a) (Atm a')
| Binop _ -> pattern_match (Atm a) (Atm Tmp)
in
match p with
| Var (v, a) when atom_match a ->
Some [(v, id)]
| Atm a when atom_match a -> Some []
| (Atm _ | Var _) -> None
| Bnr (op, pl, pr) -> begin
match ta.(id).data with
| Binop (op', idl, idr) when op' = op ->
term_match pl (ta, idl) |>> fun l1 ->
term_match pr (ta, idr) |>> fun l2 ->
Some (l1 @ l2)
| _ -> None
end
let test_matchers tp numbr rules =
let {statemap = sm; states = sa; _} = numbr in
let total = ref 0 in
let matchers =
let htbl = Hashtbl.create (Array.length sa) in
List.map (fun r -> (r.name, r.pattern)) rules |>
group_by_fst |>
List.iter (fun (r, ps) ->
total := !total + List.length ps;
let pm = (ps, lr_matcher sm sa rules r) in
sa |> Array.iter (fun s ->
if List.mem (Top r) s.point then
Hashtbl.add htbl s.id pm));
htbl
in
let seen = Hashtbl.create !total in
for id = 0 to TermPool.size tp - 1 do
if id land 1023 = 0 ||
id = TermPool.size tp - 1 then begin
progress
" coverage=%.1f%%"
(id %% TermPool.size tp)
(Hashtbl.length seen %% !total)
end;
let t = TermPool.explode_term tp id in
Hashtbl.find_all matchers
(TermPool.term tp id).state.id |>
List.iter (fun (ps, m) ->
let norm = List.fast_sort compare in
let ok =
match norm (run_matcher [] m t) with
| asn -> `Match (List.exists (fun p ->
match term_match p t with
| None -> false
| Some asn' ->
if asn = norm asn' then begin
Hashtbl.replace seen p ();
true
end else false) ps)
| exception e -> `RunFailure e
in
if ok <> `Match true then begin
let open Format in
let pp_asn fmt asn =
fprintf fmt "@[<h>";
pp_print_list
~pp_sep:(fun fmt () -> fprintf fmt ";@ ")
(fun fmt (v, d) ->
fprintf fmt "@[%s←%d@]" v d)
fmt asn;
fprintf fmt "@]"
in
eprintf "@.@[<v2>matcher error for";
eprintf "@ @[%a@]" pp_term t;
begin match ok with
| `RunFailure e ->
eprintf "@ @[exception: %s@]"
(Printexc.to_string e)
| `Match (* false *) _ ->
let asn = run_matcher [] m t in
eprintf "@ @[assignment: %a@]"
pp_asn asn;
eprintf "@ @[<v2>could not match";
List.iter (fun p ->
eprintf "@ + @[%s@]"
(show_pattern p)) ps;
eprintf "@]"
end;
eprintf "@]@.";
raise FuzzError
end)
done;
Format.eprintf "@."

View File

@ -1,214 +0,0 @@
open Cgen
open Match
let mgen ~verbose ~fuzz path lofs input oc =
let info ?(level = 1) fmt =
if level <= verbose then
Printf.eprintf fmt
else
Printf.ifprintf stdout fmt
in
let rules =
match Sexp.(run_parser ppats) input with
| `Error (ps, err, loc) ->
Printf.eprintf "%s:%d:%d %s\n"
path (lofs + ps.Sexp.line) ps.Sexp.coln err;
Printf.eprintf "%s" loc;
exit 1
| `Ok rules -> rules
in
info "adding ac variants...%!";
let nparsed =
List.fold_left
(fun npats (_, _, ps) ->
npats + List.length ps)
0 rules
in
let varsmap = Hashtbl.create 10 in
let rules =
List.concat_map (fun (name, vars, patterns) ->
(try assert (Hashtbl.find varsmap name = vars)
with Not_found -> ());
Hashtbl.replace varsmap name vars;
List.map
(fun pattern -> {name; vars; pattern})
(List.concat_map ac_equiv patterns)
) rules
in
info " %d -> %d patterns\n"
nparsed (List.length rules);
let rnames =
setify (List.map (fun r -> r.name) rules) in
info "generating match tables...%!";
let sa, am, sm = generate_table rules in
let numbr = make_numberer sa am sm in
info " %d states, %d rules\n"
(Array.length sa) (StateMap.cardinal sm);
if verbose >= 2 then begin
info "-------------\nstates:\n";
Array.iteri (fun i s ->
info " state %d: %s\n"
i (show_pattern s.seen)) sa;
info "-------------\nstatemap:\n";
Test.print_sm stderr sm;
info "-------------\n";
end;
info "generating matchers...\n";
let matchers =
List.map (fun rname ->
info "+ %s...%!" rname;
let m = lr_matcher sm sa rules rname in
let vars = Hashtbl.find varsmap rname in
info " %d nodes\n" (Action.size m);
info ~level:2 " -------------\n";
info ~level:2 " automaton:\n";
info ~level:2 "%s\n"
(Format.asprintf " @[%a@]" Action.pp m);
info ~level:2 " ----------\n";
(vars, rname, m)
) rnames
in
if fuzz then begin
info ~level:0 "fuzzing statemap...\n";
let tp = Fuzz.fuzz_numberer rules numbr in
info ~level:0 "testing %d patterns...\n"
(List.length rules);
Fuzz.test_matchers tp numbr rules
end;
info "emitting C...\n";
flush stderr;
let cgopts =
{ pfx = ""; static = true; oc = oc } in
emit_c cgopts numbr;
emit_matchers cgopts matchers;
()
let read_all ic =
let bufsz = 4096 in
let buf = Bytes.create bufsz in
let data = Buffer.create bufsz in
let read = ref 0 in
while
read := input ic buf 0 bufsz;
!read <> 0
do
Buffer.add_subbytes data buf 0 !read
done;
Buffer.contents data
let split_c src =
let begin_re, eoc_re, end_re =
let re = Str.regexp in
( re "mgen generated code"
, re "\\*/"
, re "end of generated code" )
in
let str_match regexp str =
try
let _: int =
Str.search_forward regexp str 0
in true
with Not_found -> false
in
let rec go st lofs pfx rules lines =
let line, lines =
match lines with
| [] ->
failwith (
match st with
| `Prefix -> "could not find mgen section"
| `Rules -> "mgen rules not terminated"
| `Skip -> "mgen section not terminated"
)
| l :: ls -> (l, ls)
in
match st with
| `Prefix ->
let pfx = line :: pfx in
if str_match begin_re line
then
let lofs = List.length pfx in
go `Rules lofs pfx rules lines
else go `Prefix 0 pfx rules lines
| `Rules ->
let pfx = line :: pfx in
if str_match eoc_re line
then go `Skip lofs pfx rules lines
else go `Rules lofs pfx (line :: rules) lines
| `Skip ->
if str_match end_re line then
let join = String.concat "\n" in
let pfx = join (List.rev pfx) ^ "\n\n"
and rules = join (List.rev rules)
and sfx = join (line :: lines)
in (lofs, pfx, rules, sfx)
else go `Skip lofs pfx rules lines
in
let lines = String.split_on_char '\n' src in
go `Prefix 0 [] [] lines
let () =
let usage_msg =
"mgen [--fuzz] [--verbose <N>] <file>" in
let fuzz_arg = ref false in
let verbose_arg = ref 0 in
let input_paths = ref [] in
let anon_fun filename =
input_paths := filename :: !input_paths in
let speclist =
[ ( "--fuzz", Arg.Set fuzz_arg
, " Fuzz tables and matchers" )
; ( "--verbose", Arg.Set_int verbose_arg
, "<N> Set verbosity level" )
; ( "--", Arg.Rest_all (List.iter anon_fun)
, " Stop argument parsing" ) ]
in
Arg.parse speclist anon_fun usage_msg;
let input_paths = !input_paths in
let verbose = !verbose_arg in
let fuzz = !fuzz_arg in
let input_path, input =
match input_paths with
| ["-"] -> ("-", read_all stdin)
| [path] -> (path, read_all (open_in path))
| _ ->
Printf.eprintf
"%s: single input file expected\n"
Sys.argv.(0);
Arg.usage speclist usage_msg; exit 1
in
let mgen = mgen ~verbose ~fuzz in
if Str.last_chars input_path 2 <> ".c"
then mgen input_path 0 input stdout
else
let tmp_path = input_path ^ ".tmp" in
Fun.protect
~finally:(fun () ->
try Sys.remove tmp_path with _ -> ())
(fun () ->
let lofs, pfx, rules, sfx = split_c input in
let oc = open_out tmp_path in
output_string oc pfx;
mgen input_path lofs rules oc;
output_string oc sfx;
close_out oc;
Sys.rename tmp_path input_path;
());
()

View File

@ -1,651 +0,0 @@
type cls = Kw | Kl | Ks | Kd
type op_base =
| Oadd
| Osub
| Omul
| Oor
| Oshl
| Oshr
type op = cls * op_base
let op_bases =
[Oadd; Osub; Omul; Oor; Oshl; Oshr]
let commutative = function
| (_, (Oadd | Omul | Oor)) -> true
| (_, _) -> false
let associative = function
| (_, (Oadd | Omul | Oor)) -> true
| (_, _) -> false
type atomic_pattern =
| Tmp
| AnyCon
| Con of int64
(* Tmp < AnyCon < Con k *)
type pattern =
| Bnr of op * pattern * pattern
| Atm of atomic_pattern
| Var of string * atomic_pattern
let is_atomic = function
| (Atm _ | Var _) -> true
| _ -> false
let show_op_base o =
match o with
| Oadd -> "add"
| Osub -> "sub"
| Omul -> "mul"
| Oor -> "or"
| Oshl -> "shl"
| Oshr -> "shr"
let show_op (k, o) =
show_op_base o ^
(match k with
| Kw -> "w"
| Kl -> "l"
| Ks -> "s"
| Kd -> "d")
let rec show_pattern p =
match p with
| Atm Tmp -> "%"
| Atm AnyCon -> "$"
| Atm (Con n) -> Int64.to_string n
| Var (v, p) ->
show_pattern (Atm p) ^ "'" ^ v
| Bnr (o, pl, pr) ->
"(" ^ show_op o ^
" " ^ show_pattern pl ^
" " ^ show_pattern pr ^ ")"
let get_atomic p =
match p with
| (Atm a | Var (_, a)) -> Some a
| _ -> None
let rec pattern_match p w =
match p with
| Var (_, p) ->
pattern_match (Atm p) w
| Atm Tmp ->
begin match get_atomic w with
| Some (Con _ | AnyCon) -> false
| _ -> true
end
| Atm (Con _) -> w = p
| Atm (AnyCon) ->
not (pattern_match (Atm Tmp) w)
| Bnr (o, pl, pr) ->
begin match w with
| Bnr (o', wl, wr) ->
o' = o &&
pattern_match pl wl &&
pattern_match pr wr
| _ -> false
end
type +'a cursor = (* a position inside a pattern *)
| Bnrl of op * 'a cursor * pattern
| Bnrr of op * pattern * 'a cursor
| Top of 'a
let rec fold_cursor c p =
match c with
| Bnrl (o, c', p') -> fold_cursor c' (Bnr (o, p, p'))
| Bnrr (o, p', c') -> fold_cursor c' (Bnr (o, p', p))
| Top _ -> p
let peel p x =
let once out (p, c) =
match p with
| Var (_, p) -> (Atm p, c) :: out
| Atm _ -> (p, c) :: out
| Bnr (o, pl, pr) ->
(pl, Bnrl (o, c, pr)) ::
(pr, Bnrr (o, pl, c)) :: out
in
let rec go l =
let l' = List.fold_left once [] l in
if List.length l' = List.length l
then l'
else go l'
in go [(p, Top x)]
let fold_pairs l1 l2 ini f =
let rec go acc = function
| [] -> acc
| a :: l1' ->
go (List.fold_left
(fun acc b -> f (a, b) acc)
acc l2) l1'
in go ini l1
let iter_pairs l f =
fold_pairs l l () (fun x () -> f x)
let inverse l =
List.map (fun (a, b) -> (b, a)) l
type 'a state =
{ id: int
; seen: pattern
; point: ('a cursor) list }
let rec binops side {point; _} =
List.filter_map (fun c ->
match c, side with
| Bnrl (o, c, r), `L -> Some ((o, c), r)
| Bnrr (o, l, c), `R -> Some ((o, c), l)
| _ -> None)
point
let group_by_fst l =
List.fast_sort (fun (a, _) (b, _) ->
compare a b) l |>
List.fold_left (fun (oo, l, res) (o', c) ->
match oo with
| None -> (Some o', [c], [])
| Some o when o = o' -> (oo, c :: l, res)
| Some o -> (Some o', [c], (o, l) :: res))
(None, [], []) |>
(function
| (None, _, _) -> []
| (Some o, l, res) -> (o, l) :: res)
let sort_uniq cmp l =
List.fast_sort cmp l |>
List.fold_left (fun (eo, l) e' ->
match eo with
| None -> (Some e', l)
| Some e when cmp e e' = 0 -> (eo, l)
| Some e -> (Some e', e :: l))
(None, []) |>
(function
| (None, _) -> []
| (Some e, l) -> List.rev (e :: l))
let setify l =
sort_uniq compare l
let normalize (point: ('a cursor) list) =
setify point
let next_binary tmp s1 s2 =
let pm w (_, p) = pattern_match p w in
let o1 = binops `L s1 |>
List.filter (pm s2.seen) |>
List.map fst in
let o2 = binops `R s2 |>
List.filter (pm s1.seen) |>
List.map fst in
List.map (fun (o, l) ->
o,
{ id = -1
; seen = Bnr (o, s1.seen, s2.seen)
; point = normalize (l @ tmp) })
(group_by_fst (o1 @ o2))
type p = string
module StateSet : sig
type t
val create: unit -> t
val add: t -> p state ->
[> `Added | `Found ] * p state
val iter: t -> (p state -> unit) -> unit
val elems: t -> (p state) list
end = struct
open Hashtbl.Make(struct
type t = p state
let equal s1 s2 = s1.point = s2.point
let hash s = Hashtbl.hash s.point
end)
type nonrec t =
{ h: int t
; mutable next_id: int }
let create () =
{ h = create 500; next_id = 0 }
let add set s =
assert (s.point = normalize s.point);
try
let id = find set.h s in
`Found, {s with id}
with Not_found -> begin
let id = set.next_id in
set.next_id <- id + 1;
add set.h s id;
`Added, {s with id}
end
let iter set f =
let f s id = f {s with id} in
iter f set.h
let elems set =
let res = ref [] in
iter set (fun s -> res := s :: !res);
!res
end
type table_key =
| K of op * p state * p state
module StateMap = struct
include Map.Make(struct
type t = table_key
let compare ka kb =
match ka, kb with
| K (o, sl, sr), K (o', sl', sr') ->
compare (o, sl.id, sr.id)
(o', sl'.id, sr'.id)
end)
let invert n sm =
let rmap = Array.make n [] in
iter (fun k {id; _} ->
match k with
| K (o, sl, sr) ->
rmap.(id) <-
(o, (sl.id, sr.id)) :: rmap.(id)
) sm;
Array.map group_by_fst rmap
let by_ops sm =
fold (fun tk s ops ->
match tk with
| K (op, l, r) ->
(op, ((l.id, r.id), s.id)) :: ops)
sm [] |> group_by_fst
end
type rule =
{ name: string
; vars: string list
; pattern: pattern }
let generate_table rl =
let states = StateSet.create () in
let rl =
(* these atomic patterns must occur in
* rules so that we are able to number
* all possible refs *)
[ { name = "$"; vars = []
; pattern = Atm AnyCon }
; { name = "%"; vars = []
; pattern = Atm Tmp } ] @ rl
in
(* initialize states *)
let ground =
List.concat_map
(fun r -> peel r.pattern r.name) rl |>
group_by_fst
in
let tmp = List.assoc (Atm Tmp) ground in
let con = List.assoc (Atm AnyCon) ground in
let atoms = ref [] in
let () =
List.iter (fun (seen, l) ->
let point =
if pattern_match (Atm Tmp) seen
then normalize (tmp @ l)
else normalize (con @ l)
in
let s = {id = -1; seen; point} in
let _, s = StateSet.add states s in
match get_atomic seen with
| Some atm -> atoms := (atm, s) :: !atoms
| None -> ()
) ground
in
(* setup loop state *)
let map = ref StateMap.empty in
let map_add k s' =
map := StateMap.add k s' !map
in
let flag = ref `Added in
let flagmerge = function
| `Added -> flag := `Added
| _ -> ()
in
(* iterate until fixpoint *)
while !flag = `Added do
flag := `Stop;
let statel = StateSet.elems states in
iter_pairs statel (fun (sl, sr) ->
next_binary tmp sl sr |>
List.iter (fun (o, s') ->
let flag', s' =
StateSet.add states s' in
flagmerge flag';
map_add (K (o, sl, sr)) s';
));
done;
let states =
StateSet.elems states |>
List.sort (fun s s' -> compare s.id s'.id) |>
Array.of_list
in
(states, !atoms, !map)
let intersperse x l =
let rec go left right out =
let out =
(List.rev left @ [x] @ right) ::
out in
match right with
| x :: right' ->
go (x :: left) right' out
| [] -> out
in go [] l []
let rec permute = function
| [] -> [[]]
| x :: l ->
List.concat (List.map
(intersperse x) (permute l))
(* build all binary trees with ordered
* leaves l *)
let rec bins build l =
let rec go l r out =
match r with
| [] -> out
| x :: r' ->
go (l @ [x]) r'
(fold_pairs
(bins build l)
(bins build r)
out (fun (l, r) out ->
build l r :: out))
in
match l with
| [] -> []
| [x] -> [x]
| x :: l -> go [x] l []
let products l ini f =
let rec go acc la = function
| [] -> f (List.rev la) acc
| xs :: l ->
List.fold_left (fun acc x ->
go acc (x :: la) l)
acc xs
in go ini [] l
(* combinatorial nuke... *)
let rec ac_equiv =
let rec alevel o = function
| Bnr (o', l, r) when o' = o ->
alevel o l @ alevel o r
| x -> [x]
in function
| Bnr (o, _, _) as p
when associative o ->
products
(List.map ac_equiv (alevel o p)) []
(fun choice out ->
List.concat_map
(bins (fun l r -> Bnr (o, l, r)))
(if commutative o
then permute choice
else [choice]) @ out)
| Bnr (o, l, r)
when commutative o ->
fold_pairs
(ac_equiv l) (ac_equiv r) []
(fun (l, r) out ->
Bnr (o, l, r) ::
Bnr (o, r, l) :: out)
| Bnr (o, l, r) ->
fold_pairs
(ac_equiv l) (ac_equiv r) []
(fun (l, r) out ->
Bnr (o, l, r) :: out)
| x -> [x]
module Action: sig
type node =
| Switch of (int * t) list
| Push of bool * t
| Pop of t
| Set of string * t
| Stop
and t = private
{ id: int; node: node }
val equal: t -> t -> bool
val size: t -> int
val stop: t
val mk_push: sym:bool -> t -> t
val mk_pop: t -> t
val mk_set: string -> t -> t
val mk_switch: int list -> (int -> t) -> t
val pp: Format.formatter -> t -> unit
end = struct
type node =
| Switch of (int * t) list
| Push of bool * t
| Pop of t
| Set of string * t
| Stop
and t =
{ id: int; node: node }
let equal a a' = a.id = a'.id
let size a =
let seen = Hashtbl.create 10 in
let rec node_size = function
| Switch l ->
List.fold_left
(fun n (_, a) -> n + size a) 0 l
| (Push (_, a) | Pop a | Set (_, a)) ->
size a
| Stop -> 0
and size {id; node} =
if Hashtbl.mem seen id
then 0
else begin
Hashtbl.add seen id ();
1 + node_size node
end
in
size a
let mk =
let hcons = Hashtbl.create 100 in
let fresh = ref 0 in
fun node ->
let id =
try Hashtbl.find hcons node
with Not_found ->
let id = !fresh in
Hashtbl.add hcons node id;
fresh := id + 1;
id
in
{id; node}
let stop = mk Stop
let mk_push ~sym a = mk (Push (sym, a))
let mk_pop a =
match a.node with
| Stop -> a
| _ -> mk (Pop a)
let mk_set v a = mk (Set (v, a))
let mk_switch ids f =
match List.map f ids with
| [] -> failwith "empty switch";
| c :: cs as cases ->
if List.for_all (equal c) cs then c
else
let cases = List.combine ids cases in
mk (Switch cases)
open Format
let rec pp_node fmt = function
| Switch l ->
fprintf fmt "@[<v>@[<v2>switch{";
let pp_case (c, a) =
let pp_sep fmt () = fprintf fmt "," in
fprintf fmt "@,@[<2>→%a:@ @[%a@]@]"
(pp_print_list ~pp_sep pp_print_int)
c pp a
in
inverse l |> group_by_fst |> inverse |>
List.iter pp_case;
fprintf fmt "@]@,}@]"
| Push (true, a) -> fprintf fmt "pushsym@ %a" pp a
| Push (false, a) -> fprintf fmt "push@ %a" pp a
| Pop a -> fprintf fmt "pop@ %a" pp a
| Set (v, a) -> fprintf fmt "set(%s)@ %a" v pp a
| Stop -> fprintf fmt ""
and pp fmt a = pp_node fmt a.node
end
(* a state is commutative if (a op b) enters
* it iff (b op a) enters it as well *)
let symmetric rmap id =
List.for_all (fun (_, l) ->
let l1, l2 =
List.filter (fun (a, b) -> a <> b) l |>
List.partition (fun (a, b) -> a < b)
in
setify l1 = setify (inverse l2))
rmap.(id)
(* left-to-right matching of a set of patterns;
* may raise if there is no lr matcher for the
* input rule *)
let lr_matcher statemap states rules name =
let rmap =
let nstates = Array.length states in
StateMap.invert nstates statemap
in
let exception Stuck in
(* the list of ids represents a class of terms
* whose root ends up being labelled with one
* such id; the gen function generates a matcher
* that will, given any such term, assign values
* for the Var nodes of one pattern in pats *)
let rec gen
: 'a. int list -> (pattern * 'a) list
-> (int -> (pattern * 'a) list -> Action.t)
-> Action.t
= fun ids pats k ->
Action.mk_switch (setify ids) @@ fun id_top ->
let sym = symmetric rmap id_top in
let id_ops =
if sym then
let ordered (a, b) = a <= b in
List.map (fun (o, l) ->
(o, List.filter ordered l))
rmap.(id_top)
else rmap.(id_top)
in
(* consider only the patterns that are
* compatible with the current id *)
let atm_pats, bin_pats =
List.filter (function
| Bnr (o, _, _), _ ->
List.exists
(fun (o', _) -> o' = o)
id_ops
| _ -> true) pats |>
List.partition
(fun (pat, _) -> is_atomic pat)
in
try
if bin_pats = [] then raise Stuck;
let pats_l =
List.map (function
| (Bnr (o, l, r), x) ->
(l, (o, x, r))
| _ -> assert false)
bin_pats
and pats_r =
List.map (fun (l, (o, x, r)) ->
(r, (o, l, x)))
and patstop =
List.map (fun (r, (o, l, x)) ->
(Bnr (o, l, r), x))
in
let id_pairs = List.concat_map snd id_ops in
let ids_l = List.map fst id_pairs
and ids_r id_left =
List.filter_map (fun (l, r) ->
if l = id_left then Some r else None)
id_pairs
in
(* match the left arm *)
Action.mk_push ~sym
(gen ids_l pats_l
@@ fun lid pats ->
(* then the right arm, considering
* only the remaining possible
* patterns and knowing that the
* left arm was numbered 'lid' *)
Action.mk_pop
(gen (ids_r lid) (pats_r pats)
@@ fun _rid pats ->
(* continue with the parent *)
k id_top (patstop pats)))
with Stuck ->
let atm_pats =
let seen = states.(id_top).seen in
List.filter (fun (pat, _) ->
pattern_match pat seen) atm_pats
in
if atm_pats = [] then raise Stuck else
let vars =
List.filter_map (function
| (Var (v, _), _) -> Some v
| _ -> None) atm_pats |> setify
in
match vars with
| [] -> k id_top atm_pats
| [v] -> Action.mk_set v (k id_top atm_pats)
| _ -> failwith "ambiguous var match"
in
(* generate a matcher for the rule *)
let ids_top =
Array.to_list states |>
List.filter_map (fun {id; point = p; _} ->
if List.exists ((=) (Top name)) p then
Some id
else None)
in
let rec filter_dups pats =
match pats with
| p :: pats ->
if List.exists (pattern_match p) pats
then filter_dups pats
else p :: filter_dups pats
| [] -> []
in
let pats_top =
List.filter_map (fun r ->
if r.name = name then
Some r.pattern
else None) rules |>
filter_dups |>
List.map (fun p -> (p, ()))
in
gen ids_top pats_top (fun _ pats ->
assert (pats <> []);
Action.stop)
type numberer =
{ atoms: (atomic_pattern * p state) list
; statemap: p state StateMap.t
; states: p state array
; mutable ops: op list
(* memoizes the list of possible operations
* according to the statemap *) }
let make_numberer sa am sm =
{ atoms = am
; states = sa
; statemap = sm
; ops = [] }
let atom_state n atm =
List.assoc atm n.atoms

View File

@ -1,292 +0,0 @@
type pstate =
{ data: string
; line: int
; coln: int
; indx: int }
type perror =
{ error: string
; ps: pstate }
exception ParseError of perror
type 'a parser =
{ fn: 'r. pstate -> ('a -> pstate -> 'r) -> 'r }
let update_pos ps beg fin =
let l, c = (ref ps.line, ref ps.coln) in
for i = beg to fin - 1 do
if ps.data.[i] = '\n' then
(incr l; c := 0)
else
incr c
done;
{ ps with line = !l; coln = !c }
let pret (type a) (x: a): a parser =
let fn ps k = k x ps in { fn }
let pfail error: 'a parser =
let fn ps _ = raise (ParseError {error; ps})
in { fn }
let por: 'a parser -> 'a parser -> 'a parser =
fun p1 p2 ->
let fn ps k =
try p1.fn ps k with ParseError e1 ->
try p2.fn ps k with ParseError e2 ->
if e1.ps.indx > e2.ps.indx then
raise (ParseError e1)
else
raise (ParseError e2)
in { fn }
let pbind: 'a parser -> ('a -> 'b parser) -> 'b parser =
fun p1 p2 ->
let fn ps k =
p1.fn ps (fun x ps -> (p2 x).fn ps k)
in { fn }
(* handy for recursive rules *)
let papp p x = pbind (pret x) p
let psnd: 'a parser -> 'b parser -> 'b parser =
fun p1 p2 -> pbind p1 (fun _x -> p2)
let pfst: 'a parser -> 'b parser -> 'a parser =
fun p1 p2 -> pbind p1 (fun x -> psnd p2 (pret x))
module Infix = struct
let ( let* ) = pbind
let ( ||| ) = por
let ( |<< ) = pfst
let ( |>> ) = psnd
end
open Infix
let pre: ?what:string -> string -> string parser =
fun ?what re ->
let what =
match what with
| None -> Printf.sprintf "%S" re
| Some what -> what
and re = Str.regexp re in
let fn ps k =
if not (Str.string_match re ps.data ps.indx) then
(let error =
Printf.sprintf "expected to match %s" what in
raise (ParseError {error; ps}));
let ps =
let indx = Str.match_end () in
{ (update_pos ps ps.indx indx) with indx }
in
k (Str.matched_string ps.data) ps
in { fn }
let peoi: unit parser =
let fn ps k =
if ps.indx <> String.length ps.data then
raise (ParseError
{ error = "expected end of input"; ps });
k () ps
in { fn }
let pws = pre "[ \r\n\t*]*"
let pws1 = pre "[ \r\n\t*]+"
let pthen p1 p2 =
let* x1 = p1 in
let* x2 = p2 in
pret (x1, x2)
let rec plist_tail: 'a parser -> ('a list) parser =
fun pitem ->
(pws |>> pre ")" |>> pret []) |||
(let* itm = pitem in
let* itms = plist_tail pitem in
pret (itm :: itms))
let plist pitem =
pws |>> pre ~what:"a list" "("
|>> plist_tail pitem
let plist1p p1 pitem =
pws |>> pre ~what:"a list" "("
|>> pthen p1 (plist_tail pitem)
let ppair p1 p2 =
pws |>> pre ~what:"a pair" "("
|>> pthen p1 p2 |<< pws |<< pre ")"
let run_parser p s =
let ps =
{data = s; line = 1; coln = 0; indx = 0} in
try `Ok (p.fn ps (fun res _ps -> res))
with ParseError e ->
let rec bol i =
if i = 0 then i else
if i < String.length s && s.[i] = '\n'
then i+1 (* XXX BUG *)
else bol (i-1)
in
let rec eol i =
if i = String.length s then i else
if s.[i] = '\n' then i else
eol (i+1)
in
let bol = bol e.ps.indx in
let eol = eol e.ps.indx in
(*
Printf.eprintf "bol:%d eol:%d indx:%d len:%d\n"
bol eol e.ps.indx (String.length s); (* XXX debug *)
*)
let lines =
String.split_on_char '\n'
(String.sub s bol (eol - bol))
in
let nl = List.length lines in
let caret = ref (e.ps.indx - bol) in
let msg = ref [] in
let pfx = " > " in
lines |> List.iteri (fun ln l ->
if ln <> nl - 1 || l <> "" then begin
let ll = String.length l + 1 in
msg := (pfx ^ l ^ "\n") :: !msg;
if !caret <= ll then begin
let pad = String.make !caret ' ' in
msg := (pfx ^ pad ^ "^\n") :: !msg;
end;
caret := !caret - ll;
end;
);
`Error
( e.ps, e.error
, String.concat "" (List.rev !msg) )
(* ---------------------------------------- *)
(* pattern parsing *)
(* ---------------------------------------- *)
(* Example syntax:
(with-vars (a b c d)
(patterns
(ob (add (tmp a) (con d)))
(bsm (add (tmp b) (mul (tmp m) (con 2 4 8)))) ))
*)
open Match
let pint64 =
let* s = pre "[-]?[0-9_]+" in
pret (Int64.of_string s)
let pid =
pre ~what:"an identifer"
"[a-zA-Z][a-zA-Z0-9_]*"
let pop_base =
let sob, obs = show_op_base, op_bases in
let* s = pre ~what:"an operator"
(String.concat "\\|" (List.map sob obs))
in pret (List.find (fun o -> s = sob o) obs)
let pop = let* ob = pop_base in pret (Kl, ob)
let rec ppat vs =
let pcons_tail =
let* cs = plist_tail (pws1 |>> pint64) in
match cs with
| [] -> pret [AnyCon]
| _ -> pret (List.map (fun c -> Con c) cs)
in
let pvar =
let* id = pid in
if not (List.mem id vs) then
pfail ("unbound variable: " ^ id)
else
pret id
in
pws |>> (
( let* c = pint64 in pret [Atm (Con c)] )
|||
( pre "(con)" |>> pret [Atm AnyCon] ) |||
( let* cs = pre "(con" |>> pcons_tail in
pret (List.map (fun c -> Atm c) cs) ) |||
( let* v = pre "(con" |>> pws1 |>> pvar in
let* cs = pcons_tail in
pret (List.map (fun c -> Var (v, c)) cs) )
|||
( pre "(tmp)" |>> pret [Atm Tmp] ) |||
( let* v = pre "(tmp" |>> pws1 |>> pvar in
pws |>> pre ")" |>> pret [Var (v, Tmp)] )
|||
( let* (op, rands) =
plist1p (pws |>> pop) (papp ppat vs) in
let nrands = List.length rands in
if nrands < 2 then
pfail ( "binary op requires at least"
^ " two arguments" )
else
let mk x y = Bnr (op, x, y) in
pret
(products rands []
(fun rands pats ->
(* construct a left-heavy tree *)
let r0 = List.hd rands in
let rs = List.tl rands in
List.fold_left mk r0 rs :: pats)) )
)
let pwith_vars ?(vs = []) p =
( let* vs =
pws |>> pre "(with-vars" |>> pws |>>
plist (pws |>> pid)
in pws |>> p vs |<< pws |<< pre ")" )
||| p vs
let ppats =
pwith_vars @@ fun vs ->
pre "(patterns" |>> plist_tail
(pwith_vars ~vs @@ fun vs ->
let* n, ps = ppair pid (ppat vs) in
pret (n, vs, ps))
(* ---------------------------------------- *)
(* tests *)
(* ---------------------------------------- *)
let () =
if false then
let show_patterns ps =
"[" ^ String.concat "; "
(List.map show_pattern ps) ^ "]"
in
let pat s =
Printf.printf "parse %s = " s;
let vars =
[ "foobar"; "a"; "b"; "d"
; "m"; "s"; "x" ]
in
match run_parser (ppat vars) s with
| `Ok p ->
Printf.printf "%s\n" (show_patterns p)
| `Error (_, e, _) ->
Printf.printf "ERROR: %s\n" e
in
pat "42";
pat "(tmp)";
pat "(tmp foobar)";
pat "(con)";
pat "(con 1 2 3)";
pat "(con x 1 2 3)";
pat "(add 1 2)";
pat "(add 1 2 3 4)";
pat "(sub 1 2)";
pat "(sub 1 2 3)";
pat "(tmp unbound_var)";
pat "(add 0)";
pat "(add 1 (add 2 3))";
pat "(add (tmp a) (con d))";
pat "(add (tmp b) (mul (tmp m) (con s 2 4 8)))";
pat "(add (con 1 2) (con 3 4))";
()

View File

@ -1,134 +0,0 @@
open Match
open Fuzz
open Cgen
(* unit tests *)
let test_pattern_match =
let pm = pattern_match
and nm = fun x y -> not (pattern_match x y) in
begin
assert (nm (Atm Tmp) (Atm (Con 42L)));
assert (pm (Atm AnyCon) (Atm (Con 42L)));
assert (nm (Atm (Con 42L)) (Atm AnyCon));
assert (nm (Atm (Con 42L)) (Atm Tmp));
end
let test_peel =
let o = Kw, Oadd in
let p = Bnr (o, Bnr (o, Atm Tmp, Atm Tmp),
Atm (Con 42L)) in
let l = peel p () in
let () = assert (List.length l = 3) in
let atomic_p (p, _) =
match p with Atm _ -> true | _ -> false in
let () = assert (List.for_all atomic_p l) in
let l = List.map (fun (p, c) -> fold_cursor c p) l in
let () = assert (List.for_all ((=) p) l) in
()
let test_fold_pairs =
let l = [1; 2; 3; 4; 5] in
let p = fold_pairs l l [] (fun a b -> a :: b) in
let () = assert (List.length p = 25) in
let p = sort_uniq compare p in
let () = assert (List.length p = 25) in
()
(* test pattern & state *)
let print_sm oc =
StateMap.iter (fun k s' ->
match k with
| K (o, sl, sr) ->
let top =
List.fold_left (fun top c ->
match c with
| Top r -> top ^ " " ^ r
| _ -> top) "" s'.point
in
Printf.fprintf oc
" (%s %d %d) -> %d%s\n"
(show_op o)
sl.id sr.id s'.id top)
let rules =
let oa = Kl, Oadd in
let om = Kl, Omul in
let va = Var ("a", Tmp)
and vb = Var ("b", Tmp)
and vc = Var ("c", Tmp)
and vs = Var ("s", Tmp) in
let vars = ["a"; "b"; "c"; "s"] in
let rule name pattern =
List.map
(fun pattern -> {name; vars; pattern})
(ac_equiv pattern)
in
match `X64Addr with
(* ------------------------------- *)
| `X64Addr ->
(* o + b *)
rule "ob" (Bnr (oa, Atm Tmp, Atm AnyCon))
@ (* b + s * m *)
rule "bsm" (Bnr (oa, vb, Bnr (om, Var ("m", Con 2L), vs)))
@
rule "bsm" (Bnr (oa, vb, Bnr (om, Var ("m", Con 4L), vs)))
@
rule "bsm" (Bnr (oa, vb, Bnr (om, Var ("m", Con 8L), vs)))
@ (* b + s *)
rule "bs1" (Bnr (oa, vb, vs))
@ (* o + s * m *)
(* rule "osm" (Bnr (oa, Atm AnyCon, Bnr (om, Atm (Con 4L), Atm Tmp))) *) []
@ (* o + b + s *)
rule "obs1" (Bnr (oa, Bnr (oa, Var ("o", AnyCon), vb), vs))
@ (* o + b + s * m *)
rule "obsm" (Bnr (oa, Bnr (oa, Var ("o", AnyCon), vb),
Bnr (om, Var ("m", Con 2L), vs)))
@
rule "obsm" (Bnr (oa, Bnr (oa, Var ("o", AnyCon), vb),
Bnr (om, Var ("m", Con 4L), vs)))
@
rule "obsm" (Bnr (oa, Bnr (oa, Var ("o", AnyCon), vb),
Bnr (om, Var ("m", Con 8L), vs)))
(* ------------------------------- *)
| `Add3 ->
[ { name = "add"
; vars = []
; pattern = Bnr (oa, va, Bnr (oa, vb, vc)) } ] @
[ { name = "add"
; vars = []
; pattern = Bnr (oa, Bnr (oa, va, vb), vc) } ]
(*
let sa, am, sm = generate_table rules
let () =
Array.iteri (fun i s ->
Format.printf "@[state %d: %s@]@."
i (show_pattern s.seen))
sa
let () = print_sm stdout sm; flush stdout
let matcher = lr_matcher sm sa rules "obsm" (* XXX *)
let () = Format.printf "@[<v>%a@]@." Action.pp matcher
let () = Format.printf "@[matcher size: %d@]@." (Action.size matcher)
let numbr = make_numberer sa am sm
let () =
let opts = { pfx = ""
; static = true
; oc = stdout } in
emit_c opts numbr;
emit_matchers opts
[ ( ["b"; "o"; "s"; "m"]
, "obsm"
, matcher ) ]
(*
let tp = fuzz_numberer rules numbr
let () = test_matchers tp numbr rules
*)
*)

View File

@ -1,12 +1,8 @@
#!/bin/sh
dir=`dirname "$0"`
if [ -z "${bin:-}" ]; then
bin=$dir/../qbe
fi
if [ -z "${binref:-}" ]; then
binref=${bin}.ref
fi
binref=$dir/../qbe.ref
tmp=/tmp/qbe.zzzz
@ -16,98 +12,55 @@ asmref=$tmp.ref.s
exe=$tmp.exe
out=$tmp.out
qemu_not_needed() {
"$@"
}
cc=
find_cc_and_qemu() {
if [ -n "$cc" ]; then
return
fi
target="$1"
candidate_cc="$2"
if $candidate_cc -v >/dev/null 2>&1; then
cc=$candidate_cc
echo "cc: $cc"
if [ "$target" = "$(uname -m)" ]
then
qemu=qemu_not_needed
echo "qemu: not needed, testing native architecture"
else
qemu="$3"
if $qemu -version >/dev/null 2>&1
then
sysroot=$($candidate_cc -print-sysroot)
if [ -n "$sysroot" ]; then
qemu="$qemu -L $sysroot"
fi
echo "qemu: $qemu"
elif $qemu --version >/dev/null 2>&1
then
# wine
:
else
qemu=
echo "qemu: not found"
fi
fi
echo
fi
}
init() {
case "$TARGET" in
arm64)
for p in aarch64-linux-musl aarch64-linux-gnu
do
find_cc_and_qemu aarch64 "$p-gcc -no-pie -static" "qemu-aarch64"
cc="$p-gcc -no-pie -static"
qemu="qemu-aarch64"
if
$cc -v >/dev/null 2>&1 &&
$qemu -version >/dev/null 2>&1
then
if sysroot=$($cc -print-sysroot) && test -n "$sysroot"
then
qemu="$qemu -L $sysroot"
fi
break
fi
cc=
done
if test -z "$cc" -o -z "$qemu"
if test -z "$cc"
then
echo "Cannot find arm64 compiler or qemu."
exit 77
exit 1
fi
bin="$bin -t arm64"
;;
rv64)
for p in riscv64-linux-musl riscv64-linux-gnu
do
find_cc_and_qemu riscv64 "$p-gcc -no-pie -static" "qemu-riscv64"
done
if test -z "$cc" -o -z "$qemu"
cc="$p-gcc -no-pie -static"
qemu="qemu-riscv64"
if
$cc -v >/dev/null 2>&1 &&
$qemu -version >/dev/null 2>&1
then
echo "Cannot find riscv64 compiler or qemu."
exit 77
fi
bin="$bin -t rv64"
;;
x86_64)
for p in x86_64-linux-musl x86_64-linux-gnu
do
find_cc_and_qemu x86_64 "$p-gcc -no-pie -static" "qemu-x86_64"
done
if test -z "$cc" -o -z "$qemu"
if sysroot=$($cc -print-sysroot) && test -n "$sysroot"
then
echo "Cannot find x86_64 compiler or qemu."
exit 77
qemu="$qemu -L $sysroot"
fi
bin="$bin -t amd64_sysv"
;;
amd64_win)
for p in x86_64-w64-mingw32
do
find_cc_and_qemu x86_64-w64 "$p-gcc -static" "wine"
break
fi
cc=
done
if test -z "$cc"
then
echo "Cannot find windows compiler or wine."
echo "Cannot find riscv64 compiler or qemu."
exit 1
fi
export WINEDEBUG=-all
bin="$bin -t amd64_win"
bin="$bin -t rv64"
;;
"")
case `uname` in
@ -129,7 +82,7 @@ init() {
;;
*)
echo "Unknown target '$TARGET'."
exit 77
exit 1
;;
esac
}
@ -204,7 +157,7 @@ once() {
if test -s $out
then
$qemu $exe a b c | tr -d '\r' | diff -u - $out
$qemu $exe a b c | diff -u - $out
ret=$?
reason="output"
else

196
util.c
View File

@ -154,24 +154,6 @@ vgrow(void *vp, ulong len)
*(Vec **)vp = v1;
}
void
addins(Ins **pvins, uint *pnins, Ins *i)
{
if (i->op == Onop)
return;
vgrow(pvins, ++(*pnins));
(*pvins)[(*pnins)-1] = *i;
}
void
addbins(Ins **pvins, uint *pnins, Blk *b)
{
Ins *i;
for (i=b->ins; i<&b->ins[b->nins]; i++)
addins(pvins, pnins, i);
}
void
strf(char str[NString], char *s, ...)
{
@ -247,62 +229,6 @@ iscmp(int op, int *pk, int *pc)
return 1;
}
void
igroup(Blk *b, Ins *i, Ins **i0, Ins **i1)
{
Ins *ib, *ie;
ib = b->ins;
ie = ib + b->nins;
switch (i->op) {
case Oblit0:
*i0 = i;
*i1 = i + 2;
return;
case Oblit1:
*i0 = i - 1;
*i1 = i + 1;
return;
case_Opar:
for (; i>ib && ispar((i-1)->op); i--)
;
*i0 = i;
for (; i<ie && ispar(i->op); i++)
;
*i1 = i;
return;
case Ocall:
case_Oarg:
for (; i>ib && isarg((i-1)->op); i--)
;
*i0 = i;
for (; i<ie && i->op != Ocall; i++)
;
assert(i < ie);
*i1 = i + 1;
return;
case Osel1:
for (; i>ib && (i-1)->op == Osel1; i--)
;
assert(i->op == Osel0);
/* fall through */
case Osel0:
*i0 = i++;
for (; i<ie && i->op == Osel1; i++)
;
*i1 = i;
return;
default:
if (ispar(i->op))
goto case_Opar;
if (isarg(i->op))
goto case_Oarg;
*i0 = i;
*i1 = i + 1;
return;
}
}
int
argcls(Ins *i, int n)
{
@ -327,18 +253,18 @@ emiti(Ins i)
}
void
idup(Blk *b, Ins *s, ulong n)
idup(Ins **pd, Ins *s, ulong n)
{
vgrow(&b->ins, n);
icpy(b->ins, s, n);
b->nins = n;
*pd = alloc(n * sizeof(Ins));
if (n)
memcpy(*pd, s, n * sizeof(Ins));
}
Ins *
icpy(Ins *d, Ins *s, ulong n)
{
if (n)
memmove(d, s, n * sizeof(Ins));
memcpy(d, s, n * sizeof(Ins));
return d + n;
}
@ -378,16 +304,6 @@ cmpop(int c)
return cmptab[c][1];
}
int
cmpwlneg(int op)
{
if (INRANGE(op, Ocmpw, Ocmpw1))
return cmpneg(op - Ocmpw) + Ocmpw;
if (INRANGE(op, Ocmpl, Ocmpl1))
return cmpneg(op - Ocmpl) + Ocmpl;
die("not a wl comparison");
}
int
clsmerge(short *pk, short k)
{
@ -418,28 +334,6 @@ phicls(int t, Tmp *tmp)
return t1;
}
uint
phiargn(Phi *p, Blk *b)
{
uint n;
if (p)
for (n=0; n<p->narg; n++)
if (p->blk[n] == b)
return n;
return -1;
}
Ref
phiarg(Phi *p, Blk *b)
{
uint n;
n = phiargn(p, b);
assert(n != -1u && "block not found");
return p->arg[n];
}
Ref
newtmp(char *prfx, int k, Fn *fn)
{
@ -504,40 +398,22 @@ getcon(int64_t val, Fn *fn)
}
int
addcon(Con *c0, Con *c1, int m)
addcon(Con *c0, Con *c1)
{
if (m != 1 && c1->type == CAddr)
return 0;
if (c0->type == CUndef) {
if (c0->type == CUndef)
*c0 = *c1;
c0->bits.i *= m;
} else {
else {
if (c1->type == CAddr) {
if (c0->type == CAddr)
return 0;
c0->type = CAddr;
c0->sym = c1->sym;
}
c0->bits.i += c1->bits.i * m;
c0->bits.i += c1->bits.i;
}
return 1;
}
int
isconbits(Fn *fn, Ref r, int64_t *v)
{
Con *c;
if (rtype(r) == RCon) {
c = &fn->con[r.val];
if (c->type == CBits) {
*v = c->bits.i;
return 1;
}
}
return 0;
}
void
salloc(Ref rt, Ref rs, Fn *fn)
{
@ -718,57 +594,3 @@ dumpts(BSet *bs, Tmp *tmp, FILE *f)
fprintf(f, " %s", tmp[t].name);
fprintf(f, " ]\n");
}
void
runmatch(uchar *code, Num *tn, Ref ref, Ref *var)
{
Ref stkbuf[20], *stk;
uchar *s, *pc;
int bc, i;
int n, nl, nr;
assert(rtype(ref) == RTmp);
stk = stkbuf;
pc = code;
while ((bc = *pc))
switch (bc) {
case 1: /* pushsym */
case 2: /* push */
assert(stk < &stkbuf[20]);
assert(rtype(ref) == RTmp);
nl = tn[ref.val].nl;
nr = tn[ref.val].nr;
if (bc == 1 && nl > nr) {
*stk++ = tn[ref.val].l;
ref = tn[ref.val].r;
} else {
*stk++ = tn[ref.val].r;
ref = tn[ref.val].l;
}
pc++;
break;
case 3: /* set */
var[*++pc] = ref;
if (*(pc + 1) == 0)
return;
/* fall through */
case 4: /* pop */
assert(stk > &stkbuf[0]);
ref = *--stk;
pc++;
break;
case 5: /* switch */
assert(rtype(ref) == RTmp);
n = tn[ref.val].n;
s = pc + 1;
for (i=*s++; i>0; i--, s++)
if (n == *s++)
break;
pc += *s;
break;
default: /* jump */
assert(bc >= 10);
pc = code + (bc - 10);
break;
}
}