@@ -3,31 +3,23 @@ local test = tap.test('lj-736-BC_UCLO-triggers-infinite-loop'):skipcond({
3
3
[' Test requires JIT enabled' ] = not jit .status (),
4
4
})
5
5
6
- test :plan (2 )
7
-
8
6
-- Test reproduces an issue when BC_UCLO triggers an infinite loop.
9
7
-- See details in https://github.com/LuaJIT/LuaJIT/issues/736.
10
- --
11
- -- Listing below demonstrates a problem -
12
- -- the bytecode UCLO on the line 13 makes a loop at 0013-0014:
13
- --
14
- -- - BYTECODE -- bc_uclo.lua:0-20
15
- -- 0001 KPRI 0 0
16
- -- 0002 FNEW 1 0 ; bc_uclo.lua:5
17
- -- 0003 KSHORT 2 1
18
- -- 0004 KSHORT 3 4
19
- -- 0005 KSHORT 4 1
20
- -- 0006 FORI 2 => 0011
21
- -- 0007 => ISNEN 5 0 ; 2
22
- -- 0008 JMP 6 => 0010
23
- -- 0009 UCLO 0 => 0012
24
- -- 0010 => FORL 2 => 0007
25
- -- 0011 => UCLO 0 => 0012
26
- -- 0012 => KPRI 0 0
27
- -- 0013 UCLO 0 => 0012
28
- -- 0014 FNEW 1 1 ; bc_uclo.lua:18
29
- -- 0015 UCLO 0 => 0016
30
- -- 0016 => RET0 0 1
8
+
9
+ test :plan (2 )
10
+
11
+ -- Before the patch, the code flow like in the `testcase()` below
12
+ -- may cause the problem -- use-def analysis for the 0019 UCLO
13
+ -- creates an infinite loop in 0014 - 0019:
14
+ -- | 0008 FORI base: 4 jump: => 0013
15
+ -- | 0009 ISNEN var: 7 num: 0 ; number 2
16
+ -- | 0010 JMP rbase: 8 jump: => 0012
17
+ -- | 0011 UCLO rbase: 2 jump: => 0014
18
+ -- | 0012 FORL base: 4 jump: => 0009
19
+ -- | 0013 UCLO rbase: 2 jump: => 0014
20
+ -- | 0014 KPRI dst: 2 pri: 0 ; Start of `assert()` line.
21
+ -- | ...
22
+ -- | 0019 UCLO rbase: 2 jump: => 0014
31
23
32
24
jit .opt .start (' hotloop=1' )
33
25
@@ -36,20 +28,17 @@ local assert = assert
36
28
37
29
local function testcase ()
38
30
-- The code in the first scope `do`/`end` is a prerequisite.
39
- -- It is needed so that we have a trace at the exit from which
40
- -- the creation of the snapshot will begin .
31
+ -- It contains the UCLO instruction for the `uv1`. The use-def
32
+ -- analysis for it escapes this `do`/`end` scope .
41
33
do
42
- -- Upvalue below is not used actually, but it is required
43
- -- for calling `snap_usedef()` on trace exit.
44
- local uv1 -- luacheck: ignore
34
+ local uv1 -- luacheck: no unused
45
35
local _ = function () return uv1 end
46
36
47
- -- The loop below is required for recording a trace.
48
- -- The condition inside a loop executes `goto` to a label
49
- -- outside of the loop when the code executed by JIT and
50
- -- this triggers snapshotting.
37
+ -- Records the trace for which use-def analysis is applied.
51
38
for i = 1 , 2 do
52
- -- Exit to interpreter once trace is compiled.
39
+ -- This condition triggers snapshoting and use-def analysis.
40
+ -- Before the patch this triggers the infinite loop in the
41
+ -- `snap_usedef()`, so the `goto` is never taken.
53
42
if i == 2 then
54
43
goto x
55
44
end
@@ -60,16 +49,12 @@ local function testcase()
60
49
do
61
50
local uv2 -- luacheck: no unused
62
51
63
- -- `goto` if not executed without a patch and generates an
64
- -- UCLO bytecode that makes an infinite loop in a function
65
- -- `snap_usedef` when patch is not applied. `goto` must point
66
- -- to the label on one of the previous lines. `assert()` is
67
- -- executed when patch is applied.
52
+ -- Create a tight loop for the one more upvalue (`uv2`).
53
+ -- Before the patch, use-def analysis gets stuck in this code
54
+ -- flow.
68
55
assert (nil , assert_msg )
69
56
goto x
70
-
71
- -- Line below is required, it makes `uv` upvalue, and must be
72
- -- placed after `goto`, otherwise reproducer become broken.
57
+ -- This code is unreachable by design.
73
58
local _ = function () return uv2 end -- luacheck: ignore
74
59
end
75
60
end
@@ -79,4 +64,4 @@ local ok, err = pcall(testcase)
79
64
test :is (ok , false , ' assertion is triggered in a function with testcase' )
80
65
test :ok (err :match (assert_msg ), ' BC_UCLO does not trigger an infinite loop' )
81
66
82
- os.exit ( test :check () and 0 or 1 )
67
+ test :done ( true )
0 commit comments