## Manage method frame.# ## Local variables (including function parameters).# Local: adt { # local variable dtype: byte; # type of local (DIS_[BWLP]) offset: int; # fp offset of this local next: cyclic ref Local; # for locals that are reused}; frameoff: int; # tracks growth of framemaxframe: int; # size of largest framelocals: array of ref Local; # local variablesnlocals: int; # number of locals (in the Java sense) ## fp temporaries.# Fp: adt { dtype: byte; refcnt: int;}; tmpslwm: int; # lowest temporary fp offsettmpssz: int; # size of tmps arraytmps: array of Fp; # temporary arena ## Resize the locals array.## Since the Java VM reuses local variables (e.g., perhaps using# the same slot for an int and an Object reference), max_locals# doesn't always give the number of Dis locals to allow for.# resizelocals(ix: int){ oldsz: int; oldsz = nlocals; while(ix >= nlocals) nlocals += ALLOCINCR; newlocals := array [nlocals] of ref Local; for(i := 0; i < oldsz; i++) newlocals[i] = locals[i]; for(i = oldsz; i < nlocals; i++) newlocals[i] = ref Local(byte 0, 0, nil); locals = newlocals;} ## Reserve a frame cell for a local variable of the given type.# The Java VM reuses local variables.# reservelocal(dtype: byte, ix: int){ l: ref Local; if(ix >= nlocals) resizelocals(ix); if(locals[ix].dtype == dtype) return; if(locals[ix].dtype == DIS_X) # not yet reserved l = locals[ix]; else { # check reuse list for(l = locals[ix].next; l != nil; l = l.next) { if(dtype == l.dtype) return; } l = ref Local(byte 0, 0, nil); l.next = locals[ix].next; locals[ix].next = l; } l.dtype = dtype; frameoff = align(frameoff, cellsize[int dtype]); l.offset = frameoff; frameoff += cellsize[int dtype];} ## Map the signature parameter types into 'locals' array.# mapmethodsig(sig: string, isstatic: int){ index: int; index = 0; if(isstatic == 0) { reservelocal(DIS_P, index); # this pointer index += 1; } sig = sig[1:]; # skip '(' while(sig[0] != ')') { case sig[0] { 'Z' or 'B' or 'C' or 'S' or 'I' => reservelocal(DIS_W, index); index += 1; 'F' => reservelocal(DIS_L, index); index += 1; 'J' or 'D' => reservelocal(DIS_L, index); index += 2; 'L' or '[' => reservelocal(DIS_P, index); index += 1; } sig = nextjavatype(sig); }} ## Map references to local variables in the Java bytecode into 'locals' array.# maplocalrefs(){ j: ref Jinst; jpi: ref Jinst.Pi; jpw: ref Jinst.Pw; jpx1c: ref Jinst.Px1c; jix: int; jix = 0; while(jix < code.code_length) { j = code.j[jix]; pick jp := j { Pi => jpi = jp; Px1c => jpx1c = jp; Pw => jpw = jp; } case int j.op { Jiinc => reservelocal(DIS_W, jpx1c.x1c.ix); Jiload or Jistore => reservelocal(DIS_W, jpi.i); Jiload_0 or Jistore_0 => reservelocal(DIS_W, 0); Jiload_1 or Jistore_1 => reservelocal(DIS_W, 1); Jiload_2 or Jistore_2 => reservelocal(DIS_W, 2); Jiload_3 or Jistore_3 => reservelocal(DIS_W, 3); Jlload or Jlstore or Jfload or Jfstore or Jdload or Jdstore => reservelocal(DIS_L, jpi.i); Jlload_0 or Jlstore_0 or Jfload_0 or Jfstore_0 or Jdload_0 or Jdstore_0 => reservelocal(DIS_L, 0); Jlload_1 or Jlstore_1 or Jfload_1 or Jfstore_1 or Jdload_1 or Jdstore_1 => reservelocal(DIS_L, 1); Jlload_2 or Jlstore_2 or Jfload_2 or Jfstore_2 or Jdload_2 or Jdstore_2 => reservelocal(DIS_L, 2); Jlload_3 or Jlstore_3 or Jfload_3 or Jfstore_3 or Jdload_3 or Jdstore_3 => reservelocal(DIS_L, 3); Jret or Jaload or Jastore => reservelocal(DIS_P, jpi.i); Jaload_0 or Jastore_0 => reservelocal(DIS_P, 0); Jaload_1 or Jastore_1 => reservelocal(DIS_P, 1); Jaload_2 or Jastore_2 => reservelocal(DIS_P, 2); Jaload_3 or Jastore_3 => reservelocal(DIS_P, 3); Jwide => # repeat from above case int jpw.w.op { Jiinc or Jiload or Jistore => reservelocal(DIS_W, jpw.w.ix); Jlload or Jlstore or Jfload or Jfstore or Jdload or Jdstore => reservelocal(DIS_L, jpw.w.ix); Jaload or Jastore or Jret => reservelocal(DIS_P, jpw.w.ix); } } jix += j.size; }} ## Prepare a method's frame prior to translating its code.# openframe(sig: string, isstatic: int){ frameoff = REGSIZE; # 'code' can == nil; e.g, when generating <clinit> for an interface if(code != nil) nlocals = code.max_locals; else nlocals = 2; locals = array [nlocals] of { * => ref Local(byte 0, 0, nil) }; mapmethodsig(sig, isstatic); if(code != nil) maplocalrefs(); frameoff = align(frameoff, IBY2WD); tmpslwm = frameoff; tmpssz = frameoff; tmps = array [tmpssz] of { * => (byte 0, 0) };} ## Return fp index of a local variable.# localix(dtype: byte, ix: int): int{ l: ref Local; if(dtype == locals[ix].dtype) return locals[ix].offset; for(l = locals[ix].next; l != nil; l = l.next) { if(dtype == l.dtype) return l.offset; } fatal("localix: " + string dtype + ", " + string ix); return 0;} # fp temporary management starts here incref(off: int){ if(off >= tmpslwm) # if <, then local variable tmps[off-tmpslwm].refcnt++;} acqreg(a: ref Addr){ case int a.mode { int Anone or int Aimm or int Amp or int Ampind => ; int Afp => incref(a.offset); int Afpind => incref(a.ival); }} ## Reserve the fp temporaries referenced by a StkSnap of some basic block.# reservereg(s: array of ref StkSnap, sz: int){ i: int; for(i = 0; i < sz; i++) acqreg(code.j[s[i].pc[0]].dst);} ## Get fp offset of the next available register of the given type.# getoff(dtype: byte): int{ off: int; stride: int; if(dtype == DIS_L) { stride = IBY2LG; off = tmpslwm % IBY2LG; } else { stride = IBY2WD; off = 0; } while(off < tmpssz) { if(tmps[off].refcnt == 0 && (tmps[off].dtype == dtype || tmps[off].dtype == DIS_X)) { return off; } off += stride; } return off;} ## Get a register (fp offset thereof) of the appropriate type.# getreg(dtype: byte): int{ off: int; oldsz: int; off = getoff(dtype); if(off >= tmpssz) { # increase size of temporary arena oldsz = tmpssz; tmpssz += ALLOCINCR*IBY2LG; newtmps := array [tmpssz] of Fp; for(i := 0; i < oldsz; i++) newtmps[i] = tmps[i]; for(i = oldsz; i < tmpssz; i++) newtmps[i] = Fp(byte 0, 0); tmps = newtmps; } tmps[off].dtype = dtype; if(dtype == DIS_L) # also reserve next word tmps[off+IBY2WD].dtype = dtype; tmps[off].refcnt = 1; if(tmpslwm+off+cellsize[int dtype] > frameoff) frameoff = tmpslwm+off+cellsize[int dtype]; return tmpslwm+off;} decref(off: int){ if(off >= tmpslwm) { # if <, then local variable if(--tmps[off-tmpslwm].refcnt < 0) fatal("decref: refcnt < 0"); }} ## "Free" register used by a.# relreg(a: ref Addr){ # don't mess with exception object if(code != nil && a == code.j[code.code_length].dst) return; case int a.mode { int Anone or int Aimm or int Amp or int Ampind => ; int Afp => decref(a.offset); int Afpind => decref(a.ival); }} ## Mark all temporary fp registers as available. Leave typing as is.# clearreg(){ i: int; i = 0; while(i < frameoff-tmpslwm) { tmps[i].refcnt = 0; i += IBY2WD; }} ## Calculate the type descriptor for a frame.# framedesc(): int{ ln, id, i: int; map: array of byte; l: ref Local; ln = frameoff / (8*IBY2WD) + (frameoff % (8*IBY2WD) != 0); map = array [ln] of { * => byte 0 }; #setbit(map, REFSYSEX); # slot for 'ref Sys->Exception' setbit(map, EXSTR); # slot for exception string setbit(map, EXOBJ); # slot for exception object for(i = 0; i < nlocals; i++) { if(locals[i].dtype == DIS_P) setbit(map, locals[i].offset); for(l = locals[i].next; l != nil; l = l.next) { if(l.dtype == DIS_P) setbit(map, l.offset); } } i = 0; while(i < frameoff-tmpslwm) { if(tmps[i].dtype == DIS_P) setbit(map, tmpslwm+i); i += IBY2WD; } id = descid(frameoff, ln, map); return id;} ## Dispense with a frame.# closeframe(): int{ tid: int; # frame size is always a multiple of 8 frameoff = align(frameoff, IBY2LG); if(frameoff > maxframe) maxframe = frameoff; tid = framedesc(); locals = nil; frameoff = 0; tmpslwm = 0; tmpssz = 0; tmps = nil; return tid;} ## Minimum stack extent size.# disstackext(){ discon(10*maxframe);}
#
# Manage method frame.
#
#
# Local variables (including function parameters).
#
Local: adt { # local variable
dtype: byte; # type of local (DIS_[BWLP])
offset: int; # fp offset of this local
next: cyclic ref Local; # for locals that are reused
};
frameoff: int; # tracks growth of frame
maxframe: int; # size of largest frame
locals: array of ref Local; # local variables
nlocals: int; # number of locals (in the Java sense)
#
# fp temporaries.
#
Fp: adt {
dtype: byte;
refcnt: int;
};
tmpslwm: int; # lowest temporary fp offset
tmpssz: int; # size of tmps array
tmps: array of Fp; # temporary arena
#
# Resize the locals array.
#
# Since the Java VM reuses local variables (e.g., perhaps using
# the same slot for an int and an Object reference), max_locals
# doesn't always give the number of Dis locals to allow for.
#
resizelocals(ix: int)
{
oldsz: int;
oldsz = nlocals;
while(ix >= nlocals)
nlocals += ALLOCINCR;
newlocals := array [nlocals] of ref Local;
for(i := 0; i < oldsz; i++)
newlocals[i] = locals[i];
for(i = oldsz; i < nlocals; i++)
newlocals[i] = ref Local(byte 0, 0, nil);
locals = newlocals;
}
#
# Reserve a frame cell for a local variable of the given type.
# The Java VM reuses local variables.
#
reservelocal(dtype: byte, ix: int)
{
l: ref Local;
if(ix >= nlocals)
resizelocals(ix);
if(locals[ix].dtype == dtype)
return;
if(locals[ix].dtype == DIS_X) # not yet reserved
l = locals[ix];
else { # check reuse list
for(l = locals[ix].next; l != nil; l = l.next) {
if(dtype == l.dtype)
return;
}
l = ref Local(byte 0, 0, nil);
l.next = locals[ix].next;
locals[ix].next = l;
}
l.dtype = dtype;
frameoff = align(frameoff, cellsize[int dtype]);
l.offset = frameoff;
frameoff += cellsize[int dtype];
}
#
# Map the signature parameter types into 'locals' array.
#
mapmethodsig(sig: string, isstatic: int)
{
index: int;
index = 0;
if(isstatic == 0) {
reservelocal(DIS_P, index); # this pointer
index += 1;
}
sig = sig[1:]; # skip '('
while(sig[0] != ')') {
case sig[0] {
'Z' or
'B' or
'C' or
'S' or
'I' =>
reservelocal(DIS_W, index);
index += 1;
'F' =>
reservelocal(DIS_L, index);
index += 1;
'J' or
'D' =>
reservelocal(DIS_L, index);
index += 2;
'L' or
'[' =>
reservelocal(DIS_P, index);
index += 1;
}
sig = nextjavatype(sig);
}
}
#
# Map references to local variables in the Java bytecode into 'locals' array.
#
maplocalrefs()
{
j: ref Jinst;
jpi: ref Jinst.Pi;
jpw: ref Jinst.Pw;
jpx1c: ref Jinst.Px1c;
jix: int;
jix = 0;
while(jix < code.code_length) {
j = code.j[jix];
pick jp := j {
Pi =>
jpi = jp;
Px1c =>
jpx1c = jp;
Pw =>
jpw = jp;
}
case int j.op {
Jiinc =>
reservelocal(DIS_W, jpx1c.x1c.ix);
Jiload or
Jistore =>
reservelocal(DIS_W, jpi.i);
Jiload_0 or
Jistore_0 =>
reservelocal(DIS_W, 0);
Jiload_1 or
Jistore_1 =>
reservelocal(DIS_W, 1);
Jiload_2 or
Jistore_2 =>
reservelocal(DIS_W, 2);
Jiload_3 or
Jistore_3 =>
reservelocal(DIS_W, 3);
Jlload or
Jlstore or
Jfload or
Jfstore or
Jdload or
Jdstore =>
reservelocal(DIS_L, jpi.i);
Jlload_0 or
Jlstore_0 or
Jfload_0 or
Jfstore_0 or
Jdload_0 or
Jdstore_0 =>
reservelocal(DIS_L, 0);
Jlload_1 or
Jlstore_1 or
Jfload_1 or
Jfstore_1 or
Jdload_1 or
Jdstore_1 =>
reservelocal(DIS_L, 1);
Jlload_2 or
Jlstore_2 or
Jfload_2 or
Jfstore_2 or
Jdload_2 or
Jdstore_2 =>
reservelocal(DIS_L, 2);
Jlload_3 or
Jlstore_3 or
Jfload_3 or
Jfstore_3 or
Jdload_3 or
Jdstore_3 =>
reservelocal(DIS_L, 3);
Jret or
Jaload or
Jastore =>
reservelocal(DIS_P, jpi.i);
Jaload_0 or
Jastore_0 =>
reservelocal(DIS_P, 0);
Jaload_1 or
Jastore_1 =>
reservelocal(DIS_P, 1);
Jaload_2 or
Jastore_2 =>
reservelocal(DIS_P, 2);
Jaload_3 or
Jastore_3 =>
reservelocal(DIS_P, 3);
Jwide => # repeat from above
case int jpw.w.op {
Jiinc or
Jiload or
Jistore =>
reservelocal(DIS_W, jpw.w.ix);
Jlload or
Jlstore or
Jfload or
Jfstore or
Jdload or
Jdstore =>
reservelocal(DIS_L, jpw.w.ix);
Jaload or
Jastore or
Jret =>
reservelocal(DIS_P, jpw.w.ix);
}
}
jix += j.size;
}
}
#
# Prepare a method's frame prior to translating its code.
#
openframe(sig: string, isstatic: int)
{
frameoff = REGSIZE;
# 'code' can == nil; e.g, when generating <clinit> for an interface
if(code != nil)
nlocals = code.max_locals;
else
nlocals = 2;
locals = array [nlocals] of { * => ref Local(byte 0, 0, nil) };
mapmethodsig(sig, isstatic);
if(code != nil)
maplocalrefs();
frameoff = align(frameoff, IBY2WD);
tmpslwm = frameoff;
tmpssz = frameoff;
tmps = array [tmpssz] of { * => (byte 0, 0) };
}
#
# Return fp index of a local variable.
#
localix(dtype: byte, ix: int): int
{
l: ref Local;
if(dtype == locals[ix].dtype)
return locals[ix].offset;
for(l = locals[ix].next; l != nil; l = l.next) {
if(dtype == l.dtype)
return l.offset;
}
fatal("localix: " + string dtype + ", " + string ix);
return 0;
}
# fp temporary management starts here
incref(off: int)
{
if(off >= tmpslwm) # if <, then local variable
tmps[off-tmpslwm].refcnt++;
}
acqreg(a: ref Addr)
{
case int a.mode {
int Anone or
int Aimm or
int Amp or
int Ampind =>
;
int Afp =>
incref(a.offset);
int Afpind =>
incref(a.ival);
}
}
#
# Reserve the fp temporaries referenced by a StkSnap of some basic block.
#
reservereg(s: array of ref StkSnap, sz: int)
{
i: int;
for(i = 0; i < sz; i++)
acqreg(code.j[s[i].pc[0]].dst);
}
#
# Get fp offset of the next available register of the given type.
#
getoff(dtype: byte): int
{
off: int;
stride: int;
if(dtype == DIS_L) {
stride = IBY2LG;
off = tmpslwm % IBY2LG;
} else {
stride = IBY2WD;
off = 0;
}
while(off < tmpssz) {
if(tmps[off].refcnt == 0
&& (tmps[off].dtype == dtype || tmps[off].dtype == DIS_X)) {
return off;
}
off += stride;
}
return off;
}
#
# Get a register (fp offset thereof) of the appropriate type.
#
getreg(dtype: byte): int
{
off: int;
oldsz: int;
off = getoff(dtype);
if(off >= tmpssz) { # increase size of temporary arena
oldsz = tmpssz;
tmpssz += ALLOCINCR*IBY2LG;
newtmps := array [tmpssz] of Fp;
for(i := 0; i < oldsz; i++)
newtmps[i] = tmps[i];
for(i = oldsz; i < tmpssz; i++)
newtmps[i] = Fp(byte 0, 0);
tmps = newtmps;
}
tmps[off].dtype = dtype;
if(dtype == DIS_L) # also reserve next word
tmps[off+IBY2WD].dtype = dtype;
tmps[off].refcnt = 1;
if(tmpslwm+off+cellsize[int dtype] > frameoff)
frameoff = tmpslwm+off+cellsize[int dtype];
return tmpslwm+off;
}
decref(off: int)
{
if(off >= tmpslwm) { # if <, then local variable
if(--tmps[off-tmpslwm].refcnt < 0)
fatal("decref: refcnt < 0");
}
}
#
# "Free" register used by a.
#
relreg(a: ref Addr)
{
# don't mess with exception object
if(code != nil && a == code.j[code.code_length].dst)
return;
case int a.mode {
int Anone or
int Aimm or
int Amp or
int Ampind =>
;
int Afp =>
decref(a.offset);
int Afpind =>
decref(a.ival);
}
}
#
# Mark all temporary fp registers as available. Leave typing as is.
#
clearreg()
{
i: int;
i = 0;
while(i < frameoff-tmpslwm) {
tmps[i].refcnt = 0;
i += IBY2WD;
}
}
#
# Calculate the type descriptor for a frame.
#
framedesc(): int
{
ln, id, i: int;
map: array of byte;
l: ref Local;
ln = frameoff / (8*IBY2WD) + (frameoff % (8*IBY2WD) != 0);
map = array [ln] of { * => byte 0 };
#setbit(map, REFSYSEX); # slot for 'ref Sys->Exception'
setbit(map, EXSTR); # slot for exception string
setbit(map, EXOBJ); # slot for exception object
for(i = 0; i < nlocals; i++) {
if(locals[i].dtype == DIS_P)
setbit(map, locals[i].offset);
for(l = locals[i].next; l != nil; l = l.next) {
if(l.dtype == DIS_P)
setbit(map, l.offset);
}
}
i = 0;
while(i < frameoff-tmpslwm) {
if(tmps[i].dtype == DIS_P)
setbit(map, tmpslwm+i);
i += IBY2WD;
}
id = descid(frameoff, ln, map);
return id;
}
#
# Dispense with a frame.
#
closeframe(): int
{
tid: int;
# frame size is always a multiple of 8
frameoff = align(frameoff, IBY2LG);
if(frameoff > maxframe)
maxframe = frameoff;
tid = framedesc();
locals = nil;
frameoff = 0;
tmpslwm = 0;
tmpssz = 0;
tmps = nil;
return tid;
}
#
# Minimum stack extent size.
#
disstackext()
{
discon(10*maxframe);
}