Commit 5a3045b5de

Andrew Kelley <andrew@ziglang.org>
2021-04-17 22:00:10
AstGen: implement overflow arithmetic builtins
1 parent 8cf0ef2
Changed files (3)
src/AstGen.zig
@@ -1407,6 +1407,11 @@ fn blockExprStmts(
                         .fence,
                         .ret_addr,
                         .builtin_src,
+                        .add_with_overflow,
+                        .sub_with_overflow,
+                        .mul_with_overflow,
+                        .shl_with_overflow,
+                        .log2_int_type,
                         => break :b false,
 
                         // ZIR instructions that are always either `noreturn` or `void`.
@@ -4910,7 +4915,32 @@ fn builtinCall(
         .return_address => return rvalue(gz, scope, rl, try gz.addNode(.ret_addr, node), node),
         .src => return rvalue(gz, scope, rl, try gz.addNode(.builtin_src, node), node),
 
-        .add_with_overflow,
+        .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
+        .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
+        .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),
+        .shl_with_overflow => {
+            const int_type = try typeExpr(gz, scope, params[0]);
+            const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]);
+            const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
+                .ptr_type_simple = .{
+                    .is_allowzero = false,
+                    .is_mutable = true,
+                    .is_volatile = false,
+                    .size = .One,
+                    .elem_type = int_type,
+                },
+            } });
+            const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
+            const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, params[2]);
+            const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
+            const result = try gz.addPlNode(.shl_with_overflow, node, Zir.Inst.OverflowArithmetic{
+                .lhs = lhs,
+                .rhs = rhs,
+                .ptr = ptr,
+            });
+            return rvalue(gz, scope, rl, result, node);
+        },
+
         .align_cast,
         .align_of,
         .atomic_load,
@@ -4948,7 +4978,6 @@ fn builtinCall(
         .wasm_memory_size,
         .wasm_memory_grow,
         .mod,
-        .mul_with_overflow,
         .panic,
         .pop_count,
         .ptr_cast,
@@ -4958,7 +4987,6 @@ fn builtinCall(
         .set_float_mode,
         .set_runtime_safety,
         .shl_exact,
-        .shl_with_overflow,
         .shr_exact,
         .shuffle,
         .splat,
@@ -4976,7 +5004,6 @@ fn builtinCall(
         .ceil,
         .trunc,
         .round,
-        .sub_with_overflow,
         .tag_name,
         .truncate,
         .Type,
@@ -4993,6 +5020,35 @@ fn builtinCall(
     }
 }
 
+fn overflowArithmetic(
+    gz: *GenZir,
+    scope: *Scope,
+    rl: ResultLoc,
+    node: ast.Node.Index,
+    params: []const ast.Node.Index,
+    tag: Zir.Inst.Tag,
+) InnerError!Zir.Inst.Ref {
+    const int_type = try typeExpr(gz, scope, params[0]);
+    const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
+        .ptr_type_simple = .{
+            .is_allowzero = false,
+            .is_mutable = true,
+            .is_volatile = false,
+            .size = .One,
+            .elem_type = int_type,
+        },
+    } });
+    const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
+    const rhs = try expr(gz, scope, .{ .ty = int_type }, params[2]);
+    const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
+    const result = try gz.addPlNode(tag, node, Zir.Inst.OverflowArithmetic{
+        .lhs = lhs,
+        .rhs = rhs,
+        .ptr = ptr,
+    });
+    return rvalue(gz, scope, rl, result, node);
+}
+
 fn callExpr(
     gz: *GenZir,
     scope: *Scope,
src/Sema.zig
@@ -133,8 +133,6 @@ pub fn analyzeBody(
         map[inst] = switch (tags[inst]) {
             .elided => continue,
 
-            .add => try sema.zirArithmetic(block, inst),
-            .addwrap => try sema.zirArithmetic(block, inst),
             .alloc => try sema.zirAlloc(block, inst),
             .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
             .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
@@ -173,7 +171,6 @@ pub fn analyzeBody(
             .decl_ref => try sema.zirDeclRef(block, inst),
             .decl_val => try sema.zirDeclVal(block, inst),
             .load => try sema.zirLoad(block, inst),
-            .div => try sema.zirArithmetic(block, inst),
             .elem_ptr => try sema.zirElemPtr(block, inst),
             .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
             .elem_val => try sema.zirElemVal(block, inst),
@@ -217,9 +214,6 @@ pub fn analyzeBody(
             .is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
             .loop => try sema.zirLoop(block, inst),
             .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
-            .mod_rem => try sema.zirArithmetic(block, inst),
-            .mul => try sema.zirArithmetic(block, inst),
-            .mulwrap => try sema.zirArithmetic(block, inst),
             .negate => try sema.zirNegate(block, inst, .sub),
             .negate_wrap => try sema.zirNegate(block, inst, .subwrap),
             .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
@@ -241,8 +235,6 @@ pub fn analyzeBody(
             .slice_sentinel => try sema.zirSliceSentinel(block, inst),
             .slice_start => try sema.zirSliceStart(block, inst),
             .str => try sema.zirStr(block, inst),
-            .sub => try sema.zirArithmetic(block, inst),
-            .subwrap => try sema.zirArithmetic(block, inst),
             .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
             .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
             .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
@@ -271,6 +263,7 @@ pub fn analyzeBody(
             .typeof => try sema.zirTypeof(block, inst),
             .typeof_elem => try sema.zirTypeofElem(block, inst),
             .typeof_peer => try sema.zirTypeofPeer(block, inst),
+            .log2_int_type => try sema.zirLog2IntType(block, inst),
             .xor => try sema.zirBitwise(block, inst, .xor),
             .struct_init_empty => try sema.zirStructInitEmpty(block, inst),
             .struct_init => try sema.zirStructInit(block, inst),
@@ -284,6 +277,20 @@ pub fn analyzeBody(
             .union_decl => try sema.zirUnionDecl(block, inst),
             .opaque_decl => try sema.zirOpaqueDecl(block, inst),
 
+            .add => try sema.zirArithmetic(block, inst),
+            .addwrap => try sema.zirArithmetic(block, inst),
+            .div => try sema.zirArithmetic(block, inst),
+            .mod_rem => try sema.zirArithmetic(block, inst),
+            .mul => try sema.zirArithmetic(block, inst),
+            .mulwrap => try sema.zirArithmetic(block, inst),
+            .sub => try sema.zirArithmetic(block, inst),
+            .subwrap => try sema.zirArithmetic(block, inst),
+
+            .add_with_overflow => try sema.zirOverflowArithmetic(block, inst),
+            .sub_with_overflow => try sema.zirOverflowArithmetic(block, inst),
+            .mul_with_overflow => try sema.zirOverflowArithmetic(block, inst),
+            .shl_with_overflow => try sema.zirOverflowArithmetic(block, inst),
+
             // Instructions that we know to *always* be noreturn based solely on their tag.
             // These functions match the return type of analyzeBody so that we can
             // tail call them here.
@@ -4048,6 +4055,16 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
     return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
 }
 
+fn zirOverflowArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+    const tracy = trace(@src());
+    defer tracy.end();
+
+    const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
+    const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
+
+    return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
+}
+
 fn analyzeArithmetic(
     sema: *Sema,
     block: *Scope.Block,
@@ -4402,6 +4419,12 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
     return sema.mod.constType(sema.arena, src, elem_ty);
 }
 
+fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
+    const inst_data = sema.code.instructions.items(.data)[inst].un_node;
+    const src = inst_data.src();
+    return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
+}
+
 fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
     const tracy = trace(@src());
     defer tracy.end();
src/Zir.zig
@@ -573,6 +573,9 @@ pub const Inst = struct {
         /// of one or more params.
         /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
         typeof_peer,
+        /// Given an integer type, returns the integer type for the RHS of a shift operation.
+        /// Uses the `un_node` field.
+        log2_int_type,
         /// Asserts control-flow will not reach this instruction (`unreachable`).
         /// Uses the `unreachable` union field.
         @"unreachable",
@@ -729,6 +732,14 @@ pub const Inst = struct {
         ret_addr,
         /// Implements the `@src` builtin. Uses `un_node`.
         builtin_src,
+        /// Implements the `@addWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
+        add_with_overflow,
+        /// Implements the `@subWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
+        sub_with_overflow,
+        /// Implements the `@mulWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
+        mul_with_overflow,
+        /// Implements the `@shlWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
+        shl_with_overflow,
 
         /// Returns whether the instruction is one of the control flow "noreturn" types.
         /// Function calls do not count.
@@ -865,6 +876,7 @@ pub const Inst = struct {
                 .slice_sentinel,
                 .import,
                 .typeof_peer,
+                .log2_int_type,
                 .resolve_inferred_alloc,
                 .set_eval_branch_quota,
                 .compile_log,
@@ -900,6 +912,10 @@ pub const Inst = struct {
                 .fence,
                 .ret_addr,
                 .builtin_src,
+                .add_with_overflow,
+                .sub_with_overflow,
+                .mul_with_overflow,
+                .shl_with_overflow,
                 => false,
 
                 .@"break",
@@ -1657,6 +1673,12 @@ pub const Inst = struct {
         name_start: u32,
     };
 
+    pub const OverflowArithmetic = struct {
+        lhs: Ref,
+        rhs: Ref,
+        ptr: Ref,
+    };
+
     /// Trailing: `CompileErrors.Item` for each `items_len`.
     pub const CompileErrors = struct {
         items_len: u32,
@@ -1762,6 +1784,7 @@ const Writer = struct {
             .type_info,
             .size_of,
             .bit_size_of,
+            .log2_int_type,
             => try self.writeUnNode(stream, inst),
 
             .ref,
@@ -1804,6 +1827,12 @@ const Writer = struct {
             .field_type,
             => try self.writePlNode(stream, inst),
 
+            .add_with_overflow,
+            .sub_with_overflow,
+            .mul_with_overflow,
+            .shl_with_overflow,
+            => try self.writePlNodeOverflowArithmetic(stream, inst),
+
             .add,
             .addwrap,
             .array_cat,
@@ -2061,6 +2090,18 @@ const Writer = struct {
         try self.writeSrc(stream, inst_data.src());
     }
 
+    fn writePlNodeOverflowArithmetic(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.OverflowArithmetic, 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.writeInstRef(stream, extra.ptr);
+        try stream.writeAll(") ");
+        try self.writeSrc(stream, inst_data.src());
+    }
+
     fn writePlNodeCall(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.Call, inst_data.payload_index);