authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 20:29:34+01:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2021-12-23 20:29:34+01:00
logc50bb2b80f3ccdc361a12034e7919f5288131c5e
treeef9b3c0cb66e82c1b7c328917e9c1f10c8396de4
parent8c664d3f6a59e412f33bca8c969f70ceb3545b11

stage2: lower jcc and setcc conditional jump/set instructions


2 files changed, 219 insertions(+), 137 deletions(-)

src/arch/x86_64/CodeGen.zig+1-1
......@@ -2898,7 +2898,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
28982898 _ = try self.addInst(.{
28992899 .tag = tag,
29002900 .ops = (Mir.Ops{
2901 .reg1 = reg,
2901 .reg1 = reg.to8(),
29022902 .flags = flags,
29032903 }).encode(),
29042904 .data = undefined,
src/arch/x86_64/Emit.zig+218-136
......@@ -260,149 +260,60 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
260260 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
261261}
262262
263const 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
323inline 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,
263fn 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,
364272 },
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,
369278 },
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,
374282 },
375 }
376}
377
378fn 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 };
383285 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);
386287 try emit.relocs.append(emit.bin_file.allocator, .{
387288 .source = source,
388289 .target = target,
389 .offset = emit.code.items.len,
290 .offset = emit.code.items.len - 4,
390291 .length = 6,
391292 });
392 encoder.imm32(0);
393293}
394294
395fn mirCondSetByte(emit: *Emit, tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
295fn mirCondSetByte(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
396296 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);
406317}
407318
408319fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
......@@ -472,6 +383,103 @@ const Tag = enum {
472383 syscall,
473384 ret_near,
474385 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 }
475483};
476484
477485const Encoding = enum {
......@@ -547,11 +555,43 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
547555 .d => return switch (tag) {
548556 .jmp_near => OpCode.oneByte(0xe9),
549557 .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),
550574 else => null,
551575 },
552576 .m => return switch (tag) {
553577 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),
554578 .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),
555595 else => null,
556596 },
557597 .o => return switch (tag) {
......@@ -628,6 +668,37 @@ inline fn getModRmExt(tag: Tag) ?u3 {
628668 .push => 0x6,
629669 .pop => 0x0,
630670 .@"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,
631702 else => null,
632703 };
633704}
......@@ -718,7 +789,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi
718789
719790fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
720791 const opc = getOpCode(tag, .d, false).?;
721 const encoder = try Encoder.init(code, 5);
792 const encoder = try Encoder.init(code, 6);
722793 opc.encode(encoder);
723794 encoder.imm32(imm);
724795}
......@@ -728,10 +799,13 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
728799 const modrm_ext = getModRmExt(tag).?;
729800 switch (reg_or_mem) {
730801 .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 }
732806 const encoder = try Encoder.init(code, 3);
733807 encoder.rex(.{
734 .w = false,
808 .w = tag.isSetCC(),
735809 .b = reg.isExtended(),
736810 });
737811 opc.encode(encoder);
......@@ -740,9 +814,12 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
740814 .memory => |mem_op| {
741815 const encoder = try Encoder.init(code, 8);
742816 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 }
744821 encoder.rex(.{
745 .w = false,
822 .w = tag.isSetCC(),
746823 .b = reg.isExtended(),
747824 });
748825 opc.encode(encoder);
......@@ -1172,6 +1249,7 @@ fn immOpSize(imm: i64) u8 {
11721249 return 64;
11731250}
11741251
1252// TODO
11751253fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
11761254 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
11771255 const scale = ops.flags;
......@@ -1196,6 +1274,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
11961274 }
11971275}
11981276
1277// TODO
11991278fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
12001279 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
12011280 const scale = ops.flags;
......@@ -1243,6 +1322,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
12431322 }
12441323}
12451324
1325// TODO
12461326fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
12471327 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
12481328 const scale = ops.flags;
......@@ -1757,6 +1837,8 @@ test "lower M encoding" {
17571837 try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]");
17581838 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer());
17591839 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");
17601842}
17611843
17621844test "lower O encoding" {