diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig index 0745cd46c9..5b4ab0d564 100644 --- a/src/arch/sparc64/CodeGen.zig +++ b/src/arch/sparc64/CodeGen.zig @@ -125,6 +125,12 @@ const MCValue = union(enum) { stack_offset: u32, /// The value is a pointer to one of the stack variables (payload is stack offset). ptr_stack_offset: u32, + /// The value is in the compare flags assuming an unsigned operation, + /// with this operator applied on top of it. + compare_flags_unsigned: math.CompareOperator, + /// The value is in the compare flags assuming a signed operation, + /// with this operator applied on top of it. + compare_flags_signed: math.CompareOperator, fn isMemory(mcv: MCValue) bool { return switch (mcv) { @@ -536,9 +542,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"), .is_null => @panic("TODO try self.airIsNull(inst)"), .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"), - .is_non_err => @panic("TODO try self.airIsNonErr(inst)"), + .is_non_err => try self.airIsNonErr(inst), .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"), - .is_err => @panic("TODO try self.airIsErr(inst)"), + .is_err => try self.airIsErr(inst), .is_err_ptr => @panic("TODO try self.airIsErrPtr(inst)"), .load => @panic("TODO try self.airLoad(inst)"), .loop => @panic("TODO try self.airLoop(inst)"), @@ -872,6 +878,8 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions. .unreach => unreachable, .dead => unreachable, .memory => unreachable, + .compare_flags_signed => unreachable, + .compare_flags_unsigned => unreachable, .register => |reg| { try self.register_manager.getReg(reg, null); try self.genSetReg(arg_ty, reg, arg_mcv); @@ -1004,6 +1012,26 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } +fn airIsErr(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + const ty = self.air.typeOf(un_op); + break :result try self.isErr(ty, operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + +fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void { + const un_op = self.air.instructions.items(.data)[inst].un_op; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const operand = try self.resolveInst(un_op); + const ty = self.air.typeOf(un_op); + break :result try self.isNonErr(ty, operand); + }; + return self.finishAir(inst, result, .{ un_op, .none, .none }); +} + fn airRet(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; const operand = try self.resolveInst(un_op); @@ -1259,6 +1287,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void switch (mcv) { .dead => unreachable, .unreach, .none => return, // Nothing to do. + .compare_flags_signed => return self.fail("TODO: genSetReg for compare_flags_signed", .{}), + .compare_flags_unsigned => return self.fail("TODO: genSetReg for compare_flags_unsigned", .{}), .undef => { if (!self.wantSafety()) return; // The already existing value will do just fine. @@ -1426,6 +1456,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro else => return self.fail("TODO implement memset", .{}), } }, + .compare_flags_unsigned, + .compare_flags_signed, .immediate, .ptr_stack_offset, => { @@ -1522,6 +1554,54 @@ fn getResolvedInstValue(self: *Self, inst: Air.Inst.Index) MCValue { } } +fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + const error_type = ty.errorUnionSet(); + const payload_type = ty.errorUnionPayload(); + + if (!error_type.hasRuntimeBits()) { + return MCValue{ .immediate = 0 }; // always false + } else if (!payload_type.hasRuntimeBits()) { + if (error_type.abiSize(self.target.*) <= 8) { + const reg_mcv: MCValue = switch (operand) { + .register => operand, + else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, + }; + + _ = try self.addInst(.{ + .tag = .subcc, + .data = .{ .arithmetic_3op = .{ + .is_imm = true, + .rs1 = reg_mcv.register, + .rs2_or_imm = .{ .imm = 0 }, + .rd = .g0, + } }, + }); + + return MCValue{ .compare_flags_unsigned = .gt }; + } else { + return self.fail("TODO isErr for errors with size > 8", .{}); + } + } else { + return self.fail("TODO isErr for non-empty payloads", .{}); + } +} + +fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + // Call isErr, then negate the result. + const is_err_result = try self.isErr(ty, operand); + switch (is_err_result) { + .compare_flags_unsigned => |op| { + assert(op == .gt); + return MCValue{ .compare_flags_unsigned = .lte }; + }, + .immediate => |imm| { + assert(imm == 0); + return MCValue{ .immediate = 1 }; + }, + else => unreachable, + } +} + fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigTomb { try self.ensureProcessDeathCapacity(operand_count + 1); return BigTomb{ diff --git a/src/arch/sparc64/Emit.zig b/src/arch/sparc64/Emit.zig index 81ae062c61..29ae8572a5 100644 --- a/src/arch/sparc64/Emit.zig +++ b/src/arch/sparc64/Emit.zig @@ -81,6 +81,7 @@ pub fn emitMir( .stx => try emit.mirArithmetic3Op(inst), .sub => try emit.mirArithmetic3Op(inst), + .subcc => @panic("TODO implement sparcv9 subcc"), .tcc => try emit.mirTrap(inst), } diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig index ef0be93f4c..bdf25814a9 100644 --- a/src/arch/sparc64/Mir.zig +++ b/src/arch/sparc64/Mir.zig @@ -106,6 +106,7 @@ pub const Inst = struct { /// This uses the arithmetic_3op field. // TODO add other operations. sub, + subcc, /// A.61 Trap on Integer Condition Codes (Tcc) /// This uses the trap field.