blob fc3dbcb2 (2235887B) - Raw
1 const std = @import("std"); 2 const assert = std.debug.assert; 3 const codegen = @import("../../codegen.zig"); 4 const link = @import("../../link.zig"); 5 const log = std.log.scoped(.codegen); 6 const tracking_log = std.log.scoped(.tracking); 7 const verbose_tracking_log = std.log.scoped(.verbose_tracking); 8 const wip_mir_log = std.log.scoped(.wip_mir); 9 10 const Air = @import("../../Air.zig"); 11 const Allocator = std.mem.Allocator; 12 const Emit = @import("Emit.zig"); 13 const Liveness = @import("../../Liveness.zig"); 14 const Lower = @import("Lower.zig"); 15 const Mir = @import("Mir.zig"); 16 const Zcu = @import("../../Zcu.zig"); 17 const Module = @import("../../Package/Module.zig"); 18 const InternPool = @import("../../InternPool.zig"); 19 const Type = @import("../../Type.zig"); 20 const Value = @import("../../Value.zig"); 21 22 const abi = @import("abi.zig"); 23 const bits = @import("bits.zig"); 24 const encoder = @import("encoder.zig"); 25 26 const Condition = bits.Condition; 27 const Immediate = bits.Immediate; 28 const Memory = bits.Memory; 29 const Register = bits.Register; 30 const RegisterManager = abi.RegisterManager; 31 const RegisterLock = RegisterManager.RegisterLock; 32 const FrameIndex = bits.FrameIndex; 33 34 const InnerError = codegen.CodeGenError || error{OutOfRegisters}; 35 36 const err_ret_trace_index: Air.Inst.Index = @enumFromInt(std.math.maxInt(u32)); 37 38 gpa: Allocator, 39 pt: Zcu.PerThread, 40 air: Air, 41 liveness: Liveness, 42 bin_file: *link.File, 43 debug_output: link.File.DebugInfoOutput, 44 target: *const std.Target, 45 owner: Owner, 46 inline_func: InternPool.Index, 47 mod: *Module, 48 arg_index: u32, 49 args: []MCValue, 50 va_info: union { 51 sysv: struct { 52 gp_count: u32, 53 fp_count: u32, 54 overflow_arg_area: bits.FrameAddr, 55 reg_save_area: bits.FrameAddr, 56 }, 57 win64: struct {}, 58 }, 59 ret_mcv: InstTracking, 60 err_ret_trace_reg: Register, 61 fn_type: Type, 62 src_loc: Zcu.LazySrcLoc, 63 64 eflags_inst: ?Air.Inst.Index = null, 65 66 /// MIR Instructions 67 mir_instructions: std.MultiArrayList(Mir.Inst) = .empty, 68 /// MIR extra data 69 mir_extra: std.ArrayListUnmanaged(u32) = .empty, 70 mir_table: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, 71 72 /// Byte offset within the source file of the ending curly. 73 end_di_line: u32, 74 end_di_column: u32, 75 76 /// The value is an offset into the `Function` `code` from the beginning. 77 /// To perform the reloc, write 32-bit signed little-endian integer 78 /// which is a relative jump, based on the address following the reloc. 79 epilogue_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, 80 81 reused_operands: std.StaticBitSet(Liveness.bpi - 1) = undefined, 82 const_tracking: ConstTrackingMap = .empty, 83 inst_tracking: InstTrackingMap = .empty, 84 85 // Key is the block instruction 86 blocks: std.AutoHashMapUnmanaged(Air.Inst.Index, BlockData) = .empty, 87 88 register_manager: RegisterManager = .{}, 89 90 /// Generation of the current scope, increments by 1 for every entered scope. 91 scope_generation: u32 = 0, 92 93 frame_allocs: std.MultiArrayList(FrameAlloc) = .empty, 94 free_frame_indices: std.AutoArrayHashMapUnmanaged(FrameIndex, void) = .empty, 95 frame_locs: std.MultiArrayList(Mir.FrameLoc) = .empty, 96 97 loops: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { 98 /// The state to restore before branching. 99 state: State, 100 /// The branch target. 101 target: Mir.Inst.Index, 102 }) = .empty, 103 loop_switches: std.AutoHashMapUnmanaged(Air.Inst.Index, struct { 104 start: u31, 105 len: u11, 106 min: Value, 107 else_relocs: union(enum) { 108 @"unreachable", 109 forward: std.ArrayListUnmanaged(Mir.Inst.Index), 110 backward: Mir.Inst.Index, 111 }, 112 }) = .empty, 113 114 next_temp_index: Temp.Index = @enumFromInt(0), 115 temp_type: [Temp.Index.max]Type = undefined, 116 117 const Owner = union(enum) { 118 nav_index: InternPool.Nav.Index, 119 lazy_sym: link.File.LazySymbol, 120 121 fn getSymbolIndex(owner: Owner, ctx: *CodeGen) !u32 { 122 const pt = ctx.pt; 123 switch (owner) { 124 .nav_index => |nav_index| if (ctx.bin_file.cast(.elf)) |elf_file| { 125 return elf_file.zigObjectPtr().?.getOrCreateMetadataForNav(pt.zcu, nav_index); 126 } else if (ctx.bin_file.cast(.macho)) |macho_file| { 127 return macho_file.getZigObject().?.getOrCreateMetadataForNav(macho_file, nav_index); 128 } else if (ctx.bin_file.cast(.coff)) |coff_file| { 129 const atom = try coff_file.getOrCreateAtomForNav(nav_index); 130 return coff_file.getAtom(atom).getSymbolIndex().?; 131 } else if (ctx.bin_file.cast(.plan9)) |p9_file| { 132 return p9_file.seeNav(pt, nav_index); 133 } else unreachable, 134 .lazy_sym => |lazy_sym| if (ctx.bin_file.cast(.elf)) |elf_file| { 135 return elf_file.zigObjectPtr().?.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| 136 ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); 137 } else if (ctx.bin_file.cast(.macho)) |macho_file| { 138 return macho_file.getZigObject().?.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| 139 ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); 140 } else if (ctx.bin_file.cast(.coff)) |coff_file| { 141 const atom = coff_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| 142 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); 143 return coff_file.getAtom(atom).getSymbolIndex().?; 144 } else if (ctx.bin_file.cast(.plan9)) |p9_file| { 145 return p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| 146 return ctx.fail("{s} creating lazy symbol", .{@errorName(err)}); 147 } else unreachable, 148 } 149 } 150 }; 151 152 const MaskKind = enum(u1) { sign, all }; 153 const MaskInfo = packed struct { kind: MaskKind, inverted: bool = false, scalar: Memory.Size }; 154 155 pub const MCValue = union(enum) { 156 /// No runtime bits. `void` types, empty structs, u0, enums with 1 tag, etc. 157 /// TODO Look into deleting this tag and using `dead` instead, since every use 158 /// of MCValue.none should be instead looking at the type and noticing it is 0 bits. 159 none, 160 /// Control flow will not allow this value to be observed. 161 unreach, 162 /// No more references to this value remain. 163 /// The payload is the value of scope_generation at the point where the death occurred 164 dead: u32, 165 /// The value is undefined. 166 undef, 167 /// A pointer-sized integer that fits in a register. 168 /// If the type is a pointer, this is the pointer address in virtual address space. 169 immediate: u64, 170 /// The value resides in the EFLAGS register. 171 eflags: Condition, 172 /// The value is in a register. 173 register: Register, 174 /// The value is split across two registers. 175 register_pair: [2]Register, 176 /// The value is split across three registers. 177 register_triple: [3]Register, 178 /// The value is split across four registers. 179 register_quadruple: [4]Register, 180 /// The value is a constant offset from the value in a register. 181 register_offset: bits.RegisterOffset, 182 /// The value is a tuple { wrapped, overflow } where wrapped value is stored in the GP register. 183 register_overflow: struct { reg: Register, eflags: Condition }, 184 /// The value is a bool vector stored in a vector register with a different scalar type. 185 register_mask: struct { reg: Register, info: MaskInfo }, 186 /// The value is in memory at a hard-coded address. 187 /// If the type is a pointer, it means the pointer address is stored at this memory location. 188 memory: u64, 189 /// The value is in memory at an address not-yet-allocated by the linker. 190 /// This traditionally corresponds to a relocation emitted in a relocatable object file. 191 load_symbol: bits.SymbolOffset, 192 /// The address of the memory location not-yet-allocated by the linker. 193 lea_symbol: bits.SymbolOffset, 194 /// The value is in memory at a constant offset from the address in a register. 195 indirect: bits.RegisterOffset, 196 /// The value is in memory. 197 /// Payload is a symbol index. 198 load_direct: u32, 199 /// The value is a pointer to a value in memory. 200 /// Payload is a symbol index. 201 lea_direct: u32, 202 /// The value is in memory referenced indirectly via GOT. 203 /// Payload is a symbol index. 204 load_got: u32, 205 /// The value is a pointer to a value referenced indirectly via GOT. 206 /// Payload is a symbol index. 207 lea_got: u32, 208 /// The value is a threadlocal variable. 209 /// Payload is a symbol index. 210 load_tlv: u32, 211 /// The value is a pointer to a threadlocal variable. 212 /// Payload is a symbol index. 213 lea_tlv: u32, 214 /// The value stored at an offset from a frame index 215 /// Payload is a frame address. 216 load_frame: bits.FrameAddr, 217 /// The address of an offset from a frame index 218 /// Payload is a frame address. 219 lea_frame: bits.FrameAddr, 220 /// Supports integer_per_element abi 221 elementwise_regs_then_frame: packed struct { regs: u3, frame_off: i29, frame_index: FrameIndex }, 222 /// This indicates that we have already allocated a frame index for this instruction, 223 /// but it has not been spilled there yet in the current control flow. 224 /// Payload is a frame index. 225 reserved_frame: FrameIndex, 226 air_ref: Air.Inst.Ref, 227 228 fn isModifiable(mcv: MCValue) bool { 229 return switch (mcv) { 230 .none, 231 .unreach, 232 .dead, 233 .undef, 234 .immediate, 235 .register_offset, 236 .register_mask, 237 .eflags, 238 .register_overflow, 239 .lea_symbol, 240 .lea_direct, 241 .lea_got, 242 .lea_tlv, 243 .lea_frame, 244 .elementwise_regs_then_frame, 245 .reserved_frame, 246 .air_ref, 247 => false, 248 .register, 249 .register_pair, 250 .register_triple, 251 .register_quadruple, 252 .memory, 253 .load_symbol, 254 .load_got, 255 .load_direct, 256 .load_tlv, 257 .indirect, 258 => true, 259 .load_frame => |frame_addr| !frame_addr.index.isNamed(), 260 }; 261 } 262 263 // hack around linker relocation bugs 264 fn isBase(mcv: MCValue) bool { 265 return switch (mcv) { 266 .memory, .indirect, .load_frame => true, 267 else => false, 268 }; 269 } 270 271 fn isMemory(mcv: MCValue) bool { 272 return switch (mcv) { 273 .memory, .indirect, .load_frame, .load_symbol => true, 274 else => false, 275 }; 276 } 277 278 fn isImmediate(mcv: MCValue) bool { 279 return switch (mcv) { 280 .immediate => true, 281 else => false, 282 }; 283 } 284 285 fn isRegister(mcv: MCValue) bool { 286 return switch (mcv) { 287 .register => true, 288 .register_offset => |reg_off| return reg_off.off == 0, 289 else => false, 290 }; 291 } 292 293 fn isRegisterOffset(mcv: MCValue) bool { 294 return switch (mcv) { 295 .register, .register_offset => true, 296 else => false, 297 }; 298 } 299 300 fn getReg(mcv: MCValue) ?Register { 301 return switch (mcv) { 302 .register => |reg| reg, 303 .register_offset, .indirect => |ro| ro.reg, 304 .register_overflow => |ro| ro.reg, 305 .register_mask => |rm| rm.reg, 306 else => null, 307 }; 308 } 309 310 fn getRegs(mcv: *const MCValue) []const Register { 311 return switch (mcv.*) { 312 .register => |*reg| reg[0..1], 313 inline .register_pair, 314 .register_triple, 315 .register_quadruple, 316 => |*regs| regs, 317 inline .register_offset, 318 .indirect, 319 .register_overflow, 320 .register_mask, 321 => |*pl| (&pl.reg)[0..1], 322 else => &.{}, 323 }; 324 } 325 326 fn getCondition(mcv: MCValue) ?Condition { 327 return switch (mcv) { 328 .eflags => |cc| cc, 329 .register_overflow => |reg_ov| reg_ov.eflags, 330 else => null, 331 }; 332 } 333 334 fn isAddress(mcv: MCValue) bool { 335 return switch (mcv) { 336 .immediate, .register, .register_offset, .lea_frame => true, 337 else => false, 338 }; 339 } 340 341 fn address(mcv: MCValue) MCValue { 342 return switch (mcv) { 343 .none, 344 .unreach, 345 .dead, 346 .undef, 347 .immediate, 348 .eflags, 349 .register, 350 .register_pair, 351 .register_triple, 352 .register_quadruple, 353 .register_offset, 354 .register_overflow, 355 .register_mask, 356 .lea_symbol, 357 .lea_direct, 358 .lea_got, 359 .lea_tlv, 360 .lea_frame, 361 .elementwise_regs_then_frame, 362 .reserved_frame, 363 .air_ref, 364 => unreachable, // not in memory 365 .memory => |addr| .{ .immediate = addr }, 366 .indirect => |reg_off| switch (reg_off.off) { 367 0 => .{ .register = reg_off.reg }, 368 else => .{ .register_offset = reg_off }, 369 }, 370 .load_direct => |sym_index| .{ .lea_direct = sym_index }, 371 .load_got => |sym_index| .{ .lea_got = sym_index }, 372 .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, 373 .load_frame => |frame_addr| .{ .lea_frame = frame_addr }, 374 .load_symbol => |sym_off| .{ .lea_symbol = sym_off }, 375 }; 376 } 377 378 fn deref(mcv: MCValue) MCValue { 379 return switch (mcv) { 380 .none, 381 .unreach, 382 .dead, 383 .undef, 384 .eflags, 385 .register_pair, 386 .register_triple, 387 .register_quadruple, 388 .register_overflow, 389 .register_mask, 390 .memory, 391 .indirect, 392 .load_direct, 393 .load_got, 394 .load_tlv, 395 .load_frame, 396 .load_symbol, 397 .elementwise_regs_then_frame, 398 .reserved_frame, 399 .air_ref, 400 => unreachable, // not dereferenceable 401 .immediate => |addr| .{ .memory = addr }, 402 .register => |reg| .{ .indirect = .{ .reg = reg } }, 403 .register_offset => |reg_off| .{ .indirect = reg_off }, 404 .lea_direct => |sym_index| .{ .load_direct = sym_index }, 405 .lea_got => |sym_index| .{ .load_got = sym_index }, 406 .lea_tlv => |sym_index| .{ .load_tlv = sym_index }, 407 .lea_frame => |frame_addr| .{ .load_frame = frame_addr }, 408 .lea_symbol => |sym_index| .{ .load_symbol = sym_index }, 409 }; 410 } 411 412 fn offset(mcv: MCValue, off: i32) MCValue { 413 return switch (mcv) { 414 .none, 415 .unreach, 416 .dead, 417 .undef, 418 .elementwise_regs_then_frame, 419 .reserved_frame, 420 .air_ref, 421 => unreachable, // not valid 422 .eflags, 423 .register_pair, 424 .register_triple, 425 .register_quadruple, 426 .register_overflow, 427 .register_mask, 428 .memory, 429 .indirect, 430 .load_direct, 431 .lea_direct, 432 .load_got, 433 .lea_got, 434 .load_tlv, 435 .lea_tlv, 436 .load_frame, 437 .load_symbol, 438 .lea_symbol, 439 => switch (off) { 440 0 => mcv, 441 else => unreachable, // not offsettable 442 }, 443 .immediate => |imm| .{ .immediate = @bitCast(@as(i64, @bitCast(imm)) +% off) }, 444 .register => |reg| .{ .register_offset = .{ .reg = reg, .off = off } }, 445 .register_offset => |reg_off| .{ 446 .register_offset = .{ .reg = reg_off.reg, .off = reg_off.off + off }, 447 }, 448 .lea_frame => |frame_addr| .{ 449 .lea_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off }, 450 }, 451 }; 452 } 453 454 fn mem(mcv: MCValue, function: *CodeGen, mod_rm: Memory.Mod.Rm) !Memory { 455 return switch (mcv) { 456 .none, 457 .unreach, 458 .dead, 459 .undef, 460 .immediate, 461 .eflags, 462 .register, 463 .register_pair, 464 .register_triple, 465 .register_quadruple, 466 .register_offset, 467 .register_overflow, 468 .register_mask, 469 .load_direct, 470 .lea_direct, 471 .load_got, 472 .lea_got, 473 .load_tlv, 474 .lea_tlv, 475 .lea_frame, 476 .elementwise_regs_then_frame, 477 .reserved_frame, 478 .lea_symbol, 479 => unreachable, 480 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| .{ 481 .base = .{ .reg = .ds }, 482 .mod = .{ .rm = .{ 483 .size = mod_rm.size, 484 .index = mod_rm.index, 485 .scale = mod_rm.scale, 486 .disp = small_addr + mod_rm.disp, 487 } }, 488 } else .{ .base = .{ .reg = .ds }, .mod = .{ .off = addr } }, 489 .indirect => |reg_off| .{ 490 .base = .{ .reg = registerAlias(reg_off.reg, @divExact(function.target.ptrBitWidth(), 8)) }, 491 .mod = .{ .rm = .{ 492 .size = mod_rm.size, 493 .index = mod_rm.index, 494 .scale = mod_rm.scale, 495 .disp = reg_off.off + mod_rm.disp, 496 } }, 497 }, 498 .load_frame => |frame_addr| .{ 499 .base = .{ .frame = frame_addr.index }, 500 .mod = .{ .rm = .{ 501 .size = mod_rm.size, 502 .index = mod_rm.index, 503 .scale = mod_rm.scale, 504 .disp = frame_addr.off + mod_rm.disp, 505 } }, 506 }, 507 .load_symbol => |sym_off| { 508 assert(sym_off.off == 0); 509 return .{ 510 .base = .{ .reloc = sym_off.sym_index }, 511 .mod = .{ .rm = .{ 512 .size = mod_rm.size, 513 .index = mod_rm.index, 514 .scale = mod_rm.scale, 515 .disp = sym_off.off + mod_rm.disp, 516 } }, 517 }; 518 }, 519 .air_ref => |ref| (try function.resolveInst(ref)).mem(function, mod_rm), 520 }; 521 } 522 523 pub fn format( 524 mcv: MCValue, 525 comptime _: []const u8, 526 _: std.fmt.FormatOptions, 527 writer: anytype, 528 ) @TypeOf(writer).Error!void { 529 switch (mcv) { 530 .none, .unreach, .dead, .undef => try writer.print("({s})", .{@tagName(mcv)}), 531 .immediate => |pl| try writer.print("0x{x}", .{pl}), 532 .memory => |pl| try writer.print("[ds:0x{x}]", .{pl}), 533 inline .eflags, .register => |pl| try writer.print("{s}", .{@tagName(pl)}), 534 .register_pair => |pl| try writer.print("{s}:{s}", .{ @tagName(pl[1]), @tagName(pl[0]) }), 535 .register_triple => |pl| try writer.print("{s}:{s}:{s}", .{ 536 @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), 537 }), 538 .register_quadruple => |pl| try writer.print("{s}:{s}:{s}:{s}", .{ 539 @tagName(pl[3]), @tagName(pl[2]), @tagName(pl[1]), @tagName(pl[0]), 540 }), 541 .register_offset => |pl| try writer.print("{s} + 0x{x}", .{ @tagName(pl.reg), pl.off }), 542 .register_overflow => |pl| try writer.print("{s}:{s}", .{ 543 @tagName(pl.eflags), 544 @tagName(pl.reg), 545 }), 546 .register_mask => |pl| try writer.print("mask({s},{}):{c}{s}", .{ 547 @tagName(pl.info.kind), 548 pl.info.scalar, 549 @as(u8, if (pl.info.inverted) '!' else ' '), 550 @tagName(pl.reg), 551 }), 552 .load_symbol => |pl| try writer.print("[sym:{} + 0x{x}]", .{ pl.sym_index, pl.off }), 553 .lea_symbol => |pl| try writer.print("sym:{} + 0x{x}", .{ pl.sym_index, pl.off }), 554 .indirect => |pl| try writer.print("[{s} + 0x{x}]", .{ @tagName(pl.reg), pl.off }), 555 .load_direct => |pl| try writer.print("[direct:{d}]", .{pl}), 556 .lea_direct => |pl| try writer.print("direct:{d}", .{pl}), 557 .load_got => |pl| try writer.print("[got:{d}]", .{pl}), 558 .lea_got => |pl| try writer.print("got:{d}", .{pl}), 559 .load_tlv => |pl| try writer.print("[tlv:{d}]", .{pl}), 560 .lea_tlv => |pl| try writer.print("tlv:{d}", .{pl}), 561 .load_frame => |pl| try writer.print("[{} + 0x{x}]", .{ pl.index, pl.off }), 562 .elementwise_regs_then_frame => |pl| try writer.print("elementwise:{d}:[{} + 0x{x}]", .{ 563 pl.regs, pl.frame_index, pl.frame_off, 564 }), 565 .lea_frame => |pl| try writer.print("{} + 0x{x}", .{ pl.index, pl.off }), 566 .reserved_frame => |pl| try writer.print("(dead:{})", .{pl}), 567 .air_ref => |pl| try writer.print("(air:0x{x})", .{@intFromEnum(pl)}), 568 } 569 } 570 }; 571 572 const InstTrackingMap = std.AutoArrayHashMapUnmanaged(Air.Inst.Index, InstTracking); 573 const ConstTrackingMap = std.AutoArrayHashMapUnmanaged(InternPool.Index, InstTracking); 574 const InstTracking = struct { 575 long: MCValue, 576 short: MCValue, 577 578 fn init(result: MCValue) InstTracking { 579 return .{ .long = switch (result) { 580 .none, 581 .unreach, 582 .undef, 583 .immediate, 584 .memory, 585 .load_direct, 586 .lea_direct, 587 .load_got, 588 .lea_got, 589 .load_tlv, 590 .lea_tlv, 591 .load_frame, 592 .lea_frame, 593 .load_symbol, 594 .lea_symbol, 595 => result, 596 .dead, 597 .elementwise_regs_then_frame, 598 .reserved_frame, 599 .air_ref, 600 => unreachable, 601 .eflags, 602 .register, 603 .register_pair, 604 .register_triple, 605 .register_quadruple, 606 .register_offset, 607 .register_overflow, 608 .register_mask, 609 .indirect, 610 => .none, 611 }, .short = result }; 612 } 613 614 fn getReg(self: InstTracking) ?Register { 615 return self.short.getReg(); 616 } 617 618 fn getRegs(self: *const InstTracking) []const Register { 619 return self.short.getRegs(); 620 } 621 622 fn getCondition(self: InstTracking) ?Condition { 623 return self.short.getCondition(); 624 } 625 626 fn spill(self: *InstTracking, cg: *CodeGen, inst: Air.Inst.Index) !void { 627 if (std.meta.eql(self.long, self.short)) return; // Already spilled 628 // Allocate or reuse frame index 629 switch (self.long) { 630 .none => self.long = try cg.allocRegOrMem(inst, false), 631 .load_frame => {}, 632 .lea_frame => return, 633 .reserved_frame => |index| self.long = .{ .load_frame = .{ .index = index } }, 634 else => unreachable, 635 } 636 tracking_log.debug("spill {} from {} to {}", .{ inst, self.short, self.long }); 637 try cg.genCopy(cg.typeOfIndex(inst), self.long, self.short, .{}); 638 for (self.short.getRegs()) |reg| if (reg.class() == .x87) try cg.asmRegister(.{ .f_, .free }, reg); 639 } 640 641 fn reuseFrame(self: *InstTracking) void { 642 self.* = .init(switch (self.long) { 643 .none => switch (self.short) { 644 .dead => .none, 645 else => |short| short, 646 }, 647 .reserved_frame => |index| .{ .load_frame = .{ .index = index } }, 648 else => |long| long, 649 }); 650 } 651 652 fn trackSpill(self: *InstTracking, function: *CodeGen, inst: Air.Inst.Index) !void { 653 switch (self.short) { 654 .register => |reg| function.register_manager.freeReg(reg), 655 inline .register_pair, 656 .register_triple, 657 .register_quadruple, 658 => |regs| for (regs) |reg| function.register_manager.freeReg(reg), 659 .register_offset, .indirect => |reg_off| function.register_manager.freeReg(reg_off.reg), 660 .register_overflow => |reg_ov| { 661 function.register_manager.freeReg(reg_ov.reg); 662 function.eflags_inst = null; 663 }, 664 .register_mask => |reg_mask| function.register_manager.freeReg(reg_mask.reg), 665 .eflags => function.eflags_inst = null, 666 else => {}, // TODO process stack allocation death 667 } 668 self.reuseFrame(); 669 tracking_log.debug("{} => {} (spilled)", .{ inst, self.* }); 670 } 671 672 fn verifyMaterialize(self: InstTracking, target: InstTracking) void { 673 switch (self.long) { 674 .none, 675 .load_frame, 676 .reserved_frame, 677 => switch (target.long) { 678 .none, 679 .load_frame, 680 .reserved_frame, 681 => {}, 682 else => unreachable, 683 }, 684 .unreach, 685 .undef, 686 .immediate, 687 .memory, 688 .load_direct, 689 .lea_direct, 690 .load_got, 691 .lea_got, 692 .load_tlv, 693 .lea_tlv, 694 .lea_frame, 695 .load_symbol, 696 .lea_symbol, 697 => assert(std.meta.eql(self.long, target.long)), 698 .dead, 699 .eflags, 700 .register, 701 .register_pair, 702 .register_triple, 703 .register_quadruple, 704 .register_offset, 705 .register_overflow, 706 .register_mask, 707 .indirect, 708 .elementwise_regs_then_frame, 709 .air_ref, 710 => unreachable, 711 } 712 } 713 714 fn materialize( 715 self: *InstTracking, 716 function: *CodeGen, 717 inst: Air.Inst.Index, 718 target: InstTracking, 719 ) !void { 720 self.verifyMaterialize(target); 721 try self.materializeUnsafe(function, inst, target); 722 } 723 724 fn materializeUnsafe( 725 self: InstTracking, 726 function: *CodeGen, 727 inst: Air.Inst.Index, 728 target: InstTracking, 729 ) !void { 730 const ty = function.typeOfIndex(inst); 731 if ((self.long == .none or self.long == .reserved_frame) and target.long == .load_frame) 732 try function.genCopy(ty, target.long, self.short, .{}); 733 try function.genCopy(ty, target.short, self.short, .{}); 734 } 735 736 fn trackMaterialize(self: *InstTracking, inst: Air.Inst.Index, target: InstTracking) void { 737 self.verifyMaterialize(target); 738 // Don't clobber reserved frame indices 739 self.long = if (target.long == .none) switch (self.long) { 740 .load_frame => |addr| .{ .reserved_frame = addr.index }, 741 .reserved_frame => self.long, 742 else => target.long, 743 } else target.long; 744 self.short = target.short; 745 tracking_log.debug("{} => {} (materialize)", .{ inst, self.* }); 746 } 747 748 fn resurrect(self: *InstTracking, function: *CodeGen, inst: Air.Inst.Index, scope_generation: u32) !void { 749 switch (self.short) { 750 .dead => |die_generation| if (die_generation >= scope_generation) { 751 self.reuseFrame(); 752 try function.getValue(self.short, inst); 753 tracking_log.debug("{} => {} (resurrect)", .{ inst, self.* }); 754 }, 755 else => {}, 756 } 757 } 758 759 fn die(self: *InstTracking, function: *CodeGen, inst: Air.Inst.Index) !void { 760 if (self.short == .dead) return; 761 try function.freeValue(self.short); 762 if (self.long == .none) self.long = self.short; 763 self.short = .{ .dead = function.scope_generation }; 764 tracking_log.debug("{} => {} (death)", .{ inst, self.* }); 765 } 766 767 fn reuse( 768 self: *InstTracking, 769 function: *CodeGen, 770 new_inst: ?Air.Inst.Index, 771 old_inst: Air.Inst.Index, 772 ) void { 773 self.short = .{ .dead = function.scope_generation }; 774 tracking_log.debug("{?} => {} (reuse {})", .{ new_inst, self.*, old_inst }); 775 } 776 777 fn liveOut(self: *InstTracking, function: *CodeGen, inst: Air.Inst.Index) void { 778 for (self.getRegs()) |reg| { 779 if (function.register_manager.isRegFree(reg)) { 780 tracking_log.debug("{} => {} (live-out)", .{ inst, self.* }); 781 continue; 782 } 783 784 const index = RegisterManager.indexOfRegIntoTracked(reg).?; 785 const tracked_inst = function.register_manager.registers[index]; 786 const tracking = function.getResolvedInstValue(tracked_inst); 787 788 // Disable death. 789 var found_reg = false; 790 var remaining_reg: Register = .none; 791 for (tracking.getRegs()) |tracked_reg| if (tracked_reg.id() == reg.id()) { 792 assert(!found_reg); 793 found_reg = true; 794 } else { 795 assert(remaining_reg == .none); 796 remaining_reg = tracked_reg; 797 }; 798 assert(found_reg); 799 tracking.short = switch (remaining_reg) { 800 .none => .{ .dead = function.scope_generation }, 801 else => .{ .register = remaining_reg }, 802 }; 803 804 // Perform side-effects of freeValue manually. 805 function.register_manager.freeReg(reg); 806 807 tracking_log.debug("{} => {} (live-out {})", .{ inst, self.*, tracked_inst }); 808 } 809 } 810 811 pub fn format( 812 tracking: InstTracking, 813 comptime _: []const u8, 814 _: std.fmt.FormatOptions, 815 writer: anytype, 816 ) @TypeOf(writer).Error!void { 817 if (!std.meta.eql(tracking.long, tracking.short)) try writer.print("|{}| ", .{tracking.long}); 818 try writer.print("{}", .{tracking.short}); 819 } 820 }; 821 822 const FrameAlloc = struct { 823 abi_size: u31, 824 spill_pad: u3, 825 abi_align: InternPool.Alignment, 826 ref_count: u16, 827 828 fn init(alloc_abi: struct { size: u64, pad: u3 = 0, alignment: InternPool.Alignment }) FrameAlloc { 829 return .{ 830 .abi_size = @intCast(alloc_abi.size), 831 .spill_pad = alloc_abi.pad, 832 .abi_align = alloc_abi.alignment, 833 .ref_count = 0, 834 }; 835 } 836 fn initType(ty: Type, zcu: *Zcu) FrameAlloc { 837 return init(.{ 838 .size = ty.abiSize(zcu), 839 .alignment = ty.abiAlignment(zcu), 840 }); 841 } 842 fn initSpill(ty: Type, zcu: *Zcu) FrameAlloc { 843 const abi_size = ty.abiSize(zcu); 844 const spill_size = if (abi_size < 8) 845 std.math.ceilPowerOfTwoAssert(u64, abi_size) 846 else 847 std.mem.alignForward(u64, abi_size, 8); 848 return init(.{ 849 .size = spill_size, 850 .pad = @intCast(spill_size - abi_size), 851 .alignment = ty.abiAlignment(zcu).maxStrict( 852 .fromNonzeroByteUnits(@min(spill_size, 8)), 853 ), 854 }); 855 } 856 }; 857 858 const StackAllocation = struct { 859 inst: ?Air.Inst.Index, 860 /// TODO do we need size? should be determined by inst.ty.abiSize(zcu) 861 size: u32, 862 }; 863 864 const BlockData = struct { 865 relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, 866 state: State, 867 868 fn deinit(self: *BlockData, gpa: Allocator) void { 869 self.relocs.deinit(gpa); 870 self.* = undefined; 871 } 872 }; 873 874 const CodeGen = @This(); 875 876 pub fn generate( 877 bin_file: *link.File, 878 pt: Zcu.PerThread, 879 src_loc: Zcu.LazySrcLoc, 880 func_index: InternPool.Index, 881 air: Air, 882 liveness: Liveness, 883 code: *std.ArrayListUnmanaged(u8), 884 debug_output: link.File.DebugInfoOutput, 885 ) codegen.CodeGenError!void { 886 const zcu = pt.zcu; 887 const comp = zcu.comp; 888 const gpa = zcu.gpa; 889 const ip = &zcu.intern_pool; 890 const func = zcu.funcInfo(func_index); 891 const fn_type: Type = .fromInterned(func.ty); 892 const mod = zcu.navFileScope(func.owner_nav).mod; 893 894 var function: CodeGen = .{ 895 .gpa = gpa, 896 .pt = pt, 897 .air = air, 898 .liveness = liveness, 899 .target = &mod.resolved_target.result, 900 .mod = mod, 901 .bin_file = bin_file, 902 .debug_output = debug_output, 903 .owner = .{ .nav_index = func.owner_nav }, 904 .inline_func = func_index, 905 .arg_index = undefined, 906 .args = undefined, // populated after `resolveCallingConventionValues` 907 .va_info = undefined, // populated after `resolveCallingConventionValues` 908 .ret_mcv = undefined, // populated after `resolveCallingConventionValues` 909 .err_ret_trace_reg = undefined, // populated after `resolveCallingConventionValues` 910 .fn_type = fn_type, 911 .src_loc = src_loc, 912 .end_di_line = func.rbrace_line, 913 .end_di_column = func.rbrace_column, 914 }; 915 defer { 916 function.frame_allocs.deinit(gpa); 917 function.free_frame_indices.deinit(gpa); 918 function.frame_locs.deinit(gpa); 919 function.loops.deinit(gpa); 920 function.loop_switches.deinit(gpa); 921 var block_it = function.blocks.valueIterator(); 922 while (block_it.next()) |block| block.deinit(gpa); 923 function.blocks.deinit(gpa); 924 function.inst_tracking.deinit(gpa); 925 function.const_tracking.deinit(gpa); 926 function.epilogue_relocs.deinit(gpa); 927 function.mir_instructions.deinit(gpa); 928 function.mir_extra.deinit(gpa); 929 function.mir_table.deinit(gpa); 930 } 931 try function.inst_tracking.ensureTotalCapacity(gpa, Temp.Index.max); 932 for (0..Temp.Index.max) |temp_index| { 933 const temp: Temp.Index = @enumFromInt(temp_index); 934 function.inst_tracking.putAssumeCapacityNoClobber(temp.toIndex(), .init(.none)); 935 } 936 937 wip_mir_log.debug("{}:", .{fmtNav(func.owner_nav, ip)}); 938 939 try function.frame_allocs.resize(gpa, FrameIndex.named_count); 940 function.frame_allocs.set( 941 @intFromEnum(FrameIndex.stack_frame), 942 .init(.{ .size = 0, .alignment = .@"1" }), 943 ); 944 function.frame_allocs.set( 945 @intFromEnum(FrameIndex.call_frame), 946 .init(.{ .size = 0, .alignment = .@"1" }), 947 ); 948 949 const fn_info = zcu.typeToFunc(fn_type).?; 950 var call_info = function.resolveCallingConventionValues(fn_info, &.{}, .args_frame) catch |err| switch (err) { 951 error.CodegenFail => return error.CodegenFail, 952 else => |e| return e, 953 }; 954 defer call_info.deinit(&function); 955 956 function.args = call_info.args; 957 function.ret_mcv = call_info.return_value; 958 function.err_ret_trace_reg = call_info.err_ret_trace_reg; 959 function.frame_allocs.set(@intFromEnum(FrameIndex.ret_addr), .init(.{ 960 .size = Type.usize.abiSize(zcu), 961 .alignment = Type.usize.abiAlignment(zcu).min(call_info.stack_align), 962 })); 963 function.frame_allocs.set(@intFromEnum(FrameIndex.base_ptr), .init(.{ 964 .size = Type.usize.abiSize(zcu), 965 .alignment = call_info.stack_align.min( 966 .fromNonzeroByteUnits(function.target.stackAlignment()), 967 ), 968 })); 969 function.frame_allocs.set( 970 @intFromEnum(FrameIndex.args_frame), 971 .init(.{ 972 .size = call_info.stack_byte_count, 973 .alignment = call_info.stack_align, 974 }), 975 ); 976 function.va_info = switch (fn_info.cc) { 977 else => undefined, 978 .x86_64_sysv => .{ .sysv = .{ 979 .gp_count = call_info.gp_count, 980 .fp_count = call_info.fp_count, 981 .overflow_arg_area = .{ .index = .args_frame, .off = call_info.stack_byte_count }, 982 .reg_save_area = undefined, 983 } }, 984 .x86_64_win => .{ .win64 = .{} }, 985 }; 986 if (call_info.err_ret_trace_reg != .none) { 987 function.register_manager.getRegAssumeFree(call_info.err_ret_trace_reg, err_ret_trace_index); 988 try function.inst_tracking.putNoClobber( 989 gpa, 990 err_ret_trace_index, 991 .init(.{ .register = call_info.err_ret_trace_reg }), 992 ); 993 } 994 995 function.gen() catch |err| switch (err) { 996 error.CodegenFail => return error.CodegenFail, 997 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), 998 else => |e| return e, 999 }; 1000 1001 var mir: Mir = .{ 1002 .instructions = function.mir_instructions.toOwnedSlice(), 1003 .extra = try function.mir_extra.toOwnedSlice(gpa), 1004 .table = try function.mir_table.toOwnedSlice(gpa), 1005 .frame_locs = function.frame_locs.toOwnedSlice(), 1006 }; 1007 defer mir.deinit(gpa); 1008 1009 var emit: Emit = .{ 1010 .air = function.air, 1011 .lower = .{ 1012 .bin_file = bin_file, 1013 .target = function.target, 1014 .allocator = gpa, 1015 .mir = mir, 1016 .cc = fn_info.cc, 1017 .src_loc = src_loc, 1018 .output_mode = comp.config.output_mode, 1019 .link_mode = comp.config.link_mode, 1020 .pic = mod.pic, 1021 }, 1022 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { 1023 error.CodegenFail => return error.CodegenFail, 1024 else => |e| return e, 1025 }, 1026 .debug_output = debug_output, 1027 .code = code, 1028 .prev_di_loc = .{ 1029 .line = func.lbrace_line, 1030 .column = func.lbrace_column, 1031 .is_stmt = switch (debug_output) { 1032 .dwarf => |dwarf| dwarf.dwarf.debug_line.header.default_is_stmt, 1033 .plan9 => undefined, 1034 .none => undefined, 1035 }, 1036 }, 1037 .prev_di_pc = 0, 1038 }; 1039 emit.emitMir() catch |err| switch (err) { 1040 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), 1041 1042 error.InvalidInstruction, error.CannotEncode => |e| return function.fail("emit MIR failed: {s} (Zig compiler bug)", .{@errorName(e)}), 1043 else => |e| return function.fail("emit MIR failed: {s}", .{@errorName(e)}), 1044 }; 1045 } 1046 1047 pub fn generateLazy( 1048 bin_file: *link.File, 1049 pt: Zcu.PerThread, 1050 src_loc: Zcu.LazySrcLoc, 1051 lazy_sym: link.File.LazySymbol, 1052 code: *std.ArrayListUnmanaged(u8), 1053 debug_output: link.File.DebugInfoOutput, 1054 ) codegen.CodeGenError!void { 1055 const comp = bin_file.comp; 1056 const gpa = comp.gpa; 1057 // This function is for generating global code, so we use the root module. 1058 const mod = comp.root_mod; 1059 var function: CodeGen = .{ 1060 .gpa = gpa, 1061 .pt = pt, 1062 .air = undefined, 1063 .liveness = undefined, 1064 .target = &mod.resolved_target.result, 1065 .mod = mod, 1066 .bin_file = bin_file, 1067 .debug_output = debug_output, 1068 .owner = .{ .lazy_sym = lazy_sym }, 1069 .inline_func = undefined, 1070 .arg_index = undefined, 1071 .args = undefined, 1072 .va_info = undefined, 1073 .ret_mcv = undefined, 1074 .err_ret_trace_reg = undefined, 1075 .fn_type = undefined, 1076 .src_loc = src_loc, 1077 .end_di_line = undefined, // no debug info yet 1078 .end_di_column = undefined, // no debug info yet 1079 }; 1080 defer { 1081 function.inst_tracking.deinit(gpa); 1082 function.mir_instructions.deinit(gpa); 1083 function.mir_extra.deinit(gpa); 1084 function.mir_table.deinit(gpa); 1085 } 1086 try function.inst_tracking.ensureTotalCapacity(gpa, Temp.Index.max); 1087 for (0..Temp.Index.max) |temp_index| { 1088 const temp: Temp.Index = @enumFromInt(temp_index); 1089 function.inst_tracking.putAssumeCapacityNoClobber(temp.toIndex(), .init(.none)); 1090 } 1091 1092 function.genLazy(lazy_sym) catch |err| switch (err) { 1093 error.CodegenFail => return error.CodegenFail, 1094 error.OutOfRegisters => return function.fail("ran out of registers (Zig compiler bug)", .{}), 1095 else => |e| return e, 1096 }; 1097 1098 var mir: Mir = .{ 1099 .instructions = function.mir_instructions.toOwnedSlice(), 1100 .extra = try function.mir_extra.toOwnedSlice(gpa), 1101 .table = try function.mir_table.toOwnedSlice(gpa), 1102 .frame_locs = function.frame_locs.toOwnedSlice(), 1103 }; 1104 defer mir.deinit(gpa); 1105 1106 var emit: Emit = .{ 1107 .air = function.air, 1108 .lower = .{ 1109 .bin_file = bin_file, 1110 .target = function.target, 1111 .allocator = gpa, 1112 .mir = mir, 1113 .cc = .auto, 1114 .src_loc = src_loc, 1115 .output_mode = comp.config.output_mode, 1116 .link_mode = comp.config.link_mode, 1117 .pic = mod.pic, 1118 }, 1119 .atom_index = function.owner.getSymbolIndex(&function) catch |err| switch (err) { 1120 error.CodegenFail => return error.CodegenFail, 1121 else => |e| return e, 1122 }, 1123 .debug_output = debug_output, 1124 .code = code, 1125 .prev_di_loc = undefined, // no debug info yet 1126 .prev_di_pc = undefined, // no debug info yet 1127 }; 1128 emit.emitMir() catch |err| switch (err) { 1129 error.LowerFail, error.EmitFail => return function.failMsg(emit.lower.err_msg.?), 1130 error.InvalidInstruction => return function.fail("failed to find a viable x86 instruction (Zig compiler bug)", .{}), 1131 error.CannotEncode => return function.fail("failed to encode x86 instruction (Zig compiler bug)", .{}), 1132 else => |e| return function.fail("failed to emit MIR: {s}", .{@errorName(e)}), 1133 }; 1134 } 1135 1136 const FormatNavData = struct { 1137 ip: *const InternPool, 1138 nav_index: InternPool.Nav.Index, 1139 }; 1140 fn formatNav( 1141 data: FormatNavData, 1142 comptime _: []const u8, 1143 _: std.fmt.FormatOptions, 1144 writer: anytype, 1145 ) @TypeOf(writer).Error!void { 1146 try writer.print("{}", .{data.ip.getNav(data.nav_index).fqn.fmt(data.ip)}); 1147 } 1148 fn fmtNav(nav_index: InternPool.Nav.Index, ip: *const InternPool) std.fmt.Formatter(formatNav) { 1149 return .{ .data = .{ 1150 .ip = ip, 1151 .nav_index = nav_index, 1152 } }; 1153 } 1154 1155 const FormatAirData = struct { 1156 self: *CodeGen, 1157 inst: Air.Inst.Index, 1158 }; 1159 fn formatAir( 1160 data: FormatAirData, 1161 comptime _: []const u8, 1162 _: std.fmt.FormatOptions, 1163 writer: anytype, 1164 ) @TypeOf(writer).Error!void { 1165 @import("../../print_air.zig").dumpInst( 1166 data.inst, 1167 data.self.pt, 1168 data.self.air, 1169 data.self.liveness, 1170 ); 1171 } 1172 fn fmtAir(self: *CodeGen, inst: Air.Inst.Index) std.fmt.Formatter(formatAir) { 1173 return .{ .data = .{ .self = self, .inst = inst } }; 1174 } 1175 1176 const FormatWipMirData = struct { 1177 self: *CodeGen, 1178 inst: Mir.Inst.Index, 1179 }; 1180 fn formatWipMir( 1181 data: FormatWipMirData, 1182 comptime _: []const u8, 1183 _: std.fmt.FormatOptions, 1184 writer: anytype, 1185 ) @TypeOf(writer).Error!void { 1186 const comp = data.self.bin_file.comp; 1187 const mod = comp.root_mod; 1188 var lower: Lower = .{ 1189 .bin_file = data.self.bin_file, 1190 .target = data.self.target, 1191 .allocator = data.self.gpa, 1192 .mir = .{ 1193 .instructions = data.self.mir_instructions.slice(), 1194 .extra = data.self.mir_extra.items, 1195 .table = data.self.mir_table.items, 1196 .frame_locs = (std.MultiArrayList(Mir.FrameLoc){}).slice(), 1197 }, 1198 .cc = .auto, 1199 .src_loc = data.self.src_loc, 1200 .output_mode = comp.config.output_mode, 1201 .link_mode = comp.config.link_mode, 1202 .pic = mod.pic, 1203 }; 1204 var first = true; 1205 for ((lower.lowerMir(data.inst) catch |err| switch (err) { 1206 error.LowerFail => { 1207 defer { 1208 lower.err_msg.?.deinit(data.self.gpa); 1209 lower.err_msg = null; 1210 } 1211 try writer.writeAll(lower.err_msg.?.msg); 1212 return; 1213 }, 1214 error.OutOfMemory, error.InvalidInstruction, error.CannotEncode => |e| { 1215 try writer.writeAll(switch (e) { 1216 error.OutOfMemory => "Out of memory", 1217 error.InvalidInstruction => "CodeGen failed to find a viable instruction.", 1218 error.CannotEncode => "CodeGen failed to encode the instruction.", 1219 }); 1220 return; 1221 }, 1222 else => |e| return e, 1223 }).insts) |lowered_inst| { 1224 if (!first) try writer.writeAll("\ndebug(wip_mir): "); 1225 try writer.print(" | {}", .{lowered_inst}); 1226 first = false; 1227 } 1228 if (first) { 1229 const ip = &data.self.pt.zcu.intern_pool; 1230 const mir_inst = lower.mir.instructions.get(data.inst); 1231 try writer.print(" | .{s}", .{@tagName(mir_inst.ops)}); 1232 switch (mir_inst.ops) { 1233 else => unreachable, 1234 .pseudo_dbg_prologue_end_none, 1235 .pseudo_dbg_epilogue_begin_none, 1236 .pseudo_dbg_enter_block_none, 1237 .pseudo_dbg_leave_block_none, 1238 .pseudo_dbg_var_args_none, 1239 .pseudo_dead_none, 1240 => {}, 1241 .pseudo_dbg_line_stmt_line_column, .pseudo_dbg_line_line_column => try writer.print( 1242 " {[line]d}, {[column]d}", 1243 mir_inst.data.line_column, 1244 ), 1245 .pseudo_dbg_enter_inline_func, .pseudo_dbg_leave_inline_func => try writer.print(" {}", .{ 1246 ip.getNav(ip.indexToKey(mir_inst.data.func).func.owner_nav).name.fmt(ip), 1247 }), 1248 .pseudo_dbg_local_a => try writer.print(" {}", .{mir_inst.data.a.air_inst}), 1249 .pseudo_dbg_local_ai_s => try writer.print(" {}, {d}", .{ 1250 mir_inst.data.ai.air_inst, 1251 @as(i32, @bitCast(mir_inst.data.ai.i)), 1252 }), 1253 .pseudo_dbg_local_ai_u => try writer.print(" {}, {d}", .{ 1254 mir_inst.data.ai.air_inst, 1255 mir_inst.data.ai.i, 1256 }), 1257 .pseudo_dbg_local_ai_64 => try writer.print(" {}, {d}", .{ 1258 mir_inst.data.ai.air_inst, 1259 lower.mir.extraData(Mir.Imm64, mir_inst.data.ai.i).data.decode(), 1260 }), 1261 .pseudo_dbg_local_as => { 1262 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ 1263 .base = .{ .reloc = mir_inst.data.as.sym_index }, 1264 }) }; 1265 try writer.print(" {}, {}", .{ mir_inst.data.as.air_inst, mem_op.fmt(.m) }); 1266 }, 1267 .pseudo_dbg_local_aso => { 1268 const sym_off = lower.mir.extraData(bits.SymbolOffset, mir_inst.data.ax.payload).data; 1269 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ 1270 .base = .{ .reloc = sym_off.sym_index }, 1271 .disp = sym_off.off, 1272 }) }; 1273 try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); 1274 }, 1275 .pseudo_dbg_local_aro => { 1276 const air_off = lower.mir.extraData(Mir.AirOffset, mir_inst.data.rx.payload).data; 1277 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ 1278 .base = .{ .reg = mir_inst.data.rx.r1 }, 1279 .disp = air_off.off, 1280 }) }; 1281 try writer.print(" {}, {}", .{ air_off.air_inst, mem_op.fmt(.m) }); 1282 }, 1283 .pseudo_dbg_local_af => { 1284 const frame_addr = lower.mir.extraData(bits.FrameAddr, mir_inst.data.ax.payload).data; 1285 const mem_op: encoder.Instruction.Operand = .{ .mem = .initSib(.qword, .{ 1286 .base = .{ .frame = frame_addr.index }, 1287 .disp = frame_addr.off, 1288 }) }; 1289 try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); 1290 }, 1291 .pseudo_dbg_local_am => { 1292 const mem_op: encoder.Instruction.Operand = .{ 1293 .mem = lower.mir.extraData(Mir.Memory, mir_inst.data.ax.payload).data.decode(), 1294 }; 1295 try writer.print(" {}, {}", .{ mir_inst.data.ax.air_inst, mem_op.fmt(.m) }); 1296 }, 1297 } 1298 } 1299 } 1300 fn fmtWipMir(self: *CodeGen, inst: Mir.Inst.Index) std.fmt.Formatter(formatWipMir) { 1301 return .{ .data = .{ .self = self, .inst = inst } }; 1302 } 1303 1304 const FormatTrackingData = struct { 1305 self: *CodeGen, 1306 }; 1307 fn formatTracking( 1308 data: FormatTrackingData, 1309 comptime _: []const u8, 1310 _: std.fmt.FormatOptions, 1311 writer: anytype, 1312 ) @TypeOf(writer).Error!void { 1313 var it = data.self.inst_tracking.iterator(); 1314 while (it.next()) |entry| try writer.print("\n{} = {}", .{ entry.key_ptr.*, entry.value_ptr.* }); 1315 } 1316 fn fmtTracking(self: *CodeGen) std.fmt.Formatter(formatTracking) { 1317 return .{ .data = .{ .self = self } }; 1318 } 1319 1320 fn addInst(self: *CodeGen, inst: Mir.Inst) error{OutOfMemory}!Mir.Inst.Index { 1321 const gpa = self.gpa; 1322 try self.mir_instructions.ensureUnusedCapacity(gpa, 1); 1323 const result_index: Mir.Inst.Index = @intCast(self.mir_instructions.len); 1324 self.mir_instructions.appendAssumeCapacity(inst); 1325 if (inst.ops != .pseudo_dead_none) wip_mir_log.debug("{}", .{self.fmtWipMir(result_index)}); 1326 return result_index; 1327 } 1328 1329 fn addExtra(self: *CodeGen, extra: anytype) Allocator.Error!u32 { 1330 const fields = std.meta.fields(@TypeOf(extra)); 1331 try self.mir_extra.ensureUnusedCapacity(self.gpa, fields.len); 1332 return self.addExtraAssumeCapacity(extra); 1333 } 1334 1335 fn addExtraAssumeCapacity(self: *CodeGen, extra: anytype) u32 { 1336 const fields = std.meta.fields(@TypeOf(extra)); 1337 const result: u32 = @intCast(self.mir_extra.items.len); 1338 inline for (fields) |field| { 1339 self.mir_extra.appendAssumeCapacity(switch (field.type) { 1340 u32 => @field(extra, field.name), 1341 i32, Mir.Memory.Info => @bitCast(@field(extra, field.name)), 1342 bits.FrameIndex => @intFromEnum(@field(extra, field.name)), 1343 else => @compileError("bad field type: " ++ field.name ++ ": " ++ @typeName(field.type)), 1344 }); 1345 } 1346 return result; 1347 } 1348 1349 fn asmOps(self: *CodeGen, tag: Mir.Inst.FixedTag, ops: [4]Operand) !void { 1350 return switch (ops[0]) { 1351 .none => self.asmOpOnly(tag), 1352 .reg => |reg0| switch (ops[1]) { 1353 .none => self.asmRegister(tag, reg0), 1354 .reg => |reg1| switch (ops[2]) { 1355 .none => self.asmRegisterRegister(tag, reg0, reg1), 1356 .reg => |reg2| switch (ops[3]) { 1357 .none => self.asmRegisterRegisterRegister(tag, reg0, reg1, reg2), 1358 .reg => |reg3| self.asmRegisterRegisterRegisterRegister(tag, reg0, reg1, reg2, reg3), 1359 .imm => |imm3| self.asmRegisterRegisterRegisterImmediate(tag, reg0, reg1, reg2, imm3), 1360 else => error.InvalidInstruction, 1361 }, 1362 .mem => |mem2| switch (ops[3]) { 1363 .none => self.asmRegisterRegisterMemory(tag, reg0, reg1, mem2), 1364 .reg => |reg3| self.asmRegisterRegisterMemoryRegister(tag, reg0, reg1, mem2, reg3), 1365 .imm => |imm3| self.asmRegisterRegisterMemoryImmediate(tag, reg0, reg1, mem2, imm3), 1366 else => error.InvalidInstruction, 1367 }, 1368 .imm => |imm2| switch (ops[3]) { 1369 .none => self.asmRegisterRegisterImmediate(tag, reg0, reg1, imm2), 1370 else => error.InvalidInstruction, 1371 }, 1372 else => error.InvalidInstruction, 1373 }, 1374 .mem => |mem1| switch (ops[2]) { 1375 .none => self.asmRegisterMemory(tag, reg0, mem1), 1376 .reg => |reg2| switch (ops[3]) { 1377 .none => self.asmRegisterMemoryRegister(tag, reg0, mem1, reg2), 1378 else => error.InvalidInstruction, 1379 }, 1380 .imm => |imm2| switch (ops[3]) { 1381 .none => self.asmRegisterMemoryImmediate(tag, reg0, mem1, imm2), 1382 else => error.InvalidInstruction, 1383 }, 1384 else => error.InvalidInstruction, 1385 }, 1386 .imm => |imm1| switch (ops[2]) { 1387 .none => self.asmRegisterImmediate(tag, reg0, imm1), 1388 else => error.InvalidInstruction, 1389 }, 1390 else => error.InvalidInstruction, 1391 }, 1392 .mem => |mem0| switch (ops[1]) { 1393 .none => self.asmMemory(tag, mem0), 1394 .reg => |reg1| switch (ops[2]) { 1395 .none => self.asmMemoryRegister(tag, mem0, reg1), 1396 .reg => |reg2| switch (ops[3]) { 1397 .none => self.asmMemoryRegisterRegister(tag, mem0, reg1, reg2), 1398 else => error.InvalidInstruction, 1399 }, 1400 .imm => |imm2| switch (ops[3]) { 1401 .none => self.asmMemoryRegisterImmediate(tag, mem0, reg1, imm2), 1402 else => error.InvalidInstruction, 1403 }, 1404 else => error.InvalidInstruction, 1405 }, 1406 .imm => |imm1| switch (ops[2]) { 1407 .none => self.asmMemoryImmediate(tag, mem0, imm1), 1408 else => error.InvalidInstruction, 1409 }, 1410 else => error.InvalidInstruction, 1411 }, 1412 .imm => |imm0| switch (ops[1]) { 1413 .none => self.asmImmediate(tag, imm0), 1414 .reg => |reg1| switch (ops[2]) { 1415 .none => self.asmImmediateRegister(tag, imm0, reg1), 1416 else => error.InvalidInstruction, 1417 }, 1418 .imm => |imm1| switch (ops[2]) { 1419 .none => self.asmImmediateImmediate(tag, imm0, imm1), 1420 else => error.InvalidInstruction, 1421 }, 1422 else => error.InvalidInstruction, 1423 }, 1424 .inst => |inst0| switch (ops[1]) { 1425 .none => self.asmReloc(tag, inst0), 1426 else => error.InvalidInstruction, 1427 }, 1428 }; 1429 } 1430 1431 /// A `cc` of `.z_and_np` clobbers `reg2`! 1432 fn asmCmovccRegisterRegister(self: *CodeGen, cc: Condition, reg1: Register, reg2: Register) !void { 1433 if (self.hasFeature(.cmov)) _ = try self.addInst(.{ 1434 .tag = switch (cc) { 1435 else => .cmov, 1436 .z_and_np, .nz_or_p => .pseudo, 1437 }, 1438 .ops = switch (cc) { 1439 else => .rr, 1440 .z_and_np => .pseudo_cmov_z_and_np_rr, 1441 .nz_or_p => .pseudo_cmov_nz_or_p_rr, 1442 }, 1443 .data = .{ .rr = .{ 1444 .fixes = switch (cc) { 1445 else => .fromCond(cc), 1446 .z_and_np, .nz_or_p => ._, 1447 }, 1448 .r1 = reg1, 1449 .r2 = reg2, 1450 } }, 1451 }) else { 1452 const reloc = try self.asmJccReloc(cc.negate(), undefined); 1453 try self.asmRegisterRegister(.{ ._, .mov }, reg1, reg2); 1454 self.performReloc(reloc); 1455 } 1456 } 1457 1458 /// A `cc` of `.z_and_np` is not supported by this encoding! 1459 fn asmCmovccRegisterMemory(self: *CodeGen, cc: Condition, reg: Register, m: Memory) !void { 1460 if (self.hasFeature(.cmov)) _ = try self.addInst(.{ 1461 .tag = switch (cc) { 1462 else => .cmov, 1463 .z_and_np => unreachable, 1464 .nz_or_p => .pseudo, 1465 }, 1466 .ops = switch (cc) { 1467 else => .rm, 1468 .z_and_np => unreachable, 1469 .nz_or_p => .pseudo_cmov_nz_or_p_rm, 1470 }, 1471 .data = .{ .rx = .{ 1472 .fixes = switch (cc) { 1473 else => .fromCond(cc), 1474 .z_and_np => unreachable, 1475 .nz_or_p => ._, 1476 }, 1477 .r1 = reg, 1478 .payload = try self.addExtra(Mir.Memory.encode(m)), 1479 } }, 1480 }) else { 1481 const reloc = try self.asmJccReloc(cc.negate(), undefined); 1482 try self.asmRegisterMemory(.{ ._, .mov }, reg, m); 1483 self.performReloc(reloc); 1484 } 1485 } 1486 1487 fn asmSetccRegister(self: *CodeGen, cc: Condition, reg: Register) !void { 1488 _ = try self.addInst(.{ 1489 .tag = switch (cc) { 1490 else => .set, 1491 .z_and_np, .nz_or_p => .pseudo, 1492 }, 1493 .ops = switch (cc) { 1494 else => .r, 1495 .z_and_np => .pseudo_set_z_and_np_r, 1496 .nz_or_p => .pseudo_set_nz_or_p_r, 1497 }, 1498 .data = switch (cc) { 1499 else => .{ .r = .{ 1500 .fixes = .fromCond(cc), 1501 .r1 = reg, 1502 } }, 1503 .z_and_np, .nz_or_p => .{ .rr = .{ 1504 .r1 = reg, 1505 .r2 = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to8(), 1506 } }, 1507 }, 1508 }); 1509 } 1510 1511 fn asmSetccMemory(self: *CodeGen, cc: Condition, m: Memory) !void { 1512 const payload = try self.addExtra(Mir.Memory.encode(m)); 1513 _ = try self.addInst(.{ 1514 .tag = switch (cc) { 1515 else => .set, 1516 .z_and_np, .nz_or_p => .pseudo, 1517 }, 1518 .ops = switch (cc) { 1519 else => .m, 1520 .z_and_np => .pseudo_set_z_and_np_m, 1521 .nz_or_p => .pseudo_set_nz_or_p_m, 1522 }, 1523 .data = switch (cc) { 1524 else => .{ .x = .{ 1525 .fixes = .fromCond(cc), 1526 .payload = payload, 1527 } }, 1528 .z_and_np, .nz_or_p => .{ .rx = .{ 1529 .r1 = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to8(), 1530 .payload = payload, 1531 } }, 1532 }, 1533 }); 1534 } 1535 1536 fn asmJmpReloc(self: *CodeGen, target: Mir.Inst.Index) !Mir.Inst.Index { 1537 return self.addInst(.{ 1538 .tag = .j, 1539 .ops = .inst, 1540 .data = .{ .inst = .{ 1541 .fixes = ._mp, 1542 .inst = target, 1543 } }, 1544 }); 1545 } 1546 1547 fn asmJccReloc(self: *CodeGen, cc: Condition, target: Mir.Inst.Index) !Mir.Inst.Index { 1548 return self.addInst(.{ 1549 .tag = switch (cc) { 1550 else => .j, 1551 .z_and_np, .nz_or_p => .pseudo, 1552 }, 1553 .ops = switch (cc) { 1554 else => .inst, 1555 .z_and_np => .pseudo_j_z_and_np_inst, 1556 .nz_or_p => .pseudo_j_nz_or_p_inst, 1557 }, 1558 .data = .{ .inst = .{ 1559 .fixes = switch (cc) { 1560 else => .fromCond(cc), 1561 .z_and_np, .nz_or_p => ._, 1562 }, 1563 .inst = target, 1564 } }, 1565 }); 1566 } 1567 1568 fn asmReloc(self: *CodeGen, tag: Mir.Inst.FixedTag, target: Mir.Inst.Index) !void { 1569 _ = try self.addInst(.{ 1570 .tag = tag[1], 1571 .ops = .inst, 1572 .data = .{ .inst = .{ 1573 .fixes = tag[0], 1574 .inst = target, 1575 } }, 1576 }); 1577 } 1578 1579 fn asmPlaceholder(self: *CodeGen) !Mir.Inst.Index { 1580 return self.addInst(.{ 1581 .tag = .pseudo, 1582 .ops = .pseudo_dead_none, 1583 .data = undefined, 1584 }); 1585 } 1586 1587 const MirTagAir = enum { dbg_local }; 1588 1589 fn asmAir(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index) !void { 1590 _ = try self.addInst(.{ 1591 .tag = .pseudo, 1592 .ops = switch (tag) { 1593 .dbg_local => .pseudo_dbg_local_a, 1594 }, 1595 .data = .{ .a = .{ .air_inst = inst } }, 1596 }); 1597 } 1598 1599 fn asmAirImmediate(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, imm: Immediate) !void { 1600 switch (imm) { 1601 .signed => |s| _ = try self.addInst(.{ 1602 .tag = .pseudo, 1603 .ops = switch (tag) { 1604 .dbg_local => .pseudo_dbg_local_ai_s, 1605 }, 1606 .data = .{ .ai = .{ 1607 .air_inst = inst, 1608 .i = @bitCast(s), 1609 } }, 1610 }), 1611 .unsigned => |u| _ = if (std.math.cast(u32, u)) |small| try self.addInst(.{ 1612 .tag = .pseudo, 1613 .ops = switch (tag) { 1614 .dbg_local => .pseudo_dbg_local_ai_u, 1615 }, 1616 .data = .{ .ai = .{ 1617 .air_inst = inst, 1618 .i = small, 1619 } }, 1620 }) else try self.addInst(.{ 1621 .tag = .pseudo, 1622 .ops = switch (tag) { 1623 .dbg_local => .pseudo_dbg_local_ai_64, 1624 }, 1625 .data = .{ .ai = .{ 1626 .air_inst = inst, 1627 .i = try self.addExtra(Mir.Imm64.encode(u)), 1628 } }, 1629 }), 1630 .reloc => |sym_off| _ = if (sym_off.off == 0) try self.addInst(.{ 1631 .tag = .pseudo, 1632 .ops = switch (tag) { 1633 .dbg_local => .pseudo_dbg_local_as, 1634 }, 1635 .data = .{ .as = .{ 1636 .air_inst = inst, 1637 .sym_index = sym_off.sym_index, 1638 } }, 1639 }) else try self.addInst(.{ 1640 .tag = .pseudo, 1641 .ops = switch (tag) { 1642 .dbg_local => .pseudo_dbg_local_aso, 1643 }, 1644 .data = .{ .ax = .{ 1645 .air_inst = inst, 1646 .payload = try self.addExtra(sym_off), 1647 } }, 1648 }), 1649 } 1650 } 1651 1652 fn asmAirRegisterImmediate( 1653 self: *CodeGen, 1654 tag: MirTagAir, 1655 inst: Air.Inst.Index, 1656 reg: Register, 1657 imm: Immediate, 1658 ) !void { 1659 _ = try self.addInst(.{ 1660 .tag = .pseudo, 1661 .ops = switch (tag) { 1662 .dbg_local => .pseudo_dbg_local_aro, 1663 }, 1664 .data = .{ .rx = .{ 1665 .r1 = reg, 1666 .payload = try self.addExtra(Mir.AirOffset{ 1667 .air_inst = inst, 1668 .off = imm.signed, 1669 }), 1670 } }, 1671 }); 1672 } 1673 1674 fn asmAirFrameAddress( 1675 self: *CodeGen, 1676 tag: MirTagAir, 1677 inst: Air.Inst.Index, 1678 frame_addr: bits.FrameAddr, 1679 ) !void { 1680 _ = try self.addInst(.{ 1681 .tag = .pseudo, 1682 .ops = switch (tag) { 1683 .dbg_local => .pseudo_dbg_local_af, 1684 }, 1685 .data = .{ .ax = .{ 1686 .air_inst = inst, 1687 .payload = try self.addExtra(frame_addr), 1688 } }, 1689 }); 1690 } 1691 1692 fn asmAirMemory(self: *CodeGen, tag: MirTagAir, inst: Air.Inst.Index, m: Memory) !void { 1693 _ = try self.addInst(.{ 1694 .tag = .pseudo, 1695 .ops = switch (tag) { 1696 .dbg_local => .pseudo_dbg_local_am, 1697 }, 1698 .data = .{ .ax = .{ 1699 .air_inst = inst, 1700 .payload = try self.addExtra(Mir.Memory.encode(m)), 1701 } }, 1702 }); 1703 } 1704 1705 fn asmOpOnly(self: *CodeGen, tag: Mir.Inst.FixedTag) !void { 1706 _ = try self.addInst(.{ 1707 .tag = tag[1], 1708 .ops = .none, 1709 .data = .{ .none = .{ 1710 .fixes = tag[0], 1711 } }, 1712 }); 1713 } 1714 1715 fn asmPseudo(self: *CodeGen, ops: Mir.Inst.Ops) !void { 1716 assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and 1717 std.mem.endsWith(u8, @tagName(ops), "_none")); 1718 _ = try self.addInst(.{ 1719 .tag = .pseudo, 1720 .ops = ops, 1721 .data = undefined, 1722 }); 1723 } 1724 1725 fn asmPseudoRegister(self: *CodeGen, ops: Mir.Inst.Ops, reg: Register) !void { 1726 assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and 1727 std.mem.endsWith(u8, @tagName(ops), "_r")); 1728 _ = try self.addInst(.{ 1729 .tag = .pseudo, 1730 .ops = ops, 1731 .data = .{ .r = .{ .r1 = reg } }, 1732 }); 1733 } 1734 1735 fn asmPseudoImmediate(self: *CodeGen, ops: Mir.Inst.Ops, imm: Immediate) !void { 1736 assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and 1737 std.mem.endsWith(u8, @tagName(ops), "_i_s")); 1738 _ = try self.addInst(.{ 1739 .tag = .pseudo, 1740 .ops = ops, 1741 .data = .{ .i = .{ .i = @bitCast(imm.signed) } }, 1742 }); 1743 } 1744 1745 fn asmPseudoRegisterRegister(self: *CodeGen, ops: Mir.Inst.Ops, reg1: Register, reg2: Register) !void { 1746 assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and 1747 std.mem.endsWith(u8, @tagName(ops), "_rr")); 1748 _ = try self.addInst(.{ 1749 .tag = .pseudo, 1750 .ops = ops, 1751 .data = .{ .rr = .{ .r1 = reg1, .r2 = reg2 } }, 1752 }); 1753 } 1754 1755 fn asmPseudoRegisterImmediate(self: *CodeGen, ops: Mir.Inst.Ops, reg: Register, imm: Immediate) !void { 1756 assert(std.mem.startsWith(u8, @tagName(ops), "pseudo_") and 1757 std.mem.endsWith(u8, @tagName(ops), "_ri_s")); 1758 _ = try self.addInst(.{ 1759 .tag = .pseudo, 1760 .ops = ops, 1761 .data = .{ .ri = .{ .r1 = reg, .i = @bitCast(imm.signed) } }, 1762 }); 1763 } 1764 1765 fn asmRegister(self: *CodeGen, tag: Mir.Inst.FixedTag, reg: Register) !void { 1766 _ = try self.addInst(.{ 1767 .tag = tag[1], 1768 .ops = .r, 1769 .data = .{ .r = .{ 1770 .fixes = tag[0], 1771 .r1 = reg, 1772 } }, 1773 }); 1774 } 1775 1776 fn asmImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, imm: Immediate) !void { 1777 _ = try self.addInst(.{ 1778 .tag = tag[1], 1779 .ops = switch (imm) { 1780 .signed => .i_s, 1781 .unsigned => .i_u, 1782 .reloc => .rel, 1783 }, 1784 .data = switch (imm) { 1785 .reloc => |sym_off| reloc: { 1786 assert(tag[0] == ._); 1787 break :reloc .{ .reloc = sym_off }; 1788 }, 1789 .signed, .unsigned => .{ .i = .{ 1790 .fixes = tag[0], 1791 .i = switch (imm) { 1792 .signed => |s| @bitCast(s), 1793 .unsigned => |u| @intCast(u), 1794 .reloc => unreachable, 1795 }, 1796 } }, 1797 }, 1798 }); 1799 } 1800 1801 fn asmImmediateRegister(self: *CodeGen, tag: Mir.Inst.FixedTag, imm: Immediate, reg: Register) !void { 1802 _ = try self.addInst(.{ 1803 .tag = tag[1], 1804 .ops = .ir, 1805 .data = .{ .ri = .{ 1806 .fixes = tag[0], 1807 .r1 = reg, 1808 .i = @as(u8, switch (imm) { 1809 .signed => |s| @bitCast(@as(i8, @intCast(s))), 1810 .unsigned => |u| @intCast(u), 1811 .reloc => unreachable, 1812 }), 1813 } }, 1814 }); 1815 } 1816 1817 fn asmImmediateImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, imm1: Immediate, imm2: Immediate) !void { 1818 _ = try self.addInst(.{ 1819 .tag = tag[1], 1820 .ops = .ii, 1821 .data = .{ .ii = .{ 1822 .fixes = tag[0], 1823 .i1 = switch (imm1) { 1824 .signed => |s| @bitCast(@as(i16, @intCast(s))), 1825 .unsigned => |u| @intCast(u), 1826 .reloc => unreachable, 1827 }, 1828 .i2 = switch (imm2) { 1829 .signed => |s| @bitCast(@as(i8, @intCast(s))), 1830 .unsigned => |u| @intCast(u), 1831 .reloc => unreachable, 1832 }, 1833 } }, 1834 }); 1835 } 1836 1837 fn asmRegisterRegister(self: *CodeGen, tag: Mir.Inst.FixedTag, reg1: Register, reg2: Register) !void { 1838 _ = try self.addInst(.{ 1839 .tag = tag[1], 1840 .ops = .rr, 1841 .data = .{ .rr = .{ 1842 .fixes = tag[0], 1843 .r1 = reg1, 1844 .r2 = reg2, 1845 } }, 1846 }); 1847 } 1848 1849 fn asmRegisterImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, reg: Register, imm: Immediate) !void { 1850 const ops: Mir.Inst.Ops, const i: u32 = switch (imm) { 1851 .signed => |s| .{ .ri_s, @bitCast(s) }, 1852 .unsigned => |u| if (std.math.cast(u32, u)) |small| 1853 .{ .ri_u, small } 1854 else 1855 .{ .ri_64, try self.addExtra(Mir.Imm64.encode(imm.unsigned)) }, 1856 .reloc => unreachable, 1857 }; 1858 _ = try self.addInst(.{ 1859 .tag = tag[1], 1860 .ops = ops, 1861 .data = .{ .ri = .{ 1862 .fixes = tag[0], 1863 .r1 = reg, 1864 .i = i, 1865 } }, 1866 }); 1867 } 1868 1869 fn asmRegisterRegisterRegister( 1870 self: *CodeGen, 1871 tag: Mir.Inst.FixedTag, 1872 reg1: Register, 1873 reg2: Register, 1874 reg3: Register, 1875 ) !void { 1876 _ = try self.addInst(.{ 1877 .tag = tag[1], 1878 .ops = .rrr, 1879 .data = .{ .rrr = .{ 1880 .fixes = tag[0], 1881 .r1 = reg1, 1882 .r2 = reg2, 1883 .r3 = reg3, 1884 } }, 1885 }); 1886 } 1887 1888 fn asmRegisterRegisterRegisterRegister( 1889 self: *CodeGen, 1890 tag: Mir.Inst.FixedTag, 1891 reg1: Register, 1892 reg2: Register, 1893 reg3: Register, 1894 reg4: Register, 1895 ) !void { 1896 _ = try self.addInst(.{ 1897 .tag = tag[1], 1898 .ops = .rrrr, 1899 .data = .{ .rrrr = .{ 1900 .fixes = tag[0], 1901 .r1 = reg1, 1902 .r2 = reg2, 1903 .r3 = reg3, 1904 .r4 = reg4, 1905 } }, 1906 }); 1907 } 1908 1909 fn asmRegisterRegisterRegisterImmediate( 1910 self: *CodeGen, 1911 tag: Mir.Inst.FixedTag, 1912 reg1: Register, 1913 reg2: Register, 1914 reg3: Register, 1915 imm: Immediate, 1916 ) !void { 1917 _ = try self.addInst(.{ 1918 .tag = tag[1], 1919 .ops = .rrri, 1920 .data = .{ .rrri = .{ 1921 .fixes = tag[0], 1922 .r1 = reg1, 1923 .r2 = reg2, 1924 .r3 = reg3, 1925 .i = switch (imm) { 1926 .signed => |s| @bitCast(@as(i8, @intCast(s))), 1927 .unsigned => |u| @intCast(u), 1928 .reloc => unreachable, 1929 }, 1930 } }, 1931 }); 1932 } 1933 1934 fn asmRegisterRegisterImmediate( 1935 self: *CodeGen, 1936 tag: Mir.Inst.FixedTag, 1937 reg1: Register, 1938 reg2: Register, 1939 imm: Immediate, 1940 ) !void { 1941 _ = try self.addInst(.{ 1942 .tag = tag[1], 1943 .ops = switch (imm) { 1944 .signed => .rri_s, 1945 .unsigned => .rri_u, 1946 .reloc => unreachable, 1947 }, 1948 .data = .{ .rri = .{ 1949 .fixes = tag[0], 1950 .r1 = reg1, 1951 .r2 = reg2, 1952 .i = switch (imm) { 1953 .signed => |s| @bitCast(s), 1954 .unsigned => |u| @intCast(u), 1955 .reloc => unreachable, 1956 }, 1957 } }, 1958 }); 1959 } 1960 1961 fn asmRegisterRegisterMemory( 1962 self: *CodeGen, 1963 tag: Mir.Inst.FixedTag, 1964 reg1: Register, 1965 reg2: Register, 1966 m: Memory, 1967 ) !void { 1968 _ = try self.addInst(.{ 1969 .tag = tag[1], 1970 .ops = .rrm, 1971 .data = .{ .rrx = .{ 1972 .fixes = tag[0], 1973 .r1 = reg1, 1974 .r2 = reg2, 1975 .payload = try self.addExtra(Mir.Memory.encode(m)), 1976 } }, 1977 }); 1978 } 1979 1980 fn asmRegisterRegisterMemoryRegister( 1981 self: *CodeGen, 1982 tag: Mir.Inst.FixedTag, 1983 reg1: Register, 1984 reg2: Register, 1985 m: Memory, 1986 reg3: Register, 1987 ) !void { 1988 _ = try self.addInst(.{ 1989 .tag = tag[1], 1990 .ops = .rrmr, 1991 .data = .{ .rrrx = .{ 1992 .fixes = tag[0], 1993 .r1 = reg1, 1994 .r2 = reg2, 1995 .r3 = reg3, 1996 .payload = try self.addExtra(Mir.Memory.encode(m)), 1997 } }, 1998 }); 1999 } 2000 2001 fn asmMemory(self: *CodeGen, tag: Mir.Inst.FixedTag, m: Memory) !void { 2002 _ = try self.addInst(.{ 2003 .tag = tag[1], 2004 .ops = .m, 2005 .data = .{ .x = .{ 2006 .fixes = tag[0], 2007 .payload = try self.addExtra(Mir.Memory.encode(m)), 2008 } }, 2009 }); 2010 } 2011 2012 fn asmRegisterMemory(self: *CodeGen, tag: Mir.Inst.FixedTag, reg: Register, m: Memory) !void { 2013 _ = try self.addInst(.{ 2014 .tag = tag[1], 2015 .ops = .rm, 2016 .data = .{ .rx = .{ 2017 .fixes = tag[0], 2018 .r1 = reg, 2019 .payload = try self.addExtra(Mir.Memory.encode(m)), 2020 } }, 2021 }); 2022 } 2023 2024 fn asmRegisterMemoryRegister( 2025 self: *CodeGen, 2026 tag: Mir.Inst.FixedTag, 2027 reg1: Register, 2028 m: Memory, 2029 reg2: Register, 2030 ) !void { 2031 _ = try self.addInst(.{ 2032 .tag = tag[1], 2033 .ops = .rmr, 2034 .data = .{ .rrx = .{ 2035 .fixes = tag[0], 2036 .r1 = reg1, 2037 .r2 = reg2, 2038 .payload = try self.addExtra(Mir.Memory.encode(m)), 2039 } }, 2040 }); 2041 } 2042 2043 fn asmRegisterMemoryImmediate( 2044 self: *CodeGen, 2045 tag: Mir.Inst.FixedTag, 2046 reg: Register, 2047 m: Memory, 2048 imm: Immediate, 2049 ) !void { 2050 if (switch (imm) { 2051 .signed => |s| if (std.math.cast(i16, s)) |x| @as(u16, @bitCast(x)) else null, 2052 .unsigned => |u| std.math.cast(u16, u), 2053 .reloc => unreachable, 2054 }) |small_imm| { 2055 _ = try self.addInst(.{ 2056 .tag = tag[1], 2057 .ops = .rmi, 2058 .data = .{ .rix = .{ 2059 .fixes = tag[0], 2060 .r1 = reg, 2061 .i = small_imm, 2062 .payload = try self.addExtra(Mir.Memory.encode(m)), 2063 } }, 2064 }); 2065 } else { 2066 const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { 2067 .signed => |s| @bitCast(s), 2068 .unsigned => unreachable, 2069 .reloc => unreachable, 2070 } }); 2071 assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); 2072 _ = try self.addInst(.{ 2073 .tag = tag[1], 2074 .ops = switch (imm) { 2075 .signed => .rmi_s, 2076 .unsigned => .rmi_u, 2077 .reloc => unreachable, 2078 }, 2079 .data = .{ .rx = .{ 2080 .fixes = tag[0], 2081 .r1 = reg, 2082 .payload = payload, 2083 } }, 2084 }); 2085 } 2086 } 2087 2088 fn asmRegisterRegisterMemoryImmediate( 2089 self: *CodeGen, 2090 tag: Mir.Inst.FixedTag, 2091 reg1: Register, 2092 reg2: Register, 2093 m: Memory, 2094 imm: Immediate, 2095 ) !void { 2096 _ = try self.addInst(.{ 2097 .tag = tag[1], 2098 .ops = .rrmi, 2099 .data = .{ .rrix = .{ 2100 .fixes = tag[0], 2101 .r1 = reg1, 2102 .r2 = reg2, 2103 .i = @intCast(imm.unsigned), 2104 .payload = try self.addExtra(Mir.Memory.encode(m)), 2105 } }, 2106 }); 2107 } 2108 2109 fn asmMemoryRegister(self: *CodeGen, tag: Mir.Inst.FixedTag, m: Memory, reg: Register) !void { 2110 _ = try self.addInst(.{ 2111 .tag = tag[1], 2112 .ops = .mr, 2113 .data = .{ .rx = .{ 2114 .fixes = tag[0], 2115 .r1 = reg, 2116 .payload = try self.addExtra(Mir.Memory.encode(m)), 2117 } }, 2118 }); 2119 } 2120 2121 fn asmMemoryImmediate(self: *CodeGen, tag: Mir.Inst.FixedTag, m: Memory, imm: Immediate) !void { 2122 const payload = try self.addExtra(Mir.Imm32{ .imm = switch (imm) { 2123 .signed => |s| @bitCast(s), 2124 .unsigned => |u| @intCast(u), 2125 .reloc => unreachable, 2126 } }); 2127 assert(payload + 1 == try self.addExtra(Mir.Memory.encode(m))); 2128 _ = try self.addInst(.{ 2129 .tag = tag[1], 2130 .ops = switch (imm) { 2131 .signed => .mi_s, 2132 .unsigned => .mi_u, 2133 .reloc => unreachable, 2134 }, 2135 .data = .{ .x = .{ 2136 .fixes = tag[0], 2137 .payload = payload, 2138 } }, 2139 }); 2140 } 2141 2142 fn asmMemoryRegisterRegister( 2143 self: *CodeGen, 2144 tag: Mir.Inst.FixedTag, 2145 m: Memory, 2146 reg1: Register, 2147 reg2: Register, 2148 ) !void { 2149 _ = try self.addInst(.{ 2150 .tag = tag[1], 2151 .ops = .mrr, 2152 .data = .{ .rrx = .{ 2153 .fixes = tag[0], 2154 .r1 = reg1, 2155 .r2 = reg2, 2156 .payload = try self.addExtra(Mir.Memory.encode(m)), 2157 } }, 2158 }); 2159 } 2160 2161 fn asmMemoryRegisterImmediate( 2162 self: *CodeGen, 2163 tag: Mir.Inst.FixedTag, 2164 m: Memory, 2165 reg: Register, 2166 imm: Immediate, 2167 ) !void { 2168 _ = try self.addInst(.{ 2169 .tag = tag[1], 2170 .ops = .mri, 2171 .data = .{ .rix = .{ 2172 .fixes = tag[0], 2173 .r1 = reg, 2174 .i = @intCast(imm.unsigned), 2175 .payload = try self.addExtra(Mir.Memory.encode(m)), 2176 } }, 2177 }); 2178 } 2179 2180 fn gen(self: *CodeGen) InnerError!void { 2181 const pt = self.pt; 2182 const zcu = pt.zcu; 2183 const fn_info = zcu.typeToFunc(self.fn_type).?; 2184 if (fn_info.cc != .naked) { 2185 try self.asmRegister(.{ ._, .push }, .rbp); 2186 try self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, .s(8)); 2187 try self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, .rbp, .s(0)); 2188 try self.asmRegisterRegister(.{ ._, .mov }, .rbp, .rsp); 2189 try self.asmPseudoRegister(.pseudo_cfi_def_cfa_register_r, .rbp); 2190 const backpatch_push_callee_preserved_regs = try self.asmPlaceholder(); 2191 const backpatch_frame_align = try self.asmPlaceholder(); 2192 const backpatch_frame_align_extra = try self.asmPlaceholder(); 2193 const backpatch_stack_alloc = try self.asmPlaceholder(); 2194 const backpatch_stack_alloc_extra = try self.asmPlaceholder(); 2195 2196 switch (self.ret_mcv.long) { 2197 .none, .unreach => {}, 2198 .indirect => { 2199 // The address where to store the return value for the caller is in a 2200 // register which the callee is free to clobber. Therefore, we purposely 2201 // spill it to stack immediately. 2202 const frame_index = try self.allocFrameIndex(.initSpill(.usize, zcu)); 2203 try self.genSetMem( 2204 .{ .frame = frame_index }, 2205 0, 2206 .usize, 2207 self.ret_mcv.long.address().offset(-self.ret_mcv.short.indirect.off), 2208 .{}, 2209 ); 2210 self.ret_mcv.long = .{ .load_frame = .{ .index = frame_index } }; 2211 tracking_log.debug("spill {} to {}", .{ self.ret_mcv.long, frame_index }); 2212 }, 2213 else => unreachable, 2214 } 2215 2216 if (fn_info.is_var_args) switch (fn_info.cc) { 2217 .x86_64_sysv => { 2218 const info = &self.va_info.sysv; 2219 const reg_save_area_fi = try self.allocFrameIndex(.init(.{ 2220 .size = abi.SysV.c_abi_int_param_regs.len * 8 + 2221 abi.SysV.c_abi_sse_param_regs.len * 16, 2222 .alignment = .@"16", 2223 })); 2224 info.reg_save_area = .{ .index = reg_save_area_fi }; 2225 2226 for (abi.SysV.c_abi_int_param_regs[info.gp_count..], info.gp_count..) |reg, reg_i| 2227 try self.genSetMem(.{ .frame = reg_save_area_fi }, @intCast(reg_i * 8), .usize, .{ .register = reg }, .{}); 2228 2229 try self.asmRegisterImmediate(.{ ._, .cmp }, .al, .u(info.fp_count)); 2230 const skip_sse_reloc = try self.asmJccReloc(.na, undefined); 2231 2232 const vec_2_f64 = try pt.vectorType(.{ .len = 2, .child = .f64_type }); 2233 for (abi.SysV.c_abi_sse_param_regs[info.fp_count..], info.fp_count..) |reg, reg_i| 2234 try self.genSetMem( 2235 .{ .frame = reg_save_area_fi }, 2236 @intCast(abi.SysV.c_abi_int_param_regs.len * 8 + reg_i * 16), 2237 vec_2_f64, 2238 .{ .register = reg }, 2239 .{}, 2240 ); 2241 2242 self.performReloc(skip_sse_reloc); 2243 }, 2244 .x86_64_win => return self.fail("TODO implement gen var arg function for Win64", .{}), 2245 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), 2246 }; 2247 2248 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_prologue_end_none); 2249 2250 try self.genBody(self.air.getMainBody()); 2251 2252 const epilogue = if (self.epilogue_relocs.items.len > 0) epilogue: { 2253 const epilogue_relocs_last_index = self.epilogue_relocs.items.len - 1; 2254 for (if (self.epilogue_relocs.items[epilogue_relocs_last_index] == self.mir_instructions.len - 1) epilogue_relocs: { 2255 _ = self.mir_instructions.pop(); 2256 break :epilogue_relocs self.epilogue_relocs.items[0..epilogue_relocs_last_index]; 2257 } else self.epilogue_relocs.items) |epilogue_reloc| self.performReloc(epilogue_reloc); 2258 2259 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); 2260 const backpatch_stack_dealloc = try self.asmPlaceholder(); 2261 const backpatch_pop_callee_preserved_regs = try self.asmPlaceholder(); 2262 try self.asmRegister(.{ ._, .pop }, .rbp); 2263 try self.asmPseudoRegisterImmediate(.pseudo_cfi_def_cfa_ri_s, .rsp, .s(8)); 2264 try self.asmOpOnly(.{ ._, .ret }); 2265 break :epilogue .{ 2266 .backpatch_stack_dealloc = backpatch_stack_dealloc, 2267 .backpatch_pop_callee_preserved_regs = backpatch_pop_callee_preserved_regs, 2268 }; 2269 } else null; 2270 2271 const frame_layout = try self.computeFrameLayout(fn_info.cc); 2272 const need_frame_align = frame_layout.stack_mask != std.math.maxInt(u32); 2273 const need_stack_adjust = frame_layout.stack_adjust > 0; 2274 const need_save_reg = frame_layout.save_reg_list.count() > 0; 2275 if (need_frame_align) { 2276 const page_align = @as(u32, std.math.maxInt(u32)) << 12; 2277 self.mir_instructions.set(backpatch_frame_align, .{ 2278 .tag = .@"and", 2279 .ops = .ri_s, 2280 .data = .{ .ri = .{ 2281 .r1 = .rsp, 2282 .i = @max(frame_layout.stack_mask, page_align), 2283 } }, 2284 }); 2285 if (frame_layout.stack_mask < page_align) { 2286 self.mir_instructions.set(backpatch_frame_align_extra, .{ 2287 .tag = .pseudo, 2288 .ops = .pseudo_probe_align_ri_s, 2289 .data = .{ .ri = .{ 2290 .r1 = .rsp, 2291 .i = ~frame_layout.stack_mask & page_align, 2292 } }, 2293 }); 2294 } 2295 } 2296 if (need_stack_adjust) { 2297 const page_size: u32 = 1 << 12; 2298 if (frame_layout.stack_adjust <= page_size) { 2299 self.mir_instructions.set(backpatch_stack_alloc, .{ 2300 .tag = .sub, 2301 .ops = .ri_s, 2302 .data = .{ .ri = .{ 2303 .r1 = .rsp, 2304 .i = frame_layout.stack_adjust, 2305 } }, 2306 }); 2307 } else if (frame_layout.stack_adjust < 2308 page_size * Lower.pseudo_probe_adjust_unrolled_max_insts) 2309 { 2310 self.mir_instructions.set(backpatch_stack_alloc, .{ 2311 .tag = .pseudo, 2312 .ops = .pseudo_probe_adjust_unrolled_ri_s, 2313 .data = .{ .ri = .{ 2314 .r1 = .rsp, 2315 .i = frame_layout.stack_adjust, 2316 } }, 2317 }); 2318 } else { 2319 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc); 2320 self.mir_instructions.set(backpatch_stack_alloc, .{ 2321 .tag = .pseudo, 2322 .ops = .pseudo_probe_adjust_setup_rri_s, 2323 .data = .{ .rri = .{ 2324 .r1 = .rsp, 2325 .r2 = scratch_reg, 2326 .i = frame_layout.stack_adjust, 2327 } }, 2328 }); 2329 self.mir_instructions.set(backpatch_stack_alloc_extra, .{ 2330 .tag = .pseudo, 2331 .ops = .pseudo_probe_adjust_loop_rr, 2332 .data = .{ .rr = .{ 2333 .r1 = .rsp, 2334 .r2 = scratch_reg, 2335 } }, 2336 }); 2337 } 2338 } 2339 if (epilogue) |e| if (need_frame_align or need_stack_adjust) { 2340 self.mir_instructions.set(e.backpatch_stack_dealloc, switch (-frame_layout.save_reg_list.size(self.target)) { 2341 0 => .{ 2342 .tag = .mov, 2343 .ops = .rr, 2344 .data = .{ .rr = .{ 2345 .r1 = .rsp, 2346 .r2 = .rbp, 2347 } }, 2348 }, 2349 else => |disp| .{ 2350 .tag = .lea, 2351 .ops = .rm, 2352 .data = .{ .rx = .{ 2353 .r1 = .rsp, 2354 .payload = try self.addExtra(Mir.Memory.encode(.{ 2355 .base = .{ .reg = .rbp }, 2356 .mod = .{ .rm = .{ 2357 .size = .qword, 2358 .disp = disp, 2359 } }, 2360 })), 2361 } }, 2362 }, 2363 }); 2364 }; 2365 if (need_save_reg) { 2366 self.mir_instructions.set(backpatch_push_callee_preserved_regs, .{ 2367 .tag = .pseudo, 2368 .ops = .pseudo_push_reg_list, 2369 .data = .{ .reg_list = frame_layout.save_reg_list }, 2370 }); 2371 if (epilogue) |e| self.mir_instructions.set(e.backpatch_pop_callee_preserved_regs, .{ 2372 .tag = .pseudo, 2373 .ops = .pseudo_pop_reg_list, 2374 .data = .{ .reg_list = frame_layout.save_reg_list }, 2375 }); 2376 } 2377 } else { 2378 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_prologue_end_none); 2379 try self.genBody(self.air.getMainBody()); 2380 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_epilogue_begin_none); 2381 } 2382 2383 // Drop them off at the rbrace. 2384 if (self.debug_output != .none) _ = try self.addInst(.{ 2385 .tag = .pseudo, 2386 .ops = .pseudo_dbg_line_line_column, 2387 .data = .{ .line_column = .{ 2388 .line = self.end_di_line, 2389 .column = self.end_di_column, 2390 } }, 2391 }); 2392 } 2393 2394 fn checkInvariantsAfterAirInst(self: *CodeGen) void { 2395 assert(!self.register_manager.lockedRegsExist()); 2396 2397 if (std.debug.runtime_safety) { 2398 // check consistency of tracked registers 2399 var it = self.register_manager.free_registers.iterator(.{ .kind = .unset }); 2400 while (it.next()) |index| { 2401 const tracked_inst = self.register_manager.registers[index]; 2402 const tracking = self.getResolvedInstValue(tracked_inst); 2403 for (tracking.getRegs()) |reg| { 2404 if (RegisterManager.indexOfRegIntoTracked(reg).? == index) break; 2405 } else unreachable; // tracked register not in use 2406 } 2407 } 2408 } 2409 2410 fn genBodyBlock(self: *CodeGen, body: []const Air.Inst.Index) InnerError!void { 2411 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_enter_block_none); 2412 try self.genBody(body); 2413 if (self.debug_output != .none) try self.asmPseudo(.pseudo_dbg_leave_block_none); 2414 } 2415 2416 fn genBody(cg: *CodeGen, body: []const Air.Inst.Index) InnerError!void { 2417 @setEvalBranchQuota(3_900); 2418 const pt = cg.pt; 2419 const zcu = pt.zcu; 2420 const ip = &zcu.intern_pool; 2421 const air_tags = cg.air.instructions.items(.tag); 2422 const air_datas = cg.air.instructions.items(.data); 2423 const use_old = cg.target.ofmt == .coff; 2424 2425 cg.arg_index = 0; 2426 for (body) |inst| switch (air_tags[@intFromEnum(inst)]) { 2427 .arg => { 2428 wip_mir_log.debug("{}", .{cg.fmtAir(inst)}); 2429 verbose_tracking_log.debug("{}", .{cg.fmtTracking()}); 2430 2431 cg.reused_operands = .initEmpty(); 2432 try cg.inst_tracking.ensureUnusedCapacity(cg.gpa, 1); 2433 2434 try cg.airArg(inst); 2435 2436 try cg.resetTemps(); 2437 cg.checkInvariantsAfterAirInst(); 2438 }, 2439 else => break, 2440 }; 2441 2442 if (cg.arg_index == 0) try cg.airDbgVarArgs(); 2443 cg.arg_index = 0; 2444 for (body) |inst| { 2445 if (cg.liveness.isUnused(inst) and !cg.air.mustLower(inst, ip)) continue; 2446 wip_mir_log.debug("{}", .{cg.fmtAir(inst)}); 2447 verbose_tracking_log.debug("{}", .{cg.fmtTracking()}); 2448 2449 cg.reused_operands = .initEmpty(); 2450 try cg.inst_tracking.ensureUnusedCapacity(cg.gpa, 1); 2451 switch (air_tags[@intFromEnum(inst)]) { 2452 // zig fmt: off 2453 .add, 2454 .add_wrap, 2455 .sub, 2456 .sub_wrap, 2457 => |air_tag| try cg.airBinOp(inst, air_tag), 2458 2459 .shr, .shr_exact => try cg.airShlShrBinOp(inst), 2460 .shl, .shl_exact => try cg.airShlShrBinOp(inst), 2461 2462 .mul, 2463 .mul_wrap, 2464 .rem, 2465 .mod, 2466 .div_float, 2467 .div_trunc, 2468 .div_floor, 2469 .div_exact, 2470 => |air_tag| try cg.airMulDivBinOp(inst, air_tag), 2471 2472 .add_sat => try cg.airAddSat(inst), 2473 .sub_sat => try cg.airSubSat(inst), 2474 .mul_sat => try cg.airMulSat(inst), 2475 .shl_sat => try cg.airShlSat(inst), 2476 2477 .sin, 2478 .cos, 2479 .tan, 2480 .exp, 2481 .exp2, 2482 .log, 2483 .log2, 2484 .log10, 2485 .round, 2486 => |air_tag| try cg.airUnaryMath(inst, air_tag), 2487 2488 .floor => try cg.airRound(inst, .{ .mode = .down, .precision = .inexact }), 2489 .ceil => try cg.airRound(inst, .{ .mode = .up, .precision = .inexact }), 2490 .trunc_float => try cg.airRound(inst, .{ .mode = .zero, .precision = .inexact }), 2491 .sqrt => try cg.airSqrt(inst), 2492 .neg => |air_tag| try cg.airFloatSign(inst, air_tag), 2493 2494 .add_with_overflow => try cg.airAddSubWithOverflow(inst), 2495 .sub_with_overflow => try cg.airAddSubWithOverflow(inst), 2496 .mul_with_overflow => try cg.airMulWithOverflow(inst), 2497 .shl_with_overflow => try cg.airShlWithOverflow(inst), 2498 2499 .cmp_lt_errors_len => try cg.airCmpLtErrorsLen(inst), 2500 2501 .bitcast => try cg.airBitCast(inst), 2502 .fptrunc => try cg.airFptrunc(inst), 2503 .fpext => try cg.airFpext(inst), 2504 .intcast => try cg.airIntCast(inst), 2505 .trunc => try cg.airTrunc(inst), 2506 .is_non_null => try cg.airIsNonNull(inst), 2507 .is_null => try cg.airIsNull(inst), 2508 .is_non_err => try cg.airIsNonErr(inst), 2509 .is_err => try cg.airIsErr(inst), 2510 .float_from_int => try cg.airFloatFromInt(inst), 2511 .int_from_float => try cg.airIntFromFloat(inst), 2512 .cmpxchg_strong => try cg.airCmpxchg(inst), 2513 .cmpxchg_weak => try cg.airCmpxchg(inst), 2514 .atomic_rmw => try cg.airAtomicRmw(inst), 2515 .atomic_load => try cg.airAtomicLoad(inst), 2516 .memcpy => try cg.airMemcpy(inst), 2517 .memset => try cg.airMemset(inst, false), 2518 .memset_safe => try cg.airMemset(inst, true), 2519 .ctz => try cg.airCtz(inst), 2520 .popcount => try cg.airPopCount(inst), 2521 .byte_swap => try cg.airByteSwap(inst), 2522 .bit_reverse => try cg.airBitReverse(inst), 2523 .tag_name => try cg.airTagName(inst), 2524 .error_name => try cg.airErrorName(inst), 2525 .splat => try cg.airSplat(inst), 2526 .select => try cg.airSelect(inst), 2527 .shuffle => try cg.airShuffle(inst), 2528 .reduce => try cg.airReduce(inst), 2529 .aggregate_init => try cg.airAggregateInit(inst), 2530 .prefetch => try cg.airPrefetch(inst), 2531 .mul_add => try cg.airMulAdd(inst), 2532 2533 .atomic_store_unordered => try cg.airAtomicStore(inst, .unordered), 2534 .atomic_store_monotonic => try cg.airAtomicStore(inst, .monotonic), 2535 .atomic_store_release => try cg.airAtomicStore(inst, .release), 2536 .atomic_store_seq_cst => try cg.airAtomicStore(inst, .seq_cst), 2537 2538 .array_elem_val => try cg.airArrayElemVal(inst), 2539 2540 .optional_payload => try cg.airOptionalPayload(inst), 2541 .unwrap_errunion_err => try cg.airUnwrapErrUnionErr(inst), 2542 .unwrap_errunion_payload => try cg.airUnwrapErrUnionPayload(inst), 2543 2544 .wrap_optional => try cg.airWrapOptional(inst), 2545 .wrap_errunion_payload => try cg.airWrapErrUnionPayload(inst), 2546 .wrap_errunion_err => try cg.airWrapErrUnionErr(inst), 2547 // zig fmt: on 2548 2549 .add_safe, 2550 .sub_safe, 2551 .mul_safe, 2552 .intcast_safe, 2553 => return cg.fail("TODO implement safety_checked_instructions", .{}), 2554 2555 .add_optimized => try cg.airBinOp(inst, .add), 2556 .sub_optimized => try cg.airBinOp(inst, .sub), 2557 .mul_optimized => try cg.airBinOp(inst, .mul), 2558 .div_float_optimized => try cg.airMulDivBinOp(inst, .div_float), 2559 .div_trunc_optimized => try cg.airMulDivBinOp(inst, .div_trunc), 2560 .div_floor_optimized => try cg.airMulDivBinOp(inst, .div_floor), 2561 .div_exact_optimized => try cg.airMulDivBinOp(inst, .div_exact), 2562 .rem_optimized => try cg.airMulDivBinOp(inst, .rem), 2563 .mod_optimized => try cg.airMulDivBinOp(inst, .mod), 2564 .neg_optimized => try cg.airFloatSign(inst, .neg), 2565 .reduce_optimized => try cg.airReduce(inst), 2566 .int_from_float_optimized => try cg.airIntFromFloat(inst), 2567 2568 .arg => if (cg.debug_output != .none) { 2569 // skip zero-bit arguments as they don't have a corresponding arg instruction 2570 var arg_index = cg.arg_index; 2571 while (cg.args[arg_index] == .none) arg_index += 1; 2572 cg.arg_index = arg_index + 1; 2573 2574 const name = air_datas[@intFromEnum(inst)].arg.name; 2575 if (name != .none) try cg.genLocalDebugInfo(inst, cg.getResolvedInstValue(inst).short); 2576 if (cg.liveness.isUnused(inst)) try cg.processDeath(inst); 2577 2578 for (cg.args[arg_index + 1 ..]) |arg| { 2579 if (arg != .none) break; 2580 } else try cg.airDbgVarArgs(); 2581 }, 2582 .ptr_add => |air_tag| if (use_old) try cg.airPtrArithmetic(inst, air_tag) else { 2583 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 2584 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; 2585 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 2586 try ops[0].toSlicePtr(cg); 2587 var res: [1]Temp = undefined; 2588 if (ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ 2589 .patterns = &.{ 2590 .{ .src = .{ .to_gpr, .simm32 } }, 2591 }, 2592 .dst_temps = .{.{ .rc = .general_purpose }}, 2593 .each = .{ .once = &.{ 2594 .{ ._, ._, .lea, .dst0p, .leaa(.none, .src0, .add_src0_elem_size_times_src1), ._, ._ }, 2595 } }, 2596 }, .{ 2597 .dst_constraints = .{.{ .elem_size_is = 1 }}, 2598 .patterns = &.{ 2599 .{ .src = .{ .to_gpr, .to_gpr } }, 2600 }, 2601 .dst_temps = .{.{ .rc = .general_purpose }}, 2602 .each = .{ .once = &.{ 2603 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .src1), ._, ._ }, 2604 } }, 2605 }, .{ 2606 .dst_constraints = .{.{ .elem_size_is = 2 }}, 2607 .patterns = &.{ 2608 .{ .src = .{ .to_gpr, .to_gpr } }, 2609 }, 2610 .dst_temps = .{.{ .rc = .general_purpose }}, 2611 .each = .{ .once = &.{ 2612 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"2", .src1), ._, ._ }, 2613 } }, 2614 }, .{ 2615 .dst_constraints = .{.{ .elem_size_is = 2 + 1 }}, 2616 .patterns = &.{ 2617 .{ .src = .{ .to_gpr, .to_gpr } }, 2618 }, 2619 .dst_temps = .{.{ .rc = .general_purpose }}, 2620 .each = .{ .once = &.{ 2621 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"2", .src1), ._, ._ }, 2622 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2623 } }, 2624 }, .{ 2625 .dst_constraints = .{.{ .elem_size_is = 4 }}, 2626 .patterns = &.{ 2627 .{ .src = .{ .to_gpr, .to_gpr } }, 2628 }, 2629 .dst_temps = .{.{ .rc = .general_purpose }}, 2630 .each = .{ .once = &.{ 2631 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"4", .src1), ._, ._ }, 2632 } }, 2633 }, .{ 2634 .dst_constraints = .{.{ .elem_size_is = 4 + 1 }}, 2635 .patterns = &.{ 2636 .{ .src = .{ .to_gpr, .to_gpr } }, 2637 }, 2638 .dst_temps = .{.{ .ref = .src1 }}, 2639 .each = .{ .once = &.{ 2640 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"4", .src1), ._, ._ }, 2641 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2642 } }, 2643 }, .{ 2644 .required_features = .{ .@"64bit", null, null, null }, 2645 .dst_constraints = .{.{ .elem_size_is = 8 }}, 2646 .patterns = &.{ 2647 .{ .src = .{ .to_gpr, .to_gpr } }, 2648 }, 2649 .dst_temps = .{.{ .rc = .general_purpose }}, 2650 .each = .{ .once = &.{ 2651 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"8", .src1), ._, ._ }, 2652 } }, 2653 }, .{ 2654 .required_features = .{ .@"64bit", null, null, null }, 2655 .dst_constraints = .{.{ .elem_size_is = 8 + 1 }}, 2656 .patterns = &.{ 2657 .{ .src = .{ .to_gpr, .to_gpr } }, 2658 }, 2659 .dst_temps = .{.{ .ref = .src1 }}, 2660 .each = .{ .once = &.{ 2661 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"8", .src1), ._, ._ }, 2662 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2663 } }, 2664 }, .{ 2665 .dst_constraints = .{.po2_elem_size}, 2666 .patterns = &.{ 2667 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2668 }, 2669 .dst_temps = .{.{ .ref = .src1 }}, 2670 .clobbers = .{ .eflags = true }, 2671 .each = .{ .once = &.{ 2672 .{ ._, ._l, .sh, .src1p, .sa(.none, .add_log2_src0_elem_size), ._, ._ }, 2673 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .src1), ._, ._ }, 2674 } }, 2675 }, .{ 2676 .patterns = &.{ 2677 .{ .src = .{ .to_gpr, .to_gpr } }, 2678 }, 2679 .dst_temps = .{.{ .rc = .general_purpose }}, 2680 .clobbers = .{ .eflags = true }, 2681 .each = .{ .once = &.{ 2682 .{ ._, .i_, .mul, .dst0p, .src1p, .sa(.none, .add_src0_elem_size), ._ }, 2683 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2684 } }, 2685 } }) catch |err| switch (err) { 2686 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 2687 @tagName(air_tag), 2688 cg.typeOf(bin_op.lhs).fmt(pt), 2689 ops[0].tracking(cg), 2690 ops[1].tracking(cg), 2691 }), 2692 else => |e| return e, 2693 } else { // hack around Sema OPV bugs 2694 res[0] = ops[0]; 2695 } 2696 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 2697 }, 2698 .ptr_sub => |air_tag| if (use_old) try cg.airPtrArithmetic(inst, air_tag) else { 2699 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 2700 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; 2701 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 2702 try ops[0].toSlicePtr(cg); 2703 var res: [1]Temp = undefined; 2704 if (ty_pl.ty.toType().elemType2(zcu).hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{ty_pl.ty.toType()}, &ops, comptime &.{ .{ 2705 .patterns = &.{ 2706 .{ .src = .{ .to_gpr, .simm32 } }, 2707 }, 2708 .dst_temps = .{.{ .rc = .general_purpose }}, 2709 .each = .{ .once = &.{ 2710 .{ ._, ._, .lea, .dst0p, .leaa(.none, .src0, .sub_src0_elem_size_times_src1), ._, ._ }, 2711 } }, 2712 }, .{ 2713 .dst_constraints = .{.{ .elem_size_is = 1 }}, 2714 .patterns = &.{ 2715 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2716 }, 2717 .dst_temps = .{.{ .ref = .src1 }}, 2718 .clobbers = .{ .eflags = true }, 2719 .each = .{ .once = &.{ 2720 .{ ._, ._, .neg, .src1p, ._, ._, ._ }, 2721 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .src1), ._, ._ }, 2722 } }, 2723 }, .{ 2724 .dst_constraints = .{.{ .elem_size_is = 2 }}, 2725 .patterns = &.{ 2726 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2727 }, 2728 .dst_temps = .{.{ .ref = .src1 }}, 2729 .clobbers = .{ .eflags = true }, 2730 .each = .{ .once = &.{ 2731 .{ ._, ._, .neg, .src1p, ._, ._, ._ }, 2732 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"2", .src1), ._, ._ }, 2733 } }, 2734 }, .{ 2735 .dst_constraints = .{.{ .elem_size_is = 2 + 1 }}, 2736 .patterns = &.{ 2737 .{ .src = .{ .to_gpr, .to_gpr } }, 2738 }, 2739 .dst_temps = .{.{ .rc = .general_purpose }}, 2740 .clobbers = .{ .eflags = true }, 2741 .each = .{ .once = &.{ 2742 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"2", .src1), ._, ._ }, 2743 .{ ._, ._, .neg, .dst0p, ._, ._, ._ }, 2744 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2745 } }, 2746 }, .{ 2747 .dst_constraints = .{.{ .elem_size_is = 4 }}, 2748 .patterns = &.{ 2749 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2750 }, 2751 .dst_temps = .{.{ .ref = .src1 }}, 2752 .clobbers = .{ .eflags = true }, 2753 .each = .{ .once = &.{ 2754 .{ ._, ._, .neg, .src1p, ._, ._, ._ }, 2755 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"4", .src1), ._, ._ }, 2756 } }, 2757 }, .{ 2758 .dst_constraints = .{.{ .elem_size_is = 4 + 1 }}, 2759 .patterns = &.{ 2760 .{ .src = .{ .to_gpr, .to_gpr } }, 2761 }, 2762 .dst_temps = .{.{ .rc = .general_purpose }}, 2763 .clobbers = .{ .eflags = true }, 2764 .each = .{ .once = &.{ 2765 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"4", .src1), ._, ._ }, 2766 .{ ._, ._, .neg, .dst0p, ._, ._, ._ }, 2767 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2768 } }, 2769 }, .{ 2770 .required_features = .{ .@"64bit", null, null, null }, 2771 .dst_constraints = .{.{ .elem_size_is = 8 }}, 2772 .patterns = &.{ 2773 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2774 }, 2775 .dst_temps = .{.{ .ref = .src1 }}, 2776 .clobbers = .{ .eflags = true }, 2777 .each = .{ .once = &.{ 2778 .{ ._, ._, .neg, .src1p, ._, ._, ._ }, 2779 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src0, .@"8", .src1), ._, ._ }, 2780 } }, 2781 }, .{ 2782 .required_features = .{ .@"64bit", null, null, null }, 2783 .dst_constraints = .{.{ .elem_size_is = 8 + 1 }}, 2784 .patterns = &.{ 2785 .{ .src = .{ .to_gpr, .to_gpr } }, 2786 }, 2787 .dst_temps = .{.{ .rc = .general_purpose }}, 2788 .clobbers = .{ .eflags = true }, 2789 .each = .{ .once = &.{ 2790 .{ ._, ._, .lea, .dst0p, .leasi(.none, .src1, .@"8", .src1), ._, ._ }, 2791 .{ ._, ._, .neg, .dst0p, ._, ._, ._ }, 2792 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2793 } }, 2794 }, .{ 2795 .dst_constraints = .{.po2_elem_size}, 2796 .patterns = &.{ 2797 .{ .src = .{ .to_gpr, .to_mut_gpr } }, 2798 }, 2799 .dst_temps = .{.{ .ref = .src1 }}, 2800 .clobbers = .{ .eflags = true }, 2801 .each = .{ .once = &.{ 2802 .{ ._, ._l, .sa, .src1p, .sa(.none, .add_log2_src0_elem_size), ._, ._ }, 2803 .{ ._, ._, .neg, .src1p, ._, ._, ._ }, 2804 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .src1), ._, ._ }, 2805 } }, 2806 }, .{ 2807 .patterns = &.{ 2808 .{ .src = .{ .to_gpr, .to_gpr } }, 2809 }, 2810 .dst_temps = .{.{ .rc = .general_purpose }}, 2811 .clobbers = .{ .eflags = true }, 2812 .each = .{ .once = &.{ 2813 .{ ._, .i_, .mul, .dst0p, .src1p, .sa(.none, .sub_src0_elem_size), ._ }, 2814 .{ ._, ._, .lea, .dst0p, .leai(.none, .src0, .dst0), ._, ._ }, 2815 } }, 2816 } }) catch |err| switch (err) { 2817 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 2818 @tagName(air_tag), 2819 cg.typeOf(bin_op.lhs).fmt(pt), 2820 ops[0].tracking(cg), 2821 ops[1].tracking(cg), 2822 }), 2823 else => |e| return e, 2824 } else { 2825 // hack around Sema OPV bugs 2826 res[0] = ops[0]; 2827 } 2828 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 2829 }, 2830 .max => |air_tag| if (use_old) try cg.airBinOp(inst, air_tag) else { 2831 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 2832 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 2833 var res: [1]Temp = undefined; 2834 cg.select(&res, &.{cg.typeOf(bin_op.lhs)}, &ops, comptime &.{ .{ 2835 .required_features = .{ .cmov, null, null, null }, 2836 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 2837 .patterns = &.{ 2838 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2839 }, 2840 .dst_temps = .{.{ .ref = .src0 }}, 2841 .clobbers = .{ .eflags = true }, 2842 .each = .{ .once = &.{ 2843 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 2844 .{ ._, ._l, .cmov, .dst0d, .src1d, ._, ._ }, 2845 } }, 2846 }, .{ 2847 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 2848 .patterns = &.{ 2849 .{ .src = .{ .to_mut_gpr, .mem } }, 2850 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2851 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2852 }, 2853 .dst_temps = .{.{ .ref = .src0 }}, 2854 .clobbers = .{ .eflags = true }, 2855 .each = .{ .once = &.{ 2856 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 2857 .{ ._, ._nl, .j, .@"0f", ._, ._, ._ }, 2858 .{ ._, ._, .mov, .dst0b, .src1b, ._, ._ }, 2859 } }, 2860 }, .{ 2861 .required_features = .{ .cmov, null, null, null }, 2862 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 2863 .patterns = &.{ 2864 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2865 }, 2866 .dst_temps = .{.{ .ref = .src0 }}, 2867 .clobbers = .{ .eflags = true }, 2868 .each = .{ .once = &.{ 2869 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 2870 .{ ._, ._b, .cmov, .dst0d, .src1d, ._, ._ }, 2871 } }, 2872 }, .{ 2873 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 2874 .patterns = &.{ 2875 .{ .src = .{ .to_mut_gpr, .mem } }, 2876 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2877 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2878 }, 2879 .dst_temps = .{.{ .ref = .src0 }}, 2880 .clobbers = .{ .eflags = true }, 2881 .each = .{ .once = &.{ 2882 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 2883 .{ ._, ._nb, .j, .@"0f", ._, ._, ._ }, 2884 .{ ._, ._, .mov, .dst0b, .src1b, ._, ._ }, 2885 } }, 2886 }, .{ 2887 .required_features = .{ .cmov, null, null, null }, 2888 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 2889 .patterns = &.{ 2890 .{ .src = .{ .to_mut_gpr, .mem } }, 2891 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2892 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2893 }, 2894 .dst_temps = .{.{ .ref = .src0 }}, 2895 .clobbers = .{ .eflags = true }, 2896 .each = .{ .once = &.{ 2897 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 2898 .{ ._, ._l, .cmov, .dst0w, .src1w, ._, ._ }, 2899 } }, 2900 }, .{ 2901 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 2902 .patterns = &.{ 2903 .{ .src = .{ .to_mut_gpr, .mem } }, 2904 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2905 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2906 }, 2907 .dst_temps = .{.{ .ref = .src0 }}, 2908 .clobbers = .{ .eflags = true }, 2909 .each = .{ .once = &.{ 2910 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 2911 .{ ._, ._nl, .j, .@"0f", ._, ._, ._ }, 2912 .{ ._, ._, .mov, .dst0w, .src1w, ._, ._ }, 2913 } }, 2914 }, .{ 2915 .required_features = .{ .cmov, null, null, null }, 2916 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 2917 .patterns = &.{ 2918 .{ .src = .{ .to_mut_gpr, .mem } }, 2919 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2920 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2921 }, 2922 .dst_temps = .{.{ .ref = .src0 }}, 2923 .clobbers = .{ .eflags = true }, 2924 .each = .{ .once = &.{ 2925 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 2926 .{ ._, ._b, .cmov, .dst0w, .src1w, ._, ._ }, 2927 } }, 2928 }, .{ 2929 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 2930 .patterns = &.{ 2931 .{ .src = .{ .to_mut_gpr, .mem } }, 2932 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2933 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2934 }, 2935 .dst_temps = .{.{ .ref = .src0 }}, 2936 .clobbers = .{ .eflags = true }, 2937 .each = .{ .once = &.{ 2938 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 2939 .{ ._, ._nb, .j, .@"0f", ._, ._, ._ }, 2940 .{ ._, ._, .mov, .dst0w, .src1w, ._, ._ }, 2941 } }, 2942 }, .{ 2943 .required_features = .{ .cmov, null, null, null }, 2944 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 2945 .patterns = &.{ 2946 .{ .src = .{ .to_mut_gpr, .mem } }, 2947 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2948 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2949 }, 2950 .dst_temps = .{.{ .ref = .src0 }}, 2951 .clobbers = .{ .eflags = true }, 2952 .each = .{ .once = &.{ 2953 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 2954 .{ ._, ._l, .cmov, .dst0d, .src1d, ._, ._ }, 2955 } }, 2956 }, .{ 2957 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 2958 .patterns = &.{ 2959 .{ .src = .{ .to_mut_gpr, .mem } }, 2960 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2961 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2962 }, 2963 .dst_temps = .{.{ .ref = .src0 }}, 2964 .clobbers = .{ .eflags = true }, 2965 .each = .{ .once = &.{ 2966 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 2967 .{ ._, ._nl, .j, .@"0f", ._, ._, ._ }, 2968 .{ ._, ._, .mov, .dst0d, .src1d, ._, ._ }, 2969 } }, 2970 }, .{ 2971 .required_features = .{ .cmov, null, null, null }, 2972 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 2973 .patterns = &.{ 2974 .{ .src = .{ .to_mut_gpr, .mem } }, 2975 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2976 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2977 }, 2978 .dst_temps = .{.{ .ref = .src0 }}, 2979 .clobbers = .{ .eflags = true }, 2980 .each = .{ .once = &.{ 2981 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 2982 .{ ._, ._b, .cmov, .dst0d, .src1d, ._, ._ }, 2983 } }, 2984 }, .{ 2985 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 2986 .patterns = &.{ 2987 .{ .src = .{ .to_mut_gpr, .mem } }, 2988 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 2989 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 2990 }, 2991 .dst_temps = .{.{ .ref = .src0 }}, 2992 .clobbers = .{ .eflags = true }, 2993 .each = .{ .once = &.{ 2994 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 2995 .{ ._, ._nb, .j, .@"0f", ._, ._, ._ }, 2996 .{ ._, ._, .mov, .dst0d, .src1d, ._, ._ }, 2997 } }, 2998 }, .{ 2999 .required_features = .{ .@"64bit", .cmov, null, null }, 3000 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 3001 .patterns = &.{ 3002 .{ .src = .{ .to_mut_gpr, .mem } }, 3003 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 3004 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 3005 }, 3006 .dst_temps = .{.{ .ref = .src0 }}, 3007 .clobbers = .{ .eflags = true }, 3008 .each = .{ .once = &.{ 3009 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 3010 .{ ._, ._l, .cmov, .dst0q, .src1q, ._, ._ }, 3011 } }, 3012 }, .{ 3013 .required_features = .{ .@"64bit", null, null, null }, 3014 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 3015 .patterns = &.{ 3016 .{ .src = .{ .to_mut_gpr, .mem } }, 3017 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 3018 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 3019 }, 3020 .dst_temps = .{.{ .ref = .src0 }}, 3021 .clobbers = .{ .eflags = true }, 3022 .each = .{ .once = &.{ 3023 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 3024 .{ ._, ._nl, .j, .@"0f", ._, ._, ._ }, 3025 .{ ._, ._, .mov, .dst0q, .src1q, ._, ._ }, 3026 } }, 3027 }, .{ 3028 .required_features = .{ .@"64bit", .cmov, null, null }, 3029 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 3030 .patterns = &.{ 3031 .{ .src = .{ .to_mut_gpr, .mem } }, 3032 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 3033 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 3034 }, 3035 .dst_temps = .{.{ .ref = .src0 }}, 3036 .clobbers = .{ .eflags = true }, 3037 .each = .{ .once = &.{ 3038 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 3039 .{ ._, ._b, .cmov, .dst0q, .src1q, ._, ._ }, 3040 } }, 3041 }, .{ 3042 .required_features = .{ .@"64bit", null, null, null }, 3043 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 3044 .patterns = &.{ 3045 .{ .src = .{ .to_mut_gpr, .mem } }, 3046 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 3047 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 3048 }, 3049 .dst_temps = .{.{ .ref = .src0 }}, 3050 .clobbers = .{ .eflags = true }, 3051 .each = .{ .once = &.{ 3052 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 3053 .{ ._, ._nb, .j, .@"0f", ._, ._, ._ }, 3054 .{ ._, ._, .mov, .dst0q, .src1q, ._, ._ }, 3055 } }, 3056 }, .{ 3057 .required_features = .{ .@"64bit", .cmov, null, null }, 3058 .src_constraints = .{ .any_signed_int, .any_signed_int }, 3059 .patterns = &.{ 3060 .{ .src = .{ .to_mem, .to_mem } }, 3061 }, 3062 .extra_temps = .{ 3063 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 3064 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 3065 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 3066 .unused, 3067 .unused, 3068 .unused, 3069 .unused, 3070 .unused, 3071 .unused, 3072 }, 3073 .dst_temps = .{.mem}, 3074 .clobbers = .{ .eflags = true }, 3075 .each = .{ .once = &.{ 3076 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_8), ._, ._ }, 3077 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 3078 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 3079 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 3080 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3081 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3082 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 3083 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -8), ._, ._ }, 3084 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 3085 .{ ._, ._, .lea, .tmp1p, .mem(.src1), ._, ._ }, 3086 .{ ._, ._l, .cmov, .tmp0p, .tmp1p, ._, ._ }, 3087 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 3088 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 3089 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 3090 } }, 3091 }, .{ 3092 .required_features = .{ .@"64bit", null, null, null }, 3093 .src_constraints = .{ .any_signed_int, .any_signed_int }, 3094 .patterns = &.{ 3095 .{ .src = .{ .to_mem, .to_mem } }, 3096 }, 3097 .extra_temps = .{ 3098 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 3099 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 3100 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 3101 .unused, 3102 .unused, 3103 .unused, 3104 .unused, 3105 .unused, 3106 .unused, 3107 }, 3108 .dst_temps = .{.mem}, 3109 .clobbers = .{ .eflags = true }, 3110 .each = .{ .once = &.{ 3111 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_8), ._, ._ }, 3112 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 3113 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 3114 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 3115 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3116 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3117 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 3118 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -8), ._, ._ }, 3119 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 3120 .{ ._, ._nl, .j, .@"0f", ._, ._, ._ }, 3121 .{ ._, ._, .lea, .tmp0p, .mem(.src1), ._, ._ }, 3122 .{ .@"0:", ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 3123 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 3124 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 3125 } }, 3126 }, .{ 3127 .required_features = .{ .@"64bit", .cmov, null, null }, 3128 .src_constraints = .{ .any_unsigned_int, .any_unsigned_int }, 3129 .patterns = &.{ 3130 .{ .src = .{ .to_mem, .to_mem } }, 3131 }, 3132 .extra_temps = .{ 3133 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 3134 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 3135 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 3136 .unused, 3137 .unused, 3138 .unused, 3139 .unused, 3140 .unused, 3141 .unused, 3142 }, 3143 .dst_temps = .{.mem}, 3144 .clobbers = .{ .eflags = true }, 3145 .each = .{ .once = &.{ 3146 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_8), ._, ._ }, 3147 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 3148 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_size), ._, ._ }, 3149 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"8", .tmp0, .add_size), ._, ._ }, 3150 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3151 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3152 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 3153 .{ ._, ._, .lea, .tmp1p, .mem(.src1), ._, ._ }, 3154 .{ ._, ._b, .cmov, .tmp0p, .tmp1p, ._, ._ }, 3155 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 3156 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 3157 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 3158 } }, 3159 }, .{ 3160 .required_features = .{ .@"64bit", null, null, null }, 3161 .src_constraints = .{ .any_unsigned_int, .any_unsigned_int }, 3162 .patterns = &.{ 3163 .{ .src = .{ .to_mem, .to_mem } }, 3164 }, 3165 .extra_temps = .{ 3166 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 3167 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 3168 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 3169 .unused, 3170 .unused, 3171 .unused, 3172 .unused, 3173 .unused, 3174 .unused, 3175 }, 3176 .dst_temps = .{.mem}, 3177 .clobbers = .{ .eflags = true }, 3178 .each = .{ .once = &.{ 3179 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_8), ._, ._ }, 3180 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 3181 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_size), ._, ._ }, 3182 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"8", .tmp0, .add_size), ._, ._ }, 3183 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3184 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3185 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 3186 .{ ._, ._nb, .j, .@"0f", ._, ._, ._ }, 3187 .{ ._, ._, .lea, .tmp0p, .mem(.src1), ._, ._ }, 3188 .{ .@"0:", ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 3189 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 3190 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 3191 } }, 3192 }, .{ 3193 .required_features = .{ .avx, null, null, null }, 3194 .src_constraints = .{ 3195 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3196 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3197 }, 3198 .patterns = &.{ 3199 .{ .src = .{ .to_sse, .mem } }, 3200 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3201 .{ .src = .{ .to_sse, .to_sse } }, 3202 }, 3203 .dst_temps = .{.{ .rc = .sse }}, 3204 .each = .{ .once = &.{ 3205 .{ ._, .vp_b, .maxs, .dst0x, .src0x, .src1x, ._ }, 3206 } }, 3207 }, .{ 3208 .required_features = .{ .sse4_1, null, null, null }, 3209 .src_constraints = .{ 3210 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3211 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3212 }, 3213 .patterns = &.{ 3214 .{ .src = .{ .to_mut_sse, .mem } }, 3215 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 3216 .{ .src = .{ .to_mut_sse, .to_sse } }, 3217 }, 3218 .dst_temps = .{.{ .ref = .src0 }}, 3219 .each = .{ .once = &.{ 3220 .{ ._, .p_b, .maxs, .dst0x, .src1x, ._, ._ }, 3221 } }, 3222 }, .{ 3223 .required_features = .{ .sse2, null, null, null }, 3224 .src_constraints = .{ 3225 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3226 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3227 }, 3228 .patterns = &.{ 3229 .{ .src = .{ .to_mut_sse, .mem } }, 3230 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 3231 .{ .src = .{ .to_mut_sse, .to_sse } }, 3232 }, 3233 .dst_temps = .{.{ .rc = .sse }}, 3234 .each = .{ .once = &.{ 3235 .{ ._, ._dqa, .mov, .dst0x, .src0x, ._, ._ }, 3236 .{ ._, .p_b, .cmpgt, .dst0x, .src1x, ._, ._ }, 3237 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 3238 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 3239 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 3240 } }, 3241 }, .{ 3242 .required_features = .{ .avx2, null, null, null }, 3243 .src_constraints = .{ 3244 .{ .scalar_signed_int = .{ .of = .yword, .is = .byte } }, 3245 .{ .scalar_signed_int = .{ .of = .yword, .is = .byte } }, 3246 }, 3247 .patterns = &.{ 3248 .{ .src = .{ .to_sse, .mem } }, 3249 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3250 .{ .src = .{ .to_sse, .to_sse } }, 3251 }, 3252 .dst_temps = .{.{ .rc = .sse }}, 3253 .each = .{ .once = &.{ 3254 .{ ._, .vp_b, .maxs, .dst0y, .src0y, .src1y, ._ }, 3255 } }, 3256 }, .{ 3257 .required_features = .{ .avx2, null, null, null }, 3258 .src_constraints = .{ 3259 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .byte } }, 3260 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .byte } }, 3261 }, 3262 .patterns = &.{ 3263 .{ .src = .{ .to_mem, .to_mem } }, 3264 }, 3265 .extra_temps = .{ 3266 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3267 .{ .type = .vector_32_i8, .kind = .{ .rc = .sse } }, 3268 .unused, 3269 .unused, 3270 .unused, 3271 .unused, 3272 .unused, 3273 .unused, 3274 .unused, 3275 }, 3276 .dst_temps = .{.mem}, 3277 .clobbers = .{ .eflags = true }, 3278 .each = .{ .once = &.{ 3279 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3280 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 3281 .{ ._, .vp_b, .maxs, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 3282 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 3283 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 3284 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3285 } }, 3286 }, .{ 3287 .required_features = .{ .avx, null, null, null }, 3288 .src_constraints = .{ 3289 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3290 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3291 }, 3292 .patterns = &.{ 3293 .{ .src = .{ .to_mem, .to_mem } }, 3294 }, 3295 .extra_temps = .{ 3296 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3297 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 3298 .unused, 3299 .unused, 3300 .unused, 3301 .unused, 3302 .unused, 3303 .unused, 3304 .unused, 3305 }, 3306 .dst_temps = .{.mem}, 3307 .clobbers = .{ .eflags = true }, 3308 .each = .{ .once = &.{ 3309 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3310 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3311 .{ ._, .vp_b, .maxs, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 3312 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3313 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3314 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3315 } }, 3316 }, .{ 3317 .required_features = .{ .sse4_1, null, null, null }, 3318 .src_constraints = .{ 3319 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3320 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3321 }, 3322 .patterns = &.{ 3323 .{ .src = .{ .to_mem, .to_mem } }, 3324 }, 3325 .extra_temps = .{ 3326 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3327 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 3328 .unused, 3329 .unused, 3330 .unused, 3331 .unused, 3332 .unused, 3333 .unused, 3334 .unused, 3335 }, 3336 .dst_temps = .{.mem}, 3337 .clobbers = .{ .eflags = true }, 3338 .each = .{ .once = &.{ 3339 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3340 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3341 .{ ._, .p_b, .maxs, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 3342 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3343 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3344 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3345 } }, 3346 }, .{ 3347 .required_features = .{ .sse2, null, null, null }, 3348 .src_constraints = .{ 3349 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3350 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 3351 }, 3352 .patterns = &.{ 3353 .{ .src = .{ .to_mem, .to_mem } }, 3354 }, 3355 .extra_temps = .{ 3356 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3357 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 3358 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 3359 .unused, 3360 .unused, 3361 .unused, 3362 .unused, 3363 .unused, 3364 .unused, 3365 }, 3366 .dst_temps = .{.mem}, 3367 .clobbers = .{ .eflags = true }, 3368 .each = .{ .once = &.{ 3369 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3370 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3371 .{ ._, ._dqa, .mov, .tmp2x, .tmp1x, ._, ._ }, 3372 .{ ._, .p_b, .cmpgt, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 3373 .{ ._, .p_, .@"and", .tmp2x, .tmp1x, ._, ._ }, 3374 .{ ._, .p_, .andn, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 3375 .{ ._, .p_, .@"or", .tmp1x, .tmp2x, ._, ._ }, 3376 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3377 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3378 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3379 } }, 3380 }, .{ 3381 .required_features = .{ .cmov, .slow_incdec, null, null }, 3382 .src_constraints = .{ 3383 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3384 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3385 }, 3386 .patterns = &.{ 3387 .{ .src = .{ .to_mem, .to_mem } }, 3388 }, 3389 .extra_temps = .{ 3390 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3391 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3392 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3393 .unused, 3394 .unused, 3395 .unused, 3396 .unused, 3397 .unused, 3398 .unused, 3399 }, 3400 .dst_temps = .{.mem}, 3401 .clobbers = .{ .eflags = true }, 3402 .each = .{ .once = &.{ 3403 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3404 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3405 .{ ._, ._, .movsx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3406 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 3407 .{ ._, ._l, .cmov, .tmp1d, .tmp2d, ._, ._ }, 3408 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3409 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 3410 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3411 } }, 3412 }, .{ 3413 .required_features = .{ .cmov, null, null, null }, 3414 .src_constraints = .{ 3415 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3416 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3417 }, 3418 .patterns = &.{ 3419 .{ .src = .{ .to_mem, .to_mem } }, 3420 }, 3421 .extra_temps = .{ 3422 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3423 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3424 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3425 .unused, 3426 .unused, 3427 .unused, 3428 .unused, 3429 .unused, 3430 .unused, 3431 }, 3432 .dst_temps = .{.mem}, 3433 .clobbers = .{ .eflags = true }, 3434 .each = .{ .once = &.{ 3435 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3436 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3437 .{ ._, ._, .movsx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3438 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 3439 .{ ._, ._l, .cmov, .tmp1d, .tmp2d, ._, ._ }, 3440 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3441 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3442 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3443 } }, 3444 }, .{ 3445 .required_features = .{ .slow_incdec, null, null, null }, 3446 .src_constraints = .{ 3447 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3448 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3449 }, 3450 .patterns = &.{ 3451 .{ .src = .{ .to_mem, .to_mem } }, 3452 }, 3453 .extra_temps = .{ 3454 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3455 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3456 .unused, 3457 .unused, 3458 .unused, 3459 .unused, 3460 .unused, 3461 .unused, 3462 .unused, 3463 }, 3464 .dst_temps = .{.mem}, 3465 .clobbers = .{ .eflags = true }, 3466 .each = .{ .once = &.{ 3467 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3468 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3469 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3470 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 3471 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3472 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3473 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 3474 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3475 } }, 3476 }, .{ 3477 .src_constraints = .{ 3478 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3479 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 3480 }, 3481 .patterns = &.{ 3482 .{ .src = .{ .to_mem, .to_mem } }, 3483 }, 3484 .extra_temps = .{ 3485 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3486 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 3487 .unused, 3488 .unused, 3489 .unused, 3490 .unused, 3491 .unused, 3492 .unused, 3493 .unused, 3494 }, 3495 .dst_temps = .{.mem}, 3496 .clobbers = .{ .eflags = true }, 3497 .each = .{ .once = &.{ 3498 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3499 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3500 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3501 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 3502 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3503 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3504 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3505 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3506 } }, 3507 }, .{ 3508 .required_features = .{ .sse, .mmx, null, null }, 3509 .src_constraints = .{ 3510 .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, 3511 .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, 3512 }, 3513 .patterns = &.{ 3514 .{ .src = .{ .to_mut_mmx, .mem } }, 3515 .{ .src = .{ .mem, .to_mut_mmx }, .commute = .{ 0, 1 } }, 3516 .{ .src = .{ .to_mut_mmx, .to_mmx } }, 3517 }, 3518 .dst_temps = .{.{ .ref = .src0 }}, 3519 .each = .{ .once = &.{ 3520 .{ ._, .p_b, .maxu, .dst0q, .src1q, ._, ._ }, 3521 } }, 3522 }, .{ 3523 .required_features = .{ .avx, null, null, null }, 3524 .src_constraints = .{ 3525 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3526 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3527 }, 3528 .patterns = &.{ 3529 .{ .src = .{ .to_sse, .mem } }, 3530 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3531 .{ .src = .{ .to_sse, .to_sse } }, 3532 }, 3533 .dst_temps = .{.{ .rc = .sse }}, 3534 .each = .{ .once = &.{ 3535 .{ ._, .vp_b, .maxu, .dst0x, .src0x, .src1x, ._ }, 3536 } }, 3537 }, .{ 3538 .required_features = .{ .sse2, null, null, null }, 3539 .src_constraints = .{ 3540 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3541 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3542 }, 3543 .patterns = &.{ 3544 .{ .src = .{ .to_mut_sse, .mem } }, 3545 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 3546 .{ .src = .{ .to_mut_sse, .to_sse } }, 3547 }, 3548 .dst_temps = .{.{ .ref = .src0 }}, 3549 .each = .{ .once = &.{ 3550 .{ ._, .p_b, .maxu, .dst0x, .src1x, ._, ._ }, 3551 } }, 3552 }, .{ 3553 .required_features = .{ .avx2, null, null, null }, 3554 .src_constraints = .{ 3555 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 3556 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 3557 }, 3558 .patterns = &.{ 3559 .{ .src = .{ .to_sse, .mem } }, 3560 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3561 .{ .src = .{ .to_sse, .to_sse } }, 3562 }, 3563 .dst_temps = .{.{ .rc = .sse }}, 3564 .each = .{ .once = &.{ 3565 .{ ._, .vp_b, .maxu, .dst0y, .src0y, .src1y, ._ }, 3566 } }, 3567 }, .{ 3568 .required_features = .{ .avx2, null, null, null }, 3569 .src_constraints = .{ 3570 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 3571 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 3572 }, 3573 .patterns = &.{ 3574 .{ .src = .{ .to_mem, .to_mem } }, 3575 }, 3576 .extra_temps = .{ 3577 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3578 .{ .type = .vector_32_u8, .kind = .{ .rc = .sse } }, 3579 .unused, 3580 .unused, 3581 .unused, 3582 .unused, 3583 .unused, 3584 .unused, 3585 .unused, 3586 }, 3587 .dst_temps = .{.mem}, 3588 .clobbers = .{ .eflags = true }, 3589 .each = .{ .once = &.{ 3590 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3591 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 3592 .{ ._, .vp_b, .maxu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 3593 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 3594 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 3595 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3596 } }, 3597 }, .{ 3598 .required_features = .{ .avx, null, null, null }, 3599 .src_constraints = .{ 3600 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3601 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3602 }, 3603 .patterns = &.{ 3604 .{ .src = .{ .to_mem, .to_mem } }, 3605 }, 3606 .extra_temps = .{ 3607 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3608 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 3609 .unused, 3610 .unused, 3611 .unused, 3612 .unused, 3613 .unused, 3614 .unused, 3615 .unused, 3616 }, 3617 .dst_temps = .{.mem}, 3618 .clobbers = .{ .eflags = true }, 3619 .each = .{ .once = &.{ 3620 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3621 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3622 .{ ._, .vp_b, .maxu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 3623 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3624 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3625 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3626 } }, 3627 }, .{ 3628 .required_features = .{ .sse2, null, null, null }, 3629 .src_constraints = .{ 3630 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3631 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 3632 }, 3633 .patterns = &.{ 3634 .{ .src = .{ .to_mem, .to_mem } }, 3635 }, 3636 .extra_temps = .{ 3637 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3638 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 3639 .unused, 3640 .unused, 3641 .unused, 3642 .unused, 3643 .unused, 3644 .unused, 3645 .unused, 3646 }, 3647 .dst_temps = .{.mem}, 3648 .clobbers = .{ .eflags = true }, 3649 .each = .{ .once = &.{ 3650 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3651 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3652 .{ ._, .p_b, .maxu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 3653 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3654 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3655 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3656 } }, 3657 }, .{ 3658 .required_features = .{ .cmov, .slow_incdec, null, null }, 3659 .src_constraints = .{ 3660 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3661 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3662 }, 3663 .patterns = &.{ 3664 .{ .src = .{ .to_mem, .to_mem } }, 3665 }, 3666 .extra_temps = .{ 3667 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3668 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3669 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3670 .unused, 3671 .unused, 3672 .unused, 3673 .unused, 3674 .unused, 3675 .unused, 3676 }, 3677 .dst_temps = .{.mem}, 3678 .clobbers = .{ .eflags = true }, 3679 .each = .{ .once = &.{ 3680 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3681 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3682 .{ ._, ._, .movzx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3683 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 3684 .{ ._, ._b, .cmov, .tmp1d, .tmp2d, ._, ._ }, 3685 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3686 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 3687 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3688 } }, 3689 }, .{ 3690 .required_features = .{ .cmov, null, null, null }, 3691 .src_constraints = .{ 3692 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3693 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3694 }, 3695 .patterns = &.{ 3696 .{ .src = .{ .to_mem, .to_mem } }, 3697 }, 3698 .extra_temps = .{ 3699 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3700 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3701 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3702 .unused, 3703 .unused, 3704 .unused, 3705 .unused, 3706 .unused, 3707 .unused, 3708 }, 3709 .dst_temps = .{.mem}, 3710 .clobbers = .{ .eflags = true }, 3711 .each = .{ .once = &.{ 3712 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3713 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3714 .{ ._, ._, .movzx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3715 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 3716 .{ ._, ._b, .cmov, .tmp1d, .tmp2d, ._, ._ }, 3717 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3718 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3719 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3720 } }, 3721 }, .{ 3722 .required_features = .{ .slow_incdec, null, null, null }, 3723 .src_constraints = .{ 3724 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3725 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3726 }, 3727 .patterns = &.{ 3728 .{ .src = .{ .to_mem, .to_mem } }, 3729 }, 3730 .extra_temps = .{ 3731 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3732 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3733 .unused, 3734 .unused, 3735 .unused, 3736 .unused, 3737 .unused, 3738 .unused, 3739 .unused, 3740 }, 3741 .dst_temps = .{.mem}, 3742 .clobbers = .{ .eflags = true }, 3743 .each = .{ .once = &.{ 3744 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3745 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3746 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3747 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 3748 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3749 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3750 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 3751 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3752 } }, 3753 }, .{ 3754 .src_constraints = .{ 3755 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3756 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 3757 }, 3758 .patterns = &.{ 3759 .{ .src = .{ .to_mem, .to_mem } }, 3760 }, 3761 .extra_temps = .{ 3762 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3763 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 3764 .unused, 3765 .unused, 3766 .unused, 3767 .unused, 3768 .unused, 3769 .unused, 3770 .unused, 3771 }, 3772 .dst_temps = .{.mem}, 3773 .clobbers = .{ .eflags = true }, 3774 .each = .{ .once = &.{ 3775 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3776 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 3777 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3778 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 3779 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 3780 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 3781 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 3782 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 3783 } }, 3784 }, .{ 3785 .required_features = .{ .sse, .mmx, null, null }, 3786 .src_constraints = .{ 3787 .{ .scalar_signed_int = .{ .of = .qword, .is = .word } }, 3788 .{ .scalar_signed_int = .{ .of = .qword, .is = .word } }, 3789 }, 3790 .patterns = &.{ 3791 .{ .src = .{ .to_mut_mmx, .mem } }, 3792 .{ .src = .{ .mem, .to_mut_mmx }, .commute = .{ 0, 1 } }, 3793 .{ .src = .{ .to_mut_mmx, .to_mmx } }, 3794 }, 3795 .dst_temps = .{.{ .ref = .src0 }}, 3796 .each = .{ .once = &.{ 3797 .{ ._, .p_w, .maxs, .dst0q, .src1q, ._, ._ }, 3798 } }, 3799 }, .{ 3800 .required_features = .{ .avx, null, null, null }, 3801 .src_constraints = .{ 3802 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 3803 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 3804 }, 3805 .patterns = &.{ 3806 .{ .src = .{ .to_sse, .mem } }, 3807 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3808 .{ .src = .{ .to_sse, .to_sse } }, 3809 }, 3810 .dst_temps = .{.{ .rc = .sse }}, 3811 .each = .{ .once = &.{ 3812 .{ ._, .vp_w, .maxs, .dst0x, .src0x, .src1x, ._ }, 3813 } }, 3814 }, .{ 3815 .required_features = .{ .sse2, null, null, null }, 3816 .src_constraints = .{ 3817 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 3818 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 3819 }, 3820 .patterns = &.{ 3821 .{ .src = .{ .to_mut_sse, .mem } }, 3822 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 3823 .{ .src = .{ .to_mut_sse, .to_sse } }, 3824 }, 3825 .dst_temps = .{.{ .ref = .src0 }}, 3826 .each = .{ .once = &.{ 3827 .{ ._, .p_w, .maxs, .dst0x, .src1x, ._, ._ }, 3828 } }, 3829 }, .{ 3830 .required_features = .{ .avx2, null, null, null }, 3831 .src_constraints = .{ 3832 .{ .scalar_signed_int = .{ .of = .yword, .is = .word } }, 3833 .{ .scalar_signed_int = .{ .of = .yword, .is = .word } }, 3834 }, 3835 .patterns = &.{ 3836 .{ .src = .{ .to_sse, .mem } }, 3837 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 3838 .{ .src = .{ .to_sse, .to_sse } }, 3839 }, 3840 .dst_temps = .{.{ .rc = .sse }}, 3841 .each = .{ .once = &.{ 3842 .{ ._, .vp_w, .maxs, .dst0y, .src0y, .src1y, ._ }, 3843 } }, 3844 }, .{ 3845 .required_features = .{ .avx2, null, null, null }, 3846 .src_constraints = .{ 3847 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .word } }, 3848 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .word } }, 3849 }, 3850 .patterns = &.{ 3851 .{ .src = .{ .to_mem, .to_mem } }, 3852 }, 3853 .extra_temps = .{ 3854 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3855 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 3856 .unused, 3857 .unused, 3858 .unused, 3859 .unused, 3860 .unused, 3861 .unused, 3862 .unused, 3863 }, 3864 .dst_temps = .{.mem}, 3865 .clobbers = .{ .eflags = true }, 3866 .each = .{ .once = &.{ 3867 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3868 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 3869 .{ ._, .vp_w, .maxs, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 3870 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 3871 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 3872 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3873 } }, 3874 }, .{ 3875 .required_features = .{ .avx, null, null, null }, 3876 .src_constraints = .{ 3877 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 3878 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 3879 }, 3880 .patterns = &.{ 3881 .{ .src = .{ .to_mem, .to_mem } }, 3882 }, 3883 .extra_temps = .{ 3884 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3885 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 3886 .unused, 3887 .unused, 3888 .unused, 3889 .unused, 3890 .unused, 3891 .unused, 3892 .unused, 3893 }, 3894 .dst_temps = .{.mem}, 3895 .clobbers = .{ .eflags = true }, 3896 .each = .{ .once = &.{ 3897 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3898 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3899 .{ ._, .vp_w, .maxs, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 3900 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3901 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3902 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3903 } }, 3904 }, .{ 3905 .required_features = .{ .sse2, null, null, null }, 3906 .src_constraints = .{ 3907 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 3908 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 3909 }, 3910 .patterns = &.{ 3911 .{ .src = .{ .to_mem, .to_mem } }, 3912 }, 3913 .extra_temps = .{ 3914 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3915 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 3916 .unused, 3917 .unused, 3918 .unused, 3919 .unused, 3920 .unused, 3921 .unused, 3922 .unused, 3923 }, 3924 .dst_temps = .{.mem}, 3925 .clobbers = .{ .eflags = true }, 3926 .each = .{ .once = &.{ 3927 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3928 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 3929 .{ ._, .p_w, .maxs, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 3930 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 3931 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 3932 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3933 } }, 3934 }, .{ 3935 .required_features = .{ .cmov, null, null, null }, 3936 .src_constraints = .{ 3937 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 3938 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 3939 }, 3940 .patterns = &.{ 3941 .{ .src = .{ .to_mem, .to_mem } }, 3942 }, 3943 .extra_temps = .{ 3944 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3945 .{ .type = .i16, .kind = .{ .rc = .general_purpose } }, 3946 .unused, 3947 .unused, 3948 .unused, 3949 .unused, 3950 .unused, 3951 .unused, 3952 .unused, 3953 }, 3954 .dst_temps = .{.mem}, 3955 .clobbers = .{ .eflags = true }, 3956 .each = .{ .once = &.{ 3957 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3958 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 3959 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 3960 .{ ._, ._l, .cmov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 3961 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 3962 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 3963 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3964 } }, 3965 }, .{ 3966 .src_constraints = .{ 3967 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 3968 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 3969 }, 3970 .patterns = &.{ 3971 .{ .src = .{ .to_mem, .to_mem } }, 3972 }, 3973 .extra_temps = .{ 3974 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 3975 .{ .type = .i16, .kind = .{ .rc = .general_purpose } }, 3976 .unused, 3977 .unused, 3978 .unused, 3979 .unused, 3980 .unused, 3981 .unused, 3982 .unused, 3983 }, 3984 .dst_temps = .{.mem}, 3985 .clobbers = .{ .eflags = true }, 3986 .each = .{ .once = &.{ 3987 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 3988 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 3989 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 3990 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 3991 .{ ._, ._, .mov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 3992 .{ .@"1:", ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 3993 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 3994 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 3995 } }, 3996 }, .{ 3997 .required_features = .{ .avx, null, null, null }, 3998 .src_constraints = .{ 3999 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4000 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4001 }, 4002 .patterns = &.{ 4003 .{ .src = .{ .to_sse, .mem } }, 4004 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4005 .{ .src = .{ .to_sse, .to_sse } }, 4006 }, 4007 .dst_temps = .{.{ .rc = .sse }}, 4008 .each = .{ .once = &.{ 4009 .{ ._, .vp_w, .maxu, .dst0x, .src0x, .src1x, ._ }, 4010 } }, 4011 }, .{ 4012 .required_features = .{ .sse4_1, null, null, null }, 4013 .src_constraints = .{ 4014 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4015 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4016 }, 4017 .patterns = &.{ 4018 .{ .src = .{ .to_mut_sse, .mem } }, 4019 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4020 .{ .src = .{ .to_mut_sse, .to_sse } }, 4021 }, 4022 .dst_temps = .{.{ .ref = .src0 }}, 4023 .each = .{ .once = &.{ 4024 .{ ._, .p_w, .maxu, .dst0x, .src1x, ._, ._ }, 4025 } }, 4026 }, .{ 4027 .required_features = .{ .sse2, null, null, null }, 4028 .src_constraints = .{ 4029 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4030 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4031 }, 4032 .patterns = &.{ 4033 .{ .src = .{ .to_mut_sse, .mem } }, 4034 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4035 .{ .src = .{ .to_mut_sse, .to_sse } }, 4036 }, 4037 .dst_temps = .{.{ .ref = .src0 }}, 4038 .each = .{ .once = &.{ 4039 .{ ._, .p_w, .subus, .dst0x, .src1x, ._, ._ }, 4040 .{ ._, .p_w, .add, .dst0x, .src1x, ._, ._ }, 4041 } }, 4042 }, .{ 4043 .required_features = .{ .avx2, null, null, null }, 4044 .src_constraints = .{ 4045 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 4046 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 4047 }, 4048 .patterns = &.{ 4049 .{ .src = .{ .to_sse, .mem } }, 4050 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4051 .{ .src = .{ .to_sse, .to_sse } }, 4052 }, 4053 .dst_temps = .{.{ .rc = .sse }}, 4054 .each = .{ .once = &.{ 4055 .{ ._, .vp_w, .maxu, .dst0y, .src0y, .src1y, ._ }, 4056 } }, 4057 }, .{ 4058 .required_features = .{ .avx2, null, null, null }, 4059 .src_constraints = .{ 4060 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 4061 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 4062 }, 4063 .patterns = &.{ 4064 .{ .src = .{ .to_mem, .to_mem } }, 4065 }, 4066 .extra_temps = .{ 4067 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4068 .{ .type = .vector_16_u16, .kind = .{ .rc = .sse } }, 4069 .unused, 4070 .unused, 4071 .unused, 4072 .unused, 4073 .unused, 4074 .unused, 4075 .unused, 4076 }, 4077 .dst_temps = .{.mem}, 4078 .clobbers = .{ .eflags = true }, 4079 .each = .{ .once = &.{ 4080 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4081 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 4082 .{ ._, .vp_w, .maxu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 4083 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 4084 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 4085 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4086 } }, 4087 }, .{ 4088 .required_features = .{ .avx, null, null, null }, 4089 .src_constraints = .{ 4090 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4091 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4092 }, 4093 .patterns = &.{ 4094 .{ .src = .{ .to_mem, .to_mem } }, 4095 }, 4096 .extra_temps = .{ 4097 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4098 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 4099 .unused, 4100 .unused, 4101 .unused, 4102 .unused, 4103 .unused, 4104 .unused, 4105 .unused, 4106 }, 4107 .dst_temps = .{.mem}, 4108 .clobbers = .{ .eflags = true }, 4109 .each = .{ .once = &.{ 4110 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4111 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4112 .{ ._, .vp_w, .maxu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 4113 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4114 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4115 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4116 } }, 4117 }, .{ 4118 .required_features = .{ .sse4_1, null, null, null }, 4119 .src_constraints = .{ 4120 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4121 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4122 }, 4123 .patterns = &.{ 4124 .{ .src = .{ .to_mem, .to_mem } }, 4125 }, 4126 .extra_temps = .{ 4127 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4128 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 4129 .unused, 4130 .unused, 4131 .unused, 4132 .unused, 4133 .unused, 4134 .unused, 4135 .unused, 4136 }, 4137 .dst_temps = .{.mem}, 4138 .clobbers = .{ .eflags = true }, 4139 .each = .{ .once = &.{ 4140 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4141 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4142 .{ ._, .p_w, .maxu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4143 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4144 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4145 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4146 } }, 4147 }, .{ 4148 .required_features = .{ .sse2, null, null, null }, 4149 .src_constraints = .{ 4150 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4151 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 4152 }, 4153 .patterns = &.{ 4154 .{ .src = .{ .to_mem, .to_mem } }, 4155 }, 4156 .extra_temps = .{ 4157 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4158 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 4159 .unused, 4160 .unused, 4161 .unused, 4162 .unused, 4163 .unused, 4164 .unused, 4165 .unused, 4166 }, 4167 .dst_temps = .{.mem}, 4168 .clobbers = .{ .eflags = true }, 4169 .each = .{ .once = &.{ 4170 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4171 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4172 .{ ._, .p_w, .subus, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4173 .{ ._, .p_w, .add, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4174 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4175 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4176 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4177 } }, 4178 }, .{ 4179 .required_features = .{ .cmov, null, null, null }, 4180 .src_constraints = .{ 4181 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 4182 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 4183 }, 4184 .patterns = &.{ 4185 .{ .src = .{ .to_mem, .to_mem } }, 4186 }, 4187 .extra_temps = .{ 4188 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4189 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 4190 .unused, 4191 .unused, 4192 .unused, 4193 .unused, 4194 .unused, 4195 .unused, 4196 .unused, 4197 }, 4198 .dst_temps = .{.mem}, 4199 .clobbers = .{ .eflags = true }, 4200 .each = .{ .once = &.{ 4201 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4202 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 4203 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 4204 .{ ._, ._b, .cmov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 4205 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 4206 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 4207 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4208 } }, 4209 }, .{ 4210 .src_constraints = .{ 4211 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 4212 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 4213 }, 4214 .patterns = &.{ 4215 .{ .src = .{ .to_mem, .to_mem } }, 4216 }, 4217 .extra_temps = .{ 4218 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4219 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 4220 .unused, 4221 .unused, 4222 .unused, 4223 .unused, 4224 .unused, 4225 .unused, 4226 .unused, 4227 }, 4228 .dst_temps = .{.mem}, 4229 .clobbers = .{ .eflags = true }, 4230 .each = .{ .once = &.{ 4231 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4232 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 4233 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 4234 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 4235 .{ ._, ._, .mov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 4236 .{ .@"1:", ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 4237 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 4238 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4239 } }, 4240 }, .{ 4241 .required_features = .{ .avx, null, null, null }, 4242 .src_constraints = .{ 4243 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4244 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4245 }, 4246 .patterns = &.{ 4247 .{ .src = .{ .to_sse, .mem } }, 4248 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4249 .{ .src = .{ .to_sse, .to_sse } }, 4250 }, 4251 .dst_temps = .{.{ .rc = .sse }}, 4252 .each = .{ .once = &.{ 4253 .{ ._, .vp_d, .maxs, .dst0x, .src0x, .src1x, ._ }, 4254 } }, 4255 }, .{ 4256 .required_features = .{ .sse4_1, null, null, null }, 4257 .src_constraints = .{ 4258 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4259 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4260 }, 4261 .patterns = &.{ 4262 .{ .src = .{ .to_mut_sse, .mem } }, 4263 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4264 .{ .src = .{ .to_mut_sse, .to_sse } }, 4265 }, 4266 .dst_temps = .{.{ .ref = .src0 }}, 4267 .each = .{ .once = &.{ 4268 .{ ._, .p_d, .maxs, .dst0x, .src1x, ._, ._ }, 4269 } }, 4270 }, .{ 4271 .required_features = .{ .sse2, null, null, null }, 4272 .src_constraints = .{ 4273 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4274 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4275 }, 4276 .patterns = &.{ 4277 .{ .src = .{ .to_mut_sse, .mem } }, 4278 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4279 .{ .src = .{ .to_mut_sse, .to_sse } }, 4280 }, 4281 .dst_temps = .{.{ .rc = .sse }}, 4282 .each = .{ .once = &.{ 4283 .{ ._, ._dqa, .mov, .dst0x, .src0x, ._, ._ }, 4284 .{ ._, .p_d, .cmpgt, .dst0x, .src1x, ._, ._ }, 4285 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 4286 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 4287 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 4288 } }, 4289 }, .{ 4290 .required_features = .{ .avx2, null, null, null }, 4291 .src_constraints = .{ 4292 .{ .scalar_signed_int = .{ .of = .yword, .is = .dword } }, 4293 .{ .scalar_signed_int = .{ .of = .yword, .is = .dword } }, 4294 }, 4295 .patterns = &.{ 4296 .{ .src = .{ .to_sse, .mem } }, 4297 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4298 .{ .src = .{ .to_sse, .to_sse } }, 4299 }, 4300 .dst_temps = .{.{ .rc = .sse }}, 4301 .each = .{ .once = &.{ 4302 .{ ._, .vp_d, .maxs, .dst0y, .src0y, .src1y, ._ }, 4303 } }, 4304 }, .{ 4305 .required_features = .{ .avx2, null, null, null }, 4306 .src_constraints = .{ 4307 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .dword } }, 4308 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .dword } }, 4309 }, 4310 .patterns = &.{ 4311 .{ .src = .{ .to_mem, .to_mem } }, 4312 }, 4313 .extra_temps = .{ 4314 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4315 .{ .type = .vector_8_i32, .kind = .{ .rc = .sse } }, 4316 .unused, 4317 .unused, 4318 .unused, 4319 .unused, 4320 .unused, 4321 .unused, 4322 .unused, 4323 }, 4324 .dst_temps = .{.mem}, 4325 .clobbers = .{ .eflags = true }, 4326 .each = .{ .once = &.{ 4327 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4328 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 4329 .{ ._, .vp_d, .maxs, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 4330 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 4331 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 4332 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4333 } }, 4334 }, .{ 4335 .required_features = .{ .avx, null, null, null }, 4336 .src_constraints = .{ 4337 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4338 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4339 }, 4340 .patterns = &.{ 4341 .{ .src = .{ .to_mem, .to_mem } }, 4342 }, 4343 .extra_temps = .{ 4344 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4345 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 4346 .unused, 4347 .unused, 4348 .unused, 4349 .unused, 4350 .unused, 4351 .unused, 4352 .unused, 4353 }, 4354 .dst_temps = .{.mem}, 4355 .clobbers = .{ .eflags = true }, 4356 .each = .{ .once = &.{ 4357 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4358 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4359 .{ ._, .vp_d, .maxs, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 4360 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4361 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4362 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4363 } }, 4364 }, .{ 4365 .required_features = .{ .sse4_1, null, null, null }, 4366 .src_constraints = .{ 4367 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4368 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4369 }, 4370 .patterns = &.{ 4371 .{ .src = .{ .to_mem, .to_mem } }, 4372 }, 4373 .extra_temps = .{ 4374 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4375 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 4376 .unused, 4377 .unused, 4378 .unused, 4379 .unused, 4380 .unused, 4381 .unused, 4382 .unused, 4383 }, 4384 .dst_temps = .{.mem}, 4385 .clobbers = .{ .eflags = true }, 4386 .each = .{ .once = &.{ 4387 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4388 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4389 .{ ._, .p_d, .maxs, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4390 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4391 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4392 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4393 } }, 4394 }, .{ 4395 .required_features = .{ .sse2, null, null, null }, 4396 .src_constraints = .{ 4397 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4398 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 4399 }, 4400 .patterns = &.{ 4401 .{ .src = .{ .to_mem, .to_mem } }, 4402 }, 4403 .extra_temps = .{ 4404 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4405 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 4406 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 4407 .unused, 4408 .unused, 4409 .unused, 4410 .unused, 4411 .unused, 4412 .unused, 4413 }, 4414 .dst_temps = .{.mem}, 4415 .clobbers = .{ .eflags = true }, 4416 .each = .{ .once = &.{ 4417 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4418 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4419 .{ ._, ._dqa, .mov, .tmp2x, .tmp1x, ._, ._ }, 4420 .{ ._, .p_d, .cmpgt, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4421 .{ ._, .p_, .@"and", .tmp2x, .tmp1x, ._, ._ }, 4422 .{ ._, .p_, .andn, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4423 .{ ._, .p_, .@"or", .tmp1x, .tmp2x, ._, ._ }, 4424 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4425 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4426 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4427 } }, 4428 }, .{ 4429 .required_features = .{ .cmov, null, null, null }, 4430 .src_constraints = .{ 4431 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 4432 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 4433 }, 4434 .patterns = &.{ 4435 .{ .src = .{ .to_mem, .to_mem } }, 4436 }, 4437 .extra_temps = .{ 4438 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4439 .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, 4440 .unused, 4441 .unused, 4442 .unused, 4443 .unused, 4444 .unused, 4445 .unused, 4446 .unused, 4447 }, 4448 .dst_temps = .{.mem}, 4449 .clobbers = .{ .eflags = true }, 4450 .each = .{ .once = &.{ 4451 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4452 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 4453 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4454 .{ ._, ._l, .cmov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4455 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 4456 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 4457 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4458 } }, 4459 }, .{ 4460 .src_constraints = .{ 4461 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 4462 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 4463 }, 4464 .patterns = &.{ 4465 .{ .src = .{ .to_mem, .to_mem } }, 4466 }, 4467 .extra_temps = .{ 4468 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4469 .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, 4470 .unused, 4471 .unused, 4472 .unused, 4473 .unused, 4474 .unused, 4475 .unused, 4476 .unused, 4477 }, 4478 .dst_temps = .{.mem}, 4479 .clobbers = .{ .eflags = true }, 4480 .each = .{ .once = &.{ 4481 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4482 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 4483 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4484 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 4485 .{ ._, ._, .mov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4486 .{ .@"1:", ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 4487 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 4488 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4489 } }, 4490 }, .{ 4491 .required_features = .{ .avx, null, null, null }, 4492 .src_constraints = .{ 4493 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4494 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4495 }, 4496 .patterns = &.{ 4497 .{ .src = .{ .to_sse, .mem } }, 4498 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4499 .{ .src = .{ .to_sse, .to_sse } }, 4500 }, 4501 .dst_temps = .{.{ .rc = .sse }}, 4502 .each = .{ .once = &.{ 4503 .{ ._, .vp_d, .maxu, .dst0x, .src0x, .src1x, ._ }, 4504 } }, 4505 }, .{ 4506 .required_features = .{ .sse4_1, null, null, null }, 4507 .src_constraints = .{ 4508 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4509 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4510 }, 4511 .patterns = &.{ 4512 .{ .src = .{ .to_mut_sse, .mem } }, 4513 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4514 .{ .src = .{ .to_mut_sse, .to_sse } }, 4515 }, 4516 .dst_temps = .{.{ .ref = .src0 }}, 4517 .each = .{ .once = &.{ 4518 .{ ._, .p_d, .maxu, .dst0x, .src1x, ._, ._ }, 4519 } }, 4520 }, .{ 4521 .required_features = .{ .sse2, null, null, null }, 4522 .src_constraints = .{ 4523 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4524 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4525 }, 4526 .patterns = &.{ 4527 .{ .src = .{ .to_mut_sse, .mem } }, 4528 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4529 .{ .src = .{ .to_mut_sse, .to_sse } }, 4530 }, 4531 .extra_temps = .{ 4532 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 4533 .{ .type = .vector_4_u32, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 4534 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4535 .unused, 4536 .unused, 4537 .unused, 4538 .unused, 4539 .unused, 4540 .unused, 4541 }, 4542 .dst_temps = .{.{ .rc = .sse }}, 4543 .each = .{ .once = &.{ 4544 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 4545 .{ ._, ._dqa, .mov, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 4546 .{ ._, ._dqa, .mov, .tmp2x, .dst0x, ._, ._ }, 4547 .{ ._, .p_, .xor, .dst0x, .src0x, ._, ._ }, 4548 .{ ._, .p_, .xor, .tmp2x, .src1x, ._, ._ }, 4549 .{ ._, .p_d, .cmpgt, .dst0x, .tmp2x, ._, ._ }, 4550 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 4551 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 4552 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 4553 } }, 4554 }, .{ 4555 .required_features = .{ .avx2, null, null, null }, 4556 .src_constraints = .{ 4557 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 4558 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 4559 }, 4560 .patterns = &.{ 4561 .{ .src = .{ .to_sse, .mem } }, 4562 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4563 .{ .src = .{ .to_sse, .to_sse } }, 4564 }, 4565 .dst_temps = .{.{ .rc = .sse }}, 4566 .each = .{ .once = &.{ 4567 .{ ._, .vp_d, .maxu, .dst0y, .src0y, .src1y, ._ }, 4568 } }, 4569 }, .{ 4570 .required_features = .{ .avx2, null, null, null }, 4571 .src_constraints = .{ 4572 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 4573 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 4574 }, 4575 .patterns = &.{ 4576 .{ .src = .{ .to_mem, .to_mem } }, 4577 }, 4578 .extra_temps = .{ 4579 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4580 .{ .type = .vector_8_u32, .kind = .{ .rc = .sse } }, 4581 .unused, 4582 .unused, 4583 .unused, 4584 .unused, 4585 .unused, 4586 .unused, 4587 .unused, 4588 }, 4589 .dst_temps = .{.mem}, 4590 .clobbers = .{ .eflags = true }, 4591 .each = .{ .once = &.{ 4592 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4593 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 4594 .{ ._, .vp_d, .maxu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 4595 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 4596 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 4597 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4598 } }, 4599 }, .{ 4600 .required_features = .{ .avx, null, null, null }, 4601 .src_constraints = .{ 4602 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4603 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4604 }, 4605 .patterns = &.{ 4606 .{ .src = .{ .to_mem, .to_mem } }, 4607 }, 4608 .extra_temps = .{ 4609 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4610 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4611 .unused, 4612 .unused, 4613 .unused, 4614 .unused, 4615 .unused, 4616 .unused, 4617 .unused, 4618 }, 4619 .dst_temps = .{.mem}, 4620 .clobbers = .{ .eflags = true }, 4621 .each = .{ .once = &.{ 4622 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4623 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4624 .{ ._, .vp_d, .maxu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 4625 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4626 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4627 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4628 } }, 4629 }, .{ 4630 .required_features = .{ .sse4_1, null, null, null }, 4631 .src_constraints = .{ 4632 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4633 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4634 }, 4635 .patterns = &.{ 4636 .{ .src = .{ .to_mem, .to_mem } }, 4637 }, 4638 .extra_temps = .{ 4639 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4640 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4641 .unused, 4642 .unused, 4643 .unused, 4644 .unused, 4645 .unused, 4646 .unused, 4647 .unused, 4648 }, 4649 .dst_temps = .{.mem}, 4650 .clobbers = .{ .eflags = true }, 4651 .each = .{ .once = &.{ 4652 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4653 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4654 .{ ._, .p_d, .maxu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4655 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4656 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4657 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4658 } }, 4659 }, .{ 4660 .required_features = .{ .sse2, null, null, null }, 4661 .src_constraints = .{ 4662 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4663 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 4664 }, 4665 .patterns = &.{ 4666 .{ .src = .{ .to_mem, .to_mem } }, 4667 }, 4668 .extra_temps = .{ 4669 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4670 .{ .type = .vector_4_u32, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 4671 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4672 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4673 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4674 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4675 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 4676 .unused, 4677 .unused, 4678 }, 4679 .dst_temps = .{.mem}, 4680 .clobbers = .{ .eflags = true }, 4681 .each = .{ .once = &.{ 4682 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 4683 .{ ._, ._dqa, .mov, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 4684 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4685 .{ .@"0:", ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4686 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4687 .{ ._, ._dqa, .mov, .tmp5x, .tmp3x, ._, ._ }, 4688 .{ ._, ._dqa, .mov, .tmp6x, .tmp4x, ._, ._ }, 4689 .{ ._, .p_, .xor, .tmp5x, .tmp2x, ._, ._ }, 4690 .{ ._, .p_, .xor, .tmp6x, .tmp2x, ._, ._ }, 4691 .{ ._, .p_d, .cmpgt, .tmp5x, .tmp6x, ._, ._ }, 4692 .{ ._, .p_, .@"and", .tmp3x, .tmp5x, ._, ._ }, 4693 .{ ._, .p_, .andn, .tmp5x, .tmp4x, ._, ._ }, 4694 .{ ._, .p_, .@"or", .tmp3x, .tmp5x, ._, ._ }, 4695 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 4696 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4697 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4698 } }, 4699 }, .{ 4700 .required_features = .{ .cmov, null, null, null }, 4701 .src_constraints = .{ 4702 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 4703 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 4704 }, 4705 .patterns = &.{ 4706 .{ .src = .{ .to_mem, .to_mem } }, 4707 }, 4708 .extra_temps = .{ 4709 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4710 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 4711 .unused, 4712 .unused, 4713 .unused, 4714 .unused, 4715 .unused, 4716 .unused, 4717 .unused, 4718 }, 4719 .dst_temps = .{.mem}, 4720 .clobbers = .{ .eflags = true }, 4721 .each = .{ .once = &.{ 4722 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4723 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 4724 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4725 .{ ._, ._b, .cmov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4726 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 4727 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 4728 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4729 } }, 4730 }, .{ 4731 .src_constraints = .{ 4732 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 4733 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 4734 }, 4735 .patterns = &.{ 4736 .{ .src = .{ .to_mem, .to_mem } }, 4737 }, 4738 .extra_temps = .{ 4739 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4740 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 4741 .unused, 4742 .unused, 4743 .unused, 4744 .unused, 4745 .unused, 4746 .unused, 4747 .unused, 4748 }, 4749 .dst_temps = .{.mem}, 4750 .clobbers = .{ .eflags = true }, 4751 .each = .{ .once = &.{ 4752 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4753 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 4754 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4755 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 4756 .{ ._, ._, .mov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 4757 .{ .@"1:", ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 4758 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 4759 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4760 } }, 4761 }, .{ 4762 .required_features = .{ .avx, null, null, null }, 4763 .src_constraints = .{ 4764 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4765 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4766 }, 4767 .patterns = &.{ 4768 .{ .src = .{ .to_sse, .to_sse } }, 4769 }, 4770 .dst_temps = .{.{ .rc = .sse }}, 4771 .each = .{ .once = &.{ 4772 .{ ._, .vp_q, .cmpgt, .dst0x, .src1x, .src0x, ._ }, 4773 .{ ._, .vp_b, .blendv, .dst0x, .src0x, .src1x, .dst0x }, 4774 } }, 4775 }, .{ 4776 .required_features = .{ .sse4_2, null, null, null }, 4777 .src_constraints = .{ 4778 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4779 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4780 }, 4781 .patterns = &.{ 4782 .{ .src = .{ .to_mut_sse, .mem } }, 4783 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 4784 .{ .src = .{ .to_mut_sse, .to_sse } }, 4785 }, 4786 .extra_temps = .{ 4787 .{ .type = .vector_2_i64, .kind = .{ .reg = .xmm0 } }, 4788 .unused, 4789 .unused, 4790 .unused, 4791 .unused, 4792 .unused, 4793 .unused, 4794 .unused, 4795 .unused, 4796 }, 4797 .dst_temps = .{.{ .ref = .src0 }}, 4798 .each = .{ .once = &.{ 4799 .{ ._, ._dqa, .mov, .tmp0x, .src1x, ._, ._ }, 4800 .{ ._, .p_q, .cmpgt, .tmp0x, .src0x, ._, ._ }, 4801 .{ ._, .p_b, .blendv, .dst0x, .src1x, .tmp0x, ._ }, 4802 } }, 4803 }, .{ 4804 .required_features = .{ .avx2, null, null, null }, 4805 .src_constraints = .{ 4806 .{ .scalar_signed_int = .{ .of = .yword, .is = .qword } }, 4807 .{ .scalar_signed_int = .{ .of = .yword, .is = .qword } }, 4808 }, 4809 .patterns = &.{ 4810 .{ .src = .{ .to_sse, .to_sse } }, 4811 }, 4812 .dst_temps = .{.{ .rc = .sse }}, 4813 .each = .{ .once = &.{ 4814 .{ ._, .vp_q, .cmpgt, .dst0y, .src1y, .src0y, ._ }, 4815 .{ ._, .vp_b, .blendv, .dst0y, .src0y, .src1y, .dst0y }, 4816 } }, 4817 }, .{ 4818 .required_features = .{ .avx2, null, null, null }, 4819 .src_constraints = .{ 4820 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .qword } }, 4821 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .qword } }, 4822 }, 4823 .patterns = &.{ 4824 .{ .src = .{ .to_mem, .to_mem } }, 4825 }, 4826 .extra_temps = .{ 4827 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4828 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 4829 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 4830 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 4831 .unused, 4832 .unused, 4833 .unused, 4834 .unused, 4835 .unused, 4836 }, 4837 .dst_temps = .{.mem}, 4838 .clobbers = .{ .eflags = true }, 4839 .each = .{ .once = &.{ 4840 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4841 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 4842 .{ ._, .v_dqa, .mov, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 4843 .{ ._, .vp_q, .cmpgt, .tmp3y, .tmp2y, .tmp1y, ._ }, 4844 .{ ._, .vp_b, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 4845 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 4846 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 4847 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4848 } }, 4849 }, .{ 4850 .required_features = .{ .avx, null, null, null }, 4851 .src_constraints = .{ 4852 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4853 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4854 }, 4855 .patterns = &.{ 4856 .{ .src = .{ .to_mem, .to_mem } }, 4857 }, 4858 .extra_temps = .{ 4859 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4860 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 4861 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 4862 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 4863 .unused, 4864 .unused, 4865 .unused, 4866 .unused, 4867 .unused, 4868 }, 4869 .dst_temps = .{.mem}, 4870 .clobbers = .{ .eflags = true }, 4871 .each = .{ .once = &.{ 4872 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4873 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4874 .{ ._, .v_dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4875 .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp2x, .tmp1x, ._ }, 4876 .{ ._, .vp_b, .blendv, .tmp1x, .tmp1x, .tmp2x, .tmp3x }, 4877 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4878 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4879 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4880 } }, 4881 }, .{ 4882 .required_features = .{ .sse4_2, null, null, null }, 4883 .src_constraints = .{ 4884 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4885 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 4886 }, 4887 .patterns = &.{ 4888 .{ .src = .{ .to_mem, .to_mem } }, 4889 }, 4890 .extra_temps = .{ 4891 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4892 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 4893 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 4894 .{ .type = .vector_2_i64, .kind = .{ .reg = .xmm0 } }, 4895 .unused, 4896 .unused, 4897 .unused, 4898 .unused, 4899 .unused, 4900 }, 4901 .dst_temps = .{.mem}, 4902 .clobbers = .{ .eflags = true }, 4903 .each = .{ .once = &.{ 4904 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4905 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 4906 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 4907 .{ ._, ._dqa, .mov, .tmp3x, .tmp2x, ._, ._ }, 4908 .{ ._, .p_q, .cmpgt, .tmp3x, .tmp1x, ._, ._ }, 4909 .{ ._, .p_b, .blendv, .tmp1x, .tmp2x, .tmp3x, ._ }, 4910 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 4911 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 4912 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4913 } }, 4914 }, .{ 4915 .required_features = .{ .@"64bit", .cmov, null, null }, 4916 .src_constraints = .{ 4917 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 4918 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 4919 }, 4920 .patterns = &.{ 4921 .{ .src = .{ .to_mem, .to_mem } }, 4922 }, 4923 .extra_temps = .{ 4924 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4925 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 4926 .unused, 4927 .unused, 4928 .unused, 4929 .unused, 4930 .unused, 4931 .unused, 4932 .unused, 4933 }, 4934 .dst_temps = .{.mem}, 4935 .clobbers = .{ .eflags = true }, 4936 .each = .{ .once = &.{ 4937 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4938 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 4939 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 4940 .{ ._, ._l, .cmov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 4941 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 4942 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 4943 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4944 } }, 4945 }, .{ 4946 .required_features = .{ .@"64bit", null, null, null }, 4947 .src_constraints = .{ 4948 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 4949 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 4950 }, 4951 .patterns = &.{ 4952 .{ .src = .{ .to_mem, .to_mem } }, 4953 }, 4954 .extra_temps = .{ 4955 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 4956 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 4957 .unused, 4958 .unused, 4959 .unused, 4960 .unused, 4961 .unused, 4962 .unused, 4963 .unused, 4964 }, 4965 .dst_temps = .{.mem}, 4966 .clobbers = .{ .eflags = true }, 4967 .each = .{ .once = &.{ 4968 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 4969 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 4970 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 4971 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 4972 .{ ._, ._, .mov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 4973 .{ .@"1:", ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 4974 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 4975 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 4976 } }, 4977 }, .{ 4978 .required_features = .{ .avx, null, null, null }, 4979 .src_constraints = .{ 4980 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 4981 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 4982 }, 4983 .patterns = &.{ 4984 .{ .src = .{ .to_sse, .mem } }, 4985 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 4986 .{ .src = .{ .to_sse, .to_sse } }, 4987 }, 4988 .extra_temps = .{ 4989 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 4990 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 4991 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 4992 .unused, 4993 .unused, 4994 .unused, 4995 .unused, 4996 .unused, 4997 .unused, 4998 }, 4999 .dst_temps = .{.{ .rc = .sse }}, 5000 .each = .{ .once = &.{ 5001 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5002 .{ ._, .v_, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 5003 .{ ._, .vp_, .xor, .dst0x, .tmp2x, .src0x, ._ }, 5004 .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .src1x, ._ }, 5005 .{ ._, .vp_q, .cmpgt, .dst0x, .tmp2x, .dst0x, ._ }, 5006 .{ ._, .vp_b, .blendv, .dst0x, .src0x, .src1x, .dst0x }, 5007 } }, 5008 }, .{ 5009 .required_features = .{ .sse4_2, null, null, null }, 5010 .src_constraints = .{ 5011 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5012 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5013 }, 5014 .patterns = &.{ 5015 .{ .src = .{ .to_mut_sse, .mem } }, 5016 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 5017 .{ .src = .{ .to_mut_sse, .to_sse } }, 5018 }, 5019 .extra_temps = .{ 5020 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 5021 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 5022 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5023 .{ .type = .vector_2_u64, .kind = .{ .reg = .xmm0 } }, 5024 .unused, 5025 .unused, 5026 .unused, 5027 .unused, 5028 .unused, 5029 }, 5030 .dst_temps = .{.{ .ref = .src0 }}, 5031 .each = .{ .once = &.{ 5032 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5033 .{ ._, ._, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 5034 .{ ._, ._dqa, .mov, .tmp3x, .tmp2x, ._, ._ }, 5035 .{ ._, .p_, .xor, .tmp2x, .src0x, ._, ._ }, 5036 .{ ._, .p_, .xor, .tmp3x, .src1x, ._, ._ }, 5037 .{ ._, .p_q, .cmpgt, .tmp3x, .tmp2x, ._, ._ }, 5038 .{ ._, .p_b, .blendv, .dst0x, .src1x, .tmp3x, ._ }, 5039 } }, 5040 }, .{ 5041 .required_features = .{ .avx2, null, null, null }, 5042 .src_constraints = .{ 5043 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 5044 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 5045 }, 5046 .patterns = &.{ 5047 .{ .src = .{ .to_sse, .mem } }, 5048 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 5049 .{ .src = .{ .to_sse, .to_sse } }, 5050 }, 5051 .extra_temps = .{ 5052 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 5053 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 5054 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5055 .unused, 5056 .unused, 5057 .unused, 5058 .unused, 5059 .unused, 5060 .unused, 5061 }, 5062 .dst_temps = .{.{ .rc = .sse }}, 5063 .each = .{ .once = &.{ 5064 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5065 .{ ._, .vp_q, .broadcast, .tmp2y, .lea(.qword, .tmp0), ._, ._ }, 5066 .{ ._, .vp_, .xor, .dst0y, .tmp2y, .src0y, ._ }, 5067 .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .src1y, ._ }, 5068 .{ ._, .vp_q, .cmpgt, .dst0y, .tmp2y, .dst0y, ._ }, 5069 .{ ._, .vp_b, .blendv, .dst0y, .src0y, .src1y, .dst0y }, 5070 } }, 5071 }, .{ 5072 .required_features = .{ .avx2, null, null, null }, 5073 .src_constraints = .{ 5074 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 5075 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 5076 }, 5077 .patterns = &.{ 5078 .{ .src = .{ .to_mem, .to_mem } }, 5079 }, 5080 .extra_temps = .{ 5081 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5082 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 5083 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5084 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5085 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5086 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5087 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 5088 .unused, 5089 .unused, 5090 }, 5091 .dst_temps = .{.mem}, 5092 .clobbers = .{ .eflags = true }, 5093 .each = .{ .once = &.{ 5094 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5095 .{ ._, .vp_q, .broadcast, .tmp2y, .lea(.qword, .tmp0), ._, ._ }, 5096 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5097 .{ .@"0:", .v_dqa, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 5098 .{ ._, .v_dqa, .mov, .tmp4y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 5099 .{ ._, .vp_, .xor, .tmp5y, .tmp3y, .tmp2y, ._ }, 5100 .{ ._, .vp_, .xor, .tmp6y, .tmp4y, .tmp2y, ._ }, 5101 .{ ._, .vp_q, .cmpgt, .tmp5y, .tmp6y, .tmp5y, ._ }, 5102 .{ ._, .vp_b, .blendv, .tmp3y, .tmp3y, .tmp4y, .tmp5y }, 5103 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 5104 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 5105 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5106 } }, 5107 }, .{ 5108 .required_features = .{ .avx, null, null, null }, 5109 .src_constraints = .{ 5110 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5111 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5112 }, 5113 .patterns = &.{ 5114 .{ .src = .{ .to_mem, .to_mem } }, 5115 }, 5116 .extra_temps = .{ 5117 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5118 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 5119 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5120 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5121 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5122 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5123 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5124 .unused, 5125 .unused, 5126 }, 5127 .dst_temps = .{.mem}, 5128 .clobbers = .{ .eflags = true }, 5129 .each = .{ .once = &.{ 5130 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5131 .{ ._, .v_, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 5132 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5133 .{ .@"0:", .v_dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 5134 .{ ._, .v_dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 5135 .{ ._, .vp_, .xor, .tmp5x, .tmp3x, .tmp2x, ._ }, 5136 .{ ._, .vp_, .xor, .tmp6x, .tmp4x, .tmp2x, ._ }, 5137 .{ ._, .vp_q, .cmpgt, .tmp5x, .tmp6x, .tmp5x, ._ }, 5138 .{ ._, .vp_b, .blendv, .tmp3x, .tmp3x, .tmp4x, .tmp5x }, 5139 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 5140 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 5141 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5142 } }, 5143 }, .{ 5144 .required_features = .{ .sse4_2, null, null, null }, 5145 .src_constraints = .{ 5146 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5147 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 5148 }, 5149 .patterns = &.{ 5150 .{ .src = .{ .to_mem, .to_mem } }, 5151 }, 5152 .extra_temps = .{ 5153 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5154 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 5155 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5156 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5157 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5158 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 5159 .{ .type = .vector_2_u64, .kind = .{ .reg = .xmm0 } }, 5160 .unused, 5161 .unused, 5162 }, 5163 .dst_temps = .{.mem}, 5164 .clobbers = .{ .eflags = true }, 5165 .each = .{ .once = &.{ 5166 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 5167 .{ ._, ._, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 5168 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5169 .{ .@"0:", ._dqa, .mov, .tmp5x, .tmp2x, ._, ._ }, 5170 .{ ._, ._dqa, .mov, .tmp6x, .tmp2x, ._, ._ }, 5171 .{ ._, ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 5172 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 5173 .{ ._, .p_, .xor, .tmp5x, .tmp3x, ._, ._ }, 5174 .{ ._, .p_, .xor, .tmp6x, .tmp4x, ._, ._ }, 5175 .{ ._, .p_q, .cmpgt, .tmp6x, .tmp5x, ._, ._ }, 5176 .{ ._, .p_b, .blendv, .tmp3x, .tmp4x, .tmp6x, ._ }, 5177 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 5178 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 5179 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5180 } }, 5181 }, .{ 5182 .required_features = .{ .@"64bit", .cmov, null, null }, 5183 .src_constraints = .{ 5184 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 5185 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 5186 }, 5187 .patterns = &.{ 5188 .{ .src = .{ .to_mem, .to_mem } }, 5189 }, 5190 .extra_temps = .{ 5191 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5192 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 5193 .unused, 5194 .unused, 5195 .unused, 5196 .unused, 5197 .unused, 5198 .unused, 5199 .unused, 5200 }, 5201 .dst_temps = .{.mem}, 5202 .clobbers = .{ .eflags = true }, 5203 .each = .{ .once = &.{ 5204 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5205 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5206 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5207 .{ ._, ._b, .cmov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5208 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 5209 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 5210 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5211 } }, 5212 }, .{ 5213 .required_features = .{ .@"64bit", null, null, null }, 5214 .src_constraints = .{ 5215 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 5216 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 5217 }, 5218 .patterns = &.{ 5219 .{ .src = .{ .to_mem, .to_mem } }, 5220 }, 5221 .extra_temps = .{ 5222 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5223 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 5224 .unused, 5225 .unused, 5226 .unused, 5227 .unused, 5228 .unused, 5229 .unused, 5230 .unused, 5231 }, 5232 .dst_temps = .{.mem}, 5233 .clobbers = .{ .eflags = true }, 5234 .each = .{ .once = &.{ 5235 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5236 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5237 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5238 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 5239 .{ ._, ._, .mov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5240 .{ .@"1:", ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 5241 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 5242 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5243 } }, 5244 }, .{ 5245 .required_features = .{ .@"64bit", .cmov, null, null }, 5246 .src_constraints = .{ .any_scalar_signed_int, .any_scalar_signed_int }, 5247 .patterns = &.{ 5248 .{ .src = .{ .to_mem, .to_mem } }, 5249 }, 5250 .extra_temps = .{ 5251 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5252 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 5253 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 5254 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 5255 .unused, 5256 .unused, 5257 .unused, 5258 .unused, 5259 .unused, 5260 }, 5261 .dst_temps = .{.mem}, 5262 .clobbers = .{ .eflags = true }, 5263 .each = .{ .once = &.{ 5264 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5265 .{ .@"0:", ._, .mov, .tmp1d, .sia(-1, .none, .add_src0_elem_size_div_8), ._, ._ }, 5266 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 5267 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5268 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5269 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 5270 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 5271 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 5272 .{ ._, ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5273 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5274 .{ ._, ._, .lea, .tmp1p, .memiad(.src0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5275 .{ ._, ._, .lea, .tmp2p, .memiad(.src1, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5276 .{ ._, ._l, .cmov, .tmp1p, .tmp2p, ._, ._ }, 5277 .{ ._, ._, .lea, .tmp2p, .memiad(.dst0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5278 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5279 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 5280 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 5281 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5282 } }, 5283 }, .{ 5284 .required_features = .{ .@"64bit", null, null, null }, 5285 .src_constraints = .{ .any_scalar_signed_int, .any_scalar_signed_int }, 5286 .patterns = &.{ 5287 .{ .src = .{ .to_mem, .to_mem } }, 5288 }, 5289 .extra_temps = .{ 5290 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5291 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 5292 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 5293 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 5294 .unused, 5295 .unused, 5296 .unused, 5297 .unused, 5298 .unused, 5299 }, 5300 .dst_temps = .{.mem}, 5301 .clobbers = .{ .eflags = true }, 5302 .each = .{ .once = &.{ 5303 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5304 .{ .@"0:", ._, .mov, .tmp1d, .sia(-1, .none, .add_src0_elem_size_div_8), ._, ._ }, 5305 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 5306 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5307 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5308 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 5309 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 5310 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 5311 .{ ._, ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5312 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5313 .{ ._, ._, .lea, .tmp1p, .memiad(.src0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5314 .{ ._, ._nl, .j, .@"1f", ._, ._, ._ }, 5315 .{ ._, ._, .lea, .tmp1p, .memiad(.src1, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5316 .{ .@"1:", ._, .lea, .tmp2p, .memiad(.dst0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 5317 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5318 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 5319 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 5320 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5321 } }, 5322 }, .{ 5323 .required_features = .{ .@"64bit", .cmov, null, null }, 5324 .src_constraints = .{ .any_scalar_unsigned_int, .any_scalar_unsigned_int }, 5325 .patterns = &.{ 5326 .{ .src = .{ .to_mem, .to_mem } }, 5327 }, 5328 .extra_temps = .{ 5329 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5330 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 5331 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 5332 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 5333 .unused, 5334 .unused, 5335 .unused, 5336 .unused, 5337 .unused, 5338 }, 5339 .dst_temps = .{.mem}, 5340 .clobbers = .{ .eflags = true }, 5341 .each = .{ .once = &.{ 5342 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5343 .{ .@"0:", ._, .mov, .tmp1d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5344 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 5345 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5346 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5347 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 5348 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 5349 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 5350 .{ ._, ._, .lea, .tmp1p, .memia(.src0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5351 .{ ._, ._, .lea, .tmp2p, .memia(.src1, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5352 .{ ._, ._b, .cmov, .tmp1p, .tmp2p, ._, ._ }, 5353 .{ ._, ._, .lea, .tmp2p, .memia(.dst0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5354 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5355 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 5356 .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, 5357 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 5358 } }, 5359 }, .{ 5360 .required_features = .{ .@"64bit", null, null, null }, 5361 .src_constraints = .{ .any_scalar_unsigned_int, .any_scalar_unsigned_int }, 5362 .patterns = &.{ 5363 .{ .src = .{ .to_mem, .to_mem } }, 5364 }, 5365 .extra_temps = .{ 5366 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5367 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 5368 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 5369 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 5370 .unused, 5371 .unused, 5372 .unused, 5373 .unused, 5374 .unused, 5375 }, 5376 .dst_temps = .{.mem}, 5377 .clobbers = .{ .eflags = true }, 5378 .each = .{ .once = &.{ 5379 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5380 .{ .@"0:", ._, .mov, .tmp1d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5381 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 5382 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 5383 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 5384 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 5385 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 5386 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 5387 .{ ._, ._, .lea, .tmp1p, .memia(.src0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5388 .{ ._, ._nb, .j, .@"1f", ._, ._, ._ }, 5389 .{ ._, ._, .lea, .tmp1p, .memia(.src1, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5390 .{ .@"1:", ._, .lea, .tmp2p, .memia(.dst0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 5391 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 5392 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 5393 .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, 5394 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 5395 } }, 5396 }, .{ 5397 .required_features = .{ .f16c, null, null, null }, 5398 .src_constraints = .{ 5399 .{ .scalar_float = .{ .of = .word, .is = .word } }, 5400 .{ .scalar_float = .{ .of = .word, .is = .word } }, 5401 }, 5402 .patterns = &.{ 5403 .{ .src = .{ .to_sse, .to_sse } }, 5404 }, 5405 .extra_temps = .{ 5406 .{ .type = .f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 5407 .{ .type = .f16, .kind = .{ .rc = .sse } }, 5408 .unused, 5409 .unused, 5410 .unused, 5411 .unused, 5412 .unused, 5413 .unused, 5414 .unused, 5415 }, 5416 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5417 .each = .{ .once = &.{ 5418 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 5419 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 5420 .{ ._, .v_ss, .cmp, .tmp1x, .dst0x, .dst0x, .vp(.unord) }, 5421 .{ ._, .v_ss, .max, .dst0x, .tmp0x, .dst0x, ._ }, 5422 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .tmp0x, .tmp1x }, 5423 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0x, .rm(.{}), ._ }, 5424 } }, 5425 }, .{ 5426 .required_features = .{ .sse, null, null, null }, 5427 .src_constraints = .{ 5428 .{ .scalar_float = .{ .of = .word, .is = .word } }, 5429 .{ .scalar_float = .{ .of = .word, .is = .word } }, 5430 }, 5431 .patterns = &.{ 5432 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 5433 }, 5434 .call_frame = .{ .alignment = .@"16" }, 5435 .extra_temps = .{ 5436 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, 5437 .unused, 5438 .unused, 5439 .unused, 5440 .unused, 5441 .unused, 5442 .unused, 5443 .unused, 5444 .unused, 5445 }, 5446 .dst_temps = .{.{ .ref = .src0 }}, 5447 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 5448 .each = .{ .once = &.{ 5449 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 5450 } }, 5451 }, .{ 5452 .required_features = .{ .f16c, null, null, null }, 5453 .src_constraints = .{ 5454 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 5455 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 5456 }, 5457 .patterns = &.{ 5458 .{ .src = .{ .mem, .mem } }, 5459 .{ .src = .{ .to_sse, .mem } }, 5460 .{ .src = .{ .mem, .to_sse } }, 5461 .{ .src = .{ .to_sse, .to_sse } }, 5462 }, 5463 .extra_temps = .{ 5464 .{ .type = .vector_4_f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 5465 .{ .type = .vector_4_f16, .kind = .{ .rc = .sse } }, 5466 .unused, 5467 .unused, 5468 .unused, 5469 .unused, 5470 .unused, 5471 .unused, 5472 .unused, 5473 }, 5474 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5475 .each = .{ .once = &.{ 5476 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 5477 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 5478 .{ ._, .v_ps, .cmp, .tmp1x, .dst0x, .dst0x, .vp(.unord) }, 5479 .{ ._, .v_ps, .max, .dst0x, .tmp0x, .dst0x, ._ }, 5480 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .tmp0x, .tmp1x }, 5481 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0x, .rm(.{}), ._ }, 5482 } }, 5483 }, .{ 5484 .required_features = .{ .f16c, null, null, null }, 5485 .src_constraints = .{ 5486 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 5487 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 5488 }, 5489 .patterns = &.{ 5490 .{ .src = .{ .mem, .mem } }, 5491 .{ .src = .{ .to_sse, .mem } }, 5492 .{ .src = .{ .mem, .to_sse } }, 5493 .{ .src = .{ .to_sse, .to_sse } }, 5494 }, 5495 .extra_temps = .{ 5496 .{ .type = .vector_8_f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 5497 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 5498 .unused, 5499 .unused, 5500 .unused, 5501 .unused, 5502 .unused, 5503 .unused, 5504 .unused, 5505 }, 5506 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5507 .each = .{ .once = &.{ 5508 .{ ._, .v_ps, .cvtph2, .dst0y, .src0x, ._, ._ }, 5509 .{ ._, .v_ps, .cvtph2, .tmp0y, .src1x, ._, ._ }, 5510 .{ ._, .v_ps, .cmp, .tmp1y, .dst0y, .dst0y, .vp(.unord) }, 5511 .{ ._, .v_ps, .max, .dst0y, .tmp0y, .dst0y, ._ }, 5512 .{ ._, .v_ps, .blendv, .dst0y, .dst0y, .tmp0y, .tmp1y }, 5513 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0y, .rm(.{}), ._ }, 5514 } }, 5515 }, .{ 5516 .required_features = .{ .f16c, null, null, null }, 5517 .src_constraints = .{ 5518 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 5519 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 5520 }, 5521 .patterns = &.{ 5522 .{ .src = .{ .to_mem, .to_mem } }, 5523 }, 5524 .extra_temps = .{ 5525 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5526 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 5527 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 5528 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 5529 .unused, 5530 .unused, 5531 .unused, 5532 .unused, 5533 .unused, 5534 }, 5535 .dst_temps = .{.mem}, 5536 .clobbers = .{ .eflags = true }, 5537 .each = .{ .once = &.{ 5538 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 5539 .{ .@"0:", .v_ps, .cvtph2, .tmp1y, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 5540 .{ ._, .v_ps, .cvtph2, .tmp2y, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 5541 .{ ._, .v_ps, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 5542 .{ ._, .v_ps, .max, .tmp1y, .tmp2y, .tmp1y, ._ }, 5543 .{ ._, .v_ps, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 5544 .{ ._, .v_, .cvtps2ph, .memia(.dst0x, .tmp0, .add_size), .tmp1y, .rm(.{}), ._ }, 5545 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 5546 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5547 } }, 5548 }, .{ 5549 .required_features = .{ .avx, null, null, null }, 5550 .src_constraints = .{ 5551 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5552 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5553 }, 5554 .patterns = &.{ 5555 .{ .src = .{ .to_mem, .to_mem } }, 5556 }, 5557 .call_frame = .{ .alignment = .@"16" }, 5558 .extra_temps = .{ 5559 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5560 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 5561 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 5562 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, 5563 .unused, 5564 .unused, 5565 .unused, 5566 .unused, 5567 .unused, 5568 }, 5569 .dst_temps = .{.mem}, 5570 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 5571 .each = .{ .once = &.{ 5572 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5573 .{ .@"0:", .vp_, .xor, .tmp2x, .tmp2x, .tmp2x, ._ }, 5574 .{ ._, .vp_w, .insr, .tmp1x, .tmp2x, .memia(.src0w, .tmp0, .add_size), .ui(0) }, 5575 .{ ._, .vp_w, .insr, .tmp2x, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0) }, 5576 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 5577 .{ ._, .vp_w, .extr, .memia(.dst0w, .tmp0, .add_size), .tmp1x, .ui(0), ._ }, 5578 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 5579 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5580 } }, 5581 }, .{ 5582 .required_features = .{ .sse4_1, null, null, null }, 5583 .src_constraints = .{ 5584 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5585 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5586 }, 5587 .patterns = &.{ 5588 .{ .src = .{ .to_mem, .to_mem } }, 5589 }, 5590 .call_frame = .{ .alignment = .@"16" }, 5591 .extra_temps = .{ 5592 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5593 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 5594 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 5595 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, 5596 .unused, 5597 .unused, 5598 .unused, 5599 .unused, 5600 .unused, 5601 }, 5602 .dst_temps = .{.mem}, 5603 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 5604 .each = .{ .once = &.{ 5605 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5606 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 5607 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 5608 .{ ._, .p_w, .insr, .tmp1x, .memia(.src0w, .tmp0, .add_size), .ui(0), ._ }, 5609 .{ ._, .p_w, .insr, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0), ._ }, 5610 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 5611 .{ ._, .p_w, .extr, .memia(.dst0w, .tmp0, .add_size), .tmp1x, .ui(0), ._ }, 5612 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 5613 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5614 } }, 5615 }, .{ 5616 .required_features = .{ .sse2, null, null, null }, 5617 .src_constraints = .{ 5618 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5619 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5620 }, 5621 .patterns = &.{ 5622 .{ .src = .{ .to_mem, .to_mem } }, 5623 }, 5624 .call_frame = .{ .alignment = .@"16" }, 5625 .extra_temps = .{ 5626 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5627 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 5628 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 5629 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, 5630 .{ .type = .f16, .kind = .{ .reg = .ax } }, 5631 .unused, 5632 .unused, 5633 .unused, 5634 .unused, 5635 }, 5636 .dst_temps = .{.mem}, 5637 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 5638 .each = .{ .once = &.{ 5639 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5640 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 5641 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 5642 .{ ._, .p_w, .insr, .tmp1x, .memia(.src0w, .tmp0, .add_size), .ui(0), ._ }, 5643 .{ ._, .p_w, .insr, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0), ._ }, 5644 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 5645 .{ ._, .p_w, .extr, .tmp4d, .tmp1x, .ui(0), ._ }, 5646 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp4w, ._, ._ }, 5647 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 5648 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5649 } }, 5650 }, .{ 5651 .required_features = .{ .sse, null, null, null }, 5652 .src_constraints = .{ 5653 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5654 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 5655 }, 5656 .patterns = &.{ 5657 .{ .src = .{ .to_mem, .to_mem } }, 5658 }, 5659 .call_frame = .{ .alignment = .@"16" }, 5660 .extra_temps = .{ 5661 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5662 .{ .type = .f16, .kind = .{ .reg = .eax } }, 5663 .{ .type = .f32, .kind = .mem }, 5664 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 5665 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 5666 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fmaxh" } } }, 5667 .unused, 5668 .unused, 5669 .unused, 5670 }, 5671 .dst_temps = .{.mem}, 5672 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 5673 .each = .{ .once = &.{ 5674 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5675 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 5676 .{ ._, ._, .mov, .mem(.tmp2d), .tmp1d, ._, ._ }, 5677 .{ ._, ._ss, .mov, .tmp3x, .mem(.tmp2d), ._, ._ }, 5678 .{ ._, ._, .movzx, .tmp1d, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 5679 .{ ._, ._, .mov, .mem(.tmp2d), .tmp1d, ._, ._ }, 5680 .{ ._, ._ss, .mov, .tmp4x, .mem(.tmp2d), ._, ._ }, 5681 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 5682 .{ ._, ._ss, .mov, .mem(.tmp2d), .tmp3x, ._, ._ }, 5683 .{ ._, ._, .mov, .tmp1d, .mem(.tmp2d), ._, ._ }, 5684 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 5685 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 5686 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5687 } }, 5688 }, .{ 5689 .required_features = .{ .avx, null, null, null }, 5690 .src_constraints = .{ 5691 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5692 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5693 }, 5694 .patterns = &.{ 5695 .{ .src = .{ .to_sse, .to_sse } }, 5696 }, 5697 .extra_temps = .{ 5698 .{ .type = .f32, .kind = .{ .rc = .sse } }, 5699 .unused, 5700 .unused, 5701 .unused, 5702 .unused, 5703 .unused, 5704 .unused, 5705 .unused, 5706 .unused, 5707 }, 5708 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5709 .each = .{ .once = &.{ 5710 .{ ._, .v_ss, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 5711 .{ ._, .v_ss, .max, .dst0x, .src1x, .src0x, ._ }, 5712 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 5713 } }, 5714 }, .{ 5715 .required_features = .{ .sse4_1, null, null, null }, 5716 .src_constraints = .{ 5717 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5718 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5719 }, 5720 .patterns = &.{ 5721 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 5722 }, 5723 .dst_temps = .{.{ .rc = .sse }}, 5724 .each = .{ .once = &.{ 5725 .{ ._, ._ps, .mova, .dst0x, .src1x, ._, ._ }, 5726 .{ ._, ._ss, .max, .dst0x, .src0x, ._, ._ }, 5727 .{ ._, ._ss, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 5728 .{ ._, ._ps, .blendv, .dst0x, .src1x, .src0x, ._ }, 5729 } }, 5730 }, .{ 5731 .required_features = .{ .sse, null, null, null }, 5732 .src_constraints = .{ 5733 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5734 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 5735 }, 5736 .patterns = &.{ 5737 .{ .src = .{ .to_mut_sse, .to_sse } }, 5738 }, 5739 .extra_temps = .{ 5740 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5741 .unused, 5742 .unused, 5743 .unused, 5744 .unused, 5745 .unused, 5746 .unused, 5747 .unused, 5748 .unused, 5749 }, 5750 .dst_temps = .{.{ .ref = .src0 }}, 5751 .each = .{ .once = &.{ 5752 .{ ._, ._ps, .mova, .tmp0x, .src1x, ._, ._ }, 5753 .{ ._, ._ss, .max, .tmp0x, .src0x, ._, ._ }, 5754 .{ ._, ._ss, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 5755 .{ ._, ._ps, .@"and", .tmp0x, .dst0x, ._, ._ }, 5756 .{ ._, ._ps, .andn, .dst0x, .src1x, ._, ._ }, 5757 .{ ._, ._ps, .@"or", .dst0x, .tmp0x, ._, ._ }, 5758 } }, 5759 }, .{ 5760 .required_features = .{ .avx, null, null, null }, 5761 .src_constraints = .{ 5762 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5763 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5764 }, 5765 .patterns = &.{ 5766 .{ .src = .{ .to_sse, .to_sse } }, 5767 }, 5768 .extra_temps = .{ 5769 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5770 .unused, 5771 .unused, 5772 .unused, 5773 .unused, 5774 .unused, 5775 .unused, 5776 .unused, 5777 .unused, 5778 }, 5779 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5780 .each = .{ .once = &.{ 5781 .{ ._, .v_ps, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 5782 .{ ._, .v_ps, .max, .dst0x, .src1x, .src0x, ._ }, 5783 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 5784 } }, 5785 }, .{ 5786 .required_features = .{ .sse4_1, null, null, null }, 5787 .src_constraints = .{ 5788 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5789 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5790 }, 5791 .patterns = &.{ 5792 .{ .src = .{ .{ .to_reg = .xmm0 }, .mem } }, 5793 .{ .src = .{ .mem, .{ .to_reg = .xmm0 } }, .commute = .{ 0, 1 } }, 5794 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 5795 }, 5796 .dst_temps = .{.{ .rc = .sse }}, 5797 .each = .{ .once = &.{ 5798 .{ ._, ._ps, .mova, .dst0x, .src1x, ._, ._ }, 5799 .{ ._, ._ps, .max, .dst0x, .src0x, ._, ._ }, 5800 .{ ._, ._ps, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 5801 .{ ._, ._ps, .blendv, .dst0x, .src1x, .src0x, ._ }, 5802 } }, 5803 }, .{ 5804 .required_features = .{ .sse, null, null, null }, 5805 .src_constraints = .{ 5806 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5807 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 5808 }, 5809 .patterns = &.{ 5810 .{ .src = .{ .to_mut_sse, .mem } }, 5811 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 5812 .{ .src = .{ .to_mut_sse, .to_sse } }, 5813 }, 5814 .extra_temps = .{ 5815 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5816 .unused, 5817 .unused, 5818 .unused, 5819 .unused, 5820 .unused, 5821 .unused, 5822 .unused, 5823 .unused, 5824 }, 5825 .dst_temps = .{.{ .ref = .src0 }}, 5826 .each = .{ .once = &.{ 5827 .{ ._, ._ps, .mova, .tmp0x, .src1x, ._, ._ }, 5828 .{ ._, ._ps, .max, .tmp0x, .src0x, ._, ._ }, 5829 .{ ._, ._ps, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 5830 .{ ._, ._ps, .@"and", .tmp0x, .dst0x, ._, ._ }, 5831 .{ ._, ._ps, .andn, .dst0x, .src1x, ._, ._ }, 5832 .{ ._, ._ps, .@"or", .dst0x, .tmp0x, ._, ._ }, 5833 } }, 5834 }, .{ 5835 .required_features = .{ .avx, null, null, null }, 5836 .src_constraints = .{ 5837 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 5838 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 5839 }, 5840 .patterns = &.{ 5841 .{ .src = .{ .to_sse, .to_sse } }, 5842 }, 5843 .extra_temps = .{ 5844 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 5845 .unused, 5846 .unused, 5847 .unused, 5848 .unused, 5849 .unused, 5850 .unused, 5851 .unused, 5852 .unused, 5853 }, 5854 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5855 .each = .{ .once = &.{ 5856 .{ ._, .v_ps, .cmp, .tmp0y, .src0y, .src0y, .vp(.unord) }, 5857 .{ ._, .v_ps, .max, .dst0y, .src1y, .src0y, ._ }, 5858 .{ ._, .v_ps, .blendv, .dst0y, .dst0y, .src1y, .tmp0y }, 5859 } }, 5860 }, .{ 5861 .required_features = .{ .avx, null, null, null }, 5862 .src_constraints = .{ 5863 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 5864 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 5865 }, 5866 .patterns = &.{ 5867 .{ .src = .{ .to_mem, .to_mem } }, 5868 }, 5869 .extra_temps = .{ 5870 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5871 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 5872 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 5873 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 5874 .unused, 5875 .unused, 5876 .unused, 5877 .unused, 5878 .unused, 5879 }, 5880 .dst_temps = .{.mem}, 5881 .clobbers = .{ .eflags = true }, 5882 .each = .{ .once = &.{ 5883 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5884 .{ .@"0:", .v_ps, .mova, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 5885 .{ ._, .v_ps, .mova, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 5886 .{ ._, .v_ps, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 5887 .{ ._, .v_ps, .max, .tmp1y, .tmp2y, .tmp1y, ._ }, 5888 .{ ._, .v_ps, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 5889 .{ ._, .v_ps, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 5890 .{ ._, ._, .add, .tmp0q, .si(32), ._, ._ }, 5891 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5892 } }, 5893 }, .{ 5894 .required_features = .{ .sse4_1, null, null, null }, 5895 .src_constraints = .{ 5896 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 5897 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 5898 }, 5899 .patterns = &.{ 5900 .{ .src = .{ .to_mem, .to_mem } }, 5901 }, 5902 .extra_temps = .{ 5903 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5904 .{ .type = .vector_4_f32, .kind = .{ .reg = .xmm0 } }, 5905 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5906 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5907 .unused, 5908 .unused, 5909 .unused, 5910 .unused, 5911 .unused, 5912 }, 5913 .dst_temps = .{.mem}, 5914 .clobbers = .{ .eflags = true }, 5915 .each = .{ .once = &.{ 5916 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5917 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 5918 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 5919 .{ ._, ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 5920 .{ ._, ._ps, .max, .tmp3x, .tmp1x, ._, ._ }, 5921 .{ ._, ._ps, .cmp, .tmp1x, .tmp1x, .vp(.unord), ._ }, 5922 .{ ._, ._ps, .blendv, .tmp3x, .tmp2x, .tmp1x, ._ }, 5923 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 5924 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 5925 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5926 } }, 5927 }, .{ 5928 .required_features = .{ .sse, null, null, null }, 5929 .src_constraints = .{ 5930 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 5931 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 5932 }, 5933 .patterns = &.{ 5934 .{ .src = .{ .to_mem, .to_mem } }, 5935 }, 5936 .extra_temps = .{ 5937 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 5938 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5939 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5940 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 5941 .unused, 5942 .unused, 5943 .unused, 5944 .unused, 5945 .unused, 5946 }, 5947 .dst_temps = .{.mem}, 5948 .clobbers = .{ .eflags = true }, 5949 .each = .{ .once = &.{ 5950 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 5951 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 5952 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 5953 .{ ._, ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 5954 .{ ._, ._ps, .max, .tmp3x, .tmp1x, ._, ._ }, 5955 .{ ._, ._ps, .cmp, .tmp1x, .tmp1x, .vp(.ord), ._ }, 5956 .{ ._, ._ps, .@"and", .tmp3x, .tmp1x, ._, ._ }, 5957 .{ ._, ._ps, .andn, .tmp1x, .tmp2x, ._, ._ }, 5958 .{ ._, ._ps, .@"or", .tmp1x, .tmp3x, ._, ._ }, 5959 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 5960 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 5961 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 5962 } }, 5963 }, .{ 5964 .required_features = .{ .avx, null, null, null }, 5965 .src_constraints = .{ 5966 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 5967 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 5968 }, 5969 .patterns = &.{ 5970 .{ .src = .{ .to_sse, .to_sse } }, 5971 }, 5972 .extra_temps = .{ 5973 .{ .type = .f64, .kind = .{ .rc = .sse } }, 5974 .unused, 5975 .unused, 5976 .unused, 5977 .unused, 5978 .unused, 5979 .unused, 5980 .unused, 5981 .unused, 5982 }, 5983 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 5984 .each = .{ .once = &.{ 5985 .{ ._, .v_sd, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 5986 .{ ._, .v_sd, .max, .dst0x, .src1x, .src0x, ._ }, 5987 .{ ._, .v_pd, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 5988 } }, 5989 }, .{ 5990 .required_features = .{ .sse4_1, null, null, null }, 5991 .src_constraints = .{ 5992 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 5993 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 5994 }, 5995 .patterns = &.{ 5996 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 5997 }, 5998 .dst_temps = .{.{ .rc = .sse }}, 5999 .each = .{ .once = &.{ 6000 .{ ._, ._pd, .mova, .dst0x, .src1x, ._, ._ }, 6001 .{ ._, ._sd, .max, .dst0x, .src0x, ._, ._ }, 6002 .{ ._, ._sd, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 6003 .{ ._, ._pd, .blendv, .dst0x, .src1x, .src0x, ._ }, 6004 } }, 6005 }, .{ 6006 .required_features = .{ .sse2, null, null, null }, 6007 .src_constraints = .{ 6008 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 6009 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 6010 }, 6011 .patterns = &.{ 6012 .{ .src = .{ .to_mut_sse, .to_sse } }, 6013 }, 6014 .extra_temps = .{ 6015 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6016 .unused, 6017 .unused, 6018 .unused, 6019 .unused, 6020 .unused, 6021 .unused, 6022 .unused, 6023 .unused, 6024 }, 6025 .dst_temps = .{.{ .ref = .src0 }}, 6026 .each = .{ .once = &.{ 6027 .{ ._, ._pd, .mova, .tmp0x, .src1x, ._, ._ }, 6028 .{ ._, ._sd, .max, .tmp0x, .src0x, ._, ._ }, 6029 .{ ._, ._sd, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 6030 .{ ._, ._pd, .@"and", .tmp0x, .dst0x, ._, ._ }, 6031 .{ ._, ._pd, .andn, .dst0x, .src1x, ._, ._ }, 6032 .{ ._, ._pd, .@"or", .dst0x, .tmp0x, ._, ._ }, 6033 } }, 6034 }, .{ 6035 .required_features = .{ .sse, null, null, null }, 6036 .src_constraints = .{ 6037 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 6038 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 6039 }, 6040 .patterns = &.{ 6041 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 6042 }, 6043 .call_frame = .{ .alignment = .@"16" }, 6044 .extra_temps = .{ 6045 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmax" } } }, 6046 .unused, 6047 .unused, 6048 .unused, 6049 .unused, 6050 .unused, 6051 .unused, 6052 .unused, 6053 .unused, 6054 }, 6055 .dst_temps = .{.{ .ref = .src0 }}, 6056 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6057 .each = .{ .once = &.{ 6058 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 6059 } }, 6060 }, .{ 6061 .required_features = .{ .avx, null, null, null }, 6062 .src_constraints = .{ 6063 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6064 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6065 }, 6066 .patterns = &.{ 6067 .{ .src = .{ .to_sse, .to_sse } }, 6068 }, 6069 .extra_temps = .{ 6070 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6071 .unused, 6072 .unused, 6073 .unused, 6074 .unused, 6075 .unused, 6076 .unused, 6077 .unused, 6078 .unused, 6079 }, 6080 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 6081 .each = .{ .once = &.{ 6082 .{ ._, .v_pd, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 6083 .{ ._, .v_pd, .max, .dst0x, .src1x, .src0x, ._ }, 6084 .{ ._, .v_pd, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 6085 } }, 6086 }, .{ 6087 .required_features = .{ .sse4_1, null, null, null }, 6088 .src_constraints = .{ 6089 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6090 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6091 }, 6092 .patterns = &.{ 6093 .{ .src = .{ .{ .to_reg = .xmm0 }, .mem } }, 6094 .{ .src = .{ .mem, .{ .to_reg = .xmm0 } }, .commute = .{ 0, 1 } }, 6095 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 6096 }, 6097 .dst_temps = .{.{ .rc = .sse }}, 6098 .each = .{ .once = &.{ 6099 .{ ._, ._pd, .mova, .dst0x, .src1x, ._, ._ }, 6100 .{ ._, ._pd, .max, .dst0x, .src0x, ._, ._ }, 6101 .{ ._, ._pd, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 6102 .{ ._, ._pd, .blendv, .dst0x, .src1x, .src0x, ._ }, 6103 } }, 6104 }, .{ 6105 .required_features = .{ .sse2, null, null, null }, 6106 .src_constraints = .{ 6107 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6108 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 6109 }, 6110 .patterns = &.{ 6111 .{ .src = .{ .to_mut_sse, .mem } }, 6112 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 6113 .{ .src = .{ .to_mut_sse, .to_sse } }, 6114 }, 6115 .extra_temps = .{ 6116 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6117 .unused, 6118 .unused, 6119 .unused, 6120 .unused, 6121 .unused, 6122 .unused, 6123 .unused, 6124 .unused, 6125 }, 6126 .dst_temps = .{.{ .ref = .src0 }}, 6127 .each = .{ .once = &.{ 6128 .{ ._, ._pd, .mova, .tmp0x, .src1x, ._, ._ }, 6129 .{ ._, ._pd, .max, .tmp0x, .src0x, ._, ._ }, 6130 .{ ._, ._pd, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 6131 .{ ._, ._pd, .@"and", .tmp0x, .dst0x, ._, ._ }, 6132 .{ ._, ._pd, .andn, .dst0x, .src1x, ._, ._ }, 6133 .{ ._, ._pd, .@"or", .dst0x, .tmp0x, ._, ._ }, 6134 } }, 6135 }, .{ 6136 .required_features = .{ .avx, null, null, null }, 6137 .src_constraints = .{ 6138 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 6139 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 6140 }, 6141 .patterns = &.{ 6142 .{ .src = .{ .to_sse, .to_sse } }, 6143 }, 6144 .extra_temps = .{ 6145 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 6146 .unused, 6147 .unused, 6148 .unused, 6149 .unused, 6150 .unused, 6151 .unused, 6152 .unused, 6153 .unused, 6154 }, 6155 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 6156 .each = .{ .once = &.{ 6157 .{ ._, .v_pd, .cmp, .tmp0y, .src0y, .src0y, .vp(.unord) }, 6158 .{ ._, .v_pd, .max, .dst0y, .src1y, .src0y, ._ }, 6159 .{ ._, .v_pd, .blendv, .dst0y, .dst0y, .src1y, .tmp0y }, 6160 } }, 6161 }, .{ 6162 .required_features = .{ .avx, null, null, null }, 6163 .src_constraints = .{ 6164 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 6165 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 6166 }, 6167 .patterns = &.{ 6168 .{ .src = .{ .to_mem, .to_mem } }, 6169 }, 6170 .extra_temps = .{ 6171 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6172 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 6173 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 6174 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 6175 .unused, 6176 .unused, 6177 .unused, 6178 .unused, 6179 .unused, 6180 }, 6181 .dst_temps = .{.mem}, 6182 .clobbers = .{ .eflags = true }, 6183 .each = .{ .once = &.{ 6184 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6185 .{ .@"0:", .v_pd, .mova, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 6186 .{ ._, .v_pd, .mova, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 6187 .{ ._, .v_pd, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 6188 .{ ._, .v_pd, .max, .tmp1y, .tmp2y, .tmp1y, ._ }, 6189 .{ ._, .v_pd, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 6190 .{ ._, .v_pd, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 6191 .{ ._, ._, .add, .tmp0q, .si(32), ._, ._ }, 6192 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6193 } }, 6194 }, .{ 6195 .required_features = .{ .sse4_1, null, null, null }, 6196 .src_constraints = .{ 6197 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 6198 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 6199 }, 6200 .patterns = &.{ 6201 .{ .src = .{ .to_mem, .to_mem } }, 6202 }, 6203 .extra_temps = .{ 6204 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6205 .{ .type = .vector_2_f64, .kind = .{ .reg = .xmm0 } }, 6206 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6207 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6208 .unused, 6209 .unused, 6210 .unused, 6211 .unused, 6212 .unused, 6213 }, 6214 .dst_temps = .{.mem}, 6215 .clobbers = .{ .eflags = true }, 6216 .each = .{ .once = &.{ 6217 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6218 .{ .@"0:", ._pd, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 6219 .{ ._, ._pd, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 6220 .{ ._, ._pd, .mova, .tmp3x, .tmp2x, ._, ._ }, 6221 .{ ._, ._pd, .max, .tmp3x, .tmp1x, ._, ._ }, 6222 .{ ._, ._pd, .cmp, .tmp1x, .tmp1x, .vp(.unord), ._ }, 6223 .{ ._, ._pd, .blendv, .tmp3x, .tmp2x, .tmp1x, ._ }, 6224 .{ ._, ._pd, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 6225 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 6226 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6227 } }, 6228 }, .{ 6229 .required_features = .{ .sse2, null, null, null }, 6230 .src_constraints = .{ 6231 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 6232 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 6233 }, 6234 .patterns = &.{ 6235 .{ .src = .{ .to_mem, .to_mem } }, 6236 }, 6237 .extra_temps = .{ 6238 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6239 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6240 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6241 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 6242 .unused, 6243 .unused, 6244 .unused, 6245 .unused, 6246 .unused, 6247 }, 6248 .dst_temps = .{.mem}, 6249 .clobbers = .{ .eflags = true }, 6250 .each = .{ .once = &.{ 6251 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6252 .{ .@"0:", ._pd, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 6253 .{ ._, ._pd, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 6254 .{ ._, ._pd, .mova, .tmp3x, .tmp2x, ._, ._ }, 6255 .{ ._, ._pd, .max, .tmp3x, .tmp1x, ._, ._ }, 6256 .{ ._, ._pd, .cmp, .tmp1x, .tmp1x, .vp(.ord), ._ }, 6257 .{ ._, ._pd, .@"and", .tmp3x, .tmp1x, ._, ._ }, 6258 .{ ._, ._pd, .andn, .tmp1x, .tmp2x, ._, ._ }, 6259 .{ ._, ._pd, .@"or", .tmp1x, .tmp3x, ._, ._ }, 6260 .{ ._, ._pd, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 6261 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 6262 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6263 } }, 6264 }, .{ 6265 .required_features = .{ .sse, null, null, null }, 6266 .src_constraints = .{ 6267 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 6268 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 6269 }, 6270 .patterns = &.{ 6271 .{ .src = .{ .to_mem, .to_mem } }, 6272 }, 6273 .call_frame = .{ .alignment = .@"16" }, 6274 .extra_temps = .{ 6275 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6276 .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, 6277 .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, 6278 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmax" } } }, 6279 .unused, 6280 .unused, 6281 .unused, 6282 .unused, 6283 .unused, 6284 }, 6285 .dst_temps = .{.mem}, 6286 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6287 .each = .{ .once = &.{ 6288 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6289 .{ .@"0:", ._ps, .xor, .tmp1x, .tmp1x, ._, ._ }, 6290 .{ ._, ._ps, .xor, .tmp2x, .tmp2x, ._, ._ }, 6291 .{ ._, ._ps, .movl, .tmp1x, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 6292 .{ ._, ._ps, .movl, .tmp2x, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 6293 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 6294 .{ ._, ._ps, .movl, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 6295 .{ ._, ._, .add, .tmp0q, .si(8), ._, ._ }, 6296 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6297 } }, 6298 }, .{ 6299 .required_features = .{ .x87, .cmov, null, null }, 6300 .src_constraints = .{ 6301 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6302 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6303 }, 6304 .patterns = &.{ 6305 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 6306 .{ .src = .{ .mem, .to_x87 } }, 6307 .{ .src = .{ .to_x87, .to_x87 } }, 6308 }, 6309 .extra_temps = .{ 6310 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6311 .unused, 6312 .unused, 6313 .unused, 6314 .unused, 6315 .unused, 6316 .unused, 6317 .unused, 6318 .unused, 6319 }, 6320 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 6321 .clobbers = .{ .eflags = true }, 6322 .each = .{ .once = &.{ 6323 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 6324 .{ ._, .f_, .ucomi, .tmp0t, .tmp0t, ._, ._ }, 6325 .{ ._, .f_u, .cmov, .tmp0t, .src1t, ._, ._ }, 6326 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6327 .{ ._, .f_, .ucomi, .tmp0t, .src1t, ._, ._ }, 6328 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6329 .{ ._, .f_nb, .cmov, .tmp0t, .src1t, ._, ._ }, 6330 .{ ._, .f_p, .st, .dst0t, ._, ._, ._ }, 6331 } }, 6332 }, .{ 6333 .required_features = .{ .sahf, .x87, null, null }, 6334 .src_constraints = .{ 6335 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6336 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6337 }, 6338 .patterns = &.{ 6339 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 6340 .{ .src = .{ .mem, .to_x87 } }, 6341 .{ .src = .{ .to_x87, .to_x87 } }, 6342 }, 6343 .extra_temps = .{ 6344 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6345 .{ .type = .u8, .kind = .{ .reg = .ah } }, 6346 .unused, 6347 .unused, 6348 .unused, 6349 .unused, 6350 .unused, 6351 .unused, 6352 .unused, 6353 }, 6354 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 6355 .clobbers = .{ .eflags = true }, 6356 .each = .{ .once = &.{ 6357 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 6358 .{ ._, .f_, .ucom, .tmp0t, ._, ._, ._ }, 6359 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 6360 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 6361 .{ ._, ._p, .j, .@"0f", ._, ._, ._ }, 6362 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6363 .{ ._, .f_, .ucom, .src1t, ._, ._, ._ }, 6364 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 6365 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6366 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 6367 .{ ._, ._b, .j, .@"1f", ._, ._, ._ }, 6368 .{ .@"0:", .f_p, .st, .tmp0t, ._, ._, ._ }, 6369 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 6370 .{ .@"1:", .f_p, .st, .dst0t, ._, ._, ._ }, 6371 } }, 6372 }, .{ 6373 .required_features = .{ .x87, null, null, null }, 6374 .src_constraints = .{ 6375 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6376 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 6377 }, 6378 .patterns = &.{ 6379 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 6380 .{ .src = .{ .mem, .to_x87 } }, 6381 .{ .src = .{ .to_x87, .to_x87 } }, 6382 }, 6383 .extra_temps = .{ 6384 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6385 .{ .type = .u8, .kind = .{ .reg = .ah } }, 6386 .unused, 6387 .unused, 6388 .unused, 6389 .unused, 6390 .unused, 6391 .unused, 6392 .unused, 6393 }, 6394 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 6395 .clobbers = .{ .eflags = true }, 6396 .each = .{ .once = &.{ 6397 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 6398 .{ ._, .f_, .xam, ._, ._, ._, ._ }, 6399 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 6400 .{ ._, ._, .@"test", .tmp1b, .si(0b1_000_100), ._, ._ }, 6401 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 6402 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6403 .{ ._, .f_, .ucom, .src1t, ._, ._, ._ }, 6404 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 6405 .{ ._, .f_, .xch, .src1t, ._, ._, ._ }, 6406 .{ ._, ._, .@"test", .tmp1b, .si(0b0_000_001), ._, ._ }, 6407 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 6408 .{ .@"0:", .f_p, .st, .tmp0t, ._, ._, ._ }, 6409 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 6410 .{ .@"1:", .f_p, .st, .dst0t, ._, ._, ._ }, 6411 } }, 6412 }, .{ 6413 .required_features = .{ .x87, .cmov, null, null }, 6414 .src_constraints = .{ 6415 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6416 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6417 }, 6418 .patterns = &.{ 6419 .{ .src = .{ .to_mem, .to_mem } }, 6420 }, 6421 .extra_temps = .{ 6422 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6423 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 6424 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6425 .unused, 6426 .unused, 6427 .unused, 6428 .unused, 6429 .unused, 6430 .unused, 6431 }, 6432 .dst_temps = .{.mem}, 6433 .clobbers = .{ .eflags = true }, 6434 .each = .{ .once = &.{ 6435 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 6436 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 6437 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 6438 .{ ._, .f_, .ucomi, .tmp1t, .tmp1t, ._, ._ }, 6439 .{ ._, .f_u, .cmov, .tmp1t, .tmp2t, ._, ._ }, 6440 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6441 .{ ._, .f_, .ucomi, .tmp1t, .tmp2t, ._, ._ }, 6442 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6443 .{ ._, .f_nb, .cmov, .tmp1t, .tmp2t, ._, ._ }, 6444 .{ ._, .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 6445 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 6446 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 6447 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6448 } }, 6449 }, .{ 6450 .required_features = .{ .sahf, .x87, null, null }, 6451 .src_constraints = .{ 6452 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6453 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6454 }, 6455 .patterns = &.{ 6456 .{ .src = .{ .to_mem, .to_mem } }, 6457 }, 6458 .extra_temps = .{ 6459 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6460 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 6461 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6462 .{ .type = .u8, .kind = .{ .reg = .ah } }, 6463 .unused, 6464 .unused, 6465 .unused, 6466 .unused, 6467 .unused, 6468 }, 6469 .dst_temps = .{.mem}, 6470 .clobbers = .{ .eflags = true }, 6471 .each = .{ .once = &.{ 6472 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 6473 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 6474 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 6475 .{ ._, .f_, .ucom, .tmp1t, ._, ._, ._ }, 6476 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 6477 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 6478 .{ ._, ._p, .j, .@"1f", ._, ._, ._ }, 6479 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6480 .{ ._, .f_, .ucom, .tmp2t, ._, ._, ._ }, 6481 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 6482 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6483 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 6484 .{ ._, ._b, .j, .@"2f", ._, ._, ._ }, 6485 .{ .@"1:", .f_p, .st, .tmp1t, ._, ._, ._ }, 6486 .{ ._, .f_, .ld, .tmp2t, ._, ._, ._ }, 6487 .{ .@"2:", .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 6488 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 6489 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 6490 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6491 } }, 6492 }, .{ 6493 .required_features = .{ .x87, null, null, null }, 6494 .src_constraints = .{ 6495 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6496 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 6497 }, 6498 .patterns = &.{ 6499 .{ .src = .{ .to_mem, .to_mem } }, 6500 }, 6501 .extra_temps = .{ 6502 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6503 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 6504 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 6505 .{ .type = .u8, .kind = .{ .reg = .ah } }, 6506 .unused, 6507 .unused, 6508 .unused, 6509 .unused, 6510 .unused, 6511 }, 6512 .dst_temps = .{.mem}, 6513 .clobbers = .{ .eflags = true }, 6514 .each = .{ .once = &.{ 6515 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 6516 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 6517 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 6518 .{ ._, .f_, .xam, ._, ._, ._, ._ }, 6519 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 6520 .{ ._, ._, .@"test", .tmp3b, .si(0b1_000_100), ._, ._ }, 6521 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 6522 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6523 .{ ._, .f_, .ucom, .tmp2t, ._, ._, ._ }, 6524 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 6525 .{ ._, .f_, .xch, .tmp2t, ._, ._, ._ }, 6526 .{ ._, ._, .@"test", .tmp3b, .si(0b0_000_001), ._, ._ }, 6527 .{ ._, ._nz, .j, .@"2f", ._, ._, ._ }, 6528 .{ .@"1:", .f_p, .st, .tmp1t, ._, ._, ._ }, 6529 .{ ._, .f_, .ld, .tmp2t, ._, ._, ._ }, 6530 .{ .@"2:", .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 6531 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 6532 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 6533 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6534 } }, 6535 }, .{ 6536 .required_features = .{ .sse, null, null, null }, 6537 .src_constraints = .{ 6538 .{ .scalar_float = .{ .of = .xword, .is = .xword } }, 6539 .{ .scalar_float = .{ .of = .xword, .is = .xword } }, 6540 }, 6541 .patterns = &.{ 6542 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 6543 }, 6544 .call_frame = .{ .alignment = .@"16" }, 6545 .extra_temps = .{ 6546 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, 6547 .unused, 6548 .unused, 6549 .unused, 6550 .unused, 6551 .unused, 6552 .unused, 6553 .unused, 6554 .unused, 6555 }, 6556 .dst_temps = .{.{ .ref = .src0 }}, 6557 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6558 .each = .{ .once = &.{ 6559 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 6560 } }, 6561 }, .{ 6562 .required_features = .{ .avx, null, null, null }, 6563 .src_constraints = .{ 6564 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6565 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6566 }, 6567 .patterns = &.{ 6568 .{ .src = .{ .to_mem, .to_mem } }, 6569 }, 6570 .call_frame = .{ .alignment = .@"16" }, 6571 .extra_temps = .{ 6572 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6573 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 6574 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 6575 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, 6576 .unused, 6577 .unused, 6578 .unused, 6579 .unused, 6580 .unused, 6581 }, 6582 .dst_temps = .{.mem}, 6583 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6584 .each = .{ .once = &.{ 6585 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6586 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 6587 .{ ._, .v_dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 6588 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 6589 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 6590 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 6591 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6592 } }, 6593 }, .{ 6594 .required_features = .{ .sse2, null, null, null }, 6595 .src_constraints = .{ 6596 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6597 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6598 }, 6599 .patterns = &.{ 6600 .{ .src = .{ .to_mem, .to_mem } }, 6601 }, 6602 .call_frame = .{ .alignment = .@"16" }, 6603 .extra_temps = .{ 6604 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6605 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 6606 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 6607 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, 6608 .unused, 6609 .unused, 6610 .unused, 6611 .unused, 6612 .unused, 6613 }, 6614 .dst_temps = .{.mem}, 6615 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6616 .each = .{ .once = &.{ 6617 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6618 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 6619 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 6620 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 6621 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 6622 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 6623 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6624 } }, 6625 }, .{ 6626 .required_features = .{ .sse, null, null, null }, 6627 .src_constraints = .{ 6628 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6629 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 6630 }, 6631 .patterns = &.{ 6632 .{ .src = .{ .to_mem, .to_mem } }, 6633 }, 6634 .call_frame = .{ .alignment = .@"16" }, 6635 .extra_temps = .{ 6636 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 6637 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 6638 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 6639 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmaxq" } } }, 6640 .unused, 6641 .unused, 6642 .unused, 6643 .unused, 6644 .unused, 6645 }, 6646 .dst_temps = .{.mem}, 6647 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 6648 .each = .{ .once = &.{ 6649 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 6650 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 6651 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 6652 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 6653 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 6654 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 6655 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 6656 } }, 6657 } }) catch |err| switch (err) { 6658 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 6659 @tagName(air_tag), 6660 cg.typeOf(bin_op.lhs).fmt(pt), 6661 ops[0].tracking(cg), 6662 ops[1].tracking(cg), 6663 }), 6664 else => |e| return e, 6665 }; 6666 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 6667 }, 6668 .min => |air_tag| if (use_old) try cg.airBinOp(inst, air_tag) else { 6669 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 6670 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 6671 var res: [1]Temp = undefined; 6672 cg.select(&res, &.{cg.typeOf(bin_op.lhs)}, &ops, comptime &.{ .{ 6673 .required_features = .{ .cmov, null, null, null }, 6674 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 6675 .patterns = &.{ 6676 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6677 }, 6678 .dst_temps = .{.{ .ref = .src0 }}, 6679 .clobbers = .{ .eflags = true }, 6680 .each = .{ .once = &.{ 6681 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 6682 .{ ._, ._ge, .cmov, .dst0d, .src1d, ._, ._ }, 6683 } }, 6684 }, .{ 6685 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 6686 .patterns = &.{ 6687 .{ .src = .{ .to_mut_gpr, .mem } }, 6688 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6689 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6690 }, 6691 .dst_temps = .{.{ .ref = .src0 }}, 6692 .clobbers = .{ .eflags = true }, 6693 .each = .{ .once = &.{ 6694 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 6695 .{ ._, ._nge, .j, .@"0f", ._, ._, ._ }, 6696 .{ ._, ._, .mov, .dst0b, .src1b, ._, ._ }, 6697 } }, 6698 }, .{ 6699 .required_features = .{ .cmov, null, null, null }, 6700 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 6701 .patterns = &.{ 6702 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6703 }, 6704 .dst_temps = .{.{ .ref = .src0 }}, 6705 .clobbers = .{ .eflags = true }, 6706 .each = .{ .once = &.{ 6707 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 6708 .{ ._, ._ae, .cmov, .dst0d, .src1d, ._, ._ }, 6709 } }, 6710 }, .{ 6711 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 6712 .patterns = &.{ 6713 .{ .src = .{ .to_mut_gpr, .mem } }, 6714 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6715 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6716 }, 6717 .dst_temps = .{.{ .ref = .src0 }}, 6718 .clobbers = .{ .eflags = true }, 6719 .each = .{ .once = &.{ 6720 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 6721 .{ ._, ._nae, .j, .@"0f", ._, ._, ._ }, 6722 .{ ._, ._, .mov, .dst0b, .src1b, ._, ._ }, 6723 } }, 6724 }, .{ 6725 .required_features = .{ .cmov, null, null, null }, 6726 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 6727 .patterns = &.{ 6728 .{ .src = .{ .to_mut_gpr, .mem } }, 6729 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6730 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6731 }, 6732 .dst_temps = .{.{ .ref = .src0 }}, 6733 .clobbers = .{ .eflags = true }, 6734 .each = .{ .once = &.{ 6735 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 6736 .{ ._, ._ge, .cmov, .dst0w, .src1w, ._, ._ }, 6737 } }, 6738 }, .{ 6739 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 6740 .patterns = &.{ 6741 .{ .src = .{ .to_mut_gpr, .mem } }, 6742 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6743 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6744 }, 6745 .dst_temps = .{.{ .ref = .src0 }}, 6746 .clobbers = .{ .eflags = true }, 6747 .each = .{ .once = &.{ 6748 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 6749 .{ ._, ._nge, .j, .@"0f", ._, ._, ._ }, 6750 .{ ._, ._, .mov, .dst0w, .src1w, ._, ._ }, 6751 } }, 6752 }, .{ 6753 .required_features = .{ .cmov, null, null, null }, 6754 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 6755 .patterns = &.{ 6756 .{ .src = .{ .to_mut_gpr, .mem } }, 6757 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6758 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6759 }, 6760 .dst_temps = .{.{ .ref = .src0 }}, 6761 .clobbers = .{ .eflags = true }, 6762 .each = .{ .once = &.{ 6763 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 6764 .{ ._, ._ae, .cmov, .dst0w, .src1w, ._, ._ }, 6765 } }, 6766 }, .{ 6767 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 6768 .patterns = &.{ 6769 .{ .src = .{ .to_mut_gpr, .mem } }, 6770 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6771 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6772 }, 6773 .dst_temps = .{.{ .ref = .src0 }}, 6774 .clobbers = .{ .eflags = true }, 6775 .each = .{ .once = &.{ 6776 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 6777 .{ ._, ._nae, .j, .@"0f", ._, ._, ._ }, 6778 .{ ._, ._, .mov, .dst0w, .src1w, ._, ._ }, 6779 } }, 6780 }, .{ 6781 .required_features = .{ .cmov, null, null, null }, 6782 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 6783 .patterns = &.{ 6784 .{ .src = .{ .to_mut_gpr, .mem } }, 6785 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6786 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6787 }, 6788 .dst_temps = .{.{ .ref = .src0 }}, 6789 .clobbers = .{ .eflags = true }, 6790 .each = .{ .once = &.{ 6791 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 6792 .{ ._, ._ge, .cmov, .dst0d, .src1d, ._, ._ }, 6793 } }, 6794 }, .{ 6795 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 6796 .patterns = &.{ 6797 .{ .src = .{ .to_mut_gpr, .mem } }, 6798 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6799 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6800 }, 6801 .dst_temps = .{.{ .ref = .src0 }}, 6802 .clobbers = .{ .eflags = true }, 6803 .each = .{ .once = &.{ 6804 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 6805 .{ ._, ._nge, .j, .@"0f", ._, ._, ._ }, 6806 .{ ._, ._, .mov, .dst0d, .src1d, ._, ._ }, 6807 } }, 6808 }, .{ 6809 .required_features = .{ .cmov, null, null, null }, 6810 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 6811 .patterns = &.{ 6812 .{ .src = .{ .to_mut_gpr, .mem } }, 6813 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6814 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6815 }, 6816 .dst_temps = .{.{ .ref = .src0 }}, 6817 .clobbers = .{ .eflags = true }, 6818 .each = .{ .once = &.{ 6819 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 6820 .{ ._, ._ae, .cmov, .dst0d, .src1d, ._, ._ }, 6821 } }, 6822 }, .{ 6823 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 6824 .patterns = &.{ 6825 .{ .src = .{ .to_mut_gpr, .mem } }, 6826 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6827 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6828 }, 6829 .dst_temps = .{.{ .ref = .src0 }}, 6830 .clobbers = .{ .eflags = true }, 6831 .each = .{ .once = &.{ 6832 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 6833 .{ ._, ._nae, .j, .@"0f", ._, ._, ._ }, 6834 .{ ._, ._, .mov, .dst0d, .src1d, ._, ._ }, 6835 } }, 6836 }, .{ 6837 .required_features = .{ .@"64bit", .cmov, null, null }, 6838 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 6839 .patterns = &.{ 6840 .{ .src = .{ .to_mut_gpr, .mem } }, 6841 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6842 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6843 }, 6844 .dst_temps = .{.{ .ref = .src0 }}, 6845 .clobbers = .{ .eflags = true }, 6846 .each = .{ .once = &.{ 6847 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 6848 .{ ._, ._ge, .cmov, .dst0q, .src1q, ._, ._ }, 6849 } }, 6850 }, .{ 6851 .required_features = .{ .@"64bit", null, null, null }, 6852 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 6853 .patterns = &.{ 6854 .{ .src = .{ .to_mut_gpr, .mem } }, 6855 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6856 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6857 }, 6858 .dst_temps = .{.{ .ref = .src0 }}, 6859 .clobbers = .{ .eflags = true }, 6860 .each = .{ .once = &.{ 6861 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 6862 .{ ._, ._nge, .j, .@"0f", ._, ._, ._ }, 6863 .{ ._, ._, .mov, .dst0q, .src1q, ._, ._ }, 6864 } }, 6865 }, .{ 6866 .required_features = .{ .@"64bit", .cmov, null, null }, 6867 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 6868 .patterns = &.{ 6869 .{ .src = .{ .to_mut_gpr, .mem } }, 6870 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6871 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6872 }, 6873 .dst_temps = .{.{ .ref = .src0 }}, 6874 .clobbers = .{ .eflags = true }, 6875 .each = .{ .once = &.{ 6876 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 6877 .{ ._, ._ae, .cmov, .dst0q, .src1q, ._, ._ }, 6878 } }, 6879 }, .{ 6880 .required_features = .{ .@"64bit", null, null, null }, 6881 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 6882 .patterns = &.{ 6883 .{ .src = .{ .to_mut_gpr, .mem } }, 6884 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 6885 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 6886 }, 6887 .dst_temps = .{.{ .ref = .src0 }}, 6888 .clobbers = .{ .eflags = true }, 6889 .each = .{ .once = &.{ 6890 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 6891 .{ ._, ._nae, .j, .@"0f", ._, ._, ._ }, 6892 .{ ._, ._, .mov, .dst0q, .src1q, ._, ._ }, 6893 } }, 6894 }, .{ 6895 .required_features = .{ .@"64bit", .cmov, null, null }, 6896 .src_constraints = .{ .any_signed_int, .any_signed_int }, 6897 .patterns = &.{ 6898 .{ .src = .{ .to_mem, .to_mem } }, 6899 }, 6900 .extra_temps = .{ 6901 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 6902 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 6903 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 6904 .unused, 6905 .unused, 6906 .unused, 6907 .unused, 6908 .unused, 6909 .unused, 6910 }, 6911 .dst_temps = .{.mem}, 6912 .clobbers = .{ .eflags = true }, 6913 .each = .{ .once = &.{ 6914 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_8), ._, ._ }, 6915 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 6916 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 6917 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 6918 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 6919 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 6920 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 6921 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -8), ._, ._ }, 6922 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 6923 .{ ._, ._, .lea, .tmp1p, .mem(.src1), ._, ._ }, 6924 .{ ._, ._ge, .cmov, .tmp0p, .tmp1p, ._, ._ }, 6925 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 6926 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 6927 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 6928 } }, 6929 }, .{ 6930 .required_features = .{ .@"64bit", null, null, null }, 6931 .src_constraints = .{ .any_signed_int, .any_signed_int }, 6932 .patterns = &.{ 6933 .{ .src = .{ .to_mem, .to_mem } }, 6934 }, 6935 .extra_temps = .{ 6936 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 6937 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 6938 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 6939 .unused, 6940 .unused, 6941 .unused, 6942 .unused, 6943 .unused, 6944 .unused, 6945 }, 6946 .dst_temps = .{.mem}, 6947 .clobbers = .{ .eflags = true }, 6948 .each = .{ .once = &.{ 6949 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_8), ._, ._ }, 6950 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 6951 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 6952 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 6953 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 6954 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 6955 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 6956 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -8), ._, ._ }, 6957 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 6958 .{ ._, ._nge, .j, .@"0f", ._, ._, ._ }, 6959 .{ ._, ._, .lea, .tmp0p, .mem(.src1), ._, ._ }, 6960 .{ .@"0:", ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 6961 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 6962 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 6963 } }, 6964 }, .{ 6965 .required_features = .{ .@"64bit", .cmov, null, null }, 6966 .src_constraints = .{ .any_unsigned_int, .any_unsigned_int }, 6967 .patterns = &.{ 6968 .{ .src = .{ .to_mem, .to_mem } }, 6969 }, 6970 .extra_temps = .{ 6971 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 6972 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 6973 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 6974 .unused, 6975 .unused, 6976 .unused, 6977 .unused, 6978 .unused, 6979 .unused, 6980 }, 6981 .dst_temps = .{.mem}, 6982 .clobbers = .{ .eflags = true }, 6983 .each = .{ .once = &.{ 6984 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_8), ._, ._ }, 6985 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 6986 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_size), ._, ._ }, 6987 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"8", .tmp0, .add_size), ._, ._ }, 6988 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 6989 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 6990 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 6991 .{ ._, ._, .lea, .tmp1p, .mem(.src1), ._, ._ }, 6992 .{ ._, ._ae, .cmov, .tmp0p, .tmp1p, ._, ._ }, 6993 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 6994 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 6995 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 6996 } }, 6997 }, .{ 6998 .required_features = .{ .@"64bit", null, null, null }, 6999 .src_constraints = .{ .any_unsigned_int, .any_unsigned_int }, 7000 .patterns = &.{ 7001 .{ .src = .{ .to_mem, .to_mem } }, 7002 }, 7003 .extra_temps = .{ 7004 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 7005 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 7006 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 7007 .unused, 7008 .unused, 7009 .unused, 7010 .unused, 7011 .unused, 7012 .unused, 7013 }, 7014 .dst_temps = .{.mem}, 7015 .clobbers = .{ .eflags = true }, 7016 .each = .{ .once = &.{ 7017 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_8), ._, ._ }, 7018 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 7019 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_size), ._, ._ }, 7020 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"8", .tmp0, .add_size), ._, ._ }, 7021 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 7022 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 7023 .{ ._, ._, .lea, .tmp0p, .mem(.src0), ._, ._ }, 7024 .{ ._, ._nae, .j, .@"0f", ._, ._, ._ }, 7025 .{ ._, ._, .lea, .tmp0p, .mem(.src1), ._, ._ }, 7026 .{ .@"0:", ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 7027 .{ ._, ._, .mov, .tmp2d, .sa(.src0, .add_size_div_8), ._, ._ }, 7028 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 7029 } }, 7030 }, .{ 7031 .required_features = .{ .avx, null, null, null }, 7032 .src_constraints = .{ 7033 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7034 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7035 }, 7036 .patterns = &.{ 7037 .{ .src = .{ .to_sse, .mem } }, 7038 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7039 .{ .src = .{ .to_sse, .to_sse } }, 7040 }, 7041 .dst_temps = .{.{ .rc = .sse }}, 7042 .each = .{ .once = &.{ 7043 .{ ._, .vp_b, .mins, .dst0x, .src0x, .src1x, ._ }, 7044 } }, 7045 }, .{ 7046 .required_features = .{ .sse4_1, null, null, null }, 7047 .src_constraints = .{ 7048 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7049 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7050 }, 7051 .patterns = &.{ 7052 .{ .src = .{ .to_mut_sse, .mem } }, 7053 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7054 .{ .src = .{ .to_mut_sse, .to_sse } }, 7055 }, 7056 .dst_temps = .{.{ .ref = .src0 }}, 7057 .each = .{ .once = &.{ 7058 .{ ._, .p_b, .mins, .dst0x, .src1x, ._, ._ }, 7059 } }, 7060 }, .{ 7061 .required_features = .{ .sse2, null, null, null }, 7062 .src_constraints = .{ 7063 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7064 .{ .scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7065 }, 7066 .patterns = &.{ 7067 .{ .src = .{ .to_mut_sse, .mem } }, 7068 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7069 .{ .src = .{ .to_mut_sse, .to_sse } }, 7070 }, 7071 .dst_temps = .{.{ .rc = .sse }}, 7072 .each = .{ .once = &.{ 7073 .{ ._, ._dqa, .mov, .dst0x, .src1x, ._, ._ }, 7074 .{ ._, .p_b, .cmpgt, .dst0x, .src0x, ._, ._ }, 7075 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 7076 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 7077 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 7078 } }, 7079 }, .{ 7080 .required_features = .{ .avx2, null, null, null }, 7081 .src_constraints = .{ 7082 .{ .scalar_signed_int = .{ .of = .yword, .is = .byte } }, 7083 .{ .scalar_signed_int = .{ .of = .yword, .is = .byte } }, 7084 }, 7085 .patterns = &.{ 7086 .{ .src = .{ .to_sse, .mem } }, 7087 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7088 .{ .src = .{ .to_sse, .to_sse } }, 7089 }, 7090 .dst_temps = .{.{ .rc = .sse }}, 7091 .each = .{ .once = &.{ 7092 .{ ._, .vp_b, .mins, .dst0y, .src0y, .src1y, ._ }, 7093 } }, 7094 }, .{ 7095 .required_features = .{ .avx2, null, null, null }, 7096 .src_constraints = .{ 7097 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .byte } }, 7098 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .byte } }, 7099 }, 7100 .patterns = &.{ 7101 .{ .src = .{ .to_mem, .to_mem } }, 7102 }, 7103 .extra_temps = .{ 7104 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7105 .{ .type = .vector_32_i8, .kind = .{ .rc = .sse } }, 7106 .unused, 7107 .unused, 7108 .unused, 7109 .unused, 7110 .unused, 7111 .unused, 7112 .unused, 7113 }, 7114 .dst_temps = .{.mem}, 7115 .clobbers = .{ .eflags = true }, 7116 .each = .{ .once = &.{ 7117 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7118 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 7119 .{ ._, .vp_b, .mins, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 7120 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 7121 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 7122 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7123 } }, 7124 }, .{ 7125 .required_features = .{ .avx, null, null, null }, 7126 .src_constraints = .{ 7127 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7128 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7129 }, 7130 .patterns = &.{ 7131 .{ .src = .{ .to_mem, .to_mem } }, 7132 }, 7133 .extra_temps = .{ 7134 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7135 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 7136 .unused, 7137 .unused, 7138 .unused, 7139 .unused, 7140 .unused, 7141 .unused, 7142 .unused, 7143 }, 7144 .dst_temps = .{.mem}, 7145 .clobbers = .{ .eflags = true }, 7146 .each = .{ .once = &.{ 7147 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7148 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7149 .{ ._, .vp_b, .mins, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 7150 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7151 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7152 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7153 } }, 7154 }, .{ 7155 .required_features = .{ .sse4_1, null, null, null }, 7156 .src_constraints = .{ 7157 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7158 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7159 }, 7160 .patterns = &.{ 7161 .{ .src = .{ .to_mem, .to_mem } }, 7162 }, 7163 .extra_temps = .{ 7164 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7165 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 7166 .unused, 7167 .unused, 7168 .unused, 7169 .unused, 7170 .unused, 7171 .unused, 7172 .unused, 7173 }, 7174 .dst_temps = .{.mem}, 7175 .clobbers = .{ .eflags = true }, 7176 .each = .{ .once = &.{ 7177 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7178 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7179 .{ ._, .p_b, .mins, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 7180 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7181 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7182 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7183 } }, 7184 }, .{ 7185 .required_features = .{ .sse2, null, null, null }, 7186 .src_constraints = .{ 7187 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7188 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .byte } }, 7189 }, 7190 .patterns = &.{ 7191 .{ .src = .{ .to_mem, .to_mem } }, 7192 }, 7193 .extra_temps = .{ 7194 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7195 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 7196 .{ .type = .vector_16_i8, .kind = .{ .rc = .sse } }, 7197 .unused, 7198 .unused, 7199 .unused, 7200 .unused, 7201 .unused, 7202 .unused, 7203 }, 7204 .dst_temps = .{.mem}, 7205 .clobbers = .{ .eflags = true }, 7206 .each = .{ .once = &.{ 7207 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7208 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7209 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 7210 .{ ._, .p_b, .cmpgt, .tmp1x, .tmp2x, ._, ._ }, 7211 .{ ._, .p_, .@"and", .tmp2x, .tmp1x, ._, ._ }, 7212 .{ ._, .p_, .andn, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7213 .{ ._, .p_, .@"or", .tmp1x, .tmp2x, ._, ._ }, 7214 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7215 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7216 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7217 } }, 7218 }, .{ 7219 .required_features = .{ .cmov, .slow_incdec, null, null }, 7220 .src_constraints = .{ 7221 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7222 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7223 }, 7224 .patterns = &.{ 7225 .{ .src = .{ .to_mem, .to_mem } }, 7226 }, 7227 .extra_temps = .{ 7228 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7229 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7230 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7231 .unused, 7232 .unused, 7233 .unused, 7234 .unused, 7235 .unused, 7236 .unused, 7237 }, 7238 .dst_temps = .{.mem}, 7239 .clobbers = .{ .eflags = true }, 7240 .each = .{ .once = &.{ 7241 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7242 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7243 .{ ._, ._, .movsx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7244 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 7245 .{ ._, ._ge, .cmov, .tmp1d, .tmp2d, ._, ._ }, 7246 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7247 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 7248 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7249 } }, 7250 }, .{ 7251 .required_features = .{ .cmov, null, null, null }, 7252 .src_constraints = .{ 7253 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7254 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7255 }, 7256 .patterns = &.{ 7257 .{ .src = .{ .to_mem, .to_mem } }, 7258 }, 7259 .extra_temps = .{ 7260 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7261 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7262 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7263 .unused, 7264 .unused, 7265 .unused, 7266 .unused, 7267 .unused, 7268 .unused, 7269 }, 7270 .dst_temps = .{.mem}, 7271 .clobbers = .{ .eflags = true }, 7272 .each = .{ .once = &.{ 7273 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7274 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7275 .{ ._, ._, .movsx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7276 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 7277 .{ ._, ._ge, .cmov, .tmp1d, .tmp2d, ._, ._ }, 7278 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7279 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 7280 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 7281 } }, 7282 }, .{ 7283 .required_features = .{ .slow_incdec, null, null, null }, 7284 .src_constraints = .{ 7285 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7286 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7287 }, 7288 .patterns = &.{ 7289 .{ .src = .{ .to_mem, .to_mem } }, 7290 }, 7291 .extra_temps = .{ 7292 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7293 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7294 .unused, 7295 .unused, 7296 .unused, 7297 .unused, 7298 .unused, 7299 .unused, 7300 .unused, 7301 }, 7302 .dst_temps = .{.mem}, 7303 .clobbers = .{ .eflags = true }, 7304 .each = .{ .once = &.{ 7305 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7306 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7307 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7308 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 7309 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7310 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7311 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 7312 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7313 } }, 7314 }, .{ 7315 .src_constraints = .{ 7316 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7317 .{ .multiple_scalar_signed_int = .{ .of = .byte, .is = .byte } }, 7318 }, 7319 .patterns = &.{ 7320 .{ .src = .{ .to_mem, .to_mem } }, 7321 }, 7322 .extra_temps = .{ 7323 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7324 .{ .type = .i8, .kind = .{ .rc = .general_purpose } }, 7325 .unused, 7326 .unused, 7327 .unused, 7328 .unused, 7329 .unused, 7330 .unused, 7331 .unused, 7332 }, 7333 .dst_temps = .{.mem}, 7334 .clobbers = .{ .eflags = true }, 7335 .each = .{ .once = &.{ 7336 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7337 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7338 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7339 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 7340 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7341 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7342 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 7343 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 7344 } }, 7345 }, .{ 7346 .required_features = .{ .sse, .mmx, null, null }, 7347 .src_constraints = .{ 7348 .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, 7349 .{ .scalar_unsigned_int = .{ .of = .qword, .is = .byte } }, 7350 }, 7351 .patterns = &.{ 7352 .{ .src = .{ .to_mut_mmx, .mem } }, 7353 .{ .src = .{ .mem, .to_mut_mmx }, .commute = .{ 0, 1 } }, 7354 .{ .src = .{ .to_mut_mmx, .to_mmx } }, 7355 }, 7356 .dst_temps = .{.{ .ref = .src0 }}, 7357 .each = .{ .once = &.{ 7358 .{ ._, .p_b, .minu, .dst0q, .src1q, ._, ._ }, 7359 } }, 7360 }, .{ 7361 .required_features = .{ .avx, null, null, null }, 7362 .src_constraints = .{ 7363 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7364 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7365 }, 7366 .patterns = &.{ 7367 .{ .src = .{ .to_sse, .mem } }, 7368 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7369 .{ .src = .{ .to_sse, .to_sse } }, 7370 }, 7371 .dst_temps = .{.{ .rc = .sse }}, 7372 .each = .{ .once = &.{ 7373 .{ ._, .vp_b, .minu, .dst0x, .src0x, .src1x, ._ }, 7374 } }, 7375 }, .{ 7376 .required_features = .{ .sse2, null, null, null }, 7377 .src_constraints = .{ 7378 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7379 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7380 }, 7381 .patterns = &.{ 7382 .{ .src = .{ .to_mut_sse, .mem } }, 7383 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7384 .{ .src = .{ .to_mut_sse, .to_sse } }, 7385 }, 7386 .dst_temps = .{.{ .ref = .src0 }}, 7387 .each = .{ .once = &.{ 7388 .{ ._, .p_b, .minu, .dst0x, .src1x, ._, ._ }, 7389 } }, 7390 }, .{ 7391 .required_features = .{ .avx2, null, null, null }, 7392 .src_constraints = .{ 7393 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 7394 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 7395 }, 7396 .patterns = &.{ 7397 .{ .src = .{ .to_sse, .mem } }, 7398 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7399 .{ .src = .{ .to_sse, .to_sse } }, 7400 }, 7401 .dst_temps = .{.{ .rc = .sse }}, 7402 .each = .{ .once = &.{ 7403 .{ ._, .vp_b, .minu, .dst0y, .src0y, .src1y, ._ }, 7404 } }, 7405 }, .{ 7406 .required_features = .{ .avx2, null, null, null }, 7407 .src_constraints = .{ 7408 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 7409 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .byte } }, 7410 }, 7411 .patterns = &.{ 7412 .{ .src = .{ .to_mem, .to_mem } }, 7413 }, 7414 .extra_temps = .{ 7415 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7416 .{ .type = .vector_32_u8, .kind = .{ .rc = .sse } }, 7417 .unused, 7418 .unused, 7419 .unused, 7420 .unused, 7421 .unused, 7422 .unused, 7423 .unused, 7424 }, 7425 .dst_temps = .{.mem}, 7426 .clobbers = .{ .eflags = true }, 7427 .each = .{ .once = &.{ 7428 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7429 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 7430 .{ ._, .vp_b, .minu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 7431 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 7432 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 7433 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7434 } }, 7435 }, .{ 7436 .required_features = .{ .avx, null, null, null }, 7437 .src_constraints = .{ 7438 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7439 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7440 }, 7441 .patterns = &.{ 7442 .{ .src = .{ .to_mem, .to_mem } }, 7443 }, 7444 .extra_temps = .{ 7445 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7446 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 7447 .unused, 7448 .unused, 7449 .unused, 7450 .unused, 7451 .unused, 7452 .unused, 7453 .unused, 7454 }, 7455 .dst_temps = .{.mem}, 7456 .clobbers = .{ .eflags = true }, 7457 .each = .{ .once = &.{ 7458 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7459 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7460 .{ ._, .vp_b, .minu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 7461 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7462 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7463 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7464 } }, 7465 }, .{ 7466 .required_features = .{ .sse2, null, null, null }, 7467 .src_constraints = .{ 7468 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7469 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .byte } }, 7470 }, 7471 .patterns = &.{ 7472 .{ .src = .{ .to_mem, .to_mem } }, 7473 }, 7474 .extra_temps = .{ 7475 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7476 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 7477 .unused, 7478 .unused, 7479 .unused, 7480 .unused, 7481 .unused, 7482 .unused, 7483 .unused, 7484 }, 7485 .dst_temps = .{.mem}, 7486 .clobbers = .{ .eflags = true }, 7487 .each = .{ .once = &.{ 7488 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7489 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7490 .{ ._, .p_b, .minu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 7491 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7492 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7493 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7494 } }, 7495 }, .{ 7496 .required_features = .{ .cmov, .slow_incdec, null, null }, 7497 .src_constraints = .{ 7498 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7499 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7500 }, 7501 .patterns = &.{ 7502 .{ .src = .{ .to_mem, .to_mem } }, 7503 }, 7504 .extra_temps = .{ 7505 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7506 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7507 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7508 .unused, 7509 .unused, 7510 .unused, 7511 .unused, 7512 .unused, 7513 .unused, 7514 }, 7515 .dst_temps = .{.mem}, 7516 .clobbers = .{ .eflags = true }, 7517 .each = .{ .once = &.{ 7518 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7519 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7520 .{ ._, ._, .movzx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7521 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 7522 .{ ._, ._ae, .cmov, .tmp1d, .tmp2d, ._, ._ }, 7523 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7524 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 7525 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7526 } }, 7527 }, .{ 7528 .required_features = .{ .cmov, null, null, null }, 7529 .src_constraints = .{ 7530 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7531 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7532 }, 7533 .patterns = &.{ 7534 .{ .src = .{ .to_mem, .to_mem } }, 7535 }, 7536 .extra_temps = .{ 7537 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7538 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7539 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7540 .unused, 7541 .unused, 7542 .unused, 7543 .unused, 7544 .unused, 7545 .unused, 7546 }, 7547 .dst_temps = .{.mem}, 7548 .clobbers = .{ .eflags = true }, 7549 .each = .{ .once = &.{ 7550 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7551 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7552 .{ ._, ._, .movzx, .tmp2d, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7553 .{ ._, ._, .cmp, .tmp1b, .tmp2b, ._, ._ }, 7554 .{ ._, ._ae, .cmov, .tmp1d, .tmp2d, ._, ._ }, 7555 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7556 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 7557 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 7558 } }, 7559 }, .{ 7560 .required_features = .{ .slow_incdec, null, null, null }, 7561 .src_constraints = .{ 7562 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7563 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7564 }, 7565 .patterns = &.{ 7566 .{ .src = .{ .to_mem, .to_mem } }, 7567 }, 7568 .extra_temps = .{ 7569 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7570 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7571 .unused, 7572 .unused, 7573 .unused, 7574 .unused, 7575 .unused, 7576 .unused, 7577 .unused, 7578 }, 7579 .dst_temps = .{.mem}, 7580 .clobbers = .{ .eflags = true }, 7581 .each = .{ .once = &.{ 7582 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7583 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7584 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7585 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 7586 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7587 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7588 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 7589 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7590 } }, 7591 }, .{ 7592 .src_constraints = .{ 7593 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7594 .{ .multiple_scalar_unsigned_int = .{ .of = .byte, .is = .byte } }, 7595 }, 7596 .patterns = &.{ 7597 .{ .src = .{ .to_mem, .to_mem } }, 7598 }, 7599 .extra_temps = .{ 7600 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7601 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 7602 .unused, 7603 .unused, 7604 .unused, 7605 .unused, 7606 .unused, 7607 .unused, 7608 .unused, 7609 }, 7610 .dst_temps = .{.mem}, 7611 .clobbers = .{ .eflags = true }, 7612 .each = .{ .once = &.{ 7613 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7614 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 7615 .{ ._, ._, .cmp, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7616 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 7617 .{ ._, ._, .mov, .tmp1b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 7618 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 7619 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 7620 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 7621 } }, 7622 }, .{ 7623 .required_features = .{ .sse, .mmx, null, null }, 7624 .src_constraints = .{ 7625 .{ .scalar_signed_int = .{ .of = .qword, .is = .word } }, 7626 .{ .scalar_signed_int = .{ .of = .qword, .is = .word } }, 7627 }, 7628 .patterns = &.{ 7629 .{ .src = .{ .to_mut_mmx, .mem } }, 7630 .{ .src = .{ .mem, .to_mut_mmx }, .commute = .{ 0, 1 } }, 7631 .{ .src = .{ .to_mut_mmx, .to_mmx } }, 7632 }, 7633 .dst_temps = .{.{ .ref = .src0 }}, 7634 .each = .{ .once = &.{ 7635 .{ ._, .p_w, .mins, .dst0q, .src1q, ._, ._ }, 7636 } }, 7637 }, .{ 7638 .required_features = .{ .avx, null, null, null }, 7639 .src_constraints = .{ 7640 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 7641 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 7642 }, 7643 .patterns = &.{ 7644 .{ .src = .{ .to_sse, .mem } }, 7645 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7646 .{ .src = .{ .to_sse, .to_sse } }, 7647 }, 7648 .dst_temps = .{.{ .rc = .sse }}, 7649 .each = .{ .once = &.{ 7650 .{ ._, .vp_w, .mins, .dst0x, .src0x, .src1x, ._ }, 7651 } }, 7652 }, .{ 7653 .required_features = .{ .sse2, null, null, null }, 7654 .src_constraints = .{ 7655 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 7656 .{ .scalar_signed_int = .{ .of = .xword, .is = .word } }, 7657 }, 7658 .patterns = &.{ 7659 .{ .src = .{ .to_mut_sse, .mem } }, 7660 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7661 .{ .src = .{ .to_mut_sse, .to_sse } }, 7662 }, 7663 .dst_temps = .{.{ .ref = .src0 }}, 7664 .each = .{ .once = &.{ 7665 .{ ._, .p_w, .mins, .dst0x, .src1x, ._, ._ }, 7666 } }, 7667 }, .{ 7668 .required_features = .{ .avx2, null, null, null }, 7669 .src_constraints = .{ 7670 .{ .scalar_signed_int = .{ .of = .yword, .is = .word } }, 7671 .{ .scalar_signed_int = .{ .of = .yword, .is = .word } }, 7672 }, 7673 .patterns = &.{ 7674 .{ .src = .{ .to_sse, .mem } }, 7675 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7676 .{ .src = .{ .to_sse, .to_sse } }, 7677 }, 7678 .dst_temps = .{.{ .rc = .sse }}, 7679 .each = .{ .once = &.{ 7680 .{ ._, .vp_w, .mins, .dst0y, .src0y, .src1y, ._ }, 7681 } }, 7682 }, .{ 7683 .required_features = .{ .avx2, null, null, null }, 7684 .src_constraints = .{ 7685 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .word } }, 7686 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .word } }, 7687 }, 7688 .patterns = &.{ 7689 .{ .src = .{ .to_mem, .to_mem } }, 7690 }, 7691 .extra_temps = .{ 7692 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7693 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 7694 .unused, 7695 .unused, 7696 .unused, 7697 .unused, 7698 .unused, 7699 .unused, 7700 .unused, 7701 }, 7702 .dst_temps = .{.mem}, 7703 .clobbers = .{ .eflags = true }, 7704 .each = .{ .once = &.{ 7705 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7706 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 7707 .{ ._, .vp_w, .mins, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 7708 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 7709 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 7710 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7711 } }, 7712 }, .{ 7713 .required_features = .{ .avx, null, null, null }, 7714 .src_constraints = .{ 7715 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 7716 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 7717 }, 7718 .patterns = &.{ 7719 .{ .src = .{ .to_mem, .to_mem } }, 7720 }, 7721 .extra_temps = .{ 7722 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7723 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 7724 .unused, 7725 .unused, 7726 .unused, 7727 .unused, 7728 .unused, 7729 .unused, 7730 .unused, 7731 }, 7732 .dst_temps = .{.mem}, 7733 .clobbers = .{ .eflags = true }, 7734 .each = .{ .once = &.{ 7735 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7736 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7737 .{ ._, .vp_w, .mins, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 7738 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7739 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7740 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7741 } }, 7742 }, .{ 7743 .required_features = .{ .sse2, null, null, null }, 7744 .src_constraints = .{ 7745 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 7746 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .word } }, 7747 }, 7748 .patterns = &.{ 7749 .{ .src = .{ .to_mem, .to_mem } }, 7750 }, 7751 .extra_temps = .{ 7752 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7753 .{ .type = .vector_16_i16, .kind = .{ .rc = .sse } }, 7754 .unused, 7755 .unused, 7756 .unused, 7757 .unused, 7758 .unused, 7759 .unused, 7760 .unused, 7761 }, 7762 .dst_temps = .{.mem}, 7763 .clobbers = .{ .eflags = true }, 7764 .each = .{ .once = &.{ 7765 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7766 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7767 .{ ._, .p_w, .mins, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 7768 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7769 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7770 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7771 } }, 7772 }, .{ 7773 .required_features = .{ .cmov, null, null, null }, 7774 .src_constraints = .{ 7775 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 7776 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 7777 }, 7778 .patterns = &.{ 7779 .{ .src = .{ .to_mem, .to_mem } }, 7780 }, 7781 .extra_temps = .{ 7782 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7783 .{ .type = .i16, .kind = .{ .rc = .general_purpose } }, 7784 .unused, 7785 .unused, 7786 .unused, 7787 .unused, 7788 .unused, 7789 .unused, 7790 .unused, 7791 }, 7792 .dst_temps = .{.mem}, 7793 .clobbers = .{ .eflags = true }, 7794 .each = .{ .once = &.{ 7795 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7796 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 7797 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 7798 .{ ._, ._ge, .cmov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 7799 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 7800 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 7801 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7802 } }, 7803 }, .{ 7804 .src_constraints = .{ 7805 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 7806 .{ .multiple_scalar_signed_int = .{ .of = .word, .is = .word } }, 7807 }, 7808 .patterns = &.{ 7809 .{ .src = .{ .to_mem, .to_mem } }, 7810 }, 7811 .extra_temps = .{ 7812 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7813 .{ .type = .i16, .kind = .{ .rc = .general_purpose } }, 7814 .unused, 7815 .unused, 7816 .unused, 7817 .unused, 7818 .unused, 7819 .unused, 7820 .unused, 7821 }, 7822 .dst_temps = .{.mem}, 7823 .clobbers = .{ .eflags = true }, 7824 .each = .{ .once = &.{ 7825 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7826 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 7827 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 7828 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 7829 .{ ._, ._, .mov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 7830 .{ .@"1:", ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 7831 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 7832 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7833 } }, 7834 }, .{ 7835 .required_features = .{ .avx, null, null, null }, 7836 .src_constraints = .{ 7837 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7838 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7839 }, 7840 .patterns = &.{ 7841 .{ .src = .{ .to_sse, .mem } }, 7842 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7843 .{ .src = .{ .to_sse, .to_sse } }, 7844 }, 7845 .dst_temps = .{.{ .rc = .sse }}, 7846 .each = .{ .once = &.{ 7847 .{ ._, .vp_w, .minu, .dst0x, .src0x, .src1x, ._ }, 7848 } }, 7849 }, .{ 7850 .required_features = .{ .sse4_1, null, null, null }, 7851 .src_constraints = .{ 7852 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7853 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7854 }, 7855 .patterns = &.{ 7856 .{ .src = .{ .to_mut_sse, .mem } }, 7857 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7858 .{ .src = .{ .to_mut_sse, .to_sse } }, 7859 }, 7860 .dst_temps = .{.{ .ref = .src0 }}, 7861 .each = .{ .once = &.{ 7862 .{ ._, .p_w, .minu, .dst0x, .src1x, ._, ._ }, 7863 } }, 7864 }, .{ 7865 .required_features = .{ .sse2, null, null, null }, 7866 .src_constraints = .{ 7867 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7868 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7869 }, 7870 .patterns = &.{ 7871 .{ .src = .{ .to_mut_sse, .mem } }, 7872 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 7873 .{ .src = .{ .to_mut_sse, .to_sse } }, 7874 }, 7875 .dst_temps = .{.{ .rc = .sse }}, 7876 .each = .{ .once = &.{ 7877 .{ ._, ._dqa, .mov, .dst0x, .src0x, ._, ._ }, 7878 .{ ._, .p_w, .subus, .src0x, .src1x, ._, ._ }, 7879 .{ ._, .p_w, .sub, .dst0x, .src0x, ._, ._ }, 7880 } }, 7881 }, .{ 7882 .required_features = .{ .avx2, null, null, null }, 7883 .src_constraints = .{ 7884 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 7885 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 7886 }, 7887 .patterns = &.{ 7888 .{ .src = .{ .to_sse, .mem } }, 7889 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 7890 .{ .src = .{ .to_sse, .to_sse } }, 7891 }, 7892 .dst_temps = .{.{ .rc = .sse }}, 7893 .each = .{ .once = &.{ 7894 .{ ._, .vp_w, .minu, .dst0y, .src0y, .src1y, ._ }, 7895 } }, 7896 }, .{ 7897 .required_features = .{ .avx2, null, null, null }, 7898 .src_constraints = .{ 7899 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 7900 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .word } }, 7901 }, 7902 .patterns = &.{ 7903 .{ .src = .{ .to_mem, .to_mem } }, 7904 }, 7905 .extra_temps = .{ 7906 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7907 .{ .type = .vector_16_u16, .kind = .{ .rc = .sse } }, 7908 .unused, 7909 .unused, 7910 .unused, 7911 .unused, 7912 .unused, 7913 .unused, 7914 .unused, 7915 }, 7916 .dst_temps = .{.mem}, 7917 .clobbers = .{ .eflags = true }, 7918 .each = .{ .once = &.{ 7919 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7920 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 7921 .{ ._, .vp_w, .minu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 7922 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 7923 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 7924 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7925 } }, 7926 }, .{ 7927 .required_features = .{ .avx, null, null, null }, 7928 .src_constraints = .{ 7929 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7930 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7931 }, 7932 .patterns = &.{ 7933 .{ .src = .{ .to_mem, .to_mem } }, 7934 }, 7935 .extra_temps = .{ 7936 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7937 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 7938 .unused, 7939 .unused, 7940 .unused, 7941 .unused, 7942 .unused, 7943 .unused, 7944 .unused, 7945 }, 7946 .dst_temps = .{.mem}, 7947 .clobbers = .{ .eflags = true }, 7948 .each = .{ .once = &.{ 7949 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7950 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7951 .{ ._, .vp_w, .minu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 7952 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7953 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7954 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7955 } }, 7956 }, .{ 7957 .required_features = .{ .sse4_1, null, null, null }, 7958 .src_constraints = .{ 7959 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7960 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7961 }, 7962 .patterns = &.{ 7963 .{ .src = .{ .to_mem, .to_mem } }, 7964 }, 7965 .extra_temps = .{ 7966 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7967 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 7968 .unused, 7969 .unused, 7970 .unused, 7971 .unused, 7972 .unused, 7973 .unused, 7974 .unused, 7975 }, 7976 .dst_temps = .{.mem}, 7977 .clobbers = .{ .eflags = true }, 7978 .each = .{ .once = &.{ 7979 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 7980 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 7981 .{ ._, .p_w, .minu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 7982 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 7983 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 7984 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 7985 } }, 7986 }, .{ 7987 .required_features = .{ .sse2, null, null, null }, 7988 .src_constraints = .{ 7989 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7990 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .word } }, 7991 }, 7992 .patterns = &.{ 7993 .{ .src = .{ .to_mem, .to_mem } }, 7994 }, 7995 .extra_temps = .{ 7996 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 7997 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 7998 .{ .type = .vector_8_u16, .kind = .{ .rc = .sse } }, 7999 .unused, 8000 .unused, 8001 .unused, 8002 .unused, 8003 .unused, 8004 .unused, 8005 }, 8006 .dst_temps = .{.mem}, 8007 .clobbers = .{ .eflags = true }, 8008 .each = .{ .once = &.{ 8009 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8010 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8011 .{ ._, ._dqa, .mov, .tmp2x, .tmp1x, ._, ._ }, 8012 .{ ._, .p_w, .subus, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8013 .{ ._, .p_w, .sub, .tmp2x, .tmp1x, ._, ._ }, 8014 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp2x, ._, ._ }, 8015 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8016 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8017 } }, 8018 }, .{ 8019 .required_features = .{ .cmov, null, null, null }, 8020 .src_constraints = .{ 8021 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 8022 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 8023 }, 8024 .patterns = &.{ 8025 .{ .src = .{ .to_mem, .to_mem } }, 8026 }, 8027 .extra_temps = .{ 8028 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8029 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 8030 .unused, 8031 .unused, 8032 .unused, 8033 .unused, 8034 .unused, 8035 .unused, 8036 .unused, 8037 }, 8038 .dst_temps = .{.mem}, 8039 .clobbers = .{ .eflags = true }, 8040 .each = .{ .once = &.{ 8041 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8042 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 8043 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 8044 .{ ._, ._ae, .cmov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 8045 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 8046 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 8047 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8048 } }, 8049 }, .{ 8050 .src_constraints = .{ 8051 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 8052 .{ .multiple_scalar_unsigned_int = .{ .of = .word, .is = .word } }, 8053 }, 8054 .patterns = &.{ 8055 .{ .src = .{ .to_mem, .to_mem } }, 8056 }, 8057 .extra_temps = .{ 8058 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8059 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 8060 .unused, 8061 .unused, 8062 .unused, 8063 .unused, 8064 .unused, 8065 .unused, 8066 .unused, 8067 }, 8068 .dst_temps = .{.mem}, 8069 .clobbers = .{ .eflags = true }, 8070 .each = .{ .once = &.{ 8071 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8072 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 8073 .{ ._, ._, .cmp, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 8074 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 8075 .{ ._, ._, .mov, .tmp1w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 8076 .{ .@"1:", ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 8077 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 8078 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8079 } }, 8080 }, .{ 8081 .required_features = .{ .avx, null, null, null }, 8082 .src_constraints = .{ 8083 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8084 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8085 }, 8086 .patterns = &.{ 8087 .{ .src = .{ .to_sse, .mem } }, 8088 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8089 .{ .src = .{ .to_sse, .to_sse } }, 8090 }, 8091 .dst_temps = .{.{ .rc = .sse }}, 8092 .each = .{ .once = &.{ 8093 .{ ._, .vp_d, .mins, .dst0x, .src0x, .src1x, ._ }, 8094 } }, 8095 }, .{ 8096 .required_features = .{ .sse4_1, null, null, null }, 8097 .src_constraints = .{ 8098 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8099 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8100 }, 8101 .patterns = &.{ 8102 .{ .src = .{ .to_mut_sse, .mem } }, 8103 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8104 .{ .src = .{ .to_mut_sse, .to_sse } }, 8105 }, 8106 .dst_temps = .{.{ .ref = .src0 }}, 8107 .each = .{ .once = &.{ 8108 .{ ._, .p_d, .mins, .dst0x, .src1x, ._, ._ }, 8109 } }, 8110 }, .{ 8111 .required_features = .{ .sse2, null, null, null }, 8112 .src_constraints = .{ 8113 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8114 .{ .scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8115 }, 8116 .patterns = &.{ 8117 .{ .src = .{ .to_mut_sse, .mem } }, 8118 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8119 .{ .src = .{ .to_mut_sse, .to_sse } }, 8120 }, 8121 .dst_temps = .{.{ .rc = .sse }}, 8122 .each = .{ .once = &.{ 8123 .{ ._, ._dqa, .mov, .dst0x, .src1x, ._, ._ }, 8124 .{ ._, .p_d, .cmpgt, .dst0x, .src0x, ._, ._ }, 8125 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 8126 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 8127 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 8128 } }, 8129 }, .{ 8130 .required_features = .{ .avx2, null, null, null }, 8131 .src_constraints = .{ 8132 .{ .scalar_signed_int = .{ .of = .yword, .is = .dword } }, 8133 .{ .scalar_signed_int = .{ .of = .yword, .is = .dword } }, 8134 }, 8135 .patterns = &.{ 8136 .{ .src = .{ .to_sse, .mem } }, 8137 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8138 .{ .src = .{ .to_sse, .to_sse } }, 8139 }, 8140 .dst_temps = .{.{ .rc = .sse }}, 8141 .each = .{ .once = &.{ 8142 .{ ._, .vp_d, .mins, .dst0y, .src0y, .src1y, ._ }, 8143 } }, 8144 }, .{ 8145 .required_features = .{ .avx2, null, null, null }, 8146 .src_constraints = .{ 8147 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .dword } }, 8148 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .dword } }, 8149 }, 8150 .patterns = &.{ 8151 .{ .src = .{ .to_mem, .to_mem } }, 8152 }, 8153 .extra_temps = .{ 8154 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8155 .{ .type = .vector_8_i32, .kind = .{ .rc = .sse } }, 8156 .unused, 8157 .unused, 8158 .unused, 8159 .unused, 8160 .unused, 8161 .unused, 8162 .unused, 8163 }, 8164 .dst_temps = .{.mem}, 8165 .clobbers = .{ .eflags = true }, 8166 .each = .{ .once = &.{ 8167 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8168 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 8169 .{ ._, .vp_d, .mins, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 8170 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 8171 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 8172 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8173 } }, 8174 }, .{ 8175 .required_features = .{ .avx, null, null, null }, 8176 .src_constraints = .{ 8177 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8178 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8179 }, 8180 .patterns = &.{ 8181 .{ .src = .{ .to_mem, .to_mem } }, 8182 }, 8183 .extra_temps = .{ 8184 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8185 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 8186 .unused, 8187 .unused, 8188 .unused, 8189 .unused, 8190 .unused, 8191 .unused, 8192 .unused, 8193 }, 8194 .dst_temps = .{.mem}, 8195 .clobbers = .{ .eflags = true }, 8196 .each = .{ .once = &.{ 8197 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8198 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8199 .{ ._, .vp_d, .mins, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 8200 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8201 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8202 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8203 } }, 8204 }, .{ 8205 .required_features = .{ .sse4_1, null, null, null }, 8206 .src_constraints = .{ 8207 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8208 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8209 }, 8210 .patterns = &.{ 8211 .{ .src = .{ .to_mem, .to_mem } }, 8212 }, 8213 .extra_temps = .{ 8214 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8215 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 8216 .unused, 8217 .unused, 8218 .unused, 8219 .unused, 8220 .unused, 8221 .unused, 8222 .unused, 8223 }, 8224 .dst_temps = .{.mem}, 8225 .clobbers = .{ .eflags = true }, 8226 .each = .{ .once = &.{ 8227 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8228 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8229 .{ ._, .p_d, .mins, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8230 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8231 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8232 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8233 } }, 8234 }, .{ 8235 .required_features = .{ .sse2, null, null, null }, 8236 .src_constraints = .{ 8237 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8238 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .dword } }, 8239 }, 8240 .patterns = &.{ 8241 .{ .src = .{ .to_mem, .to_mem } }, 8242 }, 8243 .extra_temps = .{ 8244 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8245 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 8246 .{ .type = .vector_4_i32, .kind = .{ .rc = .sse } }, 8247 .unused, 8248 .unused, 8249 .unused, 8250 .unused, 8251 .unused, 8252 .unused, 8253 }, 8254 .dst_temps = .{.mem}, 8255 .clobbers = .{ .eflags = true }, 8256 .each = .{ .once = &.{ 8257 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8258 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8259 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8260 .{ ._, .p_d, .cmpgt, .tmp1x, .tmp2x, ._, ._ }, 8261 .{ ._, .p_, .@"and", .tmp2x, .tmp1x, ._, ._ }, 8262 .{ ._, .p_, .andn, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8263 .{ ._, .p_, .@"or", .tmp1x, .tmp2x, ._, ._ }, 8264 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8265 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8266 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8267 } }, 8268 }, .{ 8269 .required_features = .{ .cmov, null, null, null }, 8270 .src_constraints = .{ 8271 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 8272 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 8273 }, 8274 .patterns = &.{ 8275 .{ .src = .{ .to_mem, .to_mem } }, 8276 }, 8277 .extra_temps = .{ 8278 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8279 .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, 8280 .unused, 8281 .unused, 8282 .unused, 8283 .unused, 8284 .unused, 8285 .unused, 8286 .unused, 8287 }, 8288 .dst_temps = .{.mem}, 8289 .clobbers = .{ .eflags = true }, 8290 .each = .{ .once = &.{ 8291 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8292 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 8293 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8294 .{ ._, ._ge, .cmov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8295 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 8296 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 8297 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8298 } }, 8299 }, .{ 8300 .src_constraints = .{ 8301 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 8302 .{ .multiple_scalar_signed_int = .{ .of = .dword, .is = .dword } }, 8303 }, 8304 .patterns = &.{ 8305 .{ .src = .{ .to_mem, .to_mem } }, 8306 }, 8307 .extra_temps = .{ 8308 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8309 .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, 8310 .unused, 8311 .unused, 8312 .unused, 8313 .unused, 8314 .unused, 8315 .unused, 8316 .unused, 8317 }, 8318 .dst_temps = .{.mem}, 8319 .clobbers = .{ .eflags = true }, 8320 .each = .{ .once = &.{ 8321 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8322 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 8323 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8324 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 8325 .{ ._, ._, .mov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8326 .{ .@"1:", ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 8327 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 8328 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8329 } }, 8330 }, .{ 8331 .required_features = .{ .avx, null, null, null }, 8332 .src_constraints = .{ 8333 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8334 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8335 }, 8336 .patterns = &.{ 8337 .{ .src = .{ .to_sse, .mem } }, 8338 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8339 .{ .src = .{ .to_sse, .to_sse } }, 8340 }, 8341 .dst_temps = .{.{ .rc = .sse }}, 8342 .each = .{ .once = &.{ 8343 .{ ._, .vp_d, .minu, .dst0x, .src0x, .src1x, ._ }, 8344 } }, 8345 }, .{ 8346 .required_features = .{ .sse4_1, null, null, null }, 8347 .src_constraints = .{ 8348 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8349 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8350 }, 8351 .patterns = &.{ 8352 .{ .src = .{ .to_mut_sse, .mem } }, 8353 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8354 .{ .src = .{ .to_mut_sse, .to_sse } }, 8355 }, 8356 .dst_temps = .{.{ .ref = .src0 }}, 8357 .each = .{ .once = &.{ 8358 .{ ._, .p_d, .minu, .dst0x, .src1x, ._, ._ }, 8359 } }, 8360 }, .{ 8361 .required_features = .{ .sse2, null, null, null }, 8362 .src_constraints = .{ 8363 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8364 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8365 }, 8366 .patterns = &.{ 8367 .{ .src = .{ .to_mut_sse, .mem } }, 8368 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8369 .{ .src = .{ .to_mut_sse, .to_sse } }, 8370 }, 8371 .extra_temps = .{ 8372 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 8373 .{ .type = .vector_4_u32, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 8374 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8375 .unused, 8376 .unused, 8377 .unused, 8378 .unused, 8379 .unused, 8380 .unused, 8381 }, 8382 .dst_temps = .{.{ .rc = .sse }}, 8383 .each = .{ .once = &.{ 8384 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8385 .{ ._, ._dqa, .mov, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 8386 .{ ._, ._dqa, .mov, .tmp2x, .dst0x, ._, ._ }, 8387 .{ ._, .p_, .xor, .dst0x, .src1x, ._, ._ }, 8388 .{ ._, .p_, .xor, .tmp2x, .src0x, ._, ._ }, 8389 .{ ._, .p_d, .cmpgt, .dst0x, .tmp2x, ._, ._ }, 8390 .{ ._, .p_, .@"and", .src0x, .dst0x, ._, ._ }, 8391 .{ ._, .p_, .andn, .dst0x, .src1x, ._, ._ }, 8392 .{ ._, .p_, .@"or", .dst0x, .src0x, ._, ._ }, 8393 } }, 8394 }, .{ 8395 .required_features = .{ .avx2, null, null, null }, 8396 .src_constraints = .{ 8397 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 8398 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 8399 }, 8400 .patterns = &.{ 8401 .{ .src = .{ .to_sse, .mem } }, 8402 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8403 .{ .src = .{ .to_sse, .to_sse } }, 8404 }, 8405 .dst_temps = .{.{ .rc = .sse }}, 8406 .each = .{ .once = &.{ 8407 .{ ._, .vp_d, .minu, .dst0y, .src0y, .src1y, ._ }, 8408 } }, 8409 }, .{ 8410 .required_features = .{ .avx2, null, null, null }, 8411 .src_constraints = .{ 8412 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 8413 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .dword } }, 8414 }, 8415 .patterns = &.{ 8416 .{ .src = .{ .to_mem, .to_mem } }, 8417 }, 8418 .extra_temps = .{ 8419 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8420 .{ .type = .vector_8_u32, .kind = .{ .rc = .sse } }, 8421 .unused, 8422 .unused, 8423 .unused, 8424 .unused, 8425 .unused, 8426 .unused, 8427 .unused, 8428 }, 8429 .dst_temps = .{.mem}, 8430 .clobbers = .{ .eflags = true }, 8431 .each = .{ .once = &.{ 8432 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8433 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 8434 .{ ._, .vp_d, .minu, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 8435 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 8436 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 8437 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8438 } }, 8439 }, .{ 8440 .required_features = .{ .avx, null, null, null }, 8441 .src_constraints = .{ 8442 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8443 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8444 }, 8445 .patterns = &.{ 8446 .{ .src = .{ .to_mem, .to_mem } }, 8447 }, 8448 .extra_temps = .{ 8449 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8450 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8451 .unused, 8452 .unused, 8453 .unused, 8454 .unused, 8455 .unused, 8456 .unused, 8457 .unused, 8458 }, 8459 .dst_temps = .{.mem}, 8460 .clobbers = .{ .eflags = true }, 8461 .each = .{ .once = &.{ 8462 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8463 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8464 .{ ._, .vp_d, .minu, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 8465 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8466 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8467 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8468 } }, 8469 }, .{ 8470 .required_features = .{ .sse4_1, null, null, null }, 8471 .src_constraints = .{ 8472 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8473 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8474 }, 8475 .patterns = &.{ 8476 .{ .src = .{ .to_mem, .to_mem } }, 8477 }, 8478 .extra_temps = .{ 8479 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8480 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8481 .unused, 8482 .unused, 8483 .unused, 8484 .unused, 8485 .unused, 8486 .unused, 8487 .unused, 8488 }, 8489 .dst_temps = .{.mem}, 8490 .clobbers = .{ .eflags = true }, 8491 .each = .{ .once = &.{ 8492 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8493 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8494 .{ ._, .p_d, .minu, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8495 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8496 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8497 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8498 } }, 8499 }, .{ 8500 .required_features = .{ .sse2, null, null, null }, 8501 .src_constraints = .{ 8502 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8503 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .dword } }, 8504 }, 8505 .patterns = &.{ 8506 .{ .src = .{ .to_mem, .to_mem } }, 8507 }, 8508 .extra_temps = .{ 8509 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8510 .{ .type = .vector_4_u32, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 8511 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8512 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8513 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8514 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8515 .{ .type = .vector_4_u32, .kind = .{ .rc = .sse } }, 8516 .unused, 8517 .unused, 8518 }, 8519 .dst_temps = .{.mem}, 8520 .clobbers = .{ .eflags = true }, 8521 .each = .{ .once = &.{ 8522 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8523 .{ ._, ._dqa, .mov, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 8524 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8525 .{ .@"0:", ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8526 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8527 .{ ._, ._dqa, .mov, .tmp5x, .tmp3x, ._, ._ }, 8528 .{ ._, ._dqa, .mov, .tmp6x, .tmp4x, ._, ._ }, 8529 .{ ._, .p_, .xor, .tmp5x, .tmp2x, ._, ._ }, 8530 .{ ._, .p_, .xor, .tmp6x, .tmp2x, ._, ._ }, 8531 .{ ._, .p_d, .cmpgt, .tmp5x, .tmp6x, ._, ._ }, 8532 .{ ._, .p_, .@"and", .tmp4x, .tmp5x, ._, ._ }, 8533 .{ ._, .p_, .andn, .tmp5x, .tmp3x, ._, ._ }, 8534 .{ ._, .p_, .@"or", .tmp4x, .tmp5x, ._, ._ }, 8535 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp4x, ._, ._ }, 8536 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8537 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8538 } }, 8539 }, .{ 8540 .required_features = .{ .cmov, null, null, null }, 8541 .src_constraints = .{ 8542 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 8543 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 8544 }, 8545 .patterns = &.{ 8546 .{ .src = .{ .to_mem, .to_mem } }, 8547 }, 8548 .extra_temps = .{ 8549 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8550 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 8551 .unused, 8552 .unused, 8553 .unused, 8554 .unused, 8555 .unused, 8556 .unused, 8557 .unused, 8558 }, 8559 .dst_temps = .{.mem}, 8560 .clobbers = .{ .eflags = true }, 8561 .each = .{ .once = &.{ 8562 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8563 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 8564 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8565 .{ ._, ._ae, .cmov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8566 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 8567 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 8568 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8569 } }, 8570 }, .{ 8571 .src_constraints = .{ 8572 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 8573 .{ .multiple_scalar_unsigned_int = .{ .of = .dword, .is = .dword } }, 8574 }, 8575 .patterns = &.{ 8576 .{ .src = .{ .to_mem, .to_mem } }, 8577 }, 8578 .extra_temps = .{ 8579 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8580 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 8581 .unused, 8582 .unused, 8583 .unused, 8584 .unused, 8585 .unused, 8586 .unused, 8587 .unused, 8588 }, 8589 .dst_temps = .{.mem}, 8590 .clobbers = .{ .eflags = true }, 8591 .each = .{ .once = &.{ 8592 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8593 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 8594 .{ ._, ._, .cmp, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8595 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 8596 .{ ._, ._, .mov, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 8597 .{ .@"1:", ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 8598 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 8599 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8600 } }, 8601 }, .{ 8602 .required_features = .{ .avx, null, null, null }, 8603 .src_constraints = .{ 8604 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8605 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8606 }, 8607 .patterns = &.{ 8608 .{ .src = .{ .to_sse, .mem } }, 8609 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8610 .{ .src = .{ .to_sse, .to_sse } }, 8611 }, 8612 .dst_temps = .{.{ .rc = .sse }}, 8613 .each = .{ .once = &.{ 8614 .{ ._, .vp_q, .cmpgt, .dst0x, .src0x, .src1x, ._ }, 8615 .{ ._, .vp_b, .blendv, .dst0x, .src0x, .src1x, .dst0x }, 8616 } }, 8617 }, .{ 8618 .required_features = .{ .sse4_2, null, null, null }, 8619 .src_constraints = .{ 8620 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8621 .{ .scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8622 }, 8623 .patterns = &.{ 8624 .{ .src = .{ .to_mut_sse, .mem } }, 8625 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8626 .{ .src = .{ .to_mut_sse, .to_sse } }, 8627 }, 8628 .extra_temps = .{ 8629 .{ .type = .vector_2_i64, .kind = .{ .reg = .xmm0 } }, 8630 .unused, 8631 .unused, 8632 .unused, 8633 .unused, 8634 .unused, 8635 .unused, 8636 .unused, 8637 .unused, 8638 }, 8639 .dst_temps = .{.{ .ref = .src0 }}, 8640 .each = .{ .once = &.{ 8641 .{ ._, ._dqa, .mov, .tmp0x, .src0x, ._, ._ }, 8642 .{ ._, .p_q, .cmpgt, .tmp0x, .src1x, ._, ._ }, 8643 .{ ._, .p_b, .blendv, .dst0x, .src1x, .tmp0x, ._ }, 8644 } }, 8645 }, .{ 8646 .required_features = .{ .avx2, null, null, null }, 8647 .src_constraints = .{ 8648 .{ .scalar_signed_int = .{ .of = .yword, .is = .qword } }, 8649 .{ .scalar_signed_int = .{ .of = .yword, .is = .qword } }, 8650 }, 8651 .patterns = &.{ 8652 .{ .src = .{ .to_sse, .mem } }, 8653 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8654 .{ .src = .{ .to_sse, .to_sse } }, 8655 }, 8656 .dst_temps = .{.{ .rc = .sse }}, 8657 .each = .{ .once = &.{ 8658 .{ ._, .vp_q, .cmpgt, .dst0y, .src0y, .src1y, ._ }, 8659 .{ ._, .vp_b, .blendv, .dst0y, .src0y, .src1y, .dst0y }, 8660 } }, 8661 }, .{ 8662 .required_features = .{ .avx2, null, null, null }, 8663 .src_constraints = .{ 8664 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .qword } }, 8665 .{ .multiple_scalar_signed_int = .{ .of = .yword, .is = .qword } }, 8666 }, 8667 .patterns = &.{ 8668 .{ .src = .{ .to_mem, .to_mem } }, 8669 }, 8670 .extra_temps = .{ 8671 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8672 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 8673 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 8674 .{ .type = .vector_4_i64, .kind = .{ .rc = .sse } }, 8675 .unused, 8676 .unused, 8677 .unused, 8678 .unused, 8679 .unused, 8680 }, 8681 .dst_temps = .{.mem}, 8682 .clobbers = .{ .eflags = true }, 8683 .each = .{ .once = &.{ 8684 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8685 .{ .@"0:", .v_dqa, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 8686 .{ ._, .v_dqa, .mov, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 8687 .{ ._, .vp_q, .cmpgt, .tmp3y, .tmp1y, .tmp2y, ._ }, 8688 .{ ._, .vp_b, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 8689 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 8690 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 8691 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8692 } }, 8693 }, .{ 8694 .required_features = .{ .avx, null, null, null }, 8695 .src_constraints = .{ 8696 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8697 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8698 }, 8699 .patterns = &.{ 8700 .{ .src = .{ .to_mem, .to_mem } }, 8701 }, 8702 .extra_temps = .{ 8703 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8704 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 8705 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 8706 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 8707 .unused, 8708 .unused, 8709 .unused, 8710 .unused, 8711 .unused, 8712 }, 8713 .dst_temps = .{.mem}, 8714 .clobbers = .{ .eflags = true }, 8715 .each = .{ .once = &.{ 8716 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8717 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8718 .{ ._, .v_dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8719 .{ ._, .vp_q, .cmpgt, .tmp3x, .tmp1x, .tmp2x, ._ }, 8720 .{ ._, .vp_b, .blendv, .tmp1x, .tmp1x, .tmp2x, .tmp3x }, 8721 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8722 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8723 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8724 } }, 8725 }, .{ 8726 .required_features = .{ .sse4_2, null, null, null }, 8727 .src_constraints = .{ 8728 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8729 .{ .multiple_scalar_signed_int = .{ .of = .xword, .is = .qword } }, 8730 }, 8731 .patterns = &.{ 8732 .{ .src = .{ .to_mem, .to_mem } }, 8733 }, 8734 .extra_temps = .{ 8735 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8736 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 8737 .{ .type = .vector_2_i64, .kind = .{ .rc = .sse } }, 8738 .{ .type = .vector_2_i64, .kind = .{ .reg = .xmm0 } }, 8739 .unused, 8740 .unused, 8741 .unused, 8742 .unused, 8743 .unused, 8744 }, 8745 .dst_temps = .{.mem}, 8746 .clobbers = .{ .eflags = true }, 8747 .each = .{ .once = &.{ 8748 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8749 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8750 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8751 .{ ._, ._dqa, .mov, .tmp3x, .tmp1x, ._, ._ }, 8752 .{ ._, .p_q, .cmpgt, .tmp3x, .tmp2x, ._, ._ }, 8753 .{ ._, .p_b, .blendv, .tmp1x, .tmp2x, .tmp3x, ._ }, 8754 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 8755 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8756 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8757 } }, 8758 }, .{ 8759 .required_features = .{ .@"64bit", .cmov, null, null }, 8760 .src_constraints = .{ 8761 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 8762 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 8763 }, 8764 .patterns = &.{ 8765 .{ .src = .{ .to_mem, .to_mem } }, 8766 }, 8767 .extra_temps = .{ 8768 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8769 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 8770 .unused, 8771 .unused, 8772 .unused, 8773 .unused, 8774 .unused, 8775 .unused, 8776 .unused, 8777 }, 8778 .dst_temps = .{.mem}, 8779 .clobbers = .{ .eflags = true }, 8780 .each = .{ .once = &.{ 8781 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8782 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 8783 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 8784 .{ ._, ._ge, .cmov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 8785 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 8786 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 8787 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8788 } }, 8789 }, .{ 8790 .required_features = .{ .@"64bit", null, null, null }, 8791 .src_constraints = .{ 8792 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 8793 .{ .multiple_scalar_signed_int = .{ .of = .qword, .is = .qword } }, 8794 }, 8795 .patterns = &.{ 8796 .{ .src = .{ .to_mem, .to_mem } }, 8797 }, 8798 .extra_temps = .{ 8799 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8800 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 8801 .unused, 8802 .unused, 8803 .unused, 8804 .unused, 8805 .unused, 8806 .unused, 8807 .unused, 8808 }, 8809 .dst_temps = .{.mem}, 8810 .clobbers = .{ .eflags = true }, 8811 .each = .{ .once = &.{ 8812 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8813 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 8814 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 8815 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 8816 .{ ._, ._, .mov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 8817 .{ .@"1:", ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 8818 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 8819 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8820 } }, 8821 }, .{ 8822 .required_features = .{ .avx, null, null, null }, 8823 .src_constraints = .{ 8824 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8825 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8826 }, 8827 .patterns = &.{ 8828 .{ .src = .{ .to_sse, .mem } }, 8829 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8830 .{ .src = .{ .to_sse, .to_sse } }, 8831 }, 8832 .extra_temps = .{ 8833 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 8834 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8835 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8836 .unused, 8837 .unused, 8838 .unused, 8839 .unused, 8840 .unused, 8841 .unused, 8842 }, 8843 .dst_temps = .{.{ .rc = .sse }}, 8844 .each = .{ .once = &.{ 8845 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8846 .{ ._, .v_, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 8847 .{ ._, .vp_, .xor, .dst0x, .tmp2x, .src0x, ._ }, 8848 .{ ._, .vp_, .xor, .tmp2x, .tmp2x, .src1x, ._ }, 8849 .{ ._, .vp_q, .cmpgt, .dst0x, .dst0x, .tmp2x, ._ }, 8850 .{ ._, .vp_b, .blendv, .dst0x, .src0x, .src1x, .dst0x }, 8851 } }, 8852 }, .{ 8853 .required_features = .{ .sse4_2, null, null, null }, 8854 .src_constraints = .{ 8855 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8856 .{ .scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8857 }, 8858 .patterns = &.{ 8859 .{ .src = .{ .to_mut_sse, .mem } }, 8860 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 8861 .{ .src = .{ .to_mut_sse, .to_sse } }, 8862 }, 8863 .extra_temps = .{ 8864 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 8865 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8866 .{ .type = .vector_2_u64, .kind = .{ .reg = .xmm0 } }, 8867 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8868 .unused, 8869 .unused, 8870 .unused, 8871 .unused, 8872 .unused, 8873 }, 8874 .dst_temps = .{.{ .ref = .src0 }}, 8875 .each = .{ .once = &.{ 8876 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8877 .{ ._, ._, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 8878 .{ ._, ._dqa, .mov, .tmp3x, .tmp2x, ._, ._ }, 8879 .{ ._, .p_, .xor, .tmp2x, .src0x, ._, ._ }, 8880 .{ ._, .p_, .xor, .tmp3x, .src1x, ._, ._ }, 8881 .{ ._, .p_q, .cmpgt, .tmp2x, .tmp3x, ._, ._ }, 8882 .{ ._, .p_b, .blendv, .dst0x, .src1x, .tmp2x, ._ }, 8883 } }, 8884 }, .{ 8885 .required_features = .{ .avx2, null, null, null }, 8886 .src_constraints = .{ 8887 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 8888 .{ .scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 8889 }, 8890 .patterns = &.{ 8891 .{ .src = .{ .to_sse, .mem } }, 8892 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 8893 .{ .src = .{ .to_sse, .to_sse } }, 8894 }, 8895 .extra_temps = .{ 8896 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 8897 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8898 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8899 .unused, 8900 .unused, 8901 .unused, 8902 .unused, 8903 .unused, 8904 .unused, 8905 }, 8906 .dst_temps = .{.{ .rc = .sse }}, 8907 .each = .{ .once = &.{ 8908 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8909 .{ ._, .vp_q, .broadcast, .tmp2y, .lea(.qword, .tmp0), ._, ._ }, 8910 .{ ._, .vp_, .xor, .dst0y, .tmp2y, .src0y, ._ }, 8911 .{ ._, .vp_, .xor, .tmp2y, .tmp2y, .src1y, ._ }, 8912 .{ ._, .vp_q, .cmpgt, .dst0y, .dst0y, .tmp2y, ._ }, 8913 .{ ._, .vp_b, .blendv, .dst0y, .src0y, .src1y, .dst0y }, 8914 } }, 8915 }, .{ 8916 .required_features = .{ .avx2, null, null, null }, 8917 .src_constraints = .{ 8918 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 8919 .{ .multiple_scalar_unsigned_int = .{ .of = .yword, .is = .qword } }, 8920 }, 8921 .patterns = &.{ 8922 .{ .src = .{ .to_mem, .to_mem } }, 8923 }, 8924 .extra_temps = .{ 8925 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8926 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8927 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8928 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8929 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8930 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8931 .{ .type = .vector_4_u64, .kind = .{ .rc = .sse } }, 8932 .unused, 8933 .unused, 8934 }, 8935 .dst_temps = .{.mem}, 8936 .clobbers = .{ .eflags = true }, 8937 .each = .{ .once = &.{ 8938 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8939 .{ ._, .vp_q, .broadcast, .tmp2y, .lea(.qword, .tmp0), ._, ._ }, 8940 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8941 .{ .@"0:", .v_dqa, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 8942 .{ ._, .v_dqa, .mov, .tmp4y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 8943 .{ ._, .vp_, .xor, .tmp5y, .tmp3y, .tmp2y, ._ }, 8944 .{ ._, .vp_, .xor, .tmp6y, .tmp4y, .tmp2y, ._ }, 8945 .{ ._, .vp_q, .cmpgt, .tmp5y, .tmp5y, .tmp6y, ._ }, 8946 .{ ._, .vp_b, .blendv, .tmp3y, .tmp3y, .tmp4y, .tmp5y }, 8947 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 8948 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 8949 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8950 } }, 8951 }, .{ 8952 .required_features = .{ .avx, null, null, null }, 8953 .src_constraints = .{ 8954 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8955 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8956 }, 8957 .patterns = &.{ 8958 .{ .src = .{ .to_mem, .to_mem } }, 8959 }, 8960 .extra_temps = .{ 8961 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8962 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8963 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8964 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8965 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8966 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8967 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 8968 .unused, 8969 .unused, 8970 }, 8971 .dst_temps = .{.mem}, 8972 .clobbers = .{ .eflags = true }, 8973 .each = .{ .once = &.{ 8974 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 8975 .{ ._, .v_, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 8976 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 8977 .{ .@"0:", .v_dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 8978 .{ ._, .v_dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 8979 .{ ._, .vp_, .xor, .tmp5x, .tmp3x, .tmp2x, ._ }, 8980 .{ ._, .vp_, .xor, .tmp6x, .tmp4x, .tmp2x, ._ }, 8981 .{ ._, .vp_q, .cmpgt, .tmp5x, .tmp5x, .tmp6x, ._ }, 8982 .{ ._, .vp_b, .blendv, .tmp3x, .tmp3x, .tmp4x, .tmp5x }, 8983 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 8984 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 8985 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 8986 } }, 8987 }, .{ 8988 .required_features = .{ .sse4_2, null, null, null }, 8989 .src_constraints = .{ 8990 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8991 .{ .multiple_scalar_unsigned_int = .{ .of = .xword, .is = .qword } }, 8992 }, 8993 .patterns = &.{ 8994 .{ .src = .{ .to_mem, .to_mem } }, 8995 }, 8996 .extra_temps = .{ 8997 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 8998 .{ .type = .u64, .kind = .{ .smin_mem = .{ .ref = .src0, .vectorize_to = .none } } }, 8999 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 9000 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 9001 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 9002 .{ .type = .vector_2_u64, .kind = .{ .reg = .xmm0 } }, 9003 .{ .type = .vector_2_u64, .kind = .{ .rc = .sse } }, 9004 .unused, 9005 .unused, 9006 }, 9007 .dst_temps = .{.mem}, 9008 .clobbers = .{ .eflags = true }, 9009 .each = .{ .once = &.{ 9010 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 9011 .{ ._, ._, .movddup, .tmp2x, .lea(.qword, .tmp0), ._, ._ }, 9012 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9013 .{ .@"0:", ._dqa, .mov, .tmp5x, .tmp2x, ._, ._ }, 9014 .{ ._, ._dqa, .mov, .tmp6x, .tmp2x, ._, ._ }, 9015 .{ ._, ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 9016 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 9017 .{ ._, .p_, .xor, .tmp5x, .tmp3x, ._, ._ }, 9018 .{ ._, .p_, .xor, .tmp6x, .tmp4x, ._, ._ }, 9019 .{ ._, .p_q, .cmpgt, .tmp5x, .tmp6x, ._, ._ }, 9020 .{ ._, .p_b, .blendv, .tmp3x, .tmp4x, .tmp5x, ._ }, 9021 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 9022 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 9023 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9024 } }, 9025 }, .{ 9026 .required_features = .{ .@"64bit", .cmov, null, null }, 9027 .src_constraints = .{ 9028 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 9029 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 9030 }, 9031 .patterns = &.{ 9032 .{ .src = .{ .to_mem, .to_mem } }, 9033 }, 9034 .extra_temps = .{ 9035 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9036 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 9037 .unused, 9038 .unused, 9039 .unused, 9040 .unused, 9041 .unused, 9042 .unused, 9043 .unused, 9044 }, 9045 .dst_temps = .{.mem}, 9046 .clobbers = .{ .eflags = true }, 9047 .each = .{ .once = &.{ 9048 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9049 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9050 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9051 .{ ._, ._ae, .cmov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9052 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 9053 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 9054 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9055 } }, 9056 }, .{ 9057 .required_features = .{ .@"64bit", null, null, null }, 9058 .src_constraints = .{ 9059 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 9060 .{ .multiple_scalar_unsigned_int = .{ .of = .qword, .is = .qword } }, 9061 }, 9062 .patterns = &.{ 9063 .{ .src = .{ .to_mem, .to_mem } }, 9064 }, 9065 .extra_temps = .{ 9066 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9067 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 9068 .unused, 9069 .unused, 9070 .unused, 9071 .unused, 9072 .unused, 9073 .unused, 9074 .unused, 9075 }, 9076 .dst_temps = .{.mem}, 9077 .clobbers = .{ .eflags = true }, 9078 .each = .{ .once = &.{ 9079 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9080 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9081 .{ ._, ._, .cmp, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9082 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 9083 .{ ._, ._, .mov, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9084 .{ .@"1:", ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 9085 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 9086 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9087 } }, 9088 }, .{ 9089 .required_features = .{ .@"64bit", .cmov, null, null }, 9090 .src_constraints = .{ .any_scalar_signed_int, .any_scalar_signed_int }, 9091 .patterns = &.{ 9092 .{ .src = .{ .to_mem, .to_mem } }, 9093 }, 9094 .extra_temps = .{ 9095 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9096 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 9097 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 9098 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 9099 .unused, 9100 .unused, 9101 .unused, 9102 .unused, 9103 .unused, 9104 }, 9105 .dst_temps = .{.mem}, 9106 .clobbers = .{ .eflags = true }, 9107 .each = .{ .once = &.{ 9108 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9109 .{ .@"0:", ._, .mov, .tmp1d, .sia(-1, .none, .add_src0_elem_size_div_8), ._, ._ }, 9110 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 9111 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9112 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9113 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 9114 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 9115 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 9116 .{ ._, ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9117 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9118 .{ ._, ._, .lea, .tmp1p, .memiad(.src0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9119 .{ ._, ._, .lea, .tmp2p, .memiad(.src1, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9120 .{ ._, ._ge, .cmov, .tmp1p, .tmp2p, ._, ._ }, 9121 .{ ._, ._, .lea, .tmp2p, .memiad(.dst0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9122 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9123 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 9124 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 9125 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9126 } }, 9127 }, .{ 9128 .required_features = .{ .@"64bit", null, null, null }, 9129 .src_constraints = .{ .any_scalar_signed_int, .any_scalar_signed_int }, 9130 .patterns = &.{ 9131 .{ .src = .{ .to_mem, .to_mem } }, 9132 }, 9133 .extra_temps = .{ 9134 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9135 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 9136 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 9137 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 9138 .unused, 9139 .unused, 9140 .unused, 9141 .unused, 9142 .unused, 9143 }, 9144 .dst_temps = .{.mem}, 9145 .clobbers = .{ .eflags = true }, 9146 .each = .{ .once = &.{ 9147 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9148 .{ .@"0:", ._, .mov, .tmp1d, .sia(-1, .none, .add_src0_elem_size_div_8), ._, ._ }, 9149 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 9150 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9151 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9152 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 9153 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 9154 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 9155 .{ ._, ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9156 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9157 .{ ._, ._, .lea, .tmp1p, .memiad(.src0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9158 .{ ._, ._nge, .j, .@"1f", ._, ._, ._ }, 9159 .{ ._, ._, .lea, .tmp1p, .memiad(.src1, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9160 .{ .@"1:", ._, .lea, .tmp2p, .memiad(.dst0, .tmp0, .add_size_sub_elem_size, 8), ._, ._ }, 9161 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9162 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 9163 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 9164 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9165 } }, 9166 }, .{ 9167 .required_features = .{ .@"64bit", .cmov, null, null }, 9168 .src_constraints = .{ .any_scalar_unsigned_int, .any_scalar_unsigned_int }, 9169 .patterns = &.{ 9170 .{ .src = .{ .to_mem, .to_mem } }, 9171 }, 9172 .extra_temps = .{ 9173 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9174 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 9175 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 9176 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 9177 .unused, 9178 .unused, 9179 .unused, 9180 .unused, 9181 .unused, 9182 }, 9183 .dst_temps = .{.mem}, 9184 .clobbers = .{ .eflags = true }, 9185 .each = .{ .once = &.{ 9186 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9187 .{ .@"0:", ._, .mov, .tmp1d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9188 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 9189 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9190 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9191 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 9192 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 9193 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 9194 .{ ._, ._, .lea, .tmp1p, .memia(.src0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9195 .{ ._, ._, .lea, .tmp2p, .memia(.src1, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9196 .{ ._, ._ae, .cmov, .tmp1p, .tmp2p, ._, ._ }, 9197 .{ ._, ._, .lea, .tmp2p, .memia(.dst0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9198 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9199 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 9200 .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, 9201 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 9202 } }, 9203 }, .{ 9204 .required_features = .{ .@"64bit", null, null, null }, 9205 .src_constraints = .{ .any_scalar_unsigned_int, .any_scalar_unsigned_int }, 9206 .patterns = &.{ 9207 .{ .src = .{ .to_mem, .to_mem } }, 9208 }, 9209 .extra_temps = .{ 9210 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9211 .{ .type = .isize, .kind = .{ .reg = .rsi } }, 9212 .{ .type = .u64, .kind = .{ .reg = .rdi } }, 9213 .{ .type = .u64, .kind = .{ .reg = .rcx } }, 9214 .unused, 9215 .unused, 9216 .unused, 9217 .unused, 9218 .unused, 9219 }, 9220 .dst_temps = .{.mem}, 9221 .clobbers = .{ .eflags = true }, 9222 .each = .{ .once = &.{ 9223 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9224 .{ .@"0:", ._, .mov, .tmp1d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9225 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 9226 .{ .@"1:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 9227 .{ ._, ._, .sbb, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 9228 .{ ._, ._, .lea, .tmp0p, .lead(.none, .tmp0, 8), ._, ._ }, 9229 .{ ._, ._c, .de, .tmp1d, ._, ._, ._ }, 9230 .{ ._, ._nz, .j, .@"1b", ._, ._, ._ }, 9231 .{ ._, ._, .lea, .tmp1p, .memia(.src0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9232 .{ ._, ._nae, .j, .@"1f", ._, ._, ._ }, 9233 .{ ._, ._, .lea, .tmp1p, .memia(.src1, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9234 .{ .@"1:", ._, .lea, .tmp2p, .memia(.dst0, .tmp0, .add_size_sub_elem_size), ._, ._ }, 9235 .{ ._, ._, .mov, .tmp3d, .sa(.none, .add_src0_elem_size_div_8), ._, ._ }, 9236 .{ ._, .@"rep _sq", .mov, ._, ._, ._, ._ }, 9237 .{ ._, ._, .@"test", .tmp0p, .tmp0p, ._, ._ }, 9238 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 9239 } }, 9240 }, .{ 9241 .required_features = .{ .f16c, null, null, null }, 9242 .src_constraints = .{ 9243 .{ .scalar_float = .{ .of = .word, .is = .word } }, 9244 .{ .scalar_float = .{ .of = .word, .is = .word } }, 9245 }, 9246 .patterns = &.{ 9247 .{ .src = .{ .to_sse, .to_sse } }, 9248 }, 9249 .extra_temps = .{ 9250 .{ .type = .f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 9251 .{ .type = .f16, .kind = .{ .rc = .sse } }, 9252 .unused, 9253 .unused, 9254 .unused, 9255 .unused, 9256 .unused, 9257 .unused, 9258 .unused, 9259 }, 9260 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9261 .each = .{ .once = &.{ 9262 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 9263 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 9264 .{ ._, .v_ss, .cmp, .tmp1x, .dst0x, .dst0x, .vp(.unord) }, 9265 .{ ._, .v_ss, .min, .dst0x, .tmp0x, .dst0x, ._ }, 9266 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .tmp0x, .tmp1x }, 9267 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0x, .rm(.{}), ._ }, 9268 } }, 9269 }, .{ 9270 .required_features = .{ .sse, null, null, null }, 9271 .src_constraints = .{ 9272 .{ .scalar_float = .{ .of = .word, .is = .word } }, 9273 .{ .scalar_float = .{ .of = .word, .is = .word } }, 9274 }, 9275 .patterns = &.{ 9276 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 9277 }, 9278 .call_frame = .{ .alignment = .@"16" }, 9279 .extra_temps = .{ 9280 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, 9281 .unused, 9282 .unused, 9283 .unused, 9284 .unused, 9285 .unused, 9286 .unused, 9287 .unused, 9288 .unused, 9289 }, 9290 .dst_temps = .{.{ .ref = .src0 }}, 9291 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9292 .each = .{ .once = &.{ 9293 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 9294 } }, 9295 }, .{ 9296 .required_features = .{ .f16c, null, null, null }, 9297 .src_constraints = .{ 9298 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 9299 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 9300 }, 9301 .patterns = &.{ 9302 .{ .src = .{ .mem, .mem } }, 9303 .{ .src = .{ .to_sse, .mem } }, 9304 .{ .src = .{ .mem, .to_sse } }, 9305 .{ .src = .{ .to_sse, .to_sse } }, 9306 }, 9307 .extra_temps = .{ 9308 .{ .type = .vector_4_f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 9309 .{ .type = .vector_4_f16, .kind = .{ .rc = .sse } }, 9310 .unused, 9311 .unused, 9312 .unused, 9313 .unused, 9314 .unused, 9315 .unused, 9316 .unused, 9317 }, 9318 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9319 .each = .{ .once = &.{ 9320 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 9321 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 9322 .{ ._, .v_ps, .cmp, .tmp1x, .dst0x, .dst0x, .vp(.unord) }, 9323 .{ ._, .v_ps, .min, .dst0x, .tmp0x, .dst0x, ._ }, 9324 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .tmp0x, .tmp1x }, 9325 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0x, .rm(.{}), ._ }, 9326 } }, 9327 }, .{ 9328 .required_features = .{ .f16c, null, null, null }, 9329 .src_constraints = .{ 9330 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 9331 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 9332 }, 9333 .patterns = &.{ 9334 .{ .src = .{ .mem, .mem } }, 9335 .{ .src = .{ .to_sse, .mem } }, 9336 .{ .src = .{ .mem, .to_sse } }, 9337 .{ .src = .{ .to_sse, .to_sse } }, 9338 }, 9339 .extra_temps = .{ 9340 .{ .type = .vector_8_f16, .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 9341 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 9342 .unused, 9343 .unused, 9344 .unused, 9345 .unused, 9346 .unused, 9347 .unused, 9348 .unused, 9349 }, 9350 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9351 .each = .{ .once = &.{ 9352 .{ ._, .v_ps, .cvtph2, .dst0y, .src0x, ._, ._ }, 9353 .{ ._, .v_ps, .cvtph2, .tmp0y, .src1x, ._, ._ }, 9354 .{ ._, .v_ps, .cmp, .tmp1y, .dst0y, .dst0y, .vp(.unord) }, 9355 .{ ._, .v_ps, .min, .dst0y, .tmp0y, .dst0y, ._ }, 9356 .{ ._, .v_ps, .blendv, .dst0y, .dst0y, .tmp0y, .tmp1y }, 9357 .{ ._, .v_, .cvtps2ph, .dst0q, .dst0y, .rm(.{}), ._ }, 9358 } }, 9359 }, .{ 9360 .required_features = .{ .f16c, null, null, null }, 9361 .src_constraints = .{ 9362 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 9363 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 9364 }, 9365 .patterns = &.{ 9366 .{ .src = .{ .to_mem, .to_mem } }, 9367 }, 9368 .extra_temps = .{ 9369 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9370 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 9371 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 9372 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 9373 .unused, 9374 .unused, 9375 .unused, 9376 .unused, 9377 .unused, 9378 }, 9379 .dst_temps = .{.mem}, 9380 .clobbers = .{ .eflags = true }, 9381 .each = .{ .once = &.{ 9382 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 9383 .{ .@"0:", .v_ps, .cvtph2, .tmp1y, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 9384 .{ ._, .v_ps, .cvtph2, .tmp2y, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 9385 .{ ._, .v_ps, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 9386 .{ ._, .v_ps, .min, .tmp1y, .tmp2y, .tmp1y, ._ }, 9387 .{ ._, .v_ps, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 9388 .{ ._, .v_, .cvtps2ph, .memia(.dst0x, .tmp0, .add_size), .tmp1y, .rm(.{}), ._ }, 9389 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 9390 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9391 } }, 9392 }, .{ 9393 .required_features = .{ .avx, null, null, null }, 9394 .src_constraints = .{ 9395 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9396 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9397 }, 9398 .patterns = &.{ 9399 .{ .src = .{ .to_mem, .to_mem } }, 9400 }, 9401 .call_frame = .{ .alignment = .@"16" }, 9402 .extra_temps = .{ 9403 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9404 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 9405 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 9406 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, 9407 .unused, 9408 .unused, 9409 .unused, 9410 .unused, 9411 .unused, 9412 }, 9413 .dst_temps = .{.mem}, 9414 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9415 .each = .{ .once = &.{ 9416 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9417 .{ .@"0:", .vp_, .xor, .tmp2x, .tmp2x, .tmp2x, ._ }, 9418 .{ ._, .vp_w, .insr, .tmp1x, .tmp2x, .memia(.src0w, .tmp0, .add_size), .ui(0) }, 9419 .{ ._, .vp_w, .insr, .tmp2x, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0) }, 9420 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 9421 .{ ._, .vp_w, .extr, .memia(.dst0w, .tmp0, .add_size), .tmp1x, .ui(0), ._ }, 9422 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 9423 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9424 } }, 9425 }, .{ 9426 .required_features = .{ .sse4_1, null, null, null }, 9427 .src_constraints = .{ 9428 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9429 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9430 }, 9431 .patterns = &.{ 9432 .{ .src = .{ .to_mem, .to_mem } }, 9433 }, 9434 .call_frame = .{ .alignment = .@"16" }, 9435 .extra_temps = .{ 9436 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9437 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 9438 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 9439 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, 9440 .unused, 9441 .unused, 9442 .unused, 9443 .unused, 9444 .unused, 9445 }, 9446 .dst_temps = .{.mem}, 9447 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9448 .each = .{ .once = &.{ 9449 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9450 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 9451 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 9452 .{ ._, .p_w, .insr, .tmp1x, .memia(.src0w, .tmp0, .add_size), .ui(0), ._ }, 9453 .{ ._, .p_w, .insr, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0), ._ }, 9454 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 9455 .{ ._, .p_w, .extr, .memia(.dst0w, .tmp0, .add_size), .tmp1x, .ui(0), ._ }, 9456 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 9457 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9458 } }, 9459 }, .{ 9460 .required_features = .{ .sse2, null, null, null }, 9461 .src_constraints = .{ 9462 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9463 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9464 }, 9465 .patterns = &.{ 9466 .{ .src = .{ .to_mem, .to_mem } }, 9467 }, 9468 .call_frame = .{ .alignment = .@"16" }, 9469 .extra_temps = .{ 9470 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9471 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 9472 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 9473 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, 9474 .{ .type = .f16, .kind = .{ .reg = .ax } }, 9475 .unused, 9476 .unused, 9477 .unused, 9478 .unused, 9479 }, 9480 .dst_temps = .{.mem}, 9481 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9482 .each = .{ .once = &.{ 9483 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9484 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 9485 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 9486 .{ ._, .p_w, .insr, .tmp1x, .memia(.src0w, .tmp0, .add_size), .ui(0), ._ }, 9487 .{ ._, .p_w, .insr, .tmp2x, .memia(.src1w, .tmp0, .add_size), .ui(0), ._ }, 9488 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 9489 .{ ._, .p_w, .extr, .tmp4d, .tmp1x, .ui(0), ._ }, 9490 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp4w, ._, ._ }, 9491 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 9492 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9493 } }, 9494 }, .{ 9495 .required_features = .{ .sse, null, null, null }, 9496 .src_constraints = .{ 9497 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9498 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 9499 }, 9500 .patterns = &.{ 9501 .{ .src = .{ .to_mem, .to_mem } }, 9502 }, 9503 .call_frame = .{ .alignment = .@"16" }, 9504 .extra_temps = .{ 9505 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9506 .{ .type = .f16, .kind = .{ .reg = .eax } }, 9507 .{ .type = .f32, .kind = .mem }, 9508 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 9509 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 9510 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__fminh" } } }, 9511 .unused, 9512 .unused, 9513 .unused, 9514 }, 9515 .dst_temps = .{.mem}, 9516 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9517 .each = .{ .once = &.{ 9518 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9519 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 9520 .{ ._, ._, .mov, .mem(.tmp2d), .tmp1d, ._, ._ }, 9521 .{ ._, ._ss, .mov, .tmp3x, .mem(.tmp2d), ._, ._ }, 9522 .{ ._, ._, .movzx, .tmp1d, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 9523 .{ ._, ._, .mov, .mem(.tmp2d), .tmp1d, ._, ._ }, 9524 .{ ._, ._ss, .mov, .tmp4x, .mem(.tmp2d), ._, ._ }, 9525 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 9526 .{ ._, ._ss, .mov, .mem(.tmp2d), .tmp3x, ._, ._ }, 9527 .{ ._, ._, .mov, .tmp1d, .mem(.tmp2d), ._, ._ }, 9528 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 9529 .{ ._, ._, .add, .tmp0q, .si(2), ._, ._ }, 9530 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9531 } }, 9532 }, .{ 9533 .required_features = .{ .avx, null, null, null }, 9534 .src_constraints = .{ 9535 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9536 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9537 }, 9538 .patterns = &.{ 9539 .{ .src = .{ .to_sse, .to_sse } }, 9540 }, 9541 .extra_temps = .{ 9542 .{ .type = .f32, .kind = .{ .rc = .sse } }, 9543 .unused, 9544 .unused, 9545 .unused, 9546 .unused, 9547 .unused, 9548 .unused, 9549 .unused, 9550 .unused, 9551 }, 9552 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9553 .each = .{ .once = &.{ 9554 .{ ._, .v_ss, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 9555 .{ ._, .v_ss, .min, .dst0x, .src1x, .src0x, ._ }, 9556 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 9557 } }, 9558 }, .{ 9559 .required_features = .{ .sse4_1, null, null, null }, 9560 .src_constraints = .{ 9561 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9562 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9563 }, 9564 .patterns = &.{ 9565 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 9566 }, 9567 .dst_temps = .{.{ .rc = .sse }}, 9568 .each = .{ .once = &.{ 9569 .{ ._, ._ps, .mova, .dst0x, .src1x, ._, ._ }, 9570 .{ ._, ._ss, .min, .dst0x, .src0x, ._, ._ }, 9571 .{ ._, ._ss, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 9572 .{ ._, ._ps, .blendv, .dst0x, .src1x, .src0x, ._ }, 9573 } }, 9574 }, .{ 9575 .required_features = .{ .sse, null, null, null }, 9576 .src_constraints = .{ 9577 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9578 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 9579 }, 9580 .patterns = &.{ 9581 .{ .src = .{ .to_mut_sse, .to_sse } }, 9582 }, 9583 .extra_temps = .{ 9584 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9585 .unused, 9586 .unused, 9587 .unused, 9588 .unused, 9589 .unused, 9590 .unused, 9591 .unused, 9592 .unused, 9593 }, 9594 .dst_temps = .{.{ .ref = .src0 }}, 9595 .each = .{ .once = &.{ 9596 .{ ._, ._ps, .mova, .tmp0x, .src1x, ._, ._ }, 9597 .{ ._, ._ss, .min, .tmp0x, .src0x, ._, ._ }, 9598 .{ ._, ._ss, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 9599 .{ ._, ._ps, .@"and", .tmp0x, .dst0x, ._, ._ }, 9600 .{ ._, ._ps, .andn, .dst0x, .src1x, ._, ._ }, 9601 .{ ._, ._ps, .@"or", .dst0x, .tmp0x, ._, ._ }, 9602 } }, 9603 }, .{ 9604 .required_features = .{ .avx, null, null, null }, 9605 .src_constraints = .{ 9606 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9607 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9608 }, 9609 .patterns = &.{ 9610 .{ .src = .{ .to_sse, .to_sse } }, 9611 }, 9612 .extra_temps = .{ 9613 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9614 .unused, 9615 .unused, 9616 .unused, 9617 .unused, 9618 .unused, 9619 .unused, 9620 .unused, 9621 .unused, 9622 }, 9623 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9624 .each = .{ .once = &.{ 9625 .{ ._, .v_ps, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 9626 .{ ._, .v_ps, .min, .dst0x, .src1x, .src0x, ._ }, 9627 .{ ._, .v_ps, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 9628 } }, 9629 }, .{ 9630 .required_features = .{ .sse4_1, null, null, null }, 9631 .src_constraints = .{ 9632 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9633 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9634 }, 9635 .patterns = &.{ 9636 .{ .src = .{ .{ .to_reg = .xmm0 }, .mem } }, 9637 .{ .src = .{ .mem, .{ .to_reg = .xmm0 } }, .commute = .{ 0, 1 } }, 9638 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 9639 }, 9640 .dst_temps = .{.{ .rc = .sse }}, 9641 .each = .{ .once = &.{ 9642 .{ ._, ._ps, .mova, .dst0x, .src1x, ._, ._ }, 9643 .{ ._, ._ps, .min, .dst0x, .src0x, ._, ._ }, 9644 .{ ._, ._ps, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 9645 .{ ._, ._ps, .blendv, .dst0x, .src1x, .src0x, ._ }, 9646 } }, 9647 }, .{ 9648 .required_features = .{ .sse, null, null, null }, 9649 .src_constraints = .{ 9650 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9651 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 9652 }, 9653 .patterns = &.{ 9654 .{ .src = .{ .to_mut_sse, .mem } }, 9655 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 9656 .{ .src = .{ .to_mut_sse, .to_sse } }, 9657 }, 9658 .extra_temps = .{ 9659 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9660 .unused, 9661 .unused, 9662 .unused, 9663 .unused, 9664 .unused, 9665 .unused, 9666 .unused, 9667 .unused, 9668 }, 9669 .dst_temps = .{.{ .ref = .src0 }}, 9670 .each = .{ .once = &.{ 9671 .{ ._, ._ps, .mova, .tmp0x, .src1x, ._, ._ }, 9672 .{ ._, ._ps, .min, .tmp0x, .src0x, ._, ._ }, 9673 .{ ._, ._ps, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 9674 .{ ._, ._ps, .@"and", .tmp0x, .dst0x, ._, ._ }, 9675 .{ ._, ._ps, .andn, .dst0x, .src1x, ._, ._ }, 9676 .{ ._, ._ps, .@"or", .dst0x, .tmp0x, ._, ._ }, 9677 } }, 9678 }, .{ 9679 .required_features = .{ .avx, null, null, null }, 9680 .src_constraints = .{ 9681 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 9682 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 9683 }, 9684 .patterns = &.{ 9685 .{ .src = .{ .to_sse, .to_sse } }, 9686 }, 9687 .extra_temps = .{ 9688 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 9689 .unused, 9690 .unused, 9691 .unused, 9692 .unused, 9693 .unused, 9694 .unused, 9695 .unused, 9696 .unused, 9697 }, 9698 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9699 .each = .{ .once = &.{ 9700 .{ ._, .v_ps, .cmp, .tmp0y, .src0y, .src0y, .vp(.unord) }, 9701 .{ ._, .v_ps, .min, .dst0y, .src1y, .src0y, ._ }, 9702 .{ ._, .v_ps, .blendv, .dst0y, .dst0y, .src1y, .tmp0y }, 9703 } }, 9704 }, .{ 9705 .required_features = .{ .avx, null, null, null }, 9706 .src_constraints = .{ 9707 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 9708 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 9709 }, 9710 .patterns = &.{ 9711 .{ .src = .{ .to_mem, .to_mem } }, 9712 }, 9713 .extra_temps = .{ 9714 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9715 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 9716 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 9717 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 9718 .unused, 9719 .unused, 9720 .unused, 9721 .unused, 9722 .unused, 9723 }, 9724 .dst_temps = .{.mem}, 9725 .clobbers = .{ .eflags = true }, 9726 .each = .{ .once = &.{ 9727 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9728 .{ .@"0:", .v_ps, .mova, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 9729 .{ ._, .v_ps, .mova, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 9730 .{ ._, .v_ps, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 9731 .{ ._, .v_ps, .min, .tmp1y, .tmp2y, .tmp1y, ._ }, 9732 .{ ._, .v_ps, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 9733 .{ ._, .v_ps, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 9734 .{ ._, ._, .add, .tmp0q, .si(32), ._, ._ }, 9735 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9736 } }, 9737 }, .{ 9738 .required_features = .{ .sse4_1, null, null, null }, 9739 .src_constraints = .{ 9740 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 9741 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 9742 }, 9743 .patterns = &.{ 9744 .{ .src = .{ .to_mem, .to_mem } }, 9745 }, 9746 .extra_temps = .{ 9747 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9748 .{ .type = .vector_4_f32, .kind = .{ .reg = .xmm0 } }, 9749 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9750 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9751 .unused, 9752 .unused, 9753 .unused, 9754 .unused, 9755 .unused, 9756 }, 9757 .dst_temps = .{.mem}, 9758 .clobbers = .{ .eflags = true }, 9759 .each = .{ .once = &.{ 9760 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9761 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 9762 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 9763 .{ ._, ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 9764 .{ ._, ._ps, .min, .tmp3x, .tmp1x, ._, ._ }, 9765 .{ ._, ._ps, .cmp, .tmp1x, .tmp1x, .vp(.unord), ._ }, 9766 .{ ._, ._ps, .blendv, .tmp3x, .tmp2x, .tmp1x, ._ }, 9767 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 9768 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 9769 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9770 } }, 9771 }, .{ 9772 .required_features = .{ .sse, null, null, null }, 9773 .src_constraints = .{ 9774 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 9775 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 9776 }, 9777 .patterns = &.{ 9778 .{ .src = .{ .to_mem, .to_mem } }, 9779 }, 9780 .extra_temps = .{ 9781 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 9782 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9783 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9784 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 9785 .unused, 9786 .unused, 9787 .unused, 9788 .unused, 9789 .unused, 9790 }, 9791 .dst_temps = .{.mem}, 9792 .clobbers = .{ .eflags = true }, 9793 .each = .{ .once = &.{ 9794 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 9795 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 9796 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 9797 .{ ._, ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 9798 .{ ._, ._ps, .min, .tmp3x, .tmp1x, ._, ._ }, 9799 .{ ._, ._ps, .cmp, .tmp1x, .tmp1x, .vp(.ord), ._ }, 9800 .{ ._, ._ps, .@"and", .tmp3x, .tmp1x, ._, ._ }, 9801 .{ ._, ._ps, .andn, .tmp1x, .tmp2x, ._, ._ }, 9802 .{ ._, ._ps, .@"or", .tmp1x, .tmp3x, ._, ._ }, 9803 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 9804 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 9805 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 9806 } }, 9807 }, .{ 9808 .required_features = .{ .avx, null, null, null }, 9809 .src_constraints = .{ 9810 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9811 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9812 }, 9813 .patterns = &.{ 9814 .{ .src = .{ .to_sse, .to_sse } }, 9815 }, 9816 .extra_temps = .{ 9817 .{ .type = .f64, .kind = .{ .rc = .sse } }, 9818 .unused, 9819 .unused, 9820 .unused, 9821 .unused, 9822 .unused, 9823 .unused, 9824 .unused, 9825 .unused, 9826 }, 9827 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9828 .each = .{ .once = &.{ 9829 .{ ._, .v_sd, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 9830 .{ ._, .v_sd, .min, .dst0x, .src1x, .src0x, ._ }, 9831 .{ ._, .v_pd, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 9832 } }, 9833 }, .{ 9834 .required_features = .{ .sse4_1, null, null, null }, 9835 .src_constraints = .{ 9836 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9837 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9838 }, 9839 .patterns = &.{ 9840 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 9841 }, 9842 .dst_temps = .{.{ .rc = .sse }}, 9843 .each = .{ .once = &.{ 9844 .{ ._, ._pd, .mova, .dst0x, .src1x, ._, ._ }, 9845 .{ ._, ._sd, .min, .dst0x, .src0x, ._, ._ }, 9846 .{ ._, ._sd, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 9847 .{ ._, ._pd, .blendv, .dst0x, .src1x, .src0x, ._ }, 9848 } }, 9849 }, .{ 9850 .required_features = .{ .sse2, null, null, null }, 9851 .src_constraints = .{ 9852 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9853 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9854 }, 9855 .patterns = &.{ 9856 .{ .src = .{ .to_mut_sse, .to_sse } }, 9857 }, 9858 .extra_temps = .{ 9859 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 9860 .unused, 9861 .unused, 9862 .unused, 9863 .unused, 9864 .unused, 9865 .unused, 9866 .unused, 9867 .unused, 9868 }, 9869 .dst_temps = .{.{ .ref = .src0 }}, 9870 .each = .{ .once = &.{ 9871 .{ ._, ._pd, .mova, .tmp0x, .src1x, ._, ._ }, 9872 .{ ._, ._sd, .min, .tmp0x, .src0x, ._, ._ }, 9873 .{ ._, ._sd, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 9874 .{ ._, ._pd, .@"and", .tmp0x, .dst0x, ._, ._ }, 9875 .{ ._, ._pd, .andn, .dst0x, .src1x, ._, ._ }, 9876 .{ ._, ._pd, .@"or", .dst0x, .tmp0x, ._, ._ }, 9877 } }, 9878 }, .{ 9879 .required_features = .{ .sse, null, null, null }, 9880 .src_constraints = .{ 9881 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9882 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 9883 }, 9884 .patterns = &.{ 9885 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 9886 }, 9887 .call_frame = .{ .alignment = .@"16" }, 9888 .extra_temps = .{ 9889 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmin" } } }, 9890 .unused, 9891 .unused, 9892 .unused, 9893 .unused, 9894 .unused, 9895 .unused, 9896 .unused, 9897 .unused, 9898 }, 9899 .dst_temps = .{.{ .ref = .src0 }}, 9900 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 9901 .each = .{ .once = &.{ 9902 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 9903 } }, 9904 }, .{ 9905 .required_features = .{ .avx, null, null, null }, 9906 .src_constraints = .{ 9907 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9908 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9909 }, 9910 .patterns = &.{ 9911 .{ .src = .{ .to_sse, .to_sse } }, 9912 }, 9913 .extra_temps = .{ 9914 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 9915 .unused, 9916 .unused, 9917 .unused, 9918 .unused, 9919 .unused, 9920 .unused, 9921 .unused, 9922 .unused, 9923 }, 9924 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 9925 .each = .{ .once = &.{ 9926 .{ ._, .v_pd, .cmp, .tmp0x, .src0x, .src0x, .vp(.unord) }, 9927 .{ ._, .v_pd, .min, .dst0x, .src1x, .src0x, ._ }, 9928 .{ ._, .v_pd, .blendv, .dst0x, .dst0x, .src1x, .tmp0x }, 9929 } }, 9930 }, .{ 9931 .required_features = .{ .sse4_1, null, null, null }, 9932 .src_constraints = .{ 9933 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9934 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9935 }, 9936 .patterns = &.{ 9937 .{ .src = .{ .{ .to_reg = .xmm0 }, .mem } }, 9938 .{ .src = .{ .mem, .{ .to_reg = .xmm0 } }, .commute = .{ 0, 1 } }, 9939 .{ .src = .{ .{ .to_reg = .xmm0 }, .to_sse } }, 9940 }, 9941 .dst_temps = .{.{ .rc = .sse }}, 9942 .each = .{ .once = &.{ 9943 .{ ._, ._pd, .mova, .dst0x, .src1x, ._, ._ }, 9944 .{ ._, ._pd, .min, .dst0x, .src0x, ._, ._ }, 9945 .{ ._, ._pd, .cmp, .src0x, .src0x, .vp(.unord), ._ }, 9946 .{ ._, ._pd, .blendv, .dst0x, .src1x, .src0x, ._ }, 9947 } }, 9948 }, .{ 9949 .required_features = .{ .sse2, null, null, null }, 9950 .src_constraints = .{ 9951 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9952 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 9953 }, 9954 .patterns = &.{ 9955 .{ .src = .{ .to_mut_sse, .mem } }, 9956 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 9957 .{ .src = .{ .to_mut_sse, .to_sse } }, 9958 }, 9959 .extra_temps = .{ 9960 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 9961 .unused, 9962 .unused, 9963 .unused, 9964 .unused, 9965 .unused, 9966 .unused, 9967 .unused, 9968 .unused, 9969 }, 9970 .dst_temps = .{.{ .ref = .src0 }}, 9971 .each = .{ .once = &.{ 9972 .{ ._, ._pd, .mova, .tmp0x, .src1x, ._, ._ }, 9973 .{ ._, ._pd, .min, .tmp0x, .src0x, ._, ._ }, 9974 .{ ._, ._pd, .cmp, .dst0x, .src0x, .vp(.ord), ._ }, 9975 .{ ._, ._pd, .@"and", .tmp0x, .dst0x, ._, ._ }, 9976 .{ ._, ._pd, .andn, .dst0x, .src1x, ._, ._ }, 9977 .{ ._, ._pd, .@"or", .dst0x, .tmp0x, ._, ._ }, 9978 } }, 9979 }, .{ 9980 .required_features = .{ .avx, null, null, null }, 9981 .src_constraints = .{ 9982 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 9983 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 9984 }, 9985 .patterns = &.{ 9986 .{ .src = .{ .to_sse, .to_sse } }, 9987 }, 9988 .extra_temps = .{ 9989 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 9990 .unused, 9991 .unused, 9992 .unused, 9993 .unused, 9994 .unused, 9995 .unused, 9996 .unused, 9997 .unused, 9998 }, 9999 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 10000 .each = .{ .once = &.{ 10001 .{ ._, .v_pd, .cmp, .tmp0y, .src0y, .src0y, .vp(.unord) }, 10002 .{ ._, .v_pd, .min, .dst0y, .src1y, .src0y, ._ }, 10003 .{ ._, .v_pd, .blendv, .dst0y, .dst0y, .src1y, .tmp0y }, 10004 } }, 10005 }, .{ 10006 .required_features = .{ .avx, null, null, null }, 10007 .src_constraints = .{ 10008 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 10009 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 10010 }, 10011 .patterns = &.{ 10012 .{ .src = .{ .to_mem, .to_mem } }, 10013 }, 10014 .extra_temps = .{ 10015 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10016 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 10017 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 10018 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 10019 .unused, 10020 .unused, 10021 .unused, 10022 .unused, 10023 .unused, 10024 }, 10025 .dst_temps = .{.mem}, 10026 .clobbers = .{ .eflags = true }, 10027 .each = .{ .once = &.{ 10028 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10029 .{ .@"0:", .v_pd, .mova, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 10030 .{ ._, .v_pd, .mova, .tmp2y, .memia(.src1y, .tmp0, .add_size), ._, ._ }, 10031 .{ ._, .v_pd, .cmp, .tmp3y, .tmp1y, .tmp1y, .vp(.unord) }, 10032 .{ ._, .v_pd, .min, .tmp1y, .tmp2y, .tmp1y, ._ }, 10033 .{ ._, .v_pd, .blendv, .tmp1y, .tmp1y, .tmp2y, .tmp3y }, 10034 .{ ._, .v_pd, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 10035 .{ ._, ._, .add, .tmp0q, .si(32), ._, ._ }, 10036 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10037 } }, 10038 }, .{ 10039 .required_features = .{ .sse4_1, null, null, null }, 10040 .src_constraints = .{ 10041 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 10042 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 10043 }, 10044 .patterns = &.{ 10045 .{ .src = .{ .to_mem, .to_mem } }, 10046 }, 10047 .extra_temps = .{ 10048 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10049 .{ .type = .vector_2_f64, .kind = .{ .reg = .xmm0 } }, 10050 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 10051 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 10052 .unused, 10053 .unused, 10054 .unused, 10055 .unused, 10056 .unused, 10057 }, 10058 .dst_temps = .{.mem}, 10059 .clobbers = .{ .eflags = true }, 10060 .each = .{ .once = &.{ 10061 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10062 .{ .@"0:", ._pd, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10063 .{ ._, ._pd, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10064 .{ ._, ._pd, .mova, .tmp3x, .tmp2x, ._, ._ }, 10065 .{ ._, ._pd, .min, .tmp3x, .tmp1x, ._, ._ }, 10066 .{ ._, ._pd, .cmp, .tmp1x, .tmp1x, .vp(.unord), ._ }, 10067 .{ ._, ._pd, .blendv, .tmp3x, .tmp2x, .tmp1x, ._ }, 10068 .{ ._, ._pd, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 10069 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 10070 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10071 } }, 10072 }, .{ 10073 .required_features = .{ .sse2, null, null, null }, 10074 .src_constraints = .{ 10075 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 10076 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 10077 }, 10078 .patterns = &.{ 10079 .{ .src = .{ .to_mem, .to_mem } }, 10080 }, 10081 .extra_temps = .{ 10082 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10083 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 10084 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 10085 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 10086 .unused, 10087 .unused, 10088 .unused, 10089 .unused, 10090 .unused, 10091 }, 10092 .dst_temps = .{.mem}, 10093 .clobbers = .{ .eflags = true }, 10094 .each = .{ .once = &.{ 10095 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10096 .{ .@"0:", ._pd, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10097 .{ ._, ._pd, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10098 .{ ._, ._pd, .mova, .tmp3x, .tmp2x, ._, ._ }, 10099 .{ ._, ._pd, .min, .tmp3x, .tmp1x, ._, ._ }, 10100 .{ ._, ._pd, .cmp, .tmp1x, .tmp1x, .vp(.ord), ._ }, 10101 .{ ._, ._pd, .@"and", .tmp3x, .tmp1x, ._, ._ }, 10102 .{ ._, ._pd, .andn, .tmp1x, .tmp2x, ._, ._ }, 10103 .{ ._, ._pd, .@"or", .tmp1x, .tmp3x, ._, ._ }, 10104 .{ ._, ._pd, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10105 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 10106 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10107 } }, 10108 }, .{ 10109 .required_features = .{ .sse, null, null, null }, 10110 .src_constraints = .{ 10111 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 10112 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 10113 }, 10114 .patterns = &.{ 10115 .{ .src = .{ .to_mem, .to_mem } }, 10116 }, 10117 .call_frame = .{ .alignment = .@"16" }, 10118 .extra_temps = .{ 10119 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10120 .{ .type = .f64, .kind = .{ .reg = .xmm0 } }, 10121 .{ .type = .f64, .kind = .{ .reg = .xmm1 } }, 10122 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fmin" } } }, 10123 .unused, 10124 .unused, 10125 .unused, 10126 .unused, 10127 .unused, 10128 }, 10129 .dst_temps = .{.mem}, 10130 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 10131 .each = .{ .once = &.{ 10132 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10133 .{ .@"0:", ._ps, .xor, .tmp1x, .tmp1x, ._, ._ }, 10134 .{ ._, ._ps, .xor, .tmp2x, .tmp2x, ._, ._ }, 10135 .{ ._, ._ps, .movl, .tmp1x, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 10136 .{ ._, ._ps, .movl, .tmp2x, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 10137 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 10138 .{ ._, ._ps, .movl, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 10139 .{ ._, ._, .add, .tmp0q, .si(8), ._, ._ }, 10140 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10141 } }, 10142 }, .{ 10143 .required_features = .{ .x87, .cmov, null, null }, 10144 .src_constraints = .{ 10145 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10146 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10147 }, 10148 .patterns = &.{ 10149 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 10150 .{ .src = .{ .mem, .to_x87 } }, 10151 .{ .src = .{ .to_x87, .to_x87 } }, 10152 }, 10153 .extra_temps = .{ 10154 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10155 .unused, 10156 .unused, 10157 .unused, 10158 .unused, 10159 .unused, 10160 .unused, 10161 .unused, 10162 .unused, 10163 }, 10164 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 10165 .clobbers = .{ .eflags = true }, 10166 .each = .{ .once = &.{ 10167 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 10168 .{ ._, .f_, .ucomi, .tmp0t, .tmp0t, ._, ._ }, 10169 .{ ._, .f_u, .cmov, .tmp0t, .src1t, ._, ._ }, 10170 .{ ._, .f_, .ucomi, .tmp0t, .src1t, ._, ._ }, 10171 .{ ._, .f_nb, .cmov, .tmp0t, .src1t, ._, ._ }, 10172 .{ ._, .f_p, .st, .dst0t, ._, ._, ._ }, 10173 } }, 10174 }, .{ 10175 .required_features = .{ .sahf, .x87, null, null }, 10176 .src_constraints = .{ 10177 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10178 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10179 }, 10180 .patterns = &.{ 10181 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 10182 .{ .src = .{ .mem, .to_x87 } }, 10183 .{ .src = .{ .to_x87, .to_x87 } }, 10184 }, 10185 .extra_temps = .{ 10186 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10187 .{ .type = .u8, .kind = .{ .reg = .ah } }, 10188 .unused, 10189 .unused, 10190 .unused, 10191 .unused, 10192 .unused, 10193 .unused, 10194 .unused, 10195 }, 10196 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 10197 .clobbers = .{ .eflags = true }, 10198 .each = .{ .once = &.{ 10199 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 10200 .{ ._, .f_, .ucom, .tmp0t, ._, ._, ._ }, 10201 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 10202 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 10203 .{ ._, ._p, .j, .@"0f", ._, ._, ._ }, 10204 .{ ._, .f_, .ucom, .src1t, ._, ._, ._ }, 10205 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 10206 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 10207 .{ ._, ._b, .j, .@"1f", ._, ._, ._ }, 10208 .{ .@"0:", .f_p, .st, .tmp0t, ._, ._, ._ }, 10209 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 10210 .{ .@"1:", .f_p, .st, .dst0t, ._, ._, ._ }, 10211 } }, 10212 }, .{ 10213 .required_features = .{ .x87, null, null, null }, 10214 .src_constraints = .{ 10215 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10216 .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, 10217 }, 10218 .patterns = &.{ 10219 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 10220 .{ .src = .{ .mem, .to_x87 } }, 10221 .{ .src = .{ .to_x87, .to_x87 } }, 10222 }, 10223 .extra_temps = .{ 10224 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10225 .{ .type = .u8, .kind = .{ .reg = .ah } }, 10226 .unused, 10227 .unused, 10228 .unused, 10229 .unused, 10230 .unused, 10231 .unused, 10232 .unused, 10233 }, 10234 .dst_temps = .{.{ .mut_rc = .{ .ref = .src1, .rc = .x87 } }}, 10235 .clobbers = .{ .eflags = true }, 10236 .each = .{ .once = &.{ 10237 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 10238 .{ ._, .f_, .xam, ._, ._, ._, ._ }, 10239 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 10240 .{ ._, ._, .@"test", .tmp1b, .si(0b1_000_100), ._, ._ }, 10241 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 10242 .{ ._, .f_, .ucom, .src1t, ._, ._, ._ }, 10243 .{ ._, .fn_sw, .st, .tmp1w, ._, ._, ._ }, 10244 .{ ._, ._, .@"test", .tmp1b, .si(0b0_000_001), ._, ._ }, 10245 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 10246 .{ .@"0:", .f_p, .st, .tmp0t, ._, ._, ._ }, 10247 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 10248 .{ .@"1:", .f_p, .st, .dst0t, ._, ._, ._ }, 10249 } }, 10250 }, .{ 10251 .required_features = .{ .x87, .cmov, null, null }, 10252 .src_constraints = .{ 10253 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10254 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10255 }, 10256 .patterns = &.{ 10257 .{ .src = .{ .to_mem, .to_mem } }, 10258 }, 10259 .extra_temps = .{ 10260 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10261 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 10262 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10263 .unused, 10264 .unused, 10265 .unused, 10266 .unused, 10267 .unused, 10268 .unused, 10269 }, 10270 .dst_temps = .{.mem}, 10271 .clobbers = .{ .eflags = true }, 10272 .each = .{ .once = &.{ 10273 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10274 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 10275 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 10276 .{ ._, .f_, .ucomi, .tmp1t, .tmp1t, ._, ._ }, 10277 .{ ._, .f_u, .cmov, .tmp1t, .tmp2t, ._, ._ }, 10278 .{ ._, .f_, .ucomi, .tmp1t, .tmp2t, ._, ._ }, 10279 .{ ._, .f_nb, .cmov, .tmp1t, .tmp2t, ._, ._ }, 10280 .{ ._, .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 10281 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 10282 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10283 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10284 } }, 10285 }, .{ 10286 .required_features = .{ .sahf, .x87, null, null }, 10287 .src_constraints = .{ 10288 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10289 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10290 }, 10291 .patterns = &.{ 10292 .{ .src = .{ .to_mem, .to_mem } }, 10293 }, 10294 .extra_temps = .{ 10295 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10296 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 10297 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10298 .{ .type = .u8, .kind = .{ .reg = .ah } }, 10299 .unused, 10300 .unused, 10301 .unused, 10302 .unused, 10303 .unused, 10304 }, 10305 .dst_temps = .{.mem}, 10306 .clobbers = .{ .eflags = true }, 10307 .each = .{ .once = &.{ 10308 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10309 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 10310 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 10311 .{ ._, .f_, .ucom, .tmp1t, ._, ._, ._ }, 10312 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 10313 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 10314 .{ ._, ._p, .j, .@"1f", ._, ._, ._ }, 10315 .{ ._, .f_, .ucom, .tmp2t, ._, ._, ._ }, 10316 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 10317 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 10318 .{ ._, ._b, .j, .@"2f", ._, ._, ._ }, 10319 .{ .@"1:", .f_p, .st, .tmp1t, ._, ._, ._ }, 10320 .{ ._, .f_, .ld, .tmp2t, ._, ._, ._ }, 10321 .{ .@"2:", .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 10322 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 10323 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10324 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10325 } }, 10326 }, .{ 10327 .required_features = .{ .x87, null, null, null }, 10328 .src_constraints = .{ 10329 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10330 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 10331 }, 10332 .patterns = &.{ 10333 .{ .src = .{ .to_mem, .to_mem } }, 10334 }, 10335 .extra_temps = .{ 10336 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10337 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 10338 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 10339 .{ .type = .u8, .kind = .{ .reg = .ah } }, 10340 .unused, 10341 .unused, 10342 .unused, 10343 .unused, 10344 .unused, 10345 }, 10346 .dst_temps = .{.mem}, 10347 .clobbers = .{ .eflags = true }, 10348 .each = .{ .once = &.{ 10349 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10350 .{ .@"0:", .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 10351 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 10352 .{ ._, .f_, .xam, ._, ._, ._, ._ }, 10353 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 10354 .{ ._, ._, .@"test", .tmp3b, .si(0b1_000_100), ._, ._ }, 10355 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 10356 .{ ._, .f_, .ucom, .tmp2t, ._, ._, ._ }, 10357 .{ ._, .fn_sw, .st, .tmp3w, ._, ._, ._ }, 10358 .{ ._, ._, .@"test", .tmp3b, .si(0b0_000_001), ._, ._ }, 10359 .{ ._, ._nz, .j, .@"2f", ._, ._, ._ }, 10360 .{ .@"1:", .f_p, .st, .tmp1t, ._, ._, ._ }, 10361 .{ ._, .f_, .ld, .tmp2t, ._, ._, ._ }, 10362 .{ .@"2:", .f_p, .st, .memia(.dst0t, .tmp0, .add_size), ._, ._, ._ }, 10363 .{ ._, .f_p, .st, .tmp2t, ._, ._, ._ }, 10364 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10365 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10366 } }, 10367 }, .{ 10368 .required_features = .{ .sse, null, null, null }, 10369 .src_constraints = .{ 10370 .{ .scalar_float = .{ .of = .xword, .is = .xword } }, 10371 .{ .scalar_float = .{ .of = .xword, .is = .xword } }, 10372 }, 10373 .patterns = &.{ 10374 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 10375 }, 10376 .call_frame = .{ .alignment = .@"16" }, 10377 .extra_temps = .{ 10378 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, 10379 .unused, 10380 .unused, 10381 .unused, 10382 .unused, 10383 .unused, 10384 .unused, 10385 .unused, 10386 .unused, 10387 }, 10388 .dst_temps = .{.{ .ref = .src0 }}, 10389 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 10390 .each = .{ .once = &.{ 10391 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 10392 } }, 10393 }, .{ 10394 .required_features = .{ .avx, null, null, null }, 10395 .src_constraints = .{ 10396 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10397 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10398 }, 10399 .patterns = &.{ 10400 .{ .src = .{ .to_mem, .to_mem } }, 10401 }, 10402 .call_frame = .{ .alignment = .@"16" }, 10403 .extra_temps = .{ 10404 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10405 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 10406 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 10407 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, 10408 .unused, 10409 .unused, 10410 .unused, 10411 .unused, 10412 .unused, 10413 }, 10414 .dst_temps = .{.mem}, 10415 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 10416 .each = .{ .once = &.{ 10417 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10418 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10419 .{ ._, .v_dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10420 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 10421 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10422 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 10423 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10424 } }, 10425 }, .{ 10426 .required_features = .{ .sse2, null, null, null }, 10427 .src_constraints = .{ 10428 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10429 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10430 }, 10431 .patterns = &.{ 10432 .{ .src = .{ .to_mem, .to_mem } }, 10433 }, 10434 .call_frame = .{ .alignment = .@"16" }, 10435 .extra_temps = .{ 10436 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10437 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 10438 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 10439 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, 10440 .unused, 10441 .unused, 10442 .unused, 10443 .unused, 10444 .unused, 10445 }, 10446 .dst_temps = .{.mem}, 10447 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 10448 .each = .{ .once = &.{ 10449 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10450 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10451 .{ ._, ._dqa, .mov, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10452 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 10453 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10454 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 10455 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10456 } }, 10457 }, .{ 10458 .required_features = .{ .sse, null, null, null }, 10459 .src_constraints = .{ 10460 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10461 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 10462 }, 10463 .patterns = &.{ 10464 .{ .src = .{ .to_mem, .to_mem } }, 10465 }, 10466 .call_frame = .{ .alignment = .@"16" }, 10467 .extra_temps = .{ 10468 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10469 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 10470 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 10471 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "fminq" } } }, 10472 .unused, 10473 .unused, 10474 .unused, 10475 .unused, 10476 .unused, 10477 }, 10478 .dst_temps = .{.mem}, 10479 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 10480 .each = .{ .once = &.{ 10481 .{ ._, ._, .mov, .tmp0q, .sa(.src0, .sub_size), ._, ._ }, 10482 .{ .@"0:", ._ps, .mova, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10483 .{ ._, ._ps, .mova, .tmp2x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10484 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 10485 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10486 .{ ._, ._, .add, .tmp0q, .si(16), ._, ._ }, 10487 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10488 } }, 10489 } }) catch |err| switch (err) { 10490 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 10491 @tagName(air_tag), 10492 cg.typeOf(bin_op.lhs).fmt(pt), 10493 ops[0].tracking(cg), 10494 ops[1].tracking(cg), 10495 }), 10496 else => |e| return e, 10497 }; 10498 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 10499 }, 10500 .alloc => if (use_old) try cg.airAlloc(inst) else { 10501 const ty = air_datas[@intFromEnum(inst)].ty; 10502 const slot = try cg.tempInit(ty, .{ .lea_frame = .{ 10503 .index = try cg.allocMemPtr(inst), 10504 } }); 10505 try slot.finish(inst, &.{}, &.{}, cg); 10506 }, 10507 .inferred_alloc, .inferred_alloc_comptime => unreachable, 10508 .ret_ptr => if (use_old) try cg.airRetPtr(inst) else { 10509 const ty = air_datas[@intFromEnum(inst)].ty; 10510 var slot = switch (cg.ret_mcv.long) { 10511 else => unreachable, 10512 .none => try cg.tempInit(ty, .{ .lea_frame = .{ 10513 .index = try cg.allocMemPtr(inst), 10514 } }), 10515 .load_frame => slot: { 10516 var slot = try cg.tempInit(ty, cg.ret_mcv.long); 10517 try slot.toOffset(cg.ret_mcv.short.indirect.off, cg); 10518 break :slot slot; 10519 }, 10520 }; 10521 try slot.finish(inst, &.{}, &.{}, cg); 10522 }, 10523 .assembly => try cg.airAsm(inst), 10524 .bit_and, .bit_or, .xor, .bool_and, .bool_or => |air_tag| if (use_old) try cg.airBinOp(inst, air_tag) else { 10525 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 10526 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 10527 var res: [1]Temp = undefined; 10528 cg.select(&res, &.{cg.typeOf(bin_op.lhs)}, &ops, switch (@as(Mir.Inst.Tag, switch (air_tag) { 10529 else => unreachable, 10530 .bit_and, .bool_and => .@"and", 10531 .bit_or, .bool_or => .@"or", 10532 .xor => .xor, 10533 })) { 10534 else => unreachable, 10535 inline .@"and", .@"or", .xor => |mir_tag| comptime &.{ .{ 10536 .src_constraints = .{ .{ .size = .byte }, .{ .size = .byte } }, 10537 .patterns = &.{ 10538 .{ .src = .{ .mut_mem, .imm8 } }, 10539 .{ .src = .{ .imm8, .mut_mem }, .commute = .{ 0, 1 } }, 10540 .{ .src = .{ .to_mut_gpr, .imm8 } }, 10541 .{ .src = .{ .imm8, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10542 .{ .src = .{ .mut_mem, .to_gpr } }, 10543 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 10544 .{ .src = .{ .to_mut_gpr, .mem } }, 10545 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10546 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 10547 }, 10548 .dst_temps = .{.{ .ref = .src0 }}, 10549 .clobbers = .{ .eflags = true }, 10550 .each = .{ .once = &.{ 10551 .{ ._, ._, mir_tag, .dst0b, .src1b, ._, ._ }, 10552 } }, 10553 }, .{ 10554 .src_constraints = .{ .{ .size = .word }, .{ .size = .word } }, 10555 .patterns = &.{ 10556 .{ .src = .{ .mut_mem, .imm16 } }, 10557 .{ .src = .{ .imm16, .mut_mem }, .commute = .{ 0, 1 } }, 10558 .{ .src = .{ .to_mut_gpr, .imm16 } }, 10559 .{ .src = .{ .imm16, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10560 .{ .src = .{ .mut_mem, .to_gpr } }, 10561 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 10562 .{ .src = .{ .to_mut_gpr, .mem } }, 10563 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10564 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 10565 }, 10566 .dst_temps = .{.{ .ref = .src0 }}, 10567 .clobbers = .{ .eflags = true }, 10568 .each = .{ .once = &.{ 10569 .{ ._, ._, mir_tag, .dst0w, .src1w, ._, ._ }, 10570 } }, 10571 }, .{ 10572 .src_constraints = .{ .{ .size = .dword }, .{ .size = .dword } }, 10573 .patterns = &.{ 10574 .{ .src = .{ .mut_mem, .imm32 } }, 10575 .{ .src = .{ .imm32, .mut_mem }, .commute = .{ 0, 1 } }, 10576 .{ .src = .{ .to_mut_gpr, .imm32 } }, 10577 .{ .src = .{ .imm32, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10578 .{ .src = .{ .mut_mem, .to_gpr } }, 10579 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 10580 .{ .src = .{ .to_mut_gpr, .mem } }, 10581 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10582 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 10583 }, 10584 .dst_temps = .{.{ .ref = .src0 }}, 10585 .clobbers = .{ .eflags = true }, 10586 .each = .{ .once = &.{ 10587 .{ ._, ._, mir_tag, .dst0d, .src1d, ._, ._ }, 10588 } }, 10589 }, .{ 10590 .required_features = .{ .@"64bit", null, null, null }, 10591 .src_constraints = .{ .{ .size = .qword }, .{ .size = .qword } }, 10592 .patterns = &.{ 10593 .{ .src = .{ .mut_mem, .simm32 } }, 10594 .{ .src = .{ .simm32, .mut_mem }, .commute = .{ 0, 1 } }, 10595 .{ .src = .{ .to_mut_gpr, .simm32 } }, 10596 .{ .src = .{ .simm32, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10597 .{ .src = .{ .mut_mem, .to_gpr } }, 10598 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 10599 .{ .src = .{ .to_mut_gpr, .mem } }, 10600 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 10601 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 10602 }, 10603 .dst_temps = .{.{ .ref = .src0 }}, 10604 .clobbers = .{ .eflags = true }, 10605 .each = .{ .once = &.{ 10606 .{ ._, ._, mir_tag, .dst0q, .src1q, ._, ._ }, 10607 } }, 10608 }, .{ 10609 .required_features = .{ .mmx, null, null, null }, 10610 .src_constraints = .{ .{ .size = .qword }, .{ .size = .qword } }, 10611 .patterns = &.{ 10612 .{ .src = .{ .to_mut_mm, .mem } }, 10613 .{ .src = .{ .mem, .to_mut_mm }, .commute = .{ 0, 1 } }, 10614 .{ .src = .{ .to_mut_mm, .to_mm } }, 10615 }, 10616 .dst_temps = .{.{ .ref = .src0 }}, 10617 .each = .{ .once = &.{ 10618 .{ ._, .p_, mir_tag, .dst0q, .src1q, ._, ._ }, 10619 } }, 10620 }, .{ 10621 .required_features = .{ .avx, null, null, null }, 10622 .src_constraints = .{ .{ .size = .xword }, .{ .size = .xword } }, 10623 .patterns = &.{ 10624 .{ .src = .{ .to_xmm, .mem } }, 10625 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 10626 .{ .src = .{ .to_xmm, .to_xmm } }, 10627 }, 10628 .dst_temps = .{.{ .rc = .sse }}, 10629 .each = .{ .once = &.{ 10630 .{ ._, .vp_, mir_tag, .dst0x, .src0x, .src1x, ._ }, 10631 } }, 10632 }, .{ 10633 .required_features = .{ .sse2, null, null, null }, 10634 .src_constraints = .{ .{ .size = .xword }, .{ .size = .xword } }, 10635 .patterns = &.{ 10636 .{ .src = .{ .to_mut_xmm, .mem } }, 10637 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 10638 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 10639 }, 10640 .dst_temps = .{.{ .ref = .src0 }}, 10641 .each = .{ .once = &.{ 10642 .{ ._, .p_, mir_tag, .dst0x, .src1x, ._, ._ }, 10643 } }, 10644 }, .{ 10645 .required_features = .{ .sse, null, null, null }, 10646 .src_constraints = .{ .{ .size = .xword }, .{ .size = .xword } }, 10647 .patterns = &.{ 10648 .{ .src = .{ .to_mut_xmm, .mem } }, 10649 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 10650 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 10651 }, 10652 .dst_temps = .{.{ .ref = .src0 }}, 10653 .each = .{ .once = &.{ 10654 .{ ._, ._ps, mir_tag, .dst0x, .src1x, ._, ._ }, 10655 } }, 10656 }, .{ 10657 .required_features = .{ .avx2, null, null, null }, 10658 .src_constraints = .{ .{ .size = .yword }, .{ .size = .yword } }, 10659 .patterns = &.{ 10660 .{ .src = .{ .to_ymm, .mem } }, 10661 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 10662 .{ .src = .{ .to_ymm, .to_ymm } }, 10663 }, 10664 .dst_temps = .{.{ .rc = .sse }}, 10665 .each = .{ .once = &.{ 10666 .{ ._, .vp_, mir_tag, .dst0y, .src0y, .src1y, ._ }, 10667 } }, 10668 }, .{ 10669 .required_features = .{ .avx, null, null, null }, 10670 .src_constraints = .{ .{ .size = .yword }, .{ .size = .yword } }, 10671 .patterns = &.{ 10672 .{ .src = .{ .to_ymm, .mem } }, 10673 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 10674 .{ .src = .{ .to_ymm, .to_ymm } }, 10675 }, 10676 .dst_temps = .{.{ .rc = .sse }}, 10677 .each = .{ .once = &.{ 10678 .{ ._, .v_pd, mir_tag, .dst0y, .src0y, .src1y, ._ }, 10679 } }, 10680 }, .{ 10681 .required_features = .{ .avx2, null, null, null }, 10682 .src_constraints = .{ .{ .multiple_size = .yword }, .{ .multiple_size = .yword } }, 10683 .patterns = &.{ 10684 .{ .src = .{ .to_mem, .to_mem } }, 10685 }, 10686 .extra_temps = .{ 10687 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10688 .{ .type = .vector_32_u8, .kind = .{ .rc = .sse } }, 10689 .unused, 10690 .unused, 10691 .unused, 10692 .unused, 10693 .unused, 10694 .unused, 10695 .unused, 10696 }, 10697 .dst_temps = .{.mem}, 10698 .clobbers = .{ .eflags = true }, 10699 .each = .{ .once = &.{ 10700 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10701 .{ .@"0:", .v_dqu, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 10702 .{ ._, .vp_, mir_tag, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 10703 .{ ._, .v_dqu, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 10704 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 10705 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10706 } }, 10707 }, .{ 10708 .required_features = .{ .avx, null, null, null }, 10709 .src_constraints = .{ .{ .multiple_size = .yword }, .{ .multiple_size = .yword } }, 10710 .patterns = &.{ 10711 .{ .src = .{ .to_mem, .to_mem } }, 10712 }, 10713 .extra_temps = .{ 10714 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10715 .{ .type = .vector_32_u8, .kind = .{ .rc = .sse } }, 10716 .unused, 10717 .unused, 10718 .unused, 10719 .unused, 10720 .unused, 10721 .unused, 10722 .unused, 10723 }, 10724 .dst_temps = .{.mem}, 10725 .clobbers = .{ .eflags = true }, 10726 .each = .{ .once = &.{ 10727 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10728 .{ .@"0:", .v_pd, .movu, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 10729 .{ ._, .v_pd, mir_tag, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 10730 .{ ._, .v_pd, .movu, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 10731 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 10732 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10733 } }, 10734 }, .{ 10735 .required_features = .{ .avx, null, null, null }, 10736 .src_constraints = .{ .{ .multiple_size = .xword }, .{ .multiple_size = .xword } }, 10737 .patterns = &.{ 10738 .{ .src = .{ .to_mem, .to_mem } }, 10739 }, 10740 .extra_temps = .{ 10741 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10742 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 10743 .unused, 10744 .unused, 10745 .unused, 10746 .unused, 10747 .unused, 10748 .unused, 10749 .unused, 10750 }, 10751 .dst_temps = .{.mem}, 10752 .clobbers = .{ .eflags = true }, 10753 .each = .{ .once = &.{ 10754 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10755 .{ .@"0:", .v_dqu, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10756 .{ ._, .vp_, mir_tag, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 10757 .{ ._, .v_dqu, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10758 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10759 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10760 } }, 10761 }, .{ 10762 .required_features = .{ .sse2, null, null, null }, 10763 .src_constraints = .{ .{ .multiple_size = .xword }, .{ .multiple_size = .xword } }, 10764 .patterns = &.{ 10765 .{ .src = .{ .to_mem, .to_mem } }, 10766 }, 10767 .extra_temps = .{ 10768 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10769 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 10770 .unused, 10771 .unused, 10772 .unused, 10773 .unused, 10774 .unused, 10775 .unused, 10776 .unused, 10777 }, 10778 .dst_temps = .{.mem}, 10779 .clobbers = .{ .eflags = true }, 10780 .each = .{ .once = &.{ 10781 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10782 .{ .@"0:", ._dqu, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10783 .{ ._, .p_, mir_tag, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10784 .{ ._, ._dqu, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10785 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10786 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10787 } }, 10788 }, .{ 10789 .required_features = .{ .sse, null, null, null }, 10790 .src_constraints = .{ .{ .multiple_size = .xword }, .{ .multiple_size = .xword } }, 10791 .patterns = &.{ 10792 .{ .src = .{ .to_mem, .to_mem } }, 10793 }, 10794 .extra_temps = .{ 10795 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10796 .{ .type = .vector_16_u8, .kind = .{ .rc = .sse } }, 10797 .unused, 10798 .unused, 10799 .unused, 10800 .unused, 10801 .unused, 10802 .unused, 10803 .unused, 10804 }, 10805 .dst_temps = .{.mem}, 10806 .clobbers = .{ .eflags = true }, 10807 .each = .{ .once = &.{ 10808 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10809 .{ .@"0:", ._ps, .movu, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 10810 .{ ._, ._ps, mir_tag, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 10811 .{ ._, ._ps, .movu, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 10812 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 10813 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10814 } }, 10815 }, .{ 10816 .required_features = .{ .mmx, null, null, null }, 10817 .src_constraints = .{ .{ .multiple_size = .qword }, .{ .multiple_size = .qword } }, 10818 .patterns = &.{ 10819 .{ .src = .{ .to_mem, .to_mem } }, 10820 }, 10821 .extra_temps = .{ 10822 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10823 .{ .kind = .{ .rc = .mmx } }, 10824 .unused, 10825 .unused, 10826 .unused, 10827 .unused, 10828 .unused, 10829 .unused, 10830 .unused, 10831 }, 10832 .dst_temps = .{.mem}, 10833 .clobbers = .{ .eflags = true }, 10834 .each = .{ .once = &.{ 10835 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10836 .{ .@"0:", ._q, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 10837 .{ ._, .p_, mir_tag, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 10838 .{ ._, ._q, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 10839 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 10840 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10841 } }, 10842 }, .{ 10843 .src_constraints = .{ .{ .multiple_size = .qword }, .{ .multiple_size = .qword } }, 10844 .patterns = &.{ 10845 .{ .src = .{ .to_mem, .to_mem } }, 10846 }, 10847 .extra_temps = .{ 10848 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 10849 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 10850 .unused, 10851 .unused, 10852 .unused, 10853 .unused, 10854 .unused, 10855 .unused, 10856 .unused, 10857 }, 10858 .dst_temps = .{.mem}, 10859 .clobbers = .{ .eflags = true }, 10860 .each = .{ .once = &.{ 10861 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 10862 .{ .@"0:", ._, .mov, .tmp1p, .memia(.src0p, .tmp0, .add_size), ._, ._ }, 10863 .{ ._, ._, mir_tag, .tmp1p, .memia(.src1p, .tmp0, .add_size), ._, ._ }, 10864 .{ ._, ._, .mov, .memia(.dst0p, .tmp0, .add_size), .tmp1p, ._, ._ }, 10865 .{ ._, ._, .add, .tmp0p, .sa(.tmp1, .add_size), ._, ._ }, 10866 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 10867 } }, 10868 } }, 10869 }) catch |err| switch (err) { 10870 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 10871 @tagName(air_tag), 10872 cg.typeOf(bin_op.lhs).fmt(pt), 10873 ops[0].tracking(cg), 10874 ops[1].tracking(cg), 10875 }), 10876 else => |e| return e, 10877 }; 10878 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 10879 }, 10880 .not => |air_tag| if (use_old) try cg.airUnOp(inst, air_tag) else { 10881 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 10882 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 10883 var res: [1]Temp = undefined; 10884 cg.select(&res, &.{ty_op.ty.toType()}, &ops, comptime &.{ .{ 10885 .src_constraints = .{ .{ .signed_or_exact_int = .byte }, .any }, 10886 .patterns = &.{ 10887 .{ .src = .{ .mut_mem, .none } }, 10888 .{ .src = .{ .to_mut_gpr, .none } }, 10889 }, 10890 .dst_temps = .{.{ .ref = .src0 }}, 10891 .each = .{ .once = &.{ 10892 .{ ._, ._, .not, .dst0b, ._, ._, ._ }, 10893 } }, 10894 }, .{ 10895 .src_constraints = .{ .{ .unsigned_int = .byte }, .any }, 10896 .patterns = &.{ 10897 .{ .src = .{ .mut_mem, .none } }, 10898 .{ .src = .{ .to_mut_gpr, .none } }, 10899 }, 10900 .dst_temps = .{.{ .ref = .src0 }}, 10901 .clobbers = .{ .eflags = true }, 10902 .each = .{ .once = &.{ 10903 .{ ._, ._, .xor, .dst0b, .sa(.src0, .add_umax), ._, ._ }, 10904 } }, 10905 }, .{ 10906 .src_constraints = .{ .{ .signed_or_exact_int = .word }, .any }, 10907 .patterns = &.{ 10908 .{ .src = .{ .mut_mem, .none } }, 10909 .{ .src = .{ .to_mut_gpr, .none } }, 10910 }, 10911 .dst_temps = .{.{ .ref = .src0 }}, 10912 .each = .{ .once = &.{ 10913 .{ ._, ._, .not, .dst0w, ._, ._, ._ }, 10914 } }, 10915 }, .{ 10916 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 10917 .patterns = &.{ 10918 .{ .src = .{ .mut_mem, .none } }, 10919 .{ .src = .{ .to_mut_gpr, .none } }, 10920 }, 10921 .dst_temps = .{.{ .ref = .src0 }}, 10922 .clobbers = .{ .eflags = true }, 10923 .each = .{ .once = &.{ 10924 .{ ._, ._, .xor, .dst0w, .sa(.src0, .add_umax), ._, ._ }, 10925 } }, 10926 }, .{ 10927 .src_constraints = .{ .{ .signed_or_exact_int = .dword }, .any }, 10928 .patterns = &.{ 10929 .{ .src = .{ .mut_mem, .none } }, 10930 .{ .src = .{ .to_mut_gpr, .none } }, 10931 }, 10932 .dst_temps = .{.{ .ref = .src0 }}, 10933 .each = .{ .once = &.{ 10934 .{ ._, ._, .not, .dst0d, ._, ._, ._ }, 10935 } }, 10936 }, .{ 10937 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 10938 .patterns = &.{ 10939 .{ .src = .{ .mut_mem, .none } }, 10940 .{ .src = .{ .to_mut_gpr, .none } }, 10941 }, 10942 .dst_temps = .{.{ .ref = .src0 }}, 10943 .clobbers = .{ .eflags = true }, 10944 .each = .{ .once = &.{ 10945 .{ ._, ._, .xor, .dst0d, .sa(.src0, .add_umax), ._, ._ }, 10946 } }, 10947 }, .{ 10948 .required_features = .{ .@"64bit", null, null, null }, 10949 .src_constraints = .{ .{ .signed_or_exact_int = .qword }, .any }, 10950 .patterns = &.{ 10951 .{ .src = .{ .mut_mem, .none } }, 10952 .{ .src = .{ .to_mut_gpr, .none } }, 10953 }, 10954 .dst_temps = .{.{ .ref = .src0 }}, 10955 .each = .{ .once = &.{ 10956 .{ ._, ._, .not, .dst0q, ._, ._, ._ }, 10957 } }, 10958 }, .{ 10959 .required_features = .{ .@"64bit", null, null, null }, 10960 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 10961 .patterns = &.{ 10962 .{ .src = .{ .mem, .none } }, 10963 .{ .src = .{ .to_gpr, .none } }, 10964 }, 10965 .dst_temps = .{.{ .rc = .general_purpose }}, 10966 .each = .{ .once = &.{ 10967 .{ ._, ._, .mov, .dst0q, .ua(.src0, .add_umax), ._, ._ }, 10968 .{ ._, ._, .xor, .dst0q, .src0q, ._, ._ }, 10969 } }, 10970 }, .{ 10971 .required_features = .{ .mmx, null, null, null }, 10972 .src_constraints = .{ .{ .signed_or_exact_int = .qword }, .any }, 10973 .patterns = &.{ 10974 .{ .src = .{ .mem, .none } }, 10975 .{ .src = .{ .to_mm, .none } }, 10976 }, 10977 .dst_temps = .{.{ .rc = .mmx }}, 10978 .each = .{ .once = &.{ 10979 .{ ._, .p_d, .cmpeq, .dst0q, .dst0q, ._, ._ }, 10980 .{ ._, .p_, .xor, .dst0q, .src0q, ._, ._ }, 10981 } }, 10982 }, .{ 10983 .required_features = .{ .mmx, null, null, null }, 10984 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 10985 .patterns = &.{ 10986 .{ .src = .{ .to_mut_mm, .none } }, 10987 }, 10988 .extra_temps = .{ 10989 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 10990 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 10991 .unused, 10992 .unused, 10993 .unused, 10994 .unused, 10995 .unused, 10996 .unused, 10997 .unused, 10998 }, 10999 .dst_temps = .{.{ .ref = .src0 }}, 11000 .each = .{ .once = &.{ 11001 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11002 .{ ._, .p_, .xor, .dst0q, .lea(.qword, .tmp0), ._, ._ }, 11003 } }, 11004 }, .{ 11005 .required_features = .{ .avx, null, null, null }, 11006 .src_constraints = .{ .{ .signed_or_exact_int = .xword }, .any }, 11007 .patterns = &.{ 11008 .{ .src = .{ .mem, .none } }, 11009 .{ .src = .{ .to_xmm, .none } }, 11010 }, 11011 .dst_temps = .{.{ .rc = .sse }}, 11012 .each = .{ .once = &.{ 11013 .{ ._, .vp_q, .cmpeq, .dst0x, .dst0x, .dst0x, ._ }, 11014 .{ ._, .vp_, .xor, .dst0x, .dst0x, .src0x, ._ }, 11015 } }, 11016 }, .{ 11017 .required_features = .{ .avx, null, null, null }, 11018 .src_constraints = .{ .{ .unsigned_int = .xword }, .any }, 11019 .patterns = &.{ 11020 .{ .src = .{ .to_xmm, .none } }, 11021 }, 11022 .extra_temps = .{ 11023 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11024 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11025 .unused, 11026 .unused, 11027 .unused, 11028 .unused, 11029 .unused, 11030 .unused, 11031 .unused, 11032 }, 11033 .dst_temps = .{.{ .rc = .sse }}, 11034 .each = .{ .once = &.{ 11035 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11036 .{ ._, .vp_, .xor, .dst0x, .src0x, .lea(.xword, .tmp0), ._ }, 11037 } }, 11038 }, .{ 11039 .required_features = .{ .sse2, null, null, null }, 11040 .src_constraints = .{ .{ .signed_or_exact_int = .xword }, .any }, 11041 .patterns = &.{ 11042 .{ .src = .{ .mem, .none } }, 11043 .{ .src = .{ .to_xmm, .none } }, 11044 }, 11045 .dst_temps = .{.{ .rc = .sse }}, 11046 .each = .{ .once = &.{ 11047 .{ ._, .p_d, .cmpeq, .dst0x, .dst0x, ._, ._ }, 11048 .{ ._, .p_, .xor, .dst0x, .src0x, ._, ._ }, 11049 } }, 11050 }, .{ 11051 .required_features = .{ .sse2, null, null, null }, 11052 .src_constraints = .{ .{ .unsigned_int = .xword }, .any }, 11053 .patterns = &.{ 11054 .{ .src = .{ .to_mut_xmm, .none } }, 11055 }, 11056 .extra_temps = .{ 11057 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11058 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11059 .unused, 11060 .unused, 11061 .unused, 11062 .unused, 11063 .unused, 11064 .unused, 11065 .unused, 11066 }, 11067 .dst_temps = .{.{ .ref = .src0 }}, 11068 .each = .{ .once = &.{ 11069 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11070 .{ ._, .p_, .xor, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 11071 } }, 11072 }, .{ 11073 .required_features = .{ .sse, null, null, null }, 11074 .src_constraints = .{ .{ .int = .xword }, .any }, 11075 .patterns = &.{ 11076 .{ .src = .{ .to_mut_xmm, .none } }, 11077 }, 11078 .extra_temps = .{ 11079 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11080 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11081 .unused, 11082 .unused, 11083 .unused, 11084 .unused, 11085 .unused, 11086 .unused, 11087 .unused, 11088 }, 11089 .dst_temps = .{.{ .ref = .src0 }}, 11090 .each = .{ .once = &.{ 11091 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11092 .{ ._, ._ps, .xor, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 11093 } }, 11094 }, .{ 11095 .required_features = .{ .avx2, null, null, null }, 11096 .src_constraints = .{ .{ .signed_or_exact_int = .yword }, .any }, 11097 .patterns = &.{ 11098 .{ .src = .{ .mem, .none } }, 11099 .{ .src = .{ .to_ymm, .none } }, 11100 }, 11101 .dst_temps = .{.{ .rc = .sse }}, 11102 .each = .{ .once = &.{ 11103 .{ ._, .vp_q, .cmpeq, .dst0y, .dst0y, .dst0y, ._ }, 11104 .{ ._, .vp_, .xor, .dst0y, .dst0y, .src0y, ._ }, 11105 } }, 11106 }, .{ 11107 .required_features = .{ .avx2, null, null, null }, 11108 .src_constraints = .{ .{ .unsigned_int = .yword }, .any }, 11109 .patterns = &.{ 11110 .{ .src = .{ .to_ymm, .none } }, 11111 }, 11112 .extra_temps = .{ 11113 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11114 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11115 .unused, 11116 .unused, 11117 .unused, 11118 .unused, 11119 .unused, 11120 .unused, 11121 .unused, 11122 }, 11123 .dst_temps = .{.{ .rc = .sse }}, 11124 .each = .{ .once = &.{ 11125 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11126 .{ ._, .vp_, .xor, .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 11127 } }, 11128 }, .{ 11129 .required_features = .{ .avx, null, null, null }, 11130 .src_constraints = .{ .{ .signed_or_exact_int = .yword }, .any }, 11131 .patterns = &.{ 11132 .{ .src = .{ .mem, .none } }, 11133 .{ .src = .{ .to_ymm, .none } }, 11134 }, 11135 .dst_temps = .{.{ .rc = .sse }}, 11136 .each = .{ .once = &.{ 11137 .{ ._, .v_pd, .cmp, .dst0y, .dst0y, .dst0y, .vp(.true) }, 11138 .{ ._, .v_pd, .xor, .dst0y, .dst0y, .src0y, ._ }, 11139 } }, 11140 }, .{ 11141 .required_features = .{ .avx, null, null, null }, 11142 .src_constraints = .{ .{ .unsigned_int = .yword }, .any }, 11143 .patterns = &.{ 11144 .{ .src = .{ .to_ymm, .none } }, 11145 }, 11146 .extra_temps = .{ 11147 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11148 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11149 .unused, 11150 .unused, 11151 .unused, 11152 .unused, 11153 .unused, 11154 .unused, 11155 .unused, 11156 }, 11157 .dst_temps = .{.{ .rc = .sse }}, 11158 .each = .{ .once = &.{ 11159 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11160 .{ ._, .v_pd, .xor, .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 11161 } }, 11162 }, .{ 11163 .required_features = .{ .avx2, null, null, null }, 11164 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .yword, .is = .xword } }, .any }, 11165 .patterns = &.{ 11166 .{ .src = .{ .to_mem, .none } }, 11167 }, 11168 .extra_temps = .{ 11169 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11170 .{ .kind = .{ .rc = .sse } }, 11171 .{ .kind = .{ .rc = .sse } }, 11172 .unused, 11173 .unused, 11174 .unused, 11175 .unused, 11176 .unused, 11177 .unused, 11178 }, 11179 .dst_temps = .{.mem}, 11180 .clobbers = .{ .eflags = true }, 11181 .each = .{ .once = &.{ 11182 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11183 .{ ._, .vp_q, .cmpeq, .tmp1y, .tmp1y, .tmp1y, ._ }, 11184 .{ .@"0:", .vp_, .xor, .tmp2y, .tmp1y, .memiad(.src0y, .tmp0, .add_size, -16), ._ }, 11185 .{ ._, .v_dqu, .mov, .memiad(.dst0y, .tmp0, .add_size, -16), .tmp2y, ._, ._ }, 11186 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 11187 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11188 .{ .@"0:", .vp_, .xor, .tmp2x, .tmp1x, .memad(.src0x, .add_size, -16), ._ }, 11189 .{ ._, .v_dqa, .mov, .memad(.dst0x, .add_size, -16), .tmp2x, ._, ._ }, 11190 } }, 11191 }, .{ 11192 .required_features = .{ .avx2, null, null, null }, 11193 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .yword, .is = .yword } }, .any }, 11194 .patterns = &.{ 11195 .{ .src = .{ .to_mem, .none } }, 11196 }, 11197 .extra_temps = .{ 11198 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11199 .{ .kind = .{ .rc = .sse } }, 11200 .{ .kind = .{ .rc = .sse } }, 11201 .unused, 11202 .unused, 11203 .unused, 11204 .unused, 11205 .unused, 11206 .unused, 11207 }, 11208 .dst_temps = .{.mem}, 11209 .clobbers = .{ .eflags = true }, 11210 .each = .{ .once = &.{ 11211 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11212 .{ ._, .vp_q, .cmpeq, .tmp1y, .tmp1y, .tmp1y, ._ }, 11213 .{ .@"0:", .vp_, .xor, .tmp2y, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._ }, 11214 .{ ._, .v_dqu, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp2y, ._, ._ }, 11215 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 11216 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11217 } }, 11218 }, .{ 11219 .required_features = .{ .avx, null, null, null }, 11220 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .yword, .is = .xword } }, .any }, 11221 .patterns = &.{ 11222 .{ .src = .{ .to_mem, .none } }, 11223 }, 11224 .extra_temps = .{ 11225 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11226 .{ .kind = .{ .rc = .sse } }, 11227 .{ .kind = .{ .rc = .sse } }, 11228 .unused, 11229 .unused, 11230 .unused, 11231 .unused, 11232 .unused, 11233 .unused, 11234 }, 11235 .dst_temps = .{.mem}, 11236 .clobbers = .{ .eflags = true }, 11237 .each = .{ .once = &.{ 11238 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11239 .{ ._, .v_pd, .cmp, .tmp1y, .tmp1y, .tmp1y, .vp(.true) }, 11240 .{ .@"0:", .v_pd, .xor, .tmp2y, .tmp1y, .memiad(.src0y, .tmp0, .add_size, -16), ._ }, 11241 .{ ._, .v_pd, .movu, .memiad(.dst0y, .tmp0, .add_size, -16), .tmp2y, ._, ._ }, 11242 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 11243 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11244 .{ .@"0:", .v_pd, .xor, .tmp2x, .tmp1x, .memad(.src0x, .add_size, -16), ._ }, 11245 .{ ._, .v_pd, .mova, .memad(.dst0x, .add_size, -16), .tmp2x, ._, ._ }, 11246 } }, 11247 }, .{ 11248 .required_features = .{ .avx, null, null, null }, 11249 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .yword, .is = .yword } }, .any }, 11250 .patterns = &.{ 11251 .{ .src = .{ .to_mem, .none } }, 11252 }, 11253 .extra_temps = .{ 11254 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11255 .{ .kind = .{ .rc = .sse } }, 11256 .{ .kind = .{ .rc = .sse } }, 11257 .unused, 11258 .unused, 11259 .unused, 11260 .unused, 11261 .unused, 11262 .unused, 11263 }, 11264 .dst_temps = .{.mem}, 11265 .clobbers = .{ .eflags = true }, 11266 .each = .{ .once = &.{ 11267 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11268 .{ ._, .v_pd, .cmp, .tmp1y, .tmp1y, .tmp1y, .vp(.true) }, 11269 .{ .@"0:", .v_pd, .xor, .tmp2y, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._ }, 11270 .{ ._, .v_pd, .movu, .memia(.dst0y, .tmp0, .add_size), .tmp2y, ._, ._ }, 11271 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 11272 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11273 } }, 11274 }, .{ 11275 .required_features = .{ .avx, null, null, null }, 11276 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11277 .patterns = &.{ 11278 .{ .src = .{ .to_mem, .none } }, 11279 }, 11280 .extra_temps = .{ 11281 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11282 .{ .kind = .{ .rc = .sse } }, 11283 .{ .kind = .{ .rc = .sse } }, 11284 .unused, 11285 .unused, 11286 .unused, 11287 .unused, 11288 .unused, 11289 .unused, 11290 }, 11291 .dst_temps = .{.mem}, 11292 .clobbers = .{ .eflags = true }, 11293 .each = .{ .once = &.{ 11294 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11295 .{ ._, .vp_q, .cmpeq, .tmp1x, .tmp1x, .tmp1x, ._ }, 11296 .{ .@"0:", .v_, .xor, .tmp2x, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._ }, 11297 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp2x, ._, ._ }, 11298 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11299 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11300 } }, 11301 }, .{ 11302 .required_features = .{ .sse2, null, null, null }, 11303 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11304 .patterns = &.{ 11305 .{ .src = .{ .to_mem, .none } }, 11306 }, 11307 .extra_temps = .{ 11308 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11309 .{ .kind = .{ .rc = .sse } }, 11310 .{ .kind = .{ .rc = .sse } }, 11311 .unused, 11312 .unused, 11313 .unused, 11314 .unused, 11315 .unused, 11316 .unused, 11317 }, 11318 .dst_temps = .{.mem}, 11319 .clobbers = .{ .eflags = true }, 11320 .each = .{ .once = &.{ 11321 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11322 .{ ._, .p_d, .cmpeq, .tmp1x, .tmp1x, ._, ._ }, 11323 .{ .@"0:", ._dqa, .mov, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 11324 .{ ._, .p_, .xor, .tmp2x, .tmp1x, ._, ._ }, 11325 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp2x, ._, ._ }, 11326 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11327 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11328 } }, 11329 }, .{ 11330 .required_features = .{ .@"64bit", null, null, null }, 11331 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11332 .patterns = &.{ 11333 .{ .src = .{ .mut_mem, .none } }, 11334 }, 11335 .extra_temps = .{ 11336 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11337 .unused, 11338 .unused, 11339 .unused, 11340 .unused, 11341 .unused, 11342 .unused, 11343 .unused, 11344 .unused, 11345 }, 11346 .dst_temps = .{.{ .ref = .src0 }}, 11347 .clobbers = .{ .eflags = true }, 11348 .each = .{ .once = &.{ 11349 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11350 .{ .@"0:", ._, .not, .memia(.dst0q, .tmp0, .add_size), ._, ._, ._ }, 11351 .{ ._, ._, .not, .memiad(.dst0q, .tmp0, .add_size, 8), ._, ._, ._ }, 11352 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11353 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11354 } }, 11355 }, .{ 11356 .required_features = .{ .@"64bit", null, null, null }, 11357 .src_constraints = .{ .{ .signed_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11358 .patterns = &.{ 11359 .{ .src = .{ .to_mem, .none } }, 11360 }, 11361 .extra_temps = .{ 11362 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11363 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11364 .unused, 11365 .unused, 11366 .unused, 11367 .unused, 11368 .unused, 11369 .unused, 11370 .unused, 11371 }, 11372 .dst_temps = .{.mem}, 11373 .clobbers = .{ .eflags = true }, 11374 .each = .{ .once = &.{ 11375 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 11376 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 11377 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11378 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 11379 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11380 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11381 } }, 11382 }, .{ 11383 .required_features = .{ .@"64bit", null, null, null }, 11384 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .xword, .is = .dword } }, .any }, 11385 .patterns = &.{ 11386 .{ .src = .{ .mut_mem, .none } }, 11387 }, 11388 .extra_temps = .{ 11389 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11390 .unused, 11391 .unused, 11392 .unused, 11393 .unused, 11394 .unused, 11395 .unused, 11396 .unused, 11397 .unused, 11398 }, 11399 .dst_temps = .{.{ .ref = .src0 }}, 11400 .clobbers = .{ .eflags = true }, 11401 .each = .{ .once = &.{ 11402 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11403 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16), ._, ._, ._ }, 11404 .{ ._, ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16 + 8), ._, ._, ._ }, 11405 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11406 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11407 .{ ._, ._, .not, .memad(.dst0d, .add_size, -16), ._, ._, ._ }, 11408 } }, 11409 }, .{ 11410 .required_features = .{ .@"64bit", null, null, null }, 11411 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .xword, .is = .dword } }, .any }, 11412 .patterns = &.{ 11413 .{ .src = .{ .to_mem, .none } }, 11414 }, 11415 .extra_temps = .{ 11416 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11417 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11418 .unused, 11419 .unused, 11420 .unused, 11421 .unused, 11422 .unused, 11423 .unused, 11424 .unused, 11425 }, 11426 .dst_temps = .{.mem}, 11427 .clobbers = .{ .eflags = true }, 11428 .each = .{ .once = &.{ 11429 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11430 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -16), ._, ._ }, 11431 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11432 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -16), .tmp1q, ._, ._ }, 11433 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11434 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11435 .{ ._, ._, .mov, .tmp0d, .memad(.src0d, .add_size, -16), ._, ._ }, 11436 .{ ._, ._, .not, .tmp0d, ._, ._, ._ }, 11437 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -16), .tmp0d, ._, ._ }, 11438 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -16 + 4), .si(0), ._, ._ }, 11439 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -16 + 8), .si(0), ._, ._ }, 11440 } }, 11441 }, .{ 11442 .required_features = .{ .@"64bit", null, null, null }, 11443 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .qword, .is = .qword } }, .any }, 11444 .patterns = &.{ 11445 .{ .src = .{ .mut_mem, .none } }, 11446 }, 11447 .extra_temps = .{ 11448 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11449 .unused, 11450 .unused, 11451 .unused, 11452 .unused, 11453 .unused, 11454 .unused, 11455 .unused, 11456 .unused, 11457 }, 11458 .dst_temps = .{.{ .ref = .src0 }}, 11459 .clobbers = .{ .eflags = true }, 11460 .each = .{ .once = &.{ 11461 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11462 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16), ._, ._, ._ }, 11463 .{ ._, ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16 + 8), ._, ._, ._ }, 11464 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11465 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11466 .{ ._, ._, .not, .memad(.dst0q, .add_size, -16), ._, ._, ._ }, 11467 } }, 11468 }, .{ 11469 .required_features = .{ .@"64bit", null, null, null }, 11470 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .qword, .is = .qword } }, .any }, 11471 .patterns = &.{ 11472 .{ .src = .{ .to_mem, .none } }, 11473 }, 11474 .extra_temps = .{ 11475 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11476 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11477 .unused, 11478 .unused, 11479 .unused, 11480 .unused, 11481 .unused, 11482 .unused, 11483 .unused, 11484 }, 11485 .dst_temps = .{.mem}, 11486 .clobbers = .{ .eflags = true }, 11487 .each = .{ .once = &.{ 11488 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11489 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -8), ._, ._ }, 11490 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11491 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -8), .tmp1q, ._, ._ }, 11492 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11493 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11494 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -8), .si(0), ._, ._ }, 11495 } }, 11496 }, .{ 11497 .required_features = .{ .@"64bit", null, null, null }, 11498 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .dword, .is = .dword } }, .any }, 11499 .patterns = &.{ 11500 .{ .src = .{ .mut_mem, .none } }, 11501 }, 11502 .extra_temps = .{ 11503 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11504 .unused, 11505 .unused, 11506 .unused, 11507 .unused, 11508 .unused, 11509 .unused, 11510 .unused, 11511 .unused, 11512 }, 11513 .dst_temps = .{.{ .ref = .src0 }}, 11514 .clobbers = .{ .eflags = true }, 11515 .each = .{ .once = &.{ 11516 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11517 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -8), ._, ._, ._ }, 11518 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11519 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11520 .{ ._, ._, .not, .memad(.dst0d, .add_size, -8), ._, ._, ._ }, 11521 } }, 11522 }, .{ 11523 .required_features = .{ .@"64bit", null, null, null }, 11524 .src_constraints = .{ .{ .exact_remainder_int = .{ .of = .dword, .is = .dword } }, .any }, 11525 .patterns = &.{ 11526 .{ .src = .{ .to_mem, .none } }, 11527 }, 11528 .extra_temps = .{ 11529 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11530 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11531 .unused, 11532 .unused, 11533 .unused, 11534 .unused, 11535 .unused, 11536 .unused, 11537 .unused, 11538 }, 11539 .dst_temps = .{.mem}, 11540 .clobbers = .{ .eflags = true }, 11541 .each = .{ .once = &.{ 11542 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11543 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -8), ._, ._ }, 11544 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11545 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -8), .tmp1q, ._, ._ }, 11546 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11547 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11548 .{ ._, ._, .mov, .tmp0d, .memad(.src0d, .add_size, -8), ._, ._ }, 11549 .{ ._, ._, .not, .tmp0d, ._, ._, ._ }, 11550 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -8), .tmp0d, ._, ._ }, 11551 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -8 + 4), .si(0), ._, ._ }, 11552 } }, 11553 }, .{ 11554 .required_features = .{ .@"64bit", null, null, null }, 11555 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .dword } }, .any }, 11556 .patterns = &.{ 11557 .{ .src = .{ .mut_mem, .none } }, 11558 }, 11559 .extra_temps = .{ 11560 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11561 .unused, 11562 .unused, 11563 .unused, 11564 .unused, 11565 .unused, 11566 .unused, 11567 .unused, 11568 .unused, 11569 }, 11570 .dst_temps = .{.{ .ref = .src0 }}, 11571 .clobbers = .{ .eflags = true }, 11572 .each = .{ .once = &.{ 11573 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11574 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16), ._, ._, ._ }, 11575 .{ ._, ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16 + 8), ._, ._, ._ }, 11576 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11577 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11578 .{ ._, ._, .xor, .memad(.dst0d, .add_size, -16), .sa(.src0, .add_umax), ._, ._ }, 11579 } }, 11580 }, .{ 11581 .required_features = .{ .@"64bit", null, null, null }, 11582 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .dword } }, .any }, 11583 .patterns = &.{ 11584 .{ .src = .{ .to_mem, .none } }, 11585 }, 11586 .extra_temps = .{ 11587 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11588 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11589 .unused, 11590 .unused, 11591 .unused, 11592 .unused, 11593 .unused, 11594 .unused, 11595 .unused, 11596 }, 11597 .dst_temps = .{.mem}, 11598 .clobbers = .{ .eflags = true }, 11599 .each = .{ .once = &.{ 11600 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11601 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -16), ._, ._ }, 11602 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11603 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -16), .tmp1q, ._, ._ }, 11604 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11605 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11606 .{ ._, ._, .mov, .tmp0d, .memad(.src0d, .add_size, -16), ._, ._ }, 11607 .{ ._, ._, .xor, .tmp0d, .sa(.src0, .add_umax), ._, ._ }, 11608 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -16), .tmp0d, ._, ._ }, 11609 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -16 + 4), .si(0), ._, ._ }, 11610 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -16 + 8), .si(0), ._, ._ }, 11611 } }, 11612 }, .{ 11613 .required_features = .{ .@"64bit", null, null, null }, 11614 .src_constraints = .{ .{ .remainder_int = .{ .of = .qword, .is = .dword } }, .any }, 11615 .patterns = &.{ 11616 .{ .src = .{ .mut_mem, .none } }, 11617 }, 11618 .extra_temps = .{ 11619 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11620 .unused, 11621 .unused, 11622 .unused, 11623 .unused, 11624 .unused, 11625 .unused, 11626 .unused, 11627 .unused, 11628 }, 11629 .dst_temps = .{.{ .ref = .src0 }}, 11630 .clobbers = .{ .eflags = true }, 11631 .each = .{ .once = &.{ 11632 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11633 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -8), ._, ._, ._ }, 11634 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11635 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11636 .{ ._, ._, .xor, .memad(.dst0d, .add_size, -8), .sa(.src0, .add_umax), ._, ._ }, 11637 } }, 11638 }, .{ 11639 .required_features = .{ .@"64bit", null, null, null }, 11640 .src_constraints = .{ .{ .remainder_int = .{ .of = .qword, .is = .dword } }, .any }, 11641 .patterns = &.{ 11642 .{ .src = .{ .to_mem, .none } }, 11643 }, 11644 .extra_temps = .{ 11645 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11646 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11647 .unused, 11648 .unused, 11649 .unused, 11650 .unused, 11651 .unused, 11652 .unused, 11653 .unused, 11654 }, 11655 .dst_temps = .{.mem}, 11656 .clobbers = .{ .eflags = true }, 11657 .each = .{ .once = &.{ 11658 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11659 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -8), ._, ._ }, 11660 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11661 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -8), .tmp1q, ._, ._ }, 11662 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11663 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11664 .{ ._, ._, .mov, .tmp0d, .memad(.src0d, .add_size, -8), ._, ._ }, 11665 .{ ._, ._, .xor, .tmp0d, .sa(.src0, .add_umax), ._, ._ }, 11666 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -8), .tmp0d, ._, ._ }, 11667 .{ ._, ._, .mov, .memad(.dst0d, .add_size, -8 + 4), .si(0), ._, ._ }, 11668 } }, 11669 }, .{ 11670 .required_features = .{ .@"64bit", null, null, null }, 11671 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 11672 .patterns = &.{ 11673 .{ .src = .{ .mut_mem, .none } }, 11674 }, 11675 .extra_temps = .{ 11676 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11677 .unused, 11678 .unused, 11679 .unused, 11680 .unused, 11681 .unused, 11682 .unused, 11683 .unused, 11684 .unused, 11685 }, 11686 .dst_temps = .{.{ .ref = .src0 }}, 11687 .clobbers = .{ .eflags = true }, 11688 .each = .{ .once = &.{ 11689 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11690 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16), ._, ._, ._ }, 11691 .{ ._, ._, .not, .memiad(.dst0q, .tmp0, .add_size, -16 + 8), ._, ._, ._ }, 11692 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 11693 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11694 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 11695 .{ ._, ._, .xor, .memad(.dst0q, .add_size, -16), .tmp0q, ._, ._ }, 11696 } }, 11697 }, .{ 11698 .required_features = .{ .@"64bit", null, null, null }, 11699 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 11700 .patterns = &.{ 11701 .{ .src = .{ .to_mem, .none } }, 11702 }, 11703 .extra_temps = .{ 11704 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11705 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11706 .unused, 11707 .unused, 11708 .unused, 11709 .unused, 11710 .unused, 11711 .unused, 11712 .unused, 11713 }, 11714 .dst_temps = .{.mem}, 11715 .clobbers = .{ .eflags = true }, 11716 .each = .{ .once = &.{ 11717 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 11718 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -16), ._, ._ }, 11719 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11720 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -16), .tmp1q, ._, ._ }, 11721 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11722 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11723 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 11724 .{ ._, ._, .xor, .tmp0q, .memad(.src0q, .add_size, -16), ._, ._ }, 11725 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -16), .tmp0q, ._, ._ }, 11726 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -8), .si(0), ._, ._ }, 11727 } }, 11728 }, .{ 11729 .required_features = .{ .@"64bit", null, null, null }, 11730 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11731 .patterns = &.{ 11732 .{ .src = .{ .mut_mem, .none } }, 11733 }, 11734 .extra_temps = .{ 11735 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11736 .unused, 11737 .unused, 11738 .unused, 11739 .unused, 11740 .unused, 11741 .unused, 11742 .unused, 11743 .unused, 11744 }, 11745 .dst_temps = .{.{ .ref = .src0 }}, 11746 .clobbers = .{ .eflags = true }, 11747 .each = .{ .once = &.{ 11748 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11749 .{ .@"0:", ._, .not, .memiad(.dst0q, .tmp0, .add_size, -8), ._, ._, ._ }, 11750 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11751 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11752 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 11753 .{ ._, ._, .xor, .memad(.dst0q, .add_size, -8), .tmp0q, ._, ._ }, 11754 } }, 11755 }, .{ 11756 .required_features = .{ .@"64bit", null, null, null }, 11757 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 11758 .patterns = &.{ 11759 .{ .src = .{ .to_mem, .none } }, 11760 }, 11761 .extra_temps = .{ 11762 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11763 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11764 .unused, 11765 .unused, 11766 .unused, 11767 .unused, 11768 .unused, 11769 .unused, 11770 .unused, 11771 }, 11772 .dst_temps = .{.mem}, 11773 .clobbers = .{ .eflags = true }, 11774 .each = .{ .once = &.{ 11775 .{ ._, ._, .mov, .tmp0p, .sia(8, .src0, .sub_size), ._, ._ }, 11776 .{ .@"0:", ._, .mov, .tmp1q, .memiad(.src0q, .tmp0, .add_size, -8), ._, ._ }, 11777 .{ ._, ._, .not, .tmp1q, ._, ._, ._ }, 11778 .{ ._, ._, .mov, .memiad(.dst0q, .tmp0, .add_size, -8), .tmp1q, ._, ._ }, 11779 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 11780 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 11781 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 11782 .{ ._, ._, .xor, .tmp0q, .memad(.src0q, .add_size, -8), ._, ._ }, 11783 .{ ._, ._, .mov, .memad(.dst0q, .add_size, -8), .tmp0q, ._, ._ }, 11784 } }, 11785 }, .{ 11786 .required_features = .{ .mmx, null, null, null }, 11787 .src_constraints = .{ .{ .signed_int_or_full_vec = .qword }, .any }, 11788 .patterns = &.{ 11789 .{ .src = .{ .mem, .none } }, 11790 .{ .src = .{ .to_mm, .none } }, 11791 }, 11792 .dst_temps = .{.{ .rc = .mmx }}, 11793 .each = .{ .once = &.{ 11794 .{ ._, .p_d, .cmpeq, .dst0q, .dst0q, ._, ._ }, 11795 .{ ._, .p_, .xor, .dst0q, .src0q, ._, ._ }, 11796 } }, 11797 }, .{ 11798 .required_features = .{ .mmx, null, null, null }, 11799 .src_constraints = .{ .{ .unsigned_int_vec = .qword }, .any }, 11800 .patterns = &.{ 11801 .{ .src = .{ .to_mut_mm, .none } }, 11802 }, 11803 .extra_temps = .{ 11804 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11805 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11806 .unused, 11807 .unused, 11808 .unused, 11809 .unused, 11810 .unused, 11811 .unused, 11812 .unused, 11813 }, 11814 .dst_temps = .{.{ .ref = .src0 }}, 11815 .each = .{ .once = &.{ 11816 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11817 .{ ._, .p_, .xor, .dst0q, .lea(.qword, .tmp0), ._, ._ }, 11818 } }, 11819 }, .{ 11820 .required_features = .{ .avx, null, null, null }, 11821 .src_constraints = .{ .{ .signed_int_or_full_vec = .xword }, .any }, 11822 .patterns = &.{ 11823 .{ .src = .{ .mem, .none } }, 11824 .{ .src = .{ .to_xmm, .none } }, 11825 }, 11826 .dst_temps = .{.{ .rc = .sse }}, 11827 .each = .{ .once = &.{ 11828 .{ ._, .vp_q, .cmpeq, .dst0x, .dst0x, .dst0x, ._ }, 11829 .{ ._, .vp_, .xor, .dst0x, .dst0x, .src0x, ._ }, 11830 } }, 11831 }, .{ 11832 .required_features = .{ .avx, null, null, null }, 11833 .src_constraints = .{ .{ .unsigned_int_vec = .xword }, .any }, 11834 .patterns = &.{ 11835 .{ .src = .{ .to_xmm, .none } }, 11836 }, 11837 .extra_temps = .{ 11838 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11839 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11840 .unused, 11841 .unused, 11842 .unused, 11843 .unused, 11844 .unused, 11845 .unused, 11846 .unused, 11847 }, 11848 .dst_temps = .{.{ .rc = .sse }}, 11849 .each = .{ .once = &.{ 11850 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11851 .{ ._, .vp_, .xor, .dst0x, .src0x, .lea(.xword, .tmp0), ._ }, 11852 } }, 11853 }, .{ 11854 .required_features = .{ .sse2, null, null, null }, 11855 .src_constraints = .{ .{ .signed_int_or_full_vec = .xword }, .any }, 11856 .patterns = &.{ 11857 .{ .src = .{ .mem, .none } }, 11858 .{ .src = .{ .to_xmm, .none } }, 11859 }, 11860 .dst_temps = .{.{ .rc = .sse }}, 11861 .each = .{ .once = &.{ 11862 .{ ._, .p_d, .cmpeq, .dst0x, .dst0x, ._, ._ }, 11863 .{ ._, .p_, .xor, .dst0x, .src0x, ._, ._ }, 11864 } }, 11865 }, .{ 11866 .required_features = .{ .sse2, null, null, null }, 11867 .src_constraints = .{ .{ .unsigned_int_vec = .xword }, .any }, 11868 .patterns = &.{ 11869 .{ .src = .{ .to_mut_xmm, .none } }, 11870 }, 11871 .extra_temps = .{ 11872 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11873 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11874 .unused, 11875 .unused, 11876 .unused, 11877 .unused, 11878 .unused, 11879 .unused, 11880 .unused, 11881 }, 11882 .dst_temps = .{.{ .ref = .src0 }}, 11883 .each = .{ .once = &.{ 11884 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11885 .{ ._, .p_, .xor, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 11886 } }, 11887 }, .{ 11888 .required_features = .{ .sse, null, null, null }, 11889 .src_constraints = .{ .{ .vec = .xword }, .any }, 11890 .patterns = &.{ 11891 .{ .src = .{ .to_mut_xmm, .none } }, 11892 }, 11893 .extra_temps = .{ 11894 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11895 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11896 .unused, 11897 .unused, 11898 .unused, 11899 .unused, 11900 .unused, 11901 .unused, 11902 .unused, 11903 }, 11904 .dst_temps = .{.{ .ref = .src0 }}, 11905 .each = .{ .once = &.{ 11906 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11907 .{ ._, ._ps, .xor, .dst0x, .lea(.xword, .tmp0), ._, ._ }, 11908 } }, 11909 }, .{ 11910 .required_features = .{ .avx2, null, null, null }, 11911 .src_constraints = .{ .{ .signed_int_or_full_vec = .yword }, .any }, 11912 .patterns = &.{ 11913 .{ .src = .{ .mem, .none } }, 11914 .{ .src = .{ .to_ymm, .none } }, 11915 }, 11916 .dst_temps = .{.{ .rc = .sse }}, 11917 .each = .{ .once = &.{ 11918 .{ ._, .vp_q, .cmpeq, .dst0y, .dst0y, .dst0y, ._ }, 11919 .{ ._, .vp_, .xor, .dst0y, .dst0y, .src0y, ._ }, 11920 } }, 11921 }, .{ 11922 .required_features = .{ .avx2, null, null, null }, 11923 .src_constraints = .{ .{ .unsigned_int_vec = .yword }, .any }, 11924 .patterns = &.{ 11925 .{ .src = .{ .to_ymm, .none } }, 11926 }, 11927 .extra_temps = .{ 11928 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11929 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11930 .unused, 11931 .unused, 11932 .unused, 11933 .unused, 11934 .unused, 11935 .unused, 11936 .unused, 11937 }, 11938 .dst_temps = .{.{ .rc = .sse }}, 11939 .each = .{ .once = &.{ 11940 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11941 .{ ._, .vp_, .xor, .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 11942 } }, 11943 }, .{ 11944 .required_features = .{ .avx, null, null, null }, 11945 .src_constraints = .{ .{ .signed_int_or_full_vec = .yword }, .any }, 11946 .patterns = &.{ 11947 .{ .src = .{ .mem, .none } }, 11948 .{ .src = .{ .to_ymm, .none } }, 11949 }, 11950 .dst_temps = .{.{ .rc = .sse }}, 11951 .each = .{ .once = &.{ 11952 .{ ._, .v_pd, .cmp, .dst0y, .dst0y, .dst0y, .vp(.true) }, 11953 .{ ._, .v_pd, .xor, .dst0y, .dst0y, .src0y, ._ }, 11954 } }, 11955 }, .{ 11956 .required_features = .{ .avx, null, null, null }, 11957 .src_constraints = .{ .{ .unsigned_int_vec = .yword }, .any }, 11958 .patterns = &.{ 11959 .{ .src = .{ .to_ymm, .none } }, 11960 }, 11961 .extra_temps = .{ 11962 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11963 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11964 .unused, 11965 .unused, 11966 .unused, 11967 .unused, 11968 .unused, 11969 .unused, 11970 .unused, 11971 }, 11972 .dst_temps = .{.{ .rc = .sse }}, 11973 .each = .{ .once = &.{ 11974 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 11975 .{ ._, .v_pd, .xor, .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 11976 } }, 11977 }, .{ 11978 .required_features = .{ .@"64bit", null, null, null }, 11979 .patterns = &.{ 11980 .{ .src = .{ .to_mem, .none } }, 11981 }, 11982 .extra_temps = .{ 11983 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 11984 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 11985 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 11986 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 11987 .unused, 11988 .unused, 11989 .unused, 11990 .unused, 11991 .unused, 11992 }, 11993 .dst_temps = .{.mem}, 11994 .each = .{ .once = &.{ 11995 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_src0_unaligned_size), ._, ._ }, 11996 .{ ._, ._, .lea, .tmp1p, .mem(.tmp3), ._, ._ }, 11997 .{ .@"0:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_src0_unaligned_size), ._, ._ }, 11998 .{ ._, ._, .xor, .tmp2q, .leaia(.qword, .tmp1, .tmp0, .add_src0_unaligned_size), ._, ._ }, 11999 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_src0_unaligned_size), .tmp2q, ._, ._ }, 12000 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 12001 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 12002 } }, 12003 }, .{ 12004 .patterns = &.{ 12005 .{ .src = .{ .to_mem, .none } }, 12006 }, 12007 .extra_temps = .{ 12008 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 12009 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 12010 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12011 .{ .kind = .{ .umax_mem = .{ .ref = .src0 } } }, 12012 .unused, 12013 .unused, 12014 .unused, 12015 .unused, 12016 .unused, 12017 }, 12018 .dst_temps = .{.mem}, 12019 .each = .{ .once = &.{ 12020 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_src0_unaligned_size), ._, ._ }, 12021 .{ ._, ._, .lea, .tmp1p, .mem(.tmp3), ._, ._ }, 12022 .{ .@"0:", ._, .mov, .tmp2d, .memia(.src0d, .tmp0, .add_src0_unaligned_size), ._, ._ }, 12023 .{ ._, ._, .xor, .tmp2d, .leaia(.dword, .tmp1, .tmp0, .add_src0_unaligned_size), ._, ._ }, 12024 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_src0_unaligned_size), .tmp2d, ._, ._ }, 12025 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 12026 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 12027 } }, 12028 } }) catch |err| switch (err) { 12029 error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{ 12030 @tagName(air_tag), 12031 cg.typeOf(ty_op.operand).fmt(pt), 12032 ops[0].tracking(cg), 12033 }), 12034 else => |e| return e, 12035 }; 12036 try res[0].finish(inst, &.{ty_op.operand}, &ops, cg); 12037 }, 12038 12039 .block => { 12040 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 12041 const extra = cg.air.extraData(Air.Block, ty_pl.payload); 12042 if (cg.debug_output != .none) try cg.asmPseudo(.pseudo_dbg_enter_block_none); 12043 try cg.lowerBlock(inst, @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len])); 12044 if (cg.debug_output != .none) try cg.asmPseudo(.pseudo_dbg_leave_block_none); 12045 }, 12046 .loop => if (use_old) try cg.airLoop(inst) else { 12047 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 12048 const extra = cg.air.extraData(Air.Block, ty_pl.payload); 12049 try cg.loops.putNoClobber(cg.gpa, inst, .{ 12050 .state = try cg.saveState(), 12051 .target = @intCast(cg.mir_instructions.len), 12052 }); 12053 defer assert(cg.loops.remove(inst)); 12054 try cg.genBodyBlock(@ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len])); 12055 }, 12056 .repeat => if (use_old) try cg.airRepeat(inst) else { 12057 const repeat = air_datas[@intFromEnum(inst)].repeat; 12058 const loop = cg.loops.get(repeat.loop_inst).?; 12059 try cg.restoreState(loop.state, &.{}, .{ 12060 .emit_instructions = true, 12061 .update_tracking = false, 12062 .resurrect = false, 12063 .close_scope = true, 12064 }); 12065 _ = try cg.asmJmpReloc(loop.target); 12066 }, 12067 .br => try cg.airBr(inst), 12068 .trap => try cg.asmOpOnly(.{ ._2, .ud }), 12069 .breakpoint => try cg.asmOpOnly(.{ ._3, .int }), 12070 .ret_addr => if (use_old) try cg.airRetAddr(inst) else { 12071 var slot = try cg.tempInit(.usize, .{ .load_frame = .{ 12072 .index = .ret_addr, 12073 } }); 12074 while (try slot.toRegClass(true, .general_purpose, cg)) {} 12075 try slot.finish(inst, &.{}, &.{}, cg); 12076 }, 12077 .frame_addr => if (use_old) try cg.airFrameAddress(inst) else { 12078 const slot = try cg.tempInit(.usize, .{ .lea_frame = .{ 12079 .index = .base_ptr, 12080 } }); 12081 try slot.finish(inst, &.{}, &.{}, cg); 12082 }, 12083 .call => try cg.airCall(inst, .auto, .{ .safety = true }), 12084 .call_always_tail => try cg.airCall(inst, .always_tail, .{ .safety = true }), 12085 .call_never_tail => try cg.airCall(inst, .never_tail, .{ .safety = true }), 12086 .call_never_inline => try cg.airCall(inst, .never_inline, .{ .safety = true }), 12087 12088 .clz => |air_tag| if (use_old) try cg.airClz(inst) else { 12089 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 12090 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 12091 var res: [1]Temp = undefined; 12092 cg.select(&res, &.{ty_op.ty.toType()}, &ops, comptime &.{ .{ 12093 .required_features = .{ .slow_incdec, null, null, null }, 12094 .src_constraints = .{ .{ .exact_signed_int = 1 }, .any }, 12095 .patterns = &.{ 12096 .{ .src = .{ .mut_mem, .none } }, 12097 .{ .src = .{ .to_mut_gpr, .none } }, 12098 }, 12099 .dst_temps = .{.{ .ref = .src0 }}, 12100 .clobbers = .{ .eflags = true }, 12101 .each = .{ .once = &.{ 12102 .{ ._, ._, .add, .dst0b, .si(1), ._, ._ }, 12103 } }, 12104 }, .{ 12105 .src_constraints = .{ .{ .exact_signed_int = 1 }, .any }, 12106 .patterns = &.{ 12107 .{ .src = .{ .mut_mem, .none } }, 12108 .{ .src = .{ .to_mut_gpr, .none } }, 12109 }, 12110 .dst_temps = .{.{ .ref = .src0 }}, 12111 .clobbers = .{ .eflags = true }, 12112 .each = .{ .once = &.{ 12113 .{ ._, ._c, .in, .dst0b, ._, ._, ._ }, 12114 } }, 12115 }, .{ 12116 .src_constraints = .{ .{ .exact_unsigned_int = 1 }, .any }, 12117 .patterns = &.{ 12118 .{ .src = .{ .mut_mem, .none } }, 12119 .{ .src = .{ .to_mut_gpr, .none } }, 12120 }, 12121 .dst_temps = .{.{ .ref = .src0 }}, 12122 .clobbers = .{ .eflags = true }, 12123 .each = .{ .once = &.{ 12124 .{ ._, ._, .xor, .dst0b, .si(1), ._, ._ }, 12125 } }, 12126 }, .{ 12127 .required_features = .{ .lzcnt, null, null, null }, 12128 .src_constraints = .{ .{ .unsigned_or_exact_int = .byte }, .any }, 12129 .patterns = &.{ 12130 .{ .src = .{ .mem, .none } }, 12131 .{ .src = .{ .to_gpr, .none } }, 12132 }, 12133 .dst_temps = .{.{ .rc = .general_purpose }}, 12134 .clobbers = .{ .eflags = true }, 12135 .each = .{ .once = &.{ 12136 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12137 .{ ._, ._, .lzcnt, .dst0d, .dst0d, ._, ._ }, 12138 .{ ._, ._, .sub, .dst0b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 12139 } }, 12140 }, .{ 12141 .required_features = .{ .lzcnt, null, null, null }, 12142 .src_constraints = .{ .{ .signed_int = .byte }, .any }, 12143 .patterns = &.{ 12144 .{ .src = .{ .mem, .none } }, 12145 .{ .src = .{ .to_gpr, .none } }, 12146 }, 12147 .dst_temps = .{.{ .rc = .general_purpose }}, 12148 .clobbers = .{ .eflags = true }, 12149 .each = .{ .once = &.{ 12150 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12151 .{ ._, ._, .@"and", .dst0d, .sa(.src0, .add_umax), ._, ._ }, 12152 .{ ._, ._, .lzcnt, .dst0d, .dst0d, ._, ._ }, 12153 .{ ._, ._, .sub, .dst0b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 12154 } }, 12155 }, .{ 12156 .required_features = .{ .false_deps_lzcnt_tzcnt, .lzcnt, null, null }, 12157 .src_constraints = .{ .{ .exact_int = 16 }, .any }, 12158 .patterns = &.{ 12159 .{ .src = .{ .to_mut_gpr, .none } }, 12160 }, 12161 .dst_temps = .{.{ .ref = .src0 }}, 12162 .clobbers = .{ .eflags = true }, 12163 .each = .{ .once = &.{ 12164 .{ ._, ._, .lzcnt, .dst0w, .src0w, ._, ._ }, 12165 } }, 12166 }, .{ 12167 .required_features = .{ .lzcnt, null, null, null }, 12168 .src_constraints = .{ .{ .exact_int = 16 }, .any }, 12169 .patterns = &.{ 12170 .{ .src = .{ .mem, .none } }, 12171 .{ .src = .{ .to_gpr, .none } }, 12172 }, 12173 .dst_temps = .{.{ .rc = .general_purpose }}, 12174 .clobbers = .{ .eflags = true }, 12175 .each = .{ .once = &.{ 12176 .{ ._, ._, .lzcnt, .dst0w, .src0w, ._, ._ }, 12177 } }, 12178 }, .{ 12179 .required_features = .{ .lzcnt, null, null, null }, 12180 .src_constraints = .{ .{ .signed_int = .word }, .any }, 12181 .patterns = &.{ 12182 .{ .src = .{ .to_mut_gpr, .none } }, 12183 }, 12184 .dst_temps = .{.{ .ref = .src0 }}, 12185 .clobbers = .{ .eflags = true }, 12186 .each = .{ .once = &.{ 12187 .{ ._, ._, .@"and", .src0w, .sa(.src0, .add_umax), ._, ._ }, 12188 .{ ._, ._, .lzcnt, .dst0w, .src0w, ._, ._ }, 12189 .{ ._, ._, .sub, .dst0b, .sia(16, .src0, .sub_bit_size), ._, ._ }, 12190 } }, 12191 }, .{ 12192 .required_features = .{ .false_deps_lzcnt_tzcnt, .lzcnt, null, null }, 12193 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 12194 .patterns = &.{ 12195 .{ .src = .{ .to_mut_gpr, .none } }, 12196 }, 12197 .dst_temps = .{.{ .ref = .src0 }}, 12198 .clobbers = .{ .eflags = true }, 12199 .each = .{ .once = &.{ 12200 .{ ._, ._, .lzcnt, .dst0w, .src0w, ._, ._ }, 12201 .{ ._, ._, .sub, .dst0b, .sia(16, .src0, .sub_bit_size), ._, ._ }, 12202 } }, 12203 }, .{ 12204 .required_features = .{ .lzcnt, null, null, null }, 12205 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 12206 .patterns = &.{ 12207 .{ .src = .{ .mem, .none } }, 12208 .{ .src = .{ .to_gpr, .none } }, 12209 }, 12210 .dst_temps = .{.{ .rc = .general_purpose }}, 12211 .clobbers = .{ .eflags = true }, 12212 .each = .{ .once = &.{ 12213 .{ ._, ._, .lzcnt, .dst0w, .src0w, ._, ._ }, 12214 .{ ._, ._, .sub, .dst0b, .sia(16, .src0, .sub_bit_size), ._, ._ }, 12215 } }, 12216 }, .{ 12217 .required_features = .{ .false_deps_lzcnt_tzcnt, .lzcnt, null, null }, 12218 .src_constraints = .{ .{ .exact_int = 32 }, .any }, 12219 .patterns = &.{ 12220 .{ .src = .{ .to_mut_gpr, .none } }, 12221 }, 12222 .dst_temps = .{.{ .ref = .src0 }}, 12223 .clobbers = .{ .eflags = true }, 12224 .each = .{ .once = &.{ 12225 .{ ._, ._, .lzcnt, .dst0d, .src0d, ._, ._ }, 12226 } }, 12227 }, .{ 12228 .required_features = .{ .lzcnt, null, null, null }, 12229 .src_constraints = .{ .{ .exact_int = 32 }, .any }, 12230 .patterns = &.{ 12231 .{ .src = .{ .mem, .none } }, 12232 .{ .src = .{ .to_gpr, .none } }, 12233 }, 12234 .dst_temps = .{.{ .rc = .general_purpose }}, 12235 .clobbers = .{ .eflags = true }, 12236 .each = .{ .once = &.{ 12237 .{ ._, ._, .lzcnt, .dst0d, .src0d, ._, ._ }, 12238 } }, 12239 }, .{ 12240 .required_features = .{ .lzcnt, null, null, null }, 12241 .src_constraints = .{ .{ .signed_int = .dword }, .any }, 12242 .patterns = &.{ 12243 .{ .src = .{ .to_mut_gpr, .none } }, 12244 }, 12245 .dst_temps = .{.{ .ref = .src0 }}, 12246 .clobbers = .{ .eflags = true }, 12247 .each = .{ .once = &.{ 12248 .{ ._, ._, .@"and", .src0d, .sa(.src0, .add_umax), ._, ._ }, 12249 .{ ._, ._, .lzcnt, .dst0d, .src0d, ._, ._ }, 12250 .{ ._, ._, .sub, .dst0b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 12251 } }, 12252 }, .{ 12253 .required_features = .{ .false_deps_lzcnt_tzcnt, .lzcnt, null, null }, 12254 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 12255 .patterns = &.{ 12256 .{ .src = .{ .to_mut_gpr, .none } }, 12257 }, 12258 .dst_temps = .{.{ .ref = .src0 }}, 12259 .clobbers = .{ .eflags = true }, 12260 .each = .{ .once = &.{ 12261 .{ ._, ._, .lzcnt, .dst0d, .src0d, ._, ._ }, 12262 .{ ._, ._, .sub, .dst0b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 12263 } }, 12264 }, .{ 12265 .required_features = .{ .lzcnt, null, null, null }, 12266 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 12267 .patterns = &.{ 12268 .{ .src = .{ .mem, .none } }, 12269 .{ .src = .{ .to_gpr, .none } }, 12270 }, 12271 .dst_temps = .{.{ .rc = .general_purpose }}, 12272 .clobbers = .{ .eflags = true }, 12273 .each = .{ .once = &.{ 12274 .{ ._, ._, .lzcnt, .dst0d, .src0d, ._, ._ }, 12275 .{ ._, ._, .sub, .dst0b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 12276 } }, 12277 }, .{ 12278 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 12279 .src_constraints = .{ .{ .exact_int = 64 }, .any }, 12280 .patterns = &.{ 12281 .{ .src = .{ .to_mut_gpr, .none } }, 12282 }, 12283 .dst_temps = .{.{ .ref = .src0 }}, 12284 .clobbers = .{ .eflags = true }, 12285 .each = .{ .once = &.{ 12286 .{ ._, ._, .lzcnt, .dst0q, .src0q, ._, ._ }, 12287 } }, 12288 }, .{ 12289 .required_features = .{ .@"64bit", .lzcnt, null, null }, 12290 .src_constraints = .{ .{ .exact_int = 64 }, .any }, 12291 .patterns = &.{ 12292 .{ .src = .{ .mem, .none } }, 12293 .{ .src = .{ .to_gpr, .none } }, 12294 }, 12295 .dst_temps = .{.{ .rc = .general_purpose }}, 12296 .clobbers = .{ .eflags = true }, 12297 .each = .{ .once = &.{ 12298 .{ ._, ._, .lzcnt, .dst0q, .src0q, ._, ._ }, 12299 } }, 12300 }, .{ 12301 .required_features = .{ .@"64bit", .lzcnt, null, null }, 12302 .src_constraints = .{ .{ .signed_int = .qword }, .any }, 12303 .patterns = &.{ 12304 .{ .src = .{ .mem, .none } }, 12305 .{ .src = .{ .to_gpr, .none } }, 12306 }, 12307 .dst_temps = .{.{ .rc = .general_purpose }}, 12308 .clobbers = .{ .eflags = true }, 12309 .each = .{ .once = &.{ 12310 .{ ._, ._, .mov, .dst0q, .ua(.src0, .add_umax), ._, ._ }, 12311 .{ ._, ._, .@"and", .dst0q, .src0q, ._, ._ }, 12312 .{ ._, ._, .lzcnt, .dst0q, .dst0q, ._, ._ }, 12313 .{ ._, ._, .sub, .dst0b, .sia(64, .src0, .sub_bit_size), ._, ._ }, 12314 } }, 12315 }, .{ 12316 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 12317 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 12318 .patterns = &.{ 12319 .{ .src = .{ .to_mut_gpr, .none } }, 12320 }, 12321 .dst_temps = .{.{ .ref = .src0 }}, 12322 .clobbers = .{ .eflags = true }, 12323 .each = .{ .once = &.{ 12324 .{ ._, ._, .lzcnt, .dst0q, .src0q, ._, ._ }, 12325 .{ ._, ._, .sub, .dst0b, .sia(64, .src0, .sub_bit_size), ._, ._ }, 12326 } }, 12327 }, .{ 12328 .required_features = .{ .@"64bit", .lzcnt, null, null }, 12329 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 12330 .patterns = &.{ 12331 .{ .src = .{ .mem, .none } }, 12332 .{ .src = .{ .to_gpr, .none } }, 12333 }, 12334 .dst_temps = .{.{ .rc = .general_purpose }}, 12335 .clobbers = .{ .eflags = true }, 12336 .each = .{ .once = &.{ 12337 .{ ._, ._, .lzcnt, .dst0q, .src0q, ._, ._ }, 12338 .{ ._, ._, .sub, .dst0b, .sia(64, .src0, .sub_bit_size), ._, ._ }, 12339 } }, 12340 }, .{ 12341 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12342 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .byte }, .any }, 12343 .patterns = &.{ 12344 .{ .src = .{ .mem, .none } }, 12345 .{ .src = .{ .to_gpr, .none } }, 12346 }, 12347 .extra_temps = .{ 12348 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12349 .unused, 12350 .unused, 12351 .unused, 12352 .unused, 12353 .unused, 12354 .unused, 12355 .unused, 12356 .unused, 12357 }, 12358 .dst_temps = .{.{ .rc = .general_purpose }}, 12359 .clobbers = .{ .eflags = true }, 12360 .each = .{ .once = &.{ 12361 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12362 .{ ._, ._r, .bs, .dst0d, .dst0d, ._, ._ }, 12363 .{ ._, ._, .mov, .tmp0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12364 .{ ._, ._z, .cmov, .dst0d, .tmp0d, ._, ._ }, 12365 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12366 } }, 12367 }, .{ 12368 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12369 .src_constraints = .{ .{ .signed_po2_int = .byte }, .any }, 12370 .patterns = &.{ 12371 .{ .src = .{ .mem, .none } }, 12372 .{ .src = .{ .to_gpr, .none } }, 12373 }, 12374 .extra_temps = .{ 12375 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12376 .unused, 12377 .unused, 12378 .unused, 12379 .unused, 12380 .unused, 12381 .unused, 12382 .unused, 12383 .unused, 12384 }, 12385 .dst_temps = .{.{ .rc = .general_purpose }}, 12386 .clobbers = .{ .eflags = true }, 12387 .each = .{ .once = &.{ 12388 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12389 .{ ._, ._, .@"and", .dst0d, .sa(.src0, .add_umax), ._, ._ }, 12390 .{ ._, ._r, .bs, .dst0d, .dst0d, ._, ._ }, 12391 .{ ._, ._, .mov, .tmp0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12392 .{ ._, ._z, .cmov, .dst0d, .tmp0d, ._, ._ }, 12393 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12394 } }, 12395 }, .{ 12396 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12397 .src_constraints = .{ .{ .signed_int = .byte }, .any }, 12398 .patterns = &.{ 12399 .{ .src = .{ .mem, .none } }, 12400 .{ .src = .{ .to_gpr, .none } }, 12401 }, 12402 .extra_temps = .{ 12403 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12404 .unused, 12405 .unused, 12406 .unused, 12407 .unused, 12408 .unused, 12409 .unused, 12410 .unused, 12411 .unused, 12412 }, 12413 .dst_temps = .{.{ .rc = .general_purpose }}, 12414 .clobbers = .{ .eflags = true }, 12415 .each = .{ .once = &.{ 12416 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12417 .{ ._, ._, .@"and", .tmp0d, .sa(.src0, .add_umax), ._, ._ }, 12418 .{ ._, ._r, .bs, .tmp0d, .tmp0d, ._, ._ }, 12419 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 12420 .{ ._, ._z, .cmov, .tmp0d, .dst0d, ._, ._ }, 12421 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12422 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12423 } }, 12424 }, .{ 12425 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12426 .src_constraints = .{ .{ .unsigned_int = .byte }, .any }, 12427 .patterns = &.{ 12428 .{ .src = .{ .mem, .none } }, 12429 .{ .src = .{ .to_gpr, .none } }, 12430 }, 12431 .extra_temps = .{ 12432 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12433 .unused, 12434 .unused, 12435 .unused, 12436 .unused, 12437 .unused, 12438 .unused, 12439 .unused, 12440 .unused, 12441 }, 12442 .dst_temps = .{.{ .rc = .general_purpose }}, 12443 .clobbers = .{ .eflags = true }, 12444 .each = .{ .once = &.{ 12445 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12446 .{ ._, ._r, .bs, .tmp0d, .tmp0d, ._, ._ }, 12447 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 12448 .{ ._, ._z, .cmov, .tmp0d, .dst0d, ._, ._ }, 12449 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12450 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12451 } }, 12452 }, .{ 12453 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12454 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .byte }, .any }, 12455 .patterns = &.{ 12456 .{ .src = .{ .mem, .none } }, 12457 .{ .src = .{ .to_gpr, .none } }, 12458 }, 12459 .dst_temps = .{.{ .rc = .general_purpose }}, 12460 .clobbers = .{ .eflags = true }, 12461 .each = .{ .once = &.{ 12462 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12463 .{ ._, ._r, .bs, .dst0d, .dst0d, ._, ._ }, 12464 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 12465 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12466 .{ .@"0:", ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12467 } }, 12468 }, .{ 12469 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12470 .src_constraints = .{ .{ .signed_po2_int = .byte }, .any }, 12471 .patterns = &.{ 12472 .{ .src = .{ .mem, .none } }, 12473 .{ .src = .{ .to_gpr, .none } }, 12474 }, 12475 .dst_temps = .{.{ .rc = .general_purpose }}, 12476 .clobbers = .{ .eflags = true }, 12477 .each = .{ .once = &.{ 12478 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12479 .{ ._, ._, .@"and", .dst0d, .sa(.src0, .add_umax), ._, ._ }, 12480 .{ ._, ._r, .bs, .dst0d, .dst0d, ._, ._ }, 12481 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 12482 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12483 .{ .@"0:", ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12484 } }, 12485 }, .{ 12486 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12487 .src_constraints = .{ .{ .signed_int = .byte }, .any }, 12488 .patterns = &.{ 12489 .{ .src = .{ .mem, .none } }, 12490 .{ .src = .{ .to_gpr, .none } }, 12491 }, 12492 .extra_temps = .{ 12493 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12494 .unused, 12495 .unused, 12496 .unused, 12497 .unused, 12498 .unused, 12499 .unused, 12500 .unused, 12501 .unused, 12502 }, 12503 .dst_temps = .{.{ .rc = .general_purpose }}, 12504 .clobbers = .{ .eflags = true }, 12505 .each = .{ .once = &.{ 12506 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12507 .{ ._, ._, .@"and", .tmp0d, .sa(.src0, .add_umax), ._, ._ }, 12508 .{ ._, ._r, .bs, .tmp0d, .tmp0d, ._, ._ }, 12509 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12510 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12511 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12512 .{ ._, ._, .sbb, .dst0b, .tmp0b, ._, ._ }, 12513 } }, 12514 }, .{ 12515 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12516 .src_constraints = .{ .{ .unsigned_int = .byte }, .any }, 12517 .patterns = &.{ 12518 .{ .src = .{ .mem, .none } }, 12519 .{ .src = .{ .to_gpr, .none } }, 12520 }, 12521 .extra_temps = .{ 12522 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12523 .unused, 12524 .unused, 12525 .unused, 12526 .unused, 12527 .unused, 12528 .unused, 12529 .unused, 12530 .unused, 12531 }, 12532 .dst_temps = .{.{ .rc = .general_purpose }}, 12533 .clobbers = .{ .eflags = true }, 12534 .each = .{ .once = &.{ 12535 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12536 .{ ._, ._r, .bs, .tmp0d, .tmp0d, ._, ._ }, 12537 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12538 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12539 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12540 .{ ._, ._, .sbb, .dst0b, .tmp0b, ._, ._ }, 12541 } }, 12542 }, .{ 12543 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .byte }, .any }, 12544 .patterns = &.{ 12545 .{ .src = .{ .mem, .none } }, 12546 .{ .src = .{ .to_gpr, .none } }, 12547 }, 12548 .extra_temps = .{ 12549 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12550 .unused, 12551 .unused, 12552 .unused, 12553 .unused, 12554 .unused, 12555 .unused, 12556 .unused, 12557 .unused, 12558 }, 12559 .dst_temps = .{.{ .rc = .general_purpose }}, 12560 .clobbers = .{ .eflags = true }, 12561 .each = .{ .once = &.{ 12562 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12563 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12564 .{ ._, ._r, .bs, .dst0d, .tmp0d, ._, ._ }, 12565 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12566 } }, 12567 }, .{ 12568 .src_constraints = .{ .{ .signed_po2_int = .byte }, .any }, 12569 .patterns = &.{ 12570 .{ .src = .{ .mem, .none } }, 12571 .{ .src = .{ .to_gpr, .none } }, 12572 }, 12573 .extra_temps = .{ 12574 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12575 .unused, 12576 .unused, 12577 .unused, 12578 .unused, 12579 .unused, 12580 .unused, 12581 .unused, 12582 .unused, 12583 }, 12584 .dst_temps = .{.{ .rc = .general_purpose }}, 12585 .clobbers = .{ .eflags = true }, 12586 .each = .{ .once = &.{ 12587 .{ ._, ._, .movzx, .tmp0d, .src0b, ._, ._ }, 12588 .{ ._, ._, .@"and", .tmp0d, .sa(.src0, .add_umax), ._, ._ }, 12589 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12590 .{ ._, ._r, .bs, .dst0d, .tmp0d, ._, ._ }, 12591 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12592 } }, 12593 }, .{ 12594 .src_constraints = .{ .{ .signed_int = .byte }, .any }, 12595 .patterns = &.{ 12596 .{ .src = .{ .mem, .none } }, 12597 .{ .src = .{ .to_gpr, .none } }, 12598 }, 12599 .extra_temps = .{ 12600 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12601 .unused, 12602 .unused, 12603 .unused, 12604 .unused, 12605 .unused, 12606 .unused, 12607 .unused, 12608 .unused, 12609 }, 12610 .dst_temps = .{.{ .rc = .general_purpose }}, 12611 .clobbers = .{ .eflags = true }, 12612 .each = .{ .once = &.{ 12613 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12614 .{ ._, ._, .@"and", .dst0d, .sa(.src0, .add_umax), ._, ._ }, 12615 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 12616 .{ ._, ._r, .bs, .tmp0d, .dst0d, ._, ._ }, 12617 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12618 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12619 } }, 12620 }, .{ 12621 .src_constraints = .{ .{ .unsigned_int = .byte }, .any }, 12622 .patterns = &.{ 12623 .{ .src = .{ .mem, .none } }, 12624 .{ .src = .{ .to_gpr, .none } }, 12625 }, 12626 .extra_temps = .{ 12627 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12628 .unused, 12629 .unused, 12630 .unused, 12631 .unused, 12632 .unused, 12633 .unused, 12634 .unused, 12635 .unused, 12636 }, 12637 .dst_temps = .{.{ .rc = .general_purpose }}, 12638 .clobbers = .{ .eflags = true }, 12639 .each = .{ .once = &.{ 12640 .{ ._, ._, .movzx, .dst0d, .src0b, ._, ._ }, 12641 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 12642 .{ ._, ._r, .bs, .tmp0d, .dst0d, ._, ._ }, 12643 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12644 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12645 } }, 12646 }, .{ 12647 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12648 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .word }, .any }, 12649 .patterns = &.{ 12650 .{ .src = .{ .to_mut_gpr, .none } }, 12651 }, 12652 .dst_temps = .{.{ .rc = .general_purpose }}, 12653 .clobbers = .{ .eflags = true }, 12654 .each = .{ .once = &.{ 12655 .{ ._, ._r, .bs, .src0w, .src0w, ._, ._ }, 12656 .{ ._, ._, .mov, .dst0w, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12657 .{ ._, ._nz, .cmov, .dst0w, .src0w, ._, ._ }, 12658 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12659 } }, 12660 }, .{ 12661 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12662 .src_constraints = .{ .{ .signed_int = .word }, .any }, 12663 .patterns = &.{ 12664 .{ .src = .{ .to_mut_gpr, .none } }, 12665 }, 12666 .dst_temps = .{.{ .rc = .general_purpose }}, 12667 .clobbers = .{ .eflags = true }, 12668 .each = .{ .once = &.{ 12669 .{ ._, ._, .@"and", .src0w, .sa(.src0, .add_umax), ._, ._ }, 12670 .{ ._, ._r, .bs, .src0w, .src0w, ._, ._ }, 12671 .{ ._, ._, .mov, .dst0w, .si(0xff), ._, ._ }, 12672 .{ ._, ._z, .cmov, .src0w, .dst0w, ._, ._ }, 12673 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12674 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 12675 } }, 12676 }, .{ 12677 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12678 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 12679 .patterns = &.{ 12680 .{ .src = .{ .to_mut_gpr, .none } }, 12681 }, 12682 .dst_temps = .{.{ .rc = .general_purpose }}, 12683 .clobbers = .{ .eflags = true }, 12684 .each = .{ .once = &.{ 12685 .{ ._, ._r, .bs, .src0w, .src0w, ._, ._ }, 12686 .{ ._, ._, .mov, .dst0w, .si(0xff), ._, ._ }, 12687 .{ ._, ._z, .cmov, .src0w, .dst0w, ._, ._ }, 12688 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12689 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 12690 } }, 12691 }, .{ 12692 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12693 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .word }, .any }, 12694 .patterns = &.{ 12695 .{ .src = .{ .to_mut_gpr, .none } }, 12696 }, 12697 .dst_temps = .{.{ .ref = .src0 }}, 12698 .clobbers = .{ .eflags = true }, 12699 .each = .{ .once = &.{ 12700 .{ ._, ._r, .bs, .dst0w, .src0w, ._, ._ }, 12701 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 12702 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12703 .{ .@"0:", ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12704 } }, 12705 }, .{ 12706 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12707 .src_constraints = .{ .{ .signed_int = .word }, .any }, 12708 .patterns = &.{ 12709 .{ .src = .{ .to_mut_gpr, .none } }, 12710 }, 12711 .dst_temps = .{.{ .rc = .general_purpose }}, 12712 .clobbers = .{ .eflags = true }, 12713 .each = .{ .once = &.{ 12714 .{ ._, ._, .@"and", .src0w, .sa(.src0, .add_umax), ._, ._ }, 12715 .{ ._, ._r, .bs, .src0w, .src0w, ._, ._ }, 12716 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12717 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12718 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12719 .{ ._, ._, .sbb, .dst0b, .src0b, ._, ._ }, 12720 } }, 12721 }, .{ 12722 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12723 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 12724 .patterns = &.{ 12725 .{ .src = .{ .to_mut_gpr, .none } }, 12726 }, 12727 .dst_temps = .{.{ .rc = .general_purpose }}, 12728 .clobbers = .{ .eflags = true }, 12729 .each = .{ .once = &.{ 12730 .{ ._, ._r, .bs, .src0w, .src0w, ._, ._ }, 12731 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12732 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12733 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12734 .{ ._, ._, .sbb, .dst0b, .src0b, ._, ._ }, 12735 } }, 12736 }, .{ 12737 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .word }, .any }, 12738 .patterns = &.{ 12739 .{ .src = .{ .mem, .none } }, 12740 .{ .src = .{ .to_gpr, .none } }, 12741 }, 12742 .dst_temps = .{.{ .rc = .general_purpose }}, 12743 .clobbers = .{ .eflags = true }, 12744 .each = .{ .once = &.{ 12745 .{ ._, ._, .mov, .dst0w, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12746 .{ ._, ._r, .bs, .dst0w, .src0w, ._, ._ }, 12747 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12748 } }, 12749 }, .{ 12750 .src_constraints = .{ .{ .signed_int = .word }, .any }, 12751 .patterns = &.{ 12752 .{ .src = .{ .to_mut_gpr, .none } }, 12753 }, 12754 .extra_temps = .{ 12755 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 12756 .unused, 12757 .unused, 12758 .unused, 12759 .unused, 12760 .unused, 12761 .unused, 12762 .unused, 12763 .unused, 12764 }, 12765 .dst_temps = .{.{ .rc = .general_purpose }}, 12766 .clobbers = .{ .eflags = true }, 12767 .each = .{ .once = &.{ 12768 .{ ._, ._, .@"and", .src0w, .sa(.src0, .add_umax), ._, ._ }, 12769 .{ ._, ._, .mov, .tmp0w, .si(0xff), ._, ._ }, 12770 .{ ._, ._r, .bs, .tmp0w, .src0w, ._, ._ }, 12771 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12772 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12773 } }, 12774 }, .{ 12775 .src_constraints = .{ .{ .unsigned_int = .word }, .any }, 12776 .patterns = &.{ 12777 .{ .src = .{ .mem, .none } }, 12778 .{ .src = .{ .to_gpr, .none } }, 12779 }, 12780 .extra_temps = .{ 12781 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 12782 .unused, 12783 .unused, 12784 .unused, 12785 .unused, 12786 .unused, 12787 .unused, 12788 .unused, 12789 .unused, 12790 }, 12791 .dst_temps = .{.{ .rc = .general_purpose }}, 12792 .clobbers = .{ .eflags = true }, 12793 .each = .{ .once = &.{ 12794 .{ ._, ._, .mov, .tmp0w, .si(0xff), ._, ._ }, 12795 .{ ._, ._r, .bs, .tmp0w, .src0w, ._, ._ }, 12796 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12797 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12798 } }, 12799 }, .{ 12800 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12801 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .dword }, .any }, 12802 .patterns = &.{ 12803 .{ .src = .{ .to_mut_gpr, .none } }, 12804 }, 12805 .dst_temps = .{.{ .rc = .general_purpose }}, 12806 .clobbers = .{ .eflags = true }, 12807 .each = .{ .once = &.{ 12808 .{ ._, ._r, .bs, .src0d, .src0d, ._, ._ }, 12809 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12810 .{ ._, ._nz, .cmov, .dst0d, .src0d, ._, ._ }, 12811 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12812 } }, 12813 }, .{ 12814 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12815 .src_constraints = .{ .{ .signed_int = .dword }, .any }, 12816 .patterns = &.{ 12817 .{ .src = .{ .to_mut_gpr, .none } }, 12818 }, 12819 .dst_temps = .{.{ .rc = .general_purpose }}, 12820 .clobbers = .{ .eflags = true }, 12821 .each = .{ .once = &.{ 12822 .{ ._, ._, .@"and", .src0d, .sa(.src0, .add_umax), ._, ._ }, 12823 .{ ._, ._r, .bs, .src0d, .src0d, ._, ._ }, 12824 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 12825 .{ ._, ._z, .cmov, .src0d, .dst0d, ._, ._ }, 12826 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12827 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 12828 } }, 12829 }, .{ 12830 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 12831 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 12832 .patterns = &.{ 12833 .{ .src = .{ .to_mut_gpr, .none } }, 12834 }, 12835 .dst_temps = .{.{ .rc = .general_purpose }}, 12836 .clobbers = .{ .eflags = true }, 12837 .each = .{ .once = &.{ 12838 .{ ._, ._r, .bs, .src0d, .src0d, ._, ._ }, 12839 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 12840 .{ ._, ._z, .cmov, .src0d, .dst0d, ._, ._ }, 12841 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12842 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 12843 } }, 12844 }, .{ 12845 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12846 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .dword }, .any }, 12847 .patterns = &.{ 12848 .{ .src = .{ .to_mut_gpr, .none } }, 12849 }, 12850 .dst_temps = .{.{ .ref = .src0 }}, 12851 .clobbers = .{ .eflags = true }, 12852 .each = .{ .once = &.{ 12853 .{ ._, ._r, .bs, .dst0d, .src0d, ._, ._ }, 12854 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 12855 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12856 .{ .@"0:", ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12857 } }, 12858 }, .{ 12859 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12860 .src_constraints = .{ .{ .signed_int = .dword }, .any }, 12861 .patterns = &.{ 12862 .{ .src = .{ .to_mut_gpr, .none } }, 12863 }, 12864 .dst_temps = .{.{ .rc = .general_purpose }}, 12865 .clobbers = .{ .eflags = true }, 12866 .each = .{ .once = &.{ 12867 .{ ._, ._, .@"and", .src0d, .sa(.src0, .add_umax), ._, ._ }, 12868 .{ ._, ._r, .bs, .src0d, .src0d, ._, ._ }, 12869 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12870 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12871 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12872 .{ ._, ._, .sbb, .dst0b, .src0b, ._, ._ }, 12873 } }, 12874 }, .{ 12875 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 12876 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 12877 .patterns = &.{ 12878 .{ .src = .{ .to_mut_gpr, .none } }, 12879 }, 12880 .dst_temps = .{.{ .rc = .general_purpose }}, 12881 .clobbers = .{ .eflags = true }, 12882 .each = .{ .once = &.{ 12883 .{ ._, ._r, .bs, .src0d, .src0d, ._, ._ }, 12884 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 12885 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 12886 .{ ._, ._c, .st, ._, ._, ._, ._ }, 12887 .{ ._, ._, .sbb, .dst0b, .src0b, ._, ._ }, 12888 } }, 12889 }, .{ 12890 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .dword }, .any }, 12891 .patterns = &.{ 12892 .{ .src = .{ .mem, .none } }, 12893 .{ .src = .{ .to_gpr, .none } }, 12894 }, 12895 .dst_temps = .{.{ .rc = .general_purpose }}, 12896 .clobbers = .{ .eflags = true }, 12897 .each = .{ .once = &.{ 12898 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12899 .{ ._, ._r, .bs, .dst0d, .src0d, ._, ._ }, 12900 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12901 } }, 12902 }, .{ 12903 .src_constraints = .{ .{ .signed_int = .dword }, .any }, 12904 .patterns = &.{ 12905 .{ .src = .{ .to_mut_gpr, .none } }, 12906 }, 12907 .extra_temps = .{ 12908 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12909 .unused, 12910 .unused, 12911 .unused, 12912 .unused, 12913 .unused, 12914 .unused, 12915 .unused, 12916 .unused, 12917 }, 12918 .dst_temps = .{.{ .rc = .general_purpose }}, 12919 .clobbers = .{ .eflags = true }, 12920 .each = .{ .once = &.{ 12921 .{ ._, ._, .@"and", .src0d, .sa(.src0, .add_umax), ._, ._ }, 12922 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 12923 .{ ._, ._r, .bs, .tmp0d, .src0d, ._, ._ }, 12924 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12925 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12926 } }, 12927 }, .{ 12928 .src_constraints = .{ .{ .unsigned_int = .dword }, .any }, 12929 .patterns = &.{ 12930 .{ .src = .{ .mem, .none } }, 12931 .{ .src = .{ .to_gpr, .none } }, 12932 }, 12933 .extra_temps = .{ 12934 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 12935 .unused, 12936 .unused, 12937 .unused, 12938 .unused, 12939 .unused, 12940 .unused, 12941 .unused, 12942 .unused, 12943 }, 12944 .dst_temps = .{.{ .rc = .general_purpose }}, 12945 .clobbers = .{ .eflags = true }, 12946 .each = .{ .once = &.{ 12947 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 12948 .{ ._, ._r, .bs, .tmp0d, .src0d, ._, ._ }, 12949 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12950 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12951 } }, 12952 }, .{ 12953 .required_features = .{ .@"64bit", .cmov, .bsf_bsr_0_clobbers_result, null }, 12954 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .qword }, .any }, 12955 .patterns = &.{ 12956 .{ .src = .{ .to_mut_gpr, .none } }, 12957 }, 12958 .dst_temps = .{.{ .rc = .general_purpose }}, 12959 .clobbers = .{ .eflags = true }, 12960 .each = .{ .once = &.{ 12961 .{ ._, ._r, .bs, .src0q, .src0q, ._, ._ }, 12962 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 12963 .{ ._, ._nz, .cmov, .dst0d, .src0d, ._, ._ }, 12964 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12965 } }, 12966 }, .{ 12967 .required_features = .{ .@"64bit", .cmov, .bsf_bsr_0_clobbers_result, null }, 12968 .src_constraints = .{ .{ .signed_int = .qword }, .any }, 12969 .patterns = &.{ 12970 .{ .src = .{ .mem, .none } }, 12971 .{ .src = .{ .to_gpr, .none } }, 12972 }, 12973 .extra_temps = .{ 12974 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 12975 .unused, 12976 .unused, 12977 .unused, 12978 .unused, 12979 .unused, 12980 .unused, 12981 .unused, 12982 .unused, 12983 }, 12984 .dst_temps = .{.{ .rc = .general_purpose }}, 12985 .clobbers = .{ .eflags = true }, 12986 .each = .{ .once = &.{ 12987 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 12988 .{ ._, ._, .@"and", .tmp0q, .src0q, ._, ._ }, 12989 .{ ._, ._r, .bs, .tmp0q, .tmp0q, ._, ._ }, 12990 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 12991 .{ ._, ._z, .cmov, .tmp0d, .dst0d, ._, ._ }, 12992 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 12993 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 12994 } }, 12995 }, .{ 12996 .required_features = .{ .@"64bit", .cmov, .bsf_bsr_0_clobbers_result, null }, 12997 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 12998 .patterns = &.{ 12999 .{ .src = .{ .to_mut_gpr, .none } }, 13000 }, 13001 .dst_temps = .{.{ .rc = .general_purpose }}, 13002 .clobbers = .{ .eflags = true }, 13003 .each = .{ .once = &.{ 13004 .{ ._, ._r, .bs, .src0q, .src0q, ._, ._ }, 13005 .{ ._, ._, .mov, .dst0d, .si(0xff), ._, ._ }, 13006 .{ ._, ._z, .cmov, .src0d, .dst0d, ._, ._ }, 13007 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13008 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 13009 } }, 13010 }, .{ 13011 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 13012 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .qword }, .any }, 13013 .patterns = &.{ 13014 .{ .src = .{ .to_mut_gpr, .none } }, 13015 }, 13016 .dst_temps = .{.{ .ref = .src0 }}, 13017 .clobbers = .{ .eflags = true }, 13018 .each = .{ .once = &.{ 13019 .{ ._, ._r, .bs, .dst0q, .src0q, ._, ._ }, 13020 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13021 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 13022 .{ .@"0:", ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13023 } }, 13024 }, .{ 13025 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 13026 .src_constraints = .{ .{ .signed_int = .qword }, .any }, 13027 .patterns = &.{ 13028 .{ .src = .{ .mem, .none } }, 13029 .{ .src = .{ .to_gpr, .none } }, 13030 }, 13031 .extra_temps = .{ 13032 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13033 .unused, 13034 .unused, 13035 .unused, 13036 .unused, 13037 .unused, 13038 .unused, 13039 .unused, 13040 .unused, 13041 }, 13042 .dst_temps = .{.{ .rc = .general_purpose }}, 13043 .clobbers = .{ .eflags = true }, 13044 .each = .{ .once = &.{ 13045 .{ ._, ._, .mov, .tmp0q, .ua(.src0, .add_umax), ._, ._ }, 13046 .{ ._, ._, .@"and", .tmp0q, .src0q, ._, ._ }, 13047 .{ ._, ._r, .bs, .tmp0q, .tmp0q, ._, ._ }, 13048 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 13049 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 13050 .{ ._, ._c, .st, ._, ._, ._, ._ }, 13051 .{ ._, ._, .sbb, .dst0b, .tmp0b, ._, ._ }, 13052 } }, 13053 }, .{ 13054 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 13055 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 13056 .patterns = &.{ 13057 .{ .src = .{ .to_mut_gpr, .none } }, 13058 }, 13059 .dst_temps = .{.{ .rc = .general_purpose }}, 13060 .clobbers = .{ .eflags = true }, 13061 .each = .{ .once = &.{ 13062 .{ ._, ._r, .bs, .src0q, .src0q, ._, ._ }, 13063 .{ ._, ._, .mov, .dst0b, .sa(.src0, .add_bit_size), ._, ._ }, 13064 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 13065 .{ ._, ._c, .st, ._, ._, ._, ._ }, 13066 .{ ._, ._, .sbb, .dst0b, .src0b, ._, ._ }, 13067 } }, 13068 }, .{ 13069 .required_features = .{ .@"64bit", null, null, null }, 13070 .src_constraints = .{ .{ .unsigned_po2_or_exact_int = .qword }, .any }, 13071 .patterns = &.{ 13072 .{ .src = .{ .mem, .none } }, 13073 .{ .src = .{ .to_gpr, .none } }, 13074 }, 13075 .dst_temps = .{.{ .rc = .general_purpose }}, 13076 .clobbers = .{ .eflags = true }, 13077 .each = .{ .once = &.{ 13078 .{ ._, ._, .mov, .dst0d, .sia(-1, .src0, .add_2_bit_size), ._, ._ }, 13079 .{ ._, ._r, .bs, .dst0q, .src0q, ._, ._ }, 13080 .{ ._, ._, .xor, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13081 } }, 13082 }, .{ 13083 .required_features = .{ .@"64bit", null, null, null }, 13084 .src_constraints = .{ .{ .signed_int = .qword }, .any }, 13085 .patterns = &.{ 13086 .{ .src = .{ .mem, .none } }, 13087 .{ .src = .{ .to_gpr, .none } }, 13088 }, 13089 .extra_temps = .{ 13090 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13091 .unused, 13092 .unused, 13093 .unused, 13094 .unused, 13095 .unused, 13096 .unused, 13097 .unused, 13098 .unused, 13099 }, 13100 .dst_temps = .{.{ .rc = .general_purpose }}, 13101 .clobbers = .{ .eflags = true }, 13102 .each = .{ .once = &.{ 13103 .{ ._, ._, .mov, .dst0q, .ua(.src0, .add_umax), ._, ._ }, 13104 .{ ._, ._, .@"and", .dst0q, .src0q, ._, ._ }, 13105 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 13106 .{ ._, ._r, .bs, .tmp0q, .dst0q, ._, ._ }, 13107 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13108 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 13109 } }, 13110 }, .{ 13111 .required_features = .{ .@"64bit", null, null, null }, 13112 .src_constraints = .{ .{ .unsigned_int = .qword }, .any }, 13113 .patterns = &.{ 13114 .{ .src = .{ .mem, .none } }, 13115 .{ .src = .{ .to_gpr, .none } }, 13116 }, 13117 .extra_temps = .{ 13118 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13119 .unused, 13120 .unused, 13121 .unused, 13122 .unused, 13123 .unused, 13124 .unused, 13125 .unused, 13126 .unused, 13127 }, 13128 .dst_temps = .{.{ .rc = .general_purpose }}, 13129 .clobbers = .{ .eflags = true }, 13130 .each = .{ .once = &.{ 13131 .{ ._, ._, .mov, .tmp0d, .si(0xff), ._, ._ }, 13132 .{ ._, ._r, .bs, .tmp0q, .src0q, ._, ._ }, 13133 .{ ._, ._, .mov, .dst0b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13134 .{ ._, ._, .sub, .dst0b, .tmp0b, ._, ._ }, 13135 } }, 13136 }, .{ 13137 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 13138 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13139 .patterns = &.{ 13140 .{ .src = .{ .to_mem, .none } }, 13141 }, 13142 .extra_temps = .{ 13143 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13144 .unused, 13145 .unused, 13146 .unused, 13147 .unused, 13148 .unused, 13149 .unused, 13150 .unused, 13151 .unused, 13152 }, 13153 .dst_temps = .{.{ .rc = .general_purpose }}, 13154 .clobbers = .{ .eflags = true }, 13155 .each = .{ .once = &.{ 13156 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13157 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13158 .{ ._, ._, .lzcnt, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13159 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13160 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13161 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13162 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13163 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13164 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13165 } }, 13166 }, .{ 13167 .required_features = .{ .@"64bit", .lzcnt, null, null }, 13168 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13169 .patterns = &.{ 13170 .{ .src = .{ .to_mem, .none } }, 13171 }, 13172 .extra_temps = .{ 13173 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13174 .unused, 13175 .unused, 13176 .unused, 13177 .unused, 13178 .unused, 13179 .unused, 13180 .unused, 13181 .unused, 13182 }, 13183 .dst_temps = .{.{ .rc = .general_purpose }}, 13184 .clobbers = .{ .eflags = true }, 13185 .each = .{ .once = &.{ 13186 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13187 .{ .@"0:", ._, .lzcnt, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13188 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13189 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13190 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13191 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13192 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13193 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13194 } }, 13195 }, .{ 13196 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 13197 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13198 .patterns = &.{ 13199 .{ .src = .{ .to_mem, .none } }, 13200 }, 13201 .extra_temps = .{ 13202 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13203 .unused, 13204 .unused, 13205 .unused, 13206 .unused, 13207 .unused, 13208 .unused, 13209 .unused, 13210 .unused, 13211 }, 13212 .dst_temps = .{.{ .rc = .general_purpose }}, 13213 .clobbers = .{ .eflags = true }, 13214 .each = .{ .once = &.{ 13215 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13216 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13217 .{ ._, ._r, .bs, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13218 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13219 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13220 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13221 .{ ._, ._, .mov, .dst0d, .si(-1), ._, ._ }, 13222 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13223 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13224 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13225 } }, 13226 }, .{ 13227 .required_features = .{ .@"64bit", null, null, null }, 13228 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13229 .patterns = &.{ 13230 .{ .src = .{ .to_mem, .none } }, 13231 }, 13232 .extra_temps = .{ 13233 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13234 .unused, 13235 .unused, 13236 .unused, 13237 .unused, 13238 .unused, 13239 .unused, 13240 .unused, 13241 .unused, 13242 }, 13243 .dst_temps = .{.{ .rc = .general_purpose }}, 13244 .clobbers = .{ .eflags = true }, 13245 .each = .{ .once = &.{ 13246 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13247 .{ .@"0:", ._, .mov, .dst0d, .si(-1), ._, ._ }, 13248 .{ ._, ._r, .bs, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13249 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13250 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13251 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13252 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13253 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13254 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13255 } }, 13256 }, .{ 13257 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 13258 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13259 .patterns = &.{ 13260 .{ .src = .{ .to_mem, .none } }, 13261 }, 13262 .extra_temps = .{ 13263 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13264 .unused, 13265 .unused, 13266 .unused, 13267 .unused, 13268 .unused, 13269 .unused, 13270 .unused, 13271 .unused, 13272 }, 13273 .dst_temps = .{.{ .rc = .general_purpose }}, 13274 .clobbers = .{ .eflags = true }, 13275 .each = .{ .once = &.{ 13276 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13277 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13278 .{ ._, ._, .lzcnt, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13279 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13280 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13281 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13282 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13283 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13284 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13285 } }, 13286 }, .{ 13287 .required_features = .{ .@"64bit", .lzcnt, null, null }, 13288 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13289 .patterns = &.{ 13290 .{ .src = .{ .to_mem, .none } }, 13291 }, 13292 .extra_temps = .{ 13293 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13294 .unused, 13295 .unused, 13296 .unused, 13297 .unused, 13298 .unused, 13299 .unused, 13300 .unused, 13301 .unused, 13302 }, 13303 .dst_temps = .{.{ .rc = .general_purpose }}, 13304 .clobbers = .{ .eflags = true }, 13305 .each = .{ .once = &.{ 13306 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13307 .{ .@"0:", ._, .lzcnt, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13308 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13309 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13310 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13311 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13312 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13313 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13314 } }, 13315 }, .{ 13316 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 13317 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13318 .patterns = &.{ 13319 .{ .src = .{ .to_mem, .none } }, 13320 }, 13321 .extra_temps = .{ 13322 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13323 .unused, 13324 .unused, 13325 .unused, 13326 .unused, 13327 .unused, 13328 .unused, 13329 .unused, 13330 .unused, 13331 }, 13332 .dst_temps = .{.{ .rc = .general_purpose }}, 13333 .clobbers = .{ .eflags = true }, 13334 .each = .{ .once = &.{ 13335 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13336 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13337 .{ ._, ._r, .bs, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13338 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13339 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13340 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13341 .{ ._, ._, .mov, .dst0d, .si(-1), ._, ._ }, 13342 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13343 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13344 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13345 } }, 13346 }, .{ 13347 .required_features = .{ .@"64bit", null, null, null }, 13348 .src_constraints = .{ .{ .unsigned_or_exact_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13349 .patterns = &.{ 13350 .{ .src = .{ .to_mem, .none } }, 13351 }, 13352 .extra_temps = .{ 13353 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13354 .unused, 13355 .unused, 13356 .unused, 13357 .unused, 13358 .unused, 13359 .unused, 13360 .unused, 13361 .unused, 13362 }, 13363 .dst_temps = .{.{ .rc = .general_purpose }}, 13364 .clobbers = .{ .eflags = true }, 13365 .each = .{ .once = &.{ 13366 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13367 .{ .@"0:", ._, .mov, .dst0d, .si(-1), ._, ._ }, 13368 .{ ._, ._r, .bs, .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13369 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13370 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13371 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13372 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13373 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13374 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13375 } }, 13376 }, .{ 13377 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 13378 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13379 .patterns = &.{ 13380 .{ .src = .{ .to_mem, .none } }, 13381 }, 13382 .extra_temps = .{ 13383 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13384 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13385 .unused, 13386 .unused, 13387 .unused, 13388 .unused, 13389 .unused, 13390 .unused, 13391 .unused, 13392 }, 13393 .dst_temps = .{.{ .rc = .general_purpose }}, 13394 .clobbers = .{ .eflags = true }, 13395 .each = .{ .once = &.{ 13396 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13397 .{ ._, ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13398 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13399 .{ ._, ._, .@"and", .tmp1q, .memi(.src0q, .tmp0), ._, ._ }, 13400 .{ ._, ._, .lzcnt, .dst0q, .tmp1q, ._, ._ }, 13401 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13402 .{ ._, ._, .mov, .tmp1q, .si(-1), ._, ._ }, 13403 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13404 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13405 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13406 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13407 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13408 } }, 13409 }, .{ 13410 .required_features = .{ .@"64bit", .lzcnt, null, null }, 13411 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13412 .patterns = &.{ 13413 .{ .src = .{ .to_mem, .none } }, 13414 }, 13415 .extra_temps = .{ 13416 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13417 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13418 .unused, 13419 .unused, 13420 .unused, 13421 .unused, 13422 .unused, 13423 .unused, 13424 .unused, 13425 }, 13426 .dst_temps = .{.{ .rc = .general_purpose }}, 13427 .clobbers = .{ .eflags = true }, 13428 .each = .{ .once = &.{ 13429 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13430 .{ ._, ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13431 .{ .@"0:", ._, .@"and", .tmp1q, .memi(.src0q, .tmp0), ._, ._ }, 13432 .{ ._, ._, .lzcnt, .dst0q, .tmp1q, ._, ._ }, 13433 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13434 .{ ._, ._, .mov, .tmp1q, .si(-1), ._, ._ }, 13435 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13436 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13437 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13438 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13439 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13440 } }, 13441 }, .{ 13442 .required_features = .{ .@"64bit", null, null, null }, 13443 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 13444 .patterns = &.{ 13445 .{ .src = .{ .to_mem, .none } }, 13446 }, 13447 .extra_temps = .{ 13448 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13449 .unused, 13450 .unused, 13451 .unused, 13452 .unused, 13453 .unused, 13454 .unused, 13455 .unused, 13456 .unused, 13457 }, 13458 .dst_temps = .{.{ .rc = .general_purpose }}, 13459 .clobbers = .{ .eflags = true }, 13460 .each = .{ .once = &.{ 13461 .{ ._, ._, .mov, .tmp0d, .sia(-16, .src0, .add_size), ._, ._ }, 13462 .{ ._, ._, .mov, .dst0q, .ua(.src0, .add_umax), ._, ._ }, 13463 .{ .@"0:", ._, .@"and", .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13464 .{ ._, ._r, .bs, .dst0q, .dst0q, ._, ._ }, 13465 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13466 .{ ._, ._, .mov, .dst0q, .si(-1), ._, ._ }, 13467 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13468 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13469 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13470 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13471 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13472 } }, 13473 }, .{ 13474 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 13475 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13476 .patterns = &.{ 13477 .{ .src = .{ .to_mem, .none } }, 13478 }, 13479 .extra_temps = .{ 13480 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13481 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13482 .unused, 13483 .unused, 13484 .unused, 13485 .unused, 13486 .unused, 13487 .unused, 13488 .unused, 13489 }, 13490 .dst_temps = .{.{ .rc = .general_purpose }}, 13491 .clobbers = .{ .eflags = true }, 13492 .each = .{ .once = &.{ 13493 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13494 .{ ._, ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13495 .{ .@"0:", ._, .xor, .dst0d, .dst0d, ._, ._ }, 13496 .{ ._, ._, .@"and", .tmp1q, .memi(.src0q, .tmp0), ._, ._ }, 13497 .{ ._, ._, .lzcnt, .dst0q, .tmp1q, ._, ._ }, 13498 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13499 .{ ._, ._, .mov, .tmp1q, .si(-1), ._, ._ }, 13500 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13501 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13502 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13503 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13504 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13505 } }, 13506 }, .{ 13507 .required_features = .{ .@"64bit", .lzcnt, null, null }, 13508 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13509 .patterns = &.{ 13510 .{ .src = .{ .to_mem, .none } }, 13511 }, 13512 .extra_temps = .{ 13513 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13514 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13515 .unused, 13516 .unused, 13517 .unused, 13518 .unused, 13519 .unused, 13520 .unused, 13521 .unused, 13522 }, 13523 .dst_temps = .{.{ .rc = .general_purpose }}, 13524 .clobbers = .{ .eflags = true }, 13525 .each = .{ .once = &.{ 13526 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13527 .{ ._, ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13528 .{ .@"0:", ._, .@"and", .tmp1q, .memi(.src0q, .tmp0), ._, ._ }, 13529 .{ ._, ._, .lzcnt, .dst0q, .tmp1q, ._, ._ }, 13530 .{ ._, ._nc, .j, .@"0f", ._, ._, ._ }, 13531 .{ ._, ._, .mov, .tmp1q, .si(-1), ._, ._ }, 13532 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13533 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13534 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13535 .{ .@"0:", ._, .neg, .tmp0d, ._, ._, ._ }, 13536 .{ ._, ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .add_src0_bit_size, -64), ._, ._ }, 13537 } }, 13538 }, .{ 13539 .required_features = .{ .@"64bit", null, null, null }, 13540 .src_constraints = .{ .{ .remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 13541 .patterns = &.{ 13542 .{ .src = .{ .to_mem, .none } }, 13543 }, 13544 .extra_temps = .{ 13545 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13546 .unused, 13547 .unused, 13548 .unused, 13549 .unused, 13550 .unused, 13551 .unused, 13552 .unused, 13553 .unused, 13554 }, 13555 .dst_temps = .{.{ .rc = .general_purpose }}, 13556 .clobbers = .{ .eflags = true }, 13557 .each = .{ .once = &.{ 13558 .{ ._, ._, .mov, .tmp0d, .sia(-8, .src0, .add_size), ._, ._ }, 13559 .{ ._, ._, .mov, .dst0q, .ua(.src0, .add_umax), ._, ._ }, 13560 .{ .@"0:", ._, .@"and", .dst0q, .memi(.src0q, .tmp0), ._, ._ }, 13561 .{ ._, ._r, .bs, .dst0q, .dst0q, ._, ._ }, 13562 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 13563 .{ ._, ._, .mov, .dst0q, .si(-1), ._, ._ }, 13564 .{ ._, ._, .sub, .tmp0d, .si(8), ._, ._ }, 13565 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13566 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 13567 .{ .@"0:", ._, .lea, .dst0d, .leasiad(.none, .dst0, .@"8", .tmp0, .sub_src0_bit_size, 1), ._, ._ }, 13568 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 13569 } }, 13570 }, .{ 13571 .required_features = .{ .lzcnt, .slow_incdec, null, null }, 13572 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13573 .patterns = &.{ 13574 .{ .src = .{ .to_mem, .none } }, 13575 }, 13576 .extra_temps = .{ 13577 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13578 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13579 .unused, 13580 .unused, 13581 .unused, 13582 .unused, 13583 .unused, 13584 .unused, 13585 .unused, 13586 }, 13587 .dst_temps = .{.mem}, 13588 .clobbers = .{ .eflags = true }, 13589 .each = .{ .once = &.{ 13590 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13591 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13592 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13593 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13594 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13595 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13596 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13597 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13598 } }, 13599 }, .{ 13600 .required_features = .{ .lzcnt, null, null, null }, 13601 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13602 .patterns = &.{ 13603 .{ .src = .{ .to_mem, .none } }, 13604 }, 13605 .extra_temps = .{ 13606 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13607 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13608 .unused, 13609 .unused, 13610 .unused, 13611 .unused, 13612 .unused, 13613 .unused, 13614 .unused, 13615 }, 13616 .dst_temps = .{.mem}, 13617 .clobbers = .{ .eflags = true }, 13618 .each = .{ .once = &.{ 13619 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13620 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13621 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13622 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13623 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13624 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13625 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13626 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13627 } }, 13628 }, .{ 13629 .required_features = .{ .lzcnt, .slow_incdec, null, null }, 13630 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 13631 .patterns = &.{ 13632 .{ .src = .{ .to_mem, .none } }, 13633 }, 13634 .extra_temps = .{ 13635 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13636 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13637 .unused, 13638 .unused, 13639 .unused, 13640 .unused, 13641 .unused, 13642 .unused, 13643 .unused, 13644 }, 13645 .dst_temps = .{.mem}, 13646 .clobbers = .{ .eflags = true }, 13647 .each = .{ .once = &.{ 13648 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13649 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 13650 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13651 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13652 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13653 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13654 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13655 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13656 } }, 13657 }, .{ 13658 .required_features = .{ .lzcnt, null, null, null }, 13659 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 13660 .patterns = &.{ 13661 .{ .src = .{ .to_mem, .none } }, 13662 }, 13663 .extra_temps = .{ 13664 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13665 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13666 .unused, 13667 .unused, 13668 .unused, 13669 .unused, 13670 .unused, 13671 .unused, 13672 .unused, 13673 }, 13674 .dst_temps = .{.mem}, 13675 .clobbers = .{ .eflags = true }, 13676 .each = .{ .once = &.{ 13677 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13678 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 13679 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13680 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13681 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13682 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13683 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13684 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13685 } }, 13686 }, .{ 13687 .required_features = .{ .lzcnt, .slow_incdec, null, null }, 13688 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 13689 .patterns = &.{ 13690 .{ .src = .{ .to_mem, .none } }, 13691 }, 13692 .extra_temps = .{ 13693 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13694 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13695 .unused, 13696 .unused, 13697 .unused, 13698 .unused, 13699 .unused, 13700 .unused, 13701 .unused, 13702 }, 13703 .dst_temps = .{.mem}, 13704 .clobbers = .{ .eflags = true }, 13705 .each = .{ .once = &.{ 13706 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13707 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 13708 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13709 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13710 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13711 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13712 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13713 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13714 } }, 13715 }, .{ 13716 .required_features = .{ .lzcnt, null, null, null }, 13717 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 13718 .patterns = &.{ 13719 .{ .src = .{ .to_mem, .none } }, 13720 }, 13721 .extra_temps = .{ 13722 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13723 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13724 .unused, 13725 .unused, 13726 .unused, 13727 .unused, 13728 .unused, 13729 .unused, 13730 .unused, 13731 }, 13732 .dst_temps = .{.mem}, 13733 .clobbers = .{ .eflags = true }, 13734 .each = .{ .once = &.{ 13735 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13736 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 13737 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13738 .{ ._, ._, .lzcnt, .tmp1d, .tmp1d, ._, ._ }, 13739 .{ ._, ._, .sub, .tmp1b, .sia(32, .src0, .sub_bit_size), ._, ._ }, 13740 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13741 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13742 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13743 } }, 13744 }, .{ 13745 .required_features = .{ .@"64bit", .lzcnt, .slow_incdec, null }, 13746 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 13747 .patterns = &.{ 13748 .{ .src = .{ .to_mem, .none } }, 13749 }, 13750 .extra_temps = .{ 13751 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13752 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13753 .unused, 13754 .unused, 13755 .unused, 13756 .unused, 13757 .unused, 13758 .unused, 13759 .unused, 13760 }, 13761 .dst_temps = .{.mem}, 13762 .clobbers = .{ .eflags = true }, 13763 .each = .{ .once = &.{ 13764 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13765 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13766 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 13767 .{ ._, ._, .lzcnt, .tmp1q, .tmp1q, ._, ._ }, 13768 .{ ._, ._, .sub, .tmp1b, .sia(64, .src0, .sub_bit_size), ._, ._ }, 13769 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13770 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13771 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13772 } }, 13773 }, .{ 13774 .required_features = .{ .@"64bit", .lzcnt, null, null }, 13775 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 13776 .patterns = &.{ 13777 .{ .src = .{ .to_mem, .none } }, 13778 }, 13779 .extra_temps = .{ 13780 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13781 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 13782 .unused, 13783 .unused, 13784 .unused, 13785 .unused, 13786 .unused, 13787 .unused, 13788 .unused, 13789 }, 13790 .dst_temps = .{.mem}, 13791 .clobbers = .{ .eflags = true }, 13792 .each = .{ .once = &.{ 13793 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13794 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 13795 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 13796 .{ ._, ._, .lzcnt, .tmp1q, .tmp1q, ._, ._ }, 13797 .{ ._, ._, .sub, .tmp1b, .sia(64, .src0, .sub_bit_size), ._, ._ }, 13798 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13799 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13800 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13801 } }, 13802 }, .{ 13803 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, .slow_incdec, null }, 13804 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13805 .patterns = &.{ 13806 .{ .src = .{ .to_mem, .none } }, 13807 }, 13808 .extra_temps = .{ 13809 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13810 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13811 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13812 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 13813 .unused, 13814 .unused, 13815 .unused, 13816 .unused, 13817 .unused, 13818 }, 13819 .dst_temps = .{.mem}, 13820 .clobbers = .{ .eflags = true }, 13821 .each = .{ .once = &.{ 13822 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13823 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 13824 .{ .@"0:", ._, .movzx, .tmp2d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13825 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 13826 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 13827 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 13828 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13829 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 13830 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 13831 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13832 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13833 } }, 13834 }, .{ 13835 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 13836 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13837 .patterns = &.{ 13838 .{ .src = .{ .to_mem, .none } }, 13839 }, 13840 .extra_temps = .{ 13841 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13842 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13843 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13844 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 13845 .unused, 13846 .unused, 13847 .unused, 13848 .unused, 13849 .unused, 13850 }, 13851 .dst_temps = .{.mem}, 13852 .clobbers = .{ .eflags = true }, 13853 .each = .{ .once = &.{ 13854 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13855 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 13856 .{ .@"0:", ._, .movzx, .tmp2d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13857 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 13858 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 13859 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 13860 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13861 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 13862 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 13863 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13864 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13865 } }, 13866 }, .{ 13867 .required_features = .{ .bsf_bsr_0_clobbers_result, .slow_incdec, null, null }, 13868 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13869 .patterns = &.{ 13870 .{ .src = .{ .to_mem, .none } }, 13871 }, 13872 .extra_temps = .{ 13873 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13874 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13875 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13876 .unused, 13877 .unused, 13878 .unused, 13879 .unused, 13880 .unused, 13881 .unused, 13882 }, 13883 .dst_temps = .{.mem}, 13884 .clobbers = .{ .eflags = true }, 13885 .each = .{ .once = &.{ 13886 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13887 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13888 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13889 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 13890 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 13891 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 13892 .{ ._, ._c, .st, ._, ._, ._, ._ }, 13893 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 13894 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 13895 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13896 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13897 } }, 13898 }, .{ 13899 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 13900 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13901 .patterns = &.{ 13902 .{ .src = .{ .to_mem, .none } }, 13903 }, 13904 .extra_temps = .{ 13905 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13906 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13907 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13908 .unused, 13909 .unused, 13910 .unused, 13911 .unused, 13912 .unused, 13913 .unused, 13914 }, 13915 .dst_temps = .{.mem}, 13916 .clobbers = .{ .eflags = true }, 13917 .each = .{ .once = &.{ 13918 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13919 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13920 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13921 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 13922 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 13923 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 13924 .{ ._, ._c, .st, ._, ._, ._, ._ }, 13925 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 13926 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 13927 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13928 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13929 } }, 13930 }, .{ 13931 .required_features = .{ .slow_incdec, null, null, null }, 13932 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13933 .patterns = &.{ 13934 .{ .src = .{ .to_mem, .none } }, 13935 }, 13936 .extra_temps = .{ 13937 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13938 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13939 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13940 .unused, 13941 .unused, 13942 .unused, 13943 .unused, 13944 .unused, 13945 .unused, 13946 }, 13947 .dst_temps = .{.mem}, 13948 .clobbers = .{ .eflags = true }, 13949 .each = .{ .once = &.{ 13950 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13951 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13952 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13953 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 13954 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 13955 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13956 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 13957 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13958 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 13959 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 13960 } }, 13961 }, .{ 13962 .src_constraints = .{ .{ .scalar_int_is = .byte }, .any }, 13963 .patterns = &.{ 13964 .{ .src = .{ .to_mem, .none } }, 13965 }, 13966 .extra_temps = .{ 13967 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13968 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13969 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 13970 .unused, 13971 .unused, 13972 .unused, 13973 .unused, 13974 .unused, 13975 .unused, 13976 }, 13977 .dst_temps = .{.mem}, 13978 .clobbers = .{ .eflags = true }, 13979 .each = .{ .once = &.{ 13980 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 13981 .{ .@"0:", ._, .movzx, .tmp1d, .memia(.src0b, .tmp0, .add_len), ._, ._ }, 13982 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 13983 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 13984 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 13985 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 13986 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 13987 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 13988 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 13989 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 13990 } }, 13991 }, .{ 13992 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, .slow_incdec, null }, 13993 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 13994 .patterns = &.{ 13995 .{ .src = .{ .to_mem, .none } }, 13996 }, 13997 .extra_temps = .{ 13998 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 13999 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14000 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14001 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14002 .unused, 14003 .unused, 14004 .unused, 14005 .unused, 14006 .unused, 14007 }, 14008 .dst_temps = .{.mem}, 14009 .clobbers = .{ .eflags = true }, 14010 .each = .{ .once = &.{ 14011 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14012 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14013 .{ .@"0:", ._, .movzx, .tmp2d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14014 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 14015 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 14016 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14017 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14018 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14019 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14020 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14021 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14022 } }, 14023 }, .{ 14024 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 14025 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 14026 .patterns = &.{ 14027 .{ .src = .{ .to_mem, .none } }, 14028 }, 14029 .extra_temps = .{ 14030 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14031 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14032 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14033 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14034 .unused, 14035 .unused, 14036 .unused, 14037 .unused, 14038 .unused, 14039 }, 14040 .dst_temps = .{.mem}, 14041 .clobbers = .{ .eflags = true }, 14042 .each = .{ .once = &.{ 14043 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14044 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14045 .{ .@"0:", ._, .movzx, .tmp2d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14046 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 14047 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 14048 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14049 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14050 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14051 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14052 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14053 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14054 } }, 14055 }, .{ 14056 .required_features = .{ .bsf_bsr_0_clobbers_result, .slow_incdec, null, null }, 14057 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 14058 .patterns = &.{ 14059 .{ .src = .{ .to_mem, .none } }, 14060 }, 14061 .extra_temps = .{ 14062 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14063 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14064 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14065 .unused, 14066 .unused, 14067 .unused, 14068 .unused, 14069 .unused, 14070 .unused, 14071 }, 14072 .dst_temps = .{.mem}, 14073 .clobbers = .{ .eflags = true }, 14074 .each = .{ .once = &.{ 14075 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14076 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14077 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14078 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 14079 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14080 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14081 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14082 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14083 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14084 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14085 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14086 } }, 14087 }, .{ 14088 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 14089 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 14090 .patterns = &.{ 14091 .{ .src = .{ .to_mem, .none } }, 14092 }, 14093 .extra_temps = .{ 14094 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14095 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14096 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14097 .unused, 14098 .unused, 14099 .unused, 14100 .unused, 14101 .unused, 14102 .unused, 14103 }, 14104 .dst_temps = .{.mem}, 14105 .clobbers = .{ .eflags = true }, 14106 .each = .{ .once = &.{ 14107 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14108 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14109 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14110 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 14111 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14112 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14113 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14114 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14115 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14116 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14117 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14118 } }, 14119 }, .{ 14120 .required_features = .{ .slow_incdec, null, null, null }, 14121 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 14122 .patterns = &.{ 14123 .{ .src = .{ .to_mem, .none } }, 14124 }, 14125 .extra_temps = .{ 14126 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14127 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14128 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14129 .unused, 14130 .unused, 14131 .unused, 14132 .unused, 14133 .unused, 14134 .unused, 14135 }, 14136 .dst_temps = .{.mem}, 14137 .clobbers = .{ .eflags = true }, 14138 .each = .{ .once = &.{ 14139 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14140 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14141 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14142 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14143 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 14144 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14145 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14146 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14147 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14148 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14149 } }, 14150 }, .{ 14151 .src_constraints = .{ .{ .scalar_int_is = .word }, .any }, 14152 .patterns = &.{ 14153 .{ .src = .{ .to_mem, .none } }, 14154 }, 14155 .extra_temps = .{ 14156 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14157 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14158 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14159 .unused, 14160 .unused, 14161 .unused, 14162 .unused, 14163 .unused, 14164 .unused, 14165 }, 14166 .dst_temps = .{.mem}, 14167 .clobbers = .{ .eflags = true }, 14168 .each = .{ .once = &.{ 14169 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14170 .{ .@"0:", ._, .movzx, .tmp1d, .memsia(.src0w, .@"2", .tmp0, .add_2_len), ._, ._ }, 14171 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14172 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14173 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 14174 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14175 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14176 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14177 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14178 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14179 } }, 14180 }, .{ 14181 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, .slow_incdec, null }, 14182 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14183 .patterns = &.{ 14184 .{ .src = .{ .to_mem, .none } }, 14185 }, 14186 .extra_temps = .{ 14187 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14188 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14189 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14190 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14191 .unused, 14192 .unused, 14193 .unused, 14194 .unused, 14195 .unused, 14196 }, 14197 .dst_temps = .{.mem}, 14198 .clobbers = .{ .eflags = true }, 14199 .each = .{ .once = &.{ 14200 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14201 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14202 .{ .@"0:", ._, .mov, .tmp2d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14203 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 14204 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 14205 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14206 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14207 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14208 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14209 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14210 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14211 } }, 14212 }, .{ 14213 .required_features = .{ .cmov, .bsf_bsr_0_clobbers_result, null, null }, 14214 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14215 .patterns = &.{ 14216 .{ .src = .{ .to_mem, .none } }, 14217 }, 14218 .extra_temps = .{ 14219 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14220 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14221 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14222 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14223 .unused, 14224 .unused, 14225 .unused, 14226 .unused, 14227 .unused, 14228 }, 14229 .dst_temps = .{.mem}, 14230 .clobbers = .{ .eflags = true }, 14231 .each = .{ .once = &.{ 14232 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14233 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14234 .{ .@"0:", ._, .mov, .tmp2d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14235 .{ ._, ._, .@"and", .tmp2d, .sa(.src0, .add_umax), ._, ._ }, 14236 .{ ._, ._r, .bs, .tmp2d, .tmp2d, ._, ._ }, 14237 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14238 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14239 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14240 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14241 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14242 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14243 } }, 14244 }, .{ 14245 .required_features = .{ .bsf_bsr_0_clobbers_result, .slow_incdec, null, null }, 14246 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14247 .patterns = &.{ 14248 .{ .src = .{ .to_mem, .none } }, 14249 }, 14250 .extra_temps = .{ 14251 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14252 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14253 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14254 .unused, 14255 .unused, 14256 .unused, 14257 .unused, 14258 .unused, 14259 .unused, 14260 }, 14261 .dst_temps = .{.mem}, 14262 .clobbers = .{ .eflags = true }, 14263 .each = .{ .once = &.{ 14264 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14265 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14266 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14267 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 14268 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14269 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14270 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14271 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14272 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14273 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14274 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14275 } }, 14276 }, .{ 14277 .required_features = .{ .bsf_bsr_0_clobbers_result, null, null, null }, 14278 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14279 .patterns = &.{ 14280 .{ .src = .{ .to_mem, .none } }, 14281 }, 14282 .extra_temps = .{ 14283 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14284 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14285 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14286 .unused, 14287 .unused, 14288 .unused, 14289 .unused, 14290 .unused, 14291 .unused, 14292 }, 14293 .dst_temps = .{.mem}, 14294 .clobbers = .{ .eflags = true }, 14295 .each = .{ .once = &.{ 14296 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14297 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14298 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14299 .{ ._, ._r, .bs, .tmp1d, .tmp1d, ._, ._ }, 14300 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14301 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14302 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14303 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14304 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14305 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14306 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14307 } }, 14308 }, .{ 14309 .required_features = .{ .slow_incdec, null, null, null }, 14310 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14311 .patterns = &.{ 14312 .{ .src = .{ .to_mem, .none } }, 14313 }, 14314 .extra_temps = .{ 14315 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14316 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14317 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14318 .unused, 14319 .unused, 14320 .unused, 14321 .unused, 14322 .unused, 14323 .unused, 14324 }, 14325 .dst_temps = .{.mem}, 14326 .clobbers = .{ .eflags = true }, 14327 .each = .{ .once = &.{ 14328 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14329 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14330 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14331 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14332 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 14333 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14334 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14335 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14336 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14337 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14338 } }, 14339 }, .{ 14340 .src_constraints = .{ .{ .scalar_int_is = .dword }, .any }, 14341 .patterns = &.{ 14342 .{ .src = .{ .to_mem, .none } }, 14343 }, 14344 .extra_temps = .{ 14345 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14346 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14347 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14348 .unused, 14349 .unused, 14350 .unused, 14351 .unused, 14352 .unused, 14353 .unused, 14354 }, 14355 .dst_temps = .{.mem}, 14356 .clobbers = .{ .eflags = true }, 14357 .each = .{ .once = &.{ 14358 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14359 .{ .@"0:", ._, .mov, .tmp1d, .memsia(.src0d, .@"4", .tmp0, .add_4_len), ._, ._ }, 14360 .{ ._, ._, .@"and", .tmp1d, .sa(.src0, .add_umax), ._, ._ }, 14361 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14362 .{ ._, ._r, .bs, .tmp2d, .tmp1d, ._, ._ }, 14363 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14364 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14365 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14366 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14367 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14368 } }, 14369 }, .{ 14370 .required_features = .{ .@"64bit", .cmov, .bsf_bsr_0_clobbers_result, .slow_incdec }, 14371 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14372 .patterns = &.{ 14373 .{ .src = .{ .to_mem, .none } }, 14374 }, 14375 .extra_temps = .{ 14376 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14377 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14378 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14379 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14380 .unused, 14381 .unused, 14382 .unused, 14383 .unused, 14384 .unused, 14385 }, 14386 .dst_temps = .{.mem}, 14387 .clobbers = .{ .eflags = true }, 14388 .each = .{ .once = &.{ 14389 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14390 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14391 .{ .@"0:", ._, .mov, .tmp2q, .ua(.src0, .add_umax), ._, ._ }, 14392 .{ ._, ._, .@"and", .tmp2q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14393 .{ ._, ._r, .bs, .tmp2q, .tmp2q, ._, ._ }, 14394 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14395 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14396 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14397 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14398 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14399 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14400 } }, 14401 }, .{ 14402 .required_features = .{ .@"64bit", .cmov, .bsf_bsr_0_clobbers_result, null }, 14403 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14404 .patterns = &.{ 14405 .{ .src = .{ .to_mem, .none } }, 14406 }, 14407 .extra_temps = .{ 14408 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14409 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14410 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14411 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 14412 .unused, 14413 .unused, 14414 .unused, 14415 .unused, 14416 .unused, 14417 }, 14418 .dst_temps = .{.mem}, 14419 .clobbers = .{ .eflags = true }, 14420 .each = .{ .once = &.{ 14421 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14422 .{ ._, ._, .mov, .tmp1d, .si(0xff), ._, ._ }, 14423 .{ .@"0:", ._, .mov, .tmp2q, .ua(.src0, .add_umax), ._, ._ }, 14424 .{ ._, ._, .@"and", .tmp2q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14425 .{ ._, ._r, .bs, .tmp2q, .tmp2q, ._, ._ }, 14426 .{ ._, ._z, .cmov, .tmp2d, .tmp1d, ._, ._ }, 14427 .{ ._, ._, .mov, .tmp3b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14428 .{ ._, ._, .sub, .tmp3b, .tmp2b, ._, ._ }, 14429 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14430 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14431 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14432 } }, 14433 }, .{ 14434 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, .slow_incdec, null }, 14435 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14436 .patterns = &.{ 14437 .{ .src = .{ .to_mem, .none } }, 14438 }, 14439 .extra_temps = .{ 14440 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14441 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14442 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14443 .unused, 14444 .unused, 14445 .unused, 14446 .unused, 14447 .unused, 14448 .unused, 14449 }, 14450 .dst_temps = .{.mem}, 14451 .clobbers = .{ .eflags = true }, 14452 .each = .{ .once = &.{ 14453 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14454 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 14455 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14456 .{ ._, ._r, .bs, .tmp1q, .tmp1q, ._, ._ }, 14457 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14458 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14459 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14460 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14461 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14462 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14463 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14464 } }, 14465 }, .{ 14466 .required_features = .{ .@"64bit", .bsf_bsr_0_clobbers_result, null, null }, 14467 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14468 .patterns = &.{ 14469 .{ .src = .{ .to_mem, .none } }, 14470 }, 14471 .extra_temps = .{ 14472 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14473 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14474 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14475 .unused, 14476 .unused, 14477 .unused, 14478 .unused, 14479 .unused, 14480 .unused, 14481 }, 14482 .dst_temps = .{.mem}, 14483 .clobbers = .{ .eflags = true }, 14484 .each = .{ .once = &.{ 14485 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14486 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 14487 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14488 .{ ._, ._r, .bs, .tmp1q, .tmp1q, ._, ._ }, 14489 .{ ._, ._, .mov, .tmp2b, .sa(.src0, .add_bit_size), ._, ._ }, 14490 .{ ._, ._z, .j, .@"1f", ._, ._, ._ }, 14491 .{ ._, ._c, .st, ._, ._, ._, ._ }, 14492 .{ ._, ._, .sbb, .tmp2b, .tmp1b, ._, ._ }, 14493 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp2b, ._, ._ }, 14494 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14495 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14496 } }, 14497 }, .{ 14498 .required_features = .{ .@"64bit", .slow_incdec, null, null }, 14499 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14500 .patterns = &.{ 14501 .{ .src = .{ .to_mem, .none } }, 14502 }, 14503 .extra_temps = .{ 14504 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14505 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14506 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14507 .unused, 14508 .unused, 14509 .unused, 14510 .unused, 14511 .unused, 14512 .unused, 14513 }, 14514 .dst_temps = .{.mem}, 14515 .clobbers = .{ .eflags = true }, 14516 .each = .{ .once = &.{ 14517 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14518 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 14519 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14520 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14521 .{ ._, ._r, .bs, .tmp2q, .tmp1q, ._, ._ }, 14522 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14523 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14524 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14525 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14526 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14527 } }, 14528 }, .{ 14529 .required_features = .{ .@"64bit", null, null, null }, 14530 .src_constraints = .{ .{ .scalar_int_is = .qword }, .any }, 14531 .patterns = &.{ 14532 .{ .src = .{ .to_mem, .none } }, 14533 }, 14534 .extra_temps = .{ 14535 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14536 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14537 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14538 .unused, 14539 .unused, 14540 .unused, 14541 .unused, 14542 .unused, 14543 .unused, 14544 }, 14545 .dst_temps = .{.mem}, 14546 .clobbers = .{ .eflags = true }, 14547 .each = .{ .once = &.{ 14548 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14549 .{ .@"0:", ._, .mov, .tmp1q, .ua(.src0, .add_umax), ._, ._ }, 14550 .{ ._, ._, .@"and", .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_8_len), ._, ._ }, 14551 .{ ._, ._, .mov, .tmp2d, .si(0xff), ._, ._ }, 14552 .{ ._, ._r, .bs, .tmp2q, .tmp1q, ._, ._ }, 14553 .{ ._, ._, .mov, .tmp1b, .sia(-1, .src0, .add_bit_size), ._, ._ }, 14554 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 14555 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp1b, ._, ._ }, 14556 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 14557 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 14558 } }, 14559 }, .{ 14560 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 14561 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14562 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14563 .patterns = &.{ 14564 .{ .src = .{ .to_mem, .none } }, 14565 }, 14566 .extra_temps = .{ 14567 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14568 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14569 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14570 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14571 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14572 .unused, 14573 .unused, 14574 .unused, 14575 .unused, 14576 }, 14577 .dst_temps = .{.mem}, 14578 .clobbers = .{ .eflags = true }, 14579 .each = .{ .once = &.{ 14580 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14581 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14582 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14583 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14584 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14585 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 14586 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14587 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14588 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14589 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14590 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14591 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14592 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14593 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14594 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14595 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14596 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14597 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14598 } }, 14599 }, .{ 14600 .required_features = .{ .@"64bit", .lzcnt, null, null }, 14601 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14602 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14603 .patterns = &.{ 14604 .{ .src = .{ .to_mem, .none } }, 14605 }, 14606 .extra_temps = .{ 14607 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14608 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14609 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14610 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14611 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14612 .unused, 14613 .unused, 14614 .unused, 14615 .unused, 14616 }, 14617 .dst_temps = .{.mem}, 14618 .clobbers = .{ .eflags = true }, 14619 .each = .{ .once = &.{ 14620 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14621 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14622 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14623 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14624 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14625 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14626 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14627 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14628 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14629 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14630 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14631 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14632 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14633 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14634 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14635 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14636 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14637 } }, 14638 }, .{ 14639 .required_features = .{ .@"64bit", null, null, null }, 14640 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14641 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14642 .patterns = &.{ 14643 .{ .src = .{ .to_mem, .none } }, 14644 }, 14645 .extra_temps = .{ 14646 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14647 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14648 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14649 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14650 .unused, 14651 .unused, 14652 .unused, 14653 .unused, 14654 .unused, 14655 }, 14656 .dst_temps = .{.mem}, 14657 .clobbers = .{ .eflags = true }, 14658 .each = .{ .once = &.{ 14659 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14660 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14661 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14662 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14663 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14664 .{ ._, ._r, .bs, .tmp3q, .tmp3q, ._, ._ }, 14665 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 14666 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14667 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14668 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14669 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14670 .{ .@"1:", ._, .lea, .tmp3d, .leasiad(.none, .tmp3, .@"8", .tmp2, .sub_src0_bit_size, 1), ._, ._ }, 14671 .{ ._, ._, .neg, .tmp3b, ._, ._, ._ }, 14672 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14673 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14674 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14675 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14676 } }, 14677 }, .{ 14678 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 14679 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14680 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14681 .patterns = &.{ 14682 .{ .src = .{ .to_mem, .none } }, 14683 }, 14684 .extra_temps = .{ 14685 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14686 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14687 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14688 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14689 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14690 .unused, 14691 .unused, 14692 .unused, 14693 .unused, 14694 }, 14695 .dst_temps = .{.mem}, 14696 .clobbers = .{ .eflags = true }, 14697 .each = .{ .once = &.{ 14698 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14699 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14700 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 14701 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14702 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14703 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 14704 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14705 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14706 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14707 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14708 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14709 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14710 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14711 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14712 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14713 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14714 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14715 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14716 } }, 14717 }, .{ 14718 .required_features = .{ .@"64bit", .lzcnt, null, null }, 14719 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14720 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14721 .patterns = &.{ 14722 .{ .src = .{ .to_mem, .none } }, 14723 }, 14724 .extra_temps = .{ 14725 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14726 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14727 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14728 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14729 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14730 .unused, 14731 .unused, 14732 .unused, 14733 .unused, 14734 }, 14735 .dst_temps = .{.mem}, 14736 .clobbers = .{ .eflags = true }, 14737 .each = .{ .once = &.{ 14738 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14739 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14740 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 14741 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14742 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14743 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14744 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14745 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14746 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14747 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14748 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14749 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14750 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14751 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14752 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14753 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14754 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14755 } }, 14756 }, .{ 14757 .required_features = .{ .@"64bit", null, null, null }, 14758 .dst_constraints = .{.{ .scalar_int_is = .byte }}, 14759 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14760 .patterns = &.{ 14761 .{ .src = .{ .to_mem, .none } }, 14762 }, 14763 .extra_temps = .{ 14764 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14765 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14766 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14767 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14768 .unused, 14769 .unused, 14770 .unused, 14771 .unused, 14772 .unused, 14773 }, 14774 .dst_temps = .{.mem}, 14775 .clobbers = .{ .eflags = true }, 14776 .each = .{ .once = &.{ 14777 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14778 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14779 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 14780 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14781 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14782 .{ ._, ._r, .bs, .tmp3q, .tmp3q, ._, ._ }, 14783 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 14784 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14785 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14786 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14787 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14788 .{ .@"1:", ._, .lea, .tmp3d, .leasiad(.none, .tmp3, .@"8", .tmp2, .sub_src0_bit_size, 1), ._, ._ }, 14789 .{ ._, ._, .neg, .tmp3b, ._, ._, ._ }, 14790 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_len), .tmp3b, ._, ._ }, 14791 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14792 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14793 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14794 } }, 14795 }, .{ 14796 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 14797 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14798 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14799 .patterns = &.{ 14800 .{ .src = .{ .to_mem, .none } }, 14801 }, 14802 .extra_temps = .{ 14803 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14804 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14805 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14806 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14807 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14808 .unused, 14809 .unused, 14810 .unused, 14811 .unused, 14812 }, 14813 .dst_temps = .{.mem}, 14814 .clobbers = .{ .eflags = true }, 14815 .each = .{ .once = &.{ 14816 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14817 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14818 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14819 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14820 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14821 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 14822 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14823 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14824 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14825 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14826 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14827 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14828 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14829 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14830 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 14831 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14832 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14833 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14834 } }, 14835 }, .{ 14836 .required_features = .{ .@"64bit", .lzcnt, null, null }, 14837 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14838 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14839 .patterns = &.{ 14840 .{ .src = .{ .to_mem, .none } }, 14841 }, 14842 .extra_temps = .{ 14843 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14844 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14845 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14846 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14847 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14848 .unused, 14849 .unused, 14850 .unused, 14851 .unused, 14852 }, 14853 .dst_temps = .{.mem}, 14854 .clobbers = .{ .eflags = true }, 14855 .each = .{ .once = &.{ 14856 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14857 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14858 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14859 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14860 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14861 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14862 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14863 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14864 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14865 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14866 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14867 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14868 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14869 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 14870 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14871 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14872 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14873 } }, 14874 }, .{ 14875 .required_features = .{ .@"64bit", null, null, null }, 14876 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14877 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .qword } }, .any }, 14878 .patterns = &.{ 14879 .{ .src = .{ .to_mem, .none } }, 14880 }, 14881 .extra_temps = .{ 14882 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14883 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14884 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14885 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14886 .unused, 14887 .unused, 14888 .unused, 14889 .unused, 14890 .unused, 14891 }, 14892 .dst_temps = .{.mem}, 14893 .clobbers = .{ .eflags = true }, 14894 .each = .{ .once = &.{ 14895 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14896 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14897 .{ .@"0:", ._, .mov, .tmp2d, .sia(-16, .none, .add_src0_elem_size), ._, ._ }, 14898 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14899 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14900 .{ ._, ._r, .bs, .tmp3q, .tmp3q, ._, ._ }, 14901 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 14902 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14903 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14904 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14905 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14906 .{ .@"1:", ._, .lea, .tmp3d, .leasiad(.none, .tmp3, .@"8", .tmp2, .sub_src0_bit_size, 1), ._, ._ }, 14907 .{ ._, ._, .neg, .tmp3d, ._, ._, ._ }, 14908 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 14909 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14910 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14911 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14912 } }, 14913 }, .{ 14914 .required_features = .{ .@"64bit", .false_deps_lzcnt_tzcnt, .lzcnt, null }, 14915 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14916 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14917 .patterns = &.{ 14918 .{ .src = .{ .to_mem, .none } }, 14919 }, 14920 .extra_temps = .{ 14921 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14922 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14923 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14924 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14925 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14926 .unused, 14927 .unused, 14928 .unused, 14929 .unused, 14930 }, 14931 .dst_temps = .{.mem}, 14932 .clobbers = .{ .eflags = true }, 14933 .each = .{ .once = &.{ 14934 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14935 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14936 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 14937 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14938 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14939 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 14940 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14941 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14942 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14943 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14944 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14945 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14946 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14947 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14948 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 14949 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14950 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14951 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14952 } }, 14953 }, .{ 14954 .required_features = .{ .@"64bit", .lzcnt, null, null }, 14955 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14956 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14957 .patterns = &.{ 14958 .{ .src = .{ .to_mem, .none } }, 14959 }, 14960 .extra_temps = .{ 14961 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 14962 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 14963 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 14964 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14965 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 14966 .unused, 14967 .unused, 14968 .unused, 14969 .unused, 14970 }, 14971 .dst_temps = .{.mem}, 14972 .clobbers = .{ .eflags = true }, 14973 .each = .{ .once = &.{ 14974 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 14975 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 14976 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 14977 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 14978 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 14979 .{ ._, ._, .lzcnt, .tmp4q, .tmp3q, ._, ._ }, 14980 .{ ._, ._nc, .j, .@"1f", ._, ._, ._ }, 14981 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 14982 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 14983 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 14984 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 14985 .{ .@"1:", ._, .neg, .tmp2d, ._, ._, ._ }, 14986 .{ ._, ._, .lea, .tmp3d, .leasiad(.none, .tmp4, .@"8", .tmp2, .add_src0_bit_size, -64), ._, ._ }, 14987 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 14988 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 14989 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 14990 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 14991 } }, 14992 }, .{ 14993 .required_features = .{ .@"64bit", null, null, null }, 14994 .dst_constraints = .{.{ .scalar_int_is = .word }}, 14995 .src_constraints = .{ .{ .scalar_remainder_int = .{ .of = .xword, .is = .xword } }, .any }, 14996 .patterns = &.{ 14997 .{ .src = .{ .to_mem, .none } }, 14998 }, 14999 .extra_temps = .{ 15000 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15001 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 15002 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15003 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 15004 .unused, 15005 .unused, 15006 .unused, 15007 .unused, 15008 .unused, 15009 }, 15010 .dst_temps = .{.mem}, 15011 .clobbers = .{ .eflags = true }, 15012 .each = .{ .once = &.{ 15013 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_len), ._, ._ }, 15014 .{ ._, ._, .lea, .tmp1q, .mem(.src0), ._, ._ }, 15015 .{ .@"0:", ._, .mov, .tmp2d, .sia(-8, .none, .add_src0_elem_size), ._, ._ }, 15016 .{ ._, ._, .mov, .tmp3q, .ua(.src0, .add_umax), ._, ._ }, 15017 .{ .@"1:", ._, .@"and", .tmp3q, .leai(.qword, .tmp1, .tmp2), ._, ._ }, 15018 .{ ._, ._r, .bs, .tmp3q, .tmp3q, ._, ._ }, 15019 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15020 .{ ._, ._, .mov, .tmp3q, .si(-1), ._, ._ }, 15021 .{ ._, ._, .sub, .tmp2d, .si(8), ._, ._ }, 15022 .{ ._, ._nc, .j, .@"1b", ._, ._, ._ }, 15023 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 15024 .{ .@"1:", ._, .lea, .tmp3d, .leasiad(.none, .tmp3, .@"8", .tmp2, .sub_src0_bit_size, 1), ._, ._ }, 15025 .{ ._, ._, .neg, .tmp3d, ._, ._, ._ }, 15026 .{ ._, ._, .mov, .memsia(.dst0w, .@"2", .tmp0, .add_2_len), .tmp3w, ._, ._ }, 15027 .{ ._, ._, .lea, .tmp1q, .leaa(.none, .tmp1, .add_src0_elem_size), ._, ._ }, 15028 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 15029 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15030 } }, 15031 } }) catch |err| switch (err) { 15032 error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{ 15033 @tagName(air_tag), 15034 cg.typeOf(ty_op.operand).fmt(pt), 15035 ops[0].tracking(cg), 15036 }), 15037 else => |e| return e, 15038 }; 15039 try res[0].finish(inst, &.{ty_op.operand}, &ops, cg); 15040 }, 15041 15042 .cmp_vector, .cmp_vector_optimized => |air_tag| if (use_old) try cg.airCmpVector(inst) else fallback: { 15043 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 15044 const extra = cg.air.extraData(Air.VectorCmp, ty_pl.payload).data; 15045 switch (extra.compareOperator()) { 15046 .eq, .neq => {}, 15047 else => break :fallback try cg.airCmpVector(inst), 15048 } 15049 var ops = try cg.tempsFromOperands(inst, .{ extra.lhs, extra.rhs }); 15050 var res: [1]Temp = undefined; 15051 switch (extra.compareOperator()) { 15052 .lt => unreachable, 15053 .lte => unreachable, 15054 .eq, .neq => |cmp_op| cg.select(&res, &.{ty_pl.ty.toType()}, &ops, switch (@as(Condition, switch (cmp_op) { 15055 else => unreachable, 15056 .eq => .e, 15057 .neq => .ne, 15058 })) { 15059 else => unreachable, 15060 inline .e, .ne => |cc| comptime &.{ .{ 15061 .required_features = .{ .avx2, null, null, null }, 15062 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15063 .patterns = &.{ 15064 .{ .src = .{ .to_ymm, .mem } }, 15065 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 15066 .{ .src = .{ .to_ymm, .to_ymm } }, 15067 }, 15068 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15069 .kind = .all, 15070 .inverted = switch (cc) { 15071 else => unreachable, 15072 .e => false, 15073 .ne => true, 15074 }, 15075 .scalar = .byte, 15076 } } }}, 15077 .each = .{ .once = &.{ 15078 .{ ._, .vp_b, .cmpeq, .dst0y, .src0y, .src1y, ._ }, 15079 } }, 15080 }, .{ 15081 .required_features = .{ .avx2, null, null, null }, 15082 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15083 .patterns = &.{ 15084 .{ .src = .{ .to_ymm, .mem } }, 15085 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 15086 .{ .src = .{ .to_ymm, .to_ymm } }, 15087 }, 15088 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15089 .kind = .all, 15090 .inverted = switch (cc) { 15091 else => unreachable, 15092 .e => false, 15093 .ne => true, 15094 }, 15095 .scalar = .word, 15096 } } }}, 15097 .each = .{ .once = &.{ 15098 .{ ._, .vp_w, .cmpeq, .dst0y, .src0y, .src1y, ._ }, 15099 } }, 15100 }, .{ 15101 .required_features = .{ .avx2, null, null, null }, 15102 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15103 .patterns = &.{ 15104 .{ .src = .{ .to_ymm, .mem } }, 15105 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 15106 .{ .src = .{ .to_ymm, .to_ymm } }, 15107 }, 15108 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15109 .kind = .all, 15110 .inverted = switch (cc) { 15111 else => unreachable, 15112 .e => false, 15113 .ne => true, 15114 }, 15115 .scalar = .dword, 15116 } } }}, 15117 .each = .{ .once = &.{ 15118 .{ ._, .vp_d, .cmpeq, .dst0y, .src0y, .src1y, ._ }, 15119 } }, 15120 }, .{ 15121 .required_features = .{ .avx2, null, null, null }, 15122 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 15123 .patterns = &.{ 15124 .{ .src = .{ .to_ymm, .mem } }, 15125 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 15126 .{ .src = .{ .to_ymm, .to_ymm } }, 15127 }, 15128 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15129 .kind = .all, 15130 .inverted = switch (cc) { 15131 else => unreachable, 15132 .e => false, 15133 .ne => true, 15134 }, 15135 .scalar = .qword, 15136 } } }}, 15137 .each = .{ .once = &.{ 15138 .{ ._, .vp_q, .cmpeq, .dst0y, .src0y, .src1y, ._ }, 15139 } }, 15140 }, .{ 15141 .required_features = .{ .avx, null, null, null }, 15142 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15143 .patterns = &.{ 15144 .{ .src = .{ .to_xmm, .mem } }, 15145 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 15146 .{ .src = .{ .to_xmm, .to_xmm } }, 15147 }, 15148 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15149 .kind = .all, 15150 .inverted = switch (cc) { 15151 else => unreachable, 15152 .e => false, 15153 .ne => true, 15154 }, 15155 .scalar = .byte, 15156 } } }}, 15157 .each = .{ .once = &.{ 15158 .{ ._, .vp_b, .cmpeq, .dst0x, .src0x, .src1x, ._ }, 15159 } }, 15160 }, .{ 15161 .required_features = .{ .avx, null, null, null }, 15162 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15163 .patterns = &.{ 15164 .{ .src = .{ .to_xmm, .mem } }, 15165 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 15166 .{ .src = .{ .to_xmm, .to_xmm } }, 15167 }, 15168 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15169 .kind = .all, 15170 .inverted = switch (cc) { 15171 else => unreachable, 15172 .e => false, 15173 .ne => true, 15174 }, 15175 .scalar = .word, 15176 } } }}, 15177 .each = .{ .once = &.{ 15178 .{ ._, .vp_w, .cmpeq, .dst0x, .src0x, .src1x, ._ }, 15179 } }, 15180 }, .{ 15181 .required_features = .{ .avx, null, null, null }, 15182 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15183 .patterns = &.{ 15184 .{ .src = .{ .to_xmm, .mem } }, 15185 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 15186 .{ .src = .{ .to_xmm, .to_xmm } }, 15187 }, 15188 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15189 .kind = .all, 15190 .inverted = switch (cc) { 15191 else => unreachable, 15192 .e => false, 15193 .ne => true, 15194 }, 15195 .scalar = .dword, 15196 } } }}, 15197 .each = .{ .once = &.{ 15198 .{ ._, .vp_d, .cmpeq, .dst0x, .src0x, .src1x, ._ }, 15199 } }, 15200 }, .{ 15201 .required_features = .{ .avx, null, null, null }, 15202 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 15203 .patterns = &.{ 15204 .{ .src = .{ .to_xmm, .mem } }, 15205 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 15206 .{ .src = .{ .to_xmm, .to_xmm } }, 15207 }, 15208 .dst_temps = .{.{ .mut_rc_mask = .{ .ref = .src0, .rc = .sse, .info = .{ 15209 .kind = .all, 15210 .inverted = switch (cc) { 15211 else => unreachable, 15212 .e => false, 15213 .ne => true, 15214 }, 15215 .scalar = .qword, 15216 } } }}, 15217 .each = .{ .once = &.{ 15218 .{ ._, .vp_q, .cmpeq, .dst0x, .src0x, .src1x, ._ }, 15219 } }, 15220 }, .{ 15221 .required_features = .{ .sse2, null, null, null }, 15222 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15223 .patterns = &.{ 15224 .{ .src = .{ .to_mut_xmm, .mem } }, 15225 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 15226 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 15227 }, 15228 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15229 .kind = .all, 15230 .inverted = switch (cc) { 15231 else => unreachable, 15232 .e => false, 15233 .ne => true, 15234 }, 15235 .scalar = .byte, 15236 } } }}, 15237 .each = .{ .once = &.{ 15238 .{ ._, .p_b, .cmpeq, .dst0x, .src1x, ._, ._ }, 15239 } }, 15240 }, .{ 15241 .required_features = .{ .sse2, null, null, null }, 15242 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15243 .patterns = &.{ 15244 .{ .src = .{ .to_mut_xmm, .mem } }, 15245 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 15246 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 15247 }, 15248 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15249 .kind = .all, 15250 .inverted = switch (cc) { 15251 else => unreachable, 15252 .e => false, 15253 .ne => true, 15254 }, 15255 .scalar = .word, 15256 } } }}, 15257 .each = .{ .once = &.{ 15258 .{ ._, .p_w, .cmpeq, .dst0x, .src1x, ._, ._ }, 15259 } }, 15260 }, .{ 15261 .required_features = .{ .sse2, null, null, null }, 15262 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15263 .patterns = &.{ 15264 .{ .src = .{ .to_mut_xmm, .mem } }, 15265 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 15266 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 15267 }, 15268 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15269 .kind = .all, 15270 .inverted = switch (cc) { 15271 else => unreachable, 15272 .e => false, 15273 .ne => true, 15274 }, 15275 .scalar = .dword, 15276 } } }}, 15277 .each = .{ .once = &.{ 15278 .{ ._, .p_d, .cmpeq, .dst0x, .src1x, ._, ._ }, 15279 } }, 15280 }, .{ 15281 .required_features = .{ .sse4_1, null, null, null }, 15282 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 15283 .patterns = &.{ 15284 .{ .src = .{ .to_mut_xmm, .mem } }, 15285 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 15286 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 15287 }, 15288 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15289 .kind = .all, 15290 .inverted = switch (cc) { 15291 else => unreachable, 15292 .e => false, 15293 .ne => true, 15294 }, 15295 .scalar = .qword, 15296 } } }}, 15297 .each = .{ .once = &.{ 15298 .{ ._, .p_q, .cmpeq, .dst0x, .src1x, ._, ._ }, 15299 } }, 15300 }, .{ 15301 .required_features = .{ .mmx, null, null, null }, 15302 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15303 .patterns = &.{ 15304 .{ .src = .{ .to_mut_mm, .mem } }, 15305 .{ .src = .{ .mem, .to_mut_mm }, .commute = .{ 0, 1 } }, 15306 .{ .src = .{ .to_mut_mm, .to_mm } }, 15307 }, 15308 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15309 .kind = .all, 15310 .inverted = switch (cc) { 15311 else => unreachable, 15312 .e => false, 15313 .ne => true, 15314 }, 15315 .scalar = .byte, 15316 } } }}, 15317 .each = .{ .once = &.{ 15318 .{ ._, .p_b, .cmpeq, .dst0q, .src1q, ._, ._ }, 15319 } }, 15320 }, .{ 15321 .required_features = .{ .mmx, null, null, null }, 15322 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15323 .patterns = &.{ 15324 .{ .src = .{ .to_mut_mm, .mem } }, 15325 .{ .src = .{ .mem, .to_mut_mm }, .commute = .{ 0, 1 } }, 15326 .{ .src = .{ .to_mut_mm, .to_mm } }, 15327 }, 15328 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15329 .kind = .all, 15330 .inverted = switch (cc) { 15331 else => unreachable, 15332 .e => false, 15333 .ne => true, 15334 }, 15335 .scalar = .word, 15336 } } }}, 15337 .each = .{ .once = &.{ 15338 .{ ._, .p_w, .cmpeq, .dst0q, .src1q, ._, ._ }, 15339 } }, 15340 }, .{ 15341 .required_features = .{ .mmx, null, null, null }, 15342 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15343 .patterns = &.{ 15344 .{ .src = .{ .to_mut_mm, .mem } }, 15345 .{ .src = .{ .mem, .to_mut_mm }, .commute = .{ 0, 1 } }, 15346 .{ .src = .{ .to_mut_mm, .to_mm } }, 15347 }, 15348 .dst_temps = .{.{ .ref_mask = .{ .ref = .src0, .info = .{ 15349 .kind = .all, 15350 .inverted = switch (cc) { 15351 else => unreachable, 15352 .e => false, 15353 .ne => true, 15354 }, 15355 .scalar = .dword, 15356 } } }}, 15357 .each = .{ .once = &.{ 15358 .{ ._, .p_d, .cmpeq, .dst0q, .src1q, ._, ._ }, 15359 } }, 15360 }, .{ 15361 .src_constraints = .{ .{ .bool_vec = .byte }, .{ .bool_vec = .byte } }, 15362 .patterns = &.{ 15363 .{ .src = .{ .mut_mem, .imm8 } }, 15364 .{ .src = .{ .imm8, .mut_mem }, .commute = .{ 0, 1 } }, 15365 .{ .src = .{ .to_mut_gpr, .imm8 } }, 15366 .{ .src = .{ .imm8, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15367 .{ .src = .{ .mut_mem, .to_gpr } }, 15368 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 15369 .{ .src = .{ .to_mut_gpr, .mem } }, 15370 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15371 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 15372 }, 15373 .dst_temps = .{.{ .ref = .src0 }}, 15374 .clobbers = .{ .eflags = true }, 15375 .each = .{ .once = switch (cc) { 15376 else => unreachable, 15377 .e => &.{ 15378 .{ ._, ._, .xor, .dst0b, .src1b, ._, ._ }, 15379 .{ ._, ._, .not, .dst0b, ._, ._, ._ }, 15380 }, 15381 .ne => &.{ 15382 .{ ._, ._, .xor, .dst0b, .src1b, ._, ._ }, 15383 }, 15384 } }, 15385 }, .{ 15386 .src_constraints = .{ .{ .bool_vec = .word }, .{ .bool_vec = .word } }, 15387 .patterns = &.{ 15388 .{ .src = .{ .mut_mem, .imm16 } }, 15389 .{ .src = .{ .imm16, .mut_mem }, .commute = .{ 0, 1 } }, 15390 .{ .src = .{ .to_mut_gpr, .imm16 } }, 15391 .{ .src = .{ .imm16, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15392 .{ .src = .{ .mut_mem, .to_gpr } }, 15393 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 15394 .{ .src = .{ .to_mut_gpr, .mem } }, 15395 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15396 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 15397 }, 15398 .dst_temps = .{.{ .ref = .src0 }}, 15399 .clobbers = .{ .eflags = true }, 15400 .each = .{ .once = switch (cc) { 15401 else => unreachable, 15402 .e => &.{ 15403 .{ ._, ._, .xor, .dst0w, .src1w, ._, ._ }, 15404 .{ ._, ._, .not, .dst0w, ._, ._, ._ }, 15405 }, 15406 .ne => &.{ 15407 .{ ._, ._, .xor, .dst0w, .src1w, ._, ._ }, 15408 }, 15409 } }, 15410 }, .{ 15411 .src_constraints = .{ .{ .bool_vec = .dword }, .{ .bool_vec = .dword } }, 15412 .patterns = &.{ 15413 .{ .src = .{ .mut_mem, .imm32 } }, 15414 .{ .src = .{ .imm32, .mut_mem }, .commute = .{ 0, 1 } }, 15415 .{ .src = .{ .to_mut_gpr, .imm32 } }, 15416 .{ .src = .{ .imm32, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15417 .{ .src = .{ .mut_mem, .to_gpr } }, 15418 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 15419 .{ .src = .{ .to_mut_gpr, .mem } }, 15420 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15421 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 15422 }, 15423 .dst_temps = .{.{ .ref = .src0 }}, 15424 .clobbers = .{ .eflags = true }, 15425 .each = .{ .once = switch (cc) { 15426 else => unreachable, 15427 .e => &.{ 15428 .{ ._, ._, .xor, .dst0d, .src1d, ._, ._ }, 15429 .{ ._, ._, .not, .dst0d, ._, ._, ._ }, 15430 }, 15431 .ne => &.{ 15432 .{ ._, ._, .xor, .dst0d, .src1d, ._, ._ }, 15433 }, 15434 } }, 15435 }, .{ 15436 .required_features = .{ .@"64bit", null, null, null }, 15437 .src_constraints = .{ .{ .bool_vec = .qword }, .{ .bool_vec = .qword } }, 15438 .patterns = &.{ 15439 .{ .src = .{ .mut_mem, .simm32 } }, 15440 .{ .src = .{ .simm32, .mut_mem }, .commute = .{ 0, 1 } }, 15441 .{ .src = .{ .to_mut_gpr, .simm32 } }, 15442 .{ .src = .{ .simm32, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15443 .{ .src = .{ .mut_mem, .to_gpr } }, 15444 .{ .src = .{ .to_gpr, .mut_mem }, .commute = .{ 0, 1 } }, 15445 .{ .src = .{ .to_mut_gpr, .mem } }, 15446 .{ .src = .{ .mem, .to_mut_gpr }, .commute = .{ 0, 1 } }, 15447 .{ .src = .{ .to_mut_gpr, .to_gpr } }, 15448 }, 15449 .dst_temps = .{.{ .ref = .src0 }}, 15450 .clobbers = .{ .eflags = true }, 15451 .each = .{ .once = switch (cc) { 15452 else => unreachable, 15453 .e => &.{ 15454 .{ ._, ._, .xor, .dst0q, .src1q, ._, ._ }, 15455 .{ ._, ._, .not, .dst0q, ._, ._, ._ }, 15456 }, 15457 .ne => &.{ 15458 .{ ._, ._, .xor, .dst0q, .src1q, ._, ._ }, 15459 }, 15460 } }, 15461 }, .{ 15462 .src_constraints = .{ .any_bool_vec, .any_bool_vec }, 15463 .patterns = &.{ 15464 .{ .src = .{ .to_mem, .to_mem } }, 15465 }, 15466 .extra_temps = .{ 15467 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15468 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 15469 .unused, 15470 .unused, 15471 .unused, 15472 .unused, 15473 .unused, 15474 .unused, 15475 .unused, 15476 }, 15477 .dst_temps = .{.mem}, 15478 .clobbers = .{ .eflags = true }, 15479 .each = .{ .once = switch (cc) { 15480 else => unreachable, 15481 .e => &.{ 15482 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15483 .{ .@"0:", ._, .mov, .tmp1p, .memia(.src0p, .tmp0, .add_size), ._, ._ }, 15484 .{ ._, ._, .xor, .tmp1p, .memia(.src1p, .tmp0, .add_size), ._, ._ }, 15485 .{ ._, ._, .not, .tmp1p, ._, ._, ._ }, 15486 .{ ._, ._, .mov, .memia(.dst0p, .tmp0, .add_size), .tmp1p, ._, ._ }, 15487 .{ ._, ._, .add, .tmp0p, .sa(.tmp1, .add_size), ._, ._ }, 15488 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15489 }, 15490 .ne => &.{ 15491 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15492 .{ .@"0:", ._, .mov, .tmp1p, .memia(.src0p, .tmp0, .add_size), ._, ._ }, 15493 .{ ._, ._, .xor, .tmp1p, .memia(.src1p, .tmp0, .add_size), ._, ._ }, 15494 .{ ._, ._, .mov, .memia(.dst0p, .tmp0, .add_size), .tmp1p, ._, ._ }, 15495 .{ ._, ._, .add, .tmp0p, .sa(.tmp1, .add_size), ._, ._ }, 15496 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15497 }, 15498 } }, 15499 }, .{ 15500 .required_features = .{ .avx2, null, null, null }, 15501 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15502 .patterns = &.{ 15503 .{ .src = .{ .to_mem, .to_mem } }, 15504 }, 15505 .extra_temps = .{ 15506 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15507 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15508 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15509 .{ .kind = .{ .rc = .sse } }, 15510 .unused, 15511 .unused, 15512 .unused, 15513 .unused, 15514 .unused, 15515 }, 15516 .dst_temps = .{.mem}, 15517 .clobbers = .{ .eflags = true }, 15518 .each = .{ .once = switch (cc) { 15519 else => unreachable, 15520 .e => &.{ 15521 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15522 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15523 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15524 .{ ._, .vp_b, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15525 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15526 .{ ._, ._, .mov, .memi(.dst0d, .tmp1), .tmp2d, ._, ._ }, 15527 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15528 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15529 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15530 }, 15531 .ne => &.{ 15532 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15533 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15534 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15535 .{ ._, .vp_b, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15536 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15537 .{ ._, ._, .not, .tmp2d, ._, ._, ._ }, 15538 .{ ._, ._, .mov, .memi(.dst0d, .tmp1), .tmp2d, ._, ._ }, 15539 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15540 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15541 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15542 }, 15543 } }, 15544 }, .{ 15545 .required_features = .{ .avx2, null, null, null }, 15546 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15547 .patterns = &.{ 15548 .{ .src = .{ .to_mem, .to_mem } }, 15549 }, 15550 .extra_temps = .{ 15551 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15552 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15553 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 15554 .{ .kind = .{ .rc = .sse } }, 15555 .unused, 15556 .unused, 15557 .unused, 15558 .unused, 15559 .unused, 15560 }, 15561 .dst_temps = .{.mem}, 15562 .clobbers = .{ .eflags = true }, 15563 .each = .{ .once = switch (cc) { 15564 else => unreachable, 15565 .e => &.{ 15566 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15567 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15568 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15569 .{ ._, .vp_w, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15570 .{ ._, .vp_b, .ackssw, .tmp3y, .tmp3y, .tmp3y, ._ }, 15571 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15572 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15573 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15574 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15575 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15576 }, 15577 .ne => &.{ 15578 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15579 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15580 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15581 .{ ._, .vp_w, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15582 .{ ._, .vp_b, .ackssw, .tmp3y, .tmp3y, .tmp3y, ._ }, 15583 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15584 .{ ._, ._, .not, .tmp2d, ._, ._, ._ }, 15585 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15586 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15587 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15588 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15589 }, 15590 } }, 15591 }, .{ 15592 .required_features = .{ .avx2, null, null, null }, 15593 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15594 .patterns = &.{ 15595 .{ .src = .{ .to_mem, .to_mem } }, 15596 }, 15597 .extra_temps = .{ 15598 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15599 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15600 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15601 .{ .kind = .{ .rc = .sse } }, 15602 .unused, 15603 .unused, 15604 .unused, 15605 .unused, 15606 .unused, 15607 }, 15608 .dst_temps = .{.mem}, 15609 .clobbers = .{ .eflags = true }, 15610 .each = .{ .once = switch (cc) { 15611 else => unreachable, 15612 .e => &.{ 15613 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15614 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15615 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15616 .{ ._, .vp_d, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15617 .{ ._, .v_ps, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15618 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15619 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 15620 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15621 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15622 }, 15623 .ne => &.{ 15624 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15625 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15626 .{ .@"0:", .v_dqu, .mov, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15627 .{ ._, .vp_d, .cmpeq, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), ._ }, 15628 .{ ._, .v_ps, .movmsk, .tmp2d, .tmp3y, ._, ._ }, 15629 .{ ._, ._, .not, .tmp2b, ._, ._, ._ }, 15630 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15631 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 15632 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 15633 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15634 }, 15635 } }, 15636 }, .{ 15637 .required_features = .{ .avx2, null, null, null }, 15638 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 15639 .patterns = &.{ 15640 .{ .src = .{ .to_mem, .to_mem } }, 15641 }, 15642 .extra_temps = .{ 15643 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15644 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 15645 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15646 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15647 .{ .kind = .{ .rc = .sse } }, 15648 .unused, 15649 .unused, 15650 .unused, 15651 .unused, 15652 }, 15653 .dst_temps = .{.mem}, 15654 .clobbers = .{ .eflags = true }, 15655 .each = .{ .once = switch (cc) { 15656 else => unreachable, 15657 .e => &.{ 15658 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15659 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15660 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15661 .{ .@"0:", .v_dqu, .mov, .tmp4y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15662 .{ ._, .vp_q, .cmpeq, .tmp4y, .tmp4y, .memia(.src1y, .tmp0, .add_size), ._ }, 15663 .{ ._, .v_pd, .movmsk, .tmp3d, .tmp4y, ._, ._ }, 15664 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15665 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15666 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15667 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15668 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15669 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15670 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15671 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15672 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15673 .{ .@"1:", ._, .add, .tmp0p, .si(32), ._, ._ }, 15674 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15675 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15676 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15677 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15678 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15679 }, 15680 .ne => &.{ 15681 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15682 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15683 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15684 .{ .@"0:", .v_dqu, .mov, .tmp4y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 15685 .{ ._, .vp_q, .cmpeq, .tmp4y, .tmp4y, .memia(.src1y, .tmp0, .add_size), ._ }, 15686 .{ ._, .v_pd, .movmsk, .tmp3d, .tmp4y, ._, ._ }, 15687 .{ ._, ._, .xor, .tmp3b, .si(0b1111), ._, ._ }, 15688 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15689 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15690 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15691 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15692 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15693 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15694 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15695 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15696 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15697 .{ .@"1:", ._, .add, .tmp0p, .si(32), ._, ._ }, 15698 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15699 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15700 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15701 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15702 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15703 }, 15704 } }, 15705 }, .{ 15706 .required_features = .{ .avx, null, null, null }, 15707 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15708 .patterns = &.{ 15709 .{ .src = .{ .to_mem, .to_mem } }, 15710 }, 15711 .extra_temps = .{ 15712 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15713 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15714 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 15715 .{ .kind = .{ .rc = .sse } }, 15716 .unused, 15717 .unused, 15718 .unused, 15719 .unused, 15720 .unused, 15721 }, 15722 .dst_temps = .{.mem}, 15723 .clobbers = .{ .eflags = true }, 15724 .each = .{ .once = switch (cc) { 15725 else => unreachable, 15726 .e => &.{ 15727 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15728 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15729 .{ .@"0:", .v_dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15730 .{ ._, .vp_b, .cmpeq, .tmp3x, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._ }, 15731 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15732 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15733 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15734 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15735 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15736 }, 15737 .ne => &.{ 15738 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15739 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15740 .{ .@"0:", .v_dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15741 .{ ._, .vp_b, .cmpeq, .tmp3x, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._ }, 15742 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15743 .{ ._, ._, .not, .tmp2d, ._, ._, ._ }, 15744 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15745 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15746 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15747 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15748 }, 15749 } }, 15750 }, .{ 15751 .required_features = .{ .avx, null, null, null }, 15752 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15753 .patterns = &.{ 15754 .{ .src = .{ .to_mem, .to_mem } }, 15755 }, 15756 .extra_temps = .{ 15757 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15758 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15759 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15760 .{ .kind = .{ .rc = .sse } }, 15761 .unused, 15762 .unused, 15763 .unused, 15764 .unused, 15765 .unused, 15766 }, 15767 .dst_temps = .{.mem}, 15768 .clobbers = .{ .eflags = true }, 15769 .each = .{ .once = switch (cc) { 15770 else => unreachable, 15771 .e => &.{ 15772 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15773 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15774 .{ .@"0:", .v_dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15775 .{ ._, .vp_w, .cmpeq, .tmp3x, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._ }, 15776 .{ ._, .vp_b, .ackssw, .tmp3x, .tmp3x, .tmp3x, ._ }, 15777 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15778 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15779 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 15780 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15781 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15782 }, 15783 .ne => &.{ 15784 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15785 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15786 .{ .@"0:", .v_dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15787 .{ ._, .vp_w, .cmpeq, .tmp3x, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._ }, 15788 .{ ._, .vp_b, .ackssw, .tmp3x, .tmp3x, .tmp3x, ._ }, 15789 .{ ._, .vp_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15790 .{ ._, ._, .not, .tmp2b, ._, ._, ._ }, 15791 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15792 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 15793 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15794 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15795 }, 15796 } }, 15797 }, .{ 15798 .required_features = .{ .avx, null, null, null }, 15799 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 15800 .patterns = &.{ 15801 .{ .src = .{ .to_mem, .to_mem } }, 15802 }, 15803 .extra_temps = .{ 15804 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15805 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 15806 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15807 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15808 .{ .kind = .{ .rc = .sse } }, 15809 .unused, 15810 .unused, 15811 .unused, 15812 .unused, 15813 }, 15814 .dst_temps = .{.mem}, 15815 .clobbers = .{ .eflags = true }, 15816 .each = .{ .once = switch (cc) { 15817 else => unreachable, 15818 .e => &.{ 15819 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15820 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15821 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15822 .{ .@"0:", .v_dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15823 .{ ._, .vp_d, .cmpeq, .tmp4x, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._ }, 15824 .{ ._, .v_ps, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 15825 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15826 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15827 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15828 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15829 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15830 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15831 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15832 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15833 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15834 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 15835 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15836 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15837 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15838 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15839 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15840 }, 15841 .ne => &.{ 15842 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15843 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15844 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15845 .{ .@"0:", .v_dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15846 .{ ._, .vp_d, .cmpeq, .tmp4x, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._ }, 15847 .{ ._, .v_ps, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 15848 .{ ._, ._, .xor, .tmp3b, .si(0b1111), ._, ._ }, 15849 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15850 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15851 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 15852 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15853 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15854 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15855 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15856 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15857 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15858 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 15859 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15860 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15861 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15862 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15863 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15864 }, 15865 } }, 15866 }, .{ 15867 .required_features = .{ .avx, null, null, null }, 15868 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 15869 .patterns = &.{ 15870 .{ .src = .{ .to_mem, .to_mem } }, 15871 }, 15872 .extra_temps = .{ 15873 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15874 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 15875 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15876 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15877 .{ .kind = .{ .rc = .sse } }, 15878 .unused, 15879 .unused, 15880 .unused, 15881 .unused, 15882 }, 15883 .dst_temps = .{.mem}, 15884 .clobbers = .{ .eflags = true }, 15885 .each = .{ .once = switch (cc) { 15886 else => unreachable, 15887 .e => &.{ 15888 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15889 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15890 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15891 .{ .@"0:", .v_dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15892 .{ ._, .vp_q, .cmpeq, .tmp4x, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._ }, 15893 .{ ._, .v_pd, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 15894 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15895 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15896 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15897 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15898 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15899 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15900 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15901 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15902 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15903 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 15904 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15905 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15906 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15907 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15908 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15909 }, 15910 .ne => &.{ 15911 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15912 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15913 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15914 .{ .@"0:", .v_dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15915 .{ ._, .vp_q, .cmpeq, .tmp4x, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._ }, 15916 .{ ._, .v_pd, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 15917 .{ ._, ._, .xor, .tmp3b, .si(0b11), ._, ._ }, 15918 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 15919 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 15920 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15921 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15922 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 15923 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 15924 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 15925 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 15926 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 15927 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 15928 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15929 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 15930 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 15931 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 15932 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 15933 }, 15934 } }, 15935 }, .{ 15936 .required_features = .{ .sse2, null, null, null }, 15937 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 15938 .patterns = &.{ 15939 .{ .src = .{ .to_mem, .to_mem } }, 15940 }, 15941 .extra_temps = .{ 15942 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15943 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15944 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 15945 .{ .kind = .{ .rc = .sse } }, 15946 .unused, 15947 .unused, 15948 .unused, 15949 .unused, 15950 .unused, 15951 }, 15952 .dst_temps = .{.mem}, 15953 .clobbers = .{ .eflags = true }, 15954 .each = .{ .once = switch (cc) { 15955 else => unreachable, 15956 .e => &.{ 15957 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15958 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15959 .{ .@"0:", ._dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15960 .{ ._, .p_b, .cmpeq, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 15961 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15962 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15963 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15964 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15965 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15966 }, 15967 .ne => &.{ 15968 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 15969 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 15970 .{ .@"0:", ._dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 15971 .{ ._, .p_b, .cmpeq, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 15972 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 15973 .{ ._, ._, .not, .tmp2d, ._, ._, ._ }, 15974 .{ ._, ._, .mov, .memi(.dst0w, .tmp1), .tmp2w, ._, ._ }, 15975 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 15976 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 15977 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 15978 }, 15979 } }, 15980 }, .{ 15981 .required_features = .{ .sse2, null, null, null }, 15982 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 15983 .patterns = &.{ 15984 .{ .src = .{ .to_mem, .to_mem } }, 15985 }, 15986 .extra_temps = .{ 15987 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 15988 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 15989 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 15990 .{ .kind = .{ .rc = .sse } }, 15991 .unused, 15992 .unused, 15993 .unused, 15994 .unused, 15995 .unused, 15996 }, 15997 .dst_temps = .{.mem}, 15998 .clobbers = .{ .eflags = true }, 15999 .each = .{ .once = switch (cc) { 16000 else => unreachable, 16001 .e => &.{ 16002 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16003 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16004 .{ .@"0:", ._dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16005 .{ ._, .p_w, .cmpeq, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16006 .{ ._, .p_b, .ackssw, .tmp3x, .tmp3x, ._, ._ }, 16007 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 16008 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16009 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 16010 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 16011 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16012 }, 16013 .ne => &.{ 16014 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16015 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16016 .{ .@"0:", ._dqu, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16017 .{ ._, .p_w, .cmpeq, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16018 .{ ._, .p_b, .ackssw, .tmp3x, .tmp3x, ._, ._ }, 16019 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3x, ._, ._ }, 16020 .{ ._, ._, .not, .tmp2b, ._, ._, ._ }, 16021 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16022 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 16023 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 16024 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16025 }, 16026 } }, 16027 }, .{ 16028 .required_features = .{ .sse2, null, null, null }, 16029 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 16030 .patterns = &.{ 16031 .{ .src = .{ .to_mem, .to_mem } }, 16032 }, 16033 .extra_temps = .{ 16034 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16035 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 16036 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16037 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16038 .{ .kind = .{ .rc = .sse } }, 16039 .unused, 16040 .unused, 16041 .unused, 16042 .unused, 16043 }, 16044 .dst_temps = .{.mem}, 16045 .clobbers = .{ .eflags = true }, 16046 .each = .{ .once = switch (cc) { 16047 else => unreachable, 16048 .e => &.{ 16049 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16050 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16051 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16052 .{ .@"0:", ._dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16053 .{ ._, .p_d, .cmpeq, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16054 .{ ._, ._ps, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 16055 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 16056 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 16057 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 16058 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16059 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16060 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 16061 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 16062 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 16063 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16064 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 16065 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16066 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16067 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16068 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16069 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16070 }, 16071 .ne => &.{ 16072 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16073 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16074 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16075 .{ .@"0:", ._dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16076 .{ ._, .p_d, .cmpeq, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16077 .{ ._, ._ps, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 16078 .{ ._, ._, .xor, .tmp3b, .si(0b1111), ._, ._ }, 16079 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 16080 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 16081 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 16082 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16083 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16084 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 16085 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 16086 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 16087 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16088 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 16089 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16090 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16091 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16092 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16093 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16094 }, 16095 } }, 16096 }, .{ 16097 .required_features = .{ .sse4_1, null, null, null }, 16098 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 16099 .patterns = &.{ 16100 .{ .src = .{ .to_mem, .to_mem } }, 16101 }, 16102 .extra_temps = .{ 16103 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16104 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 16105 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16106 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16107 .{ .kind = .{ .rc = .sse } }, 16108 .unused, 16109 .unused, 16110 .unused, 16111 .unused, 16112 }, 16113 .dst_temps = .{.mem}, 16114 .clobbers = .{ .eflags = true }, 16115 .each = .{ .once = switch (cc) { 16116 else => unreachable, 16117 .e => &.{ 16118 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16119 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16120 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16121 .{ .@"0:", ._dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16122 .{ ._, .p_q, .cmpeq, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16123 .{ ._, ._pd, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 16124 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 16125 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 16126 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 16127 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16128 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16129 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 16130 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 16131 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 16132 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16133 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 16134 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16135 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16136 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16137 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16138 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16139 }, 16140 .ne => &.{ 16141 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16142 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16143 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16144 .{ .@"0:", ._dqu, .mov, .tmp4x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 16145 .{ ._, .p_q, .cmpeq, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 16146 .{ ._, ._pd, .movmsk, .tmp3d, .tmp4x, ._, ._ }, 16147 .{ ._, ._, .xor, .tmp3b, .si(0b11), ._, ._ }, 16148 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 16149 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 16150 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 16151 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16152 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16153 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 16154 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 16155 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 16156 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16157 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 16158 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16159 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16160 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16161 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16162 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16163 }, 16164 } }, 16165 }, .{ 16166 .required_features = .{ .sse, .mmx, null, null }, 16167 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 16168 .patterns = &.{ 16169 .{ .src = .{ .to_mem, .to_mem } }, 16170 }, 16171 .extra_temps = .{ 16172 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16173 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16174 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16175 .{ .kind = .{ .rc = .mmx } }, 16176 .unused, 16177 .unused, 16178 .unused, 16179 .unused, 16180 .unused, 16181 }, 16182 .dst_temps = .{.mem}, 16183 .clobbers = .{ .eflags = true }, 16184 .each = .{ .once = switch (cc) { 16185 else => unreachable, 16186 .e => &.{ 16187 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16188 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16189 .{ .@"0:", ._q, .mov, .tmp3q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16190 .{ ._, .p_b, .cmpeq, .tmp3q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16191 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3q, ._, ._ }, 16192 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16193 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 16194 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 16195 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16196 }, 16197 .ne => &.{ 16198 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16199 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16200 .{ .@"0:", ._q, .mov, .tmp3q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16201 .{ ._, .p_b, .cmpeq, .tmp3q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16202 .{ ._, .p_b, .movmsk, .tmp2d, .tmp3q, ._, ._ }, 16203 .{ ._, ._, .not, .tmp2b, ._, ._, ._ }, 16204 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16205 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 16206 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 16207 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16208 }, 16209 } }, 16210 }, .{ 16211 .required_features = .{ .sse, .mmx, null, null }, 16212 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 16213 .patterns = &.{ 16214 .{ .src = .{ .to_mem, .to_mem } }, 16215 }, 16216 .extra_temps = .{ 16217 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16218 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16219 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16220 .{ .kind = .{ .rc = .mmx } }, 16221 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16222 .{ .kind = .{ .rc = .mmx } }, 16223 .unused, 16224 .unused, 16225 .unused, 16226 }, 16227 .dst_temps = .{.mem}, 16228 .clobbers = .{ .eflags = true }, 16229 .each = .{ .once = switch (cc) { 16230 else => unreachable, 16231 .e => &.{ 16232 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16233 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16234 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16235 .{ ._, .p_, .xor, .tmp3q, .tmp3q, ._, ._ }, 16236 .{ .@"0:", ._q, .mov, .tmp5q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16237 .{ ._, .p_w, .cmpeq, .tmp5q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16238 .{ ._, .p_b, .ackssw, .tmp5q, .tmp3q, ._, ._ }, 16239 .{ ._, .p_b, .movmsk, .tmp4d, .tmp5q, ._, ._ }, 16240 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 16241 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 16242 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 16243 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16244 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16245 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 16246 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 16247 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 16248 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16249 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 16250 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16251 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16252 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16253 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16254 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16255 }, 16256 .ne => &.{ 16257 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16258 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16259 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16260 .{ ._, .p_, .xor, .tmp3q, .tmp3q, ._, ._ }, 16261 .{ .@"0:", ._q, .mov, .tmp5q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16262 .{ ._, .p_w, .cmpeq, .tmp5q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16263 .{ ._, .p_b, .ackssw, .tmp5q, .tmp3q, ._, ._ }, 16264 .{ ._, .p_b, .movmsk, .tmp4d, .tmp5q, ._, ._ }, 16265 .{ ._, ._, .xor, .tmp4b, .si(0b1111), ._, ._ }, 16266 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 16267 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 16268 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 16269 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16270 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16271 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 16272 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 16273 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 16274 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16275 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 16276 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16277 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16278 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16279 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16280 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16281 }, 16282 } }, 16283 }, .{ 16284 .required_features = .{ .sse, .mmx, null, null }, 16285 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 16286 .patterns = &.{ 16287 .{ .src = .{ .to_mem, .to_mem } }, 16288 }, 16289 .extra_temps = .{ 16290 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16291 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 16292 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16293 .{ .kind = .{ .rc = .mmx } }, 16294 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16295 .{ .kind = .{ .rc = .mmx } }, 16296 .unused, 16297 .unused, 16298 .unused, 16299 }, 16300 .dst_temps = .{.mem}, 16301 .clobbers = .{ .eflags = true }, 16302 .each = .{ .once = switch (cc) { 16303 else => unreachable, 16304 .e => &.{ 16305 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16306 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16307 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16308 .{ ._, .p_, .xor, .tmp3q, .tmp3q, ._, ._ }, 16309 .{ .@"0:", ._q, .mov, .tmp5q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16310 .{ ._, .p_d, .cmpeq, .tmp5q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16311 .{ ._, .p_w, .ackssd, .tmp5q, .tmp3q, ._, ._ }, 16312 .{ ._, .p_b, .ackssw, .tmp5q, .tmp3q, ._, ._ }, 16313 .{ ._, .p_b, .movmsk, .tmp4d, .tmp5q, ._, ._ }, 16314 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 16315 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 16316 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 16317 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16318 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16319 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 16320 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 16321 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 16322 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16323 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 16324 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16325 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16326 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16327 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16328 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16329 }, 16330 .ne => &.{ 16331 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16332 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16333 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16334 .{ ._, .p_, .xor, .tmp3q, .tmp3q, ._, ._ }, 16335 .{ .@"0:", ._q, .mov, .tmp5q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16336 .{ ._, .p_d, .cmpeq, .tmp5q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16337 .{ ._, .p_w, .ackssd, .tmp5q, .tmp3q, ._, ._ }, 16338 .{ ._, .p_b, .ackssw, .tmp5q, .tmp3q, ._, ._ }, 16339 .{ ._, .p_b, .movmsk, .tmp4d, .tmp5q, ._, ._ }, 16340 .{ ._, ._, .xor, .tmp4b, .si(0b11), ._, ._ }, 16341 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 16342 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 16343 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 16344 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16345 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16346 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 16347 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 16348 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 16349 .{ ._, ._, .xor, .tmp2b, .tmp2b, ._, ._ }, 16350 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 16351 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16352 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 16353 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16354 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 16355 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 16356 }, 16357 } }, 16358 }, .{ 16359 .dst_constraints = .{.{ .bool_vec = .byte }}, 16360 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 16361 .patterns = &.{ 16362 .{ .src = .{ .to_mem, .to_mem } }, 16363 }, 16364 .extra_temps = .{ 16365 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16366 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16367 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16368 .unused, 16369 .unused, 16370 .unused, 16371 .unused, 16372 .unused, 16373 .unused, 16374 }, 16375 .dst_temps = .{.{ .rc = .general_purpose }}, 16376 .clobbers = .{ .eflags = true }, 16377 .each = .{ .once = &.{ 16378 .{ ._, ._, .xor, .dst0b, .dst0b, ._, ._ }, 16379 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16380 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16381 .{ .@"0:", ._, .mov, .tmp2b, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 16382 .{ ._, ._, .cmp, .tmp2b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 16383 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16384 .{ ._, ._l, .sh, .tmp2b, .tmp1b, ._, ._ }, 16385 .{ ._, ._, .@"or", .dst0b, .tmp2b, ._, ._ }, 16386 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16387 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 16388 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16389 } }, 16390 }, .{ 16391 .dst_constraints = .{.{ .bool_vec = .byte }}, 16392 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 16393 .patterns = &.{ 16394 .{ .src = .{ .to_mem, .to_mem } }, 16395 }, 16396 .extra_temps = .{ 16397 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16398 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16399 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 16400 .unused, 16401 .unused, 16402 .unused, 16403 .unused, 16404 .unused, 16405 .unused, 16406 }, 16407 .dst_temps = .{.{ .rc = .general_purpose }}, 16408 .clobbers = .{ .eflags = true }, 16409 .each = .{ .once = &.{ 16410 .{ ._, ._, .xor, .dst0b, .dst0b, ._, ._ }, 16411 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16412 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16413 .{ .@"0:", ._, .mov, .tmp2w, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 16414 .{ ._, ._, .cmp, .tmp2w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 16415 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16416 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16417 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16418 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16419 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 16420 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16421 } }, 16422 }, .{ 16423 .dst_constraints = .{.{ .bool_vec = .byte }}, 16424 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 16425 .patterns = &.{ 16426 .{ .src = .{ .to_mem, .to_mem } }, 16427 }, 16428 .extra_temps = .{ 16429 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16430 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16431 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16432 .unused, 16433 .unused, 16434 .unused, 16435 .unused, 16436 .unused, 16437 .unused, 16438 }, 16439 .dst_temps = .{.{ .rc = .general_purpose }}, 16440 .clobbers = .{ .eflags = true }, 16441 .each = .{ .once = &.{ 16442 .{ ._, ._, .xor, .dst0b, .dst0b, ._, ._ }, 16443 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16444 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16445 .{ .@"0:", ._, .mov, .tmp2d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 16446 .{ ._, ._, .cmp, .tmp2d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 16447 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16448 .{ ._, ._l, .sh, .tmp2b, .tmp1b, ._, ._ }, 16449 .{ ._, ._, .@"or", .dst0b, .tmp2b, ._, ._ }, 16450 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16451 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 16452 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16453 } }, 16454 }, .{ 16455 .required_features = .{ .@"64bit", null, null, null }, 16456 .dst_constraints = .{.{ .bool_vec = .byte }}, 16457 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 16458 .patterns = &.{ 16459 .{ .src = .{ .to_mem, .to_mem } }, 16460 }, 16461 .extra_temps = .{ 16462 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16463 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16464 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16465 .unused, 16466 .unused, 16467 .unused, 16468 .unused, 16469 .unused, 16470 .unused, 16471 }, 16472 .dst_temps = .{.{ .rc = .general_purpose }}, 16473 .clobbers = .{ .eflags = true }, 16474 .each = .{ .once = &.{ 16475 .{ ._, ._, .xor, .dst0b, .dst0b, ._, ._ }, 16476 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16477 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16478 .{ .@"0:", ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16479 .{ ._, ._, .cmp, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16480 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16481 .{ ._, ._l, .sh, .tmp2b, .tmp1b, ._, ._ }, 16482 .{ ._, ._, .@"or", .dst0b, .tmp2b, ._, ._ }, 16483 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16484 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 16485 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16486 } }, 16487 }, .{ 16488 .dst_constraints = .{.{ .bool_vec = .byte }}, 16489 .src_constraints = .{ .any_scalar_int, .any_scalar_int }, 16490 .patterns = &.{ 16491 .{ .src = .{ .to_mem, .to_mem } }, 16492 }, 16493 .extra_temps = .{ 16494 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16495 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16496 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16497 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16498 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16499 .unused, 16500 .unused, 16501 .unused, 16502 .unused, 16503 }, 16504 .dst_temps = .{.{ .rc = .general_purpose }}, 16505 .clobbers = .{ .eflags = true }, 16506 .each = .{ .once = &.{ 16507 .{ ._, ._, .xor, .dst0b, .dst0b, ._, ._ }, 16508 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 16509 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16510 .{ .@"0:", ._, .mov, .tmp2d, .sa(.src0p, .add_elem_limbs), ._, ._ }, 16511 .{ ._, ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 16512 .{ .@"1:", ._, .mov, .tmp4p, .memi(.src0p, .tmp0), ._, ._ }, 16513 .{ ._, ._, .xor, .tmp4p, .memi(.src1p, .tmp0), ._, ._ }, 16514 .{ ._, ._, .@"or", .tmp3p, .tmp4p, ._, ._ }, 16515 .{ ._, ._, .add, .tmp0p, .sa(.tmp4, .add_size), ._, ._ }, 16516 .{ ._, ._, .sub, .tmp2d, .si(1), ._, ._ }, 16517 .{ ._, ._b, .j, .@"1b", ._, ._, ._ }, 16518 .{ ._, ._, .@"test", .tmp3p, .tmp3p, ._, ._ }, 16519 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16520 .{ ._, ._l, .sh, .tmp2b, .tmp1b, ._, ._ }, 16521 .{ ._, ._, .@"or", .dst0b, .tmp2b, ._, ._ }, 16522 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16523 .{ ._, ._, .cmp, .tmp1b, .sa(.dst0, .add_len), ._, ._ }, 16524 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 16525 } }, 16526 }, .{ 16527 .dst_constraints = .{.{ .bool_vec = .dword }}, 16528 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 16529 .patterns = &.{ 16530 .{ .src = .{ .to_mem, .to_mem } }, 16531 }, 16532 .extra_temps = .{ 16533 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16534 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16535 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16536 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16537 .unused, 16538 .unused, 16539 .unused, 16540 .unused, 16541 .unused, 16542 }, 16543 .dst_temps = .{.{ .rc = .general_purpose }}, 16544 .clobbers = .{ .eflags = true }, 16545 .each = .{ .once = &.{ 16546 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16547 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16548 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16549 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16550 .{ ._, ._, .mov, .tmp3b, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 16551 .{ ._, ._, .cmp, .tmp3b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 16552 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16553 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16554 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16555 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16556 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 16557 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16558 } }, 16559 }, .{ 16560 .dst_constraints = .{.{ .bool_vec = .dword }}, 16561 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 16562 .patterns = &.{ 16563 .{ .src = .{ .to_mem, .to_mem } }, 16564 }, 16565 .extra_temps = .{ 16566 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16567 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16568 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16569 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 16570 .unused, 16571 .unused, 16572 .unused, 16573 .unused, 16574 .unused, 16575 }, 16576 .dst_temps = .{.{ .rc = .general_purpose }}, 16577 .clobbers = .{ .eflags = true }, 16578 .each = .{ .once = &.{ 16579 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16580 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16581 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16582 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16583 .{ ._, ._, .mov, .tmp3w, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 16584 .{ ._, ._, .cmp, .tmp3w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 16585 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16586 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16587 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16588 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16589 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 16590 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16591 } }, 16592 }, .{ 16593 .dst_constraints = .{.{ .bool_vec = .dword }}, 16594 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 16595 .patterns = &.{ 16596 .{ .src = .{ .to_mem, .to_mem } }, 16597 }, 16598 .extra_temps = .{ 16599 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16600 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16601 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16602 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16603 .unused, 16604 .unused, 16605 .unused, 16606 .unused, 16607 .unused, 16608 }, 16609 .dst_temps = .{.{ .rc = .general_purpose }}, 16610 .clobbers = .{ .eflags = true }, 16611 .each = .{ .once = &.{ 16612 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16613 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16614 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16615 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16616 .{ ._, ._, .mov, .tmp3d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 16617 .{ ._, ._, .cmp, .tmp3d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 16618 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16619 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16620 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16621 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16622 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 16623 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16624 } }, 16625 }, .{ 16626 .required_features = .{ .@"64bit", null, null, null }, 16627 .dst_constraints = .{.{ .bool_vec = .dword }}, 16628 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 16629 .patterns = &.{ 16630 .{ .src = .{ .to_mem, .to_mem } }, 16631 }, 16632 .extra_temps = .{ 16633 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16634 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16635 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16636 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16637 .unused, 16638 .unused, 16639 .unused, 16640 .unused, 16641 .unused, 16642 }, 16643 .dst_temps = .{.{ .rc = .general_purpose }}, 16644 .clobbers = .{ .eflags = true }, 16645 .each = .{ .once = &.{ 16646 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16647 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16648 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16649 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16650 .{ ._, ._, .mov, .tmp3q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16651 .{ ._, ._, .cmp, .tmp3q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16652 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16653 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16654 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16655 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16656 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 16657 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16658 } }, 16659 }, .{ 16660 .dst_constraints = .{.{ .bool_vec = .dword }}, 16661 .src_constraints = .{ .any_scalar_int, .any_scalar_int }, 16662 .patterns = &.{ 16663 .{ .src = .{ .to_mem, .to_mem } }, 16664 }, 16665 .extra_temps = .{ 16666 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16667 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16668 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16669 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16670 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16671 .unused, 16672 .unused, 16673 .unused, 16674 .unused, 16675 }, 16676 .dst_temps = .{.{ .rc = .general_purpose }}, 16677 .clobbers = .{ .eflags = true }, 16678 .each = .{ .once = &.{ 16679 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16680 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 16681 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16682 .{ .@"0:", ._, .mov, .tmp2d, .sa(.src0p, .add_elem_limbs), ._, ._ }, 16683 .{ ._, ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 16684 .{ .@"1:", ._, .mov, .tmp4p, .memi(.src0p, .tmp0), ._, ._ }, 16685 .{ ._, ._, .xor, .tmp4p, .memi(.src1p, .tmp0), ._, ._ }, 16686 .{ ._, ._, .@"or", .tmp3p, .tmp4p, ._, ._ }, 16687 .{ ._, ._, .add, .tmp0p, .sa(.tmp4, .add_size), ._, ._ }, 16688 .{ ._, ._, .sub, .tmp2d, .si(1), ._, ._ }, 16689 .{ ._, ._b, .j, .@"1b", ._, ._, ._ }, 16690 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16691 .{ ._, ._, .@"test", .tmp3p, .tmp3p, ._, ._ }, 16692 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16693 .{ ._, ._l, .sh, .tmp2d, .tmp1b, ._, ._ }, 16694 .{ ._, ._, .@"or", .dst0d, .tmp2d, ._, ._ }, 16695 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16696 .{ ._, ._, .cmp, .tmp1b, .sa(.dst0, .add_len), ._, ._ }, 16697 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 16698 } }, 16699 }, .{ 16700 .required_features = .{ .@"64bit", null, null, null }, 16701 .dst_constraints = .{.{ .bool_vec = .qword }}, 16702 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 16703 .patterns = &.{ 16704 .{ .src = .{ .to_mem, .to_mem } }, 16705 }, 16706 .extra_temps = .{ 16707 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16708 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16709 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16710 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16711 .unused, 16712 .unused, 16713 .unused, 16714 .unused, 16715 .unused, 16716 }, 16717 .dst_temps = .{.{ .rc = .general_purpose }}, 16718 .clobbers = .{ .eflags = true }, 16719 .each = .{ .once = &.{ 16720 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16721 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16722 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16723 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16724 .{ ._, ._, .mov, .tmp3b, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 16725 .{ ._, ._, .cmp, .tmp3b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 16726 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16727 .{ ._, ._l, .sh, .tmp2q, .tmp1b, ._, ._ }, 16728 .{ ._, ._, .@"or", .dst0q, .tmp2q, ._, ._ }, 16729 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16730 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 16731 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16732 } }, 16733 }, .{ 16734 .required_features = .{ .@"64bit", null, null, null }, 16735 .dst_constraints = .{.{ .bool_vec = .qword }}, 16736 .src_constraints = .{ .{ .scalar_int_is = .word }, .{ .scalar_int_is = .word } }, 16737 .patterns = &.{ 16738 .{ .src = .{ .to_mem, .to_mem } }, 16739 }, 16740 .extra_temps = .{ 16741 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16742 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16743 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16744 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 16745 .unused, 16746 .unused, 16747 .unused, 16748 .unused, 16749 .unused, 16750 }, 16751 .dst_temps = .{.{ .rc = .general_purpose }}, 16752 .clobbers = .{ .eflags = true }, 16753 .each = .{ .once = &.{ 16754 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16755 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16756 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16757 .{ ._, ._, .mov, .tmp3w, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 16758 .{ ._, ._, .cmp, .tmp3w, .memia(.src1w, .tmp0, .add_size), ._, ._ }, 16759 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16760 .{ ._, ._l, .sh, .tmp2q, .tmp1b, ._, ._ }, 16761 .{ ._, ._, .@"or", .dst0q, .tmp2q, ._, ._ }, 16762 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16763 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 16764 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16765 } }, 16766 }, .{ 16767 .required_features = .{ .@"64bit", null, null, null }, 16768 .dst_constraints = .{.{ .bool_vec = .qword }}, 16769 .src_constraints = .{ .{ .scalar_int_is = .dword }, .{ .scalar_int_is = .dword } }, 16770 .patterns = &.{ 16771 .{ .src = .{ .to_mem, .to_mem } }, 16772 }, 16773 .extra_temps = .{ 16774 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16775 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16776 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16777 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 16778 .unused, 16779 .unused, 16780 .unused, 16781 .unused, 16782 .unused, 16783 }, 16784 .dst_temps = .{.{ .rc = .general_purpose }}, 16785 .clobbers = .{ .eflags = true }, 16786 .each = .{ .once = &.{ 16787 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16788 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16789 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16790 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16791 .{ ._, ._, .mov, .tmp3d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 16792 .{ ._, ._, .cmp, .tmp3d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 16793 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16794 .{ ._, ._l, .sh, .tmp2q, .tmp1b, ._, ._ }, 16795 .{ ._, ._, .@"or", .dst0q, .tmp2q, ._, ._ }, 16796 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16797 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 16798 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16799 } }, 16800 }, .{ 16801 .required_features = .{ .@"64bit", null, null, null }, 16802 .dst_constraints = .{.{ .bool_vec = .qword }}, 16803 .src_constraints = .{ .{ .scalar_int_is = .qword }, .{ .scalar_int_is = .qword } }, 16804 .patterns = &.{ 16805 .{ .src = .{ .to_mem, .to_mem } }, 16806 }, 16807 .extra_temps = .{ 16808 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16809 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16810 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16811 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 16812 .unused, 16813 .unused, 16814 .unused, 16815 .unused, 16816 .unused, 16817 }, 16818 .dst_temps = .{.{ .rc = .general_purpose }}, 16819 .clobbers = .{ .eflags = true }, 16820 .each = .{ .once = &.{ 16821 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16822 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16823 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16824 .{ .@"0:", ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16825 .{ ._, ._, .mov, .tmp2q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 16826 .{ ._, ._, .cmp, .tmp2q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 16827 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16828 .{ ._, ._l, .sh, .tmp2q, .tmp1b, ._, ._ }, 16829 .{ ._, ._, .@"or", .dst0q, .tmp2q, ._, ._ }, 16830 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16831 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 16832 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16833 } }, 16834 }, .{ 16835 .required_features = .{ .@"64bit", null, null, null }, 16836 .dst_constraints = .{.{ .bool_vec = .qword }}, 16837 .src_constraints = .{ .any_scalar_int, .any_scalar_int }, 16838 .patterns = &.{ 16839 .{ .src = .{ .to_mem, .to_mem } }, 16840 }, 16841 .extra_temps = .{ 16842 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16843 .{ .type = .u8, .kind = .{ .reg = .cl } }, 16844 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16845 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16846 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16847 .unused, 16848 .unused, 16849 .unused, 16850 .unused, 16851 }, 16852 .dst_temps = .{.{ .rc = .general_purpose }}, 16853 .clobbers = .{ .eflags = true }, 16854 .each = .{ .once = &.{ 16855 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 16856 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 16857 .{ ._, ._, .xor, .tmp1b, .tmp1b, ._, ._ }, 16858 .{ .@"0:", ._, .mov, .tmp2d, .sa(.src0p, .add_elem_limbs), ._, ._ }, 16859 .{ ._, ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 16860 .{ .@"1:", ._, .mov, .tmp4p, .memi(.src0p, .tmp0), ._, ._ }, 16861 .{ ._, ._, .xor, .tmp4p, .memi(.src1p, .tmp0), ._, ._ }, 16862 .{ ._, ._, .@"or", .tmp3p, .tmp4p, ._, ._ }, 16863 .{ ._, ._, .add, .tmp0p, .sa(.tmp4, .add_size), ._, ._ }, 16864 .{ ._, ._, .sub, .tmp2d, .si(1), ._, ._ }, 16865 .{ ._, ._b, .j, .@"1b", ._, ._, ._ }, 16866 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16867 .{ ._, ._, .@"test", .tmp3p, .tmp3p, ._, ._ }, 16868 .{ ._, .fromCond(cc), .set, .tmp2b, ._, ._, ._ }, 16869 .{ ._, ._l, .sh, .tmp2q, .tmp1b, ._, ._ }, 16870 .{ ._, ._, .@"or", .dst0q, .tmp2q, ._, ._ }, 16871 .{ ._, ._, .add, .tmp1b, .si(1), ._, ._ }, 16872 .{ ._, ._, .cmp, .tmp1b, .sa(.dst0, .add_len), ._, ._ }, 16873 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 16874 } }, 16875 }, .{ 16876 .src_constraints = .{ .{ .scalar_int_is = .byte }, .{ .scalar_int_is = .byte } }, 16877 .patterns = &.{ 16878 .{ .src = .{ .to_mem, .to_mem } }, 16879 }, 16880 .extra_temps = .{ 16881 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 16882 .{ .type = .u32, .kind = .{ .reg = .ecx } }, 16883 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16884 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 16885 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 16886 .unused, 16887 .unused, 16888 .unused, 16889 .unused, 16890 }, 16891 .dst_temps = .{.mem}, 16892 .clobbers = .{ .eflags = true }, 16893 .each = .{ .once = &.{ 16894 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 16895 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 16896 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16897 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 16898 .{ ._, ._, .mov, .tmp4b, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 16899 .{ ._, ._, .cmp, .tmp4b, .memia(.src1b, .tmp0, .add_size), ._, ._ }, 16900 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 16901 .{ ._, ._l, .sh, .tmp3p, .tmp1b, ._, ._ }, 16902 .{ ._, ._, .@"or", .tmp2p, .tmp3p, ._, ._ }, 16903 .{ ._, ._, .add, .tmp1d, .si(1), ._, ._ }, 16904 .{ ._, ._, .@"test", .tmp1d, .sia(-1, .none, .add_ptr_bit_size), ._, ._ }, 16905 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 16906 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 16907 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 16908 .{ ._, ._, .mov, .memia(.dst0p, .tmp3, .sub_ptr_size), .tmp2p, ._, ._ }, 16909 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 16910 .{ .@"1:", ._, .add, .tmp0p, .si(1), ._, ._ }, 16911 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 16912 .{ ._, ._, .@"test", .tmp1d, .sia(-1, .none, .add_ptr_bit_size), ._, ._ }, 16913 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 16914 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 16915 .{ ._, ._, .mov, .memsi(.dst0p, .@"8", .tmp1), .tmp2p, ._, ._ }, 16916 } }, 16917 }, .{ 16918 .required_features = .{ .f16c, null, null, null }, 16919 .src_constraints = .{ 16920 .{ .scalar_float = .{ .of = .word, .is = .word } }, 16921 .{ .scalar_float = .{ .of = .word, .is = .word } }, 16922 }, 16923 .patterns = &.{ 16924 .{ .src = .{ .to_sse, .to_sse } }, 16925 }, 16926 .extra_temps = .{ 16927 .{ .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 16928 .unused, 16929 .unused, 16930 .unused, 16931 .unused, 16932 .unused, 16933 .unused, 16934 .unused, 16935 .unused, 16936 }, 16937 .dst_temps = .{.{ .mut_rc_mask = .{ 16938 .ref = .src0, 16939 .rc = .sse, 16940 .info = .{ .kind = .all, .scalar = .dword }, 16941 } }}, 16942 .each = .{ .once = &.{ 16943 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 16944 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 16945 .{ ._, .v_ss, .cmp, .dst0x, .dst0x, .tmp0x, .vp(switch (cc) { 16946 else => unreachable, 16947 .e => .eq, 16948 .ne => .neq, 16949 }) }, 16950 } }, 16951 }, .{ 16952 .required_features = .{ .f16c, null, null, null }, 16953 .src_constraints = .{ 16954 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 16955 .{ .scalar_float = .{ .of = .qword, .is = .word } }, 16956 }, 16957 .patterns = &.{ 16958 .{ .src = .{ .mem, .mem } }, 16959 .{ .src = .{ .sse, .mem } }, 16960 .{ .src = .{ .mem, .sse } }, 16961 .{ .src = .{ .to_sse, .to_sse } }, 16962 }, 16963 .extra_temps = .{ 16964 .{ .kind = .{ .rc = .sse } }, 16965 .unused, 16966 .unused, 16967 .unused, 16968 .unused, 16969 .unused, 16970 .unused, 16971 .unused, 16972 .unused, 16973 }, 16974 .dst_temps = .{.{ .mut_rc_mask = .{ 16975 .ref = .src0, 16976 .rc = .sse, 16977 .info = .{ .kind = .all, .scalar = .dword }, 16978 } }}, 16979 .each = .{ .once = &.{ 16980 .{ ._, .v_ps, .cvtph2, .dst0x, .src0q, ._, ._ }, 16981 .{ ._, .v_ps, .cvtph2, .tmp0x, .src1q, ._, ._ }, 16982 .{ ._, .v_ps, .cmp, .dst0x, .dst0x, .tmp0x, .vp(switch (cc) { 16983 else => unreachable, 16984 .e => .eq, 16985 .ne => .neq, 16986 }) }, 16987 } }, 16988 }, .{ 16989 .required_features = .{ .f16c, null, null, null }, 16990 .src_constraints = .{ 16991 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 16992 .{ .scalar_float = .{ .of = .xword, .is = .word } }, 16993 }, 16994 .patterns = &.{ 16995 .{ .src = .{ .mem, .mem } }, 16996 .{ .src = .{ .to_sse, .mem } }, 16997 .{ .src = .{ .mem, .to_sse } }, 16998 .{ .src = .{ .to_sse, .to_sse } }, 16999 }, 17000 .extra_temps = .{ 17001 .{ .kind = .{ .rc = .sse } }, 17002 .unused, 17003 .unused, 17004 .unused, 17005 .unused, 17006 .unused, 17007 .unused, 17008 .unused, 17009 .unused, 17010 }, 17011 .dst_temps = .{.{ .mut_rc_mask = .{ 17012 .ref = .src0, 17013 .rc = .sse, 17014 .info = .{ .kind = .all, .scalar = .dword }, 17015 } }}, 17016 .each = .{ .once = &.{ 17017 .{ ._, .v_ps, .cvtph2, .dst0y, .src0x, ._, ._ }, 17018 .{ ._, .v_ps, .cvtph2, .tmp0y, .src1x, ._, ._ }, 17019 .{ ._, .v_ps, .cmp, .dst0y, .dst0y, .tmp0y, .vp(switch (cc) { 17020 else => unreachable, 17021 .e => .eq, 17022 .ne => .neq, 17023 }) }, 17024 } }, 17025 }, .{ 17026 .required_features = .{ .avx, null, null, null }, 17027 .src_constraints = .{ 17028 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 17029 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 17030 }, 17031 .patterns = &.{ 17032 .{ .src = .{ .to_sse, .mem } }, 17033 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17034 .{ .src = .{ .to_sse, .to_sse } }, 17035 }, 17036 .dst_temps = .{.{ .mut_rc_mask = .{ 17037 .ref = .src0, 17038 .rc = .sse, 17039 .info = .{ .kind = .all, .scalar = .dword }, 17040 } }}, 17041 .each = .{ .once = &.{ 17042 .{ ._, .v_ss, .cmp, .dst0x, .src0x, .src1x, .vp(switch (cc) { 17043 else => unreachable, 17044 .e => .eq, 17045 .ne => .neq, 17046 }) }, 17047 } }, 17048 }, .{ 17049 .required_features = .{ .sse, null, null, null }, 17050 .src_constraints = .{ 17051 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 17052 .{ .scalar_float = .{ .of = .dword, .is = .dword } }, 17053 }, 17054 .patterns = &.{ 17055 .{ .src = .{ .to_mut_sse, .mem } }, 17056 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 17057 .{ .src = .{ .to_mut_sse, .to_sse } }, 17058 }, 17059 .dst_temps = .{.{ .ref_mask = .{ 17060 .ref = .src0, 17061 .info = .{ .kind = .all, .scalar = .dword }, 17062 } }}, 17063 .each = .{ .once = &.{ 17064 .{ ._, ._ss, .cmp, .dst0x, .src1x, .vp(switch (cc) { 17065 else => unreachable, 17066 .e => .eq, 17067 .ne => .neq, 17068 }), ._ }, 17069 } }, 17070 }, .{ 17071 .required_features = .{ .avx, null, null, null }, 17072 .src_constraints = .{ 17073 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 17074 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 17075 }, 17076 .patterns = &.{ 17077 .{ .src = .{ .to_sse, .mem } }, 17078 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17079 .{ .src = .{ .to_sse, .to_sse } }, 17080 }, 17081 .dst_temps = .{.{ .mut_rc_mask = .{ 17082 .ref = .src0, 17083 .rc = .sse, 17084 .info = .{ .kind = .all, .scalar = .dword }, 17085 } }}, 17086 .each = .{ .once = &.{ 17087 .{ ._, .v_ps, .cmp, .dst0x, .src0x, .src1x, .vp(switch (cc) { 17088 else => unreachable, 17089 .e => .eq, 17090 .ne => .neq, 17091 }) }, 17092 } }, 17093 }, .{ 17094 .required_features = .{ .sse, null, null, null }, 17095 .src_constraints = .{ 17096 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 17097 .{ .scalar_float = .{ .of = .xword, .is = .dword } }, 17098 }, 17099 .patterns = &.{ 17100 .{ .src = .{ .to_mut_sse, .mem } }, 17101 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 17102 .{ .src = .{ .to_mut_sse, .to_sse } }, 17103 }, 17104 .dst_temps = .{.{ .ref_mask = .{ 17105 .ref = .src0, 17106 .info = .{ .kind = .all, .scalar = .dword }, 17107 } }}, 17108 .each = .{ .once = &.{ 17109 .{ ._, ._ps, .cmp, .dst0x, .src1x, .vp(switch (cc) { 17110 else => unreachable, 17111 .e => .eq, 17112 .ne => .neq, 17113 }), ._ }, 17114 } }, 17115 }, .{ 17116 .required_features = .{ .avx, null, null, null }, 17117 .src_constraints = .{ 17118 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 17119 .{ .scalar_float = .{ .of = .yword, .is = .dword } }, 17120 }, 17121 .patterns = &.{ 17122 .{ .src = .{ .to_sse, .mem } }, 17123 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17124 .{ .src = .{ .to_sse, .to_sse } }, 17125 }, 17126 .dst_temps = .{.{ .mut_rc_mask = .{ 17127 .ref = .src0, 17128 .rc = .sse, 17129 .info = .{ .kind = .all, .scalar = .dword }, 17130 } }}, 17131 .each = .{ .once = &.{ 17132 .{ ._, .v_ps, .cmp, .dst0y, .src0y, .src1y, .vp(switch (cc) { 17133 else => unreachable, 17134 .e => .eq, 17135 .ne => .neq, 17136 }) }, 17137 } }, 17138 }, .{ 17139 .required_features = .{ .avx, null, null, null }, 17140 .src_constraints = .{ 17141 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 17142 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 17143 }, 17144 .patterns = &.{ 17145 .{ .src = .{ .to_sse, .mem } }, 17146 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17147 .{ .src = .{ .to_sse, .to_sse } }, 17148 }, 17149 .dst_temps = .{.{ .mut_rc_mask = .{ 17150 .ref = .src0, 17151 .rc = .sse, 17152 .info = .{ .kind = .all, .scalar = .qword }, 17153 } }}, 17154 .each = .{ .once = &.{ 17155 .{ ._, .v_sd, .cmp, .dst0x, .src0x, .src1x, .vp(switch (cc) { 17156 else => unreachable, 17157 .e => .eq, 17158 .ne => .neq, 17159 }) }, 17160 } }, 17161 }, .{ 17162 .required_features = .{ .sse2, null, null, null }, 17163 .src_constraints = .{ 17164 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 17165 .{ .scalar_float = .{ .of = .qword, .is = .qword } }, 17166 }, 17167 .patterns = &.{ 17168 .{ .src = .{ .to_mut_sse, .mem } }, 17169 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 17170 .{ .src = .{ .to_mut_sse, .to_sse } }, 17171 }, 17172 .dst_temps = .{.{ .ref_mask = .{ 17173 .ref = .src0, 17174 .info = .{ .kind = .all, .scalar = .qword }, 17175 } }}, 17176 .each = .{ .once = &.{ 17177 .{ ._, ._sd, .cmp, .dst0x, .src1x, .vp(switch (cc) { 17178 else => unreachable, 17179 .e => .eq, 17180 .ne => .neq, 17181 }), ._ }, 17182 } }, 17183 }, .{ 17184 .required_features = .{ .avx, null, null, null }, 17185 .src_constraints = .{ 17186 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 17187 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 17188 }, 17189 .patterns = &.{ 17190 .{ .src = .{ .to_sse, .mem } }, 17191 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17192 .{ .src = .{ .to_sse, .to_sse } }, 17193 }, 17194 .dst_temps = .{.{ .mut_rc_mask = .{ 17195 .ref = .src0, 17196 .rc = .sse, 17197 .info = .{ .kind = .all, .scalar = .qword }, 17198 } }}, 17199 .each = .{ .once = &.{ 17200 .{ ._, .v_pd, .cmp, .dst0x, .src0x, .src1x, .vp(switch (cc) { 17201 else => unreachable, 17202 .e => .eq, 17203 .ne => .neq, 17204 }) }, 17205 } }, 17206 }, .{ 17207 .required_features = .{ .sse2, null, null, null }, 17208 .src_constraints = .{ 17209 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 17210 .{ .scalar_float = .{ .of = .xword, .is = .qword } }, 17211 }, 17212 .patterns = &.{ 17213 .{ .src = .{ .to_mut_sse, .mem } }, 17214 .{ .src = .{ .mem, .to_mut_sse }, .commute = .{ 0, 1 } }, 17215 .{ .src = .{ .to_mut_sse, .to_sse } }, 17216 }, 17217 .dst_temps = .{.{ .ref_mask = .{ 17218 .ref = .src0, 17219 .info = .{ .kind = .all, .scalar = .qword }, 17220 } }}, 17221 .each = .{ .once = &.{ 17222 .{ ._, ._pd, .cmp, .dst0x, .src1x, .vp(switch (cc) { 17223 else => unreachable, 17224 .e => .eq, 17225 .ne => .neq, 17226 }), ._ }, 17227 } }, 17228 }, .{ 17229 .required_features = .{ .avx, null, null, null }, 17230 .src_constraints = .{ 17231 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 17232 .{ .scalar_float = .{ .of = .yword, .is = .qword } }, 17233 }, 17234 .patterns = &.{ 17235 .{ .src = .{ .to_sse, .mem } }, 17236 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 17237 .{ .src = .{ .to_sse, .to_sse } }, 17238 }, 17239 .dst_temps = .{.{ .mut_rc_mask = .{ 17240 .ref = .src0, 17241 .rc = .sse, 17242 .info = .{ .kind = .all, .scalar = .qword }, 17243 } }}, 17244 .each = .{ .once = &.{ 17245 .{ ._, .v_pd, .cmp, .dst0y, .src0y, .src1y, .vp(switch (cc) { 17246 else => unreachable, 17247 .e => .eq, 17248 .ne => .neq, 17249 }) }, 17250 } }, 17251 }, .{ 17252 .required_features = .{ .f16c, .slow_incdec, null, null }, 17253 .src_constraints = .{ 17254 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 17255 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 17256 }, 17257 .patterns = &.{ 17258 .{ .src = .{ .to_mem, .to_mem } }, 17259 }, 17260 .extra_temps = .{ 17261 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 17262 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 17263 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 17264 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 17265 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 17266 .unused, 17267 .unused, 17268 .unused, 17269 .unused, 17270 }, 17271 .dst_temps = .{.mem}, 17272 .clobbers = .{ .eflags = true }, 17273 .each = .{ .once = &.{ 17274 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 17275 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 17276 .{ .@"0:", .v_ps, .cvtph2, .tmp2y, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 17277 .{ ._, .v_ps, .cvtph2, .tmp3y, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 17278 .{ ._, .v_ps, .cmp, .tmp2y, .tmp2y, .tmp3y, .vp(switch (cc) { 17279 else => unreachable, 17280 .e => .eq, 17281 .ne => .neq, 17282 }) }, 17283 .{ ._, .v_ps, .movmsk, .tmp4d, .tmp2y, ._, ._ }, 17284 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp4b, ._, ._ }, 17285 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 17286 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 17287 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 17288 } }, 17289 }, .{ 17290 .required_features = .{ .f16c, null, null, null }, 17291 .src_constraints = .{ 17292 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 17293 .{ .multiple_scalar_float = .{ .of = .xword, .is = .word } }, 17294 }, 17295 .patterns = &.{ 17296 .{ .src = .{ .to_mem, .to_mem } }, 17297 }, 17298 .extra_temps = .{ 17299 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 17300 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 17301 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 17302 .{ .type = .vector_8_f16, .kind = .{ .rc = .sse } }, 17303 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 17304 .unused, 17305 .unused, 17306 .unused, 17307 .unused, 17308 }, 17309 .dst_temps = .{.mem}, 17310 .clobbers = .{ .eflags = true }, 17311 .each = .{ .once = &.{ 17312 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 17313 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 17314 .{ .@"0:", .v_ps, .cvtph2, .tmp2y, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 17315 .{ ._, .v_ps, .cvtph2, .tmp3y, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 17316 .{ ._, .v_ps, .cmp, .tmp2y, .tmp2y, .tmp3y, .vp(switch (cc) { 17317 else => unreachable, 17318 .e => .eq, 17319 .ne => .neq, 17320 }) }, 17321 .{ ._, .v_ps, .movmsk, .tmp4d, .tmp2y, ._, ._ }, 17322 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp4b, ._, ._ }, 17323 .{ ._, ._c, .in, .tmp1p, ._, ._, ._ }, 17324 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 17325 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 17326 } }, 17327 }, .{ 17328 .required_features = .{ .avx, .slow_incdec, null, null }, 17329 .dst_constraints = .{.{ .bool_vec = .dword }}, 17330 .src_constraints = .{ 17331 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17332 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17333 }, 17334 .patterns = &.{ 17335 .{ .src = .{ .to_mem, .to_mem } }, 17336 }, 17337 .call_frame = .{ .alignment = .@"16" }, 17338 .extra_temps = .{ 17339 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17340 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17341 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17342 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17343 else => unreachable, 17344 .e => "__eqhf2", 17345 .ne => "__nehf2", 17346 } } } }, 17347 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17348 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17349 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17350 .unused, 17351 .unused, 17352 }, 17353 .dst_temps = .{.{ .rc = .general_purpose }}, 17354 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17355 .each = .{ .once = &.{ 17356 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17357 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17358 .{ .@"0:", .vp_, .xor, .tmp2x, .tmp2x, .tmp2x, ._ }, 17359 .{ ._, .vp_w, .insr, .tmp1x, .tmp2x, .memsi(.src0w, .@"2", .tmp0), .ui(0) }, 17360 .{ ._, .vp_w, .insr, .tmp2x, .tmp2x, .memsi(.src1w, .@"2", .tmp0), .ui(0) }, 17361 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17362 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17363 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17364 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17365 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17366 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17367 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17368 .{ ._, ._, .add, .tmp0d, .si(1), ._, ._ }, 17369 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17370 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17371 } }, 17372 }, .{ 17373 .required_features = .{ .avx, null, null, null }, 17374 .dst_constraints = .{.{ .bool_vec = .dword }}, 17375 .src_constraints = .{ 17376 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17377 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17378 }, 17379 .patterns = &.{ 17380 .{ .src = .{ .to_mem, .to_mem } }, 17381 }, 17382 .call_frame = .{ .alignment = .@"16" }, 17383 .extra_temps = .{ 17384 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17385 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17386 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17387 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17388 else => unreachable, 17389 .e => "__eqhf2", 17390 .ne => "__nehf2", 17391 } } } }, 17392 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17393 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17394 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17395 .unused, 17396 .unused, 17397 }, 17398 .dst_temps = .{.{ .rc = .general_purpose }}, 17399 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17400 .each = .{ .once = &.{ 17401 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17402 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17403 .{ .@"0:", .vp_, .xor, .tmp2x, .tmp2x, .tmp2x, ._ }, 17404 .{ ._, .vp_w, .insr, .tmp1x, .tmp2x, .memsi(.src0w, .@"2", .tmp0), .ui(0) }, 17405 .{ ._, .vp_w, .insr, .tmp2x, .tmp2x, .memsi(.src1w, .@"2", .tmp0), .ui(0) }, 17406 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17407 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17408 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17409 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17410 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17411 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17412 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17413 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17414 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17415 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17416 } }, 17417 }, .{ 17418 .required_features = .{ .sse2, .slow_incdec, null, null }, 17419 .dst_constraints = .{.{ .bool_vec = .dword }}, 17420 .src_constraints = .{ 17421 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17422 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17423 }, 17424 .patterns = &.{ 17425 .{ .src = .{ .to_mem, .to_mem } }, 17426 }, 17427 .call_frame = .{ .alignment = .@"16" }, 17428 .extra_temps = .{ 17429 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17430 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17431 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17432 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17433 else => unreachable, 17434 .e => "__eqhf2", 17435 .ne => "__nehf2", 17436 } } } }, 17437 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17438 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17439 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17440 .unused, 17441 .unused, 17442 }, 17443 .dst_temps = .{.{ .rc = .general_purpose }}, 17444 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17445 .each = .{ .once = &.{ 17446 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17447 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17448 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 17449 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 17450 .{ ._, .p_w, .insr, .tmp1x, .memsi(.src0w, .@"2", .tmp0), .ui(0), ._ }, 17451 .{ ._, .p_w, .insr, .tmp2x, .memsi(.src1w, .@"2", .tmp0), .ui(0), ._ }, 17452 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17453 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17454 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17455 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17456 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17457 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17458 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17459 .{ ._, ._, .add, .tmp0d, .si(1), ._, ._ }, 17460 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17461 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17462 } }, 17463 }, .{ 17464 .required_features = .{ .sse2, null, null, null }, 17465 .dst_constraints = .{.{ .bool_vec = .dword }}, 17466 .src_constraints = .{ 17467 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17468 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17469 }, 17470 .patterns = &.{ 17471 .{ .src = .{ .to_mem, .to_mem } }, 17472 }, 17473 .call_frame = .{ .alignment = .@"16" }, 17474 .extra_temps = .{ 17475 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17476 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17477 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17478 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17479 else => unreachable, 17480 .e => "__eqhf2", 17481 .ne => "__nehf2", 17482 } } } }, 17483 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17484 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17485 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17486 .unused, 17487 .unused, 17488 }, 17489 .dst_temps = .{.{ .rc = .general_purpose }}, 17490 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17491 .each = .{ .once = &.{ 17492 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17493 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17494 .{ .@"0:", .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 17495 .{ ._, .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 17496 .{ ._, .p_w, .insr, .tmp1x, .memsi(.src0w, .@"2", .tmp0), .ui(0), ._ }, 17497 .{ ._, .p_w, .insr, .tmp2x, .memsi(.src1w, .@"2", .tmp0), .ui(0), ._ }, 17498 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17499 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17500 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17501 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17502 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17503 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17504 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17505 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17506 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17507 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17508 } }, 17509 }, .{ 17510 .required_features = .{ .sse, .slow_incdec, null, null }, 17511 .dst_constraints = .{.{ .bool_vec = .dword }}, 17512 .src_constraints = .{ 17513 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17514 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17515 }, 17516 .patterns = &.{ 17517 .{ .src = .{ .to_mem, .to_mem } }, 17518 }, 17519 .call_frame = .{ .alignment = .@"16" }, 17520 .extra_temps = .{ 17521 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17522 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17523 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17524 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17525 else => unreachable, 17526 .e => "__eqhf2", 17527 .ne => "__nehf2", 17528 } } } }, 17529 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17530 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17531 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17532 .{ .type = .f32, .kind = .mem }, 17533 .unused, 17534 }, 17535 .dst_temps = .{.{ .rc = .general_purpose }}, 17536 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17537 .each = .{ .once = &.{ 17538 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17539 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17540 .{ .@"0:", ._, .movzx, .tmp4d, .memsi(.src0w, .@"2", .tmp0), ._, ._ }, 17541 .{ ._, ._, .mov, .mem(.tmp7d), .tmp4d, ._, ._ }, 17542 .{ ._, ._ss, .mov, .tmp1x, .mem(.tmp7d), ._, ._ }, 17543 .{ ._, ._, .movzx, .tmp4d, .memsi(.src1w, .@"2", .tmp0), ._, ._ }, 17544 .{ ._, ._, .mov, .mem(.tmp7d), .tmp4d, ._, ._ }, 17545 .{ ._, ._ss, .mov, .tmp2x, .mem(.tmp7d), ._, ._ }, 17546 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17547 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17548 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17549 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17550 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17551 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17552 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17553 .{ ._, ._, .add, .tmp0d, .si(1), ._, ._ }, 17554 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17555 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17556 } }, 17557 }, .{ 17558 .required_features = .{ .sse, null, null, null }, 17559 .dst_constraints = .{.{ .bool_vec = .dword }}, 17560 .src_constraints = .{ 17561 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17562 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17563 }, 17564 .patterns = &.{ 17565 .{ .src = .{ .to_mem, .to_mem } }, 17566 }, 17567 .call_frame = .{ .alignment = .@"16" }, 17568 .extra_temps = .{ 17569 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17570 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17571 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17572 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17573 else => unreachable, 17574 .e => "__eqhf2", 17575 .ne => "__nehf2", 17576 } } } }, 17577 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17578 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17579 .{ .type = .u32, .kind = .{ .reg = .edx } }, 17580 .{ .type = .f32, .kind = .mem }, 17581 .unused, 17582 }, 17583 .dst_temps = .{.{ .rc = .general_purpose }}, 17584 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17585 .each = .{ .once = &.{ 17586 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 17587 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17588 .{ .@"0:", ._, .movzx, .tmp4d, .memsi(.src0w, .@"2", .tmp0), ._, ._ }, 17589 .{ ._, ._, .mov, .mem(.tmp7d), .tmp4d, ._, ._ }, 17590 .{ ._, ._ss, .mov, .tmp1x, .mem(.tmp7d), ._, ._ }, 17591 .{ ._, ._, .movzx, .tmp4d, .memsi(.src1w, .@"2", .tmp0), ._, ._ }, 17592 .{ ._, ._, .mov, .mem(.tmp7d), .tmp4d, ._, ._ }, 17593 .{ ._, ._ss, .mov, .tmp2x, .mem(.tmp7d), ._, ._ }, 17594 .{ ._, ._, .call, .tmp3d, ._, ._, ._ }, 17595 .{ ._, ._, .xor, .tmp6d, .tmp6d, ._, ._ }, 17596 .{ ._, ._, .@"test", .tmp4d, .tmp4d, ._, ._ }, 17597 .{ ._, .fromCond(cc), .set, .tmp6b, ._, ._, ._ }, 17598 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17599 .{ ._, ._l, .sh, .tmp6d, .tmp5b, ._, ._ }, 17600 .{ ._, ._, .@"or", .dst0d, .tmp6d, ._, ._ }, 17601 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17602 .{ ._, ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17603 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17604 } }, 17605 }, .{ 17606 .required_features = .{ .@"64bit", .avx, .slow_incdec, null }, 17607 .src_constraints = .{ 17608 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17609 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17610 }, 17611 .patterns = &.{ 17612 .{ .src = .{ .to_mem, .to_mem } }, 17613 }, 17614 .call_frame = .{ .alignment = .@"16" }, 17615 .extra_temps = .{ 17616 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17617 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17618 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17619 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17620 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17621 else => unreachable, 17622 .e => "__eqhf2", 17623 .ne => "__nehf2", 17624 } } } }, 17625 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17626 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17627 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17628 .unused, 17629 }, 17630 .dst_temps = .{.mem}, 17631 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17632 .each = .{ .once = &.{ 17633 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17634 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17635 .{ .@"0:", .vp_, .xor, .tmp3x, .tmp3x, .tmp3x, ._ }, 17636 .{ ._, .vp_w, .insr, .tmp2x, .tmp3x, .memsi(.src0w, .@"2", .tmp0), .ui(0) }, 17637 .{ ._, .vp_w, .insr, .tmp3x, .tmp3x, .memsi(.src1w, .@"2", .tmp0), .ui(0) }, 17638 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17639 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17640 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17641 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17642 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17643 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17644 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17645 .{ ._, ._, .lea, .tmp0d, .lead(.none, .tmp0, 1), ._, ._ }, 17646 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17647 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17648 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17649 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17650 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17651 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17652 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17653 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17654 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17655 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17656 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17657 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17658 } }, 17659 }, .{ 17660 .required_features = .{ .@"64bit", .avx, null, null }, 17661 .src_constraints = .{ 17662 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17663 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17664 }, 17665 .patterns = &.{ 17666 .{ .src = .{ .to_mem, .to_mem } }, 17667 }, 17668 .call_frame = .{ .alignment = .@"16" }, 17669 .extra_temps = .{ 17670 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17671 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17672 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17673 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17674 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17675 else => unreachable, 17676 .e => "__eqhf2", 17677 .ne => "__nehf2", 17678 } } } }, 17679 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17680 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17681 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17682 .unused, 17683 }, 17684 .dst_temps = .{.mem}, 17685 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17686 .each = .{ .once = &.{ 17687 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17688 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17689 .{ .@"0:", .vp_, .xor, .tmp3x, .tmp3x, .tmp3x, ._ }, 17690 .{ ._, .vp_w, .insr, .tmp2x, .tmp3x, .memsi(.src0w, .@"2", .tmp0), .ui(0) }, 17691 .{ ._, .vp_w, .insr, .tmp3x, .tmp3x, .memsi(.src1w, .@"2", .tmp0), .ui(0) }, 17692 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17693 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17694 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17695 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17696 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17697 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17698 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17699 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17700 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17701 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17702 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17703 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17704 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17705 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17706 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17707 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17708 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17709 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17710 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17711 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17712 } }, 17713 }, .{ 17714 .required_features = .{ .@"64bit", .sse2, .slow_incdec, null }, 17715 .src_constraints = .{ 17716 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17717 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17718 }, 17719 .patterns = &.{ 17720 .{ .src = .{ .to_mem, .to_mem } }, 17721 }, 17722 .call_frame = .{ .alignment = .@"16" }, 17723 .extra_temps = .{ 17724 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17725 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17726 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17727 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17728 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17729 else => unreachable, 17730 .e => "__eqhf2", 17731 .ne => "__nehf2", 17732 } } } }, 17733 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17734 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17735 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17736 .unused, 17737 }, 17738 .dst_temps = .{.mem}, 17739 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17740 .each = .{ .once = &.{ 17741 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17742 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17743 .{ .@"0:", .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 17744 .{ ._, .p_w, .insr, .tmp2x, .memsi(.src0w, .@"2", .tmp0), .ui(0), ._ }, 17745 .{ ._, .p_, .xor, .tmp3x, .tmp3x, ._, ._ }, 17746 .{ ._, .p_w, .insr, .tmp3x, .memsi(.src1w, .@"2", .tmp0), .ui(0), ._ }, 17747 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17748 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17749 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17750 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17751 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17752 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17753 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17754 .{ ._, ._, .lea, .tmp0d, .lead(.none, .tmp0, 1), ._, ._ }, 17755 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17756 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17757 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17758 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17759 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17760 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17761 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17762 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17763 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17764 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17765 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17766 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17767 } }, 17768 }, .{ 17769 .required_features = .{ .@"64bit", .sse2, null, null }, 17770 .src_constraints = .{ 17771 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17772 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17773 }, 17774 .patterns = &.{ 17775 .{ .src = .{ .to_mem, .to_mem } }, 17776 }, 17777 .call_frame = .{ .alignment = .@"16" }, 17778 .extra_temps = .{ 17779 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17780 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17781 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17782 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17783 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17784 else => unreachable, 17785 .e => "__eqhf2", 17786 .ne => "__nehf2", 17787 } } } }, 17788 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17789 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17790 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17791 .unused, 17792 }, 17793 .dst_temps = .{.mem}, 17794 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17795 .each = .{ .once = &.{ 17796 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17797 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17798 .{ .@"0:", .p_, .xor, .tmp2x, .tmp2x, ._, ._ }, 17799 .{ ._, .p_w, .insr, .tmp2x, .memsi(.src0w, .@"2", .tmp0), .ui(0), ._ }, 17800 .{ ._, .p_, .xor, .tmp3x, .tmp3x, ._, ._ }, 17801 .{ ._, .p_w, .insr, .tmp3x, .memsi(.src1w, .@"2", .tmp0), .ui(0), ._ }, 17802 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17803 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17804 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17805 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17806 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17807 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17808 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17809 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17810 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17811 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17812 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17813 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17814 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17815 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17816 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17817 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17818 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17819 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17820 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17821 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17822 } }, 17823 }, .{ 17824 .required_features = .{ .@"64bit", .sse, .slow_incdec, null }, 17825 .src_constraints = .{ 17826 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17827 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17828 }, 17829 .patterns = &.{ 17830 .{ .src = .{ .to_mem, .to_mem } }, 17831 }, 17832 .call_frame = .{ .alignment = .@"16" }, 17833 .extra_temps = .{ 17834 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17835 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17836 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17837 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17838 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17839 else => unreachable, 17840 .e => "__eqhf2", 17841 .ne => "__nehf2", 17842 } } } }, 17843 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17844 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17845 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17846 .{ .type = .f32, .kind = .mem }, 17847 }, 17848 .dst_temps = .{.mem}, 17849 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17850 .each = .{ .once = &.{ 17851 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17852 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17853 .{ .@"0:", ._, .movzx, .tmp5d, .memsi(.src0w, .@"2", .tmp0), ._, ._ }, 17854 .{ ._, ._, .mov, .mem(.tmp8d), .tmp5d, ._, ._ }, 17855 .{ ._, ._ss, .mov, .tmp2x, .mem(.tmp8d), ._, ._ }, 17856 .{ ._, ._, .movzx, .tmp5d, .memsi(.src1w, .@"2", .tmp0), ._, ._ }, 17857 .{ ._, ._, .mov, .mem(.tmp8d), .tmp5d, ._, ._ }, 17858 .{ ._, ._ss, .mov, .tmp3x, .mem(.tmp8d), ._, ._ }, 17859 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17860 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17861 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17862 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17863 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17864 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17865 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17866 .{ ._, ._, .lea, .tmp0d, .lead(.none, .tmp0, 1), ._, ._ }, 17867 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17868 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17869 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17870 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17871 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17872 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17873 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17874 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17875 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17876 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17877 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17878 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17879 } }, 17880 }, .{ 17881 .required_features = .{ .@"64bit", .sse, null, null }, 17882 .src_constraints = .{ 17883 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17884 .{ .multiple_scalar_float = .{ .of = .word, .is = .word } }, 17885 }, 17886 .patterns = &.{ 17887 .{ .src = .{ .to_mem, .to_mem } }, 17888 }, 17889 .call_frame = .{ .alignment = .@"16" }, 17890 .extra_temps = .{ 17891 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 17892 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 17893 .{ .type = .f16, .kind = .{ .reg = .xmm0 } }, 17894 .{ .type = .f16, .kind = .{ .reg = .xmm1 } }, 17895 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 17896 else => unreachable, 17897 .e => "__eqhf2", 17898 .ne => "__nehf2", 17899 } } } }, 17900 .{ .type = .i32, .kind = .{ .reg = .eax } }, 17901 .{ .type = .u8, .kind = .{ .reg = .cl } }, 17902 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 17903 .{ .type = .f32, .kind = .mem }, 17904 }, 17905 .dst_temps = .{.mem}, 17906 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 17907 .each = .{ .once = &.{ 17908 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 17909 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17910 .{ .@"0:", ._, .movzx, .tmp5d, .memsi(.src0w, .@"2", .tmp0), ._, ._ }, 17911 .{ ._, ._, .mov, .mem(.tmp8d), .tmp5d, ._, ._ }, 17912 .{ ._, ._ss, .mov, .tmp2x, .mem(.tmp8d), ._, ._ }, 17913 .{ ._, ._, .movzx, .tmp5d, .memsi(.src1w, .@"2", .tmp0), ._, ._ }, 17914 .{ ._, ._, .mov, .mem(.tmp8d), .tmp5d, ._, ._ }, 17915 .{ ._, ._ss, .mov, .tmp3x, .mem(.tmp8d), ._, ._ }, 17916 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 17917 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 17918 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 17919 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 17920 .{ ._, ._, .mov, .tmp6d, .tmp0d, ._, ._ }, 17921 .{ ._, ._l, .sh, .tmp7q, .tmp6b, ._, ._ }, 17922 .{ ._, ._, .@"or", .tmp1q, .tmp7q, ._, ._ }, 17923 .{ ._, ._c, .in, .tmp0d, ._, ._, ._ }, 17924 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17925 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 17926 .{ ._, ._, .mov, .tmp5d, .tmp0d, ._, ._ }, 17927 .{ ._, ._r, .sh, .tmp5d, .ui(3), ._, ._ }, 17928 .{ ._, ._, .mov, .memid(.dst0q, .tmp5, -8), .tmp1q, ._, ._ }, 17929 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 17930 .{ .@"1:", ._, .cmp, .tmp0d, .sa(.src0, .add_len), ._, ._ }, 17931 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 17932 .{ ._, ._, .@"test", .tmp0d, .si(0b111111), ._, ._ }, 17933 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 17934 .{ ._, ._r, .sh, .tmp0d, .ui(6), ._, ._ }, 17935 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp0), .tmp1q, ._, ._ }, 17936 } }, 17937 }, .{ 17938 .required_features = .{ .avx, .slow_incdec, null, null }, 17939 .src_constraints = .{ 17940 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 17941 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 17942 }, 17943 .patterns = &.{ 17944 .{ .src = .{ .to_mem, .to_mem } }, 17945 }, 17946 .extra_temps = .{ 17947 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 17948 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 17949 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 17950 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 17951 .unused, 17952 .unused, 17953 .unused, 17954 .unused, 17955 .unused, 17956 }, 17957 .dst_temps = .{.mem}, 17958 .clobbers = .{ .eflags = true }, 17959 .each = .{ .once = &.{ 17960 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 17961 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 17962 .{ .@"0:", .v_ps, .mova, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 17963 .{ ._, .v_ps, .cmp, .tmp2y, .tmp2y, .memia(.src1y, .tmp0, .add_size), .vp(switch (cc) { 17964 else => unreachable, 17965 .e => .eq, 17966 .ne => .neq, 17967 }) }, 17968 .{ ._, .v_ps, .movmsk, .tmp3d, .tmp2y, ._, ._ }, 17969 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp3b, ._, ._ }, 17970 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 17971 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 17972 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 17973 } }, 17974 }, .{ 17975 .required_features = .{ .avx, null, null, null }, 17976 .src_constraints = .{ 17977 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 17978 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 17979 }, 17980 .patterns = &.{ 17981 .{ .src = .{ .to_mem, .to_mem } }, 17982 }, 17983 .extra_temps = .{ 17984 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 17985 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 17986 .{ .type = .vector_8_f32, .kind = .{ .rc = .sse } }, 17987 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 17988 .unused, 17989 .unused, 17990 .unused, 17991 .unused, 17992 .unused, 17993 }, 17994 .dst_temps = .{.mem}, 17995 .clobbers = .{ .eflags = true }, 17996 .each = .{ .once = &.{ 17997 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 17998 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 17999 .{ .@"0:", .v_ps, .mova, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 18000 .{ ._, .v_ps, .cmp, .tmp2y, .tmp2y, .memia(.src1y, .tmp0, .add_size), .vp(switch (cc) { 18001 else => unreachable, 18002 .e => .eq, 18003 .ne => .neq, 18004 }) }, 18005 .{ ._, .v_ps, .movmsk, .tmp3d, .tmp2y, ._, ._ }, 18006 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp3b, ._, ._ }, 18007 .{ ._, ._c, .in, .tmp1q, ._, ._, ._ }, 18008 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 18009 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18010 } }, 18011 }, .{ 18012 .required_features = .{ .sse2, null, null, null }, 18013 .src_constraints = .{ 18014 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 18015 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 18016 }, 18017 .patterns = &.{ 18018 .{ .src = .{ .to_mem, .to_mem } }, 18019 }, 18020 .extra_temps = .{ 18021 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18022 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 18023 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 18024 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 18025 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18026 .unused, 18027 .unused, 18028 .unused, 18029 .unused, 18030 }, 18031 .dst_temps = .{.mem}, 18032 .clobbers = .{ .eflags = true }, 18033 .each = .{ .once = &.{ 18034 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18035 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 18036 .{ .@"0:", ._ps, .mova, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18037 .{ ._, ._ps, .mova, .tmp3x, .memiad(.src0x, .tmp0, .add_size, 16), ._, ._ }, 18038 .{ ._, ._ps, .cmp, .tmp2x, .memia(.src1x, .tmp0, .add_size), .vp(switch (cc) { 18039 else => unreachable, 18040 .e => .eq, 18041 .ne => .neq, 18042 }), ._ }, 18043 .{ ._, ._ps, .cmp, .tmp3x, .memiad(.src1x, .tmp0, .add_size, 16), .vp(switch (cc) { 18044 else => unreachable, 18045 .e => .eq, 18046 .ne => .neq, 18047 }), ._ }, 18048 .{ ._, .p_w, .ackssd, .tmp2x, .tmp3x, ._, ._ }, 18049 .{ ._, .p_b, .ackssw, .tmp2x, .tmp2x, ._, ._ }, 18050 .{ ._, .p_b, .movmsk, .tmp4d, .tmp2x, ._, ._ }, 18051 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp4b, ._, ._ }, 18052 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 18053 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 18054 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18055 } }, 18056 }, .{ 18057 .required_features = .{ .sse, null, null, null }, 18058 .src_constraints = .{ 18059 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 18060 .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, 18061 }, 18062 .patterns = &.{ 18063 .{ .src = .{ .to_mem, .to_mem } }, 18064 }, 18065 .extra_temps = .{ 18066 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18067 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 18068 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 18069 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18070 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18071 .unused, 18072 .unused, 18073 .unused, 18074 .unused, 18075 }, 18076 .dst_temps = .{.mem}, 18077 .clobbers = .{ .eflags = true }, 18078 .each = .{ .once = &.{ 18079 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18080 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 18081 .{ .@"0:", ._ps, .mova, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18082 .{ ._, ._ps, .cmp, .tmp2x, .memia(.src1x, .tmp0, .add_size), .vp(switch (cc) { 18083 else => unreachable, 18084 .e => .eq, 18085 .ne => .neq, 18086 }), ._ }, 18087 .{ ._, ._ps, .movmsk, .tmp3d, .tmp2x, ._, ._ }, 18088 .{ ._, ._ps, .mova, .tmp2x, .memiad(.src0x, .tmp0, .add_size, 16), ._, ._ }, 18089 .{ ._, ._ps, .cmp, .tmp2x, .memiad(.src1x, .tmp0, .add_size, 16), .vp(switch (cc) { 18090 else => unreachable, 18091 .e => .eq, 18092 .ne => .neq, 18093 }), ._ }, 18094 .{ ._, ._ps, .movmsk, .tmp4d, .tmp2x, ._, ._ }, 18095 .{ ._, ._l, .sh, .tmp4b, .ui(4), ._, ._ }, 18096 .{ ._, ._, .@"or", .tmp3b, .tmp4b, ._, ._ }, 18097 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp3b, ._, ._ }, 18098 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 18099 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 18100 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18101 } }, 18102 }, .{ 18103 .required_features = .{ .sse, null, null, null }, 18104 .src_constraints = .{ 18105 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 18106 .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, 18107 }, 18108 .patterns = &.{ 18109 .{ .src = .{ .to_mem, .to_mem } }, 18110 }, 18111 .extra_temps = .{ 18112 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18113 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 18114 .{ .type = .vector_4_f32, .kind = .{ .rc = .sse } }, 18115 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18116 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18117 .unused, 18118 .unused, 18119 .unused, 18120 .unused, 18121 }, 18122 .dst_temps = .{.mem}, 18123 .clobbers = .{ .eflags = true }, 18124 .each = .{ .once = &.{ 18125 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18126 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 18127 .{ ._, ._mp, .j, .@"1f", ._, ._, ._ }, 18128 .{ .@"0:", ._ps, .mova, .tmp2x, .memiad(.src0x, .tmp0, .add_size, -16), ._, ._ }, 18129 .{ ._, ._ps, .cmp, .tmp2x, .memiad(.src1x, .tmp0, .add_size, -16), .vp(switch (cc) { 18130 else => unreachable, 18131 .e => .eq, 18132 .ne => .neq, 18133 }), ._ }, 18134 .{ ._, ._ps, .movmsk, .tmp4d, .tmp2x, ._, ._ }, 18135 .{ ._, ._l, .sh, .tmp4b, .ui(4), ._, ._ }, 18136 .{ ._, ._, .@"or", .tmp3b, .tmp4b, ._, ._ }, 18137 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp3b, ._, ._ }, 18138 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 18139 .{ .@"1:", ._ps, .mova, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18140 .{ ._, ._ps, .cmp, .tmp2x, .memia(.src1x, .tmp0, .add_size), .vp(switch (cc) { 18141 else => unreachable, 18142 .e => .eq, 18143 .ne => .neq, 18144 }), ._ }, 18145 .{ ._, ._ps, .movmsk, .tmp3d, .tmp2x, ._, ._ }, 18146 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 18147 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18148 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp3b, ._, ._ }, 18149 } }, 18150 }, .{ 18151 .required_features = .{ .avx, .slow_incdec, null, null }, 18152 .src_constraints = .{ 18153 .{ .multiple_scalar_float = .{ .of = .zword, .is = .qword } }, 18154 .{ .multiple_scalar_float = .{ .of = .zword, .is = .qword } }, 18155 }, 18156 .patterns = &.{ 18157 .{ .src = .{ .to_mem, .to_mem } }, 18158 }, 18159 .extra_temps = .{ 18160 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18161 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 18162 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 18163 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 18164 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18165 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18166 .unused, 18167 .unused, 18168 .unused, 18169 }, 18170 .dst_temps = .{.mem}, 18171 .clobbers = .{ .eflags = true }, 18172 .each = .{ .once = &.{ 18173 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18174 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 18175 .{ .@"0:", .v_pd, .mova, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 18176 .{ ._, .v_pd, .mova, .tmp3y, .memiad(.src0y, .tmp0, .add_size, 32), ._, ._ }, 18177 .{ ._, .v_pd, .cmp, .tmp2y, .tmp2y, .memia(.src1y, .tmp0, .add_size), .vp(switch (cc) { 18178 else => unreachable, 18179 .e => .eq, 18180 .ne => .neq, 18181 }) }, 18182 .{ ._, .v_pd, .cmp, .tmp3y, .tmp3y, .memiad(.src1y, .tmp0, .add_size, 32), .vp(switch (cc) { 18183 else => unreachable, 18184 .e => .eq, 18185 .ne => .neq, 18186 }) }, 18187 .{ ._, .v_pd, .movmsk, .tmp4d, .tmp2y, ._, ._ }, 18188 .{ ._, .v_pd, .movmsk, .tmp5d, .tmp3y, ._, ._ }, 18189 .{ ._, ._l, .sh, .tmp5b, .ui(4), ._, ._ }, 18190 .{ ._, ._, .@"or", .tmp4b, .tmp5b, ._, ._ }, 18191 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp4b, ._, ._ }, 18192 .{ ._, ._, .lea, .tmp1p, .lead(.none, .tmp1, 1), ._, ._ }, 18193 .{ ._, ._, .add, .tmp0p, .si(64), ._, ._ }, 18194 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18195 } }, 18196 }, .{ 18197 .required_features = .{ .avx, null, null, null }, 18198 .src_constraints = .{ 18199 .{ .multiple_scalar_float = .{ .of = .zword, .is = .qword } }, 18200 .{ .multiple_scalar_float = .{ .of = .zword, .is = .qword } }, 18201 }, 18202 .patterns = &.{ 18203 .{ .src = .{ .to_mem, .to_mem } }, 18204 }, 18205 .extra_temps = .{ 18206 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18207 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 18208 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 18209 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 18210 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18211 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18212 .unused, 18213 .unused, 18214 .unused, 18215 }, 18216 .dst_temps = .{.mem}, 18217 .clobbers = .{ .eflags = true }, 18218 .each = .{ .once = &.{ 18219 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18220 .{ ._, ._, .lea, .tmp1p, .mem(.dst0), ._, ._ }, 18221 .{ .@"0:", .v_pd, .mova, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 18222 .{ ._, .v_pd, .mova, .tmp3y, .memiad(.src0y, .tmp0, .add_size, 32), ._, ._ }, 18223 .{ ._, .v_pd, .cmp, .tmp2y, .tmp2y, .memia(.src1y, .tmp0, .add_size), .vp(switch (cc) { 18224 else => unreachable, 18225 .e => .eq, 18226 .ne => .neq, 18227 }) }, 18228 .{ ._, .v_pd, .cmp, .tmp3y, .tmp3y, .memiad(.src1y, .tmp0, .add_size, 32), .vp(switch (cc) { 18229 else => unreachable, 18230 .e => .eq, 18231 .ne => .neq, 18232 }) }, 18233 .{ ._, .v_pd, .movmsk, .tmp4d, .tmp2y, ._, ._ }, 18234 .{ ._, .v_pd, .movmsk, .tmp5d, .tmp3y, ._, ._ }, 18235 .{ ._, ._l, .sh, .tmp5b, .ui(4), ._, ._ }, 18236 .{ ._, ._, .@"or", .tmp4b, .tmp5b, ._, ._ }, 18237 .{ ._, ._, .mov, .lea(.byte, .tmp1), .tmp4b, ._, ._ }, 18238 .{ ._, ._c, .in, .tmp1q, ._, ._, ._ }, 18239 .{ ._, ._, .add, .tmp0p, .si(64), ._, ._ }, 18240 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18241 } }, 18242 }, .{ 18243 .required_features = .{ .avx, null, null, null }, 18244 .src_constraints = .{ 18245 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 18246 .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, 18247 }, 18248 .patterns = &.{ 18249 .{ .src = .{ .to_mem, .to_mem } }, 18250 }, 18251 .extra_temps = .{ 18252 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18253 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18254 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18255 .{ .type = .vector_4_f64, .kind = .{ .rc = .sse } }, 18256 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18257 .unused, 18258 .unused, 18259 .unused, 18260 .unused, 18261 }, 18262 .dst_temps = .{.mem}, 18263 .clobbers = .{ .eflags = true }, 18264 .each = .{ .once = &.{ 18265 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18266 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18267 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18268 .{ .@"0:", .v_pd, .mova, .tmp3y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 18269 .{ ._, .v_pd, .cmp, .tmp3y, .tmp3y, .memia(.src1y, .tmp0, .add_size), .vp(switch (cc) { 18270 else => unreachable, 18271 .e => .eq, 18272 .ne => .neq, 18273 }) }, 18274 .{ ._, .v_pd, .movmsk, .tmp4d, .tmp3y, ._, ._ }, 18275 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 18276 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 18277 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 4), ._, ._ }, 18278 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18279 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18280 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 18281 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 18282 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 18283 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18284 .{ .@"1:", ._, .add, .tmp0p, .si(32), ._, ._ }, 18285 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18286 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18287 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18288 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18289 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18290 } }, 18291 }, .{ 18292 .required_features = .{ .sse2, null, null, null }, 18293 .src_constraints = .{ 18294 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 18295 .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, 18296 }, 18297 .patterns = &.{ 18298 .{ .src = .{ .to_mem, .to_mem } }, 18299 }, 18300 .extra_temps = .{ 18301 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18302 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18303 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18304 .{ .type = .vector_2_f64, .kind = .{ .rc = .sse } }, 18305 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18306 .unused, 18307 .unused, 18308 .unused, 18309 .unused, 18310 }, 18311 .dst_temps = .{.mem}, 18312 .clobbers = .{ .eflags = true }, 18313 .each = .{ .once = &.{ 18314 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18315 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18316 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18317 .{ .@"0:", ._pd, .mova, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18318 .{ ._, ._pd, .cmp, .tmp3x, .memia(.src1x, .tmp0, .add_size), .vp(switch (cc) { 18319 else => unreachable, 18320 .e => .eq, 18321 .ne => .neq, 18322 }), ._ }, 18323 .{ ._, ._pd, .movmsk, .tmp4d, .tmp3x, ._, ._ }, 18324 .{ ._, ._l, .ro, .tmp4b, .tmp1b, ._, ._ }, 18325 .{ ._, ._, .@"or", .tmp2b, .tmp4b, ._, ._ }, 18326 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 2), ._, ._ }, 18327 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18328 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18329 .{ ._, ._, .mov, .tmp4d, .tmp1d, ._, ._ }, 18330 .{ ._, ._r, .sh, .tmp4d, .ui(3), ._, ._ }, 18331 .{ ._, ._, .mov, .memid(.dst0b, .tmp4, -1), .tmp2b, ._, ._ }, 18332 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18333 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 18334 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18335 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18336 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18337 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18338 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18339 } }, 18340 }, .{ 18341 .required_features = .{ .x87, .cmov, .slow_incdec, null }, 18342 .src_constraints = .{ 18343 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18344 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18345 }, 18346 .patterns = &.{ 18347 .{ .src = .{ .to_mem, .to_mem } }, 18348 }, 18349 .extra_temps = .{ 18350 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18351 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18352 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18353 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18354 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18355 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18356 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18357 .unused, 18358 .unused, 18359 }, 18360 .dst_temps = .{.mem}, 18361 .clobbers = .{ .eflags = true }, 18362 .each = .{ .once = &.{ 18363 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18364 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18365 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18366 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18367 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 18368 .{ ._, .f_, .ld, .memia(.src1q, .tmp0, .add_size), ._, ._, ._ }, 18369 .{ ._, .f_, .ld, .memia(.src0q, .tmp0, .add_size), ._, ._, ._ }, 18370 .{ ._, .f_p, .ucomi, .tmp5t, .tmp6t, ._, ._ }, 18371 .{ ._, .f_p, .st, .tmp6t, ._, ._, ._ }, 18372 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18373 .{ ._, switch (cc) { 18374 else => unreachable, 18375 .e => ._np, 18376 .ne => ._p, 18377 }, .set, .tmp4b, ._, ._, ._ }, 18378 .{ ._, ._, switch (cc) { 18379 else => unreachable, 18380 .e => .@"and", 18381 .ne => .@"or", 18382 }, .tmp3b, .tmp4b, ._, ._ }, 18383 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18384 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18385 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18386 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18387 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18388 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18389 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18390 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18391 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18392 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 18393 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18394 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18395 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18396 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18397 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18398 } }, 18399 }, .{ 18400 .required_features = .{ .x87, .cmov, null, null }, 18401 .src_constraints = .{ 18402 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18403 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18404 }, 18405 .patterns = &.{ 18406 .{ .src = .{ .to_mem, .to_mem } }, 18407 }, 18408 .extra_temps = .{ 18409 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18410 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18411 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18412 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18413 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18414 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18415 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18416 .unused, 18417 .unused, 18418 }, 18419 .dst_temps = .{.mem}, 18420 .clobbers = .{ .eflags = true }, 18421 .each = .{ .once = &.{ 18422 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18423 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18424 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18425 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18426 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 18427 .{ ._, .f_, .ld, .memia(.src1q, .tmp0, .add_size), ._, ._, ._ }, 18428 .{ ._, .f_, .ld, .memia(.src0q, .tmp0, .add_size), ._, ._, ._ }, 18429 .{ ._, .f_p, .ucomi, .tmp5t, .tmp6t, ._, ._ }, 18430 .{ ._, .f_p, .st, .tmp6t, ._, ._, ._ }, 18431 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18432 .{ ._, switch (cc) { 18433 else => unreachable, 18434 .e => ._np, 18435 .ne => ._p, 18436 }, .set, .tmp4b, ._, ._, ._ }, 18437 .{ ._, ._, switch (cc) { 18438 else => unreachable, 18439 .e => .@"and", 18440 .ne => .@"or", 18441 }, .tmp3b, .tmp4b, ._, ._ }, 18442 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18443 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18444 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18445 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18446 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18447 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18448 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18449 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18450 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18451 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 18452 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18453 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18454 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18455 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18456 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18457 } }, 18458 }, .{ 18459 .required_features = .{ .x87, null, null, null }, 18460 .src_constraints = .{ 18461 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18462 .{ .multiple_scalar_float = .{ .of = .qword, .is = .qword } }, 18463 }, 18464 .patterns = &.{ 18465 .{ .src = .{ .to_mem, .to_mem } }, 18466 }, 18467 .extra_temps = .{ 18468 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18469 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18470 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18471 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18472 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18473 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18474 .{ .type = .u8, .kind = .{ .reg = .ah } }, 18475 .unused, 18476 .unused, 18477 }, 18478 .dst_temps = .{.mem}, 18479 .clobbers = .{ .eflags = true }, 18480 .each = .{ .once = &.{ 18481 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18482 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18483 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18484 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18485 .{ ._, .f_, .ld, .memia(.src1q, .tmp0, .add_size), ._, ._, ._ }, 18486 .{ ._, .f_, .ld, .memia(.src0q, .tmp0, .add_size), ._, ._, ._ }, 18487 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 18488 .{ ._, .fn_sw, .st, .tmp6w, ._, ._, ._ }, 18489 .{ ._, ._, .xor, .tmp6b, .si(0b1_000_000), ._, ._ }, 18490 .{ ._, ._, .@"test", .tmp6b, .si(0b1_000_100), ._, ._ }, 18491 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18492 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18493 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18494 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18495 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18496 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18497 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18498 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18499 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18500 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18501 .{ .@"1:", ._, .add, .tmp0p, .si(8), ._, ._ }, 18502 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18503 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18504 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18505 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18506 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18507 } }, 18508 }, .{ 18509 .required_features = .{ .x87, .cmov, .slow_incdec, null }, 18510 .src_constraints = .{ 18511 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18512 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18513 }, 18514 .patterns = &.{ 18515 .{ .src = .{ .to_mem, .to_mem } }, 18516 }, 18517 .extra_temps = .{ 18518 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18519 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18520 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18521 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18522 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18523 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18524 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18525 .unused, 18526 .unused, 18527 }, 18528 .dst_temps = .{.mem}, 18529 .clobbers = .{ .eflags = true }, 18530 .each = .{ .once = &.{ 18531 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18532 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18533 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18534 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18535 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 18536 .{ ._, .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 18537 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 18538 .{ ._, .f_p, .ucomi, .tmp5t, .tmp6t, ._, ._ }, 18539 .{ ._, .f_p, .st, .tmp6t, ._, ._, ._ }, 18540 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18541 .{ ._, switch (cc) { 18542 else => unreachable, 18543 .e => ._np, 18544 .ne => ._p, 18545 }, .set, .tmp4b, ._, ._, ._ }, 18546 .{ ._, ._, switch (cc) { 18547 else => unreachable, 18548 .e => .@"and", 18549 .ne => .@"or", 18550 }, .tmp3b, .tmp4b, ._, ._ }, 18551 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18552 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18553 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18554 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18555 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18556 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18557 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18558 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18559 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18560 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 18561 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18562 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18563 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18564 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18565 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18566 } }, 18567 }, .{ 18568 .required_features = .{ .x87, .cmov, null, null }, 18569 .src_constraints = .{ 18570 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18571 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18572 }, 18573 .patterns = &.{ 18574 .{ .src = .{ .to_mem, .to_mem } }, 18575 }, 18576 .extra_temps = .{ 18577 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18578 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18579 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18580 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18581 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18582 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18583 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18584 .unused, 18585 .unused, 18586 }, 18587 .dst_temps = .{.mem}, 18588 .clobbers = .{ .eflags = true }, 18589 .each = .{ .once = &.{ 18590 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18591 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18592 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18593 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18594 .{ ._, ._, .xor, .tmp4d, .tmp4d, ._, ._ }, 18595 .{ ._, .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 18596 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 18597 .{ ._, .f_p, .ucomi, .tmp5t, .tmp6t, ._, ._ }, 18598 .{ ._, .f_p, .st, .tmp6t, ._, ._, ._ }, 18599 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18600 .{ ._, switch (cc) { 18601 else => unreachable, 18602 .e => ._np, 18603 .ne => ._p, 18604 }, .set, .tmp4b, ._, ._, ._ }, 18605 .{ ._, ._, switch (cc) { 18606 else => unreachable, 18607 .e => .@"and", 18608 .ne => .@"or", 18609 }, .tmp3b, .tmp4b, ._, ._ }, 18610 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18611 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18612 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18613 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18614 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18615 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18616 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18617 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18618 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18619 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 18620 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18621 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18622 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18623 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18624 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18625 } }, 18626 }, .{ 18627 .required_features = .{ .x87, null, null, null }, 18628 .src_constraints = .{ 18629 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18630 .{ .multiple_scalar_float = .{ .of = .xword, .is = .tbyte } }, 18631 }, 18632 .patterns = &.{ 18633 .{ .src = .{ .to_mem, .to_mem } }, 18634 }, 18635 .extra_temps = .{ 18636 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18637 .{ .type = .u32, .kind = .{ .reg = .rcx } }, 18638 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18639 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 18640 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 18641 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 18642 .{ .type = .u8, .kind = .{ .reg = .ah } }, 18643 .unused, 18644 .unused, 18645 }, 18646 .dst_temps = .{.mem}, 18647 .clobbers = .{ .eflags = true }, 18648 .each = .{ .once = &.{ 18649 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18650 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18651 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18652 .{ .@"0:", ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 18653 .{ ._, .f_, .ld, .memia(.src1t, .tmp0, .add_size), ._, ._, ._ }, 18654 .{ ._, .f_, .ld, .memia(.src0t, .tmp0, .add_size), ._, ._, ._ }, 18655 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 18656 .{ ._, .fn_sw, .st, .tmp6w, ._, ._, ._ }, 18657 .{ ._, ._, .xor, .tmp6b, .si(0b1_000_000), ._, ._ }, 18658 .{ ._, ._, .@"test", .tmp6b, .si(0b1_000_100), ._, ._ }, 18659 .{ ._, .fromCond(cc), .set, .tmp3b, ._, ._, ._ }, 18660 .{ ._, ._l, .ro, .tmp3b, .tmp1b, ._, ._ }, 18661 .{ ._, ._, .@"or", .tmp2b, .tmp3b, ._, ._ }, 18662 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18663 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18664 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18665 .{ ._, ._, .mov, .tmp3d, .tmp1d, ._, ._ }, 18666 .{ ._, ._r, .sh, .tmp3d, .ui(3), ._, ._ }, 18667 .{ ._, ._, .mov, .memid(.dst0b, .tmp3, -1), .tmp2b, ._, ._ }, 18668 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18669 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 18670 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18671 .{ ._, ._, .@"test", .tmp1d, .si(0b111), ._, ._ }, 18672 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18673 .{ ._, ._r, .sh, .tmp1d, .ui(3), ._, ._ }, 18674 .{ ._, ._, .mov, .memi(.dst0b, .tmp1), .tmp2b, ._, ._ }, 18675 } }, 18676 }, .{ 18677 .required_features = .{ .avx, .slow_incdec, null, null }, 18678 .dst_constraints = .{.{ .bool_vec = .dword }}, 18679 .src_constraints = .{ 18680 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18681 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18682 }, 18683 .patterns = &.{ 18684 .{ .src = .{ .to_mem, .to_mem } }, 18685 }, 18686 .call_frame = .{ .alignment = .@"16" }, 18687 .extra_temps = .{ 18688 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18689 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18690 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18691 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18692 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18693 else => unreachable, 18694 .e => "__eqtf2", 18695 .ne => "__netf2", 18696 } } } }, 18697 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18698 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18699 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18700 .unused, 18701 }, 18702 .dst_temps = .{.{ .rc = .general_purpose }}, 18703 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18704 .each = .{ .once = &.{ 18705 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18706 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18707 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18708 .{ .@"0:", .v_dqa, .mov, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18709 .{ ._, .v_dqa, .mov, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18710 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18711 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18712 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18713 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18714 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18715 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18716 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18717 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18718 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18719 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18720 } }, 18721 }, .{ 18722 .required_features = .{ .avx, .slow_incdec, null, null }, 18723 .dst_constraints = .{.{ .bool_vec = .dword }}, 18724 .src_constraints = .{ 18725 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18726 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18727 }, 18728 .patterns = &.{ 18729 .{ .src = .{ .to_mem, .to_mem } }, 18730 }, 18731 .call_frame = .{ .alignment = .@"16" }, 18732 .extra_temps = .{ 18733 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18734 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18735 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18736 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18737 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18738 else => unreachable, 18739 .e => "__eqtf2", 18740 .ne => "__netf2", 18741 } } } }, 18742 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18743 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18744 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18745 .unused, 18746 }, 18747 .dst_temps = .{.{ .rc = .general_purpose }}, 18748 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18749 .each = .{ .once = &.{ 18750 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18751 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18752 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18753 .{ .@"0:", .v_dqa, .mov, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18754 .{ ._, .v_dqa, .mov, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18755 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18756 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18757 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18758 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18759 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18760 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18761 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18762 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18763 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18764 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18765 } }, 18766 }, .{ 18767 .required_features = .{ .sse2, .slow_incdec, null, null }, 18768 .dst_constraints = .{.{ .bool_vec = .dword }}, 18769 .src_constraints = .{ 18770 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18771 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18772 }, 18773 .patterns = &.{ 18774 .{ .src = .{ .to_mem, .to_mem } }, 18775 }, 18776 .call_frame = .{ .alignment = .@"16" }, 18777 .extra_temps = .{ 18778 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18779 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18780 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18781 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18782 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18783 else => unreachable, 18784 .e => "__eqtf2", 18785 .ne => "__netf2", 18786 } } } }, 18787 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18788 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18789 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18790 .unused, 18791 }, 18792 .dst_temps = .{.{ .rc = .general_purpose }}, 18793 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18794 .each = .{ .once = &.{ 18795 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18796 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18797 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18798 .{ .@"0:", ._dqa, .mov, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18799 .{ ._, ._dqa, .mov, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18800 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18801 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18802 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18803 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18804 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18805 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18806 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18807 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18808 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18809 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18810 } }, 18811 }, .{ 18812 .required_features = .{ .sse2, .slow_incdec, null, null }, 18813 .dst_constraints = .{.{ .bool_vec = .dword }}, 18814 .src_constraints = .{ 18815 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18816 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18817 }, 18818 .patterns = &.{ 18819 .{ .src = .{ .to_mem, .to_mem } }, 18820 }, 18821 .call_frame = .{ .alignment = .@"16" }, 18822 .extra_temps = .{ 18823 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18824 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18825 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18826 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18827 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18828 else => unreachable, 18829 .e => "__eqtf2", 18830 .ne => "__netf2", 18831 } } } }, 18832 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18833 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18834 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18835 .unused, 18836 }, 18837 .dst_temps = .{.{ .rc = .general_purpose }}, 18838 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18839 .each = .{ .once = &.{ 18840 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18841 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18842 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18843 .{ .@"0:", ._dqa, .mov, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18844 .{ ._, ._dqa, .mov, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18845 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18846 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18847 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18848 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18849 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18850 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18851 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18852 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18853 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18854 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18855 } }, 18856 }, .{ 18857 .required_features = .{ .sse, .slow_incdec, null, null }, 18858 .dst_constraints = .{.{ .bool_vec = .dword }}, 18859 .src_constraints = .{ 18860 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18861 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18862 }, 18863 .patterns = &.{ 18864 .{ .src = .{ .to_mem, .to_mem } }, 18865 }, 18866 .call_frame = .{ .alignment = .@"16" }, 18867 .extra_temps = .{ 18868 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18869 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18870 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18871 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18872 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18873 else => unreachable, 18874 .e => "__eqtf2", 18875 .ne => "__netf2", 18876 } } } }, 18877 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18878 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18879 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18880 .unused, 18881 }, 18882 .dst_temps = .{.{ .rc = .general_purpose }}, 18883 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18884 .each = .{ .once = &.{ 18885 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18886 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18887 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18888 .{ .@"0:", ._ps, .mova, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18889 .{ ._, ._ps, .mova, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18890 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18891 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18892 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18893 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18894 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18895 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18896 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18897 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18898 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18899 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18900 } }, 18901 }, .{ 18902 .required_features = .{ .sse2, .slow_incdec, null, null }, 18903 .dst_constraints = .{.{ .bool_vec = .dword }}, 18904 .src_constraints = .{ 18905 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18906 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18907 }, 18908 .patterns = &.{ 18909 .{ .src = .{ .to_mem, .to_mem } }, 18910 }, 18911 .call_frame = .{ .alignment = .@"16" }, 18912 .extra_temps = .{ 18913 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18914 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18915 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18916 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18917 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18918 else => unreachable, 18919 .e => "__eqtf2", 18920 .ne => "__netf2", 18921 } } } }, 18922 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18923 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18924 .{ .type = .u32, .kind = .{ .reg = .edx } }, 18925 .unused, 18926 }, 18927 .dst_temps = .{.{ .rc = .general_purpose }}, 18928 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18929 .each = .{ .once = &.{ 18930 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 18931 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18932 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18933 .{ .@"0:", ._ps, .mova, .tmp2x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18934 .{ ._, ._ps, .mova, .tmp3x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18935 .{ ._, ._, .call, .tmp4d, ._, ._, ._ }, 18936 .{ ._, ._, .xor, .tmp7d, .tmp7d, ._, ._ }, 18937 .{ ._, ._, .@"test", .tmp5d, .tmp5d, ._, ._ }, 18938 .{ ._, .fromCond(cc), .set, .tmp7b, ._, ._, ._ }, 18939 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18940 .{ ._, ._l, .sh, .tmp7d, .tmp6b, ._, ._ }, 18941 .{ ._, ._, .@"or", .dst0d, .tmp7d, ._, ._ }, 18942 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 18943 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 18944 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18945 } }, 18946 }, .{ 18947 .required_features = .{ .@"64bit", .avx, .slow_incdec, null }, 18948 .src_constraints = .{ 18949 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18950 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 18951 }, 18952 .patterns = &.{ 18953 .{ .src = .{ .to_mem, .to_mem } }, 18954 }, 18955 .call_frame = .{ .alignment = .@"16" }, 18956 .extra_temps = .{ 18957 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 18958 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 18959 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 18960 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 18961 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 18962 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 18963 else => unreachable, 18964 .e => "__eqtf2", 18965 .ne => "__netf2", 18966 } } } }, 18967 .{ .type = .i32, .kind = .{ .reg = .eax } }, 18968 .{ .type = .u8, .kind = .{ .reg = .cl } }, 18969 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 18970 }, 18971 .dst_temps = .{.mem}, 18972 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 18973 .each = .{ .once = &.{ 18974 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 18975 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 18976 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18977 .{ .@"0:", .v_dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 18978 .{ ._, .v_dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 18979 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 18980 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 18981 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 18982 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 18983 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 18984 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 18985 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 18986 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 18987 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 18988 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 18989 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 18990 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 18991 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 18992 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 18993 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 18994 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 18995 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 18996 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 18997 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 18998 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 18999 } }, 19000 }, .{ 19001 .required_features = .{ .@"64bit", .avx, null, null }, 19002 .src_constraints = .{ 19003 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19004 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19005 }, 19006 .patterns = &.{ 19007 .{ .src = .{ .to_mem, .to_mem } }, 19008 }, 19009 .call_frame = .{ .alignment = .@"16" }, 19010 .extra_temps = .{ 19011 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19012 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 19013 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 19014 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 19015 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 19016 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 19017 else => unreachable, 19018 .e => "__eqtf2", 19019 .ne => "__netf2", 19020 } } } }, 19021 .{ .type = .i32, .kind = .{ .reg = .eax } }, 19022 .{ .type = .u8, .kind = .{ .reg = .cl } }, 19023 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 19024 }, 19025 .dst_temps = .{.mem}, 19026 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 19027 .each = .{ .once = &.{ 19028 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19029 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19030 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19031 .{ .@"0:", .v_dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19032 .{ ._, .v_dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 19033 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 19034 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 19035 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 19036 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 19037 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 19038 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 19039 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 19040 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 19041 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19042 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 19043 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 19044 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 19045 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 19046 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19047 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 19048 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19049 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19050 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 19051 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 19052 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 19053 } }, 19054 }, .{ 19055 .required_features = .{ .@"64bit", .sse2, .slow_incdec, null }, 19056 .src_constraints = .{ 19057 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19058 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19059 }, 19060 .patterns = &.{ 19061 .{ .src = .{ .to_mem, .to_mem } }, 19062 }, 19063 .call_frame = .{ .alignment = .@"16" }, 19064 .extra_temps = .{ 19065 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19066 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 19067 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 19068 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 19069 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 19070 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 19071 else => unreachable, 19072 .e => "__eqtf2", 19073 .ne => "__netf2", 19074 } } } }, 19075 .{ .type = .i32, .kind = .{ .reg = .eax } }, 19076 .{ .type = .u8, .kind = .{ .reg = .cl } }, 19077 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 19078 }, 19079 .dst_temps = .{.mem}, 19080 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 19081 .each = .{ .once = &.{ 19082 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19083 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19084 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19085 .{ .@"0:", ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19086 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 19087 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 19088 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 19089 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 19090 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 19091 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 19092 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 19093 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 19094 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 19095 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19096 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 19097 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 19098 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 19099 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 19100 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19101 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 19102 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19103 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19104 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 19105 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 19106 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 19107 } }, 19108 }, .{ 19109 .required_features = .{ .@"64bit", .sse2, null, null }, 19110 .src_constraints = .{ 19111 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19112 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19113 }, 19114 .patterns = &.{ 19115 .{ .src = .{ .to_mem, .to_mem } }, 19116 }, 19117 .call_frame = .{ .alignment = .@"16" }, 19118 .extra_temps = .{ 19119 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19120 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 19121 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 19122 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 19123 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 19124 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 19125 else => unreachable, 19126 .e => "__eqtf2", 19127 .ne => "__netf2", 19128 } } } }, 19129 .{ .type = .i32, .kind = .{ .reg = .eax } }, 19130 .{ .type = .u8, .kind = .{ .reg = .cl } }, 19131 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 19132 }, 19133 .dst_temps = .{.mem}, 19134 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 19135 .each = .{ .once = &.{ 19136 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19137 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19138 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19139 .{ .@"0:", ._dqa, .mov, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19140 .{ ._, ._dqa, .mov, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 19141 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 19142 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 19143 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 19144 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 19145 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 19146 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 19147 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 19148 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 19149 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19150 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 19151 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 19152 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 19153 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 19154 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19155 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 19156 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19157 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19158 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 19159 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 19160 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 19161 } }, 19162 }, .{ 19163 .required_features = .{ .@"64bit", .sse, .slow_incdec, null }, 19164 .src_constraints = .{ 19165 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19166 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19167 }, 19168 .patterns = &.{ 19169 .{ .src = .{ .to_mem, .to_mem } }, 19170 }, 19171 .call_frame = .{ .alignment = .@"16" }, 19172 .extra_temps = .{ 19173 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19174 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 19175 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 19176 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 19177 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 19178 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 19179 else => unreachable, 19180 .e => "__eqtf2", 19181 .ne => "__netf2", 19182 } } } }, 19183 .{ .type = .i32, .kind = .{ .reg = .eax } }, 19184 .{ .type = .u8, .kind = .{ .reg = .cl } }, 19185 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 19186 }, 19187 .dst_temps = .{.mem}, 19188 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 19189 .each = .{ .once = &.{ 19190 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19191 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19192 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19193 .{ .@"0:", ._ps, .mova, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19194 .{ ._, ._ps, .mova, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 19195 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 19196 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 19197 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 19198 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 19199 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 19200 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 19201 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 19202 .{ ._, ._, .lea, .tmp1d, .lead(.none, .tmp1, 1), ._, ._ }, 19203 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19204 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 19205 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 19206 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 19207 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 19208 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19209 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 19210 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19211 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19212 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 19213 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 19214 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 19215 } }, 19216 }, .{ 19217 .required_features = .{ .@"64bit", .sse, null, null }, 19218 .src_constraints = .{ 19219 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19220 .{ .multiple_scalar_float = .{ .of = .xword, .is = .xword } }, 19221 }, 19222 .patterns = &.{ 19223 .{ .src = .{ .to_mem, .to_mem } }, 19224 }, 19225 .call_frame = .{ .alignment = .@"16" }, 19226 .extra_temps = .{ 19227 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19228 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 19229 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 19230 .{ .type = .f128, .kind = .{ .reg = .xmm0 } }, 19231 .{ .type = .f128, .kind = .{ .reg = .xmm1 } }, 19232 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = switch (cc) { 19233 else => unreachable, 19234 .e => "__eqtf2", 19235 .ne => "__netf2", 19236 } } } }, 19237 .{ .type = .i32, .kind = .{ .reg = .eax } }, 19238 .{ .type = .u8, .kind = .{ .reg = .cl } }, 19239 .{ .type = .u64, .kind = .{ .reg = .rdx } }, 19240 }, 19241 .dst_temps = .{.mem}, 19242 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 19243 .each = .{ .once = &.{ 19244 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19245 .{ ._, ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19246 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19247 .{ .@"0:", ._ps, .mova, .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19248 .{ ._, ._ps, .mova, .tmp4x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 19249 .{ ._, ._, .call, .tmp5d, ._, ._, ._ }, 19250 .{ ._, ._, .xor, .tmp8d, .tmp8d, ._, ._ }, 19251 .{ ._, ._, .@"test", .tmp6d, .tmp6d, ._, ._ }, 19252 .{ ._, .fromCond(cc), .set, .tmp8b, ._, ._, ._ }, 19253 .{ ._, ._, .mov, .tmp7d, .tmp1d, ._, ._ }, 19254 .{ ._, ._l, .sh, .tmp8q, .tmp7b, ._, ._ }, 19255 .{ ._, ._, .@"or", .tmp2q, .tmp8q, ._, ._ }, 19256 .{ ._, ._c, .in, .tmp1d, ._, ._, ._ }, 19257 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19258 .{ ._, ._nz, .j, .@"1f", ._, ._, ._ }, 19259 .{ ._, ._, .mov, .tmp6d, .tmp1d, ._, ._ }, 19260 .{ ._, ._r, .sh, .tmp6d, .ui(3), ._, ._ }, 19261 .{ ._, ._, .mov, .memid(.dst0q, .tmp6, -8), .tmp2q, ._, ._ }, 19262 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19263 .{ .@"1:", ._, .add, .tmp0p, .si(16), ._, ._ }, 19264 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19265 .{ ._, ._, .@"test", .tmp1d, .si(0b111111), ._, ._ }, 19266 .{ ._, ._z, .j, .@"0f", ._, ._, ._ }, 19267 .{ ._, ._r, .sh, .tmp1d, .ui(6), ._, ._ }, 19268 .{ ._, ._, .mov, .memsi(.dst0q, .@"8", .tmp1), .tmp2q, ._, ._ }, 19269 } }, 19270 } }, 19271 }) catch |err| switch (err) { 19272 error.SelectFailed => return cg.fail("failed to select {s} {s} {} {} {}", .{ 19273 @tagName(air_tag), 19274 @tagName(extra.compareOperator()), 19275 cg.typeOf(extra.lhs).fmt(pt), 19276 ops[0].tracking(cg), 19277 ops[1].tracking(cg), 19278 }), 19279 else => |e| return e, 19280 }, 19281 .gte => unreachable, 19282 .gt => unreachable, 19283 } 19284 try res[0].finish(inst, &.{ extra.lhs, extra.rhs }, &ops, cg); 19285 }, 19286 19287 .abs => |air_tag| if (use_old) try cg.airAbs(inst) else { 19288 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 19289 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 19290 var res: [1]Temp = undefined; 19291 cg.select(&res, &.{ty_op.ty.toType()}, &ops, comptime &.{ .{ 19292 .required_features = .{ .cmov, null, null, null }, 19293 .src_constraints = .{ .{ .int = .byte }, .any }, 19294 .patterns = &.{ 19295 .{ .src = .{ .to_gpr, .none } }, 19296 }, 19297 .dst_temps = .{.{ .rc = .general_purpose }}, 19298 .clobbers = .{ .eflags = true }, 19299 .each = .{ .once = &.{ 19300 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 19301 .{ ._, ._, .sub, .dst0b, .src0b, ._, ._ }, 19302 .{ ._, ._s, .cmov, .dst0d, .src0d, ._, ._ }, 19303 } }, 19304 }, .{ 19305 .src_constraints = .{ .{ .int = .byte }, .any }, 19306 .patterns = &.{ 19307 .{ .src = .{ .mut_mem, .none } }, 19308 }, 19309 .dst_temps = .{.{ .ref = .src0 }}, 19310 .clobbers = .{ .eflags = true }, 19311 .each = .{ .once = &.{ 19312 .{ ._, ._, .cmp, .src0b, .si(0), ._, ._ }, 19313 .{ ._, ._ge, .j, .@"0f", ._, ._, ._ }, 19314 .{ ._, ._, .neg, .dst0b, ._, ._, ._ }, 19315 } }, 19316 }, .{ 19317 .src_constraints = .{ .{ .int = .byte }, .any }, 19318 .patterns = &.{ 19319 .{ .src = .{ .to_mut_gpr, .none } }, 19320 }, 19321 .dst_temps = .{.{ .ref = .src0 }}, 19322 .clobbers = .{ .eflags = true }, 19323 .each = .{ .once = &.{ 19324 .{ ._, ._, .@"test", .src0b, .src0b, ._, ._ }, 19325 .{ ._, ._ns, .j, .@"0f", ._, ._, ._ }, 19326 .{ ._, ._, .neg, .dst0b, ._, ._, ._ }, 19327 } }, 19328 }, .{ 19329 .required_features = .{ .cmov, null, null, null }, 19330 .src_constraints = .{ .{ .int = .word }, .any }, 19331 .patterns = &.{ 19332 .{ .src = .{ .mem, .none } }, 19333 }, 19334 .dst_temps = .{.{ .rc = .general_purpose }}, 19335 .clobbers = .{ .eflags = true }, 19336 .each = .{ .once = &.{ 19337 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 19338 .{ ._, ._, .sub, .dst0w, .src0w, ._, ._ }, 19339 .{ ._, ._s, .cmov, .dst0w, .src0w, ._, ._ }, 19340 } }, 19341 }, .{ 19342 .required_features = .{ .cmov, null, null, null }, 19343 .src_constraints = .{ .{ .int = .word }, .any }, 19344 .patterns = &.{ 19345 .{ .src = .{ .to_gpr, .none } }, 19346 }, 19347 .dst_temps = .{.{ .rc = .general_purpose }}, 19348 .clobbers = .{ .eflags = true }, 19349 .each = .{ .once = &.{ 19350 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 19351 .{ ._, ._, .sub, .dst0w, .src0w, ._, ._ }, 19352 .{ ._, ._s, .cmov, .dst0d, .src0d, ._, ._ }, 19353 } }, 19354 }, .{ 19355 .src_constraints = .{ .{ .int = .word }, .any }, 19356 .patterns = &.{ 19357 .{ .src = .{ .mut_mem, .none } }, 19358 }, 19359 .dst_temps = .{.{ .ref = .src0 }}, 19360 .clobbers = .{ .eflags = true }, 19361 .each = .{ .once = &.{ 19362 .{ ._, ._, .cmp, .src0w, .si(0), ._, ._ }, 19363 .{ ._, ._ge, .j, .@"0f", ._, ._, ._ }, 19364 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 19365 } }, 19366 }, .{ 19367 .src_constraints = .{ .{ .int = .word }, .any }, 19368 .patterns = &.{ 19369 .{ .src = .{ .to_mut_gpr, .none } }, 19370 }, 19371 .dst_temps = .{.{ .ref = .src0 }}, 19372 .clobbers = .{ .eflags = true }, 19373 .each = .{ .once = &.{ 19374 .{ ._, ._, .@"test", .src0w, .src0w, ._, ._ }, 19375 .{ ._, ._ns, .j, .@"0f", ._, ._, ._ }, 19376 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 19377 } }, 19378 }, .{ 19379 .required_features = .{ .cmov, null, null, null }, 19380 .src_constraints = .{ .{ .int = .dword }, .any }, 19381 .patterns = &.{ 19382 .{ .src = .{ .mem, .none } }, 19383 .{ .src = .{ .to_gpr, .none } }, 19384 }, 19385 .dst_temps = .{.{ .rc = .general_purpose }}, 19386 .clobbers = .{ .eflags = true }, 19387 .each = .{ .once = &.{ 19388 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 19389 .{ ._, ._, .sub, .dst0d, .src0d, ._, ._ }, 19390 .{ ._, ._s, .cmov, .dst0d, .src0d, ._, ._ }, 19391 } }, 19392 }, .{ 19393 .src_constraints = .{ .{ .int = .dword }, .any }, 19394 .patterns = &.{ 19395 .{ .src = .{ .mut_mem, .none } }, 19396 }, 19397 .dst_temps = .{.{ .ref = .src0 }}, 19398 .clobbers = .{ .eflags = true }, 19399 .each = .{ .once = &.{ 19400 .{ ._, ._, .cmp, .src0d, .si(0), ._, ._ }, 19401 .{ ._, ._ge, .j, .@"0f", ._, ._, ._ }, 19402 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 19403 } }, 19404 }, .{ 19405 .src_constraints = .{ .{ .int = .dword }, .any }, 19406 .patterns = &.{ 19407 .{ .src = .{ .to_mut_gpr, .none } }, 19408 }, 19409 .dst_temps = .{.{ .ref = .src0 }}, 19410 .clobbers = .{ .eflags = true }, 19411 .each = .{ .once = &.{ 19412 .{ ._, ._, .@"test", .src0d, .src0d, ._, ._ }, 19413 .{ ._, ._ns, .j, .@"0f", ._, ._, ._ }, 19414 .{ ._, ._, .neg, .dst0d, ._, ._, ._ }, 19415 } }, 19416 }, .{ 19417 .required_features = .{ .@"64bit", .cmov, null, null }, 19418 .src_constraints = .{ .{ .int = .qword }, .any }, 19419 .patterns = &.{ 19420 .{ .src = .{ .mem, .none } }, 19421 .{ .src = .{ .to_gpr, .none } }, 19422 }, 19423 .dst_temps = .{.{ .rc = .general_purpose }}, 19424 .clobbers = .{ .eflags = true }, 19425 .each = .{ .once = &.{ 19426 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 19427 .{ ._, ._, .sub, .dst0q, .src0q, ._, ._ }, 19428 .{ ._, ._s, .cmov, .dst0q, .src0q, ._, ._ }, 19429 } }, 19430 }, .{ 19431 .required_features = .{ .@"64bit", null, null, null }, 19432 .src_constraints = .{ .{ .int = .qword }, .any }, 19433 .patterns = &.{ 19434 .{ .src = .{ .mut_mem, .none } }, 19435 }, 19436 .dst_temps = .{.{ .ref = .src0 }}, 19437 .clobbers = .{ .eflags = true }, 19438 .each = .{ .once = &.{ 19439 .{ ._, ._, .cmp, .src0q, .si(0), ._, ._ }, 19440 .{ ._, ._ge, .j, .@"0f", ._, ._, ._ }, 19441 .{ ._, ._, .neg, .dst0q, ._, ._, ._ }, 19442 } }, 19443 }, .{ 19444 .required_features = .{ .@"64bit", null, null, null }, 19445 .src_constraints = .{ .{ .int = .qword }, .any }, 19446 .patterns = &.{ 19447 .{ .src = .{ .to_mut_gpr, .none } }, 19448 }, 19449 .dst_temps = .{.{ .ref = .src0 }}, 19450 .clobbers = .{ .eflags = true }, 19451 .each = .{ .once = &.{ 19452 .{ ._, ._, .@"test", .src0q, .src0q, ._, ._ }, 19453 .{ ._, ._ns, .j, .@"0f", ._, ._, ._ }, 19454 .{ ._, ._, .neg, .dst0q, ._, ._, ._ }, 19455 } }, 19456 }, .{ 19457 .required_features = .{ .@"64bit", null, null, null }, 19458 .src_constraints = .{ .any_int, .any }, 19459 .patterns = &.{ 19460 .{ .src = .{ .to_mem, .none } }, 19461 }, 19462 .extra_temps = .{ 19463 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19464 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 19465 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 19466 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 19467 .unused, 19468 .unused, 19469 .unused, 19470 .unused, 19471 .unused, 19472 }, 19473 .dst_temps = .{.mem}, 19474 .clobbers = .{ .eflags = true }, 19475 .each = .{ .once = &.{ 19476 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19477 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 19478 .{ ._, ._r, .sa, .tmp1q, .ui(63), ._, ._ }, 19479 .{ ._, ._, .xor, .tmp2d, .tmp2d, ._, ._ }, 19480 .{ .@"0:", ._, .mov, .tmp3q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 19481 .{ ._, ._, .xor, .tmp3q, .tmp1q, ._, ._ }, 19482 .{ ._, ._r, .sh, .tmp2b, .ui(1), ._, ._ }, 19483 .{ ._, ._, .sbb, .tmp3q, .tmp1q, ._, ._ }, 19484 .{ ._, ._c, .set, .tmp2b, ._, ._, ._ }, 19485 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp3q, ._, ._ }, 19486 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 19487 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19488 } }, 19489 }, .{ 19490 .required_features = .{ .mmx, .ssse3, null, null }, 19491 .src_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .byte } }, .any }, 19492 .patterns = &.{ 19493 .{ .src = .{ .mem, .none } }, 19494 .{ .src = .{ .to_mm, .none } }, 19495 }, 19496 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .mmx } }}, 19497 .each = .{ .once = &.{ 19498 .{ ._, .p_b, .abs, .dst0q, .src0q, ._, ._ }, 19499 } }, 19500 }, .{ 19501 .required_features = .{ .mmx, .ssse3, null, null }, 19502 .src_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .word } }, .any }, 19503 .patterns = &.{ 19504 .{ .src = .{ .mem, .none } }, 19505 .{ .src = .{ .to_mm, .none } }, 19506 }, 19507 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .mmx } }}, 19508 .each = .{ .once = &.{ 19509 .{ ._, .p_w, .abs, .dst0q, .src0q, ._, ._ }, 19510 } }, 19511 }, .{ 19512 .required_features = .{ .mmx, .ssse3, null, null }, 19513 .src_constraints = .{ .{ .scalar_int = .{ .of = .qword, .is = .dword } }, .any }, 19514 .patterns = &.{ 19515 .{ .src = .{ .mem, .none } }, 19516 .{ .src = .{ .to_mm, .none } }, 19517 }, 19518 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .mmx } }}, 19519 .each = .{ .once = &.{ 19520 .{ ._, .p_d, .abs, .dst0q, .src0q, ._, ._ }, 19521 } }, 19522 }, .{ 19523 .required_features = .{ .ssse3, null, null, null }, 19524 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .byte } }, .any }, 19525 .patterns = &.{ 19526 .{ .src = .{ .mem, .none } }, 19527 .{ .src = .{ .to_sse, .none } }, 19528 }, 19529 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19530 .each = .{ .once = &.{ 19531 .{ ._, .p_b, .abs, .dst0x, .src0x, ._, ._ }, 19532 } }, 19533 }, .{ 19534 .required_features = .{ .ssse3, null, null, null }, 19535 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .word } }, .any }, 19536 .patterns = &.{ 19537 .{ .src = .{ .mem, .none } }, 19538 .{ .src = .{ .to_sse, .none } }, 19539 }, 19540 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19541 .each = .{ .once = &.{ 19542 .{ ._, .p_w, .abs, .dst0x, .src0x, ._, ._ }, 19543 } }, 19544 }, .{ 19545 .required_features = .{ .ssse3, null, null, null }, 19546 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any }, 19547 .patterns = &.{ 19548 .{ .src = .{ .mem, .none } }, 19549 .{ .src = .{ .to_sse, .none } }, 19550 }, 19551 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19552 .each = .{ .once = &.{ 19553 .{ ._, .p_d, .abs, .dst0x, .src0x, ._, ._ }, 19554 } }, 19555 }, .{ 19556 .required_features = .{ .avx, null, null, null }, 19557 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .byte } }, .any }, 19558 .patterns = &.{ 19559 .{ .src = .{ .mem, .none } }, 19560 .{ .src = .{ .to_sse, .none } }, 19561 }, 19562 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19563 .each = .{ .once = &.{ 19564 .{ ._, .vp_b, .abs, .dst0x, .src0x, ._, ._ }, 19565 } }, 19566 }, .{ 19567 .required_features = .{ .avx, null, null, null }, 19568 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .word } }, .any }, 19569 .patterns = &.{ 19570 .{ .src = .{ .mem, .none } }, 19571 .{ .src = .{ .to_sse, .none } }, 19572 }, 19573 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19574 .each = .{ .once = &.{ 19575 .{ ._, .vp_w, .abs, .dst0x, .src0x, ._, ._ }, 19576 } }, 19577 }, .{ 19578 .required_features = .{ .avx, null, null, null }, 19579 .src_constraints = .{ .{ .scalar_int = .{ .of = .xword, .is = .dword } }, .any }, 19580 .patterns = &.{ 19581 .{ .src = .{ .mem, .none } }, 19582 .{ .src = .{ .to_sse, .none } }, 19583 }, 19584 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19585 .each = .{ .once = &.{ 19586 .{ ._, .vp_d, .abs, .dst0x, .src0x, ._, ._ }, 19587 } }, 19588 }, .{ 19589 .required_features = .{ .avx2, null, null, null }, 19590 .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .byte } }, .any }, 19591 .patterns = &.{ 19592 .{ .src = .{ .mem, .none } }, 19593 .{ .src = .{ .to_sse, .none } }, 19594 }, 19595 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19596 .each = .{ .once = &.{ 19597 .{ ._, .vp_b, .abs, .dst0y, .src0y, ._, ._ }, 19598 } }, 19599 }, .{ 19600 .required_features = .{ .avx2, null, null, null }, 19601 .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .word } }, .any }, 19602 .patterns = &.{ 19603 .{ .src = .{ .mem, .none } }, 19604 .{ .src = .{ .to_sse, .none } }, 19605 }, 19606 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19607 .each = .{ .once = &.{ 19608 .{ ._, .vp_w, .abs, .dst0y, .src0y, ._, ._ }, 19609 } }, 19610 }, .{ 19611 .required_features = .{ .avx2, null, null, null }, 19612 .src_constraints = .{ .{ .scalar_int = .{ .of = .yword, .is = .dword } }, .any }, 19613 .patterns = &.{ 19614 .{ .src = .{ .mem, .none } }, 19615 .{ .src = .{ .to_sse, .none } }, 19616 }, 19617 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 19618 .each = .{ .once = &.{ 19619 .{ ._, .vp_d, .abs, .dst0y, .src0y, ._, ._ }, 19620 } }, 19621 }, .{ 19622 .required_features = .{ .avx2, null, null, null }, 19623 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .yword, .is = .byte } }, .any }, 19624 .patterns = &.{ 19625 .{ .src = .{ .to_mem, .none } }, 19626 }, 19627 .extra_temps = .{ 19628 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19629 .{ .kind = .{ .rc = .sse } }, 19630 .unused, 19631 .unused, 19632 .unused, 19633 .unused, 19634 .unused, 19635 .unused, 19636 .unused, 19637 }, 19638 .dst_temps = .{.mem}, 19639 .clobbers = .{ .eflags = true }, 19640 .each = .{ .once = &.{ 19641 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19642 .{ .@"0:", .vp_b, .abs, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 19643 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 19644 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 19645 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19646 } }, 19647 }, .{ 19648 .required_features = .{ .avx2, null, null, null }, 19649 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .yword, .is = .word } }, .any }, 19650 .patterns = &.{ 19651 .{ .src = .{ .to_mem, .none } }, 19652 }, 19653 .extra_temps = .{ 19654 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19655 .{ .kind = .{ .rc = .sse } }, 19656 .unused, 19657 .unused, 19658 .unused, 19659 .unused, 19660 .unused, 19661 .unused, 19662 .unused, 19663 }, 19664 .dst_temps = .{.mem}, 19665 .clobbers = .{ .eflags = true }, 19666 .each = .{ .once = &.{ 19667 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19668 .{ .@"0:", .vp_w, .abs, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 19669 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 19670 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 19671 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19672 } }, 19673 }, .{ 19674 .required_features = .{ .avx2, null, null, null }, 19675 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .yword, .is = .dword } }, .any }, 19676 .patterns = &.{ 19677 .{ .src = .{ .to_mem, .none } }, 19678 }, 19679 .extra_temps = .{ 19680 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19681 .{ .kind = .{ .rc = .sse } }, 19682 .unused, 19683 .unused, 19684 .unused, 19685 .unused, 19686 .unused, 19687 .unused, 19688 .unused, 19689 }, 19690 .dst_temps = .{.mem}, 19691 .clobbers = .{ .eflags = true }, 19692 .each = .{ .once = &.{ 19693 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19694 .{ .@"0:", .vp_d, .abs, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 19695 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp1y, ._, ._ }, 19696 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 19697 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19698 } }, 19699 }, .{ 19700 .required_features = .{ .avx, null, null, null }, 19701 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .byte } }, .any }, 19702 .patterns = &.{ 19703 .{ .src = .{ .to_mem, .none } }, 19704 }, 19705 .extra_temps = .{ 19706 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19707 .{ .kind = .{ .rc = .sse } }, 19708 .unused, 19709 .unused, 19710 .unused, 19711 .unused, 19712 .unused, 19713 .unused, 19714 .unused, 19715 }, 19716 .dst_temps = .{.mem}, 19717 .clobbers = .{ .eflags = true }, 19718 .each = .{ .once = &.{ 19719 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19720 .{ .@"0:", .vp_b, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19721 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19722 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19723 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19724 } }, 19725 }, .{ 19726 .required_features = .{ .avx, null, null, null }, 19727 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .word } }, .any }, 19728 .patterns = &.{ 19729 .{ .src = .{ .to_mem, .none } }, 19730 }, 19731 .extra_temps = .{ 19732 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19733 .{ .kind = .{ .rc = .sse } }, 19734 .unused, 19735 .unused, 19736 .unused, 19737 .unused, 19738 .unused, 19739 .unused, 19740 .unused, 19741 }, 19742 .dst_temps = .{.mem}, 19743 .clobbers = .{ .eflags = true }, 19744 .each = .{ .once = &.{ 19745 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19746 .{ .@"0:", .vp_w, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19747 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19748 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19749 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19750 } }, 19751 }, .{ 19752 .required_features = .{ .avx, null, null, null }, 19753 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .dword } }, .any }, 19754 .patterns = &.{ 19755 .{ .src = .{ .to_mem, .none } }, 19756 }, 19757 .extra_temps = .{ 19758 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19759 .{ .kind = .{ .rc = .sse } }, 19760 .unused, 19761 .unused, 19762 .unused, 19763 .unused, 19764 .unused, 19765 .unused, 19766 .unused, 19767 }, 19768 .dst_temps = .{.mem}, 19769 .clobbers = .{ .eflags = true }, 19770 .each = .{ .once = &.{ 19771 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19772 .{ .@"0:", .vp_d, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19773 .{ ._, .v_dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19774 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19775 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19776 } }, 19777 }, .{ 19778 .required_features = .{ .ssse3, null, null, null }, 19779 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .byte } }, .any }, 19780 .patterns = &.{ 19781 .{ .src = .{ .to_mem, .none } }, 19782 }, 19783 .extra_temps = .{ 19784 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19785 .{ .kind = .{ .rc = .sse } }, 19786 .unused, 19787 .unused, 19788 .unused, 19789 .unused, 19790 .unused, 19791 .unused, 19792 .unused, 19793 }, 19794 .dst_temps = .{.mem}, 19795 .clobbers = .{ .eflags = true }, 19796 .each = .{ .once = &.{ 19797 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19798 .{ .@"0:", .p_b, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19799 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19800 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19801 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19802 } }, 19803 }, .{ 19804 .required_features = .{ .ssse3, null, null, null }, 19805 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .word } }, .any }, 19806 .patterns = &.{ 19807 .{ .src = .{ .to_mem, .none } }, 19808 }, 19809 .extra_temps = .{ 19810 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19811 .{ .kind = .{ .rc = .sse } }, 19812 .unused, 19813 .unused, 19814 .unused, 19815 .unused, 19816 .unused, 19817 .unused, 19818 .unused, 19819 }, 19820 .dst_temps = .{.mem}, 19821 .clobbers = .{ .eflags = true }, 19822 .each = .{ .once = &.{ 19823 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19824 .{ .@"0:", .p_w, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19825 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19826 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19827 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19828 } }, 19829 }, .{ 19830 .required_features = .{ .ssse3, null, null, null }, 19831 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .xword, .is = .dword } }, .any }, 19832 .patterns = &.{ 19833 .{ .src = .{ .to_mem, .none } }, 19834 }, 19835 .extra_temps = .{ 19836 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19837 .{ .kind = .{ .rc = .sse } }, 19838 .unused, 19839 .unused, 19840 .unused, 19841 .unused, 19842 .unused, 19843 .unused, 19844 .unused, 19845 }, 19846 .dst_temps = .{.mem}, 19847 .clobbers = .{ .eflags = true }, 19848 .each = .{ .once = &.{ 19849 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19850 .{ .@"0:", .p_d, .abs, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 19851 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp1x, ._, ._ }, 19852 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19853 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19854 } }, 19855 }, .{ 19856 .required_features = .{ .mmx, .ssse3, null, null }, 19857 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .qword, .is = .byte } }, .any }, 19858 .patterns = &.{ 19859 .{ .src = .{ .to_mem, .none } }, 19860 }, 19861 .extra_temps = .{ 19862 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19863 .{ .kind = .{ .rc = .sse } }, 19864 .unused, 19865 .unused, 19866 .unused, 19867 .unused, 19868 .unused, 19869 .unused, 19870 .unused, 19871 }, 19872 .dst_temps = .{.mem}, 19873 .clobbers = .{ .eflags = true }, 19874 .each = .{ .once = &.{ 19875 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19876 .{ .@"0:", .p_b, .abs, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 19877 .{ ._, ._q, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 19878 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19879 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19880 } }, 19881 }, .{ 19882 .required_features = .{ .mmx, .ssse3, null, null }, 19883 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .qword, .is = .word } }, .any }, 19884 .patterns = &.{ 19885 .{ .src = .{ .to_mem, .none } }, 19886 }, 19887 .extra_temps = .{ 19888 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19889 .{ .kind = .{ .rc = .sse } }, 19890 .unused, 19891 .unused, 19892 .unused, 19893 .unused, 19894 .unused, 19895 .unused, 19896 .unused, 19897 }, 19898 .dst_temps = .{.mem}, 19899 .clobbers = .{ .eflags = true }, 19900 .each = .{ .once = &.{ 19901 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19902 .{ .@"0:", .p_w, .abs, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 19903 .{ ._, ._q, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 19904 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19905 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19906 } }, 19907 }, .{ 19908 .required_features = .{ .mmx, .ssse3, null, null }, 19909 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .qword, .is = .dword } }, .any }, 19910 .patterns = &.{ 19911 .{ .src = .{ .to_mem, .none } }, 19912 }, 19913 .extra_temps = .{ 19914 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19915 .{ .kind = .{ .rc = .sse } }, 19916 .unused, 19917 .unused, 19918 .unused, 19919 .unused, 19920 .unused, 19921 .unused, 19922 .unused, 19923 }, 19924 .dst_temps = .{.mem}, 19925 .clobbers = .{ .eflags = true }, 19926 .each = .{ .once = &.{ 19927 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19928 .{ .@"0:", .p_d, .abs, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 19929 .{ ._, ._q, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 19930 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 19931 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19932 } }, 19933 }, .{ 19934 .required_features = .{ .cmov, .slow_incdec, null, null }, 19935 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .byte, .is = .byte } }, .any }, 19936 .patterns = &.{ 19937 .{ .src = .{ .to_mem, .none } }, 19938 }, 19939 .extra_temps = .{ 19940 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19941 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 19942 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 19943 .unused, 19944 .unused, 19945 .unused, 19946 .unused, 19947 .unused, 19948 .unused, 19949 }, 19950 .dst_temps = .{.mem}, 19951 .clobbers = .{ .eflags = true }, 19952 .each = .{ .once = &.{ 19953 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19954 .{ .@"0:", ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19955 .{ ._, ._, .movsx, .tmp2d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 19956 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 19957 .{ ._, ._s, .cmov, .tmp1d, .tmp2d, ._, ._ }, 19958 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 19959 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 19960 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 19961 } }, 19962 }, .{ 19963 .required_features = .{ .cmov, null, null, null }, 19964 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .byte, .is = .byte } }, .any }, 19965 .patterns = &.{ 19966 .{ .src = .{ .to_mem, .none } }, 19967 }, 19968 .extra_temps = .{ 19969 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19970 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 19971 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 19972 .unused, 19973 .unused, 19974 .unused, 19975 .unused, 19976 .unused, 19977 .unused, 19978 }, 19979 .dst_temps = .{.mem}, 19980 .clobbers = .{ .eflags = true }, 19981 .each = .{ .once = &.{ 19982 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 19983 .{ .@"0:", ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 19984 .{ ._, ._, .movsx, .tmp2d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 19985 .{ ._, ._, .sub, .tmp1b, .tmp2b, ._, ._ }, 19986 .{ ._, ._s, .cmov, .tmp1d, .tmp2d, ._, ._ }, 19987 .{ ._, ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 19988 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 19989 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 19990 } }, 19991 }, .{ 19992 .required_features = .{ .slow_incdec, null, null, null }, 19993 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .byte, .is = .byte } }, .any }, 19994 .patterns = &.{ 19995 .{ .src = .{ .to_mem, .none } }, 19996 }, 19997 .extra_temps = .{ 19998 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 19999 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 20000 .unused, 20001 .unused, 20002 .unused, 20003 .unused, 20004 .unused, 20005 .unused, 20006 .unused, 20007 }, 20008 .dst_temps = .{.mem}, 20009 .clobbers = .{ .eflags = true }, 20010 .each = .{ .once = &.{ 20011 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20012 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 20013 .{ ._, ._, .@"test", .tmp1b, .tmp1b, ._, ._ }, 20014 .{ ._, ._ns, .j, .@"1f", ._, ._, ._ }, 20015 .{ ._, ._, .neg, .tmp1b, ._, ._, ._ }, 20016 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 20017 .{ ._, ._, .add, .tmp0p, .si(1), ._, ._ }, 20018 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20019 } }, 20020 }, .{ 20021 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .byte, .is = .byte } }, .any }, 20022 .patterns = &.{ 20023 .{ .src = .{ .to_mem, .none } }, 20024 }, 20025 .extra_temps = .{ 20026 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20027 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 20028 .unused, 20029 .unused, 20030 .unused, 20031 .unused, 20032 .unused, 20033 .unused, 20034 .unused, 20035 }, 20036 .dst_temps = .{.mem}, 20037 .clobbers = .{ .eflags = true }, 20038 .each = .{ .once = &.{ 20039 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20040 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0b, .tmp0, .add_size), ._, ._ }, 20041 .{ ._, ._, .@"test", .tmp1b, .tmp1b, ._, ._ }, 20042 .{ ._, ._ns, .j, .@"1f", ._, ._, ._ }, 20043 .{ ._, ._, .neg, .tmp1b, ._, ._, ._ }, 20044 .{ .@"1:", ._, .mov, .memia(.dst0b, .tmp0, .add_size), .tmp1b, ._, ._ }, 20045 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 20046 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 20047 } }, 20048 }, .{ 20049 .required_features = .{ .cmov, null, null, null }, 20050 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .word, .is = .word } }, .any }, 20051 .patterns = &.{ 20052 .{ .src = .{ .to_mem, .none } }, 20053 }, 20054 .extra_temps = .{ 20055 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20056 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 20057 .unused, 20058 .unused, 20059 .unused, 20060 .unused, 20061 .unused, 20062 .unused, 20063 .unused, 20064 }, 20065 .dst_temps = .{.mem}, 20066 .clobbers = .{ .eflags = true }, 20067 .each = .{ .once = &.{ 20068 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20069 .{ .@"0:", ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 20070 .{ ._, ._, .sub, .tmp1w, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 20071 .{ ._, ._s, .cmov, .tmp1w, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 20072 .{ ._, ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 20073 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 20074 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20075 } }, 20076 }, .{ 20077 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .word, .is = .word } }, .any }, 20078 .patterns = &.{ 20079 .{ .src = .{ .to_mem, .none } }, 20080 }, 20081 .extra_temps = .{ 20082 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20083 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 20084 .unused, 20085 .unused, 20086 .unused, 20087 .unused, 20088 .unused, 20089 .unused, 20090 .unused, 20091 }, 20092 .dst_temps = .{.mem}, 20093 .clobbers = .{ .eflags = true }, 20094 .each = .{ .once = &.{ 20095 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20096 .{ .@"0:", ._, .movsx, .tmp1d, .memia(.src0w, .tmp0, .add_size), ._, ._ }, 20097 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 20098 .{ ._, ._ns, .j, .@"1f", ._, ._, ._ }, 20099 .{ ._, ._, .neg, .tmp1d, ._, ._, ._ }, 20100 .{ .@"1:", ._, .mov, .memia(.dst0w, .tmp0, .add_size), .tmp1w, ._, ._ }, 20101 .{ ._, ._, .add, .tmp0p, .si(2), ._, ._ }, 20102 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20103 } }, 20104 }, .{ 20105 .required_features = .{ .cmov, null, null, null }, 20106 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .dword, .is = .dword } }, .any }, 20107 .patterns = &.{ 20108 .{ .src = .{ .to_mem, .none } }, 20109 }, 20110 .extra_temps = .{ 20111 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20112 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 20113 .unused, 20114 .unused, 20115 .unused, 20116 .unused, 20117 .unused, 20118 .unused, 20119 .unused, 20120 }, 20121 .dst_temps = .{.mem}, 20122 .clobbers = .{ .eflags = true }, 20123 .each = .{ .once = &.{ 20124 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20125 .{ .@"0:", ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 20126 .{ ._, ._, .sub, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 20127 .{ ._, ._s, .cmov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 20128 .{ ._, ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 20129 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 20130 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20131 } }, 20132 }, .{ 20133 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .dword, .is = .dword } }, .any }, 20134 .patterns = &.{ 20135 .{ .src = .{ .to_mem, .none } }, 20136 }, 20137 .extra_temps = .{ 20138 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20139 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 20140 .unused, 20141 .unused, 20142 .unused, 20143 .unused, 20144 .unused, 20145 .unused, 20146 .unused, 20147 }, 20148 .dst_temps = .{.mem}, 20149 .clobbers = .{ .eflags = true }, 20150 .each = .{ .once = &.{ 20151 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20152 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 20153 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 20154 .{ ._, ._ns, .j, .@"1f", ._, ._, ._ }, 20155 .{ ._, ._, .neg, .tmp1d, ._, ._, ._ }, 20156 .{ .@"1:", ._, .mov, .memia(.dst0d, .tmp0, .add_size), .tmp1d, ._, ._ }, 20157 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 20158 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20159 } }, 20160 }, .{ 20161 .required_features = .{ .@"64bit", .cmov, null, null }, 20162 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .qword, .is = .qword } }, .any }, 20163 .patterns = &.{ 20164 .{ .src = .{ .to_mem, .none } }, 20165 }, 20166 .extra_temps = .{ 20167 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20168 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 20169 .unused, 20170 .unused, 20171 .unused, 20172 .unused, 20173 .unused, 20174 .unused, 20175 .unused, 20176 }, 20177 .dst_temps = .{.mem}, 20178 .clobbers = .{ .eflags = true }, 20179 .each = .{ .once = &.{ 20180 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20181 .{ .@"0:", ._, .xor, .tmp1d, .tmp1d, ._, ._ }, 20182 .{ ._, ._, .sub, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 20183 .{ ._, ._s, .cmov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 20184 .{ ._, ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 20185 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 20186 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20187 } }, 20188 }, .{ 20189 .required_features = .{ .@"64bit", null, null, null }, 20190 .src_constraints = .{ .{ .multiple_scalar_int = .{ .of = .qword, .is = .qword } }, .any }, 20191 .patterns = &.{ 20192 .{ .src = .{ .to_mem, .none } }, 20193 }, 20194 .extra_temps = .{ 20195 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20196 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 20197 .unused, 20198 .unused, 20199 .unused, 20200 .unused, 20201 .unused, 20202 .unused, 20203 .unused, 20204 }, 20205 .dst_temps = .{.mem}, 20206 .clobbers = .{ .eflags = true }, 20207 .each = .{ .once = &.{ 20208 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20209 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 20210 .{ ._, ._, .@"test", .tmp1q, .tmp1q, ._, ._ }, 20211 .{ ._, ._ns, .j, .@"1f", ._, ._, ._ }, 20212 .{ ._, ._, .neg, .tmp1q, ._, ._, ._ }, 20213 .{ .@"1:", ._, .mov, .memia(.dst0q, .tmp0, .add_size), .tmp1q, ._, ._ }, 20214 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 20215 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20216 } }, 20217 }, .{ 20218 .required_features = .{ .@"64bit", null, null, null }, 20219 .src_constraints = .{ .any_scalar_int, .any }, 20220 .patterns = &.{ 20221 .{ .src = .{ .to_mem, .none } }, 20222 }, 20223 .extra_temps = .{ 20224 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20225 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20226 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 20227 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 20228 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 20229 .unused, 20230 .unused, 20231 .unused, 20232 .unused, 20233 }, 20234 .dst_temps = .{.mem}, 20235 .clobbers = .{ .eflags = true }, 20236 .each = .{ .once = &.{ 20237 .{ ._, ._, .xor, .tmp0d, .tmp0d, ._, ._ }, 20238 .{ .@"0:", ._, .mov, .tmp1d, .sa(.none, .add_src0_elem_size), ._, ._ }, 20239 .{ ._, ._, .mov, .tmp2q, .memiad(.src0q, .tmp0, .add_src0_elem_size, -8), ._, ._ }, 20240 .{ ._, ._r, .sa, .tmp2q, .ui(63), ._, ._ }, 20241 .{ ._, ._, .xor, .tmp3d, .tmp3d, ._, ._ }, 20242 .{ .@"1:", ._, .mov, .tmp4q, .memi(.src0q, .tmp0), ._, ._ }, 20243 .{ ._, ._, .xor, .tmp4q, .tmp2q, ._, ._ }, 20244 .{ ._, ._r, .sh, .tmp3b, .ui(1), ._, ._ }, 20245 .{ ._, ._, .sbb, .tmp4q, .tmp2q, ._, ._ }, 20246 .{ ._, ._c, .set, .tmp3b, ._, ._, ._ }, 20247 .{ ._, ._, .mov, .memi(.dst0q, .tmp0), .tmp4q, ._, ._ }, 20248 .{ ._, ._, .lea, .tmp0d, .lead(.none, .tmp0, 8), ._, ._ }, 20249 .{ ._, ._, .sub, .tmp1d, .si(8), ._, ._ }, 20250 .{ ._, ._a, .j, .@"1b", ._, ._, ._ }, 20251 .{ ._, ._, .cmp, .tmp0d, .sa(.none, .add_src0_unaligned_size), ._, ._ }, 20252 .{ ._, ._b, .j, .@"0b", ._, ._, ._ }, 20253 } }, 20254 }, .{ 20255 .required_features = .{ .avx, null, null, null }, 20256 .src_constraints = .{ .{ .scalar_float = .{ .of = .xword, .is = .dword } }, .any }, 20257 .patterns = &.{ 20258 .{ .src = .{ .to_sse, .none } }, 20259 }, 20260 .extra_temps = .{ 20261 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20262 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20263 .unused, 20264 .unused, 20265 .unused, 20266 .unused, 20267 .unused, 20268 .unused, 20269 .unused, 20270 }, 20271 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20272 .each = .{ .once = &.{ 20273 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20274 .{ ._, .v_ps, .@"and", .dst0x, .src0x, .lea(.xword, .tmp0), ._ }, 20275 } }, 20276 }, .{ 20277 .required_features = .{ .sse, null, null, null }, 20278 .src_constraints = .{ .{ .scalar_float = .{ .of = .xword, .is = .dword } }, .any }, 20279 .patterns = &.{ 20280 .{ .src = .{ .to_mut_sse, .none } }, 20281 }, 20282 .extra_temps = .{ 20283 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20284 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20285 .unused, 20286 .unused, 20287 .unused, 20288 .unused, 20289 .unused, 20290 .unused, 20291 .unused, 20292 }, 20293 .dst_temps = .{.{ .ref = .src0 }}, 20294 .each = .{ .once = &.{ 20295 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20296 .{ ._, ._ps, .@"and", .dst0x, .lea(.xword, .tmp0), ._, ._ }, 20297 } }, 20298 }, .{ 20299 .required_features = .{ .avx, null, null, null }, 20300 .src_constraints = .{ .{ .scalar_float = .{ .of = .yword, .is = .dword } }, .any }, 20301 .patterns = &.{ 20302 .{ .src = .{ .to_sse, .none } }, 20303 }, 20304 .extra_temps = .{ 20305 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20306 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20307 .unused, 20308 .unused, 20309 .unused, 20310 .unused, 20311 .unused, 20312 .unused, 20313 .unused, 20314 }, 20315 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20316 .each = .{ .once = &.{ 20317 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20318 .{ ._, .v_ps, .@"and", .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 20319 } }, 20320 }, .{ 20321 .required_features = .{ .avx, null, null, null }, 20322 .src_constraints = .{ .{ .scalar_float = .{ .of = .xword, .is = .qword } }, .any }, 20323 .patterns = &.{ 20324 .{ .src = .{ .to_sse, .none } }, 20325 }, 20326 .extra_temps = .{ 20327 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20328 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20329 .unused, 20330 .unused, 20331 .unused, 20332 .unused, 20333 .unused, 20334 .unused, 20335 .unused, 20336 }, 20337 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20338 .each = .{ .once = &.{ 20339 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20340 .{ ._, .v_pd, .@"and", .dst0x, .src0x, .lea(.xword, .tmp0), ._ }, 20341 } }, 20342 }, .{ 20343 .required_features = .{ .sse2, null, null, null }, 20344 .src_constraints = .{ .{ .scalar_float = .{ .of = .xword, .is = .qword } }, .any }, 20345 .patterns = &.{ 20346 .{ .src = .{ .to_mut_sse, .none } }, 20347 }, 20348 .extra_temps = .{ 20349 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20350 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20351 .unused, 20352 .unused, 20353 .unused, 20354 .unused, 20355 .unused, 20356 .unused, 20357 .unused, 20358 }, 20359 .dst_temps = .{.{ .ref = .src0 }}, 20360 .each = .{ .once = &.{ 20361 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20362 .{ ._, ._pd, .@"and", .dst0x, .lea(.xword, .tmp0), ._, ._ }, 20363 } }, 20364 }, .{ 20365 .required_features = .{ .avx, null, null, null }, 20366 .src_constraints = .{ .{ .scalar_float = .{ .of = .yword, .is = .qword } }, .any }, 20367 .patterns = &.{ 20368 .{ .src = .{ .to_sse, .none } }, 20369 }, 20370 .extra_temps = .{ 20371 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20372 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20373 .unused, 20374 .unused, 20375 .unused, 20376 .unused, 20377 .unused, 20378 .unused, 20379 .unused, 20380 }, 20381 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20382 .each = .{ .once = &.{ 20383 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20384 .{ ._, .v_pd, .@"and", .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 20385 } }, 20386 }, .{ 20387 .required_features = .{ .x87, null, null, null }, 20388 .src_constraints = .{ .{ .scalar_float = .{ .of = .xword, .is = .tbyte } }, .any }, 20389 .patterns = &.{ 20390 .{ .src = .{ .mem, .none } }, 20391 .{ .src = .{ .to_x87, .none } }, 20392 }, 20393 .extra_temps = .{ 20394 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 20395 .unused, 20396 .unused, 20397 .unused, 20398 .unused, 20399 .unused, 20400 .unused, 20401 .unused, 20402 .unused, 20403 }, 20404 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .x87 } }}, 20405 .each = .{ .once = &.{ 20406 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 20407 .{ ._, .f_, .abs, ._, ._, ._, ._ }, 20408 .{ ._, .f_p, .st, .dst0t, ._, ._, ._ }, 20409 } }, 20410 }, .{ 20411 .required_features = .{ .avx, null, null, null }, 20412 .src_constraints = .{ .{ .scalar_any_float = .xword }, .any }, 20413 .patterns = &.{ 20414 .{ .src = .{ .to_sse, .none } }, 20415 }, 20416 .extra_temps = .{ 20417 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20418 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20419 .unused, 20420 .unused, 20421 .unused, 20422 .unused, 20423 .unused, 20424 .unused, 20425 .unused, 20426 }, 20427 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20428 .each = .{ .once = &.{ 20429 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20430 .{ ._, .vp_, .@"and", .dst0x, .src0x, .lea(.xword, .tmp0), ._ }, 20431 } }, 20432 }, .{ 20433 .required_features = .{ .sse2, null, null, null }, 20434 .src_constraints = .{ .{ .scalar_any_float = .xword }, .any }, 20435 .patterns = &.{ 20436 .{ .src = .{ .to_mut_sse, .none } }, 20437 }, 20438 .extra_temps = .{ 20439 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20440 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20441 .unused, 20442 .unused, 20443 .unused, 20444 .unused, 20445 .unused, 20446 .unused, 20447 .unused, 20448 }, 20449 .dst_temps = .{.{ .ref = .src0 }}, 20450 .each = .{ .once = &.{ 20451 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20452 .{ ._, .p_, .@"and", .dst0x, .lea(.xword, .tmp0), ._, ._ }, 20453 } }, 20454 }, .{ 20455 .required_features = .{ .sse, null, null, null }, 20456 .src_constraints = .{ .{ .scalar_any_float = .xword }, .any }, 20457 .patterns = &.{ 20458 .{ .src = .{ .to_mut_sse, .none } }, 20459 }, 20460 .extra_temps = .{ 20461 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20462 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20463 .unused, 20464 .unused, 20465 .unused, 20466 .unused, 20467 .unused, 20468 .unused, 20469 .unused, 20470 }, 20471 .dst_temps = .{.{ .ref = .src0 }}, 20472 .each = .{ .once = &.{ 20473 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20474 .{ ._, ._ps, .@"and", .dst0x, .lea(.xword, .tmp0), ._, ._ }, 20475 } }, 20476 }, .{ 20477 .required_features = .{ .avx2, null, null, null }, 20478 .src_constraints = .{ .{ .scalar_any_float = .yword }, .any }, 20479 .patterns = &.{ 20480 .{ .src = .{ .to_sse, .none } }, 20481 }, 20482 .extra_temps = .{ 20483 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20484 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20485 .unused, 20486 .unused, 20487 .unused, 20488 .unused, 20489 .unused, 20490 .unused, 20491 .unused, 20492 }, 20493 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20494 .each = .{ .once = &.{ 20495 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20496 .{ ._, .vp_, .@"and", .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 20497 } }, 20498 }, .{ 20499 .required_features = .{ .avx, null, null, null }, 20500 .src_constraints = .{ .{ .scalar_any_float = .yword }, .any }, 20501 .patterns = &.{ 20502 .{ .src = .{ .to_sse, .none } }, 20503 }, 20504 .extra_temps = .{ 20505 .{ .type = .usize, .kind = .{ .rc = .general_purpose } }, 20506 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20507 .unused, 20508 .unused, 20509 .unused, 20510 .unused, 20511 .unused, 20512 .unused, 20513 .unused, 20514 }, 20515 .dst_temps = .{.{ .mut_rc = .{ .ref = .src0, .rc = .sse } }}, 20516 .each = .{ .once = &.{ 20517 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20518 .{ ._, .v_pd, .@"and", .dst0y, .src0y, .lea(.yword, .tmp0), ._ }, 20519 } }, 20520 }, .{ 20521 .required_features = .{ .avx, null, null, null }, 20522 .src_constraints = .{ .{ .multiple_scalar_float = .{ .of = .yword, .is = .dword } }, .any }, 20523 .patterns = &.{ 20524 .{ .src = .{ .to_mem, .none } }, 20525 }, 20526 .extra_temps = .{ 20527 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20528 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20529 .{ .kind = .{ .rc = .sse } }, 20530 .{ .kind = .{ .rc = .sse } }, 20531 .unused, 20532 .unused, 20533 .unused, 20534 .unused, 20535 .unused, 20536 }, 20537 .dst_temps = .{.mem}, 20538 .each = .{ .once = &.{ 20539 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20540 .{ ._, .v_ps, .mova, .tmp2y, .lea(.yword, .tmp0), ._, ._ }, 20541 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20542 .{ .@"0:", .v_ps, .@"and", .tmp3y, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._ }, 20543 .{ ._, .v_ps, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 20544 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 20545 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20546 } }, 20547 }, .{ 20548 .required_features = .{ .sse, null, null, null }, 20549 .src_constraints = .{ .{ .multiple_scalar_float = .{ .of = .xword, .is = .dword } }, .any }, 20550 .patterns = &.{ 20551 .{ .src = .{ .to_mem, .none } }, 20552 }, 20553 .extra_temps = .{ 20554 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20555 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20556 .{ .kind = .{ .rc = .sse } }, 20557 .{ .kind = .{ .rc = .sse } }, 20558 .unused, 20559 .unused, 20560 .unused, 20561 .unused, 20562 .unused, 20563 }, 20564 .dst_temps = .{.mem}, 20565 .each = .{ .once = &.{ 20566 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20567 .{ ._, ._ps, .mova, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 20568 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20569 .{ .@"0:", ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 20570 .{ ._, ._ps, .@"and", .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 20571 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 20572 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 20573 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20574 } }, 20575 }, .{ 20576 .required_features = .{ .avx, null, null, null }, 20577 .src_constraints = .{ .{ .multiple_scalar_float = .{ .of = .yword, .is = .qword } }, .any }, 20578 .patterns = &.{ 20579 .{ .src = .{ .to_mem, .none } }, 20580 }, 20581 .extra_temps = .{ 20582 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20583 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20584 .{ .kind = .{ .rc = .sse } }, 20585 .{ .kind = .{ .rc = .sse } }, 20586 .unused, 20587 .unused, 20588 .unused, 20589 .unused, 20590 .unused, 20591 }, 20592 .dst_temps = .{.mem}, 20593 .each = .{ .once = &.{ 20594 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20595 .{ ._, .v_pd, .mova, .tmp2y, .lea(.yword, .tmp0), ._, ._ }, 20596 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20597 .{ .@"0:", .v_pd, .@"and", .tmp3y, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._ }, 20598 .{ ._, .v_pd, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 20599 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 20600 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20601 } }, 20602 }, .{ 20603 .required_features = .{ .sse2, null, null, null }, 20604 .src_constraints = .{ .{ .multiple_scalar_float = .{ .of = .xword, .is = .qword } }, .any }, 20605 .patterns = &.{ 20606 .{ .src = .{ .to_mem, .none } }, 20607 }, 20608 .extra_temps = .{ 20609 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20610 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20611 .{ .kind = .{ .rc = .sse } }, 20612 .{ .kind = .{ .rc = .sse } }, 20613 .unused, 20614 .unused, 20615 .unused, 20616 .unused, 20617 .unused, 20618 }, 20619 .dst_temps = .{.mem}, 20620 .each = .{ .once = &.{ 20621 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20622 .{ ._, ._pd, .mova, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 20623 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20624 .{ .@"0:", ._pd, .mova, .tmp3x, .tmp2x, ._, ._ }, 20625 .{ ._, ._pd, .@"and", .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 20626 .{ ._, ._pd, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 20627 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 20628 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20629 } }, 20630 }, .{ 20631 .required_features = .{ .avx2, null, null, null }, 20632 .src_constraints = .{ .{ .multiple_scalar_any_float = .yword }, .any }, 20633 .patterns = &.{ 20634 .{ .src = .{ .to_mem, .none } }, 20635 }, 20636 .extra_temps = .{ 20637 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20638 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20639 .{ .kind = .{ .rc = .sse } }, 20640 .{ .kind = .{ .rc = .sse } }, 20641 .unused, 20642 .unused, 20643 .unused, 20644 .unused, 20645 .unused, 20646 }, 20647 .dst_temps = .{.mem}, 20648 .each = .{ .once = &.{ 20649 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20650 .{ ._, .v_dqa, .mov, .tmp2y, .lea(.yword, .tmp0), ._, ._ }, 20651 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20652 .{ .@"0:", .vp_, .@"and", .tmp3y, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._ }, 20653 .{ ._, .v_dqa, .mov, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 20654 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 20655 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20656 } }, 20657 }, .{ 20658 .required_features = .{ .avx, null, null, null }, 20659 .src_constraints = .{ .{ .multiple_scalar_any_float = .yword }, .any }, 20660 .patterns = &.{ 20661 .{ .src = .{ .to_mem, .none } }, 20662 }, 20663 .extra_temps = .{ 20664 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20665 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .yword } } }, 20666 .{ .kind = .{ .rc = .sse } }, 20667 .{ .kind = .{ .rc = .sse } }, 20668 .unused, 20669 .unused, 20670 .unused, 20671 .unused, 20672 .unused, 20673 }, 20674 .dst_temps = .{.mem}, 20675 .each = .{ .once = &.{ 20676 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20677 .{ ._, .v_pd, .mova, .tmp2y, .lea(.yword, .tmp0), ._, ._ }, 20678 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20679 .{ .@"0:", .v_pd, .@"and", .tmp3y, .tmp2y, .memia(.src0y, .tmp0, .add_size), ._ }, 20680 .{ ._, .v_pd, .mova, .memia(.dst0y, .tmp0, .add_size), .tmp3y, ._, ._ }, 20681 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 20682 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20683 } }, 20684 }, .{ 20685 .required_features = .{ .sse2, null, null, null }, 20686 .src_constraints = .{ .{ .multiple_scalar_any_float = .xword }, .any }, 20687 .patterns = &.{ 20688 .{ .src = .{ .to_mem, .none } }, 20689 }, 20690 .extra_temps = .{ 20691 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20692 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20693 .{ .kind = .{ .rc = .sse } }, 20694 .{ .kind = .{ .rc = .sse } }, 20695 .unused, 20696 .unused, 20697 .unused, 20698 .unused, 20699 .unused, 20700 }, 20701 .dst_temps = .{.mem}, 20702 .each = .{ .once = &.{ 20703 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20704 .{ ._, ._dqa, .mov, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 20705 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20706 .{ .@"0:", ._dqa, .mov, .tmp3x, .tmp2x, ._, ._ }, 20707 .{ ._, .p_, .@"and", .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 20708 .{ ._, ._dqa, .mov, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 20709 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 20710 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20711 } }, 20712 }, .{ 20713 .required_features = .{ .sse, null, null, null }, 20714 .src_constraints = .{ .{ .multiple_scalar_any_float = .xword }, .any }, 20715 .patterns = &.{ 20716 .{ .src = .{ .to_mem, .none } }, 20717 }, 20718 .extra_temps = .{ 20719 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 20720 .{ .kind = .{ .smax_mem = .{ .ref = .src0, .vectorize_to = .xword } } }, 20721 .{ .kind = .{ .rc = .sse } }, 20722 .{ .kind = .{ .rc = .sse } }, 20723 .unused, 20724 .unused, 20725 .unused, 20726 .unused, 20727 .unused, 20728 }, 20729 .dst_temps = .{.mem}, 20730 .each = .{ .once = &.{ 20731 .{ ._, ._, .lea, .tmp0p, .mem(.tmp1), ._, ._ }, 20732 .{ ._, ._ps, .mova, .tmp2x, .lea(.xword, .tmp0), ._, ._ }, 20733 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 20734 .{ .@"0:", ._ps, .mova, .tmp3x, .tmp2x, ._, ._ }, 20735 .{ ._, ._ps, .@"and", .tmp3x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 20736 .{ ._, ._ps, .mova, .memia(.dst0x, .tmp0, .add_size), .tmp3x, ._, ._ }, 20737 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 20738 .{ ._, ._nc, .j, .@"0b", ._, ._, ._ }, 20739 } }, 20740 } }) catch |err| switch (err) { 20741 error.SelectFailed => return cg.fail("failed to select {s} {} {}", .{ 20742 @tagName(air_tag), 20743 cg.typeOf(ty_op.operand).fmt(pt), 20744 ops[0].tracking(cg), 20745 }), 20746 else => |e| return e, 20747 }; 20748 try res[0].finish(inst, &.{ty_op.operand}, &ops, cg); 20749 }, 20750 20751 .cmp_lt, 20752 .cmp_lt_optimized, 20753 .cmp_lte, 20754 .cmp_lte_optimized, 20755 .cmp_gte, 20756 .cmp_gte_optimized, 20757 .cmp_gt, 20758 .cmp_gt_optimized, 20759 => |air_tag| if (use_old) try cg.airCmp(inst, air_tag.toCmpOp().?) else { 20760 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 20761 const cmp_op = air_tag.toCmpOp().?; 20762 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 20763 var res: [1]Temp = undefined; 20764 (if (cg.floatBits(cg.typeOf(bin_op.lhs))) |_| err: { 20765 switch (cmp_op) { 20766 else => unreachable, 20767 .lt, .lte => {}, 20768 .gt, .gte => std.mem.swap(Temp, &ops[0], &ops[1]), 20769 } 20770 break :err cg.select(&res, &.{.bool}, &ops, switch (switch (cmp_op) { 20771 else => unreachable, 20772 .lt, .gt => true, 20773 .lte, .gte => false, 20774 }) { 20775 inline false, true => |strict| comptime &.{ .{ 20776 .required_features = .{ .f16c, null, null, null }, 20777 .src_constraints = .{ .{ .float = .word }, .{ .float = .word } }, 20778 .patterns = &.{ 20779 .{ .src = .{ .to_sse, .to_sse }, .commute = .{ 0, 1 } }, 20780 }, 20781 .extra_temps = .{ 20782 .{ .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .sse } } }, 20783 .{ .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 20784 .unused, 20785 .unused, 20786 .unused, 20787 .unused, 20788 .unused, 20789 .unused, 20790 .unused, 20791 }, 20792 .dst_temps = .{.{ .cc = switch (strict) { 20793 true => .a, 20794 false => .ae, 20795 } }}, 20796 .clobbers = .{ .eflags = true }, 20797 .each = .{ .once = &.{ 20798 .{ ._, .v_ps, .cvtph2, .tmp0x, .src0q, ._, ._ }, 20799 .{ ._, .v_ps, .cvtph2, .tmp1x, .src1q, ._, ._ }, 20800 .{ ._, .v_ss, .ucomi, .tmp0x, .tmp1d, ._, ._ }, 20801 } }, 20802 }, .{ 20803 .required_features = .{ .sse, null, null, null }, 20804 .src_constraints = .{ .{ .float = .word }, .{ .float = .word } }, 20805 .patterns = &.{ 20806 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 20807 }, 20808 .call_frame = .{ .alignment = .@"16" }, 20809 .extra_temps = .{ 20810 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, 20811 .{ .type = .i32, .kind = .{ .reg = .eax } }, 20812 .unused, 20813 .unused, 20814 .unused, 20815 .unused, 20816 .unused, 20817 .unused, 20818 .unused, 20819 }, 20820 .dst_temps = .{.{ .cc = switch (strict) { 20821 true => .l, 20822 false => .le, 20823 } }}, 20824 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 20825 .each = .{ .once = &.{ 20826 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 20827 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 20828 } }, 20829 }, .{ 20830 .required_features = .{ .avx, null, null, null }, 20831 .src_constraints = .{ .{ .float = .dword }, .{ .float = .dword } }, 20832 .patterns = &.{ 20833 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 20834 .{ .src = .{ .to_sse, .to_sse }, .commute = .{ 0, 1 } }, 20835 }, 20836 .dst_temps = .{.{ .cc = switch (strict) { 20837 true => .a, 20838 false => .ae, 20839 } }}, 20840 .clobbers = .{ .eflags = true }, 20841 .each = .{ .once = &.{ 20842 .{ ._, .v_ss, .ucomi, .src0x, .src1d, ._, ._ }, 20843 } }, 20844 }, .{ 20845 .required_features = .{ .sse, null, null, null }, 20846 .src_constraints = .{ .{ .float = .dword }, .{ .float = .dword } }, 20847 .patterns = &.{ 20848 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 20849 .{ .src = .{ .to_sse, .to_sse }, .commute = .{ 0, 1 } }, 20850 }, 20851 .dst_temps = .{.{ .cc = switch (strict) { 20852 true => .a, 20853 false => .ae, 20854 } }}, 20855 .clobbers = .{ .eflags = true }, 20856 .each = .{ .once = &.{ 20857 .{ ._, ._ss, .ucomi, .src0x, .src1d, ._, ._ }, 20858 } }, 20859 }, .{ 20860 .required_features = .{ .avx, null, null, null }, 20861 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 20862 .patterns = &.{ 20863 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 20864 .{ .src = .{ .to_sse, .to_sse }, .commute = .{ 0, 1 } }, 20865 }, 20866 .dst_temps = .{.{ .cc = switch (strict) { 20867 true => .a, 20868 false => .ae, 20869 } }}, 20870 .clobbers = .{ .eflags = true }, 20871 .each = .{ .once = &.{ 20872 .{ ._, .v_sd, .ucomi, .src0x, .src1q, ._, ._ }, 20873 } }, 20874 }, .{ 20875 .required_features = .{ .sse2, null, null, null }, 20876 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 20877 .patterns = &.{ 20878 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 20879 .{ .src = .{ .to_sse, .to_sse }, .commute = .{ 0, 1 } }, 20880 }, 20881 .dst_temps = .{.{ .cc = switch (strict) { 20882 true => .a, 20883 false => .ae, 20884 } }}, 20885 .clobbers = .{ .eflags = true }, 20886 .each = .{ .once = &.{ 20887 .{ ._, ._sd, .ucomi, .src0x, .src1q, ._, ._ }, 20888 } }, 20889 }, .{ 20890 .required_features = .{ .x87, .cmov, null, null }, 20891 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 20892 .patterns = &.{ 20893 .{ .src = .{ .to_mem, .to_mem }, .commute = .{ 0, 1 } }, 20894 }, 20895 .extra_temps = .{ 20896 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 20897 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 20898 .unused, 20899 .unused, 20900 .unused, 20901 .unused, 20902 .unused, 20903 .unused, 20904 .unused, 20905 }, 20906 .dst_temps = .{.{ .cc = switch (strict) { 20907 true => .a, 20908 false => .ae, 20909 } }}, 20910 .clobbers = .{ .eflags = true }, 20911 .each = .{ .once = &.{ 20912 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 20913 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 20914 .{ ._, .f_p, .ucomi, .tmp0t, .tmp1t, ._, ._ }, 20915 .{ ._, .f_p, .st, .tmp1t, ._, ._, ._ }, 20916 } }, 20917 }, .{ 20918 .required_features = .{ .sahf, .x87, null, null }, 20919 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 20920 .patterns = &.{ 20921 .{ .src = .{ .to_mem, .to_mem }, .commute = .{ 0, 1 } }, 20922 }, 20923 .extra_temps = .{ 20924 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 20925 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 20926 .{ .type = .u8, .kind = .{ .reg = .ah } }, 20927 .unused, 20928 .unused, 20929 .unused, 20930 .unused, 20931 .unused, 20932 .unused, 20933 }, 20934 .dst_temps = .{.{ .cc = switch (strict) { 20935 true => .a, 20936 false => .ae, 20937 } }}, 20938 .clobbers = .{ .eflags = true }, 20939 .each = .{ .once = &.{ 20940 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 20941 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 20942 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 20943 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 20944 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 20945 } }, 20946 }, .{ 20947 .required_features = .{ .x87, null, null, null }, 20948 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 20949 .patterns = &.{ 20950 .{ .src = .{ .to_mem, .to_mem }, .commute = .{ 0, 1 } }, 20951 }, 20952 .extra_temps = .{ 20953 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 20954 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 20955 .{ .type = .u8, .kind = .{ .reg = .ah } }, 20956 .unused, 20957 .unused, 20958 .unused, 20959 .unused, 20960 .unused, 20961 .unused, 20962 }, 20963 .dst_temps = .{.{ .cc = switch (strict) { 20964 true => .z, 20965 false => .nc, 20966 } }}, 20967 .clobbers = .{ .eflags = true }, 20968 .each = .{ .once = &.{ 20969 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 20970 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 20971 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 20972 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 20973 switch (strict) { 20974 true => .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_001), ._, ._ }, 20975 false => .{ ._, ._r, .sh, .tmp2b, .ui(1), ._, ._ }, 20976 }, 20977 } }, 20978 }, .{ 20979 .required_features = .{ .x87, .cmov, null, null }, 20980 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 20981 .patterns = &.{ 20982 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 20983 .{ .src = .{ .to_x87, .to_x87 }, .commute = .{ 0, 1 } }, 20984 }, 20985 .extra_temps = .{ 20986 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 20987 .unused, 20988 .unused, 20989 .unused, 20990 .unused, 20991 .unused, 20992 .unused, 20993 .unused, 20994 .unused, 20995 }, 20996 .dst_temps = .{.{ .cc = switch (strict) { 20997 true => .a, 20998 false => .ae, 20999 } }}, 21000 .clobbers = .{ .eflags = true }, 21001 .each = .{ .once = &.{ 21002 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21003 .{ ._, .f_p, .ucomi, .tmp0t, .src1t, ._, ._ }, 21004 } }, 21005 }, .{ 21006 .required_features = .{ .sahf, .x87, null, null }, 21007 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21008 .patterns = &.{ 21009 .{ .src = .{ .mem, .mem }, .commute = .{ 0, 1 } }, 21010 }, 21011 .extra_temps = .{ 21012 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21013 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21014 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21015 .unused, 21016 .unused, 21017 .unused, 21018 .unused, 21019 .unused, 21020 .unused, 21021 }, 21022 .dst_temps = .{.{ .cc = switch (strict) { 21023 true => .a, 21024 false => .ae, 21025 } }}, 21026 .clobbers = .{ .eflags = true }, 21027 .each = .{ .once = &.{ 21028 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 21029 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21030 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21031 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21032 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 21033 } }, 21034 }, .{ 21035 .required_features = .{ .sahf, .x87, null, null }, 21036 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21037 .patterns = &.{ 21038 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 21039 .{ .src = .{ .to_x87, .to_x87 }, .commute = .{ 0, 1 } }, 21040 }, 21041 .extra_temps = .{ 21042 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21043 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21044 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21045 .unused, 21046 .unused, 21047 .unused, 21048 .unused, 21049 .unused, 21050 .unused, 21051 }, 21052 .dst_temps = .{.{ .cc = switch (strict) { 21053 true => .a, 21054 false => .ae, 21055 } }}, 21056 .clobbers = .{ .eflags = true }, 21057 .each = .{ .once = &.{ 21058 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21059 .{ ._, .f_p, .ucom, .src1t, ._, ._, ._ }, 21060 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21061 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 21062 } }, 21063 }, .{ 21064 .required_features = .{ .x87, null, null, null }, 21065 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21066 .patterns = &.{ 21067 .{ .src = .{ .mem, .mem }, .commute = .{ 0, 1 } }, 21068 }, 21069 .extra_temps = .{ 21070 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21071 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21072 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21073 .unused, 21074 .unused, 21075 .unused, 21076 .unused, 21077 .unused, 21078 .unused, 21079 }, 21080 .dst_temps = .{.{ .cc = switch (strict) { 21081 true => .z, 21082 false => .nc, 21083 } }}, 21084 .clobbers = .{ .eflags = true }, 21085 .each = .{ .once = &.{ 21086 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 21087 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21088 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21089 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21090 switch (strict) { 21091 true => .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_001), ._, ._ }, 21092 false => .{ ._, ._r, .sh, .tmp2b, .ui(1), ._, ._ }, 21093 }, 21094 } }, 21095 }, .{ 21096 .required_features = .{ .x87, null, null, null }, 21097 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21098 .patterns = &.{ 21099 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 21100 .{ .src = .{ .to_x87, .to_x87 }, .commute = .{ 0, 1 } }, 21101 }, 21102 .extra_temps = .{ 21103 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21104 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21105 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21106 .unused, 21107 .unused, 21108 .unused, 21109 .unused, 21110 .unused, 21111 .unused, 21112 }, 21113 .dst_temps = .{.{ .cc = switch (strict) { 21114 true => .z, 21115 false => .nc, 21116 } }}, 21117 .clobbers = .{ .eflags = true }, 21118 .each = .{ .once = &.{ 21119 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21120 .{ ._, .f_p, .ucom, .src1t, ._, ._, ._ }, 21121 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21122 switch (strict) { 21123 true => .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_001), ._, ._ }, 21124 false => .{ ._, ._r, .sh, .tmp2b, .ui(1), ._, ._ }, 21125 }, 21126 } }, 21127 }, .{ 21128 .required_features = .{ .sse, null, null, null }, 21129 .src_constraints = .{ .{ .float = .xword }, .{ .float = .xword } }, 21130 .patterns = &.{ 21131 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 21132 }, 21133 .call_frame = .{ .alignment = .@"16" }, 21134 .extra_temps = .{ 21135 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, 21136 .{ .type = .i32, .kind = .{ .reg = .eax } }, 21137 .unused, 21138 .unused, 21139 .unused, 21140 .unused, 21141 .unused, 21142 .unused, 21143 .unused, 21144 }, 21145 .dst_temps = .{.{ .cc = switch (strict) { 21146 true => .l, 21147 false => .le, 21148 } }}, 21149 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 21150 .each = .{ .once = &.{ 21151 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 21152 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 21153 } }, 21154 } }, 21155 }); 21156 } else err: { 21157 res[0] = ops[0].cmpInts(cmp_op, &ops[1], cg) catch |err| break :err err; 21158 }) catch |err| switch (err) { 21159 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 21160 @tagName(air_tag), 21161 cg.typeOf(bin_op.lhs).fmt(pt), 21162 ops[0].tracking(cg), 21163 ops[1].tracking(cg), 21164 }), 21165 else => |e| return e, 21166 }; 21167 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 21168 }, 21169 .cmp_eq, 21170 .cmp_eq_optimized, 21171 .cmp_neq, 21172 .cmp_neq_optimized, 21173 => |air_tag| if (use_old) try cg.airCmp(inst, air_tag.toCmpOp().?) else { 21174 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 21175 const cmp_op = air_tag.toCmpOp().?; 21176 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 21177 const ty = cg.typeOf(bin_op.lhs); 21178 var res: [1]Temp = undefined; 21179 const OptInfo = struct { 21180 deaths: [2]Air.Inst.Index, 21181 res: [1]Temp, 21182 state: State, 21183 reloc: Mir.Inst.Index, 21184 }; 21185 var opt_info: ?OptInfo = null; 21186 (err: switch (@as(enum { float, int }, if (cg.floatBits(ty)) |_| 21187 .float 21188 else if (cg.intInfo(ty)) |_| 21189 .int 21190 else category: { 21191 const child_ty = ty.optionalChild(zcu); 21192 const has_value_off: u31 = @intCast(child_ty.abiSize(zcu)); 21193 var has_values: [2]Temp = undefined; 21194 opt_info = @as(OptInfo, undefined); 21195 for (&has_values, &ops, &opt_info.?.deaths) |*has_value, *op, *death| { 21196 has_value.* = try op.read(.bool, .{ .disp = has_value_off }, cg); 21197 const child = try op.read(child_ty, .{}, cg); 21198 try op.die(cg); 21199 op.* = child; 21200 death.* = child.index; 21201 } 21202 cg.select( 21203 &opt_info.?.res, 21204 &.{.bool}, 21205 &has_values, 21206 switch (Condition.fromCompareOperatorUnsigned(cmp_op)) { 21207 else => unreachable, 21208 inline .e, .ne => |cc| comptime &.{.{ 21209 .src_constraints = .{ .{ .size = .byte }, .{ .size = .byte } }, 21210 .patterns = &.{ 21211 .{ .src = .{ .mem, .imm8 } }, 21212 .{ .src = .{ .imm8, .mem }, .commute = .{ 0, 1 } }, 21213 .{ .src = .{ .to_gpr, .imm8 } }, 21214 .{ .src = .{ .imm8, .to_gpr }, .commute = .{ 0, 1 } }, 21215 .{ .src = .{ .mem, .to_gpr } }, 21216 .{ .src = .{ .to_gpr, .mem }, .commute = .{ 0, 1 } }, 21217 .{ .src = .{ .to_gpr, .to_gpr } }, 21218 }, 21219 .dst_temps = .{.{ .rc = .general_purpose }}, 21220 .clobbers = .{ .eflags = true }, 21221 .each = .{ .once = &.{ 21222 .{ ._, ._, .xor, .dst0d, .dst0d, ._, ._ }, 21223 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 21224 .{ ._, .fromCond(cc), .set, .dst0b, ._, ._, ._ }, 21225 .{ ._, ._, .@"test", .src0b, .src1b, ._, ._ }, 21226 } }, 21227 }}, 21228 }, 21229 ) catch |err| switch (err) { 21230 error.SelectFailed => unreachable, 21231 else => |e| return e, 21232 }; 21233 for (has_values) |has_value| for (opt_info.?.res) |opt_res| { 21234 if (has_value.index == opt_res.index) break; 21235 } else try has_value.die(cg); 21236 opt_info.?.state = cg.initRetroactiveState(); 21237 opt_info.?.state.next_temp_index = cg.next_temp_index; 21238 var state = try cg.saveState(); 21239 state.next_temp_index = cg.next_temp_index; 21240 for (ops) |op| try op.die(cg); 21241 try cg.saveRetroactiveState(&opt_info.?.state); 21242 opt_info.?.reloc = try cg.asmJccReloc(.z, undefined); 21243 try cg.restoreState(state, &.{}, .{ 21244 .emit_instructions = false, 21245 .update_tracking = true, 21246 .resurrect = true, 21247 .close_scope = true, 21248 }); 21249 break :category if (cg.floatBits(child_ty)) |_| .float else .int; 21250 })) { 21251 .float => { 21252 cg.select(&res, &.{.bool}, &ops, switch (switch (air_tag) { 21253 else => unreachable, 21254 .cmp_eq, .cmp_neq => false, 21255 .cmp_eq_optimized, .cmp_neq_optimized => true, 21256 }) { 21257 inline false, true => |optimized| comptime &.{ .{ 21258 .required_features = .{ .f16c, null, null, null }, 21259 .src_constraints = .{ .{ .float = .word }, .{ .float = .word } }, 21260 .patterns = &.{ 21261 .{ .src = .{ .to_sse, .to_sse } }, 21262 }, 21263 .extra_temps = .{ 21264 .{ .kind = .{ .mut_rc = .{ .ref = .src0, .rc = .sse } } }, 21265 .{ .kind = .{ .mut_rc = .{ .ref = .src1, .rc = .sse } } }, 21266 .unused, 21267 .unused, 21268 .unused, 21269 .unused, 21270 .unused, 21271 .unused, 21272 .unused, 21273 }, 21274 .dst_temps = .{.{ .cc = switch (optimized) { 21275 false => .z_and_np, 21276 true => .z, 21277 } }}, 21278 .clobbers = .{ .eflags = true }, 21279 .each = .{ .once = &.{ 21280 .{ ._, .v_ps, .cvtph2, .tmp0x, .src0q, ._, ._ }, 21281 .{ ._, .v_ps, .cvtph2, .tmp1x, .src1q, ._, ._ }, 21282 .{ ._, .v_ss, .ucomi, .tmp0x, .tmp1x, ._, ._ }, 21283 } }, 21284 }, .{ 21285 .required_features = .{ .sse, null, null, null }, 21286 .src_constraints = .{ .{ .float = .word }, .{ .float = .word } }, 21287 .patterns = &.{ 21288 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 21289 }, 21290 .call_frame = .{ .alignment = .@"16" }, 21291 .extra_temps = .{ 21292 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmphf2" } } }, 21293 .{ .type = .i32, .kind = .{ .reg = .eax } }, 21294 .unused, 21295 .unused, 21296 .unused, 21297 .unused, 21298 .unused, 21299 .unused, 21300 .unused, 21301 }, 21302 .dst_temps = .{.{ .cc = .z }}, 21303 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 21304 .each = .{ .once = &.{ 21305 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 21306 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 21307 } }, 21308 }, .{ 21309 .required_features = .{ .avx, null, null, null }, 21310 .src_constraints = .{ .{ .float = .dword }, .{ .float = .dword } }, 21311 .patterns = &.{ 21312 .{ .src = .{ .to_sse, .mem } }, 21313 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 21314 .{ .src = .{ .to_sse, .to_sse } }, 21315 }, 21316 .dst_temps = .{.{ .cc = switch (optimized) { 21317 false => .z_and_np, 21318 true => .z, 21319 } }}, 21320 .clobbers = .{ .eflags = true }, 21321 .each = .{ .once = &.{ 21322 .{ ._, .v_ss, .ucomi, .src0x, .src1d, ._, ._ }, 21323 } }, 21324 }, .{ 21325 .required_features = .{ .sse, null, null, null }, 21326 .src_constraints = .{ .{ .float = .dword }, .{ .float = .dword } }, 21327 .patterns = &.{ 21328 .{ .src = .{ .to_sse, .mem } }, 21329 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 21330 .{ .src = .{ .to_sse, .to_sse } }, 21331 }, 21332 .dst_temps = .{.{ .cc = switch (optimized) { 21333 false => .z_and_np, 21334 true => .z, 21335 } }}, 21336 .clobbers = .{ .eflags = true }, 21337 .each = .{ .once = &.{ 21338 .{ ._, ._ss, .ucomi, .src0x, .src1d, ._, ._ }, 21339 } }, 21340 }, .{ 21341 .required_features = .{ .avx, null, null, null }, 21342 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 21343 .patterns = &.{ 21344 .{ .src = .{ .to_sse, .mem } }, 21345 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 21346 .{ .src = .{ .to_sse, .to_sse } }, 21347 }, 21348 .dst_temps = .{.{ .cc = switch (optimized) { 21349 false => .z_and_np, 21350 true => .z, 21351 } }}, 21352 .clobbers = .{ .eflags = true }, 21353 .each = .{ .once = &.{ 21354 .{ ._, .v_sd, .ucomi, .src0x, .src1q, ._, ._ }, 21355 } }, 21356 }, .{ 21357 .required_features = .{ .sse2, null, null, null }, 21358 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 21359 .patterns = &.{ 21360 .{ .src = .{ .to_sse, .mem } }, 21361 .{ .src = .{ .mem, .to_sse }, .commute = .{ 0, 1 } }, 21362 .{ .src = .{ .to_sse, .to_sse } }, 21363 }, 21364 .dst_temps = .{.{ .cc = switch (optimized) { 21365 false => .z_and_np, 21366 true => .z, 21367 } }}, 21368 .clobbers = .{ .eflags = true }, 21369 .each = .{ .once = &.{ 21370 .{ ._, ._sd, .ucomi, .src0x, .src1q, ._, ._ }, 21371 } }, 21372 }, .{ 21373 .required_features = .{ .x87, .cmov, null, null }, 21374 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 21375 .patterns = &.{ 21376 .{ .src = .{ .to_mem, .to_mem }, .commute = .{ 0, 1 } }, 21377 }, 21378 .extra_temps = .{ 21379 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21380 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21381 .unused, 21382 .unused, 21383 .unused, 21384 .unused, 21385 .unused, 21386 .unused, 21387 .unused, 21388 }, 21389 .dst_temps = .{.{ .cc = switch (optimized) { 21390 false => .z_and_np, 21391 true => .z, 21392 } }}, 21393 .clobbers = .{ .eflags = true }, 21394 .each = .{ .once = &.{ 21395 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 21396 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 21397 .{ ._, .f_p, .ucomi, .tmp0t, .tmp1t, ._, ._ }, 21398 .{ ._, .f_p, .st, .tmp1t, ._, ._, ._ }, 21399 } }, 21400 }, .{ 21401 .required_features = .{ .sahf, .x87, null, null }, 21402 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 21403 .patterns = &.{ 21404 .{ .src = .{ .to_mem, .to_mem }, .commute = .{ 0, 1 } }, 21405 }, 21406 .extra_temps = .{ 21407 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21408 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21409 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21410 .unused, 21411 .unused, 21412 .unused, 21413 .unused, 21414 .unused, 21415 .unused, 21416 }, 21417 .dst_temps = .{.{ .cc = switch (optimized) { 21418 false => .z_and_np, 21419 true => .z, 21420 } }}, 21421 .clobbers = .{ .eflags = true }, 21422 .each = .{ .once = &.{ 21423 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 21424 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 21425 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21426 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21427 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 21428 } }, 21429 }, .{ 21430 .required_features = .{ .x87, null, null, null }, 21431 .src_constraints = .{ .{ .float = .qword }, .{ .float = .qword } }, 21432 .patterns = &.{ 21433 .{ .src = .{ .to_mem, .to_mem } }, 21434 }, 21435 .extra_temps = .{ 21436 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21437 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21438 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21439 .unused, 21440 .unused, 21441 .unused, 21442 .unused, 21443 .unused, 21444 .unused, 21445 }, 21446 .dst_temps = .{.{ .cc = switch (optimized) { 21447 false => .z, 21448 true => .nz, 21449 } }}, 21450 .clobbers = .{ .eflags = true }, 21451 .each = .{ .once = switch (optimized) { 21452 false => &.{ 21453 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 21454 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 21455 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21456 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21457 .{ ._, ._, .xor, .tmp2b, .si(0b1_000_000), ._, ._ }, 21458 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_100), ._, ._ }, 21459 }, 21460 true => &.{ 21461 .{ ._, .f_, .ld, .src1q, ._, ._, ._ }, 21462 .{ ._, .f_, .ld, .src0q, ._, ._, ._ }, 21463 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21464 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21465 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_000), ._, ._ }, 21466 }, 21467 } }, 21468 }, .{ 21469 .required_features = .{ .x87, .cmov, null, null }, 21470 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21471 .patterns = &.{ 21472 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 21473 .{ .src = .{ .mem, .to_x87 } }, 21474 .{ .src = .{ .to_x87, .to_x87 } }, 21475 }, 21476 .extra_temps = .{ 21477 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21478 .unused, 21479 .unused, 21480 .unused, 21481 .unused, 21482 .unused, 21483 .unused, 21484 .unused, 21485 .unused, 21486 }, 21487 .dst_temps = .{.{ .cc = switch (optimized) { 21488 false => .z_and_np, 21489 true => .z, 21490 } }}, 21491 .clobbers = .{ .eflags = true }, 21492 .each = .{ .once = &.{ 21493 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21494 .{ ._, .f_p, .ucomi, .tmp0t, .src1t, ._, ._ }, 21495 } }, 21496 }, .{ 21497 .required_features = .{ .sahf, .x87, null, null }, 21498 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21499 .patterns = &.{ 21500 .{ .src = .{ .mem, .mem } }, 21501 }, 21502 .extra_temps = .{ 21503 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21504 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21505 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21506 .unused, 21507 .unused, 21508 .unused, 21509 .unused, 21510 .unused, 21511 .unused, 21512 }, 21513 .dst_temps = .{.{ .cc = switch (optimized) { 21514 false => .z_and_np, 21515 true => .z, 21516 } }}, 21517 .clobbers = .{ .eflags = true }, 21518 .each = .{ .once = &.{ 21519 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 21520 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21521 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21522 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21523 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 21524 } }, 21525 }, .{ 21526 .required_features = .{ .sahf, .x87, null, null }, 21527 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21528 .patterns = &.{ 21529 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 21530 .{ .src = .{ .mem, .to_x87 } }, 21531 .{ .src = .{ .to_x87, .to_x87 } }, 21532 }, 21533 .extra_temps = .{ 21534 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21535 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21536 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21537 .unused, 21538 .unused, 21539 .unused, 21540 .unused, 21541 .unused, 21542 .unused, 21543 }, 21544 .dst_temps = .{.{ .cc = switch (optimized) { 21545 false => .z_and_np, 21546 true => .z, 21547 } }}, 21548 .clobbers = .{ .eflags = true }, 21549 .each = .{ .once = &.{ 21550 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21551 .{ ._, .f_p, .ucom, .src1t, ._, ._, ._ }, 21552 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21553 .{ ._, ._, .sahf, ._, ._, ._, ._ }, 21554 } }, 21555 }, .{ 21556 .required_features = .{ .x87, null, null, null }, 21557 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21558 .patterns = &.{ 21559 .{ .src = .{ .mem, .mem } }, 21560 }, 21561 .extra_temps = .{ 21562 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21563 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21564 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21565 .unused, 21566 .unused, 21567 .unused, 21568 .unused, 21569 .unused, 21570 .unused, 21571 }, 21572 .dst_temps = .{.{ .cc = switch (optimized) { 21573 false => .z, 21574 true => .nz, 21575 } }}, 21576 .clobbers = .{ .eflags = true }, 21577 .each = .{ .once = switch (optimized) { 21578 false => &.{ 21579 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 21580 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21581 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21582 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21583 .{ ._, ._, .xor, .tmp2b, .si(0b1_000_000), ._, ._ }, 21584 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_100), ._, ._ }, 21585 }, 21586 true => &.{ 21587 .{ ._, .f_, .ld, .src1t, ._, ._, ._ }, 21588 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21589 .{ ._, .f_pp, .ucom, ._, ._, ._, ._ }, 21590 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21591 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_000), ._, ._ }, 21592 }, 21593 } }, 21594 }, .{ 21595 .required_features = .{ .x87, null, null, null }, 21596 .src_constraints = .{ .{ .float = .tbyte }, .{ .float = .tbyte } }, 21597 .patterns = &.{ 21598 .{ .src = .{ .to_x87, .mem }, .commute = .{ 0, 1 } }, 21599 .{ .src = .{ .mem, .to_x87 } }, 21600 .{ .src = .{ .to_x87, .to_x87 } }, 21601 }, 21602 .extra_temps = .{ 21603 .{ .type = .f80, .kind = .{ .reg = .st6 } }, 21604 .{ .type = .f80, .kind = .{ .reg = .st7 } }, 21605 .{ .type = .u8, .kind = .{ .reg = .ah } }, 21606 .unused, 21607 .unused, 21608 .unused, 21609 .unused, 21610 .unused, 21611 .unused, 21612 }, 21613 .dst_temps = .{.{ .cc = switch (optimized) { 21614 false => .z, 21615 true => .nz, 21616 } }}, 21617 .clobbers = .{ .eflags = true }, 21618 .each = .{ .once = switch (optimized) { 21619 false => &.{ 21620 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21621 .{ ._, .f_p, .ucom, .src1t, ._, ._, ._ }, 21622 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21623 .{ ._, ._, .xor, .tmp2b, .si(0b1_000_000), ._, ._ }, 21624 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_100), ._, ._ }, 21625 }, 21626 true => &.{ 21627 .{ ._, .f_, .ld, .src0t, ._, ._, ._ }, 21628 .{ ._, .f_p, .ucom, .src1t, ._, ._, ._ }, 21629 .{ ._, .fn_sw, .st, .tmp2w, ._, ._, ._ }, 21630 .{ ._, ._, .@"test", .tmp2b, .si(0b1_000_000), ._, ._ }, 21631 }, 21632 } }, 21633 }, .{ 21634 .required_features = .{ .sse, null, null, null }, 21635 .src_constraints = .{ .{ .float = .xword }, .{ .float = .xword } }, 21636 .patterns = &.{ 21637 .{ .src = .{ .{ .to_reg = .xmm0 }, .{ .to_reg = .xmm1 } } }, 21638 }, 21639 .call_frame = .{ .alignment = .@"16" }, 21640 .extra_temps = .{ 21641 .{ .type = .usize, .kind = .{ .symbol = &.{ .name = "__cmptf2" } } }, 21642 .{ .type = .i32, .kind = .{ .reg = .eax } }, 21643 .unused, 21644 .unused, 21645 .unused, 21646 .unused, 21647 .unused, 21648 .unused, 21649 .unused, 21650 }, 21651 .dst_temps = .{.{ .cc = .z }}, 21652 .clobbers = .{ .eflags = true, .caller_preserved = .ccc }, 21653 .each = .{ .once = &.{ 21654 .{ ._, ._, .call, .tmp0d, ._, ._, ._ }, 21655 .{ ._, ._, .@"test", .tmp1d, .tmp1d, ._, ._ }, 21656 } }, 21657 } }, 21658 }) catch |err| break :err err; 21659 switch (cmp_op) { 21660 else => unreachable, 21661 .eq => {}, 21662 .neq => { 21663 const cc = &res[0].unwrap(cg).temp.tracking(cg).short.eflags; 21664 cc.* = cc.negate(); 21665 }, 21666 } 21667 }, 21668 .int => res[0] = ops[0].cmpInts(cmp_op, &ops[1], cg) catch |err| break :err err, 21669 }) catch |err| switch (err) { 21670 error.SelectFailed => return cg.fail("failed to select {s} {} {} {}", .{ 21671 @tagName(air_tag), 21672 cg.typeOf(bin_op.lhs).fmt(pt), 21673 ops[0].tracking(cg), 21674 ops[1].tracking(cg), 21675 }), 21676 else => |e| return e, 21677 }; 21678 if (opt_info) |*oi| { 21679 for (ops) |op| for (res) |r| { 21680 if (op.index == r.index) break; 21681 } else try op.die(cg); 21682 try cg.genCopy(.bool, oi.res[0].tracking(cg).short, res[0].tracking(cg).short, .{}); 21683 try res[0].die(cg); 21684 res[0] = oi.res[0]; 21685 try cg.restoreState(oi.state, &oi.deaths, .{ 21686 .emit_instructions = true, 21687 .update_tracking = true, 21688 .resurrect = true, 21689 .close_scope = true, 21690 }); 21691 cg.performReloc(oi.reloc); 21692 @memset(&ops, res[0]); 21693 } 21694 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 21695 }, 21696 21697 .cond_br => try cg.airCondBr(inst), 21698 .switch_br => try cg.airSwitchBr(inst), 21699 .loop_switch_br => try cg.airLoopSwitchBr(inst), 21700 .switch_dispatch => try cg.airSwitchDispatch(inst), 21701 .@"try", .try_cold => try cg.airTry(inst), 21702 .try_ptr, .try_ptr_cold => try cg.airTryPtr(inst), 21703 .dbg_stmt => if (cg.debug_output != .none) { 21704 const dbg_stmt = air_datas[@intFromEnum(inst)].dbg_stmt; 21705 _ = try cg.addInst(.{ 21706 .tag = .pseudo, 21707 .ops = .pseudo_dbg_line_stmt_line_column, 21708 .data = .{ .line_column = .{ 21709 .line = dbg_stmt.line, 21710 .column = dbg_stmt.column, 21711 } }, 21712 }); 21713 }, 21714 .dbg_empty_stmt => if (cg.debug_output != .none) { 21715 if (cg.mir_instructions.len > 0) { 21716 const prev_mir_op = &cg.mir_instructions.items(.ops)[cg.mir_instructions.len - 1]; 21717 if (prev_mir_op.* == .pseudo_dbg_line_line_column) 21718 prev_mir_op.* = .pseudo_dbg_line_stmt_line_column; 21719 } 21720 try cg.asmOpOnly(.{ ._, .nop }); 21721 }, 21722 .dbg_inline_block => { 21723 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 21724 const extra = cg.air.extraData(Air.DbgInlineBlock, ty_pl.payload); 21725 const old_inline_func = cg.inline_func; 21726 defer cg.inline_func = old_inline_func; 21727 cg.inline_func = extra.data.func; 21728 if (cg.debug_output != .none) _ = try cg.addInst(.{ 21729 .tag = .pseudo, 21730 .ops = .pseudo_dbg_enter_inline_func, 21731 .data = .{ .func = extra.data.func }, 21732 }); 21733 try cg.lowerBlock(inst, @ptrCast(cg.air.extra[extra.end..][0..extra.data.body_len])); 21734 if (cg.debug_output != .none) _ = try cg.addInst(.{ 21735 .tag = .pseudo, 21736 .ops = .pseudo_dbg_leave_inline_func, 21737 .data = .{ .func = old_inline_func }, 21738 }); 21739 }, 21740 .dbg_var_ptr, 21741 .dbg_var_val, 21742 .dbg_arg_inline, 21743 => if (use_old) try cg.airDbgVar(inst) else if (cg.debug_output != .none) { 21744 const pl_op = air_datas[@intFromEnum(inst)].pl_op; 21745 var ops = try cg.tempsFromOperands(inst, .{pl_op.operand}); 21746 var mcv = ops[0].tracking(cg).short; 21747 switch (mcv) { 21748 else => {}, 21749 .eflags => |cc| switch (cc) { 21750 else => {}, 21751 // These values would self destruct. Maybe we make them use their 21752 // turing complete dwarf expression interpreters for once? 21753 .z_and_np, .nz_or_p => { 21754 try cg.spillEflagsIfOccupied(); 21755 mcv = ops[0].tracking(cg).short; 21756 }, 21757 }, 21758 } 21759 try cg.genLocalDebugInfo(inst, ops[0].tracking(cg).short); 21760 try ops[0].die(cg); 21761 }, 21762 .is_null_ptr => if (use_old) try cg.airIsNullPtr(inst) else { 21763 const un_op = air_datas[@intFromEnum(inst)].un_op; 21764 const opt_ty = cg.typeOf(un_op).childType(zcu); 21765 const opt_repr_is_pl = opt_ty.optionalReprIsPayload(zcu); 21766 const opt_child_ty = opt_ty.optionalChild(zcu); 21767 const opt_child_abi_size: u31 = @intCast(opt_child_ty.abiSize(zcu)); 21768 var ops = try cg.tempsFromOperands(inst, .{un_op}); 21769 if (!opt_repr_is_pl) try ops[0].toOffset(opt_child_abi_size, cg); 21770 while (try ops[0].toLea(cg)) {} 21771 try cg.asmMemoryImmediate( 21772 .{ ._, .cmp }, 21773 try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = if (!opt_repr_is_pl) 21774 .byte 21775 else if (opt_child_ty.isSlice(zcu)) 21776 .qword 21777 else 21778 .fromSize(opt_child_abi_size) }), 21779 .u(0), 21780 ); 21781 const is_null = try cg.tempInit(.bool, .{ .eflags = .e }); 21782 try is_null.finish(inst, &.{un_op}, &ops, cg); 21783 }, 21784 .is_non_null_ptr => if (use_old) try cg.airIsNonNullPtr(inst) else { 21785 const un_op = air_datas[@intFromEnum(inst)].un_op; 21786 const opt_ty = cg.typeOf(un_op).childType(zcu); 21787 const opt_repr_is_pl = opt_ty.optionalReprIsPayload(zcu); 21788 const opt_child_ty = opt_ty.optionalChild(zcu); 21789 const opt_child_abi_size: u31 = @intCast(opt_child_ty.abiSize(zcu)); 21790 var ops = try cg.tempsFromOperands(inst, .{un_op}); 21791 if (!opt_repr_is_pl) try ops[0].toOffset(opt_child_abi_size, cg); 21792 while (try ops[0].toLea(cg)) {} 21793 try cg.asmMemoryImmediate( 21794 .{ ._, .cmp }, 21795 try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = if (!opt_repr_is_pl) 21796 .byte 21797 else if (opt_child_ty.isSlice(zcu)) 21798 .qword 21799 else 21800 .fromSize(opt_child_abi_size) }), 21801 .u(0), 21802 ); 21803 const is_non_null = try cg.tempInit(.bool, .{ .eflags = .ne }); 21804 try is_non_null.finish(inst, &.{un_op}, &ops, cg); 21805 }, 21806 .is_err_ptr => if (use_old) try cg.airIsErrPtr(inst) else { 21807 const un_op = air_datas[@intFromEnum(inst)].un_op; 21808 const eu_ty = cg.typeOf(un_op).childType(zcu); 21809 const eu_err_ty = eu_ty.errorUnionSet(zcu); 21810 const eu_pl_ty = eu_ty.errorUnionPayload(zcu); 21811 const eu_err_off: i32 = @intCast(codegen.errUnionErrorOffset(eu_pl_ty, zcu)); 21812 var ops = try cg.tempsFromOperands(inst, .{un_op}); 21813 try ops[0].toOffset(eu_err_off, cg); 21814 while (try ops[0].toLea(cg)) {} 21815 try cg.asmMemoryImmediate( 21816 .{ ._, .cmp }, 21817 try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(eu_err_ty) }), 21818 .u(0), 21819 ); 21820 const is_err = try cg.tempInit(.bool, .{ .eflags = .ne }); 21821 try is_err.finish(inst, &.{un_op}, &ops, cg); 21822 }, 21823 .is_non_err_ptr => if (use_old) try cg.airIsNonErrPtr(inst) else { 21824 const un_op = air_datas[@intFromEnum(inst)].un_op; 21825 const eu_ty = cg.typeOf(un_op).childType(zcu); 21826 const eu_err_ty = eu_ty.errorUnionSet(zcu); 21827 const eu_pl_ty = eu_ty.errorUnionPayload(zcu); 21828 const eu_err_off: i32 = @intCast(codegen.errUnionErrorOffset(eu_pl_ty, zcu)); 21829 var ops = try cg.tempsFromOperands(inst, .{un_op}); 21830 try ops[0].toOffset(eu_err_off, cg); 21831 while (try ops[0].toLea(cg)) {} 21832 try cg.asmMemoryImmediate( 21833 .{ ._, .cmp }, 21834 try ops[0].tracking(cg).short.deref().mem(cg, .{ .size = cg.memSize(eu_err_ty) }), 21835 .u(0), 21836 ); 21837 const is_non_err = try cg.tempInit(.bool, .{ .eflags = .e }); 21838 try is_non_err.finish(inst, &.{un_op}, &ops, cg); 21839 }, 21840 .load => if (use_old) try cg.airLoad(inst) else fallback: { 21841 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21842 const val_ty = ty_op.ty.toType(); 21843 const ptr_ty = cg.typeOf(ty_op.operand); 21844 const ptr_info = ptr_ty.ptrInfo(zcu); 21845 if (ptr_info.packed_offset.host_size > 0 and 21846 (ptr_info.flags.vector_index == .none or val_ty.toIntern() == .bool_type)) 21847 break :fallback try cg.airLoad(inst); 21848 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21849 const res = try ops[0].load(val_ty, .{ 21850 .disp = switch (ptr_info.flags.vector_index) { 21851 .none => 0, 21852 .runtime => unreachable, 21853 else => |vector_index| @intCast(val_ty.abiSize(zcu) * @intFromEnum(vector_index)), 21854 }, 21855 }, cg); 21856 try res.finish(inst, &.{ty_op.operand}, &ops, cg); 21857 }, 21858 .int_from_ptr => if (use_old) try cg.airIntFromPtr(inst) else { 21859 const un_op = air_datas[@intFromEnum(inst)].un_op; 21860 var ops = try cg.tempsFromOperands(inst, .{un_op}); 21861 try ops[0].toSlicePtr(cg); 21862 try ops[0].finish(inst, &.{un_op}, &ops, cg); 21863 }, 21864 .int_from_bool => if (use_old) try cg.airIntFromBool(inst) else { 21865 const un_op = air_datas[@intFromEnum(inst)].un_op; 21866 const ops = try cg.tempsFromOperands(inst, .{un_op}); 21867 try ops[0].finish(inst, &.{un_op}, &ops, cg); 21868 }, 21869 .ret => try cg.airRet(inst, false), 21870 .ret_safe => try cg.airRet(inst, true), 21871 .ret_load => try cg.airRetLoad(inst), 21872 .store, .store_safe => |air_tag| if (use_old) try cg.airStore(inst, switch (air_tag) { 21873 else => unreachable, 21874 .store => false, 21875 .store_safe => true, 21876 }) else fallback: { 21877 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 21878 const ptr_ty = cg.typeOf(bin_op.lhs); 21879 const ptr_info = ptr_ty.ptrInfo(zcu); 21880 const val_ty = cg.typeOf(bin_op.rhs); 21881 if (ptr_info.packed_offset.host_size > 0 and 21882 (ptr_info.flags.vector_index == .none or val_ty.toIntern() == .bool_type)) 21883 break :fallback try cg.airStore(inst, switch (air_tag) { 21884 else => unreachable, 21885 .store => false, 21886 .store_safe => true, 21887 }); 21888 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 21889 try ops[0].store(&ops[1], .{ 21890 .disp = switch (ptr_info.flags.vector_index) { 21891 .none => 0, 21892 .runtime => unreachable, 21893 else => |vector_index| @intCast(val_ty.abiSize(zcu) * @intFromEnum(vector_index)), 21894 }, 21895 .safe = switch (air_tag) { 21896 else => unreachable, 21897 .store => false, 21898 .store_safe => true, 21899 }, 21900 }, cg); 21901 for (ops) |op| try op.die(cg); 21902 }, 21903 .unreach => {}, 21904 .optional_payload_ptr => if (use_old) try cg.airOptionalPayloadPtr(inst) else { 21905 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21906 const ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21907 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 21908 }, 21909 .optional_payload_ptr_set => if (use_old) try cg.airOptionalPayloadPtrSet(inst) else { 21910 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21911 const opt_ty = cg.typeOf(ty_op.operand).childType(zcu); 21912 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21913 if (!opt_ty.optionalReprIsPayload(zcu)) { 21914 const opt_child_ty = opt_ty.optionalChild(zcu); 21915 const opt_child_abi_size: i32 = @intCast(opt_child_ty.abiSize(zcu)); 21916 try ops[0].toOffset(opt_child_abi_size, cg); 21917 var has_value = try cg.tempInit(.bool, .{ .immediate = 1 }); 21918 try ops[0].store(&has_value, .{}, cg); 21919 try has_value.die(cg); 21920 try ops[0].toOffset(-opt_child_abi_size, cg); 21921 } 21922 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 21923 }, 21924 .unwrap_errunion_payload_ptr => if (use_old) try cg.airUnwrapErrUnionPayloadPtr(inst) else { 21925 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21926 const eu_ty = cg.typeOf(ty_op.operand).childType(zcu); 21927 const eu_pl_ty = eu_ty.errorUnionPayload(zcu); 21928 const eu_pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(eu_pl_ty, zcu)); 21929 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21930 try ops[0].toOffset(eu_pl_off, cg); 21931 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 21932 }, 21933 .unwrap_errunion_err_ptr => if (use_old) try cg.airUnwrapErrUnionErrPtr(inst) else { 21934 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21935 const eu_ty = cg.typeOf(ty_op.operand).childType(zcu); 21936 const eu_pl_ty = eu_ty.errorUnionPayload(zcu); 21937 const eu_err_off: i32 = @intCast(codegen.errUnionErrorOffset(eu_pl_ty, zcu)); 21938 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21939 try ops[0].toOffset(eu_err_off, cg); 21940 const err = try ops[0].load(eu_ty.errorUnionSet(zcu), .{}, cg); 21941 try err.finish(inst, &.{ty_op.operand}, &ops, cg); 21942 }, 21943 .errunion_payload_ptr_set => if (use_old) try cg.airErrUnionPayloadPtrSet(inst) else { 21944 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21945 const eu_ty = cg.typeOf(ty_op.operand).childType(zcu); 21946 const eu_err_ty = eu_ty.errorUnionSet(zcu); 21947 const eu_pl_ty = eu_ty.errorUnionPayload(zcu); 21948 const eu_err_off: i32 = @intCast(codegen.errUnionErrorOffset(eu_pl_ty, zcu)); 21949 const eu_pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(eu_pl_ty, zcu)); 21950 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21951 try ops[0].toOffset(eu_err_off, cg); 21952 var no_err = try cg.tempInit(eu_err_ty, .{ .immediate = 0 }); 21953 try ops[0].store(&no_err, .{}, cg); 21954 try no_err.die(cg); 21955 try ops[0].toOffset(eu_pl_off - eu_err_off, cg); 21956 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 21957 }, 21958 .struct_field_ptr => if (use_old) try cg.airStructFieldPtr(inst) else { 21959 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 21960 const extra = cg.air.extraData(Air.StructField, ty_pl.payload).data; 21961 var ops = try cg.tempsFromOperands(inst, .{extra.struct_operand}); 21962 try ops[0].toOffset(cg.fieldOffset( 21963 cg.typeOf(extra.struct_operand), 21964 ty_pl.ty.toType(), 21965 extra.field_index, 21966 ), cg); 21967 try ops[0].finish(inst, &.{extra.struct_operand}, &ops, cg); 21968 }, 21969 .struct_field_ptr_index_0, 21970 .struct_field_ptr_index_1, 21971 .struct_field_ptr_index_2, 21972 .struct_field_ptr_index_3, 21973 => |air_tag| if (use_old) try cg.airStructFieldPtrIndex(inst, switch (air_tag) { 21974 else => unreachable, 21975 .struct_field_ptr_index_0 => 0, 21976 .struct_field_ptr_index_1 => 1, 21977 .struct_field_ptr_index_2 => 2, 21978 .struct_field_ptr_index_3 => 3, 21979 }) else { 21980 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 21981 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 21982 try ops[0].toOffset(cg.fieldOffset( 21983 cg.typeOf(ty_op.operand), 21984 ty_op.ty.toType(), 21985 switch (air_tag) { 21986 else => unreachable, 21987 .struct_field_ptr_index_0 => 0, 21988 .struct_field_ptr_index_1 => 1, 21989 .struct_field_ptr_index_2 => 2, 21990 .struct_field_ptr_index_3 => 3, 21991 }, 21992 ), cg); 21993 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 21994 }, 21995 .struct_field_val => if (use_old) try cg.airStructFieldVal(inst) else fallback: { 21996 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 21997 const extra = cg.air.extraData(Air.StructField, ty_pl.payload).data; 21998 const agg_ty = cg.typeOf(extra.struct_operand); 21999 const field_ty = ty_pl.ty.toType(); 22000 const field_off: u31 = switch (agg_ty.containerLayout(zcu)) { 22001 .auto, .@"extern" => @intCast(agg_ty.structFieldOffset(extra.field_index, zcu)), 22002 .@"packed" => break :fallback try cg.airStructFieldVal(inst), 22003 }; 22004 var ops = try cg.tempsFromOperands(inst, .{extra.struct_operand}); 22005 // hack around Sema OPV bugs 22006 var res = if (field_ty.hasRuntimeBitsIgnoreComptime(zcu)) 22007 try ops[0].read(field_ty, .{ .disp = field_off }, cg) 22008 else 22009 try cg.tempInit(field_ty, .none); 22010 try res.finish(inst, &.{extra.struct_operand}, &ops, cg); 22011 }, 22012 .set_union_tag => if (use_old) try cg.airSetUnionTag(inst) else { 22013 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 22014 const union_ty = cg.typeOf(bin_op.lhs).childType(zcu); 22015 const union_layout = union_ty.unionGetLayout(zcu); 22016 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 22017 // hack around Sema OPV bugs 22018 if (union_layout.tag_size > 0) try ops[0].store(&ops[1], .{ 22019 .disp = @intCast(union_layout.tagOffset()), 22020 }, cg); 22021 const res = try cg.tempInit(.void, .none); 22022 try res.finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 22023 }, 22024 .get_union_tag => if (use_old) try cg.airGetUnionTag(inst) else { 22025 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22026 const union_ty = cg.typeOf(ty_op.operand); 22027 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22028 const union_layout = union_ty.unionGetLayout(zcu); 22029 assert(union_layout.tag_size > 0); 22030 const res = try ops[0].read(ty_op.ty.toType(), .{ 22031 .disp = @intCast(union_layout.tagOffset()), 22032 }, cg); 22033 try res.finish(inst, &.{ty_op.operand}, &ops, cg); 22034 }, 22035 .slice => if (use_old) try cg.airSlice(inst) else { 22036 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 22037 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; 22038 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 22039 try ops[0].toPair(&ops[1], cg); 22040 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 22041 }, 22042 .slice_len => if (use_old) try cg.airSliceLen(inst) else { 22043 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22044 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22045 try ops[0].toSliceLen(cg); 22046 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22047 }, 22048 .slice_ptr => if (use_old) try cg.airSlicePtr(inst) else { 22049 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22050 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22051 try ops[0].toSlicePtr(cg); 22052 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22053 }, 22054 .ptr_slice_len_ptr => if (use_old) try cg.airPtrSliceLenPtr(inst) else { 22055 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22056 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22057 try ops[0].toOffset(8, cg); 22058 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22059 }, 22060 .ptr_slice_ptr_ptr => if (use_old) try cg.airPtrSlicePtrPtr(inst) else { 22061 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22062 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22063 try ops[0].toOffset(0, cg); 22064 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22065 }, 22066 .slice_elem_val, .ptr_elem_val => |air_tag| if (use_old) switch (air_tag) { 22067 else => unreachable, 22068 .slice_elem_val => try cg.airSliceElemVal(inst), 22069 .ptr_elem_val => try cg.airPtrElemVal(inst), 22070 } else { 22071 const bin_op = air_datas[@intFromEnum(inst)].bin_op; 22072 const res_ty = cg.typeOf(bin_op.lhs).elemType2(zcu); 22073 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 22074 try ops[0].toSlicePtr(cg); 22075 var res: [1]Temp = undefined; 22076 if (res_ty.hasRuntimeBitsIgnoreComptime(zcu)) cg.select(&res, &.{res_ty}, &ops, comptime &.{ .{ 22077 .dst_constraints = .{.{ .int = .byte }}, 22078 .patterns = &.{ 22079 .{ .src = .{ .to_gpr, .simm32 } }, 22080 }, 22081 .dst_temps = .{.{ .rc = .general_purpose }}, 22082 .each = .{ .once = &.{ 22083 .{ ._, ._, .movzx, .dst0d, .leaa(.byte, .src0, .add_src0_elem_size_times_src1), ._, ._ }, 22084 } }, 22085 }, .{ 22086 .dst_constraints = .{.{ .int = .byte }}, 22087 .patterns = &.{ 22088 .{ .src = .{ .to_gpr, .to_gpr } }, 22089 }, 22090 .dst_temps = .{.{ .rc = .general_purpose }}, 22091 .each = .{ .once = &.{ 22092 .{ ._, ._, .movzx, .dst0d, .leai(.byte, .src0, .src1), ._, ._ }, 22093 } }, 22094 }, .{ 22095 .dst_constraints = .{.{ .int = .word }}, 22096 .patterns = &.{ 22097 .{ .src = .{ .to_gpr, .simm32 } }, 22098 }, 22099 .dst_temps = .{.{ .rc = .general_purpose }}, 22100 .each = .{ .once = &.{ 22101 .{ ._, ._, .movzx, .dst0d, .leaa(.word, .src0, .add_src0_elem_size_times_src1), ._, ._ }, 22102 } }, 22103 }, .{ 22104 .dst_constraints = .{.{ .int = .word }}, 22105 .patterns = &.{ 22106 .{ .src = .{ .to_gpr, .to_gpr } }, 22107 }, 22108 .dst_temps = .{.{ .rc = .general_purpose }}, 22109 .each = .{ .once = &.{ 22110 .{ ._, ._, .movzx, .dst0d, .leasi(.word, .src0, .@"2", .src1), ._, ._ }, 22111 } }, 22112 }, .{ 22113 .dst_constraints = .{.{ .int = .dword }}, 22114 .patterns = &.{ 22115 .{ .src = .{ .to_gpr, .simm32 } }, 22116 }, 22117 .dst_temps = .{.{ .rc = .general_purpose }}, 22118 .each = .{ .once = &.{ 22119 .{ ._, ._, .mov, .dst0d, .leaa(.dword, .src0, .add_src0_elem_size_times_src1), ._, ._ }, 22120 } }, 22121 }, .{ 22122 .dst_constraints = .{.{ .int = .dword }}, 22123 .patterns = &.{ 22124 .{ .src = .{ .to_gpr, .to_gpr } }, 22125 }, 22126 .dst_temps = .{.{ .rc = .general_purpose }}, 22127 .each = .{ .once = &.{ 22128 .{ ._, ._, .mov, .dst0d, .leasi(.dword, .src0, .@"4", .src1), ._, ._ }, 22129 } }, 22130 }, .{ 22131 .dst_constraints = .{.{ .int = .qword }}, 22132 .patterns = &.{ 22133 .{ .src = .{ .to_gpr, .simm32 } }, 22134 }, 22135 .dst_temps = .{.{ .rc = .general_purpose }}, 22136 .each = .{ .once = &.{ 22137 .{ ._, ._, .mov, .dst0q, .leaa(.qword, .src0, .add_src0_elem_size_times_src1), ._, ._ }, 22138 } }, 22139 }, .{ 22140 .required_features = .{ .@"64bit", null, null, null }, 22141 .dst_constraints = .{.{ .int = .qword }}, 22142 .patterns = &.{ 22143 .{ .src = .{ .to_gpr, .to_gpr } }, 22144 }, 22145 .dst_temps = .{.{ .rc = .general_purpose }}, 22146 .each = .{ .once = &.{ 22147 .{ ._, ._, .mov, .dst0q, .leasi(.qword, .src0, .@"8", .src1), ._, ._ }, 22148 } }, 22149 } }) catch |err| switch (err) { 22150 error.SelectFailed => { 22151 const elem_size = res_ty.abiSize(zcu); 22152 while (true) for (&ops) |*op| { 22153 if (try op.toRegClass(true, .general_purpose, cg)) break; 22154 } else break; 22155 const lhs_reg = ops[0].unwrap(cg).temp.tracking(cg).short.register.to64(); 22156 const rhs_reg = ops[1].unwrap(cg).temp.tracking(cg).short.register.to64(); 22157 if (!std.math.isPowerOfTwo(elem_size)) { 22158 try cg.spillEflagsIfOccupied(); 22159 try cg.asmRegisterRegisterImmediate( 22160 .{ .i_, .mul }, 22161 rhs_reg, 22162 rhs_reg, 22163 .u(elem_size), 22164 ); 22165 try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22166 .base = .{ .reg = lhs_reg }, 22167 .mod = .{ .rm = .{ .size = .qword, .index = rhs_reg } }, 22168 }); 22169 } else if (elem_size > 8) { 22170 try cg.spillEflagsIfOccupied(); 22171 try cg.asmRegisterImmediate( 22172 .{ ._l, .sh }, 22173 rhs_reg, 22174 .u(std.math.log2_int(u64, elem_size)), 22175 ); 22176 try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22177 .base = .{ .reg = lhs_reg }, 22178 .mod = .{ .rm = .{ .size = .qword, .index = rhs_reg } }, 22179 }); 22180 } else try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22181 .base = .{ .reg = lhs_reg }, 22182 .mod = .{ .rm = .{ 22183 .size = .qword, 22184 .index = rhs_reg, 22185 .scale = .fromFactor(@intCast(elem_size)), 22186 } }, 22187 }); 22188 res[0] = try ops[0].load(res_ty, .{}, cg); 22189 }, 22190 else => |e| return e, 22191 } else { 22192 // hack around Sema OPV bugs 22193 res[0] = try cg.tempInit(res_ty, .none); 22194 } 22195 try res[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 22196 }, 22197 .slice_elem_ptr, .ptr_elem_ptr => |air_tag| if (use_old) switch (air_tag) { 22198 else => unreachable, 22199 .slice_elem_ptr => try cg.airSliceElemPtr(inst), 22200 .ptr_elem_ptr => try cg.airPtrElemPtr(inst), 22201 } else { 22202 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 22203 const bin_op = cg.air.extraData(Air.Bin, ty_pl.payload).data; 22204 var ops = try cg.tempsFromOperands(inst, .{ bin_op.lhs, bin_op.rhs }); 22205 try ops[0].toSlicePtr(cg); 22206 const dst_ty = ty_pl.ty.toType(); 22207 if (dst_ty.ptrInfo(zcu).flags.vector_index == .none) zero_offset: { 22208 const elem_size = dst_ty.childType(zcu).abiSize(zcu); 22209 // hack around Sema OPV bugs 22210 if (elem_size == 0) break :zero_offset; 22211 while (true) for (&ops) |*op| { 22212 if (try op.toRegClass(true, .general_purpose, cg)) break; 22213 } else break; 22214 const lhs_reg = ops[0].unwrap(cg).temp.tracking(cg).short.register.to64(); 22215 const rhs_reg = ops[1].unwrap(cg).temp.tracking(cg).short.register.to64(); 22216 if (!std.math.isPowerOfTwo(elem_size)) { 22217 try cg.spillEflagsIfOccupied(); 22218 try cg.asmRegisterRegisterImmediate( 22219 .{ .i_, .mul }, 22220 rhs_reg, 22221 rhs_reg, 22222 .u(elem_size), 22223 ); 22224 try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22225 .base = .{ .reg = lhs_reg }, 22226 .mod = .{ .rm = .{ .size = .qword, .index = rhs_reg } }, 22227 }); 22228 } else if (elem_size > 8) { 22229 try cg.spillEflagsIfOccupied(); 22230 try cg.asmRegisterImmediate( 22231 .{ ._l, .sh }, 22232 rhs_reg, 22233 .u(std.math.log2_int(u64, elem_size)), 22234 ); 22235 try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22236 .base = .{ .reg = lhs_reg }, 22237 .mod = .{ .rm = .{ .size = .qword, .index = rhs_reg } }, 22238 }); 22239 } else try cg.asmRegisterMemory(.{ ._, .lea }, lhs_reg, .{ 22240 .base = .{ .reg = lhs_reg }, 22241 .mod = .{ .rm = .{ 22242 .size = .qword, 22243 .index = rhs_reg, 22244 .scale = .fromFactor(@intCast(elem_size)), 22245 } }, 22246 }); 22247 } 22248 try ops[0].finish(inst, &.{ bin_op.lhs, bin_op.rhs }, &ops, cg); 22249 }, 22250 .array_to_slice => if (use_old) try cg.airArrayToSlice(inst) else { 22251 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22252 var ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22253 var len = try cg.tempInit(.usize, .{ 22254 .immediate = cg.typeOf(ty_op.operand).childType(zcu).arrayLen(zcu), 22255 }); 22256 try ops[0].toPair(&len, cg); 22257 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22258 }, 22259 .error_set_has_value => return cg.fail("TODO implement error_set_has_value", .{}), 22260 .union_init => if (use_old) try cg.airUnionInit(inst) else { 22261 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 22262 const extra = cg.air.extraData(Air.UnionInit, ty_pl.payload).data; 22263 const union_ty = ty_pl.ty.toType(); 22264 var ops = try cg.tempsFromOperands(inst, .{extra.init}); 22265 var res = try cg.tempAllocMem(union_ty); 22266 const union_layout = union_ty.unionGetLayout(zcu); 22267 if (union_layout.tag_size > 0) { 22268 var tag_temp = try cg.tempFromValue(try pt.enumValueFieldIndex( 22269 union_ty.unionTagTypeSafety(zcu).?, 22270 extra.field_index, 22271 )); 22272 try res.write(&tag_temp, .{ 22273 .disp = @intCast(union_layout.tagOffset()), 22274 }, cg); 22275 try tag_temp.die(cg); 22276 } 22277 try res.write(&ops[0], .{ 22278 .disp = @intCast(union_layout.payloadOffset()), 22279 }, cg); 22280 try res.finish(inst, &.{extra.init}, &ops, cg); 22281 }, 22282 .field_parent_ptr => if (use_old) try cg.airFieldParentPtr(inst) else { 22283 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 22284 const extra = cg.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 22285 var ops = try cg.tempsFromOperands(inst, .{extra.field_ptr}); 22286 try ops[0].toOffset(-cg.fieldOffset( 22287 ty_pl.ty.toType(), 22288 cg.typeOf(extra.field_ptr), 22289 extra.field_index, 22290 ), cg); 22291 try ops[0].finish(inst, &.{extra.field_ptr}, &ops, cg); 22292 }, 22293 22294 .is_named_enum_value => return cg.fail("TODO implement is_named_enum_value", .{}), 22295 22296 .wasm_memory_size => unreachable, 22297 .wasm_memory_grow => unreachable, 22298 22299 .err_return_trace => { 22300 const ert: Temp = .{ .index = err_ret_trace_index }; 22301 try ert.finish(inst, &.{}, &.{}, cg); 22302 }, 22303 .set_err_return_trace => { 22304 const un_op = air_datas[@intFromEnum(inst)].un_op; 22305 var ops = try cg.tempsFromOperands(inst, .{un_op}); 22306 switch (ops[0].unwrap(cg)) { 22307 .ref => { 22308 const result = try cg.allocRegOrMem(err_ret_trace_index, true); 22309 try cg.genCopy(.usize, result, ops[0].tracking(cg).short, .{}); 22310 tracking_log.debug("{} => {} (birth)", .{ err_ret_trace_index, result }); 22311 cg.inst_tracking.putAssumeCapacityNoClobber(err_ret_trace_index, .init(result)); 22312 }, 22313 .temp => |temp_index| { 22314 const temp_tracking = temp_index.tracking(cg); 22315 tracking_log.debug("{} => {} (birth)", .{ err_ret_trace_index, temp_tracking.short }); 22316 cg.inst_tracking.putAssumeCapacityNoClobber(err_ret_trace_index, temp_tracking.*); 22317 assert(cg.reuseTemp(err_ret_trace_index, temp_index.toIndex(), temp_tracking)); 22318 }, 22319 .err_ret_trace => unreachable, 22320 } 22321 }, 22322 22323 .addrspace_cast => { 22324 const ty_op = air_datas[@intFromEnum(inst)].ty_op; 22325 const ops = try cg.tempsFromOperands(inst, .{ty_op.operand}); 22326 try ops[0].finish(inst, &.{ty_op.operand}, &ops, cg); 22327 }, 22328 22329 .save_err_return_trace_index => { 22330 const ty_pl = air_datas[@intFromEnum(inst)].ty_pl; 22331 const agg_ty = ty_pl.ty.toType(); 22332 assert(agg_ty.containerLayout(zcu) != .@"packed"); 22333 var ert: Temp = .{ .index = err_ret_trace_index }; 22334 var res = try ert.load(.usize, .{ .disp = @intCast(agg_ty.structFieldOffset(ty_pl.payload, zcu)) }, cg); 22335 try ert.die(cg); 22336 try res.finish(inst, &.{}, &.{}, cg); 22337 }, 22338 22339 .vector_store_elem => return cg.fail("TODO implement vector_store_elem", .{}), 22340 22341 .c_va_arg => try cg.airVaArg(inst), 22342 .c_va_copy => try cg.airVaCopy(inst), 22343 .c_va_end => try cg.airVaEnd(inst), 22344 .c_va_start => try cg.airVaStart(inst), 22345 22346 .work_item_id => unreachable, 22347 .work_group_size => unreachable, 22348 .work_group_id => unreachable, 22349 } 22350 try cg.resetTemps(); 22351 cg.checkInvariantsAfterAirInst(); 22352 } 22353 verbose_tracking_log.debug("{}", .{cg.fmtTracking()}); 22354 } 22355 22356 fn genLazy(self: *CodeGen, lazy_sym: link.File.LazySymbol) InnerError!void { 22357 const pt = self.pt; 22358 const zcu = pt.zcu; 22359 const ip = &zcu.intern_pool; 22360 switch (Type.fromInterned(lazy_sym.ty).zigTypeTag(zcu)) { 22361 .@"enum" => { 22362 const enum_ty: Type = .fromInterned(lazy_sym.ty); 22363 wip_mir_log.debug("{}.@tagName:", .{enum_ty.fmt(pt)}); 22364 22365 const param_regs = abi.getCAbiIntParamRegs(.auto); 22366 const param_locks = self.register_manager.lockRegsAssumeUnused(2, param_regs[0..2].*); 22367 defer for (param_locks) |lock| self.register_manager.unlockReg(lock); 22368 22369 const ret_reg = param_regs[0]; 22370 const enum_mcv = MCValue{ .register = param_regs[1] }; 22371 22372 const data_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 22373 const data_lock = self.register_manager.lockRegAssumeUnused(data_reg); 22374 defer self.register_manager.unlockReg(data_lock); 22375 try self.genLazySymbolRef(.lea, data_reg, .{ .kind = .const_data, .ty = enum_ty.toIntern() }); 22376 22377 var data_off: i32 = 0; 22378 const tag_names = enum_ty.enumFields(zcu); 22379 for (0..enum_ty.enumFieldCount(zcu)) |tag_index| { 22380 var arg_temp = try self.tempInit(enum_ty, enum_mcv); 22381 22382 const tag_name_len = tag_names.get(ip)[tag_index].length(ip); 22383 const tag_val = try pt.enumValueFieldIndex(enum_ty, @intCast(tag_index)); 22384 var tag_temp = try self.tempFromValue(tag_val); 22385 const cc_temp = arg_temp.cmpInts(.neq, &tag_temp, self) catch |err| switch (err) { 22386 error.SelectFailed => unreachable, 22387 else => |e| return e, 22388 }; 22389 try arg_temp.die(self); 22390 try tag_temp.die(self); 22391 const skip_reloc = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined); 22392 try cc_temp.die(self); 22393 try self.resetTemps(); 22394 22395 try self.genSetMem( 22396 .{ .reg = ret_reg }, 22397 0, 22398 .usize, 22399 .{ .register_offset = .{ .reg = data_reg, .off = data_off } }, 22400 .{}, 22401 ); 22402 try self.genSetMem(.{ .reg = ret_reg }, 8, .usize, .{ .immediate = tag_name_len }, .{}); 22403 try self.asmOpOnly(.{ ._, .ret }); 22404 22405 self.performReloc(skip_reloc); 22406 22407 data_off += @intCast(tag_name_len + 1); 22408 } 22409 22410 try self.asmOpOnly(.{ ._2, .ud }); 22411 }, 22412 else => return self.fail( 22413 "TODO implement {s} for {}", 22414 .{ @tagName(lazy_sym.kind), Type.fromInterned(lazy_sym.ty).fmt(pt) }, 22415 ), 22416 } 22417 } 22418 22419 fn getValue(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) !void { 22420 for (value.getRegs()) |reg| try self.register_manager.getReg(reg, inst); 22421 switch (value) { 22422 else => {}, 22423 .eflags, .register_overflow => self.eflags_inst = inst, 22424 } 22425 } 22426 22427 fn getValueIfFree(self: *CodeGen, value: MCValue, inst: ?Air.Inst.Index) void { 22428 for (value.getRegs()) |reg| if (self.register_manager.isRegFree(reg)) 22429 self.register_manager.getRegAssumeFree(reg, inst); 22430 } 22431 22432 fn freeReg(self: *CodeGen, reg: Register) !void { 22433 self.register_manager.freeReg(reg); 22434 if (reg.class() == .x87) try self.asmRegister(.{ .f_, .free }, reg); 22435 } 22436 22437 fn freeValue(self: *CodeGen, value: MCValue) !void { 22438 switch (value) { 22439 .register => |reg| try self.freeReg(reg), 22440 inline .register_pair, 22441 .register_triple, 22442 .register_quadruple, 22443 => |regs| for (regs) |reg| try self.freeReg(reg), 22444 .register_offset, .indirect => |reg_off| try self.freeReg(reg_off.reg), 22445 .register_overflow => |reg_ov| { 22446 try self.freeReg(reg_ov.reg); 22447 self.eflags_inst = null; 22448 }, 22449 .register_mask => |reg_mask| try self.freeReg(reg_mask.reg), 22450 .eflags => self.eflags_inst = null, 22451 else => {}, // TODO process stack allocation death 22452 } 22453 } 22454 22455 fn feed(self: *CodeGen, bt: *Liveness.BigTomb, operand: Air.Inst.Ref) !void { 22456 if (bt.feed()) if (operand.toIndex()) |inst| try self.processDeath(inst); 22457 } 22458 22459 /// Asserts there is already capacity to insert into top branch inst_table. 22460 fn processDeath(self: *CodeGen, inst: Air.Inst.Index) !void { 22461 try self.inst_tracking.getPtr(inst).?.die(self, inst); 22462 } 22463 22464 fn finishAirResult(self: *CodeGen, inst: Air.Inst.Index, result: MCValue) void { 22465 if (self.liveness.isUnused(inst) and self.air.instructions.items(.tag)[@intFromEnum(inst)] != .arg) switch (result) { 22466 .none, .dead, .unreach => {}, 22467 else => unreachable, // Why didn't the result die? 22468 } else { 22469 tracking_log.debug("{} => {} (birth)", .{ inst, result }); 22470 self.inst_tracking.putAssumeCapacityNoClobber(inst, .init(result)); 22471 // In some cases, an operand may be reused as the result. 22472 // If that operand died and was a register, it was freed by 22473 // processDeath, so we have to "re-allocate" the register. 22474 self.getValueIfFree(result, inst); 22475 } 22476 } 22477 22478 fn finishAir( 22479 self: *CodeGen, 22480 inst: Air.Inst.Index, 22481 result: MCValue, 22482 operands: [Liveness.bpi - 1]Air.Inst.Ref, 22483 ) !void { 22484 const tomb_bits = self.liveness.getTombBits(inst); 22485 for (0.., operands) |op_index, op| { 22486 if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue; 22487 if (self.reused_operands.isSet(op_index)) continue; 22488 try self.processDeath(op.toIndexAllowNone() orelse continue); 22489 } 22490 self.finishAirResult(inst, result); 22491 } 22492 22493 const FrameLayout = struct { 22494 stack_mask: u32, 22495 stack_adjust: u32, 22496 save_reg_list: Mir.RegisterList, 22497 }; 22498 22499 fn setFrameLoc( 22500 self: *CodeGen, 22501 frame_index: FrameIndex, 22502 base: Register, 22503 offset: *i32, 22504 comptime aligned: bool, 22505 ) void { 22506 const frame_i = @intFromEnum(frame_index); 22507 if (aligned) { 22508 const alignment = self.frame_allocs.items(.abi_align)[frame_i]; 22509 offset.* = @intCast(alignment.forward(@intCast(offset.*))); 22510 } 22511 self.frame_locs.set(frame_i, .{ .base = base, .disp = offset.* }); 22512 offset.* += self.frame_allocs.items(.abi_size)[frame_i]; 22513 } 22514 22515 fn computeFrameLayout(self: *CodeGen, cc: std.builtin.CallingConvention.Tag) !FrameLayout { 22516 const frame_allocs_len = self.frame_allocs.len; 22517 try self.frame_locs.resize(self.gpa, frame_allocs_len); 22518 const stack_frame_order = try self.gpa.alloc(FrameIndex, frame_allocs_len - FrameIndex.named_count); 22519 defer self.gpa.free(stack_frame_order); 22520 22521 const frame_size = self.frame_allocs.items(.abi_size); 22522 const frame_align = self.frame_allocs.items(.abi_align); 22523 const frame_offset = self.frame_locs.items(.disp); 22524 22525 for (stack_frame_order, FrameIndex.named_count..) |*frame_order, frame_index| 22526 frame_order.* = @enumFromInt(frame_index); 22527 { 22528 const SortContext = struct { 22529 frame_align: @TypeOf(frame_align), 22530 pub fn lessThan(context: @This(), lhs: FrameIndex, rhs: FrameIndex) bool { 22531 return context.frame_align[@intFromEnum(lhs)].compare(.gt, context.frame_align[@intFromEnum(rhs)]); 22532 } 22533 }; 22534 const sort_context = SortContext{ .frame_align = frame_align }; 22535 std.mem.sort(FrameIndex, stack_frame_order, sort_context, SortContext.lessThan); 22536 } 22537 22538 const call_frame_align = frame_align[@intFromEnum(FrameIndex.call_frame)]; 22539 const stack_frame_align = frame_align[@intFromEnum(FrameIndex.stack_frame)]; 22540 const args_frame_align = frame_align[@intFromEnum(FrameIndex.args_frame)]; 22541 const needed_align = call_frame_align.max(stack_frame_align); 22542 const need_align_stack = needed_align.compare(.gt, args_frame_align); 22543 22544 // Create list of registers to save in the prologue. 22545 // TODO handle register classes 22546 var save_reg_list: Mir.RegisterList = .empty; 22547 const callee_preserved_regs = abi.getCalleePreservedRegs(cc); 22548 for (callee_preserved_regs) |reg| { 22549 if (self.register_manager.isRegAllocated(reg)) { 22550 save_reg_list.push(callee_preserved_regs, reg); 22551 } 22552 } 22553 22554 var rbp_offset: i32 = 0; 22555 self.setFrameLoc(.base_ptr, .rbp, &rbp_offset, false); 22556 self.setFrameLoc(.ret_addr, .rbp, &rbp_offset, false); 22557 self.setFrameLoc(.args_frame, .rbp, &rbp_offset, false); 22558 const stack_frame_align_offset = if (need_align_stack) 22559 0 22560 else 22561 save_reg_list.size(self.target) + frame_offset[@intFromEnum(FrameIndex.args_frame)]; 22562 22563 var rsp_offset: i32 = 0; 22564 self.setFrameLoc(.call_frame, .rsp, &rsp_offset, true); 22565 self.setFrameLoc(.stack_frame, .rsp, &rsp_offset, true); 22566 for (stack_frame_order) |frame_index| self.setFrameLoc(frame_index, .rsp, &rsp_offset, true); 22567 rsp_offset += stack_frame_align_offset; 22568 rsp_offset = @intCast(needed_align.forward(@intCast(rsp_offset))); 22569 rsp_offset -= stack_frame_align_offset; 22570 frame_size[@intFromEnum(FrameIndex.call_frame)] = 22571 @intCast(rsp_offset - frame_offset[@intFromEnum(FrameIndex.stack_frame)]); 22572 22573 return .{ 22574 .stack_mask = @as(u32, std.math.maxInt(u32)) << @intCast(if (need_align_stack) @intFromEnum(needed_align) else 0), 22575 .stack_adjust = @intCast(rsp_offset - frame_offset[@intFromEnum(FrameIndex.call_frame)]), 22576 .save_reg_list = save_reg_list, 22577 }; 22578 } 22579 22580 fn getFrameAddrAlignment(self: *CodeGen, frame_addr: bits.FrameAddr) InternPool.Alignment { 22581 const alloc_align = self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_align; 22582 return @enumFromInt(@min(@intFromEnum(alloc_align), @ctz(frame_addr.off))); 22583 } 22584 22585 fn getFrameAddrSize(self: *CodeGen, frame_addr: bits.FrameAddr) u32 { 22586 return self.frame_allocs.get(@intFromEnum(frame_addr.index)).abi_size - @as(u31, @intCast(frame_addr.off)); 22587 } 22588 22589 fn allocFrameIndex(self: *CodeGen, alloc: FrameAlloc) !FrameIndex { 22590 const frame_allocs_slice = self.frame_allocs.slice(); 22591 const frame_size = frame_allocs_slice.items(.abi_size); 22592 const frame_align = frame_allocs_slice.items(.abi_align); 22593 22594 const stack_frame_align = &frame_align[@intFromEnum(FrameIndex.stack_frame)]; 22595 stack_frame_align.* = stack_frame_align.max(alloc.abi_align); 22596 22597 for (self.free_frame_indices.keys(), 0..) |frame_index, free_i| { 22598 const abi_size = frame_size[@intFromEnum(frame_index)]; 22599 if (abi_size != alloc.abi_size) continue; 22600 const abi_align = &frame_align[@intFromEnum(frame_index)]; 22601 abi_align.* = abi_align.max(alloc.abi_align); 22602 22603 _ = self.free_frame_indices.swapRemoveAt(free_i); 22604 return frame_index; 22605 } 22606 const frame_index: FrameIndex = @enumFromInt(self.frame_allocs.len); 22607 try self.frame_allocs.append(self.gpa, alloc); 22608 return frame_index; 22609 } 22610 22611 /// Use a pointer instruction as the basis for allocating stack memory. 22612 fn allocMemPtr(self: *CodeGen, inst: Air.Inst.Index) !FrameIndex { 22613 const pt = self.pt; 22614 const zcu = pt.zcu; 22615 const ptr_ty = self.typeOfIndex(inst); 22616 const val_ty = ptr_ty.childType(zcu); 22617 return self.allocFrameIndex(.init(.{ 22618 .size = std.math.cast(u32, val_ty.abiSize(zcu)) orelse { 22619 return self.fail("type '{}' too big to fit into stack frame", .{val_ty.fmt(pt)}); 22620 }, 22621 .alignment = ptr_ty.ptrAlignment(zcu).max(.@"1"), 22622 })); 22623 } 22624 22625 fn allocRegOrMem(self: *CodeGen, inst: Air.Inst.Index, reg_ok: bool) !MCValue { 22626 return self.allocRegOrMemAdvanced(self.typeOfIndex(inst), inst, reg_ok); 22627 } 22628 22629 fn allocTempRegOrMem(self: *CodeGen, elem_ty: Type, reg_ok: bool) !MCValue { 22630 return self.allocRegOrMemAdvanced(elem_ty, null, reg_ok); 22631 } 22632 22633 fn allocRegOrMemAdvanced(self: *CodeGen, ty: Type, inst: ?Air.Inst.Index, reg_ok: bool) !MCValue { 22634 const pt = self.pt; 22635 const zcu = pt.zcu; 22636 const abi_size = std.math.cast(u32, ty.abiSize(zcu)) orelse { 22637 return self.fail("type '{}' too big to fit into stack frame", .{ty.fmt(pt)}); 22638 }; 22639 22640 if (reg_ok) need_mem: { 22641 if (std.math.isPowerOfTwo(abi_size) and abi_size <= @as(u32, max_abi_size: switch (ty.zigTypeTag(zcu)) { 22642 .float => switch (ty.floatBits(self.target.*)) { 22643 16, 32, 64, 128 => 16, 22644 80 => break :need_mem, 22645 else => unreachable, 22646 }, 22647 .vector => { 22648 const elem_ty = ty.childType(zcu); 22649 break :max_abi_size if (elem_ty.toIntern() == .bool_type) 22650 8 22651 else if (self.floatBits(elem_ty)) |float_bits| switch (float_bits) { 22652 16, 32, 64, 128 => self.vectorSize(.float), 22653 80 => break :need_mem, 22654 else => unreachable, 22655 } else self.vectorSize(.int); 22656 }, 22657 else => 8, 22658 })) { 22659 if (self.register_manager.tryAllocReg(inst, self.regSetForType(ty))) |reg| { 22660 return MCValue{ .register = registerAlias(reg, abi_size) }; 22661 } 22662 } 22663 } 22664 22665 const frame_index = try self.allocFrameIndex(.initSpill(ty, zcu)); 22666 return .{ .load_frame = .{ .index = frame_index } }; 22667 } 22668 22669 fn regClassForType(self: *CodeGen, ty: Type) Register.Class { 22670 const pt = self.pt; 22671 const zcu = pt.zcu; 22672 if (self.floatBits(ty)) |float_bits| return switch (float_bits) { 22673 80 => .x87, 22674 else => .sse, 22675 }; 22676 if (!ty.isVector(zcu)) return .general_purpose; 22677 const elem_ty = ty.childType(zcu); 22678 return if (elem_ty.toIntern() == .bool_type) 22679 .general_purpose 22680 else if (self.floatBits(elem_ty)) |float_bits| 22681 if (float_bits == 80) .x87 else .sse 22682 else if (self.intInfo(elem_ty)) |_| 22683 .sse 22684 else 22685 .general_purpose; 22686 } 22687 22688 fn regSetForRegClass(rc: Register.Class) RegisterManager.RegisterBitSet { 22689 return switch (rc) { 22690 .general_purpose => abi.RegisterClass.gp, 22691 .segment, .ip, .cr, .dr => unreachable, 22692 .x87 => abi.RegisterClass.x87, 22693 .mmx => @panic("TODO"), 22694 .sse => abi.RegisterClass.sse, 22695 }; 22696 } 22697 22698 fn regSetForType(self: *CodeGen, ty: Type) RegisterManager.RegisterBitSet { 22699 return regSetForRegClass(self.regClassForType(ty)); 22700 } 22701 22702 fn vectorSize(cg: *CodeGen, kind: enum { int, float }) u6 { 22703 return if (cg.hasFeature(switch (kind) { 22704 .int => .avx2, 22705 .float => .avx, 22706 })) 32 else if (cg.hasFeature(.sse)) 16 else 8; 22707 } 22708 22709 fn limbType(cg: *CodeGen, ty: Type) Type { 22710 const pt = cg.pt; 22711 const zcu = pt.zcu; 22712 const vector_size = cg.vectorSize(if (ty.isRuntimeFloat()) .float else .int); 22713 const scalar_ty, const scalar_size = scalar: { 22714 const scalar_ty = ty.scalarType(zcu); 22715 const scalar_size = scalar_ty.abiSize(zcu); 22716 if (scalar_size <= vector_size) break :scalar .{ scalar_ty, scalar_size }; 22717 }; 22718 pt.vectorType(.{ 22719 .len = @divExact(vector_size, scalar_size), 22720 .child = scalar_ty.toIntern(), 22721 }); 22722 } 22723 22724 const State = struct { 22725 registers: RegisterManager.TrackedRegisters, 22726 reg_tracking: [RegisterManager.RegisterBitSet.bit_length]InstTracking, 22727 free_registers: RegisterManager.RegisterBitSet, 22728 next_temp_index: Temp.Index, 22729 inst_tracking_len: u32, 22730 scope_generation: u32, 22731 }; 22732 22733 fn initRetroactiveState(self: *CodeGen) State { 22734 const scope_generation = self.scope_generation + 1; 22735 self.scope_generation = scope_generation; 22736 22737 var state: State = undefined; 22738 state.next_temp_index = @enumFromInt(0); 22739 state.inst_tracking_len = @intCast(self.inst_tracking.count()); 22740 state.scope_generation = scope_generation; 22741 return state; 22742 } 22743 22744 fn saveRetroactiveState(self: *CodeGen, state: *State) !void { 22745 try self.spillEflagsIfOccupied(); 22746 const free_registers = self.register_manager.free_registers; 22747 var it = free_registers.iterator(.{ .kind = .unset }); 22748 while (it.next()) |index| { 22749 const tracked_inst = self.register_manager.registers[index]; 22750 state.registers[index] = tracked_inst; 22751 state.reg_tracking[index] = self.inst_tracking.get(tracked_inst).?; 22752 } 22753 state.free_registers = free_registers; 22754 } 22755 22756 fn saveState(self: *CodeGen) !State { 22757 var state = self.initRetroactiveState(); 22758 try self.saveRetroactiveState(&state); 22759 return state; 22760 } 22761 22762 fn restoreState(self: *CodeGen, state: State, deaths: []const Air.Inst.Index, comptime opts: struct { 22763 emit_instructions: bool, 22764 update_tracking: bool, 22765 resurrect: bool, 22766 close_scope: bool, 22767 }) !void { 22768 if (opts.close_scope) { 22769 for ( 22770 self.inst_tracking.keys()[@intFromEnum(state.next_temp_index)..@intFromEnum(self.next_temp_index)], 22771 self.inst_tracking.values()[@intFromEnum(state.next_temp_index)..@intFromEnum(self.next_temp_index)], 22772 ) |inst, *tracking| try tracking.die(self, inst); 22773 self.next_temp_index = state.next_temp_index; 22774 for ( 22775 self.inst_tracking.keys()[state.inst_tracking_len..], 22776 self.inst_tracking.values()[state.inst_tracking_len..], 22777 ) |inst, *tracking| try tracking.die(self, inst); 22778 self.inst_tracking.shrinkRetainingCapacity(state.inst_tracking_len); 22779 } 22780 22781 if (opts.resurrect) { 22782 for ( 22783 self.inst_tracking.keys()[0..@intFromEnum(state.next_temp_index)], 22784 self.inst_tracking.values()[0..@intFromEnum(state.next_temp_index)], 22785 ) |inst, *tracking| try tracking.resurrect(self, inst, state.scope_generation); 22786 for ( 22787 self.inst_tracking.keys()[Temp.Index.max..state.inst_tracking_len], 22788 self.inst_tracking.values()[Temp.Index.max..state.inst_tracking_len], 22789 ) |inst, *tracking| try tracking.resurrect(self, inst, state.scope_generation); 22790 } 22791 for (deaths) |death| try self.processDeath(death); 22792 22793 const ExpectedContents = [@typeInfo(RegisterManager.TrackedRegisters).array.len]RegisterLock; 22794 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 22795 if (opts.update_tracking) 22796 {} else std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 22797 22798 var reg_locks = if (opts.update_tracking) {} else try std.ArrayList(RegisterLock).initCapacity( 22799 stack.get(), 22800 @typeInfo(ExpectedContents).array.len, 22801 ); 22802 defer if (!opts.update_tracking) { 22803 for (reg_locks.items) |lock| self.register_manager.unlockReg(lock); 22804 reg_locks.deinit(); 22805 }; 22806 22807 for ( 22808 0.., 22809 self.register_manager.registers, 22810 state.registers, 22811 state.reg_tracking, 22812 ) |reg_i, current_slot, target_slot, reg_tracking| { 22813 const reg_index: RegisterManager.TrackedIndex = @intCast(reg_i); 22814 const current_maybe_inst = if (self.register_manager.isRegIndexFree(reg_index)) null else current_slot; 22815 const target_maybe_inst = if (state.free_registers.isSet(reg_index)) null else target_slot; 22816 if (std.debug.runtime_safety) if (target_maybe_inst) |target_inst| 22817 assert(self.inst_tracking.getIndex(target_inst).? < state.inst_tracking_len); 22818 if (opts.emit_instructions and current_maybe_inst != target_maybe_inst) { 22819 if (current_maybe_inst) |current_inst| 22820 try self.inst_tracking.getPtr(current_inst).?.spill(self, current_inst); 22821 if (target_maybe_inst) |target_inst| 22822 try self.inst_tracking.getPtr(target_inst).?.materialize(self, target_inst, reg_tracking); 22823 } 22824 if (opts.update_tracking) { 22825 if (current_maybe_inst) |current_inst| { 22826 try self.inst_tracking.getPtr(current_inst).?.trackSpill(self, current_inst); 22827 self.register_manager.freeRegIndex(reg_index); 22828 } 22829 if (target_maybe_inst) |target_inst| { 22830 self.register_manager.getRegIndexAssumeFree(reg_index, target_inst); 22831 self.inst_tracking.getPtr(target_inst).?.trackMaterialize(target_inst, reg_tracking); 22832 } 22833 } else if (target_maybe_inst) |_| 22834 try reg_locks.append(self.register_manager.lockRegIndexAssumeUnused(reg_index)); 22835 } 22836 if (opts.emit_instructions) if (self.eflags_inst) |inst| 22837 try self.inst_tracking.getPtr(inst).?.spill(self, inst); 22838 if (opts.update_tracking) if (self.eflags_inst) |inst| { 22839 self.eflags_inst = null; 22840 try self.inst_tracking.getPtr(inst).?.trackSpill(self, inst); 22841 }; 22842 22843 if (opts.update_tracking and std.debug.runtime_safety) { 22844 assert(self.eflags_inst == null); 22845 assert(self.register_manager.free_registers.eql(state.free_registers)); 22846 var used_reg_it = state.free_registers.iterator(.{ .kind = .unset }); 22847 while (used_reg_it.next()) |index| 22848 assert(self.register_manager.registers[index] == state.registers[index]); 22849 } 22850 } 22851 22852 pub fn spillInstruction(self: *CodeGen, reg: Register, inst: Air.Inst.Index) !void { 22853 const tracking = self.inst_tracking.getPtr(inst) orelse return; 22854 for (tracking.getRegs()) |tracked_reg| { 22855 if (tracked_reg.id() == reg.id()) break; 22856 } else unreachable; // spilled reg not tracked with spilled instruction 22857 try tracking.spill(self, inst); 22858 try tracking.trackSpill(self, inst); 22859 } 22860 22861 pub fn spillEflagsIfOccupied(self: *CodeGen) !void { 22862 if (self.eflags_inst) |inst| { 22863 self.eflags_inst = null; 22864 const tracking = self.inst_tracking.getPtr(inst).?; 22865 assert(tracking.getCondition() != null); 22866 try tracking.spill(self, inst); 22867 try tracking.trackSpill(self, inst); 22868 } 22869 } 22870 22871 pub fn spillCallerPreservedRegs(self: *CodeGen, cc: std.builtin.CallingConvention.Tag, ignore_reg: Register) !void { 22872 switch (cc) { 22873 inline .auto, .x86_64_sysv, .x86_64_win => |tag| inline for (comptime abi.getCallerPreservedRegs(tag)) |reg| 22874 if (reg != ignore_reg) try self.register_manager.getKnownReg(reg, null), 22875 else => unreachable, 22876 } 22877 } 22878 22879 pub fn spillRegisters(self: *CodeGen, comptime registers: []const Register) !void { 22880 inline for (registers) |reg| try self.register_manager.getKnownReg(reg, null); 22881 } 22882 22883 /// Copies a value to a register without tracking the register. The register is not considered 22884 /// allocated. A second call to `copyToTmpRegister` may return the same register. 22885 /// This can have a side effect of spilling instructions to the stack to free up a register. 22886 fn copyToTmpRegister(self: *CodeGen, ty: Type, mcv: MCValue) !Register { 22887 const reg = try self.register_manager.allocReg(null, self.regSetForType(ty)); 22888 try self.genSetReg(reg, ty, mcv, .{}); 22889 return reg; 22890 } 22891 22892 /// Allocates a new register and copies `mcv` into it. 22893 /// `reg_owner` is the instruction that gets associated with the register in the register table. 22894 /// This can have a side effect of spilling instructions to the stack to free up a register. 22895 /// WARNING make sure that the allocated register matches the returned MCValue from an instruction! 22896 fn copyToRegisterWithInstTracking( 22897 self: *CodeGen, 22898 reg_owner: Air.Inst.Index, 22899 ty: Type, 22900 mcv: MCValue, 22901 ) !MCValue { 22902 const reg: Register = try self.register_manager.allocReg(reg_owner, self.regSetForType(ty)); 22903 try self.genSetReg(reg, ty, mcv, .{}); 22904 return MCValue{ .register = reg }; 22905 } 22906 22907 fn airAlloc(self: *CodeGen, inst: Air.Inst.Index) !void { 22908 const result = MCValue{ .lea_frame = .{ .index = try self.allocMemPtr(inst) } }; 22909 return self.finishAir(inst, result, .{ .none, .none, .none }); 22910 } 22911 22912 fn airRetPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 22913 const result: MCValue = switch (self.ret_mcv.long) { 22914 else => unreachable, 22915 .none => .{ .lea_frame = .{ .index = try self.allocMemPtr(inst) } }, 22916 .load_frame => .{ .register_offset = .{ 22917 .reg = (try self.copyToRegisterWithInstTracking( 22918 inst, 22919 self.typeOfIndex(inst), 22920 self.ret_mcv.long, 22921 )).register, 22922 .off = self.ret_mcv.short.indirect.off, 22923 } }, 22924 }; 22925 return self.finishAir(inst, result, .{ .none, .none, .none }); 22926 } 22927 22928 fn airFptrunc(self: *CodeGen, inst: Air.Inst.Index) !void { 22929 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 22930 const dst_ty = self.typeOfIndex(inst); 22931 const dst_bits = dst_ty.floatBits(self.target.*); 22932 const src_ty = self.typeOf(ty_op.operand); 22933 const src_bits = src_ty.floatBits(self.target.*); 22934 22935 const result = result: { 22936 if (switch (dst_bits) { 22937 16 => switch (src_bits) { 22938 32 => !self.hasFeature(.f16c), 22939 64, 80, 128 => true, 22940 else => unreachable, 22941 }, 22942 32 => switch (src_bits) { 22943 64 => false, 22944 80, 128 => true, 22945 else => unreachable, 22946 }, 22947 64 => switch (src_bits) { 22948 80, 128 => true, 22949 else => unreachable, 22950 }, 22951 80 => switch (src_bits) { 22952 128 => true, 22953 else => unreachable, 22954 }, 22955 else => unreachable, 22956 }) { 22957 var callee_buf: ["__trunc?f?f2".len]u8 = undefined; 22958 break :result try self.genCall(.{ .lib = .{ 22959 .return_type = self.floatCompilerRtAbiType(dst_ty, src_ty).toIntern(), 22960 .param_types = &.{self.floatCompilerRtAbiType(src_ty, dst_ty).toIntern()}, 22961 .callee = std.fmt.bufPrint(&callee_buf, "__trunc{c}f{c}f2", .{ 22962 floatCompilerRtAbiName(src_bits), 22963 floatCompilerRtAbiName(dst_bits), 22964 }) catch unreachable, 22965 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}, .{}); 22966 } 22967 22968 const src_mcv = try self.resolveInst(ty_op.operand); 22969 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 22970 src_mcv 22971 else 22972 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); 22973 const dst_reg = dst_mcv.getReg().?.to128(); 22974 const dst_lock = self.register_manager.lockReg(dst_reg); 22975 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 22976 22977 if (dst_bits == 16) { 22978 assert(self.hasFeature(.f16c)); 22979 switch (src_bits) { 22980 32 => { 22981 const mat_src_reg = if (src_mcv.isRegister()) 22982 src_mcv.getReg().? 22983 else 22984 try self.copyToTmpRegister(src_ty, src_mcv); 22985 try self.asmRegisterRegisterImmediate( 22986 .{ .v_, .cvtps2ph }, 22987 dst_reg, 22988 mat_src_reg.to128(), 22989 bits.RoundMode.imm(.{}), 22990 ); 22991 }, 22992 else => unreachable, 22993 } 22994 } else { 22995 assert(src_bits == 64 and dst_bits == 32); 22996 if (self.hasFeature(.avx)) if (src_mcv.isBase()) try self.asmRegisterRegisterMemory( 22997 .{ .v_ss, .cvtsd2 }, 22998 dst_reg, 22999 dst_reg, 23000 try src_mcv.mem(self, .{ .size = .qword }), 23001 ) else try self.asmRegisterRegisterRegister( 23002 .{ .v_ss, .cvtsd2 }, 23003 dst_reg, 23004 dst_reg, 23005 (if (src_mcv.isRegister()) 23006 src_mcv.getReg().? 23007 else 23008 try self.copyToTmpRegister(src_ty, src_mcv)).to128(), 23009 ) else if (src_mcv.isBase()) try self.asmRegisterMemory( 23010 .{ ._ss, .cvtsd2 }, 23011 dst_reg, 23012 try src_mcv.mem(self, .{ .size = .qword }), 23013 ) else try self.asmRegisterRegister( 23014 .{ ._ss, .cvtsd2 }, 23015 dst_reg, 23016 (if (src_mcv.isRegister()) 23017 src_mcv.getReg().? 23018 else 23019 try self.copyToTmpRegister(src_ty, src_mcv)).to128(), 23020 ); 23021 } 23022 break :result dst_mcv; 23023 }; 23024 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 23025 } 23026 23027 fn airFpext(self: *CodeGen, inst: Air.Inst.Index) !void { 23028 const pt = self.pt; 23029 const zcu = pt.zcu; 23030 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 23031 const dst_ty = self.typeOfIndex(inst); 23032 const dst_scalar_ty = dst_ty.scalarType(zcu); 23033 const dst_bits = dst_scalar_ty.floatBits(self.target.*); 23034 const src_ty = self.typeOf(ty_op.operand); 23035 const src_scalar_ty = src_ty.scalarType(zcu); 23036 const src_bits = src_scalar_ty.floatBits(self.target.*); 23037 23038 const result = result: { 23039 if (switch (src_bits) { 23040 16 => switch (dst_bits) { 23041 32, 64 => !self.hasFeature(.f16c), 23042 80, 128 => true, 23043 else => unreachable, 23044 }, 23045 32 => switch (dst_bits) { 23046 64 => false, 23047 80, 128 => true, 23048 else => unreachable, 23049 }, 23050 64 => switch (dst_bits) { 23051 80, 128 => true, 23052 else => unreachable, 23053 }, 23054 80 => switch (dst_bits) { 23055 128 => true, 23056 else => unreachable, 23057 }, 23058 else => unreachable, 23059 }) { 23060 if (dst_ty.isVector(zcu)) break :result null; 23061 var callee_buf: ["__extend?f?f2".len]u8 = undefined; 23062 break :result try self.genCall(.{ .lib = .{ 23063 .return_type = self.floatCompilerRtAbiType(dst_scalar_ty, src_scalar_ty).toIntern(), 23064 .param_types = &.{self.floatCompilerRtAbiType(src_scalar_ty, dst_scalar_ty).toIntern()}, 23065 .callee = std.fmt.bufPrint(&callee_buf, "__extend{c}f{c}f2", .{ 23066 floatCompilerRtAbiName(src_bits), 23067 floatCompilerRtAbiName(dst_bits), 23068 }) catch unreachable, 23069 } }, &.{src_scalar_ty}, &.{.{ .air_ref = ty_op.operand }}, .{}); 23070 } 23071 23072 const src_abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 23073 const src_mcv = try self.resolveInst(ty_op.operand); 23074 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23075 src_mcv 23076 else 23077 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); 23078 const dst_reg = dst_mcv.getReg().?; 23079 const dst_alias = registerAlias(dst_reg, @intCast(@max(dst_ty.abiSize(zcu), 16))); 23080 const dst_lock = self.register_manager.lockReg(dst_reg); 23081 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 23082 23083 const vec_len = if (dst_ty.isVector(zcu)) dst_ty.vectorLen(zcu) else 1; 23084 if (src_bits == 16) { 23085 assert(self.hasFeature(.f16c)); 23086 const mat_src_reg = if (src_mcv.isRegister()) 23087 src_mcv.getReg().? 23088 else 23089 try self.copyToTmpRegister(src_ty, src_mcv); 23090 try self.asmRegisterRegister( 23091 .{ .v_ps, .cvtph2 }, 23092 dst_alias, 23093 registerAlias(mat_src_reg, src_abi_size), 23094 ); 23095 switch (dst_bits) { 23096 32 => {}, 23097 64 => try self.asmRegisterRegisterRegister( 23098 .{ .v_sd, .cvtss2 }, 23099 dst_alias, 23100 dst_alias, 23101 dst_alias, 23102 ), 23103 else => unreachable, 23104 } 23105 } else { 23106 assert(src_bits == 32 and dst_bits == 64); 23107 if (self.hasFeature(.avx)) switch (vec_len) { 23108 1 => if (src_mcv.isBase()) try self.asmRegisterRegisterMemory( 23109 .{ .v_sd, .cvtss2 }, 23110 dst_alias, 23111 dst_alias, 23112 try src_mcv.mem(self, .{ .size = self.memSize(src_ty) }), 23113 ) else try self.asmRegisterRegisterRegister( 23114 .{ .v_sd, .cvtss2 }, 23115 dst_alias, 23116 dst_alias, 23117 registerAlias(if (src_mcv.isRegister()) 23118 src_mcv.getReg().? 23119 else 23120 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), 23121 ), 23122 2...4 => if (src_mcv.isBase()) try self.asmRegisterMemory( 23123 .{ .v_pd, .cvtps2 }, 23124 dst_alias, 23125 try src_mcv.mem(self, .{ .size = self.memSize(src_ty) }), 23126 ) else try self.asmRegisterRegister( 23127 .{ .v_pd, .cvtps2 }, 23128 dst_alias, 23129 registerAlias(if (src_mcv.isRegister()) 23130 src_mcv.getReg().? 23131 else 23132 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), 23133 ), 23134 else => break :result null, 23135 } else if (src_mcv.isBase()) try self.asmRegisterMemory( 23136 switch (vec_len) { 23137 1 => .{ ._sd, .cvtss2 }, 23138 2 => .{ ._pd, .cvtps2 }, 23139 else => break :result null, 23140 }, 23141 dst_alias, 23142 try src_mcv.mem(self, .{ .size = self.memSize(src_ty) }), 23143 ) else try self.asmRegisterRegister( 23144 switch (vec_len) { 23145 1 => .{ ._sd, .cvtss2 }, 23146 2 => .{ ._pd, .cvtps2 }, 23147 else => break :result null, 23148 }, 23149 dst_alias, 23150 registerAlias(if (src_mcv.isRegister()) 23151 src_mcv.getReg().? 23152 else 23153 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), 23154 ); 23155 } 23156 break :result dst_mcv; 23157 } orelse return self.fail("TODO implement airFpext from {} to {}", .{ 23158 src_ty.fmt(pt), dst_ty.fmt(pt), 23159 }); 23160 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 23161 } 23162 23163 fn airIntCast(self: *CodeGen, inst: Air.Inst.Index) !void { 23164 const pt = self.pt; 23165 const zcu = pt.zcu; 23166 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 23167 const src_ty = self.typeOf(ty_op.operand); 23168 const dst_ty = self.typeOfIndex(inst); 23169 23170 const result = @as(?MCValue, result: { 23171 const src_abi_size: u31 = @intCast(src_ty.abiSize(zcu)); 23172 const dst_abi_size: u31 = @intCast(dst_ty.abiSize(zcu)); 23173 23174 const src_int_info = src_ty.intInfo(zcu); 23175 const dst_int_info = dst_ty.intInfo(zcu); 23176 const extend = switch (src_int_info.signedness) { 23177 .signed => dst_int_info, 23178 .unsigned => src_int_info, 23179 }.signedness; 23180 23181 const src_mcv = try self.resolveInst(ty_op.operand); 23182 if (dst_ty.isVector(zcu)) { 23183 const max_abi_size = @max(dst_abi_size, src_abi_size); 23184 const has_avx = self.hasFeature(.avx); 23185 23186 const dst_elem_abi_size = dst_ty.childType(zcu).abiSize(zcu); 23187 const src_elem_abi_size = src_ty.childType(zcu).abiSize(zcu); 23188 switch (std.math.order(dst_elem_abi_size, src_elem_abi_size)) { 23189 .lt => { 23190 if (max_abi_size > self.vectorSize(.int)) break :result null; 23191 const mir_tag: Mir.Inst.FixedTag = switch (dst_elem_abi_size) { 23192 else => break :result null, 23193 1 => switch (src_elem_abi_size) { 23194 else => break :result null, 23195 2 => switch (dst_int_info.signedness) { 23196 .signed => if (has_avx) .{ .vp_b, .ackssw } else .{ .p_b, .ackssw }, 23197 .unsigned => if (has_avx) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw }, 23198 }, 23199 }, 23200 2 => switch (src_elem_abi_size) { 23201 else => break :result null, 23202 4 => switch (dst_int_info.signedness) { 23203 .signed => if (has_avx) .{ .vp_w, .ackssd } else .{ .p_w, .ackssd }, 23204 .unsigned => if (has_avx) 23205 .{ .vp_w, .ackusd } 23206 else if (self.hasFeature(.sse4_1)) 23207 .{ .p_w, .ackusd } 23208 else 23209 break :result null, 23210 }, 23211 }, 23212 }; 23213 23214 const dst_mcv: MCValue = if (src_mcv.isRegister() and 23215 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23216 src_mcv 23217 else if (has_avx and src_mcv.isRegister()) 23218 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 23219 else 23220 try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv); 23221 const dst_reg = dst_mcv.getReg().?; 23222 const dst_alias = registerAlias(dst_reg, dst_abi_size); 23223 23224 if (has_avx) try self.asmRegisterRegisterRegister( 23225 mir_tag, 23226 dst_alias, 23227 registerAlias(if (src_mcv.isRegister()) 23228 src_mcv.getReg().? 23229 else 23230 dst_reg, src_abi_size), 23231 dst_alias, 23232 ) else try self.asmRegisterRegister( 23233 mir_tag, 23234 dst_alias, 23235 dst_alias, 23236 ); 23237 break :result dst_mcv; 23238 }, 23239 .eq => if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23240 break :result src_mcv 23241 else { 23242 const dst_mcv = try self.allocRegOrMem(inst, true); 23243 try self.genCopy(dst_ty, dst_mcv, src_mcv, .{}); 23244 break :result dst_mcv; 23245 }, 23246 .gt => if (self.hasFeature(.sse4_1)) { 23247 if (max_abi_size > self.vectorSize(.int)) break :result null; 23248 const mir_tag: Mir.Inst.FixedTag = .{ switch (dst_elem_abi_size) { 23249 else => break :result null, 23250 2 => if (has_avx) .vp_w else .p_w, 23251 4 => if (has_avx) .vp_d else .p_d, 23252 8 => if (has_avx) .vp_q else .p_q, 23253 }, switch (src_elem_abi_size) { 23254 else => break :result null, 23255 1 => switch (extend) { 23256 .signed => .movsxb, 23257 .unsigned => .movzxb, 23258 }, 23259 2 => switch (extend) { 23260 .signed => .movsxw, 23261 .unsigned => .movzxw, 23262 }, 23263 4 => switch (extend) { 23264 .signed => .movsxd, 23265 .unsigned => .movzxd, 23266 }, 23267 } }; 23268 23269 const dst_mcv: MCValue = if (src_mcv.isRegister() and 23270 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23271 src_mcv 23272 else 23273 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) }; 23274 const dst_reg = dst_mcv.getReg().?; 23275 const dst_alias = registerAlias(dst_reg, dst_abi_size); 23276 23277 if (src_mcv.isBase()) try self.asmRegisterMemory( 23278 mir_tag, 23279 dst_alias, 23280 try src_mcv.mem(self, .{ .size = self.memSize(src_ty) }), 23281 ) else try self.asmRegisterRegister( 23282 mir_tag, 23283 dst_alias, 23284 registerAlias(if (src_mcv.isRegister()) 23285 src_mcv.getReg().? 23286 else 23287 try self.copyToTmpRegister(src_ty, src_mcv), src_abi_size), 23288 ); 23289 break :result dst_mcv; 23290 } else { 23291 const mir_tag: Mir.Inst.FixedTag = switch (dst_elem_abi_size) { 23292 else => break :result null, 23293 2 => switch (src_elem_abi_size) { 23294 else => break :result null, 23295 1 => .{ .p_, .unpcklbw }, 23296 }, 23297 4 => switch (src_elem_abi_size) { 23298 else => break :result null, 23299 2 => .{ .p_, .unpcklwd }, 23300 }, 23301 8 => switch (src_elem_abi_size) { 23302 else => break :result null, 23303 2 => .{ .p_, .unpckldq }, 23304 }, 23305 }; 23306 23307 const dst_mcv: MCValue = if (src_mcv.isRegister() and 23308 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23309 src_mcv 23310 else 23311 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); 23312 const dst_reg = dst_mcv.getReg().?; 23313 23314 const ext_reg = try self.register_manager.allocReg(null, abi.RegisterClass.sse); 23315 const ext_alias = registerAlias(ext_reg, src_abi_size); 23316 const ext_lock = self.register_manager.lockRegAssumeUnused(ext_reg); 23317 defer self.register_manager.unlockReg(ext_lock); 23318 23319 try self.asmRegisterRegister(.{ .p_, .xor }, ext_alias, ext_alias); 23320 switch (extend) { 23321 .signed => try self.asmRegisterRegister( 23322 .{ switch (src_elem_abi_size) { 23323 else => unreachable, 23324 1 => .p_b, 23325 2 => .p_w, 23326 4 => .p_d, 23327 }, .cmpgt }, 23328 ext_alias, 23329 registerAlias(dst_reg, src_abi_size), 23330 ), 23331 .unsigned => {}, 23332 } 23333 try self.asmRegisterRegister( 23334 mir_tag, 23335 registerAlias(dst_reg, dst_abi_size), 23336 registerAlias(ext_reg, dst_abi_size), 23337 ); 23338 break :result dst_mcv; 23339 }, 23340 } 23341 @compileError("unreachable"); 23342 } 23343 23344 const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; 23345 23346 const src_storage_bits: u16 = switch (src_mcv) { 23347 .register, .register_offset => 64, 23348 .register_pair => 128, 23349 .load_frame => |frame_addr| @intCast(self.getFrameAddrSize(frame_addr) * 8), 23350 else => src_int_info.bits, 23351 }; 23352 23353 const dst_mcv = if ((if (src_mcv.getReg()) |src_reg| src_reg.class() == .general_purpose else src_abi_size > 8) and 23354 dst_int_info.bits <= src_storage_bits and 23355 std.math.divCeil(u16, dst_int_info.bits, 64) catch unreachable == 23356 std.math.divCeil(u32, src_storage_bits, 64) catch unreachable and 23357 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { 23358 const dst_mcv = try self.allocRegOrMem(inst, true); 23359 try self.genCopy(min_ty, dst_mcv, src_mcv, .{}); 23360 break :dst dst_mcv; 23361 }; 23362 23363 if (dst_int_info.bits <= src_int_info.bits) break :result if (dst_mcv.isRegister()) 23364 .{ .register = registerAlias(dst_mcv.getReg().?, dst_abi_size) } 23365 else 23366 dst_mcv; 23367 23368 if (dst_mcv.isRegister()) { 23369 try self.truncateRegister(src_ty, dst_mcv.getReg().?); 23370 break :result .{ .register = registerAlias(dst_mcv.getReg().?, dst_abi_size) }; 23371 } 23372 23373 const src_limbs_len = std.math.divCeil(u31, src_abi_size, 8) catch unreachable; 23374 const dst_limbs_len = @divExact(dst_abi_size, 8); 23375 23376 const high_mcv: MCValue = if (dst_mcv.isBase()) 23377 dst_mcv.address().offset((src_limbs_len - 1) * 8).deref() 23378 else 23379 .{ .register = dst_mcv.register_pair[1] }; 23380 const high_reg = if (high_mcv.isRegister()) 23381 high_mcv.getReg().? 23382 else 23383 try self.copyToTmpRegister(switch (src_int_info.signedness) { 23384 .signed => .isize, 23385 .unsigned => .usize, 23386 }, high_mcv); 23387 const high_lock = self.register_manager.lockRegAssumeUnused(high_reg); 23388 defer self.register_manager.unlockReg(high_lock); 23389 23390 const high_bits = src_int_info.bits % 64; 23391 if (high_bits > 0) { 23392 try self.truncateRegister(src_ty, high_reg); 23393 const high_ty: Type = if (dst_int_info.bits >= 64) .usize else dst_ty; 23394 try self.genCopy(high_ty, high_mcv, .{ .register = high_reg }, .{}); 23395 } 23396 23397 if (dst_limbs_len > src_limbs_len) try self.genInlineMemset( 23398 dst_mcv.address().offset(src_limbs_len * 8), 23399 switch (extend) { 23400 .signed => extend: { 23401 const extend_mcv = MCValue{ .register = high_reg }; 23402 try self.genShiftBinOpMir(.{ ._r, .sa }, .isize, extend_mcv, .u8, .{ .immediate = 63 }); 23403 break :extend extend_mcv; 23404 }, 23405 .unsigned => .{ .immediate = 0 }, 23406 }, 23407 .{ .immediate = (dst_limbs_len - src_limbs_len) * 8 }, 23408 .{}, 23409 ); 23410 23411 break :result dst_mcv; 23412 }) orelse return self.fail("TODO implement airIntCast from {} to {}", .{ 23413 src_ty.fmt(pt), dst_ty.fmt(pt), 23414 }); 23415 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 23416 } 23417 23418 fn airTrunc(self: *CodeGen, inst: Air.Inst.Index) !void { 23419 const pt = self.pt; 23420 const zcu = pt.zcu; 23421 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 23422 23423 const dst_ty = self.typeOfIndex(inst); 23424 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 23425 const src_ty = self.typeOf(ty_op.operand); 23426 const src_abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 23427 23428 const result = result: { 23429 const src_mcv = try self.resolveInst(ty_op.operand); 23430 const src_lock = 23431 if (src_mcv.getReg()) |reg| self.register_manager.lockRegAssumeUnused(reg) else null; 23432 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 23433 23434 const dst_mcv = if (src_mcv.isRegister() and src_mcv.getReg().?.class() == self.regClassForType(dst_ty) and 23435 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 23436 src_mcv 23437 else if (dst_abi_size <= 8) 23438 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv) 23439 else if (dst_abi_size <= 16 and !dst_ty.isVector(zcu)) dst: { 23440 const dst_regs = 23441 try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp); 23442 const dst_mcv: MCValue = .{ .register_pair = dst_regs }; 23443 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 23444 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 23445 23446 try self.genCopy(dst_ty, dst_mcv, src_mcv, .{}); 23447 break :dst dst_mcv; 23448 } else dst: { 23449 const dst_mcv = try self.allocRegOrMemAdvanced(src_ty, inst, true); 23450 try self.genCopy(src_ty, dst_mcv, src_mcv, .{}); 23451 break :dst dst_mcv; 23452 }; 23453 23454 if (dst_ty.zigTypeTag(zcu) == .vector) { 23455 assert(src_ty.zigTypeTag(zcu) == .vector and dst_ty.vectorLen(zcu) == src_ty.vectorLen(zcu)); 23456 const dst_elem_ty = dst_ty.childType(zcu); 23457 const dst_elem_abi_size: u32 = @intCast(dst_elem_ty.abiSize(zcu)); 23458 const src_elem_ty = src_ty.childType(zcu); 23459 const src_elem_abi_size: u32 = @intCast(src_elem_ty.abiSize(zcu)); 23460 23461 const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_elem_abi_size) { 23462 1 => switch (src_elem_abi_size) { 23463 2 => switch (dst_ty.vectorLen(zcu)) { 23464 1...8 => if (self.hasFeature(.avx)) .{ .vp_b, .ackusw } else .{ .p_b, .ackusw }, 23465 9...16 => if (self.hasFeature(.avx2)) .{ .vp_b, .ackusw } else null, 23466 else => null, 23467 }, 23468 else => null, 23469 }, 23470 2 => switch (src_elem_abi_size) { 23471 4 => switch (dst_ty.vectorLen(zcu)) { 23472 1...4 => if (self.hasFeature(.avx)) 23473 .{ .vp_w, .ackusd } 23474 else if (self.hasFeature(.sse4_1)) 23475 .{ .p_w, .ackusd } 23476 else 23477 null, 23478 5...8 => if (self.hasFeature(.avx2)) .{ .vp_w, .ackusd } else null, 23479 else => null, 23480 }, 23481 else => null, 23482 }, 23483 else => null, 23484 }) orelse return self.fail("TODO implement airTrunc for {}", .{dst_ty.fmt(pt)}); 23485 23486 const dst_info = dst_elem_ty.intInfo(zcu); 23487 const src_info = src_elem_ty.intInfo(zcu); 23488 23489 const mask_val = try pt.intValue(src_elem_ty, @as(u64, std.math.maxInt(u64)) >> @intCast(64 - dst_info.bits)); 23490 23491 const splat_ty = try pt.vectorType(.{ 23492 .len = @intCast(@divExact(@as(u64, if (src_abi_size > 16) 256 else 128), src_info.bits)), 23493 .child = src_elem_ty.ip_index, 23494 }); 23495 const splat_abi_size: u32 = @intCast(splat_ty.abiSize(zcu)); 23496 23497 const splat_val = try pt.intern(.{ .aggregate = .{ 23498 .ty = splat_ty.ip_index, 23499 .storage = .{ .repeated_elem = mask_val.ip_index }, 23500 } }); 23501 23502 const splat_mcv = try self.genTypedValue(.fromInterned(splat_val)); 23503 const splat_addr_mcv: MCValue = switch (splat_mcv) { 23504 .memory, .indirect, .load_frame => splat_mcv.address(), 23505 else => .{ .register = try self.copyToTmpRegister(.usize, splat_mcv.address()) }, 23506 }; 23507 23508 const dst_reg = dst_mcv.getReg().?; 23509 const dst_alias = registerAlias(dst_reg, src_abi_size); 23510 if (self.hasFeature(.avx)) { 23511 try self.asmRegisterRegisterMemory( 23512 .{ .vp_, .@"and" }, 23513 dst_alias, 23514 dst_alias, 23515 try splat_addr_mcv.deref().mem(self, .{ .size = .fromSize(splat_abi_size) }), 23516 ); 23517 if (src_abi_size > 16) { 23518 const temp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.sse); 23519 const temp_lock = self.register_manager.lockRegAssumeUnused(temp_reg); 23520 defer self.register_manager.unlockReg(temp_lock); 23521 23522 try self.asmRegisterRegisterImmediate( 23523 .{ if (self.hasFeature(.avx2)) .v_i128 else .v_f128, .extract }, 23524 registerAlias(temp_reg, dst_abi_size), 23525 dst_alias, 23526 .u(1), 23527 ); 23528 try self.asmRegisterRegisterRegister( 23529 mir_tag, 23530 registerAlias(dst_reg, dst_abi_size), 23531 registerAlias(dst_reg, dst_abi_size), 23532 registerAlias(temp_reg, dst_abi_size), 23533 ); 23534 } else try self.asmRegisterRegisterRegister(mir_tag, dst_alias, dst_alias, dst_alias); 23535 } else { 23536 try self.asmRegisterMemory( 23537 .{ .p_, .@"and" }, 23538 dst_alias, 23539 try splat_addr_mcv.deref().mem(self, .{ .size = .fromSize(splat_abi_size) }), 23540 ); 23541 try self.asmRegisterRegister(mir_tag, dst_alias, dst_alias); 23542 } 23543 break :result dst_mcv; 23544 } 23545 23546 // when truncating a `u16` to `u5`, for example, those top 3 bits in the result 23547 // have to be removed. this only happens if the dst if not a power-of-two size. 23548 if (dst_abi_size <= 8) { 23549 if (self.regExtraBits(dst_ty) > 0) { 23550 try self.truncateRegister(dst_ty, dst_mcv.register.to64()); 23551 } 23552 } else if (dst_abi_size <= 16) { 23553 const dst_info = dst_ty.intInfo(zcu); 23554 const high_ty = try pt.intType(dst_info.signedness, dst_info.bits - 64); 23555 if (self.regExtraBits(high_ty) > 0) { 23556 try self.truncateRegister(high_ty, dst_mcv.register_pair[1].to64()); 23557 } 23558 } 23559 23560 break :result dst_mcv; 23561 }; 23562 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 23563 } 23564 23565 fn airIntFromBool(self: *CodeGen, inst: Air.Inst.Index) !void { 23566 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 23567 const ty = self.typeOfIndex(inst); 23568 23569 const operand = try self.resolveInst(un_op); 23570 const dst_mcv = if (self.reuseOperand(inst, un_op, 0, operand)) 23571 operand 23572 else 23573 try self.copyToRegisterWithInstTracking(inst, ty, operand); 23574 23575 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); 23576 } 23577 23578 fn airSlice(self: *CodeGen, inst: Air.Inst.Index) !void { 23579 const zcu = self.pt.zcu; 23580 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 23581 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 23582 23583 const slice_ty = self.typeOfIndex(inst); 23584 const frame_index = try self.allocFrameIndex(.initSpill(slice_ty, zcu)); 23585 23586 const ptr_ty = self.typeOf(bin_op.lhs); 23587 try self.genSetMem(.{ .frame = frame_index }, 0, ptr_ty, .{ .air_ref = bin_op.lhs }, .{}); 23588 23589 const len_ty = self.typeOf(bin_op.rhs); 23590 try self.genSetMem( 23591 .{ .frame = frame_index }, 23592 @intCast(ptr_ty.abiSize(zcu)), 23593 len_ty, 23594 .{ .air_ref = bin_op.rhs }, 23595 .{}, 23596 ); 23597 23598 const result = MCValue{ .load_frame = .{ .index = frame_index } }; 23599 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 23600 } 23601 23602 fn airUnOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 23603 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 23604 const dst_mcv = try self.genUnOp(inst, tag, ty_op.operand); 23605 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 23606 } 23607 23608 fn airBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 23609 const pt = self.pt; 23610 const zcu = pt.zcu; 23611 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 23612 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); 23613 23614 const dst_ty = self.typeOfIndex(inst); 23615 if (dst_ty.isAbiInt(zcu)) { 23616 const abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 23617 const bit_size: u32 = @intCast(dst_ty.bitSize(zcu)); 23618 if (abi_size * 8 > bit_size) { 23619 const dst_lock = switch (dst_mcv) { 23620 .register => |dst_reg| self.register_manager.lockRegAssumeUnused(dst_reg), 23621 else => null, 23622 }; 23623 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 23624 23625 if (dst_mcv.isRegister()) { 23626 try self.truncateRegister(dst_ty, dst_mcv.getReg().?); 23627 } else { 23628 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 23629 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 23630 defer self.register_manager.unlockReg(tmp_lock); 23631 23632 const hi_ty = try pt.intType(.unsigned, @intCast((dst_ty.bitSize(zcu) - 1) % 64 + 1)); 23633 const hi_mcv = dst_mcv.address().offset(@intCast(bit_size / 64 * 8)).deref(); 23634 try self.genSetReg(tmp_reg, hi_ty, hi_mcv, .{}); 23635 try self.truncateRegister(dst_ty, tmp_reg); 23636 try self.genCopy(hi_ty, hi_mcv, .{ .register = tmp_reg }, .{}); 23637 } 23638 } 23639 } 23640 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); 23641 } 23642 23643 fn airPtrArithmetic(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 23644 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 23645 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 23646 const dst_mcv = try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs); 23647 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); 23648 } 23649 23650 fn activeIntBits(self: *CodeGen, dst_air: Air.Inst.Ref) u16 { 23651 const pt = self.pt; 23652 const zcu = pt.zcu; 23653 const air_tag = self.air.instructions.items(.tag); 23654 const air_data = self.air.instructions.items(.data); 23655 23656 const dst_ty = self.typeOf(dst_air); 23657 const dst_info = dst_ty.intInfo(zcu); 23658 if (dst_air.toIndex()) |inst| { 23659 switch (air_tag[@intFromEnum(inst)]) { 23660 .intcast => { 23661 const src_ty = self.typeOf(air_data[@intFromEnum(inst)].ty_op.operand); 23662 const src_info = src_ty.intInfo(zcu); 23663 return @min(switch (src_info.signedness) { 23664 .signed => switch (dst_info.signedness) { 23665 .signed => src_info.bits, 23666 .unsigned => src_info.bits - 1, 23667 }, 23668 .unsigned => switch (dst_info.signedness) { 23669 .signed => src_info.bits + 1, 23670 .unsigned => src_info.bits, 23671 }, 23672 }, dst_info.bits); 23673 }, 23674 else => {}, 23675 } 23676 } else if (dst_air.toInterned()) |ip_index| { 23677 var space: Value.BigIntSpace = undefined; 23678 const src_int = Value.fromInterned(ip_index).toBigInt(&space, zcu); 23679 return @as(u16, @intCast(src_int.bitCountTwosComp())) + 23680 @intFromBool(src_int.positive and dst_info.signedness == .signed); 23681 } 23682 return dst_info.bits; 23683 } 23684 23685 fn airMulDivBinOp(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 23686 const pt = self.pt; 23687 const zcu = pt.zcu; 23688 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 23689 const result = result: { 23690 const dst_ty = self.typeOfIndex(inst); 23691 switch (dst_ty.zigTypeTag(zcu)) { 23692 .float, .vector => break :result try self.genBinOp(inst, tag, bin_op.lhs, bin_op.rhs), 23693 else => {}, 23694 } 23695 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 23696 23697 const dst_info = dst_ty.intInfo(zcu); 23698 const src_ty = try pt.intType(dst_info.signedness, switch (tag) { 23699 else => unreachable, 23700 .mul, .mul_wrap => @max( 23701 self.activeIntBits(bin_op.lhs), 23702 self.activeIntBits(bin_op.rhs), 23703 dst_info.bits / 2, 23704 ), 23705 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_info.bits, 23706 }); 23707 const src_abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 23708 23709 if (dst_abi_size == 16 and src_abi_size == 16) switch (tag) { 23710 else => unreachable, 23711 .mul, .mul_wrap => {}, 23712 .div_trunc, .div_floor, .div_exact, .rem, .mod => { 23713 const signed = dst_ty.isSignedInt(zcu); 23714 var callee_buf: ["__udiv?i3".len]u8 = undefined; 23715 const signed_div_floor_state: struct { 23716 frame_index: FrameIndex, 23717 state: State, 23718 reloc: Mir.Inst.Index, 23719 } = if (signed and tag == .div_floor) state: { 23720 const frame_index = try self.allocFrameIndex(.initType(.usize, zcu)); 23721 try self.asmMemoryImmediate( 23722 .{ ._, .mov }, 23723 .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ .size = .qword } } }, 23724 .u(0), 23725 ); 23726 23727 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 23728 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 23729 defer self.register_manager.unlockReg(tmp_lock); 23730 23731 const lhs_mcv = try self.resolveInst(bin_op.lhs); 23732 const mat_lhs_mcv = switch (lhs_mcv) { 23733 .load_symbol => mat_lhs_mcv: { 23734 // TODO clean this up! 23735 const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); 23736 break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 23737 }, 23738 else => lhs_mcv, 23739 }; 23740 const mat_lhs_lock = switch (mat_lhs_mcv) { 23741 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 23742 else => null, 23743 }; 23744 defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); 23745 if (mat_lhs_mcv.isBase()) try self.asmRegisterMemory( 23746 .{ ._, .mov }, 23747 tmp_reg, 23748 try mat_lhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 23749 ) else try self.asmRegisterRegister( 23750 .{ ._, .mov }, 23751 tmp_reg, 23752 mat_lhs_mcv.register_pair[1], 23753 ); 23754 23755 const rhs_mcv = try self.resolveInst(bin_op.rhs); 23756 const mat_rhs_mcv = switch (rhs_mcv) { 23757 .load_symbol => mat_rhs_mcv: { 23758 // TODO clean this up! 23759 const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); 23760 break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 23761 }, 23762 else => rhs_mcv, 23763 }; 23764 const mat_rhs_lock = switch (mat_rhs_mcv) { 23765 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 23766 else => null, 23767 }; 23768 defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); 23769 if (mat_rhs_mcv.isBase()) try self.asmRegisterMemory( 23770 .{ ._, .xor }, 23771 tmp_reg, 23772 try mat_rhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 23773 ) else try self.asmRegisterRegister( 23774 .{ ._, .xor }, 23775 tmp_reg, 23776 mat_rhs_mcv.register_pair[1], 23777 ); 23778 const state = try self.saveState(); 23779 const reloc = try self.asmJccReloc(.ns, undefined); 23780 23781 break :state .{ .frame_index = frame_index, .state = state, .reloc = reloc }; 23782 } else undefined; 23783 const call_mcv = try self.genCall( 23784 .{ .lib = .{ 23785 .return_type = dst_ty.toIntern(), 23786 .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() }, 23787 .callee = std.fmt.bufPrint(&callee_buf, "__{s}{s}{c}i3", .{ 23788 if (signed) "" else "u", 23789 switch (tag) { 23790 .div_trunc, .div_exact => "div", 23791 .div_floor => if (signed) "mod" else "div", 23792 .rem, .mod => "mod", 23793 else => unreachable, 23794 }, 23795 intCompilerRtAbiName(@intCast(dst_ty.bitSize(zcu))), 23796 }) catch unreachable, 23797 } }, 23798 &.{ src_ty, src_ty }, 23799 &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } }, 23800 .{}, 23801 ); 23802 break :result if (signed) switch (tag) { 23803 .div_floor => { 23804 try self.asmRegisterRegister( 23805 .{ ._, .@"or" }, 23806 call_mcv.register_pair[0], 23807 call_mcv.register_pair[1], 23808 ); 23809 try self.asmSetccMemory(.nz, .{ 23810 .base = .{ .frame = signed_div_floor_state.frame_index }, 23811 .mod = .{ .rm = .{ .size = .byte } }, 23812 }); 23813 try self.restoreState(signed_div_floor_state.state, &.{}, .{ 23814 .emit_instructions = true, 23815 .update_tracking = true, 23816 .resurrect = true, 23817 .close_scope = true, 23818 }); 23819 self.performReloc(signed_div_floor_state.reloc); 23820 const dst_mcv = try self.genCall( 23821 .{ .lib = .{ 23822 .return_type = dst_ty.toIntern(), 23823 .param_types = &.{ src_ty.toIntern(), src_ty.toIntern() }, 23824 .callee = std.fmt.bufPrint(&callee_buf, "__div{c}i3", .{ 23825 intCompilerRtAbiName(@intCast(dst_ty.bitSize(zcu))), 23826 }) catch unreachable, 23827 } }, 23828 &.{ src_ty, src_ty }, 23829 &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } }, 23830 .{}, 23831 ); 23832 try self.asmRegisterMemory( 23833 .{ ._, .sub }, 23834 dst_mcv.register_pair[0], 23835 .{ 23836 .base = .{ .frame = signed_div_floor_state.frame_index }, 23837 .mod = .{ .rm = .{ .size = .qword } }, 23838 }, 23839 ); 23840 try self.asmRegisterImmediate(.{ ._, .sbb }, dst_mcv.register_pair[1], .u(0)); 23841 try self.freeValue( 23842 .{ .load_frame = .{ .index = signed_div_floor_state.frame_index } }, 23843 ); 23844 break :result dst_mcv; 23845 }, 23846 .mod => { 23847 const dst_regs = call_mcv.register_pair; 23848 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 23849 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 23850 23851 const tmp_regs = 23852 try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp); 23853 const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs); 23854 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); 23855 23856 const rhs_mcv = try self.resolveInst(bin_op.rhs); 23857 const mat_rhs_mcv = switch (rhs_mcv) { 23858 .load_symbol => mat_rhs_mcv: { 23859 // TODO clean this up! 23860 const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); 23861 break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 23862 }, 23863 else => rhs_mcv, 23864 }; 23865 const mat_rhs_lock = switch (mat_rhs_mcv) { 23866 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 23867 else => null, 23868 }; 23869 defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); 23870 23871 for (tmp_regs, dst_regs) |tmp_reg, dst_reg| 23872 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_reg); 23873 if (mat_rhs_mcv.isBase()) { 23874 try self.asmRegisterMemory( 23875 .{ ._, .add }, 23876 tmp_regs[0], 23877 try mat_rhs_mcv.mem(self, .{ .size = .qword }), 23878 ); 23879 try self.asmRegisterMemory( 23880 .{ ._, .adc }, 23881 tmp_regs[1], 23882 try mat_rhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 23883 ); 23884 } else for ( 23885 [_]Mir.Inst.Tag{ .add, .adc }, 23886 tmp_regs, 23887 mat_rhs_mcv.register_pair, 23888 ) |op, tmp_reg, rhs_reg| 23889 try self.asmRegisterRegister(.{ ._, op }, tmp_reg, rhs_reg); 23890 try self.asmRegisterRegister(.{ ._, .@"test" }, dst_regs[1], dst_regs[1]); 23891 for (dst_regs, tmp_regs) |dst_reg, tmp_reg| 23892 try self.asmCmovccRegisterRegister(.s, dst_reg, tmp_reg); 23893 break :result call_mcv; 23894 }, 23895 else => call_mcv, 23896 } else call_mcv; 23897 }, 23898 }; 23899 23900 try self.spillEflagsIfOccupied(); 23901 try self.spillRegisters(&.{ .rax, .rcx, .rdx }); 23902 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx }); 23903 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 23904 23905 const lhs_mcv = try self.resolveInst(bin_op.lhs); 23906 const rhs_mcv = try self.resolveInst(bin_op.rhs); 23907 break :result try self.genMulDivBinOp(tag, inst, dst_ty, src_ty, lhs_mcv, rhs_mcv); 23908 }; 23909 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 23910 } 23911 23912 fn airAddSat(self: *CodeGen, inst: Air.Inst.Index) !void { 23913 const pt = self.pt; 23914 const zcu = pt.zcu; 23915 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 23916 const ty = self.typeOf(bin_op.lhs); 23917 if (ty.zigTypeTag(zcu) == .vector or ty.abiSize(zcu) > 8) return self.fail( 23918 "TODO implement airAddSat for {}", 23919 .{ty.fmt(pt)}, 23920 ); 23921 23922 const lhs_mcv = try self.resolveInst(bin_op.lhs); 23923 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) 23924 lhs_mcv 23925 else 23926 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv); 23927 const dst_reg = dst_mcv.register; 23928 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 23929 defer self.register_manager.unlockReg(dst_lock); 23930 23931 const rhs_mcv = try self.resolveInst(bin_op.rhs); 23932 const rhs_lock = switch (rhs_mcv) { 23933 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 23934 else => null, 23935 }; 23936 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 23937 23938 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 23939 const limit_mcv = MCValue{ .register = limit_reg }; 23940 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); 23941 defer self.register_manager.unlockReg(limit_lock); 23942 23943 const reg_bits = self.regBitSize(ty); 23944 const reg_extra_bits = self.regExtraBits(ty); 23945 const cc: Condition = if (ty.isSignedInt(zcu)) cc: { 23946 if (reg_extra_bits > 0) { 23947 try self.genShiftBinOpMir(.{ ._l, .sa }, ty, dst_mcv, .u8, .{ .immediate = reg_extra_bits }); 23948 } 23949 try self.genSetReg(limit_reg, ty, dst_mcv, .{}); 23950 try self.genShiftBinOpMir(.{ ._r, .sa }, ty, limit_mcv, .u8, .{ .immediate = reg_bits - 1 }); 23951 try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, .{ 23952 .immediate = (@as(u64, 1) << @intCast(reg_bits - 1)) - 1, 23953 }); 23954 if (reg_extra_bits > 0) { 23955 const shifted_rhs_reg = try self.copyToTmpRegister(ty, rhs_mcv); 23956 const shifted_rhs_mcv = MCValue{ .register = shifted_rhs_reg }; 23957 const shifted_rhs_lock = self.register_manager.lockRegAssumeUnused(shifted_rhs_reg); 23958 defer self.register_manager.unlockReg(shifted_rhs_lock); 23959 23960 try self.genShiftBinOpMir(.{ ._l, .sa }, ty, shifted_rhs_mcv, .u8, .{ .immediate = reg_extra_bits }); 23961 try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, shifted_rhs_mcv); 23962 } else try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, rhs_mcv); 23963 break :cc .o; 23964 } else cc: { 23965 try self.genSetReg(limit_reg, ty, .{ 23966 .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - ty.bitSize(zcu)), 23967 }, .{}); 23968 23969 try self.genBinOpMir(.{ ._, .add }, ty, dst_mcv, rhs_mcv); 23970 if (reg_extra_bits > 0) { 23971 try self.genBinOpMir(.{ ._, .cmp }, ty, dst_mcv, limit_mcv); 23972 break :cc .a; 23973 } 23974 break :cc .c; 23975 }; 23976 23977 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(zcu))), 2); 23978 try self.asmCmovccRegisterRegister( 23979 cc, 23980 registerAlias(dst_reg, cmov_abi_size), 23981 registerAlias(limit_reg, cmov_abi_size), 23982 ); 23983 23984 if (reg_extra_bits > 0 and ty.isSignedInt(zcu)) 23985 try self.genShiftBinOpMir(.{ ._r, .sa }, ty, dst_mcv, .u8, .{ .immediate = reg_extra_bits }); 23986 23987 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); 23988 } 23989 23990 fn airSubSat(self: *CodeGen, inst: Air.Inst.Index) !void { 23991 const pt = self.pt; 23992 const zcu = pt.zcu; 23993 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 23994 const ty = self.typeOf(bin_op.lhs); 23995 if (ty.zigTypeTag(zcu) == .vector or ty.abiSize(zcu) > 8) return self.fail( 23996 "TODO implement airSubSat for {}", 23997 .{ty.fmt(pt)}, 23998 ); 23999 24000 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24001 const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) 24002 lhs_mcv 24003 else 24004 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv); 24005 const dst_reg = dst_mcv.register; 24006 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 24007 defer self.register_manager.unlockReg(dst_lock); 24008 24009 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24010 const rhs_lock = switch (rhs_mcv) { 24011 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 24012 else => null, 24013 }; 24014 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 24015 24016 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 24017 const limit_mcv = MCValue{ .register = limit_reg }; 24018 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); 24019 defer self.register_manager.unlockReg(limit_lock); 24020 24021 const reg_bits = self.regBitSize(ty); 24022 const reg_extra_bits = self.regExtraBits(ty); 24023 const cc: Condition = if (ty.isSignedInt(zcu)) cc: { 24024 if (reg_extra_bits > 0) { 24025 try self.genShiftBinOpMir(.{ ._l, .sa }, ty, dst_mcv, .u8, .{ .immediate = reg_extra_bits }); 24026 } 24027 try self.genSetReg(limit_reg, ty, dst_mcv, .{}); 24028 try self.genShiftBinOpMir(.{ ._r, .sa }, ty, limit_mcv, .u8, .{ .immediate = reg_bits - 1 }); 24029 try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, .{ 24030 .immediate = (@as(u64, 1) << @intCast(reg_bits - 1)) - 1, 24031 }); 24032 if (reg_extra_bits > 0) { 24033 const shifted_rhs_reg = try self.copyToTmpRegister(ty, rhs_mcv); 24034 const shifted_rhs_mcv = MCValue{ .register = shifted_rhs_reg }; 24035 const shifted_rhs_lock = self.register_manager.lockRegAssumeUnused(shifted_rhs_reg); 24036 defer self.register_manager.unlockReg(shifted_rhs_lock); 24037 24038 try self.genShiftBinOpMir(.{ ._l, .sa }, ty, shifted_rhs_mcv, .u8, .{ .immediate = reg_extra_bits }); 24039 try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, shifted_rhs_mcv); 24040 } else try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, rhs_mcv); 24041 break :cc .o; 24042 } else cc: { 24043 try self.genSetReg(limit_reg, ty, .{ .immediate = 0 }, .{}); 24044 try self.genBinOpMir(.{ ._, .sub }, ty, dst_mcv, rhs_mcv); 24045 break :cc .c; 24046 }; 24047 24048 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(zcu))), 2); 24049 try self.asmCmovccRegisterRegister( 24050 cc, 24051 registerAlias(dst_reg, cmov_abi_size), 24052 registerAlias(limit_reg, cmov_abi_size), 24053 ); 24054 24055 if (reg_extra_bits > 0 and ty.isSignedInt(zcu)) 24056 try self.genShiftBinOpMir(.{ ._r, .sa }, ty, dst_mcv, .u8, .{ .immediate = reg_extra_bits }); 24057 24058 return self.finishAir(inst, dst_mcv, .{ bin_op.lhs, bin_op.rhs, .none }); 24059 } 24060 24061 fn airMulSat(self: *CodeGen, inst: Air.Inst.Index) !void { 24062 const pt = self.pt; 24063 const zcu = pt.zcu; 24064 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 24065 const ty = self.typeOf(bin_op.lhs); 24066 24067 const result = result: { 24068 if (ty.toIntern() == .i128_type) { 24069 const ptr_c_int = try pt.singleMutPtrType(.c_int); 24070 const overflow = try self.allocTempRegOrMem(.c_int, false); 24071 24072 const dst_mcv = try self.genCall(.{ .lib = .{ 24073 .return_type = .i128_type, 24074 .param_types = &.{ .i128_type, .i128_type, ptr_c_int.toIntern() }, 24075 .callee = "__muloti4", 24076 } }, &.{ .i128, .i128, ptr_c_int }, &.{ 24077 .{ .air_ref = bin_op.lhs }, 24078 .{ .air_ref = bin_op.rhs }, 24079 overflow.address(), 24080 }, .{}); 24081 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_mcv.register_pair); 24082 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 24083 24084 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 24085 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 24086 defer self.register_manager.unlockReg(tmp_lock); 24087 24088 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24089 const mat_lhs_mcv = switch (lhs_mcv) { 24090 .load_symbol => mat_lhs_mcv: { 24091 // TODO clean this up! 24092 const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); 24093 break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 24094 }, 24095 else => lhs_mcv, 24096 }; 24097 const mat_lhs_lock = switch (mat_lhs_mcv) { 24098 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 24099 else => null, 24100 }; 24101 defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); 24102 if (mat_lhs_mcv.isBase()) try self.asmRegisterMemory( 24103 .{ ._, .mov }, 24104 tmp_reg, 24105 try mat_lhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 24106 ) else try self.asmRegisterRegister( 24107 .{ ._, .mov }, 24108 tmp_reg, 24109 mat_lhs_mcv.register_pair[1], 24110 ); 24111 24112 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24113 const mat_rhs_mcv = switch (rhs_mcv) { 24114 .load_symbol => mat_rhs_mcv: { 24115 // TODO clean this up! 24116 const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); 24117 break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 24118 }, 24119 else => rhs_mcv, 24120 }; 24121 const mat_rhs_lock = switch (mat_rhs_mcv) { 24122 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 24123 else => null, 24124 }; 24125 defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); 24126 if (mat_rhs_mcv.isBase()) try self.asmRegisterMemory( 24127 .{ ._, .xor }, 24128 tmp_reg, 24129 try mat_rhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 24130 ) else try self.asmRegisterRegister( 24131 .{ ._, .xor }, 24132 tmp_reg, 24133 mat_rhs_mcv.register_pair[1], 24134 ); 24135 24136 try self.asmRegisterImmediate(.{ ._r, .sa }, tmp_reg, .u(63)); 24137 try self.asmRegister(.{ ._, .not }, tmp_reg); 24138 try self.asmMemoryImmediate(.{ ._, .cmp }, try overflow.mem(self, .{ .size = .dword }), .s(0)); 24139 try self.freeValue(overflow); 24140 try self.asmCmovccRegisterRegister(.ne, dst_mcv.register_pair[0], tmp_reg); 24141 try self.asmRegisterImmediate(.{ ._c, .bt }, tmp_reg, .u(63)); 24142 try self.asmCmovccRegisterRegister(.ne, dst_mcv.register_pair[1], tmp_reg); 24143 break :result dst_mcv; 24144 } 24145 24146 if (ty.zigTypeTag(zcu) == .vector or ty.abiSize(zcu) > 8) return self.fail( 24147 "TODO implement airMulSat for {}", 24148 .{ty.fmt(pt)}, 24149 ); 24150 24151 try self.spillRegisters(&.{ .rax, .rcx, .rdx }); 24152 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx }); 24153 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24154 24155 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24156 const lhs_lock = switch (lhs_mcv) { 24157 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 24158 else => null, 24159 }; 24160 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 24161 24162 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24163 const rhs_lock = switch (rhs_mcv) { 24164 .register => |reg| self.register_manager.lockReg(reg), 24165 else => null, 24166 }; 24167 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 24168 24169 const limit_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 24170 const limit_mcv = MCValue{ .register = limit_reg }; 24171 const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); 24172 defer self.register_manager.unlockReg(limit_lock); 24173 24174 const reg_bits = self.regBitSize(ty); 24175 const cc: Condition = if (ty.isSignedInt(zcu)) cc: { 24176 try self.genSetReg(limit_reg, ty, lhs_mcv, .{}); 24177 try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, rhs_mcv); 24178 try self.genShiftBinOpMir(.{ ._r, .sa }, ty, limit_mcv, .u8, .{ .immediate = reg_bits - 1 }); 24179 try self.genBinOpMir(.{ ._, .xor }, ty, limit_mcv, .{ 24180 .immediate = (@as(u64, 1) << @intCast(reg_bits - 1)) - 1, 24181 }); 24182 break :cc .o; 24183 } else cc: { 24184 try self.genSetReg(limit_reg, ty, .{ 24185 .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - reg_bits), 24186 }, .{}); 24187 break :cc .c; 24188 }; 24189 24190 const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, ty, lhs_mcv, rhs_mcv); 24191 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(zcu))), 2); 24192 try self.asmCmovccRegisterRegister( 24193 cc, 24194 registerAlias(dst_mcv.register, cmov_abi_size), 24195 registerAlias(limit_reg, cmov_abi_size), 24196 ); 24197 break :result dst_mcv; 24198 }; 24199 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 24200 } 24201 24202 fn airAddSubWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { 24203 const pt = self.pt; 24204 const zcu = pt.zcu; 24205 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 24206 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 24207 const result: MCValue = result: { 24208 const tag = self.air.instructions.items(.tag)[@intFromEnum(inst)]; 24209 const ty = self.typeOf(bin_op.lhs); 24210 switch (ty.zigTypeTag(zcu)) { 24211 .vector => return self.fail("TODO implement add/sub with overflow for Vector type", .{}), 24212 .int => { 24213 try self.spillEflagsIfOccupied(); 24214 try self.spillRegisters(&.{ .rcx, .rdi, .rsi }); 24215 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rcx, .rdi, .rsi }); 24216 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24217 24218 const partial_mcv = try self.genBinOp(null, switch (tag) { 24219 .add_with_overflow => .add, 24220 .sub_with_overflow => .sub, 24221 else => unreachable, 24222 }, bin_op.lhs, bin_op.rhs); 24223 const int_info = ty.intInfo(zcu); 24224 const cc: Condition = switch (int_info.signedness) { 24225 .unsigned => .c, 24226 .signed => .o, 24227 }; 24228 24229 const tuple_ty = self.typeOfIndex(inst); 24230 if (int_info.bits >= 8 and std.math.isPowerOfTwo(int_info.bits)) { 24231 switch (partial_mcv) { 24232 .register => |reg| { 24233 self.eflags_inst = inst; 24234 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; 24235 }, 24236 else => {}, 24237 } 24238 24239 const frame_index = try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24240 try self.genSetMem( 24241 .{ .frame = frame_index }, 24242 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24243 .u1, 24244 .{ .eflags = cc }, 24245 .{}, 24246 ); 24247 try self.genSetMem( 24248 .{ .frame = frame_index }, 24249 @intCast(tuple_ty.structFieldOffset(0, zcu)), 24250 ty, 24251 partial_mcv, 24252 .{}, 24253 ); 24254 break :result .{ .load_frame = .{ .index = frame_index } }; 24255 } 24256 24257 const frame_index = try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24258 try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); 24259 break :result .{ .load_frame = .{ .index = frame_index } }; 24260 }, 24261 else => unreachable, 24262 } 24263 }; 24264 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 24265 } 24266 24267 fn airShlWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { 24268 const pt = self.pt; 24269 const zcu = pt.zcu; 24270 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 24271 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 24272 const result: MCValue = result: { 24273 const lhs_ty = self.typeOf(bin_op.lhs); 24274 const rhs_ty = self.typeOf(bin_op.rhs); 24275 switch (lhs_ty.zigTypeTag(zcu)) { 24276 .vector => return self.fail("TODO implement shl with overflow for Vector type", .{}), 24277 .int => { 24278 try self.spillEflagsIfOccupied(); 24279 try self.spillRegisters(&.{ .rcx, .rdi, .rsi }); 24280 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rcx, .rdi, .rsi }); 24281 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24282 24283 const lhs = try self.resolveInst(bin_op.lhs); 24284 const rhs = try self.resolveInst(bin_op.rhs); 24285 24286 const int_info = lhs_ty.intInfo(zcu); 24287 24288 const partial_mcv = try self.genShiftBinOp(.shl, null, lhs, rhs, lhs_ty, rhs_ty); 24289 const partial_lock = switch (partial_mcv) { 24290 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 24291 else => null, 24292 }; 24293 defer if (partial_lock) |lock| self.register_manager.unlockReg(lock); 24294 24295 const tmp_mcv = try self.genShiftBinOp(.shr, null, partial_mcv, rhs, lhs_ty, rhs_ty); 24296 const tmp_lock = switch (tmp_mcv) { 24297 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 24298 else => null, 24299 }; 24300 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); 24301 24302 try self.genBinOpMir(.{ ._, .cmp }, lhs_ty, tmp_mcv, lhs); 24303 const cc = Condition.ne; 24304 24305 const tuple_ty = self.typeOfIndex(inst); 24306 if (int_info.bits >= 8 and std.math.isPowerOfTwo(int_info.bits)) { 24307 switch (partial_mcv) { 24308 .register => |reg| { 24309 self.eflags_inst = inst; 24310 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; 24311 }, 24312 else => {}, 24313 } 24314 24315 const frame_index = try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24316 try self.genSetMem( 24317 .{ .frame = frame_index }, 24318 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24319 tuple_ty.fieldType(1, zcu), 24320 .{ .eflags = cc }, 24321 .{}, 24322 ); 24323 try self.genSetMem( 24324 .{ .frame = frame_index }, 24325 @intCast(tuple_ty.structFieldOffset(0, zcu)), 24326 tuple_ty.fieldType(0, zcu), 24327 partial_mcv, 24328 .{}, 24329 ); 24330 break :result .{ .load_frame = .{ .index = frame_index } }; 24331 } 24332 24333 const frame_index = 24334 try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24335 try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); 24336 break :result .{ .load_frame = .{ .index = frame_index } }; 24337 }, 24338 else => unreachable, 24339 } 24340 }; 24341 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 24342 } 24343 24344 fn genSetFrameTruncatedOverflowCompare( 24345 self: *CodeGen, 24346 tuple_ty: Type, 24347 frame_index: FrameIndex, 24348 src_mcv: MCValue, 24349 overflow_cc: ?Condition, 24350 ) !void { 24351 const pt = self.pt; 24352 const zcu = pt.zcu; 24353 const src_lock = switch (src_mcv) { 24354 .register => |reg| self.register_manager.lockReg(reg), 24355 else => null, 24356 }; 24357 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 24358 24359 const ty = tuple_ty.fieldType(0, zcu); 24360 const ty_size = ty.abiSize(zcu); 24361 const int_info = ty.intInfo(zcu); 24362 24363 const hi_bits = (int_info.bits - 1) % 64 + 1; 24364 const hi_ty = try pt.intType(int_info.signedness, hi_bits); 24365 24366 const limb_bits: u16 = @intCast(if (int_info.bits <= 64) self.regBitSize(ty) else 64); 24367 const limb_ty = try pt.intType(int_info.signedness, limb_bits); 24368 24369 const rest_ty = try pt.intType(.unsigned, int_info.bits - hi_bits); 24370 24371 const temp_regs = 24372 try self.register_manager.allocRegs(3, @splat(null), abi.RegisterClass.gp); 24373 const temp_locks = self.register_manager.lockRegsAssumeUnused(3, temp_regs); 24374 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 24375 24376 const overflow_reg = temp_regs[0]; 24377 if (overflow_cc) |cc| try self.asmSetccRegister(cc, overflow_reg.to8()); 24378 24379 const scratch_reg = temp_regs[1]; 24380 const hi_limb_off = if (int_info.bits <= 64) 0 else (int_info.bits - 1) / 64 * 8; 24381 const hi_limb_mcv = if (hi_limb_off > 0) 24382 src_mcv.address().offset(int_info.bits / 64 * 8).deref() 24383 else 24384 src_mcv; 24385 try self.genSetReg(scratch_reg, limb_ty, hi_limb_mcv, .{}); 24386 try self.truncateRegister(hi_ty, scratch_reg); 24387 try self.genBinOpMir(.{ ._, .cmp }, limb_ty, .{ .register = scratch_reg }, hi_limb_mcv); 24388 24389 const eq_reg = temp_regs[2]; 24390 if (overflow_cc) |_| { 24391 try self.asmSetccRegister(.ne, eq_reg.to8()); 24392 try self.genBinOpMir(.{ ._, .@"or" }, .u8, .{ .register = overflow_reg }, .{ .register = eq_reg }); 24393 } 24394 try self.genSetMem( 24395 .{ .frame = frame_index }, 24396 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24397 tuple_ty.fieldType(1, zcu), 24398 if (overflow_cc) |_| .{ .register = overflow_reg.to8() } else .{ .eflags = .ne }, 24399 .{}, 24400 ); 24401 24402 const payload_off: i32 = @intCast(tuple_ty.structFieldOffset(0, zcu)); 24403 if (hi_limb_off > 0) try self.genSetMem( 24404 .{ .frame = frame_index }, 24405 payload_off, 24406 rest_ty, 24407 src_mcv, 24408 .{}, 24409 ); 24410 try self.genSetMem( 24411 .{ .frame = frame_index }, 24412 payload_off + hi_limb_off, 24413 limb_ty, 24414 .{ .register = scratch_reg }, 24415 .{}, 24416 ); 24417 var ext_off: i32 = hi_limb_off + 8; 24418 if (ext_off < ty_size) { 24419 switch (int_info.signedness) { 24420 .signed => try self.asmRegisterImmediate(.{ ._r, .sa }, scratch_reg.to64(), .s(63)), 24421 .unsigned => try self.asmRegisterRegister(.{ ._, .xor }, scratch_reg.to32(), scratch_reg.to32()), 24422 } 24423 while (ext_off < ty_size) : (ext_off += 8) try self.genSetMem( 24424 .{ .frame = frame_index }, 24425 payload_off + ext_off, 24426 limb_ty, 24427 .{ .register = scratch_reg }, 24428 .{}, 24429 ); 24430 } 24431 } 24432 24433 fn airMulWithOverflow(self: *CodeGen, inst: Air.Inst.Index) !void { 24434 const pt = self.pt; 24435 const zcu = pt.zcu; 24436 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 24437 const bin_op = self.air.extraData(Air.Bin, ty_pl.payload).data; 24438 const tuple_ty = self.typeOfIndex(inst); 24439 const dst_ty = self.typeOf(bin_op.lhs); 24440 const result: MCValue = switch (dst_ty.zigTypeTag(zcu)) { 24441 .vector => return self.fail("TODO implement airMulWithOverflow for {}", .{dst_ty.fmt(pt)}), 24442 .int => result: { 24443 const dst_info = dst_ty.intInfo(zcu); 24444 if (dst_info.bits > 128 and dst_info.signedness == .unsigned) { 24445 const slow_inc = self.hasFeature(.slow_incdec); 24446 const abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 24447 const limb_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; 24448 24449 try self.spillRegisters(&.{ .rax, .rcx, .rdx }); 24450 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rax, .rcx, .rdx }); 24451 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24452 24453 const dst_mcv = try self.allocRegOrMem(inst, false); 24454 try self.genInlineMemset( 24455 dst_mcv.address(), 24456 .{ .immediate = 0 }, 24457 .{ .immediate = tuple_ty.abiSize(zcu) }, 24458 .{}, 24459 ); 24460 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24461 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24462 24463 const temp_regs = 24464 try self.register_manager.allocRegs(4, @splat(null), abi.RegisterClass.gp); 24465 const temp_locks = self.register_manager.lockRegsAssumeUnused(4, temp_regs); 24466 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 24467 24468 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[0].to32(), temp_regs[0].to32()); 24469 24470 const outer_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 24471 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[1].to64(), .{ 24472 .base = .{ .frame = rhs_mcv.load_frame.index }, 24473 .mod = .{ .rm = .{ 24474 .size = .qword, 24475 .index = temp_regs[0].to64(), 24476 .scale = .@"8", 24477 .disp = rhs_mcv.load_frame.off, 24478 } }, 24479 }); 24480 try self.asmRegisterRegister(.{ ._, .@"test" }, temp_regs[1].to64(), temp_regs[1].to64()); 24481 const skip_inner = try self.asmJccReloc(.z, undefined); 24482 24483 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[2].to32(), temp_regs[2].to32()); 24484 try self.asmRegisterRegister(.{ ._, .mov }, temp_regs[3].to32(), temp_regs[0].to32()); 24485 try self.asmRegisterRegister(.{ ._, .xor }, .ecx, .ecx); 24486 try self.asmRegisterRegister(.{ ._, .xor }, .edx, .edx); 24487 24488 const inner_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 24489 try self.asmRegisterImmediate(.{ ._r, .sh }, .cl, .u(1)); 24490 try self.asmMemoryRegister(.{ ._, .adc }, .{ 24491 .base = .{ .frame = dst_mcv.load_frame.index }, 24492 .mod = .{ .rm = .{ 24493 .size = .qword, 24494 .index = temp_regs[3].to64(), 24495 .scale = .@"8", 24496 .disp = dst_mcv.load_frame.off + 24497 @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))), 24498 } }, 24499 }, .rdx); 24500 try self.asmSetccRegister(.c, .cl); 24501 24502 try self.asmRegisterMemory(.{ ._, .mov }, .rax, .{ 24503 .base = .{ .frame = lhs_mcv.load_frame.index }, 24504 .mod = .{ .rm = .{ 24505 .size = .qword, 24506 .index = temp_regs[2].to64(), 24507 .scale = .@"8", 24508 .disp = lhs_mcv.load_frame.off, 24509 } }, 24510 }); 24511 try self.asmRegister(.{ ._, .mul }, temp_regs[1].to64()); 24512 24513 try self.asmRegisterImmediate(.{ ._r, .sh }, .ch, .u(1)); 24514 try self.asmMemoryRegister(.{ ._, .adc }, .{ 24515 .base = .{ .frame = dst_mcv.load_frame.index }, 24516 .mod = .{ .rm = .{ 24517 .size = .qword, 24518 .index = temp_regs[3].to64(), 24519 .scale = .@"8", 24520 .disp = dst_mcv.load_frame.off + 24521 @as(i32, @intCast(tuple_ty.structFieldOffset(0, zcu))), 24522 } }, 24523 }, .rax); 24524 try self.asmSetccRegister(.c, .ch); 24525 24526 if (slow_inc) { 24527 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), .u(1)); 24528 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[3].to32(), .u(1)); 24529 } else { 24530 try self.asmRegister(.{ ._c, .in }, temp_regs[2].to32()); 24531 try self.asmRegister(.{ ._c, .in }, temp_regs[3].to32()); 24532 } 24533 try self.asmRegisterImmediate(.{ ._, .cmp }, temp_regs[3].to32(), .u(limb_len)); 24534 _ = try self.asmJccReloc(.b, inner_loop); 24535 24536 try self.asmRegisterRegister(.{ ._, .@"or" }, .rdx, .rcx); 24537 const overflow = try self.asmJccReloc(.nz, undefined); 24538 const overflow_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 24539 try self.asmRegisterImmediate(.{ ._, .cmp }, temp_regs[2].to32(), .u(limb_len)); 24540 const no_overflow = try self.asmJccReloc(.nb, undefined); 24541 if (slow_inc) { 24542 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), .u(1)); 24543 } else { 24544 try self.asmRegister(.{ ._c, .in }, temp_regs[2].to32()); 24545 } 24546 try self.asmMemoryImmediate(.{ ._, .cmp }, .{ 24547 .base = .{ .frame = lhs_mcv.load_frame.index }, 24548 .mod = .{ .rm = .{ 24549 .size = .qword, 24550 .index = temp_regs[2].to64(), 24551 .scale = .@"8", 24552 .disp = lhs_mcv.load_frame.off - 8, 24553 } }, 24554 }, .u(0)); 24555 _ = try self.asmJccReloc(.z, overflow_loop); 24556 self.performReloc(overflow); 24557 try self.asmMemoryImmediate(.{ ._, .mov }, .{ 24558 .base = .{ .frame = dst_mcv.load_frame.index }, 24559 .mod = .{ .rm = .{ 24560 .size = .byte, 24561 .disp = dst_mcv.load_frame.off + 24562 @as(i32, @intCast(tuple_ty.structFieldOffset(1, zcu))), 24563 } }, 24564 }, .u(1)); 24565 self.performReloc(no_overflow); 24566 24567 self.performReloc(skip_inner); 24568 if (slow_inc) { 24569 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), .u(1)); 24570 } else { 24571 try self.asmRegister(.{ ._c, .in }, temp_regs[0].to32()); 24572 } 24573 try self.asmRegisterImmediate(.{ ._, .cmp }, temp_regs[0].to32(), .u(limb_len)); 24574 _ = try self.asmJccReloc(.b, outer_loop); 24575 24576 break :result dst_mcv; 24577 } 24578 24579 const lhs_active_bits = self.activeIntBits(bin_op.lhs); 24580 const rhs_active_bits = self.activeIntBits(bin_op.rhs); 24581 const src_bits = @max(lhs_active_bits, rhs_active_bits, dst_info.bits / 2); 24582 const src_ty = try pt.intType(dst_info.signedness, src_bits); 24583 if (src_bits > 64 and src_bits <= 128 and 24584 dst_info.bits > 64 and dst_info.bits <= 128) switch (dst_info.signedness) { 24585 .signed => { 24586 const ptr_c_int = try pt.singleMutPtrType(.c_int); 24587 const overflow = try self.allocTempRegOrMem(.c_int, false); 24588 const result = try self.genCall(.{ .lib = .{ 24589 .return_type = .i128_type, 24590 .param_types = &.{ .i128_type, .i128_type, ptr_c_int.toIntern() }, 24591 .callee = "__muloti4", 24592 } }, &.{ .i128, .i128, ptr_c_int }, &.{ 24593 .{ .air_ref = bin_op.lhs }, 24594 .{ .air_ref = bin_op.rhs }, 24595 overflow.address(), 24596 }, .{}); 24597 24598 const dst_mcv = try self.allocRegOrMem(inst, false); 24599 try self.genSetMem( 24600 .{ .frame = dst_mcv.load_frame.index }, 24601 @intCast(tuple_ty.structFieldOffset(0, zcu)), 24602 tuple_ty.fieldType(0, zcu), 24603 result, 24604 .{}, 24605 ); 24606 try self.asmMemoryImmediate( 24607 .{ ._, .cmp }, 24608 try overflow.mem(self, .{ .size = self.memSize(.c_int) }), 24609 .s(0), 24610 ); 24611 try self.genSetMem( 24612 .{ .frame = dst_mcv.load_frame.index }, 24613 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24614 tuple_ty.fieldType(1, zcu), 24615 .{ .eflags = .ne }, 24616 .{}, 24617 ); 24618 try self.freeValue(overflow); 24619 break :result dst_mcv; 24620 }, 24621 .unsigned => { 24622 try self.spillEflagsIfOccupied(); 24623 try self.spillRegisters(&.{ .rax, .rdx }); 24624 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); 24625 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24626 24627 const tmp_regs = 24628 try self.register_manager.allocRegs(4, @splat(null), abi.RegisterClass.gp); 24629 const tmp_locks = self.register_manager.lockRegsAssumeUnused(4, tmp_regs); 24630 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); 24631 24632 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24633 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24634 const mat_lhs_mcv = mat_lhs_mcv: switch (lhs_mcv) { 24635 .register => |lhs_reg| switch (lhs_reg.class()) { 24636 else => lhs_mcv, 24637 .sse => { 24638 const mat_lhs_mcv: MCValue = .{ 24639 .register_pair = try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp), 24640 }; 24641 try self.genCopy(dst_ty, mat_lhs_mcv, lhs_mcv, .{}); 24642 break :mat_lhs_mcv mat_lhs_mcv; 24643 }, 24644 }, 24645 .load_symbol => { 24646 // TODO clean this up! 24647 const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); 24648 break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 24649 }, 24650 else => lhs_mcv, 24651 }; 24652 const mat_lhs_locks: [2]?RegisterLock = switch (mat_lhs_mcv) { 24653 .register_pair => |mat_lhs_regs| self.register_manager.lockRegs(2, mat_lhs_regs), 24654 .indirect => |reg_off| .{ self.register_manager.lockReg(reg_off.reg), null }, 24655 else => @splat(null), 24656 }; 24657 defer for (mat_lhs_locks) |mat_lhs_lock| if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); 24658 const mat_rhs_mcv = mat_rhs_mcv: switch (rhs_mcv) { 24659 .register => |rhs_reg| switch (rhs_reg.class()) { 24660 else => rhs_mcv, 24661 .sse => { 24662 const mat_rhs_mcv: MCValue = .{ 24663 .register_pair = try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp), 24664 }; 24665 try self.genCopy(dst_ty, mat_rhs_mcv, rhs_mcv, .{}); 24666 break :mat_rhs_mcv mat_rhs_mcv; 24667 }, 24668 }, 24669 .load_symbol => { 24670 // TODO clean this up! 24671 const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); 24672 break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 24673 }, 24674 else => rhs_mcv, 24675 }; 24676 const mat_rhs_locks: [2]?RegisterLock = switch (mat_rhs_mcv) { 24677 .register_pair => |mat_rhs_regs| self.register_manager.lockRegs(2, mat_rhs_regs), 24678 .indirect => |reg_off| .{ self.register_manager.lockReg(reg_off.reg), null }, 24679 else => @splat(null), 24680 }; 24681 defer for (mat_rhs_locks) |mat_rhs_lock| if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); 24682 24683 if (mat_lhs_mcv.isBase()) try self.asmRegisterMemory( 24684 .{ ._, .mov }, 24685 .rax, 24686 try mat_lhs_mcv.mem(self, .{ .size = .qword }), 24687 ) else try self.asmRegisterRegister( 24688 .{ ._, .mov }, 24689 .rax, 24690 mat_lhs_mcv.register_pair[0], 24691 ); 24692 if (mat_rhs_mcv.isBase()) try self.asmRegisterMemory( 24693 .{ ._, .mov }, 24694 tmp_regs[0], 24695 try mat_rhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 24696 ) else try self.asmRegisterRegister( 24697 .{ ._, .mov }, 24698 tmp_regs[0], 24699 mat_rhs_mcv.register_pair[1], 24700 ); 24701 try self.asmRegisterRegister(.{ ._, .@"test" }, tmp_regs[0], tmp_regs[0]); 24702 try self.asmSetccRegister(.nz, tmp_regs[1].to8()); 24703 try self.asmRegisterRegister(.{ .i_, .mul }, tmp_regs[0], .rax); 24704 try self.asmSetccRegister(.o, tmp_regs[2].to8()); 24705 if (mat_rhs_mcv.isBase()) 24706 try self.asmMemory(.{ ._, .mul }, try mat_rhs_mcv.mem(self, .{ .size = .qword })) 24707 else 24708 try self.asmRegister(.{ ._, .mul }, mat_rhs_mcv.register_pair[0]); 24709 try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_regs[0]); 24710 try self.asmSetccRegister(.c, tmp_regs[3].to8()); 24711 try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[2].to8(), tmp_regs[3].to8()); 24712 if (mat_lhs_mcv.isBase()) try self.asmRegisterMemory( 24713 .{ ._, .mov }, 24714 tmp_regs[0], 24715 try mat_lhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 24716 ) else try self.asmRegisterRegister( 24717 .{ ._, .mov }, 24718 tmp_regs[0], 24719 mat_lhs_mcv.register_pair[1], 24720 ); 24721 try self.asmRegisterRegister(.{ ._, .@"test" }, tmp_regs[0], tmp_regs[0]); 24722 try self.asmSetccRegister(.nz, tmp_regs[3].to8()); 24723 try self.asmRegisterRegister( 24724 .{ ._, .@"and" }, 24725 tmp_regs[1].to8(), 24726 tmp_regs[3].to8(), 24727 ); 24728 try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[1].to8(), tmp_regs[2].to8()); 24729 if (mat_rhs_mcv.isBase()) try self.asmRegisterMemory( 24730 .{ .i_, .mul }, 24731 tmp_regs[0], 24732 try mat_rhs_mcv.mem(self, .{ .size = .qword }), 24733 ) else try self.asmRegisterRegister( 24734 .{ .i_, .mul }, 24735 tmp_regs[0], 24736 mat_rhs_mcv.register_pair[0], 24737 ); 24738 try self.asmSetccRegister(.o, tmp_regs[2].to8()); 24739 try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[1].to8(), tmp_regs[2].to8()); 24740 try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_regs[0]); 24741 try self.asmSetccRegister(.c, tmp_regs[2].to8()); 24742 try self.asmRegisterRegister(.{ ._, .@"or" }, tmp_regs[1].to8(), tmp_regs[2].to8()); 24743 24744 const dst_mcv = try self.allocRegOrMem(inst, false); 24745 try self.genSetMem( 24746 .{ .frame = dst_mcv.load_frame.index }, 24747 @intCast(tuple_ty.structFieldOffset(0, zcu)), 24748 tuple_ty.fieldType(0, zcu), 24749 .{ .register_pair = .{ .rax, .rdx } }, 24750 .{}, 24751 ); 24752 try self.genSetMem( 24753 .{ .frame = dst_mcv.load_frame.index }, 24754 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24755 tuple_ty.fieldType(1, zcu), 24756 .{ .register = tmp_regs[1] }, 24757 .{}, 24758 ); 24759 break :result dst_mcv; 24760 }, 24761 }; 24762 24763 try self.spillEflagsIfOccupied(); 24764 try self.spillRegisters(&.{ .rax, .rcx, .rdx, .rdi, .rsi }); 24765 const reg_locks = self.register_manager.lockRegsAssumeUnused(5, .{ .rax, .rcx, .rdx, .rdi, .rsi }); 24766 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 24767 24768 const cc: Condition = switch (dst_info.signedness) { 24769 .unsigned => .c, 24770 .signed => .o, 24771 }; 24772 24773 const lhs = try self.resolveInst(bin_op.lhs); 24774 const rhs = try self.resolveInst(bin_op.rhs); 24775 24776 const extra_bits = if (dst_info.bits <= 64) 24777 self.regExtraBits(dst_ty) 24778 else 24779 dst_info.bits % 64; 24780 const partial_mcv = try self.genMulDivBinOp(.mul, null, dst_ty, src_ty, lhs, rhs); 24781 24782 switch (partial_mcv) { 24783 .register => |reg| if (extra_bits == 0) { 24784 self.eflags_inst = inst; 24785 break :result .{ .register_overflow = .{ .reg = reg, .eflags = cc } }; 24786 } else { 24787 const frame_index = try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24788 try self.genSetFrameTruncatedOverflowCompare(tuple_ty, frame_index, partial_mcv, cc); 24789 break :result .{ .load_frame = .{ .index = frame_index } }; 24790 }, 24791 else => { 24792 // For now, this is the only supported multiply that doesn't fit in a register. 24793 if (dst_info.bits > 128 or src_bits != 64) 24794 return self.fail("TODO implement airWithOverflow from {} to {}", .{ 24795 src_ty.fmt(pt), dst_ty.fmt(pt), 24796 }); 24797 24798 const frame_index = try self.allocFrameIndex(.initSpill(tuple_ty, zcu)); 24799 if (dst_info.bits >= lhs_active_bits + rhs_active_bits) { 24800 try self.genSetMem( 24801 .{ .frame = frame_index }, 24802 @intCast(tuple_ty.structFieldOffset(0, zcu)), 24803 tuple_ty.fieldType(0, zcu), 24804 partial_mcv, 24805 .{}, 24806 ); 24807 try self.genSetMem( 24808 .{ .frame = frame_index }, 24809 @intCast(tuple_ty.structFieldOffset(1, zcu)), 24810 tuple_ty.fieldType(1, zcu), 24811 .{ .immediate = 0 }, // cc being set is impossible 24812 .{}, 24813 ); 24814 } else try self.genSetFrameTruncatedOverflowCompare( 24815 tuple_ty, 24816 frame_index, 24817 partial_mcv, 24818 null, 24819 ); 24820 break :result .{ .load_frame = .{ .index = frame_index } }; 24821 }, 24822 } 24823 }, 24824 else => unreachable, 24825 }; 24826 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 24827 } 24828 24829 /// Generates signed or unsigned integer multiplication/division. 24830 /// Clobbers .rax and .rdx registers. 24831 /// Quotient is saved in .rax and remainder in .rdx. 24832 fn genIntMulDivOpMir(self: *CodeGen, tag: Mir.Inst.FixedTag, ty: Type, lhs: MCValue, rhs: MCValue) !void { 24833 const pt = self.pt; 24834 const abi_size: u32 = @intCast(ty.abiSize(pt.zcu)); 24835 const bit_size: u32 = @intCast(self.regBitSize(ty)); 24836 if (abi_size > 8) { 24837 return self.fail("TODO implement genIntMulDivOpMir for ABI size larger than 8", .{}); 24838 } 24839 24840 try self.genSetReg(.rax, ty, lhs, .{}); 24841 switch (tag[1]) { 24842 else => unreachable, 24843 .mul => {}, 24844 .div => switch (tag[0]) { 24845 ._ => { 24846 const hi_reg: Register = 24847 switch (bit_size) { 24848 8 => .ah, 24849 16, 32, 64 => .edx, 24850 else => unreachable, 24851 }; 24852 try self.asmRegisterRegister(.{ ._, .xor }, hi_reg, hi_reg); 24853 }, 24854 .i_ => try self.asmOpOnly(.{ ._, switch (bit_size) { 24855 8 => .cbw, 24856 16 => .cwd, 24857 32 => .cdq, 24858 64 => .cqo, 24859 else => unreachable, 24860 } }), 24861 else => unreachable, 24862 }, 24863 } 24864 24865 const mat_rhs: MCValue = switch (rhs) { 24866 .register, .indirect, .load_frame => rhs, 24867 else => .{ .register = try self.copyToTmpRegister(ty, rhs) }, 24868 }; 24869 switch (mat_rhs) { 24870 .register => |reg| try self.asmRegister(tag, registerAlias(reg, abi_size)), 24871 .memory, .indirect, .load_frame => try self.asmMemory( 24872 tag, 24873 try mat_rhs.mem(self, .{ .size = .fromSize(abi_size) }), 24874 ), 24875 else => unreachable, 24876 } 24877 if (tag[1] == .div and bit_size == 8) try self.asmRegisterRegister(.{ ._, .mov }, .dl, .ah); 24878 } 24879 24880 /// Always returns a register. 24881 /// Clobbers .rax and .rdx registers. 24882 fn genInlineIntDivFloor(self: *CodeGen, ty: Type, lhs: MCValue, rhs: MCValue) !MCValue { 24883 const pt = self.pt; 24884 const zcu = pt.zcu; 24885 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 24886 const int_info = ty.intInfo(zcu); 24887 const dividend = switch (lhs) { 24888 .register => |reg| reg, 24889 else => try self.copyToTmpRegister(ty, lhs), 24890 }; 24891 const dividend_lock = self.register_manager.lockReg(dividend); 24892 defer if (dividend_lock) |lock| self.register_manager.unlockReg(lock); 24893 24894 const divisor = switch (rhs) { 24895 .register => |reg| reg, 24896 else => try self.copyToTmpRegister(ty, rhs), 24897 }; 24898 const divisor_lock = self.register_manager.lockReg(divisor); 24899 defer if (divisor_lock) |lock| self.register_manager.unlockReg(lock); 24900 24901 try self.genIntMulDivOpMir( 24902 switch (int_info.signedness) { 24903 .signed => .{ .i_, .div }, 24904 .unsigned => .{ ._, .div }, 24905 }, 24906 ty, 24907 .{ .register = dividend }, 24908 .{ .register = divisor }, 24909 ); 24910 24911 try self.asmRegisterRegister( 24912 .{ ._, .xor }, 24913 registerAlias(divisor, abi_size), 24914 registerAlias(dividend, abi_size), 24915 ); 24916 try self.asmRegisterImmediate( 24917 .{ ._r, .sa }, 24918 registerAlias(divisor, abi_size), 24919 .u(int_info.bits - 1), 24920 ); 24921 try self.asmRegisterRegister( 24922 .{ ._, .@"test" }, 24923 registerAlias(.rdx, abi_size), 24924 registerAlias(.rdx, abi_size), 24925 ); 24926 try self.asmCmovccRegisterRegister( 24927 .z, 24928 registerAlias(divisor, @max(abi_size, 2)), 24929 registerAlias(.rdx, @max(abi_size, 2)), 24930 ); 24931 try self.genBinOpMir(.{ ._, .add }, ty, .{ .register = divisor }, .{ .register = .rax }); 24932 return MCValue{ .register = divisor }; 24933 } 24934 24935 fn airShlShrBinOp(self: *CodeGen, inst: Air.Inst.Index) !void { 24936 const pt = self.pt; 24937 const zcu = pt.zcu; 24938 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 24939 24940 const air_tags = self.air.instructions.items(.tag); 24941 const tag = air_tags[@intFromEnum(inst)]; 24942 const lhs_ty = self.typeOf(bin_op.lhs); 24943 const rhs_ty = self.typeOf(bin_op.rhs); 24944 const result: MCValue = result: { 24945 switch (lhs_ty.zigTypeTag(zcu)) { 24946 .int => { 24947 try self.spillRegisters(&.{.rcx}); 24948 try self.register_manager.getKnownReg(.rcx, null); 24949 const lhs_mcv = try self.resolveInst(bin_op.lhs); 24950 const rhs_mcv = try self.resolveInst(bin_op.rhs); 24951 24952 const dst_mcv = try self.genShiftBinOp(tag, inst, lhs_mcv, rhs_mcv, lhs_ty, rhs_ty); 24953 switch (tag) { 24954 .shr, .shr_exact, .shl_exact => {}, 24955 .shl => switch (dst_mcv) { 24956 .register => |dst_reg| try self.truncateRegister(lhs_ty, dst_reg), 24957 .register_pair => |dst_regs| try self.truncateRegister(lhs_ty, dst_regs[1]), 24958 .load_frame => |frame_addr| { 24959 const tmp_reg = 24960 try self.register_manager.allocReg(null, abi.RegisterClass.gp); 24961 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 24962 defer self.register_manager.unlockReg(tmp_lock); 24963 24964 const lhs_bits: u31 = @intCast(lhs_ty.bitSize(zcu)); 24965 const tmp_ty: Type = if (lhs_bits > 64) .usize else lhs_ty; 24966 const off = frame_addr.off + (lhs_bits - 1) / 64 * 8; 24967 try self.genSetReg( 24968 tmp_reg, 24969 tmp_ty, 24970 .{ .load_frame = .{ .index = frame_addr.index, .off = off } }, 24971 .{}, 24972 ); 24973 try self.truncateRegister(lhs_ty, tmp_reg); 24974 try self.genSetMem( 24975 .{ .frame = frame_addr.index }, 24976 off, 24977 tmp_ty, 24978 .{ .register = tmp_reg }, 24979 .{}, 24980 ); 24981 }, 24982 else => {}, 24983 }, 24984 else => unreachable, 24985 } 24986 break :result dst_mcv; 24987 }, 24988 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 24989 .int => if (@as(?Mir.Inst.FixedTag, switch (lhs_ty.childType(zcu).intInfo(zcu).bits) { 24990 else => null, 24991 16 => switch (lhs_ty.vectorLen(zcu)) { 24992 else => null, 24993 1...8 => switch (tag) { 24994 else => unreachable, 24995 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 24996 .signed => if (self.hasFeature(.avx)) 24997 .{ .vp_w, .sra } 24998 else 24999 .{ .p_w, .sra }, 25000 .unsigned => if (self.hasFeature(.avx)) 25001 .{ .vp_w, .srl } 25002 else 25003 .{ .p_w, .srl }, 25004 }, 25005 .shl, .shl_exact => if (self.hasFeature(.avx)) 25006 .{ .vp_w, .sll } 25007 else 25008 .{ .p_w, .sll }, 25009 }, 25010 9...16 => switch (tag) { 25011 else => unreachable, 25012 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 25013 .signed => if (self.hasFeature(.avx2)) .{ .vp_w, .sra } else null, 25014 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_w, .srl } else null, 25015 }, 25016 .shl, .shl_exact => if (self.hasFeature(.avx2)) .{ .vp_w, .sll } else null, 25017 }, 25018 }, 25019 32 => switch (lhs_ty.vectorLen(zcu)) { 25020 else => null, 25021 1...4 => switch (tag) { 25022 else => unreachable, 25023 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 25024 .signed => if (self.hasFeature(.avx)) 25025 .{ .vp_d, .sra } 25026 else 25027 .{ .p_d, .sra }, 25028 .unsigned => if (self.hasFeature(.avx)) 25029 .{ .vp_d, .srl } 25030 else 25031 .{ .p_d, .srl }, 25032 }, 25033 .shl, .shl_exact => if (self.hasFeature(.avx)) 25034 .{ .vp_d, .sll } 25035 else 25036 .{ .p_d, .sll }, 25037 }, 25038 5...8 => switch (tag) { 25039 else => unreachable, 25040 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 25041 .signed => if (self.hasFeature(.avx2)) .{ .vp_d, .sra } else null, 25042 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_d, .srl } else null, 25043 }, 25044 .shl, .shl_exact => if (self.hasFeature(.avx2)) .{ .vp_d, .sll } else null, 25045 }, 25046 }, 25047 64 => switch (lhs_ty.vectorLen(zcu)) { 25048 else => null, 25049 1...2 => switch (tag) { 25050 else => unreachable, 25051 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 25052 .signed => if (self.hasFeature(.avx)) 25053 .{ .vp_q, .sra } 25054 else 25055 .{ .p_q, .sra }, 25056 .unsigned => if (self.hasFeature(.avx)) 25057 .{ .vp_q, .srl } 25058 else 25059 .{ .p_q, .srl }, 25060 }, 25061 .shl, .shl_exact => if (self.hasFeature(.avx)) 25062 .{ .vp_q, .sll } 25063 else 25064 .{ .p_q, .sll }, 25065 }, 25066 3...4 => switch (tag) { 25067 else => unreachable, 25068 .shr, .shr_exact => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 25069 .signed => if (self.hasFeature(.avx2)) .{ .vp_q, .sra } else null, 25070 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_q, .srl } else null, 25071 }, 25072 .shl, .shl_exact => if (self.hasFeature(.avx2)) .{ .vp_q, .sll } else null, 25073 }, 25074 }, 25075 })) |mir_tag| if (try self.air.value(bin_op.rhs, pt)) |rhs_val| { 25076 switch (zcu.intern_pool.indexToKey(rhs_val.toIntern())) { 25077 .aggregate => |rhs_aggregate| switch (rhs_aggregate.storage) { 25078 .repeated_elem => |rhs_elem| { 25079 const abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); 25080 25081 const lhs_mcv = try self.resolveInst(bin_op.lhs); 25082 const dst_reg, const lhs_reg = if (lhs_mcv.isRegister() and 25083 self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) 25084 .{lhs_mcv.getReg().?} ** 2 25085 else if (lhs_mcv.isRegister() and self.hasFeature(.avx)) .{ 25086 try self.register_manager.allocReg(inst, abi.RegisterClass.sse), 25087 lhs_mcv.getReg().?, 25088 } else .{(try self.copyToRegisterWithInstTracking( 25089 inst, 25090 lhs_ty, 25091 lhs_mcv, 25092 )).register} ** 2; 25093 const reg_locks = 25094 self.register_manager.lockRegs(2, .{ dst_reg, lhs_reg }); 25095 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| 25096 self.register_manager.unlockReg(lock); 25097 25098 const shift_imm: Immediate = 25099 .u(@intCast(Value.fromInterned(rhs_elem).toUnsignedInt(zcu))); 25100 if (self.hasFeature(.avx)) try self.asmRegisterRegisterImmediate( 25101 mir_tag, 25102 registerAlias(dst_reg, abi_size), 25103 registerAlias(lhs_reg, abi_size), 25104 shift_imm, 25105 ) else { 25106 assert(dst_reg.id() == lhs_reg.id()); 25107 try self.asmRegisterImmediate( 25108 mir_tag, 25109 registerAlias(dst_reg, abi_size), 25110 shift_imm, 25111 ); 25112 } 25113 break :result .{ .register = dst_reg }; 25114 }, 25115 else => {}, 25116 }, 25117 else => {}, 25118 } 25119 } else if (bin_op.rhs.toIndex()) |rhs_inst| switch (air_tags[@intFromEnum(rhs_inst)]) { 25120 .splat => { 25121 const abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); 25122 25123 const lhs_mcv = try self.resolveInst(bin_op.lhs); 25124 const dst_reg, const lhs_reg = if (lhs_mcv.isRegister() and 25125 self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) 25126 .{lhs_mcv.getReg().?} ** 2 25127 else if (lhs_mcv.isRegister() and self.hasFeature(.avx)) .{ 25128 try self.register_manager.allocReg(inst, abi.RegisterClass.sse), 25129 lhs_mcv.getReg().?, 25130 } else .{(try self.copyToRegisterWithInstTracking( 25131 inst, 25132 lhs_ty, 25133 lhs_mcv, 25134 )).register} ** 2; 25135 const reg_locks = self.register_manager.lockRegs(2, .{ dst_reg, lhs_reg }); 25136 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| 25137 self.register_manager.unlockReg(lock); 25138 25139 const shift_reg = 25140 try self.copyToTmpRegister(rhs_ty, .{ .air_ref = bin_op.rhs }); 25141 const shift_lock = self.register_manager.lockRegAssumeUnused(shift_reg); 25142 defer self.register_manager.unlockReg(shift_lock); 25143 25144 const mask_ty = try pt.vectorType(.{ .len = 16, .child = .u8_type }); 25145 const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 25146 .ty = mask_ty.toIntern(), 25147 .storage = .{ .elems = &([1]InternPool.Index{ 25148 (try rhs_ty.childType(zcu).maxIntScalar(pt, .u8)).toIntern(), 25149 } ++ [1]InternPool.Index{ 25150 (try pt.intValue(.u8, 0)).toIntern(), 25151 } ** 15) }, 25152 } }))); 25153 const mask_addr_reg = try self.copyToTmpRegister(.usize, mask_mcv.address()); 25154 const mask_addr_lock = self.register_manager.lockRegAssumeUnused(mask_addr_reg); 25155 defer self.register_manager.unlockReg(mask_addr_lock); 25156 25157 if (self.hasFeature(.avx)) { 25158 try self.asmRegisterRegisterMemory( 25159 .{ .vp_, .@"and" }, 25160 shift_reg.to128(), 25161 shift_reg.to128(), 25162 .{ 25163 .base = .{ .reg = mask_addr_reg }, 25164 .mod = .{ .rm = .{ .size = .xword } }, 25165 }, 25166 ); 25167 try self.asmRegisterRegisterRegister( 25168 mir_tag, 25169 registerAlias(dst_reg, abi_size), 25170 registerAlias(lhs_reg, abi_size), 25171 shift_reg.to128(), 25172 ); 25173 } else { 25174 try self.asmRegisterMemory( 25175 .{ .p_, .@"and" }, 25176 shift_reg.to128(), 25177 .{ 25178 .base = .{ .reg = mask_addr_reg }, 25179 .mod = .{ .rm = .{ .size = .xword } }, 25180 }, 25181 ); 25182 assert(dst_reg.id() == lhs_reg.id()); 25183 try self.asmRegisterRegister( 25184 mir_tag, 25185 registerAlias(dst_reg, abi_size), 25186 shift_reg.to128(), 25187 ); 25188 } 25189 break :result .{ .register = dst_reg }; 25190 }, 25191 else => {}, 25192 }, 25193 else => {}, 25194 }, 25195 else => {}, 25196 } 25197 return self.fail("TODO implement airShlShrBinOp for {}", .{lhs_ty.fmt(pt)}); 25198 }; 25199 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 25200 } 25201 25202 fn airShlSat(self: *CodeGen, inst: Air.Inst.Index) !void { 25203 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 25204 _ = bin_op; 25205 return self.fail("TODO implement shl_sat for {}", .{self.target.cpu.arch}); 25206 //return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 25207 } 25208 25209 fn airOptionalPayload(self: *CodeGen, inst: Air.Inst.Index) !void { 25210 const zcu = self.pt.zcu; 25211 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25212 const result: MCValue = result: { 25213 const pl_ty = self.typeOfIndex(inst); 25214 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 25215 25216 const opt_mcv = try self.resolveInst(ty_op.operand); 25217 if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) { 25218 const pl_mcv: MCValue = switch (opt_mcv) { 25219 .register_overflow => |ro| pl: { 25220 self.eflags_inst = null; // actually stop tracking the overflow part 25221 break :pl .{ .register = ro.reg }; 25222 }, 25223 else => opt_mcv, 25224 }; 25225 switch (pl_mcv) { 25226 .register => |pl_reg| try self.truncateRegister(pl_ty, pl_reg), 25227 else => {}, 25228 } 25229 break :result pl_mcv; 25230 } 25231 25232 const pl_mcv = try self.allocRegOrMem(inst, true); 25233 try self.genCopy(pl_ty, pl_mcv, switch (opt_mcv) { 25234 else => opt_mcv, 25235 .register_overflow => |ro| .{ .register = ro.reg }, 25236 }, .{}); 25237 break :result pl_mcv; 25238 }; 25239 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25240 } 25241 25242 fn airOptionalPayloadPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25243 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25244 25245 const dst_ty = self.typeOfIndex(inst); 25246 const opt_mcv = try self.resolveInst(ty_op.operand); 25247 25248 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) 25249 opt_mcv 25250 else 25251 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv); 25252 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 25253 } 25254 25255 fn airOptionalPayloadPtrSet(self: *CodeGen, inst: Air.Inst.Index) !void { 25256 const pt = self.pt; 25257 const zcu = pt.zcu; 25258 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25259 const result = result: { 25260 const dst_ty = self.typeOfIndex(inst); 25261 const src_ty = self.typeOf(ty_op.operand); 25262 const opt_ty = src_ty.childType(zcu); 25263 const src_mcv = try self.resolveInst(ty_op.operand); 25264 25265 if (opt_ty.optionalReprIsPayload(zcu)) { 25266 break :result if (self.liveness.isUnused(inst)) 25267 .unreach 25268 else if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 25269 src_mcv 25270 else 25271 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); 25272 } 25273 25274 const dst_mcv: MCValue = if (src_mcv.isRegister() and 25275 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 25276 src_mcv 25277 else if (self.liveness.isUnused(inst)) 25278 .{ .register = try self.copyToTmpRegister(dst_ty, src_mcv) } 25279 else 25280 try self.copyToRegisterWithInstTracking(inst, dst_ty, src_mcv); 25281 25282 const pl_ty = dst_ty.childType(zcu); 25283 const pl_abi_size: i32 = @intCast(pl_ty.abiSize(zcu)); 25284 try self.genSetMem( 25285 .{ .reg = dst_mcv.getReg().? }, 25286 pl_abi_size, 25287 .bool, 25288 .{ .immediate = 1 }, 25289 .{}, 25290 ); 25291 break :result if (self.liveness.isUnused(inst)) .unreach else dst_mcv; 25292 }; 25293 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25294 } 25295 25296 fn airUnwrapErrUnionErr(self: *CodeGen, inst: Air.Inst.Index) !void { 25297 const pt = self.pt; 25298 const zcu = pt.zcu; 25299 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25300 const err_union_ty = self.typeOf(ty_op.operand); 25301 const err_ty = err_union_ty.errorUnionSet(zcu); 25302 const payload_ty = err_union_ty.errorUnionPayload(zcu); 25303 const operand = try self.resolveInst(ty_op.operand); 25304 25305 const result: MCValue = result: { 25306 if (err_ty.errorSetIsEmpty(zcu)) { 25307 break :result MCValue{ .immediate = 0 }; 25308 } 25309 25310 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) { 25311 break :result operand; 25312 } 25313 25314 const err_off = codegen.errUnionErrorOffset(payload_ty, zcu); 25315 switch (operand) { 25316 .register => |reg| { 25317 // TODO reuse operand 25318 const eu_lock = self.register_manager.lockReg(reg); 25319 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); 25320 25321 const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); 25322 if (err_off > 0) try self.genShiftBinOpMir( 25323 .{ ._r, .sh }, 25324 err_union_ty, 25325 result, 25326 .u8, 25327 .{ .immediate = @as(u6, @intCast(err_off * 8)) }, 25328 ) else try self.truncateRegister(.anyerror, result.register); 25329 break :result result; 25330 }, 25331 .load_frame => |frame_addr| break :result .{ .load_frame = .{ 25332 .index = frame_addr.index, 25333 .off = frame_addr.off + @as(i32, @intCast(err_off)), 25334 } }, 25335 else => return self.fail("TODO implement unwrap_err_err for {}", .{operand}), 25336 } 25337 }; 25338 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25339 } 25340 25341 fn airUnwrapErrUnionPayload(self: *CodeGen, inst: Air.Inst.Index) !void { 25342 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25343 const operand_ty = self.typeOf(ty_op.operand); 25344 const operand = try self.resolveInst(ty_op.operand); 25345 const result = try self.genUnwrapErrUnionPayloadMir(inst, operand_ty, operand); 25346 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25347 } 25348 25349 // *(E!T) -> E 25350 fn airUnwrapErrUnionErrPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25351 const pt = self.pt; 25352 const zcu = pt.zcu; 25353 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25354 25355 const src_ty = self.typeOf(ty_op.operand); 25356 const src_mcv = try self.resolveInst(ty_op.operand); 25357 const src_reg = switch (src_mcv) { 25358 .register => |reg| reg, 25359 else => try self.copyToTmpRegister(src_ty, src_mcv), 25360 }; 25361 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 25362 defer self.register_manager.unlockReg(src_lock); 25363 25364 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 25365 const dst_mcv = MCValue{ .register = dst_reg }; 25366 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 25367 defer self.register_manager.unlockReg(dst_lock); 25368 25369 const eu_ty = src_ty.childType(zcu); 25370 const pl_ty = eu_ty.errorUnionPayload(zcu); 25371 const err_ty = eu_ty.errorUnionSet(zcu); 25372 const err_off: i32 = @intCast(codegen.errUnionErrorOffset(pl_ty, zcu)); 25373 const err_abi_size: u32 = @intCast(err_ty.abiSize(zcu)); 25374 try self.asmRegisterMemory( 25375 .{ ._, .mov }, 25376 registerAlias(dst_reg, err_abi_size), 25377 .{ 25378 .base = .{ .reg = src_reg }, 25379 .mod = .{ .rm = .{ 25380 .size = .fromSize(err_abi_size), 25381 .disp = err_off, 25382 } }, 25383 }, 25384 ); 25385 25386 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 25387 } 25388 25389 // *(E!T) -> *T 25390 fn airUnwrapErrUnionPayloadPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25391 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25392 const operand_ty = self.typeOf(ty_op.operand); 25393 const operand = try self.resolveInst(ty_op.operand); 25394 const result = try self.genUnwrapErrUnionPayloadPtrMir(inst, operand_ty, operand); 25395 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25396 } 25397 25398 fn airErrUnionPayloadPtrSet(self: *CodeGen, inst: Air.Inst.Index) !void { 25399 const pt = self.pt; 25400 const zcu = pt.zcu; 25401 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25402 const result: MCValue = result: { 25403 const src_ty = self.typeOf(ty_op.operand); 25404 const src_mcv = try self.resolveInst(ty_op.operand); 25405 const src_reg = switch (src_mcv) { 25406 .register => |reg| reg, 25407 else => try self.copyToTmpRegister(src_ty, src_mcv), 25408 }; 25409 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 25410 defer self.register_manager.unlockReg(src_lock); 25411 25412 const eu_ty = src_ty.childType(zcu); 25413 const pl_ty = eu_ty.errorUnionPayload(zcu); 25414 const err_ty = eu_ty.errorUnionSet(zcu); 25415 const err_off: i32 = @intCast(codegen.errUnionErrorOffset(pl_ty, zcu)); 25416 const err_abi_size: u32 = @intCast(err_ty.abiSize(zcu)); 25417 try self.asmMemoryImmediate( 25418 .{ ._, .mov }, 25419 .{ 25420 .base = .{ .reg = src_reg }, 25421 .mod = .{ .rm = .{ 25422 .size = .fromSize(err_abi_size), 25423 .disp = err_off, 25424 } }, 25425 }, 25426 .u(0), 25427 ); 25428 25429 if (self.liveness.isUnused(inst)) break :result .unreach; 25430 25431 const dst_ty = self.typeOfIndex(inst); 25432 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 25433 src_reg 25434 else 25435 try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 25436 const dst_lock = self.register_manager.lockReg(dst_reg); 25437 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 25438 25439 const pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(pl_ty, zcu)); 25440 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 25441 try self.asmRegisterMemory( 25442 .{ ._, .lea }, 25443 registerAlias(dst_reg, dst_abi_size), 25444 .{ 25445 .base = .{ .reg = src_reg }, 25446 .mod = .{ .rm = .{ .size = .qword, .disp = pl_off } }, 25447 }, 25448 ); 25449 break :result .{ .register = dst_reg }; 25450 }; 25451 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25452 } 25453 25454 fn genUnwrapErrUnionPayloadMir( 25455 self: *CodeGen, 25456 maybe_inst: ?Air.Inst.Index, 25457 err_union_ty: Type, 25458 err_union: MCValue, 25459 ) !MCValue { 25460 const pt = self.pt; 25461 const zcu = pt.zcu; 25462 const payload_ty = err_union_ty.errorUnionPayload(zcu); 25463 25464 const result: MCValue = result: { 25465 if (!payload_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 25466 25467 const payload_off: u31 = @intCast(codegen.errUnionPayloadOffset(payload_ty, zcu)); 25468 switch (err_union) { 25469 .load_frame => |frame_addr| break :result .{ .load_frame = .{ 25470 .index = frame_addr.index, 25471 .off = frame_addr.off + payload_off, 25472 } }, 25473 .register => |reg| { 25474 // TODO reuse operand 25475 const eu_lock = self.register_manager.lockReg(reg); 25476 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); 25477 25478 const payload_in_gp = self.regSetForType(payload_ty).supersetOf(abi.RegisterClass.gp); 25479 const result_mcv: MCValue = if (payload_in_gp and maybe_inst != null) 25480 try self.copyToRegisterWithInstTracking(maybe_inst.?, err_union_ty, err_union) 25481 else 25482 .{ .register = try self.copyToTmpRegister(err_union_ty, err_union) }; 25483 if (payload_off > 0) try self.genShiftBinOpMir( 25484 .{ ._r, .sh }, 25485 err_union_ty, 25486 result_mcv, 25487 .u8, 25488 .{ .immediate = @as(u6, @intCast(payload_off * 8)) }, 25489 ) else try self.truncateRegister(payload_ty, result_mcv.register); 25490 break :result if (payload_in_gp) 25491 result_mcv 25492 else if (maybe_inst) |inst| 25493 try self.copyToRegisterWithInstTracking(inst, payload_ty, result_mcv) 25494 else 25495 .{ .register = try self.copyToTmpRegister(payload_ty, result_mcv) }; 25496 }, 25497 else => return self.fail("TODO implement genUnwrapErrUnionPayloadMir for {}", .{err_union}), 25498 } 25499 }; 25500 25501 return result; 25502 } 25503 25504 fn genUnwrapErrUnionPayloadPtrMir( 25505 self: *CodeGen, 25506 maybe_inst: ?Air.Inst.Index, 25507 ptr_ty: Type, 25508 ptr_mcv: MCValue, 25509 ) !MCValue { 25510 const pt = self.pt; 25511 const zcu = pt.zcu; 25512 const err_union_ty = ptr_ty.childType(zcu); 25513 const payload_ty = err_union_ty.errorUnionPayload(zcu); 25514 25515 const result: MCValue = result: { 25516 const payload_off = codegen.errUnionPayloadOffset(payload_ty, zcu); 25517 const result_mcv: MCValue = if (maybe_inst) |inst| 25518 try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr_mcv) 25519 else 25520 .{ .register = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }; 25521 try self.genBinOpMir(.{ ._, .add }, ptr_ty, result_mcv, .{ .immediate = payload_off }); 25522 break :result result_mcv; 25523 }; 25524 25525 return result; 25526 } 25527 25528 fn airWrapOptional(self: *CodeGen, inst: Air.Inst.Index) !void { 25529 const pt = self.pt; 25530 const zcu = pt.zcu; 25531 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25532 const result: MCValue = result: { 25533 const pl_ty = self.typeOf(ty_op.operand); 25534 if (!pl_ty.hasRuntimeBits(zcu)) break :result .{ .immediate = 1 }; 25535 25536 const opt_ty = self.typeOfIndex(inst); 25537 const pl_mcv = try self.resolveInst(ty_op.operand); 25538 const same_repr = opt_ty.optionalReprIsPayload(zcu); 25539 if (same_repr and self.reuseOperand(inst, ty_op.operand, 0, pl_mcv)) break :result pl_mcv; 25540 25541 const pl_lock: ?RegisterLock = switch (pl_mcv) { 25542 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 25543 else => null, 25544 }; 25545 defer if (pl_lock) |lock| self.register_manager.unlockReg(lock); 25546 25547 const opt_mcv = try self.allocRegOrMem(inst, true); 25548 try self.genCopy(pl_ty, opt_mcv, pl_mcv, .{}); 25549 25550 if (!same_repr) { 25551 const pl_abi_size: i32 = @intCast(pl_ty.abiSize(zcu)); 25552 switch (opt_mcv) { 25553 else => unreachable, 25554 25555 .register => |opt_reg| { 25556 try self.truncateRegister(pl_ty, opt_reg); 25557 try self.asmRegisterImmediate( 25558 .{ ._s, .bt }, 25559 opt_reg, 25560 .u(@as(u6, @intCast(pl_abi_size * 8))), 25561 ); 25562 }, 25563 25564 .load_frame => |frame_addr| try self.asmMemoryImmediate( 25565 .{ ._, .mov }, 25566 .{ 25567 .base = .{ .frame = frame_addr.index }, 25568 .mod = .{ .rm = .{ 25569 .size = .byte, 25570 .disp = frame_addr.off + pl_abi_size, 25571 } }, 25572 }, 25573 .u(1), 25574 ), 25575 } 25576 } 25577 break :result opt_mcv; 25578 }; 25579 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25580 } 25581 25582 /// T to E!T 25583 fn airWrapErrUnionPayload(self: *CodeGen, inst: Air.Inst.Index) !void { 25584 const pt = self.pt; 25585 const zcu = pt.zcu; 25586 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25587 25588 const eu_ty = ty_op.ty.toType(); 25589 const pl_ty = eu_ty.errorUnionPayload(zcu); 25590 const err_ty = eu_ty.errorUnionSet(zcu); 25591 const operand = try self.resolveInst(ty_op.operand); 25592 25593 const result: MCValue = result: { 25594 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .{ .immediate = 0 }; 25595 25596 const frame_index = try self.allocFrameIndex(.initSpill(eu_ty, zcu)); 25597 const pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(pl_ty, zcu)); 25598 const err_off: i32 = @intCast(codegen.errUnionErrorOffset(pl_ty, zcu)); 25599 try self.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, operand, .{}); 25600 try self.genSetMem(.{ .frame = frame_index }, err_off, err_ty, .{ .immediate = 0 }, .{}); 25601 break :result .{ .load_frame = .{ .index = frame_index } }; 25602 }; 25603 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25604 } 25605 25606 /// E to E!T 25607 fn airWrapErrUnionErr(self: *CodeGen, inst: Air.Inst.Index) !void { 25608 const pt = self.pt; 25609 const zcu = pt.zcu; 25610 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25611 25612 const eu_ty = ty_op.ty.toType(); 25613 const pl_ty = eu_ty.errorUnionPayload(zcu); 25614 const err_ty = eu_ty.errorUnionSet(zcu); 25615 25616 const result: MCValue = result: { 25617 if (!pl_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result try self.resolveInst(ty_op.operand); 25618 25619 const frame_index = try self.allocFrameIndex(.initSpill(eu_ty, zcu)); 25620 const pl_off: i32 = @intCast(codegen.errUnionPayloadOffset(pl_ty, zcu)); 25621 const err_off: i32 = @intCast(codegen.errUnionErrorOffset(pl_ty, zcu)); 25622 try self.genSetMem(.{ .frame = frame_index }, pl_off, pl_ty, .undef, .{}); 25623 const operand = try self.resolveInst(ty_op.operand); 25624 try self.genSetMem(.{ .frame = frame_index }, err_off, err_ty, operand, .{}); 25625 break :result .{ .load_frame = .{ .index = frame_index } }; 25626 }; 25627 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25628 } 25629 25630 fn airSlicePtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25631 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25632 const result = result: { 25633 const src_mcv = try self.resolveInst(ty_op.operand); 25634 const ptr_mcv: MCValue = switch (src_mcv) { 25635 .register_pair => |regs| .{ .register = regs[0] }, 25636 else => src_mcv, 25637 }; 25638 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { 25639 switch (src_mcv) { 25640 .register_pair => |regs| try self.freeValue(.{ .register = regs[1] }), 25641 else => {}, 25642 } 25643 break :result ptr_mcv; 25644 } 25645 25646 const dst_mcv = try self.allocRegOrMem(inst, true); 25647 try self.genCopy(self.typeOfIndex(inst), dst_mcv, ptr_mcv, .{}); 25648 break :result dst_mcv; 25649 }; 25650 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25651 } 25652 25653 fn airSliceLen(self: *CodeGen, inst: Air.Inst.Index) !void { 25654 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25655 const result = result: { 25656 const src_mcv = try self.resolveInst(ty_op.operand); 25657 const len_mcv: MCValue = switch (src_mcv) { 25658 .register_pair => |regs| .{ .register = regs[1] }, 25659 .load_frame => |frame_addr| .{ .load_frame = .{ 25660 .index = frame_addr.index, 25661 .off = frame_addr.off + 8, 25662 } }, 25663 else => return self.fail("TODO implement slice_len for {}", .{src_mcv}), 25664 }; 25665 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { 25666 switch (src_mcv) { 25667 .register_pair => |regs| try self.freeValue(.{ .register = regs[0] }), 25668 .load_frame => {}, 25669 else => unreachable, 25670 } 25671 break :result len_mcv; 25672 } 25673 25674 const dst_mcv = try self.allocRegOrMem(inst, true); 25675 try self.genCopy(self.typeOfIndex(inst), dst_mcv, len_mcv, .{}); 25676 break :result dst_mcv; 25677 }; 25678 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 25679 } 25680 25681 fn airPtrSliceLenPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25682 const pt = self.pt; 25683 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25684 25685 const src_ty = self.typeOf(ty_op.operand); 25686 const src_mcv = try self.resolveInst(ty_op.operand); 25687 const src_reg = switch (src_mcv) { 25688 .register => |reg| reg, 25689 else => try self.copyToTmpRegister(src_ty, src_mcv), 25690 }; 25691 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 25692 defer self.register_manager.unlockReg(src_lock); 25693 25694 const dst_ty = self.typeOfIndex(inst); 25695 const dst_reg = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 25696 src_reg 25697 else 25698 try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 25699 const dst_mcv = MCValue{ .register = dst_reg }; 25700 const dst_lock = self.register_manager.lockReg(dst_reg); 25701 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 25702 25703 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(pt.zcu)); 25704 try self.asmRegisterMemory( 25705 .{ ._, .lea }, 25706 registerAlias(dst_reg, dst_abi_size), 25707 .{ 25708 .base = .{ .reg = src_reg }, 25709 .mod = .{ .rm = .{ .size = .qword, .disp = 8 } }, 25710 }, 25711 ); 25712 25713 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 25714 } 25715 25716 fn airPtrSlicePtrPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25717 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 25718 25719 const dst_ty = self.typeOfIndex(inst); 25720 const opt_mcv = try self.resolveInst(ty_op.operand); 25721 25722 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, opt_mcv)) 25723 opt_mcv 25724 else 25725 try self.copyToRegisterWithInstTracking(inst, dst_ty, opt_mcv); 25726 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 25727 } 25728 25729 fn elemOffset(self: *CodeGen, index_ty: Type, index: MCValue, elem_size: u64) !Register { 25730 const reg: Register = blk: { 25731 switch (index) { 25732 .immediate => |imm| { 25733 // Optimisation: if index MCValue is an immediate, we can multiply in `comptime` 25734 // and set the register directly to the scaled offset as an immediate. 25735 const reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 25736 try self.genSetReg(reg, index_ty, .{ .immediate = imm * elem_size }, .{}); 25737 break :blk reg; 25738 }, 25739 else => { 25740 const reg = try self.copyToTmpRegister(index_ty, index); 25741 try self.genIntMulComplexOpMir(index_ty, .{ .register = reg }, .{ .immediate = elem_size }); 25742 break :blk reg; 25743 }, 25744 } 25745 }; 25746 return reg; 25747 } 25748 25749 fn genSliceElemPtr(self: *CodeGen, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { 25750 const pt = self.pt; 25751 const zcu = pt.zcu; 25752 const slice_ty = self.typeOf(lhs); 25753 const slice_mcv = try self.resolveInst(lhs); 25754 const slice_mcv_lock: ?RegisterLock = switch (slice_mcv) { 25755 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 25756 else => null, 25757 }; 25758 defer if (slice_mcv_lock) |lock| self.register_manager.unlockReg(lock); 25759 25760 const elem_ty = slice_ty.childType(zcu); 25761 const elem_size = elem_ty.abiSize(zcu); 25762 const slice_ptr_field_type = slice_ty.slicePtrFieldType(zcu); 25763 25764 const index_ty = self.typeOf(rhs); 25765 const index_mcv = try self.resolveInst(rhs); 25766 const index_mcv_lock: ?RegisterLock = switch (index_mcv) { 25767 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 25768 else => null, 25769 }; 25770 defer if (index_mcv_lock) |lock| self.register_manager.unlockReg(lock); 25771 25772 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_size); 25773 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); 25774 defer self.register_manager.unlockReg(offset_reg_lock); 25775 25776 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 25777 try self.genSetReg(addr_reg, .usize, slice_mcv, .{}); 25778 // TODO we could allocate register here, but need to expect addr register and potentially 25779 // offset register. 25780 try self.genBinOpMir(.{ ._, .add }, slice_ptr_field_type, .{ .register = addr_reg }, .{ 25781 .register = offset_reg, 25782 }); 25783 return MCValue{ .register = addr_reg.to64() }; 25784 } 25785 25786 fn airSliceElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { 25787 const pt = self.pt; 25788 const zcu = pt.zcu; 25789 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 25790 25791 const result: MCValue = result: { 25792 const elem_ty = self.typeOfIndex(inst); 25793 if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 25794 25795 const slice_ty = self.typeOf(bin_op.lhs); 25796 const slice_ptr_field_type = slice_ty.slicePtrFieldType(zcu); 25797 const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs); 25798 const dst_mcv = try self.allocRegOrMem(inst, false); 25799 try self.load(dst_mcv, slice_ptr_field_type, elem_ptr); 25800 break :result dst_mcv; 25801 }; 25802 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 25803 } 25804 25805 fn airSliceElemPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 25806 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 25807 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 25808 const dst_mcv = try self.genSliceElemPtr(extra.lhs, extra.rhs); 25809 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); 25810 } 25811 25812 fn airArrayElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { 25813 const pt = self.pt; 25814 const zcu = pt.zcu; 25815 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 25816 25817 const result: MCValue = result: { 25818 const array_ty = self.typeOf(bin_op.lhs); 25819 const elem_ty = array_ty.childType(zcu); 25820 25821 const array_mcv = try self.resolveInst(bin_op.lhs); 25822 const array_lock: ?RegisterLock = switch (array_mcv) { 25823 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 25824 else => null, 25825 }; 25826 defer if (array_lock) |lock| self.register_manager.unlockReg(lock); 25827 25828 const index_ty = self.typeOf(bin_op.rhs); 25829 const index_mcv = try self.resolveInst(bin_op.rhs); 25830 const index_lock = switch (index_mcv) { 25831 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 25832 else => null, 25833 }; 25834 defer if (index_lock) |lock| self.register_manager.unlockReg(lock); 25835 25836 try self.spillEflagsIfOccupied(); 25837 if (array_ty.isVector(zcu) and elem_ty.bitSize(zcu) == 1) { 25838 const array_mat_mcv: MCValue = switch (array_mcv) { 25839 else => array_mcv, 25840 .register_mask => .{ .register = try self.copyToTmpRegister(array_ty, array_mcv) }, 25841 }; 25842 const array_mat_lock = switch (array_mat_mcv) { 25843 .register => |reg| self.register_manager.lockReg(reg), 25844 else => null, 25845 }; 25846 defer if (array_mat_lock) |lock| self.register_manager.unlockReg(lock); 25847 25848 switch (array_mat_mcv) { 25849 .register => |array_reg| switch (array_reg.class()) { 25850 .general_purpose => switch (index_mcv) { 25851 .immediate => |index_imm| try self.asmRegisterImmediate( 25852 .{ ._, .bt }, 25853 array_reg.to64(), 25854 .u(index_imm), 25855 ), 25856 else => try self.asmRegisterRegister( 25857 .{ ._, .bt }, 25858 array_reg.to64(), 25859 switch (index_mcv) { 25860 .register => |index_reg| index_reg, 25861 else => try self.copyToTmpRegister(index_ty, index_mcv), 25862 }.to64(), 25863 ), 25864 }, 25865 .sse => { 25866 const frame_index = try self.allocFrameIndex(.initType(array_ty, zcu)); 25867 try self.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mat_mcv, .{}); 25868 switch (index_mcv) { 25869 .immediate => |index_imm| try self.asmMemoryImmediate( 25870 .{ ._, .bt }, 25871 .{ 25872 .base = .{ .frame = frame_index }, 25873 .mod = .{ .rm = .{ 25874 .size = .qword, 25875 .disp = @intCast(index_imm / 64 * 8), 25876 } }, 25877 }, 25878 .u(index_imm % 64), 25879 ), 25880 else => try self.asmMemoryRegister( 25881 .{ ._, .bt }, 25882 .{ 25883 .base = .{ .frame = frame_index }, 25884 .mod = .{ .rm = .{ .size = .qword } }, 25885 }, 25886 switch (index_mcv) { 25887 .register => |index_reg| index_reg, 25888 else => try self.copyToTmpRegister(index_ty, index_mcv), 25889 }.to64(), 25890 ), 25891 } 25892 }, 25893 else => unreachable, 25894 }, 25895 .load_frame => switch (index_mcv) { 25896 .immediate => |index_imm| try self.asmMemoryImmediate( 25897 .{ ._, .bt }, 25898 try array_mat_mcv.mem(self, .{ 25899 .size = .qword, 25900 .disp = @intCast(index_imm / 64 * 8), 25901 }), 25902 .u(index_imm % 64), 25903 ), 25904 else => try self.asmMemoryRegister( 25905 .{ ._, .bt }, 25906 try array_mat_mcv.mem(self, .{ .size = .qword }), 25907 switch (index_mcv) { 25908 .register => |index_reg| index_reg, 25909 else => try self.copyToTmpRegister(index_ty, index_mcv), 25910 }.to64(), 25911 ), 25912 }, 25913 .memory, .load_symbol, .load_direct, .load_got, .load_tlv => switch (index_mcv) { 25914 .immediate => |index_imm| try self.asmMemoryImmediate( 25915 .{ ._, .bt }, 25916 .{ 25917 .base = .{ 25918 .reg = try self.copyToTmpRegister(.usize, array_mat_mcv.address()), 25919 }, 25920 .mod = .{ .rm = .{ 25921 .size = .qword, 25922 .disp = @intCast(index_imm / 64 * 8), 25923 } }, 25924 }, 25925 .u(index_imm % 64), 25926 ), 25927 else => try self.asmMemoryRegister( 25928 .{ ._, .bt }, 25929 .{ 25930 .base = .{ 25931 .reg = try self.copyToTmpRegister(.usize, array_mat_mcv.address()), 25932 }, 25933 .mod = .{ .rm = .{ .size = .qword } }, 25934 }, 25935 switch (index_mcv) { 25936 .register => |index_reg| index_reg, 25937 else => try self.copyToTmpRegister(index_ty, index_mcv), 25938 }.to64(), 25939 ), 25940 }, 25941 else => return self.fail("TODO airArrayElemVal for {s} of {}", .{ 25942 @tagName(array_mat_mcv), array_ty.fmt(pt), 25943 }), 25944 } 25945 25946 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 25947 try self.asmSetccRegister(.c, dst_reg.to8()); 25948 break :result .{ .register = dst_reg }; 25949 } 25950 25951 const elem_abi_size = elem_ty.abiSize(zcu); 25952 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 25953 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 25954 defer self.register_manager.unlockReg(addr_lock); 25955 25956 switch (array_mcv) { 25957 .register => { 25958 const frame_index = try self.allocFrameIndex(.initType(array_ty, zcu)); 25959 try self.genSetMem(.{ .frame = frame_index }, 0, array_ty, array_mcv, .{}); 25960 try self.asmRegisterMemory( 25961 .{ ._, .lea }, 25962 addr_reg, 25963 .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ .size = .qword } } }, 25964 ); 25965 }, 25966 .load_frame => |frame_addr| try self.asmRegisterMemory( 25967 .{ ._, .lea }, 25968 addr_reg, 25969 .{ 25970 .base = .{ .frame = frame_addr.index }, 25971 .mod = .{ .rm = .{ .size = .qword, .disp = frame_addr.off } }, 25972 }, 25973 ), 25974 .memory, 25975 .load_symbol, 25976 .load_direct, 25977 .load_got, 25978 .load_tlv, 25979 => try self.genSetReg(addr_reg, .usize, array_mcv.address(), .{}), 25980 .lea_symbol, .lea_direct, .lea_tlv => unreachable, 25981 else => return self.fail("TODO airArrayElemVal_val for {s} of {}", .{ 25982 @tagName(array_mcv), array_ty.fmt(pt), 25983 }), 25984 } 25985 25986 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size); 25987 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg); 25988 defer self.register_manager.unlockReg(offset_lock); 25989 25990 // TODO we could allocate register here, but need to expect addr register and potentially 25991 // offset register. 25992 const dst_mcv = try self.allocRegOrMem(inst, false); 25993 try self.genBinOpMir(.{ ._, .add }, .usize, .{ .register = addr_reg }, .{ .register = offset_reg }); 25994 try self.genCopy(elem_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }, .{}); 25995 break :result dst_mcv; 25996 }; 25997 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 25998 } 25999 26000 fn airPtrElemVal(self: *CodeGen, inst: Air.Inst.Index) !void { 26001 const pt = self.pt; 26002 const zcu = pt.zcu; 26003 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 26004 const ptr_ty = self.typeOf(bin_op.lhs); 26005 26006 // this is identical to the `airPtrElemPtr` codegen expect here an 26007 // additional `mov` is needed at the end to get the actual value 26008 26009 const result = result: { 26010 const elem_ty = ptr_ty.elemType2(zcu); 26011 if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 26012 26013 const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu)); 26014 const index_ty = self.typeOf(bin_op.rhs); 26015 const index_mcv = try self.resolveInst(bin_op.rhs); 26016 const index_lock = switch (index_mcv) { 26017 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26018 else => null, 26019 }; 26020 defer if (index_lock) |lock| self.register_manager.unlockReg(lock); 26021 26022 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size); 26023 const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg); 26024 defer self.register_manager.unlockReg(offset_lock); 26025 26026 const ptr_mcv = try self.resolveInst(bin_op.lhs); 26027 const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0)) 26028 ptr_mcv.register 26029 else 26030 try self.copyToTmpRegister(ptr_ty, ptr_mcv); 26031 const elem_ptr_lock = self.register_manager.lockRegAssumeUnused(elem_ptr_reg); 26032 defer self.register_manager.unlockReg(elem_ptr_lock); 26033 try self.asmRegisterRegister( 26034 .{ ._, .add }, 26035 elem_ptr_reg, 26036 offset_reg, 26037 ); 26038 26039 const dst_mcv = try self.allocRegOrMem(inst, true); 26040 const dst_lock = switch (dst_mcv) { 26041 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26042 else => null, 26043 }; 26044 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 26045 try self.load(dst_mcv, ptr_ty, .{ .register = elem_ptr_reg }); 26046 break :result dst_mcv; 26047 }; 26048 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 26049 } 26050 26051 fn airPtrElemPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 26052 const pt = self.pt; 26053 const zcu = pt.zcu; 26054 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 26055 const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; 26056 26057 const result = result: { 26058 const elem_ptr_ty = self.typeOfIndex(inst); 26059 const base_ptr_ty = self.typeOf(extra.lhs); 26060 26061 const base_ptr_mcv = try self.resolveInst(extra.lhs); 26062 const base_ptr_lock: ?RegisterLock = switch (base_ptr_mcv) { 26063 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26064 else => null, 26065 }; 26066 defer if (base_ptr_lock) |lock| self.register_manager.unlockReg(lock); 26067 26068 if (elem_ptr_ty.ptrInfo(zcu).flags.vector_index != .none) { 26069 break :result if (self.reuseOperand(inst, extra.lhs, 0, base_ptr_mcv)) 26070 base_ptr_mcv 26071 else 26072 try self.copyToRegisterWithInstTracking(inst, elem_ptr_ty, base_ptr_mcv); 26073 } 26074 26075 const elem_ty = base_ptr_ty.elemType2(zcu); 26076 const elem_abi_size = elem_ty.abiSize(zcu); 26077 const index_ty = self.typeOf(extra.rhs); 26078 const index_mcv = try self.resolveInst(extra.rhs); 26079 const index_lock: ?RegisterLock = switch (index_mcv) { 26080 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26081 else => null, 26082 }; 26083 defer if (index_lock) |lock| self.register_manager.unlockReg(lock); 26084 26085 const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size); 26086 const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); 26087 defer self.register_manager.unlockReg(offset_reg_lock); 26088 26089 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, elem_ptr_ty, base_ptr_mcv); 26090 try self.genBinOpMir(.{ ._, .add }, elem_ptr_ty, dst_mcv, .{ .register = offset_reg }); 26091 26092 break :result dst_mcv; 26093 }; 26094 return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); 26095 } 26096 26097 fn airSetUnionTag(self: *CodeGen, inst: Air.Inst.Index) !void { 26098 const pt = self.pt; 26099 const zcu = pt.zcu; 26100 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 26101 const ptr_union_ty = self.typeOf(bin_op.lhs); 26102 const union_ty = ptr_union_ty.childType(zcu); 26103 const tag_ty = self.typeOf(bin_op.rhs); 26104 const layout = union_ty.unionGetLayout(zcu); 26105 26106 if (layout.tag_size == 0) { 26107 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); 26108 } 26109 26110 const ptr = try self.resolveInst(bin_op.lhs); 26111 const ptr_lock: ?RegisterLock = switch (ptr) { 26112 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26113 else => null, 26114 }; 26115 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 26116 26117 const tag = try self.resolveInst(bin_op.rhs); 26118 const tag_lock: ?RegisterLock = switch (tag) { 26119 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26120 else => null, 26121 }; 26122 defer if (tag_lock) |lock| self.register_manager.unlockReg(lock); 26123 26124 const adjusted_ptr: MCValue = if (layout.payload_size > 0 and layout.tag_align.compare(.lt, layout.payload_align)) blk: { 26125 // TODO reusing the operand 26126 const reg = try self.copyToTmpRegister(ptr_union_ty, ptr); 26127 try self.genBinOpMir( 26128 .{ ._, .add }, 26129 ptr_union_ty, 26130 .{ .register = reg }, 26131 .{ .immediate = layout.payload_size }, 26132 ); 26133 break :blk MCValue{ .register = reg }; 26134 } else ptr; 26135 26136 const ptr_tag_ty = try pt.adjustPtrTypeChild(ptr_union_ty, tag_ty); 26137 try self.store(ptr_tag_ty, adjusted_ptr, tag, .{}); 26138 26139 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); 26140 } 26141 26142 fn airGetUnionTag(self: *CodeGen, inst: Air.Inst.Index) !void { 26143 const zcu = self.pt.zcu; 26144 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26145 26146 const tag_ty = self.typeOfIndex(inst); 26147 const union_ty = self.typeOf(ty_op.operand); 26148 const layout = union_ty.unionGetLayout(zcu); 26149 26150 if (layout.tag_size == 0) { 26151 return self.finishAir(inst, .none, .{ ty_op.operand, .none, .none }); 26152 } 26153 26154 // TODO reusing the operand 26155 const operand = try self.resolveInst(ty_op.operand); 26156 const operand_lock: ?RegisterLock = switch (operand) { 26157 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26158 else => null, 26159 }; 26160 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 26161 26162 const tag_abi_size = tag_ty.abiSize(zcu); 26163 const dst_mcv: MCValue = blk: { 26164 switch (operand) { 26165 .load_frame => |frame_addr| { 26166 if (tag_abi_size <= 8) { 26167 const off: i32 = @intCast(layout.tagOffset()); 26168 break :blk try self.copyToRegisterWithInstTracking(inst, tag_ty, .{ 26169 .load_frame = .{ .index = frame_addr.index, .off = frame_addr.off + off }, 26170 }); 26171 } 26172 26173 return self.fail( 26174 "TODO implement get_union_tag for ABI larger than 8 bytes and operand {}", 26175 .{operand}, 26176 ); 26177 }, 26178 .register => { 26179 const shift: u6 = @intCast(layout.tagOffset() * 8); 26180 const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); 26181 try self.genShiftBinOpMir(.{ ._r, .sh }, .usize, result, .u8, .{ .immediate = shift }); 26182 break :blk MCValue{ 26183 .register = registerAlias(result.register, @intCast(layout.tag_size)), 26184 }; 26185 }, 26186 else => return self.fail("TODO implement get_union_tag for {}", .{operand}), 26187 } 26188 }; 26189 26190 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 26191 } 26192 26193 fn airClz(self: *CodeGen, inst: Air.Inst.Index) !void { 26194 const pt = self.pt; 26195 const zcu = pt.zcu; 26196 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26197 const result = result: { 26198 try self.spillEflagsIfOccupied(); 26199 26200 const dst_ty = self.typeOfIndex(inst); 26201 const src_ty = self.typeOf(ty_op.operand); 26202 if (src_ty.zigTypeTag(zcu) == .vector) return self.fail("TODO implement airClz for {}", .{ 26203 src_ty.fmt(pt), 26204 }); 26205 26206 const src_mcv = try self.resolveInst(ty_op.operand); 26207 const mat_src_mcv = switch (src_mcv) { 26208 .immediate => MCValue{ .register = try self.copyToTmpRegister(src_ty, src_mcv) }, 26209 else => src_mcv, 26210 }; 26211 const mat_src_lock = switch (mat_src_mcv) { 26212 .register => |reg| self.register_manager.lockReg(reg), 26213 else => null, 26214 }; 26215 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock); 26216 26217 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 26218 const dst_mcv = MCValue{ .register = dst_reg }; 26219 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 26220 defer self.register_manager.unlockReg(dst_lock); 26221 26222 const abi_size: u31 = @intCast(src_ty.abiSize(zcu)); 26223 const src_bits: u31 = @intCast(src_ty.bitSize(zcu)); 26224 const has_lzcnt = self.hasFeature(.lzcnt); 26225 if (src_bits > @as(u32, if (has_lzcnt) 128 else 64)) { 26226 const src_frame_addr: bits.FrameAddr = src_frame_addr: switch (src_mcv) { 26227 .load_frame => |src_frame_addr| src_frame_addr, 26228 else => { 26229 const src_frame_addr = try self.allocFrameIndex(.initSpill(src_ty, zcu)); 26230 try self.genSetMem(.{ .frame = src_frame_addr }, 0, src_ty, src_mcv, .{}); 26231 break :src_frame_addr .{ .index = src_frame_addr }; 26232 }, 26233 }; 26234 26235 const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; 26236 const extra_bits = abi_size * 8 - src_bits; 26237 26238 const index_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26239 const index_lock = self.register_manager.lockRegAssumeUnused(index_reg); 26240 defer self.register_manager.unlockReg(index_lock); 26241 26242 try self.asmRegisterImmediate(.{ ._, .mov }, index_reg.to32(), .u(limbs_len)); 26243 switch (extra_bits) { 26244 1 => try self.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32()), 26245 else => try self.asmRegisterImmediate( 26246 .{ ._, .mov }, 26247 dst_reg.to32(), 26248 .s(@as(i32, extra_bits) - 1), 26249 ), 26250 } 26251 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 26252 try self.asmRegisterRegister(.{ ._, .@"test" }, index_reg.to32(), index_reg.to32()); 26253 const zero = try self.asmJccReloc(.z, undefined); 26254 if (self.hasFeature(.slow_incdec)) { 26255 try self.asmRegisterImmediate(.{ ._, .sub }, index_reg.to32(), .u(1)); 26256 } else { 26257 try self.asmRegister(.{ ._c, .de }, index_reg.to32()); 26258 } 26259 try self.asmMemoryImmediate(.{ ._, .cmp }, .{ 26260 .base = .{ .frame = src_frame_addr.index }, 26261 .mod = .{ .rm = .{ 26262 .size = .qword, 26263 .index = index_reg.to64(), 26264 .scale = .@"8", 26265 .disp = src_frame_addr.off, 26266 } }, 26267 }, .u(0)); 26268 _ = try self.asmJccReloc(.e, loop); 26269 try self.asmRegisterMemory(.{ ._r, .bs }, dst_reg.to64(), .{ 26270 .base = .{ .frame = src_frame_addr.index }, 26271 .mod = .{ .rm = .{ 26272 .size = .qword, 26273 .index = index_reg.to64(), 26274 .scale = .@"8", 26275 .disp = src_frame_addr.off, 26276 } }, 26277 }); 26278 self.performReloc(zero); 26279 try self.asmRegisterImmediate(.{ ._l, .sh }, index_reg.to32(), .u(6)); 26280 try self.asmRegisterRegister(.{ ._, .add }, index_reg.to32(), dst_reg.to32()); 26281 try self.asmRegisterImmediate(.{ ._, .mov }, dst_reg.to32(), .u(src_bits - 1)); 26282 try self.asmRegisterRegister(.{ ._, .sub }, dst_reg.to32(), index_reg.to32()); 26283 break :result dst_mcv; 26284 } 26285 26286 if (has_lzcnt) { 26287 if (src_bits <= 8) { 26288 const wide_reg = try self.copyToTmpRegister(src_ty, mat_src_mcv); 26289 try self.truncateRegister(src_ty, wide_reg); 26290 try self.genBinOpMir(.{ ._, .lzcnt }, .u32, dst_mcv, .{ .register = wide_reg }); 26291 try self.genBinOpMir( 26292 .{ ._, .sub }, 26293 dst_ty, 26294 dst_mcv, 26295 .{ .immediate = 32 - src_bits }, 26296 ); 26297 } else if (src_bits <= 64) { 26298 try self.genBinOpMir(.{ ._, .lzcnt }, src_ty, dst_mcv, mat_src_mcv); 26299 const extra_bits = self.regExtraBits(src_ty); 26300 if (extra_bits > 0) { 26301 try self.genBinOpMir(.{ ._, .sub }, dst_ty, dst_mcv, .{ .immediate = extra_bits }); 26302 } 26303 } else { 26304 assert(src_bits <= 128); 26305 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26306 const tmp_mcv = MCValue{ .register = tmp_reg }; 26307 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 26308 defer self.register_manager.unlockReg(tmp_lock); 26309 26310 try self.genBinOpMir(.{ ._, .lzcnt }, .u64, dst_mcv, if (mat_src_mcv.isBase()) 26311 mat_src_mcv 26312 else 26313 .{ .register = mat_src_mcv.register_pair[0] }); 26314 try self.genBinOpMir(.{ ._, .add }, dst_ty, dst_mcv, .{ .immediate = 64 }); 26315 try self.genBinOpMir(.{ ._, .lzcnt }, .u64, tmp_mcv, if (mat_src_mcv.isBase()) 26316 mat_src_mcv.address().offset(8).deref() 26317 else 26318 .{ .register = mat_src_mcv.register_pair[1] }); 26319 try self.asmCmovccRegisterRegister(.nc, dst_reg.to32(), tmp_reg.to32()); 26320 26321 if (src_bits < 128) try self.genBinOpMir( 26322 .{ ._, .sub }, 26323 dst_ty, 26324 dst_mcv, 26325 .{ .immediate = 128 - src_bits }, 26326 ); 26327 } 26328 break :result dst_mcv; 26329 } 26330 26331 assert(src_bits <= 64); 26332 const cmov_abi_size = @max(@as(u32, @intCast(dst_ty.abiSize(zcu))), 2); 26333 if (std.math.isPowerOfTwo(src_bits)) { 26334 const imm_reg = try self.copyToTmpRegister(dst_ty, .{ 26335 .immediate = src_bits ^ (src_bits - 1), 26336 }); 26337 const imm_lock = self.register_manager.lockRegAssumeUnused(imm_reg); 26338 defer self.register_manager.unlockReg(imm_lock); 26339 26340 if (src_bits <= 8) { 26341 const wide_reg = try self.copyToTmpRegister(src_ty, mat_src_mcv); 26342 const wide_lock = self.register_manager.lockRegAssumeUnused(wide_reg); 26343 defer self.register_manager.unlockReg(wide_lock); 26344 26345 try self.truncateRegister(src_ty, wide_reg); 26346 try self.genBinOpMir(.{ ._r, .bs }, .u16, dst_mcv, .{ .register = wide_reg }); 26347 } else try self.genBinOpMir(.{ ._r, .bs }, src_ty, dst_mcv, mat_src_mcv); 26348 26349 try self.asmCmovccRegisterRegister( 26350 .z, 26351 registerAlias(dst_reg, cmov_abi_size), 26352 registerAlias(imm_reg, cmov_abi_size), 26353 ); 26354 26355 try self.genBinOpMir(.{ ._, .xor }, dst_ty, dst_mcv, .{ .immediate = src_bits - 1 }); 26356 } else { 26357 const imm_reg = try self.copyToTmpRegister(dst_ty, .{ 26358 .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - self.regBitSize(dst_ty)), 26359 }); 26360 const imm_lock = self.register_manager.lockRegAssumeUnused(imm_reg); 26361 defer self.register_manager.unlockReg(imm_lock); 26362 26363 const wide_reg = try self.copyToTmpRegister(src_ty, mat_src_mcv); 26364 const wide_lock = self.register_manager.lockRegAssumeUnused(wide_reg); 26365 defer self.register_manager.unlockReg(wide_lock); 26366 26367 try self.truncateRegister(src_ty, wide_reg); 26368 try self.genBinOpMir( 26369 .{ ._r, .bs }, 26370 if (src_bits <= 8) .u16 else src_ty, 26371 dst_mcv, 26372 .{ .register = wide_reg }, 26373 ); 26374 26375 try self.asmCmovccRegisterRegister( 26376 .nz, 26377 registerAlias(imm_reg, cmov_abi_size), 26378 registerAlias(dst_reg, cmov_abi_size), 26379 ); 26380 26381 try self.genSetReg(dst_reg, dst_ty, .{ .immediate = src_bits - 1 }, .{}); 26382 try self.genBinOpMir(.{ ._, .sub }, dst_ty, dst_mcv, .{ .register = imm_reg }); 26383 } 26384 break :result dst_mcv; 26385 }; 26386 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 26387 } 26388 26389 fn airCtz(self: *CodeGen, inst: Air.Inst.Index) !void { 26390 const pt = self.pt; 26391 const zcu = pt.zcu; 26392 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26393 const result = result: { 26394 try self.spillEflagsIfOccupied(); 26395 26396 const dst_ty = self.typeOfIndex(inst); 26397 const src_ty = self.typeOf(ty_op.operand); 26398 if (src_ty.zigTypeTag(zcu) == .vector) return self.fail("TODO implement airCtz for {}", .{ 26399 src_ty.fmt(pt), 26400 }); 26401 26402 const src_mcv = try self.resolveInst(ty_op.operand); 26403 const mat_src_mcv = switch (src_mcv) { 26404 .immediate => MCValue{ .register = try self.copyToTmpRegister(src_ty, src_mcv) }, 26405 else => src_mcv, 26406 }; 26407 const mat_src_lock = switch (mat_src_mcv) { 26408 .register => |reg| self.register_manager.lockReg(reg), 26409 else => null, 26410 }; 26411 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock); 26412 26413 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 26414 const dst_mcv = MCValue{ .register = dst_reg }; 26415 const dst_lock = self.register_manager.lockReg(dst_reg); 26416 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 26417 26418 const abi_size: u31 = @intCast(src_ty.abiSize(zcu)); 26419 const src_bits: u31 = @intCast(src_ty.bitSize(zcu)); 26420 const has_bmi = self.hasFeature(.bmi); 26421 if (src_bits > @as(u32, if (has_bmi) 128 else 64)) { 26422 const src_frame_addr: bits.FrameAddr = src_frame_addr: switch (src_mcv) { 26423 .load_frame => |src_frame_addr| src_frame_addr, 26424 else => { 26425 const src_frame_addr = try self.allocFrameIndex(.initSpill(src_ty, zcu)); 26426 try self.genSetMem(.{ .frame = src_frame_addr }, 0, src_ty, src_mcv, .{}); 26427 break :src_frame_addr .{ .index = src_frame_addr }; 26428 }, 26429 }; 26430 26431 const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; 26432 const extra_bits = abi_size * 8 - src_bits; 26433 26434 const index_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26435 const index_lock = self.register_manager.lockRegAssumeUnused(index_reg); 26436 defer self.register_manager.unlockReg(index_lock); 26437 26438 try self.asmRegisterImmediate(.{ ._, .mov }, index_reg.to32(), .s(-1)); 26439 switch (extra_bits) { 26440 0 => try self.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32()), 26441 1 => try self.asmRegisterRegister(.{ ._, .mov }, dst_reg.to32(), dst_reg.to32()), 26442 else => try self.asmRegisterImmediate( 26443 .{ ._, .mov }, 26444 dst_reg.to32(), 26445 .s(-@as(i32, extra_bits)), 26446 ), 26447 } 26448 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 26449 if (self.hasFeature(.slow_incdec)) { 26450 try self.asmRegisterImmediate(.{ ._, .add }, index_reg.to32(), .u(1)); 26451 } else { 26452 try self.asmRegister(.{ ._c, .in }, index_reg.to32()); 26453 } 26454 try self.asmRegisterImmediate(.{ ._, .cmp }, index_reg.to32(), .u(limbs_len)); 26455 const zero = try self.asmJccReloc(.nb, undefined); 26456 try self.asmMemoryImmediate(.{ ._, .cmp }, .{ 26457 .base = .{ .frame = src_frame_addr.index }, 26458 .mod = .{ .rm = .{ 26459 .size = .qword, 26460 .index = index_reg.to64(), 26461 .scale = .@"8", 26462 .disp = src_frame_addr.off, 26463 } }, 26464 }, .u(0)); 26465 _ = try self.asmJccReloc(.e, loop); 26466 try self.asmRegisterMemory(.{ ._f, .bs }, dst_reg.to64(), .{ 26467 .base = .{ .frame = src_frame_addr.index }, 26468 .mod = .{ .rm = .{ 26469 .size = .qword, 26470 .index = index_reg.to64(), 26471 .scale = .@"8", 26472 .disp = src_frame_addr.off, 26473 } }, 26474 }); 26475 self.performReloc(zero); 26476 try self.asmRegisterImmediate(.{ ._l, .sh }, index_reg.to32(), .u(6)); 26477 try self.asmRegisterRegister(.{ ._, .add }, dst_reg.to32(), index_reg.to32()); 26478 break :result dst_mcv; 26479 } 26480 26481 const wide_ty: Type = if (src_bits <= 8) .u16 else src_ty; 26482 if (has_bmi) { 26483 if (src_bits <= 64) { 26484 const extra_bits = self.regExtraBits(src_ty) + @as(u64, if (src_bits <= 8) 8 else 0); 26485 const masked_mcv = if (extra_bits > 0) masked: { 26486 const tmp_mcv = tmp: { 26487 if (src_mcv.isImmediate() or self.liveness.operandDies(inst, 0)) 26488 break :tmp src_mcv; 26489 try self.genSetReg(dst_reg, wide_ty, src_mcv, .{}); 26490 break :tmp dst_mcv; 26491 }; 26492 try self.genBinOpMir( 26493 .{ ._, .@"or" }, 26494 wide_ty, 26495 tmp_mcv, 26496 .{ .immediate = (@as(u64, std.math.maxInt(u64)) >> @intCast(64 - extra_bits)) << 26497 @intCast(src_bits) }, 26498 ); 26499 break :masked tmp_mcv; 26500 } else mat_src_mcv; 26501 try self.genBinOpMir(.{ ._, .tzcnt }, wide_ty, dst_mcv, masked_mcv); 26502 } else { 26503 assert(src_bits <= 128); 26504 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26505 const tmp_mcv = MCValue{ .register = tmp_reg }; 26506 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 26507 defer self.register_manager.unlockReg(tmp_lock); 26508 26509 const lo_mat_src_mcv: MCValue = if (mat_src_mcv.isBase()) 26510 mat_src_mcv 26511 else 26512 .{ .register = mat_src_mcv.register_pair[0] }; 26513 const hi_mat_src_mcv: MCValue = if (mat_src_mcv.isBase()) 26514 mat_src_mcv.address().offset(8).deref() 26515 else 26516 .{ .register = mat_src_mcv.register_pair[1] }; 26517 const masked_mcv = if (src_bits < 128) masked: { 26518 try self.genCopy(.u64, dst_mcv, hi_mat_src_mcv, .{}); 26519 try self.genBinOpMir( 26520 .{ ._, .@"or" }, 26521 .u64, 26522 dst_mcv, 26523 .{ .immediate = @as(u64, std.math.maxInt(u64)) << @intCast(src_bits - 64) }, 26524 ); 26525 break :masked dst_mcv; 26526 } else hi_mat_src_mcv; 26527 try self.genBinOpMir(.{ ._, .tzcnt }, .u64, dst_mcv, masked_mcv); 26528 try self.genBinOpMir(.{ ._, .add }, dst_ty, dst_mcv, .{ .immediate = 64 }); 26529 try self.genBinOpMir(.{ ._, .tzcnt }, .u64, tmp_mcv, lo_mat_src_mcv); 26530 try self.asmCmovccRegisterRegister(.nc, dst_reg.to32(), tmp_reg.to32()); 26531 } 26532 break :result dst_mcv; 26533 } 26534 26535 assert(src_bits <= 64); 26536 const width_reg = try self.copyToTmpRegister(dst_ty, .{ .immediate = src_bits }); 26537 const width_lock = self.register_manager.lockRegAssumeUnused(width_reg); 26538 defer self.register_manager.unlockReg(width_lock); 26539 26540 if (src_bits <= 8 or !std.math.isPowerOfTwo(src_bits)) { 26541 const wide_reg = try self.copyToTmpRegister(src_ty, mat_src_mcv); 26542 const wide_lock = self.register_manager.lockRegAssumeUnused(wide_reg); 26543 defer self.register_manager.unlockReg(wide_lock); 26544 26545 try self.truncateRegister(src_ty, wide_reg); 26546 try self.genBinOpMir(.{ ._f, .bs }, wide_ty, dst_mcv, .{ .register = wide_reg }); 26547 } else try self.genBinOpMir(.{ ._f, .bs }, src_ty, dst_mcv, mat_src_mcv); 26548 26549 const cmov_abi_size = @max(@as(u32, @intCast(dst_ty.abiSize(zcu))), 2); 26550 try self.asmCmovccRegisterRegister( 26551 .z, 26552 registerAlias(dst_reg, cmov_abi_size), 26553 registerAlias(width_reg, cmov_abi_size), 26554 ); 26555 break :result dst_mcv; 26556 }; 26557 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 26558 } 26559 26560 fn airPopCount(self: *CodeGen, inst: Air.Inst.Index) !void { 26561 const pt = self.pt; 26562 const zcu = pt.zcu; 26563 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26564 const result: MCValue = result: { 26565 try self.spillEflagsIfOccupied(); 26566 26567 const src_ty = self.typeOf(ty_op.operand); 26568 const src_abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 26569 if (src_ty.zigTypeTag(zcu) == .vector or src_abi_size > 16) 26570 return self.fail("TODO implement airPopCount for {}", .{src_ty.fmt(pt)}); 26571 const src_mcv = try self.resolveInst(ty_op.operand); 26572 26573 const mat_src_mcv = switch (src_mcv) { 26574 .immediate => MCValue{ .register = try self.copyToTmpRegister(src_ty, src_mcv) }, 26575 else => src_mcv, 26576 }; 26577 const mat_src_lock = switch (mat_src_mcv) { 26578 .register => |reg| self.register_manager.lockReg(reg), 26579 else => null, 26580 }; 26581 defer if (mat_src_lock) |lock| self.register_manager.unlockReg(lock); 26582 26583 if (src_abi_size <= 8) { 26584 const dst_contains_src = 26585 src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv); 26586 const dst_reg = if (dst_contains_src) 26587 src_mcv.getReg().? 26588 else 26589 try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 26590 const dst_lock = self.register_manager.lockReg(dst_reg); 26591 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 26592 26593 try self.genPopCount(dst_reg, src_ty, mat_src_mcv, dst_contains_src); 26594 break :result .{ .register = dst_reg }; 26595 } 26596 26597 assert(src_abi_size > 8 and src_abi_size <= 16); 26598 const tmp_regs = try self.register_manager.allocRegs(2, .{ inst, null }, abi.RegisterClass.gp); 26599 const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs); 26600 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); 26601 26602 try self.genPopCount(tmp_regs[0], .usize, if (mat_src_mcv.isBase()) 26603 mat_src_mcv 26604 else 26605 .{ .register = mat_src_mcv.register_pair[0] }, false); 26606 const src_info = src_ty.intInfo(zcu); 26607 const hi_ty = try pt.intType(src_info.signedness, (src_info.bits - 1) % 64 + 1); 26608 try self.genPopCount(tmp_regs[1], hi_ty, if (mat_src_mcv.isBase()) 26609 mat_src_mcv.address().offset(8).deref() 26610 else 26611 .{ .register = mat_src_mcv.register_pair[1] }, false); 26612 try self.asmRegisterRegister(.{ ._, .add }, tmp_regs[0].to8(), tmp_regs[1].to8()); 26613 break :result .{ .register = tmp_regs[0] }; 26614 }; 26615 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 26616 } 26617 26618 fn genPopCount( 26619 self: *CodeGen, 26620 dst_reg: Register, 26621 src_ty: Type, 26622 src_mcv: MCValue, 26623 dst_contains_src: bool, 26624 ) !void { 26625 const pt = self.pt; 26626 26627 const src_abi_size: u32 = @intCast(src_ty.abiSize(pt.zcu)); 26628 if (self.hasFeature(.popcnt)) return self.genBinOpMir( 26629 .{ ._, .popcnt }, 26630 if (src_abi_size > 1) src_ty else .u32, 26631 .{ .register = dst_reg }, 26632 if (src_abi_size > 1) src_mcv else src: { 26633 if (!dst_contains_src) try self.genSetReg(dst_reg, src_ty, src_mcv, .{}); 26634 try self.truncateRegister(try src_ty.toUnsigned(pt), dst_reg); 26635 break :src .{ .register = dst_reg }; 26636 }, 26637 ); 26638 26639 const mask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - src_abi_size * 8); 26640 const imm_0_1: Immediate = .u(mask / 0b1_1); 26641 const imm_00_11: Immediate = .u(mask / 0b01_01); 26642 const imm_0000_1111: Immediate = .u(mask / 0b0001_0001); 26643 const imm_0000_0001: Immediate = .u(mask / 0b1111_1111); 26644 26645 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26646 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 26647 defer self.register_manager.unlockReg(tmp_lock); 26648 26649 const dst = registerAlias(dst_reg, src_abi_size); 26650 const tmp = registerAlias(tmp_reg, src_abi_size); 26651 const imm = if (src_abi_size > 4) 26652 try self.register_manager.allocReg(null, abi.RegisterClass.gp) 26653 else 26654 undefined; 26655 26656 if (!dst_contains_src) try self.genSetReg(dst, src_ty, src_mcv, .{}); 26657 // dst = operand 26658 try self.asmRegisterRegister(.{ ._, .mov }, tmp, dst); 26659 // tmp = operand 26660 try self.asmRegisterImmediate(.{ ._r, .sh }, tmp, .u(1)); 26661 // tmp = operand >> 1 26662 if (src_abi_size > 4) { 26663 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_0_1); 26664 try self.asmRegisterRegister(.{ ._, .@"and" }, tmp, imm); 26665 } else try self.asmRegisterImmediate(.{ ._, .@"and" }, tmp, imm_0_1); 26666 // tmp = (operand >> 1) & 0x55...55 26667 try self.asmRegisterRegister(.{ ._, .sub }, dst, tmp); 26668 // dst = temp1 = operand - ((operand >> 1) & 0x55...55) 26669 try self.asmRegisterRegister(.{ ._, .mov }, tmp, dst); 26670 // tmp = temp1 26671 try self.asmRegisterImmediate(.{ ._r, .sh }, dst, .u(2)); 26672 // dst = temp1 >> 2 26673 if (src_abi_size > 4) { 26674 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_00_11); 26675 try self.asmRegisterRegister(.{ ._, .@"and" }, tmp, imm); 26676 try self.asmRegisterRegister(.{ ._, .@"and" }, dst, imm); 26677 } else { 26678 try self.asmRegisterImmediate(.{ ._, .@"and" }, tmp, imm_00_11); 26679 try self.asmRegisterImmediate(.{ ._, .@"and" }, dst, imm_00_11); 26680 } 26681 // tmp = temp1 & 0x33...33 26682 // dst = (temp1 >> 2) & 0x33...33 26683 try self.asmRegisterRegister(.{ ._, .add }, tmp, dst); 26684 // tmp = temp2 = (temp1 & 0x33...33) + ((temp1 >> 2) & 0x33...33) 26685 try self.asmRegisterRegister(.{ ._, .mov }, dst, tmp); 26686 // dst = temp2 26687 try self.asmRegisterImmediate(.{ ._r, .sh }, tmp, .u(4)); 26688 // tmp = temp2 >> 4 26689 try self.asmRegisterRegister(.{ ._, .add }, dst, tmp); 26690 // dst = temp2 + (temp2 >> 4) 26691 if (src_abi_size > 4) { 26692 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_0000_1111); 26693 try self.asmRegisterImmediate(.{ ._, .mov }, tmp, imm_0000_0001); 26694 try self.asmRegisterRegister(.{ ._, .@"and" }, dst, imm); 26695 try self.asmRegisterRegister(.{ .i_, .mul }, dst, tmp); 26696 } else { 26697 try self.asmRegisterImmediate(.{ ._, .@"and" }, dst, imm_0000_1111); 26698 if (src_abi_size > 1) { 26699 try self.asmRegisterRegisterImmediate(.{ .i_, .mul }, dst, dst, imm_0000_0001); 26700 } 26701 } 26702 // dst = temp3 = (temp2 + (temp2 >> 4)) & 0x0f...0f 26703 // dst = temp3 * 0x01...01 26704 if (src_abi_size > 1) { 26705 try self.asmRegisterImmediate(.{ ._r, .sh }, dst, .u((src_abi_size - 1) * 8)); 26706 } 26707 // dst = (temp3 * 0x01...01) >> (bits - 8) 26708 } 26709 26710 fn genByteSwap( 26711 self: *CodeGen, 26712 inst: Air.Inst.Index, 26713 src_ty: Type, 26714 src_mcv: MCValue, 26715 mem_ok: bool, 26716 ) !MCValue { 26717 const pt = self.pt; 26718 const zcu = pt.zcu; 26719 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26720 const has_movbe = self.hasFeature(.movbe); 26721 26722 if (src_ty.zigTypeTag(zcu) == .vector) return self.fail( 26723 "TODO implement genByteSwap for {}", 26724 .{src_ty.fmt(pt)}, 26725 ); 26726 26727 const src_lock = switch (src_mcv) { 26728 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 26729 else => null, 26730 }; 26731 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 26732 26733 const abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 26734 switch (abi_size) { 26735 0 => unreachable, 26736 1 => return if ((mem_ok or src_mcv.isRegister()) and 26737 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 26738 src_mcv 26739 else 26740 try self.copyToRegisterWithInstTracking(inst, src_ty, src_mcv), 26741 2 => if ((mem_ok or src_mcv.isRegister()) and 26742 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 26743 { 26744 try self.genBinOpMir(.{ ._l, .ro }, src_ty, src_mcv, .{ .immediate = 8 }); 26745 return src_mcv; 26746 }, 26747 3...8 => if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { 26748 try self.genUnOpMir(.{ ._, .bswap }, src_ty, src_mcv); 26749 return src_mcv; 26750 }, 26751 9...16 => { 26752 const mat_src_mcv: MCValue = mat_src_mcv: switch (src_mcv) { 26753 .register => { 26754 const frame_index = try self.allocFrameIndex(.initSpill(src_ty, zcu)); 26755 try self.genSetMem(.{ .frame = frame_index }, 0, src_ty, src_mcv, .{}); 26756 break :mat_src_mcv .{ .load_frame = .{ .index = frame_index } }; 26757 }, 26758 .register_pair => |src_regs| if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { 26759 for (src_regs) |src_reg| try self.asmRegister(.{ ._, .bswap }, src_reg.to64()); 26760 return .{ .register_pair = .{ src_regs[1], src_regs[0] } }; 26761 } else src_mcv, 26762 else => src_mcv, 26763 }; 26764 26765 const dst_regs = 26766 try self.register_manager.allocRegs(2, .{ inst, inst }, abi.RegisterClass.gp); 26767 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 26768 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 26769 26770 for (dst_regs, 0..) |dst_reg, limb_index| { 26771 if (mat_src_mcv.isBase()) { 26772 try self.asmRegisterMemory( 26773 .{ ._, if (has_movbe) .movbe else .mov }, 26774 dst_reg.to64(), 26775 try mat_src_mcv.address().offset(@intCast(limb_index * 8)).deref().mem(self, .{ .size = .qword }), 26776 ); 26777 if (!has_movbe) try self.asmRegister(.{ ._, .bswap }, dst_reg.to64()); 26778 } else { 26779 try self.asmRegisterRegister( 26780 .{ ._, .mov }, 26781 dst_reg.to64(), 26782 mat_src_mcv.register_pair[limb_index].to64(), 26783 ); 26784 try self.asmRegister(.{ ._, .bswap }, dst_reg.to64()); 26785 } 26786 } 26787 return .{ .register_pair = .{ dst_regs[1], dst_regs[0] } }; 26788 }, 26789 else => { 26790 const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; 26791 26792 const temp_regs = 26793 try self.register_manager.allocRegs(4, @splat(null), abi.RegisterClass.gp); 26794 const temp_locks = self.register_manager.lockRegsAssumeUnused(4, temp_regs); 26795 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 26796 26797 const dst_mcv = try self.allocRegOrMem(inst, false); 26798 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[0].to32(), temp_regs[0].to32()); 26799 try self.asmRegisterImmediate(.{ ._, .mov }, temp_regs[1].to32(), .u(limbs_len - 1)); 26800 26801 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 26802 try self.asmRegisterMemory( 26803 .{ ._, if (has_movbe) .movbe else .mov }, 26804 temp_regs[2].to64(), 26805 .{ 26806 .base = .{ .frame = dst_mcv.load_frame.index }, 26807 .mod = .{ .rm = .{ 26808 .size = .qword, 26809 .index = temp_regs[0].to64(), 26810 .scale = .@"8", 26811 .disp = dst_mcv.load_frame.off, 26812 } }, 26813 }, 26814 ); 26815 try self.asmRegisterMemory( 26816 .{ ._, if (has_movbe) .movbe else .mov }, 26817 temp_regs[3].to64(), 26818 .{ 26819 .base = .{ .frame = dst_mcv.load_frame.index }, 26820 .mod = .{ .rm = .{ 26821 .size = .qword, 26822 .index = temp_regs[1].to64(), 26823 .scale = .@"8", 26824 .disp = dst_mcv.load_frame.off, 26825 } }, 26826 }, 26827 ); 26828 if (!has_movbe) { 26829 try self.asmRegister(.{ ._, .bswap }, temp_regs[2].to64()); 26830 try self.asmRegister(.{ ._, .bswap }, temp_regs[3].to64()); 26831 } 26832 try self.asmMemoryRegister(.{ ._, .mov }, .{ 26833 .base = .{ .frame = dst_mcv.load_frame.index }, 26834 .mod = .{ .rm = .{ 26835 .size = .qword, 26836 .index = temp_regs[0].to64(), 26837 .scale = .@"8", 26838 .disp = dst_mcv.load_frame.off, 26839 } }, 26840 }, temp_regs[3].to64()); 26841 try self.asmMemoryRegister(.{ ._, .mov }, .{ 26842 .base = .{ .frame = dst_mcv.load_frame.index }, 26843 .mod = .{ .rm = .{ 26844 .size = .qword, 26845 .index = temp_regs[1].to64(), 26846 .scale = .@"8", 26847 .disp = dst_mcv.load_frame.off, 26848 } }, 26849 }, temp_regs[2].to64()); 26850 if (self.hasFeature(.slow_incdec)) { 26851 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), .u(1)); 26852 try self.asmRegisterImmediate(.{ ._, .sub }, temp_regs[1].to32(), .u(1)); 26853 } else { 26854 try self.asmRegister(.{ ._c, .in }, temp_regs[0].to32()); 26855 try self.asmRegister(.{ ._c, .de }, temp_regs[1].to32()); 26856 } 26857 try self.asmRegisterRegister(.{ ._, .cmp }, temp_regs[0].to32(), temp_regs[1].to32()); 26858 _ = try self.asmJccReloc(.be, loop); 26859 return dst_mcv; 26860 }, 26861 } 26862 26863 const dst_mcv: MCValue = if (mem_ok and has_movbe and src_mcv.isRegister()) 26864 try self.allocRegOrMem(inst, true) 26865 else 26866 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.gp) }; 26867 if (dst_mcv.getReg()) |dst_reg| { 26868 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_mcv.register); 26869 defer self.register_manager.unlockReg(dst_lock); 26870 26871 try self.genSetReg(dst_reg, src_ty, src_mcv, .{}); 26872 switch (abi_size) { 26873 else => unreachable, 26874 2 => try self.genBinOpMir(.{ ._l, .ro }, src_ty, dst_mcv, .{ .immediate = 8 }), 26875 3...8 => try self.genUnOpMir(.{ ._, .bswap }, src_ty, dst_mcv), 26876 } 26877 } else try self.genBinOpMir(.{ ._, .movbe }, src_ty, dst_mcv, src_mcv); 26878 return dst_mcv; 26879 } 26880 26881 fn airByteSwap(self: *CodeGen, inst: Air.Inst.Index) !void { 26882 const pt = self.pt; 26883 const zcu = pt.zcu; 26884 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26885 26886 const src_ty = self.typeOf(ty_op.operand); 26887 const src_bits: u32 = @intCast(src_ty.bitSize(zcu)); 26888 const src_mcv = try self.resolveInst(ty_op.operand); 26889 26890 const dst_mcv = try self.genByteSwap(inst, src_ty, src_mcv, true); 26891 try self.genShiftBinOpMir( 26892 .{ ._r, switch (if (src_ty.isAbiInt(zcu)) src_ty.intInfo(zcu).signedness else .unsigned) { 26893 .signed => .sa, 26894 .unsigned => .sh, 26895 } }, 26896 src_ty, 26897 dst_mcv, 26898 if (src_bits > 256) .u16 else .u8, 26899 .{ .immediate = src_ty.abiSize(zcu) * 8 - src_bits }, 26900 ); 26901 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 26902 } 26903 26904 fn airBitReverse(self: *CodeGen, inst: Air.Inst.Index) !void { 26905 const pt = self.pt; 26906 const zcu = pt.zcu; 26907 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 26908 26909 const src_ty = self.typeOf(ty_op.operand); 26910 const abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 26911 const bit_size: u32 = @intCast(src_ty.bitSize(zcu)); 26912 const src_mcv = try self.resolveInst(ty_op.operand); 26913 26914 const dst_mcv = try self.genByteSwap(inst, src_ty, src_mcv, false); 26915 const dst_locks: [2]?RegisterLock = switch (dst_mcv) { 26916 .register => |dst_reg| .{ self.register_manager.lockReg(dst_reg), null }, 26917 .register_pair => |dst_regs| self.register_manager.lockRegs(2, dst_regs), 26918 else => unreachable, 26919 }; 26920 defer for (dst_locks) |dst_lock| if (dst_lock) |lock| self.register_manager.unlockReg(lock); 26921 26922 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 26923 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 26924 defer self.register_manager.unlockReg(tmp_lock); 26925 26926 const limb_abi_size: u32 = @min(abi_size, 8); 26927 const tmp = registerAlias(tmp_reg, limb_abi_size); 26928 const imm = if (limb_abi_size > 4) 26929 try self.register_manager.allocReg(null, abi.RegisterClass.gp) 26930 else 26931 undefined; 26932 26933 const mask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - limb_abi_size * 8); 26934 const imm_0000_1111: Immediate = .u(mask / 0b0001_0001); 26935 const imm_00_11: Immediate = .u(mask / 0b01_01); 26936 const imm_0_1: Immediate = .u(mask / 0b1_1); 26937 26938 for (dst_mcv.getRegs()) |dst_reg| { 26939 const dst = registerAlias(dst_reg, limb_abi_size); 26940 26941 // dst = temp1 = bswap(operand) 26942 try self.asmRegisterRegister(.{ ._, .mov }, tmp, dst); 26943 // tmp = temp1 26944 try self.asmRegisterImmediate(.{ ._r, .sh }, dst, .u(4)); 26945 // dst = temp1 >> 4 26946 if (limb_abi_size > 4) { 26947 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_0000_1111); 26948 try self.asmRegisterRegister(.{ ._, .@"and" }, tmp, imm); 26949 try self.asmRegisterRegister(.{ ._, .@"and" }, dst, imm); 26950 } else { 26951 try self.asmRegisterImmediate(.{ ._, .@"and" }, tmp, imm_0000_1111); 26952 try self.asmRegisterImmediate(.{ ._, .@"and" }, dst, imm_0000_1111); 26953 } 26954 // tmp = temp1 & 0x0f...0f 26955 // dst = (temp1 >> 4) & 0x0f...0f 26956 try self.asmRegisterImmediate(.{ ._l, .sh }, tmp, .u(4)); 26957 // tmp = (temp1 & 0x0f...0f) << 4 26958 try self.asmRegisterRegister(.{ ._, .@"or" }, dst, tmp); 26959 // dst = temp2 = ((temp1 >> 4) & 0x0f...0f) | ((temp1 & 0x0f...0f) << 4) 26960 try self.asmRegisterRegister(.{ ._, .mov }, tmp, dst); 26961 // tmp = temp2 26962 try self.asmRegisterImmediate(.{ ._r, .sh }, dst, .u(2)); 26963 // dst = temp2 >> 2 26964 if (limb_abi_size > 4) { 26965 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_00_11); 26966 try self.asmRegisterRegister(.{ ._, .@"and" }, tmp, imm); 26967 try self.asmRegisterRegister(.{ ._, .@"and" }, dst, imm); 26968 } else { 26969 try self.asmRegisterImmediate(.{ ._, .@"and" }, tmp, imm_00_11); 26970 try self.asmRegisterImmediate(.{ ._, .@"and" }, dst, imm_00_11); 26971 } 26972 // tmp = temp2 & 0x33...33 26973 // dst = (temp2 >> 2) & 0x33...33 26974 try self.asmRegisterMemory( 26975 .{ ._, .lea }, 26976 if (limb_abi_size > 4) tmp.to64() else tmp.to32(), 26977 .{ 26978 .base = .{ .reg = dst.to64() }, 26979 .mod = .{ .rm = .{ 26980 .size = .qword, 26981 .index = tmp.to64(), 26982 .scale = .@"4", 26983 } }, 26984 }, 26985 ); 26986 // tmp = temp3 = ((temp2 >> 2) & 0x33...33) + ((temp2 & 0x33...33) << 2) 26987 try self.asmRegisterRegister(.{ ._, .mov }, dst, tmp); 26988 // dst = temp3 26989 try self.asmRegisterImmediate(.{ ._r, .sh }, tmp, .u(1)); 26990 // tmp = temp3 >> 1 26991 if (limb_abi_size > 4) { 26992 try self.asmRegisterImmediate(.{ ._, .mov }, imm, imm_0_1); 26993 try self.asmRegisterRegister(.{ ._, .@"and" }, dst, imm); 26994 try self.asmRegisterRegister(.{ ._, .@"and" }, tmp, imm); 26995 } else { 26996 try self.asmRegisterImmediate(.{ ._, .@"and" }, dst, imm_0_1); 26997 try self.asmRegisterImmediate(.{ ._, .@"and" }, tmp, imm_0_1); 26998 } 26999 // dst = temp3 & 0x55...55 27000 // tmp = (temp3 >> 1) & 0x55...55 27001 try self.asmRegisterMemory( 27002 .{ ._, .lea }, 27003 if (limb_abi_size > 4) dst.to64() else dst.to32(), 27004 .{ 27005 .base = .{ .reg = tmp.to64() }, 27006 .mod = .{ .rm = .{ 27007 .size = .qword, 27008 .index = dst.to64(), 27009 .scale = .@"2", 27010 } }, 27011 }, 27012 ); 27013 // dst = ((temp3 >> 1) & 0x55...55) + ((temp3 & 0x55...55) << 1) 27014 } 27015 27016 const extra_bits = abi_size * 8 - bit_size; 27017 const signedness: std.builtin.Signedness = 27018 if (src_ty.isAbiInt(zcu)) src_ty.intInfo(zcu).signedness else .unsigned; 27019 if (extra_bits > 0) try self.genShiftBinOpMir(switch (signedness) { 27020 .signed => .{ ._r, .sa }, 27021 .unsigned => .{ ._r, .sh }, 27022 }, src_ty, dst_mcv, .u8, .{ .immediate = extra_bits }); 27023 27024 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 27025 } 27026 27027 fn floatSign(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag, operand: Air.Inst.Ref, ty: Type) !void { 27028 const pt = self.pt; 27029 const zcu = pt.zcu; 27030 27031 const result = result: { 27032 const scalar_bits = ty.scalarType(zcu).floatBits(self.target.*); 27033 if (scalar_bits == 80) { 27034 if (ty.zigTypeTag(zcu) != .float) return self.fail("TODO implement floatSign for {}", .{ 27035 ty.fmt(pt), 27036 }); 27037 27038 const src_mcv = try self.resolveInst(operand); 27039 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 27040 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 27041 27042 const dst_mcv: MCValue = .{ .register = .st0 }; 27043 if (!std.meta.eql(src_mcv, dst_mcv) or !self.reuseOperand(inst, operand, 0, src_mcv)) 27044 try self.register_manager.getKnownReg(.st0, inst); 27045 27046 try self.genCopy(ty, dst_mcv, src_mcv, .{}); 27047 switch (tag) { 27048 .neg => try self.asmOpOnly(.{ .f_, .chs }), 27049 .abs => try self.asmOpOnly(.{ .f_, .abs }), 27050 else => unreachable, 27051 } 27052 break :result dst_mcv; 27053 } 27054 27055 const abi_size: u32 = switch (ty.abiSize(zcu)) { 27056 1...16 => 16, 27057 17...32 => 32, 27058 else => return self.fail("TODO implement floatSign for {}", .{ 27059 ty.fmt(pt), 27060 }), 27061 }; 27062 27063 const src_mcv = try self.resolveInst(operand); 27064 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 27065 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 27066 27067 const dst_mcv: MCValue = if (src_mcv.isRegister() and 27068 self.reuseOperand(inst, operand, 0, src_mcv)) 27069 src_mcv 27070 else if (self.hasFeature(.avx)) 27071 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 27072 else 27073 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); 27074 const dst_reg = dst_mcv.getReg().?; 27075 const dst_lock = self.register_manager.lockReg(dst_reg); 27076 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 27077 27078 const vec_ty = try pt.vectorType(.{ 27079 .len = @divExact(abi_size * 8, scalar_bits), 27080 .child = (try pt.intType(.signed, scalar_bits)).ip_index, 27081 }); 27082 27083 const sign_mcv = try self.genTypedValue(switch (tag) { 27084 .neg => try vec_ty.minInt(pt, vec_ty), 27085 .abs => try vec_ty.maxInt(pt, vec_ty), 27086 else => unreachable, 27087 }); 27088 const sign_mem: Memory = if (sign_mcv.isBase()) 27089 try sign_mcv.mem(self, .{ .size = .fromSize(abi_size) }) 27090 else 27091 .{ 27092 .base = .{ .reg = try self.copyToTmpRegister(.usize, sign_mcv.address()) }, 27093 .mod = .{ .rm = .{ .size = .fromSize(abi_size) } }, 27094 }; 27095 27096 if (self.hasFeature(.avx)) try self.asmRegisterRegisterMemory( 27097 switch (scalar_bits) { 27098 16, 128 => if (abi_size <= 16 or self.hasFeature(.avx2)) switch (tag) { 27099 .neg => .{ .vp_, .xor }, 27100 .abs => .{ .vp_, .@"and" }, 27101 else => unreachable, 27102 } else switch (tag) { 27103 .neg => .{ .v_ps, .xor }, 27104 .abs => .{ .v_ps, .@"and" }, 27105 else => unreachable, 27106 }, 27107 32 => switch (tag) { 27108 .neg => .{ .v_ps, .xor }, 27109 .abs => .{ .v_ps, .@"and" }, 27110 else => unreachable, 27111 }, 27112 64 => switch (tag) { 27113 .neg => .{ .v_pd, .xor }, 27114 .abs => .{ .v_pd, .@"and" }, 27115 else => unreachable, 27116 }, 27117 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(pt)}), 27118 else => unreachable, 27119 }, 27120 registerAlias(dst_reg, abi_size), 27121 registerAlias(if (src_mcv.isRegister()) 27122 src_mcv.getReg().? 27123 else 27124 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27125 sign_mem, 27126 ) else try self.asmRegisterMemory( 27127 switch (scalar_bits) { 27128 16, 128 => switch (tag) { 27129 .neg => .{ .p_, .xor }, 27130 .abs => .{ .p_, .@"and" }, 27131 else => unreachable, 27132 }, 27133 32 => switch (tag) { 27134 .neg => .{ ._ps, .xor }, 27135 .abs => .{ ._ps, .@"and" }, 27136 else => unreachable, 27137 }, 27138 64 => switch (tag) { 27139 .neg => .{ ._pd, .xor }, 27140 .abs => .{ ._pd, .@"and" }, 27141 else => unreachable, 27142 }, 27143 80 => return self.fail("TODO implement floatSign for {}", .{ty.fmt(pt)}), 27144 else => unreachable, 27145 }, 27146 registerAlias(dst_reg, abi_size), 27147 sign_mem, 27148 ); 27149 break :result dst_mcv; 27150 }; 27151 return self.finishAir(inst, result, .{ operand, .none, .none }); 27152 } 27153 27154 fn airFloatSign(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 27155 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 27156 const ty = self.typeOf(un_op); 27157 return self.floatSign(inst, tag, un_op, ty); 27158 } 27159 27160 fn airRound(self: *CodeGen, inst: Air.Inst.Index, mode: bits.RoundMode) !void { 27161 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 27162 const ty = self.typeOf(un_op); 27163 27164 const result = result: { 27165 switch (try self.genRoundLibcall(ty, .{ .air_ref = un_op }, mode)) { 27166 .none => {}, 27167 else => |dst_mcv| break :result dst_mcv, 27168 } 27169 27170 const src_mcv = try self.resolveInst(un_op); 27171 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv)) 27172 src_mcv 27173 else 27174 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); 27175 const dst_reg = dst_mcv.getReg().?; 27176 const dst_lock = self.register_manager.lockReg(dst_reg); 27177 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 27178 try self.genRound(ty, dst_reg, src_mcv, mode); 27179 break :result dst_mcv; 27180 }; 27181 return self.finishAir(inst, result, .{ un_op, .none, .none }); 27182 } 27183 27184 fn getRoundTag(self: *CodeGen, ty: Type) ?Mir.Inst.FixedTag { 27185 const pt = self.pt; 27186 const zcu = pt.zcu; 27187 return if (self.hasFeature(.sse4_1)) switch (ty.zigTypeTag(zcu)) { 27188 .float => switch (ty.floatBits(self.target.*)) { 27189 32 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round }, 27190 64 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round }, 27191 16, 80, 128 => null, 27192 else => unreachable, 27193 }, 27194 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 27195 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 27196 32 => switch (ty.vectorLen(zcu)) { 27197 1 => if (self.hasFeature(.avx)) .{ .v_ss, .round } else .{ ._ss, .round }, 27198 2...4 => if (self.hasFeature(.avx)) .{ .v_ps, .round } else .{ ._ps, .round }, 27199 5...8 => if (self.hasFeature(.avx)) .{ .v_ps, .round } else null, 27200 else => null, 27201 }, 27202 64 => switch (ty.vectorLen(zcu)) { 27203 1 => if (self.hasFeature(.avx)) .{ .v_sd, .round } else .{ ._sd, .round }, 27204 2 => if (self.hasFeature(.avx)) .{ .v_pd, .round } else .{ ._pd, .round }, 27205 3...4 => if (self.hasFeature(.avx)) .{ .v_pd, .round } else null, 27206 else => null, 27207 }, 27208 16, 80, 128 => null, 27209 else => unreachable, 27210 }, 27211 else => null, 27212 }, 27213 else => unreachable, 27214 } else null; 27215 } 27216 27217 fn genRoundLibcall(self: *CodeGen, ty: Type, src_mcv: MCValue, mode: bits.RoundMode) !MCValue { 27218 const pt = self.pt; 27219 const zcu = pt.zcu; 27220 if (self.getRoundTag(ty)) |_| return .none; 27221 27222 if (ty.zigTypeTag(zcu) != .float) 27223 return self.fail("TODO implement genRound for {}", .{ty.fmt(pt)}); 27224 27225 var callee_buf: ["__trunc?".len]u8 = undefined; 27226 return try self.genCall(.{ .lib = .{ 27227 .return_type = ty.toIntern(), 27228 .param_types = &.{ty.toIntern()}, 27229 .callee = std.fmt.bufPrint(&callee_buf, "{s}{s}{s}", .{ 27230 floatLibcAbiPrefix(ty), 27231 switch (mode.mode) { 27232 .down => "floor", 27233 .up => "ceil", 27234 .zero => "trunc", 27235 else => unreachable, 27236 }, 27237 floatLibcAbiSuffix(ty), 27238 }) catch unreachable, 27239 } }, &.{ty}, &.{src_mcv}, .{}); 27240 } 27241 27242 fn genRound(self: *CodeGen, ty: Type, dst_reg: Register, src_mcv: MCValue, mode: bits.RoundMode) !void { 27243 const pt = self.pt; 27244 const mir_tag = self.getRoundTag(ty) orelse { 27245 const result = try self.genRoundLibcall(ty, src_mcv, mode); 27246 return self.genSetReg(dst_reg, ty, result, .{}); 27247 }; 27248 const abi_size: u32 = @intCast(ty.abiSize(pt.zcu)); 27249 const dst_alias = registerAlias(dst_reg, abi_size); 27250 switch (mir_tag[0]) { 27251 .v_ss, .v_sd => if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 27252 mir_tag, 27253 dst_alias, 27254 dst_alias, 27255 try src_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 27256 mode.imm(), 27257 ) else try self.asmRegisterRegisterRegisterImmediate( 27258 mir_tag, 27259 dst_alias, 27260 dst_alias, 27261 registerAlias(if (src_mcv.isRegister()) 27262 src_mcv.getReg().? 27263 else 27264 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27265 mode.imm(), 27266 ), 27267 else => if (src_mcv.isBase()) try self.asmRegisterMemoryImmediate( 27268 mir_tag, 27269 dst_alias, 27270 try src_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 27271 mode.imm(), 27272 ) else try self.asmRegisterRegisterImmediate( 27273 mir_tag, 27274 dst_alias, 27275 registerAlias(if (src_mcv.isRegister()) 27276 src_mcv.getReg().? 27277 else 27278 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27279 mode.imm(), 27280 ), 27281 } 27282 } 27283 27284 fn airAbs(self: *CodeGen, inst: Air.Inst.Index) !void { 27285 const pt = self.pt; 27286 const zcu = pt.zcu; 27287 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 27288 const ty = self.typeOf(ty_op.operand); 27289 27290 const result: MCValue = result: { 27291 const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.zigTypeTag(zcu)) { 27292 else => null, 27293 .int => switch (ty.abiSize(zcu)) { 27294 0 => unreachable, 27295 1...8 => { 27296 try self.spillEflagsIfOccupied(); 27297 const src_mcv = try self.resolveInst(ty_op.operand); 27298 const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); 27299 27300 try self.genUnOpMir(.{ ._, .neg }, ty, dst_mcv); 27301 27302 const cmov_abi_size = @max(@as(u32, @intCast(ty.abiSize(zcu))), 2); 27303 switch (src_mcv) { 27304 .register => |val_reg| try self.asmCmovccRegisterRegister( 27305 .l, 27306 registerAlias(dst_mcv.register, cmov_abi_size), 27307 registerAlias(val_reg, cmov_abi_size), 27308 ), 27309 .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory( 27310 .l, 27311 registerAlias(dst_mcv.register, cmov_abi_size), 27312 try src_mcv.mem(self, .{ .size = .fromSize(cmov_abi_size) }), 27313 ), 27314 else => { 27315 const val_reg = try self.copyToTmpRegister(ty, src_mcv); 27316 try self.asmCmovccRegisterRegister( 27317 .l, 27318 registerAlias(dst_mcv.register, cmov_abi_size), 27319 registerAlias(val_reg, cmov_abi_size), 27320 ); 27321 }, 27322 } 27323 break :result dst_mcv; 27324 }, 27325 9...16 => { 27326 try self.spillEflagsIfOccupied(); 27327 const src_mcv = try self.resolveInst(ty_op.operand); 27328 const dst_mcv = if (src_mcv == .register_pair and 27329 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { 27330 const dst_regs = try self.register_manager.allocRegs( 27331 2, 27332 .{ inst, inst }, 27333 abi.RegisterClass.gp, 27334 ); 27335 const dst_mcv: MCValue = .{ .register_pair = dst_regs }; 27336 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 27337 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 27338 27339 try self.genCopy(ty, dst_mcv, src_mcv, .{}); 27340 break :dst dst_mcv; 27341 }; 27342 const dst_regs = dst_mcv.register_pair; 27343 const dst_locks = self.register_manager.lockRegs(2, dst_regs); 27344 defer for (dst_locks) |dst_lock| if (dst_lock) |lock| 27345 self.register_manager.unlockReg(lock); 27346 27347 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 27348 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 27349 defer self.register_manager.unlockReg(tmp_lock); 27350 27351 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_regs[1]); 27352 try self.asmRegisterImmediate(.{ ._r, .sa }, tmp_reg, .u(63)); 27353 try self.asmRegisterRegister(.{ ._, .xor }, dst_regs[0], tmp_reg); 27354 try self.asmRegisterRegister(.{ ._, .xor }, dst_regs[1], tmp_reg); 27355 try self.asmRegisterRegister(.{ ._, .sub }, dst_regs[0], tmp_reg); 27356 try self.asmRegisterRegister(.{ ._, .sbb }, dst_regs[1], tmp_reg); 27357 27358 break :result dst_mcv; 27359 }, 27360 else => { 27361 const abi_size: u31 = @intCast(ty.abiSize(zcu)); 27362 const limb_len = std.math.divCeil(u31, abi_size, 8) catch unreachable; 27363 27364 const tmp_regs = 27365 try self.register_manager.allocRegs(3, @splat(null), abi.RegisterClass.gp); 27366 const tmp_locks = self.register_manager.lockRegsAssumeUnused(3, tmp_regs); 27367 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); 27368 27369 try self.spillEflagsIfOccupied(); 27370 const src_mcv = try self.resolveInst(ty_op.operand); 27371 const dst_mcv = if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 27372 src_mcv 27373 else 27374 try self.allocRegOrMem(inst, false); 27375 27376 try self.asmMemoryImmediate( 27377 .{ ._, .cmp }, 27378 try dst_mcv.address().offset((limb_len - 1) * 8).deref().mem(self, .{ .size = .qword }), 27379 .u(0), 27380 ); 27381 const positive = try self.asmJccReloc(.ns, undefined); 27382 27383 try self.asmRegisterRegister(.{ ._, .xor }, tmp_regs[0].to32(), tmp_regs[0].to32()); 27384 try self.asmRegisterRegister(.{ ._, .xor }, tmp_regs[1].to8(), tmp_regs[1].to8()); 27385 27386 const neg_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 27387 try self.asmRegisterRegister(.{ ._, .xor }, tmp_regs[2].to32(), tmp_regs[2].to32()); 27388 try self.asmRegisterImmediate(.{ ._r, .sh }, tmp_regs[1].to8(), .u(1)); 27389 try self.asmRegisterMemory(.{ ._, .sbb }, tmp_regs[2].to64(), .{ 27390 .base = .{ .frame = dst_mcv.load_frame.index }, 27391 .mod = .{ .rm = .{ 27392 .size = .qword, 27393 .index = tmp_regs[0].to64(), 27394 .scale = .@"8", 27395 .disp = dst_mcv.load_frame.off, 27396 } }, 27397 }); 27398 try self.asmSetccRegister(.c, tmp_regs[1].to8()); 27399 try self.asmMemoryRegister(.{ ._, .mov }, .{ 27400 .base = .{ .frame = dst_mcv.load_frame.index }, 27401 .mod = .{ .rm = .{ 27402 .size = .qword, 27403 .index = tmp_regs[0].to64(), 27404 .scale = .@"8", 27405 .disp = dst_mcv.load_frame.off, 27406 } }, 27407 }, tmp_regs[2].to64()); 27408 27409 if (self.hasFeature(.slow_incdec)) { 27410 try self.asmRegisterImmediate(.{ ._, .add }, tmp_regs[0].to32(), .u(1)); 27411 } else { 27412 try self.asmRegister(.{ ._c, .in }, tmp_regs[0].to32()); 27413 } 27414 try self.asmRegisterImmediate(.{ ._, .cmp }, tmp_regs[0].to32(), .u(limb_len)); 27415 _ = try self.asmJccReloc(.b, neg_loop); 27416 27417 self.performReloc(positive); 27418 break :result dst_mcv; 27419 }, 27420 }, 27421 .float => return self.floatSign(inst, .abs, ty_op.operand, ty), 27422 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 27423 else => null, 27424 .int => switch (ty.childType(zcu).intInfo(zcu).bits) { 27425 else => null, 27426 8 => switch (ty.vectorLen(zcu)) { 27427 else => null, 27428 1...16 => if (self.hasFeature(.avx)) 27429 .{ .vp_b, .abs } 27430 else if (self.hasFeature(.ssse3)) 27431 .{ .p_b, .abs } 27432 else 27433 null, 27434 17...32 => if (self.hasFeature(.avx2)) .{ .vp_b, .abs } else null, 27435 }, 27436 16 => switch (ty.vectorLen(zcu)) { 27437 else => null, 27438 1...8 => if (self.hasFeature(.avx)) 27439 .{ .vp_w, .abs } 27440 else if (self.hasFeature(.ssse3)) 27441 .{ .p_w, .abs } 27442 else 27443 null, 27444 9...16 => if (self.hasFeature(.avx2)) .{ .vp_w, .abs } else null, 27445 }, 27446 32 => switch (ty.vectorLen(zcu)) { 27447 else => null, 27448 1...4 => if (self.hasFeature(.avx)) 27449 .{ .vp_d, .abs } 27450 else if (self.hasFeature(.ssse3)) 27451 .{ .p_d, .abs } 27452 else 27453 null, 27454 5...8 => if (self.hasFeature(.avx2)) .{ .vp_d, .abs } else null, 27455 }, 27456 }, 27457 .float => return self.floatSign(inst, .abs, ty_op.operand, ty), 27458 }, 27459 }) orelse return self.fail("TODO implement airAbs for {}", .{ty.fmt(pt)}); 27460 27461 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 27462 const src_mcv = try self.resolveInst(ty_op.operand); 27463 const dst_reg = if (src_mcv.isRegister() and self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 27464 src_mcv.getReg().? 27465 else 27466 try self.register_manager.allocReg(inst, self.regSetForType(ty)); 27467 const dst_alias = registerAlias(dst_reg, abi_size); 27468 if (src_mcv.isBase()) try self.asmRegisterMemory( 27469 mir_tag, 27470 dst_alias, 27471 try src_mcv.mem(self, .{ .size = self.memSize(ty) }), 27472 ) else try self.asmRegisterRegister( 27473 mir_tag, 27474 dst_alias, 27475 registerAlias(if (src_mcv.isRegister()) 27476 src_mcv.getReg().? 27477 else 27478 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27479 ); 27480 break :result .{ .register = dst_reg }; 27481 }; 27482 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 27483 } 27484 27485 fn airSqrt(self: *CodeGen, inst: Air.Inst.Index) !void { 27486 const pt = self.pt; 27487 const zcu = pt.zcu; 27488 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 27489 const ty = self.typeOf(un_op); 27490 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 27491 27492 const result: MCValue = result: { 27493 switch (ty.zigTypeTag(zcu)) { 27494 .float => { 27495 const float_bits = ty.floatBits(self.target.*); 27496 if (switch (float_bits) { 27497 16 => !self.hasFeature(.f16c), 27498 32, 64 => false, 27499 80, 128 => true, 27500 else => unreachable, 27501 }) { 27502 var callee_buf: ["__sqrt?".len]u8 = undefined; 27503 break :result try self.genCall(.{ .lib = .{ 27504 .return_type = ty.toIntern(), 27505 .param_types = &.{ty.toIntern()}, 27506 .callee = std.fmt.bufPrint(&callee_buf, "{s}sqrt{s}", .{ 27507 floatLibcAbiPrefix(ty), 27508 floatLibcAbiSuffix(ty), 27509 }) catch unreachable, 27510 } }, &.{ty}, &.{.{ .air_ref = un_op }}, .{}); 27511 } 27512 }, 27513 else => {}, 27514 } 27515 27516 const src_mcv = try self.resolveInst(un_op); 27517 const dst_mcv = if (src_mcv.isRegister() and self.reuseOperand(inst, un_op, 0, src_mcv)) 27518 src_mcv 27519 else 27520 try self.copyToRegisterWithInstTracking(inst, ty, src_mcv); 27521 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size); 27522 const dst_lock = self.register_manager.lockReg(dst_reg); 27523 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 27524 27525 const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.zigTypeTag(zcu)) { 27526 .float => switch (ty.floatBits(self.target.*)) { 27527 16 => { 27528 assert(self.hasFeature(.f16c)); 27529 const mat_src_reg = if (src_mcv.isRegister()) 27530 src_mcv.getReg().? 27531 else 27532 try self.copyToTmpRegister(ty, src_mcv); 27533 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, mat_src_reg.to128()); 27534 try self.asmRegisterRegisterRegister(.{ .v_ss, .sqrt }, dst_reg, dst_reg, dst_reg); 27535 try self.asmRegisterRegisterImmediate( 27536 .{ .v_, .cvtps2ph }, 27537 dst_reg, 27538 dst_reg, 27539 bits.RoundMode.imm(.{}), 27540 ); 27541 break :result dst_mcv; 27542 }, 27543 32 => if (self.hasFeature(.avx)) .{ .v_ss, .sqrt } else .{ ._ss, .sqrt }, 27544 64 => if (self.hasFeature(.avx)) .{ .v_sd, .sqrt } else .{ ._sd, .sqrt }, 27545 else => unreachable, 27546 }, 27547 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 27548 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 27549 16 => if (self.hasFeature(.f16c)) switch (ty.vectorLen(zcu)) { 27550 1 => { 27551 try self.asmRegisterRegister( 27552 .{ .v_ps, .cvtph2 }, 27553 dst_reg, 27554 (if (src_mcv.isRegister()) 27555 src_mcv.getReg().? 27556 else 27557 try self.copyToTmpRegister(ty, src_mcv)).to128(), 27558 ); 27559 try self.asmRegisterRegisterRegister( 27560 .{ .v_ss, .sqrt }, 27561 dst_reg, 27562 dst_reg, 27563 dst_reg, 27564 ); 27565 try self.asmRegisterRegisterImmediate( 27566 .{ .v_, .cvtps2ph }, 27567 dst_reg, 27568 dst_reg, 27569 bits.RoundMode.imm(.{}), 27570 ); 27571 break :result dst_mcv; 27572 }, 27573 2...8 => { 27574 const wide_reg = registerAlias(dst_reg, abi_size * 2); 27575 if (src_mcv.isBase()) try self.asmRegisterMemory( 27576 .{ .v_ps, .cvtph2 }, 27577 wide_reg, 27578 try src_mcv.mem(self, .{ .size = .fromSize( 27579 @intCast(@divExact(wide_reg.bitSize(), 16)), 27580 ) }), 27581 ) else try self.asmRegisterRegister( 27582 .{ .v_ps, .cvtph2 }, 27583 wide_reg, 27584 (if (src_mcv.isRegister()) 27585 src_mcv.getReg().? 27586 else 27587 try self.copyToTmpRegister(ty, src_mcv)).to128(), 27588 ); 27589 try self.asmRegisterRegister(.{ .v_ps, .sqrt }, wide_reg, wide_reg); 27590 try self.asmRegisterRegisterImmediate( 27591 .{ .v_, .cvtps2ph }, 27592 dst_reg, 27593 wide_reg, 27594 bits.RoundMode.imm(.{}), 27595 ); 27596 break :result dst_mcv; 27597 }, 27598 else => null, 27599 } else null, 27600 32 => switch (ty.vectorLen(zcu)) { 27601 1 => if (self.hasFeature(.avx)) .{ .v_ss, .sqrt } else .{ ._ss, .sqrt }, 27602 2...4 => if (self.hasFeature(.avx)) .{ .v_ps, .sqrt } else .{ ._ps, .sqrt }, 27603 5...8 => if (self.hasFeature(.avx)) .{ .v_ps, .sqrt } else null, 27604 else => null, 27605 }, 27606 64 => switch (ty.vectorLen(zcu)) { 27607 1 => if (self.hasFeature(.avx)) .{ .v_sd, .sqrt } else .{ ._sd, .sqrt }, 27608 2 => if (self.hasFeature(.avx)) .{ .v_pd, .sqrt } else .{ ._pd, .sqrt }, 27609 3...4 => if (self.hasFeature(.avx)) .{ .v_pd, .sqrt } else null, 27610 else => null, 27611 }, 27612 80, 128 => null, 27613 else => unreachable, 27614 }, 27615 else => unreachable, 27616 }, 27617 else => unreachable, 27618 }) orelse return self.fail("TODO implement airSqrt for {}", .{ty.fmt(pt)}); 27619 switch (mir_tag[0]) { 27620 .v_ss, .v_sd => if (src_mcv.isBase()) try self.asmRegisterRegisterMemory( 27621 mir_tag, 27622 dst_reg, 27623 dst_reg, 27624 try src_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 27625 ) else try self.asmRegisterRegisterRegister( 27626 mir_tag, 27627 dst_reg, 27628 dst_reg, 27629 registerAlias(if (src_mcv.isRegister()) 27630 src_mcv.getReg().? 27631 else 27632 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27633 ), 27634 else => if (src_mcv.isBase()) try self.asmRegisterMemory( 27635 mir_tag, 27636 dst_reg, 27637 try src_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 27638 ) else try self.asmRegisterRegister( 27639 mir_tag, 27640 dst_reg, 27641 registerAlias(if (src_mcv.isRegister()) 27642 src_mcv.getReg().? 27643 else 27644 try self.copyToTmpRegister(ty, src_mcv), abi_size), 27645 ), 27646 } 27647 break :result dst_mcv; 27648 }; 27649 return self.finishAir(inst, result, .{ un_op, .none, .none }); 27650 } 27651 27652 fn airUnaryMath(self: *CodeGen, inst: Air.Inst.Index, tag: Air.Inst.Tag) !void { 27653 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 27654 const ty = self.typeOf(un_op); 27655 var callee_buf: ["__round?".len]u8 = undefined; 27656 const result = try self.genCall(.{ .lib = .{ 27657 .return_type = ty.toIntern(), 27658 .param_types = &.{ty.toIntern()}, 27659 .callee = std.fmt.bufPrint(&callee_buf, "{s}{s}{s}", .{ 27660 floatLibcAbiPrefix(ty), 27661 switch (tag) { 27662 .sin, 27663 .cos, 27664 .tan, 27665 .exp, 27666 .exp2, 27667 .log, 27668 .log2, 27669 .log10, 27670 .round, 27671 => @tagName(tag), 27672 else => unreachable, 27673 }, 27674 floatLibcAbiSuffix(ty), 27675 }) catch unreachable, 27676 } }, &.{ty}, &.{.{ .air_ref = un_op }}, .{}); 27677 return self.finishAir(inst, result, .{ un_op, .none, .none }); 27678 } 27679 27680 fn reuseOperand( 27681 self: *CodeGen, 27682 inst: Air.Inst.Index, 27683 operand: Air.Inst.Ref, 27684 op_index: Liveness.OperandInt, 27685 mcv: MCValue, 27686 ) bool { 27687 return self.reuseOperandAdvanced(inst, operand, op_index, mcv, inst); 27688 } 27689 27690 fn reuseOperandAdvanced( 27691 self: *CodeGen, 27692 inst: Air.Inst.Index, 27693 operand: Air.Inst.Ref, 27694 op_index: Liveness.OperandInt, 27695 mcv: MCValue, 27696 maybe_tracked_inst: ?Air.Inst.Index, 27697 ) bool { 27698 if (!self.liveness.operandDies(inst, op_index)) 27699 return false; 27700 27701 switch (mcv) { 27702 .register, .register_pair, .register_overflow, .register_mask => for (mcv.getRegs()) |reg| { 27703 // If it's in the registers table, need to associate the register(s) with the 27704 // new instruction. 27705 if (maybe_tracked_inst) |tracked_inst| { 27706 if (!self.register_manager.isRegFree(reg)) { 27707 if (RegisterManager.indexOfRegIntoTracked(reg)) |index| { 27708 self.register_manager.registers[index] = tracked_inst; 27709 } 27710 } 27711 } else self.register_manager.freeReg(reg); 27712 }, 27713 .load_frame => |frame_addr| if (frame_addr.index.isNamed()) return false, 27714 else => return false, 27715 } 27716 switch (mcv) { 27717 .eflags, .register_overflow => self.eflags_inst = maybe_tracked_inst, 27718 else => {}, 27719 } 27720 27721 // Prevent the operand deaths processing code from deallocating it. 27722 self.reused_operands.set(op_index); 27723 const op_inst = operand.toIndex().?; 27724 self.getResolvedInstValue(op_inst).reuse(self, maybe_tracked_inst, op_inst); 27725 27726 return true; 27727 } 27728 27729 fn packedLoad(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { 27730 const pt = self.pt; 27731 const zcu = pt.zcu; 27732 27733 const ptr_info = ptr_ty.ptrInfo(zcu); 27734 const val_ty: Type = .fromInterned(ptr_info.child); 27735 if (!val_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; 27736 const val_abi_size: u32 = @intCast(val_ty.abiSize(zcu)); 27737 27738 const val_bit_size: u32 = @intCast(val_ty.bitSize(zcu)); 27739 const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) { 27740 .none => 0, 27741 .runtime => unreachable, 27742 else => |vector_index| @intFromEnum(vector_index) * val_bit_size, 27743 }; 27744 if (ptr_bit_off % 8 == 0) { 27745 { 27746 const mat_ptr_mcv: MCValue = switch (ptr_mcv) { 27747 .immediate, .register, .register_offset, .lea_frame => ptr_mcv, 27748 else => .{ .register = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, 27749 }; 27750 const mat_ptr_lock = switch (mat_ptr_mcv) { 27751 .register => |mat_ptr_reg| self.register_manager.lockReg(mat_ptr_reg), 27752 else => null, 27753 }; 27754 defer if (mat_ptr_lock) |lock| self.register_manager.unlockReg(lock); 27755 27756 try self.load(dst_mcv, ptr_ty, mat_ptr_mcv.offset(@intCast(@divExact(ptr_bit_off, 8)))); 27757 } 27758 27759 if (val_abi_size * 8 > val_bit_size) { 27760 if (dst_mcv.isRegister()) { 27761 try self.truncateRegister(val_ty, dst_mcv.getReg().?); 27762 } else { 27763 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 27764 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 27765 defer self.register_manager.unlockReg(tmp_lock); 27766 27767 const hi_mcv = dst_mcv.address().offset(@intCast(val_bit_size / 64 * 8)).deref(); 27768 try self.genSetReg(tmp_reg, .usize, hi_mcv, .{}); 27769 try self.truncateRegister(val_ty, tmp_reg); 27770 try self.genCopy(.usize, hi_mcv, .{ .register = tmp_reg }, .{}); 27771 } 27772 } 27773 return; 27774 } 27775 27776 if (val_abi_size > 8) return self.fail("TODO implement packed load of {}", .{val_ty.fmt(pt)}); 27777 27778 const limb_abi_size: u31 = @min(val_abi_size, 8); 27779 const limb_abi_bits = limb_abi_size * 8; 27780 const val_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size); 27781 const val_bit_off = ptr_bit_off % limb_abi_bits; 27782 const val_extra_bits = self.regExtraBits(val_ty); 27783 27784 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); 27785 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); 27786 defer self.register_manager.unlockReg(ptr_lock); 27787 27788 const dst_reg = switch (dst_mcv) { 27789 .register => |reg| reg, 27790 else => try self.register_manager.allocReg(null, abi.RegisterClass.gp), 27791 }; 27792 const dst_lock = self.register_manager.lockReg(dst_reg); 27793 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 27794 27795 const load_abi_size = 27796 if (val_bit_off < val_extra_bits) val_abi_size else val_abi_size * 2; 27797 if (load_abi_size <= 8) { 27798 const load_reg = registerAlias(dst_reg, load_abi_size); 27799 try self.asmRegisterMemory(.{ ._, .mov }, load_reg, .{ 27800 .base = .{ .reg = ptr_reg }, 27801 .mod = .{ .rm = .{ 27802 .size = .fromSize(load_abi_size), 27803 .disp = val_byte_off, 27804 } }, 27805 }); 27806 try self.spillEflagsIfOccupied(); 27807 try self.asmRegisterImmediate(.{ ._r, .sh }, load_reg, .u(val_bit_off)); 27808 } else { 27809 const tmp_reg = 27810 registerAlias(try self.register_manager.allocReg(null, abi.RegisterClass.gp), val_abi_size); 27811 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 27812 defer self.register_manager.unlockReg(tmp_lock); 27813 27814 const dst_alias = registerAlias(dst_reg, val_abi_size); 27815 try self.asmRegisterMemory(.{ ._, .mov }, dst_alias, .{ 27816 .base = .{ .reg = ptr_reg }, 27817 .mod = .{ .rm = .{ 27818 .size = .fromSize(val_abi_size), 27819 .disp = val_byte_off, 27820 } }, 27821 }); 27822 try self.asmRegisterMemory(.{ ._, .mov }, tmp_reg, .{ 27823 .base = .{ .reg = ptr_reg }, 27824 .mod = .{ .rm = .{ 27825 .size = .fromSize(val_abi_size), 27826 .disp = val_byte_off + limb_abi_size, 27827 } }, 27828 }); 27829 try self.spillEflagsIfOccupied(); 27830 try self.asmRegisterRegisterImmediate(.{ ._rd, .sh }, dst_alias, tmp_reg, .u(val_bit_off)); 27831 } 27832 27833 if (val_extra_bits > 0) try self.truncateRegister(val_ty, dst_reg); 27834 try self.genCopy(val_ty, dst_mcv, .{ .register = dst_reg }, .{}); 27835 } 27836 27837 fn load(self: *CodeGen, dst_mcv: MCValue, ptr_ty: Type, ptr_mcv: MCValue) InnerError!void { 27838 const pt = self.pt; 27839 const zcu = pt.zcu; 27840 const dst_ty = ptr_ty.childType(zcu); 27841 if (!dst_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; 27842 switch (ptr_mcv) { 27843 .none, 27844 .unreach, 27845 .dead, 27846 .undef, 27847 .eflags, 27848 .register_pair, 27849 .register_triple, 27850 .register_quadruple, 27851 .register_overflow, 27852 .register_mask, 27853 .elementwise_regs_then_frame, 27854 .reserved_frame, 27855 => unreachable, // not a valid pointer 27856 .immediate, 27857 .register, 27858 .register_offset, 27859 .lea_symbol, 27860 .lea_direct, 27861 .lea_got, 27862 .lea_tlv, 27863 .lea_frame, 27864 => try self.genCopy(dst_ty, dst_mcv, ptr_mcv.deref(), .{}), 27865 .memory, 27866 .indirect, 27867 .load_symbol, 27868 .load_direct, 27869 .load_got, 27870 .load_tlv, 27871 .load_frame, 27872 => { 27873 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); 27874 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 27875 defer self.register_manager.unlockReg(addr_lock); 27876 27877 try self.genCopy(dst_ty, dst_mcv, .{ .indirect = .{ .reg = addr_reg } }, .{}); 27878 }, 27879 .air_ref => |ptr_ref| try self.load(dst_mcv, ptr_ty, try self.resolveInst(ptr_ref)), 27880 } 27881 } 27882 27883 fn airLoad(self: *CodeGen, inst: Air.Inst.Index) !void { 27884 const pt = self.pt; 27885 const zcu = pt.zcu; 27886 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 27887 const elem_ty = self.typeOfIndex(inst); 27888 const result: MCValue = result: { 27889 if (!elem_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 27890 27891 try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); 27892 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); 27893 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 27894 27895 const ptr_ty = self.typeOf(ty_op.operand); 27896 const elem_size = elem_ty.abiSize(zcu); 27897 27898 const elem_rs = self.regSetForType(elem_ty); 27899 const ptr_rs = self.regSetForType(ptr_ty); 27900 27901 const ptr_mcv = try self.resolveInst(ty_op.operand); 27902 const dst_mcv = if (elem_size <= 8 and std.math.isPowerOfTwo(elem_size) and 27903 elem_rs.supersetOf(ptr_rs) and self.reuseOperand(inst, ty_op.operand, 0, ptr_mcv)) 27904 // The MCValue that holds the pointer can be re-used as the value. 27905 ptr_mcv 27906 else 27907 try self.allocRegOrMem(inst, true); 27908 27909 const ptr_info = ptr_ty.ptrInfo(zcu); 27910 if (ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0) { 27911 try self.packedLoad(dst_mcv, ptr_ty, ptr_mcv); 27912 } else { 27913 try self.load(dst_mcv, ptr_ty, ptr_mcv); 27914 } 27915 27916 if (elem_ty.isAbiInt(zcu) and elem_size * 8 > elem_ty.bitSize(zcu)) { 27917 const high_mcv: MCValue = switch (dst_mcv) { 27918 .register => |dst_reg| .{ .register = dst_reg }, 27919 .register_pair => |dst_regs| .{ .register = dst_regs[1] }, 27920 else => dst_mcv.address().offset(@intCast((elem_size - 1) / 8 * 8)).deref(), 27921 }; 27922 const high_reg = if (high_mcv.isRegister()) 27923 high_mcv.getReg().? 27924 else 27925 try self.copyToTmpRegister(.usize, high_mcv); 27926 const high_lock = self.register_manager.lockReg(high_reg); 27927 defer if (high_lock) |lock| self.register_manager.unlockReg(lock); 27928 27929 try self.truncateRegister(elem_ty, high_reg); 27930 if (!high_mcv.isRegister()) try self.genCopy( 27931 if (elem_size <= 8) elem_ty else .usize, 27932 high_mcv, 27933 .{ .register = high_reg }, 27934 .{}, 27935 ); 27936 } 27937 break :result dst_mcv; 27938 }; 27939 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 27940 } 27941 27942 fn packedStore(self: *CodeGen, ptr_ty: Type, ptr_mcv: MCValue, src_mcv: MCValue) InnerError!void { 27943 const pt = self.pt; 27944 const zcu = pt.zcu; 27945 const ptr_info = ptr_ty.ptrInfo(zcu); 27946 const src_ty: Type = .fromInterned(ptr_info.child); 27947 if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; 27948 27949 const limb_abi_size: u16 = @min(ptr_info.packed_offset.host_size, 8); 27950 const limb_abi_bits = limb_abi_size * 8; 27951 const limb_ty = try pt.intType(.unsigned, limb_abi_bits); 27952 27953 const src_bit_size = src_ty.bitSize(zcu); 27954 const ptr_bit_off = ptr_info.packed_offset.bit_offset + switch (ptr_info.flags.vector_index) { 27955 .none => 0, 27956 .runtime => unreachable, 27957 else => |vector_index| @intFromEnum(vector_index) * src_bit_size, 27958 }; 27959 const src_byte_off: i32 = @intCast(ptr_bit_off / limb_abi_bits * limb_abi_size); 27960 const src_bit_off = ptr_bit_off % limb_abi_bits; 27961 27962 const ptr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); 27963 const ptr_lock = self.register_manager.lockRegAssumeUnused(ptr_reg); 27964 defer self.register_manager.unlockReg(ptr_lock); 27965 27966 const mat_src_mcv: MCValue = mat_src_mcv: switch (src_mcv) { 27967 .register => if (src_bit_size > 64) { 27968 const frame_index = try self.allocFrameIndex(.initSpill(src_ty, self.pt.zcu)); 27969 try self.genSetMem(.{ .frame = frame_index }, 0, src_ty, src_mcv, .{}); 27970 break :mat_src_mcv .{ .load_frame = .{ .index = frame_index } }; 27971 } else src_mcv, 27972 else => src_mcv, 27973 }; 27974 27975 var limb_i: u16 = 0; 27976 while (limb_i * limb_abi_bits < src_bit_off + src_bit_size) : (limb_i += 1) { 27977 const part_bit_off = if (limb_i == 0) src_bit_off else 0; 27978 const part_bit_size = 27979 @min(src_bit_off + src_bit_size - limb_i * limb_abi_bits, limb_abi_bits) - part_bit_off; 27980 const limb_mem: Memory = .{ 27981 .base = .{ .reg = ptr_reg }, 27982 .mod = .{ .rm = .{ 27983 .size = .fromSize(limb_abi_size), 27984 .disp = src_byte_off + limb_i * limb_abi_size, 27985 } }, 27986 }; 27987 27988 const part_mask = (@as(u64, std.math.maxInt(u64)) >> @intCast(64 - part_bit_size)) << 27989 @intCast(part_bit_off); 27990 const part_mask_not = part_mask ^ (@as(u64, std.math.maxInt(u64)) >> @intCast(64 - limb_abi_bits)); 27991 if (limb_abi_size <= 4) { 27992 try self.asmMemoryImmediate(.{ ._, .@"and" }, limb_mem, .u(part_mask_not)); 27993 } else if (std.math.cast(i32, @as(i64, @bitCast(part_mask_not)))) |small| { 27994 try self.asmMemoryImmediate(.{ ._, .@"and" }, limb_mem, .s(small)); 27995 } else { 27996 const part_mask_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 27997 try self.asmRegisterImmediate(.{ ._, .mov }, part_mask_reg, .u(part_mask_not)); 27998 try self.asmMemoryRegister(.{ ._, .@"and" }, limb_mem, part_mask_reg); 27999 } 28000 28001 if (src_bit_size <= 64) { 28002 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28003 const tmp_mcv = MCValue{ .register = tmp_reg }; 28004 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28005 defer self.register_manager.unlockReg(tmp_lock); 28006 28007 try self.genSetReg(tmp_reg, limb_ty, mat_src_mcv, .{}); 28008 switch (limb_i) { 28009 0 => try self.genShiftBinOpMir( 28010 .{ ._l, .sh }, 28011 limb_ty, 28012 tmp_mcv, 28013 .u8, 28014 .{ .immediate = src_bit_off }, 28015 ), 28016 1 => try self.genShiftBinOpMir( 28017 .{ ._r, .sh }, 28018 limb_ty, 28019 tmp_mcv, 28020 .u8, 28021 .{ .immediate = limb_abi_bits - src_bit_off }, 28022 ), 28023 else => unreachable, 28024 } 28025 try self.genBinOpMir(.{ ._, .@"and" }, limb_ty, tmp_mcv, .{ .immediate = part_mask }); 28026 try self.asmMemoryRegister( 28027 .{ ._, .@"or" }, 28028 limb_mem, 28029 registerAlias(tmp_reg, limb_abi_size), 28030 ); 28031 } else if (src_bit_size <= 128 and src_bit_off == 0) { 28032 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28033 const tmp_mcv = MCValue{ .register = tmp_reg }; 28034 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28035 defer self.register_manager.unlockReg(tmp_lock); 28036 28037 try self.genSetReg(tmp_reg, limb_ty, switch (limb_i) { 28038 0 => mat_src_mcv, 28039 else => mat_src_mcv.address().offset(limb_i * limb_abi_size).deref(), 28040 }, .{}); 28041 try self.genBinOpMir(.{ ._, .@"and" }, limb_ty, tmp_mcv, .{ .immediate = part_mask }); 28042 try self.asmMemoryRegister( 28043 .{ ._, .@"or" }, 28044 limb_mem, 28045 registerAlias(tmp_reg, limb_abi_size), 28046 ); 28047 } else return self.fail("TODO: implement packed store of {}", .{src_ty.fmt(pt)}); 28048 } 28049 } 28050 28051 fn store( 28052 self: *CodeGen, 28053 ptr_ty: Type, 28054 ptr_mcv: MCValue, 28055 src_mcv: MCValue, 28056 opts: CopyOptions, 28057 ) InnerError!void { 28058 const pt = self.pt; 28059 const zcu = pt.zcu; 28060 const src_ty = ptr_ty.childType(zcu); 28061 if (!src_ty.hasRuntimeBitsIgnoreComptime(zcu)) return; 28062 switch (ptr_mcv) { 28063 .none, 28064 .unreach, 28065 .dead, 28066 .undef, 28067 .eflags, 28068 .register_pair, 28069 .register_triple, 28070 .register_quadruple, 28071 .register_overflow, 28072 .register_mask, 28073 .elementwise_regs_then_frame, 28074 .reserved_frame, 28075 => unreachable, // not a valid pointer 28076 .immediate, 28077 .register, 28078 .register_offset, 28079 .lea_symbol, 28080 .lea_direct, 28081 .lea_got, 28082 .lea_tlv, 28083 .lea_frame, 28084 => try self.genCopy(src_ty, ptr_mcv.deref(), src_mcv, opts), 28085 .memory, 28086 .indirect, 28087 .load_symbol, 28088 .load_direct, 28089 .load_got, 28090 .load_tlv, 28091 .load_frame, 28092 => { 28093 const addr_reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv); 28094 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 28095 defer self.register_manager.unlockReg(addr_lock); 28096 28097 try self.genCopy(src_ty, .{ .indirect = .{ .reg = addr_reg } }, src_mcv, opts); 28098 }, 28099 .air_ref => |ptr_ref| try self.store(ptr_ty, try self.resolveInst(ptr_ref), src_mcv, opts), 28100 } 28101 } 28102 28103 fn airStore(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { 28104 const pt = self.pt; 28105 const zcu = pt.zcu; 28106 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 28107 28108 result: { 28109 if (!safety and (try self.resolveInst(bin_op.rhs)) == .undef) break :result; 28110 28111 try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); 28112 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); 28113 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 28114 28115 const src_mcv = try self.resolveInst(bin_op.rhs); 28116 const ptr_mcv = try self.resolveInst(bin_op.lhs); 28117 const ptr_ty = self.typeOf(bin_op.lhs); 28118 28119 const ptr_info = ptr_ty.ptrInfo(zcu); 28120 if (ptr_info.flags.vector_index != .none or ptr_info.packed_offset.host_size > 0) { 28121 try self.packedStore(ptr_ty, ptr_mcv, src_mcv); 28122 } else { 28123 try self.store(ptr_ty, ptr_mcv, src_mcv, .{ .safety = safety }); 28124 } 28125 } 28126 return self.finishAir(inst, .none, .{ bin_op.lhs, bin_op.rhs, .none }); 28127 } 28128 28129 fn airStructFieldPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 28130 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 28131 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 28132 const result = try self.fieldPtr(inst, extra.struct_operand, extra.field_index); 28133 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 28134 } 28135 28136 fn airStructFieldPtrIndex(self: *CodeGen, inst: Air.Inst.Index, field_index: u8) !void { 28137 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 28138 const result = try self.fieldPtr(inst, ty_op.operand, field_index); 28139 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 28140 } 28141 28142 fn fieldPtr(self: *CodeGen, inst: Air.Inst.Index, operand: Air.Inst.Ref, field_index: u32) !MCValue { 28143 const ptr_field_ty = self.typeOfIndex(inst); 28144 28145 const src_mcv = try self.resolveInst(operand); 28146 const dst_mcv = if (switch (src_mcv) { 28147 .immediate, .lea_frame => true, 28148 .register, .register_offset => self.reuseOperand(inst, operand, 0, src_mcv), 28149 else => false, 28150 }) src_mcv else try self.copyToRegisterWithInstTracking(inst, ptr_field_ty, src_mcv); 28151 return dst_mcv.offset(self.fieldOffset(self.typeOf(operand), ptr_field_ty, field_index)); 28152 } 28153 28154 fn fieldOffset(self: *CodeGen, ptr_agg_ty: Type, ptr_field_ty: Type, field_index: u32) i32 { 28155 const pt = self.pt; 28156 const zcu = pt.zcu; 28157 const agg_ty = ptr_agg_ty.childType(zcu); 28158 return switch (agg_ty.containerLayout(zcu)) { 28159 .auto, .@"extern" => @intCast(agg_ty.structFieldOffset(field_index, zcu)), 28160 .@"packed" => @divExact(@as(i32, ptr_agg_ty.ptrInfo(zcu).packed_offset.bit_offset) + 28161 (if (zcu.typeToStruct(agg_ty)) |loaded_struct| pt.structPackedFieldBitOffset(loaded_struct, field_index) else 0) - 28162 ptr_field_ty.ptrInfo(zcu).packed_offset.bit_offset, 8), 28163 }; 28164 } 28165 28166 fn airStructFieldVal(self: *CodeGen, inst: Air.Inst.Index) !void { 28167 const pt = self.pt; 28168 const zcu = pt.zcu; 28169 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 28170 const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; 28171 const result: MCValue = result: { 28172 const operand = extra.struct_operand; 28173 const index = extra.field_index; 28174 28175 const container_ty = self.typeOf(operand); 28176 const container_rc = self.regSetForType(container_ty); 28177 const field_ty = container_ty.fieldType(index, zcu); 28178 if (!field_ty.hasRuntimeBitsIgnoreComptime(zcu)) break :result .none; 28179 const field_rc = self.regSetForType(field_ty); 28180 const field_is_gp = field_rc.supersetOf(abi.RegisterClass.gp); 28181 28182 const src_mcv = try self.resolveInst(operand); 28183 const field_off: u32 = switch (container_ty.containerLayout(zcu)) { 28184 .auto, .@"extern" => @intCast(container_ty.structFieldOffset(extra.field_index, zcu) * 8), 28185 .@"packed" => if (zcu.typeToStruct(container_ty)) |loaded_struct| 28186 pt.structPackedFieldBitOffset(loaded_struct, extra.field_index) 28187 else 28188 0, 28189 }; 28190 28191 switch (src_mcv) { 28192 .register => |src_reg| { 28193 const src_reg_lock = self.register_manager.lockRegAssumeUnused(src_reg); 28194 defer self.register_manager.unlockReg(src_reg_lock); 28195 28196 const src_in_field_rc = 28197 field_rc.isSet(RegisterManager.indexOfRegIntoTracked(src_reg).?); 28198 const dst_reg = if (src_in_field_rc and self.reuseOperand(inst, operand, 0, src_mcv)) 28199 src_reg 28200 else if (field_off == 0) 28201 (try self.copyToRegisterWithInstTracking(inst, field_ty, src_mcv)).register 28202 else 28203 try self.copyToTmpRegister(.usize, .{ .register = src_reg }); 28204 const dst_mcv: MCValue = .{ .register = dst_reg }; 28205 const dst_lock = self.register_manager.lockReg(dst_reg); 28206 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 28207 28208 if (field_off > 0) { 28209 try self.spillEflagsIfOccupied(); 28210 try self.genShiftBinOpMir(.{ ._r, .sh }, .usize, dst_mcv, .u8, .{ .immediate = field_off }); 28211 } 28212 if (abi.RegisterClass.gp.isSet(RegisterManager.indexOfRegIntoTracked(dst_reg).?) and 28213 container_ty.abiSize(zcu) * 8 > field_ty.bitSize(zcu)) 28214 try self.truncateRegister(field_ty, dst_reg); 28215 28216 break :result if (field_off == 0 or field_rc.supersetOf(abi.RegisterClass.gp)) 28217 dst_mcv 28218 else 28219 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); 28220 }, 28221 .register_pair => |src_regs| { 28222 const src_regs_lock = self.register_manager.lockRegsAssumeUnused(2, src_regs); 28223 defer for (src_regs_lock) |lock| self.register_manager.unlockReg(lock); 28224 28225 const field_bit_size: u32 = @intCast(field_ty.bitSize(zcu)); 28226 const src_reg = if (field_off + field_bit_size <= 64) 28227 src_regs[0] 28228 else if (field_off >= 64) 28229 src_regs[1] 28230 else { 28231 const dst_regs: [2]Register = if (field_rc.supersetOf(container_rc) and 28232 self.reuseOperand(inst, operand, 0, src_mcv)) src_regs else dst: { 28233 const dst_regs = 28234 try self.register_manager.allocRegs(2, @splat(null), field_rc); 28235 const dst_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 28236 defer for (dst_locks) |lock| self.register_manager.unlockReg(lock); 28237 28238 try self.genCopy(container_ty, .{ .register_pair = dst_regs }, src_mcv, .{}); 28239 break :dst dst_regs; 28240 }; 28241 const dst_mcv = MCValue{ .register_pair = dst_regs }; 28242 const dst_locks = self.register_manager.lockRegs(2, dst_regs); 28243 defer for (dst_locks) |dst_lock| if (dst_lock) |lock| 28244 self.register_manager.unlockReg(lock); 28245 28246 if (field_off > 0) { 28247 try self.spillEflagsIfOccupied(); 28248 try self.genShiftBinOpMir(.{ ._r, .sh }, .u128, dst_mcv, .u8, .{ .immediate = field_off }); 28249 } 28250 28251 if (field_bit_size <= 64) { 28252 if (self.regExtraBits(field_ty) > 0) 28253 try self.truncateRegister(field_ty, dst_regs[0]); 28254 break :result if (field_rc.supersetOf(abi.RegisterClass.gp)) 28255 .{ .register = dst_regs[0] } 28256 else 28257 try self.copyToRegisterWithInstTracking(inst, field_ty, .{ 28258 .register = dst_regs[0], 28259 }); 28260 } 28261 28262 if (field_bit_size < 128) try self.truncateRegister( 28263 try pt.intType(.unsigned, @intCast(field_bit_size - 64)), 28264 dst_regs[1], 28265 ); 28266 break :result if (field_rc.supersetOf(abi.RegisterClass.gp)) 28267 dst_mcv 28268 else 28269 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); 28270 }; 28271 28272 const dst_reg = try self.copyToTmpRegister(.usize, .{ .register = src_reg }); 28273 const dst_mcv = MCValue{ .register = dst_reg }; 28274 const dst_lock = self.register_manager.lockReg(dst_reg); 28275 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 28276 28277 if (field_off % 64 > 0) { 28278 try self.spillEflagsIfOccupied(); 28279 try self.genShiftBinOpMir(.{ ._r, .sh }, .usize, dst_mcv, .u8, .{ .immediate = field_off % 64 }); 28280 } 28281 if (self.regExtraBits(field_ty) > 0) try self.truncateRegister(field_ty, dst_reg); 28282 28283 break :result if (field_rc.supersetOf(abi.RegisterClass.gp)) 28284 dst_mcv 28285 else 28286 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); 28287 }, 28288 .register_overflow => |ro| { 28289 switch (index) { 28290 // Get wrapped value for overflow operation. 28291 0 => if (self.reuseOperand(inst, extra.struct_operand, 0, src_mcv)) { 28292 self.eflags_inst = null; // actually stop tracking the overflow part 28293 break :result .{ .register = ro.reg }; 28294 } else break :result try self.copyToRegisterWithInstTracking(inst, .usize, .{ .register = ro.reg }), 28295 // Get overflow bit. 28296 1 => if (self.reuseOperandAdvanced(inst, extra.struct_operand, 0, src_mcv, null)) { 28297 self.eflags_inst = inst; // actually keep tracking the overflow part 28298 break :result .{ .eflags = ro.eflags }; 28299 } else { 28300 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 28301 try self.asmSetccRegister(ro.eflags, dst_reg.to8()); 28302 break :result .{ .register = dst_reg.to8() }; 28303 }, 28304 else => unreachable, 28305 } 28306 }, 28307 .load_frame => |frame_addr| { 28308 const field_abi_size: u32 = @intCast(field_ty.abiSize(zcu)); 28309 if (field_off % 8 == 0) { 28310 const field_byte_off = @divExact(field_off, 8); 28311 const off_mcv = src_mcv.address().offset(@intCast(field_byte_off)).deref(); 28312 const field_bit_size = field_ty.bitSize(zcu); 28313 28314 if (field_abi_size <= 8) { 28315 const int_ty = try pt.intType( 28316 if (field_ty.isAbiInt(zcu)) field_ty.intInfo(zcu).signedness else .unsigned, 28317 @intCast(field_bit_size), 28318 ); 28319 28320 const dst_reg = try self.register_manager.allocReg( 28321 if (field_is_gp) inst else null, 28322 abi.RegisterClass.gp, 28323 ); 28324 const dst_mcv = MCValue{ .register = dst_reg }; 28325 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 28326 defer self.register_manager.unlockReg(dst_lock); 28327 28328 try self.genCopy(int_ty, dst_mcv, off_mcv, .{}); 28329 if (self.regExtraBits(field_ty) > 0) try self.truncateRegister(int_ty, dst_reg); 28330 break :result if (field_is_gp) 28331 dst_mcv 28332 else 28333 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); 28334 } 28335 28336 const container_abi_size: u32 = @intCast(container_ty.abiSize(zcu)); 28337 const dst_mcv = if (field_byte_off + field_abi_size <= container_abi_size and 28338 self.reuseOperand(inst, operand, 0, src_mcv)) 28339 off_mcv 28340 else dst: { 28341 const dst_mcv = try self.allocRegOrMem(inst, true); 28342 try self.genCopy(field_ty, dst_mcv, off_mcv, .{}); 28343 break :dst dst_mcv; 28344 }; 28345 if (field_abi_size * 8 > field_bit_size and dst_mcv.isBase()) { 28346 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28347 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28348 defer self.register_manager.unlockReg(tmp_lock); 28349 28350 const hi_mcv = 28351 dst_mcv.address().offset(@intCast(field_bit_size / 64 * 8)).deref(); 28352 try self.genSetReg(tmp_reg, .usize, hi_mcv, .{}); 28353 try self.truncateRegister(field_ty, tmp_reg); 28354 try self.genCopy(.usize, hi_mcv, .{ .register = tmp_reg }, .{}); 28355 } 28356 break :result dst_mcv; 28357 } 28358 28359 const limb_abi_size: u31 = @min(field_abi_size, 8); 28360 const limb_abi_bits = limb_abi_size * 8; 28361 const field_byte_off: i32 = @intCast(field_off / limb_abi_bits * limb_abi_size); 28362 const field_bit_off = field_off % limb_abi_bits; 28363 28364 if (field_abi_size > 8) { 28365 return self.fail("TODO implement struct_field_val with large packed field", .{}); 28366 } 28367 28368 const dst_reg = try self.register_manager.allocReg( 28369 if (field_is_gp) inst else null, 28370 abi.RegisterClass.gp, 28371 ); 28372 const field_extra_bits = self.regExtraBits(field_ty); 28373 const load_abi_size = 28374 if (field_bit_off < field_extra_bits) field_abi_size else field_abi_size * 2; 28375 if (load_abi_size <= 8) { 28376 const load_reg = registerAlias(dst_reg, load_abi_size); 28377 try self.asmRegisterMemory(.{ ._, .mov }, load_reg, .{ 28378 .base = .{ .frame = frame_addr.index }, 28379 .mod = .{ .rm = .{ 28380 .size = .fromSize(load_abi_size), 28381 .disp = frame_addr.off + field_byte_off, 28382 } }, 28383 }); 28384 try self.spillEflagsIfOccupied(); 28385 try self.asmRegisterImmediate(.{ ._r, .sh }, load_reg, .u(field_bit_off)); 28386 } else { 28387 const tmp_reg = registerAlias( 28388 try self.register_manager.allocReg(null, abi.RegisterClass.gp), 28389 field_abi_size, 28390 ); 28391 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28392 defer self.register_manager.unlockReg(tmp_lock); 28393 28394 const dst_alias = registerAlias(dst_reg, field_abi_size); 28395 try self.asmRegisterMemory( 28396 .{ ._, .mov }, 28397 dst_alias, 28398 .{ 28399 .base = .{ .frame = frame_addr.index }, 28400 .mod = .{ .rm = .{ 28401 .size = .fromSize(field_abi_size), 28402 .disp = frame_addr.off + field_byte_off, 28403 } }, 28404 }, 28405 ); 28406 try self.asmRegisterMemory(.{ ._, .mov }, tmp_reg, .{ 28407 .base = .{ .frame = frame_addr.index }, 28408 .mod = .{ .rm = .{ 28409 .size = .fromSize(field_abi_size), 28410 .disp = frame_addr.off + field_byte_off + limb_abi_size, 28411 } }, 28412 }); 28413 try self.spillEflagsIfOccupied(); 28414 try self.asmRegisterRegisterImmediate( 28415 .{ ._rd, .sh }, 28416 dst_alias, 28417 tmp_reg, 28418 .u(field_bit_off), 28419 ); 28420 } 28421 28422 if (field_extra_bits > 0) try self.truncateRegister(field_ty, dst_reg); 28423 28424 const dst_mcv = MCValue{ .register = dst_reg }; 28425 break :result if (field_is_gp) 28426 dst_mcv 28427 else 28428 try self.copyToRegisterWithInstTracking(inst, field_ty, dst_mcv); 28429 }, 28430 else => return self.fail("TODO implement airStructFieldVal for {}", .{src_mcv}), 28431 } 28432 }; 28433 return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); 28434 } 28435 28436 fn airFieldParentPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 28437 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 28438 const extra = self.air.extraData(Air.FieldParentPtr, ty_pl.payload).data; 28439 28440 const ptr_agg_ty = self.typeOfIndex(inst); 28441 const src_mcv = try self.resolveInst(extra.field_ptr); 28442 const dst_mcv = if (src_mcv.isRegisterOffset() and 28443 self.reuseOperand(inst, extra.field_ptr, 0, src_mcv)) 28444 src_mcv 28445 else 28446 try self.copyToRegisterWithInstTracking(inst, ptr_agg_ty, src_mcv); 28447 const result = dst_mcv.offset(-self.fieldOffset(ptr_agg_ty, self.typeOf(extra.field_ptr), extra.field_index)); 28448 return self.finishAir(inst, result, .{ extra.field_ptr, .none, .none }); 28449 } 28450 28451 fn genUnOp(self: *CodeGen, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: Air.Inst.Ref) !MCValue { 28452 const pt = self.pt; 28453 const zcu = pt.zcu; 28454 const src_ty = self.typeOf(src_air); 28455 if (src_ty.zigTypeTag(zcu) == .vector) 28456 return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(pt)}); 28457 28458 var src_mcv = try self.resolveInst(src_air); 28459 switch (src_mcv) { 28460 .eflags => |cc| switch (tag) { 28461 .not => { 28462 if (maybe_inst) |inst| if (self.reuseOperand(inst, src_air, 0, src_mcv)) 28463 return .{ .eflags = cc.negate() }; 28464 try self.spillEflagsIfOccupied(); 28465 src_mcv = try self.resolveInst(src_air); 28466 }, 28467 else => {}, 28468 }, 28469 else => {}, 28470 } 28471 28472 const src_lock = switch (src_mcv) { 28473 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 28474 else => null, 28475 }; 28476 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 28477 28478 const dst_mcv: MCValue = dst: { 28479 if (maybe_inst) |inst| if (self.reuseOperand(inst, src_air, 0, src_mcv)) break :dst src_mcv; 28480 28481 const dst_mcv = try self.allocRegOrMemAdvanced(src_ty, maybe_inst, true); 28482 try self.genCopy(src_ty, dst_mcv, src_mcv, .{}); 28483 break :dst dst_mcv; 28484 }; 28485 const dst_lock = switch (dst_mcv) { 28486 .register => |reg| self.register_manager.lockReg(reg), 28487 else => null, 28488 }; 28489 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 28490 28491 const abi_size: u16 = @intCast(src_ty.abiSize(zcu)); 28492 switch (tag) { 28493 .not => { 28494 const limb_abi_size: u16 = @min(abi_size, 8); 28495 const int_info: InternPool.Key.IntType = if (src_ty.ip_index == .bool_type) 28496 .{ .signedness = .unsigned, .bits = 1 } 28497 else 28498 src_ty.intInfo(zcu); 28499 var byte_off: i32 = 0; 28500 while (byte_off * 8 < int_info.bits) : (byte_off += limb_abi_size) { 28501 const limb_bits: u16 = @intCast(@min(switch (int_info.signedness) { 28502 .signed => abi_size * 8, 28503 .unsigned => int_info.bits, 28504 } - byte_off * 8, limb_abi_size * 8)); 28505 const limb_ty = try pt.intType(int_info.signedness, limb_bits); 28506 const limb_mcv = switch (byte_off) { 28507 0 => dst_mcv, 28508 else => dst_mcv.address().offset(byte_off).deref(), 28509 }; 28510 28511 if (int_info.signedness == .unsigned and self.regExtraBits(limb_ty) > 0) { 28512 const mask = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - limb_bits); 28513 try self.genBinOpMir(.{ ._, .xor }, limb_ty, limb_mcv, .{ .immediate = mask }); 28514 } else try self.genUnOpMir(.{ ._, .not }, limb_ty, limb_mcv); 28515 } 28516 }, 28517 .neg => { 28518 try self.genUnOpMir(.{ ._, .neg }, src_ty, dst_mcv); 28519 const bit_size = src_ty.intInfo(zcu).bits; 28520 if (abi_size * 8 > bit_size) { 28521 if (dst_mcv.isRegister()) { 28522 try self.truncateRegister(src_ty, dst_mcv.getReg().?); 28523 } else { 28524 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28525 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28526 defer self.register_manager.unlockReg(tmp_lock); 28527 28528 const hi_mcv = dst_mcv.address().offset(@intCast(bit_size / 64 * 8)).deref(); 28529 try self.genSetReg(tmp_reg, .usize, hi_mcv, .{}); 28530 try self.truncateRegister(src_ty, tmp_reg); 28531 try self.genCopy(.usize, hi_mcv, .{ .register = tmp_reg }, .{}); 28532 } 28533 } 28534 }, 28535 else => unreachable, 28536 } 28537 return dst_mcv; 28538 } 28539 28540 fn genUnOpMir(self: *CodeGen, mir_tag: Mir.Inst.FixedTag, dst_ty: Type, dst_mcv: MCValue) !void { 28541 const pt = self.pt; 28542 const abi_size: u32 = @intCast(dst_ty.abiSize(pt.zcu)); 28543 if (abi_size > 8) return self.fail("TODO implement {} for {}", .{ mir_tag, dst_ty.fmt(pt) }); 28544 switch (dst_mcv) { 28545 .none, 28546 .unreach, 28547 .dead, 28548 .undef, 28549 .immediate, 28550 .register_offset, 28551 .eflags, 28552 .register_overflow, 28553 .register_mask, 28554 .lea_symbol, 28555 .lea_direct, 28556 .lea_got, 28557 .lea_tlv, 28558 .lea_frame, 28559 .elementwise_regs_then_frame, 28560 .reserved_frame, 28561 .air_ref, 28562 => unreachable, // unmodifiable destination 28563 .register => |dst_reg| try self.asmRegister(mir_tag, registerAlias(dst_reg, abi_size)), 28564 .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented 28565 .memory, .load_symbol, .load_got, .load_direct, .load_tlv => { 28566 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28567 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 28568 defer self.register_manager.unlockReg(addr_reg_lock); 28569 28570 try self.genSetReg(addr_reg, .usize, dst_mcv.address(), .{}); 28571 try self.asmMemory(mir_tag, .{ .base = .{ .reg = addr_reg }, .mod = .{ .rm = .{ 28572 .size = .fromSize(abi_size), 28573 } } }); 28574 }, 28575 .indirect, .load_frame => try self.asmMemory( 28576 mir_tag, 28577 try dst_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 28578 ), 28579 } 28580 } 28581 28582 /// Clobbers .rcx for non-immediate shift value. 28583 fn genShiftBinOpMir( 28584 self: *CodeGen, 28585 tag: Mir.Inst.FixedTag, 28586 lhs_ty: Type, 28587 lhs_mcv: MCValue, 28588 rhs_ty: Type, 28589 rhs_mcv: MCValue, 28590 ) !void { 28591 const pt = self.pt; 28592 const zcu = pt.zcu; 28593 const abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); 28594 const shift_abi_size: u32 = @intCast(rhs_ty.abiSize(zcu)); 28595 try self.spillEflagsIfOccupied(); 28596 28597 if (abi_size > 16) { 28598 const limbs_len = std.math.divCeil(u32, abi_size, 8) catch unreachable; 28599 assert(shift_abi_size >= 1 and shift_abi_size <= 2); 28600 28601 const rcx_lock: ?RegisterLock = switch (rhs_mcv) { 28602 .immediate => |shift_imm| switch (shift_imm) { 28603 0 => return, 28604 else => null, 28605 }, 28606 else => lock: { 28607 if (switch (rhs_mcv) { 28608 .register => |rhs_reg| rhs_reg.id() != Register.rcx.id(), 28609 else => true, 28610 }) { 28611 self.register_manager.getRegAssumeFree(.rcx, null); 28612 try self.genSetReg(.rcx, rhs_ty, rhs_mcv, .{}); 28613 } 28614 break :lock self.register_manager.lockReg(.rcx); 28615 }, 28616 }; 28617 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock); 28618 28619 const temp_regs = try self.register_manager.allocRegs(4, @splat(null), abi.RegisterClass.gp); 28620 const temp_locks = self.register_manager.lockRegsAssumeUnused(4, temp_regs); 28621 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 28622 28623 switch (tag[0]) { 28624 ._l => { 28625 try self.asmRegisterImmediate(.{ ._, .mov }, temp_regs[1].to32(), .u(limbs_len - 1)); 28626 switch (rhs_mcv) { 28627 .immediate => |shift_imm| try self.asmRegisterImmediate( 28628 .{ ._, .mov }, 28629 temp_regs[0].to32(), 28630 .u(limbs_len - (shift_imm >> 6) - 1), 28631 ), 28632 else => { 28633 try self.asmRegisterRegister( 28634 .{ ._, .movzx }, 28635 temp_regs[2].to32(), 28636 registerAlias(.rcx, shift_abi_size), 28637 ); 28638 try self.asmRegisterImmediate(.{ ._, .@"and" }, .cl, .u(std.math.maxInt(u6))); 28639 try self.asmRegisterImmediate(.{ ._r, .sh }, temp_regs[2].to32(), .u(6)); 28640 try self.asmRegisterRegister( 28641 .{ ._, .mov }, 28642 temp_regs[0].to32(), 28643 temp_regs[1].to32(), 28644 ); 28645 try self.asmRegisterRegister( 28646 .{ ._, .sub }, 28647 temp_regs[0].to32(), 28648 temp_regs[2].to32(), 28649 ); 28650 }, 28651 } 28652 }, 28653 ._r => { 28654 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[1].to32(), temp_regs[1].to32()); 28655 switch (rhs_mcv) { 28656 .immediate => |shift_imm| try self.asmRegisterImmediate( 28657 .{ ._, .mov }, 28658 temp_regs[0].to32(), 28659 .u(shift_imm >> 6), 28660 ), 28661 else => { 28662 try self.asmRegisterRegister( 28663 .{ ._, .movzx }, 28664 temp_regs[0].to32(), 28665 registerAlias(.rcx, shift_abi_size), 28666 ); 28667 try self.asmRegisterImmediate(.{ ._, .@"and" }, .cl, .u(std.math.maxInt(u6))); 28668 try self.asmRegisterImmediate(.{ ._r, .sh }, temp_regs[0].to32(), .u(6)); 28669 }, 28670 } 28671 }, 28672 else => unreachable, 28673 } 28674 28675 const slow_inc_dec = self.hasFeature(.slow_incdec); 28676 if (switch (rhs_mcv) { 28677 .immediate => |shift_imm| shift_imm >> 6 < limbs_len - 1, 28678 else => true, 28679 }) { 28680 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[2].to64(), .{ 28681 .base = .{ .frame = lhs_mcv.load_frame.index }, 28682 .mod = .{ .rm = .{ 28683 .size = .qword, 28684 .index = temp_regs[0].to64(), 28685 .scale = .@"8", 28686 .disp = lhs_mcv.load_frame.off, 28687 } }, 28688 }); 28689 const skip = switch (rhs_mcv) { 28690 .immediate => undefined, 28691 else => switch (tag[0]) { 28692 ._l => try self.asmJccReloc(.z, undefined), 28693 ._r => skip: { 28694 try self.asmRegisterImmediate( 28695 .{ ._, .cmp }, 28696 temp_regs[0].to32(), 28697 .u(limbs_len - 1), 28698 ); 28699 break :skip try self.asmJccReloc(.nb, undefined); 28700 }, 28701 else => unreachable, 28702 }, 28703 }; 28704 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 28705 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[3].to64(), .{ 28706 .base = .{ .frame = lhs_mcv.load_frame.index }, 28707 .mod = .{ .rm = .{ 28708 .size = .qword, 28709 .index = temp_regs[0].to64(), 28710 .scale = .@"8", 28711 .disp = switch (tag[0]) { 28712 ._l => lhs_mcv.load_frame.off - 8, 28713 ._r => lhs_mcv.load_frame.off + 8, 28714 else => unreachable, 28715 }, 28716 } }, 28717 }); 28718 switch (rhs_mcv) { 28719 .immediate => |shift_imm| try self.asmRegisterRegisterImmediate( 28720 .{ switch (tag[0]) { 28721 ._l => ._ld, 28722 ._r => ._rd, 28723 else => unreachable, 28724 }, .sh }, 28725 temp_regs[2].to64(), 28726 temp_regs[3].to64(), 28727 .u(shift_imm & std.math.maxInt(u6)), 28728 ), 28729 else => try self.asmRegisterRegisterRegister(.{ switch (tag[0]) { 28730 ._l => ._ld, 28731 ._r => ._rd, 28732 else => unreachable, 28733 }, .sh }, temp_regs[2].to64(), temp_regs[3].to64(), .cl), 28734 } 28735 try self.asmMemoryRegister(.{ ._, .mov }, .{ 28736 .base = .{ .frame = lhs_mcv.load_frame.index }, 28737 .mod = .{ .rm = .{ 28738 .size = .qword, 28739 .index = temp_regs[1].to64(), 28740 .scale = .@"8", 28741 .disp = lhs_mcv.load_frame.off, 28742 } }, 28743 }, temp_regs[2].to64()); 28744 try self.asmRegisterRegister(.{ ._, .mov }, temp_regs[2].to64(), temp_regs[3].to64()); 28745 switch (tag[0]) { 28746 ._l => { 28747 if (slow_inc_dec) { 28748 try self.asmRegisterImmediate(.{ ._, .sub }, temp_regs[1].to32(), .u(1)); 28749 try self.asmRegisterImmediate(.{ ._, .sub }, temp_regs[0].to32(), .u(1)); 28750 } else { 28751 try self.asmRegister(.{ ._c, .de }, temp_regs[1].to32()); 28752 try self.asmRegister(.{ ._c, .de }, temp_regs[0].to32()); 28753 } 28754 _ = try self.asmJccReloc(.nz, loop); 28755 }, 28756 ._r => { 28757 if (slow_inc_dec) { 28758 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[1].to32(), .u(1)); 28759 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), .u(1)); 28760 } else { 28761 try self.asmRegister(.{ ._c, .in }, temp_regs[1].to32()); 28762 try self.asmRegister(.{ ._c, .in }, temp_regs[0].to32()); 28763 } 28764 try self.asmRegisterImmediate( 28765 .{ ._, .cmp }, 28766 temp_regs[0].to32(), 28767 .u(limbs_len - 1), 28768 ); 28769 _ = try self.asmJccReloc(.b, loop); 28770 }, 28771 else => unreachable, 28772 } 28773 switch (rhs_mcv) { 28774 .immediate => {}, 28775 else => self.performReloc(skip), 28776 } 28777 } 28778 switch (rhs_mcv) { 28779 .immediate => |shift_imm| try self.asmRegisterImmediate( 28780 tag, 28781 temp_regs[2].to64(), 28782 .u(shift_imm & std.math.maxInt(u6)), 28783 ), 28784 else => try self.asmRegisterRegister(tag, temp_regs[2].to64(), .cl), 28785 } 28786 try self.asmMemoryRegister(.{ ._, .mov }, .{ 28787 .base = .{ .frame = lhs_mcv.load_frame.index }, 28788 .mod = .{ .rm = .{ 28789 .size = .qword, 28790 .index = temp_regs[1].to64(), 28791 .scale = .@"8", 28792 .disp = lhs_mcv.load_frame.off, 28793 } }, 28794 }, temp_regs[2].to64()); 28795 if (tag[0] == ._r and tag[1] == .sa) try self.asmRegisterImmediate( 28796 tag, 28797 temp_regs[2].to64(), 28798 .u(63), 28799 ); 28800 if (switch (rhs_mcv) { 28801 .immediate => |shift_imm| shift_imm >> 6 > 0, 28802 else => true, 28803 }) { 28804 const skip = switch (rhs_mcv) { 28805 .immediate => undefined, 28806 else => switch (tag[0]) { 28807 ._l => skip: { 28808 try self.asmRegisterRegister( 28809 .{ ._, .@"test" }, 28810 temp_regs[1].to32(), 28811 temp_regs[1].to32(), 28812 ); 28813 break :skip try self.asmJccReloc(.z, undefined); 28814 }, 28815 ._r => skip: { 28816 try self.asmRegisterImmediate( 28817 .{ ._, .cmp }, 28818 temp_regs[1].to32(), 28819 .u(limbs_len - 1), 28820 ); 28821 break :skip try self.asmJccReloc(.nb, undefined); 28822 }, 28823 else => unreachable, 28824 }, 28825 }; 28826 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 28827 switch (tag[0]) { 28828 ._l => if (slow_inc_dec) { 28829 try self.asmRegisterImmediate(.{ ._, .sub }, temp_regs[1].to32(), .u(1)); 28830 } else { 28831 try self.asmRegister(.{ ._c, .de }, temp_regs[1].to32()); 28832 }, 28833 ._r => if (slow_inc_dec) { 28834 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[1].to32(), .u(1)); 28835 } else { 28836 try self.asmRegister(.{ ._c, .in }, temp_regs[1].to32()); 28837 }, 28838 else => unreachable, 28839 } 28840 if (tag[0] == ._r and tag[1] == .sa) try self.asmMemoryRegister(.{ ._, .mov }, .{ 28841 .base = .{ .frame = lhs_mcv.load_frame.index }, 28842 .mod = .{ .rm = .{ 28843 .size = .qword, 28844 .index = temp_regs[1].to64(), 28845 .scale = .@"8", 28846 .disp = lhs_mcv.load_frame.off, 28847 } }, 28848 }, temp_regs[2].to64()) else try self.asmMemoryImmediate(.{ ._, .mov }, .{ 28849 .base = .{ .frame = lhs_mcv.load_frame.index }, 28850 .mod = .{ .rm = .{ 28851 .size = .qword, 28852 .index = temp_regs[1].to64(), 28853 .scale = .@"8", 28854 .disp = lhs_mcv.load_frame.off, 28855 } }, 28856 }, .u(0)); 28857 switch (tag[0]) { 28858 ._l => _ = try self.asmJccReloc(.nz, loop), 28859 ._r => { 28860 try self.asmRegisterImmediate( 28861 .{ ._, .cmp }, 28862 temp_regs[1].to32(), 28863 .u(limbs_len - 1), 28864 ); 28865 _ = try self.asmJccReloc(.b, loop); 28866 }, 28867 else => unreachable, 28868 } 28869 switch (rhs_mcv) { 28870 .immediate => {}, 28871 else => self.performReloc(skip), 28872 } 28873 } 28874 return; 28875 } 28876 28877 assert(shift_abi_size == 1); 28878 const shift_mcv: MCValue = shift: { 28879 switch (rhs_mcv) { 28880 .immediate => |shift_imm| switch (shift_imm) { 28881 0 => return, 28882 else => break :shift rhs_mcv, 28883 }, 28884 .register => |rhs_reg| if (rhs_reg.id() == Register.rcx.id()) 28885 break :shift rhs_mcv, 28886 else => {}, 28887 } 28888 self.register_manager.getRegAssumeFree(.rcx, null); 28889 try self.genSetReg(.cl, rhs_ty, rhs_mcv, .{}); 28890 break :shift .{ .register = .rcx }; 28891 }; 28892 if (abi_size > 8) { 28893 const info: struct { indices: [2]u31, double_tag: Mir.Inst.FixedTag } = switch (tag[0]) { 28894 ._l => .{ .indices = .{ 0, 1 }, .double_tag = .{ ._ld, .sh } }, 28895 ._r => .{ .indices = .{ 1, 0 }, .double_tag = .{ ._rd, .sh } }, 28896 else => unreachable, 28897 }; 28898 switch (lhs_mcv) { 28899 .register_pair => |lhs_regs| switch (shift_mcv) { 28900 .immediate => |shift_imm| if (shift_imm > 0 and shift_imm < 64) { 28901 try self.asmRegisterRegisterImmediate( 28902 info.double_tag, 28903 lhs_regs[info.indices[1]], 28904 lhs_regs[info.indices[0]], 28905 .u(shift_imm), 28906 ); 28907 try self.asmRegisterImmediate( 28908 tag, 28909 lhs_regs[info.indices[0]], 28910 .u(shift_imm), 28911 ); 28912 return; 28913 } else { 28914 assert(shift_imm < 128); 28915 try self.asmRegisterRegister( 28916 .{ ._, .mov }, 28917 lhs_regs[info.indices[1]], 28918 lhs_regs[info.indices[0]], 28919 ); 28920 if (tag[0] == ._r and tag[1] == .sa) try self.asmRegisterImmediate( 28921 tag, 28922 lhs_regs[info.indices[0]], 28923 .u(63), 28924 ) else try self.asmRegisterRegister( 28925 .{ ._, .xor }, 28926 lhs_regs[info.indices[0]], 28927 lhs_regs[info.indices[0]], 28928 ); 28929 if (shift_imm > 64) try self.asmRegisterImmediate( 28930 tag, 28931 lhs_regs[info.indices[1]], 28932 .u(shift_imm - 64), 28933 ); 28934 return; 28935 }, 28936 .register => |shift_reg| { 28937 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28938 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28939 defer self.register_manager.unlockReg(tmp_lock); 28940 28941 if (tag[0] == ._r and tag[1] == .sa) { 28942 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, lhs_regs[info.indices[0]]); 28943 try self.asmRegisterImmediate(tag, tmp_reg, .u(63)); 28944 } else try self.asmRegisterRegister( 28945 .{ ._, .xor }, 28946 tmp_reg.to32(), 28947 tmp_reg.to32(), 28948 ); 28949 try self.asmRegisterRegisterRegister( 28950 info.double_tag, 28951 lhs_regs[info.indices[1]], 28952 lhs_regs[info.indices[0]], 28953 registerAlias(shift_reg, 1), 28954 ); 28955 try self.asmRegisterRegister( 28956 tag, 28957 lhs_regs[info.indices[0]], 28958 registerAlias(shift_reg, 1), 28959 ); 28960 try self.asmRegisterImmediate(.{ ._, .cmp }, registerAlias(shift_reg, 1), .u(64)); 28961 try self.asmCmovccRegisterRegister( 28962 .ae, 28963 lhs_regs[info.indices[1]], 28964 lhs_regs[info.indices[0]], 28965 ); 28966 try self.asmCmovccRegisterRegister(.ae, lhs_regs[info.indices[0]], tmp_reg); 28967 return; 28968 }, 28969 else => {}, 28970 }, 28971 .load_frame => |dst_frame_addr| { 28972 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 28973 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 28974 defer self.register_manager.unlockReg(tmp_lock); 28975 28976 switch (shift_mcv) { 28977 .immediate => |shift_imm| if (shift_imm > 0 and shift_imm < 64) { 28978 try self.asmRegisterMemory( 28979 .{ ._, .mov }, 28980 tmp_reg, 28981 .{ 28982 .base = .{ .frame = dst_frame_addr.index }, 28983 .mod = .{ .rm = .{ 28984 .size = .qword, 28985 .disp = dst_frame_addr.off + info.indices[0] * 8, 28986 } }, 28987 }, 28988 ); 28989 try self.asmMemoryRegisterImmediate( 28990 info.double_tag, 28991 .{ 28992 .base = .{ .frame = dst_frame_addr.index }, 28993 .mod = .{ .rm = .{ 28994 .size = .qword, 28995 .disp = dst_frame_addr.off + info.indices[1] * 8, 28996 } }, 28997 }, 28998 tmp_reg, 28999 .u(shift_imm), 29000 ); 29001 try self.asmMemoryImmediate( 29002 tag, 29003 .{ 29004 .base = .{ .frame = dst_frame_addr.index }, 29005 .mod = .{ .rm = .{ 29006 .size = .qword, 29007 .disp = dst_frame_addr.off + info.indices[0] * 8, 29008 } }, 29009 }, 29010 .u(shift_imm), 29011 ); 29012 return; 29013 } else { 29014 assert(shift_imm < 128); 29015 try self.asmRegisterMemory( 29016 .{ ._, .mov }, 29017 tmp_reg, 29018 .{ 29019 .base = .{ .frame = dst_frame_addr.index }, 29020 .mod = .{ .rm = .{ 29021 .size = .qword, 29022 .disp = dst_frame_addr.off + info.indices[0] * 8, 29023 } }, 29024 }, 29025 ); 29026 if (shift_imm > 64) try self.asmRegisterImmediate( 29027 tag, 29028 tmp_reg, 29029 .u(shift_imm - 64), 29030 ); 29031 try self.asmMemoryRegister( 29032 .{ ._, .mov }, 29033 .{ 29034 .base = .{ .frame = dst_frame_addr.index }, 29035 .mod = .{ .rm = .{ 29036 .size = .qword, 29037 .disp = dst_frame_addr.off + info.indices[1] * 8, 29038 } }, 29039 }, 29040 tmp_reg, 29041 ); 29042 if (tag[0] == ._r and tag[1] == .sa) try self.asmMemoryImmediate( 29043 tag, 29044 .{ 29045 .base = .{ .frame = dst_frame_addr.index }, 29046 .mod = .{ .rm = .{ 29047 .size = .qword, 29048 .disp = dst_frame_addr.off + info.indices[0] * 8, 29049 } }, 29050 }, 29051 .u(63), 29052 ) else { 29053 try self.asmRegisterRegister(.{ ._, .xor }, tmp_reg.to32(), tmp_reg.to32()); 29054 try self.asmMemoryRegister( 29055 .{ ._, .mov }, 29056 .{ 29057 .base = .{ .frame = dst_frame_addr.index }, 29058 .mod = .{ .rm = .{ 29059 .size = .qword, 29060 .disp = dst_frame_addr.off + info.indices[0] * 8, 29061 } }, 29062 }, 29063 tmp_reg, 29064 ); 29065 } 29066 return; 29067 }, 29068 .register => |shift_reg| { 29069 const first_reg = 29070 try self.register_manager.allocReg(null, abi.RegisterClass.gp); 29071 const first_lock = self.register_manager.lockRegAssumeUnused(first_reg); 29072 defer self.register_manager.unlockReg(first_lock); 29073 29074 const second_reg = 29075 try self.register_manager.allocReg(null, abi.RegisterClass.gp); 29076 const second_lock = self.register_manager.lockRegAssumeUnused(second_reg); 29077 defer self.register_manager.unlockReg(second_lock); 29078 29079 try self.asmRegisterMemory( 29080 .{ ._, .mov }, 29081 first_reg, 29082 .{ 29083 .base = .{ .frame = dst_frame_addr.index }, 29084 .mod = .{ .rm = .{ 29085 .size = .qword, 29086 .disp = dst_frame_addr.off + info.indices[0] * 8, 29087 } }, 29088 }, 29089 ); 29090 try self.asmRegisterMemory( 29091 .{ ._, .mov }, 29092 second_reg, 29093 .{ 29094 .base = .{ .frame = dst_frame_addr.index }, 29095 .mod = .{ .rm = .{ 29096 .size = .qword, 29097 .disp = dst_frame_addr.off + info.indices[1] * 8, 29098 } }, 29099 }, 29100 ); 29101 if (tag[0] == ._r and tag[1] == .sa) { 29102 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, first_reg); 29103 try self.asmRegisterImmediate(tag, tmp_reg, .u(63)); 29104 } else try self.asmRegisterRegister( 29105 .{ ._, .xor }, 29106 tmp_reg.to32(), 29107 tmp_reg.to32(), 29108 ); 29109 try self.asmRegisterRegisterRegister( 29110 info.double_tag, 29111 second_reg, 29112 first_reg, 29113 registerAlias(shift_reg, 1), 29114 ); 29115 try self.asmRegisterRegister(tag, first_reg, registerAlias(shift_reg, 1)); 29116 try self.asmRegisterImmediate( 29117 .{ ._, .cmp }, 29118 registerAlias(shift_reg, 1), 29119 .u(64), 29120 ); 29121 try self.asmCmovccRegisterRegister(.ae, second_reg, first_reg); 29122 try self.asmCmovccRegisterRegister(.ae, first_reg, tmp_reg); 29123 try self.asmMemoryRegister( 29124 .{ ._, .mov }, 29125 .{ 29126 .base = .{ .frame = dst_frame_addr.index }, 29127 .mod = .{ .rm = .{ 29128 .size = .qword, 29129 .disp = dst_frame_addr.off + info.indices[1] * 8, 29130 } }, 29131 }, 29132 second_reg, 29133 ); 29134 try self.asmMemoryRegister( 29135 .{ ._, .mov }, 29136 .{ 29137 .base = .{ .frame = dst_frame_addr.index }, 29138 .mod = .{ .rm = .{ 29139 .size = .qword, 29140 .disp = dst_frame_addr.off + info.indices[0] * 8, 29141 } }, 29142 }, 29143 first_reg, 29144 ); 29145 return; 29146 }, 29147 else => {}, 29148 } 29149 }, 29150 else => {}, 29151 } 29152 } else switch (lhs_mcv) { 29153 .register => |lhs_reg| switch (shift_mcv) { 29154 .immediate => |shift_imm| return self.asmRegisterImmediate( 29155 tag, 29156 registerAlias(lhs_reg, abi_size), 29157 .u(shift_imm), 29158 ), 29159 .register => |shift_reg| return self.asmRegisterRegister( 29160 tag, 29161 registerAlias(lhs_reg, abi_size), 29162 registerAlias(shift_reg, 1), 29163 ), 29164 else => {}, 29165 }, 29166 .memory, .indirect, .load_frame => { 29167 const lhs_mem: Memory = switch (lhs_mcv) { 29168 .memory => |addr| .{ 29169 .base = .{ .reg = .ds }, 29170 .mod = .{ .rm = .{ 29171 .size = .fromSize(abi_size), 29172 .disp = std.math.cast(i32, @as(i64, @bitCast(addr))) orelse 29173 return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ 29174 @tagName(lhs_mcv), 29175 @tagName(shift_mcv), 29176 }), 29177 } }, 29178 }, 29179 .indirect => |reg_off| .{ 29180 .base = .{ .reg = reg_off.reg }, 29181 .mod = .{ .rm = .{ 29182 .size = .fromSize(abi_size), 29183 .disp = reg_off.off, 29184 } }, 29185 }, 29186 .load_frame => |frame_addr| .{ 29187 .base = .{ .frame = frame_addr.index }, 29188 .mod = .{ .rm = .{ 29189 .size = .fromSize(abi_size), 29190 .disp = frame_addr.off, 29191 } }, 29192 }, 29193 else => unreachable, 29194 }; 29195 switch (shift_mcv) { 29196 .immediate => |shift_imm| return self.asmMemoryImmediate(tag, lhs_mem, .u(shift_imm)), 29197 .register => |shift_reg| return self.asmMemoryRegister( 29198 tag, 29199 lhs_mem, 29200 registerAlias(shift_reg, 1), 29201 ), 29202 else => {}, 29203 } 29204 }, 29205 else => {}, 29206 } 29207 return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ 29208 @tagName(lhs_mcv), 29209 @tagName(shift_mcv), 29210 }); 29211 } 29212 29213 /// Result is always a register. 29214 /// Clobbers .rcx for non-immediate rhs, therefore care is needed to spill .rcx upfront. 29215 /// Asserts .rcx is free. 29216 fn genShiftBinOp( 29217 self: *CodeGen, 29218 air_tag: Air.Inst.Tag, 29219 maybe_inst: ?Air.Inst.Index, 29220 lhs_mcv: MCValue, 29221 rhs_mcv: MCValue, 29222 lhs_ty: Type, 29223 rhs_ty: Type, 29224 ) !MCValue { 29225 const pt = self.pt; 29226 const zcu = pt.zcu; 29227 if (lhs_ty.zigTypeTag(zcu) == .vector) return self.fail("TODO implement genShiftBinOp for {}", .{ 29228 lhs_ty.fmt(pt), 29229 }); 29230 29231 try self.register_manager.getKnownReg(.rcx, null); 29232 const rcx_lock = self.register_manager.lockReg(.rcx); 29233 defer if (rcx_lock) |lock| self.register_manager.unlockReg(lock); 29234 29235 const mat_lhs_mcv: MCValue, const can_reuse_lhs = switch (lhs_mcv) { 29236 .register => |lhs_reg| switch (lhs_reg.class()) { 29237 .general_purpose => .{ lhs_mcv, true }, 29238 else => lhs: { 29239 const mat_lhs_mcv = try self.allocTempRegOrMem(lhs_ty, true); 29240 try self.genCopy(lhs_ty, mat_lhs_mcv, lhs_mcv, .{}); 29241 break :lhs .{ mat_lhs_mcv, false }; 29242 }, 29243 }, 29244 else => .{ lhs_mcv, true }, 29245 }; 29246 const lhs_lock = switch (mat_lhs_mcv) { 29247 .register => |reg| self.register_manager.lockReg(reg), 29248 else => null, 29249 }; 29250 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 29251 29252 const rhs_lock = switch (rhs_mcv) { 29253 .register => |reg| self.register_manager.lockReg(reg), 29254 else => null, 29255 }; 29256 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 29257 29258 const dst_mcv: MCValue = dst: { 29259 if (can_reuse_lhs) if (maybe_inst) |inst| { 29260 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 29261 if (self.reuseOperand(inst, bin_op.lhs, 0, mat_lhs_mcv)) break :dst mat_lhs_mcv; 29262 }; 29263 const dst_mcv = try self.allocRegOrMemAdvanced(lhs_ty, maybe_inst, true); 29264 try self.genCopy(lhs_ty, dst_mcv, mat_lhs_mcv, .{}); 29265 break :dst dst_mcv; 29266 }; 29267 29268 const signedness = lhs_ty.intInfo(zcu).signedness; 29269 try self.genShiftBinOpMir(switch (air_tag) { 29270 .shl, .shl_exact => switch (signedness) { 29271 .signed => .{ ._l, .sa }, 29272 .unsigned => .{ ._l, .sh }, 29273 }, 29274 .shr, .shr_exact => switch (signedness) { 29275 .signed => .{ ._r, .sa }, 29276 .unsigned => .{ ._r, .sh }, 29277 }, 29278 else => unreachable, 29279 }, lhs_ty, dst_mcv, rhs_ty, rhs_mcv); 29280 return dst_mcv; 29281 } 29282 29283 /// Result is always a register. 29284 /// Clobbers .rax and .rdx therefore care is needed to spill .rax and .rdx upfront. 29285 /// Asserts .rax and .rdx are free. 29286 fn genMulDivBinOp( 29287 self: *CodeGen, 29288 tag: Air.Inst.Tag, 29289 maybe_inst: ?Air.Inst.Index, 29290 dst_ty: Type, 29291 src_ty: Type, 29292 lhs_mcv: MCValue, 29293 rhs_mcv: MCValue, 29294 ) !MCValue { 29295 const pt = self.pt; 29296 const zcu = pt.zcu; 29297 if (dst_ty.zigTypeTag(zcu) == .vector or dst_ty.zigTypeTag(zcu) == .float) return self.fail( 29298 "TODO implement genMulDivBinOp for {s} from {} to {}", 29299 .{ @tagName(tag), src_ty.fmt(pt), dst_ty.fmt(pt) }, 29300 ); 29301 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 29302 const src_abi_size: u32 = @intCast(src_ty.abiSize(zcu)); 29303 29304 assert(self.register_manager.isRegFree(.rax)); 29305 assert(self.register_manager.isRegFree(.rcx)); 29306 assert(self.register_manager.isRegFree(.rdx)); 29307 assert(self.eflags_inst == null); 29308 29309 if (dst_abi_size == 16 and src_abi_size == 16) { 29310 assert(tag == .mul or tag == .mul_wrap); 29311 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); 29312 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); 29313 29314 const mat_lhs_mcv = switch (lhs_mcv) { 29315 .load_symbol => mat_lhs_mcv: { 29316 // TODO clean this up! 29317 const addr_reg = try self.copyToTmpRegister(.usize, lhs_mcv.address()); 29318 break :mat_lhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 29319 }, 29320 else => lhs_mcv, 29321 }; 29322 const mat_lhs_lock = switch (mat_lhs_mcv) { 29323 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 29324 else => null, 29325 }; 29326 defer if (mat_lhs_lock) |lock| self.register_manager.unlockReg(lock); 29327 const mat_rhs_mcv = switch (rhs_mcv) { 29328 .load_symbol => mat_rhs_mcv: { 29329 // TODO clean this up! 29330 const addr_reg = try self.copyToTmpRegister(.usize, rhs_mcv.address()); 29331 break :mat_rhs_mcv MCValue{ .indirect = .{ .reg = addr_reg } }; 29332 }, 29333 else => rhs_mcv, 29334 }; 29335 const mat_rhs_lock = switch (mat_rhs_mcv) { 29336 .indirect => |reg_off| self.register_manager.lockReg(reg_off.reg), 29337 else => null, 29338 }; 29339 defer if (mat_rhs_lock) |lock| self.register_manager.unlockReg(lock); 29340 29341 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 29342 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 29343 defer self.register_manager.unlockReg(tmp_lock); 29344 29345 if (mat_lhs_mcv.isBase()) 29346 try self.asmRegisterMemory(.{ ._, .mov }, .rax, try mat_lhs_mcv.mem(self, .{ .size = .qword })) 29347 else 29348 try self.asmRegisterRegister(.{ ._, .mov }, .rax, mat_lhs_mcv.register_pair[0]); 29349 if (mat_rhs_mcv.isBase()) try self.asmRegisterMemory( 29350 .{ ._, .mov }, 29351 tmp_reg, 29352 try mat_rhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 29353 ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, mat_rhs_mcv.register_pair[1]); 29354 try self.asmRegisterRegister(.{ .i_, .mul }, tmp_reg, .rax); 29355 if (mat_rhs_mcv.isBase()) 29356 try self.asmMemory(.{ ._, .mul }, try mat_rhs_mcv.mem(self, .{ .size = .qword })) 29357 else 29358 try self.asmRegister(.{ ._, .mul }, mat_rhs_mcv.register_pair[0]); 29359 try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_reg); 29360 if (mat_lhs_mcv.isBase()) try self.asmRegisterMemory( 29361 .{ ._, .mov }, 29362 tmp_reg, 29363 try mat_lhs_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 29364 ) else try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, mat_lhs_mcv.register_pair[1]); 29365 if (mat_rhs_mcv.isBase()) 29366 try self.asmRegisterMemory(.{ .i_, .mul }, tmp_reg, try mat_rhs_mcv.mem(self, .{ .size = .qword })) 29367 else 29368 try self.asmRegisterRegister(.{ .i_, .mul }, tmp_reg, mat_rhs_mcv.register_pair[0]); 29369 try self.asmRegisterRegister(.{ ._, .add }, .rdx, tmp_reg); 29370 return .{ .register_pair = .{ .rax, .rdx } }; 29371 } 29372 29373 if (switch (tag) { 29374 else => unreachable, 29375 .mul, .mul_wrap => dst_abi_size != src_abi_size and dst_abi_size != src_abi_size * 2, 29376 .div_trunc, .div_floor, .div_exact, .rem, .mod => dst_abi_size != src_abi_size, 29377 } or src_abi_size > 8) { 29378 const src_info = src_ty.intInfo(zcu); 29379 switch (tag) { 29380 .mul, .mul_wrap => { 29381 const slow_inc = self.hasFeature(.slow_incdec); 29382 const limb_len = std.math.divCeil(u32, src_abi_size, 8) catch unreachable; 29383 29384 try self.spillRegisters(&.{ .rax, .rcx, .rdx }); 29385 const reg_locks = self.register_manager.lockRegs(3, .{ .rax, .rcx, .rdx }); 29386 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| 29387 self.register_manager.unlockReg(lock); 29388 29389 const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false); 29390 try self.genInlineMemset( 29391 dst_mcv.address(), 29392 .{ .immediate = 0 }, 29393 .{ .immediate = src_abi_size }, 29394 .{}, 29395 ); 29396 29397 const temp_regs = 29398 try self.register_manager.allocRegs(4, @splat(null), abi.RegisterClass.gp); 29399 const temp_locks = self.register_manager.lockRegsAssumeUnused(4, temp_regs); 29400 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 29401 29402 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[0].to32(), temp_regs[0].to32()); 29403 29404 const outer_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 29405 try self.asmRegisterMemory(.{ ._, .mov }, temp_regs[1].to64(), .{ 29406 .base = .{ .frame = rhs_mcv.load_frame.index }, 29407 .mod = .{ .rm = .{ 29408 .size = .qword, 29409 .index = temp_regs[0].to64(), 29410 .scale = .@"8", 29411 .disp = rhs_mcv.load_frame.off, 29412 } }, 29413 }); 29414 try self.asmRegisterRegister(.{ ._, .@"test" }, temp_regs[1].to64(), temp_regs[1].to64()); 29415 const skip_inner = try self.asmJccReloc(.z, undefined); 29416 29417 try self.asmRegisterRegister(.{ ._, .xor }, temp_regs[2].to32(), temp_regs[2].to32()); 29418 try self.asmRegisterRegister(.{ ._, .mov }, temp_regs[3].to32(), temp_regs[0].to32()); 29419 try self.asmRegisterRegister(.{ ._, .xor }, .ecx, .ecx); 29420 try self.asmRegisterRegister(.{ ._, .xor }, .edx, .edx); 29421 29422 const inner_loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 29423 try self.asmRegisterImmediate(.{ ._r, .sh }, .cl, .u(1)); 29424 try self.asmMemoryRegister(.{ ._, .adc }, .{ 29425 .base = .{ .frame = dst_mcv.load_frame.index }, 29426 .mod = .{ .rm = .{ 29427 .size = .qword, 29428 .index = temp_regs[3].to64(), 29429 .scale = .@"8", 29430 .disp = dst_mcv.load_frame.off, 29431 } }, 29432 }, .rdx); 29433 try self.asmSetccRegister(.c, .cl); 29434 29435 try self.asmRegisterMemory(.{ ._, .mov }, .rax, .{ 29436 .base = .{ .frame = lhs_mcv.load_frame.index }, 29437 .mod = .{ .rm = .{ 29438 .size = .qword, 29439 .index = temp_regs[2].to64(), 29440 .scale = .@"8", 29441 .disp = lhs_mcv.load_frame.off, 29442 } }, 29443 }); 29444 try self.asmRegister(.{ ._, .mul }, temp_regs[1].to64()); 29445 29446 try self.asmRegisterImmediate(.{ ._r, .sh }, .ch, .u(1)); 29447 try self.asmMemoryRegister(.{ ._, .adc }, .{ 29448 .base = .{ .frame = dst_mcv.load_frame.index }, 29449 .mod = .{ .rm = .{ 29450 .size = .qword, 29451 .index = temp_regs[3].to64(), 29452 .scale = .@"8", 29453 .disp = dst_mcv.load_frame.off, 29454 } }, 29455 }, .rax); 29456 try self.asmSetccRegister(.c, .ch); 29457 29458 if (slow_inc) { 29459 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[2].to32(), .u(1)); 29460 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[3].to32(), .u(1)); 29461 } else { 29462 try self.asmRegister(.{ ._c, .in }, temp_regs[2].to32()); 29463 try self.asmRegister(.{ ._c, .in }, temp_regs[3].to32()); 29464 } 29465 try self.asmRegisterImmediate(.{ ._, .cmp }, temp_regs[3].to32(), .u(limb_len)); 29466 _ = try self.asmJccReloc(.b, inner_loop); 29467 29468 self.performReloc(skip_inner); 29469 if (slow_inc) { 29470 try self.asmRegisterImmediate(.{ ._, .add }, temp_regs[0].to32(), .u(1)); 29471 } else { 29472 try self.asmRegister(.{ ._c, .in }, temp_regs[0].to32()); 29473 } 29474 try self.asmRegisterImmediate(.{ ._, .cmp }, temp_regs[0].to32(), .u(limb_len)); 29475 _ = try self.asmJccReloc(.b, outer_loop); 29476 29477 return dst_mcv; 29478 }, 29479 .div_trunc, .div_floor, .div_exact, .rem, .mod => switch (src_info.signedness) { 29480 .signed => {}, 29481 .unsigned => { 29482 const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false); 29483 const manyptr_u32_ty = try pt.ptrType(.{ 29484 .child = .u32_type, 29485 .flags = .{ 29486 .size = .many, 29487 }, 29488 }); 29489 const manyptr_const_u32_ty = try pt.ptrType(.{ 29490 .child = .u32_type, 29491 .flags = .{ 29492 .size = .many, 29493 .is_const = true, 29494 }, 29495 }); 29496 _ = try self.genCall(.{ .lib = .{ 29497 .return_type = .void_type, 29498 .param_types = &.{ 29499 manyptr_u32_ty.toIntern(), 29500 manyptr_const_u32_ty.toIntern(), 29501 manyptr_const_u32_ty.toIntern(), 29502 .usize_type, 29503 }, 29504 .callee = switch (tag) { 29505 .div_trunc, 29506 .div_floor, 29507 .div_exact, 29508 => "__udivei4", 29509 .rem, 29510 .mod, 29511 => "__umodei4", 29512 else => unreachable, 29513 }, 29514 } }, &.{ 29515 manyptr_u32_ty, 29516 manyptr_const_u32_ty, 29517 manyptr_const_u32_ty, 29518 .usize, 29519 }, &.{ 29520 dst_mcv.address(), 29521 lhs_mcv.address(), 29522 rhs_mcv.address(), 29523 .{ .immediate = 8 * src_abi_size }, 29524 }, .{}); 29525 return dst_mcv; 29526 }, 29527 }, 29528 else => {}, 29529 } 29530 return self.fail( 29531 "TODO implement genMulDivBinOp for {s} from {} to {}", 29532 .{ @tagName(tag), src_ty.fmt(pt), dst_ty.fmt(pt) }, 29533 ); 29534 } 29535 const ty = if (dst_abi_size <= 8) dst_ty else src_ty; 29536 const abi_size = if (dst_abi_size <= 8) dst_abi_size else src_abi_size; 29537 29538 const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); 29539 defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); 29540 29541 const int_info = ty.intInfo(zcu); 29542 const signedness = int_info.signedness; 29543 switch (tag) { 29544 .mul, 29545 .mul_wrap, 29546 .rem, 29547 .div_trunc, 29548 .div_exact, 29549 => { 29550 const track_inst_rax = switch (tag) { 29551 .mul, .mul_wrap => if (dst_abi_size <= 8) maybe_inst else null, 29552 .div_exact, .div_trunc => maybe_inst, 29553 else => null, 29554 }; 29555 const track_inst_rdx = switch (tag) { 29556 .rem => maybe_inst, 29557 else => null, 29558 }; 29559 try self.register_manager.getKnownReg(.rax, track_inst_rax); 29560 try self.register_manager.getKnownReg(.rdx, track_inst_rdx); 29561 29562 try self.genIntMulDivOpMir(switch (signedness) { 29563 .signed => switch (tag) { 29564 .mul, .mul_wrap => .{ .i_, .mul }, 29565 .div_trunc, .div_exact, .rem => .{ .i_, .div }, 29566 else => unreachable, 29567 }, 29568 .unsigned => switch (tag) { 29569 .mul, .mul_wrap => .{ ._, .mul }, 29570 .div_trunc, .div_exact, .rem => .{ ._, .div }, 29571 else => unreachable, 29572 }, 29573 }, ty, lhs_mcv, rhs_mcv); 29574 29575 switch (tag) { 29576 .mul, .rem, .div_trunc, .div_exact => {}, 29577 .mul_wrap => if (dst_ty.intInfo(zcu).bits < 8 * dst_abi_size) try self.truncateRegister( 29578 dst_ty, 29579 if (dst_abi_size <= 8) .rax else .rdx, 29580 ), 29581 else => unreachable, 29582 } 29583 29584 if (dst_abi_size <= 8) return .{ .register = registerAlias(switch (tag) { 29585 .mul, .mul_wrap, .div_trunc, .div_exact => .rax, 29586 .rem => .rdx, 29587 else => unreachable, 29588 }, dst_abi_size) }; 29589 29590 const dst_mcv = try self.allocRegOrMemAdvanced(dst_ty, maybe_inst, false); 29591 try self.asmMemoryRegister(.{ ._, .mov }, .{ 29592 .base = .{ .frame = dst_mcv.load_frame.index }, 29593 .mod = .{ .rm = .{ 29594 .size = .qword, 29595 .disp = dst_mcv.load_frame.off, 29596 } }, 29597 }, .rax); 29598 try self.asmMemoryRegister(.{ ._, .mov }, .{ 29599 .base = .{ .frame = dst_mcv.load_frame.index }, 29600 .mod = .{ .rm = .{ 29601 .size = .qword, 29602 .disp = dst_mcv.load_frame.off + 8, 29603 } }, 29604 }, .rdx); 29605 return dst_mcv; 29606 }, 29607 29608 .mod => { 29609 try self.register_manager.getKnownReg(.rax, null); 29610 try self.register_manager.getKnownReg( 29611 .rdx, 29612 if (signedness == .unsigned) maybe_inst else null, 29613 ); 29614 29615 switch (signedness) { 29616 .signed => { 29617 const lhs_lock = switch (lhs_mcv) { 29618 .register => |reg| self.register_manager.lockReg(reg), 29619 else => null, 29620 }; 29621 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 29622 const rhs_lock = switch (rhs_mcv) { 29623 .register => |reg| self.register_manager.lockReg(reg), 29624 else => null, 29625 }; 29626 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 29627 29628 // hack around hazard between rhs and div_floor by copying rhs to another register 29629 const rhs_copy = try self.copyToTmpRegister(ty, rhs_mcv); 29630 const rhs_copy_lock = self.register_manager.lockRegAssumeUnused(rhs_copy); 29631 defer self.register_manager.unlockReg(rhs_copy_lock); 29632 29633 const div_floor = try self.genInlineIntDivFloor(ty, lhs_mcv, rhs_mcv); 29634 try self.genIntMulComplexOpMir(ty, div_floor, .{ .register = rhs_copy }); 29635 const div_floor_lock = self.register_manager.lockReg(div_floor.register); 29636 defer if (div_floor_lock) |lock| self.register_manager.unlockReg(lock); 29637 29638 const result: MCValue = if (maybe_inst) |inst| 29639 try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv) 29640 else 29641 .{ .register = try self.copyToTmpRegister(ty, lhs_mcv) }; 29642 try self.genBinOpMir(.{ ._, .sub }, ty, result, div_floor); 29643 29644 return result; 29645 }, 29646 .unsigned => { 29647 try self.genIntMulDivOpMir(.{ ._, .div }, ty, lhs_mcv, rhs_mcv); 29648 return .{ .register = registerAlias(.rdx, abi_size) }; 29649 }, 29650 } 29651 }, 29652 29653 .div_floor => { 29654 try self.register_manager.getKnownReg( 29655 .rax, 29656 if (signedness == .unsigned) maybe_inst else null, 29657 ); 29658 try self.register_manager.getKnownReg(.rdx, null); 29659 29660 const lhs_lock: ?RegisterLock = switch (lhs_mcv) { 29661 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 29662 else => null, 29663 }; 29664 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 29665 29666 const actual_rhs_mcv: MCValue = blk: { 29667 switch (signedness) { 29668 .signed => { 29669 const rhs_lock: ?RegisterLock = switch (rhs_mcv) { 29670 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 29671 else => null, 29672 }; 29673 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 29674 29675 if (maybe_inst) |inst| { 29676 break :blk try self.copyToRegisterWithInstTracking(inst, ty, rhs_mcv); 29677 } 29678 break :blk MCValue{ .register = try self.copyToTmpRegister(ty, rhs_mcv) }; 29679 }, 29680 .unsigned => break :blk rhs_mcv, 29681 } 29682 }; 29683 const rhs_lock: ?RegisterLock = switch (actual_rhs_mcv) { 29684 .register => |reg| self.register_manager.lockReg(reg), 29685 else => null, 29686 }; 29687 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 29688 29689 switch (signedness) { 29690 .signed => return try self.genInlineIntDivFloor(ty, lhs_mcv, actual_rhs_mcv), 29691 .unsigned => { 29692 try self.genIntMulDivOpMir(.{ ._, .div }, ty, lhs_mcv, actual_rhs_mcv); 29693 return .{ .register = registerAlias(.rax, abi_size) }; 29694 }, 29695 } 29696 }, 29697 29698 else => unreachable, 29699 } 29700 } 29701 29702 fn genBinOp( 29703 self: *CodeGen, 29704 maybe_inst: ?Air.Inst.Index, 29705 air_tag: Air.Inst.Tag, 29706 lhs_air: Air.Inst.Ref, 29707 rhs_air: Air.Inst.Ref, 29708 ) !MCValue { 29709 const pt = self.pt; 29710 const zcu = pt.zcu; 29711 const lhs_ty = self.typeOf(lhs_air); 29712 const rhs_ty = self.typeOf(rhs_air); 29713 const abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); 29714 29715 if (lhs_ty.isRuntimeFloat()) libcall: { 29716 const float_bits = lhs_ty.floatBits(self.target.*); 29717 const type_needs_libcall = switch (float_bits) { 29718 16 => !self.hasFeature(.f16c), 29719 32, 64 => false, 29720 80, 128 => true, 29721 else => unreachable, 29722 }; 29723 switch (air_tag) { 29724 .rem, .mod => {}, 29725 else => if (!type_needs_libcall) break :libcall, 29726 } 29727 var callee_buf: ["__mod?f3".len]u8 = undefined; 29728 const callee = switch (air_tag) { 29729 .add, 29730 .sub, 29731 .mul, 29732 .div_float, 29733 .div_trunc, 29734 .div_floor, 29735 .div_exact, 29736 => std.fmt.bufPrint(&callee_buf, "__{s}{c}f3", .{ 29737 @tagName(air_tag)[0..3], 29738 floatCompilerRtAbiName(float_bits), 29739 }), 29740 .rem, .mod, .min, .max => std.fmt.bufPrint(&callee_buf, "{s}f{s}{s}", .{ 29741 floatLibcAbiPrefix(lhs_ty), 29742 switch (air_tag) { 29743 .rem, .mod => "mod", 29744 .min => "min", 29745 .max => "max", 29746 else => unreachable, 29747 }, 29748 floatLibcAbiSuffix(lhs_ty), 29749 }), 29750 else => return self.fail("TODO implement genBinOp for {s} {}", .{ 29751 @tagName(air_tag), lhs_ty.fmt(pt), 29752 }), 29753 } catch unreachable; 29754 const result = try self.genCall(.{ .lib = .{ 29755 .return_type = lhs_ty.toIntern(), 29756 .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() }, 29757 .callee = callee, 29758 } }, &.{ lhs_ty, rhs_ty }, &.{ .{ .air_ref = lhs_air }, .{ .air_ref = rhs_air } }, .{}); 29759 return switch (air_tag) { 29760 .mod => result: { 29761 const adjusted: MCValue = if (type_needs_libcall) adjusted: { 29762 var add_callee_buf: ["__add?f3".len]u8 = undefined; 29763 break :adjusted try self.genCall(.{ .lib = .{ 29764 .return_type = lhs_ty.toIntern(), 29765 .param_types = &.{ 29766 lhs_ty.toIntern(), 29767 rhs_ty.toIntern(), 29768 }, 29769 .callee = std.fmt.bufPrint(&add_callee_buf, "__add{c}f3", .{ 29770 floatCompilerRtAbiName(float_bits), 29771 }) catch unreachable, 29772 } }, &.{ lhs_ty, rhs_ty }, &.{ result, .{ .air_ref = rhs_air } }, .{}); 29773 } else switch (float_bits) { 29774 16, 32, 64 => adjusted: { 29775 const dst_reg = switch (result) { 29776 .register => |reg| reg, 29777 else => if (maybe_inst) |inst| 29778 (try self.copyToRegisterWithInstTracking(inst, lhs_ty, result)).register 29779 else 29780 try self.copyToTmpRegister(lhs_ty, result), 29781 }; 29782 const dst_lock = self.register_manager.lockReg(dst_reg); 29783 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 29784 29785 const rhs_mcv = try self.resolveInst(rhs_air); 29786 const src_mcv: MCValue = if (float_bits == 16) src: { 29787 assert(self.hasFeature(.f16c)); 29788 const tmp_reg = (try self.register_manager.allocReg( 29789 null, 29790 abi.RegisterClass.sse, 29791 )).to128(); 29792 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 29793 defer self.register_manager.unlockReg(tmp_lock); 29794 29795 if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 29796 .{ .vp_w, .insr }, 29797 dst_reg, 29798 dst_reg, 29799 try rhs_mcv.mem(self, .{ .size = .word }), 29800 .u(1), 29801 ) else try self.asmRegisterRegisterRegister( 29802 .{ .vp_, .unpcklwd }, 29803 dst_reg, 29804 dst_reg, 29805 (if (rhs_mcv.isRegister()) 29806 rhs_mcv.getReg().? 29807 else 29808 try self.copyToTmpRegister(rhs_ty, rhs_mcv)).to128(), 29809 ); 29810 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); 29811 break :src .{ .register = tmp_reg }; 29812 } else rhs_mcv; 29813 29814 if (self.hasFeature(.avx)) { 29815 const mir_tag: Mir.Inst.FixedTag = switch (float_bits) { 29816 16, 32 => .{ .v_ss, .add }, 29817 64 => .{ .v_sd, .add }, 29818 else => unreachable, 29819 }; 29820 if (src_mcv.isBase()) try self.asmRegisterRegisterMemory( 29821 mir_tag, 29822 dst_reg, 29823 dst_reg, 29824 try src_mcv.mem(self, .{ .size = .fromBitSize(float_bits) }), 29825 ) else try self.asmRegisterRegisterRegister( 29826 mir_tag, 29827 dst_reg, 29828 dst_reg, 29829 (if (src_mcv.isRegister()) 29830 src_mcv.getReg().? 29831 else 29832 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 29833 ); 29834 } else { 29835 const mir_tag: Mir.Inst.FixedTag = switch (float_bits) { 29836 32 => .{ ._ss, .add }, 29837 64 => .{ ._sd, .add }, 29838 else => unreachable, 29839 }; 29840 if (src_mcv.isBase()) try self.asmRegisterMemory( 29841 mir_tag, 29842 dst_reg, 29843 try src_mcv.mem(self, .{ .size = .fromBitSize(float_bits) }), 29844 ) else try self.asmRegisterRegister( 29845 mir_tag, 29846 dst_reg, 29847 (if (src_mcv.isRegister()) 29848 src_mcv.getReg().? 29849 else 29850 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 29851 ); 29852 } 29853 29854 if (float_bits == 16) try self.asmRegisterRegisterImmediate( 29855 .{ .v_, .cvtps2ph }, 29856 dst_reg, 29857 dst_reg, 29858 bits.RoundMode.imm(.{}), 29859 ); 29860 break :adjusted .{ .register = dst_reg }; 29861 }, 29862 80, 128 => return self.fail("TODO implement genBinOp for {s} of {}", .{ 29863 @tagName(air_tag), lhs_ty.fmt(pt), 29864 }), 29865 else => unreachable, 29866 }; 29867 break :result try self.genCall(.{ .lib = .{ 29868 .return_type = lhs_ty.toIntern(), 29869 .param_types = &.{ lhs_ty.toIntern(), rhs_ty.toIntern() }, 29870 .callee = callee, 29871 } }, &.{ lhs_ty, rhs_ty }, &.{ adjusted, .{ .air_ref = rhs_air } }, .{}); 29872 }, 29873 .div_trunc, .div_floor => try self.genRoundLibcall(lhs_ty, result, .{ 29874 .mode = switch (air_tag) { 29875 .div_trunc => .zero, 29876 .div_floor => .down, 29877 else => unreachable, 29878 }, 29879 .precision = .inexact, 29880 }), 29881 else => result, 29882 }; 29883 } 29884 29885 const sse_op = switch (lhs_ty.zigTypeTag(zcu)) { 29886 else => false, 29887 .float => true, 29888 .vector => switch (lhs_ty.childType(zcu).toIntern()) { 29889 .bool_type, .u1_type => false, 29890 else => true, 29891 }, 29892 }; 29893 if (sse_op and ((lhs_ty.scalarType(zcu).isRuntimeFloat() and 29894 lhs_ty.scalarType(zcu).floatBits(self.target.*) == 80) or 29895 lhs_ty.abiSize(zcu) > self.vectorSize(.float))) 29896 return self.fail("TODO implement genBinOp for {s} {}", .{ @tagName(air_tag), lhs_ty.fmt(pt) }); 29897 29898 const maybe_mask_reg = switch (air_tag) { 29899 else => null, 29900 .rem, .mod => unreachable, 29901 .max, .min => if (lhs_ty.scalarType(zcu).isRuntimeFloat()) registerAlias( 29902 if (!self.hasFeature(.avx) and self.hasFeature(.sse4_1)) mask: { 29903 try self.register_manager.getKnownReg(.xmm0, null); 29904 break :mask .xmm0; 29905 } else try self.register_manager.allocReg(null, abi.RegisterClass.sse), 29906 abi_size, 29907 ) else null, 29908 }; 29909 const mask_lock = 29910 if (maybe_mask_reg) |mask_reg| self.register_manager.lockRegAssumeUnused(mask_reg) else null; 29911 defer if (mask_lock) |lock| self.register_manager.unlockReg(lock); 29912 29913 const ordered_air: [2]Air.Inst.Ref = if (lhs_ty.isVector(zcu) and 29914 switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 29915 .bool => false, 29916 .int => switch (air_tag) { 29917 .cmp_lt, .cmp_gte => true, 29918 else => false, 29919 }, 29920 .float => switch (air_tag) { 29921 .cmp_gte, .cmp_gt => true, 29922 else => false, 29923 }, 29924 else => unreachable, 29925 }) .{ rhs_air, lhs_air } else .{ lhs_air, rhs_air }; 29926 29927 if (lhs_ty.isAbiInt(zcu)) for (ordered_air) |op_air| { 29928 switch (try self.resolveInst(op_air)) { 29929 .register => |op_reg| switch (op_reg.class()) { 29930 .sse => try self.register_manager.getReg(op_reg, null), 29931 else => {}, 29932 }, 29933 else => {}, 29934 } 29935 }; 29936 29937 const lhs_mcv = try self.resolveInst(ordered_air[0]); 29938 var rhs_mcv = try self.resolveInst(ordered_air[1]); 29939 switch (lhs_mcv) { 29940 .immediate => |imm| switch (imm) { 29941 0 => switch (air_tag) { 29942 .sub, .sub_wrap => return self.genUnOp(maybe_inst, .neg, ordered_air[1]), 29943 else => {}, 29944 }, 29945 else => {}, 29946 }, 29947 else => {}, 29948 } 29949 29950 const is_commutative = switch (air_tag) { 29951 .add, 29952 .add_wrap, 29953 .mul, 29954 .bool_or, 29955 .bit_or, 29956 .bool_and, 29957 .bit_and, 29958 .xor, 29959 .min, 29960 .max, 29961 .cmp_eq, 29962 .cmp_neq, 29963 => true, 29964 29965 else => false, 29966 }; 29967 29968 const lhs_locks: [2]?RegisterLock = switch (lhs_mcv) { 29969 .register => |lhs_reg| .{ self.register_manager.lockRegAssumeUnused(lhs_reg), null }, 29970 .register_pair => |lhs_regs| locks: { 29971 const locks = self.register_manager.lockRegsAssumeUnused(2, lhs_regs); 29972 break :locks .{ locks[0], locks[1] }; 29973 }, 29974 else => @splat(null), 29975 }; 29976 defer for (lhs_locks) |lhs_lock| if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 29977 29978 const rhs_locks: [2]?RegisterLock = switch (rhs_mcv) { 29979 .register => |rhs_reg| .{ self.register_manager.lockReg(rhs_reg), null }, 29980 .register_pair => |rhs_regs| self.register_manager.lockRegs(2, rhs_regs), 29981 else => @splat(null), 29982 }; 29983 defer for (rhs_locks) |rhs_lock| if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 29984 29985 var flipped = false; 29986 var copied_to_dst = true; 29987 const dst_mcv: MCValue = dst: { 29988 const tracked_inst = switch (air_tag) { 29989 else => maybe_inst, 29990 .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => null, 29991 }; 29992 if (maybe_inst) |inst| { 29993 if ((!sse_op or lhs_mcv.isRegister()) and 29994 self.reuseOperandAdvanced(inst, ordered_air[0], 0, lhs_mcv, tracked_inst)) 29995 break :dst lhs_mcv; 29996 if (is_commutative and (!sse_op or rhs_mcv.isRegister()) and 29997 self.reuseOperandAdvanced(inst, ordered_air[1], 1, rhs_mcv, tracked_inst)) 29998 { 29999 flipped = true; 30000 break :dst rhs_mcv; 30001 } 30002 } 30003 const dst_mcv = try self.allocRegOrMemAdvanced(lhs_ty, tracked_inst, true); 30004 if (sse_op and lhs_mcv.isRegister() and self.hasFeature(.avx)) 30005 copied_to_dst = false 30006 else 30007 try self.genCopy(lhs_ty, dst_mcv, lhs_mcv, .{}); 30008 rhs_mcv = try self.resolveInst(ordered_air[1]); 30009 break :dst dst_mcv; 30010 }; 30011 const dst_locks: [2]?RegisterLock = switch (dst_mcv) { 30012 .register => |dst_reg| .{ self.register_manager.lockReg(dst_reg), null }, 30013 .register_pair => |dst_regs| self.register_manager.lockRegs(2, dst_regs), 30014 else => @splat(null), 30015 }; 30016 defer for (dst_locks) |dst_lock| if (dst_lock) |lock| self.register_manager.unlockReg(lock); 30017 30018 const unmat_src_mcv = if (flipped) lhs_mcv else rhs_mcv; 30019 const src_mcv: MCValue = if (maybe_mask_reg) |mask_reg| 30020 if (self.hasFeature(.avx) and unmat_src_mcv.isRegister() and maybe_inst != null and 30021 self.liveness.operandDies(maybe_inst.?, if (flipped) 0 else 1)) unmat_src_mcv else src: { 30022 try self.genSetReg(mask_reg, rhs_ty, unmat_src_mcv, .{}); 30023 break :src .{ .register = mask_reg }; 30024 } 30025 else 30026 unmat_src_mcv; 30027 const src_locks: [2]?RegisterLock = switch (src_mcv) { 30028 .register => |src_reg| .{ self.register_manager.lockReg(src_reg), null }, 30029 .register_pair => |src_regs| self.register_manager.lockRegs(2, src_regs), 30030 else => @splat(null), 30031 }; 30032 defer for (src_locks) |src_lock| if (src_lock) |lock| self.register_manager.unlockReg(lock); 30033 30034 if (!sse_op) { 30035 switch (air_tag) { 30036 .add, 30037 .add_wrap, 30038 => try self.genBinOpMir(.{ ._, .add }, lhs_ty, dst_mcv, src_mcv), 30039 30040 .sub, 30041 .sub_wrap, 30042 => try self.genBinOpMir(.{ ._, .sub }, lhs_ty, dst_mcv, src_mcv), 30043 30044 .ptr_add, 30045 .ptr_sub, 30046 => { 30047 const tmp_reg = try self.copyToTmpRegister(rhs_ty, src_mcv); 30048 const tmp_mcv = MCValue{ .register = tmp_reg }; 30049 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30050 defer self.register_manager.unlockReg(tmp_lock); 30051 30052 const elem_size = lhs_ty.elemType2(zcu).abiSize(zcu); 30053 try self.genIntMulComplexOpMir(rhs_ty, tmp_mcv, .{ .immediate = elem_size }); 30054 try self.genBinOpMir( 30055 switch (air_tag) { 30056 .ptr_add => .{ ._, .add }, 30057 .ptr_sub => .{ ._, .sub }, 30058 else => unreachable, 30059 }, 30060 lhs_ty, 30061 dst_mcv, 30062 tmp_mcv, 30063 ); 30064 }, 30065 30066 .bool_or, 30067 .bit_or, 30068 => try self.genBinOpMir(.{ ._, .@"or" }, lhs_ty, dst_mcv, src_mcv), 30069 30070 .bool_and, 30071 .bit_and, 30072 => try self.genBinOpMir(.{ ._, .@"and" }, lhs_ty, dst_mcv, src_mcv), 30073 30074 .xor => try self.genBinOpMir(.{ ._, .xor }, lhs_ty, dst_mcv, src_mcv), 30075 30076 .min, 30077 .max, 30078 => { 30079 const resolved_src_mcv = switch (src_mcv) { 30080 else => src_mcv, 30081 .air_ref => |src_ref| try self.resolveInst(src_ref), 30082 }; 30083 30084 if (abi_size > 8) { 30085 const dst_regs = switch (dst_mcv) { 30086 .register_pair => |dst_regs| dst_regs, 30087 else => dst: { 30088 const dst_regs = try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp); 30089 const dst_regs_locks = self.register_manager.lockRegsAssumeUnused(2, dst_regs); 30090 defer for (dst_regs_locks) |lock| self.register_manager.unlockReg(lock); 30091 30092 try self.genCopy(lhs_ty, .{ .register_pair = dst_regs }, dst_mcv, .{}); 30093 break :dst dst_regs; 30094 }, 30095 }; 30096 const dst_regs_locks = self.register_manager.lockRegs(2, dst_regs); 30097 defer for (dst_regs_locks) |dst_lock| if (dst_lock) |lock| 30098 self.register_manager.unlockReg(lock); 30099 30100 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 30101 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30102 defer self.register_manager.unlockReg(tmp_lock); 30103 30104 const signed = lhs_ty.isSignedInt(zcu); 30105 const cc: Condition = switch (air_tag) { 30106 .min => if (signed) .nl else .nb, 30107 .max => if (signed) .nge else .nae, 30108 else => unreachable, 30109 }; 30110 30111 try self.asmRegisterRegister(.{ ._, .mov }, tmp_reg, dst_regs[1]); 30112 if (src_mcv.isBase()) { 30113 try self.asmRegisterMemory( 30114 .{ ._, .cmp }, 30115 dst_regs[0], 30116 try src_mcv.mem(self, .{ .size = .qword }), 30117 ); 30118 try self.asmRegisterMemory( 30119 .{ ._, .sbb }, 30120 tmp_reg, 30121 try src_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 30122 ); 30123 try self.asmCmovccRegisterMemory( 30124 cc, 30125 dst_regs[0], 30126 try src_mcv.mem(self, .{ .size = .qword }), 30127 ); 30128 try self.asmCmovccRegisterMemory( 30129 cc, 30130 dst_regs[1], 30131 try src_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }), 30132 ); 30133 } else { 30134 try self.asmRegisterRegister( 30135 .{ ._, .cmp }, 30136 dst_regs[0], 30137 src_mcv.register_pair[0], 30138 ); 30139 try self.asmRegisterRegister( 30140 .{ ._, .sbb }, 30141 tmp_reg, 30142 src_mcv.register_pair[1], 30143 ); 30144 try self.asmCmovccRegisterRegister(cc, dst_regs[0], src_mcv.register_pair[0]); 30145 try self.asmCmovccRegisterRegister(cc, dst_regs[1], src_mcv.register_pair[1]); 30146 } 30147 try self.genCopy(lhs_ty, dst_mcv, .{ .register_pair = dst_regs }, .{}); 30148 } else { 30149 const mat_src_mcv: MCValue = if (switch (resolved_src_mcv) { 30150 .immediate, 30151 .eflags, 30152 .register_offset, 30153 .load_symbol, 30154 .lea_symbol, 30155 .load_direct, 30156 .lea_direct, 30157 .load_got, 30158 .lea_got, 30159 .load_tlv, 30160 .lea_tlv, 30161 .lea_frame, 30162 => true, 30163 .memory => |addr| std.math.cast(i32, @as(i64, @bitCast(addr))) == null, 30164 else => false, 30165 .register_pair, 30166 .register_overflow, 30167 => unreachable, 30168 }) 30169 .{ .register = try self.copyToTmpRegister(rhs_ty, resolved_src_mcv) } 30170 else 30171 resolved_src_mcv; 30172 const mat_mcv_lock = switch (mat_src_mcv) { 30173 .register => |reg| self.register_manager.lockReg(reg), 30174 else => null, 30175 }; 30176 defer if (mat_mcv_lock) |lock| self.register_manager.unlockReg(lock); 30177 30178 try self.genBinOpMir(.{ ._, .cmp }, lhs_ty, dst_mcv, mat_src_mcv); 30179 30180 const int_info = lhs_ty.intInfo(zcu); 30181 const cc: Condition = switch (int_info.signedness) { 30182 .unsigned => switch (air_tag) { 30183 .min => .a, 30184 .max => .b, 30185 else => unreachable, 30186 }, 30187 .signed => switch (air_tag) { 30188 .min => .g, 30189 .max => .l, 30190 else => unreachable, 30191 }, 30192 }; 30193 30194 const cmov_abi_size = @max(@as(u32, @intCast(lhs_ty.abiSize(zcu))), 2); 30195 const tmp_reg = switch (dst_mcv) { 30196 .register => |reg| reg, 30197 else => try self.copyToTmpRegister(lhs_ty, dst_mcv), 30198 }; 30199 const tmp_lock = self.register_manager.lockReg(tmp_reg); 30200 defer if (tmp_lock) |lock| self.register_manager.unlockReg(lock); 30201 switch (mat_src_mcv) { 30202 .none, 30203 .unreach, 30204 .dead, 30205 .undef, 30206 .immediate, 30207 .eflags, 30208 .register_pair, 30209 .register_triple, 30210 .register_quadruple, 30211 .register_offset, 30212 .register_overflow, 30213 .register_mask, 30214 .load_symbol, 30215 .lea_symbol, 30216 .load_direct, 30217 .lea_direct, 30218 .load_got, 30219 .lea_got, 30220 .load_tlv, 30221 .lea_tlv, 30222 .lea_frame, 30223 .elementwise_regs_then_frame, 30224 .reserved_frame, 30225 .air_ref, 30226 => unreachable, 30227 .register => |src_reg| try self.asmCmovccRegisterRegister( 30228 cc, 30229 registerAlias(tmp_reg, cmov_abi_size), 30230 registerAlias(src_reg, cmov_abi_size), 30231 ), 30232 .memory, .indirect, .load_frame => try self.asmCmovccRegisterMemory( 30233 cc, 30234 registerAlias(tmp_reg, cmov_abi_size), 30235 switch (mat_src_mcv) { 30236 .memory => |addr| .{ 30237 .base = .{ .reg = .ds }, 30238 .mod = .{ .rm = .{ 30239 .size = .fromSize(cmov_abi_size), 30240 .disp = @intCast(@as(i64, @bitCast(addr))), 30241 } }, 30242 }, 30243 .indirect => |reg_off| .{ 30244 .base = .{ .reg = reg_off.reg }, 30245 .mod = .{ .rm = .{ 30246 .size = .fromSize(cmov_abi_size), 30247 .disp = reg_off.off, 30248 } }, 30249 }, 30250 .load_frame => |frame_addr| .{ 30251 .base = .{ .frame = frame_addr.index }, 30252 .mod = .{ .rm = .{ 30253 .size = .fromSize(cmov_abi_size), 30254 .disp = frame_addr.off, 30255 } }, 30256 }, 30257 else => unreachable, 30258 }, 30259 ), 30260 } 30261 try self.genCopy(lhs_ty, dst_mcv, .{ .register = tmp_reg }, .{}); 30262 } 30263 }, 30264 30265 .cmp_eq, .cmp_neq => { 30266 assert(lhs_ty.isVector(zcu) and lhs_ty.childType(zcu).toIntern() == .bool_type); 30267 try self.genBinOpMir(.{ ._, .xor }, lhs_ty, dst_mcv, src_mcv); 30268 switch (air_tag) { 30269 .cmp_eq => try self.genUnOpMir(.{ ._, .not }, lhs_ty, dst_mcv), 30270 .cmp_neq => {}, 30271 else => unreachable, 30272 } 30273 }, 30274 30275 else => return self.fail("TODO implement genBinOp for {s} {}", .{ 30276 @tagName(air_tag), lhs_ty.fmt(pt), 30277 }), 30278 } 30279 return dst_mcv; 30280 } 30281 30282 const dst_reg = registerAlias(dst_mcv.getReg().?, abi_size); 30283 const mir_tag = @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(zcu)) { 30284 else => unreachable, 30285 .float => switch (lhs_ty.floatBits(self.target.*)) { 30286 16 => { 30287 assert(self.hasFeature(.f16c)); 30288 const lhs_reg = if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size); 30289 30290 const tmp_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128(); 30291 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30292 defer self.register_manager.unlockReg(tmp_lock); 30293 30294 if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 30295 .{ .vp_w, .insr }, 30296 dst_reg, 30297 lhs_reg, 30298 try src_mcv.mem(self, .{ .size = .word }), 30299 .u(1), 30300 ) else try self.asmRegisterRegisterRegister( 30301 .{ .vp_, .unpcklwd }, 30302 dst_reg, 30303 lhs_reg, 30304 (if (src_mcv.isRegister()) 30305 src_mcv.getReg().? 30306 else 30307 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 30308 ); 30309 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); 30310 try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp_reg, dst_reg); 30311 try self.asmRegisterRegisterRegister( 30312 switch (air_tag) { 30313 .add => .{ .v_ss, .add }, 30314 .sub => .{ .v_ss, .sub }, 30315 .mul => .{ .v_ss, .mul }, 30316 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ss, .div }, 30317 .max => .{ .v_ss, .max }, 30318 .min => .{ .v_ss, .min }, 30319 else => unreachable, 30320 }, 30321 dst_reg, 30322 dst_reg, 30323 tmp_reg, 30324 ); 30325 switch (air_tag) { 30326 .div_trunc, .div_floor => try self.asmRegisterRegisterRegisterImmediate( 30327 .{ .v_ss, .round }, 30328 dst_reg, 30329 dst_reg, 30330 dst_reg, 30331 bits.RoundMode.imm(.{ 30332 .mode = switch (air_tag) { 30333 .div_trunc => .zero, 30334 .div_floor => .down, 30335 else => unreachable, 30336 }, 30337 .precision = .inexact, 30338 }), 30339 ), 30340 else => {}, 30341 } 30342 try self.asmRegisterRegisterImmediate( 30343 .{ .v_, .cvtps2ph }, 30344 dst_reg, 30345 dst_reg, 30346 bits.RoundMode.imm(.{}), 30347 ); 30348 return dst_mcv; 30349 }, 30350 32 => switch (air_tag) { 30351 .add => if (self.hasFeature(.avx)) .{ .v_ss, .add } else .{ ._ss, .add }, 30352 .sub => if (self.hasFeature(.avx)) .{ .v_ss, .sub } else .{ ._ss, .sub }, 30353 .mul => if (self.hasFeature(.avx)) .{ .v_ss, .mul } else .{ ._ss, .mul }, 30354 .div_float, 30355 .div_trunc, 30356 .div_floor, 30357 .div_exact, 30358 => if (self.hasFeature(.avx)) .{ .v_ss, .div } else .{ ._ss, .div }, 30359 .max => if (self.hasFeature(.avx)) .{ .v_ss, .max } else .{ ._ss, .max }, 30360 .min => if (self.hasFeature(.avx)) .{ .v_ss, .min } else .{ ._ss, .min }, 30361 else => unreachable, 30362 }, 30363 64 => switch (air_tag) { 30364 .add => if (self.hasFeature(.avx)) .{ .v_sd, .add } else .{ ._sd, .add }, 30365 .sub => if (self.hasFeature(.avx)) .{ .v_sd, .sub } else .{ ._sd, .sub }, 30366 .mul => if (self.hasFeature(.avx)) .{ .v_sd, .mul } else .{ ._sd, .mul }, 30367 .div_float, 30368 .div_trunc, 30369 .div_floor, 30370 .div_exact, 30371 => if (self.hasFeature(.avx)) .{ .v_sd, .div } else .{ ._sd, .div }, 30372 .max => if (self.hasFeature(.avx)) .{ .v_sd, .max } else .{ ._sd, .max }, 30373 .min => if (self.hasFeature(.avx)) .{ .v_sd, .min } else .{ ._sd, .min }, 30374 else => unreachable, 30375 }, 30376 80, 128 => null, 30377 else => unreachable, 30378 }, 30379 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 30380 else => null, 30381 .int => switch (lhs_ty.childType(zcu).intInfo(zcu).bits) { 30382 8 => switch (lhs_ty.vectorLen(zcu)) { 30383 1...16 => switch (air_tag) { 30384 .add, 30385 .add_wrap, 30386 => if (self.hasFeature(.avx)) .{ .vp_b, .add } else .{ .p_b, .add }, 30387 .sub, 30388 .sub_wrap, 30389 => if (self.hasFeature(.avx)) .{ .vp_b, .sub } else .{ .p_b, .sub }, 30390 .bit_and => if (self.hasFeature(.avx)) 30391 .{ .vp_, .@"and" } 30392 else 30393 .{ .p_, .@"and" }, 30394 .bit_or => if (self.hasFeature(.avx)) .{ .vp_, .@"or" } else .{ .p_, .@"or" }, 30395 .xor => if (self.hasFeature(.avx)) .{ .vp_, .xor } else .{ .p_, .xor }, 30396 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30397 .signed => if (self.hasFeature(.avx)) 30398 .{ .vp_b, .mins } 30399 else if (self.hasFeature(.sse4_1)) 30400 .{ .p_b, .mins } 30401 else 30402 null, 30403 .unsigned => if (self.hasFeature(.avx)) 30404 .{ .vp_b, .minu } 30405 else if (self.hasFeature(.sse4_1)) 30406 .{ .p_b, .minu } 30407 else 30408 null, 30409 }, 30410 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30411 .signed => if (self.hasFeature(.avx)) 30412 .{ .vp_b, .maxs } 30413 else if (self.hasFeature(.sse4_1)) 30414 .{ .p_b, .maxs } 30415 else 30416 null, 30417 .unsigned => if (self.hasFeature(.avx)) 30418 .{ .vp_b, .maxu } 30419 else if (self.hasFeature(.sse4_1)) 30420 .{ .p_b, .maxu } 30421 else 30422 null, 30423 }, 30424 .cmp_lt, 30425 .cmp_lte, 30426 .cmp_gte, 30427 .cmp_gt, 30428 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30429 .signed => if (self.hasFeature(.avx)) 30430 .{ .vp_b, .cmpgt } 30431 else 30432 .{ .p_b, .cmpgt }, 30433 .unsigned => null, 30434 }, 30435 .cmp_eq, 30436 .cmp_neq, 30437 => if (self.hasFeature(.avx)) .{ .vp_b, .cmpeq } else .{ .p_b, .cmpeq }, 30438 else => null, 30439 }, 30440 17...32 => switch (air_tag) { 30441 .add, 30442 .add_wrap, 30443 => if (self.hasFeature(.avx2)) .{ .vp_b, .add } else null, 30444 .sub, 30445 .sub_wrap, 30446 => if (self.hasFeature(.avx2)) .{ .vp_b, .sub } else null, 30447 .bit_and => if (self.hasFeature(.avx2)) .{ .vp_, .@"and" } else null, 30448 .bit_or => if (self.hasFeature(.avx2)) .{ .vp_, .@"or" } else null, 30449 .xor => if (self.hasFeature(.avx2)) .{ .vp_, .xor } else null, 30450 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30451 .signed => if (self.hasFeature(.avx2)) .{ .vp_b, .mins } else null, 30452 .unsigned => if (self.hasFeature(.avx)) .{ .vp_b, .minu } else null, 30453 }, 30454 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30455 .signed => if (self.hasFeature(.avx2)) .{ .vp_b, .maxs } else null, 30456 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_b, .maxu } else null, 30457 }, 30458 .cmp_lt, 30459 .cmp_lte, 30460 .cmp_gte, 30461 .cmp_gt, 30462 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30463 .signed => if (self.hasFeature(.avx)) .{ .vp_b, .cmpgt } else null, 30464 .unsigned => null, 30465 }, 30466 .cmp_eq, 30467 .cmp_neq, 30468 => if (self.hasFeature(.avx)) .{ .vp_b, .cmpeq } else null, 30469 else => null, 30470 }, 30471 else => null, 30472 }, 30473 16 => switch (lhs_ty.vectorLen(zcu)) { 30474 1...8 => switch (air_tag) { 30475 .add, 30476 .add_wrap, 30477 => if (self.hasFeature(.avx)) .{ .vp_w, .add } else .{ .p_w, .add }, 30478 .sub, 30479 .sub_wrap, 30480 => if (self.hasFeature(.avx)) .{ .vp_w, .sub } else .{ .p_w, .sub }, 30481 .mul, 30482 .mul_wrap, 30483 => if (self.hasFeature(.avx)) .{ .vp_w, .mull } else .{ .p_d, .mull }, 30484 .bit_and => if (self.hasFeature(.avx)) 30485 .{ .vp_, .@"and" } 30486 else 30487 .{ .p_, .@"and" }, 30488 .bit_or => if (self.hasFeature(.avx)) .{ .vp_, .@"or" } else .{ .p_, .@"or" }, 30489 .xor => if (self.hasFeature(.avx)) .{ .vp_, .xor } else .{ .p_, .xor }, 30490 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30491 .signed => if (self.hasFeature(.avx)) 30492 .{ .vp_w, .mins } 30493 else 30494 .{ .p_w, .mins }, 30495 .unsigned => if (self.hasFeature(.avx)) 30496 .{ .vp_w, .minu } 30497 else 30498 .{ .p_w, .minu }, 30499 }, 30500 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30501 .signed => if (self.hasFeature(.avx)) 30502 .{ .vp_w, .maxs } 30503 else 30504 .{ .p_w, .maxs }, 30505 .unsigned => if (self.hasFeature(.avx)) 30506 .{ .vp_w, .maxu } 30507 else 30508 .{ .p_w, .maxu }, 30509 }, 30510 .cmp_lt, 30511 .cmp_lte, 30512 .cmp_gte, 30513 .cmp_gt, 30514 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30515 .signed => if (self.hasFeature(.avx)) 30516 .{ .vp_w, .cmpgt } 30517 else 30518 .{ .p_w, .cmpgt }, 30519 .unsigned => null, 30520 }, 30521 .cmp_eq, 30522 .cmp_neq, 30523 => if (self.hasFeature(.avx)) .{ .vp_w, .cmpeq } else .{ .p_w, .cmpeq }, 30524 else => null, 30525 }, 30526 9...16 => switch (air_tag) { 30527 .add, 30528 .add_wrap, 30529 => if (self.hasFeature(.avx2)) .{ .vp_w, .add } else null, 30530 .sub, 30531 .sub_wrap, 30532 => if (self.hasFeature(.avx2)) .{ .vp_w, .sub } else null, 30533 .mul, 30534 .mul_wrap, 30535 => if (self.hasFeature(.avx2)) .{ .vp_w, .mull } else null, 30536 .bit_and => if (self.hasFeature(.avx2)) .{ .vp_, .@"and" } else null, 30537 .bit_or => if (self.hasFeature(.avx2)) .{ .vp_, .@"or" } else null, 30538 .xor => if (self.hasFeature(.avx2)) .{ .vp_, .xor } else null, 30539 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30540 .signed => if (self.hasFeature(.avx2)) .{ .vp_w, .mins } else null, 30541 .unsigned => if (self.hasFeature(.avx)) .{ .vp_w, .minu } else null, 30542 }, 30543 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30544 .signed => if (self.hasFeature(.avx2)) .{ .vp_w, .maxs } else null, 30545 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_w, .maxu } else null, 30546 }, 30547 .cmp_lt, 30548 .cmp_lte, 30549 .cmp_gte, 30550 .cmp_gt, 30551 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30552 .signed => if (self.hasFeature(.avx)) .{ .vp_w, .cmpgt } else null, 30553 .unsigned => null, 30554 }, 30555 .cmp_eq, 30556 .cmp_neq, 30557 => if (self.hasFeature(.avx)) .{ .vp_w, .cmpeq } else null, 30558 else => null, 30559 }, 30560 else => null, 30561 }, 30562 32 => switch (lhs_ty.vectorLen(zcu)) { 30563 1...4 => switch (air_tag) { 30564 .add, 30565 .add_wrap, 30566 => if (self.hasFeature(.avx)) .{ .vp_d, .add } else .{ .p_d, .add }, 30567 .sub, 30568 .sub_wrap, 30569 => if (self.hasFeature(.avx)) .{ .vp_d, .sub } else .{ .p_d, .sub }, 30570 .mul, 30571 .mul_wrap, 30572 => if (self.hasFeature(.avx)) 30573 .{ .vp_d, .mull } 30574 else if (self.hasFeature(.sse4_1)) 30575 .{ .p_d, .mull } 30576 else 30577 null, 30578 .bit_and => if (self.hasFeature(.avx)) 30579 .{ .vp_, .@"and" } 30580 else 30581 .{ .p_, .@"and" }, 30582 .bit_or => if (self.hasFeature(.avx)) .{ .vp_, .@"or" } else .{ .p_, .@"or" }, 30583 .xor => if (self.hasFeature(.avx)) .{ .vp_, .xor } else .{ .p_, .xor }, 30584 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30585 .signed => if (self.hasFeature(.avx)) 30586 .{ .vp_d, .mins } 30587 else if (self.hasFeature(.sse4_1)) 30588 .{ .p_d, .mins } 30589 else 30590 null, 30591 .unsigned => if (self.hasFeature(.avx)) 30592 .{ .vp_d, .minu } 30593 else if (self.hasFeature(.sse4_1)) 30594 .{ .p_d, .minu } 30595 else 30596 null, 30597 }, 30598 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30599 .signed => if (self.hasFeature(.avx)) 30600 .{ .vp_d, .maxs } 30601 else if (self.hasFeature(.sse4_1)) 30602 .{ .p_d, .maxs } 30603 else 30604 null, 30605 .unsigned => if (self.hasFeature(.avx)) 30606 .{ .vp_d, .maxu } 30607 else if (self.hasFeature(.sse4_1)) 30608 .{ .p_d, .maxu } 30609 else 30610 null, 30611 }, 30612 .cmp_lt, 30613 .cmp_lte, 30614 .cmp_gte, 30615 .cmp_gt, 30616 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30617 .signed => if (self.hasFeature(.avx)) 30618 .{ .vp_d, .cmpgt } 30619 else 30620 .{ .p_d, .cmpgt }, 30621 .unsigned => null, 30622 }, 30623 .cmp_eq, 30624 .cmp_neq, 30625 => if (self.hasFeature(.avx)) .{ .vp_d, .cmpeq } else .{ .p_d, .cmpeq }, 30626 else => null, 30627 }, 30628 5...8 => switch (air_tag) { 30629 .add, 30630 .add_wrap, 30631 => if (self.hasFeature(.avx2)) .{ .vp_d, .add } else null, 30632 .sub, 30633 .sub_wrap, 30634 => if (self.hasFeature(.avx2)) .{ .vp_d, .sub } else null, 30635 .mul, 30636 .mul_wrap, 30637 => if (self.hasFeature(.avx2)) .{ .vp_d, .mull } else null, 30638 .bit_and => if (self.hasFeature(.avx2)) .{ .vp_, .@"and" } else null, 30639 .bit_or => if (self.hasFeature(.avx2)) .{ .vp_, .@"or" } else null, 30640 .xor => if (self.hasFeature(.avx2)) .{ .vp_, .xor } else null, 30641 .min => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30642 .signed => if (self.hasFeature(.avx2)) .{ .vp_d, .mins } else null, 30643 .unsigned => if (self.hasFeature(.avx)) .{ .vp_d, .minu } else null, 30644 }, 30645 .max => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30646 .signed => if (self.hasFeature(.avx2)) .{ .vp_d, .maxs } else null, 30647 .unsigned => if (self.hasFeature(.avx2)) .{ .vp_d, .maxu } else null, 30648 }, 30649 .cmp_lt, 30650 .cmp_lte, 30651 .cmp_gte, 30652 .cmp_gt, 30653 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30654 .signed => if (self.hasFeature(.avx)) .{ .vp_d, .cmpgt } else null, 30655 .unsigned => null, 30656 }, 30657 .cmp_eq, 30658 .cmp_neq, 30659 => if (self.hasFeature(.avx)) .{ .vp_d, .cmpeq } else null, 30660 else => null, 30661 }, 30662 else => null, 30663 }, 30664 64 => switch (lhs_ty.vectorLen(zcu)) { 30665 1...2 => switch (air_tag) { 30666 .add, 30667 .add_wrap, 30668 => if (self.hasFeature(.avx)) .{ .vp_q, .add } else .{ .p_q, .add }, 30669 .sub, 30670 .sub_wrap, 30671 => if (self.hasFeature(.avx)) .{ .vp_q, .sub } else .{ .p_q, .sub }, 30672 .bit_and => if (self.hasFeature(.avx)) 30673 .{ .vp_, .@"and" } 30674 else 30675 .{ .p_, .@"and" }, 30676 .bit_or => if (self.hasFeature(.avx)) .{ .vp_, .@"or" } else .{ .p_, .@"or" }, 30677 .xor => if (self.hasFeature(.avx)) .{ .vp_, .xor } else .{ .p_, .xor }, 30678 .cmp_lt, 30679 .cmp_lte, 30680 .cmp_gte, 30681 .cmp_gt, 30682 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30683 .signed => if (self.hasFeature(.avx)) 30684 .{ .vp_q, .cmpgt } 30685 else if (self.hasFeature(.sse4_2)) 30686 .{ .p_q, .cmpgt } 30687 else 30688 null, 30689 .unsigned => null, 30690 }, 30691 .cmp_eq, 30692 .cmp_neq, 30693 => if (self.hasFeature(.avx)) 30694 .{ .vp_q, .cmpeq } 30695 else if (self.hasFeature(.sse4_1)) 30696 .{ .p_q, .cmpeq } 30697 else 30698 null, 30699 else => null, 30700 }, 30701 3...4 => switch (air_tag) { 30702 .add, 30703 .add_wrap, 30704 => if (self.hasFeature(.avx2)) .{ .vp_q, .add } else null, 30705 .sub, 30706 .sub_wrap, 30707 => if (self.hasFeature(.avx2)) .{ .vp_q, .sub } else null, 30708 .bit_and => if (self.hasFeature(.avx2)) .{ .vp_, .@"and" } else null, 30709 .bit_or => if (self.hasFeature(.avx2)) .{ .vp_, .@"or" } else null, 30710 .xor => if (self.hasFeature(.avx2)) .{ .vp_, .xor } else null, 30711 .cmp_eq, 30712 .cmp_neq, 30713 => if (self.hasFeature(.avx)) .{ .vp_d, .cmpeq } else null, 30714 .cmp_lt, 30715 .cmp_lte, 30716 .cmp_gt, 30717 .cmp_gte, 30718 => switch (lhs_ty.childType(zcu).intInfo(zcu).signedness) { 30719 .signed => if (self.hasFeature(.avx)) .{ .vp_d, .cmpgt } else null, 30720 .unsigned => null, 30721 }, 30722 else => null, 30723 }, 30724 else => null, 30725 }, 30726 else => null, 30727 }, 30728 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 30729 16 => tag: { 30730 assert(self.hasFeature(.f16c)); 30731 const lhs_reg = if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size); 30732 switch (lhs_ty.vectorLen(zcu)) { 30733 1 => { 30734 const tmp_reg = 30735 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128(); 30736 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30737 defer self.register_manager.unlockReg(tmp_lock); 30738 30739 if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 30740 .{ .vp_w, .insr }, 30741 dst_reg, 30742 lhs_reg, 30743 try src_mcv.mem(self, .{ .size = .word }), 30744 .u(1), 30745 ) else try self.asmRegisterRegisterRegister( 30746 .{ .vp_, .unpcklwd }, 30747 dst_reg, 30748 lhs_reg, 30749 (if (src_mcv.isRegister()) 30750 src_mcv.getReg().? 30751 else 30752 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 30753 ); 30754 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); 30755 try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp_reg, dst_reg); 30756 try self.asmRegisterRegisterRegister( 30757 switch (air_tag) { 30758 .add => .{ .v_ss, .add }, 30759 .sub => .{ .v_ss, .sub }, 30760 .mul => .{ .v_ss, .mul }, 30761 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ss, .div }, 30762 .max => .{ .v_ss, .max }, 30763 .min => .{ .v_ss, .max }, 30764 else => unreachable, 30765 }, 30766 dst_reg, 30767 dst_reg, 30768 tmp_reg, 30769 ); 30770 try self.asmRegisterRegisterImmediate( 30771 .{ .v_, .cvtps2ph }, 30772 dst_reg, 30773 dst_reg, 30774 bits.RoundMode.imm(.{}), 30775 ); 30776 return dst_mcv; 30777 }, 30778 2 => { 30779 const tmp_reg = (try self.register_manager.allocReg( 30780 null, 30781 abi.RegisterClass.sse, 30782 )).to128(); 30783 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30784 defer self.register_manager.unlockReg(tmp_lock); 30785 30786 if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 30787 .{ .vp_d, .insr }, 30788 dst_reg, 30789 lhs_reg, 30790 try src_mcv.mem(self, .{ .size = .dword }), 30791 .u(1), 30792 ) else try self.asmRegisterRegisterRegister( 30793 .{ .v_ps, .unpckl }, 30794 dst_reg, 30795 lhs_reg, 30796 (if (src_mcv.isRegister()) 30797 src_mcv.getReg().? 30798 else 30799 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 30800 ); 30801 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, dst_reg); 30802 try self.asmRegisterRegisterRegister( 30803 .{ .v_ps, .movhl }, 30804 tmp_reg, 30805 dst_reg, 30806 dst_reg, 30807 ); 30808 try self.asmRegisterRegisterRegister( 30809 switch (air_tag) { 30810 .add => .{ .v_ps, .add }, 30811 .sub => .{ .v_ps, .sub }, 30812 .mul => .{ .v_ps, .mul }, 30813 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, 30814 .max => .{ .v_ps, .max }, 30815 .min => .{ .v_ps, .max }, 30816 else => unreachable, 30817 }, 30818 dst_reg, 30819 dst_reg, 30820 tmp_reg, 30821 ); 30822 try self.asmRegisterRegisterImmediate( 30823 .{ .v_, .cvtps2ph }, 30824 dst_reg, 30825 dst_reg, 30826 bits.RoundMode.imm(.{}), 30827 ); 30828 return dst_mcv; 30829 }, 30830 3...4 => { 30831 const tmp_reg = (try self.register_manager.allocReg( 30832 null, 30833 abi.RegisterClass.sse, 30834 )).to128(); 30835 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30836 defer self.register_manager.unlockReg(tmp_lock); 30837 30838 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg, lhs_reg); 30839 if (src_mcv.isBase()) try self.asmRegisterMemory( 30840 .{ .v_ps, .cvtph2 }, 30841 tmp_reg, 30842 try src_mcv.mem(self, .{ .size = .qword }), 30843 ) else try self.asmRegisterRegister( 30844 .{ .v_ps, .cvtph2 }, 30845 tmp_reg, 30846 (if (src_mcv.isRegister()) 30847 src_mcv.getReg().? 30848 else 30849 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 30850 ); 30851 try self.asmRegisterRegisterRegister( 30852 switch (air_tag) { 30853 .add => .{ .v_ps, .add }, 30854 .sub => .{ .v_ps, .sub }, 30855 .mul => .{ .v_ps, .mul }, 30856 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, 30857 .max => .{ .v_ps, .max }, 30858 .min => .{ .v_ps, .max }, 30859 else => unreachable, 30860 }, 30861 dst_reg, 30862 dst_reg, 30863 tmp_reg, 30864 ); 30865 try self.asmRegisterRegisterImmediate( 30866 .{ .v_, .cvtps2ph }, 30867 dst_reg, 30868 dst_reg, 30869 bits.RoundMode.imm(.{}), 30870 ); 30871 return dst_mcv; 30872 }, 30873 5...8 => { 30874 const tmp_reg = (try self.register_manager.allocReg( 30875 null, 30876 abi.RegisterClass.sse, 30877 )).to256(); 30878 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 30879 defer self.register_manager.unlockReg(tmp_lock); 30880 30881 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, dst_reg.to256(), lhs_reg); 30882 if (src_mcv.isBase()) try self.asmRegisterMemory( 30883 .{ .v_ps, .cvtph2 }, 30884 tmp_reg, 30885 try src_mcv.mem(self, .{ .size = .xword }), 30886 ) else try self.asmRegisterRegister( 30887 .{ .v_ps, .cvtph2 }, 30888 tmp_reg, 30889 (if (src_mcv.isRegister()) 30890 src_mcv.getReg().? 30891 else 30892 try self.copyToTmpRegister(rhs_ty, src_mcv)).to128(), 30893 ); 30894 try self.asmRegisterRegisterRegister( 30895 switch (air_tag) { 30896 .add => .{ .v_ps, .add }, 30897 .sub => .{ .v_ps, .sub }, 30898 .mul => .{ .v_ps, .mul }, 30899 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, 30900 .max => .{ .v_ps, .max }, 30901 .min => .{ .v_ps, .max }, 30902 else => unreachable, 30903 }, 30904 dst_reg.to256(), 30905 dst_reg.to256(), 30906 tmp_reg, 30907 ); 30908 try self.asmRegisterRegisterImmediate( 30909 .{ .v_, .cvtps2ph }, 30910 dst_reg, 30911 dst_reg.to256(), 30912 bits.RoundMode.imm(.{}), 30913 ); 30914 return dst_mcv; 30915 }, 30916 else => break :tag null, 30917 } 30918 }, 30919 32 => switch (lhs_ty.vectorLen(zcu)) { 30920 1 => switch (air_tag) { 30921 .add => if (self.hasFeature(.avx)) .{ .v_ss, .add } else .{ ._ss, .add }, 30922 .sub => if (self.hasFeature(.avx)) .{ .v_ss, .sub } else .{ ._ss, .sub }, 30923 .mul => if (self.hasFeature(.avx)) .{ .v_ss, .mul } else .{ ._ss, .mul }, 30924 .div_float, 30925 .div_trunc, 30926 .div_floor, 30927 .div_exact, 30928 => if (self.hasFeature(.avx)) .{ .v_ss, .div } else .{ ._ss, .div }, 30929 .max => if (self.hasFeature(.avx)) .{ .v_ss, .max } else .{ ._ss, .max }, 30930 .min => if (self.hasFeature(.avx)) .{ .v_ss, .min } else .{ ._ss, .min }, 30931 .cmp_lt, 30932 .cmp_lte, 30933 .cmp_eq, 30934 .cmp_gte, 30935 .cmp_gt, 30936 .cmp_neq, 30937 => if (self.hasFeature(.avx)) .{ .v_ss, .cmp } else .{ ._ss, .cmp }, 30938 else => unreachable, 30939 }, 30940 2...4 => switch (air_tag) { 30941 .add => if (self.hasFeature(.avx)) .{ .v_ps, .add } else .{ ._ps, .add }, 30942 .sub => if (self.hasFeature(.avx)) .{ .v_ps, .sub } else .{ ._ps, .sub }, 30943 .mul => if (self.hasFeature(.avx)) .{ .v_ps, .mul } else .{ ._ps, .mul }, 30944 .div_float, 30945 .div_trunc, 30946 .div_floor, 30947 .div_exact, 30948 => if (self.hasFeature(.avx)) .{ .v_ps, .div } else .{ ._ps, .div }, 30949 .max => if (self.hasFeature(.avx)) .{ .v_ps, .max } else .{ ._ps, .max }, 30950 .min => if (self.hasFeature(.avx)) .{ .v_ps, .min } else .{ ._ps, .min }, 30951 .cmp_lt, 30952 .cmp_lte, 30953 .cmp_eq, 30954 .cmp_gte, 30955 .cmp_gt, 30956 .cmp_neq, 30957 => if (self.hasFeature(.avx)) .{ .v_ps, .cmp } else .{ ._ps, .cmp }, 30958 else => unreachable, 30959 }, 30960 5...8 => if (self.hasFeature(.avx)) switch (air_tag) { 30961 .add => .{ .v_ps, .add }, 30962 .sub => .{ .v_ps, .sub }, 30963 .mul => .{ .v_ps, .mul }, 30964 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_ps, .div }, 30965 .max => .{ .v_ps, .max }, 30966 .min => .{ .v_ps, .min }, 30967 .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => .{ .v_ps, .cmp }, 30968 else => unreachable, 30969 } else null, 30970 else => null, 30971 }, 30972 64 => switch (lhs_ty.vectorLen(zcu)) { 30973 1 => switch (air_tag) { 30974 .add => if (self.hasFeature(.avx)) .{ .v_sd, .add } else .{ ._sd, .add }, 30975 .sub => if (self.hasFeature(.avx)) .{ .v_sd, .sub } else .{ ._sd, .sub }, 30976 .mul => if (self.hasFeature(.avx)) .{ .v_sd, .mul } else .{ ._sd, .mul }, 30977 .div_float, 30978 .div_trunc, 30979 .div_floor, 30980 .div_exact, 30981 => if (self.hasFeature(.avx)) .{ .v_sd, .div } else .{ ._sd, .div }, 30982 .max => if (self.hasFeature(.avx)) .{ .v_sd, .max } else .{ ._sd, .max }, 30983 .min => if (self.hasFeature(.avx)) .{ .v_sd, .min } else .{ ._sd, .min }, 30984 .cmp_lt, 30985 .cmp_lte, 30986 .cmp_eq, 30987 .cmp_gte, 30988 .cmp_gt, 30989 .cmp_neq, 30990 => if (self.hasFeature(.avx)) .{ .v_sd, .cmp } else .{ ._sd, .cmp }, 30991 else => unreachable, 30992 }, 30993 2 => switch (air_tag) { 30994 .add => if (self.hasFeature(.avx)) .{ .v_pd, .add } else .{ ._pd, .add }, 30995 .sub => if (self.hasFeature(.avx)) .{ .v_pd, .sub } else .{ ._pd, .sub }, 30996 .mul => if (self.hasFeature(.avx)) .{ .v_pd, .mul } else .{ ._pd, .mul }, 30997 .div_float, 30998 .div_trunc, 30999 .div_floor, 31000 .div_exact, 31001 => if (self.hasFeature(.avx)) .{ .v_pd, .div } else .{ ._pd, .div }, 31002 .max => if (self.hasFeature(.avx)) .{ .v_pd, .max } else .{ ._pd, .max }, 31003 .min => if (self.hasFeature(.avx)) .{ .v_pd, .min } else .{ ._pd, .min }, 31004 .cmp_lt, 31005 .cmp_lte, 31006 .cmp_eq, 31007 .cmp_gte, 31008 .cmp_gt, 31009 .cmp_neq, 31010 => if (self.hasFeature(.avx)) .{ .v_pd, .cmp } else .{ ._pd, .cmp }, 31011 else => unreachable, 31012 }, 31013 3...4 => if (self.hasFeature(.avx)) switch (air_tag) { 31014 .add => .{ .v_pd, .add }, 31015 .sub => .{ .v_pd, .sub }, 31016 .mul => .{ .v_pd, .mul }, 31017 .div_float, .div_trunc, .div_floor, .div_exact => .{ .v_pd, .div }, 31018 .max => .{ .v_pd, .max }, 31019 .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => .{ .v_pd, .cmp }, 31020 .min => .{ .v_pd, .min }, 31021 else => unreachable, 31022 } else null, 31023 else => null, 31024 }, 31025 80, 128 => null, 31026 else => unreachable, 31027 }, 31028 }, 31029 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31030 @tagName(air_tag), lhs_ty.fmt(pt), 31031 }); 31032 31033 const lhs_copy_reg = if (maybe_mask_reg) |_| registerAlias( 31034 if (copied_to_dst) try self.copyToTmpRegister(lhs_ty, dst_mcv) else lhs_mcv.getReg().?, 31035 abi_size, 31036 ) else null; 31037 const lhs_copy_lock = if (lhs_copy_reg) |reg| self.register_manager.lockReg(reg) else null; 31038 defer if (lhs_copy_lock) |lock| self.register_manager.unlockReg(lock); 31039 31040 switch (mir_tag[1]) { 31041 else => if (self.hasFeature(.avx)) { 31042 const lhs_reg = if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size); 31043 if (src_mcv.isBase()) try self.asmRegisterRegisterMemory( 31044 mir_tag, 31045 dst_reg, 31046 lhs_reg, 31047 try src_mcv.mem(self, .{ .size = switch (lhs_ty.zigTypeTag(zcu)) { 31048 else => .fromSize(abi_size), 31049 .vector => .fromBitSize(dst_reg.bitSize()), 31050 } }), 31051 ) else try self.asmRegisterRegisterRegister( 31052 mir_tag, 31053 dst_reg, 31054 lhs_reg, 31055 registerAlias(if (src_mcv.isRegister()) 31056 src_mcv.getReg().? 31057 else 31058 try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size), 31059 ); 31060 } else { 31061 assert(copied_to_dst); 31062 if (src_mcv.isBase()) try self.asmRegisterMemory( 31063 mir_tag, 31064 dst_reg, 31065 try src_mcv.mem(self, .{ .size = switch (lhs_ty.zigTypeTag(zcu)) { 31066 else => .fromSize(abi_size), 31067 .vector => .fromBitSize(dst_reg.bitSize()), 31068 } }), 31069 ) else try self.asmRegisterRegister( 31070 mir_tag, 31071 dst_reg, 31072 registerAlias(if (src_mcv.isRegister()) 31073 src_mcv.getReg().? 31074 else 31075 try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size), 31076 ); 31077 }, 31078 .cmp => { 31079 const imm: Immediate = .u(switch (air_tag) { 31080 .cmp_eq => 0, 31081 .cmp_lt, .cmp_gt => 1, 31082 .cmp_lte, .cmp_gte => 2, 31083 .cmp_neq => 4, 31084 else => unreachable, 31085 }); 31086 if (self.hasFeature(.avx)) { 31087 const lhs_reg = 31088 if (copied_to_dst) dst_reg else registerAlias(lhs_mcv.getReg().?, abi_size); 31089 if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 31090 mir_tag, 31091 dst_reg, 31092 lhs_reg, 31093 try src_mcv.mem(self, .{ .size = switch (lhs_ty.zigTypeTag(zcu)) { 31094 else => .fromSize(abi_size), 31095 .vector => .fromBitSize(dst_reg.bitSize()), 31096 } }), 31097 imm, 31098 ) else try self.asmRegisterRegisterRegisterImmediate( 31099 mir_tag, 31100 dst_reg, 31101 lhs_reg, 31102 registerAlias(if (src_mcv.isRegister()) 31103 src_mcv.getReg().? 31104 else 31105 try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size), 31106 imm, 31107 ); 31108 } else { 31109 assert(copied_to_dst); 31110 if (src_mcv.isBase()) try self.asmRegisterMemoryImmediate( 31111 mir_tag, 31112 dst_reg, 31113 try src_mcv.mem(self, .{ .size = switch (lhs_ty.zigTypeTag(zcu)) { 31114 else => .fromSize(abi_size), 31115 .vector => .fromBitSize(dst_reg.bitSize()), 31116 } }), 31117 imm, 31118 ) else try self.asmRegisterRegisterImmediate( 31119 mir_tag, 31120 dst_reg, 31121 registerAlias(if (src_mcv.isRegister()) 31122 src_mcv.getReg().? 31123 else 31124 try self.copyToTmpRegister(rhs_ty, src_mcv), abi_size), 31125 imm, 31126 ); 31127 } 31128 }, 31129 } 31130 31131 switch (air_tag) { 31132 .add, .add_wrap, .sub, .sub_wrap, .mul, .mul_wrap, .div_float, .div_exact => {}, 31133 .div_trunc, .div_floor => try self.genRound(lhs_ty, dst_reg, .{ .register = dst_reg }, .{ 31134 .mode = switch (air_tag) { 31135 .div_trunc => .zero, 31136 .div_floor => .down, 31137 else => unreachable, 31138 }, 31139 .precision = .inexact, 31140 }), 31141 .bit_and, .bit_or, .xor => {}, 31142 .max, .min => if (maybe_mask_reg) |mask_reg| if (self.hasFeature(.avx)) { 31143 const rhs_copy_reg = registerAlias(src_mcv.getReg().?, abi_size); 31144 31145 try self.asmRegisterRegisterRegisterImmediate( 31146 @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(zcu)) { 31147 .float => switch (lhs_ty.floatBits(self.target.*)) { 31148 32 => .{ .v_ss, .cmp }, 31149 64 => .{ .v_sd, .cmp }, 31150 16, 80, 128 => null, 31151 else => unreachable, 31152 }, 31153 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31154 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 31155 32 => switch (lhs_ty.vectorLen(zcu)) { 31156 1 => .{ .v_ss, .cmp }, 31157 2...8 => .{ .v_ps, .cmp }, 31158 else => null, 31159 }, 31160 64 => switch (lhs_ty.vectorLen(zcu)) { 31161 1 => .{ .v_sd, .cmp }, 31162 2...4 => .{ .v_pd, .cmp }, 31163 else => null, 31164 }, 31165 16, 80, 128 => null, 31166 else => unreachable, 31167 }, 31168 else => unreachable, 31169 }, 31170 else => unreachable, 31171 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31172 @tagName(air_tag), lhs_ty.fmt(pt), 31173 }), 31174 mask_reg, 31175 rhs_copy_reg, 31176 rhs_copy_reg, 31177 bits.VexFloatPredicate.imm(.unord), 31178 ); 31179 try self.asmRegisterRegisterRegisterRegister( 31180 @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(zcu)) { 31181 .float => switch (lhs_ty.floatBits(self.target.*)) { 31182 32 => .{ .v_ps, .blendv }, 31183 64 => .{ .v_pd, .blendv }, 31184 16, 80, 128 => null, 31185 else => unreachable, 31186 }, 31187 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31188 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 31189 32 => switch (lhs_ty.vectorLen(zcu)) { 31190 1...8 => .{ .v_ps, .blendv }, 31191 else => null, 31192 }, 31193 64 => switch (lhs_ty.vectorLen(zcu)) { 31194 1...4 => .{ .v_pd, .blendv }, 31195 else => null, 31196 }, 31197 16, 80, 128 => null, 31198 else => unreachable, 31199 }, 31200 else => unreachable, 31201 }, 31202 else => unreachable, 31203 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31204 @tagName(air_tag), lhs_ty.fmt(pt), 31205 }), 31206 dst_reg, 31207 dst_reg, 31208 lhs_copy_reg.?, 31209 mask_reg, 31210 ); 31211 } else { 31212 const has_blend = self.hasFeature(.sse4_1); 31213 try self.asmRegisterRegisterImmediate( 31214 @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(zcu)) { 31215 .float => switch (lhs_ty.floatBits(self.target.*)) { 31216 32 => .{ ._ss, .cmp }, 31217 64 => .{ ._sd, .cmp }, 31218 16, 80, 128 => null, 31219 else => unreachable, 31220 }, 31221 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31222 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 31223 32 => switch (lhs_ty.vectorLen(zcu)) { 31224 1 => .{ ._ss, .cmp }, 31225 2...4 => .{ ._ps, .cmp }, 31226 else => null, 31227 }, 31228 64 => switch (lhs_ty.vectorLen(zcu)) { 31229 1 => .{ ._sd, .cmp }, 31230 2 => .{ ._pd, .cmp }, 31231 else => null, 31232 }, 31233 16, 80, 128 => null, 31234 else => unreachable, 31235 }, 31236 else => unreachable, 31237 }, 31238 else => unreachable, 31239 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31240 @tagName(air_tag), lhs_ty.fmt(pt), 31241 }), 31242 mask_reg, 31243 mask_reg, 31244 bits.SseFloatPredicate.imm(if (has_blend) .unord else .ord), 31245 ); 31246 if (has_blend) try self.asmRegisterRegisterRegister( 31247 @as(?Mir.Inst.FixedTag, switch (lhs_ty.zigTypeTag(zcu)) { 31248 .float => switch (lhs_ty.floatBits(self.target.*)) { 31249 32 => .{ ._ps, .blendv }, 31250 64 => .{ ._pd, .blendv }, 31251 16, 80, 128 => null, 31252 else => unreachable, 31253 }, 31254 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31255 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 31256 32 => switch (lhs_ty.vectorLen(zcu)) { 31257 1...4 => .{ ._ps, .blendv }, 31258 else => null, 31259 }, 31260 64 => switch (lhs_ty.vectorLen(zcu)) { 31261 1...2 => .{ ._pd, .blendv }, 31262 else => null, 31263 }, 31264 16, 80, 128 => null, 31265 else => unreachable, 31266 }, 31267 else => unreachable, 31268 }, 31269 else => unreachable, 31270 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31271 @tagName(air_tag), lhs_ty.fmt(pt), 31272 }), 31273 dst_reg, 31274 lhs_copy_reg.?, 31275 mask_reg, 31276 ) else { 31277 const mir_fixes = @as(?Mir.Inst.Fixes, switch (lhs_ty.zigTypeTag(zcu)) { 31278 .float => switch (lhs_ty.floatBits(self.target.*)) { 31279 32 => ._ps, 31280 64 => ._pd, 31281 16, 80, 128 => null, 31282 else => unreachable, 31283 }, 31284 .vector => switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31285 .float => switch (lhs_ty.childType(zcu).floatBits(self.target.*)) { 31286 32 => switch (lhs_ty.vectorLen(zcu)) { 31287 1...4 => ._ps, 31288 else => null, 31289 }, 31290 64 => switch (lhs_ty.vectorLen(zcu)) { 31291 1...2 => ._pd, 31292 else => null, 31293 }, 31294 16, 80, 128 => null, 31295 else => unreachable, 31296 }, 31297 else => unreachable, 31298 }, 31299 else => unreachable, 31300 }) orelse return self.fail("TODO implement genBinOp for {s} {}", .{ 31301 @tagName(air_tag), lhs_ty.fmt(pt), 31302 }); 31303 try self.asmRegisterRegister(.{ mir_fixes, .@"and" }, dst_reg, mask_reg); 31304 try self.asmRegisterRegister(.{ mir_fixes, .andn }, mask_reg, lhs_copy_reg.?); 31305 try self.asmRegisterRegister(.{ mir_fixes, .@"or" }, dst_reg, mask_reg); 31306 } 31307 }, 31308 .cmp_lt, .cmp_lte, .cmp_eq, .cmp_gte, .cmp_gt, .cmp_neq => { 31309 switch (lhs_ty.childType(zcu).zigTypeTag(zcu)) { 31310 .int => switch (air_tag) { 31311 .cmp_lt, 31312 .cmp_eq, 31313 .cmp_gt, 31314 => {}, 31315 .cmp_lte, 31316 .cmp_gte, 31317 .cmp_neq, 31318 => { 31319 const unsigned_ty = try lhs_ty.toUnsigned(pt); 31320 const not_mcv = try self.genTypedValue(try unsigned_ty.maxInt(pt, unsigned_ty)); 31321 const not_mem: Memory = if (not_mcv.isBase()) 31322 try not_mcv.mem(self, .{ .size = .fromSize(abi_size) }) 31323 else 31324 .{ .base = .{ 31325 .reg = try self.copyToTmpRegister(.usize, not_mcv.address()), 31326 }, .mod = .{ .rm = .{ .size = .fromSize(abi_size) } } }; 31327 switch (mir_tag[0]) { 31328 .vp_b, .vp_d, .vp_q, .vp_w => try self.asmRegisterRegisterMemory( 31329 .{ .vp_, .xor }, 31330 dst_reg, 31331 dst_reg, 31332 not_mem, 31333 ), 31334 .p_b, .p_d, .p_q, .p_w => try self.asmRegisterMemory( 31335 .{ .p_, .xor }, 31336 dst_reg, 31337 not_mem, 31338 ), 31339 else => unreachable, 31340 } 31341 }, 31342 else => unreachable, 31343 }, 31344 .float => {}, 31345 else => unreachable, 31346 } 31347 31348 const gp_reg = try self.register_manager.allocReg(maybe_inst, abi.RegisterClass.gp); 31349 const gp_lock = self.register_manager.lockRegAssumeUnused(gp_reg); 31350 defer self.register_manager.unlockReg(gp_lock); 31351 31352 try self.asmRegisterRegister(switch (mir_tag[0]) { 31353 ._pd, ._sd, .p_q => .{ ._pd, .movmsk }, 31354 ._ps, ._ss, .p_d => .{ ._ps, .movmsk }, 31355 .p_b => .{ .p_b, .movmsk }, 31356 .p_w => movmsk: { 31357 try self.asmRegisterRegister(.{ .p_b, .ackssw }, dst_reg, dst_reg); 31358 break :movmsk .{ .p_b, .movmsk }; 31359 }, 31360 .v_pd, .v_sd, .vp_q => .{ .v_pd, .movmsk }, 31361 .v_ps, .v_ss, .vp_d => .{ .v_ps, .movmsk }, 31362 .vp_b => .{ .vp_b, .movmsk }, 31363 .vp_w => movmsk: { 31364 try self.asmRegisterRegisterRegister( 31365 .{ .vp_b, .ackssw }, 31366 dst_reg, 31367 dst_reg, 31368 dst_reg, 31369 ); 31370 break :movmsk .{ .vp_b, .movmsk }; 31371 }, 31372 else => unreachable, 31373 }, gp_reg.to32(), dst_reg); 31374 return .{ .register = gp_reg }; 31375 }, 31376 else => unreachable, 31377 } 31378 31379 return dst_mcv; 31380 } 31381 31382 fn genBinOpMir( 31383 self: *CodeGen, 31384 mir_tag: Mir.Inst.FixedTag, 31385 ty: Type, 31386 dst_mcv: MCValue, 31387 src_mcv: MCValue, 31388 ) !void { 31389 const pt = self.pt; 31390 const zcu = pt.zcu; 31391 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 31392 try self.spillEflagsIfOccupied(); 31393 switch (dst_mcv) { 31394 .none, 31395 .unreach, 31396 .dead, 31397 .undef, 31398 .immediate, 31399 .eflags, 31400 .register_overflow, 31401 .register_mask, 31402 .lea_direct, 31403 .lea_got, 31404 .lea_tlv, 31405 .lea_frame, 31406 .lea_symbol, 31407 .elementwise_regs_then_frame, 31408 .reserved_frame, 31409 .air_ref, 31410 => unreachable, // unmodifiable destination 31411 .register, .register_pair, .register_triple, .register_quadruple, .register_offset => { 31412 switch (dst_mcv) { 31413 .register, .register_pair, .register_triple, .register_quadruple => {}, 31414 .register_offset => |ro| assert(ro.off == 0), 31415 else => unreachable, 31416 } 31417 for (dst_mcv.getRegs(), 0..) |dst_reg, dst_reg_i| { 31418 const dst_reg_lock = self.register_manager.lockReg(dst_reg); 31419 defer if (dst_reg_lock) |lock| self.register_manager.unlockReg(lock); 31420 31421 const mir_limb_tag: Mir.Inst.FixedTag = switch (dst_reg_i) { 31422 0 => mir_tag, 31423 1 => switch (mir_tag[1]) { 31424 .add => .{ ._, .adc }, 31425 .sub, .cmp => .{ ._, .sbb }, 31426 .@"or", .@"and", .xor => mir_tag, 31427 else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{ 31428 @tagName(mir_tag[1]), 31429 }), 31430 }, 31431 else => unreachable, 31432 }; 31433 const off: u4 = @intCast(dst_reg_i * 8); 31434 const limb_abi_size = @min(abi_size - off, 8); 31435 const dst_alias = registerAlias(dst_reg, limb_abi_size); 31436 switch (src_mcv) { 31437 .none, 31438 .unreach, 31439 .dead, 31440 .undef, 31441 .register_overflow, 31442 .register_mask, 31443 .elementwise_regs_then_frame, 31444 .reserved_frame, 31445 => unreachable, 31446 .register, 31447 .register_pair, 31448 .register_triple, 31449 .register_quadruple, 31450 => try self.asmRegisterRegister( 31451 mir_limb_tag, 31452 dst_alias, 31453 registerAlias(src_mcv.getRegs()[dst_reg_i], limb_abi_size), 31454 ), 31455 .immediate => |imm| { 31456 assert(off == 0); 31457 switch (self.regBitSize(ty)) { 31458 8 => try self.asmRegisterImmediate( 31459 mir_limb_tag, 31460 dst_alias, 31461 if (std.math.cast(i8, @as(i64, @bitCast(imm)))) |small| 31462 .s(small) 31463 else 31464 .u(@as(u8, @intCast(imm))), 31465 ), 31466 16 => try self.asmRegisterImmediate( 31467 mir_limb_tag, 31468 dst_alias, 31469 if (std.math.cast(i16, @as(i64, @bitCast(imm)))) |small| 31470 .s(small) 31471 else 31472 .u(@as(u16, @intCast(imm))), 31473 ), 31474 32 => try self.asmRegisterImmediate( 31475 mir_limb_tag, 31476 dst_alias, 31477 if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| 31478 .s(small) 31479 else 31480 .u(@as(u32, @intCast(imm))), 31481 ), 31482 64 => if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| 31483 try self.asmRegisterImmediate(mir_limb_tag, dst_alias, .s(small)) 31484 else 31485 try self.asmRegisterRegister(mir_limb_tag, dst_alias, registerAlias( 31486 try self.copyToTmpRegister(ty, src_mcv), 31487 limb_abi_size, 31488 )), 31489 else => unreachable, 31490 } 31491 }, 31492 .eflags, 31493 .register_offset, 31494 .memory, 31495 .indirect, 31496 .load_symbol, 31497 .lea_symbol, 31498 .load_direct, 31499 .lea_direct, 31500 .load_got, 31501 .lea_got, 31502 .load_tlv, 31503 .lea_tlv, 31504 .load_frame, 31505 .lea_frame, 31506 => { 31507 direct: { 31508 try self.asmRegisterMemory(mir_limb_tag, dst_alias, switch (src_mcv) { 31509 .memory => |addr| .{ 31510 .base = .{ .reg = .ds }, 31511 .mod = .{ .rm = .{ 31512 .size = .fromSize(limb_abi_size), 31513 .disp = std.math.cast(i32, addr + off) orelse break :direct, 31514 } }, 31515 }, 31516 .indirect => |reg_off| .{ 31517 .base = .{ .reg = reg_off.reg }, 31518 .mod = .{ .rm = .{ 31519 .size = .fromSize(limb_abi_size), 31520 .disp = reg_off.off + off, 31521 } }, 31522 }, 31523 .load_frame => |frame_addr| .{ 31524 .base = .{ .frame = frame_addr.index }, 31525 .mod = .{ .rm = .{ 31526 .size = .fromSize(limb_abi_size), 31527 .disp = frame_addr.off + off, 31528 } }, 31529 }, 31530 else => break :direct, 31531 }); 31532 continue; 31533 } 31534 31535 switch (src_mcv) { 31536 .eflags, 31537 .register_offset, 31538 .lea_symbol, 31539 .lea_direct, 31540 .lea_got, 31541 .lea_tlv, 31542 .lea_frame, 31543 => { 31544 assert(off == 0); 31545 const reg = try self.copyToTmpRegister(ty, src_mcv); 31546 return self.genBinOpMir( 31547 mir_limb_tag, 31548 ty, 31549 dst_mcv, 31550 .{ .register = reg }, 31551 ); 31552 }, 31553 .memory, 31554 .load_symbol, 31555 .load_direct, 31556 .load_got, 31557 .load_tlv, 31558 => { 31559 const ptr_ty = try pt.singleConstPtrType(ty); 31560 const addr_reg = try self.copyToTmpRegister(ptr_ty, src_mcv.address()); 31561 return self.genBinOpMir(mir_limb_tag, ty, dst_mcv, .{ 31562 .indirect = .{ .reg = addr_reg, .off = off }, 31563 }); 31564 }, 31565 else => unreachable, 31566 } 31567 }, 31568 .air_ref => |src_ref| return self.genBinOpMir( 31569 mir_tag, 31570 ty, 31571 dst_mcv, 31572 try self.resolveInst(src_ref), 31573 ), 31574 } 31575 } 31576 }, 31577 .memory, .indirect, .load_symbol, .load_got, .load_direct, .load_tlv, .load_frame => { 31578 const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; 31579 const limb_abi_size: u32 = @min(abi_size, 8); 31580 31581 const dst_info: OpInfo = switch (dst_mcv) { 31582 else => unreachable, 31583 .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { 31584 const dst_addr_reg = 31585 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); 31586 const dst_addr_lock = self.register_manager.lockRegAssumeUnused(dst_addr_reg); 31587 errdefer self.register_manager.unlockReg(dst_addr_lock); 31588 31589 try self.genSetReg(dst_addr_reg, .usize, dst_mcv.address(), .{}); 31590 break :dst .{ .addr_reg = dst_addr_reg, .addr_lock = dst_addr_lock }; 31591 }, 31592 .load_frame => null, 31593 }; 31594 defer if (dst_info) |info| self.register_manager.unlockReg(info.addr_lock); 31595 31596 const resolved_src_mcv = switch (src_mcv) { 31597 else => src_mcv, 31598 .air_ref => |src_ref| try self.resolveInst(src_ref), 31599 }; 31600 const src_info: OpInfo = switch (resolved_src_mcv) { 31601 .none, 31602 .unreach, 31603 .dead, 31604 .undef, 31605 .register_overflow, 31606 .register_mask, 31607 .elementwise_regs_then_frame, 31608 .reserved_frame, 31609 .air_ref, 31610 => unreachable, 31611 .immediate, 31612 .eflags, 31613 .register, 31614 .register_pair, 31615 .register_triple, 31616 .register_quadruple, 31617 .register_offset, 31618 .indirect, 31619 .lea_direct, 31620 .lea_got, 31621 .lea_tlv, 31622 .load_frame, 31623 .lea_frame, 31624 .lea_symbol, 31625 => null, 31626 .memory, .load_symbol, .load_got, .load_direct, .load_tlv => src: { 31627 switch (resolved_src_mcv) { 31628 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr))) != null and 31629 std.math.cast(i32, @as(i64, @bitCast(addr)) + abi_size - limb_abi_size) != null) 31630 break :src null, 31631 .load_symbol, .load_got, .load_direct, .load_tlv => {}, 31632 else => unreachable, 31633 } 31634 31635 const src_addr_reg = 31636 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); 31637 const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); 31638 errdefer self.register_manager.unlockReg(src_addr_lock); 31639 31640 try self.genSetReg(src_addr_reg, .usize, resolved_src_mcv.address(), .{}); 31641 break :src .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; 31642 }, 31643 }; 31644 defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); 31645 31646 const ty_signedness = 31647 if (ty.isAbiInt(zcu)) ty.intInfo(zcu).signedness else .unsigned; 31648 const limb_ty: Type = if (abi_size <= 8) ty else switch (ty_signedness) { 31649 .signed => .usize, 31650 .unsigned => .isize, 31651 }; 31652 var limb_i: usize = 0; 31653 var off: i32 = 0; 31654 while (off < abi_size) : ({ 31655 limb_i += 1; 31656 off += 8; 31657 }) { 31658 const mir_limb_tag: Mir.Inst.FixedTag = switch (limb_i) { 31659 0 => mir_tag, 31660 else => switch (mir_tag[1]) { 31661 .add => .{ ._, .adc }, 31662 .sub, .cmp => .{ ._, .sbb }, 31663 .@"or", .@"and", .xor => mir_tag, 31664 else => return self.fail("TODO genBinOpMir implement large ABI for {s}", .{ 31665 @tagName(mir_tag[1]), 31666 }), 31667 }, 31668 }; 31669 const dst_limb_mem: Memory = switch (dst_mcv) { 31670 .memory, 31671 .load_symbol, 31672 .load_got, 31673 .load_direct, 31674 .load_tlv, 31675 => .{ 31676 .base = .{ .reg = dst_info.?.addr_reg }, 31677 .mod = .{ .rm = .{ 31678 .size = .fromSize(limb_abi_size), 31679 .disp = off, 31680 } }, 31681 }, 31682 .indirect => |reg_off| .{ 31683 .base = .{ .reg = reg_off.reg }, 31684 .mod = .{ .rm = .{ 31685 .size = .fromSize(limb_abi_size), 31686 .disp = reg_off.off + off, 31687 } }, 31688 }, 31689 .load_frame => |frame_addr| .{ 31690 .base = .{ .frame = frame_addr.index }, 31691 .mod = .{ .rm = .{ 31692 .size = .fromSize(limb_abi_size), 31693 .disp = frame_addr.off + off, 31694 } }, 31695 }, 31696 else => unreachable, 31697 }; 31698 switch (resolved_src_mcv) { 31699 .none, 31700 .unreach, 31701 .dead, 31702 .undef, 31703 .register_overflow, 31704 .register_mask, 31705 .elementwise_regs_then_frame, 31706 .reserved_frame, 31707 .air_ref, 31708 => unreachable, 31709 .immediate => |src_imm| { 31710 const imm: u64 = switch (limb_i) { 31711 0 => src_imm, 31712 else => switch (ty_signedness) { 31713 .signed => @bitCast(@as(i64, @bitCast(src_imm)) >> 63), 31714 .unsigned => 0, 31715 }, 31716 }; 31717 switch (self.regBitSize(limb_ty)) { 31718 8 => try self.asmMemoryImmediate( 31719 mir_limb_tag, 31720 dst_limb_mem, 31721 if (std.math.cast(i8, @as(i64, @bitCast(imm)))) |small| 31722 .s(small) 31723 else 31724 .u(@as(u8, @intCast(imm))), 31725 ), 31726 16 => try self.asmMemoryImmediate( 31727 mir_limb_tag, 31728 dst_limb_mem, 31729 if (std.math.cast(i16, @as(i64, @bitCast(imm)))) |small| 31730 .s(small) 31731 else 31732 .u(@as(u16, @intCast(imm))), 31733 ), 31734 32 => try self.asmMemoryImmediate( 31735 mir_limb_tag, 31736 dst_limb_mem, 31737 if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| 31738 .s(small) 31739 else 31740 .u(@as(u32, @intCast(imm))), 31741 ), 31742 64 => if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| 31743 try self.asmMemoryImmediate(mir_limb_tag, dst_limb_mem, .s(small)) 31744 else 31745 try self.asmMemoryRegister( 31746 mir_limb_tag, 31747 dst_limb_mem, 31748 registerAlias( 31749 try self.copyToTmpRegister(limb_ty, .{ .immediate = imm }), 31750 limb_abi_size, 31751 ), 31752 ), 31753 else => unreachable, 31754 } 31755 }, 31756 .register, 31757 .register_pair, 31758 .register_triple, 31759 .register_quadruple, 31760 .register_offset, 31761 .eflags, 31762 .memory, 31763 .indirect, 31764 .load_symbol, 31765 .lea_symbol, 31766 .load_direct, 31767 .lea_direct, 31768 .load_got, 31769 .lea_got, 31770 .load_tlv, 31771 .lea_tlv, 31772 .load_frame, 31773 .lea_frame, 31774 => { 31775 const src_limb_mcv: MCValue = if (src_info) |info| .{ 31776 .indirect = .{ .reg = info.addr_reg, .off = off }, 31777 } else switch (resolved_src_mcv) { 31778 .register, .register_pair, .register_triple, .register_quadruple => .{ 31779 .register = resolved_src_mcv.getRegs()[limb_i], 31780 }, 31781 .eflags, 31782 .register_offset, 31783 .lea_symbol, 31784 .lea_direct, 31785 .lea_got, 31786 .lea_tlv, 31787 .lea_frame, 31788 => switch (limb_i) { 31789 0 => resolved_src_mcv, 31790 else => .{ .immediate = 0 }, 31791 }, 31792 .memory => |addr| .{ .memory = @bitCast(@as(i64, @bitCast(addr)) + off) }, 31793 .indirect => |reg_off| .{ .indirect = .{ 31794 .reg = reg_off.reg, 31795 .off = reg_off.off + off, 31796 } }, 31797 .load_frame => |frame_addr| .{ .load_frame = .{ 31798 .index = frame_addr.index, 31799 .off = frame_addr.off + off, 31800 } }, 31801 else => unreachable, 31802 }; 31803 const src_limb_reg = if (src_limb_mcv.isRegister()) 31804 src_limb_mcv.getReg().? 31805 else 31806 try self.copyToTmpRegister(limb_ty, src_limb_mcv); 31807 try self.asmMemoryRegister( 31808 mir_limb_tag, 31809 dst_limb_mem, 31810 registerAlias(src_limb_reg, limb_abi_size), 31811 ); 31812 }, 31813 } 31814 } 31815 }, 31816 } 31817 } 31818 31819 /// Performs multi-operand integer multiplication between dst_mcv and src_mcv, storing the result in dst_mcv. 31820 /// Does not support byte-size operands. 31821 fn genIntMulComplexOpMir(self: *CodeGen, dst_ty: Type, dst_mcv: MCValue, src_mcv: MCValue) InnerError!void { 31822 const pt = self.pt; 31823 const abi_size: u32 = @intCast(dst_ty.abiSize(pt.zcu)); 31824 try self.spillEflagsIfOccupied(); 31825 switch (dst_mcv) { 31826 .none, 31827 .unreach, 31828 .dead, 31829 .undef, 31830 .immediate, 31831 .eflags, 31832 .register_offset, 31833 .register_overflow, 31834 .register_mask, 31835 .lea_symbol, 31836 .lea_direct, 31837 .lea_got, 31838 .lea_tlv, 31839 .lea_frame, 31840 .elementwise_regs_then_frame, 31841 .reserved_frame, 31842 .air_ref, 31843 => unreachable, // unmodifiable destination 31844 .register => |dst_reg| { 31845 const alias_size = switch (abi_size) { 31846 1 => 4, 31847 else => abi_size, 31848 }; 31849 const dst_alias = registerAlias(dst_reg, alias_size); 31850 const dst_lock = self.register_manager.lockReg(dst_reg); 31851 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 31852 31853 switch (abi_size) { 31854 1 => try self.asmRegisterRegister(.{ ._, .movzx }, dst_reg.to32(), dst_reg.to8()), 31855 else => {}, 31856 } 31857 31858 const resolved_src_mcv = switch (src_mcv) { 31859 else => src_mcv, 31860 .air_ref => |src_ref| try self.resolveInst(src_ref), 31861 }; 31862 switch (resolved_src_mcv) { 31863 .none, 31864 .unreach, 31865 .dead, 31866 .undef, 31867 .register_pair, 31868 .register_triple, 31869 .register_quadruple, 31870 .register_overflow, 31871 .register_mask, 31872 .elementwise_regs_then_frame, 31873 .reserved_frame, 31874 .air_ref, 31875 => unreachable, 31876 .register => |src_reg| { 31877 switch (abi_size) { 31878 1 => try self.asmRegisterRegister(.{ ._, .movzx }, src_reg.to32(), src_reg.to8()), 31879 else => {}, 31880 } 31881 try self.asmRegisterRegister( 31882 .{ .i_, .mul }, 31883 dst_alias, 31884 registerAlias(src_reg, alias_size), 31885 ); 31886 }, 31887 .immediate => |imm| { 31888 if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| { 31889 try self.asmRegisterRegisterImmediate(.{ .i_, .mul }, dst_alias, dst_alias, .s(small)); 31890 } else { 31891 const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); 31892 return self.genIntMulComplexOpMir(dst_ty, dst_mcv, MCValue{ .register = src_reg }); 31893 } 31894 }, 31895 .register_offset, 31896 .eflags, 31897 .load_symbol, 31898 .lea_symbol, 31899 .load_direct, 31900 .lea_direct, 31901 .load_got, 31902 .lea_got, 31903 .load_tlv, 31904 .lea_tlv, 31905 .lea_frame, 31906 => { 31907 const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); 31908 switch (abi_size) { 31909 1 => try self.asmRegisterRegister(.{ ._, .movzx }, src_reg.to32(), src_reg.to8()), 31910 else => {}, 31911 } 31912 try self.asmRegisterRegister(.{ .i_, .mul }, dst_alias, registerAlias(src_reg, alias_size)); 31913 }, 31914 .memory, .indirect, .load_frame => switch (abi_size) { 31915 1 => { 31916 const src_reg = try self.copyToTmpRegister(dst_ty, resolved_src_mcv); 31917 try self.asmRegisterRegister(.{ ._, .movzx }, src_reg.to32(), src_reg.to8()); 31918 try self.asmRegisterRegister(.{ .i_, .mul }, dst_alias, registerAlias(src_reg, alias_size)); 31919 }, 31920 else => try self.asmRegisterMemory( 31921 .{ .i_, .mul }, 31922 dst_alias, 31923 switch (resolved_src_mcv) { 31924 .memory => |addr| .{ 31925 .base = .{ .reg = .ds }, 31926 .mod = .{ .rm = .{ 31927 .size = .fromSize(abi_size), 31928 .disp = std.math.cast(i32, @as(i64, @bitCast(addr))) orelse 31929 return self.asmRegisterRegister( 31930 .{ .i_, .mul }, 31931 dst_alias, 31932 registerAlias( 31933 try self.copyToTmpRegister(dst_ty, resolved_src_mcv), 31934 abi_size, 31935 ), 31936 ), 31937 } }, 31938 }, 31939 .indirect => |reg_off| .{ 31940 .base = .{ .reg = reg_off.reg }, 31941 .mod = .{ .rm = .{ 31942 .size = .fromSize(abi_size), 31943 .disp = reg_off.off, 31944 } }, 31945 }, 31946 .load_frame => |frame_addr| .{ 31947 .base = .{ .frame = frame_addr.index }, 31948 .mod = .{ .rm = .{ 31949 .size = .fromSize(abi_size), 31950 .disp = frame_addr.off, 31951 } }, 31952 }, 31953 else => unreachable, 31954 }, 31955 ), 31956 }, 31957 } 31958 }, 31959 .register_pair, .register_triple, .register_quadruple => unreachable, // unimplemented 31960 .memory, .indirect, .load_symbol, .load_direct, .load_got, .load_tlv, .load_frame => { 31961 const tmp_reg = try self.copyToTmpRegister(dst_ty, dst_mcv); 31962 const tmp_mcv = MCValue{ .register = tmp_reg }; 31963 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 31964 defer self.register_manager.unlockReg(tmp_lock); 31965 31966 try self.genIntMulComplexOpMir(dst_ty, tmp_mcv, src_mcv); 31967 try self.genCopy(dst_ty, dst_mcv, tmp_mcv, .{}); 31968 }, 31969 } 31970 } 31971 31972 fn airArg(self: *CodeGen, inst: Air.Inst.Index) !void { 31973 const pt = self.pt; 31974 const zcu = pt.zcu; 31975 // skip zero-bit arguments as they don't have a corresponding arg instruction 31976 var arg_index = self.arg_index; 31977 while (self.args[arg_index] == .none) arg_index += 1; 31978 self.arg_index = arg_index + 1; 31979 31980 const result: MCValue = if (self.debug_output == .none and self.liveness.isUnused(inst)) .unreach else result: { 31981 const arg_ty = self.typeOfIndex(inst); 31982 const src_mcv = self.args[arg_index]; 31983 switch (src_mcv) { 31984 .register, .register_pair, .load_frame => { 31985 for (src_mcv.getRegs()) |reg| self.register_manager.getRegAssumeFree(reg, inst); 31986 break :result src_mcv; 31987 }, 31988 .indirect => |reg_off| { 31989 self.register_manager.getRegAssumeFree(reg_off.reg, inst); 31990 const dst_mcv = try self.allocRegOrMem(inst, false); 31991 try self.genCopy(arg_ty, dst_mcv, src_mcv, .{}); 31992 break :result dst_mcv; 31993 }, 31994 .elementwise_regs_then_frame => |regs_frame_addr| { 31995 try self.spillEflagsIfOccupied(); 31996 31997 const fn_info = zcu.typeToFunc(self.fn_type).?; 31998 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc); 31999 var prev_reg: Register = undefined; 32000 for ( 32001 param_int_regs[param_int_regs.len - regs_frame_addr.regs ..], 32002 0.., 32003 ) |dst_reg, elem_index| { 32004 assert(self.register_manager.isRegFree(dst_reg)); 32005 if (elem_index > 0) { 32006 try self.asmRegisterImmediate(.{ ._l, .sh }, dst_reg.to8(), .u(elem_index)); 32007 try self.asmRegisterRegister( 32008 .{ ._, .@"or" }, 32009 dst_reg.to8(), 32010 prev_reg.to8(), 32011 ); 32012 } 32013 prev_reg = dst_reg; 32014 } 32015 32016 const prev_lock = if (regs_frame_addr.regs > 0) 32017 self.register_manager.lockRegAssumeUnused(prev_reg) 32018 else 32019 null; 32020 defer if (prev_lock) |lock| self.register_manager.unlockReg(lock); 32021 32022 const dst_mcv = try self.allocRegOrMem(inst, false); 32023 if (regs_frame_addr.regs > 0) try self.asmMemoryRegister( 32024 .{ ._, .mov }, 32025 try dst_mcv.mem(self, .{ .size = .byte }), 32026 prev_reg.to8(), 32027 ); 32028 try self.genInlineMemset( 32029 dst_mcv.address().offset(@intFromBool(regs_frame_addr.regs > 0)), 32030 .{ .immediate = 0 }, 32031 .{ .immediate = arg_ty.abiSize(zcu) - @intFromBool(regs_frame_addr.regs > 0) }, 32032 .{}, 32033 ); 32034 32035 const index_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 32036 const index_lock = self.register_manager.lockRegAssumeUnused(index_reg); 32037 defer self.register_manager.unlockReg(index_lock); 32038 32039 try self.asmRegisterImmediate( 32040 .{ ._, .mov }, 32041 index_reg.to32(), 32042 .u(regs_frame_addr.regs), 32043 ); 32044 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 32045 try self.asmMemoryImmediate(.{ ._, .cmp }, .{ 32046 .base = .{ .frame = regs_frame_addr.frame_index }, 32047 .mod = .{ .rm = .{ 32048 .size = .byte, 32049 .index = index_reg.to64(), 32050 .scale = .@"8", 32051 .disp = regs_frame_addr.frame_off - @as(u6, regs_frame_addr.regs) * 8, 32052 } }, 32053 }, Immediate.u(0)); 32054 const unset = try self.asmJccReloc(.e, undefined); 32055 try self.asmMemoryRegister( 32056 .{ ._s, .bt }, 32057 try dst_mcv.mem(self, .{ .size = .dword }), 32058 index_reg.to32(), 32059 ); 32060 self.performReloc(unset); 32061 if (self.hasFeature(.slow_incdec)) { 32062 try self.asmRegisterImmediate(.{ ._, .add }, index_reg.to32(), .u(1)); 32063 } else { 32064 try self.asmRegister(.{ ._c, .in }, index_reg.to32()); 32065 } 32066 try self.asmRegisterImmediate( 32067 .{ ._, .cmp }, 32068 index_reg.to32(), 32069 .u(arg_ty.vectorLen(zcu)), 32070 ); 32071 _ = try self.asmJccReloc(.b, loop); 32072 32073 break :result dst_mcv; 32074 }, 32075 else => return self.fail("TODO implement arg for {}", .{src_mcv}), 32076 } 32077 }; 32078 return self.finishAir(inst, result, .{ .none, .none, .none }); 32079 } 32080 32081 fn airDbgVarArgs(self: *CodeGen) !void { 32082 if (self.debug_output == .none) return; 32083 if (!self.pt.zcu.typeToFunc(self.fn_type).?.is_var_args) return; 32084 try self.asmPseudo(.pseudo_dbg_var_args_none); 32085 } 32086 32087 fn genLocalDebugInfo( 32088 self: *CodeGen, 32089 inst: Air.Inst.Index, 32090 mcv: MCValue, 32091 ) !void { 32092 if (self.debug_output == .none) return; 32093 switch (self.air.instructions.items(.tag)[@intFromEnum(inst)]) { 32094 else => unreachable, 32095 .arg, .dbg_arg_inline, .dbg_var_val => |tag| { 32096 switch (mcv) { 32097 .none => try self.asmAir(.dbg_local, inst), 32098 .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable, 32099 .immediate => |imm| try self.asmAirImmediate(.dbg_local, inst, .u(imm)), 32100 .lea_frame => |frame_addr| try self.asmAirFrameAddress(.dbg_local, inst, frame_addr), 32101 .lea_symbol => |sym_off| try self.asmAirImmediate(.dbg_local, inst, .rel(sym_off)), 32102 else => { 32103 const ty = switch (tag) { 32104 else => unreachable, 32105 .arg => self.typeOfIndex(inst), 32106 .dbg_arg_inline, .dbg_var_val => self.typeOf( 32107 self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op.operand, 32108 ), 32109 }; 32110 const frame_index = try self.allocFrameIndex(.initSpill(ty, self.pt.zcu)); 32111 try self.genSetMem(.{ .frame = frame_index }, 0, ty, mcv, .{}); 32112 try self.asmAirMemory(.dbg_local, inst, .{ 32113 .base = .{ .frame = frame_index }, 32114 .mod = .{ .rm = .{ .size = .qword } }, 32115 }); 32116 }, 32117 } 32118 }, 32119 .dbg_var_ptr => switch (mcv) { 32120 else => unreachable, 32121 .unreach, .dead, .elementwise_regs_then_frame, .reserved_frame, .air_ref => unreachable, 32122 .lea_frame => |frame_addr| try self.asmAirMemory(.dbg_local, inst, .{ 32123 .base = .{ .frame = frame_addr.index }, 32124 .mod = .{ .rm = .{ 32125 .size = .qword, 32126 .disp = frame_addr.off, 32127 } }, 32128 }), 32129 .lea_symbol => |sym_off| try self.asmAirMemory(.dbg_local, inst, .{ 32130 .base = .{ .reloc = sym_off.sym_index }, 32131 .mod = .{ .rm = .{ 32132 .size = .qword, 32133 .disp = sym_off.off, 32134 } }, 32135 }), 32136 .lea_direct, .lea_got, .lea_tlv => |sym_index| try self.asmAirMemory(.dbg_local, inst, .{ 32137 .base = .{ .reloc = sym_index }, 32138 .mod = .{ .rm = .{ .size = .qword } }, 32139 }), 32140 }, 32141 } 32142 } 32143 32144 fn airRetAddr(self: *CodeGen, inst: Air.Inst.Index) !void { 32145 const dst_mcv = try self.allocRegOrMem(inst, true); 32146 try self.genCopy(.usize, dst_mcv, .{ .load_frame = .{ .index = .ret_addr } }, .{}); 32147 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none }); 32148 } 32149 32150 fn airFrameAddress(self: *CodeGen, inst: Air.Inst.Index) !void { 32151 const dst_mcv = try self.allocRegOrMem(inst, true); 32152 try self.genCopy(.usize, dst_mcv, .{ .lea_frame = .{ .index = .base_ptr } }, .{}); 32153 return self.finishAir(inst, dst_mcv, .{ .none, .none, .none }); 32154 } 32155 32156 fn airCall(self: *CodeGen, inst: Air.Inst.Index, modifier: std.builtin.CallModifier, opts: CopyOptions) !void { 32157 if (modifier == .always_tail) return self.fail("TODO implement tail calls for x86_64", .{}); 32158 32159 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 32160 const extra = self.air.extraData(Air.Call, pl_op.payload); 32161 const arg_refs: []const Air.Inst.Ref = 32162 @ptrCast(self.air.extra[extra.end..][0..extra.data.args_len]); 32163 32164 const ExpectedContents = extern struct { 32165 tys: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), 32166 vals: [16][@sizeOf(MCValue)]u8 align(@alignOf(MCValue)), 32167 }; 32168 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 32169 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 32170 const allocator = stack.get(); 32171 32172 const arg_tys = try allocator.alloc(Type, arg_refs.len); 32173 defer allocator.free(arg_tys); 32174 for (arg_tys, arg_refs) |*arg_ty, arg_ref| arg_ty.* = self.typeOf(arg_ref); 32175 32176 const arg_vals = try allocator.alloc(MCValue, arg_refs.len); 32177 defer allocator.free(arg_vals); 32178 for (arg_vals, arg_refs) |*arg_val, arg_ref| arg_val.* = .{ .air_ref = arg_ref }; 32179 32180 const ret = try self.genCall(.{ .air = pl_op.operand }, arg_tys, arg_vals, opts); 32181 32182 var bt = self.liveness.iterateBigTomb(inst); 32183 try self.feed(&bt, pl_op.operand); 32184 for (arg_refs) |arg_ref| try self.feed(&bt, arg_ref); 32185 32186 const result = if (self.liveness.isUnused(inst)) .unreach else ret; 32187 return self.finishAirResult(inst, result); 32188 } 32189 32190 fn genCall(self: *CodeGen, info: union(enum) { 32191 air: Air.Inst.Ref, 32192 lib: struct { 32193 return_type: InternPool.Index, 32194 param_types: []const InternPool.Index, 32195 lib: ?[]const u8 = null, 32196 callee: []const u8, 32197 }, 32198 }, arg_types: []const Type, args: []const MCValue, opts: CopyOptions) !MCValue { 32199 const pt = self.pt; 32200 const zcu = pt.zcu; 32201 const ip = &zcu.intern_pool; 32202 32203 const fn_ty = switch (info) { 32204 .air => |callee| fn_info: { 32205 const callee_ty = self.typeOf(callee); 32206 break :fn_info switch (callee_ty.zigTypeTag(zcu)) { 32207 .@"fn" => callee_ty, 32208 .pointer => callee_ty.childType(zcu), 32209 else => unreachable, 32210 }; 32211 }, 32212 .lib => |lib| try pt.funcType(.{ 32213 .param_types = lib.param_types, 32214 .return_type = lib.return_type, 32215 .cc = self.target.cCallingConvention().?, 32216 }), 32217 }; 32218 const fn_info = zcu.typeToFunc(fn_ty).?; 32219 32220 const ExpectedContents = extern struct { 32221 var_args: [16][@sizeOf(Type)]u8 align(@alignOf(Type)), 32222 frame_indices: [16]FrameIndex, 32223 reg_locks: [16][@sizeOf(?RegisterLock)]u8 align(@alignOf(?RegisterLock)), 32224 }; 32225 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 32226 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 32227 const allocator = stack.get(); 32228 32229 const var_args = try allocator.alloc(Type, args.len - fn_info.param_types.len); 32230 defer allocator.free(var_args); 32231 for (var_args, arg_types[fn_info.param_types.len..]) |*var_arg, arg_ty| var_arg.* = arg_ty; 32232 32233 const frame_indices = try allocator.alloc(FrameIndex, args.len); 32234 defer allocator.free(frame_indices); 32235 32236 var reg_locks: std.ArrayList(?RegisterLock) = .init(allocator); 32237 defer reg_locks.deinit(); 32238 try reg_locks.ensureTotalCapacity(16); 32239 defer for (reg_locks.items) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); 32240 32241 var call_info = try self.resolveCallingConventionValues(fn_info, var_args, .call_frame); 32242 defer call_info.deinit(self); 32243 32244 // We need a properly aligned and sized call frame to be able to call this function. 32245 { 32246 const needed_call_frame: FrameAlloc = .init(.{ 32247 .size = call_info.stack_byte_count, 32248 .alignment = call_info.stack_align, 32249 }); 32250 const frame_allocs_slice = self.frame_allocs.slice(); 32251 const stack_frame_size = 32252 &frame_allocs_slice.items(.abi_size)[@intFromEnum(FrameIndex.call_frame)]; 32253 stack_frame_size.* = @max(stack_frame_size.*, needed_call_frame.abi_size); 32254 const stack_frame_align = 32255 &frame_allocs_slice.items(.abi_align)[@intFromEnum(FrameIndex.call_frame)]; 32256 stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align); 32257 } 32258 32259 try self.spillEflagsIfOccupied(); 32260 try self.spillCallerPreservedRegs(fn_info.cc, call_info.err_ret_trace_reg); 32261 32262 // set stack arguments first because this can clobber registers 32263 // also clobber spill arguments as we go 32264 switch (call_info.return_value.long) { 32265 .none, .unreach => {}, 32266 .indirect => |reg_off| try self.register_manager.getReg(reg_off.reg, null), 32267 else => unreachable, 32268 } 32269 for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, *frame_index| 32270 switch (dst_arg) { 32271 .none => {}, 32272 .register => |reg| { 32273 try self.register_manager.getReg(reg, null); 32274 try reg_locks.append(self.register_manager.lockReg(reg)); 32275 }, 32276 .register_pair => |regs| { 32277 for (regs) |reg| try self.register_manager.getReg(reg, null); 32278 try reg_locks.appendSlice(&self.register_manager.lockRegs(2, regs)); 32279 }, 32280 .indirect => |reg_off| { 32281 frame_index.* = try self.allocFrameIndex(.initType(arg_ty, zcu)); 32282 try self.genSetMem(.{ .frame = frame_index.* }, 0, arg_ty, src_arg, opts); 32283 try self.register_manager.getReg(reg_off.reg, null); 32284 try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); 32285 }, 32286 .load_frame => { 32287 try self.genCopy(arg_ty, dst_arg, src_arg, opts); 32288 try self.freeValue(src_arg); 32289 }, 32290 .elementwise_regs_then_frame => |regs_frame_addr| { 32291 const index_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 32292 const index_lock = self.register_manager.lockRegAssumeUnused(index_reg); 32293 defer self.register_manager.unlockReg(index_lock); 32294 32295 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(self, .{ .size = .dword }) else .{ 32296 .base = .{ .reg = try self.copyToTmpRegister(.usize, switch (src_arg) { 32297 else => src_arg, 32298 .air_ref => |src_ref| try self.resolveInst(src_ref), 32299 }.address()) }, 32300 .mod = .{ .rm = .{ .size = .dword } }, 32301 }; 32302 const src_lock = switch (src_mem.base) { 32303 .reg => |src_reg| self.register_manager.lockReg(src_reg), 32304 else => null, 32305 }; 32306 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 32307 32308 try self.asmRegisterImmediate( 32309 .{ ._, .mov }, 32310 index_reg.to32(), 32311 .u(regs_frame_addr.regs), 32312 ); 32313 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 32314 try self.asmMemoryRegister(.{ ._, .bt }, src_mem, index_reg.to32()); 32315 try self.asmSetccMemory(.c, .{ 32316 .base = .{ .frame = regs_frame_addr.frame_index }, 32317 .mod = .{ .rm = .{ 32318 .size = .byte, 32319 .index = index_reg.to64(), 32320 .scale = .@"8", 32321 .disp = regs_frame_addr.frame_off - @as(u6, regs_frame_addr.regs) * 8, 32322 } }, 32323 }); 32324 if (self.hasFeature(.slow_incdec)) { 32325 try self.asmRegisterImmediate(.{ ._, .add }, index_reg.to32(), .u(1)); 32326 } else { 32327 try self.asmRegister(.{ ._c, .in }, index_reg.to32()); 32328 } 32329 try self.asmRegisterImmediate( 32330 .{ ._, .cmp }, 32331 index_reg.to32(), 32332 .u(arg_ty.vectorLen(zcu)), 32333 ); 32334 _ = try self.asmJccReloc(.b, loop); 32335 32336 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc); 32337 for (param_int_regs[param_int_regs.len - regs_frame_addr.regs ..]) |dst_reg| { 32338 try self.register_manager.getReg(dst_reg, null); 32339 try reg_locks.append(self.register_manager.lockReg(dst_reg)); 32340 } 32341 }, 32342 else => unreachable, 32343 }; 32344 32345 if (call_info.err_ret_trace_reg != .none) { 32346 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| { 32347 if (switch (err_ret_trace.short) { 32348 .register => |reg| call_info.err_ret_trace_reg != reg, 32349 else => true, 32350 }) { 32351 try self.register_manager.getReg(call_info.err_ret_trace_reg, err_ret_trace_index); 32352 try reg_locks.append(self.register_manager.lockReg(call_info.err_ret_trace_reg)); 32353 32354 try self.genSetReg(call_info.err_ret_trace_reg, .usize, err_ret_trace.short, .{}); 32355 err_ret_trace.trackMaterialize(err_ret_trace_index, .{ 32356 .long = err_ret_trace.long, 32357 .short = .{ .register = call_info.err_ret_trace_reg }, 32358 }); 32359 } 32360 } 32361 } 32362 32363 // now we are free to set register arguments 32364 switch (call_info.return_value.long) { 32365 .none, .unreach => {}, 32366 .indirect => |reg_off| { 32367 const ret_ty: Type = .fromInterned(fn_info.return_type); 32368 const frame_index = try self.allocFrameIndex(.initSpill(ret_ty, zcu)); 32369 try self.genSetReg(reg_off.reg, .usize, .{ 32370 .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, 32371 }, .{}); 32372 call_info.return_value.short = .{ .load_frame = .{ .index = frame_index } }; 32373 try reg_locks.append(self.register_manager.lockReg(reg_off.reg)); 32374 }, 32375 else => unreachable, 32376 } 32377 32378 for (call_info.args, arg_types, args, frame_indices) |dst_arg, arg_ty, src_arg, frame_index| 32379 switch (dst_arg) { 32380 .none, .load_frame => {}, 32381 .register => |dst_reg| switch (fn_info.cc) { 32382 else => try self.genSetReg(registerAlias( 32383 dst_reg, 32384 @intCast(arg_ty.abiSize(zcu)), 32385 ), arg_ty, src_arg, opts), 32386 .x86_64_sysv, .x86_64_win => { 32387 const promoted_ty = self.promoteInt(arg_ty); 32388 const promoted_abi_size: u32 = @intCast(promoted_ty.abiSize(zcu)); 32389 const dst_alias = registerAlias(dst_reg, promoted_abi_size); 32390 try self.genSetReg(dst_alias, promoted_ty, src_arg, opts); 32391 if (promoted_ty.toIntern() != arg_ty.toIntern()) 32392 try self.truncateRegister(arg_ty, dst_alias); 32393 }, 32394 }, 32395 .register_pair => try self.genCopy(arg_ty, dst_arg, src_arg, opts), 32396 .indirect => |reg_off| try self.genSetReg(reg_off.reg, .usize, .{ 32397 .lea_frame = .{ .index = frame_index, .off = -reg_off.off }, 32398 }, .{}), 32399 .elementwise_regs_then_frame => |regs_frame_addr| { 32400 const src_mem: Memory = if (src_arg.isBase()) try src_arg.mem(self, .{ .size = .dword }) else .{ 32401 .base = .{ .reg = try self.copyToTmpRegister( 32402 .usize, 32403 switch (src_arg) { 32404 else => src_arg, 32405 .air_ref => |src_ref| try self.resolveInst(src_ref), 32406 }.address(), 32407 ) }, 32408 .mod = .{ .rm = .{ .size = .dword } }, 32409 }; 32410 const src_lock = switch (src_mem.base) { 32411 .reg => |src_reg| self.register_manager.lockReg(src_reg), 32412 else => null, 32413 }; 32414 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 32415 32416 const param_int_regs = abi.getCAbiIntParamRegs(fn_info.cc); 32417 for ( 32418 param_int_regs[param_int_regs.len - regs_frame_addr.regs ..], 32419 0.., 32420 ) |dst_reg, elem_index| { 32421 try self.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32()); 32422 try self.asmMemoryImmediate(.{ ._, .bt }, src_mem, .u(elem_index)); 32423 try self.asmSetccRegister(.c, dst_reg.to8()); 32424 } 32425 }, 32426 else => unreachable, 32427 }; 32428 32429 if (fn_info.is_var_args) try self.asmRegisterImmediate(.{ ._, .mov }, .al, .u(call_info.fp_count)); 32430 32431 // Due to incremental compilation, how function calls are generated depends 32432 // on linking. 32433 switch (info) { 32434 .air => |callee| if (try self.air.value(callee, pt)) |func_value| { 32435 const func_key = ip.indexToKey(func_value.ip_index); 32436 switch (switch (func_key) { 32437 else => func_key, 32438 .ptr => |ptr| if (ptr.byte_offset == 0) switch (ptr.base_addr) { 32439 .nav => |nav| ip.indexToKey(zcu.navValue(nav).toIntern()), 32440 else => func_key, 32441 } else func_key, 32442 }) { 32443 .func => |func| { 32444 if (self.bin_file.cast(.elf)) |elf_file| { 32445 const zo = elf_file.zigObjectPtr().?; 32446 const sym_index = try zo.getOrCreateMetadataForNav(zcu, func.owner_nav); 32447 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym_index })); 32448 } else if (self.bin_file.cast(.coff)) |coff_file| { 32449 const atom = try coff_file.getOrCreateAtomForNav(func.owner_nav); 32450 const sym_index = coff_file.getAtom(atom).getSymbolIndex().?; 32451 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc); 32452 try self.genSetReg(scratch_reg, .usize, .{ .lea_got = sym_index }, .{}); 32453 try self.asmRegister(.{ ._, .call }, scratch_reg); 32454 } else if (self.bin_file.cast(.macho)) |macho_file| { 32455 const zo = macho_file.getZigObject().?; 32456 const sym_index = try zo.getOrCreateMetadataForNav(macho_file, func.owner_nav); 32457 const sym = zo.symbols.items[sym_index]; 32458 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym.nlist_idx })); 32459 } else if (self.bin_file.cast(.plan9)) |p9| { 32460 const atom_index = try p9.seeNav(pt, func.owner_nav); 32461 const atom = p9.getAtom(atom_index); 32462 try self.asmMemory(.{ ._, .call }, .{ 32463 .base = .{ .reg = .ds }, 32464 .mod = .{ .rm = .{ 32465 .size = .qword, 32466 .disp = @intCast(atom.getOffsetTableAddress(p9)), 32467 } }, 32468 }); 32469 } else unreachable; 32470 }, 32471 .@"extern" => |@"extern"| if (self.bin_file.cast(.elf)) |elf_file| { 32472 const target_sym_index = try elf_file.getGlobalSymbol( 32473 @"extern".name.toSlice(ip), 32474 @"extern".lib_name.toSlice(ip), 32475 ); 32476 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); 32477 } else if (self.bin_file.cast(.macho)) |macho_file| { 32478 const target_sym_index = try macho_file.getGlobalSymbol( 32479 @"extern".name.toSlice(ip), 32480 @"extern".lib_name.toSlice(ip), 32481 ); 32482 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); 32483 } else try self.genExternSymbolRef( 32484 .call, 32485 @"extern".lib_name.toSlice(ip), 32486 @"extern".name.toSlice(ip), 32487 ), 32488 else => return self.fail("TODO implement calling bitcasted functions", .{}), 32489 } 32490 } else { 32491 assert(self.typeOf(callee).zigTypeTag(zcu) == .pointer); 32492 const scratch_reg = abi.getCAbiLinkerScratchReg(fn_info.cc); 32493 try self.genSetReg(scratch_reg, .usize, .{ .air_ref = callee }, .{}); 32494 try self.asmRegister(.{ ._, .call }, scratch_reg); 32495 }, 32496 .lib => |lib| if (self.bin_file.cast(.elf)) |elf_file| { 32497 const target_sym_index = try elf_file.getGlobalSymbol(lib.callee, lib.lib); 32498 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); 32499 } else if (self.bin_file.cast(.macho)) |macho_file| { 32500 const target_sym_index = try macho_file.getGlobalSymbol(lib.callee, lib.lib); 32501 try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = target_sym_index })); 32502 } else try self.genExternSymbolRef(.call, lib.lib, lib.callee), 32503 } 32504 return call_info.return_value.short; 32505 } 32506 32507 fn airRet(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { 32508 const pt = self.pt; 32509 const zcu = pt.zcu; 32510 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 32511 32512 const ret_ty = self.fn_type.fnReturnType(zcu); 32513 switch (self.ret_mcv.short) { 32514 .none => {}, 32515 .register => |reg| { 32516 const reg_lock = self.register_manager.lockRegAssumeUnused(reg); 32517 defer self.register_manager.unlockReg(reg_lock); 32518 try self.genCopy(ret_ty, self.ret_mcv.short, .{ .air_ref = un_op }, .{ .safety = safety }); 32519 }, 32520 inline .register_pair, .register_triple, .register_quadruple => |regs| { 32521 const reg_locks = self.register_manager.lockRegsAssumeUnused(regs.len, regs); 32522 defer for (reg_locks) |reg_lock| self.register_manager.unlockReg(reg_lock); 32523 try self.genCopy(ret_ty, self.ret_mcv.short, .{ .air_ref = un_op }, .{ .safety = safety }); 32524 }, 32525 .indirect => |reg_off| { 32526 try self.register_manager.getReg(reg_off.reg, null); 32527 const lock = self.register_manager.lockRegAssumeUnused(reg_off.reg); 32528 defer self.register_manager.unlockReg(lock); 32529 32530 try self.genSetReg(reg_off.reg, .usize, self.ret_mcv.long, .{}); 32531 try self.genSetMem( 32532 .{ .reg = reg_off.reg }, 32533 reg_off.off, 32534 ret_ty, 32535 .{ .air_ref = un_op }, 32536 .{ .safety = safety }, 32537 ); 32538 }, 32539 else => unreachable, 32540 } 32541 self.ret_mcv.liveOut(self, inst); 32542 32543 if (self.err_ret_trace_reg != .none) { 32544 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| { 32545 if (switch (err_ret_trace.short) { 32546 .register => |reg| self.err_ret_trace_reg != reg, 32547 else => true, 32548 }) try self.genSetReg(self.err_ret_trace_reg, .usize, err_ret_trace.short, .{}); 32549 err_ret_trace.liveOut(self, err_ret_trace_index); 32550 } 32551 } 32552 32553 try self.finishAir(inst, .unreach, .{ un_op, .none, .none }); 32554 32555 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction 32556 // which is available if the jump is 127 bytes or less forward. 32557 const jmp_reloc = try self.asmJmpReloc(undefined); 32558 try self.epilogue_relocs.append(self.gpa, jmp_reloc); 32559 } 32560 32561 fn airRetLoad(self: *CodeGen, inst: Air.Inst.Index) !void { 32562 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 32563 const ptr = try self.resolveInst(un_op); 32564 32565 const ptr_ty = self.typeOf(un_op); 32566 switch (self.ret_mcv.short) { 32567 .none => {}, 32568 .register, .register_pair => try self.load(self.ret_mcv.short, ptr_ty, ptr), 32569 .indirect => |reg_off| try self.genSetReg(reg_off.reg, ptr_ty, ptr, .{}), 32570 else => unreachable, 32571 } 32572 self.ret_mcv.liveOut(self, inst); 32573 32574 if (self.err_ret_trace_reg != .none) { 32575 if (self.inst_tracking.getPtr(err_ret_trace_index)) |err_ret_trace| { 32576 if (switch (err_ret_trace.short) { 32577 .register => |reg| self.err_ret_trace_reg != reg, 32578 else => true, 32579 }) try self.genSetReg(self.err_ret_trace_reg, .usize, err_ret_trace.short, .{}); 32580 err_ret_trace.liveOut(self, err_ret_trace_index); 32581 } 32582 } 32583 32584 try self.finishAir(inst, .unreach, .{ un_op, .none, .none }); 32585 32586 // TODO optimization opportunity: figure out when we can emit this as a 2 byte instruction 32587 // which is available if the jump is 127 bytes or less forward. 32588 const jmp_reloc = try self.asmJmpReloc(undefined); 32589 try self.epilogue_relocs.append(self.gpa, jmp_reloc); 32590 } 32591 32592 fn airCmp(self: *CodeGen, inst: Air.Inst.Index, op: std.math.CompareOperator) !void { 32593 const pt = self.pt; 32594 const zcu = pt.zcu; 32595 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 32596 var ty = self.typeOf(bin_op.lhs); 32597 var null_compare: ?Mir.Inst.Index = null; 32598 32599 const result: Condition = result: { 32600 try self.spillEflagsIfOccupied(); 32601 32602 const lhs_mcv = try self.resolveInst(bin_op.lhs); 32603 const lhs_locks: [2]?RegisterLock = switch (lhs_mcv) { 32604 .register => |lhs_reg| .{ self.register_manager.lockRegAssumeUnused(lhs_reg), null }, 32605 .register_pair => |lhs_regs| locks: { 32606 const locks = self.register_manager.lockRegsAssumeUnused(2, lhs_regs); 32607 break :locks .{ locks[0], locks[1] }; 32608 }, 32609 .register_offset => |lhs_ro| .{ 32610 self.register_manager.lockRegAssumeUnused(lhs_ro.reg), 32611 null, 32612 }, 32613 else => @splat(null), 32614 }; 32615 defer for (lhs_locks) |lhs_lock| if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 32616 32617 const rhs_mcv = try self.resolveInst(bin_op.rhs); 32618 const rhs_locks: [2]?RegisterLock = switch (rhs_mcv) { 32619 .register => |rhs_reg| .{ self.register_manager.lockReg(rhs_reg), null }, 32620 .register_pair => |rhs_regs| self.register_manager.lockRegs(2, rhs_regs), 32621 .register_offset => |rhs_ro| .{ self.register_manager.lockReg(rhs_ro.reg), null }, 32622 else => @splat(null), 32623 }; 32624 defer for (rhs_locks) |rhs_lock| if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 32625 32626 switch (ty.zigTypeTag(zcu)) { 32627 .float => { 32628 const float_bits = ty.floatBits(self.target.*); 32629 if (!switch (float_bits) { 32630 16 => self.hasFeature(.f16c), 32631 32 => self.hasFeature(.sse), 32632 64 => self.hasFeature(.sse2), 32633 80, 128 => false, 32634 else => unreachable, 32635 }) { 32636 var callee_buf: ["__???f2".len]u8 = undefined; 32637 const ret = try self.genCall(.{ .lib = .{ 32638 .return_type = .i32_type, 32639 .param_types = &.{ ty.toIntern(), ty.toIntern() }, 32640 .callee = std.fmt.bufPrint(&callee_buf, "__{s}{c}f2", .{ 32641 switch (op) { 32642 .eq => "eq", 32643 .neq => "ne", 32644 .lt => "lt", 32645 .lte => "le", 32646 .gt => "gt", 32647 .gte => "ge", 32648 }, 32649 floatCompilerRtAbiName(float_bits), 32650 }) catch unreachable, 32651 } }, &.{ ty, ty }, &.{ .{ .air_ref = bin_op.lhs }, .{ .air_ref = bin_op.rhs } }, .{}); 32652 try self.genBinOpMir(.{ ._, .@"test" }, .i32, ret, ret); 32653 break :result switch (op) { 32654 .eq => .e, 32655 .neq => .ne, 32656 .lt => .l, 32657 .lte => .le, 32658 .gt => .g, 32659 .gte => .ge, 32660 }; 32661 } 32662 }, 32663 .optional => if (!ty.optionalReprIsPayload(zcu)) { 32664 const opt_ty = ty; 32665 const opt_abi_size: u31 = @intCast(opt_ty.abiSize(zcu)); 32666 ty = opt_ty.optionalChild(zcu); 32667 const payload_abi_size: u31 = @intCast(ty.abiSize(zcu)); 32668 32669 const temp_lhs_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 32670 const temp_lhs_lock = self.register_manager.lockRegAssumeUnused(temp_lhs_reg); 32671 defer self.register_manager.unlockReg(temp_lhs_lock); 32672 32673 if (lhs_mcv.isBase()) try self.asmRegisterMemory( 32674 .{ ._, .mov }, 32675 temp_lhs_reg.to8(), 32676 try lhs_mcv.address().offset(payload_abi_size).deref().mem(self, .{ .size = .byte }), 32677 ) else { 32678 try self.genSetReg(temp_lhs_reg, opt_ty, lhs_mcv, .{}); 32679 try self.asmRegisterImmediate( 32680 .{ ._r, .sh }, 32681 registerAlias(temp_lhs_reg, opt_abi_size), 32682 .u(payload_abi_size * 8), 32683 ); 32684 } 32685 32686 const payload_compare = payload_compare: { 32687 if (rhs_mcv.isBase()) { 32688 const rhs_mem = 32689 try rhs_mcv.address().offset(payload_abi_size).deref().mem(self, .{ .size = .byte }); 32690 try self.asmMemoryRegister(.{ ._, .@"test" }, rhs_mem, temp_lhs_reg.to8()); 32691 const payload_compare = try self.asmJccReloc(.nz, undefined); 32692 try self.asmRegisterMemory(.{ ._, .cmp }, temp_lhs_reg.to8(), rhs_mem); 32693 break :payload_compare payload_compare; 32694 } 32695 32696 const temp_rhs_reg = try self.copyToTmpRegister(opt_ty, rhs_mcv); 32697 const temp_rhs_lock = self.register_manager.lockRegAssumeUnused(temp_rhs_reg); 32698 defer self.register_manager.unlockReg(temp_rhs_lock); 32699 32700 try self.asmRegisterImmediate( 32701 .{ ._r, .sh }, 32702 registerAlias(temp_rhs_reg, opt_abi_size), 32703 .u(payload_abi_size * 8), 32704 ); 32705 try self.asmRegisterRegister( 32706 .{ ._, .@"test" }, 32707 temp_lhs_reg.to8(), 32708 temp_rhs_reg.to8(), 32709 ); 32710 const payload_compare = try self.asmJccReloc(.nz, undefined); 32711 try self.asmRegisterRegister( 32712 .{ ._, .cmp }, 32713 temp_lhs_reg.to8(), 32714 temp_rhs_reg.to8(), 32715 ); 32716 break :payload_compare payload_compare; 32717 }; 32718 null_compare = try self.asmJmpReloc(undefined); 32719 self.performReloc(payload_compare); 32720 }, 32721 else => {}, 32722 } 32723 32724 switch (ty.zigTypeTag(zcu)) { 32725 else => { 32726 const abi_size: u16 = @intCast(ty.abiSize(zcu)); 32727 const may_flip: enum { 32728 may_flip, 32729 must_flip, 32730 must_not_flip, 32731 } = if (abi_size > 8) switch (op) { 32732 .lt, .gte => .must_not_flip, 32733 .lte, .gt => .must_flip, 32734 .eq, .neq => .may_flip, 32735 } else .may_flip; 32736 32737 const flipped = switch (may_flip) { 32738 .may_flip => !lhs_mcv.isRegister() and !lhs_mcv.isBase(), 32739 .must_flip => true, 32740 .must_not_flip => false, 32741 }; 32742 const unmat_dst_mcv = if (flipped) rhs_mcv else lhs_mcv; 32743 const dst_mcv = if (unmat_dst_mcv.isRegister() or 32744 (abi_size <= 8 and unmat_dst_mcv.isBase())) unmat_dst_mcv else dst: { 32745 const dst_mcv = try self.allocTempRegOrMem(ty, true); 32746 try self.genCopy(ty, dst_mcv, unmat_dst_mcv, .{}); 32747 break :dst dst_mcv; 32748 }; 32749 const dst_lock = 32750 if (dst_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 32751 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 32752 32753 const src_mcv = try self.resolveInst(if (flipped) bin_op.lhs else bin_op.rhs); 32754 const src_lock = 32755 if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 32756 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 32757 32758 break :result .fromCompareOperator( 32759 if (ty.isAbiInt(zcu)) ty.intInfo(zcu).signedness else .unsigned, 32760 result_op: { 32761 const flipped_op = if (flipped) op.reverse() else op; 32762 if (abi_size > 8) switch (flipped_op) { 32763 .lt, .gte => {}, 32764 .lte, .gt => unreachable, 32765 .eq, .neq => { 32766 const OpInfo = ?struct { addr_reg: Register, addr_lock: RegisterLock }; 32767 32768 const resolved_dst_mcv = switch (dst_mcv) { 32769 else => dst_mcv, 32770 .air_ref => |dst_ref| try self.resolveInst(dst_ref), 32771 }; 32772 const dst_info: OpInfo = switch (resolved_dst_mcv) { 32773 .none, 32774 .unreach, 32775 .dead, 32776 .undef, 32777 .immediate, 32778 .eflags, 32779 .register_offset, 32780 .register_overflow, 32781 .register_mask, 32782 .indirect, 32783 .lea_direct, 32784 .lea_got, 32785 .lea_tlv, 32786 .lea_frame, 32787 .lea_symbol, 32788 .elementwise_regs_then_frame, 32789 .reserved_frame, 32790 .air_ref, 32791 => unreachable, 32792 .register, .register_pair, .register_triple, .register_quadruple, .load_frame => null, 32793 .memory, .load_symbol, .load_got, .load_direct, .load_tlv => dst: { 32794 switch (resolved_dst_mcv) { 32795 .memory => |addr| if (std.math.cast( 32796 i32, 32797 @as(i64, @bitCast(addr)), 32798 ) != null and std.math.cast( 32799 i32, 32800 @as(i64, @bitCast(addr)) + abi_size - 8, 32801 ) != null) break :dst null, 32802 .load_symbol, .load_got, .load_direct, .load_tlv => {}, 32803 else => unreachable, 32804 } 32805 32806 const dst_addr_reg = (try self.register_manager.allocReg( 32807 null, 32808 abi.RegisterClass.gp, 32809 )).to64(); 32810 const dst_addr_lock = 32811 self.register_manager.lockRegAssumeUnused(dst_addr_reg); 32812 errdefer self.register_manager.unlockReg(dst_addr_lock); 32813 32814 try self.genSetReg(dst_addr_reg, .usize, resolved_dst_mcv.address(), .{}); 32815 break :dst .{ 32816 .addr_reg = dst_addr_reg, 32817 .addr_lock = dst_addr_lock, 32818 }; 32819 }, 32820 }; 32821 defer if (dst_info) |info| self.register_manager.unlockReg(info.addr_lock); 32822 32823 const resolved_src_mcv = switch (src_mcv) { 32824 else => src_mcv, 32825 .air_ref => |src_ref| try self.resolveInst(src_ref), 32826 }; 32827 const src_info: OpInfo = switch (resolved_src_mcv) { 32828 .none, 32829 .unreach, 32830 .dead, 32831 .undef, 32832 .immediate, 32833 .eflags, 32834 .register, 32835 .register_offset, 32836 .register_overflow, 32837 .register_mask, 32838 .indirect, 32839 .lea_symbol, 32840 .lea_direct, 32841 .lea_got, 32842 .lea_tlv, 32843 .lea_frame, 32844 .elementwise_regs_then_frame, 32845 .reserved_frame, 32846 .air_ref, 32847 => unreachable, 32848 .register_pair, .register_triple, .register_quadruple, .load_frame => null, 32849 .memory, .load_symbol, .load_got, .load_direct, .load_tlv => src: { 32850 switch (resolved_src_mcv) { 32851 .memory => |addr| if (std.math.cast( 32852 i32, 32853 @as(i64, @bitCast(addr)), 32854 ) != null and std.math.cast( 32855 i32, 32856 @as(i64, @bitCast(addr)) + abi_size - 8, 32857 ) != null) break :src null, 32858 .load_symbol, .load_got, .load_direct, .load_tlv => {}, 32859 else => unreachable, 32860 } 32861 32862 const src_addr_reg = (try self.register_manager.allocReg( 32863 null, 32864 abi.RegisterClass.gp, 32865 )).to64(); 32866 const src_addr_lock = 32867 self.register_manager.lockRegAssumeUnused(src_addr_reg); 32868 errdefer self.register_manager.unlockReg(src_addr_lock); 32869 32870 try self.genSetReg(src_addr_reg, .usize, resolved_src_mcv.address(), .{}); 32871 break :src .{ 32872 .addr_reg = src_addr_reg, 32873 .addr_lock = src_addr_lock, 32874 }; 32875 }, 32876 }; 32877 defer if (src_info) |info| 32878 self.register_manager.unlockReg(info.addr_lock); 32879 32880 const regs = try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp); 32881 const acc_reg = regs[0].to64(); 32882 const locks = self.register_manager.lockRegsAssumeUnused(2, regs); 32883 defer for (locks) |lock| self.register_manager.unlockReg(lock); 32884 32885 const limbs_len = std.math.divCeil(u16, abi_size, 8) catch unreachable; 32886 var limb_i: u16 = 0; 32887 while (limb_i < limbs_len) : (limb_i += 1) { 32888 const off = limb_i * 8; 32889 const tmp_reg = regs[@min(limb_i, 1)].to64(); 32890 32891 try self.genSetReg(tmp_reg, .usize, if (dst_info) |info| .{ 32892 .indirect = .{ .reg = info.addr_reg, .off = off }, 32893 } else switch (resolved_dst_mcv) { 32894 inline .register_pair, 32895 .register_triple, 32896 .register_quadruple, 32897 => |dst_regs| .{ .register = dst_regs[limb_i] }, 32898 .memory => |dst_addr| .{ 32899 .memory = @bitCast(@as(i64, @bitCast(dst_addr)) + off), 32900 }, 32901 .indirect => |reg_off| .{ .indirect = .{ 32902 .reg = reg_off.reg, 32903 .off = reg_off.off + off, 32904 } }, 32905 .load_frame => |frame_addr| .{ .load_frame = .{ 32906 .index = frame_addr.index, 32907 .off = frame_addr.off + off, 32908 } }, 32909 else => unreachable, 32910 }, .{}); 32911 32912 try self.genBinOpMir( 32913 .{ ._, .xor }, 32914 .usize, 32915 .{ .register = tmp_reg }, 32916 if (src_info) |info| .{ 32917 .indirect = .{ .reg = info.addr_reg, .off = off }, 32918 } else switch (resolved_src_mcv) { 32919 inline .register_pair, 32920 .register_triple, 32921 .register_quadruple, 32922 => |src_regs| .{ .register = src_regs[limb_i] }, 32923 .memory => |src_addr| .{ 32924 .memory = @bitCast(@as(i64, @bitCast(src_addr)) + off), 32925 }, 32926 .indirect => |reg_off| .{ .indirect = .{ 32927 .reg = reg_off.reg, 32928 .off = reg_off.off + off, 32929 } }, 32930 .load_frame => |frame_addr| .{ .load_frame = .{ 32931 .index = frame_addr.index, 32932 .off = frame_addr.off + off, 32933 } }, 32934 else => unreachable, 32935 }, 32936 ); 32937 32938 if (limb_i > 0) 32939 try self.asmRegisterRegister(.{ ._, .@"or" }, acc_reg, tmp_reg); 32940 } 32941 assert(limbs_len >= 2); // use flags from or 32942 break :result_op flipped_op; 32943 }, 32944 }; 32945 try self.genBinOpMir(.{ ._, .cmp }, ty, dst_mcv, src_mcv); 32946 break :result_op flipped_op; 32947 }, 32948 ); 32949 }, 32950 .float => { 32951 const flipped = switch (op) { 32952 .lt, .lte => true, 32953 .eq, .gte, .gt, .neq => false, 32954 }; 32955 32956 const dst_mcv = if (flipped) rhs_mcv else lhs_mcv; 32957 const dst_reg = if (dst_mcv.isRegister()) 32958 dst_mcv.getReg().? 32959 else 32960 try self.copyToTmpRegister(ty, dst_mcv); 32961 const dst_lock = self.register_manager.lockReg(dst_reg); 32962 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 32963 const src_mcv = if (flipped) lhs_mcv else rhs_mcv; 32964 32965 switch (ty.floatBits(self.target.*)) { 32966 16 => { 32967 assert(self.hasFeature(.f16c)); 32968 const tmp1_reg = 32969 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128(); 32970 const tmp1_mcv = MCValue{ .register = tmp1_reg }; 32971 const tmp1_lock = self.register_manager.lockRegAssumeUnused(tmp1_reg); 32972 defer self.register_manager.unlockReg(tmp1_lock); 32973 32974 const tmp2_reg = 32975 (try self.register_manager.allocReg(null, abi.RegisterClass.sse)).to128(); 32976 const tmp2_mcv = MCValue{ .register = tmp2_reg }; 32977 const tmp2_lock = self.register_manager.lockRegAssumeUnused(tmp2_reg); 32978 defer self.register_manager.unlockReg(tmp2_lock); 32979 32980 if (src_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 32981 .{ .vp_w, .insr }, 32982 tmp1_reg, 32983 dst_reg.to128(), 32984 try src_mcv.mem(self, .{ .size = .word }), 32985 .u(1), 32986 ) else try self.asmRegisterRegisterRegister( 32987 .{ .vp_, .unpcklwd }, 32988 tmp1_reg, 32989 dst_reg.to128(), 32990 (if (src_mcv.isRegister()) 32991 src_mcv.getReg().? 32992 else 32993 try self.copyToTmpRegister(ty, src_mcv)).to128(), 32994 ); 32995 try self.asmRegisterRegister(.{ .v_ps, .cvtph2 }, tmp1_reg, tmp1_reg); 32996 try self.asmRegisterRegister(.{ .v_, .movshdup }, tmp2_reg, tmp1_reg); 32997 try self.genBinOpMir(.{ ._ss, .ucomi }, ty, tmp1_mcv, tmp2_mcv); 32998 }, 32999 32 => try self.genBinOpMir( 33000 .{ ._ss, .ucomi }, 33001 ty, 33002 .{ .register = dst_reg }, 33003 src_mcv, 33004 ), 33005 64 => try self.genBinOpMir( 33006 .{ ._sd, .ucomi }, 33007 ty, 33008 .{ .register = dst_reg }, 33009 src_mcv, 33010 ), 33011 else => unreachable, 33012 } 33013 33014 break :result switch (if (flipped) op.reverse() else op) { 33015 .lt, .lte => unreachable, // required to have been canonicalized to gt(e) 33016 .gt => .a, 33017 .gte => .ae, 33018 .eq => .z_and_np, 33019 .neq => .nz_or_p, 33020 }; 33021 }, 33022 } 33023 }; 33024 33025 if (null_compare) |reloc| self.performReloc(reloc); 33026 self.eflags_inst = inst; 33027 return self.finishAir(inst, .{ .eflags = result }, .{ bin_op.lhs, bin_op.rhs, .none }); 33028 } 33029 33030 fn airCmpVector(self: *CodeGen, inst: Air.Inst.Index) !void { 33031 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 33032 const extra = self.air.extraData(Air.VectorCmp, ty_pl.payload).data; 33033 const dst_mcv = try self.genBinOp( 33034 inst, 33035 .fromCmpOp(extra.compareOperator(), false), 33036 extra.lhs, 33037 extra.rhs, 33038 ); 33039 return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); 33040 } 33041 33042 fn airCmpLtErrorsLen(self: *CodeGen, inst: Air.Inst.Index) !void { 33043 const pt = self.pt; 33044 const zcu = pt.zcu; 33045 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33046 33047 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 33048 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 33049 defer self.register_manager.unlockReg(addr_lock); 33050 const anyerror_lazy_sym: link.File.LazySymbol = .{ .kind = .const_data, .ty = .anyerror_type }; 33051 try self.genLazySymbolRef(.lea, addr_reg, anyerror_lazy_sym); 33052 33053 try self.spillEflagsIfOccupied(); 33054 33055 const op_ty = self.typeOf(un_op); 33056 const op_abi_size: u32 = @intCast(op_ty.abiSize(zcu)); 33057 const op_mcv = try self.resolveInst(un_op); 33058 const dst_reg = switch (op_mcv) { 33059 .register => |reg| reg, 33060 else => try self.copyToTmpRegister(op_ty, op_mcv), 33061 }; 33062 try self.asmRegisterMemory( 33063 .{ ._, .cmp }, 33064 registerAlias(dst_reg, op_abi_size), 33065 .{ 33066 .base = .{ .reg = addr_reg }, 33067 .mod = .{ .rm = .{ .size = .fromSize(op_abi_size) } }, 33068 }, 33069 ); 33070 33071 self.eflags_inst = inst; 33072 return self.finishAir(inst, .{ .eflags = .b }, .{ un_op, .none, .none }); 33073 } 33074 33075 fn airTry(self: *CodeGen, inst: Air.Inst.Index) !void { 33076 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 33077 const extra = self.air.extraData(Air.Try, pl_op.payload); 33078 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); 33079 const operand_ty = self.typeOf(pl_op.operand); 33080 const result = try self.genTry(inst, pl_op.operand, body, operand_ty, false); 33081 return self.finishAir(inst, result, .{ .none, .none, .none }); 33082 } 33083 33084 fn airTryPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 33085 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 33086 const extra = self.air.extraData(Air.TryPtr, ty_pl.payload); 33087 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[extra.end..][0..extra.data.body_len]); 33088 const operand_ty = self.typeOf(extra.data.ptr); 33089 const result = try self.genTry(inst, extra.data.ptr, body, operand_ty, true); 33090 return self.finishAir(inst, result, .{ .none, .none, .none }); 33091 } 33092 33093 fn genTry( 33094 self: *CodeGen, 33095 inst: Air.Inst.Index, 33096 operand: Air.Inst.Ref, 33097 body: []const Air.Inst.Index, 33098 operand_ty: Type, 33099 operand_is_ptr: bool, 33100 ) !MCValue { 33101 const liveness_cond_br = self.liveness.getCondBr(inst); 33102 33103 const operand_mcv = try self.resolveInst(operand); 33104 const is_err_mcv = if (operand_is_ptr) 33105 try self.isErrPtr(null, operand_ty, operand_mcv) 33106 else 33107 try self.isErr(null, operand_ty, operand_mcv); 33108 33109 const reloc = try self.genCondBrMir(.anyerror, is_err_mcv); 33110 33111 if (self.liveness.operandDies(inst, 0)) { 33112 if (operand.toIndex()) |operand_inst| try self.processDeath(operand_inst); 33113 } 33114 33115 const state = try self.saveState(); 33116 33117 for (liveness_cond_br.else_deaths) |death| try self.processDeath(death); 33118 try self.genBodyBlock(body); 33119 try self.restoreState(state, &.{}, .{ 33120 .emit_instructions = false, 33121 .update_tracking = true, 33122 .resurrect = true, 33123 .close_scope = true, 33124 }); 33125 33126 self.performReloc(reloc); 33127 33128 for (liveness_cond_br.then_deaths) |death| try self.processDeath(death); 33129 33130 const result = if (self.liveness.isUnused(inst)) 33131 .unreach 33132 else if (operand_is_ptr) 33133 try self.genUnwrapErrUnionPayloadPtrMir(inst, operand_ty, operand_mcv) 33134 else 33135 try self.genUnwrapErrUnionPayloadMir(inst, operand_ty, operand_mcv); 33136 return result; 33137 } 33138 33139 fn airDbgVar(self: *CodeGen, inst: Air.Inst.Index) !void { 33140 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 33141 try self.genLocalDebugInfo(inst, try self.resolveInst(pl_op.operand)); 33142 return self.finishAir(inst, .unreach, .{ pl_op.operand, .none, .none }); 33143 } 33144 33145 fn genCondBrMir(self: *CodeGen, ty: Type, mcv: MCValue) !Mir.Inst.Index { 33146 const pt = self.pt; 33147 const abi_size = ty.abiSize(pt.zcu); 33148 switch (mcv) { 33149 .eflags => |cc| { 33150 // Here we map the opposites since the jump is to the false branch. 33151 return self.asmJccReloc(cc.negate(), undefined); 33152 }, 33153 .register => |reg| { 33154 try self.spillEflagsIfOccupied(); 33155 try self.asmRegisterImmediate(.{ ._, .@"test" }, reg.to8(), .u(1)); 33156 return self.asmJccReloc(.z, undefined); 33157 }, 33158 .immediate, 33159 .load_frame, 33160 => { 33161 try self.spillEflagsIfOccupied(); 33162 if (abi_size <= 8) { 33163 const reg = try self.copyToTmpRegister(ty, mcv); 33164 return self.genCondBrMir(ty, .{ .register = reg }); 33165 } 33166 return self.fail("TODO implement condbr when condition is {} with abi larger than 8 bytes", .{mcv}); 33167 }, 33168 else => return self.fail("TODO implement condbr when condition is {s}", .{@tagName(mcv)}), 33169 } 33170 } 33171 33172 fn airCondBr(self: *CodeGen, inst: Air.Inst.Index) !void { 33173 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 33174 const cond = try self.resolveInst(pl_op.operand); 33175 const cond_ty = self.typeOf(pl_op.operand); 33176 const extra = self.air.extraData(Air.CondBr, pl_op.payload); 33177 const then_body: []const Air.Inst.Index = 33178 @ptrCast(self.air.extra[extra.end..][0..extra.data.then_body_len]); 33179 const else_body: []const Air.Inst.Index = 33180 @ptrCast(self.air.extra[extra.end + then_body.len ..][0..extra.data.else_body_len]); 33181 const liveness_cond_br = self.liveness.getCondBr(inst); 33182 33183 // If the condition dies here in this condbr instruction, process 33184 // that death now instead of later as this has an effect on 33185 // whether it needs to be spilled in the branches 33186 if (self.liveness.operandDies(inst, 0)) { 33187 if (pl_op.operand.toIndex()) |op_inst| try self.processDeath(op_inst); 33188 } 33189 33190 const state = try self.saveState(); 33191 const reloc = try self.genCondBrMir(cond_ty, cond); 33192 33193 for (liveness_cond_br.then_deaths) |death| try self.processDeath(death); 33194 try self.genBodyBlock(then_body); 33195 try self.restoreState(state, &.{}, .{ 33196 .emit_instructions = false, 33197 .update_tracking = true, 33198 .resurrect = true, 33199 .close_scope = true, 33200 }); 33201 33202 self.performReloc(reloc); 33203 33204 for (liveness_cond_br.else_deaths) |death| try self.processDeath(death); 33205 try self.genBodyBlock(else_body); 33206 try self.restoreState(state, &.{}, .{ 33207 .emit_instructions = false, 33208 .update_tracking = true, 33209 .resurrect = true, 33210 .close_scope = true, 33211 }); 33212 33213 // We already took care of pl_op.operand earlier, so there's nothing left to do. 33214 } 33215 33216 fn isNull(self: *CodeGen, inst: Air.Inst.Index, opt_ty: Type, opt_mcv: MCValue) !MCValue { 33217 const pt = self.pt; 33218 const zcu = pt.zcu; 33219 switch (opt_mcv) { 33220 .register_overflow => |ro| return .{ .eflags = ro.eflags.negate() }, 33221 else => {}, 33222 } 33223 33224 try self.spillEflagsIfOccupied(); 33225 33226 const pl_ty = opt_ty.optionalChild(zcu); 33227 33228 const some_info: struct { off: u31, ty: Type } = if (opt_ty.optionalReprIsPayload(zcu)) 33229 .{ .off = 0, .ty = if (pl_ty.isSlice(zcu)) pl_ty.slicePtrFieldType(zcu) else pl_ty } 33230 else 33231 .{ .off = @intCast(pl_ty.abiSize(zcu)), .ty = .bool }; 33232 33233 self.eflags_inst = inst; 33234 switch (opt_mcv) { 33235 .none, 33236 .unreach, 33237 .dead, 33238 .undef, 33239 .immediate, 33240 .eflags, 33241 .register_triple, 33242 .register_quadruple, 33243 .register_offset, 33244 .register_overflow, 33245 .register_mask, 33246 .lea_direct, 33247 .lea_got, 33248 .lea_tlv, 33249 .lea_symbol, 33250 .elementwise_regs_then_frame, 33251 .reserved_frame, 33252 .air_ref, 33253 => unreachable, 33254 33255 .lea_frame => { 33256 self.eflags_inst = null; 33257 return .{ .immediate = @intFromBool(false) }; 33258 }, 33259 33260 .register => |opt_reg| { 33261 if (some_info.off == 0) { 33262 const some_abi_size: u32 = @intCast(some_info.ty.abiSize(zcu)); 33263 const alias_reg = registerAlias(opt_reg, some_abi_size); 33264 assert(some_abi_size * 8 == alias_reg.bitSize()); 33265 try self.asmRegisterRegister(.{ ._, .@"test" }, alias_reg, alias_reg); 33266 return .{ .eflags = .z }; 33267 } 33268 assert(some_info.ty.ip_index == .bool_type); 33269 const opt_abi_size: u32 = @intCast(opt_ty.abiSize(zcu)); 33270 try self.asmRegisterImmediate( 33271 .{ ._, .bt }, 33272 registerAlias(opt_reg, opt_abi_size), 33273 .u(@as(u6, @intCast(some_info.off * 8))), 33274 ); 33275 return .{ .eflags = .nc }; 33276 }, 33277 33278 .register_pair => |opt_regs| { 33279 if (some_info.off == 0) { 33280 const some_abi_size: u32 = @intCast(some_info.ty.abiSize(zcu)); 33281 const alias_reg = registerAlias(opt_regs[0], some_abi_size); 33282 assert(some_abi_size * 8 == alias_reg.bitSize()); 33283 try self.asmRegisterRegister(.{ ._, .@"test" }, alias_reg, alias_reg); 33284 return .{ .eflags = .z }; 33285 } 33286 assert(some_info.ty.ip_index == .bool_type); 33287 const opt_abi_size: u32 = @intCast(opt_ty.abiSize(zcu)); 33288 try self.asmRegisterImmediate( 33289 .{ ._, .bt }, 33290 registerAlias(opt_regs[some_info.off / 8], opt_abi_size), 33291 .u(@as(u6, @truncate(some_info.off * 8))), 33292 ); 33293 return .{ .eflags = .nc }; 33294 }, 33295 33296 .memory, 33297 .load_symbol, 33298 .load_got, 33299 .load_direct, 33300 .load_tlv, 33301 => { 33302 const addr_reg = (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); 33303 const addr_reg_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 33304 defer self.register_manager.unlockReg(addr_reg_lock); 33305 33306 try self.genSetReg(addr_reg, .usize, opt_mcv.address(), .{}); 33307 const some_abi_size: u32 = @intCast(some_info.ty.abiSize(zcu)); 33308 try self.asmMemoryImmediate( 33309 .{ ._, .cmp }, 33310 .{ 33311 .base = .{ .reg = addr_reg }, 33312 .mod = .{ .rm = .{ 33313 .size = .fromSize(some_abi_size), 33314 .disp = some_info.off, 33315 } }, 33316 }, 33317 .u(0), 33318 ); 33319 return .{ .eflags = .e }; 33320 }, 33321 33322 .indirect, .load_frame => { 33323 const some_abi_size: u32 = @intCast(some_info.ty.abiSize(zcu)); 33324 try self.asmMemoryImmediate( 33325 .{ ._, .cmp }, 33326 switch (opt_mcv) { 33327 .indirect => |reg_off| .{ 33328 .base = .{ .reg = reg_off.reg }, 33329 .mod = .{ .rm = .{ 33330 .size = .fromSize(some_abi_size), 33331 .disp = reg_off.off + some_info.off, 33332 } }, 33333 }, 33334 .load_frame => |frame_addr| .{ 33335 .base = .{ .frame = frame_addr.index }, 33336 .mod = .{ .rm = .{ 33337 .size = .fromSize(some_abi_size), 33338 .disp = frame_addr.off + some_info.off, 33339 } }, 33340 }, 33341 else => unreachable, 33342 }, 33343 .u(0), 33344 ); 33345 return .{ .eflags = .e }; 33346 }, 33347 } 33348 } 33349 33350 fn isNullPtr(self: *CodeGen, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { 33351 const pt = self.pt; 33352 const zcu = pt.zcu; 33353 const opt_ty = ptr_ty.childType(zcu); 33354 const pl_ty = opt_ty.optionalChild(zcu); 33355 33356 try self.spillEflagsIfOccupied(); 33357 33358 const some_info: struct { off: i32, ty: Type } = if (opt_ty.optionalReprIsPayload(zcu)) 33359 .{ .off = 0, .ty = if (pl_ty.isSlice(zcu)) pl_ty.slicePtrFieldType(zcu) else pl_ty } 33360 else 33361 .{ .off = @intCast(pl_ty.abiSize(zcu)), .ty = .bool }; 33362 33363 const ptr_reg = switch (ptr_mcv) { 33364 .register => |reg| reg, 33365 else => try self.copyToTmpRegister(ptr_ty, ptr_mcv), 33366 }; 33367 const ptr_lock = self.register_manager.lockReg(ptr_reg); 33368 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 33369 33370 const some_abi_size: u32 = @intCast(some_info.ty.abiSize(zcu)); 33371 try self.asmMemoryImmediate( 33372 .{ ._, .cmp }, 33373 .{ 33374 .base = .{ .reg = ptr_reg }, 33375 .mod = .{ .rm = .{ 33376 .size = .fromSize(some_abi_size), 33377 .disp = some_info.off, 33378 } }, 33379 }, 33380 .u(0), 33381 ); 33382 33383 self.eflags_inst = inst; 33384 return .{ .eflags = .e }; 33385 } 33386 33387 fn isErr(self: *CodeGen, maybe_inst: ?Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MCValue { 33388 const pt = self.pt; 33389 const zcu = pt.zcu; 33390 const err_ty = eu_ty.errorUnionSet(zcu); 33391 if (err_ty.errorSetIsEmpty(zcu)) return MCValue{ .immediate = 0 }; // always false 33392 33393 try self.spillEflagsIfOccupied(); 33394 33395 const err_off: u31 = @intCast(codegen.errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu)); 33396 switch (eu_mcv) { 33397 .register => |reg| { 33398 const eu_lock = self.register_manager.lockReg(reg); 33399 defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); 33400 33401 const tmp_reg = try self.copyToTmpRegister(eu_ty, eu_mcv); 33402 if (err_off > 0) { 33403 try self.genShiftBinOpMir( 33404 .{ ._r, .sh }, 33405 eu_ty, 33406 .{ .register = tmp_reg }, 33407 .u8, 33408 .{ .immediate = @as(u6, @intCast(err_off * 8)) }, 33409 ); 33410 } else { 33411 try self.truncateRegister(.anyerror, tmp_reg); 33412 } 33413 try self.genBinOpMir(.{ ._, .cmp }, .anyerror, .{ .register = tmp_reg }, .{ .immediate = 0 }); 33414 }, 33415 .load_frame => |frame_addr| try self.genBinOpMir( 33416 .{ ._, .cmp }, 33417 .anyerror, 33418 .{ .load_frame = .{ 33419 .index = frame_addr.index, 33420 .off = frame_addr.off + err_off, 33421 } }, 33422 .{ .immediate = 0 }, 33423 ), 33424 else => return self.fail("TODO implement isErr for {}", .{eu_mcv}), 33425 } 33426 33427 if (maybe_inst) |inst| self.eflags_inst = inst; 33428 return MCValue{ .eflags = .a }; 33429 } 33430 33431 fn isErrPtr(self: *CodeGen, maybe_inst: ?Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { 33432 const pt = self.pt; 33433 const zcu = pt.zcu; 33434 const eu_ty = ptr_ty.childType(zcu); 33435 const err_ty = eu_ty.errorUnionSet(zcu); 33436 if (err_ty.errorSetIsEmpty(zcu)) return MCValue{ .immediate = 0 }; // always false 33437 33438 try self.spillEflagsIfOccupied(); 33439 33440 const ptr_reg = switch (ptr_mcv) { 33441 .register => |reg| reg, 33442 else => try self.copyToTmpRegister(ptr_ty, ptr_mcv), 33443 }; 33444 const ptr_lock = self.register_manager.lockReg(ptr_reg); 33445 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 33446 33447 const err_off: u31 = @intCast(codegen.errUnionErrorOffset(eu_ty.errorUnionPayload(zcu), zcu)); 33448 try self.asmMemoryImmediate( 33449 .{ ._, .cmp }, 33450 .{ 33451 .base = .{ .reg = ptr_reg }, 33452 .mod = .{ .rm = .{ 33453 .size = self.memSize(.anyerror), 33454 .disp = err_off, 33455 } }, 33456 }, 33457 .u(0), 33458 ); 33459 33460 if (maybe_inst) |inst| self.eflags_inst = inst; 33461 return MCValue{ .eflags = .a }; 33462 } 33463 33464 fn isNonErr(self: *CodeGen, inst: Air.Inst.Index, eu_ty: Type, eu_mcv: MCValue) !MCValue { 33465 const is_err_res = try self.isErr(inst, eu_ty, eu_mcv); 33466 switch (is_err_res) { 33467 .eflags => |cc| { 33468 assert(cc == .a); 33469 return MCValue{ .eflags = cc.negate() }; 33470 }, 33471 .immediate => |imm| { 33472 assert(imm == 0); 33473 return MCValue{ .immediate = @intFromBool(imm == 0) }; 33474 }, 33475 else => unreachable, 33476 } 33477 } 33478 33479 fn isNonErrPtr(self: *CodeGen, inst: Air.Inst.Index, ptr_ty: Type, ptr_mcv: MCValue) !MCValue { 33480 const is_err_res = try self.isErrPtr(inst, ptr_ty, ptr_mcv); 33481 switch (is_err_res) { 33482 .eflags => |cc| { 33483 assert(cc == .a); 33484 return MCValue{ .eflags = cc.negate() }; 33485 }, 33486 .immediate => |imm| { 33487 assert(imm == 0); 33488 return MCValue{ .immediate = @intFromBool(imm == 0) }; 33489 }, 33490 else => unreachable, 33491 } 33492 } 33493 33494 fn airIsNull(self: *CodeGen, inst: Air.Inst.Index) !void { 33495 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33496 const operand = try self.resolveInst(un_op); 33497 const ty = self.typeOf(un_op); 33498 const result = try self.isNull(inst, ty, operand); 33499 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33500 } 33501 33502 fn airIsNullPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 33503 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33504 const operand = try self.resolveInst(un_op); 33505 const ty = self.typeOf(un_op); 33506 const result = try self.isNullPtr(inst, ty, operand); 33507 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33508 } 33509 33510 fn airIsNonNull(self: *CodeGen, inst: Air.Inst.Index) !void { 33511 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33512 const operand = try self.resolveInst(un_op); 33513 const ty = self.typeOf(un_op); 33514 const result: MCValue = switch (try self.isNull(inst, ty, operand)) { 33515 .immediate => |imm| .{ .immediate = @intFromBool(imm == 0) }, 33516 .eflags => |cc| .{ .eflags = cc.negate() }, 33517 else => unreachable, 33518 }; 33519 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33520 } 33521 33522 fn airIsNonNullPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 33523 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33524 const operand = try self.resolveInst(un_op); 33525 const ty = self.typeOf(un_op); 33526 const result: MCValue = switch (try self.isNullPtr(inst, ty, operand)) { 33527 .eflags => |cc| .{ .eflags = cc.negate() }, 33528 else => unreachable, 33529 }; 33530 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33531 } 33532 33533 fn airIsErr(self: *CodeGen, inst: Air.Inst.Index) !void { 33534 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33535 const operand = try self.resolveInst(un_op); 33536 const ty = self.typeOf(un_op); 33537 const result = try self.isErr(inst, ty, operand); 33538 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33539 } 33540 33541 fn airIsErrPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 33542 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33543 const operand = try self.resolveInst(un_op); 33544 const ty = self.typeOf(un_op); 33545 const result = try self.isErrPtr(inst, ty, operand); 33546 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33547 } 33548 33549 fn airIsNonErr(self: *CodeGen, inst: Air.Inst.Index) !void { 33550 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33551 const operand = try self.resolveInst(un_op); 33552 const ty = self.typeOf(un_op); 33553 const result = try self.isNonErr(inst, ty, operand); 33554 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33555 } 33556 33557 fn airIsNonErrPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 33558 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 33559 const operand = try self.resolveInst(un_op); 33560 const ty = self.typeOf(un_op); 33561 const result = try self.isNonErrPtr(inst, ty, operand); 33562 return self.finishAir(inst, result, .{ un_op, .none, .none }); 33563 } 33564 33565 fn airLoop(self: *CodeGen, inst: Air.Inst.Index) !void { 33566 // A loop is a setup to be able to jump back to the beginning. 33567 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 33568 const loop = self.air.extraData(Air.Block, ty_pl.payload); 33569 const body: []const Air.Inst.Index = @ptrCast(self.air.extra[loop.end..][0..loop.data.body_len]); 33570 33571 const state = try self.saveState(); 33572 33573 try self.loops.putNoClobber(self.gpa, inst, .{ 33574 .state = state, 33575 .target = @intCast(self.mir_instructions.len), 33576 }); 33577 defer assert(self.loops.remove(inst)); 33578 33579 try self.genBodyBlock(body); 33580 } 33581 33582 fn lowerBlock(self: *CodeGen, inst: Air.Inst.Index, body: []const Air.Inst.Index) !void { 33583 // A block is a setup to be able to jump to the end. 33584 const inst_tracking_i = self.inst_tracking.count(); 33585 self.inst_tracking.putAssumeCapacityNoClobber(inst, .init(.unreach)); 33586 33587 try self.blocks.putNoClobber(self.gpa, inst, .{ .state = self.initRetroactiveState() }); 33588 const liveness = self.liveness.getBlock(inst); 33589 33590 try self.genBody(body); 33591 33592 var block_data = self.blocks.fetchRemove(inst).?; 33593 defer block_data.value.deinit(self.gpa); 33594 if (block_data.value.relocs.items.len > 0) { 33595 try self.restoreState(block_data.value.state, liveness.deaths, .{ 33596 .emit_instructions = false, 33597 .update_tracking = true, 33598 .resurrect = true, 33599 .close_scope = true, 33600 }); 33601 const block_relocs_last_index = block_data.value.relocs.items.len - 1; 33602 for (if (block_data.value.relocs.items[block_relocs_last_index] == self.mir_instructions.len - 1) block_relocs: { 33603 _ = self.mir_instructions.pop(); 33604 break :block_relocs block_data.value.relocs.items[0..block_relocs_last_index]; 33605 } else block_data.value.relocs.items) |block_reloc| self.performReloc(block_reloc); 33606 } 33607 33608 if (std.debug.runtime_safety) assert(self.inst_tracking.getIndex(inst).? == inst_tracking_i); 33609 const tracking = &self.inst_tracking.values()[inst_tracking_i]; 33610 if (self.liveness.isUnused(inst)) try tracking.die(self, inst); 33611 self.getValueIfFree(tracking.short, inst); 33612 } 33613 33614 fn lowerSwitchBr( 33615 self: *CodeGen, 33616 inst: Air.Inst.Index, 33617 switch_br: Air.UnwrappedSwitch, 33618 condition: MCValue, 33619 condition_dies: bool, 33620 is_loop: bool, 33621 ) !void { 33622 const zcu = self.pt.zcu; 33623 const condition_ty = self.typeOf(switch_br.operand); 33624 const condition_int_info = self.intInfo(condition_ty).?; 33625 const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits); 33626 33627 const ExpectedContents = extern struct { 33628 liveness_deaths: [1 << 8 | 1]Air.Inst.Index, 33629 bigint_limbs: [std.math.big.int.calcTwosCompLimbCount(1 << 8)]std.math.big.Limb, 33630 relocs: [1 << 6]Mir.Inst.Index, 33631 }; 33632 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 33633 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 33634 const allocator = stack.get(); 33635 33636 const state = try self.saveState(); 33637 33638 const liveness = try self.liveness.getSwitchBr(allocator, inst, switch_br.cases_len + 1); 33639 defer allocator.free(liveness.deaths); 33640 33641 if (!self.mod.pic and self.target.ofmt == .elf) table: { 33642 var prong_items: u32 = 0; 33643 var min: ?Value = null; 33644 var max: ?Value = null; 33645 { 33646 var cases_it = switch_br.iterateCases(); 33647 while (cases_it.next()) |case| { 33648 prong_items += @intCast(case.items.len + case.ranges.len); 33649 for (case.items) |item| { 33650 const val = Value.fromInterned(item.toInterned().?); 33651 if (min == null or val.compareHetero(.lt, min.?, zcu)) min = val; 33652 if (max == null or val.compareHetero(.gt, max.?, zcu)) max = val; 33653 } 33654 for (case.ranges) |range| { 33655 const low = Value.fromInterned(range[0].toInterned().?); 33656 if (min == null or low.compareHetero(.lt, min.?, zcu)) min = low; 33657 const high = Value.fromInterned(range[1].toInterned().?); 33658 if (max == null or high.compareHetero(.gt, max.?, zcu)) max = high; 33659 } 33660 } 33661 } 33662 // This condition also triggers for switches with no non-else prongs and switches on bool. 33663 if (prong_items < 1 << 2 or prong_items > 1 << 8) break :table; 33664 33665 var min_space: Value.BigIntSpace = undefined; 33666 const min_bigint = min.?.toBigInt(&min_space, zcu); 33667 var max_space: Value.BigIntSpace = undefined; 33668 const max_bigint = max.?.toBigInt(&max_space, zcu); 33669 const limbs = try allocator.alloc( 33670 std.math.big.Limb, 33671 @max(min_bigint.limbs.len, max_bigint.limbs.len) + 1, 33672 ); 33673 defer allocator.free(limbs); 33674 const table_len = table_len: { 33675 var table_len_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; 33676 table_len_bigint.sub(max_bigint, min_bigint); 33677 assert(table_len_bigint.positive); // min <= max 33678 break :table_len @as(u11, table_len_bigint.toConst().to(u10) catch break :table) + 1; // no more than a 1024 entry table 33679 }; 33680 assert(prong_items <= table_len); // each prong item introduces at least one unique integer to the range 33681 if (prong_items < table_len >> 2) break :table; // no more than 75% waste 33682 33683 const condition_index = if (condition_dies and condition.isModifiable()) condition else condition_index: { 33684 const condition_index = try self.allocTempRegOrMem(condition_ty, true); 33685 try self.genCopy(condition_ty, condition_index, condition, .{}); 33686 break :condition_index condition_index; 33687 }; 33688 try self.spillEflagsIfOccupied(); 33689 if (min.?.orderAgainstZero(zcu).compare(.neq)) try self.genBinOpMir( 33690 .{ ._, .sub }, 33691 condition_ty, 33692 condition_index, 33693 .{ .air_ref = Air.internedToRef(min.?.toIntern()) }, 33694 ); 33695 const else_reloc = if (switch_br.else_body_len > 0) else_reloc: { 33696 var cond_temp = try self.tempInit(condition_ty, condition_index); 33697 var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table_len - 1)); 33698 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { 33699 error.SelectFailed => unreachable, 33700 else => |e| return e, 33701 }; 33702 try cond_temp.die(self); 33703 try table_max_temp.die(self); 33704 const else_reloc = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined); 33705 try cc_temp.die(self); 33706 break :else_reloc else_reloc; 33707 } else undefined; 33708 const table_start: u31 = @intCast(self.mir_table.items.len); 33709 { 33710 const condition_index_reg = if (condition_index.isRegister()) 33711 condition_index.getReg().? 33712 else 33713 try self.copyToTmpRegister(.usize, condition_index); 33714 const condition_index_lock = self.register_manager.lockReg(condition_index_reg); 33715 defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock); 33716 try self.truncateRegister(condition_ty, condition_index_reg); 33717 const ptr_size = @divExact(self.target.ptrBitWidth(), 8); 33718 try self.asmMemory(.{ ._mp, .j }, .{ 33719 .base = .table, 33720 .mod = .{ .rm = .{ 33721 .size = .ptr, 33722 .index = registerAlias(condition_index_reg, ptr_size), 33723 .scale = .fromFactor(@intCast(ptr_size)), 33724 .disp = table_start * ptr_size, 33725 } }, 33726 }); 33727 } 33728 const else_reloc_marker: u32 = 0; 33729 assert(self.mir_instructions.len > else_reloc_marker); 33730 try self.mir_table.appendNTimes(self.gpa, else_reloc_marker, table_len); 33731 if (is_loop) try self.loop_switches.putNoClobber(self.gpa, inst, .{ 33732 .start = table_start, 33733 .len = table_len, 33734 .min = min.?, 33735 .else_relocs = if (switch_br.else_body_len > 0) .{ .forward = .empty } else .@"unreachable", 33736 }); 33737 defer if (is_loop) { 33738 var loop_switch_data = self.loop_switches.fetchRemove(inst).?.value; 33739 switch (loop_switch_data.else_relocs) { 33740 .@"unreachable", .backward => {}, 33741 .forward => |*else_relocs| else_relocs.deinit(self.gpa), 33742 } 33743 }; 33744 var cases_it = switch_br.iterateCases(); 33745 while (cases_it.next()) |case| { 33746 { 33747 const table = self.mir_table.items[table_start..][0..table_len]; 33748 for (case.items) |item| { 33749 const val = Value.fromInterned(item.toInterned().?); 33750 var val_space: Value.BigIntSpace = undefined; 33751 const val_bigint = val.toBigInt(&val_space, zcu); 33752 var index_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; 33753 index_bigint.sub(val_bigint, min_bigint); 33754 table[index_bigint.toConst().to(u10) catch unreachable] = @intCast(self.mir_instructions.len); 33755 } 33756 for (case.ranges) |range| { 33757 var low_space: Value.BigIntSpace = undefined; 33758 const low_bigint = Value.fromInterned(range[0].toInterned().?).toBigInt(&low_space, zcu); 33759 var high_space: Value.BigIntSpace = undefined; 33760 const high_bigint = Value.fromInterned(range[1].toInterned().?).toBigInt(&high_space, zcu); 33761 var index_bigint: std.math.big.int.Mutable = .{ .limbs = limbs, .positive = undefined, .len = undefined }; 33762 index_bigint.sub(low_bigint, min_bigint); 33763 const start = index_bigint.toConst().to(u10) catch unreachable; 33764 index_bigint.sub(high_bigint, min_bigint); 33765 const end = @as(u11, index_bigint.toConst().to(u10) catch unreachable) + 1; 33766 @memset(table[start..end], @intCast(self.mir_instructions.len)); 33767 } 33768 } 33769 33770 for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); 33771 33772 try self.genBodyBlock(case.body); 33773 try self.restoreState(state, &.{}, .{ 33774 .emit_instructions = false, 33775 .update_tracking = true, 33776 .resurrect = true, 33777 .close_scope = true, 33778 }); 33779 } 33780 if (switch_br.else_body_len > 0) { 33781 const else_body = cases_it.elseBody(); 33782 33783 const else_deaths = liveness.deaths.len - 1; 33784 for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); 33785 33786 self.performReloc(else_reloc); 33787 if (is_loop) { 33788 const loop_switch_data = self.loop_switches.getPtr(inst).?; 33789 for (loop_switch_data.else_relocs.forward.items) |reloc| self.performReloc(reloc); 33790 loop_switch_data.else_relocs.forward.deinit(self.gpa); 33791 loop_switch_data.else_relocs = .{ .backward = @intCast(self.mir_instructions.len) }; 33792 } 33793 for (self.mir_table.items[table_start..][0..table_len]) |*entry| if (entry.* == else_reloc_marker) { 33794 entry.* = @intCast(self.mir_instructions.len); 33795 }; 33796 33797 try self.genBodyBlock(else_body); 33798 try self.restoreState(state, &.{}, .{ 33799 .emit_instructions = false, 33800 .update_tracking = true, 33801 .resurrect = true, 33802 .close_scope = true, 33803 }); 33804 } 33805 return; 33806 } 33807 33808 var cases_it = switch_br.iterateCases(); 33809 while (cases_it.next()) |case| { 33810 var relocs = try allocator.alloc(Mir.Inst.Index, case.items.len + case.ranges.len); 33811 defer allocator.free(relocs); 33812 33813 try self.spillEflagsIfOccupied(); 33814 for (case.items, relocs[0..case.items.len]) |item, *reloc| { 33815 const item_mcv = try self.resolveInst(item); 33816 const cc: Condition = switch (condition) { 33817 .eflags => |cc| switch (item_mcv.immediate) { 33818 0 => cc.negate(), 33819 1 => cc, 33820 else => unreachable, 33821 }, 33822 else => cc: { 33823 var cond_temp = try self.tempInit(condition_ty, condition); 33824 var item_temp = try self.tempInit(condition_ty, item_mcv); 33825 const cc_temp = cond_temp.cmpInts(.eq, &item_temp, self) catch |err| switch (err) { 33826 error.SelectFailed => unreachable, 33827 else => |e| return e, 33828 }; 33829 try cond_temp.die(self); 33830 try item_temp.die(self); 33831 const cc = cc_temp.tracking(self).short.eflags; 33832 try cc_temp.die(self); 33833 try self.resetTemps(); 33834 break :cc cc; 33835 }, 33836 }; 33837 reloc.* = try self.asmJccReloc(cc, undefined); 33838 } 33839 33840 for (case.ranges, relocs[case.items.len..]) |range, *reloc| { 33841 var cond_temp = try self.tempInit(condition_ty, condition); 33842 const min_mcv = try self.resolveInst(range[0]); 33843 const max_mcv = try self.resolveInst(range[1]); 33844 // `null` means always false. 33845 const lt_min = cc: switch (condition) { 33846 .eflags => |cc| switch (min_mcv.immediate) { 33847 0 => null, // condition never <0 33848 1 => cc.negate(), 33849 else => unreachable, 33850 }, 33851 else => { 33852 var min_temp = try self.tempInit(condition_ty, min_mcv); 33853 const cc_temp = cond_temp.cmpInts(.lt, &min_temp, self) catch |err| switch (err) { 33854 error.SelectFailed => unreachable, 33855 else => |e| return e, 33856 }; 33857 try min_temp.die(self); 33858 const cc = cc_temp.tracking(self).short.eflags; 33859 try cc_temp.die(self); 33860 break :cc cc; 33861 }, 33862 }; 33863 const lt_min_reloc = if (lt_min) |cc| r: { 33864 break :r try self.asmJccReloc(cc, undefined); 33865 } else null; 33866 // `null` means always true. 33867 const lte_max = switch (condition) { 33868 .eflags => |cc| switch (max_mcv.immediate) { 33869 0 => cc.negate(), 33870 1 => null, // condition always >=1 33871 else => unreachable, 33872 }, 33873 else => cc: { 33874 var max_temp = try self.tempInit(condition_ty, max_mcv); 33875 const cc_temp = cond_temp.cmpInts(.lte, &max_temp, self) catch |err| switch (err) { 33876 error.SelectFailed => unreachable, 33877 else => |e| return e, 33878 }; 33879 try max_temp.die(self); 33880 const cc = cc_temp.tracking(self).short.eflags; 33881 try cc_temp.die(self); 33882 break :cc cc; 33883 }, 33884 }; 33885 try cond_temp.die(self); 33886 try self.resetTemps(); 33887 // "Success" case is in `reloc`.... 33888 if (lte_max) |cc| { 33889 reloc.* = try self.asmJccReloc(cc, undefined); 33890 } else { 33891 reloc.* = try self.asmJmpReloc(undefined); 33892 } 33893 // ...and "fail" case falls through to next checks. 33894 if (lt_min_reloc) |r| self.performReloc(r); 33895 } 33896 33897 // The jump to skip this case if the conditions all failed. 33898 const skip_case_reloc = try self.asmJmpReloc(undefined); 33899 33900 for (liveness.deaths[case.idx]) |operand| try self.processDeath(operand); 33901 33902 // Relocate all success cases to the body we're about to generate. 33903 for (relocs) |reloc| self.performReloc(reloc); 33904 try self.genBodyBlock(case.body); 33905 try self.restoreState(state, &.{}, .{ 33906 .emit_instructions = false, 33907 .update_tracking = true, 33908 .resurrect = true, 33909 .close_scope = true, 33910 }); 33911 33912 // Relocate the "skip" branch to fall through to the next case. 33913 self.performReloc(skip_case_reloc); 33914 } 33915 if (switch_br.else_body_len > 0) { 33916 const else_body = cases_it.elseBody(); 33917 33918 const else_deaths = liveness.deaths.len - 1; 33919 for (liveness.deaths[else_deaths]) |operand| try self.processDeath(operand); 33920 33921 try self.genBodyBlock(else_body); 33922 try self.restoreState(state, &.{}, .{ 33923 .emit_instructions = false, 33924 .update_tracking = true, 33925 .resurrect = true, 33926 .close_scope = true, 33927 }); 33928 } 33929 } 33930 33931 fn airSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void { 33932 const switch_br = self.air.unwrapSwitch(inst); 33933 const condition = try self.resolveInst(switch_br.operand); 33934 33935 // If the condition dies here in this switch instruction, process 33936 // that death now instead of later as this has an effect on 33937 // whether it needs to be spilled in the branches 33938 const condition_dies = self.liveness.operandDies(inst, 0); 33939 if (condition_dies) { 33940 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst); 33941 } 33942 try self.lowerSwitchBr(inst, switch_br, condition, condition_dies, false); 33943 33944 // We already took care of pl_op.operand earlier, so there's nothing left to do 33945 } 33946 33947 fn airLoopSwitchBr(self: *CodeGen, inst: Air.Inst.Index) !void { 33948 const switch_br = self.air.unwrapSwitch(inst); 33949 const condition = try self.resolveInst(switch_br.operand); 33950 33951 const mat_cond = if (condition.isModifiable() and 33952 self.reuseOperand(inst, switch_br.operand, 0, condition)) 33953 condition 33954 else mat_cond: { 33955 const mat_cond = try self.allocRegOrMem(inst, true); 33956 try self.genCopy(self.typeOf(switch_br.operand), mat_cond, condition, .{}); 33957 break :mat_cond mat_cond; 33958 }; 33959 self.inst_tracking.putAssumeCapacityNoClobber(inst, .init(mat_cond)); 33960 33961 // If the condition dies here in this switch instruction, process 33962 // that death now instead of later as this has an effect on 33963 // whether it needs to be spilled in the branches 33964 if (self.liveness.operandDies(inst, 0)) { 33965 if (switch_br.operand.toIndex()) |op_inst| try self.processDeath(op_inst); 33966 } 33967 33968 // Ensure a register is available for dispatch. 33969 if (!mat_cond.isRegister()) _ = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 33970 33971 const state = try self.saveState(); 33972 33973 try self.loops.putNoClobber(self.gpa, inst, .{ 33974 .state = state, 33975 .target = @intCast(self.mir_instructions.len), 33976 }); 33977 defer assert(self.loops.remove(inst)); 33978 33979 // Stop tracking block result without forgetting tracking info 33980 try self.freeValue(mat_cond); 33981 33982 try self.lowerSwitchBr(inst, switch_br, mat_cond, true, true); 33983 33984 try self.processDeath(inst); 33985 } 33986 33987 fn airSwitchDispatch(self: *CodeGen, inst: Air.Inst.Index) !void { 33988 const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; 33989 33990 const block_ty = self.typeOfIndex(br.block_inst); 33991 const loop_data = self.loops.getPtr(br.block_inst).?; 33992 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; 33993 { 33994 try self.getValue(block_tracking.short, null); 33995 const src_mcv = try self.resolveInst(br.operand); 33996 33997 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) { 33998 try self.getValue(block_tracking.short, br.block_inst); 33999 // .long = .none to avoid merging operand and block result stack frames. 34000 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv }; 34001 try current_tracking.materializeUnsafe(self, br.block_inst, block_tracking.*); 34002 for (current_tracking.getRegs()) |src_reg| self.register_manager.freeReg(src_reg); 34003 } else { 34004 try self.getValue(block_tracking.short, br.block_inst); 34005 try self.genCopy(block_ty, block_tracking.short, try self.resolveInst(br.operand), .{}); 34006 } 34007 } 34008 34009 // Process operand death so that it is properly accounted for in the State below. 34010 if (self.liveness.operandDies(inst, 0)) { 34011 if (br.operand.toIndex()) |op_inst| try self.processDeath(op_inst); 34012 } 34013 34014 try self.restoreState(loop_data.state, &.{}, .{ 34015 .emit_instructions = true, 34016 .update_tracking = false, 34017 .resurrect = false, 34018 .close_scope = false, 34019 }); 34020 34021 if (self.loop_switches.getPtr(br.block_inst)) |table| { 34022 const condition_ty = self.typeOf(br.operand); 34023 const condition_int_info = self.intInfo(condition_ty).?; 34024 const condition_int_ty = try self.pt.intType(condition_int_info.signedness, condition_int_info.bits); 34025 const condition_mcv = block_tracking.short; 34026 try self.spillEflagsIfOccupied(); 34027 if (table.min.orderAgainstZero(self.pt.zcu).compare(.neq)) try self.genBinOpMir( 34028 .{ ._, .sub }, 34029 condition_ty, 34030 condition_mcv, 34031 .{ .air_ref = Air.internedToRef(table.min.toIntern()) }, 34032 ); 34033 switch (table.else_relocs) { 34034 .@"unreachable" => {}, 34035 .forward => |*else_relocs| { 34036 var cond_temp = try self.tempInit(condition_ty, condition_mcv); 34037 var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1)); 34038 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { 34039 error.SelectFailed => unreachable, 34040 else => |e| return e, 34041 }; 34042 try cond_temp.die(self); 34043 try table_max_temp.die(self); 34044 try else_relocs.append(self.gpa, try self.asmJccReloc(cc_temp.tracking(self).short.eflags, undefined)); 34045 try cc_temp.die(self); 34046 }, 34047 .backward => |else_reloc| { 34048 var cond_temp = try self.tempInit(condition_ty, condition_mcv); 34049 var table_max_temp = try self.tempFromValue(try self.pt.intValue(condition_int_ty, table.len - 1)); 34050 const cc_temp = cond_temp.cmpInts(.gt, &table_max_temp, self) catch |err| switch (err) { 34051 error.SelectFailed => unreachable, 34052 else => |e| return e, 34053 }; 34054 try cond_temp.die(self); 34055 try table_max_temp.die(self); 34056 _ = try self.asmJccReloc(cc_temp.tracking(self).short.eflags, else_reloc); 34057 try cc_temp.die(self); 34058 }, 34059 } 34060 { 34061 const condition_index_reg = if (condition_mcv.isRegister()) condition_mcv.getReg().? else cond: { 34062 const condition_index_reg = 34063 RegisterManager.regAtTrackedIndex(@intCast(loop_data.state.free_registers.findFirstSet().?)); 34064 try self.genSetReg(condition_index_reg, condition_ty, condition_mcv, .{}); 34065 break :cond condition_index_reg; 34066 }; 34067 const condition_index_lock = self.register_manager.lockReg(condition_index_reg); 34068 defer if (condition_index_lock) |lock| self.register_manager.unlockReg(lock); 34069 try self.truncateRegister(condition_ty, condition_index_reg); 34070 const ptr_size = @divExact(self.target.ptrBitWidth(), 8); 34071 try self.asmMemory(.{ ._mp, .j }, .{ 34072 .base = .table, 34073 .mod = .{ .rm = .{ 34074 .size = .ptr, 34075 .index = registerAlias(condition_index_reg, ptr_size), 34076 .scale = .fromFactor(@intCast(ptr_size)), 34077 .disp = @intCast(table.start * ptr_size), 34078 } }, 34079 }); 34080 } 34081 34082 return self.finishAir(inst, .none, .{ br.operand, .none, .none }); 34083 } 34084 34085 // Emit a jump with a relocation. It will be patched up after the block ends. 34086 // Leave the jump offset undefined 34087 _ = try self.asmJmpReloc(loop_data.target); 34088 34089 // Stop tracking block result without forgetting tracking info 34090 try self.freeValue(block_tracking.short); 34091 } 34092 34093 fn performReloc(self: *CodeGen, reloc: Mir.Inst.Index) void { 34094 const next_inst: u32 = @intCast(self.mir_instructions.len); 34095 switch (self.mir_instructions.items(.tag)[reloc]) { 34096 .j => {}, 34097 .pseudo => switch (self.mir_instructions.items(.ops)[reloc]) { 34098 .pseudo_j_z_and_np_inst, .pseudo_j_nz_or_p_inst => {}, 34099 else => unreachable, 34100 }, 34101 else => unreachable, 34102 } 34103 self.mir_instructions.items(.data)[reloc].inst.inst = next_inst; 34104 } 34105 34106 fn airBr(self: *CodeGen, inst: Air.Inst.Index) !void { 34107 const zcu = self.pt.zcu; 34108 const br = self.air.instructions.items(.data)[@intFromEnum(inst)].br; 34109 34110 const block_ty = self.typeOfIndex(br.block_inst); 34111 const block_unused = 34112 !block_ty.hasRuntimeBitsIgnoreComptime(zcu) or self.liveness.isUnused(br.block_inst); 34113 const block_tracking = self.inst_tracking.getPtr(br.block_inst).?; 34114 const block_data = self.blocks.getPtr(br.block_inst).?; 34115 const first_br = block_data.relocs.items.len == 0; 34116 const block_result = result: { 34117 if (block_unused) break :result .none; 34118 34119 if (!first_br) try self.getValue(block_tracking.short, null); 34120 const src_mcv = try self.resolveInst(br.operand); 34121 34122 if (self.reuseOperandAdvanced(inst, br.operand, 0, src_mcv, br.block_inst)) { 34123 if (first_br) break :result src_mcv; 34124 34125 try self.getValue(block_tracking.short, br.block_inst); 34126 // .long = .none to avoid merging operand and block result stack frames. 34127 const current_tracking: InstTracking = .{ .long = .none, .short = src_mcv }; 34128 try current_tracking.materializeUnsafe(self, br.block_inst, block_tracking.*); 34129 for (current_tracking.getRegs()) |src_reg| self.register_manager.freeReg(src_reg); 34130 break :result block_tracking.short; 34131 } 34132 34133 const dst_mcv = if (first_br) try self.allocRegOrMem(br.block_inst, true) else dst: { 34134 try self.getValue(block_tracking.short, br.block_inst); 34135 break :dst block_tracking.short; 34136 }; 34137 try self.genCopy(block_ty, dst_mcv, try self.resolveInst(br.operand), .{}); 34138 break :result dst_mcv; 34139 }; 34140 34141 // Process operand death so that it is properly accounted for in the State below. 34142 if (self.liveness.operandDies(inst, 0)) { 34143 if (br.operand.toIndex()) |op_inst| try self.processDeath(op_inst); 34144 } 34145 34146 if (first_br) { 34147 block_tracking.* = .init(block_result); 34148 try self.saveRetroactiveState(&block_data.state); 34149 } else try self.restoreState(block_data.state, &.{}, .{ 34150 .emit_instructions = true, 34151 .update_tracking = false, 34152 .resurrect = false, 34153 .close_scope = false, 34154 }); 34155 34156 // Emit a jump with a relocation. It will be patched up after the block ends. 34157 // Leave the jump offset undefined 34158 const jmp_reloc = try self.asmJmpReloc(undefined); 34159 try block_data.relocs.append(self.gpa, jmp_reloc); 34160 34161 // Stop tracking block result without forgetting tracking info 34162 try self.freeValue(block_tracking.short); 34163 } 34164 34165 fn airRepeat(self: *CodeGen, inst: Air.Inst.Index) !void { 34166 const loop_inst = self.air.instructions.items(.data)[@intFromEnum(inst)].repeat.loop_inst; 34167 const repeat_info = self.loops.get(loop_inst).?; 34168 try self.restoreState(repeat_info.state, &.{}, .{ 34169 .emit_instructions = true, 34170 .update_tracking = false, 34171 .resurrect = false, 34172 .close_scope = true, 34173 }); 34174 _ = try self.asmJmpReloc(repeat_info.target); 34175 } 34176 34177 fn airAsm(self: *CodeGen, inst: Air.Inst.Index) !void { 34178 const pt = self.pt; 34179 const zcu = pt.zcu; 34180 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 34181 const extra = self.air.extraData(Air.Asm, ty_pl.payload); 34182 const clobbers_len: u31 = @truncate(extra.data.flags); 34183 var extra_i: usize = extra.end; 34184 const outputs: []const Air.Inst.Ref = 34185 @ptrCast(self.air.extra[extra_i..][0..extra.data.outputs_len]); 34186 extra_i += outputs.len; 34187 const inputs: []const Air.Inst.Ref = @ptrCast(self.air.extra[extra_i..][0..extra.data.inputs_len]); 34188 extra_i += inputs.len; 34189 34190 var result: MCValue = .none; 34191 var args: std.ArrayList(MCValue) = .init(self.gpa); 34192 try args.ensureTotalCapacity(outputs.len + inputs.len); 34193 defer { 34194 for (args.items) |arg| if (arg.getReg()) |reg| self.register_manager.unlockReg(.{ 34195 .tracked_index = RegisterManager.indexOfRegIntoTracked(reg) orelse continue, 34196 }); 34197 args.deinit(); 34198 } 34199 var arg_map: std.StringHashMap(u8) = .init(self.gpa); 34200 try arg_map.ensureTotalCapacity(@intCast(outputs.len + inputs.len)); 34201 defer arg_map.deinit(); 34202 34203 var outputs_extra_i = extra_i; 34204 for (outputs) |output| { 34205 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 34206 const constraint = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 34207 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 34208 // This equation accounts for the fact that even if we have exactly 4 bytes 34209 // for the string, we still use the next u32 for the null terminator. 34210 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 34211 34212 const maybe_inst = switch (output) { 34213 .none => inst, 34214 else => null, 34215 }; 34216 const ty = switch (output) { 34217 .none => self.typeOfIndex(inst), 34218 else => self.typeOf(output).childType(zcu), 34219 }; 34220 const is_read = switch (constraint[0]) { 34221 '=' => false, 34222 '+' => read: { 34223 if (output == .none) return self.fail( 34224 "read-write constraint unsupported for asm result: '{s}'", 34225 .{constraint}, 34226 ); 34227 break :read true; 34228 }, 34229 else => return self.fail("invalid constraint: '{s}'", .{constraint}), 34230 }; 34231 const is_early_clobber = constraint[1] == '&'; 34232 const rest = constraint[@as(usize, 1) + @intFromBool(is_early_clobber) ..]; 34233 const arg_mcv: MCValue = arg_mcv: { 34234 const arg_maybe_reg: ?Register = if (std.mem.eql(u8, rest, "r") or 34235 std.mem.eql(u8, rest, "f") or std.mem.eql(u8, rest, "x")) 34236 registerAlias( 34237 self.register_manager.tryAllocReg(maybe_inst, switch (rest[0]) { 34238 'r' => abi.RegisterClass.gp, 34239 'f' => abi.RegisterClass.x87, 34240 'x' => abi.RegisterClass.sse, 34241 else => unreachable, 34242 }) orelse return self.fail("ran out of registers lowering inline asm", .{}), 34243 @intCast(ty.abiSize(zcu)), 34244 ) 34245 else if (std.mem.eql(u8, rest, "m")) 34246 if (output != .none) null else return self.fail( 34247 "memory constraint unsupported for asm result: '{s}'", 34248 .{constraint}, 34249 ) 34250 else if (std.mem.eql(u8, rest, "g") or 34251 std.mem.eql(u8, rest, "rm") or std.mem.eql(u8, rest, "mr") or 34252 std.mem.eql(u8, rest, "r,m") or std.mem.eql(u8, rest, "m,r")) 34253 self.register_manager.tryAllocReg(maybe_inst, abi.RegisterClass.gp) orelse 34254 if (output != .none) 34255 null 34256 else 34257 return self.fail("ran out of registers lowering inline asm", .{}) 34258 else if (std.mem.startsWith(u8, rest, "{") and std.mem.endsWith(u8, rest, "}")) 34259 parseRegName(rest["{".len .. rest.len - "}".len]) orelse 34260 return self.fail("invalid register constraint: '{s}'", .{constraint}) 34261 else if (rest.len == 1 and std.ascii.isDigit(rest[0])) { 34262 const index = std.fmt.charToDigit(rest[0], 10) catch unreachable; 34263 if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{ 34264 constraint, 34265 }); 34266 break :arg_mcv args.items[index]; 34267 } else return self.fail("invalid constraint: '{s}'", .{constraint}); 34268 break :arg_mcv if (arg_maybe_reg) |reg| .{ .register = reg } else arg: { 34269 const ptr_mcv = try self.resolveInst(output); 34270 switch (ptr_mcv) { 34271 .immediate => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| 34272 break :arg ptr_mcv.deref(), 34273 .register, .register_offset, .lea_frame => break :arg ptr_mcv.deref(), 34274 else => {}, 34275 } 34276 break :arg .{ .indirect = .{ .reg = try self.copyToTmpRegister(.usize, ptr_mcv) } }; 34277 }; 34278 }; 34279 if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |tracked_index| { 34280 try self.register_manager.getRegIndex(tracked_index, if (output == .none) inst else null); 34281 _ = self.register_manager.lockRegIndexAssumeUnused(tracked_index); 34282 }; 34283 if (!std.mem.eql(u8, name, "_")) 34284 arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); 34285 args.appendAssumeCapacity(arg_mcv); 34286 if (output == .none) result = arg_mcv; 34287 if (is_read) try self.load(arg_mcv, self.typeOf(output), .{ .air_ref = output }); 34288 } 34289 34290 for (inputs) |input| { 34291 const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); 34292 const constraint = std.mem.sliceTo(input_bytes, 0); 34293 const name = std.mem.sliceTo(input_bytes[constraint.len + 1 ..], 0); 34294 // This equation accounts for the fact that even if we have exactly 4 bytes 34295 // for the string, we still use the next u32 for the null terminator. 34296 extra_i += (constraint.len + name.len + (2 + 3)) / 4; 34297 34298 const ty = self.typeOf(input); 34299 const input_mcv = try self.resolveInst(input); 34300 const arg_mcv: MCValue = if (std.mem.eql(u8, constraint, "r") or 34301 std.mem.eql(u8, constraint, "f") or std.mem.eql(u8, constraint, "x")) 34302 arg: { 34303 const rc = switch (constraint[0]) { 34304 'r' => abi.RegisterClass.gp, 34305 'f' => abi.RegisterClass.x87, 34306 'x' => abi.RegisterClass.sse, 34307 else => unreachable, 34308 }; 34309 if (input_mcv.isRegister() and 34310 rc.isSet(RegisterManager.indexOfRegIntoTracked(input_mcv.getReg().?).?)) 34311 break :arg input_mcv; 34312 const reg = try self.register_manager.allocReg(null, rc); 34313 try self.genSetReg(reg, ty, input_mcv, .{}); 34314 break :arg .{ .register = registerAlias(reg, @intCast(ty.abiSize(zcu))) }; 34315 } else if (std.mem.eql(u8, constraint, "i") or std.mem.eql(u8, constraint, "n")) 34316 switch (input_mcv) { 34317 .immediate => |imm| .{ .immediate = imm }, 34318 else => return self.fail("immediate operand requires comptime value: '{s}'", .{ 34319 constraint, 34320 }), 34321 } 34322 else if (std.mem.eql(u8, constraint, "m")) arg: { 34323 switch (input_mcv) { 34324 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| 34325 break :arg input_mcv, 34326 .indirect, .load_frame => break :arg input_mcv, 34327 .load_symbol, .load_direct, .load_got, .load_tlv => {}, 34328 else => { 34329 const temp_mcv = try self.allocTempRegOrMem(ty, false); 34330 try self.genCopy(ty, temp_mcv, input_mcv, .{}); 34331 break :arg temp_mcv; 34332 }, 34333 } 34334 const addr_reg = self.register_manager.tryAllocReg(null, abi.RegisterClass.gp) orelse { 34335 const temp_mcv = try self.allocTempRegOrMem(ty, false); 34336 try self.genCopy(ty, temp_mcv, input_mcv, .{}); 34337 break :arg temp_mcv; 34338 }; 34339 try self.genSetReg(addr_reg, .usize, input_mcv.address(), .{}); 34340 break :arg .{ .indirect = .{ .reg = addr_reg } }; 34341 } else if (std.mem.eql(u8, constraint, "g") or 34342 std.mem.eql(u8, constraint, "rm") or std.mem.eql(u8, constraint, "mr") or 34343 std.mem.eql(u8, constraint, "r,m") or std.mem.eql(u8, constraint, "m,r")) 34344 arg: { 34345 switch (input_mcv) { 34346 .register, .indirect, .load_frame => break :arg input_mcv, 34347 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |_| 34348 break :arg input_mcv, 34349 else => {}, 34350 } 34351 const temp_mcv = try self.allocTempRegOrMem(ty, true); 34352 try self.genCopy(ty, temp_mcv, input_mcv, .{}); 34353 break :arg temp_mcv; 34354 } else if (std.mem.eql(u8, constraint, "X")) 34355 input_mcv 34356 else if (std.mem.startsWith(u8, constraint, "{") and std.mem.endsWith(u8, constraint, "}")) arg: { 34357 const reg = parseRegName(constraint["{".len .. constraint.len - "}".len]) orelse 34358 return self.fail("invalid register constraint: '{s}'", .{constraint}); 34359 try self.register_manager.getReg(reg, null); 34360 try self.genSetReg(reg, ty, input_mcv, .{}); 34361 break :arg .{ .register = reg }; 34362 } else if (constraint.len == 1 and std.ascii.isDigit(constraint[0])) arg: { 34363 const index = std.fmt.charToDigit(constraint[0], 10) catch unreachable; 34364 if (index >= args.items.len) return self.fail("constraint out of bounds: '{s}'", .{constraint}); 34365 try self.genCopy(ty, args.items[index], input_mcv, .{}); 34366 break :arg args.items[index]; 34367 } else return self.fail("invalid constraint: '{s}'", .{constraint}); 34368 if (arg_mcv.getReg()) |reg| if (RegisterManager.indexOfRegIntoTracked(reg)) |_| { 34369 _ = self.register_manager.lockReg(reg); 34370 }; 34371 if (!std.mem.eql(u8, name, "_")) 34372 arg_map.putAssumeCapacityNoClobber(name, @intCast(args.items.len)); 34373 args.appendAssumeCapacity(arg_mcv); 34374 } 34375 34376 { 34377 var clobber_i: u32 = 0; 34378 while (clobber_i < clobbers_len) : (clobber_i += 1) { 34379 const clobber = std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[extra_i..]), 0); 34380 // This equation accounts for the fact that even if we have exactly 4 bytes 34381 // for the string, we still use the next u32 for the null terminator. 34382 extra_i += clobber.len / 4 + 1; 34383 34384 if (std.mem.eql(u8, clobber, "") or std.mem.eql(u8, clobber, "memory")) { 34385 // ok, sure 34386 } else if (std.mem.eql(u8, clobber, "cc") or 34387 std.mem.eql(u8, clobber, "flags") or 34388 std.mem.eql(u8, clobber, "eflags") or 34389 std.mem.eql(u8, clobber, "rflags")) 34390 { 34391 try self.spillEflagsIfOccupied(); 34392 } else { 34393 try self.register_manager.getReg(parseRegName(clobber) orelse 34394 return self.fail("invalid clobber: '{s}'", .{clobber}), null); 34395 } 34396 } 34397 } 34398 34399 const Label = struct { 34400 target: Mir.Inst.Index = undefined, 34401 pending_relocs: std.ArrayListUnmanaged(Mir.Inst.Index) = .empty, 34402 34403 const Kind = enum { definition, reference }; 34404 34405 fn isValid(kind: Kind, name: []const u8) bool { 34406 for (name, 0..) |c, i| switch (c) { 34407 else => return false, 34408 '$' => if (i == 0) return false, 34409 '.' => {}, 34410 '0'...'9' => if (i == 0) switch (kind) { 34411 .definition => if (name.len != 1) return false, 34412 .reference => { 34413 if (name.len != 2) return false; 34414 switch (name[1]) { 34415 else => return false, 34416 'B', 'F', 'b', 'f' => {}, 34417 } 34418 }, 34419 }, 34420 '@', 'A'...'Z', '_', 'a'...'z' => {}, 34421 }; 34422 return name.len > 0; 34423 } 34424 }; 34425 var labels: std.StringHashMapUnmanaged(Label) = .empty; 34426 defer { 34427 var label_it = labels.valueIterator(); 34428 while (label_it.next()) |label| label.pending_relocs.deinit(self.gpa); 34429 labels.deinit(self.gpa); 34430 } 34431 34432 const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; 34433 var line_it = std.mem.tokenizeAny(u8, asm_source, "\n\r;"); 34434 next_line: while (line_it.next()) |line| { 34435 var mnem_it = std.mem.tokenizeAny(u8, line, " \t"); 34436 var prefix: encoder.Instruction.Prefix = .none; 34437 const mnem_str = while (mnem_it.next()) |mnem_str| { 34438 if (mnem_str[0] == '#') continue :next_line; 34439 if (std.mem.startsWith(u8, mnem_str, "//")) continue :next_line; 34440 if (std.meta.stringToEnum(encoder.Instruction.Prefix, mnem_str)) |pre| { 34441 if (prefix != .none) return self.fail("extra prefix: '{s}'", .{mnem_str}); 34442 prefix = pre; 34443 continue; 34444 } 34445 if (!std.mem.endsWith(u8, mnem_str, ":")) break mnem_str; 34446 const label_name = mnem_str[0 .. mnem_str.len - ":".len]; 34447 if (!Label.isValid(.definition, label_name)) 34448 return self.fail("invalid label: '{s}'", .{label_name}); 34449 const label_gop = try labels.getOrPut(self.gpa, label_name); 34450 if (!label_gop.found_existing) label_gop.value_ptr.* = .{} else { 34451 const anon = std.ascii.isDigit(label_name[0]); 34452 if (!anon and label_gop.value_ptr.pending_relocs.items.len == 0) 34453 return self.fail("redefined label: '{s}'", .{label_name}); 34454 for (label_gop.value_ptr.pending_relocs.items) |pending_reloc| 34455 self.performReloc(pending_reloc); 34456 if (anon) 34457 label_gop.value_ptr.pending_relocs.clearRetainingCapacity() 34458 else 34459 label_gop.value_ptr.pending_relocs.clearAndFree(self.gpa); 34460 } 34461 label_gop.value_ptr.target = @intCast(self.mir_instructions.len); 34462 } else continue; 34463 if (mnem_str[0] == '.') { 34464 if (prefix != .none) return self.fail("prefixed directive: '{s} {s}'", .{ @tagName(prefix), mnem_str }); 34465 prefix = .directive; 34466 } 34467 34468 var mnem_size: struct { 34469 used: bool, 34470 size: ?Memory.Size, 34471 fn use(size: *@This()) ?Memory.Size { 34472 size.used = true; 34473 return size.size; 34474 } 34475 } = .{ 34476 .used = false, 34477 .size = if (prefix == .directive) 34478 null 34479 else if (std.mem.endsWith(u8, mnem_str, "b")) 34480 .byte 34481 else if (std.mem.endsWith(u8, mnem_str, "w")) 34482 .word 34483 else if (std.mem.endsWith(u8, mnem_str, "l")) 34484 .dword 34485 else if (std.mem.endsWith(u8, mnem_str, "q") and 34486 (std.mem.indexOfScalar(u8, "vp", mnem_str[0]) == null or !std.mem.endsWith(u8, mnem_str, "dq"))) 34487 .qword 34488 else if (std.mem.endsWith(u8, mnem_str, "t")) 34489 .tbyte 34490 else 34491 null, 34492 }; 34493 var mnem_tag = while (true) break std.meta.stringToEnum( 34494 encoder.Instruction.Mnemonic, 34495 mnem_str[0 .. mnem_str.len - @intFromBool(mnem_size.size != null)], 34496 ) orelse if (mnem_size.size) |_| { 34497 mnem_size.size = null; 34498 continue; 34499 } else return self.fail("invalid mnemonic: '{s}'", .{mnem_str}); 34500 if (@as(?Memory.Size, switch (mnem_tag) { 34501 .clflush => .byte, 34502 .fldcw, .fnstcw, .fstcw, .fnstsw, .fstsw => .word, 34503 .fldenv, .fnstenv, .fstenv => .none, 34504 .frstor, .fsave, .fnsave, .fxrstor, .fxrstor64, .fxsave, .fxsave64 => .none, 34505 .invlpg => .none, 34506 .invpcid => .xword, 34507 .ldmxcsr, .stmxcsr, .vldmxcsr, .vstmxcsr => .dword, 34508 else => null, 34509 })) |fixed_mnem_size| { 34510 if (mnem_size.size) |size| if (size != fixed_mnem_size) 34511 return self.fail("invalid size: '{s}'", .{mnem_str}); 34512 mnem_size.size = fixed_mnem_size; 34513 } 34514 34515 var ops: [4]Operand = @splat(.none); 34516 var ops_len: usize = 0; 34517 34518 var last_op = false; 34519 var op_it = std.mem.splitScalar(u8, mnem_it.rest(), ','); 34520 next_op: for (&ops) |*op| { 34521 const op_str = while (!last_op) { 34522 const full_str = op_it.next() orelse break :next_op; 34523 const code_str = if (std.mem.indexOfScalar(u8, full_str, '#') orelse 34524 std.mem.indexOf(u8, full_str, "//")) |comment| 34525 code: { 34526 last_op = true; 34527 break :code full_str[0..comment]; 34528 } else full_str; 34529 const trim_str = std.mem.trim(u8, code_str, " \t*"); 34530 if (trim_str.len > 0) break trim_str; 34531 } else break; 34532 if (std.mem.startsWith(u8, op_str, "%%")) { 34533 const colon = std.mem.indexOfScalarPos(u8, op_str, "%%".len + 2, ':'); 34534 const reg = parseRegName(op_str["%%".len .. colon orelse op_str.len]) orelse 34535 return self.fail("invalid register: '{s}'", .{op_str}); 34536 if (colon) |colon_pos| { 34537 const disp = std.fmt.parseInt(i32, op_str[colon_pos + ":".len ..], 0) catch 34538 return self.fail("invalid displacement: '{s}'", .{op_str}); 34539 op.* = .{ .mem = .{ 34540 .base = .{ .reg = reg }, 34541 .mod = .{ .rm = .{ 34542 .size = mnem_size.use() orelse 34543 return self.fail("unknown size: '{s}'", .{op_str}), 34544 .disp = disp, 34545 } }, 34546 } }; 34547 } else { 34548 if (mnem_size.use()) |size| if (reg.bitSize() != size.bitSize(self.target)) 34549 return self.fail("invalid register size: '{s}'", .{op_str}); 34550 op.* = .{ .reg = reg }; 34551 } 34552 } else if (std.mem.startsWith(u8, op_str, "%[") and std.mem.endsWith(u8, op_str, "]")) { 34553 const colon = std.mem.indexOfScalarPos(u8, op_str, "%[".len, ':'); 34554 const modifier = if (colon) |colon_pos| 34555 op_str[colon_pos + ":".len .. op_str.len - "]".len] 34556 else 34557 ""; 34558 op.* = switch (args.items[ 34559 arg_map.get(op_str["%[".len .. colon orelse op_str.len - "]".len]) orelse 34560 return self.fail("no matching constraint: '{s}'", .{op_str}) 34561 ]) { 34562 .immediate => |imm| if (std.mem.eql(u8, modifier, "") or std.mem.eql(u8, modifier, "c")) 34563 .{ .imm = .u(imm) } 34564 else 34565 return self.fail("invalid modifier: '{s}'", .{modifier}), 34566 .register => |reg| if (std.mem.eql(u8, modifier, "")) 34567 .{ .reg = if (mnem_size.use()) |size| 34568 registerAlias(reg, @intCast(@divExact(size.bitSize(self.target), 8))) 34569 else 34570 reg } 34571 else 34572 return self.fail("invalid modifier: '{s}'", .{modifier}), 34573 .memory => |addr| if (std.mem.eql(u8, modifier, "") or std.mem.eql(u8, modifier, "P")) 34574 .{ .mem = .{ 34575 .base = .{ .reg = .ds }, 34576 .mod = .{ .rm = .{ 34577 .size = mnem_size.use() orelse 34578 return self.fail("unknown size: '{s}'", .{op_str}), 34579 .disp = @intCast(@as(i64, @bitCast(addr))), 34580 } }, 34581 } } 34582 else 34583 return self.fail("invalid modifier: '{s}'", .{modifier}), 34584 .indirect => |reg_off| if (std.mem.eql(u8, modifier, "")) 34585 .{ .mem = .{ 34586 .base = .{ .reg = reg_off.reg }, 34587 .mod = .{ .rm = .{ 34588 .size = mnem_size.use() orelse 34589 return self.fail("unknown size: '{s}'", .{op_str}), 34590 .disp = reg_off.off, 34591 } }, 34592 } } 34593 else 34594 return self.fail("invalid modifier: '{s}'", .{modifier}), 34595 .load_frame => |frame_addr| if (std.mem.eql(u8, modifier, "")) 34596 .{ .mem = .{ 34597 .base = .{ .frame = frame_addr.index }, 34598 .mod = .{ .rm = .{ 34599 .size = mnem_size.use() orelse 34600 return self.fail("unknown size: '{s}'", .{op_str}), 34601 .disp = frame_addr.off, 34602 } }, 34603 } } 34604 else 34605 return self.fail("invalid modifier: '{s}'", .{modifier}), 34606 .lea_got => |sym_index| if (std.mem.eql(u8, modifier, "P")) 34607 .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_got = sym_index }) } 34608 else 34609 return self.fail("invalid modifier: '{s}'", .{modifier}), 34610 .lea_symbol => |sym_off| if (std.mem.eql(u8, modifier, "P")) 34611 .{ .reg = try self.copyToTmpRegister(.usize, .{ .lea_symbol = sym_off }) } 34612 else 34613 return self.fail("invalid modifier: '{s}'", .{modifier}), 34614 else => return self.fail("invalid constraint: '{s}'", .{op_str}), 34615 }; 34616 } else if (std.mem.startsWith(u8, op_str, "$")) { 34617 op.* = if (std.fmt.parseInt(u64, op_str["$".len..], 0)) |u| 34618 .{ .imm = .u(u) } 34619 else |_| if (std.fmt.parseInt(i32, op_str["$".len..], 0)) |s| 34620 .{ .imm = .s(s) } 34621 else |_| 34622 return self.fail("invalid immediate: '{s}'", .{op_str}); 34623 } else if (std.mem.endsWith(u8, op_str, ")")) { 34624 const open = std.mem.indexOfScalar(u8, op_str, '(') orelse 34625 return self.fail("invalid operand: '{s}'", .{op_str}); 34626 var sib_it = std.mem.splitScalar(u8, op_str[open + "(".len .. op_str.len - ")".len], ','); 34627 const base_str = sib_it.next() orelse 34628 return self.fail("invalid memory operand: '{s}'", .{op_str}); 34629 if (base_str.len > 0 and !std.mem.startsWith(u8, base_str, "%%")) 34630 return self.fail("invalid memory operand: '{s}'", .{op_str}); 34631 const index_str = sib_it.next() orelse ""; 34632 if (index_str.len > 0 and !std.mem.startsWith(u8, base_str, "%%")) 34633 return self.fail("invalid memory operand: '{s}'", .{op_str}); 34634 const scale_str = sib_it.next() orelse ""; 34635 if (index_str.len == 0 and scale_str.len > 0) 34636 return self.fail("invalid memory operand: '{s}'", .{op_str}); 34637 const scale: Memory.Scale = if (scale_str.len > 0) 34638 switch (std.fmt.parseInt(u4, scale_str, 10) catch 34639 return self.fail("invalid scale: '{s}'", .{op_str})) { 34640 1 => .@"1", 34641 2 => .@"2", 34642 4 => .@"4", 34643 8 => .@"8", 34644 else => return self.fail("invalid scale: '{s}'", .{op_str}), 34645 } 34646 else 34647 .@"1"; 34648 if (sib_it.next()) |_| return self.fail("invalid memory operand: '{s}'", .{op_str}); 34649 op.* = if (std.mem.eql(u8, base_str, "%%dx") and index_str.len == 0) .{ .reg = .dx } else .{ .mem = .{ 34650 .base = if (base_str.len > 0) 34651 .{ .reg = parseRegName(base_str["%%".len..]) orelse 34652 return self.fail("invalid base register: '{s}'", .{base_str}) } 34653 else 34654 .none, 34655 .mod = .{ .rm = .{ 34656 .size = mnem_size.use() orelse return self.fail("unknown size: '{s}'", .{op_str}), 34657 .index = if (index_str.len > 0) 34658 parseRegName(index_str["%%".len..]) orelse 34659 return self.fail("invalid index register: '{s}'", .{op_str}) 34660 else 34661 .none, 34662 .scale = scale, 34663 .disp = if (std.mem.startsWith(u8, op_str[0..open], "%[") and 34664 std.mem.endsWith(u8, op_str[0..open], "]")) 34665 disp: { 34666 const colon = std.mem.indexOfScalarPos(u8, op_str[0..open], "%[".len, ':'); 34667 const modifier = if (colon) |colon_pos| 34668 op_str[colon_pos + ":".len .. open - "]".len] 34669 else 34670 ""; 34671 break :disp switch (args.items[ 34672 arg_map.get(op_str["%[".len .. colon orelse open - "]".len]) orelse 34673 return self.fail("no matching constraint: '{s}'", .{op_str}) 34674 ]) { 34675 .immediate => |imm| if (std.mem.eql(u8, modifier, "") or 34676 std.mem.eql(u8, modifier, "c")) 34677 std.math.cast(i32, @as(i64, @bitCast(imm))) orelse 34678 return self.fail("invalid displacement: '{s}'", .{op_str}) 34679 else 34680 return self.fail("invalid modifier: '{s}'", .{modifier}), 34681 else => return self.fail("invalid constraint: '{s}'", .{op_str}), 34682 }; 34683 } else if (open > 0) 34684 std.fmt.parseInt(i32, op_str[0..open], 0) catch 34685 return self.fail("invalid displacement: '{s}'", .{op_str}) 34686 else 34687 0, 34688 } }, 34689 } }; 34690 } else if (Label.isValid(.reference, op_str)) { 34691 const anon = std.ascii.isDigit(op_str[0]); 34692 const label_gop = try labels.getOrPut(self.gpa, op_str[0..if (anon) 1 else op_str.len]); 34693 if (!label_gop.found_existing) label_gop.value_ptr.* = .{}; 34694 if (anon and (op_str[1] == 'b' or op_str[1] == 'B') and !label_gop.found_existing) 34695 return self.fail("undefined label: '{s}'", .{op_str}); 34696 const pending_relocs = &label_gop.value_ptr.pending_relocs; 34697 if (if (anon) 34698 op_str[1] == 'f' or op_str[1] == 'F' 34699 else 34700 !label_gop.found_existing or pending_relocs.items.len > 0) 34701 try pending_relocs.append(self.gpa, @intCast(self.mir_instructions.len)); 34702 op.* = .{ .inst = label_gop.value_ptr.target }; 34703 } else return self.fail("invalid operand: '{s}'", .{op_str}); 34704 ops_len += 1; 34705 } else if (op_it.next()) |op_str| return self.fail("extra operand: '{s}'", .{op_str}); 34706 34707 // convert from att syntax to intel syntax 34708 std.mem.reverse(Operand, ops[0..ops_len]); 34709 if (!mnem_size.used) if (mnem_size.size) |size| { 34710 comptime var max_mnem_len: usize = 0; 34711 inline for (@typeInfo(encoder.Instruction.Mnemonic).@"enum".fields) |mnem| 34712 max_mnem_len = @max(mnem.name.len, max_mnem_len); 34713 var intel_mnem_buf: [max_mnem_len + 1]u8 = undefined; 34714 const intel_mnem_str = std.fmt.bufPrint(&intel_mnem_buf, "{s}{c}", .{ 34715 @tagName(mnem_tag), 34716 @as(u8, switch (size) { 34717 .byte => 'b', 34718 .word => 'w', 34719 .dword => 'd', 34720 .qword => 'q', 34721 .tbyte => 't', 34722 else => unreachable, 34723 }), 34724 }) catch unreachable; 34725 if (std.meta.stringToEnum(encoder.Instruction.Mnemonic, intel_mnem_str)) |intel_mnem_tag| mnem_tag = intel_mnem_tag; 34726 }; 34727 const mnem_name = @tagName(mnem_tag); 34728 const mnem_fixed_tag: Mir.Inst.FixedTag = if (prefix == .directive) 34729 .{ ._, .pseudo } 34730 else for (std.enums.values(Mir.Inst.Fixes)) |fixes| { 34731 const fixes_name = @tagName(fixes); 34732 const space_i = std.mem.indexOfScalar(u8, fixes_name, ' '); 34733 const fixes_prefix = if (space_i) |i| 34734 std.meta.stringToEnum(encoder.Instruction.Prefix, fixes_name[0..i]).? 34735 else 34736 .none; 34737 if (fixes_prefix != prefix) continue; 34738 const pattern = fixes_name[if (space_i) |i| i + " ".len else 0..]; 34739 const wildcard_i = std.mem.indexOfScalar(u8, pattern, '_').?; 34740 const mnem_prefix = pattern[0..wildcard_i]; 34741 const mnem_suffix = pattern[wildcard_i + "_".len ..]; 34742 if (!std.mem.startsWith(u8, mnem_name, mnem_prefix)) continue; 34743 if (!std.mem.endsWith(u8, mnem_name, mnem_suffix)) continue; 34744 break .{ fixes, std.meta.stringToEnum( 34745 Mir.Inst.Tag, 34746 mnem_name[mnem_prefix.len .. mnem_name.len - mnem_suffix.len], 34747 ) orelse continue }; 34748 } else { 34749 assert(prefix != .none); // no combination of fixes produced a known mnemonic 34750 return self.fail("invalid prefix for mnemonic: '{s} {s}'", .{ 34751 @tagName(prefix), mnem_name, 34752 }); 34753 }; 34754 34755 (if (prefix == .directive) switch (mnem_tag) { 34756 .@".cfi_def_cfa" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none) 34757 self.asmPseudoRegisterImmediate(.pseudo_cfi_def_cfa_ri_s, ops[0].reg, ops[1].imm) 34758 else 34759 error.InvalidInstruction, 34760 .@".cfi_def_cfa_register" => if (ops[0] == .reg and ops[1] == .none) 34761 self.asmPseudoRegister(.pseudo_cfi_def_cfa_register_r, ops[0].reg) 34762 else 34763 error.InvalidInstruction, 34764 .@".cfi_def_cfa_offset" => if (ops[0] == .imm and ops[1] == .none) 34765 self.asmPseudoImmediate(.pseudo_cfi_def_cfa_offset_i_s, ops[0].imm) 34766 else 34767 error.InvalidInstruction, 34768 .@".cfi_adjust_cfa_offset" => if (ops[0] == .imm and ops[1] == .none) 34769 self.asmPseudoImmediate(.pseudo_cfi_adjust_cfa_offset_i_s, ops[0].imm) 34770 else 34771 error.InvalidInstruction, 34772 .@".cfi_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none) 34773 self.asmPseudoRegisterImmediate(.pseudo_cfi_offset_ri_s, ops[0].reg, ops[1].imm) 34774 else 34775 error.InvalidInstruction, 34776 .@".cfi_val_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none) 34777 self.asmPseudoRegisterImmediate(.pseudo_cfi_val_offset_ri_s, ops[0].reg, ops[1].imm) 34778 else 34779 error.InvalidInstruction, 34780 .@".cfi_rel_offset" => if (ops[0] == .reg and ops[1] == .imm and ops[2] == .none) 34781 self.asmPseudoRegisterImmediate(.pseudo_cfi_rel_offset_ri_s, ops[0].reg, ops[1].imm) 34782 else 34783 error.InvalidInstruction, 34784 .@".cfi_register" => if (ops[0] == .reg and ops[1] == .reg and ops[2] == .none) 34785 self.asmPseudoRegisterRegister(.pseudo_cfi_register_rr, ops[0].reg, ops[1].reg) 34786 else 34787 error.InvalidInstruction, 34788 .@".cfi_restore" => if (ops[0] == .reg and ops[1] == .none) 34789 self.asmPseudoRegister(.pseudo_cfi_restore_r, ops[0].reg) 34790 else 34791 error.InvalidInstruction, 34792 .@".cfi_undefined" => if (ops[0] == .reg and ops[1] == .none) 34793 self.asmPseudoRegister(.pseudo_cfi_undefined_r, ops[0].reg) 34794 else 34795 error.InvalidInstruction, 34796 .@".cfi_same_value" => if (ops[0] == .reg and ops[1] == .none) 34797 self.asmPseudoRegister(.pseudo_cfi_same_value_r, ops[0].reg) 34798 else 34799 error.InvalidInstruction, 34800 .@".cfi_remember_state" => if (ops[0] == .none) 34801 self.asmPseudo(.pseudo_cfi_remember_state_none) 34802 else 34803 error.InvalidInstruction, 34804 .@".cfi_restore_state" => if (ops[0] == .none) 34805 self.asmPseudo(.pseudo_cfi_restore_state_none) 34806 else 34807 error.InvalidInstruction, 34808 .@".cfi_escape" => error.InvalidInstruction, 34809 else => unreachable, 34810 } else self.asmOps(mnem_fixed_tag, ops)) catch |err| switch (err) { 34811 error.InvalidInstruction => return self.fail( 34812 "invalid instruction: '{s} {s} {s} {s} {s}'", 34813 .{ 34814 mnem_str, 34815 @tagName(ops[0]), 34816 @tagName(ops[1]), 34817 @tagName(ops[2]), 34818 @tagName(ops[3]), 34819 }, 34820 ), 34821 else => |e| return e, 34822 }; 34823 } 34824 34825 var label_it = labels.iterator(); 34826 while (label_it.next()) |label| if (label.value_ptr.pending_relocs.items.len > 0) 34827 return self.fail("undefined label: '{s}'", .{label.key_ptr.*}); 34828 34829 for (outputs, args.items[0..outputs.len]) |output, arg_mcv| { 34830 const extra_bytes = std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]); 34831 const constraint = 34832 std.mem.sliceTo(std.mem.sliceAsBytes(self.air.extra[outputs_extra_i..]), 0); 34833 const name = std.mem.sliceTo(extra_bytes[constraint.len + 1 ..], 0); 34834 // This equation accounts for the fact that even if we have exactly 4 bytes 34835 // for the string, we still use the next u32 for the null terminator. 34836 outputs_extra_i += (constraint.len + name.len + (2 + 3)) / 4; 34837 34838 if (output == .none) continue; 34839 if (arg_mcv != .register) continue; 34840 if (constraint.len == 2 and std.ascii.isDigit(constraint[1])) continue; 34841 try self.store(self.typeOf(output), .{ .air_ref = output }, arg_mcv, .{}); 34842 } 34843 34844 simple: { 34845 var buf: [Liveness.bpi - 1]Air.Inst.Ref = @splat(.none); 34846 var buf_index: usize = 0; 34847 for (outputs) |output| { 34848 if (output == .none) continue; 34849 34850 if (buf_index >= buf.len) break :simple; 34851 buf[buf_index] = output; 34852 buf_index += 1; 34853 } 34854 if (buf_index + inputs.len > buf.len) break :simple; 34855 @memcpy(buf[buf_index..][0..inputs.len], inputs); 34856 return self.finishAir(inst, result, buf); 34857 } 34858 var bt = self.liveness.iterateBigTomb(inst); 34859 for (outputs) |output| if (output != .none) try self.feed(&bt, output); 34860 for (inputs) |input| try self.feed(&bt, input); 34861 return self.finishAirResult(inst, result); 34862 } 34863 34864 const MoveStrategy = union(enum) { 34865 move: Mir.Inst.FixedTag, 34866 x87_load_store, 34867 insert_extract: InsertExtract, 34868 vex_insert_extract: InsertExtract, 34869 34870 const InsertExtract = struct { 34871 insert: Mir.Inst.FixedTag, 34872 extract: Mir.Inst.FixedTag, 34873 }; 34874 34875 pub fn read(strat: MoveStrategy, self: *CodeGen, dst_reg: Register, src_mem: Memory) !void { 34876 switch (strat) { 34877 .move => |tag| try self.asmRegisterMemory(tag, switch (tag[1]) { 34878 else => dst_reg, 34879 .lea => if (dst_reg.bitSize() >= 32) dst_reg else dst_reg.to32(), 34880 }, src_mem), 34881 .x87_load_store => if (dst_reg != .st0 and self.register_manager.isKnownRegFree(.st7)) { 34882 try self.asmMemory(.{ .f_, .ld }, src_mem); 34883 switch (dst_reg) { 34884 .st1, .st2, .st3, .st4, .st5, .st6 => try self.asmRegister(.{ .f_p, .st }, @enumFromInt(@intFromEnum(dst_reg) + 1)), 34885 .st7 => try self.asmOpOnly(.{ .f_cstp, .in }), 34886 else => unreachable, 34887 } 34888 } else { 34889 try self.asmRegister(.{ .f_p, .st }, dst_reg); 34890 try self.asmMemory(.{ .f_, .ld }, src_mem); 34891 switch (dst_reg) { 34892 .st0 => {}, 34893 .st1, .st2, .st3, .st4, .st5, .st6, .st7 => try self.asmRegister(.{ .f_, .xch }, dst_reg), 34894 else => unreachable, 34895 } 34896 }, 34897 .insert_extract => |ie| if (ie.insert[0] != .p_w or self.hasFeature(.sse2)) 34898 try self.asmRegisterMemoryImmediate(ie.insert, dst_reg, src_mem, .u(0)) 34899 else { 34900 const tmp_frame_index = try self.allocFrameIndex(.init(.{ 34901 .size = 16, 34902 .alignment = .@"16", 34903 })); 34904 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 34905 try self.asmRegisterMemory(.{ ._, .movzx }, tmp_reg.to32(), src_mem); 34906 try self.asmMemoryRegister(.{ ._, .mov }, .{ 34907 .base = .{ .frame = tmp_frame_index }, 34908 .mod = .{ .rm = .{ .size = .word } }, 34909 }, tmp_reg.to16()); 34910 try self.asmRegisterMemory(.{ ._ps, .mova }, dst_reg.to128(), .{ 34911 .base = .{ .frame = tmp_frame_index }, 34912 .mod = .{ .rm = .{ .size = .xword } }, 34913 }); 34914 }, 34915 .vex_insert_extract => |ie| try self.asmRegisterRegisterMemoryImmediate( 34916 ie.insert, 34917 dst_reg, 34918 dst_reg, 34919 src_mem, 34920 .u(0), 34921 ), 34922 } 34923 } 34924 pub fn write(strat: MoveStrategy, self: *CodeGen, dst_mem: Memory, src_reg: Register) !void { 34925 switch (strat) { 34926 .move => |tag| try self.asmMemoryRegister(tag, dst_mem, src_reg), 34927 .x87_load_store => if (self.register_manager.isKnownRegFree(.st7)) { 34928 try self.asmRegister(.{ .f_, .ld }, src_reg); 34929 try self.asmMemory(.{ .f_p, .st }, dst_mem); 34930 } else { 34931 switch (src_reg) { 34932 .st0 => {}, 34933 .st1, .st2, .st3, .st4, .st5, .st6, .st7 => try self.asmRegister(.{ .f_, .xch }, src_reg), 34934 else => unreachable, 34935 } 34936 try self.asmMemory(.{ .f_p, .st }, dst_mem); 34937 try self.asmMemory(.{ .f_, .ld }, dst_mem); 34938 switch (src_reg) { 34939 .st0 => {}, 34940 .st1, .st2, .st3, .st4, .st5, .st6, .st7 => try self.asmRegister(.{ .f_, .xch }, src_reg), 34941 else => unreachable, 34942 } 34943 }, 34944 .insert_extract, .vex_insert_extract => |ie| if (ie.extract[0] != .p_w or self.hasFeature(.sse4_1)) 34945 try self.asmMemoryRegisterImmediate(ie.extract, dst_mem, src_reg, .u(0)) 34946 else if (self.hasFeature(.sse2)) { 34947 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 34948 try self.asmRegisterRegisterImmediate(ie.extract, tmp_reg.to32(), src_reg.to128(), .u(0)); 34949 try self.asmMemoryRegister(.{ ._, .mov }, dst_mem, tmp_reg.to16()); 34950 } else { 34951 const tmp_frame_index = try self.allocFrameIndex(.init(.{ 34952 .size = 16, 34953 .alignment = .@"16", 34954 })); 34955 try self.asmMemoryRegister(.{ ._ps, .mova }, .{ 34956 .base = .{ .frame = tmp_frame_index }, 34957 .mod = .{ .rm = .{ .size = .xword } }, 34958 }, src_reg.to128()); 34959 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 34960 try self.asmRegisterMemory(.{ ._, .movzx }, tmp_reg.to32(), .{ 34961 .base = .{ .frame = tmp_frame_index }, 34962 .mod = .{ .rm = .{ .size = .word } }, 34963 }); 34964 try self.asmMemoryRegister(.{ ._, .mov }, dst_mem, tmp_reg.to16()); 34965 }, 34966 } 34967 } 34968 }; 34969 fn moveStrategy(self: *CodeGen, ty: Type, class: Register.Class, aligned: bool) !MoveStrategy { 34970 const pt = self.pt; 34971 const zcu = pt.zcu; 34972 switch (class) { 34973 .general_purpose, .segment => return .{ .move = .{ ._, .mov } }, 34974 .x87 => return .x87_load_store, 34975 .mmx => {}, 34976 .sse => switch (ty.zigTypeTag(zcu)) { 34977 else => { 34978 const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .other), .none); 34979 assert(std.mem.indexOfNone(abi.Class, classes, &.{ 34980 .integer, .sse, .sseup, .memory, .float, .float_combine, 34981 }) == null); 34982 const abi_size = ty.abiSize(zcu); 34983 if (abi_size < 4 or 34984 std.mem.indexOfScalar(abi.Class, classes, .integer) != null) switch (abi_size) { 34985 1 => if (self.hasFeature(.avx)) return .{ .vex_insert_extract = .{ 34986 .insert = .{ .vp_b, .insr }, 34987 .extract = .{ .vp_b, .extr }, 34988 } } else if (self.hasFeature(.sse4_2)) return .{ .insert_extract = .{ 34989 .insert = .{ .p_b, .insr }, 34990 .extract = .{ .p_b, .extr }, 34991 } }, 34992 2 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ 34993 .insert = .{ .vp_w, .insr }, 34994 .extract = .{ .vp_w, .extr }, 34995 } } else .{ .insert_extract = .{ 34996 .insert = .{ .p_w, .insr }, 34997 .extract = .{ .p_w, .extr }, 34998 } }, 34999 3...4 => return .{ .move = if (self.hasFeature(.avx)) 35000 .{ .v_d, .mov } 35001 else 35002 .{ ._d, .mov } }, 35003 5...8 => return .{ .move = if (self.hasFeature(.avx)) 35004 .{ .v_q, .mov } 35005 else 35006 .{ ._q, .mov } }, 35007 9...16 => return .{ .move = if (self.hasFeature(.avx)) 35008 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35009 else if (self.hasFeature(.sse2)) 35010 .{ if (aligned) ._dqa else ._dqu, .mov } 35011 else 35012 .{ ._ps, if (aligned) .mova else .movu } }, 35013 17...32 => if (self.hasFeature(.avx)) 35014 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35015 else => {}, 35016 } else switch (abi_size) { 35017 4 => return .{ .move = if (self.hasFeature(.avx)) 35018 .{ .v_ss, .mov } 35019 else 35020 .{ ._ss, .mov } }, 35021 5...8 => return .{ .move = if (self.hasFeature(.avx)) 35022 .{ .v_sd, .mov } 35023 else if (self.hasFeature(.sse2)) 35024 .{ ._sd, .mov } 35025 else 35026 .{ ._ps, .movl } }, 35027 9...16 => return .{ .move = if (self.hasFeature(.avx)) 35028 .{ .v_pd, if (aligned) .mova else .movu } 35029 else if (self.hasFeature(.sse2)) 35030 .{ ._pd, if (aligned) .mova else .movu } 35031 else 35032 .{ ._ps, if (aligned) .mova else .movu } }, 35033 17...32 => if (self.hasFeature(.avx)) 35034 return .{ .move = .{ .v_pd, if (aligned) .mova else .movu } }, 35035 else => {}, 35036 } 35037 }, 35038 .float => switch (ty.floatBits(self.target.*)) { 35039 16 => return if (self.hasFeature(.avx)) .{ .vex_insert_extract = .{ 35040 .insert = .{ .vp_w, .insr }, 35041 .extract = .{ .vp_w, .extr }, 35042 } } else .{ .insert_extract = .{ 35043 .insert = .{ .p_w, .insr }, 35044 .extract = .{ .p_w, .extr }, 35045 } }, 35046 32 => return .{ .move = if (self.hasFeature(.avx)) 35047 .{ .v_ss, .mov } 35048 else 35049 .{ ._ss, .mov } }, 35050 64 => return .{ .move = if (self.hasFeature(.avx)) 35051 .{ .v_sd, .mov } 35052 else if (self.hasFeature(.sse2)) 35053 .{ ._sd, .mov } 35054 else 35055 .{ ._ps, .movl } }, 35056 128 => return .{ .move = if (self.hasFeature(.avx)) 35057 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35058 else if (self.hasFeature(.sse2)) 35059 .{ if (aligned) ._dqa else ._dqu, .mov } 35060 else 35061 .{ ._ps, if (aligned) .mova else .movu } }, 35062 else => {}, 35063 }, 35064 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 35065 .bool => switch (ty.vectorLen(zcu)) { 35066 33...64 => return .{ .move = if (self.hasFeature(.avx)) 35067 .{ .v_q, .mov } 35068 else 35069 .{ ._q, .mov } }, 35070 else => {}, 35071 }, 35072 .int => switch (ty.childType(zcu).intInfo(zcu).bits) { 35073 1...8 => switch (ty.vectorLen(zcu)) { 35074 1...16 => return .{ .move = if (self.hasFeature(.avx)) 35075 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35076 else if (self.hasFeature(.sse2)) 35077 .{ if (aligned) ._dqa else ._dqu, .mov } 35078 else 35079 .{ ._ps, if (aligned) .mova else .movu } }, 35080 17...32 => if (self.hasFeature(.avx)) 35081 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35082 else => {}, 35083 }, 35084 9...16 => switch (ty.vectorLen(zcu)) { 35085 1...8 => return .{ .move = if (self.hasFeature(.avx)) 35086 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35087 else if (self.hasFeature(.sse2)) 35088 .{ if (aligned) ._dqa else ._dqu, .mov } 35089 else 35090 .{ ._ps, if (aligned) .mova else .movu } }, 35091 9...16 => if (self.hasFeature(.avx)) 35092 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35093 else => {}, 35094 }, 35095 17...32 => switch (ty.vectorLen(zcu)) { 35096 1...4 => return .{ .move = if (self.hasFeature(.avx)) 35097 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35098 else if (self.hasFeature(.sse2)) 35099 .{ if (aligned) ._dqa else ._dqu, .mov } 35100 else 35101 .{ ._ps, if (aligned) .mova else .movu } }, 35102 5...8 => if (self.hasFeature(.avx)) 35103 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35104 else => {}, 35105 }, 35106 33...64 => switch (ty.vectorLen(zcu)) { 35107 1...2 => return .{ .move = if (self.hasFeature(.avx)) 35108 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35109 else if (self.hasFeature(.sse2)) 35110 .{ if (aligned) ._dqa else ._dqu, .mov } 35111 else 35112 .{ ._ps, if (aligned) .mova else .movu } }, 35113 3...4 => if (self.hasFeature(.avx)) 35114 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35115 else => {}, 35116 }, 35117 65...128 => switch (ty.vectorLen(zcu)) { 35118 1 => return .{ .move = if (self.hasFeature(.avx)) 35119 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35120 else if (self.hasFeature(.sse2)) 35121 .{ if (aligned) ._dqa else ._dqu, .mov } 35122 else 35123 .{ ._ps, if (aligned) .mova else .movu } }, 35124 2 => if (self.hasFeature(.avx)) 35125 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35126 else => {}, 35127 }, 35128 129...256 => switch (ty.vectorLen(zcu)) { 35129 1 => if (self.hasFeature(.avx)) 35130 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35131 else => {}, 35132 }, 35133 else => {}, 35134 }, 35135 .pointer, .optional => if (ty.childType(zcu).isPtrAtRuntime(zcu)) 35136 switch (ty.vectorLen(zcu)) { 35137 1...2 => return .{ .move = if (self.hasFeature(.avx)) 35138 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35139 else if (self.hasFeature(.sse2)) 35140 .{ if (aligned) ._dqa else ._dqu, .mov } 35141 else 35142 .{ ._ps, if (aligned) .mova else .movu } }, 35143 3...4 => if (self.hasFeature(.avx)) 35144 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35145 else => {}, 35146 } 35147 else 35148 unreachable, 35149 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 35150 16 => switch (ty.vectorLen(zcu)) { 35151 1...8 => return .{ .move = if (self.hasFeature(.avx)) 35152 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35153 else if (self.hasFeature(.sse2)) 35154 .{ if (aligned) ._dqa else ._dqu, .mov } 35155 else 35156 .{ ._ps, if (aligned) .mova else .movu } }, 35157 9...16 => if (self.hasFeature(.avx)) 35158 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35159 else => {}, 35160 }, 35161 32 => switch (ty.vectorLen(zcu)) { 35162 1...4 => return .{ .move = if (self.hasFeature(.avx)) 35163 .{ .v_ps, if (aligned) .mova else .movu } 35164 else 35165 .{ ._ps, if (aligned) .mova else .movu } }, 35166 5...8 => if (self.hasFeature(.avx)) 35167 return .{ .move = .{ .v_ps, if (aligned) .mova else .movu } }, 35168 else => {}, 35169 }, 35170 64 => switch (ty.vectorLen(zcu)) { 35171 1...2 => return .{ .move = if (self.hasFeature(.avx)) 35172 .{ .v_pd, if (aligned) .mova else .movu } 35173 else if (self.hasFeature(.sse2)) 35174 .{ ._pd, if (aligned) .mova else .movu } 35175 else 35176 .{ ._ps, if (aligned) .mova else .movu } }, 35177 3...4 => if (self.hasFeature(.avx)) 35178 return .{ .move = .{ .v_pd, if (aligned) .mova else .movu } }, 35179 else => {}, 35180 }, 35181 80, 128 => switch (ty.vectorLen(zcu)) { 35182 1 => return .{ .move = if (self.hasFeature(.avx)) 35183 .{ if (aligned) .v_dqa else .v_dqu, .mov } 35184 else if (self.hasFeature(.sse2)) 35185 .{ if (aligned) ._dqa else ._dqu, .mov } 35186 else 35187 .{ ._ps, if (aligned) .mova else .movu } }, 35188 2 => if (self.hasFeature(.avx)) 35189 return .{ .move = .{ if (aligned) .v_dqa else .v_dqu, .mov } }, 35190 else => {}, 35191 }, 35192 else => {}, 35193 }, 35194 else => {}, 35195 }, 35196 }, 35197 .ip, .cr, .dr => {}, 35198 } 35199 return self.fail("TODO moveStrategy for {}", .{ty.fmt(pt)}); 35200 } 35201 35202 const CopyOptions = struct { 35203 safety: bool = false, 35204 }; 35205 35206 fn genCopy(self: *CodeGen, ty: Type, dst_mcv: MCValue, src_mcv: MCValue, opts: CopyOptions) InnerError!void { 35207 const pt = self.pt; 35208 35209 const src_lock = if (src_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 35210 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 35211 35212 switch (dst_mcv) { 35213 .none, 35214 .unreach, 35215 .dead, 35216 .undef, 35217 .immediate, 35218 .eflags, 35219 .register_overflow, 35220 .register_mask, 35221 .lea_direct, 35222 .lea_got, 35223 .lea_tlv, 35224 .lea_frame, 35225 .lea_symbol, 35226 .elementwise_regs_then_frame, 35227 .reserved_frame, 35228 .air_ref, 35229 => unreachable, // unmodifiable destination 35230 .register => |reg| try self.genSetReg(reg, ty, src_mcv, opts), 35231 .register_offset => |dst_reg_off| try self.genSetReg(dst_reg_off.reg, ty, switch (src_mcv) { 35232 .none, 35233 .unreach, 35234 .dead, 35235 .undef, 35236 .register_overflow, 35237 .elementwise_regs_then_frame, 35238 .reserved_frame, 35239 => unreachable, 35240 .immediate, 35241 .register, 35242 .register_offset, 35243 .lea_frame, 35244 => src_mcv.offset(-dst_reg_off.off), 35245 else => .{ .register_offset = .{ 35246 .reg = try self.copyToTmpRegister(ty, src_mcv), 35247 .off = -dst_reg_off.off, 35248 } }, 35249 }, opts), 35250 inline .register_pair, .register_triple, .register_quadruple => |dst_regs, dst_tag| { 35251 const src_info: ?struct { addr_reg: Register, addr_lock: RegisterLock } = src_info: switch (src_mcv) { 35252 .undef, .memory, .indirect, .load_frame => null, 35253 .register => |src_reg| switch (dst_regs[0].class()) { 35254 .general_purpose => switch (src_reg.class()) { 35255 else => unreachable, 35256 .sse => if (ty.abiSize(pt.zcu) <= 16) { 35257 if (self.hasFeature(.avx)) { 35258 try self.asmRegisterRegister(.{ .v_q, .mov }, dst_regs[0].to64(), src_reg.to128()); 35259 try self.asmRegisterRegisterImmediate(.{ .vp_q, .extr }, dst_regs[1].to64(), src_reg.to128(), .u(1)); 35260 } else if (self.hasFeature(.sse4_1)) { 35261 try self.asmRegisterRegister(.{ ._q, .mov }, dst_regs[0].to64(), src_reg.to128()); 35262 try self.asmRegisterRegisterImmediate(.{ .p_q, .extr }, dst_regs[1].to64(), src_reg.to128(), .u(1)); 35263 } else { 35264 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.sse); 35265 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 35266 defer self.register_manager.unlockReg(tmp_lock); 35267 35268 try self.asmRegisterRegister(.{ ._q, .mov }, dst_regs[0].to64(), src_reg.to128()); 35269 try self.asmRegisterRegister(.{ ._ps, .movhl }, tmp_reg.to128(), src_reg.to128()); 35270 try self.asmRegisterRegister(.{ ._q, .mov }, dst_regs[1].to64(), tmp_reg.to128()); 35271 } 35272 return; 35273 } else unreachable, 35274 }, 35275 else => unreachable, 35276 }, 35277 dst_tag => |src_regs| { 35278 var hazard_regs = src_regs; 35279 for (dst_regs, &hazard_regs, 1..) |dst_reg, src_reg, hazard_index| { 35280 const dst_id = dst_reg.id(); 35281 if (dst_id == src_reg.id()) continue; 35282 var mir_tag: Mir.Inst.FixedTag = .{ ._, .mov }; 35283 for (hazard_regs[hazard_index..]) |*hazard_reg| { 35284 if (dst_id != hazard_reg.id()) continue; 35285 mir_tag = .{ ._g, .xch }; 35286 hazard_reg.* = src_reg; 35287 } 35288 try self.asmRegisterRegister(mir_tag, dst_reg.to64(), src_reg.to64()); 35289 } 35290 return; 35291 }, 35292 .load_symbol, .load_direct, .load_got, .load_tlv => { 35293 const src_addr_reg = 35294 (try self.register_manager.allocReg(null, abi.RegisterClass.gp)).to64(); 35295 const src_addr_lock = self.register_manager.lockRegAssumeUnused(src_addr_reg); 35296 errdefer self.register_manager.unlockReg(src_addr_lock); 35297 35298 try self.genSetReg(src_addr_reg, .usize, src_mcv.address(), opts); 35299 break :src_info .{ .addr_reg = src_addr_reg, .addr_lock = src_addr_lock }; 35300 }, 35301 .air_ref => |src_ref| return self.genCopy(ty, dst_mcv, try self.resolveInst(src_ref), opts), 35302 else => return self.fail("TODO implement genCopy for {s} of {}", .{ 35303 @tagName(src_mcv), ty.fmt(pt), 35304 }), 35305 }; 35306 defer if (src_info) |info| self.register_manager.unlockReg(info.addr_lock); 35307 35308 for ([_]bool{ false, true }) |emit_hazard| { 35309 var hazard_count: u3 = 0; 35310 var part_disp: i32 = 0; 35311 for (dst_regs, try self.splitType(dst_regs.len, ty), 0..) |dst_reg, dst_ty, part_i| { 35312 defer part_disp += @intCast(dst_ty.abiSize(pt.zcu)); 35313 const is_hazard = if (src_mcv.getReg()) |src_reg| 35314 dst_reg.id() == src_reg.id() 35315 else if (src_info) |info| 35316 dst_reg.id() == info.addr_reg.id() 35317 else 35318 false; 35319 if (is_hazard) hazard_count += 1; 35320 if (is_hazard != emit_hazard) continue; 35321 try self.genSetReg(dst_reg, dst_ty, switch (src_mcv) { 35322 .undef => if (opts.safety and part_i > 0) .{ .register = dst_regs[0] } else .undef, 35323 dst_tag => |src_regs| .{ .register = src_regs[part_i] }, 35324 .memory, .indirect, .load_frame => src_mcv.address().offset(part_disp).deref(), 35325 .load_symbol, .load_direct, .load_got, .load_tlv => .{ .indirect = .{ 35326 .reg = src_info.?.addr_reg, 35327 .off = part_disp, 35328 } }, 35329 else => unreachable, 35330 }, opts); 35331 } 35332 switch (hazard_count) { 35333 0 => break, 35334 1 => continue, 35335 else => unreachable, 35336 } 35337 } 35338 }, 35339 .indirect => |reg_off| try self.genSetMem( 35340 .{ .reg = reg_off.reg }, 35341 reg_off.off, 35342 ty, 35343 src_mcv, 35344 opts, 35345 ), 35346 .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { 35347 switch (dst_mcv) { 35348 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| 35349 return self.genSetMem(.{ .reg = .ds }, small_addr, ty, src_mcv, opts), 35350 .load_symbol, .load_direct, .load_got, .load_tlv => {}, 35351 else => unreachable, 35352 } 35353 35354 const addr_reg = try self.copyToTmpRegister(.usize, dst_mcv.address()); 35355 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 35356 defer self.register_manager.unlockReg(addr_lock); 35357 35358 try self.genSetMem(.{ .reg = addr_reg }, 0, ty, src_mcv, opts); 35359 }, 35360 .load_frame => |frame_addr| try self.genSetMem( 35361 .{ .frame = frame_addr.index }, 35362 frame_addr.off, 35363 ty, 35364 src_mcv, 35365 opts, 35366 ), 35367 } 35368 } 35369 35370 fn genSetReg( 35371 self: *CodeGen, 35372 dst_reg: Register, 35373 ty: Type, 35374 src_mcv: MCValue, 35375 opts: CopyOptions, 35376 ) InnerError!void { 35377 const pt = self.pt; 35378 const zcu = pt.zcu; 35379 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 35380 if (ty.bitSize(zcu) > dst_reg.bitSize()) 35381 return self.fail("genSetReg called with a value larger than dst_reg", .{}); 35382 switch (src_mcv) { 35383 .none, 35384 .unreach, 35385 .dead, 35386 .register_overflow, 35387 .elementwise_regs_then_frame, 35388 .reserved_frame, 35389 => unreachable, 35390 .undef => if (opts.safety) switch (dst_reg.class()) { 35391 .general_purpose => switch (abi_size) { 35392 1 => try self.asmRegisterImmediate(.{ ._, .mov }, dst_reg.to8(), .u(0xaa)), 35393 2 => try self.asmRegisterImmediate(.{ ._, .mov }, dst_reg.to16(), .u(0xaaaa)), 35394 3...4 => try self.asmRegisterImmediate( 35395 .{ ._, .mov }, 35396 dst_reg.to32(), 35397 .s(@as(i32, @bitCast(@as(u32, 0xaaaaaaaa)))), 35398 ), 35399 5...8 => try self.asmRegisterImmediate( 35400 .{ ._, .mov }, 35401 dst_reg.to64(), 35402 .u(0xaaaaaaaaaaaaaaaa), 35403 ), 35404 else => unreachable, 35405 }, 35406 .segment, .mmx, .sse => { 35407 const full_ty = try pt.vectorType(.{ 35408 .len = self.vectorSize(.float), 35409 .child = .u8_type, 35410 }); 35411 try self.genSetReg(dst_reg, full_ty, try self.genTypedValue( 35412 .fromInterned(try pt.intern(.{ .aggregate = .{ 35413 .ty = full_ty.toIntern(), 35414 .storage = .{ .repeated_elem = (try pt.intValue(.u8, 0xaa)).toIntern() }, 35415 } })), 35416 ), opts); 35417 }, 35418 .x87 => try self.genSetReg(dst_reg, .f80, try self.genTypedValue( 35419 try pt.floatValue(.f80, @as(f80, @bitCast(@as(u80, 0xaaaaaaaaaaaaaaaaaaaa)))), 35420 ), opts), 35421 .ip, .cr, .dr => unreachable, 35422 }, 35423 .eflags => |cc| try self.asmSetccRegister(cc, dst_reg.to8()), 35424 .immediate => |imm| { 35425 if (imm == 0) { 35426 // 32-bit moves zero-extend to 64-bit, so xoring the 32-bit 35427 // register is the fastest way to zero a register. 35428 try self.spillEflagsIfOccupied(); 35429 try self.asmRegisterRegister(.{ ._, .xor }, dst_reg.to32(), dst_reg.to32()); 35430 } else if (abi_size > 4 and std.math.cast(u32, imm) != null) { 35431 // 32-bit moves zero-extend to 64-bit. 35432 try self.asmRegisterImmediate(.{ ._, .mov }, dst_reg.to32(), .u(imm)); 35433 } else if (abi_size <= 4 and @as(i64, @bitCast(imm)) < 0) { 35434 try self.asmRegisterImmediate( 35435 .{ ._, .mov }, 35436 registerAlias(dst_reg, abi_size), 35437 .s(@intCast(@as(i64, @bitCast(imm)))), 35438 ); 35439 } else { 35440 try self.asmRegisterImmediate( 35441 .{ ._, .mov }, 35442 registerAlias(dst_reg, abi_size), 35443 .u(imm), 35444 ); 35445 } 35446 }, 35447 .register => |src_reg| if (dst_reg.id() != src_reg.id()) switch (dst_reg.class()) { 35448 .general_purpose => switch (src_reg.class()) { 35449 .general_purpose => try self.asmRegisterRegister( 35450 .{ ._, .mov }, 35451 registerAlias(dst_reg, abi_size), 35452 registerAlias(src_reg, abi_size), 35453 ), 35454 .segment => try self.asmRegisterRegister( 35455 .{ ._, .mov }, 35456 registerAlias(dst_reg, abi_size), 35457 src_reg, 35458 ), 35459 .x87, .mmx, .ip, .cr, .dr => unreachable, 35460 .sse => if (self.hasFeature(.sse2)) try self.asmRegisterRegister( 35461 switch (abi_size) { 35462 1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, 35463 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov }, 35464 else => unreachable, 35465 }, 35466 registerAlias(dst_reg, @max(abi_size, 4)), 35467 src_reg.to128(), 35468 ) else { 35469 const frame_size = std.math.ceilPowerOfTwoAssert(u32, @max(abi_size, 4)); 35470 const frame_index = try self.allocFrameIndex(.init(.{ 35471 .size = frame_size, 35472 .alignment = .fromNonzeroByteUnits(frame_size), 35473 })); 35474 try self.asmMemoryRegister(switch (frame_size) { 35475 4 => .{ ._ss, .mov }, 35476 8 => .{ ._ps, .movl }, 35477 16 => .{ ._ps, .mov }, 35478 else => unreachable, 35479 }, .{ 35480 .base = .{ .frame = frame_index }, 35481 .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, 35482 }, src_reg.to128()); 35483 try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(dst_reg, abi_size), .{ 35484 .base = .{ .frame = frame_index }, 35485 .mod = .{ .rm = .{ .size = .fromSize(abi_size) } }, 35486 }); 35487 }, 35488 }, 35489 .segment => try self.asmRegisterRegister( 35490 .{ ._, .mov }, 35491 dst_reg, 35492 switch (src_reg.class()) { 35493 .general_purpose, .segment => registerAlias(src_reg, abi_size), 35494 .x87, .mmx, .ip, .cr, .dr => unreachable, 35495 .sse => try self.copyToTmpRegister(ty, src_mcv), 35496 }, 35497 ), 35498 .x87 => switch (src_reg.class()) { 35499 .general_purpose, .segment => unreachable, 35500 .x87 => switch (src_reg) { 35501 .st0 => try self.asmRegister(.{ .f_, .st }, dst_reg), 35502 .st1, .st2, .st3, .st4, .st5, .st6 => switch (dst_reg) { 35503 .st0 => { 35504 try self.asmRegister(.{ .f_p, .st }, .st0); 35505 try self.asmRegister(.{ .f_, .ld }, @enumFromInt(@intFromEnum(src_reg) - 1)); 35506 }, 35507 .st2, .st3, .st4, .st5, .st6 => if (self.register_manager.isKnownRegFree(.st7)) { 35508 try self.asmRegister(.{ .f_, .ld }, src_reg); 35509 try self.asmRegister(.{ .f_p, .st }, @enumFromInt(@intFromEnum(dst_reg) + 1)); 35510 } else { 35511 try self.asmRegister(.{ .f_, .xch }, src_reg); 35512 try self.asmRegister(.{ .f_, .xch }, dst_reg); 35513 try self.asmRegister(.{ .f_, .xch }, src_reg); 35514 }, 35515 .st7 => { 35516 if (!self.register_manager.isKnownRegFree(.st7)) try self.asmRegister(.{ .f_, .free }, dst_reg); 35517 try self.asmRegister(.{ .f_, .ld }, src_reg); 35518 try self.asmOpOnly(.{ .f_cstp, .in }); 35519 }, 35520 else => unreachable, 35521 }, 35522 else => unreachable, 35523 }, 35524 .mmx, .sse, .ip, .cr, .dr => unreachable, 35525 }, 35526 .mmx => unreachable, 35527 .sse => switch (src_reg.class()) { 35528 .general_purpose => if (self.hasFeature(.sse2)) try self.asmRegisterRegister( 35529 switch (abi_size) { 35530 1...4 => if (self.hasFeature(.avx)) .{ .v_d, .mov } else .{ ._d, .mov }, 35531 5...8 => if (self.hasFeature(.avx)) .{ .v_q, .mov } else .{ ._q, .mov }, 35532 else => unreachable, 35533 }, 35534 dst_reg.to128(), 35535 registerAlias(src_reg, @max(abi_size, 4)), 35536 ) else { 35537 const frame_size = std.math.ceilPowerOfTwoAssert(u32, @max(abi_size, 4)); 35538 const frame_index = try self.allocFrameIndex(.init(.{ 35539 .size = frame_size, 35540 .alignment = .fromNonzeroByteUnits(frame_size), 35541 })); 35542 try self.asmMemoryRegister(.{ ._, .mov }, .{ 35543 .base = .{ .frame = frame_index }, 35544 .mod = .{ .rm = .{ .size = .fromSize(abi_size) } }, 35545 }, registerAlias(src_reg, abi_size)); 35546 switch (frame_size) { 35547 else => {}, 35548 8 => try self.asmRegisterRegister(.{ ._ps, .xor }, dst_reg.to128(), dst_reg.to128()), 35549 } 35550 try self.asmRegisterMemory(switch (frame_size) { 35551 4 => .{ ._ss, .mov }, 35552 8 => .{ ._ps, .movl }, 35553 16 => .{ ._ps, .mova }, 35554 else => unreachable, 35555 }, dst_reg.to128(), .{ 35556 .base = .{ .frame = frame_index }, 35557 .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, 35558 }); 35559 }, 35560 .segment => try self.genSetReg( 35561 dst_reg, 35562 ty, 35563 .{ .register = try self.copyToTmpRegister(ty, src_mcv) }, 35564 opts, 35565 ), 35566 .x87 => { 35567 const frame_index = try self.allocFrameIndex(.init(.{ 35568 .size = 16, 35569 .alignment = .@"16", 35570 })); 35571 try MoveStrategy.write(.x87_load_store, self, .{ 35572 .base = .{ .frame = frame_index }, 35573 .mod = .{ .rm = .{ .size = .tbyte } }, 35574 }, src_reg); 35575 try self.asmRegisterMemory(if (self.hasFeature(.avx)) 35576 .{ .v_dqa, .mov } 35577 else if (self.hasFeature(.sse2)) 35578 .{ ._dqa, .mov } 35579 else 35580 .{ ._ps, .mova }, dst_reg.to128(), .{ 35581 .base = .{ .frame = frame_index }, 35582 .mod = .{ .rm = .{ .size = .xword } }, 35583 }); 35584 }, 35585 .mmx, .ip, .cr, .dr => unreachable, 35586 .sse => try self.asmRegisterRegister( 35587 @as(?Mir.Inst.FixedTag, switch (ty.scalarType(zcu).zigTypeTag(zcu)) { 35588 else => switch (abi_size) { 35589 1...16 => if (self.hasFeature(.avx)) 35590 .{ .v_dqa, .mov } 35591 else if (self.hasFeature(.sse2)) 35592 .{ ._dqa, .mov } 35593 else 35594 .{ ._ps, .mova }, 35595 17...32 => if (self.hasFeature(.avx)) .{ .v_dqa, .mov } else null, 35596 else => null, 35597 }, 35598 .float => switch (ty.scalarType(zcu).floatBits(self.target.*)) { 35599 16, 128 => switch (abi_size) { 35600 2...16 => if (self.hasFeature(.avx)) 35601 .{ .v_dqa, .mov } 35602 else if (self.hasFeature(.sse2)) 35603 .{ ._dqa, .mov } 35604 else 35605 .{ ._ps, .mova }, 35606 17...32 => if (self.hasFeature(.avx)) .{ .v_dqa, .mov } else null, 35607 else => null, 35608 }, 35609 32 => if (self.hasFeature(.avx)) .{ .v_ps, .mova } else .{ ._ps, .mova }, 35610 64 => if (self.hasFeature(.avx)) 35611 .{ .v_pd, .mova } 35612 else if (self.hasFeature(.sse2)) 35613 .{ ._pd, .mova } 35614 else 35615 .{ ._ps, .mova }, 35616 80 => null, 35617 else => unreachable, 35618 }, 35619 }) orelse return self.fail("TODO implement genSetReg for {}", .{ty.fmt(pt)}), 35620 registerAlias(dst_reg, abi_size), 35621 registerAlias(src_reg, abi_size), 35622 ), 35623 }, 35624 .ip, .cr, .dr => unreachable, 35625 }, 35626 inline .register_pair, 35627 .register_triple, 35628 .register_quadruple, 35629 => |src_regs| switch (dst_reg.class()) { 35630 .general_purpose => switch (src_regs[0].class()) { 35631 .general_purpose => try self.genSetReg(dst_reg, ty, .{ .register = src_regs[0] }, opts), 35632 else => unreachable, 35633 }, 35634 .sse => switch (src_regs[0].class()) { 35635 .general_purpose => if (abi_size <= 16) { 35636 if (self.hasFeature(.avx)) { 35637 try self.asmRegisterRegister(.{ .v_q, .mov }, dst_reg.to128(), src_regs[0].to64()); 35638 try self.asmRegisterRegisterRegisterImmediate( 35639 .{ .vp_q, .insr }, 35640 dst_reg.to128(), 35641 dst_reg.to128(), 35642 src_regs[1].to64(), 35643 .u(1), 35644 ); 35645 } else if (self.hasFeature(.sse4_1)) { 35646 try self.asmRegisterRegister(.{ ._q, .mov }, dst_reg.to128(), src_regs[0].to64()); 35647 try self.asmRegisterRegisterImmediate(.{ .p_q, .insr }, dst_reg.to128(), src_regs[1].to64(), .u(1)); 35648 } else { 35649 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.sse); 35650 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 35651 defer self.register_manager.unlockReg(tmp_lock); 35652 35653 try self.asmRegisterRegister(.{ ._q, .mov }, dst_reg.to128(), src_regs[0].to64()); 35654 try self.asmRegisterRegister(.{ ._q, .mov }, tmp_reg.to128(), src_regs[1].to64()); 35655 try self.asmRegisterRegister(.{ ._ps, .movlh }, dst_reg.to128(), tmp_reg.to128()); 35656 } 35657 } else unreachable, 35658 else => unreachable, 35659 }, 35660 else => unreachable, 35661 }, 35662 .register_offset, 35663 .indirect, 35664 .load_frame, 35665 .lea_frame, 35666 => try @as(MoveStrategy, switch (src_mcv) { 35667 .register_offset => |reg_off| switch (reg_off.off) { 35668 0 => return self.genSetReg(dst_reg, ty, .{ .register = reg_off.reg }, opts), 35669 else => .{ .move = .{ ._, .lea } }, 35670 }, 35671 .indirect => try self.moveStrategy(ty, dst_reg.class(), false), 35672 .load_frame => |frame_addr| try self.moveStrategy( 35673 ty, 35674 dst_reg.class(), 35675 self.getFrameAddrAlignment(frame_addr).compare(.gte, .fromLog2Units( 35676 std.math.log2_int_ceil(u10, @divExact(dst_reg.bitSize(), 8)), 35677 )), 35678 ), 35679 .lea_frame => .{ .move = .{ ._, .lea } }, 35680 else => unreachable, 35681 }).read(self, registerAlias(dst_reg, abi_size), switch (src_mcv) { 35682 .register_offset, .indirect => |reg_off| .{ 35683 .base = .{ .reg = reg_off.reg.to64() }, 35684 .mod = .{ .rm = .{ 35685 .size = self.memSize(ty), 35686 .disp = reg_off.off, 35687 } }, 35688 }, 35689 .load_frame, .lea_frame => |frame_addr| .{ 35690 .base = .{ .frame = frame_addr.index }, 35691 .mod = .{ .rm = .{ 35692 .size = self.memSize(ty), 35693 .disp = frame_addr.off, 35694 } }, 35695 }, 35696 else => unreachable, 35697 }), 35698 .register_mask => |src_reg_mask| { 35699 assert(src_reg_mask.reg.class() == .sse); 35700 const has_avx = self.hasFeature(.avx); 35701 const bits_reg = switch (dst_reg.class()) { 35702 .general_purpose => dst_reg, 35703 else => try self.register_manager.allocReg(null, abi.RegisterClass.gp), 35704 }; 35705 const bits_lock = self.register_manager.lockReg(bits_reg); 35706 defer if (bits_lock) |lock| self.register_manager.unlockReg(lock); 35707 35708 const pack_reg = switch (src_reg_mask.info.scalar) { 35709 else => src_reg_mask.reg, 35710 .word => try self.register_manager.allocReg(null, abi.RegisterClass.sse), 35711 }; 35712 const pack_lock = self.register_manager.lockReg(pack_reg); 35713 defer if (pack_lock) |lock| self.register_manager.unlockReg(lock); 35714 35715 var mask_size: u32 = @intCast(ty.vectorLen(zcu) * @divExact(src_reg_mask.info.scalar.bitSize(self.target), 8)); 35716 switch (src_reg_mask.info.scalar) { 35717 else => {}, 35718 .word => { 35719 const src_alias = registerAlias(src_reg_mask.reg, mask_size); 35720 const pack_alias = registerAlias(pack_reg, mask_size); 35721 if (has_avx) { 35722 try self.asmRegisterRegisterRegister(.{ .vp_b, .ackssw }, pack_alias, src_alias, src_alias); 35723 } else { 35724 try self.asmRegisterRegister(.{ ._dqa, .mov }, pack_alias, src_alias); 35725 try self.asmRegisterRegister(.{ .p_b, .ackssw }, pack_alias, pack_alias); 35726 } 35727 mask_size = std.math.divCeil(u32, mask_size, 2) catch unreachable; 35728 }, 35729 } 35730 try self.asmRegisterRegister(.{ switch (src_reg_mask.info.scalar) { 35731 .byte, .word => if (has_avx) .vp_b else .p_b, 35732 .dword => if (has_avx) .v_ps else ._ps, 35733 .qword => if (has_avx) .v_pd else ._pd, 35734 else => unreachable, 35735 }, .movmsk }, bits_reg.to32(), registerAlias(pack_reg, mask_size)); 35736 if (src_reg_mask.info.inverted) try self.asmRegister(.{ ._, .not }, registerAlias(bits_reg, abi_size)); 35737 try self.genSetReg(dst_reg, ty, .{ .register = bits_reg }, .{}); 35738 }, 35739 .memory, .load_symbol, .load_direct, .load_got, .load_tlv => { 35740 switch (src_mcv) { 35741 .memory => |addr| if (std.math.cast(i32, @as(i64, @bitCast(addr)))) |small_addr| 35742 return (try self.moveStrategy( 35743 ty, 35744 dst_reg.class(), 35745 ty.abiAlignment(zcu).check(@as(u32, @bitCast(small_addr))), 35746 )).read(self, registerAlias(dst_reg, abi_size), .{ 35747 .base = .{ .reg = .ds }, 35748 .mod = .{ .rm = .{ 35749 .size = self.memSize(ty), 35750 .disp = small_addr, 35751 } }, 35752 }), 35753 .load_symbol => |sym_off| switch (dst_reg.class()) { 35754 .general_purpose => { 35755 assert(sym_off.off == 0); 35756 try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(dst_reg, abi_size), .{ 35757 .base = .{ .reloc = sym_off.sym_index }, 35758 .mod = .{ .rm = .{ 35759 .size = self.memSize(ty), 35760 .disp = sym_off.off, 35761 } }, 35762 }); 35763 return; 35764 }, 35765 .segment, .mmx, .ip, .cr, .dr => unreachable, 35766 .x87, .sse => {}, 35767 }, 35768 .load_direct => |sym_index| switch (dst_reg.class()) { 35769 .general_purpose => { 35770 _ = try self.addInst(.{ 35771 .tag = .mov, 35772 .ops = .direct_reloc, 35773 .data = .{ .rx = .{ 35774 .r1 = registerAlias(dst_reg, abi_size), 35775 .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), 35776 } }, 35777 }); 35778 return; 35779 }, 35780 .segment, .mmx, .ip, .cr, .dr => unreachable, 35781 .x87, .sse => {}, 35782 }, 35783 .load_got, .load_tlv => {}, 35784 else => unreachable, 35785 } 35786 35787 const addr_reg = try self.copyToTmpRegister(.usize, src_mcv.address()); 35788 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 35789 defer self.register_manager.unlockReg(addr_lock); 35790 35791 try (try self.moveStrategy(ty, dst_reg.class(), false)).read(self, registerAlias(dst_reg, abi_size), .{ 35792 .base = .{ .reg = addr_reg.to64() }, 35793 .mod = .{ .rm = .{ .size = self.memSize(ty) } }, 35794 }); 35795 }, 35796 .lea_symbol => |sym_off| switch (self.bin_file.tag) { 35797 .elf, .macho => try self.asmRegisterMemory( 35798 .{ ._, .lea }, 35799 dst_reg.to64(), 35800 .{ 35801 .base = .{ .reloc = sym_off.sym_index }, 35802 .mod = .{ .rm = .{ 35803 .size = .qword, 35804 .disp = sym_off.off, 35805 } }, 35806 }, 35807 ), 35808 else => return self.fail("TODO emit symbol sequence on {s}", .{ 35809 @tagName(self.bin_file.tag), 35810 }), 35811 }, 35812 .lea_direct, .lea_got => |sym_index| _ = try self.addInst(.{ 35813 .tag = switch (src_mcv) { 35814 .lea_direct => .lea, 35815 .lea_got => .mov, 35816 else => unreachable, 35817 }, 35818 .ops = switch (src_mcv) { 35819 .lea_direct => .direct_reloc, 35820 .lea_got => .got_reloc, 35821 else => unreachable, 35822 }, 35823 .data = .{ .rx = .{ 35824 .r1 = dst_reg.to64(), 35825 .payload = try self.addExtra(bits.SymbolOffset{ .sym_index = sym_index }), 35826 } }, 35827 }), 35828 .lea_tlv => unreachable, // TODO: remove this 35829 .air_ref => |src_ref| try self.genSetReg(dst_reg, ty, try self.resolveInst(src_ref), opts), 35830 } 35831 } 35832 35833 fn genSetMem( 35834 self: *CodeGen, 35835 base: Memory.Base, 35836 disp: i32, 35837 ty: Type, 35838 src_mcv: MCValue, 35839 opts: CopyOptions, 35840 ) InnerError!void { 35841 const pt = self.pt; 35842 const zcu = pt.zcu; 35843 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 35844 const dst_ptr_mcv: MCValue = switch (base) { 35845 .none => .{ .immediate = @bitCast(@as(i64, disp)) }, 35846 .reg => |base_reg| .{ .register_offset = .{ .reg = base_reg, .off = disp } }, 35847 .frame => |base_frame_index| .{ .lea_frame = .{ .index = base_frame_index, .off = disp } }, 35848 .table => unreachable, 35849 .reloc => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index, .off = disp } }, 35850 }; 35851 switch (src_mcv) { 35852 .none, 35853 .unreach, 35854 .dead, 35855 .elementwise_regs_then_frame, 35856 .reserved_frame, 35857 => unreachable, 35858 .undef => if (opts.safety) try self.genInlineMemset( 35859 dst_ptr_mcv, 35860 src_mcv, 35861 .{ .immediate = abi_size }, 35862 opts, 35863 ), 35864 .immediate => |imm| switch (abi_size) { 35865 1, 2, 4 => { 35866 const immediate: Immediate = switch (if (ty.isAbiInt(zcu)) 35867 ty.intInfo(zcu).signedness 35868 else 35869 .unsigned) { 35870 .signed => .s(@truncate(@as(i64, @bitCast(imm)))), 35871 .unsigned => .u(@as(u32, @intCast(imm))), 35872 }; 35873 try self.asmMemoryImmediate( 35874 .{ ._, .mov }, 35875 .{ .base = base, .mod = .{ .rm = .{ 35876 .size = .fromSize(abi_size), 35877 .disp = disp, 35878 } } }, 35879 immediate, 35880 ); 35881 }, 35882 3, 5...7 => unreachable, 35883 else => if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |small| { 35884 try self.asmMemoryImmediate( 35885 .{ ._, .mov }, 35886 .{ .base = base, .mod = .{ .rm = .{ 35887 .size = .fromSize(abi_size), 35888 .disp = disp, 35889 } } }, 35890 .s(small), 35891 ); 35892 } else { 35893 var offset: i32 = 0; 35894 while (offset < abi_size) : (offset += 4) try self.asmMemoryImmediate( 35895 .{ ._, .mov }, 35896 .{ .base = base, .mod = .{ .rm = .{ 35897 .size = .dword, 35898 .disp = disp + offset, 35899 } } }, 35900 if (ty.isSignedInt(zcu)) .s( 35901 @truncate(@as(i64, @bitCast(imm)) >> (std.math.cast(u6, offset * 8) orelse 63)), 35902 ) else .u( 35903 @as(u32, @truncate(if (std.math.cast(u6, offset * 8)) |shift| imm >> shift else 0)), 35904 ), 35905 ); 35906 }, 35907 }, 35908 .eflags => |cc| try self.asmSetccMemory(cc, .{ .base = base, .mod = .{ 35909 .rm = .{ .size = .byte, .disp = disp }, 35910 } }), 35911 .register => |src_reg| { 35912 const mem_size = switch (base) { 35913 .frame => |base_fi| mem_size: { 35914 assert(disp >= 0); 35915 const frame_abi_size = self.frame_allocs.items(.abi_size)[@intFromEnum(base_fi)]; 35916 const frame_spill_pad = self.frame_allocs.items(.spill_pad)[@intFromEnum(base_fi)]; 35917 assert(frame_abi_size - frame_spill_pad - disp >= abi_size); 35918 break :mem_size if (frame_abi_size - frame_spill_pad - disp == abi_size) 35919 frame_abi_size 35920 else 35921 abi_size; 35922 }, 35923 else => abi_size, 35924 }; 35925 const src_alias = registerAlias(src_reg, abi_size); 35926 const src_size: u32 = @intCast(switch (src_alias.class()) { 35927 .general_purpose, .segment, .x87, .ip, .cr, .dr => @divExact(src_alias.bitSize(), 8), 35928 .mmx, .sse => abi_size, 35929 }); 35930 const src_align: InternPool.Alignment = .fromNonzeroByteUnits( 35931 std.math.ceilPowerOfTwoAssert(u32, src_size), 35932 ); 35933 if (src_size > mem_size) { 35934 const frame_index = try self.allocFrameIndex(.init(.{ 35935 .size = src_size, 35936 .alignment = src_align, 35937 })); 35938 const frame_mcv: MCValue = .{ .load_frame = .{ .index = frame_index } }; 35939 try (try self.moveStrategy(ty, src_alias.class(), true)).write( 35940 self, 35941 .{ .base = .{ .frame = frame_index }, .mod = .{ .rm = .{ 35942 .size = .fromSize(src_size), 35943 } } }, 35944 src_alias, 35945 ); 35946 try self.genSetMem(base, disp, ty, frame_mcv, opts); 35947 try self.freeValue(frame_mcv); 35948 } else try (try self.moveStrategy(ty, src_alias.class(), switch (base) { 35949 .none => src_align.check(@as(u32, @bitCast(disp))), 35950 .reg => |reg| switch (reg) { 35951 .es, .cs, .ss, .ds => src_align.check(@as(u32, @bitCast(disp))), 35952 else => false, 35953 }, 35954 .frame => |frame_index| self.getFrameAddrAlignment(.{ 35955 .index = frame_index, 35956 .off = disp, 35957 }).compare(.gte, src_align), 35958 .table => unreachable, 35959 .reloc => false, 35960 })).write( 35961 self, 35962 .{ .base = base, .mod = .{ .rm = .{ 35963 .size = .fromBitSize(@min( 35964 self.memSize(ty).bitSize(self.target), 35965 src_alias.bitSize(), 35966 )), 35967 .disp = disp, 35968 } } }, 35969 src_alias, 35970 ); 35971 }, 35972 inline .register_pair, .register_triple, .register_quadruple => |src_regs| { 35973 var part_disp: i32 = disp; 35974 for (try self.splitType(src_regs.len, ty), src_regs) |src_ty, src_reg| { 35975 try self.genSetMem(base, part_disp, src_ty, .{ .register = src_reg }, opts); 35976 part_disp += @intCast(src_ty.abiSize(zcu)); 35977 } 35978 }, 35979 .register_overflow => |ro| switch (ty.zigTypeTag(zcu)) { 35980 .@"struct" => { 35981 try self.genSetMem( 35982 base, 35983 disp + @as(i32, @intCast(ty.structFieldOffset(0, zcu))), 35984 ty.fieldType(0, zcu), 35985 .{ .register = ro.reg }, 35986 opts, 35987 ); 35988 try self.genSetMem( 35989 base, 35990 disp + @as(i32, @intCast(ty.structFieldOffset(1, zcu))), 35991 ty.fieldType(1, zcu), 35992 .{ .eflags = ro.eflags }, 35993 opts, 35994 ); 35995 }, 35996 .optional => { 35997 assert(!ty.optionalReprIsPayload(zcu)); 35998 const child_ty = ty.optionalChild(zcu); 35999 try self.genSetMem(base, disp, child_ty, .{ .register = ro.reg }, opts); 36000 try self.genSetMem( 36001 base, 36002 disp + @as(i32, @intCast(child_ty.abiSize(zcu))), 36003 .bool, 36004 .{ .eflags = ro.eflags }, 36005 opts, 36006 ); 36007 }, 36008 else => return self.fail("TODO implement genSetMem for {s} of {}", .{ 36009 @tagName(src_mcv), ty.fmt(pt), 36010 }), 36011 }, 36012 .register_offset => |reg_off| { 36013 const src_reg = self.copyToTmpRegister(ty, src_mcv) catch |err| switch (err) { 36014 error.OutOfRegisters => { 36015 const src_reg = registerAlias(reg_off.reg, abi_size); 36016 try self.asmRegisterMemory(.{ ._, .lea }, src_reg, .{ 36017 .base = .{ .reg = src_reg }, 36018 .mod = .{ .rm = .{ 36019 .size = .qword, 36020 .disp = reg_off.off, 36021 } }, 36022 }); 36023 try self.genSetMem(base, disp, ty, .{ .register = reg_off.reg }, opts); 36024 return self.asmRegisterMemory(.{ ._, .lea }, src_reg, .{ 36025 .base = .{ .reg = src_reg }, 36026 .mod = .{ .rm = .{ 36027 .size = .qword, 36028 .disp = -reg_off.off, 36029 } }, 36030 }); 36031 }, 36032 else => |e| return e, 36033 }; 36034 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 36035 defer self.register_manager.unlockReg(src_lock); 36036 36037 try self.genSetMem(base, disp, ty, .{ .register = src_reg }, opts); 36038 }, 36039 .register_mask => { 36040 const src_reg = try self.copyToTmpRegister(ty, src_mcv); 36041 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 36042 defer self.register_manager.unlockReg(src_lock); 36043 36044 try self.genSetMem(base, disp, ty, .{ .register = src_reg }, opts); 36045 }, 36046 .memory, 36047 .indirect, 36048 .load_direct, 36049 .lea_direct, 36050 .load_got, 36051 .lea_got, 36052 .load_tlv, 36053 .lea_tlv, 36054 .load_frame, 36055 .lea_frame, 36056 .load_symbol, 36057 .lea_symbol, 36058 => switch (abi_size) { 36059 0 => {}, 36060 1, 2, 4, 8 => { 36061 const src_reg = try self.copyToTmpRegister(ty, src_mcv); 36062 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 36063 defer self.register_manager.unlockReg(src_lock); 36064 36065 try self.genSetMem(base, disp, ty, .{ .register = src_reg }, opts); 36066 }, 36067 else => try self.genInlineMemcpy(dst_ptr_mcv, src_mcv.address(), .{ .immediate = abi_size }, .{ .no_alias = true }), 36068 }, 36069 .air_ref => |src_ref| try self.genSetMem(base, disp, ty, try self.resolveInst(src_ref), opts), 36070 } 36071 } 36072 36073 fn genInlineMemcpy(self: *CodeGen, dst_ptr: MCValue, src_ptr: MCValue, len: MCValue, opts: struct { 36074 no_alias: bool, 36075 }) InnerError!void { 36076 if (opts.no_alias and dst_ptr.isAddress() and src_ptr.isAddress()) switch (len) { 36077 else => {}, 36078 .immediate => |len_imm| switch (len_imm) { 36079 else => {}, 36080 1 => if (self.register_manager.tryAllocReg(null, abi.RegisterClass.gp)) |reg| { 36081 try self.asmRegisterMemory(.{ ._, .mov }, reg.to8(), try src_ptr.deref().mem(self, .{ .size = .byte })); 36082 try self.asmMemoryRegister(.{ ._, .mov }, try dst_ptr.deref().mem(self, .{ .size = .byte }), reg.to8()); 36083 return; 36084 }, 36085 2 => if (self.register_manager.tryAllocReg(null, abi.RegisterClass.gp)) |reg| { 36086 try self.asmRegisterMemory(.{ ._, .mov }, reg.to16(), try src_ptr.deref().mem(self, .{ .size = .word })); 36087 try self.asmMemoryRegister(.{ ._, .mov }, try dst_ptr.deref().mem(self, .{ .size = .word }), reg.to16()); 36088 return; 36089 }, 36090 4 => if (self.register_manager.tryAllocReg(null, abi.RegisterClass.gp)) |reg| { 36091 try self.asmRegisterMemory(.{ ._, .mov }, reg.to32(), try src_ptr.deref().mem(self, .{ .size = .dword })); 36092 try self.asmMemoryRegister(.{ ._, .mov }, try dst_ptr.deref().mem(self, .{ .size = .dword }), reg.to32()); 36093 return; 36094 }, 36095 8 => if (self.target.cpu.arch == .x86_64) { 36096 if (self.register_manager.tryAllocReg(null, abi.RegisterClass.gp)) |reg| { 36097 try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), try src_ptr.deref().mem(self, .{ .size = .qword })); 36098 try self.asmMemoryRegister(.{ ._, .mov }, try dst_ptr.deref().mem(self, .{ .size = .qword }), reg.to64()); 36099 return; 36100 } 36101 }, 36102 16 => if (self.hasFeature(.avx)) { 36103 if (self.register_manager.tryAllocReg(null, abi.RegisterClass.sse)) |reg| { 36104 try self.asmRegisterMemory(.{ .v_dqu, .mov }, reg.to128(), try src_ptr.deref().mem(self, .{ .size = .xword })); 36105 try self.asmMemoryRegister(.{ .v_dqu, .mov }, try dst_ptr.deref().mem(self, .{ .size = .xword }), reg.to128()); 36106 return; 36107 } 36108 } else if (self.hasFeature(.sse2)) { 36109 if (self.register_manager.tryAllocReg(null, abi.RegisterClass.sse)) |reg| { 36110 try self.asmRegisterMemory(.{ ._dqu, .mov }, reg.to128(), try src_ptr.deref().mem(self, .{ .size = .xword })); 36111 try self.asmMemoryRegister(.{ ._dqu, .mov }, try dst_ptr.deref().mem(self, .{ .size = .xword }), reg.to128()); 36112 return; 36113 } 36114 } else if (self.hasFeature(.sse)) { 36115 if (self.register_manager.tryAllocReg(null, abi.RegisterClass.sse)) |reg| { 36116 try self.asmRegisterMemory(.{ ._ps, .movu }, reg.to128(), try src_ptr.deref().mem(self, .{ .size = .xword })); 36117 try self.asmMemoryRegister(.{ ._ps, .movu }, try dst_ptr.deref().mem(self, .{ .size = .xword }), reg.to128()); 36118 return; 36119 } 36120 }, 36121 32 => if (self.hasFeature(.avx)) { 36122 if (self.register_manager.tryAllocReg(null, abi.RegisterClass.sse)) |reg| { 36123 try self.asmRegisterMemory(.{ .v_dqu, .mov }, reg.to256(), try src_ptr.deref().mem(self, .{ .size = .yword })); 36124 try self.asmMemoryRegister(.{ .v_dqu, .mov }, try dst_ptr.deref().mem(self, .{ .size = .yword }), reg.to256()); 36125 return; 36126 } 36127 }, 36128 }, 36129 }; 36130 try self.spillRegisters(&.{ .rsi, .rdi, .rcx }); 36131 try self.genSetReg(.rsi, .usize, src_ptr, .{}); 36132 try self.genSetReg(.rdi, .usize, dst_ptr, .{}); 36133 try self.genSetReg(.rcx, .usize, len, .{}); 36134 try self.asmOpOnly(.{ .@"rep _sb", .mov }); 36135 } 36136 36137 fn genInlineMemset( 36138 self: *CodeGen, 36139 dst_ptr: MCValue, 36140 value: MCValue, 36141 len: MCValue, 36142 opts: CopyOptions, 36143 ) InnerError!void { 36144 try self.spillRegisters(&.{ .rdi, .al, .rcx }); 36145 try self.genSetReg(.rdi, .usize, dst_ptr, .{}); 36146 try self.genSetReg(.al, .u8, value, opts); 36147 try self.genSetReg(.rcx, .usize, len, .{}); 36148 try self.asmOpOnly(.{ .@"rep _sb", .sto }); 36149 } 36150 36151 fn genExternSymbolRef( 36152 self: *CodeGen, 36153 comptime tag: Mir.Inst.Tag, 36154 lib: ?[]const u8, 36155 callee: []const u8, 36156 ) InnerError!void { 36157 if (self.bin_file.cast(.coff)) |coff_file| { 36158 const global_index = try coff_file.getGlobalSymbol(callee, lib); 36159 const scratch_reg = abi.getCAbiLinkerScratchReg(self.fn_type.fnCallingConvention(self.pt.zcu)); 36160 _ = try self.addInst(.{ 36161 .tag = .mov, 36162 .ops = .import_reloc, 36163 .data = .{ .rx = .{ 36164 .r1 = scratch_reg, 36165 .payload = try self.addExtra(bits.SymbolOffset{ 36166 .sym_index = link.File.Coff.global_symbol_bit | global_index, 36167 }), 36168 } }, 36169 }); 36170 switch (tag) { 36171 .mov => {}, 36172 .call => try self.asmRegister(.{ ._, .call }, scratch_reg), 36173 else => unreachable, 36174 } 36175 } else return self.fail("TODO implement calling extern functions", .{}); 36176 } 36177 36178 fn genLazySymbolRef( 36179 self: *CodeGen, 36180 comptime tag: Mir.Inst.Tag, 36181 reg: Register, 36182 lazy_sym: link.File.LazySymbol, 36183 ) InnerError!void { 36184 const pt = self.pt; 36185 if (self.bin_file.cast(.elf)) |elf_file| { 36186 const zo = elf_file.zigObjectPtr().?; 36187 const sym_index = zo.getOrCreateMetadataForLazySymbol(elf_file, pt, lazy_sym) catch |err| 36188 return self.fail("{s} creating lazy symbol", .{@errorName(err)}); 36189 if (self.mod.pic) { 36190 switch (tag) { 36191 .lea, .call => try self.genSetReg(reg, .usize, .{ 36192 .lea_symbol = .{ .sym_index = sym_index }, 36193 }, .{}), 36194 .mov => try self.genSetReg(reg, .usize, .{ 36195 .load_symbol = .{ .sym_index = sym_index }, 36196 }, .{}), 36197 else => unreachable, 36198 } 36199 switch (tag) { 36200 .lea, .mov => {}, 36201 .call => try self.asmRegister(.{ ._, .call }, reg), 36202 else => unreachable, 36203 } 36204 } else switch (tag) { 36205 .lea, .mov => try self.asmRegisterMemory(.{ ._, tag }, reg.to64(), .{ 36206 .base = .{ .reloc = sym_index }, 36207 .mod = .{ .rm = .{ .size = .qword } }, 36208 }), 36209 .call => try self.asmImmediate(.{ ._, .call }, .rel(.{ .sym_index = sym_index })), 36210 else => unreachable, 36211 } 36212 } else if (self.bin_file.cast(.plan9)) |p9_file| { 36213 const atom_index = p9_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| 36214 return self.fail("{s} creating lazy symbol", .{@errorName(err)}); 36215 var atom = p9_file.getAtom(atom_index); 36216 _ = atom.getOrCreateOffsetTableEntry(p9_file); 36217 const got_addr = atom.getOffsetTableAddress(p9_file); 36218 const got_mem: Memory = .{ 36219 .base = .{ .reg = .ds }, 36220 .mod = .{ .rm = .{ 36221 .size = .qword, 36222 .disp = @intCast(got_addr), 36223 } }, 36224 }; 36225 switch (tag) { 36226 .lea, .mov => try self.asmRegisterMemory(.{ ._, .mov }, reg.to64(), got_mem), 36227 .call => try self.asmMemory(.{ ._, .call }, got_mem), 36228 else => unreachable, 36229 } 36230 switch (tag) { 36231 .lea, .call => {}, 36232 .mov => try self.asmRegisterMemory( 36233 .{ ._, tag }, 36234 reg.to64(), 36235 .initSib(.qword, .{ .base = .{ .reg = reg.to64() } }), 36236 ), 36237 else => unreachable, 36238 } 36239 } else if (self.bin_file.cast(.coff)) |coff_file| { 36240 const atom_index = coff_file.getOrCreateAtomForLazySymbol(pt, lazy_sym) catch |err| 36241 return self.fail("{s} creating lazy symbol", .{@errorName(err)}); 36242 const sym_index = coff_file.getAtom(atom_index).getSymbolIndex().?; 36243 switch (tag) { 36244 .lea, .call => try self.genSetReg(reg, .usize, .{ .lea_got = sym_index }, .{}), 36245 .mov => try self.genSetReg(reg, .usize, .{ .load_got = sym_index }, .{}), 36246 else => unreachable, 36247 } 36248 switch (tag) { 36249 .lea, .mov => {}, 36250 .call => try self.asmRegister(.{ ._, .call }, reg), 36251 else => unreachable, 36252 } 36253 } else if (self.bin_file.cast(.macho)) |macho_file| { 36254 const zo = macho_file.getZigObject().?; 36255 const sym_index = zo.getOrCreateMetadataForLazySymbol(macho_file, pt, lazy_sym) catch |err| 36256 return self.fail("{s} creating lazy symbol", .{@errorName(err)}); 36257 const sym = zo.symbols.items[sym_index]; 36258 switch (tag) { 36259 .lea, .call => try self.genSetReg(reg, .usize, .{ 36260 .lea_symbol = .{ .sym_index = sym.nlist_idx }, 36261 }, .{}), 36262 .mov => try self.genSetReg(reg, .usize, .{ 36263 .load_symbol = .{ .sym_index = sym.nlist_idx }, 36264 }, .{}), 36265 else => unreachable, 36266 } 36267 switch (tag) { 36268 .lea, .mov => {}, 36269 .call => try self.asmRegister(.{ ._, .call }, reg), 36270 else => unreachable, 36271 } 36272 } else { 36273 return self.fail("TODO implement genLazySymbol for x86_64 {s}", .{@tagName(self.bin_file.tag)}); 36274 } 36275 } 36276 36277 fn airIntFromPtr(self: *CodeGen, inst: Air.Inst.Index) !void { 36278 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 36279 const result = result: { 36280 // TODO: handle case where the operand is a slice not a raw pointer 36281 const src_mcv = try self.resolveInst(un_op); 36282 if (self.reuseOperand(inst, un_op, 0, src_mcv)) break :result src_mcv; 36283 36284 const dst_mcv = try self.allocRegOrMem(inst, true); 36285 const dst_ty = self.typeOfIndex(inst); 36286 try self.genCopy(dst_ty, dst_mcv, src_mcv, .{}); 36287 break :result dst_mcv; 36288 }; 36289 return self.finishAir(inst, result, .{ un_op, .none, .none }); 36290 } 36291 36292 fn airBitCast(self: *CodeGen, inst: Air.Inst.Index) !void { 36293 const pt = self.pt; 36294 const zcu = pt.zcu; 36295 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 36296 const dst_ty = self.typeOfIndex(inst); 36297 const src_ty = self.typeOf(ty_op.operand); 36298 36299 const result = result: { 36300 const src_mcv = try self.resolveInst(ty_op.operand); 36301 if (dst_ty.isPtrAtRuntime(zcu) and src_ty.isPtrAtRuntime(zcu)) switch (src_mcv) { 36302 .lea_frame => break :result src_mcv, 36303 else => if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv, 36304 }; 36305 36306 const dst_rc = self.regSetForType(dst_ty); 36307 const src_rc = self.regSetForType(src_ty); 36308 36309 const src_lock = if (src_mcv.getReg()) |src_reg| self.register_manager.lockReg(src_reg) else null; 36310 defer if (src_lock) |lock| self.register_manager.unlockReg(lock); 36311 36312 const dst_mcv = if (src_mcv != .register_mask and 36313 (if (src_mcv.getReg()) |src_reg| src_reg.class() == .general_purpose else true) and 36314 dst_rc.supersetOf(src_rc) and dst_ty.abiSize(zcu) <= src_ty.abiSize(zcu) and 36315 dst_ty.abiAlignment(zcu).order(src_ty.abiAlignment(zcu)).compare(.lte) and 36316 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) src_mcv else dst: { 36317 const dst_mcv = try self.allocRegOrMem(inst, true); 36318 try self.genCopy(switch (src_mcv) { 36319 else => switch (std.math.order(dst_ty.abiSize(zcu), src_ty.abiSize(zcu))) { 36320 .lt => dst_ty, 36321 .eq => if (!dst_mcv.isBase() or src_mcv.isBase()) dst_ty else src_ty, 36322 .gt => src_ty, 36323 }, 36324 .register_mask => src_ty, 36325 }, dst_mcv, src_mcv, .{}); 36326 break :dst dst_mcv; 36327 }; 36328 36329 if (dst_ty.isRuntimeFloat()) break :result dst_mcv; 36330 36331 if (dst_ty.isAbiInt(zcu) and src_ty.isAbiInt(zcu) and 36332 dst_ty.intInfo(zcu).signedness == src_ty.intInfo(zcu).signedness) break :result dst_mcv; 36333 36334 const abi_size = dst_ty.abiSize(zcu); 36335 const bit_size = dst_ty.bitSize(zcu); 36336 if (abi_size * 8 <= bit_size or dst_ty.isVector(zcu)) break :result dst_mcv; 36337 36338 const dst_limbs_len = std.math.divCeil(u31, @intCast(bit_size), 64) catch unreachable; 36339 const high_mcv: MCValue = switch (dst_mcv) { 36340 .register => |dst_reg| .{ .register = dst_reg }, 36341 .register_pair => |dst_regs| .{ .register = dst_regs[1] }, 36342 else => dst_mcv.address().offset((dst_limbs_len - 1) * 8).deref(), 36343 }; 36344 const high_reg = if (high_mcv.isRegister()) 36345 high_mcv.getReg().? 36346 else 36347 try self.copyToTmpRegister(.usize, high_mcv); 36348 const high_lock = self.register_manager.lockReg(high_reg); 36349 defer if (high_lock) |lock| self.register_manager.unlockReg(lock); 36350 try self.truncateRegister(dst_ty, high_reg); 36351 if (!high_mcv.isRegister()) try self.genCopy( 36352 if (abi_size <= 8) dst_ty else .usize, 36353 high_mcv, 36354 .{ .register = high_reg }, 36355 .{}, 36356 ); 36357 var offset = dst_limbs_len * 8; 36358 if (offset < abi_size) { 36359 const dst_signedness: std.builtin.Signedness = if (dst_ty.isAbiInt(zcu)) 36360 dst_ty.intInfo(zcu).signedness 36361 else 36362 .unsigned; 36363 const ext_mcv: MCValue = ext_mcv: switch (dst_signedness) { 36364 .signed => { 36365 try self.asmRegisterImmediate(.{ ._r, .sa }, high_reg, .u(63)); 36366 break :ext_mcv .{ .register = high_reg }; 36367 }, 36368 .unsigned => .{ .immediate = 0 }, 36369 }; 36370 while (offset < abi_size) : (offset += 8) { 36371 const limb_mcv: MCValue = switch (dst_mcv) { 36372 .register => |dst_reg| .{ .register = dst_reg }, 36373 .register_pair => |dst_regs| .{ .register = dst_regs[@divExact(offset, 8)] }, 36374 else => dst_mcv.address().offset(offset).deref(), 36375 }; 36376 const limb_lock = if (limb_mcv.isRegister()) 36377 self.register_manager.lockReg(limb_mcv.getReg().?) 36378 else 36379 null; 36380 defer if (limb_lock) |lock| self.register_manager.unlockReg(lock); 36381 try self.genCopy(.usize, limb_mcv, ext_mcv, .{}); 36382 } 36383 } 36384 break :result dst_mcv; 36385 }; 36386 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 36387 } 36388 36389 fn airArrayToSlice(self: *CodeGen, inst: Air.Inst.Index) !void { 36390 const pt = self.pt; 36391 const zcu = pt.zcu; 36392 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 36393 36394 const slice_ty = self.typeOfIndex(inst); 36395 const ptr_ty = self.typeOf(ty_op.operand); 36396 const ptr = try self.resolveInst(ty_op.operand); 36397 const array_ty = ptr_ty.childType(zcu); 36398 const array_len = array_ty.arrayLen(zcu); 36399 36400 const frame_index = try self.allocFrameIndex(.initSpill(slice_ty, zcu)); 36401 try self.genSetMem(.{ .frame = frame_index }, 0, ptr_ty, ptr, .{}); 36402 try self.genSetMem( 36403 .{ .frame = frame_index }, 36404 @intCast(ptr_ty.abiSize(zcu)), 36405 .usize, 36406 .{ .immediate = array_len }, 36407 .{}, 36408 ); 36409 36410 const result = MCValue{ .load_frame = .{ .index = frame_index } }; 36411 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 36412 } 36413 36414 fn airFloatFromInt(self: *CodeGen, inst: Air.Inst.Index) !void { 36415 const pt = self.pt; 36416 const zcu = pt.zcu; 36417 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 36418 36419 const dst_ty = self.typeOfIndex(inst); 36420 const dst_bits = dst_ty.floatBits(self.target.*); 36421 36422 const src_ty = self.typeOf(ty_op.operand); 36423 const src_bits: u32 = @intCast(src_ty.bitSize(zcu)); 36424 const src_signedness = 36425 if (src_ty.isAbiInt(zcu)) src_ty.intInfo(zcu).signedness else .unsigned; 36426 const src_size = std.math.divCeil(u32, @max(switch (src_signedness) { 36427 .signed => src_bits, 36428 .unsigned => src_bits + 1, 36429 }, 32), 8) catch unreachable; 36430 36431 const result = result: { 36432 if (switch (dst_bits) { 36433 16, 80, 128 => true, 36434 32, 64 => src_size > 8, 36435 else => unreachable, 36436 }) { 36437 if (src_bits > 128) return self.fail("TODO implement airFloatFromInt from {} to {}", .{ 36438 src_ty.fmt(pt), dst_ty.fmt(pt), 36439 }); 36440 36441 var callee_buf: ["__floatun?i?f".len]u8 = undefined; 36442 break :result try self.genCall(.{ .lib = .{ 36443 .return_type = dst_ty.toIntern(), 36444 .param_types = &.{src_ty.toIntern()}, 36445 .callee = std.fmt.bufPrint(&callee_buf, "__float{s}{c}i{c}f", .{ 36446 switch (src_signedness) { 36447 .signed => "", 36448 .unsigned => "un", 36449 }, 36450 intCompilerRtAbiName(src_bits), 36451 floatCompilerRtAbiName(dst_bits), 36452 }) catch unreachable, 36453 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}, .{}); 36454 } 36455 36456 const src_mcv = try self.resolveInst(ty_op.operand); 36457 const src_reg = if (src_mcv.isRegister()) 36458 src_mcv.getReg().? 36459 else 36460 try self.copyToTmpRegister(src_ty, src_mcv); 36461 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 36462 defer self.register_manager.unlockReg(src_lock); 36463 36464 if (src_bits < src_size * 8) try self.truncateRegister(src_ty, src_reg); 36465 36466 const dst_reg = try self.register_manager.allocReg(inst, self.regSetForType(dst_ty)); 36467 const dst_mcv = MCValue{ .register = dst_reg }; 36468 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 36469 defer self.register_manager.unlockReg(dst_lock); 36470 36471 const mir_tag = @as(?Mir.Inst.FixedTag, switch (dst_ty.zigTypeTag(zcu)) { 36472 .float => switch (dst_ty.floatBits(self.target.*)) { 36473 32 => if (self.hasFeature(.avx)) .{ .v_ss, .cvtsi2 } else .{ ._ss, .cvtsi2 }, 36474 64 => if (self.hasFeature(.avx)) .{ .v_sd, .cvtsi2 } else .{ ._sd, .cvtsi2 }, 36475 16, 80, 128 => null, 36476 else => unreachable, 36477 }, 36478 else => null, 36479 }) orelse return self.fail("TODO implement airFloatFromInt from {} to {}", .{ 36480 src_ty.fmt(pt), dst_ty.fmt(pt), 36481 }); 36482 const dst_alias = dst_reg.to128(); 36483 const src_alias = registerAlias(src_reg, src_size); 36484 switch (mir_tag[0]) { 36485 .v_ss, .v_sd => try self.asmRegisterRegisterRegister(mir_tag, dst_alias, dst_alias, src_alias), 36486 else => try self.asmRegisterRegister(mir_tag, dst_alias, src_alias), 36487 } 36488 36489 break :result dst_mcv; 36490 }; 36491 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 36492 } 36493 36494 fn airIntFromFloat(self: *CodeGen, inst: Air.Inst.Index) !void { 36495 const pt = self.pt; 36496 const zcu = pt.zcu; 36497 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 36498 36499 const dst_ty = self.typeOfIndex(inst); 36500 const dst_bits: u32 = @intCast(dst_ty.bitSize(zcu)); 36501 const dst_signedness = 36502 if (dst_ty.isAbiInt(zcu)) dst_ty.intInfo(zcu).signedness else .unsigned; 36503 const dst_size = std.math.divCeil(u32, @max(switch (dst_signedness) { 36504 .signed => dst_bits, 36505 .unsigned => dst_bits + 1, 36506 }, 32), 8) catch unreachable; 36507 36508 const src_ty = self.typeOf(ty_op.operand); 36509 const src_bits = src_ty.floatBits(self.target.*); 36510 36511 const result = result: { 36512 if (switch (src_bits) { 36513 16, 80, 128 => true, 36514 32, 64 => dst_size > 8, 36515 else => unreachable, 36516 }) { 36517 if (dst_bits > 128) return self.fail("TODO implement airIntFromFloat from {} to {}", .{ 36518 src_ty.fmt(pt), dst_ty.fmt(pt), 36519 }); 36520 36521 var callee_buf: ["__fixuns?f?i".len]u8 = undefined; 36522 break :result try self.genCall(.{ .lib = .{ 36523 .return_type = dst_ty.toIntern(), 36524 .param_types = &.{src_ty.toIntern()}, 36525 .callee = std.fmt.bufPrint(&callee_buf, "__fix{s}{c}f{c}i", .{ 36526 switch (dst_signedness) { 36527 .signed => "", 36528 .unsigned => "uns", 36529 }, 36530 floatCompilerRtAbiName(src_bits), 36531 intCompilerRtAbiName(dst_bits), 36532 }) catch unreachable, 36533 } }, &.{src_ty}, &.{.{ .air_ref = ty_op.operand }}, .{}); 36534 } 36535 36536 const src_mcv = try self.resolveInst(ty_op.operand); 36537 const src_reg = if (src_mcv.isRegister()) 36538 src_mcv.getReg().? 36539 else 36540 try self.copyToTmpRegister(src_ty, src_mcv); 36541 const src_lock = self.register_manager.lockRegAssumeUnused(src_reg); 36542 defer self.register_manager.unlockReg(src_lock); 36543 36544 const dst_reg = try self.register_manager.allocReg(inst, self.regSetForType(dst_ty)); 36545 const dst_mcv = MCValue{ .register = dst_reg }; 36546 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 36547 defer self.register_manager.unlockReg(dst_lock); 36548 36549 try self.asmRegisterRegister( 36550 switch (src_bits) { 36551 32 => if (self.hasFeature(.avx)) .{ .v_, .cvttss2si } else .{ ._, .cvttss2si }, 36552 64 => if (self.hasFeature(.avx)) .{ .v_, .cvttsd2si } else .{ ._, .cvttsd2si }, 36553 else => unreachable, 36554 }, 36555 registerAlias(dst_reg, dst_size), 36556 src_reg.to128(), 36557 ); 36558 36559 if (dst_bits < dst_size * 8) try self.truncateRegister(dst_ty, dst_reg); 36560 36561 break :result dst_mcv; 36562 }; 36563 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 36564 } 36565 36566 fn airCmpxchg(self: *CodeGen, inst: Air.Inst.Index) !void { 36567 const pt = self.pt; 36568 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 36569 const extra = self.air.extraData(Air.Cmpxchg, ty_pl.payload).data; 36570 36571 const ptr_ty = self.typeOf(extra.ptr); 36572 const val_ty = self.typeOf(extra.expected_value); 36573 const val_abi_size: u32 = @intCast(val_ty.abiSize(pt.zcu)); 36574 36575 try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx }); 36576 const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx }); 36577 defer for (regs_lock) |lock| self.register_manager.unlockReg(lock); 36578 36579 const exp_mcv = try self.resolveInst(extra.expected_value); 36580 if (val_abi_size > 8) { 36581 const exp_addr_mcv: MCValue = switch (exp_mcv) { 36582 .memory, .indirect, .load_frame => exp_mcv.address(), 36583 else => .{ .register = try self.copyToTmpRegister(.usize, exp_mcv.address()) }, 36584 }; 36585 const exp_addr_lock = 36586 if (exp_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 36587 defer if (exp_addr_lock) |lock| self.register_manager.unlockReg(lock); 36588 36589 try self.genSetReg(.rax, .usize, exp_addr_mcv.deref(), .{}); 36590 try self.genSetReg(.rdx, .usize, exp_addr_mcv.offset(8).deref(), .{}); 36591 } else try self.genSetReg(.rax, val_ty, exp_mcv, .{}); 36592 36593 const new_mcv = try self.resolveInst(extra.new_value); 36594 const new_reg = if (val_abi_size > 8) new: { 36595 const new_addr_mcv: MCValue = switch (new_mcv) { 36596 .memory, .indirect, .load_frame => new_mcv.address(), 36597 else => .{ .register = try self.copyToTmpRegister(.usize, new_mcv.address()) }, 36598 }; 36599 const new_addr_lock = 36600 if (new_addr_mcv.getReg()) |reg| self.register_manager.lockReg(reg) else null; 36601 defer if (new_addr_lock) |lock| self.register_manager.unlockReg(lock); 36602 36603 try self.genSetReg(.rbx, .usize, new_addr_mcv.deref(), .{}); 36604 try self.genSetReg(.rcx, .usize, new_addr_mcv.offset(8).deref(), .{}); 36605 break :new null; 36606 } else try self.copyToTmpRegister(val_ty, new_mcv); 36607 const new_lock = if (new_reg) |reg| self.register_manager.lockRegAssumeUnused(reg) else null; 36608 defer if (new_lock) |lock| self.register_manager.unlockReg(lock); 36609 36610 const ptr_mcv = try self.resolveInst(extra.ptr); 36611 const mem_size: Memory.Size = .fromSize(val_abi_size); 36612 const ptr_mem: Memory = switch (ptr_mcv) { 36613 .immediate, .register, .register_offset, .lea_frame => try ptr_mcv.deref().mem(self, .{ .size = mem_size }), 36614 else => .{ 36615 .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, 36616 .mod = .{ .rm = .{ .size = mem_size } }, 36617 }, 36618 }; 36619 switch (ptr_mem.mod) { 36620 .rm => {}, 36621 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), 36622 } 36623 const ptr_lock = switch (ptr_mem.base) { 36624 .none, .frame, .reloc => null, 36625 .reg => |reg| self.register_manager.lockReg(reg), 36626 .table => unreachable, 36627 }; 36628 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 36629 36630 try self.spillEflagsIfOccupied(); 36631 if (val_abi_size <= 8) try self.asmMemoryRegister( 36632 .{ .@"lock _", .cmpxchg }, 36633 ptr_mem, 36634 registerAlias(new_reg.?, val_abi_size), 36635 ) else try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem); 36636 36637 const result: MCValue = result: { 36638 if (self.liveness.isUnused(inst)) break :result .unreach; 36639 36640 if (val_abi_size <= 8) { 36641 self.eflags_inst = inst; 36642 break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } }; 36643 } 36644 36645 const dst_mcv = try self.allocRegOrMem(inst, false); 36646 try self.genCopy(.usize, dst_mcv, .{ .register = .rax }, .{}); 36647 try self.genCopy(.usize, dst_mcv.address().offset(8).deref(), .{ .register = .rdx }, .{}); 36648 try self.genCopy(.bool, dst_mcv.address().offset(16).deref(), .{ .eflags = .ne }, .{}); 36649 break :result dst_mcv; 36650 }; 36651 return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); 36652 } 36653 36654 fn atomicOp( 36655 self: *CodeGen, 36656 ptr_mcv: MCValue, 36657 val_mcv: MCValue, 36658 ptr_ty: Type, 36659 val_ty: Type, 36660 unused: bool, 36661 rmw_op: ?std.builtin.AtomicRmwOp, 36662 order: std.builtin.AtomicOrder, 36663 ) InnerError!MCValue { 36664 const pt = self.pt; 36665 const zcu = pt.zcu; 36666 const ptr_lock = switch (ptr_mcv) { 36667 .register => |reg| self.register_manager.lockReg(reg), 36668 else => null, 36669 }; 36670 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 36671 36672 const val_lock = switch (val_mcv) { 36673 .register => |reg| self.register_manager.lockReg(reg), 36674 else => null, 36675 }; 36676 defer if (val_lock) |lock| self.register_manager.unlockReg(lock); 36677 36678 const val_abi_size: u32 = @intCast(val_ty.abiSize(zcu)); 36679 const mem_size: Memory.Size = .fromSize(val_abi_size); 36680 const ptr_mem: Memory = switch (ptr_mcv) { 36681 .immediate, .register, .register_offset, .lea_frame => try ptr_mcv.deref().mem(self, .{ .size = mem_size }), 36682 else => .{ 36683 .base = .{ .reg = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }, 36684 .mod = .{ .rm = .{ .size = mem_size } }, 36685 }, 36686 }; 36687 switch (ptr_mem.mod) { 36688 .rm => {}, 36689 .off => return self.fail("TODO airCmpxchg with {s}", .{@tagName(ptr_mcv)}), 36690 } 36691 const mem_lock = switch (ptr_mem.base) { 36692 .none, .frame, .reloc => null, 36693 .reg => |reg| self.register_manager.lockReg(reg), 36694 .table => unreachable, 36695 }; 36696 defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); 36697 36698 const use_sse = rmw_op orelse .Xchg != .Xchg and val_ty.isRuntimeFloat(); 36699 const strat: enum { lock, loop, libcall } = if (use_sse) .loop else switch (rmw_op orelse .Xchg) { 36700 .Xchg, 36701 .Add, 36702 .Sub, 36703 => if (val_abi_size <= 8) .lock else if (val_abi_size <= 16) .loop else .libcall, 36704 .And, 36705 .Or, 36706 .Xor, 36707 => if (val_abi_size <= 8 and unused) .lock else if (val_abi_size <= 16) .loop else .libcall, 36708 .Nand, 36709 .Max, 36710 .Min, 36711 => if (val_abi_size <= 16) .loop else .libcall, 36712 }; 36713 switch (strat) { 36714 .lock => { 36715 const mir_tag: Mir.Inst.FixedTag = if (rmw_op) |op| switch (op) { 36716 .Xchg => if (unused) .{ ._, .mov } else .{ ._g, .xch }, 36717 .Add => .{ .@"lock _", if (unused) .add else .xadd }, 36718 .Sub => .{ .@"lock _", if (unused) .sub else .xadd }, 36719 .And => .{ .@"lock _", .@"and" }, 36720 .Or => .{ .@"lock _", .@"or" }, 36721 .Xor => .{ .@"lock _", .xor }, 36722 else => unreachable, 36723 } else switch (order) { 36724 .unordered, .monotonic, .release, .acq_rel => .{ ._, .mov }, 36725 .acquire => unreachable, 36726 .seq_cst => .{ ._g, .xch }, 36727 }; 36728 36729 const dst_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 36730 const dst_mcv = MCValue{ .register = dst_reg }; 36731 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 36732 defer self.register_manager.unlockReg(dst_lock); 36733 36734 try self.genSetReg(dst_reg, val_ty, val_mcv, .{}); 36735 if (rmw_op == std.builtin.AtomicRmwOp.Sub and mir_tag[1] == .xadd) { 36736 try self.genUnOpMir(.{ ._, .neg }, val_ty, dst_mcv); 36737 } 36738 try self.asmMemoryRegister(mir_tag, ptr_mem, registerAlias(dst_reg, val_abi_size)); 36739 36740 return if (unused) .unreach else dst_mcv; 36741 }, 36742 .loop => _ = if (val_abi_size <= 8) { 36743 const sse_reg: Register = if (use_sse) 36744 try self.register_manager.allocReg(null, abi.RegisterClass.sse) 36745 else 36746 undefined; 36747 const sse_lock = 36748 if (use_sse) self.register_manager.lockRegAssumeUnused(sse_reg) else undefined; 36749 defer if (use_sse) self.register_manager.unlockReg(sse_lock); 36750 36751 const tmp_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 36752 const tmp_mcv = MCValue{ .register = tmp_reg }; 36753 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 36754 defer self.register_manager.unlockReg(tmp_lock); 36755 36756 try self.asmRegisterMemory(.{ ._, .mov }, registerAlias(.rax, val_abi_size), ptr_mem); 36757 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 36758 if (!use_sse and rmw_op orelse .Xchg != .Xchg) { 36759 try self.genSetReg(tmp_reg, val_ty, .{ .register = .rax }, .{}); 36760 } 36761 if (rmw_op) |op| if (use_sse) { 36762 const mir_tag = @as(?Mir.Inst.FixedTag, switch (op) { 36763 .Add => switch (val_ty.floatBits(self.target.*)) { 36764 32 => if (self.hasFeature(.avx)) .{ .v_ss, .add } else .{ ._ss, .add }, 36765 64 => if (self.hasFeature(.avx)) .{ .v_sd, .add } else .{ ._sd, .add }, 36766 else => null, 36767 }, 36768 .Sub => switch (val_ty.floatBits(self.target.*)) { 36769 32 => if (self.hasFeature(.avx)) .{ .v_ss, .sub } else .{ ._ss, .sub }, 36770 64 => if (self.hasFeature(.avx)) .{ .v_sd, .sub } else .{ ._sd, .sub }, 36771 else => null, 36772 }, 36773 .Min => switch (val_ty.floatBits(self.target.*)) { 36774 32 => if (self.hasFeature(.avx)) .{ .v_ss, .min } else .{ ._ss, .min }, 36775 64 => if (self.hasFeature(.avx)) .{ .v_sd, .min } else .{ ._sd, .min }, 36776 else => null, 36777 }, 36778 .Max => switch (val_ty.floatBits(self.target.*)) { 36779 32 => if (self.hasFeature(.avx)) .{ .v_ss, .max } else .{ ._ss, .max }, 36780 64 => if (self.hasFeature(.avx)) .{ .v_sd, .max } else .{ ._sd, .max }, 36781 else => null, 36782 }, 36783 else => unreachable, 36784 }) orelse return self.fail("TODO implement atomicOp of {s} for {}", .{ 36785 @tagName(op), val_ty.fmt(pt), 36786 }); 36787 try self.genSetReg(sse_reg, val_ty, .{ .register = .rax }, .{}); 36788 switch (mir_tag[0]) { 36789 .v_ss, .v_sd => if (val_mcv.isBase()) try self.asmRegisterRegisterMemory( 36790 mir_tag, 36791 sse_reg.to128(), 36792 sse_reg.to128(), 36793 try val_mcv.mem(self, .{ .size = self.memSize(val_ty) }), 36794 ) else try self.asmRegisterRegisterRegister( 36795 mir_tag, 36796 sse_reg.to128(), 36797 sse_reg.to128(), 36798 (if (val_mcv.isRegister()) 36799 val_mcv.getReg().? 36800 else 36801 try self.copyToTmpRegister(val_ty, val_mcv)).to128(), 36802 ), 36803 ._ss, ._sd => if (val_mcv.isBase()) try self.asmRegisterMemory( 36804 mir_tag, 36805 sse_reg.to128(), 36806 try val_mcv.mem(self, .{ .size = self.memSize(val_ty) }), 36807 ) else try self.asmRegisterRegister( 36808 mir_tag, 36809 sse_reg.to128(), 36810 (if (val_mcv.isRegister()) 36811 val_mcv.getReg().? 36812 else 36813 try self.copyToTmpRegister(val_ty, val_mcv)).to128(), 36814 ), 36815 else => unreachable, 36816 } 36817 try self.genSetReg(tmp_reg, val_ty, .{ .register = sse_reg }, .{}); 36818 } else switch (op) { 36819 .Xchg => try self.genSetReg(tmp_reg, val_ty, val_mcv, .{}), 36820 .Add => try self.genBinOpMir(.{ ._, .add }, val_ty, tmp_mcv, val_mcv), 36821 .Sub => try self.genBinOpMir(.{ ._, .sub }, val_ty, tmp_mcv, val_mcv), 36822 .And => try self.genBinOpMir(.{ ._, .@"and" }, val_ty, tmp_mcv, val_mcv), 36823 .Nand => { 36824 try self.genBinOpMir(.{ ._, .@"and" }, val_ty, tmp_mcv, val_mcv); 36825 try self.genUnOpMir(.{ ._, .not }, val_ty, tmp_mcv); 36826 }, 36827 .Or => try self.genBinOpMir(.{ ._, .@"or" }, val_ty, tmp_mcv, val_mcv), 36828 .Xor => try self.genBinOpMir(.{ ._, .xor }, val_ty, tmp_mcv, val_mcv), 36829 .Min, .Max => { 36830 const cc: Condition = switch (if (val_ty.isAbiInt(zcu)) 36831 val_ty.intInfo(zcu).signedness 36832 else 36833 .unsigned) { 36834 .unsigned => switch (op) { 36835 .Min => .a, 36836 .Max => .b, 36837 else => unreachable, 36838 }, 36839 .signed => switch (op) { 36840 .Min => .g, 36841 .Max => .l, 36842 else => unreachable, 36843 }, 36844 }; 36845 36846 const cmov_abi_size = @max(val_abi_size, 2); 36847 switch (val_mcv) { 36848 .register => |val_reg| { 36849 try self.genBinOpMir(.{ ._, .cmp }, val_ty, tmp_mcv, val_mcv); 36850 try self.asmCmovccRegisterRegister( 36851 cc, 36852 registerAlias(tmp_reg, cmov_abi_size), 36853 registerAlias(val_reg, cmov_abi_size), 36854 ); 36855 }, 36856 .memory, .indirect, .load_frame => { 36857 try self.genBinOpMir(.{ ._, .cmp }, val_ty, tmp_mcv, val_mcv); 36858 try self.asmCmovccRegisterMemory( 36859 cc, 36860 registerAlias(tmp_reg, cmov_abi_size), 36861 try val_mcv.mem(self, .{ .size = .fromSize(cmov_abi_size) }), 36862 ); 36863 }, 36864 else => { 36865 const mat_reg = try self.copyToTmpRegister(val_ty, val_mcv); 36866 const mat_lock = self.register_manager.lockRegAssumeUnused(mat_reg); 36867 defer self.register_manager.unlockReg(mat_lock); 36868 36869 try self.genBinOpMir( 36870 .{ ._, .cmp }, 36871 val_ty, 36872 tmp_mcv, 36873 .{ .register = mat_reg }, 36874 ); 36875 try self.asmCmovccRegisterRegister( 36876 cc, 36877 registerAlias(tmp_reg, cmov_abi_size), 36878 registerAlias(mat_reg, cmov_abi_size), 36879 ); 36880 }, 36881 } 36882 }, 36883 }; 36884 try self.asmMemoryRegister( 36885 .{ .@"lock _", .cmpxchg }, 36886 ptr_mem, 36887 registerAlias(tmp_reg, val_abi_size), 36888 ); 36889 _ = try self.asmJccReloc(.ne, loop); 36890 return if (unused) .unreach else .{ .register = .rax }; 36891 } else { 36892 try self.asmRegisterMemory(.{ ._, .mov }, .rax, .{ 36893 .base = ptr_mem.base, 36894 .mod = .{ .rm = .{ 36895 .size = .qword, 36896 .index = ptr_mem.mod.rm.index, 36897 .scale = ptr_mem.mod.rm.scale, 36898 .disp = ptr_mem.mod.rm.disp + 0, 36899 } }, 36900 }); 36901 try self.asmRegisterMemory(.{ ._, .mov }, .rdx, .{ 36902 .base = ptr_mem.base, 36903 .mod = .{ .rm = .{ 36904 .size = .qword, 36905 .index = ptr_mem.mod.rm.index, 36906 .scale = ptr_mem.mod.rm.scale, 36907 .disp = ptr_mem.mod.rm.disp + 8, 36908 } }, 36909 }); 36910 const loop: Mir.Inst.Index = @intCast(self.mir_instructions.len); 36911 const val_mem_mcv: MCValue = switch (val_mcv) { 36912 .memory, .indirect, .load_frame => val_mcv, 36913 else => .{ .indirect = .{ 36914 .reg = try self.copyToTmpRegister(.usize, val_mcv.address()), 36915 } }, 36916 }; 36917 const val_lo_mem = try val_mem_mcv.mem(self, .{ .size = .qword }); 36918 const val_hi_mem = try val_mem_mcv.address().offset(8).deref().mem(self, .{ .size = .qword }); 36919 if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { 36920 try self.asmRegisterRegister(.{ ._, .mov }, .rbx, .rax); 36921 try self.asmRegisterRegister(.{ ._, .mov }, .rcx, .rdx); 36922 } 36923 if (rmw_op) |op| switch (op) { 36924 .Xchg => { 36925 try self.asmRegisterMemory(.{ ._, .mov }, .rbx, val_lo_mem); 36926 try self.asmRegisterMemory(.{ ._, .mov }, .rcx, val_hi_mem); 36927 }, 36928 .Add => { 36929 try self.asmRegisterMemory(.{ ._, .add }, .rbx, val_lo_mem); 36930 try self.asmRegisterMemory(.{ ._, .adc }, .rcx, val_hi_mem); 36931 }, 36932 .Sub => { 36933 try self.asmRegisterMemory(.{ ._, .sub }, .rbx, val_lo_mem); 36934 try self.asmRegisterMemory(.{ ._, .sbb }, .rcx, val_hi_mem); 36935 }, 36936 .And => { 36937 try self.asmRegisterMemory(.{ ._, .@"and" }, .rbx, val_lo_mem); 36938 try self.asmRegisterMemory(.{ ._, .@"and" }, .rcx, val_hi_mem); 36939 }, 36940 .Nand => { 36941 try self.asmRegisterMemory(.{ ._, .@"and" }, .rbx, val_lo_mem); 36942 try self.asmRegisterMemory(.{ ._, .@"and" }, .rcx, val_hi_mem); 36943 try self.asmRegister(.{ ._, .not }, .rbx); 36944 try self.asmRegister(.{ ._, .not }, .rcx); 36945 }, 36946 .Or => { 36947 try self.asmRegisterMemory(.{ ._, .@"or" }, .rbx, val_lo_mem); 36948 try self.asmRegisterMemory(.{ ._, .@"or" }, .rcx, val_hi_mem); 36949 }, 36950 .Xor => { 36951 try self.asmRegisterMemory(.{ ._, .xor }, .rbx, val_lo_mem); 36952 try self.asmRegisterMemory(.{ ._, .xor }, .rcx, val_hi_mem); 36953 }, 36954 .Min, .Max => { 36955 const cc: Condition = switch (if (val_ty.isAbiInt(zcu)) 36956 val_ty.intInfo(zcu).signedness 36957 else 36958 .unsigned) { 36959 .unsigned => switch (op) { 36960 .Min => .a, 36961 .Max => .b, 36962 else => unreachable, 36963 }, 36964 .signed => switch (op) { 36965 .Min => .g, 36966 .Max => .l, 36967 else => unreachable, 36968 }, 36969 }; 36970 36971 const tmp_reg = try self.copyToTmpRegister(.usize, .{ .register = .rcx }); 36972 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 36973 defer self.register_manager.unlockReg(tmp_lock); 36974 36975 try self.asmRegisterMemory(.{ ._, .cmp }, .rbx, val_lo_mem); 36976 try self.asmRegisterMemory(.{ ._, .sbb }, tmp_reg, val_hi_mem); 36977 try self.asmCmovccRegisterMemory(cc, .rbx, val_lo_mem); 36978 try self.asmCmovccRegisterMemory(cc, .rcx, val_hi_mem); 36979 }, 36980 }; 36981 try self.asmMemory(.{ .@"lock _16b", .cmpxchg }, ptr_mem); 36982 _ = try self.asmJccReloc(.ne, loop); 36983 36984 if (unused) return .unreach; 36985 const dst_mcv = try self.allocTempRegOrMem(val_ty, false); 36986 try self.asmMemoryRegister(.{ ._, .mov }, .{ 36987 .base = .{ .frame = dst_mcv.load_frame.index }, 36988 .mod = .{ .rm = .{ 36989 .size = .qword, 36990 .disp = dst_mcv.load_frame.off + 0, 36991 } }, 36992 }, .rax); 36993 try self.asmMemoryRegister(.{ ._, .mov }, .{ 36994 .base = .{ .frame = dst_mcv.load_frame.index }, 36995 .mod = .{ .rm = .{ 36996 .size = .qword, 36997 .disp = dst_mcv.load_frame.off + 8, 36998 } }, 36999 }, .rdx); 37000 return dst_mcv; 37001 }, 37002 .libcall => return self.fail("TODO implement x86 atomic libcall", .{}), 37003 } 37004 } 37005 37006 fn airAtomicRmw(self: *CodeGen, inst: Air.Inst.Index) !void { 37007 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 37008 const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data; 37009 37010 try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx }); 37011 const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx }); 37012 defer for (regs_lock) |lock| self.register_manager.unlockReg(lock); 37013 37014 const unused = self.liveness.isUnused(inst); 37015 37016 const ptr_ty = self.typeOf(pl_op.operand); 37017 const ptr_mcv = try self.resolveInst(pl_op.operand); 37018 37019 const val_ty = self.typeOf(extra.operand); 37020 const val_mcv = try self.resolveInst(extra.operand); 37021 37022 const result = 37023 try self.atomicOp(ptr_mcv, val_mcv, ptr_ty, val_ty, unused, extra.op(), extra.ordering()); 37024 return self.finishAir(inst, result, .{ pl_op.operand, extra.operand, .none }); 37025 } 37026 37027 fn airAtomicLoad(self: *CodeGen, inst: Air.Inst.Index) !void { 37028 const atomic_load = self.air.instructions.items(.data)[@intFromEnum(inst)].atomic_load; 37029 37030 const ptr_ty = self.typeOf(atomic_load.ptr); 37031 const ptr_mcv = try self.resolveInst(atomic_load.ptr); 37032 const ptr_lock = switch (ptr_mcv) { 37033 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 37034 else => null, 37035 }; 37036 defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); 37037 37038 const dst_mcv = 37039 if (self.reuseOperand(inst, atomic_load.ptr, 0, ptr_mcv)) 37040 ptr_mcv 37041 else 37042 try self.allocRegOrMem(inst, true); 37043 37044 try self.load(dst_mcv, ptr_ty, ptr_mcv); 37045 return self.finishAir(inst, dst_mcv, .{ atomic_load.ptr, .none, .none }); 37046 } 37047 37048 fn airAtomicStore(self: *CodeGen, inst: Air.Inst.Index, order: std.builtin.AtomicOrder) !void { 37049 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 37050 37051 const ptr_ty = self.typeOf(bin_op.lhs); 37052 const ptr_mcv = try self.resolveInst(bin_op.lhs); 37053 37054 const val_ty = self.typeOf(bin_op.rhs); 37055 const val_mcv = try self.resolveInst(bin_op.rhs); 37056 37057 const result = try self.atomicOp(ptr_mcv, val_mcv, ptr_ty, val_ty, true, null, order); 37058 return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); 37059 } 37060 37061 fn airMemset(self: *CodeGen, inst: Air.Inst.Index, safety: bool) !void { 37062 const pt = self.pt; 37063 const zcu = pt.zcu; 37064 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 37065 37066 result: { 37067 if (!safety and (try self.resolveInst(bin_op.rhs)) == .undef) break :result; 37068 37069 try self.spillRegisters(&.{ .rax, .rdi, .rsi, .rcx }); 37070 const reg_locks = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdi, .rsi, .rcx }); 37071 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 37072 37073 const dst = try self.resolveInst(bin_op.lhs); 37074 const dst_ty = self.typeOf(bin_op.lhs); 37075 const dst_locks: [2]?RegisterLock = switch (dst) { 37076 .register => |dst_reg| .{ self.register_manager.lockRegAssumeUnused(dst_reg), null }, 37077 .register_pair => |dst_regs| .{ 37078 self.register_manager.lockRegAssumeUnused(dst_regs[0]), 37079 self.register_manager.lockRegAssumeUnused(dst_regs[1]), 37080 }, 37081 else => @splat(null), 37082 }; 37083 for (dst_locks) |dst_lock| if (dst_lock) |lock| self.register_manager.unlockReg(lock); 37084 37085 const src_val = try self.resolveInst(bin_op.rhs); 37086 const elem_ty = self.typeOf(bin_op.rhs); 37087 const src_val_lock: ?RegisterLock = switch (src_val) { 37088 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 37089 else => null, 37090 }; 37091 defer if (src_val_lock) |lock| self.register_manager.unlockReg(lock); 37092 37093 const elem_abi_size: u31 = @intCast(elem_ty.abiSize(zcu)); 37094 37095 if (elem_abi_size == 1) { 37096 const dst_ptr: MCValue = switch (dst_ty.ptrSize(zcu)) { 37097 .slice => switch (dst) { 37098 .register_pair => |dst_regs| .{ .register = dst_regs[0] }, 37099 else => dst, 37100 }, 37101 .one => dst, 37102 .c, .many => unreachable, 37103 }; 37104 const len: MCValue = switch (dst_ty.ptrSize(zcu)) { 37105 .slice => switch (dst) { 37106 .register_pair => |dst_regs| .{ .register = dst_regs[1] }, 37107 else => dst.address().offset(8).deref(), 37108 }, 37109 .one => .{ .immediate = dst_ty.childType(zcu).arrayLen(zcu) }, 37110 .c, .many => unreachable, 37111 }; 37112 const len_lock: ?RegisterLock = switch (len) { 37113 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 37114 else => null, 37115 }; 37116 defer if (len_lock) |lock| self.register_manager.unlockReg(lock); 37117 37118 try self.genInlineMemset(dst_ptr, src_val, len, .{ .safety = safety }); 37119 break :result; 37120 } 37121 37122 // Store the first element, and then rely on memcpy copying forwards. 37123 // Length zero requires a runtime check - so we handle arrays specially 37124 // here to elide it. 37125 switch (dst_ty.ptrSize(zcu)) { 37126 .slice => { 37127 const slice_ptr_ty = dst_ty.slicePtrFieldType(zcu); 37128 37129 const dst_ptr: MCValue = switch (dst) { 37130 .register_pair => |dst_regs| .{ .register = dst_regs[0] }, 37131 else => dst, 37132 }; 37133 const len: MCValue = switch (dst) { 37134 .register_pair => |dst_regs| .{ .register = dst_regs[1] }, 37135 else => dst.address().offset(8).deref(), 37136 }; 37137 37138 // Used to store the number of elements for comparison. 37139 // After comparison, updated to store number of bytes needed to copy. 37140 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37141 const len_mcv: MCValue = .{ .register = len_reg }; 37142 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg); 37143 defer self.register_manager.unlockReg(len_lock); 37144 37145 try self.genSetReg(len_reg, .usize, len, .{}); 37146 try self.asmRegisterRegister(.{ ._, .@"test" }, len_reg, len_reg); 37147 37148 const skip_reloc = try self.asmJccReloc(.z, undefined); 37149 try self.store(slice_ptr_ty, dst_ptr, src_val, .{ .safety = safety }); 37150 37151 const second_elem_ptr_reg = 37152 try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37153 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg }; 37154 const second_elem_ptr_lock = 37155 self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg); 37156 defer self.register_manager.unlockReg(second_elem_ptr_lock); 37157 37158 try self.genSetReg(second_elem_ptr_reg, .usize, .{ .register_offset = .{ 37159 .reg = try self.copyToTmpRegister(.usize, dst_ptr), 37160 .off = elem_abi_size, 37161 } }, .{}); 37162 37163 try self.genBinOpMir(.{ ._, .sub }, .usize, len_mcv, .{ .immediate = 1 }); 37164 try self.asmRegisterRegisterImmediate( 37165 .{ .i_, .mul }, 37166 len_reg, 37167 len_reg, 37168 .s(elem_abi_size), 37169 ); 37170 try self.genInlineMemcpy(second_elem_ptr_mcv, dst_ptr, len_mcv, .{ .no_alias = false }); 37171 37172 self.performReloc(skip_reloc); 37173 }, 37174 .one => { 37175 const elem_ptr_ty = try pt.singleMutPtrType(elem_ty); 37176 37177 const len = dst_ty.childType(zcu).arrayLen(zcu); 37178 37179 assert(len != 0); // prevented by Sema 37180 try self.store(elem_ptr_ty, dst, src_val, .{ .safety = safety }); 37181 37182 const second_elem_ptr_reg = 37183 try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37184 const second_elem_ptr_mcv: MCValue = .{ .register = second_elem_ptr_reg }; 37185 const second_elem_ptr_lock = 37186 self.register_manager.lockRegAssumeUnused(second_elem_ptr_reg); 37187 defer self.register_manager.unlockReg(second_elem_ptr_lock); 37188 37189 try self.genSetReg(second_elem_ptr_reg, .usize, .{ .register_offset = .{ 37190 .reg = try self.copyToTmpRegister(.usize, dst), 37191 .off = elem_abi_size, 37192 } }, .{}); 37193 37194 const bytes_to_copy: MCValue = .{ .immediate = elem_abi_size * (len - 1) }; 37195 try self.genInlineMemcpy(second_elem_ptr_mcv, dst, bytes_to_copy, .{ .no_alias = false }); 37196 }, 37197 .c, .many => unreachable, 37198 } 37199 } 37200 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); 37201 } 37202 37203 fn airMemcpy(self: *CodeGen, inst: Air.Inst.Index) !void { 37204 const pt = self.pt; 37205 const zcu = pt.zcu; 37206 const bin_op = self.air.instructions.items(.data)[@intFromEnum(inst)].bin_op; 37207 37208 try self.spillRegisters(&.{ .rdi, .rsi, .rcx }); 37209 const reg_locks = self.register_manager.lockRegsAssumeUnused(3, .{ .rdi, .rsi, .rcx }); 37210 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 37211 37212 const dst = try self.resolveInst(bin_op.lhs); 37213 const dst_ty = self.typeOf(bin_op.lhs); 37214 const dst_locks: [2]?RegisterLock = switch (dst) { 37215 .register => |dst_reg| .{ self.register_manager.lockRegAssumeUnused(dst_reg), null }, 37216 .register_pair => |dst_regs| .{ 37217 self.register_manager.lockRegAssumeUnused(dst_regs[0]), 37218 self.register_manager.lockReg(dst_regs[1]), 37219 }, 37220 else => @splat(null), 37221 }; 37222 for (dst_locks) |dst_lock| if (dst_lock) |lock| self.register_manager.unlockReg(lock); 37223 37224 const src = try self.resolveInst(bin_op.rhs); 37225 const src_locks: [2]?RegisterLock = switch (src) { 37226 .register => |src_reg| .{ self.register_manager.lockReg(src_reg), null }, 37227 .register_pair => |src_regs| .{ 37228 self.register_manager.lockRegAssumeUnused(src_regs[0]), 37229 self.register_manager.lockRegAssumeUnused(src_regs[1]), 37230 }, 37231 else => @splat(null), 37232 }; 37233 for (src_locks) |src_lock| if (src_lock) |lock| self.register_manager.unlockReg(lock); 37234 37235 const len: MCValue = switch (dst_ty.ptrSize(zcu)) { 37236 .slice => len: { 37237 const len_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37238 const len_lock = self.register_manager.lockRegAssumeUnused(len_reg); 37239 defer self.register_manager.unlockReg(len_lock); 37240 37241 switch (dst) { 37242 .register_pair => |dst_regs| try self.asmRegisterRegisterImmediate( 37243 .{ .i_, .mul }, 37244 len_reg, 37245 dst_regs[1], 37246 .s(@intCast(dst_ty.childType(zcu).abiSize(zcu))), 37247 ), 37248 else => try self.asmRegisterMemoryImmediate( 37249 .{ .i_, .mul }, 37250 len_reg, 37251 try dst.address().offset(8).deref().mem(self, .{ .size = .qword }), 37252 .s(@intCast(dst_ty.childType(zcu).abiSize(zcu))), 37253 ), 37254 } 37255 break :len .{ .register = len_reg }; 37256 }, 37257 .one => len: { 37258 const array_ty = dst_ty.childType(zcu); 37259 break :len .{ .immediate = array_ty.arrayLen(zcu) * array_ty.childType(zcu).abiSize(zcu) }; 37260 }, 37261 .c, .many => unreachable, 37262 }; 37263 const len_lock: ?RegisterLock = switch (len) { 37264 .register => |reg| self.register_manager.lockReg(reg), 37265 else => null, 37266 }; 37267 defer if (len_lock) |lock| self.register_manager.unlockReg(lock); 37268 37269 const dst_ptr: MCValue = switch (dst) { 37270 .register_pair => |dst_regs| .{ .register = dst_regs[0] }, 37271 else => dst, 37272 }; 37273 const src_ptr: MCValue = switch (src) { 37274 .register_pair => |src_regs| .{ .register = src_regs[0] }, 37275 else => src, 37276 }; 37277 37278 try self.genInlineMemcpy(dst_ptr, src_ptr, len, .{ .no_alias = true }); 37279 37280 return self.finishAir(inst, .unreach, .{ bin_op.lhs, bin_op.rhs, .none }); 37281 } 37282 37283 fn airTagName(self: *CodeGen, inst: Air.Inst.Index) !void { 37284 const pt = self.pt; 37285 const zcu = pt.zcu; 37286 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 37287 const inst_ty = self.typeOfIndex(inst); 37288 const enum_ty = self.typeOf(un_op); 37289 37290 // We need a properly aligned and sized call frame to be able to call this function. 37291 { 37292 const needed_call_frame: FrameAlloc = .init(.{ 37293 .size = inst_ty.abiSize(zcu), 37294 .alignment = inst_ty.abiAlignment(zcu), 37295 }); 37296 const frame_allocs_slice = self.frame_allocs.slice(); 37297 const stack_frame_size = 37298 &frame_allocs_slice.items(.abi_size)[@intFromEnum(FrameIndex.call_frame)]; 37299 stack_frame_size.* = @max(stack_frame_size.*, needed_call_frame.abi_size); 37300 const stack_frame_align = 37301 &frame_allocs_slice.items(.abi_align)[@intFromEnum(FrameIndex.call_frame)]; 37302 stack_frame_align.* = stack_frame_align.max(needed_call_frame.abi_align); 37303 } 37304 37305 const err_ret_trace_reg = if (zcu.comp.config.any_error_tracing) err_ret_trace_reg: { 37306 const param_gpr = abi.getCAbiIntParamRegs(.auto); 37307 break :err_ret_trace_reg param_gpr[param_gpr.len - 1]; 37308 } else .none; 37309 37310 try self.spillEflagsIfOccupied(); 37311 try self.spillCallerPreservedRegs(.auto, err_ret_trace_reg); 37312 37313 const param_regs = abi.getCAbiIntParamRegs(.auto); 37314 37315 const dst_mcv = try self.allocRegOrMem(inst, false); 37316 try self.genSetReg(param_regs[0], .usize, dst_mcv.address(), .{}); 37317 37318 const operand = try self.resolveInst(un_op); 37319 try self.genSetReg(param_regs[1], enum_ty, operand, .{}); 37320 37321 const enum_lazy_sym: link.File.LazySymbol = .{ .kind = .code, .ty = enum_ty.toIntern() }; 37322 try self.genLazySymbolRef(.call, abi.getCAbiLinkerScratchReg(self.fn_type.fnCallingConvention(zcu)), enum_lazy_sym); 37323 37324 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); 37325 } 37326 37327 fn airErrorName(self: *CodeGen, inst: Air.Inst.Index) !void { 37328 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 37329 37330 const err_ty = self.typeOf(un_op); 37331 const err_mcv = try self.resolveInst(un_op); 37332 const err_reg = try self.copyToTmpRegister(err_ty, err_mcv); 37333 const err_lock = self.register_manager.lockRegAssumeUnused(err_reg); 37334 defer self.register_manager.unlockReg(err_lock); 37335 37336 const addr_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37337 const addr_lock = self.register_manager.lockRegAssumeUnused(addr_reg); 37338 defer self.register_manager.unlockReg(addr_lock); 37339 const anyerror_lazy_sym: link.File.LazySymbol = .{ .kind = .const_data, .ty = .anyerror_type }; 37340 try self.genLazySymbolRef(.lea, addr_reg, anyerror_lazy_sym); 37341 37342 const start_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37343 const start_lock = self.register_manager.lockRegAssumeUnused(start_reg); 37344 defer self.register_manager.unlockReg(start_lock); 37345 37346 const end_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 37347 const end_lock = self.register_manager.lockRegAssumeUnused(end_reg); 37348 defer self.register_manager.unlockReg(end_lock); 37349 37350 try self.truncateRegister(err_ty, err_reg.to32()); 37351 37352 try self.asmRegisterMemory( 37353 .{ ._, .mov }, 37354 start_reg.to32(), 37355 .{ 37356 .base = .{ .reg = addr_reg.to64() }, 37357 .mod = .{ .rm = .{ 37358 .size = .dword, 37359 .index = err_reg.to64(), 37360 .scale = .@"4", 37361 .disp = (1 - 1) * 4, 37362 } }, 37363 }, 37364 ); 37365 try self.asmRegisterMemory( 37366 .{ ._, .mov }, 37367 end_reg.to32(), 37368 .{ 37369 .base = .{ .reg = addr_reg.to64() }, 37370 .mod = .{ .rm = .{ 37371 .size = .dword, 37372 .index = err_reg.to64(), 37373 .scale = .@"4", 37374 .disp = (2 - 1) * 4, 37375 } }, 37376 }, 37377 ); 37378 try self.asmRegisterRegister(.{ ._, .sub }, end_reg.to32(), start_reg.to32()); 37379 try self.asmRegisterMemory( 37380 .{ ._, .lea }, 37381 start_reg.to64(), 37382 .{ 37383 .base = .{ .reg = addr_reg.to64() }, 37384 .mod = .{ .rm = .{ 37385 .size = .dword, 37386 .index = start_reg.to64(), 37387 } }, 37388 }, 37389 ); 37390 try self.asmRegisterMemory( 37391 .{ ._, .lea }, 37392 end_reg.to32(), 37393 .{ 37394 .base = .{ .reg = end_reg.to64() }, 37395 .mod = .{ .rm = .{ 37396 .size = .byte, 37397 .disp = -1, 37398 } }, 37399 }, 37400 ); 37401 37402 const dst_mcv = try self.allocRegOrMem(inst, false); 37403 try self.asmMemoryRegister( 37404 .{ ._, .mov }, 37405 .{ 37406 .base = .{ .frame = dst_mcv.load_frame.index }, 37407 .mod = .{ .rm = .{ 37408 .size = .qword, 37409 .disp = dst_mcv.load_frame.off, 37410 } }, 37411 }, 37412 start_reg.to64(), 37413 ); 37414 try self.asmMemoryRegister( 37415 .{ ._, .mov }, 37416 .{ 37417 .base = .{ .frame = dst_mcv.load_frame.index }, 37418 .mod = .{ .rm = .{ 37419 .size = .qword, 37420 .disp = dst_mcv.load_frame.off + 8, 37421 } }, 37422 }, 37423 end_reg.to64(), 37424 ); 37425 37426 return self.finishAir(inst, dst_mcv, .{ un_op, .none, .none }); 37427 } 37428 37429 fn airSplat(self: *CodeGen, inst: Air.Inst.Index) !void { 37430 const pt = self.pt; 37431 const zcu = pt.zcu; 37432 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 37433 const vector_ty = self.typeOfIndex(inst); 37434 const vector_len = vector_ty.vectorLen(zcu); 37435 const dst_rc = self.regSetForType(vector_ty); 37436 const scalar_ty = self.typeOf(ty_op.operand); 37437 37438 const result: MCValue = result: { 37439 switch (scalar_ty.zigTypeTag(zcu)) { 37440 else => {}, 37441 .bool => { 37442 const regs = 37443 try self.register_manager.allocRegs(2, .{ inst, null }, abi.RegisterClass.gp); 37444 const reg_locks = self.register_manager.lockRegsAssumeUnused(2, regs); 37445 defer for (reg_locks) |lock| self.register_manager.unlockReg(lock); 37446 37447 try self.genSetReg(regs[1], vector_ty, .{ .immediate = 0 }, .{}); 37448 try self.genSetReg( 37449 regs[1], 37450 vector_ty, 37451 .{ .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(64 - vector_len) }, 37452 .{}, 37453 ); 37454 const src_mcv = try self.resolveInst(ty_op.operand); 37455 const abi_size = @max(std.math.divCeil(u32, vector_len, 8) catch unreachable, 4); 37456 try self.asmCmovccRegisterRegister( 37457 switch (src_mcv) { 37458 .eflags => |cc| cc, 37459 .register => |src_reg| cc: { 37460 try self.asmRegisterImmediate(.{ ._, .@"test" }, src_reg.to8(), .u(1)); 37461 break :cc .nz; 37462 }, 37463 else => cc: { 37464 try self.asmMemoryImmediate( 37465 .{ ._, .@"test" }, 37466 try src_mcv.mem(self, .{ .size = .byte }), 37467 .u(1), 37468 ); 37469 break :cc .nz; 37470 }, 37471 }, 37472 registerAlias(regs[0], abi_size), 37473 registerAlias(regs[1], abi_size), 37474 ); 37475 break :result .{ .register = regs[0] }; 37476 }, 37477 .int => if (self.hasFeature(.avx2)) avx2: { 37478 const mir_tag = @as(?Mir.Inst.FixedTag, switch (scalar_ty.intInfo(zcu).bits) { 37479 else => null, 37480 1...8 => switch (vector_len) { 37481 else => null, 37482 1...32 => .{ .vp_b, .broadcast }, 37483 }, 37484 9...16 => switch (vector_len) { 37485 else => null, 37486 1...16 => .{ .vp_w, .broadcast }, 37487 }, 37488 17...32 => switch (vector_len) { 37489 else => null, 37490 1...8 => .{ .vp_d, .broadcast }, 37491 }, 37492 33...64 => switch (vector_len) { 37493 else => null, 37494 1...4 => .{ .vp_q, .broadcast }, 37495 }, 37496 65...128 => switch (vector_len) { 37497 else => null, 37498 1...2 => .{ .v_i128, .broadcast }, 37499 }, 37500 }) orelse break :avx2; 37501 37502 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.sse); 37503 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 37504 defer self.register_manager.unlockReg(dst_lock); 37505 37506 const src_mcv = try self.resolveInst(ty_op.operand); 37507 if (src_mcv.isBase()) try self.asmRegisterMemory( 37508 mir_tag, 37509 registerAlias(dst_reg, @intCast(vector_ty.abiSize(zcu))), 37510 try src_mcv.mem(self, .{ .size = self.memSize(scalar_ty) }), 37511 ) else { 37512 if (mir_tag[0] == .v_i128) break :avx2; 37513 try self.genSetReg(dst_reg, scalar_ty, src_mcv, .{}); 37514 try self.asmRegisterRegister( 37515 mir_tag, 37516 registerAlias(dst_reg, @intCast(vector_ty.abiSize(zcu))), 37517 registerAlias(dst_reg, @intCast(scalar_ty.abiSize(zcu))), 37518 ); 37519 } 37520 break :result .{ .register = dst_reg }; 37521 } else { 37522 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.sse); 37523 const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); 37524 defer self.register_manager.unlockReg(dst_lock); 37525 37526 try self.genSetReg(dst_reg, scalar_ty, .{ .air_ref = ty_op.operand }, .{}); 37527 if (vector_len == 1) break :result .{ .register = dst_reg }; 37528 37529 const dst_alias = registerAlias(dst_reg, @intCast(vector_ty.abiSize(zcu))); 37530 const scalar_bits = scalar_ty.intInfo(zcu).bits; 37531 if (switch (scalar_bits) { 37532 1...8 => true, 37533 9...128 => false, 37534 else => unreachable, 37535 }) if (self.hasFeature(.avx)) try self.asmRegisterRegisterRegister( 37536 .{ .vp_, .unpcklbw }, 37537 dst_alias, 37538 dst_alias, 37539 dst_alias, 37540 ) else try self.asmRegisterRegister( 37541 .{ .p_, .unpcklbw }, 37542 dst_alias, 37543 dst_alias, 37544 ); 37545 if (switch (scalar_bits) { 37546 1...8 => vector_len > 2, 37547 9...16 => true, 37548 17...128 => false, 37549 else => unreachable, 37550 }) try self.asmRegisterRegisterImmediate( 37551 .{ if (self.hasFeature(.avx)) .vp_w else .p_w, .shufl }, 37552 dst_alias, 37553 dst_alias, 37554 .u(0b00_00_00_00), 37555 ); 37556 if (switch (scalar_bits) { 37557 1...8 => vector_len > 4, 37558 9...16 => vector_len > 2, 37559 17...64 => true, 37560 65...128 => false, 37561 else => unreachable, 37562 }) try self.asmRegisterRegisterImmediate( 37563 .{ if (self.hasFeature(.avx)) .vp_d else .p_d, .shuf }, 37564 dst_alias, 37565 dst_alias, 37566 .u(if (scalar_bits <= 64) 0b00_00_00_00 else 0b01_00_01_00), 37567 ); 37568 break :result .{ .register = dst_reg }; 37569 }, 37570 .float => switch (scalar_ty.floatBits(self.target.*)) { 37571 32 => switch (vector_len) { 37572 1 => { 37573 const src_mcv = try self.resolveInst(ty_op.operand); 37574 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; 37575 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37576 try self.genSetReg(dst_reg, scalar_ty, src_mcv, .{}); 37577 break :result .{ .register = dst_reg }; 37578 }, 37579 2...4 => { 37580 const src_mcv = try self.resolveInst(ty_op.operand); 37581 if (self.hasFeature(.avx)) { 37582 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37583 if (src_mcv.isBase()) try self.asmRegisterMemory( 37584 .{ .v_ss, .broadcast }, 37585 dst_reg.to128(), 37586 try src_mcv.mem(self, .{ .size = .dword }), 37587 ) else { 37588 const src_reg = if (src_mcv.isRegister()) 37589 src_mcv.getReg().? 37590 else 37591 try self.copyToTmpRegister(scalar_ty, src_mcv); 37592 try self.asmRegisterRegisterRegisterImmediate( 37593 .{ .v_ps, .shuf }, 37594 dst_reg.to128(), 37595 src_reg.to128(), 37596 src_reg.to128(), 37597 .u(0), 37598 ); 37599 } 37600 break :result .{ .register = dst_reg }; 37601 } else { 37602 const dst_mcv = if (src_mcv.isRegister() and 37603 self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) 37604 src_mcv 37605 else 37606 try self.copyToRegisterWithInstTracking(inst, scalar_ty, src_mcv); 37607 const dst_reg = dst_mcv.getReg().?; 37608 try self.asmRegisterRegisterImmediate( 37609 .{ ._ps, .shuf }, 37610 dst_reg.to128(), 37611 dst_reg.to128(), 37612 .u(0), 37613 ); 37614 break :result dst_mcv; 37615 } 37616 }, 37617 5...8 => if (self.hasFeature(.avx)) { 37618 const src_mcv = try self.resolveInst(ty_op.operand); 37619 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37620 if (src_mcv.isBase()) try self.asmRegisterMemory( 37621 .{ .v_ss, .broadcast }, 37622 dst_reg.to256(), 37623 try src_mcv.mem(self, .{ .size = .dword }), 37624 ) else { 37625 const src_reg = if (src_mcv.isRegister()) 37626 src_mcv.getReg().? 37627 else 37628 try self.copyToTmpRegister(scalar_ty, src_mcv); 37629 if (self.hasFeature(.avx2)) try self.asmRegisterRegister( 37630 .{ .v_ss, .broadcast }, 37631 dst_reg.to256(), 37632 src_reg.to128(), 37633 ) else { 37634 try self.asmRegisterRegisterRegisterImmediate( 37635 .{ .v_ps, .shuf }, 37636 dst_reg.to128(), 37637 src_reg.to128(), 37638 src_reg.to128(), 37639 .u(0), 37640 ); 37641 try self.asmRegisterRegisterRegisterImmediate( 37642 .{ .v_f128, .insert }, 37643 dst_reg.to256(), 37644 dst_reg.to256(), 37645 dst_reg.to128(), 37646 .u(1), 37647 ); 37648 } 37649 } 37650 break :result .{ .register = dst_reg }; 37651 }, 37652 else => {}, 37653 }, 37654 64 => switch (vector_len) { 37655 1 => { 37656 const src_mcv = try self.resolveInst(ty_op.operand); 37657 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; 37658 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37659 try self.genSetReg(dst_reg, scalar_ty, src_mcv, .{}); 37660 break :result .{ .register = dst_reg }; 37661 }, 37662 2 => { 37663 const src_mcv = try self.resolveInst(ty_op.operand); 37664 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37665 if (self.hasFeature(.sse3)) { 37666 if (src_mcv.isBase()) try self.asmRegisterMemory( 37667 if (self.hasFeature(.avx)) .{ .v_, .movddup } else .{ ._, .movddup }, 37668 dst_reg.to128(), 37669 try src_mcv.mem(self, .{ .size = .qword }), 37670 ) else try self.asmRegisterRegister( 37671 if (self.hasFeature(.avx)) .{ .v_, .movddup } else .{ ._, .movddup }, 37672 dst_reg.to128(), 37673 (if (src_mcv.isRegister()) 37674 src_mcv.getReg().? 37675 else 37676 try self.copyToTmpRegister(scalar_ty, src_mcv)).to128(), 37677 ); 37678 break :result .{ .register = dst_reg }; 37679 } else try self.asmRegisterRegister( 37680 .{ ._ps, .movlh }, 37681 dst_reg.to128(), 37682 (if (src_mcv.isRegister()) 37683 src_mcv.getReg().? 37684 else 37685 try self.copyToTmpRegister(scalar_ty, src_mcv)).to128(), 37686 ); 37687 }, 37688 3...4 => if (self.hasFeature(.avx)) { 37689 const src_mcv = try self.resolveInst(ty_op.operand); 37690 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37691 if (src_mcv.isBase()) try self.asmRegisterMemory( 37692 .{ .v_sd, .broadcast }, 37693 dst_reg.to256(), 37694 try src_mcv.mem(self, .{ .size = .qword }), 37695 ) else { 37696 const src_reg = if (src_mcv.isRegister()) 37697 src_mcv.getReg().? 37698 else 37699 try self.copyToTmpRegister(scalar_ty, src_mcv); 37700 if (self.hasFeature(.avx2)) try self.asmRegisterRegister( 37701 .{ .v_sd, .broadcast }, 37702 dst_reg.to256(), 37703 src_reg.to128(), 37704 ) else { 37705 try self.asmRegisterRegister( 37706 .{ .v_, .movddup }, 37707 dst_reg.to128(), 37708 src_reg.to128(), 37709 ); 37710 try self.asmRegisterRegisterRegisterImmediate( 37711 .{ .v_f128, .insert }, 37712 dst_reg.to256(), 37713 dst_reg.to256(), 37714 dst_reg.to128(), 37715 .u(1), 37716 ); 37717 } 37718 } 37719 break :result .{ .register = dst_reg }; 37720 }, 37721 else => {}, 37722 }, 37723 128 => switch (vector_len) { 37724 1 => { 37725 const src_mcv = try self.resolveInst(ty_op.operand); 37726 if (self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) break :result src_mcv; 37727 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37728 try self.genSetReg(dst_reg, scalar_ty, src_mcv, .{}); 37729 break :result .{ .register = dst_reg }; 37730 }, 37731 2 => if (self.hasFeature(.avx)) { 37732 const src_mcv = try self.resolveInst(ty_op.operand); 37733 const dst_reg = try self.register_manager.allocReg(inst, dst_rc); 37734 if (src_mcv.isBase()) try self.asmRegisterMemory( 37735 .{ .v_f128, .broadcast }, 37736 dst_reg.to256(), 37737 try src_mcv.mem(self, .{ .size = .xword }), 37738 ) else { 37739 const src_reg = if (src_mcv.isRegister()) 37740 src_mcv.getReg().? 37741 else 37742 try self.copyToTmpRegister(scalar_ty, src_mcv); 37743 try self.asmRegisterRegisterRegisterImmediate( 37744 .{ .v_f128, .insert }, 37745 dst_reg.to256(), 37746 src_reg.to256(), 37747 src_reg.to128(), 37748 .u(1), 37749 ); 37750 } 37751 break :result .{ .register = dst_reg }; 37752 }, 37753 else => {}, 37754 }, 37755 16, 80 => {}, 37756 else => unreachable, 37757 }, 37758 } 37759 return self.fail("TODO implement airSplat for {}", .{vector_ty.fmt(pt)}); 37760 }; 37761 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 37762 } 37763 37764 fn airSelect(self: *CodeGen, inst: Air.Inst.Index) !void { 37765 const pt = self.pt; 37766 const zcu = pt.zcu; 37767 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 37768 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 37769 const ty = self.typeOfIndex(inst); 37770 const vec_len = ty.vectorLen(zcu); 37771 const elem_ty = ty.childType(zcu); 37772 const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu)); 37773 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 37774 const pred_ty = self.typeOf(pl_op.operand); 37775 37776 const result = result: { 37777 const has_blend = self.hasFeature(.sse4_1); 37778 const has_avx = self.hasFeature(.avx); 37779 const need_xmm0 = has_blend and !has_avx; 37780 const pred_mcv = try self.resolveInst(pl_op.operand); 37781 const mask_reg = mask: { 37782 switch (pred_mcv) { 37783 .register => |pred_reg| switch (pred_reg.class()) { 37784 .general_purpose => {}, 37785 .sse => if (need_xmm0 and pred_reg.id() != comptime Register.xmm0.id()) { 37786 try self.register_manager.getKnownReg(.xmm0, null); 37787 try self.genSetReg(.xmm0, pred_ty, pred_mcv, .{}); 37788 break :mask .xmm0; 37789 } else break :mask if (has_blend) 37790 pred_reg 37791 else 37792 try self.copyToTmpRegister(pred_ty, pred_mcv), 37793 else => unreachable, 37794 }, 37795 else => {}, 37796 } 37797 const mask_reg: Register = if (need_xmm0) mask_reg: { 37798 try self.register_manager.getKnownReg(.xmm0, null); 37799 break :mask_reg .xmm0; 37800 } else try self.register_manager.allocReg(null, abi.RegisterClass.sse); 37801 const mask_alias = registerAlias(mask_reg, abi_size); 37802 const mask_lock = self.register_manager.lockRegAssumeUnused(mask_reg); 37803 defer self.register_manager.unlockReg(mask_lock); 37804 37805 const pred_fits_in_elem = vec_len <= elem_abi_size; 37806 if (self.hasFeature(.avx2) and abi_size <= 32) { 37807 if (pred_mcv.isRegister()) broadcast: { 37808 try self.asmRegisterRegister( 37809 .{ .v_d, .mov }, 37810 mask_reg.to128(), 37811 pred_mcv.getReg().?.to32(), 37812 ); 37813 if (pred_fits_in_elem and vec_len > 1) try self.asmRegisterRegister( 37814 .{ switch (elem_abi_size) { 37815 1 => .vp_b, 37816 2 => .vp_w, 37817 3...4 => .vp_d, 37818 5...8 => .vp_q, 37819 9...16 => { 37820 try self.asmRegisterRegisterRegisterImmediate( 37821 .{ .v_f128, .insert }, 37822 mask_alias, 37823 mask_alias, 37824 mask_reg.to128(), 37825 .u(1), 37826 ); 37827 break :broadcast; 37828 }, 37829 17...32 => break :broadcast, 37830 else => unreachable, 37831 }, .broadcast }, 37832 mask_alias, 37833 mask_reg.to128(), 37834 ); 37835 } else try self.asmRegisterMemory( 37836 .{ switch (vec_len) { 37837 1...8 => .vp_b, 37838 9...16 => .vp_w, 37839 17...32 => .vp_d, 37840 else => unreachable, 37841 }, .broadcast }, 37842 mask_alias, 37843 if (pred_mcv.isBase()) try pred_mcv.mem(self, .{ .size = .byte }) else .{ 37844 .base = .{ .reg = (try self.copyToTmpRegister( 37845 .usize, 37846 pred_mcv.address(), 37847 )).to64() }, 37848 .mod = .{ .rm = .{ .size = .byte } }, 37849 }, 37850 ); 37851 } else if (abi_size <= 16) broadcast: { 37852 try self.asmRegisterRegister( 37853 .{ if (has_avx) .v_d else ._d, .mov }, 37854 mask_alias, 37855 (if (pred_mcv.isRegister()) 37856 pred_mcv.getReg().? 37857 else 37858 try self.copyToTmpRegister(pred_ty, pred_mcv.address())).to32(), 37859 ); 37860 if (!pred_fits_in_elem or vec_len == 1) break :broadcast; 37861 if (elem_abi_size <= 1) { 37862 if (has_avx) try self.asmRegisterRegisterRegister( 37863 .{ .vp_, .unpcklbw }, 37864 mask_alias, 37865 mask_alias, 37866 mask_alias, 37867 ) else try self.asmRegisterRegister( 37868 .{ .p_, .unpcklbw }, 37869 mask_alias, 37870 mask_alias, 37871 ); 37872 if (abi_size <= 2) break :broadcast; 37873 } 37874 if (elem_abi_size <= 2) { 37875 try self.asmRegisterRegisterImmediate( 37876 .{ if (has_avx) .vp_w else .p_w, .shufl }, 37877 mask_alias, 37878 mask_alias, 37879 .u(0b00_00_00_00), 37880 ); 37881 if (abi_size <= 8) break :broadcast; 37882 } 37883 try self.asmRegisterRegisterImmediate( 37884 .{ if (has_avx) .vp_d else .p_d, .shuf }, 37885 mask_alias, 37886 mask_alias, 37887 .u(switch (elem_abi_size) { 37888 1...2, 5...8 => 0b01_00_01_00, 37889 3...4 => 0b00_00_00_00, 37890 else => unreachable, 37891 }), 37892 ); 37893 } else return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); 37894 const elem_bits: u16 = @intCast(elem_abi_size * 8); 37895 const mask_elem_ty = try pt.intType(.unsigned, elem_bits); 37896 const mask_ty = try pt.vectorType(.{ .len = vec_len, .child = mask_elem_ty.toIntern() }); 37897 if (!pred_fits_in_elem) if (self.hasFeature(.ssse3)) { 37898 var mask_elems: [32]InternPool.Index = undefined; 37899 for (mask_elems[0..vec_len], 0..) |*elem, bit| elem.* = try pt.intern(.{ .int = .{ 37900 .ty = mask_elem_ty.toIntern(), 37901 .storage = .{ .u64 = bit / elem_bits }, 37902 } }); 37903 const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 37904 .ty = mask_ty.toIntern(), 37905 .storage = .{ .elems = mask_elems[0..vec_len] }, 37906 } }))); 37907 const mask_mem: Memory = .{ 37908 .base = .{ .reg = try self.copyToTmpRegister(.usize, mask_mcv.address()) }, 37909 .mod = .{ .rm = .{ .size = self.memSize(ty) } }, 37910 }; 37911 if (has_avx) try self.asmRegisterRegisterMemory( 37912 .{ .vp_b, .shuf }, 37913 mask_alias, 37914 mask_alias, 37915 mask_mem, 37916 ) else try self.asmRegisterMemory( 37917 .{ .p_b, .shuf }, 37918 mask_alias, 37919 mask_mem, 37920 ); 37921 } else return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); 37922 { 37923 var mask_elems: [32]InternPool.Index = undefined; 37924 for (mask_elems[0..vec_len], 0..) |*elem, bit| elem.* = try pt.intern(.{ .int = .{ 37925 .ty = mask_elem_ty.toIntern(), 37926 .storage = .{ .u64 = @as(u32, 1) << @intCast(bit & (elem_bits - 1)) }, 37927 } }); 37928 const mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 37929 .ty = mask_ty.toIntern(), 37930 .storage = .{ .elems = mask_elems[0..vec_len] }, 37931 } }))); 37932 const mask_mem: Memory = .{ 37933 .base = .{ .reg = try self.copyToTmpRegister(.usize, mask_mcv.address()) }, 37934 .mod = .{ .rm = .{ .size = self.memSize(ty) } }, 37935 }; 37936 if (has_avx) { 37937 try self.asmRegisterRegisterMemory( 37938 .{ .vp_, .@"and" }, 37939 mask_alias, 37940 mask_alias, 37941 mask_mem, 37942 ); 37943 try self.asmRegisterRegisterMemory( 37944 .{ .vp_d, .cmpeq }, 37945 mask_alias, 37946 mask_alias, 37947 mask_mem, 37948 ); 37949 } else { 37950 try self.asmRegisterMemory( 37951 .{ .p_, .@"and" }, 37952 mask_alias, 37953 mask_mem, 37954 ); 37955 try self.asmRegisterMemory( 37956 .{ .p_d, .cmpeq }, 37957 mask_alias, 37958 mask_mem, 37959 ); 37960 } 37961 } 37962 break :mask mask_reg; 37963 }; 37964 const mask_alias = registerAlias(mask_reg, abi_size); 37965 const mask_lock = self.register_manager.lockRegAssumeUnused(mask_reg); 37966 defer self.register_manager.unlockReg(mask_lock); 37967 37968 const lhs_mcv = try self.resolveInst(extra.lhs); 37969 const lhs_lock = switch (lhs_mcv) { 37970 .register => |lhs_reg| self.register_manager.lockRegAssumeUnused(lhs_reg), 37971 else => null, 37972 }; 37973 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 37974 37975 const rhs_mcv = try self.resolveInst(extra.rhs); 37976 const rhs_lock = switch (rhs_mcv) { 37977 .register => |rhs_reg| self.register_manager.lockReg(rhs_reg), 37978 else => null, 37979 }; 37980 defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); 37981 37982 const reuse_mcv = if (has_blend) rhs_mcv else lhs_mcv; 37983 const dst_mcv: MCValue = if (reuse_mcv.isRegister() and self.reuseOperand( 37984 inst, 37985 if (has_blend) extra.rhs else extra.lhs, 37986 @intFromBool(has_blend), 37987 reuse_mcv, 37988 )) reuse_mcv else if (has_avx) 37989 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 37990 else 37991 try self.copyToRegisterWithInstTracking(inst, ty, reuse_mcv); 37992 const dst_reg = dst_mcv.getReg().?; 37993 const dst_alias = registerAlias(dst_reg, abi_size); 37994 const dst_lock = self.register_manager.lockReg(dst_reg); 37995 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 37996 37997 const mir_tag = @as(?Mir.Inst.FixedTag, switch (ty.childType(zcu).zigTypeTag(zcu)) { 37998 else => null, 37999 .int => switch (abi_size) { 38000 0 => unreachable, 38001 1...16 => if (has_avx) 38002 .{ .vp_b, .blendv } 38003 else if (has_blend) 38004 .{ .p_b, .blendv } 38005 else 38006 .{ .p_, undefined }, 38007 17...32 => if (self.hasFeature(.avx2)) 38008 .{ .vp_b, .blendv } 38009 else 38010 null, 38011 else => null, 38012 }, 38013 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 38014 else => unreachable, 38015 16, 80, 128 => null, 38016 32 => switch (vec_len) { 38017 0 => unreachable, 38018 1...4 => if (has_avx) .{ .v_ps, .blendv } else .{ ._ps, .blendv }, 38019 5...8 => if (has_avx) .{ .v_ps, .blendv } else null, 38020 else => null, 38021 }, 38022 64 => switch (vec_len) { 38023 0 => unreachable, 38024 1...2 => if (has_avx) .{ .v_pd, .blendv } else .{ ._pd, .blendv }, 38025 3...4 => if (has_avx) .{ .v_pd, .blendv } else null, 38026 else => null, 38027 }, 38028 }, 38029 }) orelse return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); 38030 if (has_avx) { 38031 const rhs_alias = if (rhs_mcv.isRegister()) 38032 registerAlias(rhs_mcv.getReg().?, abi_size) 38033 else rhs: { 38034 try self.genSetReg(dst_reg, ty, rhs_mcv, .{}); 38035 break :rhs dst_alias; 38036 }; 38037 if (lhs_mcv.isBase()) try self.asmRegisterRegisterMemoryRegister( 38038 mir_tag, 38039 dst_alias, 38040 rhs_alias, 38041 try lhs_mcv.mem(self, .{ .size = self.memSize(ty) }), 38042 mask_alias, 38043 ) else try self.asmRegisterRegisterRegisterRegister( 38044 mir_tag, 38045 dst_alias, 38046 rhs_alias, 38047 registerAlias(if (lhs_mcv.isRegister()) 38048 lhs_mcv.getReg().? 38049 else 38050 try self.copyToTmpRegister(ty, lhs_mcv), abi_size), 38051 mask_alias, 38052 ); 38053 } else if (has_blend) if (lhs_mcv.isBase()) try self.asmRegisterMemoryRegister( 38054 mir_tag, 38055 dst_alias, 38056 try lhs_mcv.mem(self, .{ .size = self.memSize(ty) }), 38057 mask_alias, 38058 ) else try self.asmRegisterRegisterRegister( 38059 mir_tag, 38060 dst_alias, 38061 registerAlias(if (lhs_mcv.isRegister()) 38062 lhs_mcv.getReg().? 38063 else 38064 try self.copyToTmpRegister(ty, lhs_mcv), abi_size), 38065 mask_alias, 38066 ) else { 38067 const mir_fixes = @as(?Mir.Inst.Fixes, switch (elem_ty.zigTypeTag(zcu)) { 38068 else => null, 38069 .int => .p_, 38070 .float => switch (elem_ty.floatBits(self.target.*)) { 38071 32 => ._ps, 38072 64 => ._pd, 38073 16, 80, 128 => null, 38074 else => unreachable, 38075 }, 38076 }) orelse return self.fail("TODO implement airSelect for {}", .{ty.fmt(pt)}); 38077 try self.asmRegisterRegister(.{ mir_fixes, .@"and" }, dst_alias, mask_alias); 38078 if (rhs_mcv.isBase()) try self.asmRegisterMemory( 38079 .{ mir_fixes, .andn }, 38080 mask_alias, 38081 try rhs_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 38082 ) else try self.asmRegisterRegister( 38083 .{ mir_fixes, .andn }, 38084 mask_alias, 38085 if (rhs_mcv.isRegister()) 38086 rhs_mcv.getReg().? 38087 else 38088 try self.copyToTmpRegister(ty, rhs_mcv), 38089 ); 38090 try self.asmRegisterRegister(.{ mir_fixes, .@"or" }, dst_alias, mask_alias); 38091 } 38092 break :result dst_mcv; 38093 }; 38094 return self.finishAir(inst, result, .{ pl_op.operand, extra.lhs, extra.rhs }); 38095 } 38096 38097 fn airShuffle(self: *CodeGen, inst: Air.Inst.Index) !void { 38098 const pt = self.pt; 38099 const zcu = pt.zcu; 38100 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 38101 const extra = self.air.extraData(Air.Shuffle, ty_pl.payload).data; 38102 38103 const dst_ty = self.typeOfIndex(inst); 38104 const elem_ty = dst_ty.childType(zcu); 38105 const elem_abi_size: u16 = @intCast(elem_ty.abiSize(zcu)); 38106 const dst_abi_size: u32 = @intCast(dst_ty.abiSize(zcu)); 38107 const lhs_ty = self.typeOf(extra.a); 38108 const lhs_abi_size: u32 = @intCast(lhs_ty.abiSize(zcu)); 38109 const rhs_ty = self.typeOf(extra.b); 38110 const rhs_abi_size: u32 = @intCast(rhs_ty.abiSize(zcu)); 38111 const max_abi_size = @max(dst_abi_size, lhs_abi_size, rhs_abi_size); 38112 38113 const ExpectedContents = [32]?i32; 38114 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 38115 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 38116 const allocator = stack.get(); 38117 38118 const mask_elems = try allocator.alloc(?i32, extra.mask_len); 38119 defer allocator.free(mask_elems); 38120 for (mask_elems, 0..) |*mask_elem, elem_index| { 38121 const mask_elem_val = 38122 Value.fromInterned(extra.mask).elemValue(pt, elem_index) catch unreachable; 38123 mask_elem.* = if (mask_elem_val.isUndef(zcu)) 38124 null 38125 else 38126 @intCast(mask_elem_val.toSignedInt(zcu)); 38127 } 38128 38129 const has_avx = self.hasFeature(.avx); 38130 const result = @as(?MCValue, result: { 38131 for (mask_elems) |mask_elem| { 38132 if (mask_elem) |_| break; 38133 } else break :result try self.allocRegOrMem(inst, true); 38134 38135 for (mask_elems, 0..) |mask_elem, elem_index| { 38136 if (mask_elem orelse continue != elem_index) break; 38137 } else { 38138 const lhs_mcv = try self.resolveInst(extra.a); 38139 if (self.reuseOperand(inst, extra.a, 0, lhs_mcv)) break :result lhs_mcv; 38140 const dst_mcv = try self.allocRegOrMem(inst, true); 38141 try self.genCopy(dst_ty, dst_mcv, lhs_mcv, .{}); 38142 break :result dst_mcv; 38143 } 38144 38145 for (mask_elems, 0..) |mask_elem, elem_index| { 38146 if (~(mask_elem orelse continue) != elem_index) break; 38147 } else { 38148 const rhs_mcv = try self.resolveInst(extra.b); 38149 if (self.reuseOperand(inst, extra.b, 1, rhs_mcv)) break :result rhs_mcv; 38150 const dst_mcv = try self.allocRegOrMem(inst, true); 38151 try self.genCopy(dst_ty, dst_mcv, rhs_mcv, .{}); 38152 break :result dst_mcv; 38153 } 38154 38155 for ([_]Mir.Inst.Tag{ .unpckl, .unpckh }) |variant| unpck: { 38156 if (elem_abi_size > 8) break :unpck; 38157 if (dst_abi_size > self.vectorSize(if (elem_abi_size >= 4) .float else .int)) break :unpck; 38158 38159 var sources: [2]?u1 = @splat(null); 38160 for (mask_elems, 0..) |maybe_mask_elem, elem_index| { 38161 const mask_elem = maybe_mask_elem orelse continue; 38162 const mask_elem_index = 38163 std.math.cast(u5, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :unpck; 38164 const elem_byte = (elem_index >> 1) * elem_abi_size; 38165 if (mask_elem_index * elem_abi_size != (elem_byte & 0b0111) | @as(u4, switch (variant) { 38166 .unpckl => 0b0000, 38167 .unpckh => 0b1000, 38168 else => unreachable, 38169 }) | (elem_byte << 1 & 0b10000)) break :unpck; 38170 38171 const source = @intFromBool(mask_elem < 0); 38172 if (sources[elem_index & 0b00001]) |prev_source| { 38173 if (source != prev_source) break :unpck; 38174 } else sources[elem_index & 0b00001] = source; 38175 } 38176 if (sources[0] orelse break :unpck == sources[1] orelse break :unpck) break :unpck; 38177 38178 const operands = [2]Air.Inst.Ref{ extra.a, extra.b }; 38179 const operand_tys = [2]Type{ lhs_ty, rhs_ty }; 38180 const lhs_mcv = try self.resolveInst(operands[sources[0].?]); 38181 const rhs_mcv = try self.resolveInst(operands[sources[1].?]); 38182 38183 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38184 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, lhs_mcv)) 38185 lhs_mcv 38186 else if (has_avx and lhs_mcv.isRegister()) 38187 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38188 else 38189 try self.copyToRegisterWithInstTracking(inst, operand_tys[sources[0].?], lhs_mcv); 38190 const dst_reg = dst_mcv.getReg().?; 38191 const dst_alias = registerAlias(dst_reg, max_abi_size); 38192 38193 const mir_tag: Mir.Inst.FixedTag = if ((elem_abi_size >= 4 and elem_ty.isRuntimeFloat()) or 38194 (dst_abi_size > 16 and !self.hasFeature(.avx2))) .{ switch (elem_abi_size) { 38195 4 => if (has_avx) .v_ps else ._ps, 38196 8 => if (has_avx) .v_pd else ._pd, 38197 else => unreachable, 38198 }, variant } else .{ if (has_avx) .vp_ else .p_, switch (variant) { 38199 .unpckl => switch (elem_abi_size) { 38200 1 => .unpcklbw, 38201 2 => .unpcklwd, 38202 4 => .unpckldq, 38203 8 => .unpcklqdq, 38204 else => unreachable, 38205 }, 38206 .unpckh => switch (elem_abi_size) { 38207 1 => .unpckhbw, 38208 2 => .unpckhwd, 38209 4 => .unpckhdq, 38210 8 => .unpckhqdq, 38211 else => unreachable, 38212 }, 38213 else => unreachable, 38214 } }; 38215 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemory( 38216 mir_tag, 38217 dst_alias, 38218 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38219 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38220 ) else try self.asmRegisterRegisterRegister( 38221 mir_tag, 38222 dst_alias, 38223 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38224 registerAlias(if (rhs_mcv.isRegister()) 38225 rhs_mcv.getReg().? 38226 else 38227 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38228 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemory( 38229 mir_tag, 38230 dst_alias, 38231 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38232 ) else try self.asmRegisterRegister( 38233 mir_tag, 38234 dst_alias, 38235 registerAlias(if (rhs_mcv.isRegister()) 38236 rhs_mcv.getReg().? 38237 else 38238 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38239 ); 38240 break :result dst_mcv; 38241 } 38242 38243 pshufd: { 38244 if (elem_abi_size != 4) break :pshufd; 38245 if (max_abi_size > self.vectorSize(.float)) break :pshufd; 38246 38247 var control: u8 = 0b00_00_00_00; 38248 var sources: [1]?u1 = @splat(null); 38249 for (mask_elems, 0..) |maybe_mask_elem, elem_index| { 38250 const mask_elem = maybe_mask_elem orelse continue; 38251 const mask_elem_index: u3 = @intCast(if (mask_elem < 0) ~mask_elem else mask_elem); 38252 if (mask_elem_index & 0b100 != elem_index & 0b100) break :pshufd; 38253 38254 const source = @intFromBool(mask_elem < 0); 38255 if (sources[0]) |prev_source| { 38256 if (source != prev_source) break :pshufd; 38257 } else sources[(elem_index & 0b010) >> 1] = source; 38258 38259 const select_bit: u3 = @intCast((elem_index & 0b011) << 1); 38260 const select_mask = @as(u8, @intCast(mask_elem_index & 0b011)) << select_bit; 38261 if (elem_index & 0b100 == 0) 38262 control |= select_mask 38263 else if (control & @as(u8, 0b11) << select_bit != select_mask) break :pshufd; 38264 } 38265 38266 const operands = [2]Air.Inst.Ref{ extra.a, extra.b }; 38267 const operand_tys = [2]Type{ lhs_ty, rhs_ty }; 38268 const src_mcv = try self.resolveInst(operands[sources[0] orelse break :pshufd]); 38269 38270 const dst_reg = if (src_mcv.isRegister() and 38271 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, src_mcv)) 38272 src_mcv.getReg().? 38273 else 38274 try self.register_manager.allocReg(inst, abi.RegisterClass.sse); 38275 const dst_alias = registerAlias(dst_reg, max_abi_size); 38276 38277 if (src_mcv.isBase()) try self.asmRegisterMemoryImmediate( 38278 .{ if (has_avx) .vp_d else .p_d, .shuf }, 38279 dst_alias, 38280 try src_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38281 .u(control), 38282 ) else try self.asmRegisterRegisterImmediate( 38283 .{ if (has_avx) .vp_d else .p_d, .shuf }, 38284 dst_alias, 38285 registerAlias(if (src_mcv.isRegister()) 38286 src_mcv.getReg().? 38287 else 38288 try self.copyToTmpRegister(operand_tys[sources[0].?], src_mcv), max_abi_size), 38289 .u(control), 38290 ); 38291 break :result .{ .register = dst_reg }; 38292 } 38293 38294 shufps: { 38295 if (elem_abi_size != 4) break :shufps; 38296 if (max_abi_size > self.vectorSize(.float)) break :shufps; 38297 38298 var control: u8 = 0b00_00_00_00; 38299 var sources: [2]?u1 = @splat(null); 38300 for (mask_elems, 0..) |maybe_mask_elem, elem_index| { 38301 const mask_elem = maybe_mask_elem orelse continue; 38302 const mask_elem_index: u3 = @intCast(if (mask_elem < 0) ~mask_elem else mask_elem); 38303 if (mask_elem_index & 0b100 != elem_index & 0b100) break :shufps; 38304 38305 const source = @intFromBool(mask_elem < 0); 38306 if (sources[(elem_index & 0b010) >> 1]) |prev_source| { 38307 if (source != prev_source) break :shufps; 38308 } else sources[(elem_index & 0b010) >> 1] = source; 38309 38310 const select_bit: u3 = @intCast((elem_index & 0b011) << 1); 38311 const select_mask = @as(u8, @intCast(mask_elem_index & 0b011)) << select_bit; 38312 if (elem_index & 0b100 == 0) 38313 control |= select_mask 38314 else if (control & @as(u8, 0b11) << select_bit != select_mask) break :shufps; 38315 } 38316 if (sources[0] orelse break :shufps == sources[1] orelse break :shufps) break :shufps; 38317 38318 const operands = [2]Air.Inst.Ref{ extra.a, extra.b }; 38319 const operand_tys = [2]Type{ lhs_ty, rhs_ty }; 38320 const lhs_mcv = try self.resolveInst(operands[sources[0].?]); 38321 const rhs_mcv = try self.resolveInst(operands[sources[1].?]); 38322 38323 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38324 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, lhs_mcv)) 38325 lhs_mcv 38326 else if (has_avx and lhs_mcv.isRegister()) 38327 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38328 else 38329 try self.copyToRegisterWithInstTracking(inst, operand_tys[sources[0].?], lhs_mcv); 38330 const dst_reg = dst_mcv.getReg().?; 38331 const dst_alias = registerAlias(dst_reg, max_abi_size); 38332 38333 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 38334 .{ .v_ps, .shuf }, 38335 dst_alias, 38336 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38337 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38338 .u(control), 38339 ) else try self.asmRegisterRegisterRegisterImmediate( 38340 .{ .v_ps, .shuf }, 38341 dst_alias, 38342 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38343 registerAlias(if (rhs_mcv.isRegister()) 38344 rhs_mcv.getReg().? 38345 else 38346 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38347 .u(control), 38348 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemoryImmediate( 38349 .{ ._ps, .shuf }, 38350 dst_alias, 38351 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38352 .u(control), 38353 ) else try self.asmRegisterRegisterImmediate( 38354 .{ ._ps, .shuf }, 38355 dst_alias, 38356 registerAlias(if (rhs_mcv.isRegister()) 38357 rhs_mcv.getReg().? 38358 else 38359 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38360 .u(control), 38361 ); 38362 break :result dst_mcv; 38363 } 38364 38365 shufpd: { 38366 if (elem_abi_size != 8) break :shufpd; 38367 if (max_abi_size > self.vectorSize(.float)) break :shufpd; 38368 38369 var control: u4 = 0b0_0_0_0; 38370 var sources: [2]?u1 = @splat(null); 38371 for (mask_elems, 0..) |maybe_mask_elem, elem_index| { 38372 const mask_elem = maybe_mask_elem orelse continue; 38373 const mask_elem_index: u2 = @intCast(if (mask_elem < 0) ~mask_elem else mask_elem); 38374 if (mask_elem_index & 0b10 != elem_index & 0b10) break :shufpd; 38375 38376 const source = @intFromBool(mask_elem < 0); 38377 if (sources[elem_index & 0b01]) |prev_source| { 38378 if (source != prev_source) break :shufpd; 38379 } else sources[elem_index & 0b01] = source; 38380 38381 control |= @as(u4, @intCast(mask_elem_index & 0b01)) << @intCast(elem_index); 38382 } 38383 if (sources[0] orelse break :shufpd == sources[1] orelse break :shufpd) break :shufpd; 38384 38385 const operands: [2]Air.Inst.Ref = .{ extra.a, extra.b }; 38386 const operand_tys: [2]Type = .{ lhs_ty, rhs_ty }; 38387 const lhs_mcv = try self.resolveInst(operands[sources[0].?]); 38388 const rhs_mcv = try self.resolveInst(operands[sources[1].?]); 38389 38390 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38391 self.reuseOperand(inst, operands[sources[0].?], sources[0].?, lhs_mcv)) 38392 lhs_mcv 38393 else if (has_avx and lhs_mcv.isRegister()) 38394 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38395 else 38396 try self.copyToRegisterWithInstTracking(inst, operand_tys[sources[0].?], lhs_mcv); 38397 const dst_reg = dst_mcv.getReg().?; 38398 const dst_alias = registerAlias(dst_reg, max_abi_size); 38399 38400 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 38401 .{ .v_pd, .shuf }, 38402 dst_alias, 38403 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38404 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38405 .u(control), 38406 ) else try self.asmRegisterRegisterRegisterImmediate( 38407 .{ .v_pd, .shuf }, 38408 dst_alias, 38409 registerAlias(lhs_mcv.getReg() orelse dst_reg, max_abi_size), 38410 registerAlias(if (rhs_mcv.isRegister()) 38411 rhs_mcv.getReg().? 38412 else 38413 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38414 .u(control), 38415 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemoryImmediate( 38416 .{ ._pd, .shuf }, 38417 dst_alias, 38418 try rhs_mcv.mem(self, .{ .size = .fromSize(max_abi_size) }), 38419 .u(control), 38420 ) else try self.asmRegisterRegisterImmediate( 38421 .{ ._pd, .shuf }, 38422 dst_alias, 38423 registerAlias(if (rhs_mcv.isRegister()) 38424 rhs_mcv.getReg().? 38425 else 38426 try self.copyToTmpRegister(operand_tys[sources[1].?], rhs_mcv), max_abi_size), 38427 .u(control), 38428 ); 38429 break :result dst_mcv; 38430 } 38431 38432 blend: { 38433 if (elem_abi_size < 2) break :blend; 38434 if (dst_abi_size > self.vectorSize(.float)) break :blend; 38435 if (!self.hasFeature(.sse4_1)) break :blend; 38436 38437 var control: u8 = 0b0_0_0_0_0_0_0_0; 38438 for (mask_elems, 0..) |maybe_mask_elem, elem_index| { 38439 const mask_elem = maybe_mask_elem orelse continue; 38440 const mask_elem_index = 38441 std.math.cast(u4, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :blend; 38442 if (mask_elem_index != elem_index) break :blend; 38443 38444 const select_mask = @as(u8, @intFromBool(mask_elem < 0)) << @truncate(elem_index); 38445 if (elem_index & 0b1000 == 0) 38446 control |= select_mask 38447 else if (control & @as(u8, 0b1) << @truncate(elem_index) != select_mask) break :blend; 38448 } 38449 38450 if (!elem_ty.isRuntimeFloat() and self.hasFeature(.avx2)) vpblendd: { 38451 const expanded_control = switch (elem_abi_size) { 38452 4 => control, 38453 8 => @as(u8, if (control & 0b0001 != 0) 0b00_00_00_11 else 0b00_00_00_00) | 38454 @as(u8, if (control & 0b0010 != 0) 0b00_00_11_00 else 0b00_00_00_00) | 38455 @as(u8, if (control & 0b0100 != 0) 0b00_11_00_00 else 0b00_00_00_00) | 38456 @as(u8, if (control & 0b1000 != 0) 0b11_00_00_00 else 0b00_00_00_00), 38457 else => break :vpblendd, 38458 }; 38459 38460 const lhs_mcv = try self.resolveInst(extra.a); 38461 const lhs_reg = if (lhs_mcv.isRegister()) 38462 lhs_mcv.getReg().? 38463 else 38464 try self.copyToTmpRegister(dst_ty, lhs_mcv); 38465 const lhs_lock = self.register_manager.lockReg(lhs_reg); 38466 defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); 38467 38468 const rhs_mcv = try self.resolveInst(extra.b); 38469 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.sse); 38470 if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 38471 .{ .vp_d, .blend }, 38472 registerAlias(dst_reg, dst_abi_size), 38473 registerAlias(lhs_reg, dst_abi_size), 38474 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38475 .u(expanded_control), 38476 ) else try self.asmRegisterRegisterRegisterImmediate( 38477 .{ .vp_d, .blend }, 38478 registerAlias(dst_reg, dst_abi_size), 38479 registerAlias(lhs_reg, dst_abi_size), 38480 registerAlias(if (rhs_mcv.isRegister()) 38481 rhs_mcv.getReg().? 38482 else 38483 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38484 .u(expanded_control), 38485 ); 38486 break :result .{ .register = dst_reg }; 38487 } 38488 38489 if (!elem_ty.isRuntimeFloat() or elem_abi_size == 2) pblendw: { 38490 const expanded_control = switch (elem_abi_size) { 38491 2 => control, 38492 4 => if (dst_abi_size <= 16 or 38493 @as(u4, @intCast(control >> 4)) == @as(u4, @truncate(control >> 0))) 38494 @as(u8, if (control & 0b0001 != 0) 0b00_00_00_11 else 0b00_00_00_00) | 38495 @as(u8, if (control & 0b0010 != 0) 0b00_00_11_00 else 0b00_00_00_00) | 38496 @as(u8, if (control & 0b0100 != 0) 0b00_11_00_00 else 0b00_00_00_00) | 38497 @as(u8, if (control & 0b1000 != 0) 0b11_00_00_00 else 0b00_00_00_00) 38498 else 38499 break :pblendw, 38500 8 => if (dst_abi_size <= 16 or 38501 @as(u2, @intCast(control >> 2)) == @as(u2, @truncate(control >> 0))) 38502 @as(u8, if (control & 0b01 != 0) 0b0000_1111 else 0b0000_0000) | 38503 @as(u8, if (control & 0b10 != 0) 0b1111_0000 else 0b0000_0000) 38504 else 38505 break :pblendw, 38506 16 => break :pblendw, 38507 else => unreachable, 38508 }; 38509 38510 const lhs_mcv = try self.resolveInst(extra.a); 38511 const rhs_mcv = try self.resolveInst(extra.b); 38512 38513 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38514 self.reuseOperand(inst, extra.a, 0, lhs_mcv)) 38515 lhs_mcv 38516 else if (has_avx and lhs_mcv.isRegister()) 38517 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38518 else 38519 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv); 38520 const dst_reg = dst_mcv.getReg().?; 38521 38522 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 38523 .{ .vp_w, .blend }, 38524 registerAlias(dst_reg, dst_abi_size), 38525 registerAlias(if (lhs_mcv.isRegister()) 38526 lhs_mcv.getReg().? 38527 else 38528 dst_reg, dst_abi_size), 38529 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38530 .u(expanded_control), 38531 ) else try self.asmRegisterRegisterRegisterImmediate( 38532 .{ .vp_w, .blend }, 38533 registerAlias(dst_reg, dst_abi_size), 38534 registerAlias(if (lhs_mcv.isRegister()) 38535 lhs_mcv.getReg().? 38536 else 38537 dst_reg, dst_abi_size), 38538 registerAlias(if (rhs_mcv.isRegister()) 38539 rhs_mcv.getReg().? 38540 else 38541 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38542 .u(expanded_control), 38543 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemoryImmediate( 38544 .{ .p_w, .blend }, 38545 registerAlias(dst_reg, dst_abi_size), 38546 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38547 .u(expanded_control), 38548 ) else try self.asmRegisterRegisterImmediate( 38549 .{ .p_w, .blend }, 38550 registerAlias(dst_reg, dst_abi_size), 38551 registerAlias(if (rhs_mcv.isRegister()) 38552 rhs_mcv.getReg().? 38553 else 38554 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38555 .u(expanded_control), 38556 ); 38557 break :result .{ .register = dst_reg }; 38558 } 38559 38560 const expanded_control = switch (elem_abi_size) { 38561 4, 8 => control, 38562 16 => @as(u4, if (control & 0b01 != 0) 0b00_11 else 0b00_00) | 38563 @as(u4, if (control & 0b10 != 0) 0b11_00 else 0b00_00), 38564 else => unreachable, 38565 }; 38566 38567 const lhs_mcv = try self.resolveInst(extra.a); 38568 const rhs_mcv = try self.resolveInst(extra.b); 38569 38570 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38571 self.reuseOperand(inst, extra.a, 0, lhs_mcv)) 38572 lhs_mcv 38573 else if (has_avx and lhs_mcv.isRegister()) 38574 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38575 else 38576 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv); 38577 const dst_reg = dst_mcv.getReg().?; 38578 38579 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryImmediate( 38580 switch (elem_abi_size) { 38581 4 => .{ .v_ps, .blend }, 38582 8, 16 => .{ .v_pd, .blend }, 38583 else => unreachable, 38584 }, 38585 registerAlias(dst_reg, dst_abi_size), 38586 registerAlias(if (lhs_mcv.isRegister()) 38587 lhs_mcv.getReg().? 38588 else 38589 dst_reg, dst_abi_size), 38590 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38591 .u(expanded_control), 38592 ) else try self.asmRegisterRegisterRegisterImmediate( 38593 switch (elem_abi_size) { 38594 4 => .{ .v_ps, .blend }, 38595 8, 16 => .{ .v_pd, .blend }, 38596 else => unreachable, 38597 }, 38598 registerAlias(dst_reg, dst_abi_size), 38599 registerAlias(if (lhs_mcv.isRegister()) 38600 lhs_mcv.getReg().? 38601 else 38602 dst_reg, dst_abi_size), 38603 registerAlias(if (rhs_mcv.isRegister()) 38604 rhs_mcv.getReg().? 38605 else 38606 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38607 .u(expanded_control), 38608 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemoryImmediate( 38609 switch (elem_abi_size) { 38610 4 => .{ ._ps, .blend }, 38611 8, 16 => .{ ._pd, .blend }, 38612 else => unreachable, 38613 }, 38614 registerAlias(dst_reg, dst_abi_size), 38615 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38616 .u(expanded_control), 38617 ) else try self.asmRegisterRegisterImmediate( 38618 switch (elem_abi_size) { 38619 4 => .{ ._ps, .blend }, 38620 8, 16 => .{ ._pd, .blend }, 38621 else => unreachable, 38622 }, 38623 registerAlias(dst_reg, dst_abi_size), 38624 registerAlias(if (rhs_mcv.isRegister()) 38625 rhs_mcv.getReg().? 38626 else 38627 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38628 .u(expanded_control), 38629 ); 38630 break :result .{ .register = dst_reg }; 38631 } 38632 38633 blendv: { 38634 if (dst_abi_size > self.vectorSize(if (elem_abi_size >= 4) .float else .int)) break :blendv; 38635 38636 const select_mask_elem_ty = try pt.intType(.unsigned, elem_abi_size * 8); 38637 const select_mask_ty = try pt.vectorType(.{ 38638 .len = @intCast(mask_elems.len), 38639 .child = select_mask_elem_ty.toIntern(), 38640 }); 38641 var select_mask_elems: [32]InternPool.Index = undefined; 38642 for ( 38643 select_mask_elems[0..mask_elems.len], 38644 mask_elems, 38645 0.., 38646 ) |*select_mask_elem, maybe_mask_elem, elem_index| { 38647 const mask_elem = maybe_mask_elem orelse continue; 38648 const mask_elem_index = 38649 std.math.cast(u5, if (mask_elem < 0) ~mask_elem else mask_elem) orelse break :blendv; 38650 if (mask_elem_index != elem_index) break :blendv; 38651 38652 select_mask_elem.* = (if (mask_elem < 0) 38653 try select_mask_elem_ty.maxIntScalar(pt, select_mask_elem_ty) 38654 else 38655 try select_mask_elem_ty.minIntScalar(pt, select_mask_elem_ty)).toIntern(); 38656 } 38657 const select_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 38658 .ty = select_mask_ty.toIntern(), 38659 .storage = .{ .elems = select_mask_elems[0..mask_elems.len] }, 38660 } }))); 38661 38662 if (self.hasFeature(.sse4_1)) { 38663 const mir_tag: Mir.Inst.FixedTag = .{ 38664 if ((elem_abi_size >= 4 and elem_ty.isRuntimeFloat()) or 38665 (dst_abi_size > 16 and !self.hasFeature(.avx2))) switch (elem_abi_size) { 38666 4 => if (has_avx) .v_ps else ._ps, 38667 8 => if (has_avx) .v_pd else ._pd, 38668 else => unreachable, 38669 } else if (has_avx) .vp_b else .p_b, 38670 .blendv, 38671 }; 38672 38673 const select_mask_reg = if (!has_avx) reg: { 38674 try self.register_manager.getKnownReg(.xmm0, null); 38675 try self.genSetReg(.xmm0, select_mask_elem_ty, select_mask_mcv, .{}); 38676 break :reg .xmm0; 38677 } else try self.copyToTmpRegister(select_mask_ty, select_mask_mcv); 38678 const select_mask_alias = registerAlias(select_mask_reg, dst_abi_size); 38679 const select_mask_lock = self.register_manager.lockRegAssumeUnused(select_mask_reg); 38680 defer self.register_manager.unlockReg(select_mask_lock); 38681 38682 const lhs_mcv = try self.resolveInst(extra.a); 38683 const rhs_mcv = try self.resolveInst(extra.b); 38684 38685 const dst_mcv: MCValue = if (lhs_mcv.isRegister() and 38686 self.reuseOperand(inst, extra.a, 0, lhs_mcv)) 38687 lhs_mcv 38688 else if (has_avx and lhs_mcv.isRegister()) 38689 .{ .register = try self.register_manager.allocReg(inst, abi.RegisterClass.sse) } 38690 else 38691 try self.copyToRegisterWithInstTracking(inst, dst_ty, lhs_mcv); 38692 const dst_reg = dst_mcv.getReg().?; 38693 const dst_alias = registerAlias(dst_reg, dst_abi_size); 38694 38695 if (has_avx) if (rhs_mcv.isBase()) try self.asmRegisterRegisterMemoryRegister( 38696 mir_tag, 38697 dst_alias, 38698 if (lhs_mcv.isRegister()) 38699 registerAlias(lhs_mcv.getReg().?, dst_abi_size) 38700 else 38701 dst_alias, 38702 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38703 select_mask_alias, 38704 ) else try self.asmRegisterRegisterRegisterRegister( 38705 mir_tag, 38706 dst_alias, 38707 if (lhs_mcv.isRegister()) 38708 registerAlias(lhs_mcv.getReg().?, dst_abi_size) 38709 else 38710 dst_alias, 38711 registerAlias(if (rhs_mcv.isRegister()) 38712 rhs_mcv.getReg().? 38713 else 38714 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38715 select_mask_alias, 38716 ) else if (rhs_mcv.isBase()) try self.asmRegisterMemoryRegister( 38717 mir_tag, 38718 dst_alias, 38719 try rhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38720 select_mask_alias, 38721 ) else try self.asmRegisterRegisterRegister( 38722 mir_tag, 38723 dst_alias, 38724 registerAlias(if (rhs_mcv.isRegister()) 38725 rhs_mcv.getReg().? 38726 else 38727 try self.copyToTmpRegister(dst_ty, rhs_mcv), dst_abi_size), 38728 select_mask_alias, 38729 ); 38730 break :result dst_mcv; 38731 } 38732 38733 const lhs_mcv = try self.resolveInst(extra.a); 38734 const rhs_mcv = try self.resolveInst(extra.b); 38735 38736 const dst_mcv: MCValue = if (rhs_mcv.isRegister() and 38737 self.reuseOperand(inst, extra.b, 1, rhs_mcv)) 38738 rhs_mcv 38739 else 38740 try self.copyToRegisterWithInstTracking(inst, dst_ty, rhs_mcv); 38741 const dst_reg = dst_mcv.getReg().?; 38742 const dst_alias = registerAlias(dst_reg, dst_abi_size); 38743 38744 const mask_reg = try self.copyToTmpRegister(select_mask_ty, select_mask_mcv); 38745 const mask_alias = registerAlias(mask_reg, dst_abi_size); 38746 const mask_lock = self.register_manager.lockRegAssumeUnused(mask_reg); 38747 defer self.register_manager.unlockReg(mask_lock); 38748 38749 const mir_fixes: Mir.Inst.Fixes = if (elem_ty.isRuntimeFloat()) 38750 switch (elem_ty.floatBits(self.target.*)) { 38751 16, 80, 128 => .p_, 38752 32 => ._ps, 38753 64 => ._pd, 38754 else => unreachable, 38755 } 38756 else 38757 .p_; 38758 try self.asmRegisterRegister(.{ mir_fixes, .@"and" }, dst_alias, mask_alias); 38759 if (lhs_mcv.isBase()) try self.asmRegisterMemory( 38760 .{ mir_fixes, .andn }, 38761 mask_alias, 38762 try lhs_mcv.mem(self, .{ .size = .fromSize(dst_abi_size) }), 38763 ) else try self.asmRegisterRegister( 38764 .{ mir_fixes, .andn }, 38765 mask_alias, 38766 if (lhs_mcv.isRegister()) 38767 lhs_mcv.getReg().? 38768 else 38769 try self.copyToTmpRegister(dst_ty, lhs_mcv), 38770 ); 38771 try self.asmRegisterRegister(.{ mir_fixes, .@"or" }, dst_alias, mask_alias); 38772 break :result dst_mcv; 38773 } 38774 38775 pshufb: { 38776 if (max_abi_size > 16) break :pshufb; 38777 if (!self.hasFeature(.ssse3)) break :pshufb; 38778 38779 const temp_regs = 38780 try self.register_manager.allocRegs(2, .{ inst, null }, abi.RegisterClass.sse); 38781 const temp_locks = self.register_manager.lockRegsAssumeUnused(2, temp_regs); 38782 defer for (temp_locks) |lock| self.register_manager.unlockReg(lock); 38783 38784 const lhs_temp_alias = registerAlias(temp_regs[0], max_abi_size); 38785 try self.genSetReg(temp_regs[0], lhs_ty, .{ .air_ref = extra.a }, .{}); 38786 38787 const rhs_temp_alias = registerAlias(temp_regs[1], max_abi_size); 38788 try self.genSetReg(temp_regs[1], rhs_ty, .{ .air_ref = extra.b }, .{}); 38789 38790 var lhs_mask_elems: [16]InternPool.Index = undefined; 38791 for (lhs_mask_elems[0..max_abi_size], 0..) |*lhs_mask_elem, byte_index| { 38792 const elem_index = byte_index / elem_abi_size; 38793 lhs_mask_elem.* = try pt.intern(.{ .int = .{ 38794 .ty = .u8_type, 38795 .storage = .{ .u64 = if (elem_index >= mask_elems.len) 0b1_00_00000 else elem: { 38796 const mask_elem = mask_elems[elem_index] orelse break :elem 0b1_00_00000; 38797 if (mask_elem < 0) break :elem 0b1_00_00000; 38798 const mask_elem_index: u31 = @intCast(mask_elem); 38799 const byte_off: u32 = @intCast(byte_index % elem_abi_size); 38800 break :elem @intCast(mask_elem_index * elem_abi_size + byte_off); 38801 } }, 38802 } }); 38803 } 38804 const lhs_mask_ty = try pt.vectorType(.{ .len = max_abi_size, .child = .u8_type }); 38805 const lhs_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 38806 .ty = lhs_mask_ty.toIntern(), 38807 .storage = .{ .elems = lhs_mask_elems[0..max_abi_size] }, 38808 } }))); 38809 const lhs_mask_mem: Memory = .{ 38810 .base = .{ .reg = try self.copyToTmpRegister(.usize, lhs_mask_mcv.address()) }, 38811 .mod = .{ .rm = .{ .size = .fromSize(@max(max_abi_size, 16)) } }, 38812 }; 38813 if (has_avx) try self.asmRegisterRegisterMemory( 38814 .{ .vp_b, .shuf }, 38815 lhs_temp_alias, 38816 lhs_temp_alias, 38817 lhs_mask_mem, 38818 ) else try self.asmRegisterMemory( 38819 .{ .p_b, .shuf }, 38820 lhs_temp_alias, 38821 lhs_mask_mem, 38822 ); 38823 38824 var rhs_mask_elems: [16]InternPool.Index = undefined; 38825 for (rhs_mask_elems[0..max_abi_size], 0..) |*rhs_mask_elem, byte_index| { 38826 const elem_index = byte_index / elem_abi_size; 38827 rhs_mask_elem.* = try pt.intern(.{ .int = .{ 38828 .ty = .u8_type, 38829 .storage = .{ .u64 = if (elem_index >= mask_elems.len) 0b1_00_00000 else elem: { 38830 const mask_elem = mask_elems[elem_index] orelse break :elem 0b1_00_00000; 38831 if (mask_elem >= 0) break :elem 0b1_00_00000; 38832 const mask_elem_index: u31 = @intCast(~mask_elem); 38833 const byte_off: u32 = @intCast(byte_index % elem_abi_size); 38834 break :elem @intCast(mask_elem_index * elem_abi_size + byte_off); 38835 } }, 38836 } }); 38837 } 38838 const rhs_mask_ty = try pt.vectorType(.{ .len = max_abi_size, .child = .u8_type }); 38839 const rhs_mask_mcv = try self.genTypedValue(.fromInterned(try pt.intern(.{ .aggregate = .{ 38840 .ty = rhs_mask_ty.toIntern(), 38841 .storage = .{ .elems = rhs_mask_elems[0..max_abi_size] }, 38842 } }))); 38843 const rhs_mask_mem: Memory = .{ 38844 .base = .{ .reg = try self.copyToTmpRegister(.usize, rhs_mask_mcv.address()) }, 38845 .mod = .{ .rm = .{ .size = .fromSize(@max(max_abi_size, 16)) } }, 38846 }; 38847 if (has_avx) try self.asmRegisterRegisterMemory( 38848 .{ .vp_b, .shuf }, 38849 rhs_temp_alias, 38850 rhs_temp_alias, 38851 rhs_mask_mem, 38852 ) else try self.asmRegisterMemory( 38853 .{ .p_b, .shuf }, 38854 rhs_temp_alias, 38855 rhs_mask_mem, 38856 ); 38857 38858 if (has_avx) try self.asmRegisterRegisterRegister( 38859 .{ switch (elem_ty.zigTypeTag(zcu)) { 38860 else => break :result null, 38861 .int => .vp_, 38862 .float => switch (elem_ty.floatBits(self.target.*)) { 38863 32 => .v_ps, 38864 64 => .v_pd, 38865 16, 80, 128 => break :result null, 38866 else => unreachable, 38867 }, 38868 }, .@"or" }, 38869 lhs_temp_alias, 38870 lhs_temp_alias, 38871 rhs_temp_alias, 38872 ) else try self.asmRegisterRegister( 38873 .{ switch (elem_ty.zigTypeTag(zcu)) { 38874 else => break :result null, 38875 .int => .p_, 38876 .float => switch (elem_ty.floatBits(self.target.*)) { 38877 32 => ._ps, 38878 64 => ._pd, 38879 16, 80, 128 => break :result null, 38880 else => unreachable, 38881 }, 38882 }, .@"or" }, 38883 lhs_temp_alias, 38884 rhs_temp_alias, 38885 ); 38886 break :result .{ .register = temp_regs[0] }; 38887 } 38888 38889 break :result null; 38890 }) orelse return self.fail("TODO implement airShuffle from {} and {} to {} with {}", .{ 38891 lhs_ty.fmt(pt), 38892 rhs_ty.fmt(pt), 38893 dst_ty.fmt(pt), 38894 Value.fromInterned(extra.mask).fmtValue(pt), 38895 }); 38896 return self.finishAir(inst, result, .{ extra.a, extra.b, .none }); 38897 } 38898 38899 fn airReduce(self: *CodeGen, inst: Air.Inst.Index) !void { 38900 const pt = self.pt; 38901 const zcu = pt.zcu; 38902 const reduce = self.air.instructions.items(.data)[@intFromEnum(inst)].reduce; 38903 38904 const result: MCValue = result: { 38905 const operand_ty = self.typeOf(reduce.operand); 38906 if (operand_ty.isVector(zcu) and operand_ty.childType(zcu).toIntern() == .bool_type) { 38907 try self.spillEflagsIfOccupied(); 38908 38909 const abi_size: u32 = @intCast(operand_ty.abiSize(zcu)); 38910 const operand_mcv = try self.resolveInst(reduce.operand); 38911 const mask_len = operand_ty.vectorLen(zcu); 38912 const mask_len_minus_one = (std.math.cast(u6, mask_len - 1) orelse { 38913 const acc_reg = try self.register_manager.allocReg(null, abi.RegisterClass.gp); 38914 const acc_lock = self.register_manager.lockRegAssumeUnused(acc_reg); 38915 defer self.register_manager.unlockReg(acc_lock); 38916 var limb_offset: i31 = 0; 38917 while (limb_offset < abi_size) : (limb_offset += 8) { 38918 try self.asmRegisterMemory( 38919 .{ ._, if (limb_offset == 0) .mov else switch (reduce.operation) { 38920 .Or => .@"or", 38921 .And => .@"and", 38922 else => return self.fail("TODO implement airReduce for {}", .{operand_ty.fmt(pt)}), 38923 } }, 38924 acc_reg.to64(), 38925 try operand_mcv.mem(self, .{ 38926 .size = .qword, 38927 .disp = limb_offset, 38928 }), 38929 ); 38930 } 38931 switch (reduce.operation) { 38932 .Or => { 38933 try self.asmRegisterRegister(.{ ._, .@"test" }, acc_reg.to64(), acc_reg.to64()); 38934 break :result .{ .eflags = .nz }; 38935 }, 38936 .And => { 38937 try self.asmRegisterImmediate(.{ ._, .cmp }, acc_reg.to64(), .s(-1)); 38938 break :result .{ .eflags = .z }; 38939 }, 38940 else => unreachable, 38941 } 38942 }); 38943 const mask = @as(u64, std.math.maxInt(u64)) >> ~mask_len_minus_one; 38944 switch (reduce.operation) { 38945 .Or => { 38946 if (operand_mcv.isBase()) try self.asmMemoryImmediate( 38947 .{ ._, .@"test" }, 38948 try operand_mcv.mem(self, .{ .size = .fromSize(abi_size) }), 38949 if (mask_len < abi_size * 8) 38950 .u(mask) 38951 else 38952 .s(-1), 38953 ) else { 38954 const operand_reg = registerAlias(operand_reg: { 38955 if (operand_mcv.isRegister()) { 38956 const operand_reg = operand_mcv.getReg().?; 38957 if (operand_reg.class() == .general_purpose) break :operand_reg operand_reg; 38958 } 38959 break :operand_reg try self.copyToTmpRegister(operand_ty, operand_mcv); 38960 }, abi_size); 38961 const operand_lock = self.register_manager.lockReg(operand_reg); 38962 defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); 38963 38964 if (mask_len < abi_size * 8) try self.asmRegisterImmediate( 38965 .{ ._, .@"test" }, 38966 operand_reg, 38967 .u(mask), 38968 ) else try self.asmRegisterRegister( 38969 .{ ._, .@"test" }, 38970 operand_reg, 38971 operand_reg, 38972 ); 38973 } 38974 break :result .{ .eflags = .nz }; 38975 }, 38976 .And => { 38977 const tmp_reg = registerAlias( 38978 try self.copyToTmpRegister(operand_ty, operand_mcv), 38979 abi_size, 38980 ); 38981 const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); 38982 defer self.register_manager.unlockReg(tmp_lock); 38983 38984 try self.asmRegister(.{ ._, .not }, tmp_reg); 38985 if (mask_len < abi_size * 8) 38986 try self.asmRegisterImmediate(.{ ._, .@"test" }, tmp_reg, .u(mask)) 38987 else 38988 try self.asmRegisterRegister(.{ ._, .@"test" }, tmp_reg, tmp_reg); 38989 break :result .{ .eflags = .z }; 38990 }, 38991 else => return self.fail("TODO implement airReduce for {}", .{operand_ty.fmt(pt)}), 38992 } 38993 } 38994 return self.fail("TODO implement airReduce for {}", .{operand_ty.fmt(pt)}); 38995 }; 38996 return self.finishAir(inst, result, .{ reduce.operand, .none, .none }); 38997 } 38998 38999 fn airAggregateInit(self: *CodeGen, inst: Air.Inst.Index) !void { 39000 const pt = self.pt; 39001 const zcu = pt.zcu; 39002 const result_ty = self.typeOfIndex(inst); 39003 const len: usize = @intCast(result_ty.arrayLen(zcu)); 39004 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 39005 const elements: []const Air.Inst.Ref = @ptrCast(self.air.extra[ty_pl.payload..][0..len]); 39006 const result: MCValue = result: { 39007 switch (result_ty.zigTypeTag(zcu)) { 39008 .@"struct" => { 39009 const frame_index = try self.allocFrameIndex(.initSpill(result_ty, zcu)); 39010 if (result_ty.containerLayout(zcu) == .@"packed") { 39011 const loaded_struct = zcu.intern_pool.loadStructType(result_ty.toIntern()); 39012 try self.genInlineMemset( 39013 .{ .lea_frame = .{ .index = frame_index } }, 39014 .{ .immediate = 0 }, 39015 .{ .immediate = result_ty.abiSize(zcu) }, 39016 .{}, 39017 ); 39018 for (elements, 0..) |elem, elem_i_usize| { 39019 const elem_i: u32 = @intCast(elem_i_usize); 39020 if ((try result_ty.structFieldValueComptime(pt, elem_i)) != null) continue; 39021 39022 const elem_ty = result_ty.fieldType(elem_i, zcu); 39023 const elem_bit_size: u32 = @intCast(elem_ty.bitSize(zcu)); 39024 if (elem_bit_size > 64) { 39025 return self.fail( 39026 "TODO airAggregateInit implement packed structs with large fields", 39027 .{}, 39028 ); 39029 } 39030 const elem_abi_size: u32 = @intCast(elem_ty.abiSize(zcu)); 39031 const elem_abi_bits = elem_abi_size * 8; 39032 const elem_off = pt.structPackedFieldBitOffset(loaded_struct, elem_i); 39033 const elem_byte_off: i32 = @intCast(elem_off / elem_abi_bits * elem_abi_size); 39034 const elem_bit_off = elem_off % elem_abi_bits; 39035 const elem_mcv = try self.resolveInst(elem); 39036 const mat_elem_mcv = switch (elem_mcv) { 39037 .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, 39038 else => elem_mcv, 39039 }; 39040 const elem_lock = switch (mat_elem_mcv) { 39041 .register => |reg| self.register_manager.lockReg(reg), 39042 .immediate => |imm| lock: { 39043 if (imm == 0) continue; 39044 break :lock null; 39045 }, 39046 else => null, 39047 }; 39048 defer if (elem_lock) |lock| self.register_manager.unlockReg(lock); 39049 39050 const elem_extra_bits = self.regExtraBits(elem_ty); 39051 { 39052 const temp_reg = try self.copyToTmpRegister(elem_ty, mat_elem_mcv); 39053 const temp_alias = registerAlias(temp_reg, elem_abi_size); 39054 const temp_lock = self.register_manager.lockRegAssumeUnused(temp_reg); 39055 defer self.register_manager.unlockReg(temp_lock); 39056 39057 if (elem_bit_off < elem_extra_bits) { 39058 try self.truncateRegister(elem_ty, temp_alias); 39059 } 39060 if (elem_bit_off > 0) try self.genShiftBinOpMir( 39061 .{ ._l, .sh }, 39062 elem_ty, 39063 .{ .register = temp_alias }, 39064 .u8, 39065 .{ .immediate = elem_bit_off }, 39066 ); 39067 try self.genBinOpMir( 39068 .{ ._, .@"or" }, 39069 elem_ty, 39070 .{ .load_frame = .{ .index = frame_index, .off = elem_byte_off } }, 39071 .{ .register = temp_alias }, 39072 ); 39073 } 39074 if (elem_bit_off > elem_extra_bits) { 39075 const temp_reg = try self.copyToTmpRegister(elem_ty, mat_elem_mcv); 39076 const temp_alias = registerAlias(temp_reg, elem_abi_size); 39077 const temp_lock = self.register_manager.lockRegAssumeUnused(temp_reg); 39078 defer self.register_manager.unlockReg(temp_lock); 39079 39080 if (elem_extra_bits > 0) { 39081 try self.truncateRegister(elem_ty, temp_alias); 39082 } 39083 try self.genShiftBinOpMir( 39084 .{ ._r, .sh }, 39085 elem_ty, 39086 .{ .register = temp_reg }, 39087 .u8, 39088 .{ .immediate = elem_abi_bits - elem_bit_off }, 39089 ); 39090 try self.genBinOpMir( 39091 .{ ._, .@"or" }, 39092 elem_ty, 39093 .{ .load_frame = .{ 39094 .index = frame_index, 39095 .off = elem_byte_off + @as(i32, @intCast(elem_abi_size)), 39096 } }, 39097 .{ .register = temp_alias }, 39098 ); 39099 } 39100 } 39101 } else for (elements, 0..) |elem, elem_i| { 39102 if ((try result_ty.structFieldValueComptime(pt, elem_i)) != null) continue; 39103 39104 const elem_ty = result_ty.fieldType(elem_i, zcu); 39105 const elem_off: i32 = @intCast(result_ty.structFieldOffset(elem_i, zcu)); 39106 const elem_mcv = try self.resolveInst(elem); 39107 const mat_elem_mcv = switch (elem_mcv) { 39108 .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, 39109 else => elem_mcv, 39110 }; 39111 try self.genSetMem(.{ .frame = frame_index }, elem_off, elem_ty, mat_elem_mcv, .{}); 39112 } 39113 break :result .{ .load_frame = .{ .index = frame_index } }; 39114 }, 39115 .array, .vector => { 39116 const elem_ty = result_ty.childType(zcu); 39117 if (result_ty.isVector(zcu) and elem_ty.toIntern() == .bool_type) { 39118 const result_size: u32 = @intCast(result_ty.abiSize(zcu)); 39119 const dst_reg = try self.register_manager.allocReg(inst, abi.RegisterClass.gp); 39120 try self.asmRegisterRegister( 39121 .{ ._, .xor }, 39122 registerAlias(dst_reg, @min(result_size, 4)), 39123 registerAlias(dst_reg, @min(result_size, 4)), 39124 ); 39125 39126 for (elements, 0..) |elem, elem_i| { 39127 const elem_reg = try self.copyToTmpRegister(elem_ty, .{ .air_ref = elem }); 39128 const elem_lock = self.register_manager.lockRegAssumeUnused(elem_reg); 39129 defer self.register_manager.unlockReg(elem_lock); 39130 39131 try self.asmRegisterImmediate( 39132 .{ ._, .@"and" }, 39133 registerAlias(elem_reg, @min(result_size, 4)), 39134 .u(1), 39135 ); 39136 if (elem_i > 0) try self.asmRegisterImmediate( 39137 .{ ._l, .sh }, 39138 registerAlias(elem_reg, result_size), 39139 .u(@intCast(elem_i)), 39140 ); 39141 try self.asmRegisterRegister( 39142 .{ ._, .@"or" }, 39143 registerAlias(dst_reg, result_size), 39144 registerAlias(elem_reg, result_size), 39145 ); 39146 } 39147 break :result .{ .register = dst_reg }; 39148 } else { 39149 const frame_index = try self.allocFrameIndex(.initSpill(result_ty, zcu)); 39150 const elem_size: u32 = @intCast(elem_ty.abiSize(zcu)); 39151 39152 for (elements, 0..) |elem, elem_i| { 39153 const elem_mcv = try self.resolveInst(elem); 39154 const mat_elem_mcv = switch (elem_mcv) { 39155 .load_tlv => |sym_index| MCValue{ .lea_tlv = sym_index }, 39156 else => elem_mcv, 39157 }; 39158 const elem_off: i32 = @intCast(elem_size * elem_i); 39159 try self.genSetMem( 39160 .{ .frame = frame_index }, 39161 elem_off, 39162 elem_ty, 39163 mat_elem_mcv, 39164 .{}, 39165 ); 39166 } 39167 if (result_ty.sentinel(zcu)) |sentinel| try self.genSetMem( 39168 .{ .frame = frame_index }, 39169 @intCast(elem_size * elements.len), 39170 elem_ty, 39171 try self.genTypedValue(sentinel), 39172 .{}, 39173 ); 39174 break :result .{ .load_frame = .{ .index = frame_index } }; 39175 } 39176 }, 39177 else => unreachable, 39178 } 39179 }; 39180 39181 if (elements.len <= Liveness.bpi - 1) { 39182 var buf: [Liveness.bpi - 1]Air.Inst.Ref = @splat(.none); 39183 @memcpy(buf[0..elements.len], elements); 39184 return self.finishAir(inst, result, buf); 39185 } 39186 var bt = self.liveness.iterateBigTomb(inst); 39187 for (elements) |elem| try self.feed(&bt, elem); 39188 return self.finishAirResult(inst, result); 39189 } 39190 39191 fn airUnionInit(self: *CodeGen, inst: Air.Inst.Index) !void { 39192 const pt = self.pt; 39193 const zcu = pt.zcu; 39194 const ip = &zcu.intern_pool; 39195 const ty_pl = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_pl; 39196 const extra = self.air.extraData(Air.UnionInit, ty_pl.payload).data; 39197 const result: MCValue = result: { 39198 const union_ty = self.typeOfIndex(inst); 39199 const layout = union_ty.unionGetLayout(zcu); 39200 39201 const src_ty = self.typeOf(extra.init); 39202 const src_mcv = try self.resolveInst(extra.init); 39203 if (layout.tag_size == 0) { 39204 if (layout.abi_size <= src_ty.abiSize(zcu) and 39205 self.reuseOperand(inst, extra.init, 0, src_mcv)) break :result src_mcv; 39206 39207 const dst_mcv = try self.allocRegOrMem(inst, true); 39208 try self.genCopy(src_ty, dst_mcv, src_mcv, .{}); 39209 break :result dst_mcv; 39210 } 39211 39212 const dst_mcv = try self.allocRegOrMem(inst, false); 39213 39214 const loaded_union = zcu.typeToUnion(union_ty).?; 39215 const field_name = loaded_union.loadTagType(ip).names.get(ip)[extra.field_index]; 39216 const tag_ty: Type = .fromInterned(loaded_union.enum_tag_ty); 39217 const field_index = tag_ty.enumFieldIndex(field_name, zcu).?; 39218 const tag_val = try pt.enumValueFieldIndex(tag_ty, field_index); 39219 const tag_int_val = try tag_val.intFromEnum(tag_ty, pt); 39220 const tag_int = tag_int_val.toUnsignedInt(zcu); 39221 const tag_off: i32 = @intCast(layout.tagOffset()); 39222 try self.genCopy( 39223 tag_ty, 39224 dst_mcv.address().offset(tag_off).deref(), 39225 .{ .immediate = tag_int }, 39226 .{}, 39227 ); 39228 39229 const pl_off: i32 = @intCast(layout.payloadOffset()); 39230 try self.genCopy(src_ty, dst_mcv.address().offset(pl_off).deref(), src_mcv, .{}); 39231 39232 break :result dst_mcv; 39233 }; 39234 return self.finishAir(inst, result, .{ extra.init, .none, .none }); 39235 } 39236 39237 fn airPrefetch(self: *CodeGen, inst: Air.Inst.Index) !void { 39238 const prefetch = self.air.instructions.items(.data)[@intFromEnum(inst)].prefetch; 39239 return self.finishAir(inst, .unreach, .{ prefetch.ptr, .none, .none }); 39240 } 39241 39242 fn airMulAdd(self: *CodeGen, inst: Air.Inst.Index) !void { 39243 const pt = self.pt; 39244 const zcu = pt.zcu; 39245 const pl_op = self.air.instructions.items(.data)[@intFromEnum(inst)].pl_op; 39246 const extra = self.air.extraData(Air.Bin, pl_op.payload).data; 39247 const ty = self.typeOfIndex(inst); 39248 39249 const ops = [3]Air.Inst.Ref{ extra.lhs, extra.rhs, pl_op.operand }; 39250 const result = result: { 39251 if (switch (ty.scalarType(zcu).floatBits(self.target.*)) { 39252 16, 80, 128 => true, 39253 32, 64 => !self.hasFeature(.fma), 39254 else => unreachable, 39255 }) { 39256 if (ty.zigTypeTag(zcu) != .float) return self.fail("TODO implement airMulAdd for {}", .{ 39257 ty.fmt(pt), 39258 }); 39259 39260 var callee_buf: ["__fma?".len]u8 = undefined; 39261 break :result try self.genCall(.{ .lib = .{ 39262 .return_type = ty.toIntern(), 39263 .param_types = &.{ ty.toIntern(), ty.toIntern(), ty.toIntern() }, 39264 .callee = std.fmt.bufPrint(&callee_buf, "{s}fma{s}", .{ 39265 floatLibcAbiPrefix(ty), 39266 floatLibcAbiSuffix(ty), 39267 }) catch unreachable, 39268 } }, &.{ ty, ty, ty }, &.{ 39269 .{ .air_ref = extra.lhs }, .{ .air_ref = extra.rhs }, .{ .air_ref = pl_op.operand }, 39270 }, .{}); 39271 } 39272 39273 var mcvs: [3]MCValue = undefined; 39274 var locks: [3]?RegisterManager.RegisterLock = @splat(null); 39275 defer for (locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); 39276 var order: [3]u2 = @splat(0); 39277 var unused: std.StaticBitSet(3) = .initFull(); 39278 for (ops, &mcvs, &locks, 0..) |op, *mcv, *lock, op_i| { 39279 const op_index: u2 = @intCast(op_i); 39280 mcv.* = try self.resolveInst(op); 39281 if (unused.isSet(0) and mcv.isRegister() and self.reuseOperand(inst, op, op_index, mcv.*)) { 39282 order[op_index] = 1; 39283 unused.unset(0); 39284 } else if (unused.isSet(2) and mcv.isBase()) { 39285 order[op_index] = 3; 39286 unused.unset(2); 39287 } 39288 switch (mcv.*) { 39289 .register => |reg| lock.* = self.register_manager.lockReg(reg), 39290 else => {}, 39291 } 39292 } 39293 for (&order, &mcvs, &locks) |*mop_index, *mcv, *lock| { 39294 if (mop_index.* != 0) continue; 39295 mop_index.* = 1 + @as(u2, @intCast(unused.toggleFirstSet().?)); 39296 if (mop_index.* > 1 and mcv.isRegister()) continue; 39297 const reg = try self.copyToTmpRegister(ty, mcv.*); 39298 mcv.* = .{ .register = reg }; 39299 if (lock.*) |old_lock| self.register_manager.unlockReg(old_lock); 39300 lock.* = self.register_manager.lockRegAssumeUnused(reg); 39301 } 39302 39303 const mir_tag = @as(?Mir.Inst.FixedTag, if (std.mem.eql(u2, &order, &.{ 1, 3, 2 }) or 39304 std.mem.eql(u2, &order, &.{ 3, 1, 2 })) 39305 switch (ty.zigTypeTag(zcu)) { 39306 .float => switch (ty.floatBits(self.target.*)) { 39307 32 => .{ .v_ss, .fmadd132 }, 39308 64 => .{ .v_sd, .fmadd132 }, 39309 16, 80, 128 => null, 39310 else => unreachable, 39311 }, 39312 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 39313 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 39314 32 => switch (ty.vectorLen(zcu)) { 39315 1 => .{ .v_ss, .fmadd132 }, 39316 2...8 => .{ .v_ps, .fmadd132 }, 39317 else => null, 39318 }, 39319 64 => switch (ty.vectorLen(zcu)) { 39320 1 => .{ .v_sd, .fmadd132 }, 39321 2...4 => .{ .v_pd, .fmadd132 }, 39322 else => null, 39323 }, 39324 16, 80, 128 => null, 39325 else => unreachable, 39326 }, 39327 else => unreachable, 39328 }, 39329 else => unreachable, 39330 } 39331 else if (std.mem.eql(u2, &order, &.{ 2, 1, 3 }) or std.mem.eql(u2, &order, &.{ 1, 2, 3 })) 39332 switch (ty.zigTypeTag(zcu)) { 39333 .float => switch (ty.floatBits(self.target.*)) { 39334 32 => .{ .v_ss, .fmadd213 }, 39335 64 => .{ .v_sd, .fmadd213 }, 39336 16, 80, 128 => null, 39337 else => unreachable, 39338 }, 39339 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 39340 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 39341 32 => switch (ty.vectorLen(zcu)) { 39342 1 => .{ .v_ss, .fmadd213 }, 39343 2...8 => .{ .v_ps, .fmadd213 }, 39344 else => null, 39345 }, 39346 64 => switch (ty.vectorLen(zcu)) { 39347 1 => .{ .v_sd, .fmadd213 }, 39348 2...4 => .{ .v_pd, .fmadd213 }, 39349 else => null, 39350 }, 39351 16, 80, 128 => null, 39352 else => unreachable, 39353 }, 39354 else => unreachable, 39355 }, 39356 else => unreachable, 39357 } 39358 else if (std.mem.eql(u2, &order, &.{ 2, 3, 1 }) or std.mem.eql(u2, &order, &.{ 3, 2, 1 })) 39359 switch (ty.zigTypeTag(zcu)) { 39360 .float => switch (ty.floatBits(self.target.*)) { 39361 32 => .{ .v_ss, .fmadd231 }, 39362 64 => .{ .v_sd, .fmadd231 }, 39363 16, 80, 128 => null, 39364 else => unreachable, 39365 }, 39366 .vector => switch (ty.childType(zcu).zigTypeTag(zcu)) { 39367 .float => switch (ty.childType(zcu).floatBits(self.target.*)) { 39368 32 => switch (ty.vectorLen(zcu)) { 39369 1 => .{ .v_ss, .fmadd231 }, 39370 2...8 => .{ .v_ps, .fmadd231 }, 39371 else => null, 39372 }, 39373 64 => switch (ty.vectorLen(zcu)) { 39374 1 => .{ .v_sd, .fmadd231 }, 39375 2...4 => .{ .v_pd, .fmadd231 }, 39376 else => null, 39377 }, 39378 16, 80, 128 => null, 39379 else => unreachable, 39380 }, 39381 else => unreachable, 39382 }, 39383 else => unreachable, 39384 } 39385 else 39386 unreachable) orelse return self.fail("TODO implement airMulAdd for {}", .{ty.fmt(pt)}); 39387 39388 var mops: [3]MCValue = undefined; 39389 for (order, mcvs) |mop_index, mcv| mops[mop_index - 1] = mcv; 39390 39391 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 39392 const mop1_reg = registerAlias(mops[0].getReg().?, abi_size); 39393 const mop2_reg = registerAlias(mops[1].getReg().?, abi_size); 39394 if (mops[2].isRegister()) try self.asmRegisterRegisterRegister( 39395 mir_tag, 39396 mop1_reg, 39397 mop2_reg, 39398 registerAlias(mops[2].getReg().?, abi_size), 39399 ) else try self.asmRegisterRegisterMemory( 39400 mir_tag, 39401 mop1_reg, 39402 mop2_reg, 39403 try mops[2].mem(self, .{ .size = .fromSize(abi_size) }), 39404 ); 39405 break :result mops[0]; 39406 }; 39407 return self.finishAir(inst, result, ops); 39408 } 39409 39410 fn airVaStart(self: *CodeGen, inst: Air.Inst.Index) !void { 39411 const pt = self.pt; 39412 const zcu = pt.zcu; 39413 const va_list_ty = self.air.instructions.items(.data)[@intFromEnum(inst)].ty; 39414 const ptr_anyopaque_ty = try pt.singleMutPtrType(.anyopaque); 39415 39416 const result: MCValue = switch (self.fn_type.fnCallingConvention(zcu)) { 39417 .x86_64_sysv => result: { 39418 const info = self.va_info.sysv; 39419 const dst_fi = try self.allocFrameIndex(.initSpill(va_list_ty, zcu)); 39420 var field_off: u31 = 0; 39421 // gp_offset: c_uint, 39422 try self.genSetMem( 39423 .{ .frame = dst_fi }, 39424 field_off, 39425 .c_uint, 39426 .{ .immediate = info.gp_count * 8 }, 39427 .{}, 39428 ); 39429 field_off += @intCast(Type.c_uint.abiSize(zcu)); 39430 // fp_offset: c_uint, 39431 try self.genSetMem( 39432 .{ .frame = dst_fi }, 39433 field_off, 39434 .c_uint, 39435 .{ .immediate = abi.SysV.c_abi_int_param_regs.len * 8 + info.fp_count * 16 }, 39436 .{}, 39437 ); 39438 field_off += @intCast(Type.c_uint.abiSize(zcu)); 39439 // overflow_arg_area: *anyopaque, 39440 try self.genSetMem( 39441 .{ .frame = dst_fi }, 39442 field_off, 39443 ptr_anyopaque_ty, 39444 .{ .lea_frame = info.overflow_arg_area }, 39445 .{}, 39446 ); 39447 field_off += @intCast(ptr_anyopaque_ty.abiSize(zcu)); 39448 // reg_save_area: *anyopaque, 39449 try self.genSetMem( 39450 .{ .frame = dst_fi }, 39451 field_off, 39452 ptr_anyopaque_ty, 39453 .{ .lea_frame = info.reg_save_area }, 39454 .{}, 39455 ); 39456 field_off += @intCast(ptr_anyopaque_ty.abiSize(zcu)); 39457 break :result .{ .load_frame = .{ .index = dst_fi } }; 39458 }, 39459 .x86_64_win => return self.fail("TODO implement c_va_start for Win64", .{}), 39460 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), 39461 }; 39462 return self.finishAir(inst, result, .{ .none, .none, .none }); 39463 } 39464 39465 fn airVaArg(self: *CodeGen, inst: Air.Inst.Index) !void { 39466 const pt = self.pt; 39467 const zcu = pt.zcu; 39468 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 39469 const ty = self.typeOfIndex(inst); 39470 const promote_ty = self.promoteVarArg(ty); 39471 const ptr_anyopaque_ty = try pt.singleMutPtrType(.anyopaque); 39472 const unused = self.liveness.isUnused(inst); 39473 39474 const result: MCValue = switch (self.fn_type.fnCallingConvention(zcu)) { 39475 .x86_64_sysv => result: { 39476 try self.spillEflagsIfOccupied(); 39477 39478 const tmp_regs = 39479 try self.register_manager.allocRegs(2, @splat(null), abi.RegisterClass.gp); 39480 const offset_reg = tmp_regs[0].to32(); 39481 const addr_reg = tmp_regs[1].to64(); 39482 const tmp_locks = self.register_manager.lockRegsAssumeUnused(2, tmp_regs); 39483 defer for (tmp_locks) |lock| self.register_manager.unlockReg(lock); 39484 39485 const promote_mcv = try self.allocTempRegOrMem(promote_ty, true); 39486 const promote_lock = switch (promote_mcv) { 39487 .register => |reg| self.register_manager.lockRegAssumeUnused(reg), 39488 else => null, 39489 }; 39490 defer if (promote_lock) |lock| self.register_manager.unlockReg(lock); 39491 39492 const ptr_arg_list_reg = 39493 try self.copyToTmpRegister(self.typeOf(ty_op.operand), .{ .air_ref = ty_op.operand }); 39494 const ptr_arg_list_lock = self.register_manager.lockRegAssumeUnused(ptr_arg_list_reg); 39495 defer self.register_manager.unlockReg(ptr_arg_list_lock); 39496 39497 const gp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 0 } }; 39498 const fp_offset: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 4 } }; 39499 const overflow_arg_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 8 } }; 39500 const reg_save_area: MCValue = .{ .indirect = .{ .reg = ptr_arg_list_reg, .off = 16 } }; 39501 39502 const classes = std.mem.sliceTo(&abi.classifySystemV(promote_ty, zcu, self.target.*, .arg), .none); 39503 switch (classes[0]) { 39504 .integer => { 39505 assert(classes.len == 1); 39506 39507 try self.genSetReg(offset_reg, .c_uint, gp_offset, .{}); 39508 try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, .u( 39509 abi.SysV.c_abi_int_param_regs.len * 8, 39510 )); 39511 const mem_reloc = try self.asmJccReloc(.ae, undefined); 39512 39513 try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area, .{}); 39514 if (!unused) try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, .{ 39515 .base = .{ .reg = addr_reg }, 39516 .mod = .{ .rm = .{ 39517 .size = .qword, 39518 .index = offset_reg.to64(), 39519 } }, 39520 }); 39521 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, .{ 39522 .base = .{ .reg = offset_reg.to64() }, 39523 .mod = .{ .rm = .{ 39524 .size = .qword, 39525 .disp = 8, 39526 } }, 39527 }); 39528 try self.genCopy(.c_uint, gp_offset, .{ .register = offset_reg }, .{}); 39529 const done_reloc = try self.asmJmpReloc(undefined); 39530 39531 self.performReloc(mem_reloc); 39532 try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area, .{}); 39533 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), .{ 39534 .base = .{ .reg = addr_reg }, 39535 .mod = .{ .rm = .{ 39536 .size = .qword, 39537 .disp = @intCast(@max(promote_ty.abiSize(zcu), 8)), 39538 } }, 39539 }); 39540 try self.genCopy( 39541 ptr_anyopaque_ty, 39542 overflow_arg_area, 39543 .{ .register = offset_reg.to64() }, 39544 .{}, 39545 ); 39546 39547 self.performReloc(done_reloc); 39548 if (!unused) try self.genCopy(promote_ty, promote_mcv, .{ 39549 .indirect = .{ .reg = addr_reg }, 39550 }, .{}); 39551 }, 39552 .sse => { 39553 assert(classes.len == 1); 39554 39555 try self.genSetReg(offset_reg, .c_uint, fp_offset, .{}); 39556 try self.asmRegisterImmediate(.{ ._, .cmp }, offset_reg, .u( 39557 abi.SysV.c_abi_int_param_regs.len * 8 + abi.SysV.c_abi_sse_param_regs.len * 16, 39558 )); 39559 const mem_reloc = try self.asmJccReloc(.ae, undefined); 39560 39561 try self.genSetReg(addr_reg, ptr_anyopaque_ty, reg_save_area, .{}); 39562 if (!unused) try self.asmRegisterMemory(.{ ._, .lea }, addr_reg, .{ 39563 .base = .{ .reg = addr_reg }, 39564 .mod = .{ .rm = .{ 39565 .size = .qword, 39566 .index = offset_reg.to64(), 39567 } }, 39568 }); 39569 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg, .{ 39570 .base = .{ .reg = offset_reg.to64() }, 39571 .mod = .{ .rm = .{ 39572 .size = .qword, 39573 .disp = 16, 39574 } }, 39575 }); 39576 try self.genCopy(.c_uint, fp_offset, .{ .register = offset_reg }, .{}); 39577 const done_reloc = try self.asmJmpReloc(undefined); 39578 39579 self.performReloc(mem_reloc); 39580 try self.genSetReg(addr_reg, ptr_anyopaque_ty, overflow_arg_area, .{}); 39581 try self.asmRegisterMemory(.{ ._, .lea }, offset_reg.to64(), .{ 39582 .base = .{ .reg = addr_reg }, 39583 .mod = .{ .rm = .{ 39584 .size = .qword, 39585 .disp = @intCast(@max(promote_ty.abiSize(zcu), 8)), 39586 } }, 39587 }); 39588 try self.genCopy( 39589 ptr_anyopaque_ty, 39590 overflow_arg_area, 39591 .{ .register = offset_reg.to64() }, 39592 .{}, 39593 ); 39594 39595 self.performReloc(done_reloc); 39596 if (!unused) try self.genCopy(promote_ty, promote_mcv, .{ 39597 .indirect = .{ .reg = addr_reg }, 39598 }, .{}); 39599 }, 39600 .memory => { 39601 assert(classes.len == 1); 39602 unreachable; 39603 }, 39604 else => return self.fail("TODO implement c_va_arg for {} on SysV", .{promote_ty.fmt(pt)}), 39605 } 39606 39607 if (unused) break :result .unreach; 39608 if (ty.toIntern() == promote_ty.toIntern()) break :result promote_mcv; 39609 39610 if (!promote_ty.isRuntimeFloat()) { 39611 const dst_mcv = try self.allocRegOrMem(inst, true); 39612 try self.genCopy(ty, dst_mcv, promote_mcv, .{}); 39613 break :result dst_mcv; 39614 } 39615 39616 assert(ty.toIntern() == .f32_type and promote_ty.toIntern() == .f64_type); 39617 const dst_mcv = if (promote_mcv.isRegister()) 39618 promote_mcv 39619 else 39620 try self.copyToRegisterWithInstTracking(inst, ty, promote_mcv); 39621 const dst_reg = dst_mcv.getReg().?.to128(); 39622 const dst_lock = self.register_manager.lockReg(dst_reg); 39623 defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); 39624 39625 if (self.hasFeature(.avx)) if (promote_mcv.isBase()) try self.asmRegisterRegisterMemory( 39626 .{ .v_ss, .cvtsd2 }, 39627 dst_reg, 39628 dst_reg, 39629 try promote_mcv.mem(self, .{ .size = .qword }), 39630 ) else try self.asmRegisterRegisterRegister( 39631 .{ .v_ss, .cvtsd2 }, 39632 dst_reg, 39633 dst_reg, 39634 (if (promote_mcv.isRegister()) 39635 promote_mcv.getReg().? 39636 else 39637 try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), 39638 ) else if (promote_mcv.isBase()) try self.asmRegisterMemory( 39639 .{ ._ss, .cvtsd2 }, 39640 dst_reg, 39641 try promote_mcv.mem(self, .{ .size = .qword }), 39642 ) else try self.asmRegisterRegister( 39643 .{ ._ss, .cvtsd2 }, 39644 dst_reg, 39645 (if (promote_mcv.isRegister()) 39646 promote_mcv.getReg().? 39647 else 39648 try self.copyToTmpRegister(promote_ty, promote_mcv)).to128(), 39649 ); 39650 break :result promote_mcv; 39651 }, 39652 .x86_64_win => return self.fail("TODO implement c_va_arg for Win64", .{}), 39653 else => |cc| return self.fail("{s} does not support var args", .{@tagName(cc)}), 39654 }; 39655 return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); 39656 } 39657 39658 fn airVaCopy(self: *CodeGen, inst: Air.Inst.Index) !void { 39659 const ty_op = self.air.instructions.items(.data)[@intFromEnum(inst)].ty_op; 39660 const ptr_va_list_ty = self.typeOf(ty_op.operand); 39661 39662 const dst_mcv = try self.allocRegOrMem(inst, true); 39663 try self.load(dst_mcv, ptr_va_list_ty, .{ .air_ref = ty_op.operand }); 39664 return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); 39665 } 39666 39667 fn airVaEnd(self: *CodeGen, inst: Air.Inst.Index) !void { 39668 const un_op = self.air.instructions.items(.data)[@intFromEnum(inst)].un_op; 39669 return self.finishAir(inst, .unreach, .{ un_op, .none, .none }); 39670 } 39671 39672 fn resolveInst(self: *CodeGen, ref: Air.Inst.Ref) InnerError!MCValue { 39673 const zcu = self.pt.zcu; 39674 const ty = self.typeOf(ref); 39675 39676 // If the type has no codegen bits, no need to store it. 39677 if (!ty.hasRuntimeBitsIgnoreComptime(zcu)) return .none; 39678 39679 const mcv = if (ref.toIndex()) |inst| mcv: { 39680 break :mcv self.inst_tracking.getPtr(inst).?.short; 39681 } else mcv: { 39682 const ip_index = ref.toInterned().?; 39683 const gop = try self.const_tracking.getOrPut(self.gpa, ip_index); 39684 if (!gop.found_existing) gop.value_ptr.* = .init(init: { 39685 const const_mcv = try self.genTypedValue(.fromInterned(ip_index)); 39686 switch (const_mcv) { 39687 .lea_tlv => |tlv_sym| switch (self.bin_file.tag) { 39688 .elf, .macho => { 39689 if (self.mod.pic) { 39690 try self.spillRegisters(&.{ .rdi, .rax }); 39691 } else { 39692 try self.spillRegisters(&.{.rax}); 39693 } 39694 const frame_index = try self.allocFrameIndex(.init(.{ 39695 .size = 8, 39696 .alignment = .@"8", 39697 })); 39698 try self.genSetMem( 39699 .{ .frame = frame_index }, 39700 0, 39701 .usize, 39702 .{ .lea_symbol = .{ .sym_index = tlv_sym } }, 39703 .{}, 39704 ); 39705 break :init .{ .load_frame = .{ .index = frame_index } }; 39706 }, 39707 else => break :init const_mcv, 39708 }, 39709 else => break :init const_mcv, 39710 } 39711 }); 39712 break :mcv gop.value_ptr.short; 39713 }; 39714 39715 switch (mcv) { 39716 .none, .unreach, .dead => unreachable, 39717 else => return mcv, 39718 } 39719 } 39720 39721 fn getResolvedInstValue(self: *CodeGen, inst: Air.Inst.Index) *InstTracking { 39722 const tracking = self.inst_tracking.getPtr(inst).?; 39723 return switch (tracking.short) { 39724 .none, .unreach, .dead => unreachable, 39725 else => tracking, 39726 }; 39727 } 39728 39729 /// If the MCValue is an immediate, and it does not fit within this type, 39730 /// we put it in a register. 39731 /// A potential opportunity for future optimization here would be keeping track 39732 /// of the fact that the instruction is available both as an immediate 39733 /// and as a register. 39734 fn limitImmediateType(self: *CodeGen, operand: Air.Inst.Ref, comptime T: type) !MCValue { 39735 const mcv = try self.resolveInst(operand); 39736 const ti = @typeInfo(T).int; 39737 switch (mcv) { 39738 .immediate => |imm| { 39739 // This immediate is unsigned. 39740 const U = std.meta.Int(.unsigned, ti.bits - @intFromBool(ti.signedness == .signed)); 39741 if (imm >= std.math.maxInt(U)) { 39742 return MCValue{ .register = try self.copyToTmpRegister(.usize, mcv) }; 39743 } 39744 }, 39745 else => {}, 39746 } 39747 return mcv; 39748 } 39749 39750 fn genResult(self: *CodeGen, res: codegen.GenResult) InnerError!MCValue { 39751 return switch (res) { 39752 .mcv => |mcv| switch (mcv) { 39753 .none => .none, 39754 .undef => .undef, 39755 .immediate => |imm| .{ .immediate = imm }, 39756 .memory => |addr| .{ .memory = addr }, 39757 .load_symbol => |sym_index| .{ .load_symbol = .{ .sym_index = sym_index } }, 39758 .lea_symbol => |sym_index| .{ .lea_symbol = .{ .sym_index = sym_index } }, 39759 .load_direct => |sym_index| .{ .load_direct = sym_index }, 39760 .lea_direct => |sym_index| .{ .lea_direct = sym_index }, 39761 .load_got => |sym_index| .{ .lea_got = sym_index }, 39762 .load_tlv => |sym_index| .{ .lea_tlv = sym_index }, 39763 }, 39764 .fail => |msg| return self.failMsg(msg), 39765 }; 39766 } 39767 39768 fn genTypedValue(self: *CodeGen, val: Value) InnerError!MCValue { 39769 return self.genResult(try codegen.genTypedValue(self.bin_file, self.pt, self.src_loc, val, self.target.*)); 39770 } 39771 39772 fn lowerUav(self: *CodeGen, val: Value) InnerError!MCValue { 39773 return self.genResult(try self.bin_file.lowerUav(self.pt, val.toIntern(), .none, self.src_loc)); 39774 } 39775 39776 const CallMCValues = struct { 39777 args: []MCValue, 39778 return_value: InstTracking, 39779 stack_byte_count: u31, 39780 stack_align: InternPool.Alignment, 39781 gp_count: u32, 39782 fp_count: u32, 39783 err_ret_trace_reg: Register, 39784 39785 fn deinit(self: *CallMCValues, func: *CodeGen) void { 39786 func.gpa.free(self.args); 39787 self.* = undefined; 39788 } 39789 }; 39790 39791 /// Caller must call `CallMCValues.deinit`. 39792 fn resolveCallingConventionValues( 39793 self: *CodeGen, 39794 fn_info: InternPool.Key.FuncType, 39795 var_args: []const Type, 39796 stack_frame_base: FrameIndex, 39797 ) !CallMCValues { 39798 const pt = self.pt; 39799 const zcu = pt.zcu; 39800 const ip = &zcu.intern_pool; 39801 const cc = fn_info.cc; 39802 39803 const ExpectedContents = extern struct { 39804 param_types: [32][@sizeOf(Type)]u8 align(@alignOf(Type)), 39805 }; 39806 var stack align(@max(@alignOf(ExpectedContents), @alignOf(std.heap.StackFallbackAllocator(0)))) = 39807 std.heap.stackFallback(@sizeOf(ExpectedContents), self.gpa); 39808 const allocator = stack.get(); 39809 39810 const param_types = try allocator.alloc(Type, fn_info.param_types.len + var_args.len); 39811 defer allocator.free(param_types); 39812 39813 for (param_types[0..fn_info.param_types.len], fn_info.param_types.get(ip)) |*dest, src| 39814 dest.* = .fromInterned(src); 39815 for (param_types[fn_info.param_types.len..], var_args) |*param_ty, arg_ty| 39816 param_ty.* = self.promoteVarArg(arg_ty); 39817 39818 var result: CallMCValues = .{ 39819 .args = try self.gpa.alloc(MCValue, param_types.len), 39820 // These undefined values must be populated before returning from this function. 39821 .return_value = undefined, 39822 .stack_byte_count = 0, 39823 .stack_align = undefined, 39824 .gp_count = 0, 39825 .fp_count = 0, 39826 .err_ret_trace_reg = .none, 39827 }; 39828 errdefer self.gpa.free(result.args); 39829 39830 const ret_ty: Type = .fromInterned(fn_info.return_type); 39831 switch (cc) { 39832 .naked => { 39833 assert(result.args.len == 0); 39834 result.return_value = .init(.unreach); 39835 result.stack_align = switch (self.target.cpu.arch) { 39836 else => unreachable, 39837 .x86 => .@"4", 39838 .x86_64 => .@"8", 39839 }; 39840 }, 39841 .x86_64_sysv, .x86_64_win => |cc_opts| { 39842 var ret_int_reg_i: u32 = 0; 39843 var ret_sse_reg_i: u32 = 0; 39844 var param_int_reg_i: u32 = 0; 39845 var param_sse_reg_i: u32 = 0; 39846 result.stack_align = .fromByteUnits(cc_opts.incoming_stack_alignment orelse 16); 39847 39848 switch (cc) { 39849 .x86_64_sysv => {}, 39850 .x86_64_win => result.stack_byte_count += @intCast(4 * 8), 39851 else => unreachable, 39852 } 39853 39854 // Return values 39855 if (ret_ty.isNoReturn(zcu)) { 39856 result.return_value = .init(.unreach); 39857 } else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) { 39858 // TODO: is this even possible for C calling convention? 39859 result.return_value = .init(.none); 39860 } else { 39861 var ret_tracking: [4]InstTracking = undefined; 39862 var ret_tracking_i: usize = 0; 39863 39864 const classes = switch (cc) { 39865 .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ret_ty, zcu, self.target.*, .ret), .none), 39866 .x86_64_win => &.{abi.classifyWindows(ret_ty, zcu)}, 39867 else => unreachable, 39868 }; 39869 for (classes) |class| switch (class) { 39870 .integer => { 39871 const ret_int_reg = registerAlias( 39872 abi.getCAbiIntReturnRegs(cc)[ret_int_reg_i], 39873 @intCast(@min(ret_ty.abiSize(zcu), 8)), 39874 ); 39875 ret_int_reg_i += 1; 39876 39877 ret_tracking[ret_tracking_i] = .init(.{ .register = ret_int_reg }); 39878 ret_tracking_i += 1; 39879 }, 39880 .sse, .float, .float_combine, .win_i128 => { 39881 const ret_sse_regs = abi.getCAbiSseReturnRegs(cc); 39882 const abi_size: u32 = @intCast(ret_ty.abiSize(zcu)); 39883 const reg_size = @min(abi_size, self.vectorSize(.float)); 39884 var byte_offset: u32 = 0; 39885 while (byte_offset < abi_size) : (byte_offset += reg_size) { 39886 const ret_sse_reg = registerAlias(ret_sse_regs[ret_sse_reg_i], reg_size); 39887 ret_sse_reg_i += 1; 39888 39889 ret_tracking[ret_tracking_i] = .init(.{ .register = ret_sse_reg }); 39890 ret_tracking_i += 1; 39891 } 39892 }, 39893 .sseup => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .sse), 39894 .x87 => { 39895 ret_tracking[ret_tracking_i] = .init(.{ .register = abi.getCAbiX87ReturnRegs(cc)[0] }); 39896 ret_tracking_i += 1; 39897 }, 39898 .x87up => assert(ret_tracking[ret_tracking_i - 1].short.register.class() == .x87), 39899 .complex_x87 => { 39900 ret_tracking[ret_tracking_i] = .init(.{ .register_pair = abi.getCAbiX87ReturnRegs(cc)[0..2].* }); 39901 ret_tracking_i += 1; 39902 }, 39903 .memory => { 39904 const ret_int_reg = abi.getCAbiIntReturnRegs(cc)[ret_int_reg_i].to64(); 39905 ret_int_reg_i += 1; 39906 const ret_indirect_reg = abi.getCAbiIntParamRegs(cc)[param_int_reg_i]; 39907 param_int_reg_i += 1; 39908 39909 ret_tracking[ret_tracking_i] = .{ 39910 .short = .{ .indirect = .{ .reg = ret_int_reg } }, 39911 .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, 39912 }; 39913 ret_tracking_i += 1; 39914 }, 39915 .none, .integer_per_element => unreachable, 39916 }; 39917 result.return_value = switch (ret_tracking_i) { 39918 else => unreachable, 39919 1 => ret_tracking[0], 39920 2 => .init(.{ .register_pair = .{ 39921 ret_tracking[0].short.register, 39922 ret_tracking[1].short.register, 39923 } }), 39924 3 => .init(.{ .register_triple = .{ 39925 ret_tracking[0].short.register, 39926 ret_tracking[1].short.register, 39927 ret_tracking[2].short.register, 39928 } }), 39929 4 => .init(.{ .register_quadruple = .{ 39930 ret_tracking[0].short.register, 39931 ret_tracking[1].short.register, 39932 ret_tracking[2].short.register, 39933 ret_tracking[3].short.register, 39934 } }), 39935 }; 39936 } 39937 39938 // Input params 39939 for (param_types, result.args) |ty, *arg| { 39940 assert(ty.hasRuntimeBitsIgnoreComptime(zcu)); 39941 switch (cc) { 39942 .x86_64_sysv => {}, 39943 .x86_64_win => { 39944 param_int_reg_i = @max(param_int_reg_i, param_sse_reg_i); 39945 param_sse_reg_i = param_int_reg_i; 39946 }, 39947 else => unreachable, 39948 } 39949 39950 var arg_mcv: [4]MCValue = undefined; 39951 var arg_mcv_i: usize = 0; 39952 39953 const classes = switch (cc) { 39954 .x86_64_sysv => std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .arg), .none), 39955 .x86_64_win => &.{abi.classifyWindows(ty, zcu)}, 39956 else => unreachable, 39957 }; 39958 classes: for (classes) |class| switch (class) { 39959 .integer => { 39960 const param_int_regs = abi.getCAbiIntParamRegs(cc); 39961 if (param_int_reg_i >= param_int_regs.len) break; 39962 39963 const param_int_reg = 39964 registerAlias(param_int_regs[param_int_reg_i], @intCast(@min(ty.abiSize(zcu), 8))); 39965 param_int_reg_i += 1; 39966 39967 arg_mcv[arg_mcv_i] = .{ .register = param_int_reg }; 39968 arg_mcv_i += 1; 39969 }, 39970 .sse, .float, .float_combine => { 39971 const param_sse_regs = abi.getCAbiSseParamRegs(cc, self.target); 39972 const abi_size: u32 = @intCast(ty.abiSize(zcu)); 39973 const reg_size = @min(abi_size, self.vectorSize(.float)); 39974 var byte_offset: u32 = 0; 39975 while (byte_offset < abi_size) : (byte_offset += reg_size) { 39976 if (param_sse_reg_i >= param_sse_regs.len) break :classes; 39977 39978 const param_sse_reg = registerAlias(param_sse_regs[param_sse_reg_i], reg_size); 39979 param_sse_reg_i += 1; 39980 39981 arg_mcv[arg_mcv_i] = .{ .register = param_sse_reg }; 39982 arg_mcv_i += 1; 39983 } 39984 }, 39985 .sseup => assert(arg_mcv[arg_mcv_i - 1].register.class() == .sse), 39986 .x87, .x87up, .complex_x87, .memory, .win_i128 => switch (cc) { 39987 .x86_64_sysv => switch (class) { 39988 .x87, .x87up, .complex_x87, .memory => break, 39989 else => unreachable, 39990 }, 39991 .x86_64_win => if (ty.abiSize(zcu) > 8) { 39992 const param_int_reg = abi.getCAbiIntParamRegs(cc)[param_int_reg_i].to64(); 39993 param_int_reg_i += 1; 39994 39995 arg_mcv[arg_mcv_i] = .{ .indirect = .{ .reg = param_int_reg } }; 39996 arg_mcv_i += 1; 39997 } else break, 39998 else => unreachable, 39999 }, 40000 .none => unreachable, 40001 .integer_per_element => { 40002 const param_int_regs_len: u32 = 40003 @intCast(abi.getCAbiIntParamRegs(cc).len); 40004 const remaining_param_int_regs: u3 = 40005 @intCast(param_int_regs_len - param_int_reg_i); 40006 param_int_reg_i = param_int_regs_len; 40007 40008 const frame_elem_align = 8; 40009 const frame_elems_len = ty.vectorLen(zcu) - remaining_param_int_regs; 40010 const frame_elem_size = std.mem.alignForward( 40011 u64, 40012 ty.childType(zcu).abiSize(zcu), 40013 frame_elem_align, 40014 ); 40015 const frame_size: u31 = @intCast(frame_elems_len * frame_elem_size); 40016 40017 result.stack_byte_count = 40018 std.mem.alignForward(u31, result.stack_byte_count, frame_elem_align); 40019 arg_mcv[arg_mcv_i] = .{ .elementwise_regs_then_frame = .{ 40020 .regs = remaining_param_int_regs, 40021 .frame_off = @intCast(result.stack_byte_count), 40022 .frame_index = stack_frame_base, 40023 } }; 40024 arg_mcv_i += 1; 40025 result.stack_byte_count += frame_size; 40026 }, 40027 } else { 40028 arg.* = switch (arg_mcv_i) { 40029 else => unreachable, 40030 1 => arg_mcv[0], 40031 2 => .{ .register_pair = .{ 40032 arg_mcv[0].register, 40033 arg_mcv[1].register, 40034 } }, 40035 3 => .{ .register_triple = .{ 40036 arg_mcv[0].register, 40037 arg_mcv[1].register, 40038 arg_mcv[2].register, 40039 } }, 40040 4 => .{ .register_quadruple = .{ 40041 arg_mcv[0].register, 40042 arg_mcv[1].register, 40043 arg_mcv[2].register, 40044 arg_mcv[3].register, 40045 } }, 40046 }; 40047 continue; 40048 } 40049 40050 const param_align = ty.abiAlignment(zcu).max(.@"8"); 40051 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count)); 40052 result.stack_align = result.stack_align.max(param_align); 40053 arg.* = .{ .load_frame = .{ 40054 .index = stack_frame_base, 40055 .off = result.stack_byte_count, 40056 } }; 40057 result.stack_byte_count += @intCast(ty.abiSize(zcu)); 40058 } 40059 assert(param_int_reg_i <= 6); 40060 result.gp_count = param_int_reg_i; 40061 assert(param_sse_reg_i <= 16); 40062 result.fp_count = param_sse_reg_i; 40063 }, 40064 .auto => { 40065 result.stack_align = abi.zigcc.stack_align orelse .fromByteUnits(self.vectorSize(.float)); 40066 40067 var param_gpr = abi.getCAbiIntParamRegs(cc); 40068 var param_x87 = abi.getCAbiX87ParamRegs(cc); 40069 var param_sse = abi.getCAbiSseParamRegs(cc, self.target); 40070 40071 if (zcu.comp.config.any_error_tracing) { 40072 result.err_ret_trace_reg = param_gpr[param_gpr.len - 1]; 40073 param_gpr = param_gpr[0 .. param_gpr.len - 1]; 40074 } 40075 40076 // Return values 40077 result.return_value = if (ret_ty.isNoReturn(zcu)) 40078 .init(.unreach) 40079 else if (!ret_ty.hasRuntimeBitsIgnoreComptime(zcu)) 40080 .init(.none) 40081 else return_value: { 40082 const ret_gpr = abi.getCAbiIntReturnRegs(cc); 40083 const ret_size: u31 = @intCast(ret_ty.abiSize(zcu)); 40084 if (abi.zigcc.return_in_regs) switch (self.regClassForType(ret_ty)) { 40085 .general_purpose => if (ret_size <= @as(u4, switch (self.target.cpu.arch) { 40086 else => unreachable, 40087 .x86 => 4, 40088 .x86_64 => 8, 40089 })) 40090 break :return_value .init(.{ .register = registerAlias(ret_gpr[0], ret_size) }) 40091 else if (ret_gpr.len >= 2 and ret_ty.isSliceAtRuntime(zcu)) 40092 break :return_value .init(.{ .register_pair = ret_gpr[0..2].* }), 40093 .segment, .mmx, .ip, .cr, .dr => unreachable, 40094 .x87 => if (ret_size <= 16) break :return_value .init(.{ .register = .st0 }), 40095 .sse => if (ret_size <= self.vectorSize(.float)) break :return_value .init(.{ 40096 .register = registerAlias(abi.getCAbiSseReturnRegs(cc)[0], @max(ret_size, 16)), 40097 }), 40098 }; 40099 const ret_indirect_reg = param_gpr[0]; 40100 param_gpr = param_gpr[1..]; 40101 break :return_value .{ 40102 .short = .{ .indirect = .{ .reg = ret_gpr[0] } }, 40103 .long = .{ .indirect = .{ .reg = ret_indirect_reg } }, 40104 }; 40105 }; 40106 40107 // Input params 40108 for (param_types, result.args) |param_ty, *arg| { 40109 if (!param_ty.hasRuntimeBitsIgnoreComptime(zcu)) { 40110 arg.* = .none; 40111 continue; 40112 } 40113 const param_size: u31 = @intCast(param_ty.abiSize(zcu)); 40114 if (abi.zigcc.params_in_regs) switch (self.regClassForType(param_ty)) { 40115 .general_purpose => if (param_gpr.len >= 1 and param_size <= @as(u4, switch (self.target.cpu.arch) { 40116 else => unreachable, 40117 .x86 => 4, 40118 .x86_64 => 8, 40119 })) { 40120 arg.* = .{ .register = registerAlias(param_gpr[0], param_size) }; 40121 param_gpr = param_gpr[1..]; 40122 continue; 40123 } else if (param_gpr.len >= 2 and param_ty.isSliceAtRuntime(zcu)) { 40124 arg.* = .{ .register_pair = param_gpr[0..2].* }; 40125 param_gpr = param_gpr[2..]; 40126 continue; 40127 }, 40128 .segment, .mmx, .ip, .cr, .dr => unreachable, 40129 .x87 => if (param_x87.len >= 1 and param_size <= 16) { 40130 arg.* = .{ .register = param_x87[0] }; 40131 param_x87 = param_x87[1..]; 40132 continue; 40133 }, 40134 .sse => if (param_sse.len >= 1 and param_size <= self.vectorSize(.float)) { 40135 arg.* = .{ 40136 .register = registerAlias(param_sse[0], @max(param_size, 16)), 40137 }; 40138 param_sse = param_sse[1..]; 40139 continue; 40140 }, 40141 }; 40142 const param_align = param_ty.abiAlignment(zcu); 40143 result.stack_byte_count = @intCast(param_align.forward(result.stack_byte_count)); 40144 result.stack_align = result.stack_align.max(param_align); 40145 arg.* = .{ .load_frame = .{ 40146 .index = stack_frame_base, 40147 .off = result.stack_byte_count, 40148 } }; 40149 result.stack_byte_count += param_size; 40150 } 40151 }, 40152 else => return self.fail("TODO implement function parameters and return values for {} on x86_64", .{cc}), 40153 } 40154 40155 result.stack_byte_count = @intCast(result.stack_align.forward(result.stack_byte_count)); 40156 return result; 40157 } 40158 40159 fn fail(self: *CodeGen, comptime format: []const u8, args: anytype) error{ OutOfMemory, CodegenFail } { 40160 @branchHint(.cold); 40161 const zcu = self.pt.zcu; 40162 switch (self.owner) { 40163 .nav_index => |i| return zcu.codegenFail(i, format, args), 40164 .lazy_sym => |s| return zcu.codegenFailType(s.ty, format, args), 40165 } 40166 return error.CodegenFail; 40167 } 40168 40169 fn failMsg(self: *CodeGen, msg: *Zcu.ErrorMsg) error{ OutOfMemory, CodegenFail } { 40170 @branchHint(.cold); 40171 const zcu = self.pt.zcu; 40172 switch (self.owner) { 40173 .nav_index => |i| return zcu.codegenFailMsg(i, msg), 40174 .lazy_sym => |s| return zcu.codegenFailTypeMsg(s.ty, msg), 40175 } 40176 return error.CodegenFail; 40177 } 40178 40179 fn parseRegName(name: []const u8) ?Register { 40180 if (std.mem.startsWith(u8, name, "db")) return @enumFromInt( 40181 @intFromEnum(Register.dr0) + (std.fmt.parseInt(u4, name["db".len..], 0) catch return null), 40182 ); 40183 return std.meta.stringToEnum(Register, name); 40184 } 40185 40186 /// Returns register wide enough to hold at least `size_bytes`. 40187 fn registerAlias(reg: Register, size_bytes: u32) Register { 40188 return switch (reg.class()) { 40189 .general_purpose => if (size_bytes == 0) 40190 unreachable // should be comptime-known 40191 else if (size_bytes <= 1) 40192 reg.to8() 40193 else if (size_bytes <= 2) 40194 reg.to16() 40195 else if (size_bytes <= 4) 40196 reg.to32() 40197 else if (size_bytes <= 8) 40198 reg.to64() 40199 else 40200 unreachable, 40201 .segment => if (size_bytes <= 2) 40202 reg 40203 else 40204 unreachable, 40205 .x87 => if (size_bytes >= 10 and size_bytes <= 16) 40206 reg 40207 else 40208 unreachable, 40209 .mmx => if (size_bytes <= 8) 40210 reg 40211 else 40212 unreachable, 40213 .sse => if (size_bytes <= 16) 40214 reg.to128() 40215 else if (size_bytes <= 32) 40216 reg.to256() 40217 else 40218 unreachable, 40219 .ip => if (size_bytes <= 2) 40220 .ip 40221 else if (size_bytes <= 4) 40222 .eip 40223 else if (size_bytes <= 8) 40224 .rip 40225 else 40226 unreachable, 40227 .cr => if (size_bytes <= 8) 40228 reg 40229 else 40230 unreachable, 40231 .dr => if (size_bytes <= 8) 40232 reg 40233 else 40234 unreachable, 40235 }; 40236 } 40237 40238 fn memSize(self: *CodeGen, ty: Type) Memory.Size { 40239 const zcu = self.pt.zcu; 40240 return if (self.floatBits(ty)) |float_bits| 40241 .fromBitSize(float_bits) 40242 else if (ty.isVector(zcu) and ty.vectorLen(zcu) == 1 and self.floatBits(ty.childType(zcu)) == 80) 40243 .tbyte 40244 else 40245 .fromSize(@intCast(ty.abiSize(zcu))); 40246 } 40247 40248 fn splitType(self: *CodeGen, comptime parts_len: usize, ty: Type) ![parts_len]Type { 40249 const pt = self.pt; 40250 const zcu = pt.zcu; 40251 var parts: [parts_len]Type = undefined; 40252 if (ty.isVector(zcu)) if (std.math.divExact(u32, ty.vectorLen(zcu), parts_len)) |vec_len| return .{ 40253 try pt.vectorType(.{ .len = vec_len, .child = ty.scalarType(zcu).toIntern() }), 40254 } ** parts_len else |err| switch (err) { 40255 error.DivisionByZero => unreachable, 40256 error.UnexpectedRemainder => {}, 40257 }; 40258 const classes = std.mem.sliceTo(&abi.classifySystemV(ty, zcu, self.target.*, .other), .none); 40259 if (classes.len == parts_len) for (&parts, classes, 0..) |*part, class, part_i| { 40260 part.* = switch (class) { 40261 .integer => if (part_i < parts_len - 1) 40262 .u64 40263 else part: { 40264 const elem_size = ty.abiAlignment(zcu).minStrict(.@"8").toByteUnits().?; 40265 const elem_ty = try pt.intType(.unsigned, @intCast(elem_size * 8)); 40266 break :part switch (@divExact(ty.abiSize(zcu) - part_i * 8, elem_size)) { 40267 1 => elem_ty, 40268 else => |array_len| try pt.arrayType(.{ .len = array_len, .child = elem_ty.toIntern() }), 40269 }; 40270 }, 40271 .float => .f32, 40272 .float_combine => try pt.arrayType(.{ .len = 2, .child = .f32_type }), 40273 .sse => .f64, 40274 else => break, 40275 }; 40276 } else { 40277 var part_sizes: u64 = 0; 40278 for (parts) |part| part_sizes += part.abiSize(zcu); 40279 if (part_sizes == ty.abiSize(zcu)) return parts; 40280 }; 40281 return self.fail("TODO implement splitType({d}, {})", .{ parts_len, ty.fmt(pt) }); 40282 } 40283 40284 /// Truncates the value in the register in place. 40285 /// Clobbers any remaining bits. 40286 fn truncateRegister(self: *CodeGen, ty: Type, reg: Register) !void { 40287 const pt = self.pt; 40288 const zcu = pt.zcu; 40289 const int_info: InternPool.Key.IntType = if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else .{ 40290 .signedness = .unsigned, 40291 .bits = @intCast(ty.bitSize(zcu)), 40292 }; 40293 const shift = std.math.cast(u6, 64 - int_info.bits % 64) orelse return; 40294 try self.spillEflagsIfOccupied(); 40295 switch (int_info.signedness) { 40296 .signed => { 40297 try self.genShiftBinOpMir(.{ ._l, .sa }, .isize, .{ .register = reg }, .u8, .{ .immediate = shift }); 40298 try self.genShiftBinOpMir(.{ ._r, .sa }, .isize, .{ .register = reg }, .u8, .{ .immediate = shift }); 40299 }, 40300 .unsigned => { 40301 const mask = ~@as(u64, 0) >> shift; 40302 if (int_info.bits <= 32) { 40303 try self.genBinOpMir(.{ ._, .@"and" }, .u32, .{ .register = reg }, .{ .immediate = mask }); 40304 } else { 40305 const tmp_reg = try self.copyToTmpRegister(.usize, .{ .immediate = mask }); 40306 try self.genBinOpMir(.{ ._, .@"and" }, .usize, .{ .register = reg }, .{ .register = tmp_reg }); 40307 } 40308 }, 40309 } 40310 } 40311 40312 fn regBitSize(self: *CodeGen, ty: Type) u64 { 40313 const zcu = self.pt.zcu; 40314 const abi_size = ty.abiSize(zcu); 40315 return switch (ty.zigTypeTag(zcu)) { 40316 else => switch (abi_size) { 40317 1 => 8, 40318 2 => 16, 40319 3...4 => 32, 40320 5...8 => 64, 40321 else => unreachable, 40322 }, 40323 .float => switch (abi_size) { 40324 1...16 => 128, 40325 17...32 => 256, 40326 else => unreachable, 40327 }, 40328 }; 40329 } 40330 40331 fn regExtraBits(self: *CodeGen, ty: Type) u64 { 40332 return self.regBitSize(ty) - ty.bitSize(self.pt.zcu); 40333 } 40334 40335 fn hasFeature(cg: *CodeGen, feature: std.Target.x86.Feature) bool { 40336 return switch (feature) { 40337 .@"64bit" => switch (cg.target.cpu.arch) { 40338 else => unreachable, 40339 .x86 => false, 40340 .x86_64 => true, 40341 }, 40342 .false_deps_getmant, 40343 .false_deps_lzcnt_tzcnt, 40344 .false_deps_mulc, 40345 .false_deps_mullq, 40346 .false_deps_perm, 40347 .false_deps_popcnt, 40348 .false_deps_range, 40349 .slow_3ops_lea, 40350 .slow_incdec, 40351 .slow_lea, 40352 .slow_pmaddwd, 40353 .slow_pmulld, 40354 .slow_shld, 40355 .slow_two_mem_ops, 40356 .slow_unaligned_mem_16, 40357 .slow_unaligned_mem_32, 40358 => switch (cg.mod.optimize_mode) { 40359 .Debug, .ReleaseSafe, .ReleaseFast => null, 40360 .ReleaseSmall => false, 40361 }, 40362 .fast_11bytenop, 40363 .fast_15bytenop, 40364 .fast_7bytenop, 40365 .fast_bextr, 40366 .fast_dpwssd, 40367 .fast_gather, 40368 .fast_hops, 40369 .fast_imm16, 40370 .fast_lzcnt, 40371 .fast_movbe, 40372 .fast_scalar_fsqrt, 40373 .fast_scalar_shift_masks, 40374 .fast_shld_rotate, 40375 .fast_variable_crosslane_shuffle, 40376 .fast_variable_perlane_shuffle, 40377 .fast_vector_fsqrt, 40378 .fast_vector_shift_masks, 40379 => switch (cg.mod.optimize_mode) { 40380 .Debug, .ReleaseSafe, .ReleaseFast => null, 40381 .ReleaseSmall => true, 40382 }, 40383 .mmx => false, 40384 .sahf => switch (cg.target.cpu.arch) { 40385 else => unreachable, 40386 .x86 => true, 40387 .x86_64 => null, 40388 }, 40389 else => null, 40390 } orelse std.Target.x86.featureSetHas(cg.target.cpu.features, feature); 40391 } 40392 40393 fn typeOf(self: *CodeGen, inst: Air.Inst.Ref) Type { 40394 const pt = self.pt; 40395 const zcu = pt.zcu; 40396 return self.air.typeOf(inst, &zcu.intern_pool); 40397 } 40398 40399 fn typeOfIndex(self: *CodeGen, inst: Air.Inst.Index) Type { 40400 return Temp.typeOf(.{ .index = inst }, self); 40401 } 40402 40403 fn intCompilerRtAbiName(int_bits: u32) u8 { 40404 return switch (int_bits) { 40405 1...32 => 's', 40406 33...64 => 'd', 40407 65...128 => 't', 40408 else => unreachable, 40409 }; 40410 } 40411 40412 fn floatCompilerRtAbiName(float_bits: u32) u8 { 40413 return switch (float_bits) { 40414 16 => 'h', 40415 32 => 's', 40416 64 => 'd', 40417 80 => 'x', 40418 128 => 't', 40419 else => unreachable, 40420 }; 40421 } 40422 40423 fn floatCompilerRtAbiType(self: *CodeGen, ty: Type, other_ty: Type) Type { 40424 if (ty.toIntern() == .f16_type and 40425 (other_ty.toIntern() == .f32_type or other_ty.toIntern() == .f64_type) and 40426 self.target.isDarwin()) return .u16; 40427 return ty; 40428 } 40429 40430 fn floatLibcAbiPrefix(ty: Type) []const u8 { 40431 return switch (ty.toIntern()) { 40432 .f16_type, .f80_type => "__", 40433 .f32_type, .f64_type, .f128_type, .c_longdouble_type => "", 40434 else => unreachable, 40435 }; 40436 } 40437 40438 fn floatLibcAbiSuffix(ty: Type) []const u8 { 40439 return switch (ty.toIntern()) { 40440 .f16_type => "h", 40441 .f32_type => "f", 40442 .f64_type => "", 40443 .f80_type => "x", 40444 .f128_type => "q", 40445 .c_longdouble_type => "l", 40446 else => unreachable, 40447 }; 40448 } 40449 40450 fn promoteInt(self: *CodeGen, ty: Type) Type { 40451 const pt = self.pt; 40452 const zcu = pt.zcu; 40453 const int_info: InternPool.Key.IntType = switch (ty.toIntern()) { 40454 .bool_type => .{ .signedness = .unsigned, .bits = 1 }, 40455 else => if (ty.isAbiInt(zcu)) ty.intInfo(zcu) else return ty, 40456 }; 40457 for ([_]Type{ 40458 .c_int, .c_uint, 40459 .c_long, .c_ulong, 40460 .c_longlong, .c_ulonglong, 40461 }) |promote_ty| { 40462 const promote_info = promote_ty.intInfo(zcu); 40463 if (int_info.signedness == .signed and promote_info.signedness == .unsigned) continue; 40464 if (int_info.bits + @intFromBool(int_info.signedness == .unsigned and 40465 promote_info.signedness == .signed) <= promote_info.bits) return promote_ty; 40466 } 40467 return ty; 40468 } 40469 40470 fn promoteVarArg(self: *CodeGen, ty: Type) Type { 40471 if (!ty.isRuntimeFloat()) return self.promoteInt(ty); 40472 switch (ty.floatBits(self.target.*)) { 40473 32, 64 => return .f64, 40474 else => |float_bits| { 40475 assert(float_bits == self.target.cTypeBitSize(.longdouble)); 40476 return .c_longdouble; 40477 }, 40478 } 40479 } 40480 40481 fn unalignedSize(cg: *CodeGen, ty: Type) u64 { 40482 const zcu = cg.pt.zcu; 40483 return switch (zcu.intern_pool.indexToKey(ty.toIntern())) { 40484 .vector_type => |vector_type| Type.fromInterned(vector_type.child).abiSize(zcu) * vector_type.len, 40485 else => ty.abiSize(zcu), 40486 }; 40487 } 40488 40489 fn intInfo(cg: *CodeGen, ty: Type) ?std.builtin.Type.Int { 40490 const zcu = cg.pt.zcu; 40491 const ip = &zcu.intern_pool; 40492 var ty_index = ty.ip_index; 40493 while (true) switch (ip.indexToKey(ty_index)) { 40494 .int_type => |int_type| return int_type, 40495 .ptr_type => |ptr_type| return switch (ptr_type.flags.size) { 40496 .one, .many, .c => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, 40497 .slice => null, 40498 }, 40499 .opt_type => |opt_child| return if (!Type.fromInterned(opt_child).hasRuntimeBitsIgnoreComptime(zcu)) 40500 .{ .signedness = .unsigned, .bits = 1 } 40501 else switch (ip.indexToKey(opt_child)) { 40502 .ptr_type => |ptr_type| switch (ptr_type.flags.size) { 40503 .one, .many => switch (ptr_type.flags.is_allowzero) { 40504 false => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, 40505 true => null, 40506 }, 40507 .slice, .c => null, 40508 }, 40509 else => null, 40510 }, 40511 .error_union_type => |error_union_type| return if (!Type.fromInterned(error_union_type.payload_type) 40512 .hasRuntimeBitsIgnoreComptime(zcu)) .{ .signedness = .unsigned, .bits = zcu.errorSetBits() } else null, 40513 .simple_type => |simple_type| return switch (simple_type) { 40514 .bool => .{ .signedness = .unsigned, .bits = 1 }, 40515 .anyerror => .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, 40516 .isize => .{ .signedness = .signed, .bits = cg.target.ptrBitWidth() }, 40517 .usize => .{ .signedness = .unsigned, .bits = cg.target.ptrBitWidth() }, 40518 .c_char => .{ .signedness = cg.target.charSignedness(), .bits = cg.target.cTypeBitSize(.char) }, 40519 .c_short => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.short) }, 40520 .c_ushort => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.short) }, 40521 .c_int => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.int) }, 40522 .c_uint => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.int) }, 40523 .c_long => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.long) }, 40524 .c_ulong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.long) }, 40525 .c_longlong => .{ .signedness = .signed, .bits = cg.target.cTypeBitSize(.longlong) }, 40526 .c_ulonglong => .{ .signedness = .unsigned, .bits = cg.target.cTypeBitSize(.longlong) }, 40527 .f16, .f32, .f64, .f80, .f128, .c_longdouble => null, 40528 .anyopaque, 40529 .void, 40530 .type, 40531 .comptime_int, 40532 .comptime_float, 40533 .noreturn, 40534 .null, 40535 .undefined, 40536 .enum_literal, 40537 .adhoc_inferred_error_set, 40538 .generic_poison, 40539 => unreachable, 40540 }, 40541 .struct_type => { 40542 const loaded_struct = ip.loadStructType(ty_index); 40543 switch (loaded_struct.layout) { 40544 .auto, .@"extern" => return null, 40545 .@"packed" => ty_index = loaded_struct.backingIntTypeUnordered(ip), 40546 } 40547 }, 40548 .union_type => return switch (ip.loadUnionType(ty_index).flagsUnordered(ip).layout) { 40549 .auto, .@"extern" => null, 40550 .@"packed" => .{ .signedness = .unsigned, .bits = @intCast(ty.bitSize(zcu)) }, 40551 }, 40552 .enum_type => ty_index = ip.loadEnumType(ty_index).tag_ty, 40553 .error_set_type, .inferred_error_set_type => return .{ .signedness = .unsigned, .bits = zcu.errorSetBits() }, 40554 else => return null, 40555 }; 40556 } 40557 40558 fn floatBits(cg: *CodeGen, ty: Type) ?u16 { 40559 return if (ty.isRuntimeFloat()) ty.floatBits(cg.target.*) else null; 40560 } 40561 40562 const Temp = struct { 40563 index: Air.Inst.Index, 40564 40565 fn unwrap(temp: Temp, cg: *CodeGen) union(enum) { 40566 ref: Air.Inst.Ref, 40567 temp: Index, 40568 err_ret_trace, 40569 } { 40570 switch (temp.index.unwrap()) { 40571 .ref => |ref| return .{ .ref = ref }, 40572 .target => |target_index| { 40573 if (temp.index == err_ret_trace_index) return .err_ret_trace; 40574 const temp_index: Index = @enumFromInt(target_index); 40575 assert(temp_index.isValid(cg)); 40576 return .{ .temp = temp_index }; 40577 }, 40578 } 40579 } 40580 40581 fn typeOf(temp: Temp, cg: *CodeGen) Type { 40582 return switch (temp.unwrap(cg)) { 40583 .ref => switch (cg.air.instructions.items(.tag)[@intFromEnum(temp.index)]) { 40584 .loop_switch_br => cg.typeOf(cg.air.unwrapSwitch(temp.index).operand), 40585 else => cg.air.typeOfIndex(temp.index, &cg.pt.zcu.intern_pool), 40586 }, 40587 .temp => |temp_index| temp_index.typeOf(cg), 40588 .err_ret_trace => .usize, 40589 }; 40590 } 40591 40592 fn isMut(temp: Temp, cg: *CodeGen) bool { 40593 return switch (temp.unwrap(cg)) { 40594 .ref, .err_ret_trace => false, 40595 .temp => |temp_index| switch (temp_index.tracking(cg).short) { 40596 .none, 40597 .unreach, 40598 .dead, 40599 .undef, 40600 .immediate, 40601 .eflags, 40602 .register_offset, 40603 .register_mask, 40604 .memory, 40605 .load_symbol, 40606 .lea_symbol, 40607 .indirect, 40608 .load_direct, 40609 .lea_direct, 40610 .load_got, 40611 .lea_got, 40612 .load_tlv, 40613 .lea_tlv, 40614 .lea_frame, 40615 .elementwise_regs_then_frame, 40616 .reserved_frame, 40617 .air_ref, 40618 => false, 40619 .register, 40620 .register_pair, 40621 .register_triple, 40622 .register_quadruple, 40623 .register_overflow, 40624 => true, 40625 .load_frame => |frame_addr| !frame_addr.index.isNamed(), 40626 }, 40627 }; 40628 } 40629 40630 fn tracking(temp: Temp, cg: *CodeGen) InstTracking { 40631 return cg.inst_tracking.get(temp.index).?; 40632 } 40633 40634 fn getOffset(temp: Temp, off: i32, cg: *CodeGen) !Temp { 40635 const new_temp_index = cg.next_temp_index; 40636 cg.temp_type[@intFromEnum(new_temp_index)] = .usize; 40637 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 40638 switch (temp.tracking(cg).short) { 40639 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 40640 .register => |reg| { 40641 const new_reg = 40642 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40643 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40644 try cg.asmRegisterMemory(.{ ._, .lea }, new_reg.to64(), .{ 40645 .base = .{ .reg = reg.to64() }, 40646 .mod = .{ .rm = .{ 40647 .size = .qword, 40648 .disp = off, 40649 } }, 40650 }); 40651 }, 40652 .register_offset => |reg_off| { 40653 const new_reg = 40654 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40655 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40656 try cg.asmRegisterMemory(.{ ._, .lea }, new_reg.to64(), .{ 40657 .base = .{ .reg = reg_off.reg.to64() }, 40658 .mod = .{ .rm = .{ 40659 .size = .qword, 40660 .disp = reg_off.off + off, 40661 } }, 40662 }); 40663 }, 40664 .lea_symbol => |sym_off| new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = .{ 40665 .sym_index = sym_off.sym_index, 40666 .off = sym_off.off + off, 40667 } }), 40668 .load_frame => |frame_addr| { 40669 const new_reg = 40670 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40671 new_temp_index.tracking(cg).* = .init(.{ .register_offset = .{ 40672 .reg = new_reg, 40673 .off = off, 40674 } }); 40675 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ 40676 .base = .{ .frame = frame_addr.index }, 40677 .mod = .{ .rm = .{ 40678 .size = .qword, 40679 .disp = frame_addr.off, 40680 } }, 40681 }); 40682 }, 40683 .lea_frame => |frame_addr| new_temp_index.tracking(cg).* = .init(.{ .lea_frame = .{ 40684 .index = frame_addr.index, 40685 .off = frame_addr.off + off, 40686 } }), 40687 } 40688 return .{ .index = new_temp_index.toIndex() }; 40689 } 40690 40691 fn toOffset(temp: *Temp, off: i32, cg: *CodeGen) !void { 40692 if (off == 0) return; 40693 switch (temp.unwrap(cg)) { 40694 .ref, .err_ret_trace => {}, 40695 .temp => |temp_index| { 40696 const temp_tracking = temp_index.tracking(cg); 40697 switch (temp_tracking.short) { 40698 else => {}, 40699 .register => |reg| { 40700 try cg.freeValue(temp_tracking.long); 40701 temp_tracking.* = .init(.{ .register_offset = .{ 40702 .reg = reg, 40703 .off = off, 40704 } }); 40705 return; 40706 }, 40707 .register_offset => |reg_off| { 40708 try cg.freeValue(temp_tracking.long); 40709 temp_tracking.* = .init(.{ .register_offset = .{ 40710 .reg = reg_off.reg, 40711 .off = reg_off.off + off, 40712 } }); 40713 return; 40714 }, 40715 .lea_symbol => |sym_off| { 40716 assert(std.meta.eql(temp_tracking.long.lea_symbol, sym_off)); 40717 temp_tracking.* = .init(.{ .lea_symbol = .{ 40718 .sym_index = sym_off.sym_index, 40719 .off = sym_off.off + off, 40720 } }); 40721 return; 40722 }, 40723 .lea_frame => |frame_addr| { 40724 assert(std.meta.eql(temp_tracking.long.lea_frame, frame_addr)); 40725 temp_tracking.* = .init(.{ .lea_frame = .{ 40726 .index = frame_addr.index, 40727 .off = frame_addr.off + off, 40728 } }); 40729 return; 40730 }, 40731 } 40732 }, 40733 } 40734 const new_temp = try temp.getOffset(off, cg); 40735 try temp.die(cg); 40736 temp.* = new_temp; 40737 } 40738 40739 fn getLimb(temp: Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) !Temp { 40740 const new_temp_index = cg.next_temp_index; 40741 cg.temp_type[@intFromEnum(new_temp_index)] = limb_ty; 40742 switch (temp.tracking(cg).short) { 40743 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 40744 .immediate => |imm| { 40745 assert(limb_index == 0); 40746 new_temp_index.tracking(cg).* = .init(.{ .immediate = imm }); 40747 }, 40748 .register => |reg| { 40749 assert(limb_index == 0); 40750 const new_reg = 40751 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40752 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40753 try cg.asmRegisterRegister(.{ ._, .mov }, new_reg.to64(), reg.to64()); 40754 }, 40755 .register_pair => |regs| { 40756 const new_reg = 40757 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40758 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40759 try cg.asmRegisterRegister(.{ ._, .mov }, new_reg.to64(), regs[limb_index].to64()); 40760 }, 40761 .register_offset => |reg_off| { 40762 assert(limb_index == 0); 40763 const new_reg = 40764 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40765 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40766 try cg.asmRegisterMemory(.{ ._, .lea }, new_reg.to64(), .{ 40767 .base = .{ .reg = reg_off.reg.to64() }, 40768 .mod = .{ .rm = .{ 40769 .size = .qword, 40770 .disp = reg_off.off + @as(u31, limb_index) * 8, 40771 } }, 40772 }); 40773 }, 40774 .load_symbol => |sym_off| { 40775 const new_reg = 40776 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40777 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40778 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ 40779 .base = .{ .reloc = sym_off.sym_index }, 40780 .mod = .{ .rm = .{ 40781 .size = .qword, 40782 .disp = sym_off.off + @as(u31, limb_index) * 8, 40783 } }, 40784 }); 40785 }, 40786 .lea_symbol => |sym_off| { 40787 assert(limb_index == 0); 40788 new_temp_index.tracking(cg).* = .init(.{ .lea_symbol = sym_off }); 40789 }, 40790 .load_frame => |frame_addr| { 40791 const new_reg = 40792 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 40793 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40794 try cg.asmRegisterMemory(.{ ._, .mov }, new_reg.to64(), .{ 40795 .base = .{ .frame = frame_addr.index }, 40796 .mod = .{ .rm = .{ 40797 .size = .qword, 40798 .disp = frame_addr.off + @as(u31, limb_index) * 8, 40799 } }, 40800 }); 40801 }, 40802 .lea_frame => |frame_addr| { 40803 assert(limb_index == 0); 40804 new_temp_index.tracking(cg).* = .init(.{ .lea_frame = frame_addr }); 40805 }, 40806 } 40807 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 40808 return .{ .index = new_temp_index.toIndex() }; 40809 } 40810 40811 fn toLimb(temp: *Temp, limb_ty: Type, limb_index: u28, cg: *CodeGen) !void { 40812 switch (temp.unwrap(cg)) { 40813 .ref => {}, 40814 .temp => |temp_index| { 40815 const temp_tracking = temp_index.tracking(cg); 40816 switch (temp_tracking.short) { 40817 else => {}, 40818 .register, .lea_symbol, .lea_frame => { 40819 assert(limb_index == 0); 40820 cg.temp_type[@intFromEnum(temp_index)] = limb_ty; 40821 return; 40822 }, 40823 .register_pair => |regs| { 40824 switch (temp_tracking.long) { 40825 .none, .reserved_frame => {}, 40826 else => temp_tracking.long = 40827 temp_tracking.long.address().offset(@as(u31, limb_index) * 8).deref(), 40828 } 40829 for (regs, 0..) |reg, reg_index| if (reg_index != limb_index) 40830 cg.register_manager.freeReg(reg); 40831 temp_tracking.* = .init(.{ .register = regs[limb_index] }); 40832 cg.temp_type[@intFromEnum(temp_index)] = limb_ty; 40833 return; 40834 }, 40835 .load_symbol => |sym_off| { 40836 assert(std.meta.eql(temp_tracking.long.load_symbol, sym_off)); 40837 temp_tracking.* = .init(.{ .load_symbol = .{ 40838 .sym_index = sym_off.sym_index, 40839 .off = sym_off.off + @as(u31, limb_index) * 8, 40840 } }); 40841 cg.temp_type[@intFromEnum(temp_index)] = limb_ty; 40842 return; 40843 }, 40844 .load_frame => |frame_addr| if (!frame_addr.index.isNamed()) { 40845 assert(std.meta.eql(temp_tracking.long.load_frame, frame_addr)); 40846 temp_tracking.* = .init(.{ .load_frame = .{ 40847 .index = frame_addr.index, 40848 .off = frame_addr.off + @as(u31, limb_index) * 8, 40849 } }); 40850 cg.temp_type[@intFromEnum(temp_index)] = limb_ty; 40851 return; 40852 }, 40853 } 40854 }, 40855 .err_ret_trace => unreachable, 40856 } 40857 const new_temp = try temp.getLimb(limb_ty, limb_index, cg); 40858 try temp.die(cg); 40859 temp.* = new_temp; 40860 } 40861 40862 fn toSlicePtr(temp: *Temp, cg: *CodeGen) !void { 40863 const temp_ty = temp.typeOf(cg); 40864 if (temp_ty.isSlice(cg.pt.zcu)) try temp.toLimb(temp_ty.slicePtrFieldType(cg.pt.zcu), 0, cg); 40865 } 40866 40867 fn toSliceLen(temp: *Temp, cg: *CodeGen) !void { 40868 try temp.toLimb(.usize, 1, cg); 40869 } 40870 40871 fn toReg(temp: *Temp, new_reg: Register, cg: *CodeGen) !bool { 40872 const val, const ty: Type = val_ty: switch (temp.unwrap(cg)) { 40873 .ref => |ref| .{ temp.tracking(cg).short, cg.typeOf(ref) }, 40874 .temp => |temp_index| { 40875 const temp_tracking = temp_index.tracking(cg); 40876 if (temp_tracking.short == .register and 40877 temp_tracking.short.register == new_reg) return false; 40878 break :val_ty .{ temp_tracking.short, temp_index.typeOf(cg) }; 40879 }, 40880 .err_ret_trace => .{ temp.tracking(cg).short, .usize }, 40881 }; 40882 const new_temp_index = cg.next_temp_index; 40883 try cg.register_manager.getReg(new_reg, new_temp_index.toIndex()); 40884 cg.temp_type[@intFromEnum(new_temp_index)] = ty; 40885 try cg.genSetReg(new_reg, ty, val, .{}); 40886 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40887 try temp.die(cg); 40888 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 40889 temp.* = .{ .index = new_temp_index.toIndex() }; 40890 return true; 40891 } 40892 40893 fn toRegClass(temp: *Temp, mut: bool, rc: Register.Class, cg: *CodeGen) !bool { 40894 const val = temp.tracking(cg).short; 40895 if (!mut or temp.isMut(cg)) switch (val) { 40896 else => {}, 40897 .register => |reg| if (reg.class() == rc) return false, 40898 .register_offset => |reg_off| if (reg_off.reg.class() == rc and reg_off.off == 0) return false, 40899 }; 40900 const ty = temp.typeOf(cg); 40901 const new_temp_index = cg.next_temp_index; 40902 cg.temp_type[@intFromEnum(new_temp_index)] = ty; 40903 const new_reg = try cg.register_manager.allocReg(new_temp_index.toIndex(), regSetForRegClass(rc)); 40904 try cg.genSetReg(new_reg, ty, val, .{}); 40905 new_temp_index.tracking(cg).* = .init(.{ .register = new_reg }); 40906 try temp.die(cg); 40907 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 40908 temp.* = .{ .index = new_temp_index.toIndex() }; 40909 return true; 40910 } 40911 40912 fn toPair(first_temp: *Temp, second_temp: *Temp, cg: *CodeGen) !void { 40913 while (true) for ([_]*Temp{ first_temp, second_temp }) |part_temp| { 40914 if (try part_temp.toRegClass(true, .general_purpose, cg)) break; 40915 } else break; 40916 const first_temp_tracking = first_temp.unwrap(cg).temp.tracking(cg); 40917 const second_temp_tracking = second_temp.unwrap(cg).temp.tracking(cg); 40918 const result: MCValue = .{ .register_pair = .{ 40919 first_temp_tracking.short.register, 40920 second_temp_tracking.short.register, 40921 } }; 40922 const result_temp_index = cg.next_temp_index; 40923 const result_temp: Temp = .{ .index = result_temp_index.toIndex() }; 40924 assert(cg.reuseTemp(result_temp.index, first_temp.index, first_temp_tracking)); 40925 assert(cg.reuseTemp(result_temp.index, second_temp.index, second_temp_tracking)); 40926 cg.temp_type[@intFromEnum(result_temp_index)] = .slice_const_u8; 40927 result_temp_index.tracking(cg).* = .init(result); 40928 first_temp.* = result_temp; 40929 second_temp.* = result_temp; 40930 } 40931 40932 fn asMask(temp: Temp, info: MaskInfo, cg: *CodeGen) void { 40933 assert(info.scalar != .none); 40934 const mcv = &temp.unwrap(cg).temp.tracking(cg).short; 40935 const reg = mcv.register; 40936 mcv.* = .{ .register_mask = .{ .reg = reg, .info = info } }; 40937 } 40938 40939 fn toLea(temp: *Temp, cg: *CodeGen) !bool { 40940 switch (temp.tracking(cg).short) { 40941 .none, 40942 .unreach, 40943 .dead, 40944 .undef, 40945 .eflags, 40946 .register_pair, 40947 .register_triple, 40948 .register_quadruple, 40949 .register_overflow, 40950 .register_mask, 40951 .elementwise_regs_then_frame, 40952 .reserved_frame, 40953 .air_ref, 40954 => unreachable, // not a valid pointer 40955 .immediate, 40956 .register, 40957 .register_offset, 40958 .lea_direct, 40959 .lea_got, 40960 .lea_tlv, 40961 .lea_frame, 40962 => return false, 40963 .memory, 40964 .indirect, 40965 .load_symbol, 40966 .load_direct, 40967 .load_got, 40968 .load_tlv, 40969 .load_frame, 40970 => return temp.toRegClass(true, .general_purpose, cg), 40971 .lea_symbol => |sym_off| { 40972 const off = sym_off.off; 40973 if (off == 0) return false; 40974 try temp.toOffset(-off, cg); 40975 while (try temp.toRegClass(true, .general_purpose, cg)) {} 40976 try temp.toOffset(off, cg); 40977 return true; 40978 }, 40979 } 40980 } 40981 40982 fn toMemory(temp: *Temp, cg: *CodeGen) !bool { 40983 const temp_tracking = temp.tracking(cg); 40984 if (temp_tracking.short.isMemory()) return false; 40985 const new_temp_index = cg.next_temp_index; 40986 const ty = temp.typeOf(cg); 40987 cg.temp_type[@intFromEnum(new_temp_index)] = ty; 40988 const new_frame_index = try cg.allocFrameIndex(.initSpill(ty, cg.pt.zcu)); 40989 try cg.genSetMem(.{ .frame = new_frame_index }, 0, ty, temp_tracking.short, .{}); 40990 new_temp_index.tracking(cg).* = .init(.{ .load_frame = .{ .index = new_frame_index } }); 40991 try temp.die(cg); 40992 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 40993 temp.* = .{ .index = new_temp_index.toIndex() }; 40994 return true; 40995 } 40996 40997 // hack around linker relocation bugs 40998 fn toBase(temp: *Temp, cg: *CodeGen) !bool { 40999 const temp_tracking = temp.tracking(cg); 41000 if (temp_tracking.short.isBase()) return false; 41001 if (try temp.toMemory(cg)) return true; 41002 const new_temp_index = cg.next_temp_index; 41003 cg.temp_type[@intFromEnum(new_temp_index)] = temp.typeOf(cg); 41004 const new_reg = 41005 try cg.register_manager.allocReg(new_temp_index.toIndex(), abi.RegisterClass.gp); 41006 try cg.genSetReg(new_reg, .usize, temp_tracking.short.address(), .{}); 41007 new_temp_index.tracking(cg).* = .init(.{ .indirect = .{ .reg = new_reg } }); 41008 try temp.die(cg); 41009 cg.next_temp_index = @enumFromInt(@intFromEnum(new_temp_index) + 1); 41010 temp.* = .{ .index = new_temp_index.toIndex() }; 41011 return true; 41012 } 41013 41014 const AccessOptions = struct { 41015 disp: i32 = 0, 41016 safe: bool = false, 41017 }; 41018 41019 fn load(ptr: *Temp, val_ty: Type, opts: AccessOptions, cg: *CodeGen) !Temp { 41020 const val = try cg.tempAlloc(val_ty); 41021 try ptr.toOffset(opts.disp, cg); 41022 while (try ptr.toLea(cg)) {} 41023 const val_mcv = val.tracking(cg).short; 41024 switch (val_mcv) { 41025 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 41026 .register => |val_reg| try ptr.loadReg(val_ty, registerAlias( 41027 val_reg, 41028 @intCast(val_ty.abiSize(cg.pt.zcu)), 41029 ), cg), 41030 inline .register_pair, 41031 .register_triple, 41032 .register_quadruple, 41033 => |val_regs| for (val_regs) |val_reg| { 41034 try ptr.loadReg(val_ty, val_reg, cg); 41035 try ptr.toOffset(@divExact(val_reg.bitSize(), 8), cg); 41036 while (try ptr.toLea(cg)) {} 41037 }, 41038 .register_offset => |val_reg_off| switch (val_reg_off.off) { 41039 0 => try ptr.loadReg(val_ty, registerAlias( 41040 val_reg_off.reg, 41041 @intCast(val_ty.abiSize(cg.pt.zcu)), 41042 ), cg), 41043 else => unreachable, 41044 }, 41045 .memory, .indirect, .load_frame, .load_symbol => { 41046 var val_ptr = try cg.tempInit(.usize, val_mcv.address()); 41047 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41048 try val_ptr.memcpy(ptr, &len, cg); 41049 try val_ptr.die(cg); 41050 try len.die(cg); 41051 }, 41052 } 41053 return val; 41054 } 41055 41056 fn store(ptr: *Temp, val: *Temp, opts: AccessOptions, cg: *CodeGen) !void { 41057 const val_ty = val.typeOf(cg); 41058 try ptr.toOffset(opts.disp, cg); 41059 while (try ptr.toLea(cg)) {} 41060 val_to_gpr: while (true) : (while (try ptr.toLea(cg) or 41061 try val.toRegClass(false, .general_purpose, cg)) 41062 {}) { 41063 const val_mcv = val.tracking(cg).short; 41064 switch (val_mcv) { 41065 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 41066 .undef => if (opts.safe) { 41067 var pat = try cg.tempInit(.u8, .{ .immediate = 0xaa }); 41068 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41069 try ptr.memset(&pat, &len, cg); 41070 try pat.die(cg); 41071 try len.die(cg); 41072 }, 41073 .immediate => |val_imm| { 41074 const val_op: Immediate = if (std.math.cast(u31, val_imm)) |val_uimm31| 41075 .u(val_uimm31) 41076 else if (std.math.cast(i32, @as(i64, @bitCast(val_imm)))) |val_simm32| 41077 .s(val_simm32) 41078 else 41079 continue :val_to_gpr; 41080 // hack around linker relocation bugs 41081 switch (ptr.tracking(cg).short) { 41082 else => {}, 41083 .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, 41084 } 41085 try cg.asmMemoryImmediate( 41086 .{ ._, .mov }, 41087 try ptr.tracking(cg).short.deref().mem(cg, .{ 41088 .size = cg.memSize(val_ty), 41089 }), 41090 val_op, 41091 ); 41092 }, 41093 .eflags => |cc| { 41094 // hack around linker relocation bugs 41095 switch (ptr.tracking(cg).short) { 41096 else => {}, 41097 .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, 41098 } 41099 try cg.asmSetccMemory( 41100 cc, 41101 try ptr.tracking(cg).short.deref().mem(cg, .{ .size = .byte }), 41102 ); 41103 }, 41104 .register => |val_reg| try ptr.storeRegs(val_ty, &.{registerAlias( 41105 val_reg, 41106 @intCast(val_ty.abiSize(cg.pt.zcu)), 41107 )}, cg), 41108 inline .register_pair, 41109 .register_triple, 41110 .register_quadruple, 41111 => |val_regs| try ptr.storeRegs(val_ty, &val_regs, cg), 41112 .register_offset => |val_reg_off| switch (val_reg_off.off) { 41113 0 => try ptr.storeRegs(val_ty, &.{registerAlias( 41114 val_reg_off.reg, 41115 @intCast(val_ty.abiSize(cg.pt.zcu)), 41116 )}, cg), 41117 else => continue :val_to_gpr, 41118 }, 41119 .register_overflow => |val_reg_ov| { 41120 const ip = &cg.pt.zcu.intern_pool; 41121 const first_ty: Type = .fromInterned(first_ty: switch (ip.indexToKey(val_ty.toIntern())) { 41122 .tuple_type => |tuple_type| { 41123 const tuple_field_types = tuple_type.types.get(ip); 41124 assert(tuple_field_types.len == 2 and tuple_field_types[1] == .u1_type); 41125 break :first_ty tuple_field_types[0]; 41126 }, 41127 .opt_type => |opt_child| { 41128 assert(!val_ty.optionalReprIsPayload(cg.pt.zcu)); 41129 break :first_ty opt_child; 41130 }, 41131 else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, val_ty.fmt(cg.pt) }), 41132 }); 41133 const first_size: u31 = @intCast(first_ty.abiSize(cg.pt.zcu)); 41134 try ptr.storeRegs(first_ty, &.{registerAlias(val_reg_ov.reg, first_size)}, cg); 41135 try ptr.toOffset(first_size, cg); 41136 try cg.asmSetccMemory( 41137 val_reg_ov.eflags, 41138 try ptr.tracking(cg).short.deref().mem(cg, .{ .size = .byte }), 41139 ); 41140 }, 41141 .lea_frame, .lea_symbol => continue :val_to_gpr, 41142 .memory, .indirect, .load_frame, .load_symbol => { 41143 var val_ptr = try cg.tempInit(.usize, val_mcv.address()); 41144 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41145 try ptr.memcpy(&val_ptr, &len, cg); 41146 try val_ptr.die(cg); 41147 try len.die(cg); 41148 }, 41149 } 41150 break; 41151 } 41152 } 41153 41154 fn read(src: *Temp, val_ty: Type, opts: AccessOptions, cg: *CodeGen) !Temp { 41155 var val = try cg.tempAlloc(val_ty); 41156 while (try src.toBase(cg)) {} 41157 const val_mcv = val.tracking(cg).short; 41158 switch (val_mcv) { 41159 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 41160 .register => |val_reg| try src.readReg(opts.disp, val_ty, registerAlias( 41161 val_reg, 41162 @intCast(val_ty.abiSize(cg.pt.zcu)), 41163 ), cg), 41164 inline .register_pair, .register_triple, .register_quadruple => |val_regs| { 41165 var disp = opts.disp; 41166 for (val_regs) |val_reg| { 41167 try src.readReg(disp, val_ty, val_reg, cg); 41168 disp += @divExact(val_reg.bitSize(), 8); 41169 } 41170 }, 41171 .register_offset => |val_reg_off| switch (val_reg_off.off) { 41172 0 => try src.readReg(opts.disp, val_ty, registerAlias( 41173 val_reg_off.reg, 41174 @intCast(val_ty.abiSize(cg.pt.zcu)), 41175 ), cg), 41176 else => unreachable, 41177 }, 41178 .memory, .indirect, .load_frame, .load_symbol => { 41179 var val_ptr = try cg.tempInit(.usize, val_mcv.address()); 41180 var src_ptr = 41181 try cg.tempInit(.usize, src.tracking(cg).short.address().offset(opts.disp)); 41182 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41183 try val_ptr.memcpy(&src_ptr, &len, cg); 41184 try val_ptr.die(cg); 41185 try src_ptr.die(cg); 41186 try len.die(cg); 41187 }, 41188 } 41189 return val; 41190 } 41191 41192 fn write(dst: *Temp, val: *Temp, opts: AccessOptions, cg: *CodeGen) !void { 41193 const val_ty = val.typeOf(cg); 41194 while (try dst.toBase(cg)) {} 41195 val_to_gpr: while (true) : (while (try dst.toBase(cg) or 41196 try val.toRegClass(false, .general_purpose, cg)) 41197 {}) { 41198 const val_mcv = val.tracking(cg).short; 41199 switch (val_mcv) { 41200 else => |mcv| std.debug.panic("{s}: {}\n", .{ @src().fn_name, mcv }), 41201 .undef => if (opts.safe) { 41202 var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address().offset(opts.disp)); 41203 var pat = try cg.tempInit(.u8, .{ .immediate = 0xaa }); 41204 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41205 try dst_ptr.memset(&pat, &len, cg); 41206 try dst_ptr.die(cg); 41207 try pat.die(cg); 41208 try len.die(cg); 41209 }, 41210 .immediate => |val_imm| { 41211 const val_op: Immediate = if (std.math.cast(u31, val_imm)) |val_uimm31| 41212 .u(val_uimm31) 41213 else if (std.math.cast(i32, @as(i64, @bitCast(val_imm)))) |val_simm32| 41214 .s(val_simm32) 41215 else 41216 continue :val_to_gpr; 41217 try cg.asmMemoryImmediate( 41218 .{ ._, .mov }, 41219 try dst.tracking(cg).short.mem(cg, .{ 41220 .size = cg.memSize(val_ty), 41221 .disp = opts.disp, 41222 }), 41223 val_op, 41224 ); 41225 }, 41226 .eflags => |cc| try cg.asmSetccMemory( 41227 cc, 41228 try dst.tracking(cg).short.mem(cg, .{ 41229 .size = .byte, 41230 .disp = opts.disp, 41231 }), 41232 ), 41233 .register => |val_reg| try dst.writeRegs(opts.disp, val_ty, &.{registerAlias( 41234 val_reg, 41235 @intCast(val_ty.abiSize(cg.pt.zcu)), 41236 )}, cg), 41237 inline .register_pair, 41238 .register_triple, 41239 .register_quadruple, 41240 => |val_regs| try dst.writeRegs(opts.disp, val_ty, &val_regs, cg), 41241 .register_offset => |val_reg_off| switch (val_reg_off.off) { 41242 0 => try dst.writeRegs(opts.disp, val_ty, &.{registerAlias( 41243 val_reg_off.reg, 41244 @intCast(val_ty.abiSize(cg.pt.zcu)), 41245 )}, cg), 41246 else => continue :val_to_gpr, 41247 }, 41248 .register_overflow => |val_reg_ov| { 41249 const ip = &cg.pt.zcu.intern_pool; 41250 const first_ty: Type = .fromInterned(first_ty: switch (ip.indexToKey(val_ty.toIntern())) { 41251 .tuple_type => |tuple_type| { 41252 const tuple_field_types = tuple_type.types.get(ip); 41253 assert(tuple_field_types.len == 2 and tuple_field_types[1] == .u1_type); 41254 break :first_ty tuple_field_types[0]; 41255 }, 41256 .opt_type => |opt_child| { 41257 assert(!val_ty.optionalReprIsPayload(cg.pt.zcu)); 41258 break :first_ty opt_child; 41259 }, 41260 else => std.debug.panic("{s}: {}\n", .{ @src().fn_name, val_ty.fmt(cg.pt) }), 41261 }); 41262 const first_size: u31 = @intCast(first_ty.abiSize(cg.pt.zcu)); 41263 try dst.writeRegs(opts.disp, first_ty, &.{registerAlias(val_reg_ov.reg, first_size)}, cg); 41264 try cg.asmSetccMemory( 41265 val_reg_ov.eflags, 41266 try dst.tracking(cg).short.mem(cg, .{ 41267 .size = .byte, 41268 .disp = opts.disp + first_size, 41269 }), 41270 ); 41271 }, 41272 .lea_frame, .lea_symbol => continue :val_to_gpr, 41273 .memory, .indirect, .load_frame, .load_symbol => { 41274 var dst_ptr = 41275 try cg.tempInit(.usize, dst.tracking(cg).short.address().offset(opts.disp)); 41276 var val_ptr = try cg.tempInit(.usize, val_mcv.address()); 41277 var len = try cg.tempInit(.usize, .{ .immediate = val_ty.abiSize(cg.pt.zcu) }); 41278 try dst_ptr.memcpy(&val_ptr, &len, cg); 41279 try dst_ptr.die(cg); 41280 try val_ptr.die(cg); 41281 try len.die(cg); 41282 }, 41283 } 41284 break; 41285 } 41286 } 41287 41288 fn loadReg(ptr: *Temp, dst_ty: Type, dst_reg: Register, cg: *CodeGen) !void { 41289 const dst_rc = dst_reg.class(); 41290 const strat = try cg.moveStrategy(dst_ty, dst_rc, false); 41291 // hack around linker relocation bugs 41292 switch (ptr.tracking(cg).short) { 41293 else => {}, 41294 .lea_symbol => |sym_off| if (dst_rc != .general_purpose or sym_off.off != 0) 41295 while (try ptr.toRegClass(false, .general_purpose, cg)) {}, 41296 } 41297 try strat.read(cg, dst_reg, try ptr.tracking(cg).short.deref().mem(cg, .{ 41298 .size = .fromBitSize(@min(8 * dst_ty.abiSize(cg.pt.zcu), dst_reg.bitSize())), 41299 })); 41300 } 41301 41302 fn storeRegs(ptr: *Temp, src_ty: Type, src_regs: []const Register, cg: *CodeGen) !void { 41303 var part_disp: u31 = 0; 41304 var deferred_disp: u31 = 0; 41305 var src_abi_size: u32 = @intCast(src_ty.abiSize(cg.pt.zcu)); 41306 for (src_regs) |src_reg| { 41307 const src_rc = src_reg.class(); 41308 const part_bit_size = @min(8 * src_abi_size, src_reg.bitSize()); 41309 const part_size = @divExact(part_bit_size, 8); 41310 if (src_rc == .x87 or std.math.isPowerOfTwo(part_size)) { 41311 // hack around linker relocation bugs 41312 switch (ptr.tracking(cg).short) { 41313 else => {}, 41314 .lea_symbol => while (try ptr.toRegClass(false, .general_purpose, cg)) {}, 41315 } 41316 const strat = try cg.moveStrategy(src_ty, src_rc, false); 41317 try strat.write(cg, try ptr.tracking(cg).short.deref().mem(cg, .{ 41318 .size = .fromBitSize(part_bit_size), 41319 .disp = part_disp, 41320 }), registerAlias(src_reg, part_size)); 41321 } else { 41322 const frame_size = std.math.ceilPowerOfTwoAssert(u32, part_size); 41323 const frame_index = try cg.allocFrameIndex(.init(.{ 41324 .size = frame_size, 41325 .alignment = .fromNonzeroByteUnits(frame_size), 41326 })); 41327 const strat = try cg.moveStrategy(src_ty, src_rc, true); 41328 try strat.write(cg, .{ 41329 .base = .{ .frame = frame_index }, 41330 .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, 41331 }, registerAlias(src_reg, frame_size)); 41332 try ptr.toOffset(deferred_disp, cg); 41333 deferred_disp = 0; 41334 var src_ptr = try cg.tempInit(.usize, .{ .lea_frame = .{ .index = frame_index } }); 41335 var len = try cg.tempInit(.usize, .{ .immediate = src_abi_size }); 41336 try ptr.memcpy(&src_ptr, &len, cg); 41337 try src_ptr.die(cg); 41338 try len.die(cg); 41339 } 41340 part_disp += part_size; 41341 deferred_disp += part_size; 41342 src_abi_size -= part_size; 41343 } 41344 } 41345 41346 fn readReg(src: Temp, disp: i32, dst_ty: Type, dst_reg: Register, cg: *CodeGen) !void { 41347 const strat = try cg.moveStrategy(dst_ty, dst_reg.class(), false); 41348 try strat.read(cg, dst_reg, try src.tracking(cg).short.mem(cg, .{ 41349 .size = .fromBitSize(@min(8 * dst_ty.abiSize(cg.pt.zcu), dst_reg.bitSize())), 41350 .disp = disp, 41351 })); 41352 } 41353 41354 fn writeRegs(dst: Temp, disp: i32, src_ty: Type, src_regs: []const Register, cg: *CodeGen) !void { 41355 var part_disp = disp; 41356 var src_abi_size: u32 = @intCast(src_ty.abiSize(cg.pt.zcu)); 41357 for (src_regs) |src_reg| { 41358 const src_rc = src_reg.class(); 41359 const part_bit_size = @min(8 * src_abi_size, src_reg.bitSize()); 41360 const part_size = @divExact(part_bit_size, 8); 41361 if (src_rc == .x87 or std.math.isPowerOfTwo(part_size)) { 41362 const strat = try cg.moveStrategy(src_ty, src_rc, false); 41363 try strat.write(cg, try dst.tracking(cg).short.mem(cg, .{ 41364 .size = .fromBitSize(part_bit_size), 41365 .disp = part_disp, 41366 }), registerAlias(src_reg, part_size)); 41367 } else { 41368 const frame_size = std.math.ceilPowerOfTwoAssert(u32, part_size); 41369 const frame_index = try cg.allocFrameIndex(.init(.{ 41370 .size = frame_size, 41371 .alignment = .fromNonzeroByteUnits(frame_size), 41372 })); 41373 const strat = try cg.moveStrategy(src_ty, src_rc, true); 41374 try strat.write(cg, .{ 41375 .base = .{ .frame = frame_index }, 41376 .mod = .{ .rm = .{ .size = .fromSize(frame_size) } }, 41377 }, registerAlias(src_reg, frame_size)); 41378 var dst_ptr = try cg.tempInit(.usize, dst.tracking(cg).short.address()); 41379 try dst_ptr.toOffset(part_disp, cg); 41380 var src_ptr = try cg.tempInit(.usize, .{ .lea_frame = .{ .index = frame_index } }); 41381 var len = try cg.tempInit(.usize, .{ .immediate = src_abi_size }); 41382 try dst_ptr.memcpy(&src_ptr, &len, cg); 41383 try dst_ptr.die(cg); 41384 try src_ptr.die(cg); 41385 try len.die(cg); 41386 } 41387 part_disp += part_size; 41388 src_abi_size -= part_size; 41389 } 41390 } 41391 41392 fn memcpy(dst: *Temp, src: *Temp, len: *Temp, cg: *CodeGen) !void { 41393 while (true) for ([_]*Temp{ dst, src, len }, [_]Register{ .rdi, .rsi, .rcx }) |temp, reg| { 41394 if (try temp.toReg(reg, cg)) break; 41395 } else break; 41396 try cg.asmOpOnly(.{ .@"rep _sb", .mov }); 41397 } 41398 41399 fn memset(dst: *Temp, val: *Temp, len: *Temp, cg: *CodeGen) !void { 41400 while (true) for ([_]*Temp{ dst, val, len }, [_]Register{ .rdi, .rax, .rcx }) |temp, reg| { 41401 if (try temp.toReg(reg, cg)) break; 41402 } else break; 41403 try cg.asmOpOnly(.{ .@"rep _sb", .sto }); 41404 } 41405 41406 /// Supports any `op` using `cg.intInfo(lhs.typeOf(cg)).?.signedness` as the signedness. 41407 /// Returns `error.SelectFailed` when `cg.intInfo(lhs.typeOf(cg)) == null`. 41408 fn cmpInts(lhs: *Temp, op: std.math.CompareOperator, rhs: *Temp, cg: *CodeGen) !Temp { 41409 var ops: [2]Temp = .{ lhs.*, rhs.* }; 41410 var res: [1]Temp = undefined; 41411 switch (op) { 41412 .lt, .lte, .gte, .gt => { 41413 const commute = switch (op) { 41414 .lt, .gte => false, 41415 .lte, .gt => true, 41416 else => unreachable, 41417 }; 41418 if (commute) std.mem.swap(Temp, &ops[0], &ops[1]); 41419 try cg.select(&res, &.{.bool}, &ops, comptime &.{ .{ 41420 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 41421 .patterns = &.{ 41422 .{ .src = .{ .imm8, .mem }, .commute = .{ 0, 1 } }, 41423 .{ .src = .{ .imm8, .to_gpr }, .commute = .{ 0, 1 } }, 41424 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41425 }, 41426 .dst_temps = .{.{ .cc = .g }}, 41427 .clobbers = .{ .eflags = true }, 41428 .each = .{ .once = &.{ 41429 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 41430 } }, 41431 }, .{ 41432 .src_constraints = .{ .{ .signed_int = .byte }, .{ .signed_int = .byte } }, 41433 .patterns = &.{ 41434 .{ .src = .{ .mem, .imm8 } }, 41435 .{ .src = .{ .to_gpr, .imm8 } }, 41436 .{ .src = .{ .to_gpr, .mem } }, 41437 .{ .src = .{ .to_gpr, .to_gpr } }, 41438 }, 41439 .dst_temps = .{.{ .cc = .l }}, 41440 .clobbers = .{ .eflags = true }, 41441 .each = .{ .once = &.{ 41442 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 41443 } }, 41444 }, .{ 41445 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 41446 .patterns = &.{ 41447 .{ .src = .{ .imm8, .mem }, .commute = .{ 0, 1 } }, 41448 .{ .src = .{ .imm8, .to_gpr }, .commute = .{ 0, 1 } }, 41449 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41450 }, 41451 .dst_temps = .{.{ .cc = .a }}, 41452 .clobbers = .{ .eflags = true }, 41453 .each = .{ .once = &.{ 41454 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 41455 } }, 41456 }, .{ 41457 .src_constraints = .{ .{ .unsigned_int = .byte }, .{ .unsigned_int = .byte } }, 41458 .patterns = &.{ 41459 .{ .src = .{ .mem, .imm8 } }, 41460 .{ .src = .{ .to_gpr, .imm8 } }, 41461 .{ .src = .{ .to_gpr, .mem } }, 41462 .{ .src = .{ .to_gpr, .to_gpr } }, 41463 }, 41464 .dst_temps = .{.{ .cc = .b }}, 41465 .clobbers = .{ .eflags = true }, 41466 .each = .{ .once = &.{ 41467 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 41468 } }, 41469 }, .{ 41470 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 41471 .patterns = &.{ 41472 .{ .src = .{ .imm16, .mem }, .commute = .{ 0, 1 } }, 41473 .{ .src = .{ .imm16, .to_gpr }, .commute = .{ 0, 1 } }, 41474 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41475 }, 41476 .dst_temps = .{.{ .cc = .g }}, 41477 .clobbers = .{ .eflags = true }, 41478 .each = .{ .once = &.{ 41479 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 41480 } }, 41481 }, .{ 41482 .src_constraints = .{ .{ .signed_int = .word }, .{ .signed_int = .word } }, 41483 .patterns = &.{ 41484 .{ .src = .{ .mem, .imm16 } }, 41485 .{ .src = .{ .to_gpr, .imm16 } }, 41486 .{ .src = .{ .to_gpr, .mem } }, 41487 .{ .src = .{ .to_gpr, .to_gpr } }, 41488 }, 41489 .dst_temps = .{.{ .cc = .l }}, 41490 .clobbers = .{ .eflags = true }, 41491 .each = .{ .once = &.{ 41492 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 41493 } }, 41494 }, .{ 41495 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 41496 .patterns = &.{ 41497 .{ .src = .{ .imm16, .mem }, .commute = .{ 0, 1 } }, 41498 .{ .src = .{ .imm16, .to_gpr }, .commute = .{ 0, 1 } }, 41499 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41500 }, 41501 .dst_temps = .{.{ .cc = .a }}, 41502 .clobbers = .{ .eflags = true }, 41503 .each = .{ .once = &.{ 41504 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 41505 } }, 41506 }, .{ 41507 .src_constraints = .{ .{ .unsigned_int = .word }, .{ .unsigned_int = .word } }, 41508 .patterns = &.{ 41509 .{ .src = .{ .mem, .imm16 } }, 41510 .{ .src = .{ .to_gpr, .imm16 } }, 41511 .{ .src = .{ .to_gpr, .mem } }, 41512 .{ .src = .{ .to_gpr, .to_gpr } }, 41513 }, 41514 .dst_temps = .{.{ .cc = .b }}, 41515 .clobbers = .{ .eflags = true }, 41516 .each = .{ .once = &.{ 41517 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 41518 } }, 41519 }, .{ 41520 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 41521 .patterns = &.{ 41522 .{ .src = .{ .imm32, .mem }, .commute = .{ 0, 1 } }, 41523 .{ .src = .{ .imm32, .to_gpr }, .commute = .{ 0, 1 } }, 41524 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41525 }, 41526 .dst_temps = .{.{ .cc = .g }}, 41527 .clobbers = .{ .eflags = true }, 41528 .each = .{ .once = &.{ 41529 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 41530 } }, 41531 }, .{ 41532 .src_constraints = .{ .{ .signed_int = .dword }, .{ .signed_int = .dword } }, 41533 .patterns = &.{ 41534 .{ .src = .{ .mem, .imm32 } }, 41535 .{ .src = .{ .to_gpr, .imm32 } }, 41536 .{ .src = .{ .to_gpr, .mem } }, 41537 .{ .src = .{ .to_gpr, .to_gpr } }, 41538 }, 41539 .dst_temps = .{.{ .cc = .l }}, 41540 .clobbers = .{ .eflags = true }, 41541 .each = .{ .once = &.{ 41542 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 41543 } }, 41544 }, .{ 41545 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 41546 .patterns = &.{ 41547 .{ .src = .{ .imm32, .mem }, .commute = .{ 0, 1 } }, 41548 .{ .src = .{ .imm32, .to_gpr }, .commute = .{ 0, 1 } }, 41549 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41550 }, 41551 .dst_temps = .{.{ .cc = .a }}, 41552 .clobbers = .{ .eflags = true }, 41553 .each = .{ .once = &.{ 41554 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 41555 } }, 41556 }, .{ 41557 .src_constraints = .{ .{ .unsigned_int = .dword }, .{ .unsigned_int = .dword } }, 41558 .patterns = &.{ 41559 .{ .src = .{ .mem, .imm32 } }, 41560 .{ .src = .{ .to_gpr, .imm32 } }, 41561 .{ .src = .{ .to_gpr, .mem } }, 41562 .{ .src = .{ .to_gpr, .to_gpr } }, 41563 }, 41564 .dst_temps = .{.{ .cc = .b }}, 41565 .clobbers = .{ .eflags = true }, 41566 .each = .{ .once = &.{ 41567 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 41568 } }, 41569 }, .{ 41570 .required_features = .{ .@"64bit", null, null, null }, 41571 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 41572 .patterns = &.{ 41573 .{ .src = .{ .simm32, .mem }, .commute = .{ 0, 1 } }, 41574 .{ .src = .{ .simm32, .to_gpr }, .commute = .{ 0, 1 } }, 41575 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41576 }, 41577 .dst_temps = .{.{ .cc = .g }}, 41578 .clobbers = .{ .eflags = true }, 41579 .each = .{ .once = &.{ 41580 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 41581 } }, 41582 }, .{ 41583 .required_features = .{ .@"64bit", null, null, null }, 41584 .src_constraints = .{ .{ .signed_int = .qword }, .{ .signed_int = .qword } }, 41585 .patterns = &.{ 41586 .{ .src = .{ .mem, .simm32 } }, 41587 .{ .src = .{ .to_gpr, .simm32 } }, 41588 .{ .src = .{ .to_gpr, .mem } }, 41589 .{ .src = .{ .to_gpr, .to_gpr } }, 41590 }, 41591 .dst_temps = .{.{ .cc = .l }}, 41592 .clobbers = .{ .eflags = true }, 41593 .each = .{ .once = &.{ 41594 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 41595 } }, 41596 }, .{ 41597 .required_features = .{ .@"64bit", null, null, null }, 41598 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 41599 .patterns = &.{ 41600 .{ .src = .{ .simm32, .mem }, .commute = .{ 0, 1 } }, 41601 .{ .src = .{ .simm32, .to_gpr }, .commute = .{ 0, 1 } }, 41602 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41603 }, 41604 .dst_temps = .{.{ .cc = .a }}, 41605 .clobbers = .{ .eflags = true }, 41606 .each = .{ .once = &.{ 41607 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 41608 } }, 41609 }, .{ 41610 .required_features = .{ .@"64bit", null, null, null }, 41611 .src_constraints = .{ .{ .unsigned_int = .qword }, .{ .unsigned_int = .qword } }, 41612 .patterns = &.{ 41613 .{ .src = .{ .mem, .simm32 } }, 41614 .{ .src = .{ .to_gpr, .simm32 } }, 41615 .{ .src = .{ .to_gpr, .mem } }, 41616 .{ .src = .{ .to_gpr, .to_gpr } }, 41617 }, 41618 .dst_temps = .{.{ .cc = .b }}, 41619 .clobbers = .{ .eflags = true }, 41620 .each = .{ .once = &.{ 41621 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 41622 } }, 41623 }, .{ 41624 .required_features = .{ .@"64bit", null, null, null }, 41625 .src_constraints = .{ 41626 .{ .signed_remainder_int = .{ .of = .qword, .is = .qword } }, 41627 .{ .signed_remainder_int = .{ .of = .qword, .is = .qword } }, 41628 }, 41629 .patterns = &.{ 41630 .{ .src = .{ .to_mem, .to_mem } }, 41631 }, 41632 .extra_temps = .{ 41633 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 41634 .{ .type = .i64, .kind = .{ .rc = .general_purpose } }, 41635 .unused, 41636 .unused, 41637 .unused, 41638 .unused, 41639 .unused, 41640 .unused, 41641 .unused, 41642 }, 41643 .dst_temps = .{.{ .cc = .l }}, 41644 .clobbers = .{ .eflags = true }, 41645 .each = .{ .once = &.{ 41646 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_8), ._, ._ }, 41647 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 41648 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 41649 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"8", .tmp0, .add_size, -8), ._, ._ }, 41650 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 41651 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 41652 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -8), ._, ._ }, 41653 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -8), ._, ._ }, 41654 } }, 41655 }, .{ 41656 .required_features = .{ .@"64bit", null, null, null }, 41657 .src_constraints = .{ 41658 .{ .unsigned_remainder_int = .{ .of = .qword, .is = .qword } }, 41659 .{ .unsigned_remainder_int = .{ .of = .qword, .is = .qword } }, 41660 }, 41661 .patterns = &.{ 41662 .{ .src = .{ .to_mem, .to_mem } }, 41663 }, 41664 .extra_temps = .{ 41665 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 41666 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 41667 .unused, 41668 .unused, 41669 .unused, 41670 .unused, 41671 .unused, 41672 .unused, 41673 .unused, 41674 }, 41675 .dst_temps = .{.{ .cc = .b }}, 41676 .clobbers = .{ .eflags = true }, 41677 .each = .{ .once = &.{ 41678 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_8), ._, ._ }, 41679 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 41680 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"8", .tmp0, .add_size), ._, ._ }, 41681 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"8", .tmp0, .add_size), ._, ._ }, 41682 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 41683 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 41684 } }, 41685 }, .{ 41686 .src_constraints = .{ 41687 .{ .signed_remainder_int = .{ .of = .dword, .is = .dword } }, 41688 .{ .signed_remainder_int = .{ .of = .dword, .is = .dword } }, 41689 }, 41690 .patterns = &.{ 41691 .{ .src = .{ .to_mem, .to_mem } }, 41692 }, 41693 .extra_temps = .{ 41694 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 41695 .{ .type = .i32, .kind = .{ .rc = .general_purpose } }, 41696 .unused, 41697 .unused, 41698 .unused, 41699 .unused, 41700 .unused, 41701 .unused, 41702 .unused, 41703 }, 41704 .dst_temps = .{.{ .cc = .l }}, 41705 .clobbers = .{ .eflags = true }, 41706 .each = .{ .once = &.{ 41707 .{ ._, ._, .mov, .tmp0p, .sia(1, .src0, .sub_size_div_4), ._, ._ }, 41708 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 41709 .{ .@"0:", ._, .mov, .tmp1q, .memsiad(.src0q, .@"4", .tmp0, .add_size, -4), ._, ._ }, 41710 .{ ._, ._, .sbb, .tmp1q, .memsiad(.src1q, .@"4", .tmp0, .add_size, -4), ._, ._ }, 41711 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 41712 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 41713 .{ ._, ._, .mov, .tmp1q, .memad(.src0q, .add_size, -4), ._, ._ }, 41714 .{ ._, ._, .sbb, .tmp1q, .memad(.src1q, .add_size, -4), ._, ._ }, 41715 } }, 41716 }, .{ 41717 .src_constraints = .{ 41718 .{ .unsigned_remainder_int = .{ .of = .dword, .is = .dword } }, 41719 .{ .unsigned_remainder_int = .{ .of = .dword, .is = .dword } }, 41720 }, 41721 .patterns = &.{ 41722 .{ .src = .{ .to_mem, .to_mem } }, 41723 }, 41724 .extra_temps = .{ 41725 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 41726 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 41727 .unused, 41728 .unused, 41729 .unused, 41730 .unused, 41731 .unused, 41732 .unused, 41733 .unused, 41734 }, 41735 .dst_temps = .{.{ .cc = .b }}, 41736 .clobbers = .{ .eflags = true }, 41737 .each = .{ .once = &.{ 41738 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size_div_4), ._, ._ }, 41739 .{ ._, ._c, .cl, ._, ._, ._, ._ }, 41740 .{ .@"0:", ._, .mov, .tmp1q, .memsia(.src0q, .@"4", .tmp0, .add_size), ._, ._ }, 41741 .{ ._, ._, .sbb, .tmp1q, .memsia(.src1q, .@"4", .tmp0, .add_size), ._, ._ }, 41742 .{ ._, ._c, .in, .tmp0p, ._, ._, ._ }, 41743 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 41744 } }, 41745 } }); 41746 if (commute) std.mem.swap(Temp, &ops[0], &ops[1]); 41747 }, 41748 .eq, .neq => { 41749 try cg.select(&res, &.{.bool}, &ops, comptime &.{ .{ 41750 .src_constraints = .{ .{ .int = .byte }, .{ .int = .byte } }, 41751 .patterns = &.{ 41752 .{ .src = .{ .mem, .imm8 } }, 41753 .{ .src = .{ .imm8, .mem }, .commute = .{ 0, 1 } }, 41754 .{ .src = .{ .to_gpr, .imm8 } }, 41755 .{ .src = .{ .imm8, .to_gpr }, .commute = .{ 0, 1 } }, 41756 .{ .src = .{ .to_gpr, .mem } }, 41757 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41758 .{ .src = .{ .to_gpr, .to_gpr } }, 41759 }, 41760 .dst_temps = .{.{ .cc = .e }}, 41761 .clobbers = .{ .eflags = true }, 41762 .each = .{ .once = &.{ 41763 .{ ._, ._, .cmp, .src0b, .src1b, ._, ._ }, 41764 } }, 41765 }, .{ 41766 .src_constraints = .{ .{ .int = .word }, .{ .int = .word } }, 41767 .patterns = &.{ 41768 .{ .src = .{ .mem, .imm16 } }, 41769 .{ .src = .{ .imm16, .mem }, .commute = .{ 0, 1 } }, 41770 .{ .src = .{ .to_gpr, .imm16 } }, 41771 .{ .src = .{ .imm16, .to_gpr }, .commute = .{ 0, 1 } }, 41772 .{ .src = .{ .to_gpr, .mem } }, 41773 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41774 .{ .src = .{ .to_gpr, .to_gpr } }, 41775 }, 41776 .dst_temps = .{.{ .cc = .e }}, 41777 .clobbers = .{ .eflags = true }, 41778 .each = .{ .once = &.{ 41779 .{ ._, ._, .cmp, .src0w, .src1w, ._, ._ }, 41780 } }, 41781 }, .{ 41782 .src_constraints = .{ .{ .int = .dword }, .{ .int = .dword } }, 41783 .patterns = &.{ 41784 .{ .src = .{ .mem, .imm32 } }, 41785 .{ .src = .{ .imm32, .mem }, .commute = .{ 0, 1 } }, 41786 .{ .src = .{ .to_gpr, .imm32 } }, 41787 .{ .src = .{ .imm32, .to_gpr }, .commute = .{ 0, 1 } }, 41788 .{ .src = .{ .to_gpr, .mem } }, 41789 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41790 .{ .src = .{ .to_gpr, .to_gpr } }, 41791 }, 41792 .dst_temps = .{.{ .cc = .e }}, 41793 .clobbers = .{ .eflags = true }, 41794 .each = .{ .once = &.{ 41795 .{ ._, ._, .cmp, .src0d, .src1d, ._, ._ }, 41796 } }, 41797 }, .{ 41798 .required_features = .{ .@"64bit", null, null, null }, 41799 .src_constraints = .{ .{ .int = .qword }, .{ .int = .qword } }, 41800 .patterns = &.{ 41801 .{ .src = .{ .mem, .simm32 } }, 41802 .{ .src = .{ .simm32, .mem }, .commute = .{ 0, 1 } }, 41803 .{ .src = .{ .to_gpr, .simm32 } }, 41804 .{ .src = .{ .simm32, .to_gpr }, .commute = .{ 0, 1 } }, 41805 .{ .src = .{ .to_gpr, .mem } }, 41806 .{ .src = .{ .mem, .to_gpr }, .commute = .{ 0, 1 } }, 41807 .{ .src = .{ .to_gpr, .to_gpr } }, 41808 }, 41809 .dst_temps = .{.{ .cc = .e }}, 41810 .clobbers = .{ .eflags = true }, 41811 .each = .{ .once = &.{ 41812 .{ ._, ._, .cmp, .src0q, .src1q, ._, ._ }, 41813 } }, 41814 }, .{ 41815 .required_features = .{ .sse, .mmx, null, null }, 41816 .src_constraints = .{ .{ .int = .qword }, .{ .int = .qword } }, 41817 .patterns = &.{ 41818 .{ .src = .{ .to_mut_mm, .mem } }, 41819 .{ .src = .{ .mem, .to_mut_mm }, .commute = .{ 0, 1 } }, 41820 .{ .src = .{ .to_mut_mm, .to_mm } }, 41821 }, 41822 .extra_temps = .{ 41823 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 41824 .{ .kind = .{ .rc = .mmx } }, 41825 .unused, 41826 .unused, 41827 .unused, 41828 .unused, 41829 .unused, 41830 .unused, 41831 .unused, 41832 }, 41833 .dst_temps = .{.{ .cc = .z }}, 41834 .clobbers = .{ .eflags = true }, 41835 .each = .{ .once = &.{ 41836 .{ ._, .p_, .xor, .tmp1q, .tmp1q, ._, ._ }, 41837 .{ ._, .p_, .xor, .src0q, .src1q, ._, ._ }, 41838 .{ ._, .p_b, .cmpeq, .tmp1q, .src0q, ._, ._ }, 41839 .{ ._, .p_b, .movmsk, .tmp0d, .tmp1q, ._, ._ }, 41840 .{ ._, ._, .cmp, .tmp0b, .si(-1), ._, ._ }, 41841 } }, 41842 }, .{ 41843 .required_features = .{ .avx, null, null, null }, 41844 .src_constraints = .{ .{ .int = .xword }, .{ .int = .xword } }, 41845 .patterns = &.{ 41846 .{ .src = .{ .to_xmm, .mem } }, 41847 .{ .src = .{ .mem, .to_xmm }, .commute = .{ 0, 1 } }, 41848 .{ .src = .{ .to_xmm, .to_xmm } }, 41849 }, 41850 .extra_temps = .{ 41851 .{ .kind = .{ .rc = .sse } }, 41852 .unused, 41853 .unused, 41854 .unused, 41855 .unused, 41856 .unused, 41857 .unused, 41858 .unused, 41859 .unused, 41860 }, 41861 .dst_temps = .{.{ .cc = .z }}, 41862 .clobbers = .{ .eflags = true }, 41863 .each = .{ .once = &.{ 41864 .{ ._, .vp_, .xor, .tmp0x, .src0x, .src1x, ._ }, 41865 .{ ._, .vp_, .@"test", .tmp0x, .tmp0x, ._, ._ }, 41866 } }, 41867 }, .{ 41868 .required_features = .{ .sse4_1, null, null, null }, 41869 .src_constraints = .{ .{ .int = .xword }, .{ .int = .xword } }, 41870 .patterns = &.{ 41871 .{ .src = .{ .to_mut_xmm, .mem } }, 41872 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 41873 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 41874 }, 41875 .dst_temps = .{.{ .cc = .z }}, 41876 .clobbers = .{ .eflags = true }, 41877 .each = .{ .once = &.{ 41878 .{ ._, .p_, .xor, .src0x, .src1x, ._, ._ }, 41879 .{ ._, .p_, .@"test", .src0x, .src0x, ._, ._ }, 41880 } }, 41881 }, .{ 41882 .required_features = .{ .sse2, .fast_imm16, null, null }, 41883 .src_constraints = .{ .{ .int = .xword }, .{ .int = .xword } }, 41884 .patterns = &.{ 41885 .{ .src = .{ .to_mut_xmm, .mem } }, 41886 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 41887 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 41888 }, 41889 .extra_temps = .{ 41890 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 41891 .{ .kind = .{ .rc = .sse } }, 41892 .unused, 41893 .unused, 41894 .unused, 41895 .unused, 41896 .unused, 41897 .unused, 41898 .unused, 41899 }, 41900 .dst_temps = .{.{ .cc = .z }}, 41901 .clobbers = .{ .eflags = true }, 41902 .each = .{ .once = &.{ 41903 .{ ._, .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 41904 .{ ._, .p_, .xor, .src0x, .src1x, ._, ._ }, 41905 .{ ._, .p_b, .cmpeq, .tmp1x, .src0x, ._, ._ }, 41906 .{ ._, .p_b, .movmsk, .tmp0d, .tmp1x, ._, ._ }, 41907 .{ ._, ._, .cmp, .tmp0w, .si(-1), ._, ._ }, 41908 } }, 41909 }, .{ 41910 .required_features = .{ .sse2, null, null, null }, 41911 .src_constraints = .{ .{ .int = .xword }, .{ .int = .xword } }, 41912 .patterns = &.{ 41913 .{ .src = .{ .to_mut_xmm, .mem } }, 41914 .{ .src = .{ .mem, .to_mut_xmm }, .commute = .{ 0, 1 } }, 41915 .{ .src = .{ .to_mut_xmm, .to_xmm } }, 41916 }, 41917 .extra_temps = .{ 41918 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 41919 .{ .kind = .{ .rc = .sse } }, 41920 .unused, 41921 .unused, 41922 .unused, 41923 .unused, 41924 .unused, 41925 .unused, 41926 .unused, 41927 }, 41928 .dst_temps = .{.{ .cc = .z }}, 41929 .clobbers = .{ .eflags = true }, 41930 .each = .{ .once = &.{ 41931 .{ ._, .p_, .xor, .tmp1x, .tmp1x, ._, ._ }, 41932 .{ ._, .p_, .xor, .src0x, .src1x, ._, ._ }, 41933 .{ ._, .p_b, .cmpeq, .tmp1x, .src0x, ._, ._ }, 41934 .{ ._, .p_b, .movmsk, .tmp0d, .tmp1x, ._, ._ }, 41935 .{ ._, ._, .xor, .tmp0d, .si(0xffff), ._, ._ }, 41936 } }, 41937 }, .{ 41938 .required_features = .{ .avx2, null, null, null }, 41939 .src_constraints = .{ .{ .int = .yword }, .{ .int = .yword } }, 41940 .patterns = &.{ 41941 .{ .src = .{ .to_ymm, .mem } }, 41942 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 41943 .{ .src = .{ .to_ymm, .to_ymm } }, 41944 }, 41945 .extra_temps = .{ 41946 .{ .kind = .{ .rc = .sse } }, 41947 .unused, 41948 .unused, 41949 .unused, 41950 .unused, 41951 .unused, 41952 .unused, 41953 .unused, 41954 .unused, 41955 }, 41956 .dst_temps = .{.{ .cc = .z }}, 41957 .clobbers = .{ .eflags = true }, 41958 .each = .{ .once = &.{ 41959 .{ ._, .vp_, .xor, .tmp0y, .src0y, .src1y, ._ }, 41960 .{ ._, .vp_, .@"test", .tmp0y, .tmp0y, ._, ._ }, 41961 } }, 41962 }, .{ 41963 .required_features = .{ .avx, null, null, null }, 41964 .src_constraints = .{ .{ .int = .yword }, .{ .int = .yword } }, 41965 .patterns = &.{ 41966 .{ .src = .{ .to_ymm, .mem } }, 41967 .{ .src = .{ .mem, .to_ymm }, .commute = .{ 0, 1 } }, 41968 .{ .src = .{ .to_ymm, .to_ymm } }, 41969 }, 41970 .extra_temps = .{ 41971 .{ .kind = .{ .rc = .sse } }, 41972 .unused, 41973 .unused, 41974 .unused, 41975 .unused, 41976 .unused, 41977 .unused, 41978 .unused, 41979 .unused, 41980 }, 41981 .dst_temps = .{.{ .cc = .z }}, 41982 .clobbers = .{ .eflags = true }, 41983 .each = .{ .once = &.{ 41984 .{ ._, .v_pd, .xor, .tmp0y, .src0y, .src1y, ._ }, 41985 .{ ._, .vp_, .@"test", .tmp0y, .tmp0y, ._, ._ }, 41986 } }, 41987 }, .{ 41988 .required_features = .{ .avx2, null, null, null }, 41989 .src_constraints = .{ 41990 .{ .remainder_int = .{ .of = .yword, .is = .xword } }, 41991 .{ .remainder_int = .{ .of = .yword, .is = .xword } }, 41992 }, 41993 .patterns = &.{ 41994 .{ .src = .{ .to_mem, .to_mem } }, 41995 }, 41996 .extra_temps = .{ 41997 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 41998 .{ .kind = .{ .rc = .sse } }, 41999 .unused, 42000 .unused, 42001 .unused, 42002 .unused, 42003 .unused, 42004 .unused, 42005 .unused, 42006 }, 42007 .dst_temps = .{.{ .cc = .z }}, 42008 .clobbers = .{ .eflags = true }, 42009 .each = .{ .once = &.{ 42010 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 42011 .{ .@"0:", .v_dqu, .mov, .tmp1y, .memiad(.src0y, .tmp0, .add_size, -16), ._, ._ }, 42012 .{ ._, .vp_, .xor, .tmp1y, .tmp1y, .memiad(.src1y, .tmp0, .add_size, -16), ._ }, 42013 .{ ._, .vp_, .@"test", .tmp1y, .tmp1y, ._, ._ }, 42014 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42015 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 42016 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42017 .{ ._, .v_dqa, .mov, .tmp1x, .memad(.src0x, .add_size, -16), ._, ._ }, 42018 .{ ._, .vp_, .xor, .tmp1x, .tmp1x, .memad(.src1x, .add_size, -16), ._ }, 42019 .{ ._, .vp_, .@"test", .tmp1x, .tmp1x, ._, ._ }, 42020 } }, 42021 }, .{ 42022 .required_features = .{ .avx, null, null, null }, 42023 .src_constraints = .{ 42024 .{ .remainder_int = .{ .of = .yword, .is = .xword } }, 42025 .{ .remainder_int = .{ .of = .yword, .is = .xword } }, 42026 }, 42027 .patterns = &.{ 42028 .{ .src = .{ .to_mem, .to_mem } }, 42029 }, 42030 .extra_temps = .{ 42031 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42032 .{ .kind = .{ .rc = .sse } }, 42033 .unused, 42034 .unused, 42035 .unused, 42036 .unused, 42037 .unused, 42038 .unused, 42039 .unused, 42040 }, 42041 .dst_temps = .{.{ .cc = .z }}, 42042 .clobbers = .{ .eflags = true }, 42043 .each = .{ .once = &.{ 42044 .{ ._, ._, .mov, .tmp0p, .sia(16, .src0, .sub_size), ._, ._ }, 42045 .{ .@"0:", .v_pd, .movu, .tmp1y, .memiad(.src0y, .tmp0, .add_size, -16), ._, ._ }, 42046 .{ ._, .v_pd, .xor, .tmp1y, .tmp1y, .memiad(.src1y, .tmp0, .add_size, -16), ._ }, 42047 .{ ._, .vp_, .@"test", .tmp1y, .tmp1y, ._, ._ }, 42048 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42049 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 42050 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42051 .{ ._, .v_pd, .mova, .tmp1x, .memad(.src0x, .add_size, -16), ._, ._ }, 42052 .{ ._, .v_pd, .xor, .tmp1x, .tmp1x, .memad(.src1x, .add_size, -16), ._ }, 42053 .{ ._, .vp_, .@"test", .tmp1x, .tmp1x, ._, ._ }, 42054 } }, 42055 }, .{ 42056 .required_features = .{ .avx2, null, null, null }, 42057 .src_constraints = .{ 42058 .{ .remainder_int = .{ .of = .yword, .is = .yword } }, 42059 .{ .remainder_int = .{ .of = .yword, .is = .yword } }, 42060 }, 42061 .patterns = &.{ 42062 .{ .src = .{ .to_mem, .to_mem } }, 42063 }, 42064 .extra_temps = .{ 42065 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42066 .{ .kind = .{ .rc = .sse } }, 42067 .unused, 42068 .unused, 42069 .unused, 42070 .unused, 42071 .unused, 42072 .unused, 42073 .unused, 42074 }, 42075 .dst_temps = .{.{ .cc = .z }}, 42076 .clobbers = .{ .eflags = true }, 42077 .each = .{ .once = &.{ 42078 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42079 .{ .@"0:", .v_dqu, .mov, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 42080 .{ ._, .vp_, .xor, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 42081 .{ ._, .vp_, .@"test", .tmp1y, .tmp1y, ._, ._ }, 42082 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42083 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 42084 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42085 } }, 42086 }, .{ 42087 .required_features = .{ .avx, null, null, null }, 42088 .src_constraints = .{ 42089 .{ .remainder_int = .{ .of = .yword, .is = .yword } }, 42090 .{ .remainder_int = .{ .of = .yword, .is = .yword } }, 42091 }, 42092 .patterns = &.{ 42093 .{ .src = .{ .to_mem, .to_mem } }, 42094 }, 42095 .extra_temps = .{ 42096 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42097 .{ .kind = .{ .rc = .sse } }, 42098 .unused, 42099 .unused, 42100 .unused, 42101 .unused, 42102 .unused, 42103 .unused, 42104 .unused, 42105 }, 42106 .dst_temps = .{.{ .cc = .z }}, 42107 .clobbers = .{ .eflags = true }, 42108 .each = .{ .once = &.{ 42109 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42110 .{ .@"0:", .v_pd, .movu, .tmp1y, .memia(.src0y, .tmp0, .add_size), ._, ._ }, 42111 .{ ._, .v_pd, .xor, .tmp1y, .tmp1y, .memia(.src1y, .tmp0, .add_size), ._ }, 42112 .{ ._, .vp_, .@"test", .tmp1y, .tmp1y, ._, ._ }, 42113 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42114 .{ ._, ._, .add, .tmp0p, .si(32), ._, ._ }, 42115 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42116 } }, 42117 }, .{ 42118 .required_features = .{ .avx, null, null, null }, 42119 .src_constraints = .{ 42120 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42121 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42122 }, 42123 .patterns = &.{ 42124 .{ .src = .{ .to_mem, .to_mem } }, 42125 }, 42126 .extra_temps = .{ 42127 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42128 .{ .kind = .{ .rc = .sse } }, 42129 .unused, 42130 .unused, 42131 .unused, 42132 .unused, 42133 .unused, 42134 .unused, 42135 .unused, 42136 }, 42137 .dst_temps = .{.{ .cc = .z }}, 42138 .clobbers = .{ .eflags = true }, 42139 .each = .{ .once = &.{ 42140 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42141 .{ .@"0:", .v_dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 42142 .{ ._, .vp_, .xor, .tmp1x, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._ }, 42143 .{ ._, .vp_, .@"test", .tmp1x, .tmp1x, ._, ._ }, 42144 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42145 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 42146 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42147 } }, 42148 }, .{ 42149 .required_features = .{ .sse4_1, null, null, null }, 42150 .src_constraints = .{ 42151 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42152 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42153 }, 42154 .patterns = &.{ 42155 .{ .src = .{ .to_mem, .to_mem } }, 42156 }, 42157 .extra_temps = .{ 42158 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42159 .{ .kind = .{ .rc = .sse } }, 42160 .unused, 42161 .unused, 42162 .unused, 42163 .unused, 42164 .unused, 42165 .unused, 42166 .unused, 42167 }, 42168 .dst_temps = .{.{ .cc = .z }}, 42169 .clobbers = .{ .eflags = true }, 42170 .each = .{ .once = &.{ 42171 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42172 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 42173 .{ ._, .p_, .xor, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 42174 .{ ._, .p_, .@"test", .tmp1x, .tmp1x, ._, ._ }, 42175 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42176 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 42177 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42178 } }, 42179 }, .{ 42180 .required_features = .{ .sse2, .fast_imm16, null, null }, 42181 .src_constraints = .{ 42182 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42183 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42184 }, 42185 .patterns = &.{ 42186 .{ .src = .{ .to_mem, .to_mem } }, 42187 }, 42188 .extra_temps = .{ 42189 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42190 .{ .kind = .{ .rc = .sse } }, 42191 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 42192 .unused, 42193 .unused, 42194 .unused, 42195 .unused, 42196 .unused, 42197 .unused, 42198 }, 42199 .dst_temps = .{.{ .cc = .z }}, 42200 .clobbers = .{ .eflags = true }, 42201 .each = .{ .once = &.{ 42202 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42203 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 42204 .{ ._, .p_b, .cmpeq, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 42205 .{ ._, .p_b, .movmsk, .tmp2d, .tmp1x, ._, ._ }, 42206 .{ ._, ._, .cmp, .tmp2w, .si(-1), ._, ._ }, 42207 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42208 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 42209 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42210 } }, 42211 }, .{ 42212 .required_features = .{ .sse2, null, null, null }, 42213 .src_constraints = .{ 42214 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42215 .{ .remainder_int = .{ .of = .xword, .is = .xword } }, 42216 }, 42217 .patterns = &.{ 42218 .{ .src = .{ .to_mem, .to_mem } }, 42219 }, 42220 .extra_temps = .{ 42221 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42222 .{ .kind = .{ .rc = .sse } }, 42223 .{ .type = .u16, .kind = .{ .rc = .general_purpose } }, 42224 .unused, 42225 .unused, 42226 .unused, 42227 .unused, 42228 .unused, 42229 .unused, 42230 }, 42231 .dst_temps = .{.{ .cc = .z }}, 42232 .clobbers = .{ .eflags = true }, 42233 .each = .{ .once = &.{ 42234 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42235 .{ .@"0:", ._dqa, .mov, .tmp1x, .memia(.src0x, .tmp0, .add_size), ._, ._ }, 42236 .{ ._, .p_b, .cmpeq, .tmp1x, .memia(.src1x, .tmp0, .add_size), ._, ._ }, 42237 .{ ._, .p_b, .movmsk, .tmp2d, .tmp1x, ._, ._ }, 42238 .{ ._, ._, .xor, .tmp2d, .ui(0xffff), ._, ._ }, 42239 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42240 .{ ._, ._, .add, .tmp0p, .si(16), ._, ._ }, 42241 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42242 } }, 42243 }, .{ 42244 .required_features = .{ .sse, .mmx, null, null }, 42245 .src_constraints = .{ 42246 .{ .remainder_int = .{ .of = .qword, .is = .qword } }, 42247 .{ .remainder_int = .{ .of = .qword, .is = .qword } }, 42248 }, 42249 .patterns = &.{ 42250 .{ .src = .{ .to_mem, .to_mem } }, 42251 }, 42252 .extra_temps = .{ 42253 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42254 .{ .kind = .{ .rc = .mmx } }, 42255 .{ .type = .u8, .kind = .{ .rc = .general_purpose } }, 42256 .unused, 42257 .unused, 42258 .unused, 42259 .unused, 42260 .unused, 42261 .unused, 42262 }, 42263 .dst_temps = .{.{ .cc = .z }}, 42264 .clobbers = .{ .eflags = true }, 42265 .each = .{ .once = &.{ 42266 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42267 .{ .@"0:", ._q, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 42268 .{ ._, .p_b, .cmpeq, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 42269 .{ ._, .p_b, .movmsk, .tmp2d, .tmp1q, ._, ._ }, 42270 .{ ._, ._, .cmp, .tmp2b, .si(-1), ._, ._ }, 42271 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42272 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 42273 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42274 } }, 42275 }, .{ 42276 .required_features = .{ .@"64bit", null, null, null }, 42277 .src_constraints = .{ 42278 .{ .remainder_int = .{ .of = .qword, .is = .qword } }, 42279 .{ .remainder_int = .{ .of = .qword, .is = .qword } }, 42280 }, 42281 .patterns = &.{ 42282 .{ .src = .{ .to_mem, .to_mem } }, 42283 }, 42284 .extra_temps = .{ 42285 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42286 .{ .type = .u64, .kind = .{ .rc = .general_purpose } }, 42287 .unused, 42288 .unused, 42289 .unused, 42290 .unused, 42291 .unused, 42292 .unused, 42293 .unused, 42294 }, 42295 .dst_temps = .{.{ .cc = .z }}, 42296 .clobbers = .{ .eflags = true }, 42297 .each = .{ .once = &.{ 42298 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42299 .{ .@"0:", ._, .mov, .tmp1q, .memia(.src0q, .tmp0, .add_size), ._, ._ }, 42300 .{ ._, ._, .xor, .tmp1q, .memia(.src1q, .tmp0, .add_size), ._, ._ }, 42301 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42302 .{ ._, ._, .add, .tmp0p, .si(8), ._, ._ }, 42303 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42304 } }, 42305 }, .{ 42306 .src_constraints = .{ 42307 .{ .remainder_int = .{ .of = .dword, .is = .dword } }, 42308 .{ .remainder_int = .{ .of = .dword, .is = .dword } }, 42309 }, 42310 .patterns = &.{ 42311 .{ .src = .{ .to_mem, .to_mem } }, 42312 }, 42313 .extra_temps = .{ 42314 .{ .type = .isize, .kind = .{ .rc = .general_purpose } }, 42315 .{ .type = .u32, .kind = .{ .rc = .general_purpose } }, 42316 .unused, 42317 .unused, 42318 .unused, 42319 .unused, 42320 .unused, 42321 .unused, 42322 .unused, 42323 }, 42324 .dst_temps = .{.{ .cc = .z }}, 42325 .clobbers = .{ .eflags = true }, 42326 .each = .{ .once = &.{ 42327 .{ ._, ._, .mov, .tmp0p, .sa(.src0, .sub_size), ._, ._ }, 42328 .{ .@"0:", ._, .mov, .tmp1d, .memia(.src0d, .tmp0, .add_size), ._, ._ }, 42329 .{ ._, ._, .xor, .tmp1d, .memia(.src1d, .tmp0, .add_size), ._, ._ }, 42330 .{ ._, ._nz, .j, .@"0f", ._, ._, ._ }, 42331 .{ ._, ._, .add, .tmp0p, .si(4), ._, ._ }, 42332 .{ ._, ._nz, .j, .@"0b", ._, ._, ._ }, 42333 } }, 42334 } }); 42335 }, 42336 } 42337 if (switch (op) { 42338 .lt, .gt, .eq => false, 42339 .lte, .gte, .neq => true, 42340 }) { 42341 const cc = &res[0].unwrap(cg).temp.tracking(cg).short.eflags; 42342 cc.* = cc.negate(); 42343 } 42344 lhs.*, rhs.* = ops; 42345 return res[0]; 42346 } 42347 42348 fn finish( 42349 temp: Temp, 42350 inst: Air.Inst.Index, 42351 op_refs: []const Air.Inst.Ref, 42352 op_temps: []const Temp, 42353 cg: *CodeGen, 42354 ) !void { 42355 const tomb_bits = cg.liveness.getTombBits(inst); 42356 for (0.., op_refs, op_temps) |op_index, op_ref, op_temp| { 42357 if (op_temp.index != temp.index) try op_temp.die(cg); 42358 if (tomb_bits & @as(Liveness.Bpi, 1) << @intCast(op_index) == 0) continue; 42359 if (cg.reused_operands.isSet(op_index)) continue; 42360 try cg.processDeath(op_ref.toIndexAllowNone() orelse continue); 42361 } 42362 if (cg.liveness.isUnused(inst)) try temp.die(cg) else switch (temp.unwrap(cg)) { 42363 .ref, .err_ret_trace => { 42364 const result = try cg.allocRegOrMem(inst, true); 42365 try cg.genCopy(cg.typeOfIndex(inst), result, temp.tracking(cg).short, .{}); 42366 tracking_log.debug("{} => {} (birth)", .{ inst, result }); 42367 cg.inst_tracking.putAssumeCapacityNoClobber(inst, .init(result)); 42368 }, 42369 .temp => |temp_index| { 42370 const temp_tracking = temp_index.tracking(cg); 42371 tracking_log.debug("{} => {} (birth)", .{ inst, temp_tracking.short }); 42372 cg.inst_tracking.putAssumeCapacityNoClobber(inst, temp_tracking.*); 42373 assert(cg.reuseTemp(inst, temp_index.toIndex(), temp_tracking)); 42374 }, 42375 } 42376 } 42377 42378 fn die(temp: Temp, cg: *CodeGen) !void { 42379 switch (temp.unwrap(cg)) { 42380 .ref, .err_ret_trace => {}, 42381 .temp => |temp_index| try temp_index.tracking(cg).die(cg, temp_index.toIndex()), 42382 } 42383 } 42384 42385 const Index = enum(u5) { 42386 _, 42387 42388 fn toIndex(index: Index) Air.Inst.Index { 42389 return .fromTargetIndex(@intFromEnum(index)); 42390 } 42391 42392 fn fromIndex(index: Air.Inst.Index) Index { 42393 return @enumFromInt(index.toTargetIndex()); 42394 } 42395 42396 fn tracking(index: Index, cg: *CodeGen) *InstTracking { 42397 return &cg.inst_tracking.values()[@intFromEnum(index)]; 42398 } 42399 42400 fn isValid(index: Index, cg: *CodeGen) bool { 42401 return index.tracking(cg).short != .dead; 42402 } 42403 42404 fn typeOf(index: Index, cg: *CodeGen) Type { 42405 assert(index.isValid(cg)); 42406 return cg.temp_type[@intFromEnum(index)]; 42407 } 42408 42409 const max = std.math.maxInt(@typeInfo(Index).@"enum".tag_type); 42410 const Set = std.StaticBitSet(max); 42411 const SafetySet = if (std.debug.runtime_safety) Set else struct { 42412 inline fn initEmpty() @This() { 42413 return .{}; 42414 } 42415 42416 inline fn isSet(_: @This(), index: usize) bool { 42417 assert(index < max); 42418 return true; 42419 } 42420 42421 inline fn set(_: @This(), index: usize) void { 42422 assert(index < max); 42423 } 42424 42425 inline fn eql(_: @This(), _: @This()) bool { 42426 return true; 42427 } 42428 }; 42429 }; 42430 }; 42431 42432 fn resetTemps(cg: *CodeGen) !void { 42433 var any_valid = false; 42434 for (0..@intFromEnum(cg.next_temp_index)) |temp_index| { 42435 const temp: Temp.Index = @enumFromInt(temp_index); 42436 if (temp.isValid(cg)) { 42437 any_valid = true; 42438 tracking_log.err("failed to kill {}: {}", .{ 42439 temp.toIndex(), 42440 cg.temp_type[temp_index].fmt(cg.pt), 42441 }); 42442 } 42443 cg.temp_type[temp_index] = undefined; 42444 } 42445 if (any_valid) return cg.fail("failed to kill all temps", .{}); 42446 cg.next_temp_index = @enumFromInt(0); 42447 } 42448 42449 fn reuseTemp( 42450 cg: *CodeGen, 42451 new_inst: Air.Inst.Index, 42452 old_inst: Air.Inst.Index, 42453 tracking: *InstTracking, 42454 ) bool { 42455 switch (tracking.short) { 42456 .register, 42457 .register_pair, 42458 .register_offset, 42459 .register_overflow, 42460 .register_mask, 42461 .indirect, 42462 => for (tracking.short.getRegs()) |tracked_reg| { 42463 if (RegisterManager.indexOfRegIntoTracked(tracked_reg)) |tracked_index| { 42464 cg.register_manager.registers[tracked_index] = new_inst; 42465 } 42466 }, 42467 .load_frame => |frame_addr| if (frame_addr.index.isNamed()) return false, 42468 else => {}, 42469 } 42470 switch (tracking.short) { 42471 .eflags, .register_overflow => cg.eflags_inst = new_inst, 42472 else => {}, 42473 } 42474 tracking.reuse(cg, new_inst, old_inst); 42475 return true; 42476 } 42477 42478 fn tempAlloc(cg: *CodeGen, ty: Type) !Temp { 42479 const temp_index = cg.next_temp_index; 42480 temp_index.tracking(cg).* = .init( 42481 try cg.allocRegOrMemAdvanced(ty, temp_index.toIndex(), true), 42482 ); 42483 cg.temp_type[@intFromEnum(temp_index)] = ty; 42484 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42485 return .{ .index = temp_index.toIndex() }; 42486 } 42487 42488 fn tempAllocReg(cg: *CodeGen, ty: Type, rs: RegisterManager.RegisterBitSet) !Temp { 42489 const temp_index = cg.next_temp_index; 42490 temp_index.tracking(cg).* = .init( 42491 .{ .register = try cg.register_manager.allocReg(temp_index.toIndex(), rs) }, 42492 ); 42493 cg.temp_type[@intFromEnum(temp_index)] = ty; 42494 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42495 return .{ .index = temp_index.toIndex() }; 42496 } 42497 42498 fn tempAllocRegPair(cg: *CodeGen, ty: Type, rs: RegisterManager.RegisterBitSet) !Temp { 42499 const temp_index = cg.next_temp_index; 42500 temp_index.tracking(cg).* = .init( 42501 .{ .register_pair = try cg.register_manager.allocRegs(2, temp_index.toIndex(), rs) }, 42502 ); 42503 cg.temp_type[@intFromEnum(temp_index)] = ty; 42504 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42505 return .{ .index = temp_index.toIndex() }; 42506 } 42507 42508 fn tempAllocMem(cg: *CodeGen, ty: Type) !Temp { 42509 const temp_index = cg.next_temp_index; 42510 temp_index.tracking(cg).* = .init( 42511 try cg.allocRegOrMemAdvanced(ty, temp_index.toIndex(), false), 42512 ); 42513 cg.temp_type[@intFromEnum(temp_index)] = ty; 42514 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42515 return .{ .index = temp_index.toIndex() }; 42516 } 42517 42518 fn tempInit(cg: *CodeGen, ty: Type, value: MCValue) !Temp { 42519 const temp_index = cg.next_temp_index; 42520 temp_index.tracking(cg).* = .init(value); 42521 cg.temp_type[@intFromEnum(temp_index)] = ty; 42522 try cg.getValue(value, temp_index.toIndex()); 42523 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42524 return .{ .index = temp_index.toIndex() }; 42525 } 42526 42527 fn tempFromValue(cg: *CodeGen, value: Value) !Temp { 42528 return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.genTypedValue(value)); 42529 } 42530 42531 fn tempMemFromValue(cg: *CodeGen, value: Value) !Temp { 42532 return cg.tempInit(value.typeOf(cg.pt.zcu), try cg.lowerUav(value)); 42533 } 42534 42535 fn tempFromOperand( 42536 cg: *CodeGen, 42537 inst: Air.Inst.Index, 42538 op_index: Liveness.OperandInt, 42539 op_ref: Air.Inst.Ref, 42540 ignore_death: bool, 42541 ) !Temp { 42542 const zcu = cg.pt.zcu; 42543 const ip = &zcu.intern_pool; 42544 42545 if (ignore_death or !cg.liveness.operandDies(inst, op_index)) { 42546 if (op_ref.toIndex()) |op_inst| return .{ .index = op_inst }; 42547 const val = op_ref.toInterned().?; 42548 const gop = try cg.const_tracking.getOrPut(cg.gpa, val); 42549 if (!gop.found_existing) gop.value_ptr.* = .init(init: { 42550 const const_mcv = try cg.genTypedValue(.fromInterned(val)); 42551 switch (const_mcv) { 42552 .lea_tlv => |tlv_sym| switch (cg.bin_file.tag) { 42553 .elf, .macho => { 42554 if (cg.mod.pic) { 42555 try cg.spillRegisters(&.{ .rdi, .rax }); 42556 } else { 42557 try cg.spillRegisters(&.{.rax}); 42558 } 42559 const frame_index = try cg.allocFrameIndex(.init(.{ 42560 .size = 8, 42561 .alignment = .@"8", 42562 })); 42563 try cg.genSetMem( 42564 .{ .frame = frame_index }, 42565 0, 42566 .usize, 42567 .{ .lea_symbol = .{ .sym_index = tlv_sym } }, 42568 .{}, 42569 ); 42570 break :init .{ .load_frame = .{ .index = frame_index } }; 42571 }, 42572 else => break :init const_mcv, 42573 }, 42574 else => break :init const_mcv, 42575 } 42576 }); 42577 return cg.tempInit(.fromInterned(ip.typeOf(val)), gop.value_ptr.short); 42578 } 42579 42580 const temp_index = cg.next_temp_index; 42581 const temp: Temp = .{ .index = temp_index.toIndex() }; 42582 const op_inst = op_ref.toIndex().?; 42583 const tracking = cg.getResolvedInstValue(op_inst); 42584 temp_index.tracking(cg).* = tracking.*; 42585 if (!cg.reuseTemp(temp.index, op_inst, tracking)) return .{ .index = op_ref.toIndex().? }; 42586 cg.temp_type[@intFromEnum(temp_index)] = cg.typeOf(op_ref); 42587 cg.next_temp_index = @enumFromInt(@intFromEnum(temp_index) + 1); 42588 return temp; 42589 } 42590 42591 inline fn tempsFromOperands(cg: *CodeGen, inst: Air.Inst.Index, op_refs: anytype) ![op_refs.len]Temp { 42592 var temps: [op_refs.len]Temp = undefined; 42593 inline for (&temps, 0.., op_refs) |*temp, op_index, op_ref| { 42594 temp.* = try cg.tempFromOperand(inst, op_index, op_ref, inline for (0..op_index) |prev_op_index| { 42595 if (op_ref == op_refs[prev_op_index]) break true; 42596 } else false); 42597 } 42598 return temps; 42599 } 42600 42601 const Operand = union(enum) { 42602 none, 42603 reg: Register, 42604 mem: Memory, 42605 imm: Immediate, 42606 inst: Mir.Inst.Index, 42607 }; 42608 42609 const Select = struct { 42610 cg: *CodeGen, 42611 temps: [@intFromEnum(Select.Operand.Ref.none)]Temp, 42612 labels: [@intFromEnum(Label._)]struct { 42613 backward: ?Mir.Inst.Index, 42614 forward: [1]?Mir.Inst.Index, 42615 }, 42616 top: u3, 42617 42618 fn emitLabel(s: *Select, label_index: Label) void { 42619 if (label_index == ._) return; 42620 const label = &s.labels[@intFromEnum(label_index)]; 42621 for (&label.forward) |*reloc| { 42622 if (reloc.*) |r| s.cg.performReloc(r); 42623 reloc.* = null; 42624 } 42625 label.backward = @intCast(s.cg.mir_instructions.len); 42626 } 42627 42628 fn emit(s: *Select, inst: Instruction) !void { 42629 s.emitLabel(inst[0]); 42630 const mir_tag: Mir.Inst.FixedTag = .{ inst[1], inst[2] }; 42631 var mir_ops: [4]CodeGen.Operand = undefined; 42632 inline for (&mir_ops, 3..) |*mir_op, inst_index| mir_op.* = try inst[inst_index].lower(s); 42633 s.cg.asmOps(mir_tag, mir_ops) catch |err| switch (err) { 42634 error.InvalidInstruction => { 42635 const fixes = @tagName(mir_tag[0]); 42636 const fixes_blank = std.mem.indexOfScalar(u8, fixes, '_').?; 42637 return s.cg.fail( 42638 "invalid instruction: '{s}{s}{s} {s} {s} {s} {s}'", 42639 .{ 42640 fixes[0..fixes_blank], 42641 @tagName(mir_tag[1]), 42642 fixes[fixes_blank + 1 ..], 42643 @tagName(mir_ops[0]), 42644 @tagName(mir_ops[1]), 42645 @tagName(mir_ops[2]), 42646 @tagName(mir_ops[3]), 42647 }, 42648 ); 42649 }, 42650 else => |e| return e, 42651 }; 42652 switch (mir_tag[0]) { 42653 .f_ => switch (mir_tag[1]) { 42654 .@"2xm1", 42655 .abs, 42656 .add, 42657 .chs, 42658 .clex, 42659 .com, 42660 .comi, 42661 .cos, 42662 .div, 42663 .divr, 42664 .free, 42665 .mul, 42666 .nop, 42667 .prem, 42668 .rndint, 42669 .scale, 42670 .sin, 42671 .sqrt, 42672 .st, 42673 .sub, 42674 .subr, 42675 .tst, 42676 .ucom, 42677 .ucomi, 42678 .wait, 42679 .xam, 42680 .xch, 42681 => {}, 42682 .init, .save => s.top = 0, 42683 .ld, .ptan, .sincos, .xtract => s.top -%= 1, 42684 .patan, .yl2x => s.top +%= 1, 42685 .rstor => unreachable, 42686 else => unreachable, 42687 }, 42688 .f_1 => switch (mir_tag[1]) { 42689 .ld => s.top -%= 1, 42690 .prem => {}, 42691 else => unreachable, 42692 }, 42693 .f_b, .f_be, .f_e, .f_nb, .f_nbe, .f_ne, .f_nu, .f_u => switch (mir_tag[1]) { 42694 .cmov => {}, 42695 else => unreachable, 42696 }, 42697 .f_cw, .f_env, .f_sw => switch (mir_tag[1]) { 42698 .ld, .st => {}, 42699 else => unreachable, 42700 }, 42701 .f_p1 => switch (mir_tag[1]) { 42702 .yl2x => s.top +%= 1, 42703 else => unreachable, 42704 }, 42705 .fb_ => switch (mir_tag[1]) { 42706 .ld => s.top -%= 1, 42707 else => unreachable, 42708 }, 42709 .fb_p => switch (mir_tag[1]) { 42710 .st => s.top +%= 1, 42711 else => unreachable, 42712 }, 42713 .fi_ => switch (mir_tag[1]) { 42714 .add, .com, .div, .divr, .mul, .st, .stt, .sub, .subr => {}, 42715 .ld => s.top -%= 1, 42716 else => unreachable, 42717 }, 42718 .fi_p => switch (mir_tag[1]) { 42719 .com, .st => s.top +%= 1, 42720 else => unreachable, 42721 }, 42722 .fn_ => switch (mir_tag[1]) { 42723 .clex => {}, 42724 .init, .save => s.top = 0, 42725 else => unreachable, 42726 }, 42727 .fn_cw, .fn_env, .fn_sw => switch (mir_tag[1]) { 42728 .st => {}, 42729 else => unreachable, 42730 }, 42731 .f_cstp => switch (mir_tag[1]) { 42732 .de => s.top -%= 1, 42733 .in => s.top +%= 1, 42734 else => unreachable, 42735 }, 42736 .f_l2e, .f_l2t, .f_lg2, .f_ln2, .f_pi, .f_z => switch (mir_tag[1]) { 42737 .ld => s.top -%= 1, 42738 else => unreachable, 42739 }, 42740 .f_p => switch (mir_tag[1]) { 42741 .add, .com, .comi, .div, .divr, .mul, .st, .sub, .subr, .ucom, .ucomi => s.top +%= 1, 42742 else => { 42743 const fixes = @tagName(mir_tag[0]); 42744 const fixes_blank = std.mem.indexOfScalar(u8, fixes, '_').?; 42745 std.debug.panic("{s}: {s}{s}{s}\n", .{ 42746 @src().fn_name, 42747 fixes[0..fixes_blank], 42748 @tagName(mir_tag[1]), 42749 fixes[fixes_blank + 1 ..], 42750 }); 42751 }, 42752 }, 42753 .f_pp => switch (mir_tag[1]) { 42754 .com, .ucom => s.top +%= 2, 42755 else => unreachable, 42756 }, 42757 .fx_ => switch (mir_tag[1]) { 42758 .rstor => unreachable, 42759 .save => {}, 42760 else => unreachable, 42761 }, 42762 else => {}, 42763 } 42764 } 42765 42766 fn lowerReg(s: *const Select, reg: Register) Register { 42767 if (reg.class() != .x87) return reg; 42768 return @enumFromInt(@intFromEnum(Register.st0) + (@as(u3, @intCast(reg.enc())) -% s.top)); 42769 } 42770 42771 const Case = struct { 42772 required_features: [4]?std.Target.x86.Feature = @splat(null), 42773 dst_constraints: [@intFromEnum(Select.Operand.Ref.src0) - @intFromEnum(Select.Operand.Ref.dst0)]Constraint = @splat(.any), 42774 src_constraints: [@intFromEnum(Select.Operand.Ref.none) - @intFromEnum(Select.Operand.Ref.src0)]Constraint = @splat(.any), 42775 patterns: []const Select.Pattern, 42776 call_frame: packed struct(u16) { size: u10 = 0, alignment: InternPool.Alignment } = .{ .size = 0, .alignment = .none }, 42777 extra_temps: [@intFromEnum(Select.Operand.Ref.dst0) - @intFromEnum(Select.Operand.Ref.tmp0)]TempSpec = @splat(.unused), 42778 dst_temps: [@intFromEnum(Select.Operand.Ref.src0) - @intFromEnum(Select.Operand.Ref.dst0)]TempSpec.Kind = @splat(.unused), 42779 clobbers: packed struct { 42780 eflags: bool = false, 42781 caller_preserved: enum(u2) { none, ccc, zigcc } = .none, 42782 } = .{}, 42783 each: union(enum) { 42784 once: []const Instruction, 42785 }, 42786 }; 42787 42788 const Constraint = union(enum) { 42789 any, 42790 any_bool_vec, 42791 any_int, 42792 any_signed_int, 42793 any_unsigned_int, 42794 any_scalar_int, 42795 any_scalar_signed_int, 42796 any_scalar_unsigned_int, 42797 any_float, 42798 po2_any, 42799 bool_vec: Memory.Size, 42800 vec: Memory.Size, 42801 signed_int_vec: Memory.Size, 42802 signed_int_or_full_vec: Memory.Size, 42803 unsigned_int_vec: Memory.Size, 42804 size: Memory.Size, 42805 multiple_size: Memory.Size, 42806 int: Memory.Size, 42807 scalar_int_is: Memory.Size, 42808 scalar_signed_int_is: Memory.Size, 42809 scalar_unsigned_int_is: Memory.Size, 42810 scalar_int: OfIsSizes, 42811 scalar_signed_int: OfIsSizes, 42812 scalar_unsigned_int: OfIsSizes, 42813 multiple_scalar_int: OfIsSizes, 42814 multiple_scalar_signed_int: OfIsSizes, 42815 multiple_scalar_unsigned_int: OfIsSizes, 42816 scalar_remainder_int: OfIsSizes, 42817 float: Memory.Size, 42818 scalar_any_float: Memory.Size, 42819 scalar_float: OfIsSizes, 42820 multiple_scalar_any_float: Memory.Size, 42821 multiple_scalar_float: OfIsSizes, 42822 exact_int: u16, 42823 exact_signed_int: u16, 42824 exact_unsigned_int: u16, 42825 signed_or_exact_int: Memory.Size, 42826 unsigned_or_exact_int: Memory.Size, 42827 po2_int: Memory.Size, 42828 signed_po2_int: Memory.Size, 42829 unsigned_po2_or_exact_int: Memory.Size, 42830 remainder_int: OfIsSizes, 42831 signed_remainder_int: OfIsSizes, 42832 unsigned_remainder_int: OfIsSizes, 42833 exact_remainder_int: OfIsSizes, 42834 signed_or_exact_remainder_int: OfIsSizes, 42835 unsigned_or_exact_remainder_int: OfIsSizes, 42836 signed_int: Memory.Size, 42837 unsigned_int: Memory.Size, 42838 elem_size_is: u8, 42839 po2_elem_size, 42840 elem_int: Memory.Size, 42841 42842 const OfIsSizes = struct { of: Memory.Size, is: Memory.Size }; 42843 42844 fn accepts(constraint: Constraint, ty: Type, cg: *CodeGen) bool { 42845 const zcu = cg.pt.zcu; 42846 return switch (constraint) { 42847 .any => true, 42848 .any_bool_vec => ty.isVector(zcu) and ty.childType(zcu).toIntern() == .bool_type, 42849 .any_int => cg.intInfo(ty) != null, 42850 .any_signed_int => if (cg.intInfo(ty)) |int_info| int_info.signedness == .signed else false, 42851 .any_unsigned_int => if (cg.intInfo(ty)) |int_info| int_info.signedness == .unsigned else false, 42852 .any_scalar_int => cg.intInfo(ty.scalarType(zcu)) != null, 42853 .any_scalar_signed_int => if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .signed else false, 42854 .any_scalar_unsigned_int => if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .unsigned else false, 42855 .any_float => ty.isRuntimeFloat(), 42856 .po2_any => std.math.isPowerOfTwo(ty.abiSize(zcu)), 42857 .bool_vec => |size| ty.isVector(zcu) and ty.scalarType(zcu).toIntern() == .bool_type and 42858 size.bitSize(cg.target) >= ty.vectorLen(zcu), 42859 .vec => |size| ty.isVector(zcu) and ty.scalarType(zcu).toIntern() != .bool_type and 42860 size.bitSize(cg.target) >= ty.abiSize(zcu), 42861 .signed_int_vec => |size| ty.isVector(zcu) and @divExact(size.bitSize(cg.target), 8) >= ty.abiSize(zcu) and 42862 if (cg.intInfo(ty.childType(zcu))) |int_info| int_info.signedness == .signed else false, 42863 .signed_int_or_full_vec => |size| ty.isVector(zcu) and @divExact(size.bitSize(cg.target), 8) >= ty.abiSize(zcu) and 42864 if (cg.intInfo(ty.childType(zcu))) |int_info| switch (int_info.signedness) { 42865 .signed => true, 42866 .unsigned => int_info.bits >= 8 and std.math.isPowerOfTwo(int_info.bits), 42867 } else false, 42868 .unsigned_int_vec => |size| ty.isVector(zcu) and @divExact(size.bitSize(cg.target), 8) >= ty.abiSize(zcu) and 42869 if (cg.intInfo(ty.childType(zcu))) |int_info| int_info.signedness == .unsigned else false, 42870 .size => |size| @divExact(size.bitSize(cg.target), 8) >= ty.abiSize(zcu), 42871 .multiple_size => |size| ty.abiSize(zcu) % @divExact(size.bitSize(cg.target), 8) == 0, 42872 .int => |size| if (cg.intInfo(ty)) |int_info| size.bitSize(cg.target) >= int_info.bits else false, 42873 .scalar_int_is => |size| if (cg.intInfo(ty.scalarType(zcu))) |int_info| 42874 size.bitSize(cg.target) >= int_info.bits 42875 else 42876 false, 42877 .scalar_signed_int_is => |size| if (cg.intInfo(ty.scalarType(zcu))) |int_info| switch (int_info.signedness) { 42878 .signed => size.bitSize(cg.target) >= int_info.bits, 42879 .unsigned => false, 42880 } else false, 42881 .scalar_unsigned_int_is => |size| if (cg.intInfo(ty.scalarType(zcu))) |int_info| switch (int_info.signedness) { 42882 .signed => false, 42883 .unsigned => size.bitSize(cg.target) >= int_info.bits, 42884 } else false, 42885 .scalar_int => |of_is| @divExact(of_is.of.bitSize(cg.target), 8) >= cg.unalignedSize(ty) and 42886 if (cg.intInfo(ty.scalarType(zcu))) |int_info| of_is.is.bitSize(cg.target) >= int_info.bits else false, 42887 .scalar_signed_int => |of_is| @divExact(of_is.of.bitSize(cg.target), 8) >= cg.unalignedSize(ty) and 42888 if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .signed and 42889 of_is.is.bitSize(cg.target) >= int_info.bits else false, 42890 .scalar_unsigned_int => |of_is| @divExact(of_is.of.bitSize(cg.target), 8) >= cg.unalignedSize(ty) and 42891 if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .unsigned and 42892 of_is.is.bitSize(cg.target) >= int_info.bits else false, 42893 .multiple_scalar_int => |of_is| ty.abiSize(zcu) % @divExact(of_is.of.bitSize(cg.target), 8) == 0 and 42894 if (cg.intInfo(ty.scalarType(zcu))) |int_info| of_is.is.bitSize(cg.target) >= int_info.bits else false, 42895 .multiple_scalar_signed_int => |of_is| ty.abiSize(zcu) % @divExact(of_is.of.bitSize(cg.target), 8) == 0 and 42896 if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .signed and 42897 of_is.is.bitSize(cg.target) >= int_info.bits else false, 42898 .multiple_scalar_unsigned_int => |of_is| ty.abiSize(zcu) % @divExact(of_is.of.bitSize(cg.target), 8) == 0 and 42899 if (cg.intInfo(ty.scalarType(zcu))) |int_info| int_info.signedness == .unsigned and 42900 of_is.is.bitSize(cg.target) >= int_info.bits else false, 42901 .scalar_remainder_int => |of_is| if (cg.intInfo(ty.scalarType(zcu))) |int_info| 42902 of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1 42903 else 42904 false, 42905 .float => |size| if (cg.floatBits(ty)) |float_bits| size.bitSize(cg.target) == float_bits else false, 42906 .scalar_any_float => |size| @divExact(size.bitSize(cg.target), 8) >= ty.abiSize(zcu) and 42907 cg.floatBits(ty.scalarType(zcu)) != null, 42908 .scalar_float => |of_is| @divExact(of_is.of.bitSize(cg.target), 8) >= cg.unalignedSize(ty) and 42909 if (cg.floatBits(ty.scalarType(zcu))) |float_bits| of_is.is.bitSize(cg.target) == float_bits else false, 42910 .multiple_scalar_any_float => |size| ty.abiSize(zcu) % @divExact(size.bitSize(cg.target), 8) == 0 and 42911 cg.floatBits(ty.scalarType(zcu)) != null, 42912 .multiple_scalar_float => |of_is| ty.abiSize(zcu) % @divExact(of_is.of.bitSize(cg.target), 8) == 0 and 42913 if (cg.floatBits(ty.scalarType(zcu))) |float_bits| of_is.is.bitSize(cg.target) == float_bits else false, 42914 .exact_int => |bit_size| if (cg.intInfo(ty)) |int_info| bit_size == int_info.bits else false, 42915 .exact_signed_int => |bit_size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42916 .signed => bit_size == int_info.bits, 42917 .unsigned => false, 42918 } else false, 42919 .exact_unsigned_int => |bit_size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42920 .signed => false, 42921 .unsigned => bit_size == int_info.bits, 42922 } else false, 42923 .signed_or_exact_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42924 .signed => size.bitSize(cg.target) >= int_info.bits, 42925 .unsigned => size.bitSize(cg.target) == int_info.bits, 42926 } else false, 42927 .unsigned_or_exact_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42928 .signed => size.bitSize(cg.target) == int_info.bits, 42929 .unsigned => size.bitSize(cg.target) >= int_info.bits, 42930 } else false, 42931 .po2_int => |size| if (cg.intInfo(ty)) |int_info| 42932 std.math.isPowerOfTwo(int_info.bits) and size.bitSize(cg.target) >= int_info.bits 42933 else 42934 false, 42935 .signed_po2_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42936 .signed => std.math.isPowerOfTwo(int_info.bits) and size.bitSize(cg.target) >= int_info.bits, 42937 .unsigned => false, 42938 } else false, 42939 .unsigned_po2_or_exact_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42940 .signed => size.bitSize(cg.target) == int_info.bits, 42941 .unsigned => std.math.isPowerOfTwo(int_info.bits) and size.bitSize(cg.target) >= int_info.bits, 42942 } else false, 42943 .remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| 42944 of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1 42945 else 42946 false, 42947 .signed_remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| int_info.signedness == .signed and 42948 of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1 else false, 42949 .unsigned_remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| int_info.signedness == .unsigned and 42950 of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1 else false, 42951 .exact_remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| 42952 of_is.is.bitSize(cg.target) == (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1 42953 else 42954 false, 42955 .signed_or_exact_remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42956 .signed => of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1, 42957 .unsigned => of_is.is.bitSize(cg.target) == (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1, 42958 } else false, 42959 .unsigned_or_exact_remainder_int => |of_is| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42960 .signed => of_is.is.bitSize(cg.target) == (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1, 42961 .unsigned => of_is.is.bitSize(cg.target) >= (int_info.bits - 1) % of_is.of.bitSize(cg.target) + 1, 42962 } else false, 42963 .signed_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42964 .signed => size.bitSize(cg.target) >= int_info.bits, 42965 .unsigned => false, 42966 } else false, 42967 .unsigned_int => |size| if (cg.intInfo(ty)) |int_info| switch (int_info.signedness) { 42968 .signed => false, 42969 .unsigned => size.bitSize(cg.target) >= int_info.bits, 42970 } else false, 42971 .elem_size_is => |size| size == ty.elemType2(zcu).abiSize(zcu), 42972 .po2_elem_size => std.math.isPowerOfTwo(ty.elemType2(zcu).abiSize(zcu)), 42973 .elem_int => |size| if (cg.intInfo(ty.elemType2(zcu))) |elem_int_info| 42974 size.bitSize(cg.target) >= elem_int_info.bits 42975 else 42976 false, 42977 }; 42978 } 42979 }; 42980 42981 const Pattern = struct { 42982 src: [2]Src, 42983 commute: struct { u8, u8 } = .{ 0, 0 }, 42984 42985 const Src = union(enum) { 42986 none, 42987 any, 42988 imm8, 42989 imm16, 42990 imm32, 42991 simm32, 42992 to_reg: Register, 42993 mem, 42994 to_mem, 42995 mut_mem, 42996 to_mut_mem, 42997 gpr, 42998 to_gpr, 42999 mut_gpr, 43000 to_mut_gpr, 43001 x87, 43002 to_x87, 43003 mut_x87, 43004 to_mut_x87, 43005 mmx, 43006 to_mmx, 43007 mut_mmx, 43008 to_mut_mmx, 43009 mm, 43010 to_mm, 43011 mut_mm, 43012 to_mut_mm, 43013 sse, 43014 to_sse, 43015 mut_sse, 43016 to_mut_sse, 43017 xmm, 43018 to_xmm, 43019 mut_xmm, 43020 to_mut_xmm, 43021 ymm, 43022 to_ymm, 43023 mut_ymm, 43024 to_mut_ymm, 43025 43026 fn matches(src: Src, temp: Temp, cg: *CodeGen) bool { 43027 return switch (src) { 43028 .none => unreachable, 43029 .any => true, 43030 .imm8 => switch (temp.tracking(cg).short) { 43031 .immediate => |imm| std.math.cast(u8, imm) != null, 43032 else => false, 43033 }, 43034 .imm16 => switch (temp.tracking(cg).short) { 43035 .immediate => |imm| std.math.cast(u16, imm) != null, 43036 else => false, 43037 }, 43038 .imm32 => switch (temp.tracking(cg).short) { 43039 .immediate => |imm| std.math.cast(u32, imm) != null, 43040 else => false, 43041 }, 43042 .simm32 => switch (temp.tracking(cg).short) { 43043 .immediate => |imm| std.math.cast(i32, @as(i64, @bitCast(imm))) != null, 43044 else => false, 43045 }, 43046 .mem => temp.tracking(cg).short.isMemory(), 43047 .to_mem, .to_mut_mem => true, 43048 .mut_mem => temp.isMut(cg) and temp.tracking(cg).short.isMemory(), 43049 .to_reg => true, 43050 .gpr => temp.typeOf(cg).abiSize(cg.pt.zcu) <= 8 and switch (temp.tracking(cg).short) { 43051 .register => |reg| reg.class() == .general_purpose, 43052 .register_offset => |reg_off| reg_off.reg.class() == .general_purpose and reg_off.off == 0, 43053 else => false, 43054 }, 43055 .mut_gpr => temp.isMut(cg) and temp.typeOf(cg).abiSize(cg.pt.zcu) <= 8 and switch (temp.tracking(cg).short) { 43056 .register => |reg| reg.class() == .general_purpose, 43057 .register_offset => |reg_off| reg_off.reg.class() == .general_purpose and reg_off.off == 0, 43058 else => false, 43059 }, 43060 .to_gpr, .to_mut_gpr => temp.typeOf(cg).abiSize(cg.pt.zcu) <= 8, 43061 .x87 => switch (temp.tracking(cg).short) { 43062 .register => |reg| reg.class() == .x87, 43063 .register_offset => |reg_off| reg_off.reg.class() == .x87 and reg_off.off == 0, 43064 else => false, 43065 }, 43066 .mut_x87 => temp.isMut(cg) and switch (temp.tracking(cg).short) { 43067 .register => |reg| reg.class() == .x87, 43068 .register_offset => |reg_off| reg_off.reg.class() == .x87 and reg_off.off == 0, 43069 else => false, 43070 }, 43071 .to_x87, .to_mut_x87 => true, 43072 .mmx => switch (temp.tracking(cg).short) { 43073 .register => |reg| reg.class() == .mmx, 43074 .register_offset => |reg_off| reg_off.reg.class() == .mmx and reg_off.off == 0, 43075 else => false, 43076 }, 43077 .mut_mmx => temp.isMut(cg) and switch (temp.tracking(cg).short) { 43078 .register => |reg| reg.class() == .mmx, 43079 .register_offset => |reg_off| reg_off.reg.class() == .mmx and reg_off.off == 0, 43080 else => false, 43081 }, 43082 .to_mmx, .to_mut_mmx => true, 43083 .mm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 8 and switch (temp.tracking(cg).short) { 43084 .register => |reg| reg.class() == .mmx, 43085 .register_offset => |reg_off| reg_off.reg.class() == .mmx and reg_off.off == 0, 43086 else => false, 43087 }, 43088 .mut_mm => temp.isMut(cg) and temp.typeOf(cg).abiSize(cg.pt.zcu) == 8 and switch (temp.tracking(cg).short) { 43089 .register => |reg| reg.class() == .mmx, 43090 .register_offset => |reg_off| reg_off.reg.class() == .mmx and reg_off.off == 0, 43091 else => false, 43092 }, 43093 .to_mm, .to_mut_mm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 8, 43094 .sse => switch (temp.tracking(cg).short) { 43095 .register => |reg| reg.class() == .sse, 43096 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43097 else => false, 43098 }, 43099 .mut_sse => temp.isMut(cg) and switch (temp.tracking(cg).short) { 43100 .register => |reg| reg.class() == .sse, 43101 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43102 else => false, 43103 }, 43104 .to_sse, .to_mut_sse => true, 43105 .xmm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 16 and switch (temp.tracking(cg).short) { 43106 .register => |reg| reg.class() == .sse, 43107 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43108 else => false, 43109 }, 43110 .mut_xmm => temp.isMut(cg) and temp.typeOf(cg).abiSize(cg.pt.zcu) == 16 and switch (temp.tracking(cg).short) { 43111 .register => |reg| reg.class() == .sse, 43112 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43113 else => false, 43114 }, 43115 .to_xmm, .to_mut_xmm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 16, 43116 .ymm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 32 and switch (temp.tracking(cg).short) { 43117 .register => |reg| reg.class() == .sse, 43118 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43119 else => false, 43120 }, 43121 .mut_ymm => temp.isMut(cg) and temp.typeOf(cg).abiSize(cg.pt.zcu) == 32 and switch (temp.tracking(cg).short) { 43122 .register => |reg| reg.class() == .sse, 43123 .register_offset => |reg_off| reg_off.reg.class() == .sse and reg_off.off == 0, 43124 else => false, 43125 }, 43126 .to_ymm, .to_mut_ymm => temp.typeOf(cg).abiSize(cg.pt.zcu) == 32, 43127 }; 43128 } 43129 43130 fn convert(src: Src, temp: *Temp, cg: *CodeGen) !bool { 43131 return switch (src) { 43132 .none => unreachable, 43133 .any, .imm8, .imm16, .imm32, .simm32 => false, 43134 .mem, .to_mem, .mut_mem, .to_mut_mem => try temp.toBase(cg), 43135 .to_reg => |reg| try temp.toReg(reg, cg), 43136 .gpr, .to_gpr => try temp.toRegClass(false, .general_purpose, cg), 43137 .mut_gpr, .to_mut_gpr => try temp.toRegClass(true, .general_purpose, cg), 43138 .x87, .to_x87 => try temp.toRegClass(false, .x87, cg), 43139 .mut_x87, .to_mut_x87 => try temp.toRegClass(true, .x87, cg), 43140 .mmx, .to_mmx, .mm, .to_mm => try temp.toRegClass(false, .mmx, cg), 43141 .mut_mmx, .to_mut_mmx, .mut_mm, .to_mut_mm => try temp.toRegClass(true, .mmx, cg), 43142 .sse, .to_sse, .xmm, .to_xmm, .ymm, .to_ymm => try temp.toRegClass(false, .sse, cg), 43143 .mut_sse, .to_mut_sse, .mut_xmm, .to_mut_xmm, .mut_ymm, .to_mut_ymm => try temp.toRegClass(true, .sse, cg), 43144 }; 43145 } 43146 }; 43147 }; 43148 43149 const TempSpec = struct { 43150 type: Type = .noreturn, 43151 kind: Kind, 43152 43153 const unused: TempSpec = .{ .kind = .unused }; 43154 43155 const Kind = union(enum) { 43156 unused, 43157 any, 43158 cc: Condition, 43159 ref: Select.Operand.Ref, 43160 reg: Register, 43161 rc: Register.Class, 43162 mut_rc: struct { ref: Select.Operand.Ref, rc: Register.Class }, 43163 ref_mask: struct { ref: Select.Operand.Ref, info: MaskInfo }, 43164 rc_mask: struct { rc: Register.Class, info: MaskInfo }, 43165 mut_rc_mask: struct { ref: Select.Operand.Ref, rc: Register.Class, info: MaskInfo }, 43166 mem, 43167 smin_mem: ConstInfo, 43168 smax_mem: ConstInfo, 43169 umin_mem: ConstInfo, 43170 umax_mem: ConstInfo, 43171 symbol: *const struct { lib: ?[]const u8 = null, name: []const u8 }, 43172 43173 const ConstInfo = struct { ref: Select.Operand.Ref, vectorize_to: ?Memory.Size = null }; 43174 43175 fn finish(kind: Kind, temp: Temp, s: *const Select) void { 43176 switch (kind) { 43177 else => {}, 43178 inline .rc_mask, .mut_rc_mask, .ref_mask => |mask| temp.asMask(mask.info, s.cg), 43179 } 43180 } 43181 43182 fn pass(kind: Kind) u2 { 43183 return switch (kind) { 43184 .unused => 0, 43185 .reg => 1, 43186 else => 2, 43187 }; 43188 } 43189 }; 43190 43191 fn create(spec: TempSpec, s: *Select) !struct { Temp, bool } { 43192 const cg = s.cg; 43193 return switch (spec.kind) { 43194 .unused => unreachable, 43195 .any => .{ try cg.tempAlloc(spec.type), true }, 43196 .cc => |cc| .{ try cg.tempInit(spec.type, .{ .eflags = cc }), true }, 43197 .ref => |ref| .{ ref.deref(s), false }, 43198 .reg => |reg| .{ try cg.tempInit(spec.type, .{ .register = reg }), true }, 43199 .rc => |rc| .{ try cg.tempAllocReg(spec.type, regSetForRegClass(rc)), true }, 43200 .mut_rc => |ref_rc| { 43201 const temp = ref_rc.ref.deref(s); 43202 if (temp.isMut(cg)) switch (temp.tracking(cg).short) { 43203 .register => |reg| if (reg.class() == ref_rc.rc) return .{ temp, false }, 43204 .register_offset => |reg_off| if (reg_off.off == 0 and reg_off.reg.class() == ref_rc.rc) return .{ temp, false }, 43205 else => {}, 43206 }; 43207 return .{ try cg.tempAllocReg(spec.type, regSetForRegClass(ref_rc.rc)), true }; 43208 }, 43209 .ref_mask => |ref_mask| .{ ref_mask.ref.deref(s), false }, 43210 .rc_mask => |rc_mask| .{ try cg.tempAllocReg(spec.type, regSetForRegClass(rc_mask.rc)), true }, 43211 .mut_rc_mask => |ref_rc_mask| { 43212 const temp = ref_rc_mask.ref.deref(s); 43213 if (temp.isMut(cg)) switch (temp.tracking(cg).short) { 43214 .register => |reg| if (reg.class() == ref_rc_mask.rc) return .{ temp, false }, 43215 .register_offset => |reg_off| if (reg_off.off == 0 and reg_off.reg.class() == ref_rc_mask.rc) return .{ temp, false }, 43216 else => {}, 43217 }; 43218 return .{ try cg.tempAllocReg(spec.type, regSetForRegClass(ref_rc_mask.rc)), true }; 43219 }, 43220 .mem => .{ try cg.tempAllocMem(spec.type), true }, 43221 .smin_mem, .smax_mem, .umin_mem, .umax_mem => |const_info| { 43222 const pt = cg.pt; 43223 const zcu = pt.zcu; 43224 const ip = &zcu.intern_pool; 43225 const ty = const_info.ref.deref(s).typeOf(cg); 43226 const vector_len, const scalar_ty: Type = switch (ip.indexToKey(ty.toIntern())) { 43227 else => .{ null, ty }, 43228 .vector_type => |vector_type| .{ vector_type.len, .fromInterned(vector_type.child) }, 43229 }; 43230 const res_vector_len: ?u32 = if (const_info.vectorize_to) |vectorize_to| switch (vectorize_to) { 43231 .none => null, 43232 else => @intCast(@divExact(@divExact(vectorize_to.bitSize(cg.target), 8), scalar_ty.abiSize(pt.zcu))), 43233 } else vector_len; 43234 const res_scalar_ty, const res_scalar_val: Value = res_scalar: switch (scalar_ty.toIntern()) { 43235 .bool_type => .{ 43236 scalar_ty, 43237 .fromInterned(switch (spec.kind) { 43238 else => unreachable, 43239 .smin_mem, .umax_mem => .bool_true, 43240 .smax_mem, .umin_mem => .bool_false, 43241 }), 43242 }, 43243 else => { 43244 const scalar_info: std.builtin.Type.Int = cg.intInfo(scalar_ty) orelse .{ 43245 .signedness = .signed, 43246 .bits = cg.floatBits(scalar_ty).?, 43247 }; 43248 const scalar_int_ty = try pt.intType(scalar_info.signedness, scalar_info.bits); 43249 if (scalar_info.bits <= 64) { 43250 const int_val: i64 = switch (spec.kind) { 43251 else => unreachable, 43252 .smin_mem => std.math.minInt(i64), 43253 .smax_mem => std.math.maxInt(i64), 43254 .umin_mem => 0, 43255 .umax_mem => -1, 43256 }; 43257 const shift: u6 = @intCast(64 - scalar_info.bits); 43258 break :res_scalar .{ scalar_int_ty, switch (scalar_info.signedness) { 43259 .signed => try pt.intValue_i64(scalar_int_ty, int_val >> shift), 43260 .unsigned => try pt.intValue_u64(scalar_int_ty, @as(u64, @bitCast(int_val)) >> shift), 43261 } }; 43262 } 43263 var big_int: std.math.big.int.Managed = try .init(cg.gpa); 43264 defer big_int.deinit(); 43265 try big_int.setTwosCompIntLimit(switch (spec.kind) { 43266 else => unreachable, 43267 .smin_mem, .umin_mem => .min, 43268 .smax_mem, .umax_mem => .max, 43269 }, switch (spec.kind) { 43270 else => unreachable, 43271 .smin_mem, .smax_mem => .signed, 43272 .umin_mem, .umax_mem => .unsigned, 43273 }, scalar_info.bits); 43274 try big_int.truncate(&big_int, scalar_info.signedness, scalar_info.bits); 43275 break :res_scalar .{ scalar_int_ty, try pt.intValue_big(scalar_int_ty, big_int.toConst()) }; 43276 }, 43277 }; 43278 const res_val: Value = if (res_vector_len) |len| .fromInterned(try pt.intern(.{ .aggregate = .{ 43279 .ty = (try pt.vectorType(.{ 43280 .len = len, 43281 .child = res_scalar_ty.toIntern(), 43282 })).toIntern(), 43283 .storage = .{ .repeated_elem = res_scalar_val.toIntern() }, 43284 } })) else res_scalar_val; 43285 return .{ try cg.tempMemFromValue(res_val), true }; 43286 }, 43287 .symbol => |symbol| .{ try cg.tempInit(spec.type, .{ .lea_symbol = .{ 43288 .sym_index = if (cg.bin_file.cast(.elf)) |elf_file| 43289 try elf_file.getGlobalSymbol(symbol.name, symbol.lib) 43290 else if (cg.bin_file.cast(.macho)) |macho_file| 43291 try macho_file.getGlobalSymbol(symbol.name, symbol.lib) 43292 else 43293 return cg.fail("external symbols unimplemented for {s}", .{@tagName(cg.bin_file.tag)}), 43294 } }), true }, 43295 }; 43296 } 43297 }; 43298 43299 const Instruction = struct { 43300 Label, 43301 Mir.Inst.Fixes, 43302 Mir.Inst.Tag, 43303 Select.Operand, 43304 Select.Operand, 43305 Select.Operand, 43306 Select.Operand, 43307 }; 43308 const Label = enum { @"0:", @"1:", @"2:", @"_" }; 43309 const Operand = struct { 43310 tag: Tag, 43311 base: Ref.Sized = .none, 43312 index: packed struct(u6) { 43313 ref: Ref, 43314 scale: Memory.Scale, 43315 } = .{ .ref = .none, .scale = .@"1" }, 43316 adjust: Adjust = .none, 43317 imm: i32 = 0, 43318 43319 const Tag = enum { 43320 none, 43321 backward_label, 43322 forward_label, 43323 ref, 43324 simm, 43325 uimm, 43326 lea, 43327 mem, 43328 }; 43329 const Adjust = packed struct(u8) { 43330 sign: enum(u1) { neg, pos }, 43331 lhs: enum(u4) { 43332 none, 43333 ptr_size, 43334 ptr_bit_size, 43335 size, 43336 size_sub_elem_size, 43337 src0_unaligned_size, 43338 bit_size, 43339 src0_bit_size, 43340 len, 43341 elem_limbs, 43342 src0_elem_size, 43343 src0_elem_size_times_src1, 43344 log2_src0_elem_size, 43345 smin, 43346 smax, 43347 umax, 43348 }, 43349 op: enum(u1) { mul, div }, 43350 rhs: Memory.Scale, 43351 43352 const none: Adjust = .{ .sign = .pos, .lhs = .none, .op = .mul, .rhs = .@"1" }; 43353 const sub_ptr_size: Adjust = .{ .sign = .neg, .lhs = .ptr_size, .op = .mul, .rhs = .@"1" }; 43354 const add_ptr_bit_size: Adjust = .{ .sign = .pos, .lhs = .ptr_bit_size, .op = .mul, .rhs = .@"1" }; 43355 const add_size: Adjust = .{ .sign = .pos, .lhs = .size, .op = .mul, .rhs = .@"1" }; 43356 const add_size_div_8: Adjust = .{ .sign = .pos, .lhs = .size, .op = .div, .rhs = .@"8" }; 43357 const sub_size_div_8: Adjust = .{ .sign = .neg, .lhs = .size, .op = .div, .rhs = .@"8" }; 43358 const sub_size_div_4: Adjust = .{ .sign = .neg, .lhs = .size, .op = .div, .rhs = .@"4" }; 43359 const sub_size: Adjust = .{ .sign = .neg, .lhs = .size, .op = .mul, .rhs = .@"1" }; 43360 const add_size_sub_elem_size: Adjust = .{ .sign = .pos, .lhs = .size_sub_elem_size, .op = .mul, .rhs = .@"1" }; 43361 const add_src0_unaligned_size: Adjust = .{ .sign = .pos, .lhs = .src0_unaligned_size, .op = .mul, .rhs = .@"1" }; 43362 const sub_src0_unaligned_size: Adjust = .{ .sign = .neg, .lhs = .src0_unaligned_size, .op = .mul, .rhs = .@"1" }; 43363 const add_2_bit_size: Adjust = .{ .sign = .pos, .lhs = .bit_size, .op = .mul, .rhs = .@"2" }; 43364 const add_bit_size: Adjust = .{ .sign = .pos, .lhs = .bit_size, .op = .mul, .rhs = .@"1" }; 43365 const sub_bit_size: Adjust = .{ .sign = .neg, .lhs = .bit_size, .op = .mul, .rhs = .@"1" }; 43366 const add_src0_bit_size: Adjust = .{ .sign = .pos, .lhs = .src0_bit_size, .op = .mul, .rhs = .@"1" }; 43367 const sub_src0_bit_size: Adjust = .{ .sign = .neg, .lhs = .src0_bit_size, .op = .mul, .rhs = .@"1" }; 43368 const add_8_len: Adjust = .{ .sign = .pos, .lhs = .len, .op = .mul, .rhs = .@"8" }; 43369 const add_4_len: Adjust = .{ .sign = .pos, .lhs = .len, .op = .mul, .rhs = .@"4" }; 43370 const add_3_len: Adjust = .{ .sign = .pos, .lhs = .len, .op = .mul, .rhs = .@"3" }; 43371 const add_2_len: Adjust = .{ .sign = .pos, .lhs = .len, .op = .mul, .rhs = .@"2" }; 43372 const add_len: Adjust = .{ .sign = .pos, .lhs = .len, .op = .mul, .rhs = .@"1" }; 43373 const sub_len: Adjust = .{ .sign = .neg, .lhs = .len, .op = .mul, .rhs = .@"1" }; 43374 const add_src0_elem_size: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"1" }; 43375 const add_2_src0_elem_size: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"2" }; 43376 const add_4_src0_elem_size: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"4" }; 43377 const add_8_src0_elem_size: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"8" }; 43378 const add_src0_elem_size_div_8: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size, .op = .div, .rhs = .@"8" }; 43379 const sub_src0_elem_size: Adjust = .{ .sign = .neg, .lhs = .src0_elem_size, .op = .mul, .rhs = .@"1" }; 43380 const add_src0_elem_size_times_src1: Adjust = .{ .sign = .pos, .lhs = .src0_elem_size_times_src1, .op = .mul, .rhs = .@"1" }; 43381 const sub_src0_elem_size_times_src1: Adjust = .{ .sign = .neg, .lhs = .src0_elem_size_times_src1, .op = .mul, .rhs = .@"1" }; 43382 const add_log2_src0_elem_size: Adjust = .{ .sign = .pos, .lhs = .log2_src0_elem_size, .op = .mul, .rhs = .@"1" }; 43383 const add_elem_limbs: Adjust = .{ .sign = .pos, .lhs = .elem_limbs, .op = .mul, .rhs = .@"1" }; 43384 const add_umax: Adjust = .{ .sign = .pos, .lhs = .umax, .op = .mul, .rhs = .@"1" }; 43385 }; 43386 const Ref = enum(u4) { 43387 tmp0, 43388 tmp1, 43389 tmp2, 43390 tmp3, 43391 tmp4, 43392 tmp5, 43393 tmp6, 43394 tmp7, 43395 tmp8, 43396 dst0, 43397 src0, 43398 src1, 43399 none, 43400 43401 const Sized = packed struct(u8) { 43402 ref: Ref, 43403 size: Memory.Size, 43404 43405 const none: Sized = .{ .ref = .none, .size = .none }; 43406 43407 const tmp0: Sized = .{ .ref = .tmp0, .size = .none }; 43408 const tmp0b: Sized = .{ .ref = .tmp0, .size = .byte }; 43409 const tmp0w: Sized = .{ .ref = .tmp0, .size = .word }; 43410 const tmp0d: Sized = .{ .ref = .tmp0, .size = .dword }; 43411 const tmp0p: Sized = .{ .ref = .tmp0, .size = .ptr }; 43412 const tmp0q: Sized = .{ .ref = .tmp0, .size = .qword }; 43413 const tmp0t: Sized = .{ .ref = .tmp0, .size = .tbyte }; 43414 const tmp0x: Sized = .{ .ref = .tmp0, .size = .xword }; 43415 const tmp0y: Sized = .{ .ref = .tmp0, .size = .yword }; 43416 43417 const tmp1: Sized = .{ .ref = .tmp1, .size = .none }; 43418 const tmp1b: Sized = .{ .ref = .tmp1, .size = .byte }; 43419 const tmp1w: Sized = .{ .ref = .tmp1, .size = .word }; 43420 const tmp1d: Sized = .{ .ref = .tmp1, .size = .dword }; 43421 const tmp1p: Sized = .{ .ref = .tmp1, .size = .ptr }; 43422 const tmp1q: Sized = .{ .ref = .tmp1, .size = .qword }; 43423 const tmp1t: Sized = .{ .ref = .tmp1, .size = .tbyte }; 43424 const tmp1x: Sized = .{ .ref = .tmp1, .size = .xword }; 43425 const tmp1y: Sized = .{ .ref = .tmp1, .size = .yword }; 43426 43427 const tmp2: Sized = .{ .ref = .tmp2, .size = .none }; 43428 const tmp2b: Sized = .{ .ref = .tmp2, .size = .byte }; 43429 const tmp2w: Sized = .{ .ref = .tmp2, .size = .word }; 43430 const tmp2d: Sized = .{ .ref = .tmp2, .size = .dword }; 43431 const tmp2p: Sized = .{ .ref = .tmp2, .size = .ptr }; 43432 const tmp2q: Sized = .{ .ref = .tmp2, .size = .qword }; 43433 const tmp2t: Sized = .{ .ref = .tmp2, .size = .tbyte }; 43434 const tmp2x: Sized = .{ .ref = .tmp2, .size = .xword }; 43435 const tmp2y: Sized = .{ .ref = .tmp2, .size = .yword }; 43436 43437 const tmp3: Sized = .{ .ref = .tmp3, .size = .none }; 43438 const tmp3b: Sized = .{ .ref = .tmp3, .size = .byte }; 43439 const tmp3w: Sized = .{ .ref = .tmp3, .size = .word }; 43440 const tmp3d: Sized = .{ .ref = .tmp3, .size = .dword }; 43441 const tmp3p: Sized = .{ .ref = .tmp3, .size = .ptr }; 43442 const tmp3q: Sized = .{ .ref = .tmp3, .size = .qword }; 43443 const tmp3t: Sized = .{ .ref = .tmp3, .size = .tbyte }; 43444 const tmp3x: Sized = .{ .ref = .tmp3, .size = .xword }; 43445 const tmp3y: Sized = .{ .ref = .tmp3, .size = .yword }; 43446 43447 const tmp4: Sized = .{ .ref = .tmp4, .size = .none }; 43448 const tmp4b: Sized = .{ .ref = .tmp4, .size = .byte }; 43449 const tmp4w: Sized = .{ .ref = .tmp4, .size = .word }; 43450 const tmp4d: Sized = .{ .ref = .tmp4, .size = .dword }; 43451 const tmp4p: Sized = .{ .ref = .tmp4, .size = .ptr }; 43452 const tmp4q: Sized = .{ .ref = .tmp4, .size = .qword }; 43453 const tmp4t: Sized = .{ .ref = .tmp4, .size = .tbyte }; 43454 const tmp4x: Sized = .{ .ref = .tmp4, .size = .xword }; 43455 const tmp4y: Sized = .{ .ref = .tmp4, .size = .yword }; 43456 43457 const tmp5: Sized = .{ .ref = .tmp5, .size = .none }; 43458 const tmp5b: Sized = .{ .ref = .tmp5, .size = .byte }; 43459 const tmp5w: Sized = .{ .ref = .tmp5, .size = .word }; 43460 const tmp5d: Sized = .{ .ref = .tmp5, .size = .dword }; 43461 const tmp5p: Sized = .{ .ref = .tmp5, .size = .ptr }; 43462 const tmp5q: Sized = .{ .ref = .tmp5, .size = .qword }; 43463 const tmp5t: Sized = .{ .ref = .tmp5, .size = .tbyte }; 43464 const tmp5x: Sized = .{ .ref = .tmp5, .size = .xword }; 43465 const tmp5y: Sized = .{ .ref = .tmp5, .size = .yword }; 43466 43467 const tmp6: Sized = .{ .ref = .tmp6, .size = .none }; 43468 const tmp6b: Sized = .{ .ref = .tmp6, .size = .byte }; 43469 const tmp6w: Sized = .{ .ref = .tmp6, .size = .word }; 43470 const tmp6d: Sized = .{ .ref = .tmp6, .size = .dword }; 43471 const tmp6p: Sized = .{ .ref = .tmp6, .size = .ptr }; 43472 const tmp6q: Sized = .{ .ref = .tmp6, .size = .qword }; 43473 const tmp6t: Sized = .{ .ref = .tmp6, .size = .tbyte }; 43474 const tmp6x: Sized = .{ .ref = .tmp6, .size = .xword }; 43475 const tmp6y: Sized = .{ .ref = .tmp6, .size = .yword }; 43476 43477 const tmp7: Sized = .{ .ref = .tmp7, .size = .none }; 43478 const tmp7b: Sized = .{ .ref = .tmp7, .size = .byte }; 43479 const tmp7w: Sized = .{ .ref = .tmp7, .size = .word }; 43480 const tmp7d: Sized = .{ .ref = .tmp7, .size = .dword }; 43481 const tmp7p: Sized = .{ .ref = .tmp7, .size = .ptr }; 43482 const tmp7q: Sized = .{ .ref = .tmp7, .size = .qword }; 43483 const tmp7t: Sized = .{ .ref = .tmp7, .size = .tbyte }; 43484 const tmp7x: Sized = .{ .ref = .tmp7, .size = .xword }; 43485 const tmp7y: Sized = .{ .ref = .tmp7, .size = .yword }; 43486 43487 const tmp8: Sized = .{ .ref = .tmp8, .size = .none }; 43488 const tmp8b: Sized = .{ .ref = .tmp8, .size = .byte }; 43489 const tmp8w: Sized = .{ .ref = .tmp8, .size = .word }; 43490 const tmp8d: Sized = .{ .ref = .tmp8, .size = .dword }; 43491 const tmp8p: Sized = .{ .ref = .tmp8, .size = .ptr }; 43492 const tmp8q: Sized = .{ .ref = .tmp8, .size = .qword }; 43493 const tmp8t: Sized = .{ .ref = .tmp8, .size = .tbyte }; 43494 const tmp8x: Sized = .{ .ref = .tmp8, .size = .xword }; 43495 const tmp8y: Sized = .{ .ref = .tmp8, .size = .yword }; 43496 43497 const dst0: Sized = .{ .ref = .dst0, .size = .none }; 43498 const dst0b: Sized = .{ .ref = .dst0, .size = .byte }; 43499 const dst0w: Sized = .{ .ref = .dst0, .size = .word }; 43500 const dst0d: Sized = .{ .ref = .dst0, .size = .dword }; 43501 const dst0p: Sized = .{ .ref = .dst0, .size = .ptr }; 43502 const dst0q: Sized = .{ .ref = .dst0, .size = .qword }; 43503 const dst0t: Sized = .{ .ref = .dst0, .size = .tbyte }; 43504 const dst0x: Sized = .{ .ref = .dst0, .size = .xword }; 43505 const dst0y: Sized = .{ .ref = .dst0, .size = .yword }; 43506 43507 const src0: Sized = .{ .ref = .src0, .size = .none }; 43508 const src0b: Sized = .{ .ref = .src0, .size = .byte }; 43509 const src0w: Sized = .{ .ref = .src0, .size = .word }; 43510 const src0d: Sized = .{ .ref = .src0, .size = .dword }; 43511 const src0p: Sized = .{ .ref = .src0, .size = .ptr }; 43512 const src0q: Sized = .{ .ref = .src0, .size = .qword }; 43513 const src0t: Sized = .{ .ref = .src0, .size = .tbyte }; 43514 const src0x: Sized = .{ .ref = .src0, .size = .xword }; 43515 const src0y: Sized = .{ .ref = .src0, .size = .yword }; 43516 43517 const src1: Sized = .{ .ref = .src1, .size = .none }; 43518 const src1b: Sized = .{ .ref = .src1, .size = .byte }; 43519 const src1w: Sized = .{ .ref = .src1, .size = .word }; 43520 const src1d: Sized = .{ .ref = .src1, .size = .dword }; 43521 const src1p: Sized = .{ .ref = .src1, .size = .ptr }; 43522 const src1q: Sized = .{ .ref = .src1, .size = .qword }; 43523 const src1t: Sized = .{ .ref = .src1, .size = .tbyte }; 43524 const src1x: Sized = .{ .ref = .src1, .size = .xword }; 43525 const src1y: Sized = .{ .ref = .src1, .size = .yword }; 43526 }; 43527 43528 fn deref(ref: Ref, s: *const Select) Temp { 43529 return s.temps[@intFromEnum(ref)]; 43530 } 43531 }; 43532 43533 const @"_": Select.Operand = .{ .tag = .none }; 43534 43535 const @"0b": Select.Operand = .{ .tag = .backward_label, .base = .{ .ref = .tmp0, .size = .none } }; 43536 const @"0f": Select.Operand = .{ .tag = .forward_label, .base = .{ .ref = .tmp0, .size = .none } }; 43537 const @"1b": Select.Operand = .{ .tag = .backward_label, .base = .{ .ref = .tmp1, .size = .none } }; 43538 const @"1f": Select.Operand = .{ .tag = .forward_label, .base = .{ .ref = .tmp1, .size = .none } }; 43539 const @"2b": Select.Operand = .{ .tag = .backward_label, .base = .{ .ref = .tmp2, .size = .none } }; 43540 const @"2f": Select.Operand = .{ .tag = .forward_label, .base = .{ .ref = .tmp2, .size = .none } }; 43541 43542 const tmp0b: Select.Operand = .{ .tag = .ref, .base = .tmp0b }; 43543 const tmp0w: Select.Operand = .{ .tag = .ref, .base = .tmp0w }; 43544 const tmp0d: Select.Operand = .{ .tag = .ref, .base = .tmp0d }; 43545 const tmp0p: Select.Operand = .{ .tag = .ref, .base = .tmp0p }; 43546 const tmp0q: Select.Operand = .{ .tag = .ref, .base = .tmp0q }; 43547 const tmp0t: Select.Operand = .{ .tag = .ref, .base = .tmp0t }; 43548 const tmp0x: Select.Operand = .{ .tag = .ref, .base = .tmp0x }; 43549 const tmp0y: Select.Operand = .{ .tag = .ref, .base = .tmp0y }; 43550 43551 const tmp1b: Select.Operand = .{ .tag = .ref, .base = .tmp1b }; 43552 const tmp1w: Select.Operand = .{ .tag = .ref, .base = .tmp1w }; 43553 const tmp1d: Select.Operand = .{ .tag = .ref, .base = .tmp1d }; 43554 const tmp1p: Select.Operand = .{ .tag = .ref, .base = .tmp1p }; 43555 const tmp1q: Select.Operand = .{ .tag = .ref, .base = .tmp1q }; 43556 const tmp1t: Select.Operand = .{ .tag = .ref, .base = .tmp1t }; 43557 const tmp1x: Select.Operand = .{ .tag = .ref, .base = .tmp1x }; 43558 const tmp1y: Select.Operand = .{ .tag = .ref, .base = .tmp1y }; 43559 43560 const tmp2b: Select.Operand = .{ .tag = .ref, .base = .tmp2b }; 43561 const tmp2w: Select.Operand = .{ .tag = .ref, .base = .tmp2w }; 43562 const tmp2d: Select.Operand = .{ .tag = .ref, .base = .tmp2d }; 43563 const tmp2p: Select.Operand = .{ .tag = .ref, .base = .tmp2p }; 43564 const tmp2q: Select.Operand = .{ .tag = .ref, .base = .tmp2q }; 43565 const tmp2t: Select.Operand = .{ .tag = .ref, .base = .tmp2t }; 43566 const tmp2x: Select.Operand = .{ .tag = .ref, .base = .tmp2x }; 43567 const tmp2y: Select.Operand = .{ .tag = .ref, .base = .tmp2y }; 43568 43569 const tmp3b: Select.Operand = .{ .tag = .ref, .base = .tmp3b }; 43570 const tmp3w: Select.Operand = .{ .tag = .ref, .base = .tmp3w }; 43571 const tmp3d: Select.Operand = .{ .tag = .ref, .base = .tmp3d }; 43572 const tmp3p: Select.Operand = .{ .tag = .ref, .base = .tmp3p }; 43573 const tmp3q: Select.Operand = .{ .tag = .ref, .base = .tmp3q }; 43574 const tmp3t: Select.Operand = .{ .tag = .ref, .base = .tmp3t }; 43575 const tmp3x: Select.Operand = .{ .tag = .ref, .base = .tmp3x }; 43576 const tmp3y: Select.Operand = .{ .tag = .ref, .base = .tmp3y }; 43577 43578 const tmp4b: Select.Operand = .{ .tag = .ref, .base = .tmp4b }; 43579 const tmp4w: Select.Operand = .{ .tag = .ref, .base = .tmp4w }; 43580 const tmp4d: Select.Operand = .{ .tag = .ref, .base = .tmp4d }; 43581 const tmp4p: Select.Operand = .{ .tag = .ref, .base = .tmp4p }; 43582 const tmp4q: Select.Operand = .{ .tag = .ref, .base = .tmp4q }; 43583 const tmp4t: Select.Operand = .{ .tag = .ref, .base = .tmp4t }; 43584 const tmp4x: Select.Operand = .{ .tag = .ref, .base = .tmp4x }; 43585 const tmp4y: Select.Operand = .{ .tag = .ref, .base = .tmp4y }; 43586 43587 const tmp5b: Select.Operand = .{ .tag = .ref, .base = .tmp5b }; 43588 const tmp5w: Select.Operand = .{ .tag = .ref, .base = .tmp5w }; 43589 const tmp5d: Select.Operand = .{ .tag = .ref, .base = .tmp5d }; 43590 const tmp5p: Select.Operand = .{ .tag = .ref, .base = .tmp5p }; 43591 const tmp5q: Select.Operand = .{ .tag = .ref, .base = .tmp5q }; 43592 const tmp5t: Select.Operand = .{ .tag = .ref, .base = .tmp5t }; 43593 const tmp5x: Select.Operand = .{ .tag = .ref, .base = .tmp5x }; 43594 const tmp5y: Select.Operand = .{ .tag = .ref, .base = .tmp5y }; 43595 43596 const tmp6b: Select.Operand = .{ .tag = .ref, .base = .tmp6b }; 43597 const tmp6w: Select.Operand = .{ .tag = .ref, .base = .tmp6w }; 43598 const tmp6d: Select.Operand = .{ .tag = .ref, .base = .tmp6d }; 43599 const tmp6p: Select.Operand = .{ .tag = .ref, .base = .tmp6p }; 43600 const tmp6q: Select.Operand = .{ .tag = .ref, .base = .tmp6q }; 43601 const tmp6t: Select.Operand = .{ .tag = .ref, .base = .tmp6t }; 43602 const tmp6x: Select.Operand = .{ .tag = .ref, .base = .tmp6x }; 43603 const tmp6y: Select.Operand = .{ .tag = .ref, .base = .tmp6y }; 43604 43605 const tmp7b: Select.Operand = .{ .tag = .ref, .base = .tmp7b }; 43606 const tmp7w: Select.Operand = .{ .tag = .ref, .base = .tmp7w }; 43607 const tmp7d: Select.Operand = .{ .tag = .ref, .base = .tmp7d }; 43608 const tmp7p: Select.Operand = .{ .tag = .ref, .base = .tmp7p }; 43609 const tmp7q: Select.Operand = .{ .tag = .ref, .base = .tmp7q }; 43610 const tmp7t: Select.Operand = .{ .tag = .ref, .base = .tmp7t }; 43611 const tmp7x: Select.Operand = .{ .tag = .ref, .base = .tmp7x }; 43612 const tmp7y: Select.Operand = .{ .tag = .ref, .base = .tmp7y }; 43613 43614 const tmp8b: Select.Operand = .{ .tag = .ref, .base = .tmp8b }; 43615 const tmp8w: Select.Operand = .{ .tag = .ref, .base = .tmp8w }; 43616 const tmp8d: Select.Operand = .{ .tag = .ref, .base = .tmp8d }; 43617 const tmp8p: Select.Operand = .{ .tag = .ref, .base = .tmp8p }; 43618 const tmp8q: Select.Operand = .{ .tag = .ref, .base = .tmp8q }; 43619 const tmp8t: Select.Operand = .{ .tag = .ref, .base = .tmp8t }; 43620 const tmp8x: Select.Operand = .{ .tag = .ref, .base = .tmp8x }; 43621 const tmp8y: Select.Operand = .{ .tag = .ref, .base = .tmp8y }; 43622 43623 const dst0b: Select.Operand = .{ .tag = .ref, .base = .dst0b }; 43624 const dst0w: Select.Operand = .{ .tag = .ref, .base = .dst0w }; 43625 const dst0d: Select.Operand = .{ .tag = .ref, .base = .dst0d }; 43626 const dst0p: Select.Operand = .{ .tag = .ref, .base = .dst0p }; 43627 const dst0q: Select.Operand = .{ .tag = .ref, .base = .dst0q }; 43628 const dst0t: Select.Operand = .{ .tag = .ref, .base = .dst0t }; 43629 const dst0x: Select.Operand = .{ .tag = .ref, .base = .dst0x }; 43630 const dst0y: Select.Operand = .{ .tag = .ref, .base = .dst0y }; 43631 43632 const src0b: Select.Operand = .{ .tag = .ref, .base = .src0b }; 43633 const src0w: Select.Operand = .{ .tag = .ref, .base = .src0w }; 43634 const src0d: Select.Operand = .{ .tag = .ref, .base = .src0d }; 43635 const src0p: Select.Operand = .{ .tag = .ref, .base = .src0p }; 43636 const src0q: Select.Operand = .{ .tag = .ref, .base = .src0q }; 43637 const src0t: Select.Operand = .{ .tag = .ref, .base = .src0t }; 43638 const src0x: Select.Operand = .{ .tag = .ref, .base = .src0x }; 43639 const src0y: Select.Operand = .{ .tag = .ref, .base = .src0y }; 43640 43641 const src1b: Select.Operand = .{ .tag = .ref, .base = .src1b }; 43642 const src1w: Select.Operand = .{ .tag = .ref, .base = .src1w }; 43643 const src1d: Select.Operand = .{ .tag = .ref, .base = .src1d }; 43644 const src1p: Select.Operand = .{ .tag = .ref, .base = .src1p }; 43645 const src1q: Select.Operand = .{ .tag = .ref, .base = .src1q }; 43646 const src1t: Select.Operand = .{ .tag = .ref, .base = .src1t }; 43647 const src1x: Select.Operand = .{ .tag = .ref, .base = .src1x }; 43648 const src1y: Select.Operand = .{ .tag = .ref, .base = .src1y }; 43649 43650 fn si(imm: i32) Select.Operand { 43651 return .{ .tag = .simm, .imm = imm }; 43652 } 43653 fn sa(base: Ref.Sized, adjust: Adjust) Select.Operand { 43654 return .{ .tag = .simm, .base = base, .adjust = adjust }; 43655 } 43656 fn sia(imm: i32, base: Ref.Sized, adjust: Adjust) Select.Operand { 43657 return .{ .tag = .simm, .base = base, .adjust = adjust, .imm = imm }; 43658 } 43659 fn ui(imm: u32) Select.Operand { 43660 return .{ .tag = .uimm, .imm = @bitCast(imm) }; 43661 } 43662 fn ua(base: Ref.Sized, adjust: Adjust) Select.Operand { 43663 return .{ .tag = .uimm, .base = base, .adjust = adjust }; 43664 } 43665 fn uia(imm: u32, base: Ref.Sized, adjust: Adjust) Select.Operand { 43666 return .{ .tag = .uimm, .base = base, .adjust = adjust, .imm = @bitCast(imm) }; 43667 } 43668 43669 fn rm(mode: bits.RoundMode) Select.Operand { 43670 return .{ .tag = .uimm, .imm = @intCast(mode.imm().unsigned) }; 43671 } 43672 fn sp(pred: bits.SseFloatPredicate) Select.Operand { 43673 return .{ .tag = .uimm, .imm = @intCast(pred.imm().unsigned) }; 43674 } 43675 fn vp(pred: bits.VexFloatPredicate) Select.Operand { 43676 return .{ .tag = .uimm, .imm = @intCast(pred.imm().unsigned) }; 43677 } 43678 43679 fn lea(size: Memory.Size, base: Ref) Select.Operand { 43680 return .{ 43681 .tag = .lea, 43682 .base = .{ .ref = base, .size = size }, 43683 }; 43684 } 43685 fn leaa(size: Memory.Size, base: Ref, adjust: Adjust) Select.Operand { 43686 return .{ 43687 .tag = .lea, 43688 .base = .{ .ref = base, .size = size }, 43689 .adjust = adjust, 43690 }; 43691 } 43692 fn lead(size: Memory.Size, base: Ref, disp: i32) Select.Operand { 43693 return .{ 43694 .tag = .lea, 43695 .base = .{ .ref = base, .size = size }, 43696 .imm = disp, 43697 }; 43698 } 43699 fn leai(size: Memory.Size, base: Ref, index: Ref) Select.Operand { 43700 return .{ 43701 .tag = .lea, 43702 .base = .{ .ref = base, .size = size }, 43703 .index = .{ .ref = index, .scale = .@"1" }, 43704 }; 43705 } 43706 fn leaia(size: Memory.Size, base: Ref, index: Ref, adjust: Adjust) Select.Operand { 43707 return .{ 43708 .tag = .lea, 43709 .base = .{ .ref = base, .size = size }, 43710 .index = .{ .ref = index, .scale = .@"1" }, 43711 .adjust = adjust, 43712 }; 43713 } 43714 fn leaid(size: Memory.Size, base: Ref, index: Ref, disp: i32) Select.Operand { 43715 return .{ 43716 .tag = .lea, 43717 .base = .{ .ref = base, .size = size }, 43718 .index = .{ .ref = index, .scale = .@"1" }, 43719 .imm = disp, 43720 }; 43721 } 43722 fn leasi(size: Memory.Size, base: Ref, scale: Memory.Scale, index: Ref) Select.Operand { 43723 return .{ 43724 .tag = .lea, 43725 .base = .{ .ref = base, .size = size }, 43726 .index = .{ .ref = index, .scale = scale }, 43727 }; 43728 } 43729 fn leasid(size: Memory.Size, base: Ref, scale: Memory.Scale, index: Ref, disp: i32) Select.Operand { 43730 return .{ 43731 .tag = .lea, 43732 .base = .{ .ref = base, .size = size }, 43733 .index = .{ .ref = index, .scale = scale }, 43734 .imm = disp, 43735 }; 43736 } 43737 fn leasiad(size: Memory.Size, base: Ref, scale: Memory.Scale, index: Ref, adjust: Adjust, disp: i32) Select.Operand { 43738 return .{ 43739 .tag = .lea, 43740 .base = .{ .ref = base, .size = size }, 43741 .index = .{ .ref = index, .scale = scale }, 43742 .adjust = adjust, 43743 .imm = disp, 43744 }; 43745 } 43746 43747 fn mem(base: Ref.Sized) Select.Operand { 43748 return .{ 43749 .tag = .mem, 43750 .base = base, 43751 }; 43752 } 43753 fn memd(base: Ref.Sized, disp: i32) Select.Operand { 43754 return .{ 43755 .tag = .mem, 43756 .base = base, 43757 .imm = disp, 43758 }; 43759 } 43760 fn mema(base: Ref.Sized, adjust: Adjust) Select.Operand { 43761 return .{ 43762 .tag = .mem, 43763 .base = base, 43764 .adjust = adjust, 43765 }; 43766 } 43767 fn memad(base: Ref.Sized, adjust: Adjust, disp: i32) Select.Operand { 43768 return .{ 43769 .tag = .mem, 43770 .base = base, 43771 .adjust = adjust, 43772 .imm = disp, 43773 }; 43774 } 43775 fn memi(base: Ref.Sized, index: Ref) Select.Operand { 43776 return .{ 43777 .tag = .mem, 43778 .base = base, 43779 .index = .{ .ref = index, .scale = .@"1" }, 43780 }; 43781 } 43782 fn memia(base: Ref.Sized, index: Ref, adjust: Adjust) Select.Operand { 43783 return .{ 43784 .tag = .mem, 43785 .base = base, 43786 .index = .{ .ref = index, .scale = .@"1" }, 43787 .adjust = adjust, 43788 }; 43789 } 43790 fn memiad(base: Ref.Sized, index: Ref, adjust: Adjust, disp: i32) Select.Operand { 43791 return .{ 43792 .tag = .mem, 43793 .base = base, 43794 .index = .{ .ref = index, .scale = .@"1" }, 43795 .adjust = adjust, 43796 .imm = disp, 43797 }; 43798 } 43799 fn memid(base: Ref.Sized, index: Ref, disp: i32) Select.Operand { 43800 return .{ 43801 .tag = .mem, 43802 .base = base, 43803 .index = .{ .ref = index, .scale = .@"1" }, 43804 .imm = disp, 43805 }; 43806 } 43807 fn memsi(base: Ref.Sized, scale: Memory.Scale, index: Ref) Select.Operand { 43808 return .{ 43809 .tag = .mem, 43810 .base = base, 43811 .index = .{ .ref = index, .scale = scale }, 43812 }; 43813 } 43814 fn memsia(base: Ref.Sized, scale: Memory.Scale, index: Ref, adjust: Adjust) Select.Operand { 43815 return .{ 43816 .tag = .mem, 43817 .base = base, 43818 .index = .{ .ref = index, .scale = scale }, 43819 .adjust = adjust, 43820 }; 43821 } 43822 fn memsid(base: Ref.Sized, scale: Memory.Scale, index: Ref, disp: i32) Select.Operand { 43823 return .{ 43824 .tag = .mem, 43825 .base = base, 43826 .index = .{ .ref = index, .scale = scale }, 43827 .imm = disp, 43828 }; 43829 } 43830 fn memsiad(base: Ref.Sized, scale: Memory.Scale, index: Ref, adjust: Adjust, disp: i32) Select.Operand { 43831 return .{ 43832 .tag = .mem, 43833 .base = base, 43834 .index = .{ .ref = index, .scale = scale }, 43835 .adjust = adjust, 43836 .imm = disp, 43837 }; 43838 } 43839 43840 fn adjustedImm(op: Select.Operand, comptime SignedImm: type, s: *const Select) SignedImm { 43841 const UnsignedImm = @Type(.{ 43842 .int = .{ .signedness = .unsigned, .bits = @typeInfo(SignedImm).int.bits }, 43843 }); 43844 const lhs: SignedImm = lhs: switch (op.adjust.lhs) { 43845 .none => 0, 43846 .ptr_size => @divExact(s.cg.target.ptrBitWidth(), 8), 43847 .ptr_bit_size => s.cg.target.ptrBitWidth(), 43848 .size => @intCast(op.base.ref.deref(s).typeOf(s.cg).abiSize(s.cg.pt.zcu)), 43849 .size_sub_elem_size => { 43850 const ty = op.base.ref.deref(s).typeOf(s.cg); 43851 break :lhs @intCast(ty.abiSize(s.cg.pt.zcu) - ty.elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)); 43852 }, 43853 .src0_unaligned_size => @intCast(s.cg.unalignedSize(Select.Operand.Ref.src0.deref(s).typeOf(s.cg))), 43854 .bit_size => @intCast(op.base.ref.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).bitSize(s.cg.pt.zcu)), 43855 .src0_bit_size => @intCast(Select.Operand.Ref.src0.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).bitSize(s.cg.pt.zcu)), 43856 .len => @intCast(op.base.ref.deref(s).typeOf(s.cg).vectorLen(s.cg.pt.zcu)), 43857 .elem_limbs => @intCast(@divExact( 43858 op.base.ref.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).abiSize(s.cg.pt.zcu), 43859 @divExact(op.base.size.bitSize(s.cg.target), 8), 43860 )), 43861 .src0_elem_size => @intCast(Select.Operand.Ref.src0.deref(s).typeOf(s.cg).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu)), 43862 .src0_elem_size_times_src1 => @intCast(Select.Operand.Ref.src0.deref(s).typeOf(s.cg).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu) * 43863 Select.Operand.Ref.src1.deref(s).tracking(s.cg).short.immediate), 43864 .log2_src0_elem_size => @intCast(std.math.log2(Select.Operand.Ref.src0.deref(s).typeOf(s.cg).elemType2(s.cg.pt.zcu).abiSize(s.cg.pt.zcu))), 43865 .smin => @as(SignedImm, std.math.minInt(SignedImm)) >> @truncate( 43866 -%op.base.ref.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).bitSize(s.cg.pt.zcu), 43867 ), 43868 .smax => @as(SignedImm, std.math.maxInt(SignedImm)) >> @truncate( 43869 -%op.base.ref.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).bitSize(s.cg.pt.zcu), 43870 ), 43871 .umax => @bitCast(@as(UnsignedImm, std.math.maxInt(UnsignedImm)) >> @truncate( 43872 -%op.base.ref.deref(s).typeOf(s.cg).scalarType(s.cg.pt.zcu).bitSize(s.cg.pt.zcu), 43873 )), 43874 }; 43875 const rhs = op.adjust.rhs.toLog2(); 43876 const res = res: switch (op.adjust.op) { 43877 .mul => { 43878 const res = @shlWithOverflow(lhs, rhs); 43879 assert(res[1] == 0); 43880 break :res res[0]; 43881 }, 43882 .div => @shrExact(lhs, rhs), 43883 }; 43884 return switch (op.adjust.sign) { 43885 .neg => op.imm - res, 43886 .pos => op.imm + res, 43887 }; 43888 } 43889 43890 fn lower(op: Select.Operand, s: *Select) !CodeGen.Operand { 43891 return switch (op.tag) { 43892 .none => .none, 43893 .backward_label => .{ .inst = s.labels[@intFromEnum(op.base.ref)].backward.? }, 43894 .forward_label => for (&s.labels[@intFromEnum(op.base.ref)].forward) |*label| { 43895 if (label.*) |_| continue; 43896 label.* = @intCast(s.cg.mir_instructions.len); 43897 break .{ .inst = undefined }; 43898 } else unreachable, 43899 .ref => switch (op.base.ref.deref(s).tracking(s.cg).short) { 43900 .immediate => |imm| .{ .imm = switch (op.base.size) { 43901 .byte => if (std.math.cast(i8, @as(i64, @bitCast(imm)))) |simm| .s(simm) else .u(@as(u8, @intCast(imm))), 43902 .word => if (std.math.cast(i16, @as(i64, @bitCast(imm)))) |simm| .s(simm) else .u(@as(u16, @intCast(imm))), 43903 .dword => if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |simm| .s(simm) else .u(@as(u32, @intCast(imm))), 43904 .qword => if (std.math.cast(i32, @as(i64, @bitCast(imm)))) |simm| .s(simm) else .u(imm), 43905 else => unreachable, 43906 } }, 43907 else => |mcv| .{ .mem = try mcv.mem(s.cg, .{ .size = op.base.size }) }, 43908 .register => |reg| .{ .reg = s.lowerReg(registerAlias(reg, @intCast(@divExact(op.base.size.bitSize(s.cg.target), 8)))) }, 43909 .lea_symbol => |sym_off| .{ .imm = .rel(sym_off) }, 43910 }, 43911 .simm => .{ .imm = .s(op.adjustedImm(i32, s)) }, 43912 .uimm => .{ .imm = .u(@bitCast(op.adjustedImm(i64, s))) }, 43913 .lea => .{ .mem = .{ 43914 .base = .{ .reg = registerAlias(op.base.ref.deref(s).tracking(s.cg).short.register, @divExact(s.cg.target.ptrBitWidth(), 8)) }, 43915 .mod = .{ .rm = .{ 43916 .size = op.base.size, 43917 .index = switch (op.index.ref) { 43918 else => |ref| registerAlias(ref.deref(s).tracking(s.cg).short.register, @divExact(s.cg.target.ptrBitWidth(), 8)), 43919 .none => .none, 43920 }, 43921 .scale = op.index.scale, 43922 .disp = op.adjustedImm(i32, s), 43923 } }, 43924 } }, 43925 .mem => .{ .mem = try op.base.ref.deref(s).tracking(s.cg).short.mem(s.cg, .{ 43926 .size = op.base.size, 43927 .index = switch (op.index.ref) { 43928 else => |ref| registerAlias(ref.deref(s).tracking(s.cg).short.register, @divExact(s.cg.target.ptrBitWidth(), 8)), 43929 .none => .none, 43930 }, 43931 .scale = op.index.scale, 43932 .disp = op.adjustedImm(i32, s), 43933 }) }, 43934 }; 43935 } 43936 }; 43937 }; 43938 fn select( 43939 cg: *CodeGen, 43940 dst_temps: []Temp, 43941 dst_tys: []const Type, 43942 src_temps: []Temp, 43943 cases: []const Select.Case, 43944 ) !void { 43945 @setEvalBranchQuota(33_600); 43946 cases: for (cases) |case| { 43947 for (case.required_features) |required_feature| if (required_feature) |feature| if (!cg.hasFeature(feature)) continue :cases; 43948 for (case.dst_constraints[0..dst_temps.len], dst_tys) |dst_constraint, dst_ty| if (!dst_constraint.accepts(dst_ty, cg)) continue :cases; 43949 for (case.src_constraints[0..src_temps.len], src_temps) |src_constraint, src_temp| if (!src_constraint.accepts(src_temp.typeOf(cg), cg)) continue :cases; 43950 if (std.debug.runtime_safety) { 43951 for (case.dst_constraints[dst_temps.len..]) |dst_constraint| assert(dst_constraint == .any); 43952 for (case.src_constraints[src_temps.len..]) |src_constraint| assert(src_constraint == .any); 43953 } 43954 patterns: for (case.patterns) |pattern| { 43955 for (pattern.src[0..src_temps.len], src_temps) |src_pattern, src_temp| if (!src_pattern.matches(src_temp, cg)) continue :patterns; 43956 if (std.debug.runtime_safety) for (pattern.src[src_temps.len..]) |src_pattern| assert(src_pattern == .none); 43957 43958 if (case.call_frame.alignment != .none) { 43959 const frame_allocs_slice = cg.frame_allocs.slice(); 43960 const stack_frame_size = 43961 &frame_allocs_slice.items(.abi_size)[@intFromEnum(FrameIndex.call_frame)]; 43962 stack_frame_size.* = @max(stack_frame_size.*, case.call_frame.size); 43963 const stack_frame_align = 43964 &frame_allocs_slice.items(.abi_align)[@intFromEnum(FrameIndex.call_frame)]; 43965 stack_frame_align.* = stack_frame_align.max(case.call_frame.alignment); 43966 } 43967 43968 var s: Select = .{ 43969 .cg = cg, 43970 .temps = undefined, 43971 .labels = @splat(.{ .forward = @splat(null), .backward = null }), 43972 .top = 0, 43973 }; 43974 const tmp_slots = s.temps[@intFromEnum(Select.Operand.Ref.tmp0)..@intFromEnum(Select.Operand.Ref.dst0)]; 43975 const dst_slots = s.temps[@intFromEnum(Select.Operand.Ref.dst0)..@intFromEnum(Select.Operand.Ref.src0)]; 43976 const src_slots = s.temps[@intFromEnum(Select.Operand.Ref.src0)..@intFromEnum(Select.Operand.Ref.none)]; 43977 43978 caller_preserved: { 43979 const cc = switch (case.clobbers.caller_preserved) { 43980 .none => break :caller_preserved, 43981 .ccc => cg.target.cCallingConvention().?, 43982 .zigcc => .auto, 43983 }; 43984 assert(case.clobbers.eflags); 43985 const err_ret_trace_reg = if (cc == .auto and cg.pt.zcu.comp.config.any_error_tracing) err_ret_trace_reg: { 43986 const param_gpr = abi.getCAbiIntParamRegs(.auto); 43987 break :err_ret_trace_reg param_gpr[param_gpr.len - 1]; 43988 } else .none; 43989 switch (cc) { 43990 else => unreachable, 43991 inline .x86_64_sysv, .x86_64_win, .auto => |_, tag| inline for (comptime abi.getCallerPreservedRegs(tag)) |reg| skip: { 43992 if (reg == err_ret_trace_reg) break :skip; 43993 const tracked_index = RegisterManager.indexOfKnownRegIntoTracked(reg) orelse break :skip; 43994 try cg.register_manager.getRegIndex(tracked_index, null); 43995 assert(cg.register_manager.lockRegIndexAssumeUnused(tracked_index).tracked_index == tracked_index); 43996 }, 43997 } 43998 } 43999 44000 @memcpy(src_slots[0..src_temps.len], src_temps); 44001 std.mem.swap(Temp, &src_slots[pattern.commute[0]], &src_slots[pattern.commute[1]]); 44002 for (dst_temps, dst_tys, case.dst_temps[0..dst_temps.len]) |*dst_temp, dst_ty, dst_kind| { 44003 if (dst_kind.pass() != 1) continue; 44004 dst_temp.*, _ = try Select.TempSpec.create(.{ .type = dst_ty, .kind = dst_kind }, &s); 44005 } 44006 var tmp_owned: [tmp_slots.len]bool = @splat(false); 44007 for (1..3) |pass| for (tmp_slots, &tmp_owned, case.extra_temps) |*slot, *owned, spec| { 44008 if (spec.kind.pass() != pass) continue; 44009 slot.*, owned.* = try spec.create(&s); 44010 }; 44011 44012 while (true) for (pattern.src[0..src_temps.len], src_temps) |src_pattern, *src_temp| { 44013 if (try src_pattern.convert(src_temp, cg)) break; 44014 } else break; 44015 @memcpy(src_slots[0..src_temps.len], src_temps); 44016 std.mem.swap(Temp, &src_slots[pattern.commute[0]], &src_slots[pattern.commute[1]]); 44017 44018 if (case.clobbers.eflags) try cg.spillEflagsIfOccupied(); 44019 44020 for (dst_temps, dst_tys, case.dst_temps[0..dst_temps.len]) |*dst_temp, dst_ty, dst_kind| { 44021 if (dst_kind.pass() != 2) continue; 44022 dst_temp.*, _ = try Select.TempSpec.create(.{ .type = dst_ty, .kind = dst_kind }, &s); 44023 } 44024 @memcpy(dst_slots[0..dst_temps.len], dst_temps); 44025 44026 switch (case.each) { 44027 .once => |body| { 44028 for (body) |inst| try s.emit(inst); 44029 s.emitLabel(.@"0:"); 44030 }, 44031 } 44032 assert(s.top == 0); 44033 44034 caller_preserved: { 44035 const cc = switch (case.clobbers.caller_preserved) { 44036 .none => break :caller_preserved, 44037 .ccc => cg.target.cCallingConvention().?, 44038 .zigcc => .auto, 44039 }; 44040 const err_ret_trace_reg = if (cc == .auto and cg.pt.zcu.comp.config.any_error_tracing) err_ret_trace_reg: { 44041 const param_gpr = abi.getCAbiIntParamRegs(.auto); 44042 break :err_ret_trace_reg param_gpr[param_gpr.len - 1]; 44043 } else .none; 44044 switch (cc) { 44045 else => unreachable, 44046 inline .x86_64_sysv, .x86_64_win, .auto => |_, tag| inline for (comptime abi.getCallerPreservedRegs(tag)) |reg| skip: { 44047 if (reg == err_ret_trace_reg) break :skip; 44048 cg.register_manager.unlockReg(.{ .tracked_index = RegisterManager.indexOfKnownRegIntoTracked(reg) orelse break :skip }); 44049 }, 44050 } 44051 } 44052 for (dst_temps, case.dst_temps[0..dst_temps.len]) |dst_temp, dst_kind| dst_kind.finish(dst_temp, &s); 44053 for (tmp_owned, tmp_slots) |owned, temp| if (owned) try temp.die(cg); 44054 return; 44055 } 44056 } 44057 return error.SelectFailed; 44058 }