Index: src/compiler/s390/instruction-selector-s390.cc |
diff --git a/src/compiler/s390/instruction-selector-s390.cc b/src/compiler/s390/instruction-selector-s390.cc |
index b469415d2e98b5e7d10ec6de33159202f56cf956..d5d5e079fa09546219ff74bbaa4a8cbfffc2c4f4 100644 |
--- a/src/compiler/s390/instruction-selector-s390.cc |
+++ b/src/compiler/s390/instruction-selector-s390.cc |
@@ -12,23 +12,61 @@ namespace v8 { |
namespace internal { |
namespace compiler { |
-enum ImmediateMode { |
- kShift32Imm, |
- kShift64Imm, |
- kInt32Imm, |
- kInt32Imm_Negate, |
- kUint32Imm, |
- kInt20Imm, |
- kNoImmediate |
+enum class OperandMode : uint32_t { |
+ kNone = 0u, |
+ // Immediate mode |
+ kShift32Imm = 1u << 0, |
+ kShift64Imm = 1u << 1, |
+ kInt32Imm = 1u << 2, |
+ kInt32Imm_Negate = 1u << 3, |
+ kUint32Imm = 1u << 4, |
+ kInt20Imm = 1u << 5, |
+ // Instr format |
+ kAllowRRR = 1u << 7, |
+ kAllowRM = 1u << 8, |
+ kAllowRI = 1u << 9, |
+ kAllowRRI = 1u << 10, |
+ kAllowRRM = 1u << 11, |
+ // Useful combination |
+ kAllowImmediate = kAllowRI | kAllowRRI, |
+ kAllowMemoryOperand = kAllowRM | kAllowRRM, |
+ kAllowDistinctOps = kAllowRRR | kAllowRRI | kAllowRRM, |
+ kBitWiseCommonMode = kAllowRI | kUint32Imm, |
+ kArithmeticCommonMode = kAllowRM | kAllowRI |
}; |
+typedef base::Flags<OperandMode, uint32_t> OperandModes; |
+DEFINE_OPERATORS_FOR_FLAGS(OperandModes); |
+OperandModes immediateModeMask = |
+ OperandMode::kShift32Imm | OperandMode::kShift64Imm | |
+ OperandMode::kInt32Imm | OperandMode::kInt32Imm_Negate | |
+ OperandMode::kUint32Imm | OperandMode::kInt20Imm; |
+ |
+#define BitWiseOperandMode \ |
+ ((OperandMode::kBitWiseCommonMode | \ |
+ (CpuFeatures::IsSupported(DISTINCT_OPS) \ |
+ ? OperandMode::kAllowRRR \ |
+ : OperandMode::kBitWiseCommonMode))) |
+#define AddOperandMode \ |
+ ((OperandMode::kArithmeticCommonMode | OperandMode::kInt32Imm | \ |
+ (CpuFeatures::IsSupported(DISTINCT_OPS) \ |
+ ? (OperandMode::kAllowRRR | OperandMode::kAllowRRI) \ |
+ : OperandMode::kArithmeticCommonMode))) |
+#define SubOperandMode \ |
+ ((OperandMode::kArithmeticCommonMode | OperandMode::kInt32Imm_Negate | \ |
+ (CpuFeatures::IsSupported(DISTINCT_OPS) \ |
+ ? (OperandMode::kAllowRRR | OperandMode::kAllowRRI) \ |
+ : OperandMode::kArithmeticCommonMode))) |
+#define MulOperandMode \ |
+ (OperandMode::kArithmeticCommonMode | OperandMode::kInt32Imm) |
+ |
// Adds S390-specific methods for generating operands. |
class S390OperandGenerator final : public OperandGenerator { |
public: |
explicit S390OperandGenerator(InstructionSelector* selector) |
: OperandGenerator(selector) {} |
- InstructionOperand UseOperand(Node* node, ImmediateMode mode) { |
+ InstructionOperand UseOperand(Node* node, OperandModes mode) { |
if (CanBeImmediate(node, mode)) { |
return UseImmediate(node); |
} |
@@ -45,7 +83,7 @@ class S390OperandGenerator final : public OperandGenerator { |
return 0L; |
} |
- bool CanBeImmediate(Node* node, ImmediateMode mode) { |
+ bool CanBeImmediate(Node* node, OperandModes mode) { |
int64_t value; |
if (node->opcode() == IrOpcode::kInt32Constant) |
value = OpParameter<int32_t>(node); |
@@ -56,24 +94,21 @@ class S390OperandGenerator final : public OperandGenerator { |
return CanBeImmediate(value, mode); |
} |
- bool CanBeImmediate(int64_t value, ImmediateMode mode) { |
- switch (mode) { |
- case kShift32Imm: |
- return 0 <= value && value < 32; |
- case kShift64Imm: |
- return 0 <= value && value < 64; |
- case kInt32Imm: |
- return is_int32(value); |
- case kInt32Imm_Negate: |
- return is_int32(-value); |
- case kUint32Imm: |
- return is_uint32(value); |
- case kInt20Imm: |
- return is_int20(value); |
- case kNoImmediate: |
- return false; |
- } |
- return false; |
+ bool CanBeImmediate(int64_t value, OperandModes mode) { |
+ if (mode & OperandMode::kShift32Imm) |
+ return 0 <= value && value < 32; |
+ else if (mode & OperandMode::kShift64Imm) |
+ return 0 <= value && value < 64; |
+ else if (mode & OperandMode::kInt32Imm) |
+ return is_int32(value); |
+ else if (mode & OperandMode::kInt32Imm_Negate) |
+ return is_int32(-value); |
+ else if (mode & OperandMode::kUint32Imm) |
+ return is_uint32(value); |
+ else if (mode & OperandMode::kInt20Imm) |
+ return is_int20(value); |
+ else |
+ return false; |
} |
AddressingMode GenerateMemoryOperandInputs(Node* index, Node* base, |
@@ -131,7 +166,7 @@ class S390OperandGenerator final : public OperandGenerator { |
#endif |
DCHECK(m.matches()); |
if ((m.displacement() == nullptr || |
- CanBeImmediate(m.displacement(), kInt20Imm))) { |
+ CanBeImmediate(m.displacement(), OperandMode::kInt20Imm))) { |
DCHECK(m.scale() == 0); |
return GenerateMemoryOperandInputs(m.index(), m.base(), m.displacement(), |
m.displacement_mode(), inputs, |
@@ -158,6 +193,127 @@ class S390OperandGenerator final : public OperandGenerator { |
namespace { |
+ArchOpcode SelectLoadOpcode(Node* node) { |
+ NodeMatcher m(node); |
+ DCHECK(m.IsLoad()); |
+ LoadRepresentation load_rep = LoadRepresentationOf(node->op()); |
+ ArchOpcode opcode = kArchNop; |
+ switch (load_rep.representation()) { |
+ case MachineRepresentation::kFloat32: |
+ opcode = kS390_LoadFloat32; |
+ break; |
+ case MachineRepresentation::kFloat64: |
+ opcode = kS390_LoadDouble; |
+ break; |
+ case MachineRepresentation::kBit: // Fall through. |
+ case MachineRepresentation::kWord8: |
+ opcode = load_rep.IsSigned() ? kS390_LoadWordS8 : kS390_LoadWordU8; |
+ break; |
+ case MachineRepresentation::kWord16: |
+ opcode = load_rep.IsSigned() ? kS390_LoadWordS16 : kS390_LoadWordU16; |
+ break; |
+#if !V8_TARGET_ARCH_S390X |
+ case MachineRepresentation::kTaggedSigned: // Fall through. |
+ case MachineRepresentation::kTaggedPointer: // Fall through. |
+ case MachineRepresentation::kTagged: // Fall through. |
+#endif |
+ case MachineRepresentation::kWord32: |
+ opcode = kS390_LoadWordU32; |
+ break; |
+#if V8_TARGET_ARCH_S390X |
+ case MachineRepresentation::kTaggedSigned: // Fall through. |
+ case MachineRepresentation::kTaggedPointer: // Fall through. |
+ case MachineRepresentation::kTagged: // Fall through. |
+ case MachineRepresentation::kWord64: |
+ opcode = kS390_LoadWord64; |
+ break; |
+#else |
+ case MachineRepresentation::kWord64: // Fall through. |
+#endif |
+ case MachineRepresentation::kSimd128: // Fall through. |
+ case MachineRepresentation::kNone: |
+ default: |
+ UNREACHABLE(); |
+ } |
+ return opcode; |
+} |
+ |
+bool AutoZeroExtendsWord32ToWord64(Node* node) { |
+#if !V8_TARGET_ARCH_S390X |
+ return true; |
+#else |
+ switch (node->opcode()) { |
+ case IrOpcode::kInt32Div: |
+ case IrOpcode::kUint32Div: |
+ case IrOpcode::kInt32MulHigh: |
+ case IrOpcode::kInt32Mod: |
+ case IrOpcode::kUint32Mod: |
+ return true; |
+ default: |
+ return false; |
+ } |
+ return false; |
+#endif |
+} |
+ |
+bool ZeroExtendsWord32ToWord64(Node* node) { |
+#if !V8_TARGET_ARCH_S390X |
+ return true; |
+#else |
+ switch (node->opcode()) { |
+ case IrOpcode::kInt32Add: |
+ case IrOpcode::kInt32Sub: |
+ case IrOpcode::kWord32And: |
+ case IrOpcode::kWord32Or: |
+ case IrOpcode::kWord32Xor: |
+ case IrOpcode::kWord32Shl: |
+ case IrOpcode::kWord32Shr: |
+ case IrOpcode::kWord32Sar: |
+ case IrOpcode::kInt32Mul: |
+ case IrOpcode::kWord32Ror: |
+ case IrOpcode::kInt32Div: |
+ case IrOpcode::kUint32Div: |
+ case IrOpcode::kInt32MulHigh: |
+ case IrOpcode::kInt32Mod: |
+ case IrOpcode::kUint32Mod: |
+ return true; |
+ // TODO(john.yan): consider the following case to be valid |
+ // case IrOpcode::kWord32Equal: |
+ // case IrOpcode::kInt32LessThan: |
+ // case IrOpcode::kInt32LessThanOrEqual: |
+ // case IrOpcode::kUint32LessThan: |
+ // case IrOpcode::kUint32LessThanOrEqual: |
+ // case IrOpcode::kUint32MulHigh: |
+ // // These 32-bit operations implicitly zero-extend to 64-bit on x64, so |
+ // the |
+ // // zero-extension is a no-op. |
+ // return true; |
+ // case IrOpcode::kProjection: { |
+ // Node* const value = node->InputAt(0); |
+ // switch (value->opcode()) { |
+ // case IrOpcode::kInt32AddWithOverflow: |
+ // case IrOpcode::kInt32SubWithOverflow: |
+ // case IrOpcode::kInt32MulWithOverflow: |
+ // return true; |
+ // default: |
+ // return false; |
+ // } |
+ // } |
+ case IrOpcode::kLoad: { |
+ LoadRepresentation load_rep = LoadRepresentationOf(node->op()); |
+ switch (load_rep.representation()) { |
+ case MachineRepresentation::kWord32: |
+ return true; |
+ default: |
+ return false; |
+ } |
+ } |
+ default: |
+ return false; |
+ } |
+#endif |
+} |
+ |
void VisitRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) { |
S390OperandGenerator g(selector); |
selector->Emit(opcode, g.DefineAsRegister(node), |
@@ -171,15 +327,15 @@ void VisitRRR(InstructionSelector* selector, ArchOpcode opcode, Node* node) { |
g.UseRegister(node->InputAt(1))); |
} |
+#if V8_TARGET_ARCH_S390X |
void VisitRRO(InstructionSelector* selector, ArchOpcode opcode, Node* node, |
- ImmediateMode operand_mode) { |
+ OperandModes operand_mode) { |
S390OperandGenerator g(selector); |
selector->Emit(opcode, g.DefineAsRegister(node), |
g.UseRegister(node->InputAt(0)), |
g.UseOperand(node->InputAt(1), operand_mode)); |
} |
-#if V8_TARGET_ARCH_S390X |
void VisitTryTruncateDouble(InstructionSelector* selector, ArchOpcode opcode, |
Node* node) { |
S390OperandGenerator g(selector); |
@@ -200,7 +356,7 @@ void VisitTryTruncateDouble(InstructionSelector* selector, ArchOpcode opcode, |
// Shared routine for multiple binary operations. |
template <typename Matcher> |
void VisitBinop(InstructionSelector* selector, Node* node, |
- InstructionCode opcode, ImmediateMode operand_mode, |
+ InstructionCode opcode, OperandModes operand_mode, |
FlagsContinuation* cont) { |
S390OperandGenerator g(selector); |
Matcher m(node); |
@@ -269,54 +425,149 @@ void VisitBinop(InstructionSelector* selector, Node* node, |
// Shared routine for multiple binary operations. |
template <typename Matcher> |
void VisitBinop(InstructionSelector* selector, Node* node, ArchOpcode opcode, |
- ImmediateMode operand_mode) { |
+ OperandModes operand_mode) { |
FlagsContinuation cont; |
VisitBinop<Matcher>(selector, node, opcode, operand_mode, &cont); |
} |
-} // namespace |
+void VisitBin32op(InstructionSelector* selector, Node* node, |
+ InstructionCode opcode, OperandModes operand_mode, |
+ FlagsContinuation* cont) { |
+ S390OperandGenerator g(selector); |
+ Int32BinopMatcher m(node); |
+ Node* left = m.left().node(); |
+ Node* right = m.right().node(); |
+ InstructionOperand inputs[8]; |
+ size_t input_count = 0; |
+ InstructionOperand outputs[2]; |
+ size_t output_count = 0; |
+ |
+ // match left of TruncateInt64ToInt32 |
+ if (m.left().IsTruncateInt64ToInt32() && selector->CanCover(node, left)) { |
+ left = left->InputAt(0); |
+ } |
+ // match right of TruncateInt64ToInt32 |
+ if (m.right().IsTruncateInt64ToInt32() && selector->CanCover(node, right)) { |
+ right = right->InputAt(0); |
+ } |
-void InstructionSelector::VisitLoad(Node* node) { |
- LoadRepresentation load_rep = LoadRepresentationOf(node->op()); |
- S390OperandGenerator g(this); |
- ArchOpcode opcode = kArchNop; |
- switch (load_rep.representation()) { |
- case MachineRepresentation::kFloat32: |
- opcode = kS390_LoadFloat32; |
- break; |
- case MachineRepresentation::kFloat64: |
- opcode = kS390_LoadDouble; |
- break; |
- case MachineRepresentation::kBit: // Fall through. |
- case MachineRepresentation::kWord8: |
- opcode = load_rep.IsSigned() ? kS390_LoadWordS8 : kS390_LoadWordU8; |
- break; |
- case MachineRepresentation::kWord16: |
- opcode = load_rep.IsSigned() ? kS390_LoadWordS16 : kS390_LoadWordU16; |
- break; |
-#if !V8_TARGET_ARCH_S390X |
- case MachineRepresentation::kTaggedSigned: // Fall through. |
- case MachineRepresentation::kTaggedPointer: // Fall through. |
- case MachineRepresentation::kTagged: // Fall through. |
-#endif |
- case MachineRepresentation::kWord32: |
- opcode = kS390_LoadWordU32; |
- break; |
#if V8_TARGET_ARCH_S390X |
- case MachineRepresentation::kTaggedSigned: // Fall through. |
- case MachineRepresentation::kTaggedPointer: // Fall through. |
- case MachineRepresentation::kTagged: // Fall through. |
- case MachineRepresentation::kWord64: |
- opcode = kS390_LoadWord64; |
- break; |
+ if ((ZeroExtendsWord32ToWord64(right) || g.CanBeBetterLeftOperand(right)) && |
+ node->op()->HasProperty(Operator::kCommutative) && |
+ !g.CanBeImmediate(right, operand_mode)) { |
+ std::swap(left, right); |
+ } |
#else |
- case MachineRepresentation::kWord64: // Fall through. |
+ if (node->op()->HasProperty(Operator::kCommutative) && |
+ !g.CanBeImmediate(right, operand_mode) && |
+ (g.CanBeBetterLeftOperand(right))) { |
+ std::swap(left, right); |
+ } |
#endif |
- case MachineRepresentation::kSimd128: // Fall through. |
- case MachineRepresentation::kNone: |
+ |
+ // left is always register |
+ InstructionOperand const left_input = g.UseRegister(left); |
+ inputs[input_count++] = left_input; |
+ |
+ // 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 rax, [rbp-0x10] |
+ // add rax, [rbp-0x10] |
+ // jo label |
+ inputs[input_count++] = left_input; |
+ // Can only be RR or RRR |
+ operand_mode &= OperandMode::kAllowRRR; |
+ } else if ((operand_mode & OperandMode::kAllowImmediate) && |
+ g.CanBeImmediate(right, operand_mode)) { |
+ inputs[input_count++] = g.UseImmediate(right); |
+ // Can only be RI or RRI |
+ operand_mode &= OperandMode::kAllowImmediate; |
+ } else if (operand_mode & OperandMode::kAllowMemoryOperand) { |
+ NodeMatcher mright(right); |
+ if (mright.IsLoad() && selector->CanCover(node, right) && |
+ SelectLoadOpcode(right) == kS390_LoadWordU32) { |
+ AddressingMode mode = |
+ g.GetEffectiveAddressMemoryOperand(right, inputs, &input_count); |
+ opcode |= AddressingModeField::encode(mode); |
+ operand_mode &= ~OperandMode::kAllowImmediate; |
+ if (operand_mode & OperandMode::kAllowRM) |
+ operand_mode &= ~OperandMode::kAllowDistinctOps; |
+ } else if (operand_mode & OperandMode::kAllowRM) { |
+ DCHECK(!(operand_mode & OperandMode::kAllowRRM)); |
+ inputs[input_count++] = g.Use(right); |
+ // Can not be Immediate |
+ operand_mode &= |
+ ~OperandMode::kAllowImmediate & ~OperandMode::kAllowDistinctOps; |
+ } else if (operand_mode & OperandMode::kAllowRRM) { |
+ DCHECK(!(operand_mode & OperandMode::kAllowRM)); |
+ inputs[input_count++] = g.Use(right); |
+ // Can not be Immediate |
+ operand_mode &= ~OperandMode::kAllowImmediate; |
+ } else { |
UNREACHABLE(); |
- return; |
+ } |
+ } else { |
+ inputs[input_count++] = g.UseRegister(right); |
+ // Can only be RR or RRR |
+ operand_mode &= OperandMode::kAllowRRR; |
+ } |
+ |
+ bool doZeroExt = |
+ AutoZeroExtendsWord32ToWord64(node) || !ZeroExtendsWord32ToWord64(left); |
+ |
+ inputs[input_count++] = |
+ g.TempImmediate(doZeroExt && (!AutoZeroExtendsWord32ToWord64(node))); |
+ |
+ if (cont->IsBranch()) { |
+ inputs[input_count++] = g.Label(cont->true_block()); |
+ inputs[input_count++] = g.Label(cont->false_block()); |
+ } |
+ |
+ if (doZeroExt && (operand_mode & OperandMode::kAllowDistinctOps) && |
+ // If we can deoptimize as a result of the binop, we need to make sure |
+ // that |
+ // the deopt inputs are not overwritten by the binop result. One way |
+ // to achieve that is to declare the output register as same-as-first. |
+ !cont->IsDeoptimize()) { |
+ outputs[output_count++] = g.DefineAsRegister(node); |
+ } else { |
+ outputs[output_count++] = g.DefineSameAsFirst(node); |
+ } |
+ |
+ if (cont->IsSet()) { |
+ outputs[output_count++] = g.DefineAsRegister(cont->result()); |
} |
+ |
+ DCHECK_NE(0u, input_count); |
+ DCHECK_NE(0u, output_count); |
+ DCHECK_GE(arraysize(inputs), input_count); |
+ DCHECK_GE(arraysize(outputs), output_count); |
+ |
+ opcode = cont->Encode(opcode); |
+ |
+ if (cont->IsDeoptimize()) { |
+ selector->EmitDeoptimize(opcode, output_count, outputs, input_count, inputs, |
+ cont->reason(), cont->frame_state()); |
+ } else { |
+ selector->Emit(opcode, output_count, outputs, input_count, inputs); |
+ } |
+} |
+ |
+void VisitBin32op(InstructionSelector* selector, Node* node, ArchOpcode opcode, |
+ OperandModes operand_mode) { |
+ FlagsContinuation cont; |
+ VisitBin32op(selector, node, opcode, operand_mode, &cont); |
+} |
+ |
+} // namespace |
+ |
+void InstructionSelector::VisitLoad(Node* node) { |
+ S390OperandGenerator g(this); |
+ ArchOpcode opcode = SelectLoadOpcode(node); |
InstructionOperand outputs[1]; |
outputs[0] = g.DefineAsRegister(node); |
InstructionOperand inputs[3]; |
@@ -350,7 +601,7 @@ void InstructionSelector::VisitStore(Node* node) { |
inputs[input_count++] = g.UseUniqueRegister(base); |
// OutOfLineRecordWrite uses the offset in an 'AddP' instruction as well as |
// for the store itself, so we must check compatibility with both. |
- if (g.CanBeImmediate(offset, kInt20Imm)) { |
+ if (g.CanBeImmediate(offset, OperandMode::kInt20Imm)) { |
inputs[input_count++] = g.UseImmediate(offset); |
addressing_mode = kMode_MRI; |
} else { |
@@ -494,7 +745,7 @@ void InstructionSelector::VisitCheckedLoad(Node* node) { |
AddressingMode addressingMode = kMode_MRR; |
Emit(opcode | AddressingModeField::encode(addressingMode), |
g.DefineAsRegister(node), g.UseRegister(base), g.UseRegister(offset), |
- g.UseOperand(length, kUint32Imm)); |
+ g.UseOperand(length, OperandMode::kUint32Imm)); |
} |
void InstructionSelector::VisitCheckedStore(Node* node) { |
@@ -541,7 +792,7 @@ void InstructionSelector::VisitCheckedStore(Node* node) { |
AddressingMode addressingMode = kMode_MRR; |
Emit(opcode | AddressingModeField::encode(addressingMode), g.NoOutput(), |
g.UseRegister(base), g.UseRegister(offset), |
- g.UseOperand(length, kUint32Imm), g.UseRegister(value)); |
+ g.UseOperand(length, OperandMode::kUint32Imm), g.UseRegister(value)); |
} |
#if 0 |
@@ -571,7 +822,8 @@ static inline bool IsContiguousMask64(uint64_t value, int* mb, int* me) { |
#endif |
void InstructionSelector::VisitWord32And(Node* node) { |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_And32, kUint32Imm); |
+ VisitBin32op(this, node, kS390_And32, |
+ BitWiseOperandMode | OperandMode::kAllowRM); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -623,46 +875,38 @@ void InstructionSelector::VisitWord64And(Node* node) { |
} |
} |
} |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_And64, kUint32Imm); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_And64, |
+ OperandMode::kUint32Imm); |
} |
#endif |
void InstructionSelector::VisitWord32Or(Node* node) { |
- Int32BinopMatcher m(node); |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Or32, kUint32Imm); |
+ VisitBin32op(this, node, kS390_Or32, |
+ BitWiseOperandMode | OperandMode::kAllowRM); |
} |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitWord64Or(Node* node) { |
Int64BinopMatcher m(node); |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Or64, kUint32Imm); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Or64, |
+ OperandMode::kUint32Imm); |
} |
#endif |
void InstructionSelector::VisitWord32Xor(Node* node) { |
- S390OperandGenerator g(this); |
- Int32BinopMatcher m(node); |
- if (m.right().Is(-1)) { |
- Emit(kS390_Not32, g.DefineAsRegister(node), g.UseRegister(m.left().node())); |
- } else { |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Xor32, kUint32Imm); |
- } |
+ VisitBin32op(this, node, kS390_Xor32, |
+ BitWiseOperandMode | OperandMode::kAllowRM); |
} |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitWord64Xor(Node* node) { |
- S390OperandGenerator g(this); |
- Int64BinopMatcher m(node); |
- if (m.right().Is(-1)) { |
- Emit(kS390_Not64, g.DefineAsRegister(node), g.UseRegister(m.left().node())); |
- } else { |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Xor64, kUint32Imm); |
- } |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Xor64, |
+ OperandMode::kUint32Imm); |
} |
#endif |
void InstructionSelector::VisitWord32Shl(Node* node) { |
- VisitRRO(this, kS390_ShiftLeft32, node, kShift32Imm); |
+ VisitBin32op(this, node, kS390_ShiftLeft32, BitWiseOperandMode); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -705,12 +949,12 @@ void InstructionSelector::VisitWord64Shl(Node* node) { |
} |
} |
} |
- VisitRRO(this, kS390_ShiftLeft64, node, kShift64Imm); |
+ VisitRRO(this, kS390_ShiftLeft64, node, OperandMode::kShift64Imm); |
} |
#endif |
void InstructionSelector::VisitWord32Shr(Node* node) { |
- VisitRRO(this, kS390_ShiftRight32, node, kShift32Imm); |
+ VisitBin32op(this, node, kS390_ShiftRight32, BitWiseOperandMode); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -749,7 +993,7 @@ void InstructionSelector::VisitWord64Shr(Node* node) { |
} |
} |
} |
- VisitRRO(this, kS390_ShiftRight64, node, kShift64Imm); |
+ VisitRRO(this, kS390_ShiftRight64, node, OperandMode::kShift64Imm); |
} |
#endif |
@@ -760,16 +1004,20 @@ void InstructionSelector::VisitWord32Sar(Node* node) { |
if (CanCover(node, m.left().node()) && m.left().IsWord32Shl()) { |
Int32BinopMatcher mleft(m.left().node()); |
if (mleft.right().Is(16) && m.right().Is(16)) { |
- Emit(kS390_ExtendSignWord16, g.DefineAsRegister(node), |
- g.UseRegister(mleft.left().node())); |
+ bool doZeroExt = !ZeroExtendsWord32ToWord64(mleft.left().node()); |
+ Emit(kS390_ExtendSignWord16, |
+ doZeroExt ? g.DefineAsRegister(node) : g.DefineSameAsFirst(node), |
+ g.UseRegister(mleft.left().node()), g.TempImmediate(doZeroExt)); |
return; |
} else if (mleft.right().Is(24) && m.right().Is(24)) { |
- Emit(kS390_ExtendSignWord8, g.DefineAsRegister(node), |
- g.UseRegister(mleft.left().node())); |
+ bool doZeroExt = !ZeroExtendsWord32ToWord64(mleft.left().node()); |
+ Emit(kS390_ExtendSignWord8, |
+ doZeroExt ? g.DefineAsRegister(node) : g.DefineSameAsFirst(node), |
+ g.UseRegister(mleft.left().node()), g.TempImmediate(doZeroExt)); |
return; |
} |
} |
- VisitRRO(this, kS390_ShiftRightArith32, node, kShift32Imm); |
+ VisitBin32op(this, node, kS390_ShiftRightArith32, BitWiseOperandMode); |
} |
#if !V8_TARGET_ARCH_S390X |
@@ -795,7 +1043,7 @@ void VisitPairBinop(InstructionSelector* selector, InstructionCode opcode, |
// instruction. |
selector->Emit(opcode2, g.DefineSameAsFirst(node), |
g.UseRegister(node->InputAt(0)), |
- g.UseRegister(node->InputAt(2))); |
+ g.UseRegister(node->InputAt(2)), g.TempImmediate(0)); |
} |
} |
@@ -825,7 +1073,8 @@ void InstructionSelector::VisitInt32PairMul(Node* node) { |
// The high word of the result is not used, so we emit the standard 32 bit |
// instruction. |
Emit(kS390_Mul32, g.DefineSameAsFirst(node), |
- g.UseRegister(node->InputAt(0)), g.UseRegister(node->InputAt(2))); |
+ g.UseRegister(node->InputAt(0)), g.Use(node->InputAt(2)), |
+ g.TempImmediate(0)); |
} |
} |
@@ -881,17 +1130,20 @@ void InstructionSelector::VisitWord32PairSar(Node* node) { |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitWord64Sar(Node* node) { |
- VisitRRO(this, kS390_ShiftRightArith64, node, kShift64Imm); |
+ VisitRRO(this, kS390_ShiftRightArith64, node, OperandMode::kShift64Imm); |
} |
#endif |
void InstructionSelector::VisitWord32Ror(Node* node) { |
- VisitRRO(this, kS390_RotRight32, node, kShift32Imm); |
+ // TODO(john): match dst = ror(src1, src2 + imm) |
+ VisitBin32op(this, node, kS390_RotRight32, |
+ OperandMode::kAllowRI | OperandMode::kAllowRRR | |
+ OperandMode::kAllowRRI | OperandMode::kShift32Imm); |
} |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitWord64Ror(Node* node) { |
- VisitRRO(this, kS390_RotRight64, node, kShift64Imm); |
+ VisitRRO(this, kS390_RotRight64, node, OperandMode::kShift64Imm); |
} |
#endif |
@@ -961,12 +1213,13 @@ void InstructionSelector::VisitWord32ReverseBytes(Node* node) { |
} |
void InstructionSelector::VisitInt32Add(Node* node) { |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Add32, kInt32Imm); |
+ VisitBin32op(this, node, kS390_Add32, AddOperandMode); |
} |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitInt64Add(Node* node) { |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, kInt32Imm); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, |
+ OperandMode::kInt32Imm); |
} |
#endif |
@@ -974,10 +1227,12 @@ void InstructionSelector::VisitInt32Sub(Node* node) { |
S390OperandGenerator g(this); |
Int32BinopMatcher m(node); |
if (m.left().Is(0)) { |
- Emit(kS390_Neg32, g.DefineAsRegister(node), |
- g.UseRegister(m.right().node())); |
+ Node* right = m.right().node(); |
+ bool doZeroExt = ZeroExtendsWord32ToWord64(right); |
+ Emit(kS390_Neg32, g.DefineAsRegister(node), g.UseRegister(right), |
+ g.TempImmediate(doZeroExt)); |
} else { |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Sub32, kInt32Imm_Negate); |
+ VisitBin32op(this, node, kS390_Sub32, SubOperandMode); |
} |
} |
@@ -989,7 +1244,8 @@ void InstructionSelector::VisitInt64Sub(Node* node) { |
Emit(kS390_Neg64, g.DefineAsRegister(node), |
g.UseRegister(m.right().node())); |
} else { |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Sub64, kInt32Imm_Negate); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Sub64, |
+ OperandMode::kInt32Imm_Negate); |
} |
} |
#endif |
@@ -999,35 +1255,14 @@ namespace { |
void VisitCompare(InstructionSelector* selector, InstructionCode opcode, |
InstructionOperand left, InstructionOperand right, |
FlagsContinuation* cont); |
-void EmitInt32MulWithOverflow(InstructionSelector* selector, Node* node, |
- FlagsContinuation* cont) { |
- S390OperandGenerator g(selector); |
- Int32BinopMatcher m(node); |
- InstructionOperand result_operand = g.DefineAsRegister(node); |
- InstructionOperand high32_operand = g.TempRegister(); |
- InstructionOperand temp_operand = g.TempRegister(); |
- { |
- InstructionOperand outputs[] = {result_operand, high32_operand}; |
- InstructionOperand inputs[] = {g.UseRegister(m.left().node()), |
- g.UseRegister(m.right().node())}; |
- selector->Emit(kS390_Mul32WithHigh32, 2, outputs, 2, inputs); |
- } |
- { |
- InstructionOperand shift_31 = g.UseImmediate(31); |
- InstructionOperand outputs[] = {temp_operand}; |
- InstructionOperand inputs[] = {result_operand, shift_31}; |
- selector->Emit(kS390_ShiftRightArith32, 1, outputs, 2, inputs); |
- } |
- |
- VisitCompare(selector, kS390_Cmp32, high32_operand, temp_operand, cont); |
-} |
+#if V8_TARGET_ARCH_S390X |
void VisitMul(InstructionSelector* selector, Node* node, ArchOpcode opcode) { |
S390OperandGenerator g(selector); |
Int32BinopMatcher m(node); |
Node* left = m.left().node(); |
Node* right = m.right().node(); |
- if (g.CanBeImmediate(right, kInt32Imm)) { |
+ if (g.CanBeImmediate(right, OperandMode::kInt32Imm)) { |
selector->Emit(opcode, g.DefineSameAsFirst(node), g.UseRegister(left), |
g.UseImmediate(right)); |
} else { |
@@ -1038,17 +1273,18 @@ void VisitMul(InstructionSelector* selector, Node* node, ArchOpcode opcode) { |
g.Use(right)); |
} |
} |
+#endif |
} // namespace |
void InstructionSelector::VisitInt32MulWithOverflow(Node* node) { |
if (Node* ovf = NodeProperties::FindProjection(node, 1)) { |
FlagsContinuation cont = FlagsContinuation::ForSet(kNotEqual, ovf); |
- return EmitInt32MulWithOverflow(this, node, &cont); |
+ return VisitBin32op(this, node, kS390_Mul32WithOverflow, |
+ OperandMode::kInt32Imm | OperandMode::kAllowDistinctOps, |
+ &cont); |
} |
- VisitMul(this, node, kS390_Mul32); |
- // FlagsContinuation cont; |
- // EmitInt32MulWithOverflow(this, node, &cont); |
+ VisitBin32op(this, node, kS390_Mul32, MulOperandMode); |
} |
void InstructionSelector::VisitInt32Mul(Node* node) { |
@@ -1056,14 +1292,20 @@ void InstructionSelector::VisitInt32Mul(Node* node) { |
Int32BinopMatcher m(node); |
Node* left = m.left().node(); |
Node* right = m.right().node(); |
- if (g.CanBeImmediate(right, kInt32Imm) && |
+ if (g.CanBeImmediate(right, OperandMode::kInt32Imm) && |
base::bits::IsPowerOfTwo32(g.GetImmediate(right))) { |
int power = 31 - base::bits::CountLeadingZeros32(g.GetImmediate(right)); |
- Emit(kS390_ShiftLeft32, g.DefineSameAsFirst(node), g.UseRegister(left), |
- g.UseImmediate(power)); |
+ bool doZeroExt = !ZeroExtendsWord32ToWord64(left); |
+ InstructionOperand dst = |
+ (doZeroExt && CpuFeatures::IsSupported(DISTINCT_OPS)) |
+ ? g.DefineAsRegister(node) |
+ : g.DefineSameAsFirst(node); |
+ |
+ Emit(kS390_ShiftLeft32, dst, g.UseRegister(left), g.UseImmediate(power), |
+ g.TempImmediate(doZeroExt)); |
return; |
} |
- VisitMul(this, node, kS390_Mul32); |
+ VisitBin32op(this, node, kS390_Mul32, MulOperandMode); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -1072,7 +1314,7 @@ void InstructionSelector::VisitInt64Mul(Node* node) { |
Int64BinopMatcher m(node); |
Node* left = m.left().node(); |
Node* right = m.right().node(); |
- if (g.CanBeImmediate(right, kInt32Imm) && |
+ if (g.CanBeImmediate(right, OperandMode::kInt32Imm) && |
base::bits::IsPowerOfTwo64(g.GetImmediate(right))) { |
int power = 63 - base::bits::CountLeadingZeros64(g.GetImmediate(right)); |
Emit(kS390_ShiftLeft64, g.DefineSameAsFirst(node), g.UseRegister(left), |
@@ -1084,15 +1326,8 @@ void InstructionSelector::VisitInt64Mul(Node* node) { |
#endif |
void InstructionSelector::VisitInt32MulHigh(Node* node) { |
- S390OperandGenerator g(this); |
- Int32BinopMatcher m(node); |
- Node* left = m.left().node(); |
- Node* right = m.right().node(); |
- if (g.CanBeBetterLeftOperand(right)) { |
- std::swap(left, right); |
- } |
- Emit(kS390_MulHigh32, g.DefineAsRegister(node), g.UseRegister(left), |
- g.Use(right)); |
+ VisitBin32op(this, node, kS390_MulHigh32, |
+ OperandMode::kInt32Imm | OperandMode::kAllowDistinctOps); |
} |
void InstructionSelector::VisitUint32MulHigh(Node* node) { |
@@ -1108,7 +1343,8 @@ void InstructionSelector::VisitUint32MulHigh(Node* node) { |
} |
void InstructionSelector::VisitInt32Div(Node* node) { |
- VisitRRR(this, kS390_Div32, node); |
+ VisitBin32op(this, node, kS390_Div32, |
+ OperandMode::kAllowRRM | OperandMode::kAllowRRR); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -1118,7 +1354,8 @@ void InstructionSelector::VisitInt64Div(Node* node) { |
#endif |
void InstructionSelector::VisitUint32Div(Node* node) { |
- VisitRRR(this, kS390_DivU32, node); |
+ VisitBin32op(this, node, kS390_DivU32, |
+ OperandMode::kAllowRRM | OperandMode::kAllowRRR); |
} |
#if V8_TARGET_ARCH_S390X |
@@ -1202,7 +1439,13 @@ void InstructionSelector::VisitChangeInt32ToInt64(Node* node) { |
} |
void InstructionSelector::VisitChangeUint32ToUint64(Node* node) { |
- // TODO(mbrandy): inspect input to see if nop is appropriate. |
+ S390OperandGenerator g(this); |
+ Node* value = node->InputAt(0); |
+ if (ZeroExtendsWord32ToWord64(value)) { |
+ // These 32-bit operations implicitly zero-extend to 64-bit on x64, so the |
+ // zero-extension is a no-op. |
+ return EmitIdentity(node); |
+ } |
VisitRR(this, kS390_Uint32ToUint64, node); |
} |
#endif |
@@ -1408,46 +1651,46 @@ void InstructionSelector::VisitFloat64Neg(Node* node) { |
} |
void InstructionSelector::VisitInt32AddWithOverflow(Node* node) { |
+ OperandModes mode = AddOperandMode; |
if (Node* ovf = NodeProperties::FindProjection(node, 1)) { |
FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); |
- return VisitBinop<Int32BinopMatcher>(this, node, kS390_Add32, kInt32Imm, |
- &cont); |
+ return VisitBin32op(this, node, kS390_Add32, mode, &cont); |
} |
FlagsContinuation cont; |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Add32, kInt32Imm, &cont); |
+ VisitBin32op(this, node, kS390_Add32, mode, &cont); |
} |
void InstructionSelector::VisitInt32SubWithOverflow(Node* node) { |
+ OperandModes mode = SubOperandMode; |
if (Node* ovf = NodeProperties::FindProjection(node, 1)) { |
FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); |
- return VisitBinop<Int32BinopMatcher>(this, node, kS390_Sub32, |
- kInt32Imm_Negate, &cont); |
+ return VisitBin32op(this, node, kS390_Sub32, mode, &cont); |
} |
FlagsContinuation cont; |
- VisitBinop<Int32BinopMatcher>(this, node, kS390_Sub32, kInt32Imm_Negate, |
- &cont); |
+ VisitBin32op(this, node, kS390_Sub32, mode, &cont); |
} |
#if V8_TARGET_ARCH_S390X |
void InstructionSelector::VisitInt64AddWithOverflow(Node* node) { |
if (Node* ovf = NodeProperties::FindProjection(node, 1)) { |
FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); |
- return VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, kInt32Imm, |
- &cont); |
+ return VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, |
+ OperandMode::kInt32Imm, &cont); |
} |
FlagsContinuation cont; |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, kInt32Imm, &cont); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Add64, OperandMode::kInt32Imm, |
+ &cont); |
} |
void InstructionSelector::VisitInt64SubWithOverflow(Node* node) { |
if (Node* ovf = NodeProperties::FindProjection(node, 1)) { |
FlagsContinuation cont = FlagsContinuation::ForSet(kOverflow, ovf); |
return VisitBinop<Int64BinopMatcher>(this, node, kS390_Sub64, |
- kInt32Imm_Negate, &cont); |
+ OperandMode::kInt32Imm_Negate, &cont); |
} |
FlagsContinuation cont; |
- VisitBinop<Int64BinopMatcher>(this, node, kS390_Sub64, kInt32Imm_Negate, |
- &cont); |
+ VisitBinop<Int64BinopMatcher>(this, node, kS390_Sub64, |
+ OperandMode::kInt32Imm_Negate, &cont); |
} |
#endif |
@@ -1488,7 +1731,7 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode, |
// Shared routine for multiple word compare operations. |
void VisitWordCompare(InstructionSelector* selector, Node* node, |
InstructionCode opcode, FlagsContinuation* cont, |
- bool commutative, ImmediateMode immediate_mode) { |
+ bool commutative, OperandModes immediate_mode) { |
S390OperandGenerator g(selector); |
Node* left = node->InputAt(0); |
Node* right = node->InputAt(1); |
@@ -1509,14 +1752,16 @@ void VisitWordCompare(InstructionSelector* selector, Node* node, |
void VisitWord32Compare(InstructionSelector* selector, Node* node, |
FlagsContinuation* cont) { |
- ImmediateMode mode = (CompareLogical(cont) ? kUint32Imm : kInt32Imm); |
+ OperandModes mode = |
+ (CompareLogical(cont) ? OperandMode::kUint32Imm : OperandMode::kInt32Imm); |
VisitWordCompare(selector, node, kS390_Cmp32, cont, false, mode); |
} |
#if V8_TARGET_ARCH_S390X |
void VisitWord64Compare(InstructionSelector* selector, Node* node, |
FlagsContinuation* cont) { |
- ImmediateMode mode = (CompareLogical(cont) ? kUint32Imm : kUint32Imm); |
+ OperandModes mode = |
+ (CompareLogical(cont) ? OperandMode::kUint32Imm : OperandMode::kInt32Imm); |
VisitWordCompare(selector, node, kS390_Cmp64, cont, false, mode); |
} |
#endif |
@@ -1571,7 +1816,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, |
return VisitWord32Compare(selector, value, cont); |
case IrOpcode::kWord32And: |
return VisitWordCompare(selector, value, kS390_Tst64, cont, |
- true, kUint32Imm); |
+ true, OperandMode::kUint32Imm); |
default: |
break; |
} |
@@ -1605,7 +1850,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, |
return VisitWord64Compare(selector, value, cont); |
case IrOpcode::kWord64And: |
return VisitWordCompare(selector, value, kS390_Tst64, cont, |
- true, kUint32Imm); |
+ true, OperandMode::kUint32Imm); |
default: |
break; |
} |
@@ -1659,24 +1904,28 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, |
switch (node->opcode()) { |
case IrOpcode::kInt32AddWithOverflow: |
cont->OverwriteAndNegateIfEqual(kOverflow); |
- return VisitBinop<Int32BinopMatcher>( |
- selector, node, kS390_Add32, kInt32Imm, cont); |
+ return VisitBin32op(selector, node, kS390_Add32, AddOperandMode, |
+ cont); |
case IrOpcode::kInt32SubWithOverflow: |
cont->OverwriteAndNegateIfEqual(kOverflow); |
- return VisitBinop<Int32BinopMatcher>( |
- selector, node, kS390_Sub32, kInt32Imm_Negate, cont); |
+ return VisitBin32op(selector, node, kS390_Sub32, SubOperandMode, |
+ cont); |
case IrOpcode::kInt32MulWithOverflow: |
cont->OverwriteAndNegateIfEqual(kNotEqual); |
- return EmitInt32MulWithOverflow(selector, node, cont); |
+ return VisitBin32op( |
+ selector, node, kS390_Mul32WithOverflow, |
+ OperandMode::kInt32Imm | OperandMode::kAllowDistinctOps, |
+ cont); |
#if V8_TARGET_ARCH_S390X |
case IrOpcode::kInt64AddWithOverflow: |
cont->OverwriteAndNegateIfEqual(kOverflow); |
return VisitBinop<Int64BinopMatcher>( |
- selector, node, kS390_Add64, kInt32Imm, cont); |
+ selector, node, kS390_Add64, OperandMode::kInt32Imm, cont); |
case IrOpcode::kInt64SubWithOverflow: |
cont->OverwriteAndNegateIfEqual(kOverflow); |
return VisitBinop<Int64BinopMatcher>( |
- selector, node, kS390_Sub64, kInt32Imm_Negate, cont); |
+ selector, node, kS390_Sub64, OperandMode::kInt32Imm_Negate, |
+ cont); |
#endif |
default: |
break; |
@@ -1688,7 +1937,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, |
return VisitWord32Compare(selector, value, cont); |
case IrOpcode::kWord32And: |
return VisitWordCompare(selector, value, kS390_Tst32, cont, true, |
- kUint32Imm); |
+ OperandMode::kUint32Imm); |
// TODO(mbrandy): Handle? |
// case IrOpcode::kInt32Add: |
// case IrOpcode::kWord32Or: |
@@ -1702,7 +1951,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user, |
return VisitWord64Compare(selector, value, cont); |
case IrOpcode::kWord64And: |
return VisitWordCompare(selector, value, kS390_Tst64, cont, true, |
- kUint32Imm); |
+ OperandMode::kUint32Imm); |
// TODO(mbrandy): Handle? |
// case IrOpcode::kInt64Add: |
// case IrOpcode::kWord64Or: |
@@ -1780,9 +2029,14 @@ void InstructionSelector::VisitSwitch(Node* node, const SwitchInfo& sw) { |
InstructionOperand index_operand = value_operand; |
if (sw.min_value) { |
index_operand = g.TempRegister(); |
- Emit(kS390_Sub32, index_operand, value_operand, |
- g.TempImmediate(sw.min_value)); |
+ Emit(kS390_Lay | AddressingModeField::encode(kMode_MRI), index_operand, |
+ value_operand, g.TempImmediate(-sw.min_value)); |
} |
+#if V8_TARGET_ARCH_S390X |
+ InstructionOperand index_operand_zero_ext = g.TempRegister(); |
+ Emit(kS390_Uint32ToUint64, index_operand_zero_ext, index_operand); |
+ index_operand = index_operand_zero_ext; |
+#endif |
// Generate a table lookup. |
return EmitTableSwitch(sw, index_operand); |
} |