[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