Commit d29dd5834b

Andrew Kelley <andrew@ziglang.org>
2020-07-16 07:36:35
stage2: local consts
These are now supported enough that this example code hits the limitations of the register allocator: fn add(a: u32, b: u32) void { const c = a + b; // 7 const d = a + c; // 10 const e = d + b; // 14 assert(e == 14); } // error: TODO implement copyToNewRegister So now the next step is to implement register allocation as planned.
1 parent af12596
Changed files (5)
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
         }
     }
 
-    if (mod.lookupDeclName(scope, ident_name)) |decl| {
-        return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
+    // 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,
+        };
     }
 
-    // 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 }, .{});
-                }
-            }
-        }
+    if (mod.lookupDeclName(scope, ident_name)) |decl| {
+        return try mod.addZIRInst(scope, src, zir.Inst.DeclValInModule, .{ .decl = decl }, .{});
     }
 
-    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);
     }
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 {
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 {
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 {
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;