LIR Reference
LIR is GDCC’s lower-level function IR. It is designed around explicit GDExtension-facing boundaries: types, variable slots, temporary values, control flow, object lifecycle, and Variant transitions are represented directly in the IR.
This reference describes the public LIR surface and the intent of each instruction. It does not document GDCC’s concrete backend implementation strategy.
LIR Architecture
Section titled “LIR Architecture”An LIR unit is organized around class definitions. A class contains signals, properties, and functions; functions contain the executable body.
- Module: contains one or more class definitions emitted by a compile unit.
- Class definition: contains the class name, superclass, annotations, signals, properties, and functions that make up the Godot-visible class surface.
- Signal: contains a name and parameter list for a typed signal declaration.
- Property: contains a name, type, static flag, initialization helper, and accessor helpers for field/property surface.
- Function: contains the signature, parameters, return type, variable table, and basic blocks for executable logic.
- Variables: record parameters, locals, constants, and stack slots addressed by
$id. - Basic block: contains one entry, ordinary instructions, and one terminator instruction.
- Instruction: contains an opcode, optional result, and operands for a computation, call, read/write, or jump.
LIR is not SSA. Values flow through explicit variable slots, assignments, loads, stores, and returns. That shape keeps the IR close to GDExtension C generation without making the generated C details part of this reference.
The text form is:
($<result_id> = )?<opcode> <operand>...Variable operands use $id, strings and operator names use quotes, and block labels are written as bare labels.
XML Structure and Metadata
Section titled “XML Structure and Metadata”An LIR file is XML: a top-level class_def contains signals, properties, and functions. Besides instructions, class definitions and functions carry metadata for both the Godot-visible surface and compiler-internal needs.
Class Definitions
Section titled “Class Definitions”<class_def name="RotatingCamera" super="Camera3D" is_abstract="false" is_tool="false"> <annotation key="tool" value=""/> <signals>...</signals> <properties>...</properties> <functions>...</functions></class_def>nameis optional for anonymous classes.is_abstractandis_toolare optional and default tofalse.is_toolis script-level state: when a@toolscript compiles to multiple classes (top-level plus inner classes), everyclass_defcarriesis_tool="true", but only the top-level class carries thetoolannotation.
Properties
Section titled “Properties”A property carries a name, type, static flag, init_func, getter_func, setter_func, and annotations.
init_funcis optional; its return value initializes the property. For a non-static property, the initializer’s first parameter must be anObjecttype representingself.getter_func/setter_funcare optional: the getter receivesselfand returns the property’s type, while the setter receivesselfand a value of that type. A default accessor is generated when they are omitted.- For the
@export*family, the annotationvalueencodes the Godothint_stringverbatim: no quotes and no annotation-name prefix. For example,@export_range(0, 20, 0.5)becomes<annotation key="export_range" value="0,20,0.5"/>; an empty value means an emptyhint_string.
Functions
Section titled “Functions”<function name="get_pitch" is_static="false" is_abstract="false" is_lambda="false" is_vararg="false" is_hidden="false" is_coroutine="false"> <meta call_site_context="<call_site_context>" source_identity_key="<Class>::<enclosingFunc>#<ordinal>"/> <parameters> <parameter name="to_radius" type="bool" default_value_func="_default_get_pitch$to_radius"/> </parameters> <captures> <capture name="value" type="int"/> </captures> <return_type type="float"/> <variables>...</variables> <basic_blocks entry="entry">...</basic_blocks></function>is_coroutinemarks a stackful-coroutine function.awaitis only legal inside a function where it istrue.is_lambdamarks a lambda function. Lambdas must carrymeta; plain functions never do.metais the lambda hot-reload identity metadata:source_identity_keyuses the<Class>::<enclosingFunc>#<ordinal>form, andcall_site_contextis an optional additional rebind gate.is_hiddenmarks compiler-generated helper functions, such as the default-argument helper_default_get_pitch$to_radiusand the field initializer_field_init_pitch_degree; they are not part of the source-callable method surface.capturesbelongs only to lambdas and records capture names and types.- A parameter’s
default_value_funcis optional. When the caller omits the argument, the compiler calls that function and uses its return value; omitting the argument without adefault_value_funcis an error.
Common Conventions
Section titled “Common Conventions”Instructions can require a result, allow an optional result, or produce no result.
- Required result: the instruction produces a new value, written as
$result = opcode .... - Optional result: call instructions may keep the return value or omit it when only side effects matter.
- No result: the instruction only produces side effects, control flow, or debug metadata.
Common operand forms:
- Variable:
$value, referring to a variable id or temporary id. - String:
"name", used for class names, function names, property names, and string literals. - Number or bool:
1,1.5,true, used for immediate literal operands. - Label:
entry, used for basic block ids. - Operator:
"ADD", used for Godot operator enum names.
Data Literals and Assignment
Section titled “Data Literals and Assignment”These instructions turn constants or existing variables into LIR values. They are usually expression-lowering leaves.
literal_bool
Section titled “literal_bool”Creates a bool constant.
$flag = literal_bool trueliteral_int
Section titled “literal_int”Creates an integer constant.
$count = literal_int 42literal_float
Section titled “literal_float”Creates a floating-point constant.
$ratio = literal_float 0.5literal_string
Section titled “literal_string”Creates a UTF-8 String constant.
$text = literal_string "hello"literal_string_name
Section titled “literal_string_name”Creates a StringName constant for identity-like names such as methods, properties, and signals.
$name = literal_string_name "position"literal_node_path
Section titled “literal_node_path”Creates a NodePath constant. A source ^"..." literal becomes an ordinary string operand; the operand is the path text with the ^ prefix and outer quotes removed.
$path = literal_node_path "Camera3D/Pivot"literal_nil
Section titled “literal_nil”Creates a Variant Nil value.
$v = literal_nilliteral_null
Section titled “literal_null”Creates a null object reference.
$obj = literal_nullassign
Section titled “assign”Moves a source value into a target-typed result slot.
$target = assign $sourceassign represents a typed boundary. The source must be assignable to the result variable’s type, such as same-type assignment, object upcast, Variant boundary, supported container covariance, or a value already materialized by a supported implicit conversion rule.
Construction, Lifecycle, and Operators
Section titled “Construction, Lifecycle, and Operators”These instructions create complex values, maintain lifecycle boundaries, and execute Godot operators.
construct_builtin
Section titled “construct_builtin”Constructs a builtin value using the result variable’s type.
$vec = construct_builtin $x $y $zconstruct_array
Section titled “construct_array”Constructs Array or Packed*Array. A normal Array may carry an element type hint; a packed array is selected from the result variable type.
$items = construct_array "Node"$packed = construct_arrayconstruct_dictionary
Section titled “construct_dictionary”Constructs a typed or generic Dictionary.
$map = construct_dictionary "StringName" "Node"construct_container_literal
Section titled “construct_container_literal”Constructs and fills a fresh Array or Dictionary from already-materialized operand slots. The container family is decided solely by the result variable type:
- The result type must be a non-ref ordinary
ArrayorDictionary; neverPacked*Arrayor another builtin. - For
Array, operands are element0..elementN in source order; empty operands are legal. - For
Dictionary, operands are key0/value0/key1/value1… and the count must be even. - Operand-to-element/key/value boundary conversions (such as
int -> float,String -> StringName, Variant pack/unpack) are emitted by the frontend before this instruction.
This instruction does not replace the empty-container construct_array / construct_dictionary, which remain the contract for __prepare__, property default initialization, and explicit empty construction.
$arr = construct_container_literal $a $b $c$dict = construct_container_literal $k0 $v0 $k1 $v1construct_object
Section titled “construct_object”Constructs an object of a named class.
$node = construct_object "Node"construct_signal
Section titled “construct_signal”Constructs a Signal value from a live object receiver and a compile-time signal name. The result is a destroyable builtin value and does not keep the receiver alive.
$sig = construct_signal $self "timeout"construct_callable
Section titled “construct_callable”Creates a Callable from an instance receiver and a compile-time method name. The operands are exactly (receiver, method_name); the old one-operand form is illegal.
$cb = construct_callable $self "_on_timeout"The receiver’s static type selects the dispatch path:
Objecttype: bind viagodot_new_Callable_with_Object_StringName.- Non-Object builtin: pack a temporary
Variant, then create aCallablebound to that value (the static wrapper takes noself; its internal ptrcall base isNULL). Variant, static method, or utility function: illegal as a receiver.
The result is a destroyable builtin value and does not keep the receiver alive. Static or global functions should use construct_standalone_callable.
construct_standalone_callable
Section titled “construct_standalone_callable”Creates a Callable for a compile-time known function that has no instance receiver. kind is one of utility, static_gdcc, or static_engine:
utility: a utility function; the owner must be the empty string.static_gdcc/static_engine: a static function; the owner must be a class name.
$cb = construct_standalone_callable "utility" "" "print"$cb2 = construct_standalone_callable "static_gdcc" "MathUtil" "clamp_i"construct_lambda
Section titled “construct_lambda”Creates a Callable from a lambda and capture variables. The first operand is the lambda function name; the remaining operands are captures.
$cb = construct_lambda "lambda_1" $self $valuedestruct
Section titled “destruct”Destroys or releases a resource held by a variable, with provenance.
destruct $value "AUTO_GENERATED"try_own_object
Section titled “try_own_object”Tries to take ownership of an object reference. For non-reference-counted objects, this may be a semantic no-op.
try_own_object $obj "INTERNAL"try_release_object
Section titled “try_release_object”Tries to release ownership of an object reference. For non-reference-counted objects, this may be a semantic no-op.
try_release_object $obj "USER_EXPLICIT"Lifecycle provenance may be AUTO_GENERATED, INTERNAL, USER_EXPLICIT, or UNKNOWN. New LIR should prefer explicit provenance.
unary_op
Section titled “unary_op”Executes a unary Godot operator.
$out = unary_op "NEGATE" $valueAvailable unary operators:
| Operator | Common source spelling |
|---|---|
NEGATE | unary - |
POSITIVE | unary + |
BIT_NOT | ~ |
NOT | not, ! |
binary_op
Section titled “binary_op”Executes a binary Godot operator.
$sum = binary_op "ADD" $left $rightAvailable binary operators:
| Operator | Common source spelling |
|---|---|
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 |
The LIR text operand uses the enum name, such as "ADD" or "NEGATE", not the source symbol + or -.
Additional conventions:
MODULEcarries both numeric modulo and string formatting: when the left operand is aString,%follows Godot’s formatting semantics.not inis supported as a composite rule, equivalent to applyingNOTto the result ofIN.- Object values carry type identity in LIR, so
EQUAL/NOT_EQUALbetween objects compares instance identity as in Godot; it does not require the static classes to be related, and it is not an inheritance test likeis.
Indexing and Dynamic Access
Section titled “Indexing and Dynamic Access”These instructions are the dynamic access surface for Variant, dictionary-like, array-like, object-like, keyed, named, and indexed access.
variant_get
Section titled “variant_get”Reads from a Variant with a Variant key.
$out = variant_get $variant $keyvariant_get_keyed
Section titled “variant_get_keyed”Reads from a keyed Variant, usually dictionary-like.
$out = variant_get_keyed $dict_like $keyvariant_get_named
Section titled “variant_get_named”Reads from a named Variant using StringName.
$out = variant_get_named $object_like $namevariant_get_indexed
Section titled “variant_get_indexed”Reads from a Variant using an integer index.
$out = variant_get_indexed $array_like $indexvariant_set
Section titled “variant_set”Writes to a Variant with a Variant key.
variant_set $variant $key $valuevariant_set_keyed
Section titled “variant_set_keyed”Writes to a keyed Variant.
variant_set_keyed $dict_like $key $valuevariant_set_named
Section titled “variant_set_named”Writes to a named Variant.
variant_set_named $object_like $name $valuevariant_set_indexed
Section titled “variant_set_indexed”Writes to a Variant using an integer index.
variant_set_indexed $array_like $index $valueResults often remain Variant until a later unpack_variant, assign, or explicit typed boundary.
Type and Variant Boundaries
Section titled “Type and Variant Boundaries”Type instructions handle runtime type queries, object tests, object casts, and stable-type to Variant transitions.
get_variant_type
Section titled “get_variant_type”Reads the Godot type id held by a Variant.
$type_id = get_variant_type $variantget_class_name
Section titled “get_class_name”Gets a static type name or runtime object class name as String.
$name = get_class_name $valueobject_cast
Section titled “object_cast”Performs a runtime class cast when the as target is an object class. The source may be Object, Variant, or Nil.
$node = object_cast "Node" $valueOn success it returns the same instance represented as the target type (carrying the target type identity) without changing ownership category; on failure (null, freed, non-object Variant payload, or class mismatch) it produces null. The result is optional, in which case the cast is validated but not materialized.
builtin_cast
Section titled “builtin_cast”Performs a runtime conversion when the as target is a non-Object, non-Variant, non-Nil builtin, including parameterized Array[T] / Dictionary[K, V]. Semantics align with Godot’s Variant::construct / can_convert.
$v = builtin_cast "Vector2" $value$ints = builtin_cast "PackedInt32Array" $value- The result is required; parameterized containers keep their full declared type text.
- Exact same-type and
as Variantuseassign/pack_variantinstead. - Exception: a same-family
Packed*Arrayasis not an identity-preservingassign; it produces a COW copy with a fresh identity, matching the Godot 4.5 interpreter. Packedistests are exactVariant.get_type()family matches.
is_instance_of
Section titled “is_instance_of”Tests whether a value is an instance of the compile-time type (source is). $value may be any ordinary typed value, including Variant, builtins, and parameterized containers; it is no longer limited to objects.
$ok = is_instance_of "Node" $value$is_int_array = is_instance_of "Array[int]" $value- The result is always
bool; for non-Varianttargets,null/ a nil object yieldsfalse. - Class names use canonical / Godot-facing names; parameterized containers use full type text.
Variantis the top type:x is Variantis folded totrueandx is not Varianttofalse, so stable LIR should not carryis_instance_of "Variant".- A bare
Array/Dictionarytested against a parameterized target (such as"Array[T]") still performs a runtime typed-metadata check and must not be folded away based only on the static bare slot type.
pack_variant
Section titled “pack_variant”Packs a stable value into Variant.
$variant = pack_variant $valueunpack_variant
Section titled “unpack_variant”Unpacks a Variant into the result variable’s type.
$value = unpack_variant $variantpack_variant and unpack_variant are the explicit markers for entering and leaving dynamic value space.
variant_is_nil
Section titled “variant_is_nil”Tests whether a Variant is Nil.
$is_nil = variant_is_nil $variantobject_is_null
Section titled “object_is_null”Tests whether an object reference is null.
$is_null = object_is_null $objControl Flow
Section titled “Control Flow”Control-flow instructions terminate basic blocks.
Unconditionally branches to a basic block.
goto exitBranches to true/false blocks based on a bool.
go_if $cond then_block else_blockgo_if expects a boolean condition. Dynamic or non-boolean source conditions should be normalized before the branch.
return
Section titled “return”Returns from the current function, with or without a value.
returnreturn $valueCall instructions allow optional results. Keep the result when the surrounding expression needs it; omit it for side-effect-only calls.
call_global
Section titled “call_global”Calls a global function.
$out = call_global "print" $messagecall_method
Section titled “call_method”Calls an instance method. The first variable operand is the receiver.
$out = call_method "get_child" $node $indexcall_super_method
Section titled “call_super_method”Calls a superclass method.
call_super_method "_ready" $selfcall_static_method
Section titled “call_static_method”Calls a static method on a class.
$out = call_static_method "MathUtil" "clamp_i" $value $min $maxcall_intrinsic
Section titled “call_intrinsic”Calls a backend-owned intrinsic registered by name. Implicit conversions that need materialization use this shape, such as c_int_to_float and c_vector3i_to_vector3.
$out = call_intrinsic "intrinsic_name" $argCall arguments are listed in source order. Retain the result only when the surrounding expression needs it.
Coroutine Instructions
Section titled “Coroutine Instructions”Suspends the enclosing coroutine function until the awaited operand produces a result, then publishes that result as the instruction value. At runtime, suspension is a stack switch on the stackful coroutine frame; at the LIR surface await still looks synchronous and does not split basic blocks.
$result = await $operand- It is legal only inside a function whose
is_coroutineattribute istrue; backend validation fails fast when it appears in a plain function. - The operand’s static type selects the dispatch path:
Signal: a one-shot signal wait.compiler::GdccCoroState: a static coroutine-call wait, i.e. the result produced whencall_method,call_super_method, orcall_static_methodtargets a coroutine; such a call must declare its result variable with this type.Variant: runtime dynamic dispatch.
- Any other static type is illegal. Dispatch is purely static-type-driven; there is no name-derived pairing or cross-instruction bookkeeping.
- The frontend publishes the result type: 0-arg signal →
Variant; 1-arg signal → the declared argument type; multi-arg signal →Array[Variant]; coroutine call → the callee’s declared return type (avoidcallee yields aVariantholding nil); dynamic wait →Variant. awaitis a value-producing ordinary instruction, not a block terminator, so entry-block and terminator rules are unaffected.- The single-consumer ownership rules for
compiler::GdccCoroStateare described in Compiler-Only Types.
Property and Static Access
Section titled “Property and Static Access”These instructions represent stable property/static paths, unlike variant_get_named and variant_set_named, which operate through dynamic Variant access.
load_property
Section titled “load_property”Reads a named property from an object.
$value = load_property "position" $nodestore_property
Section titled “store_property”Writes a named property on an object.
store_property "position" $node $valueload_static
Section titled “load_static”Reads a static variable or property.
$value = load_static "GameState" "score"store_static
Section titled “store_static”Writes a static variable or property.
store_static "GameState" "score" $valueMiscellaneous
Section titled “Miscellaneous”Does nothing; useful as a placeholder or test instruction.
nopline_number
Section titled “line_number”Marks the current source line for diagnostics or debug data.
line_number 27assert
Section titled “assert”User-level condition assertion. When the condition is false, the current function enters its stable runtime-error/default-return cleanup path (goto __finally__).
assert $condassert $cond $message$cond must already be bool; truthiness normalization is the frontend lowering’s responsibility. The optional $message must be assignable to String. The instruction has no result, does not mutate object state, requires no lifecycle provenance, and is not a terminator.
assert_object_live
Section titled “assert_object_live”Hard-fail dereference guard. It asserts that an object reference is still live before use; if the object is null or freed, the function enters the same cleanup path as assert.
assert_object_live $nodeIt is used only for method receivers, property receivers, _super chain access, and direct GDCC field/method owners; not for user conditional checks, equality, or ownership operations. self is guaranteed live during method execution and is exempt; RefCounted objects are also exempt because holding a reference guarantees liveness.
Instruction Index
Section titled “Instruction Index”- Data:
literal_bool,literal_int,literal_float,literal_string,literal_string_name,literal_node_path,literal_nil,literal_null,assign. - Construction, lifecycle, and operators:
construct_builtin,construct_array,construct_dictionary,construct_container_literal,construct_object,construct_signal,construct_callable,construct_standalone_callable,construct_lambda,destruct,try_own_object,try_release_object,unary_op,binary_op. - Indexing and dynamic access:
variant_get,variant_get_keyed,variant_get_named,variant_get_indexed,variant_set,variant_set_keyed,variant_set_named,variant_set_indexed. - Type:
get_variant_type,get_class_name,object_cast,builtin_cast,is_instance_of,pack_variant,unpack_variant,variant_is_nil,object_is_null. - Control flow:
goto,go_if,return. - Calls:
call_global,call_method,call_super_method,call_static_method,call_intrinsic. - Coroutines:
await. - Access:
load_property,store_property,load_static,store_static. - Misc:
nop,line_number,assert,assert_object_live.