PageRenderTime 58ms CodeModel.GetById 18ms RepoModel.GetById 0ms app.codeStats 0ms

/rpython/jit/metainterp/optimizeopt/vstring.py

https://bitbucket.org/pypy/pypy/
Python | 860 lines | 727 code | 82 blank | 51 comment | 170 complexity | 073d2e69387c1242ca59cc346619cbd7 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
  1. from rpython.jit.codewriter.effectinfo import EffectInfo
  2. from rpython.jit.metainterp.history import (Const, ConstInt, ConstPtr,
  3. get_const_ptr_for_string, get_const_ptr_for_unicode, REF, INT,
  4. DONT_CHANGE)
  5. from rpython.jit.metainterp.optimizeopt import optimizer
  6. from rpython.jit.metainterp.optimizeopt.optimizer import CONST_0, CONST_1
  7. from rpython.jit.metainterp.optimizeopt.optimizer import llhelper, REMOVED
  8. from rpython.jit.metainterp.optimizeopt.util import make_dispatcher_method
  9. from rpython.jit.metainterp.resoperation import rop, ResOperation,\
  10. AbstractResOp
  11. from rpython.jit.metainterp.optimizeopt import info
  12. from rpython.rlib.objectmodel import specialize, we_are_translated
  13. from rpython.rlib.unroll import unrolling_iterable
  14. from rpython.rtyper import annlowlevel
  15. from rpython.rtyper.lltypesystem import lltype, rstr
  16. from rpython.rlib.rarithmetic import is_valid_int
  17. MAX_CONST_LEN = 100
  18. class StrOrUnicode(object):
  19. def __init__(self, LLTYPE, hlstr, emptystr, chr,
  20. NEWSTR, STRLEN, STRGETITEM, STRSETITEM, COPYSTRCONTENT,
  21. OS_offset):
  22. self.LLTYPE = LLTYPE
  23. self.hlstr = hlstr
  24. self.emptystr = emptystr
  25. self.chr = chr
  26. self.NEWSTR = NEWSTR
  27. self.STRLEN = STRLEN
  28. self.STRGETITEM = STRGETITEM
  29. self.STRSETITEM = STRSETITEM
  30. self.COPYSTRCONTENT = COPYSTRCONTENT
  31. self.OS_offset = OS_offset
  32. def _freeze_(self):
  33. return True
  34. mode_string = StrOrUnicode(rstr.STR, annlowlevel.hlstr, '', chr,
  35. rop.NEWSTR, rop.STRLEN, rop.STRGETITEM,
  36. rop.STRSETITEM, rop.COPYSTRCONTENT, 0)
  37. mode_unicode = StrOrUnicode(rstr.UNICODE, annlowlevel.hlunicode, u'', unichr,
  38. rop.NEWUNICODE, rop.UNICODELEN, rop.UNICODEGETITEM,
  39. rop.UNICODESETITEM, rop.COPYUNICODECONTENT,
  40. EffectInfo._OS_offset_uni)
  41. # ____________________________________________________________
  42. class StrPtrInfo(info.AbstractVirtualPtrInfo):
  43. #_attrs_ = ('length', 'lenbound', 'lgtop', 'mode', '_cached_vinfo', '_is_virtual')
  44. lenbound = None
  45. lgtop = None
  46. _cached_vinfo = None
  47. def __init__(self, mode, is_virtual=False, length=-1):
  48. self.length = length
  49. self._is_virtual = is_virtual
  50. self.mode = mode
  51. self.length = length
  52. def getlenbound(self, mode):
  53. from rpython.jit.metainterp.optimizeopt import intutils
  54. if self.lenbound is None:
  55. if self.length == -1:
  56. self.lenbound = intutils.IntBound(0, intutils.MAXINT)
  57. else:
  58. self.lenbound = intutils.ConstIntBound(self.length)
  59. return self.lenbound
  60. @specialize.arg(2)
  61. def get_constant_string_spec(self, string_optimizer, mode):
  62. return None # can't be constant
  63. def force_box(self, op, optforce):
  64. if not self.is_virtual():
  65. return op
  66. optforce.forget_numberings()
  67. if self.mode is mode_string:
  68. s = self.get_constant_string_spec(optforce, mode_string)
  69. if s is not None:
  70. c_s = get_const_ptr_for_string(s)
  71. optforce.get_box_replacement(op).set_forwarded(c_s)
  72. return c_s
  73. else:
  74. s = self.get_constant_string_spec(optforce, mode_unicode)
  75. if s is not None:
  76. c_s = get_const_ptr_for_unicode(s)
  77. optforce.get_box_replacement(op).set_forwarded(c_s)
  78. return c_s
  79. self._is_virtual = False
  80. lengthbox = self.getstrlen(op, optforce.optimizer.optstring, self.mode)
  81. newop = ResOperation(self.mode.NEWSTR, [lengthbox])
  82. if not we_are_translated():
  83. newop.name = 'FORCE'
  84. optforce.emit_operation(newop)
  85. newop = optforce.getlastop()
  86. newop.set_forwarded(self)
  87. op = optforce.get_box_replacement(op)
  88. op.set_forwarded(newop)
  89. optstring = optforce.optimizer.optstring
  90. self.initialize_forced_string(op, optstring, op, CONST_0, self.mode)
  91. return newop
  92. def initialize_forced_string(self, op, string_optimizer, targetbox,
  93. offsetbox, mode):
  94. return self.string_copy_parts(op, string_optimizer, targetbox,
  95. offsetbox, mode)
  96. def getstrlen(self, op, string_optimizer, mode, create_ops=True):
  97. if self.lgtop is not None:
  98. return self.lgtop
  99. assert not self.is_virtual()
  100. if not create_ops:
  101. return None
  102. lengthop = ResOperation(mode.STRLEN, [op])
  103. lengthop.set_forwarded(self.getlenbound(mode))
  104. self.lgtop = lengthop
  105. string_optimizer.emit_operation(lengthop)
  106. return lengthop
  107. def make_guards(self, op, short, optimizer):
  108. info.AbstractVirtualPtrInfo.make_guards(self, op, short, optimizer)
  109. if self.lenbound and self.lenbound.lower >= 1:
  110. if self.mode is mode_string:
  111. lenop = ResOperation(rop.STRLEN, [op])
  112. else:
  113. assert self.mode is mode_unicode
  114. lenop = ResOperation(rop.UNICODELEN, [op])
  115. short.append(lenop)
  116. self.lenbound.make_guards(lenop, short, optimizer)
  117. def string_copy_parts(self, op, string_optimizer, targetbox, offsetbox,
  118. mode):
  119. # Copies the pointer-to-string 'self' into the target string
  120. # given by 'targetbox', at the specified offset. Returns the offset
  121. # at the end of the copy.
  122. lengthbox = self.getstrlen(op, string_optimizer, mode)
  123. srcbox = self.force_box(op, string_optimizer)
  124. return copy_str_content(string_optimizer, srcbox, targetbox,
  125. CONST_0, offsetbox, lengthbox, mode)
  126. class VStringPlainInfo(StrPtrInfo):
  127. #_attrs_ = ('mode', '_is_virtual')
  128. _chars = None
  129. def __init__(self, mode, is_virtual, length):
  130. if length != -1:
  131. self._chars = [None] * length
  132. StrPtrInfo.__init__(self, mode, is_virtual, length)
  133. def strsetitem(self, index, op, cf=None, optheap=None):
  134. self._chars[index] = op
  135. def shrink(self, length):
  136. assert length >= 0
  137. self.length = length
  138. del self._chars[length:]
  139. def setup_slice(self, longerlist, start, stop):
  140. assert 0 <= start <= stop <= len(longerlist)
  141. self._chars = longerlist[start:stop]
  142. # slice the 'longerlist', which may also contain Nones
  143. def strgetitem(self, index, optheap=None):
  144. return self._chars[index]
  145. def is_virtual(self):
  146. return self._is_virtual
  147. def getstrlen(self, op, string_optimizer, mode, create_ops=True):
  148. if self.lgtop is None:
  149. self.lgtop = ConstInt(len(self._chars))
  150. return self.lgtop
  151. @specialize.arg(2)
  152. def get_constant_string_spec(self, optforce, mode):
  153. for c in self._chars:
  154. if c is None or not c.is_constant():
  155. return None
  156. return mode.emptystr.join([mode.chr(c.getint())
  157. for c in self._chars])
  158. def string_copy_parts(self, op, string_optimizer, targetbox, offsetbox,
  159. mode):
  160. if not self.is_virtual():
  161. return StrPtrInfo.string_copy_parts(self, op, string_optimizer,
  162. targetbox, offsetbox, mode)
  163. else:
  164. return self.initialize_forced_string(op, string_optimizer,
  165. targetbox, offsetbox, mode)
  166. def initialize_forced_string(self, op, string_optimizer, targetbox,
  167. offsetbox, mode):
  168. for i in range(len(self._chars)):
  169. assert not isinstance(targetbox, Const) # ConstPtr never makes sense
  170. charbox = self.strgetitem(i) # can't be virtual
  171. if charbox is not None:
  172. op = ResOperation(mode.STRSETITEM, [targetbox,
  173. offsetbox,
  174. charbox])
  175. string_optimizer.emit_operation(op)
  176. offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
  177. return offsetbox
  178. def _visitor_walk_recursive(self, instbox, visitor, optimizer):
  179. visitor.register_virtual_fields(instbox, self._chars)
  180. @specialize.argtype(1)
  181. def visitor_dispatch_virtual_type(self, visitor):
  182. return visitor.visit_vstrplain(self.mode is mode_unicode)
  183. class VStringSliceInfo(StrPtrInfo):
  184. length = -1
  185. start = None
  186. lgtop = None
  187. s = None
  188. def __init__(self, s, start, length, mode):
  189. self.s = s
  190. self.start = start
  191. self.lgtop = length
  192. self.mode = mode
  193. self._is_virtual = True
  194. def is_virtual(self):
  195. return self._is_virtual
  196. def string_copy_parts(self, op, string_optimizer, targetbox, offsetbox,
  197. mode):
  198. return copy_str_content(string_optimizer, self.s, targetbox,
  199. self.start, offsetbox, self.lgtop, mode)
  200. @specialize.arg(2)
  201. def get_constant_string_spec(self, string_optimizer, mode):
  202. vstart = string_optimizer.getintbound(self.start)
  203. vlength = string_optimizer.getintbound(self.lgtop)
  204. if vstart.is_constant() and vlength.is_constant():
  205. vstr = string_optimizer.getptrinfo(self.s)
  206. s1 = vstr.get_constant_string_spec(string_optimizer, mode)
  207. if s1 is None:
  208. return None
  209. start = vstart.getint()
  210. length = vlength.getint()
  211. assert start >= 0
  212. assert length >= 0
  213. return s1[start : start + length]
  214. return None
  215. def getstrlen(self, op, string_optimizer, mode, create_ops=True):
  216. return self.lgtop
  217. def _visitor_walk_recursive(self, instbox, visitor, optimizer):
  218. boxes = [self.s, self.start, self.lgtop]
  219. visitor.register_virtual_fields(instbox, boxes)
  220. opinfo = optimizer.getptrinfo(self.s)
  221. if opinfo and opinfo.is_virtual():
  222. opinfo.visitor_walk_recursive(self.s, visitor, optimizer)
  223. @specialize.argtype(1)
  224. def visitor_dispatch_virtual_type(self, visitor):
  225. return visitor.visit_vstrslice(self.mode is mode_unicode)
  226. class VStringConcatInfo(StrPtrInfo):
  227. #_attrs_ = ('mode', 'vleft', 'vright', '_is_virtual')
  228. vleft = None
  229. vright = None
  230. _is_virtual = False
  231. def __init__(self, mode, vleft, vright, is_virtual):
  232. self.vleft = vleft
  233. self.vright = vright
  234. StrPtrInfo.__init__(self, mode, is_virtual)
  235. def is_virtual(self):
  236. return self._is_virtual
  237. def getstrlen(self, op, string_optimizer, mode, create_ops=True):
  238. if self.lgtop is not None:
  239. return self.lgtop
  240. lefti = string_optimizer.getptrinfo(self.vleft)
  241. len1box = lefti.getstrlen(self.vleft, string_optimizer, mode,
  242. create_ops)
  243. if len1box is None:
  244. return None
  245. righti = string_optimizer.getptrinfo(self.vright)
  246. len2box = righti.getstrlen(self.vright, string_optimizer, mode,
  247. create_ops)
  248. if len2box is None:
  249. return None
  250. self.lgtop = _int_add(string_optimizer, len1box, len2box, create_ops)
  251. # ^^^ may still be None, if string_optimizer is None
  252. return self.lgtop
  253. @specialize.arg(2)
  254. def get_constant_string_spec(self, string_optimizer, mode):
  255. ileft = string_optimizer.getptrinfo(self.vleft)
  256. s1 = ileft.get_constant_string_spec(string_optimizer, mode)
  257. if s1 is None:
  258. return None
  259. iright = string_optimizer.getptrinfo(self.vright)
  260. s2 = iright.get_constant_string_spec(string_optimizer, mode)
  261. if s2 is None:
  262. return None
  263. return s1 + s2
  264. def string_copy_parts(self, op, string_optimizer, targetbox, offsetbox,
  265. mode):
  266. lefti = string_optimizer.getptrinfo(self.vleft)
  267. offsetbox = lefti.string_copy_parts(self.vleft, string_optimizer,
  268. targetbox, offsetbox, mode)
  269. righti = string_optimizer.getptrinfo(self.vright)
  270. offsetbox = righti.string_copy_parts(self.vright, string_optimizer,
  271. targetbox, offsetbox, mode)
  272. return offsetbox
  273. def _visitor_walk_recursive(self, instbox, visitor, optimizer):
  274. # we don't store the lengthvalue in guards, because the
  275. # guard-failed code starts with a regular STR_CONCAT again
  276. leftbox = self.vleft
  277. rightbox = self.vright
  278. visitor.register_virtual_fields(instbox, [leftbox, rightbox])
  279. leftinfo = optimizer.getptrinfo(leftbox)
  280. rightinfo = optimizer.getptrinfo(rightbox)
  281. if leftinfo and leftinfo.is_virtual():
  282. leftinfo.visitor_walk_recursive(leftbox, visitor, optimizer)
  283. if rightinfo and rightinfo.is_virtual():
  284. rightinfo.visitor_walk_recursive(rightbox, visitor, optimizer)
  285. @specialize.argtype(1)
  286. def visitor_dispatch_virtual_type(self, visitor):
  287. return visitor.visit_vstrconcat(self.mode is mode_unicode)
  288. def copy_str_content(string_optimizer, srcbox, targetbox,
  289. srcoffsetbox, offsetbox, lengthbox, mode,
  290. need_next_offset=True):
  291. srcbox = string_optimizer.get_box_replacement(srcbox)
  292. srcoffset = string_optimizer.getintbound(srcoffsetbox)
  293. lgt = string_optimizer.getintbound(lengthbox)
  294. if isinstance(srcbox, ConstPtr) and srcoffset.is_constant():
  295. M = 5
  296. else:
  297. M = 2
  298. if lgt.is_constant() and lgt.getint() <= M:
  299. # up to M characters are done "inline", i.e. with STRGETITEM/STRSETITEM
  300. # instead of just a COPYSTRCONTENT.
  301. for i in range(lgt.getint()):
  302. charbox = string_optimizer.strgetitem(None, srcbox, srcoffsetbox,
  303. mode)
  304. srcoffsetbox = _int_add(string_optimizer, srcoffsetbox, CONST_1)
  305. assert not isinstance(targetbox, Const)# ConstPtr never makes sense
  306. string_optimizer.emit_operation(ResOperation(mode.STRSETITEM,
  307. [targetbox, offsetbox, charbox]))
  308. offsetbox = _int_add(string_optimizer, offsetbox, CONST_1)
  309. else:
  310. if need_next_offset:
  311. nextoffsetbox = _int_add(string_optimizer, offsetbox, lengthbox)
  312. else:
  313. nextoffsetbox = None
  314. assert not isinstance(targetbox, Const) # ConstPtr never makes sense
  315. op = ResOperation(mode.COPYSTRCONTENT, [srcbox, targetbox,
  316. srcoffsetbox, offsetbox,
  317. lengthbox])
  318. string_optimizer.emit_operation(op)
  319. offsetbox = nextoffsetbox
  320. return offsetbox
  321. def _int_add(string_optimizer, box1, box2, create_ops=True):
  322. if isinstance(box1, ConstInt):
  323. if box1.value == 0:
  324. return box2
  325. if isinstance(box2, ConstInt):
  326. return ConstInt(box1.value + box2.value)
  327. elif isinstance(box2, ConstInt) and box2.value == 0:
  328. return box1
  329. if not create_ops:
  330. return None
  331. op = ResOperation(rop.INT_ADD, [box1, box2])
  332. string_optimizer.send_extra_operation(op)
  333. return op
  334. def _int_sub(string_optimizer, box1, box2):
  335. if isinstance(box2, ConstInt):
  336. if box2.value == 0:
  337. return box1
  338. if isinstance(box1, ConstInt):
  339. return ConstInt(box1.value - box2.value)
  340. op = ResOperation(rop.INT_SUB, [box1, box2])
  341. string_optimizer.send_extra_operation(op)
  342. return op
  343. def _strgetitem(string_optimizer, strbox, indexbox, mode, resbox=None):
  344. if isinstance(strbox, ConstPtr) and isinstance(indexbox, ConstInt):
  345. if mode is mode_string:
  346. s = strbox.getref(lltype.Ptr(rstr.STR))
  347. resnewbox = ConstInt(ord(s.chars[indexbox.getint()]))
  348. else:
  349. s = strbox.getref(lltype.Ptr(rstr.UNICODE))
  350. resnewbox = ConstInt(ord(s.chars[indexbox.getint()]))
  351. if resbox is not None:
  352. string_optimizer.make_equal_to(resbox, resnewbox)
  353. return resnewbox
  354. if resbox is None:
  355. resbox = ResOperation(mode.STRGETITEM, [strbox, indexbox])
  356. else:
  357. resbox = string_optimizer.replace_op_with(resbox, mode.STRGETITEM,
  358. [strbox, indexbox])
  359. string_optimizer.emit_operation(resbox)
  360. return resbox
  361. class OptString(optimizer.Optimization):
  362. "Handling of strings and unicodes."
  363. def setup(self):
  364. self.optimizer.optstring = self
  365. def make_vstring_plain(self, op, mode, length):
  366. vvalue = VStringPlainInfo(mode, True, length)
  367. op = self.replace_op_with(op, op.getopnum())
  368. op.set_forwarded(vvalue)
  369. return vvalue
  370. def make_vstring_concat(self, op, mode, vleft, vright):
  371. vvalue = VStringConcatInfo(mode, vleft, vright, True)
  372. op = self.replace_op_with(op, op.getopnum())
  373. op.set_forwarded(vvalue)
  374. return vvalue
  375. def make_vstring_slice(self, op, strbox, startbox, mode, lengthbox):
  376. vvalue = VStringSliceInfo(strbox, startbox, lengthbox, mode)
  377. op = self.replace_op_with(op, op.getopnum())
  378. op.set_forwarded(vvalue)
  379. return vvalue
  380. def optimize_NEWSTR(self, op):
  381. self._optimize_NEWSTR(op, mode_string)
  382. def optimize_NEWUNICODE(self, op):
  383. self._optimize_NEWSTR(op, mode_unicode)
  384. def _optimize_NEWSTR(self, op, mode):
  385. length_box = self.get_constant_box(op.getarg(0))
  386. if length_box and length_box.getint() <= MAX_CONST_LEN:
  387. assert not op.get_forwarded()
  388. self.make_vstring_plain(op, mode, length_box.getint())
  389. else:
  390. self.make_nonnull_str(op, mode)
  391. self.emit_operation(op)
  392. self.pure_from_args(mode.STRLEN, [op], op.getarg(0))
  393. def optimize_STRSETITEM(self, op):
  394. opinfo = self.getptrinfo(op.getarg(0))
  395. if opinfo:
  396. assert not opinfo.is_constant()
  397. # strsetitem(ConstPtr) never makes sense
  398. if opinfo and opinfo.is_virtual():
  399. indexbox = self.get_constant_box(op.getarg(1))
  400. if indexbox is not None:
  401. opinfo.strsetitem(indexbox.getint(),
  402. self.get_box_replacement(op.getarg(2)))
  403. return
  404. self.make_nonnull(op.getarg(0))
  405. self.emit_operation(op)
  406. optimize_UNICODESETITEM = optimize_STRSETITEM
  407. def optimize_STRGETITEM(self, op):
  408. self._optimize_STRGETITEM(op, mode_string)
  409. def optimize_UNICODEGETITEM(self, op):
  410. self._optimize_STRGETITEM(op, mode_unicode)
  411. def _optimize_STRGETITEM(self, op, mode):
  412. self.strgetitem(op, op.getarg(0), op.getarg(1), mode)
  413. def strgetitem(self, op, s, index, mode):
  414. self.make_nonnull_str(s, mode)
  415. sinfo = self.getptrinfo(s)
  416. #
  417. if isinstance(sinfo, VStringSliceInfo) and sinfo.is_virtual(): # slice
  418. index = _int_add(self.optimizer, sinfo.start, index)
  419. s = sinfo.s
  420. sinfo = self.getptrinfo(sinfo.s)
  421. #
  422. if isinstance(sinfo, VStringPlainInfo):
  423. # even if no longer virtual
  424. vindex = self.getintbound(index)
  425. if vindex.is_constant():
  426. result = sinfo.strgetitem(vindex.getint())
  427. if result is not None:
  428. if op is not None:
  429. self.make_equal_to(op, result)
  430. return result
  431. #
  432. vindex = self.getintbound(index)
  433. if isinstance(sinfo, VStringConcatInfo) and vindex.is_constant():
  434. leftinfo = self.getptrinfo(sinfo.vleft)
  435. len1box = leftinfo.getstrlen(sinfo.vleft, self, mode)
  436. if isinstance(len1box, ConstInt):
  437. raw_index = vindex.getint()
  438. len1 = len1box.getint()
  439. if raw_index < len1:
  440. return self.strgetitem(op, sinfo.vleft, index, mode)
  441. else:
  442. index = ConstInt(raw_index - len1)
  443. return self.strgetitem(op, sinfo.vright, index, mode)
  444. #
  445. return _strgetitem(self, s, index, mode, op)
  446. def optimize_STRLEN(self, op):
  447. self._optimize_STRLEN(op, mode_string)
  448. def optimize_UNICODELEN(self, op):
  449. self._optimize_STRLEN(op, mode_unicode)
  450. def _optimize_STRLEN(self, op, mode):
  451. opinfo = self.getptrinfo(op.getarg(0))
  452. if opinfo:
  453. lgtop = opinfo.getstrlen(op, self, mode, False)
  454. if lgtop is not None:
  455. self.make_equal_to(op, lgtop)
  456. return
  457. self.emit_operation(op)
  458. def optimize_COPYSTRCONTENT(self, op):
  459. self._optimize_COPYSTRCONTENT(op, mode_string)
  460. def optimize_COPYUNICODECONTENT(self, op):
  461. self._optimize_COPYSTRCONTENT(op, mode_unicode)
  462. def _optimize_COPYSTRCONTENT(self, op, mode):
  463. # args: src dst srcstart dststart length
  464. assert op.getarg(0).type == REF
  465. assert op.getarg(1).type == REF
  466. assert op.getarg(2).type == INT
  467. assert op.getarg(3).type == INT
  468. assert op.getarg(4).type == INT
  469. src = self.getptrinfo(op.getarg(0))
  470. dst = self.getptrinfo(op.getarg(1))
  471. srcstart = self.getintbound(op.getarg(2))
  472. dststart = self.getintbound(op.getarg(3))
  473. length = self.getintbound(op.getarg(4))
  474. dst_virtual = (isinstance(dst, VStringPlainInfo) and dst.is_virtual())
  475. if length.is_constant() and length.getint() == 0:
  476. return
  477. elif ((str and (src.is_virtual() or src.is_constant())) and
  478. srcstart.is_constant() and dststart.is_constant() and
  479. length.is_constant() and
  480. (length.getint() < 20 or ((src.is_virtual() or src.is_constant()) and dst_virtual))):
  481. src_start = srcstart.getint()
  482. dst_start = dststart.getint()
  483. actual_length = length.getint()
  484. for index in range(actual_length):
  485. vresult = self.strgetitem(None, op.getarg(0),
  486. ConstInt(index + src_start), mode)
  487. if dst_virtual:
  488. dst.strsetitem(index + dst_start, vresult)
  489. else:
  490. new_op = ResOperation(mode.STRSETITEM, [
  491. op.getarg(1), ConstInt(index + dst_start),
  492. vresult,
  493. ])
  494. self.emit_operation(new_op)
  495. else:
  496. copy_str_content(self, op.getarg(0), op.getarg(1), op.getarg(2),
  497. op.getarg(3), op.getarg(4), mode,
  498. need_next_offset=False)
  499. def optimize_CALL_I(self, op):
  500. # dispatch based on 'oopspecindex' to a method that handles
  501. # specifically the given oopspec call. For non-oopspec calls,
  502. # oopspecindex is just zero.
  503. effectinfo = op.getdescr().get_extra_info()
  504. oopspecindex = effectinfo.oopspecindex
  505. if oopspecindex != EffectInfo.OS_NONE:
  506. for value, meth in opt_call_oopspec_ops:
  507. if oopspecindex == value: # a match with the OS_STR_xxx
  508. if meth(self, op, mode_string):
  509. return
  510. break
  511. if oopspecindex == value + EffectInfo._OS_offset_uni:
  512. # a match with the OS_UNI_xxx
  513. if meth(self, op, mode_unicode):
  514. return
  515. break
  516. if oopspecindex == EffectInfo.OS_STR2UNICODE:
  517. if self.opt_call_str_STR2UNICODE(op):
  518. return
  519. if oopspecindex == EffectInfo.OS_SHRINK_ARRAY:
  520. if self.opt_call_SHRINK_ARRAY(op):
  521. return
  522. self.emit_operation(op)
  523. optimize_CALL_R = optimize_CALL_I
  524. optimize_CALL_F = optimize_CALL_I
  525. optimize_CALL_N = optimize_CALL_I
  526. optimize_CALL_PURE_I = optimize_CALL_I
  527. optimize_CALL_PURE_R = optimize_CALL_I
  528. optimize_CALL_PURE_F = optimize_CALL_I
  529. optimize_CALL_PURE_N = optimize_CALL_I
  530. def optimize_GUARD_NO_EXCEPTION(self, op):
  531. if self.last_emitted_operation is REMOVED:
  532. return
  533. self.emit_operation(op)
  534. def opt_call_str_STR2UNICODE(self, op):
  535. # Constant-fold unicode("constant string").
  536. # More generally, supporting non-constant but virtual cases is
  537. # not obvious, because of the exception UnicodeDecodeError that
  538. # can be raised by ll_str2unicode()
  539. varg = self.getptrinfo(op.getarg(1))
  540. s = None
  541. if varg:
  542. s = varg.get_constant_string_spec(self, mode_string)
  543. if s is None:
  544. return False
  545. try:
  546. u = unicode(s)
  547. except UnicodeDecodeError:
  548. return False
  549. self.make_constant(op, get_const_ptr_for_unicode(u))
  550. self.last_emitted_operation = REMOVED
  551. return True
  552. def opt_call_stroruni_STR_CONCAT(self, op, mode):
  553. self.make_nonnull_str(op.getarg(1), mode)
  554. self.make_nonnull_str(op.getarg(2), mode)
  555. self.make_vstring_concat(op, mode,
  556. self.get_box_replacement(op.getarg(1)),
  557. self.get_box_replacement(op.getarg(2)))
  558. self.last_emitted_operation = REMOVED
  559. return True
  560. def opt_call_stroruni_STR_SLICE(self, op, mode):
  561. self.make_nonnull_str(op.getarg(1), mode)
  562. vstr = self.getptrinfo(op.getarg(1))
  563. vstart = self.getintbound(op.getarg(2))
  564. vstop = self.getintbound(op.getarg(3))
  565. #
  566. #---The following looks reasonable, but see test_str_slice_bug:
  567. # the problem is what occurs if the source string has been forced
  568. # but still contains None in its _chars
  569. #if (isinstance(vstr, VStringPlainInfo) and vstart.is_constant()
  570. # and vstop.is_constant()):
  571. # value = self.make_vstring_plain(op, mode, -1)
  572. # value.setup_slice(vstr._chars, vstart.getint(),
  573. # vstop.getint())
  574. # return True
  575. #
  576. startbox = op.getarg(2)
  577. strbox = op.getarg(1)
  578. lengthbox = _int_sub(self.optimizer, op.getarg(3), op.getarg(2))
  579. #
  580. if isinstance(vstr, VStringSliceInfo):
  581. # double slicing s[i:j][k:l]
  582. strbox = vstr.s
  583. startbox = _int_add(self.optimizer, vstr.start, startbox)
  584. #
  585. self.make_vstring_slice(op, strbox, startbox, mode, lengthbox)
  586. self.last_emitted_operation = REMOVED
  587. return True
  588. @specialize.arg(2)
  589. def opt_call_stroruni_STR_EQUAL(self, op, mode):
  590. arg1 = self.get_box_replacement(op.getarg(1))
  591. arg2 = self.get_box_replacement(op.getarg(2))
  592. i1 = self.getptrinfo(arg1)
  593. i2 = self.getptrinfo(arg2)
  594. #
  595. if i1:
  596. l1box = i1.getstrlen(arg1, self, mode, create_ops=False)
  597. else:
  598. l1box = None
  599. if i2:
  600. l2box = i2.getstrlen(arg2, self, mode, create_ops=False)
  601. else:
  602. l2box = None
  603. if (l1box is not None and l2box is not None and
  604. isinstance(l1box, ConstInt) and
  605. isinstance(l2box, ConstInt) and
  606. l1box.value != l2box.value):
  607. # statically known to have a different length
  608. self.make_constant(op, CONST_0)
  609. return True
  610. #
  611. if self.handle_str_equal_level1(arg1, arg2, op, mode):
  612. return True
  613. if self.handle_str_equal_level1(arg2, arg1, op, mode):
  614. return True
  615. if self.handle_str_equal_level2(arg1, arg2, op, mode):
  616. return True
  617. if self.handle_str_equal_level2(arg2, arg1, op, mode):
  618. return True
  619. #
  620. if i1 and i1.is_nonnull() and i2 and i2.is_nonnull():
  621. if l1box is not None and l2box is not None and l1box.same_box(l2box):
  622. do = EffectInfo.OS_STREQ_LENGTHOK
  623. else:
  624. do = EffectInfo.OS_STREQ_NONNULL
  625. self.generate_modified_call(do, [arg1, arg2], op, mode)
  626. return True
  627. return False
  628. def handle_str_equal_level1(self, arg1, arg2, resultop, mode):
  629. i1 = self.getptrinfo(arg1)
  630. i2 = self.getptrinfo(arg2)
  631. l2box = None
  632. l1box = None
  633. if i2:
  634. l2box = i2.getstrlen(arg2, self, mode, create_ops=False)
  635. if isinstance(l2box, ConstInt):
  636. if l2box.value == 0:
  637. if i1 and i1.is_nonnull():
  638. self.make_nonnull_str(arg1, mode)
  639. i1 = self.getptrinfo(arg1)
  640. lengthbox = i1.getstrlen(arg1, self, mode)
  641. else:
  642. lengthbox = None
  643. if lengthbox is not None:
  644. seo = self.optimizer.send_extra_operation
  645. op = self.replace_op_with(resultop, rop.INT_EQ,
  646. [lengthbox, CONST_0],
  647. descr=DONT_CHANGE)
  648. seo(op)
  649. return True
  650. if l2box.value == 1:
  651. if i1:
  652. l1box = i1.getstrlen(arg1, self, mode, False)
  653. if isinstance(l1box, ConstInt) and l1box.value == 1:
  654. # comparing two single chars
  655. vchar1 = self.strgetitem(None, arg1, optimizer.CONST_0,
  656. mode)
  657. vchar2 = self.strgetitem(None, arg2, optimizer.CONST_0,
  658. mode)
  659. seo = self.optimizer.send_extra_operation
  660. op = self.optimizer.replace_op_with(resultop, rop.INT_EQ,
  661. [vchar1, vchar2], descr=DONT_CHANGE)
  662. seo(op)
  663. return True
  664. if isinstance(i1, VStringSliceInfo):
  665. vchar = self.strgetitem(None, arg2, optimizer.CONST_0,
  666. mode)
  667. do = EffectInfo.OS_STREQ_SLICE_CHAR
  668. self.generate_modified_call(do, [i1.s, i1.start,
  669. i1.lgtop, vchar],
  670. resultop, mode)
  671. return True
  672. #
  673. if i2 and i2.is_null():
  674. if i1 and i1.is_nonnull():
  675. self.make_constant(resultop, CONST_0)
  676. return True
  677. if i1 and i1.is_null():
  678. self.make_constant(resultop, CONST_1)
  679. return True
  680. op = self.optimizer.replace_op_with(resultop, rop.PTR_EQ,
  681. [arg1, llhelper.CONST_NULL],
  682. descr=DONT_CHANGE)
  683. self.emit_operation(op)
  684. return True
  685. #
  686. return False
  687. def handle_str_equal_level2(self, arg1, arg2, resultbox, mode):
  688. i1 = self.getptrinfo(arg1)
  689. i2 = self.getptrinfo(arg2)
  690. l2box = None
  691. if i2:
  692. l2box = i2.getstrlen(arg1, self, mode, create_ops=False)
  693. if l2box:
  694. l2info = self.getintbound(l2box)
  695. if l2info.is_constant():
  696. if l2info.getint() == 1:
  697. vchar = self.strgetitem(None, arg2, optimizer.CONST_0, mode)
  698. if i1 and i1.is_nonnull():
  699. do = EffectInfo.OS_STREQ_NONNULL_CHAR
  700. else:
  701. do = EffectInfo.OS_STREQ_CHECKNULL_CHAR
  702. self.generate_modified_call(do, [arg1, vchar],
  703. resultbox, mode)
  704. return True
  705. #
  706. if isinstance(i1, VStringSliceInfo) and i1.is_virtual():
  707. if i2 and i2.is_nonnull():
  708. do = EffectInfo.OS_STREQ_SLICE_NONNULL
  709. else:
  710. do = EffectInfo.OS_STREQ_SLICE_CHECKNULL
  711. self.generate_modified_call(do, [i1.s, i1.start, i1.lgtop,
  712. arg2], resultbox, mode)
  713. return True
  714. return False
  715. def opt_call_stroruni_STR_CMP(self, op, mode):
  716. i1 = self.getptrinfo(op.getarg(1))
  717. i2 = self.getptrinfo(op.getarg(2))
  718. if not i1 or not i2:
  719. return False
  720. l1box = i1.getstrlen(None, self, mode, False)
  721. l2box = i2.getstrlen(None, self, mode, False)
  722. if (l1box is not None and l2box is not None and
  723. isinstance(l1box, ConstInt) and
  724. isinstance(l2box, ConstInt) and
  725. l1box.getint() == l2box.getint() == 1):
  726. # comparing two single chars
  727. char1 = self.strgetitem(None, op.getarg(1), optimizer.CONST_0, mode)
  728. char2 = self.strgetitem(None, op.getarg(2), optimizer.CONST_0, mode)
  729. seo = self.optimizer.send_extra_operation
  730. op = self.replace_op_with(op, rop.INT_SUB, [char1, char2],
  731. descr=DONT_CHANGE)
  732. seo(op)
  733. return True
  734. return False
  735. def opt_call_SHRINK_ARRAY(self, op):
  736. i1 = self.getptrinfo(op.getarg(1))
  737. i2 = self.getintbound(op.getarg(2))
  738. # If the index is constant, if the argument is virtual (we only support
  739. # VStringPlainValue for now) we can optimize away the call.
  740. if (i2 and i2.is_constant() and i1 and i1.is_virtual() and
  741. isinstance(i1, VStringPlainInfo)):
  742. length = i2.getint()
  743. i1.shrink(length)
  744. self.last_emitted_operation = REMOVED
  745. self.make_equal_to(op, op.getarg(1))
  746. return True
  747. return False
  748. def generate_modified_call(self, oopspecindex, args, result, mode):
  749. oopspecindex += mode.OS_offset
  750. cic = self.optimizer.metainterp_sd.callinfocollection
  751. calldescr, func = cic.callinfo_for_oopspec(oopspecindex)
  752. op = self.optimizer.replace_op_with(result, rop.CALL_I,
  753. [ConstInt(func)] + args,
  754. descr=calldescr)
  755. self.emit_operation(op)
  756. def propagate_forward(self, op):
  757. dispatch_opt(self, op)
  758. dispatch_opt = make_dispatcher_method(OptString, 'optimize_',
  759. default=OptString.emit_operation)
  760. def _findall_call_oopspec():
  761. prefix = 'opt_call_stroruni_'
  762. result = []
  763. for name in dir(OptString):
  764. if name.startswith(prefix):
  765. value = getattr(EffectInfo, 'OS_' + name[len(prefix):])
  766. assert is_valid_int(value) and value != 0
  767. result.append((value, getattr(OptString, name)))
  768. return unrolling_iterable(result)
  769. opt_call_oopspec_ops = _findall_call_oopspec()