blob: 450ed00306eac45cb153bd97d183ef9f9a62d6fe [file] [log] [blame]
// cmd/9l/noop.c, cmd/9l/pass.c, cmd/9l/span.c from Vita Nuova.
//
// Copyright © 1994-1999 Lucent Technologies Inc. All rights reserved.
// Portions Copyright © 1995-1997 C H Forsyth (forsyth@terzarima.net)
// Portions Copyright © 1997-1999 Vita Nuova Limited
// Portions Copyright © 2000-2008 Vita Nuova Holdings Limited (www.vitanuova.com)
// Portions Copyright © 2004,2006 Bruce Ellis
// Portions Copyright © 2005-2007 C H Forsyth (forsyth@terzarima.net)
// Revisions Copyright © 2000-2008 Lucent Technologies Inc. and others
// Portions Copyright © 2009 The Go Authors. All rights reserved.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <link.h>
#include "../cmd/9l/9.out.h"
#include "../runtime/stack.h"
#include "../runtime/funcdata.h"
static void
progedit(Link *ctxt, Prog *p)
{
char literal[64];
LSym *s;
USED(ctxt);
p->from.class = 0;
p->to.class = 0;
// Rewrite BR/BL to symbol as TYPE_BRANCH.
switch(p->as) {
case ABR:
case ABL:
case ARETURN:
case ADUFFZERO:
case ADUFFCOPY:
if(p->to.sym != nil)
p->to.type = TYPE_BRANCH;
break;
}
// Rewrite float constants to values stored in memory.
switch(p->as) {
case AFMOVS:
if(p->from.type == TYPE_FCONST) {
uint32 i32;
float32 f32;
f32 = p->from.u.dval;
memmove(&i32, &f32, 4);
sprint(literal, "$f32.%08ux", i32);
s = linklookup(ctxt, literal, 0);
s->size = 4;
p->from.type = TYPE_MEM;
p->from.sym = s;
p->from.name = NAME_EXTERN;
p->from.offset = 0;
}
break;
case AFMOVD:
if(p->from.type == TYPE_FCONST) {
uint64 i64;
memmove(&i64, &p->from.u.dval, 8);
sprint(literal, "$f64.%016llux", i64);
s = linklookup(ctxt, literal, 0);
s->size = 8;
p->from.type = TYPE_MEM;
p->from.sym = s;
p->from.name = NAME_EXTERN;
p->from.offset = 0;
}
break;
case AMOVD:
// Put >32-bit constants in memory and load them
if(p->from.type == TYPE_CONST && p->from.name == NAME_NONE && p->from.reg == 0 && (int32)p->from.offset != p->from.offset) {
sprint(literal, "$i64.%016llux", (uvlong)p->from.offset);
s = linklookup(ctxt, literal, 0);
s->size = 8;
p->from.type = TYPE_MEM;
p->from.sym = s;
p->from.name = NAME_EXTERN;
p->from.offset = 0;
}
}
// Rewrite SUB constants into ADD.
switch(p->as) {
case ASUBC:
if(p->from.type == TYPE_CONST) {
p->from.offset = -p->from.offset;
p->as = AADDC;
}
break;
case ASUBCCC:
if(p->from.type == TYPE_CONST) {
p->from.offset = -p->from.offset;
p->as = AADDCCC;
}
break;
case ASUB:
if(p->from.type == TYPE_CONST) {
p->from.offset = -p->from.offset;
p->as = AADD;
}
break;
}
}
static Prog* stacksplit(Link*, Prog*, int32, int);
static void
preprocess(Link *ctxt, LSym *cursym)
{
Prog *p, *q, *p1, *p2, *q1;
int o, mov, aoffset;
vlong textstksiz;
int32 autosize;
if(ctxt->symmorestack[0] == nil) {
ctxt->symmorestack[0] = linklookup(ctxt, "runtime.morestack", 0);
ctxt->symmorestack[1] = linklookup(ctxt, "runtime.morestack_noctxt", 0);
// TODO(minux): add morestack short-cuts with small fixed frame-size.
}
ctxt->cursym = cursym;
if(cursym->text == nil || cursym->text->link == nil)
return;
p = cursym->text;
textstksiz = p->to.offset;
cursym->args = p->to.u.argsize;
cursym->locals = textstksiz;
/*
* find leaf subroutines
* strip NOPs
* expand RET
* expand BECOME pseudo
*/
if(ctxt->debugvlog)
Bprint(ctxt->bso, "%5.2f noops\n", cputime());
Bflush(ctxt->bso);
q = nil;
for(p = cursym->text; p != nil; p = p->link) {
switch(p->as) {
/* too hard, just leave alone */
case ATEXT:
q = p;
p->mark |= LABEL|LEAF|SYNC;
if(p->link)
p->link->mark |= LABEL;
break;
case ANOR:
q = p;
if(p->to.type == TYPE_REG)
if(p->to.reg == REGZERO)
p->mark |= LABEL|SYNC;
break;
case ALWAR:
case ASTWCCC:
case AECIWX:
case AECOWX:
case AEIEIO:
case AICBI:
case AISYNC:
case ATLBIE:
case ATLBIEL:
case ASLBIA:
case ASLBIE:
case ASLBMFEE:
case ASLBMFEV:
case ASLBMTE:
case ADCBF:
case ADCBI:
case ADCBST:
case ADCBT:
case ADCBTST:
case ADCBZ:
case ASYNC:
case ATLBSYNC:
case APTESYNC:
case ATW:
case AWORD:
case ARFI:
case ARFCI:
case ARFID:
case AHRFID:
q = p;
p->mark |= LABEL|SYNC;
continue;
case AMOVW:
case AMOVWZ:
case AMOVD:
q = p;
if(p->from.reg >= REG_SPECIAL || p->to.reg >= REG_SPECIAL)
p->mark |= LABEL|SYNC;
continue;
case AFABS:
case AFABSCC:
case AFADD:
case AFADDCC:
case AFCTIW:
case AFCTIWCC:
case AFCTIWZ:
case AFCTIWZCC:
case AFDIV:
case AFDIVCC:
case AFMADD:
case AFMADDCC:
case AFMOVD:
case AFMOVDU:
/* case AFMOVDS: */
case AFMOVS:
case AFMOVSU:
/* case AFMOVSD: */
case AFMSUB:
case AFMSUBCC:
case AFMUL:
case AFMULCC:
case AFNABS:
case AFNABSCC:
case AFNEG:
case AFNEGCC:
case AFNMADD:
case AFNMADDCC:
case AFNMSUB:
case AFNMSUBCC:
case AFRSP:
case AFRSPCC:
case AFSUB:
case AFSUBCC:
q = p;
p->mark |= FLOAT;
continue;
case ABL:
case ABCL:
case ADUFFZERO:
case ADUFFCOPY:
cursym->text->mark &= ~LEAF;
case ABC:
case ABEQ:
case ABGE:
case ABGT:
case ABLE:
case ABLT:
case ABNE:
case ABR:
case ABVC:
case ABVS:
p->mark |= BRANCH;
q = p;
q1 = p->pcond;
if(q1 != nil) {
while(q1->as == ANOP) {
q1 = q1->link;
p->pcond = q1;
}
if(!(q1->mark & LEAF))
q1->mark |= LABEL;
} else
p->mark |= LABEL;
q1 = p->link;
if(q1 != nil)
q1->mark |= LABEL;
continue;
case AFCMPO:
case AFCMPU:
q = p;
p->mark |= FCMP|FLOAT;
continue;
case ARETURN:
q = p;
if(p->link != nil)
p->link->mark |= LABEL;
continue;
case ANOP:
q1 = p->link;
q->link = q1; /* q is non-nop */
q1->mark |= p->mark;
continue;
default:
q = p;
continue;
}
}
autosize = 0;
for(p = cursym->text; p != nil; p = p->link) {
o = p->as;
switch(o) {
case ATEXT:
mov = AMOVD;
aoffset = 0;
autosize = textstksiz + 8;
if((p->mark & LEAF) && autosize <= 8)
autosize = 0;
else
if(autosize & 4)
autosize += 4;
p->to.offset = autosize-8;
if(!(p->from3.offset & NOSPLIT))
p = stacksplit(ctxt, p, autosize, !(cursym->text->from3.offset&NEEDCTXT)); // emit split check
q = p;
if(autosize) {
/* use MOVDU to adjust R1 when saving R31, if autosize is small */
if(!(cursym->text->mark & LEAF) && autosize >= -BIG && autosize <= BIG) {
mov = AMOVDU;
aoffset = -autosize;
} else {
q = appendp(ctxt, p);
q->as = AADD;
q->lineno = p->lineno;
q->from.type = TYPE_CONST;
q->from.offset = -autosize;
q->to.type = TYPE_REG;
q->to.reg = REGSP;
q->spadj = +autosize;
}
} else
if(!(cursym->text->mark & LEAF)) {
if(ctxt->debugvlog) {
Bprint(ctxt->bso, "save suppressed in: %s\n",
cursym->name);
Bflush(ctxt->bso);
}
cursym->text->mark |= LEAF;
}
if(cursym->text->mark & LEAF) {
cursym->leaf = 1;
break;
}
q = appendp(ctxt, q);
q->as = AMOVD;
q->lineno = p->lineno;
q->from.type = TYPE_REG;
q->from.reg = REG_LR;
q->to.type = TYPE_REG;
q->to.reg = REGTMP;
q = appendp(ctxt, q);
q->as = mov;
q->lineno = p->lineno;
q->from.type = TYPE_REG;
q->from.reg = REGTMP;
q->to.type = TYPE_MEM;
q->to.offset = aoffset;
q->to.reg = REGSP;
if(q->as == AMOVDU)
q->spadj = -aoffset;
if(cursym->text->from3.offset & WRAPPER) {
// if(g->panic != nil && g->panic->argp == FP) g->panic->argp = bottom-of-frame
//
// MOVD g_panic(g), R3
// CMP R0, R3
// BEQ end
// MOVD panic_argp(R3), R4
// ADD $(autosize+8), R1, R5
// CMP R4, R5
// BNE end
// ADD $8, R1, R6
// MOVD R6, panic_argp(R3)
// end:
// NOP
//
// The NOP is needed to give the jumps somewhere to land.
// It is a liblink NOP, not a ppc64 NOP: it encodes to 0 instruction bytes.
q = appendp(ctxt, q);
q->as = AMOVD;
q->from.type = TYPE_MEM;
q->from.reg = REGG;
q->from.offset = 4*ctxt->arch->ptrsize; // G.panic
q->to.type = TYPE_REG;
q->to.reg = REG_R3;
q = appendp(ctxt, q);
q->as = ACMP;
q->from.type = TYPE_REG;
q->from.reg = REG_R0;
q->to.type = TYPE_REG;
q->to.reg = REG_R3;
q = appendp(ctxt, q);
q->as = ABEQ;
q->to.type = TYPE_BRANCH;
p1 = q;
q = appendp(ctxt, q);
q->as = AMOVD;
q->from.type = TYPE_MEM;
q->from.reg = REG_R3;
q->from.offset = 0; // Panic.argp
q->to.type = TYPE_REG;
q->to.reg = REG_R4;
q = appendp(ctxt, q);
q->as = AADD;
q->from.type = TYPE_CONST;
q->from.offset = autosize+8;
q->reg = REGSP;
q->to.type = TYPE_REG;
q->to.reg = REG_R5;
q = appendp(ctxt, q);
q->as = ACMP;
q->from.type = TYPE_REG;
q->from.reg = REG_R4;
q->to.type = TYPE_REG;
q->to.reg = REG_R5;
q = appendp(ctxt, q);
q->as = ABNE;
q->to.type = TYPE_BRANCH;
p2 = q;
q = appendp(ctxt, q);
q->as = AADD;
q->from.type = TYPE_CONST;
q->from.offset = 8;
q->reg = REGSP;
q->to.type = TYPE_REG;
q->to.reg = REG_R6;
q = appendp(ctxt, q);
q->as = AMOVD;
q->from.type = TYPE_REG;
q->from.reg = REG_R6;
q->to.type = TYPE_MEM;
q->to.reg = REG_R3;
q->to.offset = 0; // Panic.argp
q = appendp(ctxt, q);
q->as = ANOP;
p1->pcond = q;
p2->pcond = q;
}
break;
case ARETURN:
if(p->from.type == TYPE_CONST) {
ctxt->diag("using BECOME (%P) is not supported!", p);
break;
}
if(p->to.sym) { // retjmp
p->as = ABR;
p->to.type = TYPE_BRANCH;
break;
}
if(cursym->text->mark & LEAF) {
if(!autosize) {
p->as = ABR;
p->from = zprog.from;
p->to.type = TYPE_REG;
p->to.reg = REG_LR;
p->mark |= BRANCH;
break;
}
p->as = AADD;
p->from.type = TYPE_CONST;
p->from.offset = autosize;
p->to.type = TYPE_REG;
p->to.reg = REGSP;
p->spadj = -autosize;
q = emallocz(sizeof(Prog));
q->as = ABR;
q->lineno = p->lineno;
q->to.type = TYPE_REG;
q->to.reg = REG_LR;
q->mark |= BRANCH;
q->spadj = +autosize;
q->link = p->link;
p->link = q;
break;
}
p->as = AMOVD;
p->from.type = TYPE_MEM;
p->from.offset = 0;
p->from.reg = REGSP;
p->to.type = TYPE_REG;
p->to.reg = REGTMP;
q = emallocz(sizeof(Prog));
q->as = AMOVD;
q->lineno = p->lineno;
q->from.type = TYPE_REG;
q->from.reg = REGTMP;
q->to.type = TYPE_REG;
q->to.reg = REG_LR;
q->link = p->link;
p->link = q;
p = q;
if(0) {
// Debug bad returns
q = emallocz(sizeof(Prog));
q->as = AMOVD;
q->lineno = p->lineno;
q->from.type = TYPE_MEM;
q->from.offset = 0;
q->from.reg = REGTMP;
q->to.type = TYPE_REG;
q->to.reg = REGTMP;
q->link = p->link;
p->link = q;
p = q;
}
if(autosize) {
q = emallocz(sizeof(Prog));
q->as = AADD;
q->lineno = p->lineno;
q->from.type = TYPE_CONST;
q->from.offset = autosize;
q->to.type = TYPE_REG;
q->to.reg = REGSP;
q->spadj = -autosize;
q->link = p->link;
p->link = q;
}
q1 = emallocz(sizeof(Prog));
q1->as = ABR;
q1->lineno = p->lineno;
q1->to.type = TYPE_REG;
q1->to.reg = REG_LR;
q1->mark |= BRANCH;
q1->spadj = +autosize;
q1->link = q->link;
q->link = q1;
break;
case AADD:
if(p->to.type == TYPE_REG && p->to.reg == REGSP && p->from.type == TYPE_CONST)
p->spadj = -p->from.offset;
break;
}
}
/*
// instruction scheduling
if(debug['Q'] == 0)
return;
curtext = nil;
q = nil; // p - 1
q1 = firstp; // top of block
o = 0; // count of instructions
for(p = firstp; p != nil; p = p1) {
p1 = p->link;
o++;
if(p->mark & NOSCHED){
if(q1 != p){
sched(q1, q);
}
for(; p != nil; p = p->link){
if(!(p->mark & NOSCHED))
break;
q = p;
}
p1 = p;
q1 = p;
o = 0;
continue;
}
if(p->mark & (LABEL|SYNC)) {
if(q1 != p)
sched(q1, q);
q1 = p;
o = 1;
}
if(p->mark & (BRANCH|SYNC)) {
sched(q1, p);
q1 = p1;
o = 0;
}
if(o >= NSCHED) {
sched(q1, p);
q1 = p1;
o = 0;
}
q = p;
}
*/
}
static Prog*
stacksplit(Link *ctxt, Prog *p, int32 framesize, int noctxt)
{
Prog *q, *q1;
// MOVD g_stackguard(g), R3
p = appendp(ctxt, p);
p->as = AMOVD;
p->from.type = TYPE_MEM;
p->from.reg = REGG;
p->from.offset = 2*ctxt->arch->ptrsize; // G.stackguard0
if(ctxt->cursym->cfunc)
p->from.offset = 3*ctxt->arch->ptrsize; // G.stackguard1
p->to.type = TYPE_REG;
p->to.reg = REG_R3;
q = nil;
if(framesize <= StackSmall) {
// small stack: SP < stackguard
// CMP stackguard, SP
p = appendp(ctxt, p);
p->as = ACMPU;
p->from.type = TYPE_REG;
p->from.reg = REG_R3;
p->to.type = TYPE_REG;
p->to.reg = REGSP;
} else if(framesize <= StackBig) {
// large stack: SP-framesize < stackguard-StackSmall
// ADD $-framesize, SP, R4
// CMP stackguard, R4
p = appendp(ctxt, p);
p->as = AADD;
p->from.type = TYPE_CONST;
p->from.offset = -framesize;
p->reg = REGSP;
p->to.type = TYPE_REG;
p->to.reg = REG_R4;
p = appendp(ctxt, p);
p->as = ACMPU;
p->from.type = TYPE_REG;
p->from.reg = REG_R3;
p->to.type = TYPE_REG;
p->to.reg = REG_R4;
} else {
// Such a large stack we need to protect against wraparound.
// If SP is close to zero:
// SP-stackguard+StackGuard <= framesize + (StackGuard-StackSmall)
// The +StackGuard on both sides is required to keep the left side positive:
// SP is allowed to be slightly below stackguard. See stack.h.
//
// Preemption sets stackguard to StackPreempt, a very large value.
// That breaks the math above, so we have to check for that explicitly.
// // stackguard is R3
// CMP R3, $StackPreempt
// BEQ label-of-call-to-morestack
// ADD $StackGuard, SP, R4
// SUB R3, R4
// MOVD $(framesize+(StackGuard-StackSmall)), R31
// CMPU R31, R4
p = appendp(ctxt, p);
p->as = ACMP;
p->from.type = TYPE_REG;
p->from.reg = REG_R3;
p->to.type = TYPE_CONST;
p->to.offset = StackPreempt;
q = p = appendp(ctxt, p);
p->as = ABEQ;
p->to.type = TYPE_BRANCH;
p = appendp(ctxt, p);
p->as = AADD;
p->from.type = TYPE_CONST;
p->from.offset = StackGuard;
p->reg = REGSP;
p->to.type = TYPE_REG;
p->to.reg = REG_R4;
p = appendp(ctxt, p);
p->as = ASUB;
p->from.type = TYPE_REG;
p->from.reg = REG_R3;
p->to.type = TYPE_REG;
p->to.reg = REG_R4;
p = appendp(ctxt, p);
p->as = AMOVD;
p->from.type = TYPE_CONST;
p->from.offset = framesize + StackGuard - StackSmall;
p->to.type = TYPE_REG;
p->to.reg = REGTMP;
p = appendp(ctxt, p);
p->as = ACMPU;
p->from.type = TYPE_REG;
p->from.reg = REGTMP;
p->to.type = TYPE_REG;
p->to.reg = REG_R4;
}
// q1: BLT done
q1 = p = appendp(ctxt, p);
p->as = ABLT;
p->to.type = TYPE_BRANCH;
// MOVD LR, R5
p = appendp(ctxt, p);
p->as = AMOVD;
p->from.type = TYPE_REG;
p->from.reg = REG_LR;
p->to.type = TYPE_REG;
p->to.reg = REG_R5;
if(q)
q->pcond = p;
// BL runtime.morestack(SB)
p = appendp(ctxt, p);
p->as = ABL;
p->to.type = TYPE_BRANCH;
if(ctxt->cursym->cfunc)
p->to.sym = linklookup(ctxt, "runtime.morestackc", 0);
else
p->to.sym = ctxt->symmorestack[noctxt];
// BR start
p = appendp(ctxt, p);
p->as = ABR;
p->to.type = TYPE_BRANCH;
p->pcond = ctxt->cursym->text->link;
// placeholder for q1's jump target
p = appendp(ctxt, p);
p->as = ANOP; // zero-width place holder
q1->pcond = p;
return p;
}
static void xfol(Link*, Prog*, Prog**);
static void
follow(Link *ctxt, LSym *s)
{
Prog *firstp, *lastp;
ctxt->cursym = s;
firstp = emallocz(sizeof(Prog));
lastp = firstp;
xfol(ctxt, s->text, &lastp);
lastp->link = nil;
s->text = firstp->link;
}
static int
relinv(int a)
{
switch(a) {
case ABEQ: return ABNE;
case ABNE: return ABEQ;
case ABGE: return ABLT;
case ABLT: return ABGE;
case ABGT: return ABLE;
case ABLE: return ABGT;
case ABVC: return ABVS;
case ABVS: return ABVC;
}
return 0;
}
static void
xfol(Link *ctxt, Prog *p, Prog **last)
{
Prog *q, *r;
int a, b, i;
loop:
if(p == nil)
return;
a = p->as;
if(a == ABR) {
q = p->pcond;
if((p->mark&NOSCHED) || q && (q->mark&NOSCHED)){
p->mark |= FOLL;
(*last)->link = p;
*last = p;
p = p->link;
xfol(ctxt, p, last);
p = q;
if(p && !(p->mark & FOLL))
goto loop;
return;
}
if(q != nil) {
p->mark |= FOLL;
p = q;
if(!(p->mark & FOLL))
goto loop;
}
}
if(p->mark & FOLL) {
for(i=0,q=p; i<4; i++,q=q->link) {
if(q == *last || (q->mark&NOSCHED))
break;
b = 0; /* set */
a = q->as;
if(a == ANOP) {
i--;
continue;
}
if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID)
goto copy;
if(!q->pcond || (q->pcond->mark&FOLL))
continue;
b = relinv(a);
if(!b)
continue;
copy:
for(;;) {
r = emallocz(sizeof(Prog));
*r = *p;
if(!(r->mark&FOLL))
print("cant happen 1\n");
r->mark |= FOLL;
if(p != q) {
p = p->link;
(*last)->link = r;
*last = r;
continue;
}
(*last)->link = r;
*last = r;
if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID)
return;
r->as = b;
r->pcond = p->link;
r->link = p->pcond;
if(!(r->link->mark&FOLL))
xfol(ctxt, r->link, last);
if(!(r->pcond->mark&FOLL))
print("cant happen 2\n");
return;
}
}
a = ABR;
q = emallocz(sizeof(Prog));
q->as = a;
q->lineno = p->lineno;
q->to.type = TYPE_BRANCH;
q->to.offset = p->pc;
q->pcond = p;
p = q;
}
p->mark |= FOLL;
(*last)->link = p;
*last = p;
if(a == ABR || a == ARETURN || a == ARFI || a == ARFCI || a == ARFID || a == AHRFID){
if(p->mark & NOSCHED){
p = p->link;
goto loop;
}
return;
}
if(p->pcond != nil)
if(a != ABL && p->link != nil) {
xfol(ctxt, p->link, last);
p = p->pcond;
if(p == nil || (p->mark&FOLL))
return;
goto loop;
}
p = p->link;
goto loop;
}
LinkArch linkppc64 = {
.name = "ppc64",
.thechar = '9',
.endian = BigEndian,
.preprocess = preprocess,
.assemble = span9,
.follow = follow,
.progedit = progedit,
.minlc = 4,
.ptrsize = 8,
.regsize = 8,
};
LinkArch linkppc64le = {
.name = "ppc64le",
.thechar = '9',
.endian = LittleEndian,
.preprocess = preprocess,
.assemble = span9,
.follow = follow,
.progedit = progedit,
.minlc = 4,
.ptrsize = 8,
.regsize = 8,
};