Skip to content

Commit

Permalink
All: Split loop into loop (no condition) and while (with condition)
Browse files Browse the repository at this point in the history
  • Loading branch information
N00byEdge committed Oct 4, 2023
1 parent 9de1d17 commit 25ecdc4
Show file tree
Hide file tree
Showing 13 changed files with 97 additions and 50 deletions.
16 changes: 10 additions & 6 deletions bootstrap/ast.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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| {
Expand Down
31 changes: 24 additions & 7 deletions bootstrap/ir.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1964,20 +1964,37 @@ 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();

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);
Expand Down
21 changes: 14 additions & 7 deletions bootstrap/parser.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
Expand Down Expand Up @@ -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",
Expand Down Expand Up @@ -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,
Expand Down
32 changes: 25 additions & 7 deletions bootstrap/sema.zig
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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,
},
Expand Down
1 change: 1 addition & 0 deletions bootstrap/tokenizer.zig
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ pub const Token = union(enum) {
unreachable_keyword,
var_keyword,
volatile_keyword,
while_keyword,
__keyword,


Expand Down
6 changes: 3 additions & 3 deletions selfhost/parser.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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();
}
Expand Down Expand Up @@ -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();
Expand Down
16 changes: 8 additions & 8 deletions selfhost/tokenizer.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
}
Expand All @@ -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);
}
}.&);
Expand Down
8 changes: 4 additions & 4 deletions std/mem.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -26,15 +26,15 @@ 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;
}
}

fn set(dest: *u8, value: u8, len: u64) void {
var offset: u64 = 0;
loop(offset < len) {
while(offset < len) {
dest[offset] = value;
offset += 1;
}
Expand All @@ -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;
}
Expand Down
6 changes: 3 additions & 3 deletions std/os.n
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
4 changes: 2 additions & 2 deletions std/os/syscalls.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion std/print.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
2 changes: 1 addition & 1 deletion std/string.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion tests/loop_with_condition/main.n
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down

0 comments on commit 25ecdc4

Please sign in to comment.