| ... | ... | @@ -215,16 +215,7 @@ fn mirPushPop(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 215 | 215 | // PUSH imm32 |
| 216 | 216 | assert(tag == .push); |
| 217 | 217 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 218 | | const opc: u8 = if (imm <= math.maxInt(i8)) 0x6a else 0x6b; |
| 219 | | const encoder = try Encoder.init(emit.code, 2); |
| 220 | | encoder.opcode_1byte(opc); |
| 221 | | if (imm <= math.maxInt(i8)) { |
| 222 | | encoder.imm8(@intCast(i8, imm)); |
| 223 | | } else if (imm <= math.maxInt(i16)) { |
| 224 | | encoder.imm16(@intCast(i16, imm)); |
| 225 | | } else { |
| 226 | | encoder.imm32(imm); |
| 227 | | } |
| 218 | return lowerToIEnc(.push, imm, emit.code); |
| 228 | 219 | }, |
| 229 | 220 | 0b11 => unreachable, |
| 230 | 221 | } |
| ... | ... | @@ -422,31 +413,17 @@ fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 422 | 413 | assert(tag == .@"test"); |
| 423 | 414 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 424 | 415 | switch (ops.flags) { |
| 425 | | 0b00 => blk: { |
| 416 | 0b00 => { |
| 426 | 417 | if (ops.reg2 == .none) { |
| 427 | 418 | // TEST r/m64, imm32 |
| 419 | // MI |
| 428 | 420 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 429 | 421 | if (ops.reg1.to64() == .rax) { |
| 430 | | // TODO reduce the size of the instruction if the immediate |
| 431 | | // is smaller than 32 bits |
| 432 | | const encoder = try Encoder.init(emit.code, 6); |
| 433 | | encoder.rex(.{ |
| 434 | | .w = true, |
| 435 | | }); |
| 436 | | encoder.opcode_1byte(0xa9); |
| 437 | | encoder.imm32(imm); |
| 438 | | break :blk; |
| 422 | // TEST rax, imm32 |
| 423 | // I |
| 424 | return lowerToIEnc(.@"test", imm, emit.code); |
| 439 | 425 | } |
| 440 | | const opc: u8 = if (ops.reg1.size() == 8) 0xf6 else 0xf7; |
| 441 | | const encoder = try Encoder.init(emit.code, 7); |
| 442 | | encoder.rex(.{ |
| 443 | | .w = true, |
| 444 | | .b = ops.reg1.isExtended(), |
| 445 | | }); |
| 446 | | encoder.opcode_1byte(opc); |
| 447 | | encoder.modRm_direct(0, ops.reg1.lowId()); |
| 448 | | encoder.imm8(@intCast(i8, imm)); |
| 449 | | break :blk; |
| 426 | return lowerToMiEnc(.@"test", RegisterOrMemory.reg(ops.reg1), imm, emit.code); |
| 450 | 427 | } |
| 451 | 428 | // TEST r/m64, r64 |
| 452 | 429 | return emit.fail("TODO TEST r/m64, r64", .{}); |
| ... | ... | @@ -463,16 +440,16 @@ fn mirRet(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| 463 | 440 | switch (ops.flags) { |
| 464 | 441 | 0b00 => { |
| 465 | 442 | // RETF imm16 |
| 443 | // I |
| 466 | 444 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 467 | | encoder.opcode_1byte(0xca); |
| 468 | | encoder.imm16(@intCast(i16, imm)); |
| 445 | return lowerToIEnc(.ret_far, imm, emit.code); |
| 469 | 446 | }, |
| 470 | 447 | 0b01 => encoder.opcode_1byte(0xcb), // RETF |
| 471 | 448 | 0b10 => { |
| 472 | 449 | // RET imm16 |
| 450 | // I |
| 473 | 451 | const imm = emit.mir.instructions.items(.data)[inst].imm; |
| 474 | | encoder.opcode_1byte(0xc2); |
| 475 | | encoder.imm16(@intCast(i16, imm)); |
| 452 | return lowerToIEnc(.ret_near, imm, emit.code); |
| 476 | 453 | }, |
| 477 | 454 | 0b11 => encoder.opcode_1byte(0xc3), // RET |
| 478 | 455 | } |
| ... | ... | @@ -493,6 +470,9 @@ const Tag = enum { |
| 493 | 470 | call_near, |
| 494 | 471 | push, |
| 495 | 472 | pop, |
| 473 | @"test", |
| 474 | ret_near, |
| 475 | ret_far, |
| 496 | 476 | }; |
| 497 | 477 | |
| 498 | 478 | const Encoding = enum { |
| ... | ... | @@ -505,6 +485,9 @@ const Encoding = enum { |
| 505 | 485 | /// OP r64 |
| 506 | 486 | o, |
| 507 | 487 | |
| 488 | /// OP imm32 |
| 489 | i, |
| 490 | |
| 508 | 491 | /// OP r/m64, imm32 |
| 509 | 492 | mi, |
| 510 | 493 | |
| ... | ... | @@ -541,9 +524,17 @@ inline fn getOpCode(tag: Tag, enc: Encoding) ?u8 { |
| 541 | 524 | .pop => 0x58, |
| 542 | 525 | else => null, |
| 543 | 526 | }, |
| 527 | .i => return switch (tag) { |
| 528 | .push => 0x68, |
| 529 | .@"test" => 0xa9, |
| 530 | .ret_near => 0xc2, |
| 531 | .ret_far => 0xca, |
| 532 | else => null, |
| 533 | }, |
| 544 | 534 | .mi => return switch (tag) { |
| 545 | 535 | .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => 0x81, |
| 546 | 536 | .mov => 0xc7, |
| 537 | .@"test" => 0xf7, |
| 547 | 538 | else => null, |
| 548 | 539 | }, |
| 549 | 540 | .mr => return switch (tag) { |
| ... | ... | @@ -601,6 +592,7 @@ inline fn getModRmExt(tag: Tag) ?u3 { |
| 601 | 592 | .call_near => 0x2, |
| 602 | 593 | .push => 0x6, |
| 603 | 594 | .pop => 0x0, |
| 595 | .@"test" => 0x0, |
| 604 | 596 | else => null, |
| 605 | 597 | }; |
| 606 | 598 | } |
| ... | ... | @@ -646,6 +638,36 @@ const RegisterOrMemory = union(enum) { |
| 646 | 638 | } |
| 647 | 639 | }; |
| 648 | 640 | |
| 641 | fn lowerToIEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 642 | var opc = getOpCode(tag, .i).?; |
| 643 | if (tag == .ret_far or tag == .ret_near) { |
| 644 | const encoder = try Encoder.init(code, 3); |
| 645 | encoder.opcode_1byte(opc); |
| 646 | encoder.imm16(@intCast(i16, imm)); |
| 647 | return; |
| 648 | } |
| 649 | if (immOpSize(imm) == 8) { |
| 650 | // TODO I think getOpCode should track this |
| 651 | switch (tag) { |
| 652 | .push => opc += 2, |
| 653 | .@"test" => opc -= 1, |
| 654 | else => return error.EmitFail, |
| 655 | } |
| 656 | } |
| 657 | const encoder = try Encoder.init(code, 5); |
| 658 | if (immOpSize(imm) == 16) { |
| 659 | encoder.opcode_1byte(0x66); |
| 660 | } |
| 661 | encoder.opcode_1byte(opc); |
| 662 | if (immOpSize(imm) == 8) { |
| 663 | encoder.imm8(@intCast(i8, imm)); |
| 664 | } else if (immOpSize(imm) == 16) { |
| 665 | encoder.imm16(@intCast(i16, imm)); |
| 666 | } else { |
| 667 | encoder.imm32(imm); |
| 668 | } |
| 669 | } |
| 670 | |
| 649 | 671 | fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void { |
| 650 | 672 | if (reg.size() != 16 and reg.size() != 64) return error.EmitFail; // TODO correct for push/pop, but is it universal? |
| 651 | 673 | const opc = getOpCode(tag, .o).?; |