[pypy-svn] r34180 - in pypy/dist/pypy/jit/codegen: ppc test
mwh at codespeak.net
mwh at codespeak.net
Sat Nov 4 16:15:55 CET 2006
Author: mwh
Date: Sat Nov 4 16:15:54 2006
New Revision: 34180
Modified:
pypy/dist/pypy/jit/codegen/ppc/rgenop.py
pypy/dist/pypy/jit/codegen/test/rgenop_tests.py
Log:
(niko, mwh)
develop and implement an almost sane model for managing the stack.
Modified: pypy/dist/pypy/jit/codegen/ppc/rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/ppc/rgenop.py (original)
+++ pypy/dist/pypy/jit/codegen/ppc/rgenop.py Sat Nov 4 16:15:54 2006
@@ -56,9 +56,9 @@
CT_REGISTER = 3
class RegisterAllocation:
- def __init__(self, minreg, initial_mapping):
+ def __init__(self, minreg, initial_mapping, initial_spill_offset):
#print
- #print "RegisterAllocation __init__"
+ #print "RegisterAllocation __init__", initial_mapping
self.insns = [] # Output list of instructions
self.freeregs = gprs[minreg:] # Registers with dead values
@@ -66,22 +66,24 @@
self.loc2var = {} # Maps an AllocationSlot to a Var
self.lru = [] # Least-recently-used list of vars; first is oldest.
# Contains all vars in registers, and no vars on stack
- self._spill_index = 0 # Where to put next spilled value
+ self.spill_offset = initial_spill_offset # Where to put next spilled
+ # value, relative to rFP,
+ # measured in bytes
# Go through the initial mapping and initialize the data structures
for var, loc in initial_mapping.iteritems():
self.loc2var[loc] = var
self.var2loc[var] = loc
if loc in self.freeregs:
- del self.freeregs[self.freeregs.index(loc)]
+ self.freeregs.remove(loc)
self.lru.append(var)
self.crfinfo = [(0, 0)] * 8
def spill(self):
""" Returns an offset onto the stack for an unused spill location """
# TODO --- reuse spill slots when contained values go dead?
- self._spill_index += 4
- return self._spill_index
+ self.spill_offset -= 4
+ return self.spill_offset
def _allocate_reg(self, newarg):
@@ -90,8 +92,7 @@
reg = self.freeregs.pop()
self.loc2var[reg] = newarg
self.var2loc[newarg] = reg
- #print "allocate_reg: Putting %r into fresh register %r" % (
- # newarg, reg)
+ #print "allocate_reg: Putting %r into fresh register %r" % (newarg, reg)
return reg
# if not, find something to spill
@@ -124,16 +125,15 @@
def _promote(self, arg):
if arg in self.lru:
- del self.lru[self.lru.index(arg)]
+ self.lru.remove(arg)
self.lru.append(arg)
-
+
def allocate_for_insns(self, insns):
# Walk through instructions in forward order
for insn in insns:
- #print "Processing instruction %r with args %r and result %r:" % (
- # insn, insn.reg_args, insn.result)
- #
+ #print "Processing instruction %r with args %r and result %r:" % (insn, insn.reg_args, insn.result)
+
#print "LRU list was: %r" % (self.lru,)
# put things into the lru
@@ -157,12 +157,15 @@
# It has no register now because it has been spilled
assert argcls is GP_REGISTER, "uh-oh"
self._allocate_reg(arg)
+ else:
+ #print "it was in ", self.var2loc[arg]
+ pass
# Need to allocate a register for the destination
assert not insn.result or insn.result not in self.var2loc
cand = None
if insn.result_regclass is GP_REGISTER:
- #print "Allocating register for result %r..." % (cand,)
+ #print "Allocating register for result %r..." % (insn.result,)
cand = self._allocate_reg(insn.result)
elif insn.result_regclass is CR_FIELD:
assert crfs[0] not in self.loc2var
@@ -214,6 +217,13 @@
var, [self]))
return var
+ def load_now(self, asm, loc):
+ if loc.is_register:
+ asm.load_word(loc.number, self.value)
+ else:
+ asm.load_word(rSCRATCH, self.value)
+ asm.stw(rSCRATCH, rFP, loc.offset)
+
class Insn(object):
'''
result is the Var instance that holds the result, or None
@@ -379,7 +389,7 @@
self.reg = reg
self.stack = stack
def emit(self, asm):
- asm.lwz(self.reg.number, rSP, self.stack.offset)
+ asm.lwz(self.reg.number, rFP, self.stack.offset)
class Spill(Insn):
""" A special instruction inserted by our register "allocator."
@@ -396,7 +406,7 @@
self.reg = reg
self.stack = stack
def emit(self, asm):
- asm.stw(self.reg.number, rSP, self.stack.offset)
+ asm.stw(self.reg.number, rFP, self.stack.offset)
class Return(Insn):
""" Ensures the return value is in r3 """
@@ -428,7 +438,9 @@
r0, r1, r2, r3, r4, r5, r6, r7, r8, r9, r10, r11, r12, \
r13, r14, r15, r16, r17, r18, r19, r20, r21, r22, \
r23, r24, r25, r26, r27, r28, r29, r30, r31 = range(32)
+rSCRATCH = r0
rSP = r1
+rFP = r2 # the ABI doesn't specify a frame pointer. however, we want one
def emit(self, value):
self.mc.write(value)
@@ -531,27 +543,28 @@
class JumpPatchupGenerator(object):
- def __init__(self, asm, regalloc):
+ def __init__(self, asm, min_offset):
self.asm = asm
- self.regalloc = regalloc
+ self.min_offset = min_offset
def emit_move(self, tarloc, srcloc):
if tarloc == srcloc: return
if tarloc.is_register and srcloc.is_register:
self.asm.mr(tarloc.number, srcloc.number)
elif tarloc.is_register and not srcloc.is_register:
- self.asm.lwz(tarloc.number, rSP, srcloc.offset)
+ self.asm.lwz(tarloc.number, rFP, srcloc.offset)
elif not tarloc.is_register and srcloc.is_register:
- self.asm.stw(srcloc.number, rSP, tarloc.offset)
+ self.asm.stw(srcloc.number, rFP, tarloc.offset)
elif not tarloc.is_register and not srcloc.is_register:
- self.asm.lwz(r0, rSP, srcloc.offset)
- self.asm.stw(r0, rSP, tarloc.offset)
+ self.asm.lwz(rSCRATCH, rFP, srcloc.offset)
+ self.asm.stw(rSCRATCH, rFP, tarloc.offset)
def create_fresh_location(self):
- offset = self.regalloc.spill()
- return stack_slot(offset)
+ r = self.min_offset
+ self.min_offset -= 4
+ return stack_slot(r)
-def prepare_for_jump(asm, allocator, sourcevars, src2loc, target):
+def prepare_for_jump(asm, min_offset, sourcevars, src2loc, target):
tar2src = {} # tar var -> src var
tar2loc = {}
@@ -564,8 +577,9 @@
tar2loc[tloc] = tloc
tar2src[tloc] = sourcevars[i]
- gen = JumpPatchupGenerator(asm, allocator)
+ gen = JumpPatchupGenerator(asm, min_offset)
emit_moves(gen, tar2src, tar2loc, src2loc)
+ return gen.min_offset
class MachineCodeBlock:
@@ -598,9 +612,10 @@
class Label(GenLabel):
- def __init__(self, startaddr, arg_locations):
+ def __init__(self, startaddr, arg_locations, min_stack_offset):
self.startaddr = startaddr
self.arg_locations = arg_locations
+ self.min_stack_offset = min_stack_offset
## class FlexSwitch(CodeGenSwitch):
@@ -639,41 +654,56 @@
class Builder(GenBuilder):
- def __init__(self, rgenop, mc, parent):
+ def __init__(self, rgenop, mc):
self.rgenop = rgenop
self.asm = RPPCAssembler()
self.asm.mc = mc
self.insns = []
- self.parent = parent
+ self.stack_adj_addr = 0
+ self.initial_spill_offset = 0
+ self.initial_var2loc = None
+ self.fresh_from_jump = False
+
+ def make_fresh_from_jump(self, initial_var2loc):
+ self.fresh_from_jump = True
+ self.initial_var2loc = initial_var2loc
def _write_prologue(self, sigtoken):
- assert self.parent is None
numargs = sigtoken # for now
if not we_are_translated() and option.trap:
self.asm.trap()
- self.inputargs = [self.newvar() for i in range(numargs)]
- self.initial_varmapping = {}
- for arg in self.inputargs:
- self.initial_varmapping[arg] = gprs[3+len(self.initial_varmapping)]
+ inputargs = [self.newvar() for i in range(numargs)]
+ assert self.initial_var2loc is None
+ self.initial_var2loc = {}
+ for arg in inputargs:
+ self.initial_var2loc[arg] = gprs[3+len(self.initial_var2loc)]
+ self.initial_spill_offset = self._var_offset(0)
# Emit standard prologue
# Minimum space = 24+params+lv+4*GPR+8*FPR
# GPR=19
# Initially, we allocate only enough space for GPRs, and allow
# each basic block to ensure it has enough space to continue.
- minspace = self._stack_offset(0,0)
- self.asm.mflr(r0)
- self.asm.stw(r0,rSP,8)
+ minspace = self._stack_size(0,self._var_offset(0))
+ self.asm.mflr(rSCRATCH)
+ self.asm.stw(rSCRATCH,rSP,8)
self.asm.stmw(r13,rSP,-(4*20)) # save all regs from 13-31 to stack
+ self.asm.mr(rFP, rSP) # set up our frame pointer
self.asm.stwu(rSP,rSP,-minspace)
-
- return self.inputargs
- def _stack_offset(self, param, lv):
- """ Returns the required stack offset to store all data, assuming
- that there are 'param' words of parameters for callee functions and
- 'lv' words of local variable information. """
- return ((24 + param*4 + lv*4 + 4*19) & ~15)+16
+ return inputargs
+
+ def _var_offset(self, v):
+ """v represents an offset into the local variable area in bytes;
+ this returns the offset relative to rFP"""
+ return -(4*19+4+v)
+
+ def _stack_size(self, param, lv):
+ """ Returns the required stack size to store all data, assuming
+ that there are 'param' bytes of parameters for callee functions and
+ 'lv' is the largest (wrt to abs() :) rFP-relative byte offset of
+ any variable on the stack."""
+ return ((24 + param - lv) & ~15)+16
def _close(self):
self.rgenop.close_mc(self.asm.mc)
@@ -684,52 +714,121 @@
genmethod = getattr(self, 'op_' + opname)
return genmethod(gv_arg1, gv_arg2)
- def emit(self):
- if self.parent is not None:
- allocator = RegisterAllocation(
- self.rgenop.MINUSERREG, self.parent.var2loc)
- else:
- allocator = RegisterAllocation(
- self.rgenop.MINUSERREG, self.initial_varmapping)
+ def allocate_and_emit(self):
+ assert self.initial_var2loc is not None
+ allocator = RegisterAllocation(
+ self.rgenop.MINUSERREG, self.initial_var2loc, self.initial_spill_offset)
self.insns = allocator.allocate_for_insns(self.insns)
+ if self.insns:
+ self.patch_stack_adjustment(self._stack_size(0, allocator.spill_offset))
for insn in self.insns:
insn.emit(self.asm)
- self.var2loc = allocator.var2loc
return allocator
def finish_and_return(self, sigtoken, gv_returnvar):
gv_returnvar = gv_returnvar.load(self)
self.insns.append(Return(gv_returnvar))
- allocator = self.emit()
+ allocator = self.allocate_and_emit()
# Emit standard epilogue:
- self.asm.lwz(rSP,rSP,0) # restore old SP
- self.asm.lmw(r13,rSP,-4*20) # restore all GPRs
- self.asm.lwz(r0,rSP,8) # load old Link Register and jump to it
- self.asm.mtlr(r0) #
- self.asm.blr() #
+ self.asm.lwz(rSP,rSP,0) # restore old SP
+ self.asm.lmw(r13,rSP,-4*20) # restore all GPRs
+ self.asm.lwz(rSCRATCH,rSP,8) # load old Link Register and jump to it
+ self.asm.mtlr(rSCRATCH) #
+ self.asm.blr() #
self._close()
def finish_and_goto(self, outputargs_gv, target):
- allocator = self.emit()
- prepare_for_jump(
- self.asm, allocator, outputargs_gv, allocator.var2loc, target)
- self.asm.load_word(0, target.startaddr)
- self.asm.mtctr(0)
+ allocator = self.allocate_and_emit()
+ min_offset = min(allocator.spill_offset, target.min_stack_offset)
+ min_offset = prepare_for_jump(
+ self.asm, min_offset, outputargs_gv, allocator.var2loc, target)
+ self.patch_stack_adjustment(self._stack_size(0, min_offset))
+ self.asm.load_word(rSCRATCH, target.startaddr)
+ self.asm.mtctr(rSCRATCH)
self.asm.bctr()
self._close()
+ def emit_stack_adjustment(self):
+ # the ABI requires that at all times that r1 is valid, in the
+ # sense that it must point to the bottom of the stack and that
+ # executing SP <- *(SP) repeatedly walks the stack.
+ # this code satisfies this, although there is a 1-instruction
+ # window where such walking would find a strange intermediate
+ # "frame" (apart from when the delta is 0... XXX)
+ # as we emit these instructions waaay before doing the
+ # register allocation for this block we don't know how much
+ # stack will be required, so we patch it later (see
+ # patch_stack_adjustment below).
+ self.stack_adj_addr = self.asm.mc.tell()
+ self.asm.addi(rSCRATCH, rFP, 0) # this is the immediate that later gets patched
+ self.asm.sub(rSCRATCH, rSCRATCH, rSP) # rSCRATCH should now be <= 0
+ self.asm.stwux(rSP, rSP, rSCRATCH)
+ self.asm.stw(rFP, rSP, 0)
+
+ def patch_stack_adjustment(self, newsize):
+ if self.stack_adj_addr == 0:
+ return
+ # we build an addi instruction by hand here
+ opcode = 14 << 26
+ rD = rSCRATCH << 21
+ rA = rFP << 16
+ # if we decided to use r0 as the frame pointer, this would
+ # emit addi rFOO, r0, SIMM which would just load SIMM into
+ # rFOO and be "unlikely" to work
+ assert rA != 0
+ SIMM = (-newsize) & 0xFFFF
+ p_instruction = cast(c_void_p(self.stack_adj_addr), POINTER(c_int*1))
+ p_instruction.contents[0] = opcode | rD | rA | SIMM
+
def enter_next_block(self, kinds, args_gv):
+ if self.fresh_from_jump:
+ var2loc = self.initial_var2loc
+ self.fresh_from_jump = False
+ else:
+ var2loc = self.allocate_and_emit().var2loc
+
+ #print "enter_next_block:", args_gv, var2loc
+
+ min_stack_offset = self._var_offset(0)
+ usedregs = {}
+ livevar2loc = {}
+ for gv in args_gv:
+ if isinstance(gv, Var):
+ assert gv in var2loc
+ loc = var2loc[gv]
+ livevar2loc[gv] = loc
+ if not loc.is_register:
+ min_stack_offset = min(min_stack_offset, loc.offset)
+ else:
+ usedregs[loc] = None # XXX use this
+
+ unusedregs = [loc for loc in gprs[self.rgenop.MINUSERREG:] if loc not in usedregs]
arg_locations = []
+
for i in range(len(args_gv)):
gv = args_gv[i]
- gv = args_gv[i] = gv.load(self)
- allocator = self.emit()
- for gv in args_gv:
- arg_locations.append(allocator.var2loc[gv])
+ if isinstance(gv, Var):
+ arg_locations.append(livevar2loc[gv])
+ else:
+ if unusedregs:
+ loc = unusedregs.pop()
+ else:
+ loc = stack_slot(min_stack_offset)
+ min_stack_offset -= 4
+ gv.load_now(self.asm, loc)
+ args_gv[i] = gv = Var()
+ livevar2loc[gv] = loc
+ arg_locations.append(loc)
+
+ #print livevar2loc
+
self.insns = []
- self.initial_varmapping = allocator.var2loc
- return Label(self.asm.mc.tell(), arg_locations)
+ self.initial_var2loc = livevar2loc
+ self.initial_spill_offset = min_stack_offset
+ target_addr = self.asm.mc.tell()
+ self.emit_stack_adjustment()
+ return Label(target_addr, arg_locations, min_stack_offset)
def newvar(self):
gv = Var()
@@ -798,6 +897,7 @@
def _jump(self, gv_condition, if_true):
targetbuilder = self._fork()
+
gv = self.newvar()
self.insns.append(
Insn_GPR__IMM(RPPCAssembler.load_word,
@@ -807,6 +907,11 @@
MTCTR(gv2, [gv]))
self.insns.append(
Jump(gv_condition, gv2, if_true))
+
+ allocator = self.allocate_and_emit()
+ self.make_fresh_from_jump(allocator.var2loc)
+ targetbuilder.make_fresh_from_jump(allocator.var2loc)
+
return targetbuilder
def jump_if_false(self, gv_condition):
@@ -816,7 +921,7 @@
return self._jump(gv_condition, True)
def _fork(self):
- return self.rgenop.openbuilder(self)
+ return self.rgenop.openbuilder()
class RPPCGenOp(AbstractRGenOp):
@@ -848,12 +953,12 @@
def kindToken(T):
return None # for now
- def openbuilder(self, parent):
- return Builder(self, self.open_mc(), parent)
+ def openbuilder(self):
+ return Builder(self, self.open_mc())
def newgraph(self, sigtoken):
numargs = sigtoken # for now
- builder = self.openbuilder(None)
+ builder = self.openbuilder()
entrypoint = builder.asm.mc.tell()
inputargs_gv = builder._write_prologue(sigtoken)
return builder, entrypoint, inputargs_gv
Modified: pypy/dist/pypy/jit/codegen/test/rgenop_tests.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/test/rgenop_tests.py (original)
+++ pypy/dist/pypy/jit/codegen/test/rgenop_tests.py Sat Nov 4 16:15:54 2006
@@ -77,6 +77,9 @@
builder.finish_and_return(sigtoken, gv_s2)
# false path
+ args_gv = [gv_y]
+ false_builder.enter_next_block([signed_kind], args_gv)
+ [gv_y] = args_gv
false_builder.finish_and_return(sigtoken, gv_y)
# done
More information about the Pypy-commit
mailing list