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...@@ -2898,7 +2898,7 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
2898 _ = try self.addInst(.{2898 _ = try self.addInst(.{
2899 .tag = tag,2899 .tag = tag,
2900 .ops = (Mir.Ops{2900 .ops = (Mir.Ops{
2901 .reg1 = reg,2901 .reg1 = reg.to8(),
2902 .flags = flags,2902 .flags = flags,
2903 }).encode(),2903 }).encode(),
2904 .data = undefined,2904 .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 {...@@ -260,149 +260,60 @@ fn mirJmpCall(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
260 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);260 return lowerToMEnc(tag, RegisterOrMemory.reg(ops.reg1), emit.code);
261}261}
262262
263const CondType = enum {263fn mirCondJmp(emit: *Emit, mir_tag: Mir.Inst.Tag, inst: Mir.Inst.Index) InnerError!void {
264 /// greater than or equal264 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
265 gte,265 const target = emit.mir.instructions.items(.data)[inst].inst;
266266 const tag = switch (mir_tag) {
267 /// greater than267 .cond_jmp_greater_less => switch (ops.flags) {
268 gt,268 0b00 => Tag.jge,
269269 0b01 => Tag.jg,
270 /// less than270 0b10 => Tag.jl,
271 lt,271 0b11 => Tag.jle,
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,
364 },272 },
365 .eq => return switch (tag) {273 .cond_jmp_above_below => switch (ops.flags) {
366 .cond_jmp_eq_ne => 0x84,274 0b00 => Tag.jae,
367 .cond_set_byte_eq_ne => 0x94,275 0b01 => Tag.ja,
368 else => unreachable,276 0b10 => Tag.jb,
277 0b11 => Tag.jbe,
369 },278 },
370 .ne => return switch (tag) {279 .cond_jmp_eq_ne => switch (@truncate(u1, ops.flags)) {
371 .cond_jmp_eq_ne => 0x85,280 0b0 => Tag.jne,
372 .cond_set_byte_eq_ne => 0x95,281 0b1 => Tag.je,
373 else => unreachable,
374 },282 },
375 }283 else => unreachable,
376}284 };
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);
383 const source = emit.code.items.len;285 const source = emit.code.items.len;
384 const encoder = try Encoder.init(emit.code, 6);286 try lowerToDEnc(tag, 0, emit.code);
385 encoder.opcode_2byte(0x0f, opc);
386 try emit.relocs.append(emit.bin_file.allocator, .{287 try emit.relocs.append(emit.bin_file.allocator, .{
387 .source = source,288 .source = source,
388 .target = target,289 .target = target,
389 .offset = emit.code.items.len,290 .offset = emit.code.items.len - 4,
390 .length = 6,291 .length = 6,
391 });292 });
392 encoder.imm32(0);
393}293}
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 {
396 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);296 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
397 const cond = CondType.fromTagAndFlags(tag, ops.flags);297 const tag = switch (mir_tag) {
398 const opc = getCondOpCode(tag, cond);298 .cond_set_byte_greater_less => switch (ops.flags) {
399 const encoder = try Encoder.init(emit.code, 4);299 0b00 => Tag.setge,
400 encoder.rex(.{300 0b01 => Tag.setg,
401 .w = true,301 0b10 => Tag.setl,
402 .b = ops.reg1.isExtended(),302 0b11 => Tag.setle,
403 });303 },
404 encoder.opcode_2byte(0x0f, opc);304 .cond_set_byte_above_below => switch (ops.flags) {
405 encoder.modRm_direct(0x0, ops.reg1.lowId());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}
407318
408fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {319fn mirTest(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
...@@ -472,6 +383,103 @@ const Tag = enum {...@@ -472,6 +383,103 @@ const Tag = enum {
472 syscall,383 syscall,
473 ret_near,384 ret_near,
474 ret_far,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};
476484
477const Encoding = enum {485const Encoding = enum {
...@@ -547,11 +555,43 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {...@@ -547,11 +555,43 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
547 .d => return switch (tag) {555 .d => return switch (tag) {
548 .jmp_near => OpCode.oneByte(0xe9),556 .jmp_near => OpCode.oneByte(0xe9),
549 .call_near => OpCode.oneByte(0xe8),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 else => null,574 else => null,
551 },575 },
552 .m => return switch (tag) {576 .m => return switch (tag) {
553 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),577 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),
554 .pop => OpCode.oneByte(0x8f),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 else => null,595 else => null,
556 },596 },
557 .o => return switch (tag) {597 .o => return switch (tag) {
...@@ -628,6 +668,37 @@ inline fn getModRmExt(tag: Tag) ?u3 {...@@ -628,6 +668,37 @@ inline fn getModRmExt(tag: Tag) ?u3 {
628 .push => 0x6,668 .push => 0x6,
629 .pop => 0x0,669 .pop => 0x0,
630 .@"test" => 0x0,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 else => null,702 else => null,
632 };703 };
633}704}
...@@ -718,7 +789,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi...@@ -718,7 +789,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi
718789
719fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {790fn lowerToDEnc(tag: Tag, imm: i32, code: *std.ArrayList(u8)) InnerError!void {
720 const opc = getOpCode(tag, .d, false).?;791 const opc = getOpCode(tag, .d, false).?;
721 const encoder = try Encoder.init(code, 5);792 const encoder = try Encoder.init(code, 6);
722 opc.encode(encoder);793 opc.encode(encoder);
723 encoder.imm32(imm);794 encoder.imm32(imm);
724}795}
...@@ -728,10 +799,13 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -728,10 +799,13 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
728 const modrm_ext = getModRmExt(tag).?;799 const modrm_ext = getModRmExt(tag).?;
729 switch (reg_or_mem) {800 switch (reg_or_mem) {
730 .register => |reg| {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 const encoder = try Encoder.init(code, 3);806 const encoder = try Encoder.init(code, 3);
733 encoder.rex(.{807 encoder.rex(.{
734 .w = false,808 .w = tag.isSetCC(),
735 .b = reg.isExtended(),809 .b = reg.isExtended(),
736 });810 });
737 opc.encode(encoder);811 opc.encode(encoder);
...@@ -740,9 +814,12 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))...@@ -740,9 +814,12 @@ fn lowerToMEnc(tag: Tag, reg_or_mem: RegisterOrMemory, code: *std.ArrayList(u8))
740 .memory => |mem_op| {814 .memory => |mem_op| {
741 const encoder = try Encoder.init(code, 8);815 const encoder = try Encoder.init(code, 8);
742 if (mem_op.reg) |reg| {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 encoder.rex(.{821 encoder.rex(.{
745 .w = false,822 .w = tag.isSetCC(),
746 .b = reg.isExtended(),823 .b = reg.isExtended(),
747 });824 });
748 opc.encode(encoder);825 opc.encode(encoder);
...@@ -1172,6 +1249,7 @@ fn immOpSize(imm: i64) u8 {...@@ -1172,6 +1249,7 @@ fn immOpSize(imm: i64) u8 {
1172 return 64;1249 return 64;
1173}1250}
11741251
1252// TODO
1175fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1253fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1176 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);1254 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
1177 const scale = ops.flags;1255 const scale = ops.flags;
...@@ -1196,6 +1274,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -1196,6 +1274,7 @@ fn mirArithScaleSrc(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
1196 }1274 }
1197}1275}
11981276
1277// TODO
1199fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1278fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1200 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);1279 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
1201 const scale = ops.flags;1280 const scale = ops.flags;
...@@ -1243,6 +1322,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void...@@ -1243,6 +1322,7 @@ fn mirArithScaleDst(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void
1243 }1322 }
1244}1323}
12451324
1325// TODO
1246fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1326fn mirArithScaleImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1247 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);1327 const ops = Mir.Ops.decode(emit.mir.instructions.items(.ops)[inst]);
1248 const scale = ops.flags;1328 const scale = ops.flags;
...@@ -1757,6 +1837,8 @@ test "lower M encoding" {...@@ -1757,6 +1837,8 @@ test "lower M encoding" {
1757 try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]");1837 try expectEqualHexStrings("\xFF\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [rip + 0x10]");
1758 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer());1838 try lowerToMEnc(.jmp_near, RegisterOrMemory.mem(null, 0x10), code.buffer());
1759 try expectEqualHexStrings("\xFF\x24\x25\x10\x00\x00\x00", code.emitted(), "jmp qword ptr [ds:0x10]");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}
17611843
1762test "lower O encoding" {1844test "lower O encoding" {