[turbofan] Add fast-path for Math.hypot().
This adds a fast-path to inline `Math.hypot(v1,...,vn)` into optimized
code assuming that v1,...,vn are already numbers. The inlining follows
the general C++ implementation (which was also simplified a bit), and
thus uses Kahan summation to avoid rounding errors.
This improves the benchmark in [1] from around
testHypot: 656 ms.
testSqrt: 105 ms.
testExp: 103 ms.
to
testHypot: 147 ms.
testSqrt: 103 ms.
testExp: 102 ms.
so its roughly a **4.5x improvement**.
[1] 60a34c0dd2/bench-math-hypot.js
Bug: chromium:979893
Change-Id: Id834d5613bc22aa7ce27b9d6eca1f1f1979aa3e7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1684178
Auto-Submit: Benedikt Meurer <bmeurer@chromium.org>
Reviewed-by: Sigurd Schneider <sigurds@chromium.org>
Commit-Queue: Benedikt Meurer <bmeurer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#62483}
This commit is contained in:
parent
2f1e0b76e6
commit
545a250229
@ -20,7 +20,6 @@ BUILTIN(MathHypot) {
|
||||
if (length == 0) return Smi::kZero;
|
||||
DCHECK_LT(0, length);
|
||||
double max = 0;
|
||||
bool one_arg_is_nan = false;
|
||||
std::vector<double> abs_values;
|
||||
abs_values.reserve(length);
|
||||
for (int i = 0; i < length; i++) {
|
||||
@ -28,29 +27,20 @@ BUILTIN(MathHypot) {
|
||||
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(isolate, x,
|
||||
Object::ToNumber(isolate, x));
|
||||
double abs_value = std::abs(x->Number());
|
||||
|
||||
if (std::isnan(abs_value)) {
|
||||
one_arg_is_nan = true;
|
||||
} else {
|
||||
abs_values.push_back(abs_value);
|
||||
if (max < abs_value) {
|
||||
max = abs_value;
|
||||
}
|
||||
abs_values.push_back(abs_value);
|
||||
// Use negation here to make sure that {max} is NaN
|
||||
// in the end in case any of the arguments was NaN.
|
||||
if (!(abs_value <= max)) {
|
||||
max = abs_value;
|
||||
}
|
||||
}
|
||||
|
||||
if (max == V8_INFINITY) {
|
||||
return *isolate->factory()->NewNumber(V8_INFINITY);
|
||||
}
|
||||
|
||||
if (one_arg_is_nan) {
|
||||
return ReadOnlyRoots(isolate).nan_value();
|
||||
}
|
||||
|
||||
if (max == 0) {
|
||||
return Smi::kZero;
|
||||
} else if (max == V8_INFINITY) {
|
||||
return ReadOnlyRoots(isolate).infinity_value();
|
||||
}
|
||||
DCHECK_GT(max, 0);
|
||||
DCHECK(!(max <= 0));
|
||||
|
||||
// Kahan summation to avoid rounding errors.
|
||||
// Normalize the numbers to the largest one to avoid overflow.
|
||||
|
@ -179,6 +179,100 @@ Reduction JSCallReducer::ReduceMathMinMax(Node* node, const Operator* op,
|
||||
return Replace(value);
|
||||
}
|
||||
|
||||
// ES section #sec-math.hypot Math.hypot ( value1, value2, ...values )
|
||||
Reduction JSCallReducer::ReduceMathHypot(Node* node) {
|
||||
CallParameters const& p = CallParametersOf(node->op());
|
||||
if (p.speculation_mode() == SpeculationMode::kDisallowSpeculation) {
|
||||
return NoChange();
|
||||
}
|
||||
if (node->op()->ValueInputCount() < 3) {
|
||||
Node* value = jsgraph()->ZeroConstant();
|
||||
ReplaceWithValue(node, value);
|
||||
return Replace(value);
|
||||
}
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
NodeVector values(graph()->zone());
|
||||
|
||||
Node* max = effect =
|
||||
graph()->NewNode(simplified()->SpeculativeToNumber(
|
||||
NumberOperationHint::kNumberOrOddball, p.feedback()),
|
||||
NodeProperties::GetValueInput(node, 2), effect, control);
|
||||
max = graph()->NewNode(simplified()->NumberAbs(), max);
|
||||
values.push_back(max);
|
||||
for (int i = 3; i < node->op()->ValueInputCount(); ++i) {
|
||||
Node* input = effect = graph()->NewNode(
|
||||
simplified()->SpeculativeToNumber(NumberOperationHint::kNumberOrOddball,
|
||||
p.feedback()),
|
||||
NodeProperties::GetValueInput(node, i), effect, control);
|
||||
input = graph()->NewNode(simplified()->NumberAbs(), input);
|
||||
values.push_back(input);
|
||||
|
||||
// Make sure {max} is NaN in the end in case any argument was NaN.
|
||||
max = graph()->NewNode(
|
||||
common()->Select(MachineRepresentation::kTagged),
|
||||
graph()->NewNode(simplified()->NumberLessThanOrEqual(), input, max),
|
||||
max, input);
|
||||
}
|
||||
|
||||
Node* check0 = graph()->NewNode(simplified()->NumberEqual(), max,
|
||||
jsgraph()->ZeroConstant());
|
||||
Node* branch0 =
|
||||
graph()->NewNode(common()->Branch(BranchHint::kFalse), check0, control);
|
||||
|
||||
Node* if_true0 = graph()->NewNode(common()->IfTrue(), branch0);
|
||||
Node* vtrue0 = jsgraph()->ZeroConstant();
|
||||
|
||||
Node* if_false0 = graph()->NewNode(common()->IfFalse(), branch0);
|
||||
Node* vfalse0;
|
||||
{
|
||||
Node* check1 = graph()->NewNode(simplified()->NumberEqual(), max,
|
||||
jsgraph()->Constant(V8_INFINITY));
|
||||
Node* branch1 = graph()->NewNode(common()->Branch(BranchHint::kFalse),
|
||||
check1, if_false0);
|
||||
|
||||
Node* if_true1 = graph()->NewNode(common()->IfTrue(), branch1);
|
||||
Node* vtrue1 = jsgraph()->Constant(V8_INFINITY);
|
||||
|
||||
Node* if_false1 = graph()->NewNode(common()->IfFalse(), branch1);
|
||||
Node* vfalse1;
|
||||
{
|
||||
// Kahan summation to avoid rounding errors.
|
||||
// Normalize the numbers to the largest one to avoid overflow.
|
||||
Node* sum = jsgraph()->ZeroConstant();
|
||||
Node* compensation = jsgraph()->ZeroConstant();
|
||||
for (Node* value : values) {
|
||||
Node* n = graph()->NewNode(simplified()->NumberDivide(), value, max);
|
||||
Node* summand = graph()->NewNode(
|
||||
simplified()->NumberSubtract(),
|
||||
graph()->NewNode(simplified()->NumberMultiply(), n, n),
|
||||
compensation);
|
||||
Node* preliminary =
|
||||
graph()->NewNode(simplified()->NumberAdd(), sum, summand);
|
||||
compensation = graph()->NewNode(
|
||||
simplified()->NumberSubtract(),
|
||||
graph()->NewNode(simplified()->NumberSubtract(), preliminary, sum),
|
||||
summand);
|
||||
sum = preliminary;
|
||||
}
|
||||
vfalse1 = graph()->NewNode(
|
||||
simplified()->NumberMultiply(),
|
||||
graph()->NewNode(simplified()->NumberSqrt(), sum), max);
|
||||
}
|
||||
|
||||
if_false0 = graph()->NewNode(common()->Merge(2), if_true1, if_false1);
|
||||
vfalse0 = graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2),
|
||||
vtrue1, vfalse1, if_false0);
|
||||
}
|
||||
|
||||
control = graph()->NewNode(common()->Merge(2), if_true0, if_false0);
|
||||
Node* value =
|
||||
graph()->NewNode(common()->Phi(MachineRepresentation::kTagged, 2), vtrue0,
|
||||
vfalse0, control);
|
||||
ReplaceWithValue(node, value, effect, control);
|
||||
return Replace(value);
|
||||
}
|
||||
|
||||
Reduction JSCallReducer::Reduce(Node* node) {
|
||||
switch (node->opcode()) {
|
||||
case IrOpcode::kJSConstruct:
|
||||
@ -3518,6 +3612,8 @@ Reduction JSCallReducer::ReduceJSCall(Node* node,
|
||||
return ReduceMathUnary(node, simplified()->NumberFloor());
|
||||
case Builtins::kMathFround:
|
||||
return ReduceMathUnary(node, simplified()->NumberFround());
|
||||
case Builtins::kMathHypot:
|
||||
return ReduceMathHypot(node);
|
||||
case Builtins::kMathLog:
|
||||
return ReduceMathUnary(node, simplified()->NumberLog());
|
||||
case Builtins::kMathLog1p:
|
||||
|
@ -156,6 +156,7 @@ class V8_EXPORT_PRIVATE JSCallReducer final : public AdvancedReducer {
|
||||
Reduction ReduceMathImul(Node* node);
|
||||
Reduction ReduceMathClz32(Node* node);
|
||||
Reduction ReduceMathMinMax(Node* node, const Operator* op, Node* empty_value);
|
||||
Reduction ReduceMathHypot(Node* node);
|
||||
|
||||
Reduction ReduceNumberIsFinite(Node* node);
|
||||
Reduction ReduceNumberIsInteger(Node* node);
|
||||
|
Loading…
Reference in New Issue
Block a user