[pypy-svn] r18494 - in pypy/dist/pypy/translator/asm: . test

mwh at codespeak.net mwh at codespeak.net
Wed Oct 12 18:21:46 CEST 2005


Author: mwh
Date: Wed Oct 12 18:21:43 2005
New Revision: 18494

Modified:
   pypy/dist/pypy/translator/asm/regmap.py
   pypy/dist/pypy/translator/asm/simulator.py
   pypy/dist/pypy/translator/asm/test/test_simulator.py
Log:
(AndrewT MH) Fixed register allocation and merged into simulator with translator

Modified: pypy/dist/pypy/translator/asm/regmap.py
==============================================================================
--- pypy/dist/pypy/translator/asm/regmap.py	(original)
+++ pypy/dist/pypy/translator/asm/regmap.py	Wed Oct 12 18:21:43 2005
@@ -1,12 +1,24 @@
+"""
+To convert our IRM to an FRM machine, we must perform some swapping of the registers.  This is in effect
+'paging', we are only allowed to perform XCHG operations on the slow (memory) registers, while we can do
+anything with our fast (CPU registers).
 
+There are various algorithms available, including the Linear Scan Algorithm (google this), but for now we
+have decided to implement a simple, but (hopefully) reasonably effective last-recently-used algortithm.
 
-import random
-random.seed(101)
-n=10
-m=5
-initial=[ random.randint(1,m) for x in range(n)]
+Regardless of the swap algorithm , at each stage we must keep track of which IRM register is held in which
+FRM register.  Our original test-suite simply gave the register usages, and checked the swap/usage sequence.
+
+We need to rebuild the suite, checking the register map at EACH stage of the process.  Fiddly, but important!
+
+We need some notation:
+
+IRMxxx denotes an Infinite Register Machine that will use at most xxx registers
+
+FRMxxx.yyy denotes a finite-register machine, with xxx fast registers, and a total of yyy registers.
+
+"""
 
-#now recode to n-register machine
 
 def remap(regs,n):
     return regs
@@ -36,6 +48,7 @@
     print re
     return re
 
+import sys
 
 def remap(regs,n):
     pipe=[]
@@ -47,21 +60,72 @@
         if goingin>n:
             goingout=pipe[-1]
             re.append((goingin,goingout))
+
+
             old2new[goingin]=goingout
             old2new[goingout]=goingin
+
             val=goingout
+            #having swapped, if we have any stray mapping,remove it.
+
         else:
             val=goingin
         pipe=[val]+pipe
         re.append(val)
+
+        if old2new.get(reg,reg)!=val:
+            print regs
+            print old2new
+            print pipe
+            print re
+            sys.exit(1)
         if len(pipe)>n:
-            pipe.pop()
+            popped=pipe.pop()   #this value fell out of the pipe
+
+    print re
+    return re
+
+
+def remap(regs,n):
+    pipe=[]
+    old2new=range(len(regs)+1)
+    re=[]
+    for reg in regs:
+        goingin=old2new[reg]
+        #print reg,pipe,old2new
+        if goingin>n:
+            goingout=pipe[-1]
+            re.append((goingin,goingout))
+
+            old2new[goingout],old2new[goingin] = old2new[goingin],old2new[goingout]
+            print '>>>',old2new
+            #old2new[goingin]=goingout
+            #old2new[goingout]=goingin
+
+            val=goingout
+            #having swapped, if we have any stray mapping,remove it.
+
+        else:
+            val=goingin
+        pipe=[val]+pipe
+        re.append(val)
+
+
+        if len(pipe)>n:
+            popped=pipe.pop()   #this value fell out of the pipe
+
+    print re
+    print range(20)
+    print old2new
     return re
 
 
 assert remap([1,2,3],3)==[1,2,3]
 assert remap([1,2,3],2)==[1,2,(3,1),1]
+
+
 assert remap([1,2,3,1],2)==[1,2,(3,1),1,(3,2),2]
+
 assert remap([1,2,3,4,2],2)==[1,2,(3,1),1,(4,2),2,(4,1),1]
 assert remap([1,2,3,2,1],2)==[1, 2, (3, 1), 1, 2, (3, 1), 1]
 
@@ -69,6 +133,108 @@
 assert remap([1,2,3,4,5,4,3,2,1],10)==[1,2,3,4,5,4,3,2,1]
 assert remap([1,2,3,4,5,4,3,2,1],3)==[1,2,3,(4,1),1,(5,2),2,1,3,(5,2),2,(4,1),1]
 
+#assert remap([1,2,4,3,4,1,5,3,5,6,1,2,8,7,6,8,9,7,9,10,1,2],5)==7   #this is a real-world example for PowerPC
+
+
+class Machine:
+    def __init__(self,nreg,regtot=100):
+        self._nreg=nreg
+        self._pipe=[]
+        self._regtot=regtot
+        self._old2new=range(0,regtot+1)  #this must be as big as the total number of registers+1 (we start at index 1)
+
+    def regIRM(self,regIRM):
+        ins=[]
+        reg=regIRM
+        goingin=self._old2new[reg]
+        if goingin>self._nreg:
+            goingout=self._pipe[-1]
+            ins.append((goingin,goingout))
+            self._old2new[goingout],self._old2new[goingin] = self._old2new[goingin],self._old2new[goingout]
+            val=goingout
+        else:
+            val=goingin
+        self._pipe=[val]+self._pipe
+        ins.append(val)
+
+        if len(self._pipe)>self._nreg:
+            self._pipe.pop()   #this value fell out of the pipe
+
+        self._lastFRMUSed=val
+        return ins
+
+
+    def lastFRMUsed(self):
+        return self._lastFRMUsed
+
+    def map(self):
+        import operator
+        """answer a map IRM notation -> current FRM notation"""
+        map={}
+        for reg in range(1,self._regtot+1):
+            map[reg]=operator.indexOf(self._old2new,reg)
+        return map
+
+    def identityMap(self):
+        """check that the current map is the identity map"""
+        map=self.map()
+        for reg in range(1,self._regtot+1):
+            if map[reg]!=reg:
+                return False
+        return True
+
+print '\n\n NEW SUITE \n\n'
+
+machine=Machine(1,1)   #create an FRM1 using an IRM1 translation
+assert machine.identityMap()  #check our registers
+assert machine.regIRM(1)==[1]    # use IRM register 1,check emitted code is just usage of register 1
+assert machine.map()=={1:1}
+
+machine=Machine(1,2)  # FRM1 using IRM2 translation
+assert machine.regIRM(1)==[1]         #use IRM1.1 ,no need to swap
+assert machine.map()=={1:1,2:2}       #identity mapping is preserved.
+
+assert machine.regIRM(2)==[(2,1),1]   #use IRM1.2.  We will need a swap
+assert machine.map()=={1:2,2:1}       #now we have non-trival permutation.
+assert not machine.identityMap()      #also, show it is not the identity map.(have to test this too!)
+
+assert machine.regIRM(1)==[(2,1),1]   #use IRM1.1 again (this time we should get a swap)
+assert machine.map()=={1:1,2:2}       #and recover the identity map
+
+#now a more involved example
+
+machine=Machine(3,5)  #2 real registers, from a machine which uses 5 registers
+assert machine.identityMap()
+assert machine.regIRM(1)==[1]
+assert machine.regIRM(2)==[2]
+assert machine.regIRM(3)==[3]
+assert machine.regIRM(1)==[1]
+assert machine.regIRM(4)==[(4,2),2]
+assert machine.regIRM(2)==[(4,3),3]
+assert machine.map()=={1:1,2:3,3:4,4:2,5:5}
+
+machine=Machine(3,5)  #3 real registers, from a machine which uses 5 registers
+assert machine.identityMap()
+assert machine.regIRM(1)==[1]
+assert machine.regIRM(2)==[2]
+assert machine.regIRM(3)==[3]
+assert machine.regIRM(4)==[(4,1),1]
+assert machine.regIRM(5)==[(5,2),2]
+assert machine.regIRM(2)==[(5,3),3]
+assert machine.regIRM(3)==[2]
+
+
+#assert machine.regIRM(2)==[(4,3),3]
+#assert machine.map()=={1:1,2:3,3:4,4:2,5:5}
+
+
+
+
+
+
+
+
+
 
 
 

Modified: pypy/dist/pypy/translator/asm/simulator.py
==============================================================================
--- pypy/dist/pypy/translator/asm/simulator.py	(original)
+++ pypy/dist/pypy/translator/asm/simulator.py	Wed Oct 12 18:21:43 2005
@@ -4,10 +4,104 @@
 from pypy.translator.asm.infregmachine import Instruction
 from pypy.objspace.flow.model import Constant
 
+"""
+Notes on the register allocation algorithm:
+
+
+To convert our IRM to an FRM machine, we must perform some swapping of the registers.  This is in effect
+'paging', we are only allowed to perform XCHG operations on the slow (memory) registers, while we can do
+anything with our fast (CPU registers).
+
+There are various algorithms available, including the Linear Scan Algorithm (google this), but for now we
+have decided to implement a simple, but (hopefully) reasonably effective last-recently-used algortithm.
+
+Regardless of the swap algorithm , at each stage we must keep track of which IRM register is held in which
+FRM register.  Our original test-suite simply gave the register usages, and checked the swap/usage sequence.
+
+We need to rebuild the suite, checking the register map at EACH stage of the process.  Fiddly, but important!
+
+We need some notation:
+
+IRMxxx denotes an Infinite Register Machine that will use at most xxx registers
+
+FRMxxx.yyy denotes a finite-register machine, with xxx fast registers, and a total of yyy registers.
+
+"""
+
+
+def regmap(regperm):
+    import operator
+    """answer a map IRM notation -> current FRM notation"""
+    map={}
+    for reg in range(1,len(regperm)):
+        #print reg,map,regperm
+        map[reg]=regperm.index(reg)
+    return map
+
+def maxRegister(commands):
+    pool=[]
+    for cmd in commands:
+        if not isinstance(cmd,str):
+            pool+=cmd.registers_used()
+    if pool==[]:
+        return 1
+    return max(pool)
+
+
+def TranslateProgram(commands,nreg):
+    """answer this program into one which only uses nreg fast registers"""
+    totreg=max([max(cmd.registers_used()) for cmd in commands])
+    totreg=maxRegister(commands)
+    assert nreg>=3 ,'Some commands may use 3 registers!!!!'
+    assert totreg>=nreg
+    newprog=[]
+    pipe=[]
+    old2new=range(0,totreg+1)  #this must be as big as the total number of registers+1 (we start at index 1)
+
+
+    for cmd in commands:
+        #if we use any registers, we must possibly swap first, and then remap
+        if  isinstance(cmd,str) or cmd.name in ('J','JT','JF'): #label or jump so  pass through
+            newprog.append(cmd)
+        else:
+            #so now remap the registers!
+
+            regused=cmd.registers_used()
+            t2p=[old2new[x] for x in regused]
+            for reg in regused:
+                goingin=regmap(old2new)[reg]
+                if goingin>nreg:
+                    if pipe[-1] not in t2p:
+                        index=-1
+                    elif pipe[-2] not in t2p:
+                        index=-2
+                    else:
+                        assert pipe[-3]!=goingin #this must be true for nreg>=3
+                        index=-3
+                    #now swap to end of pipe, so code as before works.
+                    pipe[index],pipe[-1]=pipe[-1],pipe[index]
+                    goingout=pipe[-1]
+                    newprog.append(Instruction('EXCH',(goingin,goingout)))
+                    old2new[goingout],old2new[goingin] = old2new[goingin],old2new[goingout]
+                    val=goingout
+                else:
+                    val=goingin
+                pipe=[val]+pipe
+
+                if len(pipe)>nreg:
+                    pipe.pop()   #this value fell out of the pipe
+                assert len(pipe)<=nreg
+            #now we can emit the command with registers remapped
+            rm=regmap(old2new)
+            newprog.append(cmd.renumber(rm))
+    return newprog
+
+
+
 class Machine:
 
-    def RunProgram(cls,commands,args=[],nreg=10,tracing=False):
-        #run this program
+    def RunProgram(cls,commands,args=[],tracing=False):
+        nreg=maxRegister(commands)
         machine=Machine(nreg,args)
         machine._tracing = tracing
         ip=0
@@ -65,8 +159,8 @@
                     args.append('r%s=%s'%(arg, self._registers[arg]))
                 else:
                     args.append(arg)
-            print opcode, ', '.join(map(str, args))
-        #will want to trap later to defer unimplemented to the LLInterpreter...
+            #print opcode, ', '.join(map(str, args))
+            #will want to trap later to defer unimplemented to the LLInterpreter...
         m = getattr(self,opcode,None)
         if m is not None:
             m(*operands)
@@ -87,7 +181,8 @@
         self._registers[destination]=self.register(source)
 
     def EXCH(self,destination,source):
-        self._registers[destination],self._registers[source]=self.register(source),self.register(destination)
+        #self._registers[destination],self._registers[source]=self.register(source),self.register(destination)
+        self._registers[destination],self._registers[source]=self._registers[source],self._registers[destination]
 
 
     def int_gt(self,rega,regb):
@@ -116,3 +211,6 @@
         self._registers[destination] = LLFrame.__dict__['op_'+opcode](None, *sourcevalues)
 
 
+
+
+

Modified: pypy/dist/pypy/translator/asm/test/test_simulator.py
==============================================================================
--- pypy/dist/pypy/translator/asm/test/test_simulator.py	(original)
+++ pypy/dist/pypy/translator/asm/test/test_simulator.py	Wed Oct 12 18:21:43 2005
@@ -1,5 +1,8 @@
+import random
 import autopath
-from pypy.translator.asm.simulator import Machine
+
+
+from pypy.translator.asm.simulator import Machine,TranslateProgram
 from pypy.objspace.flow.model import Constant
 from pypy.translator.asm.infregmachine import Instruction
 
@@ -63,7 +66,44 @@
           Instruction('LIA', (3, Constant(2))),
           'label',
           Instruction('RETPYTHON', (3,))]
-    
+
     assert Machine.RunProgram(prog, [1,2,3]) == 3
     assert Machine.RunProgram(prog, [2,1,3]) == 77
 
+
+#now we want to test our translation system.
+#we create a random program, and demonstrate that the results are the same in translated and untranslated form
+
+
+
+def test_translation(n=10,runs=20,size=50):
+    random.seed(1001) #ensure we get the same tests each time
+    def r():
+        return random.randint(1,n)
+
+    for x in range(runs):
+        prog=[]
+        for v in range(1,n+1):
+            prog.append(Instruction('LOAD',(v,Constant(v*10))))
+        for v in range(size):
+            prog.append(Instruction('EXCH',(r(),r())))
+            prog.append(Instruction('MOV',(r(),r())))
+            prog.append(Instruction('int_add',(r(),r(),r())))
+
+        prog.append('foo')
+        for exitreg in range(1,n+1):
+            prog[-1]=Instruction('RETPYTHON',(exitreg,))
+            assert Machine.RunProgram(prog)==Machine.RunProgram(TranslateProgram(prog,nreg=3))
+
+
+def test_zeroRegisterAbuse():
+    try:
+        Machine.RunProgram([Instruction('MOV',(0,0))])
+    except AssertionError:
+        pass
+    else:
+        assert False, "should not get here"
+
+
+
+



More information about the Pypy-commit mailing list