Commits

Armin Rigo committed dbeb9dd Merge

merge heads

Comments (0)

Files changed (26)

lib_pypy/_sqlite3.py

 
         self.statement = c_void_p()
         next_char = c_char_p()
-        ret = sqlite.sqlite3_prepare_v2(self.con.db, sql, -1, byref(self.statement), byref(next_char))
+        sql_char = c_char_p(sql)
+        ret = sqlite.sqlite3_prepare_v2(self.con.db, sql_char, -1, byref(self.statement), byref(next_char))
         if ret == SQLITE_OK and self.statement.value is None:
             # an empty statement, we work around that, as it's the least trouble
             ret = sqlite.sqlite3_prepare_v2(self.con.db, "select 42", -1, byref(self.statement), byref(next_char))
         self.con._remember_statement(self)
         if _check_remaining_sql(next_char.value):
             raise Warning, "One and only one statement required"
+        # sql_char should remain alive until here
 
         self._build_row_cast_map()
 

pypy/interpreter/pyframe.py

         self.escaped = True
 
     def append_block(self, block):
-        block.previous = self.lastblock
+        assert block.previous is self.lastblock
         self.lastblock = block
 
     def pop_block(self):
         while i >= 0:
             block = lst[i]
             i -= 1
-            self.append_block(block)
+            block.previous = self.lastblock
+            self.lastblock = block
 
     def get_builtin(self):
         if self.space.config.objspace.honor__builtins__:

pypy/interpreter/pyopcode.py

         raise BytecodeCorruption, "old opcode, no longer in use"
 
     def SETUP_LOOP(self, offsettoend, next_instr):
-        block = LoopBlock(self, next_instr + offsettoend)
-        self.append_block(block)
+        block = LoopBlock(self, next_instr + offsettoend, self.lastblock)
+        self.lastblock = block
 
     def SETUP_EXCEPT(self, offsettoend, next_instr):
-        block = ExceptBlock(self, next_instr + offsettoend)
-        self.append_block(block)
+        block = ExceptBlock(self, next_instr + offsettoend, self.lastblock)
+        self.lastblock = block
 
     def SETUP_FINALLY(self, offsettoend, next_instr):
-        block = FinallyBlock(self, next_instr + offsettoend)
-        self.append_block(block)
+        block = FinallyBlock(self, next_instr + offsettoend, self.lastblock)
+        self.lastblock = block
 
     def SETUP_WITH(self, offsettoend, next_instr):
         w_manager = self.peekvalue()
         w_exit = self.space.get(w_descr, w_manager)
         self.settopvalue(w_exit)
         w_result = self.space.get_and_call_function(w_enter, w_manager)
-        block = WithBlock(self, next_instr + offsettoend)
-        self.append_block(block)
+        block = WithBlock(self, next_instr + offsettoend, self.lastblock)
+        self.lastblock = block
         self.pushvalue(w_result)
 
     def WITH_CLEANUP(self, oparg, next_instr):
 
     _immutable_ = True
 
-    def __init__(self, frame, handlerposition):
+    def __init__(self, frame, handlerposition, previous):
         self.handlerposition = handlerposition
         self.valuestackdepth = frame.valuestackdepth
-        self.previous = None # this makes a linked list of blocks
+        self.previous = previous   # this makes a linked list of blocks
 
     def __eq__(self, other):
         return (self.__class__ is other.__class__ and

pypy/jit/backend/x86/assembler.py

         self.datablockwrapper = None
         self.stack_check_slowpath = 0
         self.propagate_exception_path = 0
+        self.gcrootmap_retaddr_forced = 0
         self.teardown()
 
     def leave_jitted_hook(self):
         self.stack_check_slowpath = rawstart
 
     @staticmethod
+    @rgc.no_collect
     def _release_gil_asmgcc(css):
         # similar to trackgcroot.py:pypy_asm_stackwalk, first part
         from pypy.rpython.memory.gctransform import asmgcroot
             before()
 
     @staticmethod
+    @rgc.no_collect
     def _reacquire_gil_asmgcc(css):
         # first reacquire the GIL
         after = rffi.aroundstate.after
         next.prev = prev
 
     @staticmethod
+    @rgc.no_collect
     def _release_gil_shadowstack():
         before = rffi.aroundstate.before
         if before:
             before()
 
     @staticmethod
+    @rgc.no_collect
     def _reacquire_gil_shadowstack():
         after = rffi.aroundstate.after
         if after:
                 css = get_ebp_ofs(pos + use_words - 1)
                 self._regalloc.close_stack_struct = css
             # The location where the future CALL will put its return address
-            # will be [ESP-WORD], so save that as the next frame's top address
-            self.mc.LEA_rs(eax.value, -WORD)        # LEA EAX, [ESP-4]
+            # will be [ESP-WORD].  But we can't use that as the next frame's
+            # top address!  As the code after releasegil() runs without the
+            # GIL, it might not be set yet by the time we need it (very
+            # unlikely), or it might be overwritten by the following call
+            # to reaquiregil() (much more likely).  So we hack even more
+            # and use a dummy location containing a dummy value (a pointer
+            # to itself) which we pretend is the return address :-/ :-/ :-/
+            # It prevents us to store any %esp-based stack locations but we
+            # don't so far.
+            adr = self.datablockwrapper.malloc_aligned(WORD, WORD)
+            rffi.cast(rffi.CArrayPtr(lltype.Signed), adr)[0] = adr
+            self.gcrootmap_retaddr_forced = adr
             frame_ptr = css + WORD * (2+asmgcroot.FRAME_PTR)
-            self.mc.MOV_br(frame_ptr, eax.value)    # MOV [css.frame], EAX
+            if rx86.fits_in_32bits(adr):
+                self.mc.MOV_bi(frame_ptr, adr)          # MOV [css.frame], adr
+            else:
+                self.mc.MOV_ri(eax.value, adr)          # MOV EAX, adr
+                self.mc.MOV_br(frame_ptr, eax.value)    # MOV [css.frame], EAX
             # Save ebp
             index_of_ebp = css + WORD * (2+asmgcroot.INDEX_OF_EBP)
-            self.mc.MOV_br(index_of_ebp, ebp.value) # MOV [css.ebp], EBP
+            self.mc.MOV_br(index_of_ebp, ebp.value)     # MOV [css.ebp], EBP
             # Call the closestack() function (also releasing the GIL)
             if IS_X86_32:
                 reg = eax
         if gcrootmap.is_shadow_stack:
             args = []
         else:
+            assert self.gcrootmap_retaddr_forced == -1, (
+                      "missing mark_gc_roots() in CALL_RELEASE_GIL")
+            self.gcrootmap_retaddr_forced = 0
             css = self._regalloc.close_stack_struct
             assert css != 0
             if IS_X86_32:
             if gcrootmap.is_shadow_stack:
                 gcrootmap.write_callshape(mark, force_index)
             else:
-                self.mc.insert_gcroot_marker(mark)
+                if self.gcrootmap_retaddr_forced == 0:
+                    self.mc.insert_gcroot_marker(mark)   # common case
+                else:
+                    assert self.gcrootmap_retaddr_forced != -1, (
+                              "two mark_gc_roots() in a CALL_RELEASE_GIL")
+                    gcrootmap.put(self.gcrootmap_retaddr_forced, mark)
+                    self.gcrootmap_retaddr_forced = -1
 
     def target_arglocs(self, loop_token):
         return loop_token._x86_arglocs

pypy/jit/codewriter/jtransform.py

             self.vable_array_vars[op.result]= self.vable_array_vars[op.args[0]]
 
     rewrite_op_cast_pointer = rewrite_op_same_as
-    rewrite_op_cast_opaque_ptr = rewrite_op_same_as   # rlib.rerased
     def rewrite_op_cast_bool_to_int(self, op): pass
     def rewrite_op_cast_bool_to_uint(self, op): pass
     def rewrite_op_cast_char_to_int(self, op): pass

pypy/jit/metainterp/blackhole.py

     @arguments("r", returns="i")
     def bhimpl_ptr_nonzero(a):
         return bool(a)
+    @arguments("r", returns="r")
+    def bhimpl_cast_opaque_ptr(a):
+        return a
 
     @arguments("i", returns="i")
     def bhimpl_int_copy(a):

pypy/jit/metainterp/optimizeopt/optimizer.py

         self.posponedop = None
         self.exception_might_have_happened = False
         self.quasi_immutable_deps = None
+        self.opaque_pointers = {}
         self.newoperations = []
         if loop is not None:
             self.call_pure_results = loop.call_pure_results
     def optimize_DEBUG_MERGE_POINT(self, op):
         self.emit_operation(op)
 
+    def optimize_CAST_OPAQUE_PTR(self, op):
+        value = self.getvalue(op.getarg(0))
+        self.opaque_pointers[value] = True
+        self.make_equal_to(op.result, value)
+
 dispatch_opt = make_dispatcher_method(Optimizer, 'optimize_',
         default=Optimizer.optimize_default)
 

pypy/jit/metainterp/optimizeopt/simplify.py

         #     but it's a bit hard to implement robustly if heap.py is also run
         pass
 
+    optimize_CAST_OPAQUE_PTR = optimize_VIRTUAL_REF
+
 
 dispatch_opt = make_dispatcher_method(OptSimplify, 'optimize_',
         default=OptSimplify.emit_operation)

pypy/jit/metainterp/optimizeopt/unroll.py

     def safe_to_move(self, op):
         opnum = op.getopnum()
         descr = op.getdescr()
+        for box in op.getarglist():
+            if self.optimizer.getvalue(box) in self.optimizer.opaque_pointers:
+                return False
         if op.is_always_pure() or op.is_foldable_guard():
             return True
         elif opnum == rop.JUMP:

pypy/jit/metainterp/pyjitpl.py

         return self.execute(rop.PTR_EQ, box, history.CONST_NULL)
 
     @arguments("box")
+    def opimpl_cast_opaque_ptr(self, box):
+        return self.execute(rop.CAST_OPAQUE_PTR, box)
+
+    @arguments("box")
     def _opimpl_any_return(self, box):
         self.metainterp.finishframe(box)
 

pypy/jit/metainterp/resoperation.py

     #
     'PTR_EQ/2b',
     'PTR_NE/2b',
+    'CAST_OPAQUE_PTR/1b',
     #
     'ARRAYLEN_GC/1d',
     'STRLEN/1',

pypy/module/micronumpy/interp_numarray.py

     """
     Intermediate class representing a float literal.
     """
-    _immutable_fields_ = ["float_value"]
     signature = Signature()
 
     def __init__(self, float_value):
 
 
 class Call1(VirtualArray):
-    _immutable_fields_ = ["function", "values"]
-
     def __init__(self, function, values, signature):
         VirtualArray.__init__(self, signature)
         self.function = function
     """
     Intermediate class for performing binary operations.
     """
-    _immutable_fields_ = ["function", "left", "right"]
-
     def __init__(self, function, left, right, signature):
         VirtualArray.__init__(self, signature)
         self.function = function
     Class for representing views of arrays, they will reflect changes of parent
     arrays. Example: slices
     """
-    _immutable_fields_ = ["parent"]
-
     def __init__(self, parent, signature):
         BaseArray.__init__(self)
         self.signature = signature
         raise NotImplementedError
 
 class SingleDimSlice(ViewArray):
-    _immutable_fields_ = ["start", "stop", "step", "size"]
     static_signature = Signature()
 
     def __init__(self, start, stop, step, slice_length, parent, signature):

pypy/module/posix/app_posix.py

             self._proc = proc
         def close(self):
             self._stream.close()
-            return self._proc.wait()
+            return self._proc.wait() or None    # 0 => None
         __del__ = close
         def __getattr__(self, name):
             return getattr(self._stream, name)

pypy/module/posix/interp_posix.py

         FIELDS = PORTABLE_STAT_FIELDS
     else:
         FIELDS = STAT_FIELDS    # also when not translating at all
-    return space.newlist([space.wrap(name) for name, _ in FIELDS])
+    return space.newlist([space.wrap(name) for _, (name, _) in FIELDS])
 
 
 class State:

pypy/objspace/flow/flowcontext.py

         w_exit = self.space.getattr(w_manager, self.space.wrap("__exit__"))
         self.settopvalue(w_exit)
         w_result = self.space.call_method(w_manager, "__enter__")
-        block = WithBlock(self, next_instr + offsettoend)
-        self.append_block(block)
+        block = WithBlock(self, next_instr + offsettoend, self.lastblock)
+        self.lastblock = block
         self.pushvalue(w_result)
 
     # XXX Unimplemented 2.7 opcodes ----------------

pypy/rlib/runicode.py

             rffi.free_nonmovingbuffer(s, dataptr)
 
     def unicode_encode_mbcs(p, size, errors, errorhandler=None):
+        if size == 0:
+            return ''
         dataptr = rffi.get_nonmoving_unicodebuffer(p)
         try:
             # first get the size of the result
-            if size > 0:
-                mbcssize = WideCharToMultiByte(CP_ACP, 0,
-                                               dataptr, size, None, 0,
-                                               None, None)
-                if mbcssize == 0:
-                    raise rwin32.lastWindowsError()
-            else:
-                mbcssize = 0
+            mbcssize = WideCharToMultiByte(CP_ACP, 0,
+                                           dataptr, size, None, 0,
+                                           None, None)
+            if mbcssize == 0:
+                raise rwin32.lastWindowsError()
 
             raw_buf, gc_buf = rffi.alloc_buffer(mbcssize)
             try:

pypy/rpython/lltypesystem/rffi.py

             return hlstrtype(gc_buf)
 
         new_buf = lltype.malloc(STRTYPE, needed_size)
-        try:
-            str_chars_offset = (offsetof(STRTYPE, 'chars') + \
-                                itemoffsetof(STRTYPE.chars, 0))
-            if gc_buf:
-                src = cast_ptr_to_adr(gc_buf) + str_chars_offset
-            else:
-                src = cast_ptr_to_adr(raw_buf) + itemoffsetof(TYPEP.TO, 0)
-            dest = cast_ptr_to_adr(new_buf) + str_chars_offset
-            ## FIXME: This is bad, because dest could potentially move
-            ## if there are threads involved.
-            raw_memcopy(src, dest,
-                        llmemory.sizeof(ll_char_type) * needed_size)
-            return hlstrtype(new_buf)
-        finally:
-            keepalive_until_here(new_buf)
+        str_chars_offset = (offsetof(STRTYPE, 'chars') + \
+                            itemoffsetof(STRTYPE.chars, 0))
+        if gc_buf:
+            src = cast_ptr_to_adr(gc_buf) + str_chars_offset
+        else:
+            src = cast_ptr_to_adr(raw_buf) + itemoffsetof(TYPEP.TO, 0)
+        dest = cast_ptr_to_adr(new_buf) + str_chars_offset
+        raw_memcopy(src, dest,
+                    llmemory.sizeof(ll_char_type) * needed_size)
+        keepalive_until_here(gc_buf)
+        keepalive_until_here(new_buf)
+        return hlstrtype(new_buf)
 
     # (char*, str) -> None
     def keep_buffer_alive_until_here(raw_buf, gc_buf):
     [VOIDP, VOIDP, SIZE_T],
     lltype.Void,
     threadsafe=False
-)
+)

pypy/rpython/lltypesystem/rstr.py

 from pypy.rpython.error import TyperError
 from pypy.rlib.objectmodel import malloc_zero_filled, we_are_translated
 from pypy.rlib.objectmodel import _hash_string, enforceargs
+from pypy.rlib.objectmodel import keepalive_until_here
 from pypy.rlib.debug import ll_assert
 from pypy.rlib.jit import elidable, we_are_jitted, dont_look_inside
 from pypy.rlib.rarithmetic import ovfcheck
         src = llmemory.cast_ptr_to_adr(src) + _str_ofs(srcstart)
         dst = llmemory.cast_ptr_to_adr(dst) + _str_ofs(dststart)
         llmemory.raw_memcopy(src, dst, llmemory.sizeof(CHAR_TP) * length)
+        keepalive_until_here(src)
+        keepalive_until_here(dst)
     copy_string_contents._always_inline_ = True
     #copy_string_contents.oopspec = (
     #    '%s.copy_contents(src, dst, srcstart, dststart, length)' % name)

pypy/tool/release/package.py

             pypy_c_dir = py.path.local(override_pypy_c).dirname
         else:
             pypy_c_dir = basedir.join('pypy', 'translator', 'goal')
-        pypy_c = pypy_c_dir.join(rename_pypy_c + '.exe')
-        libpypy_c = pypy_c_dir.join('lib' + rename_pypy_c + '.dll')
+        pypy_c = pypy_c_dir.join('pypy-c.exe')
+        libpypy_c = pypy_c_dir.join('libpypy-c.dll')
         binaries = [(pypy_c, pypy_c.basename),
                     (libpypy_c, libpypy_c.basename),
                     (pypy_c_dir.join('libexpat.dll'), 'libexpat.dll')]

pypy/translator/c/gcc/instruction.py

 class Insn(object):
     _args_ = []
     _locals_ = []
+    hack = None
 
     def __repr__(self):
-        return '%s(%s)' % (self.__class__.__name__,
+        return '%s(%s) --- %r' % (self.__class__.__name__,
                            ', '.join([str(getattr(self, name))
-                                      for name in self._args_]))
+                                      for name in self._args_]),
+                                  self.hack)
     def requestgcroots(self, tracker):
         return {}
 
     def source_of(self, localvar, tag):
+        if tag is None:
+            if self.hack is None:
+                self.hack = set()
+            self.hack.add(localvar)
         return localvar
 
     def all_sources_of(self, localvar):
     def source_of(self, localvar, tag):
         if localvar == self.target:
             return somenewvalue
-        return localvar
+        return Insn.source_of(self, localvar, tag)
 
     def all_sources_of(self, localvar):
         if localvar == self.target:
     def source_of(self, localvar, tag):
         if localvar == self.target:
             return self.source
-        return localvar
+        return Insn.source_of(self, localvar, tag)
 
     def all_sources_of(self, localvar):
         if localvar == self.target:

pypy/translator/c/gcc/test/elf64/track_zero.s

+	.type	pypy_g_do_call_1, @function
+pypy_g_do_call_1:
+	pushq	%rbx
+	pushq	%r12
+	movq	%rdi, %rbx
+	movq	%rsi, %r12
+	call	number1
+	;; expected {16(%rsp) | 8(%rsp), (%rsp), %r13, %r14, %r15, %rbp | %r12}
+	testq	%rbx, %rbx	; here rbx is an integer, not a gc ref
+	je .L1			; if rbx==0, jump to L1, where rbx==NULLGCREF
+	movq	(%rax), %rbx	; else load a gc ref
+.L1:
+	/* GCROOT %rbx */
+	/* GCROOT %r12 */
+	popq	%r12
+	popq	%rbx
+	ret
+	.size	pypy_g_do_call_1, .-pypy_g_do_call_1

pypy/translator/c/gcc/test/elf64/track_zero_2.s

+	.type	pypy_g_do_call_1, @function
+pypy_g_do_call_1:
+	pushq	%rbx
+	pushq	%r12
+	movq	%rdi, %rbx
+	movq	%rsi, %r12
+	call	number1
+	;; expected {16(%rsp) | 8(%rsp), (%rsp), %r13, %r14, %r15, %rbp | %rbx, %r12}
+	testq	%rbx, %rbx
+	je .L1
+	movq	(%rax), %r12
+.L1:
+	/* GCROOT %rbx */
+	/* GCROOT %r12 */
+	popq	%r12
+	popq	%rbx
+	ret
+	.size	pypy_g_do_call_1, .-pypy_g_do_call_1

pypy/translator/c/gcc/test/elf64/track_zero_3.s

+	.type	pypy_g_do_call_1, @function
+pypy_g_do_call_1:
+	pushq	%rbx
+	movq	%rdi, %rbx
+	call	number1
+	;; expected {8(%rsp) | (%rsp), %r12, %r13, %r14, %r15, %rbp | %rbx}
+	testq	%rax, %rax
+	je .L1
+	call	RPyAssertFailed
+.L1:
+	/* GCROOT %rbx */
+	popq	%rbx
+	ret
+	.size	pypy_g_do_call_1, .-pypy_g_do_call_1

pypy/translator/c/gcc/test/elf64/track_zero_4.s

+	.type	pypy_g_do_call_1, @function
+pypy_g_do_call_1:
+	pushq	%rbx
+	pushq	%r12
+	movq	%rdi, %rbx
+	movq	%rsi, %r12
+	call	number1
+	;; expected {16(%rsp) | 8(%rsp), (%rsp), %r13, %r14, %r15, %rbp | %r12}
+	testq	%rbx, %rbx
+	movq	%r12, %rbx
+	je .L1
+	movq	(%rax), %rbx
+.L1:
+	/* GCROOT %rbx */
+	popq	%r12
+	popq	%rbx
+	ret
+	.size	pypy_g_do_call_1, .-pypy_g_do_call_1

pypy/translator/c/gcc/trackgcroot.py

         # Add the instruction to the list, and link it to the previous one.
         previnsn = self.insns[-1]
         self.insns.append(insn)
+        if (isinstance(insn, (InsnSetLocal, InsnCopyLocal)) and
+            insn.target == self.tested_for_zero):
+            self.tested_for_zero = None
 
         try:
             lst = insn.previous_insns
 
     def parse_instructions(self):
         self.insns = [InsnFunctionStart(self.CALLEE_SAVE_REGISTERS, self.WORD)]
+        self.tested_for_zero = None
         ignore_insns = False
         for lineno, line in enumerate(self.lines):
             if lineno < self.skip:
             elif match:
                 if not ignore_insns:
                     opname = match.group(1)
+                    #
+                    try:
+                        cf = self.OPS_WITH_PREFIXES_CHANGING_FLAGS[opname]
+                    except KeyError:
+                        cf = self.find_missing_changing_flags(opname)
+                    if cf:
+                        self.tested_for_zero = None
+                    #
                     try:
                         meth = getattr(self, 'visit_' + opname)
                     except AttributeError:
                 raise UnrecognizedOperation(opname)
         setattr(cls, 'visit_' + opname, cls.visit_nop)
 
+    @classmethod
+    def find_missing_changing_flags(cls, opname):
+        prefix = opname
+        while prefix and prefix not in cls.OPS_WITH_PREFIXES_CHANGING_FLAGS:
+            prefix = prefix[:-1]
+        cf = cls.OPS_WITH_PREFIXES_CHANGING_FLAGS.get(prefix, False)
+        cls.OPS_WITH_PREFIXES_CHANGING_FLAGS[opname] = cf
+        return cf
+
     def list_collecting_call_insns(self):
         return [insn for insn in self.insns if isinstance(insn, InsnCall)
                      if insn.name not in self.cannot_collect]
         'movz', 
         ])
 
+    # a partial list is hopefully good enough for now; it's all to support
+    # only one corner case, tested in elf64/track_zero.s
+    OPS_WITH_PREFIXES_CHANGING_FLAGS = dict.fromkeys([
+        'cmp', 'test', 'lahf', 'cld', 'std', 'rep',
+        'ucomi', 'comi',
+        'add', 'sub', 'xor',
+        'inc', 'dec', 'not', 'neg', 'or', 'and', 'sbb', 'adc',
+        'shl', 'shr', 'sal', 'sar', 'rol', 'ror', 'mul', 'imul', 'div', 'idiv',
+        'bt', 'call', 'int',
+        'jmp',     # not really changing flags, but we shouldn't assume
+                   # anything about the operations on the following lines
+        ], True)
+
     visit_movb = visit_nop
     visit_movw = visit_nop
     visit_addb = visit_nop
             return InsnRet(self.CALLEE_SAVE_REGISTERS)
         return InsnStop("jump")
     
-    def register_jump_to(self, label):
-        if not isinstance(self.insns[-1], InsnStop):
-            self.labels[label].previous_insns.append(self.insns[-1])
+    def register_jump_to(self, label, lastinsn=None):
+        if lastinsn is None:
+            lastinsn = self.insns[-1]
+        if not isinstance(lastinsn, InsnStop):
+            self.labels[label].previous_insns.append(lastinsn)
 
-    def conditional_jump(self, line):
+    def conditional_jump(self, line, je=False, jne=False):
         match = self.r_jump.match(line)
         if not match:
             match = self.r_jump_rel_label.match(line)
                 i += 1
         else:
             label = match.group(1)
-        self.register_jump_to(label)
-        return [InsnCondJump(label)]
+        prefix = []
+        lastinsn = None
+        postfix = []
+        if self.tested_for_zero is not None:
+            if je:
+                # generate pseudo-code...
+                prefix = [InsnCopyLocal(self.tested_for_zero, '%tmp'),
+                          InsnSetLocal(self.tested_for_zero)]
+                postfix = [InsnCopyLocal('%tmp', self.tested_for_zero)]
+                lastinsn = prefix[-1]
+            elif jne:
+                postfix = [InsnSetLocal(self.tested_for_zero)]
+        self.register_jump_to(label, lastinsn)
+        return prefix + [InsnCondJump(label)] + postfix
 
     visit_jmpl = visit_jmp
-    visit_je = conditional_jump
-    visit_jne = conditional_jump
     visit_jg = conditional_jump
     visit_jge = conditional_jump
     visit_jl = conditional_jump
     visit_jc = conditional_jump
     visit_jnc = conditional_jump
 
+    def visit_je(self, line):
+        return self.conditional_jump(line, je=True)
+
+    def visit_jne(self, line):
+        return self.conditional_jump(line, jne=True)
+
+    def _visit_test(self, line):
+        match = self.r_binaryinsn.match(line)
+        source = match.group("source")
+        target = match.group("target")
+        if source == target:
+            self.tested_for_zero = source
+        return []
+
     def _visit_xchg(self, line):
         # only support the format used in VALGRIND_DISCARD_TRANSLATIONS
         # which is to use a marker no-op "xchgl %ebx, %ebx"
     visit_and = FunctionGcRootTracker._visit_and
 
     visit_xchgl = FunctionGcRootTracker._visit_xchg
+    visit_testl = FunctionGcRootTracker._visit_test
 
     # used in "xor reg, reg" to create a NULL GC ptr
     visit_xorl = FunctionGcRootTracker.binary_insn
 
     visit_xorq = FunctionGcRootTracker.binary_insn
     visit_xchgq = FunctionGcRootTracker._visit_xchg
+    visit_testq = FunctionGcRootTracker._visit_test
 
     # FIXME: similar to visit_popl for 32-bit
     def visit_popq(self, line):

pypy/translator/platform/windows.py

             linkflags = self._args_for_shared(linkflags) + [
                 '/EXPORT:$(PYPY_MAIN_FUNCTION)']
         linkflags += self._exportsymbols_link_flags(eci, relto=path)
+        # Make sure different functions end up at different addresses!
+        # This is required for the JIT.
+        linkflags.append('/opt:noicf')
 
         if shared:
             so_name = exe_name.new(purebasename='lib' + exe_name.purebasename,