for loops

This commit is contained in:
2026-06-22 20:11:18 +02:00
parent 380b5943b3
commit 27f42dd253
15 changed files with 1369 additions and 22 deletions
+357 -1
View File
@@ -150,6 +150,18 @@ lower_location :: proc(state: ^State, expr_id: hir.Expr_Id, for_write := false)
return ir.INVALID_INSTRUCTION
}
hir_expr_is_location :: proc(state: ^State, expr_id: hir.Expr_Id) -> bool {
if expr_id == hir.INVALID_EXPR || int(expr_id) >= len(state.hir_module.exprs) {
return false
}
#partial switch state.hir_module.exprs[expr_id].kind {
case .Local, .Global, .Deref, .Index, .Field:
return true
case:
return false
}
}
lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
expr := state.hir_module.exprs[expr_id]
#partial switch expr.kind {
@@ -169,6 +181,30 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Range:
args := make([]ir.Instruction_Id, 3, state.allocator)
args[0] = lower_nested_expr(state, expr.args[0])
args[1] = lower_nested_expr(state, expr.args[1])
args[2] = append_instruction(state, ir.Instruction{
op=.Const,
span=expr.span,
type=types.BOOL,
integer=expr.integer,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return append_instruction(state, ir.Instruction{
op=.Aggregate,
span=expr.span,
type=expr.type,
args=args,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .None:
return append_instruction(state, ir.Instruction{
op=.None, span=expr.span, type=expr.type,
@@ -381,7 +417,7 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
case .String, .Array, .Struct, .None, .Optional_Some, .Address, .Deref,
case .String, .Array, .Struct, .Range, .None, .Optional_Some, .Address, .Deref,
.Index, .Slice, .Field, .Length, .Slice_Ptr, .Unwrap, .Orelse,
.Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
last = lower_compound_expr(state, frame.expr)
@@ -762,6 +798,326 @@ lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .For:
iterable_type := hir_module.exprs[statement.expr].type
if types.is_range(iterable_type, &hir_module.types) {
child := types.child_type(iterable_type, &hir_module.types)
range_value := lower_expr(state, statement.expr)
start := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=child, integer=0,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
end := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=child, integer=1,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
inclusive := append_instruction(state, ir.Instruction{
op=.Extract, span=statement.span, type=types.BOOL, integer=2,
target=ir.INVALID_REF, a=range_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
current_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=child,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = current_slot
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=start,
diagnostic=source.INVALID_DIAGNOSTIC,
})
condition_lbl := fresh_label(state)
body_lbl := fresh_label(state)
update_lbl := fresh_label(state)
exit_lbl := fresh_label(state)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
current := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
less := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Lt),
target=ir.INVALID_REF, a=current, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
equal := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Eq),
target=ir.INVALID_REF, a=current, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
false_value := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.BOOL, integer=0,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
true_value := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.BOOL, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
inclusive_args := make([]ir.Instruction_Id, 2, state.allocator)
inclusive_args[0] = equal
inclusive_args[1] = false_value
inclusive_equal := append_instruction(state, ir.Instruction{
op=.Select, span=statement.span, type=types.BOOL,
target=ir.INVALID_REF, a=inclusive, b=ir.INVALID_INSTRUCTION,
args=inclusive_args, diagnostic=source.INVALID_DIAGNOSTIC,
})
condition_args := make([]ir.Instruction_Id, 2, state.allocator)
condition_args[0] = true_value
condition_args[1] = inclusive_equal
condition := append_instruction(state, ir.Instruction{
op=.Select, span=statement.span, type=types.BOOL,
target=ir.INVALID_REF, a=less, b=ir.INVALID_INSTRUCTION,
args=condition_args, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
lower_statements(state, statement.then_body)
after_body := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
at_end := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Eq),
target=ir.INVALID_REF, a=after_body, b=end,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=at_end, integer=exit_lbl, target=ir.Ref(u32(update_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
one := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=child, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
next := append_instruction(state, ir.Instruction{
op=.Add_Checked, span=statement.span, type=child,
target=ir.INVALID_REF, a=after_body, b=one,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=child,
target=ir.INVALID_REF, a=current_slot, b=next,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=exit_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
continue
}
item, item_ok := types.container(iterable_type, &hir_module.types)
if !item_ok {
append_instruction(state, ir.Instruction{
op=.Trap, span=statement.span, type=types.VOID,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=statement.diagnostic,
})
continue
}
iterable_value := ir.INVALID_INSTRUCTION
if types.is_array(iterable_type, &hir_module.types) {
if hir_expr_is_location(state, statement.expr) {
iterable_value = lower_location(state, statement.expr)
} else {
value := lower_expr(state, statement.expr)
iterable_value = append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=iterable_type,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=iterable_type,
target=ir.INVALID_REF, a=iterable_value, b=value,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
} else {
iterable_value = lower_expr(state, statement.expr)
}
base := append_instruction(state, ir.Instruction{
op=.Slice_Ptr, span=statement.span, type=statement.iterator_type,
target=ir.INVALID_REF, a=iterable_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
length := ir.INVALID_INSTRUCTION
if item.kind == .Array {
length = append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=i64(item.count),
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
} else {
length = append_instruction(state, ir.Instruction{
op=.Length, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=iterable_value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
counter_target := ir.INVALID_REF
if statement.index_local != hir.INVALID_LOCAL {
counter_target = ir.local_ref(ir.Local_Id(statement.index_local))
}
counter_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=types.USIZE,
target=counter_target, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
if statement.index_local != hir.INVALID_LOCAL {
state.local_slots[statement.index_local] = counter_slot
}
zero := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=0,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=zero,
diagnostic=source.INVALID_DIAGNOSTIC,
})
capture_type := state.func_locals[statement.local].type
capture_slot := append_instruction(state, ir.Instruction{
op=.Alloca, span=statement.span, type=capture_type,
target=ir.local_ref(ir.Local_Id(statement.local)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_slots[statement.local] = capture_slot
condition_lbl := fresh_label(state)
body_lbl := fresh_label(state)
update_lbl := fresh_label(state)
exit_lbl := fresh_label(state)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
index := append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
condition := append_instruction(state, ir.Instruction{
op=.Compare, span=statement.span, type=types.BOOL,
integer=i64(ir.Compare_Predicate.Lt),
target=ir.INVALID_REF, a=index, b=length,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Cond_Br, span=statement.span, type=types.VOID,
a=condition, integer=body_lbl, target=ir.Ref(u32(exit_lbl)),
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=body_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
pointer_type := statement.iterator_type
if statement.pointer_capture {
pointer_type = capture_type
}
element_pointer := append_instruction(state, ir.Instruction{
op=.Pointer_Add, span=statement.span, type=pointer_type,
target=ir.INVALID_REF, a=base, b=index,
diagnostic=source.INVALID_DIAGNOSTIC,
})
captured := element_pointer
if !statement.pointer_capture {
captured = append_instruction(state, ir.Instruction{
op=.Load, span=statement.span, type=item.child,
target=ir.INVALID_REF, a=element_pointer, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=capture_type,
target=ir.INVALID_REF, a=capture_slot, b=captured,
diagnostic=source.INVALID_DIAGNOSTIC,
})
lower_statements(state, statement.then_body)
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=update_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
one := append_instruction(state, ir.Instruction{
op=.Const, span=statement.span, type=types.USIZE, integer=1,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
next := append_instruction(state, ir.Instruction{
op=.Add_Checked, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=index, b=one,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Store, span=statement.span, type=types.USIZE,
target=ir.INVALID_REF, a=counter_slot, b=next,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Br, span=statement.span, type=types.VOID, integer=condition_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(state, ir.Instruction{
op=.Label, span=statement.span, type=types.VOID, integer=exit_lbl,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
}
}