]> git.lizzy.rs Git - metalua.git/blobdiff - metalua/compiler/bytecode/lcode.lua
Merge branch 'master' of ssh://git.eclipse.org/gitroot/koneki/org.eclipse.koneki...
[metalua.git] / metalua / compiler / bytecode / lcode.lua
diff --git a/metalua/compiler/bytecode/lcode.lua b/metalua/compiler/bytecode/lcode.lua
new file mode 100644 (file)
index 0000000..ede1a1c
--- /dev/null
@@ -0,0 +1,1038 @@
+-------------------------------------------------------------------------------
+-- 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
+--
+-------------------------------------------------------------------------------
+
+--[[--------------------------------------------------------------------
+
+  $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
+    error("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
+      error("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
\ No newline at end of file