feat: preprocess native argv into sentinel slices
This commit is contained in:
@@ -1764,15 +1764,15 @@ configure_entry_point :: proc(checker: ^Checker) {
|
||||
args_item, args_ok := types.node(&checker.module.types, args_type)
|
||||
args_fields := types.fields_for(&checker.module.types, args_type)
|
||||
values_name := symbol.intern(checker.symbols, "values")
|
||||
argument_pointer := types.pointer(&checker.module.types, types.U8, false, true, true, 0)
|
||||
argument_values := types.slice(&checker.module.types, argument_pointer, false)
|
||||
argument_slice := types.slice(&checker.module.types, types.U8, false, true, 0)
|
||||
argument_values := types.slice(&checker.module.types, argument_slice, false)
|
||||
if !args_ok || args_item.kind != .Struct || args_item.tuple || args_item.c_layout ||
|
||||
len(args_fields) != 1 || args_fields[0].name != u32(values_name) ||
|
||||
!types.equal(types.resolve_alias(args_fields[0].type, &checker.module.types), argument_values) {
|
||||
checker.template_diagnostics[main_template] = source.add(
|
||||
checker.diagnostics,
|
||||
main.span,
|
||||
"@std/process Args must be an auto-layout record containing exactly 'values [][*;0]u8'",
|
||||
"@std/process Args must be an auto-layout record containing exactly 'values [][;0]u8'",
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -86,6 +86,9 @@ Opcode :: enum u8 {
|
||||
Store,
|
||||
Fill,
|
||||
Slice,
|
||||
// Private native-entry operations: own descriptors, borrow argument strings.
|
||||
Prepare_Args,
|
||||
Free_Args,
|
||||
Length,
|
||||
Slice_Ptr,
|
||||
Extract,
|
||||
|
||||
+79
-2
@@ -20,6 +20,7 @@ Emitter :: struct {
|
||||
builder: strings.Builder,
|
||||
messages: [dynamic]Trap_Message,
|
||||
allocator: mem.Allocator,
|
||||
prepares_args: bool,
|
||||
}
|
||||
|
||||
C_Record_ABI_Kind :: enum u8 {
|
||||
@@ -284,7 +285,7 @@ valid_value :: proc(
|
||||
}
|
||||
switch instructions[value_id].op {
|
||||
case .Param, .Const, .Poison, .String, .Aggregate, .Null, .Optional_Some,
|
||||
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Length, .Slice_Ptr,
|
||||
.Load_Global, .Function_Address, .Address_Of, .Load, .Union_Tag, .Slice, .Prepare_Args, .Length, .Slice_Ptr,
|
||||
.Fallible_Error, .Extract, .Select, .Unwrap,
|
||||
.Optional_Is_Some, .Optional_Value, .Orelse,
|
||||
.Widen, .Sum_Widen, .Sum_Project, .C_Coerce, .C_Vararg_Promote, .Retype, .Scalar_Cast, .Pointer_Cast, .Const_Cast, .Weaken_Pointer, .Weaken_Slice, .Decay_Array_Pointer,
|
||||
@@ -295,7 +296,7 @@ valid_value :: proc(
|
||||
.Shift_Left, .Shift_Right, .Shift_Left_Saturating, .Compare, .Call:
|
||||
return true
|
||||
case .Address_Global, .Alloca, .Index_Address, .Field_Address, .Orelse_Begin,
|
||||
.Store, .Fill, .Mem_Copy, .Mem_Set, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
|
||||
.Store, .Fill, .Free_Args, .Mem_Copy, .Mem_Set, .Trap, .Label, .Br, .Cond_Br, .Return, .Return_Void:
|
||||
return false
|
||||
}
|
||||
return false
|
||||
@@ -1545,6 +1546,16 @@ emit_instruction_stream :: proc(
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instruction.type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", ptr %%memset_element%d\n", instruction_index)
|
||||
fmt.sbprintf(&emitter.builder, " %%memset_next%d = add i64 %%memset_index%d, 1\n store i64 %%memset_next%d, ptr %%memset_index_slot%d\n br label %%memset_loop%d\nmemset_done%d:\n", instruction_index, instruction_index, instruction_index, instruction_index, instruction_index, instruction_index)
|
||||
case .Prepare_Args:
|
||||
fmt.sbprintf(&emitter.builder, " %%v%d = call %s @bro.prepare_args(i32 ", instruction_index, llvm_type(instruction.type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, types.C_INT, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ", ptr ")
|
||||
write_operand(&emitter.builder, instructions, instruction.b, instructions[instruction.b].type, &emitter.module.types)
|
||||
strings.write_string(&emitter.builder, ")\n")
|
||||
case .Free_Args:
|
||||
fmt.sbprintf(&emitter.builder, " %%args_data%d = extractvalue %s ", instruction_index, llvm_type(instructions[instruction.a].type, &emitter.module.types))
|
||||
write_operand(&emitter.builder, instructions, instruction.a, instructions[instruction.a].type, &emitter.module.types)
|
||||
fmt.sbprintf(&emitter.builder, ", 0\n call void @free(ptr %%args_data%d)\n", instruction_index)
|
||||
case .Slice:
|
||||
if !valid_instruction(instructions, instruction.a) {
|
||||
emit_recovery_value(emitter, instruction_index, instruction, "invalid slice container")
|
||||
@@ -2746,6 +2757,11 @@ emit_functions :: proc(emitter: ^Emitter) {
|
||||
if function.implementation == .Declaration && function.link_name == "write" {
|
||||
continue
|
||||
}
|
||||
if emitter.prepares_args && function.implementation == .Declaration &&
|
||||
(function.link_name == "malloc" || function.link_name == "free" ||
|
||||
function.link_name == "strlen" || function.link_name == "exit") {
|
||||
continue
|
||||
}
|
||||
if function.implementation == .Declaration {
|
||||
duplicate := false
|
||||
for previous in emitter.module.functions[:function_index] {
|
||||
@@ -2880,6 +2896,59 @@ emit_declarations :: proc(emitter: ^Emitter) {
|
||||
&emitter.builder,
|
||||
"\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",
|
||||
)
|
||||
if emitter.prepares_args {
|
||||
emit_argument_runtime(emitter)
|
||||
}
|
||||
}
|
||||
|
||||
// Only native entry wrappers own these descriptors; argument bytes stay borrowed.
|
||||
emit_argument_runtime :: proc(emitter: ^Emitter) {
|
||||
strings.write_string(&emitter.builder, `declare ptr @malloc(i64)
|
||||
declare void @free(ptr)
|
||||
declare i64 @strlen(ptr)
|
||||
declare void @exit(i32) noreturn
|
||||
|
||||
define internal { ptr, i64 } @bro.prepare_args(i32 %argc, ptr %argv) {
|
||||
entry:
|
||||
%negative = icmp slt i32 %argc, 0
|
||||
br i1 %negative, label %failure, label %check_empty
|
||||
check_empty:
|
||||
%count = zext i32 %argc to i64
|
||||
%empty = icmp eq i64 %count, 0
|
||||
br i1 %empty, label %empty_result, label %check_size
|
||||
check_size:
|
||||
%allocation = call { i64, i1 } @llvm.umul.with.overflow.i64(i64 %count, i64 16)
|
||||
%size = extractvalue { i64, i1 } %allocation, 0
|
||||
%overflow = extractvalue { i64, i1 } %allocation, 1
|
||||
br i1 %overflow, label %failure, label %allocate
|
||||
allocate:
|
||||
%data = call ptr @malloc(i64 %size)
|
||||
%failed = icmp eq ptr %data, null
|
||||
br i1 %failed, label %failure, label %loop
|
||||
loop:
|
||||
%index = phi i64 [ 0, %allocate ], [ %next, %loop ]
|
||||
%native = getelementptr ptr, ptr %argv, i64 %index
|
||||
%string = load ptr, ptr %native
|
||||
%length = call i64 @strlen(ptr %string)
|
||||
%descriptor = getelementptr { ptr, i64 }, ptr %data, i64 %index
|
||||
%with_pointer = insertvalue { ptr, i64 } poison, ptr %string, 0
|
||||
%argument = insertvalue { ptr, i64 } %with_pointer, i64 %length, 1
|
||||
store { ptr, i64 } %argument, ptr %descriptor
|
||||
%next = add i64 %index, 1
|
||||
%more = icmp ult i64 %next, %count
|
||||
br i1 %more, label %loop, label %done
|
||||
done:
|
||||
%result_pointer = insertvalue { ptr, i64 } poison, ptr %data, 0
|
||||
%result = insertvalue { ptr, i64 } %result_pointer, i64 %count, 1
|
||||
ret { ptr, i64 } %result
|
||||
empty_result:
|
||||
ret { ptr, i64 } zeroinitializer
|
||||
failure:
|
||||
call void @exit(i32 2)
|
||||
unreachable
|
||||
}
|
||||
|
||||
`)
|
||||
}
|
||||
|
||||
emit :: proc(
|
||||
@@ -2895,6 +2964,14 @@ emit :: proc(
|
||||
builder=strings.builder_make(allocator),
|
||||
allocator=allocator,
|
||||
}
|
||||
for function in module.functions {
|
||||
for instruction in function.instructions {
|
||||
if instruction.op == .Prepare_Args {
|
||||
emitter.prepares_args = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
emitter.messages.allocator = allocator
|
||||
defer {
|
||||
for message in emitter.messages {
|
||||
|
||||
+13
-14
@@ -1784,6 +1784,7 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
||||
instructions.allocator = allocator
|
||||
param_types: []types.Type
|
||||
args: []ir.Instruction_Id
|
||||
prepared_args := ir.INVALID_INSTRUCTION
|
||||
if param_index >= 0 {
|
||||
init_type := main_function.locals[param_index].type
|
||||
init_fields := types.fields_for(&module.types, init_type)
|
||||
@@ -1797,7 +1798,7 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
||||
args_index := 1 - io_index
|
||||
args_type := types.resolve_alias(init_fields[args_index].type, &module.types)
|
||||
values_type := types.resolve_alias(types.fields_for(&module.types, args_type)[0].type, &module.types)
|
||||
argument_pointer := types.child_type(values_type, &module.types)
|
||||
argument_pointer := types.pointer(&module.types, types.U8, false, true, true, 0)
|
||||
argv_type := types.pointer(&module.types, argument_pointer, false, true)
|
||||
param_types = make([]types.Type, 2, allocator)
|
||||
param_types[0] = types.C_INT
|
||||
@@ -1809,23 +1810,14 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
count := ir.instruction_id(len(instructions))
|
||||
prepared_args = ir.instruction_id(len(instructions))
|
||||
append(&instructions, ir.Instruction{
|
||||
op=.Scalar_Cast, type=types.USIZE, target=ir.INVALID_REF,
|
||||
a=ir.Instruction_Id(0), b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
slice_args := make([]ir.Instruction_Id, 2, allocator)
|
||||
slice_args[0] = ir.INVALID_INSTRUCTION
|
||||
slice_args[1] = count
|
||||
values := ir.instruction_id(len(instructions))
|
||||
append(&instructions, ir.Instruction{
|
||||
op=.Slice, type=values_type, args=slice_args, target=ir.INVALID_REF,
|
||||
a=ir.Instruction_Id(1), b=ir.INVALID_INSTRUCTION,
|
||||
op=.Prepare_Args, type=values_type, target=ir.INVALID_REF,
|
||||
a=ir.Instruction_Id(0), b=ir.Instruction_Id(1),
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
values_args := make([]ir.Instruction_Id, 1, allocator)
|
||||
values_args[0] = values
|
||||
values_args[0] = prepared_args
|
||||
process_args := ir.instruction_id(len(instructions))
|
||||
append(&instructions, ir.Instruction{
|
||||
op=.Aggregate, type=args_type, args=values_args, target=ir.INVALID_REF,
|
||||
@@ -1867,6 +1859,13 @@ append_injected_main :: proc(module: ^ir.Module, hir_module: ^hir.Module, alloca
|
||||
b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
if prepared_args != ir.INVALID_INSTRUCTION {
|
||||
append(&instructions, ir.Instruction{
|
||||
op=.Free_Args, type=types.VOID, target=ir.INVALID_REF,
|
||||
a=prepared_args, b=ir.INVALID_INSTRUCTION,
|
||||
diagnostic=source.INVALID_DIAGNOSTIC,
|
||||
})
|
||||
}
|
||||
|
||||
if types.kind(main_function.result, &hir_module.types) == .Fallible {
|
||||
success := types.fallible_success(main_function.result, &hir_module.types)
|
||||
|
||||
Reference in New Issue
Block a user