Commit eaa92feb authored by bmeurer's avatar bmeurer Committed by Commit bot

[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}
parent 652853da
......@@ -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
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment