diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-04-27 18:36:12 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-04-28 16:57:01 -0700 |
| commit | f86469bc5eea2b7bd95222d00a11bd287bfdfedf (patch) | |
| tree | c34ef71820e4c7b962a631f2b9cdecdff3f5aa79 /src/Module.zig | |
| parent | fa6bb4b662155e4d6a61cc551b5d02a2a7d5d144 (diff) | |
| download | zig-f86469bc5eea2b7bd95222d00a11bd287bfdfedf.tar.gz zig-f86469bc5eea2b7bd95222d00a11bd287bfdfedf.zip | |
stage2: semaDecl properly analyzes the decl block
Also flattened out Decl TypedValue fields into
ty, val, has_tv
and add relevant fields to Decl for alignment and link section.
Diffstat (limited to 'src/Module.zig')
| -rw-r--r-- | src/Module.zig | 325 |
1 files changed, 190 insertions, 135 deletions
diff --git a/src/Module.zig b/src/Module.zig index 55049ec547..ea9f185bee 100644 --- a/src/Module.zig +++ b/src/Module.zig @@ -154,17 +154,29 @@ pub const DeclPlusEmitH = struct { }; pub const Decl = struct { - /// This name is relative to the containing namespace of the decl. It uses - /// null-termination to save bytes, since there can be a lot of decls in a - /// compilation. The null byte is not allowed in symbol names, because - /// executable file formats use null-terminated strings for symbol names. + /// This name is relative to the containing namespace of the decl. /// All Decls have names, even values that are not bound to a zig namespace. /// This is necessary for mapping them to an address in the output file. - /// Memory owned by this decl, using Module's allocator. + /// Memory is owned by this decl, using Module's allocator. + /// Note that this cannot be changed to reference ZIR memory because when + /// ZIR updates, it would change the Decl name, but we still need the previous + /// name to delete the Decl from the hash maps it has been inserted into. name: [*:0]const u8, + /// The most recent Type of the Decl after a successful semantic analysis. + /// Populated when `has_tv`. + ty: Type, + /// The most recent Value of the Decl after a successful semantic analysis. + /// Populated when `has_tv`. + val: Value, + /// Populated when `has_tv`. + align_val: Value, + /// Populated when `has_tv`. + linksection_val: Value, + /// The memory for ty, val, align_val, linksection_val. + /// If this is `null` then there is no memory management needed. + value_arena: ?*std.heap.ArenaAllocator.State = null, /// The direct parent namespace of the Decl. /// Reference to externally owned memory. - /// This is `null` for the Decl that represents a `File`. namespace: *Scope.Namespace, /// An integer that can be checked against the corresponding incrementing @@ -174,12 +186,11 @@ pub const Decl = struct { /// The AST node index of this declaration. /// Must be recomputed when the corresponding source file is modified. src_node: ast.Node.Index, + /// Index to ZIR `extra` array to the block of ZIR code that encodes the Decl expression. + zir_block_index: Zir.Inst.Index, + zir_align_ref: Zir.Inst.Ref = .none, + zir_linksection_ref: Zir.Inst.Ref = .none, - /// The most recent value of the Decl after a successful semantic analysis. - typed_value: union(enum) { - never_succeeded: void, - most_recent: TypedValue.Managed, - }, /// Represents the "shallow" analysis status. For example, for decls that are functions, /// the function type is analyzed with this set to `in_progress`, however, the semantic /// analysis of the function body is performed with this value set to `success`. Functions @@ -214,11 +225,15 @@ pub const Decl = struct { /// to require re-analysis. outdated, }, + /// Whether `typed_value`, `align_val`, and `linksection_val` are populated. + has_tv: bool, /// This flag is set when this Decl is added to `Module.deletion_set`, and cleared /// when removed. deletion_flag: bool, /// Whether the corresponding AST decl has a `pub` keyword. is_pub: bool, + /// Whether the corresponding AST decl has a `export` keyword. + is_exported: bool, /// Represents the position of the code in the output file. /// This is populated regardless of semantic analysis and code generation. @@ -231,6 +246,9 @@ pub const Decl = struct { /// to save on memory usage. fn_link: link.File.LinkFn, + /// This is stored separately in addition to being available via `zir_decl_index` + /// because when the underlying ZIR code is updated, this field is used to find + /// out if anything changed. contents_hash: std.zig.SrcHash, /// The shallow set of other decls whose typed_value could possibly change if this Decl's @@ -247,12 +265,12 @@ pub const Decl = struct { pub fn destroy(decl: *Decl, module: *Module) void { const gpa = module.gpa; gpa.free(mem.spanZ(decl.name)); - if (decl.typedValueManaged()) |tvm| { - if (tvm.typed_value.val.castTag(.function)) |payload| { + if (decl.has_tv) { + if (decl.val.castTag(.function)) |payload| { const func = payload.data; func.deinit(gpa); } - tvm.deinit(gpa); + if (decl.value_arena) |a| a.promote(gpa).deinit(); } decl.dependants.deinit(gpa); decl.dependencies.deinit(gpa); @@ -311,9 +329,12 @@ pub const Decl = struct { return buffer.toOwnedSlice(); } - pub fn typedValue(decl: *Decl) error{AnalysisFail}!TypedValue { - const tvm = decl.typedValueManaged() orelse return error.AnalysisFail; - return tvm.typed_value; + pub fn typedValue(decl: Decl) error{AnalysisFail}!TypedValue { + if (!decl.has_tv) return error.AnalysisFail; + return TypedValue{ + .ty = decl.ty, + .val = decl.val, + }; } pub fn value(decl: *Decl) error{AnalysisFail}!Value { @@ -334,19 +355,12 @@ pub const Decl = struct { mem.spanZ(decl.name), @tagName(decl.analysis), }); - if (decl.typedValueManaged()) |tvm| { - std.debug.print(" ty={} val={}", .{ tvm.typed_value.ty, tvm.typed_value.val }); + if (decl.has_tv) { + std.debug.print(" ty={} val={}", .{ decl.ty, decl.val }); } std.debug.print("\n", .{}); } - pub fn typedValueManaged(decl: *Decl) ?*TypedValue.Managed { - switch (decl.typed_value) { - .most_recent => |*x| return x, - .never_succeeded => return null, - } - } - pub fn getFileScope(decl: Decl) *Scope.File { return decl.namespace.file_scope; } @@ -475,16 +489,6 @@ pub const EnumFull = struct { /// the `Decl` only, with a `Value` tag of `extern_fn`. pub const Fn = struct { owner_decl: *Decl, - /// Contains un-analyzed ZIR instructions generated from Zig source AST. - /// Even after we finish analysis, the ZIR is kept in memory, so that - /// comptime and inline function calls can happen. - /// Parameter names are stored here so that they may be referenced for debug info, - /// without having source code bytes loaded into memory. - /// The number of parameters is determined by referring to the type. - /// The first N elements of `extra` are indexes into `string_bytes` to - /// a null-terminated string. - /// This memory is managed with gpa, must be freed when the function is freed. - zir: Zir, /// undefined unless analysis state is `success`. body: ir.Body, state: Analysis, @@ -508,9 +512,7 @@ pub const Fn = struct { ir.dumpFn(mod, func); } - pub fn deinit(func: *Fn, gpa: *Allocator) void { - func.zir.deinit(gpa); - } + pub fn deinit(func: *Fn, gpa: *Allocator) void {} }; pub const Var = struct { @@ -3111,7 +3113,7 @@ pub fn ensureDeclAnalyzed(mod: *Module, decl: *Decl) InnerError!void { if (subsequent_analysis) { // We may need to chase the dependants and re-analyze them. // However, if the decl is a function, and the type is the same, we do not need to. - if (type_changed or decl.typed_value.most_recent.typed_value.val.tag() != .function) { + if (type_changed or decl.ty.zigTypeTag() != .Fn) { for (decl.dependants.items()) |entry| { const dep = entry.key; switch (dep.analysis) { @@ -3162,13 +3164,20 @@ pub fn semaFile(mod: *Module, file: *Scope.File) InnerError!void { .namespace = &tmp_namespace, .generation = mod.generation, .src_node = 0, // the root AST node for the file - .typed_value = .never_succeeded, .analysis = .in_progress, .deletion_flag = false, .is_pub = true, + .is_exported = false, .link = undefined, // don't try to codegen this .fn_link = undefined, // not a function .contents_hash = undefined, // top-level struct has no contents hash + .zir_block_index = undefined, + + .has_tv = false, + .ty = undefined, + .val = undefined, + .align_val = undefined, + .linksection_val = undefined, }; defer top_decl.dependencies.deinit(gpa); @@ -3223,7 +3232,56 @@ fn semaDecl(mod: *Module, decl: *Decl) !bool { const tracy = trace(@src()); defer tracy.end(); - @panic("TODO implement semaDecl"); + const gpa = mod.gpa; + + decl.analysis = .in_progress; + + var analysis_arena = std.heap.ArenaAllocator.init(gpa); + defer analysis_arena.deinit(); + + const zir = decl.namespace.file_scope.zir; + + var sema: Sema = .{ + .mod = mod, + .gpa = gpa, + .arena = &analysis_arena.allocator, + .code = zir, + .inst_map = try analysis_arena.allocator.alloc(*ir.Inst, zir.instructions.len), + .owner_decl = decl, + .namespace = decl.namespace, + .func = null, + .owner_func = null, + .param_inst_list = &.{}, + }; + var block_scope: Scope.Block = .{ + .parent = null, + .sema = &sema, + .src_decl = decl, + .instructions = .{}, + .inlining = null, + .is_comptime = true, + }; + defer block_scope.instructions.deinit(gpa); + + const inst_data = zir.instructions.items(.data)[decl.zir_block_index].pl_node; + const extra = zir.extraData(Zir.Inst.Block, inst_data.payload_index); + const body = zir.extra[extra.end..][0..extra.data.body_len]; + const break_index = try sema.analyzeBody(&block_scope, body); + + if (decl.zir_align_ref != .none) { + @panic("TODO implement decl align"); + } + if (decl.zir_linksection_ref != .none) { + @panic("TODO implement decl linksection"); + } + + decl.analysis = .complete; + decl.generation = mod.generation; + + // TODO inspect the type and return a proper type_changed result + @breakpoint(); + + return true; } /// Returns the depender's index of the dependee. @@ -3489,6 +3547,7 @@ fn scanDecl( const gpa = mod.gpa; const zir = namespace.file_scope.zir; + const decl_block_inst_data = zir.instructions.items(.data)[decl_index].pl_node; const decl_node = parent_decl.relativeToNodeIndex(decl_block_inst_data.src_node); @@ -3504,13 +3563,9 @@ fn scanDecl( const decl_key = decl_name orelse &contents_hash; const gop = try namespace.decls.getOrPut(gpa, decl_key); if (!gop.found_existing) { - if (align_inst != .none) { - return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with align()", .{}); - } - if (section_inst != .none) { - return mod.fail(&namespace.base, .{ .node_abs = decl_node }, "TODO: implement decls with linksection()", .{}); - } - const new_decl = try mod.createNewDecl(namespace, decl_key, decl_node, contents_hash); + const new_decl = try mod.allocateNewDecl(namespace, decl_node); + new_decl.contents_hash = contents_hash; + new_decl.name = try gpa.dupeZ(u8, decl_key); // Update the key reference to the longer-lived memory. gop.entry.key = &new_decl.contents_hash; gop.entry.value = new_decl; @@ -3524,7 +3579,11 @@ fn scanDecl( if (want_analysis) { mod.comp.work_queue.writeItemAssumeCapacity(.{ .analyze_decl = new_decl }); } + new_decl.is_exported = is_exported; new_decl.is_pub = is_pub; + new_decl.zir_block_index = decl_index; + new_decl.zir_align_ref = align_inst; + new_decl.zir_linksection_ref = section_inst; return; } const decl = gop.entry.value; @@ -3532,6 +3591,11 @@ fn scanDecl( // have been re-ordered. const prev_src_node = decl.src_node; decl.src_node = decl_node; + decl.is_pub = is_pub; + decl.is_exported = is_exported; + decl.zir_block_index = decl_index; + decl.zir_align_ref = align_inst; + decl.zir_linksection_ref = section_inst; if (deleted_decls.swapRemove(decl) == null) { if (true) { @panic("TODO I think this code path is unreachable; should be caught by AstGen."); @@ -3681,64 +3745,70 @@ pub fn analyzeFnBody(mod: *Module, decl: *Decl, func: *Fn) !void { defer tracy.end(); // Use the Decl's arena for function memory. - var arena = decl.typed_value.most_recent.arena.?.promote(mod.gpa); - defer decl.typed_value.most_recent.arena.?.* = arena.state; + var arena = decl.value_arena.?.promote(mod.gpa); + defer decl.value_arena.?.* = arena.state; - const fn_ty = decl.typed_value.most_recent.typed_value.ty; + const fn_ty = decl.ty; const param_inst_list = try mod.gpa.alloc(*ir.Inst, fn_ty.fnParamLen()); defer mod.gpa.free(param_inst_list); - for (param_inst_list) |*param_inst, param_index| { - const param_type = fn_ty.fnParamType(param_index); - const name = func.zir.nullTerminatedString(func.zir.extra[param_index]); - const arg_inst = try arena.allocator.create(ir.Inst.Arg); - arg_inst.* = .{ - .base = .{ - .tag = .arg, - .ty = param_type, - .src = .unneeded, - }, - .name = name, - }; - param_inst.* = &arg_inst.base; + var f = false; + if (f) { + return error.AnalysisFail; } + @panic("TODO reimplement analyzeFnBody now that ZIR is whole-file"); + + //for (param_inst_list) |*param_inst, param_index| { + // const param_type = fn_ty.fnParamType(param_index); + // const name = func.zir.nullTerminatedString(func.zir.extra[param_index]); + // const arg_inst = try arena.allocator.create(ir.Inst.Arg); + // arg_inst.* = .{ + // .base = .{ + // .tag = .arg, + // .ty = param_type, + // .src = .unneeded, + // }, + // .name = name, + // }; + // param_inst.* = &arg_inst.base; + //} - var sema: Sema = .{ - .mod = mod, - .gpa = mod.gpa, - .arena = &arena.allocator, - .code = func.zir, - .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), - .owner_decl = decl, - .namespace = decl.namespace, - .func = func, - .owner_func = func, - .param_inst_list = param_inst_list, - }; - defer mod.gpa.free(sema.inst_map); - - var inner_block: Scope.Block = .{ - .parent = null, - .sema = &sema, - .src_decl = decl, - .instructions = .{}, - .inlining = null, - .is_comptime = false, - }; - defer inner_block.instructions.deinit(mod.gpa); - - // AIR currently requires the arg parameters to be the first N instructions - try inner_block.instructions.appendSlice(mod.gpa, param_inst_list); - - func.state = .in_progress; - log.debug("set {s} to in_progress", .{decl.name}); - - _ = try sema.root(&inner_block); - - const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items); - func.state = .success; - func.body = .{ .instructions = instructions }; - log.debug("set {s} to success", .{decl.name}); + //var sema: Sema = .{ + // .mod = mod, + // .gpa = mod.gpa, + // .arena = &arena.allocator, + // .code = func.zir, + // .inst_map = try mod.gpa.alloc(*ir.Inst, func.zir.instructions.len), + // .owner_decl = decl, + // .namespace = decl.namespace, + // .func = func, + // .owner_func = func, + // .param_inst_list = param_inst_list, + //}; + //defer mod.gpa.free(sema.inst_map); + + //var inner_block: Scope.Block = .{ + // .parent = null, + // .sema = &sema, + // .src_decl = decl, + // .instructions = .{}, + // .inlining = null, + // .is_comptime = false, + //}; + //defer inner_block.instructions.deinit(mod.gpa); + + //// AIR currently requires the arg parameters to be the first N instructions + //try inner_block.instructions.appendSlice(mod.gpa, param_inst_list); + + //func.state = .in_progress; + //log.debug("set {s} to in_progress", .{decl.name}); + + //_ = try sema.root(&inner_block); + + //const instructions = try arena.allocator.dupe(*ir.Inst, inner_block.instructions.items); + //func.state = .success; + //func.body = .{ .instructions = instructions }; + //log.debug("set {s} to success", .{decl.name}); } fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { @@ -3756,12 +3826,7 @@ fn markOutdatedDecl(mod: *Module, decl: *Decl) !void { decl.analysis = .outdated; } -fn allocateNewDecl( - mod: *Module, - namespace: *Scope.Namespace, - src_node: ast.Node.Index, - contents_hash: std.zig.SrcHash, -) !*Decl { +fn allocateNewDecl(mod: *Module, namespace: *Scope.Namespace, src_node: ast.Node.Index) !*Decl { // If we have emit-h then we must allocate a bigger structure to store the emit-h state. const new_decl: *Decl = if (mod.emit_h != null) blk: { const parent_struct = try mod.gpa.create(DeclPlusEmitH); @@ -3776,10 +3841,15 @@ fn allocateNewDecl( .name = "", .namespace = namespace, .src_node = src_node, - .typed_value = .{ .never_succeeded = {} }, + .has_tv = false, + .ty = undefined, + .val = undefined, + .align_val = undefined, + .linksection_val = undefined, .analysis = .unreferenced, .deletion_flag = false, - .contents_hash = contents_hash, + .contents_hash = undefined, + .zir_block_index = undefined, .link = switch (mod.comp.bin_file.tag) { .coff => .{ .coff = link.File.Coff.TextBlock.empty }, .elf => .{ .elf = link.File.Elf.TextBlock.empty }, @@ -3798,23 +3868,11 @@ fn allocateNewDecl( }, .generation = 0, .is_pub = false, + .is_exported = false, }; return new_decl; } -fn createNewDecl( - mod: *Module, - namespace: *Scope.Namespace, - decl_name: []const u8, - src_node: ast.Node.Index, - contents_hash: std.zig.SrcHash, -) !*Decl { - const new_decl = try mod.allocateNewDecl(namespace, src_node, contents_hash); - errdefer mod.gpa.destroy(new_decl); - new_decl.name = try mem.dupeZ(mod.gpa, u8, decl_name); - return new_decl; -} - /// Get error value for error tag `name`. pub fn getErrorValue(mod: *Module, name: []const u8) !std.StringHashMapUnmanaged(ErrorInt).Entry { const gop = try mod.global_error_set.getOrPut(mod.gpa, name); @@ -3837,10 +3895,9 @@ pub fn analyzeExport( exported_decl: *Decl, ) !void { try mod.ensureDeclAnalyzed(exported_decl); - const typed_value = exported_decl.typed_value.most_recent.typed_value; - switch (typed_value.ty.zigTypeTag()) { + switch (exported_decl.ty.zigTypeTag()) { .Fn => {}, - else => return mod.fail(scope, src, "unable to export type '{}'", .{typed_value.ty}), + else => return mod.fail(scope, src, "unable to export type '{}'", .{exported_decl.ty}), } try mod.decl_exports.ensureCapacity(mod.gpa, mod.decl_exports.items().len + 1); @@ -4017,20 +4074,18 @@ pub fn createAnonymousDecl( ) !*Decl { const name_index = mod.getNextAnonNameIndex(); const scope_decl = scope.ownerDecl().?; - const name = try std.fmt.allocPrint(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); - defer mod.gpa.free(name); + const name = try std.fmt.allocPrintZ(mod.gpa, "{s}__anon_{d}", .{ scope_decl.name, name_index }); + errdefer mod.gpa.free(name); const namespace = scope_decl.namespace; - const src_hash: std.zig.SrcHash = undefined; - const new_decl = try mod.createNewDecl(namespace, name, scope_decl.src_node, src_hash); + const new_decl = try mod.allocateNewDecl(namespace, scope_decl.src_node); + new_decl.name = name; + const decl_arena_state = try decl_arena.allocator.create(std.heap.ArenaAllocator.State); decl_arena_state.* = decl_arena.state; - new_decl.typed_value = .{ - .most_recent = .{ - .typed_value = typed_value, - .arena = decl_arena_state, - }, - }; + new_decl.ty = typed_value.ty; + new_decl.val = typed_value.val; + new_decl.has_tv = true; new_decl.analysis = .complete; new_decl.generation = mod.generation; |
