blob: adabbb3a76a9af4e25136d14bb1b733dced4976e [file] [log] [blame]
-------------------------------------------------------------------------------
-- Copyright (c) 2005-2013 Kein-Hong Man, Fabien Fleutot and others.
--
-- All rights reserved.
--
-- This program and the accompanying materials are made available
-- under the terms of the Eclipse Public License v1.0 which
-- accompanies this distribution, and is available at
-- http://www.eclipse.org/legal/epl-v10.html
--
-- This program and the accompanying materials are also made available
-- under the terms of the MIT public license which accompanies this
-- distribution, and is available at http://www.lua.org/license.html
--
-- Contributors:
-- Kein-Hong Man - Initial implementation for Lua 5.0, part of Yueliang
-- Fabien Fleutot - Port to Lua 5.1, integration with Metalua
--
-------------------------------------------------------------------------------
----------------------------------------------------------------------
--
-- WARNING! You're entering a hackish area, proceed at your own risks!
--
-- This code results from the borrowing, then ruthless abuse, of
-- Yueliang's implementation of Lua 5.0 compiler. I claim
-- responsibility for all of the ugly, dirty stuff that you might spot
-- in it.
--
-- Eventually, this code will be rewritten, either in Lua or more
-- probably in C. Meanwhile, if you're interested into digging
-- metalua's sources, this is not the best part to invest your time
-- on.
--
-- End of warning.
--
----------------------------------------------------------------------
--[[--------------------------------------------------------------------
$Id$
lcode.lua
Lua 5 code generator in Lua
This file is part of Yueliang.
Copyright (c) 2005 Kein-Hong Man <khman@users.sf.net>
The COPYRIGHT file describes the conditions
under which this software may be distributed.
See the ChangeLog for more information.
------------------------------------------------------------------------
[FF] Slightly modified, mainly to produce Lua 5.1 bytecode.
----------------------------------------------------------------------]]
--[[--------------------------------------------------------------------
-- Notes:
-- * one function manipulate a pointer argument with a simple data type
-- (can't be emulated by a table, ambiguous), now returns that value:
-- luaK:concat(fs, l1, l2)
-- * some function parameters changed to boolean, additional code
-- translates boolean back to 1/0 for instruction fields
-- * Added:
-- luaK:ttisnumber(o) (from lobject.h)
-- luaK:nvalue(o) (from lobject.h)
-- luaK:setnilvalue(o) (from lobject.h)
-- luaK:setsvalue(o) (from lobject.h)
-- luaK:setnvalue(o) (from lobject.h)
-- luaK:sethvalue(o) (from lobject.h)
----------------------------------------------------------------------]]
local luaP = require 'metalua.compiler.bytecode.lopcodes'
local function debugf() end
local luaK = { }
luaK.MAXSTACK = 250 -- (llimits.h, used in lcode.lua)
luaK.LUA_MULTRET = -1 -- (lua.h)
------------------------------------------------------------------------
-- Marks the end of a patch list. It is an invalid value both as an absolute
-- address, and as a list link (would link an element to itself).
------------------------------------------------------------------------
luaK.NO_JUMP = -1
--FF 5.1
function luaK:isnumeral(e)
return e.k=="VKNUM" and e.t==self.NO_JUMP and e.t==self.NO_JUMP
end
------------------------------------------------------------------------
-- emulation of TObject macros (these are from lobject.h)
-- * TObject is a table since lcode passes references around
-- * tt member field removed, using Lua's type() instead
------------------------------------------------------------------------
function luaK:ttisnumber(o)
if o then return type(o.value) == "number" else return false end
end
function luaK:nvalue(o) return o.value end
function luaK:setnilvalue(o) o.value = nil end
function luaK:setsvalue(o, s) o.value = s end
luaK.setnvalue = luaK.setsvalue
luaK.sethvalue = luaK.setsvalue
------------------------------------------------------------------------
-- returns the instruction object for given e (expdesc)
------------------------------------------------------------------------
function luaK:getcode(fs, e)
return fs.f.code[e.info]
end
------------------------------------------------------------------------
-- codes an instruction with a signed Bx (sBx) field
------------------------------------------------------------------------
function luaK:codeAsBx(fs, o, A, sBx)
return self:codeABx(fs, o, A, sBx + luaP.MAXARG_sBx)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:hasjumps(e)
return e.t ~= e.f
end
------------------------------------------------------------------------
-- FF updated 5.1
------------------------------------------------------------------------
function luaK:_nil(fs, from, n)
if fs.pc > fs.lasttarget then -- no jumps to current position?
if fs.pc == 0 then return end --function start, positions are already clean
local previous = fs.f.code[fs.pc - 1]
if luaP:GET_OPCODE(previous) == "OP_LOADNIL" then
local pfrom = luaP:GETARG_A(previous)
local pto = luaP:GETARG_B(previous)
if pfrom <= from and from <= pto + 1 then -- can connect both?
if from + n - 1 > pto then
luaP:SETARG_B(previous, from + n - 1)
end
return
end
end
end
self:codeABC(fs, "OP_LOADNIL", from, from + n - 1, 0) -- else no optimization
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:jump(fs)
local jpc = fs.jpc -- save list of jumps to here
fs.jpc = self.NO_JUMP
local j = self:codeAsBx(fs, "OP_JMP", 0, self.NO_JUMP)
return self:concat(fs, j, jpc) -- keep them on hold
end
--FF 5.1
function luaK:ret (fs, first, nret)
luaK:codeABC (fs, "OP_RETURN", first, nret+1, 0)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:condjump(fs, op, A, B, C)
self:codeABC(fs, op, A, B, C)
return self:jump(fs)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:fixjump(fs, pc, dest)
local jmp = fs.f.code[pc]
local offset = dest - (pc + 1)
assert(dest ~= self.NO_JUMP)
if math.abs(offset) > luaP.MAXARG_sBx then
luaX:syntaxerror(fs.ls, "control structure too long")
end
luaP:SETARG_sBx(jmp, offset)
end
------------------------------------------------------------------------
-- returns current 'pc' and marks it as a jump target (to avoid wrong
-- optimizations with consecutive instructions not in the same basic block).
------------------------------------------------------------------------
function luaK:getlabel(fs)
fs.lasttarget = fs.pc
return fs.pc
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:getjump(fs, pc)
local offset = luaP:GETARG_sBx(fs.f.code[pc])
if offset == self.NO_JUMP then -- point to itself represents end of list
return self.NO_JUMP -- end of list
else
return (pc + 1) + offset -- turn offset into absolute position
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:getjumpcontrol(fs, pc)
local pi = fs.f.code[pc]
local ppi = fs.f.code[pc - 1]
if pc >= 1 and luaP:testOpMode(luaP:GET_OPCODE(ppi), "OpModeT") then
return ppi
else
return pi
end
end
------------------------------------------------------------------------
-- check whether list has any jump that do not produce a value
-- (or produce an inverted value)
------------------------------------------------------------------------
--FF updated 5.1
function luaK:need_value(fs, list, cond)
while list ~= self.NO_JUMP do
local i = self:getjumpcontrol(fs, list)
if luaP:GET_OPCODE(i) ~= "OP_TESTSET" or
luaP:GETARG_A(i) ~= luaP.NO_REG or
luaP:GETARG_C(i) ~= cond then
return true
end
list = self:getjump(fs, list)
end
return false -- not found
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
--FF updated 5.1
function luaK:patchtestreg(fs, node, reg)
assert(reg) -- pour assurer, vu que j'ai ajoute un parametre p/r a 5.0
local i = self:getjumpcontrol(fs, node)
if luaP:GET_OPCODE(i) ~= "OP_TESTSET" then
return false end -- cannot patch other instructions
if reg ~= luaP.NO_REG and reg ~= luaP:GETARG_B(i) then
luaP:SETARG_A(i, reg)
else
-- no register to put value or register already has the value
luaP:SET_OPCODE(i, "OP_TEST")
luaP:SETARG_A(i, luaP:GETARG_B(i))
luaP:SETARG_B(i, 0)
luaP:SETARG_C(i, luaP:GETARG_C(i))
end
return true
end
--FF added 5.1
function luaK:removevalues (fs, list)
while list ~= self.NO_JUMP do
self:patchtestreg (fs, list, luaP.NO_REG)
list = self:getjump (fs, list)
end
end
------------------------------------------------------------------------
-- FF updated 5.1
------------------------------------------------------------------------
function luaK:patchlistaux(fs, list, vtarget, reg, dtarget)
while list ~= self.NO_JUMP do
local _next = self:getjump(fs, list)
if self:patchtestreg (fs, list, reg) then
self:fixjump(fs, list, vtarget)
else
self:fixjump (fs, list, dtarget)
end
list = _next
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:dischargejpc(fs)
self:patchlistaux(fs, fs.jpc, fs.pc, luaP.NO_REG, fs.pc)
fs.jpc = self.NO_JUMP
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:patchlist(fs, list, target)
if target == fs.pc then
self:patchtohere(fs, list)
else
assert(target < fs.pc)
self:patchlistaux(fs, list, target, luaP.NO_REG, target)
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:patchtohere(fs, list)
self:getlabel(fs)
fs.jpc = self:concat(fs, fs.jpc, list)
end
------------------------------------------------------------------------
-- * l1 was a pointer, now l1 is returned and callee assigns the value
------------------------------------------------------------------------
function luaK:concat(fs, l1, l2)
if l2 == self.NO_JUMP then return l1 -- unchanged
elseif l1 == self.NO_JUMP then
return l2 -- changed
else
local list = l1
local _next = self:getjump(fs, list)
while _next ~= self.NO_JUMP do -- find last element
list = _next
_next = self:getjump(fs, list)
end
self:fixjump(fs, list, l2)
end
return l1 -- unchanged
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:checkstack(fs, n)
local newstack = fs.freereg + n
if newstack > fs.f.maxstacksize then
if newstack >= luaK.MAXSTACK then
luaX:syntaxerror(fs.ls, "function or expression too complex")
end
fs.f.maxstacksize = newstack
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:reserveregs(fs, n)
self:checkstack(fs, n)
fs.freereg = fs.freereg + n
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:freereg(fs, reg)
if not luaP:ISK (reg) and reg >= fs.nactvar then
fs.freereg = fs.freereg - 1
assert(reg == fs.freereg,
string.format("reg=%i, fs.freereg=%i", reg, fs.freereg))
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:freeexp(fs, e)
if e.k == "VNONRELOC" then
self:freereg(fs, e.info)
end
end
------------------------------------------------------------------------
-- k is a constant, v is... what?
-- fs.h is a hash value --> index in f.k
------------------------------------------------------------------------
-- * luaH_get, luaH_set deleted; direct table access used instead
-- * luaO_rawequalObj deleted in first assert
-- * setobj2n deleted in assignment of v to f.k table
------------------------------------------------------------------------
--FF radically updated, not completely understood
function luaK:addk(fs, k, v)
local idx = fs.h[k.value]
local f = fs.f
-- local oldsize = f.sizek
if self:ttisnumber (idx) then
--TODO this assert currently FAILS
--assert(fs.f.k[self:nvalue(idx)] == v)
return self:nvalue(idx)
else -- constant not found; create a new entry
do
local t = type (v.value)
assert(t=="nil" or t=="string" or t=="number" or t=="boolean")
end
--debugf("[const: k[%i] = %s ]", fs.nk, tostringv(v.value))
fs.f.k[fs.nk] = v
fs.h[k.value] = { }
self:setnvalue(fs.h[k.value], fs.nk)
local nk = fs.nk
fs.nk = fs.nk+1
return nk
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:stringK(fs, s)
assert (type(s)=="string")
local o = {} -- TObject
self:setsvalue(o, s)
return self:addk(fs, o, o)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:numberK(fs, r)
assert (type(r)=="number")
local o = {} -- TObject
self:setnvalue(o, r)
return self:addk(fs, o, o)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:boolK(fs, r)
assert (type(r)=="boolean")
local o = {} -- TObject
self:setnvalue(o, r)
return self:addk(fs, o, o)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:nilK(fs)
local k, v = {}, {} -- TObject
self:setnilvalue(v)
self:sethvalue(k, fs.h) -- cannot use nil as key; instead use table itself
return self:addk(fs, k, v)
end
--FF 5.1
function luaK:setreturns (fs, e, nresults)
if e.k == "VCALL" then -- expression is an open function call?
luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
elseif e.k == "VVARARG" then
luaP:SETARG_B (self:getcode (fs, e), nresults + 1)
luaP:SETARG_A (self:getcode (fs, e), fs.freereg)
self:reserveregs (fs, 1)
end
end
--FF 5.1
function luaK:setmultret (fs, e)
self:setreturns (fs, e, self.LUA_MULTRET)
end
--FF 5.1
function luaK:setoneret (fs, e)
if e.k == "VCALL" then -- expression is an open function call?
e.k = "VNONRELOC"
e.info = luaP:GETARG_A(self:getcode(fs, e))
elseif e.k == "VVARARG" then
luaP:SETARG_B (self:getcode (fs, e), 2)
e.k = "VRELOCABLE"
end
end
------------------------------------------------------------------------
--FF deprecated in 5.1
------------------------------------------------------------------------
function luaK:setcallreturns(fs, e, nresults)
assert (false, "setcallreturns deprecated")
--print "SCR:"
--printv(e)
--printv(self:getcode(fs, e))
if e.k == "VCALL" then -- expression is an open function call?
luaP:SETARG_C(self:getcode(fs, e), nresults + 1)
if nresults == 1 then -- 'regular' expression?
e.k = "VNONRELOC"
e.info = luaP:GETARG_A(self:getcode(fs, e))
end
elseif e.k == "VVARARG" then
--printf("Handle vararg return on expr %s, whose code is %s",
-- tostringv(e), tostringv(self:getcode(fs, e)))
if nresults == 1 then
luaP:SETARG_B (self:getcode (fs, e), 2)
e.k = "VRELOCABLE"
--FIXME: why no SETARG_A???
else
luaP:SETARG_B (self:getcode (fs, e), nresults + 1)
luaP:SETARG_A (self:getcode (fs, e), fs.freereg)
self:reserveregs (fs, 1)
--printf("Now code is %s", tostringv(self:getcode(fs, e)))
end
end
end
------------------------------------------------------------------------
-- Ajoute le code pour effectuer l'extraction de la locvar/upval/globvar
-- /idx, sachant
------------------------------------------------------------------------
function luaK:dischargevars(fs, e)
--printf("\ndischargevars\n")
local k = e.k
if k == "VLOCAL" then
e.k = "VNONRELOC"
elseif k == "VUPVAL" then
e.info = self:codeABC(fs, "OP_GETUPVAL", 0, e.info, 0)
e.k = "VRELOCABLE"
elseif k == "VGLOBAL" then
e.info = self:codeABx(fs, "OP_GETGLOBAL", 0, e.info)
e.k = "VRELOCABLE"
elseif k == "VINDEXED" then
self:freereg(fs, e.aux)
self:freereg(fs, e.info)
e.info = self:codeABC(fs, "OP_GETTABLE", 0, e.info, e.aux)
e.k = "VRELOCABLE"
elseif k == "VCALL" or k == "VVARARG" then
self:setoneret(fs, e)
else
-- there is one value available (somewhere)
end
--printf("\n/dischargevars\n")
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:code_label(fs, A, b, jump)
self:getlabel(fs) -- those instructions may be jump targets
return self:codeABC(fs, "OP_LOADBOOL", A, b, jump)
end
------------------------------------------------------------------------
-- FF updated 5.1
------------------------------------------------------------------------
function luaK:discharge2reg(fs, e, reg)
self:dischargevars(fs, e)
local k = e.k
if k == "VNIL" then
self:_nil(fs, reg, 1)
elseif k == "VFALSE" or k == "VTRUE" then
self:codeABC(fs, "OP_LOADBOOL", reg, (e.k == "VTRUE") and 1 or 0, 0)
elseif k == "VKNUM" then
self:codeABx (fs, "OP_LOADK", reg, self:numberK(fs, e.nval))
elseif k == "VK" then
self:codeABx(fs, "OP_LOADK", reg, e.info)
elseif k == "VRELOCABLE" then
local pc = self:getcode(fs, e)
luaP:SETARG_A(pc, reg)
elseif k == "VNONRELOC" then
if reg ~= e.info then
self:codeABC(fs, "OP_MOVE", reg, e.info, 0)
end
else
assert(e.k == "VVOID" or e.k == "VJMP")
return -- nothing to do...
end
e.info = reg
e.k = "VNONRELOC"
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:discharge2anyreg(fs, e)
if e.k ~= "VNONRELOC" then
self:reserveregs(fs, 1)
self:discharge2reg(fs, e, fs.freereg - 1)
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:exp2reg(fs, e, reg)
self:discharge2reg(fs, e, reg)
if e.k == "VJMP" then
e.t = self:concat(fs, e.t, e.info) -- put this jump in 't' list
end
if self:hasjumps(e) then
local final -- position after whole expression
local p_f = self.NO_JUMP -- position of an eventual LOAD false
local p_t = self.NO_JUMP -- position of an eventual LOAD true
if self:need_value(fs, e.t, 1) or self:need_value(fs, e.f, 0) then
local fj = self.NO_JUMP -- first jump (over LOAD ops.)
if e.k ~= "VJMP" then fj = self:jump(fs) end
p_f = self:code_label(fs, reg, 0, 1)
p_t = self:code_label(fs, reg, 1, 0)
self:patchtohere(fs, fj)
end
final = self:getlabel(fs)
self:patchlistaux(fs, e.f, final, reg, p_f)
self:patchlistaux(fs, e.t, final, reg, p_t)
end
e.f, e.t = self.NO_JUMP, self.NO_JUMP
e.info = reg
e.k = "VNONRELOC"
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:exp2nextreg(fs, e)
self:dischargevars(fs, e)
--[FF] Allready in place (added for expr.Stat)
if e.k == "VNONRELOC" and e.info == fs.freereg then
--printf("Expression already in next reg %i: %s", fs.freereg, tostringv(e))
return end
self:freeexp(fs, e)
self:reserveregs(fs, 1)
self:exp2reg(fs, e, fs.freereg - 1)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:exp2anyreg(fs, e)
--printf("exp2anyregs(e=%s)", tostringv(e))
self:dischargevars(fs, e)
if e.k == "VNONRELOC" then
if not self:hasjumps(e) then -- exp is already in a register
return e.info
end
if e.info >= fs.nactvar then -- reg. is not a local?
self:exp2reg(fs, e, e.info) -- put value on it
return e.info
end
end
self:exp2nextreg(fs, e) -- default
return e.info
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:exp2val(fs, e)
if self:hasjumps(e) then
self:exp2anyreg(fs, e)
else
self:dischargevars(fs, e)
end
end
------------------------------------------------------------------------
-- FF updated 5.1
------------------------------------------------------------------------
function luaK:exp2RK(fs, e)
self:exp2val(fs, e)
local k = e.k
if k=="VNIL" or k=="VTRUE" or k=="VFALSE" or k=="VKNUM" then
if fs.nk <= luaP.MAXINDEXRK then
if k=="VNIL" then e.info = self:nilK(fs)
elseif k=="VKNUM" then e.info = self:numberK (fs, e.nval)
else e.info = self:boolK(fs, e.k=="VTRUE") end
e.k = "VK"
return luaP:RKASK(e.info)
end
elseif k == "VK" then
if e.info <= luaP.MAXINDEXRK then -- constant fit in argC?
return luaP:RKASK (e.info)
end
end
-- not a constant in the right range: put it in a register
return self:exp2anyreg(fs, e)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:storevar(fs, var, exp)
--print("STOREVAR")
--printf("var=%s", tostringv(var))
--printf("exp=%s", tostringv(exp))
local k = var.k
if k == "VLOCAL" then
self:freeexp(fs, exp)
self:exp2reg(fs, exp, var.info)
return
elseif k == "VUPVAL" then
local e = self:exp2anyreg(fs, exp)
self:codeABC(fs, "OP_SETUPVAL", e, var.info, 0)
elseif k == "VGLOBAL" then
--printf("store global, exp=%s", tostringv(exp))
local e = self:exp2anyreg(fs, exp)
self:codeABx(fs, "OP_SETGLOBAL", e, var.info)
elseif k == "VINDEXED" then
local e = self:exp2RK(fs, exp)
self:codeABC(fs, "OP_SETTABLE", var.info, var.aux, e)
else
assert(0) -- invalid var kind to store
end
self:freeexp(fs, exp)
--print("/STOREVAR")
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:_self(fs, e, key)
self:exp2anyreg(fs, e)
self:freeexp(fs, e)
local func = fs.freereg
self:reserveregs(fs, 2)
self:codeABC(fs, "OP_SELF", func, e.info, self:exp2RK(fs, key))
self:freeexp(fs, key)
e.info = func
e.k = "VNONRELOC"
end
------------------------------------------------------------------------
-- FF updated 5.1
------------------------------------------------------------------------
function luaK:invertjump(fs, e)
--printf("invertjump on jump instruction #%i", e.info)
--printv(self:getcode(fs, e))
local pc = self:getjumpcontrol(fs, e.info)
assert(luaP:testOpMode(luaP:GET_OPCODE(pc), "OpModeT") and
luaP:GET_OPCODE(pc) ~= "OP_TESTSET" and
luaP:GET_OPCODE(pc) ~= "OP_TEST")
--printf("Before invert:")
--printv(pc)
luaP:SETARG_A(pc, (luaP:GETARG_A(pc) == 0) and 1 or 0)
--printf("After invert:")
--printv(pc)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:jumponcond(fs, e, cond)
if e.k == "VRELOCABLE" then
local ie = self:getcode(fs, e)
if luaP:GET_OPCODE(ie) == "OP_NOT" then
fs.pc = fs.pc - 1 -- remove previous OP_NOT
return self:condjump(fs, "OP_TEST", luaP:GETARG_B(ie), 0,
cond and 0 or 1)
end
-- else go through
end
self:discharge2anyreg(fs, e)
self:freeexp(fs, e)
return self:condjump(fs, "OP_TESTSET", luaP.NO_REG, e.info, cond and 1 or 0)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:goiftrue(fs, e)
local pc -- pc of last jump
self:dischargevars(fs, e)
local k = e.k
if k == "VK" or k == "VTRUE" or k == "VKNUM" then
pc = self.NO_JUMP -- always true; do nothing
elseif k == "VFALSE" then
pc = self:jump(fs) -- always jump
elseif k == "VJMP" then
self:invertjump(fs, e)
pc = e.info
else
pc = self:jumponcond(fs, e, false)
end
e.f = self:concat(fs, e.f, pc) -- insert last jump in 'f' list
self:patchtohere(fs, e.t)
e.t = self.NO_JUMP
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:goiffalse(fs, e)
local pc -- pc of last jump
self:dischargevars(fs, e)
local k = e.k
if k == "VNIL" or k == "VFALSE"then
pc = self.NO_JUMP -- always false; do nothing
elseif k == "VTRUE" then
pc = self:jump(fs) -- always jump
elseif k == "VJMP" then
pc = e.info
else
pc = self:jumponcond(fs, e, true)
end
e.t = self:concat(fs, e.t, pc) -- insert last jump in 't' list
self:patchtohere(fs, e.f)
e.f = self.NO_JUMP
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:codenot(fs, e)
self:dischargevars(fs, e)
local k = e.k
if k == "VNIL" or k == "VFALSE" then
e.k = "VTRUE"
elseif k == "VK" or k == "VKNUM" or k == "VTRUE" then
e.k = "VFALSE"
elseif k == "VJMP" then
self:invertjump(fs, e)
elseif k == "VRELOCABLE" or k == "VNONRELOC" then
self:discharge2anyreg(fs, e)
self:freeexp(fs, e)
e.info = self:codeABC(fs, "OP_NOT", 0, e.info, 0)
e.k = "VRELOCABLE"
else
assert(0) -- cannot happen
end
-- interchange true and false lists
e.f, e.t = e.t, e.f
self:removevalues(fs, e.f)
self:removevalues(fs, e.t)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:indexed(fs, t, k)
t.aux = self:exp2RK(fs, k)
t.k = "VINDEXED"
end
--FF 5.1
function luaK:constfolding (op, e1, e2)
if not self:isnumeral(e1) or not self:isnumeral(e2) then return false end
local v1, v2, e, r = e1.nval, e2 and e2.nval, nil
if op == "OP_ADD" then r = v1+v2
elseif op == "OP_SUB" then r = v1-v2
elseif op == "OP_MUL" then r = v1*v2
elseif op == "OP_DIV" then if v2==0 then return false end r = v1/v2
elseif op == "OP_MOD" then if v2==0 then return false end r = v1%v2
elseif op == "OP_POW" then r = v1^v2
elseif op == "OP_UNM" then r = -v1
elseif op == "OP_LEN" then return false
else assert (false, "Unknown numeric value") end
e1.nval = r
return true
end
--FF 5.1
function luaK:codearith (fs, op, e1, e2)
if self:constfolding (op, e1, e2) then return else
local o1 = self:exp2RK (fs, e1)
local o2 = 0
if op ~= "OP_UNM" and op ~= "OP_LEN" then
o2 = self:exp2RK (fs, e2) end
self:freeexp(fs, e2)
self:freeexp(fs, e1)
e1.info = self:codeABC (fs, op, 0, o1, o2)
e1.k = "VRELOCABLE"
end
end
--FF 5.1
function luaK:codecomp (fs, op, cond, e1, e2)
assert (type (cond) == "boolean")
local o1 = self:exp2RK (fs, e1)
local o2 = self:exp2RK (fs, e2)
self:freeexp (fs, e2)
self:freeexp (fs, e1)
if not cond and op ~= "OP_EQ" then
local temp = o1; o1=o2; o2=temp cond = true end
e1.info = self:condjump (fs, op, cond and 1 or 0, o1, o2)
e1.k = "VJMP"
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:prefix (fs, op, e)
local e2 = { t = self.NO_JUMP; f = self.NO_JUMP;
k = "VKNUM"; nval = 0 }
if op == "unm" then
if e.k == "VK" then
self:exp2anyreg (fs, e) end
self:codearith (fs, "OP_UNM", e, e2)
elseif op == "not" then
self:codenot (fs, e)
elseif op == "len" then
self:exp2anyreg (fs, e)
self:codearith (fs, "OP_LEN", e, e2)
else
assert (false, "Unknown unary operator")
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:infix (fs, op, v)
if op == "and" then
self:goiftrue(fs, v)
elseif op == "or" then
self:goiffalse(fs, v)
elseif op == "concat" then
self:exp2nextreg(fs, v) -- operand must be on the 'stack'
else
if not self:isnumeral (v) then self:exp2RK(fs, v) end
end
end
------------------------------------------------------------------------
--
-- grep "ORDER OPR" if you change these enums
------------------------------------------------------------------------
luaK.arith_opc = { -- done as a table lookup instead of a calc
add = "OP_ADD",
sub = "OP_SUB",
mul = "OP_MUL",
mod = "OP_MOD",
div = "OP_DIV",
pow = "OP_POW",
len = "OP_LEN",
["not"] = "OP_NOT"
}
luaK.test_opc = { -- was ops[] in the codebinop function
eq = {opc="OP_EQ", cond=true},
lt = {opc="OP_LT", cond=true},
le = {opc="OP_LE", cond=true},
-- Pseudo-ops, with no metatable equivalent:
ne = {opc="OP_EQ", cond=false},
gt = {opc="OP_LT", cond=false},
ge = {opc="OP_LE", cond=false}
}
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:posfix(fs, op, e1, e2)
if op == "and" then
assert(e1.t == self.NO_JUMP) -- list must be closed
self:dischargevars(fs, e2)
e2.f = self:concat(fs, e2.f, e1.f)
for k,v in pairs(e2) do e1[k]=v end -- *e1 = *e2
elseif op == "or" then
assert(e1.f == self.NO_JUMP) -- list must be closed
self:dischargevars(fs, e2)
e2.t = self:concat(fs, e2.t, e1.t)
for k,v in pairs(e2) do e1[k]=v end -- *e1 = *e2
elseif op == "concat" then
self:exp2val(fs, e2)
if e2.k == "VRELOCABLE"
and luaP:GET_OPCODE(self:getcode(fs, e2)) == "OP_CONCAT" then
assert(e1.info == luaP:GETARG_B(self:getcode(fs, e2)) - 1)
self:freeexp(fs, e1)
luaP:SETARG_B(self:getcode(fs, e2), e1.info)
e1.k = "VRELOCABLE"; e1.info = e2.info
else
self:exp2nextreg(fs, e2)
self:codearith (fs, "OP_CONCAT", e1, e2)
end
else
local opc = self.arith_opc[op]
if opc then self:codearith (fs, opc, e1, e2) else
opc = self.test_opc[op] or error ("Unknown operator "..op)
self:codecomp (fs, opc.opc, opc.cond, e1, e2)
end
end
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:fixline(fs, line)
--assert (line)
if not line then
--print(debug.traceback "fixline (line == nil)")
end
fs.f.lineinfo[fs.pc - 1] = line or 0
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:code(fs, i, line)
if not line then
line = 0
--print(debug.traceback "line == nil")
end
local f = fs.f
do -- print it
local params = { }
for _,x in ipairs{"A","B","Bx", "sBx", "C"} do
if i[x] then table.insert (params, string.format ("%s=%i", x, i[x])) end
end
debugf ("[code:\t%s\t%s]", luaP.opnames[i.OP], table.concat (params, ", "))
end
self:dischargejpc(fs) -- 'pc' will change
f.code[fs.pc] = i
f.lineinfo[fs.pc] = line
if line == 0 then
f.lineinfo[fs.pc] = fs.lastline
if fs.lastline == 0 then
--print(debug.traceback())
end
end
if f.lineinfo[fs.pc] == 0 then
f.lineinfo[fs.pc] = 42
end
local pc = fs.pc
fs.pc = fs.pc + 1
return pc
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:codeABC(fs, o, a, b, c)
assert(luaP:getOpMode(o) == "iABC", o.." is not an ABC operation")
--assert getbmode(o) ~= opargn or b == 0
--assert getcmode(o) ~= opargn or c == 0
--FF
--return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.ls.lastline)
return self:code(fs, luaP:CREATE_ABC(o, a, b, c), fs.lastline)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:codeABx(fs, o, a, bc)
assert(luaP:getOpMode(o) == "iABx" or luaP:getOpMode(o) == "iAsBx")
--assert getcmode(o) == opargn
--FF
--return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.ls.lastline)
return self:code(fs, luaP:CREATE_ABx(o, a, bc), fs.lastline)
end
------------------------------------------------------------------------
--
------------------------------------------------------------------------
function luaK:setlist (fs, base, nelems, tostore)
local c = math.floor ((nelems-1) / luaP.LFIELDS_PER_FLUSH + 1)
local b = tostore == self.LUA_MULTRET and 0 or tostore
assert (tostore ~= 0)
if c <= luaP.MAXARG_C then self:codeABC (fs, "OP_SETLIST", base, b, c)
else
self:codeABC (fs, "OP_SETLIST", base, b, 0)
self:code (fs, c, fs.lastline)--FIXME
end
fs.freereg = base + 1
end
return luaK