[ia32] Introduce vex prefix version of float64 arithmetic binop

port 50c4d8826b

BUG=

Review URL: https://codereview.chromium.org/770183002

Cr-Commit-Position: refs/heads/master@{#25595}
This commit is contained in:
weiliang.lin 2014-12-02 00:09:39 -08:00 committed by Commit bot
parent 34874b98b3
commit 2ad1c224b8
9 changed files with 333 additions and 61 deletions

View File

@ -359,7 +359,7 @@ CPU::CPU()
has_ssse3_ = (cpu_info[2] & 0x00000200) != 0; has_ssse3_ = (cpu_info[2] & 0x00000200) != 0;
has_sse41_ = (cpu_info[2] & 0x00080000) != 0; has_sse41_ = (cpu_info[2] & 0x00080000) != 0;
has_sse42_ = (cpu_info[2] & 0x00100000) != 0; has_sse42_ = (cpu_info[2] & 0x00100000) != 0;
has_avx_ = (cpu_info[2] & 0x18000000) != 0; has_avx_ = (cpu_info[2] & 0x10000000) != 0;
if (has_avx_) has_fma3_ = (cpu_info[2] & 0x00001000) != 0; if (has_avx_) has_fma3_ = (cpu_info[2] & 0x00001000) != 0;
} }

View File

@ -492,6 +492,30 @@ void CodeGenerator::AssembleArchInstruction(Instruction* instr) {
case kSSEUint32ToFloat64: case kSSEUint32ToFloat64:
__ LoadUint32(i.OutputDoubleRegister(), i.InputOperand(0)); __ LoadUint32(i.OutputDoubleRegister(), i.InputOperand(0));
break; break;
case kAVXFloat64Add: {
CpuFeatureScope avx_scope(masm(), AVX);
__ vaddsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputOperand(1));
break;
}
case kAVXFloat64Sub: {
CpuFeatureScope avx_scope(masm(), AVX);
__ vsubsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputOperand(1));
break;
}
case kAVXFloat64Mul: {
CpuFeatureScope avx_scope(masm(), AVX);
__ vmulsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputOperand(1));
break;
}
case kAVXFloat64Div: {
CpuFeatureScope avx_scope(masm(), AVX);
__ vdivsd(i.OutputDoubleRegister(), i.InputDoubleRegister(0),
i.InputOperand(1));
break;
}
case kIA32Movsxbl: case kIA32Movsxbl:
__ movsx_b(i.OutputRegister(), i.MemoryOperand()); __ movsx_b(i.OutputRegister(), i.MemoryOperand());
break; break;

View File

@ -46,6 +46,10 @@ namespace compiler {
V(SSEFloat64ToUint32) \ V(SSEFloat64ToUint32) \
V(SSEInt32ToFloat64) \ V(SSEInt32ToFloat64) \
V(SSEUint32ToFloat64) \ V(SSEUint32ToFloat64) \
V(AVXFloat64Add) \
V(AVXFloat64Sub) \
V(AVXFloat64Mul) \
V(AVXFloat64Div) \
V(IA32Movsxbl) \ V(IA32Movsxbl) \
V(IA32Movzxbl) \ V(IA32Movzxbl) \
V(IA32Movb) \ V(IA32Movb) \

View File

@ -873,29 +873,49 @@ void InstructionSelector::VisitTruncateFloat64ToFloat32(Node* node) {
void InstructionSelector::VisitFloat64Add(Node* node) { void InstructionSelector::VisitFloat64Add(Node* node) {
IA32OperandGenerator g(this); IA32OperandGenerator g(this);
Emit(kSSEFloat64Add, g.DefineSameAsFirst(node), if (IsSupported(AVX)) {
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1))); Emit(kAVXFloat64Add, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
} else {
Emit(kSSEFloat64Add, g.DefineSameAsFirst(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
}
} }
void InstructionSelector::VisitFloat64Sub(Node* node) { void InstructionSelector::VisitFloat64Sub(Node* node) {
IA32OperandGenerator g(this); IA32OperandGenerator g(this);
Emit(kSSEFloat64Sub, g.DefineSameAsFirst(node), if (IsSupported(AVX)) {
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1))); Emit(kAVXFloat64Sub, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
} else {
Emit(kSSEFloat64Sub, g.DefineSameAsFirst(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
}
} }
void InstructionSelector::VisitFloat64Mul(Node* node) { void InstructionSelector::VisitFloat64Mul(Node* node) {
IA32OperandGenerator g(this); IA32OperandGenerator g(this);
Emit(kSSEFloat64Mul, g.DefineSameAsFirst(node), if (IsSupported(AVX)) {
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1))); Emit(kAVXFloat64Mul, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
} else {
Emit(kSSEFloat64Mul, g.DefineSameAsFirst(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
}
} }
void InstructionSelector::VisitFloat64Div(Node* node) { void InstructionSelector::VisitFloat64Div(Node* node) {
IA32OperandGenerator g(this); IA32OperandGenerator g(this);
Emit(kSSEFloat64Div, g.DefineSameAsFirst(node), if (IsSupported(AVX)) {
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1))); Emit(kAVXFloat64Div, g.DefineAsRegister(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
} else {
Emit(kSSEFloat64Div, g.DefineSameAsFirst(node),
g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)));
}
} }

View File

@ -60,11 +60,17 @@ void CpuFeatures::ProbeImpl(bool cross_compile) {
if (cpu.has_sse41() && FLAG_enable_sse4_1) supported_ |= 1u << SSE4_1; if (cpu.has_sse41() && FLAG_enable_sse4_1) supported_ |= 1u << SSE4_1;
if (cpu.has_sse3() && FLAG_enable_sse3) supported_ |= 1u << SSE3; if (cpu.has_sse3() && FLAG_enable_sse3) supported_ |= 1u << SSE3;
if (cpu.has_avx() && FLAG_enable_avx) supported_ |= 1u << AVX;
if (cpu.has_fma3() && FLAG_enable_fma3) supported_ |= 1u << FMA3;
} }
void CpuFeatures::PrintTarget() { } void CpuFeatures::PrintTarget() { }
void CpuFeatures::PrintFeatures() { } void CpuFeatures::PrintFeatures() {
printf("SSE3=%d SSE4_1=%d AVX=%d FMA3=%d\n", CpuFeatures::IsSupported(SSE3),
CpuFeatures::IsSupported(SSE4_1), CpuFeatures::IsSupported(AVX),
CpuFeatures::IsSupported(FMA3));
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
@ -2437,6 +2443,16 @@ void Assembler::pinsrd(XMMRegister dst, const Operand& src, int8_t offset) {
} }
void Assembler::vsd(byte op, XMMRegister dst, XMMRegister src1,
const Operand& src2) {
DCHECK(IsEnabled(AVX));
EnsureSpace ensure_space(this);
emit_vex_prefix(src1, kLIG, kF2, k0F, kWIG);
EMIT(op);
emit_sse_operand(dst, src2);
}
void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) { void Assembler::emit_sse_operand(XMMRegister reg, const Operand& adr) {
Register ireg = { reg.code() }; Register ireg = { reg.code() };
emit_operand(ireg, adr); emit_operand(ireg, adr);
@ -2458,6 +2474,19 @@ void Assembler::emit_sse_operand(XMMRegister dst, Register src) {
} }
void Assembler::emit_vex_prefix(XMMRegister vreg, VectorLength l, SIMDPrefix pp,
LeadingOpcode mm, VexW w) {
if (mm != k0F || w != kW0) {
EMIT(0xc4);
EMIT(0xc0 | mm);
EMIT(w | ((~vreg.code() & 0xf) << 3) | l | pp);
} else {
EMIT(0xc5);
EMIT(((~vreg.code()) << 3) | l | pp);
}
}
void Assembler::RecordJSReturn() { void Assembler::RecordJSReturn() {
positions_recorder()->WriteRecordedPositions(); positions_recorder()->WriteRecordedPositions();
EnsureSpace ensure_space(this); EnsureSpace ensure_space(this);

View File

@ -1051,6 +1051,34 @@ class Assembler : public AssemblerBase {
// Parallel XMM operations. // Parallel XMM operations.
void movntdqa(XMMRegister dst, const Operand& src); void movntdqa(XMMRegister dst, const Operand& src);
void movntdq(const Operand& dst, XMMRegister src); void movntdq(const Operand& dst, XMMRegister src);
// AVX instructions
void vaddsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
vaddsd(dst, src1, Operand(src2));
}
void vaddsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
vsd(0x58, dst, src1, src2);
}
void vsubsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
vsubsd(dst, src1, Operand(src2));
}
void vsubsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
vsd(0x5c, dst, src1, src2);
}
void vmulsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
vmulsd(dst, src1, Operand(src2));
}
void vmulsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
vsd(0x59, dst, src1, src2);
}
void vdivsd(XMMRegister dst, XMMRegister src1, XMMRegister src2) {
vdivsd(dst, src1, Operand(src2));
}
void vdivsd(XMMRegister dst, XMMRegister src1, const Operand& src2) {
vsd(0x5e, dst, src1, src2);
}
void vsd(byte op, XMMRegister dst, XMMRegister src1, const Operand& src2);
// Prefetch src position into cache level. // Prefetch src position into cache level.
// Level 1, 2 or 3 specifies CPU cache level. Level 0 specifies a // Level 1, 2 or 3 specifies CPU cache level. Level 0 specifies a
// non-temporal // non-temporal
@ -1154,6 +1182,14 @@ class Assembler : public AssemblerBase {
void emit_farith(int b1, int b2, int i); void emit_farith(int b1, int b2, int i);
// Emit vex prefix
enum SIMDPrefix { kNone = 0x0, k66 = 0x1, kF3 = 0x2, kF2 = 0x3 };
enum VectorLength { kL128 = 0x0, kL256 = 0x4, kLIG = kL128 };
enum VexW { kW0 = 0x0, kW1 = 0x80, kWIG = kW0 };
enum LeadingOpcode { k0F = 0x1, k0F38 = 0x2, k0F3A = 0x2 };
inline void emit_vex_prefix(XMMRegister v, VectorLength l, SIMDPrefix pp,
LeadingOpcode m, VexW w);
// labels // labels
void print(Label* L); void print(Label* L);
void bind_to(Label* L, int pos); void bind_to(Label* L, int pos);

View File

@ -246,6 +246,9 @@ class DisassemblerIA32 {
DisassemblerIA32(const NameConverter& converter, DisassemblerIA32(const NameConverter& converter,
bool abort_on_unimplemented = true) bool abort_on_unimplemented = true)
: converter_(converter), : converter_(converter),
vex_byte0_(0),
vex_byte1_(0),
vex_byte2_(0),
instruction_table_(InstructionTable::get_instance()), instruction_table_(InstructionTable::get_instance()),
tmp_buffer_pos_(0), tmp_buffer_pos_(0),
abort_on_unimplemented_(abort_on_unimplemented) { abort_on_unimplemented_(abort_on_unimplemented) {
@ -260,6 +263,9 @@ class DisassemblerIA32 {
private: private:
const NameConverter& converter_; const NameConverter& converter_;
byte vex_byte0_; // 0xc4 or 0xc5
byte vex_byte1_;
byte vex_byte2_; // only for 3 bytes vex prefix
InstructionTable* instruction_table_; InstructionTable* instruction_table_;
v8::internal::EmbeddedVector<char, 128> tmp_buffer_; v8::internal::EmbeddedVector<char, 128> tmp_buffer_;
unsigned int tmp_buffer_pos_; unsigned int tmp_buffer_pos_;
@ -287,6 +293,57 @@ class DisassemblerIA32 {
kSAR = 7 kSAR = 7
}; };
bool vex_128() {
DCHECK(vex_byte0_ == 0xc4 || vex_byte0_ == 0xc5);
byte checked = vex_byte0_ == 0xc4 ? vex_byte2_ : vex_byte1_;
return (checked & 4) != 1;
}
bool vex_66() {
DCHECK(vex_byte0_ == 0xc4 || vex_byte0_ == 0xc5);
byte checked = vex_byte0_ == 0xc4 ? vex_byte2_ : vex_byte1_;
return (checked & 3) == 1;
}
bool vex_f3() {
DCHECK(vex_byte0_ == 0xc4 || vex_byte0_ == 0xc5);
byte checked = vex_byte0_ == 0xc4 ? vex_byte2_ : vex_byte1_;
return (checked & 3) == 2;
}
bool vex_f2() {
DCHECK(vex_byte0_ == 0xc4 || vex_byte0_ == 0xc5);
byte checked = vex_byte0_ == 0xc4 ? vex_byte2_ : vex_byte1_;
return (checked & 3) == 3;
}
bool vex_w() {
if (vex_byte0_ == 0xc5) return false;
return (vex_byte2_ & 0x80) == 1;
}
bool vex_0f() {
if (vex_byte0_ == 0xc5) return true;
return (vex_byte1_ & 3) == 1;
}
bool vex_0f38() {
if (vex_byte0_ == 0xc5) return false;
return (vex_byte1_ & 3) == 2;
}
bool vex_0f3a() {
if (vex_byte0_ == 0xc5) return false;
return (vex_byte1_ & 3) == 3;
}
int vex_vreg() {
DCHECK(vex_byte0_ == 0xc4 || vex_byte0_ == 0xc5);
byte checked = vex_byte0_ == 0xc4 ? vex_byte2_ : vex_byte1_;
return ~(checked >> 3) & 0xf;
}
char float_size_code() { return "sd"[vex_w()]; }
const char* NameOfCPURegister(int reg) const { const char* NameOfCPURegister(int reg) const {
return converter_.NameOfCPURegister(reg); return converter_.NameOfCPURegister(reg);
@ -340,6 +397,7 @@ class DisassemblerIA32 {
int FPUInstruction(byte* data); int FPUInstruction(byte* data);
int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start); int MemoryFPUInstruction(int escape_opcode, int regop, byte* modrm_start);
int RegisterFPUInstruction(int escape_opcode, byte modrm_byte); int RegisterFPUInstruction(int escape_opcode, byte modrm_byte);
int AVXInstruction(byte* data);
void AppendToBuffer(const char* format, ...); void AppendToBuffer(const char* format, ...);
@ -679,6 +737,44 @@ int DisassemblerIA32::CMov(byte* data) {
} }
int DisassemblerIA32::AVXInstruction(byte* data) {
byte opcode = *data;
byte* current = data + 1;
if (vex_f2() && vex_0f()) {
int mod, regop, rm, vvvv = vex_vreg();
get_modrm(*current, &mod, &regop, &rm);
switch (opcode) {
case 0x58:
AppendToBuffer("vaddsd %s,%s,", NameOfXMMRegister(regop),
NameOfXMMRegister(vvvv));
current += PrintRightXMMOperand(current);
break;
case 0x59:
AppendToBuffer("vmulsd %s,%s,", NameOfXMMRegister(regop),
NameOfXMMRegister(vvvv));
current += PrintRightXMMOperand(current);
break;
case 0x5c:
AppendToBuffer("vsubsd %s,%s,", NameOfXMMRegister(regop),
NameOfXMMRegister(vvvv));
current += PrintRightXMMOperand(current);
break;
case 0x5e:
AppendToBuffer("vdivsd %s,%s,", NameOfXMMRegister(regop),
NameOfXMMRegister(vvvv));
current += PrintRightXMMOperand(current);
break;
default:
UnimplementedInstruction();
}
} else {
UnimplementedInstruction();
}
return static_cast<int>(current - data);
}
// Returns number of bytes used, including *data. // Returns number of bytes used, including *data.
int DisassemblerIA32::FPUInstruction(byte* data) { int DisassemblerIA32::FPUInstruction(byte* data) {
byte escape_opcode = *data; byte escape_opcode = *data;
@ -903,65 +999,81 @@ int DisassemblerIA32::InstructionDecode(v8::internal::Vector<char> out_buffer,
} else if (*data == 0x2E /*cs*/) { } else if (*data == 0x2E /*cs*/) {
branch_hint = "predicted not taken"; branch_hint = "predicted not taken";
data++; data++;
} else if (*data == 0xC4 && *(data + 1) >= 0xc0) {
vex_byte0_ = *data;
vex_byte1_ = *(data + 1);
vex_byte2_ = *(data + 2);
data += 3;
} else if (*data == 0xC5 && *(data + 1) >= 0xc0) {
vex_byte0_ = *data;
vex_byte1_ = *(data + 1);
data += 2;
} }
bool processed = true; // Will be set to false if the current instruction bool processed = true; // Will be set to false if the current instruction
// is not in 'instructions' table. // is not in 'instructions' table.
const InstructionDesc& idesc = instruction_table_->Get(*data); // Decode AVX instructions.
switch (idesc.type) { if (vex_byte0_ != 0) {
case ZERO_OPERANDS_INSTR: data += AVXInstruction(data);
AppendToBuffer(idesc.mnem); } else {
data++; const InstructionDesc& idesc = instruction_table_->Get(*data);
break; switch (idesc.type) {
case ZERO_OPERANDS_INSTR:
AppendToBuffer(idesc.mnem);
data++;
break;
case TWO_OPERANDS_INSTR: case TWO_OPERANDS_INSTR:
data++; data++;
data += PrintOperands(idesc.mnem, idesc.op_order_, data); data += PrintOperands(idesc.mnem, idesc.op_order_, data);
break; break;
case JUMP_CONDITIONAL_SHORT_INSTR: case JUMP_CONDITIONAL_SHORT_INSTR:
data += JumpConditionalShort(data, branch_hint); data += JumpConditionalShort(data, branch_hint);
break; break;
case REGISTER_INSTR: case REGISTER_INSTR:
AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07)); AppendToBuffer("%s %s", idesc.mnem, NameOfCPURegister(*data & 0x07));
data++; data++;
break; break;
case MOVE_REG_INSTR: { case MOVE_REG_INSTR: {
byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1)); byte* addr =
AppendToBuffer("mov %s,%s", reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
NameOfCPURegister(*data & 0x07), AppendToBuffer("mov %s,%s", NameOfCPURegister(*data & 0x07),
NameOfAddress(addr)); NameOfAddress(addr));
data += 5; data += 5;
break; break;
}
case CALL_JUMP_INSTR: {
byte* addr = data + *reinterpret_cast<int32_t*>(data + 1) + 5;
AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
data += 5;
break;
}
case SHORT_IMMEDIATE_INSTR: {
byte* addr =
reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data + 1));
AppendToBuffer("%s eax,%s", idesc.mnem, NameOfAddress(addr));
data += 5;
break;
}
case BYTE_IMMEDIATE_INSTR: {
AppendToBuffer("%s al,0x%x", idesc.mnem, data[1]);
data += 2;
break;
}
case NO_INSTR:
processed = false;
break;
default:
UNIMPLEMENTED(); // This type is not implemented.
} }
case CALL_JUMP_INSTR: {
byte* addr = data + *reinterpret_cast<int32_t*>(data+1) + 5;
AppendToBuffer("%s %s", idesc.mnem, NameOfAddress(addr));
data += 5;
break;
}
case SHORT_IMMEDIATE_INSTR: {
byte* addr = reinterpret_cast<byte*>(*reinterpret_cast<int32_t*>(data+1));
AppendToBuffer("%s eax,%s", idesc.mnem, NameOfAddress(addr));
data += 5;
break;
}
case BYTE_IMMEDIATE_INSTR: {
AppendToBuffer("%s al,0x%x", idesc.mnem, data[1]);
data += 2;
break;
}
case NO_INSTR:
processed = false;
break;
default:
UNIMPLEMENTED(); // This type is not implemented.
} }
//---------------------------- //----------------------------
if (!processed) { if (!processed) {

View File

@ -471,6 +471,21 @@ TEST(DisasmIa320) {
} }
} }
// AVX instruction
{
if (CpuFeatures::IsSupported(AVX)) {
CpuFeatureScope scope(&assm, AVX);
__ vaddsd(xmm0, xmm1, xmm2);
__ vaddsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
__ vmulsd(xmm0, xmm1, xmm2);
__ vmulsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
__ vsubsd(xmm0, xmm1, xmm2);
__ vsubsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
__ vdivsd(xmm0, xmm1, xmm2);
__ vdivsd(xmm0, xmm1, Operand(ebx, ecx, times_4, 10000));
}
}
// xchg. // xchg.
{ {
__ xchg(eax, eax); __ xchg(eax, eax);

View File

@ -601,6 +601,38 @@ TEST_F(InstructionSelectorTest, Int32MulHigh) {
EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), edx)); EXPECT_TRUE(s.IsFixed(s[0]->OutputAt(0), edx));
} }
TEST_F(InstructionSelectorTest, Float64BinopArithmetic) {
{
StreamBuilder m(this, kMachFloat64, kMachFloat64, kMachFloat64);
Node* add = m.Float64Add(m.Parameter(0), m.Parameter(1));
Node* mul = m.Float64Mul(add, m.Parameter(1));
Node* sub = m.Float64Sub(mul, add);
Node* ret = m.Float64Div(mul, sub);
m.Return(ret);
Stream s = m.Build(AVX);
ASSERT_EQ(4U, s.size());
EXPECT_EQ(kAVXFloat64Add, s[0]->arch_opcode());
EXPECT_EQ(kAVXFloat64Mul, s[1]->arch_opcode());
EXPECT_EQ(kAVXFloat64Sub, s[2]->arch_opcode());
EXPECT_EQ(kAVXFloat64Div, s[3]->arch_opcode());
}
{
StreamBuilder m(this, kMachFloat64, kMachFloat64, kMachFloat64);
Node* add = m.Float64Add(m.Parameter(0), m.Parameter(1));
Node* mul = m.Float64Mul(add, m.Parameter(1));
Node* sub = m.Float64Sub(mul, add);
Node* ret = m.Float64Div(mul, sub);
m.Return(ret);
Stream s = m.Build();
ASSERT_EQ(4U, s.size());
EXPECT_EQ(kSSEFloat64Add, s[0]->arch_opcode());
EXPECT_EQ(kSSEFloat64Mul, s[1]->arch_opcode());
EXPECT_EQ(kSSEFloat64Sub, s[2]->arch_opcode());
EXPECT_EQ(kSSEFloat64Div, s[3]->arch_opcode());
}
}
} // namespace compiler } // namespace compiler
} // namespace internal } // namespace internal
} // namespace v8 } // namespace v8