Commit 5769c963e0

Andrew Kelley <andrew@ziglang.org>
2021-03-22 03:23:12
Sema: implement arithmetic
1 parent 07c2043
src/astgen.zig
@@ -3000,19 +3000,18 @@ fn as(
     lhs: ast.Node.Index,
     rhs: ast.Node.Index,
 ) InnerError!zir.Inst.Ref {
-    if (true) @panic("TODO update for zir-memory-layout");
     const dest_type = try typeExpr(mod, scope, lhs);
     switch (rl) {
         .none, .discard, .ref, .ty => {
             const result = try expr(mod, scope, .{ .ty = dest_type }, rhs);
-            return rvalue(mod, scope, rl, result);
+            return rvalue(mod, scope, rl, result, node);
         },
 
         .ptr => |result_ptr| {
-            return asRlPtr(mod, scope, rl, src, result_ptr, rhs, dest_type);
+            return asRlPtr(mod, scope, rl, result_ptr, rhs, dest_type);
         },
         .block_ptr => |block_scope| {
-            return asRlPtr(mod, scope, rl, src, block_scope.rl_ptr.?, rhs, dest_type);
+            return asRlPtr(mod, scope, rl, block_scope.rl_ptr, rhs, dest_type);
         },
 
         .bitcasted_ptr => |bitcasted_ptr| {
@@ -3030,7 +3029,6 @@ fn asRlPtr(
     mod: *Module,
     scope: *Scope,
     rl: ResultLoc,
-    src: usize,
     result_ptr: zir.Inst.Ref,
     operand_node: ast.Node.Index,
     dest_type: zir.Inst.Ref,
@@ -3038,32 +3036,35 @@ fn asRlPtr(
     // Detect whether this expr() call goes into rvalue() to store the result into the
     // result location. If it does, elide the coerce_result_ptr instruction
     // as well as the store instruction, instead passing the result as an rvalue.
+    const parent_gz = scope.getGenZir();
+
     var as_scope: Scope.GenZir = .{
         .parent = scope,
-        .decl = scope.ownerDecl().?,
-        .arena = scope.arena(),
+        .zir_code = parent_gz.zir_code,
         .force_comptime = scope.isComptime(),
         .instructions = .{},
     };
     defer as_scope.instructions.deinit(mod.gpa);
 
-    as_scope.rl_ptr = try addZIRBinOp(mod, &as_scope.base, src, .coerce_result_ptr, dest_type, result_ptr);
+    as_scope.rl_ptr = try as_scope.addBin(.coerce_result_ptr, dest_type, result_ptr);
     const result = try expr(mod, &as_scope.base, .{ .block_ptr = &as_scope }, operand_node);
-    const parent_zir = &scope.getGenZir().instructions;
+    const parent_zir = &parent_gz.instructions;
     if (as_scope.rvalue_rl_count == 1) {
         // Busted! This expression didn't actually need a pointer.
+        const zir_tags = parent_gz.zir_code.instructions.items(.tag);
+        const zir_datas = parent_gz.zir_code.instructions.items(.data);
         const expected_len = parent_zir.items.len + as_scope.instructions.items.len - 2;
         try parent_zir.ensureCapacity(mod.gpa, expected_len);
         for (as_scope.instructions.items) |src_inst| {
-            if (src_inst == as_scope.rl_ptr.?) continue;
-            if (src_inst.castTag(.store_to_block_ptr)) |store| {
-                if (store.positionals.lhs == as_scope.rl_ptr.?) continue;
+            if (src_inst == as_scope.rl_ptr) continue;
+            if (zir_tags[src_inst] == .store_to_block_ptr) {
+                if (zir_datas[src_inst].bin.lhs == as_scope.rl_ptr) continue;
             }
             parent_zir.appendAssumeCapacity(src_inst);
         }
         assert(parent_zir.items.len == expected_len);
-        const casted_result = try addZIRBinOp(mod, scope, dest_type.src, .as, dest_type, result);
-        return rvalue(mod, scope, rl, casted_result);
+        const casted_result = try parent_gz.addBin(.as, dest_type, result);
+        return rvalue(mod, scope, rl, casted_result, operand_node);
     } else {
         try parent_zir.appendSlice(mod.gpa, as_scope.instructions.items);
         return result;
src/Module.zig
@@ -1667,6 +1667,9 @@ pub const SrcLoc = struct {
             .node_offset_asm_ret_ty,
             .node_offset_if_cond,
             .node_offset_anyframe_type,
+            .node_offset_bin_op,
+            .node_offset_bin_lhs,
+            .node_offset_bin_rhs,
             => src_loc.container.decl.container.file_scope,
         };
     }
@@ -1722,6 +1725,9 @@ pub const SrcLoc = struct {
             .node_offset_asm_ret_ty => @panic("TODO"),
             .node_offset_if_cond => @panic("TODO"),
             .node_offset_anyframe_type => @panic("TODO"),
+            .node_offset_bin_op => @panic("TODO"),
+            .node_offset_bin_lhs => @panic("TODO"),
+            .node_offset_bin_rhs => @panic("TODO"),
         }
     }
 };
@@ -1846,6 +1852,20 @@ pub const LazySrcLoc = union(enum) {
     /// to the type expression.
     /// The Decl is determined contextually.
     node_offset_anyframe_type: i32,
+    /// The source location points to a binary expression, such as `a + b`, found
+    /// by taking this AST node index offset from the containing Decl AST node.
+    /// The Decl is determined contextually.
+    node_offset_bin_op: i32,
+    /// The source location points to the LHS of a binary expression, found
+    /// by taking this AST node index offset from the containing Decl AST node,
+    /// which points to a binary expression AST node. Next, nagivate to the LHS.
+    /// The Decl is determined contextually.
+    node_offset_bin_lhs: i32,
+    /// The source location points to the RHS of a binary expression, found
+    /// by taking this AST node index offset from the containing Decl AST node,
+    /// which points to a binary expression AST node. Next, nagivate to the RHS.
+    /// The Decl is determined contextually.
+    node_offset_bin_rhs: i32,
 
     /// Upgrade to a `SrcLoc` based on the `Decl` or file in the provided scope.
     pub fn toSrcLoc(lazy: LazySrcLoc, scope: *Scope) SrcLoc {
@@ -1877,6 +1897,9 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_asm_ret_ty,
             .node_offset_if_cond,
             .node_offset_anyframe_type,
+            .node_offset_bin_op,
+            .node_offset_bin_lhs,
+            .node_offset_bin_rhs,
             => .{
                 .container = .{ .decl = scope.srcDecl().? },
                 .lazy = lazy,
@@ -1914,6 +1937,9 @@ pub const LazySrcLoc = union(enum) {
             .node_offset_asm_ret_ty,
             .node_offset_if_cond,
             .node_offset_anyframe_type,
+            .node_offset_bin_op,
+            .node_offset_bin_lhs,
+            .node_offset_bin_rhs,
             => .{
                 .container = .{ .decl = decl },
                 .lazy = lazy,
src/Sema.zig
@@ -2419,17 +2419,18 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
     const tracy = trace(@src());
     defer tracy.end();
 
-    if (true) @panic("TODO rework with zir-memory-layout in mind");
-
-    const bin_inst = sema.code.instructions.items(.data)[inst].bin;
-    const src: LazySrcLoc = .todo;
-    const lhs = try sema.resolveInst(bin_inst.lhs);
-    const rhs = try sema.resolveInst(bin_inst.rhs);
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
+    const lhs_src: LazySrcLoc = .{ .node_offset_bin_lhs = inst_data.src_node };
+    const rhs_src: LazySrcLoc = .{ .node_offset_bin_rhs = inst_data.src_node };
+    const extra = sema.code.extraData(zir.Inst.Bin, inst_data.payload_index).data;
+    const lhs = try sema.resolveInst(extra.lhs);
+    const rhs = try sema.resolveInst(extra.rhs);
 
     const instructions = &[_]*Inst{ lhs, rhs };
     const resolved_type = try sema.resolvePeerTypes(block, instructions);
-    const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs.src);
-    const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs.src);
+    const casted_lhs = try sema.coerce(block, resolved_type, lhs, lhs_src);
+    const casted_rhs = try sema.coerce(block, resolved_type, rhs, rhs_src);
 
     const scalar_type = if (resolved_type.zigTypeTag() == .Vector)
         resolved_type.elemType()
@@ -2455,8 +2456,9 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
 
     const is_int = scalar_tag == .Int or scalar_tag == .ComptimeInt;
     const is_float = scalar_tag == .Float or scalar_tag == .ComptimeFloat;
+    const zir_tags = block.sema.code.instructions.items(.tag);
 
-    if (!is_int and !(is_float and floatOpAllowed(inst.base.tag))) {
+    if (!is_int and !(is_float and floatOpAllowed(zir_tags[inst]))) {
         return sema.mod.fail(&block.base, src, "invalid operands to binary expression: '{s}' and '{s}'", .{ @tagName(lhs.ty.zigTypeTag()), @tagName(rhs.ty.zigTypeTag()) });
     }
 
@@ -2468,71 +2470,56 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerEr
                     .val = Value.initTag(.undef),
                 });
             }
-            return sema.analyzeInstComptimeOp(block, scalar_type, inst, lhs_val, rhs_val);
+            // incase rhs is 0, simply return lhs without doing any calculations
+            // TODO Once division is implemented we should throw an error when dividing by 0.
+            if (rhs_val.compareWithZero(.eq)) {
+                return sema.mod.constInst(sema.arena, src, .{
+                    .ty = scalar_type,
+                    .val = lhs_val,
+                });
+            }
+
+            const value = switch (zir_tags[inst]) {
+                .add => blk: {
+                    const val = if (is_int)
+                        try Module.intAdd(sema.arena, lhs_val, rhs_val)
+                    else
+                        try Module.floatAdd(sema.arena, scalar_type, src, lhs_val, rhs_val);
+                    break :blk val;
+                },
+                .sub => blk: {
+                    const val = if (is_int)
+                        try Module.intSub(sema.arena, lhs_val, rhs_val)
+                    else
+                        try Module.floatSub(sema.arena, scalar_type, src, lhs_val, rhs_val);
+                    break :blk val;
+                },
+                else => return sema.mod.fail(&block.base, src, "TODO Implement arithmetic operand '{s}'", .{@tagName(zir_tags[inst])}),
+            };
+
+            log.debug("{s}({}, {}) result: {}", .{ @tagName(zir_tags[inst]), lhs_val, rhs_val, value });
+
+            return sema.mod.constInst(sema.arena, src, .{
+                .ty = scalar_type,
+                .val = value,
+            });
         }
     }
 
     try sema.requireRuntimeBlock(block, src);
-    const ir_tag: Inst.Tag = switch (inst.base.tag) {
+    const ir_tag: Inst.Tag = switch (zir_tags[inst]) {
         .add => .add,
         .addwrap => .addwrap,
         .sub => .sub,
         .subwrap => .subwrap,
         .mul => .mul,
         .mulwrap => .mulwrap,
-        else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(inst.base.tag)}),
+        else => return sema.mod.fail(&block.base, src, "TODO implement arithmetic for operand '{s}''", .{@tagName(zir_tags[inst])}),
     };
 
     return block.addBinOp(src, scalar_type, ir_tag, casted_lhs, casted_rhs);
 }
 
-/// Analyzes operands that are known at comptime
-fn analyzeInstComptimeOp(
-    sema: *Sema,
-    block: *Scope.Block,
-    res_type: Type,
-    inst: zir.Inst.Index,
-    lhs_val: Value,
-    rhs_val: Value,
-) InnerError!*Inst {
-    if (true) @panic("TODO rework analyzeInstComptimeOp for zir-memory-layout");
-
-    // incase rhs is 0, simply return lhs without doing any calculations
-    // TODO Once division is implemented we should throw an error when dividing by 0.
-    if (rhs_val.compareWithZero(.eq)) {
-        return sema.mod.constInst(sema.arena, inst.base.src, .{
-            .ty = res_type,
-            .val = lhs_val,
-        });
-    }
-    const is_int = res_type.isInt() or res_type.zigTypeTag() == .ComptimeInt;
-
-    const value = switch (inst.base.tag) {
-        .add => blk: {
-            const val = if (is_int)
-                try Module.intAdd(sema.arena, lhs_val, rhs_val)
-            else
-                try Module.floatAdd(sema.arena, res_type, inst.base.src, lhs_val, rhs_val);
-            break :blk val;
-        },
-        .sub => blk: {
-            const val = if (is_int)
-                try Module.intSub(sema.arena, lhs_val, rhs_val)
-            else
-                try Module.floatSub(sema.arena, res_type, inst.base.src, lhs_val, rhs_val);
-            break :blk val;
-        },
-        else => return sema.mod.fail(&block.base, inst.base.src, "TODO Implement arithmetic operand '{s}'", .{@tagName(inst.base.tag)}),
-    };
-
-    log.debug("{s}({}, {}) result: {}", .{ @tagName(inst.base.tag), lhs_val, rhs_val, value });
-
-    return sema.mod.constInst(sema.arena, inst.base.src, .{
-        .ty = res_type,
-        .val = value,
-    });
-}
-
 fn zirDerefNode(sema: *Sema, block: *Scope.Block, inst: zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
src/zir.zig
@@ -524,6 +524,7 @@ pub const Inst = struct {
         cmp_neq,
         /// Coerces a result location pointer to a new element type. It is evaluated "backwards"-
         /// as type coercion from the new element type to the old element type.
+        /// Uses the `bin` union field.
         /// LHS is destination element type, RHS is result pointer.
         coerce_result_ptr,
         /// Emit an error message and fail compilation.
@@ -1327,33 +1328,12 @@ const Writer = struct {
         const tag = tags[inst];
         try stream.print("= {s}(", .{@tagName(tags[inst])});
         switch (tag) {
-            .add,
-            .addwrap,
-            .array_cat,
-            .array_mul,
-            .mul,
-            .mulwrap,
-            .sub,
-            .subwrap,
             .array_type,
             .bit_and,
             .bit_or,
             .as,
-            .bool_and,
-            .bool_or,
             .@"break",
-            .cmp_lt,
-            .cmp_lte,
-            .cmp_eq,
-            .cmp_gte,
-            .cmp_gt,
-            .cmp_neq,
             .coerce_result_ptr,
-            .div,
-            .mod_rem,
-            .shl,
-            .shr,
-            .xor,
             .elem_ptr,
             .elem_val,
             .intcast,
@@ -1447,6 +1427,29 @@ const Writer = struct {
             .suspend_block,
             => try self.writePlNode(stream, inst),
 
+            .add,
+            .addwrap,
+            .array_cat,
+            .array_mul,
+            .mul,
+            .mulwrap,
+            .sub,
+            .subwrap,
+            .bool_and,
+            .bool_or,
+            .cmp_lt,
+            .cmp_lte,
+            .cmp_eq,
+            .cmp_gte,
+            .cmp_gt,
+            .cmp_neq,
+            .div,
+            .mod_rem,
+            .shl,
+            .shr,
+            .xor,
+            => try self.writePlNodeBin(stream, inst),
+
             .as_node => try self.writeAs(stream, inst),
 
             .breakpoint,
@@ -1589,6 +1592,16 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writePlNodeBin(self: *Writer, stream: anytype, inst: Inst.Index) !void {
+        const inst_data = self.code.instructions.items(.data)[inst].pl_node;
+        const extra = self.code.extraData(Inst.Bin, inst_data.payload_index).data;
+        try self.writeInstRef(stream, extra.lhs);
+        try stream.writeAll(", ");
+        try self.writeInstRef(stream, extra.rhs);
+        try stream.writeAll(") ");
+        try self.writeSrc(stream, inst_data.src());
+    }
+
     fn writeAs(self: *Writer, stream: anytype, inst: Inst.Index) !void {
         const inst_data = self.code.instructions.items(.data)[inst].pl_node;
         const extra = self.code.extraData(Inst.As, inst_data.payload_index).data;