authorgravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-20 13:00:59+02:00
committergravatar for kubkon@jakubkonka.comJakub Konka <kubkon@jakubkonka.com> 2022-05-20 13:52:52+02:00
log274654d73e36df29fdc017a009c8607fb46a15e4
tree2232e0c506cf32b6d1056f2fb54415bd73f66978
parent0e43d007c098837d1863f2dab80b99e95910e0aa

x64: implement matching SSE instructions for generic cross-comp target


4 files changed, 384 insertions(+), 160 deletions(-)

src/arch/x86_64/CodeGen.zig+80-28
......@@ -881,7 +881,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
881881 switch (elem_ty.zigTypeTag()) {
882882 .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}),
883883 .Float => {
884 if (self.intrinsicsAllowed(elem_ty)) {
884 if (intrinsicsAllowed(self.target.*, elem_ty)) {
885885 const ptr_bytes: u64 = 32;
886886 if (abi_size <= ptr_bytes) {
887887 if (self.register_manager.tryAllocReg(inst, sse)) |reg| {
......@@ -970,7 +970,7 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou
970970fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
971971 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {
972972 .Float => blk: {
973 if (self.intrinsicsAllowed(ty)) break :blk sse;
973 if (intrinsicsAllowed(self.target.*, ty)) break :blk sse;
974974 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});
975975 },
976976 else => gp,
......@@ -987,7 +987,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
987987fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {
988988 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {
989989 .Float => blk: {
990 if (self.intrinsicsAllowed(ty)) break :blk sse;
990 if (intrinsicsAllowed(self.target.*, ty)) break :blk sse;
991991 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});
992992 },
993993 else => gp,
......@@ -3462,16 +3462,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
34623462 },
34633463 .register => |src_reg| switch (dst_ty.zigTypeTag()) {
34643464 .Float => {
3465 if (self.intrinsicsAllowed(dst_ty)) {
3465 if (intrinsicsAllowed(self.target.*, dst_ty)) {
34663466 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
34673467 .f32 => switch (mir_tag) {
3468 .add => Mir.Inst.Tag.add_f32_avx,
3469 .cmp => Mir.Inst.Tag.cmp_f32_avx,
3468 .add => if (hasAvxSupport(self.target.*))
3469 Mir.Inst.Tag.add_f32_avx
3470 else
3471 Mir.Inst.Tag.add_f32_sse,
3472 .cmp => if (hasAvxSupport(self.target.*))
3473 Mir.Inst.Tag.cmp_f32_avx
3474 else
3475 Mir.Inst.Tag.cmp_f32_sse,
34703476 else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),
34713477 },
34723478 .f64 => switch (mir_tag) {
3473 .add => Mir.Inst.Tag.add_f64_avx,
3474 .cmp => Mir.Inst.Tag.cmp_f64_avx,
3479 .add => if (hasAvxSupport(self.target.*))
3480 Mir.Inst.Tag.add_f64_avx
3481 else
3482 Mir.Inst.Tag.add_f64_sse,
3483 .cmp => if (hasAvxSupport(self.target.*))
3484 Mir.Inst.Tag.cmp_f64_avx
3485 else
3486 Mir.Inst.Tag.cmp_f64_sse,
34753487 else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
34763488 },
34773489 else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),
......@@ -5324,10 +5336,16 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
53245336 .register => |reg| {
53255337 switch (ty.zigTypeTag()) {
53265338 .Float => {
5327 if (self.intrinsicsAllowed(ty)) {
5339 if (intrinsicsAllowed(self.target.*, ty)) {
53285340 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5329 .f32 => .mov_f32_avx,
5330 .f64 => .mov_f64_avx,
5341 .f32 => if (hasAvxSupport(self.target.*))
5342 Mir.Inst.Tag.mov_f32_avx
5343 else
5344 Mir.Inst.Tag.mov_f32_sse,
5345 .f64 => if (hasAvxSupport(self.target.*))
5346 Mir.Inst.Tag.mov_f64_avx
5347 else
5348 Mir.Inst.Tag.mov_f64_sse,
53315349 else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),
53325350 };
53335351 _ = try self.addInst(.{
......@@ -5508,10 +5526,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55085526
55095527 switch (ty.zigTypeTag()) {
55105528 .Float => {
5511 if (self.intrinsicsAllowed(ty)) {
5529 if (intrinsicsAllowed(self.target.*, ty)) {
55125530 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5513 .f32 => .mov_f32_avx,
5514 .f64 => .mov_f64_avx,
5531 .f32 => if (hasAvxSupport(self.target.*))
5532 Mir.Inst.Tag.mov_f32_avx
5533 else
5534 Mir.Inst.Tag.mov_f32_sse,
5535 .f64 => if (hasAvxSupport(self.target.*))
5536 Mir.Inst.Tag.mov_f64_avx
5537 else
5538 Mir.Inst.Tag.mov_f64_sse,
55155539 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
55165540 };
55175541 _ = try self.addInst(.{
......@@ -6032,10 +6056,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
60326056 },
60336057 },
60346058 .Float => {
6035 if (self.intrinsicsAllowed(ty)) {
6059 if (intrinsicsAllowed(self.target.*, ty)) {
60366060 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6037 .f32 => .mov_f32_avx,
6038 .f64 => .mov_f64_avx,
6061 .f32 => if (hasAvxSupport(self.target.*))
6062 Mir.Inst.Tag.mov_f32_avx
6063 else
6064 Mir.Inst.Tag.mov_f32_sse,
6065 .f64 => if (hasAvxSupport(self.target.*))
6066 Mir.Inst.Tag.mov_f64_avx
6067 else
6068 Mir.Inst.Tag.mov_f64_sse,
60396069 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
60406070 };
60416071 _ = try self.addInst(.{
......@@ -6072,10 +6102,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
60726102 const base_reg = try self.register_manager.allocReg(null, gp);
60736103 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
60746104
6075 if (self.intrinsicsAllowed(ty)) {
6105 if (intrinsicsAllowed(self.target.*, ty)) {
60766106 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6077 .f32 => .mov_f32_avx,
6078 .f64 => .mov_f64_avx,
6107 .f32 => if (hasAvxSupport(self.target.*))
6108 Mir.Inst.Tag.mov_f32_avx
6109 else
6110 Mir.Inst.Tag.mov_f32_sse,
6111 .f64 => if (hasAvxSupport(self.target.*))
6112 Mir.Inst.Tag.mov_f64_avx
6113 else
6114 Mir.Inst.Tag.mov_f64_sse,
60796115 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
60806116 };
60816117
......@@ -6115,10 +6151,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
61156151 const base_reg = try self.register_manager.allocReg(null, gp);
61166152 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
61176153
6118 if (self.intrinsicsAllowed(ty)) {
6154 if (intrinsicsAllowed(self.target.*, ty)) {
61196155 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6120 .f32 => .mov_f32_avx,
6121 .f64 => .mov_f64_avx,
6156 .f32 => if (hasAvxSupport(self.target.*))
6157 Mir.Inst.Tag.mov_f32_avx
6158 else
6159 Mir.Inst.Tag.mov_f32_sse,
6160 .f64 => if (hasAvxSupport(self.target.*))
6161 Mir.Inst.Tag.mov_f64_avx
6162 else
6163 Mir.Inst.Tag.mov_f64_sse,
61226164 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
61236165 };
61246166
......@@ -6230,10 +6272,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
62306272 },
62316273 },
62326274 .Float => {
6233 if (self.intrinsicsAllowed(ty)) {
6275 if (intrinsicsAllowed(self.target.*, ty)) {
62346276 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6235 .f32 => .mov_f32_avx,
6236 .f64 => .mov_f64_avx,
6277 .f32 => if (hasAvxSupport(self.target.*))
6278 Mir.Inst.Tag.mov_f32_avx
6279 else
6280 Mir.Inst.Tag.mov_f32_sse,
6281 .f64 => if (hasAvxSupport(self.target.*))
6282 Mir.Inst.Tag.mov_f64_avx
6283 else
6284 Mir.Inst.Tag.mov_f64_sse,
62376285 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
62386286 };
62396287 _ = try self.addInst(.{
......@@ -7046,11 +7094,15 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
70467094 }
70477095}
70487096
7049fn intrinsicsAllowed(self: *Self, ty: Type) bool {
7097fn intrinsicsAllowed(target: Target, ty: Type) bool {
70507098 return switch (ty.tag()) {
70517099 .f32,
70527100 .f64,
7053 => Target.x86.featureSetHasAny(self.target.cpu.features, .{ .avx, .avx2 }),
7101 => Target.x86.featureSetHasAny(target.cpu.features, .{ .sse2, .avx, .avx2 }),
70547102 else => unreachable, // TODO finish this off
70557103 };
70567104}
7105
7106fn hasAvxSupport(target: Target) bool {
7107 return Target.x86.featureSetHasAny(target.cpu.features, .{ .avx, .avx2 });
7108}
src/arch/x86_64/Emit.zig+273-130
......@@ -182,6 +182,16 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
182182 .interrupt => try emit.mirInterrupt(inst),
183183 .nop => try emit.mirNop(),
184184
185 // SSE instructions
186 .mov_f64_sse => try emit.mirMovFloatSse(.movsd, inst),
187 .mov_f32_sse => try emit.mirMovFloatSse(.movss, inst),
188
189 .add_f64_sse => try emit.mirAddFloatSse(.addsd, inst),
190 .add_f32_sse => try emit.mirAddFloatSse(.addss, inst),
191
192 .cmp_f64_sse => try emit.mirCmpFloatSse(.ucomisd, inst),
193 .cmp_f32_sse => try emit.mirCmpFloatSse(.ucomiss, inst),
194
185195 // AVX instructions
186196 .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst),
187197 .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst),
......@@ -536,6 +546,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
536546}
537547
538548inline fn setRexWRegister(reg: Register) bool {
549 if (reg.size() > 64) return false;
539550 if (reg.size() == 64) return true;
540551 return switch (reg) {
541552 .ah, .ch, .dh, .bh => true,
......@@ -963,11 +974,55 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
963974 }
964975}
965976
977// SSE instructions
978
979fn mirMovFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
980 const ops = emit.mir.instructions.items(.ops)[inst].decode();
981 switch (ops.flags) {
982 0b00 => {
983 const imm = emit.mir.instructions.items(.data)[inst].imm;
984 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
985 .disp = imm,
986 .base = ops.reg2,
987 }), emit.code);
988 },
989 0b01 => {
990 const imm = emit.mir.instructions.items(.data)[inst].imm;
991 return lowerToMrEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
992 .disp = imm,
993 .base = ops.reg1,
994 }), ops.reg2, emit.code);
995 },
996 0b10 => {
997 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
998 },
999 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1000 }
1001}
1002
1003fn mirAddFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1004 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1005 switch (ops.flags) {
1006 0b00 => {
1007 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1008 },
1009 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1010 }
1011}
1012
1013fn mirCmpFloatSse(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1014 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1015 switch (ops.flags) {
1016 0b00 => {
1017 return lowerToRmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
1018 },
1019 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
1020 }
1021}
9661022// AVX instructions
9671023
9681024fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
9691025 const ops = emit.mir.instructions.items(.ops)[inst].decode();
970
9711026 switch (ops.flags) {
9721027 0b00 => {
9731028 const imm = emit.mir.instructions.items(.data)[inst].imm;
......@@ -986,24 +1041,22 @@ fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
9861041 0b10 => {
9871042 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
9881043 },
989 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
1044 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
9901045 }
9911046}
9921047
9931048fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
9941049 const ops = emit.mir.instructions.items(.ops)[inst].decode();
995
9961050 switch (ops.flags) {
9971051 0b00 => {
9981052 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
9991053 },
1000 else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
1054 else => return emit.fail("TODO unused variant 0b{b} for {}", .{ ops.flags, tag }),
10011055 }
10021056}
10031057
10041058fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
10051059 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1006
10071060 switch (ops.flags) {
10081061 0b00 => {
10091062 return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
......@@ -1247,6 +1300,14 @@ const Tag = enum {
12471300 cmovng,
12481301 cmovb,
12491302 cmovnae,
1303 movsd,
1304 movss,
1305 addsd,
1306 addss,
1307 cmpsd,
1308 cmpss,
1309 ucomisd,
1310 ucomiss,
12501311 vmovsd,
12511312 vmovss,
12521313 vaddsd,
......@@ -1256,6 +1317,22 @@ const Tag = enum {
12561317 vucomisd,
12571318 vucomiss,
12581319
1320 fn isSse(tag: Tag) bool {
1321 return switch (tag) {
1322 .movsd,
1323 .movss,
1324 .addsd,
1325 .addss,
1326 .cmpsd,
1327 .cmpss,
1328 .ucomisd,
1329 .ucomiss,
1330 => true,
1331
1332 else => false,
1333 };
1334 }
1335
12591336 fn isAvx(tag: Tag) bool {
12601337 return switch (tag) {
12611338 .vmovsd,
......@@ -1369,190 +1446,256 @@ const Encoding = enum {
13691446 rvmi,
13701447};
13711448
1372const OpCode = union(enum) {
1373 one_byte: u8,
1374 two_byte: struct { _1: u8, _2: u8 },
1375
1376 fn oneByte(opc: u8) OpCode {
1377 return .{ .one_byte = opc };
1378 }
1449const OpCode = struct {
1450 bytes: [3]u8,
1451 count: usize,
13791452
1380 fn twoByte(opc1: u8, opc2: u8) OpCode {
1381 return .{ .two_byte = .{ ._1 = opc1, ._2 = opc2 } };
1453 fn init(comptime in_bytes: []const u8) OpCode {
1454 comptime assert(in_bytes.len <= 3);
1455 comptime var bytes: [3]u8 = undefined;
1456 inline for (in_bytes) |x, i| {
1457 bytes[i] = x;
1458 }
1459 return .{ .bytes = bytes, .count = in_bytes.len };
13821460 }
13831461
13841462 fn encode(opc: OpCode, encoder: Encoder) void {
1385 switch (opc) {
1386 .one_byte => |v| encoder.opcode_1byte(v),
1387 .two_byte => |v| encoder.opcode_2byte(v._1, v._2),
1463 switch (opc.count) {
1464 1 => encoder.opcode_1byte(opc.bytes[0]),
1465 2 => encoder.opcode_2byte(opc.bytes[0], opc.bytes[1]),
1466 3 => encoder.opcode_3byte(opc.bytes[0], opc.bytes[1], opc.bytes[2]),
1467 else => unreachable,
13881468 }
13891469 }
13901470
13911471 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {
1392 assert(opc == .one_byte);
1393 encoder.opcode_withReg(opc.one_byte, reg.lowEnc());
1472 assert(opc.count == 1);
1473 encoder.opcode_withReg(opc.bytes[0], reg.lowEnc());
13941474 }
13951475};
13961476
13971477inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
1478 // zig fmt: off
13981479 switch (enc) {
13991480 .zo => return switch (tag) {
1400 .ret_near => OpCode.oneByte(0xc3),
1401 .ret_far => OpCode.oneByte(0xcb),
1402 .int3 => OpCode.oneByte(0xcc),
1403 .nop => OpCode.oneByte(0x90),
1404 .syscall => OpCode.twoByte(0x0f, 0x05),
1405 .cbw => OpCode.oneByte(0x98),
1406 .cwd, .cdq, .cqo => OpCode.oneByte(0x99),
1407 else => unreachable,
1481 .ret_near => OpCode.init(&.{0xc3}),
1482 .ret_far => OpCode.init(&.{0xcb}),
1483 .int3 => OpCode.init(&.{0xcc}),
1484 .nop => OpCode.init(&.{0x90}),
1485 .syscall => OpCode.init(&.{ 0x0f, 0x05 }),
1486 .cbw => OpCode.init(&.{0x98}),
1487 .cwd,
1488 .cdq,
1489 .cqo => OpCode.init(&.{0x99}),
1490 else => unreachable,
14081491 },
14091492 .d => return switch (tag) {
1410 .jmp_near => OpCode.oneByte(0xe9),
1411 .call_near => OpCode.oneByte(0xe8),
1412 .jo => if (is_one_byte) OpCode.oneByte(0x70) else OpCode.twoByte(0x0f, 0x80),
1413 .jno => if (is_one_byte) OpCode.oneByte(0x71) else OpCode.twoByte(0x0f, 0x81),
1414 .jb, .jc, .jnae => if (is_one_byte) OpCode.oneByte(0x72) else OpCode.twoByte(0x0f, 0x82),
1415 .jnb, .jnc, .jae => if (is_one_byte) OpCode.oneByte(0x73) else OpCode.twoByte(0x0f, 0x83),
1416 .je, .jz => if (is_one_byte) OpCode.oneByte(0x74) else OpCode.twoByte(0x0f, 0x84),
1417 .jne, .jnz => if (is_one_byte) OpCode.oneByte(0x75) else OpCode.twoByte(0x0f, 0x85),
1418 .jna, .jbe => if (is_one_byte) OpCode.oneByte(0x76) else OpCode.twoByte(0x0f, 0x86),
1419 .jnbe, .ja => if (is_one_byte) OpCode.oneByte(0x77) else OpCode.twoByte(0x0f, 0x87),
1420 .js => if (is_one_byte) OpCode.oneByte(0x78) else OpCode.twoByte(0x0f, 0x88),
1421 .jns => if (is_one_byte) OpCode.oneByte(0x79) else OpCode.twoByte(0x0f, 0x89),
1422 .jpe, .jp => if (is_one_byte) OpCode.oneByte(0x7a) else OpCode.twoByte(0x0f, 0x8a),
1423 .jpo, .jnp => if (is_one_byte) OpCode.oneByte(0x7b) else OpCode.twoByte(0x0f, 0x8b),
1424 .jnge, .jl => if (is_one_byte) OpCode.oneByte(0x7c) else OpCode.twoByte(0x0f, 0x8c),
1425 .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d),
1426 .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e),
1427 .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f),
1428 else => unreachable,
1493 .jmp_near => OpCode.init(&.{0xe9}),
1494 .call_near => OpCode.init(&.{0xe8}),
1495 .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}),
1496 .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}),
1497 .jb,
1498 .jc,
1499 .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}),
1500 .jnb,
1501 .jnc,
1502 .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}),
1503 .je,
1504 .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}),
1505 .jne,
1506 .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}),
1507 .jna,
1508 .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}),
1509 .jnbe,
1510 .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}),
1511 .js => if (is_one_byte) OpCode.init(&.{0x78}) else OpCode.init(&.{0x0f,0x88}),
1512 .jns => if (is_one_byte) OpCode.init(&.{0x79}) else OpCode.init(&.{0x0f,0x89}),
1513 .jpe,
1514 .jp => if (is_one_byte) OpCode.init(&.{0x7a}) else OpCode.init(&.{0x0f,0x8a}),
1515 .jpo,
1516 .jnp => if (is_one_byte) OpCode.init(&.{0x7b}) else OpCode.init(&.{0x0f,0x8b}),
1517 .jnge,
1518 .jl => if (is_one_byte) OpCode.init(&.{0x7c}) else OpCode.init(&.{0x0f,0x8c}),
1519 .jge,
1520 .jnl => if (is_one_byte) OpCode.init(&.{0x7d}) else OpCode.init(&.{0x0f,0x8d}),
1521 .jle,
1522 .jng => if (is_one_byte) OpCode.init(&.{0x7e}) else OpCode.init(&.{0x0f,0x8e}),
1523 .jg,
1524 .jnle => if (is_one_byte) OpCode.init(&.{0x7f}) else OpCode.init(&.{0x0f,0x8f}),
1525 else => unreachable,
14291526 },
14301527 .m => return switch (tag) {
1431 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),
1432 .pop => OpCode.oneByte(0x8f),
1433 .seto => OpCode.twoByte(0x0f, 0x90),
1434 .setno => OpCode.twoByte(0x0f, 0x91),
1435 .setb, .setc, .setnae => OpCode.twoByte(0x0f, 0x92),
1436 .setnb, .setnc, .setae => OpCode.twoByte(0x0f, 0x93),
1437 .sete, .setz => OpCode.twoByte(0x0f, 0x94),
1438 .setne, .setnz => OpCode.twoByte(0x0f, 0x95),
1439 .setbe, .setna => OpCode.twoByte(0x0f, 0x96),
1440 .seta, .setnbe => OpCode.twoByte(0x0f, 0x97),
1441 .sets => OpCode.twoByte(0x0f, 0x98),
1442 .setns => OpCode.twoByte(0x0f, 0x99),
1443 .setp, .setpe => OpCode.twoByte(0x0f, 0x9a),
1444 .setnp, .setop => OpCode.twoByte(0x0f, 0x9b),
1445 .setl, .setnge => OpCode.twoByte(0x0f, 0x9c),
1446 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),
1447 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),
1448 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),
1449 .idiv, .div, .imul, .mul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
1450 .fisttp16 => OpCode.oneByte(0xdf),
1451 .fisttp32 => OpCode.oneByte(0xdb),
1452 .fisttp64 => OpCode.oneByte(0xdd),
1453 .fld32 => OpCode.oneByte(0xd9),
1454 .fld64 => OpCode.oneByte(0xdd),
1455 else => unreachable,
1528 .jmp_near,
1529 .call_near,
1530 .push => OpCode.init(&.{0xff}),
1531 .pop => OpCode.init(&.{0x8f}),
1532 .seto => OpCode.init(&.{0x0f,0x90}),
1533 .setno => OpCode.init(&.{0x0f,0x91}),
1534 .setb,
1535 .setc,
1536 .setnae => OpCode.init(&.{0x0f,0x92}),
1537 .setnb,
1538 .setnc,
1539 .setae => OpCode.init(&.{0x0f,0x93}),
1540 .sete,
1541 .setz => OpCode.init(&.{0x0f,0x94}),
1542 .setne,
1543 .setnz => OpCode.init(&.{0x0f,0x95}),
1544 .setbe,
1545 .setna => OpCode.init(&.{0x0f,0x96}),
1546 .seta,
1547 .setnbe => OpCode.init(&.{0x0f,0x97}),
1548 .sets => OpCode.init(&.{0x0f,0x98}),
1549 .setns => OpCode.init(&.{0x0f,0x99}),
1550 .setp,
1551 .setpe => OpCode.init(&.{0x0f,0x9a}),
1552 .setnp,
1553 .setop => OpCode.init(&.{0x0f,0x9b}),
1554 .setl,
1555 .setnge => OpCode.init(&.{0x0f,0x9c}),
1556 .setnl,
1557 .setge => OpCode.init(&.{0x0f,0x9d}),
1558 .setle,
1559 .setng => OpCode.init(&.{0x0f,0x9e}),
1560 .setnle,
1561 .setg => OpCode.init(&.{0x0f,0x9f}),
1562 .idiv,
1563 .div,
1564 .imul,
1565 .mul => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}),
1566 .fisttp16 => OpCode.init(&.{0xdf}),
1567 .fisttp32 => OpCode.init(&.{0xdb}),
1568 .fisttp64 => OpCode.init(&.{0xdd}),
1569 .fld32 => OpCode.init(&.{0xd9}),
1570 .fld64 => OpCode.init(&.{0xdd}),
1571 else => unreachable,
14561572 },
14571573 .o => return switch (tag) {
1458 .push => OpCode.oneByte(0x50),
1459 .pop => OpCode.oneByte(0x58),
1460 else => unreachable,
1574 .push => OpCode.init(&.{0x50}),
1575 .pop => OpCode.init(&.{0x58}),
1576 else => unreachable,
14611577 },
14621578 .i => return switch (tag) {
1463 .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68),
1464 .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9),
1465 .ret_near => OpCode.oneByte(0xc2),
1466 .ret_far => OpCode.oneByte(0xca),
1467 else => unreachable,
1579 .push => if (is_one_byte) OpCode.init(&.{0x6a}) else OpCode.init(&.{0x68}),
1580 .@"test" => if (is_one_byte) OpCode.init(&.{0xa8}) else OpCode.init(&.{0xa9}),
1581 .ret_near => OpCode.init(&.{0xc2}),
1582 .ret_far => OpCode.init(&.{0xca}),
1583 else => unreachable,
14681584 },
14691585 .m1 => return switch (tag) {
1470 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1),
1471 else => unreachable,
1586 .shl, .sal,
1587 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd0}) else OpCode.init(&.{0xd1}),
1588 else => unreachable,
14721589 },
14731590 .mc => return switch (tag) {
1474 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3),
1475 else => unreachable,
1591 .shl, .sal,
1592 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd2}) else OpCode.init(&.{0xd3}),
1593 else => unreachable,
14761594 },
14771595 .mi => return switch (tag) {
1478 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
1479 .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
1480 .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
1481 else => unreachable,
1596 .adc, .add,
1597 .sub, .xor,
1598 .@"and", .@"or",
1599 .sbb, .cmp => if (is_one_byte) OpCode.init(&.{0x80}) else OpCode.init(&.{0x81}),
1600 .mov => if (is_one_byte) OpCode.init(&.{0xc6}) else OpCode.init(&.{0xc7}),
1601 .@"test" => if (is_one_byte) OpCode.init(&.{0xf6}) else OpCode.init(&.{0xf7}),
1602 else => unreachable,
14821603 },
14831604 .mi8 => return switch (tag) {
1484 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83),
1485 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1),
1486 else => unreachable,
1605 .adc, .add,
1606 .sub, .xor,
1607 .@"and", .@"or",
1608 .sbb, .cmp => OpCode.init(&.{0x83}),
1609 .shl, .sal,
1610 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xc0}) else OpCode.init(&.{0xc1}),
1611 else => unreachable,
14871612 },
14881613 .mr => return switch (tag) {
1489 .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
1490 .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),
1491 .sub => OpCode.oneByte(if (is_one_byte) 0x28 else 0x29),
1492 .xor => OpCode.oneByte(if (is_one_byte) 0x30 else 0x31),
1493 .@"and" => OpCode.oneByte(if (is_one_byte) 0x20 else 0x21),
1494 .@"or" => OpCode.oneByte(if (is_one_byte) 0x08 else 0x09),
1495 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),
1496 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
1497 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
1498 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
1499 else => unreachable,
1614 .adc => if (is_one_byte) OpCode.init(&.{0x10}) else OpCode.init(&.{0x11}),
1615 .add => if (is_one_byte) OpCode.init(&.{0x00}) else OpCode.init(&.{0x01}),
1616 .sub => if (is_one_byte) OpCode.init(&.{0x28}) else OpCode.init(&.{0x29}),
1617 .xor => if (is_one_byte) OpCode.init(&.{0x30}) else OpCode.init(&.{0x31}),
1618 .@"and" => if (is_one_byte) OpCode.init(&.{0x20}) else OpCode.init(&.{0x21}),
1619 .@"or" => if (is_one_byte) OpCode.init(&.{0x08}) else OpCode.init(&.{0x09}),
1620 .sbb => if (is_one_byte) OpCode.init(&.{0x18}) else OpCode.init(&.{0x19}),
1621 .cmp => if (is_one_byte) OpCode.init(&.{0x38}) else OpCode.init(&.{0x39}),
1622 .mov => if (is_one_byte) OpCode.init(&.{0x88}) else OpCode.init(&.{0x89}),
1623 .@"test" => if (is_one_byte) OpCode.init(&.{0x84}) else OpCode.init(&.{0x85}),
1624 .movsd => OpCode.init(&.{0xf2,0x0f,0x11}),
1625 .movss => OpCode.init(&.{0xf3,0x0f,0x11}),
1626 else => unreachable,
15001627 },
15011628 .rm => return switch (tag) {
1502 .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13),
1503 .add => OpCode.oneByte(if (is_one_byte) 0x02 else 0x03),
1504 .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b),
1505 .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33),
1506 .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23),
1507 .@"or" => OpCode.oneByte(if (is_one_byte) 0x0a else 0x0b),
1508 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),
1509 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),
1510 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),
1511 .movsx => OpCode.twoByte(0x0f, if (is_one_byte) 0xbe else 0xbf),
1512 .movsxd => OpCode.oneByte(0x63),
1513 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),
1514 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),
1515 .imul => OpCode.twoByte(0x0f, 0xaf),
1516 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
1517 .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),
1518 .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),
1629 .adc => if (is_one_byte) OpCode.init(&.{0x12}) else OpCode.init(&.{0x13}),
1630 .add => if (is_one_byte) OpCode.init(&.{0x02}) else OpCode.init(&.{0x03}),
1631 .sub => if (is_one_byte) OpCode.init(&.{0x2a}) else OpCode.init(&.{0x2b}),
1632 .xor => if (is_one_byte) OpCode.init(&.{0x32}) else OpCode.init(&.{0x33}),
1633 .@"and" => if (is_one_byte) OpCode.init(&.{0x22}) else OpCode.init(&.{0x23}),
1634 .@"or" => if (is_one_byte) OpCode.init(&.{0x0a}) else OpCode.init(&.{0x0b}),
1635 .sbb => if (is_one_byte) OpCode.init(&.{0x1a}) else OpCode.init(&.{0x1b}),
1636 .cmp => if (is_one_byte) OpCode.init(&.{0x3a}) else OpCode.init(&.{0x3b}),
1637 .mov => if (is_one_byte) OpCode.init(&.{0x8a}) else OpCode.init(&.{0x8b}),
1638 .movsx => if (is_one_byte) OpCode.init(&.{0x0f,0xbe}) else OpCode.init(&.{0x0f,0xbf}),
1639 .movsxd => OpCode.init(&.{0x63}),
1640 .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}),
1641 .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}),
1642 .imul => OpCode.init(&.{0x0f,0xaf}),
1643 .cmove,
1644 .cmovz => OpCode.init(&.{0x0f,0x44}),
1645 .cmovb,
1646 .cmovnae => OpCode.init(&.{0x0f,0x42}),
1647 .cmovl,
1648 .cmovng => OpCode.init(&.{0x0f,0x4c}),
1649 .movsd => OpCode.init(&.{0xf2,0x0f,0x10}),
1650 .movss => OpCode.init(&.{0xf3,0x0f,0x10}),
1651 .addsd => OpCode.init(&.{0xf2,0x0f,0x58}),
1652 .addss => OpCode.init(&.{0xf3,0x0f,0x58}),
1653 .ucomisd => OpCode.init(&.{0x66,0x0f,0x2e}),
1654 .ucomiss => OpCode.init(&.{0x0f,0x2e}),
15191655 else => unreachable,
15201656 },
15211657 .oi => return switch (tag) {
1522 .mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8),
1658 .mov => if (is_one_byte) OpCode.init(&.{0xb0}) else OpCode.init(&.{0xb8}),
15231659 else => unreachable,
15241660 },
15251661 .fd => return switch (tag) {
1526 .mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1),
1662 .mov => if (is_one_byte) OpCode.init(&.{0xa0}) else OpCode.init(&.{0xa1}),
15271663 else => unreachable,
15281664 },
15291665 .td => return switch (tag) {
1530 .mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
1666 .mov => if (is_one_byte) OpCode.init(&.{0xa2}) else OpCode.init(&.{0xa3}),
15311667 else => unreachable,
15321668 },
15331669 .rmi => return switch (tag) {
1534 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
1535 else => unreachable,
1670 .imul => if (is_one_byte) OpCode.init(&.{0x6b}) else OpCode.init(&.{0x69}),
1671 else => unreachable,
15361672 },
15371673 .mv => return switch (tag) {
1538 .vmovsd, .vmovss => OpCode.oneByte(0x11),
1674 .vmovsd,
1675 .vmovss => OpCode.init(&.{0x11}),
15391676 else => unreachable,
15401677 },
15411678 .vm => return switch (tag) {
1542 .vmovsd, .vmovss => OpCode.oneByte(0x10),
1543 .vucomisd, .vucomiss => OpCode.oneByte(0x2e),
1679 .vmovsd,
1680 .vmovss => OpCode.init(&.{0x10}),
1681 .vucomisd,
1682 .vucomiss => OpCode.init(&.{0x2e}),
15441683 else => unreachable,
15451684 },
15461685 .rvm => return switch (tag) {
1547 .vaddsd, .vaddss => OpCode.oneByte(0x58),
1548 .vmovsd, .vmovss => OpCode.oneByte(0x10),
1686 .vaddsd,
1687 .vaddss => OpCode.init(&.{0x58}),
1688 .vmovsd,
1689 .vmovss => OpCode.init(&.{0x10}),
15491690 else => unreachable,
15501691 },
15511692 .rvmi => return switch (tag) {
1552 .vcmpsd, .vcmpss => OpCode.oneByte(0xc2),
1553 else => unreachable,
1693 .vcmpsd,
1694 .vcmpss => OpCode.init(&.{0xc2}),
1695 else => unreachable,
15541696 },
15551697 }
1698 // zig fmt: on
15561699}
15571700
15581701inline fn getModRmExt(tag: Tag) u3 {
src/arch/x86_64/Mir.zig+20-2
......@@ -345,11 +345,29 @@ pub const Inst = struct {
345345 /// Nop
346346 nop,
347347
348 /// AVX instructions
348 /// SSE instructions
349349 /// ops flags: form:
350350 /// 0b00 reg1, qword ptr [reg2 + imm32]
351351 /// 0b01 qword ptr [reg1 + imm32], reg2
352352 /// 0b10 reg1, reg2
353 mov_f64_sse,
354 mov_f32_sse,
355
356 /// ops flags: form:
357 /// 0b00 reg1, reg2
358 add_f64_sse,
359 add_f32_sse,
360
361 /// ops flags: form:
362 /// 0b00 reg1, reg2
363 cmp_f64_sse,
364 cmp_f32_sse,
365
366 /// AVX instructions
367 /// ops flags: form:
368 /// 0b00 reg1, qword ptr [reg2 + imm32]
369 /// 0b01 qword ptr [reg1 + imm32], reg2
370 /// 0b10 reg1, reg1, reg2
353371 mov_f64_avx,
354372 mov_f32_avx,
355373
......@@ -359,7 +377,7 @@ pub const Inst = struct {
359377 add_f32_avx,
360378
361379 /// ops flags: form:
362 ///
380 /// 0b00 reg1, reg1, reg2
363381 cmp_f64_avx,
364382 cmp_f32_avx,
365383
src/arch/x86_64/bits.zig+11
......@@ -441,6 +441,17 @@ pub const Encoder = struct {
441441 self.code.appendAssumeCapacity(opcode);
442442 }
443443
444 /// Encodes a 3 byte opcode
445 ///
446 /// e.g. MOVSD has the opcode 0xf2 0x0f 0x10
447 ///
448 /// encoder.opcode_3byte(0xf2, 0x0f, 0x10);
449 pub fn opcode_3byte(self: Self, prefix_1: u8, prefix_2: u8, opcode: u8) void {
450 self.code.appendAssumeCapacity(prefix_1);
451 self.code.appendAssumeCapacity(prefix_2);
452 self.code.appendAssumeCapacity(opcode);
453 }
454
444455 /// Encodes a 1 byte opcode with a reg field
445456 ///
446457 /// Remember to add a REX prefix byte if reg is extended!