diff options
Diffstat (limited to 'src/arch')
| -rw-r--r-- | src/arch/arm/CodeGen.zig | 139 |
1 files changed, 84 insertions, 55 deletions
diff --git a/src/arch/arm/CodeGen.zig b/src/arch/arm/CodeGen.zig index e2744e7459..13093f1fbb 100644 --- a/src/arch/arm/CodeGen.zig +++ b/src/arch/arm/CodeGen.zig @@ -1261,27 +1261,84 @@ fn airOptionalPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } +fn airWrapOptional(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 optional_ty = self.air.typeOfIndex(inst); + const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); + + // Optional with a zero-bit payload type is just a boolean true + if (abi_size == 1) { + break :result MCValue{ .immediate = 1 }; + } else { + return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); + } + }; + return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); +} + +/// Given an error union, returns the error +fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue { + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return error_union_mcv; + + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionErr for registers", .{}), + .stack_argument_offset => |off| { + return MCValue{ .stack_argument_offset = off }; + }, + .stack_offset => |off| { + return MCValue{ .stack_offset = off }; + }, + .memory => |addr| { + return MCValue{ .memory = addr }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + 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_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); const mcv = try self.resolveInst(ty_op.operand); - if (!payload_ty.hasRuntimeBits()) break :result mcv; - - return self.fail("TODO implement unwrap error union error for non-empty payloads", .{}); + break :result try self.errUnionErr(mcv, error_union_ty); }; 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 { + const payload_ty = error_union_ty.errorUnionPayload(); + if (!payload_ty.hasRuntimeBits()) return MCValue.none; + + const error_ty = error_union_ty.errorUnionSet(); + const error_size = @intCast(u32, error_ty.abiSize(self.target.*)); + const eu_align = @intCast(u32, error_union_ty.abiAlignment(self.target.*)); + const offset = std.mem.alignForwardGeneric(u32, error_size, eu_align); + + // TODO optimization for small error unions: put into register + switch (error_union_mcv) { + .register => return self.fail("TODO errUnionPayload for registers", .{}), + .stack_argument_offset => |off| { + return MCValue{ .stack_argument_offset = off - offset }; + }, + .stack_offset => |off| { + return MCValue{ .stack_offset = off - offset }; + }, + .memory => |addr| { + return MCValue{ .memory = addr - offset }; + }, + else => unreachable, // invalid MCValue for an error union + } +} + 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_ty = self.air.typeOf(ty_op.operand); - const payload_ty = error_union_ty.errorUnionPayload(); - if (!payload_ty.hasRuntimeBits()) break :result MCValue.none; - - return self.fail("TODO implement unwrap error union payload for non-empty payloads", .{}); + const mcv = try self.resolveInst(ty_op.operand); + break :result try self.errUnionPayload(mcv, error_union_ty); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1306,22 +1363,6 @@ fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn airWrapOptional(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 optional_ty = self.air.typeOfIndex(inst); - const abi_size = @intCast(u32, optional_ty.abiSize(self.target.*)); - - // Optional with a zero-bit payload type is just a boolean true - if (abi_size == 1) { - break :result MCValue{ .immediate = 1 }; - } else { - return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch}); - } - }; - return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); -} - /// T to E!T fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { const ty_op = self.air.instructions.items(.data)[inst].ty_op; @@ -1343,9 +1384,9 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } -fn slicePtr(self: *Self, mcv: MCValue) !MCValue { +/// Given a slice, returns the length +fn slicePtr(mcv: MCValue) MCValue { switch (mcv) { - .dead, .unreach => unreachable, .register => unreachable, // a slice doesn't fit in one register .stack_argument_offset => |off| { return MCValue{ .stack_argument_offset = off }; @@ -1356,7 +1397,7 @@ fn slicePtr(self: *Self, mcv: MCValue) !MCValue { .memory => |addr| { return MCValue{ .memory = addr }; }, - else => return self.fail("TODO implement slice_ptr for {}", .{mcv}), + else => unreachable, // invalid MCValue for a slice } } @@ -1364,7 +1405,7 @@ fn airSlicePtr(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 mcv = try self.resolveInst(ty_op.operand); - break :result try self.slicePtr(mcv); + break :result slicePtr(mcv); }; return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); } @@ -1444,7 +1485,7 @@ fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { if (index_is_register) self.register_manager.freezeRegs(&.{index_mcv.register}); defer if (index_is_register) self.register_manager.unfreezeRegs(&.{index_mcv.register}); - const base_mcv = try self.slicePtr(slice_mcv); + const base_mcv = slicePtr(slice_mcv); switch (elem_size) { 1, 4 => { @@ -1507,7 +1548,7 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void { const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { const slice_mcv = try self.resolveInst(extra.lhs); const index_mcv = try self.resolveInst(extra.rhs); - const base_mcv = try self.slicePtr(slice_mcv); + const base_mcv = slicePtr(slice_mcv); const slice_ty = self.air.typeOf(extra.lhs); @@ -3163,35 +3204,15 @@ fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue { fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { const error_type = ty.errorUnionSet(); - const payload_type = ty.errorUnionPayload(); + const error_int_type = Type.initTag(.u16); if (!error_type.hasRuntimeBits()) { return MCValue{ .immediate = 0 }; // always false } - if (!payload_type.hasRuntimeBits()) { - 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.addInst(.{ - .tag = .cmp, - .data = .{ .rr_op = .{ - .rd = undefined, - .rn = reg_mcv.register, - .op = Instruction.Operand.fromU32(0).?, - } }, - }); - - 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", .{}); - } + const error_mcv = try self.errUnionErr(operand, ty); + _ = try self.binOp(.cmp_eq, null, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type); + return MCValue{ .compare_flags_unsigned = .gt }; } fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { @@ -4044,7 +4065,15 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); }, .memory => |addr| try self.genSetReg(ptr_ty, src_reg, .{ .immediate = @intCast(u32, addr) }), - .stack_argument_offset => return self.fail("TODO genSetStackArgument src={}", .{mcv}), + .stack_argument_offset => |off| { + _ = try self.addInst(.{ + .tag = .ldr_ptr_stack_argument, + .data = .{ .r_stack_offset = .{ + .rt = src_reg, + .stack_offset = off, + } }, + }); + }, else => unreachable, } |
