Commit fdfb8c9e authored by Ross McIlroy's avatar Ross McIlroy Committed by Commit Bot

[TurboFan] Add support for generic lowering of StringConcat bytecode.

Adds support for lowering of ToPrimitiveToString and StringConcat bytecodes
to the corresponding builtins. As part of this, moves the interpreter
implementation of these operations into the appropriate builtin generators
and add builtin support for them.

Also adds TailCallRuntimeN operator to code-assembler which enables tail calling
a runtime function when the arguments have already been pushed onto the stack.

BUG=v8:6243

Change-Id: Id5c851bc42e4ff490d9a23a8990ae331c7eac73e
Reviewed-on: https://chromium-review.googlesource.com/515362
Commit-Queue: Ross McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#45756}
parent 074b0464
...@@ -939,6 +939,7 @@ v8_source_set("v8_builtins_generators") { ...@@ -939,6 +939,7 @@ v8_source_set("v8_builtins_generators") {
"src/builtins/builtins-constructor-gen.h", "src/builtins/builtins-constructor-gen.h",
"src/builtins/builtins-constructor.h", "src/builtins/builtins-constructor.h",
"src/builtins/builtins-conversion-gen.cc", "src/builtins/builtins-conversion-gen.cc",
"src/builtins/builtins-conversion-gen.h",
"src/builtins/builtins-date-gen.cc", "src/builtins/builtins-date-gen.cc",
"src/builtins/builtins-debug-gen.cc", "src/builtins/builtins-debug-gen.cc",
"src/builtins/builtins-forin-gen.cc", "src/builtins/builtins-forin-gen.cc",
......
...@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r5; } ...@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r5; }
const Register StringCompareDescriptor::LeftRegister() { return r1; } const Register StringCompareDescriptor::LeftRegister() { return r1; }
const Register StringCompareDescriptor::RightRegister() { return r0; } const Register StringCompareDescriptor::RightRegister() { return r0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r0; }
const Register ApiGetterDescriptor::HolderRegister() { return r0; } const Register ApiGetterDescriptor::HolderRegister() { return r0; }
const Register ApiGetterDescriptor::CallbackRegister() { return r3; } const Register ApiGetterDescriptor::CallbackRegister() { return r3; }
......
...@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return x5; } ...@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return x5; }
const Register StringCompareDescriptor::LeftRegister() { return x1; } const Register StringCompareDescriptor::LeftRegister() { return x1; }
const Register StringCompareDescriptor::RightRegister() { return x0; } const Register StringCompareDescriptor::RightRegister() { return x0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return x0; }
const Register ApiGetterDescriptor::HolderRegister() { return x0; } const Register ApiGetterDescriptor::HolderRegister() { return x0; }
const Register ApiGetterDescriptor::CallbackRegister() { return x3; } const Register ApiGetterDescriptor::CallbackRegister() { return x3; }
......
...@@ -2,28 +2,16 @@ ...@@ -2,28 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "src/builtins/builtins-conversion-gen.h"
#include "src/builtins/builtins-utils-gen.h" #include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h" #include "src/builtins/builtins.h"
#include "src/code-factory.h" #include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/objects-inl.h" #include "src/objects-inl.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
class ConversionBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
protected:
void Generate_NonPrimitiveToPrimitive(Node* context, Node* input,
ToPrimitiveHint hint);
void Generate_OrdinaryToPrimitive(Node* context, Node* input,
OrdinaryToPrimitiveHint hint);
};
// ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] ) // ES6 section 7.1.1 ToPrimitive ( input [ , PreferredType ] )
void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive( void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
Node* context, Node* input, ToPrimitiveHint hint) { Node* context, Node* input, ToPrimitiveHint hint) {
...@@ -136,6 +124,53 @@ TF_BUILTIN(ToString, CodeStubAssembler) { ...@@ -136,6 +124,53 @@ TF_BUILTIN(ToString, CodeStubAssembler) {
Return(ToString(context, input)); Return(ToString(context, input));
} }
// ES6 section 7.1.1 ToPrimitive( argument, "default" ) followed by
// ES6 section 7.1.12 ToString ( argument )
compiler::Node* ConversionBuiltinsAssembler::ToPrimitiveToString(
Node* context, Node* input, Variable* feedback) {
Label is_string(this), to_primitive(this, Label::kDeferred),
to_string(this, Label::kDeferred), done(this);
VARIABLE(result, MachineRepresentation::kTagged, input);
GotoIf(TaggedIsSmi(input), &to_string);
GotoIf(IsString(input), &is_string);
BranchIfJSReceiver(input, &to_primitive, &to_string);
BIND(&to_primitive);
{
Callable callable = CodeFactory::NonPrimitiveToPrimitive(isolate());
result.Bind(CallStub(callable, context, input));
Goto(&to_string);
}
BIND(&to_string);
{
if (feedback) {
feedback->Bind(SmiConstant(ToPrimitiveToStringFeedback::kAny));
}
result.Bind(CallBuiltin(Builtins::kToString, context, result.value()));
Goto(&done);
}
BIND(&is_string);
{
if (feedback) {
feedback->Bind(SmiConstant(ToPrimitiveToStringFeedback::kString));
}
Goto(&done);
}
BIND(&done);
return result.value();
}
TF_BUILTIN(ToPrimitiveToString, ConversionBuiltinsAssembler) {
Node* context = Parameter(Descriptor::kContext);
Node* input = Parameter(Descriptor::kArgument);
Return(ToPrimitiveToString(context, input));
}
// 7.1.1.1 OrdinaryToPrimitive ( O, hint ) // 7.1.1.1 OrdinaryToPrimitive ( O, hint )
void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive( void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
Node* context, Node* input, OrdinaryToPrimitiveHint hint) { Node* context, Node* input, OrdinaryToPrimitiveHint hint) {
......
// Copyright 2017 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.
#ifndef V8_BUILTINS_BUILTINS_CONVERSION_GEN_H_
#define V8_BUILTINS_BUILTINS_CONVERSION_GEN_H_
#include "src/code-stub-assembler.h"
namespace v8 {
namespace internal {
class ConversionBuiltinsAssembler : public CodeStubAssembler {
public:
explicit ConversionBuiltinsAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
Node* ToPrimitiveToString(Node* context, Node* input,
Variable* feedback = nullptr);
protected:
void Generate_NonPrimitiveToPrimitive(Node* context, Node* input,
ToPrimitiveHint hint);
void Generate_OrdinaryToPrimitive(Node* context, Node* input,
OrdinaryToPrimitiveHint hint);
};
} // namespace internal
} // namespace v8
#endif // V8_BUILTINS_BUILTINS_CONVERSION_GEN_H_
...@@ -126,6 +126,7 @@ namespace internal { ...@@ -126,6 +126,7 @@ namespace internal {
TFS(StringIndexOf, kReceiver, kSearchString, kPosition) \ TFS(StringIndexOf, kReceiver, kSearchString, kPosition) \
TFC(StringLessThan, Compare, 1) \ TFC(StringLessThan, Compare, 1) \
TFC(StringLessThanOrEqual, Compare, 1) \ TFC(StringLessThanOrEqual, Compare, 1) \
TFC(StringConcat, StringConcat, 1) \
\ \
/* Interpreter */ \ /* Interpreter */ \
ASM(InterpreterEntryTrampoline) \ ASM(InterpreterEntryTrampoline) \
...@@ -192,6 +193,7 @@ namespace internal { ...@@ -192,6 +193,7 @@ namespace internal {
TFC(NonNumberToNumber, TypeConversion, 1) \ TFC(NonNumberToNumber, TypeConversion, 1) \
TFC(ToNumber, TypeConversion, 1) \ TFC(ToNumber, TypeConversion, 1) \
TFC(ToString, TypeConversion, 1) \ TFC(ToString, TypeConversion, 1) \
TFC(ToPrimitiveToString, TypeConversion, 1) \
TFC(ToInteger, TypeConversion, 1) \ TFC(ToInteger, TypeConversion, 1) \
TFC(ToLength, TypeConversion, 1) \ TFC(ToLength, TypeConversion, 1) \
TFC(ClassOf, Typeof, 1) \ TFC(ClassOf, Typeof, 1) \
......
This diff is collapsed.
...@@ -24,6 +24,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler { ...@@ -24,6 +24,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
Label* if_equal, Label* if_not_equal, Label* if_equal, Label* if_not_equal,
Label* if_notbothdirectonebyte); Label* if_notbothdirectonebyte);
// String concatenation.
Node* ConcatenateStrings(Node* context, Node* first_arg_ptr, Node* arg_count,
Label* bailout_to_runtime);
protected: protected:
Node* DirectStringData(Node* string, Node* string_instance_type); Node* DirectStringData(Node* string, Node* string_instance_type);
...@@ -54,6 +58,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler { ...@@ -54,6 +58,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index, Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
UnicodeEncoding encoding); UnicodeEncoding encoding);
Node* ConcatenateSequentialStrings(Node* context, Node* first_arg_ptr,
Node* arg_count, Node* total_length,
String::Encoding encoding);
void StringIndexOf(Node* const subject_string, void StringIndexOf(Node* const subject_string,
Node* const subject_instance_type, Node* const subject_instance_type,
Node* const search_string, Node* const search_string,
......
...@@ -9175,21 +9175,23 @@ Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) { ...@@ -9175,21 +9175,23 @@ Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
return IsSetWord32<JSArrayBuffer::WasNeutered>(buffer_bit_field); return IsSetWord32<JSArrayBuffer::WasNeutered>(buffer_bit_field);
} }
CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler, Node* argc, CodeStubArguments::CodeStubArguments(
Node* fp, CodeStubAssembler* assembler, Node* argc, Node* fp,
CodeStubAssembler::ParameterMode mode) CodeStubAssembler::ParameterMode param_mode, ReceiverMode receiver_mode)
: assembler_(assembler), : assembler_(assembler),
argc_mode_(mode), argc_mode_(param_mode),
receiver_mode_(receiver_mode),
argc_(argc), argc_(argc),
arguments_(nullptr), arguments_(nullptr),
fp_(fp != nullptr ? fp : assembler_->LoadFramePointer()) { fp_(fp != nullptr ? fp : assembler_->LoadFramePointer()) {
Node* offset = assembler_->ElementOffsetFromIndex( Node* offset = assembler_->ElementOffsetFromIndex(
argc_, FAST_ELEMENTS, mode, argc_, FAST_ELEMENTS, param_mode,
(StandardFrameConstants::kFixedSlotCountAboveFp - 1) * kPointerSize); (StandardFrameConstants::kFixedSlotCountAboveFp - 1) * kPointerSize);
arguments_ = assembler_->IntPtrAdd(fp_, offset); arguments_ = assembler_->IntPtrAdd(fp_, offset);
} }
Node* CodeStubArguments::GetReceiver() const { Node* CodeStubArguments::GetReceiver() const {
DCHECK_EQ(receiver_mode_, ReceiverMode::kHasReceiver);
return assembler_->Load(MachineType::AnyTagged(), arguments_, return assembler_->Load(MachineType::AnyTagged(), arguments_,
assembler_->IntPtrConstant(kPointerSize)); assembler_->IntPtrConstant(kPointerSize));
} }
...@@ -9267,8 +9269,14 @@ void CodeStubArguments::ForEach( ...@@ -9267,8 +9269,14 @@ void CodeStubArguments::ForEach(
} }
void CodeStubArguments::PopAndReturn(Node* value) { void CodeStubArguments::PopAndReturn(Node* value) {
assembler_->PopAndReturn( Node* pop_count;
assembler_->IntPtrAdd(argc_, assembler_->IntPtrConstant(1)), value); if (receiver_mode_ == ReceiverMode::kHasReceiver) {
pop_count = assembler_->IntPtrOrSmiAdd(
argc_, assembler_->IntPtrOrSmiConstant(1, argc_mode_), argc_mode_);
} else {
pop_count = argc_;
}
assembler_->PopAndReturn(pop_count, value);
} }
Node* CodeStubAssembler::IsFastElementsKind(Node* elements_kind) { Node* CodeStubAssembler::IsFastElementsKind(Node* elements_kind) {
......
...@@ -1527,15 +1527,21 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler { ...@@ -1527,15 +1527,21 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
class CodeStubArguments { class CodeStubArguments {
public: public:
typedef compiler::Node Node; typedef compiler::Node Node;
enum ReceiverMode { kHasReceiver, kNoReceiver };
// |argc| is an intptr value which specifies the number of arguments passed // |argc| is an intptr value which specifies the number of arguments passed
// to the builtin excluding the receiver. // to the builtin excluding the receiver. The arguments will include a
CodeStubArguments(CodeStubAssembler* assembler, Node* argc) // receiver iff |receiver_mode| is kHasReceiver.
CodeStubArguments(CodeStubAssembler* assembler, Node* argc,
ReceiverMode receiver_mode = ReceiverMode::kHasReceiver)
: CodeStubArguments(assembler, argc, nullptr, : CodeStubArguments(assembler, argc, nullptr,
CodeStubAssembler::INTPTR_PARAMETERS) {} CodeStubAssembler::INTPTR_PARAMETERS, receiver_mode) {
// |argc| is either a smi or intptr depending on |param_mode| }
// |argc| is either a smi or intptr depending on |param_mode|. The arguments
// include a receiver iff |receiver_mode| is kHasReceiver.
CodeStubArguments(CodeStubAssembler* assembler, Node* argc, Node* fp, CodeStubArguments(CodeStubAssembler* assembler, Node* argc, Node* fp,
CodeStubAssembler::ParameterMode param_mode); CodeStubAssembler::ParameterMode param_mode,
ReceiverMode receiver_mode = ReceiverMode::kHasReceiver);
Node* GetReceiver() const; Node* GetReceiver() const;
...@@ -1575,6 +1581,7 @@ class CodeStubArguments { ...@@ -1575,6 +1581,7 @@ class CodeStubArguments {
CodeStubAssembler* assembler_; CodeStubAssembler* assembler_;
CodeStubAssembler::ParameterMode argc_mode_; CodeStubAssembler::ParameterMode argc_mode_;
ReceiverMode receiver_mode_;
Node* argc_; Node* argc_;
Node* arguments_; Node* arguments_;
Node* fp_; Node* fp_;
......
...@@ -2174,11 +2174,28 @@ void BytecodeGraphBuilder::VisitToNumber() { ...@@ -2174,11 +2174,28 @@ void BytecodeGraphBuilder::VisitToNumber() {
} }
void BytecodeGraphBuilder::VisitToPrimitiveToString() { void BytecodeGraphBuilder::VisitToPrimitiveToString() {
UNREACHABLE(); // TODO(rmcilroy): Implement this. PrepareEagerCheckpoint();
Node* object = environment()->LookupAccumulator();
Node* node = NewNode(javascript()->ToPrimitiveToString(), object);
environment()->BindRegister(bytecode_iterator().GetRegisterOperand(0), node,
Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitStringConcat() { void BytecodeGraphBuilder::VisitStringConcat() {
UNREACHABLE(); // TODO(rmcilroy): Implement this. interpreter::Register first_reg = bytecode_iterator().GetRegisterOperand(0);
int operand_count =
static_cast<int>(bytecode_iterator().GetRegisterCountOperand(1));
Node** operands =
local_zone()->NewArray<Node*>(static_cast<size_t>(operand_count));
int operand_base = first_reg.index();
for (int i = 0; i < operand_count; ++i) {
operands[i] =
environment()->LookupRegister(interpreter::Register(operand_base + i));
}
Node* node = MakeNode(javascript()->StringConcat(operand_count),
operand_count, operands, false);
environment()->BindAccumulator(node, Environment::kAttachFrameState);
} }
void BytecodeGraphBuilder::VisitJump() { BuildJump(); } void BytecodeGraphBuilder::VisitJump() { BuildJump(); }
......
...@@ -620,6 +620,22 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function, ...@@ -620,6 +620,22 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function,
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes); return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes);
} }
Node* CodeAssembler::TailCallRuntimeN(Runtime::FunctionId function,
Node* context, Node* argc) {
CallDescriptor* desc = Linkage::GetRuntimeCallDescriptor(
zone(), function, 0, Operator::kNoProperties,
CallDescriptor::kSupportsTailCalls);
int return_count = static_cast<int>(desc->ReturnCount());
Node* centry =
HeapConstant(CodeFactory::RuntimeCEntry(isolate(), return_count));
Node* ref = ExternalConstant(ExternalReference(function, isolate()));
Node* nodes[] = {centry, ref, argc, context};
return raw_assembler()->TailCallN(desc, arraysize(nodes), nodes);
}
// Instantiate TailCallRuntime() for argument counts used by CSA-generated code // Instantiate TailCallRuntime() for argument counts used by CSA-generated code
#define INSTANTIATE(...) \ #define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \ template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \
......
...@@ -351,6 +351,11 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -351,6 +351,11 @@ class V8_EXPORT_PRIVATE CodeAssembler {
Node* TailCallRuntime(Runtime::FunctionId function, Node* context, Node* TailCallRuntime(Runtime::FunctionId function, Node* context,
TArgs... args); TArgs... args);
// Tail call into the runtime passing the same |argc| stack arguments that we
// were called with.
Node* TailCallRuntimeN(Runtime::FunctionId function, Node* context,
Node* argc);
template <class... TArgs> template <class... TArgs>
Node* CallStub(Callable const& callable, Node* context, TArgs... args) { Node* CallStub(Callable const& callable, Node* context, TArgs... args) {
Node* target = HeapConstant(callable.code()); Node* target = HeapConstant(callable.code());
......
...@@ -79,6 +79,7 @@ REPLACE_STUB_CALL(ToNumber) ...@@ -79,6 +79,7 @@ REPLACE_STUB_CALL(ToNumber)
REPLACE_STUB_CALL(ToName) REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject) REPLACE_STUB_CALL(ToObject)
REPLACE_STUB_CALL(ToString) REPLACE_STUB_CALL(ToString)
REPLACE_STUB_CALL(ToPrimitiveToString)
#undef REPLACE_STUB_CALL #undef REPLACE_STUB_CALL
void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable, void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
...@@ -153,6 +154,19 @@ void JSGenericLowering::LowerJSTypeOf(Node* node) { ...@@ -153,6 +154,19 @@ void JSGenericLowering::LowerJSTypeOf(Node* node) {
Operator::kEliminatable); Operator::kEliminatable);
} }
void JSGenericLowering::LowerJSStringConcat(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
int operand_count = StringConcatParameterOf(node->op()).operand_count();
Callable callable = Builtins::CallableFor(isolate(), Builtins::kStringConcat);
const CallInterfaceDescriptor& descriptor = callable.descriptor();
CallDescriptor* desc = Linkage::GetStubCallDescriptor(
isolate(), zone(), descriptor, operand_count, flags,
node->op()->properties());
Node* stub_code = jsgraph()->HeapConstant(callable.code());
node->InsertInput(zone(), 0, stub_code);
node->InsertInput(zone(), 1, jsgraph()->Int32Constant(operand_count));
NodeProperties::ChangeOp(node, common()->Call(desc));
}
void JSGenericLowering::LowerJSLoadProperty(Node* node) { void JSGenericLowering::LowerJSLoadProperty(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
...@@ -171,7 +185,6 @@ void JSGenericLowering::LowerJSLoadProperty(Node* node) { ...@@ -171,7 +185,6 @@ void JSGenericLowering::LowerJSLoadProperty(Node* node) {
} }
} }
void JSGenericLowering::LowerJSLoadNamed(Node* node) { void JSGenericLowering::LowerJSLoadNamed(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node); CallDescriptor::Flags flags = FrameStateFlagForCall(node);
NamedAccess const& p = NamedAccessOf(node->op()); NamedAccess const& p = NamedAccessOf(node->op());
......
...@@ -116,6 +116,29 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const* op) { ...@@ -116,6 +116,29 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const* op) {
return OpParameter<SpreadWithArityParameter>(op); return OpParameter<SpreadWithArityParameter>(op);
} }
bool operator==(StringConcatParameter const& lhs,
StringConcatParameter const& rhs) {
return lhs.operand_count() == rhs.operand_count();
}
bool operator!=(StringConcatParameter const& lhs,
StringConcatParameter const& rhs) {
return !(lhs == rhs);
}
size_t hash_value(StringConcatParameter const& p) {
return base::hash_combine(p.operand_count());
}
std::ostream& operator<<(std::ostream& os, StringConcatParameter const& p) {
return os << p.operand_count();
}
StringConcatParameter const& StringConcatParameterOf(Operator const* op) {
DCHECK(op->opcode() == IrOpcode::kJSStringConcat);
return OpParameter<StringConcatParameter>(op);
}
std::ostream& operator<<(std::ostream& os, CallParameters const& p) { std::ostream& operator<<(std::ostream& os, CallParameters const& p) {
os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode() << ", " os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode() << ", "
<< p.tail_call_mode(); << p.tail_call_mode();
...@@ -590,6 +613,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) { ...@@ -590,6 +613,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(ToNumber, Operator::kNoProperties, 1, 1) \ V(ToNumber, Operator::kNoProperties, 1, 1) \
V(ToObject, Operator::kFoldable, 1, 1) \ V(ToObject, Operator::kFoldable, 1, 1) \
V(ToString, Operator::kNoProperties, 1, 1) \ V(ToString, Operator::kNoProperties, 1, 1) \
V(ToPrimitiveToString, Operator::kNoProperties, 1, 1) \
V(Create, Operator::kNoProperties, 2, 1) \ V(Create, Operator::kNoProperties, 2, 1) \
V(CreateIterResultObject, Operator::kEliminatable, 2, 1) \ V(CreateIterResultObject, Operator::kEliminatable, 2, 1) \
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \ V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
...@@ -740,6 +764,15 @@ BINARY_OP_LIST(BINARY_OP) ...@@ -740,6 +764,15 @@ BINARY_OP_LIST(BINARY_OP)
COMPARE_OP_LIST(COMPARE_OP) COMPARE_OP_LIST(COMPARE_OP)
#undef COMPARE_OP #undef COMPARE_OP
const Operator* JSOperatorBuilder::StringConcat(int operand_count) {
StringConcatParameter parameters(operand_count);
return new (zone()) Operator1<StringConcatParameter>( // --
IrOpcode::kJSStringConcat, Operator::kNoProperties, // opcode
"JSStringConcat", // name
operand_count, 1, 1, 1, 1, 2, // counts
parameters); // parameter
}
const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral( const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral(
const VectorSlotPair& feedback) { const VectorSlotPair& feedback) {
FeedbackParameter parameters(feedback); FeedbackParameter parameters(feedback);
...@@ -1011,7 +1044,6 @@ const Operator* JSOperatorBuilder::CreateArguments(CreateArgumentsType type) { ...@@ -1011,7 +1044,6 @@ const Operator* JSOperatorBuilder::CreateArguments(CreateArgumentsType type) {
type); // parameter type); // parameter
} }
const Operator* JSOperatorBuilder::CreateArray(size_t arity, const Operator* JSOperatorBuilder::CreateArray(size_t arity,
Handle<AllocationSite> site) { Handle<AllocationSite> site) {
// constructor, new_target, arg1, ..., argN // constructor, new_target, arg1, ..., argN
......
...@@ -619,6 +619,27 @@ std::ostream& operator<<(std::ostream&, CreateLiteralParameters const&); ...@@ -619,6 +619,27 @@ std::ostream& operator<<(std::ostream&, CreateLiteralParameters const&);
const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op); const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op);
// Defines the number of operands passed to a JSStringConcat operator.
class StringConcatParameter final {
public:
explicit StringConcatParameter(int operand_count)
: operand_count_(operand_count) {}
int operand_count() const { return operand_count_; }
private:
uint32_t const operand_count_;
};
bool operator==(StringConcatParameter const&, StringConcatParameter const&);
bool operator!=(StringConcatParameter const&, StringConcatParameter const&);
size_t hash_value(StringConcatParameter const&);
std::ostream& operator<<(std::ostream&, StringConcatParameter const&);
StringConcatParameter const& StringConcatParameterOf(Operator const*);
class GeneratorStoreParameters final { class GeneratorStoreParameters final {
public: public:
GeneratorStoreParameters(int register_count, SuspendFlags flags) GeneratorStoreParameters(int register_count, SuspendFlags flags)
...@@ -684,6 +705,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -684,6 +705,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* ToNumber(); const Operator* ToNumber();
const Operator* ToObject(); const Operator* ToObject();
const Operator* ToString(); const Operator* ToString();
const Operator* ToPrimitiveToString();
const Operator* Create(); const Operator* Create();
const Operator* CreateArguments(CreateArgumentsType type); const Operator* CreateArguments(CreateArgumentsType type);
...@@ -766,6 +788,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -766,6 +788,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* LoadMessage(); const Operator* LoadMessage();
const Operator* StoreMessage(); const Operator* StoreMessage();
const Operator* StringConcat(int operand_count);
// Used to implement Ignition's SuspendGenerator bytecode. // Used to implement Ignition's SuspendGenerator bytecode.
const Operator* GeneratorStore(int register_count, const Operator* GeneratorStore(int register_count,
SuspendFlags suspend_flags); SuspendFlags suspend_flags);
......
...@@ -1163,6 +1163,7 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) { ...@@ -1163,6 +1163,7 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
} }
Reduction JSTypedLowering::ReduceJSToString(Node* node) { Reduction JSTypedLowering::ReduceJSToString(Node* node) {
DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
// Try to reduce the input first. // Try to reduce the input first.
Node* const input = node->InputAt(0); Node* const input = node->InputAt(0);
Reduction reduction = ReduceJSToStringInput(input); Reduction reduction = ReduceJSToStringInput(input);
...@@ -1173,6 +1174,23 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) { ...@@ -1173,6 +1174,23 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) {
return NoChange(); return NoChange();
} }
Reduction JSTypedLowering::ReduceJSToPrimitiveToString(Node* node) {
DCHECK_EQ(IrOpcode::kJSToPrimitiveToString, node->opcode());
Node* input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::Primitive())) {
// If node is already a primitive, then reduce to JSToString and try to
// reduce that further.
NodeProperties::ChangeOp(node, javascript()->ToString());
Reduction reduction = ReduceJSToString(node);
if (reduction.Changed()) {
return reduction;
}
return Changed(node);
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSToObject(Node* node) { Reduction JSTypedLowering::ReduceJSToObject(Node* node) {
DCHECK_EQ(IrOpcode::kJSToObject, node->opcode()); DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 0); Node* receiver = NodeProperties::GetValueInput(node, 0);
...@@ -2244,6 +2262,8 @@ Reduction JSTypedLowering::Reduce(Node* node) { ...@@ -2244,6 +2262,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSToNumber(node); return ReduceJSToNumber(node);
case IrOpcode::kJSToString: case IrOpcode::kJSToString:
return ReduceJSToString(node); return ReduceJSToString(node);
case IrOpcode::kJSToPrimitiveToString:
return ReduceJSToPrimitiveToString(node);
case IrOpcode::kJSToObject: case IrOpcode::kJSToObject:
return ReduceJSToObject(node); return ReduceJSToObject(node);
case IrOpcode::kJSTypeOf: case IrOpcode::kJSTypeOf:
......
...@@ -67,6 +67,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final ...@@ -67,6 +67,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSToNumber(Node* node); Reduction ReduceJSToNumber(Node* node);
Reduction ReduceJSToStringInput(Node* input); Reduction ReduceJSToStringInput(Node* input);
Reduction ReduceJSToString(Node* node); Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToPrimitiveToString(Node* node);
Reduction ReduceJSToObject(Node* node); Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node); Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSConstructForwardVarargs(Node* node); Reduction ReduceJSConstructForwardVarargs(Node* node);
......
...@@ -116,7 +116,8 @@ ...@@ -116,7 +116,8 @@
V(JSToName) \ V(JSToName) \
V(JSToNumber) \ V(JSToNumber) \
V(JSToObject) \ V(JSToObject) \
V(JSToString) V(JSToString) \
V(JSToPrimitiveToString)
#define JS_OTHER_UNOP_LIST(V) \ #define JS_OTHER_UNOP_LIST(V) \
V(JSClassOf) \ V(JSClassOf) \
...@@ -177,6 +178,7 @@ ...@@ -177,6 +178,7 @@
V(JSGeneratorRestoreContinuation) \ V(JSGeneratorRestoreContinuation) \
V(JSGeneratorRestoreRegister) \ V(JSGeneratorRestoreRegister) \
V(JSStackCheck) \ V(JSStackCheck) \
V(JSStringConcat) \
V(JSDebugger) V(JSDebugger)
#define JS_OP_LIST(V) \ #define JS_OP_LIST(V) \
......
...@@ -94,6 +94,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) { ...@@ -94,6 +94,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSToNumber: case IrOpcode::kJSToNumber:
case IrOpcode::kJSToObject: case IrOpcode::kJSToObject:
case IrOpcode::kJSToString: case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
// Call operations // Call operations
case IrOpcode::kJSConstructForwardVarargs: case IrOpcode::kJSConstructForwardVarargs:
......
...@@ -2908,6 +2908,7 @@ class RepresentationSelector { ...@@ -2908,6 +2908,7 @@ class RepresentationSelector {
case IrOpcode::kJSToName: case IrOpcode::kJSToName:
case IrOpcode::kJSToObject: case IrOpcode::kJSToObject:
case IrOpcode::kJSToString: case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
VisitInputs(node); VisitInputs(node);
// Assume the output is tagged. // Assume the output is tagged.
return SetOutput(node, MachineRepresentation::kTagged); return SetOutput(node, MachineRepresentation::kTagged);
......
...@@ -270,6 +270,7 @@ class Typer::Visitor : public Reducer { ...@@ -270,6 +270,7 @@ class Typer::Visitor : public Reducer {
static Type* ToNumber(Type*, Typer*); static Type* ToNumber(Type*, Typer*);
static Type* ToObject(Type*, Typer*); static Type* ToObject(Type*, Typer*);
static Type* ToString(Type*, Typer*); static Type* ToString(Type*, Typer*);
static Type* ToPrimitiveToString(Type*, Typer*);
#define DECLARE_METHOD(Name) \ #define DECLARE_METHOD(Name) \
static Type* Name(Type* type, Typer* t) { \ static Type* Name(Type* type, Typer* t) { \
return t->operation_typer_.Name(type); \ return t->operation_typer_.Name(type); \
...@@ -504,6 +505,15 @@ Type* Typer::Visitor::ToString(Type* type, Typer* t) { ...@@ -504,6 +505,15 @@ Type* Typer::Visitor::ToString(Type* type, Typer* t) {
return Type::String(); return Type::String();
} }
// static
Type* Typer::Visitor::ToPrimitiveToString(Type* type, Typer* t) {
// ES6 section 7.1.1 ToPrimitive( argument, "default" ) followed by
// ES6 section 7.1.12 ToString ( argument )
type = ToPrimitive(type, t);
if (type->Is(Type::String())) return type;
return Type::String();
}
// Type checks. // Type checks.
Type* Typer::Visitor::ObjectIsDetectableCallable(Type* type, Typer* t) { Type* Typer::Visitor::ObjectIsDetectableCallable(Type* type, Typer* t) {
...@@ -1006,6 +1016,9 @@ Type* Typer::Visitor::JSShiftRightLogicalTyper(Type* lhs, Type* rhs, Typer* t) { ...@@ -1006,6 +1016,9 @@ Type* Typer::Visitor::JSShiftRightLogicalTyper(Type* lhs, Type* rhs, Typer* t) {
return NumberShiftRightLogical(ToNumber(lhs, t), ToNumber(rhs, t), t); return NumberShiftRightLogical(ToNumber(lhs, t), ToNumber(rhs, t), t);
} }
// JS string concatenation.
Type* Typer::Visitor::TypeJSStringConcat(Node* node) { return Type::String(); }
// JS arithmetic operators. // JS arithmetic operators.
...@@ -1082,6 +1095,10 @@ Type* Typer::Visitor::TypeJSToString(Node* node) { ...@@ -1082,6 +1095,10 @@ Type* Typer::Visitor::TypeJSToString(Node* node) {
return TypeUnaryOp(node, ToString); return TypeUnaryOp(node, ToString);
} }
Type* Typer::Visitor::TypeJSToPrimitiveToString(Node* node) {
return TypeUnaryOp(node, ToPrimitiveToString);
}
// JS object operators. // JS object operators.
......
...@@ -543,6 +543,16 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -543,6 +543,16 @@ void Verifier::Visitor::Check(Node* node) {
// Type is 32 bit integral. // Type is 32 bit integral.
CheckTypeIs(node, Type::Integral32()); CheckTypeIs(node, Type::Integral32());
break; break;
case IrOpcode::kJSStringConcat:
// Type is string and all inputs are strings.
CheckTypeIs(node, Type::String());
for (int i = 0; i < StringConcatParameterOf(node->op()).operand_count();
i++) {
CheckValueInputIs(node, i, Type::String());
}
break;
case IrOpcode::kJSAdd: case IrOpcode::kJSAdd:
// Type is Number or String. // Type is Number or String.
CheckTypeIs(node, Type::NumberOrString()); CheckTypeIs(node, Type::NumberOrString());
...@@ -575,6 +585,7 @@ void Verifier::Visitor::Check(Node* node) { ...@@ -575,6 +585,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Number()); CheckTypeIs(node, Type::Number());
break; break;
case IrOpcode::kJSToString: case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
// Type is String. // Type is String.
CheckTypeIs(node, Type::String()); CheckTypeIs(node, Type::String());
break; break;
......
...@@ -439,6 +439,7 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) { ...@@ -439,6 +439,7 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
case Bytecode::kToNumber: case Bytecode::kToNumber:
case Bytecode::kToName: case Bytecode::kToName:
// Misc. // Misc.
case Bytecode::kStringConcat:
case Bytecode::kForInPrepare: case Bytecode::kForInPrepare:
case Bytecode::kForInContinue: case Bytecode::kForInContinue:
case Bytecode::kForInNext: case Bytecode::kForInNext:
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; }
const Register StringCompareDescriptor::LeftRegister() { return edx; } const Register StringCompareDescriptor::LeftRegister() { return edx; }
const Register StringCompareDescriptor::RightRegister() { return eax; } const Register StringCompareDescriptor::RightRegister() { return eax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; }
const Register ApiGetterDescriptor::HolderRegister() { return ecx; } const Register ApiGetterDescriptor::HolderRegister() { return ecx; }
const Register ApiGetterDescriptor::CallbackRegister() { return eax; } const Register ApiGetterDescriptor::CallbackRegister() { return eax; }
......
...@@ -258,6 +258,20 @@ void StringCompareDescriptor::InitializePlatformSpecific( ...@@ -258,6 +258,20 @@ void StringCompareDescriptor::InitializePlatformSpecific(
data->InitializePlatformSpecific(arraysize(registers), registers); data->InitializePlatformSpecific(arraysize(registers), registers);
} }
void StringConcatDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) {
Register registers[] = {ArgumentsCountRegister()};
data->InitializePlatformSpecific(arraysize(registers), registers);
}
void StringConcatDescriptor::InitializePlatformIndependent(
CallInterfaceDescriptorData* data) {
// kArgumentsCount
MachineType machine_types[] = {MachineType::Int32()};
data->InitializePlatformIndependent(arraysize(machine_types), 0,
machine_types);
}
void TypeConversionDescriptor::InitializePlatformSpecific( void TypeConversionDescriptor::InitializePlatformSpecific(
CallInterfaceDescriptorData* data) { CallInterfaceDescriptorData* data) {
Register registers[] = {ArgumentRegister()}; Register registers[] = {ArgumentRegister()};
......
...@@ -67,6 +67,7 @@ class PlatformInterfaceDescriptor; ...@@ -67,6 +67,7 @@ class PlatformInterfaceDescriptor;
V(StringCharAt) \ V(StringCharAt) \
V(StringCharCodeAt) \ V(StringCharCodeAt) \
V(StringCompare) \ V(StringCompare) \
V(StringConcat) \
V(SubString) \ V(SubString) \
V(ForInPrepare) \ V(ForInPrepare) \
V(GetProperty) \ V(GetProperty) \
...@@ -689,7 +690,6 @@ class ArrayNArgumentsConstructorDescriptor : public CallInterfaceDescriptor { ...@@ -689,7 +690,6 @@ class ArrayNArgumentsConstructorDescriptor : public CallInterfaceDescriptor {
ArrayNArgumentsConstructorDescriptor, CallInterfaceDescriptor) ArrayNArgumentsConstructorDescriptor, CallInterfaceDescriptor)
}; };
class CompareDescriptor : public CallInterfaceDescriptor { class CompareDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kLeft, kRight) DEFINE_PARAMETERS(kLeft, kRight)
...@@ -752,6 +752,15 @@ class StringCompareDescriptor : public CallInterfaceDescriptor { ...@@ -752,6 +752,15 @@ class StringCompareDescriptor : public CallInterfaceDescriptor {
static const Register RightRegister(); static const Register RightRegister();
}; };
class StringConcatDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kArgumentsCount)
DECLARE_DESCRIPTOR_WITH_CUSTOM_FUNCTION_TYPE(StringConcatDescriptor,
CallInterfaceDescriptor)
static const Register ArgumentsCountRegister();
};
class SubStringDescriptor : public CallInterfaceDescriptor { class SubStringDescriptor : public CallInterfaceDescriptor {
public: public:
DEFINE_PARAMETERS(kString, kFrom, kTo) DEFINE_PARAMETERS(kString, kFrom, kTo)
......
This diff is collapsed.
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return t1; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return t1; }
const Register StringCompareDescriptor::LeftRegister() { return a1; } const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; } const Register StringCompareDescriptor::RightRegister() { return a0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return a0; }
const Register ApiGetterDescriptor::HolderRegister() { return a0; } const Register ApiGetterDescriptor::HolderRegister() { return a0; }
const Register ApiGetterDescriptor::CallbackRegister() { return a3; } const Register ApiGetterDescriptor::CallbackRegister() { return a3; }
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return a5; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return a5; }
const Register StringCompareDescriptor::LeftRegister() { return a1; } const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; } const Register StringCompareDescriptor::RightRegister() { return a0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return a0; }
const Register ApiGetterDescriptor::HolderRegister() { return a0; } const Register ApiGetterDescriptor::HolderRegister() { return a0; }
const Register ApiGetterDescriptor::CallbackRegister() { return a3; } const Register ApiGetterDescriptor::CallbackRegister() { return a3; }
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r8; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r8; }
const Register StringCompareDescriptor::LeftRegister() { return r4; } const Register StringCompareDescriptor::LeftRegister() { return r4; }
const Register StringCompareDescriptor::RightRegister() { return r3; } const Register StringCompareDescriptor::RightRegister() { return r3; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r3; }
const Register ApiGetterDescriptor::HolderRegister() { return r3; } const Register ApiGetterDescriptor::HolderRegister() { return r3; }
const Register ApiGetterDescriptor::CallbackRegister() { return r6; } const Register ApiGetterDescriptor::CallbackRegister() { return r6; }
......
...@@ -35,23 +35,6 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) { ...@@ -35,23 +35,6 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
static_cast<PretenureFlag>(pretenured_flag)); static_cast<PretenureFlag>(pretenured_flag));
} }
RUNTIME_FUNCTION(Runtime_InterpreterStringConcat) {
HandleScope scope(isolate);
DCHECK_LE(2, args.length());
int const argc = args.length();
ScopedVector<Handle<Object>> argv(argc);
isolate->counters()->string_add_runtime()->Increment();
IncrementalStringBuilder builder(isolate);
for (int i = 0; i < argc; ++i) {
Handle<String> str = Handle<String>::cast(args.at(i));
if (str->length() != 0) {
builder.AppendString(str);
}
}
RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
}
#ifdef V8_TRACE_IGNITION #ifdef V8_TRACE_IGNITION
namespace { namespace {
......
...@@ -108,7 +108,6 @@ MaybeHandle<String> StringReplaceOneCharWithString( ...@@ -108,7 +108,6 @@ MaybeHandle<String> StringReplaceOneCharWithString(
} }
} }
RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) { RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(3, args.length());
...@@ -197,7 +196,6 @@ RUNTIME_FUNCTION(Runtime_SubString) { ...@@ -197,7 +196,6 @@ RUNTIME_FUNCTION(Runtime_SubString) {
return *isolate->factory()->NewSubString(string, start, end); return *isolate->factory()->NewSubString(string, start, end);
} }
RUNTIME_FUNCTION(Runtime_StringAdd) { RUNTIME_FUNCTION(Runtime_StringAdd) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
...@@ -208,6 +206,22 @@ RUNTIME_FUNCTION(Runtime_StringAdd) { ...@@ -208,6 +206,22 @@ RUNTIME_FUNCTION(Runtime_StringAdd) {
isolate->factory()->NewConsString(str1, str2)); isolate->factory()->NewConsString(str1, str2));
} }
RUNTIME_FUNCTION(Runtime_StringConcat) {
HandleScope scope(isolate);
DCHECK_LE(2, args.length());
int const argc = args.length();
ScopedVector<Handle<Object>> argv(argc);
isolate->counters()->string_add_runtime()->Increment();
IncrementalStringBuilder builder(isolate);
for (int i = 0; i < argc; ++i) {
Handle<String> str = Handle<String>::cast(args.at(i));
if (str->length() != 0) {
builder.AppendString(str);
}
}
RETURN_RESULT_OR_FAILURE(isolate, builder.Finish());
}
RUNTIME_FUNCTION(Runtime_InternalizeString) { RUNTIME_FUNCTION(Runtime_InternalizeString) {
HandleScope handles(isolate); HandleScope handles(isolate);
...@@ -216,7 +230,6 @@ RUNTIME_FUNCTION(Runtime_InternalizeString) { ...@@ -216,7 +230,6 @@ RUNTIME_FUNCTION(Runtime_InternalizeString) {
return *isolate->factory()->InternalizeString(string); return *isolate->factory()->InternalizeString(string);
} }
RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) { RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
...@@ -236,7 +249,6 @@ RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) { ...@@ -236,7 +249,6 @@ RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
return Smi::FromInt(subject->Get(i)); return Smi::FromInt(subject->Get(i));
} }
RUNTIME_FUNCTION(Runtime_StringCompare) { RUNTIME_FUNCTION(Runtime_StringCompare) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
...@@ -256,7 +268,6 @@ RUNTIME_FUNCTION(Runtime_StringCompare) { ...@@ -256,7 +268,6 @@ RUNTIME_FUNCTION(Runtime_StringCompare) {
UNREACHABLE(); UNREACHABLE();
} }
RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(3, args.length());
...@@ -329,7 +340,6 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) { ...@@ -329,7 +340,6 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
} }
} }
RUNTIME_FUNCTION(Runtime_StringBuilderJoin) { RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(3, args.length());
...@@ -470,7 +480,6 @@ static void JoinSparseArrayWithSeparator(FixedArray* elements, ...@@ -470,7 +480,6 @@ static void JoinSparseArrayWithSeparator(FixedArray* elements,
DCHECK(cursor <= buffer.length()); DCHECK(cursor <= buffer.length());
} }
RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
HandleScope scope(isolate); HandleScope scope(isolate);
DCHECK_EQ(3, args.length()); DCHECK_EQ(3, args.length());
...@@ -555,7 +564,6 @@ RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) { ...@@ -555,7 +564,6 @@ RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
} }
} }
// Copies Latin1 characters to the given fixed array looking up // Copies Latin1 characters to the given fixed array looking up
// one-char strings in the cache. Gives up on the first char that is // one-char strings in the cache. Gives up on the first char that is
// not in the cache and fills the remainder with smi zeros. Returns // not in the cache and fills the remainder with smi zeros. Returns
...@@ -586,7 +594,6 @@ static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars, ...@@ -586,7 +594,6 @@ static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
return i; return i;
} }
// Converts a String to JSArray. // Converts a String to JSArray.
// For example, "foo" => ["f", "o", "o"]. // For example, "foo" => ["f", "o", "o"].
RUNTIME_FUNCTION(Runtime_StringToArray) { RUNTIME_FUNCTION(Runtime_StringToArray) {
...@@ -634,7 +641,6 @@ RUNTIME_FUNCTION(Runtime_StringToArray) { ...@@ -634,7 +641,6 @@ RUNTIME_FUNCTION(Runtime_StringToArray) {
return *isolate->factory()->NewJSArrayWithElements(elements); return *isolate->factory()->NewJSArrayWithElements(elements);
} }
RUNTIME_FUNCTION(Runtime_StringLessThan) { RUNTIME_FUNCTION(Runtime_StringLessThan) {
HandleScope handle_scope(isolate); HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length()); DCHECK_EQ(2, args.length());
...@@ -726,7 +732,6 @@ RUNTIME_FUNCTION(Runtime_FlattenString) { ...@@ -726,7 +732,6 @@ RUNTIME_FUNCTION(Runtime_FlattenString) {
return *String::Flatten(str); return *String::Flatten(str);
} }
RUNTIME_FUNCTION(Runtime_StringCharFromCode) { RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
HandleScope handlescope(isolate); HandleScope handlescope(isolate);
DCHECK_EQ(1, args.length()); DCHECK_EQ(1, args.length());
......
...@@ -229,7 +229,6 @@ namespace internal { ...@@ -229,7 +229,6 @@ namespace internal {
#define FOR_EACH_INTRINSIC_INTERPRETER(F) \ #define FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F) \ FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F) \
F(InterpreterNewClosure, 4, 1) \ F(InterpreterNewClosure, 4, 1) \
F(InterpreterStringConcat, -1 /* >= 2 */, 1) \
F(InterpreterAdvanceBytecodeOffset, 2, 1) F(InterpreterAdvanceBytecodeOffset, 2, 1)
#define FOR_EACH_INTRINSIC_FUNCTION(F) \ #define FOR_EACH_INTRINSIC_FUNCTION(F) \
...@@ -542,6 +541,7 @@ namespace internal { ...@@ -542,6 +541,7 @@ namespace internal {
F(StringLastIndexOf, 2, 1) \ F(StringLastIndexOf, 2, 1) \
F(SubString, 3, 1) \ F(SubString, 3, 1) \
F(StringAdd, 2, 1) \ F(StringAdd, 2, 1) \
F(StringConcat, -1 /* >= 2 */, 1) \
F(InternalizeString, 1, 1) \ F(InternalizeString, 1, 1) \
F(StringCharCodeAtRT, 2, 1) \ F(StringCharCodeAtRT, 2, 1) \
F(StringCompare, 2, 1) \ F(StringCompare, 2, 1) \
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r7; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r7; }
const Register StringCompareDescriptor::LeftRegister() { return r3; } const Register StringCompareDescriptor::LeftRegister() { return r3; }
const Register StringCompareDescriptor::RightRegister() { return r2; } const Register StringCompareDescriptor::RightRegister() { return r2; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r2; }
const Register ApiGetterDescriptor::HolderRegister() { return r2; } const Register ApiGetterDescriptor::HolderRegister() { return r2; }
const Register ApiGetterDescriptor::CallbackRegister() { return r5; } const Register ApiGetterDescriptor::CallbackRegister() { return r5; }
......
...@@ -186,6 +186,7 @@ ...@@ -186,6 +186,7 @@
'builtins/builtins-constructor-gen.h', 'builtins/builtins-constructor-gen.h',
'builtins/builtins-constructor.h', 'builtins/builtins-constructor.h',
'builtins/builtins-conversion-gen.cc', 'builtins/builtins-conversion-gen.cc',
'builtins/builtins-conversion-gen.h',
'builtins/builtins-date-gen.cc', 'builtins/builtins-date-gen.cc',
'builtins/builtins-debug-gen.cc', 'builtins/builtins-debug-gen.cc',
'builtins/builtins-forin-gen.cc', 'builtins/builtins-forin-gen.cc',
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r11; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r11; }
const Register StringCompareDescriptor::LeftRegister() { return rdx; } const Register StringCompareDescriptor::LeftRegister() { return rdx; }
const Register StringCompareDescriptor::RightRegister() { return rax; } const Register StringCompareDescriptor::RightRegister() { return rax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return rbx; }
const Register ApiGetterDescriptor::HolderRegister() { return rcx; } const Register ApiGetterDescriptor::HolderRegister() { return rcx; }
const Register ApiGetterDescriptor::CallbackRegister() { return rbx; } const Register ApiGetterDescriptor::CallbackRegister() { return rbx; }
......
...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; } ...@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; }
const Register StringCompareDescriptor::LeftRegister() { return edx; } const Register StringCompareDescriptor::LeftRegister() { return edx; }
const Register StringCompareDescriptor::RightRegister() { return eax; } const Register StringCompareDescriptor::RightRegister() { return eax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; }
const Register ApiGetterDescriptor::HolderRegister() { return ecx; } const Register ApiGetterDescriptor::HolderRegister() { return ecx; }
const Register ApiGetterDescriptor::CallbackRegister() { return eax; } const Register ApiGetterDescriptor::CallbackRegister() { return eax; }
......
...@@ -42,6 +42,7 @@ const SharedOperator kSharedOperators[] = { ...@@ -42,6 +42,7 @@ const SharedOperator kSharedOperators[] = {
} }
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2), SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2), SHARED(ToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToPrimitiveToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2), SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToObject, Operator::kFoldable, 1, 1, 1, 1, 1, 1, 2), SHARED(ToObject, Operator::kFoldable, 1, 1, 1, 1, 1, 1, 2),
SHARED(Create, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2), SHARED(Create, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
......
...@@ -250,11 +250,27 @@ TEST_F(JSTypedLoweringTest, JSToStringWithBoolean) { ...@@ -250,11 +250,27 @@ TEST_F(JSTypedLoweringTest, JSToStringWithBoolean) {
IsHeapConstant(factory()->false_string()))); IsHeapConstant(factory()->false_string())));
} }
// -----------------------------------------------------------------------------
// JSToPrimitiveToString
TEST_F(JSTypedLoweringTest, JSToPrimitiveToStringWithBoolean) {
Node* const input = Parameter(Type::Boolean(), 0);
Node* const context = Parameter(Type::Any(), 1);
Node* const frame_state = EmptyFrameState();
Node* const effect = graph()->start();
Node* const control = graph()->start();
Reduction r = Reduce(graph()->NewNode(javascript()->ToString(), input,
context, frame_state, effect, control));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsSelect(MachineRepresentation::kTagged, input,
IsHeapConstant(factory()->true_string()),
IsHeapConstant(factory()->false_string())));
}
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------
// JSStrictEqual // JSStrictEqual
TEST_F(JSTypedLoweringTest, JSStrictEqualWithTheHole) { TEST_F(JSTypedLoweringTest, JSStrictEqualWithTheHole) {
Node* const the_hole = HeapConstant(factory()->the_hole_value()); Node* const the_hole = HeapConstant(factory()->the_hole_value());
Node* const context = UndefinedConstant(); Node* const context = UndefinedConstant();
......
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