Author: Armin Rigo <ar...@tunes.org> Branch: Changeset: r72243:37c876808e80 Date: 2014-06-26 12:36 +0200 http://bitbucket.org/pypy/pypy/changeset/37c876808e80/
Log: hg merge jit-get-errno Optimize the errno handling in the JIT, notably around external function calls. Linux-only. diff --git a/rpython/jit/backend/llsupport/test/ztranslation_test.py b/rpython/jit/backend/llsupport/test/ztranslation_test.py --- a/rpython/jit/backend/llsupport/test/ztranslation_test.py +++ b/rpython/jit/backend/llsupport/test/ztranslation_test.py @@ -3,7 +3,7 @@ from rpython.rlib.jit import JitDriver, unroll_parameters, set_param from rpython.rlib.jit import PARAMETERS, dont_look_inside from rpython.rlib.jit import promote -from rpython.rlib import jit_hooks +from rpython.rlib import jit_hooks, rposix from rpython.rlib.objectmodel import keepalive_until_here from rpython.rlib.rthread import ThreadLocalReference from rpython.jit.backend.detect_cpu import getcpuclass @@ -24,6 +24,7 @@ # - full optimizer # - floats neg and abs # - threadlocalref_get + # - get_errno, set_errno class Frame(object): _virtualizable_ = ['i'] @@ -64,6 +65,8 @@ if k - abs(j): raise ValueError if k - abs(-j): raise ValueError if t.get().nine != 9: raise ValueError + rposix.set_errno(total) + if rposix.get_errno() != total: raise ValueError return chr(total % 253) # from rpython.rtyper.lltypesystem import lltype, rffi diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py --- a/rpython/jit/backend/x86/assembler.py +++ b/rpython/jit/backend/x86/assembler.py @@ -2325,12 +2325,38 @@ ed = effectinfo.extradescrs[0] assert isinstance(ed, ThreadLocalRefDescr) addr1 = rffi.cast(lltype.Signed, ed.get_tlref_addr()) + # 'addr1' is the address is the current thread, but we assume that + # it is a thread-local at a constant offset from %fs/%gs. addr0 = stmtlocal.threadlocal_base() addr = addr1 - addr0 assert rx86.fits_in_32bits(addr) mc = self.mc - mc.writechar(stmtlocal.SEGMENT_TL) # prefix - mc.MOV_rj(resloc.value, addr) + mc.writechar(stmtlocal.SEGMENT_TL) # prefix: %fs or %gs + mc.MOV_rj(resloc.value, addr) # memory read + + def get_set_errno(self, op, loc, issue_a_write): + # this function is only called on Linux + from rpython.jit.backend.x86 import stmtlocal + addr = stmtlocal.get_errno_tl() + assert rx86.fits_in_32bits(addr) + mc = self.mc + mc.writechar(stmtlocal.SEGMENT_TL) # prefix: %fs or %gs + # !!important: the *next* instruction must be the one using 'addr'!! + if issue_a_write: + if isinstance(loc, RegLoc): + mc.MOV32_jr(addr, loc.value) # memory write from reg + else: + assert isinstance(loc, ImmedLoc) + newvalue = loc.value + newvalue = rffi.cast(rffi.INT, newvalue) + newvalue = rffi.cast(lltype.Signed, newvalue) + mc.MOV32_ji(addr, newvalue) # memory write immediate + else: + assert isinstance(loc, RegLoc) + if IS_X86_32: + mc.MOV_rj(loc.value, addr) # memory read + elif IS_X86_64: + mc.MOVSX32_rj(loc.value, addr) # memory read, sign-extend genop_discard_list = [Assembler386.not_implemented_op_discard] * rop._LAST diff --git a/rpython/jit/backend/x86/regalloc.py b/rpython/jit/backend/x86/regalloc.py --- a/rpython/jit/backend/x86/regalloc.py +++ b/rpython/jit/backend/x86/regalloc.py @@ -693,6 +693,7 @@ self.perform_math(op, [loc0], loc0) TLREF_SUPPORT = sys.platform.startswith('linux') + ERRNO_SUPPORT = sys.platform.startswith('linux') def _consider_threadlocalref_get(self, op): if self.TLREF_SUPPORT: @@ -701,6 +702,22 @@ else: self._consider_call(op) + def _consider_get_errno(self, op): + if self.ERRNO_SUPPORT: + resloc = self.force_allocate_reg(op.result) + self.assembler.get_set_errno(op, resloc, issue_a_write=False) + else: + self._consider_call(op) + + def _consider_set_errno(self, op): + if self.ERRNO_SUPPORT: + # op.getarg(0) is the function set_errno; op.getarg(1) is + # the new errno value + loc0 = self.rm.make_sure_var_in_reg(op.getarg(1)) + self.assembler.get_set_errno(op, loc0, issue_a_write=True) + else: + self._consider_call(op) + def _call(self, op, arglocs, force_store=[], guard_not_forced_op=None): # we need to save registers on the stack: # @@ -780,6 +797,10 @@ return self._consider_math_sqrt(op) if oopspecindex == EffectInfo.OS_THREADLOCALREF_GET: return self._consider_threadlocalref_get(op) + if oopspecindex == EffectInfo.OS_GET_ERRNO: + return self._consider_get_errno(op) + if oopspecindex == EffectInfo.OS_SET_ERRNO: + return self._consider_set_errno(op) self._consider_call(op) def consider_call_may_force(self, op, guard_op): diff --git a/rpython/jit/backend/x86/stmtlocal.py b/rpython/jit/backend/x86/stmtlocal.py --- a/rpython/jit/backend/x86/stmtlocal.py +++ b/rpython/jit/backend/x86/stmtlocal.py @@ -21,6 +21,10 @@ asm("%s" : "=r"(result)); return result; } +static long pypy__get_errno_tl(void) +{ + return ((long)&errno) - pypy__threadlocal_base(); +} ''' % _instruction]) @@ -30,3 +34,10 @@ compilation_info=eci, _nowrapper=True, ) #transactionsafe=True) + +get_errno_tl = rffi.llexternal( + 'pypy__get_errno_tl', + [], lltype.Signed, + compilation_info=eci, + _nowrapper=True, + ) #transactionsafe=True) diff --git a/rpython/jit/codewriter/effectinfo.py b/rpython/jit/codewriter/effectinfo.py --- a/rpython/jit/codewriter/effectinfo.py +++ b/rpython/jit/codewriter/effectinfo.py @@ -23,6 +23,8 @@ OS_SHRINK_ARRAY = 3 # rgc.ll_shrink_array OS_DICT_LOOKUP = 4 # ll_dict_lookup OS_THREADLOCALREF_GET = 5 # llop.threadlocalref_get + OS_GET_ERRNO = 6 # rposix.get_errno + OS_SET_ERRNO = 7 # rposix.set_errno # OS_STR_CONCAT = 22 # "stroruni.concat" OS_STR_SLICE = 23 # "stroruni.slice" diff --git a/rpython/jit/codewriter/jtransform.py b/rpython/jit/codewriter/jtransform.py --- a/rpython/jit/codewriter/jtransform.py +++ b/rpython/jit/codewriter/jtransform.py @@ -438,6 +438,8 @@ elif oopspec_name.endswith('dict.lookup'): # also ordereddict.lookup prepare = self._handle_dict_lookup_call + elif oopspec_name.startswith('rposix.'): + prepare = self._handle_rposix_call else: prepare = self.prepare_builtin_call try: @@ -1898,6 +1900,16 @@ else: raise NotImplementedError(oopspec_name) + def _handle_rposix_call(self, op, oopspec_name, args): + if oopspec_name == 'rposix.get_errno': + return self._handle_oopspec_call(op, args, EffectInfo.OS_GET_ERRNO, + EffectInfo.EF_CANNOT_RAISE) + elif oopspec_name == 'rposix.set_errno': + return self._handle_oopspec_call(op, args, EffectInfo.OS_SET_ERRNO, + EffectInfo.EF_CANNOT_RAISE) + else: + raise NotImplementedError(oopspec_name) + def rewrite_op_jit_force_quasi_immutable(self, op): v_inst, c_fieldname = op.args descr1 = self.cpu.fielddescrof(v_inst.concretetype.TO, diff --git a/rpython/jit/codewriter/test/test_jtransform.py b/rpython/jit/codewriter/test/test_jtransform.py --- a/rpython/jit/codewriter/test/test_jtransform.py +++ b/rpython/jit/codewriter/test/test_jtransform.py @@ -148,6 +148,8 @@ EI.OS_RAW_MALLOC_VARSIZE_CHAR: ([INT], ARRAYPTR), EI.OS_RAW_FREE: ([ARRAYPTR], lltype.Void), EI.OS_THREADLOCALREF_GET: ([], rclass.OBJECTPTR), + EI.OS_GET_ERRNO: ([], INT), + EI.OS_SET_ERRNO: ([INT], lltype.Void), } argtypes = argtypes[oopspecindex] assert argtypes[0] == [v.concretetype for v in op.args[1:]] @@ -156,7 +158,9 @@ assert extraeffect == EI.EF_ELIDABLE_CAN_RAISE elif oopspecindex == EI.OS_RAW_MALLOC_VARSIZE_CHAR: assert extraeffect == EI.EF_CAN_RAISE - elif oopspecindex == EI.OS_RAW_FREE: + elif oopspecindex in (EI.OS_RAW_FREE, + EI.OS_GET_ERRNO, + EI.OS_SET_ERRNO): assert extraeffect == EI.EF_CANNOT_RAISE elif oopspecindex == EI.OS_THREADLOCALREF_GET: assert extraeffect == EI.EF_LOOPINVARIANT @@ -1320,6 +1324,38 @@ assert op0.args[2] == 'calldescr-%d' % OS_THREADLOCALREF_GET assert op0.result == v2 +def test_get_errno(): + # test that the oopspec is present and correctly transformed + from rpython.rlib import rposix + FUNC = lltype.FuncType([], lltype.Signed) + func = lltype.functionptr(FUNC, 'get_errno', _callable=rposix.get_errno) + v3 = varoftype(lltype.Signed) + op = SpaceOperation('direct_call', [const(func)], v3) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op1 = tr.rewrite_operation(op) + assert op1.opname == 'residual_call_r_i' + assert op1.args[0].value == func + assert op1.args[1] == ListOfKind('ref', []) + assert op1.args[2] == 'calldescr-%d' % effectinfo.EffectInfo.OS_GET_ERRNO + assert op1.result == v3 + +def test_set_errno(): + # test that the oopspec is present and correctly transformed + from rpython.rlib import rposix + FUNC = lltype.FuncType([lltype.Signed], lltype.Void) + func = lltype.functionptr(FUNC, 'set_errno', _callable=rposix.set_errno) + v1 = varoftype(lltype.Signed) + v3 = varoftype(lltype.Void) + op = SpaceOperation('direct_call', [const(func), v1], v3) + tr = Transformer(FakeCPU(), FakeBuiltinCallControl()) + op1 = tr.rewrite_operation(op) + assert op1.opname == 'residual_call_ir_v' + assert op1.args[0].value == func + assert op1.args[1] == ListOfKind('int', [v1]) + assert op1.args[2] == ListOfKind('ref', []) + assert op1.args[3] == 'calldescr-%d' % effectinfo.EffectInfo.OS_SET_ERRNO + assert op1.result == v3 + def test_unknown_operation(): op = SpaceOperation('foobar', [], varoftype(lltype.Void)) tr = Transformer() diff --git a/rpython/rlib/rposix.py b/rpython/rlib/rposix.py --- a/rpython/rlib/rposix.py +++ b/rpython/rlib/rposix.py @@ -98,9 +98,11 @@ # the default wrapper for set_errno is not suitable for use in critical places # like around GIL handling logic, so we provide our own wrappers. +@jit.oopspec("rposix.get_errno()") def get_errno(): return intmask(_get_errno()) +@jit.oopspec("rposix.set_errno(errno)") def set_errno(errno): _set_errno(rffi.cast(INT, errno)) _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit