summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc')
-rw-r--r--deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc2470
1 files changed, 2470 insertions, 0 deletions
diff --git a/deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc b/deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc
new file mode 100644
index 0000000000..1e241a8ae9
--- /dev/null
+++ b/deps/v8/src/compiler/backend/ia32/instruction-selector-ia32.cc
@@ -0,0 +1,2470 @@
+// Copyright 2014 the V8 project authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "src/base/adapters.h"
+#include "src/compiler/backend/instruction-selector-impl.h"
+#include "src/compiler/node-matchers.h"
+#include "src/compiler/node-properties.h"
+
+namespace v8 {
+namespace internal {
+namespace compiler {
+
+// Adds IA32-specific methods for generating operands.
+class IA32OperandGenerator final : public OperandGenerator {
+ public:
+ explicit IA32OperandGenerator(InstructionSelector* selector)
+ : OperandGenerator(selector) {}
+
+ InstructionOperand UseByteRegister(Node* node) {
+ // TODO(titzer): encode byte register use constraints.
+ return UseFixed(node, edx);
+ }
+
+ InstructionOperand DefineAsByteRegister(Node* node) {
+ // TODO(titzer): encode byte register def constraints.
+ return DefineAsRegister(node);
+ }
+
+ bool CanBeMemoryOperand(InstructionCode opcode, Node* node, Node* input,
+ int effect_level) {
+ if (input->opcode() != IrOpcode::kLoad ||
+ !selector()->CanCover(node, input)) {
+ return false;
+ }
+ if (effect_level != selector()->GetEffectLevel(input)) {
+ return false;
+ }
+ MachineRepresentation rep =
+ LoadRepresentationOf(input->op()).representation();
+ switch (opcode) {
+ case kIA32And:
+ case kIA32Or:
+ case kIA32Xor:
+ case kIA32Add:
+ case kIA32Sub:
+ case kIA32Cmp:
+ case kIA32Test:
+ return rep == MachineRepresentation::kWord32 || IsAnyTagged(rep);
+ case kIA32Cmp16:
+ case kIA32Test16:
+ return rep == MachineRepresentation::kWord16;
+ case kIA32Cmp8:
+ case kIA32Test8:
+ return rep == MachineRepresentation::kWord8;
+ default:
+ break;
+ }
+ return false;
+ }
+
+ bool CanBeImmediate(Node* node) {
+ switch (node->opcode()) {
+ case IrOpcode::kInt32Constant:
+ case IrOpcode::kNumberConstant:
+ case IrOpcode::kExternalConstant:
+ case IrOpcode::kRelocatableInt32Constant:
+ case IrOpcode::kRelocatableInt64Constant:
+ return true;
+ case IrOpcode::kHeapConstant: {
+// TODO(bmeurer): We must not dereference handles concurrently. If we
+// really have to this here, then we need to find a way to put this
+// information on the HeapConstant node already.
+#if 0
+ // Constants in new space cannot be used as immediates in V8 because
+ // the GC does not scan code objects when collecting the new generation.
+ Handle<HeapObject> value = HeapConstantOf(node->op());
+ return !Heap::InNewSpace(*value);
+#else
+ return false;
+#endif
+ }
+ default:
+ return false;
+ }
+ }
+
+ AddressingMode GenerateMemoryOperandInputs(Node* index, int scale, Node* base,
+ Node* displacement_node,
+ DisplacementMode displacement_mode,
+ InstructionOperand inputs[],
+ size_t* input_count) {
+ AddressingMode mode = kMode_MRI;
+ int32_t displacement = (displacement_node == nullptr)
+ ? 0
+ : OpParameter<int32_t>(displacement_node->op());
+ if (displacement_mode == kNegativeDisplacement) {
+ displacement = -displacement;
+ }
+ if (base != nullptr) {
+ if (base->opcode() == IrOpcode::kInt32Constant) {
+ displacement += OpParameter<int32_t>(base->op());
+ base = nullptr;
+ }
+ }
+ if (base != nullptr) {
+ inputs[(*input_count)++] = UseRegister(base);
+ if (index != nullptr) {
+ DCHECK(scale >= 0 && scale <= 3);
+ inputs[(*input_count)++] = UseRegister(index);
+ if (displacement != 0) {
+ inputs[(*input_count)++] = TempImmediate(displacement);
+ static const AddressingMode kMRnI_modes[] = {kMode_MR1I, kMode_MR2I,
+ kMode_MR4I, kMode_MR8I};
+ mode = kMRnI_modes[scale];
+ } else {
+ static const AddressingMode kMRn_modes[] = {kMode_MR1, kMode_MR2,
+ kMode_MR4, kMode_MR8};
+ mode = kMRn_modes[scale];
+ }
+ } else {
+ if (displacement == 0) {
+ mode = kMode_MR;
+ } else {
+ inputs[(*input_count)++] = TempImmediate(displacement);
+ mode = kMode_MRI;
+ }
+ }
+ } else {
+ DCHECK(scale >= 0 && scale <= 3);
+ if (index != nullptr) {
+ inputs[(*input_count)++] = UseRegister(index);
+ if (displacement != 0) {
+ inputs[(*input_count)++] = TempImmediate(displacement);
+ static const AddressingMode kMnI_modes[] = {kMode_MRI, kMode_M2I,
+ kMode_M4I, kMode_M8I};
+ mode = kMnI_modes[scale];
+ } else {
+ static const AddressingMode kMn_modes[] = {kMode_MR, kMode_M2,
+ kMode_M4, kMode_M8};
+ mode = kMn_modes[scale];
+ }
+ } else {
+ inputs[(*input_count)++] = TempImmediate(displacement);
+ return kMode_MI;
+ }
+ }
+ return mode;
+ }
+
+ AddressingMode GetEffectiveAddressMemoryOperand(Node* node,
+ InstructionOperand inputs[],
+ size_t* input_count) {
+ BaseWithIndexAndDisplacement32Matcher m(node, AddressOption::kAllowAll);
+ DCHECK(m.matches());
+ if ((m.displacement() == nullptr || CanBeImmediate(m.displacement()))) {
+ return GenerateMemoryOperandInputs(
+ m.index(), m.scale(), m.base(), m.displacement(),
+ m.displacement_mode(), inputs, input_count);
+ } else {
+ inputs[(*input_count)++] = UseRegister(node->InputAt(0));
+ inputs[(*input_count)++] = UseRegister(node->InputAt(1));
+ return kMode_MR1;
+ }
+ }
+
+ InstructionOperand GetEffectiveIndexOperand(Node* index,
+ AddressingMode* mode) {
+ if (CanBeImmediate(index)) {
+ *mode = kMode_MRI;
+ return UseImmediate(index);
+ } else {
+ *mode = kMode_MR1;
+ return UseUniqueRegister(index);
+ }
+ }
+
+ bool CanBeBetterLeftOperand(Node* node) const {
+ return !selector()->IsLive(node);
+ }
+};
+
+namespace {
+
+void VisitRO(InstructionSelector* selector, Node* node, ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Node* input = node->InputAt(0);
+ // We have to use a byte register as input to movsxb.
+ InstructionOperand input_op =
+ opcode == kIA32Movsxbl ? g.UseFixed(input, eax) : g.Use(input);
+ selector->Emit(opcode, g.DefineAsRegister(node), input_op, arraysize(temps),
+ temps);
+}
+
+void VisitRR(InstructionSelector* selector, Node* node,
+ InstructionCode opcode) {
+ IA32OperandGenerator g(selector);
+ selector->Emit(opcode, g.DefineAsRegister(node),
+ g.UseRegister(node->InputAt(0)));
+}
+
+void VisitRROFloat(InstructionSelector* selector, Node* node,
+ ArchOpcode avx_opcode, ArchOpcode sse_opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
+ InstructionOperand operand1 = g.Use(node->InputAt(1));
+ if (selector->IsSupported(AVX)) {
+ selector->Emit(avx_opcode, g.DefineAsRegister(node), operand0, operand1);
+ } else {
+ selector->Emit(sse_opcode, g.DefineSameAsFirst(node), operand0, operand1);
+ }
+}
+
+void VisitFloatUnop(InstructionSelector* selector, Node* node, Node* input,
+ ArchOpcode avx_opcode, ArchOpcode sse_opcode) {
+ IA32OperandGenerator g(selector);
+ if (selector->IsSupported(AVX)) {
+ selector->Emit(avx_opcode, g.DefineAsRegister(node), g.Use(input));
+ } else {
+ selector->Emit(sse_opcode, g.DefineSameAsFirst(node), g.UseRegister(input));
+ }
+}
+
+void VisitRRSimd(InstructionSelector* selector, Node* node,
+ ArchOpcode avx_opcode, ArchOpcode sse_opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
+ if (selector->IsSupported(AVX)) {
+ selector->Emit(avx_opcode, g.DefineAsRegister(node), operand0);
+ } else {
+ selector->Emit(sse_opcode, g.DefineSameAsFirst(node), operand0);
+ }
+}
+
+void VisitRRISimd(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
+ InstructionOperand operand1 =
+ g.UseImmediate(OpParameter<int32_t>(node->op()));
+ // 8x16 uses movsx_b on dest to extract a byte, which only works
+ // if dest is a byte register.
+ InstructionOperand dest = opcode == kIA32I8x16ExtractLane
+ ? g.DefineAsFixed(node, eax)
+ : g.DefineAsRegister(node);
+ selector->Emit(opcode, dest, operand0, operand1);
+}
+
+void VisitRRISimd(InstructionSelector* selector, Node* node,
+ ArchOpcode avx_opcode, ArchOpcode sse_opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
+ InstructionOperand operand1 =
+ g.UseImmediate(OpParameter<int32_t>(node->op()));
+ if (selector->IsSupported(AVX)) {
+ selector->Emit(avx_opcode, g.DefineAsRegister(node), operand0, operand1);
+ } else {
+ selector->Emit(sse_opcode, g.DefineSameAsFirst(node), operand0, operand1);
+ }
+}
+
+} // namespace
+
+void InstructionSelector::VisitStackSlot(Node* node) {
+ StackSlotRepresentation rep = StackSlotRepresentationOf(node->op());
+ int slot = frame_->AllocateSpillSlot(rep.size());
+ OperandGenerator g(this);
+
+ Emit(kArchStackSlot, g.DefineAsRegister(node),
+ sequence()->AddImmediate(Constant(slot)), 0, nullptr);
+}
+
+void InstructionSelector::VisitDebugAbort(Node* node) {
+ IA32OperandGenerator g(this);
+ Emit(kArchDebugAbort, g.NoOutput(), g.UseFixed(node->InputAt(0), edx));
+}
+
+void InstructionSelector::VisitSpeculationFence(Node* node) {
+ IA32OperandGenerator g(this);
+ Emit(kLFence, g.NoOutput());
+}
+
+void InstructionSelector::VisitLoad(Node* node) {
+ LoadRepresentation load_rep = LoadRepresentationOf(node->op());
+
+ ArchOpcode opcode = kArchNop;
+ switch (load_rep.representation()) {
+ case MachineRepresentation::kFloat32:
+ opcode = kIA32Movss;
+ break;
+ case MachineRepresentation::kFloat64:
+ opcode = kIA32Movsd;
+ break;
+ case MachineRepresentation::kBit: // Fall through.
+ case MachineRepresentation::kWord8:
+ opcode = load_rep.IsSigned() ? kIA32Movsxbl : kIA32Movzxbl;
+ break;
+ case MachineRepresentation::kWord16:
+ opcode = load_rep.IsSigned() ? kIA32Movsxwl : kIA32Movzxwl;
+ break;
+ case MachineRepresentation::kTaggedSigned: // Fall through.
+ case MachineRepresentation::kTaggedPointer: // Fall through.
+ case MachineRepresentation::kTagged: // Fall through.
+ case MachineRepresentation::kWord32:
+ opcode = kIA32Movl;
+ break;
+ case MachineRepresentation::kSimd128:
+ opcode = kIA32Movdqu;
+ break;
+ case MachineRepresentation::kWord64: // Fall through.
+ case MachineRepresentation::kNone:
+ UNREACHABLE();
+ return;
+ }
+
+ IA32OperandGenerator g(this);
+ InstructionOperand outputs[1];
+ outputs[0] = g.DefineAsRegister(node);
+ InstructionOperand inputs[3];
+ size_t input_count = 0;
+ AddressingMode mode =
+ g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
+ InstructionCode code = opcode | AddressingModeField::encode(mode);
+ if (node->opcode() == IrOpcode::kPoisonedLoad) {
+ CHECK_NE(poisoning_level_, PoisoningMitigationLevel::kDontPoison);
+ code |= MiscField::encode(kMemoryAccessPoisoned);
+ }
+ Emit(code, 1, outputs, input_count, inputs);
+}
+
+void InstructionSelector::VisitPoisonedLoad(Node* node) { VisitLoad(node); }
+
+void InstructionSelector::VisitProtectedLoad(Node* node) {
+ // TODO(eholk)
+ UNIMPLEMENTED();
+}
+
+void InstructionSelector::VisitStore(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* value = node->InputAt(2);
+
+ StoreRepresentation store_rep = StoreRepresentationOf(node->op());
+ WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
+ MachineRepresentation rep = store_rep.representation();
+
+ if (write_barrier_kind != kNoWriteBarrier) {
+ DCHECK(CanBeTaggedPointer(rep));
+ AddressingMode addressing_mode;
+ InstructionOperand inputs[] = {
+ g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode),
+ g.UseUniqueRegister(value)};
+ RecordWriteMode record_write_mode = RecordWriteMode::kValueIsAny;
+ switch (write_barrier_kind) {
+ case kNoWriteBarrier:
+ UNREACHABLE();
+ break;
+ case kMapWriteBarrier:
+ record_write_mode = RecordWriteMode::kValueIsMap;
+ break;
+ case kPointerWriteBarrier:
+ record_write_mode = RecordWriteMode::kValueIsPointer;
+ break;
+ case kFullWriteBarrier:
+ record_write_mode = RecordWriteMode::kValueIsAny;
+ break;
+ }
+ InstructionOperand temps[] = {g.TempRegister(), g.TempRegister()};
+ size_t const temp_count = arraysize(temps);
+ InstructionCode code = kArchStoreWithWriteBarrier;
+ code |= AddressingModeField::encode(addressing_mode);
+ code |= MiscField::encode(static_cast<int>(record_write_mode));
+ Emit(code, 0, nullptr, arraysize(inputs), inputs, temp_count, temps);
+ } else {
+ ArchOpcode opcode = kArchNop;
+ switch (rep) {
+ case MachineRepresentation::kFloat32:
+ opcode = kIA32Movss;
+ break;
+ case MachineRepresentation::kFloat64:
+ opcode = kIA32Movsd;
+ break;
+ case MachineRepresentation::kBit: // Fall through.
+ case MachineRepresentation::kWord8:
+ opcode = kIA32Movb;
+ break;
+ case MachineRepresentation::kWord16:
+ opcode = kIA32Movw;
+ break;
+ case MachineRepresentation::kTaggedSigned: // Fall through.
+ case MachineRepresentation::kTaggedPointer: // Fall through.
+ case MachineRepresentation::kTagged: // Fall through.
+ case MachineRepresentation::kWord32:
+ opcode = kIA32Movl;
+ break;
+ case MachineRepresentation::kSimd128:
+ opcode = kIA32Movdqu;
+ break;
+ case MachineRepresentation::kWord64: // Fall through.
+ case MachineRepresentation::kNone:
+ UNREACHABLE();
+ return;
+ }
+
+ InstructionOperand val;
+ if (g.CanBeImmediate(value)) {
+ val = g.UseImmediate(value);
+ } else if (rep == MachineRepresentation::kWord8 ||
+ rep == MachineRepresentation::kBit) {
+ val = g.UseByteRegister(value);
+ } else {
+ val = g.UseRegister(value);
+ }
+
+ InstructionOperand inputs[4];
+ size_t input_count = 0;
+ AddressingMode addressing_mode =
+ g.GetEffectiveAddressMemoryOperand(node, inputs, &input_count);
+ InstructionCode code =
+ opcode | AddressingModeField::encode(addressing_mode);
+ inputs[input_count++] = val;
+ Emit(code, 0, static_cast<InstructionOperand*>(nullptr), input_count,
+ inputs);
+ }
+}
+
+void InstructionSelector::VisitProtectedStore(Node* node) {
+ // TODO(eholk)
+ UNIMPLEMENTED();
+}
+
+// Architecture supports unaligned access, therefore VisitLoad is used instead
+void InstructionSelector::VisitUnalignedLoad(Node* node) { UNREACHABLE(); }
+
+// Architecture supports unaligned access, therefore VisitStore is used instead
+void InstructionSelector::VisitUnalignedStore(Node* node) { UNREACHABLE(); }
+
+namespace {
+
+// Shared routine for multiple binary operations.
+void VisitBinop(InstructionSelector* selector, Node* node,
+ InstructionCode opcode, FlagsContinuation* cont) {
+ IA32OperandGenerator g(selector);
+ Int32BinopMatcher m(node);
+ Node* left = m.left().node();
+ Node* right = m.right().node();
+ InstructionOperand inputs[6];
+ size_t input_count = 0;
+ InstructionOperand outputs[1];
+ size_t output_count = 0;
+
+ // TODO(turbofan): match complex addressing modes.
+ if (left == right) {
+ // If both inputs refer to the same operand, enforce allocating a register
+ // for both of them to ensure that we don't end up generating code like
+ // this:
+ //
+ // mov eax, [ebp-0x10]
+ // add eax, [ebp-0x10]
+ // jo label
+ InstructionOperand const input = g.UseRegister(left);
+ inputs[input_count++] = input;
+ inputs[input_count++] = input;
+ } else if (g.CanBeImmediate(right)) {
+ inputs[input_count++] = g.UseRegister(left);
+ inputs[input_count++] = g.UseImmediate(right);
+ } else {
+ int effect_level = selector->GetEffectLevel(node);
+ if (cont->IsBranch()) {
+ effect_level = selector->GetEffectLevel(
+ cont->true_block()->PredecessorAt(0)->control_input());
+ }
+ if (node->op()->HasProperty(Operator::kCommutative) &&
+ g.CanBeBetterLeftOperand(right) &&
+ (!g.CanBeBetterLeftOperand(left) ||
+ !g.CanBeMemoryOperand(opcode, node, right, effect_level))) {
+ std::swap(left, right);
+ }
+ if (g.CanBeMemoryOperand(opcode, node, right, effect_level)) {
+ inputs[input_count++] = g.UseRegister(left);
+ AddressingMode addressing_mode =
+ g.GetEffectiveAddressMemoryOperand(right, inputs, &input_count);
+ opcode |= AddressingModeField::encode(addressing_mode);
+ } else {
+ inputs[input_count++] = g.UseRegister(left);
+ inputs[input_count++] = g.Use(right);
+ }
+ }
+
+ outputs[output_count++] = g.DefineSameAsFirst(node);
+
+ DCHECK_NE(0u, input_count);
+ DCHECK_EQ(1u, output_count);
+ DCHECK_GE(arraysize(inputs), input_count);
+ DCHECK_GE(arraysize(outputs), output_count);
+
+ selector->EmitWithContinuation(opcode, output_count, outputs, input_count,
+ inputs, cont);
+}
+
+// Shared routine for multiple binary operations.
+void VisitBinop(InstructionSelector* selector, Node* node,
+ InstructionCode opcode) {
+ FlagsContinuation cont;
+ VisitBinop(selector, node, opcode, &cont);
+}
+
+} // namespace
+
+void InstructionSelector::VisitWord32And(Node* node) {
+ VisitBinop(this, node, kIA32And);
+}
+
+void InstructionSelector::VisitWord32Or(Node* node) {
+ VisitBinop(this, node, kIA32Or);
+}
+
+void InstructionSelector::VisitWord32Xor(Node* node) {
+ IA32OperandGenerator g(this);
+ Int32BinopMatcher m(node);
+ if (m.right().Is(-1)) {
+ Emit(kIA32Not, g.DefineSameAsFirst(node), g.UseRegister(m.left().node()));
+ } else {
+ VisitBinop(this, node, kIA32Xor);
+ }
+}
+
+// Shared routine for multiple shift operations.
+static inline void VisitShift(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ Node* left = node->InputAt(0);
+ Node* right = node->InputAt(1);
+
+ if (g.CanBeImmediate(right)) {
+ selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left),
+ g.UseImmediate(right));
+ } else {
+ selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left),
+ g.UseFixed(right, ecx));
+ }
+}
+
+namespace {
+
+void VisitMulHigh(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand temps[] = {g.TempRegister(eax)};
+ selector->Emit(
+ opcode, g.DefineAsFixed(node, edx), g.UseFixed(node->InputAt(0), eax),
+ g.UseUniqueRegister(node->InputAt(1)), arraysize(temps), temps);
+}
+
+void VisitDiv(InstructionSelector* selector, Node* node, ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand temps[] = {g.TempRegister(edx)};
+ selector->Emit(opcode, g.DefineAsFixed(node, eax),
+ g.UseFixed(node->InputAt(0), eax),
+ g.UseUnique(node->InputAt(1)), arraysize(temps), temps);
+}
+
+void VisitMod(InstructionSelector* selector, Node* node, ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand temps[] = {g.TempRegister(eax)};
+ selector->Emit(opcode, g.DefineAsFixed(node, edx),
+ g.UseFixed(node->InputAt(0), eax),
+ g.UseUnique(node->InputAt(1)), arraysize(temps), temps);
+}
+
+void EmitLea(InstructionSelector* selector, Node* result, Node* index,
+ int scale, Node* base, Node* displacement,
+ DisplacementMode displacement_mode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand inputs[4];
+ size_t input_count = 0;
+ AddressingMode mode =
+ g.GenerateMemoryOperandInputs(index, scale, base, displacement,
+ displacement_mode, inputs, &input_count);
+
+ DCHECK_NE(0u, input_count);
+ DCHECK_GE(arraysize(inputs), input_count);
+
+ InstructionOperand outputs[1];
+ outputs[0] = g.DefineAsRegister(result);
+
+ InstructionCode opcode = AddressingModeField::encode(mode) | kIA32Lea;
+
+ selector->Emit(opcode, 1, outputs, input_count, inputs);
+}
+
+} // namespace
+
+void InstructionSelector::VisitWord32Shl(Node* node) {
+ Int32ScaleMatcher m(node, true);
+ if (m.matches()) {
+ Node* index = node->InputAt(0);
+ Node* base = m.power_of_two_plus_one() ? index : nullptr;
+ EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement);
+ return;
+ }
+ VisitShift(this, node, kIA32Shl);
+}
+
+void InstructionSelector::VisitWord32Shr(Node* node) {
+ VisitShift(this, node, kIA32Shr);
+}
+
+void InstructionSelector::VisitWord32Sar(Node* node) {
+ VisitShift(this, node, kIA32Sar);
+}
+
+void InstructionSelector::VisitInt32PairAdd(Node* node) {
+ IA32OperandGenerator g(this);
+
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ if (projection1) {
+ // We use UseUniqueRegister here to avoid register sharing with the temp
+ // register.
+ InstructionOperand inputs[] = {
+ g.UseRegister(node->InputAt(0)),
+ g.UseUniqueRegisterOrSlotOrConstant(node->InputAt(1)),
+ g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
+
+ InstructionOperand outputs[] = {g.DefineSameAsFirst(node),
+ g.DefineAsRegister(projection1)};
+
+ InstructionOperand temps[] = {g.TempRegister()};
+
+ Emit(kIA32AddPair, 2, outputs, 4, inputs, 1, temps);
+ } else {
+ // The high word of the result is not used, so we emit the standard 32 bit
+ // instruction.
+ Emit(kIA32Add, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
+ g.Use(node->InputAt(2)));
+ }
+}
+
+void InstructionSelector::VisitInt32PairSub(Node* node) {
+ IA32OperandGenerator g(this);
+
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ if (projection1) {
+ // We use UseUniqueRegister here to avoid register sharing with the temp
+ // register.
+ InstructionOperand inputs[] = {
+ g.UseRegister(node->InputAt(0)),
+ g.UseUniqueRegisterOrSlotOrConstant(node->InputAt(1)),
+ g.UseRegister(node->InputAt(2)), g.UseUniqueRegister(node->InputAt(3))};
+
+ InstructionOperand outputs[] = {g.DefineSameAsFirst(node),
+ g.DefineAsRegister(projection1)};
+
+ InstructionOperand temps[] = {g.TempRegister()};
+
+ Emit(kIA32SubPair, 2, outputs, 4, inputs, 1, temps);
+ } else {
+ // The high word of the result is not used, so we emit the standard 32 bit
+ // instruction.
+ Emit(kIA32Sub, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
+ g.Use(node->InputAt(2)));
+ }
+}
+
+void InstructionSelector::VisitInt32PairMul(Node* node) {
+ IA32OperandGenerator g(this);
+
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ if (projection1) {
+ // InputAt(3) explicitly shares ecx with OutputRegister(1) to save one
+ // register and one mov instruction.
+ InstructionOperand inputs[] = {
+ g.UseUnique(node->InputAt(0)),
+ g.UseUniqueRegisterOrSlotOrConstant(node->InputAt(1)),
+ g.UseUniqueRegister(node->InputAt(2)),
+ g.UseFixed(node->InputAt(3), ecx)};
+
+ InstructionOperand outputs[] = {
+ g.DefineAsFixed(node, eax),
+ g.DefineAsFixed(NodeProperties::FindProjection(node, 1), ecx)};
+
+ InstructionOperand temps[] = {g.TempRegister(edx)};
+
+ Emit(kIA32MulPair, 2, outputs, 4, inputs, 1, temps);
+ } else {
+ // The high word of the result is not used, so we emit the standard 32 bit
+ // instruction.
+ Emit(kIA32Imul, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
+ g.Use(node->InputAt(2)));
+ }
+}
+
+void VisitWord32PairShift(InstructionSelector* selector, InstructionCode opcode,
+ Node* node) {
+ IA32OperandGenerator g(selector);
+
+ Node* shift = node->InputAt(2);
+ InstructionOperand shift_operand;
+ if (g.CanBeImmediate(shift)) {
+ shift_operand = g.UseImmediate(shift);
+ } else {
+ shift_operand = g.UseFixed(shift, ecx);
+ }
+ InstructionOperand inputs[] = {g.UseFixed(node->InputAt(0), eax),
+ g.UseFixed(node->InputAt(1), edx),
+ shift_operand};
+
+ InstructionOperand outputs[2];
+ InstructionOperand temps[1];
+ int32_t output_count = 0;
+ int32_t temp_count = 0;
+ outputs[output_count++] = g.DefineAsFixed(node, eax);
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ if (projection1) {
+ outputs[output_count++] = g.DefineAsFixed(projection1, edx);
+ } else {
+ temps[temp_count++] = g.TempRegister(edx);
+ }
+
+ selector->Emit(opcode, output_count, outputs, 3, inputs, temp_count, temps);
+}
+
+void InstructionSelector::VisitWord32PairShl(Node* node) {
+ VisitWord32PairShift(this, kIA32ShlPair, node);
+}
+
+void InstructionSelector::VisitWord32PairShr(Node* node) {
+ VisitWord32PairShift(this, kIA32ShrPair, node);
+}
+
+void InstructionSelector::VisitWord32PairSar(Node* node) {
+ VisitWord32PairShift(this, kIA32SarPair, node);
+}
+
+void InstructionSelector::VisitWord32Ror(Node* node) {
+ VisitShift(this, node, kIA32Ror);
+}
+
+#define RO_OP_LIST(V) \
+ V(Word32Clz, kIA32Lzcnt) \
+ V(Word32Ctz, kIA32Tzcnt) \
+ V(Word32Popcnt, kIA32Popcnt) \
+ V(ChangeFloat32ToFloat64, kSSEFloat32ToFloat64) \
+ V(RoundInt32ToFloat32, kSSEInt32ToFloat32) \
+ V(ChangeInt32ToFloat64, kSSEInt32ToFloat64) \
+ V(ChangeUint32ToFloat64, kSSEUint32ToFloat64) \
+ V(TruncateFloat32ToInt32, kSSEFloat32ToInt32) \
+ V(TruncateFloat32ToUint32, kSSEFloat32ToUint32) \
+ V(ChangeFloat64ToInt32, kSSEFloat64ToInt32) \
+ V(ChangeFloat64ToUint32, kSSEFloat64ToUint32) \
+ V(TruncateFloat64ToUint32, kSSEFloat64ToUint32) \
+ V(TruncateFloat64ToFloat32, kSSEFloat64ToFloat32) \
+ V(RoundFloat64ToInt32, kSSEFloat64ToInt32) \
+ V(BitcastFloat32ToInt32, kIA32BitcastFI) \
+ V(BitcastInt32ToFloat32, kIA32BitcastIF) \
+ V(Float32Sqrt, kSSEFloat32Sqrt) \
+ V(Float64Sqrt, kSSEFloat64Sqrt) \
+ V(Float64ExtractLowWord32, kSSEFloat64ExtractLowWord32) \
+ V(Float64ExtractHighWord32, kSSEFloat64ExtractHighWord32) \
+ V(SignExtendWord8ToInt32, kIA32Movsxbl) \
+ V(SignExtendWord16ToInt32, kIA32Movsxwl)
+
+#define RR_OP_LIST(V) \
+ V(TruncateFloat64ToWord32, kArchTruncateDoubleToI) \
+ V(Float32RoundDown, kSSEFloat32Round | MiscField::encode(kRoundDown)) \
+ V(Float64RoundDown, kSSEFloat64Round | MiscField::encode(kRoundDown)) \
+ V(Float32RoundUp, kSSEFloat32Round | MiscField::encode(kRoundUp)) \
+ V(Float64RoundUp, kSSEFloat64Round | MiscField::encode(kRoundUp)) \
+ V(Float32RoundTruncate, kSSEFloat32Round | MiscField::encode(kRoundToZero)) \
+ V(Float64RoundTruncate, kSSEFloat64Round | MiscField::encode(kRoundToZero)) \
+ V(Float32RoundTiesEven, \
+ kSSEFloat32Round | MiscField::encode(kRoundToNearest)) \
+ V(Float64RoundTiesEven, kSSEFloat64Round | MiscField::encode(kRoundToNearest))
+
+#define RRO_FLOAT_OP_LIST(V) \
+ V(Float32Add, kAVXFloat32Add, kSSEFloat32Add) \
+ V(Float64Add, kAVXFloat64Add, kSSEFloat64Add) \
+ V(Float32Sub, kAVXFloat32Sub, kSSEFloat32Sub) \
+ V(Float64Sub, kAVXFloat64Sub, kSSEFloat64Sub) \
+ V(Float32Mul, kAVXFloat32Mul, kSSEFloat32Mul) \
+ V(Float64Mul, kAVXFloat64Mul, kSSEFloat64Mul) \
+ V(Float32Div, kAVXFloat32Div, kSSEFloat32Div) \
+ V(Float64Div, kAVXFloat64Div, kSSEFloat64Div)
+
+#define FLOAT_UNOP_LIST(V) \
+ V(Float32Abs, kAVXFloat32Abs, kSSEFloat32Abs) \
+ V(Float64Abs, kAVXFloat64Abs, kSSEFloat64Abs) \
+ V(Float32Neg, kAVXFloat32Neg, kSSEFloat32Neg) \
+ V(Float64Neg, kAVXFloat64Neg, kSSEFloat64Neg)
+
+#define RO_VISITOR(Name, opcode) \
+ void InstructionSelector::Visit##Name(Node* node) { \
+ VisitRO(this, node, opcode); \
+ }
+RO_OP_LIST(RO_VISITOR)
+#undef RO_VISITOR
+#undef RO_OP_LIST
+
+#define RR_VISITOR(Name, opcode) \
+ void InstructionSelector::Visit##Name(Node* node) { \
+ VisitRR(this, node, opcode); \
+ }
+RR_OP_LIST(RR_VISITOR)
+#undef RR_VISITOR
+#undef RR_OP_LIST
+
+#define RRO_FLOAT_VISITOR(Name, avx, sse) \
+ void InstructionSelector::Visit##Name(Node* node) { \
+ VisitRROFloat(this, node, avx, sse); \
+ }
+RRO_FLOAT_OP_LIST(RRO_FLOAT_VISITOR)
+#undef RRO_FLOAT_VISITOR
+#undef RRO_FLOAT_OP_LIST
+
+#define FLOAT_UNOP_VISITOR(Name, avx, sse) \
+ void InstructionSelector::Visit##Name(Node* node) { \
+ VisitFloatUnop(this, node, node->InputAt(0), avx, sse); \
+ }
+FLOAT_UNOP_LIST(FLOAT_UNOP_VISITOR)
+#undef FLOAT_UNOP_VISITOR
+#undef FLOAT_UNOP_LIST
+
+void InstructionSelector::VisitWord32ReverseBits(Node* node) { UNREACHABLE(); }
+
+void InstructionSelector::VisitWord64ReverseBytes(Node* node) { UNREACHABLE(); }
+
+void InstructionSelector::VisitWord32ReverseBytes(Node* node) {
+ IA32OperandGenerator g(this);
+ Emit(kIA32Bswap, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)));
+}
+
+void InstructionSelector::VisitInt32Add(Node* node) {
+ IA32OperandGenerator g(this);
+
+ // Try to match the Add to a lea pattern
+ BaseWithIndexAndDisplacement32Matcher m(node);
+ if (m.matches() &&
+ (m.displacement() == nullptr || g.CanBeImmediate(m.displacement()))) {
+ InstructionOperand inputs[4];
+ size_t input_count = 0;
+ AddressingMode mode = g.GenerateMemoryOperandInputs(
+ m.index(), m.scale(), m.base(), m.displacement(), m.displacement_mode(),
+ inputs, &input_count);
+
+ DCHECK_NE(0u, input_count);
+ DCHECK_GE(arraysize(inputs), input_count);
+
+ InstructionOperand outputs[1];
+ outputs[0] = g.DefineAsRegister(node);
+
+ InstructionCode opcode = AddressingModeField::encode(mode) | kIA32Lea;
+ Emit(opcode, 1, outputs, input_count, inputs);
+ return;
+ }
+
+ // No lea pattern match, use add
+ VisitBinop(this, node, kIA32Add);
+}
+
+void InstructionSelector::VisitInt32Sub(Node* node) {
+ IA32OperandGenerator g(this);
+ Int32BinopMatcher m(node);
+ if (m.left().Is(0)) {
+ Emit(kIA32Neg, g.DefineSameAsFirst(node), g.Use(m.right().node()));
+ } else {
+ VisitBinop(this, node, kIA32Sub);
+ }
+}
+
+void InstructionSelector::VisitInt32Mul(Node* node) {
+ Int32ScaleMatcher m(node, true);
+ if (m.matches()) {
+ Node* index = node->InputAt(0);
+ Node* base = m.power_of_two_plus_one() ? index : nullptr;
+ EmitLea(this, node, index, m.scale(), base, nullptr, kPositiveDisplacement);
+ return;
+ }
+ IA32OperandGenerator g(this);
+ Node* left = node->InputAt(0);
+ Node* right = node->InputAt(1);
+ if (g.CanBeImmediate(right)) {
+ Emit(kIA32Imul, g.DefineAsRegister(node), g.Use(left),
+ g.UseImmediate(right));
+ } else {
+ if (g.CanBeBetterLeftOperand(right)) {
+ std::swap(left, right);
+ }
+ Emit(kIA32Imul, g.DefineSameAsFirst(node), g.UseRegister(left),
+ g.Use(right));
+ }
+}
+
+void InstructionSelector::VisitInt32MulHigh(Node* node) {
+ VisitMulHigh(this, node, kIA32ImulHigh);
+}
+
+void InstructionSelector::VisitUint32MulHigh(Node* node) {
+ VisitMulHigh(this, node, kIA32UmulHigh);
+}
+
+void InstructionSelector::VisitInt32Div(Node* node) {
+ VisitDiv(this, node, kIA32Idiv);
+}
+
+void InstructionSelector::VisitUint32Div(Node* node) {
+ VisitDiv(this, node, kIA32Udiv);
+}
+
+void InstructionSelector::VisitInt32Mod(Node* node) {
+ VisitMod(this, node, kIA32Idiv);
+}
+
+void InstructionSelector::VisitUint32Mod(Node* node) {
+ VisitMod(this, node, kIA32Udiv);
+}
+
+void InstructionSelector::VisitRoundUint32ToFloat32(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Emit(kSSEUint32ToFloat32, g.DefineAsRegister(node), g.Use(node->InputAt(0)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat64Mod(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister(eax), g.TempRegister()};
+ Emit(kSSEFloat64Mod, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat32Max(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Emit(kSSEFloat32Max, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat64Max(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Emit(kSSEFloat64Max, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat32Min(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Emit(kSSEFloat32Min, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat64Min(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempRegister()};
+ Emit(kSSEFloat64Min, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(1)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) {
+ UNREACHABLE();
+}
+
+void InstructionSelector::VisitFloat64Ieee754Binop(Node* node,
+ InstructionCode opcode) {
+ IA32OperandGenerator g(this);
+ Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
+ g.UseRegister(node->InputAt(1)))
+ ->MarkAsCall();
+}
+
+void InstructionSelector::VisitFloat64Ieee754Unop(Node* node,
+ InstructionCode opcode) {
+ IA32OperandGenerator g(this);
+ Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)))
+ ->MarkAsCall();
+}
+
+void InstructionSelector::EmitPrepareArguments(
+ ZoneVector<PushParameter>* arguments, const CallDescriptor* call_descriptor,
+ Node* node) {
+ IA32OperandGenerator g(this);
+
+ // Prepare for C function call.
+ if (call_descriptor->IsCFunctionCall()) {
+ InstructionOperand temps[] = {g.TempRegister()};
+ size_t const temp_count = arraysize(temps);
+ Emit(kArchPrepareCallCFunction | MiscField::encode(static_cast<int>(
+ call_descriptor->ParameterCount())),
+ 0, nullptr, 0, nullptr, temp_count, temps);
+
+ // Poke any stack arguments.
+ for (size_t n = 0; n < arguments->size(); ++n) {
+ PushParameter input = (*arguments)[n];
+ if (input.node) {
+ int const slot = static_cast<int>(n);
+ InstructionOperand value = g.CanBeImmediate(node)
+ ? g.UseImmediate(input.node)
+ : g.UseRegister(input.node);
+ Emit(kIA32Poke | MiscField::encode(slot), g.NoOutput(), value);
+ }
+ }
+ } else {
+ // Push any stack arguments.
+ int effect_level = GetEffectLevel(node);
+ for (PushParameter input : base::Reversed(*arguments)) {
+ // Skip any alignment holes in pushed nodes.
+ if (input.node == nullptr) continue;
+ if (g.CanBeMemoryOperand(kIA32Push, node, input.node, effect_level)) {
+ InstructionOperand outputs[1];
+ InstructionOperand inputs[4];
+ size_t input_count = 0;
+ InstructionCode opcode = kIA32Push;
+ AddressingMode mode = g.GetEffectiveAddressMemoryOperand(
+ input.node, inputs, &input_count);
+ opcode |= AddressingModeField::encode(mode);
+ Emit(opcode, 0, outputs, input_count, inputs);
+ } else {
+ InstructionOperand value =
+ g.CanBeImmediate(input.node)
+ ? g.UseImmediate(input.node)
+ : IsSupported(ATOM) ||
+ sequence()->IsFP(GetVirtualRegister(input.node))
+ ? g.UseRegister(input.node)
+ : g.Use(input.node);
+ if (input.location.GetType() == MachineType::Float32()) {
+ Emit(kIA32PushFloat32, g.NoOutput(), value);
+ } else if (input.location.GetType() == MachineType::Float64()) {
+ Emit(kIA32PushFloat64, g.NoOutput(), value);
+ } else if (input.location.GetType() == MachineType::Simd128()) {
+ Emit(kIA32PushSimd128, g.NoOutput(), value);
+ } else {
+ Emit(kIA32Push, g.NoOutput(), value);
+ }
+ }
+ }
+ }
+}
+
+void InstructionSelector::EmitPrepareResults(
+ ZoneVector<PushParameter>* results, const CallDescriptor* call_descriptor,
+ Node* node) {
+ IA32OperandGenerator g(this);
+
+ int reverse_slot = 0;
+ for (PushParameter output : *results) {
+ if (!output.location.IsCallerFrameSlot()) continue;
+ // Skip any alignment holes in nodes.
+ if (output.node != nullptr) {
+ DCHECK(!call_descriptor->IsCFunctionCall());
+ if (output.location.GetType() == MachineType::Float32()) {
+ MarkAsFloat32(output.node);
+ } else if (output.location.GetType() == MachineType::Float64()) {
+ MarkAsFloat64(output.node);
+ }
+ Emit(kIA32Peek, g.DefineAsRegister(output.node),
+ g.UseImmediate(reverse_slot));
+ }
+ reverse_slot += output.location.GetSizeInPointers();
+ }
+}
+
+bool InstructionSelector::IsTailCallAddressImmediate() { return true; }
+
+int InstructionSelector::GetTempsCountForTailCallFromJSFunction() { return 0; }
+
+namespace {
+
+void VisitCompareWithMemoryOperand(InstructionSelector* selector,
+ InstructionCode opcode, Node* left,
+ InstructionOperand right,
+ FlagsContinuation* cont) {
+ DCHECK_EQ(IrOpcode::kLoad, left->opcode());
+ IA32OperandGenerator g(selector);
+ size_t input_count = 0;
+ InstructionOperand inputs[4];
+ AddressingMode addressing_mode =
+ g.GetEffectiveAddressMemoryOperand(left, inputs, &input_count);
+ opcode |= AddressingModeField::encode(addressing_mode);
+ inputs[input_count++] = right;
+
+ selector->EmitWithContinuation(opcode, 0, nullptr, input_count, inputs, cont);
+}
+
+// Shared routine for multiple compare operations.
+void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
+ InstructionOperand left, InstructionOperand right,
+ FlagsContinuation* cont) {
+ selector->EmitWithContinuation(opcode, left, right, cont);
+}
+
+// Shared routine for multiple compare operations.
+void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
+ Node* left, Node* right, FlagsContinuation* cont,
+ bool commutative) {
+ IA32OperandGenerator g(selector);
+ if (commutative && g.CanBeBetterLeftOperand(right)) {
+ std::swap(left, right);
+ }
+ VisitCompare(selector, opcode, g.UseRegister(left), g.Use(right), cont);
+}
+
+MachineType MachineTypeForNarrow(Node* node, Node* hint_node) {
+ if (hint_node->opcode() == IrOpcode::kLoad) {
+ MachineType hint = LoadRepresentationOf(hint_node->op());
+ if (node->opcode() == IrOpcode::kInt32Constant ||
+ node->opcode() == IrOpcode::kInt64Constant) {
+ int64_t constant = node->opcode() == IrOpcode::kInt32Constant
+ ? OpParameter<int32_t>(node->op())
+ : OpParameter<int64_t>(node->op());
+ if (hint == MachineType::Int8()) {
+ if (constant >= std::numeric_limits<int8_t>::min() &&
+ constant <= std::numeric_limits<int8_t>::max()) {
+ return hint;
+ }
+ } else if (hint == MachineType::Uint8()) {
+ if (constant >= std::numeric_limits<uint8_t>::min() &&
+ constant <= std::numeric_limits<uint8_t>::max()) {
+ return hint;
+ }
+ } else if (hint == MachineType::Int16()) {
+ if (constant >= std::numeric_limits<int16_t>::min() &&
+ constant <= std::numeric_limits<int16_t>::max()) {
+ return hint;
+ }
+ } else if (hint == MachineType::Uint16()) {
+ if (constant >= std::numeric_limits<uint16_t>::min() &&
+ constant <= std::numeric_limits<uint16_t>::max()) {
+ return hint;
+ }
+ } else if (hint == MachineType::Int32()) {
+ return hint;
+ } else if (hint == MachineType::Uint32()) {
+ if (constant >= 0) return hint;
+ }
+ }
+ }
+ return node->opcode() == IrOpcode::kLoad ? LoadRepresentationOf(node->op())
+ : MachineType::None();
+}
+
+// Tries to match the size of the given opcode to that of the operands, if
+// possible.
+InstructionCode TryNarrowOpcodeSize(InstructionCode opcode, Node* left,
+ Node* right, FlagsContinuation* cont) {
+ // TODO(epertoso): we can probably get some size information out of phi nodes.
+ // If the load representations don't match, both operands will be
+ // zero/sign-extended to 32bit.
+ MachineType left_type = MachineTypeForNarrow(left, right);
+ MachineType right_type = MachineTypeForNarrow(right, left);
+ if (left_type == right_type) {
+ switch (left_type.representation()) {
+ case MachineRepresentation::kBit:
+ case MachineRepresentation::kWord8: {
+ if (opcode == kIA32Test) return kIA32Test8;
+ if (opcode == kIA32Cmp) {
+ if (left_type.semantic() == MachineSemantic::kUint32) {
+ cont->OverwriteUnsignedIfSigned();
+ } else {
+ CHECK_EQ(MachineSemantic::kInt32, left_type.semantic());
+ }
+ return kIA32Cmp8;
+ }
+ break;
+ }
+ case MachineRepresentation::kWord16:
+ if (opcode == kIA32Test) return kIA32Test16;
+ if (opcode == kIA32Cmp) {
+ if (left_type.semantic() == MachineSemantic::kUint32) {
+ cont->OverwriteUnsignedIfSigned();
+ } else {
+ CHECK_EQ(MachineSemantic::kInt32, left_type.semantic());
+ }
+ return kIA32Cmp16;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+ return opcode;
+}
+
+// Shared routine for multiple float32 compare operations (inputs commuted).
+void VisitFloat32Compare(InstructionSelector* selector, Node* node,
+ FlagsContinuation* cont) {
+ Node* const left = node->InputAt(0);
+ Node* const right = node->InputAt(1);
+ VisitCompare(selector, kSSEFloat32Cmp, right, left, cont, false);
+}
+
+// Shared routine for multiple float64 compare operations (inputs commuted).
+void VisitFloat64Compare(InstructionSelector* selector, Node* node,
+ FlagsContinuation* cont) {
+ Node* const left = node->InputAt(0);
+ Node* const right = node->InputAt(1);
+ VisitCompare(selector, kSSEFloat64Cmp, right, left, cont, false);
+}
+
+// Shared routine for multiple word compare operations.
+void VisitWordCompare(InstructionSelector* selector, Node* node,
+ InstructionCode opcode, FlagsContinuation* cont) {
+ IA32OperandGenerator g(selector);
+ Node* left = node->InputAt(0);
+ Node* right = node->InputAt(1);
+
+ InstructionCode narrowed_opcode =
+ TryNarrowOpcodeSize(opcode, left, right, cont);
+
+ int effect_level = selector->GetEffectLevel(node);
+ if (cont->IsBranch()) {
+ effect_level = selector->GetEffectLevel(
+ cont->true_block()->PredecessorAt(0)->control_input());
+ }
+
+ // If one of the two inputs is an immediate, make sure it's on the right, or
+ // if one of the two inputs is a memory operand, make sure it's on the left.
+ if ((!g.CanBeImmediate(right) && g.CanBeImmediate(left)) ||
+ (g.CanBeMemoryOperand(narrowed_opcode, node, right, effect_level) &&
+ !g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level))) {
+ if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
+ std::swap(left, right);
+ }
+
+ // Match immediates on right side of comparison.
+ if (g.CanBeImmediate(right)) {
+ if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) {
+ return VisitCompareWithMemoryOperand(selector, narrowed_opcode, left,
+ g.UseImmediate(right), cont);
+ }
+ return VisitCompare(selector, opcode, g.Use(left), g.UseImmediate(right),
+ cont);
+ }
+
+ // Match memory operands on left side of comparison.
+ if (g.CanBeMemoryOperand(narrowed_opcode, node, left, effect_level)) {
+ bool needs_byte_register =
+ narrowed_opcode == kIA32Test8 || narrowed_opcode == kIA32Cmp8;
+ return VisitCompareWithMemoryOperand(
+ selector, narrowed_opcode, left,
+ needs_byte_register ? g.UseByteRegister(right) : g.UseRegister(right),
+ cont);
+ }
+
+ return VisitCompare(selector, opcode, left, right, cont,
+ node->op()->HasProperty(Operator::kCommutative));
+}
+
+void VisitWordCompare(InstructionSelector* selector, Node* node,
+ FlagsContinuation* cont) {
+ if (selector->isolate() != nullptr) {
+ StackCheckMatcher<Int32BinopMatcher, IrOpcode::kUint32LessThan> m(
+ selector->isolate(), node);
+ if (m.Matched()) {
+ // Compare(Load(js_stack_limit), LoadStackPointer)
+ if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
+ InstructionCode opcode = cont->Encode(kIA32StackCheck);
+ CHECK(cont->IsBranch());
+ selector->EmitWithContinuation(opcode, cont);
+ return;
+ }
+ }
+ WasmStackCheckMatcher<Int32BinopMatcher, IrOpcode::kUint32LessThan> wasm_m(
+ node);
+ if (wasm_m.Matched()) {
+ // This is a wasm stack check. By structure, we know that we can use the
+ // stack pointer directly, as wasm code does not modify the stack at points
+ // where stack checks are performed.
+ Node* left = node->InputAt(0);
+ LocationOperand esp(InstructionOperand::EXPLICIT, LocationOperand::REGISTER,
+ InstructionSequence::DefaultRepresentation(),
+ RegisterCode::kRegCode_esp);
+ return VisitCompareWithMemoryOperand(selector, kIA32Cmp, left, esp, cont);
+ }
+ VisitWordCompare(selector, node, kIA32Cmp, cont);
+}
+
+void VisitAtomicExchange(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode, MachineRepresentation rep) {
+ IA32OperandGenerator g(selector);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* value = node->InputAt(2);
+
+ AddressingMode addressing_mode;
+ InstructionOperand value_operand = (rep == MachineRepresentation::kWord8)
+ ? g.UseFixed(value, edx)
+ : g.UseUniqueRegister(value);
+ InstructionOperand inputs[] = {
+ value_operand, g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ InstructionOperand outputs[] = {
+ (rep == MachineRepresentation::kWord8)
+ // Using DefineSameAsFirst requires the register to be unallocated.
+ ? g.DefineAsFixed(node, edx)
+ : g.DefineSameAsFirst(node)};
+ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
+ selector->Emit(code, 1, outputs, arraysize(inputs), inputs);
+}
+
+void VisitAtomicBinOp(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode, MachineRepresentation rep) {
+ AddressingMode addressing_mode;
+ IA32OperandGenerator g(selector);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* value = node->InputAt(2);
+ InstructionOperand inputs[] = {
+ g.UseUniqueRegister(value), g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ InstructionOperand outputs[] = {g.DefineAsFixed(node, eax)};
+ InstructionOperand temp[] = {(rep == MachineRepresentation::kWord8)
+ ? g.UseByteRegister(node)
+ : g.TempRegister()};
+ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
+ selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ arraysize(temp), temp);
+}
+
+void VisitPairAtomicBinOp(InstructionSelector* selector, Node* node,
+ ArchOpcode opcode) {
+ IA32OperandGenerator g(selector);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* value = node->InputAt(2);
+ // For Word64 operations, the value input is split into the a high node,
+ // and a low node in the int64-lowering phase.
+ Node* value_high = node->InputAt(3);
+
+ // Wasm lives in 32-bit address space, so we do not need to worry about
+ // base/index lowering. This will need to be fixed for Wasm64.
+ AddressingMode addressing_mode;
+ InstructionOperand inputs[] = {
+ g.UseUniqueRegisterOrSlotOrConstant(value), g.UseFixed(value_high, ecx),
+ g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
+ Node* projection0 = NodeProperties::FindProjection(node, 0);
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ if (projection1) {
+ InstructionOperand outputs[] = {g.DefineAsFixed(projection0, eax),
+ g.DefineAsFixed(projection1, edx)};
+ selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ 0, {});
+ } else if (projection0) {
+ InstructionOperand outputs[] = {g.DefineAsFixed(projection0, eax)};
+ InstructionOperand temps[] = {g.TempRegister(edx)};
+ const int num_temps = arraysize(temps);
+ selector->Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ num_temps, temps);
+ } else {
+ InstructionOperand temps[] = {g.TempRegister(eax), g.TempRegister(edx)};
+ const int num_temps = arraysize(temps);
+ selector->Emit(code, 0, nullptr, arraysize(inputs), inputs, num_temps,
+ temps);
+ }
+}
+
+} // namespace
+
+// Shared routine for word comparison with zero.
+void InstructionSelector::VisitWordCompareZero(Node* user, Node* value,
+ FlagsContinuation* cont) {
+ // Try to combine with comparisons against 0 by simply inverting the branch.
+ while (value->opcode() == IrOpcode::kWord32Equal && CanCover(user, value)) {
+ Int32BinopMatcher m(value);
+ if (!m.right().Is(0)) break;
+
+ user = value;
+ value = m.left().node();
+ cont->Negate();
+ }
+
+ if (CanCover(user, value)) {
+ switch (value->opcode()) {
+ case IrOpcode::kWord32Equal:
+ cont->OverwriteAndNegateIfEqual(kEqual);
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kInt32LessThan:
+ cont->OverwriteAndNegateIfEqual(kSignedLessThan);
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kInt32LessThanOrEqual:
+ cont->OverwriteAndNegateIfEqual(kSignedLessThanOrEqual);
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kUint32LessThan:
+ cont->OverwriteAndNegateIfEqual(kUnsignedLessThan);
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kUint32LessThanOrEqual:
+ cont->OverwriteAndNegateIfEqual(kUnsignedLessThanOrEqual);
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kFloat32Equal:
+ cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
+ return VisitFloat32Compare(this, value, cont);
+ case IrOpcode::kFloat32LessThan:
+ cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan);
+ return VisitFloat32Compare(this, value, cont);
+ case IrOpcode::kFloat32LessThanOrEqual:
+ cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual);
+ return VisitFloat32Compare(this, value, cont);
+ case IrOpcode::kFloat64Equal:
+ cont->OverwriteAndNegateIfEqual(kUnorderedEqual);
+ return VisitFloat64Compare(this, value, cont);
+ case IrOpcode::kFloat64LessThan:
+ cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThan);
+ return VisitFloat64Compare(this, value, cont);
+ case IrOpcode::kFloat64LessThanOrEqual:
+ cont->OverwriteAndNegateIfEqual(kUnsignedGreaterThanOrEqual);
+ return VisitFloat64Compare(this, value, cont);
+ case IrOpcode::kProjection:
+ // Check if this is the overflow output projection of an
+ // <Operation>WithOverflow node.
+ if (ProjectionIndexOf(value->op()) == 1u) {
+ // We cannot combine the <Operation>WithOverflow with this branch
+ // unless the 0th projection (the use of the actual value of the
+ // <Operation> is either nullptr, which means there's no use of the
+ // actual value, or was already defined, which means it is scheduled
+ // *AFTER* this branch).
+ Node* const node = value->InputAt(0);
+ Node* const result = NodeProperties::FindProjection(node, 0);
+ if (result == nullptr || IsDefined(result)) {
+ switch (node->opcode()) {
+ case IrOpcode::kInt32AddWithOverflow:
+ cont->OverwriteAndNegateIfEqual(kOverflow);
+ return VisitBinop(this, node, kIA32Add, cont);
+ case IrOpcode::kInt32SubWithOverflow:
+ cont->OverwriteAndNegateIfEqual(kOverflow);
+ return VisitBinop(this, node, kIA32Sub, cont);
+ case IrOpcode::kInt32MulWithOverflow:
+ cont->OverwriteAndNegateIfEqual(kOverflow);
+ return VisitBinop(this, node, kIA32Imul, cont);
+ default:
+ break;
+ }
+ }
+ }
+ break;
+ case IrOpcode::kInt32Sub:
+ return VisitWordCompare(this, value, cont);
+ case IrOpcode::kWord32And:
+ return VisitWordCompare(this, value, kIA32Test, cont);
+ default:
+ break;
+ }
+ }
+
+ // Continuation could not be combined with a compare, emit compare against 0.
+ IA32OperandGenerator g(this);
+ VisitCompare(this, kIA32Cmp, g.Use(value), g.TempImmediate(0), cont);
+}
+
+void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) {
+ IA32OperandGenerator g(this);
+ InstructionOperand value_operand = g.UseRegister(node->InputAt(0));
+
+ // Emit either ArchTableSwitch or ArchLookupSwitch.
+ if (enable_switch_jump_table_ == kEnableSwitchJumpTable) {
+ static const size_t kMaxTableSwitchValueRange = 2 << 16;
+ size_t table_space_cost = 4 + sw.value_range();
+ size_t table_time_cost = 3;
+ size_t lookup_space_cost = 3 + 2 * sw.case_count();
+ size_t lookup_time_cost = sw.case_count();
+ if (sw.case_count() > 4 &&
+ table_space_cost + 3 * table_time_cost <=
+ lookup_space_cost + 3 * lookup_time_cost &&
+ sw.min_value() > std::numeric_limits<int32_t>::min() &&
+ sw.value_range() <= kMaxTableSwitchValueRange) {
+ InstructionOperand index_operand = value_operand;
+ if (sw.min_value()) {
+ index_operand = g.TempRegister();
+ Emit(kIA32Lea | AddressingModeField::encode(kMode_MRI), index_operand,
+ value_operand, g.TempImmediate(-sw.min_value()));
+ }
+ // Generate a table lookup.
+ return EmitTableSwitch(sw, index_operand);
+ }
+ }
+
+ // Generate a tree of conditional jumps.
+ return EmitBinarySearchSwitch(sw, value_operand);
+}
+
+void InstructionSelector::VisitWord32Equal(Node* const node) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kEqual, node);
+ Int32BinopMatcher m(node);
+ if (m.right().Is(0)) {
+ return VisitWordCompareZero(m.node(), m.left().node(), &cont);
+ }
+ VisitWordCompare(this, node, &cont);
+}
+
+void InstructionSelector::VisitInt32LessThan(Node* node) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kSignedLessThan, node);
+ VisitWordCompare(this, node, &cont);
+}
+
+void InstructionSelector::VisitInt32LessThanOrEqual(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kSignedLessThanOrEqual, node);
+ VisitWordCompare(this, node, &cont);
+}
+
+void InstructionSelector::VisitUint32LessThan(Node* node) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kUnsignedLessThan, node);
+ VisitWordCompare(this, node, &cont);
+}
+
+void InstructionSelector::VisitUint32LessThanOrEqual(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kUnsignedLessThanOrEqual, node);
+ VisitWordCompare(this, node, &cont);
+}
+
+void InstructionSelector::VisitInt32AddWithOverflow(Node* node) {
+ if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
+ return VisitBinop(this, node, kIA32Add, &cont);
+ }
+ FlagsContinuation cont;
+ VisitBinop(this, node, kIA32Add, &cont);
+}
+
+void InstructionSelector::VisitInt32SubWithOverflow(Node* node) {
+ if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
+ return VisitBinop(this, node, kIA32Sub, &cont);
+ }
+ FlagsContinuation cont;
+ VisitBinop(this, node, kIA32Sub, &cont);
+}
+
+void InstructionSelector::VisitInt32MulWithOverflow(Node* node) {
+ if (Node* ovf = NodeProperties::FindProjection(node, 1)) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf);
+ return VisitBinop(this, node, kIA32Imul, &cont);
+ }
+ FlagsContinuation cont;
+ VisitBinop(this, node, kIA32Imul, &cont);
+}
+
+void InstructionSelector::VisitFloat32Equal(Node* node) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node);
+ VisitFloat32Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat32LessThan(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kUnsignedGreaterThan, node);
+ VisitFloat32Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat32LessThanOrEqual(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node);
+ VisitFloat32Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat64Equal(Node* node) {
+ FlagsContinuation cont = FlagsContinuation::ForSet(kUnorderedEqual, node);
+ VisitFloat64Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat64LessThan(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kUnsignedGreaterThan, node);
+ VisitFloat64Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat64LessThanOrEqual(Node* node) {
+ FlagsContinuation cont =
+ FlagsContinuation::ForSet(kUnsignedGreaterThanOrEqual, node);
+ VisitFloat64Compare(this, node, &cont);
+}
+
+void InstructionSelector::VisitFloat64InsertLowWord32(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* left = node->InputAt(0);
+ Node* right = node->InputAt(1);
+ Float64Matcher mleft(left);
+ if (mleft.HasValue() && (bit_cast<uint64_t>(mleft.Value()) >> 32) == 0u) {
+ Emit(kSSEFloat64LoadLowWord32, g.DefineAsRegister(node), g.Use(right));
+ return;
+ }
+ Emit(kSSEFloat64InsertLowWord32, g.DefineSameAsFirst(node),
+ g.UseRegister(left), g.Use(right));
+}
+
+void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* left = node->InputAt(0);
+ Node* right = node->InputAt(1);
+ Emit(kSSEFloat64InsertHighWord32, g.DefineSameAsFirst(node),
+ g.UseRegister(left), g.Use(right));
+}
+
+void InstructionSelector::VisitFloat64SilenceNaN(Node* node) {
+ IA32OperandGenerator g(this);
+ Emit(kSSEFloat64SilenceNaN, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)));
+}
+
+void InstructionSelector::VisitWord32AtomicLoad(Node* node) {
+ LoadRepresentation load_rep = LoadRepresentationOf(node->op());
+ DCHECK(load_rep.representation() == MachineRepresentation::kWord8 ||
+ load_rep.representation() == MachineRepresentation::kWord16 ||
+ load_rep.representation() == MachineRepresentation::kWord32);
+ USE(load_rep);
+ VisitLoad(node);
+}
+
+void InstructionSelector::VisitWord32AtomicStore(Node* node) {
+ IA32OperandGenerator g(this);
+ MachineRepresentation rep = AtomicStoreRepresentationOf(node->op());
+ ArchOpcode opcode = kArchNop;
+ switch (rep) {
+ case MachineRepresentation::kWord8:
+ opcode = kWord32AtomicExchangeInt8;
+ break;
+ case MachineRepresentation::kWord16:
+ opcode = kWord32AtomicExchangeInt16;
+ break;
+ case MachineRepresentation::kWord32:
+ opcode = kWord32AtomicExchangeWord32;
+ break;
+ default:
+ UNREACHABLE();
+ break;
+ }
+ VisitAtomicExchange(this, node, opcode, rep);
+}
+
+void InstructionSelector::VisitWord32AtomicExchange(Node* node) {
+ IA32OperandGenerator g(this);
+ MachineType type = AtomicOpType(node->op());
+ ArchOpcode opcode = kArchNop;
+ if (type == MachineType::Int8()) {
+ opcode = kWord32AtomicExchangeInt8;
+ } else if (type == MachineType::Uint8()) {
+ opcode = kWord32AtomicExchangeUint8;
+ } else if (type == MachineType::Int16()) {
+ opcode = kWord32AtomicExchangeInt16;
+ } else if (type == MachineType::Uint16()) {
+ opcode = kWord32AtomicExchangeUint16;
+ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
+ opcode = kWord32AtomicExchangeWord32;
+ } else {
+ UNREACHABLE();
+ return;
+ }
+ VisitAtomicExchange(this, node, opcode, type.representation());
+}
+
+void InstructionSelector::VisitWord32AtomicCompareExchange(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* old_value = node->InputAt(2);
+ Node* new_value = node->InputAt(3);
+
+ MachineType type = AtomicOpType(node->op());
+ ArchOpcode opcode = kArchNop;
+ if (type == MachineType::Int8()) {
+ opcode = kWord32AtomicCompareExchangeInt8;
+ } else if (type == MachineType::Uint8()) {
+ opcode = kWord32AtomicCompareExchangeUint8;
+ } else if (type == MachineType::Int16()) {
+ opcode = kWord32AtomicCompareExchangeInt16;
+ } else if (type == MachineType::Uint16()) {
+ opcode = kWord32AtomicCompareExchangeUint16;
+ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
+ opcode = kWord32AtomicCompareExchangeWord32;
+ } else {
+ UNREACHABLE();
+ return;
+ }
+ AddressingMode addressing_mode;
+ InstructionOperand new_val_operand =
+ (type.representation() == MachineRepresentation::kWord8)
+ ? g.UseByteRegister(new_value)
+ : g.UseUniqueRegister(new_value);
+ InstructionOperand inputs[] = {
+ g.UseFixed(old_value, eax), new_val_operand, g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ InstructionOperand outputs[] = {g.DefineAsFixed(node, eax)};
+ InstructionCode code = opcode | AddressingModeField::encode(addressing_mode);
+ Emit(code, 1, outputs, arraysize(inputs), inputs);
+}
+
+void InstructionSelector::VisitWord32AtomicBinaryOperation(
+ Node* node, ArchOpcode int8_op, ArchOpcode uint8_op, ArchOpcode int16_op,
+ ArchOpcode uint16_op, ArchOpcode word32_op) {
+ MachineType type = AtomicOpType(node->op());
+ ArchOpcode opcode = kArchNop;
+ if (type == MachineType::Int8()) {
+ opcode = int8_op;
+ } else if (type == MachineType::Uint8()) {
+ opcode = uint8_op;
+ } else if (type == MachineType::Int16()) {
+ opcode = int16_op;
+ } else if (type == MachineType::Uint16()) {
+ opcode = uint16_op;
+ } else if (type == MachineType::Int32() || type == MachineType::Uint32()) {
+ opcode = word32_op;
+ } else {
+ UNREACHABLE();
+ return;
+ }
+ VisitAtomicBinOp(this, node, opcode, type.representation());
+}
+
+#define VISIT_ATOMIC_BINOP(op) \
+ void InstructionSelector::VisitWord32Atomic##op(Node* node) { \
+ VisitWord32AtomicBinaryOperation( \
+ node, kWord32Atomic##op##Int8, kWord32Atomic##op##Uint8, \
+ kWord32Atomic##op##Int16, kWord32Atomic##op##Uint16, \
+ kWord32Atomic##op##Word32); \
+ }
+VISIT_ATOMIC_BINOP(Add)
+VISIT_ATOMIC_BINOP(Sub)
+VISIT_ATOMIC_BINOP(And)
+VISIT_ATOMIC_BINOP(Or)
+VISIT_ATOMIC_BINOP(Xor)
+#undef VISIT_ATOMIC_BINOP
+
+void InstructionSelector::VisitWord32AtomicPairLoad(Node* node) {
+ IA32OperandGenerator g(this);
+ AddressingMode mode;
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ InstructionOperand inputs[] = {g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &mode)};
+ Node* projection0 = NodeProperties::FindProjection(node, 0);
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ InstructionCode code =
+ kIA32Word32AtomicPairLoad | AddressingModeField::encode(mode);
+
+ if (projection1) {
+ InstructionOperand temps[] = {g.TempDoubleRegister()};
+ InstructionOperand outputs[] = {g.DefineAsRegister(projection0),
+ g.DefineAsRegister(projection1)};
+ Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ arraysize(temps), temps);
+ } else if (projection0) {
+ InstructionOperand temps[] = {g.TempDoubleRegister(), g.TempRegister()};
+ InstructionOperand outputs[] = {g.DefineAsRegister(projection0)};
+ Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ arraysize(temps), temps);
+ } else {
+ InstructionOperand temps[] = {g.TempDoubleRegister(), g.TempRegister(),
+ g.TempRegister()};
+ Emit(code, 0, nullptr, arraysize(inputs), inputs, arraysize(temps), temps);
+ }
+}
+
+void InstructionSelector::VisitWord32AtomicPairStore(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* base = node->InputAt(0);
+ Node* index = node->InputAt(1);
+ Node* value = node->InputAt(2);
+ Node* value_high = node->InputAt(3);
+
+ AddressingMode addressing_mode;
+ InstructionOperand inputs[] = {
+ g.UseUniqueRegisterOrSlotOrConstant(value), g.UseFixed(value_high, ecx),
+ g.UseUniqueRegister(base),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ // Allocating temp registers here as stores are performed using an atomic
+ // exchange, the output of which is stored in edx:eax, which should be saved
+ // and restored at the end of the instruction.
+ InstructionOperand temps[] = {g.TempRegister(eax), g.TempRegister(edx)};
+ const int num_temps = arraysize(temps);
+ InstructionCode code =
+ kIA32Word32AtomicPairStore | AddressingModeField::encode(addressing_mode);
+ Emit(code, 0, nullptr, arraysize(inputs), inputs, num_temps, temps);
+}
+
+void InstructionSelector::VisitWord32AtomicPairAdd(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairAdd);
+}
+
+void InstructionSelector::VisitWord32AtomicPairSub(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairSub);
+}
+
+void InstructionSelector::VisitWord32AtomicPairAnd(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairAnd);
+}
+
+void InstructionSelector::VisitWord32AtomicPairOr(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairOr);
+}
+
+void InstructionSelector::VisitWord32AtomicPairXor(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairXor);
+}
+
+void InstructionSelector::VisitWord32AtomicPairExchange(Node* node) {
+ VisitPairAtomicBinOp(this, node, kIA32Word32AtomicPairExchange);
+}
+
+void InstructionSelector::VisitWord32AtomicPairCompareExchange(Node* node) {
+ IA32OperandGenerator g(this);
+ Node* index = node->InputAt(1);
+ AddressingMode addressing_mode;
+
+ InstructionOperand inputs[] = {
+ // High, Low values of old value
+ g.UseFixed(node->InputAt(2), eax), g.UseFixed(node->InputAt(3), edx),
+ // High, Low values of new value
+ g.UseUniqueRegisterOrSlotOrConstant(node->InputAt(4)),
+ g.UseFixed(node->InputAt(5), ecx),
+ // InputAt(0) => base
+ g.UseUniqueRegister(node->InputAt(0)),
+ g.GetEffectiveIndexOperand(index, &addressing_mode)};
+ Node* projection0 = NodeProperties::FindProjection(node, 0);
+ Node* projection1 = NodeProperties::FindProjection(node, 1);
+ InstructionCode code = kIA32Word32AtomicPairCompareExchange |
+ AddressingModeField::encode(addressing_mode);
+
+ if (projection1) {
+ InstructionOperand outputs[] = {g.DefineAsFixed(projection0, eax),
+ g.DefineAsFixed(projection1, edx)};
+ Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs, 0, {});
+ } else if (projection0) {
+ InstructionOperand outputs[] = {g.DefineAsFixed(projection0, eax)};
+ InstructionOperand temps[] = {g.TempRegister(edx)};
+ const int num_temps = arraysize(temps);
+ Emit(code, arraysize(outputs), outputs, arraysize(inputs), inputs,
+ num_temps, temps);
+ } else {
+ InstructionOperand temps[] = {g.TempRegister(eax), g.TempRegister(edx)};
+ const int num_temps = arraysize(temps);
+ Emit(code, 0, nullptr, arraysize(inputs), inputs, num_temps, temps);
+ }
+}
+
+#define SIMD_INT_TYPES(V) \
+ V(I32x4) \
+ V(I16x8) \
+ V(I8x16)
+
+#define SIMD_BINOP_LIST(V) \
+ V(F32x4Add) \
+ V(F32x4AddHoriz) \
+ V(F32x4Sub) \
+ V(F32x4Mul) \
+ V(F32x4Min) \
+ V(F32x4Max) \
+ V(F32x4Eq) \
+ V(F32x4Ne) \
+ V(F32x4Lt) \
+ V(F32x4Le) \
+ V(I32x4Add) \
+ V(I32x4AddHoriz) \
+ V(I32x4Sub) \
+ V(I32x4Mul) \
+ V(I32x4MinS) \
+ V(I32x4MaxS) \
+ V(I32x4Eq) \
+ V(I32x4Ne) \
+ V(I32x4GtS) \
+ V(I32x4GeS) \
+ V(I32x4MinU) \
+ V(I32x4MaxU) \
+ V(I32x4GtU) \
+ V(I32x4GeU) \
+ V(I16x8SConvertI32x4) \
+ V(I16x8Add) \
+ V(I16x8AddSaturateS) \
+ V(I16x8AddHoriz) \
+ V(I16x8Sub) \
+ V(I16x8SubSaturateS) \
+ V(I16x8Mul) \
+ V(I16x8MinS) \
+ V(I16x8MaxS) \
+ V(I16x8Eq) \
+ V(I16x8Ne) \
+ V(I16x8GtS) \
+ V(I16x8GeS) \
+ V(I16x8AddSaturateU) \
+ V(I16x8SubSaturateU) \
+ V(I16x8MinU) \
+ V(I16x8MaxU) \
+ V(I16x8GtU) \
+ V(I16x8GeU) \
+ V(I8x16SConvertI16x8) \
+ V(I8x16Add) \
+ V(I8x16AddSaturateS) \
+ V(I8x16Sub) \
+ V(I8x16SubSaturateS) \
+ V(I8x16MinS) \
+ V(I8x16MaxS) \
+ V(I8x16Eq) \
+ V(I8x16Ne) \
+ V(I8x16GtS) \
+ V(I8x16GeS) \
+ V(I8x16AddSaturateU) \
+ V(I8x16SubSaturateU) \
+ V(I8x16MinU) \
+ V(I8x16MaxU) \
+ V(I8x16GtU) \
+ V(I8x16GeU) \
+ V(S128And) \
+ V(S128Or) \
+ V(S128Xor)
+
+#define SIMD_UNOP_LIST(V) \
+ V(F32x4SConvertI32x4) \
+ V(F32x4RecipApprox) \
+ V(F32x4RecipSqrtApprox) \
+ V(I32x4SConvertI16x8Low) \
+ V(I32x4SConvertI16x8High) \
+ V(I32x4Neg) \
+ V(I32x4UConvertI16x8Low) \
+ V(I32x4UConvertI16x8High) \
+ V(I16x8SConvertI8x16Low) \
+ V(I16x8SConvertI8x16High) \
+ V(I16x8Neg) \
+ V(I16x8UConvertI8x16Low) \
+ V(I16x8UConvertI8x16High) \
+ V(I8x16Neg)
+
+#define SIMD_UNOP_PREFIX_LIST(V) \
+ V(F32x4Abs) \
+ V(F32x4Neg) \
+ V(S128Not)
+
+#define SIMD_ANYTRUE_LIST(V) \
+ V(S1x4AnyTrue) \
+ V(S1x8AnyTrue) \
+ V(S1x16AnyTrue)
+
+#define SIMD_ALLTRUE_LIST(V) \
+ V(S1x4AllTrue) \
+ V(S1x8AllTrue) \
+ V(S1x16AllTrue)
+
+#define SIMD_SHIFT_OPCODES(V) \
+ V(I32x4Shl) \
+ V(I32x4ShrS) \
+ V(I32x4ShrU) \
+ V(I16x8Shl) \
+ V(I16x8ShrS) \
+ V(I16x8ShrU) \
+ V(I8x16Shl)
+
+#define SIMD_I8X16_RIGHT_SHIFT_OPCODES(V) \
+ V(I8x16ShrS) \
+ V(I8x16ShrU)
+
+void InstructionSelector::VisitF32x4Splat(Node* node) {
+ VisitRRSimd(this, node, kAVXF32x4Splat, kSSEF32x4Splat);
+}
+
+void InstructionSelector::VisitF32x4ExtractLane(Node* node) {
+ VisitRRISimd(this, node, kAVXF32x4ExtractLane, kSSEF32x4ExtractLane);
+}
+
+void InstructionSelector::VisitF32x4UConvertI32x4(Node* node) {
+ VisitRRSimd(this, node, kAVXF32x4UConvertI32x4, kSSEF32x4UConvertI32x4);
+}
+
+void InstructionSelector::VisitI32x4SConvertF32x4(Node* node) {
+ VisitRRSimd(this, node, kAVXI32x4SConvertF32x4, kSSEI32x4SConvertF32x4);
+}
+
+void InstructionSelector::VisitI32x4UConvertF32x4(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand temps[] = {g.TempSimd128Register()};
+ InstructionCode opcode =
+ IsSupported(AVX) ? kAVXI32x4UConvertF32x4 : kSSEI32x4UConvertF32x4;
+ Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(node->InputAt(0)),
+ arraysize(temps), temps);
+}
+
+void InstructionSelector::VisitI8x16Mul(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand operand0 = g.UseUniqueRegister(node->InputAt(0));
+ InstructionOperand operand1 = g.UseUniqueRegister(node->InputAt(1));
+ InstructionOperand temps[] = {g.TempSimd128Register()};
+ if (IsSupported(AVX)) {
+ Emit(kAVXI8x16Mul, g.DefineAsRegister(node), operand0, operand1,
+ arraysize(temps), temps);
+ } else {
+ Emit(kSSEI8x16Mul, g.DefineSameAsFirst(node), operand0, operand1,
+ arraysize(temps), temps);
+ }
+}
+
+void InstructionSelector::VisitS128Zero(Node* node) {
+ IA32OperandGenerator g(this);
+ Emit(kIA32S128Zero, g.DefineAsRegister(node));
+}
+
+void InstructionSelector::VisitS128Select(Node* node) {
+ IA32OperandGenerator g(this);
+ InstructionOperand operand2 = g.UseRegister(node->InputAt(2));
+ if (IsSupported(AVX)) {
+ Emit(kAVXS128Select, g.DefineAsRegister(node), g.Use(node->InputAt(0)),
+ g.Use(node->InputAt(1)), operand2);
+ } else {
+ Emit(kSSES128Select, g.DefineSameAsFirst(node),
+ g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(1)),
+ operand2);
+ }
+}
+
+#define VISIT_SIMD_SPLAT(Type) \
+ void InstructionSelector::Visit##Type##Splat(Node* node) { \
+ VisitRO(this, node, kIA32##Type##Splat); \
+ }
+SIMD_INT_TYPES(VISIT_SIMD_SPLAT)
+#undef VISIT_SIMD_SPLAT
+
+#define VISIT_SIMD_EXTRACT_LANE(Type) \
+ void InstructionSelector::Visit##Type##ExtractLane(Node* node) { \
+ VisitRRISimd(this, node, kIA32##Type##ExtractLane); \
+ }
+SIMD_INT_TYPES(VISIT_SIMD_EXTRACT_LANE)
+#undef VISIT_SIMD_EXTRACT_LANE
+
+#define VISIT_SIMD_REPLACE_LANE(Type) \
+ void InstructionSelector::Visit##Type##ReplaceLane(Node* node) { \
+ IA32OperandGenerator g(this); \
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0)); \
+ InstructionOperand operand1 = \
+ g.UseImmediate(OpParameter<int32_t>(node->op())); \
+ InstructionOperand operand2 = g.Use(node->InputAt(1)); \
+ if (IsSupported(AVX)) { \
+ Emit(kAVX##Type##ReplaceLane, g.DefineAsRegister(node), operand0, \
+ operand1, operand2); \
+ } else { \
+ Emit(kSSE##Type##ReplaceLane, g.DefineSameAsFirst(node), operand0, \
+ operand1, operand2); \
+ } \
+ }
+SIMD_INT_TYPES(VISIT_SIMD_REPLACE_LANE)
+VISIT_SIMD_REPLACE_LANE(F32x4)
+#undef VISIT_SIMD_REPLACE_LANE
+#undef SIMD_INT_TYPES
+
+#define VISIT_SIMD_SHIFT(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ VisitRRISimd(this, node, kAVX##Opcode, kSSE##Opcode); \
+ }
+SIMD_SHIFT_OPCODES(VISIT_SIMD_SHIFT)
+#undef VISIT_SIMD_SHIFT
+#undef SIMD_SHIFT_OPCODES
+
+#define VISIT_SIMD_I8X16_RIGHT_SHIFT(Op) \
+ void InstructionSelector::Visit##Op(Node* node) { \
+ VisitRRISimd(this, node, kIA32##Op); \
+ }
+
+SIMD_I8X16_RIGHT_SHIFT_OPCODES(VISIT_SIMD_I8X16_RIGHT_SHIFT)
+#undef SIMD_I8X16_RIGHT_SHIFT_OPCODES
+#undef VISIT_SIMD_I8X16_RIGHT_SHIFT
+
+#define VISIT_SIMD_UNOP(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ IA32OperandGenerator g(this); \
+ Emit(kIA32##Opcode, g.DefineAsRegister(node), g.Use(node->InputAt(0))); \
+ }
+SIMD_UNOP_LIST(VISIT_SIMD_UNOP)
+#undef VISIT_SIMD_UNOP
+#undef SIMD_UNOP_LIST
+
+#define VISIT_SIMD_UNOP_PREFIX(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ IA32OperandGenerator g(this); \
+ InstructionCode opcode = IsSupported(AVX) ? kAVX##Opcode : kSSE##Opcode; \
+ Emit(opcode, g.DefineAsRegister(node), g.Use(node->InputAt(0))); \
+ }
+SIMD_UNOP_PREFIX_LIST(VISIT_SIMD_UNOP_PREFIX)
+#undef VISIT_SIMD_UNOP_PREFIX
+#undef SIMD_UNOP_PREFIX_LIST
+
+#define VISIT_SIMD_ANYTRUE(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ IA32OperandGenerator g(this); \
+ InstructionOperand temps[] = {g.TempRegister()}; \
+ Emit(kIA32##Opcode, g.DefineAsRegister(node), \
+ g.UseRegister(node->InputAt(0)), arraysize(temps), temps); \
+ }
+SIMD_ANYTRUE_LIST(VISIT_SIMD_ANYTRUE)
+#undef VISIT_SIMD_ANYTRUE
+#undef SIMD_ANYTRUE_LIST
+
+#define VISIT_SIMD_ALLTRUE(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ IA32OperandGenerator g(this); \
+ InstructionOperand temps[] = {g.TempRegister()}; \
+ Emit(kIA32##Opcode, g.DefineAsRegister(node), g.Use(node->InputAt(0)), \
+ arraysize(temps), temps); \
+ }
+SIMD_ALLTRUE_LIST(VISIT_SIMD_ALLTRUE)
+#undef VISIT_SIMD_ALLTRUE
+#undef SIMD_ALLTRUE_LIST
+
+#define VISIT_SIMD_BINOP(Opcode) \
+ void InstructionSelector::Visit##Opcode(Node* node) { \
+ VisitRROFloat(this, node, kAVX##Opcode, kSSE##Opcode); \
+ }
+SIMD_BINOP_LIST(VISIT_SIMD_BINOP)
+#undef VISIT_SIMD_BINOP
+#undef SIMD_BINOP_LIST
+
+void VisitPack(InstructionSelector* selector, Node* node, ArchOpcode avx_opcode,
+ ArchOpcode sse_opcode) {
+ IA32OperandGenerator g(selector);
+ InstructionOperand operand0 = g.UseRegister(node->InputAt(0));
+ InstructionOperand operand1 = g.Use(node->InputAt(1));
+ if (selector->IsSupported(AVX)) {
+ selector->Emit(avx_opcode, g.DefineSameAsFirst(node), operand0, operand1);
+ } else {
+ selector->Emit(sse_opcode, g.DefineSameAsFirst(node), operand0, operand1);
+ }
+}
+
+void InstructionSelector::VisitI16x8UConvertI32x4(Node* node) {
+ VisitPack(this, node, kAVXI16x8UConvertI32x4, kSSEI16x8UConvertI32x4);
+}
+
+void InstructionSelector::VisitI8x16UConvertI16x8(Node* node) {
+ VisitPack(this, node, kAVXI8x16UConvertI16x8, kSSEI8x16UConvertI16x8);
+}
+
+void InstructionSelector::VisitInt32AbsWithOverflow(Node* node) {
+ UNREACHABLE();
+}
+
+void InstructionSelector::VisitInt64AbsWithOverflow(Node* node) {
+ UNREACHABLE();
+}
+
+namespace {
+
+// Packs a 4 lane shuffle into a single imm8 suitable for use by pshufd,
+// pshuflw, and pshufhw.
+uint8_t PackShuffle4(uint8_t* shuffle) {
+ return (shuffle[0] & 3) | ((shuffle[1] & 3) << 2) | ((shuffle[2] & 3) << 4) |
+ ((shuffle[3] & 3) << 6);
+}
+
+// Gets an 8 bit lane mask suitable for 16x8 pblendw.
+uint8_t PackBlend8(const uint8_t* shuffle16x8) {
+ int8_t result = 0;
+ for (int i = 0; i < 8; ++i) {
+ result |= (shuffle16x8[i] >= 8 ? 1 : 0) << i;
+ }
+ return result;
+}
+
+// Gets an 8 bit lane mask suitable for 32x4 pblendw.
+uint8_t PackBlend4(const uint8_t* shuffle32x4) {
+ int8_t result = 0;
+ for (int i = 0; i < 4; ++i) {
+ result |= (shuffle32x4[i] >= 4 ? 0x3 : 0) << (i * 2);
+ }
+ return result;
+}
+
+// Returns true if shuffle can be decomposed into two 16x4 half shuffles
+// followed by a 16x8 blend.
+// E.g. [3 2 1 0 15 14 13 12].
+bool TryMatch16x8HalfShuffle(uint8_t* shuffle16x8, uint8_t* blend_mask) {
+ *blend_mask = 0;
+ for (int i = 0; i < 8; i++) {
+ if ((shuffle16x8[i] & 0x4) != (i & 0x4)) return false;
+ *blend_mask |= (shuffle16x8[i] > 7 ? 1 : 0) << i;
+ }
+ return true;
+}
+
+struct ShuffleEntry {
+ uint8_t shuffle[kSimd128Size];
+ ArchOpcode opcode;
+ ArchOpcode avx_opcode;
+ bool src0_needs_reg;
+ bool src1_needs_reg;
+};
+
+// Shuffles that map to architecture-specific instruction sequences. These are
+// matched very early, so we shouldn't include shuffles that match better in
+// later tests, like 32x4 and 16x8 shuffles. In general, these patterns should
+// map to either a single instruction, or be finer grained, such as zip/unzip or
+// transpose patterns.
+static const ShuffleEntry arch_shuffles[] = {
+ {{0, 1, 2, 3, 4, 5, 6, 7, 16, 17, 18, 19, 20, 21, 22, 23},
+ kIA32S64x2UnpackLow,
+ kIA32S64x2UnpackLow,
+ true,
+ false},
+ {{8, 9, 10, 11, 12, 13, 14, 15, 24, 25, 26, 27, 28, 29, 30, 31},
+ kIA32S64x2UnpackHigh,
+ kIA32S64x2UnpackHigh,
+ true,
+ false},
+ {{0, 1, 2, 3, 16, 17, 18, 19, 4, 5, 6, 7, 20, 21, 22, 23},
+ kIA32S32x4UnpackLow,
+ kIA32S32x4UnpackLow,
+ true,
+ false},
+ {{8, 9, 10, 11, 24, 25, 26, 27, 12, 13, 14, 15, 28, 29, 30, 31},
+ kIA32S32x4UnpackHigh,
+ kIA32S32x4UnpackHigh,
+ true,
+ false},
+ {{0, 1, 16, 17, 2, 3, 18, 19, 4, 5, 20, 21, 6, 7, 22, 23},
+ kIA32S16x8UnpackLow,
+ kIA32S16x8UnpackLow,
+ true,
+ false},
+ {{8, 9, 24, 25, 10, 11, 26, 27, 12, 13, 28, 29, 14, 15, 30, 31},
+ kIA32S16x8UnpackHigh,
+ kIA32S16x8UnpackHigh,
+ true,
+ false},
+ {{0, 16, 1, 17, 2, 18, 3, 19, 4, 20, 5, 21, 6, 22, 7, 23},
+ kIA32S8x16UnpackLow,
+ kIA32S8x16UnpackLow,
+ true,
+ false},
+ {{8, 24, 9, 25, 10, 26, 11, 27, 12, 28, 13, 29, 14, 30, 15, 31},
+ kIA32S8x16UnpackHigh,
+ kIA32S8x16UnpackHigh,
+ true,
+ false},
+
+ {{0, 1, 4, 5, 8, 9, 12, 13, 16, 17, 20, 21, 24, 25, 28, 29},
+ kSSES16x8UnzipLow,
+ kAVXS16x8UnzipLow,
+ true,
+ false},
+ {{2, 3, 6, 7, 10, 11, 14, 15, 18, 19, 22, 23, 26, 27, 30, 31},
+ kSSES16x8UnzipHigh,
+ kAVXS16x8UnzipHigh,
+ true,
+ true},
+ {{0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30},
+ kSSES8x16UnzipLow,
+ kAVXS8x16UnzipLow,
+ true,
+ true},
+ {{1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31},
+ kSSES8x16UnzipHigh,
+ kAVXS8x16UnzipHigh,
+ true,
+ true},
+
+ {{0, 16, 2, 18, 4, 20, 6, 22, 8, 24, 10, 26, 12, 28, 14, 30},
+ kSSES8x16TransposeLow,
+ kAVXS8x16TransposeLow,
+ true,
+ true},
+ {{1, 17, 3, 19, 5, 21, 7, 23, 9, 25, 11, 27, 13, 29, 15, 31},
+ kSSES8x16TransposeHigh,
+ kAVXS8x16TransposeHigh,
+ true,
+ true},
+ {{7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8},
+ kSSES8x8Reverse,
+ kAVXS8x8Reverse,
+ false,
+ false},
+ {{3, 2, 1, 0, 7, 6, 5, 4, 11, 10, 9, 8, 15, 14, 13, 12},
+ kSSES8x4Reverse,
+ kAVXS8x4Reverse,
+ false,
+ false},
+ {{1, 0, 3, 2, 5, 4, 7, 6, 9, 8, 11, 10, 13, 12, 15, 14},
+ kSSES8x2Reverse,
+ kAVXS8x2Reverse,
+ true,
+ true}};
+
+bool TryMatchArchShuffle(const uint8_t* shuffle, const ShuffleEntry* table,
+ size_t num_entries, bool is_swizzle,
+ const ShuffleEntry** arch_shuffle) {
+ uint8_t mask = is_swizzle ? kSimd128Size - 1 : 2 * kSimd128Size - 1;
+ for (size_t i = 0; i < num_entries; ++i) {
+ const ShuffleEntry& entry = table[i];
+ int j = 0;
+ for (; j < kSimd128Size; ++j) {
+ if ((entry.shuffle[j] & mask) != (shuffle[j] & mask)) {
+ break;
+ }
+ }
+ if (j == kSimd128Size) {
+ *arch_shuffle = &entry;
+ return true;
+ }
+ }
+ return false;
+}
+
+} // namespace
+
+void InstructionSelector::VisitS8x16Shuffle(Node* node) {
+ uint8_t shuffle[kSimd128Size];
+ bool is_swizzle;
+ CanonicalizeShuffle(node, shuffle, &is_swizzle);
+
+ int imm_count = 0;
+ static const int kMaxImms = 6;
+ uint32_t imms[kMaxImms];
+ int temp_count = 0;
+ static const int kMaxTemps = 2;
+ InstructionOperand temps[kMaxTemps];
+
+ IA32OperandGenerator g(this);
+ bool use_avx = CpuFeatures::IsSupported(AVX);
+ // AVX and swizzles don't generally need DefineSameAsFirst to avoid a move.
+ bool no_same_as_first = use_avx || is_swizzle;
+ // We generally need UseRegister for input0, Use for input1.
+ bool src0_needs_reg = true;
+ bool src1_needs_reg = false;
+ ArchOpcode opcode = kIA32S8x16Shuffle; // general shuffle is the default
+
+ uint8_t offset;
+ uint8_t shuffle32x4[4];
+ uint8_t shuffle16x8[8];
+ int index;
+ const ShuffleEntry* arch_shuffle;
+ if (TryMatchConcat(shuffle, &offset)) {
+ // Swap inputs from the normal order for (v)palignr.
+ SwapShuffleInputs(node);
+ is_swizzle = false; // It's simpler to just handle the general case.
+ no_same_as_first = use_avx; // SSE requires same-as-first.
+ opcode = kIA32S8x16Alignr;
+ // palignr takes a single imm8 offset.
+ imms[imm_count++] = offset;
+ } else if (TryMatchArchShuffle(shuffle, arch_shuffles,
+ arraysize(arch_shuffles), is_swizzle,
+ &arch_shuffle)) {
+ opcode = use_avx ? arch_shuffle->avx_opcode : arch_shuffle->opcode;
+ src0_needs_reg = !use_avx || arch_shuffle->src0_needs_reg;
+ // SSE can't take advantage of both operands in registers and needs
+ // same-as-first.
+ src1_needs_reg = use_avx && arch_shuffle->src1_needs_reg;
+ no_same_as_first = use_avx;
+ } else if (TryMatch32x4Shuffle(shuffle, shuffle32x4)) {
+ uint8_t shuffle_mask = PackShuffle4(shuffle32x4);
+ if (is_swizzle) {
+ if (TryMatchIdentity(shuffle)) {
+ // Bypass normal shuffle code generation in this case.
+ EmitIdentity(node);
+ return;
+ } else {
+ // pshufd takes a single imm8 shuffle mask.
+ opcode = kIA32S32x4Swizzle;
+ no_same_as_first = true;
+ src0_needs_reg = false;
+ imms[imm_count++] = shuffle_mask;
+ }
+ } else {
+ // 2 operand shuffle
+ // A blend is more efficient than a general 32x4 shuffle; try it first.
+ if (TryMatchBlend(shuffle)) {
+ opcode = kIA32S16x8Blend;
+ uint8_t blend_mask = PackBlend4(shuffle32x4);
+ imms[imm_count++] = blend_mask;
+ } else {
+ opcode = kIA32S32x4Shuffle;
+ no_same_as_first = true;
+ src0_needs_reg = false;
+ imms[imm_count++] = shuffle_mask;
+ int8_t blend_mask = PackBlend4(shuffle32x4);
+ imms[imm_count++] = blend_mask;
+ }
+ }
+ } else if (TryMatch16x8Shuffle(shuffle, shuffle16x8)) {
+ uint8_t blend_mask;
+ if (TryMatchBlend(shuffle)) {
+ opcode = kIA32S16x8Blend;
+ blend_mask = PackBlend8(shuffle16x8);
+ imms[imm_count++] = blend_mask;
+ } else if (TryMatchDup<8>(shuffle, &index)) {
+ opcode = kIA32S16x8Dup;
+ src0_needs_reg = false;
+ imms[imm_count++] = index;
+ } else if (TryMatch16x8HalfShuffle(shuffle16x8, &blend_mask)) {
+ opcode = is_swizzle ? kIA32S16x8HalfShuffle1 : kIA32S16x8HalfShuffle2;
+ // Half-shuffles don't need DefineSameAsFirst or UseRegister(src0).
+ no_same_as_first = true;
+ src0_needs_reg = false;
+ uint8_t mask_lo = PackShuffle4(shuffle16x8);
+ uint8_t mask_hi = PackShuffle4(shuffle16x8 + 4);
+ imms[imm_count++] = mask_lo;
+ imms[imm_count++] = mask_hi;
+ if (!is_swizzle) imms[imm_count++] = blend_mask;
+ }
+ } else if (TryMatchDup<16>(shuffle, &index)) {
+ opcode = kIA32S8x16Dup;
+ no_same_as_first = use_avx;
+ src0_needs_reg = true;
+ imms[imm_count++] = index;
+ }
+ if (opcode == kIA32S8x16Shuffle) {
+ // Use same-as-first for general swizzle, but not shuffle.
+ no_same_as_first = !is_swizzle;
+ src0_needs_reg = !no_same_as_first;
+ imms[imm_count++] = Pack4Lanes(shuffle);
+ imms[imm_count++] = Pack4Lanes(shuffle + 4);
+ imms[imm_count++] = Pack4Lanes(shuffle + 8);
+ imms[imm_count++] = Pack4Lanes(shuffle + 12);
+ temps[temp_count++] = g.TempRegister();
+ }
+
+ // Use DefineAsRegister(node) and Use(src0) if we can without forcing an extra
+ // move instruction in the CodeGenerator.
+ Node* input0 = node->InputAt(0);
+ InstructionOperand dst =
+ no_same_as_first ? g.DefineAsRegister(node) : g.DefineSameAsFirst(node);
+ InstructionOperand src0 =
+ src0_needs_reg ? g.UseRegister(input0) : g.Use(input0);
+
+ int input_count = 0;
+ InstructionOperand inputs[2 + kMaxImms + kMaxTemps];
+ inputs[input_count++] = src0;
+ if (!is_swizzle) {
+ Node* input1 = node->InputAt(1);
+ inputs[input_count++] =
+ src1_needs_reg ? g.UseRegister(input1) : g.Use(input1);
+ }
+ for (int i = 0; i < imm_count; ++i) {
+ inputs[input_count++] = g.UseImmediate(imms[i]);
+ }
+ Emit(opcode, 1, &dst, input_count, inputs, temp_count, temps);
+}
+
+// static
+MachineOperatorBuilder::Flags
+InstructionSelector::SupportedMachineOperatorFlags() {
+ MachineOperatorBuilder::Flags flags =
+ MachineOperatorBuilder::kWord32ShiftIsSafe |
+ MachineOperatorBuilder::kWord32Ctz |
+ MachineOperatorBuilder::kSpeculationFence;
+ if (CpuFeatures::IsSupported(POPCNT)) {
+ flags |= MachineOperatorBuilder::kWord32Popcnt;
+ }
+ if (CpuFeatures::IsSupported(SSE4_1)) {
+ flags |= MachineOperatorBuilder::kFloat32RoundDown |
+ MachineOperatorBuilder::kFloat64RoundDown |
+ MachineOperatorBuilder::kFloat32RoundUp |
+ MachineOperatorBuilder::kFloat64RoundUp |
+ MachineOperatorBuilder::kFloat32RoundTruncate |
+ MachineOperatorBuilder::kFloat64RoundTruncate |
+ MachineOperatorBuilder::kFloat32RoundTiesEven |
+ MachineOperatorBuilder::kFloat64RoundTiesEven;
+ }
+ return flags;
+}
+
+// static
+MachineOperatorBuilder::AlignmentRequirements
+InstructionSelector::AlignmentRequirements() {
+ return MachineOperatorBuilder::AlignmentRequirements::
+ FullUnalignedAccessSupport();
+}
+
+} // namespace compiler
+} // namespace internal
+} // namespace v8