def eval_operation(self, operation): tracer = self.llinterpreter.tracer if tracer: tracer.dump(str(operation)) ophandler = self.getoperationhandler(operation.opname) # XXX slighly unnice but an important safety check if operation.opname == 'direct_call': assert isinstance(operation.args[0], Constant) elif operation.opname == 'indirect_call': assert isinstance(operation.args[0], Variable) if getattr(ophandler, 'specialform', False): retval = ophandler(*operation.args) else: vals = [self.getval_or_subop(x) for x in operation.args] if getattr(ophandler, 'need_result_type', False): vals.insert(0, operation.result.concretetype) try: retval = ophandler(*vals) except LLException, e: # safety check check that the operation is allowed to raise that # exception if operation.opname in lloperation.LL_OPERATIONS: canraise = lloperation.LL_OPERATIONS[ operation.opname].canraise if Exception not in canraise: exc = self.llinterpreter.find_exception(e) for canraiseexc in canraise: if issubclass(exc, canraiseexc): break else: raise TypeError( "the operation %s is not expected to raise %s" % (operation, exc)) # for exception-transformed graphs, store the LLException # into the exc_data used by this graph exc_data = self.llinterpreter.get_transformed_exc_data( self.graph) if exc_data: etype = e.args[0] evalue = e.args[1] exc_data.exc_type = etype exc_data.exc_value = evalue from pypy.translator import exceptiontransform retval = exceptiontransform.error_value( operation.result.concretetype) else: raise
def eval_operation(self, operation): tracer = self.llinterpreter.tracer if tracer: tracer.dump(str(operation)) ophandler = self.getoperationhandler(operation.opname) # XXX slighly unnice but an important safety check if operation.opname == 'direct_call': assert isinstance(operation.args[0], Constant) elif operation.opname == 'indirect_call': assert isinstance(operation.args[0], Variable) if getattr(ophandler, 'specialform', False): retval = ophandler(*operation.args) else: vals = [self.getval_or_subop(x) for x in operation.args] if getattr(ophandler, 'need_result_type', False): vals.insert(0, operation.result.concretetype) try: retval = ophandler(*vals) except LLException, e: # safety check check that the operation is allowed to raise that # exception if operation.opname in lloperation.LL_OPERATIONS: canraise = lloperation.LL_OPERATIONS[operation.opname].canraise if Exception not in canraise: exc = self.llinterpreter.find_exception(e) for canraiseexc in canraise: if issubclass(exc, canraiseexc): break else: raise TypeError("the operation %s is not expected to raise %s" % (operation, exc)) # for exception-transformed graphs, store the LLException # into the exc_data used by this graph exc_data = self.llinterpreter.get_transformed_exc_data( self.graph) if exc_data: etype = e.args[0] evalue = e.args[1] exc_data.exc_type = etype exc_data.exc_value = evalue from pypy.translator import exceptiontransform retval = exceptiontransform.error_value( operation.result.concretetype) else: raise
class LLFrame(object): def __init__(self, graph, args, llinterpreter): assert not graph or isinstance(graph, FunctionGraph) self.graph = graph self.args = args self.llinterpreter = llinterpreter self.heap = llinterpreter.heap self.bindings = {} self.curr_block = None self.curr_operation_index = 0 self.alloca_objects = [] # _______________________________________________________ # variable setters/getters helpers def clear(self): self.bindings.clear() def fillvars(self, block, values): vars = block.inputargs assert len(vars) == len(values), ( "block %s received %d args, expected %d" % (block, len(values), len(vars))) for var, val in zip(vars, values): self.setvar(var, val) def setvar(self, var, val): if var.concretetype is not lltype.Void: try: val = lltype.enforce(var.concretetype, val) except TypeError: assert False, "type error: input value of type:\n\n\t%r\n\n===> variable of type:\n\n\t%r\n" % ( lltype.typeOf(val), var.concretetype) assert isinstance(var, Variable) self.bindings[var] = val def setifvar(self, var, val): if isinstance(var, Variable): self.setvar(var, val) def getval(self, varorconst): try: val = varorconst.value except AttributeError: val = self.bindings[varorconst] if isinstance(val, ComputedIntSymbolic): val = val.compute_fn() if varorconst.concretetype is not lltype.Void: try: val = lltype.enforce(varorconst.concretetype, val) except TypeError: assert False, "type error: %r val from %r var/const" % ( lltype.typeOf(val), varorconst.concretetype) return val def getval_or_subop(self, varorsubop): from pypy.translator.oosupport.treebuilder import SubOperation if isinstance(varorsubop, SubOperation): self.eval_operation(varorsubop.op) resultval = self.getval(varorsubop.op.result) del self.bindings[varorsubop.op.result] # XXX hack return resultval else: return self.getval(varorsubop) # _______________________________________________________ # other helpers def getoperationhandler(self, opname): ophandler = getattr(self, 'op_' + opname, None) if ophandler is None: # try to import the operation from opimpl.py ophandler = lloperation.LL_OPERATIONS[opname].fold setattr(self.__class__, 'op_' + opname, staticmethod(ophandler)) return ophandler # _______________________________________________________ # evaling functions def eval(self): graph = self.graph tracer = self.llinterpreter.tracer if tracer: tracer.enter(graph) self.llinterpreter.frame_stack.append(self) try: try: nextblock = graph.startblock args = self.args while 1: self.clear() self.fillvars(nextblock, args) nextblock, args = self.eval_block(nextblock) if nextblock is None: for obj in self.alloca_objects: obj._obj._free() return args except Exception: self.llinterpreter.traceback_frames.append(self) raise finally: leavingframe = self.llinterpreter.frame_stack.pop() assert leavingframe is self if tracer: tracer.leave() def eval_block(self, block): """ return (nextblock, values) tuple. If nextblock is None, values is the concrete return value. """ self.curr_block = block catch_exception = block.exitswitch == c_last_exception e = None try: for i, op in enumerate(block.operations): self.curr_operation_index = i self.eval_operation(op) except LLException, e: if not (catch_exception and op is block.operations[-1]): raise # determine nextblock and/or return value if len(block.exits) == 0: # return block tracer = self.llinterpreter.tracer if len(block.inputargs) == 2: # exception if tracer: tracer.dump('raise') etypevar, evaluevar = block.getvariables() etype = self.getval(etypevar) evalue = self.getval(evaluevar) # watch out, these are _ptr's raise LLException(etype, evalue) resultvar, = block.getvariables() result = self.getval(resultvar) exc_data = self.llinterpreter.get_transformed_exc_data(self.graph) if exc_data: # re-raise the exception set by this graph, if any etype = exc_data.exc_type if etype: evalue = exc_data.exc_value if tracer: tracer.dump('raise') exc_data.exc_type = lltype.typeOf(etype)._defl() exc_data.exc_value = lltype.typeOf(evalue)._defl() from pypy.translator import exceptiontransform T = resultvar.concretetype errvalue = exceptiontransform.error_value(T) # check that the exc-transformed graph returns the error # value when it returns with an exception set assert result == errvalue raise LLException(etype, evalue) if tracer: tracer.dump('return') return None, result elif block.exitswitch is None: # single-exit block assert len(block.exits) == 1 link = block.exits[0] elif catch_exception: link = block.exits[0] if e: exdata = self.llinterpreter.typer.getexceptiondata() cls = e.args[0] inst = e.args[1] for link in block.exits[1:]: assert issubclass(link.exitcase, py.builtin.BaseException) if self.op_direct_call(exdata.fn_exception_match, cls, link.llexitcase): self.setifvar(link.last_exception, cls) self.setifvar(link.last_exc_value, inst) break else: # no handler found, pass on raise e else: llexitvalue = self.getval(block.exitswitch) if block.exits[-1].exitcase == "default": defaultexit = block.exits[-1] nondefaultexits = block.exits[:-1] assert defaultexit.llexitcase is None else: defaultexit = None nondefaultexits = block.exits for link in nondefaultexits: if link.llexitcase == llexitvalue: break # found -- the result is in 'link' else: if defaultexit is None: raise ValueError( "exit case %r not found in the exit links " "of %r" % (llexitvalue, block)) else: link = defaultexit return link.target, [self.getval(x) for x in link.args]
def __init__(self, hrtyper, fnobj, can_raise): ll_func = fnobj._callable FUNCTYPE = lltype.typeOf(fnobj) nb_args = len(FUNCTYPE.ARGS) self.can_raise = can_raise # parse the oopspec and fill in the arguments operation_name, args = ll_func.oopspec.split('(', 1) assert args.endswith(')') args = args[:-1] + ',' # trailing comma to force tuple syntax if args.strip() == ',': args = '()' argnames = ll_func.func_code.co_varnames[:nb_args] d = dict(zip(argnames, [Index(n) for n in range(nb_args)])) self.argtuple = eval(args, d) # end of rather XXX'edly hackish parsing OOPARGTYPES = [] arg_llsig_to_oopsig = {} for i, obj in enumerate(self.argtuple): if isinstance(obj, Index): arg_llsig_to_oopsig[obj.n] = i OOPARG = FUNCTYPE.ARGS[obj.n] else: OOPARG = lltype.typeOf(obj) OOPARGTYPES.append(OOPARG) self.residualargsources = [] for i in range(nb_args): ARGTYPE = FUNCTYPE.ARGS[i] if ARGTYPE is not lltype.Void: self.residualargsources.append(arg_llsig_to_oopsig[i]) RGenOp = hrtyper.RGenOp self.args_gv = [None] * nb_args fnptr = fnobj._as_ptr() self.gv_fnptr = RGenOp.constPrebuiltGlobal(fnptr) result_kind = RGenOp.kindToken(FUNCTYPE.RESULT) self.result_kind = result_kind if FUNCTYPE.RESULT is lltype.Void: self.errorbox = None else: error_value = exceptiontransform.error_value(FUNCTYPE.RESULT) self.errorbox = rvalue.redbox_from_prebuilt_value(RGenOp, error_value) redboxbuilder = rvalue.ll_redboxbuilder(FUNCTYPE.RESULT) self.redboxbuilder = redboxbuilder self.sigtoken = RGenOp.sigToken(FUNCTYPE) if operation_name == 'newlist': typename, method = 'list', 'oop_newlist' SELFTYPE = FUNCTYPE.RESULT.TO self.is_method = False elif operation_name == 'newdict': typename, method = 'dict', 'oop_newdict' SELFTYPE = FUNCTYPE.RESULT.TO self.is_method = False else: typename, method = operation_name.split('.') method = 'oop_%s_%s' % (typename, method) SELFTYPE = FUNCTYPE.ARGS[self.argtuple[0].n].TO self.is_method = True vmodule = __import__('pypy.jit.timeshifter.v%s' % (typename,), None, None, [method]) self.typedesc = vmodule.TypeDesc(hrtyper, SELFTYPE) self.ll_handler = getattr(vmodule, method) self.couldfold = getattr(self.ll_handler, 'couldfold', False) if self.couldfold: oopargcheck = ll_func.oopargcheck # required if couldfold=True # make a copy of the function, for specialization purposes oopargcheck = func_with_new_name(oopargcheck, 'argcheck_%s' % (method,)) ARGS = FUNCTYPE.ARGS residualargsources = self.residualargsources unrolling_ARGS = unrolling_iterable(ARGS) unrolling_OOPARGS = unrolling_iterable(enumerate(OOPARGTYPES)) def do_call(jitstate, argboxes): oopargs = () for i, ARG in unrolling_OOPARGS: v = rvalue.ll_getvalue(argboxes[i], ARG) oopargs += (v,) if not oopargcheck(*oopargs): raise SegfaultException args = () j = 0 for ARG in unrolling_ARGS: if ARG == lltype.Void: v = None else: argsrc = residualargsources[j] j = j + 1 v = oopargs[argsrc] args += (v,) result = fnptr(*args) if FUNCTYPE.RESULT == lltype.Void: return None return rvalue.ll_fromvalue(jitstate, result) self.do_call = do_call # hack! to avoid confusion between the .typedesc attribute # of oopspecdescs of different types (lists, dicts, etc.) # let's use different subclasses for the oopspecdesc too. self.__class__ = globals()['OopSpecDesc_%s' % typename]