[wasm][compiler][arm64] Support floating point selects

Add Float32Select and Float64Select as OptionalOperators and insert
these, if supported, when handling a Select expression in the wasm
graph builder. FlagsContinuation have been modified to support the
select operation and code generation support has been added for arm64.

This improves the 'Bullet' physics benchmark by ~2-3%.

Change-Id: I928c3085c9136ad8baeeb34c71c47c1c8338844c
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2763871
Commit-Queue: Martyn Capewell <martyn.capewell@arm.com>
Reviewed-by: Andreas Haas <ahaas@chromium.org>
Reviewed-by: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#73657}
This commit is contained in:
Sam Parker 2021-03-24 11:14:27 +00:00 committed by Commit Bot
parent e438ae2df3
commit 783b0e856e
29 changed files with 319 additions and 20 deletions

View File

@ -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));

View File

@ -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);

View File

@ -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();

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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

View File

@ -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:

View File

@ -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,

View File

@ -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<int>(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);

View File

@ -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);

View File

@ -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();
}

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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();

View File

@ -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;

View File

@ -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<Word32Adapter>(node);
}

View File

@ -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)

View File

@ -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<Flag, unsigned>;
@ -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();

View File

@ -734,6 +734,8 @@
V(Float64ExtractHighWord32) \
V(Float64InsertLowWord32) \
V(Float64InsertHighWord32) \
V(Float32Select) \
V(Float64Select) \
V(TaggedPoisonOnSpeculation) \
V(Word32PoisonOnSpeculation) \
V(Word64PoisonOnSpeculation) \

View File

@ -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) {

View File

@ -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:

View File

@ -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.

View File

@ -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,

View File

@ -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,

View File

@ -400,6 +400,117 @@ TEST(RunWord64Popcnt) {
#endif // V8_TARGET_ARCH_64_BIT
TEST(RunFloat32SelectRegFloatCompare) {
BufferedRawMachineAssemblerTester<float> 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<double> 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<float> 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<double> 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<float> 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<double> 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<int32_t>* m, int index) {
switch (index) {

View File

@ -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.

View File

@ -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
};