From 5816997ae79c6025d5f85aab0c9ab456fecadec9 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 14:17:04 -0700 Subject: stage2: get tests passing * implement enough of ret_err_value to pass wasm tests * only do the proper `@panic` implementation for the backends which support it, which is currently only the C backend. The other backends will see `@breakpoint(); unreachable;` same as before. - I plan to do AIR memory layout reworking as a prerequisite to fixing other backends, because that will help me put all the constants up front, which will allow the codegen to lower to memory without jumps. * `@panic` is implemented using anon decls for the message. Makes it easier on the backends. Might want to look into re-using decls for this in the future. * implement DWARF .debug_info for pointer-like optionals. --- src/codegen.zig | 214 ++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 129 insertions(+), 85 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/codegen.zig b/src/codegen.zig index 0b63222242..f9f5a861fb 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -142,40 +142,52 @@ pub fn generateSymbol( ), }; }, - .Pointer => { - // TODO populate .debug_info for the pointer - if (typed_value.val.castTag(.decl_ref)) |payload| { - const decl = payload.data; - if (decl.analysis != .complete) return error.AnalysisFail; - // TODO handle the dependency of this symbol on the decl's vaddr. - // If the decl changes vaddr, then this symbol needs to get regenerated. - const vaddr = bin_file.getDeclVAddr(decl); - const endian = bin_file.options.target.cpu.arch.endian(); - switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { - 16 => { - try code.resize(2); - mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); - }, - 32 => { - try code.resize(4); - mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); - }, - 64 => { - try code.resize(8); - mem.writeInt(u64, code.items[0..8], vaddr, endian); - }, - else => unreachable, + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => { + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement generateSymbol for slice {}", + .{typed_value.val}, + ), + }; + }, + else => { + // TODO populate .debug_info for the pointer + if (typed_value.val.castTag(.decl_ref)) |payload| { + const decl = payload.data; + if (decl.analysis != .complete) return error.AnalysisFail; + // TODO handle the dependency of this symbol on the decl's vaddr. + // If the decl changes vaddr, then this symbol needs to get regenerated. + const vaddr = bin_file.getDeclVAddr(decl); + const endian = bin_file.options.target.cpu.arch.endian(); + switch (bin_file.options.target.cpu.arch.ptrBitWidth()) { + 16 => { + try code.resize(2); + mem.writeInt(u16, code.items[0..2], @intCast(u16, vaddr), endian); + }, + 32 => { + try code.resize(4); + mem.writeInt(u32, code.items[0..4], @intCast(u32, vaddr), endian); + }, + 64 => { + try code.resize(8); + mem.writeInt(u64, code.items[0..8], vaddr, endian); + }, + else => unreachable, + } + return Result{ .appended = {} }; } - return Result{ .appended = {} }; - } - return Result{ - .fail = try ErrorMsg.create( - bin_file.allocator, - src_loc, - "TODO implement generateSymbol for pointer {}", - .{typed_value.val}, - ), - }; + return Result{ + .fail = try ErrorMsg.create( + bin_file.allocator, + src_loc, + "TODO implement generateSymbol for pointer {}", + .{typed_value.val}, + ), + }; + }, }, .Int => { // TODO populate .debug_info for the integer @@ -2244,10 +2256,10 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { try self.register_manager.getReg(reg, null); try self.genSetReg(arg.src, arg.ty, reg, arg_mcv); }, - .stack_offset => { + .stack_offset => |off| { // Here we need to emit instructions like this: // mov qword ptr [rsp + stack_offset], x - return self.fail(inst.base.src, "TODO implement calling with parameters in memory", .{}); + try self.genSetStack(arg.src, arg.ty, off, arg_mcv); }, .ptr_stack_offset => { return self.fail(inst.base.src, "TODO implement calling with MCValue.ptr_stack_offset arg", .{}); @@ -3444,9 +3456,11 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { }, } }, - .embedded_in_code => |code_offset| { - _ = code_offset; - return self.fail(src, "TODO implement set stack variable from embedded_in_code", .{}); + .embedded_in_code => { + // TODO this and `.stack_offset` below need to get improved to support types greater than + // register size, and do general memcpy + const reg = try self.copyToTmpRegister(src, ty, mcv); + return self.genSetStack(src, ty, stack_offset, MCValue{ .register = reg }); }, .register => |reg| { try self.genX8664ModRMRegToStack(src, ty, stack_offset, reg, 0x89); @@ -3456,6 +3470,9 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(src, "TODO implement set stack variable from memory vaddr", .{}); }, .stack_offset => |off| { + // TODO this and `.embedded_in_code` above need to get improved to support types greater than + // register size, and do general memcpy + if (stack_offset == off) return; // Copy stack variable to itself; nothing to do. @@ -4161,33 +4178,48 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { const ptr_bits = self.target.cpu.arch.ptrBitWidth(); const ptr_bytes: u64 = @divExact(ptr_bits, 8); switch (typed_value.ty.zigTypeTag()) { - .Pointer => { - if (typed_value.val.castTag(.decl_ref)) |payload| { - if (self.bin_file.cast(link.File.Elf)) |elf_file| { - const decl = payload.data; - const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; - const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { - const decl = payload.data; - const got_addr = blk: { - const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; - const got = seg.sections.items[macho_file.got_section_index.?]; - break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; - }; - return MCValue{ .memory = got_addr }; - } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { - const decl = payload.data; - const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; - return MCValue{ .memory = got_addr }; - } else { - return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); + .Pointer => switch (typed_value.ty.ptrSize()) { + .Slice => { + var buf: Type.Payload.ElemType = undefined; + const ptr_type = typed_value.ty.slicePtrFieldType(&buf); + const ptr_mcv = try self.genTypedValue(src, .{ .ty = ptr_type, .val = typed_value.val }); + const slice_len = typed_value.val.sliceLen(); + // Codegen can't handle some kinds of indirection. If the wrong union field is accessed here it may mean + // the Sema code needs to use anonymous Decls or alloca instructions to store data. + const ptr_imm = ptr_mcv.memory; + _ = slice_len; + _ = ptr_imm; + // We need more general support for const data being stored in memory to make this work. + return self.fail(src, "TODO codegen for const slices", .{}); + }, + else => { + if (typed_value.val.castTag(.decl_ref)) |payload| { + if (self.bin_file.cast(link.File.Elf)) |elf_file| { + const decl = payload.data; + const got = &elf_file.program_headers.items[elf_file.phdr_got_index.?]; + const got_addr = got.p_vaddr + decl.link.elf.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.MachO)) |macho_file| { + const decl = payload.data; + const got_addr = blk: { + const seg = macho_file.load_commands.items[macho_file.data_const_segment_cmd_index.?].Segment; + const got = seg.sections.items[macho_file.got_section_index.?]; + break :blk got.addr + decl.link.macho.offset_table_index * ptr_bytes; + }; + return MCValue{ .memory = got_addr }; + } else if (self.bin_file.cast(link.File.Coff)) |coff_file| { + const decl = payload.data; + const got_addr = coff_file.offset_table_virtual_address + decl.link.coff.offset_table_index * ptr_bytes; + return MCValue{ .memory = got_addr }; + } else { + return self.fail(src, "TODO codegen non-ELF const Decl pointer", .{}); + } } - } - if (typed_value.val.tag() == .int_u64) { - return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; - } - return self.fail(src, "TODO codegen more kinds of const pointers", .{}); + if (typed_value.val.tag() == .int_u64) { + return MCValue{ .immediate = typed_value.val.toUnsignedInt() }; + } + return self.fail(src, "TODO codegen more kinds of const pointers", .{}); + }, }, .Int => { const info = typed_value.ty.intInfo(self.target.*); @@ -4264,27 +4296,39 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { var next_stack_offset: u32 = 0; for (param_types) |ty, i| { - switch (ty.zigTypeTag()) { - .Bool, .Int => { - if (!ty.hasCodeGenBits()) { - assert(cc != .C); - result.args[i] = .{ .none = {} }; - } else { - const param_size = @intCast(u32, ty.abiSize(self.target.*)); - if (next_int_reg >= c_abi_int_param_regs.len) { - result.args[i] = .{ .stack_offset = next_stack_offset }; - next_stack_offset += param_size; - } else { - const aliased_reg = registerAlias( - c_abi_int_param_regs[next_int_reg], - param_size, - ); - result.args[i] = .{ .register = aliased_reg }; - next_int_reg += 1; - } - } - }, - else => return self.fail(src, "TODO implement function parameters of type {s}", .{@tagName(ty.zigTypeTag())}), + if (!ty.hasCodeGenBits()) { + assert(cc != .C); + result.args[i] = .{ .none = {} }; + continue; + } + const param_size = @intCast(u32, ty.abiSize(self.target.*)); + const pass_in_reg = switch (ty.zigTypeTag()) { + .Bool => true, + .Int => param_size <= 8, + .Pointer => ty.ptrSize() != .Slice, + .Optional => ty.isPtrLikeOptional(), + else => false, + }; + if (pass_in_reg) { + if (next_int_reg >= c_abi_int_param_regs.len) { + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; + } else { + const aliased_reg = registerAlias( + c_abi_int_param_regs[next_int_reg], + param_size, + ); + result.args[i] = .{ .register = aliased_reg }; + next_int_reg += 1; + } + } else { + // For simplicity of codegen, slices and other types are always pushed onto the stack. + // TODO: look into optimizing this by passing things as registers sometimes, + // such as ptr and len of slices as separate registers. + // TODO: also we need to honor the C ABI for relevant types rather than passing on + // the stack here. + result.args[i] = .{ .stack_offset = next_stack_offset }; + next_stack_offset += param_size; } } result.stack_byte_count = next_stack_offset; -- cgit v1.2.3 From 5c8bd443d92c6306f60857720103ae46ca7b8b3e Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Wed, 7 Jul 2021 19:50:56 -0700 Subject: stage2: fix if expressions on error unions AstGen had the then-else logic backwards for if expressions on error unions. This commit fixes it. Turns out AstGen only really needs `is_non_null` and `is_non_err`, and does not need the `is_null` or `is_err` variants. So I removed the `is_null{,_ptr}` and `is_err{,_ptr}` ZIR instructions (-4) and added `is_non_err`, `is_non_err_ptr` ZIR instructions (+2) for a total of (-2) ZIR instructions, giving us a tiny bit more headroom within the 256 tag limit. This required swapping the order of then/else blocks in a handful of cases, but ultimately means the ZIR will be in the same as source order, which is convenient when debugging. AIR code on the other hand, gains the `is_non_err` and `is_non_err_ptr` instructions. Sema: fix logic in zirErrUnionCode and zirErrUnionCodePtr returning the wrong result type. --- src/AstGen.zig | 92 ++++++++++++++++++++++++++-------------------------- src/Sema.zig | 55 +++++++++++++++++-------------- src/Zir.zig | 32 ++++++------------ src/air.zig | 12 ++++++- src/codegen.zig | 12 +++++++ src/codegen/c.zig | 11 ++++--- src/codegen/wasm.zig | 9 +++-- 7 files changed, 119 insertions(+), 104 deletions(-) (limited to 'src/codegen.zig') diff --git a/src/AstGen.zig b/src/AstGen.zig index f7fea3a99c..a816628f66 100644 --- a/src/AstGen.zig +++ b/src/AstGen.zig @@ -786,7 +786,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_err_ptr, + .is_non_err_ptr, .err_union_payload_unsafe_ptr, .err_union_code_ptr, node_datas[node].rhs, @@ -798,7 +798,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_err, + .is_non_err, .err_union_payload_unsafe, .err_union_code, node_datas[node].rhs, @@ -813,7 +813,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_null_ptr, + .is_non_null_ptr, .optional_payload_unsafe_ptr, undefined, node_datas[node].rhs, @@ -825,7 +825,7 @@ fn expr(gz: *GenZir, scope: *Scope, rl: ResultLoc, node: ast.Node.Index) InnerEr rl, node, node_datas[node].lhs, - .is_null, + .is_non_null, .optional_payload_unsafe, undefined, node_datas[node].rhs, @@ -1948,11 +1948,9 @@ fn unusedResultExpr(gz: *GenZir, scope: *Scope, statement: ast.Node.Index) Inner .float128, .int_type, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .mod_rem, .mul, .mulwrap, @@ -4621,8 +4619,8 @@ fn tryExpr( }; const err_ops = switch (rl) { // zig fmt: off - .ref => [3]Zir.Inst.Tag{ .is_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, - else => [3]Zir.Inst.Tag{ .is_err, .err_union_code, .err_union_payload_unsafe }, + .ref => [3]Zir.Inst.Tag{ .is_non_err_ptr, .err_union_code_ptr, .err_union_payload_unsafe_ptr }, + else => [3]Zir.Inst.Tag{ .is_non_err, .err_union_code, .err_union_payload_unsafe }, // zig fmt: on }; // This could be a pointer or value depending on the `operand_rl` parameter. @@ -4640,21 +4638,21 @@ fn tryExpr( var then_scope = parent_gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); - const err_code = try then_scope.addUnNode(err_ops[1], operand, node); - try genDefers(&then_scope, &fn_block.base, scope, .{ .both = err_code }); - const then_result = try then_scope.addUnNode(.ret_node, err_code, node); - - var else_scope = parent_gz.makeSubBlock(scope); - defer else_scope.instructions.deinit(astgen.gpa); - block_scope.break_count += 1; // This could be a pointer or value depending on `err_ops[2]`. - const unwrapped_payload = try else_scope.addUnNode(err_ops[2], operand, node); - const else_result = switch (rl) { + const unwrapped_payload = try then_scope.addUnNode(err_ops[2], operand, node); + const then_result = switch (rl) { .ref => unwrapped_payload, - else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), + else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), }; + var else_scope = parent_gz.makeSubBlock(scope); + defer else_scope.instructions.deinit(astgen.gpa); + + const err_code = try else_scope.addUnNode(err_ops[1], operand, node); + try genDefers(&else_scope, &fn_block.base, scope, .{ .both = err_code }); + const else_result = try else_scope.addUnNode(.ret_node, err_code, node); + return finishThenElseBlock( parent_gz, rl, @@ -4711,18 +4709,28 @@ fn orelseCatchExpr( var then_scope = parent_gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); + // This could be a pointer or value depending on `unwrap_op`. + const unwrapped_payload = try then_scope.addUnNode(unwrap_op, operand, node); + const then_result = switch (rl) { + .ref => unwrapped_payload, + else => try rvalue(&then_scope, block_scope.break_result_loc, unwrapped_payload, node), + }; + + var else_scope = parent_gz.makeSubBlock(scope); + defer else_scope.instructions.deinit(astgen.gpa); + var err_val_scope: Scope.LocalVal = undefined; - const then_sub_scope = blk: { - const payload = payload_token orelse break :blk &then_scope.base; + const else_sub_scope = blk: { + const payload = payload_token orelse break :blk &else_scope.base; if (mem.eql(u8, tree.tokenSlice(payload), "_")) { return astgen.failTok(payload, "discard of error capture; omit it instead", .{}); } const err_name = try astgen.identAsString(payload); err_val_scope = .{ - .parent = &then_scope.base, - .gen_zir = &then_scope, + .parent = &else_scope.base, + .gen_zir = &else_scope, .name = err_name, - .inst = try then_scope.addUnNode(unwrap_code_op, operand, node), + .inst = try else_scope.addUnNode(unwrap_code_op, operand, node), .token_src = payload, .id_cat = .@"capture", }; @@ -4730,23 +4738,13 @@ fn orelseCatchExpr( }; block_scope.break_count += 1; - const then_result = try expr(&then_scope, then_sub_scope, block_scope.break_result_loc, rhs); - try checkUsed(parent_gz, &then_scope.base, then_sub_scope); + const else_result = try expr(&else_scope, else_sub_scope, block_scope.break_result_loc, rhs); + try checkUsed(parent_gz, &else_scope.base, else_sub_scope); // We hold off on the break instructions as well as copying the then/else // instructions into place until we know whether to keep store_to_block_ptr // instructions or not. - var else_scope = parent_gz.makeSubBlock(scope); - defer else_scope.instructions.deinit(astgen.gpa); - - // This could be a pointer or value depending on `unwrap_op`. - const unwrapped_payload = try else_scope.addUnNode(unwrap_op, operand, node); - const else_result = switch (rl) { - .ref => unwrapped_payload, - else => try rvalue(&else_scope, block_scope.break_result_loc, unwrapped_payload, node), - }; - return finishThenElseBlock( parent_gz, rl, @@ -4964,7 +4962,7 @@ fn ifExpr( if (if_full.error_token) |_| { const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; const err_union = try expr(&block_scope, &block_scope.base, cond_rl, if_full.ast.cond_expr); - const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; + const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; break :c .{ .inst = err_union, .bool_bit = try block_scope.addUnNode(tag, err_union, node), @@ -5221,7 +5219,7 @@ fn whileExpr( if (while_full.error_token) |_| { const cond_rl: ResultLoc = if (payload_is_ref) .ref else .none; const err_union = try expr(&continue_scope, &continue_scope.base, cond_rl, while_full.ast.cond_expr); - const tag: Zir.Inst.Tag = if (payload_is_ref) .is_err_ptr else .is_err; + const tag: Zir.Inst.Tag = if (payload_is_ref) .is_non_err_ptr else .is_non_err; break :c .{ .inst = err_union, .bool_bit = try continue_scope.addUnNode(tag, err_union, node), @@ -6229,23 +6227,25 @@ fn ret(gz: *GenZir, scope: *Scope, node: ast.Node.Index) InnerError!Zir.Inst.Ref } // Emit conditional branch for generating errdefers. - const is_err = try gz.addUnNode(.is_err, operand, node); + const is_non_err = try gz.addUnNode(.is_non_err, operand, node); const condbr = try gz.addCondBr(.condbr, node); var then_scope = gz.makeSubBlock(scope); defer then_scope.instructions.deinit(astgen.gpa); - const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ - .both = try then_scope.addUnNode(.err_union_code, operand, node), - }; - try genDefers(&then_scope, defer_outer, scope, which_ones); + + try genDefers(&then_scope, defer_outer, scope, .normal_only); _ = try then_scope.addUnNode(.ret_node, operand, node); var else_scope = gz.makeSubBlock(scope); defer else_scope.instructions.deinit(astgen.gpa); - try genDefers(&else_scope, defer_outer, scope, .normal_only); + + const which_ones: DefersToEmit = if (!defer_counts.need_err_code) .both_sans_err else .{ + .both = try else_scope.addUnNode(.err_union_code, operand, node), + }; + try genDefers(&else_scope, defer_outer, scope, which_ones); _ = try else_scope.addUnNode(.ret_node, operand, node); - try setCondBrPayload(condbr, is_err, &then_scope, &else_scope); + try setCondBrPayload(condbr, is_non_err, &then_scope, &else_scope); return Zir.Inst.Ref.unreachable_value; }, diff --git a/src/Sema.zig b/src/Sema.zig index 24c51bdc46..86e5f59af6 100644 --- a/src/Sema.zig +++ b/src/Sema.zig @@ -225,12 +225,10 @@ pub fn analyzeBody( .float => try sema.zirFloat(block, inst), .float128 => try sema.zirFloat128(block, inst), .int_type => try sema.zirIntType(block, inst), - .is_err => try sema.zirIsErr(block, inst), - .is_err_ptr => try sema.zirIsErrPtr(block, inst), - .is_non_null => try sema.zirIsNull(block, inst, true), - .is_non_null_ptr => try sema.zirIsNullPtr(block, inst, true), - .is_null => try sema.zirIsNull(block, inst, false), - .is_null_ptr => try sema.zirIsNullPtr(block, inst, false), + .is_non_err => try sema.zirIsNonErr(block, inst), + .is_non_err_ptr => try sema.zirIsNonErrPtr(block, inst), + .is_non_null => try sema.zirIsNonNull(block, inst), + .is_non_null_ptr => try sema.zirIsNonNullPtr(block, inst), .loop => try sema.zirLoop(block, inst), .merge_error_sets => try sema.zirMergeErrorSets(block, inst), .negate => try sema.zirNegate(block, inst, .sub), @@ -2981,17 +2979,19 @@ fn zirErrUnionCode(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) Inner if (operand.ty.zigTypeTag() != .ErrorUnion) return sema.mod.fail(&block.base, src, "expected error union type, found '{}'", .{operand.ty}); + const result_ty = operand.ty.castTag(.error_union).?.data.error_set; + if (operand.value()) |val| { assert(val.getError() != null); const data = val.castTag(.error_union).?.data; return sema.mod.constInst(sema.arena, src, .{ - .ty = operand.ty.castTag(.error_union).?.data.error_set, + .ty = result_ty, .val = data, }); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err, operand); + return block.addUnOp(src, result_ty, .unwrap_errunion_err, operand); } /// Pointer in, value out @@ -3007,18 +3007,20 @@ fn zirErrUnionCodePtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) In if (operand.ty.elemType().zigTypeTag() != .ErrorUnion) return sema.mod.fail(&block.base, src, "expected error union type, found {}", .{operand.ty.elemType()}); + const result_ty = operand.ty.elemType().castTag(.error_union).?.data.error_set; + if (operand.value()) |pointer_val| { const val = try pointer_val.pointerDeref(sema.arena); assert(val.getError() != null); const data = val.castTag(.error_union).?.data; return sema.mod.constInst(sema.arena, src, .{ - .ty = operand.ty.elemType().castTag(.error_union).?.data.error_set, + .ty = result_ty, .val = data, }); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, operand.ty.castTag(.error_union).?.data.payload, .unwrap_errunion_err_ptr, operand); + return block.addUnOp(src, result_ty, .unwrap_errunion_err_ptr, operand); } fn zirEnsureErrPayloadVoid(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!void { @@ -5298,11 +5300,10 @@ fn zirBoolBr( return &block_inst.base; } -fn zirIsNull( +fn zirIsNonNull( sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, - invert_logic: bool, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5310,14 +5311,13 @@ fn zirIsNull( const inst_data = sema.code.instructions.items(.data)[inst].un_node; const src = inst_data.src(); const operand = try sema.resolveInst(inst_data.operand); - return sema.analyzeIsNull(block, src, operand, invert_logic); + return sema.analyzeIsNull(block, src, operand, true); } -fn zirIsNullPtr( +fn zirIsNonNullPtr( sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index, - invert_logic: bool, ) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5326,19 +5326,19 @@ fn zirIsNullPtr( const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); const loaded = try sema.analyzeLoad(block, src, ptr, src); - return sema.analyzeIsNull(block, src, loaded, invert_logic); + return sema.analyzeIsNull(block, src, loaded, true); } -fn zirIsErr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirIsNonErr(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].un_node; const operand = try sema.resolveInst(inst_data.operand); - return sema.analyzeIsErr(block, inst_data.src(), operand); + return sema.analyzeIsNonErr(block, inst_data.src(), operand); } -fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { +fn zirIsNonErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerError!*Inst { const tracy = trace(@src()); defer tracy.end(); @@ -5346,7 +5346,7 @@ fn zirIsErrPtr(sema: *Sema, block: *Scope.Block, inst: Zir.Inst.Index) InnerErro const src = inst_data.src(); const ptr = try sema.resolveInst(inst_data.operand); const loaded = try sema.analyzeLoad(block, src, ptr, src); - return sema.analyzeIsErr(block, src, loaded); + return sema.analyzeIsNonErr(block, src, loaded); } fn zirCondbr( @@ -7219,20 +7219,25 @@ fn analyzeIsNull( return block.addUnOp(src, result_ty, inst_tag, operand); } -fn analyzeIsErr(sema: *Sema, block: *Scope.Block, src: LazySrcLoc, operand: *Inst) InnerError!*Inst { +fn analyzeIsNonErr( + sema: *Sema, + block: *Scope.Block, + src: LazySrcLoc, + operand: *Inst, +) InnerError!*Inst { const ot = operand.ty.zigTypeTag(); - if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, false); - if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, true); + if (ot != .ErrorSet and ot != .ErrorUnion) return sema.mod.constBool(sema.arena, src, true); + if (ot == .ErrorSet) return sema.mod.constBool(sema.arena, src, false); assert(ot == .ErrorUnion); const result_ty = Type.initTag(.bool); if (try sema.resolvePossiblyUndefinedValue(block, src, operand)) |err_union| { if (err_union.isUndef()) { return sema.mod.constUndef(sema.arena, src, result_ty); } - return sema.mod.constBool(sema.arena, src, err_union.getError() != null); + return sema.mod.constBool(sema.arena, src, err_union.getError() == null); } try sema.requireRuntimeBlock(block, src); - return block.addUnOp(src, result_ty, .is_err, operand); + return block.addUnOp(src, result_ty, .is_non_err, operand); } fn analyzeSlice( diff --git a/src/Zir.zig b/src/Zir.zig index a80660b5bf..77b71f6caf 100644 --- a/src/Zir.zig +++ b/src/Zir.zig @@ -398,21 +398,15 @@ pub const Inst = struct { /// Return a boolean false if an optional is null. `x != null` /// Uses the `un_node` field. is_non_null, - /// Return a boolean true if an optional is null. `x == null` - /// Uses the `un_node` field. - is_null, /// Return a boolean false if an optional is null. `x.* != null` /// Uses the `un_node` field. is_non_null_ptr, - /// Return a boolean true if an optional is null. `x.* == null` - /// Uses the `un_node` field. - is_null_ptr, - /// Return a boolean true if value is an error + /// Return a boolean false if value is an error /// Uses the `un_node` field. - is_err, - /// Return a boolean true if dereferenced pointer is an error + is_non_err, + /// Return a boolean false if dereferenced pointer is an error /// Uses the `un_node` field. - is_err_ptr, + is_non_err_ptr, /// A labeled block of code that loops forever. At the end of the body will have either /// a `repeat` instruction or a `repeat_inline` instruction. /// Uses the `pl_node` field. The AST node is either a for loop or while loop. @@ -1046,11 +1040,9 @@ pub const Inst = struct { .float128, .int_type, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .mod_rem, .mul, .mulwrap, @@ -1306,11 +1298,9 @@ pub const Inst = struct { .float128 = .pl_node, .int_type = .int_type, .is_non_null = .un_node, - .is_null = .un_node, .is_non_null_ptr = .un_node, - .is_null_ptr = .un_node, - .is_err = .un_node, - .is_err_ptr = .un_node, + .is_non_err = .un_node, + .is_non_err_ptr = .un_node, .loop = .pl_node, .repeat = .node, .repeat_inline = .node, @@ -2857,11 +2847,9 @@ const Writer = struct { .err_union_code, .err_union_code_ptr, .is_non_null, - .is_null, .is_non_null_ptr, - .is_null_ptr, - .is_err, - .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .typeof, .typeof_elem, .struct_init_empty, diff --git a/src/air.zig b/src/air.zig index 1b7faa641b..e73367945b 100644 --- a/src/air.zig +++ b/src/air.zig @@ -90,8 +90,12 @@ pub const Inst = struct { is_non_null_ptr, /// E!T => bool is_err, + /// E!T => bool (inverted logic) + is_non_err, /// *E!T => bool is_err_ptr, + /// *E!T => bool (inverted logic) + is_non_err_ptr, bool_and, bool_or, /// Read a value from a pointer. @@ -154,7 +158,9 @@ pub const Inst = struct { .is_null, .is_null_ptr, .is_err, + .is_non_err, .is_err_ptr, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, @@ -759,7 +765,9 @@ const DumpAir = struct { .is_null, .is_null_ptr, .is_err, + .is_non_err, .is_err_ptr, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, @@ -888,11 +896,13 @@ const DumpAir = struct { .bitcast, .not, .is_non_null, - .is_null, .is_non_null_ptr, + .is_null, .is_null_ptr, .is_err, .is_err_ptr, + .is_non_err, + .is_non_err_ptr, .ptrtoint, .floatcast, .intcast, diff --git a/src/codegen.zig b/src/codegen.zig index f9f5a861fb..6b9bd633d0 100644 --- a/src/codegen.zig +++ b/src/codegen.zig @@ -859,6 +859,8 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { .is_non_null_ptr => return self.genIsNonNullPtr(inst.castTag(.is_non_null_ptr).?), .is_null => return self.genIsNull(inst.castTag(.is_null).?), .is_null_ptr => return self.genIsNullPtr(inst.castTag(.is_null_ptr).?), + .is_non_err => return self.genIsNonErr(inst.castTag(.is_non_err).?), + .is_non_err_ptr => return self.genIsNonErrPtr(inst.castTag(.is_non_err_ptr).?), .is_err => return self.genIsErr(inst.castTag(.is_err).?), .is_err_ptr => return self.genIsErrPtr(inst.castTag(.is_err_ptr).?), .load => return self.genLoad(inst.castTag(.load).?), @@ -2972,6 +2974,16 @@ fn Function(comptime arch: std.Target.Cpu.Arch) type { return self.fail(inst.base.src, "TODO load the operand and call genIsErr", .{}); } + fn genIsNonErr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + switch (arch) { + else => return self.fail(inst.base.src, "TODO implement is_non_err for {}", .{self.target.cpu.arch}), + } + } + + fn genIsNonErrPtr(self: *Self, inst: *ir.Inst.UnOp) !MCValue { + return self.fail(inst.base.src, "TODO load the operand and call genIsNonErr", .{}); + } + fn genLoop(self: *Self, inst: *ir.Inst.Loop) !MCValue { // A loop is a setup to be able to jump back to the beginning. const start_index = self.code.items.len; diff --git a/src/codegen/c.zig b/src/codegen/c.zig index a9521d21a8..db0e910643 100644 --- a/src/codegen/c.zig +++ b/src/codegen/c.zig @@ -895,8 +895,10 @@ pub fn genBody(o: *Object, body: ir.Body) error{ AnalysisFail, OutOfMemory }!voi .ref => try genRef(o, inst.castTag(.ref).?), .struct_field_ptr => try genStructFieldPtr(o, inst.castTag(.struct_field_ptr).?), - .is_err => try genIsErr(o, inst.castTag(.is_err).?), - .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?), + .is_err => try genIsErr(o, inst.castTag(.is_err).?, "", "!="), + .is_non_err => try genIsErr(o, inst.castTag(.is_non_err).?, "", "=="), + .is_err_ptr => try genIsErr(o, inst.castTag(.is_err_ptr).?, "[0]", "!="), + .is_non_err_ptr => try genIsErr(o, inst.castTag(.is_non_err_ptr).?, "[0]", "=="), .unwrap_errunion_payload => try genUnwrapErrUnionPay(o, inst.castTag(.unwrap_errunion_payload).?), .unwrap_errunion_err => try genUnwrapErrUnionErr(o, inst.castTag(.unwrap_errunion_err).?), @@ -1446,15 +1448,14 @@ fn genWrapErrUnionPay(o: *Object, inst: *Inst.UnOp) !CValue { return local; } -fn genIsErr(o: *Object, inst: *Inst.UnOp) !CValue { +fn genIsErr(o: *Object, inst: *Inst.UnOp, deref_suffix: []const u8, op_str: []const u8) !CValue { const writer = o.writer(); - const maybe_deref = if (inst.base.tag == .is_err_ptr) "[0]" else ""; const operand = try o.resolveInst(inst.operand); const local = try o.allocLocal(Type.initTag(.bool), .Const); try writer.writeAll(" = ("); try o.writeCValue(writer, operand); - try writer.print("){s}.error != 0;\n", .{maybe_deref}); + try writer.print("){s}.error {s} 0;\n", .{ deref_suffix, op_str }); return local; } diff --git a/src/codegen/wasm.zig b/src/codegen/wasm.zig index ec4ec66b1e..3476ab2ce6 100644 --- a/src/codegen/wasm.zig +++ b/src/codegen/wasm.zig @@ -814,7 +814,8 @@ pub const Context = struct { .constant => unreachable, .dbg_stmt => WValue.none, .div => self.genBinOp(inst.castTag(.div).?, .div), - .is_err => self.genIsErr(inst.castTag(.is_err).?), + .is_err => self.genIsErr(inst.castTag(.is_err).?, .i32_ne), + .is_non_err => self.genIsErr(inst.castTag(.is_non_err).?, .i32_eq), .load => self.genLoad(inst.castTag(.load).?), .loop => self.genLoop(inst.castTag(.loop).?), .mul => self.genBinOp(inst.castTag(.mul).?, .mul), @@ -1278,7 +1279,7 @@ pub const Context = struct { return .none; } - fn genIsErr(self: *Context, inst: *Inst.UnOp) InnerError!WValue { + fn genIsErr(self: *Context, inst: *Inst.UnOp, opcode: wasm.Opcode) InnerError!WValue { const operand = self.resolveInst(inst.operand); const offset = self.code.items.len; const writer = self.code.writer(); @@ -1289,9 +1290,7 @@ pub const Context = struct { try writer.writeByte(wasm.opcode(.i32_const)); try leb.writeILEB128(writer, @as(i32, 0)); - // we want to break out of the condition if they're *not* equal, - // because that means there's an error. - try writer.writeByte(wasm.opcode(.i32_ne)); + try writer.writeByte(@enumToInt(opcode)); return WValue{ .code_offset = offset }; } -- cgit v1.2.3