WebAssembly原理与核心技术
上QQ阅读APP看书,第一时间看更新

3.3 指令解码

在第2章我们实现的二进制模块解码器还缺少一个最重要的部分:指令和表达式解码逻辑。具体来说,全局项的初始值表达式、元素和数据项的偏移量表达式、代码项的字节码还没有处理。下面是这4项以及表达式的编码格式。


global: global_type|init_expr
elem  : table_idx|offset_expr|vec<func_idx>
data  : mem_idx|offset_expr|vec<byte>
code  : byte_count|vec<locals>|expr
expr  : instr*|0x0b

这一节我们为解码器补上指令解码逻辑。以代码项为例,下面是改动之后的解码方法(省略了错误处理代码)。


func (reader *wasmReader) readCode(idx int) Code {
    return Code{
        Locals: reader.readLocalsVec(),
        Expr:   reader.readExpr(), // 新增代码
    }
}

由于全局项、元素项和数据项中的表达式更为简单(详见第11章),所以原来的代码不需要大改,只要把readExpr()方法重新实现就可以了。下面是重新定义后的Expr类型,以及调整后的readExpr()方法。


type Expr = []Instruction

func (reader *WasmReader) readExpr() Expr {
    instrs, end := reader.readInstructions()
    if end != End_ {
        panic(fmt.Errorf("invalid expr end: %d", end))
    }
    return instrs
}

上面代码的重点是确保表达式以end指令结尾,真正的解码工作由readInstructions()方法完成。该方法读取并收集指令,直到遇到else或者end指令,代码如下所示。


func (reader *wasmReader) readInstructions() (instrs []Instruction, end byte) {
    for {
        instr := reader.readInstruction()
        if instr.Opcode == Else_ || instr.Opcode == End_ {
            end = instr.Opcode
            return
        }
        instrs = append(instrs, instr)
    }
}

单条指令的解码逻辑很简单,先读取操作码,然后根据操作码读取立即数,代码如下所示。


func (reader *wasmReader) readInstruction() (instr Instruction) {
    instr.Opcode = reader.readByte()
    instr.Args = reader.readArgs(instr.Opcode)
    return
}

立即数解码逻辑是一个大switch-case语句,部分代码如下所示(完整代码在binary/reader.go文件)。


func (reader *WasmReader) readArgs(opcode byte) interface{} {
    switch opcode {
    case Block:            return reader.readBlockArgs()
    case Loop:             return reader.readBlockArgs()
    case If:               return reader.readIfArgs()
    case Br, BrIf:         return reader.readVarU32()           
    case BrTable:          return reader.readBrTableArgs()
    case Call:             return reader.readVarU32()           
    case CallIndirect:     return reader.readCallIndirectArgs()
    case LocalGet|Set|Tee: return reader.readVarU32()           
    case GlobalGet|Set:    return reader.readVarU32()           
    case MemorySize|Grow:  return reader.readZero()
    case I32Const:         return reader.readVarS32()
    case I64Const:         return reader.readVarS64()
    case F32Const:         return reader.readF32()
    case F64Const:         return reader.readF64()
    case TruncSat:         return reader.readByte()
    case I32Load|...:      return reader.readMemArg()
    default:               return nil
    }
}

立即数解码方法比较简单,这里我们以if指令的立即数解码方法为例,下面是readIfArgs()方法代码。


func (reader *wasmReader) readIfArgs() (args IfArgs) {
    var end byte
    args.BT = reader.readBlockType()
    args.Instrs1, end = reader.readInstructions()
    if end == Else_ {
        args.Instrs2, end = reader.readInstructions()
        if end != End_ {
            panic(fmt.Errorf("invalid block end: %d", end))
        }
    }
    return
}

注意看readIfArgs()和readInstructions()方法是如何相互递归调用的。块类型解码方法也比较简单,直接解码LEB128有符号数(并进行检查)即可,代码如下所示。


const (
    BlockTypeI32   BlockType = -1  // ()->(i32)
    BlockTypeI64   BlockType = -2  // ()->(i64)
    BlockTypeF32   BlockType = -3  // ()->(f32)
    BlockTypeF64   BlockType = -4  // ()->(f64)
    BlockTypeEmpty BlockType = -64 // ()->()
)

func (reader *wasmReader) readBlockType() int32 {
    bt := reader.readVarS32()
    if bt < 0 {
        switch bt {
        case BlockTypeI32, BlockTypeI64, BlockTypeF32, BlockTypeF64,
            BlockTypeEmpty:
        default:
            panic(fmt.Errorf("malformed block type: %d", bt))
        }
    }
    return bt
}

为了便于使用,我们给Module结构体添加一个方法,把块类型转换成相应的函数类型,代码如下所示。


func (module Module) GetBlockType(bt BlockType) FuncType {
    switch bt {
    case BlockTypeI32:   return FuncType{ResultTypes: []ValType{ValTypeI32}}
    case BlockTypeI64:   return FuncType{ResultTypes: []ValType{ValTypeI64}}
    case BlockTypeF32:   return FuncType{ResultTypes: []ValType{ValTypeF32}}
    case BlockTypeF64:   return FuncType{ResultTypes: []ValType{ValTypeF64}}
    case BlockTypeEmpty: return FuncType{}
    default:             return module.TypeSec[bt]
    }
}