[builtins] Migrate Math.clz32 to a TurboFan builtin.
This allows us to remove the troublesome %_MathClz32 intrinsic and also allows us to utilize the functionality that is already available in TurboFan. Also introduce a proper NumberClz32 operator so we don't need to introduce a machine operator at the JS level. R=epertoso@chromium.org Review URL: https://codereview.chromium.org/1852553003 Cr-Commit-Position: refs/heads/master@{#35208}
This commit is contained in:
parent
652853daa6
commit
eaa92feb9a
@ -1520,6 +1520,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
|
||||
SimpleInstallFunction(math, "asin", Builtins::kMathAsin, 1, true);
|
||||
SimpleInstallFunction(math, "atan", Builtins::kMathAtan, 1, true);
|
||||
SimpleInstallFunction(math, "ceil", Builtins::kMathCeil, 1, true);
|
||||
SimpleInstallFunction(math, "clz32", Builtins::kMathClz32, 1, true);
|
||||
Handle<JSFunction> math_floor =
|
||||
SimpleInstallFunction(math, "floor", Builtins::kMathFloor, 1, true);
|
||||
native_context()->set_math_floor(*math_floor);
|
||||
|
@ -2182,6 +2182,74 @@ void Builtins::Generate_MathCeil(compiler::CodeStubAssembler* assembler) {
|
||||
&compiler::CodeStubAssembler::Float64Ceil);
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.11 Math.clz32 ( x )
|
||||
void Builtins::Generate_MathClz32(compiler::CodeStubAssembler* assembler) {
|
||||
typedef compiler::CodeStubAssembler::Label Label;
|
||||
typedef compiler::Node Node;
|
||||
typedef compiler::CodeStubAssembler::Variable Variable;
|
||||
|
||||
Node* context = assembler->Parameter(4);
|
||||
|
||||
// Shared entry point for the clz32 operation.
|
||||
Variable var_clz32_x(assembler, MachineRepresentation::kWord32);
|
||||
Label do_clz32(assembler);
|
||||
|
||||
// We might need to loop once for ToNumber conversion.
|
||||
Variable var_x(assembler, MachineRepresentation::kTagged);
|
||||
Label loop(assembler, &var_x);
|
||||
var_x.Bind(assembler->Parameter(1));
|
||||
assembler->Goto(&loop);
|
||||
assembler->Bind(&loop);
|
||||
{
|
||||
// Load the current {x} value.
|
||||
Node* x = var_x.value();
|
||||
|
||||
// Check if {x} is a Smi or a HeapObject.
|
||||
Label if_xissmi(assembler), if_xisnotsmi(assembler);
|
||||
assembler->Branch(assembler->WordIsSmi(x), &if_xissmi, &if_xisnotsmi);
|
||||
|
||||
assembler->Bind(&if_xissmi);
|
||||
{
|
||||
var_clz32_x.Bind(assembler->SmiToWord32(x));
|
||||
assembler->Goto(&do_clz32);
|
||||
}
|
||||
|
||||
assembler->Bind(&if_xisnotsmi);
|
||||
{
|
||||
// Check if {x} is a HeapNumber.
|
||||
Label if_xisheapnumber(assembler),
|
||||
if_xisnotheapnumber(assembler, Label::kDeferred);
|
||||
assembler->Branch(
|
||||
assembler->WordEqual(assembler->LoadMap(x),
|
||||
assembler->HeapNumberMapConstant()),
|
||||
&if_xisheapnumber, &if_xisnotheapnumber);
|
||||
|
||||
assembler->Bind(&if_xisheapnumber);
|
||||
{
|
||||
var_clz32_x.Bind(assembler->TruncateHeapNumberValueToWord32(x));
|
||||
assembler->Goto(&do_clz32);
|
||||
}
|
||||
|
||||
assembler->Bind(&if_xisnotheapnumber);
|
||||
{
|
||||
// Need to convert {x} to a Number first.
|
||||
Callable callable =
|
||||
CodeFactory::NonNumberToNumber(assembler->isolate());
|
||||
var_x.Bind(assembler->CallStub(callable, context, x));
|
||||
assembler->Goto(&loop);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
assembler->Bind(&do_clz32);
|
||||
{
|
||||
Node* x_value = var_clz32_x.value();
|
||||
Node* value = assembler->Word32Clz(x_value);
|
||||
Node* result = assembler->ChangeInt32ToTagged(value);
|
||||
assembler->Return(result);
|
||||
}
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.16 Math.floor ( x )
|
||||
void Builtins::Generate_MathFloor(compiler::CodeStubAssembler* assembler) {
|
||||
Generate_MathRoundingOperation(assembler,
|
||||
|
@ -308,6 +308,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
|
||||
// Define list of builtins implemented in TurboFan (with JS linkage).
|
||||
#define BUILTIN_LIST_T(V) \
|
||||
V(MathCeil, 2) \
|
||||
V(MathClz32, 2) \
|
||||
V(MathFloor, 2) \
|
||||
V(MathRound, 2) \
|
||||
V(MathSqrt, 2) \
|
||||
@ -585,8 +586,10 @@ class Builtins {
|
||||
static void Generate_InternalArrayCode(MacroAssembler* masm);
|
||||
static void Generate_ArrayCode(MacroAssembler* masm);
|
||||
|
||||
// ES6 section 20.2.2.10 Math.floor ( x )
|
||||
// ES6 section 20.2.2.10 Math.ceil ( x )
|
||||
static void Generate_MathCeil(compiler::CodeStubAssembler* assembler);
|
||||
// ES6 section 20.2.2.11 Math.clz32 ( x )
|
||||
static void Generate_MathClz32(compiler::CodeStubAssembler* assembler);
|
||||
// ES6 section 20.2.2.16 Math.floor ( x )
|
||||
static void Generate_MathFloor(compiler::CodeStubAssembler* assembler);
|
||||
enum class MathMaxMinKind { kMax, kMin };
|
||||
|
@ -103,7 +103,8 @@ class Schedule;
|
||||
V(ChangeInt32ToFloat64) \
|
||||
V(ChangeInt32ToInt64) \
|
||||
V(ChangeUint32ToFloat64) \
|
||||
V(ChangeUint32ToUint64)
|
||||
V(ChangeUint32ToUint64) \
|
||||
V(Word32Clz)
|
||||
|
||||
class CodeStubAssembler {
|
||||
public:
|
||||
|
@ -129,7 +129,7 @@ Reduction JSBuiltinReducer::ReduceMathImul(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// ES6 draft 08-24-14, section 20.2.2.16.
|
||||
// ES6 section 20.2.2.10 Math.ceil ( x )
|
||||
Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
|
||||
JSCallReduction r(node);
|
||||
if (r.InputsMatchOne(Type::Number())) {
|
||||
@ -140,6 +140,24 @@ Reduction JSBuiltinReducer::ReduceMathCeil(Node* node) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// ES6 section 20.2.2.11 Math.clz32 ( x )
|
||||
Reduction JSBuiltinReducer::ReduceMathClz32(Node* node) {
|
||||
JSCallReduction r(node);
|
||||
if (r.InputsMatchOne(Type::Unsigned32())) {
|
||||
// Math.clz32(a:unsigned32) -> NumberClz32(a)
|
||||
Node* value = graph()->NewNode(simplified()->NumberClz32(), r.left());
|
||||
return Replace(value);
|
||||
}
|
||||
if (r.InputsMatchOne(Type::Number())) {
|
||||
// Math.clz32(a:number) -> NumberClz32(NumberToUint32(a))
|
||||
Node* value = graph()->NewNode(
|
||||
simplified()->NumberClz32(),
|
||||
graph()->NewNode(simplified()->NumberToUint32(), r.left()));
|
||||
return Replace(value);
|
||||
}
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
// ES6 draft 08-24-14, section 20.2.2.16.
|
||||
Reduction JSBuiltinReducer::ReduceMathFloor(Node* node) {
|
||||
JSCallReduction r(node);
|
||||
@ -209,6 +227,9 @@ Reduction JSBuiltinReducer::Reduce(Node* node) {
|
||||
case kMathImul:
|
||||
reduction = ReduceMathImul(node);
|
||||
break;
|
||||
case kMathClz32:
|
||||
reduction = ReduceMathClz32(node);
|
||||
break;
|
||||
case kMathCeil:
|
||||
reduction = ReduceMathCeil(node);
|
||||
break;
|
||||
|
@ -34,6 +34,7 @@ class JSBuiltinReducer final : public AdvancedReducer {
|
||||
Reduction ReduceMathMax(Node* node);
|
||||
Reduction ReduceMathImul(Node* node);
|
||||
Reduction ReduceMathCeil(Node* node);
|
||||
Reduction ReduceMathClz32(Node* node);
|
||||
Reduction ReduceMathFloor(Node* node);
|
||||
Reduction ReduceMathFround(Node* node);
|
||||
Reduction ReduceMathRound(Node* node);
|
||||
|
@ -50,8 +50,6 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
|
||||
return ReduceIsJSReceiver(node);
|
||||
case Runtime::kInlineIsSmi:
|
||||
return ReduceIsSmi(node);
|
||||
case Runtime::kInlineMathClz32:
|
||||
return ReduceMathClz32(node);
|
||||
case Runtime::kInlineValueOf:
|
||||
return ReduceValueOf(node);
|
||||
case Runtime::kInlineFixedArrayGet:
|
||||
@ -206,11 +204,6 @@ Reduction JSIntrinsicLowering::ReduceIsSmi(Node* node) {
|
||||
}
|
||||
|
||||
|
||||
Reduction JSIntrinsicLowering::ReduceMathClz32(Node* node) {
|
||||
return Change(node, machine()->Word32Clz());
|
||||
}
|
||||
|
||||
|
||||
Reduction JSIntrinsicLowering::ReduceValueOf(Node* node) {
|
||||
// if (%_IsSmi(value)) {
|
||||
// return value;
|
||||
|
@ -45,7 +45,6 @@ class JSIntrinsicLowering final : public AdvancedReducer {
|
||||
Reduction ReduceIsInstanceType(Node* node, InstanceType instance_type);
|
||||
Reduction ReduceIsJSReceiver(Node* node);
|
||||
Reduction ReduceIsSmi(Node* node);
|
||||
Reduction ReduceMathClz32(Node* node);
|
||||
Reduction ReduceValueOf(Node* node);
|
||||
Reduction ReduceFixedArrayGet(Node* node);
|
||||
Reduction ReduceFixedArraySet(Node* node);
|
||||
|
@ -350,6 +350,7 @@ class MachineOperatorBuilder final : public ZoneObject {
|
||||
V(Word, Shr) \
|
||||
V(Word, Sar) \
|
||||
V(Word, Ror) \
|
||||
V(Word, Clz) \
|
||||
V(Word, Equal) \
|
||||
V(Int, Add) \
|
||||
V(Int, Sub) \
|
||||
|
@ -185,6 +185,7 @@
|
||||
V(NumberShiftLeft) \
|
||||
V(NumberShiftRight) \
|
||||
V(NumberShiftRightLogical) \
|
||||
V(NumberClz32) \
|
||||
V(NumberCeil) \
|
||||
V(NumberFloor) \
|
||||
V(NumberRound) \
|
||||
|
@ -465,6 +465,8 @@ const Operator* RepresentationChanger::Uint32OperatorFor(
|
||||
return machine()->Uint32LessThan();
|
||||
case IrOpcode::kNumberLessThanOrEqual:
|
||||
return machine()->Uint32LessThanOrEqual();
|
||||
case IrOpcode::kNumberClz32:
|
||||
return machine()->Word32Clz();
|
||||
default:
|
||||
UNREACHABLE();
|
||||
return nullptr;
|
||||
|
@ -962,6 +962,12 @@ class RepresentationSelector {
|
||||
}
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kNumberClz32: {
|
||||
VisitUnop(node, UseInfo::TruncatingWord32(),
|
||||
MachineRepresentation::kWord32);
|
||||
if (lower()) NodeProperties::ChangeOp(node, Uint32Op(node));
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kNumberCeil: {
|
||||
VisitUnop(node, UseInfo::Float64(), MachineRepresentation::kFloat64);
|
||||
if (lower()) DeferReplacement(node, lowering->Float64Ceil(node));
|
||||
|
@ -173,6 +173,7 @@ const ElementAccess& ElementAccessOf(const Operator* op) {
|
||||
V(NumberShiftLeft, Operator::kNoProperties, 2) \
|
||||
V(NumberShiftRight, Operator::kNoProperties, 2) \
|
||||
V(NumberShiftRightLogical, Operator::kNoProperties, 2) \
|
||||
V(NumberClz32, Operator::kNoProperties, 1) \
|
||||
V(NumberCeil, Operator::kNoProperties, 1) \
|
||||
V(NumberFloor, Operator::kNoProperties, 1) \
|
||||
V(NumberRound, Operator::kNoProperties, 1) \
|
||||
|
@ -143,6 +143,7 @@ class SimplifiedOperatorBuilder final : public ZoneObject {
|
||||
const Operator* NumberShiftLeft();
|
||||
const Operator* NumberShiftRight();
|
||||
const Operator* NumberShiftRightLogical();
|
||||
const Operator* NumberClz32();
|
||||
const Operator* NumberCeil();
|
||||
const Operator* NumberFloor();
|
||||
const Operator* NumberRound();
|
||||
|
@ -1631,8 +1631,6 @@ Type* Typer::Visitor::TypeJSCallRuntime(Node* node) {
|
||||
case Runtime::kInlineConstructDouble:
|
||||
case Runtime::kInlineMathAtan2:
|
||||
return Type::Number();
|
||||
case Runtime::kInlineMathClz32:
|
||||
return Type::Range(0, 32, zone());
|
||||
case Runtime::kInlineCreateIterResultObject:
|
||||
case Runtime::kInlineRegExpConstructResult:
|
||||
return Type::OtherObject();
|
||||
@ -1759,6 +1757,10 @@ Type* Typer::Visitor::TypeNumberShiftRightLogical(Node* node) {
|
||||
return Type::Unsigned32();
|
||||
}
|
||||
|
||||
Type* Typer::Visitor::TypeNumberClz32(Node* node) {
|
||||
return typer_->cache_.kZeroToThirtyTwo;
|
||||
}
|
||||
|
||||
Type* Typer::Visitor::TypeNumberCeil(Node* node) {
|
||||
return TypeUnaryOp(node, NumberCeil);
|
||||
}
|
||||
|
@ -685,6 +685,11 @@ void Verifier::Visitor::Check(Node* node) {
|
||||
CheckValueInputIs(node, 1, Type::Unsigned32());
|
||||
CheckUpperIs(node, Type::Unsigned32());
|
||||
break;
|
||||
case IrOpcode::kNumberClz32:
|
||||
// Unsigned32 -> Unsigned32
|
||||
CheckValueInputIs(node, 0, Type::Unsigned32());
|
||||
CheckUpperIs(node, Type::Unsigned32());
|
||||
break;
|
||||
case IrOpcode::kNumberCeil:
|
||||
case IrOpcode::kNumberFloor:
|
||||
case IrOpcode::kNumberRound:
|
||||
|
@ -12703,15 +12703,6 @@ void HOptimizedGraphBuilder::GenerateMathPow(CallRuntime* call) {
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::GenerateMathClz32(CallRuntime* call) {
|
||||
DCHECK(call->arguments()->length() == 1);
|
||||
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
||||
HValue* value = Pop();
|
||||
HInstruction* result = NewUncasted<HUnaryMathOperation>(value, kMathClz32);
|
||||
return ast_context()->ReturnInstruction(result, call->id());
|
||||
}
|
||||
|
||||
|
||||
void HOptimizedGraphBuilder::GenerateMathLogRT(CallRuntime* call) {
|
||||
DCHECK(call->arguments()->length() == 1);
|
||||
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
|
||||
|
@ -2269,7 +2269,6 @@ class HOptimizedGraphBuilder : public HGraphBuilder, public AstVisitor {
|
||||
F(ConstructDouble) \
|
||||
F(DoubleHi) \
|
||||
F(DoubleLo) \
|
||||
F(MathClz32) \
|
||||
F(MathLogRT) \
|
||||
/* ES6 Collections */ \
|
||||
F(MapClear) \
|
||||
|
@ -143,11 +143,6 @@ function MathHypot(x, y) { // Function length is 2.
|
||||
return %math_sqrt(sum) * max;
|
||||
}
|
||||
|
||||
// ES6 draft 07-18-14, section 20.2.2.11
|
||||
function MathClz32JS(x) {
|
||||
return %_MathClz32(x >>> 0);
|
||||
}
|
||||
|
||||
// ES6 draft 09-27-13, section 20.2.2.9.
|
||||
// Cube root approximation, refer to: http://metamerist.com/cbrt/cbrt.htm
|
||||
// Using initial approximation adapted from Kahan's cbrt and 4 iterations
|
||||
@ -209,13 +204,11 @@ utils.InstallFunctions(GlobalMath, DONT_ENUM, [
|
||||
"acosh", MathAcosh,
|
||||
"atanh", MathAtanh,
|
||||
"hypot", MathHypot,
|
||||
"clz32", MathClz32JS,
|
||||
"cbrt", MathCbrt
|
||||
]);
|
||||
|
||||
%SetForceInlineFlag(MathAbs);
|
||||
%SetForceInlineFlag(MathAtan2JS);
|
||||
%SetForceInlineFlag(MathClz32JS);
|
||||
%SetForceInlineFlag(MathRandom);
|
||||
%SetForceInlineFlag(MathSign);
|
||||
|
||||
|
@ -108,17 +108,6 @@ RUNTIME_FUNCTION(Runtime_MathExpRT) {
|
||||
}
|
||||
|
||||
|
||||
RUNTIME_FUNCTION(Runtime_MathClz32) {
|
||||
HandleScope scope(isolate);
|
||||
DCHECK(args.length() == 1);
|
||||
isolate->counters()->math_clz32_runtime()->Increment();
|
||||
|
||||
CONVERT_NUMBER_CHECKED(uint32_t, x, Uint32, args[0]);
|
||||
return *isolate->factory()->NewNumberFromUint(
|
||||
base::bits::CountLeadingZeros32(x));
|
||||
}
|
||||
|
||||
|
||||
// Slow version of Math.pow. We check for fast paths for special cases.
|
||||
// Used if VFP3 is not available.
|
||||
RUNTIME_FUNCTION(Runtime_MathPow) {
|
||||
|
@ -356,7 +356,6 @@ namespace internal {
|
||||
F(RemPiO2, 2, 1) \
|
||||
F(MathAtan2, 2, 1) \
|
||||
F(MathExpRT, 1, 1) \
|
||||
F(MathClz32, 1, 1) \
|
||||
F(MathPow, 2, 1) \
|
||||
F(MathPowRT, 2, 1) \
|
||||
F(GenerateRandomNumbers, 1, 1)
|
||||
|
@ -75,8 +75,8 @@ TEST(SimpleCallRuntime1Arg) {
|
||||
VoidDescriptor descriptor(isolate);
|
||||
CodeStubAssemblerTester m(isolate, descriptor);
|
||||
Node* context = m.HeapConstant(Handle<Context>(isolate->native_context()));
|
||||
Node* b = m.SmiTag(m.Int32Constant(-1));
|
||||
m.Return(m.CallRuntime(Runtime::kMathClz32, context, b));
|
||||
Node* b = m.SmiTag(m.Int32Constant(0));
|
||||
m.Return(m.CallRuntime(Runtime::kNumberToSmi, context, b));
|
||||
Handle<Code> code = m.GenerateCode();
|
||||
FunctionTester ft(descriptor, code);
|
||||
MaybeHandle<Object> result = ft.Call();
|
||||
@ -89,8 +89,8 @@ TEST(SimpleTailCallRuntime1Arg) {
|
||||
VoidDescriptor descriptor(isolate);
|
||||
CodeStubAssemblerTester m(isolate, descriptor);
|
||||
Node* context = m.HeapConstant(Handle<Context>(isolate->native_context()));
|
||||
Node* b = m.SmiTag(m.Int32Constant(-1));
|
||||
m.TailCallRuntime(Runtime::kMathClz32, context, b);
|
||||
Node* b = m.SmiTag(m.Int32Constant(0));
|
||||
m.TailCallRuntime(Runtime::kNumberToSmi, context, b);
|
||||
Handle<Code> code = m.GenerateCode();
|
||||
FunctionTester ft(descriptor, code);
|
||||
MaybeHandle<Object> result = ft.Call();
|
||||
|
@ -2,8 +2,6 @@
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax
|
||||
|
||||
var stdlib = { Math: Math };
|
||||
|
||||
var f = (function Module(stdlib) {
|
||||
@ -26,6 +24,5 @@ for (var i = 0; i < 32; ++i) {
|
||||
assertEquals(i, f((-1) >>> i));
|
||||
}
|
||||
for (var i = -2147483648; i < 2147483648; i += 3999773) {
|
||||
assertEquals(%MathClz32(i), f(i));
|
||||
assertEquals(%MathClz32(i), %_MathClz32(i >>> 0));
|
||||
assertEquals(Math.clz32(i), f(i));
|
||||
}
|
||||
|
@ -241,22 +241,6 @@ TEST_F(JSIntrinsicLoweringTest, InlineIsJSReceiver) {
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// %_MathClz32
|
||||
|
||||
TEST_F(JSIntrinsicLoweringTest, InlineMathClz32) {
|
||||
Node* const input = Parameter(0);
|
||||
Node* const context = Parameter(1);
|
||||
Node* const effect = graph()->start();
|
||||
Node* const control = graph()->start();
|
||||
Reduction const r = Reduce(
|
||||
graph()->NewNode(javascript()->CallRuntime(Runtime::kInlineMathClz32, 1),
|
||||
input, context, effect, control));
|
||||
ASSERT_TRUE(r.Changed());
|
||||
EXPECT_THAT(r.replacement(), IsWord32Clz(input));
|
||||
}
|
||||
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// %_ValueOf
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user