| 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 27bb6c6f10933ac3313dc8ec7e0904e870cf58f8..a0fed62be331a2aa09d911fa4992dc6075848d22 100644
|
| --- a/src/compiler/s390/instruction-selector-s390.cc
|
| +++ b/src/compiler/s390/instruction-selector-s390.cc
|
| @@ -21,6 +21,7 @@ enum class OperandMode : uint32_t {
|
| kInt32Imm_Negate = 1u << 3,
|
| kUint32Imm = 1u << 4,
|
| kInt20Imm = 1u << 5,
|
| + kUint12Imm = 1u << 6,
|
| // Instr format
|
| kAllowRRR = 1u << 7,
|
| kAllowRM = 1u << 8,
|
| @@ -83,6 +84,13 @@ class S390OperandGenerator final : public OperandGenerator {
|
| return UseRegister(node);
|
| }
|
|
|
| + InstructionOperand UseAnyExceptImmediate(Node* node) {
|
| + if (NodeProperties::IsConstant(node))
|
| + return UseRegister(node);
|
| + else
|
| + return Use(node);
|
| + }
|
| +
|
| int64_t GetImmediate(Node* node) {
|
| if (node->opcode() == IrOpcode::kInt32Constant)
|
| return OpParameter<int32_t>(node);
|
| @@ -117,10 +125,38 @@ class S390OperandGenerator final : public OperandGenerator {
|
| return is_uint32(value);
|
| else if (mode & OperandMode::kInt20Imm)
|
| return is_int20(value);
|
| + else if (mode & OperandMode::kUint12Imm)
|
| + return is_uint12(value);
|
| else
|
| return false;
|
| }
|
|
|
| + bool CanBeMemoryOperand(InstructionCode opcode, Node* user, Node* input,
|
| + int effect_level) {
|
| + if (input->opcode() != IrOpcode::kLoad ||
|
| + !selector()->CanCover(user, input)) {
|
| + return false;
|
| + }
|
| +
|
| + if (effect_level != selector()->GetEffectLevel(input)) {
|
| + return false;
|
| + }
|
| +
|
| + MachineRepresentation rep =
|
| + LoadRepresentationOf(input->op()).representation();
|
| + switch (opcode) {
|
| + case kS390_Cmp64:
|
| + case kS390_LoadAndTestWord64:
|
| + return rep == MachineRepresentation::kWord64 || IsAnyTagged(rep);
|
| + case kS390_LoadAndTestWord32:
|
| + case kS390_Cmp32:
|
| + return rep == MachineRepresentation::kWord32;
|
| + default:
|
| + break;
|
| + }
|
| + return false;
|
| + }
|
| +
|
| AddressingMode GenerateMemoryOperandInputs(Node* index, Node* base,
|
| Node* displacement,
|
| DisplacementMode displacement_mode,
|
| @@ -164,9 +200,9 @@ class S390OperandGenerator final : public OperandGenerator {
|
| return mode;
|
| }
|
|
|
| - AddressingMode GetEffectiveAddressMemoryOperand(Node* operand,
|
| - InstructionOperand inputs[],
|
| - size_t* input_count) {
|
| + AddressingMode GetEffectiveAddressMemoryOperand(
|
| + Node* operand, InstructionOperand inputs[], size_t* input_count,
|
| + OperandModes immediate_mode = OperandMode::kInt20Imm) {
|
| #if V8_TARGET_ARCH_S390X
|
| BaseWithIndexAndDisplacement64Matcher m(operand,
|
| AddressOption::kAllowInputSwap);
|
| @@ -176,7 +212,7 @@ class S390OperandGenerator final : public OperandGenerator {
|
| #endif
|
| DCHECK(m.matches());
|
| if ((m.displacement() == nullptr ||
|
| - CanBeImmediate(m.displacement(), OperandMode::kInt20Imm))) {
|
| + CanBeImmediate(m.displacement(), immediate_mode))) {
|
| DCHECK(m.scale() == 0);
|
| return GenerateMemoryOperandInputs(m.index(), m.base(), m.displacement(),
|
| m.displacement_mode(), inputs,
|
| @@ -203,6 +239,25 @@ class S390OperandGenerator final : public OperandGenerator {
|
|
|
| namespace {
|
|
|
| +bool S390OpcodeOnlySupport12BitDisp(ArchOpcode opcode) {
|
| + switch (opcode) {
|
| + case kS390_CmpFloat:
|
| + case kS390_CmpDouble:
|
| + return true;
|
| + default:
|
| + return false;
|
| + }
|
| +}
|
| +
|
| +bool S390OpcodeOnlySupport12BitDisp(InstructionCode op) {
|
| + ArchOpcode opcode = ArchOpcodeField::decode(op);
|
| + return S390OpcodeOnlySupport12BitDisp(opcode);
|
| +}
|
| +
|
| +#define OpcodeImmMode(op) \
|
| + (S390OpcodeOnlySupport12BitDisp(op) ? OperandMode::kUint12Imm \
|
| + : OperandMode::kInt20Imm)
|
| +
|
| ArchOpcode SelectLoadOpcode(Node* node) {
|
| NodeMatcher m(node);
|
| DCHECK(m.IsLoad());
|
| @@ -1741,25 +1796,87 @@ void VisitCompare(InstructionSelector* selector, InstructionCode opcode,
|
| }
|
| }
|
|
|
| +void VisitWordCompareZero(InstructionSelector* selector, Node* user,
|
| + Node* value, InstructionCode opcode,
|
| + FlagsContinuation* cont);
|
| +
|
| +void VisitLoadAndTest(InstructionSelector* selector, InstructionCode opcode,
|
| + Node* node, Node* value, FlagsContinuation* cont,
|
| + bool discard_output = false);
|
| +
|
| // Shared routine for multiple word compare operations.
|
| void VisitWordCompare(InstructionSelector* selector, Node* node,
|
| InstructionCode opcode, FlagsContinuation* cont,
|
| - bool commutative, OperandModes immediate_mode) {
|
| + OperandModes immediate_mode) {
|
| S390OperandGenerator g(selector);
|
| Node* left = node->InputAt(0);
|
| Node* right = node->InputAt(1);
|
|
|
| - // Match immediates on left or right side of comparison.
|
| - if (g.CanBeImmediate(right, immediate_mode)) {
|
| - VisitCompare(selector, opcode, g.UseRegister(left), g.UseImmediate(right),
|
| - cont);
|
| - } else if (g.CanBeImmediate(left, immediate_mode)) {
|
| - if (!commutative) cont->Commute();
|
| - VisitCompare(selector, opcode, g.UseRegister(right), g.UseImmediate(left),
|
| - cont);
|
| + DCHECK(IrOpcode::IsComparisonOpcode(node->opcode()) ||
|
| + node->opcode() == IrOpcode::kInt32Sub ||
|
| + node->opcode() == IrOpcode::kInt64Sub);
|
| +
|
| + InstructionOperand inputs[8];
|
| + InstructionOperand outputs[1];
|
| + size_t input_count = 0;
|
| + size_t output_count = 0;
|
| +
|
| + // 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.
|
| + int effect_level = selector->GetEffectLevel(node);
|
| + if (cont->IsBranch()) {
|
| + effect_level = selector->GetEffectLevel(
|
| + cont->true_block()->PredecessorAt(0)->control_input());
|
| + }
|
| +
|
| + if ((!g.CanBeImmediate(right, immediate_mode) &&
|
| + g.CanBeImmediate(left, immediate_mode)) ||
|
| + (!g.CanBeMemoryOperand(opcode, node, right, effect_level) &&
|
| + g.CanBeMemoryOperand(opcode, node, left, effect_level))) {
|
| + if (!node->op()->HasProperty(Operator::kCommutative)) cont->Commute();
|
| + std::swap(left, right);
|
| + }
|
| +
|
| + // check if compare with 0
|
| + if (g.CanBeImmediate(right, immediate_mode) && g.GetImmediate(right) == 0) {
|
| + DCHECK(opcode == kS390_Cmp32 || opcode == kS390_Cmp64);
|
| + ArchOpcode load_and_test = (opcode == kS390_Cmp32)
|
| + ? kS390_LoadAndTestWord32
|
| + : kS390_LoadAndTestWord64;
|
| + return VisitLoadAndTest(selector, load_and_test, node, left, cont, true);
|
| + }
|
| +
|
| + inputs[input_count++] = g.UseRegister(left);
|
| + if (g.CanBeMemoryOperand(opcode, node, right, effect_level)) {
|
| + // generate memory operand
|
| + AddressingMode addressing_mode = g.GetEffectiveAddressMemoryOperand(
|
| + right, inputs, &input_count, OpcodeImmMode(opcode));
|
| + opcode |= AddressingModeField::encode(addressing_mode);
|
| + } else if (g.CanBeImmediate(right, immediate_mode)) {
|
| + inputs[input_count++] = g.UseImmediate(right);
|
| + } else {
|
| + inputs[input_count++] = g.UseAnyExceptImmediate(right);
|
| + }
|
| +
|
| + opcode = cont->Encode(opcode);
|
| + if (cont->IsBranch()) {
|
| + inputs[input_count++] = g.Label(cont->true_block());
|
| + inputs[input_count++] = g.Label(cont->false_block());
|
| + } else if (cont->IsSet()) {
|
| + outputs[output_count++] = g.DefineAsRegister(cont->result());
|
| + } else if (cont->IsTrap()) {
|
| + inputs[input_count++] = g.UseImmediate(cont->trap_id());
|
| } else {
|
| - VisitCompare(selector, opcode, g.UseRegister(left), g.UseRegister(right),
|
| - cont);
|
| + DCHECK(cont->IsDeoptimize());
|
| + // nothing to do
|
| + }
|
| +
|
| + DCHECK(input_count <= 8 && output_count <= 1);
|
| + if (cont->IsDeoptimize()) {
|
| + selector->EmitDeoptimize(opcode, 0, nullptr, input_count, inputs,
|
| + cont->reason(), cont->frame_state());
|
| + } else {
|
| + selector->Emit(opcode, output_count, outputs, input_count, inputs);
|
| }
|
| }
|
|
|
| @@ -1767,7 +1884,7 @@ void VisitWord32Compare(InstructionSelector* selector, Node* node,
|
| FlagsContinuation* cont) {
|
| OperandModes mode =
|
| (CompareLogical(cont) ? OperandMode::kUint32Imm : OperandMode::kInt32Imm);
|
| - VisitWordCompare(selector, node, kS390_Cmp32, cont, false, mode);
|
| + VisitWordCompare(selector, node, kS390_Cmp32, cont, mode);
|
| }
|
|
|
| #if V8_TARGET_ARCH_S390X
|
| @@ -1775,28 +1892,97 @@ void VisitWord64Compare(InstructionSelector* selector, Node* node,
|
| FlagsContinuation* cont) {
|
| OperandModes mode =
|
| (CompareLogical(cont) ? OperandMode::kUint32Imm : OperandMode::kInt32Imm);
|
| - VisitWordCompare(selector, node, kS390_Cmp64, cont, false, mode);
|
| + VisitWordCompare(selector, node, kS390_Cmp64, cont, mode);
|
| }
|
| #endif
|
|
|
| // Shared routine for multiple float32 compare operations.
|
| void VisitFloat32Compare(InstructionSelector* selector, Node* node,
|
| FlagsContinuation* cont) {
|
| - S390OperandGenerator g(selector);
|
| - Node* left = node->InputAt(0);
|
| - Node* right = node->InputAt(1);
|
| - VisitCompare(selector, kS390_CmpFloat, g.UseRegister(left),
|
| - g.UseRegister(right), cont);
|
| + VisitWordCompare(selector, node, kS390_CmpFloat, cont, OperandMode::kNone);
|
| }
|
|
|
| // Shared routine for multiple float64 compare operations.
|
| void VisitFloat64Compare(InstructionSelector* selector, Node* node,
|
| FlagsContinuation* cont) {
|
| + VisitWordCompare(selector, node, kS390_CmpDouble, cont, OperandMode::kNone);
|
| +}
|
| +
|
| +void VisitTestUnderMask(InstructionSelector* selector, Node* node,
|
| + FlagsContinuation* cont) {
|
| + DCHECK(node->opcode() == IrOpcode::kWord32And ||
|
| + node->opcode() == IrOpcode::kWord64And);
|
| + ArchOpcode opcode =
|
| + (node->opcode() == IrOpcode::kWord32And) ? kS390_Tst32 : kS390_Tst64;
|
| S390OperandGenerator g(selector);
|
| Node* left = node->InputAt(0);
|
| Node* right = node->InputAt(1);
|
| - VisitCompare(selector, kS390_CmpDouble, g.UseRegister(left),
|
| - g.UseRegister(right), cont);
|
| + if (!g.CanBeImmediate(right, OperandMode::kUint32Imm) &&
|
| + g.CanBeImmediate(left, OperandMode::kUint32Imm)) {
|
| + std::swap(left, right);
|
| + }
|
| + VisitCompare(selector, opcode, g.UseRegister(left),
|
| + g.UseOperand(right, OperandMode::kUint32Imm), cont);
|
| +}
|
| +
|
| +void VisitLoadAndTest(InstructionSelector* selector, InstructionCode opcode,
|
| + Node* node, Node* value, FlagsContinuation* cont,
|
| + bool discard_output) {
|
| + static_assert(kS390_LoadAndTestFloat64 - kS390_LoadAndTestWord32 == 3,
|
| + "LoadAndTest Opcode shouldn't contain other opcodes.");
|
| +
|
| + // TODO(john.yan): Add support for Float32/Float64.
|
| + DCHECK(opcode >= kS390_LoadAndTestWord32 ||
|
| + opcode <= kS390_LoadAndTestWord64);
|
| +
|
| + S390OperandGenerator g(selector);
|
| + InstructionOperand inputs[8];
|
| + InstructionOperand outputs[2];
|
| + size_t input_count = 0;
|
| + size_t output_count = 0;
|
| + bool use_value = false;
|
| +
|
| + int effect_level = selector->GetEffectLevel(node);
|
| + if (cont->IsBranch()) {
|
| + effect_level = selector->GetEffectLevel(
|
| + cont->true_block()->PredecessorAt(0)->control_input());
|
| + }
|
| +
|
| + if (g.CanBeMemoryOperand(opcode, node, value, effect_level)) {
|
| + // generate memory operand
|
| + AddressingMode addressing_mode =
|
| + g.GetEffectiveAddressMemoryOperand(value, inputs, &input_count);
|
| + opcode |= AddressingModeField::encode(addressing_mode);
|
| + } else {
|
| + inputs[input_count++] = g.UseAnyExceptImmediate(value);
|
| + use_value = true;
|
| + }
|
| +
|
| + if (!discard_output && !use_value) {
|
| + outputs[output_count++] = g.DefineAsRegister(value);
|
| + }
|
| +
|
| + opcode = cont->Encode(opcode);
|
| + if (cont->IsBranch()) {
|
| + inputs[input_count++] = g.Label(cont->true_block());
|
| + inputs[input_count++] = g.Label(cont->false_block());
|
| + } else if (cont->IsSet()) {
|
| + outputs[output_count++] = g.DefineAsRegister(cont->result());
|
| + } else if (cont->IsTrap()) {
|
| + inputs[input_count++] = g.UseImmediate(cont->trap_id());
|
| + } else {
|
| + DCHECK(cont->IsDeoptimize());
|
| + // nothing to do
|
| + }
|
| +
|
| + DCHECK(input_count <= 8 && output_count <= 2);
|
| + 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);
|
| + }
|
| }
|
|
|
| // Shared routine for word comparisons against zero.
|
| @@ -1814,6 +2000,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user,
|
| cont->Negate();
|
| }
|
|
|
| + FlagsCondition fc = cont->condition();
|
| if (selector->CanCover(user, value)) {
|
| switch (value->opcode()) {
|
| case IrOpcode::kWord32Equal: {
|
| @@ -1828,8 +2015,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user,
|
| case IrOpcode::kInt32Sub:
|
| return VisitWord32Compare(selector, value, cont);
|
| case IrOpcode::kWord32And:
|
| - return VisitWordCompare(selector, value, kS390_Tst64, cont,
|
| - true, OperandMode::kUint32Imm);
|
| + return VisitTestUnderMask(selector, value, cont);
|
| default:
|
| break;
|
| }
|
| @@ -1862,8 +2048,7 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user,
|
| case IrOpcode::kInt64Sub:
|
| return VisitWord64Compare(selector, value, cont);
|
| case IrOpcode::kWord64And:
|
| - return VisitWordCompare(selector, value, kS390_Tst64, cont,
|
| - true, OperandMode::kUint32Imm);
|
| + return VisitTestUnderMask(selector, value, cont);
|
| default:
|
| break;
|
| }
|
| @@ -1947,53 +2132,77 @@ void VisitWordCompareZero(InstructionSelector* selector, Node* user,
|
| }
|
| break;
|
| case IrOpcode::kInt32Sub:
|
| - return VisitWord32Compare(selector, value, cont);
|
| + if (fc == kNotEqual || fc == kEqual)
|
| + return VisitWord32Compare(selector, value, cont);
|
| + break;
|
| case IrOpcode::kWord32And:
|
| - return VisitWordCompare(selector, value, kS390_Tst32, cont, true,
|
| - OperandMode::kUint32Imm);
|
| -// TODO(mbrandy): Handle?
|
| -// case IrOpcode::kInt32Add:
|
| -// case IrOpcode::kWord32Or:
|
| -// case IrOpcode::kWord32Xor:
|
| -// case IrOpcode::kWord32Sar:
|
| -// case IrOpcode::kWord32Shl:
|
| -// case IrOpcode::kWord32Shr:
|
| -// case IrOpcode::kWord32Ror:
|
| + return VisitTestUnderMask(selector, value, cont);
|
| + case IrOpcode::kLoad: {
|
| + LoadRepresentation load_rep = LoadRepresentationOf(value->op());
|
| + switch (load_rep.representation()) {
|
| + case MachineRepresentation::kWord32:
|
| + if (opcode == kS390_LoadAndTestWord32) {
|
| + return VisitLoadAndTest(selector, opcode, user, value, cont);
|
| + }
|
| + default:
|
| + break;
|
| + }
|
| + break;
|
| + }
|
| + case IrOpcode::kInt32Add:
|
| + // can't handle overflow case.
|
| + break;
|
| + case IrOpcode::kWord32Or:
|
| + return VisitBin32op(selector, value, kS390_Or32, OrOperandMode, cont);
|
| + case IrOpcode::kWord32Xor:
|
| + return VisitBin32op(selector, value, kS390_Xor32, XorOperandMode, cont);
|
| + case IrOpcode::kWord32Sar:
|
| + case IrOpcode::kWord32Shl:
|
| + case IrOpcode::kWord32Shr:
|
| + case IrOpcode::kWord32Ror:
|
| + // doesn't generate cc, so ignore.
|
| + break;
|
| #if V8_TARGET_ARCH_S390X
|
| case IrOpcode::kInt64Sub:
|
| - return VisitWord64Compare(selector, value, cont);
|
| + if (fc == kNotEqual || fc == kEqual)
|
| + return VisitWord64Compare(selector, value, cont);
|
| + break;
|
| case IrOpcode::kWord64And:
|
| - return VisitWordCompare(selector, value, kS390_Tst64, cont, true,
|
| - OperandMode::kUint32Imm);
|
| -// TODO(mbrandy): Handle?
|
| -// case IrOpcode::kInt64Add:
|
| -// case IrOpcode::kWord64Or:
|
| -// case IrOpcode::kWord64Xor:
|
| -// case IrOpcode::kWord64Sar:
|
| -// case IrOpcode::kWord64Shl:
|
| -// case IrOpcode::kWord64Shr:
|
| -// case IrOpcode::kWord64Ror:
|
| + return VisitTestUnderMask(selector, value, cont);
|
| + case IrOpcode::kInt64Add:
|
| + // can't handle overflow case.
|
| + break;
|
| + case IrOpcode::kWord64Or:
|
| + // TODO(john.yan): need to handle
|
| + break;
|
| + case IrOpcode::kWord64Xor:
|
| + // TODO(john.yan): need to handle
|
| + break;
|
| + case IrOpcode::kWord64Sar:
|
| + case IrOpcode::kWord64Shl:
|
| + case IrOpcode::kWord64Shr:
|
| + case IrOpcode::kWord64Ror:
|
| + // doesn't generate cc, so ignore
|
| + break;
|
| #endif
|
| default:
|
| break;
|
| }
|
| }
|
|
|
| - // Branch could not be combined with a compare, emit compare against 0.
|
| - S390OperandGenerator g(selector);
|
| - VisitCompare(selector, opcode, g.UseRegister(value), g.TempImmediate(0),
|
| - cont);
|
| + // Branch could not be combined with a compare, emit LoadAndTest
|
| + VisitLoadAndTest(selector, opcode, user, value, cont, true);
|
| }
|
|
|
| void VisitWord32CompareZero(InstructionSelector* selector, Node* user,
|
| Node* value, FlagsContinuation* cont) {
|
| - VisitWordCompareZero(selector, user, value, kS390_Cmp32, cont);
|
| + VisitWordCompareZero(selector, user, value, kS390_LoadAndTestWord32, cont);
|
| }
|
|
|
| #if V8_TARGET_ARCH_S390X
|
| void VisitWord64CompareZero(InstructionSelector* selector, Node* user,
|
| Node* value, FlagsContinuation* cont) {
|
| - VisitWordCompareZero(selector, user, value, kS390_Cmp64, cont);
|
| + VisitWordCompareZero(selector, user, value, kS390_LoadAndTestWord64, cont);
|
| }
|
| #endif
|
|
|
|
|