[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