LIR 参考
LIR 是 GDCC 在较低层使用的函数级中间表示。它面向 GDExtension 这类显式调用边界设计:类型、变量、临时值、控制流、对象生命周期和 Variant 动态边界都需要在 IR 中被明确表达。
这份参考描述 LIR 的公开结构和每条指令的语义意图,不描述 GDCC 内部如何把这些指令落到具体实现。阅读时可以把 LIR 看成一个“接近目标运行时,但仍保留编译器语义”的线性表示。
LIR 组成架构
Section titled “LIR 组成架构”一个 LIR 单元由类定义组成。类定义之下包含信号、属性和函数;函数体是 LIR 的核心执行单元。
- 模块:包含一个或多个类定义,表示一次编译输出中的脚本类集合。
- 类定义:包含类名、父类、注解、信号、属性和函数,描述 Godot 可见的脚本类表面。
- 信号:包含名称和参数列表,描述类发出的 typed signal。
- 属性:包含名称、类型、静态性、初始化 helper 和访问 helper,描述类字段与 Godot 属性表面。
- 函数:包含签名、参数、返回类型、变量表和基本块,表示可执行逻辑。
- 变量表:记录参数、局部变量、常量或栈存储,给指令中的
$id提供类型和存储语义。 - 基本块:包含一个入口、一串普通指令和一个终结指令,表示线性代码片段和控制流节点。
- 指令:包含 opcode、可选结果和操作数,表示具体计算、调用、读写或跳转。
LIR 不采用 SSA。一个值可以通过变量槽、显式赋值、加载和存储继续流动。这样做的设计目标是让 IR 更接近 GDExtension C 代码的生成模型:变量槽、属性槽、返回槽和生命周期边界都可以被直接表达。
基本指令文本形态如下:
($<result_id> = )?<opcode> <operand>...结果值写在左侧,变量操作数使用 $id,字符串和操作符名称使用引号,基本块标签直接写标签名。没有结果的指令只写 opcode 和操作数。
指令按返回值形态分为三类:
- 必须有结果:指令会产生新值,写作
$result = opcode ...。 - 可选结果:调用类指令可保留返回值,也可在只关心副作用时省略结果。
- 无结果:指令只产生副作用、控制流或调试标记。
操作数常见形态:
- 变量:写作
$value,引用变量表或临时值中的变量 id。 - 字符串:写作
"name",表示类名、函数名、属性名、字符串字面量等。 - 整数、浮点和布尔:写作
1、1.5、true,表示立即值。 - 标签:写作
entry、exit,表示基本块 id。 - 操作符:写作
"ADD"、"LESS",表示 Godot 操作符枚举名称。
数据字面量和赋值
Section titled “数据字面量和赋值”这些指令把常量或已有变量变成 LIR 值。它们通常是表达式 lowering 的叶子节点。
literal_bool
Section titled “literal_bool”创建 bool 常量。适合条件、比较结果替代值、布尔字面量。
$flag = literal_bool trueliteral_int
Section titled “literal_int”创建整数常量。LIR 表面按整数值表达,目标变量类型决定后续边界。
$count = literal_int 42literal_float
Section titled “literal_float”创建浮点常量。用于 float 字面量或需要浮点临时值的位置。
$ratio = literal_float 0.5literal_string
Section titled “literal_string”创建 String 常量,字符串内容按 UTF-8 文本表达。
$text = literal_string "hello"literal_string_name
Section titled “literal_string_name”创建 StringName 常量。适合方法名、属性名、信号名等身份化名称边界。
$name = literal_string_name "position"literal_nil
Section titled “literal_nil”创建 Variant 的 Nil 值。用于动态空值或 Variant 空边界。
$v = literal_nilliteral_null
Section titled “literal_null”创建对象空引用。用于对象类型的 null 值。
$obj = literal_nullassign
Section titled “assign”把一个变量的值写入另一个结果变量,显式表达类型兼容边界。
$target = assign $sourceassign 不只是文本拷贝。它表达“把 source 放入 result 所属类型槽”的语义,因此需要满足同类型、对象上行转换、Variant 边界、有限容器协变,或已经按受支持的基本类型规则转换后的值。
构造、销毁和运算
Section titled “构造、销毁和运算”这些指令负责创建复杂值、处理生命周期,以及执行 Godot 操作符。
construct_builtin
Section titled “construct_builtin”按结果变量类型构造内建值,例如向量、颜色、变换等。参数列表对应构造输入。
$vec = construct_builtin $x $y $zconstruct_array
Section titled “construct_array”构造 Array 或 Packed*Array。普通 Array 可带元素类型提示;packed array 由结果变量类型决定。
$items = construct_array "Node"$packed = construct_arrayconstruct_dictionary
Section titled “construct_dictionary”构造 Dictionary,可选择提供 key/value 类型名。省略类型名表示动态字典。
$map = construct_dictionary "StringName" "Node"construct_object
Section titled “construct_object”构造某个类的对象实例。结果变量类型应能承载该类实例。
$node = construct_object "Node"construct_callable
Section titled “construct_callable”从当前编译单元中的函数创建 Callable。
$cb = construct_callable "do_work"construct_lambda
Section titled “construct_lambda”从 lambda 函数和捕获列表创建 Callable。第一个操作数是 lambda 函数名,之后是捕获变量。
$cb = construct_lambda "lambda_1" $self $valuedestruct
Section titled “destruct”销毁或释放变量持有的可销毁资源。只应出现在有明确来源的生命周期路径中。
destruct $value "AUTO_GENERATED"try_own_object
Section titled “try_own_object”尝试为对象取得引用所有权;非引用计数对象上语义上可为空操作。
try_own_object $obj "INTERNAL"try_release_object
Section titled “try_release_object”尝试释放对象引用所有权;非引用计数对象上语义上可为空操作。
try_release_object $obj "USER_EXPLICIT"生命周期指令可以带来源字符串:AUTO_GENERATED、INTERNAL、USER_EXPLICIT 或 UNKNOWN。省略时等价于兼容旧格式的未知来源。新的 LIR 应尽量显式写出来源,避免无法判断这条生命周期操作由谁负责。
unary_op
Section titled “unary_op”执行一元 Godot 操作符,例如取负或逻辑非。
$out = unary_op "NEGATE" $value可用的一元操作符:
| 操作符 | 常见源码写法 |
|---|---|
NEGATE | unary - |
POSITIVE | unary + |
BIT_NOT | ~ |
NOT | not、! |
binary_op
Section titled “binary_op”执行二元 Godot 操作符,例如加法、比较、位运算。
$sum = binary_op "ADD" $left $right可用的二元操作符:
| 操作符 | 常见源码写法 |
|---|---|
EQUAL | == |
NOT_EQUAL | != |
LESS | < |
LESS_EQUAL | <= |
GREATER | > |
GREATER_EQUAL | >= |
ADD | binary + |
SUBTRACT | binary - |
MULTIPLY | * |
DIVIDE | / |
MODULE | % |
POWER | ** |
SHIFT_LEFT | << |
SHIFT_RIGHT | >> |
BIT_AND | & |
BIT_OR | | |
BIT_XOR | ^ |
AND | and、&& |
OR | or、|| |
XOR | xor |
IN | in |
LIR 文本中的操作符操作数写枚举名,例如 "ADD"、"NEGATE",不是写源码符号 + 或 -。
索引和动态访问
Section titled “索引和动态访问”索引指令面向 Variant、字典、数组、对象和动态 keyed/named/indexed 访问。它们把“通过 key/name/index 访问一个动态承载值”的语义显式拆出来。
variant_get
Section titled “variant_get”通过另一个 Variant key 从 Variant 中读取值。适合最通用的动态 get。
$out = variant_get $variant $keyvariant_get_keyed
Section titled “variant_get_keyed”通过 key 从 keyed Variant 读取值,通常用于字典类访问。
$out = variant_get_keyed $dict_like $keyvariant_get_named
Section titled “variant_get_named”通过 StringName 从 named Variant 读取值,通常用于对象属性或命名成员。
$out = variant_get_named $object_like $namevariant_get_indexed
Section titled “variant_get_indexed”通过整数 index 从 Variant 读取值,通常用于数组或 packed array。
$out = variant_get_indexed $array_like $indexvariant_set
Section titled “variant_set”通过 Variant key 向 Variant 写入值。
variant_set $variant $key $valuevariant_set_keyed
Section titled “variant_set_keyed”通过 key 向 keyed Variant 写入值。
variant_set_keyed $dict_like $key $valuevariant_set_named
Section titled “variant_set_named”通过 StringName 向 named Variant 写入值。
variant_set_named $object_like $name $valuevariant_set_indexed
Section titled “variant_set_indexed”通过整数 index 向 Variant 写入值。
variant_set_indexed $array_like $index $value这些指令的结果通常仍是 Variant 或后续要进入 typed boundary 的值。若后续需要稳定类型,应配合 unpack_variant、assign 或显式构造边界。
类型和 Variant 边界
Section titled “类型和 Variant 边界”类型指令用于运行时类型查询、对象判断、对象转换,以及稳定类型和 Variant 之间的打包/解包。
get_variant_type
Section titled “get_variant_type”读取 Variant 当前承载值的 Godot 类型 id。
$type_id = get_variant_type $variantget_class_name
Section titled “get_class_name”取得变量的类型名或对象类名,结果是 String。
$name = get_class_name $valueobject_cast
Section titled “object_cast”尝试把对象转换为指定类;不匹配时结果为空对象。结果可选,但通常应保留。
$node = object_cast "Node" $objis_instance_of
Section titled “is_instance_of”判断对象是否为指定类实例,结果为 bool。
$ok = is_instance_of "Node" $objpack_variant
Section titled “pack_variant”把稳定类型值装入 Variant。用于进入动态边界。
$variant = pack_variant $valueunpack_variant
Section titled “unpack_variant”从 Variant 解出结果变量所需类型。用于离开动态边界。
$value = unpack_variant $variantpack_variant 和 unpack_variant 是 LIR 中最重要的动态边界标记。它们让 Variant 的进入和离开变成显式动作,而不是由调用或赋值隐式猜测。
variant_is_nil
Section titled “variant_is_nil”判断 Variant 是否为 Nil。
$is_nil = variant_is_nil $variantobject_is_null
Section titled “object_is_null”判断对象引用是否为空。
$is_null = object_is_null $obj控制流指令只能作为基本块终结指令使用。每个基本块应以一个终结指令结束,从而让控制流图清晰可验证。
无条件跳转到目标基本块。
goto exit根据 bool 条件跳转到 true/false 两个基本块。
go_if $cond then_block else_blockgo_if 的条件应是布尔值。来自 Variant 或其它稳定类型的条件通常需要先经过显式归一化,再进入 go_if。
return
Section titled “return”从当前函数返回,可带返回值,也可无返回值。
returnreturn $value调用指令都允许结果可选:如果调用返回值被使用,就写 $result = ...;如果只需要副作用,可以省略结果。
call_global
Section titled “call_global”调用全局函数,例如全局工具函数或 Godot 全局函数。
$out = call_global "print" $messagecall_method
Section titled “call_method”调用对象实例方法。第一个变量操作数是 receiver,之后是参数。
$out = call_method "get_child" $node $indexcall_super_method
Section titled “call_super_method”调用父类方法。用于源码中的 super 调用语义。
call_super_method "_ready" $selfcall_static_method
Section titled “call_static_method”调用类上的静态方法。
$out = call_static_method "MathUtil" "clamp_i" $value $min $maxcall_intrinsic
Section titled “call_intrinsic”调用编译器定义的 intrinsic。适合无法自然表达为普通全局/方法调用的内建语义。
$out = call_intrinsic "intrinsic_name" $arg调用参数按顺序列出。返回值是否保留应由源码上下文决定:表达式位置通常需要结果,statement 位置的 void 或纯副作用调用通常不需要结果。
属性和静态存取
Section titled “属性和静态存取”这组指令表达对象属性和静态成员的直接读写。它们与 variant_get_named / variant_set_named 的区别是:这里的属性名和类名是 LIR 表面上的稳定字符串,而不是动态 Variant 路径。
load_property
Section titled “load_property”从对象读取命名属性。
$value = load_property "position" $nodestore_property
Section titled “store_property”向对象写入命名属性。
store_property "position" $node $valueload_static
Section titled “load_static”读取类上的静态变量或静态属性。
$value = load_static "GameState" "score"store_static
Section titled “store_static”写入类上的静态变量或静态属性。
store_static "GameState" "score" $value属性读写应由类型和可见性分析保证目标存在、可读或可写。LIR 本身通过指令形态表达“这是一个稳定属性路径”,而不是重新描述源码解析规则。
空操作。用于占位、测试或保持指令序列结构。
nopline_number
Section titled “line_number”标记后续指令对应的源码行号,服务诊断或调试信息。line_number 不改变程序语义。
line_number 27- 数据:
literal_bool、literal_int、literal_float、literal_string、literal_string_name、literal_nil、literal_null、assign。 - 构造、生命周期和运算:
construct_builtin、construct_array、construct_dictionary、construct_object、construct_callable、construct_lambda、destruct、try_own_object、try_release_object、unary_op、binary_op。 - 索引和动态访问:
variant_get、variant_get_keyed、variant_get_named、variant_get_indexed、variant_set、variant_set_keyed、variant_set_named、variant_set_indexed。 - 类型:
get_variant_type、get_class_name、object_cast、is_instance_of、pack_variant、unpack_variant、variant_is_nil、object_is_null。 - 控制流:
goto、go_if、return。 - 调用:
call_global、call_method、call_super_method、call_static_method、call_intrinsic。 - 存取:
load_property、store_property、load_static、store_static。 - 其它:
nop、line_number。