[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:
parent
e438ae2df3
commit
783b0e856e
@ -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));
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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,
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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();
|
||||
|
@ -734,6 +734,8 @@
|
||||
V(Float64ExtractHighWord32) \
|
||||
V(Float64InsertLowWord32) \
|
||||
V(Float64InsertHighWord32) \
|
||||
V(Float32Select) \
|
||||
V(Float64Select) \
|
||||
V(TaggedPoisonOnSpeculation) \
|
||||
V(Word32PoisonOnSpeculation) \
|
||||
V(Word64PoisonOnSpeculation) \
|
||||
|
@ -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) {
|
||||
|
@ -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:
|
||||
|
@ -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.
|
||||
|
@ -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,
|
||||
|
@ -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,
|
||||
|
@ -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) {
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user