From 204f61d7f5aa7b5e27bdaaab9237fca17096ad3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Jul 2020 15:34:31 -0700 Subject: stage2: Module: use StringHashMapUnmanaged --- src-self-hosted/Module.zig | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 975158c477..089e570eda 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -36,7 +36,7 @@ bin_file_path: []const u8, decl_exports: std.AutoHashMapUnmanaged(*Decl, []*Export) = .{}, /// We track which export is associated with the given symbol name for quick /// detection of symbol collisions. -symbol_exports: std.StringHashMap(*Export), +symbol_exports: std.StringHashMapUnmanaged(*Export) = .{}, /// This models the Decls that perform exports, so that `decl_exports` can be updated when a Decl /// is modified. Note that the key of this table is not the Decl being exported, but the Decl that /// is performing the export of another Decl. @@ -769,7 +769,6 @@ pub fn init(gpa: *Allocator, options: InitOptions) !Module { .bin_file_path = options.bin_file_path, .bin_file = bin_file, .optimize_mode = options.optimize_mode, - .symbol_exports = std.StringHashMap(*Export).init(gpa), .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), .keep_source_files_loaded = options.keep_source_files_loaded, }; @@ -812,7 +811,7 @@ pub fn deinit(self: *Module) void { } self.export_owners.deinit(gpa); - self.symbol_exports.deinit(); + self.symbol_exports.deinit(gpa); self.root_scope.destroy(gpa); self.* = undefined; } @@ -2305,7 +2304,7 @@ fn analyzeExport(self: *Module, scope: *Scope, src: usize, symbol_name: []const return; } - try self.symbol_exports.putNoClobber(symbol_name, new_export); + try self.symbol_exports.putNoClobber(self.gpa, symbol_name, new_export); self.bin_file.updateDeclExports(self, exported_decl, de_gop.entry.value) catch |err| switch (err) { error.OutOfMemory => return error.OutOfMemory, else => { -- cgit v1.2.3 From 14cef9dd3d8074b0dc2ee48a0905300ce6317aed Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Jul 2020 20:13:51 -0700 Subject: stage2 parser: split out PrefixOp into separate AST Nodes This is part of a larger effort to improve the memory layout of AST nodes of the self-hosted parser to reduce wasted memory. Reduction of wasted memory also translates to improved performance because of fewer memory allocations, and fewer cache misses. Compared to master, when running `zig fmt` on the std lib: * cache-misses: 801,829 => 768,624 * instructions: 3,234,877,167 => 3,232,075,022 * peak memory: 81480 KB => 75964 KB --- lib/std/zig/ast.zig | 240 +++++++++++++++++++-------- lib/std/zig/parse.zig | 348 +++++++++++++++++++++++++++------------ lib/std/zig/render.zig | 356 ++++++++++++++++++++++++---------------- src-self-hosted/Module.zig | 8 + src-self-hosted/translate_c.zig | 167 +++++++++---------- 5 files changed, 711 insertions(+), 408 deletions(-) (limited to 'src-self-hosted/Module.zig') diff --git a/lib/std/zig/ast.zig b/lib/std/zig/ast.zig index 0502f5bb64..1de00dd0b0 100644 --- a/lib/std/zig/ast.zig +++ b/lib/std/zig/ast.zig @@ -410,7 +410,20 @@ pub const Node = struct { // Operators InfixOp, - PrefixOp, + AddressOf, + Await, + BitNot, + BoolNot, + OptionalType, + Negation, + NegationWrap, + Resume, + Try, + ArrayType, + /// ArrayType but has a sentinel node. + ArrayTypeSentinel, + PtrType, + SliceType, /// Not all suffix operations are under this tag. To save memory, some /// suffix operations have dedicated Node tags. SuffixOp, @@ -1797,85 +1810,116 @@ pub const Node = struct { } }; - pub const PrefixOp = struct { - base: Node = Node{ .id = .PrefixOp }, + pub const AddressOf = SimplePrefixOp(.AddressOf); + pub const Await = SimplePrefixOp(.Await); + pub const BitNot = SimplePrefixOp(.BitNot); + pub const BoolNot = SimplePrefixOp(.BoolNot); + pub const OptionalType = SimplePrefixOp(.OptionalType); + pub const Negation = SimplePrefixOp(.Negation); + pub const NegationWrap = SimplePrefixOp(.NegationWrap); + pub const Resume = SimplePrefixOp(.Resume); + pub const Try = SimplePrefixOp(.Try); + + pub fn SimplePrefixOp(comptime tag: Id) type { + return struct { + base: Node = Node{ .id = tag }, + op_token: TokenIndex, + rhs: *Node, + + const Self = @This(); + + pub fn iterate(self: *const Self, index: usize) ?*Node { + if (index == 0) return self.rhs; + return null; + } + + pub fn firstToken(self: *const Self) TokenIndex { + return self.op_token; + } + + pub fn lastToken(self: *const Self) TokenIndex { + return self.rhs.lastToken(); + } + }; + } + + pub const ArrayType = struct { + base: Node = Node{ .id = .ArrayType }, op_token: TokenIndex, - op: Op, rhs: *Node, + len_expr: *Node, - pub const Op = union(enum) { - AddressOf, - ArrayType: ArrayInfo, - Await, - BitNot, - BoolNot, - OptionalType, - Negation, - NegationWrap, - Resume, - PtrType: PtrInfo, - SliceType: PtrInfo, - Try, - }; + pub fn iterate(self: *const ArrayType, index: usize) ?*Node { + var i = index; - pub const ArrayInfo = struct { - len_expr: *Node, - sentinel: ?*Node, - }; + if (i < 1) return self.len_expr; + i -= 1; - pub const PtrInfo = struct { - allowzero_token: ?TokenIndex = null, - align_info: ?Align = null, - const_token: ?TokenIndex = null, - volatile_token: ?TokenIndex = null, - sentinel: ?*Node = null, - - pub const Align = struct { - node: *Node, - bit_range: ?BitRange, - - pub const BitRange = struct { - start: *Node, - end: *Node, - }; - }; - }; + if (i < 1) return self.rhs; + i -= 1; + + return null; + } - pub fn iterate(self: *const PrefixOp, index: usize) ?*Node { + pub fn firstToken(self: *const ArrayType) TokenIndex { + return self.op_token; + } + + pub fn lastToken(self: *const ArrayType) TokenIndex { + return self.rhs.lastToken(); + } + }; + + pub const ArrayTypeSentinel = struct { + base: Node = Node{ .id = .ArrayTypeSentinel }, + op_token: TokenIndex, + rhs: *Node, + len_expr: *Node, + sentinel: *Node, + + pub fn iterate(self: *const ArrayTypeSentinel, index: usize) ?*Node { var i = index; - switch (self.op) { - .PtrType, .SliceType => |addr_of_info| { - if (addr_of_info.sentinel) |sentinel| { - if (i < 1) return sentinel; - i -= 1; - } + if (i < 1) return self.len_expr; + i -= 1; - if (addr_of_info.align_info) |align_info| { - if (i < 1) return align_info.node; - i -= 1; - } - }, + if (i < 1) return self.sentinel; + i -= 1; - .ArrayType => |array_info| { - if (i < 1) return array_info.len_expr; - i -= 1; - if (array_info.sentinel) |sentinel| { - if (i < 1) return sentinel; - i -= 1; - } - }, + if (i < 1) return self.rhs; + i -= 1; - .AddressOf, - .Await, - .BitNot, - .BoolNot, - .OptionalType, - .Negation, - .NegationWrap, - .Try, - .Resume, - => {}, + return null; + } + + pub fn firstToken(self: *const ArrayTypeSentinel) TokenIndex { + return self.op_token; + } + + pub fn lastToken(self: *const ArrayTypeSentinel) TokenIndex { + return self.rhs.lastToken(); + } + }; + + pub const PtrType = struct { + base: Node = Node{ .id = .PtrType }, + op_token: TokenIndex, + rhs: *Node, + /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents + /// one of these possibly-null things. Then we have them directly follow the PtrType in memory. + ptr_info: PtrInfo = .{}, + + pub fn iterate(self: *const PtrType, index: usize) ?*Node { + var i = index; + + if (self.ptr_info.sentinel) |sentinel| { + if (i < 1) return sentinel; + i -= 1; + } + + if (self.ptr_info.align_info) |align_info| { + if (i < 1) return align_info.node; + i -= 1; } if (i < 1) return self.rhs; @@ -1884,11 +1928,47 @@ pub const Node = struct { return null; } - pub fn firstToken(self: *const PrefixOp) TokenIndex { + pub fn firstToken(self: *const PtrType) TokenIndex { return self.op_token; } - pub fn lastToken(self: *const PrefixOp) TokenIndex { + pub fn lastToken(self: *const PtrType) TokenIndex { + return self.rhs.lastToken(); + } + }; + + pub const SliceType = struct { + base: Node = Node{ .id = .SliceType }, + op_token: TokenIndex, + rhs: *Node, + /// TODO Add a u8 flags field to Node where it would otherwise be padding, and each bit represents + /// one of these possibly-null things. Then we have them directly follow the SliceType in memory. + ptr_info: PtrInfo = .{}, + + pub fn iterate(self: *const SliceType, index: usize) ?*Node { + var i = index; + + if (self.ptr_info.sentinel) |sentinel| { + if (i < 1) return sentinel; + i -= 1; + } + + if (self.ptr_info.align_info) |align_info| { + if (i < 1) return align_info.node; + i -= 1; + } + + if (i < 1) return self.rhs; + i -= 1; + + return null; + } + + pub fn firstToken(self: *const SliceType) TokenIndex { + return self.op_token; + } + + pub fn lastToken(self: *const SliceType) TokenIndex { return self.rhs.lastToken(); } }; @@ -2797,6 +2877,24 @@ pub const Node = struct { }; }; +pub const PtrInfo = struct { + allowzero_token: ?TokenIndex = null, + align_info: ?Align = null, + const_token: ?TokenIndex = null, + volatile_token: ?TokenIndex = null, + sentinel: ?*Node = null, + + pub const Align = struct { + node: *Node, + bit_range: ?BitRange = null, + + pub const BitRange = struct { + start: *Node, + end: *Node, + }; + }; +}; + test "iterate" { var root = Node.Root{ .base = Node{ .id = Node.Id.Root }, diff --git a/lib/std/zig/parse.zig b/lib/std/zig/parse.zig index f5f0c10826..d46833c23f 100644 --- a/lib/std/zig/parse.zig +++ b/lib/std/zig/parse.zig @@ -1120,10 +1120,9 @@ const Parser = struct { const expr_node = try p.expectNode(parseExpr, .{ .ExpectedExpr = .{ .token = p.tok_i }, }); - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.Resume); node.* = .{ .op_token = token, - .op = .Resume, .rhs = expr_node, }; return &node.base; @@ -2413,24 +2412,25 @@ const Parser = struct { /// / KEYWORD_await fn parsePrefixOp(p: *Parser) !?*Node { const token = p.nextToken(); - const op: Node.PrefixOp.Op = switch (p.token_ids[token]) { - .Bang => .BoolNot, - .Minus => .Negation, - .Tilde => .BitNot, - .MinusPercent => .NegationWrap, - .Ampersand => .AddressOf, - .Keyword_try => .Try, - .Keyword_await => .Await, + switch (p.token_ids[token]) { + .Bang => return p.allocSimplePrefixOp(.BoolNot, token), + .Minus => return p.allocSimplePrefixOp(.Negation, token), + .Tilde => return p.allocSimplePrefixOp(.BitNot, token), + .MinusPercent => return p.allocSimplePrefixOp(.NegationWrap, token), + .Ampersand => return p.allocSimplePrefixOp(.AddressOf, token), + .Keyword_try => return p.allocSimplePrefixOp(.Try, token), + .Keyword_await => return p.allocSimplePrefixOp(.Await, token), else => { p.putBackToken(token); return null; }, - }; + } + } - const node = try p.arena.allocator.create(Node.PrefixOp); + fn allocSimplePrefixOp(p: *Parser, comptime tag: Node.Id, token: TokenIndex) !?*Node { + const node = try p.arena.allocator.create(Node.SimplePrefixOp(tag)); node.* = .{ .op_token = token, - .op = op, .rhs = undefined, // set by caller }; return &node.base; @@ -2450,19 +2450,14 @@ const Parser = struct { /// / PtrTypeStart (KEYWORD_align LPAREN Expr (COLON INTEGER COLON INTEGER)? RPAREN / KEYWORD_const / KEYWORD_volatile / KEYWORD_allowzero)* fn parsePrefixTypeOp(p: *Parser) !?*Node { if (p.eatToken(.QuestionMark)) |token| { - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.OptionalType); node.* = .{ .op_token = token, - .op = .OptionalType, .rhs = undefined, // set by caller }; return &node.base; } - // TODO: Returning a AnyFrameType instead of PrefixOp makes casting and setting .rhs or - // .return_type more difficult for the caller (see parsePrefixOpExpr helper). - // Consider making the AnyFrameType a member of PrefixOp and add a - // PrefixOp.AnyFrameType variant? if (p.eatToken(.Keyword_anyframe)) |token| { const arrow = p.eatToken(.Arrow) orelse { p.putBackToken(token); @@ -2482,11 +2477,15 @@ const Parser = struct { if (try p.parsePtrTypeStart()) |node| { // If the token encountered was **, there will be two nodes instead of one. // The attributes should be applied to the rightmost operator. - const prefix_op = node.cast(Node.PrefixOp).?; - var ptr_info = if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) - &prefix_op.rhs.cast(Node.PrefixOp).?.op.PtrType + var ptr_info = if (node.cast(Node.PtrType)) |ptr_type| + if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) + &ptr_type.rhs.cast(Node.PtrType).?.ptr_info + else + &ptr_type.ptr_info + else if (node.cast(Node.SliceType)) |slice_type| + &slice_type.ptr_info else - &prefix_op.op.PtrType; + unreachable; while (true) { if (p.eatToken(.Keyword_align)) |align_token| { @@ -2505,7 +2504,7 @@ const Parser = struct { .ExpectedIntegerLiteral = .{ .token = p.tok_i }, }); - break :bit_range_value Node.PrefixOp.PtrInfo.Align.BitRange{ + break :bit_range_value ast.PtrInfo.Align.BitRange{ .start = range_start, .end = range_end, }; @@ -2519,7 +2518,7 @@ const Parser = struct { continue; } - ptr_info.align_info = Node.PrefixOp.PtrInfo.Align{ + ptr_info.align_info = ast.PtrInfo.Align{ .node = expr_node, .bit_range = bit_range, }; @@ -2563,58 +2562,54 @@ const Parser = struct { } if (try p.parseArrayTypeStart()) |node| { - switch (node.cast(Node.PrefixOp).?.op) { - .ArrayType => {}, - .SliceType => |*slice_type| { - // Collect pointer qualifiers in any order, but disallow duplicates - while (true) { - if (try p.parseByteAlign()) |align_expr| { - if (slice_type.align_info != null) { - try p.errors.append(p.gpa, .{ - .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, - }); - continue; - } - slice_type.align_info = Node.PrefixOp.PtrInfo.Align{ - .node = align_expr, - .bit_range = null, - }; + if (node.cast(Node.SliceType)) |slice_type| { + // Collect pointer qualifiers in any order, but disallow duplicates + while (true) { + if (try p.parseByteAlign()) |align_expr| { + if (slice_type.ptr_info.align_info != null) { + try p.errors.append(p.gpa, .{ + .ExtraAlignQualifier = .{ .token = p.tok_i - 1 }, + }); continue; } - if (p.eatToken(.Keyword_const)) |const_token| { - if (slice_type.const_token != null) { - try p.errors.append(p.gpa, .{ - .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, - }); - continue; - } - slice_type.const_token = const_token; + slice_type.ptr_info.align_info = ast.PtrInfo.Align{ + .node = align_expr, + .bit_range = null, + }; + continue; + } + if (p.eatToken(.Keyword_const)) |const_token| { + if (slice_type.ptr_info.const_token != null) { + try p.errors.append(p.gpa, .{ + .ExtraConstQualifier = .{ .token = p.tok_i - 1 }, + }); continue; } - if (p.eatToken(.Keyword_volatile)) |volatile_token| { - if (slice_type.volatile_token != null) { - try p.errors.append(p.gpa, .{ - .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, - }); - continue; - } - slice_type.volatile_token = volatile_token; + slice_type.ptr_info.const_token = const_token; + continue; + } + if (p.eatToken(.Keyword_volatile)) |volatile_token| { + if (slice_type.ptr_info.volatile_token != null) { + try p.errors.append(p.gpa, .{ + .ExtraVolatileQualifier = .{ .token = p.tok_i - 1 }, + }); continue; } - if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { - if (slice_type.allowzero_token != null) { - try p.errors.append(p.gpa, .{ - .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, - }); - continue; - } - slice_type.allowzero_token = allowzero_token; + slice_type.ptr_info.volatile_token = volatile_token; + continue; + } + if (p.eatToken(.Keyword_allowzero)) |allowzero_token| { + if (slice_type.ptr_info.allowzero_token != null) { + try p.errors.append(p.gpa, .{ + .ExtraAllowZeroQualifier = .{ .token = p.tok_i - 1 }, + }); continue; } - break; + slice_type.ptr_info.allowzero_token = allowzero_token; + continue; } - }, - else => unreachable, + break; + } } return node; } @@ -2728,29 +2723,32 @@ const Parser = struct { null; const rbracket = try p.expectToken(.RBracket); - const op: Node.PrefixOp.Op = if (expr) |len_expr| - .{ - .ArrayType = .{ + if (expr) |len_expr| { + if (sentinel) |s| { + const node = try p.arena.allocator.create(Node.ArrayTypeSentinel); + node.* = .{ + .op_token = lbracket, + .rhs = undefined, // set by caller .len_expr = len_expr, - .sentinel = sentinel, - }, + .sentinel = s, + }; + return &node.base; + } else { + const node = try p.arena.allocator.create(Node.ArrayType); + node.* = .{ + .op_token = lbracket, + .rhs = undefined, // set by caller + .len_expr = len_expr, + }; + return &node.base; } - else - .{ - .SliceType = Node.PrefixOp.PtrInfo{ - .allowzero_token = null, - .align_info = null, - .const_token = null, - .volatile_token = null, - .sentinel = sentinel, - }, - }; + } - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.SliceType); node.* = .{ .op_token = lbracket, - .op = op, .rhs = undefined, // set by caller + .ptr_info = .{ .sentinel = sentinel }, }; return &node.base; } @@ -2768,28 +2766,26 @@ const Parser = struct { }) else null; - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.PtrType); node.* = .{ .op_token = asterisk, - .op = .{ .PtrType = .{ .sentinel = sentinel } }, .rhs = undefined, // set by caller + .ptr_info = .{ .sentinel = sentinel }, }; return &node.base; } if (p.eatToken(.AsteriskAsterisk)) |double_asterisk| { - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.PtrType); node.* = .{ .op_token = double_asterisk, - .op = .{ .PtrType = .{} }, .rhs = undefined, // set by caller }; // Special case for **, which is its own token - const child = try p.arena.allocator.create(Node.PrefixOp); + const child = try p.arena.allocator.create(Node.PtrType); child.* = .{ .op_token = double_asterisk, - .op = .{ .PtrType = .{} }, .rhs = undefined, // set by caller }; node.rhs = &child.base; @@ -2808,10 +2804,9 @@ const Parser = struct { p.putBackToken(ident); } else { _ = try p.expectToken(.RBracket); - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.PtrType); node.* = .{ .op_token = lbracket, - .op = .{ .PtrType = .{} }, .rhs = undefined, // set by caller }; return &node.base; @@ -2824,11 +2819,11 @@ const Parser = struct { else null; _ = try p.expectToken(.RBracket); - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.PtrType); node.* = .{ .op_token = lbracket, - .op = .{ .PtrType = .{ .sentinel = sentinel } }, .rhs = undefined, // set by caller + .ptr_info = .{ .sentinel = sentinel }, }; return &node.base; } @@ -3146,10 +3141,9 @@ const Parser = struct { fn parseTry(p: *Parser) !?*Node { const token = p.eatToken(.Keyword_try) orelse return null; - const node = try p.arena.allocator.create(Node.PrefixOp); + const node = try p.arena.allocator.create(Node.Try); node.* = .{ .op_token = token, - .op = .Try, .rhs = undefined, // set by caller }; return &node.base; @@ -3228,15 +3222,87 @@ const Parser = struct { var rightmost_op = first_op; while (true) { switch (rightmost_op.id) { - .PrefixOp => { - var prefix_op = rightmost_op.cast(Node.PrefixOp).?; + .AddressOf => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.AddressOf).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .Await => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.Await).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .BitNot => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.BitNot).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .BoolNot => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.BoolNot).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .OptionalType => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.OptionalType).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .Negation => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.Negation).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .NegationWrap => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.NegationWrap).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .Resume => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.Resume).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .Try => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.Try).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .ArrayType => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.ArrayType).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .ArrayTypeSentinel => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.ArrayTypeSentinel).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .SliceType => { + if (try opParseFn(p)) |rhs| { + rightmost_op.cast(Node.SliceType).?.rhs = rhs; + rightmost_op = rhs; + } else break; + }, + .PtrType => { + var ptr_type = rightmost_op.cast(Node.PtrType).?; // If the token encountered was **, there will be two nodes - if (p.token_ids[prefix_op.op_token] == .AsteriskAsterisk) { - rightmost_op = prefix_op.rhs; - prefix_op = rightmost_op.cast(Node.PrefixOp).?; + if (p.token_ids[ptr_type.op_token] == .AsteriskAsterisk) { + rightmost_op = ptr_type.rhs; + ptr_type = rightmost_op.cast(Node.PtrType).?; } if (try opParseFn(p)) |rhs| { - prefix_op.rhs = rhs; + ptr_type.rhs = rhs; rightmost_op = rhs; } else break; }, @@ -3253,8 +3319,80 @@ const Parser = struct { // If any prefix op existed, a child node on the RHS is required switch (rightmost_op.id) { - .PrefixOp => { - const prefix_op = rightmost_op.cast(Node.PrefixOp).?; + .AddressOf => { + const prefix_op = rightmost_op.cast(Node.AddressOf).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .Await => { + const prefix_op = rightmost_op.cast(Node.Await).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .BitNot => { + const prefix_op = rightmost_op.cast(Node.BitNot).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .BoolNot => { + const prefix_op = rightmost_op.cast(Node.BoolNot).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .OptionalType => { + const prefix_op = rightmost_op.cast(Node.OptionalType).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .Negation => { + const prefix_op = rightmost_op.cast(Node.Negation).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .NegationWrap => { + const prefix_op = rightmost_op.cast(Node.NegationWrap).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .Resume => { + const prefix_op = rightmost_op.cast(Node.Resume).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .Try => { + const prefix_op = rightmost_op.cast(Node.Try).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .ArrayType => { + const prefix_op = rightmost_op.cast(Node.ArrayType).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .ArrayTypeSentinel => { + const prefix_op = rightmost_op.cast(Node.ArrayTypeSentinel).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .PtrType => { + const prefix_op = rightmost_op.cast(Node.PtrType).?; + prefix_op.rhs = try p.expectNode(childParseFn, .{ + .InvalidToken = .{ .token = p.tok_i }, + }); + }, + .SliceType => { + const prefix_op = rightmost_op.cast(Node.SliceType).?; prefix_op.rhs = try p.expectNode(childParseFn, .{ .InvalidToken = .{ .token = p.tok_i }, }); diff --git a/lib/std/zig/render.zig b/lib/std/zig/render.zig index f24886c0ab..b75e6f6ca3 100644 --- a/lib/std/zig/render.zig +++ b/lib/std/zig/render.zig @@ -468,166 +468,192 @@ fn renderExpression( return renderExpression(allocator, stream, tree, indent, start_col, infix_op_node.rhs, space); }, - .PrefixOp => { - const prefix_op_node = @fieldParentPtr(ast.Node.PrefixOp, "base", base); - - switch (prefix_op_node.op) { - .PtrType => |ptr_info| { - const op_tok_id = tree.token_ids[prefix_op_node.op_token]; - switch (op_tok_id) { - .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'), - .LBracket => if (tree.token_ids[prefix_op_node.op_token + 2] == .Identifier) - try stream.writeAll("[*c") - else - try stream.writeAll("[*"), - else => unreachable, - } - if (ptr_info.sentinel) |sentinel| { - const colon_token = tree.prevToken(sentinel.firstToken()); - try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : - const sentinel_space = switch (op_tok_id) { - .LBracket => Space.None, - else => Space.Space, - }; - try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space); - } - switch (op_tok_id) { - .Asterisk, .AsteriskAsterisk => {}, - .LBracket => try stream.writeByte(']'), - else => unreachable, - } - if (ptr_info.allowzero_token) |allowzero_token| { - try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero - } - if (ptr_info.align_info) |align_info| { - const lparen_token = tree.prevToken(align_info.node.firstToken()); - const align_token = tree.prevToken(lparen_token); - - try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align - try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( - - try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); - - if (align_info.bit_range) |bit_range| { - const colon1 = tree.prevToken(bit_range.start.firstToken()); - const colon2 = tree.prevToken(bit_range.end.firstToken()); + .BitNot => { + const bit_not = @fieldParentPtr(ast.Node.BitNot, "base", base); + try renderToken(tree, stream, bit_not.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, bit_not.rhs, space); + }, + .BoolNot => { + const bool_not = @fieldParentPtr(ast.Node.BoolNot, "base", base); + try renderToken(tree, stream, bool_not.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, bool_not.rhs, space); + }, + .Negation => { + const negation = @fieldParentPtr(ast.Node.Negation, "base", base); + try renderToken(tree, stream, negation.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, negation.rhs, space); + }, + .NegationWrap => { + const negation_wrap = @fieldParentPtr(ast.Node.NegationWrap, "base", base); + try renderToken(tree, stream, negation_wrap.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, negation_wrap.rhs, space); + }, + .OptionalType => { + const opt_type = @fieldParentPtr(ast.Node.OptionalType, "base", base); + try renderToken(tree, stream, opt_type.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, opt_type.rhs, space); + }, + .AddressOf => { + const addr_of = @fieldParentPtr(ast.Node.AddressOf, "base", base); + try renderToken(tree, stream, addr_of.op_token, indent, start_col, Space.None); + return renderExpression(allocator, stream, tree, indent, start_col, addr_of.rhs, space); + }, + .Try => { + const try_node = @fieldParentPtr(ast.Node.Try, "base", base); + try renderToken(tree, stream, try_node.op_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, try_node.rhs, space); + }, + .Resume => { + const resume_node = @fieldParentPtr(ast.Node.Resume, "base", base); + try renderToken(tree, stream, resume_node.op_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, resume_node.rhs, space); + }, + .Await => { + const await_node = @fieldParentPtr(ast.Node.Await, "base", base); + try renderToken(tree, stream, await_node.op_token, indent, start_col, Space.Space); + return renderExpression(allocator, stream, tree, indent, start_col, await_node.rhs, space); + }, - try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); - try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); + .ArrayType => { + const array_type = @fieldParentPtr(ast.Node.ArrayType, "base", base); + return renderArrayType( + allocator, + stream, + tree, + indent, + start_col, + array_type.op_token, + array_type.rhs, + array_type.len_expr, + null, + space, + ); + }, + .ArrayTypeSentinel => { + const array_type = @fieldParentPtr(ast.Node.ArrayTypeSentinel, "base", base); + return renderArrayType( + allocator, + stream, + tree, + indent, + start_col, + array_type.op_token, + array_type.rhs, + array_type.len_expr, + array_type.sentinel, + space, + ); + }, - const rparen_token = tree.nextToken(bit_range.end.lastToken()); - try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) - } else { - const rparen_token = tree.nextToken(align_info.node.lastToken()); - try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) - } - } - if (ptr_info.const_token) |const_token| { - try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const - } - if (ptr_info.volatile_token) |volatile_token| { - try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile - } - }, + .PtrType => { + const ptr_type = @fieldParentPtr(ast.Node.PtrType, "base", base); + const op_tok_id = tree.token_ids[ptr_type.op_token]; + switch (op_tok_id) { + .Asterisk, .AsteriskAsterisk => try stream.writeByte('*'), + .LBracket => if (tree.token_ids[ptr_type.op_token + 2] == .Identifier) + try stream.writeAll("[*c") + else + try stream.writeAll("[*"), + else => unreachable, + } + if (ptr_type.ptr_info.sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + const sentinel_space = switch (op_tok_id) { + .LBracket => Space.None, + else => Space.Space, + }; + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, sentinel_space); + } + switch (op_tok_id) { + .Asterisk, .AsteriskAsterisk => {}, + .LBracket => try stream.writeByte(']'), + else => unreachable, + } + if (ptr_type.ptr_info.allowzero_token) |allowzero_token| { + try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero + } + if (ptr_type.ptr_info.align_info) |align_info| { + const lparen_token = tree.prevToken(align_info.node.firstToken()); + const align_token = tree.prevToken(lparen_token); - .SliceType => |ptr_info| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); // [ - if (ptr_info.sentinel) |sentinel| { - const colon_token = tree.prevToken(sentinel.firstToken()); - try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); - try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ] - } else { - try renderToken(tree, stream, tree.nextToken(prefix_op_node.op_token), indent, start_col, Space.None); // ] - } + try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( - if (ptr_info.allowzero_token) |allowzero_token| { - try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero - } - if (ptr_info.align_info) |align_info| { - const lparen_token = tree.prevToken(align_info.node.firstToken()); - const align_token = tree.prevToken(lparen_token); + try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); - try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align - try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( + if (align_info.bit_range) |bit_range| { + const colon1 = tree.prevToken(bit_range.start.firstToken()); + const colon2 = tree.prevToken(bit_range.end.firstToken()); - try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); + try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); + try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); - if (align_info.bit_range) |bit_range| { - const colon1 = tree.prevToken(bit_range.start.firstToken()); - const colon2 = tree.prevToken(bit_range.end.firstToken()); + const rparen_token = tree.nextToken(bit_range.end.lastToken()); + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) + } else { + const rparen_token = tree.nextToken(align_info.node.lastToken()); + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) + } + } + if (ptr_type.ptr_info.const_token) |const_token| { + try renderToken(tree, stream, const_token, indent, start_col, Space.Space); // const + } + if (ptr_type.ptr_info.volatile_token) |volatile_token| { + try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); // volatile + } + return renderExpression(allocator, stream, tree, indent, start_col, ptr_type.rhs, space); + }, - try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); - try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); + .SliceType => { + const slice_type = @fieldParentPtr(ast.Node.SliceType, "base", base); + try renderToken(tree, stream, slice_type.op_token, indent, start_col, Space.None); // [ + if (slice_type.ptr_info.sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); + try renderToken(tree, stream, tree.nextToken(sentinel.lastToken()), indent, start_col, Space.None); // ] + } else { + try renderToken(tree, stream, tree.nextToken(slice_type.op_token), indent, start_col, Space.None); // ] + } - const rparen_token = tree.nextToken(bit_range.end.lastToken()); - try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) - } else { - const rparen_token = tree.nextToken(align_info.node.lastToken()); - try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) - } - } - if (ptr_info.const_token) |const_token| { - try renderToken(tree, stream, const_token, indent, start_col, Space.Space); - } - if (ptr_info.volatile_token) |volatile_token| { - try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); - } - }, + if (slice_type.ptr_info.allowzero_token) |allowzero_token| { + try renderToken(tree, stream, allowzero_token, indent, start_col, Space.Space); // allowzero + } + if (slice_type.ptr_info.align_info) |align_info| { + const lparen_token = tree.prevToken(align_info.node.firstToken()); + const align_token = tree.prevToken(lparen_token); - .ArrayType => |array_info| { - const lbracket = prefix_op_node.op_token; - const rbracket = tree.nextToken(if (array_info.sentinel) |sentinel| - sentinel.lastToken() - else - array_info.len_expr.lastToken()); + try renderToken(tree, stream, align_token, indent, start_col, Space.None); // align + try renderToken(tree, stream, lparen_token, indent, start_col, Space.None); // ( - try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ + try renderExpression(allocator, stream, tree, indent, start_col, align_info.node, Space.None); - const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment; - const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment; - const new_indent = if (ends_with_comment) indent + indent_delta else indent; - const new_space = if (ends_with_comment) Space.Newline else Space.None; - try renderExpression(allocator, stream, tree, new_indent, start_col, array_info.len_expr, new_space); - if (starts_with_comment) { - try stream.writeByte('\n'); - } - if (ends_with_comment or starts_with_comment) { - try stream.writeByteNTimes(' ', indent); - } - if (array_info.sentinel) |sentinel| { - const colon_token = tree.prevToken(sentinel.firstToken()); - try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : - try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); - } - try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] - }, - .BitNot, - .BoolNot, - .Negation, - .NegationWrap, - .OptionalType, - .AddressOf, - => { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.None); - }, + if (align_info.bit_range) |bit_range| { + const colon1 = tree.prevToken(bit_range.start.firstToken()); + const colon2 = tree.prevToken(bit_range.end.firstToken()); - .Try, - .Resume, - => { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); - }, + try renderToken(tree, stream, colon1, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.start, Space.None); + try renderToken(tree, stream, colon2, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, bit_range.end, Space.None); - .Await => |await_info| { - try renderToken(tree, stream, prefix_op_node.op_token, indent, start_col, Space.Space); - }, + const rparen_token = tree.nextToken(bit_range.end.lastToken()); + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) + } else { + const rparen_token = tree.nextToken(align_info.node.lastToken()); + try renderToken(tree, stream, rparen_token, indent, start_col, Space.Space); // ) + } } - - return renderExpression(allocator, stream, tree, indent, start_col, prefix_op_node.rhs, space); + if (slice_type.ptr_info.const_token) |const_token| { + try renderToken(tree, stream, const_token, indent, start_col, Space.Space); + } + if (slice_type.ptr_info.volatile_token) |volatile_token| { + try renderToken(tree, stream, volatile_token, indent, start_col, Space.Space); + } + return renderExpression(allocator, stream, tree, indent, start_col, slice_type.rhs, space); }, .ArrayInitializer, .ArrayInitializerDot => { @@ -2057,6 +2083,46 @@ fn renderExpression( } } +fn renderArrayType( + allocator: *mem.Allocator, + stream: anytype, + tree: *ast.Tree, + indent: usize, + start_col: *usize, + lbracket: ast.TokenIndex, + rhs: *ast.Node, + len_expr: *ast.Node, + opt_sentinel: ?*ast.Node, + space: Space, +) (@TypeOf(stream).Error || Error)!void { + const rbracket = tree.nextToken(if (opt_sentinel) |sentinel| + sentinel.lastToken() + else + len_expr.lastToken()); + + try renderToken(tree, stream, lbracket, indent, start_col, Space.None); // [ + + const starts_with_comment = tree.token_ids[lbracket + 1] == .LineComment; + const ends_with_comment = tree.token_ids[rbracket - 1] == .LineComment; + const new_indent = if (ends_with_comment) indent + indent_delta else indent; + const new_space = if (ends_with_comment) Space.Newline else Space.None; + try renderExpression(allocator, stream, tree, new_indent, start_col, len_expr, new_space); + if (starts_with_comment) { + try stream.writeByte('\n'); + } + if (ends_with_comment or starts_with_comment) { + try stream.writeByteNTimes(' ', indent); + } + if (opt_sentinel) |sentinel| { + const colon_token = tree.prevToken(sentinel.firstToken()); + try renderToken(tree, stream, colon_token, indent, start_col, Space.None); // : + try renderExpression(allocator, stream, tree, indent, start_col, sentinel, Space.None); + } + try renderToken(tree, stream, rbracket, indent, start_col, Space.None); // ] + + return renderExpression(allocator, stream, tree, indent, start_col, rhs, space); +} + fn renderAsmOutput( allocator: *mem.Allocator, stream: anytype, diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 089e570eda..993313d947 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -1308,10 +1308,18 @@ fn astGenExpr(self: *Module, scope: *Scope, ast_node: *ast.Node) InnerError!*zir .ControlFlowExpression => return self.astGenControlFlowExpression(scope, @fieldParentPtr(ast.Node.ControlFlowExpression, "base", ast_node)), .If => return self.astGenIf(scope, @fieldParentPtr(ast.Node.If, "base", ast_node)), .InfixOp => return self.astGenInfixOp(scope, @fieldParentPtr(ast.Node.InfixOp, "base", ast_node)), + .BoolNot => return self.astGenBoolNot(scope, @fieldParentPtr(ast.Node.BoolNot, "base", ast_node)), else => return self.failNode(scope, ast_node, "TODO implement astGenExpr for {}", .{@tagName(ast_node.id)}), } } +fn astGenBoolNot(self: *Module, scope: *Scope, node: *ast.Node.BoolNot) InnerError!*zir.Inst { + const operand = try self.astGenExpr(scope, node.rhs); + const tree = scope.tree(); + const src = tree.token_locs[node.op_token].start; + return self.addZIRInst(scope, src, zir.Inst.BoolNot, .{ .operand = operand }, .{}); +} + fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) InnerError!*zir.Inst { switch (infix_node.op) { .Assign => { diff --git a/src-self-hosted/translate_c.zig b/src-self-hosted/translate_c.zig index 4ae2d74336..c45befd372 100644 --- a/src-self-hosted/translate_c.zig +++ b/src-self-hosted/translate_c.zig @@ -1561,7 +1561,7 @@ fn transImplicitCastExpr( return maybeSuppressResult(rp, scope, result_used, sub_expr_node); } - const prefix_op = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); prefix_op.rhs = try transExpr(rp, scope, sub_expr, .used, .r_value); return maybeSuppressResult(rp, scope, result_used, &prefix_op.base); @@ -1673,11 +1673,7 @@ fn isBoolRes(res: *ast.Node) bool { else => {}, }, - .PrefixOp => switch (@fieldParentPtr(ast.Node.PrefixOp, "base", res).op) { - .BoolNot => return true, - - else => {}, - }, + .BoolNot => return true, .BoolLiteral => return true, .GroupedExpression => return isBoolRes(@fieldParentPtr(ast.Node.GroupedExpression, "base", res).expr), else => {}, @@ -2162,21 +2158,16 @@ fn transCreateNodeArrayType( source_loc: ZigClangSourceLocation, ty: *const ZigClangType, len: anytype, -) TransError!*ast.Node { - var node = try transCreateNodePrefixOp( - rp.c, - .{ - .ArrayType = .{ - .len_expr = undefined, - .sentinel = null, - }, - }, - .LBracket, - "[", - ); - node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, len); +) !*ast.Node { + const node = try rp.c.arena.create(ast.Node.ArrayType); + const op_token = try appendToken(rp.c, .LBracket, "["); + const len_expr = try transCreateNodeInt(rp.c, len); _ = try appendToken(rp.c, .RBracket, "]"); - node.rhs = try transType(rp, ty, source_loc); + node.* = .{ + .op_token = op_token, + .rhs = try transType(rp, ty, source_loc), + .len_expr = len_expr, + }; return &node.base; } @@ -2449,7 +2440,7 @@ fn transDoWhileLoop( }, }; defer cond_scope.deinit(); - const prefix_op = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + const prefix_op = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); prefix_op.rhs = try transBoolExpr(rp, &cond_scope.base, @ptrCast(*const ZigClangExpr, ZigClangDoStmt_getCond(stmt)), .used, .r_value, true); _ = try appendToken(rp.c, .RParen, ")"); if_node.condition = &prefix_op.base; @@ -3036,7 +3027,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar else return transCreatePreCrement(rp, scope, stmt, .AssignSub, .MinusEqual, "-=", used), .AddrOf => { - const op_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + const op_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); op_node.rhs = try transExpr(rp, scope, op_expr, used, .r_value); return &op_node.base; }, @@ -3052,7 +3043,7 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar .Plus => return transExpr(rp, scope, op_expr, used, .r_value), .Minus => { if (!qualTypeHasWrappingOverflow(ZigClangExpr_getType(op_expr))) { - const op_node = try transCreateNodePrefixOp(rp.c, .Negation, .Minus, "-"); + const op_node = try transCreateNodeSimplePrefixOp(rp.c, .Negation, .Minus, "-"); op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); return &op_node.base; } else if (cIsUnsignedInteger(ZigClangExpr_getType(op_expr))) { @@ -3065,12 +3056,12 @@ fn transUnaryOperator(rp: RestorePoint, scope: *Scope, stmt: *const ZigClangUnar return revertAndWarn(rp, error.UnsupportedTranslation, ZigClangUnaryOperator_getBeginLoc(stmt), "C negation with non float non integer", .{}); }, .Not => { - const op_node = try transCreateNodePrefixOp(rp.c, .BitNot, .Tilde, "~"); + const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BitNot, .Tilde, "~"); op_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); return &op_node.base; }, .LNot => { - const op_node = try transCreateNodePrefixOp(rp.c, .BoolNot, .Bang, "!"); + const op_node = try transCreateNodeSimplePrefixOp(rp.c, .BoolNot, .Bang, "!"); op_node.rhs = try transBoolExpr(rp, scope, op_expr, .used, .r_value, true); return &op_node.base; }, @@ -3116,7 +3107,7 @@ fn transCreatePreCrement( const node = try transCreateNodeVarDecl(rp.c, false, true, ref); node.eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); node.init_node = &rhs_node.base; node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); @@ -3182,7 +3173,7 @@ fn transCreatePostCrement( const node = try transCreateNodeVarDecl(rp.c, false, true, ref); node.eq_token = try appendToken(rp.c, .Equal, "="); - const rhs_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + const rhs_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); rhs_node.rhs = try transExpr(rp, scope, op_expr, .used, .r_value); node.init_node = &rhs_node.base; node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); @@ -3336,7 +3327,7 @@ fn transCreateCompoundAssign( const node = try transCreateNodeVarDecl(rp.c, false, true, ref); node.eq_token = try appendToken(rp.c, .Equal, "="); - const addr_node = try transCreateNodePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); + const addr_node = try transCreateNodeSimplePrefixOp(rp.c, .AddressOf, .Ampersand, "&"); addr_node.rhs = try transExpr(rp, scope, lhs, .used, .l_value); node.init_node = &addr_node.base; node.semicolon_token = try appendToken(rp.c, .Semicolon, ";"); @@ -3984,16 +3975,15 @@ fn transCreateNodeFieldAccess(c: *Context, container: *ast.Node, field_name: []c return &field_access_node.base; } -fn transCreateNodePrefixOp( +fn transCreateNodeSimplePrefixOp( c: *Context, - op: ast.Node.PrefixOp.Op, + comptime tag: ast.Node.Id, op_tok_id: std.zig.Token.Id, bytes: []const u8, -) !*ast.Node.PrefixOp { - const node = try c.arena.create(ast.Node.PrefixOp); +) !*ast.Node.SimplePrefixOp(tag) { + const node = try c.arena.create(ast.Node.SimplePrefixOp(tag)); node.* = .{ .op_token = try appendToken(c, op_tok_id, bytes), - .op = op, .rhs = undefined, // translate and set afterward }; return node; @@ -4065,8 +4055,8 @@ fn transCreateNodePtrType( is_const: bool, is_volatile: bool, op_tok_id: std.zig.Token.Id, -) !*ast.Node.PrefixOp { - const node = try c.arena.create(ast.Node.PrefixOp); +) !*ast.Node.PtrType { + const node = try c.arena.create(ast.Node.PtrType); const op_token = switch (op_tok_id) { .LBracket => blk: { const lbracket = try appendToken(c, .LBracket, "["); @@ -4086,11 +4076,9 @@ fn transCreateNodePtrType( }; node.* = .{ .op_token = op_token, - .op = .{ - .PtrType = .{ - .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, - .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, - }, + .ptr_info = .{ + .const_token = if (is_const) try appendToken(c, .Keyword_const, "const") else null, + .volatile_token = if (is_volatile) try appendToken(c, .Keyword_volatile, "volatile") else null, }, .rhs = undefined, // translate and set afterward }; @@ -4569,12 +4557,12 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour .Pointer => { const child_qt = ZigClangType_getPointeeType(ty); if (qualTypeChildIsFnProto(child_qt)) { - const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); + const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); optional_node.rhs = try transQualType(rp, child_qt, source_loc); return &optional_node.base; } if (typeIsOpaque(rp.c, ZigClangQualType_getTypePtr(child_qt), source_loc)) { - const optional_node = try transCreateNodePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); + const optional_node = try transCreateNodeSimplePrefixOp(rp.c, .OptionalType, .QuestionMark, "?"); const pointer_node = try transCreateNodePtrType( rp.c, ZigClangQualType_isConstQualified(child_qt), @@ -4599,21 +4587,8 @@ fn transType(rp: RestorePoint, ty: *const ZigClangType, source_loc: ZigClangSour const size_ap_int = ZigClangConstantArrayType_getSize(const_arr_ty); const size = ZigClangAPInt_getLimitedValue(size_ap_int, math.maxInt(usize)); - var node = try transCreateNodePrefixOp( - rp.c, - .{ - .ArrayType = .{ - .len_expr = undefined, - .sentinel = null, - }, - }, - .LBracket, - "[", - ); - node.op.ArrayType.len_expr = try transCreateNodeInt(rp.c, size); - _ = try appendToken(rp.c, .RBracket, "]"); - node.rhs = try transQualType(rp, ZigClangConstantArrayType_getElementType(const_arr_ty), source_loc); - return &node.base; + const elem_ty = ZigClangQualType_getTypePtr(ZigClangConstantArrayType_getElementType(const_arr_ty)); + return try transCreateNodeArrayType(rp, source_loc, elem_ty, size); }, .IncompleteArray => { const incomplete_array_ty = @ptrCast(*const ZigClangIncompleteArrayType, ty); @@ -5824,7 +5799,7 @@ fn parseCSuffixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, if (prev_id == .Keyword_void) { const ptr = try transCreateNodePtrType(c, false, false, .Asterisk); ptr.rhs = node; - const optional_node = try transCreateNodePrefixOp(c, .OptionalType, .QuestionMark, "?"); + const optional_node = try transCreateNodeSimplePrefixOp(c, .OptionalType, .QuestionMark, "?"); optional_node.rhs = &ptr.base; return &optional_node.base; } else { @@ -5993,18 +5968,18 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, switch (op_tok.id) { .Bang => { - const node = try transCreateNodePrefixOp(c, .BoolNot, .Bang, "!"); + const node = try transCreateNodeSimplePrefixOp(c, .BoolNot, .Bang, "!"); node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); return &node.base; }, .Minus => { - const node = try transCreateNodePrefixOp(c, .Negation, .Minus, "-"); + const node = try transCreateNodeSimplePrefixOp(c, .Negation, .Minus, "-"); node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); return &node.base; }, .Plus => return try parseCPrefixOpExpr(c, it, source, source_loc, scope), .Tilde => { - const node = try transCreateNodePrefixOp(c, .BitNot, .Tilde, "~"); + const node = try transCreateNodeSimplePrefixOp(c, .BitNot, .Tilde, "~"); node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); return &node.base; }, @@ -6013,7 +5988,7 @@ fn parseCPrefixOpExpr(c: *Context, it: *CTokenList.Iterator, source: []const u8, return try transCreateNodePtrDeref(c, node); }, .Ampersand => { - const node = try transCreateNodePrefixOp(c, .AddressOf, .Ampersand, "&"); + const node = try transCreateNodeSimplePrefixOp(c, .AddressOf, .Ampersand, "&"); node.rhs = try parseCPrefixOpExpr(c, it, source, source_loc, scope); return &node.base; }, @@ -6034,29 +6009,49 @@ fn tokenSlice(c: *Context, token: ast.TokenIndex) []u8 { } fn getContainer(c: *Context, node: *ast.Node) ?*ast.Node { - if (node.id == .ContainerDecl) { - return node; - } else if (node.id == .PrefixOp) { - return node; - } else if (node.cast(ast.Node.Identifier)) |ident| { - if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { - if (value.cast(ast.Node.VarDecl)) |var_decl| - return getContainer(c, var_decl.init_node.?); - } - } else if (node.cast(ast.Node.InfixOp)) |infix| { - if (infix.op != .Period) - return null; - if (getContainerTypeOf(c, infix.lhs)) |ty_node| { - if (ty_node.cast(ast.Node.ContainerDecl)) |container| { - for (container.fieldsAndDecls()) |field_ref| { - const field = field_ref.cast(ast.Node.ContainerField).?; - const ident = infix.rhs.cast(ast.Node.Identifier).?; - if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { - return getContainer(c, field.type_expr.?); + switch (node.id) { + .ContainerDecl, + .AddressOf, + .Await, + .BitNot, + .BoolNot, + .OptionalType, + .Negation, + .NegationWrap, + .Resume, + .Try, + .ArrayType, + .ArrayTypeSentinel, + .PtrType, + .SliceType, + => return node, + + .Identifier => { + const ident = node.cast(ast.Node.Identifier).?; + if (c.global_scope.sym_table.get(tokenSlice(c, ident.token))) |value| { + if (value.cast(ast.Node.VarDecl)) |var_decl| + return getContainer(c, var_decl.init_node.?); + } + }, + + .InfixOp => { + const infix = node.cast(ast.Node.InfixOp).?; + if (infix.op != .Period) + return null; + if (getContainerTypeOf(c, infix.lhs)) |ty_node| { + if (ty_node.cast(ast.Node.ContainerDecl)) |container| { + for (container.fieldsAndDecls()) |field_ref| { + const field = field_ref.cast(ast.Node.ContainerField).?; + const ident = infix.rhs.cast(ast.Node.Identifier).?; + if (mem.eql(u8, tokenSlice(c, field.name_token), tokenSlice(c, ident.token))) { + return getContainer(c, field.type_expr.?); + } } } } - } + }, + + else => {}, } return null; } @@ -6091,11 +6086,9 @@ fn getContainerTypeOf(c: *Context, ref: *ast.Node) ?*ast.Node { fn getFnProto(c: *Context, ref: *ast.Node) ?*ast.Node.FnProto { const init = if (ref.cast(ast.Node.VarDecl)) |v| v.init_node.? else return null; if (getContainerTypeOf(c, init)) |ty_node| { - if (ty_node.cast(ast.Node.PrefixOp)) |prefix| { - if (prefix.op == .OptionalType) { - if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { - return fn_proto; - } + if (ty_node.cast(ast.Node.OptionalType)) |prefix| { + if (prefix.rhs.cast(ast.Node.FnProto)) |fn_proto| { + return fn_proto; } } } -- cgit v1.2.3 From 4f5e065d6ec10cb27589995bf5d6390647f783f2 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 13 Jul 2020 20:47:47 -0700 Subject: stage2: add ZIR support for BoolNot --- src-self-hosted/Module.zig | 12 ++++++++++++ src-self-hosted/codegen.zig | 7 +++++++ src-self-hosted/ir.zig | 10 ++++++++++ src-self-hosted/zir.zig | 30 ++++++++++++++++++++++++++++++ 4 files changed, 59 insertions(+) (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 993313d947..1ed655a055 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -2566,6 +2566,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In .condbr => return self.analyzeInstCondBr(scope, old_inst.cast(zir.Inst.CondBr).?), .isnull => return self.analyzeInstIsNull(scope, old_inst.cast(zir.Inst.IsNull).?), .isnonnull => return self.analyzeInstIsNonNull(scope, old_inst.cast(zir.Inst.IsNonNull).?), + .boolnot => return self.analyzeInstBoolNot(scope, old_inst.cast(zir.Inst.BoolNot).?), } } @@ -3243,6 +3244,17 @@ fn analyzeInstCmp(self: *Module, scope: *Scope, inst: *zir.Inst.Cmp) InnerError! return self.fail(scope, inst.base.src, "TODO implement more cmp analysis", .{}); } +fn analyzeInstBoolNot(self: *Module, scope: *Scope, inst: *zir.Inst.BoolNot) InnerError!*Inst { + const uncasted_operand = try self.resolveInst(scope, inst.positionals.operand); + const bool_type = Type.initTag(.bool); + const operand = try self.coerce(scope, bool_type, uncasted_operand); + if (try self.resolveDefinedValue(scope, operand)) |val| { + return self.constBool(scope, inst.base.src, !val.toBool()); + } + const b = try self.requireRuntimeBlock(scope, inst.base.src); + return self.addNewInstArgs(b, inst.base.src, bool_type, Inst.Not, .{ .operand = operand }); +} + fn analyzeInstIsNull(self: *Module, scope: *Scope, inst: *zir.Inst.IsNull) InnerError!*Inst { const operand = try self.resolveInst(scope, inst.positionals.operand); return self.analyzeIsNull(scope, inst.base.src, operand, true); diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 143ad787b7..731c039d9c 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -407,6 +407,13 @@ const Function = struct { .retvoid => return self.genRetVoid(inst.cast(ir.Inst.RetVoid).?, arch), .sub => return self.genSub(inst.cast(ir.Inst.Sub).?, arch), .unreach => return MCValue{ .unreach = {} }, + .not => return self.genNot(inst.cast(ir.Inst.Not).?, arch), + } + } + + fn genNot(self: *Function, inst: *ir.Inst.Not, comptime arch: std.Target.Cpu.Arch) !MCValue { + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}), } } diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index 74da3430a7..6c0c807468 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -60,6 +60,7 @@ pub const Inst = struct { retvoid, sub, unreach, + not, }; pub fn cast(base: *Inst, comptime T: type) ?*T { @@ -194,6 +195,15 @@ pub const Inst = struct { false_death_count: u32 = 0, }; + pub const Not = struct { + pub const base_tag = Tag.not; + + base: Inst, + args: struct { + operand: *Inst, + }, + }; + pub const Constant = struct { pub const base_tag = Tag.constant; base: Inst, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 0dfbdd20a0..45ced54255 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -56,6 +56,7 @@ pub const Inst = struct { declval, /// Same as declval but the parameter is a `*Module.Decl` rather than a name. declval_in_module, + boolnot, /// String Literal. Makes an anonymous Decl and then takes a pointer to it. str, int, @@ -115,6 +116,7 @@ pub const Inst = struct { .cmp, .isnull, .isnonnull, + .boolnot, => false, .condbr, @@ -143,6 +145,7 @@ pub const Inst = struct { .declval_in_module => DeclValInModule, .compileerror => CompileError, .@"const" => Const, + .boolnot => BoolNot, .str => Str, .int => Int, .inttype => IntType, @@ -299,6 +302,16 @@ pub const Inst = struct { kw_args: struct {}, }; + pub const BoolNot = struct { + pub const base_tag = Tag.boolnot; + base: Inst, + + positionals: struct { + operand: *Inst, + }, + kw_args: struct {}, + }; + pub const Str = struct { pub const base_tag = Tag.str; base: Inst, @@ -762,6 +775,7 @@ const Writer = struct { .declval_in_module => return self.writeInstToStreamGeneric(stream, .declval_in_module, inst), .compileerror => return self.writeInstToStreamGeneric(stream, .compileerror, inst), .@"const" => return self.writeInstToStreamGeneric(stream, .@"const", inst), + .boolnot => return self.writeInstToStreamGeneric(stream, .boolnot, inst), .str => return self.writeInstToStreamGeneric(stream, .str, inst), .int => return self.writeInstToStreamGeneric(stream, .int, inst), .inttype => return self.writeInstToStreamGeneric(stream, .inttype, inst), @@ -1658,6 +1672,22 @@ const EmitZIR = struct { }; for (body.instructions) |inst| { const new_inst = switch (inst.tag) { + .not => blk: { + const old_inst = inst.cast(ir.Inst.Not).?; + assert(inst.ty.zigTypeTag() == .Bool); + const new_inst = try self.arena.allocator.create(Inst.BoolNot); + new_inst.* = .{ + .base = .{ + .src = inst.src, + .tag = Inst.BoolNot.base_tag, + }, + .positionals = .{ + .operand = try self.resolveInst(new_body, old_inst.args.operand), + }, + .kw_args = .{}, + }; + break :blk &new_inst.base; + }, .add => blk: { const old_inst = inst.cast(ir.Inst.Add).?; const new_inst = try self.arena.allocator.create(Inst.Add); -- cgit v1.2.3 From a92990f99312c946b5e527517a27a67a5a5513c0 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Tue, 14 Jul 2020 02:24:12 -0700 Subject: stage2: implement enough for assert() function to codegen --- src-self-hosted/Module.zig | 44 ++++++++++++++----------- src-self-hosted/codegen.zig | 74 +++++++++++++++++++++++++++++++++++++++--- src-self-hosted/type.zig | 19 +++++++++-- src-self-hosted/value.zig | 34 ++++++++++++------- test/stage2/compare_output.zig | 57 ++++++++++++++++++++++++++++++++ 5 files changed, 192 insertions(+), 36 deletions(-) (limited to 'src-self-hosted/Module.zig') diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 1ed655a055..de9e1f2386 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -1358,17 +1358,19 @@ fn astGenInfixOp(self: *Module, scope: *Scope, infix_node: *ast.Node.InfixOp) In const tree = scope.tree(); const src = tree.token_locs[infix_node.op_token].start; + const op: std.math.CompareOperator = switch (infix_node.op) { + .BangEqual => .neq, + .EqualEqual => .eq, + .GreaterThan => .gt, + .GreaterOrEqual => .gte, + .LessThan => .lt, + .LessOrEqual => .lte, + else => unreachable, + }; + return self.addZIRInst(scope, src, zir.Inst.Cmp, .{ .lhs = lhs, - .op = @as(std.math.CompareOperator, switch (infix_node.op) { - .BangEqual => .neq, - .EqualEqual => .eq, - .GreaterThan => .gt, - .GreaterOrEqual => .gte, - .LessThan => .lt, - .LessOrEqual => .lte, - else => unreachable, - }), + .op = op, .rhs = rhs, }, .{}); }, @@ -1415,11 +1417,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir defer then_scope.instructions.deinit(self.gpa); const then_result = try self.astGenExpr(&then_scope.base, if_node.body); - const then_src = tree.token_locs[if_node.body.lastToken()].start; - _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ - .block = block, - .operand = then_result, - }, .{}); + if (!then_result.tag.isNoReturn()) { + const then_src = tree.token_locs[if_node.body.lastToken()].start; + _ = try self.addZIRInst(&then_scope.base, then_src, zir.Inst.Break, .{ + .block = block, + .operand = then_result, + }, .{}); + } condbr.positionals.true_body = .{ .instructions = try then_scope.arena.dupe(*zir.Inst, then_scope.instructions.items), }; @@ -1433,11 +1437,13 @@ fn astGenIf(self: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir if (if_node.@"else") |else_node| { const else_result = try self.astGenExpr(&else_scope.base, else_node.body); - const else_src = tree.token_locs[else_node.body.lastToken()].start; - _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ - .block = block, - .operand = else_result, - }, .{}); + if (!else_result.tag.isNoReturn()) { + const else_src = tree.token_locs[else_node.body.lastToken()].start; + _ = try self.addZIRInst(&else_scope.base, else_src, zir.Inst.Break, .{ + .block = block, + .operand = else_result, + }, .{}); + } } else { // TODO Optimization opportunity: we can avoid an allocation and a memcpy here // by directly allocating the body for this one instruction. diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 5753559ce1..314d497808 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -415,7 +415,46 @@ const Function = struct { // No side effects, so if it's unreferenced, do nothing. if (inst.base.isUnused()) return MCValue.dead; + const operand = try self.resolveInst(inst.args.operand); + switch (operand) { + .dead => unreachable, + .unreach => unreachable, + .compare_flags_unsigned => |op| return MCValue{ + .compare_flags_unsigned = switch (op) { + .gte => .lt, + .gt => .lte, + .neq => .eq, + .lt => .gte, + .lte => .gt, + .eq => .neq, + }, + }, + .compare_flags_signed => |op| return MCValue{ + .compare_flags_signed = switch (op) { + .gte => .lt, + .gt => .lte, + .neq => .eq, + .lt => .gte, + .lte => .gt, + .eq => .neq, + }, + }, + else => {}, + } + switch (arch) { + .x86_64 => { + var imm = ir.Inst.Constant{ + .base = .{ + .tag = .constant, + .deaths = 0, + .ty = inst.args.operand.ty, + .src = inst.args.operand.src, + }, + .val = Value.initTag(.bool_true), + }; + return try self.genX8664BinMath(&inst.base, inst.args.operand, &imm.base, 6, 0x30); + }, else => return self.fail(inst.base.src, "TODO implement NOT for {}", .{self.target.cpu.arch}), } } @@ -444,7 +483,7 @@ const Function = struct { } } - /// ADD, SUB + /// ADD, SUB, XOR, OR, AND fn genX8664BinMath(self: *Function, inst: *ir.Inst, op_lhs: *ir.Inst, op_rhs: *ir.Inst, opx: u8, mr: u8) !MCValue { try self.code.ensureCapacity(self.code.items.len + 8); @@ -705,7 +744,7 @@ const Function = struct { fn genCondBr(self: *Function, inst: *ir.Inst.CondBr, comptime arch: std.Target.Cpu.Arch) !MCValue { switch (arch) { - .i386, .x86_64 => { + .x86_64 => { try self.code.ensureCapacity(self.code.items.len + 6); const cond = try self.resolveInst(inst.args.condition); @@ -734,7 +773,20 @@ const Function = struct { }; return self.genX86CondBr(inst, opcode, arch); }, - else => return self.fail(inst.base.src, "TODO implement condbr {} when condition not already in the compare flags", .{self.target.cpu.arch}), + .register => |reg_usize| { + const reg = @intToEnum(Reg(arch), @intCast(u8, reg_usize)); + // test reg, 1 + // TODO detect al, ax, eax + try self.code.ensureCapacity(self.code.items.len + 4); + self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 }); + self.code.appendSliceAssumeCapacity(&[_]u8{ + 0xf6, + @as(u8, 0xC0) | (0 << 3) | @truncate(u3, reg.id()), + 0x01, + }); + return self.genX86CondBr(inst, 0x84, arch); + }, + else => return self.fail(inst.base.src, "TODO implement condbr {} when condition is {}", .{ self.target.cpu.arch, @tagName(cond) }), } }, else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.target.cpu.arch}), @@ -892,7 +944,18 @@ const Function = struct { .none => unreachable, .unreach => unreachable, .compare_flags_unsigned => |op| { - return self.fail(src, "TODO set register with compare flags value (unsigned)", .{}); + try self.code.ensureCapacity(self.code.items.len + 3); + self.rex(.{ .b = reg.isExtended(), .w = reg.size() == 64 }); + const opcode: u8 = switch (op) { + .gte => 0x93, + .gt => 0x97, + .neq => 0x95, + .lt => 0x92, + .lte => 0x96, + .eq => 0x94, + }; + const id = @as(u8, reg.id() & 0b111); + self.code.appendSliceAssumeCapacity(&[_]u8{ 0x0f, opcode, 0xC0 | id }); }, .compare_flags_signed => |op| { return self.fail(src, "TODO set register with compare flags value (signed)", .{}); @@ -1147,6 +1210,9 @@ const Function = struct { } return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; }, + .Bool => { + return MCValue{ .immediate = @boolToInt(typed_value.val.toBool()) }; + }, .ComptimeInt => unreachable, // semantic analysis prevents this .ComptimeFloat => unreachable, // semantic analysis prevents this else => return self.fail(src, "TODO implement const of type '{}'", .{typed_value.ty}), diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index 512dd631a7..1cc1b690f3 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -163,6 +163,22 @@ pub const Type = extern union { return sentinel_b == null; } }, + .Fn => { + if (!a.fnReturnType().eql(b.fnReturnType())) + return false; + if (a.fnCallingConvention() != b.fnCallingConvention()) + return false; + const a_param_len = a.fnParamLen(); + const b_param_len = b.fnParamLen(); + if (a_param_len != b_param_len) + return false; + var i: usize = 0; + while (i < a_param_len) : (i += 1) { + if (!a.fnParamType(i).eql(b.fnParamType(i))) + return false; + } + return true; + }, .Float, .Struct, .Optional, @@ -170,14 +186,13 @@ pub const Type = extern union { .ErrorSet, .Enum, .Union, - .Fn, .BoundFn, .Opaque, .Frame, .AnyFrame, .Vector, .EnumLiteral, - => @panic("TODO implement more Type equality comparison"), + => std.debug.panic("TODO implement Type equality comparison of {} and {}", .{ a, b }), } } diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index c1e9a38bd1..a3e1daa383 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -427,8 +427,6 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, - .bool_true, - .bool_false, .null_value, .function, .ref_val, @@ -441,8 +439,11 @@ pub const Value = extern union { .the_one_possible_value, // An integer with one possible value is always zero. .zero, + .bool_false, => return BigIntMutable.init(&space.limbs, 0).toConst(), + .bool_true => return BigIntMutable.init(&space.limbs, 1).toConst(), + .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(), @@ -493,8 +494,6 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, - .bool_true, - .bool_false, .null_value, .function, .ref_val, @@ -507,8 +506,11 @@ pub const Value = extern union { .zero, .the_one_possible_value, // an integer with one possible value is always zero + .bool_false, => return 0, + .bool_true => return 1, + .int_u64 => return self.cast(Payload.Int_u64).?.int, .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable, @@ -560,8 +562,6 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, - .bool_true, - .bool_false, .null_value, .function, .ref_val, @@ -574,8 +574,11 @@ pub const Value = extern union { .the_one_possible_value, // an integer with one possible value is always zero .zero, + .bool_false, => return 0, + .bool_true => return 1, + .int_u64 => { const x = self.cast(Payload.Int_u64).?.int; if (x == 0) return 0; @@ -632,8 +635,6 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, - .bool_true, - .bool_false, .null_value, .function, .ref_val, @@ -646,8 +647,18 @@ pub const Value = extern union { .zero, .undef, .the_one_possible_value, // an integer with one possible value is always zero + .bool_false, => return true, + .bool_true => { + const info = ty.intInfo(target); + if (info.signed) { + return info.bits >= 2; + } else { + return info.bits >= 1; + } + }, + .int_u64 => switch (ty.zigTypeTag()) { .Int => { const x = self.cast(Payload.Int_u64).?.int; @@ -796,8 +807,6 @@ pub const Value = extern union { .fn_ccc_void_no_args_type, .single_const_pointer_to_comptime_int_type, .const_slice_u8_type, - .bool_true, - .bool_false, .null_value, .function, .ref_val, @@ -810,8 +819,11 @@ pub const Value = extern union { .zero, .the_one_possible_value, // an integer with one possible value is always zero + .bool_false, => return .eq, + .bool_true => return .gt, + .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), @@ -855,7 +867,7 @@ pub const Value = extern union { pub fn toBool(self: Value) bool { return switch (self.tag()) { .bool_true => true, - .bool_false => false, + .bool_false, .zero => false, else => unreachable, }; } diff --git a/test/stage2/compare_output.zig b/test/stage2/compare_output.zig index 27c9f48d72..c4f85bfba4 100644 --- a/test/stage2/compare_output.zig +++ b/test/stage2/compare_output.zig @@ -170,4 +170,61 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + { + var case = ctx.exe("assert function", linux_x64); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ add(3, 4); + \\ + \\ exit(); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 7); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + case.addCompareOutput( + \\export fn _start() noreturn { + \\ add(100, 200); + \\ + \\ exit(); + \\} + \\ + \\fn add(a: u32, b: u32) void { + \\ assert(a + b == 300); + \\} + \\ + \\pub fn assert(ok: bool) void { + \\ if (!ok) unreachable; // assertion failure + \\} + \\ + \\fn exit() noreturn { + \\ asm volatile ("syscall" + \\ : + \\ : [number] "{rax}" (231), + \\ [arg1] "{rdi}" (0) + \\ : "rcx", "r11", "memory" + \\ ); + \\ unreachable; + \\} + , + "", + ); + } } -- cgit v1.2.3