| ... | ... | @@ -409,10 +409,7 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void { |
| 409 | 409 | _ = try self.addInst(.{ |
| 410 | 410 | .tag = .setcc, |
| 411 | 411 | .ops = .r_cc, |
| 412 | | .data = .{ .r_cc = .{ |
| 413 | | .r1 = reg, |
| 414 | | .cc = cc, |
| 415 | | } }, |
| 412 | .data = .{ .r_cc = .{ .r = reg, .cc = cc } }, |
| 416 | 413 | }); |
| 417 | 414 | } |
| 418 | 415 | |
| ... | ... | @@ -424,14 +421,11 @@ fn asmSetccMemory(self: *Self, m: Memory, cc: bits.Condition) !void { |
| 424 | 421 | .rip => .m_rip_cc, |
| 425 | 422 | else => unreachable, |
| 426 | 423 | }, |
| 427 | | .data = .{ .x_cc = .{ |
| 428 | | .payload = switch (m) { |
| 429 | | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 430 | | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 431 | | else => unreachable, |
| 432 | | }, |
| 433 | | .cc = cc, |
| 434 | | } }, |
| 424 | .data = .{ .x_cc = .{ .cc = cc, .payload = switch (m) { |
| 425 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 426 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 427 | else => unreachable, |
| 428 | } } }, |
| 435 | 429 | }); |
| 436 | 430 | } |
| 437 | 431 | |
| ... | ... | @@ -439,11 +433,7 @@ fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bi |
| 439 | 433 | _ = try self.addInst(.{ |
| 440 | 434 | .tag = .cmovcc, |
| 441 | 435 | .ops = .rr_cc, |
| 442 | | .data = .{ .rr_cc = .{ |
| 443 | | .r1 = reg1, |
| 444 | | .r2 = reg2, |
| 445 | | .cc = cc, |
| 446 | | } }, |
| 436 | .data = .{ .rr_cc = .{ .r1 = reg1, .r2 = reg2, .cc = cc } }, |
| 447 | 437 | }); |
| 448 | 438 | } |
| 449 | 439 | |
| ... | ... | @@ -455,15 +445,11 @@ fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condi |
| 455 | 445 | .rip => .rm_rip_cc, |
| 456 | 446 | else => unreachable, |
| 457 | 447 | }, |
| 458 | | .data = .{ .rx_cc = .{ |
| 459 | | .r1 = reg, |
| 460 | | .cc = cc, |
| 461 | | .payload = switch (m) { |
| 462 | | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 463 | | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 464 | | else => unreachable, |
| 465 | | }, |
| 466 | | } }, |
| 448 | .data = .{ .rx_cc = .{ .r = reg, .cc = cc, .payload = switch (m) { |
| 449 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 450 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 451 | else => unreachable, |
| 452 | } } }, |
| 467 | 453 | }); |
| 468 | 454 | } |
| 469 | 455 | |
| ... | ... | @@ -479,10 +465,7 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins |
| 479 | 465 | return self.addInst(.{ |
| 480 | 466 | .tag = .jcc, |
| 481 | 467 | .ops = .inst_cc, |
| 482 | | .data = .{ .inst_cc = .{ |
| 483 | | .inst = target, |
| 484 | | .cc = cc, |
| 485 | | } }, |
| 468 | .data = .{ .inst_cc = .{ .inst = target, .cc = cc } }, |
| 486 | 469 | }); |
| 487 | 470 | } |
| 488 | 471 | |
| ... | ... | @@ -503,13 +486,15 @@ fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { |
| 503 | 486 | } |
| 504 | 487 | |
| 505 | 488 | fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { |
| 506 | | const ops: Mir.Inst.Ops = if (imm == .signed) .imm_s else .imm_u; |
| 507 | 489 | _ = try self.addInst(.{ |
| 508 | 490 | .tag = tag, |
| 509 | | .ops = ops, |
| 510 | | .data = .{ .imm = switch (imm) { |
| 511 | | .signed => |x| @bitCast(u32, x), |
| 512 | | .unsigned => |x| @intCast(u32, x), |
| 491 | .ops = switch (imm) { |
| 492 | .signed => .i_s, |
| 493 | .unsigned => .i_u, |
| 494 | }, |
| 495 | .data = .{ .i = switch (imm) { |
| 496 | .signed => |s| @bitCast(u32, s), |
| 497 | .unsigned => |u| @intCast(u32, u), |
| 513 | 498 | } }, |
| 514 | 499 | }); |
| 515 | 500 | } |
| ... | ... | @@ -518,37 +503,43 @@ fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Reg |
| 518 | 503 | _ = try self.addInst(.{ |
| 519 | 504 | .tag = tag, |
| 520 | 505 | .ops = .rr, |
| 521 | | .data = .{ .rr = .{ |
| 522 | | .r1 = reg1, |
| 523 | | .r2 = reg2, |
| 524 | | } }, |
| 506 | .data = .{ .rr = .{ .r1 = reg1, .r2 = reg2 } }, |
| 525 | 507 | }); |
| 526 | 508 | } |
| 527 | 509 | |
| 528 | 510 | fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { |
| 529 | 511 | const ops: Mir.Inst.Ops = switch (imm) { |
| 530 | 512 | .signed => .ri_s, |
| 531 | | .unsigned => |x| if (x <= math.maxInt(u32)) .ri_u else .ri64, |
| 532 | | }; |
| 533 | | const data: Mir.Inst.Data = switch (ops) { |
| 534 | | .ri_s => .{ .ri = .{ |
| 535 | | .r1 = reg, |
| 536 | | .imm = @bitCast(u32, imm.signed), |
| 537 | | } }, |
| 538 | | .ri_u => .{ .ri = .{ |
| 539 | | .r1 = reg, |
| 540 | | .imm = @intCast(u32, imm.unsigned), |
| 541 | | } }, |
| 542 | | .ri64 => .{ .rx = .{ |
| 543 | | .r1 = reg, |
| 544 | | .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)), |
| 545 | | } }, |
| 546 | | else => unreachable, |
| 513 | .unsigned => |u| if (math.cast(u32, u)) |_| .ri_u else .ri64, |
| 547 | 514 | }; |
| 548 | 515 | _ = try self.addInst(.{ |
| 549 | 516 | .tag = tag, |
| 550 | 517 | .ops = ops, |
| 551 | | .data = data, |
| 518 | .data = switch (ops) { |
| 519 | .ri_s, .ri_u => .{ .ri = .{ .r = reg, .i = switch (imm) { |
| 520 | .signed => |s| @bitCast(u32, s), |
| 521 | .unsigned => |u| @intCast(u32, u), |
| 522 | } } }, |
| 523 | .ri64 => .{ .rx = .{ |
| 524 | .r = reg, |
| 525 | .payload = try self.addExtra(Mir.Imm64.encode(imm.unsigned)), |
| 526 | } }, |
| 527 | else => unreachable, |
| 528 | }, |
| 529 | }); |
| 530 | } |
| 531 | |
| 532 | fn asmRegisterRegisterRegister( |
| 533 | self: *Self, |
| 534 | tag: Mir.Inst.Tag, |
| 535 | reg1: Register, |
| 536 | reg2: Register, |
| 537 | reg3: Register, |
| 538 | ) !void { |
| 539 | _ = try self.addInst(.{ |
| 540 | .tag = tag, |
| 541 | .ops = .rrr, |
| 542 | .data = .{ .rrr = .{ .r1 = reg1, .r2 = reg2, .r3 = reg3 } }, |
| 552 | 543 | }); |
| 553 | 544 | } |
| 554 | 545 | |
| ... | ... | @@ -559,109 +550,133 @@ fn asmRegisterRegisterImmediate( |
| 559 | 550 | reg2: Register, |
| 560 | 551 | imm: Immediate, |
| 561 | 552 | ) !void { |
| 562 | | const ops: Mir.Inst.Ops = switch (imm) { |
| 563 | | .signed => .rri_s, |
| 564 | | .unsigned => .rri_u, |
| 565 | | }; |
| 566 | | const data: Mir.Inst.Data = switch (ops) { |
| 567 | | .rri_s => .{ .rri = .{ |
| 568 | | .r1 = reg1, |
| 569 | | .r2 = reg2, |
| 570 | | .imm = @bitCast(u32, imm.signed), |
| 571 | | } }, |
| 572 | | .rri_u => .{ .rri = .{ |
| 573 | | .r1 = reg1, |
| 574 | | .r2 = reg2, |
| 575 | | .imm = @intCast(u32, imm.unsigned), |
| 576 | | } }, |
| 577 | | else => unreachable, |
| 578 | | }; |
| 579 | 553 | _ = try self.addInst(.{ |
| 580 | 554 | .tag = tag, |
| 581 | | .ops = ops, |
| 582 | | .data = data, |
| 555 | .ops = switch (imm) { |
| 556 | .signed => .rri_s, |
| 557 | .unsigned => .rri_u, |
| 558 | }, |
| 559 | .data = .{ .rri = .{ .r1 = reg1, .r2 = reg2, .i = switch (imm) { |
| 560 | .signed => |s| @bitCast(u32, s), |
| 561 | .unsigned => |u| @intCast(u32, u), |
| 562 | } } }, |
| 583 | 563 | }); |
| 584 | 564 | } |
| 585 | 565 | |
| 586 | 566 | fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { |
| 587 | | const ops: Mir.Inst.Ops = switch (m) { |
| 588 | | .sib => .m_sib, |
| 589 | | .rip => .m_rip, |
| 590 | | else => unreachable, |
| 591 | | }; |
| 592 | | const data: Mir.Inst.Data = .{ .payload = switch (ops) { |
| 593 | | .m_sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 594 | | .m_rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 595 | | else => unreachable, |
| 596 | | } }; |
| 597 | 567 | _ = try self.addInst(.{ |
| 598 | 568 | .tag = tag, |
| 599 | | .ops = ops, |
| 600 | | .data = data, |
| 569 | .ops = switch (m) { |
| 570 | .sib => .m_sib, |
| 571 | .rip => .m_rip, |
| 572 | else => unreachable, |
| 573 | }, |
| 574 | .data = .{ .payload = switch (m) { |
| 575 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 576 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 577 | else => unreachable, |
| 578 | } }, |
| 601 | 579 | }); |
| 602 | 580 | } |
| 603 | 581 | |
| 604 | | fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate) !void { |
| 605 | | const ops: Mir.Inst.Ops = switch (m) { |
| 606 | | .sib => if (imm == .signed) .mi_s_sib else .mi_u_sib, |
| 607 | | .rip => if (imm == .signed) .mi_s_rip else .mi_u_rip, |
| 608 | | else => unreachable, |
| 609 | | }; |
| 610 | | const payload: u32 = switch (ops) { |
| 611 | | .mi_s_sib, .mi_u_sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 612 | | .mi_s_rip, .mi_u_rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 613 | | else => unreachable, |
| 614 | | }; |
| 615 | | const data: Mir.Inst.Data = .{ |
| 616 | | .xi = .{ .payload = payload, .imm = switch (imm) { |
| 617 | | .signed => |x| @bitCast(u32, x), |
| 618 | | .unsigned => |x| @intCast(u32, x), |
| 619 | | } }, |
| 620 | | }; |
| 582 | fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void { |
| 621 | 583 | _ = try self.addInst(.{ |
| 622 | 584 | .tag = tag, |
| 623 | | .ops = ops, |
| 624 | | .data = data, |
| 585 | .ops = switch (m) { |
| 586 | .sib => .rm_sib, |
| 587 | .rip => .rm_rip, |
| 588 | else => unreachable, |
| 589 | }, |
| 590 | .data = .{ .rx = .{ .r = reg, .payload = switch (m) { |
| 591 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 592 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 593 | else => unreachable, |
| 594 | } } }, |
| 625 | 595 | }); |
| 626 | 596 | } |
| 627 | 597 | |
| 628 | | fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !void { |
| 629 | | const ops: Mir.Inst.Ops = switch (m) { |
| 630 | | .sib => .rm_sib, |
| 631 | | .rip => .rm_rip, |
| 632 | | else => unreachable, |
| 633 | | }; |
| 634 | | const data: Mir.Inst.Data = .{ |
| 635 | | .rx = .{ .r1 = reg, .payload = switch (ops) { |
| 636 | | .rm_sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 637 | | .rm_rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 598 | fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void { |
| 599 | _ = try self.addInst(.{ |
| 600 | .tag = tag, |
| 601 | .ops = switch (m) { |
| 602 | .sib => .mr_sib, |
| 603 | .rip => .mr_rip, |
| 638 | 604 | else => unreachable, |
| 639 | | } }, |
| 640 | | }; |
| 605 | }, |
| 606 | .data = .{ .rx = .{ .r = reg, .payload = switch (m) { |
| 607 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 608 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 609 | else => unreachable, |
| 610 | } } }, |
| 611 | }); |
| 612 | } |
| 613 | |
| 614 | fn asmMemoryImmediate(self: *Self, tag: Mir.Inst.Tag, m: Memory, imm: Immediate) !void { |
| 641 | 615 | _ = try self.addInst(.{ |
| 642 | 616 | .tag = tag, |
| 643 | | .ops = ops, |
| 644 | | .data = data, |
| 617 | .ops = switch (m) { |
| 618 | .sib => switch (imm) { |
| 619 | .signed => .mi_sib_s, |
| 620 | .unsigned => .mi_sib_u, |
| 621 | }, |
| 622 | .rip => switch (imm) { |
| 623 | .signed => .mi_rip_s, |
| 624 | .unsigned => .mi_rip_u, |
| 625 | }, |
| 626 | else => unreachable, |
| 627 | }, |
| 628 | .data = .{ .ix = .{ .i = switch (imm) { |
| 629 | .signed => |s| @bitCast(u32, s), |
| 630 | .unsigned => |u| @intCast(u32, u), |
| 631 | }, .payload = switch (m) { |
| 632 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 633 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 634 | else => unreachable, |
| 635 | } } }, |
| 645 | 636 | }); |
| 646 | 637 | } |
| 647 | 638 | |
| 648 | | fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void { |
| 649 | | const ops: Mir.Inst.Ops = switch (m) { |
| 650 | | .sib => .mr_sib, |
| 651 | | .rip => .mr_rip, |
| 652 | | else => unreachable, |
| 653 | | }; |
| 654 | | const data: Mir.Inst.Data = .{ |
| 655 | | .rx = .{ .r1 = reg, .payload = switch (ops) { |
| 656 | | .mr_sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 657 | | .mr_rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 639 | fn asmMemoryRegisterRegister( |
| 640 | self: *Self, |
| 641 | tag: Mir.Inst.Tag, |
| 642 | m: Memory, |
| 643 | reg1: Register, |
| 644 | reg2: Register, |
| 645 | ) !void { |
| 646 | _ = try self.addInst(.{ |
| 647 | .tag = tag, |
| 648 | .ops = switch (m) { |
| 649 | .sib => .mrr_sib, |
| 650 | .rip => .mrr_rip, |
| 658 | 651 | else => unreachable, |
| 659 | | } }, |
| 660 | | }; |
| 652 | }, |
| 653 | .data = .{ .rrx = .{ .r1 = reg1, .r2 = reg2, .payload = switch (m) { |
| 654 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 655 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 656 | else => unreachable, |
| 657 | } } }, |
| 658 | }); |
| 659 | } |
| 660 | |
| 661 | fn asmMemoryRegisterImmediate( |
| 662 | self: *Self, |
| 663 | tag: Mir.Inst.Tag, |
| 664 | m: Memory, |
| 665 | reg: Register, |
| 666 | imm: Immediate, |
| 667 | ) !void { |
| 661 | 668 | _ = try self.addInst(.{ |
| 662 | 669 | .tag = tag, |
| 663 | | .ops = ops, |
| 664 | | .data = data, |
| 670 | .ops = switch (m) { |
| 671 | .sib => .mri_sib, |
| 672 | .rip => .mri_rip, |
| 673 | else => unreachable, |
| 674 | }, |
| 675 | .data = .{ .rix = .{ .r = reg, .i = @intCast(u8, imm.unsigned), .payload = switch (m) { |
| 676 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 677 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 678 | else => unreachable, |
| 679 | } } }, |
| 665 | 680 | }); |
| 666 | 681 | } |
| 667 | 682 | |
| ... | ... | @@ -768,18 +783,12 @@ fn gen(self: *Self) InnerError!void { |
| 768 | 783 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 769 | 784 | .tag = .sub, |
| 770 | 785 | .ops = .ri_u, |
| 771 | | .data = .{ .ri = .{ |
| 772 | | .r1 = .rsp, |
| 773 | | .imm = aligned_stack_end, |
| 774 | | } }, |
| 786 | .data = .{ .ri = .{ .r = .rsp, .i = aligned_stack_end } }, |
| 775 | 787 | }); |
| 776 | 788 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 777 | 789 | .tag = .add, |
| 778 | 790 | .ops = .ri_u, |
| 779 | | .data = .{ .ri = .{ |
| 780 | | .r1 = .rsp, |
| 781 | | .imm = aligned_stack_end, |
| 782 | | } }, |
| 791 | .data = .{ .ri = .{ .r = .rsp, .i = aligned_stack_end } }, |
| 783 | 792 | }); |
| 784 | 793 | |
| 785 | 794 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ |
| ... | ... | @@ -1392,38 +1401,80 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 1392 | 1401 | |
| 1393 | 1402 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1394 | 1403 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1395 | | if (self.liveness.isUnused(inst)) |
| 1396 | | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); |
| 1397 | | |
| 1398 | | const operand_ty = self.air.typeOf(ty_op.operand); |
| 1399 | | const operand = try self.resolveInst(ty_op.operand); |
| 1400 | | const info_a = operand_ty.intInfo(self.target.*); |
| 1401 | | const info_b = self.air.typeOfIndex(inst).intInfo(self.target.*); |
| 1402 | | |
| 1403 | | const operand_abi_size = operand_ty.abiSize(self.target.*); |
| 1404 | | const dest_ty = self.air.typeOfIndex(inst); |
| 1405 | | const dest_abi_size = dest_ty.abiSize(self.target.*); |
| 1406 | | const dst_mcv: MCValue = blk: { |
| 1407 | | if (info_a.bits == info_b.bits) { |
| 1408 | | break :blk operand; |
| 1409 | | } |
| 1410 | | if (operand_abi_size > 8 or dest_abi_size > 8) { |
| 1411 | | return self.fail("TODO implement intCast for abi sizes larger than 8", .{}); |
| 1412 | | } |
| 1413 | | |
| 1414 | | const operand_lock: ?RegisterLock = switch (operand) { |
| 1404 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1405 | const src_ty = self.air.typeOf(ty_op.operand); |
| 1406 | const src_int_info = src_ty.intInfo(self.target.*); |
| 1407 | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); |
| 1408 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 1409 | const src_lock = switch (src_mcv) { |
| 1415 | 1410 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1416 | 1411 | else => null, |
| 1417 | 1412 | }; |
| 1418 | | defer if (operand_lock) |lock| self.register_manager.unlockReg(lock); |
| 1413 | defer if (src_lock) |lock| self.register_manager.unlockReg(lock); |
| 1419 | 1414 | |
| 1420 | | const reg = try self.register_manager.allocReg(inst, gp); |
| 1421 | | try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); |
| 1422 | | try self.genSetReg(operand_ty, reg, operand); |
| 1423 | | break :blk MCValue{ .register = reg }; |
| 1424 | | }; |
| 1415 | const dst_ty = self.air.typeOfIndex(inst); |
| 1416 | const dst_int_info = dst_ty.intInfo(self.target.*); |
| 1417 | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 1418 | const dst_mcv = if (dst_abi_size <= src_abi_size and |
| 1419 | self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) |
| 1420 | src_mcv |
| 1421 | else |
| 1422 | try self.allocRegOrMem(inst, true); |
| 1425 | 1423 | |
| 1426 | | return self.finishAir(inst, dst_mcv, .{ ty_op.operand, .none, .none }); |
| 1424 | const min_ty = if (dst_int_info.bits < src_int_info.bits) dst_ty else src_ty; |
| 1425 | const signedness: std.builtin.Signedness = if (dst_int_info.signedness == .signed and |
| 1426 | src_int_info.signedness == .signed) .signed else .unsigned; |
| 1427 | switch (dst_mcv) { |
| 1428 | .register => |dst_reg| { |
| 1429 | const min_abi_size = @min(dst_abi_size, src_abi_size); |
| 1430 | const tag: Mir.Inst.Tag = switch (signedness) { |
| 1431 | .signed => .movsx, |
| 1432 | .unsigned => if (min_abi_size > 2) .mov else .movzx, |
| 1433 | }; |
| 1434 | const dst_alias = switch (tag) { |
| 1435 | .movsx => dst_reg.to64(), |
| 1436 | .mov, .movzx => if (min_abi_size > 4) dst_reg.to64() else dst_reg.to32(), |
| 1437 | else => unreachable, |
| 1438 | }; |
| 1439 | switch (src_mcv) { |
| 1440 | .register => |src_reg| { |
| 1441 | try self.asmRegisterRegister( |
| 1442 | tag, |
| 1443 | dst_alias, |
| 1444 | registerAlias(src_reg, min_abi_size), |
| 1445 | ); |
| 1446 | }, |
| 1447 | .stack_offset => |src_off| { |
| 1448 | try self.asmRegisterMemory(tag, dst_alias, Memory.sib( |
| 1449 | Memory.PtrSize.fromSize(min_abi_size), |
| 1450 | .{ .base = .rbp, .disp = -src_off }, |
| 1451 | )); |
| 1452 | }, |
| 1453 | else => return self.fail("TODO airIntCast from {s} to {s}", .{ |
| 1454 | @tagName(src_mcv), |
| 1455 | @tagName(dst_mcv), |
| 1456 | }), |
| 1457 | } |
| 1458 | if (self.regExtraBits(min_ty) > 0) try self.truncateRegister(min_ty, dst_reg); |
| 1459 | }, |
| 1460 | else => { |
| 1461 | try self.setRegOrMem(min_ty, dst_mcv, src_mcv); |
| 1462 | const extra = dst_abi_size * 8 - dst_int_info.bits; |
| 1463 | if (extra > 0) { |
| 1464 | try self.genShiftBinOpMir(switch (signedness) { |
| 1465 | .signed => .sal, |
| 1466 | .unsigned => .shl, |
| 1467 | }, dst_ty, dst_mcv, .{ .immediate = extra }); |
| 1468 | try self.genShiftBinOpMir(switch (signedness) { |
| 1469 | .signed => .sar, |
| 1470 | .unsigned => .shr, |
| 1471 | }, dst_ty, dst_mcv, .{ .immediate = extra }); |
| 1472 | } |
| 1473 | }, |
| 1474 | } |
| 1475 | break :result dst_mcv; |
| 1476 | }; |
| 1477 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 1427 | 1478 | } |
| 1428 | 1479 | |
| 1429 | 1480 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -1550,28 +1601,161 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1550 | 1601 | |
| 1551 | 1602 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1552 | 1603 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1553 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1554 | | .dead |
| 1555 | | else |
| 1556 | | return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); |
| 1604 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1605 | const ty = self.air.typeOf(bin_op.lhs); |
| 1606 | |
| 1607 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 1608 | const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) |
| 1609 | lhs_mcv |
| 1610 | else |
| 1611 | try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv); |
| 1612 | const dst_reg = dst_mcv.register; |
| 1613 | const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 1614 | defer self.register_manager.unlockReg(dst_lock); |
| 1615 | |
| 1616 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 1617 | const rhs_lock = switch (rhs_mcv) { |
| 1618 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1619 | else => null, |
| 1620 | }; |
| 1621 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1622 | |
| 1623 | const limit_reg = try self.register_manager.allocReg(null, gp); |
| 1624 | const limit_mcv = MCValue{ .register = limit_reg }; |
| 1625 | const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); |
| 1626 | defer self.register_manager.unlockReg(limit_lock); |
| 1627 | |
| 1628 | const reg_bits = self.regBitSize(ty); |
| 1629 | const cc: Condition = if (ty.isSignedInt()) cc: { |
| 1630 | try self.genSetReg(ty, limit_reg, dst_mcv); |
| 1631 | try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); |
| 1632 | try self.genBinOpMir(.xor, ty, limit_mcv, .{ |
| 1633 | .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, |
| 1634 | }); |
| 1635 | break :cc .o; |
| 1636 | } else cc: { |
| 1637 | try self.genSetReg(ty, limit_reg, .{ |
| 1638 | .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1639 | }); |
| 1640 | break :cc .c; |
| 1641 | }; |
| 1642 | try self.genBinOpMir(.add, ty, dst_mcv, rhs_mcv); |
| 1643 | |
| 1644 | const abi_size = @intCast(u32, @max(ty.abiSize(self.target.*), 2)); |
| 1645 | try self.asmCmovccRegisterRegister( |
| 1646 | registerAlias(dst_reg, abi_size), |
| 1647 | registerAlias(limit_reg, abi_size), |
| 1648 | cc, |
| 1649 | ); |
| 1650 | break :result dst_mcv; |
| 1651 | }; |
| 1557 | 1652 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1558 | 1653 | } |
| 1559 | 1654 | |
| 1560 | 1655 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1561 | 1656 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1562 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1563 | | .dead |
| 1564 | | else |
| 1565 | | return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); |
| 1657 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1658 | const ty = self.air.typeOf(bin_op.lhs); |
| 1659 | |
| 1660 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 1661 | const dst_mcv = if (lhs_mcv.isRegister() and self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) |
| 1662 | lhs_mcv |
| 1663 | else |
| 1664 | try self.copyToRegisterWithInstTracking(inst, ty, lhs_mcv); |
| 1665 | const dst_reg = dst_mcv.register; |
| 1666 | const dst_lock = self.register_manager.lockRegAssumeUnused(dst_reg); |
| 1667 | defer self.register_manager.unlockReg(dst_lock); |
| 1668 | |
| 1669 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 1670 | const rhs_lock = switch (rhs_mcv) { |
| 1671 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1672 | else => null, |
| 1673 | }; |
| 1674 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1675 | |
| 1676 | const limit_reg = try self.register_manager.allocReg(null, gp); |
| 1677 | const limit_mcv = MCValue{ .register = limit_reg }; |
| 1678 | const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); |
| 1679 | defer self.register_manager.unlockReg(limit_lock); |
| 1680 | |
| 1681 | const reg_bits = self.regBitSize(ty); |
| 1682 | const cc: Condition = if (ty.isSignedInt()) cc: { |
| 1683 | try self.genSetReg(ty, limit_reg, dst_mcv); |
| 1684 | try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); |
| 1685 | try self.genBinOpMir(.xor, ty, limit_mcv, .{ |
| 1686 | .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, |
| 1687 | }); |
| 1688 | break :cc .o; |
| 1689 | } else cc: { |
| 1690 | try self.genSetReg(ty, limit_reg, .{ .immediate = 0 }); |
| 1691 | break :cc .c; |
| 1692 | }; |
| 1693 | try self.genBinOpMir(.sub, ty, dst_mcv, rhs_mcv); |
| 1694 | |
| 1695 | const abi_size = @intCast(u32, @max(ty.abiSize(self.target.*), 2)); |
| 1696 | try self.asmCmovccRegisterRegister( |
| 1697 | registerAlias(dst_reg, abi_size), |
| 1698 | registerAlias(limit_reg, abi_size), |
| 1699 | cc, |
| 1700 | ); |
| 1701 | break :result dst_mcv; |
| 1702 | }; |
| 1566 | 1703 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1567 | 1704 | } |
| 1568 | 1705 | |
| 1569 | 1706 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1570 | 1707 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1571 | | const result: MCValue = if (self.liveness.isUnused(inst)) |
| 1572 | | .dead |
| 1573 | | else |
| 1574 | | return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); |
| 1708 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1709 | const ty = self.air.typeOf(bin_op.lhs); |
| 1710 | |
| 1711 | try self.spillRegisters(&.{ .rax, .rdx }); |
| 1712 | const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); |
| 1713 | defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 1714 | |
| 1715 | const lhs_mcv = try self.resolveInst(bin_op.lhs); |
| 1716 | const lhs_lock = switch (lhs_mcv) { |
| 1717 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1718 | else => null, |
| 1719 | }; |
| 1720 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1721 | |
| 1722 | const rhs_mcv = try self.resolveInst(bin_op.rhs); |
| 1723 | const rhs_lock = switch (rhs_mcv) { |
| 1724 | .register => |reg| self.register_manager.lockReg(reg), |
| 1725 | else => null, |
| 1726 | }; |
| 1727 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 1728 | |
| 1729 | const limit_reg = try self.register_manager.allocReg(null, gp); |
| 1730 | const limit_mcv = MCValue{ .register = limit_reg }; |
| 1731 | const limit_lock = self.register_manager.lockRegAssumeUnused(limit_reg); |
| 1732 | defer self.register_manager.unlockReg(limit_lock); |
| 1733 | |
| 1734 | const reg_bits = self.regBitSize(ty); |
| 1735 | const cc: Condition = if (ty.isSignedInt()) cc: { |
| 1736 | try self.genSetReg(ty, limit_reg, lhs_mcv); |
| 1737 | try self.genBinOpMir(.xor, ty, limit_mcv, rhs_mcv); |
| 1738 | try self.genBinOpMir(.sar, ty, limit_mcv, .{ .immediate = reg_bits - 1 }); |
| 1739 | try self.genBinOpMir(.xor, ty, limit_mcv, .{ |
| 1740 | .immediate = (@as(u64, 1) << @intCast(u6, reg_bits - 1)) - 1, |
| 1741 | }); |
| 1742 | break :cc .o; |
| 1743 | } else cc: { |
| 1744 | try self.genSetReg(ty, limit_reg, .{ |
| 1745 | .immediate = @as(u64, std.math.maxInt(u64)) >> @intCast(u6, 64 - reg_bits), |
| 1746 | }); |
| 1747 | break :cc .c; |
| 1748 | }; |
| 1749 | |
| 1750 | const dst_mcv = try self.genMulDivBinOp(.mul, inst, ty, lhs_mcv, rhs_mcv); |
| 1751 | const abi_size = @intCast(u32, @max(ty.abiSize(self.target.*), 2)); |
| 1752 | try self.asmCmovccRegisterRegister( |
| 1753 | registerAlias(dst_mcv.register, abi_size), |
| 1754 | registerAlias(limit_reg, abi_size), |
| 1755 | cc, |
| 1756 | ); |
| 1757 | break :result dst_mcv; |
| 1758 | }; |
| 1575 | 1759 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1576 | 1760 | } |
| 1577 | 1761 | |
| ... | ... | @@ -1599,6 +1783,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1599 | 1783 | .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), |
| 1600 | 1784 | .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), |
| 1601 | 1785 | .shl_with_overflow => blk: { |
| 1786 | try self.register_manager.getReg(.rcx, null); |
| 1602 | 1787 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1603 | 1788 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1604 | 1789 | const shift_ty = self.air.typeOf(bin_op.rhs); |
| ... | ... | @@ -1889,6 +2074,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1889 | 2074 | try self.spillRegisters(&.{.rcx}); |
| 1890 | 2075 | |
| 1891 | 2076 | const tag = self.air.instructions.items(.tag)[inst]; |
| 2077 | try self.register_manager.getReg(.rcx, null); |
| 1892 | 2078 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1893 | 2079 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1894 | 2080 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| ... | ... | @@ -2018,7 +2204,7 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2018 | 2204 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); |
| 2019 | 2205 | if (err_off > 0) { |
| 2020 | 2206 | const shift = @intCast(u6, err_off * 8); |
| 2021 | | try self.genShiftBinOpMir(.shr, err_union_ty, result.register, .{ .immediate = shift }); |
| 2207 | try self.genShiftBinOpMir(.shr, err_union_ty, result, .{ .immediate = shift }); |
| 2022 | 2208 | } else { |
| 2023 | 2209 | try self.truncateRegister(Type.anyerror, result.register); |
| 2024 | 2210 | } |
| ... | ... | @@ -2050,9 +2236,7 @@ fn genUnwrapErrorUnionPayloadMir( |
| 2050 | 2236 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 2051 | 2237 | |
| 2052 | 2238 | const result: MCValue = result: { |
| 2053 | | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { |
| 2054 | | break :result MCValue.none; |
| 2055 | | } |
| 2239 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; |
| 2056 | 2240 | |
| 2057 | 2241 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 2058 | 2242 | switch (err_union) { |
| ... | ... | @@ -2065,17 +2249,17 @@ fn genUnwrapErrorUnionPayloadMir( |
| 2065 | 2249 | const eu_lock = self.register_manager.lockReg(reg); |
| 2066 | 2250 | defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); |
| 2067 | 2251 | |
| 2068 | | const result_reg: Register = if (maybe_inst) |inst| |
| 2069 | | (try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register |
| 2252 | const result_mcv: MCValue = if (maybe_inst) |inst| |
| 2253 | try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union) |
| 2070 | 2254 | else |
| 2071 | | try self.copyToTmpRegister(err_union_ty, err_union); |
| 2255 | .{ .register = try self.copyToTmpRegister(err_union_ty, err_union) }; |
| 2072 | 2256 | if (payload_off > 0) { |
| 2073 | 2257 | const shift = @intCast(u6, payload_off * 8); |
| 2074 | | try self.genShiftBinOpMir(.shr, err_union_ty, result_reg, .{ .immediate = shift }); |
| 2258 | try self.genShiftBinOpMir(.shr, err_union_ty, result_mcv, .{ .immediate = shift }); |
| 2075 | 2259 | } else { |
| 2076 | | try self.truncateRegister(payload_ty, result_reg); |
| 2260 | try self.truncateRegister(payload_ty, result_mcv.register); |
| 2077 | 2261 | } |
| 2078 | | break :result MCValue{ .register = result_reg }; |
| 2262 | break :result result_mcv; |
| 2079 | 2263 | }, |
| 2080 | 2264 | else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}), |
| 2081 | 2265 | } |
| ... | ... | @@ -2253,8 +2437,8 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2253 | 2437 | |
| 2254 | 2438 | .stack_offset => |off| try self.asmMemoryImmediate( |
| 2255 | 2439 | .mov, |
| 2256 | | Memory.sib(.byte, .{ .base = .rsp, .disp = pl_abi_size - off }), |
| 2257 | | Immediate.u(0), |
| 2440 | Memory.sib(.byte, .{ .base = .rbp, .disp = pl_abi_size - off }), |
| 2441 | Immediate.u(1), |
| 2258 | 2442 | ), |
| 2259 | 2443 | } |
| 2260 | 2444 | } |
| ... | ... | @@ -2473,10 +2657,9 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 2473 | 2657 | } |
| 2474 | 2658 | |
| 2475 | 2659 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2476 | | const is_volatile = false; // TODO |
| 2477 | 2660 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2478 | | const result: MCValue = if (!is_volatile and self.liveness.isUnused(inst)) .dead else result: { |
| 2479 | | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2661 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2662 | const result = if (!slice_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 2480 | 2663 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2481 | 2664 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| 2482 | 2665 | const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs); |
| ... | ... | @@ -2563,54 +2746,44 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2563 | 2746 | } |
| 2564 | 2747 | |
| 2565 | 2748 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2566 | | const is_volatile = false; // TODO |
| 2567 | 2749 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 2568 | | |
| 2569 | | if (!is_volatile and self.liveness.isUnused(inst)) { |
| 2570 | | return self.finishAir(inst, .dead, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2571 | | } |
| 2572 | | |
| 2573 | | // this is identical to the `airPtrElemPtr` codegen expect here an |
| 2574 | | // additional `mov` is needed at the end to get the actual value |
| 2575 | | |
| 2576 | 2750 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2577 | | const ptr = try self.resolveInst(bin_op.lhs); |
| 2578 | | const ptr_lock: ?RegisterLock = switch (ptr) { |
| 2579 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2580 | | else => null, |
| 2581 | | }; |
| 2582 | | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 2751 | const result = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 2752 | // this is identical to the `airPtrElemPtr` codegen expect here an |
| 2753 | // additional `mov` is needed at the end to get the actual value |
| 2754 | |
| 2755 | const elem_ty = ptr_ty.elemType2(); |
| 2756 | const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2757 | const index_ty = self.air.typeOf(bin_op.rhs); |
| 2758 | const index_mcv = try self.resolveInst(bin_op.rhs); |
| 2759 | const index_lock = switch (index_mcv) { |
| 2760 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2761 | else => null, |
| 2762 | }; |
| 2763 | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| 2583 | 2764 | |
| 2584 | | const elem_ty = ptr_ty.elemType2(); |
| 2585 | | const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); |
| 2586 | | const index_ty = self.air.typeOf(bin_op.rhs); |
| 2587 | | const index = try self.resolveInst(bin_op.rhs); |
| 2588 | | const index_lock: ?RegisterLock = switch (index) { |
| 2589 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2590 | | else => null, |
| 2591 | | }; |
| 2592 | | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| 2765 | const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size); |
| 2766 | const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 2767 | defer self.register_manager.unlockReg(offset_lock); |
| 2593 | 2768 | |
| 2594 | | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); |
| 2595 | | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 2596 | | defer self.register_manager.unlockReg(offset_reg_lock); |
| 2597 | | |
| 2598 | | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); |
| 2599 | | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 2769 | const ptr_mcv = try self.resolveInst(bin_op.lhs); |
| 2770 | const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0)) |
| 2771 | ptr_mcv.register |
| 2772 | else |
| 2773 | try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| 2774 | const elem_ptr_lock = self.register_manager.lockRegAssumeUnused(elem_ptr_reg); |
| 2775 | defer self.register_manager.unlockReg(elem_ptr_lock); |
| 2776 | try self.asmRegisterRegister(.add, elem_ptr_reg, offset_reg); |
| 2600 | 2777 | |
| 2601 | | const result: MCValue = result: { |
| 2602 | | if (elem_abi_size > 8) { |
| 2603 | | return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); |
| 2604 | | } else { |
| 2605 | | try self.asmRegisterMemory( |
| 2606 | | .mov, |
| 2607 | | registerAlias(dst_mcv.register, elem_abi_size), |
| 2608 | | Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{ .base = dst_mcv.register }), |
| 2609 | | ); |
| 2610 | | break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; |
| 2611 | | } |
| 2778 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 2779 | const dst_lock = switch (dst_mcv) { |
| 2780 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2781 | else => null, |
| 2782 | }; |
| 2783 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 2784 | try self.load(dst_mcv, .{ .register = elem_ptr_reg }, ptr_ty); |
| 2785 | break :result dst_mcv; |
| 2612 | 2786 | }; |
| 2613 | | |
| 2614 | 2787 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 2615 | 2788 | } |
| 2616 | 2789 | |
| ... | ... | @@ -2618,36 +2791,34 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2618 | 2791 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2619 | 2792 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2620 | 2793 | |
| 2621 | | if (self.liveness.isUnused(inst)) { |
| 2622 | | return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); |
| 2623 | | } |
| 2624 | | |
| 2625 | | const ptr_ty = self.air.typeOf(extra.lhs); |
| 2626 | | const ptr = try self.resolveInst(extra.lhs); |
| 2627 | | const ptr_lock: ?RegisterLock = switch (ptr) { |
| 2628 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2629 | | else => null, |
| 2630 | | }; |
| 2631 | | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 2632 | | |
| 2633 | | const elem_ty = ptr_ty.elemType2(); |
| 2634 | | const elem_abi_size = elem_ty.abiSize(self.target.*); |
| 2635 | | const index_ty = self.air.typeOf(extra.rhs); |
| 2636 | | const index = try self.resolveInst(extra.rhs); |
| 2637 | | const index_lock: ?RegisterLock = switch (index) { |
| 2638 | | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2639 | | else => null, |
| 2640 | | }; |
| 2641 | | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| 2794 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2795 | const ptr_ty = self.air.typeOf(extra.lhs); |
| 2796 | const ptr = try self.resolveInst(extra.lhs); |
| 2797 | const ptr_lock: ?RegisterLock = switch (ptr) { |
| 2798 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2799 | else => null, |
| 2800 | }; |
| 2801 | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 2642 | 2802 | |
| 2643 | | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); |
| 2644 | | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 2645 | | defer self.register_manager.unlockReg(offset_reg_lock); |
| 2803 | const elem_ty = ptr_ty.elemType2(); |
| 2804 | const elem_abi_size = elem_ty.abiSize(self.target.*); |
| 2805 | const index_ty = self.air.typeOf(extra.rhs); |
| 2806 | const index = try self.resolveInst(extra.rhs); |
| 2807 | const index_lock: ?RegisterLock = switch (index) { |
| 2808 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2809 | else => null, |
| 2810 | }; |
| 2811 | defer if (index_lock) |lock| self.register_manager.unlockReg(lock); |
| 2646 | 2812 | |
| 2647 | | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); |
| 2648 | | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 2813 | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); |
| 2814 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 2815 | defer self.register_manager.unlockReg(offset_reg_lock); |
| 2649 | 2816 | |
| 2650 | | return self.finishAir(inst, dst_mcv, .{ extra.lhs, extra.rhs, .none }); |
| 2817 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); |
| 2818 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); |
| 2819 | break :result dst_mcv; |
| 2820 | }; |
| 2821 | return self.finishAir(inst, result, .{ extra.lhs, extra.rhs, .none }); |
| 2651 | 2822 | } |
| 2652 | 2823 | |
| 2653 | 2824 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| ... | ... | @@ -2728,7 +2899,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2728 | 2899 | else |
| 2729 | 2900 | 0; |
| 2730 | 2901 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); |
| 2731 | | try self.genShiftBinOpMir(.shr, Type.usize, result.register, .{ .immediate = shift }); |
| 2902 | try self.genShiftBinOpMir(.shr, Type.usize, result, .{ .immediate = shift }); |
| 2732 | 2903 | break :blk MCValue{ |
| 2733 | 2904 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), |
| 2734 | 2905 | }; |
| ... | ... | @@ -2820,12 +2991,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 2820 | 2991 | const extra_bits = self.regExtraBits(src_ty); |
| 2821 | 2992 | const masked_mcv = if (extra_bits > 0) masked: { |
| 2822 | 2993 | const mask_mcv = MCValue{ |
| 2823 | | .immediate = ((@as(u64, 1) << @intCast(u6, extra_bits)) - 1) << @intCast(u6, src_bits), |
| 2994 | .immediate = ((@as(u64, 1) << @intCast(u6, extra_bits)) - 1) << |
| 2995 | @intCast(u6, src_bits), |
| 2824 | 2996 | }; |
| 2825 | 2997 | const tmp_mcv = tmp: { |
| 2826 | | if (src_mcv.isImmediate() or self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { |
| 2827 | | break :tmp src_mcv; |
| 2828 | | } |
| 2998 | if (src_mcv.isImmediate() or self.liveness.operandDies(inst, 0)) break :tmp src_mcv; |
| 2829 | 2999 | try self.genSetReg(src_ty, dst_reg, src_mcv); |
| 2830 | 3000 | break :tmp dst_mcv; |
| 2831 | 3001 | }; |
| ... | ... | @@ -3526,34 +3696,37 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 3526 | 3696 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3527 | 3697 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3528 | 3698 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; |
| 3529 | | const result = try self.structFieldPtr(inst, extra.struct_operand, extra.field_index); |
| 3699 | const result = try self.fieldPtr(inst, extra.struct_operand, extra.field_index); |
| 3530 | 3700 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 3531 | 3701 | } |
| 3532 | 3702 | |
| 3533 | 3703 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { |
| 3534 | 3704 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 3535 | | const result = try self.structFieldPtr(inst, ty_op.operand, index); |
| 3705 | const result = try self.fieldPtr(inst, ty_op.operand, index); |
| 3536 | 3706 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); |
| 3537 | 3707 | } |
| 3538 | 3708 | |
| 3539 | | fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { |
| 3709 | fn fieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, index: u32) !MCValue { |
| 3540 | 3710 | if (self.liveness.isUnused(inst)) { |
| 3541 | 3711 | return MCValue.dead; |
| 3542 | 3712 | } |
| 3543 | 3713 | |
| 3544 | 3714 | const mcv = try self.resolveInst(operand); |
| 3545 | 3715 | const ptr_ty = self.air.typeOf(operand); |
| 3546 | | const struct_ty = ptr_ty.childType(); |
| 3547 | | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { |
| 3548 | | return self.fail("TODO structFieldPtr implement packed structs", .{}); |
| 3549 | | } |
| 3550 | | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); |
| 3716 | const container_ty = ptr_ty.childType(); |
| 3717 | const field_offset = switch (container_ty.containerLayout()) { |
| 3718 | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*)), |
| 3719 | .Packed => if (container_ty.zigTypeTag() == .Struct and ptr_ty.ptrInfo().data.host_size == 0) |
| 3720 | container_ty.packedStructFieldByteOffset(index, self.target.*) |
| 3721 | else |
| 3722 | 0, |
| 3723 | }; |
| 3551 | 3724 | |
| 3552 | 3725 | const dst_mcv: MCValue = result: { |
| 3553 | 3726 | switch (mcv) { |
| 3554 | 3727 | .stack_offset => { |
| 3555 | 3728 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ |
| 3556 | | .immediate = struct_field_offset, |
| 3729 | .immediate = field_offset, |
| 3557 | 3730 | }); |
| 3558 | 3731 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 3559 | 3732 | defer self.register_manager.unlockReg(offset_reg_lock); |
| ... | ... | @@ -3563,7 +3736,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3563 | 3736 | break :result dst_mcv; |
| 3564 | 3737 | }, |
| 3565 | 3738 | .ptr_stack_offset => |off| { |
| 3566 | | const ptr_stack_offset = off - @intCast(i32, struct_field_offset); |
| 3739 | const ptr_stack_offset = off - @intCast(i32, field_offset); |
| 3567 | 3740 | break :result MCValue{ .ptr_stack_offset = ptr_stack_offset }; |
| 3568 | 3741 | }, |
| 3569 | 3742 | .register => |reg| { |
| ... | ... | @@ -3571,7 +3744,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3571 | 3744 | defer self.register_manager.unlockReg(reg_lock); |
| 3572 | 3745 | |
| 3573 | 3746 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ |
| 3574 | | .immediate = struct_field_offset, |
| 3747 | .immediate = field_offset, |
| 3575 | 3748 | }); |
| 3576 | 3749 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 3577 | 3750 | defer self.register_manager.unlockReg(offset_reg_lock); |
| ... | ... | @@ -3592,7 +3765,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3592 | 3765 | try self.genBinOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); |
| 3593 | 3766 | break :result MCValue{ .register = result_reg }; |
| 3594 | 3767 | }, |
| 3595 | | else => return self.fail("TODO implement codegen struct_field_ptr for {}", .{mcv}), |
| 3768 | else => return self.fail("TODO implement fieldPtr for {}", .{mcv}), |
| 3596 | 3769 | } |
| 3597 | 3770 | }; |
| 3598 | 3771 | return dst_mcv; |
| ... | ... | @@ -3609,18 +3782,22 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3609 | 3782 | } |
| 3610 | 3783 | |
| 3611 | 3784 | const mcv = try self.resolveInst(operand); |
| 3612 | | const struct_ty = self.air.typeOf(operand); |
| 3613 | | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { |
| 3614 | | return self.fail("TODO airStructFieldVal implement packed structs", .{}); |
| 3615 | | } |
| 3616 | | const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); |
| 3617 | | const struct_field_ty = struct_ty.structFieldType(index); |
| 3785 | const container_ty = self.air.typeOf(operand); |
| 3786 | const field_ty = container_ty.structFieldType(index); |
| 3787 | const field_bit_offset = switch (container_ty.containerLayout()) { |
| 3788 | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*) * 8), |
| 3789 | .Packed => if (container_ty.castTag(.@"struct")) |struct_obj| |
| 3790 | struct_obj.data.packedFieldBitOffset(self.target.*, index) |
| 3791 | else |
| 3792 | 0, |
| 3793 | }; |
| 3618 | 3794 | |
| 3619 | 3795 | const result: MCValue = result: { |
| 3620 | 3796 | switch (mcv) { |
| 3621 | 3797 | .stack_offset => |off| { |
| 3622 | | const stack_offset = off - @intCast(i32, struct_field_offset); |
| 3623 | | break :result MCValue{ .stack_offset = stack_offset }; |
| 3798 | const byte_offset = std.math.divExact(u32, field_bit_offset, 8) catch |
| 3799 | return self.fail("TODO implement struct_field_val for a packed struct", .{}); |
| 3800 | break :result MCValue{ .stack_offset = off - @intCast(i32, byte_offset) }; |
| 3624 | 3801 | }, |
| 3625 | 3802 | .register => |reg| { |
| 3626 | 3803 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| ... | ... | @@ -3643,27 +3820,23 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3643 | 3820 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 3644 | 3821 | |
| 3645 | 3822 | // Shift by struct_field_offset. |
| 3646 | | const shift = @intCast(u8, struct_field_offset * 8); |
| 3647 | | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); |
| 3823 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv, .{ .immediate = field_bit_offset }); |
| 3648 | 3824 | |
| 3649 | | // Mask with reg.bitSize() - struct_field_size |
| 3650 | | const max_reg_bit_width = Register.rax.bitSize(); |
| 3651 | | const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); |
| 3652 | | const mask = (~@as(u64, 0)) >> mask_shift; |
| 3825 | // Mask to field_bit_size bits |
| 3826 | const field_bit_size = field_ty.bitSize(self.target.*); |
| 3827 | const mask = ~@as(u64, 0) >> @intCast(u6, 64 - field_bit_size); |
| 3653 | 3828 | |
| 3654 | 3829 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 3655 | 3830 | try self.genBinOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); |
| 3656 | 3831 | |
| 3657 | | const signedness: std.builtin.Signedness = blk: { |
| 3658 | | if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; |
| 3659 | | break :blk struct_field_ty.intInfo(self.target.*).signedness; |
| 3660 | | }; |
| 3661 | | const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); |
| 3662 | | if (signedness == .signed and field_size < 8) { |
| 3832 | const signedness = |
| 3833 | if (field_ty.isAbiInt()) field_ty.intInfo(self.target.*).signedness else .unsigned; |
| 3834 | const field_byte_size = @intCast(u32, field_ty.abiSize(self.target.*)); |
| 3835 | if (signedness == .signed and field_byte_size < 8) { |
| 3663 | 3836 | try self.asmRegisterRegister( |
| 3664 | 3837 | .movsx, |
| 3665 | 3838 | dst_mcv.register, |
| 3666 | | registerAlias(dst_mcv.register, field_size), |
| 3839 | registerAlias(dst_mcv.register, field_byte_size), |
| 3667 | 3840 | ); |
| 3668 | 3841 | } |
| 3669 | 3842 | |
| ... | ... | @@ -3707,10 +3880,10 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: |
| 3707 | 3880 | const src_ty = self.air.typeOf(src_air); |
| 3708 | 3881 | const src_mcv = try self.resolveInst(src_air); |
| 3709 | 3882 | if (src_ty.zigTypeTag() == .Vector) { |
| 3710 | | return self.fail("TODO implement genBinOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 3883 | return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 3711 | 3884 | } |
| 3712 | 3885 | if (src_ty.abiSize(self.target.*) > 8) { |
| 3713 | | return self.fail("TODO implement genBinOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 3886 | return self.fail("TODO implement genUnOp for {}", .{src_ty.fmt(self.bin_file.options.module.?)}); |
| 3714 | 3887 | } |
| 3715 | 3888 | |
| 3716 | 3889 | switch (src_mcv) { |
| ... | ... | @@ -3805,29 +3978,186 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue |
| 3805 | 3978 | } |
| 3806 | 3979 | |
| 3807 | 3980 | /// Clobbers .rcx for non-immediate shift value. |
| 3808 | | fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void { |
| 3809 | | switch (tag) { |
| 3810 | | .sal, .sar, .shl, .shr => {}, |
| 3811 | | else => unreachable, |
| 3812 | | } |
| 3813 | | |
| 3814 | | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 3815 | | blk: { |
| 3816 | | switch (shift) { |
| 3981 | fn genShiftBinOpMir( |
| 3982 | self: *Self, |
| 3983 | tag: Mir.Inst.Tag, |
| 3984 | ty: Type, |
| 3985 | lhs_mcv: MCValue, |
| 3986 | shift_mcv: MCValue, |
| 3987 | ) !void { |
| 3988 | const rhs_mcv: MCValue = rhs: { |
| 3989 | switch (shift_mcv) { |
| 3817 | 3990 | .immediate => |imm| switch (imm) { |
| 3818 | 3991 | 0 => return, |
| 3819 | | else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)), |
| 3820 | | }, |
| 3821 | | .register => |shift_reg| { |
| 3822 | | if (shift_reg == .rcx) break :blk; |
| 3992 | else => break :rhs shift_mcv, |
| 3823 | 3993 | }, |
| 3994 | .register => |shift_reg| if (shift_reg == .rcx) break :rhs shift_mcv, |
| 3824 | 3995 | else => {}, |
| 3825 | 3996 | } |
| 3826 | 3997 | self.register_manager.getRegAssumeFree(.rcx, null); |
| 3827 | | try self.genSetReg(Type.u8, .rcx, shift); |
| 3828 | | } |
| 3998 | try self.genSetReg(Type.u8, .rcx, shift_mcv); |
| 3999 | break :rhs .{ .register = .rcx }; |
| 4000 | }; |
| 4001 | |
| 4002 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4003 | if (abi_size <= 8) { |
| 4004 | switch (lhs_mcv) { |
| 4005 | .register => |lhs_reg| switch (rhs_mcv) { |
| 4006 | .immediate => |rhs_imm| try self.asmRegisterImmediate( |
| 4007 | tag, |
| 4008 | registerAlias(lhs_reg, abi_size), |
| 4009 | Immediate.u(rhs_imm), |
| 4010 | ), |
| 4011 | .register => |rhs_reg| try self.asmRegisterRegister( |
| 4012 | tag, |
| 4013 | registerAlias(lhs_reg, abi_size), |
| 4014 | registerAlias(rhs_reg, 1), |
| 4015 | ), |
| 4016 | else => return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ |
| 4017 | @tagName(lhs_mcv), |
| 4018 | @tagName(rhs_mcv), |
| 4019 | }), |
| 4020 | }, |
| 4021 | .stack_offset => |lhs_off| switch (rhs_mcv) { |
| 4022 | .immediate => |rhs_imm| try self.asmMemoryImmediate( |
| 4023 | tag, |
| 4024 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -lhs_off }), |
| 4025 | Immediate.u(rhs_imm), |
| 4026 | ), |
| 4027 | .register => |rhs_reg| try self.asmMemoryRegister( |
| 4028 | tag, |
| 4029 | Memory.sib(Memory.PtrSize.fromSize(abi_size), .{ .base = .rbp, .disp = -lhs_off }), |
| 4030 | registerAlias(rhs_reg, 1), |
| 4031 | ), |
| 4032 | else => return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ |
| 4033 | @tagName(lhs_mcv), |
| 4034 | @tagName(rhs_mcv), |
| 4035 | }), |
| 4036 | }, |
| 4037 | else => return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ |
| 4038 | @tagName(lhs_mcv), |
| 4039 | @tagName(rhs_mcv), |
| 4040 | }), |
| 4041 | } |
| 4042 | } else if (abi_size <= 16) { |
| 4043 | const tmp_reg = try self.register_manager.allocReg(null, gp); |
| 4044 | const tmp_lock = self.register_manager.lockRegAssumeUnused(tmp_reg); |
| 4045 | defer self.register_manager.unlockReg(tmp_lock); |
| 4046 | |
| 4047 | const info: struct { offsets: [2]i32, double_tag: Mir.Inst.Tag } = switch (tag) { |
| 4048 | .shl, .sal => .{ .offsets = .{ 0, 8 }, .double_tag = .shld }, |
| 4049 | .shr, .sar => .{ .offsets = .{ 8, 0 }, .double_tag = .shrd }, |
| 4050 | else => unreachable, |
| 4051 | }; |
| 4052 | switch (lhs_mcv) { |
| 4053 | .stack_offset => |dst_off| switch (rhs_mcv) { |
| 4054 | .immediate => |rhs_imm| if (rhs_imm == 0) {} else if (rhs_imm < 64) { |
| 4055 | try self.asmRegisterMemory( |
| 4056 | .mov, |
| 4057 | tmp_reg, |
| 4058 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4059 | ); |
| 4060 | try self.asmMemoryRegisterImmediate( |
| 4061 | info.double_tag, |
| 4062 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[1] - dst_off }), |
| 4063 | tmp_reg, |
| 4064 | Immediate.u(rhs_imm), |
| 4065 | ); |
| 4066 | try self.asmMemoryImmediate( |
| 4067 | tag, |
| 4068 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4069 | Immediate.u(rhs_imm), |
| 4070 | ); |
| 4071 | } else { |
| 4072 | assert(rhs_imm < 128); |
| 4073 | try self.asmRegisterMemory( |
| 4074 | .mov, |
| 4075 | tmp_reg, |
| 4076 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4077 | ); |
| 4078 | if (rhs_imm > 64) { |
| 4079 | try self.asmRegisterImmediate(tag, tmp_reg, Immediate.u(rhs_imm - 64)); |
| 4080 | } |
| 4081 | try self.asmMemoryRegister( |
| 4082 | .mov, |
| 4083 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[1] - dst_off }), |
| 4084 | tmp_reg, |
| 4085 | ); |
| 4086 | switch (tag) { |
| 4087 | .shl, .sal, .shr => { |
| 4088 | try self.asmRegisterRegister(.xor, tmp_reg.to32(), tmp_reg.to32()); |
| 4089 | try self.asmMemoryRegister( |
| 4090 | .mov, |
| 4091 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4092 | tmp_reg, |
| 4093 | ); |
| 4094 | }, |
| 4095 | .sar => try self.asmMemoryImmediate( |
| 4096 | tag, |
| 4097 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4098 | Immediate.u(63), |
| 4099 | ), |
| 4100 | else => unreachable, |
| 4101 | } |
| 4102 | }, |
| 4103 | else => { |
| 4104 | const first_reg = try self.register_manager.allocReg(null, gp); |
| 4105 | const first_lock = self.register_manager.lockRegAssumeUnused(first_reg); |
| 4106 | defer self.register_manager.unlockReg(first_lock); |
| 4107 | |
| 4108 | const second_reg = try self.register_manager.allocReg(null, gp); |
| 4109 | const second_lock = self.register_manager.lockRegAssumeUnused(second_reg); |
| 4110 | defer self.register_manager.unlockReg(second_lock); |
| 3829 | 4111 | |
| 3830 | | try self.asmRegisterRegister(tag, registerAlias(reg, abi_size), .cl); |
| 4112 | try self.genSetReg(Type.u8, .cl, rhs_mcv); |
| 4113 | try self.asmRegisterMemory( |
| 4114 | .mov, |
| 4115 | first_reg, |
| 4116 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4117 | ); |
| 4118 | try self.asmRegisterMemory( |
| 4119 | .mov, |
| 4120 | second_reg, |
| 4121 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[1] - dst_off }), |
| 4122 | ); |
| 4123 | switch (tag) { |
| 4124 | .shl, .sal, .shr => try self.asmRegisterRegister( |
| 4125 | .xor, |
| 4126 | tmp_reg.to32(), |
| 4127 | tmp_reg.to32(), |
| 4128 | ), |
| 4129 | .sar => { |
| 4130 | try self.asmRegisterRegister(.mov, tmp_reg, first_reg); |
| 4131 | try self.asmRegisterImmediate(tag, tmp_reg, Immediate.u(63)); |
| 4132 | }, |
| 4133 | else => unreachable, |
| 4134 | } |
| 4135 | try self.asmRegisterRegisterRegister(info.double_tag, second_reg, first_reg, .cl); |
| 4136 | try self.asmRegisterRegister(tag, first_reg, .cl); |
| 4137 | try self.asmRegisterImmediate(.cmp, .cl, Immediate.u(64)); |
| 4138 | try self.asmCmovccRegisterRegister(second_reg, first_reg, .ae); |
| 4139 | try self.asmCmovccRegisterRegister(first_reg, tmp_reg, .ae); |
| 4140 | try self.asmMemoryRegister( |
| 4141 | .mov, |
| 4142 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[1] - dst_off }), |
| 4143 | second_reg, |
| 4144 | ); |
| 4145 | try self.asmMemoryRegister( |
| 4146 | .mov, |
| 4147 | Memory.sib(.qword, .{ .base = .rbp, .disp = info.offsets[0] - dst_off }), |
| 4148 | first_reg, |
| 4149 | ); |
| 4150 | }, |
| 4151 | }, |
| 4152 | else => return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ |
| 4153 | @tagName(lhs_mcv), |
| 4154 | @tagName(rhs_mcv), |
| 4155 | }), |
| 4156 | } |
| 4157 | } else return self.fail("TODO genShiftBinOpMir between {s} and {s}", .{ |
| 4158 | @tagName(lhs_mcv), |
| 4159 | @tagName(rhs_mcv), |
| 4160 | }); |
| 3831 | 4161 | } |
| 3832 | 4162 | |
| 3833 | 4163 | /// Result is always a register. |
| ... | ... | @@ -3837,68 +4167,61 @@ fn genShiftBinOp( |
| 3837 | 4167 | self: *Self, |
| 3838 | 4168 | tag: Air.Inst.Tag, |
| 3839 | 4169 | maybe_inst: ?Air.Inst.Index, |
| 3840 | | lhs: MCValue, |
| 3841 | | rhs: MCValue, |
| 4170 | lhs_mcv: MCValue, |
| 4171 | rhs_mcv: MCValue, |
| 3842 | 4172 | lhs_ty: Type, |
| 3843 | 4173 | rhs_ty: Type, |
| 3844 | 4174 | ) !MCValue { |
| 3845 | | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { |
| 4175 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 3846 | 4176 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| 3847 | 4177 | } |
| 3848 | | if (lhs_ty.abiSize(self.target.*) > 8) { |
| 4178 | |
| 4179 | assert(rhs_ty.abiSize(self.target.*) == 1); |
| 4180 | |
| 4181 | const lhs_abi_size = lhs_ty.abiSize(self.target.*); |
| 4182 | if (lhs_abi_size > 16) { |
| 3849 | 4183 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); |
| 3850 | 4184 | } |
| 3851 | 4185 | |
| 3852 | | assert(rhs_ty.abiSize(self.target.*) == 1); |
| 4186 | try self.register_manager.getReg(.rcx, null); |
| 4187 | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| 4188 | defer self.register_manager.unlockReg(rcx_lock); |
| 3853 | 4189 | |
| 3854 | | const lhs_lock: ?RegisterLock = switch (lhs) { |
| 4190 | const lhs_lock = switch (lhs_mcv) { |
| 3855 | 4191 | .register => |reg| self.register_manager.lockReg(reg), |
| 3856 | 4192 | else => null, |
| 3857 | 4193 | }; |
| 3858 | 4194 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3859 | 4195 | |
| 3860 | | const rhs_lock: ?RegisterLock = switch (rhs) { |
| 4196 | const rhs_lock = switch (rhs_mcv) { |
| 3861 | 4197 | .register => |reg| self.register_manager.lockReg(reg), |
| 3862 | 4198 | else => null, |
| 3863 | 4199 | }; |
| 3864 | 4200 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3865 | 4201 | |
| 3866 | | self.register_manager.getRegAssumeFree(.rcx, null); |
| 3867 | | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); |
| 3868 | | defer self.register_manager.unlockReg(rcx_lock); |
| 3869 | | |
| 3870 | | const dst: MCValue = blk: { |
| 4202 | const dst_mcv: MCValue = dst: { |
| 3871 | 4203 | if (maybe_inst) |inst| { |
| 3872 | 4204 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3873 | | // TODO dst can also be a memory location |
| 3874 | | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs) and lhs.isRegister()) { |
| 3875 | | break :blk lhs; |
| 3876 | | } |
| 3877 | | break :blk try self.copyToRegisterWithInstTracking(inst, lhs_ty, lhs); |
| 4205 | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) break :dst lhs_mcv; |
| 3878 | 4206 | } |
| 3879 | | break :blk MCValue{ .register = try self.copyToTmpRegister(lhs_ty, lhs) }; |
| 4207 | const dst_mcv = try self.allocRegOrMemAdvanced(lhs_ty, maybe_inst, true); |
| 4208 | try self.setRegOrMem(lhs_ty, dst_mcv, lhs_mcv); |
| 4209 | break :dst dst_mcv; |
| 3880 | 4210 | }; |
| 3881 | 4211 | |
| 3882 | 4212 | const signedness = lhs_ty.intInfo(self.target.*).signedness; |
| 3883 | | switch (tag) { |
| 3884 | | .shl => try self.genShiftBinOpMir(switch (signedness) { |
| 4213 | try self.genShiftBinOpMir(switch (tag) { |
| 4214 | .shl, .shl_exact => switch (signedness) { |
| 3885 | 4215 | .signed => .sal, |
| 3886 | 4216 | .unsigned => .shl, |
| 3887 | | }, lhs_ty, dst.register, rhs), |
| 3888 | | |
| 3889 | | .shl_exact => try self.genShiftBinOpMir(.shl, lhs_ty, dst.register, rhs), |
| 3890 | | |
| 3891 | | .shr, |
| 3892 | | .shr_exact, |
| 3893 | | => try self.genShiftBinOpMir(switch (signedness) { |
| 4217 | }, |
| 4218 | .shr, .shr_exact => switch (signedness) { |
| 3894 | 4219 | .signed => .sar, |
| 3895 | 4220 | .unsigned => .shr, |
| 3896 | | }, lhs_ty, dst.register, rhs), |
| 3897 | | |
| 4221 | }, |
| 3898 | 4222 | else => unreachable, |
| 3899 | | } |
| 3900 | | |
| 3901 | | return dst; |
| 4223 | }, lhs_ty, dst_mcv, rhs_mcv); |
| 4224 | return dst_mcv; |
| 3902 | 4225 | } |
| 3903 | 4226 | |
| 3904 | 4227 | /// Result is always a register. |
| ... | ... | @@ -3915,7 +4238,8 @@ fn genMulDivBinOp( |
| 3915 | 4238 | if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { |
| 3916 | 4239 | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); |
| 3917 | 4240 | } |
| 3918 | | if (ty.abiSize(self.target.*) > 8) { |
| 4241 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); |
| 4242 | if (abi_size > 8) { |
| 3919 | 4243 | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); |
| 3920 | 4244 | } |
| 3921 | 4245 | if (tag == .div_float) { |
| ... | ... | @@ -3925,10 +4249,8 @@ fn genMulDivBinOp( |
| 3925 | 4249 | assert(self.register_manager.isRegFree(.rax)); |
| 3926 | 4250 | assert(self.register_manager.isRegFree(.rdx)); |
| 3927 | 4251 | |
| 3928 | | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); |
| 3929 | | defer for (reg_locks) |reg| { |
| 3930 | | self.register_manager.unlockReg(reg); |
| 3931 | | }; |
| 4252 | const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); |
| 4253 | defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3932 | 4254 | |
| 3933 | 4255 | const int_info = ty.intInfo(self.target.*); |
| 3934 | 4256 | const signedness = int_info.signedness; |
| ... | ... | @@ -3953,35 +4275,24 @@ fn genMulDivBinOp( |
| 3953 | 4275 | |
| 3954 | 4276 | const mir_tag: Mir.Inst.Tag = switch (signedness) { |
| 3955 | 4277 | .signed => switch (tag) { |
| 3956 | | .mul, .mulwrap => Mir.Inst.Tag.imul, |
| 3957 | | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.idiv, |
| 4278 | .mul, .mulwrap => .imul, |
| 4279 | .div_trunc, .div_exact, .rem => .idiv, |
| 3958 | 4280 | else => unreachable, |
| 3959 | 4281 | }, |
| 3960 | 4282 | .unsigned => switch (tag) { |
| 3961 | | .mul, .mulwrap => Mir.Inst.Tag.mul, |
| 3962 | | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.div, |
| 4283 | .mul, .mulwrap => .mul, |
| 4284 | .div_trunc, .div_exact, .rem => .div, |
| 3963 | 4285 | else => unreachable, |
| 3964 | 4286 | }, |
| 3965 | 4287 | }; |
| 3966 | 4288 | |
| 3967 | 4289 | try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); |
| 3968 | 4290 | |
| 3969 | | switch (signedness) { |
| 3970 | | .signed => switch (tag) { |
| 3971 | | .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ .register = .rax }, |
| 3972 | | .rem => return MCValue{ .register = .rdx }, |
| 3973 | | else => unreachable, |
| 3974 | | }, |
| 3975 | | .unsigned => switch (tag) { |
| 3976 | | .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ |
| 3977 | | .register = registerAlias(.rax, @intCast(u32, ty.abiSize(self.target.*))), |
| 3978 | | }, |
| 3979 | | .rem => return MCValue{ |
| 3980 | | .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))), |
| 3981 | | }, |
| 3982 | | else => unreachable, |
| 3983 | | }, |
| 3984 | | } |
| 4291 | return .{ .register = registerAlias(switch (tag) { |
| 4292 | .mul, .mulwrap, .div_trunc, .div_exact => .rax, |
| 4293 | .rem => .rdx, |
| 4294 | else => unreachable, |
| 4295 | }, abi_size) }; |
| 3985 | 4296 | }, |
| 3986 | 4297 | |
| 3987 | 4298 | .mod => { |
| ... | ... | @@ -3998,14 +4309,14 @@ fn genMulDivBinOp( |
| 3998 | 4309 | const result: MCValue = if (maybe_inst) |inst| |
| 3999 | 4310 | try self.copyToRegisterWithInstTracking(inst, ty, lhs) |
| 4000 | 4311 | else |
| 4001 | | MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; |
| 4312 | .{ .register = try self.copyToTmpRegister(ty, lhs) }; |
| 4002 | 4313 | try self.genBinOpMir(.sub, ty, result, div_floor); |
| 4003 | 4314 | |
| 4004 | 4315 | return result; |
| 4005 | 4316 | }, |
| 4006 | 4317 | .unsigned => { |
| 4007 | 4318 | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); |
| 4008 | | return MCValue{ .register = registerAlias(.rdx, @intCast(u32, ty.abiSize(self.target.*))) }; |
| 4319 | return .{ .register = registerAlias(.rdx, abi_size) }; |
| 4009 | 4320 | }, |
| 4010 | 4321 | } |
| 4011 | 4322 | }, |
| ... | ... | @@ -4397,7 +4708,7 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 4397 | 4708 | ); |
| 4398 | 4709 | } |
| 4399 | 4710 | }, |
| 4400 | | else => return self.fail("TODO getBinOpMir implement large immediate ABI", .{}), |
| 4711 | else => return self.fail("TODO genBinOpMir implement large immediate ABI", .{}), |
| 4401 | 4712 | } |
| 4402 | 4713 | }, |
| 4403 | 4714 | .memory, |
| ... | ... | @@ -4478,28 +4789,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu |
| 4478 | 4789 | ); |
| 4479 | 4790 | } |
| 4480 | 4791 | }, |
| 4481 | | else => return self.fail("TODO getBinOpMir implement large immediate ABI", .{}), |
| 4792 | else => return self.fail("TODO genBinOpMir implement large immediate ABI", .{}), |
| 4482 | 4793 | } |
| 4483 | 4794 | }, |
| 4484 | 4795 | .memory, |
| 4485 | 4796 | .stack_offset, |
| 4486 | 4797 | .ptr_stack_offset, |
| 4487 | 4798 | => { |
| 4488 | | return self.fail("TODO implement x86 ADD/SUB/CMP source memory", .{}); |
| 4799 | return self.fail("TODO implement x86 genBinOpMir source memory", .{}); |
| 4489 | 4800 | }, |
| 4490 | 4801 | .linker_load => { |
| 4491 | | return self.fail("TODO implement x86 ADD/SUB/CMP source symbol at index in linker", .{}); |
| 4802 | return self.fail("TODO implement x86 genBinOpMir source symbol at index in linker", .{}); |
| 4492 | 4803 | }, |
| 4493 | 4804 | .eflags => { |
| 4494 | | return self.fail("TODO implement x86 ADD/SUB/CMP source eflags", .{}); |
| 4805 | return self.fail("TODO implement x86 genBinOpMir source eflags", .{}); |
| 4495 | 4806 | }, |
| 4496 | 4807 | } |
| 4497 | 4808 | }, |
| 4498 | 4809 | .memory => { |
| 4499 | | return self.fail("TODO implement x86 ADD/SUB/CMP destination memory", .{}); |
| 4810 | return self.fail("TODO implement x86 genBinOpMir destination memory", .{}); |
| 4500 | 4811 | }, |
| 4501 | 4812 | .linker_load => { |
| 4502 | | return self.fail("TODO implement x86 ADD/SUB/CMP destination symbol at index", .{}); |
| 4813 | return self.fail("TODO implement x86 genBinOpMir destination symbol at index", .{}); |
| 4503 | 4814 | }, |
| 4504 | 4815 | } |
| 4505 | 4816 | } |
| ... | ... | @@ -5437,7 +5748,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) ! |
| 5437 | 5748 | const tmp_reg = try self.copyToTmpRegister(ty, operand); |
| 5438 | 5749 | if (err_off > 0) { |
| 5439 | 5750 | const shift = @intCast(u6, err_off * 8); |
| 5440 | | try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ .immediate = shift }); |
| 5751 | try self.genShiftBinOpMir(.shr, ty, .{ .register = tmp_reg }, .{ .immediate = shift }); |
| 5441 | 5752 | } else { |
| 5442 | 5753 | try self.truncateRegister(Type.anyerror, tmp_reg); |
| 5443 | 5754 | } |
| ... | ... | @@ -5945,13 +6256,23 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5945 | 6256 | const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 5946 | 6257 | extra_i += inputs.len; |
| 5947 | 6258 | |
| 5948 | | const dead = !is_volatile and self.liveness.isUnused(inst); |
| 5949 | | const result: MCValue = if (dead) .dead else result: { |
| 6259 | var result: MCValue = .none; |
| 6260 | if (!is_volatile and self.liveness.isUnused(inst)) result = .dead else { |
| 6261 | var args = std.StringArrayHashMap(MCValue).init(self.gpa); |
| 6262 | try args.ensureTotalCapacity(outputs.len + inputs.len + clobbers_len); |
| 6263 | defer { |
| 6264 | for (args.values()) |arg| switch (arg) { |
| 6265 | .register => |reg| self.register_manager.unlockReg(.{ .register = reg }), |
| 6266 | else => {}, |
| 6267 | }; |
| 6268 | args.deinit(); |
| 6269 | } |
| 6270 | |
| 5950 | 6271 | if (outputs.len > 1) { |
| 5951 | 6272 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); |
| 5952 | 6273 | } |
| 5953 | 6274 | |
| 5954 | | const output_constraint: ?[]const u8 = for (outputs) |output| { |
| 6275 | for (outputs) |output| { |
| 5955 | 6276 | if (output != .none) { |
| 5956 | 6277 | return self.fail("TODO implement codegen for non-expr asm", .{}); |
| 5957 | 6278 | } |
| ... | ... | @@ -5962,8 +6283,21 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5962 | 6283 | // for the string, we still use the next u32 for the null terminator. |
| 5963 | 6284 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 5964 | 6285 | |
| 5965 | | break constraint; |
| 5966 | | } else null; |
| 6286 | const mcv: MCValue = if (mem.eql(u8, constraint, "=r")) |
| 6287 | .{ .register = self.register_manager.tryAllocReg(inst, gp) orelse |
| 6288 | return self.fail("ran out of registers lowering inline asm", .{}) } |
| 6289 | else if (mem.startsWith(u8, constraint, "={") and mem.endsWith(u8, constraint, "}")) |
| 6290 | .{ .register = parseRegName(constraint["={".len .. constraint.len - "}".len]) orelse |
| 6291 | return self.fail("unrecognized register constraint: '{s}'", .{constraint}) } |
| 6292 | else |
| 6293 | return self.fail("unrecognized constraint: '{s}'", .{constraint}); |
| 6294 | args.putAssumeCapacity(name, mcv); |
| 6295 | switch (mcv) { |
| 6296 | .register => |reg| _ = self.register_manager.lockRegAssumeUnused(reg), |
| 6297 | else => {}, |
| 6298 | } |
| 6299 | if (output == .none) result = mcv; |
| 6300 | } |
| 5967 | 6301 | |
| 5968 | 6302 | for (inputs) |input| { |
| 5969 | 6303 | const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); |
| ... | ... | @@ -5997,52 +6331,73 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5997 | 6331 | } |
| 5998 | 6332 | } |
| 5999 | 6333 | |
| 6000 | | const asm_source = std.mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; |
| 6001 | | |
| 6002 | | { |
| 6003 | | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); |
| 6004 | | while (iter.next()) |ins| { |
| 6005 | | if (mem.eql(u8, ins, "syscall")) { |
| 6006 | | try self.asmOpOnly(.syscall); |
| 6007 | | } else if (mem.indexOf(u8, ins, "push")) |_| { |
| 6008 | | const arg = ins[4..]; |
| 6009 | | if (mem.indexOf(u8, arg, "$")) |l| { |
| 6010 | | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { |
| 6011 | | return self.fail("TODO implement more inline asm int parsing", .{}); |
| 6012 | | }; |
| 6013 | | try self.asmImmediate(.push, Immediate.u(n)); |
| 6014 | | } else if (mem.indexOf(u8, arg, "%%")) |l| { |
| 6015 | | const reg_name = ins[4 + l + 2 ..]; |
| 6016 | | const reg = parseRegName(reg_name) orelse |
| 6017 | | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 6018 | | try self.asmRegister(.push, reg); |
| 6019 | | } else return self.fail("TODO more push operands", .{}); |
| 6020 | | } else if (mem.indexOf(u8, ins, "pop")) |_| { |
| 6021 | | const arg = ins[3..]; |
| 6022 | | if (mem.indexOf(u8, arg, "%%")) |l| { |
| 6023 | | const reg_name = ins[3 + l + 2 ..]; |
| 6024 | | const reg = parseRegName(reg_name) orelse |
| 6025 | | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 6026 | | try self.asmRegister(.pop, reg); |
| 6027 | | } else return self.fail("TODO more pop operands", .{}); |
| 6028 | | } else { |
| 6029 | | return self.fail("TODO implement support for more x86 assembly instructions", .{}); |
| 6334 | const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; |
| 6335 | var line_it = mem.tokenize(u8, asm_source, "\n\r"); |
| 6336 | while (line_it.next()) |line| { |
| 6337 | var mnem_it = mem.tokenize(u8, line, " \t"); |
| 6338 | const mnem = mnem_it.next() orelse continue; |
| 6339 | if (mem.startsWith(u8, mnem, "#")) continue; |
| 6340 | var arg_it = mem.tokenize(u8, mnem_it.rest(), ", "); |
| 6341 | if (std.ascii.eqlIgnoreCase(mnem, "syscall")) { |
| 6342 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6343 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6344 | try self.asmOpOnly(.syscall); |
| 6345 | } else if (std.ascii.eqlIgnoreCase(mnem, "push")) { |
| 6346 | const src = arg_it.next() orelse |
| 6347 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6348 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6349 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6350 | if (mem.startsWith(u8, src, "$")) { |
| 6351 | const imm = std.fmt.parseInt(u32, src["$".len..], 0) catch |
| 6352 | return self.fail("Invalid immediate: '{s}'", .{src}); |
| 6353 | try self.asmImmediate(.push, Immediate.u(imm)); |
| 6354 | } else if (mem.startsWith(u8, src, "%%")) { |
| 6355 | const reg = parseRegName(src["%%".len..]) orelse |
| 6356 | return self.fail("Invalid register: '{s}'", .{src}); |
| 6357 | try self.asmRegister(.push, reg); |
| 6358 | } else return self.fail("Unsupported operand: '{s}'", .{src}); |
| 6359 | } else if (std.ascii.eqlIgnoreCase(mnem, "pop")) { |
| 6360 | const dst = arg_it.next() orelse |
| 6361 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6362 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6363 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6364 | if (mem.startsWith(u8, dst, "%%")) { |
| 6365 | const reg = parseRegName(dst["%%".len..]) orelse |
| 6366 | return self.fail("Invalid register: '{s}'", .{dst}); |
| 6367 | try self.asmRegister(.pop, reg); |
| 6368 | } else return self.fail("Unsupported operand: '{s}'", .{dst}); |
| 6369 | } else if (std.ascii.eqlIgnoreCase(mnem, "movq")) { |
| 6370 | const src = arg_it.next() orelse |
| 6371 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6372 | const dst = arg_it.next() orelse |
| 6373 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6374 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6375 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6376 | if (mem.startsWith(u8, src, "%%")) { |
| 6377 | const colon = mem.indexOfScalarPos(u8, src, "%%".len + 2, ':'); |
| 6378 | const src_reg = parseRegName(src["%%".len .. colon orelse src.len]) orelse |
| 6379 | return self.fail("Invalid register: '{s}'", .{src}); |
| 6380 | if (colon) |colon_pos| { |
| 6381 | const src_disp = std.fmt.parseInt(i32, src[colon_pos + 1 ..], 0) catch |
| 6382 | return self.fail("Invalid immediate: '{s}'", .{src}); |
| 6383 | if (mem.startsWith(u8, dst, "%[") and mem.endsWith(u8, dst, "]")) { |
| 6384 | switch (args.get(dst["%[".len .. dst.len - "]".len]) orelse |
| 6385 | return self.fail("no matching constraint for: '{s}'", .{dst})) { |
| 6386 | .register => |dst_reg| try self.asmRegisterMemory( |
| 6387 | .mov, |
| 6388 | dst_reg, |
| 6389 | Memory.sib(.qword, .{ .base = src_reg, .disp = src_disp }), |
| 6390 | ), |
| 6391 | else => return self.fail("Invalid constraint: '{s}'", .{dst}), |
| 6392 | } |
| 6393 | } else return self.fail("Unsupported operand: '{s}'", .{dst}); |
| 6394 | } else return self.fail("Unsupported operand: '{s}'", .{src}); |
| 6030 | 6395 | } |
| 6396 | } else { |
| 6397 | return self.fail("Unsupported instruction: '{s}'", .{mnem}); |
| 6031 | 6398 | } |
| 6032 | 6399 | } |
| 6033 | | |
| 6034 | | if (output_constraint) |output| { |
| 6035 | | if (output.len < 4 or output[0] != '=' or output[1] != '{' or output[output.len - 1] != '}') { |
| 6036 | | return self.fail("unrecognized asm output constraint: '{s}'", .{output}); |
| 6037 | | } |
| 6038 | | const reg_name = output[2 .. output.len - 1]; |
| 6039 | | const reg = parseRegName(reg_name) orelse |
| 6040 | | return self.fail("unrecognized register: '{s}'", .{reg_name}); |
| 6041 | | break :result .{ .register = reg }; |
| 6042 | | } else { |
| 6043 | | break :result .none; |
| 6044 | | } |
| 6045 | | }; |
| 6400 | } |
| 6046 | 6401 | |
| 6047 | 6402 | simple: { |
| 6048 | 6403 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); |
| ... | ... | @@ -6277,20 +6632,25 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6277 | 6632 | .disp = -stack_offset, |
| 6278 | 6633 | }), immediate); |
| 6279 | 6634 | }, |
| 6280 | | 8 => { |
| 6635 | 3, 5...7 => unreachable, |
| 6636 | else => { |
| 6281 | 6637 | // 64 bit write to memory would take two mov's anyways so we |
| 6282 | 6638 | // insted just use two 32 bit writes to avoid register allocation |
| 6283 | | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ |
| 6284 | | .base = base_reg, |
| 6285 | | .disp = -stack_offset + 4, |
| 6286 | | }), Immediate.u(@truncate(u32, x_big >> 32))); |
| 6287 | | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ |
| 6288 | | .base = base_reg, |
| 6289 | | .disp = -stack_offset, |
| 6290 | | }), Immediate.u(@truncate(u32, x_big))); |
| 6291 | | }, |
| 6292 | | else => { |
| 6293 | | return self.fail("TODO implement set abi_size=large stack variable with immediate", .{}); |
| 6639 | var offset: i32 = 0; |
| 6640 | while (offset < abi_size) : (offset += 4) try self.asmMemoryImmediate( |
| 6641 | .mov, |
| 6642 | Memory.sib(.dword, .{ .base = base_reg, .disp = offset - stack_offset }), |
| 6643 | if (ty.isSignedInt()) |
| 6644 | Immediate.s(@truncate( |
| 6645 | i32, |
| 6646 | @bitCast(i64, x_big) >> (math.cast(u6, offset * 8) orelse 63), |
| 6647 | )) |
| 6648 | else |
| 6649 | Immediate.u(@truncate( |
| 6650 | u32, |
| 6651 | if (math.cast(u6, offset * 8)) |shift| x_big >> shift else 0, |
| 6652 | )), |
| 6653 | ); |
| 6294 | 6654 | }, |
| 6295 | 6655 | } |
| 6296 | 6656 | }, |
| ... | ... | @@ -6391,7 +6751,7 @@ fn genInlineMemcpyRegisterRegister( |
| 6391 | 6751 | }), registerAlias(tmp_reg, nearest_power_of_two)); |
| 6392 | 6752 | |
| 6393 | 6753 | if (nearest_power_of_two > 1) { |
| 6394 | | try self.genShiftBinOpMir(.shr, ty, tmp_reg, .{ |
| 6754 | try self.genShiftBinOpMir(.shr, ty, .{ .register = tmp_reg }, .{ |
| 6395 | 6755 | .immediate = nearest_power_of_two * 8, |
| 6396 | 6756 | }); |
| 6397 | 6757 | } |
| ... | ... | @@ -6894,20 +7254,37 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 6894 | 7254 | const ptr_ty = self.air.typeOf(extra.ptr); |
| 6895 | 7255 | const ptr_mcv = try self.resolveInst(extra.ptr); |
| 6896 | 7256 | const val_ty = self.air.typeOf(extra.expected_value); |
| 7257 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); |
| 7258 | |
| 7259 | try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx }); |
| 7260 | const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx }); |
| 7261 | for (regs_lock) |lock| self.register_manager.unlockReg(lock); |
| 6897 | 7262 | |
| 6898 | 7263 | const exp_mcv = try self.resolveInst(extra.expected_value); |
| 6899 | | try self.genSetReg(val_ty, .rax, exp_mcv); |
| 7264 | if (val_abi_size > 8) switch (exp_mcv) { |
| 7265 | .stack_offset => |exp_off| { |
| 7266 | try self.genSetReg(Type.usize, .rax, .{ .stack_offset = exp_off - 0 }); |
| 7267 | try self.genSetReg(Type.usize, .rdx, .{ .stack_offset = exp_off - 8 }); |
| 7268 | }, |
| 7269 | else => return self.fail("TODO implement cmpxchg for {s}", .{@tagName(exp_mcv)}), |
| 7270 | } else try self.genSetReg(val_ty, .rax, exp_mcv); |
| 6900 | 7271 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); |
| 6901 | 7272 | defer self.register_manager.unlockReg(rax_lock); |
| 6902 | 7273 | |
| 6903 | 7274 | const new_mcv = try self.resolveInst(extra.new_value); |
| 6904 | | const new_reg = try self.copyToTmpRegister(val_ty, new_mcv); |
| 7275 | const new_reg: Register = if (val_abi_size > 8) switch (new_mcv) { |
| 7276 | .stack_offset => |new_off| new: { |
| 7277 | try self.genSetReg(Type.usize, .rbx, .{ .stack_offset = new_off - 0 }); |
| 7278 | try self.genSetReg(Type.usize, .rcx, .{ .stack_offset = new_off - 8 }); |
| 7279 | break :new undefined; |
| 7280 | }, |
| 7281 | else => return self.fail("TODO implement cmpxchg for {s}", .{@tagName(exp_mcv)}), |
| 7282 | } else try self.copyToTmpRegister(val_ty, new_mcv); |
| 6905 | 7283 | const new_lock = self.register_manager.lockRegAssumeUnused(new_reg); |
| 6906 | 7284 | defer self.register_manager.unlockReg(new_lock); |
| 6907 | 7285 | |
| 6908 | | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); |
| 6909 | 7286 | const ptr_size = Memory.PtrSize.fromSize(val_abi_size); |
| 6910 | | const ptr_mem: Memory = switch (ptr_mcv) { |
| 7287 | const ptr_mem = switch (ptr_mcv) { |
| 6911 | 7288 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), |
| 6912 | 7289 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), |
| 6913 | 7290 | else => Memory.sib(ptr_size, .{ .base = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }), |
| ... | ... | @@ -6916,16 +7293,30 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 6916 | 7293 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); |
| 6917 | 7294 | |
| 6918 | 7295 | try self.spillEflagsIfOccupied(); |
| 6919 | | _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ |
| 6920 | | .r1 = new_reg, |
| 6921 | | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 6922 | | } } }); |
| 7296 | if (val_abi_size <= 8) { |
| 7297 | _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ |
| 7298 | .r = registerAlias(new_reg, val_abi_size), |
| 7299 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7300 | } } }); |
| 7301 | } else { |
| 7302 | _ = try self.addInst(.{ .tag = .cmpxchgb, .ops = .lock_m_sib, .data = .{ |
| 7303 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7304 | } }); |
| 7305 | } |
| 6923 | 7306 | |
| 6924 | 7307 | const result: MCValue = result: { |
| 6925 | 7308 | if (self.liveness.isUnused(inst)) break :result .dead; |
| 6926 | 7309 | |
| 6927 | | self.eflags_inst = inst; |
| 6928 | | break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } }; |
| 7310 | if (val_abi_size <= 8) { |
| 7311 | self.eflags_inst = inst; |
| 7312 | break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } }; |
| 7313 | } |
| 7314 | |
| 7315 | const dst_mcv = try self.allocRegOrMem(inst, false); |
| 7316 | try self.genSetStack(Type.bool, dst_mcv.stack_offset - 16, .{ .eflags = .ne }, .{}); |
| 7317 | try self.genSetStack(Type.usize, dst_mcv.stack_offset - 8, .{ .register = .rdx }, .{}); |
| 7318 | try self.genSetStack(Type.usize, dst_mcv.stack_offset - 0, .{ .register = .rax }, .{}); |
| 7319 | break :result dst_mcv; |
| 6929 | 7320 | }; |
| 6930 | 7321 | return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); |
| 6931 | 7322 | } |
| ... | ... | @@ -6938,9 +7329,10 @@ fn atomicOp( |
| 6938 | 7329 | ptr_ty: Type, |
| 6939 | 7330 | val_ty: Type, |
| 6940 | 7331 | unused: bool, |
| 6941 | | op: ?std.builtin.AtomicRmwOp, |
| 7332 | rmw_op: ?std.builtin.AtomicRmwOp, |
| 6942 | 7333 | order: std.builtin.AtomicOrder, |
| 6943 | 7334 | ) InnerError!void { |
| 7335 | const dst_mcv = MCValue{ .register = dst_reg }; |
| 6944 | 7336 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 6945 | 7337 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 6946 | 7338 | |
| ... | ... | @@ -6958,7 +7350,7 @@ fn atomicOp( |
| 6958 | 7350 | |
| 6959 | 7351 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); |
| 6960 | 7352 | const ptr_size = Memory.PtrSize.fromSize(val_abi_size); |
| 6961 | | const ptr_mem: Memory = switch (ptr_mcv) { |
| 7353 | const ptr_mem = switch (ptr_mcv) { |
| 6962 | 7354 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), |
| 6963 | 7355 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), |
| 6964 | 7356 | else => Memory.sib(ptr_size, .{ .base = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }), |
| ... | ... | @@ -6966,48 +7358,197 @@ fn atomicOp( |
| 6966 | 7358 | const mem_lock = if (ptr_mem.base()) |reg| self.register_manager.lockReg(reg) else null; |
| 6967 | 7359 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); |
| 6968 | 7360 | |
| 6969 | | try self.genSetReg(val_ty, dst_reg, val_mcv); |
| 6970 | | |
| 6971 | | const need_loop = val_ty.isRuntimeFloat() or if (op) |rmw| switch (rmw) { |
| 6972 | | .Xchg, .Add, .Sub => false, |
| 6973 | | .And, .Or, .Xor => !unused, |
| 6974 | | .Nand, .Max, .Min => true, |
| 6975 | | } else false; |
| 6976 | | if (!need_loop) { |
| 6977 | | const tag: Mir.Inst.Tag = if (op) |rmw| switch (rmw) { |
| 6978 | | .Xchg => if (unused) .mov else .xchg, |
| 6979 | | .Add => if (unused) .add else .xadd, |
| 6980 | | .Sub => if (unused) .sub else .xadd, |
| 6981 | | .And => .@"and", |
| 6982 | | .Or => .@"or", |
| 6983 | | .Xor => .xor, |
| 6984 | | else => unreachable, |
| 6985 | | } else switch (order) { |
| 6986 | | .Unordered, .Monotonic, .Release, .AcqRel => .mov, |
| 6987 | | .Acquire => unreachable, |
| 6988 | | .SeqCst => .xchg, |
| 6989 | | }; |
| 6990 | | if (op == std.builtin.AtomicRmwOp.Sub and tag == .xadd) { |
| 6991 | | try self.genUnOpMir(.neg, val_ty, .{ .register = dst_reg }); |
| 6992 | | } |
| 6993 | | _ = try self.addInst(.{ .tag = tag, .ops = switch (tag) { |
| 6994 | | .mov, .xchg => .mr_sib, |
| 6995 | | .xadd, .add, .sub, .@"and", .@"or", .xor => .lock_mr_sib, |
| 6996 | | else => unreachable, |
| 6997 | | }, .data = .{ .rx = .{ |
| 6998 | | .r1 = registerAlias(dst_reg, val_abi_size), |
| 6999 | | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7000 | | } } }); |
| 7001 | | return; |
| 7002 | | } |
| 7361 | const method: enum { lock, loop, libcall } = if (val_ty.isRuntimeFloat()) |
| 7362 | .loop |
| 7363 | else switch (rmw_op orelse .Xchg) { |
| 7364 | .Xchg, |
| 7365 | .Add, |
| 7366 | .Sub, |
| 7367 | => if (val_abi_size <= 8) .lock else if (val_abi_size <= 16) .loop else .libcall, |
| 7368 | .And, |
| 7369 | .Or, |
| 7370 | .Xor, |
| 7371 | => if (val_abi_size <= 8 and unused) .lock else if (val_abi_size <= 16) .loop else .libcall, |
| 7372 | .Nand, |
| 7373 | .Max, |
| 7374 | .Min, |
| 7375 | => if (val_abi_size <= 16) .loop else .libcall, |
| 7376 | }; |
| 7377 | switch (method) { |
| 7378 | .lock => { |
| 7379 | const tag: Mir.Inst.Tag = if (rmw_op) |op| switch (op) { |
| 7380 | .Xchg => if (unused) .mov else .xchg, |
| 7381 | .Add => if (unused) .add else .xadd, |
| 7382 | .Sub => if (unused) .sub else .xadd, |
| 7383 | .And => .@"and", |
| 7384 | .Or => .@"or", |
| 7385 | .Xor => .xor, |
| 7386 | else => unreachable, |
| 7387 | } else switch (order) { |
| 7388 | .Unordered, .Monotonic, .Release, .AcqRel => .mov, |
| 7389 | .Acquire => unreachable, |
| 7390 | .SeqCst => .xchg, |
| 7391 | }; |
| 7392 | |
| 7393 | try self.genSetReg(val_ty, dst_reg, val_mcv); |
| 7394 | if (rmw_op == std.builtin.AtomicRmwOp.Sub and tag == .xadd) { |
| 7395 | try self.genUnOpMir(.neg, val_ty, .{ .register = dst_reg }); |
| 7396 | } |
| 7397 | _ = try self.addInst(.{ .tag = tag, .ops = switch (tag) { |
| 7398 | .mov, .xchg => .mr_sib, |
| 7399 | .xadd, .add, .sub, .@"and", .@"or", .xor => .lock_mr_sib, |
| 7400 | else => unreachable, |
| 7401 | }, .data = .{ .rx = .{ |
| 7402 | .r = registerAlias(dst_reg, val_abi_size), |
| 7403 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7404 | } } }); |
| 7405 | }, |
| 7406 | .loop => _ = try self.asmJccReloc(if (val_abi_size <= 8) loop: { |
| 7407 | try self.genSetReg(val_ty, dst_reg, val_mcv); |
| 7408 | try self.asmRegisterMemory(.mov, registerAlias(.rax, val_abi_size), ptr_mem); |
| 7409 | const loop = @intCast(u32, self.mir_instructions.len); |
| 7410 | if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { |
| 7411 | try self.genSetReg(val_ty, dst_reg, .{ .register = .rax }); |
| 7412 | } |
| 7413 | if (rmw_op) |op| switch (op) { |
| 7414 | .Xchg => try self.genSetReg(val_ty, dst_reg, val_mcv), |
| 7415 | .Add => try self.genBinOpMir(.add, val_ty, dst_mcv, val_mcv), |
| 7416 | .Sub => try self.genBinOpMir(.sub, val_ty, dst_mcv, val_mcv), |
| 7417 | .And => try self.genBinOpMir(.@"and", val_ty, dst_mcv, val_mcv), |
| 7418 | .Nand => { |
| 7419 | try self.genBinOpMir(.@"and", val_ty, dst_mcv, val_mcv); |
| 7420 | try self.genUnOpMir(.not, val_ty, dst_mcv); |
| 7421 | }, |
| 7422 | .Or => try self.genBinOpMir(.@"or", val_ty, dst_mcv, val_mcv), |
| 7423 | .Xor => try self.genBinOpMir(.xor, val_ty, dst_mcv, val_mcv), |
| 7424 | .Min, .Max => { |
| 7425 | const cc: Condition = switch (if (val_ty.isAbiInt()) |
| 7426 | val_ty.intInfo(self.target.*).signedness |
| 7427 | else |
| 7428 | .unsigned) { |
| 7429 | .unsigned => switch (op) { |
| 7430 | .Min => .a, |
| 7431 | .Max => .b, |
| 7432 | else => unreachable, |
| 7433 | }, |
| 7434 | .signed => switch (op) { |
| 7435 | .Min => .g, |
| 7436 | .Max => .l, |
| 7437 | else => unreachable, |
| 7438 | }, |
| 7439 | }; |
| 7003 | 7440 | |
| 7004 | | return self.fail("TODO implement x86 atomic loop", .{}); |
| 7441 | try self.genBinOpMir(.cmp, val_ty, dst_mcv, val_mcv); |
| 7442 | switch (val_mcv) { |
| 7443 | .register => |val_reg| try self.asmCmovccRegisterRegister( |
| 7444 | registerAlias(dst_reg, val_abi_size), |
| 7445 | registerAlias(val_reg, val_abi_size), |
| 7446 | cc, |
| 7447 | ), |
| 7448 | .stack_offset => |val_off| try self.asmCmovccRegisterMemory( |
| 7449 | registerAlias(dst_reg, val_abi_size), |
| 7450 | Memory.sib( |
| 7451 | Memory.PtrSize.fromSize(val_abi_size), |
| 7452 | .{ .base = .rbp, .disp = -val_off }, |
| 7453 | ), |
| 7454 | cc, |
| 7455 | ), |
| 7456 | else => { |
| 7457 | const val_reg = try self.copyToTmpRegister(val_ty, val_mcv); |
| 7458 | try self.asmCmovccRegisterRegister( |
| 7459 | registerAlias(dst_reg, val_abi_size), |
| 7460 | registerAlias(val_reg, val_abi_size), |
| 7461 | cc, |
| 7462 | ); |
| 7463 | }, |
| 7464 | } |
| 7465 | }, |
| 7466 | }; |
| 7467 | _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ |
| 7468 | .r = registerAlias(dst_reg, val_abi_size), |
| 7469 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7470 | } } }); |
| 7471 | break :loop loop; |
| 7472 | } else loop: { |
| 7473 | try self.asmRegisterMemory(.mov, .rax, Memory.sib(.qword, .{ |
| 7474 | .base = ptr_mem.sib.base, |
| 7475 | .scale_index = ptr_mem.sib.scale_index, |
| 7476 | .disp = ptr_mem.sib.disp + 0, |
| 7477 | })); |
| 7478 | try self.asmRegisterMemory(.mov, .rdx, Memory.sib(.qword, .{ |
| 7479 | .base = ptr_mem.sib.base, |
| 7480 | .scale_index = ptr_mem.sib.scale_index, |
| 7481 | .disp = ptr_mem.sib.disp + 8, |
| 7482 | })); |
| 7483 | const loop = @intCast(u32, self.mir_instructions.len); |
| 7484 | switch (val_mcv) { |
| 7485 | .stack_offset => |val_off| { |
| 7486 | const val_lo_mem = Memory.sib(.qword, .{ .base = .rbp, .disp = 0 - val_off }); |
| 7487 | const val_hi_mem = Memory.sib(.qword, .{ .base = .rbp, .disp = 8 - val_off }); |
| 7488 | |
| 7489 | if (rmw_op != std.builtin.AtomicRmwOp.Xchg) { |
| 7490 | try self.asmRegisterRegister(.mov, .rbx, .rax); |
| 7491 | try self.asmRegisterRegister(.mov, .rcx, .rdx); |
| 7492 | } |
| 7493 | if (rmw_op) |op| switch (op) { |
| 7494 | .Xchg => { |
| 7495 | try self.asmRegisterMemory(.mov, .rbx, val_lo_mem); |
| 7496 | try self.asmRegisterMemory(.mov, .rcx, val_hi_mem); |
| 7497 | }, |
| 7498 | .Add => { |
| 7499 | try self.asmRegisterMemory(.add, .rbx, val_lo_mem); |
| 7500 | try self.asmRegisterMemory(.adc, .rcx, val_hi_mem); |
| 7501 | }, |
| 7502 | .Sub => { |
| 7503 | try self.asmRegisterMemory(.sub, .rbx, val_lo_mem); |
| 7504 | try self.asmRegisterMemory(.sbb, .rcx, val_hi_mem); |
| 7505 | }, |
| 7506 | .And => { |
| 7507 | try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); |
| 7508 | try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); |
| 7509 | }, |
| 7510 | .Nand => { |
| 7511 | try self.asmRegisterMemory(.@"and", .rbx, val_lo_mem); |
| 7512 | try self.asmRegisterMemory(.@"and", .rcx, val_hi_mem); |
| 7513 | try self.asmRegister(.not, .rbx); |
| 7514 | try self.asmRegister(.not, .rcx); |
| 7515 | }, |
| 7516 | .Or => { |
| 7517 | try self.asmRegisterMemory(.@"or", .rbx, val_lo_mem); |
| 7518 | try self.asmRegisterMemory(.@"or", .rcx, val_hi_mem); |
| 7519 | }, |
| 7520 | .Xor => { |
| 7521 | try self.asmRegisterMemory(.xor, .rbx, val_lo_mem); |
| 7522 | try self.asmRegisterMemory(.xor, .rcx, val_hi_mem); |
| 7523 | }, |
| 7524 | else => return self.fail( |
| 7525 | "TODO implement x86 atomic loop for large abi {s}", |
| 7526 | .{@tagName(op)}, |
| 7527 | ), |
| 7528 | }; |
| 7529 | }, |
| 7530 | else => return self.fail( |
| 7531 | "TODO implement x86 atomic loop for large abi {s}", |
| 7532 | .{@tagName(val_mcv)}, |
| 7533 | ), |
| 7534 | } |
| 7535 | _ = try self.addInst(.{ .tag = .cmpxchgb, .ops = .lock_m_sib, .data = .{ |
| 7536 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), |
| 7537 | } }); |
| 7538 | break :loop loop; |
| 7539 | }, .ne), |
| 7540 | .libcall => return self.fail("TODO implement x86 atomic libcall", .{}), |
| 7541 | } |
| 7005 | 7542 | } |
| 7006 | 7543 | |
| 7007 | 7544 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { |
| 7008 | 7545 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 7009 | 7546 | const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data; |
| 7010 | 7547 | |
| 7548 | try self.spillRegisters(&.{ .rax, .rdx, .rbx, .rcx }); |
| 7549 | const regs_lock = self.register_manager.lockRegsAssumeUnused(4, .{ .rax, .rdx, .rbx, .rcx }); |
| 7550 | defer for (regs_lock) |lock| self.register_manager.unlockReg(lock); |
| 7551 | |
| 7011 | 7552 | const unused = self.liveness.isUnused(inst); |
| 7012 | 7553 | const dst_reg = try self.register_manager.allocReg(if (unused) null else inst, gp); |
| 7013 | 7554 | |
| ... | ... | @@ -7587,8 +8128,8 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 7587 | 8128 | switch (int_info.signedness) { |
| 7588 | 8129 | .signed => { |
| 7589 | 8130 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 7590 | | try self.genShiftBinOpMir(.sal, Type.isize, reg, .{ .immediate = shift }); |
| 7591 | | try self.genShiftBinOpMir(.sar, Type.isize, reg, .{ .immediate = shift }); |
| 8131 | try self.genShiftBinOpMir(.sal, Type.isize, .{ .register = reg }, .{ .immediate = shift }); |
| 8132 | try self.genShiftBinOpMir(.sar, Type.isize, .{ .register = reg }, .{ .immediate = shift }); |
| 7592 | 8133 | }, |
| 7593 | 8134 | .unsigned => { |
| 7594 | 8135 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |