x64: handle basic f32 using AVX registers

This commit is contained in:
Jakub Konka 2022-05-18 14:15:27 +02:00
parent 020f99d893
commit 36b939e8db
4 changed files with 352 additions and 341 deletions

View File

@ -3469,22 +3469,28 @@ fn genBinOpMir(self: *Self, mir_tag: Mir.Inst.Tag, dst_ty: Type, dst_mcv: MCValu
return self.genBinOpMir(mir_tag, dst_ty, dst_mcv, .{ .register = reg });
},
.register => |src_reg| switch (dst_ty.zigTypeTag()) {
.Float => switch (dst_ty.tag()) {
.f64 => {
_ = try self.addInst(.{
.tag = switch (mir_tag) {
.add => .add_f64,
.cmp => .cmp_f64,
else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
},
.ops = Mir.Inst.Ops.encode(.{
.reg1 = dst_reg.to128(),
.reg2 = src_reg.to128(),
}),
.data = undefined,
});
},
else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),
.Float => {
const actual_tag: Mir.Inst.Tag = switch (dst_ty.tag()) {
.f32 => switch (mir_tag) {
.add => Mir.Inst.Tag.add_f32,
.cmp => Mir.Inst.Tag.cmp_f32,
else => return self.fail("TODO genBinOpMir for f32 register-register with MIR tag {}", .{mir_tag}),
},
.f64 => switch (mir_tag) {
.add => Mir.Inst.Tag.add_f64,
.cmp => Mir.Inst.Tag.cmp_f64,
else => return self.fail("TODO genBinOpMir for f64 register-register with MIR tag {}", .{mir_tag}),
},
else => return self.fail("TODO genBinOpMir for float register-register and type {}", .{dst_ty.fmtDebug()}),
};
_ = try self.addInst(.{
.tag = actual_tag,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = dst_reg.to128(),
.reg2 = src_reg.to128(),
}),
.data = undefined,
});
},
else => {
_ = try self.addInst(.{
@ -5475,20 +5481,25 @@ fn genSetStack(self: *Self, ty: Type, stack_offset: i32, mcv: MCValue, opts: Inl
const base_reg = opts.dest_stack_base orelse .rbp;
switch (ty.zigTypeTag()) {
.Float => switch (ty.tag()) {
.f32 => return self.fail("TODO genSetStack for register for f32", .{}),
.f64 => {
_ = try self.addInst(.{
.tag = .mov_f64,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = base_reg,
.reg2 = reg.to128(),
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
});
},
else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
.Float => {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => .mov_f32,
.f64 => .mov_f64,
else => return self.fail("TODO genSetStack for register for type {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
.tag = tag,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = switch (ty.tag()) {
.f32 => base_reg.to32(),
.f64 => base_reg.to64(),
else => unreachable,
},
.reg2 = reg.to128(),
.flags = 0b01,
}),
.data = .{ .imm = @bitCast(u32, -stack_offset) },
});
},
else => {
if (!math.isPowerOfTwo(abi_size)) {
@ -5991,21 +6002,22 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
}
},
},
.Float => switch (ty.tag()) {
.f32 => return self.fail("TODO genSetReg from register for f32", .{}),
.f64 => {
_ = try self.addInst(.{
.tag = .mov_f64,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = src_reg.to128(),
.flags = 0b10,
}),
.data = undefined,
});
return;
},
else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
.Float => {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => .mov_f32,
.f64 => .mov_f64,
else => return self.fail("TODO genSetReg from register for {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
.tag = tag,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = src_reg.to128(),
.flags = 0b10,
}),
.data = undefined,
});
return;
},
else => {},
}
@ -6035,22 +6047,27 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
},
.memory => |x| switch (ty.zigTypeTag()) {
.Float => {
switch (ty.tag()) {
.f32 => return self.fail("TODO genSetReg from memory for f32", .{}),
.f64 => {
const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
_ = try self.addInst(.{
.tag = .mov_f64,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = base_reg.to64(),
}),
.data = .{ .imm = 0 },
});
},
const base_reg = try self.register_manager.allocReg(null, .{ .selector_mask = gp });
try self.loadMemPtrIntoRegister(base_reg, Type.usize, mcv);
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => .mov_f32,
.f64 => .mov_f64,
else => return self.fail("TODO genSetReg from memory for {}", .{ty.fmtDebug()}),
}
};
_ = try self.addInst(.{
.tag = tag,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = switch (ty.tag()) {
.f32 => base_reg.to32(),
.f64 => base_reg.to64(),
else => unreachable,
},
}),
.data = .{ .imm = 0 },
});
},
else => {
if (x <= math.maxInt(i32)) {
@ -6142,20 +6159,25 @@ fn genSetReg(self: *Self, ty: Type, reg: Register, mcv: MCValue) InnerError!void
}
},
},
.Float => switch (ty.tag()) {
.f32 => return self.fail("TODO genSetReg from stack offset for f32", .{}),
.f64 => {
_ = try self.addInst(.{
.tag = .mov_f64,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = .rbp,
}),
.data = .{ .imm = @bitCast(u32, -off) },
});
return;
},
else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
.Float => {
const tag: Mir.Inst.Tag = switch (ty.tag()) {
.f32 => .mov_f32,
.f64 => .mov_f64,
else => return self.fail("TODO genSetReg from stack offset for {}", .{ty.fmtDebug()}),
};
_ = try self.addInst(.{
.tag = tag,
.ops = Mir.Inst.Ops.encode(.{
.reg1 = reg.to128(),
.reg2 = switch (ty.tag()) {
.f32 => .ebp,
.f64 => .rbp,
else => unreachable,
},
}),
.data = .{ .imm = @bitCast(u32, -off) },
});
return;
},
else => {},
}

View File

@ -183,11 +183,14 @@ pub fn lowerMir(emit: *Emit) InnerError!void {
.nop => try emit.mirNop(),
// AVX instructions
.mov_f64 => try emit.mirMovF64(inst),
.mov_f64 => try emit.mirMovFloatAvx(.vmovsd, inst),
.mov_f32 => try emit.mirMovFloatAvx(.vmovss, inst),
.add_f64 => try emit.mirAddF64(inst),
.add_f64 => try emit.mirAddFloatAvx(.vaddsd, inst),
.add_f32 => try emit.mirAddFloatAvx(.vaddss, inst),
.cmp_f64 => try emit.mirCmpF64(inst),
.cmp_f64 => try emit.mirCmpFloatAvx(.vucomisd, inst),
.cmp_f32 => try emit.mirCmpFloatAvx(.vucomiss, inst),
// Pseudo-instructions
.call_extern => try emit.mirCallExtern(inst),
@ -962,71 +965,48 @@ fn mirLeaPie(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
// AVX instructions
fn mirMovF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const tag = emit.mir.instructions.items(.tag)[inst];
assert(tag == .mov_f64);
fn mirMovFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = emit.mir.instructions.items(.ops)[inst].decode();
switch (ops.flags) {
0b00 => {
const imm = emit.mir.instructions.items(.data)[inst].imm;
return lowerToVmEnc(.vmovsd, ops.reg1, RegisterOrMemory.mem(.qword_ptr, .{
return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg2.size()), .{
.disp = imm,
.base = ops.reg2,
}), emit.code);
},
0b01 => {
const imm = emit.mir.instructions.items(.data)[inst].imm;
return lowerToMvEnc(.vmovsd, RegisterOrMemory.mem(.qword_ptr, .{
return lowerToMvEnc(tag, RegisterOrMemory.mem(Memory.PtrSize.new(ops.reg1.size()), .{
.disp = imm,
.base = ops.reg1,
}), ops.reg2, emit.code);
},
0b10 => {
return lowerToRvmEnc(
.vmovsd,
ops.reg1,
ops.reg1,
RegisterOrMemory.reg(ops.reg2),
emit.code,
);
return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
},
else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
}
}
fn mirAddF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const tag = emit.mir.instructions.items(.tag)[inst];
assert(tag == .add_f64);
fn mirAddFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = emit.mir.instructions.items(.ops)[inst].decode();
switch (ops.flags) {
0b00 => {
return lowerToRvmEnc(
.vaddsd,
ops.reg1,
ops.reg1,
RegisterOrMemory.reg(ops.reg2),
emit.code,
);
return lowerToRvmEnc(tag, ops.reg1, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
},
else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
}
}
fn mirCmpF64(emit: *Emit, inst: Mir.Inst.Index) InnerError!void {
const tag = emit.mir.instructions.items(.tag)[inst];
assert(tag == .cmp_f64);
fn mirCmpFloatAvx(emit: *Emit, tag: Tag, inst: Mir.Inst.Index) InnerError!void {
const ops = emit.mir.instructions.items(.ops)[inst].decode();
switch (ops.flags) {
0b00 => {
return lowerToVmEnc(
.vucomisd,
ops.reg1,
RegisterOrMemory.reg(ops.reg2),
emit.code,
);
return lowerToVmEnc(tag, ops.reg1, RegisterOrMemory.reg(ops.reg2), emit.code);
},
else => return emit.fail("TODO unused variant 0b{b} for mov_f64", .{ops.flags}),
}
@ -1268,16 +1248,24 @@ const Tag = enum {
cmovb,
cmovnae,
vmovsd,
vmovss,
vaddsd,
vaddss,
vcmpsd,
vcmpss,
vucomisd,
vucomiss,
fn isAvx(tag: Tag) bool {
return switch (tag) {
.vmovsd,
.vmovss,
.vaddsd,
.vaddss,
.vcmpsd,
.vcmpss,
.vucomisd,
.vucomiss,
=> true,
else => false,
@ -1406,7 +1394,7 @@ const OpCode = union(enum) {
}
};
inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) OpCode {
switch (enc) {
.zo => return switch (tag) {
.ret_near => OpCode.oneByte(0xc3),
@ -1416,7 +1404,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.syscall => OpCode.twoByte(0x0f, 0x05),
.cbw => OpCode.oneByte(0x98),
.cwd, .cdq, .cqo => OpCode.oneByte(0x99),
else => null,
else => unreachable,
},
.d => return switch (tag) {
.jmp_near => OpCode.oneByte(0xe9),
@ -1437,7 +1425,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.jge, .jnl => if (is_one_byte) OpCode.oneByte(0x7d) else OpCode.twoByte(0x0f, 0x8d),
.jle, .jng => if (is_one_byte) OpCode.oneByte(0x7e) else OpCode.twoByte(0x0f, 0x8e),
.jg, .jnle => if (is_one_byte) OpCode.oneByte(0x7f) else OpCode.twoByte(0x0f, 0x8f),
else => null,
else => unreachable,
},
.m => return switch (tag) {
.jmp_near, .call_near, .push => OpCode.oneByte(0xff),
@ -1464,38 +1452,38 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.fisttp64 => OpCode.oneByte(0xdd),
.fld32 => OpCode.oneByte(0xd9),
.fld64 => OpCode.oneByte(0xdd),
else => null,
else => unreachable,
},
.o => return switch (tag) {
.push => OpCode.oneByte(0x50),
.pop => OpCode.oneByte(0x58),
else => null,
else => unreachable,
},
.i => return switch (tag) {
.push => OpCode.oneByte(if (is_one_byte) 0x6a else 0x68),
.@"test" => OpCode.oneByte(if (is_one_byte) 0xa8 else 0xa9),
.ret_near => OpCode.oneByte(0xc2),
.ret_far => OpCode.oneByte(0xca),
else => null,
else => unreachable,
},
.m1 => return switch (tag) {
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd0 else 0xd1),
else => null,
else => unreachable,
},
.mc => return switch (tag) {
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xd2 else 0xd3),
else => null,
else => unreachable,
},
.mi => return switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(if (is_one_byte) 0x80 else 0x81),
.mov => OpCode.oneByte(if (is_one_byte) 0xc6 else 0xc7),
.@"test" => OpCode.oneByte(if (is_one_byte) 0xf6 else 0xf7),
else => null,
else => unreachable,
},
.mi8 => return switch (tag) {
.adc, .add, .sub, .xor, .@"and", .@"or", .sbb, .cmp => OpCode.oneByte(0x83),
.shl, .sal, .shr, .sar => OpCode.oneByte(if (is_one_byte) 0xc0 else 0xc1),
else => null,
else => unreachable,
},
.mr => return switch (tag) {
.adc => OpCode.oneByte(if (is_one_byte) 0x10 else 0x11),
@ -1508,8 +1496,7 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.cmp => OpCode.oneByte(if (is_one_byte) 0x38 else 0x39),
.mov => OpCode.oneByte(if (is_one_byte) 0x88 else 0x89),
.@"test" => OpCode.oneByte(if (is_one_byte) 0x84 else 0x85),
.vmovsd => OpCode.oneByte(0x11),
else => null,
else => unreachable,
},
.rm => return switch (tag) {
.adc => OpCode.oneByte(if (is_one_byte) 0x12 else 0x13),
@ -1529,48 +1516,46 @@ inline fn getOpCode(tag: Tag, enc: Encoding, is_one_byte: bool) ?OpCode {
.cmove, .cmovz => OpCode.twoByte(0x0f, 0x44),
.cmovb, .cmovnae => OpCode.twoByte(0x0f, 0x42),
.cmovl, .cmovng => OpCode.twoByte(0x0f, 0x4c),
.vmovsd => OpCode.oneByte(0x10),
.vucomisd => OpCode.oneByte(0x2e),
else => null,
else => unreachable,
},
.oi => return switch (tag) {
.mov => OpCode.oneByte(if (is_one_byte) 0xb0 else 0xb8),
else => null,
else => unreachable,
},
.fd => return switch (tag) {
.mov => OpCode.oneByte(if (is_one_byte) 0xa0 else 0xa1),
else => null,
else => unreachable,
},
.td => return switch (tag) {
.mov => OpCode.oneByte(if (is_one_byte) 0xa2 else 0xa3),
else => null,
else => unreachable,
},
.rmi => return switch (tag) {
.imul => OpCode.oneByte(if (is_one_byte) 0x6b else 0x69),
else => null,
else => unreachable,
},
.mv => return switch (tag) {
.vmovsd => OpCode.oneByte(0x11),
else => null,
.vmovsd, .vmovss => OpCode.oneByte(0x11),
else => unreachable,
},
.vm => return switch (tag) {
.vmovsd => OpCode.oneByte(0x10),
.vucomisd => OpCode.oneByte(0x2e),
else => null,
.vmovsd, .vmovss => OpCode.oneByte(0x10),
.vucomisd, .vucomiss => OpCode.oneByte(0x2e),
else => unreachable,
},
.rvm => return switch (tag) {
.vaddsd => OpCode.oneByte(0x58),
.vmovsd => OpCode.oneByte(0x10),
else => null,
.vaddsd, .vaddss => OpCode.oneByte(0x58),
.vmovsd, .vmovss => OpCode.oneByte(0x10),
else => unreachable,
},
.rvmi => return switch (tag) {
.vcmpsd => OpCode.oneByte(0xc2),
else => null,
.vcmpsd, .vcmpss => OpCode.oneByte(0xc2),
else => unreachable,
},
}
}
inline fn getModRmExt(tag: Tag) ?u3 {
inline fn getModRmExt(tag: Tag) u3 {
return switch (tag) {
.adc => 0x2,
.add => 0x0,
@ -1631,11 +1616,11 @@ inline fn getModRmExt(tag: Tag) ?u3 {
.fisttp64 => 0x1,
.fld32 => 0x0,
.fld64 => 0x0,
else => null,
else => unreachable,
};
}
const VexPrefix = struct {
const VexEncoding = struct {
prefix: Encoder.Vex,
reg: ?enum {
ndd,
@ -1644,7 +1629,7 @@ const VexPrefix = struct {
},
};
inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
inline fn getVexEncoding(tag: Tag, enc: Encoding) VexEncoding {
const desc: struct {
reg: enum {
none,
@ -1671,21 +1656,27 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
switch (enc) {
.mv => switch (tag) {
.vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
else => return null,
.vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true },
else => unreachable,
},
.vm => switch (tag) {
.vmovsd => break :blk .{ .lig = true, .simd_prefix = .p_f2, .wig = true },
.vmovss => break :blk .{ .lig = true, .simd_prefix = .p_f3, .wig = true },
.vucomisd => break :blk .{ .lig = true, .simd_prefix = .p_66, .wig = true },
else => return null,
.vucomiss => break :blk .{ .lig = true, .wig = true },
else => unreachable,
},
.rvm => switch (tag) {
.vaddsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
.vaddss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
.vmovsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
else => return null,
.vmovss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
else => unreachable,
},
.rvmi => switch (tag) {
.vcmpsd => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f2, .wig = true },
else => return null,
.vcmpss => break :blk .{ .reg = .nds, .lig = true, .simd_prefix = .p_f3, .wig = true },
else => unreachable,
},
else => unreachable,
}
@ -1711,7 +1702,7 @@ inline fn getVexPrefix(tag: Tag, enc: Encoding) ?VexPrefix {
.p_f3 => vex.simd_prefix_f3(),
}
return VexPrefix{ .prefix = vex, .reg = switch (desc.reg) {
return VexEncoding{ .prefix = vex, .reg = switch (desc.reg) {
.none => null,
.nds => .nds,
.dds => .dds,
@ -1862,7 +1853,7 @@ const RegisterOrMemory = union(enum) {
fn lowerToZoEnc(tag: Tag, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .zo, false).?;
const opc = getOpCode(tag, .zo, false);
const encoder = try Encoder.init(code, 2);
switch (tag) {
.cqo => {
@ -1879,12 +1870,12 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
if (tag == .ret_far or tag == .ret_near) {
const encoder = try Encoder.init(code, 3);
const opc = getOpCode(tag, .i, false).?;
const opc = getOpCode(tag, .i, false);
opc.encode(encoder);
encoder.imm16(@bitCast(i16, @truncate(u16, imm)));
return;
}
const opc = getOpCode(tag, .i, immOpSize(imm) == 8).?;
const opc = getOpCode(tag, .i, immOpSize(imm) == 8);
const encoder = try Encoder.init(code, 5);
if (immOpSize(imm) == 16) {
encoder.prefix16BitMode();
@ -1895,7 +1886,7 @@ fn lowerToIEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .o, false).?;
const opc = getOpCode(tag, .o, false);
const encoder = try Encoder.init(code, 3);
if (reg.size() == 16) {
encoder.prefix16BitMode();
@ -1909,7 +1900,7 @@ fn lowerToOEnc(tag: Tag, reg: Register, code: *std.ArrayList(u8)) InnerError!voi
fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .d, false).?;
const opc = getOpCode(tag, .d, false);
const encoder = try Encoder.init(code, 6);
opc.encode(encoder);
encoder.imm32(@bitCast(i32, imm));
@ -1917,8 +1908,8 @@ fn lowerToDEnc(tag: Tag, imm: u32, code: *std.ArrayList(u8)) InnerError!void {
fn lowerToMxEnc(tag: Tag, reg_or_mem: RegisterOrMemory, enc: Encoding, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
const modrm_ext = getModRmExt(tag).?;
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8);
const modrm_ext = getModRmExt(tag);
switch (reg_or_mem) {
.register => |reg| {
const encoder = try Encoder.init(code, 4);
@ -1973,10 +1964,7 @@ fn lowerToFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8)) I
fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8), td: bool) InnerError!void {
assert(!tag.isAvx());
const opc = if (td)
getOpCode(tag, .td, reg.size() == 8).?
else
getOpCode(tag, .fd, reg.size() == 8).?;
const opc = if (td) getOpCode(tag, .td, reg.size() == 8) else getOpCode(tag, .fd, reg.size() == 8);
const encoder = try Encoder.init(code, 10);
if (reg.size() == 16) {
encoder.prefix16BitMode();
@ -1996,7 +1984,7 @@ fn lowerToTdFdEnc(tag: Tag, reg: Register, moffs: u64, code: *std.ArrayList(u8),
fn lowerToOiEnc(tag: Tag, reg: Register, imm: u64, code: *std.ArrayList(u8)) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .oi, reg.size() == 8).?;
const opc = getOpCode(tag, .oi, reg.size() == 8);
const encoder = try Encoder.init(code, 10);
if (reg.size() == 16) {
encoder.prefix16BitMode();
@ -2023,8 +2011,8 @@ fn lowerToMiXEnc(
code: *std.ArrayList(u8),
) InnerError!void {
assert(!tag.isAvx());
const modrm_ext = getModRmExt(tag).?;
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8).?;
const modrm_ext = getModRmExt(tag);
const opc = getOpCode(tag, enc, reg_or_mem.size() == 8);
switch (reg_or_mem) {
.register => |dst_reg| {
const encoder = try Encoder.init(code, 7);
@ -2079,7 +2067,7 @@ fn lowerToRmEnc(
code: *std.ArrayList(u8),
) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8).?;
const opc = getOpCode(tag, .rm, reg.size() == 8 or reg_or_mem.size() == 8);
switch (reg_or_mem) {
.register => |src_reg| {
const encoder = try Encoder.init(code, 5);
@ -2126,7 +2114,7 @@ fn lowerToMrEnc(
code: *std.ArrayList(u8),
) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8).?;
const opc = getOpCode(tag, .mr, reg.size() == 8 or reg_or_mem.size() == 8);
switch (reg_or_mem) {
.register => |dst_reg| {
const encoder = try Encoder.init(code, 4);
@ -2172,7 +2160,7 @@ fn lowerToRmiEnc(
code: *std.ArrayList(u8),
) InnerError!void {
assert(!tag.isAvx());
const opc = getOpCode(tag, .rmi, false).?;
const opc = getOpCode(tag, .rmi, false);
const encoder = try Encoder.init(code, 13);
if (reg.size() == 16) {
encoder.prefix16BitMode();
@ -2216,9 +2204,9 @@ fn lowerToVmEnc(
reg_or_mem: RegisterOrMemory,
code: *std.ArrayList(u8),
) InnerError!void {
const opc = getOpCode(tag, .vm, false).?;
var vex_prefix = getVexPrefix(tag, .vm).?;
const vex = &vex_prefix.prefix;
const opc = getOpCode(tag, .vm, false);
var enc = getVexEncoding(tag, .vm);
const vex = &enc.prefix;
switch (reg_or_mem) {
.register => |src_reg| {
const encoder = try Encoder.init(code, 5);
@ -2226,12 +2214,11 @@ fn lowerToVmEnc(
.r = reg.isExtended(),
.b = src_reg.isExtended(),
});
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
encoder.modRm_direct(reg.lowEnc(), src_reg.lowEnc());
},
.memory => |src_mem| {
assert(src_mem.ptr_size == .qword_ptr);
const encoder = try Encoder.init(code, 10);
if (src_mem.base) |base| {
vex.rex(.{
@ -2243,7 +2230,7 @@ fn lowerToVmEnc(
.r = reg.isExtended(),
});
}
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
src_mem.encode(encoder, reg.lowEnc());
},
@ -2257,9 +2244,9 @@ fn lowerToMvEnc(
reg: Register,
code: *std.ArrayList(u8),
) InnerError!void {
const opc = getOpCode(tag, .mv, false).?;
var vex_prefix = getVexPrefix(tag, .mv).?;
const vex = &vex_prefix.prefix;
const opc = getOpCode(tag, .mv, false);
var enc = getVexEncoding(tag, .mv);
const vex = &enc.prefix;
switch (reg_or_mem) {
.register => |dst_reg| {
const encoder = try Encoder.init(code, 4);
@ -2267,12 +2254,11 @@ fn lowerToMvEnc(
.r = reg.isExtended(),
.b = dst_reg.isExtended(),
});
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
encoder.modRm_direct(reg.lowEnc(), dst_reg.lowEnc());
},
.memory => |dst_mem| {
assert(dst_mem.ptr_size == .qword_ptr);
const encoder = try Encoder.init(code, 10);
if (dst_mem.base) |base| {
vex.rex(.{
@ -2284,7 +2270,7 @@ fn lowerToMvEnc(
.r = reg.isExtended(),
});
}
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
dst_mem.encode(encoder, reg.lowEnc());
},
@ -2298,12 +2284,12 @@ fn lowerToRvmEnc(
reg_or_mem: RegisterOrMemory,
code: *std.ArrayList(u8),
) InnerError!void {
const opc = getOpCode(tag, .rvm, false).?;
var vex_prefix = getVexPrefix(tag, .rvm).?;
const vex = &vex_prefix.prefix;
const opc = getOpCode(tag, .rvm, false);
var enc = getVexEncoding(tag, .rvm);
const vex = &enc.prefix;
switch (reg_or_mem) {
.register => |reg3| {
if (vex_prefix.reg) |vvvv| {
if (enc.reg) |vvvv| {
switch (vvvv) {
.nds => vex.reg(reg2.enc()),
else => unreachable, // TODO
@ -2314,7 +2300,7 @@ fn lowerToRvmEnc(
.r = reg1.isExtended(),
.b = reg3.isExtended(),
});
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
},
@ -2333,13 +2319,13 @@ fn lowerToRvmiEnc(
imm: u32,
code: *std.ArrayList(u8),
) InnerError!void {
const opc = getOpCode(tag, .rvmi, false).?;
var vex_prefix = getVexPrefix(tag, .rvmi).?;
const vex = &vex_prefix.prefix;
const opc = getOpCode(tag, .rvmi, false);
var enc = getVexEncoding(tag, .rvmi);
const vex = &enc.prefix;
const encoder: Encoder = blk: {
switch (reg_or_mem) {
.register => |reg3| {
if (vex_prefix.reg) |vvvv| {
if (enc.reg) |vvvv| {
switch (vvvv) {
.nds => vex.reg(reg2.enc()),
else => unreachable, // TODO
@ -2350,7 +2336,7 @@ fn lowerToRvmiEnc(
.r = reg1.isExtended(),
.b = reg3.isExtended(),
});
encoder.vex(vex_prefix.prefix);
encoder.vex(enc.prefix);
opc.encode(encoder);
encoder.modRm_direct(reg1.lowEnc(), reg3.lowEnc());
break :blk encoder;

View File

@ -351,14 +351,17 @@ pub const Inst = struct {
/// 0b01 qword ptr [reg1 + imm32], reg2
/// 0b10 reg1, reg2
mov_f64,
mov_f32,
/// ops flags: form:
/// 0b00 reg1, reg1, reg2
add_f64,
add_f32,
/// ops flags: form:
///
cmp_f64,
cmp_f32,
/// Pseudo-instructions
/// call extern function

View File

@ -1,163 +1,163 @@
const builtin = @import("builtin");
test {
_ = @import("behavior/align.zig");
_ = @import("behavior/alignof.zig");
_ = @import("behavior/array.zig");
_ = @import("behavior/async_fn.zig");
_ = @import("behavior/atomics.zig");
_ = @import("behavior/await_struct.zig");
// _ = @import("behavior/align.zig");
// _ = @import("behavior/alignof.zig");
// _ = @import("behavior/array.zig");
// _ = @import("behavior/async_fn.zig");
// _ = @import("behavior/atomics.zig");
// _ = @import("behavior/await_struct.zig");
_ = @import("behavior/basic.zig");
_ = @import("behavior/bit_shifting.zig");
_ = @import("behavior/bitcast.zig");
_ = @import("behavior/bitreverse.zig");
_ = @import("behavior/bool.zig");
_ = @import("behavior/bugs/394.zig");
_ = @import("behavior/bugs/421.zig");
_ = @import("behavior/bugs/529.zig");
_ = @import("behavior/bugs/624.zig");
_ = @import("behavior/bugs/655.zig");
_ = @import("behavior/bugs/656.zig");
_ = @import("behavior/bugs/679.zig");
_ = @import("behavior/bugs/704.zig");
_ = @import("behavior/bugs/718.zig");
_ = @import("behavior/bugs/726.zig");
_ = @import("behavior/bugs/828.zig");
_ = @import("behavior/bugs/920.zig");
_ = @import("behavior/bugs/1025.zig");
_ = @import("behavior/bugs/1076.zig");
_ = @import("behavior/bugs/1111.zig");
_ = @import("behavior/bugs/1120.zig");
_ = @import("behavior/bugs/1277.zig");
_ = @import("behavior/bugs/1310.zig");
_ = @import("behavior/bugs/1381.zig");
_ = @import("behavior/bugs/1421.zig");
_ = @import("behavior/bugs/1442.zig");
_ = @import("behavior/bugs/1486.zig");
_ = @import("behavior/bugs/1500.zig");
_ = @import("behavior/bugs/1607.zig");
_ = @import("behavior/bugs/1735.zig");
_ = @import("behavior/bugs/1741.zig");
_ = @import("behavior/bugs/1851.zig");
_ = @import("behavior/bugs/1914.zig");
_ = @import("behavior/bugs/2006.zig");
_ = @import("behavior/bugs/2114.zig");
_ = @import("behavior/bugs/2346.zig");
_ = @import("behavior/bugs/2578.zig");
_ = @import("behavior/bugs/2692.zig");
_ = @import("behavior/bugs/2889.zig");
_ = @import("behavior/bugs/3007.zig");
_ = @import("behavior/bugs/3046.zig");
_ = @import("behavior/bugs/3112.zig");
_ = @import("behavior/bugs/3367.zig");
_ = @import("behavior/bugs/3384.zig");
_ = @import("behavior/bugs/3586.zig");
_ = @import("behavior/bugs/3742.zig");
_ = @import("behavior/bugs/3779.zig");
_ = @import("behavior/bugs/4328.zig");
_ = @import("behavior/bugs/4560.zig");
_ = @import("behavior/bugs/4769_a.zig");
_ = @import("behavior/bugs/4769_b.zig");
_ = @import("behavior/bugs/4954.zig");
_ = @import("behavior/bugs/5398.zig");
_ = @import("behavior/bugs/5413.zig");
_ = @import("behavior/bugs/5474.zig");
_ = @import("behavior/bugs/5487.zig");
_ = @import("behavior/bugs/6456.zig");
_ = @import("behavior/bugs/6781.zig");
_ = @import("behavior/bugs/6850.zig");
_ = @import("behavior/bugs/7003.zig");
_ = @import("behavior/bugs/7027.zig");
_ = @import("behavior/bugs/7047.zig");
_ = @import("behavior/bugs/7187.zig");
_ = @import("behavior/bugs/7250.zig");
_ = @import("behavior/bugs/9584.zig");
_ = @import("behavior/bugs/10138.zig");
_ = @import("behavior/bugs/10147.zig");
_ = @import("behavior/bugs/10970.zig");
_ = @import("behavior/bugs/11046.zig");
_ = @import("behavior/bugs/11100.zig");
_ = @import("behavior/bugs/11139.zig");
_ = @import("behavior/bugs/11159.zig");
_ = @import("behavior/bugs/11162.zig");
_ = @import("behavior/bugs/11165.zig");
_ = @import("behavior/bugs/11181.zig");
_ = @import("behavior/bugs/11182.zig");
_ = @import("behavior/bugs/11213.zig");
_ = @import("behavior/byteswap.zig");
_ = @import("behavior/byval_arg_var.zig");
_ = @import("behavior/call.zig");
_ = @import("behavior/cast.zig");
_ = @import("behavior/cast_int.zig");
_ = @import("behavior/comptime_memory.zig");
_ = @import("behavior/const_slice_child.zig");
_ = @import("behavior/defer.zig");
_ = @import("behavior/enum.zig");
_ = @import("behavior/error.zig");
_ = @import("behavior/eval.zig");
_ = @import("behavior/field_parent_ptr.zig");
_ = @import("behavior/floatop.zig");
_ = @import("behavior/fn.zig");
_ = @import("behavior/fn_delegation.zig");
_ = @import("behavior/fn_in_struct_in_comptime.zig");
_ = @import("behavior/for.zig");
_ = @import("behavior/generics.zig");
_ = @import("behavior/hasdecl.zig");
_ = @import("behavior/hasfield.zig");
_ = @import("behavior/if.zig");
_ = @import("behavior/import.zig");
_ = @import("behavior/incomplete_struct_param_tld.zig");
_ = @import("behavior/int128.zig");
_ = @import("behavior/int_div.zig");
_ = @import("behavior/inttoptr.zig");
_ = @import("behavior/ir_block_deps.zig");
_ = @import("behavior/math.zig");
_ = @import("behavior/maximum_minimum.zig");
_ = @import("behavior/member_func.zig");
_ = @import("behavior/merge_error_sets.zig");
_ = @import("behavior/muladd.zig");
_ = @import("behavior/namespace_depends_on_compile_var.zig");
_ = @import("behavior/null.zig");
_ = @import("behavior/optional.zig");
_ = @import("behavior/pointers.zig");
_ = @import("behavior/popcount.zig");
_ = @import("behavior/prefetch.zig");
_ = @import("behavior/ptrcast.zig");
_ = @import("behavior/pub_enum.zig");
_ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
_ = @import("behavior/reflection.zig");
_ = @import("behavior/saturating_arithmetic.zig");
_ = @import("behavior/select.zig");
_ = @import("behavior/shuffle.zig");
_ = @import("behavior/sizeof_and_typeof.zig");
_ = @import("behavior/slice.zig");
_ = @import("behavior/slice_sentinel_comptime.zig");
_ = @import("behavior/src.zig");
_ = @import("behavior/struct.zig");
_ = @import("behavior/packed-struct.zig");
_ = @import("behavior/struct_contains_null_ptr_itself.zig");
_ = @import("behavior/struct_contains_slice_of_itself.zig");
_ = @import("behavior/switch.zig");
_ = @import("behavior/switch_prong_err_enum.zig");
_ = @import("behavior/switch_prong_implicit_cast.zig");
_ = @import("behavior/this.zig");
_ = @import("behavior/translate_c_macros.zig");
_ = @import("behavior/truncate.zig");
_ = @import("behavior/try.zig");
_ = @import("behavior/tuple.zig");
_ = @import("behavior/type.zig");
_ = @import("behavior/type_info.zig");
_ = @import("behavior/typename.zig");
_ = @import("behavior/undefined.zig");
_ = @import("behavior/underscore.zig");
_ = @import("behavior/union.zig");
_ = @import("behavior/union_with_members.zig");
_ = @import("behavior/usingnamespace.zig");
_ = @import("behavior/var_args.zig");
_ = @import("behavior/vector.zig");
_ = @import("behavior/void.zig");
_ = @import("behavior/while.zig");
_ = @import("behavior/widening.zig");
// _ = @import("behavior/bit_shifting.zig");
// _ = @import("behavior/bitcast.zig");
// _ = @import("behavior/bitreverse.zig");
// _ = @import("behavior/bool.zig");
// _ = @import("behavior/bugs/394.zig");
// _ = @import("behavior/bugs/421.zig");
// _ = @import("behavior/bugs/529.zig");
// _ = @import("behavior/bugs/624.zig");
// _ = @import("behavior/bugs/655.zig");
// _ = @import("behavior/bugs/656.zig");
// _ = @import("behavior/bugs/679.zig");
// _ = @import("behavior/bugs/704.zig");
// _ = @import("behavior/bugs/718.zig");
// _ = @import("behavior/bugs/726.zig");
// _ = @import("behavior/bugs/828.zig");
// _ = @import("behavior/bugs/920.zig");
// _ = @import("behavior/bugs/1025.zig");
// _ = @import("behavior/bugs/1076.zig");
// _ = @import("behavior/bugs/1111.zig");
// _ = @import("behavior/bugs/1120.zig");
// _ = @import("behavior/bugs/1277.zig");
// _ = @import("behavior/bugs/1310.zig");
// _ = @import("behavior/bugs/1381.zig");
// _ = @import("behavior/bugs/1421.zig");
// _ = @import("behavior/bugs/1442.zig");
// _ = @import("behavior/bugs/1486.zig");
// _ = @import("behavior/bugs/1500.zig");
// _ = @import("behavior/bugs/1607.zig");
// _ = @import("behavior/bugs/1735.zig");
// _ = @import("behavior/bugs/1741.zig");
// _ = @import("behavior/bugs/1851.zig");
// _ = @import("behavior/bugs/1914.zig");
// _ = @import("behavior/bugs/2006.zig");
// _ = @import("behavior/bugs/2114.zig");
// _ = @import("behavior/bugs/2346.zig");
// _ = @import("behavior/bugs/2578.zig");
// _ = @import("behavior/bugs/2692.zig");
// _ = @import("behavior/bugs/2889.zig");
// _ = @import("behavior/bugs/3007.zig");
// _ = @import("behavior/bugs/3046.zig");
// _ = @import("behavior/bugs/3112.zig");
// _ = @import("behavior/bugs/3367.zig");
// _ = @import("behavior/bugs/3384.zig");
// _ = @import("behavior/bugs/3586.zig");
// _ = @import("behavior/bugs/3742.zig");
// _ = @import("behavior/bugs/3779.zig");
// _ = @import("behavior/bugs/4328.zig");
// _ = @import("behavior/bugs/4560.zig");
// _ = @import("behavior/bugs/4769_a.zig");
// _ = @import("behavior/bugs/4769_b.zig");
// _ = @import("behavior/bugs/4954.zig");
// _ = @import("behavior/bugs/5398.zig");
// _ = @import("behavior/bugs/5413.zig");
// _ = @import("behavior/bugs/5474.zig");
// _ = @import("behavior/bugs/5487.zig");
// _ = @import("behavior/bugs/6456.zig");
// _ = @import("behavior/bugs/6781.zig");
// _ = @import("behavior/bugs/6850.zig");
// _ = @import("behavior/bugs/7003.zig");
// _ = @import("behavior/bugs/7027.zig");
// _ = @import("behavior/bugs/7047.zig");
// _ = @import("behavior/bugs/7187.zig");
// _ = @import("behavior/bugs/7250.zig");
// _ = @import("behavior/bugs/9584.zig");
// _ = @import("behavior/bugs/10138.zig");
// _ = @import("behavior/bugs/10147.zig");
// _ = @import("behavior/bugs/10970.zig");
// _ = @import("behavior/bugs/11046.zig");
// _ = @import("behavior/bugs/11100.zig");
// _ = @import("behavior/bugs/11139.zig");
// _ = @import("behavior/bugs/11159.zig");
// _ = @import("behavior/bugs/11162.zig");
// _ = @import("behavior/bugs/11165.zig");
// _ = @import("behavior/bugs/11181.zig");
// _ = @import("behavior/bugs/11182.zig");
// _ = @import("behavior/bugs/11213.zig");
// _ = @import("behavior/byteswap.zig");
// _ = @import("behavior/byval_arg_var.zig");
// _ = @import("behavior/call.zig");
// _ = @import("behavior/cast.zig");
// _ = @import("behavior/cast_int.zig");
// _ = @import("behavior/comptime_memory.zig");
// _ = @import("behavior/const_slice_child.zig");
// _ = @import("behavior/defer.zig");
// _ = @import("behavior/enum.zig");
// _ = @import("behavior/error.zig");
// _ = @import("behavior/eval.zig");
// _ = @import("behavior/field_parent_ptr.zig");
// _ = @import("behavior/floatop.zig");
// _ = @import("behavior/fn.zig");
// _ = @import("behavior/fn_delegation.zig");
// _ = @import("behavior/fn_in_struct_in_comptime.zig");
// _ = @import("behavior/for.zig");
// _ = @import("behavior/generics.zig");
// _ = @import("behavior/hasdecl.zig");
// _ = @import("behavior/hasfield.zig");
// _ = @import("behavior/if.zig");
// _ = @import("behavior/import.zig");
// _ = @import("behavior/incomplete_struct_param_tld.zig");
// _ = @import("behavior/int128.zig");
// _ = @import("behavior/int_div.zig");
// _ = @import("behavior/inttoptr.zig");
// _ = @import("behavior/ir_block_deps.zig");
// _ = @import("behavior/math.zig");
// _ = @import("behavior/maximum_minimum.zig");
// _ = @import("behavior/member_func.zig");
// _ = @import("behavior/merge_error_sets.zig");
// _ = @import("behavior/muladd.zig");
// _ = @import("behavior/namespace_depends_on_compile_var.zig");
// _ = @import("behavior/null.zig");
// _ = @import("behavior/optional.zig");
// _ = @import("behavior/pointers.zig");
// _ = @import("behavior/popcount.zig");
// _ = @import("behavior/prefetch.zig");
// _ = @import("behavior/ptrcast.zig");
// _ = @import("behavior/pub_enum.zig");
// _ = @import("behavior/ref_var_in_if_after_if_2nd_switch_prong.zig");
// _ = @import("behavior/reflection.zig");
// _ = @import("behavior/saturating_arithmetic.zig");
// _ = @import("behavior/select.zig");
// _ = @import("behavior/shuffle.zig");
// _ = @import("behavior/sizeof_and_typeof.zig");
// _ = @import("behavior/slice.zig");
// _ = @import("behavior/slice_sentinel_comptime.zig");
// _ = @import("behavior/src.zig");
// _ = @import("behavior/struct.zig");
// _ = @import("behavior/packed-struct.zig");
// _ = @import("behavior/struct_contains_null_ptr_itself.zig");
// _ = @import("behavior/struct_contains_slice_of_itself.zig");
// _ = @import("behavior/switch.zig");
// _ = @import("behavior/switch_prong_err_enum.zig");
// _ = @import("behavior/switch_prong_implicit_cast.zig");
// _ = @import("behavior/this.zig");
// _ = @import("behavior/translate_c_macros.zig");
// _ = @import("behavior/truncate.zig");
// _ = @import("behavior/try.zig");
// _ = @import("behavior/tuple.zig");
// _ = @import("behavior/type.zig");
// _ = @import("behavior/type_info.zig");
// _ = @import("behavior/typename.zig");
// _ = @import("behavior/undefined.zig");
// _ = @import("behavior/underscore.zig");
// _ = @import("behavior/union.zig");
// _ = @import("behavior/union_with_members.zig");
// _ = @import("behavior/usingnamespace.zig");
// _ = @import("behavior/var_args.zig");
// _ = @import("behavior/vector.zig");
// _ = @import("behavior/void.zig");
// _ = @import("behavior/while.zig");
// _ = @import("behavior/widening.zig");
if (builtin.stage2_arch == .wasm32) {
_ = @import("behavior/wasm.zig");