authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-09-10 09:13:08+02:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2022-09-10 09:13:08+02:00
log5778077f9f747c98cf1136e536abdb99051f7b41
treea5aef7f0adfc82080226558ed631388ff4da20da
parent69da56b36c1bd23214b9c72587cdd424637d2ced
parent94499898e5cd31209ddfdae3f0c9b418b7f67e60
signaturebadge-question-mark Signed by PGP key 4AEE18F83AFDEB23

Merge pull request #12799 from joachimschmidt557/stage2-arm

stage2 ARM: introduce allocRegs mechanism and other improvements

28 files changed, 1669 insertions(+), 1149 deletions(-)

src/arch/arm/CodeGen.zig+1558-1057
...@@ -438,9 +438,8 @@ fn gen(self: *Self) !void {...@@ -438,9 +438,8 @@ fn gen(self: *Self) !void {
438 // mov fp, sp438 // mov fp, sp
439 _ = try self.addInst(.{439 _ = try self.addInst(.{
440 .tag = .mov,440 .tag = .mov,
441 .data = .{ .rr_op = .{441 .data = .{ .r_op_mov = .{
442 .rd = .fp,442 .rd = .fp,
443 .rn = .r0,
444 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),443 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
445 } },444 } },
446 });445 });
...@@ -452,9 +451,7 @@ fn gen(self: *Self) !void {...@@ -452,9 +451,7 @@ fn gen(self: *Self) !void {
452 // The address of where to store the return value is in451 // The address of where to store the return value is in
453 // r0. As this register might get overwritten along the452 // r0. As this register might get overwritten along the
454 // way, save the address to the stack.453 // way, save the address to the stack.
455 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4) + 4;454 const stack_offset = try self.allocMem(4, 4, null);
456 self.next_stack_offset = stack_offset;
457 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
458455
459 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });456 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
460 self.ret_mcv = MCValue{ .stack_offset = stack_offset };457 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
...@@ -491,14 +488,10 @@ fn gen(self: *Self) !void {...@@ -491,14 +488,10 @@ fn gen(self: *Self) !void {
491 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);488 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
492 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;489 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
493 self.max_end_stack = stack_size;490 self.max_end_stack = stack_size;
494 if (Instruction.Operand.fromU32(stack_size)) |op| {491 self.mir_instructions.set(sub_reloc, .{
495 self.mir_instructions.set(sub_reloc, .{492 .tag = .sub_sp_scratch_r0,
496 .tag = .sub,493 .data = .{ .imm32 = stack_size },
497 .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } },494 });
498 });
499 } else {
500 return self.failSymbol("TODO ARM: allow larger stacks", .{});
501 }
502495
503 _ = try self.addInst(.{496 _ = try self.addInst(.{
504 .tag = .dbg_epilogue_begin,497 .tag = .dbg_epilogue_begin,
...@@ -531,9 +524,8 @@ fn gen(self: *Self) !void {...@@ -531,9 +524,8 @@ fn gen(self: *Self) !void {
531 // mov sp, fp524 // mov sp, fp
532 _ = try self.addInst(.{525 _ = try self.addInst(.{
533 .tag = .mov,526 .tag = .mov,
534 .data = .{ .rr_op = .{527 .data = .{ .r_op_mov = .{
535 .rd = .sp,528 .rd = .sp,
536 .rn = .r0,
537 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),529 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
538 } },530 } },
539 });531 });
...@@ -895,17 +887,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {...@@ -895,17 +887,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
895 try table.ensureUnusedCapacity(self.gpa, additional_count);887 try table.ensureUnusedCapacity(self.gpa, additional_count);
896}888}
897889
898fn allocMem(self: *Self, inst: Air.Inst.Index, abi_size: u32, abi_align: u32) !u32 {890fn allocMem(
891 self: *Self,
892 abi_size: u32,
893 abi_align: u32,
894 maybe_inst: ?Air.Inst.Index,
895) !u32 {
896 assert(abi_size > 0);
897 assert(abi_align > 0);
898
899 if (abi_align > self.stack_align)899 if (abi_align > self.stack_align)
900 self.stack_align = abi_align;900 self.stack_align = abi_align;
901
901 // TODO find a free slot instead of always appending902 // TODO find a free slot instead of always appending
902 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;903 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
903 self.next_stack_offset = offset;904 self.next_stack_offset = offset;
904 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);905 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
905 try self.stack.putNoClobber(self.gpa, offset, .{906
906 .inst = inst,907 if (maybe_inst) |inst| {
907 .size = abi_size,908 try self.stack.putNoClobber(self.gpa, offset, .{
908 });909 .inst = inst,
910 .size = abi_size,
911 });
912 }
913
909 return offset;914 return offset;
910}915}
911916
...@@ -927,35 +932,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {...@@ -927,35 +932,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
927 };932 };
928 // TODO swap this for inst.ty.ptrAlign933 // TODO swap this for inst.ty.ptrAlign
929 const abi_align = elem_ty.abiAlignment(self.target.*);934 const abi_align = elem_ty.abiAlignment(self.target.*);
930 return self.allocMem(inst, abi_size, abi_align);935
936 return self.allocMem(abi_size, abi_align, inst);
931}937}
932938
933fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {939fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
934 const elem_ty = self.air.typeOfIndex(inst);
935 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {940 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
936 const mod = self.bin_file.options.module.?;941 const mod = self.bin_file.options.module.?;
937 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});942 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
938 };943 };
939 const abi_align = elem_ty.abiAlignment(self.target.*);944 const abi_align = elem_ty.abiAlignment(self.target.*);
940 if (abi_align > self.stack_align)
941 self.stack_align = abi_align;
942945
943 if (reg_ok) {946 if (reg_ok) {
944 // Make sure the type can fit in a register before we try to allocate one.947 // Make sure the type can fit in a register before we try to allocate one.
945 const ptr_bits = self.target.cpu.arch.ptrBitWidth();948 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
946 const ptr_bytes: u64 = @divExact(ptr_bits, 8);949 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
947 if (abi_size <= ptr_bytes) {950 if (abi_size <= ptr_bytes) {
948 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {951 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
949 return MCValue{ .register = reg };952 return MCValue{ .register = reg };
950 }953 }
951 }954 }
952 }955 }
953 const stack_offset = try self.allocMem(inst, abi_size, abi_align);956
957 const stack_offset = try self.allocMem(abi_size, abi_align, maybe_inst);
954 return MCValue{ .stack_offset = stack_offset };958 return MCValue{ .stack_offset = stack_offset };
955}959}
956960
957pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {961pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void {
958 const stack_mcv = try self.allocRegOrMem(inst, false);962 const stack_mcv = try self.allocRegOrMem(self.air.typeOfIndex(inst), false, inst);
959 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });963 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
960964
961 const reg_mcv = self.getResolvedInstValue(inst);965 const reg_mcv = self.getResolvedInstValue(inst);
...@@ -976,12 +980,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void...@@ -976,12 +980,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
976/// occupied980/// occupied
977fn spillCompareFlagsIfOccupied(self: *Self) !void {981fn spillCompareFlagsIfOccupied(self: *Self) !void {
978 if (self.cpsr_flags_inst) |inst_to_save| {982 if (self.cpsr_flags_inst) |inst_to_save| {
983 const ty = self.air.typeOfIndex(inst_to_save);
979 const mcv = self.getResolvedInstValue(inst_to_save);984 const mcv = self.getResolvedInstValue(inst_to_save);
980 const new_mcv = switch (mcv) {985 const new_mcv = switch (mcv) {
981 .cpsr_flags => try self.allocRegOrMem(inst_to_save, true),986 .cpsr_flags => try self.allocRegOrMem(ty, true, inst_to_save),
982 .register_c_flag,987 .register_c_flag,
983 .register_v_flag,988 .register_v_flag,
984 => try self.allocRegOrMem(inst_to_save, false),989 => try self.allocRegOrMem(ty, false, inst_to_save),
985 else => unreachable, // mcv doesn't occupy the compare flags990 else => unreachable, // mcv doesn't occupy the compare flags
986 };991 };
987992
...@@ -1112,10 +1117,11 @@ fn truncRegister(...@@ -1112,10 +1117,11 @@ fn truncRegister(
1112 });1117 });
1113}1118}
11141119
1120/// Asserts that both operand_ty and dest_ty are integer types
1115fn trunc(1121fn trunc(
1116 self: *Self,1122 self: *Self,
1117 maybe_inst: ?Air.Inst.Index,1123 maybe_inst: ?Air.Inst.Index,
1118 operand: MCValue,1124 operand_bind: ReadArg.Bind,
1119 operand_ty: Type,1125 operand_ty: Type,
1120 dest_ty: Type,1126 dest_ty: Type,
1121) !MCValue {1127) !MCValue {
...@@ -1123,39 +1129,38 @@ fn trunc(...@@ -1123,39 +1129,38 @@ fn trunc(
1123 const info_b = dest_ty.intInfo(self.target.*);1129 const info_b = dest_ty.intInfo(self.target.*);
11241130
1125 if (info_b.bits <= 32) {1131 if (info_b.bits <= 32) {
1126 const operand_reg = switch (operand) {1132 if (info_a.bits > 32) {
1127 .register => |r| r,1133 return self.fail("TODO load least significant word into register", .{});
1128 else => operand_reg: {1134 }
1129 if (info_a.bits <= 32) {
1130 break :operand_reg try self.copyToTmpRegister(operand_ty, operand);
1131 } else {
1132 return self.fail("TODO load least significant word into register", .{});
1133 }
1134 },
1135 };
1136 const operand_reg_lock = self.register_manager.lockReg(operand_reg);
1137 defer if (operand_reg_lock) |reg| self.register_manager.unlockReg(reg);
11381135
1139 const dest_reg = if (maybe_inst) |inst| blk: {1136 var operand_reg: Register = undefined;
1140 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1137 var dest_reg: Register = undefined;
11411138
1142 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {1139 const read_args = [_]ReadArg{
1143 break :blk operand_reg;1140 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg },
1144 } else {1141 };
1145 break :blk try self.register_manager.allocReg(inst, gp);1142 const write_args = [_]WriteArg{
1146 }1143 .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1147 } else try self.register_manager.allocReg(null, gp);1144 };
1145 try self.allocRegs(
1146 &read_args,
1147 &write_args,
1148 if (maybe_inst) |inst| .{
1149 .corresponding_inst = inst,
1150 .operand_mapping = &.{0},
1151 } else null,
1152 );
11481153
1149 switch (info_b.bits) {1154 switch (info_b.bits) {
1150 32 => {1155 32 => {
1151 try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });1156 try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
1152 return MCValue{ .register = dest_reg };
1153 },1157 },
1154 else => {1158 else => {
1155 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);1159 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
1156 return MCValue{ .register = dest_reg };
1157 },1160 },
1158 }1161 }
1162
1163 return MCValue{ .register = dest_reg };
1159 } else {1164 } else {
1160 return self.fail("TODO: truncate to ints > 32 bits", .{});1165 return self.fail("TODO: truncate to ints > 32 bits", .{});
1161 }1166 }
...@@ -1163,12 +1168,12 @@ fn trunc(...@@ -1163,12 +1168,12 @@ fn trunc(
11631168
1164fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {1169fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
1165 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1170 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1166 const operand = try self.resolveInst(ty_op.operand);1171 const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1167 const operand_ty = self.air.typeOf(ty_op.operand);1172 const operand_ty = self.air.typeOf(ty_op.operand);
1168 const dest_ty = self.air.typeOfIndex(inst);1173 const dest_ty = self.air.typeOfIndex(inst);
11691174
1170 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {1175 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
1171 break :blk try self.trunc(inst, operand, operand_ty, dest_ty);1176 break :blk try self.trunc(inst, operand_bind, operand_ty, dest_ty);
1172 };1177 };
11731178
1174 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });1179 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -1184,29 +1189,32 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {...@@ -1184,29 +1189,32 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
1184fn airNot(self: *Self, inst: Air.Inst.Index) !void {1189fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1185 const ty_op = self.air.instructions.items(.data)[inst].ty_op;1190 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1186 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1191 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1187 const operand = try self.resolveInst(ty_op.operand);1192 const operand_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1188 const operand_ty = self.air.typeOf(ty_op.operand);1193 const operand_ty = self.air.typeOf(ty_op.operand);
1189 switch (operand) {1194 switch (try operand_bind.resolveToMcv(self)) {
1190 .dead => unreachable,1195 .dead => unreachable,
1191 .unreach => unreachable,1196 .unreach => unreachable,
1192 .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() },1197 .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() },
1193 else => {1198 else => {
1194 switch (operand_ty.zigTypeTag()) {1199 switch (operand_ty.zigTypeTag()) {
1195 .Bool => {1200 .Bool => {
1196 const op_reg = switch (operand) {1201 var op_reg: Register = undefined;
1197 .register => |r| r,1202 var dest_reg: Register = undefined;
1198 else => try self.copyToTmpRegister(operand_ty, operand),
1199 };
1200 const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1201 defer self.register_manager.unlockReg(op_reg_lock);
12021203
1203 const dest_reg = blk: {1204 const read_args = [_]ReadArg{
1204 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {1205 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
1205 break :blk op_reg;
1206 }
1207
1208 break :blk try self.register_manager.allocReg(null, gp);
1209 };1206 };
1207 const write_args = [_]WriteArg{
1208 .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1209 };
1210 try self.allocRegs(
1211 &read_args,
1212 &write_args,
1213 ReuseMetadata{
1214 .corresponding_inst = inst,
1215 .operand_mapping = &.{0},
1216 },
1217 );
12101218
1211 _ = try self.addInst(.{1219 _ = try self.addInst(.{
1212 .tag = .eor,1220 .tag = .eor,
...@@ -1223,26 +1231,28 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1223,26 +1231,28 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1223 .Int => {1231 .Int => {
1224 const int_info = operand_ty.intInfo(self.target.*);1232 const int_info = operand_ty.intInfo(self.target.*);
1225 if (int_info.bits <= 32) {1233 if (int_info.bits <= 32) {
1226 const op_reg = switch (operand) {1234 var op_reg: Register = undefined;
1227 .register => |r| r,1235 var dest_reg: Register = undefined;
1228 else => try self.copyToTmpRegister(operand_ty, operand),
1229 };
1230 const op_reg_lock = self.register_manager.lockRegAssumeUnused(op_reg);
1231 defer self.register_manager.unlockReg(op_reg_lock);
1232
1233 const dest_reg = blk: {
1234 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1235 break :blk op_reg;
1236 }
12371236
1238 break :blk try self.register_manager.allocReg(null, gp);1237 const read_args = [_]ReadArg{
1238 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
1239 };1239 };
1240 const write_args = [_]WriteArg{
1241 .{ .ty = operand_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1242 };
1243 try self.allocRegs(
1244 &read_args,
1245 &write_args,
1246 ReuseMetadata{
1247 .corresponding_inst = inst,
1248 .operand_mapping = &.{0},
1249 },
1250 );
12401251
1241 _ = try self.addInst(.{1252 _ = try self.addInst(.{
1242 .tag = .mvn,1253 .tag = .mvn,
1243 .data = .{ .rr_op = .{1254 .data = .{ .r_op_mov = .{
1244 .rd = dest_reg,1255 .rd = dest_reg,
1245 .rn = undefined,
1246 .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),1256 .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
1247 } },1257 } },
1248 });1258 });
...@@ -1267,11 +1277,11 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {...@@ -1267,11 +1277,11 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
1267fn minMax(1277fn minMax(
1268 self: *Self,1278 self: *Self,
1269 tag: Air.Inst.Tag,1279 tag: Air.Inst.Tag,
1270 maybe_inst: ?Air.Inst.Index,1280 lhs_bind: ReadArg.Bind,
1271 lhs: MCValue,1281 rhs_bind: ReadArg.Bind,
1272 rhs: MCValue,
1273 lhs_ty: Type,1282 lhs_ty: Type,
1274 rhs_ty: Type,1283 rhs_ty: Type,
1284 maybe_inst: ?Air.Inst.Index,
1275) !MCValue {1285) !MCValue {
1276 switch (lhs_ty.zigTypeTag()) {1286 switch (lhs_ty.zigTypeTag()) {
1277 .Float => return self.fail("TODO ARM min/max on floats", .{}),1287 .Float => return self.fail("TODO ARM min/max on floats", .{}),
...@@ -1281,34 +1291,25 @@ fn minMax(...@@ -1281,34 +1291,25 @@ fn minMax(
1281 assert(lhs_ty.eql(rhs_ty, mod));1291 assert(lhs_ty.eql(rhs_ty, mod));
1282 const int_info = lhs_ty.intInfo(self.target.*);1292 const int_info = lhs_ty.intInfo(self.target.*);
1283 if (int_info.bits <= 32) {1293 if (int_info.bits <= 32) {
1284 const lhs_is_register = lhs == .register;1294 var lhs_reg: Register = undefined;
1285 const rhs_is_register = rhs == .register;1295 var rhs_reg: Register = undefined;
1296 var dest_reg: Register = undefined;
12861297
1287 const lhs_reg = switch (lhs) {1298 const read_args = [_]ReadArg{
1288 .register => |r| r,1299 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1289 else => try self.copyToTmpRegister(lhs_ty, lhs),1300 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1290 };1301 };
1291 const lhs_reg_lock = self.register_manager.lockReg(lhs_reg);1302 const write_args = [_]WriteArg{
1292 defer if (lhs_reg_lock) |reg| self.register_manager.unlockReg(reg);1303 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1293
1294 const rhs_reg = switch (rhs) {
1295 .register => |r| r,
1296 else => try self.copyToTmpRegister(rhs_ty, rhs),
1297 };1304 };
1298 const rhs_reg_lock = self.register_manager.lockReg(rhs_reg);1305 try self.allocRegs(
1299 defer if (rhs_reg_lock) |reg| self.register_manager.unlockReg(reg);1306 &read_args,
13001307 &write_args,
1301 const dest_reg = if (maybe_inst) |inst| blk: {1308 if (maybe_inst) |inst| .{
1302 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1309 .corresponding_inst = inst,
13031310 .operand_mapping = &.{ 0, 1 },
1304 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {1311 } else null,
1305 break :blk lhs_reg;1312 );
1306 } else if (rhs_is_register and self.reuseOperand(inst, bin_op.rhs, 1, rhs)) {
1307 break :blk rhs_reg;
1308 } else {
1309 break :blk try self.register_manager.allocReg(inst, gp);
1310 }
1311 } else try self.register_manager.allocReg(null, gp);
13121313
1313 // lhs == reg should have been checked by airMinMax1314 // lhs == reg should have been checked by airMinMax
1314 //1315 //
...@@ -1318,7 +1319,13 @@ fn minMax(...@@ -1318,7 +1319,13 @@ fn minMax(
1318 // register.1319 // register.
1319 assert(lhs_reg != rhs_reg); // see note above1320 assert(lhs_reg != rhs_reg); // see note above
13201321
1321 _ = try self.binOpRegister(.cmp, .{ .register = lhs_reg }, .{ .register = rhs_reg }, lhs_ty, rhs_ty, null);1322 _ = try self.addInst(.{
1323 .tag = .cmp,
1324 .data = .{ .r_op_cmp = .{
1325 .rn = lhs_reg,
1326 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1327 } },
1328 });
13221329
1323 const cond_choose_lhs: Condition = switch (tag) {1330 const cond_choose_lhs: Condition = switch (tag) {
1324 .max => switch (int_info.signedness) {1331 .max => switch (int_info.signedness) {
...@@ -1337,9 +1344,8 @@ fn minMax(...@@ -1337,9 +1344,8 @@ fn minMax(
1337 _ = try self.addInst(.{1344 _ = try self.addInst(.{
1338 .tag = .mov,1345 .tag = .mov,
1339 .cond = cond_choose_lhs,1346 .cond = cond_choose_lhs,
1340 .data = .{ .rr_op = .{1347 .data = .{ .r_op_mov = .{
1341 .rd = dest_reg,1348 .rd = dest_reg,
1342 .rn = .r0,
1343 .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none),1349 .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none),
1344 } },1350 } },
1345 });1351 });
...@@ -1348,9 +1354,8 @@ fn minMax(...@@ -1348,9 +1354,8 @@ fn minMax(
1348 _ = try self.addInst(.{1354 _ = try self.addInst(.{
1349 .tag = .mov,1355 .tag = .mov,
1350 .cond = cond_choose_rhs,1356 .cond = cond_choose_rhs,
1351 .data = .{ .rr_op = .{1357 .data = .{ .r_op_mov = .{
1352 .rd = dest_reg,1358 .rd = dest_reg,
1353 .rn = .r0,
1354 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),1359 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
1355 } },1360 } },
1356 });1361 });
...@@ -1368,15 +1373,17 @@ fn minMax(...@@ -1368,15 +1373,17 @@ fn minMax(
1368fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {1373fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
1369 const tag = self.air.instructions.items(.tag)[inst];1374 const tag = self.air.instructions.items(.tag)[inst];
1370 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1375 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1371 const lhs = try self.resolveInst(bin_op.lhs);
1372 const rhs = try self.resolveInst(bin_op.rhs);
1373 const lhs_ty = self.air.typeOf(bin_op.lhs);1376 const lhs_ty = self.air.typeOf(bin_op.lhs);
1374 const rhs_ty = self.air.typeOf(bin_op.rhs);1377 const rhs_ty = self.air.typeOf(bin_op.rhs);
13751378
1376 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1379 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1380 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1381 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1382
1383 const lhs = try self.resolveInst(bin_op.lhs);
1377 if (bin_op.lhs == bin_op.rhs) break :result lhs;1384 if (bin_op.lhs == bin_op.rhs) break :result lhs;
13781385
1379 break :result try self.minMax(tag, inst, lhs, rhs, lhs_ty, rhs_ty);1386 break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1380 };1387 };
1381 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1388 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1382}1389}
...@@ -1390,7 +1397,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1390,7 +1397,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
1390 const len = try self.resolveInst(bin_op.rhs);1397 const len = try self.resolveInst(bin_op.rhs);
1391 const len_ty = self.air.typeOf(bin_op.rhs);1398 const len_ty = self.air.typeOf(bin_op.rhs);
13921399
1393 const stack_offset = try self.allocMem(inst, 8, 4);1400 const stack_offset = try self.allocMem(8, 4, inst);
1394 try self.genSetStack(ptr_ty, stack_offset, ptr);1401 try self.genSetStack(ptr_ty, stack_offset, ptr);
1395 try self.genSetStack(len_ty, stack_offset - 4, len);1402 try self.genSetStack(len_ty, stack_offset - 4, len);
1396 break :result MCValue{ .stack_offset = stack_offset };1403 break :result MCValue{ .stack_offset = stack_offset };
...@@ -1400,38 +1407,65 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -1400,38 +1407,65 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
14001407
1401fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {1408fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1402 const bin_op = self.air.instructions.items(.data)[inst].bin_op;1409 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1403 const lhs = try self.resolveInst(bin_op.lhs);
1404 const rhs = try self.resolveInst(bin_op.rhs);
1405 const lhs_ty = self.air.typeOf(bin_op.lhs);1410 const lhs_ty = self.air.typeOf(bin_op.lhs);
1406 const rhs_ty = self.air.typeOf(bin_op.rhs);1411 const rhs_ty = self.air.typeOf(bin_op.rhs);
14071412
1408 const result: MCValue = if (self.liveness.isUnused(inst))1413 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1409 .dead1414 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1410 else1415 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1411 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{1416
1412 .lhs = bin_op.lhs,1417 break :result switch (tag) {
1413 .rhs = bin_op.rhs,1418 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1414 .inst = inst,1419 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1415 });1420
1421 .mul => try self.mul(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1422
1423 .div_float => try self.divFloat(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1424
1425 .div_trunc => try self.div(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1426 .div_floor => try self.div(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1427
1428 .div_exact => try self.divExact(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1429
1430 .rem => try self.rem(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1431
1432 .mod => try self.modulo(lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1433
1434 .addwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1435 .subwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1436 .mulwrap => try self.wrappingArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1437
1438 .bit_and => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1439 .bit_or => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1440 .xor => try self.bitwise(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1441
1442 .shl_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1443 .shr_exact => try self.shiftExact(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1444
1445 .shl => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1446 .shr => try self.shiftNormal(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1447
1448 .bool_and => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1449 .bool_or => try self.booleanOp(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1450
1451 else => unreachable,
1452 };
1453 };
1416 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1454 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1417}1455}
14181456
1419fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {1457fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
1420 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1458 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1421 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;1459 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
1422 const lhs = try self.resolveInst(bin_op.lhs);
1423 const rhs = try self.resolveInst(bin_op.rhs);
1424 const lhs_ty = self.air.typeOf(bin_op.lhs);1460 const lhs_ty = self.air.typeOf(bin_op.lhs);
1425 const rhs_ty = self.air.typeOf(bin_op.rhs);1461 const rhs_ty = self.air.typeOf(bin_op.rhs);
14261462
1427 const result: MCValue = if (self.liveness.isUnused(inst))1463 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1428 .dead1464 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1429 else1465 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1430 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{1466
1431 .lhs = bin_op.lhs,1467 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1432 .rhs = bin_op.rhs,1468 };
1433 .inst = inst,
1434 });
1435 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });1469 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
1436}1470}
14371471
...@@ -1458,8 +1492,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1458,8 +1492,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1458 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;1492 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
1459 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;1493 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1460 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {1494 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1461 const lhs = try self.resolveInst(extra.lhs);1495 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1462 const rhs = try self.resolveInst(extra.rhs);1496 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1463 const lhs_ty = self.air.typeOf(extra.lhs);1497 const lhs_ty = self.air.typeOf(extra.lhs);
1464 const rhs_ty = self.air.typeOf(extra.rhs);1498 const rhs_ty = self.air.typeOf(extra.rhs);
14651499
...@@ -1475,17 +1509,16 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1475,17 +1509,16 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1475 assert(lhs_ty.eql(rhs_ty, mod));1509 assert(lhs_ty.eql(rhs_ty, mod));
1476 const int_info = lhs_ty.intInfo(self.target.*);1510 const int_info = lhs_ty.intInfo(self.target.*);
1477 if (int_info.bits < 32) {1511 if (int_info.bits < 32) {
1478 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);1512 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
14791513
1480 try self.spillCompareFlagsIfOccupied();1514 try self.spillCompareFlagsIfOccupied();
1481 self.cpsr_flags_inst = null;
14821515
1483 const base_tag: Air.Inst.Tag = switch (tag) {1516 const base_tag: Air.Inst.Tag = switch (tag) {
1484 .add_with_overflow => .add,1517 .add_with_overflow => .add,
1485 .sub_with_overflow => .sub,1518 .sub_with_overflow => .sub,
1486 else => unreachable,1519 else => unreachable,
1487 };1520 };
1488 const dest = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);1521 const dest = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1489 const dest_reg = dest.register;1522 const dest_reg = dest.register;
1490 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);1523 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
1491 defer self.register_manager.unlockReg(dest_reg_lock);1524 defer self.register_manager.unlockReg(dest_reg_lock);
...@@ -1498,25 +1531,34 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1498,25 +1531,34 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
1498 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);1531 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
14991532
1500 // cmp dest, truncated1533 // cmp dest, truncated
1501 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null);1534 _ = try self.addInst(.{
1535 .tag = .cmp,
1536 .data = .{ .r_op_cmp = .{
1537 .rn = dest_reg,
1538 .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
1539 } },
1540 });
15021541
1503 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });1542 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1504 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });1543 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
15051544
1506 break :result MCValue{ .stack_offset = stack_offset };1545 break :result MCValue{ .stack_offset = stack_offset };
1507 } else if (int_info.bits == 32) {1546 } else if (int_info.bits == 32) {
1547 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
1548 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
1549
1508 // Only say yes if the operation is1550 // Only say yes if the operation is
1509 // commutative, i.e. we can swap both of the1551 // commutative, i.e. we can swap both of the
1510 // operands1552 // operands
1511 const lhs_immediate_ok = switch (tag) {1553 const lhs_immediate_ok = switch (tag) {
1512 .add_with_overflow => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,1554 .add_with_overflow => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
1513 .sub_with_overflow => false,1555 .sub_with_overflow => false,
1514 else => unreachable,1556 else => unreachable,
1515 };1557 };
1516 const rhs_immediate_ok = switch (tag) {1558 const rhs_immediate_ok = switch (tag) {
1517 .add_with_overflow,1559 .add_with_overflow,
1518 .sub_with_overflow,1560 .sub_with_overflow,
1519 => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,1561 => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
1520 else => unreachable,1562 else => unreachable,
1521 };1563 };
15221564
...@@ -1531,12 +1573,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1531,12 +1573,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
15311573
1532 const dest = blk: {1574 const dest = blk: {
1533 if (rhs_immediate_ok) {1575 if (rhs_immediate_ok) {
1534 break :blk try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, null);1576 break :blk try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, null);
1535 } else if (lhs_immediate_ok) {1577 } else if (lhs_immediate_ok) {
1536 // swap lhs and rhs1578 // swap lhs and rhs
1537 break :blk try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, null);1579 break :blk try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, null);
1538 } else {1580 } else {
1539 break :blk try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, null);1581 break :blk try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1540 }1582 }
1541 };1583 };
15421584
...@@ -1563,8 +1605,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1563,8 +1605,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1563 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;1605 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1564 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });1606 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1565 const result: MCValue = result: {1607 const result: MCValue = result: {
1566 const lhs = try self.resolveInst(extra.lhs);1608 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1567 const rhs = try self.resolveInst(extra.rhs);1609 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1568 const lhs_ty = self.air.typeOf(extra.lhs);1610 const lhs_ty = self.air.typeOf(extra.lhs);
1569 const rhs_ty = self.air.typeOf(extra.rhs);1611 const rhs_ty = self.air.typeOf(extra.rhs);
15701612
...@@ -1580,17 +1622,16 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1580,17 +1622,16 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1580 assert(lhs_ty.eql(rhs_ty, mod));1622 assert(lhs_ty.eql(rhs_ty, mod));
1581 const int_info = lhs_ty.intInfo(self.target.*);1623 const int_info = lhs_ty.intInfo(self.target.*);
1582 if (int_info.bits <= 16) {1624 if (int_info.bits <= 16) {
1583 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);1625 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
15841626
1585 try self.spillCompareFlagsIfOccupied();1627 try self.spillCompareFlagsIfOccupied();
1586 self.cpsr_flags_inst = null;
15871628
1588 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {1629 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1589 .signed => .smulbb,1630 .signed => .smulbb,
1590 .unsigned => .mul,1631 .unsigned => .mul,
1591 };1632 };
15921633
1593 const dest = try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, null);1634 const dest = try self.binOpRegisterNew(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, null);
1594 const dest_reg = dest.register;1635 const dest_reg = dest.register;
1595 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);1636 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
1596 defer self.register_manager.unlockReg(dest_reg_lock);1637 defer self.register_manager.unlockReg(dest_reg_lock);
...@@ -1603,62 +1644,48 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1603,62 +1644,48 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1603 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);1644 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
16041645
1605 // cmp dest, truncated1646 // cmp dest, truncated
1606 _ = try self.binOp(.cmp_eq, dest, .{ .register = truncated_reg }, Type.usize, Type.usize, null);1647 _ = try self.addInst(.{
1648 .tag = .cmp,
1649 .data = .{ .r_op_cmp = .{
1650 .rn = dest_reg,
1651 .op = Instruction.Operand.reg(truncated_reg, Instruction.Operand.Shift.none),
1652 } },
1653 });
16071654
1608 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });1655 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
1609 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });1656 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
16101657
1611 break :result MCValue{ .stack_offset = stack_offset };1658 break :result MCValue{ .stack_offset = stack_offset };
1612 } else if (int_info.bits <= 32) {1659 } else if (int_info.bits <= 32) {
1613 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);1660 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
16141661
1615 try self.spillCompareFlagsIfOccupied();1662 try self.spillCompareFlagsIfOccupied();
1616 self.cpsr_flags_inst = null;
16171663
1618 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {1664 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1619 .signed => .smull,1665 .signed => .smull,
1620 .unsigned => .umull,1666 .unsigned => .umull,
1621 };1667 };
16221668
1623 // TODO extract umull etc. to binOpTwoRegister1669 var lhs_reg: Register = undefined;
1624 // once MCValue.rr is implemented1670 var rhs_reg: Register = undefined;
1625 const lhs_is_register = lhs == .register;1671 var rdhi: Register = undefined;
1626 const rhs_is_register = rhs == .register;1672 var rdlo: Register = undefined;
16271673 var truncated_reg: Register = undefined;
1628 const lhs_lock: ?RegisterLock = if (lhs_is_register)
1629 self.register_manager.lockReg(lhs.register)
1630 else
1631 null;
1632 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
1633
1634 const lhs_reg = if (lhs_is_register)
1635 lhs.register
1636 else
1637 try self.register_manager.allocReg(null, gp);
1638 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
1639 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
1640
1641 const rhs_reg = if (rhs_is_register)
1642 rhs.register
1643 else
1644 try self.register_manager.allocReg(null, gp);
1645 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
1646 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
1647
1648 const dest_regs = try self.register_manager.allocRegs(2, .{ null, null }, gp);
1649 const dest_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dest_regs);
1650 defer for (dest_regs_locks) |reg| {
1651 self.register_manager.unlockReg(reg);
1652 };
1653 const rdlo = dest_regs[0];
1654 const rdhi = dest_regs[1];
1655
1656 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
1657 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
16581674
1659 const truncated_reg = try self.register_manager.allocReg(null, gp);1675 const read_args = [_]ReadArg{
1660 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);1676 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1661 defer self.register_manager.unlockReg(truncated_reg_lock);1677 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1678 };
1679 const write_args = [_]WriteArg{
1680 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdhi },
1681 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &rdlo },
1682 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &truncated_reg },
1683 };
1684 try self.allocRegs(
1685 &read_args,
1686 &write_args,
1687 null,
1688 );
16621689
1663 _ = try self.addInst(.{1690 _ = try self.addInst(.{
1664 .tag = base_tag,1691 .tag = base_tag,
...@@ -1677,14 +1704,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1677,14 +1704,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1677 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });1704 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
16781705
1679 // cmp truncated, rdlo1706 // cmp truncated, rdlo
1680 _ = try self.binOp(.cmp_eq, .{ .register = truncated_reg }, .{ .register = rdlo }, Type.usize, Type.usize, null);1707 _ = try self.addInst(.{
1708 .tag = .cmp,
1709 .data = .{ .r_op_cmp = .{
1710 .rn = truncated_reg,
1711 .op = Instruction.Operand.reg(rdlo, Instruction.Operand.Shift.none),
1712 } },
1713 });
16811714
1682 // mov rdlo, #01715 // mov rdlo, #0
1683 _ = try self.addInst(.{1716 _ = try self.addInst(.{
1684 .tag = .mov,1717 .tag = .mov,
1685 .data = .{ .rr_op = .{1718 .data = .{ .r_op_mov = .{
1686 .rd = rdlo,1719 .rd = rdlo,
1687 .rn = .r0,
1688 .op = Instruction.Operand.fromU32(0).?,1720 .op = Instruction.Operand.fromU32(0).?,
1689 } },1721 } },
1690 });1722 });
...@@ -1693,23 +1725,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1693,23 +1725,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1693 _ = try self.addInst(.{1725 _ = try self.addInst(.{
1694 .tag = .mov,1726 .tag = .mov,
1695 .cond = .ne,1727 .cond = .ne,
1696 .data = .{ .rr_op = .{1728 .data = .{ .r_op_mov = .{
1697 .rd = rdlo,1729 .rd = rdlo,
1698 .rn = .r0,
1699 .op = Instruction.Operand.fromU32(1).?,1730 .op = Instruction.Operand.fromU32(1).?,
1700 } },1731 } },
1701 });1732 });
17021733
1703 // cmp rdhi, #01734 // cmp rdhi, #0
1704 _ = try self.binOp(.cmp_eq, .{ .register = rdhi }, .{ .immediate = 0 }, Type.usize, Type.usize, null);1735 _ = try self.addInst(.{
1736 .tag = .cmp,
1737 .data = .{ .r_op_cmp = .{
1738 .rn = rdhi,
1739 .op = Instruction.Operand.fromU32(0).?,
1740 } },
1741 });
17051742
1706 // movne rdlo, #11743 // movne rdlo, #1
1707 _ = try self.addInst(.{1744 _ = try self.addInst(.{
1708 .tag = .mov,1745 .tag = .mov,
1709 .cond = .ne,1746 .cond = .ne,
1710 .data = .{ .rr_op = .{1747 .data = .{ .r_op_mov = .{
1711 .rd = rdlo,1748 .rd = rdlo,
1712 .rn = .r0,
1713 .op = Instruction.Operand.fromU32(1).?,1749 .op = Instruction.Operand.fromU32(1).?,
1714 } },1750 } },
1715 });1751 });
...@@ -1733,8 +1769,6 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1733,8 +1769,6 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1733 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;1769 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
1734 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });1770 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
1735 const result: MCValue = result: {1771 const result: MCValue = result: {
1736 const lhs = try self.resolveInst(extra.lhs);
1737 const rhs = try self.resolveInst(extra.rhs);
1738 const lhs_ty = self.air.typeOf(extra.lhs);1772 const lhs_ty = self.air.typeOf(extra.lhs);
1739 const rhs_ty = self.air.typeOf(extra.rhs);1773 const rhs_ty = self.air.typeOf(extra.rhs);
17401774
...@@ -1748,30 +1782,109 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {...@@ -1748,30 +1782,109 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
1748 .Int => {1782 .Int => {
1749 const int_info = lhs_ty.intInfo(self.target.*);1783 const int_info = lhs_ty.intInfo(self.target.*);
1750 if (int_info.bits <= 32) {1784 if (int_info.bits <= 32) {
1751 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);1785 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
1752
1753 const lhs_lock: ?RegisterLock = if (lhs == .register)
1754 self.register_manager.lockRegAssumeUnused(lhs.register)
1755 else
1756 null;
1757 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
17581786
1759 try self.spillCompareFlagsIfOccupied();1787 try self.spillCompareFlagsIfOccupied();
1760 self.cpsr_flags_inst = null;
17611788
1762 // lsl dest, lhs, rhs1789 const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1763 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);1790 .signed => Mir.Inst.Tag.asr,
1764 const dest_reg = dest.register;1791 .unsigned => Mir.Inst.Tag.lsr,
1765 const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg);1792 };
1766 defer self.register_manager.unlockReg(dest_lock);1793
1794 var lhs_reg: Register = undefined;
1795 var rhs_reg: Register = undefined;
1796 var dest_reg: Register = undefined;
1797 var reconstructed_reg: Register = undefined;
1798
1799 const rhs_mcv = try self.resolveInst(extra.rhs);
1800 const rhs_immediate_ok = rhs_mcv == .immediate and Instruction.Operand.fromU32(rhs_mcv.immediate) != null;
1801
1802 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1803 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
1804
1805 if (rhs_immediate_ok) {
1806 const read_args = [_]ReadArg{
1807 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1808 };
1809 const write_args = [_]WriteArg{
1810 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1811 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
1812 };
1813 try self.allocRegs(
1814 &read_args,
1815 &write_args,
1816 null,
1817 );
1818
1819 // lsl dest, lhs, rhs
1820 _ = try self.addInst(.{
1821 .tag = .lsl,
1822 .data = .{ .rr_shift = .{
1823 .rd = dest_reg,
1824 .rm = lhs_reg,
1825 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_mcv.immediate)),
1826 } },
1827 });
1828
1829 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
1830
1831 // asr/lsr reconstructed, dest, rhs
1832 _ = try self.addInst(.{
1833 .tag = shr_mir_tag,
1834 .data = .{ .rr_shift = .{
1835 .rd = reconstructed_reg,
1836 .rm = dest_reg,
1837 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_mcv.immediate)),
1838 } },
1839 });
1840 } else {
1841 const read_args = [_]ReadArg{
1842 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1843 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
1844 };
1845 const write_args = [_]WriteArg{
1846 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1847 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &reconstructed_reg },
1848 };
1849 try self.allocRegs(
1850 &read_args,
1851 &write_args,
1852 null,
1853 );
1854
1855 // lsl dest, lhs, rhs
1856 _ = try self.addInst(.{
1857 .tag = .lsl,
1858 .data = .{ .rr_shift = .{
1859 .rd = dest_reg,
1860 .rm = lhs_reg,
1861 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1862 } },
1863 });
1864
1865 try self.truncRegister(dest_reg, dest_reg, int_info.signedness, int_info.bits);
17671866
1768 // asr/lsr reconstructed, dest, rhs1867 // asr/lsr reconstructed, dest, rhs
1769 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);1868 _ = try self.addInst(.{
1869 .tag = shr_mir_tag,
1870 .data = .{ .rr_shift = .{
1871 .rd = reconstructed_reg,
1872 .rm = dest_reg,
1873 .shift_amount = Instruction.ShiftAmount.reg(rhs_reg),
1874 } },
1875 });
1876 }
17701877
1771 // cmp lhs, reconstructed1878 // cmp lhs, reconstructed
1772 _ = try self.binOp(.cmp_eq, lhs, reconstructed, lhs_ty, lhs_ty, null);1879 _ = try self.addInst(.{
1880 .tag = .cmp,
1881 .data = .{ .r_op_cmp = .{
1882 .rn = lhs_reg,
1883 .op = Instruction.Operand.reg(reconstructed_reg, Instruction.Operand.Shift.none),
1884 } },
1885 });
17731886
1774 try self.genSetStack(lhs_ty, stack_offset, dest);1887 try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg });
1775 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });1888 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
17761889
1777 break :result MCValue{ .stack_offset = stack_offset };1890 break :result MCValue{ .stack_offset = stack_offset };
...@@ -1826,19 +1939,57 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {...@@ -1826,19 +1939,57 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
1826}1939}
18271940
1828/// Given an error union, returns the error1941/// Given an error union, returns the error
1829fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {1942fn errUnionErr(
1943 self: *Self,
1944 error_union_bind: ReadArg.Bind,
1945 error_union_ty: Type,
1946 maybe_inst: ?Air.Inst.Index,
1947) !MCValue {
1830 const err_ty = error_union_ty.errorUnionSet();1948 const err_ty = error_union_ty.errorUnionSet();
1831 const payload_ty = error_union_ty.errorUnionPayload();1949 const payload_ty = error_union_ty.errorUnionPayload();
1832 if (err_ty.errorSetIsEmpty()) {1950 if (err_ty.errorSetIsEmpty()) {
1833 return MCValue{ .immediate = 0 };1951 return MCValue{ .immediate = 0 };
1834 }1952 }
1835 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {1953 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1836 return error_union_mcv;1954 return try error_union_bind.resolveToMcv(self);
1837 }1955 }
18381956
1839 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));1957 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
1840 switch (error_union_mcv) {1958 switch (try error_union_bind.resolveToMcv(self)) {
1841 .register => return self.fail("TODO errUnionErr for registers", .{}),1959 .register => {
1960 var operand_reg: Register = undefined;
1961 var dest_reg: Register = undefined;
1962
1963 const read_args = [_]ReadArg{
1964 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
1965 };
1966 const write_args = [_]WriteArg{
1967 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
1968 };
1969 try self.allocRegs(
1970 &read_args,
1971 &write_args,
1972 if (maybe_inst) |inst| .{
1973 .corresponding_inst = inst,
1974 .operand_mapping = &.{0},
1975 } else null,
1976 );
1977
1978 const err_bit_offset = err_offset * 8;
1979 const err_bit_size = @intCast(u32, err_ty.abiSize(self.target.*)) * 8;
1980
1981 _ = try self.addInst(.{
1982 .tag = .ubfx, // errors are unsigned integers
1983 .data = .{ .rr_lsb_width = .{
1984 .rd = dest_reg,
1985 .rn = operand_reg,
1986 .lsb = @intCast(u5, err_bit_offset),
1987 .width = @intCast(u6, err_bit_size),
1988 } },
1989 });
1990
1991 return MCValue{ .register = dest_reg };
1992 },
1842 .stack_argument_offset => |off| {1993 .stack_argument_offset => |off| {
1843 return MCValue{ .stack_argument_offset = off + err_offset };1994 return MCValue{ .stack_argument_offset = off + err_offset };
1844 },1995 },
...@@ -1855,27 +2006,66 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV...@@ -1855,27 +2006,66 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
1855fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {2006fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
1856 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2007 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1857 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2009 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1858 const error_union_ty = self.air.typeOf(ty_op.operand);2010 const error_union_ty = self.air.typeOf(ty_op.operand);
1859 const mcv = try self.resolveInst(ty_op.operand);2011
1860 break :result try self.errUnionErr(mcv, error_union_ty);2012 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
1861 };2013 };
1862 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2014 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1863}2015}
18642016
1865/// Given an error union, returns the payload2017/// Given an error union, returns the payload
1866fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCValue {2018fn errUnionPayload(
2019 self: *Self,
2020 error_union_bind: ReadArg.Bind,
2021 error_union_ty: Type,
2022 maybe_inst: ?Air.Inst.Index,
2023) !MCValue {
1867 const err_ty = error_union_ty.errorUnionSet();2024 const err_ty = error_union_ty.errorUnionSet();
1868 const payload_ty = error_union_ty.errorUnionPayload();2025 const payload_ty = error_union_ty.errorUnionPayload();
1869 if (err_ty.errorSetIsEmpty()) {2026 if (err_ty.errorSetIsEmpty()) {
1870 return error_union_mcv;2027 return try error_union_bind.resolveToMcv(self);
1871 }2028 }
1872 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {2029 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1873 return MCValue.none;2030 return MCValue.none;
1874 }2031 }
18752032
1876 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));2033 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
1877 switch (error_union_mcv) {2034 switch (try error_union_bind.resolveToMcv(self)) {
1878 .register => return self.fail("TODO errUnionPayload for registers", .{}),2035 .register => {
2036 var operand_reg: Register = undefined;
2037 var dest_reg: Register = undefined;
2038
2039 const read_args = [_]ReadArg{
2040 .{ .ty = error_union_ty, .bind = error_union_bind, .class = gp, .reg = &operand_reg },
2041 };
2042 const write_args = [_]WriteArg{
2043 .{ .ty = err_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2044 };
2045 try self.allocRegs(
2046 &read_args,
2047 &write_args,
2048 if (maybe_inst) |inst| .{
2049 .corresponding_inst = inst,
2050 .operand_mapping = &.{0},
2051 } else null,
2052 );
2053
2054 const payload_bit_offset = payload_offset * 8;
2055 const payload_bit_size = @intCast(u32, payload_ty.abiSize(self.target.*)) * 8;
2056
2057 _ = try self.addInst(.{
2058 .tag = if (payload_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx,
2059 .data = .{ .rr_lsb_width = .{
2060 .rd = dest_reg,
2061 .rn = operand_reg,
2062 .lsb = @intCast(u5, payload_bit_offset),
2063 .width = @intCast(u6, payload_bit_size),
2064 } },
2065 });
2066
2067 return MCValue{ .register = dest_reg };
2068 },
1879 .stack_argument_offset => |off| {2069 .stack_argument_offset => |off| {
1880 return MCValue{ .stack_argument_offset = off + payload_offset };2070 return MCValue{ .stack_argument_offset = off + payload_offset };
1881 },2071 },
...@@ -1892,9 +2082,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)...@@ -1892,9 +2082,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
1892fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {2082fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
1893 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2083 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1894 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2084 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2085 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
1895 const error_union_ty = self.air.typeOf(ty_op.operand);2086 const error_union_ty = self.air.typeOf(ty_op.operand);
1896 const error_union = try self.resolveInst(ty_op.operand);2087
1897 break :result try self.errUnionPayload(error_union, error_union_ty);2088 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
1898 };2089 };
1899 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2090 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
1900}2091}
...@@ -1938,17 +2129,18 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {...@@ -1938,17 +2129,18 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
1938 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2129 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1939 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2130 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1940 const error_union_ty = self.air.getRefType(ty_op.ty);2131 const error_union_ty = self.air.getRefType(ty_op.ty);
2132 const error_ty = error_union_ty.errorUnionSet();
1941 const payload_ty = error_union_ty.errorUnionPayload();2133 const payload_ty = error_union_ty.errorUnionPayload();
1942 const operand = try self.resolveInst(ty_op.operand);2134 const operand = try self.resolveInst(ty_op.operand);
1943 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;2135 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;
19442136
1945 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));2137 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
1946 const abi_align = error_union_ty.abiAlignment(self.target.*);2138 const abi_align = error_union_ty.abiAlignment(self.target.*);
1947 const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align));2139 const stack_offset = @intCast(u32, try self.allocMem(abi_size, abi_align, inst));
1948 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);2140 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1949 const err_off = errUnionErrorOffset(payload_ty, self.target.*);2141 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
1950 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand);2142 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand);
1951 try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 });2143 try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 });
19522144
1953 break :result MCValue{ .stack_offset = stack_offset };2145 break :result MCValue{ .stack_offset = stack_offset };
1954 };2146 };
...@@ -1960,16 +2152,17 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {...@@ -1960,16 +2152,17 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
1960 const ty_op = self.air.instructions.items(.data)[inst].ty_op;2152 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1961 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2153 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1962 const error_union_ty = self.air.getRefType(ty_op.ty);2154 const error_union_ty = self.air.getRefType(ty_op.ty);
2155 const error_ty = error_union_ty.errorUnionSet();
1963 const payload_ty = error_union_ty.errorUnionPayload();2156 const payload_ty = error_union_ty.errorUnionPayload();
1964 const operand = try self.resolveInst(ty_op.operand);2157 const operand = try self.resolveInst(ty_op.operand);
1965 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;2158 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;
19662159
1967 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));2160 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
1968 const abi_align = error_union_ty.abiAlignment(self.target.*);2161 const abi_align = error_union_ty.abiAlignment(self.target.*);
1969 const stack_offset = @intCast(u32, try self.allocMem(inst, abi_size, abi_align));2162 const stack_offset = @intCast(u32, try self.allocMem(abi_size, abi_align, inst));
1970 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);2163 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
1971 const err_off = errUnionErrorOffset(payload_ty, self.target.*);2164 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
1972 try self.genSetStack(Type.anyerror, stack_offset - @intCast(u32, err_off), operand);2165 try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), operand);
1973 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef);2166 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef);
19742167
1975 break :result MCValue{ .stack_offset = stack_offset };2168 break :result MCValue{ .stack_offset = stack_offset };
...@@ -2008,7 +2201,6 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -2008,7 +2201,6 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
2008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2201 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2009 const mcv = try self.resolveInst(ty_op.operand);2202 const mcv = try self.resolveInst(ty_op.operand);
2010 switch (mcv) {2203 switch (mcv) {
2011 .dead, .unreach => unreachable,
2012 .register => unreachable, // a slice doesn't fit in one register2204 .register => unreachable, // a slice doesn't fit in one register
2013 .stack_argument_offset => |off| {2205 .stack_argument_offset => |off| {
2014 break :result MCValue{ .stack_argument_offset = off + 4 };2206 break :result MCValue{ .stack_argument_offset = off + 4 };
...@@ -2019,7 +2211,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {...@@ -2019,7 +2211,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
2019 .memory => |addr| {2211 .memory => |addr| {
2020 break :result MCValue{ .memory = addr + 4 };2212 break :result MCValue{ .memory = addr + 4 };
2021 },2213 },
2022 else => return self.fail("TODO implement slice_len for {}", .{mcv}),2214 else => unreachable, // invalid MCValue for a slice
2023 }2215 }
2024 };2216 };
2025 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2217 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -2034,7 +2226,12 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2034,7 +2226,12 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
2034 .ptr_stack_offset => |off| {2226 .ptr_stack_offset => |off| {
2035 break :result MCValue{ .ptr_stack_offset = off - 4 };2227 break :result MCValue{ .ptr_stack_offset = off - 4 };
2036 },2228 },
2037 else => return self.fail("TODO implement ptr_slice_len_ptr for {}", .{mcv}),2229 else => {
2230 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
2231 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 4 } };
2232
2233 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
2234 },
2038 }2235 }
2039 };2236 };
2040 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2237 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
...@@ -2049,91 +2246,96 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2049,91 +2246,96 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
2049 .ptr_stack_offset => |off| {2246 .ptr_stack_offset => |off| {
2050 break :result MCValue{ .ptr_stack_offset = off };2247 break :result MCValue{ .ptr_stack_offset = off };
2051 },2248 },
2052 else => return self.fail("TODO implement ptr_slice_ptr_ptr for {}", .{mcv}),2249 else => {
2250 if (self.reuseOperand(inst, ty_op.operand, 0, mcv)) {
2251 break :result mcv;
2252 } else {
2253 break :result MCValue{ .register = try self.copyToTmpRegister(Type.usize, mcv) };
2254 }
2255 },
2053 }2256 }
2054 };2257 };
2055 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2258 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2056}2259}
20572260
2058fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {2261fn ptrElemVal(
2059 const is_volatile = false; // TODO2262 self: *Self,
2060 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2263 ptr_bind: ReadArg.Bind,
20612264 index_bind: ReadArg.Bind,
2062 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });2265 ptr_ty: Type,
2063 const result: MCValue = result: {2266 maybe_inst: ?Air.Inst.Index,
2064 const slice_mcv = try self.resolveInst(bin_op.lhs);2267) !MCValue {
20652268 const elem_ty = ptr_ty.childType();
2066 // TODO optimize for the case where the index is a constant,2269 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2067 // i.e. index_mcv == .immediate
2068 const index_mcv = try self.resolveInst(bin_op.rhs);
2069 const index_is_register = index_mcv == .register;
2070
2071 const slice_ty = self.air.typeOf(bin_op.lhs);
2072 const elem_ty = slice_ty.childType();
2073 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
2074
2075 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2076 const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf);
20772270
2078 const index_lock: ?RegisterLock = if (index_is_register)2271 switch (elem_size) {
2079 self.register_manager.lockRegAssumeUnused(index_mcv.register)2272 1, 4 => {
2080 else2273 var base_reg: Register = undefined;
2081 null;2274 var index_reg: Register = undefined;
2082 defer if (index_lock) |reg| self.register_manager.unlockReg(reg);2275 var dest_reg: Register = undefined;
20832276
2084 const base_mcv = slicePtr(slice_mcv);2277 const read_args = [_]ReadArg{
2278 .{ .ty = ptr_ty, .bind = ptr_bind, .class = gp, .reg = &base_reg },
2279 .{ .ty = Type.usize, .bind = index_bind, .class = gp, .reg = &index_reg },
2280 };
2281 const write_args = [_]WriteArg{
2282 .{ .ty = elem_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2283 };
2284 try self.allocRegs(
2285 &read_args,
2286 &write_args,
2287 if (maybe_inst) |inst| .{
2288 .corresponding_inst = inst,
2289 .operand_mapping = &.{ 0, 1 },
2290 } else null,
2291 );
2292
2293 const tag: Mir.Inst.Tag = switch (elem_size) {
2294 1 => .ldrb,
2295 4 => .ldr,
2296 else => unreachable,
2297 };
2298 const shift: u5 = switch (elem_size) {
2299 1 => 0,
2300 4 => 2,
2301 else => unreachable,
2302 };
20852303
2086 switch (elem_size) {2304 _ = try self.addInst(.{
2087 1, 4 => {2305 .tag = tag,
2088 const base_reg = switch (base_mcv) {2306 .data = .{ .rr_offset = .{
2089 .register => |r| r,2307 .rt = dest_reg,
2090 else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv),2308 .rn = base_reg,
2091 };2309 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
2092 const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg);2310 } },
2093 defer self.register_manager.unlockReg(base_reg_lock);2311 });
20942312
2095 const dst_reg = try self.register_manager.allocReg(inst, gp);2313 return MCValue{ .register = dest_reg };
2096 const dst_mcv = MCValue{ .register = dst_reg };2314 },
2097 const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);2315 else => {
2098 defer self.register_manager.unlockReg(dst_reg_lock);2316 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, Type.usize, null);
20992317
2100 const index_reg: Register = switch (index_mcv) {2318 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
2101 .register => |reg| reg,2319 try self.load(dest, addr, ptr_ty);
2102 else => try self.copyToTmpRegister(Type.usize, index_mcv),2320 return dest;
2103 };2321 },
2104 const index_reg_lock = self.register_manager.lockReg(index_reg);2322 }
2105 defer if (index_reg_lock) |lock| self.register_manager.unlockReg(lock);2323}
21062324
2107 const tag: Mir.Inst.Tag = switch (elem_size) {2325fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2108 1 => .ldrb,2326 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2109 4 => .ldr,2327 const slice_ty = self.air.typeOf(bin_op.lhs);
2110 else => unreachable,2328 const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2111 };2329 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2112 const shift: u5 = switch (elem_size) {2330 const ptr_ty = slice_ty.slicePtrFieldType(&buf);
2113 1 => 0,
2114 4 => 2,
2115 else => unreachable,
2116 };
21172331
2118 _ = try self.addInst(.{2332 const slice_mcv = try self.resolveInst(bin_op.lhs);
2119 .tag = tag,2333 const base_mcv = slicePtr(slice_mcv);
2120 .data = .{ .rr_offset = .{
2121 .rt = dst_reg,
2122 .rn = base_reg,
2123 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
2124 } },
2125 });
21262334
2127 break :result dst_mcv;2335 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2128 },2336 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2129 else => {
2130 const dest = try self.allocRegOrMem(inst, true);
2131 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ptr_field_type, Type.usize, null);
2132 try self.load(dest, addr, slice_ptr_field_type);
21332337
2134 break :result dest;2338 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
2135 },
2136 }
2137 };2339 };
2138 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2340 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2139}2341}
...@@ -2143,40 +2345,108 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -2143,40 +2345,108 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2143 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2345 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2144 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2346 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2145 const slice_mcv = try self.resolveInst(extra.lhs);2347 const slice_mcv = try self.resolveInst(extra.lhs);
2146 const index_mcv = try self.resolveInst(extra.rhs);
2147 const base_mcv = slicePtr(slice_mcv);2348 const base_mcv = slicePtr(slice_mcv);
21482349
2350 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2351 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2352
2149 const slice_ty = self.air.typeOf(extra.lhs);2353 const slice_ty = self.air.typeOf(extra.lhs);
2354 const index_ty = self.air.typeOf(extra.rhs);
21502355
2151 const addr = try self.binOp(.ptr_add, base_mcv, index_mcv, slice_ty, Type.usize, null);2356 const addr = try self.ptrArithmetic(.ptr_add, base_bind, index_bind, slice_ty, index_ty, null);
2152 break :result addr;2357 break :result addr;
2153 };2358 };
2154 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2359 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
2155}2360}
21562361
2157fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {2362fn arrayElemVal(
2158 const bin_op = self.air.instructions.items(.data)[inst].bin_op;2363 self: *Self,
2159 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});2364 array_bind: ReadArg.Bind,
2160 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2365 index_bind: ReadArg.Bind,
2161}2366 array_ty: Type,
21622367 maybe_inst: ?Air.Inst.Index,
2163fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {2368) InnerError!MCValue {
2164 const is_volatile = false; // TODO2369 const elem_ty = array_ty.childType();
2165 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2166 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});
2167 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2168}
21692370
2170fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {2371 const mcv = try array_bind.resolveToMcv(self);
2171 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2372 switch (mcv) {
2172 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;2373 .stack_offset,
2173 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {2374 .memory,
2174 const ptr_mcv = try self.resolveInst(extra.lhs);2375 .stack_argument_offset,
2175 const index_mcv = try self.resolveInst(extra.rhs);2376 => {
2377 const ptr_to_mcv = switch (mcv) {
2378 .stack_offset => |off| MCValue{ .ptr_stack_offset = off },
2379 .memory => |addr| MCValue{ .immediate = @intCast(u32, addr) },
2380 .stack_argument_offset => |off| blk: {
2381 const reg = try self.register_manager.allocReg(null, gp);
21762382
2177 const ptr_ty = self.air.typeOf(extra.lhs);2383 _ = try self.addInst(.{
2384 .tag = .ldr_ptr_stack_argument,
2385 .data = .{ .r_stack_offset = .{
2386 .rt = reg,
2387 .stack_offset = off,
2388 } },
2389 });
2390
2391 break :blk MCValue{ .register = reg };
2392 },
2393 else => unreachable,
2394 };
2395 const ptr_to_mcv_lock: ?RegisterLock = switch (ptr_to_mcv) {
2396 .register => |reg| self.register_manager.lockRegAssumeUnused(reg),
2397 else => null,
2398 };
2399 defer if (ptr_to_mcv_lock) |lock| self.register_manager.unlockReg(lock);
2400
2401 const base_bind: ReadArg.Bind = .{ .mcv = ptr_to_mcv };
2402
2403 var ptr_ty_payload: Type.Payload.ElemType = .{
2404 .base = .{ .tag = .single_mut_pointer },
2405 .data = elem_ty,
2406 };
2407 const ptr_ty = Type.initPayload(&ptr_ty_payload.base);
2408
2409 return try self.ptrElemVal(base_bind, index_bind, ptr_ty, maybe_inst);
2410 },
2411 else => return self.fail("TODO implement array_elem_val for {}", .{mcv}),
2412 }
2413}
2414
2415fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2416 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2417 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2418 const array_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2419 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2420 const array_ty = self.air.typeOf(bin_op.lhs);
21782421
2179 const addr = try self.binOp(.ptr_add, ptr_mcv, index_mcv, ptr_ty, Type.usize, null);2422 break :result try self.arrayElemVal(array_bind, index_bind, array_ty, inst);
2423 };
2424 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2425}
2426
2427fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2428 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2429 const ptr_ty = self.air.typeOf(bin_op.lhs);
2430 const result: MCValue = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2431 const base_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
2432 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
2433
2434 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
2435 };
2436 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2437}
2438
2439fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2440 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2441 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2442 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2443 const ptr_bind: ReadArg.Bind = .{ .inst = extra.lhs };
2444 const index_bind: ReadArg.Bind = .{ .inst = extra.rhs };
2445
2446 const ptr_ty = self.air.typeOf(extra.lhs);
2447 const index_ty = self.air.typeOf(extra.rhs);
2448
2449 const addr = try self.ptrArithmetic(.ptr_add, ptr_bind, index_bind, ptr_ty, index_ty, null);
2180 break :result addr;2450 break :result addr;
2181 };2451 };
2182 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });2452 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
...@@ -2240,7 +2510,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {...@@ -2240,7 +2510,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
2240 return self.finishAir(inst, result, .{ un_op, .none, .none });2510 return self.finishAir(inst, result, .{ un_op, .none, .none });
2241}2511}
22422512
2243fn reuseOperand(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, op_index: Liveness.OperandInt, mcv: MCValue) bool {2513fn reuseOperand(
2514 self: *Self,
2515 inst: Air.Inst.Index,
2516 operand: Air.Inst.Ref,
2517 op_index: Liveness.OperandInt,
2518 mcv: MCValue,
2519) bool {
2244 if (!self.liveness.operandDies(inst, op_index))2520 if (!self.liveness.operandDies(inst, op_index))
2245 return false;2521 return false;
22462522
...@@ -2362,16 +2638,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -2362,16 +2638,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
2362 if (self.liveness.isUnused(inst) and !is_volatile)2638 if (self.liveness.isUnused(inst) and !is_volatile)
2363 break :result MCValue.dead;2639 break :result MCValue.dead;
23642640
2365 const dst_mcv: MCValue = blk: {2641 const dest_mcv: MCValue = blk: {
2366 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {2642 const ptr_fits_dest = elem_ty.abiSize(self.target.*) <= 4;
2643 if (ptr_fits_dest and self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
2367 // The MCValue that holds the pointer can be re-used as the value.2644 // The MCValue that holds the pointer can be re-used as the value.
2368 break :blk ptr;2645 break :blk ptr;
2369 } else {2646 } else {
2370 break :blk try self.allocRegOrMem(inst, true);2647 break :blk try self.allocRegOrMem(elem_ty, true, inst);
2371 }2648 }
2372 };2649 };
2373 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));2650 try self.load(dest_mcv, ptr, self.air.typeOf(ty_op.operand));
2374 break :result dst_mcv;2651
2652 break :result dest_mcv;
2375 };2653 };
2376 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });2654 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
2377}2655}
...@@ -2498,26 +2776,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde...@@ -2498,26 +2776,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
2498 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };2776 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
2499 },2777 },
2500 else => {2778 else => {
2501 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{2779 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
2502 .immediate = struct_field_offset,2780 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
2503 });
2504 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg);
2505 defer self.register_manager.unlockReg(offset_reg_lock);
2506
2507 const addr_reg = try self.copyToTmpRegister(ptr_ty, mcv);
2508 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg);
2509 defer self.register_manager.unlockReg(addr_reg_lock);
2510
2511 const dest = try self.binOp(
2512 .add,
2513 .{ .register = addr_reg },
2514 .{ .register = offset_reg },
2515 Type.usize,
2516 Type.usize,
2517 null,
2518 );
25192781
2520 break :result dest;2782 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
2521 },2783 },
2522 }2784 }
2523 };2785 };
...@@ -2532,6 +2794,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2532,6 +2794,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2532 const mcv = try self.resolveInst(operand);2794 const mcv = try self.resolveInst(operand);
2533 const struct_ty = self.air.typeOf(operand);2795 const struct_ty = self.air.typeOf(operand);
2534 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));2796 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
2797 const struct_field_ty = struct_ty.structFieldType(index);
25352798
2536 switch (mcv) {2799 switch (mcv) {
2537 .dead, .unreach => unreachable,2800 .dead, .unreach => unreachable,
...@@ -2569,11 +2832,45 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2569,11 +2832,45 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
2569 } else {2832 } else {
2570 // Copy to new register2833 // Copy to new register
2571 const dest_reg = try self.register_manager.allocReg(null, gp);2834 const dest_reg = try self.register_manager.allocReg(null, gp);
2572 try self.genSetReg(struct_ty.structFieldType(index), dest_reg, field);2835 try self.genSetReg(struct_field_ty, dest_reg, field);
25732836
2574 break :result MCValue{ .register = dest_reg };2837 break :result MCValue{ .register = dest_reg };
2575 }2838 }
2576 },2839 },
2840 .register => {
2841 var operand_reg: Register = undefined;
2842 var dest_reg: Register = undefined;
2843
2844 const read_args = [_]ReadArg{
2845 .{ .ty = struct_ty, .bind = .{ .mcv = mcv }, .class = gp, .reg = &operand_reg },
2846 };
2847 const write_args = [_]WriteArg{
2848 .{ .ty = struct_field_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2849 };
2850 try self.allocRegs(
2851 &read_args,
2852 &write_args,
2853 ReuseMetadata{
2854 .corresponding_inst = inst,
2855 .operand_mapping = &.{0},
2856 },
2857 );
2858
2859 const field_bit_offset = struct_field_offset * 8;
2860 const field_bit_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)) * 8;
2861
2862 _ = try self.addInst(.{
2863 .tag = if (struct_field_ty.isSignedInt()) Mir.Inst.Tag.sbfx else .ubfx,
2864 .data = .{ .rr_lsb_width = .{
2865 .rd = dest_reg,
2866 .rn = operand_reg,
2867 .lsb = @intCast(u5, field_bit_offset),
2868 .width = @intCast(u6, field_bit_size),
2869 } },
2870 });
2871
2872 break :result MCValue{ .register = dest_reg };
2873 },
2577 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),2874 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
2578 }2875 }
2579 };2876 };
...@@ -2583,114 +2880,285 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {...@@ -2583,114 +2880,285 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
25832880
2584fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {2881fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
2585 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;2882 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2586 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;2883 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
2587 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFieldParentPtr", .{});2884 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2588 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });2885 const field_ptr = try self.resolveInst(extra.field_ptr);
2886 const struct_ty = self.air.getRefType(ty_pl.ty).childType();
2887 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(extra.field_index, self.target.*));
2888 switch (field_ptr) {
2889 .ptr_stack_offset => |off| {
2890 break :result MCValue{ .ptr_stack_offset = off + struct_field_offset };
2891 },
2892 else => {
2893 const lhs_bind: ReadArg.Bind = .{ .mcv = field_ptr };
2894 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
2895
2896 break :result try self.addSub(.sub, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
2897 },
2898 }
2899 };
2900 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none });
2589}2901}
25902902
2591/// Allocates a new register. If Inst in non-null, additionally tracks2903/// An argument to a Mir instruction which is read (and possibly also
2592/// this register and the corresponding int and removes all previous2904/// written to) by the respective instruction
2593/// tracking. Does not do the actual moving (that is handled by2905const ReadArg = struct {
2594/// genSetReg).2906 ty: Type,
2595fn prepareNewRegForMoving(2907 bind: Bind,
2596 self: *Self,2908 class: RegisterManager.RegisterBitSet,
2597 track_inst: ?Air.Inst.Index,2909 reg: *Register,
2598 register_class: RegisterManager.RegisterBitSet,
2599 mcv: MCValue,
2600) !Register {
2601 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
2602 const reg = try self.register_manager.allocReg(track_inst, register_class);
26032910
2604 if (track_inst) |inst| {2911 const Bind = union(enum) {
2605 // Overwrite the MCValue associated with this inst2912 inst: Air.Inst.Ref,
2606 branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });2913 mcv: MCValue,
26072914
2608 // If the previous MCValue occupied some space we track, we2915 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
2609 // need to make sure it is marked as free now.2916 return switch (bind) {
2610 switch (mcv) {2917 .inst => |inst| try function.resolveInst(inst),
2611 .cpsr_flags => {2918 .mcv => |mcv| mcv,
2612 assert(self.cpsr_flags_inst.? == inst);2919 };
2613 self.cpsr_flags_inst = null;
2614 },
2615 .register => |prev_reg| {
2616 assert(!self.register_manager.isRegFree(prev_reg));
2617 self.register_manager.freeReg(prev_reg);
2618 },
2619 else => {},
2620 }2920 }
2621 }
26222921
2623 return reg;2922 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 {
2624}2923 switch (bind) {
2924 .inst => |inst| {
2925 // TODO resolve independently of inst_table
2926 const mcv = try function.resolveInst(inst);
2927 switch (mcv) {
2928 .immediate => |imm| return imm,
2929 else => return null,
2930 }
2931 },
2932 .mcv => |mcv| {
2933 switch (mcv) {
2934 .immediate => |imm| return imm,
2935 else => return null,
2936 }
2937 },
2938 }
2939 }
2940 };
2941};
2942
2943/// An argument to a Mir instruction which is written to (but not read
2944/// from) by the respective instruction
2945const WriteArg = struct {
2946 ty: Type,
2947 bind: Bind,
2948 class: RegisterManager.RegisterBitSet,
2949 reg: *Register,
2950
2951 const Bind = union(enum) {
2952 reg: Register,
2953 none: void,
2954 };
2955};
2956
2957/// Holds all data necessary for enabling the potential reuse of
2958/// operand registers as destinations
2959const ReuseMetadata = struct {
2960 corresponding_inst: Air.Inst.Index,
2961
2962 /// Maps every element index of read_args to the corresponding
2963 /// index in the Air instruction
2964 ///
2965 /// When the order of read_args corresponds exactly to the order
2966 /// of the inputs of the Air instruction, this would be e.g.
2967 /// &.{ 0, 1 }. However, when the order is not the same or some
2968 /// inputs to the Air instruction are omitted (e.g. when they can
2969 /// be represented as immediates to the Mir instruction),
2970 /// operand_mapping should reflect that fact.
2971 operand_mapping: []const Liveness.OperandInt,
2972};
26252973
2626/// Don't call this function directly. Use binOp instead.2974/// Allocate a set of registers for use as arguments for a Mir
2975/// instruction
2627///2976///
2628/// Calling this function signals an intention to generate a Mir2977/// If the Mir instruction these registers are allocated for
2629/// instruction of the form2978/// corresponds exactly to a single Air instruction, populate
2979/// reuse_metadata in order to enable potential reuse of an operand as
2980/// the destination (provided that that operand dies in this
2981/// instruction).
2630///2982///
2631/// op dest, lhs, rhs2983/// Reusing an operand register as destination is the only time two
2984/// arguments may share the same register. In all other cases,
2985/// allocRegs guarantees that a register will never be allocated to
2986/// more than one argument.
2632///2987///
2633/// Asserts that generating an instruction of that form is possible.2988/// Furthermore, allocReg guarantees that all arguments which are
2634fn binOpRegister(2989/// already bound to registers before calling allocRegs will not
2990/// change their register binding. This is done by locking these
2991/// registers.
2992fn allocRegs(
2635 self: *Self,2993 self: *Self,
2636 mir_tag: Mir.Inst.Tag,2994 read_args: []const ReadArg,
2637 lhs: MCValue,2995 write_args: []const WriteArg,
2638 rhs: MCValue,2996 reuse_metadata: ?ReuseMetadata,
2639 lhs_ty: Type,2997) InnerError!void {
2640 rhs_ty: Type,2998 // Air instructions have exactly one output
2641 metadata: ?BinOpMetadata,2999 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
2642) !MCValue {3000
2643 const lhs_is_register = lhs == .register;3001 // The operand mapping is a 1:1 mapping of read args to their
2644 const rhs_is_register = rhs == .register;3002 // corresponding operand index in the Air instruction
3003 assert(!(reuse_metadata != null and reuse_metadata.?.operand_mapping.len != read_args.len)); // see note above
3004
3005 const locks = try self.gpa.alloc(?RegisterLock, read_args.len + write_args.len);
3006 defer self.gpa.free(locks);
3007 const read_locks = locks[0..read_args.len];
3008 const write_locks = locks[read_args.len..];
3009
3010 std.mem.set(?RegisterLock, locks, null);
3011 defer for (locks) |lock| {
3012 if (lock) |locked_reg| self.register_manager.unlockReg(locked_reg);
3013 };
26453014
2646 const lhs_lock: ?RegisterLock = if (lhs_is_register)3015 // When we reuse a read_arg as a destination, the corresponding
2647 self.register_manager.lockReg(lhs.register)3016 // MCValue of the read_arg will be set to .dead. In that case, we
2648 else3017 // skip allocating this read_arg.
2649 null;3018 var reused_read_arg: ?usize = null;
2650 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
26513019
2652 const lhs_reg = if (lhs_is_register) lhs.register else blk: {3020 // Lock all args which are already allocated to registers
2653 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {3021 for (read_args) |arg, i| {
2654 break :inst Air.refToIndex(md.lhs).?;3022 const mcv = try arg.bind.resolveToMcv(self);
2655 } else null;3023 if (mcv == .register) {
3024 read_locks[i] = self.register_manager.lockReg(mcv.register);
3025 }
3026 }
26563027
2657 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);3028 for (write_args) |arg, i| {
2658 };3029 if (arg.bind == .reg) {
2659 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);3030 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
2660 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);3031 }
3032 }
26613033
2662 const rhs_reg = if (rhs_is_register) rhs.register else blk: {3034 // Allocate registers for all args which aren't allocated to
2663 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {3035 // registers yet
2664 break :inst Air.refToIndex(md.rhs).?;3036 for (read_args) |arg, i| {
2665 } else null;3037 const mcv = try arg.bind.resolveToMcv(self);
3038 if (mcv == .register) {
3039 arg.reg.* = mcv.register;
3040 } else {
3041 const track_inst: ?Air.Inst.Index = switch (arg.bind) {
3042 .inst => |inst| Air.refToIndex(inst).?,
3043 else => null,
3044 };
3045 arg.reg.* = try self.register_manager.allocReg(track_inst, arg.class);
3046 read_locks[i] = self.register_manager.lockReg(arg.reg.*);
3047 }
3048 }
26663049
2667 break :blk try self.prepareNewRegForMoving(track_inst, gp, rhs);3050 if (reuse_metadata != null) {
2668 };3051 const inst = reuse_metadata.?.corresponding_inst;
2669 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);3052 const operand_mapping = reuse_metadata.?.operand_mapping;
2670 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);3053 const arg = write_args[0];
26713054 if (arg.bind == .reg) {
2672 const dest_reg = switch (mir_tag) {3055 arg.reg.* = arg.bind.reg;
2673 .cmp => .r0, // cmp has no destination regardless3056 } else {
2674 else => if (metadata) |md| blk: {3057 reuse_operand: for (read_args) |read_arg, i| {
2675 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {3058 if (read_arg.bind == .inst) {
2676 break :blk lhs_reg;3059 const operand = read_arg.bind.inst;
2677 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {3060 const mcv = try self.resolveInst(operand);
2678 break :blk rhs_reg;3061 if (mcv == .register and
3062 std.meta.eql(arg.class, read_arg.class) and
3063 self.reuseOperand(inst, operand, operand_mapping[i], mcv))
3064 {
3065 arg.reg.* = mcv.register;
3066 write_locks[0] = null;
3067 reused_read_arg = i;
3068 break :reuse_operand;
3069 }
3070 }
2679 } else {3071 } else {
2680 break :blk try self.register_manager.allocReg(md.inst, gp);3072 arg.reg.* = try self.register_manager.allocReg(inst, arg.class);
3073 write_locks[0] = self.register_manager.lockReg(arg.reg.*);
2681 }3074 }
2682 } else try self.register_manager.allocReg(null, gp),3075 }
2683 };3076 } else {
3077 for (write_args) |arg, i| {
3078 if (arg.bind == .reg) {
3079 arg.reg.* = arg.bind.reg;
3080 } else {
3081 arg.reg.* = try self.register_manager.allocReg(null, arg.class);
3082 write_locks[i] = self.register_manager.lockReg(arg.reg.*);
3083 }
3084 }
3085 }
3086
3087 // For all read_args which need to be moved from non-register to
3088 // register, perform the move
3089 for (read_args) |arg, i| {
3090 if (reused_read_arg) |j| {
3091 // Check whether this read_arg was reused
3092 if (i == j) continue;
3093 }
3094
3095 const mcv = try arg.bind.resolveToMcv(self);
3096 if (mcv != .register) {
3097 if (arg.bind == .inst) {
3098 const branch = &self.branch_stack.items[self.branch_stack.items.len - 1];
3099 const inst = Air.refToIndex(arg.bind.inst).?;
3100
3101 // Overwrite the MCValue associated with this inst
3102 branch.inst_table.putAssumeCapacity(inst, .{ .register = arg.reg.* });
3103
3104 // If the previous MCValue occupied some space we track, we
3105 // need to make sure it is marked as free now.
3106 switch (mcv) {
3107 .cpsr_flags => {
3108 assert(self.cpsr_flags_inst.? == inst);
3109 self.cpsr_flags_inst = null;
3110 },
3111 .register => |prev_reg| {
3112 assert(!self.register_manager.isRegFree(prev_reg));
3113 self.register_manager.freeReg(prev_reg);
3114 },
3115 else => {},
3116 }
3117 }
3118
3119 try self.genSetReg(arg.ty, arg.reg.*, mcv);
3120 }
3121 }
3122}
26843123
2685 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);3124/// Wrapper around allocRegs and addInst tailored for specific Mir
2686 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);3125/// instructions which are binary operations acting on two registers
3126///
3127/// Returns the destination register
3128fn binOpRegisterNew(
3129 self: *Self,
3130 mir_tag: Mir.Inst.Tag,
3131 lhs_bind: ReadArg.Bind,
3132 rhs_bind: ReadArg.Bind,
3133 lhs_ty: Type,
3134 rhs_ty: Type,
3135 maybe_inst: ?Air.Inst.Index,
3136) !MCValue {
3137 var lhs_reg: Register = undefined;
3138 var rhs_reg: Register = undefined;
3139 var dest_reg: Register = undefined;
3140
3141 const read_args = [_]ReadArg{
3142 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
3143 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
3144 };
3145 const write_args = [_]WriteArg{
3146 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3147 };
3148 try self.allocRegs(
3149 &read_args,
3150 &write_args,
3151 if (maybe_inst) |inst| .{
3152 .corresponding_inst = inst,
3153 .operand_mapping = &.{ 0, 1 },
3154 } else null,
3155 );
26873156
2688 const mir_data: Mir.Inst.Data = switch (mir_tag) {3157 const mir_data: Mir.Inst.Data = switch (mir_tag) {
2689 .add,3158 .add,
2690 .adds,3159 .adds,
2691 .sub,3160 .sub,
2692 .subs,3161 .subs,
2693 .cmp,
2694 .@"and",3162 .@"and",
2695 .orr,3163 .orr,
2696 .eor,3164 .eor,
...@@ -2725,78 +3193,51 @@ fn binOpRegister(...@@ -2725,78 +3193,51 @@ fn binOpRegister(
2725 return MCValue{ .register = dest_reg };3193 return MCValue{ .register = dest_reg };
2726}3194}
27273195
2728/// Don't call this function directly. Use binOp instead.3196/// Wrapper around allocRegs and addInst tailored for specific Mir
2729///3197/// instructions which are binary operations acting on a register and
2730/// Calling this function signals an intention to generate a Mir3198/// an immediate
2731/// instruction of the form
2732///
2733/// op dest, lhs, #rhs_imm
2734///3199///
2735/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to3200/// Returns the destination register
2736/// rhs and vice versa. This parameter is only used when maybe_inst !=3201fn binOpImmediateNew(
2737/// null.
2738///
2739/// Asserts that generating an instruction of that form is possible.
2740fn binOpImmediate(
2741 self: *Self,3202 self: *Self,
2742 mir_tag: Mir.Inst.Tag,3203 mir_tag: Mir.Inst.Tag,
2743 lhs: MCValue,3204 lhs_bind: ReadArg.Bind,
2744 rhs: MCValue,3205 rhs_immediate: u32,
2745 lhs_ty: Type,3206 lhs_ty: Type,
2746 lhs_and_rhs_swapped: bool,3207 lhs_and_rhs_swapped: bool,
2747 metadata: ?BinOpMetadata,3208 maybe_inst: ?Air.Inst.Index,
2748) !MCValue {3209) !MCValue {
2749 const lhs_is_register = lhs == .register;3210 var lhs_reg: Register = undefined;
27503211 var dest_reg: Register = undefined;
2751 const lhs_lock: ?RegisterLock = if (lhs_is_register)
2752 self.register_manager.lockReg(lhs.register)
2753 else
2754 null;
2755 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
2756
2757 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2758 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2759 break :inst Air.refToIndex(
2760 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
2761 ).?;
2762 } else null;
27633212
2764 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);3213 const read_args = [_]ReadArg{
3214 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
2765 };3215 };
2766 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);3216 const write_args = [_]WriteArg{
2767 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);3217 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
2768
2769 const dest_reg = switch (mir_tag) {
2770 .cmp => .r0, // cmp has no destination reg
2771 else => if (metadata) |md| blk: {
2772 if (lhs_is_register and self.reuseOperand(
2773 md.inst,
2774 if (lhs_and_rhs_swapped) md.rhs else md.lhs,
2775 if (lhs_and_rhs_swapped) 1 else 0,
2776 lhs,
2777 )) {
2778 break :blk lhs_reg;
2779 } else {
2780 break :blk try self.register_manager.allocReg(md.inst, gp);
2781 }
2782 } else try self.register_manager.allocReg(null, gp),
2783 };3218 };
27843219 const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
2785 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);3220 try self.allocRegs(
3221 &read_args,
3222 &write_args,
3223 if (maybe_inst) |inst| .{
3224 .corresponding_inst = inst,
3225 .operand_mapping = operand_mapping,
3226 } else null,
3227 );
27863228
2787 const mir_data: Mir.Inst.Data = switch (mir_tag) {3229 const mir_data: Mir.Inst.Data = switch (mir_tag) {
2788 .add,3230 .add,
2789 .adds,3231 .adds,
2790 .sub,3232 .sub,
2791 .subs,3233 .subs,
2792 .cmp,
2793 .@"and",3234 .@"and",
2794 .orr,3235 .orr,
2795 .eor,3236 .eor,
2796 => .{ .rr_op = .{3237 => .{ .rr_op = .{
2797 .rd = dest_reg,3238 .rd = dest_reg,
2798 .rn = lhs_reg,3239 .rn = lhs_reg,
2799 .op = Instruction.Operand.fromU32(rhs.immediate).?,3240 .op = Instruction.Operand.fromU32(rhs_immediate).?,
2800 } },3241 } },
2801 .lsl,3242 .lsl,
2802 .asr,3243 .asr,
...@@ -2804,7 +3245,7 @@ fn binOpImmediate(...@@ -2804,7 +3245,7 @@ fn binOpImmediate(
2804 => .{ .rr_shift = .{3245 => .{ .rr_shift = .{
2805 .rd = dest_reg,3246 .rd = dest_reg,
2806 .rm = lhs_reg,3247 .rm = lhs_reg,
2807 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)),3248 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_immediate)),
2808 } },3249 } },
2809 else => unreachable,3250 else => unreachable,
2810 };3251 };
...@@ -2817,417 +3258,502 @@ fn binOpImmediate(...@@ -2817,417 +3258,502 @@ fn binOpImmediate(
2817 return MCValue{ .register = dest_reg };3258 return MCValue{ .register = dest_reg };
2818}3259}
28193260
2820const BinOpMetadata = struct {3261fn addSub(
2821 inst: Air.Inst.Index,
2822 lhs: Air.Inst.Ref,
2823 rhs: Air.Inst.Ref,
2824};
2825
2826/// For all your binary operation needs, this function will generate
2827/// the corresponding Mir instruction(s). Returns the location of the
2828/// result.
2829///
2830/// If the binary operation itself happens to be an Air instruction,
2831/// pass the corresponding index in the inst parameter. That helps
2832/// this function do stuff like reusing operands.
2833///
2834/// This function does not do any lowering to Mir itself, but instead
2835/// looks at the lhs and rhs and determines which kind of lowering
2836/// would be best suitable and then delegates the lowering to other
2837/// functions.
2838fn binOp(
2839 self: *Self,3262 self: *Self,
2840 tag: Air.Inst.Tag,3263 tag: Air.Inst.Tag,
2841 lhs: MCValue,3264 lhs_bind: ReadArg.Bind,
2842 rhs: MCValue,3265 rhs_bind: ReadArg.Bind,
2843 lhs_ty: Type,3266 lhs_ty: Type,
2844 rhs_ty: Type,3267 rhs_ty: Type,
2845 metadata: ?BinOpMetadata,3268 maybe_inst: ?Air.Inst.Index,
2846) InnerError!MCValue {3269) InnerError!MCValue {
2847 switch (tag) {3270 switch (lhs_ty.zigTypeTag()) {
2848 .add,3271 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2849 .sub,3272 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2850 .cmp_eq,3273 .Int => {
2851 => {3274 const mod = self.bin_file.options.module.?;
2852 switch (lhs_ty.zigTypeTag()) {3275 assert(lhs_ty.eql(rhs_ty, mod));
2853 .Float => return self.fail("TODO ARM binary operations on floats", .{}),3276 const int_info = lhs_ty.intInfo(self.target.*);
2854 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3277 if (int_info.bits <= 32) {
2855 .Int => {3278 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
2856 const mod = self.bin_file.options.module.?;3279 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
2857 assert(lhs_ty.eql(rhs_ty, mod));3280
2858 const int_info = lhs_ty.intInfo(self.target.*);3281 // Only say yes if the operation is
2859 if (int_info.bits <= 32) {3282 // commutative, i.e. we can swap both of the
2860 // Only say yes if the operation is3283 // operands
2861 // commutative, i.e. we can swap both of the3284 const lhs_immediate_ok = switch (tag) {
2862 // operands3285 .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
2863 const lhs_immediate_ok = switch (tag) {3286 .sub => false,
2864 .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,3287 else => unreachable,
2865 .sub,3288 };
2866 .cmp_eq,3289 const rhs_immediate_ok = switch (tag) {
2867 => false,3290 .add,
2868 else => unreachable,3291 .sub,
2869 };3292 => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
2870 const rhs_immediate_ok = switch (tag) {3293 else => unreachable,
2871 .add,3294 };
2872 .sub,
2873 .cmp_eq,
2874 => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,
2875 else => unreachable,
2876 };
28773295
2878 const mir_tag: Mir.Inst.Tag = switch (tag) {3296 const mir_tag: Mir.Inst.Tag = switch (tag) {
2879 .add => .add,3297 .add => .add,
2880 .sub => .sub,3298 .sub => .sub,
2881 .cmp_eq => .cmp,3299 else => unreachable,
2882 else => unreachable,3300 };
2883 };
28843301
2885 if (rhs_immediate_ok) {3302 if (rhs_immediate_ok) {
2886 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);3303 return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
2887 } else if (lhs_immediate_ok) {3304 } else if (lhs_immediate_ok) {
2888 // swap lhs and rhs3305 // swap lhs and rhs
2889 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);3306 return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
2890 } else {3307 } else {
2891 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3308 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
2892 }3309 }
2893 } else {3310 } else {
2894 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});3311 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2895 }
2896 },
2897 else => unreachable,
2898 }
2899 },
2900 .mul => {
2901 switch (lhs_ty.zigTypeTag()) {
2902 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2903 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2904 .Int => {
2905 const mod = self.bin_file.options.module.?;
2906 assert(lhs_ty.eql(rhs_ty, mod));
2907 const int_info = lhs_ty.intInfo(self.target.*);
2908 if (int_info.bits <= 32) {
2909 // TODO add optimisations for multiplication
2910 // with immediates, for example a * 2 can be
2911 // lowered to a << 1
2912 return try self.binOpRegister(.mul, lhs, rhs, lhs_ty, rhs_ty, metadata);
2913 } else {
2914 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2915 }
2916 },
2917 else => unreachable,
2918 }3312 }
2919 },3313 },
2920 .div_float => {3314 else => unreachable,
2921 switch (lhs_ty.zigTypeTag()) {3315 }
2922 .Float => return self.fail("TODO ARM binary operations on floats", .{}),3316}
2923 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3317
2924 else => unreachable,3318fn mul(
3319 self: *Self,
3320 lhs_bind: ReadArg.Bind,
3321 rhs_bind: ReadArg.Bind,
3322 lhs_ty: Type,
3323 rhs_ty: Type,
3324 maybe_inst: ?Air.Inst.Index,
3325) InnerError!MCValue {
3326 switch (lhs_ty.zigTypeTag()) {
3327 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3328 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3329 .Int => {
3330 const mod = self.bin_file.options.module.?;
3331 assert(lhs_ty.eql(rhs_ty, mod));
3332 const int_info = lhs_ty.intInfo(self.target.*);
3333 if (int_info.bits <= 32) {
3334 // TODO add optimisations for multiplication
3335 // with immediates, for example a * 2 can be
3336 // lowered to a << 1
3337 return try self.binOpRegisterNew(.mul, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3338 } else {
3339 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
2925 }3340 }
2926 },3341 },
2927 .div_trunc, .div_floor => {3342 else => unreachable,
2928 switch (lhs_ty.zigTypeTag()) {3343 }
2929 .Float => return self.fail("TODO ARM binary operations on floats", .{}),3344}
2930 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3345
2931 .Int => {3346fn divFloat(
2932 const mod = self.bin_file.options.module.?;3347 self: *Self,
2933 assert(lhs_ty.eql(rhs_ty, mod));3348 lhs_bind: ReadArg.Bind,
2934 const int_info = lhs_ty.intInfo(self.target.*);3349 rhs_bind: ReadArg.Bind,
2935 if (int_info.bits <= 32) {3350 lhs_ty: Type,
2936 switch (int_info.signedness) {3351 rhs_ty: Type,
2937 .signed => {3352 maybe_inst: ?Air.Inst.Index,
2938 return self.fail("TODO ARM signed integer division", .{});3353) InnerError!MCValue {
2939 },3354 _ = lhs_bind;
2940 .unsigned => {3355 _ = rhs_bind;
2941 switch (rhs) {3356 _ = lhs_ty;
2942 .immediate => |imm| {3357 _ = rhs_ty;
2943 if (std.math.isPowerOfTwo(imm)) {3358 _ = maybe_inst;
2944 const shift = MCValue{ .immediate = std.math.log2_int(u32, imm) };3359
2945 return try self.binOp(.shr, lhs, shift, lhs_ty, rhs_ty, metadata);3360 switch (lhs_ty.zigTypeTag()) {
2946 } else {3361 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2947 return self.fail("TODO ARM integer division by constants", .{});3362 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2948 }3363 else => unreachable,
2949 },3364 }
2950 else => return self.fail("TODO ARM integer division", .{}),3365}
2951 }3366
2952 },3367fn div(
3368 self: *Self,
3369 tag: Air.Inst.Tag,
3370 lhs_bind: ReadArg.Bind,
3371 rhs_bind: ReadArg.Bind,
3372 lhs_ty: Type,
3373 rhs_ty: Type,
3374 maybe_inst: ?Air.Inst.Index,
3375) InnerError!MCValue {
3376 _ = tag;
3377
3378 switch (lhs_ty.zigTypeTag()) {
3379 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3380 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3381 .Int => {
3382 const mod = self.bin_file.options.module.?;
3383 assert(lhs_ty.eql(rhs_ty, mod));
3384 const int_info = lhs_ty.intInfo(self.target.*);
3385 if (int_info.bits <= 32) {
3386 switch (int_info.signedness) {
3387 .signed => {
3388 return self.fail("TODO ARM signed integer division", .{});
3389 },
3390 .unsigned => {
3391 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3392
3393 if (rhs_immediate) |imm| {
3394 if (std.math.isPowerOfTwo(imm)) {
3395 const shift = std.math.log2_int(u32, imm);
3396 return try self.binOpImmediateNew(.lsr, lhs_bind, shift, lhs_ty, false, maybe_inst);
3397 } else {
3398 return self.fail("TODO ARM integer division by constants", .{});
3399 }
3400 } else {
3401 return self.fail("TODO ARM integer division", .{});
2953 }3402 }
2954 } else {3403 },
2955 return self.fail("TODO ARM integer division for integers > u32/i32", .{});3404 }
2956 }3405 } else {
2957 },3406 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
2958 else => unreachable,
2959 }
2960 },
2961 .div_exact => {
2962 switch (lhs_ty.zigTypeTag()) {
2963 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2964 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2965 .Int => return self.fail("TODO ARM div_exact", .{}),
2966 else => unreachable,
2967 }3407 }
2968 },3408 },
2969 .rem => {3409 else => unreachable,
2970 switch (lhs_ty.zigTypeTag()) {3410 }
2971 .Float => return self.fail("TODO ARM binary operations on floats", .{}),3411}
2972 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3412
2973 .Int => {3413fn divExact(
2974 const mod = self.bin_file.options.module.?;3414 self: *Self,
2975 assert(lhs_ty.eql(rhs_ty, mod));3415 lhs_bind: ReadArg.Bind,
2976 const int_info = lhs_ty.intInfo(self.target.*);3416 rhs_bind: ReadArg.Bind,
2977 if (int_info.bits <= 32) {3417 lhs_ty: Type,
2978 switch (int_info.signedness) {3418 rhs_ty: Type,
2979 .signed => {3419 maybe_inst: ?Air.Inst.Index,
2980 return self.fail("TODO ARM signed integer mod", .{});3420) InnerError!MCValue {
2981 },3421 _ = lhs_bind;
2982 .unsigned => {3422 _ = rhs_bind;
2983 switch (rhs) {3423 _ = lhs_ty;
2984 .immediate => |imm| {3424 _ = rhs_ty;
2985 if (std.math.isPowerOfTwo(imm)) {3425 _ = maybe_inst;
2986 const log2 = std.math.log2_int(u32, imm);3426
29873427 switch (lhs_ty.zigTypeTag()) {
2988 const lhs_is_register = lhs == .register;3428 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
29893429 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2990 const lhs_lock: ?RegisterLock = if (lhs_is_register)3430 .Int => return self.fail("TODO ARM div_exact", .{}),
2991 self.register_manager.lockReg(lhs.register)3431 else => unreachable,
2992 else3432 }
2993 null;3433}
2994 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);3434
29953435fn rem(
2996 const lhs_reg = if (lhs_is_register) lhs.register else blk: {3436 self: *Self,
2997 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {3437 lhs_bind: ReadArg.Bind,
2998 break :inst Air.refToIndex(md.lhs).?;3438 rhs_bind: ReadArg.Bind,
2999 } else null;3439 lhs_ty: Type,
30003440 rhs_ty: Type,
3001 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);3441 maybe_inst: ?Air.Inst.Index,
3002 };3442) InnerError!MCValue {
3003 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);3443 switch (lhs_ty.zigTypeTag()) {
3004 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);3444 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
30053445 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3006 const dest_reg = if (metadata) |md| blk: {3446 .Int => {
3007 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {3447 const mod = self.bin_file.options.module.?;
3008 break :blk lhs_reg;3448 assert(lhs_ty.eql(rhs_ty, mod));
3009 } else {3449 const int_info = lhs_ty.intInfo(self.target.*);
3010 break :blk try self.register_manager.allocReg(md.inst, gp);3450 if (int_info.bits <= 32) {
3011 }3451 switch (int_info.signedness) {
3012 } else try self.register_manager.allocReg(null, gp);3452 .signed => {
30133453 return self.fail("TODO ARM signed integer mod", .{});
3014 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);3454 },
30153455 .unsigned => {
3016 try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2);3456 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3017 return MCValue{ .register = dest_reg };3457
3018 } else {3458 if (rhs_immediate) |imm| {
3019 return self.fail("TODO ARM integer mod by constants", .{});3459 if (std.math.isPowerOfTwo(imm)) {
3020 }3460 const log2 = std.math.log2_int(u32, imm);
3021 },3461
3022 else => return self.fail("TODO ARM integer mod", .{}),3462 var lhs_reg: Register = undefined;
3023 }3463 var dest_reg: Register = undefined;
3024 },3464
3465 const read_args = [_]ReadArg{
3466 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
3467 };
3468 const write_args = [_]WriteArg{
3469 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
3470 };
3471 try self.allocRegs(
3472 &read_args,
3473 &write_args,
3474 if (maybe_inst) |inst| .{
3475 .corresponding_inst = inst,
3476 .operand_mapping = &.{0},
3477 } else null,
3478 );
3479
3480 try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2);
3481
3482 return MCValue{ .register = dest_reg };
3483 } else {
3484 return self.fail("TODO ARM integer mod by constants", .{});
3485 }
3486 } else {
3487 return self.fail("TODO ARM integer mod", .{});
3025 }3488 }
3026 } else {3489 },
3027 return self.fail("TODO ARM integer division for integers > u32/i32", .{});3490 }
3028 }3491 } else {
3029 },3492 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
3030 else => unreachable,
3031 }3493 }
3032 },3494 },
3033 .mod => {3495 else => unreachable,
3034 switch (lhs_ty.zigTypeTag()) {3496 }
3035 .Float => return self.fail("TODO ARM binary operations on floats", .{}),3497}
3036 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3498
3037 .Int => return self.fail("TODO ARM mod", .{}),3499fn modulo(
3038 else => unreachable,3500 self: *Self,
3501 lhs_bind: ReadArg.Bind,
3502 rhs_bind: ReadArg.Bind,
3503 lhs_ty: Type,
3504 rhs_ty: Type,
3505 maybe_inst: ?Air.Inst.Index,
3506) InnerError!MCValue {
3507 _ = lhs_bind;
3508 _ = rhs_bind;
3509 _ = lhs_ty;
3510 _ = rhs_ty;
3511 _ = maybe_inst;
3512
3513 switch (lhs_ty.zigTypeTag()) {
3514 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3515 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3516 .Int => return self.fail("TODO ARM mod", .{}),
3517 else => unreachable,
3518 }
3519}
3520
3521fn wrappingArithmetic(
3522 self: *Self,
3523 tag: Air.Inst.Tag,
3524 lhs_bind: ReadArg.Bind,
3525 rhs_bind: ReadArg.Bind,
3526 lhs_ty: Type,
3527 rhs_ty: Type,
3528 maybe_inst: ?Air.Inst.Index,
3529) InnerError!MCValue {
3530 const base_tag: Air.Inst.Tag = switch (tag) {
3531 .addwrap => .add,
3532 .subwrap => .sub,
3533 .mulwrap => .mul,
3534 else => unreachable,
3535 };
3536
3537 // Generate an add/sub/mul
3538 const result = try self.addSub(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3539
3540 // Truncate if necessary
3541 switch (lhs_ty.zigTypeTag()) {
3542 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3543 .Int => {
3544 const int_info = lhs_ty.intInfo(self.target.*);
3545 if (int_info.bits <= 32) {
3546 const result_reg = result.register;
3547
3548 if (int_info.bits < 32) {
3549 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3550 return result;
3551 } else return result;
3552 } else {
3553 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3039 }3554 }
3040 },3555 },
3041 .addwrap,3556 else => unreachable,
3042 .subwrap,3557 }
3043 .mulwrap,3558}
3044 => {3559
3045 const base_tag: Air.Inst.Tag = switch (tag) {3560fn bitwise(
3046 .addwrap => .add,3561 self: *Self,
3047 .subwrap => .sub,3562 tag: Air.Inst.Tag,
3048 .mulwrap => .mul,3563 lhs_bind: ReadArg.Bind,
3049 else => unreachable,3564 rhs_bind: ReadArg.Bind,
3050 };3565 lhs_ty: Type,
3566 rhs_ty: Type,
3567 maybe_inst: ?Air.Inst.Index,
3568) InnerError!MCValue {
3569 switch (lhs_ty.zigTypeTag()) {
3570 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3571 .Int => {
3572 const mod = self.bin_file.options.module.?;
3573 assert(lhs_ty.eql(rhs_ty, mod));
3574 const int_info = lhs_ty.intInfo(self.target.*);
3575 if (int_info.bits <= 32) {
3576 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3577 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3578
3579 const lhs_immediate_ok = if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
3580 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
3581
3582 const mir_tag: Mir.Inst.Tag = switch (tag) {
3583 .bit_and => .@"and",
3584 .bit_or => .orr,
3585 .xor => .eor,
3586 else => unreachable,
3587 };
30513588
3052 // Generate an add/sub/mul3589 if (rhs_immediate_ok) {
3053 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3590 return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
30543591 } else if (lhs_immediate_ok) {
3055 // Truncate if necessary3592 // swap lhs and rhs
3056 switch (lhs_ty.zigTypeTag()) {3593 return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
3057 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3594 } else {
3058 .Int => {3595 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3059 const int_info = lhs_ty.intInfo(self.target.*);3596 }
3060 if (int_info.bits <= 32) {3597 } else {
3061 const result_reg = result.register;3598 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3062
3063 if (int_info.bits < 32) {
3064 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3065 return result;
3066 } else return result;
3067 } else {
3068 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3069 }
3070 },
3071 else => unreachable,
3072 }3599 }
3073 },3600 },
3074 .bit_and,3601 else => unreachable,
3075 .bit_or,3602 }
3076 .xor,3603}
3077 => {
3078 switch (lhs_ty.zigTypeTag()) {
3079 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3080 .Int => {
3081 const mod = self.bin_file.options.module.?;
3082 assert(lhs_ty.eql(rhs_ty, mod));
3083 const int_info = lhs_ty.intInfo(self.target.*);
3084 if (int_info.bits <= 32) {
3085 const lhs_immediate_ok = lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null;
3086 const rhs_immediate_ok = rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null;
3087
3088 const mir_tag: Mir.Inst.Tag = switch (tag) {
3089 .bit_and => .@"and",
3090 .bit_or => .orr,
3091 .xor => .eor,
3092 else => unreachable,
3093 };
30943604
3095 if (rhs_immediate_ok) {3605fn shiftExact(
3096 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);3606 self: *Self,
3097 } else if (lhs_immediate_ok) {3607 tag: Air.Inst.Tag,
3098 // swap lhs and rhs3608 lhs_bind: ReadArg.Bind,
3099 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);3609 rhs_bind: ReadArg.Bind,
3100 } else {3610 lhs_ty: Type,
3101 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3611 rhs_ty: Type,
3102 }3612 maybe_inst: ?Air.Inst.Index,
3103 } else {3613) InnerError!MCValue {
3104 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});3614 switch (lhs_ty.zigTypeTag()) {
3105 }3615 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3106 },3616 .Int => {
3107 else => unreachable,3617 const int_info = lhs_ty.intInfo(self.target.*);
3108 }3618 if (int_info.bits <= 32) {
3109 },3619 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3110 .shl_exact,
3111 .shr_exact,
3112 => {
3113 switch (lhs_ty.zigTypeTag()) {
3114 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3115 .Int => {
3116 const int_info = lhs_ty.intInfo(self.target.*);
3117 if (int_info.bits <= 32) {
3118 const rhs_immediate_ok = rhs == .immediate;
3119
3120 const mir_tag: Mir.Inst.Tag = switch (tag) {
3121 .shl_exact => .lsl,
3122 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
3123 .signed => Mir.Inst.Tag.asr,
3124 .unsigned => Mir.Inst.Tag.lsr,
3125 },
3126 else => unreachable,
3127 };
31283620
3129 if (rhs_immediate_ok) {3621 const mir_tag: Mir.Inst.Tag = switch (tag) {
3130 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);3622 .shl_exact => .lsl,
3131 } else {3623 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
3132 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3624 .signed => Mir.Inst.Tag.asr,
3133 }3625 .unsigned => Mir.Inst.Tag.lsr,
3134 } else {3626 },
3135 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});3627 else => unreachable,
3136 }3628 };
3137 },3629
3138 else => unreachable,3630 if (rhs_immediate) |imm| {
3631 return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
3632 } else {
3633 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3634 }
3635 } else {
3636 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3139 }3637 }
3140 },3638 },
3141 .shl,3639 else => unreachable,
3142 .shr,3640 }
3143 => {3641}
3144 const base_tag: Air.Inst.Tag = switch (tag) {3642
3145 .shl => .shl_exact,3643fn shiftNormal(
3146 .shr => .shr_exact,3644 self: *Self,
3147 else => unreachable,3645 tag: Air.Inst.Tag,
3148 };3646 lhs_bind: ReadArg.Bind,
3647 rhs_bind: ReadArg.Bind,
3648 lhs_ty: Type,
3649 rhs_ty: Type,
3650 maybe_inst: ?Air.Inst.Index,
3651) InnerError!MCValue {
3652 const base_tag: Air.Inst.Tag = switch (tag) {
3653 .shl => .shl_exact,
3654 .shr => .shr_exact,
3655 else => unreachable,
3656 };
31493657
3150 // Generate a shl_exact/shr_exact3658 // Generate a shl_exact/shr_exact
3151 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3659 const result = try self.shiftExact(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
31523660
3153 // Truncate if necessary3661 // Truncate if necessary
3154 switch (tag) {3662 switch (tag) {
3155 .shr => return result,3663 .shr => return result,
3156 .shl => switch (lhs_ty.zigTypeTag()) {3664 .shl => switch (lhs_ty.zigTypeTag()) {
3157 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),3665 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3158 .Int => {3666 .Int => {
3159 const int_info = lhs_ty.intInfo(self.target.*);3667 const int_info = lhs_ty.intInfo(self.target.*);
3160 if (int_info.bits <= 32) {3668 if (int_info.bits <= 32) {
3161 const result_reg = result.register;3669 const result_reg = result.register;
31623670
3163 if (int_info.bits < 32) {3671 if (int_info.bits < 32) {
3164 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);3672 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3165 return result;3673 return result;
3166 } else return result;3674 } else return result;
3167 } else {3675 } else {
3168 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});3676 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3169 }3677 }
3170 },3678 },
3171 else => unreachable,3679 else => unreachable,
3172 },
3173 else => unreachable,
3174 }
3175 },3680 },
3176 .bool_and,3681 else => unreachable,
3177 .bool_or,3682 }
3178 => {3683}
3179 switch (lhs_ty.zigTypeTag()) {
3180 .Bool => {
3181 const lhs_immediate_ok = lhs == .immediate;
3182 const rhs_immediate_ok = rhs == .immediate;
31833684
3184 const mir_tag: Mir.Inst.Tag = switch (tag) {3685fn booleanOp(
3185 .bool_and => .@"and",3686 self: *Self,
3186 .bool_or => .orr,3687 tag: Air.Inst.Tag,
3187 else => unreachable,3688 lhs_bind: ReadArg.Bind,
3188 };3689 rhs_bind: ReadArg.Bind,
3690 lhs_ty: Type,
3691 rhs_ty: Type,
3692 maybe_inst: ?Air.Inst.Index,
3693) InnerError!MCValue {
3694 switch (lhs_ty.zigTypeTag()) {
3695 .Bool => {
3696 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3697 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
31893698
3190 if (rhs_immediate_ok) {3699 const mir_tag: Mir.Inst.Tag = switch (tag) {
3191 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);3700 .bool_and => .@"and",
3192 } else if (lhs_immediate_ok) {3701 .bool_or => .orr,
3193 // swap lhs and rhs
3194 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
3195 } else {
3196 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
3197 }
3198 },
3199 else => unreachable,3702 else => unreachable,
3703 };
3704
3705 if (rhs_immediate) |imm| {
3706 return try self.binOpImmediateNew(mir_tag, lhs_bind, imm, lhs_ty, false, maybe_inst);
3707 } else if (lhs_immediate) |imm| {
3708 // swap lhs and rhs
3709 return try self.binOpImmediateNew(mir_tag, rhs_bind, imm, rhs_ty, true, maybe_inst);
3710 } else {
3711 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3200 }3712 }
3201 },3713 },
3202 .ptr_add,3714 else => unreachable,
3203 .ptr_sub,3715 }
3204 => {3716}
3205 switch (lhs_ty.zigTypeTag()) {
3206 .Pointer => {
3207 const ptr_ty = lhs_ty;
3208 const elem_ty = switch (ptr_ty.ptrSize()) {
3209 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
3210 else => ptr_ty.childType(),
3211 };
3212 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
32133717
3214 if (elem_size == 1) {3718fn ptrArithmetic(
3215 const base_tag: Mir.Inst.Tag = switch (tag) {3719 self: *Self,
3216 .ptr_add => .add,3720 tag: Air.Inst.Tag,
3217 .ptr_sub => .sub,3721 lhs_bind: ReadArg.Bind,
3218 else => unreachable,3722 rhs_bind: ReadArg.Bind,
3219 };3723 lhs_ty: Type,
3724 rhs_ty: Type,
3725 maybe_inst: ?Air.Inst.Index,
3726) InnerError!MCValue {
3727 switch (lhs_ty.zigTypeTag()) {
3728 .Pointer => {
3729 const mod = self.bin_file.options.module.?;
3730 assert(rhs_ty.eql(Type.usize, mod));
32203731
3221 return try self.binOpRegister(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);3732 const ptr_ty = lhs_ty;
3222 } else {3733 const elem_ty = switch (ptr_ty.ptrSize()) {
3223 // convert the offset into a byte offset by3734 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
3224 // multiplying it with elem_size3735 else => ptr_ty.childType(),
3225 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);3736 };
3226 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);3737 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
3227 return addr;3738
3228 }3739 const base_tag: Air.Inst.Tag = switch (tag) {
3229 },3740 .ptr_add => .add,
3741 .ptr_sub => .sub,
3230 else => unreachable,3742 else => unreachable,
3743 };
3744
3745 if (elem_size == 1) {
3746 return try self.addSub(base_tag, lhs_bind, rhs_bind, Type.usize, Type.usize, maybe_inst);
3747 } else {
3748 // convert the offset into a byte offset by
3749 // multiplying it with elem_size
3750 const imm_bind = ReadArg.Bind{ .mcv = .{ .immediate = elem_size } };
3751
3752 const offset = try self.mul(rhs_bind, imm_bind, Type.usize, Type.usize, null);
3753 const offset_bind = ReadArg.Bind{ .mcv = offset };
3754
3755 const addr = try self.addSub(base_tag, lhs_bind, offset_bind, Type.usize, Type.usize, null);
3756 return addr;
3231 }3757 }
3232 },3758 },
3233 else => unreachable,3759 else => unreachable,
...@@ -3312,9 +3838,8 @@ fn genInlineMemcpy(...@@ -3312,9 +3838,8 @@ fn genInlineMemcpy(
3312 // mov count, #03838 // mov count, #0
3313 _ = try self.addInst(.{3839 _ = try self.addInst(.{
3314 .tag = .mov,3840 .tag = .mov,
3315 .data = .{ .rr_op = .{3841 .data = .{ .r_op_mov = .{
3316 .rd = count,3842 .rd = count,
3317 .rn = .r0,
3318 .op = Instruction.Operand.imm(0, 0),3843 .op = Instruction.Operand.imm(0, 0),
3319 } },3844 } },
3320 });3845 });
...@@ -3323,8 +3848,7 @@ fn genInlineMemcpy(...@@ -3323,8 +3848,7 @@ fn genInlineMemcpy(
3323 // cmp count, len3848 // cmp count, len
3324 _ = try self.addInst(.{3849 _ = try self.addInst(.{
3325 .tag = .cmp,3850 .tag = .cmp,
3326 .data = .{ .rr_op = .{3851 .data = .{ .r_op_cmp = .{
3327 .rd = .r0,
3328 .rn = count,3852 .rn = count,
3329 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),3853 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
3330 } },3854 } },
...@@ -3418,9 +3942,8 @@ fn genInlineMemsetCode(...@@ -3418,9 +3942,8 @@ fn genInlineMemsetCode(
3418 // mov count, #03942 // mov count, #0
3419 _ = try self.addInst(.{3943 _ = try self.addInst(.{
3420 .tag = .mov,3944 .tag = .mov,
3421 .data = .{ .rr_op = .{3945 .data = .{ .r_op_mov = .{
3422 .rd = count,3946 .rd = count,
3423 .rn = .r0,
3424 .op = Instruction.Operand.imm(0, 0),3947 .op = Instruction.Operand.imm(0, 0),
3425 } },3948 } },
3426 });3949 });
...@@ -3429,8 +3952,7 @@ fn genInlineMemsetCode(...@@ -3429,8 +3952,7 @@ fn genInlineMemsetCode(
3429 // cmp count, len3952 // cmp count, len
3430 _ = try self.addInst(.{3953 _ = try self.addInst(.{
3431 .tag = .cmp,3954 .tag = .cmp,
3432 .data = .{ .rr_op = .{3955 .data = .{ .r_op_cmp = .{
3433 .rd = .r0,
3434 .rn = count,3956 .rn = count,
3435 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),3957 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
3436 } },3958 } },
...@@ -3568,7 +4090,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {...@@ -3568,7 +4090,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
3568 .register => |reg| blk: {4090 .register => |reg| blk: {
3569 const abi_size = @intCast(u32, ty.abiSize(self.target.*));4091 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
3570 const abi_align = ty.abiAlignment(self.target.*);4092 const abi_align = ty.abiAlignment(self.target.*);
3571 const stack_offset = try self.allocMem(inst, abi_size, abi_align);4093 const stack_offset = try self.allocMem(abi_size, abi_align, inst);
3572 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });4094 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
35734095
3574 break :blk MCValue{ .stack_offset = stack_offset };4096 break :blk MCValue{ .stack_offset = stack_offset };
...@@ -3655,7 +4177,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions....@@ -3655,7 +4177,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
3655 const ret_ty = fn_ty.fnReturnType();4177 const ret_ty = fn_ty.fnReturnType();
3656 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));4178 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3657 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));4179 const ret_abi_align = @intCast(u32, ret_ty.abiAlignment(self.target.*));
3658 const stack_offset = try self.allocMem(inst, ret_abi_size, ret_abi_align);4180 const stack_offset = try self.allocMem(ret_abi_size, ret_abi_align, inst);
36594181
3660 var ptr_ty_payload: Type.Payload.ElemType = .{4182 var ptr_ty_payload: Type.Payload.ElemType = .{
3661 .base = .{ .tag = .single_mut_pointer },4183 .base = .{ .tag = .single_mut_pointer },
...@@ -3843,14 +4365,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {...@@ -3843,14 +4365,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
3843 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));4365 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
3844 const abi_align = ret_ty.abiAlignment(self.target.*);4366 const abi_align = ret_ty.abiAlignment(self.target.*);
38454367
3846 // This is essentially allocMem without the4368 const offset = try self.allocMem(abi_size, abi_align, null);
3847 // instruction tracking
3848 if (abi_align > self.stack_align)
3849 self.stack_align = abi_align;
3850 // TODO find a free slot instead of always appending
3851 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
3852 self.next_stack_offset = offset;
3853 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
38544369
3855 const tmp_mcv = MCValue{ .stack_offset = offset };4370 const tmp_mcv = MCValue{ .stack_offset = offset };
3856 try self.load(tmp_mcv, ptr, ptr_ty);4371 try self.load(tmp_mcv, ptr, ptr_ty);
...@@ -3871,32 +4386,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {...@@ -3871,32 +4386,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
3871 const lhs_ty = self.air.typeOf(bin_op.lhs);4386 const lhs_ty = self.air.typeOf(bin_op.lhs);
38724387
3873 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {4388 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3874 const operands: BinOpOperands = .{ .inst = .{4389 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
3875 .inst = inst,
3876 .lhs = bin_op.lhs,
3877 .rhs = bin_op.rhs,
3878 } };
3879 break :blk try self.cmp(operands, lhs_ty, op);
3880 };4390 };
38814391
3882 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });4392 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
3883}4393}
38844394
3885const BinOpOperands = union(enum) {
3886 inst: struct {
3887 inst: Air.Inst.Index,
3888 lhs: Air.Inst.Ref,
3889 rhs: Air.Inst.Ref,
3890 },
3891 mcv: struct {
3892 lhs: MCValue,
3893 rhs: MCValue,
3894 },
3895};
3896
3897fn cmp(4395fn cmp(
3898 self: *Self,4396 self: *Self,
3899 operands: BinOpOperands,4397 lhs: ReadArg.Bind,
4398 rhs: ReadArg.Bind,
3900 lhs_ty: Type,4399 lhs_ty: Type,
3901 op: math.CompareOperator,4400 op: math.CompareOperator,
3902) !MCValue {4401) !MCValue {
...@@ -3926,22 +4425,47 @@ fn cmp(...@@ -3926,22 +4425,47 @@ fn cmp(
3926 if (int_info.bits <= 32) {4425 if (int_info.bits <= 32) {
3927 try self.spillCompareFlagsIfOccupied();4426 try self.spillCompareFlagsIfOccupied();
39284427
3929 switch (operands) {4428 var lhs_reg: Register = undefined;
3930 .inst => |inst_op| {4429 var rhs_reg: Register = undefined;
3931 const metadata: BinOpMetadata = .{
3932 .inst = inst_op.inst,
3933 .lhs = inst_op.lhs,
3934 .rhs = inst_op.rhs,
3935 };
3936 const lhs = try self.resolveInst(inst_op.lhs);
3937 const rhs = try self.resolveInst(inst_op.rhs);
39384430
3939 self.cpsr_flags_inst = inst_op.inst;4431 const rhs_immediate = try rhs.resolveToImmediate(self);
3940 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata);4432 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
3941 },4433
3942 .mcv => |mcv_op| {4434 if (rhs_immediate_ok) {
3943 _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null);4435 const read_args = [_]ReadArg{
3944 },4436 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4437 };
4438 try self.allocRegs(
4439 &read_args,
4440 &.{},
4441 null, // we won't be able to reuse a register as there are no write_regs
4442 );
4443
4444 _ = try self.addInst(.{
4445 .tag = .cmp,
4446 .data = .{ .r_op_cmp = .{
4447 .rn = lhs_reg,
4448 .op = Instruction.Operand.fromU32(rhs_immediate.?).?,
4449 } },
4450 });
4451 } else {
4452 const read_args = [_]ReadArg{
4453 .{ .ty = int_ty, .bind = lhs, .class = gp, .reg = &lhs_reg },
4454 .{ .ty = int_ty, .bind = rhs, .class = gp, .reg = &rhs_reg },
4455 };
4456 try self.allocRegs(
4457 &read_args,
4458 &.{},
4459 null, // we won't be able to reuse a register as there are no write_regs
4460 );
4461
4462 _ = try self.addInst(.{
4463 .tag = .cmp,
4464 .data = .{ .r_op_cmp = .{
4465 .rn = lhs_reg,
4466 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
4467 } },
4468 });
3945 }4469 }
39464470
3947 return switch (int_info.signedness) {4471 return switch (int_info.signedness) {
...@@ -4020,9 +4544,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {...@@ -4020,9 +4544,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
4020 // bne ...4544 // bne ...
4021 _ = try self.addInst(.{4545 _ = try self.addInst(.{
4022 .tag = .cmp,4546 .tag = .cmp,
4023 .cond = .al,4547 .data = .{ .r_op_cmp = .{
4024 .data = .{ .rr_op = .{
4025 .rd = .r0,
4026 .rn = reg,4548 .rn = reg,
4027 .op = Instruction.Operand.imm(1, 0),4549 .op = Instruction.Operand.imm(1, 0),
4028 } },4550 } },
...@@ -4132,7 +4654,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4132,7 +4654,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4132 if (else_value == .dead)4654 if (else_value == .dead)
4133 continue;4655 continue;
4134 // The instruction is only overridden in the else branch.4656 // The instruction is only overridden in the else branch.
4135 var i: usize = self.branch_stack.items.len - 2;4657 var i: usize = self.branch_stack.items.len - 1;
4136 while (true) {4658 while (true) {
4137 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?4659 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
4138 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {4660 if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| {
...@@ -4159,7 +4681,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4159,7 +4681,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4159 if (then_value == .dead)4681 if (then_value == .dead)
4160 continue;4682 continue;
4161 const parent_mcv = blk: {4683 const parent_mcv = blk: {
4162 var i: usize = self.branch_stack.items.len - 2;4684 var i: usize = self.branch_stack.items.len - 1;
4163 while (true) {4685 while (true) {
4164 i -= 1;4686 i -= 1;
4165 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {4687 if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| {
...@@ -4185,75 +4707,39 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4185,75 +4707,39 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
4185 return self.finishAir(inst, .unreach, .{ .none, .none, .none });4707 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
4186}4708}
41874709
4188fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {4710fn isNull(
4189 if (ty.isPtrLikeOptional()) {4711 self: *Self,
4190 assert(ty.abiSize(self.target.*) == 4);4712 operand_bind: ReadArg.Bind,
41914713 operand_ty: Type,
4192 const reg_mcv: MCValue = switch (operand) {4714) !MCValue {
4193 .register => operand,4715 if (operand_ty.isPtrLikeOptional()) {
4194 else => .{ .register = try self.copyToTmpRegister(ty, operand) },4716 assert(operand_ty.abiSize(self.target.*) == 4);
4195 };
4196
4197 _ = try self.addInst(.{
4198 .tag = .cmp,
4199 .data = .{ .rr_op = .{
4200 .rd = undefined,
4201 .rn = reg_mcv.register,
4202 .op = Instruction.Operand.fromU32(0).?,
4203 } },
4204 });
42054717
4206 return MCValue{ .cpsr_flags = .eq };4718 const imm_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = 0 } };
4719 return self.cmp(operand_bind, imm_bind, Type.usize, .eq);
4207 } else {4720 } else {
4208 return self.fail("TODO implement non-pointer optionals", .{});4721 return self.fail("TODO implement non-pointer optionals", .{});
4209 }4722 }
4210}4723}
42114724
4212fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {4725fn isNonNull(
4213 const is_null_result = try self.isNull(ty, operand);4726 self: *Self,
4727 operand_bind: ReadArg.Bind,
4728 operand_ty: Type,
4729) !MCValue {
4730 const is_null_result = try self.isNull(operand_bind, operand_ty);
4214 assert(is_null_result.cpsr_flags == .eq);4731 assert(is_null_result.cpsr_flags == .eq);
42154732
4216 return MCValue{ .cpsr_flags = .ne };4733 return MCValue{ .cpsr_flags = .ne };
4217}4734}
42184735
4219fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4220 const error_type = ty.errorUnionSet();
4221 const error_int_type = Type.initTag(.u16);
4222
4223 if (error_type.errorSetIsEmpty()) {
4224 return MCValue{ .immediate = 0 }; // always false
4225 }
4226
4227 const error_mcv = try self.errUnionErr(operand, ty);
4228 _ = try self.binOp(.cmp_eq, error_mcv, .{ .immediate = 0 }, error_int_type, error_int_type, null);
4229 return MCValue{ .cpsr_flags = .hi };
4230}
4231
4232fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue {
4233 const is_err_result = try self.isErr(ty, operand);
4234 switch (is_err_result) {
4235 .cpsr_flags => |cond| {
4236 assert(cond == .hi);
4237 return MCValue{ .cpsr_flags = cond.negate() };
4238 },
4239 .immediate => |imm| {
4240 assert(imm == 0);
4241 return MCValue{ .immediate = 1 };
4242 },
4243 else => unreachable,
4244 }
4245}
4246
4247fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {4736fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
4248 const un_op = self.air.instructions.items(.data)[inst].un_op;4737 const un_op = self.air.instructions.items(.data)[inst].un_op;
4249
4250 try self.spillCompareFlagsIfOccupied();
4251 self.cpsr_flags_inst = inst;
4252
4253 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4738 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4254 const operand = try self.resolveInst(un_op);4739 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4255 const ty = self.air.typeOf(un_op);4740 const operand_ty = self.air.typeOf(un_op);
4256 break :result try self.isNull(ty, operand);4741
4742 break :result try self.isNull(operand_bind, operand_ty);
4257 };4743 };
4258 return self.finishAir(inst, result, .{ un_op, .none, .none });4744 return self.finishAir(inst, result, .{ un_op, .none, .none });
4259}4745}
...@@ -4263,16 +4749,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4263,16 +4749,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4263 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4749 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4264 const operand_ptr = try self.resolveInst(un_op);4750 const operand_ptr = try self.resolveInst(un_op);
4265 const ptr_ty = self.air.typeOf(un_op);4751 const ptr_ty = self.air.typeOf(un_op);
4266 const operand: MCValue = blk: {4752 const elem_ty = ptr_ty.elemType();
4267 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4753
4268 // The MCValue that holds the pointer can be re-used as the value.4754 const operand = try self.allocRegOrMem(elem_ty, true, null);
4269 break :blk operand_ptr;
4270 } else {
4271 break :blk try self.allocRegOrMem(inst, true);
4272 }
4273 };
4274 try self.load(operand, operand_ptr, ptr_ty);4755 try self.load(operand, operand_ptr, ptr_ty);
4275 break :result try self.isNull(ptr_ty.elemType(), operand);4756
4757 break :result try self.isNull(.{ .mcv = operand }, elem_ty);
4276 };4758 };
4277 return self.finishAir(inst, result, .{ un_op, .none, .none });4759 return self.finishAir(inst, result, .{ un_op, .none, .none });
4278}4760}
...@@ -4280,9 +4762,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4280,9 +4762,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4280fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {4762fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
4281 const un_op = self.air.instructions.items(.data)[inst].un_op;4763 const un_op = self.air.instructions.items(.data)[inst].un_op;
4282 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4764 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4283 const operand = try self.resolveInst(un_op);4765 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4284 const ty = self.air.typeOf(un_op);4766 const operand_ty = self.air.typeOf(un_op);
4285 break :result try self.isNonNull(ty, operand);4767
4768 break :result try self.isNonNull(operand_bind, operand_ty);
4286 };4769 };
4287 return self.finishAir(inst, result, .{ un_op, .none, .none });4770 return self.finishAir(inst, result, .{ un_op, .none, .none });
4288}4771}
...@@ -4292,26 +4775,57 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4292,26 +4775,57 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
4292 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4775 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4293 const operand_ptr = try self.resolveInst(un_op);4776 const operand_ptr = try self.resolveInst(un_op);
4294 const ptr_ty = self.air.typeOf(un_op);4777 const ptr_ty = self.air.typeOf(un_op);
4295 const operand: MCValue = blk: {4778 const elem_ty = ptr_ty.elemType();
4296 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4779
4297 // The MCValue that holds the pointer can be re-used as the value.4780 const operand = try self.allocRegOrMem(elem_ty, true, null);
4298 break :blk operand_ptr;
4299 } else {
4300 break :blk try self.allocRegOrMem(inst, true);
4301 }
4302 };
4303 try self.load(operand, operand_ptr, ptr_ty);4781 try self.load(operand, operand_ptr, ptr_ty);
4304 break :result try self.isNonNull(ptr_ty.elemType(), operand);4782
4783 break :result try self.isNonNull(.{ .mcv = operand }, elem_ty);
4305 };4784 };
4306 return self.finishAir(inst, result, .{ un_op, .none, .none });4785 return self.finishAir(inst, result, .{ un_op, .none, .none });
4307}4786}
43084787
4788fn isErr(
4789 self: *Self,
4790 error_union_bind: ReadArg.Bind,
4791 error_union_ty: Type,
4792) !MCValue {
4793 const error_type = error_union_ty.errorUnionSet();
4794
4795 if (error_type.errorSetIsEmpty()) {
4796 return MCValue{ .immediate = 0 }; // always false
4797 }
4798
4799 const error_mcv = try self.errUnionErr(error_union_bind, error_union_ty, null);
4800 return try self.cmp(.{ .mcv = error_mcv }, .{ .mcv = .{ .immediate = 0 } }, error_type, .gt);
4801}
4802
4803fn isNonErr(
4804 self: *Self,
4805 error_union_bind: ReadArg.Bind,
4806 error_union_ty: Type,
4807) !MCValue {
4808 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
4809 switch (is_err_result) {
4810 .cpsr_flags => |cond| {
4811 assert(cond == .hi);
4812 return MCValue{ .cpsr_flags = cond.negate() };
4813 },
4814 .immediate => |imm| {
4815 assert(imm == 0);
4816 return MCValue{ .immediate = 1 };
4817 },
4818 else => unreachable,
4819 }
4820}
4821
4309fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {4822fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
4310 const un_op = self.air.instructions.items(.data)[inst].un_op;4823 const un_op = self.air.instructions.items(.data)[inst].un_op;
4311 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4824 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4312 const operand = try self.resolveInst(un_op);4825 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4313 const ty = self.air.typeOf(un_op);4826 const error_union_ty = self.air.typeOf(un_op);
4314 break :result try self.isErr(ty, operand);4827
4828 break :result try self.isErr(error_union_bind, error_union_ty);
4315 };4829 };
4316 return self.finishAir(inst, result, .{ un_op, .none, .none });4830 return self.finishAir(inst, result, .{ un_op, .none, .none });
4317}4831}
...@@ -4321,16 +4835,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4321,16 +4835,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4321 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4835 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4322 const operand_ptr = try self.resolveInst(un_op);4836 const operand_ptr = try self.resolveInst(un_op);
4323 const ptr_ty = self.air.typeOf(un_op);4837 const ptr_ty = self.air.typeOf(un_op);
4324 const operand: MCValue = blk: {4838 const elem_ty = ptr_ty.elemType();
4325 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4839
4326 // The MCValue that holds the pointer can be re-used as the value.4840 const operand = try self.allocRegOrMem(elem_ty, true, null);
4327 break :blk operand_ptr;
4328 } else {
4329 break :blk try self.allocRegOrMem(inst, true);
4330 }
4331 };
4332 try self.load(operand, operand_ptr, ptr_ty);4841 try self.load(operand, operand_ptr, ptr_ty);
4333 break :result try self.isErr(ptr_ty.elemType(), operand);4842
4843 break :result try self.isErr(.{ .mcv = operand }, elem_ty);
4334 };4844 };
4335 return self.finishAir(inst, result, .{ un_op, .none, .none });4845 return self.finishAir(inst, result, .{ un_op, .none, .none });
4336}4846}
...@@ -4338,9 +4848,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4338,9 +4848,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4338fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {4848fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
4339 const un_op = self.air.instructions.items(.data)[inst].un_op;4849 const un_op = self.air.instructions.items(.data)[inst].un_op;
4340 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4850 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4341 const operand = try self.resolveInst(un_op);4851 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4342 const ty = self.air.typeOf(un_op);4852 const error_union_ty = self.air.typeOf(un_op);
4343 break :result try self.isNonErr(ty, operand);4853
4854 break :result try self.isNonErr(error_union_bind, error_union_ty);
4344 };4855 };
4345 return self.finishAir(inst, result, .{ un_op, .none, .none });4856 return self.finishAir(inst, result, .{ un_op, .none, .none });
4346}4857}
...@@ -4350,16 +4861,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {...@@ -4350,16 +4861,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
4350 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {4861 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4351 const operand_ptr = try self.resolveInst(un_op);4862 const operand_ptr = try self.resolveInst(un_op);
4352 const ptr_ty = self.air.typeOf(un_op);4863 const ptr_ty = self.air.typeOf(un_op);
4353 const operand: MCValue = blk: {4864 const elem_ty = ptr_ty.elemType();
4354 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {4865
4355 // The MCValue that holds the pointer can be re-used as the value.4866 const operand = try self.allocRegOrMem(elem_ty, true, null);
4356 break :blk operand_ptr;
4357 } else {
4358 break :blk try self.allocRegOrMem(inst, true);
4359 }
4360 };
4361 try self.load(operand, operand_ptr, ptr_ty);4867 try self.load(operand, operand_ptr, ptr_ty);
4362 break :result try self.isNonErr(ptr_ty.elemType(), operand);4868
4869 break :result try self.isNonErr(.{ .mcv = operand }, elem_ty);
4363 };4870 };
4364 return self.finishAir(inst, result, .{ un_op, .none, .none });4871 return self.finishAir(inst, result, .{ un_op, .none, .none });
4365}4872}
...@@ -4456,14 +4963,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {...@@ -4456,14 +4963,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
4456 defer self.gpa.free(branch_into_prong_relocs);4963 defer self.gpa.free(branch_into_prong_relocs);
44574964
4458 for (items) |item, idx| {4965 for (items) |item, idx| {
4459 const condition = try self.resolveInst(pl_op.operand);4966 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq);
4460 const item_mcv = try self.resolveInst(item);
4461
4462 const operands: BinOpOperands = .{ .mcv = .{
4463 .lhs = condition,
4464 .rhs = item_mcv,
4465 } };
4466 const cmp_result = try self.cmp(operands, condition_ty, .neq);
4467 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);4967 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
4468 }4968 }
44694969
...@@ -4579,7 +5079,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {...@@ -4579,7 +5079,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
4579 .none, .dead, .unreach => unreachable,5079 .none, .dead, .unreach => unreachable,
4580 .register, .stack_offset, .memory => operand_mcv,5080 .register, .stack_offset, .memory => operand_mcv,
4581 .immediate, .stack_argument_offset, .cpsr_flags => blk: {5081 .immediate, .stack_argument_offset, .cpsr_flags => blk: {
4582 const new_mcv = try self.allocRegOrMem(block, true);5082 const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block);
4583 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);5083 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
4584 break :blk new_mcv;5084 break :blk new_mcv;
4585 },5085 },
...@@ -4832,9 +5332,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro...@@ -4832,9 +5332,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
4832 .register_v_flag => .vs,5332 .register_v_flag => .vs,
4833 else => unreachable,5333 else => unreachable,
4834 },5334 },
4835 .data = .{ .rr_op = .{5335 .data = .{ .r_op_mov = .{
4836 .rd = cond_reg,5336 .rd = cond_reg,
4837 .rn = .r0,
4838 .op = Instruction.Operand.fromU32(1).?,5337 .op = Instruction.Operand.fromU32(1).?,
4839 } },5338 } },
4840 });5339 });
...@@ -4935,9 +5434,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4935,9 +5434,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4935 // mov reg, 05434 // mov reg, 0
4936 _ = try self.addInst(.{5435 _ = try self.addInst(.{
4937 .tag = .mov,5436 .tag = .mov,
4938 .data = .{ .rr_op = .{5437 .data = .{ .r_op_mov = .{
4939 .rd = reg,5438 .rd = reg,
4940 .rn = .r0,
4941 .op = zero,5439 .op = zero,
4942 } },5440 } },
4943 });5441 });
...@@ -4946,9 +5444,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4946,9 +5444,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4946 _ = try self.addInst(.{5444 _ = try self.addInst(.{
4947 .tag = .mov,5445 .tag = .mov,
4948 .cond = condition,5446 .cond = condition,
4949 .data = .{ .rr_op = .{5447 .data = .{ .r_op_mov = .{
4950 .rd = reg,5448 .rd = reg,
4951 .rn = .r0,
4952 .op = one,5449 .op = one,
4953 } },5450 } },
4954 });5451 });
...@@ -4957,18 +5454,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4957,18 +5454,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4957 if (Instruction.Operand.fromU32(x)) |op| {5454 if (Instruction.Operand.fromU32(x)) |op| {
4958 _ = try self.addInst(.{5455 _ = try self.addInst(.{
4959 .tag = .mov,5456 .tag = .mov,
4960 .data = .{ .rr_op = .{5457 .data = .{ .r_op_mov = .{
4961 .rd = reg,5458 .rd = reg,
4962 .rn = .r0,
4963 .op = op,5459 .op = op,
4964 } },5460 } },
4965 });5461 });
4966 } else if (Instruction.Operand.fromU32(~x)) |op| {5462 } else if (Instruction.Operand.fromU32(~x)) |op| {
4967 _ = try self.addInst(.{5463 _ = try self.addInst(.{
4968 .tag = .mvn,5464 .tag = .mvn,
4969 .data = .{ .rr_op = .{5465 .data = .{ .r_op_mov = .{
4970 .rd = reg,5466 .rd = reg,
4971 .rn = .r0,
4972 .op = op,5467 .op = op,
4973 } },5468 } },
4974 });5469 });
...@@ -4984,9 +5479,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -4984,9 +5479,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
4984 } else {5479 } else {
4985 _ = try self.addInst(.{5480 _ = try self.addInst(.{
4986 .tag = .mov,5481 .tag = .mov,
4987 .data = .{ .rr_op = .{5482 .data = .{ .r_op_mov = .{
4988 .rd = reg,5483 .rd = reg,
4989 .rn = .r0,
4990 .op = Instruction.Operand.imm(@truncate(u8, x), 0),5484 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
4991 } },5485 } },
4992 });5486 });
...@@ -5028,9 +5522,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5028,9 +5522,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5028 // orr reg, reg, #0xdd, 85522 // orr reg, reg, #0xdd, 8
5029 _ = try self.addInst(.{5523 _ = try self.addInst(.{
5030 .tag = .mov,5524 .tag = .mov,
5031 .data = .{ .rr_op = .{5525 .data = .{ .r_op_mov = .{
5032 .rd = reg,5526 .rd = reg,
5033 .rn = .r0,
5034 .op = Instruction.Operand.imm(@truncate(u8, x), 0),5527 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
5035 } },5528 } },
5036 });5529 });
...@@ -5069,9 +5562,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -5069,9 +5562,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
5069 // mov reg, src_reg5562 // mov reg, src_reg
5070 _ = try self.addInst(.{5563 _ = try self.addInst(.{
5071 .tag = .mov,5564 .tag = .mov,
5072 .data = .{ .rr_op = .{5565 .data = .{ .r_op_mov = .{
5073 .rd = reg,5566 .rd = reg,
5074 .rn = .r0,
5075 .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),5567 .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
5076 } },5568 } },
5077 });5569 });
...@@ -5307,7 +5799,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {...@@ -5307,7 +5799,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
5307 const array_ty = ptr_ty.childType();5799 const array_ty = ptr_ty.childType();
5308 const array_len = @intCast(u32, array_ty.arrayLen());5800 const array_len = @intCast(u32, array_ty.arrayLen());
53095801
5310 const stack_offset = try self.allocMem(inst, 8, 8);5802 const stack_offset = try self.allocMem(8, 8, inst);
5311 try self.genSetStack(ptr_ty, stack_offset, ptr);5803 try self.genSetStack(ptr_ty, stack_offset, ptr);
5312 try self.genSetStack(Type.initTag(.usize), stack_offset - 4, .{ .immediate = array_len });5804 try self.genSetStack(Type.initTag(.usize), stack_offset - 4, .{ .immediate = array_len });
5313 break :result MCValue{ .stack_offset = stack_offset };5805 break :result MCValue{ .stack_offset = stack_offset };
...@@ -5461,15 +5953,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {...@@ -5461,15 +5953,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
5461 const extra = self.air.extraData(Air.Try, pl_op.payload);5953 const extra = self.air.extraData(Air.Try, pl_op.payload);
5462 const body = self.air.extra[extra.end..][0..extra.data.body_len];5954 const body = self.air.extra[extra.end..][0..extra.data.body_len];
5463 const result: MCValue = result: {5955 const result: MCValue = result: {
5956 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
5464 const error_union_ty = self.air.typeOf(pl_op.operand);5957 const error_union_ty = self.air.typeOf(pl_op.operand);
5465 const error_union = try self.resolveInst(pl_op.operand);5958 const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
5466 const is_err_result = try self.isErr(error_union_ty, error_union);5959 const error_union_align = error_union_ty.abiAlignment(self.target.*);
5960
5961 // The error union will die in the body. However, we need the
5962 // error union after the body in order to extract the payload
5963 // of the error union, so we create a copy of it
5964 const error_union_copy = try self.allocMem(error_union_size, error_union_align, null);
5965 try self.genSetStack(error_union_ty, error_union_copy, try error_union_bind.resolveToMcv(self));
5966
5967 const is_err_result = try self.isErr(error_union_bind, error_union_ty);
5467 const reloc = try self.condBr(is_err_result);5968 const reloc = try self.condBr(is_err_result);
54685969
5469 try self.genBody(body);5970 try self.genBody(body);
5470
5471 try self.performReloc(reloc);5971 try self.performReloc(reloc);
5472 break :result try self.errUnionPayload(error_union, error_union_ty);5972
5973 break :result try self.errUnionPayload(.{ .mcv = .{ .stack_offset = error_union_copy } }, error_union_ty, null);
5473 };5974 };
5474 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });5975 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
5475}5976}
src/arch/arm/Emit.zig+88-12
...@@ -11,6 +11,7 @@ const link = @import("../../link.zig");...@@ -11,6 +11,7 @@ const link = @import("../../link.zig");
11const Module = @import("../../Module.zig");11const Module = @import("../../Module.zig");
12const Type = @import("../../type.zig").Type;12const Type = @import("../../type.zig").Type;
13const ErrorMsg = Module.ErrorMsg;13const ErrorMsg = Module.ErrorMsg;
14const Target = std.Target;
14const assert = std.debug.assert;15const assert = std.debug.assert;
15const DW = std.dwarf;16const DW = std.dwarf;
16const leb128 = std.leb;17const leb128 = std.leb;
...@@ -93,6 +94,8 @@ pub fn emitMir(...@@ -93,6 +94,8 @@ pub fn emitMir(
93 .sub => try emit.mirDataProcessing(inst),94 .sub => try emit.mirDataProcessing(inst),
94 .subs => try emit.mirDataProcessing(inst),95 .subs => try emit.mirDataProcessing(inst),
9596
97 .sub_sp_scratch_r0 => try emit.mirSubStackPointer(inst),
98
96 .asr => try emit.mirShift(inst),99 .asr => try emit.mirShift(inst),
97 .lsl => try emit.mirShift(inst),100 .lsl => try emit.mirShift(inst),
98 .lsr => try emit.mirShift(inst),101 .lsr => try emit.mirShift(inst),
...@@ -190,6 +193,24 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {...@@ -190,6 +193,24 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
190 .dbg_epilogue_begin,193 .dbg_epilogue_begin,
191 .dbg_prologue_end,194 .dbg_prologue_end,
192 => return 0,195 => return 0,
196
197 .sub_sp_scratch_r0 => {
198 const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
199
200 if (imm32 == 0) {
201 return 0 * 4;
202 } else if (Instruction.Operand.fromU32(imm32) != null) {
203 // sub
204 return 1 * 4;
205 } else if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) {
206 // movw; movt; sub
207 return 3 * 4;
208 } else {
209 // mov; orr; orr; orr; sub
210 return 5 * 4;
211 }
212 },
213
193 else => return 4,214 else => return 4,
194 }215 }
195}216}
...@@ -385,20 +406,75 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {...@@ -385,20 +406,75 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
385fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {406fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
386 const tag = emit.mir.instructions.items(.tag)[inst];407 const tag = emit.mir.instructions.items(.tag)[inst];
387 const cond = emit.mir.instructions.items(.cond)[inst];408 const cond = emit.mir.instructions.items(.cond)[inst];
388 const rr_op = emit.mir.instructions.items(.data)[inst].rr_op;
389409
390 switch (tag) {410 switch (tag) {
391 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),411 .add,
392 .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)),412 .adds,
393 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),413 .@"and",
394 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),414 .eor,
395 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),415 .orr,
396 .mov => try emit.writeInstruction(Instruction.mov(cond, rr_op.rd, rr_op.op)),416 .rsb,
397 .mvn => try emit.writeInstruction(Instruction.mvn(cond, rr_op.rd, rr_op.op)),417 .sub,
398 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),418 .subs,
399 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),419 => {
400 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),420 const rr_op = emit.mir.instructions.items(.data)[inst].rr_op;
401 .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)),421 switch (tag) {
422 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),
423 .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)),
424 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
425 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),
426 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
427 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
428 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
429 .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)),
430 else => unreachable,
431 }
432 },
433 .cmp => {
434 const r_op_cmp = emit.mir.instructions.items(.data)[inst].r_op_cmp;
435 try emit.writeInstruction(Instruction.cmp(cond, r_op_cmp.rn, r_op_cmp.op));
436 },
437 .mov,
438 .mvn,
439 => {
440 const r_op_mov = emit.mir.instructions.items(.data)[inst].r_op_mov;
441 switch (tag) {
442 .mov => try emit.writeInstruction(Instruction.mov(cond, r_op_mov.rd, r_op_mov.op)),
443 .mvn => try emit.writeInstruction(Instruction.mvn(cond, r_op_mov.rd, r_op_mov.op)),
444 else => unreachable,
445 }
446 },
447 else => unreachable,
448 }
449}
450
451fn mirSubStackPointer(emit: *Emit, inst: Mir.Inst.Index) !void {
452 const tag = emit.mir.instructions.items(.tag)[inst];
453 const cond = emit.mir.instructions.items(.cond)[inst];
454 const imm32 = emit.mir.instructions.items(.data)[inst].imm32;
455
456 switch (tag) {
457 .sub_sp_scratch_r0 => {
458 if (imm32 == 0) return;
459
460 const operand = Instruction.Operand.fromU32(imm32) orelse blk: {
461 const scratch: Register = .r0;
462
463 if (Target.arm.featureSetHas(emit.target.cpu.features, .has_v7)) {
464 try emit.writeInstruction(Instruction.movw(cond, scratch, @truncate(u16, imm32)));
465 try emit.writeInstruction(Instruction.movt(cond, scratch, @truncate(u16, imm32 >> 16)));
466 } else {
467 try emit.writeInstruction(Instruction.mov(cond, scratch, Instruction.Operand.imm(@truncate(u8, imm32), 0)));
468 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 8), 12)));
469 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 16), 8)));
470 try emit.writeInstruction(Instruction.orr(cond, scratch, scratch, Instruction.Operand.imm(@truncate(u8, imm32 >> 24), 4)));
471 }
472
473 break :blk Instruction.Operand.reg(scratch, Instruction.Operand.Shift.none);
474 };
475
476 try emit.writeInstruction(Instruction.sub(cond, .sp, .sp, operand));
477 },
402 else => unreachable,478 else => unreachable,
403 }479 }
404}480}
src/arch/arm/Mir.zig+23
...@@ -111,6 +111,11 @@ pub const Inst = struct {...@@ -111,6 +111,11 @@ pub const Inst = struct {
111 strh,111 strh,
112 /// Subtract112 /// Subtract
113 sub,113 sub,
114 /// Pseudo-instruction: Subtract 32-bit immediate from stack
115 ///
116 /// r0 can be used by Emit as a scratch register for loading
117 /// the immediate
118 sub_sp_scratch_r0,
114 /// Subtract, update condition flags119 /// Subtract, update condition flags
115 subs,120 subs,
116 /// Supervisor Call121 /// Supervisor Call
...@@ -144,6 +149,10 @@ pub const Inst = struct {...@@ -144,6 +149,10 @@ pub const Inst = struct {
144 ///149 ///
145 /// Used by e.g. svc150 /// Used by e.g. svc
146 imm24: u24,151 imm24: u24,
152 /// A 32-bit immediate value.
153 ///
154 /// Used by e.g. sub_sp_scratch_r0
155 imm32: u32,
147 /// Index into `extra`. Meaning of what can be found there is context-dependent.156 /// Index into `extra`. Meaning of what can be found there is context-dependent.
148 ///157 ///
149 /// Used by e.g. load_memory158 /// Used by e.g. load_memory
...@@ -166,6 +175,20 @@ pub const Inst = struct {...@@ -166,6 +175,20 @@ pub const Inst = struct {
166 rd: Register,175 rd: Register,
167 imm16: u16,176 imm16: u16,
168 },177 },
178 /// A register and an operand
179 ///
180 /// Used by mov and mvn
181 r_op_mov: struct {
182 rd: Register,
183 op: bits.Instruction.Operand,
184 },
185 /// A register and an operand
186 ///
187 /// Used by cmp
188 r_op_cmp: struct {
189 rn: Register,
190 op: bits.Instruction.Operand,
191 },
169 /// Two registers and a shift amount192 /// Two registers and a shift amount
170 ///193 ///
171 /// Used by e.g. lsl194 /// Used by e.g. lsl
test/behavior/alignof.zig-1
...@@ -13,7 +13,6 @@ const Foo = struct {...@@ -13,7 +13,6 @@ const Foo = struct {
13test "@alignOf(T) before referencing T" {13test "@alignOf(T) before referencing T" {
14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
17 comptime try expect(@alignOf(Foo) != maxInt(usize));16 comptime try expect(@alignOf(Foo) != maxInt(usize));
18 if (native_arch == .x86_64) {17 if (native_arch == .x86_64) {
19 comptime try expect(@alignOf(Foo) == 4);18 comptime try expect(@alignOf(Foo) == 4);
test/behavior/array.zig-5
...@@ -175,7 +175,6 @@ test "nested arrays of integers" {...@@ -175,7 +175,6 @@ test "nested arrays of integers" {
175175
176test "implicit comptime in array type size" {176test "implicit comptime in array type size" {
177 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;177 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
179178
180 var arr: [plusOne(10)]bool = undefined;179 var arr: [plusOne(10)]bool = undefined;
181 try expect(arr.len == 11);180 try expect(arr.len == 11);
...@@ -245,7 +244,6 @@ const Sub = struct { b: u8 };...@@ -245,7 +244,6 @@ const Sub = struct { b: u8 };
245const Str = struct { a: []Sub };244const Str = struct { a: []Sub };
246test "set global var array via slice embedded in struct" {245test "set global var array via slice embedded in struct" {
247 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;246 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
248 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
249247
250 var s = Str{ .a = s_array[0..] };248 var s = Str{ .a = s_array[0..] };
251249
...@@ -298,7 +296,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {...@@ -298,7 +296,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
298296
299test "comptime evaluating function that takes array by value" {297test "comptime evaluating function that takes array by value" {
300 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
301 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
302299
303 const arr = [_]u8{ 1, 2 };300 const arr = [_]u8{ 1, 2 };
304 const x = comptime testArrayByValAtComptime(arr);301 const x = comptime testArrayByValAtComptime(arr);
...@@ -427,7 +424,6 @@ test "anonymous literal in array" {...@@ -427,7 +424,6 @@ test "anonymous literal in array" {
427424
428test "access the null element of a null terminated array" {425test "access the null element of a null terminated array" {
429 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
430 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
431427
432 const S = struct {428 const S = struct {
433 fn doTheTest() !void {429 fn doTheTest() !void {
...@@ -484,7 +480,6 @@ test "sentinel element count towards the ABI size calculation" {...@@ -484,7 +480,6 @@ test "sentinel element count towards the ABI size calculation" {
484test "zero-sized array with recursive type definition" {480test "zero-sized array with recursive type definition" {
485 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
486 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO482 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
487 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
488483
489 const U = struct {484 const U = struct {
490 fn foo(comptime T: type, comptime n: usize) type {485 fn foo(comptime T: type, comptime n: usize) type {
test/behavior/basic.zig-4
...@@ -465,7 +465,6 @@ fn nine() u8 {...@@ -465,7 +465,6 @@ fn nine() u8 {
465465
466test "struct inside function" {466test "struct inside function" {
467 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;467 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
468 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
469468
470 try testStructInFn();469 try testStructInFn();
471 comptime try testStructInFn();470 comptime try testStructInFn();
...@@ -514,7 +513,6 @@ var global_foo: *i32 = undefined;...@@ -514,7 +513,6 @@ var global_foo: *i32 = undefined;
514513
515test "peer result location with typed parent, runtime condition, comptime prongs" {514test "peer result location with typed parent, runtime condition, comptime prongs" {
516 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
517 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
518516
519 const S = struct {517 const S = struct {
520 fn doTheTest(arg: i32) i32 {518 fn doTheTest(arg: i32) i32 {
...@@ -643,7 +641,6 @@ test "global constant is loaded with a runtime-known index" {...@@ -643,7 +641,6 @@ test "global constant is loaded with a runtime-known index" {
643641
644test "multiline string literal is null terminated" {642test "multiline string literal is null terminated" {
645 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
647644
648 const s1 =645 const s1 =
649 \\one646 \\one
...@@ -1060,7 +1057,6 @@ comptime {...@@ -1060,7 +1057,6 @@ comptime {
10601057
1061test "switch inside @as gets correct type" {1058test "switch inside @as gets correct type" {
1062 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1059 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1063 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10641060
1065 var a: u32 = 0;1061 var a: u32 = 0;
1066 var b: [2]u32 = undefined;1062 var b: [2]u32 = undefined;
test/behavior/bitcast.zig-1
...@@ -138,7 +138,6 @@ test "@bitCast extern structs at runtime and comptime" {...@@ -138,7 +138,6 @@ test "@bitCast extern structs at runtime and comptime" {
138 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;138 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
139 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;139 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
140 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;140 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
142141
143 const Full = extern struct {142 const Full = extern struct {
144 number: u16,143 number: u16,
test/behavior/cast.zig-10
...@@ -523,7 +523,6 @@ fn testCastConstArrayRefToConstSlice() !void {...@@ -523,7 +523,6 @@ fn testCastConstArrayRefToConstSlice() !void {
523523
524test "peer type resolution: error and [N]T" {524test "peer type resolution: error and [N]T" {
525 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;525 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
526 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
527526
528 try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));527 try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
529 comptime try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));528 comptime try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
...@@ -548,7 +547,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {...@@ -548,7 +547,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
548test "single-item pointer of array to slice to unknown length pointer" {547test "single-item pointer of array to slice to unknown length pointer" {
549 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;548 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
550 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO549 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
551 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
552550
553 try testCastPtrOfArrayToSliceAndPtr();551 try testCastPtrOfArrayToSliceAndPtr();
554 comptime try testCastPtrOfArrayToSliceAndPtr();552 comptime try testCastPtrOfArrayToSliceAndPtr();
...@@ -578,7 +576,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {...@@ -578,7 +576,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {
578test "cast *[1][*]const u8 to [*]const ?[*]const u8" {576test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
579 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;577 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
580 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO578 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
581 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
582 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO579 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
583580
584 const window_name = [1][*]const u8{"window name"};581 const window_name = [1][*]const u8{"window name"};
...@@ -649,7 +646,6 @@ test "@floatCast cast down" {...@@ -649,7 +646,6 @@ test "@floatCast cast down" {
649test "peer type resolution: unreachable, error set, unreachable" {646test "peer type resolution: unreachable, error set, unreachable" {
650 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO647 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
651 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO648 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
653649
654 const Error = error{650 const Error = error{
655 FileDescriptorAlreadyPresentInSet,651 FileDescriptorAlreadyPresentInSet,
...@@ -922,7 +918,6 @@ test "peer cast *[N:x]T to *[N]T" {...@@ -922,7 +918,6 @@ test "peer cast *[N:x]T to *[N]T" {
922918
923test "peer cast [*:x]T to [*]T" {919test "peer cast [*:x]T to [*]T" {
924 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
925 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
926 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO921 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
927922
928 const S = struct {923 const S = struct {
...@@ -964,7 +959,6 @@ test "peer cast [:x]T to [*:x]T" {...@@ -964,7 +959,6 @@ test "peer cast [:x]T to [*:x]T" {
964959
965test "peer type resolution implicit cast to return type" {960test "peer type resolution implicit cast to return type" {
966 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;961 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
967 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
968962
969 const S = struct {963 const S = struct {
970 fn doTheTest() !void {964 fn doTheTest() !void {
...@@ -984,7 +978,6 @@ test "peer type resolution implicit cast to return type" {...@@ -984,7 +978,6 @@ test "peer type resolution implicit cast to return type" {
984978
985test "peer type resolution implicit cast to variable type" {979test "peer type resolution implicit cast to variable type" {
986 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;980 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
987 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
988 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO981 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
989982
990 const S = struct {983 const S = struct {
...@@ -1009,7 +1002,6 @@ test "variable initialization uses result locations properly with regards to the...@@ -1009,7 +1002,6 @@ test "variable initialization uses result locations properly with regards to the
10091002
1010test "cast between C pointer with different but compatible types" {1003test "cast between C pointer with different but compatible types" {
1011 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1004 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1012 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10131005
1014 const S = struct {1006 const S = struct {
1015 fn foo(arg: [*]c_ushort) u16 {1007 fn foo(arg: [*]c_ushort) u16 {
...@@ -1026,7 +1018,6 @@ test "cast between C pointer with different but compatible types" {...@@ -1026,7 +1018,6 @@ test "cast between C pointer with different but compatible types" {
10261018
1027test "peer type resolve string lit with sentinel-terminated mutable slice" {1019test "peer type resolve string lit with sentinel-terminated mutable slice" {
1028 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1020 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1030 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO1021 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10311022
1032 var array: [4:0]u8 = undefined;1023 var array: [4:0]u8 = undefined;
...@@ -1079,7 +1070,6 @@ test "comptime float casts" {...@@ -1079,7 +1070,6 @@ test "comptime float casts" {
1079test "pointer reinterpret const float to int" {1070test "pointer reinterpret const float to int" {
1080 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1071 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1081 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO1072 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1082 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10831073
1084 // The hex representation is 0x3fe3333333333303.1074 // The hex representation is 0x3fe3333333333303.
1085 const float: f64 = 5.99999999999994648725e-01;1075 const float: f64 = 5.99999999999994648725e-01;
test/behavior/comptime_memory.zig-2
...@@ -87,7 +87,6 @@ fn bigToNativeEndian(comptime T: type, v: T) T {...@@ -87,7 +87,6 @@ fn bigToNativeEndian(comptime T: type, v: T) T {
87test "type pun endianness" {87test "type pun endianness" {
88 if (builtin.zig_backend == .stage1) return error.SkipZigTest;88 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
89 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO89 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
90 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9190
92 comptime {91 comptime {
93 const StructOfBytes = extern struct { x: [4]u8 };92 const StructOfBytes = extern struct { x: [4]u8 };
...@@ -398,7 +397,6 @@ test "offset field ptr by enclosing array element size" {...@@ -398,7 +397,6 @@ test "offset field ptr by enclosing array element size" {
398test "accessing reinterpreted memory of parent object" {397test "accessing reinterpreted memory of parent object" {
399 if (builtin.zig_backend == .stage1) return error.SkipZigTest;398 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
400 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO399 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
401 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
402 const S = extern struct {400 const S = extern struct {
403 a: f32,401 a: f32,
404 b: [4]u8,402 b: [4]u8,
test/behavior/const_slice_child.zig-1
...@@ -9,7 +9,6 @@ var argv: [*]const [*]const u8 = undefined;...@@ -9,7 +9,6 @@ var argv: [*]const [*]const u8 = undefined;
9test "const slice child" {9test "const slice child" {
10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO10 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO11 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1312
14 const strs = [_][*]const u8{ "one", "two", "three" };13 const strs = [_][*]const u8{ "one", "two", "three" };
15 argv = &strs;14 argv = &strs;
test/behavior/enum.zig-6
...@@ -606,7 +606,6 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {...@@ -606,7 +606,6 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
606}606}
607607
608test "enum with specified tag values" {608test "enum with specified tag values" {
609 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
610 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;609 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
611610
612 try testEnumWithSpecifiedTagValues(MultipleChoice.C);611 try testEnumWithSpecifiedTagValues(MultipleChoice.C);
...@@ -614,7 +613,6 @@ test "enum with specified tag values" {...@@ -614,7 +613,6 @@ test "enum with specified tag values" {
614}613}
615614
616test "non-exhaustive enum" {615test "non-exhaustive enum" {
617 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
618 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;616 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
619617
620 const S = struct {618 const S = struct {
...@@ -677,7 +675,6 @@ test "empty non-exhaustive enum" {...@@ -677,7 +675,6 @@ test "empty non-exhaustive enum" {
677}675}
678676
679test "single field non-exhaustive enum" {677test "single field non-exhaustive enum" {
680 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
681 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;678 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
682679
683 const S = struct {680 const S = struct {
...@@ -741,7 +738,6 @@ test "cast integer literal to enum" {...@@ -741,7 +738,6 @@ test "cast integer literal to enum" {
741}738}
742739
743test "enum with specified and unspecified tag values" {740test "enum with specified and unspecified tag values" {
744 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
745 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;741 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
746742
747 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);743 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
...@@ -925,7 +921,6 @@ test "enum literal casting to tagged union" {...@@ -925,7 +921,6 @@ test "enum literal casting to tagged union" {
925const Bar = enum { A, B, C, D };921const Bar = enum { A, B, C, D };
926922
927test "enum literal casting to error union with payload enum" {923test "enum literal casting to error union with payload enum" {
928 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
929 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;924 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
930925
931 var bar: error{B}!Bar = undefined;926 var bar: error{B}!Bar = undefined;
...@@ -1132,7 +1127,6 @@ test "tag name functions are unique" {...@@ -1132,7 +1127,6 @@ test "tag name functions are unique" {
11321127
1133test "size of enum with only one tag which has explicit integer tag type" {1128test "size of enum with only one tag which has explicit integer tag type" {
1134 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;1129 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1135 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1136 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;1130 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11371131
1138 const E = enum(u8) { nope = 10 };1132 const E = enum(u8) { nope = 10 };
test/behavior/error.zig-4
...@@ -222,7 +222,6 @@ fn testErrorSetType() !void {...@@ -222,7 +222,6 @@ fn testErrorSetType() !void {
222222
223test "explicit error set cast" {223test "explicit error set cast" {
224 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO224 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
225 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
226 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
227226
228 try testExplicitErrorSetCast(Set1.A);227 try testExplicitErrorSetCast(Set1.A);
...@@ -282,7 +281,6 @@ test "inferred empty error set comptime catch" {...@@ -282,7 +281,6 @@ test "inferred empty error set comptime catch" {
282}281}
283282
284test "error union peer type resolution" {283test "error union peer type resolution" {
285 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
286 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO284 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
287285
288 try testErrorUnionPeerTypeResolution(1);286 try testErrorUnionPeerTypeResolution(1);
...@@ -327,7 +325,6 @@ fn foo3(b: usize) Error!usize {...@@ -327,7 +325,6 @@ fn foo3(b: usize) Error!usize {
327325
328test "error: Infer error set from literals" {326test "error: Infer error set from literals" {
329 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;327 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
330 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
331328
332 _ = nullLiteral("n") catch |err| handleErrors(err);329 _ = nullLiteral("n") catch |err| handleErrors(err);
333 _ = floatLiteral("n") catch |err| handleErrors(err);330 _ = floatLiteral("n") catch |err| handleErrors(err);
...@@ -700,7 +697,6 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" {...@@ -700,7 +697,6 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" {
700697
701test "simple else prong allowed even when all errors handled" {698test "simple else prong allowed even when all errors handled" {
702 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO699 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
703 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
704 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO700 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
705701
706 const S = struct {702 const S = struct {
test/behavior/eval.zig-10
...@@ -69,7 +69,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 {...@@ -69,7 +69,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 {
69}69}
7070
71test "constant expressions" {71test "constant expressions" {
72 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
73 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO72 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7473
75 var array: [array_size]u8 = undefined;74 var array: [array_size]u8 = undefined;
...@@ -138,7 +137,6 @@ test "pointer to type" {...@@ -138,7 +137,6 @@ test "pointer to type" {
138test "a type constructed in a global expression" {137test "a type constructed in a global expression" {
139 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO138 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
140 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO139 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
142140
143 var l: List = undefined;141 var l: List = undefined;
144 l.array[0] = 10;142 l.array[0] = 10;
...@@ -338,7 +336,6 @@ fn doesAlotT(comptime T: type, value: usize) T {...@@ -338,7 +336,6 @@ fn doesAlotT(comptime T: type, value: usize) T {
338}336}
339337
340test "@setEvalBranchQuota at same scope as generic function call" {338test "@setEvalBranchQuota at same scope as generic function call" {
341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
342 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO339 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
343340
344 try expect(doesAlotT(u32, 2) == 2);341 try expect(doesAlotT(u32, 2) == 2);
...@@ -565,7 +562,6 @@ test "inlined loop has array literal with elided runtime scope on first iteratio...@@ -565,7 +562,6 @@ test "inlined loop has array literal with elided runtime scope on first iteratio
565}562}
566563
567test "ptr to local array argument at comptime" {564test "ptr to local array argument at comptime" {
568 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
569 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO565 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
570566
571 comptime {567 comptime {
...@@ -806,7 +802,6 @@ test "array concatenation sets the sentinel - value" {...@@ -806,7 +802,6 @@ test "array concatenation sets the sentinel - value" {
806test "array concatenation sets the sentinel - pointer" {802test "array concatenation sets the sentinel - pointer" {
807 if (builtin.zig_backend == .stage1) return error.SkipZigTest;803 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
808 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;804 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
809 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
810 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;805 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
811806
812 var a = [2]u3{ 1, 7 };807 var a = [2]u3{ 1, 7 };
...@@ -956,7 +951,6 @@ test "const local with comptime init through array init" {...@@ -956,7 +951,6 @@ test "const local with comptime init through array init" {
956951
957test "closure capture type of runtime-known parameter" {952test "closure capture type of runtime-known parameter" {
958 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO953 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
959 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
960954
961 const S = struct {955 const S = struct {
962 fn b(c: anytype) !void {956 fn b(c: anytype) !void {
...@@ -1074,7 +1068,6 @@ test "comptime break operand passing through runtime switch converted to runtime...@@ -1074,7 +1068,6 @@ test "comptime break operand passing through runtime switch converted to runtime
10741068
1075test "no dependency loop for alignment of self struct" {1069test "no dependency loop for alignment of self struct" {
1076 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1070 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1077 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10781071
1079 const S = struct {1072 const S = struct {
1080 fn doTheTest() !void {1073 fn doTheTest() !void {
...@@ -1111,7 +1104,6 @@ test "no dependency loop for alignment of self struct" {...@@ -1111,7 +1104,6 @@ test "no dependency loop for alignment of self struct" {
11111104
1112test "no dependency loop for alignment of self bare union" {1105test "no dependency loop for alignment of self bare union" {
1113 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1106 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1114 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11151107
1116 const S = struct {1108 const S = struct {
1117 fn doTheTest() !void {1109 fn doTheTest() !void {
...@@ -1148,7 +1140,6 @@ test "no dependency loop for alignment of self bare union" {...@@ -1148,7 +1140,6 @@ test "no dependency loop for alignment of self bare union" {
11481140
1149test "no dependency loop for alignment of self tagged union" {1141test "no dependency loop for alignment of self tagged union" {
1150 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11521143
1153 const S = struct {1144 const S = struct {
1154 fn doTheTest() !void {1145 fn doTheTest() !void {
...@@ -1336,7 +1327,6 @@ test "lazy sizeof is resolved in division" {...@@ -1336,7 +1327,6 @@ test "lazy sizeof is resolved in division" {
1336}1327}
13371328
1338test "lazy value is resolved as slice operand" {1329test "lazy value is resolved as slice operand" {
1339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1340 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1330 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13411331
1342 const A = struct { a: u32 };1332 const A = struct { a: u32 };
test/behavior/field_parent_ptr.zig-2
...@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;...@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
2const builtin = @import("builtin");2const builtin = @import("builtin");
33
4test "@fieldParentPtr non-first field" {4test "@fieldParentPtr non-first field" {
5 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;5 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
8 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;7 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
...@@ -11,7 +10,6 @@ test "@fieldParentPtr non-first field" {...@@ -11,7 +10,6 @@ test "@fieldParentPtr non-first field" {
11}10}
1211
13test "@fieldParentPtr first field" {12test "@fieldParentPtr first field" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;13 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;14 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
test/behavior/for.zig-4
...@@ -5,7 +5,6 @@ const expectEqual = std.testing.expectEqual;...@@ -5,7 +5,6 @@ const expectEqual = std.testing.expectEqual;
5const mem = std.mem;5const mem = std.mem;
66
7test "continue in for loop" {7test "continue in for loop" {
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
109
11 const array = [_]i32{ 1, 2, 3, 4, 5 };10 const array = [_]i32{ 1, 2, 3, 4, 5 };
...@@ -130,7 +129,6 @@ test "for with null and T peer types and inferred result location type" {...@@ -130,7 +129,6 @@ test "for with null and T peer types and inferred result location type" {
130}129}
131130
132test "2 break statements and an else" {131test "2 break statements and an else" {
133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
134 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;132 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
135133
136 const S = struct {134 const S = struct {
...@@ -177,7 +175,6 @@ fn mangleString(s: []u8) void {...@@ -177,7 +175,6 @@ fn mangleString(s: []u8) void {
177}175}
178176
179test "for copies its payload" {177test "for copies its payload" {
180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
181 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
182179
183 const S = struct {180 const S = struct {
...@@ -213,7 +210,6 @@ test "for on slice with allowzero ptr" {...@@ -213,7 +210,6 @@ test "for on slice with allowzero ptr" {
213210
214test "else continue outer for" {211test "else continue outer for" {
215 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO212 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
216 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
217213
218 var i: usize = 6;214 var i: usize = 6;
219 var buf: [5]u8 = undefined;215 var buf: [5]u8 = undefined;
test/behavior/generics.zig-1
...@@ -91,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {...@@ -91,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {
9191
92test "type constructed by comptime function call" {92test "type constructed by comptime function call" {
93 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;93 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
94 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
95 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;94 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9695
97 var l: SimpleList(10) = undefined;96 var l: SimpleList(10) = undefined;
test/behavior/merge_error_sets.zig-1
...@@ -12,7 +12,6 @@ fn foo() C!void {...@@ -12,7 +12,6 @@ fn foo() C!void {
12}12}
1313
14test "merge error sets" {14test "merge error sets" {
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;15 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
17 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;16 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
18 if (foo()) {17 if (foo()) {
test/behavior/pointers.zig-5
...@@ -18,7 +18,6 @@ fn testDerefPtr() !void {...@@ -18,7 +18,6 @@ fn testDerefPtr() !void {
1818
19test "pointer arithmetic" {19test "pointer arithmetic" {
20 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;20 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
22 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;21 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2322
24 var ptr: [*]const u8 = "abcd";23 var ptr: [*]const u8 = "abcd";
...@@ -66,7 +65,6 @@ test "initialize const optional C pointer to null" {...@@ -66,7 +65,6 @@ test "initialize const optional C pointer to null" {
6665
67test "assigning integer to C pointer" {66test "assigning integer to C pointer" {
68 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;67 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
69 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
70 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;68 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7169
72 var x: i32 = 0;70 var x: i32 = 0;
...@@ -281,7 +279,6 @@ test "array initialization types" {...@@ -281,7 +279,6 @@ test "array initialization types" {
281279
282test "null terminated pointer" {280test "null terminated pointer" {
283 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;281 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
285 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO282 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
286283
287 const S = struct {284 const S = struct {
...@@ -299,7 +296,6 @@ test "null terminated pointer" {...@@ -299,7 +296,6 @@ test "null terminated pointer" {
299296
300test "allow any sentinel" {297test "allow any sentinel" {
301 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
302 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
303 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO299 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
304300
305 const S = struct {301 const S = struct {
...@@ -315,7 +311,6 @@ test "allow any sentinel" {...@@ -315,7 +311,6 @@ test "allow any sentinel" {
315311
316test "pointer sentinel with enums" {312test "pointer sentinel with enums" {
317 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;313 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
318 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
319 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO314 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320315
321 const S = struct {316 const S = struct {
test/behavior/ptrcast.zig-3
...@@ -4,7 +4,6 @@ const expect = std.testing.expect;...@@ -4,7 +4,6 @@ const expect = std.testing.expect;
4const native_endian = builtin.target.cpu.arch.endian();4const native_endian = builtin.target.cpu.arch.endian();
55
6test "reinterpret bytes as integer with nonzero offset" {6test "reinterpret bytes as integer with nonzero offset" {
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
8 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
98
10 try testReinterpretBytesAsInteger();9 try testReinterpretBytesAsInteger();
...@@ -39,7 +38,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {...@@ -39,7 +38,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
39}38}
4039
41test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {40test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
42 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
43 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO41 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4442
45 try testReinterpretStructWrappedBytesAsInteger();43 try testReinterpretStructWrappedBytesAsInteger();
...@@ -179,7 +177,6 @@ test "lower reinterpreted comptime field ptr" {...@@ -179,7 +177,6 @@ test "lower reinterpreted comptime field ptr" {
179}177}
180178
181test "reinterpret struct field at comptime" {179test "reinterpret struct field at comptime" {
182 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
183 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
184181
185 const numNative = comptime Bytes.init(0x12345678);182 const numNative = comptime Bytes.init(0x12345678);
test/behavior/sizeof_and_typeof.zig-2
...@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {...@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {
18}18}
1919
20test "@TypeOf() with multiple arguments" {20test "@TypeOf() with multiple arguments" {
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
22 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;21 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
23 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;22 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
24 {23 {
...@@ -77,7 +76,6 @@ const P = packed struct {...@@ -77,7 +76,6 @@ const P = packed struct {
77};76};
7877
79test "@offsetOf" {78test "@offsetOf" {
80 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
81 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;79 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8280
83 // Packed structs have fixed memory layout81 // Packed structs have fixed memory layout
test/behavior/slice.zig-3
...@@ -28,7 +28,6 @@ comptime {...@@ -28,7 +28,6 @@ comptime {
2828
29test "slicing" {29test "slicing" {
30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;30 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3231
33 var array: [20]i32 = undefined;32 var array: [20]i32 = undefined;
3433
...@@ -269,7 +268,6 @@ fn sliceSum(comptime q: []const u8) i32 {...@@ -269,7 +268,6 @@ fn sliceSum(comptime q: []const u8) i32 {
269268
270test "slice type with custom alignment" {269test "slice type with custom alignment" {
271 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
272 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
273271
274 const LazilyResolvedType = struct {272 const LazilyResolvedType = struct {
275 anything: i32,273 anything: i32,
...@@ -283,7 +281,6 @@ test "slice type with custom alignment" {...@@ -283,7 +281,6 @@ test "slice type with custom alignment" {
283281
284test "obtaining a null terminated slice" {282test "obtaining a null terminated slice" {
285 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;283 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
287284
288 // here we have a normal array285 // here we have a normal array
289 var buf: [50]u8 = undefined;286 var buf: [50]u8 = undefined;
test/behavior/struct.zig-8
...@@ -10,7 +10,6 @@ top_level_field: i32,...@@ -10,7 +10,6 @@ top_level_field: i32,
1010
11test "top level fields" {11test "top level fields" {
12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;12 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1413
15 var instance = @This(){14 var instance = @This(){
16 .top_level_field = 1234,15 .top_level_field = 1234,
...@@ -104,7 +103,6 @@ fn testMutation(foo: *StructFoo) void {...@@ -104,7 +103,6 @@ fn testMutation(foo: *StructFoo) void {
104103
105test "struct byval assign" {104test "struct byval assign" {
106 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;105 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
108106
109 var foo1: StructFoo = undefined;107 var foo1: StructFoo = undefined;
110 var foo2: StructFoo = undefined;108 var foo2: StructFoo = undefined;
...@@ -240,7 +238,6 @@ test "usingnamespace within struct scope" {...@@ -240,7 +238,6 @@ test "usingnamespace within struct scope" {
240238
241test "struct field init with catch" {239test "struct field init with catch" {
242 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
244241
245 const S = struct {242 const S = struct {
246 fn doTheTest() !void {243 fn doTheTest() !void {
...@@ -281,7 +278,6 @@ const Val = struct {...@@ -281,7 +278,6 @@ const Val = struct {
281test "struct point to self" {278test "struct point to self" {
282 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;279 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
283 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO280 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
285281
286 var root: Node = undefined;282 var root: Node = undefined;
287 root.val.x = 1;283 root.val.x = 1;
...@@ -297,7 +293,6 @@ test "struct point to self" {...@@ -297,7 +293,6 @@ test "struct point to self" {
297293
298test "void struct fields" {294test "void struct fields" {
299 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;295 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
300 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
301296
302 const foo = VoidStructFieldsFoo{297 const foo = VoidStructFieldsFoo{
303 .a = void{},298 .a = void{},
...@@ -761,7 +756,6 @@ test "packed struct with u0 field access" {...@@ -761,7 +756,6 @@ test "packed struct with u0 field access" {
761}756}
762757
763test "access to global struct fields" {758test "access to global struct fields" {
764 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
765 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO759 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
766 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO760 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
767761
...@@ -1260,7 +1254,6 @@ test "typed init through error unions and optionals" {...@@ -1260,7 +1254,6 @@ test "typed init through error unions and optionals" {
12601254
1261test "initialize struct with empty literal" {1255test "initialize struct with empty literal" {
1262 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1256 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1263 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12641257
1265 const S = struct { x: i32 = 1234 };1258 const S = struct { x: i32 = 1234 };
1266 var s: S = .{};1259 var s: S = .{};
...@@ -1362,7 +1355,6 @@ test "store to comptime field" {...@@ -1362,7 +1355,6 @@ test "store to comptime field" {
13621355
1363test "struct field init value is size of the struct" {1356test "struct field init value is size of the struct" {
1364 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO1357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1365 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13661358
1367 const namespace = struct {1359 const namespace = struct {
1368 const S = extern struct {1360 const S = extern struct {
test/behavior/switch.zig-2
...@@ -348,7 +348,6 @@ test "switch on const enum with var" {...@@ -348,7 +348,6 @@ test "switch on const enum with var" {
348}348}
349349
350test "anon enum literal used in switch on union enum" {350test "anon enum literal used in switch on union enum" {
351 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
352 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO351 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
353352
354 const Foo = union(enum) {353 const Foo = union(enum) {
...@@ -490,7 +489,6 @@ test "switch prongs with error set cases make a new error set type for capture v...@@ -490,7 +489,6 @@ test "switch prongs with error set cases make a new error set type for capture v
490}489}
491490
492test "return result loc and then switch with range implicit casted to error union" {491test "return result loc and then switch with range implicit casted to error union" {
493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
494 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO492 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
495493
496 const S = struct {494 const S = struct {
test/behavior/this.zig-1
...@@ -25,7 +25,6 @@ test "this refer to module call private fn" {...@@ -25,7 +25,6 @@ test "this refer to module call private fn" {
25}25}
2626
27test "this refer to container" {27test "this refer to container" {
28 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
29 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;28 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3029
31 var pt: Point(i32) = undefined;30 var pt: Point(i32) = undefined;
test/behavior/try.zig-1
...@@ -3,7 +3,6 @@ const builtin = @import("builtin");...@@ -3,7 +3,6 @@ const builtin = @import("builtin");
3const expect = std.testing.expect;3const expect = std.testing.expect;
44
5test "try on error union" {5test "try on error union" {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;6 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
87
9 try tryOnErrorUnionImpl();8 try tryOnErrorUnionImpl();
test/behavior/union.zig-1
...@@ -92,7 +92,6 @@ const FooExtern = extern union {...@@ -92,7 +92,6 @@ const FooExtern = extern union {
92};92};
9393
94test "basic extern unions" {94test "basic extern unions" {
95 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
96 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;95 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9796
98 var foo = FooExtern{ .int = 1 };97 var foo = FooExtern{ .int = 1 };
test/behavior/usingnamespace.zig-1
...@@ -58,7 +58,6 @@ test "two files usingnamespace import each other" {...@@ -58,7 +58,6 @@ test "two files usingnamespace import each other" {
58}58}
5959
60test {60test {
61 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
62 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO61 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
6362
64 const AA = struct {63 const AA = struct {
test/behavior/while.zig-1
...@@ -175,7 +175,6 @@ test "while with optional as condition with else" {...@@ -175,7 +175,6 @@ test "while with optional as condition with else" {
175175
176test "while with error union condition" {176test "while with error union condition" {
177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
180179
181 numbers_left = 10;180 numbers_left = 10;