harden compiler parsing, recovery, and deep-expression handling
This commit is contained in:
+157
-127
@@ -2,6 +2,7 @@ package lower
|
||||
|
||||
import "../hir"
|
||||
import "../ir"
|
||||
import "../source"
|
||||
import "../types"
|
||||
import "core:fmt"
|
||||
import "core:mem"
|
||||
@@ -11,6 +12,7 @@ State :: struct {
|
||||
instructions: [dynamic]ir.Instruction,
|
||||
local_values: []int,
|
||||
local_slots: []int,
|
||||
expr_stack: [dynamic]Lower_Expr_Frame,
|
||||
allocator: mem.Allocator,
|
||||
}
|
||||
|
||||
@@ -35,142 +37,25 @@ sentinel :: proc(value_type: types.Type) -> i64 {
|
||||
}
|
||||
}
|
||||
|
||||
lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
if expr_id < 0 || expr_id >= len(state.hir_module.exprs) {
|
||||
trap := append_instruction(state, ir.Instruction{
|
||||
op=.Trap,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
_ = trap
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Const,
|
||||
type=types.I64,
|
||||
integer=sentinel(types.I64),
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
}
|
||||
expr := state.hir_module.exprs[expr_id]
|
||||
switch expr.kind {
|
||||
case .Invalid:
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Trap,
|
||||
span=expr.span,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=expr.diagnostic,
|
||||
})
|
||||
fallback := expr.type
|
||||
if !types.is_concrete_integer(fallback) {
|
||||
fallback = types.I64
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Const,
|
||||
span=expr.span,
|
||||
type=fallback,
|
||||
integer=sentinel(fallback),
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
case .Integer:
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Const,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
integer=expr.integer,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
case .Local:
|
||||
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 {
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Load,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
target=-1,
|
||||
a=state.local_slots[expr.target],
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
}
|
||||
if expr.target >= 0 && expr.target < len(state.local_values) {
|
||||
return state.local_values[expr.target]
|
||||
}
|
||||
case .Global:
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Load_Global,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
target=expr.target,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
case .Widen:
|
||||
value := lower_expr(state, expr.left)
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Widen,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
target=-1,
|
||||
a=value,
|
||||
b=-1,
|
||||
diagnostic=-1,
|
||||
})
|
||||
case .Add:
|
||||
left := lower_expr(state, expr.left)
|
||||
right := lower_expr(state, expr.right)
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Add_Checked,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
target=-1,
|
||||
a=left,
|
||||
b=right,
|
||||
diagnostic=-1,
|
||||
})
|
||||
case .Call:
|
||||
args := make([]int, len(expr.args), state.allocator)
|
||||
for arg, index in expr.args {
|
||||
args[index] = lower_expr(state, arg)
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Call,
|
||||
span=expr.span,
|
||||
type=expr.type,
|
||||
target=expr.target,
|
||||
a=-1,
|
||||
b=-1,
|
||||
args=args,
|
||||
diagnostic=-1,
|
||||
})
|
||||
}
|
||||
append_recovery_value :: proc(state: ^State, span: source.Span, value_type: types.Type, diagnostic := -1) -> int {
|
||||
append_instruction(state, ir.Instruction{
|
||||
op=.Trap,
|
||||
span=expr.span,
|
||||
span=span,
|
||||
type=types.VOID,
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
diagnostic=expr.diagnostic,
|
||||
diagnostic=diagnostic,
|
||||
})
|
||||
fallback := value_type
|
||||
if !types.is_concrete_integer(fallback) {
|
||||
fallback = types.I64
|
||||
}
|
||||
return append_instruction(state, ir.Instruction{
|
||||
op=.Const,
|
||||
span=expr.span,
|
||||
type=types.I64,
|
||||
integer=sentinel(types.I64),
|
||||
span=span,
|
||||
type=fallback,
|
||||
integer=sentinel(fallback),
|
||||
target=-1,
|
||||
a=-1,
|
||||
b=-1,
|
||||
@@ -178,6 +63,133 @@ lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
})
|
||||
}
|
||||
|
||||
Lower_Expr_Frame :: struct {
|
||||
expr: int,
|
||||
stage: u8,
|
||||
left: int,
|
||||
arg_index: int,
|
||||
args: []int,
|
||||
}
|
||||
|
||||
lower_expr :: proc(state: ^State, expr_id: int) -> int {
|
||||
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 := -1
|
||||
for len(stack) > 0 {
|
||||
frame_index := len(stack)-1
|
||||
frame := stack[frame_index]
|
||||
if frame.expr < 0 || 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 {
|
||||
switch expr.kind {
|
||||
case .Invalid:
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
case .Integer:
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Const, span=expr.span, type=expr.type, integer=expr.integer,
|
||||
target=-1, a=-1, b=-1, diagnostic=-1,
|
||||
})
|
||||
_ = pop(&stack)
|
||||
case .Local:
|
||||
last = -1
|
||||
if expr.target >= 0 && expr.target < len(state.local_slots) && state.local_slots[expr.target] >= 0 {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Load, span=expr.span, type=expr.type, target=-1,
|
||||
a=state.local_slots[expr.target], b=-1, diagnostic=-1,
|
||||
})
|
||||
} else if expr.target >= 0 && expr.target < len(state.local_values) &&
|
||||
state.local_values[expr.target] >= 0 {
|
||||
last = state.local_values[expr.target]
|
||||
}
|
||||
if last < 0 {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Global:
|
||||
if expr.target < 0 || expr.target >= 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=expr.target,
|
||||
a=-1, b=-1, diagnostic=-1,
|
||||
})
|
||||
}
|
||||
_ = pop(&stack)
|
||||
case .Widen:
|
||||
stack[frame_index].stage = 1
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
case .Add:
|
||||
stack[frame_index].stage = 2
|
||||
append(&stack, Lower_Expr_Frame{expr=expr.left})
|
||||
case .Call:
|
||||
if expr.target < 0 || expr.target >= len(state.hir_module.functions) {
|
||||
last = append_recovery_value(state, expr.span, expr.type, expr.diagnostic)
|
||||
_ = pop(&stack)
|
||||
continue
|
||||
}
|
||||
stack[frame_index].args = make([]int, 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 == 1 {
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Widen, span=expr.span, type=expr.type, target=-1,
|
||||
a=last, b=-1, diagnostic=-1,
|
||||
})
|
||||
_ = 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=.Add_Checked, span=expr.span, type=expr.type, target=-1,
|
||||
a=frame.left, b=last, diagnostic=-1,
|
||||
})
|
||||
_ = 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
|
||||
}
|
||||
}
|
||||
last = append_instruction(state, ir.Instruction{
|
||||
op=.Call, span=expr.span, type=expr.type, target=expr.target,
|
||||
a=-1, b=-1, args=stack[frame_index].args, diagnostic=-1,
|
||||
})
|
||||
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,
|
||||
@@ -186,9 +198,11 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
local_slots=make([]int, 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] = -1
|
||||
@@ -211,6 +225,13 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
switch statement.kind {
|
||||
case .Declaration:
|
||||
value := lower_expr(&state, statement.expr)
|
||||
if statement.local < 0 || statement.local >= len(function.locals) {
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Trap, span=statement.span, type=types.VOID,
|
||||
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic,
|
||||
})
|
||||
continue
|
||||
}
|
||||
local := function.locals[statement.local]
|
||||
if local.mutable {
|
||||
slot := append_instruction(&state, ir.Instruction{
|
||||
@@ -241,6 +262,13 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
if statement.local >= 0 && statement.local < len(state.local_slots) {
|
||||
slot = state.local_slots[statement.local]
|
||||
}
|
||||
if slot < 0 || statement.local < 0 || statement.local >= len(function.locals) {
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Trap, span=statement.span, type=types.VOID,
|
||||
target=-1, a=-1, b=-1, diagnostic=statement.diagnostic,
|
||||
})
|
||||
continue
|
||||
}
|
||||
append_instruction(&state, ir.Instruction{
|
||||
op=.Store,
|
||||
span=statement.span,
|
||||
@@ -311,6 +339,8 @@ lower_body :: proc(hir_module: ^hir.Module, function: hir.Function, allocator: m
|
||||
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,
|
||||
|
||||
Reference in New Issue
Block a user