diff --git a/src-self-hosted/Module.zig b/src-self-hosted/Module.zig index 42a30a1d20..72e5f6cd63 100644 --- a/src-self-hosted/Module.zig +++ b/src-self-hosted/Module.zig @@ -700,19 +700,22 @@ pub const Scope = struct { pub const GenZIR = struct { pub const base_tag: Tag = .gen_zir; base: Scope = Scope{ .tag = base_tag }, + /// Parents can be: `GenZIR`, `ZIRModule`, `File` + parent: *Scope, decl: *Decl, arena: *Allocator, + /// The first N instructions in a function body ZIR are arg instructions. instructions: std.ArrayListUnmanaged(*zir.Inst) = .{}, }; /// This structure lives as long as the AST generation of the Block - /// node that contains the variable. This struct's parents can be - /// other `LocalVar` and finally a `GenZIR` at the top. + /// node that contains the variable. pub const LocalVar = struct { pub const base_tag: Tag = .local_var; base: Scope = Scope{ .tag = base_tag }, - gen_zir: *GenZIR, + /// Parents can be: `LocalVar`, `GenZIR`. parent: *Scope, + gen_zir: *GenZIR, name: []const u8, inst: *zir.Inst, }; @@ -1164,6 +1167,7 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var fn_type_scope: Scope.GenZIR = .{ .decl = decl, .arena = &fn_type_scope_arena.allocator, + .parent = decl.scope, }; defer fn_type_scope.instructions.deinit(self.gpa); @@ -1241,12 +1245,32 @@ fn astGenAndAnalyzeDecl(self: *Module, decl: *Decl) !bool { var gen_scope: Scope.GenZIR = .{ .decl = decl, .arena = &gen_scope_arena.allocator, + .parent = decl.scope, }; defer gen_scope.instructions.deinit(self.gpa); + // We need an instruction for each parameter, and they must be first in the body. + try gen_scope.instructions.resize(self.gpa, fn_proto.params_len); + var params_scope = &gen_scope.base; + for (fn_proto.params()) |param, i| { + const name_token = param.name_token.?; + const src = tree.token_locs[name_token].start; + const param_name = tree.tokenSlice(name_token); + const arg = try newZIRInst(&gen_scope_arena.allocator, src, zir.Inst.Arg, .{}, .{}); + gen_scope.instructions.items[i] = &arg.base; + const sub_scope = try gen_scope_arena.allocator.create(Scope.LocalVar); + sub_scope.* = .{ + .parent = params_scope, + .gen_zir = &gen_scope, + .name = param_name, + .inst = &arg.base, + }; + params_scope = &sub_scope.base; + } + const body_block = body_node.cast(ast.Node.Block).?; - try astgen.blockExpr(self, &gen_scope.base, body_block); + try astgen.blockExpr(self, params_scope, body_block); if (!fn_type.fnReturnType().isNoReturn() and (gen_scope.instructions.items.len == 0 or !gen_scope.instructions.items[gen_scope.instructions.items.len - 1].tag.isNoReturn())) @@ -2236,17 +2260,16 @@ fn analyzeInstCompileError(self: *Module, scope: *Scope, inst: *zir.Inst.Compile fn analyzeInstArg(self: *Module, scope: *Scope, inst: *zir.Inst.Arg) InnerError!*Inst { const b = try self.requireRuntimeBlock(scope, inst.base.src); const fn_ty = b.func.?.owner_decl.typed_value.most_recent.typed_value.ty; + const param_index = b.instructions.items.len; const param_count = fn_ty.fnParamLen(); - if (inst.positionals.index >= param_count) { + if (param_index >= param_count) { return self.fail(scope, inst.base.src, "parameter index {} outside list of length {}", .{ - inst.positionals.index, + param_index, param_count, }); } - const param_type = fn_ty.fnParamType(inst.positionals.index); - return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, .{ - .index = inst.positionals.index, - }); + const param_type = fn_ty.fnParamType(param_index); + return self.addNewInstArgs(b, inst.base.src, param_type, Inst.Arg, {}); } fn analyzeInstBlock(self: *Module, scope: *Scope, inst: *zir.Inst.Block) InnerError!*Inst { diff --git a/src-self-hosted/astgen.zig b/src-self-hosted/astgen.zig index 1f157e6389..be70a724c2 100644 --- a/src-self-hosted/astgen.zig +++ b/src-self-hosted/astgen.zig @@ -160,6 +160,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In } } var block_scope: Scope.GenZIR = .{ + .parent = scope, .decl = scope.decl().?, .arena = scope.arena(), .instructions = .{}, @@ -180,6 +181,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In .instructions = try block_scope.arena.dupe(*zir.Inst, block_scope.instructions.items), }); var then_scope: Scope.GenZIR = .{ + .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, .instructions = .{}, @@ -199,6 +201,7 @@ fn ifExpr(mod: *Module, scope: *Scope, if_node: *ast.Node.If) InnerError!*zir.In }; var else_scope: Scope.GenZIR = .{ + .parent = scope, .decl = block_scope.decl, .arena = block_scope.arena, .instructions = .{}, @@ -250,7 +253,11 @@ fn controlFlowExpr( } fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerError!*zir.Inst { + const tracy = trace(@src()); + defer tracy.end(); + const tree = scope.tree(); + // TODO implement @"aoeu" identifiers const ident_name = tree.tokenSlice(ident.token); const src = tree.token_locs[ident.token].start; if (mem.eql(u8, ident_name, "_")) { @@ -288,23 +295,27 @@ fn identifier(mod: *Module, scope: *Scope, ident: *ast.Node.Identifier) InnerErr } } + // Local variables, including function parameters. + { + var s = scope; + while (true) switch (s.tag) { + .local_var => { + const local_var = s.cast(Scope.LocalVar).?; + if (mem.eql(u8, local_var.name, ident_name)) { + return local_var.inst; + } + s = local_var.parent; + }, + .gen_zir => s = s.cast(Scope.GenZIR).?.parent, + else => break, + }; + } + if (mod.lookupDeclName(scope, ident_name)) |decl| { return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{}); } - // Function parameter - if (scope.decl()) |decl| { - if (tree.root_node.decls()[decl.src_index].cast(ast.Node.FnProto)) |fn_proto| { - for (fn_proto.params()) |param, i| { - const param_name = tree.tokenSlice(param.name_token.?); - if (mem.eql(u8, param_name, ident_name)) { - return try mod.addZIRInst(scope, src, zir.Inst.Arg, .{ .index = i }, .{}); - } - } - } - } - - return mod.failNode(scope, &ident.base, "TODO implement local variable identifier lookup", .{}); + return mod.failNode(scope, &ident.base, "use of undeclared identifier '{}'", .{ident_name}); } fn stringLiteral(mod: *Module, scope: *Scope, str_lit: *ast.Node.StringLiteral) InnerError!*zir.Inst { @@ -434,7 +445,7 @@ fn callExpr(mod: *Module, scope: *Scope, node: *ast.Node.Call) InnerError!*zir.I const lhs = try expr(mod, scope, node.lhs); const param_nodes = node.params(); - const args = try scope.cast(Scope.GenZIR).?.arena.alloc(*zir.Inst, param_nodes.len); + const args = try scope.getGenZIR().arena.alloc(*zir.Inst, param_nodes.len); for (param_nodes) |param_node, i| { args[i] = try expr(mod, scope, param_node); } diff --git a/src-self-hosted/codegen.zig b/src-self-hosted/codegen.zig index 314d497808..e78ee28b5d 100644 --- a/src-self-hosted/codegen.zig +++ b/src-self-hosted/codegen.zig @@ -73,6 +73,7 @@ pub fn generateSymbol( .code = code, .err_msg = null, .args = mc_args, + .arg_index = 0, .branch_stack = &branch_stack, .src = src, }; @@ -255,6 +256,7 @@ const Function = struct { code: *std.ArrayList(u8), err_msg: ?*ErrorMsg, args: []MCValue, + arg_index: usize, src: usize, /// Whenever there is a runtime branch, we push a Branch onto this stack, @@ -603,7 +605,9 @@ const Function = struct { } fn genArg(self: *Function, inst: *ir.Inst.Arg) !MCValue { - return self.args[inst.args.index]; + const i = self.arg_index; + self.arg_index += 1; + return self.args[i]; } fn genBreakpoint(self: *Function, src: usize, comptime arch: std.Target.Cpu.Arch) !MCValue { diff --git a/src-self-hosted/ir.zig b/src-self-hosted/ir.zig index c654bef611..a150957de0 100644 --- a/src-self-hosted/ir.zig +++ b/src-self-hosted/ir.zig @@ -101,10 +101,7 @@ pub const Inst = struct { pub const Arg = struct { pub const base_tag = Tag.arg; base: Inst, - - args: struct { - index: usize, - }, + args: void, }; pub const Assembly = struct { diff --git a/src-self-hosted/zir.zig b/src-self-hosted/zir.zig index 45ced54255..2f696d1787 100644 --- a/src-self-hosted/zir.zig +++ b/src-self-hosted/zir.zig @@ -34,7 +34,8 @@ pub const Inst = struct { /// These names are used directly as the instruction names in the text format. pub const Tag = enum { - /// Function parameter value. + /// Function parameter value. These must be first in a function's main block, + /// in respective order with the parameters. arg, /// A labeled block of code, which can return a value. block, @@ -184,9 +185,7 @@ pub const Inst = struct { pub const base_tag = Tag.arg; base: Inst, - positionals: struct { - index: usize, - }, + positionals: struct {}, kw_args: struct {}, }; @@ -1384,15 +1383,17 @@ const EmitZIR = struct { for (src_decls.items) |ir_decl| { switch (ir_decl.analysis) { .unreferenced => continue, + .complete => {}, + .codegen_failure => {}, // We still can emit the ZIR. + .codegen_failure_retryable => {}, // We still can emit the ZIR. + .in_progress => unreachable, .outdated => unreachable, .sema_failure, .sema_failure_retryable, - .codegen_failure, .dependency_failure, - .codegen_failure_retryable, => if (self.old_module.failed_decls.get(ir_decl)) |err_msg| { const fail_inst = try self.arena.allocator.create(Inst.CompileError); fail_inst.* = .{ @@ -1728,7 +1729,7 @@ const EmitZIR = struct { .src = inst.src, .tag = Inst.Arg.base_tag, }, - .positionals = .{ .index = old_inst.args.index }, + .positionals = .{}, .kw_args = .{}, }; break :blk &new_inst.base;