diff options
| author | Andrew Kelley <andrew@ziglang.org> | 2021-04-17 13:00:10 -0700 |
|---|---|---|
| committer | Andrew Kelley <andrew@ziglang.org> | 2021-04-17 13:00:10 -0700 |
| commit | 5a3045b5de03fade87ccbb4191344861374ea0a4 (patch) | |
| tree | e3a30afe0d303c5f3e7a9f8fdcdf32b1045f9b01 /src | |
| parent | 8cf0ef27790a96e784c368d00338229f205c95d9 (diff) | |
| download | zig-5a3045b5de03fade87ccbb4191344861374ea0a4.tar.gz zig-5a3045b5de03fade87ccbb4191344861374ea0a4.zip | |
AstGen: implement overflow arithmetic builtins
Diffstat (limited to 'src')
| -rw-r--r-- | src/AstGen.zig | 64 | ||||
| -rw-r--r-- | src/Sema.zig | 39 | ||||
| -rw-r--r-- | src/Zir.zig | 41 |
3 files changed, 132 insertions, 12 deletions
diff --git a/src/AstGen.zig b/src/AstGen.zig index 6b4dfc187b..68e8fcc42e 100644 --- a/src/AstGen.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index f98882b0b0..6fa6174804 100644 --- a/src/Sema.zig +++ b/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(); diff --git a/src/Zir.zig b/src/Zir.zig index 066be22be0..879f8d2459 100644 --- a/src/Zir.zig +++ b/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); |
