Files
brolang/compiler/lower/lower.odin
T

679 lines
24 KiB
Odin

package lower
import "../hir"
import "../ir"
import "../source"
import "../target"
import "../types"
import "core:fmt"
import "core:mem"
State :: struct {
hir_module: ^hir.Module,
instructions: [dynamic]ir.Instruction,
local_values: []ir.Instruction_Id,
local_slots: []ir.Instruction_Id,
expr_stack: [dynamic]Lower_Expr_Frame,
allocator: mem.Allocator,
}
append_instruction :: proc(state: ^State, instruction: ir.Instruction) -> ir.Instruction_Id {
id := ir.instruction_id(len(state.instructions))
append(&state.instructions, instruction)
return id
}
clone_args :: proc(values: []ir.Instruction_Id, allocator: mem.Allocator) -> []ir.Instruction_Id {
result := make([]ir.Instruction_Id, len(values), allocator)
copy(result, values)
return result
}
sentinel :: proc(value_type: types.Type, selected := target.DEFAULT) -> i64 {
if types.is_float(value_type, selected) {
return i64(0x7fc0_0000) if types.bits(value_type, selected) == 32 else transmute(i64)u64(0x7ff8_0000_0000_0000)
}
switch types.bits(value_type, selected) {
case 8: return -86
case 16: return -21846
case 32: return -1431655766
case: return -6148914691236517206
}
}
append_recovery_value :: proc(
state: ^State,
span: source.Span,
value_type: types.Type,
diagnostic := source.INVALID_DIAGNOSTIC,
) -> ir.Instruction_Id {
append_instruction(state, ir.Instruction{
op=.Trap,
span=span,
type=types.VOID,
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=diagnostic,
})
fallback := value_type
if !types.is_valid(fallback) {
fallback = types.I64
}
return append_instruction(state, ir.Instruction{
op=.Const,
span=span,
type=fallback,
integer=sentinel(fallback, state.hir_module.target),
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
Lower_Expr_Frame :: struct {
expr: hir.Expr_Id,
stage: u8,
left: ir.Instruction_Id,
arg_index: int,
args: []ir.Instruction_Id,
}
lower_nested_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
outer := state.expr_stack
state.expr_stack = nil
state.expr_stack.allocator = state.allocator
result := lower_expr(state, expr_id)
delete(state.expr_stack)
state.expr_stack = outer
return result
}
lower_location :: proc(state: ^State, expr_id: hir.Expr_Id, for_write := false) -> ir.Instruction_Id {
if expr_id == hir.INVALID_EXPR || int(expr_id) >= len(state.hir_module.exprs) {
return ir.INVALID_INSTRUCTION
}
expr := state.hir_module.exprs[expr_id]
#partial switch expr.kind {
case .Local:
local := hir.as_local(expr.target)
if local != hir.INVALID_LOCAL && int(local) < len(state.local_slots) {
return state.local_slots[local]
}
case .Global:
global := hir.as_global(expr.target)
if global != hir.INVALID_GLOBAL && int(global) < len(state.hir_module.globals) {
return append_instruction(state, ir.Instruction{
op=.Address_Global, span=expr.span, type=expr.type,
target=ir.global_ref(ir.Global_Id(global)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
case .Deref:
return lower_nested_expr(state, expr.left)
case .Index:
container_type := state.hir_module.exprs[expr.left].type
container := lower_nested_expr(state, expr.left)
if types.is_array(container_type, &state.hir_module.types) {
container = lower_location(state, expr.left, for_write)
}
index := lower_nested_expr(state, expr.right)
return append_instruction(state, ir.Instruction{
op=.Index_Address, span=expr.span, type=expr.type,
integer=0 if for_write else 1,
target=ir.INVALID_REF, a=container, b=index,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Field:
base_type := state.hir_module.exprs[expr.left].type
base := lower_nested_expr(state, expr.left)
if !types.is_pointer(base_type, &state.hir_module.types) {
base = lower_location(state, expr.left, for_write)
}
return append_instruction(state, ir.Instruction{
op=.Field_Address, span=expr.span, type=expr.type, integer=expr.integer,
target=ir.INVALID_REF, a=base, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
return ir.INVALID_INSTRUCTION
}
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 {
case .String:
return append_instruction(state, ir.Instruction{
op=.String, 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,
})
case .Array, .Struct:
args := make([]ir.Instruction_Id, len(expr.args), state.allocator)
for arg, index in expr.args {
args[index] = lower_nested_expr(state, arg)
}
return append_instruction(state, ir.Instruction{
op=.Aggregate, span=expr.span, type=expr.type, args=args, integer=expr.integer,
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,
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Optional_Some:
value := lower_nested_expr(state, expr.left)
return append_instruction(state, ir.Instruction{
op=.Optional_Some, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Address:
location := lower_location(state, expr.left, types.is_mutable(expr.type, &state.hir_module.types))
if location != ir.INVALID_INSTRUCTION {
return append_instruction(state, ir.Instruction{
op=.Address_Of, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=location, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
case .Deref, .Index, .Field:
location := lower_location(state, expr_id)
if location == ir.INVALID_INSTRUCTION {
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
}
return append_instruction(state, ir.Instruction{
op=.Load, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=location, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Slice:
container := lower_nested_expr(state, expr.left)
if types.is_array(state.hir_module.exprs[expr.left].type, &state.hir_module.types) {
container = lower_location(state, expr.left)
}
args := make([]ir.Instruction_Id, len(expr.args), state.allocator)
for arg, index in expr.args {
args[index] = ir.INVALID_INSTRUCTION
if arg != hir.INVALID_EXPR {
args[index] = lower_nested_expr(state, arg)
}
}
return append_instruction(state, ir.Instruction{
op=.Slice, span=expr.span, type=expr.type, args=args,
target=ir.INVALID_REF, a=container, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Unwrap:
value := lower_nested_expr(state, expr.left)
return append_instruction(state, ir.Instruction{
op=.Unwrap, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Length:
container_type := state.hir_module.exprs[expr.left].type
item, ok := types.node(&state.hir_module.types, container_type)
if ok && item.kind == .Array {
return append_instruction(state, ir.Instruction{
op=.Const, span=expr.span, type=types.USIZE, integer=i64(item.count),
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_, array, array_ok := types.array_pointer(container_type, &state.hir_module.types)
if array_ok {
return append_instruction(state, ir.Instruction{
op=.Const, span=expr.span, type=types.USIZE, integer=i64(array.count),
target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
value := lower_nested_expr(state, expr.left)
return append_instruction(state, ir.Instruction{
op=.Length, span=expr.span, type=types.USIZE,
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Slice_Ptr:
container_type := state.hir_module.exprs[expr.left].type
value := lower_nested_expr(state, expr.left)
if types.is_array(container_type, &state.hir_module.types) {
value = lower_location(state, expr.left)
}
return append_instruction(state, ir.Instruction{
op=.Slice_Ptr, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
case .Orelse:
value := lower_nested_expr(state, expr.left)
begin := append_instruction(state, ir.Instruction{
op=.Orelse_Begin, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
fallback := lower_nested_expr(state, expr.right)
return append_instruction(state, ir.Instruction{
op=.Orelse, span=expr.span, type=expr.type,
target=ir.INVALID_REF, a=begin, b=fallback,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
return append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
}
lower_expr :: proc(state: ^State, expr_id: hir.Expr_Id) -> ir.Instruction_Id {
stack := state.expr_stack
clear_dynamic_array(&stack)
defer {
for frame in stack {
delete(frame.args, state.allocator)
}
clear_dynamic_array(&stack)
state.expr_stack = stack
}
append(&stack, Lower_Expr_Frame{expr=expr_id})
last := ir.INVALID_INSTRUCTION
for len(stack) > 0 {
frame_index := len(stack)-1
frame := stack[frame_index]
if frame.expr == hir.INVALID_EXPR || int(frame.expr) >= len(state.hir_module.exprs) {
last = append_recovery_value(state, source.Span{}, types.I64)
_ = pop(&stack)
continue
}
expr := state.hir_module.exprs[frame.expr]
if frame.stage == 0 {
#partial switch expr.kind {
case .Invalid:
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
_ = pop(&stack)
case .Integer, .Float:
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:
last = lower_compound_expr(state, frame.expr)
_ = pop(&stack)
case .Local:
last = ir.INVALID_INSTRUCTION
local := hir.as_local(expr.target)
if local != hir.INVALID_LOCAL && int(local) < len(state.local_slots) && state.local_slots[local] != ir.INVALID_INSTRUCTION {
last = append_instruction(state, ir.Instruction{
op=.Load, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=state.local_slots[local], b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
} else if local != hir.INVALID_LOCAL && int(local) < len(state.local_values) &&
state.local_values[local] != ir.INVALID_INSTRUCTION {
last = state.local_values[local]
}
if last == ir.INVALID_INSTRUCTION {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
}
_ = pop(&stack)
case .Global:
global := hir.as_global(expr.target)
if global == hir.INVALID_GLOBAL || int(global) >= len(state.hir_module.globals) {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
} else {
last = append_instruction(state, ir.Instruction{
op=.Load_Global, span=expr.span, type=expr.type, target=ir.global_ref(ir.Global_Id(global)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
case .Function:
function := hir.as_function(expr.target)
if function == hir.INVALID_FUNCTION || int(function) >= len(state.hir_module.functions) {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
} else {
last = append_instruction(state, ir.Instruction{
op=.Function_Address, span=expr.span, type=expr.type,
target=ir.function_ref(ir.Function_Id(function)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
}
_ = pop(&stack)
case .Widen, .C_Vararg_Promote, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer:
stack[frame_index].stage = 1
append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Negate:
stack[frame_index].stage = 5
append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Add, .Pointer_Add:
stack[frame_index].stage = 2
append(&stack, Lower_Expr_Frame{expr=expr.left})
case .Call:
function := hir.as_function(expr.target)
if function == hir.INVALID_FUNCTION {
if expr.left == hir.INVALID_EXPR {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
_ = pop(&stack)
continue
}
stack[frame_index].stage = 6
append(&stack, Lower_Expr_Frame{expr=expr.left})
continue
}
if int(function) >= len(state.hir_module.functions) {
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
_ = pop(&stack)
continue
}
stack[frame_index].args = make([]ir.Instruction_Id, len(expr.args), state.allocator)
stack[frame_index].stage = 4
if len(expr.args) > 0 {
append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
}
}
continue
}
if frame.stage == 6 {
stack[frame_index].left = last
stack[frame_index].args = make([]ir.Instruction_Id, len(expr.args), state.allocator)
stack[frame_index].stage = 4
if len(expr.args) > 0 {
append(&stack, Lower_Expr_Frame{expr=expr.args[0]})
}
continue
}
if frame.stage == 5 {
last = append_instruction(state, ir.Instruction{
op=.Neg_Checked, span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
continue
}
if frame.stage == 1 {
op := ir.Opcode.Widen
#partial switch expr.kind {
case .Weaken_Pointer: op = .Weaken_Pointer
case .Weaken_Slice: op = .Weaken_Slice
case .Decay_Array_Pointer: op = .Decay_Array_Pointer
case .C_Vararg_Promote: op = .C_Vararg_Promote
case: op = .Widen
}
last = append_instruction(state, ir.Instruction{
op=op,
span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=last, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
continue
}
if frame.stage == 2 {
stack[frame_index].left = last
stack[frame_index].stage = 3
append(&stack, Lower_Expr_Frame{expr=expr.right})
continue
}
if frame.stage == 3 {
last = append_instruction(state, ir.Instruction{
op=.Pointer_Add if expr.kind == .Pointer_Add else .Add_Checked,
span=expr.span, type=expr.type, target=ir.INVALID_REF,
a=frame.left, b=last, diagnostic=source.INVALID_DIAGNOSTIC,
})
_ = pop(&stack)
continue
}
if frame.stage == 4 {
if frame.arg_index < len(expr.args) {
stack[frame_index].args[frame.arg_index] = last
stack[frame_index].arg_index += 1
if frame.arg_index+1 < len(expr.args) {
append(&stack, Lower_Expr_Frame{expr=expr.args[frame.arg_index+1]})
continue
}
}
function := hir.as_function(expr.target)
callee := frame.left
if function != hir.INVALID_FUNCTION {
callee = ir.INVALID_INSTRUCTION
}
last = append_instruction(state, ir.Instruction{
op=.Call, span=expr.span, type=expr.type, target=ir.function_ref(ir.Function_Id(function)),
a=callee, b=ir.INVALID_INSTRUCTION, args=stack[frame_index].args, diagnostic=source.INVALID_DIAGNOSTIC,
})
stack[frame_index].args = nil
_ = pop(&stack)
}
}
return last
}
lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: mem.Allocator) -> []ir.Instruction {
state := State{
hir_module=hir_module,
allocator=allocator,
local_values=make([]ir.Instruction_Id, len(function.locals), allocator),
local_slots=make([]ir.Instruction_Id, len(function.locals), allocator),
}
state.instructions.allocator = allocator
state.expr_stack.allocator = allocator
defer {
delete(state.local_values, allocator)
delete(state.local_slots, allocator)
delete(state.expr_stack)
}
for _, index in state.local_values {
state.local_values[index] = ir.INVALID_INSTRUCTION
state.local_slots[index] = ir.INVALID_INSTRUCTION
}
for local_id in function.params {
param := append_instruction(&state, ir.Instruction{
op=.Param,
type=function.locals[local_id].type,
target=ir.local_ref(ir.Local_Id(local_id)),
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
state.local_values[local_id] = param
}
for local_id in function.params {
slot := append_instruction(&state, ir.Instruction{
op=.Alloca, type=function.locals[local_id].type,
target=ir.local_ref(ir.Local_Id(local_id)),
a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(&state, ir.Instruction{
op=.Store, type=function.locals[local_id].type,
target=ir.INVALID_REF, a=slot, b=state.local_values[local_id],
diagnostic=source.INVALID_DIAGNOSTIC,
})
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,
})
}
}
if len(state.instructions) == 0 ||
(state.instructions[len(state.instructions)-1].op != .Return &&
state.instructions[len(state.instructions)-1].op != .Return_Void) {
if types.is_void(function.result) {
append_instruction(&state, ir.Instruction{op=.Return_Void, type=types.VOID, target=ir.INVALID_REF, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
} else {
value := append_instruction(&state, ir.Instruction{
op=.Const,
type=function.result,
integer=sentinel(function.result, hir_module.target),
target=ir.INVALID_REF,
a=ir.INVALID_INSTRUCTION,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
append_instruction(&state, ir.Instruction{op=.Return, type=function.result, target=ir.INVALID_REF, a=value, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC})
}
}
return state.instructions[:]
}
lower_global_initializer :: proc(hir_module: ^hir.Module, global: hir.Global, allocator: mem.Allocator) -> []ir.Instruction {
state := State{hir_module=hir_module, allocator=allocator}
state.instructions.allocator = allocator
state.expr_stack.allocator = allocator
defer delete(state.expr_stack)
value := lower_expr(&state, global.expr)
append_instruction(&state, ir.Instruction{
op=.Return,
type=global.type,
target=ir.INVALID_REF,
a=value,
b=ir.INVALID_INSTRUCTION,
diagnostic=source.INVALID_DIAGNOSTIC,
})
return state.instructions[:]
}
lower :: proc(hir_module: ^hir.Module, allocator := context.allocator) -> ir.Module {
module := ir.init_module(hir_module.target, allocator)
types.destroy_store(&module.types)
module.types = types.clone_store(&hir_module.types, allocator)
for value in hir_module.strings {
append(&module.strings, fmt.aprintf("%s", value, allocator=allocator))
}
for global in hir_module.globals {
_ = ir.global_id(len(module.globals))
append(&module.globals, ir.Global{
name=global.name,
link_name=fmt.aprintf("%s", global.link_name, allocator=allocator),
type=global.type,
is_static=global.is_static,
external=global.external,
writable=global.writable,
static_value=global.static_value,
initializer=nil if global.is_static || global.external else lower_global_initializer(hir_module, global, allocator),
problematic=global.problematic,
diagnostic=global.diagnostic,
})
}
for function in hir_module.functions {
param_types := make([]types.Type, len(function.params), allocator)
for local_id, index in function.params {
param_types[index] = function.locals[local_id].type
}
_ = ir.function_id(len(module.functions))
append(&module.functions, ir.Function{
link_name=fmt.aprintf("%s", function.link_name, allocator=allocator),
calling_convention=.C if function.calling_convention == .C else .Brolang,
implementation=.Declaration if function.implementation == .Declaration else .Definition,
linkage=.External if function.linkage == .External else .Internal,
is_main=function.is_main,
variadic=function.variadic,
param_types=param_types,
result=function.result,
instructions=nil if function.implementation == .Declaration else lower_body(hir_module, function, allocator),
problematic=function.problematic,
})
}
return module
}