diff options
Diffstat (limited to 'deps/v8/src/compiler/arm64/instruction-selector-arm64.cc')
-rw-r--r-- | deps/v8/src/compiler/arm64/instruction-selector-arm64.cc | 402 |
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 | |