lowerIrChunkToLuaBytecodeChunk function

LuaBytecodeBinaryChunk lowerIrChunkToLuaBytecodeChunk(
  1. LualikeIrChunk chunk, {
  2. String? chunkName,
})

Mechanically lowers finalized lualike IR into Lua 5.5 bytecode.

This is not an optimization pass. By the time a chunk reaches this layer, register allocation, call shape, closure capture, and control flow must already be decided in IR/SSA (see doc/decisions.md IR contract).

Opcode expansion

Several IR opcodes have no direct bytecode equivalent and are expanded here into 2-3 instruction sequences:

IR opcode Expansion Reason
SHLI a,b,c LOADI tmp,c; SHL a,b,tmp; MMBIN b,tmp,__shl IR means R(b) << c; Lua SHLI means c << R(b)
SUBI a,b,c ADDI a,b,-c; MMBINI … when -c fits signed C; else LOADI+SUB+MMBIN ADDI always does +; SUBI negates and uses __sub
*K with high C LOADK/KX tmp,C; * a,b,tmp; MMBIN … C field too small for large constant indices
GETFIELD/etc with high C LOADK tmp,C; GETTABLE … tmp C field too small

These expansions are purely mechanical — they do not introduce new policy decisions. All shape choices are finalized in IR/SSA.

Debug obligations at this boundary:

  • Copy IR LocalDebugEntry ranges through program-counter remapping.
  • Preserve LocalDebugEntry.register so in-memory prototypes work before serialize; after load, local-register inference recovers them again.
  • Force main LuaBytecodePrototype.lineDefined to 0 (Lua main chunk convention) so the VM does not treat main as a regular function and inject a synthetic (vararg table) local for debug.getlocal.

Implementation

LuaBytecodeBinaryChunk lowerIrChunkToLuaBytecodeChunk(
  LualikeIrChunk chunk, {
  String? chunkName,
}) {
  final mainPrototype = lowerIrPrototypeToLuaBytecodePrototype(
    chunk.mainPrototype,
    sourceName:
        chunk.mainPrototype.debugInfo?.absoluteSourcePath ??
        chunkName ??
        '=(lualike_ir)',
    isMainPrototype: true,
  );
  return LuaBytecodeBinaryChunk(
    header: const LuaBytecodeChunkHeader.official(),
    rootUpvalueCount: mainPrototype.upvalues.length,
    mainPrototype: mainPrototype,
  );
}