authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-17 13:00:10-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2021-04-17 13:00:10-07:00
log5a3045b5de03fade87ccbb4191344861374ea0a4
treee3a30afe0d303c5f3e7a9f8fdcdf32b1045f9b01
parent8cf0ef27790a96e784c368d00338229f205c95d9

AstGen: implement overflow arithmetic builtins


3 files changed, 132 insertions(+), 12 deletions(-)

src/AstGen.zig+60-4
......@@ -1407,6 +1407,11 @@ fn blockExprStmts(
14071407 .fence,
14081408 .ret_addr,
14091409 .builtin_src,
1410 .add_with_overflow,
1411 .sub_with_overflow,
1412 .mul_with_overflow,
1413 .shl_with_overflow,
1414 .log2_int_type,
14101415 => break :b false,
14111416
14121417 // ZIR instructions that are always either `noreturn` or `void`.
......@@ -4910,7 +4915,32 @@ fn builtinCall(
49104915 .return_address => return rvalue(gz, scope, rl, try gz.addNode(.ret_addr, node), node),
49114916 .src => return rvalue(gz, scope, rl, try gz.addNode(.builtin_src, node), node),
49124917
4913 .add_with_overflow,
4918 .add_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .add_with_overflow),
4919 .sub_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .sub_with_overflow),
4920 .mul_with_overflow => return overflowArithmetic(gz, scope, rl, node, params, .mul_with_overflow),
4921 .shl_with_overflow => {
4922 const int_type = try typeExpr(gz, scope, params[0]);
4923 const log2_int_type = try gz.addUnNode(.log2_int_type, int_type, params[0]);
4924 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
4925 .ptr_type_simple = .{
4926 .is_allowzero = false,
4927 .is_mutable = true,
4928 .is_volatile = false,
4929 .size = .One,
4930 .elem_type = int_type,
4931 },
4932 } });
4933 const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
4934 const rhs = try expr(gz, scope, .{ .ty = log2_int_type }, params[2]);
4935 const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
4936 const result = try gz.addPlNode(.shl_with_overflow, node, Zir.Inst.OverflowArithmetic{
4937 .lhs = lhs,
4938 .rhs = rhs,
4939 .ptr = ptr,
4940 });
4941 return rvalue(gz, scope, rl, result, node);
4942 },
4943
49144944 .align_cast,
49154945 .align_of,
49164946 .atomic_load,
......@@ -4948,7 +4978,6 @@ fn builtinCall(
49484978 .wasm_memory_size,
49494979 .wasm_memory_grow,
49504980 .mod,
4951 .mul_with_overflow,
49524981 .panic,
49534982 .pop_count,
49544983 .ptr_cast,
......@@ -4958,7 +4987,6 @@ fn builtinCall(
49584987 .set_float_mode,
49594988 .set_runtime_safety,
49604989 .shl_exact,
4961 .shl_with_overflow,
49624990 .shr_exact,
49634991 .shuffle,
49644992 .splat,
......@@ -4976,7 +5004,6 @@ fn builtinCall(
49765004 .ceil,
49775005 .trunc,
49785006 .round,
4979 .sub_with_overflow,
49805007 .tag_name,
49815008 .truncate,
49825009 .Type,
......@@ -4993,6 +5020,35 @@ fn builtinCall(
49935020 }
49945021}
49955022
5023fn overflowArithmetic(
5024 gz: *GenZir,
5025 scope: *Scope,
5026 rl: ResultLoc,
5027 node: ast.Node.Index,
5028 params: []const ast.Node.Index,
5029 tag: Zir.Inst.Tag,
5030) InnerError!Zir.Inst.Ref {
5031 const int_type = try typeExpr(gz, scope, params[0]);
5032 const ptr_type = try gz.add(.{ .tag = .ptr_type_simple, .data = .{
5033 .ptr_type_simple = .{
5034 .is_allowzero = false,
5035 .is_mutable = true,
5036 .is_volatile = false,
5037 .size = .One,
5038 .elem_type = int_type,
5039 },
5040 } });
5041 const lhs = try expr(gz, scope, .{ .ty = int_type }, params[1]);
5042 const rhs = try expr(gz, scope, .{ .ty = int_type }, params[2]);
5043 const ptr = try expr(gz, scope, .{ .ty = ptr_type }, params[3]);
5044 const result = try gz.addPlNode(tag, node, Zir.Inst.OverflowArithmetic{
5045 .lhs = lhs,
5046 .rhs = rhs,
5047 .ptr = ptr,
5048 });
5049 return rvalue(gz, scope, rl, result, node);
5050}
5051
49965052fn callExpr(
49975053 gz: *GenZir,
49985054 scope: *Scope,
src/Sema.zig+31-8
......@@ -133,8 +133,6 @@ pub fn analyzeBody(
133133 map[inst] = switch (tags[inst]) {
134134 .elided => continue,
135135
136 .add => try sema.zirArithmetic(block, inst),
137 .addwrap => try sema.zirArithmetic(block, inst),
138136 .alloc => try sema.zirAlloc(block, inst),
139137 .alloc_inferred => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_const)),
140138 .alloc_inferred_mut => try sema.zirAllocInferred(block, inst, Type.initTag(.inferred_alloc_mut)),
......@@ -173,7 +171,6 @@ pub fn analyzeBody(
173171 .decl_ref => try sema.zirDeclRef(block, inst),
174172 .decl_val => try sema.zirDeclVal(block, inst),
175173 .load => try sema.zirLoad(block, inst),
176 .div => try sema.zirArithmetic(block, inst),
177174 .elem_ptr => try sema.zirElemPtr(block, inst),
178175 .elem_ptr_node => try sema.zirElemPtrNode(block, inst),
179176 .elem_val => try sema.zirElemVal(block, inst),
......@@ -217,9 +214,6 @@ pub fn analyzeBody(
217214 .is_null_ptr => try sema.zirIsNullPtr(block, inst, false),
218215 .loop => try sema.zirLoop(block, inst),
219216 .merge_error_sets => try sema.zirMergeErrorSets(block, inst),
220 .mod_rem => try sema.zirArithmetic(block, inst),
221 .mul => try sema.zirArithmetic(block, inst),
222 .mulwrap => try sema.zirArithmetic(block, inst),
223217 .negate => try sema.zirNegate(block, inst, .sub),
224218 .negate_wrap => try sema.zirNegate(block, inst, .subwrap),
225219 .optional_payload_safe => try sema.zirOptionalPayload(block, inst, true),
......@@ -241,8 +235,6 @@ pub fn analyzeBody(
241235 .slice_sentinel => try sema.zirSliceSentinel(block, inst),
242236 .slice_start => try sema.zirSliceStart(block, inst),
243237 .str => try sema.zirStr(block, inst),
244 .sub => try sema.zirArithmetic(block, inst),
245 .subwrap => try sema.zirArithmetic(block, inst),
246238 .switch_block => try sema.zirSwitchBlock(block, inst, false, .none),
247239 .switch_block_multi => try sema.zirSwitchBlockMulti(block, inst, false, .none),
248240 .switch_block_else => try sema.zirSwitchBlock(block, inst, false, .@"else"),
......@@ -271,6 +263,7 @@ pub fn analyzeBody(
271263 .typeof => try sema.zirTypeof(block, inst),
272264 .typeof_elem => try sema.zirTypeofElem(block, inst),
273265 .typeof_peer => try sema.zirTypeofPeer(block, inst),
266 .log2_int_type => try sema.zirLog2IntType(block, inst),
274267 .xor => try sema.zirBitwise(block, inst, .xor),
275268 .struct_init_empty => try sema.zirStructInitEmpty(block, inst),
276269 .struct_init => try sema.zirStructInit(block, inst),
......@@ -284,6 +277,20 @@ pub fn analyzeBody(
284277 .union_decl => try sema.zirUnionDecl(block, inst),
285278 .opaque_decl => try sema.zirOpaqueDecl(block, inst),
286279
280 .add => try sema.zirArithmetic(block, inst),
281 .addwrap => try sema.zirArithmetic(block, inst),
282 .div => try sema.zirArithmetic(block, inst),
283 .mod_rem => try sema.zirArithmetic(block, inst),
284 .mul => try sema.zirArithmetic(block, inst),
285 .mulwrap => try sema.zirArithmetic(block, inst),
286 .sub => try sema.zirArithmetic(block, inst),
287 .subwrap => try sema.zirArithmetic(block, inst),
288
289 .add_with_overflow => try sema.zirOverflowArithmetic(block, inst),
290 .sub_with_overflow => try sema.zirOverflowArithmetic(block, inst),
291 .mul_with_overflow => try sema.zirOverflowArithmetic(block, inst),
292 .shl_with_overflow => try sema.zirOverflowArithmetic(block, inst),
293
287294 // Instructions that we know to *always* be noreturn based solely on their tag.
288295 // These functions match the return type of analyzeBody so that we can
289296 // tail call them here.
......@@ -4048,6 +4055,16 @@ fn zirArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
40484055 return sema.analyzeArithmetic(block, tag_override, lhs, rhs, src, lhs_src, rhs_src);
40494056}
40504057
4058fn zirOverflowArithmetic(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4059 const tracy = trace(@src());
4060 defer tracy.end();
4061
4062 const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
4063 const src: LazySrcLoc = .{ .node_offset_bin_op = inst_data.src_node };
4064
4065 return sema.mod.fail(&block.base, src, "TODO implement Sema.zirOverflowArithmetic", .{});
4066}
4067
40514068fn analyzeArithmetic(
40524069 sema: *Sema,
40534070 block: *Scope.Block,
......@@ -4402,6 +4419,12 @@ fn zirTypeofElem(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerEr
44024419 return sema.mod.constType(sema.arena, src, elem_ty);
44034420}
44044421
4422fn zirLog2IntType(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
4423 const inst_data = sema.code.instructions.items(.data)[inst].un_node;
4424 const src = inst_data.src();
4425 return sema.mod.fail(&block.base, src, "TODO: implement Sema.zirLog2IntType", .{});
4426}
4427
44054428fn zirTypeofPeer(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst {
44064429 const tracy = trace(@src());
44074430 defer tracy.end();
src/Zir.zig+41
......@@ -573,6 +573,9 @@ pub const Inst = struct {
573573 /// of one or more params.
574574 /// Uses the `pl_node` field. AST node is the `@TypeOf` call. Payload is `MultiOp`.
575575 typeof_peer,
576 /// Given an integer type, returns the integer type for the RHS of a shift operation.
577 /// Uses the `un_node` field.
578 log2_int_type,
576579 /// Asserts control-flow will not reach this instruction (`unreachable`).
577580 /// Uses the `unreachable` union field.
578581 @"unreachable",
......@@ -729,6 +732,14 @@ pub const Inst = struct {
729732 ret_addr,
730733 /// Implements the `@src` builtin. Uses `un_node`.
731734 builtin_src,
735 /// Implements the `@addWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
736 add_with_overflow,
737 /// Implements the `@subWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
738 sub_with_overflow,
739 /// Implements the `@mulWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
740 mul_with_overflow,
741 /// Implements the `@shlWithOverflow` builtin. Uses `pl_node` with `OverflowArithmetic`.
742 shl_with_overflow,
732743
733744 /// Returns whether the instruction is one of the control flow "noreturn" types.
734745 /// Function calls do not count.
......@@ -865,6 +876,7 @@ pub const Inst = struct {
865876 .slice_sentinel,
866877 .import,
867878 .typeof_peer,
879 .log2_int_type,
868880 .resolve_inferred_alloc,
869881 .set_eval_branch_quota,
870882 .compile_log,
......@@ -900,6 +912,10 @@ pub const Inst = struct {
900912 .fence,
901913 .ret_addr,
902914 .builtin_src,
915 .add_with_overflow,
916 .sub_with_overflow,
917 .mul_with_overflow,
918 .shl_with_overflow,
903919 => false,
904920
905921 .@"break",
......@@ -1657,6 +1673,12 @@ pub const Inst = struct {
16571673 name_start: u32,
16581674 };
16591675
1676 pub const OverflowArithmetic = struct {
1677 lhs: Ref,
1678 rhs: Ref,
1679 ptr: Ref,
1680 };
1681
16601682 /// Trailing: `CompileErrors.Item` for each `items_len`.
16611683 pub const CompileErrors = struct {
16621684 items_len: u32,
......@@ -1762,6 +1784,7 @@ const Writer = struct {
17621784 .type_info,
17631785 .size_of,
17641786 .bit_size_of,
1787 .log2_int_type,
17651788 => try self.writeUnNode(stream, inst),
17661789
17671790 .ref,
......@@ -1804,6 +1827,12 @@ const Writer = struct {
18041827 .field_type,
18051828 => try self.writePlNode(stream, inst),
18061829
1830 .add_with_overflow,
1831 .sub_with_overflow,
1832 .mul_with_overflow,
1833 .shl_with_overflow,
1834 => try self.writePlNodeOverflowArithmetic(stream, inst),
1835
18071836 .add,
18081837 .addwrap,
18091838 .array_cat,
......@@ -2061,6 +2090,18 @@ const Writer = struct {
20612090 try self.writeSrc(stream, inst_data.src());
20622091 }
20632092
2093 fn writePlNodeOverflowArithmetic(self: *Writer, stream: anytype, inst: Inst.Index) !void {
2094 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
2095 const extra = self.code.extraData(Inst.OverflowArithmetic, inst_data.payload_index).data;
2096 try self.writeInstRef(stream, extra.lhs);
2097 try stream.writeAll(", ");
2098 try self.writeInstRef(stream, extra.rhs);
2099 try stream.writeAll(", ");
2100 try self.writeInstRef(stream, extra.ptr);
2101 try stream.writeAll(") ");
2102 try self.writeSrc(stream, inst_data.src());
2103 }
2104
20642105 fn writePlNodeCall(self: *Writer, stream: anytype, inst: Inst.Index) !void {
20652106 const inst_data = self.code.instructions.items(.data)[inst].pl_node;
20662107 const extra = self.code.extraData(Inst.Call, inst_data.payload_index);