From cf654b52d68f20a403965e70371a9ad193370d8c Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 3 Jun 2020 21:53:13 -0400 Subject: [PATCH] stage2: -femit-zir respects decl names and supports cycles --- src-self-hosted/Module.zig | 24 ++- src-self-hosted/type.zig | 22 +++ src-self-hosted/value.zig | 12 ++ src-self-hosted/zir.zig | 390 ++++++++++++++++++++++++------------- test/stage2/zir.zig | 35 ++-- 5 files changed, 330 insertions(+), 153 deletions(-) diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 5bdd38c69..4ddd286f1 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -1097,6 +1097,9 @@ fn resolveDecl(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*De const decl = kv.value; try self.reAnalyzeDecl(decl, old_inst); return decl; + } else if (old_inst.cast(zir.Inst.DeclVal)) |decl_val| { + // This is just a named reference to another decl. + return self.analyzeDeclVal(scope, decl_val); } else { const new_decl = blk: { try self.decl_table.ensureCapacity(self.decl_table.size + 1); @@ -1443,6 +1446,7 @@ fn analyzeInst(self: *Module, scope: *Scope, old_inst: *zir.Inst) InnerError!*In .breakpoint => return self.analyzeInstBreakpoint(scope, old_inst.cast(zir.Inst.Breakpoint).?), .call => return self.analyzeInstCall(scope, old_inst.cast(zir.Inst.Call).?), .declref => return self.analyzeInstDeclRef(scope, old_inst.cast(zir.Inst.DeclRef).?), + .declval => return self.analyzeInstDeclVal(scope, old_inst.cast(zir.Inst.DeclVal).?), .str => { const bytes = old_inst.cast(zir.Inst.Str).?.positionals.bytes; // The bytes references memory inside the ZIR module, which can get deallocated @@ -1501,6 +1505,24 @@ fn analyzeInstDeclRef(self: *Module, scope: *Scope, inst: *zir.Inst.DeclRef) Inn return self.analyzeDeclRef(scope, inst.base.src, decl); } +fn analyzeDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Decl { + const decl_name = inst.positionals.name; + // This will need to get more fleshed out when there are proper structs & namespaces. + const zir_module = scope.namespace(); + const src_decl = zir_module.contents.module.findDecl(decl_name) orelse + return self.fail(scope, inst.base.src, "use of undeclared identifier '{}'", .{decl_name}); + + const decl = try self.resolveCompleteDecl(scope, src_decl); + + return decl; +} + +fn analyzeInstDeclVal(self: *Module, scope: *Scope, inst: *zir.Inst.DeclVal) InnerError!*Inst { + const decl = try self.analyzeDeclVal(scope, inst); + const ptr = try self.analyzeDeclRef(scope, inst.base.src, decl); + return self.analyzeDeref(scope, inst.base.src, ptr, inst.base.src); +} + fn analyzeDeclRef(self: *Module, scope: *Scope, src: usize, decl: *Decl) InnerError!*Inst { const decl_tv = try decl.typedValue(); const ty_payload = try scope.arena().create(Type.Payload.SingleConstPointer); @@ -1621,7 +1643,7 @@ fn analyzeInstFnType(self: *Module, scope: *Scope, fntype: *zir.Inst.FnType) Inn } fn analyzeInstPrimitive(self: *Module, scope: *Scope, primitive: *zir.Inst.Primitive) InnerError!*Inst { - return self.constType(scope, primitive.base.src, primitive.positionals.tag.toType()); + return self.constInst(scope, primitive.base.src, primitive.positionals.tag.toTypedValue()); } fn analyzeInstAs(self: *Module, scope: *Scope, as: *zir.Inst.As) InnerError!*Inst { diff --git a/src-self-hosted/type.zig b/src-self-hosted/type.zig index bdce3ba2d..aa8c00009 100644 --- a/src-self-hosted/type.zig +++ b/src-self-hosted/type.zig @@ -51,6 +51,7 @@ pub const Type = extern union { .comptime_float => return .ComptimeFloat, .noreturn => return .NoReturn, .@"null" => return .Null, + .@"undefined" => return .Undefined, .fn_noreturn_no_args => return .Fn, .fn_naked_noreturn_no_args => return .Fn, @@ -201,6 +202,7 @@ pub const Type = extern union { => return out_stream.writeAll(@tagName(t)), .@"null" => return out_stream.writeAll("@TypeOf(null)"), + .@"undefined" => return out_stream.writeAll("@TypeOf(undefined)"), .const_slice_u8 => return out_stream.writeAll("[]const u8"), .fn_noreturn_no_args => return out_stream.writeAll("fn() noreturn"), @@ -265,6 +267,7 @@ pub const Type = extern union { .comptime_float => return Value.initTag(.comptime_float_type), .noreturn => return Value.initTag(.noreturn_type), .@"null" => return Value.initTag(.null_type), + .@"undefined" => return Value.initTag(.undefined_type), .fn_noreturn_no_args => return Value.initTag(.fn_noreturn_no_args_type), .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), @@ -318,6 +321,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", => false, }; } @@ -378,6 +382,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", => unreachable, }; } @@ -410,6 +415,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .array_u8_sentinel_0, .const_slice_u8, @@ -454,6 +460,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .array_u8_sentinel_0, .single_const_pointer, @@ -498,6 +505,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .array_u8_sentinel_0, .fn_noreturn_no_args, @@ -543,6 +551,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -586,6 +595,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -630,6 +640,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -662,6 +673,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -707,6 +719,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -781,6 +794,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -826,6 +840,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -870,6 +885,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -914,6 +930,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -958,6 +975,7 @@ pub const Type = extern union { .comptime_float, .noreturn, .@"null", + .@"undefined", .array, .single_const_pointer, .single_const_pointer_to_comptime_int, @@ -1013,6 +1031,7 @@ pub const Type = extern union { .anyerror, .noreturn, .@"null", + .@"undefined", .fn_noreturn_no_args, .fn_naked_noreturn_no_args, .fn_ccc_void_no_args, @@ -1062,6 +1081,7 @@ pub const Type = extern union { .void, .noreturn, .@"null", + .@"undefined", => return true, .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, @@ -1115,6 +1135,7 @@ pub const Type = extern union { .void, .noreturn, .@"null", + .@"undefined", .int_unsigned, .int_signed, .array, @@ -1157,6 +1178,7 @@ pub const Type = extern union { comptime_float, noreturn, @"null", + @"undefined", fn_noreturn_no_args, fn_naked_noreturn_no_args, fn_ccc_void_no_args, diff --git a/src-self-hosted/value.zig b/src-self-hosted/value.zig index 2727ad26a..5660cd760 100644 --- a/src-self-hosted/value.zig +++ b/src-self-hosted/value.zig @@ -47,6 +47,7 @@ pub const Value = extern union { comptime_float_type, noreturn_type, null_type, + undefined_type, fn_noreturn_no_args_type, fn_naked_noreturn_no_args_type, fn_ccc_void_no_args_type, @@ -141,6 +142,7 @@ pub const Value = extern union { .comptime_float_type => return out_stream.writeAll("comptime_float"), .noreturn_type => return out_stream.writeAll("noreturn"), .null_type => return out_stream.writeAll("@TypeOf(null)"), + .undefined_type => return out_stream.writeAll("@TypeOf(undefined)"), .fn_noreturn_no_args_type => return out_stream.writeAll("fn() noreturn"), .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), @@ -225,6 +227,7 @@ pub const Value = extern union { .comptime_float_type => Type.initTag(.comptime_float), .noreturn_type => Type.initTag(.noreturn), .null_type => Type.initTag(.@"null"), + .undefined_type => Type.initTag(.@"undefined"), .fn_noreturn_no_args_type => Type.initTag(.fn_noreturn_no_args), .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), @@ -281,6 +284,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -339,6 +343,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -398,6 +403,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -462,6 +468,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -555,6 +562,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -610,6 +618,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -710,6 +719,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -771,6 +781,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, @@ -849,6 +860,7 @@ pub const Value = extern union { .comptime_float_type, .noreturn_type, .null_type, + .undefined_type, .fn_noreturn_no_args_type, .fn_naked_noreturn_no_args_type, .fn_ccc_void_no_args_type, diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index b8a142199..749d9d9c8 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -27,9 +27,11 @@ pub const Inst = struct { pub const Tag = enum { breakpoint, call, - /// Represents a reference to a global decl by name. - /// The syntax `@foo` is equivalent to `declref("foo")`. + /// Represents a pointer to a global decl by name. declref, + /// The syntax `@foo` is equivalent to `declval("foo")`. + /// declval is equivalent to declref followed by deref. + declval, str, int, ptrtoint, @@ -59,6 +61,7 @@ pub const Inst = struct { .breakpoint => Breakpoint, .call => Call, .declref => DeclRef, + .declval => DeclVal, .str => Str, .int => Int, .ptrtoint => PtrToInt, @@ -122,6 +125,16 @@ pub const Inst = struct { kw_args: struct {}, }; + pub const DeclVal = struct { + pub const base_tag = Tag.declval; + base: Inst, + + positionals: struct { + name: []const u8, + }, + kw_args: struct {}, + }; + pub const Str = struct { pub const base_tag = Tag.str; base: Inst, @@ -254,11 +267,11 @@ pub const Inst = struct { base: Inst, positionals: struct { - tag: BuiltinType, + tag: Builtin, }, kw_args: struct {}, - pub const BuiltinType = enum { + pub const Builtin = enum { isize, usize, c_short, @@ -282,32 +295,42 @@ pub const Inst = struct { anyerror, comptime_int, comptime_float, + @"true", + @"false", + @"null", + @"undefined", + void_value, - pub fn toType(self: BuiltinType) Type { + pub fn toTypedValue(self: Builtin) TypedValue { return switch (self) { - .isize => Type.initTag(.isize), - .usize => Type.initTag(.usize), - .c_short => Type.initTag(.c_short), - .c_ushort => Type.initTag(.c_ushort), - .c_int => Type.initTag(.c_int), - .c_uint => Type.initTag(.c_uint), - .c_long => Type.initTag(.c_long), - .c_ulong => Type.initTag(.c_ulong), - .c_longlong => Type.initTag(.c_longlong), - .c_ulonglong => Type.initTag(.c_ulonglong), - .c_longdouble => Type.initTag(.c_longdouble), - .c_void => Type.initTag(.c_void), - .f16 => Type.initTag(.f16), - .f32 => Type.initTag(.f32), - .f64 => Type.initTag(.f64), - .f128 => Type.initTag(.f128), - .bool => Type.initTag(.bool), - .void => Type.initTag(.void), - .noreturn => Type.initTag(.noreturn), - .type => Type.initTag(.type), - .anyerror => Type.initTag(.anyerror), - .comptime_int => Type.initTag(.comptime_int), - .comptime_float => Type.initTag(.comptime_float), + .isize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.isize_type) }, + .usize => .{ .ty = Type.initTag(.type), .val = Value.initTag(.usize_type) }, + .c_short => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_short_type) }, + .c_ushort => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ushort_type) }, + .c_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_int_type) }, + .c_uint => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_uint_type) }, + .c_long => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_long_type) }, + .c_ulong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulong_type) }, + .c_longlong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longlong_type) }, + .c_ulonglong => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_ulonglong_type) }, + .c_longdouble => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_longdouble_type) }, + .c_void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.c_void_type) }, + .f16 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f16_type) }, + .f32 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f32_type) }, + .f64 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f64_type) }, + .f128 => .{ .ty = Type.initTag(.type), .val = Value.initTag(.f128_type) }, + .bool => .{ .ty = Type.initTag(.type), .val = Value.initTag(.bool_type) }, + .void => .{ .ty = Type.initTag(.type), .val = Value.initTag(.void_type) }, + .noreturn => .{ .ty = Type.initTag(.type), .val = Value.initTag(.noreturn_type) }, + .type => .{ .ty = Type.initTag(.type), .val = Value.initTag(.type_type) }, + .anyerror => .{ .ty = Type.initTag(.type), .val = Value.initTag(.anyerror_type) }, + .comptime_int => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_int_type) }, + .comptime_float => .{ .ty = Type.initTag(.type), .val = Value.initTag(.comptime_float_type) }, + .@"true" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_true) }, + .@"false" => .{ .ty = Type.initTag(.bool), .val = Value.initTag(.bool_false) }, + .@"null" => .{ .ty = Type.initTag(.@"null"), .val = Value.initTag(.null_value) }, + .@"undefined" => .{ .ty = Type.initTag(.@"undefined"), .val = Value.initTag(.undef) }, + .void_value => .{ .ty = Type.initTag(.void), .val = Value.initTag(.the_one_possible_value) }, }; } }; @@ -440,7 +463,7 @@ pub const Module = struct { self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; } - const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body }); + const InstPtrTable = std.AutoHashMap(*Inst, struct { inst: *Inst, index: ?usize }); /// TODO Look into making a table to speed this up. pub fn findDecl(self: Module, name: []const u8) ?*Inst { @@ -462,17 +485,17 @@ pub const Module = struct { try inst_table.ensureCapacity(self.decls.len); for (self.decls) |decl, decl_i| { - try inst_table.putNoClobber(decl, .{ .index = decl_i, .fn_body = null }); + try inst_table.putNoClobber(decl, .{ .inst = decl, .index = null }); if (decl.cast(Inst.Fn)) |fn_inst| { for (fn_inst.positionals.body.instructions) |inst, inst_i| { - try inst_table.putNoClobber(inst, .{ .index = inst_i, .fn_body = &fn_inst.positionals.body }); + try inst_table.putNoClobber(inst, .{ .inst = inst, .index = inst_i }); } } } for (self.decls) |decl, i| { - try stream.print("@{} ", .{i}); + try stream.print("@{} ", .{decl.name}); try self.writeInstToStream(stream, decl, &inst_table); try stream.writeByte('\n'); } @@ -489,6 +512,7 @@ pub const Module = struct { .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), .call => return self.writeInstToStreamGeneric(stream, .call, decl, inst_table), .declref => return self.writeInstToStreamGeneric(stream, .declref, decl, inst_table), + .declval => return self.writeInstToStreamGeneric(stream, .declval, decl, inst_table), .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), @@ -587,9 +611,18 @@ pub const Module = struct { } fn writeInstParamToStream(self: Module, stream: var, inst: *Inst, inst_table: *const InstPtrTable) !void { - const info = inst_table.getValue(inst).?; - const prefix = if (info.fn_body == null) "@" else "%"; - try stream.print("{}{}", .{ prefix, info.index }); + if (inst_table.getValue(inst)) |info| { + if (info.index) |i| { + try stream.print("%{}", .{info.index}); + } else { + try stream.print("@{}", .{info.inst.name}); + } + } else if (inst.cast(Inst.DeclVal)) |decl_val| { + try stream.print("@{}", .{decl_val.positionals.name}); + } else { + //try stream.print("?", .{}); + unreachable; + } } }; @@ -964,47 +997,17 @@ const Parser = struct { self.i = src; return self.fail("unrecognized identifier: {}", .{bad_name}); } else { - const name_array = try self.arena.allocator.create(Inst.Str); - name_array.* = .{ + const declval = try self.arena.allocator.create(Inst.DeclVal); + declval.* = .{ .base = .{ .name = try self.generateName(), .src = src, - .tag = Inst.Str.base_tag, + .tag = Inst.DeclVal.base_tag, }, - .positionals = .{ .bytes = ident }, + .positionals = .{ .name = ident }, .kw_args = .{}, }; - const name = try self.arena.allocator.create(Inst.Ref); - name.* = .{ - .base = .{ - .name = try self.generateName(), - .src = src, - .tag = Inst.Ref.base_tag, - }, - .positionals = .{ .operand = &name_array.base }, - .kw_args = .{}, - }; - const declref = try self.arena.allocator.create(Inst.DeclRef); - declref.* = .{ - .base = .{ - .name = try self.generateName(), - .src = src, - .tag = Inst.DeclRef.base_tag, - }, - .positionals = .{ .name = &name.base }, - .kw_args = .{}, - }; - const deref = try self.arena.allocator.create(Inst.Deref); - deref.* = .{ - .base = .{ - .name = try self.generateName(), - .src = src, - .tag = Inst.Deref.base_tag, - }, - .positionals = .{ .ptr = &declref.base }, - .kw_args = .{}, - }; - return &deref.base; + return &declval.base; } }; if (local_ref) { @@ -1025,12 +1028,15 @@ pub fn emit(allocator: *Allocator, old_module: IrModule) !Module { var ctx: EmitZIR = .{ .allocator = allocator, .decls = .{}, - .decl_table = std.AutoHashMap(*ir.Inst, *Inst).init(allocator), .arena = std.heap.ArenaAllocator.init(allocator), .old_module = &old_module, + .next_auto_name = 0, + .names = std.StringHashMap(void).init(allocator), + .primitive_table = std.AutoHashMap(Inst.Primitive.Builtin, *Inst).init(allocator), }; defer ctx.decls.deinit(allocator); - defer ctx.decl_table.deinit(); + defer ctx.names.deinit(); + defer ctx.primitive_table.deinit(); errdefer ctx.arena.deinit(); try ctx.emit(); @@ -1046,47 +1052,90 @@ const EmitZIR = struct { arena: std.heap.ArenaAllocator, old_module: *const IrModule, decls: std.ArrayListUnmanaged(*Inst), - decl_table: std.AutoHashMap(*ir.Inst, *Inst), + names: std.StringHashMap(void), + next_auto_name: usize, + primitive_table: std.AutoHashMap(Inst.Primitive.Builtin, *Inst), fn emit(self: *EmitZIR) !void { - var it = self.old_module.decl_exports.iterator(); - while (it.next()) |kv| { - const decl = kv.key; - const exports = kv.value; - const export_value = try self.emitTypedValue(decl.src, decl.typed_value.most_recent.typed_value); - for (exports) |module_export| { - const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); - const export_inst = try self.arena.allocator.create(Inst.Export); - export_inst.* = .{ - .base = .{ - .name = try self.autoName(), - .src = module_export.src, - .tag = Inst.Export.base_tag, - }, - .positionals = .{ - .symbol_name = symbol_name, - .value = export_value, - }, - .kw_args = .{}, - }; - try self.decls.append(self.allocator, &export_inst.base); + // Put all the Decls in a list and sort them by name to avoid nondeterminism introduced + // by the hash table. + var src_decls = std.ArrayList(*IrModule.Decl).init(self.allocator); + defer src_decls.deinit(); + try src_decls.ensureCapacity(self.old_module.decl_table.size); + try self.decls.ensureCapacity(self.allocator, self.old_module.decl_table.size); + try self.names.ensureCapacity(self.old_module.decl_table.size); + + var decl_it = self.old_module.decl_table.iterator(); + while (decl_it.next()) |kv| { + const decl = kv.value; + src_decls.appendAssumeCapacity(decl); + self.names.putAssumeCapacityNoClobber(mem.spanZ(decl.name), {}); + } + std.sort.sort(*IrModule.Decl, src_decls.items, {}, (struct { + fn lessThan(context: void, a: *IrModule.Decl, b: *IrModule.Decl) bool { + return a.src < b.src; + } + }).lessThan); + + // Emit all the decls. + for (src_decls.items) |ir_decl| { + if (self.old_module.export_owners.getValue(ir_decl)) |exports| { + for (exports) |module_export| { + const declval = try self.emitDeclVal(ir_decl.src, mem.spanZ(module_export.exported_decl.name)); + const symbol_name = try self.emitStringLiteral(module_export.src, module_export.options.name); + const export_inst = try self.arena.allocator.create(Inst.Export); + export_inst.* = .{ + .base = .{ + .name = try self.autoName(), + .src = module_export.src, + .tag = Inst.Export.base_tag, + }, + .positionals = .{ + .symbol_name = symbol_name, + .value = declval, + }, + .kw_args = .{}, + }; + try self.decls.append(self.allocator, &export_inst.base); + } + } else { + const new_decl = try self.emitTypedValue(ir_decl.src, ir_decl.typed_value.most_recent.typed_value); + new_decl.name = try self.arena.allocator.dupe(u8, mem.spanZ(ir_decl.name)); } } } - fn resolveInst(self: *EmitZIR, inst_table: *const std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst { + fn resolveInst(self: *EmitZIR, inst_table: *std.AutoHashMap(*ir.Inst, *Inst), inst: *ir.Inst) !*Inst { if (inst.cast(ir.Inst.Constant)) |const_inst| { - if (self.decl_table.getValue(inst)) |decl| { - return decl; - } - const new_decl = try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); - try self.decl_table.putNoClobber(inst, new_decl); + const new_decl = if (const_inst.val.cast(Value.Payload.Function)) |func_pl| blk: { + const owner_decl = func_pl.func.owner_decl; + break :blk try self.emitDeclVal(inst.src, mem.spanZ(owner_decl.name)); + } else if (const_inst.val.cast(Value.Payload.DeclRef)) |declref| blk: { + break :blk try self.emitDeclRef(inst.src, declref.decl); + } else blk: { + break :blk try self.emitTypedValue(inst.src, .{ .ty = inst.ty, .val = const_inst.val }); + }; + try inst_table.putNoClobber(inst, new_decl); return new_decl; } else { return inst_table.getValue(inst).?; } } + fn emitDeclVal(self: *EmitZIR, src: usize, decl_name: []const u8) !*Inst { + const declval = try self.arena.allocator.create(Inst.DeclVal); + declval.* = .{ + .base = .{ + .name = try self.autoName(), + .src = src, + .tag = Inst.DeclVal.base_tag, + }, + .positionals = .{ .name = try self.arena.allocator.dupe(u8, decl_name) }, + .kw_args = .{}, + }; + return &declval.base; + } + fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); const int_inst = try self.arena.allocator.create(Inst.Int); @@ -1105,8 +1154,31 @@ const EmitZIR = struct { return &int_inst.base; } + fn emitDeclRef(self: *EmitZIR, src: usize, decl: *IrModule.Decl) !*Inst { + const declval = try self.emitDeclVal(src, mem.spanZ(decl.name)); + const ref_inst = try self.arena.allocator.create(Inst.Ref); + ref_inst.* = .{ + .base = .{ + .name = try self.autoName(), + .src = src, + .tag = Inst.Ref.base_tag, + }, + .positionals = .{ + .operand = declval, + }, + .kw_args = .{}, + }; + try self.decls.append(self.allocator, &ref_inst.base); + + return &ref_inst.base; + } + fn emitTypedValue(self: *EmitZIR, src: usize, typed_value: TypedValue) Allocator.Error!*Inst { const allocator = &self.arena.allocator; + if (typed_value.val.cast(Value.Payload.DeclRef)) |decl_ref| { + const decl = decl_ref.decl; + return self.emitDeclRef(src, decl); + } switch (typed_value.ty.zigTypeTag()) { .Pointer => { const ptr_elem_type = typed_value.ty.elemType(); @@ -1142,7 +1214,6 @@ const EmitZIR = struct { }, .kw_args = .{}, }; - try self.decls.append(self.allocator, &as_inst.base); return &as_inst.base; }, @@ -1182,6 +1253,33 @@ const EmitZIR = struct { try self.decls.append(self.allocator, &fn_inst.base); return &fn_inst.base; }, + .Array => { + // TODO more checks to make sure this can be emitted as a string literal + //const array_elem_type = ptr_elem_type.elemType(); + //if (array_elem_type.eql(Type.initTag(.u8)) and + // ptr_elem_type.hasSentinel(Value.initTag(.zero))) + //{ + //} + const bytes = typed_value.val.toAllocatedBytes(allocator) catch |err| switch (err) { + error.AnalysisFail => unreachable, + else => |e| return e, + }; + const str_inst = try self.arena.allocator.create(Inst.Str); + str_inst.* = .{ + .base = .{ + .name = try self.autoName(), + .src = src, + .tag = Inst.Str.base_tag, + }, + .positionals = .{ + .bytes = bytes, + }, + .kw_args = .{}, + }; + try self.decls.append(self.allocator, &str_inst.base); + return &str_inst.base; + }, + .Void => return self.emitPrimitive(src, .void_value), else => |t| std.debug.panic("TODO implement emitTypedValue for {}", .{@tagName(t)}), } } @@ -1395,30 +1493,30 @@ const EmitZIR = struct { fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { switch (ty.tag()) { - .isize => return self.emitPrimitiveType(src, .isize), - .usize => return self.emitPrimitiveType(src, .usize), - .c_short => return self.emitPrimitiveType(src, .c_short), - .c_ushort => return self.emitPrimitiveType(src, .c_ushort), - .c_int => return self.emitPrimitiveType(src, .c_int), - .c_uint => return self.emitPrimitiveType(src, .c_uint), - .c_long => return self.emitPrimitiveType(src, .c_long), - .c_ulong => return self.emitPrimitiveType(src, .c_ulong), - .c_longlong => return self.emitPrimitiveType(src, .c_longlong), - .c_ulonglong => return self.emitPrimitiveType(src, .c_ulonglong), - .c_longdouble => return self.emitPrimitiveType(src, .c_longdouble), - .c_void => return self.emitPrimitiveType(src, .c_void), - .f16 => return self.emitPrimitiveType(src, .f16), - .f32 => return self.emitPrimitiveType(src, .f32), - .f64 => return self.emitPrimitiveType(src, .f64), - .f128 => return self.emitPrimitiveType(src, .f128), - .anyerror => return self.emitPrimitiveType(src, .anyerror), + .isize => return self.emitPrimitive(src, .isize), + .usize => return self.emitPrimitive(src, .usize), + .c_short => return self.emitPrimitive(src, .c_short), + .c_ushort => return self.emitPrimitive(src, .c_ushort), + .c_int => return self.emitPrimitive(src, .c_int), + .c_uint => return self.emitPrimitive(src, .c_uint), + .c_long => return self.emitPrimitive(src, .c_long), + .c_ulong => return self.emitPrimitive(src, .c_ulong), + .c_longlong => return self.emitPrimitive(src, .c_longlong), + .c_ulonglong => return self.emitPrimitive(src, .c_ulonglong), + .c_longdouble => return self.emitPrimitive(src, .c_longdouble), + .c_void => return self.emitPrimitive(src, .c_void), + .f16 => return self.emitPrimitive(src, .f16), + .f32 => return self.emitPrimitive(src, .f32), + .f64 => return self.emitPrimitive(src, .f64), + .f128 => return self.emitPrimitive(src, .f128), + .anyerror => return self.emitPrimitive(src, .anyerror), else => switch (ty.zigTypeTag()) { - .Bool => return self.emitPrimitiveType(src, .bool), - .Void => return self.emitPrimitiveType(src, .void), - .NoReturn => return self.emitPrimitiveType(src, .noreturn), - .Type => return self.emitPrimitiveType(src, .type), - .ComptimeInt => return self.emitPrimitiveType(src, .comptime_int), - .ComptimeFloat => return self.emitPrimitiveType(src, .comptime_float), + .Bool => return self.emitPrimitive(src, .bool), + .Void => return self.emitPrimitive(src, .void), + .NoReturn => return self.emitPrimitive(src, .noreturn), + .Type => return self.emitPrimitive(src, .type), + .ComptimeInt => return self.emitPrimitive(src, .comptime_int), + .ComptimeFloat => return self.emitPrimitive(src, .comptime_float), .Fn => { const param_types = try self.allocator.alloc(Type, ty.fnParamLen()); defer self.allocator.free(param_types); @@ -1453,24 +1551,36 @@ const EmitZIR = struct { } fn autoName(self: *EmitZIR) ![]u8 { - return std.fmt.allocPrint(&self.arena.allocator, "{}", .{self.decls.items.len}); + while (true) { + const proposed_name = try std.fmt.allocPrint(&self.arena.allocator, "unnamed${}", .{self.next_auto_name}); + self.next_auto_name += 1; + const gop = try self.names.getOrPut(proposed_name); + if (!gop.found_existing) { + gop.kv.value = {}; + return proposed_name; + } + } } - fn emitPrimitiveType(self: *EmitZIR, src: usize, tag: Inst.Primitive.BuiltinType) !*Inst { - const primitive_inst = try self.arena.allocator.create(Inst.Primitive); - primitive_inst.* = .{ - .base = .{ - .name = try self.autoName(), - .src = src, - .tag = Inst.Primitive.base_tag, - }, - .positionals = .{ - .tag = tag, - }, - .kw_args = .{}, - }; - try self.decls.append(self.allocator, &primitive_inst.base); - return &primitive_inst.base; + fn emitPrimitive(self: *EmitZIR, src: usize, tag: Inst.Primitive.Builtin) !*Inst { + const gop = try self.primitive_table.getOrPut(tag); + if (!gop.found_existing) { + const primitive_inst = try self.arena.allocator.create(Inst.Primitive); + primitive_inst.* = .{ + .base = .{ + .name = try self.autoName(), + .src = src, + .tag = Inst.Primitive.base_tag, + }, + .positionals = .{ + .tag = tag, + }, + .kw_args = .{}, + }; + try self.decls.append(self.allocator, &primitive_inst.base); + gop.kv.value = &primitive_inst.base; + } + return gop.kv.value; } fn emitStringLiteral(self: *EmitZIR, src: usize, str: []const u8) !*Inst { diff --git a/test/stage2/zir.zig b/test/stage2/zir.zig index f8b9d797d..7d5e330b8 100644 --- a/test/stage2/zir.zig +++ b/test/stage2/zir.zig @@ -21,14 +21,17 @@ pub fn addCases(ctx: *TestContext) void { \\ %11 = return() \\}) , - \\@0 = primitive(void) - \\@1 = fntype([], @0, cc=C) - \\@2 = fn(@1, { + \\@void = primitive(void) + \\@fnty = fntype([], @void, cc=C) + \\@9 = str("entry") + \\@10 = ref(@9) + \\@unnamed$6 = str("entry") + \\@unnamed$7 = ref(@unnamed$6) + \\@unnamed$8 = export(@unnamed$7, @entry) + \\@unnamed$10 = fntype([], @void, cc=C) + \\@entry = fn(@unnamed$10, { \\ %0 = return() \\}) - \\@3 = str("entry") - \\@4 = ref(@3) - \\@5 = export(@4, @2) \\ ); ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", linux_x64, @@ -68,14 +71,22 @@ pub fn addCases(ctx: *TestContext) void { \\@10 = ref(@9) \\@11 = export(@10, @entry) , - \\@0 = primitive(void) - \\@1 = fntype([], @0, cc=C) - \\@2 = fn(@1, { + \\@void = primitive(void) + \\@fnty = fntype([], @void, cc=C) + \\@0 = int(0) + \\@1 = int(1) + \\@2 = int(2) + \\@3 = int(3) + \\@unnamed$7 = fntype([], @void, cc=C) + \\@entry = fn(@unnamed$7, { \\ %0 = return() \\}) - \\@3 = str("entry") - \\@4 = ref(@3) - \\@5 = export(@4, @2) + \\@a = str("2\x08\x01\n") + \\@9 = str("entry") + \\@10 = ref(@9) + \\@unnamed$14 = str("entry") + \\@unnamed$15 = ref(@unnamed$14) + \\@unnamed$16 = export(@unnamed$15, @entry) \\ );