booleans, comparisons, and if/else
This commit is contained in:
+229
-100
@@ -13,10 +13,19 @@ State :: struct {
|
||||
instructions: [dynamic]ir.Instruction,
|
||||
local_values: []ir.Instruction_Id,
|
||||
local_slots: []ir.Instruction_Id,
|
||||
func_locals: []hir.Local,
|
||||
func_result: types.Type,
|
||||
expr_stack: [dynamic]Lower_Expr_Frame,
|
||||
next_label: i64,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
fresh_label :: proc(state: ^State) -> i64 {
|
||||
id := state.next_label
|
||||
state.next_label += 1
|
||||
return id
|
||||
}
|
||||
|
||||
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
|
||||
id := ir.instruction_id(len(state.instructions))
|
||||
append(&state.instructions, instruction)
|
||||
@@ -265,6 +274,77 @@ lower_compound_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instructi
|
||||
target=ir.INVALID_REF, a=begin, b=fallback,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Not:
|
||||
value := lower_nested_expr(state, expr.left)
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Not, span=expr.span, type=types.BOOL,
|
||||
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Eq, .Ne, .Lt, .Le, .Gt, .Ge:
|
||||
left := lower_nested_expr(state, expr.left)
|
||||
right := lower_nested_expr(state, expr.right)
|
||||
predicate := ir.Compare_Predicate.Eq
|
||||
#partial switch expr.kind {
|
||||
case .Eq: predicate = .Eq
|
||||
case .Ne: predicate = .Ne
|
||||
case .Lt: predicate = .Lt
|
||||
case .Le: predicate = .Le
|
||||
case .Gt: predicate = .Gt
|
||||
case .Ge: predicate = .Ge
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Compare, span=expr.span, type=types.BOOL, integer=i64(predicate),
|
||||
target=ir.INVALID_REF, a=left, b=right,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .And, .Or:
|
||||
// Short-circuit via a bool slot: store the left operand, branch on it, and
|
||||
// only evaluate/store the right operand when needed. Avoids phi nodes.
|
||||
slot := append_instruction(state, ir.Instruction{
|
||||
op=.Alloca, span=expr.span, type=types.BOOL,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
left := lower_nested_expr(state, expr.left)
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Store, span=expr.span, type=types.BOOL,
|
||||
target=ir.INVALID_REF, a=slot, b=left, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
rhs_lbl := fresh_label(state)
|
||||
done_lbl := fresh_label(state)
|
||||
true_target := rhs_lbl if expr.kind == .And else done_lbl
|
||||
false_target := done_lbl if expr.kind == .And else rhs_lbl
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Cond_Br, span=expr.span, type=types.VOID,
|
||||
a=left, integer=true_target, target=ir.Ref(u32(false_target)),
|
||||
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Label, span=expr.span, type=types.VOID, integer=rhs_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
right := lower_nested_expr(state, expr.right)
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Store, span=expr.span, type=types.BOOL,
|
||||
target=ir.INVALID_REF, a=slot, b=right, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Br, span=expr.span, type=types.VOID, integer=done_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=expr.span, type=types.VOID, integer=done_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Load, span=expr.span, type=types.BOOL,
|
||||
target=ir.INVALID_REF, a=slot, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
}
|
||||
@@ -295,14 +375,15 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
case .Invalid:
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
case .Integer, .Float:
|
||||
case .Integer, .Float, .Bool:
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
||||
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,
|
||||
.Index, .Slice, .Field, .Length, .Slice_Ptr, .Unwrap, .Orelse:
|
||||
.Index, .Slice, .Field, .Length, .Slice_Ptr, .Unwrap, .Orelse,
|
||||
.Not, .Eq, .Ne, .Lt, .Le, .Gt, .Ge, .And, .Or:
|
||||
last = lower_compound_expr(state, frame.expr)
|
||||
_ = pop(&stack)
|
||||
case .Local:
|
||||
@@ -453,10 +534,155 @@ lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
|
||||
return last
|
||||
}
|
||||
|
||||
lower_statements :: proc(state: ^State, statements: []hir.Stmt_Id) {
|
||||
hir_module := state.hir_module
|
||||
for statement_id in statements {
|
||||
statement := hir_module.statements[statement_id]
|
||||
switch statement.kind {
|
||||
case .Declaration:
|
||||
value := lower_expr(state, statement.expr)
|
||||
if statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(state.func_locals) {
|
||||
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
|
||||
}
|
||||
local := state.func_locals[statement.local]
|
||||
slot := append_instruction(state, ir.Instruction{
|
||||
op=.Alloca,
|
||||
span=statement.span,
|
||||
type=local.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] = slot
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=local.type,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Assignment:
|
||||
value := lower_expr(state, statement.expr)
|
||||
slot := ir.INVALID_INSTRUCTION
|
||||
value_type := types.INVALID
|
||||
if statement.target != hir.INVALID_EXPR {
|
||||
slot = lower_location(state, statement.target, true)
|
||||
if int(statement.target) < len(hir_module.exprs) {
|
||||
value_type = hir_module.exprs[statement.target].type
|
||||
}
|
||||
} else if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
|
||||
slot = state.local_slots[statement.local]
|
||||
value_type = state.func_locals[statement.local].type
|
||||
}
|
||||
if slot == ir.INVALID_INSTRUCTION || !types.is_valid(value_type) {
|
||||
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
|
||||
}
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=value_type,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Return:
|
||||
if statement.expr == hir.INVALID_EXPR {
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Return_Void,
|
||||
span=statement.span,
|
||||
type=types.VOID,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
value := lower_expr(state, statement.expr)
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Return,
|
||||
span=statement.span,
|
||||
type=state.func_result,
|
||||
target=ir.INVALID_REF,
|
||||
a=value,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
case .Expression, .Sink:
|
||||
_ = lower_expr(state, statement.expr)
|
||||
case .Trap:
|
||||
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,
|
||||
})
|
||||
case .If:
|
||||
cond := lower_expr(state, statement.expr)
|
||||
has_else := statement.else_body != nil
|
||||
then_lbl := fresh_label(state)
|
||||
else_lbl := fresh_label(state) if has_else else then_lbl
|
||||
merge_lbl := fresh_label(state)
|
||||
false_target := else_lbl if has_else else merge_lbl
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Cond_Br, span=statement.span, type=types.VOID,
|
||||
a=cond, integer=then_lbl, target=ir.Ref(u32(false_target)),
|
||||
b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Label, span=statement.span, type=types.VOID, integer=then_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
lower_statements(state, statement.then_body)
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Br, span=statement.span, type=types.VOID, integer=merge_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
if has_else {
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Label, span=statement.span, type=types.VOID, integer=else_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
lower_statements(state, statement.else_body)
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Br, span=statement.span, type=types.VOID, integer=merge_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=merge_lbl,
|
||||
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: mem.Allocator) -> []ir.Instruction {
|
||||
state := State{
|
||||
hir_module=hir_module,
|
||||
allocator=allocator,
|
||||
func_locals=function.locals,
|
||||
func_result=function.result,
|
||||
local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||
local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
|
||||
}
|
||||
@@ -497,104 +723,7 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
state.local_slots[local_id] = slot
|
||||
}
|
||||
|
||||
for statement_id in function.body {
|
||||
statement := hir_module.statements[statement_id]
|
||||
switch statement.kind {
|
||||
case .Declaration:
|
||||
value := lower_expr(&state, statement.expr)
|
||||
if statement.local == hir.INVALID_LOCAL || int(statement.local) >= len(function.locals) {
|
||||
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
|
||||
}
|
||||
local := function.locals[statement.local]
|
||||
slot := append_instruction(&state, ir.Instruction{
|
||||
op=.Alloca,
|
||||
span=statement.span,
|
||||
type=local.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] = slot
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=local.type,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Assignment:
|
||||
value := lower_expr(&state, statement.expr)
|
||||
slot := ir.INVALID_INSTRUCTION
|
||||
value_type := types.INVALID
|
||||
if statement.target != hir.INVALID_EXPR {
|
||||
slot = lower_location(&state, statement.target, true)
|
||||
if int(statement.target) < len(hir_module.exprs) {
|
||||
value_type = hir_module.exprs[statement.target].type
|
||||
}
|
||||
} else if statement.local != hir.INVALID_LOCAL && int(statement.local) < len(state.local_slots) {
|
||||
slot = state.local_slots[statement.local]
|
||||
value_type = function.locals[statement.local].type
|
||||
}
|
||||
if slot == ir.INVALID_INSTRUCTION || !types.is_valid(value_type) {
|
||||
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
|
||||
}
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
type=value_type,
|
||||
target=ir.INVALID_REF,
|
||||
a=slot,
|
||||
b=value,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
case .Return:
|
||||
if statement.expr == hir.INVALID_EXPR {
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Return_Void,
|
||||
span=statement.span,
|
||||
type=types.VOID,
|
||||
target=ir.INVALID_REF,
|
||||
a=ir.INVALID_INSTRUCTION,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
} else {
|
||||
value := lower_expr(&state, statement.expr)
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Return,
|
||||
span=statement.span,
|
||||
type=function.result,
|
||||
target=ir.INVALID_REF,
|
||||
a=value,
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
case .Expression, .Sink:
|
||||
_ = lower_expr(&state, statement.expr)
|
||||
case .Trap:
|
||||
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,
|
||||
})
|
||||
}
|
||||
}
|
||||
lower_statements(&state, function.body)
|
||||
if len(state.instructions) == 0 ||
|
||||
(state.instructions[len(state.instructions)-1].op != .Return &&
|
||||
state.instructions[len(state.instructions)-1].op != .Return_Void) {
|
||||
|
||||
Reference in New Issue
Block a user