Jit and the copy module causes miscompilation

Issue #2904 resolved
Björn Lindqvist
created an issue

See the following test code:

import copy
def eva(board, w):
    height = len(board) - 1
    width = len(board[0])
    eb = copy.copy(list(reversed(board[0:height])))

    ch = [0]*width
    for c in range(width):
        # Works if I add the assert!!!
        #assert height == 20
        h = height-1
        while h > 0:
            if eb[h][c] != 0:
                break
            h -= 1
        ch[c] = h + 1
    return max(ch)

board = [
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [4, 0, 0, 1, 1, 0, 0, 0, 0, 0], [4, 4, 4, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
    ]
for x in range(50):
    print(eva(list(board), [1.0, 1.0, 5.0, 2.0, 1.0, 1.0, 1.0]))

When running it with pypy it correctly prints 3 for about 40 loops. Then the JIT runs and after that it prints 2 instead. Python ofc prints the right answer 3 all the time. pypy3 version 3.5.3 and python3 3.7.0.

Comments (5)

  1. Carl Friedrich Bolz-Tereick

    Thanks a lot for hunting that down! This looks like a serious problem. I've simplified the program a little bit further, this fails too:

    import sys
    
    def eva(board):
        height = len(board) - 1
        width = len(board[0])
        eb = board[height-1::-1]
    
        m = -sys.maxint
        for c in range(width):
            # Works if I add the assert!!!
            #assert height == 20
            h = height-1
            while h > 0:
                if eb[h][c] != 0:
                    break
                h -= 1
            if h + 1 > m:
                m = h + 1
        return m
    
    board = [
        [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0], [0, 0, 0, 1, 0, 0, 0, 0, 0, 0], [4, 0, 0, 1, 1, 0, 0, 0, 0, 0], [4, 4, 4, 1, 0, 0, 0, 0, 0, 0], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
        ]
    for x in range(50):
        res = eva(board)
        print res
        assert res == 3
    

    (If I disable unrolling, the program passes. That may be unrelated though, since a lot of random changes make the program "work", eg indeed adding the assert)

  2. Armin Rigo

    With this function, I get the debug_print emitting a value 30002 instead of 30020. In the produced assembler there is "i1 = int_mul(c, 10000)" followed by "int_add_ovf(i1, 2)", i.e. the JIT front-end mistakenly thinks height should be the constant 2 here...

    def eva(board, w):
        height = len(board) - 1
        width = len(board[0])
        eb = (list(reversed(board[0:height])))
    
        ch = [0]*width
        assert height == 20
        for c in range(width):
            # Works if I add the assert!!!
            #assert height == 20
            debug_print(c*10000+height)
            h = height-1
            while h > 0:
                if eb[h][c] != 0:
                    break
                h -= 1
            print '===================================', h+1
            ch[c] = h + 1
        assert height == 20
        print
        return ch#max(ch)
    
  3. Armin Rigo

    Even simpler example:

    from __pypy__ import debug_print
    
    def eva(height):
        for c in range(10):
            debug_print(">>>>>>>>>>>", height-1)
            h = height
            while h > 0:
                h -= 1
    
    while True:
        eva(5)
    

    Dumps a mixture of 0 and 4 instead of all 4, depending on the magic threshold values. Try pypy --jit threshold=15.

  4. Log in to comment