------------------------------------------------------------------------------- -- 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$ lopcodes.lua Lua 5 virtual machine opcodes in Lua This file is part of Yueliang. Copyright (c) 2005 Kein-Hong Man 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: -- * an Instruction is a table with OP, A, B, C, Bx elements; this -- should allow instruction handling to work with doubles and ints -- * Added: -- luaP:Instruction(i): convert field elements to a 4-char string -- luaP:DecodeInst(x): convert 4-char string into field elements -- * WARNING luaP:Instruction outputs instructions encoded in little- -- endian form and field size and positions are hard-coded ----------------------------------------------------------------------]] local function debugf() end local luaP = { } --[[ =========================================================================== We assume that instructions are unsigned numbers. All instructions have an opcode in the first 6 bits. Instructions can have the following fields: 'A' : 8 bits 'B' : 9 bits 'C' : 9 bits 'Bx' : 18 bits ('B' and 'C' together) 'sBx' : signed Bx A signed argument is represented in excess K; that is, the number value is the unsigned value minus K. K is exactly the maximum value for that argument (so that -max is represented by 0, and +max is represented by 2*max), which is half the maximum for the corresponding unsigned argument. =========================================================================== --]] luaP.OpMode = {"iABC", "iABx", "iAsBx"} -- basic instruction format ------------------------------------------------------------------------ -- size and position of opcode arguments. -- * WARNING size and position is hard-coded elsewhere in this script ------------------------------------------------------------------------ luaP.SIZE_C = 9 luaP.SIZE_B = 9 luaP.SIZE_Bx = luaP.SIZE_C + luaP.SIZE_B luaP.SIZE_A = 8 luaP.SIZE_OP = 6 luaP.POS_C = luaP.SIZE_OP luaP.POS_B = luaP.POS_C + luaP.SIZE_C luaP.POS_Bx = luaP.POS_C luaP.POS_A = luaP.POS_B + luaP.SIZE_B --FF from 5.1 luaP.BITRK = 2^(luaP.SIZE_B - 1) function luaP:ISK(x) return x >= self.BITRK end luaP.MAXINDEXRK = luaP.BITRK - 1 function luaP:RKASK(x) if x < self.BITRK then return x+self.BITRK else return x end end ------------------------------------------------------------------------ -- limits for opcode arguments. -- we use (signed) int to manipulate most arguments, -- so they must fit in BITS_INT-1 bits (-1 for sign) ------------------------------------------------------------------------ -- removed "#if SIZE_Bx < BITS_INT-1" test, assume this script is -- running on a Lua VM with double or int as LUA_NUMBER luaP.MAXARG_Bx = math.ldexp(1, luaP.SIZE_Bx) - 1 luaP.MAXARG_sBx = math.floor(luaP.MAXARG_Bx / 2) -- 'sBx' is signed luaP.MAXARG_A = math.ldexp(1, luaP.SIZE_A) - 1 luaP.MAXARG_B = math.ldexp(1, luaP.SIZE_B) - 1 luaP.MAXARG_C = math.ldexp(1, luaP.SIZE_C) - 1 -- creates a mask with 'n' 1 bits at position 'p' -- MASK1(n,p) deleted -- creates a mask with 'n' 0 bits at position 'p' -- MASK0(n,p) deleted --[[-------------------------------------------------------------------- Visual representation for reference: 31 | | | 0 bit position +-----+-----+-----+----------+ | B | C | A | Opcode | iABC format +-----+-----+-----+----------+ - 9 - 9 - 8 - 6 - field sizes +-----+-----+-----+----------+ | [s]Bx | A | Opcode | iABx | iAsBx format +-----+-----+-----+----------+ ----------------------------------------------------------------------]] ------------------------------------------------------------------------ -- the following macros help to manipulate instructions -- * changed to a table object representation, very clean compared to -- the [nightmare] alternatives of using a number or a string ------------------------------------------------------------------------ -- these accept or return opcodes in the form of string names function luaP:GET_OPCODE(i) return self.ROpCode[i.OP] end function luaP:SET_OPCODE(i, o) i.OP = self.OpCode[o] end function luaP:GETARG_A(i) return i.A end function luaP:SETARG_A(i, u) i.A = u end function luaP:GETARG_B(i) return i.B end function luaP:SETARG_B(i, b) i.B = b end function luaP:GETARG_C(i) return i.C end function luaP:SETARG_C(i, b) i.C = b end function luaP:GETARG_Bx(i) return i.Bx end function luaP:SETARG_Bx(i, b) i.Bx = b end function luaP:GETARG_sBx(i) return i.Bx - self.MAXARG_sBx end function luaP:SETARG_sBx(i, b) i.Bx = b + self.MAXARG_sBx end function luaP:CREATE_ABC(o,a,b,c) return {OP = self.OpCode[o], A = a, B = b, C = c} end function luaP:CREATE_ABx(o,a,bc) return {OP = self.OpCode[o], A = a, Bx = bc} end ------------------------------------------------------------------------ -- Bit shuffling stuffs ------------------------------------------------------------------------ if false and pcall (require, 'bit') then ------------------------------------------------------------------------ -- Return a 4-char string little-endian encoded form of an instruction ------------------------------------------------------------------------ function luaP:Instruction(i) --FIXME end else ------------------------------------------------------------------------ -- Version without bit manipulation library. ------------------------------------------------------------------------ local p2 = {1,2,4,8,16,32,64,128,256, 512, 1024, 2048, 4096} -- keeps [n] bits from [x] local function keep (x, n) return x % p2[n+1] end -- shifts bits of [x] [n] places to the right local function srb (x,n) return math.floor (x / p2[n+1]) end -- shifts bits of [x] [n] places to the left local function slb (x,n) return x * p2[n+1] end ------------------------------------------------------------------------ -- Return a 4-char string little-endian encoded form of an instruction ------------------------------------------------------------------------ function luaP:Instruction(i) -- printf("Instr->string: %s %s", self.opnames[i.OP], table.tostring(i)) local c0, c1, c2, c3 -- change to OP/A/B/C format if needed if i.Bx then i.C = keep (i.Bx, 9); i.B = srb (i.Bx, 9) end -- c0 = 6B from opcode + 2LSB from A (flushed to MSB) c0 = i.OP + slb (keep (i.A, 2), 6) -- c1 = 6MSB from A + 2LSB from C (flushed to MSB) c1 = srb (i.A, 2) + slb (keep (i.C, 2), 6) -- c2 = 7MSB from C + 1LSB from B (flushed to MSB) c2 = srb (i.C, 2) + slb (keep (i.B, 1), 7) -- c3 = 8MSB from B c3 = srb (i.B, 1) --printf ("Instruction: %s %s", self.opnames[i.OP], tostringv (i)) --printf ("Bin encoding: %x %x %x %x", c0, c1, c2, c3) return string.char(c0, c1, c2, c3) end end ------------------------------------------------------------------------ -- decodes a 4-char little-endian string into an instruction struct ------------------------------------------------------------------------ function luaP:DecodeInst(x) error "Not implemented" end ------------------------------------------------------------------------ -- invalid register that fits in 8 bits ------------------------------------------------------------------------ luaP.NO_REG = luaP.MAXARG_A ------------------------------------------------------------------------ -- R(x) - register -- Kst(x) - constant (in constant table) -- RK(x) == if x < MAXSTACK then R(x) else Kst(x-MAXSTACK) ------------------------------------------------------------------------ ------------------------------------------------------------------------ -- grep "ORDER OP" if you change these enums ------------------------------------------------------------------------ --[[-------------------------------------------------------------------- Lua virtual machine opcodes (enum OpCode): ------------------------------------------------------------------------ name args description ------------------------------------------------------------------------ OP_MOVE A B R(A) := R(B) OP_LOADK A Bx R(A) := Kst(Bx) OP_LOADBOOL A B C R(A) := (Bool)B; if (C) PC++ OP_LOADNIL A B R(A) := ... := R(B) := nil OP_GETUPVAL A B R(A) := UpValue[B] OP_GETGLOBAL A Bx R(A) := Gbl[Kst(Bx)] OP_GETTABLE A B C R(A) := R(B)[RK(C)] OP_SETGLOBAL A Bx Gbl[Kst(Bx)] := R(A) OP_SETUPVAL A B UpValue[B] := R(A) OP_SETTABLE A B C R(A)[RK(B)] := RK(C) OP_NEWTABLE A B C R(A) := {} (size = B,C) OP_SELF A B C R(A+1) := R(B); R(A) := R(B)[RK(C)] OP_ADD A B C R(A) := RK(B) + RK(C) OP_SUB A B C R(A) := RK(B) - RK(C) OP_MUL A B C R(A) := RK(B) * RK(C) OP_DIV A B C R(A) := RK(B) / RK(C) OP_POW A B C R(A) := RK(B) ^ RK(C) OP_UNM A B R(A) := -R(B) OP_NOT A B R(A) := not R(B) OP_CONCAT A B C R(A) := R(B).. ... ..R(C) OP_JMP sBx PC += sBx OP_EQ A B C if ((RK(B) == RK(C)) ~= A) then pc++ OP_LT A B C if ((RK(B) < RK(C)) ~= A) then pc++ OP_LE A B C if ((RK(B) <= RK(C)) ~= A) then pc++ OP_TEST A B C if (R(B) <=> C) then R(A) := R(B) else pc++ OP_CALL A B C R(A), ... ,R(A+C-2) := R(A)(R(A+1), ... ,R(A+B-1)) OP_TAILCALL A B C return R(A)(R(A+1), ... ,R(A+B-1)) OP_RETURN A B return R(A), ... ,R(A+B-2) (see note) OP_FORLOOP A sBx R(A)+=R(A+2); if R(A) =) R(A) OP_CLOSURE A Bx R(A) := closure(KPROTO[Bx], R(A), ... ,R(A+n)) ----------------------------------------------------------------------]] luaP.opnames = {} -- opcode names luaP.OpCode = {} -- lookup name -> number luaP.ROpCode = {} -- lookup number -> name local i = 0 for v in string.gfind([[ MOVE -- 0 LOADK LOADBOOL LOADNIL GETUPVAL GETGLOBAL -- 5 GETTABLE SETGLOBAL SETUPVAL SETTABLE NEWTABLE -- 10 SELF ADD SUB MUL DIV -- 15 MOD POW UNM NOT LEN -- 20 CONCAT JMP EQ LT LE -- 25 TEST TESTSET CALL TAILCALL RETURN -- 30 FORLOOP FORPREP TFORLOOP SETLIST CLOSE -- 35 CLOSURE VARARG ]], "[%a]+") do local n = "OP_"..v luaP.opnames[i] = v luaP.OpCode[n] = i luaP.ROpCode[i] = n i = i + 1 end luaP.NUM_OPCODES = i --[[ =========================================================================== Notes: (1) In OP_CALL, if (B == 0) then B = top. C is the number of returns - 1, and can be 0: OP_CALL then sets 'top' to last_result+1, so next open instruction (OP_CALL, OP_RETURN, OP_SETLIST) may use 'top'. (2) In OP_RETURN, if (B == 0) then return up to 'top' (3) For comparisons, B specifies what conditions the test should accept. (4) All 'skips' (pc++) assume that next instruction is a jump (5) OP_SETLISTO is used when the last item in a table constructor is a function, so the number of elements set is up to top of stack =========================================================================== --]] ------------------------------------------------------------------------ -- masks for instruction properties ------------------------------------------------------------------------ -- was enum OpModeMask: luaP.OpModeBreg = 2 -- B is a register luaP.OpModeBrk = 3 -- B is a register/constant luaP.OpModeCrk = 4 -- C is a register/constant luaP.OpModesetA = 5 -- instruction set register A luaP.OpModeK = 6 -- Bx is a constant luaP.OpModeT = 1 -- operator is a test ------------------------------------------------------------------------ -- get opcode mode, e.g. "iABC" ------------------------------------------------------------------------ function luaP:getOpMode(m) --printv(m) --printv(self.OpCode[m]) --printv(self.opmodes [self.OpCode[m]+1]) return self.OpMode[tonumber(string.sub(self.opmodes[self.OpCode[m] + 1], 7, 7))] end ------------------------------------------------------------------------ -- test an instruction property flag -- * b is a string, e.g. "OpModeBreg" ------------------------------------------------------------------------ function luaP:testOpMode(m, b) return (string.sub(self.opmodes[self.OpCode[m] + 1], self[b], self[b]) == "1") end -- number of list items to accumulate before a SETLIST instruction -- (must be a power of 2) -- * used in lparser, lvm, ldebug, ltests luaP.LFIELDS_PER_FLUSH = 50 --FF updated to match 5.1 -- luaP_opnames[] is set above, as the luaP.opnames table -- opmode(t,b,bk,ck,sa,k,m) deleted --[[-------------------------------------------------------------------- Legend for luaP:opmodes: 1 T -> T (is a test?) 2 B -> B is a register 3 b -> B is an RK register/constant combination 4 C -> C is an RK register/constant combination 5 A -> register A is set by the opcode 6 K -> Bx is a constant 7 m -> 1 if iABC layout, 2 if iABx layout, 3 if iAsBx layout ----------------------------------------------------------------------]] luaP.opmodes = { -- TBbCAKm opcode "0100101", -- OP_MOVE 0 "0000112", -- OP_LOADK "0000101", -- OP_LOADBOOL "0100101", -- OP_LOADNIL "0000101", -- OP_GETUPVAL "0000112", -- OP_GETGLOBAL 5 "0101101", -- OP_GETTABLE "0000012", -- OP_SETGLOBAL "0000001", -- OP_SETUPVAL "0011001", -- OP_SETTABLE "0000101", -- OP_NEWTABLE 10 "0101101", -- OP_SELF "0011101", -- OP_ADD "0011101", -- OP_SUB "0011101", -- OP_MUL "0011101", -- OP_DIV 15 "0011101", -- OP_MOD "0011101", -- OP_POW "0100101", -- OP_UNM "0100101", -- OP_NOT "0100101", -- OP_LEN 20 "0101101", -- OP_CONCAT "0000003", -- OP_JMP "1011001", -- OP_EQ "1011001", -- OP_LT "1011001", -- OP_LE 25 "1000101", -- OP_TEST "1100101", -- OP_TESTSET "0000001", -- OP_CALL "0000001", -- OP_TAILCALL "0000001", -- OP_RETURN 30 "0000003", -- OP_FORLOOP "0000103", -- OP_FORPREP "1000101", -- OP_TFORLOOP "0000001", -- OP_SETLIST "0000001", -- OP_CLOSE 35 "0000102", -- OP_CLOSURE "0000101" -- OP_VARARG } return luaP