[pypy-svn] r53071 - in pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx: . test
fijal at codespeak.net
fijal at codespeak.net
Sat Mar 29 00:36:57 CET 2008
Author: fijal
Date: Sat Mar 29 00:36:56 2008
New Revision: 53071
Added:
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/__init__.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/autopath.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/conftest.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/rgenop.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/__init__.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/test_rgenop.py (contents, props changed)
pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/viewcode.py (contents, props changed)
Log:
Another approach. Port the old backend (which seems to mostly pass tests)
and try to modify it a bit later.
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/__init__.py
==============================================================================
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/autopath.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/autopath.py Sat Mar 29 00:36:56 2008
@@ -0,0 +1,114 @@
+"""
+self cloning, automatic path configuration
+
+copy this into any subdirectory of pypy from which scripts need
+to be run, typically all of the test subdirs.
+The idea is that any such script simply issues
+
+ import autopath
+
+and this will make sure that the parent directory containing "pypy"
+is in sys.path.
+
+If you modify the master "autopath.py" version (in pypy/tool/autopath.py)
+you can directly run it which will copy itself on all autopath.py files
+it finds under the pypy root directory.
+
+This module always provides these attributes:
+
+ pypydir pypy root directory path
+ this_dir directory where this autopath.py resides
+
+"""
+
+
+def __dirinfo(part):
+ """ return (partdir, this_dir) and insert parent of partdir
+ into sys.path. If the parent directories don't have the part
+ an EnvironmentError is raised."""
+
+ import sys, os
+ try:
+ head = this_dir = os.path.realpath(os.path.dirname(__file__))
+ except NameError:
+ head = this_dir = os.path.realpath(os.path.dirname(sys.argv[0]))
+
+ while head:
+ partdir = head
+ head, tail = os.path.split(head)
+ if tail == part:
+ break
+ else:
+ raise EnvironmentError, "'%s' missing in '%r'" % (partdir, this_dir)
+
+ pypy_root = os.path.join(head, '')
+ try:
+ sys.path.remove(head)
+ except ValueError:
+ pass
+ sys.path.insert(0, head)
+
+ munged = {}
+ for name, mod in sys.modules.items():
+ if '.' in name:
+ continue
+ fn = getattr(mod, '__file__', None)
+ if not isinstance(fn, str):
+ continue
+ newname = os.path.splitext(os.path.basename(fn))[0]
+ if not newname.startswith(part + '.'):
+ continue
+ path = os.path.join(os.path.dirname(os.path.realpath(fn)), '')
+ if path.startswith(pypy_root) and newname != part:
+ modpaths = os.path.normpath(path[len(pypy_root):]).split(os.sep)
+ if newname != '__init__':
+ modpaths.append(newname)
+ modpath = '.'.join(modpaths)
+ if modpath not in sys.modules:
+ munged[modpath] = mod
+
+ for name, mod in munged.iteritems():
+ if name not in sys.modules:
+ sys.modules[name] = mod
+ if '.' in name:
+ prename = name[:name.rfind('.')]
+ postname = name[len(prename)+1:]
+ if prename not in sys.modules:
+ __import__(prename)
+ if not hasattr(sys.modules[prename], postname):
+ setattr(sys.modules[prename], postname, mod)
+
+ return partdir, this_dir
+
+def __clone():
+ """ clone master version of autopath.py into all subdirs """
+ from os.path import join, walk
+ if not this_dir.endswith(join('pypy','tool')):
+ raise EnvironmentError("can only clone master version "
+ "'%s'" % join(pypydir, 'tool',_myname))
+
+
+ def sync_walker(arg, dirname, fnames):
+ if _myname in fnames:
+ fn = join(dirname, _myname)
+ f = open(fn, 'rwb+')
+ try:
+ if f.read() == arg:
+ print "checkok", fn
+ else:
+ print "syncing", fn
+ f = open(fn, 'w')
+ f.write(arg)
+ finally:
+ f.close()
+ s = open(join(pypydir, 'tool', _myname), 'rb').read()
+ walk(pypydir, sync_walker, s)
+
+_myname = 'autopath.py'
+
+# set guaranteed attributes
+
+pypydir, this_dir = __dirinfo('pypy')
+
+if __name__ == '__main__':
+ __clone()
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/conftest.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/conftest.py Sat Mar 29 00:36:56 2008
@@ -0,0 +1,16 @@
+import py
+from pypy.jit.codegen import detect_cpu
+
+
+class Directory(py.test.collect.Directory):
+
+ def run(self):
+ try:
+ processor = detect_cpu.autodetect()
+ except detect_cpu.ProcessorAutodetectError, e:
+ py.test.skip(str(e))
+ else:
+ if processor != 'i386':
+ py.test.skip('detected a %r CPU' % (processor,))
+
+ return super(Directory, self).run()
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/rgenop.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/rgenop.py Sat Mar 29 00:36:56 2008
@@ -0,0 +1,1120 @@
+import sys, py, os
+from pypy.rlib.objectmodel import specialize
+from pypy.rpython.lltypesystem import lltype, llmemory
+from pypy.jit.codegen.i386.ri386 import *
+from pypy.jit.codegen.i386.codebuf import CodeBlockOverflow
+from pypy.jit.codegen.model import AbstractRGenOp, GenLabel, GenBuilder
+from pypy.jit.codegen.model import GenVar, GenConst, CodeGenSwitch
+from pypy.rlib import objectmodel
+from pypy.rpython.annlowlevel import llhelper
+
+WORD = 4
+DEBUG_CALL_ALIGN = True
+if sys.platform == 'darwin':
+ CALL_ALIGN = 4
+else:
+ CALL_ALIGN = 1
+
+class Var(GenVar):
+
+ def __init__(self, stackpos):
+ # 'stackpos' is an index relative to the pushed arguments
+ # (where N is the number of arguments of the function
+ # and B is a small integer for stack alignment purposes):
+ #
+ # B + 0 = last arg
+ # = ...
+ # B +N-1 = 1st arg
+ # B + N = return address
+ # B +N+1 = local var
+ # B +N+2 = ...
+ # ... <--- esp+4
+ # local var <--- esp
+ #
+ self.stackpos = stackpos
+
+ def operand(self, builder):
+ return builder.stack_access(self.stackpos)
+
+ def nonimmoperand(self, builder, tmpregister):
+ return self.operand(builder)
+
+ def __repr__(self):
+ return 'var@%d' % (self.stackpos,)
+
+ repr = __repr__
+
+
+##class Const(GenConst):
+
+## def revealconst(self, TYPE):
+## if isinstance(self, IntConst):
+## self.revealconst_int(TYPE)
+## elif isinstance(self, PtrConst):
+## self.revealconst_ptr(TYPE)
+
+## if isinstance(TYPE, lltype.Ptr):
+## if isinstance(self, PtrConst):
+## return self.revealconst_ptr(TYPE)
+## el
+## return self.revealconst_ptr(TYPE)
+## elif TYPE is lltype.Float:
+## assert isinstance(self, DoubleConst)
+## return self.revealconst_double()
+## else:
+## assert isinstance(TYPE, lltype.Primitive)
+## assert TYPE is not lltype.Void, "cannot make red boxes of voids"
+## assert isinstance(self, IntConst)
+## return self.revealconst_primitive(TYPE)
+## return self.value
+## revealconst._annspecialcase_ = 'specialize:arg(1)'
+
+
+class IntConst(GenConst):
+
+ def __init__(self, value):
+ self.value = value
+
+ def operand(self, builder):
+ return imm(self.value)
+
+ def nonimmoperand(self, builder, tmpregister):
+ builder.mc.MOV(tmpregister, self.operand(builder))
+ return tmpregister
+
+ @specialize.arg(1)
+ def revealconst(self, T):
+ if isinstance(T, lltype.Ptr):
+ return lltype.cast_int_to_ptr(T, self.value)
+ elif T is llmemory.Address:
+ return llmemory.cast_int_to_adr(self.value)
+ else:
+ return lltype.cast_primitive(T, self.value)
+
+ def __repr__(self):
+ "NOT_RPYTHON"
+ try:
+ return "const=%s" % (imm(self.value).assembler(),)
+ except TypeError: # from Symbolics
+ return "const=%r" % (self.value,)
+
+ def repr(self):
+ return "const=$%s" % (self.value,)
+
+
+##class FnPtrConst(IntConst):
+## def __init__(self, value, mc):
+## self.value = value
+## self.mc = mc # to keep it alive
+
+
+class AddrConst(GenConst):
+
+ def __init__(self, addr):
+ self.addr = addr
+
+ def operand(self, builder):
+ return imm(llmemory.cast_adr_to_int(self.addr))
+
+ def nonimmoperand(self, builder, tmpregister):
+ builder.mc.MOV(tmpregister, self.operand(builder))
+ return tmpregister
+
+ @specialize.arg(1)
+ def revealconst(self, T):
+ if T is llmemory.Address:
+ return self.addr
+ elif isinstance(T, lltype.Ptr):
+ return llmemory.cast_adr_to_ptr(self.addr, T)
+ elif T is lltype.Signed:
+ return llmemory.cast_adr_to_int(self.addr)
+ else:
+ assert 0, "XXX not implemented"
+
+ def __repr__(self):
+ "NOT_RPYTHON"
+ return "const=%r" % (self.addr,)
+
+ def repr(self):
+ return "const=<0x%x>" % (llmemory.cast_adr_to_int(self.addr),)
+
+
+class Label(GenLabel):
+
+ def __init__(self, startaddr, arg_positions, stackdepth):
+ self.startaddr = startaddr
+ self.arg_positions = arg_positions
+ self.stackdepth = stackdepth
+
+
+class FlexSwitch(CodeGenSwitch):
+
+ def __init__(self, rgenop):
+ self.rgenop = rgenop
+ self.default_case_builder = None
+ self.default_case_key = 0
+ self._je_key = 0
+
+ def initialize(self, builder, gv_exitswitch):
+ mc = builder.mc
+ mc.MOV(eax, gv_exitswitch.operand(builder))
+ self.saved_state = builder._save_state()
+ self._reserve(mc)
+
+ def _reserve(self, mc):
+ RESERVED = 11*4+5 # XXX quite a lot for now :-/
+ pos = mc.tell()
+ mc.UD2()
+ mc.write('\x00' * (RESERVED-1))
+ self.nextfreepos = pos
+ self.endfreepos = pos + RESERVED
+
+ def _reserve_more(self):
+ start = self.nextfreepos
+ end = self.endfreepos
+ newmc = self.rgenop.open_mc()
+ self._reserve(newmc)
+ self.rgenop.close_mc(newmc)
+ fullmc = self.rgenop.InMemoryCodeBuilder(start, end)
+ fullmc.JMP(rel32(self.nextfreepos))
+ fullmc.done()
+
+ def add_case(self, gv_case):
+ rgenop = self.rgenop
+ targetbuilder = Builder._new_from_state(rgenop, self.saved_state)
+ try:
+ self._add_case(gv_case, targetbuilder)
+ except CodeBlockOverflow:
+ self._reserve_more()
+ self._add_case(gv_case, targetbuilder)
+ targetbuilder._open()
+ return targetbuilder
+
+ def _add_case(self, gv_case, targetbuilder):
+ # XXX this code needs to be simplified, now that we always
+ # have a default case
+ start = self.nextfreepos
+ end = self.endfreepos
+ mc = self.rgenop.InMemoryCodeBuilder(start, end)
+ mc.CMP(eax, gv_case.operand(None))
+ self._je_key = targetbuilder.come_from(mc, 'JE', self._je_key)
+ pos = mc.tell()
+ if self.default_case_builder:
+ self.default_case_key = self.default_case_builder.come_from(
+ mc, 'JMP', self.default_case_key)
+ else:
+ illegal_start = mc.tell()
+ mc.JMP(rel32(0))
+ ud2_addr = mc.tell()
+ mc.UD2()
+ illegal_mc = self.rgenop.InMemoryCodeBuilder(illegal_start, end)
+ illegal_mc.JMP(rel32(ud2_addr))
+ mc.done()
+ self._je_key = 0
+ self.nextfreepos = pos
+
+ def _add_default(self):
+ rgenop = self.rgenop
+ targetbuilder = Builder._new_from_state(rgenop, self.saved_state)
+ self.default_case_builder = targetbuilder
+ start = self.nextfreepos
+ end = self.endfreepos
+ mc = self.rgenop.InMemoryCodeBuilder(start, end)
+ self.default_case_key = targetbuilder.come_from(mc, 'JMP')
+ targetbuilder._open()
+ return targetbuilder
+
+class Builder(GenBuilder):
+
+ def __init__(self, rgenop, stackdepth):
+ self.rgenop = rgenop
+ self.stackdepth = stackdepth
+ self.mc = None
+ self._pending_come_from = {}
+ self.start = 0
+ self.closed = False
+ self.tail = (0, 0)
+
+ def _open(self):
+ if self.mc is None and not self.closed:
+ self.mc = self.rgenop.open_mc()
+ if not self.start:
+ # This is the first open. remember the start address
+ # and patch all come froms.
+ self.start = self.mc.tell()
+ come_froms = self._pending_come_from
+ self._pending_come_from = None
+ for start, (end, insn) in come_froms.iteritems():
+ if end == self.start:
+ # there was a pending JMP just before self.start,
+ # so we can as well overwrite the JMP and start writing
+ # code directly there
+ self.mc.seekback(end - start)
+ self.start = start
+ break
+ for start, (end, insn) in come_froms.iteritems():
+ if start != self.start:
+ mc = self.rgenop.InMemoryCodeBuilder(start, end)
+ self._emit_come_from(mc, insn, self.start)
+ mc.done()
+ else:
+ # We have been paused and are being opened again.
+ # Is the new codeblock immediately after the previous one?
+ prevstart, prevend = self.tail
+ curpos = self.mc.tell()
+ if prevend == curpos:
+ # Yes. We can overwrite the JMP and just continue writing
+ # code directly there
+ self.mc.seekback(prevend - prevstart)
+ else:
+ # No. Patch the jump at the end of the previous codeblock.
+ mc = self.rgenop.InMemoryCodeBuilder(prevstart, prevend)
+ mc.JMP(rel32(curpos))
+ mc.done()
+
+ def pause_writing(self, alive_vars_gv):
+ if self.mc is not None:
+ start = self.mc.tell()
+ self.mc.JMP(rel32(0))
+ end = self.mc.tell()
+ self.tail = (start, end)
+ self.mc.done()
+ self.rgenop.close_mc(self.mc)
+ self.mc = None
+ return self
+
+ def start_writing(self):
+ self._open()
+
+ def _emit_come_from(self, mc, insn, addr):
+ if insn == 'JMP':
+ mc.JMP(rel32(addr))
+ elif insn == 'JE':
+ mc.JE(rel32(addr))
+ elif insn == 'JNE':
+ mc.JNE(rel32(addr))
+ else:
+ raise ValueError('Unsupported jump')
+
+ def come_from(self, mc, insn, key=0):
+ start = mc.tell()
+ if self._pending_come_from is None:
+ self._emit_come_from(mc, insn, self.start)
+ else:
+ self._emit_come_from(mc, insn, 0)
+ end = mc.tell()
+ if key != 0:
+ del self._pending_come_from[key]
+ self._pending_come_from[start] = (end, insn)
+ return start
+
+ def end(self):
+ pass
+
+ def _write_prologue(self, sigtoken):
+ self._open()
+ numargs = sigtoken # for now
+ #self.mc.BREAKPOINT()
+ # self.stackdepth-1 is the return address; the arguments
+ # come just before
+ return [Var(self.stackdepth-2-n) for n in range(numargs)]
+
+ def _close(self):
+ self.closed = True
+ self.mc.done()
+ self.rgenop.close_mc(self.mc)
+ self.mc = None
+
+ def _fork(self):
+ return self.rgenop.newbuilder(self.stackdepth)
+
+ def _save_state(self):
+ return self.stackdepth
+
+ @staticmethod
+ def _new_from_state(rgenop, stackdepth):
+ return rgenop.newbuilder(stackdepth)
+
+ @specialize.arg(1)
+ def genop1(self, opname, gv_arg):
+ genmethod = getattr(self, 'op_' + opname)
+ return genmethod(gv_arg)
+
+ @specialize.arg(1)
+ def genop2(self, opname, gv_arg1, gv_arg2):
+ genmethod = getattr(self, 'op_' + opname)
+ return genmethod(gv_arg1, gv_arg2)
+
+ def genop_getfield(self, (offset, fieldsize), gv_ptr):
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ if fieldsize == WORD:
+ op = mem(edx, offset)
+ else:
+ if fieldsize == 1:
+ op = mem8(edx, offset)
+ else:
+ assert fieldsize == 2
+ op = mem(edx, offset)
+ self.mc.MOVZX(eax, op)
+ op = eax
+ return self.returnvar(op)
+
+ def genop_setfield(self, (offset, fieldsize), gv_ptr, gv_value):
+ self.mc.MOV(eax, gv_value.operand(self))
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ if fieldsize == 1:
+ self.mc.MOV(mem8(edx, offset), al)
+ else:
+ if fieldsize == 2:
+ self.mc.o16() # followed by the MOV below
+ else:
+ assert fieldsize == WORD
+ self.mc.MOV(mem(edx, offset), eax)
+
+ def genop_getsubstruct(self, (offset, fieldsize), gv_ptr):
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ self.mc.LEA(eax, mem(edx, offset))
+ return self.returnvar(eax)
+
+ def itemaddr(self, base, arraytoken, gv_index):
+ # uses ecx
+ lengthoffset, startoffset, itemoffset = arraytoken
+ if itemoffset == 1:
+ memSIBx = memSIB8
+ else:
+ memSIBx = memSIB
+ if isinstance(gv_index, IntConst):
+ startoffset += itemoffset * gv_index.value
+ op = memSIBx(base, None, 0, startoffset)
+ elif itemoffset in SIZE2SHIFT:
+ self.mc.MOV(ecx, gv_index.operand(self))
+ op = memSIBx(base, ecx, SIZE2SHIFT[itemoffset], startoffset)
+ else:
+ self.mc.IMUL(ecx, gv_index.operand(self), imm(itemoffset))
+ op = memSIBx(base, ecx, 0, startoffset)
+ return op
+
+ def genop_getarrayitem(self, arraytoken, gv_ptr, gv_index):
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ op = self.itemaddr(edx, arraytoken, gv_index)
+ _, _, itemsize = arraytoken
+ if itemsize != WORD:
+ assert itemsize == 1 or itemsize == 2
+ self.mc.MOVZX(eax, op)
+ op = eax
+ return self.returnvar(op)
+
+ def genop_getarraysubstruct(self, arraytoken, gv_ptr, gv_index):
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ op = self.itemaddr(edx, arraytoken, gv_index)
+ self.mc.LEA(eax, op)
+ return self.returnvar(eax)
+
+ def genop_getarraysize(self, arraytoken, gv_ptr):
+ lengthoffset, startoffset, itemoffset = arraytoken
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ return self.returnvar(mem(edx, lengthoffset))
+
+ def genop_setarrayitem(self, arraytoken, gv_ptr, gv_index, gv_value):
+ self.mc.MOV(eax, gv_value.operand(self))
+ self.mc.MOV(edx, gv_ptr.operand(self))
+ destop = self.itemaddr(edx, arraytoken, gv_index)
+ _, _, itemsize = arraytoken
+ if itemsize != WORD:
+ if itemsize == 1:
+ self.mc.MOV(destop, al)
+ return
+ elif itemsize == 2:
+ self.mc.o16() # followed by the MOV below
+ else:
+ raise AssertionError
+ self.mc.MOV(destop, eax)
+
+ def genop_malloc_fixedsize(self, size):
+ # XXX boehm only, no atomic/non atomic distinction for now
+ self.push(imm(size))
+ self.mc.CALL(rel32(gc_malloc_fnaddr()))
+ return self.returnvar(eax)
+
+ def genop_malloc_varsize(self, varsizealloctoken, gv_size):
+ # XXX boehm only, no atomic/non atomic distinction for now
+ # XXX no overflow checking for now
+ op_size = self.itemaddr(None, varsizealloctoken, gv_size)
+ self.mc.LEA(edx, op_size)
+ self.push(edx)
+ self.mc.CALL(rel32(gc_malloc_fnaddr()))
+ lengthoffset, _, _ = varsizealloctoken
+ self.mc.MOV(ecx, gv_size.operand(self))
+ self.mc.MOV(mem(eax, lengthoffset), ecx)
+ return self.returnvar(eax)
+
+ def genop_call(self, sigtoken, gv_fnptr, args_gv):
+ numargs = sigtoken # for now
+ MASK = CALL_ALIGN-1
+ if MASK:
+ final_depth = self.stackdepth + numargs
+ delta = ((final_depth+MASK)&~MASK)-final_depth
+ if delta:
+ self.mc.SUB(esp, imm(delta*WORD))
+ self.stackdepth += delta
+ for i in range(numargs-1, -1, -1):
+ gv_arg = args_gv[i]
+ self.push(gv_arg.operand(self))
+ if DEBUG_CALL_ALIGN:
+ self.mc.MOV(eax, esp)
+ self.mc.AND(eax, imm8((WORD*CALL_ALIGN)-1))
+ self.mc.ADD(eax, imm32(sys.maxint)) # overflows unless eax == 0
+ self.mc.INTO()
+ if gv_fnptr.is_const:
+ target = gv_fnptr.revealconst(lltype.Signed)
+ self.mc.CALL(rel32(target))
+ else:
+ self.mc.CALL(gv_fnptr.operand(self))
+ # XXX only for int return_kind, check calling conventions
+ return self.returnvar(eax)
+
+ def genop_same_as(self, kind, gv_x):
+ if gv_x.is_const: # must always return a var
+ return self.returnvar(gv_x.operand(self))
+ else:
+ return gv_x
+
+ def genop_debug_pdb(self): # may take an args_gv later
+ self.mc.BREAKPOINT()
+
+ def enter_next_block(self, kinds, args_gv):
+ self._open()
+ arg_positions = []
+ seen = {}
+ for i in range(len(args_gv)):
+ gv = args_gv[i]
+ # turn constants into variables; also make copies of vars that
+ # are duplicate in args_gv
+ if not isinstance(gv, Var) or gv.stackpos in seen:
+ gv = args_gv[i] = self.returnvar(gv.operand(self))
+ # remember the var's position in the stack
+ arg_positions.append(gv.stackpos)
+ seen[gv.stackpos] = None
+ return Label(self.mc.tell(), arg_positions, self.stackdepth)
+
+ def jump_if_false(self, gv_condition, args_gv):
+ targetbuilder = self._fork()
+ self.mc.CMP(gv_condition.operand(self), imm8(0))
+ targetbuilder.come_from(self.mc, 'JE')
+ return targetbuilder
+
+ def jump_if_true(self, gv_condition, args_gv):
+ targetbuilder = self._fork()
+ self.mc.CMP(gv_condition.operand(self), imm8(0))
+ targetbuilder.come_from(self.mc, 'JNE')
+ return targetbuilder
+
+ def finish_and_return(self, sigtoken, gv_returnvar):
+ self._open()
+ initialstackdepth = self.rgenop._initial_stack_depth(sigtoken)
+ self.mc.MOV(eax, gv_returnvar.operand(self))
+ self.mc.ADD(esp, imm(WORD * (self.stackdepth - initialstackdepth)))
+ self.mc.RET()
+ self._close()
+
+ def finish_and_goto(self, outputargs_gv, target):
+ self._open()
+ remap_stack_layout(self, outputargs_gv, target)
+ self.mc.JMP(rel32(target.startaddr))
+ self._close()
+
+ def flexswitch(self, gv_exitswitch, args_gv):
+ result = FlexSwitch(self.rgenop)
+ result.initialize(self, gv_exitswitch)
+ self._close()
+ return result, result._add_default()
+
+ def show_incremental_progress(self):
+ pass
+
+ def log(self, msg):
+ self.mc.log(msg)
+
+ # ____________________________________________________________
+
+ def stack_access(self, stackpos):
+ return mem(esp, WORD * (self.stackdepth-1 - stackpos))
+
+ def push(self, op):
+ self.mc.PUSH(op)
+ self.stackdepth += 1
+
+ def returnvar(self, op):
+ res = Var(self.stackdepth)
+ self.push(op)
+ return res
+
+ @staticmethod
+ def identity(gv_x):
+ return gv_x
+
+ op_int_is_true = identity
+
+ def op_int_add(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.ADD(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_sub(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.SUB(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_mul(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.IMUL(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_floordiv(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CDQ()
+ self.mc.IDIV(gv_y.nonimmoperand(self, ecx))
+ return self.returnvar(eax)
+
+ def op_int_mod(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CDQ()
+ self.mc.IDIV(gv_y.nonimmoperand(self, ecx))
+ return self.returnvar(edx)
+
+ def op_int_and(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.AND(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_or(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.OR(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_xor(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.XOR(eax, gv_y.operand(self))
+ return self.returnvar(eax)
+
+ def op_int_lt(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETL(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_le(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETLE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_eq(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_ne(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETNE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_gt(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETG(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_ge(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETGE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_int_neg(self, gv_x):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.NEG(eax)
+ return self.returnvar(eax)
+
+ def op_int_abs(self, gv_x):
+ self.mc.MOV(eax, gv_x.operand(self))
+ # ABS-computing code from Psyco, found by exhaustive search
+ # on *all* short sequences of operations :-)
+ self.mc.ADD(eax, eax)
+ self.mc.SBB(eax, gv_x.operand(self))
+ self.mc.SBB(edx, edx)
+ self.mc.XOR(eax, edx)
+ return self.returnvar(eax)
+
+ def op_int_invert(self, gv_x):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.NOT(eax)
+ return self.returnvar(eax)
+
+ def op_int_lshift(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.MOV(ecx, gv_y.operand(self)) # XXX check if ecx >= 32
+ self.mc.SHL(eax, cl)
+ return self.returnvar(eax)
+
+ def op_int_rshift(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.MOV(ecx, gv_y.operand(self)) # XXX check if ecx >= 32
+ self.mc.SAR(eax, cl)
+ return self.returnvar(eax)
+
+ op_uint_is_true = op_int_is_true
+ op_uint_invert = op_int_invert
+ op_uint_add = op_int_add
+ op_uint_sub = op_int_sub
+
+ def op_uint_mul(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.MUL(gv_y.nonimmoperand(self, edx))
+ return self.returnvar(eax)
+
+ def op_uint_floordiv(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.XOR(edx, edx)
+ self.mc.DIV(gv_y.nonimmoperand(self, ecx))
+ return self.returnvar(eax)
+
+ def op_uint_mod(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.XOR(edx, edx)
+ self.mc.DIV(gv_y.nonimmoperand(self, ecx))
+ return self.returnvar(edx)
+
+ def op_uint_lt(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETB(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_uint_le(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETBE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ op_uint_eq = op_int_eq
+ op_uint_ne = op_int_ne
+
+ def op_uint_gt(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETA(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_uint_ge(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.CMP(eax, gv_y.operand(self))
+ self.mc.SETAE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ op_uint_and = op_int_and
+ op_uint_or = op_int_or
+ op_uint_xor = op_int_xor
+ op_uint_lshift = op_int_lshift
+
+ def op_uint_rshift(self, gv_x, gv_y):
+ self.mc.MOV(eax, gv_x.operand(self))
+ self.mc.MOV(ecx, gv_y.operand(self)) # XXX check if ecx >= 32
+ self.mc.SHR(eax, cl)
+ return self.returnvar(eax)
+
+ def op_bool_not(self, gv_x):
+ self.mc.CMP(gv_x.operand(self), imm8(0))
+ self.mc.SETE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ def op_cast_bool_to_int(self, gv_x):
+ self.mc.CMP(gv_x.operand(self), imm8(0))
+ self.mc.SETNE(al)
+ self.mc.MOVZX(eax, al)
+ return self.returnvar(eax)
+
+ op_cast_bool_to_uint = op_cast_bool_to_int
+
+ op_cast_char_to_int = identity
+ op_cast_unichar_to_int = identity
+ op_cast_int_to_char = identity
+ op_cast_int_to_unichar = identity
+ op_cast_int_to_uint = identity
+ op_cast_uint_to_int = identity
+ op_cast_ptr_to_int = identity
+ op_cast_int_to_ptr = identity
+
+ op_char_lt = op_int_lt
+ op_char_le = op_int_le
+ op_char_eq = op_int_eq
+ op_char_ne = op_int_ne
+ op_char_gt = op_int_gt
+ op_char_ge = op_int_ge
+
+ op_unichar_eq = op_int_eq
+ op_unichar_ne = op_int_ne
+
+ op_ptr_nonzero = op_int_is_true
+ op_ptr_iszero = op_bool_not # for now
+ op_ptr_eq = op_int_eq
+ op_ptr_ne = op_int_ne
+
+
+SIZE2SHIFT = {1: 0,
+ 2: 1,
+ 4: 2,
+ 8: 3}
+
+GC_MALLOC = lltype.Ptr(lltype.FuncType([lltype.Signed], llmemory.Address))
+
+def gc_malloc(size):
+ from pypy.rpython.lltypesystem.lloperation import llop
+ return llop.call_boehm_gc_alloc(llmemory.Address, size)
+
+def gc_malloc_fnaddr():
+ """Returns the address of the Boehm 'malloc' function."""
+ if objectmodel.we_are_translated():
+ gc_malloc_ptr = llhelper(GC_MALLOC, gc_malloc)
+ return lltype.cast_ptr_to_int(gc_malloc_ptr)
+ else:
+ # <pedronis> don't do this at home
+ import threading
+ if not isinstance(threading.currentThread(), threading._MainThread):
+ import py
+ py.test.skip("must run in the main thread")
+ try:
+ from ctypes import cast, c_void_p
+ from pypy.rpython.rctypes.tool import util
+ path = util.find_library('gc')
+ if path is None:
+ raise ImportError("Boehm (libgc) not found")
+ boehmlib = util.load_library(path)
+ except ImportError, e:
+ import py
+ py.test.skip(str(e))
+ else:
+ GC_malloc = boehmlib.GC_malloc
+ return cast(GC_malloc, c_void_p).value
+
+# ____________________________________________________________
+
+def remap_stack_layout(builder, outputargs_gv, target):
+## import os
+## s = ', '.join([gv.repr() for gv in outputargs_gv])
+## os.write(2, "writing at %d (stack=%d, [%s])\n --> %d (stack=%d, %s)\n"
+## % (builder.mc.tell(),
+## builder.stackdepth,
+## s,
+## target.startaddr,
+## target.stackdepth,
+## target.arg_positions))
+
+ N = target.stackdepth
+ if builder.stackdepth < N:
+ builder.mc.SUB(esp, imm(WORD * (N - builder.stackdepth)))
+ builder.stackdepth = N
+
+ M = len(outputargs_gv)
+ arg_positions = target.arg_positions
+ assert M == len(arg_positions)
+ targetlayout = [None] * N
+ srccount = [-N] * N
+ for i in range(M):
+ pos = arg_positions[i]
+ gv = outputargs_gv[i]
+ assert targetlayout[pos] is None
+ targetlayout[pos] = gv
+ srccount[pos] = 0
+ pending_dests = M
+ for i in range(M):
+ targetpos = arg_positions[i]
+ gv = outputargs_gv[i]
+ if isinstance(gv, Var):
+ p = gv.stackpos
+ if 0 <= p < N:
+ if p == targetpos:
+ srccount[p] = -N # ignore 'v=v'
+ pending_dests -= 1
+ else:
+ srccount[p] += 1
+
+ while pending_dests:
+ progress = False
+ for i in range(N):
+ if srccount[i] == 0:
+ srccount[i] = -1
+ pending_dests -= 1
+ gv_src = targetlayout[i]
+ if isinstance(gv_src, Var):
+ p = gv_src.stackpos
+ if 0 <= p < N:
+ srccount[p] -= 1
+ builder.mc.MOV(eax, gv_src.operand(builder))
+ builder.mc.MOV(builder.stack_access(i), eax)
+ progress = True
+ if not progress:
+ # we are left with only pure disjoint cycles; break them
+ for i in range(N):
+ if srccount[i] >= 0:
+ dst = i
+ builder.mc.MOV(edx, builder.stack_access(dst))
+ while True:
+ assert srccount[dst] == 1
+ srccount[dst] = -1
+ pending_dests -= 1
+ gv_src = targetlayout[dst]
+ assert isinstance(gv_src, Var)
+ src = gv_src.stackpos
+ assert 0 <= src < N
+ if src == i:
+ break
+ builder.mc.MOV(eax, builder.stack_access(src))
+ builder.mc.MOV(builder.stack_access(dst), eax)
+ dst = src
+ builder.mc.MOV(builder.stack_access(dst), edx)
+ assert pending_dests == 0
+
+ if builder.stackdepth > N:
+ builder.mc.ADD(esp, imm(WORD * (builder.stackdepth - N)))
+ builder.stackdepth = N
+
+
+#
+
+dummy_var = Var(0)
+
+class ReplayFlexSwitch(CodeGenSwitch):
+
+ def __init__(self, replay_builder):
+ self.replay_builder = replay_builder
+
+ def add_case(self, gv_case):
+ return self.replay_builder
+
+class ReplayBuilder(GenBuilder):
+
+ def __init__(self, rgenop):
+ self.rgenop = rgenop
+
+ def end(self):
+ pass
+
+ @specialize.arg(1)
+ def genop1(self, opname, gv_arg):
+ return dummy_var
+
+ @specialize.arg(1)
+ def genop2(self, opname, gv_arg1, gv_arg2):
+ return dummy_var
+
+ def genop_getfield(self, fieldtoken, gv_ptr):
+ return dummy_var
+
+ def genop_setfield(self, fieldtoken, gv_ptr, gv_value):
+ return dummy_var
+
+ def genop_getsubstruct(self, fieldtoken, gv_ptr):
+ return dummy_var
+
+ def genop_getarrayitem(self, arraytoken, gv_ptr, gv_index):
+ return dummy_var
+
+ def genop_getarraysubstruct(self, arraytoken, gv_ptr, gv_index):
+ return dummy_var
+
+ def genop_getarraysize(self, arraytoken, gv_ptr):
+ return dummy_var
+
+ def genop_setarrayitem(self, arraytoken, gv_ptr, gv_index, gv_value):
+ return dummy_var
+
+ def genop_malloc_fixedsize(self, size):
+ return dummy_var
+
+ def genop_malloc_varsize(self, varsizealloctoken, gv_size):
+ return dummy_var
+
+ def genop_call(self, sigtoken, gv_fnptr, args_gv):
+ return dummy_var
+
+ def genop_same_as(self, kind, gv_x):
+ return dummy_var
+
+ def genop_debug_pdb(self): # may take an args_gv later
+ pass
+
+ def enter_next_block(self, kinds, args_gv):
+ return None
+
+ def jump_if_false(self, gv_condition, args_gv):
+ return self
+
+ def jump_if_true(self, gv_condition, args_gv):
+ return self
+
+ def finish_and_return(self, sigtoken, gv_returnvar):
+ pass
+
+ def finish_and_goto(self, outputargs_gv, target):
+ pass
+
+ def flexswitch(self, gv_exitswitch, args_gv):
+ flexswitch = ReplayFlexSwitch(self)
+ return flexswitch, self
+
+ def show_incremental_progress(self):
+ pass
+
+class RI386GenOp(AbstractRGenOp):
+ from pypy.jit.codegen.i386.codebuf import MachineCodeBlock
+ from pypy.jit.codegen.i386.codebuf import InMemoryCodeBuilder
+
+ MC_SIZE = 65536
+
+ def __init__(self):
+ self.mcs = [] # machine code blocks where no-one is currently writing
+ self.keepalive_gc_refs = []
+ self.total_code_blocks = 0
+
+ def open_mc(self):
+ if self.mcs:
+ # XXX think about inserting NOPS for alignment
+ return self.mcs.pop()
+ else:
+ # XXX supposed infinite for now
+ self.total_code_blocks += 1
+ return self.MachineCodeBlock(self.MC_SIZE)
+
+ 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 check_no_open_mc(self):
+ assert len(self.mcs) == self.total_code_blocks
+
+ def newbuilder(self, stackdepth):
+ return Builder(self, stackdepth)
+
+ def newgraph(self, sigtoken, name):
+ builder = self.newbuilder(self._initial_stack_depth(sigtoken))
+ builder._open() # Force builder to have an mc
+ entrypoint = builder.mc.tell()
+ inputargs_gv = builder._write_prologue(sigtoken)
+ return builder, IntConst(entrypoint), inputargs_gv
+
+ def _initial_stack_depth(self, sigtoken):
+ # If a stack depth is a multiple of CALL_ALIGN then the
+ # arguments are correctly aligned for a call. We have to
+ # precompute initialstackdepth to guarantee that. For OS/X the
+ # convention is that the stack should be aligned just after all
+ # arguments are pushed, i.e. just before the return address is
+ # pushed by the CALL instruction. In other words, after
+ # 'numargs' arguments have been pushed the stack is aligned:
+ numargs = sigtoken # for now
+ MASK = CALL_ALIGN - 1
+ initialstackdepth = ((numargs+MASK)&~MASK) + 1
+ return initialstackdepth
+
+ def replay(self, label, kinds):
+ return ReplayBuilder(self), [dummy_var] * len(kinds)
+
+ @specialize.genconst(1)
+ def genconst(self, llvalue):
+ T = lltype.typeOf(llvalue)
+ if T is llmemory.Address:
+ return AddrConst(llvalue)
+ elif isinstance(T, lltype.Primitive):
+ return IntConst(lltype.cast_primitive(lltype.Signed, llvalue))
+ elif isinstance(T, lltype.Ptr):
+ lladdr = llmemory.cast_ptr_to_adr(llvalue)
+ if T.TO._gckind == 'gc':
+ self.keepalive_gc_refs.append(lltype.cast_opaque_ptr(llmemory.GCREF, llvalue))
+ return AddrConst(lladdr)
+ else:
+ assert 0, "XXX not implemented"
+
+ # attached later constPrebuiltGlobal = global_rgenop.genconst
+
+ @staticmethod
+ @specialize.memo()
+ def fieldToken(T, name):
+ FIELD = getattr(T, name)
+ if isinstance(FIELD, lltype.ContainerType):
+ fieldsize = 0 # not useful for getsubstruct
+ else:
+ fieldsize = llmemory.sizeof(FIELD)
+ return (llmemory.offsetof(T, name), fieldsize)
+
+ @staticmethod
+ @specialize.memo()
+ def allocToken(T):
+ return llmemory.sizeof(T)
+
+ @staticmethod
+ @specialize.memo()
+ def varsizeAllocToken(T):
+ if isinstance(T, lltype.Array):
+ return RI386GenOp.arrayToken(T)
+ else:
+ # var-sized structs
+ arrayfield = T._arrayfld
+ ARRAYFIELD = getattr(T, arrayfield)
+ arraytoken = RI386GenOp.arrayToken(ARRAYFIELD)
+ length_offset, items_offset, item_size = arraytoken
+ arrayfield_offset = llmemory.offsetof(T, arrayfield)
+ return (arrayfield_offset+length_offset,
+ arrayfield_offset+items_offset,
+ item_size)
+
+ @staticmethod
+ @specialize.memo()
+ def arrayToken(A):
+ return (llmemory.ArrayLengthOffset(A),
+ llmemory.ArrayItemsOffset(A),
+ llmemory.ItemOffset(A.OF))
+
+ @staticmethod
+ @specialize.memo()
+ def kindToken(T):
+ if T is lltype.Float:
+ py.test.skip("not implemented: floats in the i386 back-end")
+ return None # for now
+
+ @staticmethod
+ @specialize.memo()
+ def sigToken(FUNCTYPE):
+ numargs = 0
+ for ARG in FUNCTYPE.ARGS:
+ if ARG is not lltype.Void:
+ numargs += 1
+ return numargs # for now
+
+ @staticmethod
+ def erasedType(T):
+ if T is llmemory.Address:
+ return llmemory.Address
+ if isinstance(T, lltype.Primitive):
+ return lltype.Signed
+ elif isinstance(T, lltype.Ptr):
+ return llmemory.GCREF
+ else:
+ assert 0, "XXX not implemented"
+
+global_rgenop = RI386GenOp()
+RI386GenOp.constPrebuiltGlobal = global_rgenop.genconst
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/__init__.py
==============================================================================
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/test_rgenop.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/test/test_rgenop.py Sat Mar 29 00:36:56 2008
@@ -0,0 +1,8 @@
+from pypy.jit.codegen.iaxx.rgenop import RI386GenOp
+from pypy.jit.codegen.test.rgenop_tests import AbstractRGenOpTests
+
+class TestRI386Genop(AbstractRGenOpTests):
+ RGenOp = RI386GenOp
+
+ # for the individual tests see
+ # ====> ../../test/rgenop_tests.py
Added: pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/viewcode.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-hotpath/pypy/jit/codegen/iaxx/viewcode.py Sat Mar 29 00:36:56 2008
@@ -0,0 +1,339 @@
+#! /usr/bin/env python
+"""
+Viewer for the CODE_DUMP output of compiled programs generating code.
+
+Try:
+ ./viewcode.py dumpfile.txt
+or
+ /tmp/usession-xxx/testing_1/testing_1 -var 4 2>&1 | ./viewcode.py
+"""
+
+import autopath
+import operator, sys, os, re, py
+from bisect import bisect_left
+
+# don't use pypy.tool.udir here to avoid removing old usessions which
+# might still contain interesting executables
+udir = py.path.local.make_numbered_dir(prefix='viewcode-', keep=2)
+tmpfile = str(udir.join('dump.tmp'))
+
+# ____________________________________________________________
+# Some support code from Psyco. There is more over there,
+# I am porting it in a lazy fashion... See py-utils/xam.py
+
+if sys.platform == "win32":
+ XXX # lots more in Psyco
+
+def machine_code_dump(data, originaddr):
+ # the disassembler to use. 'objdump' writes GNU-style instructions.
+ # 'ndisasm' would use Intel syntax, but you need to fix the output parsing.
+ objdump = 'objdump -b binary -m i386 --adjust-vma=%(origin)d -D %(file)s'
+ #
+ f = open(tmpfile, 'wb')
+ f.write(data)
+ f.close()
+ g = os.popen(objdump % {'file': tmpfile, 'origin': originaddr}, 'r')
+ result = g.readlines()
+ g.close()
+ return result[6:] # drop some objdump cruft
+
+def load_symbols(filename):
+ # the program that lists symbols, and the output it gives
+ symbollister = 'nm %s'
+ re_symbolentry = re.compile(r'([0-9a-fA-F]+)\s\w\s(.*)')
+ #
+ print 'loading symbols from %s...' % (filename,)
+ symbols = {}
+ g = os.popen(symbollister % filename, "r")
+ for line in g:
+ match = re_symbolentry.match(line)
+ if match:
+ addr = long(match.group(1), 16)
+ name = match.group(2)
+ if name.startswith('pypy_g_'):
+ name = '\xb7' + name[7:]
+ symbols[addr] = name
+ g.close()
+ print '%d symbols found' % (len(symbols),)
+ return symbols
+
+re_addr = re.compile(r'[\s,$]0x([0-9a-fA-F][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]+)')
+re_lineaddr = re.compile(r'\s*0?x?([0-9a-fA-F]+)')
+
+def lineaddresses(line):
+ result = []
+ i = 0
+ while 1:
+ match = re_addr.search(line, i)
+ if not match:
+ break
+ i = match.end()
+ addr = long(match.group(1), 16)
+ result.append(addr)
+ return result
+
+# ____________________________________________________________
+
+class CodeRange(object):
+ fallthrough = False
+
+ def __init__(self, world, addr, data):
+ self.world = world
+ self.addr = addr
+ self.data = data
+
+ def __repr__(self):
+ return '<CodeRange %s length %d>' % (hex(self.addr), len(self.data))
+
+ def touches(self, other):
+ return (self .addr < other.addr + len(other.data) and
+ other.addr < self .addr + len(self.data))
+
+ def update_from_old(self, other):
+ if other.addr < self.addr:
+ delta = self.addr - other.addr
+ assert delta <= len(other.data)
+ self.addr -= delta
+ self.data = other.data[:delta] + self.data
+ self_end = self .addr + len(self .data)
+ other_end = other.addr + len(other.data)
+ if other_end > self_end:
+ extra = other_end - self_end
+ assert extra <= len(other.data)
+ self.data += other.data[-extra:]
+
+ def cmpop(op):
+ def _cmp(self, other):
+ if not isinstance(other, CodeRange):
+ return NotImplemented
+ return op((self.addr, self.data), (other.addr, other.data))
+ return _cmp
+ __lt__ = cmpop(operator.lt)
+ __le__ = cmpop(operator.le)
+ __eq__ = cmpop(operator.eq)
+ __ne__ = cmpop(operator.ne)
+ __gt__ = cmpop(operator.gt)
+ __ge__ = cmpop(operator.ge)
+ del cmpop
+
+ def disassemble(self):
+ if not hasattr(self, 'text'):
+ lines = machine_code_dump(self.data, self.addr)
+ # instead of adding symbol names in the dumps we could
+ # also make the 0xNNNNNNNN addresses be red and show the
+ # symbol name when the mouse is over them
+ logentries = self.world.logentries
+ symbols = self.world.symbols
+ for i, line in enumerate(lines):
+ match = re_lineaddr.match(line)
+ if match:
+ addr = long(match.group(1), 16)
+ logentry = logentries.get(addr)
+ if logentry:
+ lines[i] = '\n%s\n%s' % (logentry, lines[i])
+ for addr in lineaddresses(line):
+ sym = symbols.get(addr)
+ if sym:
+ lines[i] = '%s\t%s\n' % (lines[i].rstrip(), sym)
+ self.text = ''.join(lines)
+ return self.text
+
+ def findjumps(self):
+ text = self.disassemble()
+ lines = text.splitlines()
+ for i, line in enumerate(lines):
+ if '\tj' not in line: # poor heuristic to recognize lines that
+ continue # could be jump instructions
+ addrs = list(lineaddresses(line))
+ if not addrs:
+ continue
+ addr = addrs[-1]
+ final = '\tjmp' in line
+ yield i, addr, final
+ if self.fallthrough:
+ yield len(lines), self.addr + len(self.data), True
+
+
+class World(object):
+
+ def __init__(self):
+ self.ranges = []
+ self.labeltargets = {}
+ self.jumps = {}
+ self.symbols = {}
+ self.logentries = {}
+
+ def parse(self, f):
+ for line in f:
+ if line.startswith('CODE_DUMP '):
+ pieces = line.split()
+ assert pieces[1].startswith('@')
+ assert pieces[2].startswith('+')
+ baseaddr = long(pieces[1][1:], 16) & 0xFFFFFFFFL
+ offset = int(pieces[2][1:])
+ addr = baseaddr + offset
+ data = pieces[3].replace(':', '').decode('hex')
+ coderange = CodeRange(self, addr, data)
+ i = bisect_left(self.ranges, coderange)
+ j = i
+ while i>0 and coderange.touches(self.ranges[i-1]):
+ coderange.update_from_old(self.ranges[i-1])
+ i -= 1
+ while j<len(self.ranges) and coderange.touches(self.ranges[j]):
+ coderange.update_from_old(self.ranges[j])
+ j += 1
+ self.ranges[i:j] = [coderange]
+ elif line.startswith('LOG '):
+ pieces = line.split(None, 3)
+ assert pieces[1].startswith('@')
+ assert pieces[2].startswith('+')
+ baseaddr = long(pieces[1][1:], 16) & 0xFFFFFFFFL
+ offset = int(pieces[2][1:])
+ addr = baseaddr + offset
+ self.logentries[addr] = pieces[3]
+ elif line.startswith('SYS_EXECUTABLE '):
+ filename = line[len('SYS_EXECUTABLE '):].strip()
+ self.symbols.update(load_symbols(filename))
+ # find cross-references between blocks
+ fnext = 0.1
+ for i, r in enumerate(self.ranges):
+ for lineno, targetaddr, _ in r.findjumps():
+ self.labeltargets[targetaddr] = True
+ if i % 100 == 99:
+ f = float(i) / len(self.ranges)
+ if f >= fnext:
+ sys.stderr.write("%d%%" % int(f*100.0))
+ fnext += 0.1
+ sys.stderr.write(".")
+ sys.stderr.write("100%\n")
+ # split blocks at labeltargets
+ t = self.labeltargets
+ #print t
+ for r in self.ranges:
+ #print r.addr, r.addr + len(r.data)
+ for i in range(r.addr + 1, r.addr + len(r.data)):
+ if i in t:
+ #print i
+ ofs = i - r.addr
+ self.ranges.append(CodeRange(self, i, r.data[ofs:]))
+ r.data = r.data[:ofs]
+ r.fallthrough = True
+ try:
+ del r.text
+ except AttributeError:
+ pass
+ break
+ # hack hack hacked
+
+ def show(self):
+ g1 = Graph('codedump')
+ for r in self.ranges:
+ text, width = tab2columns(r.disassemble())
+ text = '0x%x\n\n%s' % (r.addr, text)
+ g1.emit_node('N_%x' % r.addr, shape="box", label=text,
+ width=str(width*0.1125))
+ for lineno, targetaddr, final in r.findjumps():
+ if final:
+ color = "black"
+ else:
+ color = "red"
+ g1.emit_edge('N_%x' % r.addr, 'N_%x' % targetaddr, color=color)
+ g1.display()
+
+
+def tab2columns(text):
+ lines = text.split('\n')
+ columnwidth = []
+ for line in lines:
+ columns = line.split('\t')[:-1]
+ while len(columnwidth) < len(columns):
+ columnwidth.append(0)
+ for i, s in enumerate(columns):
+ width = len(s.strip())
+ if not s.endswith(':'):
+ width += 2
+ columnwidth[i] = max(columnwidth[i], width)
+ columnwidth.append(1)
+ result = []
+ for line in lines:
+ columns = line.split('\t')
+ text = []
+ for width, s in zip(columnwidth, columns):
+ text.append(s.strip().ljust(width))
+ result.append(' '.join(text))
+ lengths = [len(line) for line in result]
+ lengths.append(1)
+ totalwidth = max(lengths)
+ return '\\l'.join(result), totalwidth
+
+# ____________________________________________________________
+# XXX pasted from
+# http://codespeak.net/svn/user/arigo/hack/misc/graphlib.py
+# but needs to be a bit more subtle later
+
+from pypy.translator.tool.make_dot import DotGen
+from pypy.translator.tool.pygame.graphclient import display_layout
+
+class Graph(DotGen):
+
+ def highlight(self, word, text, linked_to=None):
+ if not hasattr(self, '_links'):
+ self._links = {}
+ self._links_to = {}
+ self._links[word] = text
+ if linked_to:
+ self._links_to[word] = linked_to
+
+ def display(self):
+ "Display a graph page locally."
+ display_layout(_Page(self))
+
+
+class NoGraph(Exception):
+ pass
+
+class _Page:
+ def __init__(self, graph_builder):
+ if callable(graph_builder):
+ graph = graph_builder()
+ else:
+ graph = graph_builder
+ if graph is None:
+ raise NoGraph
+ self.graph_builder = graph_builder
+
+ def content(self):
+ return _PageContent(self.graph_builder)
+
+class _PageContent:
+ fixedfont = True
+
+ def __init__(self, graph_builder):
+ if callable(graph_builder):
+ graph = graph_builder()
+ else:
+ graph = graph_builder
+ assert graph is not None
+ self.graph_builder = graph_builder
+ self.graph = graph
+ self.links = getattr(graph, '_links', {})
+ if not hasattr(graph, '_source'):
+ graph._source = graph.generate(target=None)
+ self.source = graph._source
+
+ def followlink(self, link):
+ try:
+ return _Page(self.graph._links_to[link])
+ except NoGraph:
+ return _Page(self.graph_builder)
+
+# ____________________________________________________________
+
+if __name__ == '__main__':
+ if len(sys.argv) == 1:
+ f = sys.stdin
+ else:
+ f = open(sys.argv[1], 'r')
+ world = World()
+ world.parse(f)
+ world.show()
More information about the Pypy-commit
mailing list