From 5769c963e0748088ba2636e870cbc6f887d10454 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Sun, 21 Mar 2021 19:23:12 -0700 Subject: [PATCH] Sema: implement arithmetic --- src/Module.zig | 26 ++++++++++++ src/Sema.zig | 105 ++++++++++++++++++++++--------------------------- src/astgen.zig | 29 +++++++------- src/zir.zig | 55 ++++++++++++++++---------- 4 files changed, 121 insertions(+), 94 deletions(-) diff --git a/src/Module.zig b/src/Module.zig index d21d62d0e8..c89ffadcc1 100644 --- a/src/Module.zig +++ b/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, diff --git a/src/Sema.zig b/src/Sema.zig index 478b29d03b..22defda67b 100644 --- a/src/Sema.zig +++ b/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(); diff --git a/src/astgen.zig b/src/astgen.zig index f471bbb5de..d33e5b0db5 100644 --- a/src/astgen.zig +++ b/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; diff --git a/src/zir.zig b/src/zir.zig index 08750207cc..ed3ad8fd2b 100644 --- a/src/zir.zig +++ b/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;