mirror of
https://github.com/golang/go
synced 2024-11-20 04:44:40 -07:00
1d5dc4fd48
The type information is (and for years has been) included as an extra field in the address chunk of an instruction. Unfortunately, suppose there is a string at a+24(FP) and we have an instruction reading its length. It will say: MOVQ x+32(FP), AX and the type of *that* argument is int (not slice), because it is the length being read. This confuses the picture seen by debuggers and now, worse, by the garbage collector. Instead of attaching the type information to all uses, emit an explicit list of TYPE instructions with the information. The TYPE instructions are no-ops whose only role is to provide an address to attach type information to. For example, this function: func f(x, y, z int) (a, b string) { return } now compiles into: --- prog list "f" --- 0000 (/Users/rsc/x.go:3) TEXT f+0(SB),$0-56 0001 (/Users/rsc/x.go:3) LOCALS , 0002 (/Users/rsc/x.go:3) TYPE x+0(FP){int},$8 0003 (/Users/rsc/x.go:3) TYPE y+8(FP){int},$8 0004 (/Users/rsc/x.go:3) TYPE z+16(FP){int},$8 0005 (/Users/rsc/x.go:3) TYPE a+24(FP){string},$16 0006 (/Users/rsc/x.go:3) TYPE b+40(FP){string},$16 0007 (/Users/rsc/x.go:3) MOVQ $0,b+40(FP) 0008 (/Users/rsc/x.go:3) MOVQ $0,b+48(FP) 0009 (/Users/rsc/x.go:3) MOVQ $0,a+24(FP) 0010 (/Users/rsc/x.go:3) MOVQ $0,a+32(FP) 0011 (/Users/rsc/x.go:4) RET , The { } show the formerly hidden type information. The { } syntax is used when printing from within the gc compiler. It is not accepted by the assemblers. The same type information is now included on global variables: 0055 (/Users/rsc/x.go:15) GLOBL slice+0(SB){[]string},$24(AL*0) This more accurate type information fixes a bug in the garbage collector's precise heap collection. The linker only cares about globals right now, but having the local information should make things a little nicer for Carl in the future. Fixes #4907. R=ken2 CC=golang-dev https://golang.org/cl/7395056
772 lines
14 KiB
C
772 lines
14 KiB
C
// Copyright 2009 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
#undef EXTERN
|
|
#define EXTERN
|
|
#include <u.h>
|
|
#include <libc.h>
|
|
#include "gg.h"
|
|
#include "opt.h"
|
|
|
|
void
|
|
defframe(Prog *ptxt)
|
|
{
|
|
// fill in argument size
|
|
ptxt->to.type = D_CONST2;
|
|
ptxt->to.offset2 = rnd(curfn->type->argwid, widthptr);
|
|
|
|
// fill in final stack size
|
|
if(stksize > maxstksize)
|
|
maxstksize = stksize;
|
|
ptxt->to.offset = rnd(maxstksize+maxarg, widthptr);
|
|
maxstksize = 0;
|
|
}
|
|
|
|
// Sweep the prog list to mark any used nodes.
|
|
void
|
|
markautoused(Prog* p)
|
|
{
|
|
for (; p; p = p->link) {
|
|
if (p->as == ATYPE)
|
|
continue;
|
|
|
|
if (p->from.name == D_AUTO && p->from.node)
|
|
p->from.node->used = 1;
|
|
|
|
if (p->to.name == D_AUTO && p->to.node)
|
|
p->to.node->used = 1;
|
|
}
|
|
}
|
|
|
|
// Fixup instructions after compactframe has moved all autos around.
|
|
void
|
|
fixautoused(Prog* p)
|
|
{
|
|
Prog **lp;
|
|
|
|
for (lp=&p; (p=*lp) != P; ) {
|
|
if (p->as == ATYPE && p->from.node && p->from.name == D_AUTO && !p->from.node->used) {
|
|
*lp = p->link;
|
|
continue;
|
|
}
|
|
|
|
if (p->from.name == D_AUTO && p->from.node)
|
|
p->from.offset += p->from.node->stkdelta;
|
|
|
|
if (p->to.name == D_AUTO && p->to.node)
|
|
p->to.offset += p->to.node->stkdelta;
|
|
|
|
lp = &p->link;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* generate:
|
|
* call f
|
|
* proc=-1 normal call but no return
|
|
* proc=0 normal call
|
|
* proc=1 goroutine run in new proc
|
|
* proc=2 defer call save away stack
|
|
* proc=3 normal call to C pointer (not Go func value)
|
|
*/
|
|
void
|
|
ginscall(Node *f, int proc)
|
|
{
|
|
Prog *p;
|
|
Node n1, r, r1, con;
|
|
|
|
switch(proc) {
|
|
default:
|
|
fatal("ginscall: bad proc %d", proc);
|
|
break;
|
|
|
|
case 0: // normal call
|
|
case -1: // normal call but no return
|
|
if(f->op == ONAME && f->class == PFUNC) {
|
|
p = gins(ABL, N, f);
|
|
afunclit(&p->to, f);
|
|
if(proc == -1 || noreturn(p))
|
|
gins(AUNDEF, N, N);
|
|
break;
|
|
}
|
|
nodreg(&r, types[tptr], 7);
|
|
nodreg(&r1, types[tptr], 1);
|
|
gmove(f, &r);
|
|
r.op = OINDREG;
|
|
gmove(&r, &r1);
|
|
r.op = OREGISTER;
|
|
r1.op = OINDREG;
|
|
gins(ABL, &r, &r1);
|
|
break;
|
|
|
|
case 3: // normal call of c function pointer
|
|
gins(ABL, N, f);
|
|
break;
|
|
|
|
case 1: // call in new proc (go)
|
|
case 2: // deferred call (defer)
|
|
regalloc(&r, types[tptr], N);
|
|
p = gins(AMOVW, N, &r);
|
|
p->from.type = D_OREG;
|
|
p->from.reg = REGSP;
|
|
|
|
p = gins(AMOVW, &r, N);
|
|
p->to.type = D_OREG;
|
|
p->to.reg = REGSP;
|
|
p->to.offset = -12;
|
|
p->scond |= C_WBIT;
|
|
|
|
memset(&n1, 0, sizeof n1);
|
|
n1.op = OADDR;
|
|
n1.left = f;
|
|
gins(AMOVW, &n1, &r);
|
|
|
|
p = gins(AMOVW, &r, N);
|
|
p->to.type = D_OREG;
|
|
p->to.reg = REGSP;
|
|
p->to.offset = 8;
|
|
|
|
nodconst(&con, types[TINT32], argsize(f->type));
|
|
gins(AMOVW, &con, &r);
|
|
p = gins(AMOVW, &r, N);
|
|
p->to.type = D_OREG;
|
|
p->to.reg = REGSP;
|
|
p->to.offset = 4;
|
|
regfree(&r);
|
|
|
|
if(proc == 1)
|
|
ginscall(newproc, 0);
|
|
else
|
|
ginscall(deferproc, 0);
|
|
|
|
nodreg(&r, types[tptr], 1);
|
|
p = gins(AMOVW, N, N);
|
|
p->from.type = D_CONST;
|
|
p->from.reg = REGSP;
|
|
p->from.offset = 12;
|
|
p->to.reg = REGSP;
|
|
p->to.type = D_REG;
|
|
|
|
if(proc == 2) {
|
|
nodconst(&con, types[TINT32], 0);
|
|
p = gins(ACMP, &con, N);
|
|
p->reg = 0;
|
|
patch(gbranch(ABNE, T, -1), retpc);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* n is call to interface method.
|
|
* generate res = n.
|
|
*/
|
|
void
|
|
cgen_callinter(Node *n, Node *res, int proc)
|
|
{
|
|
int r;
|
|
Node *i, *f;
|
|
Node tmpi, nodo, nodr, nodsp;
|
|
Prog *p;
|
|
|
|
i = n->left;
|
|
if(i->op != ODOTINTER)
|
|
fatal("cgen_callinter: not ODOTINTER %O", i->op);
|
|
|
|
f = i->right; // field
|
|
if(f->op != ONAME)
|
|
fatal("cgen_callinter: not ONAME %O", f->op);
|
|
|
|
i = i->left; // interface
|
|
|
|
// Release res register during genlist and cgen,
|
|
// which might have their own function calls.
|
|
r = -1;
|
|
if(res != N && (res->op == OREGISTER || res->op == OINDREG)) {
|
|
r = res->val.u.reg;
|
|
reg[r]--;
|
|
}
|
|
|
|
if(!i->addable) {
|
|
tempname(&tmpi, i->type);
|
|
cgen(i, &tmpi);
|
|
i = &tmpi;
|
|
}
|
|
|
|
genlist(n->list); // args
|
|
if(r >= 0)
|
|
reg[r]++;
|
|
|
|
regalloc(&nodr, types[tptr], res);
|
|
regalloc(&nodo, types[tptr], &nodr);
|
|
nodo.op = OINDREG;
|
|
|
|
agen(i, &nodr); // REG = &inter
|
|
|
|
nodindreg(&nodsp, types[tptr], REGSP);
|
|
nodsp.xoffset = 4;
|
|
nodo.xoffset += widthptr;
|
|
cgen(&nodo, &nodsp); // 4(SP) = 4(REG) -- i.data
|
|
|
|
nodo.xoffset -= widthptr;
|
|
cgen(&nodo, &nodr); // REG = 0(REG) -- i.tab
|
|
|
|
nodo.xoffset = n->left->xoffset + 3*widthptr + 8;
|
|
|
|
if(proc == 0) {
|
|
// plain call: use direct c function pointer - more efficient
|
|
cgen(&nodo, &nodr); // REG = 20+offset(REG) -- i.tab->fun[f]
|
|
nodr.op = OINDREG;
|
|
proc = 3;
|
|
} else {
|
|
// go/defer. generate go func value.
|
|
p = gins(AMOVW, &nodo, &nodr);
|
|
p->from.type = D_CONST; // REG = &(20+offset(REG)) -- i.tab->fun[f]
|
|
}
|
|
|
|
// BOTCH nodr.type = fntype;
|
|
nodr.type = n->left->type;
|
|
ginscall(&nodr, proc);
|
|
|
|
regfree(&nodr);
|
|
regfree(&nodo);
|
|
|
|
setmaxarg(n->left->type);
|
|
}
|
|
|
|
/*
|
|
* generate function call;
|
|
* proc=0 normal call
|
|
* proc=1 goroutine run in new proc
|
|
* proc=2 defer call save away stack
|
|
*/
|
|
void
|
|
cgen_call(Node *n, int proc)
|
|
{
|
|
Type *t;
|
|
Node nod, afun;
|
|
|
|
if(n == N)
|
|
return;
|
|
|
|
if(n->left->ullman >= UINF) {
|
|
// if name involves a fn call
|
|
// precompute the address of the fn
|
|
tempname(&afun, types[tptr]);
|
|
cgen(n->left, &afun);
|
|
}
|
|
|
|
genlist(n->list); // assign the args
|
|
t = n->left->type;
|
|
|
|
setmaxarg(t);
|
|
|
|
// call tempname pointer
|
|
if(n->left->ullman >= UINF) {
|
|
regalloc(&nod, types[tptr], N);
|
|
cgen_as(&nod, &afun);
|
|
nod.type = t;
|
|
ginscall(&nod, proc);
|
|
regfree(&nod);
|
|
goto ret;
|
|
}
|
|
|
|
// call pointer
|
|
if(n->left->op != ONAME || n->left->class != PFUNC) {
|
|
regalloc(&nod, types[tptr], N);
|
|
cgen_as(&nod, n->left);
|
|
nod.type = t;
|
|
ginscall(&nod, proc);
|
|
regfree(&nod);
|
|
goto ret;
|
|
}
|
|
|
|
// call direct
|
|
n->left->method = 1;
|
|
ginscall(n->left, proc);
|
|
|
|
|
|
ret:
|
|
;
|
|
}
|
|
|
|
/*
|
|
* call to n has already been generated.
|
|
* generate:
|
|
* res = return value from call.
|
|
*/
|
|
void
|
|
cgen_callret(Node *n, Node *res)
|
|
{
|
|
Node nod;
|
|
Type *fp, *t;
|
|
Iter flist;
|
|
|
|
t = n->left->type;
|
|
if(t->etype == TPTR32 || t->etype == TPTR64)
|
|
t = t->type;
|
|
|
|
fp = structfirst(&flist, getoutarg(t));
|
|
if(fp == T)
|
|
fatal("cgen_callret: nil");
|
|
|
|
memset(&nod, 0, sizeof(nod));
|
|
nod.op = OINDREG;
|
|
nod.val.u.reg = REGSP;
|
|
nod.addable = 1;
|
|
|
|
nod.xoffset = fp->width + 4; // +4: saved lr at 0(SP)
|
|
nod.type = fp->type;
|
|
cgen_as(res, &nod);
|
|
}
|
|
|
|
/*
|
|
* call to n has already been generated.
|
|
* generate:
|
|
* res = &return value from call.
|
|
*/
|
|
void
|
|
cgen_aret(Node *n, Node *res)
|
|
{
|
|
Node nod1, nod2;
|
|
Type *fp, *t;
|
|
Iter flist;
|
|
|
|
t = n->left->type;
|
|
if(isptr[t->etype])
|
|
t = t->type;
|
|
|
|
fp = structfirst(&flist, getoutarg(t));
|
|
if(fp == T)
|
|
fatal("cgen_aret: nil");
|
|
|
|
memset(&nod1, 0, sizeof(nod1));
|
|
nod1.op = OINDREG;
|
|
nod1.val.u.reg = REGSP;
|
|
nod1.addable = 1;
|
|
|
|
nod1.xoffset = fp->width + 4; // +4: saved lr at 0(SP)
|
|
nod1.type = fp->type;
|
|
|
|
if(res->op != OREGISTER) {
|
|
regalloc(&nod2, types[tptr], res);
|
|
agen(&nod1, &nod2);
|
|
gins(AMOVW, &nod2, res);
|
|
regfree(&nod2);
|
|
} else
|
|
agen(&nod1, res);
|
|
}
|
|
|
|
/*
|
|
* generate return.
|
|
* n->left is assignments to return values.
|
|
*/
|
|
void
|
|
cgen_ret(Node *n)
|
|
{
|
|
genlist(n->list); // copy out args
|
|
if(hasdefer || curfn->exit)
|
|
gjmp(retpc);
|
|
else
|
|
gins(ARET, N, N);
|
|
}
|
|
|
|
/*
|
|
* generate += *= etc.
|
|
*/
|
|
void
|
|
cgen_asop(Node *n)
|
|
{
|
|
Node n1, n2, n3, n4;
|
|
Node *nl, *nr;
|
|
Prog *p1;
|
|
Addr addr;
|
|
int a, w;
|
|
|
|
nl = n->left;
|
|
nr = n->right;
|
|
|
|
if(nr->ullman >= UINF && nl->ullman >= UINF) {
|
|
tempname(&n1, nr->type);
|
|
cgen(nr, &n1);
|
|
n2 = *n;
|
|
n2.right = &n1;
|
|
cgen_asop(&n2);
|
|
goto ret;
|
|
}
|
|
|
|
if(!isint[nl->type->etype])
|
|
goto hard;
|
|
if(!isint[nr->type->etype])
|
|
goto hard;
|
|
if(is64(nl->type) || is64(nr->type))
|
|
goto hard64;
|
|
|
|
switch(n->etype) {
|
|
case OADD:
|
|
case OSUB:
|
|
case OXOR:
|
|
case OAND:
|
|
case OOR:
|
|
a = optoas(n->etype, nl->type);
|
|
if(nl->addable) {
|
|
if(smallintconst(nr))
|
|
n3 = *nr;
|
|
else {
|
|
regalloc(&n3, nr->type, N);
|
|
cgen(nr, &n3);
|
|
}
|
|
regalloc(&n2, nl->type, N);
|
|
cgen(nl, &n2);
|
|
gins(a, &n3, &n2);
|
|
cgen(&n2, nl);
|
|
regfree(&n2);
|
|
if(n3.op != OLITERAL)
|
|
regfree(&n3);
|
|
goto ret;
|
|
}
|
|
if(nr->ullman < UINF)
|
|
if(sudoaddable(a, nl, &addr, &w)) {
|
|
w = optoas(OAS, nl->type);
|
|
regalloc(&n2, nl->type, N);
|
|
p1 = gins(w, N, &n2);
|
|
p1->from = addr;
|
|
regalloc(&n3, nr->type, N);
|
|
cgen(nr, &n3);
|
|
gins(a, &n3, &n2);
|
|
p1 = gins(w, &n2, N);
|
|
p1->to = addr;
|
|
regfree(&n2);
|
|
regfree(&n3);
|
|
sudoclean();
|
|
goto ret;
|
|
}
|
|
}
|
|
|
|
hard:
|
|
n2.op = 0;
|
|
n1.op = 0;
|
|
if(nr->op == OLITERAL) {
|
|
// don't allocate a register for literals.
|
|
} else if(nr->ullman >= nl->ullman || nl->addable) {
|
|
regalloc(&n2, nr->type, N);
|
|
cgen(nr, &n2);
|
|
nr = &n2;
|
|
} else {
|
|
tempname(&n2, nr->type);
|
|
cgen(nr, &n2);
|
|
nr = &n2;
|
|
}
|
|
if(!nl->addable) {
|
|
igen(nl, &n1, N);
|
|
nl = &n1;
|
|
}
|
|
|
|
n3 = *n;
|
|
n3.left = nl;
|
|
n3.right = nr;
|
|
n3.op = n->etype;
|
|
|
|
regalloc(&n4, nl->type, N);
|
|
cgen(&n3, &n4);
|
|
gmove(&n4, nl);
|
|
|
|
if(n1.op)
|
|
regfree(&n1);
|
|
if(n2.op == OREGISTER)
|
|
regfree(&n2);
|
|
regfree(&n4);
|
|
goto ret;
|
|
|
|
hard64:
|
|
if(nr->ullman > nl->ullman) {
|
|
tempname(&n2, nr->type);
|
|
cgen(nr, &n2);
|
|
igen(nl, &n1, N);
|
|
} else {
|
|
igen(nl, &n1, N);
|
|
tempname(&n2, nr->type);
|
|
cgen(nr, &n2);
|
|
}
|
|
|
|
n3 = *n;
|
|
n3.left = &n1;
|
|
n3.right = &n2;
|
|
n3.op = n->etype;
|
|
|
|
cgen(&n3, &n1);
|
|
|
|
ret:
|
|
;
|
|
}
|
|
|
|
int
|
|
samereg(Node *a, Node *b)
|
|
{
|
|
if(a->op != OREGISTER)
|
|
return 0;
|
|
if(b->op != OREGISTER)
|
|
return 0;
|
|
if(a->val.u.reg != b->val.u.reg)
|
|
return 0;
|
|
return 1;
|
|
}
|
|
|
|
/*
|
|
* generate high multiply
|
|
* res = (nl * nr) >> wordsize
|
|
*/
|
|
void
|
|
cgen_hmul(Node *nl, Node *nr, Node *res)
|
|
{
|
|
int w;
|
|
Node n1, n2, *tmp;
|
|
Type *t;
|
|
Prog *p;
|
|
|
|
if(nl->ullman < nr->ullman) {
|
|
tmp = nl;
|
|
nl = nr;
|
|
nr = tmp;
|
|
}
|
|
t = nl->type;
|
|
w = t->width * 8;
|
|
regalloc(&n1, t, res);
|
|
cgen(nl, &n1);
|
|
regalloc(&n2, t, N);
|
|
cgen(nr, &n2);
|
|
switch(simtype[t->etype]) {
|
|
case TINT8:
|
|
case TINT16:
|
|
gins(optoas(OMUL, t), &n2, &n1);
|
|
gshift(AMOVW, &n1, SHIFT_AR, w, &n1);
|
|
break;
|
|
case TUINT8:
|
|
case TUINT16:
|
|
gins(optoas(OMUL, t), &n2, &n1);
|
|
gshift(AMOVW, &n1, SHIFT_LR, w, &n1);
|
|
break;
|
|
case TINT32:
|
|
case TUINT32:
|
|
// perform a long multiplication.
|
|
if(issigned[t->etype])
|
|
p = gins(AMULL, &n2, N);
|
|
else
|
|
p = gins(AMULLU, &n2, N);
|
|
// n2 * n1 -> (n1 n2)
|
|
p->reg = n1.val.u.reg;
|
|
p->to.type = D_REGREG;
|
|
p->to.reg = n1.val.u.reg;
|
|
p->to.offset = n2.val.u.reg;
|
|
break;
|
|
default:
|
|
fatal("cgen_hmul %T", t);
|
|
break;
|
|
}
|
|
cgen(&n1, res);
|
|
regfree(&n1);
|
|
regfree(&n2);
|
|
}
|
|
|
|
/*
|
|
* generate shift according to op, one of:
|
|
* res = nl << nr
|
|
* res = nl >> nr
|
|
*/
|
|
void
|
|
cgen_shift(int op, int bounded, Node *nl, Node *nr, Node *res)
|
|
{
|
|
Node n1, n2, n3, nt, t, lo, hi;
|
|
int w, v;
|
|
Prog *p1, *p2, *p3;
|
|
Type *tr;
|
|
uvlong sc;
|
|
|
|
USED(bounded);
|
|
if(nl->type->width > 4)
|
|
fatal("cgen_shift %T", nl->type);
|
|
|
|
w = nl->type->width * 8;
|
|
|
|
if(op == OLROT) {
|
|
v = mpgetfix(nr->val.u.xval);
|
|
regalloc(&n1, nl->type, res);
|
|
if(w == 32) {
|
|
cgen(nl, &n1);
|
|
gshift(AMOVW, &n1, SHIFT_RR, w-v, &n1);
|
|
} else {
|
|
regalloc(&n2, nl->type, N);
|
|
cgen(nl, &n2);
|
|
gshift(AMOVW, &n2, SHIFT_LL, v, &n1);
|
|
gshift(AORR, &n2, SHIFT_LR, w-v, &n1);
|
|
regfree(&n2);
|
|
}
|
|
gmove(&n1, res);
|
|
regfree(&n1);
|
|
return;
|
|
}
|
|
|
|
if(nr->op == OLITERAL) {
|
|
regalloc(&n1, nl->type, res);
|
|
cgen(nl, &n1);
|
|
sc = mpgetfix(nr->val.u.xval);
|
|
if(sc == 0) {
|
|
// nothing to do
|
|
} else if(sc >= nl->type->width*8) {
|
|
if(op == ORSH && issigned[nl->type->etype])
|
|
gshift(AMOVW, &n1, SHIFT_AR, w, &n1);
|
|
else
|
|
gins(AEOR, &n1, &n1);
|
|
} else {
|
|
if(op == ORSH && issigned[nl->type->etype])
|
|
gshift(AMOVW, &n1, SHIFT_AR, sc, &n1);
|
|
else if(op == ORSH)
|
|
gshift(AMOVW, &n1, SHIFT_LR, sc, &n1);
|
|
else // OLSH
|
|
gshift(AMOVW, &n1, SHIFT_LL, sc, &n1);
|
|
}
|
|
gmove(&n1, res);
|
|
regfree(&n1);
|
|
return;
|
|
}
|
|
|
|
tr = nr->type;
|
|
if(tr->width > 4) {
|
|
tempname(&nt, nr->type);
|
|
if(nl->ullman >= nr->ullman) {
|
|
regalloc(&n2, nl->type, res);
|
|
cgen(nl, &n2);
|
|
cgen(nr, &nt);
|
|
n1 = nt;
|
|
} else {
|
|
cgen(nr, &nt);
|
|
regalloc(&n2, nl->type, res);
|
|
cgen(nl, &n2);
|
|
}
|
|
split64(&nt, &lo, &hi);
|
|
regalloc(&n1, types[TUINT32], N);
|
|
regalloc(&n3, types[TUINT32], N);
|
|
gmove(&lo, &n1);
|
|
gmove(&hi, &n3);
|
|
splitclean();
|
|
gins(ATST, &n3, N);
|
|
nodconst(&t, types[TUINT32], w);
|
|
p1 = gins(AMOVW, &t, &n1);
|
|
p1->scond = C_SCOND_NE;
|
|
tr = types[TUINT32];
|
|
regfree(&n3);
|
|
} else {
|
|
if(nl->ullman >= nr->ullman) {
|
|
regalloc(&n2, nl->type, res);
|
|
cgen(nl, &n2);
|
|
regalloc(&n1, nr->type, N);
|
|
cgen(nr, &n1);
|
|
} else {
|
|
regalloc(&n1, nr->type, N);
|
|
cgen(nr, &n1);
|
|
regalloc(&n2, nl->type, res);
|
|
cgen(nl, &n2);
|
|
}
|
|
}
|
|
|
|
// test for shift being 0
|
|
gins(ATST, &n1, N);
|
|
p3 = gbranch(ABEQ, T, -1);
|
|
|
|
// test and fix up large shifts
|
|
// TODO: if(!bounded), don't emit some of this.
|
|
regalloc(&n3, tr, N);
|
|
nodconst(&t, types[TUINT32], w);
|
|
gmove(&t, &n3);
|
|
gcmp(ACMP, &n1, &n3);
|
|
if(op == ORSH) {
|
|
if(issigned[nl->type->etype]) {
|
|
p1 = gshift(AMOVW, &n2, SHIFT_AR, w-1, &n2);
|
|
p2 = gregshift(AMOVW, &n2, SHIFT_AR, &n1, &n2);
|
|
} else {
|
|
p1 = gins(AEOR, &n2, &n2);
|
|
p2 = gregshift(AMOVW, &n2, SHIFT_LR, &n1, &n2);
|
|
}
|
|
p1->scond = C_SCOND_HS;
|
|
p2->scond = C_SCOND_LO;
|
|
} else {
|
|
p1 = gins(AEOR, &n2, &n2);
|
|
p2 = gregshift(AMOVW, &n2, SHIFT_LL, &n1, &n2);
|
|
p1->scond = C_SCOND_HS;
|
|
p2->scond = C_SCOND_LO;
|
|
}
|
|
regfree(&n3);
|
|
|
|
patch(p3, pc);
|
|
gmove(&n2, res);
|
|
|
|
regfree(&n1);
|
|
regfree(&n2);
|
|
}
|
|
|
|
void
|
|
clearfat(Node *nl)
|
|
{
|
|
uint32 w, c, q;
|
|
Node dst, nc, nz, end;
|
|
Prog *p, *pl;
|
|
|
|
/* clear a fat object */
|
|
if(debug['g'])
|
|
dump("\nclearfat", nl);
|
|
|
|
|
|
w = nl->type->width;
|
|
// Avoid taking the address for simple enough types.
|
|
if(componentgen(N, nl))
|
|
return;
|
|
|
|
c = w % 4; // bytes
|
|
q = w / 4; // quads
|
|
|
|
regalloc(&dst, types[tptr], N);
|
|
agen(nl, &dst);
|
|
nodconst(&nc, types[TUINT32], 0);
|
|
regalloc(&nz, types[TUINT32], 0);
|
|
cgen(&nc, &nz);
|
|
|
|
if(q >= 4) {
|
|
regalloc(&end, types[tptr], N);
|
|
p = gins(AMOVW, &dst, &end);
|
|
p->from.type = D_CONST;
|
|
p->from.offset = q*4;
|
|
|
|
p = gins(AMOVW, &nz, &dst);
|
|
p->to.type = D_OREG;
|
|
p->to.offset = 4;
|
|
p->scond |= C_PBIT;
|
|
pl = p;
|
|
|
|
p = gins(ACMP, &dst, N);
|
|
raddr(&end, p);
|
|
patch(gbranch(ABNE, T, 0), pl);
|
|
|
|
regfree(&end);
|
|
} else
|
|
while(q > 0) {
|
|
p = gins(AMOVW, &nz, &dst);
|
|
p->to.type = D_OREG;
|
|
p->to.offset = 4;
|
|
p->scond |= C_PBIT;
|
|
//print("1. %P\n", p);
|
|
q--;
|
|
}
|
|
|
|
while(c > 0) {
|
|
p = gins(AMOVBU, &nz, &dst);
|
|
p->to.type = D_OREG;
|
|
p->to.offset = 1;
|
|
p->scond |= C_PBIT;
|
|
//print("2. %P\n", p);
|
|
c--;
|
|
}
|
|
regfree(&dst);
|
|
regfree(&nz);
|
|
}
|