/rpython/jit/metainterp/virtualizable.py
Python | 345 lines | 271 code | 32 blank | 42 comment | 46 complexity | f76dbec336a37f0b2bacbac63875f8a3 MD5 | raw file
Possible License(s): AGPL-3.0, BSD-3-Clause, Apache-2.0
- from rpython.jit.codewriter.effectinfo import EffectInfo
- from rpython.jit.metainterp import history
- from rpython.jit.metainterp.typesystem import deref, fieldType, arrayItem
- from rpython.jit.metainterp.warmstate import wrap, unwrap
- from rpython.jit.metainterp.resumecode import numb_next_item
- from rpython.rlib.unroll import unrolling_iterable
- from rpython.rtyper import rvirtualizable
- from rpython.rtyper.lltypesystem import lltype, llmemory
- from rpython.rtyper.rclass import IR_IMMUTABLE_ARRAY, IR_IMMUTABLE
- class VirtualizableInfo(object):
- def __init__(self, warmrunnerdesc, VTYPEPTR):
- self.warmrunnerdesc = warmrunnerdesc
- cpu = warmrunnerdesc.cpu
- self.cpu = cpu
- #
- VTYPEPTR1 = VTYPEPTR
- while 'virtualizable_accessor' not in deref(VTYPEPTR)._hints:
- VTYPEPTR = cpu.ts.get_superclass(VTYPEPTR)
- assert VTYPEPTR is not None, (
- "%r is listed in the jit driver's 'virtualizables', "
- "but that class doesn't have a '_virtualizable_' attribute "
- "(if it has _virtualizable2_, rename it to _virtualizable_)"
- % (VTYPEPTR1,))
- self.VTYPEPTR = VTYPEPTR
- self.VTYPE = VTYPE = deref(VTYPEPTR)
- self.vable_token_descr = cpu.fielddescrof(VTYPE, 'vable_token')
- #
- accessor = VTYPE._hints['virtualizable_accessor']
- all_fields = accessor.fields
- static_fields = []
- array_fields = []
- for name, tp in all_fields.iteritems():
- if tp == IR_IMMUTABLE_ARRAY:
- array_fields.append(name)
- elif tp == IR_IMMUTABLE:
- static_fields.append(name)
- else:
- raise Exception("unknown type: %s" % tp)
- self.static_fields = static_fields
- self.array_fields = array_fields
- #
- FIELDTYPES = [fieldType(VTYPE, name) for name in static_fields]
- ARRAYITEMTYPES = []
- for name in array_fields:
- ARRAYPTR = fieldType(VTYPE, name)
- ARRAY = deref(ARRAYPTR)
- assert isinstance(ARRAYPTR, lltype.Ptr)
- if not isinstance(ARRAY, lltype.GcArray):
- raise Exception(
- "The virtualizable field '%s' is not an array (found %r)."
- " It usually means that you must try harder to ensure that"
- " the list is not resized at run-time. You can do that by"
- " using rpython.rlib.debug.make_sure_not_resized()." %
- (name, ARRAY))
- ARRAYITEMTYPES.append(arrayItem(ARRAY))
- self.array_descrs = [cpu.arraydescrof(deref(fieldType(VTYPE, name)))
- for name in array_fields]
- #
- self.num_static_extra_boxes = len(static_fields)
- self.num_arrays = len(array_fields)
- self.static_field_to_extra_box = dict(
- [(name, i) for (i, name) in enumerate(static_fields)])
- self.array_field_counter = dict(
- [(name, i) for (i, name) in enumerate(array_fields)])
- self.static_extra_types = [history.getkind(TYPE)
- for TYPE in FIELDTYPES]
- self.arrayitem_extra_types = [history.getkind(ITEM)
- for ITEM in ARRAYITEMTYPES]
- self.static_field_descrs = [cpu.fielddescrof(VTYPE, name)
- for name in static_fields]
- self.array_field_descrs = [cpu.fielddescrof(VTYPE, name)
- for name in array_fields]
- for descr in self.static_field_descrs:
- descr.vinfo = self
- for descr in self.array_field_descrs:
- descr.vinfo = self
- self.static_field_by_descrs = dict(
- [(descr, i) for (i, descr) in enumerate(self.static_field_descrs)])
- self.array_field_by_descrs = dict(
- [(descr, i) for (i, descr) in enumerate(self.array_field_descrs)])
- #
- getlength = cpu.ts.getlength
- getarrayitem = cpu.ts.getarrayitem
- setarrayitem = cpu.ts.setarrayitem
- def read_boxes(cpu, virtualizable):
- assert lltype.typeOf(virtualizable) == llmemory.GCREF
- virtualizable = cast_gcref_to_vtype(virtualizable)
- boxes = []
- for _, fieldname in unroll_static_fields:
- x = getattr(virtualizable, fieldname)
- boxes.append(wrap(cpu, x))
- for _, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- for i in range(getlength(lst)):
- boxes.append(wrap(cpu, getarrayitem(lst, i)))
- return boxes
- def write_boxes(virtualizable, boxes):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- i = 0
- for FIELDTYPE, fieldname in unroll_static_fields:
- x = unwrap(FIELDTYPE, boxes[i])
- setattr(virtualizable, fieldname, x)
- i = i + 1
- for ARRAYITEMTYPE, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- for j in range(getlength(lst)):
- x = unwrap(ARRAYITEMTYPE, boxes[i])
- setarrayitem(lst, j, x)
- i = i + 1
- assert len(boxes) == i + 1
- def get_total_size(virtualizable):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- size = 0
- for _, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- size += getlength(lst)
- for _, fieldname in unroll_static_fields:
- size += 1
- return size
- def write_from_resume_data_partial(virtualizable, reader, index, numb):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- # Load values from the reader (see resume.py) described by
- # the list of numbers 'nums', and write them in their proper
- # place in the 'virtualizable'.
- for FIELDTYPE, fieldname in unroll_static_fields:
- item, index = numb_next_item(numb, index)
- x = reader.load_value_of_type(FIELDTYPE, item)
- setattr(virtualizable, fieldname, x)
- for ARRAYITEMTYPE, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- for j in range(getlength(lst)):
- item, index = numb_next_item(numb, index)
- x = reader.load_value_of_type(ARRAYITEMTYPE, item)
- setarrayitem(lst, j, x)
- return index
- def load_list_of_boxes(virtualizable, reader, vable_box, numb, index):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- # Uses 'virtualizable' only to know the length of the arrays;
- # does not write anything into it. The returned list is in
- # the format expected of virtualizable_boxes, so it ends in
- # the virtualizable itself.
- boxes = []
- for FIELDTYPE, fieldname in unroll_static_fields:
- item, index = numb_next_item(numb, index)
- box = reader.decode_box_of_type(FIELDTYPE, item)
- boxes.append(box)
- for ARRAYITEMTYPE, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- for j in range(getlength(lst)):
- item, index = numb_next_item(numb, index)
- box = reader.decode_box_of_type(ARRAYITEMTYPE, item)
- boxes.append(box)
- boxes.append(vable_box)
- return boxes, index
- def check_boxes(virtualizable, boxes):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- # for debugging
- i = 0
- for FIELDTYPE, fieldname in unroll_static_fields:
- x = unwrap(FIELDTYPE, boxes[i])
- assert getattr(virtualizable, fieldname) == x
- i = i + 1
- for ARRAYITEMTYPE, fieldname in unroll_array_fields:
- lst = getattr(virtualizable, fieldname)
- for j in range(getlength(lst)):
- x = unwrap(ARRAYITEMTYPE, boxes[i])
- assert getarrayitem(lst, j) == x
- i = i + 1
- assert len(boxes) == i + 1
- def get_index_in_array(virtualizable, arrayindex, index):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- index += self.num_static_extra_boxes
- j = 0
- for _, fieldname in unroll_array_fields:
- if arrayindex == j:
- return index
- lst = getattr(virtualizable, fieldname)
- index += getlength(lst)
- j = j + 1
- assert False, "invalid arrayindex"
- def get_array_length(virtualizable, arrayindex):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- j = 0
- for _, fieldname in unroll_array_fields:
- if arrayindex == j:
- lst = getattr(virtualizable, fieldname)
- return getlength(lst)
- j += 1
- assert False, "invalid arrayindex"
- unroll_static_fields = unrolling_iterable(zip(FIELDTYPES,
- static_fields))
- unroll_array_fields = unrolling_iterable(zip(ARRAYITEMTYPES,
- array_fields))
- unroll_static_fields_rev = unrolling_iterable(
- reversed(list(unroll_static_fields)))
- unroll_array_fields_rev = unrolling_iterable(
- reversed(list(unroll_array_fields)))
- self.read_boxes = read_boxes
- self.write_boxes = write_boxes
- self.write_from_resume_data_partial = write_from_resume_data_partial
- self.load_list_of_boxes = load_list_of_boxes
- self.check_boxes = check_boxes
- self.get_index_in_array = get_index_in_array
- self.get_array_length = get_array_length
- self.get_total_size = get_total_size
- def cast_to_vtype(virtualizable):
- return self.cpu.ts.cast_to_instance_maybe(VTYPEPTR, virtualizable)
- self.cast_to_vtype = cast_to_vtype
- def cast_gcref_to_vtype(virtualizable):
- assert lltype.typeOf(virtualizable) == llmemory.GCREF
- return lltype.cast_opaque_ptr(VTYPEPTR, virtualizable)
- self.cast_gcref_to_vtype = cast_gcref_to_vtype
- def clear_vable_token(virtualizable):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- if virtualizable.vable_token:
- force_now(virtualizable)
- assert not virtualizable.vable_token
- self.clear_vable_token = clear_vable_token
- def tracing_before_residual_call(virtualizable):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- assert not virtualizable.vable_token
- virtualizable.vable_token = TOKEN_TRACING_RESCALL
- self.tracing_before_residual_call = tracing_before_residual_call
- def tracing_after_residual_call(virtualizable):
- """
- Returns whether or not the virtualizable was forced during a
- CALL_MAY_FORCE.
- """
- virtualizable = cast_gcref_to_vtype(virtualizable)
- if virtualizable.vable_token:
- # not modified by the residual call; assert that it is still
- # set to TOKEN_TRACING_RESCALL and clear it.
- assert virtualizable.vable_token == TOKEN_TRACING_RESCALL
- virtualizable.vable_token = TOKEN_NONE
- return False
- else:
- # marker "modified during residual call" set.
- return True
- self.tracing_after_residual_call = tracing_after_residual_call
- def force_now(virtualizable):
- token = virtualizable.vable_token
- if token == TOKEN_TRACING_RESCALL:
- # The values in the virtualizable are always correct during
- # tracing. We only need to reset vable_token to TOKEN_NONE
- # as a marker for the tracing, to tell it that this
- # virtualizable escapes.
- virtualizable.vable_token = TOKEN_NONE
- else:
- from rpython.jit.metainterp.compile import ResumeGuardForcedDescr
- ResumeGuardForcedDescr.force_now(cpu, token)
- assert virtualizable.vable_token == TOKEN_NONE
- force_now._dont_inline_ = True
- self.force_now = force_now
- def is_token_nonnull_gcref(virtualizable):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- return bool(virtualizable.vable_token)
- self.is_token_nonnull_gcref = is_token_nonnull_gcref
- def reset_token_gcref(virtualizable):
- virtualizable = cast_gcref_to_vtype(virtualizable)
- virtualizable.vable_token = TOKEN_NONE
- self.reset_token_gcref = reset_token_gcref
- def reset_vable_token(virtualizable):
- virtualizable.vable_token = TOKEN_NONE
- self.reset_vable_token = reset_vable_token
- def _freeze_(self):
- return True
- def finish(self):
- #
- def force_virtualizable_if_necessary(virtualizable):
- if virtualizable.vable_token:
- self.force_now(virtualizable)
- force_virtualizable_if_necessary._always_inline_ = True
- #
- all_graphs = self.warmrunnerdesc.translator.graphs
- ts = self.warmrunnerdesc.cpu.ts
- (_, FUNCPTR) = ts.get_FuncType([self.VTYPEPTR], lltype.Void)
- funcptr = self.warmrunnerdesc.helper_func(
- FUNCPTR, force_virtualizable_if_necessary)
- rvirtualizable.replace_force_virtualizable_with_call(
- all_graphs, self.VTYPEPTR, funcptr)
- (_, FUNCPTR) = ts.get_FuncType([llmemory.GCREF], lltype.Void)
- self.clear_vable_ptr = self.warmrunnerdesc.helper_func(
- FUNCPTR, self.clear_vable_token)
- FUNC = FUNCPTR.TO
- ei = EffectInfo([], [], [], [], [], [], EffectInfo.EF_CANNOT_RAISE,
- can_invalidate=False,
- oopspecindex=EffectInfo.OS_JIT_FORCE_VIRTUALIZABLE)
- self.clear_vable_descr = self.cpu.calldescrof(FUNC, FUNC.ARGS,
- FUNC.RESULT, ei)
- def unwrap_virtualizable_box(self, virtualizable_box):
- return virtualizable_box.getref(llmemory.GCREF)
- def is_vtypeptr(self, TYPE):
- return TYPE == self.VTYPEPTR
- # ____________________________________________________________
- #
- # The 'vable_token' field of a virtualizable is either NULL, points
- # to the JITFRAME object for the current assembler frame, or is
- # the special value TOKEN_TRACING_RESCALL. It is:
- #
- # 1. NULL (TOKEN_NONE) if not in the JIT at all, except as described below.
- #
- # 2. NULL when tracing is in progress; except:
- #
- # 3. equal to TOKEN_TRACING_RESCALL during tracing when we do a
- # residual call, calling random unknown other parts of the interpreter;
- # it is reset to NULL as soon as something occurs to the virtualizable.
- #
- # 4. when running the machine code with a virtualizable, it is set
- # to the JITFRAME, as obtained with the FORCE_TOKEN operation.
- _DUMMY = lltype.GcStruct('JITFRAME_DUMMY')
- _dummy = lltype.malloc(_DUMMY)
- TOKEN_NONE = lltype.nullptr(llmemory.GCREF.TO)
- TOKEN_TRACING_RESCALL = lltype.cast_opaque_ptr(llmemory.GCREF, _dummy)