aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAndrew Kelley <andrew@ziglang.org>2022-09-14 20:00:38 -0400
committerGitHub <noreply@github.com>2022-09-14 20:00:38 -0400
commit78cd7b57ef7a3c8c645aab5c30b3fbf3834e6970 (patch)
tree7523ab6cb09ccc1d171c5014eeb324809261bde2 /src
parent17596c79a9239edec533a041f9a4da3a7af0d980 (diff)
parent86dc982e74dd17633d2bdc0f5ba8e36c55f78053 (diff)
downloadzig-78cd7b57ef7a3c8c645aab5c30b3fbf3834e6970.tar.gz
zig-78cd7b57ef7a3c8c645aab5c30b3fbf3834e6970.zip
Merge pull request #12206 from koachan/sparc64-codegen
stage2: sparc64: Another batch of Air lowerings, bugfixes, etc.
Diffstat (limited to 'src')
-rw-r--r--src/arch/sparc64/CodeGen.zig592
-rw-r--r--src/arch/sparc64/Mir.zig2
-rw-r--r--src/arch/sparc64/bits.zig24
3 files changed, 465 insertions, 153 deletions
diff --git a/src/arch/sparc64/CodeGen.zig b/src/arch/sparc64/CodeGen.zig
index cd8098e581..cfcfedf7cc 100644
--- a/src/arch/sparc64/CodeGen.zig
+++ b/src/arch/sparc64/CodeGen.zig
@@ -499,20 +499,29 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub),
.add => try self.airBinOp(inst, .add),
- .addwrap => @panic("TODO try self.airAddWrap(inst)"),
- .add_sat => @panic("TODO try self.airAddSat(inst)"),
- .sub => @panic("TODO try self.airBinOp(inst)"),
- .subwrap => @panic("TODO try self.airSubWrap(inst)"),
- .sub_sat => @panic("TODO try self.airSubSat(inst)"),
- .mul => @panic("TODO try self.airMul(inst)"),
- .mulwrap => @panic("TODO try self.airMulWrap(inst)"),
- .mul_sat => @panic("TODO try self.airMulSat(inst)"),
- .rem => try self.airRem(inst),
- .mod => try self.airMod(inst),
- .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
- .shl_sat => @panic("TODO try self.airShlSat(inst)"),
+ .addwrap => try self.airBinOp(inst, .addwrap),
+ .sub => try self.airBinOp(inst, .sub),
+ .subwrap => try self.airBinOp(inst, .subwrap),
+ .mul => try self.airBinOp(inst, .mul),
+ .mulwrap => try self.airBinOp(inst, .mulwrap),
+ .shl => try self.airBinOp(inst, .shl),
+ .shl_exact => try self.airBinOp(inst, .shl_exact),
+ .shr => try self.airBinOp(inst, .shr),
+ .shr_exact => try self.airBinOp(inst, .shr_exact),
+ .bool_and => try self.airBinOp(inst, .bool_and),
+ .bool_or => try self.airBinOp(inst, .bool_or),
+ .bit_and => try self.airBinOp(inst, .bit_and),
+ .bit_or => try self.airBinOp(inst, .bit_or),
+ .xor => try self.airBinOp(inst, .xor),
+
+ .add_sat => try self.airAddSat(inst),
+ .sub_sat => try self.airSubSat(inst),
+ .mul_sat => try self.airMulSat(inst),
+ .shl_sat => try self.airShlSat(inst),
.min => @panic("TODO try self.airMin(inst)"),
.max => @panic("TODO try self.airMax(inst)"),
+ .rem => try self.airRem(inst),
+ .mod => try self.airMod(inst),
.slice => try self.airSlice(inst),
.sqrt,
@@ -530,12 +539,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.round,
.trunc_float,
.neg,
- => @panic("TODO try self.airUnaryMath(inst)"),
+ => try self.airUnaryMath(inst),
.add_with_overflow => try self.airAddSubWithOverflow(inst),
.sub_with_overflow => try self.airAddSubWithOverflow(inst),
- .mul_with_overflow => @panic("TODO try self.airMulWithOverflow(inst)"),
- .shl_with_overflow => @panic("TODO try self.airShlWithOverflow(inst)"),
+ .mul_with_overflow => try self.airMulWithOverflow(inst),
+ .shl_with_overflow => try self.airShlWithOverflow(inst),
.div_float, .div_trunc, .div_floor, .div_exact => try self.airDiv(inst),
@@ -546,14 +555,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.cmp_gt => try self.airCmp(inst, .gt),
.cmp_neq => try self.airCmp(inst, .neq),
.cmp_vector => @panic("TODO try self.airCmpVector(inst)"),
- .cmp_lt_errors_len => @panic("TODO try self.airCmpLtErrorsLen(inst)"),
-
- .bool_and => @panic("TODO try self.airBoolOp(inst)"),
- .bool_or => @panic("TODO try self.airBoolOp(inst)"),
- .bit_and => try self.airBinOp(inst, .bit_and),
- .bit_or => try self.airBinOp(inst, .bit_or),
- .xor => try self.airBinOp(inst, .xor),
- .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
+ .cmp_lt_errors_len => try self.airCmpLtErrorsLen(inst),
.alloc => try self.airAlloc(inst),
.ret_ptr => try self.airRetPtr(inst),
@@ -584,15 +586,15 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.load => try self.airLoad(inst),
.loop => try self.airLoop(inst),
.not => try self.airNot(inst),
- .ptrtoint => @panic("TODO try self.airPtrToInt(inst)"),
+ .ptrtoint => try self.airPtrToInt(inst),
.ret => try self.airRet(inst),
.ret_load => try self.airRetLoad(inst),
.store => try self.airStore(inst),
.struct_field_ptr=> @panic("TODO try self.airStructFieldPtr(inst)"),
.struct_field_val=> try self.airStructFieldVal(inst),
.array_to_slice => try self.airArrayToSlice(inst),
- .int_to_float => @panic("TODO try self.airIntToFloat(inst)"),
- .float_to_int => @panic("TODO try self.airFloatToInt(inst)"),
+ .int_to_float => try self.airIntToFloat(inst),
+ .float_to_int => try self.airFloatToInt(inst),
.cmpxchg_strong => @panic("TODO try self.airCmpxchg(inst)"),
.cmpxchg_weak => @panic("TODO try self.airCmpxchg(inst)"),
.atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"),
@@ -601,12 +603,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.memset => try self.airMemset(inst),
.set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"),
.get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"),
- .clz => @panic("TODO try self.airClz(inst)"),
- .ctz => @panic("TODO try self.airCtz(inst)"),
- .popcount => @panic("TODO try self.airPopcount(inst)"),
+ .clz => try self.airClz(inst),
+ .ctz => try self.airCtz(inst),
+ .popcount => try self.airPopcount(inst),
.byte_swap => @panic("TODO try self.airByteSwap(inst)"),
.bit_reverse => @panic("TODO try self.airBitReverse(inst)"),
- .tag_name => @panic("TODO try self.airTagName(inst)"),
+ .tag_name => try self.airTagName(inst),
.error_name => @panic("TODO try self.airErrorName(inst)"),
.splat => @panic("TODO try self.airSplat(inst)"),
.select => @panic("TODO try self.airSelect(inst)"),
@@ -614,7 +616,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.reduce => @panic("TODO try self.airReduce(inst)"),
.aggregate_init => try self.airAggregateInit(inst),
.union_init => @panic("TODO try self.airUnionInit(inst)"),
- .prefetch => @panic("TODO try self.airPrefetch(inst)"),
+ .prefetch => try self.airPrefetch(inst),
.mul_add => @panic("TODO try self.airMulAdd(inst)"),
.@"try" => try self.airTry(inst),
@@ -650,7 +652,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.field_parent_ptr => @panic("TODO try self.airFieldParentPtr(inst)"),
.switch_br => try self.airSwitch(inst),
- .slice_ptr => @panic("TODO try self.airSlicePtr(inst)"),
+ .slice_ptr => try self.airSlicePtr(inst),
.slice_len => try self.airSliceLen(inst),
.ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"),
@@ -659,16 +661,16 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.array_elem_val => try self.airArrayElemVal(inst),
.slice_elem_val => try self.airSliceElemVal(inst),
.slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
- .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
+ .ptr_elem_val => try self.airPtrElemVal(inst),
.ptr_elem_ptr => try self.airPtrElemPtr(inst),
.constant => unreachable, // excluded from function bodies
.const_ty => unreachable, // excluded from function bodies
.unreach => self.finishAirBookkeeping(),
- .optional_payload => @panic("TODO try self.airOptionalPayload(inst)"),
- .optional_payload_ptr => @panic("TODO try self.airOptionalPayloadPtr(inst)"),
- .optional_payload_ptr_set => @panic("TODO try self.airOptionalPayloadPtrSet(inst)"),
+ .optional_payload => try self.airOptionalPayload(inst),
+ .optional_payload_ptr => try self.airOptionalPayloadPtr(inst),
+ .optional_payload_ptr_set => try self.airOptionalPayloadPtrSet(inst),
.unwrap_errunion_err => try self.airUnwrapErrErr(inst),
.unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
.unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
@@ -677,7 +679,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
.err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"),
.set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"),
- .wrap_optional => @panic("TODO try self.airWrapOptional(inst)"),
+ .wrap_optional => try self.airWrapOptional(inst),
.wrap_errunion_payload => @panic("TODO try self.airWrapErrUnionPayload(inst)"),
.wrap_errunion_err => try self.airWrapErrUnionErr(inst),
@@ -723,6 +725,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
}
}
+fn airAddSat(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
const tag = self.air.instructions.items(.tag)[inst];
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
@@ -763,7 +771,6 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
};
try self.spillConditionFlagsIfOccupied();
- self.condition_flags_inst = inst;
const dest = blk: {
if (rhs_immediate_ok) {
@@ -1241,6 +1248,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
return bt.finishAir(result);
}
+fn airClz(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 airClz for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
@@ -1299,6 +1312,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
}
+fn airCmpLtErrorsLen(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);
+ _ = operand;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airCmpLtErrorsLen for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const condition = try self.resolveInst(pl_op.operand);
@@ -1446,6 +1467,12 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, .unreach, .{ .none, .none, .none });
}
+fn airCtz(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 airCtz for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
fn airDbgBlock(self: *Self, inst: Air.Inst.Index) !void {
// TODO emit debug info lexical block
return self.finishAir(inst, .dead, .{ .none, .none, .none });
@@ -1519,6 +1546,14 @@ fn airFence(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, .dead, .{ .none, .none, .none });
}
+fn airFloatToInt(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 airFloatToInt for {}", .{
+ self.target.cpu.arch,
+ });
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
const ty_op = self.air.instructions.items(.data)[inst].ty_op;
if (self.liveness.isUnused(inst))
@@ -1537,6 +1572,14 @@ fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
}
+fn airIntToFloat(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 airIntToFloat for {}", .{
+ self.target.cpu.arch,
+ });
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .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: {
@@ -1768,6 +1811,78 @@ fn airMod(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none });
}
+fn airMulSat(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
+fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+ //const tag = self.air.instructions.items(.tag)[inst];
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+ const lhs = try self.resolveInst(extra.lhs);
+ const rhs = try self.resolveInst(extra.rhs);
+ const lhs_ty = self.air.typeOf(extra.lhs);
+ const rhs_ty = self.air.typeOf(extra.rhs);
+
+ switch (lhs_ty.zigTypeTag()) {
+ .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
+ .Int => {
+ const mod = self.bin_file.options.module.?;
+ assert(lhs_ty.eql(rhs_ty, mod));
+ const int_info = lhs_ty.intInfo(self.target.*);
+ switch (int_info.bits) {
+ 1...32 => {
+ try self.spillConditionFlagsIfOccupied();
+
+ const dest = try self.binOp(.mul, lhs, rhs, lhs_ty, rhs_ty, null);
+
+ const dest_reg = dest.register;
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
+
+ const truncated_reg = try self.register_manager.allocReg(null, gp);
+ const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
+ defer self.register_manager.unlockReg(truncated_reg_lock);
+
+ try self.truncRegister(
+ dest_reg,
+ truncated_reg,
+ int_info.signedness,
+ int_info.bits,
+ );
+
+ _ = try self.addInst(.{
+ .tag = .cmp,
+ .data = .{ .arithmetic_2op = .{
+ .is_imm = false,
+ .rs1 = dest_reg,
+ .rs2_or_imm = .{ .rs2 = truncated_reg },
+ } },
+ });
+
+ const cond = Instruction.ICondition.ne;
+ const ccr = Instruction.CCR.xcc;
+
+ break :result MCValue{ .register_with_overflow = .{
+ .reg = truncated_reg,
+ .flag = .{ .cond = cond, .ccr = ccr },
+ } };
+ },
+ // XXX DO NOT call __multi3 directly as it'll result in us doing six multiplications,
+ // which is far more than strictly necessary
+ 33...64 => return self.fail("TODO copy compiler-rt's mulddi3 for a 64x64->128 multiply", .{}),
+ else => return self.fail("TODO overflow operations on other integer sizes", .{}),
+ }
+ },
+ else => unreachable,
+ }
+ };
+ return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
+}
+
fn airNot(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: {
@@ -1863,6 +1978,43 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
+fn airOptionalPayload(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 .optional_payload for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
+fn airOptionalPayloadPtr(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 .optional_payload_ptr for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
+fn airOptionalPayloadPtrSet(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 .optional_payload_ptr_set for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
+fn airPopcount(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 airPopcount for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
+fn airPrefetch(self: *Self, inst: Air.Inst.Index) !void {
+ const prefetch = self.air.instructions.items(.data)[inst].prefetch;
+ // TODO Emit a PREFETCH/IPREFETCH as necessary, see A.7 and A.42
+ return self.finishAir(inst, MCValue.dead, .{ prefetch.ptr, .none, .none });
+}
+
+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});
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
@@ -1870,6 +2022,12 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
}
+fn airPtrToInt(self: *Self, inst: Air.Inst.Index) !void {
+ const un_op = self.air.instructions.items(.data)[inst].un_op;
+ const result = try self.resolveInst(un_op);
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airRem(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const lhs = try self.resolveInst(bin_op.lhs);
@@ -1911,6 +2069,101 @@ fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
}
+fn airShlSat(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
+fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
+ const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
+ const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
+ const lhs = try self.resolveInst(extra.lhs);
+ const rhs = try self.resolveInst(extra.rhs);
+ const lhs_ty = self.air.typeOf(extra.lhs);
+ const rhs_ty = self.air.typeOf(extra.rhs);
+
+ switch (lhs_ty.zigTypeTag()) {
+ .Vector => return self.fail("TODO implement mul_with_overflow for vectors", .{}),
+ .Int => {
+ const int_info = lhs_ty.intInfo(self.target.*);
+ if (int_info.bits <= 64) {
+ try self.spillConditionFlagsIfOccupied();
+
+ const lhs_lock: ?RegisterLock = if (lhs == .register)
+ self.register_manager.lockRegAssumeUnused(lhs.register)
+ else
+ null;
+ // TODO this currently crashes stage1
+ // defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+ // Increase shift amount (i.e, rhs) by shamt_bits - int_info.bits
+ // e.g if shifting a i48 then use sr*x (shamt_bits == 64) but increase rhs by 16
+ // and if shifting a i24 then use sr* (shamt_bits == 32) but increase rhs by 8
+ const new_rhs = switch (int_info.bits) {
+ 1...31 => if (rhs == .immediate) MCValue{
+ .immediate = rhs.immediate + 32 - int_info.bits,
+ } else try self.binOp(.add, rhs, .{ .immediate = 32 - int_info.bits }, rhs_ty, rhs_ty, null),
+ 33...63 => if (rhs == .immediate) MCValue{
+ .immediate = rhs.immediate + 64 - int_info.bits,
+ } else try self.binOp(.add, rhs, .{ .immediate = 64 - int_info.bits }, rhs_ty, rhs_ty, null),
+ 32, 64 => rhs,
+ else => unreachable,
+ };
+
+ const new_rhs_lock: ?RegisterLock = if (new_rhs == .register)
+ self.register_manager.lockRegAssumeUnused(new_rhs.register)
+ else
+ null;
+ // TODO this currently crashes stage1
+ // defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+ const dest = try self.binOp(.shl, lhs, new_rhs, lhs_ty, rhs_ty, null);
+ const dest_reg = dest.register;
+ const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
+ defer self.register_manager.unlockReg(dest_reg_lock);
+
+ const shr = try self.binOp(.shr, dest, new_rhs, lhs_ty, rhs_ty, null);
+
+ _ = try self.addInst(.{
+ .tag = .cmp,
+ .data = .{ .arithmetic_2op = .{
+ .is_imm = false,
+ .rs1 = dest_reg,
+ .rs2_or_imm = .{ .rs2 = shr.register },
+ } },
+ });
+
+ const cond = Instruction.ICondition.ne;
+ const ccr = switch (int_info.bits) {
+ 1...32 => Instruction.CCR.icc,
+ 33...64 => Instruction.CCR.xcc,
+ else => unreachable,
+ };
+
+ // TODO Those should really be written as defers, however stage1 currently
+ // panics when those are turned into defer statements so those are
+ // written here at the end as ordinary statements.
+ // Because of that, on failure, the lock on those registers wouldn't be
+ // released.
+ if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
+ if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
+
+ break :result MCValue{ .register_with_overflow = .{
+ .reg = dest_reg,
+ .flag = .{ .cond = cond, .ccr = ccr },
+ } };
+ } else {
+ return self.fail("TODO overflow operations on other integer sizes", .{});
+ }
+ },
+ else => unreachable,
+ }
+ };
+ return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
+}
+
fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
@@ -1996,6 +2249,25 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
}
+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);
+ switch (mcv) {
+ .dead, .unreach, .none => unreachable,
+ .register => unreachable, // a slice doesn't fit in one register
+ .stack_offset => |off| {
+ break :result MCValue{ .stack_offset = off };
+ },
+ .memory => |addr| {
+ break :result MCValue{ .memory = addr };
+ },
+ else => return self.fail("TODO implement slice_len for {}", .{mcv}),
+ }
+ };
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
fn airStore(self: *Self, inst: Air.Inst.Index) !void {
const bin_op = self.air.instructions.items(.data)[inst].bin_op;
const ptr = try self.resolveInst(bin_op.lhs);
@@ -2083,11 +2355,27 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none });
}
+fn airSubSat(self: *Self, inst: Air.Inst.Index) !void {
+ const bin_op = self.air.instructions.items(.data)[inst].bin_op;
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
+}
+
fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
_ = inst;
return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
}
+fn airTagName(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);
+ const result: MCValue = if (self.liveness.isUnused(inst)) .dead else {
+ _ = operand;
+ return self.fail("TODO implement airTagName for {}", .{self.target.cpu.arch});
+ };
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
fn airTry(self: *Self, inst: Air.Inst.Index) !void {
const pl_op = self.air.instructions.items(.data)[inst].pl_op;
const extra = self.air.extraData(Air.Try, pl_op.payload);
@@ -2106,6 +2394,15 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
}
+fn airUnaryMath(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
+ return self.fail("TODO implement airUnaryMath for {}", .{self.target.cpu.arch});
+ return self.finishAir(inst, result, .{ un_op, .none, .none });
+}
+
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: {
@@ -2145,6 +2442,20 @@ fn airWrapErrUnionErr(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);
+
+ // Optional with a zero-bit payload type is just a boolean true
+ if (optional_ty.abiSize(self.target.*) == 1)
+ break :result MCValue{ .immediate = 1 };
+
+ return self.fail("TODO implement wrap optional for {}", .{self.target.cpu.arch});
+ };
+ return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
+}
+
// Common helper functions
/// Adds a Type to the .debug_info at the current position. The bytes will be populated later,
@@ -2264,6 +2575,10 @@ fn binOp(
switch (tag) {
.add,
.sub,
+ .mul,
+ .bit_and,
+ .bit_or,
+ .xor,
.cmp_eq,
=> {
switch (lhs_ty.zigTypeTag()) {
@@ -2278,12 +2593,20 @@ fn binOp(
// operands
const lhs_immediate_ok = switch (tag) {
.add => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
+ .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
+ .bit_and => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
+ .bit_or => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
+ .xor => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
.sub, .cmp_eq => false,
else => unreachable,
};
const rhs_immediate_ok = switch (tag) {
.add,
.sub,
+ .mul,
+ .bit_and,
+ .bit_or,
+ .xor,
.cmp_eq,
=> rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
else => unreachable,
@@ -2292,6 +2615,10 @@ fn binOp(
const mir_tag: Mir.Inst.Tag = switch (tag) {
.add => .add,
.sub => .sub,
+ .mul => .mulx,
+ .bit_and => .@"and",
+ .bit_or => .@"or",
+ .xor => .xor,
.cmp_eq => .cmp,
else => unreachable,
};
@@ -2313,72 +2640,60 @@ fn binOp(
}
},
- .div_trunc => {
+ .addwrap,
+ .subwrap,
+ .mulwrap,
+ => {
+ const base_tag: Air.Inst.Tag = switch (tag) {
+ .addwrap => .add,
+ .subwrap => .sub,
+ .mulwrap => .mul,
+ else => unreachable,
+ };
+
+ // Generate the base operation
+ const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
+
+ // Truncate if necessary
switch (lhs_ty.zigTypeTag()) {
.Vector => return self.fail("TODO binary operations on vectors", .{}),
.Int => {
- assert(lhs_ty.eql(rhs_ty, mod));
const int_info = lhs_ty.intInfo(self.target.*);
if (int_info.bits <= 64) {
- const rhs_immediate_ok = switch (tag) {
- .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
- else => unreachable,
- };
-
- const mir_tag: Mir.Inst.Tag = switch (tag) {
- .div_trunc => switch (int_info.signedness) {
- .signed => Mir.Inst.Tag.sdivx,
- .unsigned => Mir.Inst.Tag.udivx,
- },
- else => unreachable,
- };
-
- if (rhs_immediate_ok) {
- return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata);
- } else {
- return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
- }
+ const result_reg = result.register;
+ try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
+ return result;
} else {
- return self.fail("TODO binary operations on int with bits > 64", .{});
+ return self.fail("TODO binary operations on integers > u64/i64", .{});
}
},
else => unreachable,
}
},
- .mul => {
+ .div_trunc => {
switch (lhs_ty.zigTypeTag()) {
.Vector => return self.fail("TODO binary operations on vectors", .{}),
.Int => {
assert(lhs_ty.eql(rhs_ty, mod));
const int_info = lhs_ty.intInfo(self.target.*);
if (int_info.bits <= 64) {
- // Only say yes if the operation is
- // commutative, i.e. we can swap both of the
- // operands
- const lhs_immediate_ok = switch (tag) {
- .mul => lhs == .immediate and lhs.immediate <= std.math.maxInt(u12),
- else => unreachable,
- };
const rhs_immediate_ok = switch (tag) {
- .mul => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
+ .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
else => unreachable,
};
const mir_tag: Mir.Inst.Tag = switch (tag) {
- .mul => .mulx,
+ .div_trunc => switch (int_info.signedness) {
+ .signed => Mir.Inst.Tag.sdivx,
+ .unsigned => Mir.Inst.Tag.udivx,
+ },
else => unreachable,
};
if (rhs_immediate_ok) {
- // At this point, rhs is an immediate
- return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
- } else if (lhs_immediate_ok) {
- // swap lhs and rhs
- // At this point, lhs is an immediate
- return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
+ return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata);
} else {
- // TODO convert large immediates to register before adding
return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
}
} else {
@@ -2419,88 +2734,61 @@ fn binOp(
}
},
- .bit_and,
- .bit_or,
- .xor,
+ .bool_and,
+ .bool_or,
=> {
switch (lhs_ty.zigTypeTag()) {
- .Vector => return self.fail("TODO binary operations on vectors", .{}),
- .Int => {
- assert(lhs_ty.eql(rhs_ty, mod));
- const int_info = lhs_ty.intInfo(self.target.*);
- if (int_info.bits <= 64) {
- // Only say yes if the operation is
- // commutative, i.e. we can swap both of the
- // operands
- const lhs_immediate_ok = switch (tag) {
- .bit_and,
- .bit_or,
- .xor,
- => lhs == .immediate and lhs.immediate <= std.math.maxInt(u13),
- else => unreachable,
- };
- const rhs_immediate_ok = switch (tag) {
- .bit_and,
- .bit_or,
- .xor,
- => rhs == .immediate and rhs.immediate <= std.math.maxInt(u13),
- else => unreachable,
- };
-
- const mir_tag: Mir.Inst.Tag = switch (tag) {
- .bit_and => .@"and",
- .bit_or => .@"or",
- .xor => .xor,
- else => unreachable,
- };
+ .Bool => {
+ assert(lhs != .immediate); // should have been handled by Sema
+ assert(rhs != .immediate); // should have been handled by Sema
+
+ const mir_tag: Mir.Inst.Tag = switch (tag) {
+ .bool_and => .@"and",
+ .bool_or => .@"or",
+ else => unreachable,
+ };
- if (rhs_immediate_ok) {
- return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
- } else if (lhs_immediate_ok) {
- // swap lhs and rhs
- return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
- } else {
- // TODO convert large immediates to register before adding
- return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
- }
- } else {
- return self.fail("TODO binary operations on int with bits > 64", .{});
- }
+ return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
},
else => unreachable,
}
},
- .shl => {
+ .shl,
+ .shr,
+ => {
const base_tag: Air.Inst.Tag = switch (tag) {
.shl => .shl_exact,
+ .shr => .shr_exact,
else => unreachable,
};
- // Generate a shl_exact/shr_exact
+ // Generate the base operation
const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
// Truncate if necessary
- switch (tag) {
- .shl => switch (lhs_ty.zigTypeTag()) {
- .Vector => return self.fail("TODO binary operations on vectors", .{}),
- .Int => {
- const int_info = lhs_ty.intInfo(self.target.*);
- if (int_info.bits <= 64) {
- const result_reg = result.register;
- try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
- return result;
- } else {
- return self.fail("TODO binary operations on integers > u64/i64", .{});
- }
- },
- else => unreachable,
+ switch (lhs_ty.zigTypeTag()) {
+ .Vector => return self.fail("TODO binary operations on vectors", .{}),
+ .Int => {
+ const int_info = lhs_ty.intInfo(self.target.*);
+ if (int_info.bits <= 64) {
+ // 32 and 64 bit operands doesn't need truncating
+ if (int_info.bits == 32 or int_info.bits == 64) return result;
+
+ const result_reg = result.register;
+ try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
+ return result;
+ } else {
+ return self.fail("TODO binary operations on integers > u64/i64", .{});
+ }
},
else => unreachable,
}
},
- .shl_exact => {
+ .shl_exact,
+ .shr_exact,
+ => {
switch (lhs_ty.zigTypeTag()) {
.Vector => return self.fail("TODO binary operations on vectors", .{}),
.Int => {
@@ -2509,7 +2797,11 @@ fn binOp(
const rhs_immediate_ok = rhs == .immediate;
const mir_tag: Mir.Inst.Tag = switch (tag) {
- .shl_exact => .sllx,
+ .shl_exact => if (int_info.bits <= 32) Mir.Inst.Tag.sll else Mir.Inst.Tag.sllx,
+ .shr_exact => switch (int_info.signedness) {
+ .signed => if (int_info.bits <= 32) Mir.Inst.Tag.sra else Mir.Inst.Tag.srax,
+ .unsigned => if (int_info.bits <= 32) Mir.Inst.Tag.srl else Mir.Inst.Tag.srlx,
+ },
else => unreachable,
};
@@ -2616,7 +2908,21 @@ fn binOpImmediate(
.rs2_or_imm = .{ .imm = @intCast(u12, rhs.immediate) },
},
},
- .sllx => .{
+ .sll,
+ .srl,
+ .sra,
+ => .{
+ .shift = .{
+ .is_imm = true,
+ .rd = dest_reg,
+ .rs1 = lhs_reg,
+ .rs2_or_imm = .{ .imm = @intCast(u5, rhs.immediate) },
+ },
+ },
+ .sllx,
+ .srlx,
+ .srax,
+ => .{
.shift = .{
.is_imm = true,
.rd = dest_reg,
@@ -2740,7 +3046,13 @@ fn binOpRegister(
.rs2_or_imm = .{ .rs2 = rhs_reg },
},
},
- .sllx => .{
+ .sll,
+ .srl,
+ .sra,
+ .sllx,
+ .srlx,
+ .srax,
+ => .{
.shift = .{
.is_imm = false,
.rd = dest_reg,
diff --git a/src/arch/sparc64/Mir.zig b/src/arch/sparc64/Mir.zig
index a040af60c4..744bd84943 100644
--- a/src/arch/sparc64/Mir.zig
+++ b/src/arch/sparc64/Mir.zig
@@ -218,7 +218,7 @@ pub const Inst = struct {
/// Used by e.g. call
branch_link: struct {
inst: Index,
- link: Register = .o7,
+ // link is always %o7
},
/// Branch with prediction, checking the integer status code
diff --git a/src/arch/sparc64/bits.zig b/src/arch/sparc64/bits.zig
index ea85c59f2b..4e6140bdab 100644
--- a/src/arch/sparc64/bits.zig
+++ b/src/arch/sparc64/bits.zig
@@ -1339,48 +1339,48 @@ pub const Instruction = union(enum) {
pub fn sll(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0101, .shift32, rs1, rs2, rd),
- u5 => format3l(0b11, 0b10_0101, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0101, .shift32, rs1, rs2, rd),
+ u5 => format3l(0b10, 0b10_0101, rs1, rs2, rd),
else => unreachable,
};
}
pub fn srl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0110, .shift32, rs1, rs2, rd),
- u5 => format3l(0b11, 0b10_0110, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0110, .shift32, rs1, rs2, rd),
+ u5 => format3l(0b10, 0b10_0110, rs1, rs2, rd),
else => unreachable,
};
}
pub fn sra(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0111, .shift32, rs1, rs2, rd),
- u5 => format3l(0b11, 0b10_0111, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0111, .shift32, rs1, rs2, rd),
+ u5 => format3l(0b10, 0b10_0111, rs1, rs2, rd),
else => unreachable,
};
}
pub fn sllx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0101, .shift64, rs1, rs2, rd),
- u6 => format3m(0b11, 0b10_0101, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0101, .shift64, rs1, rs2, rd),
+ u6 => format3m(0b10, 0b10_0101, rs1, rs2, rd),
else => unreachable,
};
}
pub fn srlx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0110, .shift64, rs1, rs2, rd),
- u6 => format3m(0b11, 0b10_0110, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0110, .shift64, rs1, rs2, rd),
+ u6 => format3m(0b10, 0b10_0110, rs1, rs2, rd),
else => unreachable,
};
}
pub fn srax(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
return switch (s2) {
- Register => format3k(0b11, 0b10_0111, .shift64, rs1, rs2, rd),
- u6 => format3m(0b11, 0b10_0111, rs1, rs2, rd),
+ Register => format3k(0b10, 0b10_0111, .shift64, rs1, rs2, rd),
+ u6 => format3m(0b10, 0b10_0111, rs1, rs2, rd),
else => unreachable,
};
}