From edcebe701339453bf49379d865a8049c5b22a49e Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 21 Dec 2021 18:50:01 +0100 Subject: [PATCH 1/2] stage2 ARM: implement is_null and is_non_null for ptr-like optionals --- src/arch/arm/CodeGen.zig | 50 +++++++++++++++++++++++++++------------- test/stage2/arm.zig | 20 ++++++++++++++++ 2 files changed, 54 insertions(+), 16 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 980c2cb169..02419a24d8 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -2334,18 +2334,28 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, .unreach, .{ .none, .none, .none }); } -fn isNull(self: *Self, operand: MCValue) !MCValue { - _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNonNull and invert the result. - return self.fail("TODO call isNonNull and invert the result", .{}); +fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue { + if (ty.isPtrLikeOptional()) { + assert(ty.abiSize(self.target.*) == 4); + + const reg_mcv: MCValue = switch (operand) { + .register => operand, + else => .{ .register = try self.copyToTmpRegister(ty, operand) }, + }; + + try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); + + return MCValue{ .compare_flags_unsigned = .eq }; + } else { + return self.fail("TODO implement non-pointer optionals", .{}); + } } -fn isNonNull(self: *Self, operand: MCValue) !MCValue { - _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNull and invert the result. - return self.fail("TODO call isNull and invert the result", .{}); +fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { + const is_null_result = try self.isNull(ty, operand); + assert(is_null_result.compare_flags_unsigned == .eq); + + return MCValue{ .compare_flags_unsigned = .neq }; } fn isErr(self: *Self, operand: MCValue) !MCValue { @@ -2364,9 +2374,14 @@ fn isNonErr(self: *Self, operand: MCValue) !MCValue { fn airIsNull(self: *Self, inst: Air.Inst.Index) !void { const un_op = self.air.instructions.items(.data)[inst].un_op; + + try self.spillCompareFlagsIfOccupied(); + self.compare_flags_inst = inst; + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const operand = try self.resolveInst(un_op); - break :result try self.isNull(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isNull(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2375,6 +2390,7 @@ fn airIsNullPtr(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_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2383,8 +2399,8 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void { break :blk try self.allocRegOrMem(inst, true); } }; - try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isNull(operand); + try self.load(operand, operand_ptr, ptr_ty); + break :result try self.isNull(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2393,7 +2409,8 @@ fn airIsNonNull(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); - break :result try self.isNonNull(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isNonNull(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2402,6 +2419,7 @@ fn airIsNonNullPtr(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_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2410,8 +2428,8 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { break :blk try self.allocRegOrMem(inst, true); } }; - try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isNonNull(operand); + try self.load(operand, operand_ptr, ptr_ty); + break :result try self.isNonNull(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig index e3db9473dc..c8f8d5bfe1 100644 --- a/test/stage2/arm.zig +++ b/test/stage2/arm.zig @@ -568,4 +568,24 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + + { + var case = ctx.exe("optionals", linux_arm); + case.addCompareOutput( + \\var x: u32 = 42; + \\ + \\pub fn main() void { + \\ var p: ?*u32 = null; + \\ assert(p == null); + \\ p = &x; + \\ assert(p != null); + \\} + \\ + \\fn assert(ok: bool) void { + \\ if (!ok) unreachable; + \\} + , + "", + ); + } } From c55f58d8bb38ef356282e43fa010b5e3f8da8a00 Mon Sep 17 00:00:00 2001 From: joachimschmidt557 Date: Tue, 21 Dec 2021 23:12:33 +0100 Subject: [PATCH 2/2] stage2 ARM: implement is_err and is_non_err for simple error unions --- src/arch/arm/CodeGen.zig | 108 ++++++++++++++++++++++++++------------- test/stage2/arm.zig | 49 ++++++++++++++++++ 2 files changed, 122 insertions(+), 35 deletions(-) diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index 02419a24d8..28020e7b16 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1104,7 +1104,13 @@ fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void { fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; - const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement unwrap error union payload for {}", .{self.target.cpu.arch}); + const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { + const err_ty = self.air.typeOf(ty_op.operand); + const payload_ty = err_ty.errorUnionPayload(); + if (!payload_ty.hasCodeGenBits()) break :result MCValue.none; + + return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -2358,18 +2364,45 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { return MCValue{ .compare_flags_unsigned = .neq }; } -fn isErr(self: *Self, operand: MCValue) !MCValue { +fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNonNull and invert the result. - return self.fail("TODO call isNonErr and invert the result", .{}); + + const error_type = ty.errorUnionSet(); + const payload_type = ty.errorUnionPayload(); + + if (!error_type.hasCodeGenBits()) { + return MCValue{ .immediate = 0 }; // always false + } else if (!payload_type.hasCodeGenBits()) { + if (error_type.abiSize(self.target.*) <= 4) { + const reg_mcv: MCValue = switch (operand) { + .register => operand, + else => .{ .register = try self.copyToTmpRegister(error_type, operand) }, + }; + + try self.genArmBinOpCode(undefined, reg_mcv, .{ .immediate = 0 }, false, .cmp_eq, undefined); + + return MCValue{ .compare_flags_unsigned = .gt }; + } else { + return self.fail("TODO isErr for errors with size > 4", .{}); + } + } else { + return self.fail("TODO isErr for non-empty payloads", .{}); + } } -fn isNonErr(self: *Self, operand: MCValue) !MCValue { - _ = operand; - // Here you can specialize this instruction if it makes sense to, otherwise the default - // will call isNull and invert the result. - return self.fail("TODO call isErr and invert the result", .{}); +fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { + 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 airIsNull(self: *Self, inst: Air.Inst.Index) !void { @@ -2438,7 +2471,8 @@ 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); - break :result try self.isErr(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isErr(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2447,6 +2481,7 @@ fn airIsErrPtr(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_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2455,8 +2490,8 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { break :blk try self.allocRegOrMem(inst, true); } }; - try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isErr(operand); + try self.load(operand, operand_ptr, ptr_ty); + break :result try self.isErr(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2465,7 +2500,8 @@ 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); - break :result try self.isNonErr(operand); + const ty = self.air.typeOf(un_op); + break :result try self.isNonErr(ty, operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -2474,6 +2510,7 @@ fn airIsNonErrPtr(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_ptr = try self.resolveInst(un_op); + const ptr_ty = self.air.typeOf(un_op); const operand: MCValue = blk: { if (self.reuseOperand(inst, un_op, 0, operand_ptr)) { // The MCValue that holds the pointer can be re-used as the value. @@ -2482,8 +2519,8 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { break :blk try self.allocRegOrMem(inst, true); } }; - try self.load(operand, operand_ptr, self.air.typeOf(un_op)); - break :result try self.isNonErr(operand); + try self.load(operand, operand_ptr, ptr_ty); + break :result try self.isNonErr(ptr_ty.elemType(), operand); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -3383,31 +3420,32 @@ fn genTypedValue(self: *Self, typed_value: TypedValue) InnerError!MCValue { } }, .ErrorSet => { - switch (typed_value.val.tag()) { - .@"error" => { - const err_name = typed_value.val.castTag(.@"error").?.data.name; - const module = self.bin_file.options.module.?; - const global_error_set = module.global_error_set; - const error_index = global_error_set.get(err_name).?; - return MCValue{ .immediate = error_index }; - }, - else => { - // In this case we are rendering an error union which has a 0 bits payload. - return MCValue{ .immediate = 0 }; - }, - } + const err_name = typed_value.val.castTag(.@"error").?.data.name; + const module = self.bin_file.options.module.?; + const global_error_set = module.global_error_set; + const error_index = global_error_set.get(err_name).?; + return MCValue{ .immediate = error_index }; }, .ErrorUnion => { const error_type = typed_value.ty.errorUnionSet(); const payload_type = typed_value.ty.errorUnionPayload(); - const sub_val = typed_value.val.castTag(.eu_payload).?.data; - if (!payload_type.hasCodeGenBits()) { - // We use the error type directly as the type. - return self.genTypedValue(.{ .ty = error_type, .val = sub_val }); + if (typed_value.val.castTag(.eu_payload)) |pl| { + if (!payload_type.hasCodeGenBits()) { + // We use the error type directly as the type. + return MCValue{ .immediate = 0 }; + } + + _ = pl; + return self.fail("TODO implement error union const of type '{}' (non-error)", .{typed_value.ty}); + } else { + if (!payload_type.hasCodeGenBits()) { + // We use the error type directly as the type. + return self.genTypedValue(.{ .ty = error_type, .val = typed_value.val }); + } + + return self.fail("TODO implement error union const of type '{}' (error)", .{typed_value.ty}); } - - return self.fail("TODO implement error union const of type '{}'", .{typed_value.ty}); }, else => return self.fail("TODO implement const of type '{}'", .{typed_value.ty}), } diff --git a/test/stage2/arm.zig b/test/stage2/arm.zig index c8f8d5bfe1..a0b1484b75 100644 --- a/test/stage2/arm.zig +++ b/test/stage2/arm.zig @@ -588,4 +588,53 @@ pub fn addCases(ctx: *TestContext) !void { "", ); } + + { + var case = ctx.exe("errors", linux_arm); + case.addCompareOutput( + \\pub fn main() void { + \\ foo() catch print(); + \\} + \\ + \\fn foo() anyerror!void {} + \\ + \\fn print() void { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (4), + \\ [arg1] "{r0}" (1), + \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{r2}" ("Hello, World!\n".len), + \\ : "memory" + \\ ); + \\ return; + \\} + , + "", + ); + + case.addCompareOutput( + \\pub fn main() void { + \\ foo() catch print(); + \\} + \\ + \\fn foo() anyerror!void { + \\ return error.Test; + \\} + \\ + \\fn print() void { + \\ asm volatile ("svc #0" + \\ : + \\ : [number] "{r7}" (4), + \\ [arg1] "{r0}" (1), + \\ [arg2] "{r1}" (@ptrToInt("Hello, World!\n")), + \\ [arg3] "{r2}" ("Hello, World!\n".len), + \\ : "memory" + \\ ); + \\ return; + \\} + , + "Hello, World!\n", + ); + } }