plan9fox/sys/src/cmd/vl/asm.c
cinap_lenrek da9b38c75c 5l,6l,8l,kl,ql,vl: allow duplicate GLOBAL symbols (from Ori Bernstein)
The plan 9 assemblers support the DUPOK flag on text symbols. They parse and
ignore it on GLOBL symbols. This patch makes it work in the linkers.

The reason I ran into this is because my programming language (Myrddin) uses
data symbols to generate type information, and it's useful to avoid
duplicating all of the type info in every file that gets generated.
2017-03-19 03:05:24 +01:00

1489 lines
32 KiB
C

#include "l.h"
long OFFSET;
/*
long BADOFFSET = -1;
if(OFFSET <= BADOFFSET && OFFSET+4 > BADOFFSET)\
abort();\
OFFSET += 4;\
if(OFFSET == BADOFFSET)\
abort();\
OFFSET++;\
*/
#define LPUT(l) { \
if (little) { \
LLEPUT(l); \
} else { \
LBEPUT(l); \
} \
}
#define LLEPUT(c)\
{\
cbp[0] = (c);\
cbp[1] = (c)>>8;\
cbp[2] = (c)>>16;\
cbp[3] = (c)>>24;\
cbp += 4;\
cbc -= 4;\
if(cbc <= 0)\
cflush();\
}
#define LBEPUT(c)\
{\
cbp[0] = (c)>>24;\
cbp[1] = (c)>>16;\
cbp[2] = (c)>>8;\
cbp[3] = (c);\
cbp += 4;\
cbc -= 4;\
if(cbc <= 0)\
cflush();\
}
#define HPUT(h) { \
if (little) { \
HLEPUT(h); \
} else { \
HBEPUT(h); \
} \
}
#define HLEPUT(c)\
{\
cbp[0] = (c);\
cbp[1] = (c)>>8;\
cbp += 2;\
cbc -= 2;\
if(cbc <= 0)\
cflush();\
}
#define HBEPUT(c)\
{\
cbp[0] = (c)>>8;\
cbp[1] = (c);\
cbp += 2;\
cbc -= 2;\
if(cbc <= 0)\
cflush();\
}
#define CPUT(c)\
{\
cbp[0] = (c);\
cbp++;\
cbc--;\
if(cbc <= 0)\
cflush();\
}
void
objput(long l) /* emit long in byte order appropriate to object machine */
{
LPUT(l);
}
void
objhput(short s)
{
HPUT(s);
}
void
lput(long l) /* emit long in big-endian byte order */
{
LBEPUT(l);
}
long
entryvalue(void)
{
char *a;
Sym *s;
a = INITENTRY;
if(*a >= '0' && *a <= '9')
return atolwhex(a);
s = lookup(a, 0);
if(s->type == 0)
return INITTEXT;
if(s->type != STEXT && s->type != SLEAF)
diag("entry not text: %s", s->name);
return s->value;
}
void
asmb(void)
{
Prog *p;
long t, etext;
Optab *o;
if(debug['v'])
Bprint(&bso, "%5.2f asm\n", cputime());
Bflush(&bso);
OFFSET = HEADR;
seek(cout, OFFSET, 0);
pc = INITTEXT;
for(p = firstp; p != P; p = p->link) {
if(p->as == ATEXT) {
curtext = p;
autosize = p->to.offset + 4;
}
if(p->pc != pc) {
diag("phase error %lux sb %lux",
p->pc, pc);
if(!debug['a'])
prasm(curp);
pc = p->pc;
}
curp = p;
o = oplook(p); /* could probably avoid this call */
if(asmout(p, o, 0)) {
p = p->link;
pc += 4;
}
pc += o->size;
}
if(debug['a'])
Bprint(&bso, "\n");
Bflush(&bso);
cflush();
etext = INITTEXT + textsize;
for(t = pc; t < etext; t += sizeof(buf)-100) {
if(etext-t > sizeof(buf)-100)
datblk(t, sizeof(buf)-100, 1);
else
datblk(t, etext-t, 1);
}
Bflush(&bso);
cflush();
curtext = P;
switch(HEADTYPE) {
case 0:
case 4:
OFFSET = rnd(HEADR+textsize, 4096);
seek(cout, OFFSET, 0);
break;
case 1:
case 2:
case 3:
case 5:
case 6:
OFFSET = HEADR+textsize;
seek(cout, OFFSET, 0);
break;
}
for(t = 0; t < datsize; t += sizeof(buf)-100) {
if(datsize-t > sizeof(buf)-100)
datblk(t, sizeof(buf)-100, 0);
else
datblk(t, datsize-t, 0);
}
symsize = 0;
lcsize = 0;
if(!debug['s']) {
if(debug['v'])
Bprint(&bso, "%5.2f sym\n", cputime());
Bflush(&bso);
switch(HEADTYPE) {
case 0:
case 4:
OFFSET = rnd(HEADR+textsize, 4096)+datsize;
seek(cout, OFFSET, 0);
break;
case 3:
case 2:
case 1:
case 5:
case 6:
OFFSET = HEADR+textsize+datsize;
seek(cout, OFFSET, 0);
break;
}
if(!debug['s'])
asmsym();
if(debug['v'])
Bprint(&bso, "%5.2f pc\n", cputime());
Bflush(&bso);
if(!debug['s'])
asmlc();
cflush();
}
if(debug['v'])
Bprint(&bso, "%5.2f header\n", cputime());
Bflush(&bso);
OFFSET = 0;
seek(cout, OFFSET, 0);
switch(HEADTYPE) {
case 0:
lput(0x160L<<16); /* magic and sections */
lput(0L); /* time and date */
lput(rnd(HEADR+textsize, 4096)+datsize);
lput(symsize); /* nsyms */
lput((0x38L<<16)|7L); /* size of optional hdr and flags */
lput((0413<<16)|0437L); /* magic and version */
lput(rnd(HEADR+textsize, 4096)); /* sizes */
lput(datsize);
lput(bsssize);
lput(entryvalue()); /* va of entry */
lput(INITTEXT-HEADR); /* va of base of text */
lput(INITDAT); /* va of base of data */
lput(INITDAT+datsize); /* va of base of bss */
lput(~0L); /* gp reg mask */
lput(0L);
lput(0L);
lput(0L);
lput(0L);
lput(~0L); /* gp value ?? */
break;
case 1:
lput(0x160L<<16); /* magic and sections */
lput(0L); /* time and date */
lput(HEADR+textsize+datsize);
lput(symsize); /* nsyms */
lput((0x38L<<16)|7L); /* size of optional hdr and flags */
lput((0407<<16)|0437L); /* magic and version */
lput(textsize); /* sizes */
lput(datsize);
lput(bsssize);
lput(entryvalue()); /* va of entry */
lput(INITTEXT); /* va of base of text */
lput(INITDAT); /* va of base of data */
lput(INITDAT+datsize); /* va of base of bss */
lput(~0L); /* gp reg mask */
lput(lcsize);
lput(0L);
lput(0L);
lput(0L);
lput(~0L); /* gp value ?? */
lput(0L); /* complete mystery */
break;
case 2:
if (little)
t = 24;
else
t = 16;
lput(((((4*t)+0)*t)+7)); /* magic */
lput(textsize); /* sizes */
lput(datsize);
lput(bsssize);
lput(symsize); /* nsyms */
lput(entryvalue()); /* va of entry */
lput(0L);
lput(lcsize);
break;
case 3:
lput((0x160L<<16)|3L); /* magic and sections */
lput(time(0)); /* time and date */
lput(HEADR+textsize+datsize);
lput(symsize); /* nsyms */
lput((0x38L<<16)|7L); /* size of optional hdr and flags */
lput((0407<<16)|0437L); /* magic and version */
lput(textsize); /* sizes */
lput(datsize);
lput(bsssize);
lput(entryvalue()); /* va of entry */
lput(INITTEXT); /* va of base of text */
lput(INITDAT); /* va of base of data */
lput(INITDAT+datsize); /* va of base of bss */
lput(~0L); /* gp reg mask */
lput(lcsize);
lput(0L);
lput(0L);
lput(0L);
lput(~0L); /* gp value ?? */
strnput(".text", 8); /* text segment */
lput(INITTEXT); /* address */
lput(INITTEXT);
lput(textsize);
lput(HEADR);
lput(0L);
lput(HEADR+textsize+datsize+symsize);
lput(lcsize & 0xffff); /* line number size */
lput(0x20L); /* flags */
strnput(".data", 8); /* data segment */
lput(INITDAT); /* address */
lput(INITDAT);
lput(datsize);
lput(HEADR+textsize);
lput(0L);
lput(0L);
lput(0L);
lput(0x40L); /* flags */
strnput(".bss", 8); /* bss segment */
lput(INITDAT+datsize); /* address */
lput(INITDAT+datsize);
lput(bsssize);
lput(0L);
lput(0L);
lput(0L);
lput(0L);
lput(0x80L); /* flags */
break;
case 4:
lput((0x160L<<16)|3L); /* magic and sections */
lput(time(0)); /* time and date */
lput(rnd(HEADR+textsize, 4096)+datsize);
lput(symsize); /* nsyms */
lput((0x38L<<16)|7L); /* size of optional hdr and flags */
lput((0413<<16)|01012L); /* magic and version */
lput(textsize); /* sizes */
lput(datsize);
lput(bsssize);
lput(entryvalue()); /* va of entry */
lput(INITTEXT); /* va of base of text */
lput(INITDAT); /* va of base of data */
lput(INITDAT+datsize); /* va of base of bss */
lput(~0L); /* gp reg mask */
lput(lcsize);
lput(0L);
lput(0L);
lput(0L);
lput(~0L); /* gp value ?? */
strnput(".text", 8); /* text segment */
lput(INITTEXT); /* address */
lput(INITTEXT);
lput(textsize);
lput(HEADR);
lput(0L);
lput(HEADR+textsize+datsize+symsize);
lput(lcsize & 0xffff); /* line number size */
lput(0x20L); /* flags */
strnput(".data", 8); /* data segment */
lput(INITDAT); /* address */
lput(INITDAT);
lput(datsize);
lput(rnd(HEADR+textsize, 4096)); /* sizes */
lput(0L);
lput(0L);
lput(0L);
lput(0x40L); /* flags */
strnput(".bss", 8); /* bss segment */
lput(INITDAT+datsize); /* address */
lput(INITDAT+datsize);
lput(bsssize);
lput(0L);
lput(0L);
lput(0L);
lput(0L);
lput(0x80L); /* flags */
break;
case 5:
/* first part of ELF is byte-wide parts, thus no byte-order issues */
strnput("\177ELF", 4); /* e_ident */
CPUT(1); /* class = 32 bit */
CPUT(little? 1: 2); /* data: 1 = LSB, 2 = MSB */
CPUT(1); /* version = 1 */
strnput("", 9); /* reserved for expansion */
/* entire remainder of ELF file is in target byte order */
/* file header part of ELF header */
objhput(2); /* type = EXEC */
objhput(8); /* machine = MIPS */
objput(1L); /* version = CURRENT */
objput(entryvalue()); /* entry vaddr */
objput(52L); /* offset to first phdr */
objput(0L); /* offset to first shdr */
objput(0L); /* flags (no MIPS flags defined) */
objhput(52); /* Ehdr size */
objhput(32); /* Phdr size */
objhput(3); /* # of Phdrs */
objhput(0); /* Shdr size */
objhput(0); /* # of Shdrs */
objhput(0); /* Shdr string size */
/* "Program headers" - one per chunk of file to load */
/*
* include ELF headers in text -- 8l doesn't,
* but in theory it aids demand loading.
*/
objput(1L); /* text: type = PT_LOAD */
objput(0L); /* file offset */
objput(INITTEXT-HEADR); /* vaddr */
objput(INITTEXT-HEADR); /* paddr */
objput(HEADR+textsize); /* file size */
objput(HEADR+textsize); /* memory size */
objput(0x05L); /* protections = RX */
objput(0x1000L); /* page-align text off's & vaddrs */
objput(1L); /* data: type = PT_LOAD */
objput(HEADR+textsize); /* file offset */
objput(INITDAT); /* vaddr */
objput(INITDAT); /* paddr */
objput(datsize); /* file size */
objput(datsize+bsssize); /* memory size */
objput(0x06L); /* protections = RW */
if(INITDAT % 4096 == 0 && (HEADR + textsize) % 4096 == 0)
objput(0x1000L); /* page-align data off's & vaddrs */
else
objput(0L); /* do not claim alignment */
objput(0L); /* P9 symbols: type = PT_NULL */
objput(HEADR+textsize+datsize); /* file offset */
objput(0L);
objput(0L);
objput(symsize); /* symbol table size */
objput(lcsize); /* line number size */
objput(0x04L); /* protections = R */
objput(0L); /* do not claim alignment */
break;
case 6:
break;
}
cflush();
}
void
strnput(char *s, int n)
{
for(; *s; s++){
CPUT(*s);
n--;
}
for(; n > 0; n--)
CPUT(0);
}
void
cflush(void)
{
int n;
n = sizeof(buf.cbuf) - cbc;
if(n)
write(cout, buf.cbuf, n);
cbp = buf.cbuf;
cbc = sizeof(buf.cbuf);
}
void
nopstat(char *f, Count *c)
{
if(c->outof)
Bprint(&bso, "%s delay %ld/%ld (%.2f)\n", f,
c->outof - c->count, c->outof,
(double)(c->outof - c->count)/c->outof);
}
void
asmsym(void)
{
Prog *p;
Auto *a;
Sym *s;
int h;
s = lookup("etext", 0);
if(s->type == STEXT)
putsymb(s->name, 'T', s->value, s->version);
for(h=0; h<NHASH; h++)
for(s=hash[h]; s!=S; s=s->link)
switch(s->type) {
case SCONST:
putsymb(s->name, 'D', s->value, s->version);
continue;
case SSTRING:
putsymb(s->name, 'T', s->value, s->version);
continue;
case SDATA:
putsymb(s->name, 'D', s->value+INITDAT, s->version);
continue;
case SBSS:
putsymb(s->name, 'B', s->value+INITDAT, s->version);
continue;
case SFILE:
putsymb(s->name, 'f', s->value, s->version);
continue;
}
for(p=textp; p!=P; p=p->cond) {
s = p->from.sym;
if(s->type != STEXT && s->type != SLEAF)
continue;
/* filenames first */
for(a=p->to.autom; a; a=a->link)
if(a->type == D_FILE)
putsymb(a->asym->name, 'z', a->aoffset, 0);
else
if(a->type == D_FILE1)
putsymb(a->asym->name, 'Z', a->aoffset, 0);
if(s->type == STEXT)
putsymb(s->name, 'T', s->value, s->version);
else
putsymb(s->name, 'L', s->value, s->version);
/* frame, auto and param after */
putsymb(".frame", 'm', p->to.offset+4, 0);
for(a=p->to.autom; a; a=a->link)
if(a->type == D_AUTO)
putsymb(a->asym->name, 'a', -a->aoffset, 0);
else
if(a->type == D_PARAM)
putsymb(a->asym->name, 'p', a->aoffset, 0);
}
if(debug['v'] || debug['n'])
Bprint(&bso, "symsize = %lud\n", symsize);
Bflush(&bso);
}
void
putsymb(char *s, int t, long v, int ver)
{
int i, f;
if(t == 'f')
s++;
LBEPUT(v);
if(ver)
t += 'a' - 'A';
CPUT(t+0x80); /* 0x80 is variable length */
if(t == 'Z' || t == 'z') {
CPUT(s[0]);
for(i=1; s[i] != 0 || s[i+1] != 0; i += 2) {
CPUT(s[i]);
CPUT(s[i+1]);
}
CPUT(0);
CPUT(0);
i++;
}
else {
for(i=0; s[i]; i++)
CPUT(s[i]);
CPUT(0);
}
symsize += 4 + 1 + i + 1;
if(debug['n']) {
if(t == 'z' || t == 'Z') {
Bprint(&bso, "%c %.8lux ", t, v);
for(i=1; s[i] != 0 || s[i+1] != 0; i+=2) {
f = ((s[i]&0xff) << 8) | (s[i+1]&0xff);
Bprint(&bso, "/%x", f);
}
Bprint(&bso, "\n");
return;
}
if(ver)
Bprint(&bso, "%c %.8lux %s<%d>\n", t, v, s, ver);
else
Bprint(&bso, "%c %.8lux %s\n", t, v, s);
}
}
#define MINLC 4
void
asmlc(void)
{
long oldpc, oldlc;
Prog *p;
long v, s;
oldpc = INITTEXT;
oldlc = 0;
for(p = firstp; p != P; p = p->link) {
if(p->line == oldlc || p->as == ATEXT || p->as == ANOP) {
if(p->as == ATEXT)
curtext = p;
if(debug['L'])
Bprint(&bso, "%6lux %P\n",
p->pc, p);
continue;
}
if(debug['L'])
Bprint(&bso, "\t\t%6ld", lcsize);
v = (p->pc - oldpc) / MINLC;
while(v) {
s = 127;
if(v < 127)
s = v;
CPUT(s+128); /* 129-255 +pc */
if(debug['L'])
Bprint(&bso, " pc+%ld*%d(%ld)", s, MINLC, s+128);
v -= s;
lcsize++;
}
s = p->line - oldlc;
oldlc = p->line;
oldpc = p->pc + MINLC;
if(s > 64 || s < -64) {
CPUT(0); /* 0 vv +lc */
CPUT(s>>24);
CPUT(s>>16);
CPUT(s>>8);
CPUT(s);
if(debug['L']) {
if(s > 0)
Bprint(&bso, " lc+%ld(%d,%ld)\n",
s, 0, s);
else
Bprint(&bso, " lc%ld(%d,%ld)\n",
s, 0, s);
Bprint(&bso, "%6lux %P\n",
p->pc, p);
}
lcsize += 5;
continue;
}
if(s > 0) {
CPUT(0+s); /* 1-64 +lc */
if(debug['L']) {
Bprint(&bso, " lc+%ld(%ld)\n", s, 0+s);
Bprint(&bso, "%6lux %P\n",
p->pc, p);
}
} else {
CPUT(64-s); /* 65-128 -lc */
if(debug['L']) {
Bprint(&bso, " lc%ld(%ld)\n", s, 64-s);
Bprint(&bso, "%6lux %P\n",
p->pc, p);
}
}
lcsize++;
}
while(lcsize & 1) {
s = 129;
CPUT(s);
lcsize++;
}
if(debug['v'] || debug['L'])
Bprint(&bso, "lcsize = %ld\n", lcsize);
Bflush(&bso);
}
void
datblk(long s, long n, int str)
{
Prog *p;
char *cast;
long l, fl, j, d;
int i, c;
memset(buf.dbuf, 0, n+100);
for(p = datap; p != P; p = p->link) {
curp = p;
if(str != (p->from.sym->type == SSTRING))
continue;
l = p->from.sym->value + p->from.offset - s;
c = p->reg;
i = 0;
if(l < 0) {
if(l+c <= 0)
continue;
while(l < 0) {
l++;
i++;
}
}
if(l >= n)
continue;
if(p->as != AINIT && p->as != ADYNT && !p->from.sym->dupok) {
for(j=l+(c-i)-1; j>=l; j--)
if(buf.dbuf[j]) {
print("%P\n", p);
diag("multiple initialization");
break;
}
}
switch(p->to.type) {
default:
diag("unknown mode in initialization\n%P", p);
break;
case D_FCONST:
switch(c) {
default:
case 4:
fl = ieeedtof(p->to.ieee);
cast = (char*)&fl;
for(; i<c; i++) {
buf.dbuf[l] = cast[fnuxi8[i+4]];
l++;
}
break;
case 8:
cast = (char*)p->to.ieee;
for(; i<c; i++) {
buf.dbuf[l] = cast[fnuxi8[i]];
l++;
}
break;
}
break;
case D_SCONST:
for(; i<c; i++) {
buf.dbuf[l] = p->to.sval[i];
l++;
}
break;
case D_CONST:
d = p->to.offset;
if(p->to.sym) {
switch(p->to.sym->type) {
case STEXT:
case SLEAF:
case SSTRING:
d += p->to.sym->value;
break;
case SDATA:
case SBSS:
d += p->to.sym->value + INITDAT;
break;
}
}
cast = (char*)&d;
switch(c) {
default:
diag("bad nuxi %d %d\n%P", c, i, curp);
break;
case 1:
for(; i<c; i++) {
buf.dbuf[l] = cast[inuxi1[i]];
l++;
}
break;
case 2:
for(; i<c; i++) {
buf.dbuf[l] = cast[inuxi2[i]];
l++;
}
break;
case 4:
for(; i<c; i++) {
buf.dbuf[l] = cast[inuxi4[i]];
l++;
}
break;
}
break;
}
}
write(cout, buf.dbuf, n);
}
#define OP_RRR(op,r1,r2,r3)\
(op|(((r1)&31L)<<16)|(((r2)&31L)<<21)|(((r3)&31L)<<11))
#define OP_IRR(op,i,r2,r3)\
(op|((i)&0xffffL)|(((r2)&31L)<<21)|(((r3)&31L)<<16))
#define OP_SRR(op,s,r2,r3)\
(op|(((s)&31L)<<6)|(((r2)&31L)<<16)|(((r3)&31L)<<11))
#define OP_FRRR(op,r1,r2,r3)\
(op|(((r1)&31L)<<16)|(((r2)&31L)<<11)|(((r3)&31L)<<6))
#define OP_JMP(op,i)\
((op)|((i)&0x3ffffffL))
#define OP(x,y)\
(((x)<<3)|((y)<<0))
#define SP(x,y)\
(((x)<<29)|((y)<<26))
#define BCOND(x,y)\
(((x)<<19)|((y)<<16))
#define MMU(x,y)\
(SP(2,0)|(16<<21)|((x)<<3)|((y)<<0))
#define FPF(x,y)\
(SP(2,1)|(16<<21)|((x)<<3)|((y)<<0))
#define FPD(x,y)\
(SP(2,1)|(17<<21)|((x)<<3)|((y)<<0))
#define FPW(x,y)\
(SP(2,1)|(20<<21)|((x)<<3)|((y)<<0))
int vshift(int);
int
asmout(Prog *p, Optab *o, int aflag)
{
long o1, o2, o3, o4, o5, o6, o7, v;
Prog *ct;
int r, a;
o1 = 0;
o2 = 0;
o3 = 0;
o4 = 0;
o5 = 0;
o6 = 0;
o7 = 0;
switch(o->type) {
default:
diag("unknown type %d", o->type);
if(!debug['a'])
prasm(p);
break;
case 0: /* pseudo ops */
if(aflag) {
if(p->link) {
if(p->as == ATEXT) {
ct = curtext;
o2 = autosize;
curtext = p;
autosize = p->to.offset + 4;
o1 = asmout(p->link, oplook(p->link), aflag);
curtext = ct;
autosize = o2;
} else
o1 = asmout(p->link, oplook(p->link), aflag);
}
return o1;
}
break;
case 1: /* mov[v] r1,r2 ==> OR r1,r0,r2 */
o1 = OP_RRR(oprrr(AOR), p->from.reg, REGZERO, p->to.reg);
break;
case 2: /* add/sub r1,[r2],r3 */
r = p->reg;
if(r == NREG)
r = p->to.reg;
o1 = OP_RRR(oprrr(p->as), p->from.reg, r, p->to.reg);
break;
case 3: /* mov $soreg, r ==> or/add $i,o,r */
v = regoff(&p->from);
r = p->from.reg;
if(r == NREG)
r = o->param;
a = AADDU;
if(o->a1 == C_ANDCON)
a = AOR;
o1 = OP_IRR(opirr(a), v, r, p->to.reg);
break;
case 4: /* add $scon,[r1],r2 */
v = regoff(&p->from);
r = p->reg;
if(r == NREG)
r = p->to.reg;
o1 = OP_IRR(opirr(p->as), v, r, p->to.reg);
break;
case 5: /* syscall */
if(aflag)
return 0;
o1 = oprrr(p->as);
break;
case 6: /* beq r1,[r2],sbra */
if(aflag)
return 0;
if(p->cond == P)
v = -4 >> 2;
else
v = (p->cond->pc - pc-4) >> 2;
if(((v << 16) >> 16) != v)
diag("short branch too far: %ld\n%P", v, p);
o1 = OP_IRR(opirr(p->as), v, p->from.reg, p->reg);
break;
case 7: /* mov r, soreg ==> sw o(r) */
r = p->to.reg;
if(r == NREG)
r = o->param;
v = regoff(&p->to);
o1 = OP_IRR(opirr(p->as), v, r, p->from.reg);
break;
case 8: /* mov soreg, r ==> lw o(r) */
r = p->from.reg;
if(r == NREG)
r = o->param;
v = regoff(&p->from);
o1 = OP_IRR(opirr(p->as+ALAST), v, r, p->to.reg);
break;
case 9: /* asl r1,[r2],r3 */
r = p->reg;
if(r == NREG)
r = p->to.reg;
o1 = OP_RRR(oprrr(p->as), r, p->from.reg, p->to.reg);
break;
case 10: /* add $con,[r1],r2 ==> mov $con,t; add t,[r1],r2 */
v = regoff(&p->from);
r = AOR;
if(v < 0)
r = AADDU;
o1 = OP_IRR(opirr(r), v, 0, REGTMP);
r = p->reg;
if(r == NREG)
r = p->to.reg;
o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
break;
case 11: /* jmp lbra */
if(aflag)
return 0;
if(p->cond == P)
v = p->pc >> 2;
else
v = p->cond->pc >> 2;
o1 = OP_JMP(opirr(p->as), v);
if(!debug['Y'] && p->link && p->cond && isnop(p->link)) {
nop.branch.count--;
nop.branch.outof--;
nop.jump.outof++;
o2 = asmout(p->cond, oplook(p->cond), 1);
if(o2) {
o1 += 1;
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n",
p->pc, o1, o2, p);
LPUT(o1);
LPUT(o2);
return 1;
}
}
break;
case 12: /* movbs r,r */
v = 16;
if(p->as == AMOVB)
v = 24;
o1 = OP_SRR(opirr(ASLL), v, p->from.reg, p->to.reg);
o2 = OP_SRR(opirr(ASRA), v, p->to.reg, p->to.reg);
break;
case 13: /* movbu r,r */
if(p->as == AMOVBU)
o1 = OP_IRR(opirr(AAND), 0xffL, p->from.reg, p->to.reg);
else
o1 = OP_IRR(opirr(AAND), 0xffffL, p->from.reg, p->to.reg);
break;
case 16: /* sll $c,[r1],r2 */
v = regoff(&p->from);
r = p->reg;
if(r == NREG)
r = p->to.reg;
/* OP_SRR will use only the low 5 bits of the shift value */
if(v >= 32 && vshift(p->as))
o1 = OP_SRR(opirr(p->as+ALAST), v-32, r, p->to.reg);
else
o1 = OP_SRR(opirr(p->as), v, r, p->to.reg);
break;
case 18: /* jmp [r1],0(r2) */
if(aflag)
return 0;
r = p->reg;
if(r == NREG)
r = o->param;
o1 = OP_RRR(oprrr(p->as), 0, p->to.reg, r);
break;
case 19: /* mov $lcon,r ==> lu+or */
v = regoff(&p->from);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
o2 = OP_IRR(opirr(AOR), v, p->to.reg, p->to.reg);
break;
case 20: /* mov lohi,r */
r = OP(2,0); /* mfhi */
if(p->from.type == D_LO)
r = OP(2,2); /* mflo */
o1 = OP_RRR(r, REGZERO, REGZERO, p->to.reg);
break;
case 21: /* mov r,lohi */
r = OP(2,1); /* mthi */
if(p->to.type == D_LO)
r = OP(2,3); /* mtlo */
o1 = OP_RRR(r, REGZERO, p->from.reg, REGZERO);
break;
case 22: /* mul r1,r2 */
o1 = OP_RRR(oprrr(p->as), p->from.reg, p->reg, REGZERO);
break;
case 23: /* add $lcon,r1,r2 ==> lu+or+add */
v = regoff(&p->from);
if(p->to.reg == REGTMP || p->reg == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
r = p->reg;
if(r == NREG)
r = p->to.reg;
o3 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
break;
case 24: /* mov $ucon,,r ==> lu r */
v = regoff(&p->from);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, p->to.reg);
break;
case 25: /* add/and $ucon,[r1],r2 ==> lu $con,t; add t,[r1],r2 */
v = regoff(&p->from);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
r = p->reg;
if(r == NREG)
r = p->to.reg;
o2 = OP_RRR(oprrr(p->as), REGTMP, r, p->to.reg);
break;
case 26: /* mov $lsext/auto/oreg,,r2 ==> lu+or+add */
v = regoff(&p->from);
if(p->to.reg == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
r = p->from.reg;
if(r == NREG)
r = o->param;
o3 = OP_RRR(oprrr(AADDU), REGTMP, r, p->to.reg);
break;
case 27: /* mov [sl]ext/auto/oreg,fr ==> lwc1 o(r) */
r = p->from.reg;
if(r == NREG)
r = o->param;
v = regoff(&p->from);
switch(o->size) {
case 20:
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(AMOVF+ALAST), 0, REGTMP, p->to.reg+1);
o5 = OP_IRR(opirr(AMOVF+ALAST), 4, REGTMP, p->to.reg);
break;
case 16:
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(AMOVF+ALAST), 0, REGTMP, p->to.reg);
break;
case 8:
o1 = OP_IRR(opirr(AMOVF+ALAST), v, r, p->to.reg+1);
o2 = OP_IRR(opirr(AMOVF+ALAST), v+4, r, p->to.reg);
break;
case 4:
o1 = OP_IRR(opirr(AMOVF+ALAST), v, r, p->to.reg);
break;
}
break;
case 28: /* mov fr,[sl]ext/auto/oreg ==> swc1 o(r) */
r = p->to.reg;
if(r == NREG)
r = o->param;
v = regoff(&p->to);
switch(o->size) {
case 20:
if(r == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(AMOVF), 0, REGTMP, p->from.reg+1);
o5 = OP_IRR(opirr(AMOVF), 4, REGTMP, p->from.reg);
break;
case 16:
if(r == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(AMOVF), 0, REGTMP, p->from.reg);
break;
case 8:
o1 = OP_IRR(opirr(AMOVF), v, r, p->from.reg+1);
o2 = OP_IRR(opirr(AMOVF), v+4, r, p->from.reg);
break;
case 4:
o1 = OP_IRR(opirr(AMOVF), v, r, p->from.reg);
break;
}
break;
case 30: /* movw r,fr */
r = SP(2,1)|(4<<21); /* mtc1 */
o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
break;
case 31: /* movw fr,r */
r = SP(2,1)|(0<<21); /* mfc1 */
o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
break;
case 32: /* fadd fr1,[fr2],fr3 */
r = p->reg;
if(r == NREG)
o1 = OP_FRRR(oprrr(p->as), p->from.reg, p->to.reg, p->to.reg);
else
o1 = OP_FRRR(oprrr(p->as), p->from.reg, r, p->to.reg);
break;
case 33: /* fabs fr1,fr3 */
o1 = OP_FRRR(oprrr(p->as), 0, p->from.reg, p->to.reg);
break;
case 34: /* mov $con,fr ==> or/add $i,r,r2 */
v = regoff(&p->from);
r = AADDU;
if(o->a1 == C_ANDCON)
r = AOR;
o1 = OP_IRR(opirr(r), v, 0, REGTMP);
o2 = OP_RRR(SP(2,1)|(4<<21), REGTMP, 0, p->to.reg); /* mtc1 */
break;
case 35: /* mov r,lext/luto/oreg ==> sw o(r) */
/*
* the lowbits of the constant cannot
* be moved into the offset of the load
* because the mips 4000 in 64-bit mode
* does a 64-bit add and it will screw up.
*/
v = regoff(&p->to);
r = p->to.reg;
if(r == NREG)
r = o->param;
if(r == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(p->as), 0, REGTMP, p->from.reg);
break;
case 36: /* mov lext/lauto/lreg,r ==> lw o(r30) */
v = regoff(&p->from);
r = p->from.reg;
if(r == NREG)
r = o->param;
if(r == REGTMP)
diag("cant synthesize large constant\n%P", p);
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_IRR(opirr(AOR), v, REGTMP, REGTMP);
o3 = OP_RRR(oprrr(AADDU), r, REGTMP, REGTMP);
o4 = OP_IRR(opirr(p->as+ALAST), 0, REGTMP, p->to.reg);
break;
case 37: /* movw r,mr */
r = SP(2,0)|(4<<21); /* mtc0 */
if(p->as == AMOVV)
r = SP(2,0)|(5<<21); /* dmtc0 */
o1 = OP_RRR(r, p->from.reg, 0, p->to.reg);
break;
case 38: /* movw mr,r */
r = SP(2,0)|(0<<21); /* mfc0 */
if(p->as == AMOVV)
r = SP(2,0)|(1<<21); /* dmfc0 */
o1 = OP_RRR(r, p->to.reg, 0, p->from.reg);
break;
case 39: /* rfe ==> jmp+rfe */
if(aflag)
return 0;
o1 = OP_RRR(oprrr(AJMP), 0, p->to.reg, REGZERO);
o2 = oprrr(p->as);
break;
case 40: /* word */
if(aflag)
return 0;
o1 = regoff(&p->to);
break;
case 41: /* movw r,fcr */
o1 = OP_RRR(SP(2,1)|(2<<21), REGZERO, 0, p->to.reg); /* mfcc1 */
o2 = OP_RRR(SP(2,1)|(6<<21), p->from.reg, 0, p->to.reg);/* mtcc1 */
break;
case 42: /* movw fcr,r */
o1 = OP_RRR(SP(2,1)|(2<<21), p->to.reg, 0, p->from.reg);/* mfcc1 */
break;
case 45: /* case r */
if(p->link == P)
v = p->pc+28;
else
v = p->link->pc;
if(v & (1<<15))
o1 = OP_IRR(opirr(ALAST), (v>>16)+1, REGZERO, REGTMP);
else
o1 = OP_IRR(opirr(ALAST), v>>16, REGZERO, REGTMP);
o2 = OP_SRR(opirr(ASLL), 2, p->from.reg, p->from.reg);
o3 = OP_RRR(oprrr(AADD), p->from.reg, REGTMP, REGTMP);
o4 = OP_IRR(opirr(AMOVW+ALAST), v, REGTMP, REGTMP);
o5 = OP_RRR(oprrr(ANOR), REGZERO, REGZERO, REGZERO);
o6 = OP_RRR(oprrr(AJMP), 0, REGTMP, REGZERO);
o7 = OP_RRR(oprrr(ANOR), REGZERO, REGZERO, REGZERO);
break;
case 46: /* bcase $con,lbra */
if(p->cond == P)
v = p->pc;
else
v = p->cond->pc;
o1 = v;
break;
}
if(aflag)
return o1;
v = p->pc;
switch(o->size) {
default:
if(debug['a'])
Bprint(&bso, " %.8lux:\t\t%P\n", v, p);
break;
case 4:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux\t%P\n", v, o1, p);
LPUT(o1);
break;
case 8:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux%P\n", v, o1, o2, p);
LPUT(o1);
LPUT(o2);
break;
case 12:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux%P\n", v, o1, o2, o3, p);
LPUT(o1);
LPUT(o2);
LPUT(o3);
break;
case 16:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux%P\n",
v, o1, o2, o3, o4, p);
LPUT(o1);
LPUT(o2);
LPUT(o3);
LPUT(o4);
break;
case 20:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
v, o1, o2, o3, o4, o5, p);
LPUT(o1);
LPUT(o2);
LPUT(o3);
LPUT(o4);
LPUT(o5);
break;
case 28:
if(debug['a'])
Bprint(&bso, " %.8lux: %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux %.8lux%P\n",
v, o1, o2, o3, o4, o5, o6, o7, p);
LPUT(o1);
LPUT(o2);
LPUT(o3);
LPUT(o4);
LPUT(o5);
LPUT(o6);
LPUT(o7);
break;
}
return 0;
}
int
isnop(Prog *p)
{
if(p->as != ANOR)
return 0;
if(p->reg != REGZERO && p->reg != NREG)
return 0;
if(p->from.type != D_REG || p->from.reg != REGZERO)
return 0;
if(p->to.type != D_REG || p->to.reg != REGZERO)
return 0;
return 1;
}
long
oprrr(int a)
{
switch(a) {
case AADD: return OP(4,0);
case AADDU: return OP(4,1);
case ASGT: return OP(5,2);
case ASGTU: return OP(5,3);
case AAND: return OP(4,4);
case AOR: return OP(4,5);
case AXOR: return OP(4,6);
case ASUB: return OP(4,2);
case ASUBU: return OP(4,3);
case ANOR: return OP(4,7);
case ASLL: return OP(0,4);
case ASRL: return OP(0,6);
case ASRA: return OP(0,7);
case AREM:
case ADIV: return OP(3,2);
case AREMU:
case ADIVU: return OP(3,3);
case AMUL: return OP(3,0);
case AMULU: return OP(3,1);
case AJMP: return OP(1,0);
case AJAL: return OP(1,1);
case ABREAK: return OP(1,5);
case ASYSCALL: return OP(1,4);
case ATLBP: return MMU(1,0);
case ATLBR: return MMU(0,1);
case ATLBWI: return MMU(0,2);
case ATLBWR: return MMU(0,6);
case ARFE: return MMU(2,0);
case ADIVF: return FPF(0,3);
case ADIVD: return FPD(0,3);
case AMULF: return FPF(0,2);
case AMULD: return FPD(0,2);
case ASUBF: return FPF(0,1);
case ASUBD: return FPD(0,1);
case AADDF: return FPF(0,0);
case AADDD: return FPD(0,0);
case AMOVFW: return FPF(4,4);
case AMOVDW: return FPD(4,4);
case AMOVWF: return FPW(4,0);
case AMOVDF: return FPD(4,0);
case AMOVWD: return FPW(4,1);
case AMOVFD: return FPF(4,1);
case AABSF: return FPF(0,5);
case AABSD: return FPD(0,5);
case AMOVF: return FPF(0,6);
case AMOVD: return FPD(0,6);
case ANEGF: return FPF(0,7);
case ANEGD: return FPD(0,7);
case ACMPEQF: return FPF(6,2);
case ACMPEQD: return FPD(6,2);
case ACMPGTF: return FPF(7,4);
case ACMPGTD: return FPD(7,4);
case ACMPGEF: return FPF(7,6);
case ACMPGED: return FPD(7,6);
case ADIVV: return OP(3,6);
case ADIVVU: return OP(3,7);
case AADDV: return OP(5,4);
case AADDVU: return OP(5,5);
}
diag("bad rrr %d", a);
return 0;
}
long
opirr(int a)
{
switch(a) {
case AADD: return SP(1,0);
case AADDU: return SP(1,1);
case ASGT: return SP(1,2);
case ASGTU: return SP(1,3);
case AAND: return SP(1,4);
case AOR: return SP(1,5);
case AXOR: return SP(1,6);
case ALAST: return SP(1,7);
case ASLL: return OP(0,0);
case ASRL: return OP(0,2);
case ASRA: return OP(0,3);
case AJMP: return SP(0,2);
case AJAL: return SP(0,3);
case ABEQ: return SP(0,4);
case ABNE: return SP(0,5);
case ABGEZ: return SP(0,1)|BCOND(0,1);
case ABGEZAL: return SP(0,1)|BCOND(2,1);
case ABGTZ: return SP(0,7);
case ABLEZ: return SP(0,6);
case ABLTZ: return SP(0,1)|BCOND(0,0);
case ABLTZAL: return SP(0,1)|BCOND(2,0);
case ABFPT: return SP(2,1)|(257<<16);
case ABFPF: return SP(2,1)|(256<<16);
case AMOVB:
case AMOVBU: return SP(5,0);
case AMOVH:
case AMOVHU: return SP(5,1);
case AMOVW: return SP(5,3);
case AMOVV: return SP(7,7);
case AMOVF: return SP(7,1);
case AMOVWL: return SP(5,2);
case AMOVWR: return SP(5,6);
case AMOVVL: return SP(5,4);
case AMOVVR: return SP(5,5);
case ABREAK: return SP(5,7);
case AMOVWL+ALAST: return SP(4,2);
case AMOVWR+ALAST: return SP(4,6);
case AMOVVL+ALAST: return SP(3,2);
case AMOVVR+ALAST: return SP(3,3);
case AMOVB+ALAST: return SP(4,0);
case AMOVBU+ALAST: return SP(4,4);
case AMOVH+ALAST: return SP(4,1);
case AMOVHU+ALAST: return SP(4,5);
case AMOVW+ALAST: return SP(4,3);
case AMOVV+ALAST: return SP(6,7);
case AMOVF+ALAST: return SP(6,1);
case ASLLV: return OP(7,0);
case ASRLV: return OP(7,2);
case ASRAV: return OP(7,3);
case ASLLV+ALAST: return OP(7,4);
case ASRLV+ALAST: return OP(7,6);
case ASRAV+ALAST: return OP(7,7);
case AADDV: return SP(3,0);
case AADDVU: return SP(3,1);
}
diag("bad irr %d", a);
abort();
return 0;
}
int
vshift(int a)
{
switch(a){
case ASLLV: return 1;
case ASRLV: return 1;
case ASRAV: return 1;
}
return 0;
}