| ... | ... | @@ -260,149 +260,60 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 260 | 260 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); |
| 261 | 261 | } |
| 262 | 262 | |
| 263 | | const CondType = enum { |
| 264 | | /// greater than or equal |
| 265 | | gte, |
| 266 | | |
| 267 | | /// greater than |
| 268 | | gt, |
| 269 | | |
| 270 | | /// less than |
| 271 | | lt, |
| 272 | | |
| 273 | | /// less than or equal |
| 274 | | lte, |
| 275 | | |
| 276 | | /// above or equal |
| 277 | | ae, |
| 278 | | |
| 279 | | /// above |
| 280 | | a, |
| 281 | | |
| 282 | | /// below |
| 283 | | b, |
| 284 | | |
| 285 | | /// below or equal |
| 286 | | be, |
| 287 | | |
| 288 | | /// not equal |
| 289 | | ne, |
| 290 | | |
| 291 | | /// equal |
| 292 | | eq, |
| 293 | | |
| 294 | | fn fromTagAndFlags(tag: Mir.Inst.Tag, flags: u2) CondType { |
| 295 | | return switch (tag) { |
| 296 | | .cond_jmp_greater_less, |
| 297 | | .cond_set_byte_greater_less, |
| 298 | | => switch (flags) { |
| 299 | | 0b00 => CondType.gte, |
| 300 | | 0b01 => CondType.gt, |
| 301 | | 0b10 => CondType.lt, |
| 302 | | 0b11 => CondType.lte, |
| 303 | | }, |
| 304 | | .cond_jmp_above_below, |
| 305 | | .cond_set_byte_above_below, |
| 306 | | => switch (flags) { |
| 307 | | 0b00 => CondType.ae, |
| 308 | | 0b01 => CondType.a, |
| 309 | | 0b10 => CondType.b, |
| 310 | | 0b11 => CondType.be, |
| 311 | | }, |
| 312 | | .cond_jmp_eq_ne, |
| 313 | | .cond_set_byte_eq_ne, |
| 314 | | => switch (@truncate(u1, flags)) { |
| 315 | | 0b0 => CondType.ne, |
| 316 | | 0b1 => CondType.eq, |
| 317 | | }, |
| 318 | | else => unreachable, |
| 319 | | }; |
| 320 | | } |
| 321 | | }; |
| 322 | | |
| 323 | | inline fn getCondOpCode(tag: Mir.Inst.Tag, cond: CondType) u8 { |
| 324 | | switch (cond) { |
| 325 | | .gte => return switch (tag) { |
| 326 | | .cond_jmp_greater_less => 0x8d, |
| 327 | | .cond_set_byte_greater_less => 0x9d, |
| 328 | | else => unreachable, |
| 329 | | }, |
| 330 | | .gt => return switch (tag) { |
| 331 | | .cond_jmp_greater_less => 0x8f, |
| 332 | | .cond_set_byte_greater_less => 0x9f, |
| 333 | | else => unreachable, |
| 334 | | }, |
| 335 | | .lt => return switch (tag) { |
| 336 | | .cond_jmp_greater_less => 0x8c, |
| 337 | | .cond_set_byte_greater_less => 0x9c, |
| 338 | | else => unreachable, |
| 339 | | }, |
| 340 | | .lte => return switch (tag) { |
| 341 | | .cond_jmp_greater_less => 0x8e, |
| 342 | | .cond_set_byte_greater_less => 0x9e, |
| 343 | | else => unreachable, |
| 344 | | }, |
| 345 | | .ae => return switch (tag) { |
| 346 | | .cond_jmp_above_below => 0x83, |
| 347 | | .cond_set_byte_above_below => 0x93, |
| 348 | | else => unreachable, |
| 349 | | }, |
| 350 | | .a => return switch (tag) { |
| 351 | | .cond_jmp_above_below => 0x87, |
| 352 | | .cond_set_byte_greater_less => 0x97, |
| 353 | | else => unreachable, |
| 354 | | }, |
| 355 | | .b => return switch (tag) { |
| 356 | | .cond_jmp_above_below => 0x82, |
| 357 | | .cond_set_byte_greater_less => 0x92, |
| 358 | | else => unreachable, |
| 359 | | }, |
| 360 | | .be => return switch (tag) { |
| 361 | | .cond_jmp_above_below => 0x86, |
| 362 | | .cond_set_byte_greater_less => 0x96, |
| 363 | | else => unreachable, |
| 263 | fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 264 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 265 | const target = emit.mir.instructions.items(.data)[inst].inst; |
| 266 | const tag = switch (mir_tag) { |
| 267 | .cond_jmp_greater_less => switch (ops.flags) { |
| 268 | 0b00 => Tag.jge, |
| 269 | 0b01 => Tag.jg, |
| 270 | 0b10 => Tag.jl, |
| 271 | 0b11 => Tag.jle, |
| 364 | 272 | }, |
| 365 | | .eq => return switch (tag) { |
| 366 | | .cond_jmp_eq_ne => 0x84, |
| 367 | | .cond_set_byte_eq_ne => 0x94, |
| 368 | | else => unreachable, |
| 273 | .cond_jmp_above_below => switch (ops.flags) { |
| 274 | 0b00 => Tag.jae, |
| 275 | 0b01 => Tag.ja, |
| 276 | 0b10 => Tag.jb, |
| 277 | 0b11 => Tag.jbe, |
| 369 | 278 | }, |
| 370 | | .ne => return switch (tag) { |
| 371 | | .cond_jmp_eq_ne => 0x85, |
| 372 | | .cond_set_byte_eq_ne => 0x95, |
| 373 | | else => unreachable, |
| 279 | .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) { |
| 280 | 0b0 => Tag.jne, |
| 281 | 0b1 => Tag.je, |
| 374 | 282 | }, |
| 375 | | } |
| 376 | | } |
| 377 | | |
| 378 | | fn mirCondJmp(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 379 | | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 380 | | const target = emit.mir.instructions.items(.data)[inst].inst; |
| 381 | | const cond = CondType.fromTagAndFlags(tag, ops.flags); |
| 382 | | const opc = getCondOpCode(tag, cond); |
| 283 | else => unreachable, |
| 284 | }; |
| 383 | 285 | const source = emit.code.items.len; |
| 384 | | const encoder = try Encoder.init(emit.code, 6); |
| 385 | | encoder.opcode_2byte(0x0f, opc); |
| 286 | try lowerToDEnc(tag, 0, emit.code); |
| 386 | 287 | try emit.relocs.append(emit.bin_file.allocator, .{ |
| 387 | 288 | .source = source, |
| 388 | 289 | .target = target, |
| 389 | | .offset = emit.code.items.len, |
| 290 | .offset = emit.code.items.len - 4, |
| 390 | 291 | .length = 6, |
| 391 | 292 | }); |
| 392 | | encoder.imm32(0); |
| 393 | 293 | } |
| 394 | 294 | |
| 395 | | fn mirCondSetByte(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 295 | fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void { |
| 396 | 296 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 397 | | const cond = CondType.fromTagAndFlags(tag, ops.flags); |
| 398 | | const opc = getCondOpCode(tag, cond); |
| 399 | | const encoder = try Encoder.init(emit.code, 4); |
| 400 | | encoder.rex(.{ |
| 401 | | .w = true, |
| 402 | | .b = ops.reg1.isExtended(), |
| 403 | | }); |
| 404 | | encoder.opcode_2byte(0x0f, opc); |
| 405 | | encoder.modRm_direct(0x0, ops.reg1.lowId()); |
| 297 | const tag = switch (mir_tag) { |
| 298 | .cond_set_byte_greater_less => switch (ops.flags) { |
| 299 | 0b00 => Tag.setge, |
| 300 | 0b01 => Tag.setg, |
| 301 | 0b10 => Tag.setl, |
| 302 | 0b11 => Tag.setle, |
| 303 | }, |
| 304 | .cond_set_byte_above_below => switch (ops.flags) { |
| 305 | 0b00 => Tag.setae, |
| 306 | 0b01 => Tag.seta, |
| 307 | 0b10 => Tag.setb, |
| 308 | 0b11 => Tag.setbe, |
| 309 | }, |
| 310 | .cond_set_byte_eq_ne => switch (@truncate(u1, ops.flags)) { |
| 311 | 0b0 => Tag.setne, |
| 312 | 0b1 => Tag.sete, |
| 313 | }, |
| 314 | else => unreachable, |
| 315 | }; |
| 316 | return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code); |
| 406 | 317 | } |
| 407 | 318 | |
| 408 | 319 | fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void { |
| ... | ... | @@ -472,6 +383,103 @@ const Tag = enum { |
| 472 | 383 | syscall, |
| 473 | 384 | ret_near, |
| 474 | 385 | ret_far, |
| 386 | jo, |
| 387 | jno, |
| 388 | jb, |
| 389 | jbe, |
| 390 | jc, |
| 391 | jnae, |
| 392 | jnc, |
| 393 | jae, |
| 394 | je, |
| 395 | jz, |
| 396 | jne, |
| 397 | jnz, |
| 398 | jna, |
| 399 | jnb, |
| 400 | jnbe, |
| 401 | ja, |
| 402 | js, |
| 403 | jns, |
| 404 | jpe, |
| 405 | jp, |
| 406 | jpo, |
| 407 | jnp, |
| 408 | jnge, |
| 409 | jl, |
| 410 | jge, |
| 411 | jnl, |
| 412 | jle, |
| 413 | jng, |
| 414 | jg, |
| 415 | jnle, |
| 416 | seto, |
| 417 | setno, |
| 418 | setb, |
| 419 | setc, |
| 420 | setnae, |
| 421 | setnb, |
| 422 | setnc, |
| 423 | setae, |
| 424 | sete, |
| 425 | setz, |
| 426 | setne, |
| 427 | setnz, |
| 428 | setbe, |
| 429 | setna, |
| 430 | seta, |
| 431 | setnbe, |
| 432 | sets, |
| 433 | setns, |
| 434 | setp, |
| 435 | setpe, |
| 436 | setnp, |
| 437 | setop, |
| 438 | setl, |
| 439 | setnge, |
| 440 | setnl, |
| 441 | setge, |
| 442 | setle, |
| 443 | setng, |
| 444 | setnle, |
| 445 | setg, |
| 446 | |
| 447 | fn isSetCC(tag: Tag) bool { |
| 448 | return switch (tag) { |
| 449 | .seto, |
| 450 | .setno, |
| 451 | .setb, |
| 452 | .setc, |
| 453 | .setnae, |
| 454 | .setnb, |
| 455 | .setnc, |
| 456 | .setae, |
| 457 | .sete, |
| 458 | .setz, |
| 459 | .setne, |
| 460 | .setnz, |
| 461 | .setbe, |
| 462 | .setna, |
| 463 | .seta, |
| 464 | .setnbe, |
| 465 | .sets, |
| 466 | .setns, |
| 467 | .setp, |
| 468 | .setpe, |
| 469 | .setnp, |
| 470 | .setop, |
| 471 | .setl, |
| 472 | .setnge, |
| 473 | .setnl, |
| 474 | .setge, |
| 475 | .setle, |
| 476 | .setng, |
| 477 | .setnle, |
| 478 | .setg, |
| 479 | => true, |
| 480 | else => false, |
| 481 | }; |
| 482 | } |
| 475 | 483 | }; |
| 476 | 484 | |
| 477 | 485 | const Encoding = enum { |
| ... | ... | @@ -547,11 +555,43 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode { |
| 547 | 555 | .d => return switch (tag) { |
| 548 | 556 | .jmp_near => OpCode.oneByte(0xe9), |
| 549 | 557 | .call_near => OpCode.oneByte(0xe8), |
| 558 | .jo => if (is_one_byte) OpCode.oneByte(0x70) else OpCode.twoByte(0x0f, 0x80), |
| 559 | .jno => if (is_one_byte) OpCode.oneByte(0x71) else OpCode.twoByte(0x0f, 0x81), |
| 560 | .jb, .jc, .jnae => if (is_one_byte) OpCode.oneByte(0x72) else OpCode.twoByte(0x0f, 0x82), |
| 561 | .jnb, .jnc, .jae => if (is_one_byte) OpCode.oneByte(0x73) else OpCode.twoByte(0x0f, 0x83), |
| 562 | .je, .jz => if (is_one_byte) OpCode.oneByte(0x74) else OpCode.twoByte(0x0f, 0x84), |
| 563 | .jne, .jnz => if (is_one_byte) OpCode.oneByte(0x75) else OpCode.twoByte(0x0f, 0x85), |
| 564 | .jna, .jbe => if (is_one_byte) OpCode.oneByte(0x76) else OpCode.twoByte(0x0f, 0x86), |
| 565 | .jnbe, .ja => if (is_one_byte) OpCode.oneByte(0x77) else OpCode.twoByte(0x0f, 0x87), |
| 566 | .js => if (is_one_byte) OpCode.oneByte(0x78) else OpCode.twoByte(0x0f, 0x88), |
| 567 | .jns => if (is_one_byte) OpCode.oneByte(0x79) else OpCode.twoByte(0x0f, 0x89), |
| 568 | .jpe, .jp => if (is_one_byte) OpCode.oneByte(0x7a) else OpCode.twoByte(0x0f, 0x8a), |
| 569 | .jpo, .jnp => if (is_one_byte) OpCode.oneByte(0x7b) else OpCode.twoByte(0x0f, 0x8b), |
| 570 | .jnge, .jl => if (is_one_byte) OpCode.oneByte(0x7c) else OpCode.twoByte(0x0f, 0x8c), |
| 571 | .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d), |
| 572 | .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e), |
| 573 | .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f), |
| 550 | 574 | else => null, |
| 551 | 575 | }, |
| 552 | 576 | .m => return switch (tag) { |
| 553 | 577 | .jmp_near, .call_near, .push => OpCode.oneByte(0xff), |
| 554 | 578 | .pop => OpCode.oneByte(0x8f), |
| 579 | .seto => OpCode.twoByte(0x0f, 0x90), |
| 580 | .setno => OpCode.twoByte(0x0f, 0x91), |
| 581 | .setb, .setc, .setnae => OpCode.twoByte(0x0f, 0x92), |
| 582 | .setnb, .setnc, .setae => OpCode.twoByte(0x0f, 0x93), |
| 583 | .sete, .setz => OpCode.twoByte(0x0f, 0x94), |
| 584 | .setne, .setnz => OpCode.twoByte(0x0f, 0x95), |
| 585 | .setbe, .setna => OpCode.twoByte(0x0f, 0x96), |
| 586 | .seta, .setnbe => OpCode.twoByte(0x0f, 0x97), |
| 587 | .sets => OpCode.twoByte(0x0f, 0x98), |
| 588 | .setns => OpCode.twoByte(0x0f, 0x99), |
| 589 | .setp, .setpe => OpCode.twoByte(0x0f, 0x9a), |
| 590 | .setnp, .setop => OpCode.twoByte(0x0f, 0x9b), |
| 591 | .setl, .setnge => OpCode.twoByte(0x0f, 0x9c), |
| 592 | .setnl, .setge => OpCode.twoByte(0x0f, 0x9d), |
| 593 | .setle, .setng => OpCode.twoByte(0x0f, 0x9e), |
| 594 | .setnle, .setg => OpCode.twoByte(0x0f, 0x9f), |
| 555 | 595 | else => null, |
| 556 | 596 | }, |
| 557 | 597 | .o => return switch (tag) { |
| ... | ... | @@ -628,6 +668,37 @@ inline fn getModRmExt(tag: Tag) ?u3 { |
| 628 | 668 | .push => 0x6, |
| 629 | 669 | .pop => 0x0, |
| 630 | 670 | .@"test" => 0x0, |
| 671 | .seto, |
| 672 | .setno, |
| 673 | .setb, |
| 674 | .setc, |
| 675 | .setnae, |
| 676 | .setnb, |
| 677 | .setnc, |
| 678 | .setae, |
| 679 | .sete, |
| 680 | .setz, |
| 681 | .setne, |
| 682 | .setnz, |
| 683 | .setbe, |
| 684 | .setna, |
| 685 | .seta, |
| 686 | .setnbe, |
| 687 | .sets, |
| 688 | .setns, |
| 689 | .setp, |
| 690 | .setpe, |
| 691 | .setnp, |
| 692 | .setop, |
| 693 | .setl, |
| 694 | .setnge, |
| 695 | .setnl, |
| 696 | .setge, |
| 697 | .setle, |
| 698 | .setng, |
| 699 | .setnle, |
| 700 | .setg, |
| 701 | => 0x0, |
| 631 | 702 | else => null, |
| 632 | 703 | }; |
| 633 | 704 | } |
| ... | ... | @@ -718,7 +789,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi |
| 718 | 789 | |
| 719 | 790 | fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void { |
| 720 | 791 | const opc = getOpCode(tag, .d, false).?; |
| 721 | | const encoder = try Encoder.init(code, 5); |
| 792 | const encoder = try Encoder.init(code, 6); |
| 722 | 793 | opc.encode(encoder); |
| 723 | 794 | encoder.imm32(imm); |
| 724 | 795 | } |
| ... | ... | @@ -728,10 +799,13 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 728 | 799 | const modrm_ext = getModRmExt(tag).?; |
| 729 | 800 | switch (reg_or_mem) { |
| 730 | 801 | .register => |reg| { |
| 731 | | if (reg.size() != 64) return error.EmitFail; |
| 802 | // TODO clean this up! |
| 803 | if (reg.size() != 64) { |
| 804 | if (reg.size() != 8 and !tag.isSetCC()) return error.EmitFail; |
| 805 | } |
| 732 | 806 | const encoder = try Encoder.init(code, 3); |
| 733 | 807 | encoder.rex(.{ |
| 734 | | .w = false, |
| 808 | .w = tag.isSetCC(), |
| 735 | 809 | .b = reg.isExtended(), |
| 736 | 810 | }); |
| 737 | 811 | opc.encode(encoder); |
| ... | ... | @@ -740,9 +814,12 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8)) |
| 740 | 814 | .memory => |mem_op| { |
| 741 | 815 | const encoder = try Encoder.init(code, 8); |
| 742 | 816 | if (mem_op.reg) |reg| { |
| 743 | | if (reg.size() != 64) return error.EmitFail; |
| 817 | // TODO clean this up! |
| 818 | if (reg.size() != 64) { |
| 819 | if (reg.size() != 8 and !tag.isSetCC()) return error.EmitFail; |
| 820 | } |
| 744 | 821 | encoder.rex(.{ |
| 745 | | .w = false, |
| 822 | .w = tag.isSetCC(), |
| 746 | 823 | .b = reg.isExtended(), |
| 747 | 824 | }); |
| 748 | 825 | opc.encode(encoder); |
| ... | ... | @@ -1172,6 +1249,7 @@ fn immOpSize(imm: i64) u8 { |
| 1172 | 1249 | return 64; |
| 1173 | 1250 | } |
| 1174 | 1251 | |
| 1252 | // TODO |
| 1175 | 1253 | fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 1176 | 1254 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1177 | 1255 | const scale = ops.flags; |
| ... | ... | @@ -1196,6 +1274,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1196 | 1274 | } |
| 1197 | 1275 | } |
| 1198 | 1276 | |
| 1277 | // TODO |
| 1199 | 1278 | fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 1200 | 1279 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1201 | 1280 | const scale = ops.flags; |
| ... | ... | @@ -1243,6 +1322,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void |
| 1243 | 1322 | } |
| 1244 | 1323 | } |
| 1245 | 1324 | |
| 1325 | // TODO |
| 1246 | 1326 | fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void { |
| 1247 | 1327 | const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]); |
| 1248 | 1328 | const scale = ops.flags; |
| ... | ... | @@ -1757,6 +1837,8 @@ test "lower M encoding" { |
| 1757 | 1837 | try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]"); |
| 1758 | 1838 | try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer()); |
| 1759 | 1839 | try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]"); |
| 1840 | try lowerToMEnc(.seta, RegisterOrMemory.reg(.r11b), code.buffer()); |
| 1841 | try expectEqualHexStrings("\x49\x0F\x97\xC3", code.emitted(), "seta r11b"); |
| 1760 | 1842 | } |
| 1761 | 1843 | |
| 1762 | 1844 | test "lower O encoding" { |