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 {
438438 // mov fp, sp
439439 _ = try self.addInst(.{
440440 .tag = .mov,
441 .data = .{ .rr_op = .{
441 .data = .{ .r_op_mov = .{
442442 .rd = .fp,
443 .rn = .r0,
444443 .op = Instruction.Operand.reg(.sp, Instruction.Operand.Shift.none),
445444 } },
446445 });
......@@ -452,9 +451,7 @@ fn gen(self: *Self) !void {
452451 // The address of where to store the return value is in
453452 // r0. As this register might get overwritten along the
454453 // way, save the address to the stack.
455 const stack_offset = mem.alignForwardGeneric(u32, self.next_stack_offset, 4) + 4;
456 self.next_stack_offset = stack_offset;
457 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
454 const stack_offset = try self.allocMem(4, 4, null);
458455
459456 try self.genSetStack(Type.usize, stack_offset, MCValue{ .register = .r0 });
460457 self.ret_mcv = MCValue{ .stack_offset = stack_offset };
......@@ -491,14 +488,10 @@ fn gen(self: *Self) !void {
491488 const aligned_total_stack_end = mem.alignForwardGeneric(u32, total_stack_size, self.stack_align);
492489 const stack_size = aligned_total_stack_end - self.saved_regs_stack_space;
493490 self.max_end_stack = stack_size;
494 if (Instruction.Operand.fromU32(stack_size)) |op| {
495 self.mir_instructions.set(sub_reloc, .{
496 .tag = .sub,
497 .data = .{ .rr_op = .{ .rd = .sp, .rn = .sp, .op = op } },
498 });
499 } else {
500 return self.failSymbol("TODO ARM: allow larger stacks", .{});
501 }
491 self.mir_instructions.set(sub_reloc, .{
492 .tag = .sub_sp_scratch_r0,
493 .data = .{ .imm32 = stack_size },
494 });
502495
503496 _ = try self.addInst(.{
504497 .tag = .dbg_epilogue_begin,
......@@ -531,9 +524,8 @@ fn gen(self: *Self) !void {
531524 // mov sp, fp
532525 _ = try self.addInst(.{
533526 .tag = .mov,
534 .data = .{ .rr_op = .{
527 .data = .{ .r_op_mov = .{
535528 .rd = .sp,
536 .rn = .r0,
537529 .op = Instruction.Operand.reg(.fp, Instruction.Operand.Shift.none),
538530 } },
539531 });
......@@ -895,17 +887,30 @@ fn ensureProcessDeathCapacity(self: *Self, additional_count: usize) !void {
895887 try table.ensureUnusedCapacity(self.gpa, additional_count);
896888}
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
899899 if (abi_align > self.stack_align)
900900 self.stack_align = abi_align;
901
901902 // TODO find a free slot instead of always appending
902903 const offset = mem.alignForwardGeneric(u32, self.next_stack_offset, abi_align) + abi_size;
903904 self.next_stack_offset = offset;
904905 self.max_end_stack = @maximum(self.max_end_stack, self.next_stack_offset);
905 try self.stack.putNoClobber(self.gpa, offset, .{
906 .inst = inst,
907 .size = abi_size,
908 });
906
907 if (maybe_inst) |inst| {
908 try self.stack.putNoClobber(self.gpa, offset, .{
909 .inst = inst,
910 .size = abi_size,
911 });
912 }
913
909914 return offset;
910915}
911916
......@@ -927,35 +932,34 @@ fn allocMemPtr(self: *Self, inst: Air.Inst.Index) !u32 {
927932 };
928933 // TODO swap this for inst.ty.ptrAlign
929934 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);
931937}
932938
933fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
934 const elem_ty = self.air.typeOfIndex(inst);
939fn allocRegOrMem(self: *Self, elem_ty: Type, reg_ok: bool, maybe_inst: ?Air.Inst.Index) !MCValue {
935940 const abi_size = math.cast(u32, elem_ty.abiSize(self.target.*)) orelse {
936941 const mod = self.bin_file.options.module.?;
937942 return self.fail("type '{}' too big to fit into stack frame", .{elem_ty.fmt(mod)});
938943 };
939944 const abi_align = elem_ty.abiAlignment(self.target.*);
940 if (abi_align > self.stack_align)
941 self.stack_align = abi_align;
942945
943946 if (reg_ok) {
944947 // Make sure the type can fit in a register before we try to allocate one.
945948 const ptr_bits = self.target.cpu.arch.ptrBitWidth();
946949 const ptr_bytes: u64 = @divExact(ptr_bits, 8);
947950 if (abi_size <= ptr_bytes) {
948 if (self.register_manager.tryAllocReg(inst, gp)) |reg| {
951 if (self.register_manager.tryAllocReg(maybe_inst, gp)) |reg| {
949952 return MCValue{ .register = reg };
950953 }
951954 }
952955 }
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);
954958 return MCValue{ .stack_offset = stack_offset };
955959}
956960
957961pub 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);
959963 log.debug("spilling {} (%{d}) to stack mcv {any}", .{ reg, inst, stack_mcv });
960964
961965 const reg_mcv = self.getResolvedInstValue(inst);
......@@ -976,12 +980,13 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void
976980/// occupied
977981fn spillCompareFlagsIfOccupied(self: *Self) !void {
978982 if (self.cpsr_flags_inst) |inst_to_save| {
983 const ty = self.air.typeOfIndex(inst_to_save);
979984 const mcv = self.getResolvedInstValue(inst_to_save);
980985 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),
982987 .register_c_flag,
983988 .register_v_flag,
984 => try self.allocRegOrMem(inst_to_save, false),
989 => try self.allocRegOrMem(ty, false, inst_to_save),
985990 else => unreachable, // mcv doesn't occupy the compare flags
986991 };
987992
......@@ -1112,10 +1117,11 @@ fn truncRegister(
11121117 });
11131118}
11141119
1120/// Asserts that both operand_ty and dest_ty are integer types
11151121fn trunc(
11161122 self: *Self,
11171123 maybe_inst: ?Air.Inst.Index,
1118 operand: MCValue,
1124 operand_bind: ReadArg.Bind,
11191125 operand_ty: Type,
11201126 dest_ty: Type,
11211127) !MCValue {
......@@ -1123,39 +1129,38 @@ fn trunc(
11231129 const info_b = dest_ty.intInfo(self.target.*);
11241130
11251131 if (info_b.bits <= 32) {
1126 const operand_reg = switch (operand) {
1127 .register => |r| r,
1128 else => operand_reg: {
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);
1132 if (info_a.bits > 32) {
1133 return self.fail("TODO load least significant word into register", .{});
1134 }
11381135
1139 const dest_reg = if (maybe_inst) |inst| blk: {
1140 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
1136 var operand_reg: Register = undefined;
1137 var dest_reg: Register = undefined;
11411138
1142 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1143 break :blk operand_reg;
1144 } else {
1145 break :blk try self.register_manager.allocReg(inst, gp);
1146 }
1147 } else try self.register_manager.allocReg(null, gp);
1139 const read_args = [_]ReadArg{
1140 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &operand_reg },
1141 };
1142 const write_args = [_]WriteArg{
1143 .{ .ty = dest_ty, .bind = .none, .class = gp, .reg = &dest_reg },
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
11491154 switch (info_b.bits) {
11501155 32 => {
11511156 try self.genSetReg(operand_ty, dest_reg, .{ .register = operand_reg });
1152 return MCValue{ .register = dest_reg };
11531157 },
11541158 else => {
11551159 try self.truncRegister(operand_reg, dest_reg, info_b.signedness, info_b.bits);
1156 return MCValue{ .register = dest_reg };
11571160 },
11581161 }
1162
1163 return MCValue{ .register = dest_reg };
11591164 } else {
11601165 return self.fail("TODO: truncate to ints > 32 bits", .{});
11611166 }
......@@ -1163,12 +1168,12 @@ fn trunc(
11631168
11641169fn airTrunc(self: *Self, inst: Air.Inst.Index) !void {
11651170 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 };
11671172 const operand_ty = self.air.typeOf(ty_op.operand);
11681173 const dest_ty = self.air.typeOfIndex(inst);
11691174
11701175 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);
11721177 };
11731178
11741179 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -1184,29 +1189,32 @@ fn airBoolToInt(self: *Self, inst: Air.Inst.Index) !void {
11841189fn airNot(self: *Self, inst: Air.Inst.Index) !void {
11851190 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
11861191 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 };
11881193 const operand_ty = self.air.typeOf(ty_op.operand);
1189 switch (operand) {
1194 switch (try operand_bind.resolveToMcv(self)) {
11901195 .dead => unreachable,
11911196 .unreach => unreachable,
11921197 .cpsr_flags => |cond| break :result MCValue{ .cpsr_flags = cond.negate() },
11931198 else => {
11941199 switch (operand_ty.zigTypeTag()) {
11951200 .Bool => {
1196 const op_reg = switch (operand) {
1197 .register => |r| r,
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);
1201 var op_reg: Register = undefined;
1202 var dest_reg: Register = undefined;
12021203
1203 const dest_reg = blk: {
1204 if (operand == .register and self.reuseOperand(inst, ty_op.operand, 0, operand)) {
1205 break :blk op_reg;
1206 }
1207
1208 break :blk try self.register_manager.allocReg(null, gp);
1204 const read_args = [_]ReadArg{
1205 .{ .ty = operand_ty, .bind = operand_bind, .class = gp, .reg = &op_reg },
12091206 };
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
12111219 _ = try self.addInst(.{
12121220 .tag = .eor,
......@@ -1223,26 +1231,28 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
12231231 .Int => {
12241232 const int_info = operand_ty.intInfo(self.target.*);
12251233 if (int_info.bits <= 32) {
1226 const op_reg = switch (operand) {
1227 .register => |r| r,
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 }
1234 var op_reg: Register = undefined;
1235 var dest_reg: Register = undefined;
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 },
12391239 };
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
12411252 _ = try self.addInst(.{
12421253 .tag = .mvn,
1243 .data = .{ .rr_op = .{
1254 .data = .{ .r_op_mov = .{
12441255 .rd = dest_reg,
1245 .rn = undefined,
12461256 .op = Instruction.Operand.reg(op_reg, Instruction.Operand.Shift.none),
12471257 } },
12481258 });
......@@ -1267,11 +1277,11 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void {
12671277fn minMax(
12681278 self: *Self,
12691279 tag: Air.Inst.Tag,
1270 maybe_inst: ?Air.Inst.Index,
1271 lhs: MCValue,
1272 rhs: MCValue,
1280 lhs_bind: ReadArg.Bind,
1281 rhs_bind: ReadArg.Bind,
12731282 lhs_ty: Type,
12741283 rhs_ty: Type,
1284 maybe_inst: ?Air.Inst.Index,
12751285) !MCValue {
12761286 switch (lhs_ty.zigTypeTag()) {
12771287 .Float => return self.fail("TODO ARM min/max on floats", .{}),
......@@ -1281,34 +1291,25 @@ fn minMax(
12811291 assert(lhs_ty.eql(rhs_ty, mod));
12821292 const int_info = lhs_ty.intInfo(self.target.*);
12831293 if (int_info.bits <= 32) {
1284 const lhs_is_register = lhs == .register;
1285 const rhs_is_register = rhs == .register;
1294 var lhs_reg: Register = undefined;
1295 var rhs_reg: Register = undefined;
1296 var dest_reg: Register = undefined;
12861297
1287 const lhs_reg = switch (lhs) {
1288 .register => |r| r,
1289 else => try self.copyToTmpRegister(lhs_ty, lhs),
1298 const read_args = [_]ReadArg{
1299 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
1300 .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg },
12901301 };
1291 const lhs_reg_lock = self.register_manager.lockReg(lhs_reg);
1292 defer if (lhs_reg_lock) |reg| self.register_manager.unlockReg(reg);
1293
1294 const rhs_reg = switch (rhs) {
1295 .register => |r| r,
1296 else => try self.copyToTmpRegister(rhs_ty, rhs),
1302 const write_args = [_]WriteArg{
1303 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
12971304 };
1298 const rhs_reg_lock = self.register_manager.lockReg(rhs_reg);
1299 defer if (rhs_reg_lock) |reg| self.register_manager.unlockReg(reg);
1300
1301 const dest_reg = if (maybe_inst) |inst| blk: {
1302 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
1303
1304 if (lhs_is_register and self.reuseOperand(inst, bin_op.lhs, 0, lhs)) {
1305 break :blk lhs_reg;
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);
1305 try self.allocRegs(
1306 &read_args,
1307 &write_args,
1308 if (maybe_inst) |inst| .{
1309 .corresponding_inst = inst,
1310 .operand_mapping = &.{ 0, 1 },
1311 } else null,
1312 );
13121313
13131314 // lhs == reg should have been checked by airMinMax
13141315 //
......@@ -1318,7 +1319,13 @@ fn minMax(
13181319 // register.
13191320 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
13231330 const cond_choose_lhs: Condition = switch (tag) {
13241331 .max => switch (int_info.signedness) {
......@@ -1337,9 +1344,8 @@ fn minMax(
13371344 _ = try self.addInst(.{
13381345 .tag = .mov,
13391346 .cond = cond_choose_lhs,
1340 .data = .{ .rr_op = .{
1347 .data = .{ .r_op_mov = .{
13411348 .rd = dest_reg,
1342 .rn = .r0,
13431349 .op = Instruction.Operand.reg(lhs_reg, Instruction.Operand.Shift.none),
13441350 } },
13451351 });
......@@ -1348,9 +1354,8 @@ fn minMax(
13481354 _ = try self.addInst(.{
13491355 .tag = .mov,
13501356 .cond = cond_choose_rhs,
1351 .data = .{ .rr_op = .{
1357 .data = .{ .r_op_mov = .{
13521358 .rd = dest_reg,
1353 .rn = .r0,
13541359 .op = Instruction.Operand.reg(rhs_reg, Instruction.Operand.Shift.none),
13551360 } },
13561361 });
......@@ -1368,15 +1373,17 @@ fn minMax(
13681373fn airMinMax(self: *Self, inst: Air.Inst.Index) !void {
13691374 const tag = self.air.instructions.items(.tag)[inst];
13701375 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);
13731376 const lhs_ty = self.air.typeOf(bin_op.lhs);
13741377 const rhs_ty = self.air.typeOf(bin_op.rhs);
13751378
13761379 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);
13771384 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);
13801387 };
13811388 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
13821389}
......@@ -1390,7 +1397,7 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
13901397 const len = try self.resolveInst(bin_op.rhs);
13911398 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);
13941401 try self.genSetStack(ptr_ty, stack_offset, ptr);
13951402 try self.genSetStack(len_ty, stack_offset - 4, len);
13961403 break :result MCValue{ .stack_offset = stack_offset };
......@@ -1400,38 +1407,65 @@ fn airSlice(self: *Self, inst: Air.Inst.Index) !void {
14001407
14011408fn airBinOp(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
14021409 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);
14051410 const lhs_ty = self.air.typeOf(bin_op.lhs);
14061411 const rhs_ty = self.air.typeOf(bin_op.rhs);
14071412
1408 const result: MCValue = if (self.liveness.isUnused(inst))
1409 .dead
1410 else
1411 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1412 .lhs = bin_op.lhs,
1413 .rhs = bin_op.rhs,
1414 .inst = inst,
1415 });
1413 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1414 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1415 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1416
1417 break :result switch (tag) {
1418 .add => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
1419 .sub => try self.addSub(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst),
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 };
14161454 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14171455}
14181456
14191457fn airPtrArithmetic(self: *Self, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void {
14201458 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
14211459 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);
14241460 const lhs_ty = self.air.typeOf(bin_op.lhs);
14251461 const rhs_ty = self.air.typeOf(bin_op.rhs);
14261462
1427 const result: MCValue = if (self.liveness.isUnused(inst))
1428 .dead
1429 else
1430 try self.binOp(tag, lhs, rhs, lhs_ty, rhs_ty, BinOpMetadata{
1431 .lhs = bin_op.lhs,
1432 .rhs = bin_op.rhs,
1433 .inst = inst,
1434 });
1463 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1464 const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs };
1465 const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
1466
1467 break :result try self.ptrArithmetic(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst);
1468 };
14351469 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
14361470}
14371471
......@@ -1458,8 +1492,8 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14581492 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
14591493 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
14601494 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
1461 const lhs = try self.resolveInst(extra.lhs);
1462 const rhs = try self.resolveInst(extra.rhs);
1495 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1496 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
14631497 const lhs_ty = self.air.typeOf(extra.lhs);
14641498 const rhs_ty = self.air.typeOf(extra.rhs);
14651499
......@@ -1475,17 +1509,16 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14751509 assert(lhs_ty.eql(rhs_ty, mod));
14761510 const int_info = lhs_ty.intInfo(self.target.*);
14771511 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
14801514 try self.spillCompareFlagsIfOccupied();
1481 self.cpsr_flags_inst = null;
14821515
14831516 const base_tag: Air.Inst.Tag = switch (tag) {
14841517 .add_with_overflow => .add,
14851518 .sub_with_overflow => .sub,
14861519 else => unreachable,
14871520 };
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);
14891522 const dest_reg = dest.register;
14901523 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
14911524 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -1498,25 +1531,34 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
14981531 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
14991532
15001533 // 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
15031542 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
15041543 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
15051544
15061545 break :result MCValue{ .stack_offset = stack_offset };
15071546 } 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
15081550 // Only say yes if the operation is
15091551 // commutative, i.e. we can swap both of the
15101552 // operands
15111553 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,
15131555 .sub_with_overflow => false,
15141556 else => unreachable,
15151557 };
15161558 const rhs_immediate_ok = switch (tag) {
15171559 .add_with_overflow,
15181560 .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,
15201562 else => unreachable,
15211563 };
15221564
......@@ -1531,12 +1573,12 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void {
15311573
15321574 const dest = blk: {
15331575 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);
15351577 } else if (lhs_immediate_ok) {
15361578 // 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);
15381580 } 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);
15401582 }
15411583 };
15421584
......@@ -1563,8 +1605,8 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15631605 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
15641606 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
15651607 const result: MCValue = result: {
1566 const lhs = try self.resolveInst(extra.lhs);
1567 const rhs = try self.resolveInst(extra.rhs);
1608 const lhs_bind: ReadArg.Bind = .{ .inst = extra.lhs };
1609 const rhs_bind: ReadArg.Bind = .{ .inst = extra.rhs };
15681610 const lhs_ty = self.air.typeOf(extra.lhs);
15691611 const rhs_ty = self.air.typeOf(extra.rhs);
15701612
......@@ -1580,17 +1622,16 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
15801622 assert(lhs_ty.eql(rhs_ty, mod));
15811623 const int_info = lhs_ty.intInfo(self.target.*);
15821624 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
15851627 try self.spillCompareFlagsIfOccupied();
1586 self.cpsr_flags_inst = null;
15871628
15881629 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
15891630 .signed => .smulbb,
15901631 .unsigned => .mul,
15911632 };
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);
15941635 const dest_reg = dest.register;
15951636 const dest_reg_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
15961637 defer self.register_manager.unlockReg(dest_reg_lock);
......@@ -1603,62 +1644,48 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16031644 try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits);
16041645
16051646 // 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
16081655 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
16091656 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
16101657
16111658 break :result MCValue{ .stack_offset = stack_offset };
16121659 } 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
16151662 try self.spillCompareFlagsIfOccupied();
1616 self.cpsr_flags_inst = null;
16171663
16181664 const base_tag: Mir.Inst.Tag = switch (int_info.signedness) {
16191665 .signed => .smull,
16201666 .unsigned => .umull,
16211667 };
16221668
1623 // TODO extract umull etc. to binOpTwoRegister
1624 // once MCValue.rr is implemented
1625 const lhs_is_register = lhs == .register;
1626 const rhs_is_register = rhs == .register;
1627
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);
1669 var lhs_reg: Register = undefined;
1670 var rhs_reg: Register = undefined;
1671 var rdhi: Register = undefined;
1672 var rdlo: Register = undefined;
1673 var truncated_reg: Register = undefined;
16581674
1659 const truncated_reg = try self.register_manager.allocReg(null, gp);
1660 const truncated_reg_lock = self.register_manager.lockRegAssumeUnused(truncated_reg);
1661 defer self.register_manager.unlockReg(truncated_reg_lock);
1675 const read_args = [_]ReadArg{
1676 .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg },
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
16631690 _ = try self.addInst(.{
16641691 .tag = base_tag,
......@@ -1677,14 +1704,19 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16771704 try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg });
16781705
16791706 // 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
16821715 // mov rdlo, #0
16831716 _ = try self.addInst(.{
16841717 .tag = .mov,
1685 .data = .{ .rr_op = .{
1718 .data = .{ .r_op_mov = .{
16861719 .rd = rdlo,
1687 .rn = .r0,
16881720 .op = Instruction.Operand.fromU32(0).?,
16891721 } },
16901722 });
......@@ -1693,23 +1725,27 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
16931725 _ = try self.addInst(.{
16941726 .tag = .mov,
16951727 .cond = .ne,
1696 .data = .{ .rr_op = .{
1728 .data = .{ .r_op_mov = .{
16971729 .rd = rdlo,
1698 .rn = .r0,
16991730 .op = Instruction.Operand.fromU32(1).?,
17001731 } },
17011732 });
17021733
17031734 // 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
17061743 // movne rdlo, #1
17071744 _ = try self.addInst(.{
17081745 .tag = .mov,
17091746 .cond = .ne,
1710 .data = .{ .rr_op = .{
1747 .data = .{ .r_op_mov = .{
17111748 .rd = rdlo,
1712 .rn = .r0,
17131749 .op = Instruction.Operand.fromU32(1).?,
17141750 } },
17151751 });
......@@ -1733,8 +1769,6 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
17331769 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
17341770 if (self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none });
17351771 const result: MCValue = result: {
1736 const lhs = try self.resolveInst(extra.lhs);
1737 const rhs = try self.resolveInst(extra.rhs);
17381772 const lhs_ty = self.air.typeOf(extra.lhs);
17391773 const rhs_ty = self.air.typeOf(extra.rhs);
17401774
......@@ -1748,30 +1782,109 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void {
17481782 .Int => {
17491783 const int_info = lhs_ty.intInfo(self.target.*);
17501784 if (int_info.bits <= 32) {
1751 const stack_offset = try self.allocMem(inst, tuple_size, tuple_align);
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);
1785 const stack_offset = try self.allocMem(tuple_size, tuple_align, inst);
17581786
17591787 try self.spillCompareFlagsIfOccupied();
1760 self.cpsr_flags_inst = null;
17611788
1762 // lsl dest, lhs, rhs
1763 const dest = try self.binOp(.shl, lhs, rhs, lhs_ty, rhs_ty, null);
1764 const dest_reg = dest.register;
1765 const dest_lock = self.register_manager.lockRegAssumeUnused(dest_reg);
1766 defer self.register_manager.unlockReg(dest_lock);
1789 const shr_mir_tag: Mir.Inst.Tag = switch (int_info.signedness) {
1790 .signed => Mir.Inst.Tag.asr,
1791 .unsigned => Mir.Inst.Tag.lsr,
1792 };
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, rhs
1769 const reconstructed = try self.binOp(.shr, dest, rhs, lhs_ty, rhs_ty, null);
1867 // asr/lsr reconstructed, dest, rhs
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
17711878 // 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 });
17751888 try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .cpsr_flags = .ne });
17761889
17771890 break :result MCValue{ .stack_offset = stack_offset };
......@@ -1826,19 +1939,57 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void {
18261939}
18271940
18281941/// 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 {
18301948 const err_ty = error_union_ty.errorUnionSet();
18311949 const payload_ty = error_union_ty.errorUnionPayload();
18321950 if (err_ty.errorSetIsEmpty()) {
18331951 return MCValue{ .immediate = 0 };
18341952 }
18351953 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
1836 return error_union_mcv;
1954 return try error_union_bind.resolveToMcv(self);
18371955 }
18381956
18391957 const err_offset = @intCast(u32, errUnionErrorOffset(payload_ty, self.target.*));
1840 switch (error_union_mcv) {
1841 .register => return self.fail("TODO errUnionErr for registers", .{}),
1958 switch (try error_union_bind.resolveToMcv(self)) {
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 },
18421993 .stack_argument_offset => |off| {
18431994 return MCValue{ .stack_argument_offset = off + err_offset };
18441995 },
......@@ -1855,27 +2006,66 @@ fn errUnionErr(self: *Self, error_union_mcv: MCValue, error_union_ty: Type) !MCV
18552006fn airUnwrapErrErr(self: *Self, inst: Air.Inst.Index) !void {
18562007 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
18572008 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2009 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
18582010 const error_union_ty = self.air.typeOf(ty_op.operand);
1859 const mcv = try self.resolveInst(ty_op.operand);
1860 break :result try self.errUnionErr(mcv, error_union_ty);
2011
2012 break :result try self.errUnionErr(error_union_bind, error_union_ty, inst);
18612013 };
18622014 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
18632015}
18642016
18652017/// 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 {
18672024 const err_ty = error_union_ty.errorUnionSet();
18682025 const payload_ty = error_union_ty.errorUnionPayload();
18692026 if (err_ty.errorSetIsEmpty()) {
1870 return error_union_mcv;
2027 return try error_union_bind.resolveToMcv(self);
18712028 }
18722029 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) {
18732030 return MCValue.none;
18742031 }
18752032
18762033 const payload_offset = @intCast(u32, errUnionPayloadOffset(payload_ty, self.target.*));
1877 switch (error_union_mcv) {
1878 .register => return self.fail("TODO errUnionPayload for registers", .{}),
2034 switch (try error_union_bind.resolveToMcv(self)) {
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 },
18792069 .stack_argument_offset => |off| {
18802070 return MCValue{ .stack_argument_offset = off + payload_offset };
18812071 },
......@@ -1892,9 +2082,10 @@ fn errUnionPayload(self: *Self, error_union_mcv: MCValue, error_union_ty: Type)
18922082fn airUnwrapErrPayload(self: *Self, inst: Air.Inst.Index) !void {
18932083 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
18942084 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2085 const error_union_bind: ReadArg.Bind = .{ .inst = ty_op.operand };
18952086 const error_union_ty = self.air.typeOf(ty_op.operand);
1896 const error_union = try self.resolveInst(ty_op.operand);
1897 break :result try self.errUnionPayload(error_union, error_union_ty);
2087
2088 break :result try self.errUnionPayload(error_union_bind, error_union_ty, inst);
18982089 };
18992090 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
19002091}
......@@ -1938,17 +2129,18 @@ fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void {
19382129 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
19392130 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
19402131 const error_union_ty = self.air.getRefType(ty_op.ty);
2132 const error_ty = error_union_ty.errorUnionSet();
19412133 const payload_ty = error_union_ty.errorUnionPayload();
19422134 const operand = try self.resolveInst(ty_op.operand);
19432135 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;
19442136
19452137 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
19462138 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));
19482140 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
19492141 const err_off = errUnionErrorOffset(payload_ty, self.target.*);
19502142 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
19532145 break :result MCValue{ .stack_offset = stack_offset };
19542146 };
......@@ -1960,16 +2152,17 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void {
19602152 const ty_op = self.air.instructions.items(.data)[inst].ty_op;
19612153 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
19622154 const error_union_ty = self.air.getRefType(ty_op.ty);
2155 const error_ty = error_union_ty.errorUnionSet();
19632156 const payload_ty = error_union_ty.errorUnionPayload();
19642157 const operand = try self.resolveInst(ty_op.operand);
19652158 if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand;
19662159
19672160 const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
19682161 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));
19702163 const payload_off = errUnionPayloadOffset(payload_ty, self.target.*);
19712164 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);
19732166 try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef);
19742167
19752168 break :result MCValue{ .stack_offset = stack_offset };
......@@ -2008,7 +2201,6 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
20082201 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
20092202 const mcv = try self.resolveInst(ty_op.operand);
20102203 switch (mcv) {
2011 .dead, .unreach => unreachable,
20122204 .register => unreachable, // a slice doesn't fit in one register
20132205 .stack_argument_offset => |off| {
20142206 break :result MCValue{ .stack_argument_offset = off + 4 };
......@@ -2019,7 +2211,7 @@ fn airSliceLen(self: *Self, inst: Air.Inst.Index) !void {
20192211 .memory => |addr| {
20202212 break :result MCValue{ .memory = addr + 4 };
20212213 },
2022 else => return self.fail("TODO implement slice_len for {}", .{mcv}),
2214 else => unreachable, // invalid MCValue for a slice
20232215 }
20242216 };
20252217 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -2034,7 +2226,12 @@ fn airPtrSliceLenPtr(self: *Self, inst: Air.Inst.Index) !void {
20342226 .ptr_stack_offset => |off| {
20352227 break :result MCValue{ .ptr_stack_offset = off - 4 };
20362228 },
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 },
20382235 }
20392236 };
20402237 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
......@@ -2049,91 +2246,96 @@ fn airPtrSlicePtrPtr(self: *Self, inst: Air.Inst.Index) !void {
20492246 .ptr_stack_offset => |off| {
20502247 break :result MCValue{ .ptr_stack_offset = off };
20512248 },
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 },
20532256 }
20542257 };
20552258 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
20562259}
20572260
2058fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2059 const is_volatile = false; // TODO
2060 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2061
2062 if (!is_volatile and self.liveness.isUnused(inst)) return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none });
2063 const result: MCValue = result: {
2064 const slice_mcv = try self.resolveInst(bin_op.lhs);
2065
2066 // TODO optimize for the case where the index is a constant,
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);
2261fn ptrElemVal(
2262 self: *Self,
2263 ptr_bind: ReadArg.Bind,
2264 index_bind: ReadArg.Bind,
2265 ptr_ty: Type,
2266 maybe_inst: ?Air.Inst.Index,
2267) !MCValue {
2268 const elem_ty = ptr_ty.childType();
2269 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
20772270
2078 const index_lock: ?RegisterLock = if (index_is_register)
2079 self.register_manager.lockRegAssumeUnused(index_mcv.register)
2080 else
2081 null;
2082 defer if (index_lock) |reg| self.register_manager.unlockReg(reg);
2271 switch (elem_size) {
2272 1, 4 => {
2273 var base_reg: Register = undefined;
2274 var index_reg: Register = undefined;
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) {
2087 1, 4 => {
2088 const base_reg = switch (base_mcv) {
2089 .register => |r| r,
2090 else => try self.copyToTmpRegister(slice_ptr_field_type, base_mcv),
2091 };
2092 const base_reg_lock = self.register_manager.lockRegAssumeUnused(base_reg);
2093 defer self.register_manager.unlockReg(base_reg_lock);
2304 _ = try self.addInst(.{
2305 .tag = tag,
2306 .data = .{ .rr_offset = .{
2307 .rt = dest_reg,
2308 .rn = base_reg,
2309 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
2310 } },
2311 });
20942312
2095 const dst_reg = try self.register_manager.allocReg(inst, gp);
2096 const dst_mcv = MCValue{ .register = dst_reg };
2097 const dst_reg_lock = self.register_manager.lockRegAssumeUnused(dst_reg);
2098 defer self.register_manager.unlockReg(dst_reg_lock);
2313 return MCValue{ .register = dest_reg };
2314 },
2315 else => {
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) {
2101 .register => |reg| reg,
2102 else => try self.copyToTmpRegister(Type.usize, index_mcv),
2103 };
2104 const index_reg_lock = self.register_manager.lockReg(index_reg);
2105 defer if (index_reg_lock) |lock| self.register_manager.unlockReg(lock);
2318 const dest = try self.allocRegOrMem(elem_ty, true, maybe_inst);
2319 try self.load(dest, addr, ptr_ty);
2320 return dest;
2321 },
2322 }
2323}
21062324
2107 const tag: Mir.Inst.Tag = switch (elem_size) {
2108 1 => .ldrb,
2109 4 => .ldr,
2110 else => unreachable,
2111 };
2112 const shift: u5 = switch (elem_size) {
2113 1 => 0,
2114 4 => 2,
2115 else => unreachable,
2116 };
2325fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void {
2326 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2327 const slice_ty = self.air.typeOf(bin_op.lhs);
2328 const result: MCValue = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: {
2329 var buf: Type.SlicePtrFieldTypeBuffer = undefined;
2330 const ptr_ty = slice_ty.slicePtrFieldType(&buf);
21172331
2118 _ = try self.addInst(.{
2119 .tag = tag,
2120 .data = .{ .rr_offset = .{
2121 .rt = dst_reg,
2122 .rn = base_reg,
2123 .offset = .{ .offset = Instruction.Offset.reg(index_reg, .{ .lsl = shift }) },
2124 } },
2125 });
2332 const slice_mcv = try self.resolveInst(bin_op.lhs);
2333 const base_mcv = slicePtr(slice_mcv);
21262334
2127 break :result dst_mcv;
2128 },
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);
2335 const base_bind: ReadArg.Bind = .{ .mcv = base_mcv };
2336 const index_bind: ReadArg.Bind = .{ .inst = bin_op.rhs };
21332337
2134 break :result dest;
2135 },
2136 }
2338 break :result try self.ptrElemVal(base_bind, index_bind, ptr_ty, inst);
21372339 };
21382340 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
21392341}
......@@ -2143,40 +2345,108 @@ fn airSliceElemPtr(self: *Self, inst: Air.Inst.Index) !void {
21432345 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
21442346 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
21452347 const slice_mcv = try self.resolveInst(extra.lhs);
2146 const index_mcv = try self.resolveInst(extra.rhs);
21472348 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
21492353 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);
21522357 break :result addr;
21532358 };
21542359 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
21552360}
21562361
2157fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void {
2158 const bin_op = self.air.instructions.items(.data)[inst].bin_op;
2159 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement array_elem_val for {}", .{self.target.cpu.arch});
2160 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2161}
2162
2163fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void {
2164 const is_volatile = false; // TODO
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}
2362fn arrayElemVal(
2363 self: *Self,
2364 array_bind: ReadArg.Bind,
2365 index_bind: ReadArg.Bind,
2366 array_ty: Type,
2367 maybe_inst: ?Air.Inst.Index,
2368) InnerError!MCValue {
2369 const elem_ty = array_ty.childType();
21692370
2170fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void {
2171 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2172 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data;
2173 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
2174 const ptr_mcv = try self.resolveInst(extra.lhs);
2175 const index_mcv = try self.resolveInst(extra.rhs);
2371 const mcv = try array_bind.resolveToMcv(self);
2372 switch (mcv) {
2373 .stack_offset,
2374 .memory,
2375 .stack_argument_offset,
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);
21802450 break :result addr;
21812451 };
21822452 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none });
......@@ -2240,7 +2510,13 @@ fn airUnaryMath(self: *Self, inst: Air.Inst.Index) !void {
22402510 return self.finishAir(inst, result, .{ un_op, .none, .none });
22412511}
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 {
22442520 if (!self.liveness.operandDies(inst, op_index))
22452521 return false;
22462522
......@@ -2362,16 +2638,18 @@ fn airLoad(self: *Self, inst: Air.Inst.Index) !void {
23622638 if (self.liveness.isUnused(inst) and !is_volatile)
23632639 break :result MCValue.dead;
23642640
2365 const dst_mcv: MCValue = blk: {
2366 if (self.reuseOperand(inst, ty_op.operand, 0, ptr)) {
2641 const dest_mcv: MCValue = blk: {
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)) {
23672644 // The MCValue that holds the pointer can be re-used as the value.
23682645 break :blk ptr;
23692646 } else {
2370 break :blk try self.allocRegOrMem(inst, true);
2647 break :blk try self.allocRegOrMem(elem_ty, true, inst);
23712648 }
23722649 };
2373 try self.load(dst_mcv, ptr, self.air.typeOf(ty_op.operand));
2374 break :result dst_mcv;
2650 try self.load(dest_mcv, ptr, self.air.typeOf(ty_op.operand));
2651
2652 break :result dest_mcv;
23752653 };
23762654 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none });
23772655}
......@@ -2498,26 +2776,10 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde
24982776 break :result MCValue{ .ptr_stack_offset = off - struct_field_offset };
24992777 },
25002778 else => {
2501 const offset_reg = try self.copyToTmpRegister(ptr_ty, .{
2502 .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 );
2779 const lhs_bind: ReadArg.Bind = .{ .mcv = mcv };
2780 const rhs_bind: ReadArg.Bind = .{ .mcv = .{ .immediate = struct_field_offset } };
25192781
2520 break :result dest;
2782 break :result try self.addSub(.add, lhs_bind, rhs_bind, Type.usize, Type.usize, null);
25212783 },
25222784 }
25232785 };
......@@ -2532,6 +2794,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
25322794 const mcv = try self.resolveInst(operand);
25332795 const struct_ty = self.air.typeOf(operand);
25342796 const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*));
2797 const struct_field_ty = struct_ty.structFieldType(index);
25352798
25362799 switch (mcv) {
25372800 .dead, .unreach => unreachable,
......@@ -2569,11 +2832,45 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
25692832 } else {
25702833 // Copy to new register
25712834 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
25742837 break :result MCValue{ .register = dest_reg };
25752838 }
25762839 },
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 },
25772874 else => return self.fail("TODO implement codegen struct_field_val for {}", .{mcv}),
25782875 }
25792876 };
......@@ -2583,114 +2880,285 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void {
25832880
25842881fn airFieldParentPtr(self: *Self, inst: Air.Inst.Index) !void {
25852882 const ty_pl = self.air.instructions.items(.data)[inst].ty_pl;
2586 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data;
2587 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement airFieldParentPtr", .{});
2588 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
2883 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data;
2884 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
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 });
25892901}
25902902
2591/// Allocates a new register. If Inst in non-null, additionally tracks
2592/// this register and the corresponding int and removes all previous
2593/// tracking. Does not do the actual moving (that is handled by
2594/// genSetReg).
2595fn prepareNewRegForMoving(
2596 self: *Self,
2597 track_inst: ?Air.Inst.Index,
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);
2903/// An argument to a Mir instruction which is read (and possibly also
2904/// written to) by the respective instruction
2905const ReadArg = struct {
2906 ty: Type,
2907 bind: Bind,
2908 class: RegisterManager.RegisterBitSet,
2909 reg: *Register,
26032910
2604 if (track_inst) |inst| {
2605 // Overwrite the MCValue associated with this inst
2606 branch.inst_table.putAssumeCapacity(inst, .{ .register = reg });
2911 const Bind = union(enum) {
2912 inst: Air.Inst.Ref,
2913 mcv: MCValue,
26072914
2608 // If the previous MCValue occupied some space we track, we
2609 // need to make sure it is marked as free now.
2610 switch (mcv) {
2611 .cpsr_flags => {
2612 assert(self.cpsr_flags_inst.? == inst);
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 => {},
2915 fn resolveToMcv(bind: Bind, function: *Self) InnerError!MCValue {
2916 return switch (bind) {
2917 .inst => |inst| try function.resolveInst(inst),
2918 .mcv => |mcv| mcv,
2919 };
26202920 }
2621 }
26222921
2623 return reg;
2624}
2922 fn resolveToImmediate(bind: Bind, function: *Self) InnerError!?u32 {
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
26272976///
2628/// Calling this function signals an intention to generate a Mir
2629/// instruction of the form
2977/// If the Mir instruction these registers are allocated for
2978/// 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).
26302982///
2631/// op dest, lhs, rhs
2983/// 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.
26322987///
2633/// Asserts that generating an instruction of that form is possible.
2634fn binOpRegister(
2988/// Furthermore, allocReg guarantees that all arguments which are
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(
26352993 self: *Self,
2636 mir_tag: Mir.Inst.Tag,
2637 lhs: MCValue,
2638 rhs: MCValue,
2639 lhs_ty: Type,
2640 rhs_ty: Type,
2641 metadata: ?BinOpMetadata,
2642) !MCValue {
2643 const lhs_is_register = lhs == .register;
2644 const rhs_is_register = rhs == .register;
2994 read_args: []const ReadArg,
2995 write_args: []const WriteArg,
2996 reuse_metadata: ?ReuseMetadata,
2997) InnerError!void {
2998 // Air instructions have exactly one output
2999 assert(!(reuse_metadata != null and write_args.len != 1)); // see note above
3000
3001 // The operand mapping is a 1:1 mapping of read args to their
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)
2647 self.register_manager.lockReg(lhs.register)
2648 else
2649 null;
2650 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
3015 // When we reuse a read_arg as a destination, the corresponding
3016 // MCValue of the read_arg will be set to .dead. In that case, we
3017 // skip allocating this read_arg.
3018 var reused_read_arg: ?usize = null;
26513019
2652 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2653 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2654 break :inst Air.refToIndex(md.lhs).?;
2655 } else null;
3020 // Lock all args which are already allocated to registers
3021 for (read_args) |arg, i| {
3022 const mcv = try arg.bind.resolveToMcv(self);
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);
2658 };
2659 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2660 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
3028 for (write_args) |arg, i| {
3029 if (arg.bind == .reg) {
3030 write_locks[i] = self.register_manager.lockReg(arg.bind.reg);
3031 }
3032 }
26613033
2662 const rhs_reg = if (rhs_is_register) rhs.register else blk: {
2663 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2664 break :inst Air.refToIndex(md.rhs).?;
2665 } else null;
3034 // Allocate registers for all args which aren't allocated to
3035 // registers yet
3036 for (read_args) |arg, i| {
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);
2668 };
2669 const new_rhs_lock = self.register_manager.lockReg(rhs_reg);
2670 defer if (new_rhs_lock) |reg| self.register_manager.unlockReg(reg);
2671
2672 const dest_reg = switch (mir_tag) {
2673 .cmp => .r0, // cmp has no destination regardless
2674 else => if (metadata) |md| blk: {
2675 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
2676 break :blk lhs_reg;
2677 } else if (rhs_is_register and self.reuseOperand(md.inst, md.rhs, 1, rhs)) {
2678 break :blk rhs_reg;
3050 if (reuse_metadata != null) {
3051 const inst = reuse_metadata.?.corresponding_inst;
3052 const operand_mapping = reuse_metadata.?.operand_mapping;
3053 const arg = write_args[0];
3054 if (arg.bind == .reg) {
3055 arg.reg.* = arg.bind.reg;
3056 } else {
3057 reuse_operand: for (read_args) |read_arg, i| {
3058 if (read_arg.bind == .inst) {
3059 const operand = read_arg.bind.inst;
3060 const mcv = try self.resolveInst(operand);
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 }
26793071 } 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.*);
26813074 }
2682 } else try self.register_manager.allocReg(null, gp),
2683 };
3075 }
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);
2686 if (!rhs_is_register) try self.genSetReg(rhs_ty, rhs_reg, rhs);
3124/// Wrapper around allocRegs and addInst tailored for specific Mir
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
26883157 const mir_data: Mir.Inst.Data = switch (mir_tag) {
26893158 .add,
26903159 .adds,
26913160 .sub,
26923161 .subs,
2693 .cmp,
26943162 .@"and",
26953163 .orr,
26963164 .eor,
......@@ -2725,78 +3193,51 @@ fn binOpRegister(
27253193 return MCValue{ .register = dest_reg };
27263194}
27273195
2728/// Don't call this function directly. Use binOp instead.
2729///
2730/// Calling this function signals an intention to generate a Mir
2731/// instruction of the form
2732///
2733/// op dest, lhs, #rhs_imm
3196/// Wrapper around allocRegs and addInst tailored for specific Mir
3197/// instructions which are binary operations acting on a register and
3198/// an immediate
27343199///
2735/// Set lhs_and_rhs_swapped to true iff inst.bin_op.lhs corresponds to
2736/// rhs and vice versa. This parameter is only used when maybe_inst !=
2737/// null.
2738///
2739/// Asserts that generating an instruction of that form is possible.
2740fn binOpImmediate(
3200/// Returns the destination register
3201fn binOpImmediateNew(
27413202 self: *Self,
27423203 mir_tag: Mir.Inst.Tag,
2743 lhs: MCValue,
2744 rhs: MCValue,
3204 lhs_bind: ReadArg.Bind,
3205 rhs_immediate: u32,
27453206 lhs_ty: Type,
27463207 lhs_and_rhs_swapped: bool,
2747 metadata: ?BinOpMetadata,
3208 maybe_inst: ?Air.Inst.Index,
27483209) !MCValue {
2749 const lhs_is_register = lhs == .register;
2750
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;
3210 var lhs_reg: Register = undefined;
3211 var dest_reg: Register = undefined;
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 },
27653215 };
2766 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
2767 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(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),
3216 const write_args = [_]WriteArg{
3217 .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg },
27833218 };
2784
2785 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
3219 const operand_mapping: []const Liveness.OperandInt = if (lhs_and_rhs_swapped) &.{1} else &.{0};
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
27873229 const mir_data: Mir.Inst.Data = switch (mir_tag) {
27883230 .add,
27893231 .adds,
27903232 .sub,
27913233 .subs,
2792 .cmp,
27933234 .@"and",
27943235 .orr,
27953236 .eor,
27963237 => .{ .rr_op = .{
27973238 .rd = dest_reg,
27983239 .rn = lhs_reg,
2799 .op = Instruction.Operand.fromU32(rhs.immediate).?,
3240 .op = Instruction.Operand.fromU32(rhs_immediate).?,
28003241 } },
28013242 .lsl,
28023243 .asr,
......@@ -2804,7 +3245,7 @@ fn binOpImmediate(
28043245 => .{ .rr_shift = .{
28053246 .rd = dest_reg,
28063247 .rm = lhs_reg,
2807 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs.immediate)),
3248 .shift_amount = Instruction.ShiftAmount.imm(@intCast(u5, rhs_immediate)),
28083249 } },
28093250 else => unreachable,
28103251 };
......@@ -2817,417 +3258,502 @@ fn binOpImmediate(
28173258 return MCValue{ .register = dest_reg };
28183259}
28193260
2820const BinOpMetadata = struct {
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(
3261fn addSub(
28393262 self: *Self,
28403263 tag: Air.Inst.Tag,
2841 lhs: MCValue,
2842 rhs: MCValue,
3264 lhs_bind: ReadArg.Bind,
3265 rhs_bind: ReadArg.Bind,
28433266 lhs_ty: Type,
28443267 rhs_ty: Type,
2845 metadata: ?BinOpMetadata,
3268 maybe_inst: ?Air.Inst.Index,
28463269) InnerError!MCValue {
2847 switch (tag) {
2848 .add,
2849 .sub,
2850 .cmp_eq,
2851 => {
2852 switch (lhs_ty.zigTypeTag()) {
2853 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2854 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2855 .Int => {
2856 const mod = self.bin_file.options.module.?;
2857 assert(lhs_ty.eql(rhs_ty, mod));
2858 const int_info = lhs_ty.intInfo(self.target.*);
2859 if (int_info.bits <= 32) {
2860 // Only say yes if the operation is
2861 // commutative, i.e. we can swap both of the
2862 // operands
2863 const lhs_immediate_ok = switch (tag) {
2864 .add => lhs == .immediate and Instruction.Operand.fromU32(lhs.immediate) != null,
2865 .sub,
2866 .cmp_eq,
2867 => false,
2868 else => unreachable,
2869 };
2870 const rhs_immediate_ok = switch (tag) {
2871 .add,
2872 .sub,
2873 .cmp_eq,
2874 => rhs == .immediate and Instruction.Operand.fromU32(rhs.immediate) != null,
2875 else => unreachable,
2876 };
3270 switch (lhs_ty.zigTypeTag()) {
3271 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3272 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3273 .Int => {
3274 const mod = self.bin_file.options.module.?;
3275 assert(lhs_ty.eql(rhs_ty, mod));
3276 const int_info = lhs_ty.intInfo(self.target.*);
3277 if (int_info.bits <= 32) {
3278 const lhs_immediate = try lhs_bind.resolveToImmediate(self);
3279 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3280
3281 // Only say yes if the operation is
3282 // commutative, i.e. we can swap both of the
3283 // operands
3284 const lhs_immediate_ok = switch (tag) {
3285 .add => if (lhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
3286 .sub => false,
3287 else => unreachable,
3288 };
3289 const rhs_immediate_ok = switch (tag) {
3290 .add,
3291 .sub,
3292 => if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false,
3293 else => unreachable,
3294 };
28773295
2878 const mir_tag: Mir.Inst.Tag = switch (tag) {
2879 .add => .add,
2880 .sub => .sub,
2881 .cmp_eq => .cmp,
2882 else => unreachable,
2883 };
3296 const mir_tag: Mir.Inst.Tag = switch (tag) {
3297 .add => .add,
3298 .sub => .sub,
3299 else => unreachable,
3300 };
28843301
2885 if (rhs_immediate_ok) {
2886 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
2887 } else if (lhs_immediate_ok) {
2888 // swap lhs and rhs
2889 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
2890 } else {
2891 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
2892 }
2893 } else {
2894 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,
3302 if (rhs_immediate_ok) {
3303 return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
3304 } else if (lhs_immediate_ok) {
3305 // swap lhs and rhs
3306 return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
3307 } else {
3308 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3309 }
3310 } else {
3311 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
29183312 }
29193313 },
2920 .div_float => {
2921 switch (lhs_ty.zigTypeTag()) {
2922 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2923 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2924 else => unreachable,
3314 else => unreachable,
3315 }
3316}
3317
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", .{});
29253340 }
29263341 },
2927 .div_trunc, .div_floor => {
2928 switch (lhs_ty.zigTypeTag()) {
2929 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2930 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2931 .Int => {
2932 const mod = self.bin_file.options.module.?;
2933 assert(lhs_ty.eql(rhs_ty, mod));
2934 const int_info = lhs_ty.intInfo(self.target.*);
2935 if (int_info.bits <= 32) {
2936 switch (int_info.signedness) {
2937 .signed => {
2938 return self.fail("TODO ARM signed integer division", .{});
2939 },
2940 .unsigned => {
2941 switch (rhs) {
2942 .immediate => |imm| {
2943 if (std.math.isPowerOfTwo(imm)) {
2944 const shift = MCValue{ .immediate = std.math.log2_int(u32, imm) };
2945 return try self.binOp(.shr, lhs, shift, lhs_ty, rhs_ty, metadata);
2946 } else {
2947 return self.fail("TODO ARM integer division by constants", .{});
2948 }
2949 },
2950 else => return self.fail("TODO ARM integer division", .{}),
2951 }
2952 },
3342 else => unreachable,
3343 }
3344}
3345
3346fn divFloat(
3347 self: *Self,
3348 lhs_bind: ReadArg.Bind,
3349 rhs_bind: ReadArg.Bind,
3350 lhs_ty: Type,
3351 rhs_ty: Type,
3352 maybe_inst: ?Air.Inst.Index,
3353) InnerError!MCValue {
3354 _ = lhs_bind;
3355 _ = rhs_bind;
3356 _ = lhs_ty;
3357 _ = rhs_ty;
3358 _ = maybe_inst;
3359
3360 switch (lhs_ty.zigTypeTag()) {
3361 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3362 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3363 else => unreachable,
3364 }
3365}
3366
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", .{});
29533402 }
2954 } else {
2955 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
2956 }
2957 },
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,
3403 },
3404 }
3405 } else {
3406 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
29673407 }
29683408 },
2969 .rem => {
2970 switch (lhs_ty.zigTypeTag()) {
2971 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
2972 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
2973 .Int => {
2974 const mod = self.bin_file.options.module.?;
2975 assert(lhs_ty.eql(rhs_ty, mod));
2976 const int_info = lhs_ty.intInfo(self.target.*);
2977 if (int_info.bits <= 32) {
2978 switch (int_info.signedness) {
2979 .signed => {
2980 return self.fail("TODO ARM signed integer mod", .{});
2981 },
2982 .unsigned => {
2983 switch (rhs) {
2984 .immediate => |imm| {
2985 if (std.math.isPowerOfTwo(imm)) {
2986 const log2 = std.math.log2_int(u32, imm);
2987
2988 const lhs_is_register = lhs == .register;
2989
2990 const lhs_lock: ?RegisterLock = if (lhs_is_register)
2991 self.register_manager.lockReg(lhs.register)
2992 else
2993 null;
2994 defer if (lhs_lock) |reg| self.register_manager.unlockReg(reg);
2995
2996 const lhs_reg = if (lhs_is_register) lhs.register else blk: {
2997 const track_inst: ?Air.Inst.Index = if (metadata) |md| inst: {
2998 break :inst Air.refToIndex(md.lhs).?;
2999 } else null;
3000
3001 break :blk try self.prepareNewRegForMoving(track_inst, gp, lhs);
3002 };
3003 const new_lhs_lock = self.register_manager.lockReg(lhs_reg);
3004 defer if (new_lhs_lock) |reg| self.register_manager.unlockReg(reg);
3005
3006 const dest_reg = if (metadata) |md| blk: {
3007 if (lhs_is_register and self.reuseOperand(md.inst, md.lhs, 0, lhs)) {
3008 break :blk lhs_reg;
3009 } else {
3010 break :blk try self.register_manager.allocReg(md.inst, gp);
3011 }
3012 } else try self.register_manager.allocReg(null, gp);
3013
3014 if (!lhs_is_register) try self.genSetReg(lhs_ty, lhs_reg, lhs);
3015
3016 try self.truncRegister(lhs_reg, dest_reg, int_info.signedness, log2);
3017 return MCValue{ .register = dest_reg };
3018 } else {
3019 return self.fail("TODO ARM integer mod by constants", .{});
3020 }
3021 },
3022 else => return self.fail("TODO ARM integer mod", .{}),
3023 }
3024 },
3409 else => unreachable,
3410 }
3411}
3412
3413fn divExact(
3414 self: *Self,
3415 lhs_bind: ReadArg.Bind,
3416 rhs_bind: ReadArg.Bind,
3417 lhs_ty: Type,
3418 rhs_ty: Type,
3419 maybe_inst: ?Air.Inst.Index,
3420) InnerError!MCValue {
3421 _ = lhs_bind;
3422 _ = rhs_bind;
3423 _ = lhs_ty;
3424 _ = rhs_ty;
3425 _ = maybe_inst;
3426
3427 switch (lhs_ty.zigTypeTag()) {
3428 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3429 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3430 .Int => return self.fail("TODO ARM div_exact", .{}),
3431 else => unreachable,
3432 }
3433}
3434
3435fn rem(
3436 self: *Self,
3437 lhs_bind: ReadArg.Bind,
3438 rhs_bind: ReadArg.Bind,
3439 lhs_ty: Type,
3440 rhs_ty: Type,
3441 maybe_inst: ?Air.Inst.Index,
3442) InnerError!MCValue {
3443 switch (lhs_ty.zigTypeTag()) {
3444 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3445 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3446 .Int => {
3447 const mod = self.bin_file.options.module.?;
3448 assert(lhs_ty.eql(rhs_ty, mod));
3449 const int_info = lhs_ty.intInfo(self.target.*);
3450 if (int_info.bits <= 32) {
3451 switch (int_info.signedness) {
3452 .signed => {
3453 return self.fail("TODO ARM signed integer mod", .{});
3454 },
3455 .unsigned => {
3456 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
3457
3458 if (rhs_immediate) |imm| {
3459 if (std.math.isPowerOfTwo(imm)) {
3460 const log2 = std.math.log2_int(u32, imm);
3461
3462 var lhs_reg: Register = undefined;
3463 var dest_reg: Register = undefined;
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", .{});
30253488 }
3026 } else {
3027 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
3028 }
3029 },
3030 else => unreachable,
3489 },
3490 }
3491 } else {
3492 return self.fail("TODO ARM integer division for integers > u32/i32", .{});
30313493 }
30323494 },
3033 .mod => {
3034 switch (lhs_ty.zigTypeTag()) {
3035 .Float => return self.fail("TODO ARM binary operations on floats", .{}),
3036 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3037 .Int => return self.fail("TODO ARM mod", .{}),
3038 else => unreachable,
3495 else => unreachable,
3496 }
3497}
3498
3499fn modulo(
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", .{});
30393554 }
30403555 },
3041 .addwrap,
3042 .subwrap,
3043 .mulwrap,
3044 => {
3045 const base_tag: Air.Inst.Tag = switch (tag) {
3046 .addwrap => .add,
3047 .subwrap => .sub,
3048 .mulwrap => .mul,
3049 else => unreachable,
3050 };
3556 else => unreachable,
3557 }
3558}
3559
3560fn bitwise(
3561 self: *Self,
3562 tag: Air.Inst.Tag,
3563 lhs_bind: ReadArg.Bind,
3564 rhs_bind: ReadArg.Bind,
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/mul
3053 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
3054
3055 // Truncate if necessary
3056 switch (lhs_ty.zigTypeTag()) {
3057 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3058 .Int => {
3059 const int_info = lhs_ty.intInfo(self.target.*);
3060 if (int_info.bits <= 32) {
3061 const result_reg = result.register;
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,
3589 if (rhs_immediate_ok) {
3590 return try self.binOpImmediateNew(mir_tag, lhs_bind, rhs_immediate.?, lhs_ty, false, maybe_inst);
3591 } else if (lhs_immediate_ok) {
3592 // swap lhs and rhs
3593 return try self.binOpImmediateNew(mir_tag, rhs_bind, lhs_immediate.?, rhs_ty, true, maybe_inst);
3594 } else {
3595 return try self.binOpRegisterNew(mir_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
3596 }
3597 } else {
3598 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
30723599 }
30733600 },
3074 .bit_and,
3075 .bit_or,
3076 .xor,
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 };
3601 else => unreachable,
3602 }
3603}
30943604
3095 if (rhs_immediate_ok) {
3096 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
3097 } else if (lhs_immediate_ok) {
3098 // swap lhs and rhs
3099 return try self.binOpImmediate(mir_tag, rhs, lhs, rhs_ty, true, metadata);
3100 } else {
3101 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
3102 }
3103 } else {
3104 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3105 }
3106 },
3107 else => unreachable,
3108 }
3109 },
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 };
3605fn shiftExact(
3606 self: *Self,
3607 tag: Air.Inst.Tag,
3608 lhs_bind: ReadArg.Bind,
3609 rhs_bind: ReadArg.Bind,
3610 lhs_ty: Type,
3611 rhs_ty: Type,
3612 maybe_inst: ?Air.Inst.Index,
3613) InnerError!MCValue {
3614 switch (lhs_ty.zigTypeTag()) {
3615 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3616 .Int => {
3617 const int_info = lhs_ty.intInfo(self.target.*);
3618 if (int_info.bits <= 32) {
3619 const rhs_immediate = try rhs_bind.resolveToImmediate(self);
31283620
3129 if (rhs_immediate_ok) {
3130 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
3131 } else {
3132 return try self.binOpRegister(mir_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
3133 }
3134 } else {
3135 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3136 }
3137 },
3138 else => unreachable,
3621 const mir_tag: Mir.Inst.Tag = switch (tag) {
3622 .shl_exact => .lsl,
3623 .shr_exact => switch (lhs_ty.intInfo(self.target.*).signedness) {
3624 .signed => Mir.Inst.Tag.asr,
3625 .unsigned => Mir.Inst.Tag.lsr,
3626 },
3627 else => unreachable,
3628 };
3629
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", .{});
31393637 }
31403638 },
3141 .shl,
3142 .shr,
3143 => {
3144 const base_tag: Air.Inst.Tag = switch (tag) {
3145 .shl => .shl_exact,
3146 .shr => .shr_exact,
3147 else => unreachable,
3148 };
3639 else => unreachable,
3640 }
3641}
3642
3643fn shiftNormal(
3644 self: *Self,
3645 tag: Air.Inst.Tag,
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_exact
3151 const result = try self.binOp(base_tag, lhs, rhs, lhs_ty, rhs_ty, metadata);
3658 // Generate a shl_exact/shr_exact
3659 const result = try self.shiftExact(base_tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, maybe_inst);
31523660
3153 // Truncate if necessary
3154 switch (tag) {
3155 .shr => return result,
3156 .shl => switch (lhs_ty.zigTypeTag()) {
3157 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3158 .Int => {
3159 const int_info = lhs_ty.intInfo(self.target.*);
3160 if (int_info.bits <= 32) {
3161 const result_reg = result.register;
3661 // Truncate if necessary
3662 switch (tag) {
3663 .shr => return result,
3664 .shl => switch (lhs_ty.zigTypeTag()) {
3665 .Vector => return self.fail("TODO ARM binary operations on vectors", .{}),
3666 .Int => {
3667 const int_info = lhs_ty.intInfo(self.target.*);
3668 if (int_info.bits <= 32) {
3669 const result_reg = result.register;
31623670
3163 if (int_info.bits < 32) {
3164 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3165 return result;
3166 } else return result;
3167 } else {
3168 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3169 }
3170 },
3171 else => unreachable,
3172 },
3173 else => unreachable,
3174 }
3671 if (int_info.bits < 32) {
3672 try self.truncRegister(result_reg, result_reg, int_info.signedness, int_info.bits);
3673 return result;
3674 } else return result;
3675 } else {
3676 return self.fail("TODO ARM binary operations on integers > u32/i32", .{});
3677 }
3678 },
3679 else => unreachable,
31753680 },
3176 .bool_and,
3177 .bool_or,
3178 => {
3179 switch (lhs_ty.zigTypeTag()) {
3180 .Bool => {
3181 const lhs_immediate_ok = lhs == .immediate;
3182 const rhs_immediate_ok = rhs == .immediate;
3681 else => unreachable,
3682 }
3683}
31833684
3184 const mir_tag: Mir.Inst.Tag = switch (tag) {
3185 .bool_and => .@"and",
3186 .bool_or => .orr,
3187 else => unreachable,
3188 };
3685fn booleanOp(
3686 self: *Self,
3687 tag: Air.Inst.Tag,
3688 lhs_bind: ReadArg.Bind,
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) {
3191 return try self.binOpImmediate(mir_tag, lhs, rhs, lhs_ty, false, metadata);
3192 } else if (lhs_immediate_ok) {
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 },
3699 const mir_tag: Mir.Inst.Tag = switch (tag) {
3700 .bool_and => .@"and",
3701 .bool_or => .orr,
31993702 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);
32003712 }
32013713 },
3202 .ptr_add,
3203 .ptr_sub,
3204 => {
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.*));
3714 else => unreachable,
3715 }
3716}
32133717
3214 if (elem_size == 1) {
3215 const base_tag: Mir.Inst.Tag = switch (tag) {
3216 .ptr_add => .add,
3217 .ptr_sub => .sub,
3218 else => unreachable,
3219 };
3718fn ptrArithmetic(
3719 self: *Self,
3720 tag: Air.Inst.Tag,
3721 lhs_bind: ReadArg.Bind,
3722 rhs_bind: ReadArg.Bind,
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);
3222 } else {
3223 // convert the offset into a byte offset by
3224 // multiplying it with elem_size
3225 const offset = try self.binOp(.mul, rhs, .{ .immediate = elem_size }, Type.usize, Type.usize, null);
3226 const addr = try self.binOp(tag, lhs, offset, Type.initTag(.manyptr_u8), Type.usize, null);
3227 return addr;
3228 }
3229 },
3732 const ptr_ty = lhs_ty;
3733 const elem_ty = switch (ptr_ty.ptrSize()) {
3734 .One => ptr_ty.childType().childType(), // ptr to array, so get array element type
3735 else => ptr_ty.childType(),
3736 };
3737 const elem_size = @intCast(u32, elem_ty.abiSize(self.target.*));
3738
3739 const base_tag: Air.Inst.Tag = switch (tag) {
3740 .ptr_add => .add,
3741 .ptr_sub => .sub,
32303742 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;
32313757 }
32323758 },
32333759 else => unreachable,
......@@ -3312,9 +3838,8 @@ fn genInlineMemcpy(
33123838 // mov count, #0
33133839 _ = try self.addInst(.{
33143840 .tag = .mov,
3315 .data = .{ .rr_op = .{
3841 .data = .{ .r_op_mov = .{
33163842 .rd = count,
3317 .rn = .r0,
33183843 .op = Instruction.Operand.imm(0, 0),
33193844 } },
33203845 });
......@@ -3323,8 +3848,7 @@ fn genInlineMemcpy(
33233848 // cmp count, len
33243849 _ = try self.addInst(.{
33253850 .tag = .cmp,
3326 .data = .{ .rr_op = .{
3327 .rd = .r0,
3851 .data = .{ .r_op_cmp = .{
33283852 .rn = count,
33293853 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
33303854 } },
......@@ -3418,9 +3942,8 @@ fn genInlineMemsetCode(
34183942 // mov count, #0
34193943 _ = try self.addInst(.{
34203944 .tag = .mov,
3421 .data = .{ .rr_op = .{
3945 .data = .{ .r_op_mov = .{
34223946 .rd = count,
3423 .rn = .r0,
34243947 .op = Instruction.Operand.imm(0, 0),
34253948 } },
34263949 });
......@@ -3429,8 +3952,7 @@ fn genInlineMemsetCode(
34293952 // cmp count, len
34303953 _ = try self.addInst(.{
34313954 .tag = .cmp,
3432 .data = .{ .rr_op = .{
3433 .rd = .r0,
3955 .data = .{ .r_op_cmp = .{
34343956 .rn = count,
34353957 .op = Instruction.Operand.reg(len, Instruction.Operand.Shift.none),
34363958 } },
......@@ -3568,7 +4090,7 @@ fn airArg(self: *Self, inst: Air.Inst.Index) !void {
35684090 .register => |reg| blk: {
35694091 const abi_size = @intCast(u32, ty.abiSize(self.target.*));
35704092 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);
35724094 try self.genSetStack(ty, stack_offset, MCValue{ .register = reg });
35734095
35744096 break :blk MCValue{ .stack_offset = stack_offset };
......@@ -3655,7 +4177,7 @@ fn airCall(self: *Self, inst: Air.Inst.Index, modifier: std.builtin.CallOptions.
36554177 const ret_ty = fn_ty.fnReturnType();
36564178 const ret_abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
36574179 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
36604182 var ptr_ty_payload: Type.Payload.ElemType = .{
36614183 .base = .{ .tag = .single_mut_pointer },
......@@ -3843,14 +4365,7 @@ fn airRetLoad(self: *Self, inst: Air.Inst.Index) !void {
38434365 const abi_size = @intCast(u32, ret_ty.abiSize(self.target.*));
38444366 const abi_align = ret_ty.abiAlignment(self.target.*);
38454367
3846 // This is essentially allocMem without the
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);
4368 const offset = try self.allocMem(abi_size, abi_align, null);
38544369
38554370 const tmp_mcv = MCValue{ .stack_offset = offset };
38564371 try self.load(tmp_mcv, ptr, ptr_ty);
......@@ -3871,32 +4386,16 @@ fn airCmp(self: *Self, inst: Air.Inst.Index, op: math.CompareOperator) !void {
38714386 const lhs_ty = self.air.typeOf(bin_op.lhs);
38724387
38734388 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else blk: {
3874 const operands: BinOpOperands = .{ .inst = .{
3875 .inst = inst,
3876 .lhs = bin_op.lhs,
3877 .rhs = bin_op.rhs,
3878 } };
3879 break :blk try self.cmp(operands, lhs_ty, op);
4389 break :blk try self.cmp(.{ .inst = bin_op.lhs }, .{ .inst = bin_op.rhs }, lhs_ty, op);
38804390 };
38814391
38824392 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none });
38834393}
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
38974395fn cmp(
38984396 self: *Self,
3899 operands: BinOpOperands,
4397 lhs: ReadArg.Bind,
4398 rhs: ReadArg.Bind,
39004399 lhs_ty: Type,
39014400 op: math.CompareOperator,
39024401) !MCValue {
......@@ -3926,22 +4425,47 @@ fn cmp(
39264425 if (int_info.bits <= 32) {
39274426 try self.spillCompareFlagsIfOccupied();
39284427
3929 switch (operands) {
3930 .inst => |inst_op| {
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);
4428 var lhs_reg: Register = undefined;
4429 var rhs_reg: Register = undefined;
39384430
3939 self.cpsr_flags_inst = inst_op.inst;
3940 _ = try self.binOp(.cmp_eq, lhs, rhs, int_ty, int_ty, metadata);
3941 },
3942 .mcv => |mcv_op| {
3943 _ = try self.binOp(.cmp_eq, mcv_op.lhs, mcv_op.rhs, int_ty, int_ty, null);
3944 },
4431 const rhs_immediate = try rhs.resolveToImmediate(self);
4432 const rhs_immediate_ok = if (rhs_immediate) |imm| Instruction.Operand.fromU32(imm) != null else false;
4433
4434 if (rhs_immediate_ok) {
4435 const read_args = [_]ReadArg{
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 });
39454469 }
39464470
39474471 return switch (int_info.signedness) {
......@@ -4020,9 +4544,7 @@ fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index {
40204544 // bne ...
40214545 _ = try self.addInst(.{
40224546 .tag = .cmp,
4023 .cond = .al,
4024 .data = .{ .rr_op = .{
4025 .rd = .r0,
4547 .data = .{ .r_op_cmp = .{
40264548 .rn = reg,
40274549 .op = Instruction.Operand.imm(1, 0),
40284550 } },
......@@ -4132,7 +4654,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void {
41324654 if (else_value == .dead)
41334655 continue;
41344656 // 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;
41364658 while (true) {
41374659 i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead?
41384660 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 {
41594681 if (then_value == .dead)
41604682 continue;
41614683 const parent_mcv = blk: {
4162 var i: usize = self.branch_stack.items.len - 2;
4684 var i: usize = self.branch_stack.items.len - 1;
41634685 while (true) {
41644686 i -= 1;
41654687 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 {
41854707 return self.finishAir(inst, .unreach, .{ .none, .none, .none });
41864708}
41874709
4188fn isNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
4189 if (ty.isPtrLikeOptional()) {
4190 assert(ty.abiSize(self.target.*) == 4);
4191
4192 const reg_mcv: MCValue = switch (operand) {
4193 .register => operand,
4194 else => .{ .register = try self.copyToTmpRegister(ty, operand) },
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 });
4710fn isNull(
4711 self: *Self,
4712 operand_bind: ReadArg.Bind,
4713 operand_ty: Type,
4714) !MCValue {
4715 if (operand_ty.isPtrLikeOptional()) {
4716 assert(operand_ty.abiSize(self.target.*) == 4);
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);
42074720 } else {
42084721 return self.fail("TODO implement non-pointer optionals", .{});
42094722 }
42104723}
42114724
4212fn isNonNull(self: *Self, ty: Type, operand: MCValue) !MCValue {
4213 const is_null_result = try self.isNull(ty, operand);
4725fn isNonNull(
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);
42144731 assert(is_null_result.cpsr_flags == .eq);
42154732
42164733 return MCValue{ .cpsr_flags = .ne };
42174734}
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
42474736fn airIsNull(self: *Self, inst: Air.Inst.Index) !void {
42484737 const un_op = self.air.instructions.items(.data)[inst].un_op;
4249
4250 try self.spillCompareFlagsIfOccupied();
4251 self.cpsr_flags_inst = inst;
4252
42534738 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4254 const operand = try self.resolveInst(un_op);
4255 const ty = self.air.typeOf(un_op);
4256 break :result try self.isNull(ty, operand);
4739 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4740 const operand_ty = self.air.typeOf(un_op);
4741
4742 break :result try self.isNull(operand_bind, operand_ty);
42574743 };
42584744 return self.finishAir(inst, result, .{ un_op, .none, .none });
42594745}
......@@ -4263,16 +4749,12 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
42634749 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
42644750 const operand_ptr = try self.resolveInst(un_op);
42654751 const ptr_ty = self.air.typeOf(un_op);
4266 const operand: MCValue = blk: {
4267 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4268 // The MCValue that holds the pointer can be re-used as the value.
4269 break :blk operand_ptr;
4270 } else {
4271 break :blk try self.allocRegOrMem(inst, true);
4272 }
4273 };
4752 const elem_ty = ptr_ty.elemType();
4753
4754 const operand = try self.allocRegOrMem(elem_ty, true, null);
42744755 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);
42764758 };
42774759 return self.finishAir(inst, result, .{ un_op, .none, .none });
42784760}
......@@ -4280,9 +4762,10 @@ fn airIsNullPtr(self: *Self, inst: Air.Inst.Index) !void {
42804762fn airIsNonNull(self: *Self, inst: Air.Inst.Index) !void {
42814763 const un_op = self.air.instructions.items(.data)[inst].un_op;
42824764 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4283 const operand = try self.resolveInst(un_op);
4284 const ty = self.air.typeOf(un_op);
4285 break :result try self.isNonNull(ty, operand);
4765 const operand_bind: ReadArg.Bind = .{ .inst = un_op };
4766 const operand_ty = self.air.typeOf(un_op);
4767
4768 break :result try self.isNonNull(operand_bind, operand_ty);
42864769 };
42874770 return self.finishAir(inst, result, .{ un_op, .none, .none });
42884771}
......@@ -4292,26 +4775,57 @@ fn airIsNonNullPtr(self: *Self, inst: Air.Inst.Index) !void {
42924775 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
42934776 const operand_ptr = try self.resolveInst(un_op);
42944777 const ptr_ty = self.air.typeOf(un_op);
4295 const operand: MCValue = blk: {
4296 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4297 // The MCValue that holds the pointer can be re-used as the value.
4298 break :blk operand_ptr;
4299 } else {
4300 break :blk try self.allocRegOrMem(inst, true);
4301 }
4302 };
4778 const elem_ty = ptr_ty.elemType();
4779
4780 const operand = try self.allocRegOrMem(elem_ty, true, null);
43034781 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);
43054784 };
43064785 return self.finishAir(inst, result, .{ un_op, .none, .none });
43074786}
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
43094822fn airIsErr(self: *Self, inst: Air.Inst.Index) !void {
43104823 const un_op = self.air.instructions.items(.data)[inst].un_op;
43114824 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4312 const operand = try self.resolveInst(un_op);
4313 const ty = self.air.typeOf(un_op);
4314 break :result try self.isErr(ty, operand);
4825 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4826 const error_union_ty = self.air.typeOf(un_op);
4827
4828 break :result try self.isErr(error_union_bind, error_union_ty);
43154829 };
43164830 return self.finishAir(inst, result, .{ un_op, .none, .none });
43174831}
......@@ -4321,16 +4835,12 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
43214835 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
43224836 const operand_ptr = try self.resolveInst(un_op);
43234837 const ptr_ty = self.air.typeOf(un_op);
4324 const operand: MCValue = blk: {
4325 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4326 // The MCValue that holds the pointer can be re-used as the value.
4327 break :blk operand_ptr;
4328 } else {
4329 break :blk try self.allocRegOrMem(inst, true);
4330 }
4331 };
4838 const elem_ty = ptr_ty.elemType();
4839
4840 const operand = try self.allocRegOrMem(elem_ty, true, null);
43324841 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);
43344844 };
43354845 return self.finishAir(inst, result, .{ un_op, .none, .none });
43364846}
......@@ -4338,9 +4848,10 @@ fn airIsErrPtr(self: *Self, inst: Air.Inst.Index) !void {
43384848fn airIsNonErr(self: *Self, inst: Air.Inst.Index) !void {
43394849 const un_op = self.air.instructions.items(.data)[inst].un_op;
43404850 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
4341 const operand = try self.resolveInst(un_op);
4342 const ty = self.air.typeOf(un_op);
4343 break :result try self.isNonErr(ty, operand);
4851 const error_union_bind: ReadArg.Bind = .{ .inst = un_op };
4852 const error_union_ty = self.air.typeOf(un_op);
4853
4854 break :result try self.isNonErr(error_union_bind, error_union_ty);
43444855 };
43454856 return self.finishAir(inst, result, .{ un_op, .none, .none });
43464857}
......@@ -4350,16 +4861,12 @@ fn airIsNonErrPtr(self: *Self, inst: Air.Inst.Index) !void {
43504861 const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: {
43514862 const operand_ptr = try self.resolveInst(un_op);
43524863 const ptr_ty = self.air.typeOf(un_op);
4353 const operand: MCValue = blk: {
4354 if (self.reuseOperand(inst, un_op, 0, operand_ptr)) {
4355 // The MCValue that holds the pointer can be re-used as the value.
4356 break :blk operand_ptr;
4357 } else {
4358 break :blk try self.allocRegOrMem(inst, true);
4359 }
4360 };
4864 const elem_ty = ptr_ty.elemType();
4865
4866 const operand = try self.allocRegOrMem(elem_ty, true, null);
43614867 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);
43634870 };
43644871 return self.finishAir(inst, result, .{ un_op, .none, .none });
43654872}
......@@ -4456,14 +4963,7 @@ fn airSwitch(self: *Self, inst: Air.Inst.Index) !void {
44564963 defer self.gpa.free(branch_into_prong_relocs);
44574964
44584965 for (items) |item, idx| {
4459 const condition = try self.resolveInst(pl_op.operand);
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);
4966 const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq);
44674967 branch_into_prong_relocs[idx] = try self.condBr(cmp_result);
44684968 }
44694969
......@@ -4579,7 +5079,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void {
45795079 .none, .dead, .unreach => unreachable,
45805080 .register, .stack_offset, .memory => operand_mcv,
45815081 .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);
45835083 try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv);
45845084 break :blk new_mcv;
45855085 },
......@@ -4832,9 +5332,8 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro
48325332 .register_v_flag => .vs,
48335333 else => unreachable,
48345334 },
4835 .data = .{ .rr_op = .{
5335 .data = .{ .r_op_mov = .{
48365336 .rd = cond_reg,
4837 .rn = .r0,
48385337 .op = Instruction.Operand.fromU32(1).?,
48395338 } },
48405339 });
......@@ -4935,9 +5434,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
49355434 // mov reg, 0
49365435 _ = try self.addInst(.{
49375436 .tag = .mov,
4938 .data = .{ .rr_op = .{
5437 .data = .{ .r_op_mov = .{
49395438 .rd = reg,
4940 .rn = .r0,
49415439 .op = zero,
49425440 } },
49435441 });
......@@ -4946,9 +5444,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
49465444 _ = try self.addInst(.{
49475445 .tag = .mov,
49485446 .cond = condition,
4949 .data = .{ .rr_op = .{
5447 .data = .{ .r_op_mov = .{
49505448 .rd = reg,
4951 .rn = .r0,
49525449 .op = one,
49535450 } },
49545451 });
......@@ -4957,18 +5454,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
49575454 if (Instruction.Operand.fromU32(x)) |op| {
49585455 _ = try self.addInst(.{
49595456 .tag = .mov,
4960 .data = .{ .rr_op = .{
5457 .data = .{ .r_op_mov = .{
49615458 .rd = reg,
4962 .rn = .r0,
49635459 .op = op,
49645460 } },
49655461 });
49665462 } else if (Instruction.Operand.fromU32(~x)) |op| {
49675463 _ = try self.addInst(.{
49685464 .tag = .mvn,
4969 .data = .{ .rr_op = .{
5465 .data = .{ .r_op_mov = .{
49705466 .rd = reg,
4971 .rn = .r0,
49725467 .op = op,
49735468 } },
49745469 });
......@@ -4984,9 +5479,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
49845479 } else {
49855480 _ = try self.addInst(.{
49865481 .tag = .mov,
4987 .data = .{ .rr_op = .{
5482 .data = .{ .r_op_mov = .{
49885483 .rd = reg,
4989 .rn = .r0,
49905484 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
49915485 } },
49925486 });
......@@ -5028,9 +5522,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
50285522 // orr reg, reg, #0xdd, 8
50295523 _ = try self.addInst(.{
50305524 .tag = .mov,
5031 .data = .{ .rr_op = .{
5525 .data = .{ .r_op_mov = .{
50325526 .rd = reg,
5033 .rn = .r0,
50345527 .op = Instruction.Operand.imm(@truncate(u8, x), 0),
50355528 } },
50365529 });
......@@ -5069,9 +5562,8 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
50695562 // mov reg, src_reg
50705563 _ = try self.addInst(.{
50715564 .tag = .mov,
5072 .data = .{ .rr_op = .{
5565 .data = .{ .r_op_mov = .{
50735566 .rd = reg,
5074 .rn = .r0,
50755567 .op = Instruction.Operand.reg(src_reg, Instruction.Operand.Shift.none),
50765568 } },
50775569 });
......@@ -5307,7 +5799,7 @@ fn airArrayToSlice(self: *Self, inst: Air.Inst.Index) !void {
53075799 const array_ty = ptr_ty.childType();
53085800 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);
53115803 try self.genSetStack(ptr_ty, stack_offset, ptr);
53125804 try self.genSetStack(Type.initTag(.usize), stack_offset - 4, .{ .immediate = array_len });
53135805 break :result MCValue{ .stack_offset = stack_offset };
......@@ -5461,15 +5953,24 @@ fn airTry(self: *Self, inst: Air.Inst.Index) !void {
54615953 const extra = self.air.extraData(Air.Try, pl_op.payload);
54625954 const body = self.air.extra[extra.end..][0..extra.data.body_len];
54635955 const result: MCValue = result: {
5956 const error_union_bind: ReadArg.Bind = .{ .inst = pl_op.operand };
54645957 const error_union_ty = self.air.typeOf(pl_op.operand);
5465 const error_union = try self.resolveInst(pl_op.operand);
5466 const is_err_result = try self.isErr(error_union_ty, error_union);
5958 const error_union_size = @intCast(u32, error_union_ty.abiSize(self.target.*));
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);
54675968 const reloc = try self.condBr(is_err_result);
54685969
54695970 try self.genBody(body);
5470
54715971 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);
54735974 };
54745975 return self.finishAir(inst, result, .{ pl_op.operand, .none, .none });
54755976}
src/arch/arm/Emit.zig+88-12
......@@ -11,6 +11,7 @@ const link = @import("../../link.zig");
1111const Module = @import("../../Module.zig");
1212const Type = @import("../../type.zig").Type;
1313const ErrorMsg = Module.ErrorMsg;
14const Target = std.Target;
1415const assert = std.debug.assert;
1516const DW = std.dwarf;
1617const leb128 = std.leb;
......@@ -93,6 +94,8 @@ pub fn emitMir(
9394 .sub => try emit.mirDataProcessing(inst),
9495 .subs => try emit.mirDataProcessing(inst),
9596
97 .sub_sp_scratch_r0 => try emit.mirSubStackPointer(inst),
98
9699 .asr => try emit.mirShift(inst),
97100 .lsl => try emit.mirShift(inst),
98101 .lsr => try emit.mirShift(inst),
......@@ -190,6 +193,24 @@ fn instructionSize(emit: *Emit, inst: Mir.Inst.Index) usize {
190193 .dbg_epilogue_begin,
191194 .dbg_prologue_end,
192195 => 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
193214 else => return 4,
194215 }
195216}
......@@ -385,20 +406,75 @@ fn dbgAdvancePCAndLine(self: *Emit, line: u32, column: u32) !void {
385406fn mirDataProcessing(emit: *Emit, inst: Mir.Inst.Index) !void {
386407 const tag = emit.mir.instructions.items(.tag)[inst];
387408 const cond = emit.mir.instructions.items(.cond)[inst];
388 const rr_op = emit.mir.instructions.items(.data)[inst].rr_op;
389409
390410 switch (tag) {
391 .add => try emit.writeInstruction(Instruction.add(cond, rr_op.rd, rr_op.rn, rr_op.op)),
392 .adds => try emit.writeInstruction(Instruction.adds(cond, rr_op.rd, rr_op.rn, rr_op.op)),
393 .@"and" => try emit.writeInstruction(Instruction.@"and"(cond, rr_op.rd, rr_op.rn, rr_op.op)),
394 .cmp => try emit.writeInstruction(Instruction.cmp(cond, rr_op.rn, rr_op.op)),
395 .eor => try emit.writeInstruction(Instruction.eor(cond, rr_op.rd, rr_op.rn, rr_op.op)),
396 .mov => try emit.writeInstruction(Instruction.mov(cond, rr_op.rd, rr_op.op)),
397 .mvn => try emit.writeInstruction(Instruction.mvn(cond, rr_op.rd, rr_op.op)),
398 .orr => try emit.writeInstruction(Instruction.orr(cond, rr_op.rd, rr_op.rn, rr_op.op)),
399 .rsb => try emit.writeInstruction(Instruction.rsb(cond, rr_op.rd, rr_op.rn, rr_op.op)),
400 .sub => try emit.writeInstruction(Instruction.sub(cond, rr_op.rd, rr_op.rn, rr_op.op)),
401 .subs => try emit.writeInstruction(Instruction.subs(cond, rr_op.rd, rr_op.rn, rr_op.op)),
411 .add,
412 .adds,
413 .@"and",
414 .eor,
415 .orr,
416 .rsb,
417 .sub,
418 .subs,
419 => {
420 const rr_op = emit.mir.instructions.items(.data)[inst].rr_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 },
402478 else => unreachable,
403479 }
404480}
src/arch/arm/Mir.zig+23
......@@ -111,6 +111,11 @@ pub const Inst = struct {
111111 strh,
112112 /// Subtract
113113 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,
114119 /// Subtract, update condition flags
115120 subs,
116121 /// Supervisor Call
......@@ -144,6 +149,10 @@ pub const Inst = struct {
144149 ///
145150 /// Used by e.g. svc
146151 imm24: u24,
152 /// A 32-bit immediate value.
153 ///
154 /// Used by e.g. sub_sp_scratch_r0
155 imm32: u32,
147156 /// Index into `extra`. Meaning of what can be found there is context-dependent.
148157 ///
149158 /// Used by e.g. load_memory
......@@ -166,6 +175,20 @@ pub const Inst = struct {
166175 rd: Register,
167176 imm16: u16,
168177 },
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 },
169192 /// Two registers and a shift amount
170193 ///
171194 /// Used by e.g. lsl
test/behavior/alignof.zig-1
......@@ -13,7 +13,6 @@ const Foo = struct {
1313test "@alignOf(T) before referencing T" {
1414 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1515 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
16 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1716 comptime try expect(@alignOf(Foo) != maxInt(usize));
1817 if (native_arch == .x86_64) {
1918 comptime try expect(@alignOf(Foo) == 4);
test/behavior/array.zig-5
......@@ -175,7 +175,6 @@ test "nested arrays of integers" {
175175
176176test "implicit comptime in array type size" {
177177 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
179178
180179 var arr: [plusOne(10)]bool = undefined;
181180 try expect(arr.len == 11);
......@@ -245,7 +244,6 @@ const Sub = struct { b: u8 };
245244const Str = struct { a: []Sub };
246245test "set global var array via slice embedded in struct" {
247246 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
248 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
249247
250248 var s = Str{ .a = s_array[0..] };
251249
......@@ -298,7 +296,6 @@ fn testArrayByValAtComptime(b: [2]u8) u8 {
298296
299297test "comptime evaluating function that takes array by value" {
300298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
301 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
302299
303300 const arr = [_]u8{ 1, 2 };
304301 const x = comptime testArrayByValAtComptime(arr);
......@@ -427,7 +424,6 @@ test "anonymous literal in array" {
427424
428425test "access the null element of a null terminated array" {
429426 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
430 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
431427
432428 const S = struct {
433429 fn doTheTest() !void {
......@@ -484,7 +480,6 @@ test "sentinel element count towards the ABI size calculation" {
484480test "zero-sized array with recursive type definition" {
485481 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
486482 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
487 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
488483
489484 const U = struct {
490485 fn foo(comptime T: type, comptime n: usize) type {
test/behavior/basic.zig-4
......@@ -465,7 +465,6 @@ fn nine() u8 {
465465
466466test "struct inside function" {
467467 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
468 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
469468
470469 try testStructInFn();
471470 comptime try testStructInFn();
......@@ -514,7 +513,6 @@ var global_foo: *i32 = undefined;
514513
515514test "peer result location with typed parent, runtime condition, comptime prongs" {
516515 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
517 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
518516
519517 const S = struct {
520518 fn doTheTest(arg: i32) i32 {
......@@ -643,7 +641,6 @@ test "global constant is loaded with a runtime-known index" {
643641
644642test "multiline string literal is null terminated" {
645643 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
646 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
647644
648645 const s1 =
649646 \\one
......@@ -1060,7 +1057,6 @@ comptime {
10601057
10611058test "switch inside @as gets correct type" {
10621059 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1063 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10641060
10651061 var a: u32 = 0;
10661062 var b: [2]u32 = undefined;
test/behavior/bitcast.zig-1
......@@ -138,7 +138,6 @@ test "@bitCast extern structs at runtime and comptime" {
138138 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest;
139139 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
140140 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
142141
143142 const Full = extern struct {
144143 number: u16,
test/behavior/cast.zig-10
......@@ -523,7 +523,6 @@ fn testCastConstArrayRefToConstSlice() !void {
523523
524524test "peer type resolution: error and [N]T" {
525525 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
526 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
527526
528527 try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
529528 comptime try expect(mem.eql(u8, try testPeerErrorAndArray(0), "OK"));
......@@ -548,7 +547,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
548547test "single-item pointer of array to slice to unknown length pointer" {
549548 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
550549 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
551 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
552550
553551 try testCastPtrOfArrayToSliceAndPtr();
554552 comptime try testCastPtrOfArrayToSliceAndPtr();
......@@ -578,7 +576,6 @@ fn testCastPtrOfArrayToSliceAndPtr() !void {
578576test "cast *[1][*]const u8 to [*]const ?[*]const u8" {
579577 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
580578 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
581 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
582579 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
583580
584581 const window_name = [1][*]const u8{"window name"};
......@@ -649,7 +646,6 @@ test "@floatCast cast down" {
649646test "peer type resolution: unreachable, error set, unreachable" {
650647 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
651648 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
652 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
653649
654650 const Error = error{
655651 FileDescriptorAlreadyPresentInSet,
......@@ -922,7 +918,6 @@ test "peer cast *[N:x]T to *[N]T" {
922918
923919test "peer cast [*:x]T to [*]T" {
924920 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
925 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
926921 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
927922
928923 const S = struct {
......@@ -964,7 +959,6 @@ test "peer cast [:x]T to [*:x]T" {
964959
965960test "peer type resolution implicit cast to return type" {
966961 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
967 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
968962
969963 const S = struct {
970964 fn doTheTest() !void {
......@@ -984,7 +978,6 @@ test "peer type resolution implicit cast to return type" {
984978
985979test "peer type resolution implicit cast to variable type" {
986980 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
987 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
988981 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
989982
990983 const S = struct {
......@@ -1009,7 +1002,6 @@ test "variable initialization uses result locations properly with regards to the
10091002
10101003test "cast between C pointer with different but compatible types" {
10111004 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1012 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10131005
10141006 const S = struct {
10151007 fn foo(arg: [*]c_ushort) u16 {
......@@ -1026,7 +1018,6 @@ test "cast between C pointer with different but compatible types" {
10261018
10271019test "peer type resolve string lit with sentinel-terminated mutable slice" {
10281020 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1029 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10301021 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
10311022
10321023 var array: [4:0]u8 = undefined;
......@@ -1079,7 +1070,6 @@ test "comptime float casts" {
10791070test "pointer reinterpret const float to int" {
10801071 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
10811072 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
1082 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10831073
10841074 // The hex representation is 0x3fe3333333333303.
10851075 const float: f64 = 5.99999999999994648725e-01;
test/behavior/comptime_memory.zig-2
......@@ -87,7 +87,6 @@ fn bigToNativeEndian(comptime T: type, v: T) T {
8787test "type pun endianness" {
8888 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
8989 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
90 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
9190
9291 comptime {
9392 const StructOfBytes = extern struct { x: [4]u8 };
......@@ -398,7 +397,6 @@ test "offset field ptr by enclosing array element size" {
398397test "accessing reinterpreted memory of parent object" {
399398 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
400399 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
401 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
402400 const S = extern struct {
403401 a: f32,
404402 b: [4]u8,
test/behavior/const_slice_child.zig-1
......@@ -9,7 +9,6 @@ var argv: [*]const [*]const u8 = undefined;
99test "const slice child" {
1010 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
1111 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
12 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
1312
1413 const strs = [_][*]const u8{ "one", "two", "three" };
1514 argv = &strs;
test/behavior/enum.zig-6
......@@ -606,7 +606,6 @@ fn testEnumWithSpecifiedTagValues(x: MultipleChoice) !void {
606606}
607607
608608test "enum with specified tag values" {
609 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
610609 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
611610
612611 try testEnumWithSpecifiedTagValues(MultipleChoice.C);
......@@ -614,7 +613,6 @@ test "enum with specified tag values" {
614613}
615614
616615test "non-exhaustive enum" {
617 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
618616 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
619617
620618 const S = struct {
......@@ -677,7 +675,6 @@ test "empty non-exhaustive enum" {
677675}
678676
679677test "single field non-exhaustive enum" {
680 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
681678 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
682679
683680 const S = struct {
......@@ -741,7 +738,6 @@ test "cast integer literal to enum" {
741738}
742739
743740test "enum with specified and unspecified tag values" {
744 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
745741 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
746742
747743 try testEnumWithSpecifiedAndUnspecifiedTagValues(MultipleChoice2.D);
......@@ -925,7 +921,6 @@ test "enum literal casting to tagged union" {
925921const Bar = enum { A, B, C, D };
926922
927923test "enum literal casting to error union with payload enum" {
928 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
929924 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
930925
931926 var bar: error{B}!Bar = undefined;
......@@ -1132,7 +1127,6 @@ test "tag name functions are unique" {
11321127
11331128test "size of enum with only one tag which has explicit integer tag type" {
11341129 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1135 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
11361130 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
11371131
11381132 const E = enum(u8) { nope = 10 };
test/behavior/error.zig-4
......@@ -222,7 +222,6 @@ fn testErrorSetType() !void {
222222
223223test "explicit error set cast" {
224224 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
225 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
226225 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
227226
228227 try testExplicitErrorSetCast(Set1.A);
......@@ -282,7 +281,6 @@ test "inferred empty error set comptime catch" {
282281}
283282
284283test "error union peer type resolution" {
285 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
286284 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
287285
288286 try testErrorUnionPeerTypeResolution(1);
......@@ -327,7 +325,6 @@ fn foo3(b: usize) Error!usize {
327325
328326test "error: Infer error set from literals" {
329327 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
330 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
331328
332329 _ = nullLiteral("n") catch |err| handleErrors(err);
333330 _ = floatLiteral("n") catch |err| handleErrors(err);
......@@ -700,7 +697,6 @@ test "ret_ptr doesn't cause own inferred error set to be resolved" {
700697
701698test "simple else prong allowed even when all errors handled" {
702699 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
703 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
704700 if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
705701
706702 const S = struct {
test/behavior/eval.zig-10
......@@ -69,7 +69,6 @@ fn constExprEvalOnSingleExprBlocksFn(x: i32, b: bool) i32 {
6969}
7070
7171test "constant expressions" {
72 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
7372 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
7473
7574 var array: [array_size]u8 = undefined;
......@@ -138,7 +137,6 @@ test "pointer to type" {
138137test "a type constructed in a global expression" {
139138 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
140139 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
141 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
142140
143141 var l: List = undefined;
144142 l.array[0] = 10;
......@@ -338,7 +336,6 @@ fn doesAlotT(comptime T: type, value: usize) T {
338336}
339337
340338test "@setEvalBranchQuota at same scope as generic function call" {
341 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
342339 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
343340
344341 try expect(doesAlotT(u32, 2) == 2);
......@@ -565,7 +562,6 @@ test "inlined loop has array literal with elided runtime scope on first iteratio
565562}
566563
567564test "ptr to local array argument at comptime" {
568 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
569565 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
570566
571567 comptime {
......@@ -806,7 +802,6 @@ test "array concatenation sets the sentinel - value" {
806802test "array concatenation sets the sentinel - pointer" {
807803 if (builtin.zig_backend == .stage1) return error.SkipZigTest;
808804 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
809 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
810805 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
811806
812807 var a = [2]u3{ 1, 7 };
......@@ -956,7 +951,6 @@ test "const local with comptime init through array init" {
956951
957952test "closure capture type of runtime-known parameter" {
958953 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
959 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
960954
961955 const S = struct {
962956 fn b(c: anytype) !void {
......@@ -1074,7 +1068,6 @@ test "comptime break operand passing through runtime switch converted to runtime
10741068
10751069test "no dependency loop for alignment of self struct" {
10761070 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1077 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
10781071
10791072 const S = struct {
10801073 fn doTheTest() !void {
......@@ -1111,7 +1104,6 @@ test "no dependency loop for alignment of self struct" {
11111104
11121105test "no dependency loop for alignment of self bare union" {
11131106 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1114 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11151107
11161108 const S = struct {
11171109 fn doTheTest() !void {
......@@ -1148,7 +1140,6 @@ test "no dependency loop for alignment of self bare union" {
11481140
11491141test "no dependency loop for alignment of self tagged union" {
11501142 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1151 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
11521143
11531144 const S = struct {
11541145 fn doTheTest() !void {
......@@ -1336,7 +1327,6 @@ test "lazy sizeof is resolved in division" {
13361327}
13371328
13381329test "lazy value is resolved as slice operand" {
1339 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13401330 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
13411331
13421332 const A = struct { a: u32 };
test/behavior/field_parent_ptr.zig-2
......@@ -2,7 +2,6 @@ const expect = @import("std").testing.expect;
22const builtin = @import("builtin");
33
44test "@fieldParentPtr non-first field" {
5 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
65 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
87 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
......@@ -11,7 +10,6 @@ test "@fieldParentPtr non-first field" {
1110}
1211
1312test "@fieldParentPtr first field" {
14 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1513 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1614 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1715 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
test/behavior/for.zig-4
......@@ -5,7 +5,6 @@ const expectEqual = std.testing.expectEqual;
55const mem = std.mem;
66
77test "continue in for loop" {
8 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
98 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
109
1110 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" {
130129}
131130
132131test "2 break statements and an else" {
133 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
134132 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
135133
136134 const S = struct {
......@@ -177,7 +175,6 @@ fn mangleString(s: []u8) void {
177175}
178176
179177test "for copies its payload" {
180 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
181178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
182179
183180 const S = struct {
......@@ -213,7 +210,6 @@ test "for on slice with allowzero ptr" {
213210
214211test "else continue outer for" {
215212 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
216 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
217213
218214 var i: usize = 6;
219215 var buf: [5]u8 = undefined;
test/behavior/generics.zig-1
......@@ -91,7 +91,6 @@ fn max_f64(a: f64, b: f64) f64 {
9191
9292test "type constructed by comptime function call" {
9393 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
94 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9594 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9695
9796 var l: SimpleList(10) = undefined;
test/behavior/merge_error_sets.zig-1
......@@ -12,7 +12,6 @@ fn foo() C!void {
1212}
1313
1414test "merge error sets" {
15 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1615 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
1716 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
1817 if (foo()) {
test/behavior/pointers.zig-5
......@@ -18,7 +18,6 @@ fn testDerefPtr() !void {
1818
1919test "pointer arithmetic" {
2020 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2221 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
2322
2423 var ptr: [*]const u8 = "abcd";
......@@ -66,7 +65,6 @@ test "initialize const optional C pointer to null" {
6665
6766test "assigning integer to C pointer" {
6867 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
69 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
7068 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
7169
7270 var x: i32 = 0;
......@@ -281,7 +279,6 @@ test "array initialization types" {
281279
282280test "null terminated pointer" {
283281 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
285282 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
286283
287284 const S = struct {
......@@ -299,7 +296,6 @@ test "null terminated pointer" {
299296
300297test "allow any sentinel" {
301298 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
302 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
303299 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
304300
305301 const S = struct {
......@@ -315,7 +311,6 @@ test "allow any sentinel" {
315311
316312test "pointer sentinel with enums" {
317313 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
318 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
319314 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
320315
321316 const S = struct {
test/behavior/ptrcast.zig-3
......@@ -4,7 +4,6 @@ const expect = std.testing.expect;
44const native_endian = builtin.target.cpu.arch.endian();
55
66test "reinterpret bytes as integer with nonzero offset" {
7 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
87 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
98
109 try testReinterpretBytesAsInteger();
......@@ -39,7 +38,6 @@ fn testReinterpretWithOffsetAndNoWellDefinedLayout() !void {
3938}
4039
4140test "reinterpret bytes inside auto-layout struct as integer with nonzero offset" {
42 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
4341 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
4442
4543 try testReinterpretStructWrappedBytesAsInteger();
......@@ -179,7 +177,6 @@ test "lower reinterpreted comptime field ptr" {
179177}
180178
181179test "reinterpret struct field at comptime" {
182 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
183180 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
184181
185182 const numNative = comptime Bytes.init(0x12345678);
test/behavior/sizeof_and_typeof.zig-2
......@@ -18,7 +18,6 @@ test "@sizeOf on compile-time types" {
1818}
1919
2020test "@TypeOf() with multiple arguments" {
21 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2221 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
2322 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
2423 {
......@@ -77,7 +76,6 @@ const P = packed struct {
7776};
7877
7978test "@offsetOf" {
80 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
8179 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
8280
8381 // Packed structs have fixed memory layout
test/behavior/slice.zig-3
......@@ -28,7 +28,6 @@ comptime {
2828
2929test "slicing" {
3030 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
31 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
3231
3332 var array: [20]i32 = undefined;
3433
......@@ -269,7 +268,6 @@ fn sliceSum(comptime q: []const u8) i32 {
269268
270269test "slice type with custom alignment" {
271270 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
272 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
273271
274272 const LazilyResolvedType = struct {
275273 anything: i32,
......@@ -283,7 +281,6 @@ test "slice type with custom alignment" {
283281
284282test "obtaining a null terminated slice" {
285283 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
286 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
287284
288285 // here we have a normal array
289286 var buf: [50]u8 = undefined;
test/behavior/struct.zig-8
......@@ -10,7 +10,6 @@ top_level_field: i32,
1010
1111test "top level fields" {
1212 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
13 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
1413
1514 var instance = @This(){
1615 .top_level_field = 1234,
......@@ -104,7 +103,6 @@ fn testMutation(foo: *StructFoo) void {
104103
105104test "struct byval assign" {
106105 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
107 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
108106
109107 var foo1: StructFoo = undefined;
110108 var foo2: StructFoo = undefined;
......@@ -240,7 +238,6 @@ test "usingnamespace within struct scope" {
240238
241239test "struct field init with catch" {
242240 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
243 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
244241
245242 const S = struct {
246243 fn doTheTest() !void {
......@@ -281,7 +278,6 @@ const Val = struct {
281278test "struct point to self" {
282279 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
283280 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
284 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
285281
286282 var root: Node = undefined;
287283 root.val.x = 1;
......@@ -297,7 +293,6 @@ test "struct point to self" {
297293
298294test "void struct fields" {
299295 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
300 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
301296
302297 const foo = VoidStructFieldsFoo{
303298 .a = void{},
......@@ -761,7 +756,6 @@ test "packed struct with u0 field access" {
761756}
762757
763758test "access to global struct fields" {
764 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
765759 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
766760 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
767761
......@@ -1260,7 +1254,6 @@ test "typed init through error unions and optionals" {
12601254
12611255test "initialize struct with empty literal" {
12621256 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1263 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
12641257
12651258 const S = struct { x: i32 = 1234 };
12661259 var s: S = .{};
......@@ -1362,7 +1355,6 @@ test "store to comptime field" {
13621355
13631356test "struct field init value is size of the struct" {
13641357 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
1365 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
13661358
13671359 const namespace = struct {
13681360 const S = extern struct {
test/behavior/switch.zig-2
......@@ -348,7 +348,6 @@ test "switch on const enum with var" {
348348}
349349
350350test "anon enum literal used in switch on union enum" {
351 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
352351 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
353352
354353 const Foo = union(enum) {
......@@ -490,7 +489,6 @@ test "switch prongs with error set cases make a new error set type for capture v
490489}
491490
492491test "return result loc and then switch with range implicit casted to error union" {
493 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
494492 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
495493
496494 const S = struct {
test/behavior/this.zig-1
......@@ -25,7 +25,6 @@ test "this refer to module call private fn" {
2525}
2626
2727test "this refer to container" {
28 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
2928 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
3029
3130 var pt: Point(i32) = undefined;
test/behavior/try.zig-1
......@@ -3,7 +3,6 @@ const builtin = @import("builtin");
33const expect = std.testing.expect;
44
55test "try on error union" {
6 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
76 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
87
98 try tryOnErrorUnionImpl();
test/behavior/union.zig-1
......@@ -92,7 +92,6 @@ const FooExtern = extern union {
9292};
9393
9494test "basic extern unions" {
95 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
9695 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
9796
9897 var foo = FooExtern{ .int = 1 };
test/behavior/usingnamespace.zig-1
......@@ -58,7 +58,6 @@ test "two files usingnamespace import each other" {
5858}
5959
6060test {
61 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
6261 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
6362
6463 const AA = struct {
test/behavior/while.zig-1
......@@ -175,7 +175,6 @@ test "while with optional as condition with else" {
175175
176176test "while with error union condition" {
177177 if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest;
178 if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest;
179178 if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest;
180179
181180 numbers_left = 10;