PageRenderTime 115ms CodeModel.GetById 16ms app.highlight 83ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/jit/codewriter/jtransform.py

https://bitbucket.org/pypy/pypy/
Python | 1489 lines | 1427 code | 29 blank | 33 comment | 62 complexity | e09e371286aa89e80140a582f16ed54b MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1import py
   2
   3from pypy.jit.codewriter import support, heaptracker, longlong
   4from pypy.jit.codewriter.effectinfo import EffectInfo
   5from pypy.jit.codewriter.flatten import ListOfKind, IndirectCallTargets
   6from pypy.jit.codewriter.policy import log
   7from pypy.jit.metainterp import quasiimmut
   8from pypy.jit.metainterp.history import getkind
   9from pypy.jit.metainterp.typesystem import deref, arrayItem
  10from pypy.jit.metainterp.blackhole import BlackholeInterpreter
  11from pypy.objspace.flow.model import SpaceOperation, Variable, Constant, c_last_exception
  12from pypy.rlib import objectmodel
  13from pypy.rlib.jit import _we_are_jitted
  14from pypy.rpython.lltypesystem import lltype, llmemory, rstr, rclass, rffi
  15from pypy.rpython.rclass import IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY
  16from pypy.translator.simplify import get_funcobj
  17from pypy.translator.unsimplify import varoftype
  18
  19class UnsupportedMallocFlags(Exception):
  20    pass
  21
  22def transform_graph(graph, cpu=None, callcontrol=None, portal_jd=None):
  23    """Transform a control flow graph to make it suitable for
  24    being flattened in a JitCode.
  25    """
  26    t = Transformer(cpu, callcontrol, portal_jd)
  27    t.transform(graph)
  28
  29def integer_bounds(size, unsigned):
  30    if unsigned:
  31        return 0, 1 << (8 * size)
  32    else:
  33        return -(1 << (8 * size - 1)), 1 << (8 * size - 1)
  34
  35class Transformer(object):
  36    vable_array_vars = None
  37
  38    def __init__(self, cpu=None, callcontrol=None, portal_jd=None):
  39        self.cpu = cpu
  40        self.callcontrol = callcontrol
  41        self.portal_jd = portal_jd   # non-None only for the portal graph(s)
  42
  43    def transform(self, graph):
  44        self.graph = graph
  45        for block in list(graph.iterblocks()):
  46            self.optimize_block(block)
  47
  48    def optimize_block(self, block):
  49        if block.operations == ():
  50            return
  51        self.vable_array_vars = {}
  52        self.vable_flags = {}
  53        renamings = {}
  54        renamings_constants = {}    # subset of 'renamings', {Var:Const} only
  55        newoperations = []
  56        #
  57        def do_rename(var, var_or_const):
  58            if var.concretetype is lltype.Void:
  59                renamings[var] = Constant(None, lltype.Void)
  60                return
  61            renamings[var] = var_or_const
  62            if isinstance(var_or_const, Constant):
  63                value = var_or_const.value
  64                value = lltype._cast_whatever(var.concretetype, value)
  65                renamings_constants[var] = Constant(value, var.concretetype)
  66        #
  67        for op in block.operations:
  68            if renamings_constants:
  69                op = self._do_renaming(renamings_constants, op)
  70            oplist = self.rewrite_operation(op)
  71            #
  72            count_before_last_operation = len(newoperations)
  73            if not isinstance(oplist, list):
  74                oplist = [oplist]
  75            for op1 in oplist:
  76                if isinstance(op1, SpaceOperation):
  77                    newoperations.append(self._do_renaming(renamings, op1))
  78                elif op1 is None:
  79                    # rewrite_operation() returns None to mean "has no real
  80                    # effect, the result should just be renamed to args[0]"
  81                    if op.result is not None:
  82                        do_rename(op.result, renamings.get(op.args[0],
  83                                                           op.args[0]))
  84                elif isinstance(op1, Constant):
  85                    do_rename(op.result, op1)
  86                else:
  87                    raise TypeError(repr(op1))
  88        #
  89        if block.exitswitch == c_last_exception:
  90            if len(newoperations) == count_before_last_operation:
  91                self._killed_exception_raising_operation(block)
  92        block.operations = newoperations
  93        block.exitswitch = renamings.get(block.exitswitch, block.exitswitch)
  94        self.follow_constant_exit(block)
  95        self.optimize_goto_if_not(block)
  96        for link in block.exits:
  97            self._check_no_vable_array(link.args)
  98            self._do_renaming_on_link(renamings, link)
  99
 100    def _do_renaming(self, rename, op):
 101        op = SpaceOperation(op.opname, op.args[:], op.result)
 102        for i, v in enumerate(op.args):
 103            if isinstance(v, Variable):
 104                if v in rename:
 105                    op.args[i] = rename[v]
 106            elif isinstance(v, ListOfKind):
 107                newlst = []
 108                for x in v:
 109                    if x in rename:
 110                        x = rename[x]
 111                    newlst.append(x)
 112                op.args[i] = ListOfKind(v.kind, newlst)
 113        return op
 114
 115    def _check_no_vable_array(self, list):
 116        if not self.vable_array_vars:
 117            return
 118        for v in list:
 119            if v in self.vable_array_vars:
 120                raise AssertionError(
 121                    "A virtualizable array is passed around; it should\n"
 122                    "only be used immediately after being read.  Note\n"
 123                    "that a possible cause is indexing with an index not\n"
 124                    "known non-negative, or catching IndexError, or\n"
 125                    "not inlining at all (for tests: use listops=True).\n"
 126                    "Occurred in: %r" % self.graph)
 127            # extra explanation: with the way things are organized in
 128            # rpython/rlist.py, the ll_getitem becomes a function call
 129            # that is typically meant to be inlined by the JIT, but
 130            # this does not work with vable arrays because
 131            # jtransform.py expects the getfield and the getarrayitem
 132            # to be in the same basic block.  It works a bit as a hack
 133            # for simple cases where we performed the backendopt
 134            # inlining before (even with a very low threshold, because
 135            # there is _always_inline_ on the relevant functions).
 136
 137    def _do_renaming_on_link(self, rename, link):
 138        for i, v in enumerate(link.args):
 139            if isinstance(v, Variable):
 140                if v in rename:
 141                    link.args[i] = rename[v]
 142
 143    def _killed_exception_raising_operation(self, block):
 144        assert block.exits[0].exitcase is None
 145        block.exits = block.exits[:1]
 146        block.exitswitch = None
 147
 148    # ----------
 149
 150    def follow_constant_exit(self, block):
 151        v = block.exitswitch
 152        if isinstance(v, Constant) and v != c_last_exception:
 153            llvalue = v.value
 154            for link in block.exits:
 155                if link.llexitcase == llvalue:
 156                    break
 157            else:
 158                assert link.exitcase == 'default'
 159            block.exitswitch = None
 160            link.exitcase = link.llexitcase = None
 161            block.recloseblock(link)
 162
 163    def optimize_goto_if_not(self, block):
 164        """Replace code like 'v = int_gt(x,y); exitswitch = v'
 165           with just 'exitswitch = ('int_gt',x,y)'."""
 166        if len(block.exits) != 2:
 167            return False
 168        v = block.exitswitch
 169        if (v == c_last_exception or isinstance(v, tuple)
 170            or v.concretetype != lltype.Bool):
 171            return False
 172        for op in block.operations[::-1]:
 173            if v in op.args:
 174                return False   # variable is also used in cur block
 175            if v is op.result:
 176                if op.opname not in ('int_lt', 'int_le', 'int_eq', 'int_ne',
 177                                     'int_gt', 'int_ge',
 178                                     'int_is_zero', 'int_is_true',
 179                                     'ptr_eq', 'ptr_ne',
 180                                     'ptr_iszero', 'ptr_nonzero'):
 181                    return False    # not a supported operation
 182                # ok! optimize this case
 183                block.operations.remove(op)
 184                block.exitswitch = (op.opname,) + tuple(op.args)
 185                if op.opname in ('ptr_iszero', 'ptr_nonzero'):
 186                    block.exitswitch += ('-live-before',)
 187                # if the variable escape to the next block along a link,
 188                # replace it with a constant, because we know its value
 189                for link in block.exits:
 190                    while v in link.args:
 191                        index = link.args.index(v)
 192                        link.args[index] = Constant(link.llexitcase,
 193                                                    lltype.Bool)
 194                return True
 195        return False
 196
 197    # ----------
 198
 199    def rewrite_operation(self, op):
 200        try:
 201            rewrite = _rewrite_ops[op.opname]
 202        except KeyError:
 203            raise Exception("the JIT doesn't support the operation %r"
 204                            " in %r" % (op, getattr(self, 'graph', '?')))
 205        return rewrite(self, op)
 206
 207    def rewrite_op_same_as(self, op):
 208        if op.args[0] in self.vable_array_vars:
 209            self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
 210
 211    def rewrite_op_cast_pointer(self, op):
 212        newop = self.rewrite_op_same_as(op)
 213        assert newop is None
 214        return
 215        # disabled for now
 216        if (self._is_rclass_instance(op.args[0]) and
 217                self._is_rclass_instance(op.result)):
 218            FROM = op.args[0].concretetype.TO
 219            TO = op.result.concretetype.TO
 220            if lltype._castdepth(TO, FROM) > 0:
 221                vtable = heaptracker.get_vtable_for_gcstruct(self.cpu, TO)
 222                const_vtable = Constant(vtable, lltype.typeOf(vtable))
 223                return [None, # hack, do the right renaming from op.args[0] to op.result
 224                        SpaceOperation("record_known_class", [op.args[0], const_vtable], None)]
 225
 226    def rewrite_op_jit_record_known_class(self, op):
 227        return SpaceOperation("record_known_class", [op.args[0], op.args[1]], None)
 228
 229    def rewrite_op_cast_bool_to_int(self, op): pass
 230    def rewrite_op_cast_bool_to_uint(self, op): pass
 231    def rewrite_op_cast_char_to_int(self, op): pass
 232    def rewrite_op_cast_unichar_to_int(self, op): pass
 233    def rewrite_op_cast_int_to_char(self, op): pass
 234    def rewrite_op_cast_int_to_unichar(self, op): pass
 235    def rewrite_op_cast_int_to_uint(self, op): pass
 236    def rewrite_op_cast_uint_to_int(self, op): pass
 237
 238    def _rewrite_symmetric(self, op):
 239        """Rewrite 'c1+v2' into 'v2+c1' in an attempt to avoid generating
 240        too many variants of the bytecode."""
 241        if (isinstance(op.args[0], Constant) and
 242            isinstance(op.args[1], Variable)):
 243            reversename = {'int_lt': 'int_gt',
 244                           'int_le': 'int_ge',
 245                           'int_gt': 'int_lt',
 246                           'int_ge': 'int_le',
 247                           'uint_lt': 'uint_gt',
 248                           'uint_le': 'uint_ge',
 249                           'uint_gt': 'uint_lt',
 250                           'uint_ge': 'uint_le',
 251                           'float_lt': 'float_gt',
 252                           'float_le': 'float_ge',
 253                           'float_gt': 'float_lt',
 254                           'float_ge': 'float_le',
 255                           }.get(op.opname, op.opname)
 256            return SpaceOperation(reversename,
 257                                  [op.args[1], op.args[0]] + op.args[2:],
 258                                  op.result)
 259        else:
 260            return op
 261
 262    rewrite_op_int_add = _rewrite_symmetric
 263    rewrite_op_int_mul = _rewrite_symmetric
 264    rewrite_op_int_and = _rewrite_symmetric
 265    rewrite_op_int_or  = _rewrite_symmetric
 266    rewrite_op_int_xor = _rewrite_symmetric
 267    rewrite_op_int_lt  = _rewrite_symmetric
 268    rewrite_op_int_le  = _rewrite_symmetric
 269    rewrite_op_int_gt  = _rewrite_symmetric
 270    rewrite_op_int_ge  = _rewrite_symmetric
 271    rewrite_op_uint_lt = _rewrite_symmetric
 272    rewrite_op_uint_le = _rewrite_symmetric
 273    rewrite_op_uint_gt = _rewrite_symmetric
 274    rewrite_op_uint_ge = _rewrite_symmetric
 275
 276    rewrite_op_float_add = _rewrite_symmetric
 277    rewrite_op_float_mul = _rewrite_symmetric
 278    rewrite_op_float_lt  = _rewrite_symmetric
 279    rewrite_op_float_le  = _rewrite_symmetric
 280    rewrite_op_float_gt  = _rewrite_symmetric
 281    rewrite_op_float_ge  = _rewrite_symmetric
 282
 283    def rewrite_op_int_add_ovf(self, op):
 284        op0 = self._rewrite_symmetric(op)
 285        op1 = SpaceOperation('-live-', [], None)
 286        return [op0, op1]
 287
 288    rewrite_op_int_mul_ovf = rewrite_op_int_add_ovf
 289
 290    def rewrite_op_int_sub_ovf(self, op):
 291        op1 = SpaceOperation('-live-', [], None)
 292        return [op, op1]
 293
 294    # ----------
 295    # Various kinds of calls
 296
 297    def rewrite_op_direct_call(self, op):
 298        kind = self.callcontrol.guess_call_kind(op)
 299        return getattr(self, 'handle_%s_call' % kind)(op)
 300
 301    def rewrite_op_indirect_call(self, op):
 302        kind = self.callcontrol.guess_call_kind(op)
 303        return getattr(self, 'handle_%s_indirect_call' % kind)(op)
 304
 305    def rewrite_call(self, op, namebase, initialargs, args=None):
 306        """Turn 'i0 = direct_call(fn, i1, i2, ref1, ref2)'
 307           into 'i0 = xxx_call_ir_i(fn, descr, [i1,i2], [ref1,ref2])'.
 308           The name is one of '{residual,direct}_call_{r,ir,irf}_{i,r,f,v}'."""
 309        if args is None:
 310            args = op.args[1:]
 311        self._check_no_vable_array(args)
 312        lst_i, lst_r, lst_f = self.make_three_lists(args)
 313        reskind = getkind(op.result.concretetype)[0]
 314        if lst_f or reskind == 'f': kinds = 'irf'
 315        elif lst_i: kinds = 'ir'
 316        else: kinds = 'r'
 317        sublists = []
 318        if 'i' in kinds: sublists.append(lst_i)
 319        if 'r' in kinds: sublists.append(lst_r)
 320        if 'f' in kinds: sublists.append(lst_f)
 321        return SpaceOperation('%s_%s_%s' % (namebase, kinds, reskind),
 322                              initialargs + sublists, op.result)
 323
 324    def make_three_lists(self, vars):
 325        args_i = []
 326        args_r = []
 327        args_f = []
 328        for v in vars:
 329            self.add_in_correct_list(v, args_i, args_r, args_f)
 330        return [ListOfKind('int', args_i),
 331                ListOfKind('ref', args_r),
 332                ListOfKind('float', args_f)]
 333
 334    def add_in_correct_list(self, v, lst_i, lst_r, lst_f):
 335        kind = getkind(v.concretetype)
 336        if kind == 'void': return
 337        elif kind == 'int': lst = lst_i
 338        elif kind == 'ref': lst = lst_r
 339        elif kind == 'float': lst = lst_f
 340        else: raise AssertionError(kind)
 341        lst.append(v)
 342
 343    def handle_residual_call(self, op, extraargs=[], may_call_jitcodes=False):
 344        """A direct_call turns into the operation 'residual_call_xxx' if it
 345        is calling a function that we don't want to JIT.  The initial args
 346        of 'residual_call_xxx' are the function to call, and its calldescr."""
 347        calldescr = self.callcontrol.getcalldescr(op)
 348        op1 = self.rewrite_call(op, 'residual_call',
 349                                [op.args[0], calldescr] + extraargs)
 350        if may_call_jitcodes or self.callcontrol.calldescr_canraise(calldescr):
 351            op1 = [op1, SpaceOperation('-live-', [], None)]
 352        return op1
 353
 354    def handle_regular_call(self, op):
 355        """A direct_call turns into the operation 'inline_call_xxx' if it
 356        is calling a function that we want to JIT.  The initial arg of
 357        'inline_call_xxx' is the JitCode of the called function."""
 358        [targetgraph] = self.callcontrol.graphs_from(op)
 359        jitcode = self.callcontrol.get_jitcode(targetgraph,
 360                                               called_from=self.graph)
 361        op0 = self.rewrite_call(op, 'inline_call', [jitcode])
 362        op1 = SpaceOperation('-live-', [], None)
 363        return [op0, op1]
 364
 365    def handle_builtin_call(self, op):
 366        oopspec_name, args = support.decode_builtin_call(op)
 367        # dispatch to various implementations depending on the oopspec_name
 368        if oopspec_name.startswith('list.') or oopspec_name == 'newlist':
 369            prepare = self._handle_list_call
 370        elif oopspec_name.startswith('stroruni.'):
 371            prepare = self._handle_stroruni_call
 372        elif oopspec_name == 'str.str2unicode':
 373            prepare = self._handle_str2unicode_call
 374        elif oopspec_name.startswith('virtual_ref'):
 375            prepare = self._handle_virtual_ref_call
 376        elif oopspec_name.startswith('jit.'):
 377            prepare = self._handle_jit_call
 378        elif oopspec_name.startswith('libffi_'):
 379            prepare = self._handle_libffi_call
 380        elif oopspec_name.startswith('math.sqrt'):
 381            prepare = self._handle_math_sqrt_call
 382        else:
 383            prepare = self.prepare_builtin_call
 384        try:
 385            op1 = prepare(op, oopspec_name, args)
 386        except NotSupported:
 387            op1 = op
 388        # If the resulting op1 is still a direct_call, turn it into a
 389        # residual_call.
 390        if isinstance(op1, SpaceOperation) and op1.opname == 'direct_call':
 391            op1 = self.handle_residual_call(op1)
 392        return op1
 393
 394    def handle_recursive_call(self, op):
 395        jitdriver_sd = self.callcontrol.jitdriver_sd_from_portal_runner_ptr(
 396            op.args[0].value)
 397        assert jitdriver_sd is not None
 398        ops = self.promote_greens(op.args[1:], jitdriver_sd.jitdriver)
 399        num_green_args = len(jitdriver_sd.jitdriver.greens)
 400        args = ([Constant(jitdriver_sd.index, lltype.Signed)] +
 401                self.make_three_lists(op.args[1:1+num_green_args]) +
 402                self.make_three_lists(op.args[1+num_green_args:]))
 403        kind = getkind(op.result.concretetype)[0]
 404        op0 = SpaceOperation('recursive_call_%s' % kind, args, op.result)
 405        op1 = SpaceOperation('-live-', [], None)
 406        return ops + [op0, op1]
 407
 408    handle_residual_indirect_call = handle_residual_call
 409
 410    def handle_regular_indirect_call(self, op):
 411        """An indirect call where at least one target has a JitCode."""
 412        lst = []
 413        for targetgraph in self.callcontrol.graphs_from(op):
 414            jitcode = self.callcontrol.get_jitcode(targetgraph,
 415                                                   called_from=self.graph)
 416            lst.append(jitcode)
 417        op0 = SpaceOperation('-live-', [], None)
 418        op1 = SpaceOperation('int_guard_value', [op.args[0]], None)
 419        op2 = self.handle_residual_call(op, [IndirectCallTargets(lst)], True)
 420        result = [op0, op1]
 421        if isinstance(op2, list):
 422            result += op2
 423        else:
 424            result.append(op2)
 425        return result
 426
 427    def prepare_builtin_call(self, op, oopspec_name, args,
 428                              extra=None, extrakey=None):
 429        argtypes = [v.concretetype for v in args]
 430        resulttype = op.result.concretetype
 431        c_func, TP = support.builtin_func_for_spec(self.cpu.rtyper,
 432                                                   oopspec_name, argtypes,
 433                                                   resulttype, extra, extrakey)
 434        return SpaceOperation('direct_call', [c_func] + args, op.result)
 435
 436    def _do_builtin_call(self, op, oopspec_name=None, args=None,
 437                         extra=None, extrakey=None):
 438        if oopspec_name is None: oopspec_name = op.opname
 439        if args is None: args = op.args
 440        op1 = self.prepare_builtin_call(op, oopspec_name, args,
 441                                        extra, extrakey)
 442        return self.rewrite_op_direct_call(op1)
 443
 444    # XXX some of the following functions should not become residual calls
 445    # but be really compiled
 446    rewrite_op_int_floordiv_ovf_zer = _do_builtin_call
 447    rewrite_op_int_floordiv_ovf     = _do_builtin_call
 448    rewrite_op_int_floordiv_zer     = _do_builtin_call
 449    rewrite_op_int_mod_ovf_zer = _do_builtin_call
 450    rewrite_op_int_mod_ovf     = _do_builtin_call
 451    rewrite_op_int_mod_zer     = _do_builtin_call
 452    rewrite_op_int_lshift_ovf  = _do_builtin_call
 453    rewrite_op_int_abs         = _do_builtin_call
 454    rewrite_op_llong_abs          = _do_builtin_call
 455    rewrite_op_llong_floordiv     = _do_builtin_call
 456    rewrite_op_llong_floordiv_zer = _do_builtin_call
 457    rewrite_op_llong_mod          = _do_builtin_call
 458    rewrite_op_llong_mod_zer      = _do_builtin_call
 459    rewrite_op_ullong_floordiv     = _do_builtin_call
 460    rewrite_op_ullong_floordiv_zer = _do_builtin_call
 461    rewrite_op_ullong_mod          = _do_builtin_call
 462    rewrite_op_ullong_mod_zer      = _do_builtin_call
 463    rewrite_op_gc_identityhash = _do_builtin_call
 464    rewrite_op_gc_id           = _do_builtin_call
 465    rewrite_op_uint_mod        = _do_builtin_call
 466    rewrite_op_cast_float_to_uint = _do_builtin_call
 467    rewrite_op_cast_uint_to_float = _do_builtin_call
 468
 469    # ----------
 470    # getfield/setfield/mallocs etc.
 471
 472    def rewrite_op_hint(self, op):
 473        hints = op.args[1].value
 474        if hints.get('promote') and op.args[0].concretetype is not lltype.Void:
 475            assert op.args[0].concretetype != lltype.Ptr(rstr.STR)
 476            kind = getkind(op.args[0].concretetype)
 477            op0 = SpaceOperation('-live-', [], None)
 478            op1 = SpaceOperation('%s_guard_value' % kind, [op.args[0]], None)
 479            # the special return value None forces op.result to be considered
 480            # equal to op.args[0]
 481            return [op0, op1, None]
 482        if (hints.get('promote_string') and
 483            op.args[0].concretetype is not lltype.Void):
 484            S = lltype.Ptr(rstr.STR)
 485            assert op.args[0].concretetype == S
 486            self._register_extra_helper(EffectInfo.OS_STREQ_NONNULL,
 487                                        "str.eq_nonnull",
 488                                        [S, S],
 489                                        lltype.Signed,
 490                                        EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
 491            descr, p = self.callcontrol.callinfocollection.callinfo_for_oopspec(
 492                EffectInfo.OS_STREQ_NONNULL)
 493            # XXX this is fairly ugly way of creating a constant,
 494            #     however, callinfocollection has no better interface
 495            c = Constant(p.adr.ptr, lltype.typeOf(p.adr.ptr))
 496            op1 = SpaceOperation('str_guard_value', [op.args[0], c, descr],
 497                                 op.result)
 498            return [SpaceOperation('-live-', [], None), op1, None]
 499        else:
 500            log.WARNING('ignoring hint %r at %r' % (hints, self.graph))
 501
 502    def _rewrite_raw_malloc(self, op, name, args):
 503        d = op.args[1].value.copy()
 504        d.pop('flavor')
 505        add_memory_pressure = d.pop('add_memory_pressure', False)
 506        zero = d.pop('zero', False)
 507        track_allocation = d.pop('track_allocation', True)
 508        if d:
 509            raise UnsupportedMallocFlags(d)
 510        TYPE = op.args[0].value
 511        if zero:
 512            name += '_zero'
 513        if add_memory_pressure:
 514            name += '_add_memory_pressure'
 515        if not track_allocation:
 516            name += '_no_track_allocation'
 517        return self._do_builtin_call(op, name, args,
 518                                     extra = (TYPE,),
 519                                     extrakey = TYPE)
 520
 521    def rewrite_op_malloc_varsize(self, op):
 522        if op.args[1].value['flavor'] == 'raw':
 523            return self._rewrite_raw_malloc(op, 'raw_malloc_varsize',
 524                                            [op.args[2]])
 525        if op.args[0].value == rstr.STR:
 526            return SpaceOperation('newstr', [op.args[2]], op.result)
 527        elif op.args[0].value == rstr.UNICODE:
 528            return SpaceOperation('newunicode', [op.args[2]], op.result)
 529        else:
 530            # XXX only strings or simple arrays for now
 531            ARRAY = op.args[0].value
 532            arraydescr = self.cpu.arraydescrof(ARRAY)
 533            return SpaceOperation('new_array', [arraydescr, op.args[2]],
 534                                  op.result)
 535
 536    def rewrite_op_free(self, op):
 537        d = op.args[1].value.copy()
 538        assert d['flavor'] == 'raw'
 539        d.pop('flavor')
 540        track_allocation = d.pop('track_allocation', True)
 541        if d:
 542            raise UnsupportedMallocFlags(d)
 543        STRUCT = op.args[0].concretetype.TO
 544        name = 'raw_free'
 545        if not track_allocation:
 546            name += '_no_track_allocation'
 547        return self._do_builtin_call(op, name, [op.args[0]],
 548                                     extra = (STRUCT,), extrakey = STRUCT)
 549
 550    def rewrite_op_getarrayitem(self, op):
 551        ARRAY = op.args[0].concretetype.TO
 552        if self._array_of_voids(ARRAY):
 553            return []
 554        if op.args[0] in self.vable_array_vars:     # for virtualizables
 555            vars = self.vable_array_vars[op.args[0]]
 556            (v_base, arrayfielddescr, arraydescr) = vars
 557            kind = getkind(op.result.concretetype)
 558            return [SpaceOperation('-live-', [], None),
 559                    SpaceOperation('getarrayitem_vable_%s' % kind[0],
 560                                   [v_base, arrayfielddescr, arraydescr,
 561                                    op.args[1]], op.result)]
 562        # normal case follows
 563        arraydescr = self.cpu.arraydescrof(ARRAY)
 564        kind = getkind(op.result.concretetype)
 565        return SpaceOperation('getarrayitem_%s_%s' % (ARRAY._gckind, kind[0]),
 566                              [op.args[0], arraydescr, op.args[1]],
 567                              op.result)
 568
 569    def rewrite_op_setarrayitem(self, op):
 570        ARRAY = op.args[0].concretetype.TO
 571        if self._array_of_voids(ARRAY):
 572            return []
 573        if op.args[0] in self.vable_array_vars:     # for virtualizables
 574            vars = self.vable_array_vars[op.args[0]]
 575            (v_base, arrayfielddescr, arraydescr) = vars
 576            kind = getkind(op.args[2].concretetype)
 577            return [SpaceOperation('-live-', [], None),
 578                    SpaceOperation('setarrayitem_vable_%s' % kind[0],
 579                                   [v_base, arrayfielddescr, arraydescr,
 580                                    op.args[1], op.args[2]], None)]
 581        arraydescr = self.cpu.arraydescrof(ARRAY)
 582        kind = getkind(op.args[2].concretetype)
 583        return SpaceOperation('setarrayitem_%s_%s' % (ARRAY._gckind, kind[0]),
 584                              [op.args[0], arraydescr, op.args[1], op.args[2]],
 585                              None)
 586
 587    def rewrite_op_getarraysize(self, op):
 588        ARRAY = op.args[0].concretetype.TO
 589        assert ARRAY._gckind == 'gc'
 590        if op.args[0] in self.vable_array_vars:     # for virtualizables
 591            vars = self.vable_array_vars[op.args[0]]
 592            (v_base, arrayfielddescr, arraydescr) = vars
 593            return [SpaceOperation('-live-', [], None),
 594                    SpaceOperation('arraylen_vable',
 595                                   [v_base, arrayfielddescr, arraydescr],
 596                                   op.result)]
 597        # normal case follows
 598        arraydescr = self.cpu.arraydescrof(ARRAY)
 599        return SpaceOperation('arraylen_gc', [op.args[0], arraydescr],
 600                              op.result)
 601
 602    def _array_of_voids(self, ARRAY):
 603        #if isinstance(ARRAY, ootype.Array):
 604        #    return ARRAY.ITEM == ootype.Void
 605        #else:
 606        return ARRAY.OF == lltype.Void
 607
 608    def rewrite_op_getfield(self, op):
 609        if self.is_typeptr_getset(op):
 610            return self.handle_getfield_typeptr(op)
 611        # turn the flow graph 'getfield' operation into our own version
 612        [v_inst, c_fieldname] = op.args
 613        RESULT = op.result.concretetype
 614        if RESULT is lltype.Void:
 615            return
 616        # check for virtualizable
 617        try:
 618            if self.is_virtualizable_getset(op):
 619                descr = self.get_virtualizable_field_descr(op)
 620                kind = getkind(RESULT)[0]
 621                return [SpaceOperation('-live-', [], None),
 622                        SpaceOperation('getfield_vable_%s' % kind,
 623                                       [v_inst, descr], op.result)]
 624        except VirtualizableArrayField, e:
 625            # xxx hack hack hack
 626            vinfo = e.args[1]
 627            arrayindex = vinfo.array_field_counter[op.args[1].value]
 628            arrayfielddescr = vinfo.array_field_descrs[arrayindex]
 629            arraydescr = vinfo.array_descrs[arrayindex]
 630            self.vable_array_vars[op.result] = (op.args[0],
 631                                                arrayfielddescr,
 632                                                arraydescr)
 633            return []
 634        # check for _immutable_fields_ hints
 635        immut = v_inst.concretetype.TO._immutable_field(c_fieldname.value)
 636        if immut:
 637            if (self.callcontrol is not None and
 638                self.callcontrol.could_be_green_field(v_inst.concretetype.TO,
 639                                                      c_fieldname.value)):
 640                pure = '_greenfield'
 641            else:
 642                pure = '_pure'
 643        else:
 644            pure = ''
 645        self.check_field_access(v_inst.concretetype.TO)
 646        argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
 647        descr = self.cpu.fielddescrof(v_inst.concretetype.TO,
 648                                      c_fieldname.value)
 649        kind = getkind(RESULT)[0]
 650        op1 = SpaceOperation('getfield_%s_%s%s' % (argname, kind, pure),
 651                             [v_inst, descr], op.result)
 652        #
 653        if immut in (IR_QUASIIMMUTABLE, IR_QUASIIMMUTABLE_ARRAY):
 654            descr1 = self.cpu.fielddescrof(
 655                v_inst.concretetype.TO,
 656                quasiimmut.get_mutate_field_name(c_fieldname.value))
 657            op1 = [SpaceOperation('-live-', [], None),
 658                   SpaceOperation('record_quasiimmut_field',
 659                                  [v_inst, descr, descr1], None),
 660                   op1]
 661        return op1
 662
 663    def rewrite_op_setfield(self, op):
 664        if self.is_typeptr_getset(op):
 665            # ignore the operation completely -- instead, it's done by 'new'
 666            return
 667        # turn the flow graph 'setfield' operation into our own version
 668        [v_inst, c_fieldname, v_value] = op.args
 669        RESULT = v_value.concretetype
 670        if RESULT is lltype.Void:
 671            return
 672        # check for virtualizable
 673        if self.is_virtualizable_getset(op):
 674            descr = self.get_virtualizable_field_descr(op)
 675            kind = getkind(RESULT)[0]
 676            return [SpaceOperation('-live-', [], None),
 677                    SpaceOperation('setfield_vable_%s' % kind,
 678                                   [v_inst, descr, v_value], None)]
 679        self.check_field_access(v_inst.concretetype.TO)
 680        argname = getattr(v_inst.concretetype.TO, '_gckind', 'gc')
 681        descr = self.cpu.fielddescrof(v_inst.concretetype.TO,
 682                                      c_fieldname.value)
 683        kind = getkind(RESULT)[0]
 684        return SpaceOperation('setfield_%s_%s' % (argname, kind),
 685                              [v_inst, descr, v_value],
 686                              None)
 687
 688    def is_typeptr_getset(self, op):
 689        return (op.args[1].value == 'typeptr' and
 690                op.args[0].concretetype.TO._hints.get('typeptr'))
 691
 692    def check_field_access(self, STRUCT):
 693        # check against a GcStruct with a nested GcStruct as a first argument
 694        # but which is not an object at all; see metainterp/test/test_loop,
 695        # test_regular_pointers_in_short_preamble.
 696        if not isinstance(STRUCT, lltype.GcStruct):
 697            return
 698        if STRUCT._first_struct() == (None, None):
 699            return
 700        PARENT = STRUCT
 701        while not PARENT._hints.get('typeptr'):
 702            _, PARENT = PARENT._first_struct()
 703            if PARENT is None:
 704                raise NotImplementedError("%r is a GcStruct using nesting but "
 705                                          "not inheriting from object" %
 706                                          (STRUCT,))
 707
 708    def get_vinfo(self, v_virtualizable):
 709        if self.callcontrol is None:      # for tests
 710            return None
 711        return self.callcontrol.get_vinfo(v_virtualizable.concretetype)
 712
 713    def is_virtualizable_getset(self, op):
 714        # every access of an object of exactly the type VTYPEPTR is
 715        # likely to be a virtualizable access, but we still have to
 716        # check it in pyjitpl.py.
 717        vinfo = self.get_vinfo(op.args[0])
 718        if vinfo is None:
 719            return False
 720        res = False
 721        if op.args[1].value in vinfo.static_field_to_extra_box:
 722            res = True
 723        if op.args[1].value in vinfo.array_fields:
 724            res = VirtualizableArrayField(self.graph, vinfo)
 725
 726        if res:
 727            flags = self.vable_flags[op.args[0]]
 728            if 'fresh_virtualizable' in flags:
 729                return False
 730        if isinstance(res, Exception):
 731            raise res
 732        return res
 733
 734    def get_virtualizable_field_descr(self, op):
 735        fieldname = op.args[1].value
 736        vinfo = self.get_vinfo(op.args[0])
 737        index = vinfo.static_field_to_extra_box[fieldname]
 738        return vinfo.static_field_descrs[index]
 739
 740    def handle_getfield_typeptr(self, op):
 741        if isinstance(op.args[0], Constant):
 742            cls = op.args[0].value.typeptr
 743            return Constant(cls, concretetype=rclass.CLASSTYPE)
 744        op0 = SpaceOperation('-live-', [], None)
 745        op1 = SpaceOperation('guard_class', [op.args[0]], op.result)
 746        return [op0, op1]
 747
 748    def rewrite_op_malloc(self, op):
 749        if op.args[1].value['flavor'] == 'raw':
 750            return self._rewrite_raw_malloc(op, 'raw_malloc_fixedsize', [])
 751        #
 752        assert op.args[1].value == {'flavor': 'gc'}
 753        STRUCT = op.args[0].value
 754        vtable = heaptracker.get_vtable_for_gcstruct(self.cpu, STRUCT)
 755        if vtable:
 756            # do we have a __del__?
 757            try:
 758                rtti = lltype.getRuntimeTypeInfo(STRUCT)
 759            except ValueError:
 760                pass
 761            else:
 762                if hasattr(rtti._obj, 'destructor_funcptr'):
 763                    RESULT = lltype.Ptr(STRUCT)
 764                    assert RESULT == op.result.concretetype
 765                    return self._do_builtin_call(op, 'alloc_with_del', [],
 766                                                 extra = (RESULT, vtable),
 767                                                 extrakey = STRUCT)
 768            heaptracker.register_known_gctype(self.cpu, vtable, STRUCT)
 769            opname = 'new_with_vtable'
 770        else:
 771            opname = 'new'
 772        sizedescr = self.cpu.sizeof(STRUCT)
 773        return SpaceOperation(opname, [sizedescr], op.result)
 774
 775    def rewrite_op_getinteriorarraysize(self, op):
 776        # only supports strings and unicodes
 777        assert len(op.args) == 2
 778        assert op.args[1].value == 'chars'
 779        optype = op.args[0].concretetype
 780        if optype == lltype.Ptr(rstr.STR):
 781            opname = "strlen"
 782        else:
 783            assert optype == lltype.Ptr(rstr.UNICODE)
 784            opname = "unicodelen"
 785        return SpaceOperation(opname, [op.args[0]], op.result)
 786
 787    def rewrite_op_getinteriorfield(self, op):
 788        assert len(op.args) == 3
 789        optype = op.args[0].concretetype
 790        if optype == lltype.Ptr(rstr.STR):
 791            opname = "strgetitem"
 792            return SpaceOperation(opname, [op.args[0], op.args[2]], op.result)
 793        elif optype == lltype.Ptr(rstr.UNICODE):
 794            opname = "unicodegetitem"
 795            return SpaceOperation(opname, [op.args[0], op.args[2]], op.result)
 796        else:
 797            v_inst, v_index, c_field = op.args
 798            if op.result.concretetype is lltype.Void:
 799                return
 800            # only GcArray of Struct supported
 801            assert isinstance(v_inst.concretetype.TO, lltype.GcArray)
 802            STRUCT = v_inst.concretetype.TO.OF
 803            assert isinstance(STRUCT, lltype.Struct)
 804            descr = self.cpu.interiorfielddescrof(v_inst.concretetype.TO,
 805                                                  c_field.value)
 806            args = [v_inst, v_index, descr]
 807            kind = getkind(op.result.concretetype)[0]
 808            return SpaceOperation('getinteriorfield_gc_%s' % kind, args,
 809                                  op.result)
 810
 811    def rewrite_op_setinteriorfield(self, op):
 812        assert len(op.args) == 4
 813        optype = op.args[0].concretetype
 814        if optype == lltype.Ptr(rstr.STR):
 815            opname = "strsetitem"
 816            return SpaceOperation(opname, [op.args[0], op.args[2], op.args[3]],
 817                                  op.result)
 818        elif optype == lltype.Ptr(rstr.UNICODE):
 819            opname = "unicodesetitem"
 820            return SpaceOperation(opname, [op.args[0], op.args[2], op.args[3]],
 821                                  op.result)
 822        else:
 823            v_inst, v_index, c_field, v_value = op.args
 824            if v_value.concretetype is lltype.Void:
 825                return
 826            # only GcArray of Struct supported
 827            assert isinstance(v_inst.concretetype.TO, lltype.GcArray)
 828            STRUCT = v_inst.concretetype.TO.OF
 829            assert isinstance(STRUCT, lltype.Struct)
 830            descr = self.cpu.interiorfielddescrof(v_inst.concretetype.TO,
 831                                                  c_field.value)
 832            kind = getkind(v_value.concretetype)[0]
 833            args = [v_inst, v_index, v_value, descr]
 834            return SpaceOperation('setinteriorfield_gc_%s' % kind, args,
 835                                  op.result)
 836
 837    def _rewrite_equality(self, op, opname):
 838        arg0, arg1 = op.args
 839        if isinstance(arg0, Constant) and not arg0.value:
 840            return SpaceOperation(opname, [arg1], op.result)
 841        elif isinstance(arg1, Constant) and not arg1.value:
 842            return SpaceOperation(opname, [arg0], op.result)
 843        else:
 844            return self._rewrite_symmetric(op)
 845
 846    def _is_gc(self, v):
 847        return getattr(getattr(v.concretetype, "TO", None), "_gckind", "?") == 'gc'
 848
 849    def _is_rclass_instance(self, v):
 850        return lltype._castdepth(v.concretetype.TO, rclass.OBJECT) >= 0
 851
 852    def _rewrite_cmp_ptrs(self, op):
 853        if self._is_gc(op.args[0]):
 854            return op
 855        else:
 856            opname = {'ptr_eq': 'int_eq',
 857                      'ptr_ne': 'int_ne',
 858                      'ptr_iszero': 'int_is_zero',
 859                      'ptr_nonzero': 'int_is_true'}[op.opname]
 860            return SpaceOperation(opname, op.args, op.result)
 861
 862    def rewrite_op_int_eq(self, op):
 863        return self._rewrite_equality(op, 'int_is_zero')
 864
 865    def rewrite_op_int_ne(self, op):
 866        return self._rewrite_equality(op, 'int_is_true')
 867
 868    def rewrite_op_ptr_eq(self, op):
 869        prefix = ''
 870        if self._is_rclass_instance(op.args[0]):
 871            assert self._is_rclass_instance(op.args[1])
 872            op = SpaceOperation('instance_ptr_eq', op.args, op.result)
 873            prefix = 'instance_'
 874        op1 = self._rewrite_equality(op, prefix + 'ptr_iszero')
 875        return self._rewrite_cmp_ptrs(op1)
 876
 877    def rewrite_op_ptr_ne(self, op):
 878        prefix = ''
 879        if self._is_rclass_instance(op.args[0]):
 880            assert self._is_rclass_instance(op.args[1])
 881            op = SpaceOperation('instance_ptr_ne', op.args, op.result)
 882            prefix = 'instance_'
 883        op1 = self._rewrite_equality(op, prefix + 'ptr_nonzero')
 884        return self._rewrite_cmp_ptrs(op1)
 885
 886    rewrite_op_ptr_iszero = _rewrite_cmp_ptrs
 887    rewrite_op_ptr_nonzero = _rewrite_cmp_ptrs
 888
 889    def rewrite_op_cast_ptr_to_int(self, op):
 890        if self._is_gc(op.args[0]):
 891            return op
 892
 893    def rewrite_op_cast_opaque_ptr(self, op):
 894        # None causes the result of this op to get aliased to op.args[0]
 895        return [SpaceOperation('mark_opaque_ptr', op.args, None), None]
 896
 897    def rewrite_op_force_cast(self, op):
 898        v_arg = op.args[0]
 899        v_result = op.result
 900        assert not self._is_gc(v_arg)
 901
 902        if v_arg.concretetype == v_result.concretetype:
 903            return
 904
 905        float_arg = v_arg.concretetype in [lltype.Float, lltype.SingleFloat]
 906        float_res = v_result.concretetype in [lltype.Float, lltype.SingleFloat]
 907        if not float_arg and not float_res:
 908            # some int -> some int cast
 909            return self._int_to_int_cast(v_arg, v_result)
 910        elif float_arg and float_res:
 911            # some float -> some float cast
 912            return self._float_to_float_cast(v_arg, v_result)
 913        elif not float_arg and float_res:
 914            # some int -> some float
 915            ops = []
 916            v2 = varoftype(lltype.Float)
 917            sizesign = rffi.size_and_sign(v_arg.concretetype)
 918            if sizesign <= rffi.size_and_sign(lltype.Signed):
 919                # cast from a type that fits in an int: either the size is
 920                # smaller, or it is equal and it is not unsigned
 921                v1 = varoftype(lltype.Signed)
 922                oplist = self.rewrite_operation(
 923                    SpaceOperation('force_cast', [v_arg], v1)
 924                )
 925                if oplist:
 926                    ops.extend(oplist)
 927                else:
 928                    v1 = v_arg
 929                op = self.rewrite_operation(
 930                    SpaceOperation('cast_int_to_float', [v1], v2)
 931                )
 932                ops.append(op)
 933            else:
 934                if sizesign == rffi.size_and_sign(lltype.Unsigned):
 935                    opname = 'cast_uint_to_float'
 936                elif sizesign == rffi.size_and_sign(lltype.SignedLongLong):
 937                    opname = 'cast_longlong_to_float'
 938                elif sizesign == rffi.size_and_sign(lltype.UnsignedLongLong):
 939                    opname = 'cast_ulonglong_to_float'
 940                else:
 941                    raise AssertionError('cast_x_to_float: %r' % (sizesign,))
 942                ops1 = self.rewrite_operation(
 943                    SpaceOperation(opname, [v_arg], v2)
 944                )
 945                if not isinstance(ops1, list): ops1 = [ops1]
 946                ops.extend(ops1)
 947            op2 = self.rewrite_operation(
 948                SpaceOperation('force_cast', [v2], v_result)
 949            )
 950            if op2:
 951                ops.append(op2)
 952            else:
 953                ops[-1].result = v_result
 954            return ops
 955        elif float_arg and not float_res:
 956            # some float -> some int
 957            ops = []
 958            v1 = varoftype(lltype.Float)
 959            op1 = self.rewrite_operation(
 960                SpaceOperation('force_cast', [v_arg], v1)
 961            )
 962            if op1:
 963                ops.append(op1)
 964            else:
 965                v1 = v_arg
 966            sizesign = rffi.size_and_sign(v_result.concretetype)
 967            if sizesign <= rffi.size_and_sign(lltype.Signed):
 968                # cast to a type that fits in an int: either the size is
 969                # smaller, or it is equal and it is not unsigned
 970                v2 = varoftype(lltype.Signed)
 971                op = self.rewrite_operation(
 972                    SpaceOperation('cast_float_to_int', [v1], v2)
 973                )
 974                ops.append(op)
 975                oplist = self.rewrite_operation(
 976                    SpaceOperation('force_cast', [v2], v_result)
 977                )
 978                if oplist:
 979                    ops.extend(oplist)
 980                else:
 981                    op.result = v_result
 982            else:
 983                if sizesign == rffi.size_and_sign(lltype.Unsigned):
 984                    opname = 'cast_float_to_uint'
 985                elif sizesign == rffi.size_and_sign(lltype.SignedLongLong):
 986                    opname = 'cast_float_to_longlong'
 987                elif sizesign == rffi.size_and_sign(lltype.UnsignedLongLong):
 988                    opname = 'cast_float_to_ulonglong'
 989                else:
 990                    raise AssertionError('cast_float_to_x: %r' % (sizesign,))
 991                ops1 = self.rewrite_operation(
 992                    SpaceOperation(opname, [v1], v_result)
 993                )
 994                if not isinstance(ops1, list): ops1 = [ops1]
 995                ops.extend(ops1)
 996            return ops
 997        else:
 998            assert False
 999
1000    def _int_to_int_cast(self, v_arg, v_result):
1001        longlong_arg = longlong.is_longlong(v_arg.concretetype)
1002        longlong_res = longlong.is_longlong(v_result.concretetype)
1003        size1, unsigned1 = rffi.size_and_sign(v_arg.concretetype)
1004        size2, unsigned2 = rffi.size_and_sign(v_result.concretetype)
1005
1006        if longlong_arg and longlong_res:
1007            return
1008        elif longlong_arg:
1009            v = varoftype(lltype.Signed)
1010            op1 = self.rewrite_operation(
1011                SpaceOperation('truncate_longlong_to_int', [v_arg], v)
1012            )
1013            op2 = SpaceOperation('force_cast', [v], v_result)
1014            oplist = self.rewrite_operation(op2)
1015            if not oplist:
1016                op1.result = v_result
1017                oplist = []
1018            return [op1] + oplist
1019        elif longlong_res:
1020            if unsigned1:
1021                INTERMEDIATE = lltype.Unsigned
1022            else:
1023                INTERMEDIATE = lltype.Signed
1024            v = varoftype(INTERMEDIATE)
1025            op1 = SpaceOperation('force_cast', [v_arg], v)
1026            oplist = self.rewrite_operation(op1)
1027            if not oplist:
1028                v = v_arg
1029                oplist = []
1030            if unsigned1:
1031                if unsigned2:
1032                    opname = 'cast_uint_to_ulonglong'
1033                else:
1034                    opname = 'cast_uint_to_longlong'
1035            else:
1036                if unsigned2:
1037                    opname = 'cast_int_to_ulonglong'
1038                else:
1039                    opname = 'cast_int_to_longlong'
1040            op2 = self.rewrite_operation(
1041                SpaceOperation(opname, [v], v_result)
1042            )
1043            return oplist + [op2]
1044
1045        # We've now, ostensibly, dealt with the longlongs, everything should be
1046        # a Signed or smaller
1047        assert size1 <= rffi.sizeof(lltype.Signed)
1048        assert size2 <= rffi.sizeof(lltype.Signed)
1049
1050        # the target type is LONG or ULONG
1051        if size2 == rffi.sizeof(lltype.Signed):
1052            return
1053
1054        min1, max1 = integer_bounds(size1, unsigned1)
1055        min2, max2 = integer_bounds(size2, unsigned2)
1056
1057        # the target type includes the source range
1058        if min2 <= min1 <= max1 <= max2:
1059            return
1060
1061        result = []
1062        if min2:
1063            c_min2 = Constant(min2, lltype.Signed)
1064            v2 = varoftype(lltype.Signed)
1065            result.append(SpaceOperation('int_sub', [v_arg, c_min2], v2))
1066        else:
1067            v2 = v_arg
1068        c_mask = Constant(int((1 << (8 * size2)) - 1), lltype.Signed)
1069        if min2:
1070            v3 = varoftype(lltype.Signed)
1071        else:
1072            v3 = v_result
1073        result.append(SpaceOperation('int_and', [v2, c_mask], v3))
1074        if min2:
1075            result.append(SpaceOperation('int_add', [v3, c_min2], v_result))
1076        return result
1077
1078    def _float_to_float_cast(self, v_arg, v_result):
1079        if v_arg.concretetype == lltype.SingleFloat:
1080            assert v_result.concretetype == lltype.Float, "cast %s -> %s" % (
1081                v_arg.concretetype, v_result.concretetype)
1082            return SpaceOperation('cast_singlefloat_to_float', [v_arg],
1083                                  v_result)
1084        if v_result.concretetype == lltype.SingleFloat:
1085            assert v_arg.concretetype == lltype.Float, "cast %s -> %s" % (
1086                v_arg.concretetype, v_result.concretetype)
1087            return SpaceOperation('cast_float_to_singlefloat', [v_arg],
1088                                  v_result)
1089
1090    def rewrite_op_direct_ptradd(self, op):
1091        # xxx otherwise, not implemented:
1092        assert op.args[0].concretetype == rffi.CCHARP
1093        #
1094        return SpaceOperation('int_add', [op.args[0], op.args[1]], op.result)
1095
1096    # ----------
1097    # Long longs, for 32-bit only.  Supported operations are left unmodified,
1098    # and unsupported ones are turned into a call to a function from
1099    # jit.codewriter.support.
1100
1101    for _op, _oopspec in [('llong_invert',  'INVERT'),
1102                          ('llong_lt',      'LT'),
1103                          ('llong_le',      'LE'),
1104                          ('llong_eq',      'EQ'),
1105                          ('llong_ne',      'NE'),
1106                          ('llong_gt',      'GT'),
1107                          ('llong_ge',      'GE'),
1108                          ('llong_add',     'ADD'),
1109                          ('llong_sub',     'SUB'),
1110                          ('llong_mul',     'MUL'),
1111                          ('llong_and',     'AND'),
1112                          ('llong_or',      'OR'),
1113                          ('llong_xor',     'XOR'),
1114                          ('llong_lshift',  'LSHIFT'),
1115                          ('llong_rshift',  'RSHIFT'),
1116                          ('cast_int_to_longlong',     'FROM_INT'),
1117                          ('truncate_longlong_to_int', 'TO_INT'),
1118                          ('cast_float_to_longlong',   'FROM_FLOAT'),
1119                          ('cast_longlong_to_float',   'TO_FLOAT'),
1120                          ('cast_uint_to_longlong',    'FROM_UINT'),
1121                          ]:
1122        exec py.code.Source('''
1123            def rewrite_op_%s(self, op):
1124                args = op.args
1125                op1 = self.prepare_builtin_call(op, "llong_%s", args)
1126                op2 = self._handle_oopspec_call(op1, args,
1127                                                EffectInfo.OS_LLONG_%s,
1128                                           EffectInfo.EF_ELIDABLE_CANNOT_RAISE)
1129                if %r == "TO_INT":
1130                    assert op2.result.concretetype == lltype.Signed
1131                return op2
1132        ''' % (_op, _oopspec.lower(), _oopspec, _oopspec)).compile()
1133
1134    for _op, _oopspec in [('cast_int_to_ulonglong',     'FROM_INT'),
1135                          ('cast_uint_to_ulonglong',    'FROM_UINT'),
1136                          ('cast_float_to_ulonglong',   'FROM_FLOAT'),
1137                          ('cast_ulonglong_to_float',   'U_TO_FLOAT'),
1138                          ('ullong_invert', 'INVERT'),
1139                          ('ullong_lt',     'ULT'),
1140                          ('ullong_le',     'ULE'),
1141                          ('ullong…

Large files files are truncated, but you can click here to view the full file