Commit 7554360f authored by bmeurer's avatar bmeurer Committed by Commit bot

[builtins] Migrate String.fromCharCode to TurboFan code stub.

When we moved the String.fromCharCode builtin to C++, we slightly
regressed the fast single character code argument case. Recovered some
of the performance by implementing the builtin using the TurboFan
CodeStubAssembler.

Drive-by-fix: Make sure the stack trace from the implicit ToNumber
conversion in String.fromCharCode includes the builtin by adding a
regression test for that.

R=yangguo@chromium.org
BUG=chromium:609831,chromium:613947,v8:5049

Review-Url: https://codereview.chromium.org/2021143003
Cr-Commit-Position: refs/heads/master@{#36611}
parent 60afed46
......@@ -1323,7 +1323,7 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
// Install the String.fromCharCode function.
SimpleInstallFunction(string_fun, "fromCharCode",
Builtins::kStringFromCharCode, 1, false);
Builtins::kStringFromCharCode, 1, true);
// Create the %StringPrototype%
Handle<JSValue> prototype =
......
This diff is collapsed.
......@@ -173,7 +173,6 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(ReflectSet, kNone) \
V(ReflectSetPrototypeOf, kNone) \
\
V(StringFromCharCode, kNone) \
V(StringPrototypeTrim, kNone) \
V(StringPrototypeTrimLeft, kNone) \
V(StringPrototypeTrimRight, kNone) \
......@@ -332,6 +331,7 @@ inline bool operator&(BuiltinExtraArguments lhs, BuiltinExtraArguments rhs) {
V(MathTrunc, 2) \
V(ObjectHasOwnProperty, 2) \
V(ArrayIsArray, 2) \
V(StringFromCharCode, 2) \
V(StringPrototypeCharAt, 2) \
V(StringPrototypeCharCodeAt, 2) \
V(AtomicsLoad, 3) \
......@@ -656,6 +656,8 @@ class Builtins {
// ES6 section 22.1.2.2 Array.isArray
static void Generate_ArrayIsArray(CodeStubAssembler* assembler);
// ES6 section 21.1.2.1 String.fromCharCode ( ...codeUnits )
static void Generate_StringFromCharCode(CodeStubAssembler* assembler);
// ES6 section 21.1.3.1 String.prototype.charAt ( pos )
static void Generate_StringPrototypeCharAt(CodeStubAssembler* assembler);
// ES6 section 21.1.3.2 String.prototype.charCodeAt ( pos )
......
......@@ -637,6 +637,47 @@ Node* CodeStubAssembler::AllocateSeqOneByteString(int length) {
return result;
}
Node* CodeStubAssembler::AllocateSeqOneByteString(Node* context, Node* length) {
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqOneByteString size and check if it fits into new space.
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
if_join(this);
Node* size = WordAnd(
IntPtrAdd(
IntPtrAdd(length, IntPtrConstant(SeqOneByteString::kHeaderSize)),
IntPtrConstant(kObjectAlignmentMask)),
IntPtrConstant(~kObjectAlignmentMask));
Branch(IntPtrLessThanOrEqual(size,
IntPtrConstant(Page::kMaxRegularHeapObjectSize)),
&if_sizeissmall, &if_notsizeissmall);
Bind(&if_sizeissmall);
{
// Just allocate the SeqOneByteString in new space.
Node* result = Allocate(size);
StoreMapNoWriteBarrier(result, LoadRoot(Heap::kOneByteStringMapRootIndex));
StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kLengthOffset,
SmiFromWord(length));
StoreObjectFieldNoWriteBarrier(result, SeqOneByteString::kHashFieldSlot,
IntPtrConstant(String::kEmptyHashField));
var_result.Bind(result);
Goto(&if_join);
}
Bind(&if_notsizeissmall);
{
// We might need to allocate in large object space, go to the runtime.
Node* result = CallRuntime(Runtime::kAllocateSeqOneByteString, context,
SmiFromWord(length));
var_result.Bind(result);
Goto(&if_join);
}
Bind(&if_join);
return var_result.value();
}
Node* CodeStubAssembler::AllocateSeqTwoByteString(int length) {
Node* result = Allocate(SeqTwoByteString::SizeFor(length));
StoreMapNoWriteBarrier(result, LoadRoot(Heap::kStringMapRootIndex));
......@@ -647,6 +688,47 @@ Node* CodeStubAssembler::AllocateSeqTwoByteString(int length) {
return result;
}
Node* CodeStubAssembler::AllocateSeqTwoByteString(Node* context, Node* length) {
Variable var_result(this, MachineRepresentation::kTagged);
// Compute the SeqTwoByteString size and check if it fits into new space.
Label if_sizeissmall(this), if_notsizeissmall(this, Label::kDeferred),
if_join(this);
Node* size = WordAnd(
IntPtrAdd(IntPtrAdd(WordShl(length, 1),
IntPtrConstant(SeqTwoByteString::kHeaderSize)),
IntPtrConstant(kObjectAlignmentMask)),
IntPtrConstant(~kObjectAlignmentMask));
Branch(IntPtrLessThanOrEqual(size,
IntPtrConstant(Page::kMaxRegularHeapObjectSize)),
&if_sizeissmall, &if_notsizeissmall);
Bind(&if_sizeissmall);
{
// Just allocate the SeqTwoByteString in new space.
Node* result = Allocate(size);
StoreMapNoWriteBarrier(result, LoadRoot(Heap::kStringMapRootIndex));
StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kLengthOffset,
SmiFromWord(length));
StoreObjectFieldNoWriteBarrier(result, SeqTwoByteString::kHashFieldSlot,
IntPtrConstant(String::kEmptyHashField));
var_result.Bind(result);
Goto(&if_join);
}
Bind(&if_notsizeissmall);
{
// We might need to allocate in large object space, go to the runtime.
Node* result = CallRuntime(Runtime::kAllocateSeqTwoByteString, context,
SmiFromWord(length));
var_result.Bind(result);
Goto(&if_join);
}
Bind(&if_join);
return var_result.value();
}
Node* CodeStubAssembler::AllocateJSArray(ElementsKind kind, Node* array_map,
Node* capacity_node, Node* length_node,
compiler::Node* allocation_site,
......
......@@ -55,6 +55,7 @@ class CodeStubAssembler : public compiler::CodeAssembler {
// Smi conversions.
compiler::Node* SmiToFloat64(compiler::Node* value);
compiler::Node* SmiFromWord(compiler::Node* value) { return SmiTag(value); }
compiler::Node* SmiFromWord32(compiler::Node* value);
compiler::Node* SmiToWord(compiler::Node* value) { return SmiUntag(value); }
compiler::Node* SmiToWord32(compiler::Node* value);
......@@ -175,8 +176,12 @@ class CodeStubAssembler : public compiler::CodeAssembler {
compiler::Node* AllocateHeapNumberWithValue(compiler::Node* value);
// Allocate a SeqOneByteString with the given length.
compiler::Node* AllocateSeqOneByteString(int length);
compiler::Node* AllocateSeqOneByteString(compiler::Node* context,
compiler::Node* length);
// Allocate a SeqTwoByteString with the given length.
compiler::Node* AllocateSeqTwoByteString(int length);
compiler::Node* AllocateSeqTwoByteString(compiler::Node* context,
compiler::Node* length);
// Allocated an JSArray
compiler::Node* AllocateJSArray(ElementsKind kind, compiler::Node* array_map,
compiler::Node* capacity,
......
......@@ -325,6 +325,25 @@ RUNTIME_FUNCTION(Runtime_AllocateInTargetSpace) {
return *isolate->factory()->NewFillerObject(size, double_align, space);
}
RUNTIME_FUNCTION(Runtime_AllocateSeqOneByteString) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(length, 0);
Handle<SeqOneByteString> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawOneByteString(length));
return *result;
}
RUNTIME_FUNCTION(Runtime_AllocateSeqTwoByteString) {
HandleScope scope(isolate);
DCHECK_EQ(1, args.length());
CONVERT_SMI_ARG_CHECKED(length, 0);
Handle<SeqTwoByteString> result;
ASSIGN_RETURN_FAILURE_ON_EXCEPTION(
isolate, result, isolate->factory()->NewRawTwoByteString(length));
return *result;
}
// Collect the raw data for a stack trace. Returns an array of 4
// element segments each containing a receiver, function, code and
......
......@@ -300,6 +300,8 @@ namespace internal {
F(Interrupt, 0, 1) \
F(AllocateInNewSpace, 1, 1) \
F(AllocateInTargetSpace, 2, 1) \
F(AllocateSeqOneByteString, 1, 1) \
F(AllocateSeqTwoByteString, 1, 1) \
F(CollectStackTrace, 2, 1) \
F(MessageGetStartPosition, 1, 1) \
F(MessageGetScript, 1, 1) \
......
// Copyright 2016 the V8 project authors. All rights reserved.
// 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 thrower = { [Symbol.toPrimitive]: function() { FAIL } };
function testTrace(func) {
try {
func(thrower);
assertUnreachable();
} catch (e) {
assertTrue(e.stack.indexOf("fromCharCode") >= 0);
}
}
testTrace(String.fromCharCode);
function foo(x) { return String.fromCharCode(x); }
foo(1);
foo(2);
testTrace(foo);
%OptimizeFunctionOnNextCall(foo);
testTrace(foo);
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