Author: Richard Plangger <[email protected]>
Branch: s390x-backend
Changeset: r80195:6177697cbd11
Date: 2015-10-14 12:25 +0200
http://bitbucket.org/pypy/pypy/changeset/6177697cbd11/

Log:    started the auto instruction encoding, AR_rr correctly assembles

diff --git a/rpython/jit/backend/zarch/codebuilder.py 
b/rpython/jit/backend/zarch/codebuilder.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/zarch/codebuilder.py
@@ -0,0 +1,93 @@
+from rpython.jit.backend.zarch import conditions as cond
+from rpython.jit.backend.zarch import registers as reg
+from rpython.jit.backend.llsupport.asmmemmgr import BlockBuilderMixin
+from rpython.rlib.objectmodel import we_are_translated
+from rpython.rtyper.lltypesystem import lltype, rffi, llmemory
+from rpython.tool.udir import udir
+from rpython.jit.backend.detect_cpu import autodetect
+
+clear_cache = rffi.llexternal(
+    "__clear_cache",
+    [llmemory.Address, llmemory.Address],
+    lltype.Void,
+    _nowrapper=True,
+    sandboxsafe=True)
+
+
+def binary_helper_call(name):
+    function = getattr(support, 'arm_%s' % name)
+
+    def f(self, c=cond.AL):
+        """Generates a call to a helper function, takes its
+        arguments in r0 and r1, result is placed in r0"""
+        addr = rffi.cast(lltype.Signed, function)
+        self.BL(addr, c)
+    return f
+
+
+codes = {
+    'ADD_rr': 0x1A,
+}
+
+def encode_rr(reg1, reg2):
+    return chr(((reg2 & 0x0f) << 4) | (reg1 & 0xf))
+
+class AbstractZARCHBuilder(object):
+    def write32(self, word):
+        self.writechar(chr(word & 0xFF))
+        self.writechar(chr((word >> 8) & 0xFF))
+        self.writechar(chr((word >> 16) & 0xFF))
+        self.writechar(chr((word >> 24) & 0xFF))
+
+    def AR_rr(self, reg1, reg2):
+        self.writechar(chr(0x1A))
+        self.writechar(encode_rr(reg1, reg2))
+
+class InstrBuilder(BlockBuilderMixin, AbstractZARCHBuilder):
+
+    def __init__(self):
+        AbstractZARCHBuilder.__init__(self)
+        self.init_block_builder()
+        #
+        # ResOperation --> offset in the assembly.
+        # ops_offset[None] represents the beginning of the code after the last 
op
+        # (i.e., the tail of the loop)
+        self.ops_offset = {}
+
+    def mark_op(self, op):
+        pos = self.get_relative_pos()
+        self.ops_offset[op] = pos
+
+    def _dump_trace(self, addr, name, formatter=-1):
+        if not we_are_translated():
+            if formatter != -1:
+                name = name % formatter
+            dir = udir.ensure('asm', dir=True)
+            f = dir.join(name).open('wb')
+            data = rffi.cast(rffi.CCHARP, addr)
+            for i in range(self.currpos()):
+                f.write(data[i])
+            f.close()
+
+    def clear_cache(self, addr):
+        if we_are_translated():
+            startaddr = rffi.cast(llmemory.Address, addr)
+            endaddr = rffi.cast(llmemory.Address,
+                            addr + self.get_relative_pos())
+            clear_cache(startaddr, endaddr)
+
+    def copy_to_raw_memory(self, addr):
+        self._copy_to_raw_memory(addr)
+        self.clear_cache(addr)
+        self._dump(addr, "jit-backend-dump", 'arm')
+
+    def currpos(self):
+        return self.get_relative_pos()
+
+#define_instructions(AbstractARMBuilder)
+
+_classes = (AbstractZARCHBuilder,)
+
+# Used to build the MachineCodeBlockWrapper
+all_instructions = sorted([name for cls in _classes for name in cls.__dict__ \
+                          if name.split('_')[0].isupper()])
diff --git a/rpython/jit/backend/zarch/runner.py 
b/rpython/jit/backend/zarch/runner.py
--- a/rpython/jit/backend/zarch/runner.py
+++ b/rpython/jit/backend/zarch/runner.py
@@ -9,7 +9,7 @@
 
     def cast_ptr_to_int(x):
         adr = llmemory.cast_ptr_to_adr(x)
-        return adr
+        return adr # TODO
     cast_ptr_to_int._annspecialcase_ = 'specialize:arglltype(0)'
     cast_ptr_to_int = staticmethod(cast_ptr_to_int)
 
diff --git a/rpython/jit/backend/zarch/test/test_assembler.py 
b/rpython/jit/backend/zarch/test/test_assembler.py
--- a/rpython/jit/backend/zarch/test/test_assembler.py
+++ b/rpython/jit/backend/zarch/test/test_assembler.py
@@ -18,7 +18,6 @@
 
 CPU = getcpuclass()
 
-
 class TestRunningAssembler(object):
     def setup_method(self, method):
         cpu = CPU(None, None)
diff --git a/rpython/jit/backend/zarch/test/test_auto_encoding.py 
b/rpython/jit/backend/zarch/test/test_auto_encoding.py
new file mode 100644
--- /dev/null
+++ b/rpython/jit/backend/zarch/test/test_auto_encoding.py
@@ -0,0 +1,280 @@
+import os, random, struct
+import subprocess
+import py
+from rpython.jit.backend.zarch import codebuilder
+from rpython.rlib.rarithmetic import intmask
+from rpython.tool.udir import udir
+
+INPUTNAME = 'checkfile_%s.s'
+FILENAME = 'checkfile_%s.o'
+BEGIN_TAG = '<<<zarch-test-begin>>>'
+END_TAG =   '<<<zarch-test-end>>>'
+
+class CodeCheckerMixin(object):
+    def __init__(self, expected, accept_unnecessary_prefix):
+        self.expected = expected
+        self.accept_unnecessary_prefix = accept_unnecessary_prefix
+        self.index = 0
+
+    def begin(self, op):
+        self.op = op
+        self.instrindex = self.index
+
+    def writechar(self, char):
+        if char != self.expected[self.index:self.index+1]:
+            if (char == self.accept_unnecessary_prefix
+                and self.index == self.instrindex):
+                return    # ignore the extra character '\x40'
+            print self.op
+            print "\x09from codebuilder.py:", 
hexdump(self.expected[self.instrindex:self.index] + char)+"..."
+            print "\x09from 'as':   ", 
hexdump(self.expected[self.instrindex:self.index+15])+"..."
+            raise Exception("Differs")
+        self.index += 1
+
+    def done(self):
+        assert len(self.expected) == self.index
+
+    def stack_frame_size_delta(self, delta):
+        pass   # ignored
+
+    def check_stack_size_at_ret(self):
+        pass   # ignored
+
+class CodeCheckerZARCH(CodeCheckerMixin, codebuilder.InstrBuilder):
+    pass
+
+def hexdump(s):
+    return ' '.join(["%02X" % ord(c) for c in s])
+
+def reduce_to_32bit(s):
+    if s[:2] != '%r':
+        return s
+    if s[2:].isdigit():
+        return s + 'd'
+    else:
+        return '%e' + s[2:]
+
+# ____________________________________________________________
+
+COUNT1 = 15
+suffixes = {0:'', 1:'b', 2:'w', 4:'l', 8:'q'}
+
+
+class TestZARCH(object):
+    WORD = 8
+    TESTDIR = 'zarch'
+    REGS = range(15+1)
+    REGNAMES = ['%%r%d' % i for i in REGS]
+    accept_unnecessary_prefix = None
+    methname = '?'
+
+    def reg_tests(self):
+        return self.REGS
+
+    def stack_bp_tests(self, count=COUNT1):
+        return ([0, 4, -4, 124, 128, -128, -132] +
+                [random.randrange(-0x20000000, 0x20000000) * 4
+                 for i in range(count)])
+
+    def stack_sp_tests(self, count=COUNT1):
+        return ([0, 4, 124, 128] +
+                [random.randrange(0, 0x20000000) * 4
+                 for i in range(count)])
+
+    def memory_tests(self):
+        return [(reg, ofs)
+                    for reg in self.NONSPECREGS
+                    for ofs in self.stack_bp_tests(5)
+                ]
+
+    def array_tests(self):
+        return [(reg1, reg2, scaleshift, ofs)
+                    for reg1 in self.NONSPECREGS
+                    for reg2 in self.NONSPECREGS
+                    for scaleshift in [0, 1, 2, 3]
+                    for ofs in self.stack_bp_tests(1)
+                ]
+
+    def imm8_tests(self):
+        v = ([-128,-1,0,1,127] +
+             [random.randrange(-127, 127) for i in range(COUNT1)])
+        return v
+
+    def imm32_tests(self):
+        v = ([-0x80000000, 0x7FFFFFFF, 128, 256, -129, -255] +
+             [random.randrange(-32768,32768)<<16 |
+                 random.randrange(0,65536) for i in range(COUNT1)] +
+             [random.randrange(128, 256) for i in range(COUNT1)])
+        return self.imm8_tests() + v
+
+    def relative_tests(self):
+        py.test.skip("explicit test required for %r" % (self.methname,))
+
+    def get_all_tests(self):
+        return {
+            'r': self.reg_tests,
+            }
+
+    def assembler_operand_reg(self, regnum):
+        return self.REGNAMES[regnum]
+
+    def assembler_operand_reg8(self, regnum):
+        assert regnum & rx86.BYTE_REG_FLAG
+        return self.REGNAMES8[regnum &~ rx86.BYTE_REG_FLAG]
+
+    def assembler_operand_xmm_reg(self, regnum):
+        return self.XMMREGNAMES[regnum]
+
+    def assembler_operand_stack_bp(self, position):
+        return '%d(%s)' % (position, self.REGNAMES[5])
+
+    def assembler_operand_stack_sp(self, position):
+        return '%d(%s)' % (position, self.REGNAMES[4])
+
+    def assembler_operand_memory(self, (reg1, offset)):
+        if not offset: offset = ''
+        return '%s(%s)' % (offset, self.REGNAMES[reg1])
+
+    def assembler_operand_array(self, (reg1, reg2, scaleshift, offset)):
+        if not offset: offset = ''
+        return '%s(%s,%s,%d)' % (offset, self.REGNAMES[reg1],
+                                 self.REGNAMES[reg2], 1<<scaleshift)
+
+    def assembler_operand_imm(self, value):
+        return '$%d' % value
+
+    def assembler_operand_imm_addr(self, value):
+        return '%d' % value
+
+    def get_all_assembler_operands(self):
+        return {
+            'r': self.assembler_operand_reg,
+            }
+
+    def run_test(self, methname, instrname, argmodes, args_lists,
+                 instr_suffix=None):
+        global labelcount
+        labelcount = 0
+        oplist = []
+        testdir = udir.ensure(self.TESTDIR, dir=1)
+        inputname = str(testdir.join(INPUTNAME % methname))
+        filename  = str(testdir.join(FILENAME  % methname))
+        with open(inputname, 'w') as g:
+            g.write('\x09.string "%s"\n' % BEGIN_TAG)
+            #
+            for args in args_lists:
+                suffix = ""
+                if instr_suffix is not None:
+                    suffix = instr_suffix    # overwrite
+
+                assembler_operand = self.get_all_assembler_operands()
+                ops = []
+                for mode, v in zip(argmodes, args):
+                    ops.append(assembler_operand[mode](v))
+                ops.reverse()
+                #
+                op = '\t%s%s %s' % (instrname.lower(), suffix,
+                                      ', '.join(ops))
+                g.write('%s\n' % op)
+                oplist.append(op)
+            g.write('\t.string "%s"\n' % END_TAG)
+        proc = subprocess.Popen(['as', '-m' + str(self.WORD*8), '-mzarch',
+                                 inputname, '-o', filename],
+                                stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        stdout, stderr = proc.communicate()
+        if proc.returncode or stderr:
+            raise Exception("could not execute assembler. error:\n%s" % 
(stderr))
+        with open(inputname, 'r') as g:
+            got = g.read()
+        error = [line for line in got.splitlines() if 'error' in line.lower()]
+        if error:
+            raise Exception("Assembler got an error: %r" % error[0])
+        error = [line for line in got.splitlines()
+                 if 'warning' in line.lower()]
+        if error:
+            raise Exception("Assembler got a warning: %r" % error[0])
+        try:
+            with open(filename, 'rb') as f:
+                data = f.read()
+                i = data.find(BEGIN_TAG)
+                assert i>=0
+                j = data.find(END_TAG, i)
+                assert j>=0
+                as_code = data[i+len(BEGIN_TAG)+1:j]
+        except IOError:
+            raise Exception("Assembler did not produce output?")
+        return oplist, as_code
+
+    def make_all_tests(self, methname, modes, args=[]):
+        if modes:
+            tests = self.get_all_tests()
+            m = modes[0]
+            lst = tests[m]()
+            random.shuffle(lst)
+            if methname == 'PSRAD_xi' and m == 'i':
+                lst = [x for x in lst if 0 <= x <= 31]
+            result = []
+            for v in lst:
+                result += self.make_all_tests(methname, modes[1:], args+[v])
+            return result
+        else:
+            # special cases
+            if methname in ('ADD_ri', 'AND_ri', 'CMP_ri', 'OR_ri',
+                            'SUB_ri', 'XOR_ri', 'SBB_ri'):
+                if args[0] == rx86.R.eax:
+                    return []  # ADD EAX, constant: there is a special encoding
+            if methname in ('CMP8_ri',):
+                if args[0] == rx86.R.al:
+                    return []   # CMP AL, constant: there is a special encoding
+            if methname == 'XCHG_rr' and rx86.R.eax in args:
+                return [] # special encoding
+            if methname == 'MOV_rj' and args[0] == rx86.R.eax:
+                return []   # MOV EAX, [immediate]: there is a special encoding
+            if methname == 'MOV_jr' and args[1] == rx86.R.eax:
+                return []   # MOV [immediate], EAX: there is a special encoding
+            if methname == 'MOV8_rj' and args[0] == rx86.R.al:
+                return []   # MOV AL, [immediate]: there is a special encoding
+            if methname == 'MOV8_jr' and args[1] == rx86.R.al:
+                return []   # MOV [immediate], AL: there is a special encoding
+
+            return [args]
+
+    def should_skip_instruction(self, instrname, argmodes):
+        return False
+
+    def complete_test(self, methname):
+        if '_' in methname:
+            instrname, argmodes = methname.split('_')
+        else:
+            instrname, argmodes = methname, ''
+
+        if self.should_skip_instruction(instrname, argmodes):
+            print "Skipping %s" % methname
+            return
+
+        instr_suffix = None
+
+        print "Testing %s with argmodes=%r" % (instrname, argmodes)
+        self.methname = methname
+        ilist = self.make_all_tests(methname, argmodes)
+        oplist, as_code = self.run_test(methname, instrname, argmodes, ilist,
+                                        instr_suffix)
+        cc = CodeCheckerZARCH(as_code, self.accept_unnecessary_prefix)
+        for op, args in zip(oplist, ilist):
+            if op:
+                cc.begin(op)
+                getattr(cc, methname)(*args)
+        cc.done()
+
+    def setup_class(cls):
+        import os
+        g = os.popen('as -version </dev/null -o /dev/null 2>&1')
+        data = g.read()
+        g.close()
+        if not data.startswith('GNU assembler'):
+            py.test.skip("full tests require the GNU 'as' assembler")
+
+    @py.test.mark.parametrize("name", codebuilder.all_instructions)
+    def test_all(self, name):
+        self.complete_test(name)
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to