| ... | @@ -409,10 +409,7 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void { | ... | @@ -409,10 +409,7 @@ fn asmSetccRegister(self: *Self, reg: Register, cc: bits.Condition) !void { |
| 409 | _ = try self.addInst(.{ | 409 | _ = try self.addInst(.{ |
| 410 | .tag = .setcc, | 410 | .tag = .setcc, |
| 411 | .ops = .r_cc, | 411 | .ops = .r_cc, |
| 412 | .data = .{ .r_cc = .{ | 412 | .data = .{ .r_cc = .{ .r = reg, .cc = cc } }, |
| 413 | .r1 = reg, | | |
| 414 | .cc = cc, | | |
| 415 | } }, | | |
| 416 | }); | 413 | }); |
| 417 | } | 414 | } |
| 418 | | 415 | |
| ... | @@ -424,14 +421,11 @@ fn asmSetccMemory(self: *Self, m: Memory, cc: bits.Condition) !void { | ... | @@ -424,14 +421,11 @@ fn asmSetccMemory(self: *Self, m: Memory, cc: bits.Condition) !void { |
| 424 | .rip => .m_rip_cc, | 421 | .rip => .m_rip_cc, |
| 425 | else => unreachable, | 422 | else => unreachable, |
| 426 | }, | 423 | }, |
| 427 | .data = .{ .x_cc = .{ | 424 | .data = .{ .x_cc = .{ .cc = cc, .payload = switch (m) { |
| 428 | .payload = switch (m) { | 425 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 429 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), | 426 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 430 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), | 427 | else => unreachable, |
| 431 | else => unreachable, | 428 | } } }, |
| 432 | }, | | |
| 433 | .cc = cc, | | |
| 434 | } }, | | |
| 435 | }); | 429 | }); |
| 436 | } | 430 | } |
| 437 | | 431 | |
| ... | @@ -439,11 +433,7 @@ fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bi | ... | @@ -439,11 +433,7 @@ fn asmCmovccRegisterRegister(self: *Self, reg1: Register, reg2: Register, cc: bi |
| 439 | _ = try self.addInst(.{ | 433 | _ = try self.addInst(.{ |
| 440 | .tag = .cmovcc, | 434 | .tag = .cmovcc, |
| 441 | .ops = .rr_cc, | 435 | .ops = .rr_cc, |
| 442 | .data = .{ .rr_cc = .{ | 436 | .data = .{ .rr_cc = .{ .r1 = reg1, .r2 = reg2, .cc = cc } }, |
| 443 | .r1 = reg1, | | |
| 444 | .r2 = reg2, | | |
| 445 | .cc = cc, | | |
| 446 | } }, | | |
| 447 | }); | 437 | }); |
| 448 | } | 438 | } |
| 449 | | 439 | |
| ... | @@ -455,15 +445,11 @@ fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condi | ... | @@ -455,15 +445,11 @@ fn asmCmovccRegisterMemory(self: *Self, reg: Register, m: Memory, cc: bits.Condi |
| 455 | .rip => .rm_rip_cc, | 445 | .rip => .rm_rip_cc, |
| 456 | else => unreachable, | 446 | else => unreachable, |
| 457 | }, | 447 | }, |
| 458 | .data = .{ .rx_cc = .{ | 448 | .data = .{ .rx_cc = .{ .r = reg, .cc = cc, .payload = switch (m) { |
| 459 | .r1 = reg, | 449 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), |
| 460 | .cc = cc, | 450 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), |
| 461 | .payload = switch (m) { | 451 | else => unreachable, |
| 462 | .sib => try self.addExtra(Mir.MemorySib.encode(m)), | 452 | } } }, |
| 463 | .rip => try self.addExtra(Mir.MemoryRip.encode(m)), | | |
| 464 | else => unreachable, | | |
| 465 | }, | | |
| 466 | } }, | | |
| 467 | }); | 453 | }); |
| 468 | } | 454 | } |
| 469 | | 455 | |
| ... | @@ -479,10 +465,7 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins | ... | @@ -479,10 +465,7 @@ fn asmJccReloc(self: *Self, target: Mir.Inst.Index, cc: bits.Condition) !Mir.Ins |
| 479 | return self.addInst(.{ | 465 | return self.addInst(.{ |
| 480 | .tag = .jcc, | 466 | .tag = .jcc, |
| 481 | .ops = .inst_cc, | 467 | .ops = .inst_cc, |
| 482 | .data = .{ .inst_cc = .{ | 468 | .data = .{ .inst_cc = .{ .inst = target, .cc = cc } }, |
| 483 | .inst = target, | | |
| 484 | .cc = cc, | | |
| 485 | } }, | | |
| 486 | }); | 469 | }); |
| 487 | } | 470 | } |
| 488 | | 471 | |
| ... | @@ -503,13 +486,15 @@ fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { | ... | @@ -503,13 +486,15 @@ fn asmRegister(self: *Self, tag: Mir.Inst.Tag, reg: Register) !void { |
| 503 | } | 486 | } |
| 504 | | 487 | |
| 505 | fn asmImmediate(self: *Self, tag: Mir.Inst.Tag, imm: Immediate) !void { | 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 | _ = try self.addInst(.{ | 489 | _ = try self.addInst(.{ |
| 508 | .tag = tag, | 490 | .tag = tag, |
| 509 | .ops = ops, | 491 | .ops = switch (imm) { |
| 510 | .data = .{ .imm = switch (imm) { | 492 | .signed => .i_s, |
| 511 | .signed => |x| @bitCast(u32, x), | 493 | .unsigned => .i_u, |
| 512 | .unsigned => |x| @intCast(u32, x), | 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,37 +503,43 @@ fn asmRegisterRegister(self: *Self, tag: Mir.Inst.Tag, reg1: Register, reg2: Reg |
| 518 | _ = try self.addInst(.{ | 503 | _ = try self.addInst(.{ |
| 519 | .tag = tag, | 504 | .tag = tag, |
| 520 | .ops = .rr, | 505 | .ops = .rr, |
| 521 | .data = .{ .rr = .{ | 506 | .data = .{ .rr = .{ .r1 = reg1, .r2 = reg2 } }, |
| 522 | .r1 = reg1, | | |
| 523 | .r2 = reg2, | | |
| 524 | } }, | | |
| 525 | }); | 507 | }); |
| 526 | } | 508 | } |
| 527 | | 509 | |
| 528 | fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { | 510 | fn asmRegisterImmediate(self: *Self, tag: Mir.Inst.Tag, reg: Register, imm: Immediate) !void { |
| 529 | const ops: Mir.Inst.Ops = switch (imm) { | 511 | const ops: Mir.Inst.Ops = switch (imm) { |
| 530 | .signed => .ri_s, | 512 | .signed => .ri_s, |
| 531 | .unsigned => |x| if (x <= math.maxInt(u32)) .ri_u else .ri64, | 513 | .unsigned => |u| if (math.cast(u32, u)) |_| .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, | | |
| 547 | }; | 514 | }; |
| 548 | _ = try self.addInst(.{ | 515 | _ = try self.addInst(.{ |
| 549 | .tag = tag, | 516 | .tag = tag, |
| 550 | .ops = ops, | 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,109 +550,133 @@ fn asmRegisterRegisterImmediate( |
| 559 | reg2: Register, | 550 | reg2: Register, |
| 560 | imm: Immediate, | 551 | imm: Immediate, |
| 561 | ) !void { | 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 | _ = try self.addInst(.{ | 553 | _ = try self.addInst(.{ |
| 580 | .tag = tag, | 554 | .tag = tag, |
| 581 | .ops = ops, | 555 | .ops = switch (imm) { |
| 582 | .data = data, | 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 | fn asmMemory(self: *Self, tag: Mir.Inst.Tag, m: Memory) !void { | 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 | _ = try self.addInst(.{ | 567 | _ = try self.addInst(.{ |
| 598 | .tag = tag, | 568 | .tag = tag, |
| 599 | .ops = ops, | 569 | .ops = switch (m) { |
| 600 | .data = data, | 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 { | 582 | fn asmRegisterMemory(self: *Self, tag: Mir.Inst.Tag, reg: Register, m: Memory) !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 | }; | | |
| 621 | _ = try self.addInst(.{ | 583 | _ = try self.addInst(.{ |
| 622 | .tag = tag, | 584 | .tag = tag, |
| 623 | .ops = ops, | 585 | .ops = switch (m) { |
| 624 | .data = data, | 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 { | 598 | fn asmMemoryRegister(self: *Self, tag: Mir.Inst.Tag, m: Memory, reg: Register) !void { |
| 629 | const ops: Mir.Inst.Ops = switch (m) { | 599 | _ = try self.addInst(.{ |
| 630 | .sib => .rm_sib, | 600 | .tag = tag, |
| 631 | .rip => .rm_rip, | 601 | .ops = switch (m) { |
| 632 | else => unreachable, | 602 | .sib => .mr_sib, |
| 633 | }; | 603 | .rip => .mr_rip, |
| 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)), | | |
| 638 | else => unreachable, | 604 | else => unreachable, |
| 639 | } }, | 605 | }, |
| 640 | }; | 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 | _ = try self.addInst(.{ | 615 | _ = try self.addInst(.{ |
| 642 | .tag = tag, | 616 | .tag = tag, |
| 643 | .ops = ops, | 617 | .ops = switch (m) { |
| 644 | .data = data, | 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 { | 639 | fn asmMemoryRegisterRegister( |
| 649 | const ops: Mir.Inst.Ops = switch (m) { | 640 | self: *Self, |
| 650 | .sib => .mr_sib, | 641 | tag: Mir.Inst.Tag, |
| 651 | .rip => .mr_rip, | 642 | m: Memory, |
| 652 | else => unreachable, | 643 | reg1: Register, |
| 653 | }; | 644 | reg2: Register, |
| 654 | const data: Mir.Inst.Data = .{ | 645 | ) !void { |
| 655 | .rx = .{ .r1 = reg, .payload = switch (ops) { | 646 | _ = try self.addInst(.{ |
| 656 | .mr_sib => try self.addExtra(Mir.MemorySib.encode(m)), | 647 | .tag = tag, |
| 657 | .mr_rip => try self.addExtra(Mir.MemoryRip.encode(m)), | 648 | .ops = switch (m) { |
| | 649 | .sib => .mrr_sib, |
| | 650 | .rip => .mrr_rip, |
| 658 | else => unreachable, | 651 | else => unreachable, |
| 659 | } }, | 652 | }, |
| 660 | }; | 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 | _ = try self.addInst(.{ | 668 | _ = try self.addInst(.{ |
| 662 | .tag = tag, | 669 | .tag = tag, |
| 663 | .ops = ops, | 670 | .ops = switch (m) { |
| 664 | .data = data, | 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,18 +783,12 @@ fn gen(self: *Self) InnerError!void { |
| 768 | self.mir_instructions.set(backpatch_stack_sub, .{ | 783 | self.mir_instructions.set(backpatch_stack_sub, .{ |
| 769 | .tag = .sub, | 784 | .tag = .sub, |
| 770 | .ops = .ri_u, | 785 | .ops = .ri_u, |
| 771 | .data = .{ .ri = .{ | 786 | .data = .{ .ri = .{ .r = .rsp, .i = aligned_stack_end } }, |
| 772 | .r1 = .rsp, | | |
| 773 | .imm = aligned_stack_end, | | |
| 774 | } }, | | |
| 775 | }); | 787 | }); |
| 776 | self.mir_instructions.set(backpatch_stack_add, .{ | 788 | self.mir_instructions.set(backpatch_stack_add, .{ |
| 777 | .tag = .add, | 789 | .tag = .add, |
| 778 | .ops = .ri_u, | 790 | .ops = .ri_u, |
| 779 | .data = .{ .ri = .{ | 791 | .data = .{ .ri = .{ .r = .rsp, .i = aligned_stack_end } }, |
| 780 | .r1 = .rsp, | | |
| 781 | .imm = aligned_stack_end, | | |
| 782 | } }, | | |
| 783 | }); | 792 | }); |
| 784 | | 793 | |
| 785 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ | 794 | const save_reg_list = try self.addExtra(Mir.SaveRegisterList{ |
| ... | @@ -1392,38 +1401,80 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1392,38 +1401,80 @@ fn airFpext(self: *Self, inst: Air.Inst.Index) !void { |
| 1392 | | 1401 | |
| 1393 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { | 1402 | fn airIntCast(self: *Self, inst: Air.Inst.Index) !void { |
| 1394 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 1403 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; |
| 1395 | if (self.liveness.isUnused(inst)) | 1404 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1396 | return self.finishAir(inst, .dead, .{ ty_op.operand, .none, .none }); | 1405 | const src_ty = self.air.typeOf(ty_op.operand); |
| 1397 | | 1406 | const src_int_info = src_ty.intInfo(self.target.*); |
| 1398 | const operand_ty = self.air.typeOf(ty_op.operand); | 1407 | const src_abi_size = @intCast(u32, src_ty.abiSize(self.target.*)); |
| 1399 | const operand = try self.resolveInst(ty_op.operand); | 1408 | const src_mcv = try self.resolveInst(ty_op.operand); |
| 1400 | const info_a = operand_ty.intInfo(self.target.*); | 1409 | const src_lock = switch (src_mcv) { |
| 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) { | | |
| 1415 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 1410 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 1416 | else => null, | 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); | 1415 | const dst_ty = self.air.typeOfIndex(inst); |
| 1421 | try self.genSetReg(dest_ty, reg, .{ .immediate = 0 }); | 1416 | const dst_int_info = dst_ty.intInfo(self.target.*); |
| 1422 | try self.genSetReg(operand_ty, reg, operand); | 1417 | const dst_abi_size = @intCast(u32, dst_ty.abiSize(self.target.*)); |
| 1423 | break :blk MCValue{ .register = reg }; | 1418 | const dst_mcv = if (dst_abi_size <= src_abi_size and |
| 1424 | }; | 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 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { | 1480 | fn airTrunc(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -1550,28 +1601,161 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1550,28 +1601,161 @@ fn airMulDivBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1550 | | 1601 | |
| 1551 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { | 1602 | fn airAddSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1552 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1603 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1553 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1604 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1554 | .dead | 1605 | const ty = self.air.typeOf(bin_op.lhs); |
| 1555 | else | 1606 | |
| 1556 | return self.fail("TODO implement add_sat for {}", .{self.target.cpu.arch}); | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1652 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1558 | } | 1653 | } |
| 1559 | | 1654 | |
| 1560 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { | 1655 | fn airSubSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1561 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1656 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1562 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1657 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1563 | .dead | 1658 | const ty = self.air.typeOf(bin_op.lhs); |
| 1564 | else | 1659 | |
| 1565 | return self.fail("TODO implement sub_sat for {}", .{self.target.cpu.arch}); | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 1703 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); |
| 1567 | } | 1704 | } |
| 1568 | | 1705 | |
| 1569 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { | 1706 | fn airMulSat(self: *Self, inst: Air.Inst.Index) !void { |
| 1570 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 1707 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 1571 | const result: MCValue = if (self.liveness.isUnused(inst)) | 1708 | const result: MCValue = if (self.liveness.isUnused(inst)) .dead else result: { |
| 1572 | .dead | 1709 | const ty = self.air.typeOf(bin_op.lhs); |
| 1573 | else | 1710 | |
| 1574 | return self.fail("TODO implement mul_sat for {}", .{self.target.cpu.arch}); | 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 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,6 +1783,7 @@ fn airAddSubShlWithOverflow(self: *Self, inst: Air.Inst.Index) !void { |
| 1599 | .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), | 1783 | .add_with_overflow => try self.genBinOp(null, .add, bin_op.lhs, bin_op.rhs), |
| 1600 | .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), | 1784 | .sub_with_overflow => try self.genBinOp(null, .sub, bin_op.lhs, bin_op.rhs), |
| 1601 | .shl_with_overflow => blk: { | 1785 | .shl_with_overflow => blk: { |
| | 1786 | try self.register_manager.getReg(.rcx, null); |
| 1602 | const lhs = try self.resolveInst(bin_op.lhs); | 1787 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1603 | const rhs = try self.resolveInst(bin_op.rhs); | 1788 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1604 | const shift_ty = self.air.typeOf(bin_op.rhs); | 1789 | const shift_ty = self.air.typeOf(bin_op.rhs); |
| ... | @@ -1889,6 +2074,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -1889,6 +2074,7 @@ fn airShlShrBinOp(self: *Self, inst: Air.Inst.Index) !void { |
| 1889 | try self.spillRegisters(&.{.rcx}); | 2074 | try self.spillRegisters(&.{.rcx}); |
| 1890 | | 2075 | |
| 1891 | const tag = self.air.instructions.items(.tag)[inst]; | 2076 | const tag = self.air.instructions.items(.tag)[inst]; |
| | 2077 | try self.register_manager.getReg(.rcx, null); |
| 1892 | const lhs = try self.resolveInst(bin_op.lhs); | 2078 | const lhs = try self.resolveInst(bin_op.lhs); |
| 1893 | const rhs = try self.resolveInst(bin_op.rhs); | 2079 | const rhs = try self.resolveInst(bin_op.rhs); |
| 1894 | const lhs_ty = self.air.typeOf(bin_op.lhs); | 2080 | const lhs_ty = self.air.typeOf(bin_op.lhs); |
| ... | @@ -2018,7 +2204,7 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2018,7 +2204,7 @@ fn airUnwrapErrUnionErr(self: *Self, inst: Air.Inst.Index) !void { |
| 2018 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); | 2204 | const result = try self.copyToRegisterWithInstTracking(inst, err_union_ty, operand); |
| 2019 | if (err_off > 0) { | 2205 | if (err_off > 0) { |
| 2020 | const shift = @intCast(u6, err_off * 8); | 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 | } else { | 2208 | } else { |
| 2023 | try self.truncateRegister(Type.anyerror, result.register); | 2209 | try self.truncateRegister(Type.anyerror, result.register); |
| 2024 | } | 2210 | } |
| ... | @@ -2050,9 +2236,7 @@ fn genUnwrapErrorUnionPayloadMir( | ... | @@ -2050,9 +2236,7 @@ fn genUnwrapErrorUnionPayloadMir( |
| 2050 | const payload_ty = err_union_ty.errorUnionPayload(); | 2236 | const payload_ty = err_union_ty.errorUnionPayload(); |
| 2051 | | 2237 | |
| 2052 | const result: MCValue = result: { | 2238 | const result: MCValue = result: { |
| 2053 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) { | 2239 | if (!payload_ty.hasRuntimeBitsIgnoreComptime()) break :result .none; |
| 2054 | break :result MCValue.none; | | |
| 2055 | } | | |
| 2056 | | 2240 | |
| 2057 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); | 2241 | const payload_off = errUnionPayloadOffset(payload_ty, self.target.*); |
| 2058 | switch (err_union) { | 2242 | switch (err_union) { |
| ... | @@ -2065,17 +2249,17 @@ fn genUnwrapErrorUnionPayloadMir( | ... | @@ -2065,17 +2249,17 @@ fn genUnwrapErrorUnionPayloadMir( |
| 2065 | const eu_lock = self.register_manager.lockReg(reg); | 2249 | const eu_lock = self.register_manager.lockReg(reg); |
| 2066 | defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); | 2250 | defer if (eu_lock) |lock| self.register_manager.unlockReg(lock); |
| 2067 | | 2251 | |
| 2068 | const result_reg: Register = if (maybe_inst) |inst| | 2252 | const result_mcv: MCValue = if (maybe_inst) |inst| |
| 2069 | (try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union)).register | 2253 | try self.copyToRegisterWithInstTracking(inst, err_union_ty, err_union) |
| 2070 | else | 2254 | else |
| 2071 | try self.copyToTmpRegister(err_union_ty, err_union); | 2255 | .{ .register = try self.copyToTmpRegister(err_union_ty, err_union) }; |
| 2072 | if (payload_off > 0) { | 2256 | if (payload_off > 0) { |
| 2073 | const shift = @intCast(u6, payload_off * 8); | 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 | } else { | 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 | else => return self.fail("TODO implement genUnwrapErrorUnionPayloadMir for {}", .{err_union}), | 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,8 +2437,8 @@ fn airWrapOptional(self: *Self, inst: Air.Inst.Index) !void { |
| 2253 | | 2437 | |
| 2254 | .stack_offset => |off| try self.asmMemoryImmediate( | 2438 | .stack_offset => |off| try self.asmMemoryImmediate( |
| 2255 | .mov, | 2439 | .mov, |
| 2256 | Memory.sib(.byte, .{ .base = .rsp, .disp = pl_abi_size - off }), | 2440 | Memory.sib(.byte, .{ .base = .rbp, .disp = pl_abi_size - off }), |
| 2257 | Immediate.u(0), | 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,10 +2657,9 @@ fn genSliceElemPtr(self: *Self, lhs: Air.Inst.Ref, rhs: Air.Inst.Ref) !MCValue { |
| 2473 | } | 2657 | } |
| 2474 | | 2658 | |
| 2475 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { | 2659 | fn airSliceElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2476 | const is_volatile = false; // TODO | | |
| 2477 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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: { | 2661 | const slice_ty = self.air.typeOf(bin_op.lhs); |
| 2479 | 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 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; | 2663 | var buf: Type.SlicePtrFieldTypeBuffer = undefined; |
| 2481 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); | 2664 | const slice_ptr_field_type = slice_ty.slicePtrFieldType(&buf); |
| 2482 | const elem_ptr = try self.genSliceElemPtr(bin_op.lhs, bin_op.rhs); | 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,54 +2746,44 @@ fn airArrayElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2563 | } | 2746 | } |
| 2564 | | 2747 | |
| 2565 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { | 2748 | fn airPtrElemVal(self: *Self, inst: Air.Inst.Index) !void { |
| 2566 | const is_volatile = false; // TODO | | |
| 2567 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 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 | const ptr_ty = self.air.typeOf(bin_op.lhs); | 2750 | const ptr_ty = self.air.typeOf(bin_op.lhs); |
| 2577 | const ptr = try self.resolveInst(bin_op.lhs); | 2751 | const result = if (!ptr_ty.isVolatilePtr() and self.liveness.isUnused(inst)) .dead else result: { |
| 2578 | const ptr_lock: ?RegisterLock = switch (ptr) { | 2752 | // this is identical to the `airPtrElemPtr` codegen expect here an |
| 2579 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 2753 | // additional `mov` is needed at the end to get the actual value |
| 2580 | else => null, | 2754 | |
| 2581 | }; | 2755 | const elem_ty = ptr_ty.elemType2(); |
| 2582 | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); | 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(); | 2765 | const offset_reg = try self.elemOffset(index_ty, index_mcv, elem_abi_size); |
| 2585 | const elem_abi_size = @intCast(u32, elem_ty.abiSize(self.target.*)); | 2766 | const offset_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 2586 | const index_ty = self.air.typeOf(bin_op.rhs); | 2767 | defer self.register_manager.unlockReg(offset_lock); |
| 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); | | |
| 2593 | | 2768 | |
| 2594 | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); | 2769 | const ptr_mcv = try self.resolveInst(bin_op.lhs); |
| 2595 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); | 2770 | const elem_ptr_reg = if (ptr_mcv.isRegister() and self.liveness.operandDies(inst, 0)) |
| 2596 | defer self.register_manager.unlockReg(offset_reg_lock); | 2771 | ptr_mcv.register |
| 2597 | | 2772 | else |
| 2598 | const dst_mcv = try self.copyToRegisterWithInstTracking(inst, ptr_ty, ptr); | 2773 | try self.copyToTmpRegister(ptr_ty, ptr_mcv); |
| 2599 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); | 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: { | 2778 | const dst_mcv = try self.allocRegOrMem(inst, true); |
| 2602 | if (elem_abi_size > 8) { | 2779 | const dst_lock = switch (dst_mcv) { |
| 2603 | return self.fail("TODO copy value with size {} from pointer", .{elem_abi_size}); | 2780 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2604 | } else { | 2781 | else => null, |
| 2605 | try self.asmRegisterMemory( | 2782 | }; |
| 2606 | .mov, | 2783 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 2607 | registerAlias(dst_mcv.register, elem_abi_size), | 2784 | try self.load(dst_mcv, .{ .register = elem_ptr_reg }, ptr_ty); |
| 2608 | Memory.sib(Memory.PtrSize.fromSize(elem_abi_size), .{ .base = dst_mcv.register }), | 2785 | break :result dst_mcv; |
| 2609 | ); | | |
| 2610 | break :result .{ .register = registerAlias(dst_mcv.register, @intCast(u32, elem_abi_size)) }; | | |
| 2611 | } | | |
| 2612 | }; | 2786 | }; |
| 2613 | | | |
| 2614 | return self.finishAir(inst, result, .{ bin_op.lhs, bin_op.rhs, .none }); | 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,36 +2791,34 @@ fn airPtrElemPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 2618 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 2791 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 2619 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; | 2792 | const extra = self.air.extraData(Air.Bin, ty_pl.payload).data; |
| 2620 | | 2793 | |
| 2621 | if (self.liveness.isUnused(inst)) { | 2794 | const result = if (self.liveness.isUnused(inst)) .dead else result: { |
| 2622 | return self.finishAir(inst, .dead, .{ extra.lhs, extra.rhs, .none }); | 2795 | const ptr_ty = self.air.typeOf(extra.lhs); |
| 2623 | } | 2796 | const ptr = try self.resolveInst(extra.lhs); |
| 2624 | | 2797 | const ptr_lock: ?RegisterLock = switch (ptr) { |
| 2625 | const ptr_ty = self.air.typeOf(extra.lhs); | 2798 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), |
| 2626 | const ptr = try self.resolveInst(extra.lhs); | 2799 | else => null, |
| 2627 | const ptr_lock: ?RegisterLock = switch (ptr) { | 2800 | }; |
| 2628 | .register => |reg| self.register_manager.lockRegAssumeUnused(reg), | 2801 | defer if (ptr_lock) |lock| self.register_manager.unlockReg(lock); |
| 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); | | |
| 2642 | | 2802 | |
| 2643 | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); | 2803 | const elem_ty = ptr_ty.elemType2(); |
| 2644 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); | 2804 | const elem_abi_size = elem_ty.abiSize(self.target.*); |
| 2645 | defer self.register_manager.unlockReg(offset_reg_lock); | 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); | 2813 | const offset_reg = try self.elemOffset(index_ty, index, elem_abi_size); |
| 2648 | try self.genBinOpMir(.add, ptr_ty, dst_mcv, .{ .register = offset_reg }); | 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 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | 2824 | fn airSetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| ... | @@ -2728,7 +2899,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -2728,7 +2899,7 @@ fn airGetUnionTag(self: *Self, inst: Air.Inst.Index) !void { |
| 2728 | else | 2899 | else |
| 2729 | 0; | 2900 | 0; |
| 2730 | const result = try self.copyToRegisterWithInstTracking(inst, union_ty, operand); | 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 | break :blk MCValue{ | 2903 | break :blk MCValue{ |
| 2733 | .register = registerAlias(result.register, @intCast(u32, layout.tag_size)), | 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,12 +2991,11 @@ fn airCtz(self: *Self, inst: Air.Inst.Index) !void { |
| 2820 | const extra_bits = self.regExtraBits(src_ty); | 2991 | const extra_bits = self.regExtraBits(src_ty); |
| 2821 | const masked_mcv = if (extra_bits > 0) masked: { | 2992 | const masked_mcv = if (extra_bits > 0) masked: { |
| 2822 | const mask_mcv = MCValue{ | 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 | const tmp_mcv = tmp: { | 2997 | const tmp_mcv = tmp: { |
| 2826 | if (src_mcv.isImmediate() or self.reuseOperand(inst, ty_op.operand, 0, src_mcv)) { | 2998 | if (src_mcv.isImmediate() or self.liveness.operandDies(inst, 0)) break :tmp src_mcv; |
| 2827 | break :tmp src_mcv; | | |
| 2828 | } | | |
| 2829 | try self.genSetReg(src_ty, dst_reg, src_mcv); | 2999 | try self.genSetReg(src_ty, dst_reg, src_mcv); |
| 2830 | break :tmp dst_mcv; | 3000 | break :tmp dst_mcv; |
| 2831 | }; | 3001 | }; |
| ... | @@ -3526,34 +3696,37 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3526,34 +3696,37 @@ fn airStore(self: *Self, inst: Air.Inst.Index) !void { |
| 3526 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { | 3696 | fn airStructFieldPtr(self: *Self, inst: Air.Inst.Index) !void { |
| 3527 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; | 3697 | const ty_pl = self.air.instructions.items(.data)[inst].ty_pl; |
| 3528 | const extra = self.air.extraData(Air.StructField, ty_pl.payload).data; | 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 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); | 3700 | return self.finishAir(inst, result, .{ extra.struct_operand, .none, .none }); |
| 3531 | } | 3701 | } |
| 3532 | | 3702 | |
| 3533 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { | 3703 | fn airStructFieldPtrIndex(self: *Self, inst: Air.Inst.Index, index: u8) !void { |
| 3534 | const ty_op = self.air.instructions.items(.data)[inst].ty_op; | 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 | return self.finishAir(inst, result, .{ ty_op.operand, .none, .none }); | 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 | if (self.liveness.isUnused(inst)) { | 3710 | if (self.liveness.isUnused(inst)) { |
| 3541 | return MCValue.dead; | 3711 | return MCValue.dead; |
| 3542 | } | 3712 | } |
| 3543 | | 3713 | |
| 3544 | const mcv = try self.resolveInst(operand); | 3714 | const mcv = try self.resolveInst(operand); |
| 3545 | const ptr_ty = self.air.typeOf(operand); | 3715 | const ptr_ty = self.air.typeOf(operand); |
| 3546 | const struct_ty = ptr_ty.childType(); | 3716 | const container_ty = ptr_ty.childType(); |
| 3547 | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { | 3717 | const field_offset = switch (container_ty.containerLayout()) { |
| 3548 | return self.fail("TODO structFieldPtr implement packed structs", .{}); | 3718 | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*)), |
| 3549 | } | 3719 | .Packed => if (container_ty.zigTypeTag() == .Struct and ptr_ty.ptrInfo().data.host_size == 0) |
| 3550 | const struct_field_offset = @intCast(u32, struct_ty.structFieldOffset(index, self.target.*)); | 3720 | container_ty.packedStructFieldByteOffset(index, self.target.*) |
| | 3721 | else |
| | 3722 | 0, |
| | 3723 | }; |
| 3551 | | 3724 | |
| 3552 | const dst_mcv: MCValue = result: { | 3725 | const dst_mcv: MCValue = result: { |
| 3553 | switch (mcv) { | 3726 | switch (mcv) { |
| 3554 | .stack_offset => { | 3727 | .stack_offset => { |
| 3555 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ | 3728 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ |
| 3556 | .immediate = struct_field_offset, | 3729 | .immediate = field_offset, |
| 3557 | }); | 3730 | }); |
| 3558 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); | 3731 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 3559 | defer self.register_manager.unlockReg(offset_reg_lock); | 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,7 +3736,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3563 | break :result dst_mcv; | 3736 | break :result dst_mcv; |
| 3564 | }, | 3737 | }, |
| 3565 | .ptr_stack_offset => |off| { | 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 | break :result MCValue{ .ptr_stack_offset = ptr_stack_offset }; | 3740 | break :result MCValue{ .ptr_stack_offset = ptr_stack_offset }; |
| 3568 | }, | 3741 | }, |
| 3569 | .register => |reg| { | 3742 | .register => |reg| { |
| ... | @@ -3571,7 +3744,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde | ... | @@ -3571,7 +3744,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3571 | defer self.register_manager.unlockReg(reg_lock); | 3744 | defer self.register_manager.unlockReg(reg_lock); |
| 3572 | | 3745 | |
| 3573 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ | 3746 | const offset_reg = try self.copyToTmpRegister(ptr_ty, .{ |
| 3574 | .immediate = struct_field_offset, | 3747 | .immediate = field_offset, |
| 3575 | }); | 3748 | }); |
| 3576 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); | 3749 | const offset_reg_lock = self.register_manager.lockRegAssumeUnused(offset_reg); |
| 3577 | defer self.register_manager.unlockReg(offset_reg_lock); | 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,7 +3765,7 @@ fn structFieldPtr(self: *Self, inst: Air.Inst.Index, operand: Air.Inst.Ref, inde |
| 3592 | try self.genBinOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); | 3765 | try self.genBinOpMir(.add, ptr_ty, .{ .register = result_reg }, .{ .register = offset_reg }); |
| 3593 | break :result MCValue{ .register = result_reg }; | 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 | return dst_mcv; | 3771 | return dst_mcv; |
| ... | @@ -3609,18 +3782,22 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3609,18 +3782,22 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3609 | } | 3782 | } |
| 3610 | | 3783 | |
| 3611 | const mcv = try self.resolveInst(operand); | 3784 | const mcv = try self.resolveInst(operand); |
| 3612 | const struct_ty = self.air.typeOf(operand); | 3785 | const container_ty = self.air.typeOf(operand); |
| 3613 | if (struct_ty.zigTypeTag() == .Struct and struct_ty.containerLayout() == .Packed) { | 3786 | const field_ty = container_ty.structFieldType(index); |
| 3614 | return self.fail("TODO airStructFieldVal implement packed structs", .{}); | 3787 | const field_bit_offset = switch (container_ty.containerLayout()) { |
| 3615 | } | 3788 | .Auto, .Extern => @intCast(u32, container_ty.structFieldOffset(index, self.target.*) * 8), |
| 3616 | const struct_field_offset = struct_ty.structFieldOffset(index, self.target.*); | 3789 | .Packed => if (container_ty.castTag(.@"struct")) |struct_obj| |
| 3617 | const struct_field_ty = struct_ty.structFieldType(index); | 3790 | struct_obj.data.packedFieldBitOffset(self.target.*, index) |
| | 3791 | else |
| | 3792 | 0, |
| | 3793 | }; |
| 3618 | | 3794 | |
| 3619 | const result: MCValue = result: { | 3795 | const result: MCValue = result: { |
| 3620 | switch (mcv) { | 3796 | switch (mcv) { |
| 3621 | .stack_offset => |off| { | 3797 | .stack_offset => |off| { |
| 3622 | const stack_offset = off - @intCast(i32, struct_field_offset); | 3798 | const byte_offset = std.math.divExact(u32, field_bit_offset, 8) catch |
| 3623 | break :result MCValue{ .stack_offset = stack_offset }; | 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 | .register => |reg| { | 3802 | .register => |reg| { |
| 3626 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); | 3803 | const reg_lock = self.register_manager.lockRegAssumeUnused(reg); |
| ... | @@ -3643,27 +3820,23 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -3643,27 +3820,23 @@ fn airStructFieldVal(self: *Self, inst: Air.Inst.Index) !void { |
| 3643 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); | 3820 | defer if (dst_mcv_lock) |lock| self.register_manager.unlockReg(lock); |
| 3644 | | 3821 | |
| 3645 | // Shift by struct_field_offset. | 3822 | // Shift by struct_field_offset. |
| 3646 | const shift = @intCast(u8, struct_field_offset * 8); | 3823 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv, .{ .immediate = field_bit_offset }); |
| 3647 | try self.genShiftBinOpMir(.shr, Type.usize, dst_mcv.register, .{ .immediate = shift }); | | |
| 3648 | | 3824 | |
| 3649 | // Mask with reg.bitSize() - struct_field_size | 3825 | // Mask to field_bit_size bits |
| 3650 | const max_reg_bit_width = Register.rax.bitSize(); | 3826 | const field_bit_size = field_ty.bitSize(self.target.*); |
| 3651 | const mask_shift = @intCast(u6, (max_reg_bit_width - struct_field_ty.bitSize(self.target.*))); | 3827 | const mask = ~@as(u64, 0) >> @intCast(u6, 64 - field_bit_size); |
| 3652 | const mask = (~@as(u64, 0)) >> mask_shift; | | |
| 3653 | | 3828 | |
| 3654 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); | 3829 | const tmp_reg = try self.copyToTmpRegister(Type.usize, .{ .immediate = mask }); |
| 3655 | try self.genBinOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); | 3830 | try self.genBinOpMir(.@"and", Type.usize, dst_mcv, .{ .register = tmp_reg }); |
| 3656 | | 3831 | |
| 3657 | const signedness: std.builtin.Signedness = blk: { | 3832 | const signedness = |
| 3658 | if (struct_field_ty.zigTypeTag() != .Int) break :blk .unsigned; | 3833 | if (field_ty.isAbiInt()) field_ty.intInfo(self.target.*).signedness else .unsigned; |
| 3659 | break :blk struct_field_ty.intInfo(self.target.*).signedness; | 3834 | const field_byte_size = @intCast(u32, field_ty.abiSize(self.target.*)); |
| 3660 | }; | 3835 | if (signedness == .signed and field_byte_size < 8) { |
| 3661 | const field_size = @intCast(u32, struct_field_ty.abiSize(self.target.*)); | | |
| 3662 | if (signedness == .signed and field_size < 8) { | | |
| 3663 | try self.asmRegisterRegister( | 3836 | try self.asmRegisterRegister( |
| 3664 | .movsx, | 3837 | .movsx, |
| 3665 | dst_mcv.register, | 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,10 +3880,10 @@ fn genUnOp(self: *Self, maybe_inst: ?Air.Inst.Index, tag: Air.Inst.Tag, src_air: |
| 3707 | const src_ty = self.air.typeOf(src_air); | 3880 | const src_ty = self.air.typeOf(src_air); |
| 3708 | const src_mcv = try self.resolveInst(src_air); | 3881 | const src_mcv = try self.resolveInst(src_air); |
| 3709 | if (src_ty.zigTypeTag() == .Vector) { | 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 | if (src_ty.abiSize(self.target.*) > 8) { | 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 | switch (src_mcv) { | 3889 | switch (src_mcv) { |
| ... | @@ -3805,29 +3978,186 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue | ... | @@ -3805,29 +3978,186 @@ fn genUnOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValue |
| 3805 | } | 3978 | } |
| 3806 | | 3979 | |
| 3807 | /// Clobbers .rcx for non-immediate shift value. | 3980 | /// Clobbers .rcx for non-immediate shift value. |
| 3808 | fn genShiftBinOpMir(self: *Self, tag: Mir.Inst.Tag, ty: Type, reg: Register, shift: MCValue) !void { | 3981 | fn genShiftBinOpMir( |
| 3809 | switch (tag) { | 3982 | self: *Self, |
| 3810 | .sal, .sar, .shl, .shr => {}, | 3983 | tag: Mir.Inst.Tag, |
| 3811 | else => unreachable, | 3984 | ty: Type, |
| 3812 | } | 3985 | lhs_mcv: MCValue, |
| 3813 | | 3986 | shift_mcv: MCValue, |
| 3814 | const abi_size = @intCast(u32, ty.abiSize(self.target.*)); | 3987 | ) !void { |
| 3815 | blk: { | 3988 | const rhs_mcv: MCValue = rhs: { |
| 3816 | switch (shift) { | 3989 | switch (shift_mcv) { |
| 3817 | .immediate => |imm| switch (imm) { | 3990 | .immediate => |imm| switch (imm) { |
| 3818 | 0 => return, | 3991 | 0 => return, |
| 3819 | else => return self.asmRegisterImmediate(tag, registerAlias(reg, abi_size), Immediate.u(imm)), | 3992 | else => break :rhs shift_mcv, |
| 3820 | }, | | |
| 3821 | .register => |shift_reg| { | | |
| 3822 | if (shift_reg == .rcx) break :blk; | | |
| 3823 | }, | 3993 | }, |
| | 3994 | .register => |shift_reg| if (shift_reg == .rcx) break :rhs shift_mcv, |
| 3824 | else => {}, | 3995 | else => {}, |
| 3825 | } | 3996 | } |
| 3826 | self.register_manager.getRegAssumeFree(.rcx, null); | 3997 | self.register_manager.getRegAssumeFree(.rcx, null); |
| 3827 | try self.genSetReg(Type.u8, .rcx, shift); | 3998 | try self.genSetReg(Type.u8, .rcx, shift_mcv); |
| 3828 | } | 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 | /// Result is always a register. | 4163 | /// Result is always a register. |
| ... | @@ -3837,68 +4167,61 @@ fn genShiftBinOp( | ... | @@ -3837,68 +4167,61 @@ fn genShiftBinOp( |
| 3837 | self: *Self, | 4167 | self: *Self, |
| 3838 | tag: Air.Inst.Tag, | 4168 | tag: Air.Inst.Tag, |
| 3839 | maybe_inst: ?Air.Inst.Index, | 4169 | maybe_inst: ?Air.Inst.Index, |
| 3840 | lhs: MCValue, | 4170 | lhs_mcv: MCValue, |
| 3841 | rhs: MCValue, | 4171 | rhs_mcv: MCValue, |
| 3842 | lhs_ty: Type, | 4172 | lhs_ty: Type, |
| 3843 | rhs_ty: Type, | 4173 | rhs_ty: Type, |
| 3844 | ) !MCValue { | 4174 | ) !MCValue { |
| 3845 | if (lhs_ty.zigTypeTag() == .Vector or lhs_ty.zigTypeTag() == .Float) { | 4175 | if (lhs_ty.zigTypeTag() == .Vector) { |
| 3846 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); | 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 | return self.fail("TODO implement genShiftBinOp for {}", .{lhs_ty.fmtDebug()}); | 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 | .register => |reg| self.register_manager.lockReg(reg), | 4191 | .register => |reg| self.register_manager.lockReg(reg), |
| 3856 | else => null, | 4192 | else => null, |
| 3857 | }; | 4193 | }; |
| 3858 | defer if (lhs_lock) |lock| self.register_manager.unlockReg(lock); | 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 | .register => |reg| self.register_manager.lockReg(reg), | 4197 | .register => |reg| self.register_manager.lockReg(reg), |
| 3862 | else => null, | 4198 | else => null, |
| 3863 | }; | 4199 | }; |
| 3864 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); | 4200 | defer if (rhs_lock) |lock| self.register_manager.unlockReg(lock); |
| 3865 | | 4201 | |
| 3866 | self.register_manager.getRegAssumeFree(.rcx, null); | 4202 | const dst_mcv: MCValue = dst: { |
| 3867 | const rcx_lock = self.register_manager.lockRegAssumeUnused(.rcx); | | |
| 3868 | defer self.register_manager.unlockReg(rcx_lock); | | |
| 3869 | | | |
| 3870 | const dst: MCValue = blk: { | | |
| 3871 | if (maybe_inst) |inst| { | 4203 | if (maybe_inst) |inst| { |
| 3872 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; | 4204 | const bin_op = self.air.instructions.items(.data)[inst].bin_op; |
| 3873 | // TODO dst can also be a memory location | 4205 | if (self.reuseOperand(inst, bin_op.lhs, 0, lhs_mcv)) break :dst lhs_mcv; |
| 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); | | |
| 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 | const signedness = lhs_ty.intInfo(self.target.*).signedness; | 4212 | const signedness = lhs_ty.intInfo(self.target.*).signedness; |
| 3883 | switch (tag) { | 4213 | try self.genShiftBinOpMir(switch (tag) { |
| 3884 | .shl => try self.genShiftBinOpMir(switch (signedness) { | 4214 | .shl, .shl_exact => switch (signedness) { |
| 3885 | .signed => .sal, | 4215 | .signed => .sal, |
| 3886 | .unsigned => .shl, | 4216 | .unsigned => .shl, |
| 3887 | }, lhs_ty, dst.register, rhs), | 4217 | }, |
| 3888 | | 4218 | .shr, .shr_exact => switch (signedness) { |
| 3889 | .shl_exact => try self.genShiftBinOpMir(.shl, lhs_ty, dst.register, rhs), | | |
| 3890 | | | |
| 3891 | .shr, | | |
| 3892 | .shr_exact, | | |
| 3893 | => try self.genShiftBinOpMir(switch (signedness) { | | |
| 3894 | .signed => .sar, | 4219 | .signed => .sar, |
| 3895 | .unsigned => .shr, | 4220 | .unsigned => .shr, |
| 3896 | }, lhs_ty, dst.register, rhs), | 4221 | }, |
| 3897 | | | |
| 3898 | else => unreachable, | 4222 | else => unreachable, |
| 3899 | } | 4223 | }, lhs_ty, dst_mcv, rhs_mcv); |
| 3900 | | 4224 | return dst_mcv; |
| 3901 | return dst; | | |
| 3902 | } | 4225 | } |
| 3903 | | 4226 | |
| 3904 | /// Result is always a register. | 4227 | /// Result is always a register. |
| ... | @@ -3915,7 +4238,8 @@ fn genMulDivBinOp( | ... | @@ -3915,7 +4238,8 @@ fn genMulDivBinOp( |
| 3915 | if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { | 4238 | if (ty.zigTypeTag() == .Vector or ty.zigTypeTag() == .Float) { |
| 3916 | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); | 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 | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); | 4243 | return self.fail("TODO implement genMulDivBinOp for {}", .{ty.fmtDebug()}); |
| 3920 | } | 4244 | } |
| 3921 | if (tag == .div_float) { | 4245 | if (tag == .div_float) { |
| ... | @@ -3925,10 +4249,8 @@ fn genMulDivBinOp( | ... | @@ -3925,10 +4249,8 @@ fn genMulDivBinOp( |
| 3925 | assert(self.register_manager.isRegFree(.rax)); | 4249 | assert(self.register_manager.isRegFree(.rax)); |
| 3926 | assert(self.register_manager.isRegFree(.rdx)); | 4250 | assert(self.register_manager.isRegFree(.rdx)); |
| 3927 | | 4251 | |
| 3928 | const reg_locks = self.register_manager.lockRegsAssumeUnused(2, .{ .rax, .rdx }); | 4252 | const reg_locks = self.register_manager.lockRegs(2, .{ .rax, .rdx }); |
| 3929 | defer for (reg_locks) |reg| { | 4253 | defer for (reg_locks) |reg_lock| if (reg_lock) |lock| self.register_manager.unlockReg(lock); |
| 3930 | self.register_manager.unlockReg(reg); | | |
| 3931 | }; | | |
| 3932 | | 4254 | |
| 3933 | const int_info = ty.intInfo(self.target.*); | 4255 | const int_info = ty.intInfo(self.target.*); |
| 3934 | const signedness = int_info.signedness; | 4256 | const signedness = int_info.signedness; |
| ... | @@ -3953,35 +4275,24 @@ fn genMulDivBinOp( | ... | @@ -3953,35 +4275,24 @@ fn genMulDivBinOp( |
| 3953 | | 4275 | |
| 3954 | const mir_tag: Mir.Inst.Tag = switch (signedness) { | 4276 | const mir_tag: Mir.Inst.Tag = switch (signedness) { |
| 3955 | .signed => switch (tag) { | 4277 | .signed => switch (tag) { |
| 3956 | .mul, .mulwrap => Mir.Inst.Tag.imul, | 4278 | .mul, .mulwrap => .imul, |
| 3957 | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.idiv, | 4279 | .div_trunc, .div_exact, .rem => .idiv, |
| 3958 | else => unreachable, | 4280 | else => unreachable, |
| 3959 | }, | 4281 | }, |
| 3960 | .unsigned => switch (tag) { | 4282 | .unsigned => switch (tag) { |
| 3961 | .mul, .mulwrap => Mir.Inst.Tag.mul, | 4283 | .mul, .mulwrap => .mul, |
| 3962 | .div_trunc, .div_exact, .rem => Mir.Inst.Tag.div, | 4284 | .div_trunc, .div_exact, .rem => .div, |
| 3963 | else => unreachable, | 4285 | else => unreachable, |
| 3964 | }, | 4286 | }, |
| 3965 | }; | 4287 | }; |
| 3966 | | 4288 | |
| 3967 | try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); | 4289 | try self.genIntMulDivOpMir(mir_tag, ty, .signed, lhs, rhs); |
| 3968 | | 4290 | |
| 3969 | switch (signedness) { | 4291 | return .{ .register = registerAlias(switch (tag) { |
| 3970 | .signed => switch (tag) { | 4292 | .mul, .mulwrap, .div_trunc, .div_exact => .rax, |
| 3971 | .mul, .mulwrap, .div_trunc, .div_exact => return MCValue{ .register = .rax }, | 4293 | .rem => .rdx, |
| 3972 | .rem => return MCValue{ .register = .rdx }, | 4294 | else => unreachable, |
| 3973 | else => unreachable, | 4295 | }, abi_size) }; |
| 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 | } | | |
| 3985 | }, | 4296 | }, |
| 3986 | | 4297 | |
| 3987 | .mod => { | 4298 | .mod => { |
| ... | @@ -3998,14 +4309,14 @@ fn genMulDivBinOp( | ... | @@ -3998,14 +4309,14 @@ fn genMulDivBinOp( |
| 3998 | const result: MCValue = if (maybe_inst) |inst| | 4309 | const result: MCValue = if (maybe_inst) |inst| |
| 3999 | try self.copyToRegisterWithInstTracking(inst, ty, lhs) | 4310 | try self.copyToRegisterWithInstTracking(inst, ty, lhs) |
| 4000 | else | 4311 | else |
| 4001 | MCValue{ .register = try self.copyToTmpRegister(ty, lhs) }; | 4312 | .{ .register = try self.copyToTmpRegister(ty, lhs) }; |
| 4002 | try self.genBinOpMir(.sub, ty, result, div_floor); | 4313 | try self.genBinOpMir(.sub, ty, result, div_floor); |
| 4003 | | 4314 | |
| 4004 | return result; | 4315 | return result; |
| 4005 | }, | 4316 | }, |
| 4006 | .unsigned => { | 4317 | .unsigned => { |
| 4007 | try self.genIntMulDivOpMir(.div, ty, .unsigned, lhs, rhs); | 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,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 | .memory, | 4714 | .memory, |
| ... | @@ -4478,28 +4789,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu | ... | @@ -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 | .memory, | 4795 | .memory, |
| 4485 | .stack_offset, | 4796 | .stack_offset, |
| 4486 | .ptr_stack_offset, | 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 | .linker_load => { | 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 | .eflags => { | 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 | .memory => { | 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 | .linker_load => { | 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,7 +5748,7 @@ fn isErr(self: *Self, maybe_inst: ?Air.Inst.Index, ty: Type, operand: MCValue) ! |
| 5437 | const tmp_reg = try self.copyToTmpRegister(ty, operand); | 5748 | const tmp_reg = try self.copyToTmpRegister(ty, operand); |
| 5438 | if (err_off > 0) { | 5749 | if (err_off > 0) { |
| 5439 | const shift = @intCast(u6, err_off * 8); | 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 | } else { | 5752 | } else { |
| 5442 | try self.truncateRegister(Type.anyerror, tmp_reg); | 5753 | try self.truncateRegister(Type.anyerror, tmp_reg); |
| 5443 | } | 5754 | } |
| ... | @@ -5945,13 +6256,23 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { | ... | @@ -5945,13 +6256,23 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5945 | const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); | 6256 | const inputs = @ptrCast([]const Air.Inst.Ref, self.air.extra[extra_i..][0..extra.data.inputs_len]); |
| 5946 | extra_i += inputs.len; | 6257 | extra_i += inputs.len; |
| 5947 | | 6258 | |
| 5948 | const dead = !is_volatile and self.liveness.isUnused(inst); | 6259 | var result: MCValue = .none; |
| 5949 | const result: MCValue = if (dead) .dead else result: { | 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 | if (outputs.len > 1) { | 6271 | if (outputs.len > 1) { |
| 5951 | return self.fail("TODO implement codegen for asm with more than 1 output", .{}); | 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 | if (output != .none) { | 6276 | if (output != .none) { |
| 5956 | return self.fail("TODO implement codegen for non-expr asm", .{}); | 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,8 +6283,21 @@ fn airAsm(self: *Self, inst: Air.Inst.Index) !void { |
| 5962 | // for the string, we still use the next u32 for the null terminator. | 6283 | // for the string, we still use the next u32 for the null terminator. |
| 5963 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; | 6284 | extra_i += (constraint.len + name.len + (2 + 3)) / 4; |
| 5964 | | 6285 | |
| 5965 | break constraint; | 6286 | const mcv: MCValue = if (mem.eql(u8, constraint, "=r")) |
| 5966 | } else null; | 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 | for (inputs) |input| { | 6302 | for (inputs) |input| { |
| 5969 | const input_bytes = std.mem.sliceAsBytes(self.air.extra[extra_i..]); | 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,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]; | 6334 | const asm_source = mem.sliceAsBytes(self.air.extra[extra_i..])[0..extra.data.source_len]; |
| 6001 | | 6335 | var line_it = mem.tokenize(u8, asm_source, "\n\r"); |
| 6002 | { | 6336 | while (line_it.next()) |line| { |
| 6003 | var iter = std.mem.tokenize(u8, asm_source, "\n\r"); | 6337 | var mnem_it = mem.tokenize(u8, line, " \t"); |
| 6004 | while (iter.next()) |ins| { | 6338 | const mnem = mnem_it.next() orelse continue; |
| 6005 | if (mem.eql(u8, ins, "syscall")) { | 6339 | if (mem.startsWith(u8, mnem, "#")) continue; |
| 6006 | try self.asmOpOnly(.syscall); | 6340 | var arg_it = mem.tokenize(u8, mnem_it.rest(), ", "); |
| 6007 | } else if (mem.indexOf(u8, ins, "push")) |_| { | 6341 | if (std.ascii.eqlIgnoreCase(mnem, "syscall")) { |
| 6008 | const arg = ins[4..]; | 6342 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6009 | if (mem.indexOf(u8, arg, "$")) |l| { | 6343 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6010 | const n = std.fmt.parseInt(u8, ins[4 + l + 1 ..], 10) catch { | 6344 | try self.asmOpOnly(.syscall); |
| 6011 | return self.fail("TODO implement more inline asm int parsing", .{}); | 6345 | } else if (std.ascii.eqlIgnoreCase(mnem, "push")) { |
| 6012 | }; | 6346 | const src = arg_it.next() orelse |
| 6013 | try self.asmImmediate(.push, Immediate.u(n)); | 6347 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6014 | } else if (mem.indexOf(u8, arg, "%%")) |l| { | 6348 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6015 | const reg_name = ins[4 + l + 2 ..]; | 6349 | return self.fail("Too many operands: '{s}'", .{line}); |
| 6016 | const reg = parseRegName(reg_name) orelse | 6350 | if (mem.startsWith(u8, src, "$")) { |
| 6017 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 6351 | const imm = std.fmt.parseInt(u32, src["$".len..], 0) catch |
| 6018 | try self.asmRegister(.push, reg); | 6352 | return self.fail("Invalid immediate: '{s}'", .{src}); |
| 6019 | } else return self.fail("TODO more push operands", .{}); | 6353 | try self.asmImmediate(.push, Immediate.u(imm)); |
| 6020 | } else if (mem.indexOf(u8, ins, "pop")) |_| { | 6354 | } else if (mem.startsWith(u8, src, "%%")) { |
| 6021 | const arg = ins[3..]; | 6355 | const reg = parseRegName(src["%%".len..]) orelse |
| 6022 | if (mem.indexOf(u8, arg, "%%")) |l| { | 6356 | return self.fail("Invalid register: '{s}'", .{src}); |
| 6023 | const reg_name = ins[3 + l + 2 ..]; | 6357 | try self.asmRegister(.push, reg); |
| 6024 | const reg = parseRegName(reg_name) orelse | 6358 | } else return self.fail("Unsupported operand: '{s}'", .{src}); |
| 6025 | return self.fail("unrecognized register: '{s}'", .{reg_name}); | 6359 | } else if (std.ascii.eqlIgnoreCase(mnem, "pop")) { |
| 6026 | try self.asmRegister(.pop, reg); | 6360 | const dst = arg_it.next() orelse |
| 6027 | } else return self.fail("TODO more pop operands", .{}); | 6361 | return self.fail("Not enough operands: '{s}'", .{line}); |
| 6028 | } else { | 6362 | if (arg_it.next()) |trailing| if (!mem.startsWith(u8, trailing, "#")) |
| 6029 | return self.fail("TODO implement support for more x86 assembly instructions", .{}); | 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 | | 6400 | } |
| 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 | }; | | |
| 6046 | | 6401 | |
| 6047 | simple: { | 6402 | simple: { |
| 6048 | var buf = [1]Air.Inst.Ref{.none} ** (Liveness.bpi - 1); | 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,20 +6632,25 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl |
| 6277 | .disp = -stack_offset, | 6632 | .disp = -stack_offset, |
| 6278 | }), immediate); | 6633 | }), immediate); |
| 6279 | }, | 6634 | }, |
| 6280 | 8 => { | 6635 | 3, 5...7 => unreachable, |
| | 6636 | else => { |
| 6281 | // 64 bit write to memory would take two mov's anyways so we | 6637 | // 64 bit write to memory would take two mov's anyways so we |
| 6282 | // insted just use two 32 bit writes to avoid register allocation | 6638 | // insted just use two 32 bit writes to avoid register allocation |
| 6283 | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ | 6639 | var offset: i32 = 0; |
| 6284 | .base = base_reg, | 6640 | while (offset < abi_size) : (offset += 4) try self.asmMemoryImmediate( |
| 6285 | .disp = -stack_offset + 4, | 6641 | .mov, |
| 6286 | }), Immediate.u(@truncate(u32, x_big >> 32))); | 6642 | Memory.sib(.dword, .{ .base = base_reg, .disp = offset - stack_offset }), |
| 6287 | try self.asmMemoryImmediate(.mov, Memory.sib(.dword, .{ | 6643 | if (ty.isSignedInt()) |
| 6288 | .base = base_reg, | 6644 | Immediate.s(@truncate( |
| 6289 | .disp = -stack_offset, | 6645 | i32, |
| 6290 | }), Immediate.u(@truncate(u32, x_big))); | 6646 | @bitCast(i64, x_big) >> (math.cast(u6, offset * 8) orelse 63), |
| 6291 | }, | 6647 | )) |
| 6292 | else => { | 6648 | else |
| 6293 | return self.fail("TODO implement set abi_size=large stack variable with immediate", .{}); | 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,7 +6751,7 @@ fn genInlineMemcpyRegisterRegister( |
| 6391 | }), registerAlias(tmp_reg, nearest_power_of_two)); | 6751 | }), registerAlias(tmp_reg, nearest_power_of_two)); |
| 6392 | | 6752 | |
| 6393 | if (nearest_power_of_two > 1) { | 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 | .immediate = nearest_power_of_two * 8, | 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,20 +7254,37 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 6894 | const ptr_ty = self.air.typeOf(extra.ptr); | 7254 | const ptr_ty = self.air.typeOf(extra.ptr); |
| 6895 | const ptr_mcv = try self.resolveInst(extra.ptr); | 7255 | const ptr_mcv = try self.resolveInst(extra.ptr); |
| 6896 | const val_ty = self.air.typeOf(extra.expected_value); | 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 | const exp_mcv = try self.resolveInst(extra.expected_value); | 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 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); | 7271 | const rax_lock = self.register_manager.lockRegAssumeUnused(.rax); |
| 6901 | defer self.register_manager.unlockReg(rax_lock); | 7272 | defer self.register_manager.unlockReg(rax_lock); |
| 6902 | | 7273 | |
| 6903 | const new_mcv = try self.resolveInst(extra.new_value); | 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 | const new_lock = self.register_manager.lockRegAssumeUnused(new_reg); | 7283 | const new_lock = self.register_manager.lockRegAssumeUnused(new_reg); |
| 6906 | defer self.register_manager.unlockReg(new_lock); | 7284 | defer self.register_manager.unlockReg(new_lock); |
| 6907 | | 7285 | |
| 6908 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); | | |
| 6909 | const ptr_size = Memory.PtrSize.fromSize(val_abi_size); | 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 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), | 7288 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), |
| 6912 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), | 7289 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), |
| 6913 | else => Memory.sib(ptr_size, .{ .base = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }), | 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,16 +7293,30 @@ fn airCmpxchg(self: *Self, inst: Air.Inst.Index) !void { |
| 6916 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); | 7293 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); |
| 6917 | | 7294 | |
| 6918 | try self.spillEflagsIfOccupied(); | 7295 | try self.spillEflagsIfOccupied(); |
| 6919 | _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ | 7296 | if (val_abi_size <= 8) { |
| 6920 | .r1 = new_reg, | 7297 | _ = try self.addInst(.{ .tag = .cmpxchg, .ops = .lock_mr_sib, .data = .{ .rx = .{ |
| 6921 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), | 7298 | .r = registerAlias(new_reg, val_abi_size), |
| 6922 | } } }); | 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 | const result: MCValue = result: { | 7307 | const result: MCValue = result: { |
| 6925 | if (self.liveness.isUnused(inst)) break :result .dead; | 7308 | if (self.liveness.isUnused(inst)) break :result .dead; |
| 6926 | | 7309 | |
| 6927 | self.eflags_inst = inst; | 7310 | if (val_abi_size <= 8) { |
| 6928 | break :result .{ .register_overflow = .{ .reg = .rax, .eflags = .ne } }; | 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 | return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); | 7321 | return self.finishAir(inst, result, .{ extra.ptr, extra.expected_value, extra.new_value }); |
| 6931 | } | 7322 | } |
| ... | @@ -6938,9 +7329,10 @@ fn atomicOp( | ... | @@ -6938,9 +7329,10 @@ fn atomicOp( |
| 6938 | ptr_ty: Type, | 7329 | ptr_ty: Type, |
| 6939 | val_ty: Type, | 7330 | val_ty: Type, |
| 6940 | unused: bool, | 7331 | unused: bool, |
| 6941 | op: ?std.builtin.AtomicRmwOp, | 7332 | rmw_op: ?std.builtin.AtomicRmwOp, |
| 6942 | order: std.builtin.AtomicOrder, | 7333 | order: std.builtin.AtomicOrder, |
| 6943 | ) InnerError!void { | 7334 | ) InnerError!void { |
| | 7335 | const dst_mcv = MCValue{ .register = dst_reg }; |
| 6944 | const dst_lock = self.register_manager.lockReg(dst_reg); | 7336 | const dst_lock = self.register_manager.lockReg(dst_reg); |
| 6945 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); | 7337 | defer if (dst_lock) |lock| self.register_manager.unlockReg(lock); |
| 6946 | | 7338 | |
| ... | @@ -6958,7 +7350,7 @@ fn atomicOp( | ... | @@ -6958,7 +7350,7 @@ fn atomicOp( |
| 6958 | | 7350 | |
| 6959 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); | 7351 | const val_abi_size = @intCast(u32, val_ty.abiSize(self.target.*)); |
| 6960 | const ptr_size = Memory.PtrSize.fromSize(val_abi_size); | 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 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), | 7354 | .register => |reg| Memory.sib(ptr_size, .{ .base = reg }), |
| 6963 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), | 7355 | .ptr_stack_offset => |off| Memory.sib(ptr_size, .{ .base = .rbp, .disp = -off }), |
| 6964 | else => Memory.sib(ptr_size, .{ .base = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }), | 7356 | else => Memory.sib(ptr_size, .{ .base = try self.copyToTmpRegister(ptr_ty, ptr_mcv) }), |
| ... | @@ -6966,48 +7358,197 @@ fn atomicOp( | ... | @@ -6966,48 +7358,197 @@ fn atomicOp( |
| 6966 | const mem_lock = if (ptr_mem.base()) |reg| self.register_manager.lockReg(reg) else null; | 7358 | const mem_lock = if (ptr_mem.base()) |reg| self.register_manager.lockReg(reg) else null; |
| 6967 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); | 7359 | defer if (mem_lock) |lock| self.register_manager.unlockReg(lock); |
| 6968 | | 7360 | |
| 6969 | try self.genSetReg(val_ty, dst_reg, val_mcv); | 7361 | const method: enum { lock, loop, libcall } = if (val_ty.isRuntimeFloat()) |
| 6970 | | 7362 | .loop |
| 6971 | const need_loop = val_ty.isRuntimeFloat() or if (op) |rmw| switch (rmw) { | 7363 | else switch (rmw_op orelse .Xchg) { |
| 6972 | .Xchg, .Add, .Sub => false, | 7364 | .Xchg, |
| 6973 | .And, .Or, .Xor => !unused, | 7365 | .Add, |
| 6974 | .Nand, .Max, .Min => true, | 7366 | .Sub, |
| 6975 | } else false; | 7367 | => if (val_abi_size <= 8) .lock else if (val_abi_size <= 16) .loop else .libcall, |
| 6976 | if (!need_loop) { | 7368 | .And, |
| 6977 | const tag: Mir.Inst.Tag = if (op) |rmw| switch (rmw) { | 7369 | .Or, |
| 6978 | .Xchg => if (unused) .mov else .xchg, | 7370 | .Xor, |
| 6979 | .Add => if (unused) .add else .xadd, | 7371 | => if (val_abi_size <= 8 and unused) .lock else if (val_abi_size <= 16) .loop else .libcall, |
| 6980 | .Sub => if (unused) .sub else .xadd, | 7372 | .Nand, |
| 6981 | .And => .@"and", | 7373 | .Max, |
| 6982 | .Or => .@"or", | 7374 | .Min, |
| 6983 | .Xor => .xor, | 7375 | => if (val_abi_size <= 16) .loop else .libcall, |
| 6984 | else => unreachable, | 7376 | }; |
| 6985 | } else switch (order) { | 7377 | switch (method) { |
| 6986 | .Unordered, .Monotonic, .Release, .AcqRel => .mov, | 7378 | .lock => { |
| 6987 | .Acquire => unreachable, | 7379 | const tag: Mir.Inst.Tag = if (rmw_op) |op| switch (op) { |
| 6988 | .SeqCst => .xchg, | 7380 | .Xchg => if (unused) .mov else .xchg, |
| 6989 | }; | 7381 | .Add => if (unused) .add else .xadd, |
| 6990 | if (op == std.builtin.AtomicRmwOp.Sub and tag == .xadd) { | 7382 | .Sub => if (unused) .sub else .xadd, |
| 6991 | try self.genUnOpMir(.neg, val_ty, .{ .register = dst_reg }); | 7383 | .And => .@"and", |
| 6992 | } | 7384 | .Or => .@"or", |
| 6993 | _ = try self.addInst(.{ .tag = tag, .ops = switch (tag) { | 7385 | .Xor => .xor, |
| 6994 | .mov, .xchg => .mr_sib, | 7386 | else => unreachable, |
| 6995 | .xadd, .add, .sub, .@"and", .@"or", .xor => .lock_mr_sib, | 7387 | } else switch (order) { |
| 6996 | else => unreachable, | 7388 | .Unordered, .Monotonic, .Release, .AcqRel => .mov, |
| 6997 | }, .data = .{ .rx = .{ | 7389 | .Acquire => unreachable, |
| 6998 | .r1 = registerAlias(dst_reg, val_abi_size), | 7390 | .SeqCst => .xchg, |
| 6999 | .payload = try self.addExtra(Mir.MemorySib.encode(ptr_mem)), | 7391 | }; |
| 7000 | } } }); | 7392 | |
| 7001 | return; | 7393 | try self.genSetReg(val_ty, dst_reg, val_mcv); |
| 7002 | } | 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 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { | 7544 | fn airAtomicRmw(self: *Self, inst: Air.Inst.Index) !void { |
| 7008 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; | 7545 | const pl_op = self.air.instructions.items(.data)[inst].pl_op; |
| 7009 | const extra = self.air.extraData(Air.AtomicRmw, pl_op.payload).data; | 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 | const unused = self.liveness.isUnused(inst); | 7552 | const unused = self.liveness.isUnused(inst); |
| 7012 | const dst_reg = try self.register_manager.allocReg(if (unused) null else inst, gp); | 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,8 +8128,8 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void { |
| 7587 | switch (int_info.signedness) { | 8128 | switch (int_info.signedness) { |
| 7588 | .signed => { | 8129 | .signed => { |
| 7589 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 8130 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |
| 7590 | try self.genShiftBinOpMir(.sal, Type.isize, reg, .{ .immediate = shift }); | 8131 | try self.genShiftBinOpMir(.sal, Type.isize, .{ .register = reg }, .{ .immediate = shift }); |
| 7591 | try self.genShiftBinOpMir(.sar, Type.isize, reg, .{ .immediate = shift }); | 8132 | try self.genShiftBinOpMir(.sar, Type.isize, .{ .register = reg }, .{ .immediate = shift }); |
| 7592 | }, | 8133 | }, |
| 7593 | .unsigned => { | 8134 | .unsigned => { |
| 7594 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); | 8135 | const shift = @intCast(u6, max_reg_bit_width - int_info.bits); |