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 {...@@ -881,7 +881,7 @@ fn allocRegOrMem(self: *Self, inst: Air.Inst.Index, reg_ok: bool) !MCValue {
881 switch (elem_ty.zigTypeTag()) {881 switch (elem_ty.zigTypeTag()) {
882 .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}),882 .Vector => return self.fail("TODO allocRegOrMem for Vector type", .{}),
883 .Float => {883 .Float => {
884 if (self.intrinsicsAllowed(elem_ty)) {884 if (intrinsicsAllowed(self.target.*, elem_ty)) {
885 const ptr_bytes: u64 = 32;885 const ptr_bytes: u64 = 32;
886 if (abi_size <= ptr_bytes) {886 if (abi_size <= ptr_bytes) {
887 if (self.register_manager.tryAllocReg(inst, sse)) |reg| {887 if (self.register_manager.tryAllocReg(inst, sse)) |reg| {
...@@ -970,7 +970,7 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou...@@ -970,7 +970,7 @@ pub fn spillRegisters(self: *Self, comptime count: comptime_int, registers: [cou
970fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {970fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
971 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {971 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {
972 .Float => blk: {972 .Float => blk: {
973 if (self.intrinsicsAllowed(ty)) break :blk sse;973 if (intrinsicsAllowed(self.target.*, ty)) break :blk sse;
974 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});974 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});
975 },975 },
976 else => gp,976 else => gp,
...@@ -987,7 +987,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {...@@ -987,7 +987,7 @@ fn copyToTmpRegister(self: *Self, ty: Type, mcv: MCValue) !Register {
987fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {987fn copyToRegisterWithInstTracking(self: *Self, reg_owner: Air.Inst.Index, ty: Type, mcv: MCValue) !MCValue {
988 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {988 const reg_class: RegisterManager.RegisterBitSet = switch (ty.zigTypeTag()) {
989 .Float => blk: {989 .Float => blk: {
990 if (self.intrinsicsAllowed(ty)) break :blk sse;990 if (intrinsicsAllowed(self.target.*, ty)) break :blk sse;
991 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});991 return self.fail("TODO copy {} to register", .{ty.fmtDebug()});
992 },992 },
993 else => gp,993 else => gp,
...@@ -3462,16 +3462,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu...@@ -3462,16 +3462,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
3462 },3462 },
3463 .register => |src_reg| switch (dst_ty.zigTypeTag()) {3463 .register => |src_reg| switch (dst_ty.zigTypeTag()) {
3464 .Float => {3464 .Float => {
3465 if (self.intrinsicsAllowed(dst_ty)) {3465 if (intrinsicsAllowed(self.target.*, dst_ty)) {
3466 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {3466 const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
3467 .f32 => switch (mir_tag) {3467 .f32 => switch (mir_tag) {
3468 .add => Mir.Inst.Tag.add_f32_avx,3468 .add => if (hasAvxSupport(self.target.*))
3469 .cmp => Mir.Inst.Tag.cmp_f32_avx,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,
3470 else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),3476 else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),
3471 },3477 },
3472 .f64 => switch (mir_tag) {3478 .f64 => switch (mir_tag) {
3473 .add => Mir.Inst.Tag.add_f64_avx,3479 .add => if (hasAvxSupport(self.target.*))
3474 .cmp => Mir.Inst.Tag.cmp_f64_avx,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,
3475 else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),3487 else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
3476 },3488 },
3477 else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),3489 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...@@ -5324,10 +5336,16 @@ fn genSetStackArg(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue) InnerE
5324 .register => |reg| {5336 .register => |reg| {
5325 switch (ty.zigTypeTag()) {5337 switch (ty.zigTypeTag()) {
5326 .Float => {5338 .Float => {
5327 if (self.intrinsicsAllowed(ty)) {5339 if (intrinsicsAllowed(self.target.*, ty)) {
5328 const tag: Mir.Inst.Tag = switch (ty.tag()) {5340 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5329 .f32 => .mov_f32_avx,5341 .f32 => if (hasAvxSupport(self.target.*))
5330 .f64 => .mov_f64_avx,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,
5331 else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),5349 else => return self.fail("TODO genSetStackArg for register for type {}", .{ty.fmtDebug()}),
5332 };5350 };
5333 _ = try self.addInst(.{5351 _ = try self.addInst(.{
...@@ -5508,10 +5526,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl...@@ -5508,10 +5526,16 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
55085526
5509 switch (ty.zigTypeTag()) {5527 switch (ty.zigTypeTag()) {
5510 .Float => {5528 .Float => {
5511 if (self.intrinsicsAllowed(ty)) {5529 if (intrinsicsAllowed(self.target.*, ty)) {
5512 const tag: Mir.Inst.Tag = switch (ty.tag()) {5530 const tag: Mir.Inst.Tag = switch (ty.tag()) {
5513 .f32 => .mov_f32_avx,5531 .f32 => if (hasAvxSupport(self.target.*))
5514 .f64 => .mov_f64_avx,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,
5515 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),5539 else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
5516 };5540 };
5517 _ = try self.addInst(.{5541 _ = try self.addInst(.{
...@@ -6032,10 +6056,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6032,10 +6056,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6032 },6056 },
6033 },6057 },
6034 .Float => {6058 .Float => {
6035 if (self.intrinsicsAllowed(ty)) {6059 if (intrinsicsAllowed(self.target.*, ty)) {
6036 const tag: Mir.Inst.Tag = switch (ty.tag()) {6060 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6037 .f32 => .mov_f32_avx,6061 .f32 => if (hasAvxSupport(self.target.*))
6038 .f64 => .mov_f64_avx,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,
6039 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),6069 else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
6040 };6070 };
6041 _ = try self.addInst(.{6071 _ = try self.addInst(.{
...@@ -6072,10 +6102,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6072,10 +6102,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6072 const base_reg = try self.register_manager.allocReg(null, gp);6102 const base_reg = try self.register_manager.allocReg(null, gp);
6073 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);6103 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
60746104
6075 if (self.intrinsicsAllowed(ty)) {6105 if (intrinsicsAllowed(self.target.*, ty)) {
6076 const tag: Mir.Inst.Tag = switch (ty.tag()) {6106 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6077 .f32 => .mov_f32_avx,6107 .f32 => if (hasAvxSupport(self.target.*))
6078 .f64 => .mov_f64_avx,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,
6079 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),6115 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
6080 };6116 };
60816117
...@@ -6115,10 +6151,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6115,10 +6151,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6115 const base_reg = try self.register_manager.allocReg(null, gp);6151 const base_reg = try self.register_manager.allocReg(null, gp);
6116 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);6152 try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
61176153
6118 if (self.intrinsicsAllowed(ty)) {6154 if (intrinsicsAllowed(self.target.*, ty)) {
6119 const tag: Mir.Inst.Tag = switch (ty.tag()) {6155 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6120 .f32 => .mov_f32_avx,6156 .f32 => if (hasAvxSupport(self.target.*))
6121 .f64 => .mov_f64_avx,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,
6122 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),6164 else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
6123 };6165 };
61246166
...@@ -6230,10 +6272,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void...@@ -6230,10 +6272,16 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
6230 },6272 },
6231 },6273 },
6232 .Float => {6274 .Float => {
6233 if (self.intrinsicsAllowed(ty)) {6275 if (intrinsicsAllowed(self.target.*, ty)) {
6234 const tag: Mir.Inst.Tag = switch (ty.tag()) {6276 const tag: Mir.Inst.Tag = switch (ty.tag()) {
6235 .f32 => .mov_f32_avx,6277 .f32 => if (hasAvxSupport(self.target.*))
6236 .f64 => .mov_f64_avx,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,
6237 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),6285 else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
6238 };6286 };
6239 _ = try self.addInst(.{6287 _ = try self.addInst(.{
...@@ -7046,11 +7094,15 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {...@@ -7046,11 +7094,15 @@ fn truncateRegister(self: *Self, ty: Type, reg: Register) !void {
7046 }7094 }
7047}7095}
70487096
7049fn intrinsicsAllowed(self: *Self, ty: Type) bool {7097fn intrinsicsAllowed(target: Target, ty: Type) bool {
7050 return switch (ty.tag()) {7098 return switch (ty.tag()) {
7051 .f32,7099 .f32,
7052 .f64,7100 .f64,
7053 => Target.x86.featureSetHasAny(self.target.cpu.features, .{ .avx, .avx2 }),7101 => Target.x86.featureSetHasAny(target.cpu.features, .{ .sse2, .avx, .avx2 }),
7054 else => unreachable, // TODO finish this off7102 else => unreachable, // TODO finish this off
7055 };7103 };
7056}7104}
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 {...@@ -182,6 +182,16 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
182 .interrupt => try emit.mirInterrupt(inst),182 .interrupt => try emit.mirInterrupt(inst),
183 .nop => try emit.mirNop(),183 .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
185 // AVX instructions195 // AVX instructions
186 .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst),196 .mov_f64_avx => try emit.mirMovFloatAvx(.vmovsd, inst),
187 .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst),197 .mov_f32_avx => try emit.mirMovFloatAvx(.vmovss, inst),
...@@ -536,6 +546,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {...@@ -536,6 +546,7 @@ fn mirArithMemImm(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
536}546}
537547
538inline fn setRexWRegister(reg: Register) bool {548inline fn setRexWRegister(reg: Register) bool {
549 if (reg.size() > 64) return false;
539 if (reg.size() == 64) return true;550 if (reg.size() == 64) return true;
540 return switch (reg) {551 return switch (reg) {
541 .ah, .ch, .dh, .bh => true,552 .ah, .ch, .dh, .bh => true,
...@@ -963,11 +974,55 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {...@@ -963,11 +974,55 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
963 }974 }
964}975}
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}
966// AVX instructions1022// AVX instructions
9671023
968fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1024fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
969 const ops = emit.mir.instructions.items(.ops)[inst].decode();1025 const ops = emit.mir.instructions.items(.ops)[inst].decode();
970
971 switch (ops.flags) {1026 switch (ops.flags) {
972 0b00 => {1027 0b00 => {
973 const imm = emit.mir.instructions.items(.data)[inst].imm;1028 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 {...@@ -986,24 +1041,22 @@ fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
986 0b10 => {1041 0b10 => {
987 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);1042 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
988 },1043 },
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 }),
990 }1045 }
991}1046}
9921047
993fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1048fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
994 const ops = emit.mir.instructions.items(.ops)[inst].decode();1049 const ops = emit.mir.instructions.items(.ops)[inst].decode();
995
996 switch (ops.flags) {1050 switch (ops.flags) {
997 0b00 => {1051 0b00 => {
998 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);1052 return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
999 },1053 },
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 }),
1001 }1055 }
1002}1056}
10031057
1004fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {1058fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
1005 const ops = emit.mir.instructions.items(.ops)[inst].decode();1059 const ops = emit.mir.instructions.items(.ops)[inst].decode();
1006
1007 switch (ops.flags) {1060 switch (ops.flags) {
1008 0b00 => {1061 0b00 => {
1009 return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);1062 return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
...@@ -1247,6 +1300,14 @@ const Tag = enum {...@@ -1247,6 +1300,14 @@ const Tag = enum {
1247 cmovng,1300 cmovng,
1248 cmovb,1301 cmovb,
1249 cmovnae,1302 cmovnae,
1303 movsd,
1304 movss,
1305 addsd,
1306 addss,
1307 cmpsd,
1308 cmpss,
1309 ucomisd,
1310 ucomiss,
1250 vmovsd,1311 vmovsd,
1251 vmovss,1312 vmovss,
1252 vaddsd,1313 vaddsd,
...@@ -1256,6 +1317,22 @@ const Tag = enum {...@@ -1256,6 +1317,22 @@ const Tag = enum {
1256 vucomisd,1317 vucomisd,
1257 vucomiss,1318 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
1259 fn isAvx(tag: Tag) bool {1336 fn isAvx(tag: Tag) bool {
1260 return switch (tag) {1337 return switch (tag) {
1261 .vmovsd,1338 .vmovsd,
...@@ -1369,190 +1446,256 @@ const Encoding = enum {...@@ -1369,190 +1446,256 @@ const Encoding = enum {
1369 rvmi,1446 rvmi,
1370};1447};
13711448
1372const OpCode = union(enum) {1449const OpCode = struct {
1373 one_byte: u8,1450 bytes: [3]u8,
1374 two_byte: struct { _1: u8, _2: u8 },1451 count: usize,
1375
1376 fn oneByte(opc: u8) OpCode {
1377 return .{ .one_byte = opc };
1378 }
13791452
1380 fn twoByte(opc1: u8, opc2: u8) OpCode {1453 fn init(comptime in_bytes: []const u8) OpCode {
1381 return .{ .two_byte = .{ ._1 = opc1, ._2 = opc2 } };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 };
1382 }1460 }
13831461
1384 fn encode(opc: OpCode, encoder: Encoder) void {1462 fn encode(opc: OpCode, encoder: Encoder) void {
1385 switch (opc) {1463 switch (opc.count) {
1386 .one_byte => |v| encoder.opcode_1byte(v),1464 1 => encoder.opcode_1byte(opc.bytes[0]),
1387 .two_byte => |v| encoder.opcode_2byte(v._1, v._2),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,
1388 }1468 }
1389 }1469 }
13901470
1391 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {1471 fn encodeWithReg(opc: OpCode, encoder: Encoder, reg: Register) void {
1392 assert(opc == .one_byte);1472 assert(opc.count == 1);
1393 encoder.opcode_withReg(opc.one_byte, reg.lowEnc());1473 encoder.opcode_withReg(opc.bytes[0], reg.lowEnc());
1394 }1474 }
1395};1475};
13961476
1397inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {1477inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
1478 // zig fmt: off
1398 switch (enc) {1479 switch (enc) {
1399 .zo => return switch (tag) {1480 .zo => return switch (tag) {
1400 .ret_near => OpCode.oneByte(0xc3),1481 .ret_near => OpCode.init(&.{0xc3}),
1401 .ret_far => OpCode.oneByte(0xcb),1482 .ret_far => OpCode.init(&.{0xcb}),
1402 .int3 => OpCode.oneByte(0xcc),1483 .int3 => OpCode.init(&.{0xcc}),
1403 .nop => OpCode.oneByte(0x90),1484 .nop => OpCode.init(&.{0x90}),
1404 .syscall => OpCode.twoByte(0x0f, 0x05),1485 .syscall => OpCode.init(&.{ 0x0f, 0x05 }),
1405 .cbw => OpCode.oneByte(0x98),1486 .cbw => OpCode.init(&.{0x98}),
1406 .cwd, .cdq, .cqo => OpCode.oneByte(0x99),1487 .cwd,
1407 else => unreachable,1488 .cdq,
1489 .cqo => OpCode.init(&.{0x99}),
1490 else => unreachable,
1408 },1491 },
1409 .d => return switch (tag) {1492 .d => return switch (tag) {
1410 .jmp_near => OpCode.oneByte(0xe9),1493 .jmp_near => OpCode.init(&.{0xe9}),
1411 .call_near => OpCode.oneByte(0xe8),1494 .call_near => OpCode.init(&.{0xe8}),
1412 .jo => if (is_one_byte) OpCode.oneByte(0x70) else OpCode.twoByte(0x0f, 0x80),1495 .jo => if (is_one_byte) OpCode.init(&.{0x70}) else OpCode.init(&.{0x0f,0x80}),
1413 .jno => if (is_one_byte) OpCode.oneByte(0x71) else OpCode.twoByte(0x0f, 0x81),1496 .jno => if (is_one_byte) OpCode.init(&.{0x71}) else OpCode.init(&.{0x0f,0x81}),
1414 .jb, .jc, .jnae => if (is_one_byte) OpCode.oneByte(0x72) else OpCode.twoByte(0x0f, 0x82),1497 .jb,
1415 .jnb, .jnc, .jae => if (is_one_byte) OpCode.oneByte(0x73) else OpCode.twoByte(0x0f, 0x83),1498 .jc,
1416 .je, .jz => if (is_one_byte) OpCode.oneByte(0x74) else OpCode.twoByte(0x0f, 0x84),1499 .jnae => if (is_one_byte) OpCode.init(&.{0x72}) else OpCode.init(&.{0x0f,0x82}),
1417 .jne, .jnz => if (is_one_byte) OpCode.oneByte(0x75) else OpCode.twoByte(0x0f, 0x85),1500 .jnb,
1418 .jna, .jbe => if (is_one_byte) OpCode.oneByte(0x76) else OpCode.twoByte(0x0f, 0x86),1501 .jnc,
1419 .jnbe, .ja => if (is_one_byte) OpCode.oneByte(0x77) else OpCode.twoByte(0x0f, 0x87),1502 .jae => if (is_one_byte) OpCode.init(&.{0x73}) else OpCode.init(&.{0x0f,0x83}),
1420 .js => if (is_one_byte) OpCode.oneByte(0x78) else OpCode.twoByte(0x0f, 0x88),1503 .je,
1421 .jns => if (is_one_byte) OpCode.oneByte(0x79) else OpCode.twoByte(0x0f, 0x89),1504 .jz => if (is_one_byte) OpCode.init(&.{0x74}) else OpCode.init(&.{0x0f,0x84}),
1422 .jpe, .jp => if (is_one_byte) OpCode.oneByte(0x7a) else OpCode.twoByte(0x0f, 0x8a),1505 .jne,
1423 .jpo, .jnp => if (is_one_byte) OpCode.oneByte(0x7b) else OpCode.twoByte(0x0f, 0x8b),1506 .jnz => if (is_one_byte) OpCode.init(&.{0x75}) else OpCode.init(&.{0x0f,0x85}),
1424 .jnge, .jl => if (is_one_byte) OpCode.oneByte(0x7c) else OpCode.twoByte(0x0f, 0x8c),1507 .jna,
1425 .jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d),1508 .jbe => if (is_one_byte) OpCode.init(&.{0x76}) else OpCode.init(&.{0x0f,0x86}),
1426 .jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e),1509 .jnbe,
1427 .jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f),1510 .ja => if (is_one_byte) OpCode.init(&.{0x77}) else OpCode.init(&.{0x0f,0x87}),
1428 else => unreachable,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,
1429 },1526 },
1430 .m => return switch (tag) {1527 .m => return switch (tag) {
1431 .jmp_near, .call_near, .push => OpCode.oneByte(0xff),1528 .jmp_near,
1432 .pop => OpCode.oneByte(0x8f),1529 .call_near,
1433 .seto => OpCode.twoByte(0x0f, 0x90),1530 .push => OpCode.init(&.{0xff}),
1434 .setno => OpCode.twoByte(0x0f, 0x91),1531 .pop => OpCode.init(&.{0x8f}),
1435 .setb, .setc, .setnae => OpCode.twoByte(0x0f, 0x92),1532 .seto => OpCode.init(&.{0x0f,0x90}),
1436 .setnb, .setnc, .setae => OpCode.twoByte(0x0f, 0x93),1533 .setno => OpCode.init(&.{0x0f,0x91}),
1437 .sete, .setz => OpCode.twoByte(0x0f, 0x94),1534 .setb,
1438 .setne, .setnz => OpCode.twoByte(0x0f, 0x95),1535 .setc,
1439 .setbe, .setna => OpCode.twoByte(0x0f, 0x96),1536 .setnae => OpCode.init(&.{0x0f,0x92}),
1440 .seta, .setnbe => OpCode.twoByte(0x0f, 0x97),1537 .setnb,
1441 .sets => OpCode.twoByte(0x0f, 0x98),1538 .setnc,
1442 .setns => OpCode.twoByte(0x0f, 0x99),1539 .setae => OpCode.init(&.{0x0f,0x93}),
1443 .setp, .setpe => OpCode.twoByte(0x0f, 0x9a),1540 .sete,
1444 .setnp, .setop => OpCode.twoByte(0x0f, 0x9b),1541 .setz => OpCode.init(&.{0x0f,0x94}),
1445 .setl, .setnge => OpCode.twoByte(0x0f, 0x9c),1542 .setne,
1446 .setnl, .setge => OpCode.twoByte(0x0f, 0x9d),1543 .setnz => OpCode.init(&.{0x0f,0x95}),
1447 .setle, .setng => OpCode.twoByte(0x0f, 0x9e),1544 .setbe,
1448 .setnle, .setg => OpCode.twoByte(0x0f, 0x9f),1545 .setna => OpCode.init(&.{0x0f,0x96}),
1449 .idiv, .div, .imul, .mul => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),1546 .seta,
1450 .fisttp16 => OpCode.oneByte(0xdf),1547 .setnbe => OpCode.init(&.{0x0f,0x97}),
1451 .fisttp32 => OpCode.oneByte(0xdb),1548 .sets => OpCode.init(&.{0x0f,0x98}),
1452 .fisttp64 => OpCode.oneByte(0xdd),1549 .setns => OpCode.init(&.{0x0f,0x99}),
1453 .fld32 => OpCode.oneByte(0xd9),1550 .setp,
1454 .fld64 => OpCode.oneByte(0xdd),1551 .setpe => OpCode.init(&.{0x0f,0x9a}),
1455 else => unreachable,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,
1456 },1572 },
1457 .o => return switch (tag) {1573 .o => return switch (tag) {
1458 .push => OpCode.oneByte(0x50),1574 .push => OpCode.init(&.{0x50}),
1459 .pop => OpCode.oneByte(0x58),1575 .pop => OpCode.init(&.{0x58}),
1460 else => unreachable,1576 else => unreachable,
1461 },1577 },
1462 .i => return switch (tag) {1578 .i => return switch (tag) {
1463 .push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68),1579 .push => if (is_one_byte) OpCode.init(&.{0x6a}) else OpCode.init(&.{0x68}),
1464 .@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9),1580 .@"test" => if (is_one_byte) OpCode.init(&.{0xa8}) else OpCode.init(&.{0xa9}),
1465 .ret_near => OpCode.oneByte(0xc2),1581 .ret_near => OpCode.init(&.{0xc2}),
1466 .ret_far => OpCode.oneByte(0xca),1582 .ret_far => OpCode.init(&.{0xca}),
1467 else => unreachable,1583 else => unreachable,
1468 },1584 },
1469 .m1 => return switch (tag) {1585 .m1 => return switch (tag) {
1470 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1),1586 .shl, .sal,
1471 else => unreachable,1587 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd0}) else OpCode.init(&.{0xd1}),
1588 else => unreachable,
1472 },1589 },
1473 .mc => return switch (tag) {1590 .mc => return switch (tag) {
1474 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3),1591 .shl, .sal,
1475 else => unreachable,1592 .shr, .sar => if (is_one_byte) OpCode.init(&.{0xd2}) else OpCode.init(&.{0xd3}),
1593 else => unreachable,
1476 },1594 },
1477 .mi => return switch (tag) {1595 .mi => return switch (tag) {
1478 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),1596 .adc, .add,
1479 .mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),1597 .sub, .xor,
1480 .@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),1598 .@"and", .@"or",
1481 else => unreachable,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,
1482 },1603 },
1483 .mi8 => return switch (tag) {1604 .mi8 => return switch (tag) {
1484 .adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83),1605 .adc, .add,
1485 .shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1),1606 .sub, .xor,
1486 else => unreachable,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,
1487 },1612 },
1488 .mr => return switch (tag) {1613 .mr => return switch (tag) {
1489 .adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),1614 .adc => if (is_one_byte) OpCode.init(&.{0x10}) else OpCode.init(&.{0x11}),
1490 .add => OpCode.oneByte(if (is_one_byte) 0x00 else 0x01),1615 .add => if (is_one_byte) OpCode.init(&.{0x00}) else OpCode.init(&.{0x01}),
1491 .sub => OpCode.oneByte(if (is_one_byte) 0x28 else 0x29),1616 .sub => if (is_one_byte) OpCode.init(&.{0x28}) else OpCode.init(&.{0x29}),
1492 .xor => OpCode.oneByte(if (is_one_byte) 0x30 else 0x31),1617 .xor => if (is_one_byte) OpCode.init(&.{0x30}) else OpCode.init(&.{0x31}),
1493 .@"and" => OpCode.oneByte(if (is_one_byte) 0x20 else 0x21),1618 .@"and" => if (is_one_byte) OpCode.init(&.{0x20}) else OpCode.init(&.{0x21}),
1494 .@"or" => OpCode.oneByte(if (is_one_byte) 0x08 else 0x09),1619 .@"or" => if (is_one_byte) OpCode.init(&.{0x08}) else OpCode.init(&.{0x09}),
1495 .sbb => OpCode.oneByte(if (is_one_byte) 0x18 else 0x19),1620 .sbb => if (is_one_byte) OpCode.init(&.{0x18}) else OpCode.init(&.{0x19}),
1496 .cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),1621 .cmp => if (is_one_byte) OpCode.init(&.{0x38}) else OpCode.init(&.{0x39}),
1497 .mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),1622 .mov => if (is_one_byte) OpCode.init(&.{0x88}) else OpCode.init(&.{0x89}),
1498 .@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),1623 .@"test" => if (is_one_byte) OpCode.init(&.{0x84}) else OpCode.init(&.{0x85}),
1499 else => unreachable,1624 .movsd => OpCode.init(&.{0xf2,0x0f,0x11}),
1625 .movss => OpCode.init(&.{0xf3,0x0f,0x11}),
1626 else => unreachable,
1500 },1627 },
1501 .rm => return switch (tag) {1628 .rm => return switch (tag) {
1502 .adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13),1629 .adc => if (is_one_byte) OpCode.init(&.{0x12}) else OpCode.init(&.{0x13}),
1503 .add => OpCode.oneByte(if (is_one_byte) 0x02 else 0x03),1630 .add => if (is_one_byte) OpCode.init(&.{0x02}) else OpCode.init(&.{0x03}),
1504 .sub => OpCode.oneByte(if (is_one_byte) 0x2a else 0x2b),1631 .sub => if (is_one_byte) OpCode.init(&.{0x2a}) else OpCode.init(&.{0x2b}),
1505 .xor => OpCode.oneByte(if (is_one_byte) 0x32 else 0x33),1632 .xor => if (is_one_byte) OpCode.init(&.{0x32}) else OpCode.init(&.{0x33}),
1506 .@"and" => OpCode.oneByte(if (is_one_byte) 0x22 else 0x23),1633 .@"and" => if (is_one_byte) OpCode.init(&.{0x22}) else OpCode.init(&.{0x23}),
1507 .@"or" => OpCode.oneByte(if (is_one_byte) 0x0a else 0x0b),1634 .@"or" => if (is_one_byte) OpCode.init(&.{0x0a}) else OpCode.init(&.{0x0b}),
1508 .sbb => OpCode.oneByte(if (is_one_byte) 0x1a else 0x1b),1635 .sbb => if (is_one_byte) OpCode.init(&.{0x1a}) else OpCode.init(&.{0x1b}),
1509 .cmp => OpCode.oneByte(if (is_one_byte) 0x3a else 0x3b),1636 .cmp => if (is_one_byte) OpCode.init(&.{0x3a}) else OpCode.init(&.{0x3b}),
1510 .mov => OpCode.oneByte(if (is_one_byte) 0x8a else 0x8b),1637 .mov => if (is_one_byte) OpCode.init(&.{0x8a}) else OpCode.init(&.{0x8b}),
1511 .movsx => OpCode.twoByte(0x0f, if (is_one_byte) 0xbe else 0xbf),1638 .movsx => if (is_one_byte) OpCode.init(&.{0x0f,0xbe}) else OpCode.init(&.{0x0f,0xbf}),
1512 .movsxd => OpCode.oneByte(0x63),1639 .movsxd => OpCode.init(&.{0x63}),
1513 .movzx => OpCode.twoByte(0x0f, if (is_one_byte) 0xb6 else 0xb7),1640 .movzx => if (is_one_byte) OpCode.init(&.{0x0f,0xb6}) else OpCode.init(&.{0x0f,0xb7}),
1514 .lea => OpCode.oneByte(if (is_one_byte) 0x8c else 0x8d),1641 .lea => if (is_one_byte) OpCode.init(&.{0x8c}) else OpCode.init(&.{0x8d}),
1515 .imul => OpCode.twoByte(0x0f, 0xaf),1642 .imul => OpCode.init(&.{0x0f,0xaf}),
1516 .cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),1643 .cmove,
1517 .cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),1644 .cmovz => OpCode.init(&.{0x0f,0x44}),
1518 .cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),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}),
1519 else => unreachable,1655 else => unreachable,
1520 },1656 },
1521 .oi => return switch (tag) {1657 .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}),
1523 else => unreachable,1659 else => unreachable,
1524 },1660 },
1525 .fd => return switch (tag) {1661 .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}),
1527 else => unreachable,1663 else => unreachable,
1528 },1664 },
1529 .td => return switch (tag) {1665 .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}),
1531 else => unreachable,1667 else => unreachable,
1532 },1668 },
1533 .rmi => return switch (tag) {1669 .rmi => return switch (tag) {
1534 .imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),1670 .imul => if (is_one_byte) OpCode.init(&.{0x6b}) else OpCode.init(&.{0x69}),
1535 else => unreachable,1671 else => unreachable,
1536 },1672 },
1537 .mv => return switch (tag) {1673 .mv => return switch (tag) {
1538 .vmovsd, .vmovss => OpCode.oneByte(0x11),1674 .vmovsd,
1675 .vmovss => OpCode.init(&.{0x11}),
1539 else => unreachable,1676 else => unreachable,
1540 },1677 },
1541 .vm => return switch (tag) {1678 .vm => return switch (tag) {
1542 .vmovsd, .vmovss => OpCode.oneByte(0x10),1679 .vmovsd,
1543 .vucomisd, .vucomiss => OpCode.oneByte(0x2e),1680 .vmovss => OpCode.init(&.{0x10}),
1681 .vucomisd,
1682 .vucomiss => OpCode.init(&.{0x2e}),
1544 else => unreachable,1683 else => unreachable,
1545 },1684 },
1546 .rvm => return switch (tag) {1685 .rvm => return switch (tag) {
1547 .vaddsd, .vaddss => OpCode.oneByte(0x58),1686 .vaddsd,
1548 .vmovsd, .vmovss => OpCode.oneByte(0x10),1687 .vaddss => OpCode.init(&.{0x58}),
1688 .vmovsd,
1689 .vmovss => OpCode.init(&.{0x10}),
1549 else => unreachable,1690 else => unreachable,
1550 },1691 },
1551 .rvmi => return switch (tag) {1692 .rvmi => return switch (tag) {
1552 .vcmpsd, .vcmpss => OpCode.oneByte(0xc2),1693 .vcmpsd,
1553 else => unreachable,1694 .vcmpss => OpCode.init(&.{0xc2}),
1695 else => unreachable,
1554 },1696 },
1555 }1697 }
1698 // zig fmt: on
1556}1699}
15571700
1558inline fn getModRmExt(tag: Tag) u3 {1701inline fn getModRmExt(tag: Tag) u3 {
src/arch/x86_64/Mir.zig+20-2
...@@ -345,11 +345,29 @@ pub const Inst = struct {...@@ -345,11 +345,29 @@ pub const Inst = struct {
345 /// Nop345 /// Nop
346 nop,346 nop,
347347
348 /// AVX instructions348 /// SSE instructions
349 /// ops flags: form:349 /// ops flags: form:
350 /// 0b00 reg1, qword ptr [reg2 + imm32]350 /// 0b00 reg1, qword ptr [reg2 + imm32]
351 /// 0b01 qword ptr [reg1 + imm32], reg2351 /// 0b01 qword ptr [reg1 + imm32], reg2
352 /// 0b10 reg1, reg2352 /// 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
353 mov_f64_avx,371 mov_f64_avx,
354 mov_f32_avx,372 mov_f32_avx,
355373
...@@ -359,7 +377,7 @@ pub const Inst = struct {...@@ -359,7 +377,7 @@ pub const Inst = struct {
359 add_f32_avx,377 add_f32_avx,
360378
361 /// ops flags: form:379 /// ops flags: form:
362 ///380 /// 0b00 reg1, reg1, reg2
363 cmp_f64_avx,381 cmp_f64_avx,
364 cmp_f32_avx,382 cmp_f32_avx,
365383
src/arch/x86_64/bits.zig+11
...@@ -441,6 +441,17 @@ pub const Encoder = struct {...@@ -441,6 +441,17 @@ pub const Encoder = struct {
441 self.code.appendAssumeCapacity(opcode);441 self.code.appendAssumeCapacity(opcode);
442 }442 }
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
444 /// Encodes a 1 byte opcode with a reg field455 /// Encodes a 1 byte opcode with a reg field
445 ///456 ///
446 /// Remember to add a REX prefix byte if reg is extended!457 /// Remember to add a REX prefix byte if reg is extended!