Author: Ilya Osadchiy <osadchiy.i...@gmail.com> Branch: Changeset: r44899:6b6f75812436 Date: 2011-06-03 00:11 +0300 http://bitbucket.org/pypy/pypy/changeset/6b6f75812436/
Log: merge upstream diff --git a/pypy/annotation/annrpython.py b/pypy/annotation/annrpython.py --- a/pypy/annotation/annrpython.py +++ b/pypy/annotation/annrpython.py @@ -228,7 +228,7 @@ # graph -- it's already low-level operations! for a, s_newarg in zip(graph.getargs(), cells): s_oldarg = self.binding(a) - assert s_oldarg.contains(s_newarg) + assert annmodel.unionof(s_oldarg, s_newarg) == s_oldarg else: assert not self.frozen for a in cells: diff --git a/pypy/jit/backend/llsupport/regalloc.py b/pypy/jit/backend/llsupport/regalloc.py --- a/pypy/jit/backend/llsupport/regalloc.py +++ b/pypy/jit/backend/llsupport/regalloc.py @@ -213,6 +213,15 @@ self.reg_bindings[v] = loc return loc + def force_spill_var(self, var): + self._sync_var(var) + try: + loc = self.reg_bindings[var] + del self.reg_bindings[var] + self.free_regs.append(loc) + except KeyError: + pass # 'var' is already not in a register + def loc(self, box): """ Return the location of 'box'. """ diff --git a/pypy/jit/backend/test/calling_convention_test.py b/pypy/jit/backend/test/calling_convention_test.py --- a/pypy/jit/backend/test/calling_convention_test.py +++ b/pypy/jit/backend/test/calling_convention_test.py @@ -23,6 +23,7 @@ def constfloat(x): return ConstFloat(longlong.getfloatstorage(x)) + class FakeStats(object): pass class TestCallingConv(Runner): @@ -30,15 +31,131 @@ Ptr = lltype.Ptr FuncType = lltype.FuncType - def __init__(self): - self.cpu = getcpuclass()(rtyper=None, stats=FakeStats()) - self.cpu.setup_once() + def setup_class(cls): + cls.cpu = getcpuclass()(rtyper=None, stats=FakeStats()) + cls.cpu.setup_once() + + def _prepare_args(self, args, floats, ints): + local_floats = list(floats) + local_ints = list(ints) + expected_result = 0.0 + for i in range(len(args)): + x = args[i] + if x[0] == 'f': + x = local_floats.pop() + t = longlong.getfloatstorage(x) + self.cpu.set_future_value_float(i, t) + else: + x = local_ints.pop() + self.cpu.set_future_value_int(i, x) + expected_result += x + return expected_result @classmethod def get_funcbox(cls, cpu, func_ptr): addr = llmemory.cast_ptr_to_adr(func_ptr) return ConstInt(heaptracker.adr2int(addr)) + def test_call_aligned_with_spilled_values(self): + from pypy.rlib.libffi import types + cpu = self.cpu + if not cpu.supports_floats: + py.test.skip('requires floats') + + + def func(*args): + return float(sum(args)) + + F = lltype.Float + I = lltype.Signed + floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56] + ints = [7, 11, 23, 13, -42, 1111, 95, 1] + for case in range(256): + local_floats = list(floats) + local_ints = list(ints) + args = [] + spills = [] + funcargs = [] + float_count = 0 + int_count = 0 + for i in range(8): + if case & (1<<i): + args.append('f%d' % float_count) + spills.append('force_spill(f%d)' % float_count) + float_count += 1 + funcargs.append(F) + else: + args.append('i%d' % int_count) + spills.append('force_spill(i%d)' % int_count) + int_count += 1 + funcargs.append(I) + + arguments = ', '.join(args) + spill_ops = '\n'.join(spills) + + FUNC = self.FuncType(funcargs, F) + FPTR = self.Ptr(FUNC) + func_ptr = llhelper(FPTR, func) + calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + funcbox = self.get_funcbox(cpu, func_ptr) + + ops = '[%s]\n' % arguments + ops += '%s\n' % spill_ops + ops += 'f99 = call(ConstClass(func_ptr), %s, descr=calldescr)\n' % arguments + ops += 'finish(f99, %s)\n' % arguments + + loop = parse(ops, namespace=locals()) + looptoken = LoopToken() + done_number = self.cpu.get_fail_descr_number(loop.operations[-1].getdescr()) + self.cpu.compile_loop(loop.inputargs, loop.operations, looptoken) + expected_result = self._prepare_args(args, floats, ints) + + res = self.cpu.execute_token(looptoken) + x = longlong.getrealfloat(cpu.get_latest_value_float(0)) + assert abs(x - expected_result) < 0.0001 + + def test_call_aligned_with_imm_values(self): + from pypy.rlib.libffi import types + cpu = self.cpu + if not cpu.supports_floats: + py.test.skip('requires floats') + + + def func(*args): + return float(sum(args)) + + F = lltype.Float + I = lltype.Signed + floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56] + ints = [7, 11, 23, 13, -42, 1111, 95, 1] + for case in range(256): + result = 0.0 + args = [] + argslist = [] + local_floats = list(floats) + local_ints = list(ints) + for i in range(8): + if case & (1<<i): + args.append(F) + arg = local_floats.pop() + result += arg + argslist.append(constfloat(arg)) + else: + args.append(I) + arg = local_ints.pop() + result += arg + argslist.append(ConstInt(arg)) + FUNC = self.FuncType(args, F) + FPTR = self.Ptr(FUNC) + func_ptr = llhelper(FPTR, func) + calldescr = cpu.calldescrof(FUNC, FUNC.ARGS, FUNC.RESULT) + funcbox = self.get_funcbox(cpu, func_ptr) + + res = self.execute_operation(rop.CALL, + [funcbox] + argslist, + 'float', descr=calldescr) + assert abs(res.getfloat() - result) < 0.0001 + def test_call_aligned_with_args_on_the_stack(self): from pypy.rlib.libffi import types cpu = self.cpu @@ -104,21 +221,6 @@ floats = [0.7, 5.8, 0.1, 0.3, 0.9, -2.34, -3.45, -4.56] ints = [7, 11, 23, 42, -42, 1111, 95, 1] - def _prepare_args(args): - local_floats = list(floats) - local_ints = list(ints) - expected_result = 0.0 - for i in range(len(args)): - x = args[i] - if x[0] == 'f': - x = local_floats.pop() - t = longlong.getfloatstorage(x) - cpu.set_future_value_float(i, t) - else: - x = local_ints.pop() - cpu.set_future_value_int(i, x) - expected_result += x - return expected_result for case in range(256): float_count = 0 @@ -152,7 +254,7 @@ done_number = self.cpu.get_fail_descr_number(called_loop.operations[-1].getdescr()) self.cpu.compile_loop(called_loop.inputargs, called_loop.operations, called_looptoken) - expected_result = _prepare_args(args) + expected_result = self._prepare_args(args, floats, ints) res = cpu.execute_token(called_looptoken) assert res.identifier == 3 t = longlong.getrealfloat(cpu.get_latest_value_float(0)) @@ -181,7 +283,7 @@ self.cpu.compile_loop(loop.inputargs, loop.operations, othertoken) # prepare call to called_loop - _prepare_args(args) + self._prepare_args(args, floats, ints) res = cpu.execute_token(othertoken) x = longlong.getrealfloat(cpu.get_latest_value_float(0)) assert res.identifier == 4 diff --git a/pypy/jit/backend/x86/regalloc.py b/pypy/jit/backend/x86/regalloc.py --- a/pypy/jit/backend/x86/regalloc.py +++ b/pypy/jit/backend/x86/regalloc.py @@ -268,6 +268,12 @@ return self.rm.force_allocate_reg(var, forbidden_vars, selected_reg, need_lower_byte) + def force_spill_var(self, var): + if var.type == FLOAT: + return self.xrm.force_spill_var(var) + else: + return self.rm.force_spill_var(var) + def load_xmm_aligned_16_bytes(self, var, forbidden_vars=[]): # Load 'var' in a register; but if it is a constant, we can return # a 16-bytes-aligned ConstFloatLoc. @@ -418,6 +424,8 @@ if self.can_merge_with_next_guard(op, i, operations): oplist_with_guard[op.getopnum()](self, op, operations[i + 1]) i += 1 + elif not we_are_translated() and op.getopnum() == -124: + self._consider_force_spill(op) else: oplist[op.getopnum()](self, op) if op.result is not None: @@ -1293,6 +1301,10 @@ def consider_jit_debug(self, op): pass + def _consider_force_spill(self, op): + # This operation is used only for testing + self.force_spill_var(op.getarg(0)) + def get_mark_gc_roots(self, gcrootmap, use_copy_area=False): shape = gcrootmap.get_basic_shape(IS_X86_64) for v, val in self.fm.frame_bindings.items(): diff --git a/pypy/jit/metainterp/compile.py b/pypy/jit/metainterp/compile.py --- a/pypy/jit/metainterp/compile.py +++ b/pypy/jit/metainterp/compile.py @@ -124,18 +124,21 @@ return old_loop_token if loop.preamble.operations is not None: - send_loop_to_backend(metainterp_sd, loop, "loop") + send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, + "loop") record_loop_or_bridge(metainterp_sd, loop) token = loop.preamble.token if full_preamble_needed: - send_loop_to_backend(metainterp_sd, loop.preamble, "entry bridge") + send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, + loop.preamble, "entry bridge") insert_loop_token(old_loop_tokens, loop.preamble.token) jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp( greenkey, loop.preamble.token) record_loop_or_bridge(metainterp_sd, loop.preamble) return token else: - send_loop_to_backend(metainterp_sd, loop, "loop") + send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, + "loop") insert_loop_token(old_loop_tokens, loop_token) jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp( greenkey, loop.token) @@ -150,7 +153,9 @@ # XXX do we still need a list? old_loop_tokens.append(loop_token) -def send_loop_to_backend(metainterp_sd, loop, type): +def send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, type): + jitdriver_sd.on_compile(metainterp_sd.logger_ops, loop.token, + loop.operations, type, greenkey) globaldata = metainterp_sd.globaldata loop_token = loop.token loop_token.number = n = globaldata.loopnumbering @@ -186,8 +191,11 @@ if metainterp_sd.warmrunnerdesc is not None: # for tests metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(loop.token) -def send_bridge_to_backend(metainterp_sd, faildescr, inputargs, operations, - original_loop_token): +def send_bridge_to_backend(jitdriver_sd, metainterp_sd, faildescr, inputargs, + operations, original_loop_token): + n = metainterp_sd.cpu.get_fail_descr_number(faildescr) + jitdriver_sd.on_compile_bridge(metainterp_sd.logger_ops, + original_loop_token, operations, n) if not we_are_translated(): show_loop(metainterp_sd) TreeLoop.check_consistency_of(inputargs, operations) @@ -204,7 +212,6 @@ metainterp_sd.stats.compiled() metainterp_sd.log("compiled new bridge") # - n = metainterp_sd.cpu.get_fail_descr_number(faildescr) metainterp_sd.logger_ops.log_bridge(inputargs, operations, n, ops_offset) # if metainterp_sd.warmrunnerdesc is not None: # for tests @@ -390,8 +397,9 @@ inputargs = metainterp.history.inputargs if not we_are_translated(): self._debug_suboperations = new_loop.operations - send_bridge_to_backend(metainterp.staticdata, self, inputargs, - new_loop.operations, new_loop.token) + send_bridge_to_backend(metainterp.jitdriver_sd, metainterp.staticdata, + self, inputargs, new_loop.operations, + new_loop.token) def copy_all_attributes_into(self, res): # XXX a bit ugly to have to list them all here @@ -570,7 +578,8 @@ # to every guard in the loop. new_loop_token = make_loop_token(len(redargs), jitdriver_sd) new_loop.token = new_loop_token - send_loop_to_backend(metainterp_sd, new_loop, "entry bridge") + send_loop_to_backend(self.original_greenkey, metainterp.jitdriver_sd, + metainterp_sd, new_loop, "entry bridge") # send the new_loop to warmspot.py, to be called directly the next time jitdriver_sd.warmstate.attach_unoptimized_bridge_from_interp( self.original_greenkey, diff --git a/pypy/jit/metainterp/jitdriver.py b/pypy/jit/metainterp/jitdriver.py --- a/pypy/jit/metainterp/jitdriver.py +++ b/pypy/jit/metainterp/jitdriver.py @@ -20,6 +20,7 @@ # self.portal_finishtoken... pypy.jit.metainterp.pyjitpl # self.index ... pypy.jit.codewriter.call # self.mainjitcode ... pypy.jit.codewriter.call + # self.on_compile ... pypy.jit.metainterp.warmstate # These attributes are read by the backend in CALL_ASSEMBLER: # self.assembler_helper_adr diff --git a/pypy/jit/metainterp/logger.py b/pypy/jit/metainterp/logger.py --- a/pypy/jit/metainterp/logger.py +++ b/pypy/jit/metainterp/logger.py @@ -75,6 +75,40 @@ else: return '?' + def repr_of_resop(self, memo, op, ops_offset=None): + if op.getopnum() == rop.DEBUG_MERGE_POINT: + loc = op.getarg(0)._get_str() + reclev = op.getarg(1).getint() + return "debug_merge_point('%s', %s)" % (loc, reclev) + if ops_offset is None: + offset = -1 + else: + offset = ops_offset.get(op, -1) + if offset == -1: + s_offset = "" + else: + s_offset = "+%d: " % offset + args = ", ".join([self.repr_of_arg(memo, op.getarg(i)) for i in range(op.numargs())]) + if op.result is not None: + res = self.repr_of_arg(memo, op.result) + " = " + else: + res = "" + is_guard = op.is_guard() + if op.getdescr() is not None: + descr = op.getdescr() + if is_guard and self.guard_number: + index = self.metainterp_sd.cpu.get_fail_descr_number(descr) + r = "<Guard%d>" % index + else: + r = self.repr_of_descr(descr) + args += ', descr=' + r + if is_guard and op.getfailargs() is not None: + fail_args = ' [' + ", ".join([self.repr_of_arg(memo, arg) + for arg in op.getfailargs()]) + ']' + else: + fail_args = '' + return s_offset + res + op.getopname() + '(' + args + ')' + fail_args + def _log_operations(self, inputargs, operations, ops_offset): if not have_debug_prints(): return @@ -86,37 +120,7 @@ debug_print('[' + args + ']') for i in range(len(operations)): op = operations[i] - if op.getopnum() == rop.DEBUG_MERGE_POINT: - loc = op.getarg(0)._get_str() - reclev = op.getarg(1).getint() - debug_print("debug_merge_point('%s', %s)" % (loc, reclev)) - continue - offset = ops_offset.get(op, -1) - if offset == -1: - s_offset = "" - else: - s_offset = "+%d: " % offset - args = ", ".join([self.repr_of_arg(memo, op.getarg(i)) for i in range(op.numargs())]) - if op.result is not None: - res = self.repr_of_arg(memo, op.result) + " = " - else: - res = "" - is_guard = op.is_guard() - if op.getdescr() is not None: - descr = op.getdescr() - if is_guard and self.guard_number: - index = self.metainterp_sd.cpu.get_fail_descr_number(descr) - r = "<Guard%d>" % index - else: - r = self.repr_of_descr(descr) - args += ', descr=' + r - if is_guard and op.getfailargs() is not None: - fail_args = ' [' + ", ".join([self.repr_of_arg(memo, arg) - for arg in op.getfailargs()]) + ']' - else: - fail_args = '' - debug_print(s_offset + res + op.getopname() + - '(' + args + ')' + fail_args) + debug_print(self.repr_of_resop(memo, operations[i], ops_offset)) if ops_offset and None in ops_offset: offset = ops_offset[None] debug_print("+%d: --end of the loop--" % offset) diff --git a/pypy/jit/metainterp/pyjitpl.py b/pypy/jit/metainterp/pyjitpl.py --- a/pypy/jit/metainterp/pyjitpl.py +++ b/pypy/jit/metainterp/pyjitpl.py @@ -867,7 +867,6 @@ any_operation = len(self.metainterp.history.operations) > 0 jitdriver_sd = self.metainterp.staticdata.jitdrivers_sd[jdindex] self.verify_green_args(jitdriver_sd, greenboxes) - # xxx we may disable the following line in some context later self.debug_merge_point(jitdriver_sd, self.metainterp.in_recursion, greenboxes) diff --git a/pypy/jit/metainterp/test/support.py b/pypy/jit/metainterp/test/support.py --- a/pypy/jit/metainterp/test/support.py +++ b/pypy/jit/metainterp/test/support.py @@ -51,6 +51,8 @@ greenfield_info = None result_type = result_kind portal_runner_ptr = "???" + on_compile = lambda *args: None + on_compile_bridge = lambda *args: None stats = history.Stats() cpu = CPUClass(rtyper, stats, None, False) diff --git a/pypy/jit/metainterp/test/test_jitdriver.py b/pypy/jit/metainterp/test/test_jitdriver.py --- a/pypy/jit/metainterp/test/test_jitdriver.py +++ b/pypy/jit/metainterp/test/test_jitdriver.py @@ -10,8 +10,59 @@ def getloc2(g): return "in jitdriver2, with g=%d" % g +class JitDriverTests(object): + def test_on_compile(self): + called = {} + + class MyJitDriver(JitDriver): + def on_compile(self, logger, looptoken, operations, type, n, m): + called[(m, n, type)] = looptoken -class MultipleJitDriversTests: + driver = MyJitDriver(greens = ['n', 'm'], reds = ['i']) + + def loop(n, m): + i = 0 + while i < n + m: + driver.can_enter_jit(n=n, m=m, i=i) + driver.jit_merge_point(n=n, m=m, i=i) + i += 1 + + self.meta_interp(loop, [1, 4]) + assert sorted(called.keys()) == [(4, 1, "entry bridge"), (4, 1, "loop")] + self.meta_interp(loop, [2, 4]) + assert sorted(called.keys()) == [(4, 1, "entry bridge"), (4, 1, "loop"), + (4, 2, "entry bridge"), (4, 2, "loop")] + + def test_on_compile_bridge(self): + called = {} + + class MyJitDriver(JitDriver): + def on_compile(self, logger, looptoken, operations, type, n, m): + called[(m, n, type)] = loop + def on_compile_bridge(self, logger, orig_token, operations, n): + assert 'bridge' not in called + called['bridge'] = orig_token + + driver = MyJitDriver(greens = ['n', 'm'], reds = ['i']) + + def loop(n, m): + i = 0 + while i < n + m: + driver.can_enter_jit(n=n, m=m, i=i) + driver.jit_merge_point(n=n, m=m, i=i) + if i >= 4: + i += 2 + i += 1 + + self.meta_interp(loop, [1, 10]) + assert sorted(called.keys()) == ['bridge', (10, 1, "entry bridge"), + (10, 1, "loop")] + + +class TestLLtypeSingle(JitDriverTests, LLJitMixin): + pass + +class MultipleJitDriversTests(object): def test_simple(self): myjitdriver1 = JitDriver(greens=[], reds=['n', 'm'], diff --git a/pypy/jit/metainterp/warmstate.py b/pypy/jit/metainterp/warmstate.py --- a/pypy/jit/metainterp/warmstate.py +++ b/pypy/jit/metainterp/warmstate.py @@ -566,6 +566,19 @@ return can_inline_greenargs(*greenargs) self.can_inline_greenargs = can_inline_greenargs self.can_inline_callable = can_inline_callable + if hasattr(jd.jitdriver, 'on_compile'): + def on_compile(logger, token, operations, type, greenkey): + greenargs = unwrap_greenkey(greenkey) + return jd.jitdriver.on_compile(logger, token, operations, type, + *greenargs) + def on_compile_bridge(logger, orig_token, operations, n): + return jd.jitdriver.on_compile_bridge(logger, orig_token, + operations, n) + jd.on_compile = on_compile + jd.on_compile_bridge = on_compile_bridge + else: + jd.on_compile = lambda *args: None + jd.on_compile_bridge = lambda *args: None def get_assembler_token(greenkey, redboxes): # 'redboxes' is only used to know the types of red arguments diff --git a/pypy/jit/tl/tinyframe/test/test_tinyframe.py b/pypy/jit/tl/tinyframe/test/test_tinyframe.py --- a/pypy/jit/tl/tinyframe/test/test_tinyframe.py +++ b/pypy/jit/tl/tinyframe/test/test_tinyframe.py @@ -96,11 +96,12 @@ RETURN r1 ''') s = StringIO() + prev = sys.stdout sys.stdout = s try: interpret(code) finally: - sys.stdout = sys.__stdout__ + sys.stdout = prev lines = s.getvalue().splitlines() assert lines == [ '0', diff --git a/pypy/jit/tool/oparser.py b/pypy/jit/tool/oparser.py --- a/pypy/jit/tool/oparser.py +++ b/pypy/jit/tool/oparser.py @@ -6,7 +6,9 @@ from pypy.jit.metainterp.history import TreeLoop, BoxInt, ConstInt,\ ConstObj, ConstPtr, Box, BasicFailDescr, BoxFloat, ConstFloat,\ LoopToken, get_const_ptr_for_string, get_const_ptr_for_unicode -from pypy.jit.metainterp.resoperation import rop, ResOperation, ResOpWithDescr, N_aryOp +from pypy.jit.metainterp.resoperation import rop, ResOperation, \ + ResOpWithDescr, N_aryOp, \ + UnaryOp, PlainResOp from pypy.jit.metainterp.typesystem import llhelper from pypy.jit.codewriter.heaptracker import adr2int from pypy.jit.codewriter import longlong @@ -35,6 +37,23 @@ def clone(self): return ESCAPE_OP(self.OPNUM, self.getarglist()[:], self.result, self.getdescr()) +class FORCE_SPILL(UnaryOp, PlainResOp): + + OPNUM = -124 + + def __init__(self, opnum, args, result=None, descr=None): + assert result is None + assert descr is None + assert opnum == self.OPNUM + self.result = result + self.initarglist(args) + + def getopnum(self): + return self.OPNUM + + def clone(self): + return FORCE_SPILL(self.OPNUM, self.getarglist()[:]) + class ExtendedTreeLoop(TreeLoop): def getboxes(self): @@ -220,6 +239,8 @@ except AttributeError: if opname == 'escape': opnum = ESCAPE_OP.OPNUM + elif opname == 'force_spill': + opnum = FORCE_SPILL.OPNUM else: raise ParseError("unknown op: %s" % opname) endnum = line.rfind(')') @@ -261,6 +282,8 @@ def create_op(self, opnum, args, result, descr): if opnum == ESCAPE_OP.OPNUM: return ESCAPE_OP(opnum, args, result, descr) + if opnum == FORCE_SPILL.OPNUM: + return FORCE_SPILL(opnum, args, result, descr) else: return ResOperation(opnum, args, result, descr) diff --git a/pypy/module/_file/interp_file.py b/pypy/module/_file/interp_file.py --- a/pypy/module/_file/interp_file.py +++ b/pypy/module/_file/interp_file.py @@ -349,11 +349,11 @@ may be returned, even if no size parameter was given.""") _decl(locals(), "readline", - """readlines([size]) -> list of strings, each a line from the file. + """readline([size]) -> next line from the file, as a string. -Call readline() repeatedly and return a list of the lines so read. -The optional size argument, if given, is an approximate bound on the -total number of bytes in the lines returned.""") +Retain newline. A non-negative size argument limits the maximum +number of bytes to return (an incomplete line may be returned then). +Return an empty string at EOF.""") _decl(locals(), "readlines", """readlines([size]) -> list of strings, each a line from the file. diff --git a/pypy/module/bz2/interp_bz2.py b/pypy/module/bz2/interp_bz2.py --- a/pypy/module/bz2/interp_bz2.py +++ b/pypy/module/bz2/interp_bz2.py @@ -363,42 +363,44 @@ def seek(self, offset, whence): READMAX = 2**18 # 256KB - if whence == 1: - if offset >= 0: - read = r_longlong(0) - while read < offset: - count = offset - read - if count < READMAX: - count = intmask(count) - else: - count = READMAX - read += len(self.read(count)) - else: - pos = self.readlength + offset - self.seek(pos, 0) + + # Make offset relative to the start of the file + if whence == 2: + # Read everything to arrive at the end + while len(self.read(READMAX)) > 0: + pass + offset += self.readlength + elif whence == 1: + offset += self.readlength elif whence == 0: + pass + else: + raise operationerrfmt(self.space.w_ValueError, + "Invalid value for whence: %d", whence) + + # Make offset relative to the current pos + # Rewind iff necessary + if offset < self.readlength: self.stream.seek(0, 0) self.decompressor = W_BZ2Decompressor(self.space) self.readlength = r_longlong(0) self.buffer = "" self.finished = False - read = 0 - while read < offset: - count = offset - read - if count < READMAX: - count = intmask(count) - else: - count = READMAX - length = len(self.read(count)) - read += length - if not length: - break else: - # first measure the length by reading everything left - while len(self.read(READMAX)) > 0: - pass - pos = self.readlength + offset - self.seek(pos, 0) + offset -= self.readlength + + # Seek + read = r_longlong(0) + while read < offset: + count = offset - read + if count < READMAX: + count = intmask(count) + else: + count = READMAX + length = len(self.read(count)) + if not length: + break + read += length def readall(self): w_result = self.decompressor.decompress(self.stream.readall()) diff --git a/pypy/module/cpyext/test/test_sysmodule.py b/pypy/module/cpyext/test/test_sysmodule.py --- a/pypy/module/cpyext/test/test_sysmodule.py +++ b/pypy/module/cpyext/test/test_sysmodule.py @@ -22,12 +22,13 @@ Py_RETURN_NONE; """)]) import sys, StringIO + prev = sys.stdout sys.stdout = StringIO.StringIO() try: module.writestdout() assert sys.stdout.getvalue() == "format: 42\n" finally: - sys.stdout = sys.__stdout__ + sys.stdout = prev class TestSysModule(BaseApiTest): def test_sysmodule(self, space, api): diff --git a/pypy/module/oracle/__init__.py b/pypy/module/oracle/__init__.py --- a/pypy/module/oracle/__init__.py +++ b/pypy/module/oracle/__init__.py @@ -28,6 +28,7 @@ appleveldefs = { 'version': 'app_oracle.version', + 'paramstyle': 'app_oracle.paramstyle', 'makedsn': 'app_oracle.makedsn', 'TimestampFromTicks': 'app_oracle.TimestampFromTicks', } diff --git a/pypy/module/oracle/app_oracle.py b/pypy/module/oracle/app_oracle.py --- a/pypy/module/oracle/app_oracle.py +++ b/pypy/module/oracle/app_oracle.py @@ -1,4 +1,5 @@ version = '5.0.0' +paramstyle = 'named' class Warning(StandardError): pass diff --git a/pypy/module/oracle/interp_connect.py b/pypy/module/oracle/interp_connect.py --- a/pypy/module/oracle/interp_connect.py +++ b/pypy/module/oracle/interp_connect.py @@ -159,9 +159,20 @@ # set the internal and external names; these are needed for global # transactions but are limited in terms of the lengths of the strings if twophase: - raise OperationError( - interp_error.get(space).w_NotSupportedError, - space.wrap("XXX write me")) + status = roci.OCIAttrSet( + self.serverHandle, roci.OCI_HTYPE_SERVER, + "cx_Oracle", 0, + roci.OCI_ATTR_INTERNAL_NAME, + self.environment.errorHandle) + self.environment.checkForError( + status, "Connection_Connect(): set internal name") + status = roci.OCIAttrSet( + self.serverHandle, roci.OCI_HTYPE_SERVER, + "cx_Oracle", 0, + roci.OCI_ATTR_EXTERNAL_NAME, + self.environment.errorHandle) + self.environment.checkForError( + status, "Connection_Connect(): set external name") # allocate the session handle handleptr = lltype.malloc(rffi.CArrayPtr(roci.OCISession).TO, diff --git a/pypy/module/oracle/roci.py b/pypy/module/oracle/roci.py --- a/pypy/module/oracle/roci.py +++ b/pypy/module/oracle/roci.py @@ -73,7 +73,8 @@ defines = ''' OCI_ATTR_SERVER OCI_ATTR_SESSION OCI_ATTR_USERNAME OCI_ATTR_PASSWORD OCI_ATTR_STMT_TYPE OCI_ATTR_PARAM OCI_ATTR_PARAM_COUNT OCI_ATTR_ROW_COUNT - OCI_ATTR_NAME OCI_ATTR_SCALE OCI_ATTR_PRECISION OCI_ATTR_IS_NULL + OCI_ATTR_NAME OCI_ATTR_INTERNAL_NAME OCI_ATTR_EXTERNAL_NAME + OCI_ATTR_SCALE OCI_ATTR_PRECISION OCI_ATTR_IS_NULL OCI_ATTR_DATA_SIZE OCI_ATTR_DATA_TYPE OCI_ATTR_REF_TDO OCI_ATTR_SCHEMA_NAME OCI_ATTR_TYPE_NAME OCI_ATTR_TYPECODE OCI_ATTR_NUM_TYPE_ATTRS OCI_ATTR_LIST_TYPE_ATTRS diff --git a/pypy/module/oracle/test/test_connect.py b/pypy/module/oracle/test/test_connect.py --- a/pypy/module/oracle/test/test_connect.py +++ b/pypy/module/oracle/test/test_connect.py @@ -41,6 +41,10 @@ if hasattr(self, 'cnx'): self.cnx.close() + def test_constants(self): + assert '.' in oracle.version + assert oracle.paramstyle == 'named' + def test_connect(self): self.cnx = oracle.connect(self.username, self.password, self.tnsentry, threaded=True) @@ -49,6 +53,13 @@ assert self.cnx.tnsentry == self.tnsentry assert isinstance(self.cnx.version, str) + def test_connect_twophase(self): + self.cnx = oracle.connect(self.username, self.password, + self.tnsentry, twophase=True) + assert self.cnx.username == self.username + assert self.cnx.password == self.password + assert self.cnx.tnsentry == self.tnsentry + def test_singleArg(self): self.cnx = oracle.connect("%s/%s@%s" % (self.username, self.password, self.tnsentry)) diff --git a/pypy/module/pypyjit/__init__.py b/pypy/module/pypyjit/__init__.py --- a/pypy/module/pypyjit/__init__.py +++ b/pypy/module/pypyjit/__init__.py @@ -7,13 +7,15 @@ interpleveldefs = { 'set_param': 'interp_jit.set_param', 'residual_call': 'interp_jit.residual_call', + 'set_compile_hook': 'interp_jit.set_compile_hook', } def setup_after_space_initialization(self): # force the __extend__ hacks to occur early - import pypy.module.pypyjit.interp_jit + from pypy.module.pypyjit.interp_jit import pypyjitdriver # add the 'defaults' attribute from pypy.rlib.jit import PARAMETERS space = self.space + pypyjitdriver.space = space w_obj = space.wrap(PARAMETERS) space.setattr(space.wrap(self), space.wrap('defaults'), w_obj) diff --git a/pypy/module/pypyjit/interp_jit.py b/pypy/module/pypyjit/interp_jit.py --- a/pypy/module/pypyjit/interp_jit.py +++ b/pypy/module/pypyjit/interp_jit.py @@ -12,6 +12,8 @@ from pypy.interpreter.pycode import PyCode, CO_GENERATOR from pypy.interpreter.pyframe import PyFrame from pypy.interpreter.pyopcode import ExitFrame +from pypy.interpreter.gateway import unwrap_spec +from pypy.interpreter.baseobjspace import ObjSpace, W_Root from opcode import opmap from pypy.rlib.objectmodel import we_are_translated @@ -49,6 +51,44 @@ greens = ['next_instr', 'is_being_profiled', 'pycode'] virtualizables = ['frame'] + def on_compile(self, logger, looptoken, operations, type, next_instr, + is_being_profiled, ll_pycode): + from pypy.rpython.annlowlevel import cast_base_ptr_to_instance + + space = self.space + cache = space.fromcache(Cache) + if space.is_true(cache.w_compile_hook): + memo = {} + list_w = [space.wrap(logger.repr_of_resop(memo, op)) + for op in operations] + pycode = cast_base_ptr_to_instance(PyCode, ll_pycode) + try: + space.call_function(cache.w_compile_hook, + space.wrap('main'), + space.wrap(type), + space.newtuple([pycode, + space.wrap(next_instr), + space.wrap(is_being_profiled)]), + space.newlist(list_w)) + except OperationError, e: + e.write_unraisable(space, "jit hook ", cache.w_compile_hook) + + def on_compile_bridge(self, logger, orig_looptoken, operations, n): + space = self.space + cache = space.fromcache(Cache) + if space.is_true(cache.w_compile_hook): + memo = {} + list_w = [space.wrap(logger.repr_of_resop(memo, op)) + for op in operations] + try: + space.call_function(cache.w_compile_hook, + space.wrap('main'), + space.wrap('bridge'), + space.wrap(n), + space.newlist(list_w)) + except OperationError, e: + e.write_unraisable(space, "jit hook ", cache.w_compile_hook) + pypyjitdriver = PyPyJitDriver(get_printable_location = get_printable_location, get_jitcell_at = get_jitcell_at, set_jitcell_at = set_jitcell_at, @@ -149,3 +189,28 @@ '''For testing. Invokes callable(...), but without letting the JIT follow the call.''' return space.call_args(w_callable, __args__) + +class Cache(object): + def __init__(self, space): + self.w_compile_hook = space.w_None + +@unwrap_spec(ObjSpace, W_Root) +def set_compile_hook(space, w_hook): + """ set_compile_hook(hook) + + Set a compiling hook that will be called each time a loop is compiled. + The hook will be called with the following signature: + hook(merge_point_type, loop_type, greenkey or guard_number, operations) + + for now merge point type is always `main` + + loop_type can be either `loop` `entry_bridge` or `bridge` + in case loop is not `bridge`, greenkey will be a set of constants + for jit merge point. in case it's `main` it'll be a tuple + (code, offset, is_being_profiled) + + XXX write down what else + """ + cache = space.fromcache(Cache) + cache.w_compile_hook = w_hook + return space.w_None diff --git a/pypy/module/pypyjit/test/test_jit_hook.py b/pypy/module/pypyjit/test/test_jit_hook.py new file mode 100644 --- /dev/null +++ b/pypy/module/pypyjit/test/test_jit_hook.py @@ -0,0 +1,89 @@ + +import py +from pypy.conftest import gettestobjspace, option +from pypy.interpreter.pycode import PyCode +from pypy.interpreter.gateway import interp2app +from pypy.jit.metainterp.history import LoopToken +from pypy.jit.metainterp.resoperation import ResOperation, rop +from pypy.jit.metainterp.logger import Logger +from pypy.rpython.annlowlevel import (cast_instance_to_base_ptr, + cast_base_ptr_to_instance) +from pypy.module.pypyjit.interp_jit import pypyjitdriver +from pypy.jit.tool.oparser import parse +from pypy.jit.metainterp.typesystem import llhelper + +class MockSD(object): + class cpu: + ts = llhelper + +class AppTestJitHook(object): + def setup_class(cls): + if option.runappdirect: + py.test.skip("Can't run this test with -A") + space = gettestobjspace(usemodules=('pypyjit',)) + cls.space = space + w_f = space.appexec([], """(): + def f(): + pass + return f + """) + ll_code = cast_instance_to_base_ptr(w_f.code) + logger = Logger(MockSD()) + + oplist = parse(""" + [i1, i2] + i3 = int_add(i1, i2) + guard_true(i3) [] + """).operations + + def interp_on_compile(): + pypyjitdriver.on_compile(logger, LoopToken(), oplist, 'loop', + 0, False, ll_code) + + def interp_on_compile_bridge(): + pypyjitdriver.on_compile_bridge(logger, LoopToken(), oplist, 0) + + cls.w_on_compile = space.wrap(interp2app(interp_on_compile)) + cls.w_on_compile_bridge = space.wrap(interp2app(interp_on_compile_bridge)) + + def test_on_compile(self): + import pypyjit + all = [] + + def hook(*args): + assert args[0] == 'main' + assert args[1] in ['loop', 'bridge'] + all.append(args[2:]) + + self.on_compile() + pypyjit.set_compile_hook(hook) + assert not all + self.on_compile() + assert len(all) == 1 + assert all[0][0][0].co_name == 'f' + assert all[0][0][1] == 0 + assert all[0][0][2] == False + assert len(all[0][1]) == 2 + assert 'int_add' in all[0][1][0] + self.on_compile_bridge() + assert len(all) == 2 + pypyjit.set_compile_hook(None) + self.on_compile() + assert len(all) == 2 + + def test_on_compile_exception(self): + import pypyjit, sys, cStringIO + + def hook(*args): + 1/0 + + pypyjit.set_compile_hook(hook) + s = cStringIO.StringIO() + prev = sys.stderr + sys.stderr = s + try: + self.on_compile() + finally: + sys.stderr = prev + assert 'jit hook' in s.getvalue() + assert 'ZeroDivisionError' in s.getvalue() diff --git a/pypy/objspace/std/test/test_floatobject.py b/pypy/objspace/std/test/test_floatobject.py --- a/pypy/objspace/std/test/test_floatobject.py +++ b/pypy/objspace/std/test/test_floatobject.py @@ -63,6 +63,19 @@ def setup_class(cls): cls.w_py26 = cls.space.wrap(sys.version_info >= (2, 6)) + def test_conjugate(self): + assert (1.).conjugate() == 1. + assert (-1.).conjugate() == -1. + + class F(float): + pass + assert F(1.).conjugate() == 1. + + class F(float): + def __pos__(self): + return 42. + assert F(1.).conjugate() == 1. + def test_negatives(self): assert -1.1 < 0 assert -0.1 < 0 diff --git a/pypy/objspace/std/test/test_intobject.py b/pypy/objspace/std/test/test_intobject.py --- a/pypy/objspace/std/test/test_intobject.py +++ b/pypy/objspace/std/test/test_intobject.py @@ -285,6 +285,19 @@ class AppTestInt: + def test_conjugate(self): + assert (1).conjugate() == 1 + assert (-1).conjugate() == -1 + + class I(int): + pass + assert I(1).conjugate() == 1 + + class I(int): + def __pos__(self): + return 42 + assert I(1).conjugate() == 1 + def test_trunc(self): import math assert math.trunc(1) == 1 diff --git a/pypy/objspace/std/test/test_longobject.py b/pypy/objspace/std/test/test_longobject.py --- a/pypy/objspace/std/test/test_longobject.py +++ b/pypy/objspace/std/test/test_longobject.py @@ -300,6 +300,11 @@ assert type(L(7).conjugate()) is long + class L(long): + def __pos__(self): + return 43 + assert L(7).conjugate() == 7L + def test_bit_length(self): assert 8L.bit_length() == 4 assert (-1<<40).bit_length() == 41 diff --git a/pypy/rlib/jit.py b/pypy/rlib/jit.py --- a/pypy/rlib/jit.py +++ b/pypy/rlib/jit.py @@ -370,6 +370,24 @@ raise set_user_param._annspecialcase_ = 'specialize:arg(0)' + + def on_compile(self, logger, looptoken, operations, type, *greenargs): + """ A hook called when loop is compiled. Overwrite + for your own jitdriver if you want to do something special, like + call applevel code + """ + + def on_compile_bridge(self, logger, orig_looptoken, operations, n): + """ A hook called when a bridge is compiled. Overwrite + for your own jitdriver if you want to do something special + """ + + # note: if you overwrite this functions with the above signature it'll + # work, but the *greenargs is different for each jitdriver, so we + # can't share the same methods + del on_compile + del on_compile_bridge + def _make_extregistryentries(self): # workaround: we cannot declare ExtRegistryEntries for functions # used as methods of a frozen object, but we can attach the diff --git a/pypy/rlib/rsre/rsre_core.py b/pypy/rlib/rsre/rsre_core.py --- a/pypy/rlib/rsre/rsre_core.py +++ b/pypy/rlib/rsre/rsre_core.py @@ -759,17 +759,27 @@ @specializectx def find_repetition_end(ctx, ppos, ptr, maxcount): end = ctx.end - if maxcount <= 1: - if maxcount == 1 and ptr < end: - # Relatively common case: maxcount == 1. If we are not at the - # end of the string, it's done by a single direct check. - op = ctx.pat(ppos) - for op1, checkerfn in unroll_char_checker: - if op1 == op: - if checkerfn(ctx, ptr, ppos): - return ptr + 1 + ptrp1 = ptr + 1 + # First get rid of the cases where we don't have room for any match. + if maxcount <= 0 or ptrp1 > end: return ptr - elif maxcount != 65535: + # Check the first character directly. If it doesn't match, we are done. + # The idea is to be fast for cases like re.search("b+"), where we expect + # the common case to be a non-match. It's much faster with the JIT to + # have the non-match inlined here rather than detect it in the fre() call. + op = ctx.pat(ppos) + for op1, checkerfn in unroll_char_checker: + if op1 == op: + if checkerfn(ctx, ptr, ppos): + break + else: + return ptr + # It matches at least once. If maxcount == 1 (relatively common), + # then we are done. + if maxcount == 1: + return ptrp1 + # Else we really need to count how many times it matches. + if maxcount != 65535: # adjust end end1 = ptr + maxcount if end1 <= end: @@ -777,7 +787,7 @@ op = ctx.pat(ppos) for op1, fre in unroll_fre_checker: if op1 == op: - return fre(ctx, ptr, end, ppos) + return fre(ctx, ptrp1, end, ppos) raise Error("rsre.find_repetition_end[%d]" % op) @specializectx diff --git a/pypy/rlib/rsre/test/test_zjit.py b/pypy/rlib/rsre/test/test_zjit.py --- a/pypy/rlib/rsre/test/test_zjit.py +++ b/pypy/rlib/rsre/test/test_zjit.py @@ -160,3 +160,9 @@ res = self.meta_interp_match(r"<[\S ]+>", "<..a .. aa>") assert res == 13 self.check_enter_count(1) + + + def test_find_repetition_end_fastpath(self): + res = self.meta_interp_search(r"b+", "a"*30 + "b") + assert res == 30 + self.check_loops(call=0) diff --git a/pypy/rlib/test/test_jit.py b/pypy/rlib/test/test_jit.py --- a/pypy/rlib/test/test_jit.py +++ b/pypy/rlib/test/test_jit.py @@ -52,9 +52,12 @@ import sys s = StringIO() + prev = sys.stdout sys.stdout = s - dis.dis(g) - sys.stdout = sys.__stdout__ + try: + dis.dis(g) + finally: + sys.stdout = prev x = s.getvalue().find('CALL_FUNCTION') assert x != -1 x = s.getvalue().find('CALL_FUNCTION', x) _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit