One program, all the way down
Let’s take one eight-line Luce program apart. We will keep asking the same question: what became explicit at this stage that was only implied before?
The program
clamp_double doubles a value and caps it at ten. main uses the number of command-line arguments. That number becomes available when the program starts, so the emitted instructions calculate the answer at run time.
func clamp_double(value: i64, limit: i64) -> i64:
let doubled = value * 2
if doubled > limit:
return limit
return doubled
func main(args: list[str]):
print(str(clamp_double(len(args), 10)))At this point, a reader supplies a lot of meaning automatically: value refers to the first parameter, multiplication happens before assignment, the indented lines belong to the function, and both returns produce an i64. A compiler has to make every one of those facts concrete.
1. The lexer finds the pieces
The lexer reads characters and emits a flat list. Words become names or keywords. Punctuation becomes its own tokens. Newlines and indentation become layout tokens, giving the parser explicit block boundaries.
At this stage each token carries its kind and source position. Name resolution and type checking happen after the parser has built a tree.
2. The parser finds the shape
The parser turns the flat tokens into a tree. Multiplication sits inside the initializer; that initializer sits inside the binding; the binding sits inside the function body.
let doubled = value * 2The parser records the nestinglet binding: doubledmultiplyname: valueinteger literal: 2
This tree records the grammatical reading of the source. The next stage resolves the names and checks whether the operations are valid.
3. Semantics resolves names and types
Now the compiler can answer questions that depend on each other. Which value is this? It is the first parameter. What is its type? i64. Can it multiply with the literal 2? Yes—the literal can land directly as i64. What type does doubled get? Also i64.
value * 2clamp_doubledoubled: i64This checked tree is HIR. It resembles the source and carries the resolved name, integer width, result type, and overflow rule that lowering must preserve.
4. MIR spells out the work
MIR is Luce’s own intermediate representation—the IR before LLVM. Structured source becomes numbered registers and basic blocks. A block is simply a straight run of instructions ending in a jump, branch, return, error, or trap.
r0 = local_get %0 load value
r1 = const 2 the literal is now i64
r2 = multiply.i64 r0, r1 checked i64 multiplication
local_set %2, r2 store doubled
r6 = greater.i64 r4, r5 compare doubled and limit
branch r6, b1, b2 choose one return blockMIR records the facts needed for execution: integer widths, registers, block targets, intrinsics, and cleanup of the temporary string in main. Earlier stages have already used indentation, precedence, and name lookup to determine this instruction sequence.
5. LLVM IR adds the target-facing details
The backend turns verified MIR into LLVM IR. This is still readable text, but it speaks in target-neutral machine operations and Luce’s runtime ABI. The multiplication becomes LLVM’s checked-overflow intrinsic:
%12 = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 %11, i64 2)
%13 = extractvalue { i64, i1 } %12, 0 the product
%14 = extractvalue { i64, i1 } %12, 1 did it overflow?
br i1 %14, label %19, label %22 trap or continueThe function has more parameters than the source did. Luce passes runtime state, call-depth information, and result storage through a stable internal calling convention. Those details were irrelevant to source typing, but they are necessary once code can call the runtime or report a source-aware trap.
6. LLVM chooses ARM64 instructions
LLVM now optimizes the module and selects instructions for the build target. It inlines clamp_double into luce_main, replaces multiplication by two with a left shift, and replaces the two return branches with a conditional select.
mov x9, #0x4000000000000000
cmn x8, x9Load 2⁶², then add it only to set flags. This is a compact way to test whether doubling a signed 64-bit number would leave its legal range.
b.mi overflowIf the sign flags say the input is outside the safe half-range, branch to the code that raises Luce’s integer-overflow trap.
lsl x8, x8, #1Shift left by one bit. For a safe signed integer, this is exactly multiplication by two.
cmp x8, #0xaCompare the doubled value with hexadecimal a, which is decimal ten.
csel x8, x8, x9, ltIf the doubled value is less than ten, keep it; otherwise select the register holding ten.
Luce defines the result and overflow behavior. LLVM then chooses instructions that implement those rules for ARM64. Here a shift performs the multiplication and csel implements the cap from the comparison flags.
Read the generated files
The panel below contains every line from the trace snapshot. Open the instruction guide above each low-level file, then scroll the file or open it in a full browser tab for searching.
Luce source. Names and indentation describe the program for a reader.
open source file
func clamp_double(value: i64, limit: i64) -> i64:
let doubled = value * 2
if doubled > limit:
return limit
return doubled
func main(args: list[str]):
print(str(clamp_double(len(args), 10)))
Luce MIR. Types, registers, blocks, calls, and lifetime operations form the instruction plan sent to the LLVM backend.
open MIR fileMIR instruction guide hover, focus, or tap dotted terms
r7 is one typed intermediate value, %2 is a local storage slot, and b1 is a basic block. Read each block from top to bottom, then follow its final control-flow instruction.
Hover or focus a dotted term in the file for its explanation. On a touch screen, tap a term to keep the note open and tap elsewhere to close it. The list includes the operations that appear in this file.
func …- Starts one MIR function and lists the source-level parameters with their resolved types.
local %N- Reserves a typed local slot. The slot gives later blocks a stable place to read or replace.
b0, b1, …- Names a basic block. Control enters at the label and runs until a branch, jump, trap, or return chooses what happens next.
r0, r1, …- Names one typed intermediate result. Each register is assigned once, which makes data flow explicit.
local_get- Reads a local slot into a fresh MIR register so this use has a precise value and type.
local_set- Writes a register into a local slot. Later blocks read that slot after control-flow paths join.
const- Creates a typed literal such as 2 or 10 inside the instruction stream.
multiply.i64- Multiplies signed 64-bit integers and carries Luce's overflow behavior into the backend.
greater.i64- Compares two signed 64-bit values and produces a Boolean register for a later branch.
intrinsic NAME- Invokes a language operation with runtime rules recorded by the compiler. The name identifies the operation.
intrinsic len- Reads the logical length defined by the value's type, such as argument count or Unicode-scalar count.
intrinsic str_value- Converts a typed value into owned text through Luce's string conversion rules.
intrinsic print- Sends text through the installed host output capability.
intrinsic drop_storage- Releases any reference held by a temporary value and leaves that temporary in its empty state.
call NAME- Calls a resolved function directly. MIR already knows the target and argument order.
branch CONDITION, TRUE, FALSE- Chooses one of two basic blocks from a Boolean register.
ret- Returns the current function's result to its caller and closes the current control-flow path.
func clamp_double(value: i64, limit: i64) -> i64
local %2 doubled: i64
b0:
r0 = local_get %0
r1 = const 2
r2 = multiply.i64 r0, r1
local_set %2, r2
r4 = local_get %2
r5 = local_get %1
r6 = greater.i64 r4, r5
branch r6, b1, b2
b1:
r8 = local_get %1
ret r8
b2:
r10 = local_get %2
ret r10
func main(args: list[str]) -> None
local %1 (temporary): str
b0:
r0 = local_get %0
r1 = intrinsic len, r0
r2 = const 10
r3 = call clamp_double, r1, r2
r4 = intrinsic str_value, r3
local_set %1, r4
intrinsic print, r4
r7 = local_get %1
r8 = intrinsic drop_storage, r7
local_set %1, r8
ret
LLVM IR. The backend expands MIR into typed memory operations, calls, checks, and control-flow blocks for LLVM.
open LLVM IR fileLLVM IR instruction guide hover, focus, or tap dotted terms
%7 is an SSA value, i64 is a 64-bit integer type, ptr is a pointer, and @name is a module symbol. Numbered labels divide the function into basic blocks.
Hover or focus a dotted term in the file for its explanation. On a touch screen, tap a term to keep the note open and tap elsewhere to close it. The list includes the operations that appear in this file.
@name = …- Defines module data such as text constants, source positions, function records, and the artifact identity tag.
define … @name- Begins a generated function. The parameter attributes tell LLVM which pointers are valid, writable, or read-only.
declare … @name- Declares a runtime or LLVM helper whose body is provided elsewhere.
numeric label- Starts an LLVM basic block. Every branch names one of these labels as its destination.
%0, %1, …- Names an SSA value. Each name is assigned once, allowing LLVM to trace definitions and uses directly.
alloca- Reserves a stack slot for a local value, return area, or temporary aggregate.
getelementptr- Calculates the address of a field or indexed element while preserving LLVM's type and bounds information.
load- Copies a typed value from memory into an SSA value.
store- Copies an SSA value into a stack slot, object field, return area, or runtime structure.
icmp- Compares integers or pointers and produces the one-bit condition consumed by a branch or select.
br- Transfers control to another block. With an
i1operand it chooses between two destinations. select- Chooses one of two SSA values from a condition, which can become a branch-free machine instruction.
call- Invokes a generated function, a runtime helper, a host callback, or an LLVM intrinsic.
extractvalue- Reads one field from an SSA aggregate, such as the result and overflow bit returned together.
insertvalue- Builds an SSA aggregate one field at a time.
ptrtoint- Encodes a pointer as an integer field inside Luce's uniform runtime value representation.
inttoptr- Recovers a pointer from the integer field of Luce's uniform runtime value representation.
zext- Widens an unsigned value by filling the new high bits with zero.
trunc- Keeps the low bits while converting to a narrower integer representation.
lshr- Shifts bits right and fills the high side with zero; this is useful for unpacking handle fields.
and- Combines or masks bits, often to inspect a flag in a packed handle or status value.
or- Sets or combines bits in a packed value.
sub- Subtracts integer values; the generated entry code also uses it to update the call-depth budget.
mul- Multiplies integers after the language-specific checks have been represented.
ret- Returns a status or value and ends the current basic block.
@llvm.smul.with.overflow.i64- Returns the signed product and a one-bit overflow result together. Luce branches to its overflow trap when that bit is set.
@llvm.memcpy.inline- Copies a small fixed-size value representation; LLVM can expand it directly for the target.
@luce_rt_open- Creates the per-run runtime state and installs the table used to turn function numbers into source-aware traces.
@luce_rt_close- Closes the per-run runtime state after the final status and reports have been collected.
@luce_rt_args_list- Builds the owned
list[str]passed tomainfrom the host's argument callbacks. @luce_rt_status- Combines the generated function result with runtime trap, error, exhaustion, and exit state.
@luce_rt_report_error- Sends an uncaught recoverable error through the host's error-report callback.
@luce_rt_report- Sends the completed trap report through the host callback selected by the entry wrapper.
@luce_rt_leaked- Reads the live-object count used to detect references that remain after program cleanup.
@luce_rt_exhaust- Records allocation exhaustion in the current run so the entry wrapper returns the corresponding status.
@luce_rt_raise- Records a source-aware trap in the runtime so every execution path reports the same code and location.
@luce_rt_unwound- Adds one generated function frame while a trap or uncaught error travels toward the entry point.
@luce_rt_release- Removes a strong owner and triggers destruction at zero.
@luce_rt_drop_storage- Releases reference-bearing fields in a temporary runtime value.
@luce_rt_str- Converts a uniform runtime value into owned UTF-8 text.
@luce_rt_files_install- Copies file-related callbacks from the host table into the current runtime context.
@luce_rt_sockets_install- Copies network callbacks from the host table into the current runtime context.
@luce_rt_graphics_install- Copies window and graphics callbacks from the host table into the current runtime context.
!prof- Attaches branch-probability metadata so LLVM can place common and failure paths efficiently.
; ModuleID = '/Users/sedov/Dev/luciaos/www/lucelang/examples/journey.luc'
source_filename = "/Users/sedov/Dev/luciaos/www/lucelang/examples/journey.luc"
target triple = "arm64-apple-darwin24.6.0"
@luce.text.0 = private unnamed_addr constant [16 x i8] c"integer overflow"
@luce.text.1 = private unnamed_addr constant [0 x i8] zeroinitializer
@luce.text.2 = private unnamed_addr constant [21 x i8] c"null object reference"
@luce.text.3 = private unnamed_addr constant [22 x i8] c"object used after free"
@luce.text.4 = private unnamed_addr constant [19 x i8] c"call depth exceeded"
@luce.text.5 = private unnamed_addr constant [24 x i8] c"host service unavailable"
@luce.text.6 = private unnamed_addr constant [12 x i8] c"clamp_double"
@luce.text.7 = private unnamed_addr constant [58 x i8] c"/Users/sedov/Dev/luciaos/www/lucelang/examples/journey.luc"
@luce.origins.0 = private constant [12 x { i32, i32 }] [{ i32, i32 } { i32 2, i32 5 }, { i32, i32 } { i32 2, i32 5 }, { i32, i32 } { i32 2, i32 5 }, { i32, i32 } { i32 2, i32 5 }, { i32, i32 } { i32 3, i32 5 }, { i32, i32 } { i32 3, i32 5 }, { i32, i32 } { i32 3, i32 5 }, { i32, i32 } { i32 3, i32 5 }, { i32, i32 } { i32 4, i32 9 }, { i32, i32 } { i32 4, i32 9 }, { i32, i32 } { i32 5, i32 5 }, { i32, i32 } { i32 5, i32 5 }]
@luce.text.8 = private unnamed_addr constant [4 x i8] c"main"
@luce.origins.1 = private constant [11 x { i32, i32 }] [{ i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }, { i32, i32 } { i32 8, i32 5 }]
@luce.functions = private constant [2 x { ptr, i64, ptr, i64, ptr, i64 }] [{ ptr, i64, ptr, i64, ptr, i64 } { ptr @luce.text.6, i64 12, ptr @luce.text.7, i64 58, ptr @luce.origins.0, i64 12 }, { ptr, i64, ptr, i64, ptr, i64 } { ptr @luce.text.8, i64 4, ptr @luce.text.7, i64 58, ptr @luce.origins.1, i64 11 }]
@luce_artifact = constant { i64, i64, i64, i32, i32, i32, i32, { i32, [52 x i8] } } { i64 23734338332087628, i64 0, i64 -7092229304759745108, i32 3, i32 29, i32 1, i32 0, { i32, [52 x i8] } { i32 18, [52 x i8] c"aarch64-macos-none\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00\00" } }
define internal i32 @luce.0.clamp_double(ptr align 8 nocapture readonly nonnull dereferenceable(472) noundef %0, ptr align 8 nocapture nonnull noundef %1, i64 noundef %2, i64 %3, i64 %4, ptr align 8 nocapture nonnull dereferenceable(8) writeonly noundef %5) {
6:
%7 = alloca i64, align 8
%8 = alloca i64, align 8
%9 = alloca i64, align 8
store i64 %3, ptr %7, align 8
store i64 %4, ptr %8, align 8
store i64 0, ptr %9, align 8
br label %10
10:
%11 = load i64, ptr %7, align 8
%12 = call { i64, i1 } @llvm.smul.with.overflow.i64(i64 %11, i64 2)
%13 = extractvalue { i64, i1 } %12, 0
%14 = extractvalue { i64, i1 } %12, 1
br i1 %14, label %19, label %22, !prof !0
15:
%16 = load i64, ptr %8, align 8
store i64 %16, ptr %5, align 8
ret i32 0
17:
%18 = load i64, ptr %9, align 8
store i64 %18, ptr %5, align 8
ret i32 0
19:
%20 = extractvalue { ptr, i64 } { ptr @luce.text.0, i64 16 }, 0
%21 = extractvalue { ptr, i64 } { ptr @luce.text.0, i64 16 }, 1
call void @luce_rt_raise(ptr %1, i32 0, ptr %20, i64 %21)
call void @luce_rt_unwound(ptr %1, i32 0, i32 2)
ret i32 1
22:
store i64 %13, ptr %9, align 8
%23 = load i64, ptr %9, align 8
%24 = load i64, ptr %8, align 8
%25 = icmp sgt i64 %23, %24
br i1 %25, label %15, label %17
}
define internal i32 @luce.1.main(ptr align 8 nocapture readonly nonnull dereferenceable(472) noundef %0, ptr align 8 nocapture nonnull noundef %1, i64 noundef %2, i64 %3) {
4:
%5 = alloca i64, align 8
%6 = alloca { i8, i8, [6 x i8], i64, i64 }, align 8
store i64 %3, ptr %5, align 8
%7 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 0
store i8 4, ptr %7, align 1
%8 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 1
store i8 -1, ptr %8, align 1
%9 = extractvalue { ptr, i64 } { ptr @luce.text.1, i64 0 }, 0
%10 = ptrtoint ptr %9 to i64
%11 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 3
store i64 %10, ptr %11, align 8
%12 = extractvalue { ptr, i64 } { ptr @luce.text.1, i64 0 }, 1
%13 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 4
store i64 %12, ptr %13, align 8
%14 = sub nsw i64 %2, 1
%15 = alloca i64, align 8
%16 = alloca { i8, i8, [6 x i8], i64, i64 }, align 8
%17 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %16, i32 0, i32 0
store i8 2, ptr %17, align 1
%18 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %16, i32 0, i32 4
store i64 0, ptr %18, align 8
%19 = alloca { i8, i8, [6 x i8], i64, i64 }, align 8
%20 = alloca { i8, i8, [6 x i8], i64, i64 }, align 8
br label %21
21:
%22 = load i64, ptr %5, align 8
%23 = trunc i64 %22 to i32
%24 = lshr i64 %22, 32
%25 = trunc i64 %24 to i32
%26 = icmp eq i32 %23, -1
br i1 %26, label %27, label %30, !prof !0
27:
%28 = extractvalue { ptr, i64 } { ptr @luce.text.2, i64 21 }, 0
%29 = extractvalue { ptr, i64 } { ptr @luce.text.2, i64 21 }, 1
call void @luce_rt_raise(ptr %1, i32 14, ptr %28, i64 %29)
call void @luce_rt_unwound(ptr %1, i32 1, i32 1)
ret i32 1
30:
%31 = getelementptr inbounds i8, ptr %1, i64 96
%32 = load ptr, ptr %31, align 8!alias.scope !1, !noalias !2
%33 = zext i32 %23 to i64
%34 = mul nsw i64 %33, 112
%35 = getelementptr inbounds i8, ptr %32, i64 %34
%36 = getelementptr inbounds i8, ptr %35, i64 96
%37 = load i32, ptr %36, align 4, !alias.scope !1, !noalias !2
%38 = icmp ne i32 %37, %25
br i1 %38, label %39, label %42, !prof !0
39:
%40 = extractvalue { ptr, i64 } { ptr @luce.text.3, i64 22 }, 0
%41 = extractvalue { ptr, i64 } { ptr @luce.text.3, i64 22 }, 1
call void @luce_rt_raise(ptr %1, i32 13, ptr %40, i64 %41)
call void @luce_rt_unwound(ptr %1, i32 1, i32 1)
ret i32 1
42:
%43 = and i32 %37, 1
%44 = icmp ne i32 %43, 0
br i1 %44, label %45, label %48, !prof !0
45:
%46 = extractvalue { ptr, i64 } { ptr @luce.text.3, i64 22 }, 0
%47 = extractvalue { ptr, i64 } { ptr @luce.text.3, i64 22 }, 1
call void @luce_rt_raise(ptr %1, i32 13, ptr %46, i64 %47)
call void @luce_rt_unwound(ptr %1, i32 1, i32 1)
ret i32 1
48:
%49 = getelementptr inbounds i8, ptr %35, i64 16
%50 = load i64, ptr %49, align 8!alias.scope !1, !noalias !2
%51 = load ptr, ptr %35, align 8, !alias.scope !1, !noalias !2
%52 = icmp slt i64 %14, 1
br i1 %52, label %53, label %56, !prof !0
53:
%54 = extractvalue { ptr, i64 } { ptr @luce.text.4, i64 19 }, 0
%55 = extractvalue { ptr, i64 } { ptr @luce.text.4, i64 19 }, 1
call void @luce_rt_raise(ptr %1, i32 6, ptr %54, i64 %55)
call void @luce_rt_unwound(ptr %1, i32 1, i32 3)
ret i32 1
56:
%57 = call i32 @luce.0.clamp_double(ptr %0, ptr %1, i64 %14, i64 %50, i64 10, ptr %15)
%58 = icmp ne i32 %57, 0
br i1 %58, label %59, label %60, !prof !0
59:
call void @luce_rt_unwound(ptr %1, i32 1, i32 3)
ret i32 1
60:
%61 = load i64, ptr %15, align 8
%62 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %16, i32 0, i32 3
store i64 %61, ptr %62, align 8
%63 = call i32 @luce_rt_str(ptr %1, ptr %16, ptr %19)
%64 = icmp ne i32 %63, 0
br i1 %64, label %65, label %66, !prof !0
65:
call void @luce_rt_unwound(ptr %1, i32 1, i32 4)
ret i32 1
66:
%67 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %19, i32 0, i32 3
%68 = load i64, ptr %67, align 8
%69 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %19, i32 0, i32 1
%70 = load i8, ptr %69, align 1
%71 = icmp eq i8 %70, -1
%72 = inttoptr i64 %68 to ptr
%73 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %19, i32 0, i32 2
%74 = select i1 %71, ptr %72, ptr %73
%75 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %19, i32 0, i32 4
%76 = load i64, ptr %75, align 8
%77 = zext i8 %70 to i64
%78 = select i1 %71, i64 %76, i64 %77
%79 = insertvalue { ptr, i64 } poison, ptr %74, 0
%80 = insertvalue { ptr, i64 } %79, i64 %78, 1
call void @llvm.memcpy.inline.p0.p0.i64(ptr align 8 %6, ptr align 8 %19, i64 24, i1 false)
%81 = extractvalue { ptr, i64 } %80, 0
%82 = extractvalue { ptr, i64 } %80, 1
%83 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 1
%84 = load ptr, ptr %83, align 8
%85 = icmp eq ptr %84, null
br i1 %85, label %86, label %89, !prof !0
86:
%87 = extractvalue { ptr, i64 } { ptr @luce.text.5, i64 24 }, 0
%88 = extractvalue { ptr, i64 } { ptr @luce.text.5, i64 24 }, 1
call void @luce_rt_raise(ptr %1, i32 9, ptr %87, i64 %88)
call void @luce_rt_unwound(ptr %1, i32 1, i32 6)
ret i32 1
89:
%90 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 0
%91 = load ptr, ptr %90, align 8
%92 = call i32 %84(ptr %91, ptr %81, i64 %82)
%93 = icmp eq i32 %92, -1
br i1 %93, label %94, label %95, !prof !0
94:
call void @luce_rt_exhaust(ptr %1)
ret i32 1
95:
%96 = icmp ne i32 %92, 0
%97 = icmp ne i32 %92, 1
%98 = and i1 %96, %97
br i1 %98, label %99, label %102, !prof !0
99:
%100 = extractvalue { ptr, i64 } { ptr @luce.text.5, i64 24 }, 0
%101 = extractvalue { ptr, i64 } { ptr @luce.text.5, i64 24 }, 1
call void @luce_rt_raise(ptr %1, i32 9, ptr %100, i64 %101)
call void @luce_rt_unwound(ptr %1, i32 1, i32 6)
ret i32 1
102:
%103 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 3
%104 = load i64, ptr %103, align 8
%105 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 1
%106 = load i8, ptr %105, align 1
%107 = icmp eq i8 %106, -1
%108 = inttoptr i64 %104 to ptr
%109 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 2
%110 = select i1 %107, ptr %108, ptr %109
%111 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %6, i32 0, i32 4
%112 = load i64, ptr %111, align 8
%113 = zext i8 %106 to i64
%114 = select i1 %107, i64 %112, i64 %113
%115 = insertvalue { ptr, i64 } poison, ptr %110, 0
%116 = insertvalue { ptr, i64 } %115, i64 %114, 1
call void @luce_rt_drop_storage(ptr %1, ptr %6, ptr %20)
%117 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %20, i32 0, i32 3
%118 = load i64, ptr %117, align 8
%119 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %20, i32 0, i32 1
%120 = load i8, ptr %119, align 1
%121 = icmp eq i8 %120, -1
%122 = inttoptr i64 %118 to ptr
%123 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %20, i32 0, i32 2
%124 = select i1 %121, ptr %122, ptr %123
%125 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %20, i32 0, i32 4
%126 = load i64, ptr %125, align 8
%127 = zext i8 %120 to i64
%128 = select i1 %121, i64 %126, i64 %127
%129 = insertvalue { ptr, i64 } poison, ptr %124, 0
%130 = insertvalue { ptr, i64 } %129, i64 %128, 1
call void @llvm.memcpy.inline.p0.p0.i64(ptr align 8 %6, ptr align 8 %20, i64 24, i1 false)
ret i32 0
}
; Function Attrs: nounwind speculatable willreturn nofree nosync nocallback memory(none)
declare { i64, i1 } @llvm.smul.with.overflow.i64(i64 %0, i64 %1) #0
; Function Attrs: nounwind cold willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
declare void @luce_rt_raise(ptr nocapture nonnull noundef %0, i32 %1, ptr nocapture readonly %2, i64 %3) #1
; Function Attrs: nounwind cold willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
declare void @luce_rt_unwound(ptr nocapture nonnull noundef %0, i32 %1, i32 %2) #1
; Function Attrs: nounwind willreturn memory(read, argmem: readwrite, inaccessiblemem: readwrite)
declare i32 @luce_rt_str(ptr nocapture nonnull noundef %0, ptr align 8 nocapture readonly nonnull dereferenceable(24) noundef %1, ptr align 8 nocapture nonnull dereferenceable(24) writeonly noundef %2) #2
; Function Attrs: nounwind willreturn nofree nocallback memory(argmem: readwrite)
declare void @llvm.memcpy.inline.p0.p0.i64(ptr noalias nocapture writeonly %0, ptr noalias nocapture readonly %1, i64 %2, i1 immarg %3) #3
; Function Attrs: nounwind cold willreturn memory(argmem: write)
declare void @luce_rt_exhaust(ptr nocapture nonnull noundef %0) #4
; Function Attrs: nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite)
declare void @luce_rt_drop_storage(ptr nocapture nonnull noundef %0, ptr align 8 nocapture readonly nonnull dereferenceable(24) noundef %1, ptr align 8 nocapture nonnull dereferenceable(24) writeonly noundef %2) #5
define i32 @luce_main(ptr align 8 nocapture readonly nonnull dereferenceable(472) noundef %0) {
1:
%2 = alloca i64, align 8
%3 = alloca i32, align 8
store i64 256, ptr %2, align 8
%4 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 0
%5 = load ptr, ptr %4, align 8
%6 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 14
%7 = load ptr, ptr %6, align 8
%8 = icmp eq ptr %7, null
br i1 %8, label %11, label %9
9:
%10 = call i64 %7(ptr %5)
store i64 %10, ptr %2, align 8
br label %11
11:
%12 = load i64, ptr %2, align 8
%13 = call ptr @luce_rt_open(ptr @luce.functions, i64 2)
%14 = icmp eq ptr %13, null
br i1 %14, label %15, label %16, !prof !0
15:
ret i32 2
16:
%17 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 28
%18 = load ptr, ptr %17, align 8
%19 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 54
%20 = load ptr, ptr %19, align 8
%21 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 55
%22 = load ptr, ptr %21, align 8
%23 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 56
%24 = load ptr, ptr %23, align 8
%25 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 57
%26 = load ptr, ptr %25, align 8
%27 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 58
%28 = load ptr, ptr %27, align 8
%29 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 29
%30 = load ptr, ptr %29, align 8
%31 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 30
%32 = load ptr, ptr %31, align 8
%33 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 31
%34 = load ptr, ptr %33, align 8
%35 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 32
%36 = load ptr, ptr %35, align 8
call void @luce_rt_files_install(ptr %13, ptr %5, ptr %18, ptr %20, ptr %22, ptr %24, ptr %26, ptr %28, ptr %30, ptr %32, ptr %34, ptr %36)
%37 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 48
%38 = load ptr, ptr %37, align 8
%39 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 49
%40 = load ptr, ptr %39, align 8
%41 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 50
%42 = load ptr, ptr %41, align 8
%43 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 51
%44 = load ptr, ptr %43, align 8
%45 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 52
%46 = load ptr, ptr %45, align 8
call void @luce_rt_sockets_install(ptr %13, ptr %5, ptr %38, ptr %40, ptr %42, ptr %44, ptr %46)
%47 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 40
%48 = load ptr, ptr %47, align 8
%49 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 41
%50 = load ptr, ptr %49, align 8
%51 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 42
%52 = load ptr, ptr %51, align 8
%53 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 43
%54 = load ptr, ptr %53, align 8
%55 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 44
%56 = load ptr, ptr %55, align 8
%57 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 45
%58 = load ptr, ptr %57, align 8
%59 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 46
%60 = load ptr, ptr %59, align 8
%61 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 47
%62 = load ptr, ptr %61, align 8
call void @luce_rt_graphics_install(ptr %13, ptr %5, ptr %48, ptr %50, ptr %52, ptr %54, ptr %56, ptr %58, ptr %60, ptr %62)
%63 = icmp slt i64 %12, 1
br i1 %63, label %64, label %65, !prof !0
64:
call void @luce_rt_raise(ptr %13, i32 6, ptr @luce.text.4, i64 19)
store i32 1, ptr %3, align 8
br label %73
65:
%66 = alloca { i8, i8, [6 x i8], i64, i64 }, align 8
%67 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 4
%68 = load ptr, ptr %67, align 8
%69 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 5
%70 = load ptr, ptr %69, align 8
%71 = call i32 @luce_rt_args_list(ptr %13, ptr %5, ptr %68, ptr %70, ptr %66)
%72 = icmp ne i32 %71, 0
br i1 %72, label %77, label %78, !prof !0
73:
%74 = load i32, ptr %3, align 8
%75 = icmp eq i32 %74, 1
%76 = icmp eq i32 %74, 2
br i1 %75, label %83, label %86, !prof !0
77:
store i32 1, ptr %3, align 8
br label %73
78:
%79 = getelementptr inbounds { i8, i8, [6 x i8], i64, i64 }, ptr %66, i32 0, i32 3
%80 = load i64, ptr %79, align 8
%81 = call i32 @luce.1.main(ptr %0, ptr %13, i64 %12, i64 %80)
store i32 %81, ptr %3, align 8
%82 = call i32 @luce_rt_release(ptr %13, ptr %66)
br label %73
83:
%84 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 2
%85 = load ptr, ptr %84, align 8
call void @luce_rt_report(ptr %13, ptr %5, ptr %85)
br label %86
86:
br i1 %76, label %87, label %90, !prof !0
87:
%88 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 15
%89 = load ptr, ptr %88, align 8
call void @luce_rt_report_error(ptr %13, ptr %5, ptr %89)
br label %90
90:
%91 = call i32 @luce_rt_status(ptr %13, i32 %74)
%92 = icmp eq i32 %91, 2
%93 = getelementptr inbounds { ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr, ptr }, ptr %0, i32 0, i32 3
%94 = load ptr, ptr %93, align 8
%95 = icmp eq ptr %94, null
%96 = or i1 %95, %92
br i1 %96, label %97, label %98
97:
call void @luce_rt_close(ptr %13)
ret i32 %91
98:
%99 = call i64 @luce_rt_leaked(ptr %13)
call void %94(ptr %5, i64 %99)
br label %97
}
; Function Attrs: nounwind willreturn memory(argmem: read, inaccessiblemem: readwrite)
declare noalias ptr @luce_rt_open(ptr readonly %0, i64 %1) #6
; Function Attrs: nounwind willreturn memory(argmem: readwrite)
declare void @luce_rt_files_install(ptr nocapture nonnull noundef %0, ptr %1, ptr %2, ptr %3, ptr %4, ptr %5, ptr %6, ptr %7, ptr %8, ptr %9, ptr %10, ptr %11) #7
; Function Attrs: nounwind willreturn memory(readwrite)
declare void @luce_rt_sockets_install(ptr nocapture nonnull noundef %0, ptr %1, ptr %2, ptr %3, ptr %4, ptr %5, ptr %6) #8
; Function Attrs: nounwind willreturn memory(argmem: readwrite)
declare void @luce_rt_graphics_install(ptr nocapture nonnull noundef %0, ptr %1, ptr %2, ptr %3, ptr %4, ptr %5, ptr %6, ptr %7, ptr %8, ptr %9) #7
; Function Attrs: nounwind memory(readwrite)
declare i32 @luce_rt_args_list(ptr nocapture nonnull noundef %0, ptr %1, ptr %2, ptr %3, ptr align 8 nocapture nonnull dereferenceable(24) writeonly noundef %4) #9
; Function Attrs: nounwind memory(readwrite)
declare i32 @luce_rt_release(ptr nocapture nonnull noundef %0, ptr align 8 nocapture readonly nonnull dereferenceable(24) noundef %1) #9
; Function Attrs: cold
declare void @luce_rt_report(ptr nocapture nonnull noundef %0, ptr %1, ptr %2) #10
; Function Attrs: cold
declare void @luce_rt_report_error(ptr nocapture nonnull noundef %0, ptr %1, ptr %2) #10
; Function Attrs: nounwind willreturn memory(argmem: read)
declare i32 @luce_rt_status(ptr nocapture nonnull noundef %0, i32 %1) #11
; Function Attrs: nounwind willreturn memory(argmem: read)
declare i64 @luce_rt_leaked(ptr nocapture nonnull noundef %0) #11
; Function Attrs: nounwind memory(readwrite)
declare void @luce_rt_close(ptr nocapture nonnull noundef %0) #9
attributes #0 = { nounwind speculatable willreturn nofree nosync nocallback memory(none) }
attributes #1 = { nounwind cold willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) }
attributes #2 = { nounwind willreturn memory(read, argmem: readwrite, inaccessiblemem: readwrite) }
attributes #3 = { nounwind willreturn nofree nocallback memory(argmem: readwrite) }
attributes #4 = { nounwind cold willreturn memory(argmem: write) }
attributes #5 = { nounwind willreturn memory(argmem: readwrite, inaccessiblemem: readwrite) }
attributes #6 = { nounwind willreturn memory(argmem: read, inaccessiblemem: readwrite) }
attributes #7 = { nounwind willreturn memory(argmem: readwrite) }
attributes #8 = { nounwind willreturn memory(readwrite) }
attributes #9 = { nounwind memory(readwrite) }
attributes #10 = { cold }
attributes #11 = { nounwind willreturn memory(argmem: read) }
!0 = !{!"branch_weights", i32 1, i32 2000}
!1 = !{!3}
!2 = !{!4}
!3 = !{!"luce.rows", !5}
!4 = !{!"luce.elements", !5}
!5 = !{!"luce.alias"}
ARM64 object code. LLVM selected these instructions, registers, calls, and branch conditions for this target.
open assembly fileARM64 instruction guide hover, focus, or tap dotted terms
x8 is a 64-bit register; w8 is its low 32 bits. A leading # marks a constant, brackets describe a memory address, and conditional instructions read the processor flags set by the preceding arithmetic or comparison.
Hover or focus a dotted term in the file for its explanation. On a touch screen, tap a term to keep the note open and tap elsewhere to close it. The list includes the operations that appear in this file.
mov- Copies a register value or loads a small constant. Calls also use it to place arguments in the required registers.
add- Adds registers or a constant. Address setup and stack restoration frequently use this instruction.
sub- Subtracts registers or a constant. Function prologues use it to reserve stack space.
cmp- Performs a subtraction solely to set condition flags; a following branch or select consumes those flags.
cmn- Performs an addition solely to set condition flags. The checked multiply-by-two path uses it to recognize unsafe inputs.
csel- Selects one of two registers from the current condition flags. The clamp uses it to choose the doubled value or limit.
ldr- Loads a register from memory using a scaled address offset.
str- Stores one register into memory using a scaled address offset.
ldp- Loads two adjacent registers, often restoring saved registers or reading neighboring fields.
stp- Stores two adjacent registers, often saving the caller's registers in a function prologue.
ldur- Loads from an unscaled byte offset, which is useful for stack slots below the frame pointer.
stur- Stores to an unscaled byte offset, which is useful for stack slots below the frame pointer.
ldurb- Loads one byte using an unscaled byte offset.
sturb- Stores one byte using an unscaled byte offset.
adrp- Loads the page address of a symbol. The linker fills in the final page-relative relocation.
bl- Calls a direct target and records the return address in
x30. blr- Calls the function address held in a register, as required for host callbacks and function values.
b- Jumps to another instruction address.
b.eq- Branches when the previous comparison reported equality.
b.ne- Branches when the previous comparison reported different values.
b.le- Branches when the signed left operand was smaller or equal.
b.mi- Branches when the negative flag is set. Here it enters the overflow path after
cmndetects an unsafe doubling input. b.hs- Branches when an unsigned comparison found higher-or-same, represented by a set carry flag.
cbz- Compares a register with zero and branches when it is zero.
cbnz- Compares a register with zero and branches when it contains a nonzero value.
tbnz- Tests one selected bit and branches when that bit is set; packed runtime flags use this form.
lsl- Shifts bits left. A shift by one multiplies by two after the overflow check has established a safe range.
lsr- Shifts bits right and fills with zero, often extracting the upper field of a packed handle.
umaddl- Widens two 32-bit unsigned operands, multiplies them, and adds a 64-bit base address.
orr- Combines bits, commonly setting tag or flag bits in a packed value.
ret- Returns to the address in
x30and ends the current machine function.
/Users/sedov/Dev/luciaos/www/lucelang/out/traces/journey.o: file format mach-o arm64
Disassembly of section __TEXT,__text:
0000000000000000 <ltmp0>:
; luce_main():
0: stp x24, x23, [sp, #-0x40]!
4: stp x22, x21, [sp, #0x10]
8: stp x20, x19, [sp, #0x20]
c: stp x29, x30, [sp, #0x30]
10: add x29, sp, #0x30
14: sub sp, sp, #0x70
18: ldr x8, [x0, #0x70]
1c: ldr x19, [x0]
20: mov x20, x0
24: cbz x8, 0x50 <ltmp0+0x50>
28: mov x0, x19
2c: blr x8
30: mov x23, x0
34: adrp x0, 0x0 <ltmp0>
38: add x0, x0, #0x0
3c: mov w1, #0x2 ; =2
40: mov w22, #0x2 ; =2
44: bl 0x44 <ltmp0+0x44>
48: cbnz x0, 0x6c <ltmp0+0x6c>
4c: b 0x21c <ltmp0+0x21c>
50: mov w23, #0x100 ; =256
54: adrp x0, 0x0 <ltmp0>
58: add x0, x0, #0x0
5c: mov w1, #0x2 ; =2
60: mov w22, #0x2 ; =2
64: bl 0x64 <ltmp0+0x64>
68: cbz x0, 0x21c <ltmp0+0x21c>
6c: ldp x3, x4, [x20, #0x1b0]
70: mov x21, x0
74: ldp x5, x6, [x20, #0x1c0]
78: ldr x2, [x20, #0xe0]
7c: ldp x8, x9, [x20, #0xf8]
80: ldr x7, [x20, #0x1d0]
84: ldur q0, [x20, #0xe8]
88: sub sp, sp, #0x20
8c: mov x1, x19
90: stp x8, x9, [sp, #0x10]
94: str q0, [sp]
98: bl 0x98 <ltmp0+0x98>
9c: add sp, sp, #0x20
a0: ldp x2, x3, [x20, #0x180]
a4: mov x0, x21
a8: ldp x4, x5, [x20, #0x190]
ac: mov x1, x19
b0: ldr x6, [x20, #0x1a0]
b4: bl 0xb4 <ltmp0+0xb4>
b8: ldp x8, x9, [x20, #0x170]
bc: ldp x2, x3, [x20, #0x140]
c0: ldp x4, x5, [x20, #0x150]
c4: ldp x6, x7, [x20, #0x160]
c8: stp x8, x9, [sp, #-0x10]!
cc: mov x0, x21
d0: mov x1, x19
d4: bl 0xd4 <ltmp0+0xd4>
d8: add sp, sp, #0x10
dc: cmp x23, #0x0
e0: b.le 0x278 <ltmp0+0x278>
e4: sub x22, sp, #0x20
e8: mov sp, x22
ec: ldp x2, x3, [x20, #0x20]
f0: mov x0, x21
f4: mov x1, x19
f8: mov x4, x22
fc: bl 0xfc <ltmp0+0xfc>
100: cbnz w0, 0x32c <ltmp0+0x32c>
104: ldr x8, [x22, #0x8]
108: mov w9, #0x2 ; =2
10c: stur xzr, [x29, #-0x58]
110: sturb w9, [x29, #-0x68]
114: cmn w8, #0x1
118: b.eq 0x294 <ltmp0+0x294>
11c: mov w9, #0x70 ; =112
120: ldr x10, [x21, #0x60]
124: umaddl x9, w8, w9, x10
128: lsr x8, x8, #32
12c: ldr w10, [x9, #0x60]
130: cmp w10, w8
134: b.ne 0x238 <ltmp0+0x238>
138: tbnz w8, #0x0, 0x238 <ltmp0+0x238>
13c: cmp x23, #0x1
140: b.eq 0x2bc <ltmp0+0x2bc>
144: ldr x8, [x9, #0x10]
148: mov x9, #0x4000000000000000 ; =4611686018427387904
14c: cmn x8, x9
150: b.mi 0x2d8 <ltmp0+0x2d8>
154: lsl x8, x8, #1
158: mov w9, #0xa ; =10
15c: sub x1, x29, #0x68
160: sub x2, x29, #0x80
164: mov x0, x21
168: cmp x8, #0xa
16c: csel x8, x8, x9, lt
170: stur x8, [x29, #-0x60]
174: bl 0x174 <ltmp0+0x174>
178: cbnz w0, 0x310 <ltmp0+0x310>
17c: ldp x9, x10, [x29, #-0x78]
180: ldur q0, [x29, #-0x80]
184: ldr x8, [x20, #0x8]
188: ldurb w11, [x29, #-0x7f]
18c: stur q0, [x29, #-0x50]
190: stur x10, [x29, #-0x40]
194: cbz x8, 0x250 <ltmp0+0x250>
198: sub x12, x29, #0x80
19c: cmp w11, #0xff
1a0: ldr x0, [x20]
1a4: orr x12, x12, #0x2
1a8: csel x2, x10, x11, eq
1ac: csel x1, x9, x12, eq
1b0: blr x8
1b4: cmn w0, #0x1
1b8: b.eq 0x358 <ltmp0+0x358>
1bc: cmp w0, #0x2
1c0: b.hs 0x250 <ltmp0+0x250>
1c4: sub x1, x29, #0x50
1c8: sub x2, x29, #0x98
1cc: mov x0, x21
1d0: bl 0x1d0 <ltmp0+0x1d0>
1d4: mov x0, x21
1d8: mov x1, x22
1dc: bl 0x1dc <ltmp0+0x1dc>
1e0: mov w1, wzr
1e4: mov x0, x21
1e8: bl 0x1e8 <ltmp0+0x1e8>
1ec: mov w22, w0
1f0: cmp w0, #0x2
1f4: b.eq 0x214 <ltmp0+0x214>
1f8: ldr x20, [x20, #0x18]
1fc: cbz x20, 0x214 <ltmp0+0x214>
200: mov x0, x21
204: bl 0x204 <ltmp0+0x204>
208: mov x1, x0
20c: mov x0, x19
210: blr x20
214: mov x0, x21
218: bl 0x218 <ltmp0+0x218>
21c: mov w0, w22
220: sub sp, x29, #0x30
224: ldp x29, x30, [sp, #0x30]
228: ldp x20, x19, [sp, #0x20]
22c: ldp x22, x21, [sp, #0x10]
230: ldp x24, x23, [sp], #0x40
234: ret
238: adrp x2, 0x0 <ltmp0>
23c: add x2, x2, #0x0
240: mov x0, x21
244: mov w1, #0xd ; =13
248: mov w3, #0x16 ; =22
24c: b 0x2a8 <ltmp0+0x2a8>
250: adrp x2, 0x0 <ltmp0>
254: add x2, x2, #0x0
258: mov x0, x21
25c: mov w1, #0x9 ; =9
260: mov w3, #0x18 ; =24
264: bl 0x264 <ltmp0+0x264>
268: mov x0, x21
26c: mov w1, #0x1 ; =1
270: mov w2, #0x6 ; =6
274: b 0x31c <ltmp0+0x31c>
278: adrp x2, 0x0 <ltmp0>
27c: add x2, x2, #0x0
280: mov x0, x21
284: mov w1, #0x6 ; =6
288: mov w3, #0x13 ; =19
28c: bl 0x28c <ltmp0+0x28c>
290: b 0x32c <ltmp0+0x32c>
294: adrp x2, 0x0 <ltmp0>
298: add x2, x2, #0x0
29c: mov x0, x21
2a0: mov w1, #0xe ; =14
2a4: mov w3, #0x15 ; =21
2a8: bl 0x2a8 <ltmp0+0x2a8>
2ac: mov x0, x21
2b0: mov w1, #0x1 ; =1
2b4: mov w2, #0x1 ; =1
2b8: b 0x31c <ltmp0+0x31c>
2bc: adrp x2, 0x0 <ltmp0>
2c0: add x2, x2, #0x0
2c4: mov x0, x21
2c8: mov w1, #0x6 ; =6
2cc: mov w3, #0x13 ; =19
2d0: bl 0x2d0 <ltmp0+0x2d0>
2d4: b 0x300 <ltmp0+0x300>
2d8: adrp x2, 0x0 <ltmp0>
2dc: add x2, x2, #0x0
2e0: mov x0, x21
2e4: mov w1, wzr
2e8: mov w3, #0x10 ; =16
2ec: bl 0x2ec <ltmp0+0x2ec>
2f0: mov x0, x21
2f4: mov w1, wzr
2f8: mov w2, #0x2 ; =2
2fc: bl 0x2fc <ltmp0+0x2fc>
300: mov x0, x21
304: mov w1, #0x1 ; =1
308: mov w2, #0x3 ; =3
30c: b 0x31c <ltmp0+0x31c>
310: mov x0, x21
314: mov w1, #0x1 ; =1
318: mov w2, #0x4 ; =4
31c: bl 0x31c <ltmp0+0x31c>
320: mov x0, x21
324: mov x1, x22
328: bl 0x328 <ltmp0+0x328>
32c: ldr x2, [x20, #0x10]
330: mov x0, x21
334: mov x1, x19
338: bl 0x338 <ltmp0+0x338>
33c: mov w1, #0x1 ; =1
340: mov x0, x21
344: bl 0x344 <ltmp0+0x344>
348: mov w22, w0
34c: cmp w0, #0x2
350: b.ne 0x1f8 <ltmp0+0x1f8>
354: b 0x214 <ltmp0+0x214>
358: mov x0, x21
35c: bl 0x35c <ltmp0+0x35c>
360: b 0x320 <ltmp0+0x320>
7. Object, link, load, call
The assembly lives in a relocatable object file. “Relocatable” means calls and addresses may still contain placeholders. The platform linker joins that object with Luce’s runtime and start code, resolves symbols, and writes either a loadable .lc artifact or a normal executable.
- The object carries
luce_main.That is the one entry the Luce host knows how to call. - The linker supplies runtime functions.Text conversion, printing, ARC, traps, collections, and workers live behind ordinary native symbols.
- The artifact carries an identity tag.The loader checks machine, ABI, generator, and source identity before mapping code.
- The operating system maps native pages.The artifact already contains processor instructions and links to its runtime services.
- The host calls
luce_main.The command-line list is passed in, the instructions above run, and the runtime releases remaining objects on every exit.
Reproduce the trace
The site’s trace-maintenance job runs an inspection compiler in a detached Git worktree. A site-only command prints the LLVM module alongside the public MIR and object outputs.
git worktree add --detach /tmp/luce-traces HEAD
# Build the temporary inspection compiler inside /tmp/luce-traces.
LUCELANG_COMPILER=/tmp/luce-traces/build/luce \
./www/lucelang/generate-traces.shThe maintenance script checks each example, prints MIR and LLVM IR, emits an object, disassembles that object, and verifies that all four files describe the same program.
The reading order to remember: source states the program; HIR records resolved language decisions; MIR lists the language operations; LLVM IR expresses them for a native backend; assembly is the instruction sequence selected for one processor.