Posted on 2022-09-12 05:54:12 default
下面的内容分为两部分,第一部分介绍了Move虚拟机的结构: 作为一个栈式虚拟机,它的操作数栈、调用栈。另外还介绍了Move虚拟机中,虚拟机提供了数据结构来支持函数调用和返回。
第二部分介绍了Move虚拟机中比较关键的字节码指令的作用。
要彻底理解Move虚拟机字节码的指令工作原理,还需要理解Move语言编译器的关键编译过程。
例如:编译器何时生成MoveLoc指令,何时生成CopyLoc指令,解引用是用哪条指令实现的,给结构体的字段赋值是哪条指令实现的,借用检查发生在编译的哪个阶段,move_to和move_from如何实现,为什么Move没有全局变量。这些问题,需要从编译阶段入手,了解编译的大致过程,结合虚拟机的功能,做整体分析。
解释器:
pub(crate) struct Interpreter {
/// 操作数栈,Move虚拟机中的Value保存在这里并用于堆栈操作
operand_stack: Stack,
/// 活动函数的调用栈
call_stack: CallStack,
}
Interpreter
结构体中,有一个操作数栈,它的作用是所有的算术、关系、比较、copy、move、pack、unpack
等等除了调用native函数和普通函数之外的所有操作符,都使用operand_stack
作为操作数栈。
call_stack
专门用作调用普通函数或native函数的调用栈。
所以我们知道 Move 的虚拟机,是一个栈式的虚拟机,有三点可以证明:
mem_set、mem_get
类的指令,这类指令都接受内存地址作为操作数,自然它也没有内存的概念(例如wasm虚拟机就有内存的概念,它的内存是用一个连续的平坦的字节数组来模拟);operand_stack
,而不是使用寄存器来做运算;locals
数组,用来保存局部变量,而不像寄存器类机器一样,把局部变量保存在内存中栈的区域。下面的 Frame
结构体,就代表了一个函数执行时的栈桢:
pc
是当前执行的指令序号locals
保存函数的局部变量的数组struct Frame {
pc: u16,
locals: Locals,
function: Arc<Function>,
ty_args: Vec<Type>,
}
多个函数之间嵌套调用时,在 Interpreter
结构体的 call_stack
中,保存了多个 Frame
结构体,可以看到 CallStack
类型如下:
struct CallStack(Vec<Frame>);
并且 CallStack
类型有如下函数:
impl CallStack {
/// Create a new empty call stack.
fn new() -> Self {
CallStack(vec![])
}
/// Push a `Frame` on the call stack.
fn push(&mut self, frame: Frame) -> ::std::result::Result<(), Frame> {
if self.0.len() < CALL_STACK_SIZE_LIMIT {
self.0.push(frame);
Ok(())
} else {
Err(frame)
}
}
/// Pop a `Frame` off the call stack.
fn pop(&mut self) -> Option<Frame> {
self.0.pop()
}
fn current_location(&self) -> Location {
let location_opt = self.0.last().map(|frame| frame.location());
location_opt.unwrap_or(Location::Undefined)
}
}
上面的函数 push、pop
是用来在函数调用进入和退出的时候,保存和取消函数栈帧 Frame
的函数。
下面是所有虚拟机支持的字节码类型:
/// 弹出并丢弃在栈顶的值。
/// 栈顶的值必须是一个可以拷贝的类型。
Pop,
/// 从函数返回,根据函数签名中的返回类型可能带有返回值。
/// 返回值被push到栈上。
/// 已执行的函数的函数签名定义了 Ret 操作码的语义。
Ret,
/// 如果栈顶的值是 true,跳转到 CodeOffset 位置处的指令。
/// Code offsets 是想对于指令流的开始来说的。
BrTrue(CodeOffset),
/// 如果栈顶的值是 false,跳转到 CodeOffset 位置处的指令。
/// Code offsets 是想对于指令流的开始来说的。
BrFalse(CodeOffset),
/// 无条件跳转到 CodeOffset 处的指令。
/// Code offsets 是想对于指令流的开始来说的。
Branch(CodeOffset),
/// 将一个 U8 的常量 push 到栈上。
LdU8(u8),
/// 将一个 U64 的常量 push 到栈上。
LdU64(u64),
/// 将一个 U128 的常量 push 到栈上。
LdU128(u128),
/// 将栈顶的值转换为 U8
CastU8,
/// 将栈顶的值转换为 U64
CastU64,
/// 将栈顶的值转换为 U128
CastU128,
/// push一个常量到栈上。
/// 该值通过 "常量池索引" 从 "常量池" 加载和反序列化(根据其类型)
LdConst(ConstantPoolIndex),
/// push "true" 到栈上
LdTrue,
/// push "false" 到栈上
LdFalse,
/// 将 `LocalIndex` 标识的 局部变量 推送到堆栈上。
/// 值会被拷贝并且 局部变量 依然可以安全的使用。
CopyLoc(LocalIndex),
/// 将 `LocalIndex` 标识的 局部变量 推送到堆栈上。
/// 局部变量 被移动并且从那时起使用无效,除非 store 操作在对该 局部变量 进行任何读取之前写入到另外的 局部变量。
MoveLoc(LocalIndex),
/// 从栈顶弹出一个值并且将它存储到由 LocalIndex 指定的函数 locals 数组中。
StLoc(LocalIndex),
/// 调用一个函数。
/// 栈上已经存在调用参数,参数是从第一个到最后一个push到栈中的。
/// 接着参数从栈中被消耗,并且push到函数的locals即局部变量数组中。
/// 函数的返回值放在栈上,并且对调用者可用。
Call(FunctionHandleIndex),
CallGeneric(FunctionInstantiationIndex),
/// 创建一个由 `StructHandleIndex` 指定的类型的实例,并且将这个实例 push 到栈上。
/// 结构体所有字段的值,必须以它们在结构体中出现的顺序,依次的 push 栈上。
/// Pack指令必须完整的初始化结构体的实例,意味着如有字段是结构体,则嵌套初始化。
/// 结构体实例,在栈上的类型是 Struct 类型,Struct 类型相当与把 栈上的多个元素打包了。
Pack(StructDefinitionIndex),
PackGeneric(StructDefInstantiationIndex),
/// 将栈上打包的Struct类型中的items字段,解包后取出所有字段,并放在栈上
Unpack(StructDefinitionIndex),
UnpackGeneric(StructDefInstantiationIndex),
/// 读取一个引用。引用在栈上,它会被消耗并且读取后的值也会放在栈上。
/// 读取一个引用会读取一个被引用对象的拷贝。
/// 如此,ReadRef 要求被读取的值有Copy的特性。
ReadRef,
/// 将栈上已经存在的Value写入到引用的Value中。
/// WriteRef 要求值的类型具有 `Drop` 能力,因为之前的值丢失了
WriteRef,
/// 将一个可变的的引用转变为一个不可变引用
FreezeRef,
/// 将一个由 LocalIndex 指定的局部变量的可变引用放在栈上
/// 局部变量不能是一个引用
MutBorrowLoc(LocalIndex),
/// 将一个由 LocalIndex 指定的局部变量的不可变引用放在栈上
/// 局部变量不能是一个引用
ImmBorrowLoc(LocalIndex),
/// 将一个由 FieldHandleIndex 指定的字段的可变引用放在栈上
/// 栈顶必须已经存在一个包含了该字段的容器类型的引用
MutBorrowField(FieldHandleIndex),
/// 将一个由 FieldInstantiationIndex 指定的字段的可变引用放在栈上
/// 栈顶必须已经存在一个包含了该字段的容器类型的可变引用
MutBorrowFieldGeneric(FieldInstantiationIndex),
/// 将一个由 FieldHandleIndex 指定的字段的引用放在栈上
/// 栈顶必须已经存在一个包含了该字段的容器类型的引用
ImmBorrowField(FieldHandleIndex),
/// 将一个由 FieldInstantiationIndex 指定的字段的可变引用放在栈上
/// 栈顶必须已经存在一个包含了该字段的容器类型的可变引用
ImmBorrowFieldGeneric(FieldInstantiationIndex),
/// 返回对在作为参数传递的 地址 处发布的类型为 "StructDefinitionIndex" 的实例的可变引用。
/// 如果这样的对象不存在或引用已被分发,则中止执行。
/// address地址Value必须已经在栈上存在
MutBorrowGlobal(StructDefinitionIndex),
MutBorrowGlobalGeneric(StructDefInstantiationIndex),
/// 返回对在作为参数传递的 地址 处发布的类型为 "StructDefinitionIndex" 的实例的不可变引用。
/// 如果这样的对象不存在或引用已被分发,则中止执行。
/// address地址Value必须已经在栈上存在
ImmBorrowGlobal(StructDefinitionIndex),
ImmBorrowGlobalGeneric(StructDefInstantiationIndex),
Add,
Sub,
Mul,
Mod,
Div,
BitOr,
BitAnd,
Xor,
Or,
And,
Not,
Eq,
Neq,
Lt,
Gt,
Le,
Ge,
Abort,
Nop,
/// 返回一个地址是否已经 publish 一个 StructDefinitionIndex 类型的对象
Exists(StructDefinitionIndex),
ExistsGeneric(StructDefInstantiationIndex),
/// 移动一个由 栈定的地址指定的 StructDefinitionIndex 类型的实例
/// 如果那个对象不存在就终止执行
MoveFrom(StructDefinitionIndex),
MoveFromGeneric(StructDefInstantiationIndex),
/// 将栈顶的Value实例移动到紧挨着栈顶元素的 `Signer` 的地址上
/// 如果 StructDefinitionIndex 类型的对象,已经在地址上存在,就停止执行
MoveTo(StructDefinitionIndex),
MoveToGeneric(StructDefInstantiationIndex),
Shl,
Shr,
VecPack(SignatureIndex, u64),
VecLen(SignatureIndex),
VecImmBorrow(SignatureIndex),
VecMutBorrow(SignatureIndex),
VecPushBack(SignatureIndex),
VecPopBack(SignatureIndex),
VecUnpack(SignatureIndex, u64),
VecSwap(SignatureIndex),
Quality posts is the crucial to invite the visitors to visit the web page, that’s what this web page is providing. OKBet download
Live casino games offer a practical gaming experience similar to a land-based casino!!! Want to know more about the site? visit here —>> Online casino