def export_state(self, targetop): original_jump_args = targetop.getarglist() jump_args = [ self.getvalue(a).get_key_box() for a in original_jump_args ] assert self.optimizer.loop.resume_at_jump_descr resume_at_jump_descr = self.optimizer.loop.resume_at_jump_descr.clone_if_mutable( ) assert isinstance(resume_at_jump_descr, ResumeGuardDescr) resume_at_jump_descr.rd_snapshot = self.fix_snapshot( jump_args, resume_at_jump_descr.rd_snapshot) modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) values = [self.getvalue(arg) for arg in jump_args] inputargs = virtual_state.make_inputargs(values, self.optimizer) short_inputargs = virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) if self.boxes_created_this_iteration is not None: for box in self.inputargs: self.boxes_created_this_iteration[box] = True short_boxes = ShortBoxes(self.optimizer, inputargs, self.boxes_created_this_iteration) self.optimizer.clear_newoperations() for i in range(len(original_jump_args)): if values[i].is_virtual(): values[i].force_box(self.optimizer) if original_jump_args[i] is not jump_args[i]: op = ResOperation(rop.SAME_AS, [jump_args[i]], original_jump_args[i]) self.optimizer.emit_operation(op) inputarg_setup_ops = self.optimizer.get_newoperations() target_token = targetop.getdescr() assert isinstance(target_token, TargetToken) targetop.initarglist(inputargs) target_token.virtual_state = virtual_state target_token.short_preamble = [ ResOperation(rop.LABEL, short_inputargs, None) ] target_token.resume_at_jump_descr = resume_at_jump_descr exported_values = {} for box in inputargs: exported_values[box] = self.optimizer.getvalue(box) for op in short_boxes.operations(): if op and op.result: box = op.result exported_values[box] = self.optimizer.getvalue(box) target_token.exported_state = ExportedState(short_boxes, inputarg_setup_ops, exported_values)
def export_state(self, targetop): original_jump_args = targetop.getarglist() jump_args = [self.getvalue(a).get_key_box() for a in original_jump_args] assert self.optimizer.loop.resume_at_jump_descr resume_at_jump_descr = self.optimizer.loop.resume_at_jump_descr.clone_if_mutable() assert isinstance(resume_at_jump_descr, ResumeGuardDescr) resume_at_jump_descr.rd_snapshot = self.fix_snapshot(jump_args, resume_at_jump_descr.rd_snapshot) modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) values = [self.getvalue(arg) for arg in jump_args] inputargs = virtual_state.make_inputargs(values, self.optimizer) short_inputargs = virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) if self.boxes_created_this_iteration is not None: for box in self.inputargs: self.boxes_created_this_iteration[box] = True short_boxes = ShortBoxes(self.optimizer, inputargs, self.boxes_created_this_iteration) self.optimizer.clear_newoperations() for i in range(len(original_jump_args)): if values[i].is_virtual(): values[i].force_box(self.optimizer) if original_jump_args[i] is not jump_args[i]: op = ResOperation(rop.SAME_AS, [jump_args[i]], original_jump_args[i]) self.optimizer.emit_operation(op) inputarg_setup_ops = self.optimizer.get_newoperations() target_token = targetop.getdescr() assert isinstance(target_token, TargetToken) targetop.initarglist(inputargs) target_token.virtual_state = virtual_state target_token.short_preamble = [ResOperation(rop.LABEL, short_inputargs, None)] target_token.resume_at_jump_descr = resume_at_jump_descr exported_values = {} for box in inputargs: exported_values[box] = self.optimizer.getvalue(box) for op in short_boxes.operations(): if op and op.result: box = op.result exported_values[box] = self.optimizer.getvalue(box) target_token.exported_state = ExportedState(short_boxes, inputarg_setup_ops, exported_values)
def jump_to_already_compiled_trace(self, jumpop): assert jumpop.getopnum() == rop.JUMP cell_token = jumpop.getdescr() assert isinstance(cell_token, JitCellToken) if not cell_token.target_tokens: return False if not self.inline_short_preamble: assert cell_token.target_tokens[0].virtual_state is None jumpop.setdescr(cell_token.target_tokens[0]) self.optimizer.send_extra_operation(jumpop) return True args = jumpop.getarglist() modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(args) debug_start('jit-log-virtualstate') virtual_state.debug_print("Looking for ") for target in cell_token.target_tokens: if not target.virtual_state: continue ok = False extra_guards = [] bad = {} debugmsg = 'Did not match ' if target.virtual_state.generalization_of(virtual_state, bad): ok = True debugmsg = 'Matched ' else: try: cpu = self.optimizer.cpu target.virtual_state.generate_guards(virtual_state, args, cpu, extra_guards) ok = True debugmsg = 'Guarded to match ' except InvalidLoop: pass target.virtual_state.debug_print(debugmsg, bad) if ok: debug_stop('jit-log-virtualstate') values = [self.getvalue(arg) for arg in jumpop.getarglist()] args = target.virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) short_inputargs = target.short_preamble[0].getarglist() inliner = Inliner(short_inputargs, args) for guard in extra_guards: if guard.is_guard(): descr = target.resume_at_jump_descr.clone_if_mutable() inliner.inline_descr_inplace(descr) guard.setdescr(descr) self.optimizer.send_extra_operation(guard) try: for shop in target.short_preamble[1:]: newop = inliner.inline_op(shop) self.optimizer.send_extra_operation(newop) except InvalidLoop: debug_print("Inlining failed unexpectedly", "jumping to preamble instead") assert cell_token.target_tokens[0].virtual_state is None jumpop.setdescr(cell_token.target_tokens[0]) self.optimizer.send_extra_operation(jumpop) return True debug_stop('jit-log-virtualstate') return False
def close_loop(self, start_label, jumpop): virtual_state = self.initial_virtual_state short_inputargs = self.short[0].getarglist() inputargs = self.inputargs short_jumpargs = inputargs[:] # Construct jumpargs from the virtual state original_jumpargs = jumpop.getarglist()[:] values = [self.getvalue(arg) for arg in jumpop.getarglist()] try: jumpargs = virtual_state.make_inputargs(values, self.optimizer) except BadVirtualState: raise InvalidLoop jumpop.initarglist(jumpargs) # Inline the short preamble at the end of the loop jmp_to_short_args = virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) assert len(short_inputargs) == len(jmp_to_short_args) args = {} for i in range(len(short_inputargs)): if short_inputargs[i] in args: if args[short_inputargs[i]] != jmp_to_short_args[i]: raise InvalidLoop args[short_inputargs[i]] = jmp_to_short_args[i] self.short_inliner = Inliner(short_inputargs, jmp_to_short_args) for op in self.short[1:]: newop = self.short_inliner.inline_op(op) self.optimizer.send_extra_operation(newop) # Import boxes produced in the preamble but used in the loop newoperations = self.optimizer.get_newoperations() self.boxes_created_this_iteration = {} i = j = 0 while i < len(newoperations) or j < len(jumpargs): if i == len(newoperations): while j < len(jumpargs): a = jumpargs[j] if self.optimizer.loop.logops: debug_print('J: ' + self.optimizer.loop.logops.repr_of_arg(a)) self.import_box(a, inputargs, short_jumpargs, jumpargs) j += 1 else: op = newoperations[i] self.boxes_created_this_iteration[op.result] = True args = op.getarglist() if op.is_guard(): args = args + op.getfailargs() if self.optimizer.loop.logops: debug_print('OP: ' + self.optimizer.loop.logops.repr_of_resop(op)) for a in args: if self.optimizer.loop.logops: debug_print('A: ' + self.optimizer.loop.logops.repr_of_arg(a)) self.import_box(a, inputargs, short_jumpargs, jumpargs) i += 1 newoperations = self.optimizer.get_newoperations() jumpop.initarglist(jumpargs) self.optimizer.send_extra_operation(jumpop) self.short.append(ResOperation(rop.JUMP, short_jumpargs, None, descr=jumpop.getdescr())) # Verify that the virtual state at the end of the loop is one # that is compatible with the virtual state at the start of the loop modifier = VirtualStateAdder(self.optimizer) final_virtual_state = modifier.get_virtual_state(original_jumpargs) debug_start('jit-log-virtualstate') virtual_state.debug_print('Closed loop with ') bad = {} if not virtual_state.generalization_of(final_virtual_state, bad): # We ended up with a virtual state that is not compatible # and we are thus unable to jump to the start of the loop final_virtual_state.debug_print("Bad virtual state at end of loop, ", bad) debug_stop('jit-log-virtualstate') raise InvalidLoop debug_stop('jit-log-virtualstate') maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards if self.optimizer.emitted_guards > maxguards: target_token = jumpop.getdescr() assert isinstance(target_token, TargetToken) target_token.targeting_jitcell_token.retraced_count = sys.maxint self.finilize_short_preamble(start_label)
def import_state(self, targetop): if not targetop: # Trace did not start with a label self.inputargs = self.optimizer.loop.inputargs self.short = None self.initial_virtual_state = None return self.inputargs = targetop.getarglist() target_token = targetop.getdescr() assert isinstance(target_token, TargetToken) exported_state = target_token.exported_state if not exported_state: # No state exported, construct one without virtuals self.short = None modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(self.inputargs) self.initial_virtual_state = virtual_state return self.short = target_token.short_preamble[:] self.short_seen = {} self.short_boxes = exported_state.short_boxes self.short_resume_at_jump_descr = target_token.resume_at_jump_descr self.initial_virtual_state = target_token.virtual_state seen = {} for box in self.inputargs: if box in seen: continue seen[box] = True preamble_value = exported_state.exported_values[box] value = self.optimizer.getvalue(box) value.import_from(preamble_value, self.optimizer) # Setup the state of the new optimizer by emiting the # short operations and discarding the result self.optimizer.emitting_dissabled = True for op in exported_state.inputarg_setup_ops: self.optimizer.send_extra_operation(op) seen = {} for op in self.short_boxes.operations(): self.ensure_short_op_emitted(op, self.optimizer, seen) if op and op.result: preamble_value = exported_state.exported_values[op.result] value = self.optimizer.getvalue(op.result) if not value.is_virtual(): imp = ValueImporter(self, preamble_value, op) self.optimizer.importable_values[value] = imp newvalue = self.optimizer.getvalue(op.result) newresult = newvalue.get_key_box() # note that emitting here SAME_AS should not happen, but # in case it does, we would prefer to be suboptimal in asm # to a fatal RPython exception. if newresult is not op.result and not newvalue.is_constant(): op = ResOperation(rop.SAME_AS, [op.result], newresult) self.optimizer._newoperations.append(op) if self.optimizer.loop.logops: debug_print(' Falling back to add extra: ' + self.optimizer.loop.logops.repr_of_resop(op)) self.optimizer.flush() self.optimizer.emitting_dissabled = False
def propagate_all_forward(self): loop = self.optimizer.loop jumpop = loop.operations[-1] if jumpop.getopnum() == rop.JUMP: loop.operations = loop.operations[:-1] else: loopop = None self.optimizer.propagate_all_forward() if jumpop: assert jumpop.getdescr() is loop.token jump_args = jumpop.getarglist() jumpop.initarglist([]) self.optimizer.flush() KillHugeIntBounds(self.optimizer).apply() loop.preamble.operations = self.optimizer.newoperations jump_args = [self.getvalue(a).get_key_box() for a in jump_args] start_resumedescr = loop.preamble.start_resumedescr.clone_if_mutable() self.start_resumedescr = start_resumedescr assert isinstance(start_resumedescr, ResumeGuardDescr) start_resumedescr.rd_snapshot = self.fix_snapshot(loop, jump_args, start_resumedescr.rd_snapshot) modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(jump_args) values = [self.getvalue(arg) for arg in jump_args] inputargs = virtual_state.make_inputargs(values) short_inputargs = virtual_state.make_inputargs(values, keyboxes=True) self.constant_inputargs = {} for box in jump_args: const = self.get_constant_box(box) if const: self.constant_inputargs[box] = const sb = ShortBoxes(self.optimizer, inputargs + self.constant_inputargs.keys()) self.short_boxes = sb preamble_optimizer = self.optimizer loop.preamble.quasi_immutable_deps = self.optimizer.quasi_immutable_deps self.optimizer = self.optimizer.new() loop.quasi_immutable_deps = self.optimizer.quasi_immutable_deps logops = self.optimizer.loop.logops if logops: args = ", ".join([logops.repr_of_arg(arg) for arg in inputargs]) debug_print("inputargs: " + args) args = ", ".join([logops.repr_of_arg(arg) for arg in short_inputargs]) debug_print("short inputargs: " + args) self.short_boxes.debug_print(logops) # Force virtuals amoung the jump_args of the preamble to get the # operations needed to setup the proper state of those virtuals # in the peeled loop inputarg_setup_ops = [] preamble_optimizer.newoperations = [] seen = {} for box in inputargs: if box in seen: continue seen[box] = True value = preamble_optimizer.getvalue(box) inputarg_setup_ops.extend(value.make_guards(box)) for box in short_inputargs: if box in seen: continue seen[box] = True value = preamble_optimizer.getvalue(box) value.force_box() preamble_optimizer.flush() inputarg_setup_ops += preamble_optimizer.newoperations # Setup the state of the new optimizer by emiting the # short preamble operations and discarding the result self.optimizer.emitting_dissabled = True for op in inputarg_setup_ops: self.optimizer.send_extra_operation(op) seen = {} for op in self.short_boxes.operations(): self.ensure_short_op_emitted(op, self.optimizer, seen) if op and op.result: # The order of these guards is not important as # self.optimizer.emitting_dissabled is False value = preamble_optimizer.getvalue(op.result) for guard in value.make_guards(op.result): self.optimizer.send_extra_operation(guard) newresult = self.optimizer.getvalue(op.result).get_key_box() if newresult is not op.result: self.short_boxes.alias(newresult, op.result) self.optimizer.flush() self.optimizer.emitting_dissabled = False # XXX Hack to prevent the arraylen/strlen/unicodelen ops generated # by value.make_guards() from ending up in pure_operations for key, op in self.optimizer.pure_operations.items(): if not self.short_boxes.has_producer(op.result): del self.optimizer.pure_operations[key] initial_inputargs_len = len(inputargs) self.inliner = Inliner(loop.inputargs, jump_args) short = self.inline(inputargs, self.cloned_operations, loop.inputargs, short_inputargs, virtual_state) loop.inputargs = inputargs args = [preamble_optimizer.getvalue(self.short_boxes.original(a)).force_box() for a in inputargs] jmp = ResOperation(rop.JUMP, args, None) jmp.setdescr(loop.token) loop.preamble.operations.append(jmp) loop.operations = self.optimizer.newoperations maxguards = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.max_retrace_guards if self.optimizer.emitted_guards > maxguards: loop.preamble.token.retraced_count = sys.maxint if short: assert short[-1].getopnum() == rop.JUMP short[-1].setdescr(loop.token) # Turn guards into conditional jumps to the preamble for i in range(len(short)): op = short[i] if op.is_guard(): op = op.clone() op.setfailargs(None) descr = self.start_resumedescr.clone_if_mutable() op.setdescr(descr) short[i] = op short_loop = TreeLoop("short preamble") short_loop.inputargs = short_inputargs short_loop.operations = short # Clone ops and boxes to get private versions and boxmap = {} newargs = [None] * len(short_loop.inputargs) for i in range(len(short_loop.inputargs)): a = short_loop.inputargs[i] if a in boxmap: newargs[i] = boxmap[a] else: newargs[i] = a.clonebox() boxmap[a] = newargs[i] inliner = Inliner(short_loop.inputargs, newargs) for box, const in self.constant_inputargs.items(): inliner.argmap[box] = const short_loop.inputargs = newargs ops = [inliner.inline_op(op) for op in short_loop.operations] short_loop.operations = ops descr = self.start_resumedescr.clone_if_mutable() inliner.inline_descr_inplace(descr) short_loop.start_resumedescr = descr assert isinstance(loop.preamble.token, LoopToken) if loop.preamble.token.short_preamble: loop.preamble.token.short_preamble.append(short_loop) else: loop.preamble.token.short_preamble = [short_loop] short_loop.virtual_state = virtual_state # Forget the values to allow them to be freed for box in short_loop.inputargs: box.forget_value() for op in short_loop.operations: if op.result: op.result.forget_value()
def propagate_forward(self, op): if op.getopnum() == rop.JUMP: loop_token = op.getdescr() assert isinstance(loop_token, LoopToken) # FIXME: Use a tree, similar to the tree formed by the full # preamble and it's bridges, instead of a list to save time and # memory. This should also allow better behaviour in # situations that the is_emittable() chain currently cant # handle and the inlining fails unexpectedly belwo. short = loop_token.short_preamble if short: args = op.getarglist() modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(args) debug_start("jit-log-virtualstate") virtual_state.debug_print("Looking for ") for sh in short: ok = False extra_guards = [] bad = {} debugmsg = "Did not match " if sh.virtual_state.generalization_of(virtual_state, bad): ok = True debugmsg = "Matched " else: try: cpu = self.optimizer.cpu sh.virtual_state.generate_guards(virtual_state, args, cpu, extra_guards) ok = True debugmsg = "Guarded to match " except InvalidLoop: pass sh.virtual_state.debug_print(debugmsg, bad) if ok: debug_stop("jit-log-virtualstate") values = [self.getvalue(arg) for arg in op.getarglist()] args = sh.virtual_state.make_inputargs(values, keyboxes=True) inliner = Inliner(sh.inputargs, args) for guard in extra_guards: if guard.is_guard(): descr = sh.start_resumedescr.clone_if_mutable() inliner.inline_descr_inplace(descr) guard.setdescr(descr) self.emit_operation(guard) try: for shop in sh.operations: newop = inliner.inline_op(shop) self.emit_operation(newop) except InvalidLoop: debug_print("Inlining failed unexpectedly", "jumping to preamble instead") self.emit_operation(op) return debug_stop("jit-log-virtualstate") retraced_count = loop_token.retraced_count limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit if not self.retraced and retraced_count < limit: loop_token.retraced_count += 1 if not loop_token.failed_states: debug_print("Retracing (%d of %d)" % (retraced_count, limit)) raise RetraceLoop for failed in loop_token.failed_states: if failed.generalization_of(virtual_state): # Retracing once more will most likely fail again break else: debug_print("Retracing (%d of %d)" % (retraced_count, limit)) raise RetraceLoop else: if not loop_token.failed_states: loop_token.failed_states = [virtual_state] else: loop_token.failed_states.append(virtual_state) self.emit_operation(op)
def inline(self, inputargs, loop_operations, loop_args, short_inputargs, virtual_state): inliner = self.inliner short_jumpargs = inputargs[:] short = [] short_seen = {} for box, const in self.constant_inputargs.items(): short_seen[box] = True for op in self.short_boxes.operations(): if op is not None: if len(self.getvalue(op.result).make_guards(op.result)) > 0: self.add_op_to_short(op, short, short_seen, False, True) # This loop is equivalent to the main optimization loop in # Optimizer.propagate_all_forward jumpop = None for newop in loop_operations: newop = inliner.inline_op(newop, clone=False) if newop.getopnum() == rop.JUMP: jumpop = newop break # self.optimizer.first_optimization.propagate_forward(newop) self.optimizer.send_extra_operation(newop) self.boxes_created_this_iteration = {} assert jumpop original_jumpargs = jumpop.getarglist()[:] values = [self.getvalue(arg) for arg in jumpop.getarglist()] jumpargs = virtual_state.make_inputargs(values) jumpop.initarglist(jumpargs) jmp_to_short_args = virtual_state.make_inputargs(values, keyboxes=True) self.short_inliner = Inliner(short_inputargs, jmp_to_short_args) for box, const in self.constant_inputargs.items(): self.short_inliner.argmap[box] = const for op in short: newop = self.short_inliner.inline_op(op) self.optimizer.send_extra_operation(newop) self.optimizer.flush() i = j = 0 while i < len(self.optimizer.newoperations) or j < len(jumpargs): if i == len(self.optimizer.newoperations): while j < len(jumpargs): a = jumpargs[j] if self.optimizer.loop.logops: debug_print("J: " + self.optimizer.loop.logops.repr_of_arg(a)) self.import_box(a, inputargs, short, short_jumpargs, jumpargs, short_seen) j += 1 else: op = self.optimizer.newoperations[i] self.boxes_created_this_iteration[op.result] = True args = op.getarglist() if op.is_guard(): args = args + op.getfailargs() if self.optimizer.loop.logops: debug_print("OP: " + self.optimizer.loop.logops.repr_of_resop(op)) for a in args: if self.optimizer.loop.logops: debug_print("A: " + self.optimizer.loop.logops.repr_of_arg(a)) self.import_box(a, inputargs, short, short_jumpargs, jumpargs, short_seen) i += 1 jumpop.initarglist(jumpargs) self.optimizer.send_extra_operation(jumpop) short.append(ResOperation(rop.JUMP, short_jumpargs, None)) modifier = VirtualStateAdder(self.optimizer) final_virtual_state = modifier.get_virtual_state(original_jumpargs) debug_start("jit-log-virtualstate") virtual_state.debug_print("Closed loop with ") bad = {} if not virtual_state.generalization_of(final_virtual_state, bad): # We ended up with a virtual state that is not compatible # and we are thus unable to jump to the start of the loop # XXX Is it possible to end up here? If so, consider: # - Fallback on having the preamble jump to itself? # - Would virtual_state.generate_guards make sense here? final_virtual_state.debug_print("Bad virtual state at end of loop, ", bad) debug_stop("jit-log-virtualstate") raise InvalidLoop debug_stop("jit-log-virtualstate") return short
def propagate_forward(self, op): if op.getopnum() == rop.JUMP: loop_token = op.getdescr() assert isinstance(loop_token, LoopToken) short = loop_token.short_preamble if short: args = op.getarglist() modifier = VirtualStateAdder(self.optimizer) virtual_state = modifier.get_virtual_state(args) debug_start('jit-log-virtualstate') virtual_state.debug_print("Looking for ") for sh in short: ok = False extra_guards = [] bad = {} debugmsg = 'Did not match ' if sh.virtual_state.generalization_of(virtual_state, bad): ok = True debugmsg = 'Matched ' else: try: cpu = self.optimizer.cpu sh.virtual_state.generate_guards(virtual_state, args, cpu, extra_guards) ok = True debugmsg = 'Guarded to match ' except InvalidLoop: pass sh.virtual_state.debug_print(debugmsg, bad) if ok: debug_stop('jit-log-virtualstate') values = [self.getvalue(arg) for arg in op.getarglist()] args = sh.virtual_state.make_inputargs(values, self.optimizer, keyboxes=True) inliner = Inliner(sh.inputargs, args) for guard in extra_guards: if guard.is_guard(): descr = sh.start_resumedescr.clone_if_mutable() inliner.inline_descr_inplace(descr) guard.setdescr(descr) self.emit_operation(guard) try: for shop in sh.operations: newop = inliner.inline_op(shop) self.emit_operation(newop) except InvalidLoop: debug_print("Inlining failed unexpectedly", "jumping to preamble instead") self.emit_operation(op) return debug_stop('jit-log-virtualstate') retraced_count = loop_token.retraced_count limit = self.optimizer.metainterp_sd.warmrunnerdesc.memory_manager.retrace_limit if not self.retraced and retraced_count<limit: loop_token.retraced_count += 1 if not loop_token.failed_states: debug_print("Retracing (%d of %d)" % (retraced_count, limit)) raise RetraceLoop for failed in loop_token.failed_states: if failed.generalization_of(virtual_state): # Retracing once more will most likely fail again break else: debug_print("Retracing (%d of %d)" % (retraced_count, limit)) raise RetraceLoop else: if not loop_token.failed_states: loop_token.failed_states=[virtual_state] else: loop_token.failed_states.append(virtual_state) self.emit_operation(op)