harden compiler parsing, recovery, and deep-expression handling
This commit is contained in:
+109
-19
@@ -40,9 +40,37 @@ function_result_type :: proc(function: ir.Function) -> string {
|
||||
return llvm_type(function.result)
|
||||
}
|
||||
|
||||
write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: int) {
|
||||
if value_id < 0 || value_id >= len(instructions) {
|
||||
fmt.sbprintf(builder, "-6148914691236517206")
|
||||
sentinel :: proc(value_type: types.Type) -> i64 {
|
||||
switch value_type.bits {
|
||||
case 8: return -86
|
||||
case 16: return -21846
|
||||
case 32: return -1431655766
|
||||
case: return -6148914691236517206
|
||||
}
|
||||
}
|
||||
|
||||
valid_instruction :: proc(instructions: []ir.Instruction, instruction_id: int) -> bool {
|
||||
return instruction_id >= 0 && instruction_id < len(instructions)
|
||||
}
|
||||
|
||||
valid_value :: proc(instructions: []ir.Instruction, value_id: int, expected: types.Type) -> bool {
|
||||
if !valid_instruction(instructions, value_id) ||
|
||||
!types.is_concrete_integer(expected) ||
|
||||
!types.equal(instructions[value_id].type, expected) {
|
||||
return false
|
||||
}
|
||||
switch instructions[value_id].op {
|
||||
case .Param, .Const, .Load_Global, .Load, .Widen, .Add_Checked, .Call:
|
||||
return true
|
||||
case .Alloca, .Store, .Trap, .Return, .Return_Void:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
write_operand :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, value_id: int, expected: types.Type) {
|
||||
if !valid_value(instructions, value_id, expected) {
|
||||
fmt.sbprintf(builder, "%d", sentinel(expected))
|
||||
return
|
||||
}
|
||||
value := instructions[value_id]
|
||||
@@ -95,13 +123,27 @@ emit_trap_call :: proc(emitter: ^Emitter, message_id: int) {
|
||||
)
|
||||
}
|
||||
|
||||
emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []int) {
|
||||
emit_recovery_value :: proc(emitter: ^Emitter, instruction_id: int, instruction: ir.Instruction, fallback: string) {
|
||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, fallback)
|
||||
emit_trap_call(emitter, message)
|
||||
if types.is_concrete_integer(instruction.type) {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = add %s 0, %d\n",
|
||||
instruction_id,
|
||||
llvm_type(instruction.type),
|
||||
sentinel(instruction.type),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
emit_call_args :: proc(builder: ^strings.Builder, instructions: []ir.Instruction, args: []int, param_types: []types.Type) {
|
||||
for arg, index in args {
|
||||
if index > 0 {
|
||||
strings.write_string(builder, ", ")
|
||||
}
|
||||
fmt.sbprintf(builder, "%s ", llvm_type(instructions[arg].type))
|
||||
write_operand(builder, instructions, arg)
|
||||
fmt.sbprintf(builder, "%s ", llvm_type(param_types[index]))
|
||||
write_operand(builder, instructions, arg, param_types[index])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -122,11 +164,14 @@ emit_instruction_stream :: proc(
|
||||
case .Param, .Const:
|
||||
case .Load_Global:
|
||||
if instruction.target < 0 || instruction.target >= len(emitter.module.globals) {
|
||||
message := diagnostic_message(emitter, -1, instruction.span, "invalid global reference")
|
||||
emit_trap_call(emitter, message)
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference")
|
||||
continue
|
||||
}
|
||||
global := emitter.module.globals[instruction.target]
|
||||
if !types.equal(instruction.type, global.type) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid global reference type")
|
||||
continue
|
||||
}
|
||||
if global.is_static {
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
@@ -145,8 +190,18 @@ emit_instruction_stream :: proc(
|
||||
)
|
||||
}
|
||||
case .Alloca:
|
||||
if !types.is_concrete_integer(instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid allocation type")
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = alloca %s\n", instruction_id, llvm_type(instruction.type))
|
||||
case .Load:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
instructions[instruction.a].op != .Alloca ||
|
||||
!types.equal(instructions[instruction.a].type, instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid load slot")
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(
|
||||
&emitter.builder,
|
||||
" %%v%d = load %s, ptr %%v%d\n",
|
||||
@@ -155,22 +210,41 @@ emit_instruction_stream :: proc(
|
||||
instruction.a,
|
||||
)
|
||||
case .Store:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
instructions[instruction.a].op != .Alloca ||
|
||||
!types.equal(instructions[instruction.a].type, instruction.type) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid store operand")
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", llvm_type(instruction.type))
|
||||
write_operand(&emitter.builder, instructions, instruction.b)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%v%d\n", instruction.a)
|
||||
case .Widen:
|
||||
if !valid_instruction(instructions, instruction.a) ||
|
||||
!types.is_concrete_integer(instructions[instruction.a].type) ||
|
||||
!types.is_concrete_integer(instruction.type) ||
|
||||
instructions[instruction.a].type.bits >= instruction.type.bits {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid widening operand")
|
||||
continue
|
||||
}
|
||||
from_type := instructions[instruction.a].type
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = sext %s ", instruction_id, llvm_type(from_type))
|
||||
write_operand(&emitter.builder, instructions, instruction.a)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, from_type)
|
||||
fmt.sbprintf(&emitter.builder, " to %s\n", llvm_type(instruction.type))
|
||||
case .Add_Checked:
|
||||
if !valid_value(instructions, instruction.a, instruction.type) ||
|
||||
!valid_value(instructions, instruction.b, instruction.type) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid addition operand")
|
||||
continue
|
||||
}
|
||||
type_name := llvm_type(instruction.type)
|
||||
fmt.sbprintf(&emitter.builder, " %%pair%d = call ", instruction_id)
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
fmt.sbprintf(&emitter.builder, "%s, i1 } @llvm.sadd.with.overflow.%s(%s ", type_name, type_name, type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.a)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instruction.type)
|
||||
fmt.sbprintf(&emitter.builder, ", %s ", type_name)
|
||||
write_operand(&emitter.builder, instructions, instruction.b)
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type)
|
||||
fmt.sbprintf(&emitter.builder, ")\n")
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = extractvalue ", instruction_id)
|
||||
strings.write_string(&emitter.builder, "{ ")
|
||||
@@ -191,11 +265,27 @@ emit_instruction_stream :: proc(
|
||||
fmt.sbprintf(&emitter.builder, " unreachable\noverflow_continue%d:\n", instruction_id)
|
||||
case .Call:
|
||||
if instruction.target < 0 || instruction.target >= len(emitter.module.functions) {
|
||||
message := diagnostic_message(emitter, -1, instruction.span, "invalid function specialization")
|
||||
emit_trap_call(emitter, message)
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid function specialization")
|
||||
continue
|
||||
}
|
||||
target := emitter.module.functions[instruction.target]
|
||||
valid_args := len(instruction.args) == len(target.param_types)
|
||||
if valid_args {
|
||||
for arg, index in instruction.args {
|
||||
if !valid_value(instructions, arg, target.param_types[index]) {
|
||||
valid_args = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
target_result := target.result
|
||||
if target.is_main {
|
||||
target_result = types.I32
|
||||
}
|
||||
if !valid_args || !types.equal(instruction.type, target_result) {
|
||||
emit_recovery_value(emitter, instruction_id, instruction, "invalid function call operands")
|
||||
continue
|
||||
}
|
||||
if instruction.type.kind != .Void {
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = ", instruction_id)
|
||||
} else {
|
||||
@@ -206,7 +296,7 @@ emit_instruction_stream :: proc(
|
||||
strings.write_string(&emitter.builder, "fastcc ")
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, "%s @%s(", function_result_type(target), target.link_name)
|
||||
emit_call_args(&emitter.builder, instructions, instruction.args)
|
||||
emit_call_args(&emitter.builder, instructions, instruction.args, target.param_types)
|
||||
strings.write_string(&emitter.builder, ")\n")
|
||||
case .Trap:
|
||||
message := diagnostic_message(emitter, instruction.diagnostic, instruction.span, "invalid recovered source")
|
||||
@@ -217,7 +307,7 @@ emit_instruction_stream :: proc(
|
||||
continue
|
||||
}
|
||||
fmt.sbprintf(&emitter.builder, " ret %s ", function_result_type(function))
|
||||
write_operand(&emitter.builder, instructions, instruction.a)
|
||||
write_operand(&emitter.builder, instructions, instruction.a, function.result)
|
||||
strings.write_string(&emitter.builder, "\n")
|
||||
after_return = true
|
||||
case .Return_Void:
|
||||
@@ -286,11 +376,11 @@ emit_global_accessors :: proc(emitter: ^Emitter) {
|
||||
placeholder_function.result = global.type
|
||||
value := emit_instruction_stream(emitter, global.initializer, placeholder_function, true)
|
||||
fmt.sbprintf(&emitter.builder, " store %s ", type_name)
|
||||
write_operand(&emitter.builder, global.initializer, value)
|
||||
write_operand(&emitter.builder, global.initializer, value, global.type)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr @bro.g.%d\n", global_id)
|
||||
fmt.sbprintf(&emitter.builder, " store i8 2, ptr @bro.gstate.%d\n", global_id)
|
||||
fmt.sbprintf(&emitter.builder, " ret %s ", type_name)
|
||||
write_operand(&emitter.builder, global.initializer, value)
|
||||
write_operand(&emitter.builder, global.initializer, value, global.type)
|
||||
strings.write_string(&emitter.builder, "\nready:\n")
|
||||
fmt.sbprintf(&emitter.builder, " %%value = load %s, ptr @bro.g.%d\n ret %s %%value\n}\n\n", type_name, global_id, type_name)
|
||||
}
|
||||
@@ -383,7 +473,7 @@ emit_declarations :: proc(emitter: ^Emitter) {
|
||||
}
|
||||
strings.write_string(
|
||||
&emitter.builder,
|
||||
"\ndefine internal void @bro.trap(ptr %message, i64 %length) {\nentry:\n %written = call i64 @write(i32 2, ptr %message, i64 %length)\n call void @llvm.trap()\n unreachable\n}\n\n",
|
||||
"\ndefine internal void @bro.trap(ptr %message, i64 %length) noreturn {\nentry:\n %written = call i64 @write(i32 2, ptr %message, i64 %length)\n call void @llvm.trap()\n unreachable\n}\n\n",
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user