资讯详情

luajit开发文档wiki中文版(四) LuaJIT 内部结构

2022年6月10日15:15:22

luajit开发文档中文版(一)下载和安装

luajit中文版开发文档(二)LuaJIT扩展

luajit中文版开发文档(3)FAQ 常见问题

luajit开发文档wiki中文版(一) 总目录

luajit开发文档wiki中文版(二) LuaJIT 扩展

luajit开发文档wiki中文版(3)性能调整和测试

luajit开发文档wiki中文版(4) LuaJIT 内部结构

luajit开发文档wiki中文版(五) 系统集成

luajit开发文档wiki中文版(六) LuaJIT 开发

介绍

描述了以下文档 LuaJIT 2.0 字节码指令。详情请参考src/lj_bc.hLuaJIT 源代码。可以使用字节码。 列出luajit -bl,参见-b 选项。

单字节码指令为 32 位宽,具有 8 位操作码字段和多个 8 位或 16 位操作数字段。指令采用以下两种格式之一:

B C A OP
D
A OP

这张照片显示了右侧的最低有效。内存指令总是按照主机字节的顺序存储。例如,0xbbccaa1e 是操作码为 0x1e (ADDVV) 指令,操作数 A = 0xaa、B = 0xbb 和 C = 0xcc。

指令名称的后缀区分同一基本指令的变体:

  • V可变槽
  • S 字符串常量
  • N数常数
  • P 原始类型
  • B 无符号字节文字
  • M 多参数/结果

以下是可能的操作数类型:

  • (无):未使用的操作数
  • var: 可变槽号
  • dst:可变槽号,用作目的地
  • base:基本槽号,可读写
  • rbase:只读基本槽号
  • uv:上值数
  • 点燃:字面意思
  • lits:有符号的文字
  • pri:原始类型(0 = nil,1 = false,2 = true)
  • num:数字常量,索引到常量表
  • str:字符串常量,常量表的否定索引
  • tab:模板表,否定索引到常量表
  • func:函数原型,常量表的否定索引
  • cdata:cdata 常量,常量表的负索引
  • jump:与下一个指令相比,分支目标偏置为 0x8000

比较操作

所有的比较和测试操作都紧跟在一起JMP指令结束后,指令包含条件跳转的目标。如果比较或测试是真实的,所有的比较和测试都将跳转到目标。否则,他们将通过JMP.

OP
A
D
Description
ISLT var var Jump if A < D
ISGE var var Jump if A ≥ D
ISLE var var Jump if A ≤ D
ISGT var var Jump if A > D
ISEQV var var Jump if A = D
ISNEV var var Jump if A ≠ D
ISEQS var str Jump if A = D
ISNES var str Jump if A ≠ D
ISEQN var num Jump if A = D
ISNEN var num Jump if A ≠ D
ISEQP var pri Jump if A = D
ISNEP var 一元测试和复制操作

这些指令测试一个变量在布尔上下文中的计算结果是真还是假。仅在 Lua中nil并被false认为是假的,所有其他值都是真。这些指令是为简单的真实性测试而生成的,例如if x then在评估andandor 运算符时。

OP
A
D
Description
ISTC dst var Copy D to A and jump, if D is true
ISFC dst var Copy D to A and jump, if D is false
IST   var Jump if D is true
ISF   var Jump if D is false

:我们需要测试和复制操作做什么?

:在 Lua 中,andandor运算符返回其操作数之一的原始值。通常只有在解析完整表达式后才知道结果是否未被使用。在这种情况下,测试和复制操作可以很容易地转换为先前发出的字节码中的测试操作。

一元操作

OP
A
D
Description
MOV dst var Copy D to A
NOT dst var Set A to boolean not of D
UNM dst var Set A to -D (unary minus)
LEN dst var Set A to #D (object length)

Binary ops

OP
A
B
C
Description
ADDVN dst var num A = B + C
SUBVN dst var num A = B - C
MULVN dst var num A = B * C
DIVVN dst var num A = B / C
MODVN dst var num A = B % C
ADDNV dst var num A = C + B
SUBNV dst var num A = C - B
MULNV dst var num A = C * B
DIVNV dst var num A = C / B
MODNV dst var num A = C % B
ADDVV dst var var A = B + C
SUBVV dst var var A = B - C
MULVV dst var var A = B * C
DIVVV dst var var A = B / C
MODVV dst var var A = B % C
POW dst var var A = B ^ C
CAT dst rbase rbase A = B .. ~ .. C

注意:该CAT指令将变量槽 B 到 C 中的所有值连接起来。

Constant ops

OP
A
D
Description
KSTR dst str Set A to string constant D
KCDATA dst cdata Set A to cdata constant D
KSHORT dst lits Set A to 16 bit signed integer D
KNUM dst num Set A to number constant D
KPRI dst pri Set A to primitive D
KNIL base base Set slots A to D to nil

注意:使用 设置单个nilKPRIKNIL仅在需要将多个值设置为 时使用nil

Upvalue and Function ops

OP
A
D
Description
UGET dst uv Set A to upvalue D
USETV uv var Set upvalue A to D
USETS uv str Set upvalue A to string constant D
USETN uv num Set upvalue A to number constant D
USETP uv pri Set upvalue A to primitive D
UCLO rbase jump Close upvalues for slots ≥ rbase and jump to target D
FNEW dst func Create new closure from prototype D and store it in A

:为什么UCLO会有跳跃目标?

A :UCLO通常是块中的最后一条指令,通常后面跟一个JMP. 将跳转合并到UCLO加速执行并简化一些字节码修复步骤(参见 参考资料fs_fixup_ret()src/lj_parse.c。非分支UCLO只是跳转到下一条指令。

Table ops

OP
A
B
C/D
Description
TNEW dst   lit Set A to new table with size D (see below)
TDUP dst   tab Set A to duplicated template table D
GGET dst   str A = _G[D]
GSET var   str _G[D] = A
TGETV dst var var A = B[C]
TGETS dst var str A = B[C]
TGETB dst var lit A = B[C]
TSETV var var var B[C] = A
TSETS var var str B[C] = A
TSETB var var lit B[C] = A
TSETM base   num* (A-1)[D], (A-1)[D+1], ... = A, A+1, ...

备注

  • 16 位文字 D 操作数TNEW分为两个字段:最低 11 位给出数组大小(分配插槽 0..asize-1,如果为零,则不分配)。高 5 位将哈希大小作为 2 的幂(分配 2^hsize 哈希槽,如果为零,则不分配)。
  • GGET并被GSET命名为“全局”获取和设置,但实际上索引当前函数环境getfenv(1)(通常与 相同_G)。
  • TGETB并将TSETB8 位文字 C 操作数解释为表 B 中的无符号整数索引 (0..255)。
  • 操作数 DTSETM指向常量表中的偏置浮点数。只有尾数的最低 32 位用作起始表索引。来自前一个字节码的 MULTRES 给出了要填充的表槽数。

调用和可变参数处理

所有调用指令都需要一个特殊的设置:要调用的函数(或对象)位于插槽 A 中,后面是连续插槽中的参数。操作数 C 是固定参数数量的加一。操作数 B 是返回值数量的一加,或者对于返回所有结果的调用为零(并相应地设置 MULTRES)。

带有多个参数(CALLMCALLMT)的调用的操作数 C 设置为固定参数的数量。MULTRES 添加到其中以获取要传递的实际参数数量。

为了保持一致性,专用调用指令ITERCITERN可变参数指令VARG共享相同的操作数格式。ITERCand的操作数 CITERN始终为 3 = 1+2,即两个参数被传递给迭代器函数。操作数 C ofVARG重新用于保存封闭函数的固定参数的数量。这加快了对下面 vararg 伪帧的可变参数部分的访问。

MULTRES 是一个内部变量,用于跟踪上一次调用或VARG具有多个结果的指令返回的结果数量。它被多个参数的调用(CALLMCALLMT)或返回(RETM)以及表初始化程序(TSETM)使用。

OP
A
B
C/D
Description
CALLM base lit lit Call: A, ..., A+B-2 = A(A+1, ..., A+C+MULTRES)
CALL base lit lit Call: A, ..., A+B-2 = A(A+1, ..., A+C-1)
CALLMT base   lit Tailcall: return A(A+1, ..., A+D+MULTRES)
CALLT base   lit Tailcall: return A(A+1, ..., A+D-1)
ITERC base lit lit Call iterator: A, A+1, A+2 = A-3, A-2, A-1; A, ..., A+B-2 = A(A+1, A+2)
ITERN base lit lit Specialized ITERC, if iterator function A-3 is next()
VARG base lit lit Vararg: A, ..., A+B-2 = ...
ISNEXT base   jump Verify ITERN specialization and jump

注意:Lua 解析器启发式地确定是否pairs()或 next()可能在循环中使用。在这种情况下,JMP和 迭代器调用ITERC被替换为专用版本 ISNEXTITERN

ISNEXT在运行时验证迭代器实际上是next() 函数,参数是表,控制变量是 nil. 然后它将控制变量的槽的最低 32 位设置为零并跳转到迭代器调用,它使用这个数字有效地逐步遍历表的键。

如果任何假设被证明是错误的,则字节码在运行时将被反专门化为JMPand ITERC

 

Returns

所有返回指令都将从槽 A 开始的结果复制到从基本槽(保存帧链接和当前执行函数的槽)下一个开始的槽。

RET0指令RET1只是RET. 操作数 D 是要返回的结果数的一加。

对于RETM,操作数 D 保存要返回的固定结果的数量。将 MULTRES 添加到其中以获取要返回的实际结果数。

OP
A
D
Description
RETM base lit return A, ..., A+D+MULTRES-1
RET rbase lit return A, ..., A+D-2
RET0 rbase lit return
RET1 rbase lit return A

循环和分支

Lua 语言提供了四种循环类型,它们被翻译成不同的字节码指令:

  • 数字“for”循环:for i=start,stop,step do body end=> 设置开始、停止、步骤FORI主体FORL
  • 迭代器'for'循环:for vars... in iter,state,ctl do body end=> set iter,state,ctl JMPbodyITERC ITERL
  • 'while' 循环:while cond do body end=> inverse-cond- JMP LOOPbodyJMP
  • “重复”循环:repeat body until cond=>LOOP身体条件-JMP

breakandgoto语句被翻译成无条件的 orJMP指令UCLO

OP
A
D
Description
FORI base jump Numeric 'for' loop init
JFORI base jump Numeric 'for' loop init, JIT-compiled
FORL base jump Numeric 'for' loop
IFORL base jump Numeric 'for' loop, force interpreter
JFORL base lit Numeric 'for' loop, JIT-compiled
ITERL base jump Iterator 'for' loop
IITERL base jump Iterator 'for' loop, force interpreter
JITERL base lit Iterator 'for' loop, JIT-compiled
LOOP rbase jump Generic loop
ILOOP rbase jump Generic loop, force interpreter
JLOOP rbase lit Generic loop, JIT-compiled
JMP rbase jump Jump

操作数 A 保存指令的第一个未使用的槽、JMP指令的循环控制变量的基本槽*FOR*(idxstopstepext idx)或指令迭代器返回结果的基*ITERL(存储在下面是 funcstatectl)。

JFORL和指令将跟踪JITERLJLOOP存储在操作数 D 中(JFORI从对应的 中检索JFORL)。否则,操作数 D 指向循环后的第一条指令。

和指令FORL进行热点检测。如果循环执行得足够频繁,就会触发跟踪记录。ITERLLOOP

IFORLJIT 编译器使用IITERLILOOP指令将无法编译的循环列入黑名单。他们不在解释器中进行热点检测和强制执行。

如果循环进入条件为真JFORI,则JFORLJITERL和指令进入 JIT 编译的跟踪。JLOOP

*FORL指令idx = idx + step先做。所有*FOR* 指令都会检查idx <= stop(if step >= 0) 或idx >= stop (if step < 0)。如果为真,idx则复制到ext idx槽(循环体中可见的循环变量)。然后输入循环体或 JIT 编译的跟踪。否则,通过继续执行 . 之后的下一条指令来离开循环*FORL

*ITERL指令检查槽 A 中迭代器返回的第一个结果是否为非nil. 如果为真,则将此值复制到插槽 A-1 并输入循环体或 JIT 编译的跟踪。

这些*LOOP指令实际上是无操作的(热点检测除外)并且不分支。操作数 A 和 D 仅由 JIT 编译器用于加速数据流和控制流分析。字节码指令本身是必需的,因此 JIT 编译器可以对其进行修补以输入循环的 JIT 编译跟踪。

Function headers

OP
A
D
Description
FUNCF rbase   Fixed-arg Lua function
IFUNCF rbase   Fixed-arg Lua function, force interpreter
JFUNCF rbase lit Fixed-arg Lua function, JIT-compiled
FUNCV rbase   Vararg Lua function
IFUNCV rbase   Vararg Lua function, force interpreter
JFUNCV rbase lit Vararg Lua function, JIT-compiled
FUNCC rbase   Pseudo-header for C functions
FUNCCW rbase   Pseudo-header for wrapped C functions
FUNC* rbase   Pseudo-header for fast functions

操作数 A 保存函数的帧大小。操作数 D 保存 和 的跟踪JFUNCFJFUNCV

对于 Lua 函数,省略的固定参数被设置为nil,多余的参数被忽略。Vararg 函数设置涉及创建一个特殊的 vararg 框架,该框架包含固定参数之外的参数。固定的参数被复制到一个常规的 Lua 函数框架,并且它们在可变参数框架中的槽被设置为nil.

和指令为固定参数或可变参数 Lua 函数设置框架并进行热点检测FUNCFFUNCV如果函数执行得足够频繁,就会触发跟踪记录。

JIT 编译器使用IFUNCFIFUNCV指令将无法编译的函数列入黑名单。他们不在解释器中进行热点检测和强制执行。

JFUNCF和指令在JFUNCV初始设置后进入 JIT 编译的跟踪。

FUNCCand指令是C 闭包字段FUNCCW指向的伪头文件。pc它们从不发出,仅用于分派到 C 函数调用的设置代码。

所有较高编号的字节码指令都用作快速函数的伪标头。它们从不发出,仅用于为相应的快速功能分派到机器代码。

LuaJIT 2.0 Bytecode Dump Format

LuaJIT 字节码转储格式是使用luajit -bor string.dump函数生成的。它可以保存到文件并稍后加载,而不是存储普通的 Lua 源,占用更多空间并花费更长的时间加载。

字节码转储格式的详细信息可以src/lj_bcdump.h 在 LuaJIT 源代码中找到。这是简洁的格式说明:

dump   = header proto+ 0U
header = ESC 'L' 'J' versionB flagsU [namelenU nameB*]
proto  = lengthU pdata
pdata  = phead bcinsW* uvdataH* kgc* knum* [debugB*]
phead  = flagsB numparamsB framesizeB numuvB numkgcU numknU numbcU
         [debuglenU [firstlineU numlineU]]
kgc    = kgctypeU { ktab | (loU hiU) | (rloU rhiU iloU ihiU) | strB* }
knum   = intU0 | (loU1 hiU)
ktab   = narrayU nhashU karray* khash*
karray = ktabk
khash  = ktabk ktabk
ktabk  = ktabtypeU { intU | (loU hiU) | strB* }

B = 8 bit, H = 16 bit, W = 32 bit, U = ULEB128 of W, U0/U1 = ULEB128 of W+1

 : 将描述转换为人类可读的文本 :-)

转储以魔法开始\x1bLJ。神奇的是版本号,它表示字节码的版本。不同版本不兼容。在撰写本文时,当前版本号1由. 接下来, 使用 ULEB128对位标志(在 中找到)进行编码。如果未设置标志,则接下来是 ULEB128 编码的块名称的长度,并且它本身紧跟在长度之后,否则跳过此步骤。BCDUMP_VERSIONsrc/lj_dump.hBCDUMP_F_{STRIP, BE, FFI}src/lj_dump.hBCDUMP_F_STRIP

:具体lj_bcwrite.c:370 ctx->status = ctx->wfunc(ctx->L, ctx->sb.buf, ctx->sb.n, ctx->wdata);做什么?

 : 更多信息GCproto

接下来,GCproto写入携带字节码的对象。注意复数对象,每个函数有一个对象。对象写得最深,首先在前,即:

function a()
 function b()
  print(1)
 end
 return b
end
a()()

首先b,然后a再编写范围的其余部分。

最后有一个\0字节,表示bcread_proto.

BCDUMP_F_* 标志

GCProto

todo

LuaJIT 2.0 SSA IR

介绍

以下文档描述了LuaJIT 2.0 的 JIT 编译器使用的中间表示 ( IR )。跟踪编译器按照控制流记录字节码指令,并即时发出相应的 IR 指令。

IR具有以下特点:

    • IR 采用 SSA(静态单一分配)形式。每条指令(节点)都代表一个值的单一定义。多条指令形成一个部分连接的数据流图。循环的数据流使用 PHI 指令表示。控制流总是隐含的。

    • IR 是线性的、无指针的且隐含编号:每条指令都可以通过其在线性数组中的位置来唯一引用 (IRRef)。特制的、有偏见的 IR 参考允许快速做出 const 与非 const 决策。存储明确的参考编号、值编号或类似信息不会浪费任何空间。

    • IR 是 2-operand-normalized 形式:每条指令都有一个操作码和最多两个操作数。一些指令可能需要更多的操作数(例如CALL*),这些操作数是使用扩展指令(CARG)组成的。

    • IR 是有类型的:每条指令都有一个输出数据类型。建模类型对应于基本 Lua 数据类型和低级数据类型。更高级别的数据类型根据需要使用受保护的断言进行间接建模。

    • IR 具有隔离的每个操作码链接:这允许以相反的顺序快速搜索特定指令,而无需完全遍历。这用于加速许多优化,例如 CSE 或别名分析。大多数搜索在零(不匹配)之后停止,实际上是一两次取消引用。

    • IR 非常紧凑:每条指令只需要 64 位,并且所有指令彼此相邻。这种布局的缓存效率非常高,索引或遍历速度非常快。

    • IR 是增量生成的:IR 数组是双向增长的:常量向下增长,所有其他指令向上增长。大多数优化都是即时执行的,消除的指令要么根本不发出,要么在代码生成期间被忽略,要么被适当地标记。一般不需要在中间插入或删除指令。这避免了大多数编译器教科书中提出的缓存效率非常低的链接节点海数据结构。

    • IR 是统一的:它同时携带高级语义和低级细节。编译器的不同阶段使用 IR 的不同方面,但共享一个通用的 IR 格式。消除经典的 HIR、MIR、LIR 分离(高、中、低级 IR)大大降低了复杂性和编译器开销。它避免了由于抽象不匹配导致的语义信息丢失,并允许对内存引用进行廉价且有效的高级语义消歧。

    • IR 使用辅助快照:快照捕获对应于字节码执行堆栈中修改的槽和帧的 IR 引用。每个快照都保存一个特定的字节码执行状态,以后可以在跟踪退出时恢复。快照被稀疏地发射和压缩。快照提供了 IR 和字节码域(以及源代码域,通过字节码调试信息)之间的链接。

Status

src/lj_ir.h有关完整的详细信息,请参阅src/lj_jit.hLuaJIT 源代码。生成的 IR 可以与luajit -jdump(跟踪的字节码、IR 和机器代码)或luajit -jdump=i(仅 IR)一起列出。

示例 IR 转储

$ ./luajit -jdump=bitmsr
LuaJIT 2.0.0-beta10 -- Copyright (C) 2005-2012 Mike Pall. http://luajit.org/
JIT: ON CMOV SSE2 SSE3 AMD fold cse dce fwd dse narrow loop abc sink fuse
> local x = 1.2 for i=1,1e3 do x = x * -3 end
---- TRACE 1 start stdin:1
0006  MULVN    0   0   1  ; -3
0007  FORL     1 => 0006
---- TRACE 1 IR
....              SNAP   #0   [ ---- ]
0001 rbp      int SLOAD  #2    CI
0002 xmm7  >  num SLOAD  #1    T
0003 xmm7   + num MUL    0002  -3  
0004 rbp    + int ADD    0001  +1  
....              SNAP   #1   [ ---- 0003 ]
0005       >  int LE     0004  +1000
....              SNAP   #2   [ ---- 0003 0004 ---- ---- 0004 ]
0006 ------------ LOOP ------------
0007 xmm7   + num MUL    0003  -3  
0008 rbp    + int ADD    0004  +1  
....              SNAP   #3   [ ---- 0007 ]
0009       >  int LE     0008  +1000
0010 rbp      int PHI    0004  0008
0011 xmm7     num PHI    0003  0007
---- TRACE 1 mcode 81
394cffa3  mov dword [0x4183f4a0], 0x1
394cffae  movsd xmm0, [0x4184f698]
394cffb7  cvtsd2si ebp, [rdx+0x8]
394cffbc  cmp dword [rdx+0x4], 0xfffeffff
394cffc3  jnb 0x394c0010	->0
394cffc9  movsd xmm7, [rdx]
394cffcd  mulsd xmm7, xmm0
394cffd1  add ebp, +0x01
394cffd4  cmp ebp, 0x3e8
394cffda  jg 0x394c0014	->1
->LOOP:
394cffe0  mulsd xmm7, xmm0
394cffe4  add ebp, +0x01
394cffe7  cmp ebp, 0x3e8
394cffed  jle 0x394cffe0	->LOOP
394cffef  jmp 0x394c001c	->3
---- TRACE 1 stop -> loop

上面打印了跟踪的字节码、从带有快照的字节码生成的 IR,以及从 IR 生成的机器码.

The columns of the IR are as follows:

1st column: IR instruction number (implicit SSA ref)
2nd column: physical CPU register or physical CPU stack slot that
  value is written to when converted to machine code.
  '[%x+]' (rather than register name) indicates hexadecimal offset
  from stack pointer.
  (This column is only present if the 'r' flags is included in -jdump, which
  augments the IR with register/stack slots.  It is not part of the IR itself.)
3rd column: Instruction flags:
  ">" (IRT_GUARD = 0x80 instruction flag) are locations of
    guards (leading to possible side exits from the trace).
  "+" (IRT_ISPHI = 0x40 instruction flag) indicates
    instruction is left or right PHI operand. (i.e referred
    to in some PHI instruction).
4th column: IR type (see IR Types below)
5th column: IR opcode (see opcode reference)
6th/7th column: IR operands (SSA refs or literals)
  '#' prefixes refer to slot numbers, used in SLOADS.
     #0 is the base frame (modified only in tail calls).
     #1 is the first slot in the first frame (register 0 in
     the bytecode)
  '[+-]' prefixes indicate positive or negative numeric literals.
  '[0x%d+]' and NULL are memory addresses.
  '"..."' are strings.
  '@' prefixes indicate slots (what is this?).
  Other possible values: "bias" (number 2^52+2^51 ?), "userdata:%p",
     "userdata:%p" (table)--when do these occur?.

另见 SSA 转储格式注释: http: //lua-users.org/lists/lua-l/2008-06/msg00225.html(旧版本)。参见dump.lua。formatk_

每个快照 (SNAP) 都会列出修改后的堆栈槽及其值。快照列表中的第 i 个值表示在插槽号#i 中写入值的 IR 的索引。'---' 表示该槽没有被写入。帧由“|”分隔。有关快照的更多评论,请参阅http://lua-users.org/lists/lua-l/2009-11/msg00089.html。

IR Types

每条指令都有一个输出数据类型,它要么是基本 Lua 类型之一,要么是低级类型。该顺序经过精心设计,以简化标记值类型的映射并优化常见检查(例如“任何整数类型”)。见src/lj_ir.hsrc/lj_obj.h

# Dump IRT_ Description
0 nil NIL 'nil' value
1 fal FALSE 'false' value
2 tru TRUE 'true' value
3 lud LIGHTUD Lightuserdata value
4 str STR Interned string object
5 p32 P32 32 bit pointer
6 thr THREAD Thread object
7 pro PROTO Function prototype object
8 fun FUNC Function (closure) object
9 p64 P64 64 bit pointer
10 cdt CDATA cdata object
11 tab TAB Table object
12 udt UDATA Userdata object
13 flt FLOAT 32 bit FP number (float)
14 num NUM 64 bit FP number (double)
15 i8 I8 8 bit signed integer (int8_t)
16 u8 U8 8 bit unsigned integer (uint8_t)
17 i16 I16 16 bit signed integer (int16_t)
18 u16 U16 16 bit unsigned integer (uint16_t)
19 int INT 32 bit signed integer (int32_t)
20 u32 U32 32 bit unsigned integer (uint32_t)
21 i64 I64 64 bit signed integer (int64_t)
22 u64 U64 64 bit unsigned integer (uint64_t)
23 sfp SOFTFP Hi-word of split soft-fp operations

Constants

常量指令仅存在于 IR 的常量部分(向上增长到较低的参考)。IR 常量是 interned(去重),并且只能通过查看它们的引用来比较它们的相等性。

常量指令永远不会出现在转储中,因为-jdump始终显示内联到引用指令中的实际常量值

OP
Left
Right
Description
KPRI     Primitive type: nil, false, true
KINT #int 32 bit integer constant
KGC #ptr Garbage collected constant
KPTR #ptr Pointer constant
KKPTR #ptr Pointer constant to constant data
KNULL #ptr Typed NULL constant
KNUM #k64ptr Double-precision floating-point constant
KINT64 #k64ptr 64 bit integer constant
KSLOT kref #slot Hash slot for constant

每个跟踪KPRI在固定引用处都有三个指令,用于常量 nil、false 和 true(REF_NIL、REF_FALSE、REF_TRUE)。

32 位整数或指针值占用左右 16 位操作数的空间。64 位值在一个全局常量表中,并由 32 位指针间接引用。

KPTR是指向可能非常量数据的常量指针(绝对地址)。KKPTR指向绝对恒定的数据。只有VM已知为常量的数据才符合条件,例如,一个内部的 Lua 字符串。被用户(例如const char *)标记为“const”的内容不符合条件。

KSLOT用作键HREFK并保存要在其中找到键的哈希槽和对常量键本身的引用。

Guarded Assertions

受保护的断言有双重目的:

  • 它们提供有关其操作数的断言,编译器可以使用该断言来优化同一跟踪中的所有后续指令。
  • 它们由后端作为分支比较发出,在直通路径中具有“真实”结果。“假”结果退出跟踪并将状态恢复到最近的快照。

标签: q18j5a连接器压力变送器std920

锐单商城拥有海量元器件数据手册IC替代型号,打造 电子元器件IC百科大全!

锐单商城 - 一站式电子元器件采购平台

OP
Left
Right
Description
LT left right left < right (signed)
GE left right left ≥ right (signed)
LE left right left ≤ right (signed)
GT left right left > right (signed)
ULT left right left < right (unsigned/unordered)
UGE left right left ≥ right (unsigned/unordered)
ULE left right left ≤ right (unsigned/unordered)
UGT left right left > right (unsigned/unordered)
EQ left right left = right
NE left right left ≠ right
ABC bound index Array Bounds Check: bound > index (unsigned)
RETF proto pc Return to lower frame: check target PC, shift base