Commits

Armin Rigo  committed e0e01c9

In-progress: reduce the various numbers assigned to ResOps, including
for __str__(). Now it adds a constraint to oparsed texts: we cannot use
e.g. both 'p0' and 'i0' (only one variable is allowed at position 0),
or 'f0' and 'f1' (float variables are assumed to take two positions each).
This should be nicely checked by oparser, though.

  • Participants
  • Parent commits 2b24249
  • Branches no-failargs

Comments (0)

Files changed (5)

File pypy/jit/backend/llgraph/runner.py

         self.last_exception = None
 
     def setenv(self, box, arg):
+        if box.type == INT:
+            # typecheck the result
+            if isinstance(arg, bool):
+                arg = int(arg)
+            assert lltype.typeOf(arg) == lltype.Signed
+        elif box.type == REF:
+            assert lltype.typeOf(arg) == llmemory.GCREF
+        elif box.type == FLOAT:
+            assert lltype.typeOf(arg) == longlong.FLOATSTORAGE
+        else:
+            raise AssertionError(box)
+        #
         assert box.getvarindex() >= 0
         self.env[box] = arg
         self.framecontent[box.getvarindex()] = arg
                 if hasattr(gf.descr, '_llgraph_bridge'):
                     i = 0
                     self.lltrace = gf.descr._llgraph_bridge
-                    newvals = [self.env[arg] for arg in self.lltrace.inputargs]
+                    newvals = [self.framecontent[arg._varindex]
+                               for arg in self.lltrace.inputargs]
                     self.do_renaming(self.lltrace.inputargs, newvals)
                     continue
                 raise
-            if op.type == INT:
-                # typecheck the result
-                if isinstance(resval, bool):
-                    resval = int(resval)
-                assert lltype.typeOf(resval) == lltype.Signed
-            elif op.type == REF:
-                assert lltype.typeOf(resval) == llmemory.GCREF
-            elif op.type == FLOAT:
-                assert lltype.typeOf(resval) == longlong.FLOATSTORAGE
-            else:
-                assert op.type == VOID
-                assert resval is None
             if op.type != VOID:
                 self.setenv(op, resval)
+            else:
+                assert resval is None
             i += 1
 
     def do_renaming(self, newargs, newvalues):

File pypy/jit/backend/test/runner_test.py

 
     add_loop_instruction = ['overload for a specific cpu']
     bridge_loop_instruction = ['overload for a specific cpu']
-    _nextvarindex = 0
-
-    def update_varindexes(self, newtext2op):
-        # 'self.text2op' is a dict mapping variable names (as present in
-        # the oparser source) to the corresponding ResOp.  This function
-        # updates it with 'newtext2op', then assigns '_varindex' to all
-        # the new ResOps.  The choice of '_varindex' is done automatically
-        # to avoid conflicts, but existing '_varindex' are not changed.
-        # If 'newtext2op' is not a dict but a list, comes up with names
-        # as per str(op).
-        if isinstance(newtext2op, list):
-            newtext2op = dict([(str(op), op) for op in newtext2op])
-        try:
-            text2op = self.text2op
-        except AttributeError:
-            text2op = self.text2op = {}
-        text2op.update(newtext2op)
-        if newtext2op:
-            # update 'self._nextvarindex' to a value higher than any seen
-            # in newtext2op.  Pick two more rather than one more just in
-            # case the highest so far was a FLOAT.
-            newops = newtext2op.values()
-            random.shuffle(newops)
-            maximum = max([getattr(op, '_varindex', -2) for op in newops])
-            self._nextvarindex = max(self._nextvarindex, maximum + 2)
-            for op in newops:
-                self.assign_varindex(op)
-
-    def assign_varindex(self, op):
-        if (isinstance(op, AbstractResOp) and op.type != VOID and
-                getattr(op, '_varindex', -1) == -1):
-            op._varindex = self._nextvarindex
-            if hasattr(op, '_str'):
-                del op._str     # recompute it
-            # this op consumes either one or two numbers, depending on its
-            # type as a non-FLOAT or FLOAT.  We don't care too much about
-            # being on 32-bit vs 64-bit here.
-            self._nextvarindex += 1 + (op.type == FLOAT)
 
     def execute_operation(self, opname, valueboxes, result_type, descr=None):
         inputargs, operations = self._get_single_operation_list(opname,
                                                                 result_type,
                                                                 valueboxes,
                                                                 descr)
-        self.update_varindexes(inputargs + operations)
+        oparser.assign_all_varindices(inputargs + operations)
         looptoken = JitCellToken()
         self.cpu.compile_loop(inputargs, operations, looptoken)
         args = []
             namespace['faildescr3'] = BasicFailDescr(3)
         if 'faildescr4' not in namespace:
             namespace['faildescr4'] = BasicFailDescr(4)
-        loop = oparser.parse(s, namespace=namespace, mutable=True,
-                             oldvars=getattr(self, 'text2op', {}))
-        self.update_varindexes(loop.text2op)
+        loop = oparser.parse(s, namespace=namespace, mutable=True)
         return loop.inputargs, loop.operations, JitCellToken()
 
     def get_frame_value(self, frame, varname):
-        op = self.text2op[varname]
-        index = op.getvarindex()
+        index = int(varname[1:])
         if varname.startswith('i'):
             return self.cpu.get_frame_value_int(frame, index)
         if varname.startswith('p'):
         faildescr = BasicFailDescr(1)
         inputargs, ops, token = self.parse("""
         [f0]
-        f1 = float_add(f0, 1.)
-        finish(f1, descr=faildescr)
+        f2 = float_add(f0, 1.)
+        finish(f2, descr=faildescr)
         """, namespace=locals())
         self.cpu.compile_loop(inputargs, ops, token)
         frame = self.cpu.execute_token(token, longlong.getfloatstorage(2.8))
             label(%s, descr=targettoken)
             i1 = int_sub(i0, 1)
             i2 = int_ge(i1, 0)
-            guard_true(i2, descr=faildescr) [%s]
+            guard_true(i2, descr=faildescr)
             jump(%s, descr=targettoken)
-            """ % (inp, inp, inp, ", ".join(jumpargs)), None)
+            """ % (inp, inp, ", ".join(jumpargs)), None)
             #
             self.cpu.compile_loop(inpargs, operations, looptoken)
             #
             #
             assert dstvalues[index_counter] == 11
             dstvalues[index_counter] = 0
-            for i, (box, val) in enumerate(zip(inpargs, dstvalues)):
-                if box.type == 'i':
-                    got = self.cpu.get_latest_value_int(frame, i)
-                elif box.type == 'r':
-                    got = self.cpu.get_latest_value_ref(frame, i)
-                elif box.type == 'f':
-                    got = self.cpu.get_latest_value_float(frame, i)
-                else:
-                    assert 0
+            assert len(inputargs) == len(inpargs) == len(dstvalues)
+            for (name, val) in zip(inputargs, dstvalues):
+                got = self.get_frame_value(frame, name)
                 assert type(got) == type(val)
                 assert got == val
 
         [%(fboxes)s]
         label(%(fboxes)s, descr=targettoken)
         i2 = float_le(f0, 9.2)
-        guard_true(i2, descr=faildescr1) [%(fboxes)s]
-        finish(descr=faildescr2) [%(fboxes)s]
+        guard_true(i2, descr=faildescr1)
+        finish(descr=faildescr2)
         """ % {'fboxes': fboxes}, {'faildescr1': faildescr1,
                                    'faildescr2': faildescr2,
                                    'targettoken': targettoken})
             args.append(longlong.getfloatstorage(x))
         frame = self.cpu.execute_token(looptoken, *args)
         assert self.cpu.get_latest_descr(frame).identifier == 2
-        res = self.cpu.get_latest_value_float(frame, 0)
+        res = self.get_frame_value(frame, "f0")
         assert longlong.getrealfloat(res) == 8.5
-        for i in range(1, len(fboxes.split(","))):
-            got = longlong.getrealfloat(self.cpu.get_latest_value_float(frame, i))
+        fboxeslist = map(str.strip, fboxes.split(","))
+        for i in range(1, len(fboxeslist)):
+            res = self.get_frame_value(frame, fboxeslist[i])
+            got = longlong.getrealfloat(res)
             assert got == 13.5 + 6.73 * i
 
     def test_compile_bridge_spilled_float(self):
         if not self.cpu.supports_floats:
             py.test.skip("requires floats")
         fboxes = [boxfloat() for i in range(3)]
-        faildescr1 = BasicFailDescr(100)
         loopops = """
-        [i0,f1, f2]
+        [i0, f1, f2]
         f3 = float_add(f1, f2)
         force_spill(f3)
         force_spill(f1)
         force_spill(f2)
-        guard_false(i0, descr=faildescr0) [f1, f2, f3]
-        finish() []"""
+        guard_false(i0, descr=faildescr0)
+        finish()"""
         inputargs, operations, looptoken = self.parse(
             loopops, namespace={'faildescr0': BasicFailDescr(1)})
         self.cpu.compile_loop(inputargs, operations, looptoken)
         args = [1]
         args.append(longlong.getfloatstorage(132.25))
         args.append(longlong.getfloatstorage(0.75))
-        frame = self.cpu.execute_token(looptoken, *args)  #xxx check
+        frame = self.cpu.execute_token(looptoken, *args)
         assert operations[-2].getdescr()== self.cpu.get_latest_descr(frame)
-        f1 = self.cpu.get_latest_value_float(frame, 0)
-        f2 = self.cpu.get_latest_value_float(frame, 1)
-        f3 = self.cpu.get_latest_value_float(frame, 2)
+        f1 = self.get_frame_value(frame, "f1")
+        f2 = self.get_frame_value(frame, "f2")
+        f3 = self.get_frame_value(frame, "f3")
         assert longlong.getrealfloat(f1) == 132.25
         assert longlong.getrealfloat(f2) == 0.75
         assert longlong.getrealfloat(f3) == 133.0
 
+        faildescr1 = BasicFailDescr(100)
         bridgeops = [
             create_resop(rop.FINISH, None, [], descr=faildescr1,
                          mutable=True),
             ]
-        bridgeops[-1].setfailargs(fboxes)
         self.cpu.compile_bridge(operations[-2].getdescr(), fboxes,
                                 bridgeops, looptoken)
-        args = [1,
-                longlong.getfloatstorage(132.25),
-                longlong.getfloatstorage(0.75)]
         frame = self.cpu.execute_token(looptoken, *args)
         assert self.cpu.get_latest_descr(frame).identifier == 100
-        f1 = self.cpu.get_latest_value_float(frame, 0)
-        f2 = self.cpu.get_latest_value_float(frame, 1)
-        f3 = self.cpu.get_latest_value_float(frame, 2)
+        f1 = self.get_frame_value(frame, "f1")
+        f2 = self.get_frame_value(frame, "f2")
+        f3 = self.get_frame_value(frame, "f3")
         assert longlong.getrealfloat(f1) == 132.25
         assert longlong.getrealfloat(f2) == 0.75
         assert longlong.getrealfloat(f3) == 133.0

File pypy/jit/metainterp/resoperation.py

             return object.__repr__(self)
 
     def __str__(self):
-        if not hasattr(self, '_str'):
-            if self.type == INT:
-                t = 'i'
-            elif self.type == FLOAT:
-                t = 'f'
-            elif self.type == REF:
-                t = 'p'
-            else:
-                t = '?'
-            if getattr(self, '_varindex', -1) < 0:
-                self._str = '%s%d' % (t, AbstractResOp._counter)
+        if self.type == INT:
+            t = 'i'
+        elif self.type == FLOAT:
+            t = 'f'
+        elif self.type == REF:
+            t = 'p'
+        else:
+            t = '?'
+        if getattr(self, '_varindex', -1) < 0:
+            if not hasattr(self, '_str'):
                 AbstractResOp._counter += 1
-            else:
-                self._str = '%s%s' % (t.upper(), self._varindex)
-        return self._str
+                self._str = '%s-%d' % (t, AbstractResOp._counter)
+            return self._str
+        else:
+            return '%s%d' % (t, self._varindex)
 
     def repr(self, graytext=False):
         # RPython-friendly version

File pypy/jit/tool/oparser.py

 
 from pypy.jit.metainterp.resoperation import rop, opclasses, rop_lowercase,\
      ResOpWithDescr, N_aryOp, UnaryOp, PlainResOp, create_resop_dispatch,\
-     ResOpNone, create_resop_0, example_for_opnum
+     ResOpNone, create_resop_0, example_for_opnum, FLOAT
 from pypy.jit.metainterp import optmodel
 from pypy.rpython.lltypesystem import lltype, llmemory
 from pypy.rlib.objectmodel import Symbolic
     use_mock_model = False
 
     def __init__(self, input, cpu, namespace, type_system,
-                 invent_fail_descr=True, results=None, mutable=False,
-                 oldvars={}):
+                 invent_fail_descr=True, results=None, mutable=False):
         self.input = input
-        self.oldvars = oldvars
         self.vars = {}
         self.cpu = cpu
         self._consts = namespace
         self.original_jitcell_token = self.model.JitCellToken()
         self.results = results
         self.mutable = mutable
+        self.allops = []
 
     def get_const(self, name, typ):
         if self._consts is None:
 
     def newvar(self, elem):
         if elem not in self.vars:
-            if elem not in self.oldvars:
-                if elem[0] in 'ifp':
-                    if elem[0] == 'p':
-                        p = 'r'
-                    else:
-                        p = elem[0]
-                    opnum = getattr(rop, 'INPUT_' + p)
-                    box = create_resop_0(opnum, example_for_opnum(opnum),
-                                         mutable=self.mutable)
+            if elem[0] in 'ifp':
+                if elem[0] == 'p':
+                    p = 'r'
                 else:
-                    raise ParseError("Unknown variable type: %s" % elem)
-                box._str = elem
+                    p = elem[0]
+                opnum = getattr(rop, 'INPUT_' + p)
+                box = create_resop_0(opnum, example_for_opnum(opnum),
+                                     mutable=self.mutable)
             else:
-                box = self.oldvars[elem]
+                raise ParseError("Unknown variable type: %s" % elem)
+            self.setstr(box, elem)
             self.vars[elem] = box
         return self.vars[elem]
 
         else:
             result = self.results[num]
         opres = self.create_op(opnum, result, args, descr)
-        opres._str = res
+        self.setstr(opres, res)
         self.vars[res] = opres
         return opres
 
+    def setstr(self, op, text):
+        op._str = text
+        try:
+            num = int(text[1:])
+        except ValueError:
+            num = -1
+        if num >= 0:
+            op._varindex = num
+        self.allops.append(op)
+
     def parse_op_no_result(self, line):
         opnum, args, descr = self.parse_op(line)
         res = self.create_op(opnum, None, args, descr)
         num, ops, last_offset = self.parse_ops(base_indent, newlines, 0)
         if num < len(newlines):
             raise ParseError("unexpected dedent at line: %s" % newlines[num])
+        assign_all_varindices(self.allops)
         loop = self.model.ExtendedTreeLoop("loop")
         loop.comment = first_comment
         loop.original_jitcell_token = self.original_jitcell_token
         loop.operations = ops
         loop.inputargs = inpargs
         loop.last_offset = last_offset
-        loop.text2op = self.vars
         return loop
 
     def parse_ops(self, indent, lines, start):
 
 def parse(input, cpu=None, namespace=DEFAULT, type_system='lltype',
           invent_fail_descr=True, OpParser=OpParser,
-          results=None, mutable=False, oldvars={}):
+          results=None, mutable=False):
     if namespace is DEFAULT:
         namespace = {}
     return OpParser(input, cpu, namespace, type_system,
-                    invent_fail_descr, results, mutable, oldvars).parse()
+                    invent_fail_descr, results, mutable).parse()
 
 def pure_parse(*args, **kwds):
     kwds['invent_fail_descr'] = False
     return parse(*args, **kwds)
+
+
+def assign_all_varindices(allops):
+    assigned_varindices = {}
+    unassigned_varindices = []
+    #
+    for op in allops:
+        if getattr(op, '_varindex', -1) >= 0:
+            num = op._varindex
+            assert num not in assigned_varindices, (
+                "duplicate or overlapping var: %s and %s" % (
+                op, assigned_varindices[num]))
+            assigned_varindices[num] = op
+            if op.type == FLOAT:
+                assert (num + 1) not in assigned_varindices, (
+                    "duplicate or overlapping var: %s and %s" % (
+                    op, assigned_varindices[num + 1]))
+                assigned_varindices[num + 1] = op
+        else:
+            unassigned_varindices.append(op)
+    #
+    nextindex = 0
+    for op in unassigned_varindices:
+        while (nextindex in assigned_varindices or
+               (op.type == FLOAT and (nextindex + 1) in assigned_varindices)):
+            nextindex += 1
+        op._varindex = nextindex
+        nextindex += 1
+        if op.type == FLOAT:
+            nextindex += 1

File pypy/jit/tool/test/test_oparser.py

 import sys
 from pypy.rpython.lltypesystem import lltype, llmemory
 
-from pypy.jit.tool.oparser import parse, OpParser
-from pypy.jit.metainterp.resoperation import rop
+from pypy.jit.tool.oparser import parse, OpParser, assign_all_varindices
+from pypy.jit.metainterp.resoperation import rop, INT, FLOAT
 from pypy.jit.metainterp.history import AbstractDescr, JitCellToken,\
      TargetToken
 
         for modname, mod in sys.modules.iteritems():
             if isinstance(mod, ForbiddenModule):
                 sys.modules[modname] = mod.old_mod
+
+
+def test_assign_all_varindices():
+    class FakeOp:
+        def __init__(self, varindex=-1, type=INT):
+            self._varindex = varindex
+            self.type = type
+    def indices(lst):
+        return [op._varindex for op in lst]
+
+    ops = [FakeOp(5), FakeOp(6)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [5, 6]
+
+    ops = [FakeOp(5), FakeOp(6, FLOAT)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [5, 6]
+
+    ops = [FakeOp(5), FakeOp(5)]
+    py.test.raises(AssertionError, assign_all_varindices, ops)
+    ops = [FakeOp(5), FakeOp(5, FLOAT)]
+    py.test.raises(AssertionError, assign_all_varindices, ops)
+    ops = [FakeOp(5), FakeOp(4, FLOAT)]
+    py.test.raises(AssertionError, assign_all_varindices, ops)
+    ops = [FakeOp(4, FLOAT), FakeOp(5)]
+    py.test.raises(AssertionError, assign_all_varindices, ops)
+
+    ops = [FakeOp(), FakeOp(type=FLOAT), FakeOp(1)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [0, 2, 1]
+
+    ops = [FakeOp(), FakeOp(type=FLOAT), FakeOp(2)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [0, 3, 2]
+
+    ops = [FakeOp(), FakeOp(type=FLOAT), FakeOp(3)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [0, 1, 3]
+
+    ops = [FakeOp(), FakeOp(type=FLOAT), FakeOp(2, FLOAT)]
+    assign_all_varindices(ops)
+    assert indices(ops) == [0, 4, 2]