跳转到内容

LIR 参考

LIR 是 GDCC 在较低层使用的函数级中间表示。它面向 GDExtension 这类显式调用边界设计:类型、变量、临时值、控制流、对象生命周期和 Variant 动态边界都需要在 IR 中被明确表达。

这份参考描述 LIR 的公开结构和每条指令的语义意图,不描述 GDCC 内部如何把这些指令落到具体实现。阅读时可以把 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",表示类名、函数名、属性名、字符串字面量等。
  • 整数、浮点和布尔:写作 11.5true,表示立即值。
  • 标签:写作 entryexit,表示基本块 id。
  • 操作符:写作 "ADD""LESS",表示 Godot 操作符枚举名称。

这些指令把常量或已有变量变成 LIR 值。它们通常是表达式 lowering 的叶子节点。

创建 bool 常量。适合条件、比较结果替代值、布尔字面量。

$flag = literal_bool true

创建整数常量。LIR 表面按整数值表达,目标变量类型决定后续边界。

$count = literal_int 42

创建浮点常量。用于 float 字面量或需要浮点临时值的位置。

$ratio = literal_float 0.5

创建 String 常量,字符串内容按 UTF-8 文本表达。

$text = literal_string "hello"

创建 StringName 常量。适合方法名、属性名、信号名等身份化名称边界。

$name = literal_string_name "position"

创建 Variant 的 Nil 值。用于动态空值或 Variant 空边界。

$v = literal_nil

创建对象空引用。用于对象类型的 null 值。

$obj = literal_null

把一个变量的值写入另一个结果变量,显式表达类型兼容边界。

$target = assign $source

assign 不只是文本拷贝。它表达“把 source 放入 result 所属类型槽”的语义,因此需要满足同类型、对象上行转换、Variant 边界、有限容器协变,或已经按受支持的基本类型规则转换后的值。

这些指令负责创建复杂值、处理生命周期,以及执行 Godot 操作符。

按结果变量类型构造内建值,例如向量、颜色、变换等。参数列表对应构造输入。

$vec = construct_builtin $x $y $z

构造 ArrayPacked*Array。普通 Array 可带元素类型提示;packed array 由结果变量类型决定。

$items = construct_array "Node"
$packed = construct_array

构造 Dictionary,可选择提供 key/value 类型名。省略类型名表示动态字典。

$map = construct_dictionary "StringName" "Node"

构造某个类的对象实例。结果变量类型应能承载该类实例。

$node = construct_object "Node"

从当前编译单元中的函数创建 Callable

$cb = construct_callable "do_work"

从 lambda 函数和捕获列表创建 Callable。第一个操作数是 lambda 函数名,之后是捕获变量。

$cb = construct_lambda "lambda_1" $self $value

销毁或释放变量持有的可销毁资源。只应出现在有明确来源的生命周期路径中。

destruct $value "AUTO_GENERATED"

尝试为对象取得引用所有权;非引用计数对象上语义上可为空操作。

try_own_object $obj "INTERNAL"

尝试释放对象引用所有权;非引用计数对象上语义上可为空操作。

try_release_object $obj "USER_EXPLICIT"

生命周期指令可以带来源字符串:AUTO_GENERATEDINTERNALUSER_EXPLICITUNKNOWN。省略时等价于兼容旧格式的未知来源。新的 LIR 应尽量显式写出来源,避免无法判断这条生命周期操作由谁负责。

执行一元 Godot 操作符,例如取负或逻辑非。

$out = unary_op "NEGATE" $value

可用的一元操作符:

操作符常见源码写法
NEGATEunary -
POSITIVEunary +
BIT_NOT~
NOTnot!

执行二元 Godot 操作符,例如加法、比较、位运算。

$sum = binary_op "ADD" $left $right

可用的二元操作符:

操作符常见源码写法
EQUAL==
NOT_EQUAL!=
LESS<
LESS_EQUAL<=
GREATER>
GREATER_EQUAL>=
ADDbinary +
SUBTRACTbinary -
MULTIPLY*
DIVIDE/
MODULE%
POWER**
SHIFT_LEFT<<
SHIFT_RIGHT>>
BIT_AND&
BIT_OR|
BIT_XOR^
ANDand&&
ORor||
XORxor
INin

LIR 文本中的操作符操作数写枚举名,例如 "ADD""NEGATE",不是写源码符号 +-

索引指令面向 Variant、字典、数组、对象和动态 keyed/named/indexed 访问。它们把“通过 key/name/index 访问一个动态承载值”的语义显式拆出来。

通过另一个 Variant key 从 Variant 中读取值。适合最通用的动态 get。

$out = variant_get $variant $key

通过 key 从 keyed Variant 读取值,通常用于字典类访问。

$out = variant_get_keyed $dict_like $key

通过 StringName 从 named Variant 读取值,通常用于对象属性或命名成员。

$out = variant_get_named $object_like $name

通过整数 index 从 Variant 读取值,通常用于数组或 packed array。

$out = variant_get_indexed $array_like $index

通过 Variant key 向 Variant 写入值。

variant_set $variant $key $value

通过 key 向 keyed Variant 写入值。

variant_set_keyed $dict_like $key $value

通过 StringName 向 named Variant 写入值。

variant_set_named $object_like $name $value

通过整数 index 向 Variant 写入值。

variant_set_indexed $array_like $index $value

这些指令的结果通常仍是 Variant 或后续要进入 typed boundary 的值。若后续需要稳定类型,应配合 unpack_variantassign 或显式构造边界。

类型指令用于运行时类型查询、对象判断、对象转换,以及稳定类型和 Variant 之间的打包/解包。

读取 Variant 当前承载值的 Godot 类型 id。

$type_id = get_variant_type $variant

取得变量的类型名或对象类名,结果是 String

$name = get_class_name $value

尝试把对象转换为指定类;不匹配时结果为空对象。结果可选,但通常应保留。

$node = object_cast "Node" $obj

判断对象是否为指定类实例,结果为 bool

$ok = is_instance_of "Node" $obj

把稳定类型值装入 Variant。用于进入动态边界。

$variant = pack_variant $value

Variant 解出结果变量所需类型。用于离开动态边界。

$value = unpack_variant $variant

pack_variantunpack_variant 是 LIR 中最重要的动态边界标记。它们让 Variant 的进入和离开变成显式动作,而不是由调用或赋值隐式猜测。

判断 Variant 是否为 Nil。

$is_nil = variant_is_nil $variant

判断对象引用是否为空。

$is_null = object_is_null $obj

控制流指令只能作为基本块终结指令使用。每个基本块应以一个终结指令结束,从而让控制流图清晰可验证。

无条件跳转到目标基本块。

goto exit

根据 bool 条件跳转到 true/false 两个基本块。

go_if $cond then_block else_block

go_if 的条件应是布尔值。来自 Variant 或其它稳定类型的条件通常需要先经过显式归一化,再进入 go_if

从当前函数返回,可带返回值,也可无返回值。

return
return $value

调用指令都允许结果可选:如果调用返回值被使用,就写 $result = ...;如果只需要副作用,可以省略结果。

调用全局函数,例如全局工具函数或 Godot 全局函数。

$out = call_global "print" $message

调用对象实例方法。第一个变量操作数是 receiver,之后是参数。

$out = call_method "get_child" $node $index

调用父类方法。用于源码中的 super 调用语义。

call_super_method "_ready" $self

调用类上的静态方法。

$out = call_static_method "MathUtil" "clamp_i" $value $min $max

调用编译器定义的 intrinsic。适合无法自然表达为普通全局/方法调用的内建语义。

$out = call_intrinsic "intrinsic_name" $arg

调用参数按顺序列出。返回值是否保留应由源码上下文决定:表达式位置通常需要结果,statement 位置的 void 或纯副作用调用通常不需要结果。

这组指令表达对象属性和静态成员的直接读写。它们与 variant_get_named / variant_set_named 的区别是:这里的属性名和类名是 LIR 表面上的稳定字符串,而不是动态 Variant 路径。

从对象读取命名属性。

$value = load_property "position" $node

向对象写入命名属性。

store_property "position" $node $value

读取类上的静态变量或静态属性。

$value = load_static "GameState" "score"

写入类上的静态变量或静态属性。

store_static "GameState" "score" $value

属性读写应由类型和可见性分析保证目标存在、可读或可写。LIR 本身通过指令形态表达“这是一个稳定属性路径”,而不是重新描述源码解析规则。

空操作。用于占位、测试或保持指令序列结构。

nop

标记后续指令对应的源码行号,服务诊断或调试信息。line_number 不改变程序语义。

line_number 27
  • 数据:literal_boolliteral_intliteral_floatliteral_stringliteral_string_nameliteral_nilliteral_nullassign
  • 构造、生命周期和运算:construct_builtinconstruct_arrayconstruct_dictionaryconstruct_objectconstruct_callableconstruct_lambdadestructtry_own_objecttry_release_objectunary_opbinary_op
  • 索引和动态访问:variant_getvariant_get_keyedvariant_get_namedvariant_get_indexedvariant_setvariant_set_keyedvariant_set_namedvariant_set_indexed
  • 类型:get_variant_typeget_class_nameobject_castis_instance_ofpack_variantunpack_variantvariant_is_nilobject_is_null
  • 控制流:gotogo_ifreturn
  • 调用:call_globalcall_methodcall_super_methodcall_static_methodcall_intrinsic
  • 存取:load_propertystore_propertyload_staticstore_static
  • 其它:nopline_number