Bladeren bron

指令集

runningwater 1 jaar geleden
bovenliggende
commit
5bd195ef2f
7 gewijzigde bestanden met toevoegingen van 350 en 2 verwijderingen
  1. 95 0
      .gitignore
  2. 3 0
      binchunk/binary_chunk.go
  3. 2 1
      lua/foo_bar.lua
  4. 46 1
      main.go
  5. 56 0
      vm/instruction.go
  6. 138 0
      vm/opcodes.go
  7. 10 0
      vm/readme.md

+ 95 - 0
.gitignore

@@ -0,0 +1,95 @@
+### Go template
+# If you prefer the allow list template instead of the deny list, see community template:
+# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
+#
+# Binaries for programs and plugins
+*.exe
+*.exe~
+*.dll
+*.so
+*.dylib
+
+# Test binary, built with `go test -c`
+*.test
+
+# Output of the go coverage tool, specifically when used with LiteIDE
+*.out
+
+# Dependency directories (remove the comment below to include it)
+# vendor/
+
+# Go workspace file
+go.work
+
+### Lua template
+# Compiled Lua sources
+luac.out
+
+# luarocks build files
+*.src.rock
+*.zip
+*.tar.gz
+
+# Object files
+*.o
+*.os
+*.ko
+*.obj
+*.elf
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+*.def
+*.exp
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+
+### macOS template
+# General
+.DS_Store
+.AppleDouble
+.LSOverride
+
+# Icon must end with two \r
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear in the root of a volume
+.DocumentRevisions-V100
+.fseventsd
+.Spotlight-V100
+.TemporaryItems
+.Trashes
+.VolumeIcon.icns
+.com.apple.timemachine.donotpresent
+
+# Directories potentially created on remote AFP share
+.AppleDB
+.AppleDesktop
+Network Trash Folder
+Temporary Items
+.apdisk
+
+.idea

+ 3 - 0
binchunk/binary_chunk.go

@@ -2,6 +2,9 @@
 // Author: ynwdlxm@163.com
 // Date: 2024/4/5 15:52
 // Desc:
+//  luac -v
+// Lua 5.3.4  Copyright (C) 1994-2017 Lua.org, PUC-Rio
+//
 // (base) ➜  lua xxd -u -g 1 luac.out
 // 00000000: 1B 4C 75 61 53 00 19 93 0D 0A 1A 0A 04 08 04 08  .LuaS...........
 // 00000010: 08 78 56 00 00 00 00 00 00 00 00 00 00 00 28 77  .xV...........(w

+ 2 - 1
lua/foo_bar.lua

@@ -1,5 +1,6 @@
 function foo()
-    function bar() end
+    function bar()
+    end
 end
 
 function bar()

+ 46 - 1
main.go

@@ -8,6 +8,7 @@ package main
 import (
     "fmt"
     "luago/binchunk"
+    "luago/vm"
     "os"
 )
 
@@ -83,7 +84,51 @@ func printCode(f *binchunk.Prototype) {
             line = fmt.Sprintf("%d", f.LineInfo[pc])
         }
         // 序号、行号和 十六进制表示
-        fmt.Printf("\t%d\t[%s]\t0X%08X\n", pc+1, line, c)
+        // fmt.Printf("\t%d\t[%s]\t0X%08X\n", pc+1, line, c)
+        i := vm.Instruction(c)
+        fmt.Printf("\t%d\t[%s]\t%s \t", pc+1, line, i.OpName())
+        printOperands(i)
+        fmt.Printf("\n")
+    }
+}
+
+func printOperands(i vm.Instruction) {
+    switch i.OpMode() {
+    case vm.IABC:
+        a, b, c := i.ABC()
+        fmt.Printf("%d", a)
+        // 如果操作数 B 和 C 的最高位是 1, 就默认为它表示常量表索引,按负数输出
+        if i.BMode() != vm.OpArgN {
+            if b > 0XFF {
+                fmt.Printf(" %d", -1-b&0XFF)
+            } else {
+                fmt.Printf(" %d", b)
+            }
+        }
+        if i.CMode() != vm.OpArgN {
+            if c > 0XFF {
+                fmt.Printf(" %d", -1-c&0XFF)
+            } else {
+                fmt.Printf(" %d", c)
+            }
+        }
+    case vm.IABx:
+        a, bx := i.ABx()
+        fmt.Printf("%d", a)
+        if i.BMode() == vm.OpArgK {
+            fmt.Printf(" %d", -1-bx)
+        } else if i.BMode() == vm.OpArgU {
+            fmt.Printf(" %d", bx)
+        }
+    case vm.IAsBx:
+        a, sbx := i.AsBx()
+        fmt.Printf("%d %d", a, sbx)
+    case vm.IAx:
+        ax := i.Ax()
+        fmt.Printf("%d", -1-ax)
+    default:
+        panic("unhandled default case")
+
     }
 }
 

+ 56 - 0
vm/instruction.go

@@ -0,0 +1,56 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2024/4/14 15:44
+// Desc: 指令解码
+
+package vm
+
+const MAXARG_Bx = 1<<18 - 1 // 2^18 - 1 = 262143
+const MAXARG_sBx = MAXARG_Bx >> 1
+
+type Instruction uint32
+
+// Opcode 提取操作码,后 6 位
+func (i Instruction) Opcode() int {
+    return int(i & 0X3F)
+}
+
+// ABC 从 iABC 模式指令中提取参数
+func (i Instruction) ABC() (a, b, c int) {
+    a = int(i >> 6 & 0XFF)
+    b = int(i >> 14 & 0X1FF)
+    c = int(i >> 23 & 0x1FF)
+    return
+}
+
+// ABx 从 iABx 模式指令中提取参数
+func (i Instruction) ABx() (a, bx int) {
+    a = int(i >> 6 & 0XFF)
+    bx = int(i >> 14)
+    return
+}
+
+// AsBx 从 iAsBx 模式指令中提取参数
+func (i Instruction) AsBx() (a, sbx int) {
+    a, bx := i.ABx()
+    // sbx 有符号数
+    return a, bx - MAXARG_sBx
+}
+
+// Ax 从 iAx 模式指令中提取参数
+func (i Instruction) Ax() int {
+    return int(i >> 6)
+}
+
+func (i Instruction) OpName() string {
+    return opcodes[i.Opcode()].name
+}
+func (i Instruction) OpMode() byte {
+    return opcodes[i.Opcode()].opMode
+}
+func (i Instruction) BMode() byte {
+    return opcodes[i.Opcode()].argBMode
+}
+func (i Instruction) CMode() byte {
+    return opcodes[i.Opcode()].argCMode
+}

+ 138 - 0
vm/opcodes.go

@@ -0,0 +1,138 @@
+// Author: simon
+// Author: ynwdlxm@163.com
+// Date: 2024/4/13 22:53
+// Desc: 操作码 每条指令占4 个字节(32 比特),其中 6 比特用于操作码(Opcode),26 比特用于操作数(Operand)
+// * iABC 模式指令可以携带 A、B、C 三个操作数,分别占用 8、9、9 个比特
+// * iABx 模式指令可以携带 A 和 Bx 两个操作数,分别占用 8 和 18 个比特
+// * iAsBx 模式的指令可以携带 A 和 sBx 两个操作数,分别占用 8 和 18 个比特
+// * iAx 模式的指令中携带一个操作数,占用全部 26 比特
+// * 在 四种模式中,只用 iAsBx 模式下的 sBx 操作数会被解释成有符号整数,其他情况下操作数均被解释成无符号整数
+
+package vm
+
+// op MODE
+const (
+    IABC = iota
+    IABx
+    IAsBx
+    IAx
+)
+
+// Lua 5.3 共定义了 47 条指令,按照作用,
+// 可大致分为 常量加载指令、运算符相关指令、循环和跳转指令、函数调用相关指令、表操作指令以及 Upvalue 操作指令
+const (
+    MOVE = iota
+    LOADK
+    LOADKX
+
+    LOADBOOL
+    LOADNIL
+    GETUPVAL
+    GETTABUP
+    GETTABLE
+    SETTABUP
+    SETUPVAL
+    SETTABLE
+    NEWTABLE
+    SELF
+    ADD
+    SUB
+    MUL
+    MOD
+    POW
+    DIV
+    IDIV
+    BAND
+    BOR
+    BXOR
+    SHL
+    SHR
+    UNM
+    BNOT
+    NOT
+    LEN
+    CONCAT
+    JMP
+    EQ
+    LT
+    LE
+    TEST
+    TESTSET
+    CALL
+    TALLCALL
+    RETURN
+    FORLOOP
+    FORPREP
+    TFORCALL
+    TFORLOOP
+    SETLIST
+    CLOSURE
+    VARARG
+    EXTRAARG
+)
+
+// 操作数
+const (
+    OpArgN = iota // argument is not used
+    OpArgU        // argument is used
+    OpArgR        // argument is a register or a jump offset
+    OpArgK        // argument is a constant or register/constant
+)
+
+type opcode struct {
+    testFlag byte // operator is a test (next instruction must be a jump)
+    setAFlag byte // instruction set register A
+    argBMode byte // B arg mode
+    argCMode byte // C arg mode
+    opMode   byte // op mode
+    name     string
+}
+
+var opcodes = []opcode{
+    /*testFlag     setAFlag     argBMode          argCMode           opMode        name        */
+    {0, 1, OpArgR, OpArgN, IABC, "MOVE     "},
+    {0, 1, OpArgK, OpArgN, IABx, "LOADK    "},
+    {0, 1, OpArgN, OpArgN, IABx, "LOADKX   "},
+    {0, 1, OpArgU, OpArgU, IABC, "LOADBOOL "},
+    {0, 1, OpArgU, OpArgN, IABC, "LOADNIl  "},
+    {0, 1, OpArgU, OpArgN, IABC, "GETUPVAL "},
+    {0, 1, OpArgU, OpArgK, IABC, "GETTABUP "},
+    {0, 1, OpArgR, OpArgK, IABC, "GETTABLE "},
+    {0, 0, OpArgK, OpArgK, IABC, "GETTABUP "},
+    {0, 0, OpArgU, OpArgN, IABC, "SETUPVAL "},
+    {0, 0, OpArgK, OpArgK, IABC, "SETTABLE "},
+    {0, 1, OpArgU, OpArgU, IABC, "NEWTABLE "},
+    {0, 1, OpArgR, OpArgK, IABC, "SELF     "},
+    {0, 1, OpArgK, OpArgK, IABC, "ADD      "},
+    {0, 1, OpArgK, OpArgK, IABC, "SUB      "},
+    {0, 1, OpArgK, OpArgK, IABC, "MUL      "},
+    {0, 1, OpArgK, OpArgK, IABC, "MOD      "},
+    {0, 1, OpArgK, OpArgK, IABC, "POW      "},
+    {0, 1, OpArgK, OpArgK, IABC, "DIV      "},
+    {0, 1, OpArgK, OpArgK, IABC, "IDIV     "},
+    {0, 1, OpArgK, OpArgK, IABC, "BAND     "},
+    {0, 1, OpArgK, OpArgK, IABC, "BOR      "},
+    {0, 1, OpArgK, OpArgK, IABC, "BXOR     "},
+    {0, 1, OpArgK, OpArgK, IABC, "SHL      "},
+    {0, 1, OpArgK, OpArgK, IABC, "SHR      "},
+    {0, 1, OpArgR, OpArgN, IABC, "UNM      "},
+    {0, 1, OpArgR, OpArgN, IABC, "BNOT     "},
+    {0, 1, OpArgR, OpArgN, IABC, "NOT      "},
+    {0, 1, OpArgR, OpArgN, IABC, "LEN      "},
+    {0, 1, OpArgR, OpArgR, IABC, "CONCAT   "},
+    {0, 0, OpArgR, OpArgN, IAsBx, "JMP     "},
+    {1, 0, OpArgK, OpArgK, IABC, "EQ       "},
+    {1, 0, OpArgK, OpArgK, IABC, "LT       "},
+    {1, 0, OpArgK, OpArgK, IABC, "LE       "},
+    {1, 0, OpArgN, OpArgU, IABC, "TEST     "},
+    {1, 1, OpArgR, OpArgU, IABC, "TESTSET  "},
+    {0, 1, OpArgU, OpArgU, IABC, "CALL     "},
+    {0, 1, OpArgU, OpArgU, IABC, "TALLCALL "},
+    {0, 0, OpArgU, OpArgN, IABC, "RETURN   "},
+    {0, 1, OpArgR, OpArgN, IAsBx, "FORLOOP "},
+    {0, 1, OpArgR, OpArgN, IAsBx, "FORREEP "},
+    {0, 0, OpArgU, OpArgU, IABC, "SETLIST  "},
+    {0, 1, OpArgU, OpArgN, IABx, "CLOSURE  "},
+    {0, 1, OpArgU, OpArgN, IABC, "VARARG   "},
+    {0, 0, OpArgU, OpArgU, IAx, "EXTRAARG  "},
+}

+ 10 - 0
vm/readme.md

@@ -0,0 +1,10 @@
+* Lua 虚拟机基于寄存器实现
+* Lua 虚拟机采用**定长指令集**, 每条指令占4 个字节(32 比特),其中 6 比特用于操作码(Opcode),26 比特用于操作数(Operand)
+* Lua 5.3 共定义了 47 条指令,按照作用,可大致分为 常量加载指令、运算符相关指令、循环和跳转指令、函数调用相关指令、表操作指令以及 Upvalue 操作指令
+* 每条 Lua 虚拟机指令占 4 个字节,其中低 6 比特用于**操作码**,高 26 比特用于操作数
+* 按照高 26 个比特的分配方式, Lua 虚拟机可以分为四类,分别对应四种编码模式(Mode): iABC、iABx、iAsBx、iAx
+* iABC 模式指令可以携带 A、B、C 三个操作数,分别占用 8、9、9 个比特
+* iABx 模式指令可以携带 A 和 Bx 两个操作数,分别占用 8 和 18 个比特
+* iAsBx 模式的指令可以携带 A 和 sBx 两个操作数,分别占用 8 和 18 个比特
+* iAx 模式的指令中携带一个操作数,占用全部 26 比特
+* 在 四种模式中,只用 iAsBx 模式下的 sBx 操作数会被解释成有符号整数,其他情况下操作数均被解释成无符号整数