summaryrefslogtreecommitdiff
path: root/deps/v8/src/compiler/arm64/instruction-selector-arm64.cc
diff options
context:
space:
mode:
Diffstat (limited to 'deps/v8/src/compiler/arm64/instruction-selector-arm64.cc')
-rw-r--r--deps/v8/src/compiler/arm64/instruction-selector-arm64.cc402
1 files changed, 165 insertions, 237 deletions
diff --git a/deps/v8/src/compiler/arm64/instruction-selector-arm64.cc b/deps/v8/src/compiler/arm64/instruction-selector-arm64.cc
index 7a5b84275a..6abad0aa92 100644
--- a/deps/v8/src/compiler/arm64/instruction-selector-arm64.cc
+++ b/deps/v8/src/compiler/arm64/instruction-selector-arm64.cc
@@ -37,6 +37,15 @@ class Arm64OperandGenerator final : public OperandGenerator {
return UseRegister(node);
}
+ // Use the zero register if the node has the immediate value zero, otherwise
+ // assign a register.
+ InstructionOperand UseRegisterOrImmediateZero(Node* node) {
+ if (IsIntegerConstant(node) && (GetIntegerConstantValue(node) == 0)) {
+ return UseImmediate(node);
+ }
+ return UseRegister(node);
+ }
+
// Use the provided node if it has the required value, or create a
// TempImmediate otherwise.
InstructionOperand UseImmediateOrTemp(Node* node, int32_t value) {
@@ -214,14 +223,14 @@ void VisitBinop(InstructionSelector* selector, Node* node,
size_t input_count = 0;
InstructionOperand outputs[2];
size_t output_count = 0;
- bool is_cmp = opcode == kArm64Cmp32;
+ bool is_cmp = (opcode == kArm64Cmp32) || (opcode == kArm64Cmn32);
// We can commute cmp by switching the inputs and commuting the flags
// continuation.
bool can_commute = m.HasProperty(Operator::kCommutative) || is_cmp;
- // The cmp instruction is encoded as sub with zero output register, and
- // therefore supports the same operand modes.
+ // The cmp and cmn instructions are encoded as sub or add with zero output
+ // register, and therefore support the same operand modes.
bool is_add_sub = m.IsInt32Add() || m.IsInt64Add() || m.IsInt32Sub() ||
m.IsInt64Sub() || is_cmp;
@@ -247,18 +256,18 @@ void VisitBinop(InstructionSelector* selector, Node* node,
} else if (TryMatchAnyShift(selector, node, right_node, &opcode,
!is_add_sub)) {
Matcher m_shift(right_node);
- inputs[input_count++] = g.UseRegister(left_node);
+ inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
inputs[input_count++] = g.UseRegister(m_shift.left().node());
inputs[input_count++] = g.UseImmediate(m_shift.right().node());
} else if (can_commute && TryMatchAnyShift(selector, node, left_node, &opcode,
!is_add_sub)) {
if (is_cmp) cont->Commute();
Matcher m_shift(left_node);
- inputs[input_count++] = g.UseRegister(right_node);
+ inputs[input_count++] = g.UseRegisterOrImmediateZero(right_node);
inputs[input_count++] = g.UseRegister(m_shift.left().node());
inputs[input_count++] = g.UseImmediate(m_shift.right().node());
} else {
- inputs[input_count++] = g.UseRegister(left_node);
+ inputs[input_count++] = g.UseRegisterOrImmediateZero(left_node);
inputs[input_count++] = g.UseRegister(right_node);
}
@@ -384,58 +393,80 @@ void InstructionSelector::VisitStore(Node* node) {
Node* value = node->InputAt(2);
StoreRepresentation store_rep = OpParameter<StoreRepresentation>(node);
+ WriteBarrierKind write_barrier_kind = store_rep.write_barrier_kind();
MachineType rep = RepresentationOf(store_rep.machine_type());
- if (store_rep.write_barrier_kind() == kFullWriteBarrier) {
- DCHECK(rep == kRepTagged);
- // TODO(dcarney): refactor RecordWrite function to take temp registers
- // and pass them here instead of using fixed regs
- // TODO(dcarney): handle immediate indices.
- InstructionOperand temps[] = {g.TempRegister(x11), g.TempRegister(x12)};
- Emit(kArm64StoreWriteBarrier, g.NoOutput(), g.UseFixed(base, x10),
- g.UseFixed(index, x11), g.UseFixed(value, x12), arraysize(temps),
- temps);
- return;
- }
- DCHECK_EQ(kNoWriteBarrier, store_rep.write_barrier_kind());
- ArchOpcode opcode;
- ImmediateMode immediate_mode = kNoImmediate;
- switch (rep) {
- case kRepFloat32:
- opcode = kArm64StrS;
- immediate_mode = kLoadStoreImm32;
- break;
- case kRepFloat64:
- opcode = kArm64StrD;
- immediate_mode = kLoadStoreImm64;
- break;
- case kRepBit: // Fall through.
- case kRepWord8:
- opcode = kArm64Strb;
- immediate_mode = kLoadStoreImm8;
- break;
- case kRepWord16:
- opcode = kArm64Strh;
- immediate_mode = kLoadStoreImm16;
- break;
- case kRepWord32:
- opcode = kArm64StrW;
- immediate_mode = kLoadStoreImm32;
- break;
- case kRepTagged: // Fall through.
- case kRepWord64:
- opcode = kArm64Str;
- immediate_mode = kLoadStoreImm64;
- break;
- default:
- UNREACHABLE();
- return;
- }
- if (g.CanBeImmediate(index, immediate_mode)) {
- Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
- g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
+
+ // TODO(arm64): I guess this could be done in a better way.
+ if (write_barrier_kind != kNoWriteBarrier) {
+ DCHECK_EQ(kRepTagged, rep);
+ InstructionOperand inputs[3];
+ size_t input_count = 0;
+ inputs[input_count++] = g.UseUniqueRegister(base);
+ inputs[input_count++] = g.UseUniqueRegister(index);
+ inputs[input_count++] = (write_barrier_kind == kMapWriteBarrier)
+ ? g.UseRegister(value)
+ : 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 |= MiscField::encode(static_cast<int>(record_write_mode));
+ Emit(code, 0, nullptr, input_count, inputs, temp_count, temps);
} else {
- Emit(opcode | AddressingModeField::encode(kMode_MRR), g.NoOutput(),
- g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
+ ArchOpcode opcode;
+ ImmediateMode immediate_mode = kNoImmediate;
+ switch (rep) {
+ case kRepFloat32:
+ opcode = kArm64StrS;
+ immediate_mode = kLoadStoreImm32;
+ break;
+ case kRepFloat64:
+ opcode = kArm64StrD;
+ immediate_mode = kLoadStoreImm64;
+ break;
+ case kRepBit: // Fall through.
+ case kRepWord8:
+ opcode = kArm64Strb;
+ immediate_mode = kLoadStoreImm8;
+ break;
+ case kRepWord16:
+ opcode = kArm64Strh;
+ immediate_mode = kLoadStoreImm16;
+ break;
+ case kRepWord32:
+ opcode = kArm64StrW;
+ immediate_mode = kLoadStoreImm32;
+ break;
+ case kRepTagged: // Fall through.
+ case kRepWord64:
+ opcode = kArm64Str;
+ immediate_mode = kLoadStoreImm64;
+ break;
+ default:
+ UNREACHABLE();
+ return;
+ }
+ if (g.CanBeImmediate(index, immediate_mode)) {
+ Emit(opcode | AddressingModeField::encode(kMode_MRI), g.NoOutput(),
+ g.UseRegister(base), g.UseImmediate(index), g.UseRegister(value));
+ } else {
+ Emit(opcode | AddressingModeField::encode(kMode_MRR), g.NoOutput(),
+ g.UseRegister(base), g.UseRegister(index), g.UseRegister(value));
+ }
}
}
@@ -912,12 +943,30 @@ void InstructionSelector::VisitWord64Ror(Node* node) {
}
+void InstructionSelector::VisitWord64Clz(Node* node) {
+ Arm64OperandGenerator g(this);
+ Emit(kArm64Clz, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
+}
+
+
void InstructionSelector::VisitWord32Clz(Node* node) {
Arm64OperandGenerator g(this);
Emit(kArm64Clz32, g.DefineAsRegister(node), g.UseRegister(node->InputAt(0)));
}
+void InstructionSelector::VisitWord32Ctz(Node* node) { UNREACHABLE(); }
+
+
+void InstructionSelector::VisitWord64Ctz(Node* node) { UNREACHABLE(); }
+
+
+void InstructionSelector::VisitWord32Popcnt(Node* node) { UNREACHABLE(); }
+
+
+void InstructionSelector::VisitWord64Popcnt(Node* node) { UNREACHABLE(); }
+
+
void InstructionSelector::VisitInt32Add(Node* node) {
Arm64OperandGenerator g(this);
Int32BinopMatcher m(node);
@@ -997,12 +1046,7 @@ void InstructionSelector::VisitInt32Sub(Node* node) {
}
}
- if (m.left().Is(0)) {
- Emit(kArm64Neg32, g.DefineAsRegister(node),
- g.UseRegister(m.right().node()));
- } else {
- VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
- }
+ VisitAddSub<Int32BinopMatcher>(this, node, kArm64Sub32, kArm64Add32);
}
@@ -1023,11 +1067,7 @@ void InstructionSelector::VisitInt64Sub(Node* node) {
}
}
- if (m.left().Is(0)) {
- Emit(kArm64Neg, g.DefineAsRegister(node), g.UseRegister(m.right().node()));
- } else {
- VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
- }
+ VisitAddSub<Int64BinopMatcher>(this, node, kArm64Sub, kArm64Add);
}
@@ -1276,6 +1316,16 @@ void InstructionSelector::VisitTruncateInt64ToInt32(Node* node) {
}
+void InstructionSelector::VisitRoundInt64ToFloat32(Node* node) {
+ VisitRR(this, kArm64Int64ToFloat32, node);
+}
+
+
+void InstructionSelector::VisitRoundInt64ToFloat64(Node* node) {
+ VisitRR(this, kArm64Int64ToFloat64, node);
+}
+
+
void InstructionSelector::VisitBitcastFloat32ToInt32(Node* node) {
VisitRR(this, kArm64Float64ExtractLowWord32, node);
}
@@ -1363,16 +1413,24 @@ void InstructionSelector::VisitFloat64Mod(Node* node) {
}
-void InstructionSelector::VisitFloat32Max(Node* node) { UNREACHABLE(); }
+void InstructionSelector::VisitFloat32Max(Node* node) {
+ VisitRRR(this, kArm64Float32Max, node);
+}
-void InstructionSelector::VisitFloat64Max(Node* node) { UNREACHABLE(); }
+void InstructionSelector::VisitFloat64Max(Node* node) {
+ VisitRRR(this, kArm64Float64Max, node);
+}
-void InstructionSelector::VisitFloat32Min(Node* node) { UNREACHABLE(); }
+void InstructionSelector::VisitFloat32Min(Node* node) {
+ VisitRRR(this, kArm64Float32Min, node);
+}
-void InstructionSelector::VisitFloat64Min(Node* node) { UNREACHABLE(); }
+void InstructionSelector::VisitFloat64Min(Node* node) {
+ VisitRRR(this, kArm64Float64Min, node);
+}
void InstructionSelector::VisitFloat32Abs(Node* node) {
@@ -1410,26 +1468,13 @@ void InstructionSelector::VisitFloat64RoundTiesAway(Node* node) {
}
-void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
+void InstructionSelector::EmitPrepareArguments(NodeVector* arguments,
+ const CallDescriptor* descriptor,
+ Node* node) {
Arm64OperandGenerator g(this);
- const CallDescriptor* descriptor = OpParameter<const CallDescriptor*>(node);
-
- FrameStateDescriptor* frame_state_descriptor = nullptr;
- if (descriptor->NeedsFrameState()) {
- frame_state_descriptor = GetFrameStateDescriptor(
- node->InputAt(static_cast<int>(descriptor->InputCount())));
- }
-
- CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
-
- // Compute InstructionOperands for inputs and outputs.
- // TODO(turbofan): on ARM64 it's probably better to use the code object in a
- // register if there are multiple uses of it. Improve constant pool and the
- // heuristics in the register allocator for where to emit constants.
- InitializeCallBuffer(node, &buffer, true, true);
// Push the arguments to the stack.
- int aligned_push_count = static_cast<int>(buffer.pushed_nodes.size());
+ int aligned_push_count = static_cast<int>(arguments->size());
bool pushed_count_uneven = aligned_push_count & 1;
// TODO(dcarney): claim and poke probably take small immediates,
// loop here or whatever.
@@ -1444,163 +1489,21 @@ void InstructionSelector::VisitCall(Node* node, BasicBlock* handler) {
int slot = aligned_push_count - 1;
// Emit the uneven pushes.
if (pushed_count_uneven) {
- Node* input = buffer.pushed_nodes[slot];
+ Node* input = (*arguments)[slot];
Emit(kArm64Poke, g.NoOutput(), g.UseRegister(input),
g.TempImmediate(slot));
slot--;
}
// Now all pushes can be done in pairs.
for (; slot >= 0; slot -= 2) {
- Emit(kArm64PokePair, g.NoOutput(),
- g.UseRegister(buffer.pushed_nodes[slot]),
- g.UseRegister(buffer.pushed_nodes[slot - 1]),
- g.TempImmediate(slot));
- }
- }
-
- // Pass label of exception handler block.
- CallDescriptor::Flags flags = descriptor->flags();
- if (handler != nullptr) {
- DCHECK_EQ(IrOpcode::kIfException, handler->front()->opcode());
- IfExceptionHint hint = OpParameter<IfExceptionHint>(handler->front());
- if (hint == IfExceptionHint::kLocallyCaught) {
- flags |= CallDescriptor::kHasLocalCatchHandler;
+ Emit(kArm64PokePair, g.NoOutput(), g.UseRegister((*arguments)[slot]),
+ g.UseRegister((*arguments)[slot - 1]), g.TempImmediate(slot));
}
- flags |= CallDescriptor::kHasExceptionHandler;
- buffer.instruction_args.push_back(g.Label(handler));
}
-
- // Select the appropriate opcode based on the call type.
- InstructionCode opcode;
- switch (descriptor->kind()) {
- case CallDescriptor::kCallAddress:
- opcode =
- kArchCallCFunction |
- MiscField::encode(static_cast<int>(descriptor->CParameterCount()));
- break;
- case CallDescriptor::kCallCodeObject:
- opcode = kArchCallCodeObject | MiscField::encode(flags);
- break;
- case CallDescriptor::kCallJSFunction:
- opcode = kArchCallJSFunction | MiscField::encode(flags);
- break;
- default:
- UNREACHABLE();
- return;
- }
-
- // Emit the call instruction.
- size_t const output_count = buffer.outputs.size();
- auto* outputs = output_count ? &buffer.outputs.front() : nullptr;
- Emit(opcode, output_count, outputs, buffer.instruction_args.size(),
- &buffer.instruction_args.front())->MarkAsCall();
}
-void InstructionSelector::VisitTailCall(Node* node) {
- Arm64OperandGenerator g(this);
- const CallDescriptor* descriptor = OpParameter<const CallDescriptor*>(node);
- DCHECK_NE(0, descriptor->flags() & CallDescriptor::kSupportsTailCalls);
- DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kPatchableCallSite);
- DCHECK_EQ(0, descriptor->flags() & CallDescriptor::kNeedsNopAfterCall);
-
- // TODO(turbofan): Relax restriction for stack parameters.
- if (linkage()->GetIncomingDescriptor()->CanTailCall(node)) {
- CallBuffer buffer(zone(), descriptor, nullptr);
-
- // Compute InstructionOperands for inputs and outputs.
- // TODO(turbofan): on ARM64 it's probably better to use the code object in a
- // register if there are multiple uses of it. Improve constant pool and the
- // heuristics in the register allocator for where to emit constants.
- InitializeCallBuffer(node, &buffer, true, false);
-
- // Select the appropriate opcode based on the call type.
- InstructionCode opcode;
- switch (descriptor->kind()) {
- case CallDescriptor::kCallCodeObject:
- opcode = kArchTailCallCodeObject;
- break;
- case CallDescriptor::kCallJSFunction:
- opcode = kArchTailCallJSFunction;
- break;
- default:
- UNREACHABLE();
- return;
- }
- opcode |= MiscField::encode(descriptor->flags());
-
- // Emit the tailcall instruction.
- Emit(opcode, 0, nullptr, buffer.instruction_args.size(),
- &buffer.instruction_args.front());
- } else {
- FrameStateDescriptor* frame_state_descriptor = nullptr;
- if (descriptor->NeedsFrameState()) {
- frame_state_descriptor = GetFrameStateDescriptor(
- node->InputAt(static_cast<int>(descriptor->InputCount())));
- }
-
- CallBuffer buffer(zone(), descriptor, frame_state_descriptor);
-
- // Compute InstructionOperands for inputs and outputs.
- // TODO(turbofan): on ARM64 it's probably better to use the code object in a
- // register if there are multiple uses of it. Improve constant pool and the
- // heuristics in the register allocator for where to emit constants.
- InitializeCallBuffer(node, &buffer, true, false);
-
- // Push the arguments to the stack.
- int aligned_push_count = static_cast<int>(buffer.pushed_nodes.size());
- bool pushed_count_uneven = aligned_push_count & 1;
- // TODO(dcarney): claim and poke probably take small immediates,
- // loop here or whatever.
- // Bump the stack pointer(s).
- if (aligned_push_count > 0) {
- // TODO(dcarney): it would be better to bump the csp here only
- // and emit paired stores with increment for non c frames.
- Emit(kArm64Claim, g.NoOutput(), g.TempImmediate(aligned_push_count));
- }
- // Move arguments to the stack.
- {
- int slot = aligned_push_count - 1;
- // Emit the uneven pushes.
- if (pushed_count_uneven) {
- Node* input = buffer.pushed_nodes[slot];
- Emit(kArm64Poke, g.NoOutput(), g.UseRegister(input),
- g.TempImmediate(slot));
- slot--;
- }
- // Now all pushes can be done in pairs.
- for (; slot >= 0; slot -= 2) {
- Emit(kArm64PokePair, g.NoOutput(),
- g.UseRegister(buffer.pushed_nodes[slot]),
- g.UseRegister(buffer.pushed_nodes[slot - 1]),
- g.TempImmediate(slot));
- }
- }
-
- // Select the appropriate opcode based on the call type.
- InstructionCode opcode;
- switch (descriptor->kind()) {
- case CallDescriptor::kCallCodeObject: {
- opcode = kArchCallCodeObject;
- break;
- }
- case CallDescriptor::kCallJSFunction:
- opcode = kArchCallJSFunction;
- break;
- default:
- UNREACHABLE();
- return;
- }
- opcode |= MiscField::encode(descriptor->flags());
-
- // Emit the call instruction.
- size_t const output_count = buffer.outputs.size();
- auto* outputs = output_count ? &buffer.outputs.front() : nullptr;
- Emit(opcode, output_count, outputs, buffer.instruction_args.size(),
- &buffer.instruction_args.front())->MarkAsCall();
- Emit(kArchRet, 0, nullptr, output_count, outputs);
- }
-}
+bool InstructionSelector::IsTailCallAddressImmediate() { return false; }
namespace {
@@ -1646,8 +1549,29 @@ void VisitWordCompare(InstructionSelector* selector, Node* node,
void VisitWord32Compare(InstructionSelector* selector, Node* node,
FlagsContinuation* cont) {
- VisitBinop<Int32BinopMatcher>(selector, node, kArm64Cmp32, kArithmeticImm,
- cont);
+ Int32BinopMatcher m(node);
+ ArchOpcode opcode = kArm64Cmp32;
+
+ // Select negated compare for comparisons with negated right input.
+ if (m.right().IsInt32Sub()) {
+ Node* sub = m.right().node();
+ Int32BinopMatcher msub(sub);
+ if (msub.left().Is(0)) {
+ bool can_cover = selector->CanCover(node, sub);
+ node->ReplaceInput(1, msub.right().node());
+ // Even if the comparison node covers the subtraction, after the input
+ // replacement above, the node still won't cover the input to the
+ // subtraction; the subtraction still uses it.
+ // In order to get shifted operations to work, we must remove the rhs
+ // input to the subtraction, as TryMatchAnyShift requires this node to
+ // cover the input shift. We do this by setting it to the lhs input,
+ // as we know it's zero, and the result of the subtraction isn't used by
+ // any other node.
+ if (can_cover) sub->ReplaceInput(1, msub.left().node());
+ opcode = kArm64Cmn32;
+ }
+ }
+ VisitBinop<Int32BinopMatcher>(selector, node, opcode, kArithmeticImm, cont);
}
@@ -2107,7 +2031,11 @@ void InstructionSelector::VisitFloat64InsertHighWord32(Node* node) {
// static
MachineOperatorBuilder::Flags
InstructionSelector::SupportedMachineOperatorFlags() {
- return MachineOperatorBuilder::kFloat64RoundDown |
+ return MachineOperatorBuilder::kFloat32Max |
+ MachineOperatorBuilder::kFloat32Min |
+ MachineOperatorBuilder::kFloat64Max |
+ MachineOperatorBuilder::kFloat64Min |
+ MachineOperatorBuilder::kFloat64RoundDown |
MachineOperatorBuilder::kFloat64RoundTruncate |
MachineOperatorBuilder::kFloat64RoundTiesAway |
MachineOperatorBuilder::kWord32ShiftIsSafe |