| ... | ... | @@ -91,7 +91,7 @@ register_manager: RegisterManager = .{}, |
| 91 | 91 | /// Maps offset to what is stored there. |
| 92 | 92 | stack: std.AutoHashMapUnmanaged(u32, StackAllocation) = .{}, |
| 93 | 93 | /// Tracks the current instruction allocated to the compare flags |
| 94 | | condition_flags_inst: ?Air.Inst.Index = null, |
| 94 | compare_flags_inst: ?Air.Inst.Index = null, |
| 95 | 95 | |
| 96 | 96 | /// Offset from the stack base, representing the end of the stack frame. |
| 97 | 97 | max_end_stack: u32 = 0, |
| ... | ... | @@ -154,7 +154,7 @@ const MCValue = union(enum) { |
| 154 | 154 | /// The value resides in the N, Z, C, V flags. The value is 1 (if |
| 155 | 155 | /// the type is u1) or true (if the type in bool) iff the |
| 156 | 156 | /// specified condition is true. |
| 157 | | condition_flags: Condition, |
| 157 | compare_flags: Condition, |
| 158 | 158 | /// The value is a function argument passed via the stack. |
| 159 | 159 | stack_argument_offset: u32, |
| 160 | 160 | }; |
| ... | ... | @@ -201,6 +201,29 @@ const BigTomb = struct { |
| 201 | 201 | log.debug("%{d} => {}", .{ bt.inst, result }); |
| 202 | 202 | const branch = &bt.function.branch_stack.items[bt.function.branch_stack.items.len - 1]; |
| 203 | 203 | branch.inst_table.putAssumeCapacityNoClobber(bt.inst, result); |
| 204 | |
| 205 | switch (result) { |
| 206 | .register => |reg| { |
| 207 | // In some cases (such as bitcast), an operand |
| 208 | // may be the same MCValue as the result. If |
| 209 | // that operand died and was a register, it |
| 210 | // was freed by processDeath. We have to |
| 211 | // "re-allocate" the register. |
| 212 | if (bt.function.register_manager.isRegFree(reg)) { |
| 213 | bt.function.register_manager.getRegAssumeFree(reg, bt.inst); |
| 214 | } |
| 215 | }, |
| 216 | .register_with_overflow => |rwo| { |
| 217 | if (bt.function.register_manager.isRegFree(rwo.reg)) { |
| 218 | bt.function.register_manager.getRegAssumeFree(rwo.reg, bt.inst); |
| 219 | } |
| 220 | bt.function.compare_flags_inst = bt.inst; |
| 221 | }, |
| 222 | .compare_flags => |_| { |
| 223 | bt.function.compare_flags_inst = bt.inst; |
| 224 | }, |
| 225 | else => {}, |
| 226 | } |
| 204 | 227 | } |
| 205 | 228 | bt.function.finishAirBookkeeping(); |
| 206 | 229 | } |
| ... | ... | @@ -539,8 +562,8 @@ fn genBody(self: *Self, body: []const Air.Inst.Index) InnerError!void { |
| 539 | 562 | .ptr_add => try self.airPtrArithmetic(inst, .ptr_add), |
| 540 | 563 | .ptr_sub => try self.airPtrArithmetic(inst, .ptr_sub), |
| 541 | 564 | |
| 542 | | .min => try self.airMin(inst), |
| 543 | | .max => try self.airMax(inst), |
| 565 | .min => try self.airMinMax(inst), |
| 566 | .max => try self.airMinMax(inst), |
| 544 | 567 | |
| 545 | 568 | .add_sat => try self.airAddSat(inst), |
| 546 | 569 | .sub_sat => try self.airSubSat(inst), |
| ... | ... | @@ -764,10 +787,10 @@ fn processDeath(self: *Self, inst: Air.Inst.Index) void { |
| 764 | 787 | }, |
| 765 | 788 | .register_with_overflow => |rwo| { |
| 766 | 789 | self.register_manager.freeReg(rwo.reg); |
| 767 | | self.condition_flags_inst = null; |
| 790 | self.compare_flags_inst = null; |
| 768 | 791 | }, |
| 769 | | .condition_flags => { |
| 770 | | self.condition_flags_inst = null; |
| 792 | .compare_flags => { |
| 793 | self.compare_flags_inst = null; |
| 771 | 794 | }, |
| 772 | 795 | else => {}, // TODO process stack allocation death |
| 773 | 796 | } |
| ... | ... | @@ -808,6 +831,15 @@ fn finishAir(self: *Self, inst: Air.Inst.Index, result: MCValue, operands: [Live |
| 808 | 831 | self.register_manager.getRegAssumeFree(reg, inst); |
| 809 | 832 | } |
| 810 | 833 | }, |
| 834 | .register_with_overflow => |rwo| { |
| 835 | if (self.register_manager.isRegFree(rwo.reg)) { |
| 836 | self.register_manager.getRegAssumeFree(rwo.reg, inst); |
| 837 | } |
| 838 | self.compare_flags_inst = inst; |
| 839 | }, |
| 840 | .compare_flags => |_| { |
| 841 | self.compare_flags_inst = inst; |
| 842 | }, |
| 811 | 843 | else => {}, |
| 812 | 844 | } |
| 813 | 845 | } |
| ... | ... | @@ -931,11 +963,11 @@ pub fn spillInstruction(self: *Self, reg: Register, inst: Air.Inst.Index) !void |
| 931 | 963 | /// Save the current instruction stored in the compare flags if |
| 932 | 964 | /// occupied |
| 933 | 965 | fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 934 | | if (self.condition_flags_inst) |inst_to_save| { |
| 966 | if (self.compare_flags_inst) |inst_to_save| { |
| 935 | 967 | const ty = self.air.typeOfIndex(inst_to_save); |
| 936 | 968 | const mcv = self.getResolvedInstValue(inst_to_save); |
| 937 | 969 | const new_mcv = switch (mcv) { |
| 938 | | .condition_flags => try self.allocRegOrMem(ty, true, inst_to_save), |
| 970 | .compare_flags => try self.allocRegOrMem(ty, true, inst_to_save), |
| 939 | 971 | .register_with_overflow => try self.allocRegOrMem(ty, false, inst_to_save), |
| 940 | 972 | else => unreachable, // mcv doesn't occupy the compare flags |
| 941 | 973 | }; |
| ... | ... | @@ -946,7 +978,7 @@ fn spillCompareFlagsIfOccupied(self: *Self) !void { |
| 946 | 978 | const branch = &self.branch_stack.items[self.branch_stack.items.len - 1]; |
| 947 | 979 | try branch.inst_table.put(self.gpa, inst_to_save, new_mcv); |
| 948 | 980 | |
| 949 | | self.condition_flags_inst = null; |
| 981 | self.compare_flags_inst = null; |
| 950 | 982 | |
| 951 | 983 | // TODO consolidate with register manager and spillInstruction |
| 952 | 984 | // this call should really belong in the register manager! |
| ... | ... | @@ -984,8 +1016,27 @@ fn airAlloc(self: *Self, inst: Air.Inst.Index) !void { |
| 984 | 1016 | } |
| 985 | 1017 | |
| 986 | 1018 | fn airRetPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 987 | | const stack_offset = try self.allocMemPtr(inst); |
| 988 | | return self.finishAir(inst, .{ .ptr_stack_offset = stack_offset }, .{ .none, .none, .none }); |
| 1019 | const result: MCValue = switch (self.ret_mcv) { |
| 1020 | .none, .register => .{ .ptr_stack_offset = try self.allocMemPtr(inst) }, |
| 1021 | .stack_offset => blk: { |
| 1022 | // self.ret_mcv is an address to where this function |
| 1023 | // should store its result into |
| 1024 | const ret_ty = self.fn_type.fnReturnType(); |
| 1025 | var ptr_ty_payload: Type.Payload.ElemType = .{ |
| 1026 | .base = .{ .tag = .single_mut_pointer }, |
| 1027 | .data = ret_ty, |
| 1028 | }; |
| 1029 | const ptr_ty = Type.initPayload(&ptr_ty_payload.base); |
| 1030 | |
| 1031 | // addr_reg will contain the address of where to store the |
| 1032 | // result into |
| 1033 | const addr_reg = try self.copyToTmpRegister(ptr_ty, self.ret_mcv); |
| 1034 | break :blk .{ .register = addr_reg }; |
| 1035 | }, |
| 1036 | else => unreachable, // invalid return result |
| 1037 | }; |
| 1038 | |
| 1039 | return self.finishAir(inst, result, .{ .none, .none, .none }); |
| 989 | 1040 | } |
| 990 | 1041 | |
| 991 | 1042 | fn airFptrunc(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1155,7 +1206,7 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1155 | 1206 | switch (operand) { |
| 1156 | 1207 | .dead => unreachable, |
| 1157 | 1208 | .unreach => unreachable, |
| 1158 | | .condition_flags => |cond| break :result MCValue{ .condition_flags = cond.negate() }, |
| 1209 | .compare_flags => |cond| break :result MCValue{ .compare_flags = cond.negate() }, |
| 1159 | 1210 | else => { |
| 1160 | 1211 | switch (operand_ty.zigTypeTag()) { |
| 1161 | 1212 | .Bool => { |
| ... | ... | @@ -1234,15 +1285,102 @@ fn airNot(self: *Self, inst: Air.Inst.Index) !void { |
| 1234 | 1285 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1235 | 1286 | } |
| 1236 | 1287 | |
| 1237 | | fn airMin(self: *Self, inst: Air.Inst.Index) !void { |
| 1238 | | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1239 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement min for {}", .{self.target.cpu.arch}); |
| 1240 | | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1288 | fn minMax( |
| 1289 | self: *Self, |
| 1290 | tag: Air.Inst.Tag, |
| 1291 | lhs_bind: ReadArg.Bind, |
| 1292 | rhs_bind: ReadArg.Bind, |
| 1293 | lhs_ty: Type, |
| 1294 | rhs_ty: Type, |
| 1295 | maybe_inst: ?Air.Inst.Index, |
| 1296 | ) !MCValue { |
| 1297 | switch (lhs_ty.zigTypeTag()) { |
| 1298 | .Float => return self.fail("TODO ARM min/max on floats", .{}), |
| 1299 | .Vector => return self.fail("TODO ARM min/max on vectors", .{}), |
| 1300 | .Int => { |
| 1301 | const mod = self.bin_file.options.module.?; |
| 1302 | assert(lhs_ty.eql(rhs_ty, mod)); |
| 1303 | const int_info = lhs_ty.intInfo(self.target.*); |
| 1304 | if (int_info.bits <= 64) { |
| 1305 | var lhs_reg: Register = undefined; |
| 1306 | var rhs_reg: Register = undefined; |
| 1307 | var dest_reg: Register = undefined; |
| 1308 | |
| 1309 | const read_args = [_]ReadArg{ |
| 1310 | .{ .ty = lhs_ty, .bind = lhs_bind, .class = gp, .reg = &lhs_reg }, |
| 1311 | .{ .ty = rhs_ty, .bind = rhs_bind, .class = gp, .reg = &rhs_reg }, |
| 1312 | }; |
| 1313 | const write_args = [_]WriteArg{ |
| 1314 | .{ .ty = lhs_ty, .bind = .none, .class = gp, .reg = &dest_reg }, |
| 1315 | }; |
| 1316 | try self.allocRegs( |
| 1317 | &read_args, |
| 1318 | &write_args, |
| 1319 | if (maybe_inst) |inst| .{ |
| 1320 | .corresponding_inst = inst, |
| 1321 | .operand_mapping = &.{ 0, 1 }, |
| 1322 | } else null, |
| 1323 | ); |
| 1324 | |
| 1325 | // lhs == reg should have been checked by airMinMax |
| 1326 | assert(lhs_reg != rhs_reg); // see note above |
| 1327 | |
| 1328 | _ = try self.addInst(.{ |
| 1329 | .tag = .cmp_shifted_register, |
| 1330 | .data = .{ .rr_imm6_shift = .{ |
| 1331 | .rn = lhs_reg, |
| 1332 | .rm = rhs_reg, |
| 1333 | .imm6 = 0, |
| 1334 | .shift = .lsl, |
| 1335 | } }, |
| 1336 | }); |
| 1337 | |
| 1338 | const cond_choose_lhs: Condition = switch (tag) { |
| 1339 | .max => switch (int_info.signedness) { |
| 1340 | .signed => Condition.gt, |
| 1341 | .unsigned => Condition.hi, |
| 1342 | }, |
| 1343 | .min => switch (int_info.signedness) { |
| 1344 | .signed => Condition.lt, |
| 1345 | .unsigned => Condition.cc, |
| 1346 | }, |
| 1347 | else => unreachable, |
| 1348 | }; |
| 1349 | |
| 1350 | _ = try self.addInst(.{ |
| 1351 | .tag = .csel, |
| 1352 | .data = .{ .rrr_cond = .{ |
| 1353 | .rd = dest_reg, |
| 1354 | .rn = lhs_reg, |
| 1355 | .rm = rhs_reg, |
| 1356 | .cond = cond_choose_lhs, |
| 1357 | } }, |
| 1358 | }); |
| 1359 | |
| 1360 | return MCValue{ .register = dest_reg }; |
| 1361 | } else { |
| 1362 | return self.fail("TODO ARM min/max on integers > u32/i32", .{}); |
| 1363 | } |
| 1364 | }, |
| 1365 | else => unreachable, |
| 1366 | } |
| 1241 | 1367 | } |
| 1242 | 1368 | |
| 1243 | | fn airMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1369 | fn airMinMax(self: *Self, inst: Air.Inst.Index) !void { |
| 1370 | const tag = self.air.instructions.items(.tag)[inst]; |
| 1244 | 1371 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1245 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement max for {}", .{self.target.cpu.arch}); |
| 1372 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| 1373 | const rhs_ty = self.air.typeOf(bin_op.rhs); |
| 1374 | |
| 1375 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1376 | const lhs_bind: ReadArg.Bind = .{ .inst = bin_op.lhs }; |
| 1377 | const rhs_bind: ReadArg.Bind = .{ .inst = bin_op.rhs }; |
| 1378 | |
| 1379 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1380 | if (bin_op.lhs == bin_op.rhs) break :result lhs; |
| 1381 | |
| 1382 | break :result try self.minMax(tag, lhs_bind, rhs_bind, lhs_ty, rhs_ty, inst); |
| 1383 | }; |
| 1246 | 1384 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1247 | 1385 | } |
| 1248 | 1386 | |
| ... | ... | @@ -1477,9 +1615,9 @@ fn allocRegs( |
| 1477 | 1615 | // If the previous MCValue occupied some space we track, we |
| 1478 | 1616 | // need to make sure it is marked as free now. |
| 1479 | 1617 | switch (mcv) { |
| 1480 | | .condition_flags => { |
| 1481 | | assert(self.condition_flags_inst.? == inst); |
| 1482 | | self.condition_flags_inst = null; |
| 1618 | .compare_flags => { |
| 1619 | assert(self.compare_flags_inst.? == inst); |
| 1620 | self.compare_flags_inst = null; |
| 1483 | 1621 | }, |
| 1484 | 1622 | .register => |prev_reg| { |
| 1485 | 1623 | assert(!self.register_manager.isRegFree(prev_reg)); |
| ... | ... | @@ -2276,7 +2414,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2276 | 2414 | const stack_offset = try self.allocMem(tuple_size, tuple_align, inst); |
| 2277 | 2415 | |
| 2278 | 2416 | try self.spillCompareFlagsIfOccupied(); |
| 2279 | | self.condition_flags_inst = null; |
| 2417 | self.compare_flags_inst = null; |
| 2280 | 2418 | |
| 2281 | 2419 | const base_tag: Air.Inst.Tag = switch (tag) { |
| 2282 | 2420 | .add_with_overflow => .add, |
| ... | ... | @@ -2308,7 +2446,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2308 | 2446 | }); |
| 2309 | 2447 | |
| 2310 | 2448 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2311 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2449 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); |
| 2312 | 2450 | |
| 2313 | 2451 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2314 | 2452 | }, |
| ... | ... | @@ -2343,7 +2481,7 @@ fn airOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2343 | 2481 | }; |
| 2344 | 2482 | |
| 2345 | 2483 | try self.spillCompareFlagsIfOccupied(); |
| 2346 | | self.condition_flags_inst = inst; |
| 2484 | self.compare_flags_inst = inst; |
| 2347 | 2485 | |
| 2348 | 2486 | const dest = blk: { |
| 2349 | 2487 | if (rhs_immediate_ok) { |
| ... | ... | @@ -2452,7 +2590,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2452 | 2590 | } |
| 2453 | 2591 | |
| 2454 | 2592 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2455 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2593 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); |
| 2456 | 2594 | |
| 2457 | 2595 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2458 | 2596 | } else if (int_info.bits <= 64) { |
| ... | ... | @@ -2592,7 +2730,7 @@ fn airMulWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2592 | 2730 | try self.truncRegister(dest_reg, truncated_reg, int_info.signedness, int_info.bits); |
| 2593 | 2731 | |
| 2594 | 2732 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = truncated_reg }); |
| 2595 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2733 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); |
| 2596 | 2734 | |
| 2597 | 2735 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2598 | 2736 | } else return self.fail("TODO implement mul_with_overflow for integers > u64/i64", .{}); |
| ... | ... | @@ -2724,7 +2862,7 @@ fn airShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 2724 | 2862 | }); |
| 2725 | 2863 | |
| 2726 | 2864 | try self.genSetStack(lhs_ty, stack_offset, .{ .register = dest_reg }); |
| 2727 | | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .condition_flags = .ne }); |
| 2865 | try self.genSetStack(Type.initTag(.u1), stack_offset - overflow_bit_offset, .{ .compare_flags = .ne }); |
| 2728 | 2866 | |
| 2729 | 2867 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2730 | 2868 | } else { |
| ... | ... | @@ -2890,7 +3028,23 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2890 | 3028 | /// T to E!T |
| 2891 | 3029 | fn airWrapErrUnionPayload(self: *Self, inst: Air.Inst.Index) !void { |
| 2892 | 3030 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2893 | | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else return self.fail("TODO implement wrap errunion payload for {}", .{self.target.cpu.arch}); |
| 3031 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 3032 | const error_union_ty = self.air.getRefType(ty_op.ty); |
| 3033 | const error_ty = error_union_ty.errorUnionSet(); |
| 3034 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 3035 | const operand = try self.resolveInst(ty_op.operand); |
| 3036 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; |
| 3037 | |
| 3038 | const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 3039 | const abi_align = error_union_ty.abiAlignment(self.target.*); |
| 3040 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); |
| 3041 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 3042 | const err_off = errUnionErrorOffset(payload_ty, self.target.*); |
| 3043 | try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), operand); |
| 3044 | try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), .{ .immediate = 0 }); |
| 3045 | |
| 3046 | break :result MCValue{ .stack_offset = stack_offset }; |
| 3047 | }; |
| 2894 | 3048 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2895 | 3049 | } |
| 2896 | 3050 | |
| ... | ... | @@ -2899,11 +3053,20 @@ fn airWrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2899 | 3053 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 2900 | 3054 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2901 | 3055 | const error_union_ty = self.air.getRefType(ty_op.ty); |
| 3056 | const error_ty = error_union_ty.errorUnionSet(); |
| 2902 | 3057 | const payload_ty = error_union_ty.errorUnionPayload(); |
| 2903 | | const mcv = try self.resolveInst(ty_op.operand); |
| 2904 | | if (!payload_ty.hasRuntimeBits()) break :result mcv; |
| 3058 | const operand = try self.resolveInst(ty_op.operand); |
| 3059 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result operand; |
| 2905 | 3060 | |
| 2906 | | return self.fail("TODO implement wrap errunion error for non-empty payloads", .{}); |
| 3061 | const abi_size = @intCast(u32, error_union_ty.abiSize(self.target.*)); |
| 3062 | const abi_align = error_union_ty.abiAlignment(self.target.*); |
| 3063 | const stack_offset = try self.allocMem(abi_size, abi_align, inst); |
| 3064 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 3065 | const err_off = errUnionErrorOffset(payload_ty, self.target.*); |
| 3066 | try self.genSetStack(error_ty, stack_offset - @intCast(u32, err_off), operand); |
| 3067 | try self.genSetStack(payload_ty, stack_offset - @intCast(u32, payload_off), .undef); |
| 3068 | |
| 3069 | break :result MCValue{ .stack_offset = stack_offset }; |
| 2907 | 3070 | }; |
| 2908 | 3071 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 2909 | 3072 | } |
| ... | ... | @@ -3175,7 +3338,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 3175 | 3338 | .undef => unreachable, |
| 3176 | 3339 | .unreach => unreachable, |
| 3177 | 3340 | .dead => unreachable, |
| 3178 | | .condition_flags, |
| 3341 | .compare_flags, |
| 3179 | 3342 | .register_with_overflow, |
| 3180 | 3343 | => unreachable, // cannot hold an address |
| 3181 | 3344 | .immediate => |imm| try self.setRegOrMem(elem_ty, dst_mcv, .{ .memory = imm }), |
| ... | ... | @@ -3187,7 +3350,7 @@ fn load(self: *Self, dst_mcv: MCValue, ptr: MCValue, ptr_ty: Type) InnerError!vo |
| 3187 | 3350 | switch (dst_mcv) { |
| 3188 | 3351 | .dead => unreachable, |
| 3189 | 3352 | .undef => unreachable, |
| 3190 | | .condition_flags => unreachable, |
| 3353 | .compare_flags => unreachable, |
| 3191 | 3354 | .register => |dst_reg| { |
| 3192 | 3355 | try self.genLdrRegister(dst_reg, addr_reg, elem_ty); |
| 3193 | 3356 | }, |
| ... | ... | @@ -3315,6 +3478,104 @@ fn genInlineMemcpy( |
| 3315 | 3478 | // end: |
| 3316 | 3479 | } |
| 3317 | 3480 | |
| 3481 | fn genInlineMemset( |
| 3482 | self: *Self, |
| 3483 | dst: MCValue, |
| 3484 | val: MCValue, |
| 3485 | len: MCValue, |
| 3486 | ) !void { |
| 3487 | const dst_reg = switch (dst) { |
| 3488 | .register => |r| r, |
| 3489 | else => try self.copyToTmpRegister(Type.initTag(.manyptr_u8), dst), |
| 3490 | }; |
| 3491 | const dst_reg_lock = self.register_manager.lockReg(dst_reg); |
| 3492 | defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3493 | |
| 3494 | const val_reg = switch (val) { |
| 3495 | .register => |r| r, |
| 3496 | else => try self.copyToTmpRegister(Type.initTag(.u8), val), |
| 3497 | }; |
| 3498 | const val_reg_lock = self.register_manager.lockReg(val_reg); |
| 3499 | defer if (val_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3500 | |
| 3501 | const len_reg = switch (len) { |
| 3502 | .register => |r| r, |
| 3503 | else => try self.copyToTmpRegister(Type.usize, len), |
| 3504 | }; |
| 3505 | const len_reg_lock = self.register_manager.lockReg(len_reg); |
| 3506 | defer if (len_reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3507 | |
| 3508 | const count_reg = try self.register_manager.allocReg(null, gp); |
| 3509 | |
| 3510 | try self.genInlineMemsetCode(dst_reg, val_reg, len_reg, count_reg); |
| 3511 | } |
| 3512 | |
| 3513 | fn genInlineMemsetCode( |
| 3514 | self: *Self, |
| 3515 | dst: Register, |
| 3516 | val: Register, |
| 3517 | len: Register, |
| 3518 | count: Register, |
| 3519 | ) !void { |
| 3520 | // mov count, #0 |
| 3521 | _ = try self.addInst(.{ |
| 3522 | .tag = .movz, |
| 3523 | .data = .{ .r_imm16_sh = .{ |
| 3524 | .rd = count, |
| 3525 | .imm16 = 0, |
| 3526 | } }, |
| 3527 | }); |
| 3528 | |
| 3529 | // loop: |
| 3530 | // cmp count, len |
| 3531 | _ = try self.addInst(.{ |
| 3532 | .tag = .cmp_shifted_register, |
| 3533 | .data = .{ .rr_imm6_shift = .{ |
| 3534 | .rn = count, |
| 3535 | .rm = len, |
| 3536 | .imm6 = 0, |
| 3537 | .shift = .lsl, |
| 3538 | } }, |
| 3539 | }); |
| 3540 | |
| 3541 | // bge end |
| 3542 | _ = try self.addInst(.{ |
| 3543 | .tag = .b_cond, |
| 3544 | .data = .{ .inst_cond = .{ |
| 3545 | .inst = @intCast(u32, self.mir_instructions.len + 4), |
| 3546 | .cond = .ge, |
| 3547 | } }, |
| 3548 | }); |
| 3549 | |
| 3550 | // strb val, [src, count] |
| 3551 | _ = try self.addInst(.{ |
| 3552 | .tag = .strb_register, |
| 3553 | .data = .{ .load_store_register_register = .{ |
| 3554 | .rt = val, |
| 3555 | .rn = dst, |
| 3556 | .offset = Instruction.LoadStoreOffset.reg(count).register, |
| 3557 | } }, |
| 3558 | }); |
| 3559 | |
| 3560 | // add count, count, #1 |
| 3561 | _ = try self.addInst(.{ |
| 3562 | .tag = .add_immediate, |
| 3563 | .data = .{ .rr_imm12_sh = .{ |
| 3564 | .rd = count, |
| 3565 | .rn = count, |
| 3566 | .imm12 = 1, |
| 3567 | } }, |
| 3568 | }); |
| 3569 | |
| 3570 | // b loop |
| 3571 | _ = try self.addInst(.{ |
| 3572 | .tag = .b, |
| 3573 | .data = .{ .inst = @intCast(u32, self.mir_instructions.len - 4) }, |
| 3574 | }); |
| 3575 | |
| 3576 | // end: |
| 3577 | } |
| 3578 | |
| 3318 | 3579 | fn airLoad(self: *Self, inst: Air.Inst.Index) !void { |
| 3319 | 3580 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3320 | 3581 | const elem_ty = self.air.typeOfIndex(inst); |
| ... | ... | @@ -3389,6 +3650,7 @@ fn genStrRegister(self: *Self, value_reg: Register, addr_reg: Register, ty: Type |
| 3389 | 3650 | } |
| 3390 | 3651 | |
| 3391 | 3652 | fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type) InnerError!void { |
| 3653 | log.debug("store: storing {} to {}", .{ value, ptr }); |
| 3392 | 3654 | const abi_size = value_ty.abiSize(self.target.*); |
| 3393 | 3655 | |
| 3394 | 3656 | switch (ptr) { |
| ... | ... | @@ -3396,7 +3658,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3396 | 3658 | .undef => unreachable, |
| 3397 | 3659 | .unreach => unreachable, |
| 3398 | 3660 | .dead => unreachable, |
| 3399 | | .condition_flags, |
| 3661 | .compare_flags, |
| 3400 | 3662 | .register_with_overflow, |
| 3401 | 3663 | => unreachable, // cannot hold an address |
| 3402 | 3664 | .immediate => |imm| { |
| ... | ... | @@ -3413,6 +3675,7 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3413 | 3675 | .dead => unreachable, |
| 3414 | 3676 | .undef => unreachable, |
| 3415 | 3677 | .register => |value_reg| { |
| 3678 | log.debug("store: register {} to {}", .{ value_reg, addr_reg }); |
| 3416 | 3679 | try self.genStrRegister(value_reg, addr_reg, value_ty); |
| 3417 | 3680 | }, |
| 3418 | 3681 | else => { |
| ... | ... | @@ -3431,8 +3694,8 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3431 | 3694 | self.register_manager.unlockReg(reg); |
| 3432 | 3695 | }; |
| 3433 | 3696 | |
| 3434 | | const src_reg = addr_reg; |
| 3435 | | const dst_reg = regs[0]; |
| 3697 | const src_reg = regs[0]; |
| 3698 | const dst_reg = addr_reg; |
| 3436 | 3699 | const len_reg = regs[1]; |
| 3437 | 3700 | const count_reg = regs[2]; |
| 3438 | 3701 | const tmp_reg = regs[3]; |
| ... | ... | @@ -3442,7 +3705,6 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3442 | 3705 | // sub src_reg, fp, #off |
| 3443 | 3706 | try self.genSetReg(ptr_ty, src_reg, .{ .ptr_stack_offset = off }); |
| 3444 | 3707 | }, |
| 3445 | | .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), |
| 3446 | 3708 | .stack_argument_offset => |off| { |
| 3447 | 3709 | _ = try self.addInst(.{ |
| 3448 | 3710 | .tag = .ldr_ptr_stack_argument, |
| ... | ... | @@ -3452,6 +3714,24 @@ fn store(self: *Self, ptr: MCValue, value: MCValue, ptr_ty: Type, value_ty: Type |
| 3452 | 3714 | } }, |
| 3453 | 3715 | }); |
| 3454 | 3716 | }, |
| 3717 | .memory => |addr| try self.genSetReg(Type.usize, src_reg, .{ .immediate = @intCast(u32, addr) }), |
| 3718 | .linker_load => |load_struct| { |
| 3719 | const tag: Mir.Inst.Tag = switch (load_struct.@"type") { |
| 3720 | .got => .load_memory_ptr_got, |
| 3721 | .direct => .load_memory_ptr_direct, |
| 3722 | }; |
| 3723 | const mod = self.bin_file.options.module.?; |
| 3724 | _ = try self.addInst(.{ |
| 3725 | .tag = tag, |
| 3726 | .data = .{ |
| 3727 | .payload = try self.addExtra(Mir.LoadMemoryPie{ |
| 3728 | .register = @enumToInt(src_reg), |
| 3729 | .atom_index = mod.declPtr(self.mod_fn.owner_decl).link.macho.sym_index, |
| 3730 | .sym_index = load_struct.sym_index, |
| 3731 | }), |
| 3732 | }, |
| 3733 | }); |
| 3734 | }, |
| 3455 | 3735 | else => return self.fail("TODO store {} to register", .{value}), |
| 3456 | 3736 | } |
| 3457 | 3737 | |
| ... | ... | @@ -3551,7 +3831,7 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3551 | 3831 | 0 => MCValue{ .register = rwo.reg }, |
| 3552 | 3832 | |
| 3553 | 3833 | // get overflow bit: return C or V flag |
| 3554 | | 1 => MCValue{ .condition_flags = rwo.flag }, |
| 3834 | 1 => MCValue{ .compare_flags = rwo.flag }, |
| 3555 | 3835 | |
| 3556 | 3836 | else => unreachable, |
| 3557 | 3837 | }; |
| ... | ... | @@ -4005,8 +4285,8 @@ fn cmp( |
| 4005 | 4285 | } |
| 4006 | 4286 | |
| 4007 | 4287 | return switch (int_info.signedness) { |
| 4008 | | .signed => MCValue{ .condition_flags = Condition.fromCompareOperatorSigned(op) }, |
| 4009 | | .unsigned => MCValue{ .condition_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| 4288 | .signed => MCValue{ .compare_flags = Condition.fromCompareOperatorSigned(op) }, |
| 4289 | .unsigned => MCValue{ .compare_flags = Condition.fromCompareOperatorUnsigned(op) }, |
| 4010 | 4290 | }; |
| 4011 | 4291 | } else { |
| 4012 | 4292 | return self.fail("TODO AArch64 cmp for ints > 64 bits", .{}); |
| ... | ... | @@ -4064,7 +4344,7 @@ fn airDbgVar(self: *Self, inst: Air.Inst.Index) !void { |
| 4064 | 4344 | |
| 4065 | 4345 | fn condBr(self: *Self, condition: MCValue) !Mir.Inst.Index { |
| 4066 | 4346 | switch (condition) { |
| 4067 | | .condition_flags => |cond| return try self.addInst(.{ |
| 4347 | .compare_flags => |cond| return try self.addInst(.{ |
| 4068 | 4348 | .tag = .b_cond, |
| 4069 | 4349 | .data = .{ |
| 4070 | 4350 | .inst_cond = .{ |
| ... | ... | @@ -4120,7 +4400,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4120 | 4400 | var parent_stack = try self.stack.clone(self.gpa); |
| 4121 | 4401 | defer parent_stack.deinit(self.gpa); |
| 4122 | 4402 | const parent_registers = self.register_manager.registers; |
| 4123 | | const parent_condition_flags_inst = self.condition_flags_inst; |
| 4403 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 4124 | 4404 | |
| 4125 | 4405 | try self.branch_stack.append(.{}); |
| 4126 | 4406 | errdefer { |
| ... | ... | @@ -4139,7 +4419,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4139 | 4419 | defer saved_then_branch.deinit(self.gpa); |
| 4140 | 4420 | |
| 4141 | 4421 | self.register_manager.registers = parent_registers; |
| 4142 | | self.condition_flags_inst = parent_condition_flags_inst; |
| 4422 | self.compare_flags_inst = parent_compare_flags_inst; |
| 4143 | 4423 | |
| 4144 | 4424 | self.stack.deinit(self.gpa); |
| 4145 | 4425 | self.stack = parent_stack; |
| ... | ... | @@ -4186,7 +4466,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4186 | 4466 | if (else_value == .dead) |
| 4187 | 4467 | continue; |
| 4188 | 4468 | // The instruction is only overridden in the else branch. |
| 4189 | | var i: usize = self.branch_stack.items.len - 2; |
| 4469 | var i: usize = self.branch_stack.items.len - 1; |
| 4190 | 4470 | while (true) { |
| 4191 | 4471 | i -= 1; // If this overflows, the question is: why wasn't the instruction marked dead? |
| 4192 | 4472 | if (self.branch_stack.items[i].inst_table.get(else_key)) |mcv| { |
| ... | ... | @@ -4213,7 +4493,7 @@ fn airCondBr(self: *Self, inst: Air.Inst.Index) !void { |
| 4213 | 4493 | if (then_value == .dead) |
| 4214 | 4494 | continue; |
| 4215 | 4495 | const parent_mcv = blk: { |
| 4216 | | var i: usize = self.branch_stack.items.len - 2; |
| 4496 | var i: usize = self.branch_stack.items.len - 1; |
| 4217 | 4497 | while (true) { |
| 4218 | 4498 | i -= 1; |
| 4219 | 4499 | if (self.branch_stack.items[i].inst_table.get(then_key)) |mcv| { |
| ... | ... | @@ -4267,9 +4547,9 @@ fn isErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 4267 | 4547 | fn isNonErr(self: *Self, ty: Type, operand: MCValue) !MCValue { |
| 4268 | 4548 | const is_err_result = try self.isErr(ty, operand); |
| 4269 | 4549 | switch (is_err_result) { |
| 4270 | | .condition_flags => |cond| { |
| 4550 | .compare_flags => |cond| { |
| 4271 | 4551 | assert(cond == .hi); |
| 4272 | | return MCValue{ .condition_flags = cond.negate() }; |
| 4552 | return MCValue{ .compare_flags = cond.negate() }; |
| 4273 | 4553 | }, |
| 4274 | 4554 | .immediate => |imm| { |
| 4275 | 4555 | assert(imm == 0); |
| ... | ... | @@ -4432,10 +4712,132 @@ fn airBlock(self: *Self, inst: Air.Inst.Index) !void { |
| 4432 | 4712 | |
| 4433 | 4713 | fn airSwitch(self: *Self, inst: Air.Inst.Index) !void { |
| 4434 | 4714 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 4435 | | const condition = pl_op.operand; |
| 4436 | | _ = condition; |
| 4715 | const condition_ty = self.air.typeOf(pl_op.operand); |
| 4716 | const switch_br = self.air.extraData(Air.SwitchBr, pl_op.payload); |
| 4717 | const liveness = try self.liveness.getSwitchBr( |
| 4718 | self.gpa, |
| 4719 | inst, |
| 4720 | switch_br.data.cases_len + 1, |
| 4721 | ); |
| 4722 | defer self.gpa.free(liveness.deaths); |
| 4723 | |
| 4724 | var extra_index: usize = switch_br.end; |
| 4725 | var case_i: u32 = 0; |
| 4726 | while (case_i < switch_br.data.cases_len) : (case_i += 1) { |
| 4727 | const case = self.air.extraData(Air.SwitchBr.Case, extra_index); |
| 4728 | const items = @ptrCast([]const Air.Inst.Ref, self.air.extra[case.end..][0..case.data.items_len]); |
| 4729 | assert(items.len > 0); |
| 4730 | const case_body = self.air.extra[case.end + items.len ..][0..case.data.body_len]; |
| 4731 | extra_index = case.end + items.len + case_body.len; |
| 4732 | |
| 4733 | // For every item, we compare it to condition and branch into |
| 4734 | // the prong if they are equal. After we compared to all |
| 4735 | // items, we branch into the next prong (or if no other prongs |
| 4736 | // exist out of the switch statement). |
| 4737 | // |
| 4738 | // cmp condition, item1 |
| 4739 | // beq prong |
| 4740 | // cmp condition, item2 |
| 4741 | // beq prong |
| 4742 | // cmp condition, item3 |
| 4743 | // beq prong |
| 4744 | // b out |
| 4745 | // prong: ... |
| 4746 | // ... |
| 4747 | // out: ... |
| 4748 | const branch_into_prong_relocs = try self.gpa.alloc(u32, items.len); |
| 4749 | defer self.gpa.free(branch_into_prong_relocs); |
| 4750 | |
| 4751 | for (items) |item, idx| { |
| 4752 | const cmp_result = try self.cmp(.{ .inst = pl_op.operand }, .{ .inst = item }, condition_ty, .neq); |
| 4753 | branch_into_prong_relocs[idx] = try self.condBr(cmp_result); |
| 4754 | } |
| 4437 | 4755 | |
| 4438 | | return self.fail("TODO airSwitch for {}", .{self.target.cpu.arch}); |
| 4756 | const branch_away_from_prong_reloc = try self.addInst(.{ |
| 4757 | .tag = .b, |
| 4758 | .data = .{ .inst = undefined }, // populated later through performReloc |
| 4759 | }); |
| 4760 | |
| 4761 | for (branch_into_prong_relocs) |reloc| { |
| 4762 | try self.performReloc(reloc); |
| 4763 | } |
| 4764 | |
| 4765 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4766 | const parent_next_stack_offset = self.next_stack_offset; |
| 4767 | const parent_free_registers = self.register_manager.free_registers; |
| 4768 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 4769 | var parent_stack = try self.stack.clone(self.gpa); |
| 4770 | defer parent_stack.deinit(self.gpa); |
| 4771 | const parent_registers = self.register_manager.registers; |
| 4772 | |
| 4773 | try self.branch_stack.append(.{}); |
| 4774 | errdefer { |
| 4775 | _ = self.branch_stack.pop(); |
| 4776 | } |
| 4777 | |
| 4778 | try self.ensureProcessDeathCapacity(liveness.deaths[case_i].len); |
| 4779 | for (liveness.deaths[case_i]) |operand| { |
| 4780 | self.processDeath(operand); |
| 4781 | } |
| 4782 | try self.genBody(case_body); |
| 4783 | |
| 4784 | // Revert to the previous register and stack allocation state. |
| 4785 | var saved_case_branch = self.branch_stack.pop(); |
| 4786 | defer saved_case_branch.deinit(self.gpa); |
| 4787 | |
| 4788 | self.register_manager.registers = parent_registers; |
| 4789 | self.compare_flags_inst = parent_compare_flags_inst; |
| 4790 | self.stack.deinit(self.gpa); |
| 4791 | self.stack = parent_stack; |
| 4792 | parent_stack = .{}; |
| 4793 | |
| 4794 | self.next_stack_offset = parent_next_stack_offset; |
| 4795 | self.register_manager.free_registers = parent_free_registers; |
| 4796 | |
| 4797 | try self.performReloc(branch_away_from_prong_reloc); |
| 4798 | } |
| 4799 | |
| 4800 | if (switch_br.data.else_body_len > 0) { |
| 4801 | const else_body = self.air.extra[extra_index..][0..switch_br.data.else_body_len]; |
| 4802 | |
| 4803 | // Capture the state of register and stack allocation state so that we can revert to it. |
| 4804 | const parent_next_stack_offset = self.next_stack_offset; |
| 4805 | const parent_free_registers = self.register_manager.free_registers; |
| 4806 | const parent_compare_flags_inst = self.compare_flags_inst; |
| 4807 | var parent_stack = try self.stack.clone(self.gpa); |
| 4808 | defer parent_stack.deinit(self.gpa); |
| 4809 | const parent_registers = self.register_manager.registers; |
| 4810 | |
| 4811 | try self.branch_stack.append(.{}); |
| 4812 | errdefer { |
| 4813 | _ = self.branch_stack.pop(); |
| 4814 | } |
| 4815 | |
| 4816 | const else_deaths = liveness.deaths.len - 1; |
| 4817 | try self.ensureProcessDeathCapacity(liveness.deaths[else_deaths].len); |
| 4818 | for (liveness.deaths[else_deaths]) |operand| { |
| 4819 | self.processDeath(operand); |
| 4820 | } |
| 4821 | try self.genBody(else_body); |
| 4822 | |
| 4823 | // Revert to the previous register and stack allocation state. |
| 4824 | var saved_case_branch = self.branch_stack.pop(); |
| 4825 | defer saved_case_branch.deinit(self.gpa); |
| 4826 | |
| 4827 | self.register_manager.registers = parent_registers; |
| 4828 | self.compare_flags_inst = parent_compare_flags_inst; |
| 4829 | self.stack.deinit(self.gpa); |
| 4830 | self.stack = parent_stack; |
| 4831 | parent_stack = .{}; |
| 4832 | |
| 4833 | self.next_stack_offset = parent_next_stack_offset; |
| 4834 | self.register_manager.free_registers = parent_free_registers; |
| 4835 | |
| 4836 | // TODO consolidate returned MCValues between prongs and else branch like we do |
| 4837 | // in airCondBr. |
| 4838 | } |
| 4839 | |
| 4840 | return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); |
| 4439 | 4841 | } |
| 4440 | 4842 | |
| 4441 | 4843 | fn performReloc(self: *Self, inst: Mir.Inst.Index) !void { |
| ... | ... | @@ -4464,7 +4866,7 @@ fn br(self: *Self, block: Air.Inst.Index, operand: Air.Inst.Ref) !void { |
| 4464 | 4866 | block_data.mcv = switch (operand_mcv) { |
| 4465 | 4867 | .none, .dead, .unreach => unreachable, |
| 4466 | 4868 | .register, .stack_offset, .memory => operand_mcv, |
| 4467 | | .immediate, .stack_argument_offset, .condition_flags => blk: { |
| 4869 | .immediate, .stack_argument_offset, .compare_flags => blk: { |
| 4468 | 4870 | const new_mcv = try self.allocRegOrMem(self.air.typeOfIndex(block), true, block); |
| 4469 | 4871 | try self.setRegOrMem(self.air.typeOfIndex(block), new_mcv, operand_mcv); |
| 4470 | 4872 | break :blk new_mcv; |
| ... | ... | @@ -4644,10 +5046,14 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) InnerErro |
| 4644 | 5046 | 2 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaa }), |
| 4645 | 5047 | 4 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaa }), |
| 4646 | 5048 | 8 => return self.genSetStack(ty, stack_offset, .{ .immediate = 0xaaaaaaaaaaaaaaaa }), |
| 4647 | | else => return self.fail("TODO implement memset", .{}), |
| 5049 | else => try self.genInlineMemset( |
| 5050 | .{ .ptr_stack_offset = stack_offset }, |
| 5051 | .{ .immediate = 0xaa }, |
| 5052 | .{ .immediate = abi_size }, |
| 5053 | ), |
| 4648 | 5054 | } |
| 4649 | 5055 | }, |
| 4650 | | .condition_flags, |
| 5056 | .compare_flags, |
| 4651 | 5057 | .immediate, |
| 4652 | 5058 | .ptr_stack_offset, |
| 4653 | 5059 | => { |
| ... | ... | @@ -4807,7 +5213,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void |
| 4807 | 5213 | } }, |
| 4808 | 5214 | }); |
| 4809 | 5215 | }, |
| 4810 | | .condition_flags => |condition| { |
| 5216 | .compare_flags => |condition| { |
| 4811 | 5217 | _ = try self.addInst(.{ |
| 4812 | 5218 | .tag = .cset, |
| 4813 | 5219 | .data = .{ .r_cond = .{ |
| ... | ... | @@ -5084,7 +5490,7 @@ fn genSetStackArgument(self: *Self, ty: Type, stack_offset: u32, mcv: MCValue) I |
| 5084 | 5490 | try self.genInlineMemcpy(src_reg, dst_reg, len_reg, count_reg, tmp_reg); |
| 5085 | 5491 | } |
| 5086 | 5492 | }, |
| 5087 | | .condition_flags, |
| 5493 | .compare_flags, |
| 5088 | 5494 | .immediate, |
| 5089 | 5495 | .ptr_stack_offset, |
| 5090 | 5496 | => { |