diff options
| author | joachimschmidt557 <joachim.schmidt557@outlook.com> | 2022-12-29 12:21:31 +0800 |
|---|---|---|
| committer | Joachim Schmidt <joachim.schmidt557@outlook.com> | 2022-12-29 11:12:08 +0100 |
| commit | 1caf56c5fbbb10fa28f8bf204d073983ce2a6dd5 (patch) | |
| tree | 024193b17c0c447172874a360d4fde8a4e10bf67 /src/arch | |
| parent | 34887cf136878c87357fa0eec52a12db300d8f27 (diff) | |
| download | zig-1caf56c5fbbb10fa28f8bf204d073983ce2a6dd5.tar.gz zig-1caf56c5fbbb10fa28f8bf204d073983ce2a6dd5.zip | |
stage2 AArch64: implement errUnion{Err,Payload} for registers
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/aarch64/CodeGen.zig | 170 |
1 files changed, 139 insertions, 31 deletions
diff --git a/src/arch/aarch64/CodeGen.zig b/src/arch/aarch64/CodeGen.zig index 924903e8a5..60cf4d889f 100644 --- a/src/arch/aarch64/CodeGen.zig +++ b/src/arch/aarch64/CodeGen.zig @@ -3050,19 +3050,60 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { } /// Given an error union, returns the error -fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { +fn errUnionErr( + self: *Self, + error_union_bind: ReadArg.Bind, + error_union_ty: Type, + maybe_inst: ?Air.Inst.Index, +) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); if (err_ty.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { - return error_union_mcv; + return try error_union_bind.resolveToMcv(self); } const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*)); - switch (error_union_mcv) { - .register => return self.fail("TODO errUnionErr for registers", .{}), + switch (try error_union_bind.resolveToMcv(self)) { + .register => { + var operand_reg: Register = undefined; + var dest_reg: Register = undefined; + + const read_args = [_]ReadArg{ + .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, + }; + const write_args = [_]WriteArg{ + .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, + }; + try self.allocRegs( + &read_args, + &write_args, + if (maybe_inst) |inst| .{ + .corresponding_inst = inst, + .operand_mapping = &.{0}, + } else null, + ); + + const err_bit_offset = err_offset * 8; + const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8; + + _ = try self.addInst(.{ + .tag = .ubfx, // errors are unsigned integers + .data = .{ + .rr_lsb_width = .{ + // Set both registers to the X variant to get the full width + .rd = dest_reg.toX(), + .rn = operand_reg.toX(), + .lsb = @intCast(u6, err_bit_offset), + .width = @intCast(u7, err_bit_size), + }, + }, + }); + + return MCValue{ .register = dest_reg }; + }, .stack_argument_offset => |off| { return MCValue{ .stack_argument_offset = off + err_offset }; }, @@ -3079,27 +3120,69 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV fn airUnwrapErrErr(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 result: { + const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; const error_union_ty = self.air.typeOf(ty_op.operand); - const mcv = try self.resolveInst(ty_op.operand); - break :result try self.errUnionErr(mcv, error_union_ty); + + break :result try self.errUnionErr(error_union_bind, error_union_ty, inst); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } /// Given an error union, returns the payload -fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { +fn errUnionPayload( + self: *Self, + error_union_bind: ReadArg.Bind, + error_union_ty: Type, + maybe_inst: ?Air.Inst.Index, +) !MCValue { const err_ty = error_union_ty.errorUnionSet(); const payload_ty = error_union_ty.errorUnionPayload(); if (err_ty.errorSetIsEmpty()) { - return error_union_mcv; + return try error_union_bind.resolveToMcv(self); } if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { return MCValue.none; } const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*)); - switch (error_union_mcv) { - .register => return self.fail("TODO errUnionPayload for registers", .{}), + switch (try error_union_bind.resolveToMcv(self)) { + .register => { + var operand_reg: Register = undefined; + var dest_reg: Register = undefined; + + const read_args = [_]ReadArg{ + .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg }, + }; + const write_args = [_]WriteArg{ + .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg }, + }; + try self.allocRegs( + &read_args, + &write_args, + if (maybe_inst) |inst| .{ + .corresponding_inst = inst, + .operand_mapping = &.{0}, + } else null, + ); + + const payload_bit_offset = payload_offset * 8; + const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8; + + _ = try self.addInst(.{ + .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx, + .data = .{ + .rr_lsb_width = .{ + // Set both registers to the X variant to get the full width + .rd = dest_reg.toX(), + .rn = operand_reg.toX(), + .lsb = @intCast(u5, payload_bit_offset), + .width = @intCast(u6, payload_bit_size), + }, + }, + }); + + return MCValue{ .register = dest_reg }; + }, .stack_argument_offset => |off| { return MCValue{ .stack_argument_offset = off + payload_offset }; }, @@ -3116,9 +3199,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) 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 result: { + const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand }; const error_union_ty = self.air.typeOf(ty_op.operand); - const error_union = try self.resolveInst(ty_op.operand); - break :result try self.errUnionPayload(error_union, error_union_ty); + + break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -3399,9 +3483,14 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { } fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { - const is_volatile = false; // TODO const bin_op = self.air.instructions.items(.data)[inst].bin_op; - const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement ptr_elem_val for {}", .{self.target.cpu.arch}); + const ptr_ty = self.air.typeOf(bin_op.lhs); + const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { + const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; + const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; + + break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst); + }; return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); } @@ -4792,19 +4881,27 @@ fn isNonNull(self: *Self, operand_bind: ReadArg.Bind, operand_ty: Type) !MCValue return MCValue{ .compare_flags = is_null_res.compare_flags.negate() }; } -fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { - const error_type = ty.errorUnionSet(); +fn isErr( + self: *Self, + error_union_bind: ReadArg.Bind, + error_union_ty: Type, +) !MCValue { + const error_type = error_union_ty.errorUnionSet(); if (error_type.errorSetIsEmpty()) { return MCValue{ .immediate = 0 }; // always false } - const error_mcv = try self.errUnionErr(operand, ty); + const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null); return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt); } -fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { - const is_err_result = try self.isErr(ty, operand); +fn isNonErr( + self: *Self, + error_union_bind: ReadArg.Bind, + error_union_ty: Type, +) !MCValue { + const is_err_result = try self.isErr(error_union_bind, error_union_ty); switch (is_err_result) { .compare_flags => |cond| { assert(cond == .hi); @@ -4873,9 +4970,10 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void { 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); + const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; + const error_union_ty = self.air.typeOf(un_op); + + break :result try self.isErr(error_union_bind, error_union_ty); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4890,7 +4988,7 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.allocRegOrMem(elem_ty, true, null); try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isErr(elem_ty, operand); + break :result try self.isErr(.{ .mcv = operand }, elem_ty); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4898,9 +4996,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void { 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); + const error_union_bind: ReadArg.Bind = .{ .inst = un_op }; + const error_union_ty = self.air.typeOf(un_op); + + break :result try self.isNonErr(error_union_bind, error_union_ty); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -4915,7 +5014,7 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void { const operand = try self.allocRegOrMem(elem_ty, true, null); try self.load(operand, operand_ptr, ptr_ty); - break :result try self.isNonErr(elem_ty, operand); + break :result try self.isNonErr(.{ .mcv = operand }, elem_ty); }; return self.finishAir(inst, result, .{ un_op, .none, .none }); } @@ -5960,15 +6059,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void { const extra = self.air.extraData(Air.Try, pl_op.payload); const body = self.air.extra[extra.end..][0..extra.data.body_len]; const result: MCValue = result: { + const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand }; const error_union_ty = self.air.typeOf(pl_op.operand); - const error_union = try self.resolveInst(pl_op.operand); - const is_err_result = try self.isErr(error_union_ty, error_union); + const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); + const error_union_align = error_union_ty.abiAlignment(self.target.*); + + // The error union will die in the body. However, we need the + // error union after the body in order to extract the payload + // of the error union, so we create a copy of it + const error_union_copy = try self.allocMem(error_union_size, error_union_align, null); + try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self)); + + const is_err_result = try self.isErr(error_union_bind, error_union_ty); const reloc = try self.condBr(is_err_result); try self.genBody(body); - try self.performReloc(reloc); - break :result try self.errUnionPayload(error_union, error_union_ty); + + break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null); }; return self.finishAir(inst, result, .{ pl_op.operand, .none, .none }); } |
