[pypy-svn] r34143 - in pypy/dist/pypy/jit/codegen: ppc ppc/test test

niko at codespeak.net niko at codespeak.net
Fri Nov 3 19:16:21 CET 2006


Author: niko
Date: Fri Nov  3 19:16:19 2006
New Revision: 34143

Added:
   pypy/dist/pypy/jit/codegen/ppc/test/test_emit_moves.py
Modified:
   pypy/dist/pypy/jit/codegen/ppc/rgenop.py
   pypy/dist/pypy/jit/codegen/ppc/test/test_rgenop.py
   pypy/dist/pypy/jit/codegen/test/rgenop_tests.py
Log:
(mwh, niko)

Wrote test sub class that ensures register allocation actually
   spills *something* by artifically restricting number of regs it uses

Fixed many bugs

Wrote general code to reshuffle data on basic block edges, added unit
   tests for the algorithm



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	Fri Nov  3 19:16:19 2006
@@ -4,9 +4,23 @@
 from pypy.rlib.objectmodel import specialize, we_are_translated
 from pypy.jit.codegen.ppc.conftest import option
 
-class Register(object):
-    def __init__(self):
-        pass
+class AllocationSlot(object):
+    pass
+
+class _StackSlot(AllocationSlot):
+    is_register = False
+    def __init__(self, offset):
+        self.offset = offset
+
+_stack_slot_cache = {}
+def stack_slot(offset):
+    if offset in _stack_slot_cache:
+        return _stack_slot_cache[offset]
+    _stack_slot_cache[offset] = res = _StackSlot(offset)
+    return res
+
+class Register(AllocationSlot):
+    is_register = True
 
 class GPR(Register):
     def __init__(self, number):
@@ -42,92 +56,142 @@
 CT_REGISTER = 3
 
 class RegisterAllocation:
-    def __init__(self, initial_mapping):
-        self.insns = []
-        self.freeregs = gprs[3:]
-        self.reg2var = {}
-        self.var2reg = {}
-        self.var2spill = {}
-        for var, reg in initial_mapping.iteritems():
-            self.reg2var[reg] = var
-            self.var2reg[var] = reg
+    def __init__(self, minreg, initial_mapping):
+        #print
+        #print "RegisterAllocation __init__"
+        
+        self.insns = []   # Output list of instructions
+        self.freeregs = gprs[minreg:] # Registers with dead values
+        self.var2loc = {} # Maps a Var to an AllocationSlot
+        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
+
+        # 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.lru.append(var)
         self.crfinfo = [(0, 0)] * 8
-        self._spill_index = 0
 
-    def _spill(self):
+    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
 
-    def _allocate_reg(self, newarg, lru):
+    def _allocate_reg(self, newarg):
 
         # check if there is a register available
         if self.freeregs:
             reg = self.freeregs.pop()
-            self.reg2var[reg] = newarg
-            self.var2reg[newarg] = reg
+            self.loc2var[reg] = newarg
+            self.var2loc[newarg] = reg
+            #print "allocate_reg: Putting %r into fresh register %r" % (
+            #    newarg, reg)
             return reg
 
         # if not, find something to spill
-        argtospill = lru.pop(0)
-        assert argtospill in self.var2reg
-        spill = self._spill()
-        reg = self.var2reg[argtospill] # move argtospill to spill slot
-        self.var2spill[argtospill] = spill
-        del self.var2reg[argtospill]
-        self.reg2var[reg] = newarg     # assign reg to newarg
-        self.var2reg[newarg] = reg
+        argtospill = self.lru.pop(0)
+        reg = self.var2loc[argtospill]
+        assert reg.is_register
+
+        # Move the value we are spilling onto the stack, both in the
+        # data structures and in the instructions:
+        spill = stack_slot(self.spill())
+        self.var2loc[argtospill] = spill
+        self.loc2var[spill] = argtospill
         self.insns.append(Spill(argtospill, reg, spill))
+        #print "allocate_reg: Spilled %r to %r." % (argtospill, spill)
+
+        # If the value is currently on the stack, load it up into the
+        # register we are putting it into
+        if newarg in self.var2loc:
+            spill = self.var2loc[newarg]
+            assert not spill.is_register
+            self.insns.append(Unspill(newarg, reg, spill))
+            del self.loc2var[spill] # not stored there anymore, reuse??
+            #print "allocate_reg: Unspilled %r from %r." % (newarg, spill)
+
+        # Update data structures to put newarg into the register
+        self.var2loc[newarg] = reg
+        self.loc2var[reg] = newarg
+        #print "allocate_reg: Put %r in stolen reg %r." % (newarg, reg)
         return reg
+
+    def _promote(self, arg):
+        if arg in self.lru:
+            del self.lru[self.lru.index(arg)]
+        self.lru.append(arg)
         
     def allocate_for_insns(self, insns):
         # Walk through instructions in forward order
-        lru = []
         for insn in insns:
 
+            #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
-            for arg in insn.reg_args:
-                if arg in lru:
-                    del lru[lru.index(arg)]
-                lru.append(arg)
+            for i in range(len(insn.reg_args)):
+                arg = insn.reg_args[i]
+                argcls = insn.reg_arg_regclasses[i]
+                if argcls == GP_REGISTER:
+                    self._promote(arg)
+            if insn.result and insn.result_regclass == GP_REGISTER:
+                self._promote(insn.result)
+            #print "LRU list is now: %r" % (self.lru,)
 
             # We need to allocate a register for each used
             # argument that is not already in one
             for i in range(len(insn.reg_args)):
                 arg = insn.reg_args[i]
                 argcls = insn.reg_arg_regclasses[i]
+                #print "Allocating register for %r..." % (arg,)
 
-                if arg not in self.var2reg:
+                if not self.var2loc[arg].is_register:
                     # It has no register now because it has been spilled
                     assert argcls is GP_REGISTER, "uh-oh"
-                    reg = self._allocate_reg(arg, lru)
-                    self.insns.append(
-                        Unspill(arg, reg, self.var2spill[arg]))
-                    del self.var2spill[arg]
+                    self._allocate_reg(arg)
 
             # Need to allocate a register for the destination
-            assert not insn.result or insn.result not in self.var2reg
+            assert not insn.result or insn.result not in self.var2loc
             cand = None
             if insn.result_regclass is GP_REGISTER:
-                cand = self._allocate_reg(insn.result, lru)
+                #print "Allocating register for result %r..." % (cand,)
+                cand = self._allocate_reg(insn.result)
             elif insn.result_regclass is CR_FIELD:
-                assert crfs[0] not in self.reg2var
+                assert crfs[0] not in self.loc2var
+                assert isinstance(insn, CMPInsn)
                 cand = crfs[0]
                 self.crfinfo[0] = insn.info
             elif insn.result_regclass is CT_REGISTER:
-                assert ctr not in self.reg2var
+                assert ctr not in self.loc2var
                 cand = ctr
             elif insn.result_regclass is not NO_REGISTER:
                 assert 0
-            if cand is not None:
-                self.var2reg[insn.result] = cand
-                self.reg2var[cand] = insn.result
+            if cand is not None and cand not in self.loc2var:
+                self.var2loc[insn.result] = cand
+                self.loc2var[cand] = insn.result
+            else:
+                assert cand is None or self.loc2var[cand] is insn.result
             insn.allocate(self)
             self.insns.append(insn)
         return self.insns
 
+_var_index = [0]
 class Var(GenVar):
+    def __init__(self):
+        self.__magic_index = _var_index[0]
+        _var_index[0] += 1
     def load(self, builder):
         return self
+    def __repr__(self):
+        return "<Var %d>" % self.__magic_index
 
 class IntConst(GenConst):
 
@@ -158,9 +222,16 @@
     reg_args is the vars that need to have registers allocated for them
     reg_arg_regclasses is the type of register that needs to be allocated
     '''
+    info = (0,0)
+    def __init__(self):
+        self.__magic_index = _var_index[0]
+        _var_index[0] += 1
+    def __repr__(self):
+        return "<%s %d>" % (self.__class__.__name__, self.__magic_index)
 
 class Insn_GPR__GPR_GPR(Insn):
     def __init__(self, methptr, result, args):
+        Insn.__init__(self)
         self.methptr = methptr
 
         self.result = result
@@ -169,9 +240,9 @@
         self.reg_arg_regclasses = [GP_REGISTER, GP_REGISTER]
 
     def allocate(self, allocator):
-        self.result_reg = allocator.var2reg[self.result]
-        self.arg_reg1 = allocator.var2reg[self.reg_args[0]]
-        self.arg_reg2 = allocator.var2reg[self.reg_args[1]]
+        self.result_reg = allocator.var2loc[self.result]
+        self.arg_reg1 = allocator.var2loc[self.reg_args[0]]
+        self.arg_reg2 = allocator.var2loc[self.reg_args[1]]
 
     def emit(self, asm):
         self.methptr(asm,
@@ -181,6 +252,7 @@
 
 class Insn_GPR__GPR_IMM(Insn):
     def __init__(self, methptr, result, args):
+        Insn.__init__(self)
         self.methptr = methptr
         self.imm = args[1]
 
@@ -189,8 +261,8 @@
         self.reg_args = [args[0]]
         self.reg_arg_regclasses = [GP_REGISTER]
     def allocate(self, allocator):
-        self.result_reg = allocator.var2reg[self.result]
-        self.arg_reg = allocator.var2reg[self.reg_args[0]]
+        self.result_reg = allocator.var2loc[self.result]
+        self.arg_reg = allocator.var2loc[self.reg_args[0]]
     def emit(self, asm):
         self.methptr(asm,
                      self.result_reg.number,
@@ -199,6 +271,7 @@
 
 class Insn_GPR__IMM(Insn):
     def __init__(self, methptr, result, args):
+        Insn.__init__(self)
         self.methptr = methptr
         self.imm = args[0]
 
@@ -207,14 +280,18 @@
         self.reg_args = []
         self.reg_arg_regclasses = []
     def allocate(self, allocator):
-        self.result_reg = allocator.var2reg[self.result]
+        self.result_reg = allocator.var2loc[self.result]
     def emit(self, asm):
         self.methptr(asm,
                      self.result_reg.number,
                      self.imm.value)
 
-class CMPW(Insn):
+class CMPInsn(Insn):
+    pass
+
+class CMPW(CMPInsn):
     def __init__(self, info, result, args):
+        Insn.__init__(self)
         self.info = info
 
         self.result = result
@@ -224,15 +301,16 @@
         self.reg_arg_regclasses = [GP_REGISTER, GP_REGISTER]
 
     def allocate(self, allocator):
-        self.result_reg = allocator.var2reg[self.result]
-        self.arg_reg1 = allocator.var2reg[self.reg_args[0]]
-        self.arg_reg2 = allocator.var2reg[self.reg_args[1]]
+        self.result_reg = allocator.var2loc[self.result]
+        self.arg_reg1 = allocator.var2loc[self.reg_args[0]]
+        self.arg_reg2 = allocator.var2loc[self.reg_args[1]]
 
     def emit(self, asm):
         asm.cmpw(self.result_reg.number, self.arg_reg1.number, self.arg_reg2.number)
 
-class CMPWI(Insn):
+class CMPWI(CMPInsn):
     def __init__(self, info, result, args):
+        Insn.__init__(self)
         self.info = info
         self.imm = args[1]
 
@@ -243,14 +321,15 @@
         self.reg_arg_regclasses = [GP_REGISTER]
 
     def allocate(self, allocator):
-        self.result_reg = allocator.var2reg[self.result]
-        self.arg_reg = allocator.var2reg[self.reg_args[0]]
+        self.result_reg = allocator.var2loc[self.result]
+        self.arg_reg = allocator.var2loc[self.reg_args[0]]
 
     def emit(self, asm):
         asm.cmpwi(self.result_reg.number, self.arg_reg.number, self.imm.value)
 
 class MTCTR(Insn):
     def __init__(self, result, args):
+        Insn.__init__(self)
         self.result = result
         self.result_regclass = CT_REGISTER
 
@@ -258,13 +337,14 @@
         self.reg_arg_regclasses = [GP_REGISTER]
 
     def allocate(self, allocator):
-        self.arg_reg = allocator.var2reg[self.reg_args[0]]
+        self.arg_reg = allocator.var2loc[self.reg_args[0]]
 
     def emit(self, asm):
         asm.mtctr(self.arg_reg.number)
 
 class Jump(Insn):
     def __init__(self, gv_cond, gv_target, jump_if_true):
+        Insn.__init__(self)
         self.gv_cond = gv_cond
         self.gv_target = gv_target
         self.jump_if_true = jump_if_true
@@ -274,8 +354,8 @@
         self.reg_args = [gv_cond, gv_target]
         self.reg_arg_regclasses = [CR_FIELD, CT_REGISTER]
     def allocate(self, allocator):
-        assert allocator.var2reg[self.reg_args[1]] is ctr
-        self.crf = allocator.var2reg[self.reg_args[0]]
+        assert allocator.var2loc[self.reg_args[1]] is ctr
+        self.crf = allocator.var2loc[self.reg_args[0]]
         self.bit, self.negated = allocator.crfinfo[self.crf.number]
     def emit(self, asm):
         if self.negated ^ self.jump_if_true:
@@ -288,33 +368,51 @@
     """ A special instruction inserted by our register "allocator."  It
     indicates that we need to load a value from the stack into a register
     because we spilled a particular value. """
-    def __init__(self, var, reg, offset):
+    def __init__(self, var, reg, stack):
         """
         var --- the var we spilled (a Var)
         reg --- the reg we spilled it from (an integer)
         offset --- the offset on the stack we spilled it to (an integer)
         """
+        Insn.__init__(self)
         self.var = var
         self.reg = reg
-        self.offset = offset
+        self.stack = stack
     def emit(self, asm):
-        asm.lwz(self.reg.number, rSP, self.offset)
+        asm.lwz(self.reg.number, rSP, self.stack.offset)
 
 class Spill(Insn):
     """ A special instruction inserted by our register "allocator."
     It indicates that we need to store a value from the register into
     the stack because we spilled a particular value."""
-    def __init__(self, var, reg, offset):
+    def __init__(self, var, reg, stack):
         """
         var --- the var we are spilling (a Var)
         reg --- the reg we are spilling it from (an integer)
         offset --- the offset on the stack we are spilling it to (an integer)
         """
+        Insn.__init__(self)
         self.var = var
         self.reg = reg
-        self.offset = offset
+        self.stack = stack
     def emit(self, asm):
-        asm.stw(self.reg.number, rSP, self.offset)
+        asm.stw(self.reg.number, rSP, self.stack.offset)
+
+class Return(Insn):
+    """ Ensures the return value is in r3 """
+    def __init__(self, var):
+        Insn.__init__(self)
+        self.var = var
+        self.reg_args = [self.var]
+        self.reg_arg_regclasses = [GP_REGISTER]
+        self.result = None
+        self.result_regclass = NO_REGISTER
+        self.reg = None
+    def allocate(self, allocator):
+        self.reg = allocator.var2loc[self.var]
+    def emit(self, asm):
+        if self.reg.number != 3:
+            asm.mr(r3, self.reg.number)
 
 from pypy.jit.codegen.ppc import codebuf_posix as memhandler
 from ctypes import POINTER, cast, c_char, c_void_p, CFUNCTYPE, c_int
@@ -336,25 +434,129 @@
     self.mc.write(value)
 RPPCAssembler.emit = emit
 
-def prepare_for_jump(builder, cur_locations, target):
-    assert len(target.arg_locations) == len(cur_locations)
-    targetregs = target.arg_locations
-    outregs = cur_locations
-    for i in range(len(cur_locations)):
-        treg = targetregs[i]
-        oreg = outregs[i]
-        if oreg == treg:
-            continue
-        if treg in outregs:
-            outi = outregs.index(treg)
-            assert outi > i
-            builder.asm.xor(treg.number, treg.number, oreg.number)
-            builder.asm.xor(oreg.number, treg.number, oreg.number)
-            builder.asm.xor(treg.number, treg.number, oreg.number)
-            outregs[outi] = oreg
-            outregs[i] == treg
-        else:
-            builder.asm.mr(treg.number, oreg.number)
+class CycleData:
+    # tar2src  -> map target var to source var
+    # src2tar  -> map source var to target var (!)
+    # tar2loc  -> map target var to location
+    # src2loc  -> map source var to location
+    # loc2src  -> map location to source var
+    # srcstack -> list of source vars
+    # freshctr -> how many fresh locations have we made so far
+    # emitted  -> list of emitted targets
+    pass
+
+def emit_moves(gen, tar2src, tar2loc, src2loc):
+
+    # Basic idea:
+    #
+    #   Construct a graph for each move (Ti <- Si)
+    #   There is an edge between two nodes i and j if loc[Ti] == loc[Sj]
+    #   If there are no cycles, then a simple tree walk will suffice
+    #   Algorithm is: avoid cycles by creating temps when needed
+    #
+    #   Do tree walk, if backedge is detected to node j, then move Sj to
+    #   a fresh slot Sn, and change Sj from Ti <- Sj to Ti <- Sn.  Now
+    #   there is no need for the backedge, so don't add it and continue.
+    #   When finishing a leaf node, emit the move.
+
+    tarvars = tar2src.keys()
+    
+    data = CycleData()
+    data.tar2src = tar2src
+    data.src2tar = {}
+    data.tar2loc = tar2loc
+    data.src2loc = src2loc
+    data.loc2src = {}
+    data.srcstack = []
+    data.freshctr = 0
+    data.emitted = []
+
+    for tar, src in tar2src.items():
+        data.src2tar[src] = tar
+
+    for src, loc in src2loc.items():
+        if src in data.src2tar:
+            data.loc2src[loc] = src
+
+    for tarvar in tarvars:
+        _cycle_walk(gen, tarvar, data)
+            
+    return data
+
+def _cycle_walk(gen, tarvar, data):
+
+    if tarvar in data.emitted: return
+
+    tarloc = data.tar2loc[tarvar]
+    srcvar = data.tar2src[tarvar]
+    srcloc = data.src2loc[srcvar]
+
+    # if location we are about to write to is not going to be read
+    # by anyone, we are safe
+    if tarloc not in data.loc2src:
+        gen.emit_move(tarloc, srcloc)
+        data.emitted.append(tarvar)
+        return
+
+    # Find source node that conflicts with us
+    conflictsrcvar = data.loc2src[tarloc]
+
+    if conflictsrcvar not in data.srcstack:
+        # No cycle on our stack yet
+        data.srcstack.append(srcvar)
+        _cycle_walk(gen, data.src2tar[conflictsrcvar], data)
+        srcloc = data.src2loc[srcvar] # warning: may have changed, so reload
+        gen.emit_move(tarloc, srcloc)
+        data.emitted.append(tarvar)
+        return 
+    
+    # Cycle detected, break it by moving the other node's source data
+    # somewhere else so we can overwrite it
+    freshloc = gen.create_fresh_location()
+    conflictsrcloc = data.src2loc[conflictsrcvar]
+    gen.emit_move(freshloc, conflictsrcloc)
+    data.src2loc[conflictsrcvar] = freshloc
+    gen.emit_move(tarloc, srcloc) # now safe to do our move
+    data.emitted.append(tarvar)
+    return
+
+class JumpPatchupGenerator(object):
+
+    def __init__(self, asm, regalloc):
+        self.asm = asm
+        self.regalloc = regalloc
+
+    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)
+        elif not tarloc.is_register and srcloc.is_register:
+            self.asm.stw(srcloc.number, rSP, 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)
+
+    def create_fresh_location(self):
+        offset = self.regalloc.spill()
+        return stack_slot(offset)
+
+def prepare_for_jump(asm, allocator, sourcevars, src2loc, target):
+
+    tar2src = {}     # tar var -> src var
+    tar2loc = {}
+
+    # construct mapping of targets to sources; note that "target vars"
+    # and "target locs" are the same thing right now
+    targetlocs = target.arg_locations
+    for i in range(len(targetlocs)):
+        tloc = targetlocs[i]
+        tar2loc[tloc] = tloc
+        tar2src[tloc] = sourcevars[i]
+
+    gen = JumpPatchupGenerator(asm, allocator)
+    emit_moves(gen, tar2src, tar2loc, src2loc)
 
 class MachineCodeBlock:
 
@@ -475,21 +677,21 @@
 
     def emit(self):
         if self.parent is not None:
-            allocator = RegisterAllocation(self.parent.var2reg)
+            allocator = RegisterAllocation(
+                self.rgenop.MINUSERREG, self.parent.var2loc)
         else:
-            allocator = RegisterAllocation(self.initial_varmapping)
+            allocator = RegisterAllocation(
+                self.rgenop.MINUSERREG, self.initial_varmapping)
         self.insns = allocator.allocate_for_insns(self.insns)
         for insn in self.insns:
             insn.emit(self.asm)
-        self.var2reg = allocator.var2reg
+        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()
-        reg = allocator.var2reg[gv_returnvar]
-        if reg.number != 3:
-            self.asm.mr(r3, reg.number)
 
         # Emit standard epilogue:
         self.asm.lwz(rSP,rSP,0)     # restore old SP
@@ -501,8 +703,8 @@
 
     def finish_and_goto(self, outputargs_gv, target):
         allocator = self.emit()
-        cur_locations = [allocator.var2reg[v] for v in outputargs_gv]
-        prepare_for_jump(self, cur_locations, target)
+        prepare_for_jump(
+            self.asm, allocator, outputargs_gv, allocator.var2loc, target)
         self.asm.load_word(0, target.startaddr)
         self.asm.mtctr(0)
         self.asm.bctr()
@@ -515,9 +717,9 @@
             gv = args_gv[i] = gv.load(self)
         allocator = self.emit()
         for gv in args_gv:
-            arg_locations.append(allocator.var2reg[gv])
+            arg_locations.append(allocator.var2loc[gv])
         self.insns = []
-        self.initial_varmapping = allocator.var2reg
+        self.initial_varmapping = allocator.var2loc
         return Label(self.asm.mc.tell(), arg_locations)
 
     def newvar(self):
@@ -532,6 +734,13 @@
         gv_result = self.newvar()
         return (gv_result, gv_x.load(self))
 
+    def op_int_mul(self, gv_x, gv_y):
+        gv_result, gv_x, gv_y = self.new_and_load_2(gv_x, gv_y)
+        self.insns.append(
+            Insn_GPR__GPR_GPR(RPPCAssembler.mullw,
+                              gv_result, [gv_x, gv_y]))
+        return gv_result        
+
     def op_int_add(self, gv_x, gv_y):
         if isinstance(gv_y, IntConst) and abs(gv_y.value) < 2**16:
             gv_result = self.newvar()
@@ -604,6 +813,10 @@
 class RPPCGenOp(AbstractRGenOp):
     from pypy.jit.codegen.i386.codebuf import MachineCodeBlock
 
+    # minimum register we will use for register allocation
+    # we can artifically restrict it for testing purposes
+    MINUSERREG = 3
+
     def __init__(self):
         self.mcs = []   # machine code blocks where no-one is currently writing
 

Added: pypy/dist/pypy/jit/codegen/ppc/test/test_emit_moves.py
==============================================================================
--- (empty file)
+++ pypy/dist/pypy/jit/codegen/ppc/test/test_emit_moves.py	Fri Nov  3 19:16:19 2006
@@ -0,0 +1,54 @@
+import py
+from pypy.jit.codegen.ppc.rgenop import emit_moves
+
+class TheHeap(object):
+    def __init__(self, locs):
+        self.data = {}
+        for i in range(locs):
+            self.data[i] = i
+        self.numlocs = locs
+    def emit_move(self, tar, src):
+        self.data[tar] = self.data[src]
+    def create_fresh_location(self):
+        self.numlocs += 1
+        return self.numlocs-1
+
+def test_simple_cycle():
+    heap = TheHeap(2)
+    tar2src = {'A':'b', 'B':'a'}
+    tar2loc = {'A':0, 'B':1}
+    src2loc = {'a':0, 'b':1}
+    assert heap.data[0] == 0
+    assert heap.data[1] == 1
+    emit_moves(heap, tar2src, tar2loc, src2loc)
+    assert heap.data[0] == 1
+    assert heap.data[1] == 0
+    assert heap.numlocs == 3 # only creates 1 extra loc
+
+def test_cycle_3():
+    heap = TheHeap(3)
+    tar2src = {'A':'b', 'B':'c', 'C':'a'}
+    tar2loc = {'A':0, 'B':1, 'C':2}
+    src2loc = {'a':0, 'b':1, 'c':2}
+    assert heap.data[0] == 0
+    assert heap.data[1] == 1
+    assert heap.data[2] == 2
+    emit_moves(heap, tar2src, tar2loc, src2loc)
+    assert heap.data[0] == 1
+    assert heap.data[1] == 2
+    assert heap.data[2] == 0
+    assert heap.numlocs == 4 # only creates 1 extra loc
+
+def test_dag():
+    heap = TheHeap(3)
+    tar2src = {'A':'a', 'B':'b'}
+    tar2loc = {'A':0, 'B':1}
+    src2loc = {'a':1, 'b':2}
+    assert heap.data[0] == 0
+    assert heap.data[1] == 1
+    assert heap.data[2] == 2
+    emit_moves(heap, tar2src, tar2loc, src2loc)
+    assert heap.data[0] == 1
+    assert heap.data[1] == 2
+    assert heap.data[2] == 2
+    assert heap.numlocs == 3 # only creates 1 extra loc

Modified: pypy/dist/pypy/jit/codegen/ppc/test/test_rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/ppc/test/test_rgenop.py	(original)
+++ pypy/dist/pypy/jit/codegen/ppc/test/test_rgenop.py	Fri Nov  3 19:16:19 2006
@@ -1,5 +1,15 @@
+import py
 from pypy.jit.codegen.ppc.rgenop import RPPCGenOp
 from pypy.jit.codegen.test.rgenop_tests import AbstractRGenOpTests
 
+class FewRegisters(RPPCGenOp):
+    MINUSERREG = 29
+
 class TestRPPCGenop(AbstractRGenOpTests):
     RGenOp = RPPCGenOp
+
+class TestRPPCGenopNoRegs(AbstractRGenOpTests):
+    RGenOp = FewRegisters
+
+    def compile(self, runner, argtypes):
+        py.test.skip("Skip compiled tests w/ restricted register allocator")

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	Fri Nov  3 19:16:19 2006
@@ -96,31 +96,42 @@
 
 
 def make_goto(rgenop):
+    # z = 1
     # while x > 0:
     #     y += x
+    #     z *= x
     #     x -= 1
+    # y += z
     # return y
     signed_kind = rgenop.kindToken(lltype.Signed)
     sigtoken = rgenop.sigToken(FUNC2)
     builder, entrypoint, [gv_x, gv_y] = rgenop.newgraph(sigtoken)
 
     # loop start block
-    args_gv = [gv_x, gv_y]
-    loopblock = builder.enter_next_block([signed_kind, signed_kind], args_gv)
-    [gv_x, gv_y] = args_gv
+    args_gv = [gv_x, gv_y, rgenop.genconst(1)]
+    loopblock = builder.enter_next_block(
+        [signed_kind, signed_kind, signed_kind], args_gv)
+    [gv_x, gv_y, gv_z] = args_gv
 
     gv_cond = builder.genop2("int_gt", gv_x, rgenop.genconst(0))
     bodybuilder = builder.jump_if_true(gv_cond)
-    builder.finish_and_return(sigtoken, gv_y)
+    args_gv = [gv_y, gv_z]
+    builder.enter_next_block(
+        [signed_kind, signed_kind], args_gv)
+    [gv_y, gv_z] = args_gv
+    gv_y3 = builder.genop2("int_add", gv_y, gv_z)
+    builder.finish_and_return(sigtoken, gv_y3)
 
     # loop body
-    args_gv = [gv_y, gv_x]
-    bodybuilder.enter_next_block([signed_kind, signed_kind], args_gv)
-    [gv_y, gv_x] = args_gv
+    args_gv = [gv_z, gv_y, gv_x]
+    bodybuilder.enter_next_block(
+        [signed_kind, signed_kind, signed_kind], args_gv)
+    [gv_z, gv_y, gv_x] = args_gv
 
+    gv_z2 = bodybuilder.genop2("int_mul", gv_x, gv_z)
     gv_y2 = bodybuilder.genop2("int_add", gv_x, gv_y)
     gv_x2 = bodybuilder.genop2("int_sub", gv_x, rgenop.genconst(1))
-    bodybuilder.finish_and_goto([gv_x2, gv_y2], loopblock)
+    bodybuilder.finish_and_goto([gv_x2, gv_y2, gv_z2], loopblock)
 
     # done
     gv_gotofn = rgenop.gencallableconst(sigtoken, "goto", entrypoint)
@@ -293,17 +304,17 @@
         gv_gotofn = make_goto(rgenop)
         print gv_gotofn.value
         fnptr = cast(c_void_p(gv_gotofn.value), CFUNCTYPE(c_int, c_int, c_int))
-        res = fnptr(30, 17)    # <== the segfault is here
-        assert res == 31 * 15 + 17
+        res = fnptr(10, 17)    # <== the segfault is here
+        assert res == 3628872
         res = fnptr(3, 17)    # <== or here
-        assert res == 23
+        assert res == 29
 
     def test_goto_compile(self):
         fn = self.compile(get_goto_runner(self.RGenOp), [int, int])
-        res = fn(30, 17)
-        assert res == 31 * 15 + 17
+        res = fn(10, 17)
+        assert res == 3628872
         res = fn(3, 17)
-        assert res == 23
+        assert res == 29
 
     def test_if_direct(self):
         rgenop = self.RGenOp()



More information about the Pypy-commit mailing list