diff --git a/src/codegen/arm64/macro-assembler-arm64-inl.h b/src/codegen/arm64/macro-assembler-arm64-inl.h index f6575c70ee..6a33f864ab 100644 --- a/src/codegen/arm64/macro-assembler-arm64-inl.h +++ b/src/codegen/arm64/macro-assembler-arm64-inl.h @@ -548,7 +548,7 @@ void TurboAssembler::Fcmp(const VRegister& fn, double value) { } } -void MacroAssembler::Fcsel(const VRegister& fd, const VRegister& fn, +void TurboAssembler::Fcsel(const VRegister& fd, const VRegister& fn, const VRegister& fm, Condition cond) { DCHECK(allow_macro_instructions()); DCHECK((cond != al) && (cond != nv)); diff --git a/src/codegen/arm64/macro-assembler-arm64.h b/src/codegen/arm64/macro-assembler-arm64.h index ebffc0fd3d..a749676ccc 100644 --- a/src/codegen/arm64/macro-assembler-arm64.h +++ b/src/codegen/arm64/macro-assembler-arm64.h @@ -698,6 +698,8 @@ class V8_EXPORT_PRIVATE TurboAssembler : public TurboAssemblerBase { const Operand& operand); void Csel(const Register& rd, const Register& rn, const Operand& operand, Condition cond); + inline void Fcsel(const VRegister& fd, const VRegister& fn, + const VRegister& fm, Condition cond); // Emits a runtime assert that the stack pointer is aligned. void AssertSpAligned(); @@ -1511,8 +1513,6 @@ class V8_EXPORT_PRIVATE MacroAssembler : public TurboAssembler { Condition cond); inline void Extr(const Register& rd, const Register& rn, const Register& rm, unsigned lsb); - inline void Fcsel(const VRegister& fd, const VRegister& fn, - const VRegister& fm, Condition cond); void Fcvtl(const VRegister& vd, const VRegister& vn) { DCHECK(allow_macro_instructions()); fcvtl(vd, vn); diff --git a/src/compiler/backend/arm/code-generator-arm.cc b/src/compiler/backend/arm/code-generator-arm.cc index 2eb5e276f9..dba31fe0bc 100644 --- a/src/compiler/backend/arm/code-generator-arm.cc +++ b/src/compiler/backend/arm/code-generator-arm.cc @@ -41,6 +41,7 @@ class ArmOperandConverter final : public InstructionOperandConverter { case kFlags_deoptimize_and_poison: case kFlags_set: case kFlags_trap: + case kFlags_select: return SetCC; case kFlags_none: return LeaveCC; @@ -3706,6 +3707,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { } } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + void CodeGenerator::FinishFrame(Frame* frame) { auto call_descriptor = linkage()->GetIncomingDescriptor(); diff --git a/src/compiler/backend/arm64/code-generator-arm64.cc b/src/compiler/backend/arm64/code-generator-arm64.cc index db39855470..4fd139015f 100644 --- a/src/compiler/backend/arm64/code-generator-arm64.cc +++ b/src/compiler/backend/arm64/code-generator-arm64.cc @@ -2970,6 +2970,23 @@ void CodeGenerator::AssembleArchBoolean(Instruction* instr, __ Cset(reg, cc); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + Arm64OperandConverter i(this, instr); + MachineRepresentation rep = + LocationOperand::cast(instr->OutputAt(0))->representation(); + Condition cc = FlagsConditionToCondition(condition); + DCHECK_EQ(instr->InputCount(), 4); + if (rep == MachineRepresentation::kFloat32) { + __ Fcsel(i.OutputFloat32Register(), i.InputFloat32Register(2), + i.InputFloat32Register(3), cc); + } else { + DCHECK_EQ(rep, MachineRepresentation::kFloat64); + __ Fcsel(i.OutputFloat64Register(), i.InputFloat64Register(2), + i.InputFloat64Register(3), cc); + } +} + void CodeGenerator::AssembleArchBinarySearchSwitch(Instruction* instr) { Arm64OperandConverter i(this, instr); Register input = i.InputRegister32(0); diff --git a/src/compiler/backend/arm64/instruction-selector-arm64.cc b/src/compiler/backend/arm64/instruction-selector-arm64.cc index a4bd5873e5..8b74623160 100644 --- a/src/compiler/backend/arm64/instruction-selector-arm64.cc +++ b/src/compiler/backend/arm64/instruction-selector-arm64.cc @@ -452,7 +452,7 @@ void VisitBinop(InstructionSelector* selector, Node* node, InstructionCode opcode, ImmediateMode operand_mode, FlagsContinuation* cont) { Arm64OperandGenerator g(selector); - InstructionOperand inputs[3]; + InstructionOperand inputs[4]; size_t input_count = 0; InstructionOperand outputs[1]; size_t output_count = 0; @@ -507,6 +507,11 @@ void VisitBinop(InstructionSelector* selector, Node* node, outputs[output_count++] = g.DefineAsRegister(node); } + if (cont->IsSelect()) { + inputs[input_count++] = g.UseRegister(cont->true_value()); + inputs[input_count++] = g.UseRegister(cont->false_value()); + } + DCHECK_NE(0u, input_count); DCHECK((output_count != 0) || IsComparisonField::decode(properties)); DCHECK_GE(arraysize(inputs), input_count); @@ -2133,7 +2138,15 @@ namespace { void VisitCompare(InstructionSelector* selector, InstructionCode opcode, InstructionOperand left, InstructionOperand right, FlagsContinuation* cont) { - selector->EmitWithContinuation(opcode, left, right, cont); + if (cont->IsSelect()) { + Arm64OperandGenerator g(selector); + InstructionOperand inputs[] = { left, right, + g.UseRegister(cont->true_value()), + g.UseRegister(cont->false_value()) }; + selector->EmitWithContinuation(opcode, 0, nullptr, 4, inputs, cont); + } else { + selector->EmitWithContinuation(opcode, left, right, cont); + } } // This function checks whether we can convert: @@ -2820,8 +2833,8 @@ void InstructionSelector::VisitWordCompareZero(Node* user, Node* value, g.UseRegister(value), g.Label(cont->true_block()), g.Label(cont->false_block())); } else { - EmitWithContinuation(cont->Encode(kArm64Tst32), g.UseRegister(value), - g.UseRegister(value), cont); + VisitCompare(this, cont->Encode(kArm64Tst32), g.UseRegister(value), + g.UseRegister(value), cont); } } @@ -4017,7 +4030,9 @@ InstructionSelector::SupportedMachineOperatorFlags() { MachineOperatorBuilder::kUint32DivIsSafe | MachineOperatorBuilder::kWord32ReverseBits | MachineOperatorBuilder::kWord64ReverseBits | - MachineOperatorBuilder::kSatConversionIsSafe; + MachineOperatorBuilder::kSatConversionIsSafe | + MachineOperatorBuilder::kFloat32Select | + MachineOperatorBuilder::kFloat64Select; } // static diff --git a/src/compiler/backend/code-generator.cc b/src/compiler/backend/code-generator.cc index 934ab0abb7..678b268cc8 100644 --- a/src/compiler/backend/code-generator.cc +++ b/src/compiler/backend/code-generator.cc @@ -873,6 +873,10 @@ CodeGenerator::CodeGenResult CodeGenerator::AssembleInstruction( AssembleArchBoolean(instr, condition); break; } + case kFlags_select: { + AssembleArchSelect(instr, condition); + break; + } case kFlags_trap: { #if V8_ENABLE_WEBASSEMBLY AssembleArchTrap(instr, condition); diff --git a/src/compiler/backend/code-generator.h b/src/compiler/backend/code-generator.h index 11b5cf207f..1d1bda743b 100644 --- a/src/compiler/backend/code-generator.h +++ b/src/compiler/backend/code-generator.h @@ -261,6 +261,7 @@ class V8_EXPORT_PRIVATE CodeGenerator final : public GapResolver::Assembler { void AssembleArchDeoptBranch(Instruction* instr, BranchInfo* branch); void AssembleArchBoolean(Instruction* instr, FlagsCondition condition); + void AssembleArchSelect(Instruction* instr, FlagsCondition condition); #if V8_ENABLE_WEBASSEMBLY void AssembleArchTrap(Instruction* instr, FlagsCondition condition); #endif // V8_ENABLE_WEBASSEMBLY diff --git a/src/compiler/backend/ia32/code-generator-ia32.cc b/src/compiler/backend/ia32/code-generator-ia32.cc index 54c34dc7c6..5d256d1167 100644 --- a/src/compiler/backend/ia32/code-generator-ia32.cc +++ b/src/compiler/backend/ia32/code-generator-ia32.cc @@ -4451,6 +4451,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { __ jmp(Operand::JumpTable(input, times_system_pointer_size, table)); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + // The calling convention for JSFunctions on IA32 passes arguments on the // stack and the JSFunction and context in EDI and ESI, respectively, thus // the steps of the call look as follows: diff --git a/src/compiler/backend/instruction-codes.h b/src/compiler/backend/instruction-codes.h index 3ca0e1d262..31d669813e 100644 --- a/src/compiler/backend/instruction-codes.h +++ b/src/compiler/backend/instruction-codes.h @@ -212,7 +212,8 @@ enum FlagsMode { kFlags_deoptimize = 3, kFlags_deoptimize_and_poison = 4, kFlags_set = 5, - kFlags_trap = 6 + kFlags_trap = 6, + kFlags_select = 7, }; V8_EXPORT_PRIVATE std::ostream& operator<<(std::ostream& os, diff --git a/src/compiler/backend/instruction-selector.cc b/src/compiler/backend/instruction-selector.cc index 1eccac92f7..20617de7a0 100644 --- a/src/compiler/backend/instruction-selector.cc +++ b/src/compiler/backend/instruction-selector.cc @@ -880,7 +880,7 @@ Instruction* InstructionSelector::EmitWithContinuation( AppendDeoptimizeArguments(&continuation_inputs_, cont->kind(), cont->reason(), cont->feedback(), FrameState{cont->frame_state()}); - } else if (cont->IsSet()) { + } else if (cont->IsSet() || cont->IsSelect()) { continuation_outputs_.push_back(g.DefineAsRegister(cont->result())); } else if (cont->IsTrap()) { int trap_id = static_cast(cont->trap_id()); @@ -1748,6 +1748,8 @@ void InstructionSelector::VisitNode(Node* node) { return MarkAsFloat32(node), VisitFloat32Max(node); case IrOpcode::kFloat32Min: return MarkAsFloat32(node), VisitFloat32Min(node); + case IrOpcode::kFloat32Select: + return MarkAsFloat32(node), VisitSelect(node); case IrOpcode::kFloat64Add: return MarkAsFloat64(node), VisitFloat64Add(node); case IrOpcode::kFloat64Sub: @@ -1816,6 +1818,8 @@ void InstructionSelector::VisitNode(Node* node) { return VisitFloat64LessThan(node); case IrOpcode::kFloat64LessThanOrEqual: return VisitFloat64LessThanOrEqual(node); + case IrOpcode::kFloat64Select: + return MarkAsFloat64(node), VisitSelect(node); case IrOpcode::kFloat32RoundDown: return MarkAsFloat32(node), VisitFloat32RoundDown(node); case IrOpcode::kFloat64RoundDown: @@ -3138,6 +3142,13 @@ void InstructionSelector::VisitDeoptimizeUnless(Node* node) { } } +void InstructionSelector::VisitSelect(Node* node) { + FlagsContinuation cont = + FlagsContinuation::ForSelect(kNotEqual, node, + node->InputAt(1), node->InputAt(2)); + VisitWordCompareZero(node, node->InputAt(0), &cont); +} + void InstructionSelector::VisitDynamicCheckMapsWithDeoptUnless(Node* node) { OperandGenerator g(this); DynamicCheckMapsWithDeoptUnlessNode n(node); diff --git a/src/compiler/backend/instruction-selector.h b/src/compiler/backend/instruction-selector.h index c7bc99005d..9f60954ef4 100644 --- a/src/compiler/backend/instruction-selector.h +++ b/src/compiler/backend/instruction-selector.h @@ -92,6 +92,11 @@ class FlagsContinuation final { return FlagsContinuation(condition, trap_id, result); } + static FlagsContinuation ForSelect(FlagsCondition condition, Node* result, + Node* true_value, Node* false_value) { + return FlagsContinuation(condition, result, true_value, false_value); + } + bool IsNone() const { return mode_ == kFlags_none; } bool IsBranch() const { return mode_ == kFlags_branch || mode_ == kFlags_branch_and_poison; @@ -105,6 +110,7 @@ class FlagsContinuation final { } bool IsSet() const { return mode_ == kFlags_set; } bool IsTrap() const { return mode_ == kFlags_trap; } + bool IsSelect() const { return mode_ == kFlags_select; } FlagsCondition condition() const { DCHECK(!IsNone()); return condition_; @@ -138,7 +144,7 @@ class FlagsContinuation final { return extra_args_count_; } Node* result() const { - DCHECK(IsSet()); + DCHECK(IsSet() || IsSelect()); return frame_state_or_result_; } TrapId trap_id() const { @@ -153,6 +159,14 @@ class FlagsContinuation final { DCHECK(IsBranch()); return false_block_; } + Node* true_value() const { + DCHECK(IsSelect()); + return true_value_; + } + Node* false_value() const { + DCHECK(IsSelect()); + return false_value_; + } void Negate() { DCHECK(!IsNone()); @@ -244,6 +258,18 @@ class FlagsContinuation final { DCHECK_NOT_NULL(result); } + FlagsContinuation(FlagsCondition condition, Node* result, + Node* true_value, Node* false_value) + : mode_(kFlags_select), + condition_(condition), + frame_state_or_result_(result), + true_value_(true_value), + false_value_(false_value) { + DCHECK_NOT_NULL(result); + DCHECK_NOT_NULL(true_value); + DCHECK_NOT_NULL(false_value); + } + FlagsMode const mode_; FlagsCondition condition_; DeoptimizeKind kind_; // Only valid if mode_ == kFlags_deoptimize* @@ -256,6 +282,8 @@ class FlagsContinuation final { BasicBlock* true_block_; // Only valid if mode_ == kFlags_branch*. BasicBlock* false_block_; // Only valid if mode_ == kFlags_branch*. TrapId trap_id_; // Only valid if mode_ == kFlags_trap. + Node* true_value_; // Only valid if mode_ == kFlags_select. + Node* false_value_; // Only valid if mode_ == kFlags_select. }; // This struct connects nodes of parameters which are going to be pushed on the @@ -631,6 +659,7 @@ class V8_EXPORT_PRIVATE InstructionSelector final { void VisitSwitch(Node* node, const SwitchInfo& sw); void VisitDeoptimize(DeoptimizeKind kind, DeoptimizeReason reason, FeedbackSource const& feedback, FrameState frame_state); + void VisitSelect(Node* node); void VisitReturn(Node* ret); void VisitThrow(Node* node); void VisitRetain(Node* node); diff --git a/src/compiler/backend/instruction.cc b/src/compiler/backend/instruction.cc index 5d9cc490b6..6bac263f9c 100644 --- a/src/compiler/backend/instruction.cc +++ b/src/compiler/backend/instruction.cc @@ -416,6 +416,8 @@ std::ostream& operator<<(std::ostream& os, const FlagsMode& fm) { return os << "set"; case kFlags_trap: return os << "trap"; + case kFlags_select: + return os << "select"; } UNREACHABLE(); } diff --git a/src/compiler/backend/mips/code-generator-mips.cc b/src/compiler/backend/mips/code-generator-mips.cc index 4a1dafd9f2..4066ba77e8 100644 --- a/src/compiler/backend/mips/code-generator-mips.cc +++ b/src/compiler/backend/mips/code-generator-mips.cc @@ -4049,6 +4049,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { }); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + void CodeGenerator::FinishFrame(Frame* frame) { auto call_descriptor = linkage()->GetIncomingDescriptor(); diff --git a/src/compiler/backend/mips64/code-generator-mips64.cc b/src/compiler/backend/mips64/code-generator-mips64.cc index e154495e18..6edb153943 100644 --- a/src/compiler/backend/mips64/code-generator-mips64.cc +++ b/src/compiler/backend/mips64/code-generator-mips64.cc @@ -4262,6 +4262,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { }); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + void CodeGenerator::FinishFrame(Frame* frame) { auto call_descriptor = linkage()->GetIncomingDescriptor(); diff --git a/src/compiler/backend/ppc/code-generator-ppc.cc b/src/compiler/backend/ppc/code-generator-ppc.cc index 9dcc3917d0..c4e597bb15 100644 --- a/src/compiler/backend/ppc/code-generator-ppc.cc +++ b/src/compiler/backend/ppc/code-generator-ppc.cc @@ -43,6 +43,7 @@ class PPCOperandConverter final : public InstructionOperandConverter { case kFlags_deoptimize_and_poison: case kFlags_set: case kFlags_trap: + case kFlags_select: return SetRC; case kFlags_none: return LeaveRC; @@ -4069,6 +4070,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { __ Jump(kScratchReg); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + void CodeGenerator::FinishFrame(Frame* frame) { auto call_descriptor = linkage()->GetIncomingDescriptor(); const RegList double_saves = call_descriptor->CalleeSavedFPRegisters(); diff --git a/src/compiler/backend/s390/code-generator-s390.cc b/src/compiler/backend/s390/code-generator-s390.cc index 76fe1ccfdb..0d1a3ed624 100644 --- a/src/compiler/backend/s390/code-generator-s390.cc +++ b/src/compiler/backend/s390/code-generator-s390.cc @@ -4245,6 +4245,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { __ Jump(kScratchReg); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + void CodeGenerator::FinishFrame(Frame* frame) { auto call_descriptor = linkage()->GetIncomingDescriptor(); const RegList double_saves = call_descriptor->CalleeSavedFPRegisters(); diff --git a/src/compiler/backend/x64/code-generator-x64.cc b/src/compiler/backend/x64/code-generator-x64.cc index feef866620..bc08544e57 100644 --- a/src/compiler/backend/x64/code-generator-x64.cc +++ b/src/compiler/backend/x64/code-generator-x64.cc @@ -4440,6 +4440,11 @@ void CodeGenerator::AssembleArchTableSwitch(Instruction* instr) { __ jmp(Operand(kScratchRegister, input, times_8, 0)); } +void CodeGenerator::AssembleArchSelect(Instruction* instr, + FlagsCondition condition) { + UNIMPLEMENTED(); +} + namespace { static const int kQuadWordSize = 16; diff --git a/src/compiler/machine-operator-reducer.cc b/src/compiler/machine-operator-reducer.cc index 363e929597..5d61dfac6a 100644 --- a/src/compiler/machine-operator-reducer.cc +++ b/src/compiler/machine-operator-reducer.cc @@ -1951,6 +1951,10 @@ Reduction MachineOperatorReducer::ReduceWordNXor(Node* node) { Reduction MachineOperatorReducer::ReduceWord32Xor(Node* node) { DCHECK_EQ(IrOpcode::kWord32Xor, node->opcode()); + Int32BinopMatcher m(node); + if (m.right().IsWord32Equal() && m.left().Is(1)) { + return Replace(Word32Equal(m.right().node(), Int32Constant(0))); + } return ReduceWordNXor(node); } diff --git a/src/compiler/machine-operator.cc b/src/compiler/machine-operator.cc index bf81716f2f..b06e9b9a75 100644 --- a/src/compiler/machine-operator.cc +++ b/src/compiler/machine-operator.cc @@ -582,7 +582,9 @@ std::ostream& operator<<(std::ostream& os, TruncateKind kind) { V(Float64RoundTruncate, Operator::kNoProperties, 1, 0, 1) \ V(Float64RoundTiesAway, Operator::kNoProperties, 1, 0, 1) \ V(Float32RoundTiesEven, Operator::kNoProperties, 1, 0, 1) \ - V(Float64RoundTiesEven, Operator::kNoProperties, 1, 0, 1) + V(Float64RoundTiesEven, Operator::kNoProperties, 1, 0, 1) \ + V(Float32Select, Operator::kNoProperties, 3, 0, 1) \ + V(Float64Select, Operator::kNoProperties, 3, 0, 1) // The format is: // V(Name, properties, value_input_count, control_input_count, output_count) diff --git a/src/compiler/machine-operator.h b/src/compiler/machine-operator.h index fec6a71e60..37460d5bb9 100644 --- a/src/compiler/machine-operator.h +++ b/src/compiler/machine-operator.h @@ -270,6 +270,8 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final kWord64Popcnt = 1u << 15, kWord32ReverseBits = 1u << 16, kWord64ReverseBits = 1u << 17, + kFloat32Select = 1u << 18, + kFloat64Select = 1u << 19, kInt32AbsWithOverflow = 1u << 20, kInt64AbsWithOverflow = 1u << 21, kWord32Rol = 1u << 22, @@ -281,7 +283,8 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final kFloat64RoundTiesAway | kFloat32RoundTiesEven | kFloat64RoundTiesEven | kWord32Ctz | kWord64Ctz | kWord32Popcnt | kWord64Popcnt | kWord32ReverseBits | kWord64ReverseBits | kInt32AbsWithOverflow | - kInt64AbsWithOverflow | kWord32Rol | kWord64Rol | kSatConversionIsSafe + kInt64AbsWithOverflow | kWord32Rol | kWord64Rol | kSatConversionIsSafe | + kFloat32Select | kFloat64Select }; using Flags = base::Flags; @@ -577,6 +580,10 @@ class V8_EXPORT_PRIVATE MachineOperatorBuilder final const OptionalOperator Float32RoundTiesEven(); const OptionalOperator Float64RoundTiesEven(); + // Floating point conditional selects. + const OptionalOperator Float32Select(); + const OptionalOperator Float64Select(); + // Floating point neg. const Operator* Float32Neg(); const Operator* Float64Neg(); diff --git a/src/compiler/opcodes.h b/src/compiler/opcodes.h index b70f4f6693..3e5314d857 100644 --- a/src/compiler/opcodes.h +++ b/src/compiler/opcodes.h @@ -734,6 +734,8 @@ V(Float64ExtractHighWord32) \ V(Float64InsertLowWord32) \ V(Float64InsertHighWord32) \ + V(Float32Select) \ + V(Float64Select) \ V(TaggedPoisonOnSpeculation) \ V(Word32PoisonOnSpeculation) \ V(Word64PoisonOnSpeculation) \ diff --git a/src/compiler/raw-machine-assembler.h b/src/compiler/raw-machine-assembler.h index 764c0a3224..6be006b9a4 100644 --- a/src/compiler/raw-machine-assembler.h +++ b/src/compiler/raw-machine-assembler.h @@ -702,6 +702,12 @@ class V8_EXPORT_PRIVATE RawMachineAssembler { Node* Float64GreaterThanOrEqual(Node* a, Node* b) { return Float64LessThanOrEqual(b, a); } + Node* Float32Select(Node* condition, Node* b, Node* c) { + return AddNode(machine()->Float32Select().op(), condition, b, c); + } + Node* Float64Select(Node* condition, Node* b, Node* c) { + return AddNode(machine()->Float64Select().op(), condition, b, c); + } // Conversions. Node* BitcastTaggedToWord(Node* a) { diff --git a/src/compiler/verifier.cc b/src/compiler/verifier.cc index 3b2103684c..b5b3ab5a98 100644 --- a/src/compiler/verifier.cc +++ b/src/compiler/verifier.cc @@ -1791,6 +1791,8 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) { case IrOpcode::kFloat64ExtractHighWord32: case IrOpcode::kFloat64InsertLowWord32: case IrOpcode::kFloat64InsertHighWord32: + case IrOpcode::kFloat32Select: + case IrOpcode::kFloat64Select: case IrOpcode::kInt32PairAdd: case IrOpcode::kInt32PairSub: case IrOpcode::kInt32PairMul: diff --git a/src/compiler/wasm-compiler.cc b/src/compiler/wasm-compiler.cc index 55b76c6495..2f12e658db 100644 --- a/src/compiler/wasm-compiler.cc +++ b/src/compiler/wasm-compiler.cc @@ -1292,6 +1292,28 @@ Node* WasmGraphBuilder::BranchExpectFalse(Node* cond, Node** true_node, return gasm_->Branch(cond, true_node, false_node, BranchHint::kFalse); } +Node* WasmGraphBuilder::Select(Node *cond, Node* true_node, + Node* false_node, wasm::ValueType type) { + MachineOperatorBuilder* m = mcgraph()->machine(); + wasm::ValueKind kind = type.kind(); + // Lower to select if supported. + if (kind == wasm::kF32 && m->Float32Select().IsSupported()) { + return mcgraph()->graph()->NewNode(m->Float32Select().op(), cond, + true_node, false_node); + } + if (kind == wasm::kF64 && m->Float64Select().IsSupported()) { + return mcgraph()->graph()->NewNode(m->Float64Select().op(), cond, + true_node, false_node); + } + // Default to control-flow. + Node* controls[2]; + BranchNoHint(cond, &controls[0], &controls[1]); + Node* merge = Merge(2, controls); + SetControl(merge); + Node* inputs[] = {true_node, false_node, merge}; + return Phi(type, 2, inputs); +} + TrapId WasmGraphBuilder::GetTrapIdForTrap(wasm::TrapReason reason) { // TODO(wasm): "!env_" should not happen when compiling an actual wasm // function. diff --git a/src/compiler/wasm-compiler.h b/src/compiler/wasm-compiler.h index 10fc41229d..722dc97c83 100644 --- a/src/compiler/wasm-compiler.h +++ b/src/compiler/wasm-compiler.h @@ -286,6 +286,9 @@ class WasmGraphBuilder { wasm::WasmCodePosition position); void TrapIfFalse(wasm::TrapReason reason, Node* cond, wasm::WasmCodePosition position); + Node* Select(Node *cond, Node* true_node, Node* false_node, + wasm::ValueType type); + void TrapIfEq32(wasm::TrapReason reason, Node* node, int32_t val, wasm::WasmCodePosition position); void ZeroCheck32(wasm::TrapReason reason, Node* node, diff --git a/src/wasm/graph-builder-interface.cc b/src/wasm/graph-builder-interface.cc index 7c9095b15a..7a903f5fa7 100644 --- a/src/wasm/graph-builder-interface.cc +++ b/src/wasm/graph-builder-interface.cc @@ -419,13 +419,8 @@ class WasmGraphBuildingInterface { void Select(FullDecoder* decoder, const Value& cond, const Value& fval, const Value& tval, Value* result) { - TFNode* controls[2]; - builder_->BranchNoHint(cond.node, &controls[0], &controls[1]); - TFNode* merge = builder_->Merge(2, controls); - TFNode* inputs[] = {tval.node, fval.node, merge}; - TFNode* phi = builder_->Phi(tval.type, 2, inputs); - result->node = phi; - builder_->SetControl(merge); + result->node = + builder_->Select(cond.node, tval.node, fval.node, result->type); } ValueVector CopyStackValues(FullDecoder* decoder, uint32_t count, diff --git a/test/cctest/compiler/test-run-machops.cc b/test/cctest/compiler/test-run-machops.cc index 5f7b6eed88..3160848b68 100644 --- a/test/cctest/compiler/test-run-machops.cc +++ b/test/cctest/compiler/test-run-machops.cc @@ -400,6 +400,117 @@ TEST(RunWord64Popcnt) { #endif // V8_TARGET_ARCH_64_BIT +TEST(RunFloat32SelectRegFloatCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Float32(), + MachineType::Float32()); + if (!m.machine()->Float32Select().IsSupported()) { + return; + } + + Node* cmp = m.Float32Equal(m.Parameter(0), m.Parameter(1)); + m.Return(m.Float32Select(cmp, m.Parameter(0), m.Parameter(1))); + + FOR_FLOAT32_INPUTS(pl) { + FOR_FLOAT32_INPUTS(pr) { + float expected_result = pl == pr ? pl : pr; + CHECK_FLOAT_EQ(expected_result, m.Call(pl, pr)); + } + } +} + +TEST(RunFloat64SelectRegFloatCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Float64(), + MachineType::Float64()); + if (!m.machine()->Float64Select().IsSupported()) { + return; + } + + Node* cmp = m.Float64LessThan(m.Parameter(0), m.Parameter(1)); + m.Return(m.Float64Select(cmp, m.Parameter(0), m.Parameter(1))); + + FOR_FLOAT64_INPUTS(pl) { + FOR_FLOAT64_INPUTS(pr) { + double expected_result = pl < pr ? pl : pr; + CHECK_DOUBLE_EQ(expected_result, m.Call(pl, pr)); + } + } +} + +TEST(RunFloat32SelectImmediateOnLeftFloatCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Float32()); + if (!m.machine()->Float32Select().IsSupported()) { + return; + } + + const float pl = -5.0; + Node* a = m.Float32Constant(pl); + Node* cmp = m.Float32LessThan(a, m.Parameter(0)); + m.Return(m.Float32Select(cmp, a, m.Parameter(0))); + + FOR_FLOAT32_INPUTS(pr) { + float expected_result = pl < pr ? pl : pr; + CHECK_FLOAT_EQ(expected_result, m.Call(pr)); + } +} + +TEST(RunFloat64SelectImmediateOnRightFloatCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Float64()); + if (!m.machine()->Float64Select().IsSupported()) { + return; + } + + double pr = 5.0; + Node* b = m.Float64Constant(pr); + Node* cmp = m.Float64LessThanOrEqual(m.Parameter(0), b); + m.Return(m.Float64Select(cmp, m.Parameter(0), b)); + + FOR_FLOAT64_INPUTS(pl) { + double expected_result = pl <= pr ? pl : pr; + CHECK_DOUBLE_EQ(expected_result, m.Call(pl)); + } +} + +TEST(RunFloat32SelectImmediateIntCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Int32(), + MachineType::Int32()); + if (!m.machine()->Float32Select().IsSupported()) { + return; + } + + float tval = -1.0; + float fval = 1.0; + Node* cmp = m.Int32LessThanOrEqual(m.Parameter(0), m.Parameter(1)); + m.Return(m.Float64Select(cmp, m.Float32Constant(tval), + m.Float32Constant(fval))); + + FOR_INT32_INPUTS(pl) { + FOR_INT32_INPUTS(pr) { + float expected_result = pl <= pr ? tval : fval; + CHECK_FLOAT_EQ(expected_result, m.Call(pl, pr)); + } + } +} + +TEST(RunFloat64SelectImmediateIntCompare) { + BufferedRawMachineAssemblerTester m(MachineType::Int64(), + MachineType::Int64()); + if (!m.machine()->Float64Select().IsSupported()) { + return; + } + + double tval = -1.0; + double fval = 1.0; + Node* cmp = m.Int64LessThan(m.Parameter(0), m.Parameter(1)); + m.Return(m.Float64Select(cmp, m.Float64Constant(tval), + m.Float64Constant(fval))); + + FOR_INT64_INPUTS(pl) { + FOR_INT64_INPUTS(pr) { + double expected_result = pl < pr ? tval : fval; + CHECK_DOUBLE_EQ(expected_result, m.Call(pl, pr)); + } + } +} static Node* Int32Input(RawMachineAssemblerTester* m, int index) { switch (index) { diff --git a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc index 7926060186..39f0740d8a 100644 --- a/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc +++ b/test/unittests/compiler/arm64/instruction-selector-arm64-unittest.cc @@ -2719,6 +2719,32 @@ TEST_P(InstructionSelectorFPCmpTest, WithImmediateZeroOnLeft) { INSTANTIATE_TEST_SUITE_P(InstructionSelectorTest, InstructionSelectorFPCmpTest, ::testing::ValuesIn(kFPCmpInstructions)); +TEST_F(InstructionSelectorTest, Float32SelectWithRegisters) { + StreamBuilder m(this, MachineType::Int32(), MachineType::Float32(), + MachineType::Float32()); + Node* cond = m.Int32Constant(1); + m.Return(m.Float32Select(cond, m.Parameter(0), m.Parameter(1))); + Stream s = m.Build(); + EXPECT_EQ(kArm64Tst32, s[0]->arch_opcode()); + EXPECT_EQ(4U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(kFlags_select, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); +} + +TEST_F(InstructionSelectorTest, Float64SelectWithRegisters) { + StreamBuilder m(this, MachineType::Int32(), MachineType::Float64(), + MachineType::Float64()); + Node* cond = m.Int32Constant(1); + m.Return(m.Float64Select(cond, m.Parameter(0), m.Parameter(1))); + Stream s = m.Build(); + EXPECT_EQ(kArm64Tst32, s[0]->arch_opcode()); + EXPECT_EQ(4U, s[0]->InputCount()); + EXPECT_EQ(1U, s[0]->OutputCount()); + EXPECT_EQ(kFlags_select, s[0]->flags_mode()); + EXPECT_EQ(kNotEqual, s[0]->flags_condition()); +} + // ----------------------------------------------------------------------------- // Conversions. diff --git a/test/unittests/compiler/machine-operator-unittest.cc b/test/unittests/compiler/machine-operator-unittest.cc index e53050ad55..edb2942084 100644 --- a/test/unittests/compiler/machine-operator-unittest.cc +++ b/test/unittests/compiler/machine-operator-unittest.cc @@ -306,6 +306,8 @@ const OptionalOperatorEntry kOptionalOperators[] = { OPTIONAL_ENTRY(Float64RoundDown, 1, 0, 1), // -- OPTIONAL_ENTRY(Float64RoundTruncate, 1, 0, 1), // -- OPTIONAL_ENTRY(Float64RoundTiesAway, 1, 0, 1), // -- + OPTIONAL_ENTRY(Float64Select, 3, 0, 1), // -- + OPTIONAL_ENTRY(Float32Select, 3, 0, 1), // -- #undef OPTIONAL_ENTRY };