Skip to content

Commit b720190

Browse files
committed
fix compiler producing incorrect LOADNIL byte code
1 parent 2348fd0 commit b720190

File tree

2 files changed

+85
-1
lines changed

2 files changed

+85
-1
lines changed

compile.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ func (cd *codeStore) PropagateMV(top int, save *int, reg *int, inc int) {
237237

238238
func (cd *codeStore) AddLoadNil(a, b, line int) {
239239
last := cd.Last()
240-
if opGetOpCode(last) == OP_LOADNIL && (opGetArgA(last)+opGetArgB(last)) == a {
240+
if opGetOpCode(last) == OP_LOADNIL && (opGetArgB(last)+1) == a {
241241
cd.SetB(cd.LastPC(), b)
242242
} else {
243243
cd.AddABC(OP_LOADNIL, a, b, 0, line)

script_test.go

+84
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"fmt"
55
"os"
66
"runtime"
7+
"strings"
78
"sync/atomic"
89
"testing"
910
"time"
@@ -146,3 +147,86 @@ func TestGlua(t *testing.T) {
146147
func TestLua(t *testing.T) {
147148
testScriptDir(t, luaTests, "_lua5.1-tests")
148149
}
150+
151+
func TestMergingLoadNilBug(t *testing.T) {
152+
// there was a bug where a multiple load nils were being incorrectly merged, and the following code exposed it
153+
s := `
154+
function test()
155+
local a = 0
156+
local b = 1
157+
local c = 2
158+
local d = 3
159+
local e = 4 -- reg 4
160+
local f = 5
161+
local g = 6
162+
local h = 7
163+
164+
if e == 4 then
165+
e = nil -- should clear reg 4, but clears regs 4-8 by mistake
166+
end
167+
if f == nil then
168+
error("bad f")
169+
end
170+
if g == nil then
171+
error("bad g")
172+
end
173+
if h == nil then
174+
error("bad h")
175+
end
176+
end
177+
178+
test()
179+
`
180+
181+
L := NewState()
182+
defer L.Close()
183+
if err := L.DoString(s); err != nil {
184+
t.Error(err)
185+
}
186+
}
187+
188+
func TestMergingLoadNil(t *testing.T) {
189+
// multiple nil assignments to consecutive registers should be merged
190+
s := `
191+
function test()
192+
local a = 0
193+
local b = 1
194+
local c = 2
195+
196+
-- this should generate just one LOADNIL byte code instruction
197+
a = nil
198+
b = nil
199+
c = nil
200+
201+
print(a,b,c)
202+
end
203+
204+
test()`
205+
206+
chunk, err := parse.Parse(strings.NewReader(s), "test")
207+
if err != nil {
208+
t.Fatal(err)
209+
}
210+
211+
compiled, err := Compile(chunk, "test")
212+
if err != nil {
213+
t.Fatal(err)
214+
}
215+
216+
if len(compiled.FunctionPrototypes) != 1 {
217+
t.Fatal("expected 1 function prototype")
218+
}
219+
220+
// there should be exactly 1 LOADNIL instruction in the byte code generated for the above
221+
// anymore, and the LOADNIL merging is not working correctly
222+
count := 0
223+
for _, instr := range compiled.FunctionPrototypes[0].Code {
224+
if opGetOpCode(instr) == OP_LOADNIL {
225+
count++
226+
}
227+
}
228+
229+
if count != 1 {
230+
t.Fatalf("expected 1 LOADNIL instruction, found %d", count)
231+
}
232+
}

0 commit comments

Comments
 (0)