[pypy-svn] r77376 - in pypy/branch/jit-str/pypy/jit: codewriter codewriter/test metainterp metainterp/optimizeopt metainterp/test
arigo at codespeak.net
arigo at codespeak.net
Sun Sep 26 13:56:58 CEST 2010
Author: arigo
Date: Sun Sep 26 13:56:56 2010
New Revision: 77376
Added:
pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py (contents, props changed)
Modified:
pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py
pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py
pypy/branch/jit-str/pypy/jit/codewriter/support.py
pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py
pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py
pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py
pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py
pypy/branch/jit-str/pypy/jit/metainterp/resume.py
pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py
pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py
pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py
pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py
pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py
Log:
- Implement a number of variants of string equality,
and pick the best one in optimizeopt.
- Refactor the string-optimization code in its own
file, string.py.
- Change in resume.py: the nice two-steps building of
objects -- first allocate them all, then fill them
all -- no longer works with strings, because we need
to build the strings in a specific order (e.g. a
string concatenation needs to know the two concatenated
strings' lengths before it can even allocate the result).
Rely on recursion instead.
Modified: pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py (original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/effectinfo.py Sun Sep 26 13:56:56 2010
@@ -19,9 +19,18 @@
OS_NONE = 0 # normal case, no oopspec
OS_ARRAYCOPY = 1 # "list.ll_arraycopy"
OS_STR_CONCAT = 2 # "stroruni.concat"
- OS_STR_SLICE = 3 # "stroruni.slice"
- OS_UNI_CONCAT = 4 # "stroruni.concat"
+ OS_UNI_CONCAT = 3 # "stroruni.concat"
+ OS_STR_SLICE = 4 # "stroruni.slice"
OS_UNI_SLICE = 5 # "stroruni.slice"
+ OS_STR_EQUAL = 6 # "stroruni.equal"
+ OS_UNI_EQUAL = 7 # "stroruni.equal"
+ OS_STREQ_SLICE_CHECKNULL = 8 # s2!=NULL and s1[x:x+length]==s2
+ OS_STREQ_SLICE_NONNULL = 9 # s1[x:x+length]==s2 (assert s2!=NULL)
+ OS_STREQ_SLICE_CHAR = 10 # s1[x:x+length]==char
+ OS_STREQ_NONNULL = 11 # s1 == s2 (assert s1!=NULL,s2!=NULL)
+ OS_STREQ_NONNULL_CHAR = 12 # s1 == char (assert s1!=NULL)
+ OS_STREQ_CHECKNULL_CHAR = 13 # s1!=NULL and s1==char
+ OS_STREQ_LENGTHOK = 14 # s1 == s2 (assert len(s1)==len(s2))
def __new__(cls, readonly_descrs_fields,
write_descrs_fields, write_descrs_arrays,
@@ -135,11 +144,18 @@
assert calldescr is not None
return calldescr, func
-def funcptr_for_oopspec(oopspecindex):
- """A memo function that returns a pointer to the function described
- by OS_XYZ (as a real low-level function pointer)."""
+
+def _funcptr_for_oopspec_memo(oopspecindex):
from pypy.jit.codewriter import heaptracker
_, func_as_int = _callinfo_for_oopspec.get(oopspecindex, (None, 0))
funcadr = heaptracker.int2adr(func_as_int)
return funcadr.ptr
-funcptr_for_oopspec._annspecialcase_ = 'specialize:memo'
+_funcptr_for_oopspec_memo._annspecialcase_ = 'specialize:memo'
+
+def funcptr_for_oopspec(oopspecindex):
+ """A memo function that returns a pointer to the function described
+ by OS_XYZ (as a real low-level function pointer)."""
+ funcptr = _funcptr_for_oopspec_memo(oopspecindex)
+ assert funcptr
+ return funcptr
+funcptr_for_oopspec._annspecialcase_ = 'specialize:arg(0)'
Modified: pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py (original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/jtransform.py Sun Sep 26 13:56:56 2010
@@ -12,6 +12,7 @@
from pypy.rlib import objectmodel
from pypy.rlib.jit import _we_are_jitted
from pypy.translator.simplify import get_funcobj
+from pypy.translator.unsimplify import varoftype
def transform_graph(graph, cpu=None, callcontrol=None, portal_jd=None):
@@ -1042,15 +1043,70 @@
op1 = [op1, SpaceOperation('-live-', [], None)]
return op1
+ def _register_extra_helper(self, oopspecindex, oopspec_name,
+ argtypes, resulttype):
+ # a bit hackish
+ if oopspecindex in _callinfo_for_oopspec:
+ return
+ c_func, TP = support.builtin_func_for_spec(self.cpu.rtyper,
+ oopspec_name, argtypes,
+ resulttype)
+ op = SpaceOperation('pseudo_call',
+ [c_func] + [varoftype(T) for T in argtypes],
+ varoftype(resulttype))
+ calldescr = self.callcontrol.getcalldescr(op, oopspecindex)
+ _callinfo_for_oopspec[oopspecindex] = calldescr, c_func.value
+
def _handle_stroruni_call(self, op, oopspec_name, args):
if args[0].concretetype.TO == rstr.STR:
dict = {"stroruni.concat": EffectInfo.OS_STR_CONCAT,
- "stroruni.slice": EffectInfo.OS_STR_SLICE}
+ "stroruni.slice": EffectInfo.OS_STR_SLICE,
+ "stroruni.equal": EffectInfo.OS_STR_EQUAL,
+ }
elif args[0].concretetype.TO == rstr.UNICODE:
dict = {"stroruni.concat": EffectInfo.OS_UNI_CONCAT,
- "stroruni.slice": EffectInfo.OS_UNI_SLICE}
+ "stroruni.slice": EffectInfo.OS_UNI_SLICE,
+ "stroruni.equal": EffectInfo.OS_UNI_EQUAL,
+ }
else:
assert 0, "args[0].concretetype must be STR or UNICODE"
+ #
+ if oopspec_name == "stroruni.equal":
+ SoU = args[0].concretetype # Ptr(STR) or Ptr(UNICODE)
+ for otherindex, othername, argtypes, resulttype in [
+
+ (EffectInfo.OS_STREQ_SLICE_CHECKNULL,
+ "str.eq_slice_checknull",
+ [SoU, lltype.Signed, lltype.Signed, SoU],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_SLICE_NONNULL,
+ "str.eq_slice_nonnull",
+ [SoU, lltype.Signed, lltype.Signed, SoU],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_SLICE_CHAR,
+ "str.eq_slice_char",
+ [SoU, lltype.Signed, lltype.Signed, lltype.Char],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_NONNULL,
+ "str.eq_nonnull",
+ [SoU, SoU],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_NONNULL_CHAR,
+ "str.eq_nonnull_char",
+ [SoU, lltype.Char],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_CHECKNULL_CHAR,
+ "str.eq_checknull_char",
+ [SoU, lltype.Char],
+ lltype.Signed),
+ (EffectInfo.OS_STREQ_LENGTHOK,
+ "str.eq_lengthok",
+ [SoU, SoU],
+ lltype.Signed),
+ ]:
+ self._register_extra_helper(otherindex, othername,
+ argtypes, resulttype)
+ #
return self._handle_oopspec_call(op, args, dict[oopspec_name])
# ----------
Modified: pypy/branch/jit-str/pypy/jit/codewriter/support.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/support.py (original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/support.py Sun Sep 26 13:56:56 2010
@@ -277,6 +277,85 @@
_ll_1_str_str2unicode = ll_rstr.LLHelpers.ll_str2unicode
+ def _ll_4_str_eq_slice_checknull(s1, start, length, s2):
+ """str1[start : start + length] == str2."""
+ if not s2:
+ return 0
+ chars2 = s2.chars
+ if len(chars2) != length:
+ return 0
+ j = 0
+ chars1 = s1.chars
+ while j < length:
+ if chars1[start + j] != chars2[j]:
+ return 0
+ j += 1
+ return 1
+
+ def _ll_4_str_eq_slice_nonnull(s1, start, length, s2):
+ """str1[start : start + length] == str2, assuming str2 != NULL."""
+ chars2 = s2.chars
+ if len(chars2) != length:
+ return 0
+ j = 0
+ chars1 = s1.chars
+ while j < length:
+ if chars1[start + j] != chars2[j]:
+ return 0
+ j += 1
+ return 1
+
+ def _ll_4_str_eq_slice_char(s1, start, length, c2):
+ """str1[start : start + length] == c2."""
+ if length != 1:
+ return 0
+ if s1.chars[start] != c2:
+ return 0
+ return 1
+
+ def _ll_2_str_eq_nonnull(s1, s2):
+ len1 = len(s1.chars)
+ len2 = len(s2.chars)
+ if len1 != len2:
+ return 0
+ j = 0
+ chars1 = s1.chars
+ chars2 = s2.chars
+ while j < len1:
+ if chars1[j] != chars2[j]:
+ return 0
+ j += 1
+ return 1
+
+ def _ll_2_str_eq_nonnull_char(s1, c2):
+ chars = s1.chars
+ if len(chars) != 1:
+ return 0
+ if chars[0] != c2:
+ return 0
+ return 1
+
+ def _ll_2_str_eq_checknull_char(s1, c2):
+ if not s1:
+ return 0
+ chars = s1.chars
+ if len(chars) != 1:
+ return 0
+ if chars[0] != c2:
+ return 0
+ return 1
+
+ def _ll_2_str_eq_lengthok(s1, s2):
+ j = 0
+ chars1 = s1.chars
+ chars2 = s2.chars
+ len1 = len(chars1)
+ while j < len1:
+ if chars1[j] != chars2[j]:
+ return 0
+ j += 1
+ return 1
+
# ---------- malloc with del ----------
def _ll_2_raw_malloc(TP, size):
Modified: pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py (original)
+++ pypy/branch/jit-str/pypy/jit/codewriter/test/test_support.py Sun Sep 26 13:56:56 2010
@@ -1,7 +1,8 @@
import py
from pypy.rpython.lltypesystem import lltype
+from pypy.rpython.annlowlevel import llstr
from pypy.objspace.flow.model import Variable, Constant, SpaceOperation
-from pypy.jit.codewriter.support import decode_builtin_call
+from pypy.jit.codewriter.support import decode_builtin_call, LLtypeHelpers
def newconst(x):
return Constant(x, lltype.typeOf(x))
@@ -65,3 +66,70 @@
assert opargs == [newconst(myarray), newconst(2), vc, vi]
#impl = runner.get_oopspec_impl('spam.foobar', lltype.Ptr(A))
#assert impl(myarray, 2, 'A', 5) == 42 * ord('A')
+
+def test_streq_slice_checknull():
+ p1 = llstr("hello world")
+ p2 = llstr("wor")
+ func = LLtypeHelpers._ll_4_str_eq_slice_checknull.im_func
+ assert func(p1, 6, 3, p2) == True
+ assert func(p1, 6, 2, p2) == False
+ assert func(p1, 5, 3, p2) == False
+ assert func(p1, 2, 1, llstr(None)) == False
+
+def test_streq_slice_nonnull():
+ p1 = llstr("hello world")
+ p2 = llstr("wor")
+ func = LLtypeHelpers._ll_4_str_eq_slice_nonnull.im_func
+ assert func(p1, 6, 3, p2) == True
+ assert func(p1, 6, 2, p2) == False
+ assert func(p1, 5, 3, p2) == False
+ py.test.raises(AttributeError, func, p1, 2, 1, llstr(None))
+
+def test_streq_slice_char():
+ p1 = llstr("hello world")
+ func = LLtypeHelpers._ll_4_str_eq_slice_char.im_func
+ assert func(p1, 6, 3, "w") == False
+ assert func(p1, 6, 0, "w") == False
+ assert func(p1, 6, 1, "w") == True
+ assert func(p1, 6, 1, "x") == False
+
+def test_streq_nonnull():
+ p1 = llstr("wor")
+ p2 = llstr("wor")
+ assert p1 != p2
+ func = LLtypeHelpers._ll_2_str_eq_nonnull.im_func
+ assert func(p1, p1) == True
+ assert func(p1, p2) == True
+ assert func(p1, llstr("wrl")) == False
+ assert func(p1, llstr("world")) == False
+ assert func(p1, llstr("w")) == False
+ py.test.raises(AttributeError, func, p1, llstr(None))
+ py.test.raises(AttributeError, func, llstr(None), p2)
+
+def test_streq_nonnull_char():
+ func = LLtypeHelpers._ll_2_str_eq_nonnull_char.im_func
+ assert func(llstr("wor"), "x") == False
+ assert func(llstr("w"), "x") == False
+ assert func(llstr(""), "x") == False
+ assert func(llstr("x"), "x") == True
+ py.test.raises(AttributeError, func, llstr(None), "x")
+
+def test_streq_checknull_char():
+ func = LLtypeHelpers._ll_2_str_eq_checknull_char.im_func
+ assert func(llstr("wor"), "x") == False
+ assert func(llstr("w"), "x") == False
+ assert func(llstr(""), "x") == False
+ assert func(llstr("x"), "x") == True
+ assert func(llstr(None), "x") == False
+
+def test_streq_lengthok():
+ p1 = llstr("wor")
+ p2 = llstr("wor")
+ assert p1 != p2
+ func = LLtypeHelpers._ll_2_str_eq_lengthok.im_func
+ assert func(p1, p1) == True
+ assert func(p1, p2) == True
+ assert func(p1, llstr("wrl")) == False
+ py.test.raises(IndexError, func, p1, llstr("w"))
+ py.test.raises(AttributeError, func, p1, llstr(None))
+ py.test.raises(AttributeError, func, llstr(None), p2)
Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/__init__.py Sun Sep 26 13:56:56 2010
@@ -3,6 +3,7 @@
from pypy.jit.metainterp.optimizeopt.intbounds import OptIntBounds
from pypy.jit.metainterp.optimizeopt.virtualize import OptVirtualize
from pypy.jit.metainterp.optimizeopt.heap import OptHeap
+from pypy.jit.metainterp.optimizeopt.string import OptString
def optimize_loop_1(metainterp_sd, loop, virtuals=True):
"""Optimize loop.operations to make it match the input of loop.specnodes
@@ -13,6 +14,7 @@
optimizations = [OptIntBounds(),
OptRewrite(),
OptVirtualize(),
+ OptString(),
OptHeap(),
]
optimizer = Optimizer(metainterp_sd, loop, optimizations, virtuals)
@@ -23,4 +25,3 @@
expect 'specnodes' on the bridge.
"""
optimize_loop_1(metainterp_sd, bridge, False)
-
Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/optimizer.py Sun Sep 26 13:56:56 2010
@@ -9,10 +9,10 @@
from pypy.jit.metainterp.optimizeutil import InvalidLoop, args_dict
from pypy.jit.metainterp import resume, compile
from pypy.jit.metainterp.typesystem import llhelper, oohelper
-from pypy.rpython.lltypesystem import lltype, rstr
+from pypy.rpython.lltypesystem import lltype
from pypy.jit.metainterp.history import AbstractDescr, make_hashable_int
from pypy.jit.metainterp.optimizeopt.intutils import IntBound, IntUnbounded
-from pypy.rpython import annlowlevel
+from pypy.tool.pairtype import extendabletype
LEVEL_UNKNOWN = '\x00'
LEVEL_NONNULL = '\x01'
@@ -24,6 +24,7 @@
MININT = -sys.maxint - 1
class OptValue(object):
+ __metaclass__ = extendabletype
_attrs_ = ('box', 'known_class', 'last_guard_index', 'level', 'intbound')
last_guard_index = -1
@@ -127,27 +128,6 @@
def setitem(self, index, value):
raise NotImplementedError
- def getstrlen(self, newoperations):
- s = self.get_constant_string()
- if s is not None:
- return ConstInt(len(s))
- else:
- self.ensure_nonnull()
- box = self.force_box()
- lengthbox = BoxInt()
- newoperations.append(ResOperation(rop.STRLEN, [box], lengthbox))
- return lengthbox
-
- def get_constant_string(self):
- if self.is_constant():
- s = self.box.getref(lltype.Ptr(rstr.STR))
- return annlowlevel.hlstr(s)
- else:
- return None
-
- def string_copy_parts(self, *args):
- from pypy.jit.metainterp.optimizeopt import virtualize
- return virtualize.default_string_copy_parts(self, *args)
class ConstantValue(OptValue):
def __init__(self, box):
@@ -273,6 +253,7 @@
return None
def make_equal_to(self, box, value):
+ assert isinstance(value, OptValue)
assert box not in self.values
self.values[box] = value
Added: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py
==============================================================================
--- (empty file)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/string.py Sun Sep 26 13:56:56 2010
@@ -0,0 +1,536 @@
+from pypy.rpython.lltypesystem import lltype, rstr, llmemory
+from pypy.rpython import annlowlevel
+from pypy.jit.metainterp.history import Box, BoxInt, BoxPtr
+from pypy.jit.metainterp.history import Const, ConstInt, ConstPtr
+from pypy.jit.metainterp.history import get_const_ptr_for_string
+from pypy.jit.metainterp.resoperation import rop, ResOperation
+from pypy.jit.metainterp.optimizeopt import optimizer, virtualize
+from pypy.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
+from pypy.jit.metainterp.optimizeopt.optimizer import llhelper
+from pypy.jit.metainterp.optimizeutil import _findall
+from pypy.jit.codewriter.effectinfo import EffectInfo, callinfo_for_oopspec
+from pypy.jit.codewriter import heaptracker
+from pypy.rlib.unroll import unrolling_iterable
+from pypy.rlib.objectmodel import missing_value
+
+
+class __extend__(optimizer.OptValue):
+ """New methods added to the base class OptValue for this file."""
+
+ def getstrlen(self, newoperations):
+ s = self.get_constant_string()
+ if s is not None:
+ return ConstInt(len(s))
+ else:
+ if newoperations is None:
+ return None
+ self.ensure_nonnull()
+ box = self.force_box()
+ lengthbox = BoxInt()
+ newoperations.append(ResOperation(rop.STRLEN, [box], lengthbox))
+ return lengthbox
+
+ def get_constant_string(self):
+ if self.is_constant():
+ s = self.box.getref(lltype.Ptr(rstr.STR))
+ return annlowlevel.hlstr(s)
+ else:
+ return None
+
+ def string_copy_parts(self, newoperations, targetbox, offsetbox):
+ # Copies the pointer-to-string 'self' into the target string
+ # given by 'targetbox', at the specified offset. Returns the offset
+ # at the end of the copy.
+ lengthbox = self.getstrlen(newoperations)
+ srcbox = self.force_box()
+ return copy_str_content(newoperations, srcbox, targetbox,
+ CONST_0, offsetbox, lengthbox)
+
+
+class VAbstractStringValue(virtualize.AbstractVirtualValue):
+
+ def _really_force(self):
+ s = self.get_constant_string()
+ if s is not None:
+ c_s = get_const_ptr_for_string(s)
+ self.make_constant(c_s)
+ return
+ assert self.source_op is not None
+ self.box = box = self.source_op.result
+ newoperations = self.optimizer.newoperations
+ lengthbox = self.getstrlen(newoperations)
+ newoperations.append(ResOperation(rop.NEWSTR, [lengthbox], box))
+ self.string_copy_parts(newoperations, box, CONST_0)
+
+
+class VStringPlainValue(VAbstractStringValue):
+ """A string built with newstr(const)."""
+ _lengthbox = None # cache only
+
+ def setup(self, size):
+ self._chars = [optimizer.CVAL_UNINITIALIZED_ZERO] * size
+
+ def setup_slice(self, longerlist, start, stop):
+ assert 0 <= start <= stop <= len(longerlist)
+ self._chars = longerlist[start:stop]
+
+ def getstrlen(self, _):
+ if self._lengthbox is None:
+ self._lengthbox = ConstInt(len(self._chars))
+ return self._lengthbox
+
+ def getitem(self, index):
+ return self._chars[index]
+
+ def setitem(self, index, charvalue):
+ assert isinstance(charvalue, optimizer.OptValue)
+ self._chars[index] = charvalue
+
+ def get_constant_string(self):
+ for c in self._chars:
+ if c is optimizer.CVAL_UNINITIALIZED_ZERO or not c.is_constant():
+ return None
+ return ''.join([chr(c.box.getint()) for c in self._chars])
+
+ def string_copy_parts(self, newoperations, targetbox, offsetbox):
+ for i in range(len(self._chars)):
+ charbox = self._chars[i].force_box()
+ newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox], None))
+ offsetbox = _int_add(newoperations, offsetbox, CONST_1)
+ return offsetbox
+
+ def get_args_for_fail(self, modifier):
+ if self.box is None and not modifier.already_seen_virtual(self.keybox):
+ charboxes = [value.get_key_box() for value in self._chars]
+ modifier.register_virtual_fields(self.keybox, charboxes)
+ for value in self._chars:
+ value.get_args_for_fail(modifier)
+
+ def _make_virtual(self, modifier):
+ return modifier.make_vstrplain()
+
+
+class VStringConcatValue(VAbstractStringValue):
+ """The concatenation of two other strings."""
+
+ def setup(self, left, right, lengthbox):
+ self.left = left
+ self.right = right
+ self.lengthbox = lengthbox
+
+ def getstrlen(self, _):
+ return self.lengthbox
+
+ def get_constant_string(self):
+ s1 = self.left.get_constant_string()
+ if s1 is None:
+ return None
+ s2 = self.right.get_constant_string()
+ if s2 is None:
+ return None
+ return s1 + s2
+
+ def string_copy_parts(self, newoperations, targetbox, offsetbox):
+ offsetbox = self.left.string_copy_parts(newoperations, targetbox,
+ offsetbox)
+ offsetbox = self.right.string_copy_parts(newoperations, targetbox,
+ offsetbox)
+ return offsetbox
+
+ def get_args_for_fail(self, modifier):
+ if self.box is None and not modifier.already_seen_virtual(self.keybox):
+ # we don't store the lengthvalue in guards, because the
+ # guard-failed code starts with a regular STR_CONCAT again
+ leftbox = self.left.get_key_box()
+ rightbox = self.right.get_key_box()
+ modifier.register_virtual_fields(self.keybox, [leftbox, rightbox])
+ self.left.get_args_for_fail(modifier)
+ self.right.get_args_for_fail(modifier)
+
+ def _make_virtual(self, modifier):
+ return modifier.make_vstrconcat()
+
+
+class VStringSliceValue(VAbstractStringValue):
+ """A slice."""
+ vstr = vstart = vlength = missing_value # annotator fix
+
+ def setup(self, vstr, vstart, vlength):
+ self.vstr = vstr
+ self.vstart = vstart
+ self.vlength = vlength
+
+ def getstrlen(self, _):
+ return self.vlength.force_box()
+
+ def get_constant_string(self):
+ if self.vstart.is_constant() and self.vlength.is_constant():
+ s1 = self.vstr.get_constant_string()
+ if s1 is None:
+ return None
+ start = self.vstart.box.getint()
+ length = self.vlength.box.getint()
+ return s1[start : start + length]
+ return None
+
+ def string_copy_parts(self, newoperations, targetbox, offsetbox):
+ lengthbox = self.getstrlen(newoperations)
+ return copy_str_content(newoperations,
+ self.vstr.force_box(), targetbox,
+ self.vstart.force_box(), offsetbox,
+ lengthbox)
+
+ def get_args_for_fail(self, modifier):
+ if self.box is None and not modifier.already_seen_virtual(self.keybox):
+ boxes = [self.vstr.get_key_box(),
+ self.vstart.get_key_box(),
+ self.vlength.get_key_box()]
+ modifier.register_virtual_fields(self.keybox, boxes)
+ self.vstr.get_args_for_fail(modifier)
+ self.vstart.get_args_for_fail(modifier)
+ self.vlength.get_args_for_fail(modifier)
+
+ def _make_virtual(self, modifier):
+ return modifier.make_vstrslice()
+
+
+def copy_str_content(newoperations, srcbox, targetbox,
+ srcoffsetbox, offsetbox, lengthbox):
+ if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
+ M = 5
+ else:
+ M = 2
+ if isinstance(lengthbox, ConstInt) and lengthbox.value <= M:
+ # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
+ # instead of just a COPYSTRCONTENT.
+ for i in range(lengthbox.value):
+ charbox = _strgetitem(newoperations, srcbox, srcoffsetbox)
+ srcoffsetbox = _int_add(newoperations, srcoffsetbox, CONST_1)
+ newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
+ offsetbox,
+ charbox], None))
+ offsetbox = _int_add(newoperations, offsetbox, CONST_1)
+ else:
+ nextoffsetbox = _int_add(newoperations, offsetbox, lengthbox)
+ op = ResOperation(rop.COPYSTRCONTENT, [srcbox, targetbox,
+ srcoffsetbox, offsetbox,
+ lengthbox], None)
+ newoperations.append(op)
+ offsetbox = nextoffsetbox
+ return offsetbox
+
+def _int_add(newoperations, box1, box2):
+ if isinstance(box1, ConstInt):
+ if box1.value == 0:
+ return box2
+ if isinstance(box2, ConstInt):
+ return ConstInt(box1.value + box2.value)
+ elif isinstance(box2, ConstInt) and box2.value == 0:
+ return box1
+ resbox = BoxInt()
+ newoperations.append(ResOperation(rop.INT_ADD, [box1, box2], resbox))
+ return resbox
+
+def _int_sub(newoperations, box1, box2):
+ if isinstance(box2, ConstInt):
+ if box2.value == 0:
+ return box1
+ if isinstance(box1, ConstInt):
+ return ConstInt(box1.value - box2.value)
+ resbox = BoxInt()
+ newoperations.append(ResOperation(rop.INT_SUB, [box1, box2], resbox))
+ return resbox
+
+def _strgetitem(newoperations, strbox, indexbox):
+ if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
+ s = strbox.getref(lltype.Ptr(rstr.STR))
+ return ConstInt(ord(s.chars[indexbox.getint()]))
+ resbox = BoxInt()
+ newoperations.append(ResOperation(rop.STRGETITEM, [strbox, indexbox],
+ resbox))
+ return resbox
+
+
+class OptString(optimizer.Optimization):
+ "Handling of strings and unicodes."
+
+ def make_vstring_plain(self, box, source_op=None):
+ vvalue = VStringPlainValue(self.optimizer, box, source_op)
+ self.make_equal_to(box, vvalue)
+ return vvalue
+
+ def make_vstring_concat(self, box, source_op=None):
+ vvalue = VStringConcatValue(self.optimizer, box, source_op)
+ self.make_equal_to(box, vvalue)
+ return vvalue
+
+ def make_vstring_slice(self, box, source_op=None):
+ vvalue = VStringSliceValue(self.optimizer, box, source_op)
+ self.make_equal_to(box, vvalue)
+ return vvalue
+
+ def optimize_CALL(self, op):
+ # dispatch based on 'oopspecindex' to a method that handles
+ # specifically the given oopspec call. For non-oopspec calls,
+ # oopspecindex is just zero.
+ effectinfo = op.descr.get_extra_info()
+ if effectinfo is not None:
+ oopspecindex = effectinfo.oopspecindex
+ for value, meth in opt_call_oopspec_ops:
+ if oopspecindex == value:
+ if meth(self, op):
+ return
+ self.emit_operation(op)
+
+ def opt_call_oopspec_ARRAYCOPY(self, op):
+ source_value = self.getvalue(op.args[1])
+ dest_value = self.getvalue(op.args[2])
+ source_start_box = self.get_constant_box(op.args[3])
+ dest_start_box = self.get_constant_box(op.args[4])
+ length = self.get_constant_box(op.args[5])
+ if (source_value.is_virtual() and source_start_box and dest_start_box
+ and length and dest_value.is_virtual()):
+ # XXX optimize the case where dest value is not virtual,
+ # but we still can avoid a mess
+ source_start = source_start_box.getint()
+ dest_start = dest_start_box.getint()
+ for index in range(length.getint()):
+ val = source_value.getitem(index + source_start)
+ dest_value.setitem(index + dest_start, val)
+ return True
+ if length and length.getint() == 0:
+ return True # 0-length arraycopy
+ return False
+
+ def optimize_NEWSTR(self, op):
+ length_box = self.get_constant_box(op.args[0])
+ if length_box:
+ # if the original 'op' did not have a ConstInt as argument,
+ # build a new one with the ConstInt argument
+ if not isinstance(op.args[0], ConstInt):
+ op = ResOperation(rop.NEWSTR, [length_box], op.result)
+ vvalue = self.make_vstring_plain(op.result, op)
+ vvalue.setup(length_box.getint())
+ else:
+ self.getvalue(op.result).ensure_nonnull()
+ self.emit_operation(op)
+
+ def optimize_STRSETITEM(self, op):
+ value = self.getvalue(op.args[0])
+ if value.is_virtual() and isinstance(value, VStringPlainValue):
+ indexbox = self.get_constant_box(op.args[1])
+ if indexbox is not None:
+ value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
+ return
+ value.ensure_nonnull()
+ self.emit_operation(op)
+
+ def optimize_STRGETITEM(self, op):
+ value = self.getvalue(op.args[0])
+ vindex = self.getvalue(op.args[1])
+ vresult = self.strgetitem(value, vindex)
+ self.make_equal_to(op.result, vresult)
+
+ def strgetitem(self, value, vindex):
+ value.ensure_nonnull()
+ #
+ if value.is_virtual() and isinstance(value, VStringSliceValue):
+ fullindexbox = _int_add(self.optimizer.newoperations,
+ value.vstart.force_box(),
+ vindex.force_box())
+ value = value.vstr
+ vindex = self.getvalue(fullindexbox)
+ #
+ if isinstance(value, VStringPlainValue): # even if no longer virtual
+ if vindex.is_constant():
+ return value.getitem(vindex.box.getint())
+ #
+ resbox = _strgetitem(self.optimizer.newoperations,
+ value.force_box(),vindex.force_box())
+ return self.getvalue(resbox)
+
+ def optimize_STRLEN(self, op):
+ value = self.getvalue(op.args[0])
+ lengthbox = value.getstrlen(self.optimizer.newoperations)
+ self.make_equal_to(op.result, self.getvalue(lengthbox))
+
+ def opt_call_oopspec_STR_CONCAT(self, op):
+ vleft = self.getvalue(op.args[1])
+ vright = self.getvalue(op.args[2])
+ vleft.ensure_nonnull()
+ vright.ensure_nonnull()
+ newoperations = self.optimizer.newoperations
+ len1box = vleft.getstrlen(newoperations)
+ len2box = vright.getstrlen(newoperations)
+ lengthbox = _int_add(newoperations, len1box, len2box)
+ value = self.make_vstring_concat(op.result, op)
+ value.setup(vleft, vright, lengthbox)
+ return True
+
+ def opt_call_oopspec_STR_SLICE(self, op):
+ newoperations = self.optimizer.newoperations
+ vstr = self.getvalue(op.args[1])
+ vstart = self.getvalue(op.args[2])
+ vstop = self.getvalue(op.args[3])
+ #
+ if (isinstance(vstr, VStringPlainValue) and vstart.is_constant()
+ and vstop.is_constant()):
+ # slicing with constant bounds of a VStringPlainValue
+ value = self.make_vstring_plain(op.result, op)
+ value.setup_slice(vstr._chars, vstart.box.getint(),
+ vstop.box.getint())
+ return True
+ #
+ vstr.ensure_nonnull()
+ if isinstance(vstr, VStringSliceValue):
+ # double slicing s[i:j][k:l]
+ vintermediate = vstr
+ vstr = vintermediate.vstr
+ startbox = _int_add(newoperations,
+ vintermediate.vstart.force_box(),
+ vstart.force_box())
+ vstart = self.getvalue(startbox)
+ #
+ lengthbox = _int_sub(newoperations, vstop.force_box(),
+ vstart.force_box())
+ value = self.make_vstring_slice(op.result, op)
+ value.setup(vstr, vstart, self.getvalue(lengthbox))
+ return True
+
+ def opt_call_oopspec_STR_EQUAL(self, op):
+ v1 = self.getvalue(op.args[1])
+ v2 = self.getvalue(op.args[2])
+ #
+ l1box = v1.getstrlen(None)
+ l2box = v2.getstrlen(None)
+ if (l1box is not None and l2box is not None and
+ isinstance(l1box, ConstInt) and
+ isinstance(l2box, ConstInt) and
+ l1box.value != l2box.value):
+ # statically known to have a different length
+ self.make_constant(op.result, CONST_0)
+ return True
+ #
+ if self.handle_str_equal_level1(v1, v2, op.result):
+ return True
+ if self.handle_str_equal_level1(v2, v1, op.result):
+ return True
+ if self.handle_str_equal_level2(v1, v2, op.result):
+ return True
+ if self.handle_str_equal_level2(v2, v1, op.result):
+ return True
+ #
+ if v1.is_nonnull() and v2.is_nonnull():
+ if l1box is not None and l2box is not None and (
+ l1box == l2box or (isinstance(l1box, ConstInt) and
+ isinstance(l2box, ConstInt) and
+ l1box.value == l2box.value)):
+ do = EffectInfo.OS_STREQ_LENGTHOK
+ else:
+ do = EffectInfo.OS_STREQ_NONNULL
+ self.generate_modified_call(do, [v1.force_box(),
+ v2.force_box()], op.result)
+ return True
+ return False
+
+ def handle_str_equal_level1(self, v1, v2, resultbox):
+ l2box = v2.getstrlen(None)
+ if isinstance(l2box, ConstInt):
+ if l2box.value == 0:
+ lengthbox = v1.getstrlen(self.optimizer.newoperations)
+ seo = self.optimizer.send_extra_operation
+ seo(ResOperation(rop.INT_EQ, [lengthbox, CONST_0], resultbox))
+ return True
+ if l2box.value == 1:
+ l1box = v1.getstrlen(None)
+ if isinstance(l1box, ConstInt) and l1box.value == 1:
+ # comparing two single chars
+ vchar1 = self.strgetitem(v1, optimizer.CVAL_ZERO)
+ vchar2 = self.strgetitem(v2, optimizer.CVAL_ZERO)
+ seo = self.optimizer.send_extra_operation
+ seo(ResOperation(rop.INT_EQ, [vchar1.force_box(),
+ vchar2.force_box()],
+ resultbox))
+ return True
+ if isinstance(v1, VStringSliceValue):
+ vchar = self.strgetitem(v2, optimizer.CVAL_ZERO)
+ do = EffectInfo.OS_STREQ_SLICE_CHAR
+ self.generate_modified_call(do, [v1.vstr.force_box(),
+ v1.vstart.force_box(),
+ v1.vlength.force_box(),
+ vchar.force_box()],
+ resultbox)
+ return True
+ #
+ if v2.is_null():
+ if v1.is_nonnull():
+ self.make_constant(resultbox, CONST_0)
+ return True
+ if v1.is_null():
+ self.make_constant(resultbox, CONST_1)
+ return True
+ op = ResOperation(rop.PTR_EQ, [v1.force_box(),
+ llhelper.CONST_NULL],
+ resultbox)
+ self.optimizer.newoperations.append(op)
+ return True
+ #
+ return False
+
+ def handle_str_equal_level2(self, v1, v2, resultbox):
+ l2box = v2.getstrlen(None)
+ if isinstance(l2box, ConstInt):
+ if l2box.value == 1:
+ vchar = self.strgetitem(v2, optimizer.CVAL_ZERO)
+ if v1.is_nonnull():
+ do = EffectInfo.OS_STREQ_NONNULL_CHAR
+ else:
+ do = EffectInfo.OS_STREQ_CHECKNULL_CHAR
+ self.generate_modified_call(do, [v1.force_box(),
+ vchar.force_box()], resultbox)
+ return True
+ #
+ if v1.is_virtual() and isinstance(v1, VStringSliceValue):
+ if v2.is_nonnull():
+ do = EffectInfo.OS_STREQ_SLICE_NONNULL
+ else:
+ do = EffectInfo.OS_STREQ_SLICE_CHECKNULL
+ self.generate_modified_call(do, [v1.vstr.force_box(),
+ v1.vstart.force_box(),
+ v1.vlength.force_box(),
+ v2.force_box()], resultbox)
+ return True
+ return False
+
+ def generate_modified_call(self, oopspecindex, args, result):
+ calldescr, func = callinfo_for_oopspec(oopspecindex)
+ func = llmemory.cast_ptr_to_adr(func)
+ func = heaptracker.adr2int(func)
+ op = ResOperation(rop.CALL, [ConstInt(func)] + args, result,
+ descr=calldescr)
+ self.optimizer.newoperations.append(op)
+ generate_modified_call._annspecialcase_ = 'specialize:arg(1)'
+
+ def propagate_forward(self, op):
+ opnum = op.opnum
+ for value, func in optimize_ops:
+ if opnum == value:
+ func(self, op)
+ break
+ else:
+ self.emit_operation(op)
+
+optimize_ops = _findall(OptString, 'optimize_')
+
+def _findall_call_oopspec():
+ prefix = 'opt_call_oopspec_'
+ result = []
+ for name in dir(OptString):
+ if name.startswith(prefix):
+ value = getattr(EffectInfo, 'OS_' + name[len(prefix):])
+ assert isinstance(value, int) and value != 0
+ result.append((value, getattr(OptString, name)))
+ return unrolling_iterable(result)
+opt_call_oopspec_ops = _findall_call_oopspec()
Modified: pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/optimizeopt/virtualize.py Sun Sep 26 13:56:56 2010
@@ -5,11 +5,8 @@
from pypy.jit.metainterp.specnode import VirtualStructSpecNode
from pypy.jit.metainterp.resoperation import rop, ResOperation
from pypy.jit.metainterp.optimizeutil import _findall
-from pypy.jit.metainterp.history import get_const_ptr_for_string
from pypy.rlib.objectmodel import we_are_translated
from pypy.jit.metainterp.optimizeopt.optimizer import *
-from pypy.jit.codewriter.effectinfo import EffectInfo
-from pypy.rlib.unroll import unrolling_iterable
class AbstractVirtualValue(OptValue):
@@ -197,217 +194,6 @@
return modifier.make_varray(self.arraydescr)
-class VAbstractStringValue(AbstractVirtualValue):
-
- def _really_force(self):
- s = self.get_constant_string()
- if s is not None:
- c_s = get_const_ptr_for_string(s)
- self.make_constant(c_s)
- return
- assert self.source_op is not None
- self.box = box = self.source_op.result
- newoperations = self.optimizer.newoperations
- lengthbox = self.getstrlen(newoperations)
- newoperations.append(ResOperation(rop.NEWSTR, [lengthbox], box))
- self.string_copy_parts(newoperations, box, CONST_0)
-
-
-class VStringPlainValue(VAbstractStringValue):
- """A string built with newstr(const)."""
- _lengthbox = None # cache only
-
- def setup(self, size):
- self._chars = [CVAL_UNINITIALIZED_ZERO] * size
-
- def getstrlen(self, _):
- if self._lengthbox is None:
- self._lengthbox = ConstInt(len(self._chars))
- return self._lengthbox
-
- def getitem(self, index):
- return self._chars[index]
-
- def setitem(self, index, charvalue):
- assert isinstance(charvalue, OptValue)
- self._chars[index] = charvalue
-
- def get_constant_string(self):
- for c in self._chars:
- if c is CVAL_UNINITIALIZED_ZERO or not c.is_constant():
- return None
- return ''.join([chr(c.box.getint()) for c in self._chars])
-
- def string_copy_parts(self, newoperations, targetbox, offsetbox):
- for i in range(len(self._chars)):
- charbox = self._chars[i].force_box()
- newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
- offsetbox,
- charbox], None))
- offsetbox = _int_add(newoperations, offsetbox, CONST_1)
- return offsetbox
-
- def get_args_for_fail(self, modifier):
- if self.box is None and not modifier.already_seen_virtual(self.keybox):
- charboxes = [value.get_key_box() for value in self._chars]
- modifier.register_virtual_fields(self.keybox, charboxes)
- for value in self._chars:
- value.get_args_for_fail(modifier)
-
- def _make_virtual(self, modifier):
- return modifier.make_vstrplain()
-
-
-class VStringConcatValue(VAbstractStringValue):
- """The concatenation of two other strings."""
-
- def setup(self, left, right, lengthbox):
- self.left = left
- self.right = right
- self.lengthbox = lengthbox
-
- def getstrlen(self, _):
- return self.lengthbox
-
- def get_constant_string(self):
- s1 = self.left.get_constant_string()
- if s1 is None:
- return None
- s2 = self.right.get_constant_string()
- if s2 is None:
- return None
- return s1 + s2
-
- def string_copy_parts(self, newoperations, targetbox, offsetbox):
- offsetbox = self.left.string_copy_parts(newoperations, targetbox,
- offsetbox)
- offsetbox = self.right.string_copy_parts(newoperations, targetbox,
- offsetbox)
- return offsetbox
-
- def get_args_for_fail(self, modifier):
- if self.box is None and not modifier.already_seen_virtual(self.keybox):
- # we don't store the lengthvalue in guards, because the
- # guard-failed code starts with a regular STR_CONCAT again
- leftbox = self.left.get_key_box()
- rightbox = self.right.get_key_box()
- modifier.register_virtual_fields(self.keybox, [leftbox, rightbox])
- self.left.get_args_for_fail(modifier)
- self.right.get_args_for_fail(modifier)
-
- def _make_virtual(self, modifier):
- return modifier.make_vstrconcat()
-
-
-class VStringSliceValue(VAbstractStringValue):
- """A slice."""
-
- def setup(self, vstr, vstart, vlength):
- self.vstr = vstr
- self.vstart = vstart
- self.vlength = vlength
-
- def getstrlen(self, newoperations):
- return self.vlength.force_box()
-
- def get_constant_string(self):
- if self.vstart.is_constant() and self.vlength.is_constant():
- s1 = self.vstr.get_constant_string()
- if s1 is None:
- return None
- start = self.vstart.box.getint()
- length = self.vlength.box.getint()
- return s1[start : start + length]
- return None
-
- def string_copy_parts(self, newoperations, targetbox, offsetbox):
- lengthbox = self.getstrlen(newoperations)
- return copy_str_content(newoperations,
- self.vstr.force_box(), targetbox,
- self.vstart.force_box(), offsetbox,
- lengthbox)
-
- def get_args_for_fail(self, modifier):
- if self.box is None and not modifier.already_seen_virtual(self.keybox):
- boxes = [self.vstr.get_key_box(),
- self.vstart.get_key_box(),
- self.vlength.get_key_box()]
- modifier.register_virtual_fields(self.keybox, boxes)
- self.vstr.get_args_for_fail(modifier)
- self.vstart.get_args_for_fail(modifier)
- self.vlength.get_args_for_fail(modifier)
-
- def _make_virtual(self, modifier):
- return modifier.make_vstrslice()
-
-
-def default_string_copy_parts(srcvalue, newoperations, targetbox, offsetbox):
- # Copies the pointer-to-string 'srcvalue' into the target string
- # given by 'targetbox', at the specified offset. Returns the offset
- # at the end of the copy.
- lengthbox = srcvalue.getstrlen(newoperations)
- srcbox = srcvalue.force_box()
- return copy_str_content(newoperations, srcbox, targetbox,
- CONST_0, offsetbox, lengthbox)
-
-def copy_str_content(newoperations, srcbox, targetbox,
- srcoffsetbox, offsetbox, lengthbox):
- if isinstance(srcbox, ConstPtr) and isinstance(srcoffsetbox, Const):
- M = 5
- else:
- M = 2
- if isinstance(lengthbox, ConstInt) and lengthbox.value <= M:
- # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
- # instead of just a COPYSTRCONTENT.
- for i in range(lengthbox.value):
- charbox = _strgetitem(newoperations, srcbox, srcoffsetbox)
- srcoffsetbox = _int_add(newoperations, srcoffsetbox, CONST_1)
- newoperations.append(ResOperation(rop.STRSETITEM, [targetbox,
- offsetbox,
- charbox], None))
- offsetbox = _int_add(newoperations, offsetbox, CONST_1)
- else:
- nextoffsetbox = _int_add(newoperations, offsetbox, lengthbox)
- op = ResOperation(rop.COPYSTRCONTENT, [srcbox, targetbox,
- srcoffsetbox, offsetbox,
- lengthbox], None)
- newoperations.append(op)
- offsetbox = nextoffsetbox
- return offsetbox
-
-def _int_add(newoperations, box1, box2):
- if isinstance(box1, ConstInt):
- if box1.value == 0:
- return box2
- if isinstance(box2, ConstInt):
- return ConstInt(box1.value + box2.value)
- elif isinstance(box2, ConstInt) and box2.value == 0:
- return box1
- resbox = BoxInt()
- newoperations.append(ResOperation(rop.INT_ADD, [box1, box2], resbox))
- return resbox
-
-def _int_sub(newoperations, box1, box2):
- if isinstance(box2, ConstInt):
- if box2.value == 0:
- return box1
- if isinstance(box1, ConstInt):
- return ConstInt(box1.value - box2.value)
- resbox = BoxInt()
- newoperations.append(ResOperation(rop.INT_SUB, [box1, box2], resbox))
- return resbox
-
-def _strgetitem(newoperations, strbox, indexbox):
- # hum, this repetition of the operations is not quite right
- if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
- s = strbox.getref(lltype.Ptr(rstr.STR))
- return ConstInt(ord(s.chars[indexbox.getint()]))
- resbox = BoxInt()
- newoperations.append(ResOperation(rop.STRGETITEM, [strbox, indexbox],
- resbox))
- return resbox
-
-
class __extend__(SpecNode):
def setup_virtual_node(self, optimizer, box, newinputargs):
raise NotImplementedError
@@ -496,21 +282,6 @@
self.make_equal_to(box, vvalue)
return vvalue
- def make_vstring_plain(self, box, source_op=None):
- vvalue = VStringPlainValue(self.optimizer, box, source_op)
- self.make_equal_to(box, vvalue)
- return vvalue
-
- def make_vstring_concat(self, box, source_op=None):
- vvalue = VStringConcatValue(self.optimizer, box, source_op)
- self.make_equal_to(box, vvalue)
- return vvalue
-
- def make_vstring_slice(self, box, source_op=None):
- vvalue = VStringSliceValue(self.optimizer, box, source_op)
- self.make_equal_to(box, vvalue)
- return vvalue
-
def optimize_JUMP(self, op):
orgop = self.optimizer.loop.operations[-1]
exitargs = []
@@ -649,113 +420,6 @@
###self.heap_op_optimizer.optimize_SETARRAYITEM_GC(op, value, fieldvalue)
self.emit_operation(op)
- def optimize_CALL(self, op):
- # dispatch based on 'oopspecindex' to a method that handles
- # specifically the given oopspec call. For non-oopspec calls,
- # oopspecindex is just zero.
- effectinfo = op.descr.get_extra_info()
- if effectinfo is not None:
- oopspecindex = effectinfo.oopspecindex
- for value, meth in opt_call_oopspec_ops:
- if oopspecindex == value:
- if meth(self, op):
- return
- self.emit_operation(op)
-
- def opt_call_oopspec_ARRAYCOPY(self, op):
- source_value = self.getvalue(op.args[1])
- dest_value = self.getvalue(op.args[2])
- source_start_box = self.get_constant_box(op.args[3])
- dest_start_box = self.get_constant_box(op.args[4])
- length = self.get_constant_box(op.args[5])
- if (source_value.is_virtual() and source_start_box and dest_start_box
- and length and dest_value.is_virtual()):
- # XXX optimize the case where dest value is not virtual,
- # but we still can avoid a mess
- source_start = source_start_box.getint()
- dest_start = dest_start_box.getint()
- for index in range(length.getint()):
- val = source_value.getitem(index + source_start)
- dest_value.setitem(index + dest_start, val)
- return True
- if length and length.getint() == 0:
- return True # 0-length arraycopy
- return False
-
- def optimize_NEWSTR(self, op):
- length_box = self.get_constant_box(op.args[0])
- if length_box:
- # if the original 'op' did not have a ConstInt as argument,
- # build a new one with the ConstInt argument
- if not isinstance(op.args[0], ConstInt):
- op = ResOperation(rop.NEWSTR, [length_box], op.result)
- vvalue = self.make_vstring_plain(op.result, op)
- vvalue.setup(length_box.getint())
- else:
- self.getvalue(op.result).ensure_nonnull()
- self.emit_operation(op)
-
- def optimize_STRSETITEM(self, op):
- value = self.getvalue(op.args[0])
- if value.is_virtual() and isinstance(value, VStringPlainValue):
- indexbox = self.get_constant_box(op.args[1])
- if indexbox is not None:
- value.setitem(indexbox.getint(), self.getvalue(op.args[2]))
- return
- value.ensure_nonnull()
- self.emit_operation(op)
-
- def optimize_STRGETITEM(self, op):
- value = self.getvalue(op.args[0])
- if isinstance(value, VStringPlainValue): # even if no longer virtual
- indexbox = self.get_constant_box(op.args[1])
- if indexbox is not None:
- charvalue = value.getitem(indexbox.getint())
- self.make_equal_to(op.result, charvalue)
- return
- value.ensure_nonnull()
- self.emit_operation(op)
-
- def optimize_STRLEN(self, op):
- value = self.getvalue(op.args[0])
- lengthbox = value.getstrlen(self.optimizer.newoperations)
- self.make_equal_to(op.result, self.getvalue(lengthbox))
-
- def opt_call_oopspec_STR_CONCAT(self, op):
- vleft = self.getvalue(op.args[1])
- vright = self.getvalue(op.args[2])
- vleft.ensure_nonnull()
- vright.ensure_nonnull()
- newoperations = self.optimizer.newoperations
- len1box = vleft.getstrlen(newoperations)
- len2box = vright.getstrlen(newoperations)
- lengthbox = _int_add(newoperations, len1box, len2box)
- value = self.make_vstring_concat(op.result, op)
- value.setup(vleft, vright, lengthbox)
- return True
-
- def opt_call_oopspec_STR_SLICE(self, op):
- newoperations = self.optimizer.newoperations
- vstr = self.getvalue(op.args[1])
- vstart = self.getvalue(op.args[2])
- vstop = self.getvalue(op.args[3])
- vstr.ensure_nonnull()
- lengthbox = _int_sub(newoperations, vstop.force_box(),
- vstart.force_box())
- value = self.make_vstring_slice(op.result, op)
- #
- if isinstance(vstr, VStringSliceValue):
- # double slicing s[i:j][k:l]
- vintermediate = vstr
- vstr = vintermediate.vstr
- startbox = _int_add(newoperations,
- vintermediate.vstart.force_box(),
- vstart.force_box())
- vstart = self.getvalue(startbox)
- #
- value.setup(vstr, vstart, self.getvalue(lengthbox))
- return True
-
def propagate_forward(self, op):
opnum = op.opnum
for value, func in optimize_ops:
@@ -766,14 +430,3 @@
self.emit_operation(op)
optimize_ops = _findall(OptVirtualize, 'optimize_')
-
-def _findall_call_oopspec():
- prefix = 'opt_call_oopspec_'
- result = []
- for name in dir(OptVirtualize):
- if name.startswith(prefix):
- value = getattr(EffectInfo, 'OS_' + name[len(prefix):])
- assert isinstance(value, int) and value != 0
- result.append((value, getattr(OptVirtualize, name)))
- return unrolling_iterable(result)
-opt_call_oopspec_ops = _findall_call_oopspec()
Modified: pypy/branch/jit-str/pypy/jit/metainterp/resume.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/resume.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/resume.py Sun Sep 26 13:56:56 2010
@@ -9,7 +9,7 @@
from pypy.rpython.lltypesystem import lltype, llmemory, rffi, rstr
from pypy.rlib import rarithmetic
from pypy.rlib.objectmodel import we_are_translated, specialize
-from pypy.rlib.debug import have_debug_prints
+from pypy.rlib.debug import have_debug_prints, ll_assert
from pypy.rlib.debug import debug_start, debug_stop, debug_print
# Logic to encode the chain of frames and the state of the boxes at a
@@ -408,9 +408,7 @@
class AbstractVirtualInfo(object):
- #def allocate(self, metainterp):
- # raise NotImplementedError
- #def setfields(self, decoder, struct):
+ #def allocate(self, decoder, index):
# raise NotImplementedError
def equals(self, fieldnums):
return tagged_list_eq(self.fieldnums, fieldnums)
@@ -430,6 +428,7 @@
for i in range(len(self.fielddescrs)):
descr = self.fielddescrs[i]
decoder.setfield(descr, struct, self.fieldnums[i])
+ return struct
def debug_prints(self):
assert len(self.fielddescrs) == len(self.fieldnums)
@@ -444,8 +443,10 @@
self.known_class = known_class
@specialize.argtype(1)
- def allocate(self, decoder):
- return decoder.allocate_with_vtable(self.known_class)
+ def allocate(self, decoder, index):
+ struct = decoder.allocate_with_vtable(self.known_class)
+ decoder.virtuals_cache[index] = struct
+ return self.setfields(decoder, struct)
def debug_prints(self):
debug_print("\tvirtualinfo", self.known_class.repr_rpython())
@@ -457,8 +458,10 @@
self.typedescr = typedescr
@specialize.argtype(1)
- def allocate(self, decoder):
- return decoder.allocate_struct(self.typedescr)
+ def allocate(self, decoder, index):
+ struct = decoder.allocate_struct(self.typedescr)
+ decoder.virtuals_cache[index] = struct
+ return self.setfields(decoder, struct)
def debug_prints(self):
debug_print("\tvstructinfo", self.typedescr.repr_rpython())
@@ -470,14 +473,11 @@
#self.fieldnums = ...
@specialize.argtype(1)
- def allocate(self, decoder):
+ def allocate(self, decoder, index):
length = len(self.fieldnums)
- return decoder.allocate_array(self.arraydescr, length)
-
- @specialize.argtype(1)
- def setfields(self, decoder, array):
arraydescr = self.arraydescr
- length = len(self.fieldnums)
+ array = decoder.allocate_array(arraydescr, length)
+ decoder.virtuals_cache[index] = array
# NB. the check for the kind of array elements is moved out of the loop
if arraydescr.is_array_of_pointers():
for i in range(length):
@@ -491,25 +491,25 @@
for i in range(length):
decoder.setarrayitem_int(arraydescr, array, i,
self.fieldnums[i])
+ return array
def debug_prints(self):
debug_print("\tvarrayinfo", self.arraydescr)
for i in self.fieldnums:
debug_print("\t\t", str(untag(i)))
+
class VStrPlainInfo(AbstractVirtualInfo):
"""Stands for the string made out of the characters of all fieldnums."""
@specialize.argtype(1)
- def allocate(self, decoder):
- length = len(self.fieldnums)
- return decoder.allocate_string(length)
-
- @specialize.argtype(1)
- def setfields(self, decoder, string):
+ def allocate(self, decoder, index):
length = len(self.fieldnums)
+ string = decoder.allocate_string(length)
+ decoder.virtuals_cache[index] = string
for i in range(length):
- decoder.strsetitem(string, i, self.fieldnums[i])
+ decoder.string_setitem(string, i, self.fieldnums[i])
+ return string
def debug_prints(self):
debug_print("\tvstrplaininfo length", len(self.fieldnums))
@@ -520,18 +520,14 @@
other strings."""
@specialize.argtype(1)
- def allocate(self, decoder):
+ def allocate(self, decoder, index):
# xxx for blackhole resuming, this will build all intermediate
# strings and throw them away immediately, which is a bit sub-
# efficient. Not sure we care.
left, right = self.fieldnums
- return decoder.concat_strings(left, right)
-
- @specialize.argtype(1)
- def setfields(self, decoder, string):
- # we do everything in allocate(); no risk of circular data structure
- # with strings.
- pass
+ string = decoder.concat_strings(left, right)
+ decoder.virtuals_cache[index] = string
+ return string
def debug_prints(self):
debug_print("\tvstrconcatinfo")
@@ -543,15 +539,11 @@
"""Stands for the string made out of slicing another string."""
@specialize.argtype(1)
- def allocate(self, decoder):
- str, start, length = self.fieldnums
- return decoder.slice_string(str, start, length)
-
- @specialize.argtype(1)
- def setfields(self, decoder, string):
- # we do everything in allocate(); no risk of circular data structure
- # with strings.
- pass
+ def allocate(self, decoder, index):
+ largerstr, start, length = self.fieldnums
+ string = decoder.slice_string(largerstr, start, length)
+ decoder.virtuals_cache[index] = string
+ return string
def debug_prints(self):
debug_print("\tvstrsliceinfo")
@@ -568,7 +560,8 @@
blackholing and want the best performance.
"""
_mixin_ = True
- virtuals = None
+ rd_virtuals = None
+ virtuals_cache = None
virtual_default = None
def _init(self, cpu, storage):
@@ -580,17 +573,29 @@
self._prepare_virtuals(storage.rd_virtuals)
self._prepare_pendingfields(storage.rd_pendingfields)
+ def getvirtual(self, index):
+ # Returns the index'th virtual, building it lazily if needed.
+ # Note that this may be called recursively; that's why the
+ # allocate() methods must fill in the cache as soon as they
+ # have the object, before they fill its fields.
+ v = self.virtuals_cache[index]
+ if not v:
+ v = self.rd_virtuals[index].allocate(self, index)
+ ll_assert(v == self.virtuals_cache[index], "resume.py: bad cache")
+ return v
+
+ def force_all_virtuals(self):
+ rd_virtuals = self.rd_virtuals
+ if rd_virtuals:
+ for i in range(len(rd_virtuals)):
+ if rd_virtuals[i] is not None:
+ self.getvirtual(i)
+ return self.virtuals_cache
+
def _prepare_virtuals(self, virtuals):
if virtuals:
- self.virtuals = [self.virtual_default] * len(virtuals)
- for i in range(len(virtuals)):
- vinfo = virtuals[i]
- if vinfo is not None:
- self.virtuals[i] = vinfo.allocate(self)
- for i in range(len(virtuals)):
- vinfo = virtuals[i]
- if vinfo is not None:
- vinfo.setfields(self, self.virtuals[i])
+ self.rd_virtuals = virtuals
+ self.virtuals_cache = [self.virtual_default] * len(virtuals)
def _prepare_pendingfields(self, pendingfields):
if pendingfields is not None:
@@ -698,6 +703,11 @@
return self.metainterp.execute_and_record(rop.NEWSTR,
None, ConstInt(length))
+ def string_setitem(self, strbox, index, charnum):
+ charbox = self.decode_box(charnum, INT)
+ self.metainterp.execute_and_record(rop.STRSETITEM, None,
+ strbox, ConstInt(index), charbox)
+
def concat_strings(self, str1num, str2num):
calldescr, func = callinfo_for_oopspec(EffectInfo.OS_STR_CONCAT)
str1box = self.decode_box(str1num, REF)
@@ -756,9 +766,7 @@
else:
box = self.consts[num]
elif tag == TAGVIRTUAL:
- virtuals = self.virtuals
- assert virtuals is not None
- box = virtuals[num]
+ box = self.getvirtual(num)
elif tag == TAGINT:
box = ConstInt(num)
else:
@@ -843,7 +851,7 @@
resumereader.handling_async_forcing()
vrefinfo = metainterp_sd.virtualref_info
resumereader.consume_vref_and_vable(vrefinfo, vinfo)
- return resumereader.virtuals
+ return resumereader.force_all_virtuals()
class ResumeDataDirectReader(AbstractResumeDataReader):
unique_id = lambda: None
@@ -861,7 +869,9 @@
# special case for resuming after a GUARD_NOT_FORCED: we already
# have the virtuals
self.resume_after_guard_not_forced = 2
- self.virtuals = all_virtuals
+ self.virtuals_cache = all_virtuals
+ # self.rd_virtuals can remain None, because virtuals_cache is
+ # already filled
def handling_async_forcing(self):
self.resume_after_guard_not_forced = 1
@@ -935,6 +945,10 @@
def allocate_string(self, length):
return self.cpu.bh_newstr(length)
+ def string_setitem(self, str, index, charnum):
+ char = self.decode_int(charnum)
+ self.cpu.bh_strsetitem(str, index, char)
+
def concat_strings(self, str1num, str2num):
str1 = self.decode_ref(str1num)
str2 = self.decode_ref(str2num)
@@ -995,9 +1009,7 @@
return self.cpu.ts.NULLREF
return self.consts[num].getref_base()
elif tag == TAGVIRTUAL:
- virtuals = self.virtuals
- assert virtuals is not None
- return virtuals[num]
+ return self.getvirtual(num)
else:
assert tag == TAGBOX
if num < 0:
Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizefindnode.py Sun Sep 26 13:56:56 2010
@@ -123,6 +123,27 @@
EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR_SLICE))
strequaldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
EffectInfo([], [], [], oopspecindex=EffectInfo.OS_STR_EQUAL))
+ streq_slice_checknull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_SLICE_CHECKNULL))
+ streq_slice_nonnull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_SLICE_NONNULL))
+ streq_slice_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_SLICE_CHAR))
+ streq_nonnull_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_NONNULL))
+ streq_nonnull_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_NONNULL_CHAR))
+ streq_checknull_char_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_CHECKNULL_CHAR))
+ streq_lengthok_descr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT,
+ EffectInfo([], [], [],
+ oopspecindex=EffectInfo.OS_STREQ_LENGTHOK))
class LoopToken(AbstractDescr):
pass
Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_optimizeopt.py Sun Sep 26 13:56:56 2010
@@ -4098,6 +4098,42 @@
"""
self.optimize_loop(ops, 'Not, Not, Not, Not, Not', expected)
+ def test_str_slice_getitem1(self):
+ ops = """
+ [p1, i1, i2, i3]
+ p2 = call(0, p1, i1, i2, descr=slicedescr)
+ i4 = strgetitem(p2, i3)
+ escape(i4)
+ jump(p1, i1, i2, i3)
+ """
+ expected = """
+ [p1, i1, i2, i3]
+ i6 = int_sub(i2, i1) # killed by the backend
+ i5 = int_add(i1, i3)
+ i4 = strgetitem(p1, i5)
+ escape(i4)
+ jump(p1, i1, i2, i3)
+ """
+ self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+
+ def test_str_slice_plain(self):
+ ops = """
+ [i3, i4]
+ p1 = newstr(2)
+ strsetitem(p1, 0, i3)
+ strsetitem(p1, 1, i4)
+ p2 = call(0, p1, 1, 2, descr=slicedescr)
+ i5 = strgetitem(p2, 0)
+ escape(i5)
+ jump(i3, i4)
+ """
+ expected = """
+ [i3, i4]
+ escape(i4)
+ jump(i3, i4)
+ """
+ self.optimize_loop(ops, 'Not, Not', expected)
+
def test_str_slice_concat(self):
ops = """
[p1, i1, i2, p2]
@@ -4119,6 +4155,345 @@
"""
self.optimize_loop(ops, 'Not, Not, Not, Not', expected)
+ # ----------
+ def optimize_loop_extradescrs(self, ops, spectext, optops):
+ from pypy.jit.metainterp.optimizeopt import string
+ def my_callinfo_for_oopspec(oopspecindex):
+ calldescrtype = type(LLtypeMixin.strequaldescr)
+ for value in LLtypeMixin.__dict__.values():
+ if isinstance(value, calldescrtype):
+ if (value.get_extra_info() and
+ value.get_extra_info().oopspecindex == oopspecindex):
+ from pypy.rpython.lltypesystem import lltype
+ func = lltype.nullptr(lltype.FuncType([], lltype.Void))
+ # returns 0 for 'func' in this test
+ return value, func
+ raise AssertionError("not found: oopspecindex=%d" % oopspecindex)
+ #
+ saved = string.callinfo_for_oopspec
+ try:
+ string.callinfo_for_oopspec = my_callinfo_for_oopspec
+ self.optimize_loop(ops, spectext, optops)
+ finally:
+ string.callinfo_for_oopspec = saved
+
+ def test_str_equal_noop1(self):
+ ops = """
+ [p1, p2]
+ i0 = call(0, p1, p2, descr=strequaldescr)
+ escape(i0)
+ jump(p1, p2)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not', ops)
+
+ def test_str_equal_noop2(self):
+ ops = """
+ [p1, p2, p3]
+ p4 = call(0, p1, p2, descr=strconcatdescr)
+ i0 = call(0, p3, p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, p2, p3)
+ """
+ expected = """
+ [p1, p2, p3]
+ i1 = strlen(p1)
+ i2 = strlen(p2)
+ i3 = int_add(i1, i2)
+ p4 = newstr(i3)
+ i4 = strlen(p1)
+ copystrcontent(p1, p4, 0, 0, i4)
+ i5 = strlen(p2)
+ i6 = int_add(i4, i5) # will be killed by the backend
+ copystrcontent(p2, p4, 0, i4, i5)
+ i0 = call(0, p3, p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, p2, p3)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not', expected)
+
+ def test_str_equal_slice1(self):
+ ops = """
+ [p1, i1, i2, p3]
+ p4 = call(0, p1, i1, i2, descr=slicedescr)
+ i0 = call(0, p4, p3, descr=strequaldescr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ expected = """
+ [p1, i1, i2, p3]
+ i3 = int_sub(i2, i1)
+ i0 = call(0, p1, i1, i3, p3, descr=streq_slice_checknull_descr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+ def test_str_equal_slice2(self):
+ ops = """
+ [p1, i1, i2, p3]
+ p4 = call(0, p1, i1, i2, descr=slicedescr)
+ i0 = call(0, p3, p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ expected = """
+ [p1, i1, i2, p3]
+ i4 = int_sub(i2, i1)
+ i0 = call(0, p1, i1, i4, p3, descr=streq_slice_checknull_descr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+ def test_str_equal_slice3(self):
+ ops = """
+ [p1, i1, i2, p3]
+ guard_nonnull(p3) []
+ p4 = call(0, p1, i1, i2, descr=slicedescr)
+ i0 = call(0, p3, p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ expected = """
+ [p1, i1, i2, p3]
+ guard_nonnull(p3) []
+ i4 = int_sub(i2, i1)
+ i0 = call(0, p1, i1, i4, p3, descr=streq_slice_nonnull_descr)
+ escape(i0)
+ jump(p1, i1, i2, p3)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+ def test_str_equal_slice4(self):
+ ops = """
+ [p1, i1, i2]
+ p3 = call(0, p1, i1, i2, descr=slicedescr)
+ i0 = call(0, p3, "x", descr=strequaldescr)
+ escape(i0)
+ jump(p1, i1, i2)
+ """
+ expected = """
+ [p1, i1, i2]
+ i3 = int_sub(i2, i1)
+ i0 = call(0, p1, i1, i3, 120, descr=streq_slice_char_descr)
+ escape(i0)
+ jump(p1, i1, i2)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not', expected)
+
+ def test_str_equal_slice5(self):
+ ops = """
+ [p1, i1, i2, i3]
+ p4 = call(0, p1, i1, i2, descr=slicedescr)
+ p5 = newstr(1)
+ strsetitem(p5, 0, i3)
+ i0 = call(0, p5, p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, i1, i2, i3)
+ """
+ expected = """
+ [p1, i1, i2, i3]
+ i4 = int_sub(i2, i1)
+ i0 = call(0, p1, i1, i4, i3, descr=streq_slice_char_descr)
+ escape(i0)
+ jump(p1, i1, i2, i3)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not, Not, Not', expected)
+
+ def test_str_equal_none1(self):
+ ops = """
+ [p1]
+ i0 = call(0, p1, NULL, descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ i0 = ptr_eq(p1, NULL)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_none2(self):
+ ops = """
+ [p1]
+ i0 = call(0, NULL, p1, descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ i0 = ptr_eq(p1, NULL)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_nonnull1(self):
+ ops = """
+ [p1]
+ guard_nonnull(p1) []
+ i0 = call(0, p1, "hello world", descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ guard_nonnull(p1) []
+ i0 = call(0, p1, "hello world", descr=streq_nonnull_descr)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_nonnull2(self):
+ ops = """
+ [p1]
+ guard_nonnull(p1) []
+ i0 = call(0, p1, "", descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ guard_nonnull(p1) []
+ i1 = strlen(p1)
+ i0 = int_eq(i1, 0)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_nonnull3(self):
+ ops = """
+ [p1]
+ guard_nonnull(p1) []
+ i0 = call(0, p1, "x", descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ guard_nonnull(p1) []
+ i0 = call(0, p1, 120, descr=streq_nonnull_char_descr)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_nonnull4(self):
+ ops = """
+ [p1, p2]
+ p4 = call(0, p1, p2, descr=strconcatdescr)
+ i0 = call(0, "hello world", p4, descr=strequaldescr)
+ escape(i0)
+ jump(p1, p2)
+ """
+ expected = """
+ [p1, p2]
+ i1 = strlen(p1)
+ i2 = strlen(p2)
+ i3 = int_add(i1, i2)
+ p4 = newstr(i3)
+ i4 = strlen(p1)
+ copystrcontent(p1, p4, 0, 0, i4)
+ i5 = strlen(p2)
+ i6 = int_add(i4, i5) # will be killed by the backend
+ copystrcontent(p2, p4, 0, i4, i5)
+ i0 = call(0, "hello world", p4, descr=streq_nonnull_descr)
+ escape(i0)
+ jump(p1, p2)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not', expected)
+
+ def test_str_equal_chars0(self):
+ ops = """
+ [i1]
+ p1 = newstr(0)
+ i0 = call(0, p1, "", descr=strequaldescr)
+ escape(i0)
+ jump(i1)
+ """
+ expected = """
+ [i1]
+ escape(1)
+ jump(i1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_chars1(self):
+ ops = """
+ [i1]
+ p1 = newstr(1)
+ strsetitem(p1, 0, i1)
+ i0 = call(0, p1, "x", descr=strequaldescr)
+ escape(i0)
+ jump(i1)
+ """
+ expected = """
+ [i1]
+ i0 = int_eq(i1, 120) # ord('x')
+ escape(i0)
+ jump(i1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_chars2(self):
+ ops = """
+ [i1, i2]
+ p1 = newstr(2)
+ strsetitem(p1, 0, i1)
+ strsetitem(p1, 1, i2)
+ i0 = call(0, p1, "xy", descr=strequaldescr)
+ escape(i0)
+ jump(i1, i2)
+ """
+ expected = """
+ [i1, i2]
+ p1 = newstr(2)
+ strsetitem(p1, 0, i1)
+ strsetitem(p1, 1, i2)
+ i0 = call(0, p1, "xy", descr=streq_lengthok_descr)
+ escape(i0)
+ jump(i1, i2)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not, Not', expected)
+
+ def test_str_equal_chars3(self):
+ ops = """
+ [p1]
+ i0 = call(0, "x", p1, descr=strequaldescr)
+ escape(i0)
+ jump(p1)
+ """
+ expected = """
+ [p1]
+ i0 = call(0, p1, 120, descr=streq_checknull_char_descr)
+ escape(i0)
+ jump(p1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ def test_str_equal_lengthmismatch1(self):
+ ops = """
+ [i1]
+ p1 = newstr(1)
+ strsetitem(p1, 0, i1)
+ i0 = call(0, "xy", p1, descr=strequaldescr)
+ escape(i0)
+ jump(i1)
+ """
+ expected = """
+ [i1]
+ escape(0)
+ jump(i1)
+ """
+ self.optimize_loop_extradescrs(ops, 'Not', expected)
+
+ # XXX unicode operations
+ # XXX str2unicode
+
##class TestOOtype(BaseTestOptimizeOpt, OOtypeMixin):
Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_resume.py Sun Sep 26 13:56:56 2010
@@ -199,10 +199,10 @@
def test_prepare_virtuals():
class FakeVinfo(object):
- def allocate(self, decoder):
- return "allocated"
- def setfields(self, decoder, virtual):
- assert virtual == "allocated"
+ def allocate(self, decoder, index):
+ s = "allocated"
+ decoder.virtuals_cache[index] = s
+ return s
class FakeStorage(object):
rd_virtuals = [FakeVinfo(), None]
rd_numb = []
@@ -212,7 +212,97 @@
_already_allocated_resume_virtuals = None
cpu = None
reader = ResumeDataDirectReader(None, FakeStorage())
- assert reader.virtuals == ["allocated", reader.virtual_default]
+ assert reader.force_all_virtuals() == ["allocated", reader.virtual_default]
+
+# ____________________________________________________________
+
+class FakeResumeDataReader(AbstractResumeDataReader):
+ def allocate_with_vtable(self, known_class):
+ return FakeBuiltObject(vtable=known_class)
+ def allocate_struct(self, typedescr):
+ return FakeBuiltObject(typedescr=typedescr)
+ def allocate_array(self, arraydescr, length):
+ return FakeBuiltObject(arraydescr=arraydescr, items=[None]*length)
+ def setfield(self, descr, struct, fieldnum):
+ setattr(struct, descr, fieldnum)
+ def setarrayitem_int(self, arraydescr, array, i, fieldnum):
+ assert 0 <= i < len(array.items)
+ assert arraydescr is array.arraydescr
+ array.items[i] = fieldnum
+ def allocate_string(self, length):
+ return FakeBuiltObject(string=[None]*length)
+ def string_setitem(self, string, i, fieldnum):
+ value, tag = untag(fieldnum)
+ assert tag == TAGINT
+ assert 0 <= i < len(string.string)
+ string.string[i] = value
+ def concat_strings(self, left, right):
+ return FakeBuiltObject(strconcat=[left, right])
+ def slice_string(self, str, start, length):
+ return FakeBuiltObject(strslice=[str, start, length])
+
+class FakeBuiltObject(object):
+ def __init__(self, **kwds):
+ self.__dict__ = kwds
+ def __eq__(self, other):
+ return (self.__class__ == other.__class__ and
+ self.__dict__ == other.__dict__)
+ def __repr__(self):
+ return 'FakeBuiltObject(%s)' % (
+ ', '.join(['%s=%r' % item for item in self.__dict__.items()]))
+
+class FakeArrayDescr(object):
+ def is_array_of_pointers(self): return False
+ def is_array_of_floats(self): return False
+
+def test_virtualinfo():
+ info = VirtualInfo(123, ["fielddescr1"])
+ info.fieldnums = [tag(456, TAGINT)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(vtable=123, fielddescr1=tag(456, TAGINT))]
+
+def test_vstructinfo():
+ info = VStructInfo(124, ["fielddescr1"])
+ info.fieldnums = [tag(456, TAGINT)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(typedescr=124, fielddescr1=tag(456, TAGINT))]
+
+def test_varrayinfo():
+ arraydescr = FakeArrayDescr()
+ info = VArrayInfo(arraydescr)
+ info.fieldnums = [tag(456, TAGINT)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(arraydescr=arraydescr, items=[tag(456, TAGINT)])]
+
+def test_vstrplaininfo():
+ info = VStrPlainInfo()
+ info.fieldnums = [tag(60, TAGINT)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(string=[60])]
+
+def test_vstrconcatinfo():
+ info = VStrConcatInfo()
+ info.fieldnums = [tag(10, TAGBOX), tag(20, TAGBOX)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(strconcat=info.fieldnums)]
+
+def test_vstrsliceinfo():
+ info = VStrSliceInfo()
+ info.fieldnums = [tag(10, TAGBOX), tag(20, TAGBOX), tag(30, TAGBOX)]
+ reader = FakeResumeDataReader()
+ reader._prepare_virtuals([info])
+ assert reader.force_all_virtuals() == [
+ FakeBuiltObject(strslice=info.fieldnums)]
# ____________________________________________________________
@@ -957,7 +1047,7 @@
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
- assert len(reader.virtuals) == 2
+ assert len(reader.virtuals_cache) == 2
b2t = reader.decode_ref(modifier._gettagged(b2s))
b4t = reader.decode_ref(modifier._gettagged(b4s))
trace = metainterp.trace
@@ -973,9 +1063,9 @@
(rop.SETFIELD_GC, [b4t, b3t], None, LLtypeMixin.valuedescr),
(rop.SETFIELD_GC, [b4t, b5t], None, LLtypeMixin.otherdescr)]
if untag(modifier._gettagged(b2s))[0] == -2:
- expected = [b2new, b4new] + b2set + b4set
+ expected = [b2new, b4new] + b4set + b2set
else:
- expected = [b4new, b2new] + b4set + b2set
+ expected = [b4new, b2new] + b2set + b4set
for x, y in zip(expected, trace):
assert x == y
@@ -1020,7 +1110,7 @@
# resume
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
- assert len(reader.virtuals) == 1
+ assert len(reader.virtuals_cache) == 1
b2t = reader.decode_ref(tag(0, TAGVIRTUAL))
trace = metainterp.trace
expected = [
@@ -1065,7 +1155,7 @@
NULL = ConstPtr.value
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
- assert len(reader.virtuals) == 1
+ assert len(reader.virtuals_cache) == 1
b2t = reader.decode_ref(tag(0, TAGVIRTUAL))
trace = metainterp.trace
@@ -1112,7 +1202,7 @@
metainterp = MyMetaInterp()
reader = ResumeDataFakeReader(storage, newboxes, metainterp)
- assert reader.virtuals is None
+ assert reader.virtuals_cache is None
trace = metainterp.trace
b2set = (rop.SETFIELD_GC, [b2t, b4t], None, LLtypeMixin.nextdescr)
expected = [b2set]
Modified: pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/test/test_string.py Sun Sep 26 13:56:56 2010
@@ -281,6 +281,24 @@
return 42
self.meta_interp(f, [10, 10])
+ def test_streq_char(self):
+ for somestr in ["?abcdefg", ]: #u"def"]:
+ jitdriver = JitDriver(greens = [], reds = ['m', 'n'])
+ @dont_look_inside
+ def escape(x):
+ pass
+ def f(n, m):
+ assert n >= 0
+ while m >= 0:
+ jitdriver.can_enter_jit(m=m, n=n)
+ jitdriver.jit_merge_point(m=m, n=n)
+ s = somestr[:m]
+ escape(s == "?")
+ m -= 1
+ return 42
+ self.meta_interp(f, [6, 7])
+ self.check_loops(newstr=0, newunicode=0)
+
class TestOOtype(StringTests, OOJitMixin):
CALL = "oosend"
Modified: pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py
==============================================================================
--- pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py (original)
+++ pypy/branch/jit-str/pypy/jit/metainterp/warmstate.py Sun Sep 26 13:56:56 2010
@@ -83,7 +83,7 @@
return history.ConstFloat(value)
else:
return history.BoxFloat(value)
- elif isinstance(value, (str, unicode)):
+ elif isinstance(value, str) or isinstance(value, unicode):
assert len(value) == 1 # must be a character
value = ord(value)
else:
More information about the Pypy-commit
mailing list