|
| 1 | +local tap = require('tap') |
| 2 | + |
| 3 | +-- Test file to demonstrate LuaJIT misbehaviour on folding |
| 4 | +-- for bitshift operations. |
| 5 | +-- See also, https://github.com/LuaJIT/LuaJIT/issues/1079. |
| 6 | + |
| 7 | +local test = tap.test('lj-1079-fix-64-bitshift-folds'):skipcond({ |
| 8 | + ['Test requires JIT enabled'] = not jit.status(), |
| 9 | +}) |
| 10 | + |
| 11 | +local bit = require('bit') |
| 12 | + |
| 13 | +test:plan(4) |
| 14 | + |
| 15 | +-- Generic function for `bit.ror()`, `bit.rol()`. |
| 16 | +local function bitop_rotation(bitop_func) |
| 17 | + local r = {} |
| 18 | + for i = 1, 4 do |
| 19 | + -- (i & k1) o k2 ==> (i o k2) & (k1 o k2) |
| 20 | + -- XXX: Don't use named constants here to match folding rules. |
| 21 | + -- `7LL` is just some mask, that doesn't change the `i` value. |
| 22 | + -- `32` is used for the half bit-width rotation. |
| 23 | + local int64 = bit.band(i, 7LL) |
| 24 | + r[i] = tonumber(bitop_func(int64, 32)) |
| 25 | + end |
| 26 | + return r |
| 27 | +end |
| 28 | + |
| 29 | +-- Similar function for `bit.rshift()`. |
| 30 | +local function bitop_rshift_signed() |
| 31 | + local r = {} |
| 32 | + for i = 1, 4 do |
| 33 | + -- (i & k1) o k2 ==> (i o k2) & (k1 o k2) |
| 34 | + -- XXX: Use `-i` instead of `i` to prevent other folding due |
| 35 | + -- to IR difference so the IRs don't match fold rule mask. |
| 36 | + -- (-i & 7LL) < 1 << 32 => result == 0. |
| 37 | + local int64 = bit.band(-i, 7LL) |
| 38 | + r[i] = tonumber(bit.rshift(int64, 32)) |
| 39 | + end |
| 40 | + return r |
| 41 | +end |
| 42 | + |
| 43 | +-- A little bit different example, which leads to the assertion |
| 44 | +-- failure due to the incorrect recording. |
| 45 | +local function bitop_rshift_huge() |
| 46 | + local r = {} |
| 47 | + for i = 1, 4 do |
| 48 | + -- (i & k1) o k2 ==> (i o k2) & (k1 o k2) |
| 49 | + -- XXX: Need to use cast to the int64_t via `+ 0LL`, see the |
| 50 | + -- documentation [1] for the details. |
| 51 | + -- [1]: https://bitop.luajit.org/semantics.html |
| 52 | + local int64 = bit.band(2 ^ 33 + i, 2 ^ 33 + 0LL) |
| 53 | + r[i] = tonumber(bit.rshift(int64, 32)) |
| 54 | + end |
| 55 | + return r |
| 56 | +end |
| 57 | + |
| 58 | +local function test_64bitness(subtest, payload_func, bitop_func) |
| 59 | + subtest:plan(1) |
| 60 | + |
| 61 | + jit.off() |
| 62 | + jit.flush() |
| 63 | + local results_joff = payload_func(bitop_func) |
| 64 | + jit.on() |
| 65 | + -- Reset hotcounters. |
| 66 | + jit.opt.start('hotloop=1') |
| 67 | + local results_jon = payload_func(bitop_func) |
| 68 | + subtest:is_deeply(results_jon, results_joff, |
| 69 | + 'same results for VM and JIT for ' .. subtest.name) |
| 70 | +end |
| 71 | + |
| 72 | +test:test('rshift signed', test_64bitness, bitop_rshift_signed) |
| 73 | +test:test('rshift huge', test_64bitness, bitop_rshift_huge) |
| 74 | +test:test('rol', test_64bitness, bitop_rotation, bit.rol) |
| 75 | +test:test('ror', test_64bitness, bitop_rotation, bit.ror) |
| 76 | + |
| 77 | +test:done(true) |
0 commit comments