1-- $Id: testes/code.lua $
  2-- See Copyright Notice in file all.lua
  3
  4if T==nil then
  5  (Message or print)('\n >>> testC not active: skipping opcode tests <<<\n')
  6  return
  7end
  8print "testing code generation and optimizations"
  9
 10-- to test constant propagation
 11local k0aux <const> = 0
 12local k0 <const> = k0aux
 13local k1 <const> = 1
 14local k3 <const> = 3
 15local k6 <const> = k3 + (k3 << k0)
 16local kFF0 <const> = 0xFF0
 17local k3_78 <const> = 3.78
 18local x, k3_78_4 <const> = 10, k3_78 / 4
 19assert(x == 10)
 20
 21local kx <const> = "x"
 22
 23local kTrue <const> = true
 24local kFalse <const> = false
 25
 26local kNil <const> = nil
 27
 28-- this code gave an error for the code checker
 29do
 30  local function f (a)
 31  for k,v,w in a do end
 32  end
 33end
 34
 35
 36-- testing reuse in constant table
 37local function checkKlist (func, list)
 38  local k = T.listk(func)
 39  assert(#k == #list)
 40  for i = 1, #k do
 41    assert(k[i] == list[i] and math.type(k[i]) == math.type(list[i]))
 42  end
 43end
 44
 45local function foo ()
 46  local a
 47  a = k3;
 48  a = 0; a = 0.0; a = -7 + 7
 49  a = k3_78/4; a = k3_78_4
 50  a = -k3_78/4; a = k3_78/4; a = -3.78/4
 51  a = -3.79/4; a = 0.0; a = -0;
 52  a = k3; a = 3.0; a = 3; a = 3.0
 53end
 54
 55checkKlist(foo, {3.78/4, -3.78/4, -3.79/4})
 56
 57
 58foo = function (f, a)
 59        f(100 * 1000)
 60        f(100.0 * 1000)
 61        f(-100 * 1000)
 62        f(-100 * 1000.0)
 63        f(100000)
 64        f(100000.0)
 65        f(-100000)
 66        f(-100000.0)
 67      end
 68
 69checkKlist(foo, {100000, 100000.0, -100000, -100000.0})
 70
 71
 72-- floats x integers
 73foo = function (t, a)
 74  t[a] = 1; t[a] = 1.0
 75  t[a] = 1; t[a] = 1.0
 76  t[a] = 2; t[a] = 2.0
 77  t[a] = 0; t[a] = 0.0
 78  t[a] = 1; t[a] = 1.0
 79  t[a] = 2; t[a] = 2.0
 80  t[a] = 0; t[a] = 0.0
 81end
 82
 83checkKlist(foo, {1, 1.0, 2, 2.0, 0, 0.0})
 84
 85
 86-- testing opcodes
 87
 88-- check that 'f' opcodes match '...'
 89local function check (f, ...)
 90  local arg = {...}
 91  local c = T.listcode(f)
 92  for i=1, #arg do
 93    local opcode = string.match(c[i], "%u%w+")
 94    -- print(arg[i], opcode)
 95    assert(arg[i] == opcode)
 96  end
 97  assert(c[#arg+2] == undef)
 98end
 99
100
101-- check that 'f' opcodes match '...' and that 'f(p) == r'.
102local function checkR (f, p, r, ...)
103  local r1 = f(p)
104  assert(r == r1 and math.type(r) == math.type(r1))
105  check(f, ...)
106end
107
108
109-- check that 'a' and 'b' has the same opcodes
110local function checkequal (a, b)
111  a = T.listcode(a)
112  b = T.listcode(b)
113  assert(#a == #b)
114  for i = 1, #a do
115    a[i] = string.gsub(a[i], '%b()', '')   -- remove line number
116    b[i] = string.gsub(b[i], '%b()', '')   -- remove line number
117    assert(a[i] == b[i])
118  end
119end
120
121
122-- some basic instructions
123check(function ()   -- function does not create upvalues
124  (function () end){f()}
125end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
126     'SETLIST', 'CALL', 'RETURN0')
127
128check(function (x)   -- function creates upvalues
129  (function () return x end){f()}
130end, 'CLOSURE', 'NEWTABLE', 'EXTRAARG', 'GETTABUP', 'CALL',
131     'SETLIST', 'CALL', 'RETURN')
132
133
134-- sequence of LOADNILs
135check(function ()
136  local kNil <const> = nil
137  local a,b,c
138  local d; local e;
139  local f,g,h;
140  d = nil; d=nil; b=nil; a=kNil; c=nil;
141end, 'LOADNIL', 'RETURN0')
142
143check(function ()
144  local a,b,c,d = 1,1,1,1
145  d=nil;c=nil;b=nil;a=nil
146end, 'LOADI', 'LOADI', 'LOADI', 'LOADI', 'LOADNIL', 'RETURN0')
147
148do
149  local a,b,c,d = 1,1,1,1
150  d=nil;c=nil;b=nil;a=nil
151  assert(a == nil and b == nil and c == nil and d == nil)
152end
153
154
155-- single return
156check (function (a,b,c) return a end, 'RETURN1')
157
158
159-- infinite loops
160check(function () while kTrue do local a = -1 end end,
161'LOADI', 'JMP', 'RETURN0')
162
163check(function () while 1 do local a = -1 end end,
164'LOADI', 'JMP', 'RETURN0')
165
166check(function () repeat local x = 1 until true end,
167'LOADI', 'RETURN0')
168
169
170-- concat optimization
171check(function (a,b,c,d) return a..b..c..d end,
172  'MOVE', 'MOVE', 'MOVE', 'MOVE', 'CONCAT', 'RETURN1')
173
174-- not
175check(function () return not not nil end, 'LOADFALSE', 'RETURN1')
176check(function () return not not kFalse end, 'LOADFALSE', 'RETURN1')
177check(function () return not not true end, 'LOADTRUE', 'RETURN1')
178check(function () return not not k3 end, 'LOADTRUE', 'RETURN1')
179
180-- direct access to locals
181check(function ()
182  local a,b,c,d
183  a = b*a
184  c.x, a[b] = -((a + d/b - a[b]) ^ a.x), b
185end,
186  'LOADNIL',
187  'MUL', 'MMBIN',
188  'DIV', 'MMBIN', 'ADD', 'MMBIN', 'GETTABLE', 'SUB', 'MMBIN',
189  'GETFIELD', 'POW', 'MMBIN', 'UNM', 'SETTABLE', 'SETFIELD', 'RETURN0')
190
191
192-- direct access to constants
193check(function ()
194  local a,b
195  local c = kNil
196  a[kx] = 3.2
197  a.x = b
198  a[b] = 'x'
199end,
200  'LOADNIL', 'SETFIELD', 'SETFIELD', 'SETTABLE', 'RETURN0')
201
202-- "get/set table" with numeric indices
203check(function (a)
204  local k255 <const> = 255
205  a[1] = a[100]
206  a[k255] = a[256]
207  a[256] = 5
208end,
209  'GETI', 'SETI',
210  'LOADI', 'GETTABLE', 'SETI',
211  'LOADI', 'SETTABLE',  'RETURN0')
212
213check(function ()
214  local a,b
215  a = a - a
216  b = a/a
217  b = 5-4
218end,
219  'LOADNIL', 'SUB', 'MMBIN', 'DIV', 'MMBIN', 'LOADI', 'RETURN0')
220
221check(function ()
222  local a,b
223  a[kTrue] = false
224end,
225  'LOADNIL', 'LOADTRUE', 'SETTABLE', 'RETURN0')
226
227
228-- equalities
229checkR(function (a) if a == 1 then return 2 end end, 1, 2,
230  'EQI', 'JMP', 'LOADI', 'RETURN1')
231
232checkR(function (a) if -4.0 == a then return 2 end end, -4, 2,
233  'EQI', 'JMP', 'LOADI', 'RETURN1')
234
235checkR(function (a) if a == "hi" then return 2 end end, 10, nil,
236  'EQK', 'JMP', 'LOADI', 'RETURN1')
237
238checkR(function (a) if a == 10000 then return 2 end end, 1, nil,
239  'EQK', 'JMP', 'LOADI', 'RETURN1')   -- number too large
240
241checkR(function (a) if -10000 == a then return 2 end end, -10000, 2,
242  'EQK', 'JMP', 'LOADI', 'RETURN1')   -- number too large
243
244-- comparisons
245
246checkR(function (a) if -10 <= a then return 2 end end, -10, 2,
247  'GEI', 'JMP', 'LOADI', 'RETURN1')
248
249checkR(function (a) if 128.0 > a then return 2 end end, 129, nil,
250  'LTI', 'JMP', 'LOADI', 'RETURN1')
251
252checkR(function (a) if -127.0 < a then return 2 end end, -127, nil,
253  'GTI', 'JMP', 'LOADI', 'RETURN1')
254
255checkR(function (a) if 10 < a then return 2 end end, 11, 2,
256  'GTI', 'JMP', 'LOADI', 'RETURN1')
257
258checkR(function (a) if 129 < a then return 2 end end, 130, 2,
259  'LOADI', 'LT', 'JMP', 'LOADI', 'RETURN1')
260
261checkR(function (a) if a >= 23.0 then return 2 end end, 25, 2,
262  'GEI', 'JMP', 'LOADI', 'RETURN1')
263
264checkR(function (a) if a >= 23.1 then return 2 end end, 0, nil,
265  'LOADK', 'LE', 'JMP', 'LOADI', 'RETURN1')
266
267checkR(function (a) if a > 2300.0 then return 2 end end, 0, nil,
268  'LOADF', 'LT', 'JMP', 'LOADI', 'RETURN1')
269
270
271-- constant folding
272local function checkK (func, val)
273  check(func, 'LOADK', 'RETURN1')
274  checkKlist(func, {val})
275  assert(func() == val)
276end
277
278local function checkI (func, val)
279  check(func, 'LOADI', 'RETURN1')
280  checkKlist(func, {})
281  assert(func() == val)
282end
283
284local function checkF (func, val)
285  check(func, 'LOADF', 'RETURN1')
286  checkKlist(func, {})
287  assert(func() == val)
288end
289
290checkF(function () return 0.0 end, 0.0)
291checkI(function () return k0 end, 0)
292checkI(function () return -k0//1 end, 0)
293checkK(function () return 3^-1 end, 1/3)
294checkK(function () return (1 + 1)^(50 + 50) end, 2^100)
295checkK(function () return (-2)^(31 - 2) end, -0x20000000 + 0.0)
296checkF(function () return (-k3^0 + 5) // 3.0 end, 1.0)
297checkI(function () return -k3 % 5 end, 2)
298checkF(function () return -((2.0^8 + -(-1)) % 8)/2 * 4 - 3 end, -5.0)
299checkF(function () return -((2^8 + -(-1)) % 8)//2 * 4 - 3 end, -7.0)
300checkI(function () return 0xF0.0 | 0xCC.0 ~ 0xAA & 0xFD end, 0xF4)
301checkI(function () return ~(~kFF0 | kFF0) end, 0)
302checkI(function () return ~~-1024.0 end, -1024)
303checkI(function () return ((100 << k6) << -4) >> 2 end, 100)
304
305-- borders around MAXARG_sBx ((((1 << 17) - 1) >> 1) == 65535)
306local a = 17; local sbx = ((1 << a) - 1) >> 1   -- avoid folding
307local border <const> = 65535
308checkI(function () return border end, sbx)
309checkI(function () return -border end, -sbx)
310checkI(function () return border + 1 end, sbx + 1)
311checkK(function () return border + 2 end, sbx + 2)
312checkK(function () return -(border + 1) end, -(sbx + 1))
313
314local border <const> = 65535.0
315checkF(function () return border end, sbx + 0.0)
316checkF(function () return -border end, -sbx + 0.0)
317checkF(function () return border + 1 end, (sbx + 1.0))
318checkK(function () return border + 2 end, (sbx + 2.0))
319checkK(function () return -(border + 1) end, -(sbx + 1.0))
320
321
322-- immediate operands
323checkR(function (x) return x + k1 end, 10, 11, 'ADDI', 'MMBINI', 'RETURN1')
324checkR(function (x) return x - 127 end, 10, -117, 'ADDI', 'MMBINI', 'RETURN1')
325checkR(function (x) return 128 + x end, 0.0, 128.0,
326         'ADDI', 'MMBINI', 'RETURN1')
327checkR(function (x) return x * -127 end, -1.0, 127.0,
328         'MULK', 'MMBINK', 'RETURN1')
329checkR(function (x) return 20 * x end, 2, 40, 'MULK', 'MMBINK', 'RETURN1')
330checkR(function (x) return x ^ -2 end, 2, 0.25, 'POWK', 'MMBINK', 'RETURN1')
331checkR(function (x) return x / 40 end, 40, 1.0, 'DIVK', 'MMBINK', 'RETURN1')
332checkR(function (x) return x // 1 end, 10.0, 10.0,
333         'IDIVK', 'MMBINK', 'RETURN1')
334checkR(function (x) return x % (100 - 10) end, 91, 1,
335         'MODK', 'MMBINK', 'RETURN1')
336checkR(function (x) return k1 << x end, 3, 8, 'SHLI', 'MMBINI', 'RETURN1')
337checkR(function (x) return x << 127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
338checkR(function (x) return x << -127 end, 10, 0, 'SHRI', 'MMBINI', 'RETURN1')
339checkR(function (x) return x >> 128 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
340checkR(function (x) return x >> -127 end, 8, 0, 'SHRI', 'MMBINI', 'RETURN1')
341checkR(function (x) return x & 1 end, 9, 1, 'BANDK', 'MMBINK', 'RETURN1')
342checkR(function (x) return 10 | x end, 1, 11, 'BORK', 'MMBINK', 'RETURN1')
343checkR(function (x) return -10 ~ x end, -1, 9, 'BXORK', 'MMBINK', 'RETURN1')
344
345-- K operands in arithmetic operations
346checkR(function (x) return x + 0.0 end, 1, 1.0, 'ADDK', 'MMBINK', 'RETURN1')
347--  check(function (x) return 128 + x end, 'ADDK', 'MMBINK', 'RETURN1')
348checkR(function (x) return x * -10000 end, 2, -20000,
349         'MULK', 'MMBINK', 'RETURN1')
350--  check(function (x) return 20 * x end, 'MULK', 'MMBINK', 'RETURN1')
351checkR(function (x) return x ^ 0.5 end, 4, 2.0, 'POWK', 'MMBINK', 'RETURN1')
352checkR(function (x) return x / 2.0 end, 4, 2.0, 'DIVK', 'MMBINK', 'RETURN1')
353checkR(function (x) return x // 10000 end, 10000, 1,
354         'IDIVK', 'MMBINK', 'RETURN1')
355checkR(function (x) return x % (100.0 - 10) end, 91, 1.0,
356         'MODK', 'MMBINK', 'RETURN1')
357
358-- no foldings (and immediate operands)
359check(function () return -0.0 end, 'LOADF', 'UNM', 'RETURN1')
360check(function () return k3/0 end, 'LOADI', 'DIVK', 'MMBINK', 'RETURN1')
361check(function () return 0%0 end, 'LOADI', 'MODK', 'MMBINK', 'RETURN1')
362check(function () return -4//0 end, 'LOADI', 'IDIVK', 'MMBINK', 'RETURN1')
363check(function (x) return x >> 2.0 end, 'LOADF', 'SHR', 'MMBIN', 'RETURN1')
364check(function (x) return x << 128 end, 'LOADI', 'SHL', 'MMBIN', 'RETURN1')
365check(function (x) return x & 2.0 end, 'LOADF', 'BAND', 'MMBIN', 'RETURN1')
366
367-- basic 'for' loops
368check(function () for i = -10, 10.5 do end end,
369'LOADI', 'LOADK', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
370check(function () for i = 0xfffffff, 10.0, 1 do end end,
371'LOADK', 'LOADF', 'LOADI', 'FORPREP', 'FORLOOP', 'RETURN0')
372
373-- bug in constant folding for 5.1
374check(function () return -nil end, 'LOADNIL', 'UNM', 'RETURN1')
375
376
377check(function ()
378  local a,b,c
379  b[c], a = c, b
380  b[a], a = c, b
381  a, b = c, a
382  a = a
383end,
384  'LOADNIL',
385  'MOVE', 'MOVE', 'SETTABLE',
386  'MOVE', 'MOVE', 'MOVE', 'SETTABLE',
387  'MOVE', 'MOVE', 'MOVE',
388  -- no code for a = a
389  'RETURN0')
390
391
392-- x == nil , x ~= nil
393-- checkequal(function (b) if (a==nil) then a=1 end; if a~=nil then a=1 end end,
394--            function () if (a==9) then a=1 end; if a~=9 then a=1 end end)
395
396-- check(function () if a==nil then a='a' end end,
397-- 'GETTABUP', 'EQ', 'JMP', 'SETTABUP', 'RETURN')
398
399do   -- tests for table access in upvalues
400  local t
401  check(function () t[kx] = t.y end, 'GETTABUP', 'SETTABUP')
402  check(function (a) t[a()] = t[a()] end,
403  'MOVE', 'CALL', 'GETUPVAL', 'MOVE', 'CALL',
404  'GETUPVAL', 'GETTABLE', 'SETTABLE')
405end
406
407-- de morgan
408checkequal(function () local a; if not (a or b) then b=a end end,
409           function () local a; if (not a and not b) then b=a end end)
410
411checkequal(function (l) local a; return 0 <= a and a <= l end,
412           function (l) local a; return not (not(a >= 0) or not(a <= l)) end)
413
414
415-- if-break optimizations
416check(function (a, b)
417        while a do
418          if b then break else a = a + 1 end
419        end
420      end,
421'TEST', 'JMP', 'TEST', 'JMP', 'ADDI', 'MMBINI', 'JMP', 'RETURN0')
422
423checkequal(function () return 6 or true or nil end,
424           function () return k6 or kTrue or kNil end)
425
426checkequal(function () return 6 and true or nil end,
427           function () return k6 and kTrue or kNil end)
428
429
430do   -- string constants
431  local k0 <const> = "00000000000000000000000000000000000000000000000000"
432  local function f1 ()
433    local k <const> = k0
434    return function ()
435             return function () return k end
436           end
437  end
438
439  local f2 = f1()
440  local f3 = f2()
441  assert(f3() == k0)
442  checkK(f3, k0)
443  -- string is not needed by other functions
444  assert(T.listk(f1)[1] == nil)
445  assert(T.listk(f2)[1] == nil)
446end
447
448print 'OK'
449