[pypy-svn] r67901 - in pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86: . test

arigo at codespeak.net arigo at codespeak.net
Fri Sep 25 23:59:44 CEST 2009


Author: arigo
Date: Fri Sep 25 23:59:42 2009
New Revision: 67901

Modified:
   pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/ri386.py
   pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/test/test_ri386.py
Log:
Start from scratch ri386.py.  Keep the encoding description
but kill the multimethod dispatch.


Modified: pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/ri386.py
==============================================================================
--- pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/ri386.py	(original)
+++ pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/ri386.py	Fri Sep 25 23:59:42 2009
@@ -1,401 +1,133 @@
 from pypy.rlib.rarithmetic import intmask
 from pypy.rlib.objectmodel import ComputedIntSymbolic, we_are_translated
+from pypy.rlib.objectmodel import specialize
+from pypy.rlib.unroll import unrolling_iterable
 
-class OPERAND(object):
-    _attrs_ = []
-    def __repr__(self):
-        return '<%s %s>' % (self.__class__.__name__, self.assembler())
-
-class REG(OPERAND):
-    width = 4
-    def __repr__(self):
-        return '<%s>' % self.__class__.__name__.lower()
-    def assembler(self):
-        return '%' + self.__class__.__name__.lower()
-    def lowest8bits(self):
-        if self.op < 4:
-            return registers8[self.op]
-        else:
-            raise ValueError
+WORD = 4
 
-class FLOATREG(OPERAND):
-    width = 8
+eax = 0
+ecx = 1
+edx = 2
+ebx = 3
+esp = 4
+ebp = 5
+esi = 6
+edi = 7
 
-    def __repr__(self):
-        return '<ST(%d)>' % self.num
+# ____________________________________________________________
+# Emit a single char
 
-    def assembler(self):
-        raise TypeError("Float registers should not appear in assembler")
-
-class XMMREG(OPERAND):
-    width = 8
-
-    def __repr__(self):
-        return '<XMM(%d)>' % self.op
-
-    def assembler(self):
-        return '%xmm' + str(self.op)
-
-class ST0(FLOATREG): num=0
-class ST1(FLOATREG): num=1
-class ST2(FLOATREG): num=2
-class ST3(FLOATREG): num=3
-class ST4(FLOATREG): num=4
-class ST5(FLOATREG): num=5
-class ST6(FLOATREG): num=6
-class ST7(FLOATREG): num=7
-
-class XMM0(XMMREG): op=0
-class XMM1(XMMREG): op=1
-class XMM2(XMMREG): op=2
-class XMM3(XMMREG): op=3
-class XMM4(XMMREG): op=4
-class XMM5(XMMREG): op=5
-class XMM6(XMMREG): op=6
-class XMM7(XMMREG): op=7
-
-class REG8(OPERAND):
-    width = 1
-    def __repr__(self):
-        return '<%s>' % self.__class__.__name__.lower()
-    def assembler(self):
-        return '%' + self.__class__.__name__.lower()    
-
-class EAX(REG): op=0
-class ECX(REG): op=1
-class EDX(REG): op=2
-class EBX(REG): op=3
-class ESP(REG): op=4
-class EBP(REG): op=5
-class ESI(REG): op=6
-class EDI(REG): op=7
-
-class AL(REG8): op=0
-class CL(REG8): op=1
-class DL(REG8): op=2
-class BL(REG8): op=3
-class AH(REG8): op=4
-class CH(REG8): op=5
-class DH(REG8): op=6
-class BH(REG8): op=7
-
-class IMM32(OPERAND):
-    width = 4
-    value = 0      # annotator hack
-
-    def __init__(self, value):
-        self.value = value
-    def assembler(self):
-        return '$%d' % (self.value,)
-
-    def lowest8bits(self):
-        val = self.value & 0xFF
-        if val > 0x7F:
-            val -= 0x100
-        return IMM8(val)
-
-class IMM8(IMM32):
-    width = 1
-
-class IMM16(OPERAND):  # only for RET
-    width = 2
-    value = 0      # annotator hack
-
-    def __init__(self, value):
-        self.value = value
-    def assembler(self):
-        return '$%d' % (self.value,)
-
-class MODRM(OPERAND):
-    width = 4
-
-    def __init__(self, byte, extradata):
-        self.byte = byte
-        self.extradata = extradata
-
-    def lowest8bits(self):
-        return MODRM8(self.byte, self.extradata)
-
-    def assembler(self):
-        mod = self.byte & 0xC0
-        rm  = self.byte & 0x07
-        if mod == 0xC0:
-            return registers[rm].assembler()
-        if self.byte == 0x05:
-            return '%d' % (unpack(self.extradata),)
-        if mod == 0x00:
-            offset_bytes = 0
-        elif mod == 0x40:
-            offset_bytes = 1
-        else:
-            offset_bytes = 4
-        if rm == 4:
-            SIB = ord(self.extradata[0])
-            scale = (SIB & 0xC0) >> 6
-            index = (SIB & 0x38) >> 3
-            base  = (SIB & 0x07)
-            if base == 5 and mod == 0x00:
-                offset_bytes = 4
-                basename = ''
-            else:
-                basename = registers[base].assembler()
-            if index == 4:
-                # no index
-                s = '(%s)' % (basename,)
-            else:
-                indexname = registers[index].assembler()
-                s = '(%s,%s,%d)' % (basename, indexname, 1 << scale)
-            offset = self.extradata[1:]
-        else:
-            s = '(%s)' % (registers[rm].assembler(),)
-            offset = self.extradata
+def encode_char(mc, char, orbyte, *ignored):
+    mc.writechar(chr(char | orbyte))
+    return 0
 
-        assert len(offset) == offset_bytes
-        if offset_bytes > 0:
-            s = '%d%s' % (unpack(offset), s)
-        return s
-
-    def is_register(self):
-        mod = self.byte & 0xC0
-        return mod == 0xC0
-
-    def ofs_relative_to_ebp(self):
-        # very custom: if self is a mem(ebp, ofs) then return ofs
-        # otherwise raise ValueError
-        mod = self.byte & 0xC0
-        rm  = self.byte & 0x07
-        if mod == 0xC0:
-            raise ValueError     # self is just a register
-        if self.byte == 0x05:
-            raise ValueError     # self is just an [immediate]
-        if rm != 5:
-            raise ValueError     # not a simple [ebp+ofs]
-        offset = self.extradata
-        if not offset:
-            return 0
-        else:
-            return unpack(offset)
+# ____________________________________________________________
+# Encode a register number in the orbyte
 
-    def is_relative_to_ebp(self):
-        try:
-            self.ofs_relative_to_ebp()
-        except ValueError:
-            return False
-        else:
-            return True
+ at specialize.arg(1)
+def encode_register(mc, (argnum, factor), orbyte, *args):
+    reg = args[argnum-1]
+    assert 0 <= reg < 8
+    return orbyte | (reg * factor)
 
-    def involves_ecx(self):
-        # very custom: is ecx present in this mod/rm?
-        mod = self.byte & 0xC0
-        rm  = self.byte & 0x07
-        if mod != 0xC0 and rm == 4:
-            SIB = ord(self.extradata[0])
-            index = (SIB & 0x38) >> 3
-            base  = (SIB & 0x07)
-            return base == ECX.op or index == ECX.op
-        else:
-            return rm == ECX.op
+def register(argnum, factor=1):
+    return encode_register, (argnum, factor)
 
-class MODRM64(MODRM):
-    width = 8
+# ____________________________________________________________
+# Encode a constant in the orbyte
 
-class MODRM8(MODRM):
-    width = 1
+def encode_orbyte(mc, constant, orbyte, *ignored):
+    return orbyte | constant
 
-class REL32(OPERAND):
-    width = 4
-    def __init__(self, absolute_target):
-        self.absolute_target = absolute_target
-    def assembler(self):
-        return '%d' % (self.absolute_target,)
-
-class MISSING(OPERAND):
-    def __repr__(self):
-        return '<MISSING>'
+def orbyte(value):
+    return encode_orbyte, value
 
 # ____________________________________________________________
-# Public interface: the concrete operands to instructions
-# 
-# NB.: UPPERCASE names represent classes of operands (the same
-#      instruction can have multiple modes, depending on these
-#      classes), while lowercase names are concrete operands.
-
-
-eax = EAX()
-ecx = ECX()
-edx = EDX()
-ebx = EBX()
-esp = ESP()
-ebp = EBP()
-esi = ESI()
-edi = EDI()
-
-al = AL()
-cl = CL()
-dl = DL()
-bl = BL()
-ah = AH()
-ch = CH()
-dh = DH()
-bh = BH()
-
-st0 = ST0()
-st1 = ST1()
-st2 = ST2()
-st3 = ST3()
-st4 = ST4()
-st5 = ST5()
-st6 = ST6()
-st7 = ST7()
-
-xmm0 = XMM0()
-xmm1 = XMM1()
-xmm2 = XMM2()
-xmm3 = XMM3()
-xmm4 = XMM4()
-xmm5 = XMM5()
-xmm6 = XMM6()
-xmm7 = XMM7()
-
-registers = [eax, ecx, edx, ebx, esp, ebp, esi, edi]
-registers8 = [al, cl, dl, bl, ah, ch, dh, bh]
-xmm_registers = [xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7]
-
-for r in registers + registers8:
-    r.bitmask = 1 << r.op
-del r
-
-imm32 = IMM32
-imm8 = IMM8
-imm16 = IMM16
-rel32 = REL32
-
-def imm(value):
-    if isinstance(value, ComputedIntSymbolic):
-        value = value.compute_fn()
-    if not we_are_translated():
-        assert type(value) is int
-    if single_byte(value):
-        return imm8(value)
-    else:
-        return imm32(value)
+# Emit an immediate value
 
-def memregister(register):
-    assert register.width == 4
-    return MODRM(0xC0 | register.op, '')
-
-def mem(basereg, offset=0):
-    return memSIB(basereg, None, 0, offset)
-
-def heap(offset):
-    return memSIB(None, None, 0, offset)
-
-def heap8(offset):
-    return memSIB8(None, None, 0, offset)
-
-def heap64(offset):
-    return memSIB64(None, None, 0, offset)
-
-def mem64(basereg, offset=0):
-    return memSIB64(basereg, None, 0, offset)
-
-def memSIB(base, index, scaleshift, offset):
-    return _SIBencode(MODRM, base, index, scaleshift, offset)
-
-def memSIB64(base, index, scaleshift, offset):
-    return _SIBencode(MODRM64, base, index, scaleshift, offset)    
-
-def memregister8(register):
-    assert register.width == 1
-    return MODRM8(0xC0 | register.op, '')
-
-def mem8(basereg, offset=0):
-    return memSIB8(basereg, None, 0, offset)
-
-def memSIB8(base, index, scaleshift, offset):
-    return _SIBencode(MODRM8, base, index, scaleshift, offset)
-
-def _SIBencode(cls, base, index, scaleshift, offset):
-    assert base is None or isinstance(base, REG)
-    assert index is None or (isinstance(index, REG) and index is not esp)
-    assert 0<=scaleshift<4
-
-    if base is None:
-        if index is None:
-            return cls(0x05, packimm32(offset))
-        if scaleshift > 0:
-            return cls(0x04, chr((scaleshift<<6) | (index.op<<3) | 0x05) +
-                               packimm32(offset))
-        base = index
-        index = None
-
-    if index is not None:
-        SIB = chr((scaleshift<<6) | (index.op<<3) | base.op)
-    elif base is esp:
-        SIB = '\x24'
-    elif offset == 0 and base is not ebp:
-        return cls(base.op, '')
-    elif single_byte(offset):
-        return cls(0x40 | base.op, packimm8(offset))
-    else:
-        return cls(0x80 | base.op, packimm32(offset))
+ at specialize.arg(1)
+def encode_immediate(mc, (argnum, width), orbyte, *args):
+    imm = args[argnum-1]
+    assert orbyte == 0
+    mc.writeimm(imm, width)
+    return 0
 
-    if offset == 0 and base is not ebp:
-        return cls(0x04, SIB)
-    elif single_byte(offset):
-        return cls(0x44, SIB + packimm8(offset))
-    else:
-        return cls(0x84, SIB + packimm32(offset))
+def immediate(argnum, width='i'):
+    return encode_immediate, (argnum, width)
 
-def fixedsize_ebp_ofs(offset):
-    return MODRM(0x80 | EBP.op, packimm32(offset))
+# ____________________________________________________________
+# Emit a mod/rm referencing a stack location
+# This depends on the fact that our function prologue contains
+# exactly 4 PUSHes.
+
+def get_ebp_ofs(position):
+    # Argument is a stack position (0, 1, 2...).
+    # Returns (ebp-16), (ebp-20), (ebp-24)...
+    # This depends on the fact that our function prologue contains
+    # exactly 4 PUSHes.
+    return -WORD * (4 + position)
 
 def single_byte(value):
     return -128 <= value < 128
 
-def packimm32(i):
-    return (chr(i & 0xFF) +
-            chr((i >> 8) & 0xFF) +
-            chr((i >> 16) & 0xFF) +
-            chr((i >> 24) & 0xFF))
-
-def packimm8(i):
-    if i < 0:
-        i += 256
-    return chr(i)
-
-def packimm16(i):
-    return (chr(i & 0xFF) +
-            chr((i >> 8) & 0xFF))
-
-def unpack(s):
-    assert len(s) in (1, 2, 4)
-    if len(s) == 1:
-        a = ord(s[0])
-        if a > 0x7f:
-            a -= 0x100
+ at specialize.arg(1)
+def encode_stack(mc, (argnum, allow_single_byte), orbyte, *args):
+    offset = get_ebp_ofs(args[argnum-1])
+    if allow_single_byte and single_byte(offset):
+        mc.writechar(chr(0x40 | ebp | orbyte))
+        mc.writeimm(offset, 'b')
     else:
-        a = ord(s[0]) | (ord(s[1]) << 8)
-        if len(s) == 2:
-            if a > 0x7fff:
-                a -= 0x10000
-        else:
-            a |= (ord(s[2]) << 16) | (ord(s[3]) << 24)
-            a = intmask(a)
-    return a
+        mc.writechar(chr(0x80 | ebp | orbyte))
+        mc.writeimm(offset, 'i')
+    return 0
+
+def stack(argnum, allow_single_byte=True):
+    return encode_stack, (argnum, allow_single_byte)
 
-missing = MISSING()
+# ____________________________________________________________
+
+def insn(*encoding):
+    def encode(mc, *args):
+        orbyte = 0
+        for encode_step, encode_static_arg in encoding_steps:
+            orbyte = encode_step(mc, encode_static_arg, orbyte, *args)
+        assert orbyte == 0
+    #
+    encoding_steps = []
+    for step in encoding:
+        if isinstance(step, str):
+            for c in step:
+                encoding_steps.append((encode_char, ord(c)))
+        else:
+            assert type(step) is tuple and len(step) == 2
+            encoding_steps.append(step)
+    encoding_steps = unrolling_iterable(encoding_steps)
+    return encode
+
+# ____________________________________________________________
 
-# __________________________________________________________
-# Abstract base class, with methods like NOP(), ADD(x, y), etc.
 
 class I386CodeBuilder(object):
+    """Abstract base class."""
 
-    def write(self, data):
+    def writechar(self, char):
         raise NotImplementedError
 
-    def tell(self):
-        raise NotImplementedError
+    @specialize.arg(2)
+    def writeimm(self, imm, width='i'):
+        self.writechar(chr(imm & 0xFF))
+        if width != 'b':     # != byte
+            self.writechar(chr((imm >> 8) & 0xFF))
+            if width != 'h':    # != 2-bytes word
+                self.writechar(chr((imm >> 16) & 0xFF))
+                self.writechar(chr((imm >> 24) & 0xFF))
+
+    MOV_ri = insn(register(1), '\xB8', immediate(2))
+    MOV_si = insn('\xC7', orbyte(0<<3), stack(1), immediate(2))
+    MOV_rr = insn('\x89', register(2,8), register(1), '\xC0')
+    MOV_sr = insn('\x89', register(2,8), stack(1))
+    MOV_rs = insn('\x8B', register(1,8), stack(2))
 
+    NOP = insn('\x90')
 
-import ri386setup  # side-effect: add methods to I386CodeBuilder
+    ADD_rr = insn('\x01', register(2,8), register(1), '\xC0')

Modified: pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/test/test_ri386.py
==============================================================================
--- pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/test/test_ri386.py	(original)
+++ pypy/branch/remove-ri386-multimethod/pypy/jit/backend/x86/test/test_ri386.py	Fri Sep 25 23:59:42 2009
@@ -5,112 +5,40 @@
     def __init__(self):
         self.buffer = []
 
-    def write(self, data):
-        for c in data:
-            self.buffer.append(c)    # extend the list of characters
-
-    def tell(self):
-        return len(self.buffer)
+    def writechar(self, c):
+        self.buffer.append(c)    # extend the list of characters
 
     def getvalue(self):
         return ''.join(self.buffer)
 
 
-def test_example():
+def test_mov_ri():
+    s = CodeBuilder()
+    s.MOV_ri(ecx, -2)
+    assert s.getvalue() == '\xB9\xFE\xFF\xFF\xFF'
+
+def test_mov_si():
+    s = CodeBuilder()
+    s.MOV_si(5, 1<<24)
+    assert s.getvalue() == '\xC7\x45\xDC\x00\x00\x00\x01'
+
+def test_mov_rr():
+    s = CodeBuilder()
+    s.MOV_rr(ebx, ebp)
+    assert s.getvalue() == '\x89\xEB'
+
+def test_mov_sr():
+    s = CodeBuilder()
+    s.MOV_sr(5, edx)
+    assert s.getvalue() == '\x89\x55\xDC'
+
+def test_mov_rs():
+    s = CodeBuilder()
+    s.MOV_rs(edx, 5)
+    assert s.getvalue() == '\x8B\x55\xDC'
+
+def test_nop_add_rr():
     s = CodeBuilder()
     s.NOP()
-    s.ADD(eax, eax)
+    s.ADD_rr(eax, eax)
     assert s.getvalue() == '\x90\x01\xC0'
-
-def test_modrm():
-    assert memregister(ecx).assembler() == '%ecx'
-    assert memregister(ebp).assembler() == '%ebp'
-    assert memregister(esp).assembler() == '%esp'
-    assert memSIB(eax, ebx, 0, 2).assembler() == '2(%eax,%ebx,1)'
-    assert memSIB(None, ebx, 1, 2).assembler() == '2(,%ebx,2)'
-    assert memSIB(eax, None, 0, 2).assembler() == '2(%eax)'
-    assert memSIB(None, None, 0, 2).assembler() == '2'
-    assert memSIB(ebp, ebx, 0, 2).assembler() == '2(%ebp,%ebx,1)'
-    assert memSIB(ebp, None, 0, 2).assembler() == '2(%ebp)'
-    assert memSIB(None, ebp, 1, 2).assembler() == '2(,%ebp,2)'
-    assert memSIB(ebp, ebp, 0, 2).assembler() == '2(%ebp,%ebp,1)'
-    assert memSIB(eax, ebx, 0, 0).assembler() == '(%eax,%ebx,1)'
-    assert memSIB(None, ebx, 1, 0).assembler() == '0(,%ebx,2)'
-    assert memSIB(eax, None, 0, 0).assembler() == '(%eax)'
-    assert memSIB(None, None, 0, 0).assembler() == '0'
-    assert memSIB(ebp, ebx, 0, 0).assembler() == '0(%ebp,%ebx,1)'
-    assert memSIB(ebp, None, 0, 0).assembler() == '0(%ebp)'
-    assert memSIB(None, ebp, 1, 0).assembler() == '0(,%ebp,2)'
-    assert memSIB(ebp, ebp, 0, 0).assembler() == '0(%ebp,%ebp,1)'
-
-
-def test_basic():
-    def check(expected, insn, *args):
-        s = CodeBuilder()
-        getattr(s, insn)(*args)
-        assert s.getvalue() == expected
-
-    # nop
-    yield check, '\x90',                     'NOP'
-    # mov [ebp+19], ecx
-    yield check, '\x89\x4D\x13',             'MOV', mem(ebp, 19), ecx
-    # add edx, 0x12345678
-    yield check, '\x81\xEA\x78\x56\x34\x12', 'SUB', edx, imm32(0x12345678)
-    # mov dh, 1  (inefficient encoding)
-    yield check, '\xC6\xC6\x01',             'MOV', memregister8(dh), imm8(1)
-    # add esp, 12
-    yield check, '\x83\xC4\x0C',             'ADD', esp, imm8(12)
-    # mov esp, 12
-    yield check, '\xBC\x0C\x00\x00\x00',     'MOV', esp, imm8(12)
-    # sub esi, ecx
-    yield check, '\x29\xCE',                 'SUB', esi, ecx
-    # ret
-    yield check, '\xC3',                     'RET'
-    # ret 20
-    yield check, '\xC2\x14\x00',             'RET', imm16(20)
-    # mov eax, [8*ecx]
-    yield check, '\x89\x04\xcd\x00\x00\x00\x00', \
-                                             'MOV', memSIB(None,ecx,3,0), eax
-    # call +17
-    yield check, '\xE8\x11\x00\x00\x00',     'CALL', rel32(22)
-    # fld
-    #yield check, '\xDD\x44\x24\x04', 'FLD', mem(esp, 4)
-    # fadd
-    #yield check, '\xDC\x44\x24\x08', 'FADD', mem(esp, 8)
-
-##def test_conditional():
-##    """Compare the encoding for the instructions JE, JAE, JC etc.,
-##    with the encoding for the 'Jcond' pseudo-instruction.
-##    """
-##    def check(insn, *args):
-##        from pypy.jit.codegen.i386.ri386setup import Conditions
-##        for cond, value in Conditions.items():
-##            s1 = CodeBuilder()
-##            getattr(s1, insn+cond)(*args)
-##            s2 = CodeBuilder()
-##            getattr(s2, insn+'cond')(imm8(value), *args)
-
-
-def test_translate():
-    from pypy.rpython.test.test_llinterp import interpret
-
-    def f():
-        s = CodeBuilder()
-        s.SUB(esi, ecx)
-        s.MOV(mem(ebp, 19), ecx)
-        return s.getvalue()
-
-    res = interpret(f, [])
-    assert ''.join(res.chars) == '\x29\xCE\x89\x4D\x13'
-
-
-def test_unpack_compiled():
-    from pypy.translator.c.test.test_genc import compile
-
-    def f(n):
-        return mem(ebp, n).ofs_relative_to_ebp()
-
-    fn = compile(f, [int])
-    for i in [0, 4, 44, 124, 128, 132, 252, 256, 10000000,
-              -4, -44, -124, -128, -132, -252, -256, -10000000]:
-        assert fn(i) == i



More information about the Pypy-commit mailing list