authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-06-24 20:29:21+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-06-24 20:29:21+02:00
log905a18849f7f2c3f269fbf425170e0c86b12524a
tree437c299f0a0c141c8593d3ff69a59e8840469a73
parent7c87f9c828282aa12fb2d77c9c6a2318d83b8dff
parent672d6df42900b8521597c58af64a723985bbfd36
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #11923 from koachan/sparc64-codegen

stage2: sparc64: Another batch of new Air lowerings, improvements, and fixes

9 files changed, 895 insertions(+), 213 deletions(-)

src/arch/sparc64/CodeGen.zig+627-191
......@@ -13,6 +13,7 @@ const link = @import("../../link.zig");
1313const Module = @import("../../Module.zig");
1414const TypedValue = @import("../../TypedValue.zig");
1515const ErrorMsg = Module.ErrorMsg;
16const codegen = @import("../../codegen.zig");
1617const Air = @import("../../Air.zig");
1718const Mir = @import("Mir.zig");
1819const Emit = @import("Emit.zig");
......@@ -26,6 +27,8 @@ const build_options = @import("build_options");
2627
2728const bits = @import("bits.zig");
2829const abi = @import("abi.zig");
30const errUnionPayloadOffset = codegen.errUnionPayloadOffset;
31const errUnionErrorOffset = codegen.errUnionErrorOffset;
2932const Instruction = bits.Instruction;
3033const ShiftWidth = Instruction.ShiftWidth;
3134const RegisterManager = abi.RegisterManager;
......@@ -93,8 +96,11 @@ register_manager: RegisterManager = .{},
9396/// Maps offset to what is stored there.
9497stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{},
9598
96/// Tracks the current instruction allocated to the compare flags
97compare_flags_inst: ?Air.Inst.Index = null,
99/// Tracks the current instruction allocated to the condition flags
100condition_flags_inst: ?Air.Inst.Index = null,
101
102/// Tracks the current instruction allocated to the condition register
103condition_register_inst: ?Air.Inst.Index = null,
98104
99105/// Offset from the stack base, representing the end of the stack frame.
100106max_end_stack: u32 = 0,
......@@ -141,17 +147,19 @@ const MCValue = union(enum) {
141147 stack_offset: u32,
142148 /// The value is a pointer to one of the stack variables (payload is stack offset).
143149 ptr_stack_offset: u32,
144 /// The value is in the specified CCR assuming an unsigned operation,
145 /// with the operator applied on top of it.
146 compare_flags_unsigned: struct {
147 cmp: math.CompareOperator,
150 /// The value is in the specified CCR. The value is 1 (if
151 /// the type is u1) or true (if the type in bool) iff the
152 /// specified condition is true.
153 condition_flags: struct {
154 cond: Instruction.Condition,
148155 ccr: Instruction.CCR,
149156 },
150 /// The value is in the specified CCR assuming an signed operation,
151 /// with the operator applied on top of it.
152 compare_flags_signed: struct {
153 cmp: math.CompareOperator,
154 ccr: Instruction.CCR,
157 /// The value is in the specified Register. The value is 1 (if
158 /// the type is u1) or true (if the type in bool) iff the
159 /// specified condition is true.
160 condition_register: struct {
161 cond: Instruction.RCondition,
162 reg: Register,
155163 },
156164
157165 fn isMemory(mcv: MCValue) bool {
......@@ -176,6 +184,8 @@ const MCValue = union(enum) {
176184
177185 .immediate,
178186 .memory,
187 .condition_flags,
188 .condition_register,
179189 .ptr_stack_offset,
180190 .undef,
181191 => false,
......@@ -226,26 +236,12 @@ const CallMCValues = struct {
226236const BigTomb = struct {
227237 function: *Self,
228238 inst: Air.Inst.Index,
229 tomb_bits: Liveness.Bpi,
230 big_tomb_bits: u32,
231 bit_index: usize,
239 lbt: Liveness.BigTomb,
232240
233241 fn feed(bt: *BigTomb, op_ref: Air.Inst.Ref) void {
234 const this_bit_index = bt.bit_index;
235 bt.bit_index += 1;
236
237 const op_int = @enumToInt(op_ref);
238 if (op_int < Air.Inst.Ref.typed_value_map.len) return;
239 const op_index = @intCast(Air.Inst.Index, op_int - Air.Inst.Ref.typed_value_map.len);
240
241 if (this_bit_index < Liveness.bpi - 1) {
242 const dies = @truncate(u1, bt.tomb_bits >> @intCast(Liveness.OperandInt, this_bit_index)) != 0;
243 if (!dies) return;
244 } else {
245 const big_bit_index = @intCast(u5, this_bit_index - (Liveness.bpi - 1));
246 const dies = @truncate(u1, bt.big_tomb_bits >> big_bit_index) != 0;
247 if (!dies) return;
248 }
242 const dies = bt.lbt.feed();
243 const op_index = Air.refToIndex(op_ref) orelse return;
244 if (!dies) return;
249245 bt.function.processDeath(op_index);
250246 }
251247
......@@ -511,8 +507,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
511507 .mul => @panic("TODO try self.airMul(inst)"),
512508 .mulwrap => @panic("TODO try self.airMulWrap(inst)"),
513509 .mul_sat => @panic("TODO try self.airMulSat(inst)"),
514 .rem => @panic("TODO try self.airRem(inst)"),
515 .mod => @panic("TODO try self.airMod(inst)"),
510 .rem => try self.airRem(inst),
511 .mod => try self.airMod(inst),
516512 .shl, .shl_exact => @panic("TODO try self.airShl(inst)"),
517513 .shl_sat => @panic("TODO try self.airShlSat(inst)"),
518514 .min => @panic("TODO try self.airMin(inst)"),
......@@ -553,9 +549,9 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
553549
554550 .bool_and => @panic("TODO try self.airBoolOp(inst)"),
555551 .bool_or => @panic("TODO try self.airBoolOp(inst)"),
556 .bit_and => @panic("TODO try self.airBitAnd(inst)"),
557 .bit_or => @panic("TODO try self.airBitOr(inst)"),
558 .xor => @panic("TODO try self.airXor(inst)"),
552 .bit_and => try self.airBinOp(inst, .bit_and),
553 .bit_or => try self.airBinOp(inst, .bit_or),
554 .xor => try self.airBinOp(inst, .xor),
559555 .shr, .shr_exact => @panic("TODO try self.airShr(inst)"),
560556
561557 .alloc => try self.airAlloc(inst),
......@@ -568,17 +564,17 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
568564 .breakpoint => try self.airBreakpoint(),
569565 .ret_addr => @panic("TODO try self.airRetAddr(inst)"),
570566 .frame_addr => @panic("TODO try self.airFrameAddress(inst)"),
571 .fence => @panic("TODO try self.airFence()"),
567 .fence => try self.airFence(inst),
572568 .cond_br => try self.airCondBr(inst),
573569 .dbg_stmt => try self.airDbgStmt(inst),
574570 .fptrunc => @panic("TODO try self.airFptrunc(inst)"),
575571 .fpext => @panic("TODO try self.airFpext(inst)"),
576 .intcast => @panic("TODO try self.airIntCast(inst)"),
572 .intcast => try self.airIntCast(inst),
577573 .trunc => @panic("TODO try self.airTrunc(inst)"),
578 .bool_to_int => @panic("TODO try self.airBoolToInt(inst)"),
579 .is_non_null => @panic("TODO try self.airIsNonNull(inst)"),
574 .bool_to_int => try self.airBoolToInt(inst),
575 .is_non_null => try self.airIsNonNull(inst),
580576 .is_non_null_ptr => @panic("TODO try self.airIsNonNullPtr(inst)"),
581 .is_null => @panic("TODO try self.airIsNull(inst)"),
577 .is_null => try self.airIsNull(inst),
582578 .is_null_ptr => @panic("TODO try self.airIsNullPtr(inst)"),
583579 .is_non_err => try self.airIsNonErr(inst),
584580 .is_non_err_ptr => @panic("TODO try self.airIsNonErrPtr(inst)"),
......@@ -601,7 +597,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
601597 .atomic_rmw => @panic("TODO try self.airAtomicRmw(inst)"),
602598 .atomic_load => @panic("TODO try self.airAtomicLoad(inst)"),
603599 .memcpy => @panic("TODO try self.airMemcpy(inst)"),
604 .memset => @panic("TODO try self.airMemset(inst)"),
600 .memset => try self.airMemset(inst),
605601 .set_union_tag => @panic("TODO try self.airSetUnionTag(inst)"),
606602 .get_union_tag => @panic("TODO try self.airGetUnionTag(inst)"),
607603 .clz => @panic("TODO try self.airClz(inst)"),
......@@ -615,12 +611,12 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
615611 .select => @panic("TODO try self.airSelect(inst)"),
616612 .shuffle => @panic("TODO try self.airShuffle(inst)"),
617613 .reduce => @panic("TODO try self.airReduce(inst)"),
618 .aggregate_init => @panic("TODO try self.airAggregateInit(inst)"),
614 .aggregate_init => try self.airAggregateInit(inst),
619615 .union_init => @panic("TODO try self.airUnionInit(inst)"),
620616 .prefetch => @panic("TODO try self.airPrefetch(inst)"),
621617 .mul_add => @panic("TODO try self.airMulAdd(inst)"),
622618
623 .@"try" => @panic("TODO try self.airTry(inst)"),
619 .@"try" => try self.airTry(inst),
624620 .try_ptr => @panic("TODO try self.airTryPtr(inst)"),
625621
626622 .dbg_var_ptr,
......@@ -659,7 +655,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
659655 .ptr_slice_len_ptr => @panic("TODO try self.airPtrSliceLenPtr(inst)"),
660656 .ptr_slice_ptr_ptr => @panic("TODO try self.airPtrSlicePtrPtr(inst)"),
661657
662 .array_elem_val => @panic("TODO try self.airArrayElemVal(inst)"),
658 .array_elem_val => try self.airArrayElemVal(inst),
663659 .slice_elem_val => try self.airSliceElemVal(inst),
664660 .slice_elem_ptr => @panic("TODO try self.airSliceElemPtr(inst)"),
665661 .ptr_elem_val => @panic("TODO try self.airPtrElemVal(inst)"),
......@@ -676,7 +672,7 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void {
676672 .unwrap_errunion_payload => try self.airUnwrapErrPayload(inst),
677673 .unwrap_errunion_err_ptr => @panic("TODO try self.airUnwrapErrErrPtr(inst)"),
678674 .unwrap_errunion_payload_ptr=> @panic("TODO try self.airUnwrapErrPayloadPtr(inst)"),
679 .errunion_payload_ptr_set => @panic("TODO try self.airErrUnionPayloadPtrSet(inst)"),
675 .errunion_payload_ptr_set => try self.airErrUnionPayloadPtrSet(inst),
680676 .err_return_trace => @panic("TODO try self.airErrReturnTrace(inst)"),
681677 .set_err_return_trace => @panic("TODO try self.airSetErrReturnTrace(inst)"),
682678
......@@ -738,8 +734,8 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
738734 else => unreachable,
739735 };
740736
741 try self.spillCompareFlagsIfOccupied();
742 self.compare_flags_inst = inst;
737 try self.spillConditionFlagsIfOccupied();
738 self.condition_flags_inst = inst;
743739
744740 const dest = blk: {
745741 if (rhs_immediate_ok) {
......@@ -781,11 +777,39 @@ fn airAddSubWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
781777 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
782778}
783779
780fn airAggregateInit(self: *Self, inst: Air.Inst.Index) !void {
781 const vector_ty = self.air.typeOfIndex(inst);
782 const len = vector_ty.vectorLen();
783 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
784 const elements = @ptrCast([]const Air.Inst.Ref, self.air.extra[ty_pl.payload..][0..len]);
785 const result: MCValue = res: {
786 if (self.liveness.isUnused(inst)) break :res MCValue.dead;
787 return self.fail("TODO implement airAggregateInit for {}", .{self.target.cpu.arch});
788 };
789
790 if (elements.len <= Liveness.bpi - 1) {
791 var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1);
792 std.mem.copy(Air.Inst.Ref, &buf, elements);
793 return self.finishAir(inst, result, buf);
794 }
795 var bt = try self.iterateBigTomb(inst, elements.len);
796 for (elements) |elem| {
797 bt.feed(elem);
798 }
799 return bt.finishAir(result);
800}
801
784802fn airAlloc(self: *Self, inst: Air.Inst.Index) !void {
785803 const stack_offset = try self.allocMemPtr(inst);
786804 return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none });
787805}
788806
807fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
808 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
809 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
810 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
811}
812
789813fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
790814 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
791815 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -969,6 +993,13 @@ fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
969993 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
970994}
971995
996fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
997 const un_op = self.air.instructions.items(.data)[inst].un_op;
998 const operand = try self.resolveInst(un_op);
999 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else operand;
1000 return self.finishAir(inst, result, .{ un_op, .none, .none });
1001}
1002
9721003fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
9731004 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
9741005 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
......@@ -1072,7 +1103,14 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
10721103
10731104 // CCR is volatile across function calls
10741105 // (SCD 2.4.1, page 3P-10)
1075 try self.spillCompareFlagsIfOccupied();
1106 try self.spillConditionFlagsIfOccupied();
1107
1108 // Save caller-saved registers, but crucially *after* we save the
1109 // compare flags as saving compare flags may require a new
1110 // caller-saved register
1111 for (abi.caller_preserved_regs) |reg| {
1112 try self.register_manager.getReg(reg, null);
1113 }
10761114
10771115 for (info.args) |mc_arg, arg_i| {
10781116 const arg = args[arg_i];
......@@ -1167,7 +1205,12 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
11671205 return self.finishAir(inst, result, buf);
11681206 }
11691207
1170 @panic("TODO handle return value with BigTomb");
1208 var bt = try self.iterateBigTomb(inst, 1 + args.len);
1209 bt.feed(callee);
1210 for (args) |arg| {
1211 bt.feed(arg);
1212 }
1213 return bt.finishAir(result);
11711214}
11721215
11731216fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
......@@ -1208,12 +1251,18 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
12081251 .inst = inst,
12091252 });
12101253
1211 try self.spillCompareFlagsIfOccupied();
1212 self.compare_flags_inst = inst;
1254 try self.spillConditionFlagsIfOccupied();
1255 self.condition_flags_inst = inst;
12131256
12141257 break :result switch (int_info.signedness) {
1215 .signed => MCValue{ .compare_flags_signed = .{ .cmp = op, .ccr = .xcc } },
1216 .unsigned => MCValue{ .compare_flags_unsigned = .{ .cmp = op, .ccr = .xcc } },
1258 .signed => MCValue{ .condition_flags = .{
1259 .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorSigned(op) },
1260 .ccr = .xcc,
1261 } },
1262 .unsigned => MCValue{ .condition_flags = .{
1263 .cond = .{ .icond = Instruction.ICondition.fromCompareOperatorUnsigned(op) },
1264 .ccr = .xcc,
1265 } },
12171266 };
12181267 } else {
12191268 return self.fail("TODO SPARCv9 cmp for ints > 64 bits", .{});
......@@ -1224,69 +1273,14 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
12241273
12251274fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
12261275 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1227 const cond = try self.resolveInst(pl_op.operand);
1276 const condition = try self.resolveInst(pl_op.operand);
12281277 const extra = self.air.extraData(Air.CondBr, pl_op.payload);
12291278 const then_body = self.air.extra[extra.end..][0..extra.data.then_body_len];
12301279 const else_body = self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len];
12311280 const liveness_condbr = self.liveness.getCondBr(inst);
12321281
1233 // Here we either emit a BPcc for branching on CCR content,
1234 // or emit a BPr to branch on register content.
1235 const reloc: Mir.Inst.Index = switch (cond) {
1236 .compare_flags_signed,
1237 .compare_flags_unsigned,
1238 => try self.addInst(.{
1239 .tag = .bpcc,
1240 .data = .{
1241 .branch_predict_int = .{
1242 .ccr = switch (cond) {
1243 .compare_flags_signed => |cmp_op| cmp_op.ccr,
1244 .compare_flags_unsigned => |cmp_op| cmp_op.ccr,
1245 else => unreachable,
1246 },
1247 .cond = switch (cond) {
1248 .compare_flags_signed => |cmp_op| blk: {
1249 // Here we map to the opposite condition because the jump is to the false branch.
1250 const condition = Instruction.ICondition.fromCompareOperatorSigned(cmp_op.cmp);
1251 break :blk condition.negate();
1252 },
1253 .compare_flags_unsigned => |cmp_op| blk: {
1254 // Here we map to the opposite condition because the jump is to the false branch.
1255 const condition = Instruction.ICondition.fromCompareOperatorUnsigned(cmp_op.cmp);
1256 break :blk condition.negate();
1257 },
1258 else => unreachable,
1259 },
1260 .inst = undefined, // Will be filled by performReloc
1261 },
1262 },
1263 }),
1264 else => blk: {
1265 const reg = switch (cond) {
1266 .register => |r| r,
1267 else => try self.copyToTmpRegister(Type.bool, cond),
1268 };
1269
1270 break :blk try self.addInst(.{
1271 .tag = .bpr,
1272 .data = .{
1273 .branch_predict_reg = .{
1274 .cond = .eq_zero,
1275 .rs1 = reg,
1276 .inst = undefined, // populated later through performReloc
1277 },
1278 },
1279 });
1280 },
1281 };
1282
1283 // Regardless of the branch type that's emitted, we need to reserve
1284 // a space for the delay slot.
1285 // TODO Find a way to fill this delay slot
1286 _ = try self.addInst(.{
1287 .tag = .nop,
1288 .data = .{ .nop = {} },
1289 });
1282 // Here we emit a branch to the false section.
1283 const reloc: Mir.Inst.Index = try self.condBr(condition);
12901284
12911285 // If the condition dies here in this condbr instruction, process
12921286 // that death now instead of later as this has an effect on
......@@ -1305,7 +1299,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
13051299 var parent_stack = try self.stack.clone(self.gpa);
13061300 defer parent_stack.deinit(self.gpa);
13071301 const parent_registers = self.register_manager.registers;
1308 const parent_compare_flags_inst = self.compare_flags_inst;
1302 const parent_condition_flags_inst = self.condition_flags_inst;
13091303
13101304 try self.branch_stack.append(.{});
13111305 errdefer {
......@@ -1324,7 +1318,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
13241318 defer saved_then_branch.deinit(self.gpa);
13251319
13261320 self.register_manager.registers = parent_registers;
1327 self.compare_flags_inst = parent_compare_flags_inst;
1321 self.condition_flags_inst = parent_condition_flags_inst;
13281322
13291323 self.stack.deinit(self.gpa);
13301324 self.stack = parent_stack;
......@@ -1468,6 +1462,53 @@ fn airDiv(self: *Self, inst: Air.Inst.Index) !void {
14681462 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14691463}
14701464
1465fn airErrUnionPayloadPtrSet(self: *Self, inst: Air.Inst.Index) !void {
1466 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1467 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement .errunion_payload_ptr_set for {}", .{self.target.cpu.arch});
1468 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1469}
1470
1471fn airFence(self: *Self, inst: Air.Inst.Index) !void {
1472 // TODO weaken this as needed, currently this implements the strongest membar form
1473 const fence = self.air.instructions.items(.data)[inst].fence;
1474 _ = fence;
1475
1476 // membar #StoreStore | #LoadStore | #StoreLoad | #LoadLoad
1477 _ = try self.addInst(.{
1478 .tag = .membar,
1479 .data = .{
1480 .membar_mask = .{
1481 .mmask = .{
1482 .store_store = true,
1483 .store_load = true,
1484 .load_store = true,
1485 .load_load = true,
1486 },
1487 },
1488 },
1489 });
1490
1491 return self.finishAir(inst, .dead, .{ .none, .none, .none });
1492}
1493
1494fn airIntCast(self: *Self, inst: Air.Inst.Index) !void {
1495 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1496 if (self.liveness.isUnused(inst))
1497 return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none });
1498
1499 const operand_ty = self.air.typeOf(ty_op.operand);
1500 const operand = try self.resolveInst(ty_op.operand);
1501 const info_a = operand_ty.intInfo(self.target.*);
1502 const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*);
1503 if (info_a.signedness != info_b.signedness)
1504 return self.fail("TODO gen intcast sign safety in semantic analysis", .{});
1505
1506 if (info_a.bits == info_b.bits)
1507 return self.finishAir(inst, operand, .{ ty_op.operand, .none, .none });
1508
1509 return self.fail("TODO implement intCast for {}", .{self.target.cpu.arch});
1510}
1511
14711512fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
14721513 const un_op = self.air.instructions.items(.data)[inst].un_op;
14731514 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -1488,6 +1529,24 @@ fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
14881529 return self.finishAir(inst, result, .{ un_op, .none, .none });
14891530}
14901531
1532fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
1533 const un_op = self.air.instructions.items(.data)[inst].un_op;
1534 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1535 const operand = try self.resolveInst(un_op);
1536 break :result try self.isNull(operand);
1537 };
1538 return self.finishAir(inst, result, .{ un_op, .none, .none });
1539}
1540
1541fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
1542 const un_op = self.air.instructions.items(.data)[inst].un_op;
1543 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1544 const operand = try self.resolveInst(un_op);
1545 break :result try self.isNonNull(operand);
1546 };
1547 return self.finishAir(inst, result, .{ un_op, .none, .none });
1548}
1549
14911550fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
14921551 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
14931552 const elem_ty = self.air.typeOfIndex(inst);
......@@ -1529,6 +1588,158 @@ fn airLoop(self: *Self, inst: Air.Inst.Index) !void {
15291588 return self.finishAirBookkeeping();
15301589}
15311590
1591fn airMemset(self: *Self, inst: Air.Inst.Index) !void {
1592 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
1593 const extra = self.air.extraData(Air.Bin, pl_op.payload);
1594
1595 const operand = pl_op.operand;
1596 const value = extra.data.lhs;
1597 const length = extra.data.rhs;
1598 _ = operand;
1599 _ = value;
1600 _ = length;
1601
1602 return self.fail("TODO implement airMemset for {}", .{self.target.cpu.arch});
1603}
1604
1605fn airMod(self: *Self, inst: Air.Inst.Index) !void {
1606 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1607 const lhs = try self.resolveInst(bin_op.lhs);
1608 const rhs = try self.resolveInst(bin_op.rhs);
1609 const lhs_ty = self.air.typeOf(bin_op.lhs);
1610 const rhs_ty = self.air.typeOf(bin_op.rhs);
1611 assert(lhs_ty.eql(rhs_ty, self.bin_file.options.module.?));
1612
1613 if (self.liveness.isUnused(inst))
1614 return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
1615
1616 // TODO add safety check
1617
1618 // We use manual assembly emission to generate faster code
1619 // First, ensure lhs, rhs, rem, and added are in registers
1620
1621 const lhs_is_register = lhs == .register;
1622 const rhs_is_register = rhs == .register;
1623
1624 const lhs_reg = if (lhs_is_register)
1625 lhs.register
1626 else
1627 try self.register_manager.allocReg(null, gp);
1628
1629 const lhs_lock = self.register_manager.lockReg(lhs_reg);
1630 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1631
1632 const rhs_reg = if (rhs_is_register)
1633 rhs.register
1634 else
1635 try self.register_manager.allocReg(null, gp);
1636 const rhs_lock = self.register_manager.lockReg(rhs_reg);
1637 defer if (rhs_lock) |reg| self.register_manager.unlockReg(reg);
1638
1639 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1640 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
1641
1642 const regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
1643 const regs_locks = self.register_manager.lockRegsAssumeUnused(2, regs);
1644 defer for (regs_locks) |reg| {
1645 self.register_manager.unlockReg(reg);
1646 };
1647
1648 const add_reg = regs[0];
1649 const mod_reg = regs[1];
1650
1651 // mod_reg = @rem(lhs_reg, rhs_reg)
1652 _ = try self.addInst(.{
1653 .tag = .sdivx,
1654 .data = .{
1655 .arithmetic_3op = .{
1656 .is_imm = false,
1657 .rd = mod_reg,
1658 .rs1 = lhs_reg,
1659 .rs2_or_imm = .{ .rs2 = rhs_reg },
1660 },
1661 },
1662 });
1663
1664 _ = try self.addInst(.{
1665 .tag = .mulx,
1666 .data = .{
1667 .arithmetic_3op = .{
1668 .is_imm = false,
1669 .rd = mod_reg,
1670 .rs1 = mod_reg,
1671 .rs2_or_imm = .{ .rs2 = rhs_reg },
1672 },
1673 },
1674 });
1675
1676 _ = try self.addInst(.{
1677 .tag = .sub,
1678 .data = .{
1679 .arithmetic_3op = .{
1680 .is_imm = false,
1681 .rd = mod_reg,
1682 .rs1 = lhs_reg,
1683 .rs2_or_imm = .{ .rs2 = mod_reg },
1684 },
1685 },
1686 });
1687
1688 // add_reg = mod_reg + rhs_reg
1689 _ = try self.addInst(.{
1690 .tag = .add,
1691 .data = .{
1692 .arithmetic_3op = .{
1693 .is_imm = false,
1694 .rd = add_reg,
1695 .rs1 = mod_reg,
1696 .rs2_or_imm = .{ .rs2 = rhs_reg },
1697 },
1698 },
1699 });
1700
1701 // if (add_reg == rhs_reg) add_reg = 0
1702 _ = try self.addInst(.{
1703 .tag = .cmp,
1704 .data = .{
1705 .arithmetic_2op = .{
1706 .is_imm = false,
1707 .rs1 = add_reg,
1708 .rs2_or_imm = .{ .rs2 = rhs_reg },
1709 },
1710 },
1711 });
1712
1713 _ = try self.addInst(.{
1714 .tag = .movcc,
1715 .data = .{
1716 .conditional_move_int = .{
1717 .is_imm = true,
1718 .ccr = .xcc,
1719 .cond = .{ .icond = .eq },
1720 .rd = add_reg,
1721 .rs2_or_imm = .{ .imm = 0 },
1722 },
1723 },
1724 });
1725
1726 // if (lhs_reg < 0) mod_reg = add_reg
1727 _ = try self.addInst(.{
1728 .tag = .movr,
1729 .data = .{
1730 .conditional_move_reg = .{
1731 .is_imm = false,
1732 .cond = .lt_zero,
1733 .rd = mod_reg,
1734 .rs1 = lhs_reg,
1735 .rs2_or_imm = .{ .rs2 = add_reg },
1736 },
1737 },
1738 });
1739
1740 return self.finishAir(inst, .{ .register = mod_reg }, .{ bin_op.lhs, bin_op.rhs, .none });
1741}
1742
15321743fn airNot(self: *Self, inst: Air.Inst.Index) !void {
15331744 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
15341745 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -1537,42 +1748,17 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
15371748 switch (operand) {
15381749 .dead => unreachable,
15391750 .unreach => unreachable,
1540 .compare_flags_unsigned => |op| {
1541 const r = MCValue{
1542 .compare_flags_unsigned = .{
1543 .cmp = switch (op.cmp) {
1544 .gte => .lt,
1545 .gt => .lte,
1546 .neq => .eq,
1547 .lt => .gte,
1548 .lte => .gt,
1549 .eq => .neq,
1550 },
1551 .ccr = op.ccr,
1552 },
1553 };
1554 break :result r;
1555 },
1556 .compare_flags_signed => |op| {
1557 const r = MCValue{
1558 .compare_flags_signed = .{
1559 .cmp = switch (op.cmp) {
1560 .gte => .lt,
1561 .gt => .lte,
1562 .neq => .eq,
1563 .lt => .gte,
1564 .lte => .gt,
1565 .eq => .neq,
1566 },
1751 .condition_flags => |op| {
1752 break :result MCValue{
1753 .condition_flags = .{
1754 .cond = op.cond.negate(),
15671755 .ccr = op.ccr,
15681756 },
15691757 };
1570 break :result r;
15711758 },
15721759 else => {
15731760 switch (operand_ty.zigTypeTag()) {
15741761 .Bool => {
1575 // TODO convert this to mvn + and
15761762 const op_reg = switch (operand) {
15771763 .register => |r| r,
15781764 else => try self.copyToTmpRegister(operand_ty, operand),
......@@ -1638,7 +1824,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
16381824
16391825 break :result MCValue{ .register = dest_reg };
16401826 } else {
1641 return self.fail("TODO AArch64 not on integers > u64/i64", .{});
1827 return self.fail("TODO sparc64 not on integers > u64/i64", .{});
16421828 }
16431829 },
16441830 else => unreachable,
......@@ -1656,6 +1842,27 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
16561842 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
16571843}
16581844
1845fn airRem(self: *Self, inst: Air.Inst.Index) !void {
1846 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1847 const lhs = try self.resolveInst(bin_op.lhs);
1848 const rhs = try self.resolveInst(bin_op.rhs);
1849 const lhs_ty = self.air.typeOf(bin_op.lhs);
1850 const rhs_ty = self.air.typeOf(bin_op.rhs);
1851
1852 // TODO add safety check
1853
1854 // result = lhs - @divTrunc(lhs, rhs) * rhs
1855 const result: MCValue = if (self.liveness.isUnused(inst)) blk: {
1856 break :blk .dead;
1857 } else blk: {
1858 const tmp0 = try self.binOp(.div_trunc, lhs, rhs, lhs_ty, rhs_ty, null);
1859 const tmp1 = try self.binOp(.mul, tmp0, rhs, lhs_ty, rhs_ty, null);
1860 break :blk try self.binOp(.sub, lhs, tmp1, lhs_ty, rhs_ty, null);
1861 };
1862
1863 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1864}
1865
16591866fn airRet(self: *Self, inst: Air.Inst.Index) !void {
16601867 const un_op = self.air.instructions.items(.data)[inst].un_op;
16611868 const operand = try self.resolveInst(un_op);
......@@ -1826,7 +2033,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
18262033 _ = try self.addInst(.{
18272034 .tag = .movcc,
18282035 .data = .{
1829 .conditional_move = .{
2036 .conditional_move_int = .{
18302037 .ccr = rwo.flag.ccr,
18312038 .cond = .{ .icond = rwo.flag.cond },
18322039 .is_imm = true,
......@@ -1855,6 +2062,24 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
18552062 return self.fail("TODO implement switch for {}", .{self.target.cpu.arch});
18562063}
18572064
2065fn airTry(self: *Self, inst: Air.Inst.Index) !void {
2066 const pl_op = self.air.instructions.items(.data)[inst].pl_op;
2067 const extra = self.air.extraData(Air.Try, pl_op.payload);
2068 const body = self.air.extra[extra.end..][0..extra.data.body_len];
2069 const result: MCValue = result: {
2070 const error_union_ty = self.air.typeOf(pl_op.operand);
2071 const error_union = try self.resolveInst(pl_op.operand);
2072 const is_err_result = try self.isErr(error_union_ty, error_union);
2073 const reloc = try self.condBr(is_err_result);
2074
2075 try self.genBody(body);
2076
2077 try self.performReloc(reloc);
2078 break :result try self.errUnionPayload(error_union, error_union_ty);
2079 };
2080 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
2081}
2082
18582083fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
18592084 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
18602085 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
......@@ -2011,7 +2236,10 @@ fn binOp(
20112236) InnerError!MCValue {
20122237 const mod = self.bin_file.options.module.?;
20132238 switch (tag) {
2014 .add, .cmp_eq => {
2239 .add,
2240 .sub,
2241 .cmp_eq,
2242 => {
20152243 switch (lhs_ty.zigTypeTag()) {
20162244 .Float => return self.fail("TODO binary operations on floats", .{}),
20172245 .Vector => return self.fail("TODO binary operations on vectors", .{}),
......@@ -2037,6 +2265,7 @@ fn binOp(
20372265
20382266 const mir_tag: Mir.Inst.Tag = switch (tag) {
20392267 .add => .add,
2268 .sub => .sub,
20402269 .cmp_eq => .cmp,
20412270 else => unreachable,
20422271 };
......@@ -2058,6 +2287,39 @@ fn binOp(
20582287 }
20592288 },
20602289
2290 .div_trunc => {
2291 switch (lhs_ty.zigTypeTag()) {
2292 .Vector => return self.fail("TODO binary operations on vectors", .{}),
2293 .Int => {
2294 assert(lhs_ty.eql(rhs_ty, mod));
2295 const int_info = lhs_ty.intInfo(self.target.*);
2296 if (int_info.bits <= 64) {
2297 const rhs_immediate_ok = switch (tag) {
2298 .div_trunc => rhs == .immediate and rhs.immediate <= std.math.maxInt(u12),
2299 else => unreachable,
2300 };
2301
2302 const mir_tag: Mir.Inst.Tag = switch (tag) {
2303 .div_trunc => switch (int_info.signedness) {
2304 .signed => Mir.Inst.Tag.sdivx,
2305 .unsigned => Mir.Inst.Tag.udivx,
2306 },
2307 else => unreachable,
2308 };
2309
2310 if (rhs_immediate_ok) {
2311 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, true, metadata);
2312 } else {
2313 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
2314 }
2315 } else {
2316 return self.fail("TODO binary operations on int with bits > 64", .{});
2317 }
2318 },
2319 else => unreachable,
2320 }
2321 },
2322
20612323 .mul => {
20622324 switch (lhs_ty.zigTypeTag()) {
20632325 .Vector => return self.fail("TODO binary operations on vectors", .{}),
......@@ -2131,6 +2393,58 @@ fn binOp(
21312393 }
21322394 },
21332395
2396 .bit_and,
2397 .bit_or,
2398 .xor,
2399 => {
2400 switch (lhs_ty.zigTypeTag()) {
2401 .Vector => return self.fail("TODO binary operations on vectors", .{}),
2402 .Int => {
2403 assert(lhs_ty.eql(rhs_ty, mod));
2404 const int_info = lhs_ty.intInfo(self.target.*);
2405 if (int_info.bits <= 64) {
2406 // Only say yes if the operation is
2407 // commutative, i.e. we can swap both of the
2408 // operands
2409 const lhs_immediate_ok = switch (tag) {
2410 .bit_and,
2411 .bit_or,
2412 .xor,
2413 => lhs == .immediate and lhs.immediate <= std.math.maxInt(u13),
2414 else => unreachable,
2415 };
2416 const rhs_immediate_ok = switch (tag) {
2417 .bit_and,
2418 .bit_or,
2419 .xor,
2420 => rhs == .immediate and rhs.immediate <= std.math.maxInt(u13),
2421 else => unreachable,
2422 };
2423
2424 const mir_tag: Mir.Inst.Tag = switch (tag) {
2425 .bit_and => .@"and",
2426 .bit_or => .@"or",
2427 .xor => .xor,
2428 else => unreachable,
2429 };
2430
2431 if (rhs_immediate_ok) {
2432 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
2433 } else if (lhs_immediate_ok) {
2434 // swap lhs and rhs
2435 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
2436 } else {
2437 // TODO convert large immediates to register before adding
2438 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
2439 }
2440 } else {
2441 return self.fail("TODO binary operations on int with bits > 64", .{});
2442 }
2443 },
2444 else => unreachable,
2445 }
2446 },
2447
21342448 .shl => {
21352449 const base_tag: Air.Inst.Tag = switch (tag) {
21362450 .shl => .shl_exact,
......@@ -2259,7 +2573,14 @@ fn binOpImmediate(
22592573 const mir_data: Mir.Inst.Data = switch (mir_tag) {
22602574 .add,
22612575 .addcc,
2576 .@"and",
2577 .@"or",
2578 .xor,
2579 .xnor,
22622580 .mulx,
2581 .sdivx,
2582 .udivx,
2583 .sub,
22632584 .subcc,
22642585 => .{
22652586 .arithmetic_3op = .{
......@@ -2272,7 +2593,6 @@ fn binOpImmediate(
22722593 .sllx => .{
22732594 .shift = .{
22742595 .is_imm = true,
2275 .width = ShiftWidth.shift64,
22762596 .rd = dest_reg,
22772597 .rs1 = lhs_reg,
22782598 .rs2_or_imm = .{ .imm = @intCast(u6, rhs.immediate) },
......@@ -2377,7 +2697,14 @@ fn binOpRegister(
23772697 const mir_data: Mir.Inst.Data = switch (mir_tag) {
23782698 .add,
23792699 .addcc,
2700 .@"and",
2701 .@"or",
2702 .xor,
2703 .xnor,
23802704 .mulx,
2705 .sdivx,
2706 .udivx,
2707 .sub,
23812708 .subcc,
23822709 => .{
23832710 .arithmetic_3op = .{
......@@ -2390,7 +2717,6 @@ fn binOpRegister(
23902717 .sllx => .{
23912718 .shift = .{
23922719 .is_imm = false,
2393 .width = ShiftWidth.shift64,
23942720 .rd = dest_reg,
23952721 .rs1 = lhs_reg,
23962722 .rs2_or_imm = .{ .rs2 = rhs_reg },
......@@ -2464,6 +2790,62 @@ fn brVoid(self: *Self, block: Air.Inst.Index) !void {
24642790 block_data.relocs.appendAssumeCapacity(br_index);
24652791}
24662792
2793fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
2794 // Here we either emit a BPcc for branching on CCR content,
2795 // or emit a BPr to branch on register content.
2796 const reloc: Mir.Inst.Index = switch (condition) {
2797 .condition_flags => |flags| try self.addInst(.{
2798 .tag = .bpcc,
2799 .data = .{
2800 .branch_predict_int = .{
2801 .ccr = flags.ccr,
2802 // Here we map to the opposite condition because the jump is to the false branch.
2803 .cond = flags.cond.icond.negate(),
2804 .inst = undefined, // Will be filled by performReloc
2805 },
2806 },
2807 }),
2808 .condition_register => |reg| try self.addInst(.{
2809 .tag = .bpr,
2810 .data = .{
2811 .branch_predict_reg = .{
2812 .rs1 = reg.reg,
2813 // Here we map to the opposite condition because the jump is to the false branch.
2814 .cond = reg.cond.negate(),
2815 .inst = undefined, // Will be filled by performReloc
2816 },
2817 },
2818 }),
2819 else => blk: {
2820 const reg = switch (condition) {
2821 .register => |r| r,
2822 else => try self.copyToTmpRegister(Type.bool, condition),
2823 };
2824
2825 break :blk try self.addInst(.{
2826 .tag = .bpr,
2827 .data = .{
2828 .branch_predict_reg = .{
2829 .cond = .eq_zero,
2830 .rs1 = reg,
2831 .inst = undefined, // populated later through performReloc
2832 },
2833 },
2834 });
2835 },
2836 };
2837
2838 // Regardless of the branch type that's emitted, we need to reserve
2839 // a space for the delay slot.
2840 // TODO Find a way to fill this delay slot
2841 _ = try self.addInst(.{
2842 .tag = .nop,
2843 .data = .{ .nop = {} },
2844 });
2845
2846 return reloc;
2847}
2848
24672849/// Copies a value to a register without tracking the register. The register is not considered
24682850/// allocated. A second call to `copyToTmpRegister` may return the same register.
24692851/// This can have a side effect of spilling instructions to the stack to free up a register.
......@@ -2478,6 +2860,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
24782860 try table.ensureUnusedCapacity(self.gpa, additional_count);
24792861}
24802862
2863/// Given an error union, returns the payload
2864fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {
2865 const err_ty = error_union_ty.errorUnionSet();
2866 const payload_ty = error_union_ty.errorUnionPayload();
2867 if (err_ty.errorSetIsEmpty()) {
2868 return error_union_mcv;
2869 }
2870 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
2871 return MCValue.none;
2872 }
2873
2874 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
2875 switch (error_union_mcv) {
2876 .register => return self.fail("TODO errUnionPayload for registers", .{}),
2877 .stack_offset => |off| {
2878 return MCValue{ .stack_offset = off - payload_offset };
2879 },
2880 .memory => |addr| {
2881 return MCValue{ .memory = addr + payload_offset };
2882 },
2883 else => unreachable, // invalid MCValue for an error union
2884 }
2885}
2886
24812887fn fail(self: *Self, comptime format: []const u8, args: anytype) InnerError {
24822888 @setCold(true);
24832889 assert(self.err_msg == null);
......@@ -2667,20 +3073,10 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
26673073 switch (mcv) {
26683074 .dead => unreachable,
26693075 .unreach, .none => return, // Nothing to do.
2670 .compare_flags_signed,
2671 .compare_flags_unsigned,
2672 => {
2673 const condition = switch (mcv) {
2674 .compare_flags_unsigned => |op| Instruction.ICondition.fromCompareOperatorUnsigned(op.cmp),
2675 .compare_flags_signed => |op| Instruction.ICondition.fromCompareOperatorSigned(op.cmp),
2676 else => unreachable,
2677 };
3076 .condition_flags => |op| {
3077 const condition = op.cond;
3078 const ccr = op.ccr;
26783079
2679 const ccr = switch (mcv) {
2680 .compare_flags_unsigned => |op| op.ccr,
2681 .compare_flags_signed => |op| op.ccr,
2682 else => unreachable,
2683 };
26843080 // TODO handle floating point CCRs
26853081 assert(ccr == .xcc or ccr == .icc);
26863082
......@@ -2698,11 +3094,39 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
26983094 _ = try self.addInst(.{
26993095 .tag = .movcc,
27003096 .data = .{
2701 .conditional_move = .{
3097 .conditional_move_int = .{
27023098 .ccr = ccr,
2703 .cond = .{ .icond = condition },
3099 .cond = condition,
3100 .is_imm = true,
3101 .rd = reg,
3102 .rs2_or_imm = .{ .imm = 1 },
3103 },
3104 },
3105 });
3106 },
3107 .condition_register => |op| {
3108 const condition = op.cond;
3109 const register = op.reg;
3110
3111 _ = try self.addInst(.{
3112 .tag = .mov,
3113 .data = .{
3114 .arithmetic_2op = .{
3115 .is_imm = false,
3116 .rs1 = reg,
3117 .rs2_or_imm = .{ .rs2 = .g0 },
3118 },
3119 },
3120 });
3121
3122 _ = try self.addInst(.{
3123 .tag = .movr,
3124 .data = .{
3125 .conditional_move_reg = .{
3126 .cond = condition,
27043127 .is_imm = true,
27053128 .rd = reg,
3129 .rs1 = register,
27063130 .rs2_or_imm = .{ .imm = 1 },
27073131 },
27083132 },
......@@ -2773,7 +3197,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
27733197 .data = .{
27743198 .shift = .{
27753199 .is_imm = true,
2776 .width = .shift64,
27773200 .rd = reg,
27783201 .rs1 = reg,
27793202 .rs2_or_imm = .{ .imm = 12 },
......@@ -2804,7 +3227,6 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
28043227 .data = .{
28053228 .shift = .{
28063229 .is_imm = true,
2807 .width = .shift64,
28083230 .rd = reg,
28093231 .rs1 = reg,
28103232 .rs2_or_imm = .{ .imm = 32 },
......@@ -2874,8 +3296,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
28743296 else => return self.fail("TODO implement memset", .{}),
28753297 }
28763298 },
2877 .compare_flags_unsigned,
2878 .compare_flags_signed,
3299 .condition_flags,
3300 .condition_register,
28793301 .immediate,
28803302 .ptr_stack_offset,
28813303 => {
......@@ -2916,7 +3338,7 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
29163338 _ = try self.addInst(.{
29173339 .tag = .movcc,
29183340 .data = .{
2919 .conditional_move = .{
3341 .conditional_move_int = .{
29203342 .ccr = rwo.flag.ccr,
29213343 .cond = .{ .icond = rwo.flag.cond },
29223344 .is_imm = true,
......@@ -3103,7 +3525,7 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
31033525 } },
31043526 });
31053527
3106 return MCValue{ .compare_flags_unsigned = .{ .cmp = .gt, .ccr = .xcc } };
3528 return MCValue{ .condition_flags = .{ .cond = .{ .icond = .gu }, .ccr = .xcc } };
31073529 } else {
31083530 return self.fail("TODO isErr for errors with size > 8", .{});
31093531 }
......@@ -3116,9 +3538,30 @@ fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
31163538 // Call isErr, then negate the result.
31173539 const is_err_result = try self.isErr(ty, operand);
31183540 switch (is_err_result) {
3119 .compare_flags_unsigned => |op| {
3120 assert(op.cmp == .gt);
3121 return MCValue{ .compare_flags_unsigned = .{ .cmp = .lte, .ccr = op.ccr } };
3541 .condition_flags => |op| {
3542 return MCValue{ .condition_flags = .{ .cond = op.cond.negate(), .ccr = op.ccr } };
3543 },
3544 .immediate => |imm| {
3545 assert(imm == 0);
3546 return MCValue{ .immediate = 1 };
3547 },
3548 else => unreachable,
3549 }
3550}
3551
3552fn isNull(self: *Self, operand: MCValue) !MCValue {
3553 _ = operand;
3554 // Here you can specialize this instruction if it makes sense to, otherwise the default
3555 // will call isNonNull and invert the result.
3556 return self.fail("TODO call isNonNull and invert the result", .{});
3557}
3558
3559fn isNonNull(self: *Self, operand: MCValue) !MCValue {
3560 // Call isNull, then negate the result.
3561 const is_null_result = try self.isNull(operand);
3562 switch (is_null_result) {
3563 .condition_flags => |op| {
3564 return MCValue{ .condition_flags = .{ .cond = op.cond.negate(), .ccr = op.ccr } };
31223565 },
31233566 .immediate => |imm| {
31243567 assert(imm == 0);
......@@ -3133,9 +3576,7 @@ fn iterateBigTomb(self: *Self, inst: Air.Inst.Index, operand_count: usize) !BigT
31333576 return BigTomb{
31343577 .function = self,
31353578 .inst = inst,
3136 .tomb_bits = self.liveness.getTombBits(inst),
3137 .big_tomb_bits = self.liveness.special.get(inst) orelse 0,
3138 .bit_index = 0,
3579 .lbt = self.liveness.iterateBigTomb(inst),
31393580 };
31403581}
31413582
......@@ -3168,8 +3609,8 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
31683609 .undef => unreachable,
31693610 .unreach => unreachable,
31703611 .dead => unreachable,
3171 .compare_flags_unsigned,
3172 .compare_flags_signed,
3612 .condition_flags,
3613 .condition_register,
31733614 .register_with_overflow,
31743615 => unreachable, // cannot hold an address
31753616 .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }),
......@@ -3181,7 +3622,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo
31813622 switch (dst_mcv) {
31823623 .dead => unreachable,
31833624 .undef => unreachable,
3184 .compare_flags_signed, .compare_flags_unsigned => unreachable,
3625 .condition_flags => unreachable,
31853626 .register => |dst_reg| {
31863627 try self.genLoad(dst_reg, addr_reg, i13, 0, elem_size);
31873628 },
......@@ -3277,10 +3718,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void {
32773718 },
32783719 .register_with_overflow => |rwo| {
32793720 self.register_manager.freeReg(rwo.reg);
3280 self.compare_flags_inst = null;
3721 self.condition_flags_inst = null;
32813722 },
3282 .compare_flags_signed, .compare_flags_unsigned => {
3283 self.compare_flags_inst = null;
3723 .condition_flags => {
3724 self.condition_flags_inst = null;
32843725 },
32853726 else => {}, // TODO process stack allocation death
32863727 }
......@@ -3474,15 +3915,13 @@ fn setRegOrMem(self: *Self, ty: Type, loc: MCValue, val: MCValue) !void {
34743915 }
34753916}
34763917
3477/// Save the current instruction stored in the compare flags if
3918/// Save the current instruction stored in the condition flags if
34783919/// occupied
3479fn spillCompareFlagsIfOccupied(self: *Self) !void {
3480 if (self.compare_flags_inst) |inst_to_save| {
3920fn spillConditionFlagsIfOccupied(self: *Self) !void {
3921 if (self.condition_flags_inst) |inst_to_save| {
34813922 const mcv = self.getResolvedInstValue(inst_to_save);
34823923 const new_mcv = switch (mcv) {
3483 .compare_flags_signed,
3484 .compare_flags_unsigned,
3485 => try self.allocRegOrMem(inst_to_save, true),
3924 .condition_flags => try self.allocRegOrMem(inst_to_save, true),
34863925 .register_with_overflow => try self.allocRegOrMem(inst_to_save, false),
34873926 else => unreachable, // mcv doesn't occupy the compare flags
34883927 };
......@@ -3493,7 +3932,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void {
34933932 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
34943933 try branch.inst_table.put(self.gpa, inst_to_save, new_mcv);
34953934
3496 self.compare_flags_inst = null;
3935 self.condition_flags_inst = null;
34973936
34983937 // TODO consolidate with register manager and spillInstruction
34993938 // this call should really belong in the register manager!
......@@ -3522,8 +3961,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type
35223961 .undef => unreachable,
35233962 .unreach => unreachable,
35243963 .dead => unreachable,
3525 .compare_flags_unsigned,
3526 .compare_flags_signed,
3964 .condition_flags,
3965 .condition_register,
35273966 .register_with_overflow,
35283967 => unreachable, // cannot hold an address
35293968 .immediate => |imm| {
......@@ -3604,7 +4043,6 @@ fn truncRegister(
36044043 .data = .{
36054044 .shift = .{
36064045 .is_imm = true,
3607 .width = ShiftWidth.shift64,
36084046 .rd = dest_reg,
36094047 .rs1 = operand_reg,
36104048 .rs2_or_imm = .{ .imm = @intCast(u6, 64 - int_bits) },
......@@ -3619,7 +4057,6 @@ fn truncRegister(
36194057 .data = .{
36204058 .shift = .{
36214059 .is_imm = true,
3622 .width = ShiftWidth.shift32,
36234060 .rd = dest_reg,
36244061 .rs1 = dest_reg,
36254062 .rs2_or_imm = .{ .imm = @intCast(u6, int_bits) },
......@@ -3636,7 +4073,6 @@ fn truncRegister(
36364073 .data = .{
36374074 .shift = .{
36384075 .is_imm = true,
3639 .width = ShiftWidth.shift32,
36404076 .rd = dest_reg,
36414077 .rs1 = operand_reg,
36424078 .rs2_or_imm = .{ .imm = 0 },
src/arch/sparc64/Emit.zig+84-8
......@@ -93,13 +93,20 @@ pub fn emitMir(
9393 .lduw => try emit.mirArithmetic3Op(inst),
9494 .ldx => try emit.mirArithmetic3Op(inst),
9595
96 .@"and" => try emit.mirArithmetic3Op(inst),
9697 .@"or" => try emit.mirArithmetic3Op(inst),
9798 .xor => try emit.mirArithmetic3Op(inst),
9899 .xnor => try emit.mirArithmetic3Op(inst),
99100
101 .membar => try emit.mirMembar(inst),
102
100103 .movcc => try emit.mirConditionalMove(inst),
101104
105 .movr => try emit.mirConditionalMove(inst),
106
102107 .mulx => try emit.mirArithmetic3Op(inst),
108 .sdivx => try emit.mirArithmetic3Op(inst),
109 .udivx => try emit.mirArithmetic3Op(inst),
103110
104111 .nop => try emit.mirNop(),
105112
......@@ -110,12 +117,12 @@ pub fn emitMir(
110117
111118 .sethi => try emit.mirSethi(inst),
112119
113 .sll => @panic("TODO implement sparc64 sll"),
114 .srl => @panic("TODO implement sparc64 srl"),
115 .sra => @panic("TODO implement sparc64 sra"),
116 .sllx => @panic("TODO implement sparc64 sllx"),
117 .srlx => @panic("TODO implement sparc64 srlx"),
118 .srax => @panic("TODO implement sparc64 srax"),
120 .sll => try emit.mirShift(inst),
121 .srl => try emit.mirShift(inst),
122 .sra => try emit.mirShift(inst),
123 .sllx => try emit.mirShift(inst),
124 .srlx => try emit.mirShift(inst),
125 .srax => try emit.mirShift(inst),
119126
120127 .stb => try emit.mirArithmetic3Op(inst),
121128 .sth => try emit.mirArithmetic3Op(inst),
......@@ -201,7 +208,7 @@ fn mirArithmetic2Op(emit: *Emit, inst: Mir.Inst.Index) !void {
201208 .@"return" => try emit.writeInstruction(Instruction.@"return"(Register, rs1, rs2)),
202209 .cmp => try emit.writeInstruction(Instruction.subcc(Register, rs1, rs2, .g0)),
203210 .mov => try emit.writeInstruction(Instruction.@"or"(Register, .g0, rs2, rs1)),
204 .not => try emit.writeInstruction(Instruction.xnor(Register, .g0, rs2, rs1)),
211 .not => try emit.writeInstruction(Instruction.xnor(Register, rs2, .g0, rs1)),
205212 else => unreachable,
206213 }
207214 }
......@@ -224,10 +231,13 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
224231 .lduh => try emit.writeInstruction(Instruction.lduh(i13, rs1, imm, rd)),
225232 .lduw => try emit.writeInstruction(Instruction.lduw(i13, rs1, imm, rd)),
226233 .ldx => try emit.writeInstruction(Instruction.ldx(i13, rs1, imm, rd)),
234 .@"and" => try emit.writeInstruction(Instruction.@"and"(i13, rs1, imm, rd)),
227235 .@"or" => try emit.writeInstruction(Instruction.@"or"(i13, rs1, imm, rd)),
228236 .xor => try emit.writeInstruction(Instruction.xor(i13, rs1, imm, rd)),
229237 .xnor => try emit.writeInstruction(Instruction.xnor(i13, rs1, imm, rd)),
230238 .mulx => try emit.writeInstruction(Instruction.mulx(i13, rs1, imm, rd)),
239 .sdivx => try emit.writeInstruction(Instruction.sdivx(i13, rs1, imm, rd)),
240 .udivx => try emit.writeInstruction(Instruction.udivx(i13, rs1, imm, rd)),
231241 .save => try emit.writeInstruction(Instruction.save(i13, rs1, imm, rd)),
232242 .restore => try emit.writeInstruction(Instruction.restore(i13, rs1, imm, rd)),
233243 .stb => try emit.writeInstruction(Instruction.stb(i13, rs1, imm, rd)),
......@@ -248,10 +258,13 @@ fn mirArithmetic3Op(emit: *Emit, inst: Mir.Inst.Index) !void {
248258 .lduh => try emit.writeInstruction(Instruction.lduh(Register, rs1, rs2, rd)),
249259 .lduw => try emit.writeInstruction(Instruction.lduw(Register, rs1, rs2, rd)),
250260 .ldx => try emit.writeInstruction(Instruction.ldx(Register, rs1, rs2, rd)),
261 .@"and" => try emit.writeInstruction(Instruction.@"and"(Register, rs1, rs2, rd)),
251262 .@"or" => try emit.writeInstruction(Instruction.@"or"(Register, rs1, rs2, rd)),
252263 .xor => try emit.writeInstruction(Instruction.xor(Register, rs1, rs2, rd)),
253264 .xnor => try emit.writeInstruction(Instruction.xnor(Register, rs1, rs2, rd)),
254265 .mulx => try emit.writeInstruction(Instruction.mulx(Register, rs1, rs2, rd)),
266 .sdivx => try emit.writeInstruction(Instruction.sdivx(Register, rs1, rs2, rd)),
267 .udivx => try emit.writeInstruction(Instruction.udivx(Register, rs1, rs2, rd)),
255268 .save => try emit.writeInstruction(Instruction.save(Register, rs1, rs2, rd)),
256269 .restore => try emit.writeInstruction(Instruction.restore(Register, rs1, rs2, rd)),
257270 .stb => try emit.writeInstruction(Instruction.stb(Register, rs1, rs2, rd)),
......@@ -314,7 +327,7 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void {
314327
315328 switch (tag) {
316329 .movcc => {
317 const data = emit.mir.instructions.items(.data)[inst].conditional_move;
330 const data = emit.mir.instructions.items(.data)[inst].conditional_move_int;
318331 if (data.is_imm) {
319332 try emit.writeInstruction(Instruction.movcc(
320333 i11,
......@@ -333,10 +346,41 @@ fn mirConditionalMove(emit: *Emit, inst: Mir.Inst.Index) !void {
333346 ));
334347 }
335348 },
349 .movr => {
350 const data = emit.mir.instructions.items(.data)[inst].conditional_move_reg;
351 if (data.is_imm) {
352 try emit.writeInstruction(Instruction.movr(
353 i10,
354 data.cond,
355 data.rs1,
356 data.rs2_or_imm.imm,
357 data.rd,
358 ));
359 } else {
360 try emit.writeInstruction(Instruction.movr(
361 Register,
362 data.cond,
363 data.rs1,
364 data.rs2_or_imm.rs2,
365 data.rd,
366 ));
367 }
368 },
336369 else => unreachable,
337370 }
338371}
339372
373fn mirMembar(emit: *Emit, inst: Mir.Inst.Index) !void {
374 const tag = emit.mir.instructions.items(.tag)[inst];
375 const mask = emit.mir.instructions.items(.data)[inst].membar_mask;
376 assert(tag == .membar);
377
378 try emit.writeInstruction(Instruction.membar(
379 mask.cmask,
380 mask.mmask,
381 ));
382}
383
340384fn mirNop(emit: *Emit) !void {
341385 try emit.writeInstruction(Instruction.nop());
342386}
......@@ -352,6 +396,38 @@ fn mirSethi(emit: *Emit, inst: Mir.Inst.Index) !void {
352396 try emit.writeInstruction(Instruction.sethi(imm, rd));
353397}
354398
399fn mirShift(emit: *Emit, inst: Mir.Inst.Index) !void {
400 const tag = emit.mir.instructions.items(.tag)[inst];
401 const data = emit.mir.instructions.items(.data)[inst].shift;
402
403 const rd = data.rd;
404 const rs1 = data.rs1;
405
406 if (data.is_imm) {
407 const imm = data.rs2_or_imm.imm;
408 switch (tag) {
409 .sll => try emit.writeInstruction(Instruction.sll(u5, rs1, @truncate(u5, imm), rd)),
410 .srl => try emit.writeInstruction(Instruction.srl(u5, rs1, @truncate(u5, imm), rd)),
411 .sra => try emit.writeInstruction(Instruction.sra(u5, rs1, @truncate(u5, imm), rd)),
412 .sllx => try emit.writeInstruction(Instruction.sllx(u6, rs1, imm, rd)),
413 .srlx => try emit.writeInstruction(Instruction.srlx(u6, rs1, imm, rd)),
414 .srax => try emit.writeInstruction(Instruction.srax(u6, rs1, imm, rd)),
415 else => unreachable,
416 }
417 } else {
418 const rs2 = data.rs2_or_imm.rs2;
419 switch (tag) {
420 .sll => try emit.writeInstruction(Instruction.sll(Register, rs1, rs2, rd)),
421 .srl => try emit.writeInstruction(Instruction.srl(Register, rs1, rs2, rd)),
422 .sra => try emit.writeInstruction(Instruction.sra(Register, rs1, rs2, rd)),
423 .sllx => try emit.writeInstruction(Instruction.sllx(Register, rs1, rs2, rd)),
424 .srlx => try emit.writeInstruction(Instruction.srlx(Register, rs1, rs2, rd)),
425 .srax => try emit.writeInstruction(Instruction.srax(Register, rs1, rs2, rd)),
426 else => unreachable,
427 }
428 }
429}
430
355431fn mirTrap(emit: *Emit, inst: Mir.Inst.Index) !void {
356432 const tag = emit.mir.instructions.items(.tag)[inst];
357433 const data = emit.mir.instructions.items(.data)[inst].trap;
src/arch/sparc64/Mir.zig+40-7
......@@ -73,18 +73,28 @@ pub const Inst = struct {
7373 /// A.31 Logical Operations
7474 /// This uses the arithmetic_3op field.
7575 // TODO add other operations.
76 @"and",
7677 @"or",
7778 xor,
7879 xnor,
7980
81 /// A.32 Memory Barrier
82 /// This uses the membar_mask field.
83 membar,
84
8085 /// A.35 Move Integer Register on Condition (MOVcc)
81 /// This uses the conditional_move field.
86 /// This uses the conditional_move_int field.
8287 movcc,
8388
89 /// A.36 Move Integer Register on Register Condition (MOVr)
90 /// This uses the conditional_move_reg field.
91 movr,
92
8493 /// A.37 Multiply and Divide (64-bit)
8594 /// This uses the arithmetic_3op field.
86 // TODO add other operations.
8795 mulx,
96 sdivx,
97 udivx,
8898
8999 /// A.40 No Operation
90100 /// This uses the nop field.
......@@ -154,8 +164,9 @@ pub const Inst = struct {
154164 /// This uses the arithmetic_2op field, with rs1
155165 /// being the *destination* register.
156166 // TODO is it okay to abuse rs1 in this way?
157 // TODO this differs from official encoding for convenience, fix it later
158 not, // not rs2/imm, rs1 -> xnor %g0, rs2/imm, rs1
167 // not rs2, rs1 -> xnor rs2, %g0, rs1
168 // not imm, rs1 -> xnor %g0, imm, rs1
169 not,
159170 };
160171
161172 /// The position of an MIR instruction within the `Mir` instructions array.
......@@ -230,12 +241,19 @@ pub const Inst = struct {
230241 inst: Index,
231242 },
232243
233 /// Conditional move.
244 /// Membar mask, controls the barrier behavior
245 /// Used by e.g. membar
246 membar_mask: struct {
247 mmask: Instruction.MemOrderingConstraint = .{},
248 cmask: Instruction.MemCompletionConstraint = .{},
249 },
250
251 /// Conditional move, checking the integer status code
234252 /// if is_imm true then it uses the imm field of rs2_or_imm,
235253 /// otherwise it uses rs2 field.
236254 ///
237255 /// Used by e.g. movcc
238 conditional_move: struct {
256 conditional_move_int: struct {
239257 is_imm: bool,
240258 ccr: Instruction.CCR,
241259 cond: Instruction.Condition,
......@@ -246,6 +264,22 @@ pub const Inst = struct {
246264 },
247265 },
248266
267 /// Conditional move, comparing a register's content with zero
268 /// if is_imm true then it uses the imm field of rs2_or_imm,
269 /// otherwise it uses rs2 field.
270 ///
271 /// Used by e.g. movr
272 conditional_move_reg: struct {
273 is_imm: bool,
274 cond: Instruction.RCondition,
275 rd: Register,
276 rs1: Register,
277 rs2_or_imm: union {
278 rs2: Register,
279 imm: i10,
280 },
281 },
282
249283 /// No additional data
250284 ///
251285 /// Used by e.g. flushw
......@@ -266,7 +300,6 @@ pub const Inst = struct {
266300 /// Used by e.g. sllx
267301 shift: struct {
268302 is_imm: bool,
269 width: Instruction.ShiftWidth,
270303 rd: Register,
271304 rs1: Register,
272305 rs2_or_imm: union {
src/arch/sparc64/bits.zig+123-7
......@@ -475,6 +475,21 @@ pub const Instruction = union(enum) {
475475 ne_zero,
476476 gt_zero,
477477 ge_zero,
478
479 /// Returns the condition which is true iff the given condition is
480 /// false (if such a condition exists).
481 pub fn negate(cond: RCondition) RCondition {
482 return switch (cond) {
483 .eq_zero => .ne_zero,
484 .ne_zero => .eq_zero,
485 .lt_zero => .ge_zero,
486 .ge_zero => .lt_zero,
487 .le_zero => .gt_zero,
488 .gt_zero => .le_zero,
489 .reserved1 => unreachable,
490 .reserved2 => unreachable,
491 };
492 }
478493 };
479494
480495 pub const ASI = enum(u8) {
......@@ -673,10 +688,27 @@ pub const Instruction = union(enum) {
673688 }
674689 };
675690
676 pub const Condition = packed union {
691 pub const ConditionTag = enum { fcond, icond };
692 pub const Condition = union(ConditionTag) {
677693 fcond: FCondition,
678694 icond: ICondition,
679 encoded: u4,
695
696 /// Encodes the condition into the instruction bit pattern.
697 pub fn enc(cond: Condition) u4 {
698 return switch (cond) {
699 .icond => |c| @enumToInt(c),
700 .fcond => |c| @enumToInt(c),
701 };
702 }
703
704 /// Returns the condition which is true iff the given condition is
705 /// false (if such a condition exists).
706 pub fn negate(cond: Condition) Condition {
707 return switch (cond) {
708 .icond => |c| .{ .icond = c.negate() },
709 .fcond => |c| .{ .fcond = c.negate() },
710 };
711 }
680712 };
681713
682714 pub fn toU32(self: Instruction) u32 {
......@@ -755,7 +787,7 @@ pub const Instruction = union(enum) {
755787 return Instruction{
756788 .format_2b = .{
757789 .a = @boolToInt(annul),
758 .cond = cond.encoded,
790 .cond = cond.enc(),
759791 .op2 = op2,
760792 .disp22 = udisp_truncated,
761793 },
......@@ -776,7 +808,7 @@ pub const Instruction = union(enum) {
776808 return Instruction{
777809 .format_2c = .{
778810 .a = @boolToInt(annul),
779 .cond = cond.encoded,
811 .cond = cond.enc(),
780812 .op2 = op2,
781813 .cc1 = ccr_cc1,
782814 .cc0 = ccr_cc0,
......@@ -1057,7 +1089,7 @@ pub const Instruction = union(enum) {
10571089 .rd = rd.enc(),
10581090 .op3 = op3,
10591091 .cc2 = ccr_cc2,
1060 .cond = cond.encoded,
1092 .cond = cond.enc(),
10611093 .cc1 = ccr_cc1,
10621094 .cc0 = ccr_cc0,
10631095 .rs2 = rs2.enc(),
......@@ -1074,7 +1106,7 @@ pub const Instruction = union(enum) {
10741106 .rd = rd.enc(),
10751107 .op3 = op3,
10761108 .cc2 = ccr_cc2,
1077 .cond = cond.encoded,
1109 .cond = cond.enc(),
10781110 .cc1 = ccr_cc1,
10791111 .cc0 = ccr_cc0,
10801112 .simm11 = @bitCast(u11, imm),
......@@ -1122,7 +1154,7 @@ pub const Instruction = union(enum) {
11221154 .format_4g = .{
11231155 .rd = rd.enc(),
11241156 .op3 = op3,
1125 .cond = cond.encoded,
1157 .cond = cond.enc(),
11261158 .opf_cc = opf_cc,
11271159 .opf_low = opf_low,
11281160 .rs2 = rs2.enc(),
......@@ -1197,6 +1229,14 @@ pub const Instruction = union(enum) {
11971229 };
11981230 }
11991231
1232 pub fn @"and"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1233 return switch (s2) {
1234 Register => format3a(0b10, 0b00_0001, rs1, rs2, rd),
1235 i13 => format3b(0b10, 0b00_0001, rs1, rs2, rd),
1236 else => unreachable,
1237 };
1238 }
1239
12001240 pub fn @"or"(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
12011241 return switch (s2) {
12021242 Register => format3a(0b10, 0b00_0010, rs1, rs2, rd),
......@@ -1221,6 +1261,10 @@ pub const Instruction = union(enum) {
12211261 };
12221262 }
12231263
1264 pub fn membar(cmask: MemCompletionConstraint, mmask: MemOrderingConstraint) Instruction {
1265 return format3h(cmask, mmask);
1266 }
1267
12241268 pub fn movcc(comptime s2: type, cond: Condition, ccr: CCR, rs2: s2, rd: Register) Instruction {
12251269 return switch (s2) {
12261270 Register => format4c(0b10_1100, cond, ccr, rs2, rd),
......@@ -1229,6 +1273,14 @@ pub const Instruction = union(enum) {
12291273 };
12301274 }
12311275
1276 pub fn movr(comptime s2: type, cond: RCondition, rs1: Register, rs2: s2, rd: Register) Instruction {
1277 return switch (s2) {
1278 Register => format3e(0b10, 0b10_1111, cond, rs1, rs2, rd),
1279 i10 => format3f(0b10, 0b10_1111, cond, rs1, rs2, rd),
1280 else => unreachable,
1281 };
1282 }
1283
12321284 pub fn mulx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
12331285 return switch (s2) {
12341286 Register => format3a(0b10, 0b00_1001, rs1, rs2, rd),
......@@ -1237,6 +1289,22 @@ pub const Instruction = union(enum) {
12371289 };
12381290 }
12391291
1292 pub fn sdivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1293 return switch (s2) {
1294 Register => format3a(0b10, 0b10_1101, rs1, rs2, rd),
1295 i13 => format3b(0b10, 0b10_1101, rs1, rs2, rd),
1296 else => unreachable,
1297 };
1298 }
1299
1300 pub fn udivx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1301 return switch (s2) {
1302 Register => format3a(0b10, 0b00_1101, rs1, rs2, rd),
1303 i13 => format3b(0b10, 0b00_1101, rs1, rs2, rd),
1304 else => unreachable,
1305 };
1306 }
1307
12401308 pub fn nop() Instruction {
12411309 return sethi(0, .g0);
12421310 }
......@@ -1269,6 +1337,54 @@ pub const Instruction = union(enum) {
12691337 return format2a(0b100, imm, rd);
12701338 }
12711339
1340 pub fn sll(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1341 return switch (s2) {
1342 Register => format3k(0b11, 0b10_0101, .shift32, rs1, rs2, rd),
1343 u5 => format3l(0b11, 0b10_0101, rs1, rs2, rd),
1344 else => unreachable,
1345 };
1346 }
1347
1348 pub fn srl(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1349 return switch (s2) {
1350 Register => format3k(0b11, 0b10_0110, .shift32, rs1, rs2, rd),
1351 u5 => format3l(0b11, 0b10_0110, rs1, rs2, rd),
1352 else => unreachable,
1353 };
1354 }
1355
1356 pub fn sra(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1357 return switch (s2) {
1358 Register => format3k(0b11, 0b10_0111, .shift32, rs1, rs2, rd),
1359 u5 => format3l(0b11, 0b10_0111, rs1, rs2, rd),
1360 else => unreachable,
1361 };
1362 }
1363
1364 pub fn sllx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1365 return switch (s2) {
1366 Register => format3k(0b11, 0b10_0101, .shift64, rs1, rs2, rd),
1367 u6 => format3m(0b11, 0b10_0101, rs1, rs2, rd),
1368 else => unreachable,
1369 };
1370 }
1371
1372 pub fn srlx(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1373 return switch (s2) {
1374 Register => format3k(0b11, 0b10_0110, .shift64, rs1, rs2, rd),
1375 u6 => format3m(0b11, 0b10_0110, rs1, rs2, rd),
1376 else => unreachable,
1377 };
1378 }
1379
1380 pub fn srax(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
1381 return switch (s2) {
1382 Register => format3k(0b11, 0b10_0111, .shift64, rs1, rs2, rd),
1383 u6 => format3m(0b11, 0b10_0111, rs1, rs2, rd),
1384 else => unreachable,
1385 };
1386 }
1387
12721388 pub fn stb(comptime s2: type, rs1: Register, rs2: s2, rd: Register) Instruction {
12731389 return switch (s2) {
12741390 Register => format3a(0b11, 0b00_0101, rs1, rs2, rd),
test/behavior/array.zig+1
......@@ -511,6 +511,7 @@ test "zero-sized array with recursive type definition" {
511511
512512test "type coercion of anon struct literal to array" {
513513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
514 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
514515 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
515516 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
516517
test/behavior/atomics.zig+13
......@@ -8,6 +8,7 @@ test "cmpxchg" {
88 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
99 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1010 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
1112 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1213
1314 try testCmpxchg();
......@@ -48,6 +49,7 @@ test "atomicrmw and atomicload" {
4849 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
4950 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
5051 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
52 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
5153 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
5254
5355 var data: u8 = 200;
......@@ -77,6 +79,7 @@ test "cmpxchg with ptr" {
7779 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
7880 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
7981 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
82 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
8083 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8184
8285 var data1: i32 = 1234;
......@@ -103,6 +106,7 @@ test "cmpxchg with ignored result" {
103106 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
104107 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
105108 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
109 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
106110 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
107111
108112 var x: i32 = 1234;
......@@ -117,6 +121,7 @@ test "128-bit cmpxchg" {
117121 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
118122 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
119123 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
124 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
120125 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
121126
122127 if (builtin.cpu.arch != .x86_64) return error.SkipZigTest;
......@@ -150,6 +155,7 @@ test "cmpxchg on a global variable" {
150155 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
151156 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
152157 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
158 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
153159 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
154160
155161 if ((builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) and
......@@ -168,6 +174,7 @@ test "atomic load and rmw with enum" {
168174 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
169175 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
170176 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
177 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
171178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
172179
173180 const Value = enum(u8) { a, b, c };
......@@ -186,6 +193,7 @@ test "atomic store" {
186193 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
187194 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
188195 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
196 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
189197 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
190198
191199 var x: u32 = 0;
......@@ -200,6 +208,7 @@ test "atomic store comptime" {
200208 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
201209 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
202210 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
211 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
203212 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
204213
205214 comptime try testAtomicStore();
......@@ -219,6 +228,7 @@ test "atomicrmw with floats" {
219228 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
220229 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
221230 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
231 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
222232 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
223233
224234 if ((builtin.zig_backend == .stage1 or builtin.zig_backend == .stage2_llvm) and
......@@ -247,6 +257,7 @@ test "atomicrmw with ints" {
247257 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
248258 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
249259 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
260 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
250261 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
251262
252263 try testAtomicRmwInt();
......@@ -281,6 +292,7 @@ test "atomics with different types" {
281292 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
282293 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
283294 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
295 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
284296 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
285297
286298 try testAtomicsWithType(bool, true, false);
......@@ -311,6 +323,7 @@ test "return @atomicStore, using it as a void value" {
311323 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
312324 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
313325 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
326 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
314327 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
315328
316329 const S = struct {
test/behavior/basic.zig+5
......@@ -24,6 +24,8 @@ fn testTruncate(x: u32) u8 {
2424}
2525
2626test "truncate to non-power-of-two integers" {
27 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
28
2729 try testTrunc(u32, u1, 0b10101, 0b1);
2830 try testTrunc(u32, u1, 0b10110, 0b0);
2931 try testTrunc(u32, u2, 0b10101, 0b01);
......@@ -401,6 +403,7 @@ fn testPointerToVoidReturnType2() *const void {
401403
402404test "array 2D const double ptr" {
403405 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
406 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
404407 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
405408 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
406409 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
......@@ -414,6 +417,7 @@ test "array 2D const double ptr" {
414417
415418test "array 2D const double ptr with offset" {
416419 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
420 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
417421 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
418422 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
419423 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
......@@ -427,6 +431,7 @@ test "array 2D const double ptr with offset" {
427431
428432test "array 3D const double ptr with offset" {
429433 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
434 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
430435 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
431436 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
432437 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
test/behavior/bugs/9584.zig+1
......@@ -46,6 +46,7 @@ test {
4646 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
4747 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
4848 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
49 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
4950 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
5051 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
5152
test/behavior/byteswap.zig+1
......@@ -7,6 +7,7 @@ test "@byteSwap integers" {
77 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
88 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
99 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10 if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest;
1011
1112 const ByteSwapIntTest = struct {
1213 fn run() !void {