From 25ecdc4aa217e2c9bd8877bd3d795ca776fe7b0c Mon Sep 17 00:00:00 2001 From: Hannes Bredberg Date: Thu, 5 Oct 2023 01:57:14 +0200 Subject: [PATCH] All: Split loop into loop (no condition) and while (with condition) --- bootstrap/ast.zig | 16 ++++++++++------ bootstrap/ir.zig | 31 ++++++++++++++++++++++++------- bootstrap/parser.zig | 21 ++++++++++++++------- bootstrap/sema.zig | 32 +++++++++++++++++++++++++------- bootstrap/tokenizer.zig | 1 + selfhost/parser.n | 6 +++--- selfhost/tokenizer.n | 16 ++++++++-------- std/mem.n | 8 ++++---- std/os.n | 6 +++--- std/os/syscalls.n | 4 ++-- std/print.n | 2 +- std/string.n | 2 +- tests/loop_with_condition/main.n | 2 +- 13 files changed, 97 insertions(+), 50 deletions(-) diff --git a/bootstrap/ast.zig b/bootstrap/ast.zig index 59c41ae..eff4d39 100644 --- a/bootstrap/ast.zig +++ b/bootstrap/ast.zig @@ -230,7 +230,10 @@ pub const StatementNode = struct { first_not_taken: StmtIndex.OptIndex, }, loop_statement: struct { - condition: ExprIndex.OptIndex, + first_child: StmtIndex.OptIndex, + }, + while_statement: struct { + condition: ExprIndex.Index, first_child: StmtIndex.OptIndex, }, switch_statement: struct { @@ -533,13 +536,14 @@ fn dumpNode(node: anytype, indent_level: usize) anyerror!void { try dumpStatementChain(stmt.first_not_taken, indent_level); } }, + .while_statement => |stmt| { + std.debug.print("while (", .{}); + try dumpNode(expressions.get(stmt.condition), indent_level); + std.debug.print(") ", .{}); + try dumpStatementChain(stmt.first_child, indent_level); + }, .loop_statement => |stmt| { std.debug.print("loop ", .{}); - if(ExprIndex.unwrap(stmt.condition)) |cond_idx| { - std.debug.print("(", .{}); - try dumpNode(expressions.get(cond_idx), indent_level); - std.debug.print(") ", .{}); - } try dumpStatementChain(stmt.first_child, indent_level); }, .break_statement => |stmt| { diff --git a/bootstrap/ir.zig b/bootstrap/ir.zig index 1f1ff70..2a730be 100644 --- a/bootstrap/ir.zig +++ b/bootstrap/ir.zig @@ -1964,6 +1964,26 @@ const IRWriter = struct { self.basic_block = if_exit; }, .loop_statement => |loop| { + const loop_enter_branch = try self.emit(.{.goto = undefined}); + const loop_body_entry = blocks.insert(.{}); + decls.get(loop_enter_branch).instr.goto = try addEdge(self.basic_block, loop_body_entry); + try blocks.get(self.basic_block).filled(); + + const exit_block = blocks.insert(.{}); + stmt.ir_block = BlockIndex.toOpt(exit_block); + const loop_body_end = try self.writeBlockStatementIntoBlock(loop.body.first_stmt, loop_body_entry); + try blocks.get(exit_block).seal(); + + if(loop.body.reaches_end) { + const loop_instr = try self.emit(.{.goto = undefined}); + decls.get(loop_instr).instr.goto = try addEdge(loop_body_end, loop_body_entry); + } + + try blocks.get(loop_body_end).filled(); + try blocks.get(loop_body_entry).seal(); + self.basic_block = exit_block; + }, + .while_statement => |loop| { const original_bb = self.basic_block; const loop_enter_branch = try self.emit(.{.goto = undefined}); try blocks.get(original_bb).filled(); @@ -1971,13 +1991,10 @@ const IRWriter = struct { const loop_body_block = blocks.insert(.{}); const loop_exit_block = blocks.insert(.{}); - var loop_start_block = loop_body_block; - if(sema.ValueIndex.unwrap(loop.condition)) |sema_cond_idx| { - loop_start_block = blocks.insert(.{}); - self.basic_block = loop_start_block; - try self.evaluateShortCircuitingValue(sema_cond_idx, loop_body_block, loop_exit_block); - try blocks.get(loop_start_block).filled(); - } + const loop_start_block = blocks.insert(.{}); + self.basic_block = loop_start_block; + try self.evaluateShortCircuitingValue(loop.condition, loop_body_block, loop_exit_block); + try blocks.get(loop_start_block).filled(); decls.get(loop_enter_branch).instr.goto = try addEdge(original_bb, loop_start_block); stmt.ir_block = BlockIndex.toOpt(loop_exit_block); diff --git a/bootstrap/parser.zig b/bootstrap/parser.zig index 89a2cc0..d66bd05 100644 --- a/bootstrap/parser.zig +++ b/bootstrap/parser.zig @@ -239,15 +239,21 @@ fn parseStatement(self: *@This()) anyerror!ast.StmtIndex.Index { }, .loop_keyword => { _ = try self.tokenize(); - const condition = if ((try self.peekToken()) == .@"(_ch") blk: { - _ = try self.tokenize(); - const res = try self.parseExpression(null); - _ = try self.expect(.@")_ch"); - break :blk ast.ExprIndex.toOpt(res); - } else .none; const body = try self.parseBlockStatement(); return ast.statements.insert(.{.value = .{ .loop_statement = .{ + .first_child = body, + }, + }}); + }, + .while_keyword => { + _ = try self.tokenize(); + _ = try self.expect(.@"(_ch"); + const condition = try self.parseExpression(null); + _ = try self.expect(.@")_ch"); + const body = try self.parseBlockStatement(); + return ast.statements.insert(.{.value = .{ + .while_statement = .{ .condition = condition, .first_child = body, }, @@ -356,6 +362,7 @@ fn parseExpression(self: *@This(), precedence_in: ?usize) anyerror!ast.ExprIndex .continue_keyword => @panic("TODO: Continue expressions"), .endcase_keyword => @panic("TODO: Endcase expressions"), .if_keyword => @panic("TODO: If expressions"), + .while_keyword => @panic("TODO: While expressions"), .loop_keyword => @panic("TODO: Loop expressions"), .switch_keyword => @panic("TODO: Switch expressions"), .unreachable_keyword => return .@"unreachable", @@ -679,7 +686,7 @@ fn parseExpression(self: *@This(), precedence_in: ?usize) anyerror!ast.ExprIndex .@":_ch", .@"~_ch", .@"!_ch", .break_keyword, .case_keyword, .const_keyword, .continue_keyword, .else_keyword, .endcase_keyword, .enum_keyword, .fn_keyword, - .if_keyword, .loop_keyword, .return_keyword, .struct_keyword, + .if_keyword, .loop_keyword, .while_keyword, .return_keyword, .struct_keyword, .switch_keyword, .var_keyword, .volatile_keyword, .__keyword, .bool_keyword, .type_keyword, .void_keyword, .anyopaque_keyword, .end_of_file, .true_keyword, .false_keyword, .undefined_keyword, .comptime_keyword, diff --git a/bootstrap/sema.zig b/bootstrap/sema.zig index 5660143..ec88a9c 100644 --- a/bootstrap/sema.zig +++ b/bootstrap/sema.zig @@ -523,19 +523,33 @@ fn analyzeStatementChain( const body_scope = scopes.insert(.{ .outer_scope = ScopeIndex.toOpt(block_scope_idx), }); - const condition = if(ast.ExprIndex.unwrap(loop.condition)) |loop_cond_idx| blk: { - break :blk ValueIndex.toOpt(try semaASTExpr(block_scope_idx, loop_cond_idx, false, .bool, null)); - } else .none; - const loop_stmt_idx = stmt_builder.insert(.{.value = .{.loop_statement = .{.condition = condition, .body = undefined, .breaks = false}}}); + const loop_stmt_idx = stmt_builder.insert(.{.value = .{.loop_statement = .{.body = undefined, .breaks = false}}}); const body = try analyzeStatementChain(body_scope, loop.first_child, return_type, StatementIndex.toOpt(loop_stmt_idx)); const loop_stmt = statements.get(loop_stmt_idx); loop_stmt.value.loop_statement.body = body; - reaches_end = loop_stmt.value.loop_statement.breaks or condition != .none; + reaches_end = loop_stmt.value.loop_statement.breaks; + }, + .while_statement => |loop| { + const body_scope = scopes.insert(.{ + .outer_scope = ScopeIndex.toOpt(block_scope_idx), + }); + const condition = try semaASTExpr(block_scope_idx, loop.condition, false, .bool, null); + const while_stmt_idx = stmt_builder.insert(.{.value = .{.while_statement = .{.condition = condition, .body = undefined, .breaks = false}}}); + const body = try analyzeStatementChain(body_scope, loop.first_child, return_type, StatementIndex.toOpt(while_stmt_idx)); + statements.get(while_stmt_idx).value.while_statement.body = body; + reaches_end = true; }, .break_statement => { if(StatementIndex.unwrap(current_break_block)) |break_block| { reaches_end = false; - statements.get(break_block).value.loop_statement.breaks = true; + switch(statements.get(break_block).value) { + inline + .loop_statement, .while_statement + => |*stmt| { + stmt.breaks = true; + }, + else => unreachable, + } _ = stmt_builder.insert(.{.value = .{.break_statement = break_block}}); } else { return error.BreakOutsideLoop; @@ -2446,8 +2460,12 @@ pub const Statement = struct { taken: Block, not_taken: Block, }, + while_statement: struct { + condition: ValueIndex.Index, + body: Block, + breaks: bool, + }, loop_statement: struct { - condition: ValueIndex.OptIndex, body: Block, breaks: bool, }, diff --git a/bootstrap/tokenizer.zig b/bootstrap/tokenizer.zig index aae854e..1c55ad4 100644 --- a/bootstrap/tokenizer.zig +++ b/bootstrap/tokenizer.zig @@ -120,6 +120,7 @@ pub const Token = union(enum) { unreachable_keyword, var_keyword, volatile_keyword, + while_keyword, __keyword, diff --git a/selfhost/parser.n b/selfhost/parser.n index ea54c8d..e103c39 100644 --- a/selfhost/parser.n +++ b/selfhost/parser.n @@ -183,7 +183,7 @@ fn parse_statement(context: *ParserContext) u32 { fn parse_block_body(context: *ParserContext) u32 { var stmts = builder.Builder{}; - loop(context.peek() != .@"}") { + while(context.peek() != .@"}") { stmts.add(parse_statement(context), node_next.ptr()); } return stmts.head; @@ -199,7 +199,7 @@ fn parse_block(context: *ParserContext) u32 { fn parse_function_expression(context: *ParserContext) u32 { context.expect("Expected '(' before parameter list".&, .@"("); var stmt_builder = builder.Builder{}; - loop(context.peek() != .@")") { + while(context.peek() != .@")") { if(context.peek() == .comptime_keyword) { context.advance(); } @@ -374,7 +374,7 @@ fn parse_primary_with_postfix(context: *ParserContext, require: bool, precedence else if(p == .@"(") { result = context.add_advance(.function_call); var argument_builder = builder.Builder{}; - loop(context.peek() != .@")") { + while(context.peek() != .@")") { argument_builder.add(parse_expression(context), node_next.ptr()); if(context.peek() == .@",") { context.advance(); diff --git a/selfhost/tokenizer.n b/selfhost/tokenizer.n index 209a8da..452c43e 100644 --- a/selfhost/tokenizer.n +++ b/selfhost/tokenizer.n @@ -190,17 +190,17 @@ fn init() void { { var ch: u32 = 0; - loop(ch <= 0xFF) { + while(ch <= 0xFF) { is_ident_char[ch] = false; ch += 1; } ch = 'a'; - loop(ch <= 'z') { + while(ch <= 'z') { is_ident_char[ch] = true; ch += 1; } ch = 'A'; - loop(ch <= 'Z') { + while(ch <= 'Z') { is_ident_char[ch] = true; ch += 1; } @@ -324,22 +324,22 @@ fn init() void { { var ch: u32 = 0; - loop(ch <= 0xFF) { + while(ch <= 0xFF) { digit_value[ch] = 0xFF; ch += 1; } ch = '0'; - loop(ch <= '9') { + while(ch <= '9') { digit_value[ch] = @truncate(u8, ch) - '0'; ch += 1; } ch = 'a'; - loop(ch <= 'f') { + while(ch <= 'f') { digit_value[ch] = @truncate(u8, ch) - 'a'; ch += 1; } ch = 'A'; - loop(ch <= 'F') { + while(ch <= 'F') { digit_value[ch] = @truncate(u8, ch) - 'A'; ch += 1; } @@ -359,7 +359,7 @@ fn init() void { base = 2; } } - loop(digit_value[context.peek(0)] < base) { + while(digit_value[context.peek(0)] < base) { context.advance(1); } }.&); diff --git a/std/mem.n b/std/mem.n index 9686825..86da0ce 100644 --- a/std/mem.n +++ b/std/mem.n @@ -13,7 +13,7 @@ fn swap(comptime T: type, a: *T, b: *T) inline void { } fn reverse_range(comptime T: type, beg: *T, end: *T) void { - loop(beg < end) { + while(beg < end) { swap_no_check_equal(T, beg, end); beg += 1; end -= 1; @@ -26,7 +26,7 @@ fn reverse(comptime T: type, ptr: *T, len: u64) inline void { fn copy(dest: *u8, src: *const u8, len: u64) void { var offset: u64 = 0; - loop(offset < len) { + while(offset < len) { dest[offset] = src[offset]; offset += 1; } @@ -34,7 +34,7 @@ fn copy(dest: *u8, src: *const u8, len: u64) void { fn set(dest: *u8, value: u8, len: u64) void { var offset: u64 = 0; - loop(offset < len) { + while(offset < len) { dest[offset] = value; offset += 1; } @@ -54,7 +54,7 @@ fn zeroes(comptime T: type, comptime len: u64) inline |out_buf| [len]T { fn equals(comptime T: type, lhs: *const T, rhs: *const T, len: u64) bool { var i: u64 = 0; - loop(i < len) { + while(i < len) { if(lhs[i] != rhs[i]) { return false; } diff --git a/std/os.n b/std/os.n index 1ac9763..59ed3dc 100644 --- a/std/os.n +++ b/std/os.n @@ -21,21 +21,21 @@ fn dir_name(path: *const u8) |out_buf| [MAX_PATH]u8 { return; } var i = string.len(path) - 1; - loop(path[i] == '/') { + while(path[i] == '/') { i -= 1; } if(i == 0) { std.mem.copy(@int_to_ptr(*u8, @ptr_to_int(out_buf)), "/".&, 2); return; } - loop(path[i] != '/') { + while(path[i] != '/') { i -= 1; } if(i == 0) { std.mem.copy(@int_to_ptr(*u8, @ptr_to_int(out_buf)), ".".&, 2); return; } - loop(path[i] == '/') { + while(path[i] == '/') { i -= 1; } std.mem.copy(@int_to_ptr(*u8, @ptr_to_int(out_buf)), path, i + 1); diff --git a/std/os/syscalls.n b/std/os/syscalls.n index 2c7e177..ee76903 100644 --- a/std/os/syscalls.n +++ b/std/os/syscalls.n @@ -21,7 +21,7 @@ fn read(fd: u32, buf: *u8, size: u64) inline u64 { } fn read_all(fd: u32, buf: *u8, size: u64) u64 { - loop(size > 0) { + while(size > 0) { const bytes_read = read(fd, buf, size); size -= bytes_read; buf += bytes_read; @@ -33,7 +33,7 @@ fn write(fd: u32, buf: *const u8, size: u64) inline u64 { } fn write_all(fd: u32, buf: *const u8, size: u64) void { - loop(size > 0) { + while(size > 0) { const written = write(fd, buf, size); size -= written; buf += written; diff --git a/std/print.n b/std/print.n index db889b2..5c82cde 100644 --- a/std/print.n +++ b/std/print.n @@ -45,7 +45,7 @@ fn log_hex_upper(s: *const u8, value: u64) void { } fn str(s: *const u8) void { - loop(s.* != 0) { + while(s.* != 0) { char(s.*); s += 1; } diff --git a/std/string.n b/std/string.n index 31ac884..e4c571b 100644 --- a/std/string.n +++ b/std/string.n @@ -20,7 +20,7 @@ fn write_u32_decimal(value: u32) inline |out_buf| [11]u8 { fn len(string: *const u8) u64 { var result: u64 = 0; - loop(string[result] != 0) { + while(string[result] != 0) { result += 1; } return result; diff --git a/tests/loop_with_condition/main.n b/tests/loop_with_condition/main.n index 3e42acd..ec68671 100644 --- a/tests/loop_with_condition/main.n +++ b/tests/loop_with_condition/main.n @@ -3,7 +3,7 @@ const test_lib = @import("../test_lib.n"); fn main() noreturn { var i: u32 = 0; - loop(i < 10) { + while(i < 10) { std.print.unsigned_decimal(i); i += 1; }