From bd6c637f7c01d9d1c9223562a08c610d3473ac20 Mon Sep 17 00:00:00 2001 From: David Gonzalez Martin Date: Mon, 12 Feb 2024 12:53:05 -0600 Subject: [PATCH] Fix range compute --- bootstrap/Compilation.zig | 162 ++++++++++++++++++++++++----- bootstrap/backend/llvm.zig | 20 ++-- lib/std/start.nat | 6 +- lib/std/std.nat | 1 + test/standalone/slice_len/main.nat | 11 ++ 5 files changed, 159 insertions(+), 41 deletions(-) create mode 100644 test/standalone/slice_len/main.nat diff --git a/bootstrap/Compilation.zig b/bootstrap/Compilation.zig index b63d1fe..cef6fac 100644 --- a/bootstrap/Compilation.zig +++ b/bootstrap/Compilation.zig @@ -717,6 +717,7 @@ pub const Instruction = union(enum) { pub const BasicBlock = struct{ instructions: ArrayList(Instruction.Index) = .{}, + useful_instructions: usize = 0, predecessor: BasicBlock.Index = .null, // TODO: not use a bool terminated: bool = false, @@ -812,6 +813,7 @@ pub const V = struct{ pub const Comptime = union(enum){ unresolved: Node.Index, undefined, + void, type: Type.Index, bool: bool, comptime_int: ComptimeInt, @@ -1476,9 +1478,7 @@ pub const Builder = struct { try builder.branch(unit, context, instruction_index, true_block, false_block); builder.current_basic_block = false_block; - const unreachable_instruction = try unit.instructions.append(context.allocator, .@"unreachable"); - // TODO: terminate block properly - try builder.appendInstruction(unit, context, unreachable_instruction); + try builder.buildUnreachable(unit, context); builder.current_basic_block = true_block; }, @@ -1639,6 +1639,38 @@ pub const Builder = struct { fn appendInstruction(builder: *Builder, unit: *Unit, context: *const Context, instruction_index: Instruction.Index) !void { const basic_block = unit.basic_blocks.get(builder.current_basic_block); if (!basic_block.terminated) { + basic_block.useful_instructions += @intFromBool(switch (unit.instructions.get(instruction_index).*) { + .argument_declaration, + .branch, + .call, + .cast, + .constant_int, + .extract_value, + .insert_value, + .get_element_pointer, + .global, + .inline_assembly, + .integer_compare, + .integer_binary_operation, + .jump, + .load, + .umin, + .smin, + .phi, + .ret, + .ret_void, + .stack_slot, + .store, + .syscall, + .@"unreachable", + => true, + .block, + .pop_scope, + .push_scope, + .debug_checkpoint, + .debug_declare_local_variable, + => false, + }); try basic_block.instructions.append(context.allocator, instruction_index); } else { const instruction = unit.instructions.get(instruction_index); @@ -1944,6 +1976,35 @@ pub const Builder = struct { if (body_node.id == .block) { function.body = try builder.resolveBlock(unit, context, body_node_index); + if (builder.return_block == .null) { + const cbb = unit.basic_blocks.get(builder.current_basic_block); + const function_prototype = unit.function_prototypes.get(function_prototype_index); + const return_type = function_prototype.return_type; + + if (!cbb.terminated) { + switch (function_prototype.attributes.naked) { + true => { + assert(return_type == .noreturn); + try builder.buildUnreachable(unit, context); + }, + false => switch (return_type) { + .void => { + try builder.buildRet(unit, context, .{ + .value = .{ + .@"comptime" = .void, + }, + .type = .void, + }); + }, + .noreturn => { + try builder.buildUnreachable(unit, context); + }, + else => unreachable, + }, + } + + } + } const function_definition_index = builder.current_function; @@ -2125,9 +2186,9 @@ pub const Builder = struct { .unsigned => .zero_extend, }; } + } else { + unreachable; } - - unreachable; }, .comptime_int => { return .materialize_int; @@ -3720,6 +3781,17 @@ pub const Builder = struct { }); try builder.appendInstruction(unit, context, slice_builder); + const array_len_value = V{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = array.count, + }, + }, + }, + .type = .usize, + }; + const final_slice = try unit.instructions.append(context.allocator, .{ .insert_value = .{ .expression = V{ @@ -3729,16 +3801,47 @@ pub const Builder = struct { .type = expression_to_slice.type, }, .index = 1, - .new_value = .{ - .value = .{ - .@"comptime" = .{ - .constant_int = .{ - .value = array.count, + .new_value = switch (range_start.value) { + .runtime => b: { + const range_compute = try unit.instructions.append(context.allocator, .{ + .integer_binary_operation = .{ + .id = .sub, + .left = array_len_value, + .right = range_start, + .signedness = .unsigned, }, - }, + }); + try builder.appendInstruction(unit, context, range_compute); + + break :b V{ + .value = .{ + .runtime = range_compute, + }, + .type = .usize, + }; }, - .type = .usize, + .@"comptime" => |ct| b: { + const range_start_value = switch (ct) { + .constant_int => |const_int| const_int.value, + else => |t| @panic(@tagName(t)), + }; + + const range = array.count - range_start_value; + + break :b V{ + .value = .{ + .@"comptime" = .{ + .constant_int = .{ + .value = range, + }, + }, + }, + .type = .usize, + }; + }, + else => |t| @panic(@tagName(t)), }, + // }, }, }); try builder.appendInstruction(unit, context, final_slice); @@ -4772,24 +4875,18 @@ pub const Builder = struct { const phi = &unit.instructions.get(builder.return_phi).phi; try phi.values.append(context.allocator, return_value); try phi.basic_blocks.append(context.allocator, current_basic_block); - - const ret = try unit.instructions.append(context.allocator, .{ - .ret = .{ - .value = .{ - .runtime = builder.return_phi, - }, - .type = return_type, + + try builder.buildRet(unit, context, .{ + .value = .{ + .runtime = builder.return_phi, }, + .type = return_type, }); - try builder.appendInstruction(unit, context, ret); builder.current_basic_block = current_basic_block; try builder.jump(unit, context, builder.return_block); } else { - const ret = try unit.instructions.append(context.allocator, .{ - .ret = return_value, - }); - try builder.appendInstruction(unit, context, ret); + try builder.buildRet(unit, context, return_value); } }, .call => { @@ -4827,8 +4924,7 @@ pub const Builder = struct { } }, .@"unreachable" => { - const instruction = try unit.instructions.append(context.allocator, .@"unreachable"); - try builder.appendInstruction(unit, context, instruction); + try builder.buildUnreachable(unit, context); }, .@"while" => { assert(statement_node.left != .null); @@ -5586,6 +5682,20 @@ pub const Builder = struct { return result; } + + fn buildUnreachable(builder: *Builder, unit: *Unit, context: *const Context) !void { + const instruction = try unit.instructions.append(context.allocator, .@"unreachable"); + try builder.appendInstruction(unit, context, instruction); + unit.basic_blocks.get(builder.current_basic_block).terminated = true; + } + + fn buildRet(builder: *Builder, unit: *Unit, context: *const Context, value: V) !void { + const ret = try unit.instructions.append(context.allocator, .{ + .ret = value, + }); + try builder.appendInstruction(unit, context, ret); + unit.basic_blocks.get(builder.current_basic_block).terminated = true; + } }; pub const Enum = struct { diff --git a/bootstrap/backend/llvm.zig b/bootstrap/backend/llvm.zig index cb92eed..2281b8b 100644 --- a/bootstrap/backend/llvm.zig +++ b/bootstrap/backend/llvm.zig @@ -3179,7 +3179,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo } }, .ret => |return_value| { - const value = try llvm.emitRightValue(unit, context, return_value); + const value = switch (return_value.type) { + .void => null, + else => try llvm.emitRightValue(unit, context, return_value), + }; // const value = llvm.llvm_value_map.get(return_value).?; const ret = llvm.builder.createRet(value) orelse return LLVM.Value.Instruction.Error.ret; _ = ret; // autofix @@ -3463,17 +3466,10 @@ pub fn codegen(unit: *Compilation.Unit, context: *const Compilation.Context) !vo } if (!builder.isCurrentBlockTerminated()) { - const return_type = function_prototype.return_type; - if (return_type == Compilation.Type.Index.noreturn) { - _ = builder.createUnreachable() orelse return LLVM.Value.Instruction.Error.@"unreachable"; - } else if (return_type == Compilation.Type.Index.void) { - _ = builder.createRet(null) orelse unreachable; - } else { - var message_len: usize = 0; - const function_str = llvm.function.toString(&message_len); - const function_dump = function_str[0..message_len]; - std.debug.panic("Function block with no termination:\n{s}\n", .{function_dump}); - } + var message_len: usize = 0; + const function_str = llvm.function.toString(&message_len); + const function_dump = function_str[0..message_len]; + std.debug.panic("Function block with no termination:\n{s}\n", .{function_dump}); } const verify_function = true; diff --git a/lib/std/start.nat b/lib/std/start.nat index 10a4567..042579d 100644 --- a/lib/std/start.nat +++ b/lib/std/start.nat @@ -8,7 +8,7 @@ comptime { } } -const _start:: export = fn naked() noreturn { +const _start :: export = fn naked() noreturn { #asm({ xor ebp, ebp; mov rdi, rsp; @@ -21,7 +21,7 @@ var argument_count: usize = 0; var argument_values: [&]const [&:0]const u8 = undefined; var environment_values: [&:null]const ?[&:null]const u8 = undefined; -const start:: export = fn (argc_argv_address: usize) noreturn { +const start :: export = fn (argc_argv_address: usize) noreturn { var argument_address_iterator = argc_argv_address; const argument_count_ptr: &usize = #cast(argument_address_iterator); argument_count = argument_count_ptr.@; @@ -33,6 +33,6 @@ const start:: export = fn (argc_argv_address: usize) noreturn { std.os.exit(exit_code = result); } -const main:: export = fn (argc: s32, argv: [&:null]?[&:null]u8, env: [&:null]?[&:null]u8) s32 { +const main :: export = fn (argc: s32, argv: [&:null]?[&:null]u8, env: [&:null]?[&:null]u8) s32 { return #import("main").main(); } diff --git a/lib/std/std.nat b/lib/std/std.nat index 195f262..0ce116e 100644 --- a/lib/std/std.nat +++ b/lib/std/std.nat @@ -42,6 +42,7 @@ const format_usize = fn(n: usize, buffer: &[65]u8) []u8 { const print_usize = fn(n: usize) void { var buffer: [65]u8 = undefined; const bytes = format_usize(n, buffer = buffer.&); + #assert(bytes.len < buffer.len); const file_descriptor = os.StdFileDescriptor.get(descriptor = .stdout); const file_writer = FileWriter{ .descriptor = file_descriptor, diff --git a/test/standalone/slice_len/main.nat b/test/standalone/slice_len/main.nat new file mode 100644 index 0000000..46edb2e --- /dev/null +++ b/test/standalone/slice_len/main.nat @@ -0,0 +1,11 @@ + +const main = fn() s32 { + var buffer: [65]u8 = undefined; + const slice = foo(5, buffer.&); + #assert(slice.len + 5 == buffer.len); + const result: u32 = #cast(slice.len + 5 - buffer.len); + return #cast(result); +} +const foo = fn(n: usize, buffer: &[65]u8) []u8 { + return buffer[n..]; +}