[pypy-svn] r33135 - in pypy/dist/pypy/jit/codegen/i386: . test

arigo at codespeak.net arigo at codespeak.net
Tue Oct 10 20:42:35 CEST 2006


Author: arigo
Date: Tue Oct 10 20:42:33 2006
New Revision: 33135

Modified:
   pypy/dist/pypy/jit/codegen/i386/codebuf.py
   pypy/dist/pypy/jit/codegen/i386/rgenop.py
   pypy/dist/pypy/jit/codegen/i386/ri386setup.py
   pypy/dist/pypy/jit/codegen/i386/test/test_rgenop.py
Log:
(pedronis, arigo)

Implement flexswitch in the i386 back-end.

InMemoryCodeBuilder is a kind of MachineCodeBlock that
produces instructions at a pre-existing range of addresses,
so that the flexswitch can dynamically overwrite parts of
the generated code.


Modified: pypy/dist/pypy/jit/codegen/i386/codebuf.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/codebuf.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/codebuf.py	Tue Oct 10 20:42:33 2006
@@ -12,11 +12,16 @@
 class CodeBlockOverflow(Exception):
     pass
 
-class MachineCodeBlock(I386CodeBuilder):
+class InMemoryCodeBuilder(I386CodeBuilder):
 
-    def __init__(self, map_size):
-        res = memhandler.alloc(map_size)
-        self._data = cast(res, POINTER(c_char * map_size))
+    def __init__(self, start, end):
+        map_size = end - start
+        res = c_void_p(start)
+        data = cast(res, POINTER(c_char * map_size))
+        self._init(data, map_size)
+
+    def _init(self, data, map_size):
+        self._data = data
         self._size = map_size
         self._pos = 0
 
@@ -33,13 +38,21 @@
         baseaddr = cast(self._data, c_void_p).value
         return baseaddr + self._pos
 
-    def __del__(self):
-        memhandler.free(cast(self._data, PTR), self._size)
-
     def execute(self, arg1, arg2):
         fnptr = cast(self._data, binaryfn)
         return fnptr(arg1, arg2)
 
+
+class MachineCodeBlock(InMemoryCodeBuilder):
+
+    def __init__(self, map_size):
+        res = memhandler.alloc(map_size)
+        data = cast(res, POINTER(c_char * map_size))
+        self._init(data, map_size)
+
+    def __del__(self):
+        memhandler.free(cast(self._data, PTR), self._size)
+
 binaryfn = CFUNCTYPE(c_int, c_int, c_int)    # for testing
 
 # ____________________________________________________________

Modified: pypy/dist/pypy/jit/codegen/i386/rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/rgenop.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/rgenop.py	Tue Oct 10 20:42:33 2006
@@ -1,8 +1,9 @@
 from pypy.rpython.objectmodel import specialize
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.jit.codegen.i386.ri386 import *
+from pypy.jit.codegen.i386.codebuf import InMemoryCodeBuilder
 from pypy.jit.codegen.model import AbstractRGenOp, CodeGenBlock, CodeGenerator
-from pypy.jit.codegen.model import GenVar, GenConst
+from pypy.jit.codegen.model import GenVar, GenConst, CodeGenSwitch
 from pypy.rpython import objectmodel
 from pypy.rpython.annlowlevel import llhelper
 
@@ -130,6 +131,50 @@
         self.stackdepth = stackdepth
 
 
+class FlexSwitch(CodeGenSwitch):
+
+    def __init__(self, rgenop):
+        self.rgenop = rgenop
+        self.default_case_addr = 0
+
+    def initialize(self, builder, gv_exitswitch):
+        RESERVED = 11*8+5      # XXX quite a lot for now :-/
+        mc = builder.mc
+        mc.MOV(eax, gv_exitswitch.operand(builder))
+        self.saved_state = builder._save_state()
+        pos = mc.tell()
+        mc.UD2()
+        mc.write('\x00' * (RESERVED-1))
+        self.nextfreepos = pos
+        self.endfreepos = pos + RESERVED
+
+    def add_case(self, gv_case):
+        rgenop = self.rgenop
+        targetbuilder = Builder._new_from_state(rgenop, self.saved_state)
+        start = self.nextfreepos
+        end   = self.endfreepos
+        mc = InMemoryCodeBuilder(start, end)
+        mc.CMP(eax, gv_case.operand(None))
+        mc.JE(rel32(targetbuilder.mc.tell()))
+        pos = mc.tell()
+        if self.default_case_addr:
+            mc.JMP(rel32(self.default_case_addr))
+        else:
+            mc.UD2()
+        self.nextfreepos = pos
+        return targetbuilder
+
+    def add_default(self):
+        rgenop = self.rgenop
+        targetbuilder = Builder._new_from_state(rgenop, self.saved_state)
+        self.default_case_addr = targetbuilder.mc.tell()
+        start = self.nextfreepos
+        end   = self.endfreepos
+        mc = InMemoryCodeBuilder(start, end)
+        mc.JMP(rel32(self.default_case_addr))
+        return targetbuilder
+
+
 class Builder(CodeGenerator):
 
     def __init__(self, rgenop, mc, stackdepth):
@@ -149,6 +194,13 @@
     def _fork(self):
         return self.rgenop.openbuilder(self.stackdepth)
 
+    def _save_state(self):
+        return self.stackdepth
+
+    @staticmethod
+    def _new_from_state(rgenop, stackdepth):
+        return rgenop.openbuilder(stackdepth)
+
     @specialize.arg(1)
     def genop1(self, opname, gv_arg):
         genmethod = getattr(self, 'op_' + opname)
@@ -299,6 +351,12 @@
         self.mc.JMP(rel32(targetblock.startaddr))
         self._close()
 
+    def flexswitch(self, gv_exitswitch):
+        result = FlexSwitch(self.rgenop)
+        result.initialize(self, gv_exitswitch)
+        self._close()
+        return result
+
     # ____________________________________________________________
 
     def stack_access(self, stackpos):
@@ -660,6 +718,9 @@
             return self.MachineCodeBlock(65536)   # XXX supposed infinite for now
 
     def close_mc(self, mc):
+        # an open 'mc' is ready for receiving code... but it's also ready
+        # for being garbage collected, so be sure to close it if you
+        # want the generated code to stay around :-)
         self.mcs.append(mc)
 
     def openbuilder(self, stackdepth):

Modified: pypy/dist/pypy/jit/codegen/i386/ri386setup.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/ri386setup.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/ri386setup.py	Tue Oct 10 20:42:33 2006
@@ -406,6 +406,9 @@
 BREAKPOINT.mode0(['\xCC'])
 BREAKPOINT.as_alias = "INT3"
 
+UD2 = Instruction()      # reserved as an illegal instruction
+UD2.mode0(['\x0F\x0B'])
+
 o16 = Instruction()      # 16-bits instruction prefix (name from 'nasm')
 o16.mode0(['\x66'])
 

Modified: pypy/dist/pypy/jit/codegen/i386/test/test_rgenop.py
==============================================================================
--- pypy/dist/pypy/jit/codegen/i386/test/test_rgenop.py	(original)
+++ pypy/dist/pypy/jit/codegen/i386/test/test_rgenop.py	Tue Oct 10 20:42:33 2006
@@ -2,7 +2,7 @@
 from pypy.rpython.llinterp import LLInterpreter
 from pypy.rpython.objectmodel import keepalive_until_here
 from pypy.rpython.annlowlevel import MixLevelAnnotatorPolicy
-from pypy.translator.c.test.test_genc import compile
+from pypy.translator.c.test import test_genc
 from pypy.jit.codegen.i386.rgenop import RI386GenOp
 
 from ctypes import c_void_p, cast, CFUNCTYPE, c_int
@@ -12,6 +12,11 @@
     rtyper = None
 GENOP_POLICY = MixLevelAnnotatorPolicy(PseudoAnnhelper())
 
+def compile(runner, argtypes):
+    return test_genc.compile(runner, [int, int],
+                             gcpolicy        = 'boehm',
+                             annotatorpolicy = GENOP_POLICY)
+
 # ____________________________________________________________
 
 FUNC = lltype.FuncType([lltype.Signed], lltype.Signed)
@@ -50,7 +55,7 @@
     assert res == 42
 
 def test_adder_compile():
-    fn = compile(runner, [int, int], annotatorpolicy=GENOP_POLICY)
+    fn = compile(runner, [int, int])
     res = fn(9080983, -9080941)
     assert res == 42
 
@@ -101,7 +106,7 @@
     assert res == 42
 
 def test_dummy_compile():
-    fn = compile(dummy_runner, [int, int], annotatorpolicy=GENOP_POLICY)
+    fn = compile(dummy_runner, [int, int])
     res = fn(40, 37)
     assert res == 42
 
@@ -161,7 +166,7 @@
     assert res == 17
 
 def test_branching_compile():
-    fn = compile(branching_runner, [int, int], annotatorpolicy=GENOP_POLICY)
+    fn = compile(branching_runner, [int, int])
     res = fn(30, 17)
     assert res == 29
     res = fn(3, 17)
@@ -229,8 +234,82 @@
     assert res == 23
 
 def test_goto_compile():
-    fn = compile(goto_runner, [int, int], annotatorpolicy=GENOP_POLICY)
+    fn = compile(goto_runner, [int, int])
     res = fn(30, 17)
     assert res == 31 * 15 + 17
     res = fn(3, 17)
     assert res == 23
+
+# ____________________________________________________________
+
+def build_switch(rgenop):
+    """
+    def f(v0, v1):
+        if v0 == 0: # switch
+            return 21*v1
+        elif v0 == 1:
+            return 21+v1
+        else:
+            return v1
+    """
+    signed_tok = rgenop.kindToken(lltype.Signed)
+    f2_token = rgenop.sigToken(FUNC2)
+    builder, graph, (gv0, gv1) = rgenop.newgraph(f2_token)
+
+    flexswitch = builder.flexswitch(gv0)
+    const21 = rgenop.genconst(21)
+
+    # case == 0
+    const0 = rgenop.genconst(0)
+    case_builder = flexswitch.add_case(const0)
+    case_args_gv = [gv1]
+    case_builder.enter_next_block([signed_tok], case_args_gv)
+    [gv1_case0] = case_args_gv
+    gv_res_case0 = case_builder.genop2('int_mul', const21, gv1_case0)
+    case_builder.finish_and_return(f2_token, gv_res_case0)
+    # default
+    default_builder = flexswitch.add_default()
+    default_args_gv = [gv1]
+    default_builder.enter_next_block([signed_tok], default_args_gv)
+    [gv1_default] = default_args_gv
+    default_builder.finish_and_return(f2_token, gv1_default)
+    # case == 1
+    const1 = rgenop.genconst(1)
+    case_builder = flexswitch.add_case(const1)
+    case_args_gv = [gv1]
+    case_builder.enter_next_block([signed_tok], case_args_gv)
+    [gv1_case1] = case_args_gv
+    gv_res_case1 = case_builder.genop2('int_add', const21, gv1_case1)
+    case_builder.finish_and_return(f2_token, gv_res_case1)
+
+    gv_switch = rgenop.gencallableconst(f2_token, "switch", graph)
+    return gv_switch
+
+def switch_runner(x, y):
+    rgenop = RI386GenOp()
+    gv_switchfn = build_switch(rgenop)
+    switchfn = gv_switchfn.revealconst(lltype.Ptr(FUNC2))
+    res = switchfn(x, y)
+    keepalive_until_here(rgenop)    # to keep the code blocks alive
+    return res
+
+def test_switch_direct():
+    rgenop = RI386GenOp()
+    gv_switchfn = build_switch(rgenop)
+    print gv_switchfn.value
+    fnptr = cast(c_void_p(gv_switchfn.value), CFUNCTYPE(c_int, c_int, c_int))
+    res = fnptr(0, 2)
+    assert res == 42
+    res = fnptr(1, 16)
+    assert res == 37
+    res = fnptr(42, 16)
+    assert res == 16
+
+def test_switch_compile():
+    fn = compile(switch_runner, [int, int])
+    res = fn(0, 2)
+    assert res == 42
+    res = fn(1, 17)
+    assert res == 38
+    res = fn(42, 18)
+    assert res == 18



More information about the Pypy-commit mailing list