mirror of
git://c9x.me/qbe.git
synced 2026-04-09 03:49:46 +00:00
Compare commits
82 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ff0651552 | ||
|
|
7ac9722ccb | ||
|
|
4f9b94a9b3 | ||
|
|
5f40188f9e | ||
|
|
01102ad63b | ||
|
|
d5f02dc67c | ||
|
|
d166a61141 | ||
|
|
cf06ce159d | ||
|
|
640c78d0da | ||
|
|
afd5d2e518 | ||
|
|
e8365dd0a2 | ||
|
|
c6336557da | ||
|
|
5c1eb24e2c | ||
|
|
7201079137 | ||
|
|
112cc1b824 | ||
|
|
6a2dca8b99 | ||
|
|
e0ded59639 | ||
|
|
0f6bbb1c7c | ||
|
|
73f0accb45 | ||
|
|
03da40271f | ||
|
|
120f316162 | ||
|
|
8d5b86ac4c | ||
|
|
348f2eac90 | ||
|
|
903610de4f | ||
|
|
f7ab20680b | ||
|
|
789e38fdb2 | ||
|
|
86d13b93e8 | ||
|
|
ec5502645f | ||
|
|
f3ca257737 | ||
|
|
1cb255cb04 | ||
|
|
c2ff93e75e | ||
|
|
9e36cbe4d8 | ||
|
|
ecfdac4f00 | ||
|
|
1c769584ac | ||
|
|
0ce9966c23 | ||
|
|
434cf5fdc4 | ||
|
|
ca928f6f8d | ||
|
|
024dffac8b | ||
|
|
c16f7eafca | ||
|
|
327736b3a6 | ||
|
|
90050202f5 | ||
|
|
626f0b2781 | ||
|
|
bb8de8c633 | ||
|
|
efcb5465e5 | ||
|
|
e8fa27bcdb | ||
|
|
7de34d3454 | ||
|
|
6dab93b4b2 | ||
|
|
96f18be71d | ||
|
|
0631bc4fec | ||
|
|
e7ebdc8fb6 | ||
|
|
b5be429091 | ||
|
|
ddf5ced4a7 | ||
|
|
7a19de5cf4 | ||
|
|
cf9f95f521 | ||
|
|
2c2051542b | ||
|
|
c8220b638b | ||
|
|
620277c004 | ||
|
|
1dd22a9b47 | ||
|
|
b24af7d3f7 | ||
|
|
99169df2ff | ||
|
|
fc98435f81 | ||
|
|
875c1abf93 | ||
|
|
8ded7a56a2 | ||
|
|
4a809d69b5 | ||
|
|
8e8f706436 | ||
|
|
2d046a0ac6 | ||
|
|
a609527752 | ||
|
|
56e2263ca4 | ||
|
|
8a5e1c3a23 | ||
|
|
a374da3c2e | ||
|
|
24d1324424 | ||
|
|
a2ad38086c | ||
|
|
dc3f7d7c4a | ||
|
|
de5ced474d | ||
|
|
c8fc20b8ef | ||
|
|
85827e2232 | ||
|
|
1b7770e271 | ||
|
|
be5d46fe7d | ||
|
|
b582e84e10 | ||
|
|
9ee57a637a | ||
|
|
190fd3fb09 | ||
|
|
bd7a73d0ea |
1
.gitignore
vendored
1
.gitignore
vendored
@ -3,3 +3,4 @@ qbe
|
||||
config.h
|
||||
.comfile
|
||||
*.out
|
||||
*~
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
||||
© 2015-2023 Quentin Carbonneaux <quentin@c9x.me>
|
||||
© 2015-2026 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"),
|
||||
|
||||
16
Makefile
16
Makefile
@ -5,8 +5,9 @@ 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 simpl.o live.o spill.o rega.o emit.o
|
||||
AMD64OBJ = amd64/targ.o amd64/sysv.o amd64/isel.o amd64/emit.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
|
||||
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)
|
||||
@ -71,12 +72,18 @@ 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)
|
||||
|
||||
@ -90,4 +97,7 @@ src:
|
||||
}" < $$F; \
|
||||
done
|
||||
|
||||
.PHONY: clean clean-gen check check-arm64 src 80 install uninstall
|
||||
wc:
|
||||
@wc -l $(SRCALL)
|
||||
|
||||
.PHONY: clean clean-gen check check-arm64 check-rv64 src 80 wc install uninstall
|
||||
|
||||
2
README
2
README
@ -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 manually.
|
||||
Alternatively, you may simply copy the qbe binary.
|
||||
|
||||
8
alias.c
8
alias.c
@ -132,7 +132,7 @@ store(Ref r, int sz, Fn *fn)
|
||||
void
|
||||
fillalias(Fn *fn)
|
||||
{
|
||||
uint n, m;
|
||||
uint n;
|
||||
int t, sz;
|
||||
int64_t x;
|
||||
Blk *b;
|
||||
@ -146,8 +146,6 @@ 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);
|
||||
@ -217,4 +215,8 @@ 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
81
all.h
@ -21,6 +21,7 @@ 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;
|
||||
@ -43,6 +44,7 @@ enum {
|
||||
struct Target {
|
||||
char name[16];
|
||||
char apple;
|
||||
char windows;
|
||||
int gpr0; /* first general purpose reg */
|
||||
int ngpr;
|
||||
int fpr0; /* first floating point reg */
|
||||
@ -61,6 +63,7 @@ struct Target {
|
||||
void (*emitfin)(FILE *);
|
||||
char asloc[4];
|
||||
char assym[4];
|
||||
uint cansel:1;
|
||||
};
|
||||
|
||||
#define BIT(n) ((bits)1 << (n))
|
||||
@ -115,7 +118,7 @@ static inline int rtype(Ref r)
|
||||
|
||||
static inline int rsval(Ref r)
|
||||
{
|
||||
return (int32_t)((int64_t)r.val << 3) >> 3;
|
||||
return ((int)r.val ^ 0x10000000) - 0x10000000;
|
||||
}
|
||||
|
||||
enum CmpI {
|
||||
@ -182,6 +185,8 @@ enum {
|
||||
Oalloc1 = Oalloc16,
|
||||
Oflag = Oflagieq,
|
||||
Oflag1 = Oflagfuo,
|
||||
Oxsel = Oxselieq,
|
||||
Oxsel1 = Oxselfuo,
|
||||
NPubOp = Onop,
|
||||
Jjf = Jjfieq,
|
||||
Jjf1 = Jjffuo,
|
||||
@ -190,6 +195,7 @@ 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)
|
||||
@ -197,6 +203,7 @@ 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()) */
|
||||
@ -212,7 +219,16 @@ enum {
|
||||
struct Op {
|
||||
char *name;
|
||||
short argcls[2][4];
|
||||
int canfold;
|
||||
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? */
|
||||
};
|
||||
|
||||
struct Ins {
|
||||
@ -227,7 +243,8 @@ struct Phi {
|
||||
Ref *arg;
|
||||
Blk **blk;
|
||||
uint narg;
|
||||
int cls;
|
||||
short cls;
|
||||
uint visit:1;
|
||||
Phi *link;
|
||||
};
|
||||
|
||||
@ -250,6 +267,7 @@ struct Blk {
|
||||
Blk *dom, *dlink;
|
||||
Blk **fron;
|
||||
uint nfron;
|
||||
int depth;
|
||||
|
||||
Blk **pred;
|
||||
uint npred;
|
||||
@ -281,6 +299,12 @@ struct Sym {
|
||||
uint32_t id;
|
||||
};
|
||||
|
||||
struct Num {
|
||||
uchar n;
|
||||
uchar nl, nr;
|
||||
Ref l, r;
|
||||
};
|
||||
|
||||
enum {
|
||||
NoAlias,
|
||||
MayAlias,
|
||||
@ -335,6 +359,7 @@ struct Tmp {
|
||||
Wuw
|
||||
} width;
|
||||
int visit;
|
||||
uint gcmbid;
|
||||
};
|
||||
|
||||
struct Con {
|
||||
@ -364,6 +389,7 @@ struct Addr { /* amd64 addressing */
|
||||
struct Lnk {
|
||||
char export;
|
||||
char thread;
|
||||
char common;
|
||||
char align;
|
||||
char *sec;
|
||||
char *secf;
|
||||
@ -383,8 +409,10 @@ struct Fn {
|
||||
Blk **rpo;
|
||||
bits reg;
|
||||
int slot;
|
||||
int salign;
|
||||
char vararg;
|
||||
char dynalloc;
|
||||
char leaf;
|
||||
char name[NString];
|
||||
Lnk lnk;
|
||||
};
|
||||
@ -458,29 +486,36 @@ 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(Ins **, Ins *, ulong);
|
||||
void idup(Blk *, 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 addcon(Con *, Con *, int);
|
||||
int isconbits(Fn *fn, Ref r, int64_t *v);
|
||||
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 *);
|
||||
@ -512,16 +547,21 @@ void elimsb(Fn *);
|
||||
|
||||
/* cfg.c */
|
||||
Blk *newblk(void);
|
||||
void edgedel(Blk *, Blk **);
|
||||
void fillpreds(Fn *);
|
||||
void fillrpo(Fn *);
|
||||
void fillcfg(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 *);
|
||||
@ -539,17 +579,31 @@ 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 copy(Fn *);
|
||||
void narrowpars(Fn *fn);
|
||||
Ref copyref(Fn *, Blk *, Ins *);
|
||||
Ref phicopyref(Fn *, Blk *, Phi *);
|
||||
|
||||
/* fold.c */
|
||||
void fold(Fn *);
|
||||
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);
|
||||
|
||||
/* simpl.c */
|
||||
void simpl(Fn *);
|
||||
@ -570,7 +624,8 @@ void emitfnlnk(char *, Lnk *, FILE *);
|
||||
void emitdat(Dat *, FILE *);
|
||||
void emitdbgfile(char *, FILE *);
|
||||
void emitdbgloc(uint, uint, FILE *);
|
||||
int stashbits(void *, int);
|
||||
int stashbits(bits, int);
|
||||
void elf_emitfnfin(char *, FILE *);
|
||||
void elf_emitfin(FILE *);
|
||||
void macho_emitfin(FILE *);
|
||||
void pe_emitfin(FILE *);
|
||||
|
||||
34
amd64/all.h
34
amd64/all.h
@ -4,14 +4,14 @@ typedef struct Amd64Op Amd64Op;
|
||||
|
||||
enum Amd64Reg {
|
||||
RAX = RXX+1, /* caller-save */
|
||||
RCX,
|
||||
RDX,
|
||||
RSI,
|
||||
RDI,
|
||||
R8,
|
||||
R9,
|
||||
R10,
|
||||
R11,
|
||||
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 */
|
||||
|
||||
RBX, /* callee-save */
|
||||
R12,
|
||||
@ -41,9 +41,13 @@ enum Amd64Reg {
|
||||
|
||||
NFPR = XMM14 - XMM0 + 1, /* reserve XMM15 */
|
||||
NGPR = RSP - RAX + 1,
|
||||
NGPS = R11 - RAX + 1,
|
||||
NFPS = NFPR,
|
||||
NCLR = R15 - RBX + 1,
|
||||
|
||||
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 */
|
||||
};
|
||||
MAKESURE(reg_not_tmp, XMM15 < (int)Tmp0);
|
||||
|
||||
@ -63,8 +67,16 @@ 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_emitfn(Fn *, FILE *);
|
||||
void amd64_sysv_emitfn(Fn *, FILE *);
|
||||
void amd64_winabi_emitfn(Fn *, FILE *);
|
||||
|
||||
546
amd64/emit.c
546
amd64/emit.c
@ -1,25 +1,33 @@
|
||||
#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") \
|
||||
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")
|
||||
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")
|
||||
|
||||
enum {
|
||||
SLong = 0,
|
||||
@ -60,67 +68,79 @@ enum {
|
||||
static struct {
|
||||
short op;
|
||||
short cls;
|
||||
char *asm;
|
||||
char *fmt;
|
||||
} omap[] = {
|
||||
{ Oadd, Ka, "+add%k %1, %=" },
|
||||
{ Osub, Ka, "-sub%k %1, %=" },
|
||||
{ Oand, Ki, "+and%k %1, %=" },
|
||||
{ Oor, Ki, "+or%k %1, %=" },
|
||||
{ Oxor, Ki, "+xor%k %1, %=" },
|
||||
{ Osar, Ki, "-sar%k %B1, %=" },
|
||||
{ Oshr, Ki, "-shr%k %B1, %=" },
|
||||
{ Oshl, Ki, "-shl%k %B1, %=" },
|
||||
{ Omul, Ki, "+imul%k %1, %=" },
|
||||
{ Omul, Ks, "+mulss %1, %=" },
|
||||
{ Omul, Kd, "+mulsd %1, %=" },
|
||||
{ Odiv, Ka, "-div%k %1, %=" },
|
||||
{ Ostorel, Ka, "movq %L0, %M1" },
|
||||
{ Ostorew, Ka, "movl %W0, %M1" },
|
||||
{ Ostoreh, Ka, "movw %H0, %M1" },
|
||||
{ Ostoreb, Ka, "movb %B0, %M1" },
|
||||
{ Ostores, Ka, "movss %S0, %M1" },
|
||||
{ Ostored, Ka, "movsd %D0, %M1" },
|
||||
{ Oload, Ka, "mov%k %M0, %=" },
|
||||
{ Oloadsw, Kl, "movslq %M0, %L=" },
|
||||
{ Oloadsw, Kw, "movl %M0, %W=" },
|
||||
{ Oloaduw, Ki, "movl %M0, %W=" },
|
||||
{ Oloadsh, Ki, "movsw%k %M0, %=" },
|
||||
{ Oloaduh, Ki, "movzw%k %M0, %=" },
|
||||
{ Oloadsb, Ki, "movsb%k %M0, %=" },
|
||||
{ Oloadub, Ki, "movzb%k %M0, %=" },
|
||||
{ Oextsw, Kl, "movslq %W0, %L=" },
|
||||
{ Oextuw, Kl, "movl %W0, %W=" },
|
||||
{ Oextsh, Ki, "movsw%k %H0, %=" },
|
||||
{ Oextuh, Ki, "movzw%k %H0, %=" },
|
||||
{ Oextsb, Ki, "movsb%k %B0, %=" },
|
||||
{ Oextub, Ki, "movzb%k %B0, %=" },
|
||||
{ Oadd, Ka, "+add%k %1, %=" },
|
||||
{ Osub, Ka, "-sub%k %1, %=" },
|
||||
{ Oand, Ki, "+and%k %1, %=" },
|
||||
{ Oor, Ki, "+or%k %1, %=" },
|
||||
{ Oxor, Ki, "+xor%k %1, %=" },
|
||||
{ Osar, Ki, "-sar%k %B1, %=" },
|
||||
{ Oshr, Ki, "-shr%k %B1, %=" },
|
||||
{ Oshl, Ki, "-shl%k %B1, %=" },
|
||||
{ Omul, Ki, "+imul%k %1, %=" },
|
||||
{ Omul, Ks, "+mulss %1, %=" },
|
||||
{ Omul, Kd, "+mulsd %1, %=" },
|
||||
{ Odiv, Ka, "-div%k %1, %=" },
|
||||
{ Ostorel, Ka, "movq %L0, %M1" },
|
||||
{ Ostorew, Ka, "movl %W0, %M1" },
|
||||
{ Ostoreh, Ka, "movw %H0, %M1" },
|
||||
{ Ostoreb, Ka, "movb %B0, %M1" },
|
||||
{ Ostores, Ka, "movss %S0, %M1" },
|
||||
{ Ostored, Ka, "movsd %D0, %M1" },
|
||||
{ Oload, Ka, "mov%k %M0, %=" },
|
||||
{ Oloadsw, Kl, "movslq %M0, %L=" },
|
||||
{ Oloadsw, Kw, "movl %M0, %W=" },
|
||||
{ Oloaduw, Ki, "movl %M0, %W=" },
|
||||
{ Oloadsh, Ki, "movsw%k %M0, %=" },
|
||||
{ Oloaduh, Ki, "movzw%k %M0, %=" },
|
||||
{ Oloadsb, Ki, "movsb%k %M0, %=" },
|
||||
{ Oloadub, Ki, "movzb%k %M0, %=" },
|
||||
{ Oextsw, Kl, "movslq %W0, %L=" },
|
||||
{ Oextuw, Kl, "movl %W0, %W=" },
|
||||
{ Oextsh, Ki, "movsw%k %H0, %=" },
|
||||
{ Oextuh, Ki, "movzw%k %H0, %=" },
|
||||
{ Oextsb, Ki, "movsb%k %B0, %=" },
|
||||
{ Oextub, Ki, "movzb%k %B0, %=" },
|
||||
|
||||
{ Oexts, Kd, "cvtss2sd %0, %=" },
|
||||
{ Otruncd, Ks, "cvtsd2ss %0, %=" },
|
||||
{ Ostosi, Ki, "cvttss2si%k %0, %=" },
|
||||
{ Odtosi, Ki, "cvttsd2si%k %0, %=" },
|
||||
{ Oswtof, Ka, "cvtsi2%k %W0, %=" },
|
||||
{ Osltof, Ka, "cvtsi2%k %L0, %=" },
|
||||
{ Ocast, Ki, "movq %D0, %L=" },
|
||||
{ Ocast, Ka, "movq %L0, %D=" },
|
||||
{ Oexts, Kd, "cvtss2sd %0, %=" },
|
||||
{ Otruncd, Ks, "cvtsd2ss %0, %=" },
|
||||
{ Ostosi, Ki, "cvttss2si%k %0, %=" },
|
||||
{ Odtosi, Ki, "cvttsd2si%k %0, %=" },
|
||||
{ Oswtof, Ka, "cvtsi2%k %W0, %=" },
|
||||
{ Osltof, Ka, "cvtsi2%k %L0, %=" },
|
||||
{ Ocast, Ki, "movq %D0, %L=" },
|
||||
{ Ocast, Ka, "movq %L0, %D=" },
|
||||
|
||||
{ Oaddr, Ki, "lea%k %M0, %=" },
|
||||
{ Oswap, Ki, "xchg%k %0, %1" },
|
||||
{ Osign, Kl, "cqto" },
|
||||
{ Osign, Kw, "cltd" },
|
||||
{ Oxdiv, Ki, "div%k %0" },
|
||||
{ Oxidiv, Ki, "idiv%k %0" },
|
||||
{ Oxcmp, Ks, "ucomiss %S0, %S1" },
|
||||
{ Oxcmp, Kd, "ucomisd %D0, %D1" },
|
||||
{ Oxcmp, Ki, "cmp%k %0, %1" },
|
||||
{ Oxtest, Ki, "test%k %0, %1" },
|
||||
#define X(c, s) \
|
||||
{ Oflag+c, Ki, "set" s " %B=\n\tmovzb%k %B=, %=" },
|
||||
{ Oaddr, Ki, "lea%k %M0, %=" },
|
||||
{ Oswap, Ki, "xchg%k %0, %1" },
|
||||
{ Osign, Kl, "cqto" },
|
||||
{ Osign, Kw, "cltd" },
|
||||
{ Oxdiv, Ki, "div%k %0" },
|
||||
{ Oxidiv, Ki, "idiv%k %0" },
|
||||
{ Oxcmp, Ks, "ucomiss %S0, %S1" },
|
||||
{ Oxcmp, Kd, "ucomisd %D0, %D1" },
|
||||
{ Oxcmp, Ki, "cmp%k %0, %1" },
|
||||
{ Oxtest, Ki, "test%k %0, %1" },
|
||||
#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"},
|
||||
@ -142,23 +162,32 @@ static char *rname[][4] = {
|
||||
|
||||
|
||||
static int
|
||||
slot(Ref r, Fn *fn)
|
||||
slot(Ref r, E *e)
|
||||
{
|
||||
int s;
|
||||
|
||||
s = rsval(r);
|
||||
assert(s <= fn->slot);
|
||||
assert(s <= e->fn->slot);
|
||||
/* specific to NAlign == 3 */
|
||||
if (s < 0)
|
||||
return -4 * s;
|
||||
else if (fn->vararg)
|
||||
return -176 + -4 * (fn->slot - s);
|
||||
else
|
||||
return -4 * (fn->slot - s);
|
||||
if (s < 0) {
|
||||
if (e->fp == RSP)
|
||||
return 4*-s - 8 + e->fsz + e->nclob*8;
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
emitcon(Con *con, FILE *f)
|
||||
emitcon(Con *con, E *e)
|
||||
{
|
||||
char *p, *l;
|
||||
|
||||
@ -168,16 +197,16 @@ emitcon(Con *con, FILE *f)
|
||||
p = l[0] == '"' ? "" : T.assym;
|
||||
if (con->sym.type == SThr) {
|
||||
if (T.apple)
|
||||
fprintf(f, "%s%s@TLVP", p, l);
|
||||
fprintf(e->f, "%s%s@TLVP", p, l);
|
||||
else
|
||||
fprintf(f, "%%fs:%s%s@tpoff", p, l);
|
||||
fprintf(e->f, "%%fs:%s%s@tpoff", p, l);
|
||||
} else
|
||||
fprintf(f, "%s%s", p, l);
|
||||
fprintf(e->f, "%s%s", p, l);
|
||||
if (con->bits.i)
|
||||
fprintf(f, "%+"PRId64, con->bits.i);
|
||||
fprintf(e->f, "%+"PRId64, con->bits.i);
|
||||
break;
|
||||
case CBits:
|
||||
fprintf(f, "%"PRId64, con->bits.i);
|
||||
fprintf(e->f, "%"PRId64, con->bits.i);
|
||||
break;
|
||||
default:
|
||||
die("unreachable");
|
||||
@ -212,10 +241,10 @@ getarg(char c, Ins *i)
|
||||
}
|
||||
}
|
||||
|
||||
static void emitins(Ins, Fn *, FILE *);
|
||||
static void emitins(Ins, E *);
|
||||
|
||||
static void
|
||||
emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
|
||||
emitcopy(Ref r1, Ref r2, int k, E *e)
|
||||
{
|
||||
Ins icp;
|
||||
|
||||
@ -223,11 +252,11 @@ emitcopy(Ref r1, Ref r2, int k, Fn *fn, FILE *f)
|
||||
icp.arg[0] = r2;
|
||||
icp.to = r1;
|
||||
icp.cls = k;
|
||||
emitins(icp, fn, f);
|
||||
emitins(icp, e);
|
||||
}
|
||||
|
||||
static void
|
||||
emitf(char *s, Ins *i, Fn *fn, FILE *f)
|
||||
emitf(char *s, Ins *i, E *e)
|
||||
{
|
||||
static char clstoa[][3] = {"l", "q", "ss", "sd"};
|
||||
char c;
|
||||
@ -247,25 +276,25 @@ emitf(char *s, Ins *i, Fn *fn, FILE *f)
|
||||
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, fn, f);
|
||||
emitcopy(i->to, i->arg[0], i->cls, e);
|
||||
s++;
|
||||
break;
|
||||
}
|
||||
|
||||
fputc('\t', f);
|
||||
fputc('\t', e->f);
|
||||
Next:
|
||||
while ((c = *s++) != '%')
|
||||
if (!c) {
|
||||
fputc('\n', f);
|
||||
fputc('\n', e->f);
|
||||
return;
|
||||
} else
|
||||
fputc(c, f);
|
||||
fputc(c, e->f);
|
||||
switch ((c = *s++)) {
|
||||
case '%':
|
||||
fputc('%', f);
|
||||
fputc('%', e->f);
|
||||
break;
|
||||
case 'k':
|
||||
fputs(clstoa[i->cls], f);
|
||||
fputs(clstoa[i->cls], e->f);
|
||||
break;
|
||||
case '0':
|
||||
case '1':
|
||||
@ -282,37 +311,42 @@ Next:
|
||||
switch (rtype(ref)) {
|
||||
case RTmp:
|
||||
assert(isreg(ref));
|
||||
fprintf(f, "%%%s", regtoa(ref.val, sz));
|
||||
fprintf(e->f, "%%%s", regtoa(ref.val, sz));
|
||||
break;
|
||||
case RSlot:
|
||||
fprintf(f, "%d(%%rbp)", slot(ref, fn));
|
||||
fprintf(e->f, "%d(%%%s)",
|
||||
slot(ref, e),
|
||||
regtoa(e->fp, SLong)
|
||||
);
|
||||
break;
|
||||
case RMem:
|
||||
Mem:
|
||||
m = &fn->mem[ref.val];
|
||||
m = &e->fn->mem[ref.val];
|
||||
if (rtype(m->base) == RSlot) {
|
||||
off.type = CBits;
|
||||
off.bits.i = slot(m->base, fn);
|
||||
addcon(&m->offset, &off);
|
||||
m->base = TMP(RBP);
|
||||
off.bits.i = slot(m->base, e);
|
||||
addcon(&m->offset, &off, 1);
|
||||
m->base = TMP(e->fp);
|
||||
}
|
||||
if (m->offset.type != CUndef)
|
||||
emitcon(&m->offset, f);
|
||||
fputc('(', f);
|
||||
emitcon(&m->offset, e);
|
||||
fputc('(', e->f);
|
||||
if (!req(m->base, R))
|
||||
fprintf(f, "%%%s", regtoa(m->base.val, SLong));
|
||||
fprintf(e->f, "%%%s",
|
||||
regtoa(m->base.val, SLong)
|
||||
);
|
||||
else if (m->offset.type == CAddr)
|
||||
fprintf(f, "%%rip");
|
||||
fprintf(e->f, "%%rip");
|
||||
if (!req(m->index, R))
|
||||
fprintf(f, ", %%%s, %d",
|
||||
fprintf(e->f, ", %%%s, %d",
|
||||
regtoa(m->index.val, SLong),
|
||||
m->scale
|
||||
);
|
||||
fputc(')', f);
|
||||
fputc(')', e->f);
|
||||
break;
|
||||
case RCon:
|
||||
fputc('$', f);
|
||||
emitcon(&fn->con[ref.val], f);
|
||||
fputc('$', e->f);
|
||||
emitcon(&e->fn->con[ref.val], e);
|
||||
break;
|
||||
default:
|
||||
die("unreachable");
|
||||
@ -337,18 +371,21 @@ Next:
|
||||
case RMem:
|
||||
goto Mem;
|
||||
case RSlot:
|
||||
fprintf(f, "%d(%%rbp)", slot(ref, fn));
|
||||
fprintf(e->f, "%d(%%%s)",
|
||||
slot(ref, e),
|
||||
regtoa(e->fp, SLong)
|
||||
);
|
||||
break;
|
||||
case RCon:
|
||||
off = fn->con[ref.val];
|
||||
emitcon(&off, f);
|
||||
off = e->fn->con[ref.val];
|
||||
emitcon(&off, e);
|
||||
if (off.type == CAddr)
|
||||
if (off.sym.type != SThr || T.apple)
|
||||
fprintf(f, "(%%rip)");
|
||||
fprintf(e->f, "(%%rip)");
|
||||
break;
|
||||
case RTmp:
|
||||
assert(isreg(ref));
|
||||
fprintf(f, "(%%%s)", regtoa(ref.val, SLong));
|
||||
fprintf(e->f, "(%%%s)", regtoa(ref.val, SLong));
|
||||
break;
|
||||
default:
|
||||
die("unreachable");
|
||||
@ -360,13 +397,13 @@ Next:
|
||||
goto Next;
|
||||
}
|
||||
|
||||
static void *negmask[4] = {
|
||||
[Ks] = (uint32_t[4]){ 0x80000000 },
|
||||
[Kd] = (uint64_t[2]){ 0x8000000000000000 },
|
||||
static bits negmask[4] = {
|
||||
[Ks] = 0x80000000,
|
||||
[Kd] = 0x8000000000000000,
|
||||
};
|
||||
|
||||
static void
|
||||
emitins(Ins i, Fn *fn, FILE *f)
|
||||
emitins(Ins i, E *e)
|
||||
{
|
||||
Ref r;
|
||||
int64_t val;
|
||||
@ -377,6 +414,8 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
|
||||
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
|
||||
@ -393,7 +432,7 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
|| (omap[o].cls == Ka))
|
||||
break;
|
||||
}
|
||||
emitf(omap[o].asm, &i, fn, f);
|
||||
emitf(omap[o].fmt, &i, e);
|
||||
break;
|
||||
case Onop:
|
||||
/* just do nothing for nops, they are inserted
|
||||
@ -410,7 +449,7 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
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, fn, f);
|
||||
emitf("imul%k %0, %1, %=", &i, e);
|
||||
break;
|
||||
}
|
||||
goto Table;
|
||||
@ -419,18 +458,18 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
* 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, fn, f);
|
||||
emitf("add%k %0, %=", &i, fn, f);
|
||||
emitins(ineg, e);
|
||||
emitf("add%k %0, %=", &i, e);
|
||||
break;
|
||||
}
|
||||
goto Table;
|
||||
case Oneg:
|
||||
if (!req(i.to, i.arg[0]))
|
||||
emitf("mov%k %0, %=", &i, fn, f);
|
||||
emitf("mov%k %0, %=", &i, e);
|
||||
if (KBASE(i.cls) == 0)
|
||||
emitf("neg%k %=", &i, fn, f);
|
||||
emitf("neg%k %=", &i, e);
|
||||
else
|
||||
fprintf(f,
|
||||
fprintf(e->f,
|
||||
"\txorp%c %sfp%d(%%rip), %%%s\n",
|
||||
"xxsd"[i.cls],
|
||||
T.asloc,
|
||||
@ -443,8 +482,8 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
* 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, fn, f);
|
||||
emitf("mov%k %0, %=", &i, fn, f);
|
||||
emitf("mov%k %=, %1", &i, e);
|
||||
emitf("mov%k %0, %=", &i, e);
|
||||
i.arg[0] = i.to;
|
||||
}
|
||||
goto Table;
|
||||
@ -460,53 +499,54 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
t0 = rtype(i.arg[0]);
|
||||
if (i.cls == Kl
|
||||
&& t0 == RCon
|
||||
&& fn->con[i.arg[0].val].type == CBits) {
|
||||
val = fn->con[i.arg[0].val].bits.i;
|
||||
&& e->fn->con[i.arg[0].val].type == CBits) {
|
||||
val = e->fn->con[i.arg[0].val].bits.i;
|
||||
if (isreg(i.to))
|
||||
if (val >= 0 && val <= UINT32_MAX) {
|
||||
emitf("movl %W0, %W=", &i, fn, f);
|
||||
emitf("movl %W0, %W=", &i, e);
|
||||
break;
|
||||
}
|
||||
if (rtype(i.to) == RSlot)
|
||||
if (val < INT32_MIN || val > INT32_MAX) {
|
||||
emitf("movl %0, %=", &i, fn, f);
|
||||
emitf("movl %0>>32, 4+%=", &i, fn, f);
|
||||
emitf("movl %0, %=", &i, e);
|
||||
emitf("movl %0>>32, 4+%=", &i, e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (isreg(i.to)
|
||||
&& t0 == RCon
|
||||
&& fn->con[i.arg[0].val].type == CAddr) {
|
||||
emitf("lea%k %M0, %=", &i, fn, f);
|
||||
&& e->fn->con[i.arg[0].val].type == CAddr) {
|
||||
emitf("lea%k %M0, %=", &i, e);
|
||||
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, fn, f);
|
||||
emitf("mov%k %1, %=", &i, fn, f);
|
||||
emitf("mov%k %0, %1", &i, e);
|
||||
emitf("mov%k %1, %=", &i, e);
|
||||
break;
|
||||
}
|
||||
/* conveniently, the assembler knows if it
|
||||
* should use movabsq when reading movq */
|
||||
emitf("mov%k %0, %=", &i, fn, f);
|
||||
emitf("mov%k %0, %=", &i, e);
|
||||
break;
|
||||
case Oaddr:
|
||||
if (!T.apple
|
||||
&& rtype(i.arg[0]) == RCon
|
||||
&& fn->con[i.arg[0].val].sym.type == SThr) {
|
||||
&& e->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 = &fn->con[i.arg[0].val];
|
||||
con = &e->fn->con[i.arg[0].val];
|
||||
sym = str(con->sym.id);
|
||||
emitf("movq %%fs:0, %L=", &i, fn, f);
|
||||
fprintf(f, "\tleaq %s%s@tpoff",
|
||||
emitf("movq %%fs:0, %L=", &i, e);
|
||||
fprintf(e->f, "\tleaq %s%s@tpoff",
|
||||
sym[0] == '"' ? "" : T.assym, sym);
|
||||
if (con->bits.i)
|
||||
fprintf(f, "%+"PRId64, con->bits.i);
|
||||
fprintf(f, "(%%%s), %%%s\n",
|
||||
fprintf(e->f, "%+"PRId64,
|
||||
con->bits.i);
|
||||
fprintf(e->f, "(%%%s), %%%s\n",
|
||||
regtoa(i.to.val, SLong),
|
||||
regtoa(i.to.val, SLong));
|
||||
break;
|
||||
@ -517,12 +557,12 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
* assembly... */
|
||||
switch (rtype(i.arg[0])) {
|
||||
case RCon:
|
||||
fprintf(f, "\tcallq ");
|
||||
emitcon(&fn->con[i.arg[0].val], f);
|
||||
fprintf(f, "\n");
|
||||
fprintf(e->f, "\tcallq ");
|
||||
emitcon(&e->fn->con[i.arg[0].val], e);
|
||||
fprintf(e->f, "\n");
|
||||
break;
|
||||
case RTmp:
|
||||
emitf("callq *%L0", &i, fn, f);
|
||||
emitf("callq *%L0", &i, e);
|
||||
break;
|
||||
default:
|
||||
die("invalid call argument");
|
||||
@ -533,9 +573,10 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
* maybe we should split Osalloc in 2 different
|
||||
* instructions depending on the result
|
||||
*/
|
||||
emitf("subq %L0, %%rsp", &i, fn, f);
|
||||
assert(e->fp == RBP);
|
||||
emitf("subq %L0, %%rsp", &i, e);
|
||||
if (!req(i.to, R))
|
||||
emitcopy(i.to, TMP(RSP), Kl, fn, f);
|
||||
emitcopy(i.to, TMP(RSP), Kl, e);
|
||||
break;
|
||||
case Oswap:
|
||||
if (KBASE(i.cls) == 0)
|
||||
@ -543,34 +584,51 @@ emitins(Ins i, Fn *fn, FILE *f)
|
||||
/* for floats, there is no swap instruction
|
||||
* so we use xmm15 as a temporary
|
||||
*/
|
||||
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);
|
||||
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);
|
||||
break;
|
||||
case Odbgloc:
|
||||
emitdbgloc(i.arg[0].val, i.arg[1].val, f);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
framesz(Fn *fn)
|
||||
static void
|
||||
sysv_framesz(E *e)
|
||||
{
|
||||
uint64_t i, o, f;
|
||||
|
||||
/* specific to NAlign == 3 */
|
||||
for (i=0, o=0; i<NCLR; i++)
|
||||
o ^= 1 & (fn->reg >> amd64_sysv_rclob[i]);
|
||||
f = fn->slot;
|
||||
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;
|
||||
f = (f + 3) & -4;
|
||||
return 4*f + 8*o + 176*fn->vararg;
|
||||
if (f > 0
|
||||
&& e->fp == RSP
|
||||
&& e->fn->salign == 4)
|
||||
f += 2;
|
||||
e->fsz = 4*f + 8*o + 176*e->fn->vararg;
|
||||
}
|
||||
|
||||
void
|
||||
amd64_emitfn(Fn *fn, FILE *f)
|
||||
amd64_sysv_emitfn(Fn *fn, FILE *f)
|
||||
{
|
||||
static char *ctoa[] = {
|
||||
#define X(c, s) [c] = s,
|
||||
#define X(c, s, _) [c] = s,
|
||||
CMP(X)
|
||||
#undef X
|
||||
};
|
||||
@ -578,13 +636,20 @@ amd64_emitfn(Fn *fn, FILE *f)
|
||||
Blk *b, *s;
|
||||
Ins *i, itmp;
|
||||
int *r, c, o, n, lbl;
|
||||
uint64_t fs;
|
||||
uint p;
|
||||
E *e;
|
||||
|
||||
e = &(E){.f = f, .fn = fn};
|
||||
emitfnlnk(fn->name, &fn->lnk, f);
|
||||
fputs("\tpushq %rbp\n\tmovq %rsp, %rbp\n", f);
|
||||
fs = framesz(fn);
|
||||
if (fs)
|
||||
fprintf(f, "\tsubq $%"PRIu64", %%rsp\n", fs);
|
||||
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);
|
||||
if (fn->vararg) {
|
||||
o = -176;
|
||||
for (r=amd64_sysv_rsave; r<&amd64_sysv_rsave[6]; r++, o+=8)
|
||||
@ -592,18 +657,24 @@ amd64_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]; r++)
|
||||
for (r=amd64_sysv_rclob; r<&amd64_sysv_rclob[NCLR_SYSV]; r++)
|
||||
if (fn->reg & BIT(*r)) {
|
||||
itmp.arg[0] = TMP(*r);
|
||||
emitf("pushq %L0", &itmp, fn, f);
|
||||
fs += 8;
|
||||
emitf("pushq %L0", &itmp, e);
|
||||
e->nclob++;
|
||||
}
|
||||
|
||||
for (lbl=0, b=fn->start; b; b=b->link) {
|
||||
if (lbl || b->npred > 1)
|
||||
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");
|
||||
fprintf(f, "%sbb%d:\n", T.asloc, id0+b->id);
|
||||
}
|
||||
for (i=b->ins; i!=&b->ins[b->nins]; i++)
|
||||
emitins(*i, fn, f);
|
||||
emitins(*i, e);
|
||||
lbl = 1;
|
||||
switch (b->jmp.type) {
|
||||
case Jhlt:
|
||||
@ -614,17 +685,19 @@ amd64_emitfn(Fn *fn, FILE *f)
|
||||
fprintf(f,
|
||||
"\tmovq %%rbp, %%rsp\n"
|
||||
"\tsubq $%"PRIu64", %%rsp\n",
|
||||
fs
|
||||
);
|
||||
for (r=&amd64_sysv_rclob[NCLR]; r>amd64_sysv_rclob;)
|
||||
e->fsz + e->nclob * 8);
|
||||
for (r=&amd64_sysv_rclob[NCLR_SYSV]; r>amd64_sysv_rclob;)
|
||||
if (fn->reg & BIT(*--r)) {
|
||||
itmp.arg[0] = TMP(*r);
|
||||
emitf("popq %L0", &itmp, fn, f);
|
||||
emitf("popq %L0", &itmp, e);
|
||||
}
|
||||
fprintf(f,
|
||||
"\tleave\n"
|
||||
"\tret\n"
|
||||
);
|
||||
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:
|
||||
@ -654,3 +727,118 @@ amd64_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;
|
||||
}
|
||||
|
||||
462
amd64/isel.c
462
amd64/isel.c
@ -18,14 +18,7 @@
|
||||
* dce should be moved out...
|
||||
*/
|
||||
|
||||
typedef struct ANum ANum;
|
||||
|
||||
struct ANum {
|
||||
char n, l, r;
|
||||
Ins *i;
|
||||
};
|
||||
|
||||
static int amatch(Addr *, Ref, int, ANum *, Fn *);
|
||||
static int amatch(Addr *, Num *, Ref, Fn *);
|
||||
|
||||
static int
|
||||
noimm(Ref r, Fn *fn)
|
||||
@ -94,11 +87,23 @@ 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, KWIDE(k) ? 8 : 4);
|
||||
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
|
||||
*/
|
||||
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
|
||||
@ -166,11 +171,15 @@ 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, ANum *an, Fn *fn)
|
||||
seladdr(Ref *r, Num *tn, Fn *fn)
|
||||
{
|
||||
Addr a;
|
||||
Ref r0;
|
||||
@ -178,7 +187,7 @@ seladdr(Ref *r, ANum *an, Fn *fn)
|
||||
r0 = *r;
|
||||
if (rtype(r0) == RTmp) {
|
||||
memset(&a, 0, sizeof a);
|
||||
if (!amatch(&a, r0, an[r0.val].n, an, fn))
|
||||
if (!amatch(&a, tn, r0, fn))
|
||||
return;
|
||||
if (!req(a.base, R))
|
||||
if (a.offset.type == CAddr) {
|
||||
@ -242,7 +251,7 @@ selcmp(Ref arg[2], int k, int swap, Fn *fn)
|
||||
}
|
||||
|
||||
static void
|
||||
sel(Ins i, ANum *an, Fn *fn)
|
||||
sel(Ins i, Num *tn, Fn *fn)
|
||||
{
|
||||
Ref r0, r1, tmp[7];
|
||||
int x, j, k, kc, sh, swap;
|
||||
@ -404,10 +413,10 @@ sel(Ins i, ANum *an, Fn *fn)
|
||||
if (i.op == Ostores)
|
||||
i.op = Ostorew;
|
||||
}
|
||||
seladdr(&i.arg[1], an, fn);
|
||||
seladdr(&i.arg[1], tn, fn);
|
||||
goto Emit;
|
||||
case_Oload:
|
||||
seladdr(&i.arg[0], an, fn);
|
||||
seladdr(&i.arg[0], tn, fn);
|
||||
goto Emit;
|
||||
case Odbgloc:
|
||||
case Ocall:
|
||||
@ -428,7 +437,8 @@ sel(Ins i, ANum *an, Fn *fn)
|
||||
case Oexts:
|
||||
case Otruncd:
|
||||
case Ocast:
|
||||
case_OExt:
|
||||
case_Oxsel:
|
||||
case_Oext:
|
||||
Emit:
|
||||
emiti(i);
|
||||
i1 = curi; /* fixarg() can change curi */
|
||||
@ -442,7 +452,9 @@ Emit:
|
||||
break;
|
||||
default:
|
||||
if (isext(i.op))
|
||||
goto case_OExt;
|
||||
goto case_Oext;
|
||||
if (isxsel(i.op))
|
||||
goto case_Oxsel;
|
||||
if (isload(i.op))
|
||||
goto case_Oload;
|
||||
if (iscmp(i.op, &kc, &x)) {
|
||||
@ -496,6 +508,88 @@ 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)
|
||||
{
|
||||
@ -525,7 +619,7 @@ seljmp(Blk *b, Fn *fn)
|
||||
b->jmp.type = Jjf + Cine;
|
||||
}
|
||||
else if (iscmp(fi->op, &k, &c)
|
||||
&& c != NCmpI+Cfeq /* see sel() */
|
||||
&& c != NCmpI+Cfeq /* see sel(), selsel() */
|
||||
&& c != NCmpI+Cfne) {
|
||||
swap = cmpswap(fi->arg, c);
|
||||
if (swap)
|
||||
@ -559,155 +653,225 @@ 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
|
||||
aref(Ref r, ANum *ai)
|
||||
opn(int op, int l, int r)
|
||||
{
|
||||
switch (rtype(r)) {
|
||||
case RCon:
|
||||
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;
|
||||
}
|
||||
return 2;
|
||||
case RTmp:
|
||||
return ai[r.val].n;
|
||||
case Oadd:
|
||||
return Oaddtbl[(l + l*l)/2 + r];
|
||||
default:
|
||||
die("constant or temporary expected");
|
||||
return 2;
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ascale(Ref r, Con *con)
|
||||
refn(Ref r, Num *tn, Con *con)
|
||||
{
|
||||
int64_t n;
|
||||
|
||||
if (rtype(r) != RCon)
|
||||
return 0;
|
||||
if (con[r.val].type != CBits)
|
||||
return 0;
|
||||
n = con[r.val].bits.i;
|
||||
return n == 1 || n == 2 || n == 4 || n == 8;
|
||||
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)
|
||||
return 0;
|
||||
return 1;
|
||||
default:
|
||||
return INT_MIN;
|
||||
}
|
||||
}
|
||||
|
||||
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(ANum *ai, Blk *b, Con *con)
|
||||
anumber(Num *tn, 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)
|
||||
ai[i->to.val].i = i;
|
||||
if (i->op != Oadd && i->op != Omul)
|
||||
if (rtype(i->to) != RTmp)
|
||||
continue;
|
||||
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;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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, Ref r, int n, ANum *ai, Fn *fn)
|
||||
amatch(Addr *a, Num *tn, Ref r, Fn *fn)
|
||||
{
|
||||
Ins *i;
|
||||
int nl, nr, t, s;
|
||||
Ref al, ar;
|
||||
static int pat[] = {Pobis, Pobi1, Pbis, Pois, Pbi1, -1};
|
||||
Ref ro, rb, ri, rs, v[4];
|
||||
Con *c, co;
|
||||
int s, n, *p;
|
||||
|
||||
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;
|
||||
if (rtype(r) != RTmp)
|
||||
return 0;
|
||||
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);
|
||||
|
||||
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);
|
||||
break;
|
||||
}
|
||||
r = al;
|
||||
/* fall through */
|
||||
case 0:
|
||||
s = fn->tmp[r.val].slot;
|
||||
if (s != -1)
|
||||
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");
|
||||
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;
|
||||
if (s != -1)
|
||||
a->base = SLOT(s);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* instruction selection
|
||||
@ -722,7 +886,7 @@ amd64_isel(Fn *fn)
|
||||
uint a;
|
||||
int n, al;
|
||||
int64_t sz;
|
||||
ANum *ainfo;
|
||||
Num *num;
|
||||
|
||||
/* assign slots to fast allocs */
|
||||
b = fn->start;
|
||||
@ -741,12 +905,13 @@ 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;
|
||||
ainfo = emalloc(n * sizeof ainfo[0]);
|
||||
num = emalloc(n * sizeof num[0]);
|
||||
for (b=fn->start; b; b=b->link) {
|
||||
curi = &insb[NIns];
|
||||
for (sb=(Blk*[3]){b->s1, b->s2, 0}; *sb; sb++)
|
||||
@ -755,15 +920,20 @@ amd64_isel(Fn *fn)
|
||||
assert(a+1 < p->narg);
|
||||
fixarg(&p->arg[a], p->cls, 0, fn);
|
||||
}
|
||||
memset(ainfo, 0, n * sizeof ainfo[0]);
|
||||
anumber(ainfo, b, fn->con);
|
||||
memset(num, 0, n * sizeof num[0]);
|
||||
anumber(num, b, fn->con);
|
||||
seljmp(b, 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);
|
||||
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);
|
||||
}
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
free(ainfo);
|
||||
free(num);
|
||||
|
||||
if (debug['I']) {
|
||||
fprintf(stderr, "\n> After instruction selection:\n");
|
||||
|
||||
27
amd64/sysv.c
27
amd64/sysv.c
@ -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+NFPS+1) * sizeof(int) &&
|
||||
sizeof amd64_sysv_rclob == (NCLR+1) * sizeof(int)
|
||||
sizeof amd64_sysv_rsave == (NGPS_SYSV+NFPS+1) * sizeof(int) &&
|
||||
sizeof amd64_sysv_rclob == (NCLR_SYSV+1) * sizeof(int)
|
||||
);
|
||||
|
||||
/* layout of call's second argument (RCall)
|
||||
@ -508,8 +508,7 @@ split(Fn *fn, Blk *b)
|
||||
|
||||
++fn->nblk;
|
||||
bn = newblk();
|
||||
bn->nins = &insb[NIns] - curi;
|
||||
idup(&bn->ins, curi, bn->nins);
|
||||
idup(bn, curi, &insb[NIns]-curi);
|
||||
curi = &insb[NIns];
|
||||
bn->visit = ++b->visit;
|
||||
strf(bn->name, "%s.%d", b->name, b->visit);
|
||||
@ -657,9 +656,9 @@ void
|
||||
amd64_sysv_abi(Fn *fn)
|
||||
{
|
||||
Blk *b;
|
||||
Ins *i, *i0, *ip;
|
||||
Ins *i, *i0;
|
||||
RAlloc *ral;
|
||||
int n, fa;
|
||||
int n0, n1, ioff, fa;
|
||||
|
||||
for (b=fn->start; b; b=b->link)
|
||||
b->visit = 0;
|
||||
@ -669,12 +668,13 @@ amd64_sysv_abi(Fn *fn)
|
||||
if (!ispar(i->op))
|
||||
break;
|
||||
fa = selpar(fn, b->ins, i);
|
||||
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;
|
||||
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;
|
||||
|
||||
/* lower calls, returns, and vararg instructions */
|
||||
ral = 0;
|
||||
@ -711,8 +711,7 @@ amd64_sysv_abi(Fn *fn)
|
||||
if (b == fn->start)
|
||||
for (; ral; ral=ral->link)
|
||||
emiti(ral->i);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
} while (b != fn->start);
|
||||
|
||||
if (debug['A']) {
|
||||
|
||||
33
amd64/targ.c
33
amd64/targ.c
@ -19,20 +19,21 @@ 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, \
|
||||
.emitfn = amd64_emitfn, \
|
||||
.cansel = 1,
|
||||
|
||||
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
|
||||
};
|
||||
|
||||
@ -42,5 +43,25 @@ 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
|
||||
};
|
||||
|
||||
763
amd64/winabi.c
Executable file
763
amd64/winabi.c
Executable file
@ -0,0 +1,763 @@
|
||||
#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(®_usage, &ret_arg_class, /*is_float=*/false,
|
||||
/*by_copy=*/true);
|
||||
}
|
||||
ret_arg_class.size = ret_type->size;
|
||||
}
|
||||
Ref env = R;
|
||||
classify_arguments(®_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(®_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(®_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, ¶m_reg_usage, &extra_alloc);
|
||||
}
|
||||
lower_args_for_block(func, func->start, ¶m_reg_usage, &extra_alloc);
|
||||
|
||||
if (debug['A']) {
|
||||
fprintf(stderr, "\n> After ABI lowering:\n");
|
||||
printfn(func, stderr);
|
||||
}
|
||||
}
|
||||
64
arm64/abi.c
64
arm64/abi.c
@ -90,7 +90,7 @@ isfloatv(Typ *t, char *cls)
|
||||
static void
|
||||
typclass(Class *c, Typ *t, int *gp, int *fp)
|
||||
{
|
||||
uint64_t sz;
|
||||
uint64_t sz, hfasz;
|
||||
uint n;
|
||||
|
||||
sz = (t->size + 7) & -8;
|
||||
@ -103,7 +103,21 @@ typclass(Class *c, Typ *t, int *gp, int *fp)
|
||||
if (t->align > 3)
|
||||
err("alignments larger than 8 are not supported");
|
||||
|
||||
if (t->isdark || sz > 16 || sz == 0) {
|
||||
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) {
|
||||
/* large structs are replaced by a
|
||||
* pointer to some caller-allocated
|
||||
* memory */
|
||||
@ -112,26 +126,14 @@ typclass(Class *c, Typ *t, int *gp, int *fp)
|
||||
c->ngp = 1;
|
||||
*c->reg = *gp;
|
||||
*c->cls = Kl;
|
||||
return;
|
||||
}
|
||||
|
||||
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
|
||||
else {
|
||||
for (n=0; n<sz/8; n++, c->ngp++) {
|
||||
c->reg[n] = *gp++;
|
||||
c->cls[n] = Kl;
|
||||
}
|
||||
|
||||
c->nreg = n;
|
||||
c->nreg = n;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
@ -427,7 +429,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)
|
||||
if (i->op == Oarg || i->op == Oarge || isargbh(i->op))
|
||||
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);
|
||||
@ -543,8 +545,7 @@ split(Fn *fn, Blk *b)
|
||||
|
||||
++fn->nblk;
|
||||
bn = newblk();
|
||||
bn->nins = &insb[NIns] - curi;
|
||||
idup(&bn->ins, curi, bn->nins);
|
||||
idup(bn, curi, &insb[NIns]-curi);
|
||||
curi = &insb[NIns];
|
||||
bn->visit = ++b->visit;
|
||||
strf(bn->name, "%s.%d", b->name, b->visit);
|
||||
@ -728,9 +729,9 @@ void
|
||||
arm64_abi(Fn *fn)
|
||||
{
|
||||
Blk *b;
|
||||
Ins *i, *i0, *ip;
|
||||
Ins *i, *i0;
|
||||
Insl *il;
|
||||
int n;
|
||||
int n0, n1, ioff;
|
||||
Params p;
|
||||
|
||||
for (b=fn->start; b; b=b->link)
|
||||
@ -741,12 +742,13 @@ arm64_abi(Fn *fn)
|
||||
if (!ispar(i->op))
|
||||
break;
|
||||
p = selpar(fn, b->ins, i);
|
||||
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;
|
||||
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;
|
||||
|
||||
/* lower calls, returns, and vararg instructions */
|
||||
il = 0;
|
||||
@ -789,8 +791,7 @@ arm64_abi(Fn *fn)
|
||||
if (b == fn->start)
|
||||
for (; il; il=il->link)
|
||||
emiti(il->i);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
} while (b != fn->start);
|
||||
|
||||
if (debug['A']) {
|
||||
@ -841,8 +842,7 @@ apple_extsb(Fn *fn)
|
||||
emit(op, Kw, i->to, i->arg[0], R);
|
||||
}
|
||||
}
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
|
||||
if (debug['A']) {
|
||||
|
||||
77
arm64/emit.c
77
arm64/emit.c
@ -37,7 +37,7 @@ enum {
|
||||
static struct {
|
||||
short op;
|
||||
short cls;
|
||||
char *asm;
|
||||
char *fmt;
|
||||
} omap[] = {
|
||||
{ Oadd, Ki, "add %=, %0, %1" },
|
||||
{ Oadd, Ka, "fadd %=, %0, %1" },
|
||||
@ -109,6 +109,10 @@ static struct {
|
||||
{ NOp, 0, 0 }
|
||||
};
|
||||
|
||||
enum {
|
||||
V31 = 0x1fffffff, /* local name for V31 */
|
||||
};
|
||||
|
||||
static char *
|
||||
rname(int r, int k)
|
||||
{
|
||||
@ -132,6 +136,12 @@ 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;
|
||||
@ -160,7 +170,8 @@ emitf(char *s, Ins *i, E *e)
|
||||
Ref r;
|
||||
int k, c;
|
||||
Con *pc;
|
||||
uint n, sp;
|
||||
uint64_t n;
|
||||
uint sp;
|
||||
|
||||
fputc('\t', e->f);
|
||||
|
||||
@ -171,7 +182,7 @@ emitf(char *s, Ins *i, E *e)
|
||||
if (c == ' ' && !sp) {
|
||||
fputc('\t', e->f);
|
||||
sp = 1;
|
||||
} else if ( !c) {
|
||||
} else if (!c) {
|
||||
fputc('\n', e->f);
|
||||
return;
|
||||
} else
|
||||
@ -194,14 +205,14 @@ emitf(char *s, Ins *i, E *e)
|
||||
goto Switch;
|
||||
case '?':
|
||||
if (KBASE(k) == 0)
|
||||
fputs(rname(R18, k), e->f);
|
||||
fputs(rname(IP1, k), e->f);
|
||||
else
|
||||
fputs(k==Ks ? "s31" : "d31", e->f);
|
||||
fputs(rname(V31, k), e->f);
|
||||
break;
|
||||
case '=':
|
||||
case '0':
|
||||
r = c == '=' ? i->to : i->arg[0];
|
||||
assert(isreg(r));
|
||||
assert(isreg(r) || req(r, TMP(V31)));
|
||||
fputs(rname(r.val, k), e->f);
|
||||
break;
|
||||
case '1':
|
||||
@ -217,10 +228,17 @@ emitf(char *s, Ins *i, E *e)
|
||||
pc = &e->fn->con[r.val];
|
||||
n = pc->bits.i;
|
||||
assert(pc->type == CBits);
|
||||
if (n & 0xfff000)
|
||||
fprintf(e->f, "#%u, lsl #12", n>>12);
|
||||
else
|
||||
fprintf(e->f, "#%u", n);
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
@ -304,6 +322,7 @@ 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;
|
||||
}
|
||||
@ -326,8 +345,8 @@ loadcon(Con *c, int r, int k, E *e)
|
||||
|
||||
static void emitins(Ins *, E *);
|
||||
|
||||
static void
|
||||
fixarg(Ref *pr, int sz, E *e)
|
||||
static int
|
||||
fixarg(Ref *pr, int sz, int t, E *e)
|
||||
{
|
||||
Ins *i;
|
||||
Ref r;
|
||||
@ -337,11 +356,14 @@ fixarg(Ref *pr, int sz, E *e)
|
||||
if (rtype(r) == RSlot) {
|
||||
s = slot(r, e);
|
||||
if (s > sz * 4095u) {
|
||||
i = &(Ins){Oaddr, Kl, TMP(IP0), {r}};
|
||||
if (t < 0)
|
||||
return 1;
|
||||
i = &(Ins){Oaddr, Kl, TMP(t), {r}};
|
||||
emitins(i, e);
|
||||
*pr = TMP(IP0);
|
||||
*pr = TMP(t);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void
|
||||
@ -349,16 +371,28 @@ emitins(Ins *i, E *e)
|
||||
{
|
||||
char *l, *p, *rn;
|
||||
uint64_t s;
|
||||
int o;
|
||||
int o, t;
|
||||
Ref r;
|
||||
Con *c;
|
||||
|
||||
switch (i->op) {
|
||||
default:
|
||||
if (isload(i->op))
|
||||
fixarg(&i->arg[0], loadsz(i), e);
|
||||
if (isstore(i->op))
|
||||
fixarg(&i->arg[1], storesz(i), e);
|
||||
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);
|
||||
}
|
||||
}
|
||||
Table:
|
||||
/* most instructions are just pulled out of
|
||||
* the table omap[], some special cases are
|
||||
@ -374,7 +408,7 @@ emitins(Ins *i, E *e)
|
||||
|| (omap[o].cls == Ki && KBASE(i->cls) == 0))
|
||||
break;
|
||||
}
|
||||
emitf(omap[o].asm, i, e);
|
||||
emitf(omap[o].fmt, i, e);
|
||||
break;
|
||||
case Onop:
|
||||
break;
|
||||
@ -384,7 +418,7 @@ emitins(Ins *i, E *e)
|
||||
if (rtype(i->to) == RSlot) {
|
||||
r = i->to;
|
||||
if (!isreg(i->arg[0])) {
|
||||
i->to = TMP(R18);
|
||||
i->to = TMP(IP1);
|
||||
emitins(i, e);
|
||||
i->arg[0] = i->to;
|
||||
}
|
||||
@ -405,7 +439,7 @@ emitins(Ins *i, E *e)
|
||||
emitins(i, e);
|
||||
break;
|
||||
default:
|
||||
assert(i->to.val != R18);
|
||||
assert(i->to.val != IP1);
|
||||
goto Table;
|
||||
}
|
||||
break;
|
||||
@ -514,6 +548,7 @@ 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) {
|
||||
|
||||
@ -24,7 +24,7 @@ imm(Con *c, int k, int64_t *pn)
|
||||
i = Iplo12;
|
||||
if (n < 0) {
|
||||
i = Inlo12;
|
||||
n = -n;
|
||||
n = -(uint64_t)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, KWIDE(k) ? 8 : 4);
|
||||
n = stashbits(c->bits.i, KWIDE(k) ? 8 : 4);
|
||||
vgrow(&fn->con, ++fn->ncon);
|
||||
c = &fn->con[fn->ncon-1];
|
||||
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
|
||||
@ -306,8 +306,7 @@ arm64_isel(Fn *fn)
|
||||
seljmp(b, fn);
|
||||
for (i=&b->ins[b->nins]; i!=b->ins;)
|
||||
sel(*--i, fn);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
|
||||
if (debug['I']) {
|
||||
|
||||
@ -16,7 +16,7 @@ int arm64_rclob[] = {
|
||||
-1
|
||||
};
|
||||
|
||||
#define RGLOB (BIT(FP) | BIT(SP) | BIT(R18))
|
||||
#define RGLOB (BIT(FP) | BIT(SP) | BIT(IP1) | BIT(R18))
|
||||
|
||||
static int
|
||||
arm64_memargs(int op)
|
||||
@ -31,7 +31,7 @@ arm64_memargs(int op)
|
||||
.fpr0 = V0, \
|
||||
.nfpr = NFPR, \
|
||||
.rglob = RGLOB, \
|
||||
.nrglob = 3, \
|
||||
.nrglob = 4, \
|
||||
.rsave = arm64_rsave, \
|
||||
.nrsave = {NGPS, NFPS}, \
|
||||
.retregs = arm64_retregs, \
|
||||
@ -40,6 +40,7 @@ arm64_memargs(int op)
|
||||
.isel = arm64_isel, \
|
||||
.abi1 = arm64_abi, \
|
||||
.emitfn = arm64_emitfn, \
|
||||
.cansel = 0, \
|
||||
|
||||
Target T_arm64 = {
|
||||
.name = "arm64",
|
||||
|
||||
354
cfg.c
354
cfg.c
@ -8,66 +8,50 @@ 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;
|
||||
}
|
||||
|
||||
void
|
||||
edgedel(Blk *bs, Blk **pbd)
|
||||
static void
|
||||
fixphis(Fn *f)
|
||||
{
|
||||
Blk *bd;
|
||||
Blk *b, *bp;
|
||||
Phi *p;
|
||||
uint a;
|
||||
int mult;
|
||||
uint n, 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));
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
addpred(Blk *bp, Blk *bc)
|
||||
addpred(Blk *bp, Blk *b)
|
||||
{
|
||||
if (!bc->pred) {
|
||||
bc->pred = alloc(bc->npred * sizeof bc->pred[0]);
|
||||
bc->visit = 0;
|
||||
}
|
||||
bc->pred[bc->visit++] = bp;
|
||||
vgrow(&b->pred, ++b->npred);
|
||||
b->pred[b->npred-1] = 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);
|
||||
@ -76,52 +60,55 @@ fillpreds(Fn *f)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
rporec(Blk *b, uint x)
|
||||
static void
|
||||
porec(Blk *b, uint *npo)
|
||||
{
|
||||
Blk *s1, *s2;
|
||||
|
||||
if (!b || b->id != -1u)
|
||||
return x;
|
||||
b->id = 1;
|
||||
return;
|
||||
b->id = 0; /* marker */
|
||||
s1 = b->s1;
|
||||
s2 = b->s2;
|
||||
if (s1 && s2 && s1->loop > s2->loop) {
|
||||
s1 = b->s2;
|
||||
s2 = b->s1;
|
||||
}
|
||||
x = rporec(s1, x);
|
||||
x = rporec(s2, x);
|
||||
b->id = x;
|
||||
assert(x != -1u);
|
||||
return x - 1;
|
||||
porec(s1, npo);
|
||||
porec(s2, npo);
|
||||
b->id = (*npo)++;
|
||||
}
|
||||
|
||||
/* fill the rpo information */
|
||||
void
|
||||
static void
|
||||
fillrpo(Fn *f)
|
||||
{
|
||||
uint n;
|
||||
Blk *b, **p;
|
||||
|
||||
for (b=f->start; b; b=b->link)
|
||||
b->id = -1u;
|
||||
n = 1 + rporec(f->start, f->nblk-1);
|
||||
f->nblk -= n;
|
||||
f->rpo = alloc(f->nblk * sizeof f->rpo[0]);
|
||||
f->nblk = 0;
|
||||
porec(f->start, &f->nblk);
|
||||
vgrow(&f->rpo, f->nblk);
|
||||
for (p=&f->start; (b=*p);) {
|
||||
if (b->id == -1u) {
|
||||
edgedel(b, &b->s1);
|
||||
edgedel(b, &b->s2);
|
||||
*p = b->link;
|
||||
} else {
|
||||
b->id -= n;
|
||||
b->id = f->nblk-b->id-1;
|
||||
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.
|
||||
@ -262,6 +249,50 @@ 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)
|
||||
{
|
||||
@ -329,3 +360,208 @@ 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);
|
||||
}
|
||||
}
|
||||
|
||||
577
copy.c
577
copy.c
@ -1,16 +1,272 @@
|
||||
#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
|
||||
iscon(Ref r, int64_t bits, Fn *fn)
|
||||
ext(Ins *i, Ext *e)
|
||||
{
|
||||
return rtype(r) == RCon
|
||||
&& fn->con[r.val].type == CBits
|
||||
&& fn->con[r.val].bits.i == bits;
|
||||
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;
|
||||
}
|
||||
|
||||
static int
|
||||
iscopy(Ins *i, Ref r, Fn *fn)
|
||||
bitwidth(uint64_t v)
|
||||
{
|
||||
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),
|
||||
@ -20,210 +276,133 @@ iscopy(Ins *i, Ref r, Fn *fn)
|
||||
[Wsw] = BIT(Wsw),
|
||||
[Wuw] = BIT(Wuw),
|
||||
};
|
||||
bits b;
|
||||
Ext e;
|
||||
Tmp *t;
|
||||
int64_t v;
|
||||
int w, z;
|
||||
|
||||
switch (i->op) {
|
||||
case Ocopy:
|
||||
return 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 (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];
|
||||
}
|
||||
if (!isext(i->op) || rtype(r) != RTmp)
|
||||
return 0;
|
||||
if (i->op == Oextsw || i->op == Oextuw)
|
||||
if (i->cls == Kw)
|
||||
return 1;
|
||||
|
||||
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;
|
||||
return R;
|
||||
}
|
||||
|
||||
static Ref
|
||||
copyof(Ref r, Ref *cpy)
|
||||
static int
|
||||
phieq(Phi *pa, Phi *pb)
|
||||
{
|
||||
if (rtype(r) == RTmp && !req(cpy[r.val], R))
|
||||
return cpy[r.val];
|
||||
return r;
|
||||
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;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
Ref
|
||||
phicopyref(Fn *fn, Blk *b, Phi *p)
|
||||
{
|
||||
Use **stk, *u, *u1;
|
||||
uint nstk, a;
|
||||
int t;
|
||||
Ref r1;
|
||||
Phi *p0;
|
||||
Blk *d, **s;
|
||||
Phi *p1;
|
||||
uint n, c;
|
||||
|
||||
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;
|
||||
/* identical args */
|
||||
for (n=0; n<p->narg-1; n++)
|
||||
if (!req(p->arg[n], p->arg[n+1]))
|
||||
break;
|
||||
if (n == p->narg-1)
|
||||
return p->arg[n];
|
||||
|
||||
/* same as a previous phi */
|
||||
for (p1=b->phi; p1!=p; p1=p1->link) {
|
||||
assert(p1);
|
||||
if (phieq(p1, p))
|
||||
return p1->to;
|
||||
}
|
||||
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);
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -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
|
||||
The parameter list is a comma separated list of distinct
|
||||
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,6 +976,7 @@ 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
|
||||
|
||||
|
||||
15
doc/native_win.txt
Normal file
15
doc/native_win.txt
Normal file
@ -0,0 +1,15 @@
|
||||
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
79
emit.c
@ -61,11 +61,14 @@ emitfnlnk(char *n, Lnk *l, FILE *f)
|
||||
void
|
||||
emitdat(Dat *d, FILE *f)
|
||||
{
|
||||
static char *dtoa[] = {
|
||||
[DB] = "\t.byte",
|
||||
[DH] = "\t.short",
|
||||
[DW] = "\t.int",
|
||||
[DL] = "\t.quad"
|
||||
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 int64_t zero;
|
||||
char *p;
|
||||
@ -75,7 +78,17 @@ emitdat(Dat *d, FILE *f)
|
||||
zero = 0;
|
||||
break;
|
||||
case DEnd:
|
||||
if (zero != -1) {
|
||||
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) {
|
||||
emitlnk(d->name, d->lnk, SecBss, f);
|
||||
fprintf(f, "\t.fill %"PRId64",1,0\n", zero);
|
||||
}
|
||||
@ -101,12 +114,13 @@ emitdat(Dat *d, FILE *f)
|
||||
else if (d->isref) {
|
||||
p = d->u.ref.name[0] == '"' ? "" : T.assym;
|
||||
fprintf(f, "%s %s%s%+"PRId64"\n",
|
||||
dtoa[d->type], p, d->u.ref.name,
|
||||
di[d->type].decl, p, d->u.ref.name,
|
||||
d->u.ref.off);
|
||||
}
|
||||
else {
|
||||
fprintf(f, "%s %"PRId64"\n",
|
||||
dtoa[d->type], d->u.num);
|
||||
di[d->type].decl,
|
||||
d->u.num & di[d->type].mask);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@ -115,7 +129,7 @@ emitdat(Dat *d, FILE *f)
|
||||
typedef struct Asmbits Asmbits;
|
||||
|
||||
struct Asmbits {
|
||||
char bits[16];
|
||||
bits n;
|
||||
int size;
|
||||
Asmbits *link;
|
||||
};
|
||||
@ -123,18 +137,17 @@ struct Asmbits {
|
||||
static Asmbits *stash;
|
||||
|
||||
int
|
||||
stashbits(void *bits, int size)
|
||||
stashbits(bits n, 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)
|
||||
if (memcmp(bits, b->bits, size) == 0)
|
||||
if (size <= b->size && b->n == n)
|
||||
return i;
|
||||
b = emalloc(sizeof *b);
|
||||
memcpy(b->bits, bits, size);
|
||||
b->n = n;
|
||||
b->size = size;
|
||||
b->link = 0;
|
||||
*pb = b;
|
||||
@ -145,9 +158,8 @@ static void
|
||||
emitfin(FILE *f, char *sec[3])
|
||||
{
|
||||
Asmbits *b;
|
||||
char *p;
|
||||
int lg, i;
|
||||
double d;
|
||||
union { int32_t i; float f; } u;
|
||||
|
||||
if (!stash)
|
||||
return;
|
||||
@ -161,17 +173,24 @@ emitfin(FILE *f, char *sec[3])
|
||||
"%sfp%d:",
|
||||
sec[lg-2], lg, T.asloc, i
|
||||
);
|
||||
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");
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
while ((b=stash)) {
|
||||
@ -208,6 +227,14 @@ 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
349
fold.c
@ -1,22 +1,6 @@
|
||||
#include "all.h"
|
||||
|
||||
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;
|
||||
/* boring folding code */
|
||||
|
||||
static int
|
||||
iscon(Con *c, int w, uint64_t k)
|
||||
@ -29,305 +13,7 @@ iscon(Con *c, int w, uint64_t k)
|
||||
return (uint32_t)c->bits.i == (uint32_t)k;
|
||||
}
|
||||
|
||||
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
|
||||
int
|
||||
foldint(Con *res, int op, int w, Con *cl, Con *cr)
|
||||
{
|
||||
union {
|
||||
@ -516,7 +202,7 @@ foldflt(Con *res, int op, int w, Con *cl, Con *cr)
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
static Ref
|
||||
opfold(int op, int cls, Con *cl, Con *cr, Fn *fn)
|
||||
{
|
||||
Ref r;
|
||||
@ -524,12 +210,37 @@ 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 Bot;
|
||||
return R;
|
||||
} 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.val;
|
||||
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;
|
||||
}
|
||||
|
||||
460
gcm.c
Normal file
460
gcm.c
Normal file
@ -0,0 +1,460 @@
|
||||
#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
Normal file
508
gvn.c
Normal file
@ -0,0 +1,508 @@
|
||||
#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
Normal file
121
ifopt.c
Normal file
@ -0,0 +1,121 @@
|
||||
#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
4
load.c
@ -380,6 +380,7 @@ 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);
|
||||
@ -481,8 +482,7 @@ loadopt(Fn *fn)
|
||||
vgrow(&ib, ++nt);
|
||||
ib[nt-1] = *i;
|
||||
}
|
||||
b->nins = nt;
|
||||
idup(&b->ins, ib, nt);
|
||||
idup(b, ib, nt);
|
||||
}
|
||||
vfree(ib);
|
||||
vfree(ilog);
|
||||
|
||||
32
main.c
32
main.c
@ -11,6 +11,7 @@ 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 */
|
||||
@ -20,6 +21,7 @@ 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;
|
||||
@ -27,6 +29,7 @@ extern Target T_rv64;
|
||||
static Target *tlist[] = {
|
||||
&T_amd64_sysv,
|
||||
&T_amd64_apple,
|
||||
&T_amd64_win,
|
||||
&T_arm64,
|
||||
&T_arm64_apple,
|
||||
&T_rv64,
|
||||
@ -59,8 +62,7 @@ func(Fn *fn)
|
||||
printfn(fn, stderr);
|
||||
}
|
||||
T.abi0(fn);
|
||||
fillrpo(fn);
|
||||
fillpreds(fn);
|
||||
fillcfg(fn);
|
||||
filluse(fn);
|
||||
promote(fn);
|
||||
filluse(fn);
|
||||
@ -73,25 +75,37 @@ func(Fn *fn)
|
||||
fillalias(fn);
|
||||
coalesce(fn);
|
||||
filluse(fn);
|
||||
filldom(fn);
|
||||
ssacheck(fn);
|
||||
copy(fn);
|
||||
gvn(fn);
|
||||
fillcfg(fn);
|
||||
simplcfg(fn);
|
||||
filluse(fn);
|
||||
fold(fn);
|
||||
filldom(fn);
|
||||
gcm(fn);
|
||||
filluse(fn);
|
||||
ssacheck(fn);
|
||||
if (T.cansel) {
|
||||
ifconvert(fn);
|
||||
fillcfg(fn);
|
||||
filluse(fn);
|
||||
filldom(fn);
|
||||
ssacheck(fn);
|
||||
}
|
||||
T.abi1(fn);
|
||||
simpl(fn);
|
||||
fillpreds(fn);
|
||||
fillcfg(fn);
|
||||
filluse(fn);
|
||||
T.isel(fn);
|
||||
fillrpo(fn);
|
||||
fillcfg(fn);
|
||||
filllive(fn);
|
||||
fillloop(fn);
|
||||
fillcost(fn);
|
||||
spill(fn);
|
||||
rega(fn);
|
||||
fillrpo(fn);
|
||||
fillcfg(fn);
|
||||
simpljmp(fn);
|
||||
fillpreds(fn);
|
||||
fillrpo(fn);
|
||||
fillcfg(fn);
|
||||
assert(fn->rpo[0] == fn->start);
|
||||
for (n=0;; n++)
|
||||
if (n == fn->nblk-1) {
|
||||
|
||||
15
mem.c
15
mem.c
@ -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,10 +447,15 @@ 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 && off0 < off1) {
|
||||
sz = rsval((i+1)->arg[0]);
|
||||
assert(sz >= 0);
|
||||
(i+1)->arg[0] = INT(-sz);
|
||||
if (s->s == s0->s) {
|
||||
if (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);
|
||||
|
||||
@ -70,7 +70,7 @@ struct Stmt {
|
||||
|
||||
int yylex(void), yyerror(char *);
|
||||
Symb expr(Node *), lval(Node *);
|
||||
void bool(Node *, int, int);
|
||||
void branch(Node *, int, int);
|
||||
|
||||
FILE *of;
|
||||
int line;
|
||||
@ -329,7 +329,7 @@ expr(Node *n)
|
||||
case 'a':
|
||||
l = lbl;
|
||||
lbl += 3;
|
||||
bool(n, l, l+1);
|
||||
branch(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
|
||||
bool(Node *n, int lt, int lf)
|
||||
branch(Node *n, int lt, int lf)
|
||||
{
|
||||
Symb s;
|
||||
int l;
|
||||
@ -483,16 +483,16 @@ bool(Node *n, int lt, int lf)
|
||||
case 'o':
|
||||
l = lbl;
|
||||
lbl += 1;
|
||||
bool(n->l, lt, l);
|
||||
branch(n->l, lt, l);
|
||||
fprintf(of, "@l%d\n", l);
|
||||
bool(n->r, lt, lf);
|
||||
branch(n->r, lt, lf);
|
||||
break;
|
||||
case 'a':
|
||||
l = lbl;
|
||||
lbl += 1;
|
||||
bool(n->l, l, lf);
|
||||
branch(n->l, l, lf);
|
||||
fprintf(of, "@l%d\n", l);
|
||||
bool(n->r, lt, lf);
|
||||
branch(n->r, lt, lf);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -526,7 +526,7 @@ stmt(Stmt *s, int b)
|
||||
case If:
|
||||
l = lbl;
|
||||
lbl += 3;
|
||||
bool(s->p1, l, l+1);
|
||||
branch(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);
|
||||
bool(s->p1, l+1, l+2);
|
||||
branch(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
311
ops.h
@ -6,184 +6,217 @@
|
||||
#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 */
|
||||
/*********************/
|
||||
|
||||
/* 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)
|
||||
/* 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)
|
||||
|
||||
/* Comparisons */
|
||||
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(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(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(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(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(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(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)
|
||||
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)
|
||||
|
||||
/* Memory */
|
||||
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(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(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)
|
||||
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)
|
||||
|
||||
/* Extensions and Truncations */
|
||||
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(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(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)
|
||||
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)
|
||||
|
||||
/* Stack Allocation */
|
||||
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)
|
||||
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)
|
||||
|
||||
/* Variadic Function Helpers */
|
||||
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(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(copy, T(w,l,s,d, x,x,x,x), 0) X(0, 0, 1) 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)
|
||||
|
||||
/* Debug */
|
||||
O(dbgloc, T(w,e,e,e, w,e,e,e), 0) X(0, 0, 1) V(0)
|
||||
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)
|
||||
|
||||
/****************************************/
|
||||
/* INTERNAL OPERATIONS (keep nop first) */
|
||||
/****************************************/
|
||||
|
||||
/* Miscellaneous and Architecture-Specific Operations */
|
||||
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)
|
||||
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)
|
||||
|
||||
/* Arguments, Parameters, and Calls */
|
||||
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)
|
||||
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)
|
||||
|
||||
/* Flags Setting */
|
||||
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)
|
||||
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)
|
||||
|
||||
/* 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
72
parse.c
@ -15,8 +15,17 @@ enum {
|
||||
};
|
||||
|
||||
Op optab[NOp] = {
|
||||
#define O(op, t, cf) [O##op]={#op, t, cf},
|
||||
#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},
|
||||
#include "ops.h"
|
||||
#undef F
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@ -48,6 +57,7 @@ enum Token {
|
||||
Thlt,
|
||||
Texport,
|
||||
Tthread,
|
||||
Tcommon,
|
||||
Tfunc,
|
||||
Ttype,
|
||||
Tdata,
|
||||
@ -106,6 +116,7 @@ static char *kwmap[Ntok] = {
|
||||
[Thlt] = "hlt",
|
||||
[Texport] = "export",
|
||||
[Tthread] = "thread",
|
||||
[Tcommon] = "common",
|
||||
[Tfunc] = "function",
|
||||
[Ttype] = "type",
|
||||
[Tdata] = "data",
|
||||
@ -132,7 +143,7 @@ enum {
|
||||
TMask = 16383, /* for temps hash */
|
||||
BMask = 8191, /* for blocks hash */
|
||||
|
||||
K = 9583425, /* found using tools/lexh.c */
|
||||
K = 11183273, /* found using tools/lexh.c */
|
||||
M = 23,
|
||||
};
|
||||
|
||||
@ -150,7 +161,8 @@ static struct {
|
||||
static int lnum;
|
||||
|
||||
static Fn *curf;
|
||||
static int tmph[TMask+1];
|
||||
static int *tmph;
|
||||
static int tmphcap;
|
||||
static Phi **plink;
|
||||
static Blk *curb;
|
||||
static Blk **blink;
|
||||
@ -203,12 +215,15 @@ getint()
|
||||
n = 0;
|
||||
c = fgetc(inf);
|
||||
m = (c == '-');
|
||||
if (m || c == '+')
|
||||
if (m) {
|
||||
c = fgetc(inf);
|
||||
if (!isdigit(c))
|
||||
err("integer expected");
|
||||
}
|
||||
do {
|
||||
n = 10*n + (c - '0');
|
||||
c = fgetc(inf);
|
||||
} while ('0' <= c && c <= '9');
|
||||
} while (isdigit(c));
|
||||
ungetc(c, inf);
|
||||
if (m)
|
||||
n = 1 + ~n;
|
||||
@ -277,7 +292,7 @@ lex()
|
||||
lnum++;
|
||||
return Tnl;
|
||||
}
|
||||
if (isdigit(c) || c == '-' || c == '+') {
|
||||
if (isdigit(c) || c == '-') {
|
||||
ungetc(c, inf);
|
||||
tokval.num = getint();
|
||||
return Tint;
|
||||
@ -382,19 +397,27 @@ expect(int t)
|
||||
static Ref
|
||||
tmpref(char *v)
|
||||
{
|
||||
int t, *h;
|
||||
int t, i;
|
||||
|
||||
h = &tmph[hash(v) & TMask];
|
||||
t = *h;
|
||||
if (t) {
|
||||
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];
|
||||
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;
|
||||
*h = t;
|
||||
tmph[i] = t;
|
||||
newtmp(0, Kx, curf);
|
||||
strcpy(curf->tmp[t].name, v);
|
||||
return TMP(t);
|
||||
@ -568,8 +591,7 @@ findblk(char *name)
|
||||
static void
|
||||
closeblk()
|
||||
{
|
||||
curb->nins = curi - insb;
|
||||
idup(&curb->ins, insb, curb->nins);
|
||||
idup(curb, insb, curi-insb);
|
||||
blink = &curb->link;
|
||||
curi = insb;
|
||||
}
|
||||
@ -680,6 +702,7 @@ parseline(PState ps)
|
||||
goto Ins;
|
||||
}
|
||||
if (op == Tcall) {
|
||||
curf->leaf = 0;
|
||||
arg[0] = parseref();
|
||||
parserefl(1);
|
||||
op = Ocall;
|
||||
@ -896,6 +919,7 @@ 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)
|
||||
@ -919,12 +943,12 @@ parsefn(Lnk *lnk)
|
||||
curf->mem = vnew(0, sizeof curf->mem[0], PFn);
|
||||
curf->nmem = 0;
|
||||
curf->nblk = nblk;
|
||||
curf->rpo = 0;
|
||||
for (b=0; b; b=b->link)
|
||||
curf->rpo = vnew(nblk, sizeof curf->rpo[0], PFn);
|
||||
for (b=curf->start; b; b=b->link)
|
||||
b->dlink = 0; /* was trashed by findblk() */
|
||||
for (i=0; i<BMask+1; ++i)
|
||||
blkh[i] = 0;
|
||||
memset(tmph, 0, sizeof tmph);
|
||||
memset(tmph, 0, tmphcap * sizeof tmph[0]);
|
||||
typecheck(curf);
|
||||
return curf;
|
||||
}
|
||||
@ -1091,6 +1115,9 @@ 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();
|
||||
}
|
||||
@ -1156,6 +1183,9 @@ 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");
|
||||
@ -1199,6 +1229,7 @@ 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:
|
||||
@ -1294,6 +1325,9 @@ printref(Ref r, Fn *fn, FILE *f)
|
||||
case RInt:
|
||||
fprintf(f, "%d", rsval(r));
|
||||
break;
|
||||
case -1:
|
||||
fprintf(f, "R");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
8
rega.c
8
rega.c
@ -439,8 +439,7 @@ doblk(Blk *b, RMap *cur)
|
||||
* the above loop must be changed */
|
||||
}
|
||||
}
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
|
||||
/* qsort() comparison function to peel
|
||||
@ -671,10 +670,9 @@ rega(Fn *fn)
|
||||
blist = b1;
|
||||
fn->nblk++;
|
||||
strf(b1->name, "%s_%s", b->name, s->name);
|
||||
b1->nins = &insb[NIns] - curi;
|
||||
stmov += b1->nins;
|
||||
stmov += &insb[NIns]-curi;
|
||||
stblk += 1;
|
||||
idup(&b1->ins, curi, b1->nins);
|
||||
idup(b1, curi, &insb[NIns]-curi);
|
||||
b1->jmp.type = Jjmp;
|
||||
b1->s1 = s;
|
||||
**ps = b1;
|
||||
|
||||
20
rv64/abi.c
20
rv64/abi.c
@ -587,9 +587,9 @@ void
|
||||
rv64_abi(Fn *fn)
|
||||
{
|
||||
Blk *b;
|
||||
Ins *i, *i0, *ip;
|
||||
Ins *i, *i0;
|
||||
Insl *il;
|
||||
int n;
|
||||
int n0, n1, ioff;
|
||||
Params p;
|
||||
|
||||
for (b=fn->start; b; b=b->link)
|
||||
@ -600,12 +600,13 @@ rv64_abi(Fn *fn)
|
||||
if (!ispar(i->op))
|
||||
break;
|
||||
p = selpar(fn, b->ins, i);
|
||||
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;
|
||||
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;
|
||||
|
||||
/* lower calls, returns, and vararg instructions */
|
||||
il = 0;
|
||||
@ -642,8 +643,7 @@ rv64_abi(Fn *fn)
|
||||
if (b == fn->start)
|
||||
for (; il; il=il->link)
|
||||
emiti(il->i);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
} while (b != fn->start);
|
||||
|
||||
if (debug['A']) {
|
||||
|
||||
11
rv64/emit.c
11
rv64/emit.c
@ -8,7 +8,7 @@ enum {
|
||||
static struct {
|
||||
short op;
|
||||
short cls;
|
||||
char *asm;
|
||||
char *fmt;
|
||||
} 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].asm, i, fn, f);
|
||||
emitf(omap[o].fmt, 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;
|
||||
Ins *i, ii;
|
||||
|
||||
emitfnlnk(fn->name, &fn->lnk, f);
|
||||
|
||||
@ -549,6 +549,11 @@ 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",
|
||||
|
||||
@ -31,6 +31,7 @@ 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;
|
||||
@ -41,7 +42,7 @@ fixarg(Ref *r, int k, Ins *i, Fn *fn)
|
||||
* immediates
|
||||
*/
|
||||
assert(c->type == CBits);
|
||||
n = stashbits(&c->bits, KWIDE(k) ? 8 : 4);
|
||||
n = stashbits(c->bits.i, KWIDE(k) ? 8 : 4);
|
||||
vgrow(&fn->con, ++fn->ncon);
|
||||
c = &fn->con[fn->ncon-1];
|
||||
sprintf(buf, "\"%sfp%d\"", T.asloc, n);
|
||||
@ -244,8 +245,7 @@ rv64_isel(Fn *fn)
|
||||
seljmp(b, fn);
|
||||
for (i=&b->ins[b->nins]; i!=b->ins;)
|
||||
sel(*--i, fn);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
|
||||
if (debug['I']) {
|
||||
|
||||
@ -50,6 +50,7 @@ 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));
|
||||
|
||||
62
simpl.c
62
simpl.c
@ -30,16 +30,43 @@ 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, mul 2^n into shift,
|
||||
* bit rotations, ... */
|
||||
* copy 0 into xor, bit rotations,
|
||||
* etc. */
|
||||
switch (i->op) {
|
||||
case Oblit1:
|
||||
assert(i > b->ins);
|
||||
@ -53,12 +80,29 @@ ins(Ins **pi, int *new, Blk *b, Fn *fn)
|
||||
}
|
||||
blit((i-1)->arg, rsval(i->arg[0]), fn);
|
||||
*pi = i-1;
|
||||
break;
|
||||
default:
|
||||
if (*new)
|
||||
emiti(*i);
|
||||
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;
|
||||
}
|
||||
if (*new)
|
||||
emiti(*i);
|
||||
}
|
||||
|
||||
void
|
||||
@ -74,9 +118,7 @@ simpl(Fn *fn)
|
||||
--i;
|
||||
ins(&i, &new, b, fn);
|
||||
}
|
||||
if (new) {
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
}
|
||||
if (new)
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
}
|
||||
|
||||
25
spill.c
25
spill.c
@ -406,26 +406,20 @@ 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--;
|
||||
@ -517,8 +511,7 @@ spill(Fn *fn)
|
||||
p->to = slot(p->to.val);
|
||||
}
|
||||
bscopy(b->in, v);
|
||||
b->nins = &insb[NIns] - curi;
|
||||
idup(&b->ins, curi, b->nins);
|
||||
idup(b, curi, &insb[NIns]-curi);
|
||||
}
|
||||
|
||||
/* align the locals to a 16 byte boundary */
|
||||
|
||||
9
ssa.c
9
ssa.c
@ -1,7 +1,7 @@
|
||||
#include "all.h"
|
||||
#include <stdarg.h>
|
||||
|
||||
static void
|
||||
void
|
||||
adduse(Tmp *tmp, int ty, Blk *b, ...)
|
||||
{
|
||||
Use *u;
|
||||
@ -40,11 +40,10 @@ filluse(Fn *fn)
|
||||
Blk *b;
|
||||
Phi *p;
|
||||
Ins *i;
|
||||
int m, t, tp, w;
|
||||
int m, t, tp, w, x;
|
||||
uint a;
|
||||
Tmp *tmp;
|
||||
|
||||
/* todo, is this the correct file? */
|
||||
tmp = fn->tmp;
|
||||
for (t=Tmp0; t<fn->ntmp; t++) {
|
||||
tmp[t].def = 0;
|
||||
@ -84,6 +83,8 @@ 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;
|
||||
@ -143,7 +144,7 @@ phiins(Fn *fn)
|
||||
continue;
|
||||
}
|
||||
bszero(u);
|
||||
k = -1;
|
||||
k = Kx;
|
||||
bp = be;
|
||||
for (b=fn->start; b; b=b->link) {
|
||||
b->visit = 0;
|
||||
|
||||
48
test/_gcm1.ssa
Normal file
48
test/_gcm1.ssa
Normal file
@ -0,0 +1,48 @@
|
||||
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
|
||||
}
|
||||
43
test/_gcm2.ssa
Normal file
43
test/_gcm2.ssa
Normal file
@ -0,0 +1,43 @@
|
||||
# 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
|
||||
}
|
||||
17
test/_load-elim.ssa
Normal file
17
test/_load-elim.ssa
Normal file
@ -0,0 +1,17 @@
|
||||
# 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
|
||||
}
|
||||
@ -28,7 +28,7 @@ function $test() {
|
||||
|
||||
# >>> driver
|
||||
# #include <stdio.h>
|
||||
# struct four { long l; char c; int i; };
|
||||
# struct four { long 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",
|
||||
|
||||
@ -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 l; } st5;
|
||||
# typedef struct { float f; long long l; } st5;
|
||||
# typedef struct { char t[16]; } st6;
|
||||
# typedef struct { float f; double d; } st7;
|
||||
# typedef struct { int i[4]; } st8;
|
||||
|
||||
@ -10,6 +10,7 @@ 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 }
|
||||
@ -112,6 +113,14 @@ 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() {
|
||||
@ -128,6 +137,7 @@ 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
|
||||
}
|
||||
@ -140,8 +150,9 @@ 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 l; char b; } Slb;
|
||||
# typedef struct { long 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 };
|
||||
@ -150,16 +161,18 @@ 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(" { %ld, '%c' }", s->l, s->b); }
|
||||
# void plb(Slb *s) { printf(" { %lld, '%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 l) { printf(" %ld", l); }
|
||||
# void pl(long long l) { printf(" %lld", l); }
|
||||
# void ps(float s) { printf(" %g", s); }
|
||||
# void pd(double d) { printf(" %g", d); }
|
||||
# /* --------------------------- */
|
||||
@ -193,8 +206,8 @@ function w $main() {
|
||||
# pss(&p0); puts("");
|
||||
# qfn4(p0);
|
||||
# }
|
||||
# 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) {
|
||||
# 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) {
|
||||
# printf("qbe->c(%d)", 5);
|
||||
# pss(&p7); ps(p8); pl(p9); puts("");
|
||||
# qfn5(p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);
|
||||
@ -223,12 +236,18 @@ function w $main() {
|
||||
# pbig(&p0); puts("");
|
||||
# qfn9(p0);
|
||||
# }
|
||||
# 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) {
|
||||
# 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) {
|
||||
# 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
|
||||
@ -254,4 +273,6 @@ 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 }
|
||||
# <<<
|
||||
|
||||
20
test/abi9.ssa
Normal file
20
test/abi9.ssa
Normal file
@ -0,0 +1,20 @@
|
||||
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
|
||||
# <<<
|
||||
21
test/alias1.ssa
Normal file
21
test/alias1.ssa
Normal file
@ -0,0 +1,21 @@
|
||||
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
|
||||
}
|
||||
@ -1,3 +1,4 @@
|
||||
# skip amd64_win (no signals on win32)
|
||||
# test amd64 addressing modes
|
||||
|
||||
export
|
||||
|
||||
15
test/copy.ssa
Normal file
15
test/copy.ssa
Normal file
@ -0,0 +1,15 @@
|
||||
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); }
|
||||
# <<<
|
||||
@ -1,4 +1,4 @@
|
||||
# skip arm64 arm64_apple rv64
|
||||
# skip arm64 arm64_apple rv64 amd64_win
|
||||
# a hack example,
|
||||
# we use a dark type to get
|
||||
# a pointer to the stack.
|
||||
|
||||
@ -72,6 +72,7 @@ function l $dtol(d %f) {
|
||||
|
||||
|
||||
# >>> driver
|
||||
# #include <float.h>
|
||||
# #include <limits.h>
|
||||
#
|
||||
# extern float fneg(float);
|
||||
@ -113,15 +114,19 @@ function l $dtol(d %f) {
|
||||
# return 7;
|
||||
# }
|
||||
# for (i=0; i<sizeof(fin)/sizeof(fin[0]); i++) {
|
||||
# if (stol((float)fin[i]) != (unsigned long long)(float)fin[i])
|
||||
# return 8;
|
||||
# if (fin[i] >= 1LL << DBL_MANT_DIG)
|
||||
# break;
|
||||
# if (dtol(fin[i]) != (unsigned long long)fin[i])
|
||||
# return 9;
|
||||
# return 8;
|
||||
# if((unsigned long long)fin[i] > UINT_MAX)
|
||||
# continue;
|
||||
# if (stow((float)fin[i]) != (unsigned int)(float)fin[i])
|
||||
# return 10;
|
||||
# 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 11;
|
||||
# }
|
||||
# return 0;
|
||||
|
||||
19
test/gvn1.ssa
Normal file
19
test/gvn1.ssa
Normal file
@ -0,0 +1,19 @@
|
||||
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; }
|
||||
# <<<
|
||||
31
test/gvn2.ssa
Normal file
31
test/gvn2.ssa
Normal file
@ -0,0 +1,31 @@
|
||||
# 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); }
|
||||
# <<<
|
||||
238
test/ifc.ssa
Normal file
238
test/ifc.ssa
Normal file
@ -0,0 +1,238 @@
|
||||
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
|
||||
# ;
|
||||
# }
|
||||
# <<<
|
||||
64
test/isel4.ssa
Normal file
64
test/isel4.ssa
Normal file
@ -0,0 +1,64 @@
|
||||
# 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;
|
||||
# }
|
||||
# <<<
|
||||
16
test/isel5.ssa
Normal file
16
test/isel5.ssa
Normal file
@ -0,0 +1,16 @@
|
||||
# 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
|
||||
# <<<
|
||||
38
test/isel6.ssa
Normal file
38
test/isel6.ssa
Normal file
@ -0,0 +1,38 @@
|
||||
# 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
|
||||
# <<<
|
||||
@ -1,3 +1,4 @@
|
||||
# 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}
|
||||
|
||||
|
||||
@ -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",
|
||||
"alloc1", "alloc2", "thread", "common",
|
||||
|
||||
};
|
||||
enum {
|
||||
|
||||
64
tools/log2.c
Normal file
64
tools/log2.c
Normal file
@ -0,0 +1,64 @@
|
||||
#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");
|
||||
}
|
||||
3
tools/mgen/.gitignore
vendored
Normal file
3
tools/mgen/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.cm[iox]
|
||||
*.o
|
||||
mgen
|
||||
1
tools/mgen/.ocp-indent
Normal file
1
tools/mgen/.ocp-indent
Normal file
@ -0,0 +1 @@
|
||||
match_clause=4
|
||||
16
tools/mgen/Makefile
Normal file
16
tools/mgen/Makefile
Normal file
@ -0,0 +1,16 @@
|
||||
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
|
||||
420
tools/mgen/cgen.ml
Normal file
420
tools/mgen/cgen.ml
Normal file
@ -0,0 +1,420 @@
|
||||
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
|
||||
413
tools/mgen/fuzz.ml
Normal file
413
tools/mgen/fuzz.ml
Normal file
@ -0,0 +1,413 @@
|
||||
(* 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 "@."
|
||||
|
||||
|
||||
214
tools/mgen/main.ml
Normal file
214
tools/mgen/main.ml
Normal file
@ -0,0 +1,214 @@
|
||||
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;
|
||||
());
|
||||
|
||||
()
|
||||
651
tools/mgen/match.ml
Normal file
651
tools/mgen/match.ml
Normal file
@ -0,0 +1,651 @@
|
||||
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
|
||||
292
tools/mgen/sexp.ml
Normal file
292
tools/mgen/sexp.ml
Normal file
@ -0,0 +1,292 @@
|
||||
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))";
|
||||
()
|
||||
134
tools/mgen/test.ml
Normal file
134
tools/mgen/test.ml
Normal file
@ -0,0 +1,134 @@
|
||||
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
|
||||
*)
|
||||
|
||||
*)
|
||||
115
tools/test.sh
115
tools/test.sh
@ -1,8 +1,12 @@
|
||||
#!/bin/sh
|
||||
|
||||
dir=`dirname "$0"`
|
||||
bin=$dir/../qbe
|
||||
binref=$dir/../qbe.ref
|
||||
if [ -z "${bin:-}" ]; then
|
||||
bin=$dir/../qbe
|
||||
fi
|
||||
if [ -z "${binref:-}" ]; then
|
||||
binref=${bin}.ref
|
||||
fi
|
||||
|
||||
tmp=/tmp/qbe.zzzz
|
||||
|
||||
@ -12,55 +16,98 @@ 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
|
||||
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=
|
||||
find_cc_and_qemu aarch64 "$p-gcc -no-pie -static" "qemu-aarch64"
|
||||
done
|
||||
if test -z "$cc"
|
||||
if test -z "$cc" -o -z "$qemu"
|
||||
then
|
||||
echo "Cannot find arm64 compiler or qemu."
|
||||
exit 1
|
||||
exit 77
|
||||
fi
|
||||
bin="$bin -t arm64"
|
||||
;;
|
||||
rv64)
|
||||
for p in riscv64-linux-musl riscv64-linux-gnu
|
||||
do
|
||||
cc="$p-gcc -no-pie -static"
|
||||
qemu="qemu-riscv64"
|
||||
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=
|
||||
find_cc_and_qemu riscv64 "$p-gcc -no-pie -static" "qemu-riscv64"
|
||||
done
|
||||
if test -z "$cc" -o -z "$qemu"
|
||||
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"
|
||||
then
|
||||
echo "Cannot find x86_64 compiler or qemu."
|
||||
exit 77
|
||||
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"
|
||||
done
|
||||
if test -z "$cc"
|
||||
then
|
||||
echo "Cannot find riscv64 compiler or qemu."
|
||||
echo "Cannot find windows compiler or wine."
|
||||
exit 1
|
||||
fi
|
||||
bin="$bin -t rv64"
|
||||
export WINEDEBUG=-all
|
||||
bin="$bin -t amd64_win"
|
||||
;;
|
||||
"")
|
||||
case `uname` in
|
||||
@ -82,7 +129,7 @@ init() {
|
||||
;;
|
||||
*)
|
||||
echo "Unknown target '$TARGET'."
|
||||
exit 1
|
||||
exit 77
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@ -157,7 +204,7 @@ once() {
|
||||
|
||||
if test -s $out
|
||||
then
|
||||
$qemu $exe a b c | diff -u - $out
|
||||
$qemu $exe a b c | tr -d '\r' | diff -u - $out
|
||||
ret=$?
|
||||
reason="output"
|
||||
else
|
||||
|
||||
196
util.c
196
util.c
@ -154,6 +154,24 @@ 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, ...)
|
||||
{
|
||||
@ -229,6 +247,62 @@ 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)
|
||||
{
|
||||
@ -253,18 +327,18 @@ emiti(Ins i)
|
||||
}
|
||||
|
||||
void
|
||||
idup(Ins **pd, Ins *s, ulong n)
|
||||
idup(Blk *b, Ins *s, ulong n)
|
||||
{
|
||||
*pd = alloc(n * sizeof(Ins));
|
||||
if (n)
|
||||
memcpy(*pd, s, n * sizeof(Ins));
|
||||
vgrow(&b->ins, n);
|
||||
icpy(b->ins, s, n);
|
||||
b->nins = n;
|
||||
}
|
||||
|
||||
Ins *
|
||||
icpy(Ins *d, Ins *s, ulong n)
|
||||
{
|
||||
if (n)
|
||||
memcpy(d, s, n * sizeof(Ins));
|
||||
memmove(d, s, n * sizeof(Ins));
|
||||
return d + n;
|
||||
}
|
||||
|
||||
@ -304,6 +378,16 @@ 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)
|
||||
{
|
||||
@ -334,6 +418,28 @@ 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)
|
||||
{
|
||||
@ -398,22 +504,40 @@ getcon(int64_t val, Fn *fn)
|
||||
}
|
||||
|
||||
int
|
||||
addcon(Con *c0, Con *c1)
|
||||
addcon(Con *c0, Con *c1, int m)
|
||||
{
|
||||
if (c0->type == CUndef)
|
||||
if (m != 1 && c1->type == CAddr)
|
||||
return 0;
|
||||
if (c0->type == CUndef) {
|
||||
*c0 = *c1;
|
||||
else {
|
||||
c0->bits.i *= m;
|
||||
} else {
|
||||
if (c1->type == CAddr) {
|
||||
if (c0->type == CAddr)
|
||||
return 0;
|
||||
c0->type = CAddr;
|
||||
c0->sym = c1->sym;
|
||||
}
|
||||
c0->bits.i += c1->bits.i;
|
||||
c0->bits.i += c1->bits.i * m;
|
||||
}
|
||||
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)
|
||||
{
|
||||
@ -594,3 +718,57 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user