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") {
"src/builtins/builtins-constructor-gen.h",
"src/builtins/builtins-constructor.h",
"src/builtins/builtins-conversion-gen.cc",
"src/builtins/builtins-conversion-gen.h",
"src/builtins/builtins-date-gen.cc",
"src/builtins/builtins-debug-gen.cc",
"src/builtins/builtins-forin-gen.cc",
......
......@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r5; }
const Register StringCompareDescriptor::LeftRegister() { return r1; }
const Register StringCompareDescriptor::RightRegister() { return r0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r0; }
const Register ApiGetterDescriptor::HolderRegister() { return r0; }
const Register ApiGetterDescriptor::CallbackRegister() { return r3; }
......
......@@ -49,6 +49,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return x5; }
const Register StringCompareDescriptor::LeftRegister() { return x1; }
const Register StringCompareDescriptor::RightRegister() { return x0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return x0; }
const Register ApiGetterDescriptor::HolderRegister() { return x0; }
const Register ApiGetterDescriptor::CallbackRegister() { return x3; }
......
......@@ -2,28 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/builtins/builtins-conversion-gen.h"
#include "src/builtins/builtins-utils-gen.h"
#include "src/builtins/builtins.h"
#include "src/code-factory.h"
#include "src/code-stub-assembler.h"
#include "src/objects-inl.h"
namespace v8 {
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 ] )
void ConversionBuiltinsAssembler::Generate_NonPrimitiveToPrimitive(
Node* context, Node* input, ToPrimitiveHint hint) {
......@@ -136,6 +124,53 @@ TF_BUILTIN(ToString, CodeStubAssembler) {
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 )
void ConversionBuiltinsAssembler::Generate_OrdinaryToPrimitive(
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 {
TFS(StringIndexOf, kReceiver, kSearchString, kPosition) \
TFC(StringLessThan, Compare, 1) \
TFC(StringLessThanOrEqual, Compare, 1) \
TFC(StringConcat, StringConcat, 1) \
\
/* Interpreter */ \
ASM(InterpreterEntryTrampoline) \
......@@ -192,6 +193,7 @@ namespace internal {
TFC(NonNumberToNumber, TypeConversion, 1) \
TFC(ToNumber, TypeConversion, 1) \
TFC(ToString, TypeConversion, 1) \
TFC(ToPrimitiveToString, TypeConversion, 1) \
TFC(ToInteger, TypeConversion, 1) \
TFC(ToLength, TypeConversion, 1) \
TFC(ClassOf, Typeof, 1) \
......
This diff is collapsed.
......@@ -24,6 +24,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
Label* if_equal, Label* if_not_equal,
Label* if_notbothdirectonebyte);
// String concatenation.
Node* ConcatenateStrings(Node* context, Node* first_arg_ptr, Node* arg_count,
Label* bailout_to_runtime);
protected:
Node* DirectStringData(Node* string, Node* string_instance_type);
......@@ -54,6 +58,10 @@ class StringBuiltinsAssembler : public CodeStubAssembler {
Node* LoadSurrogatePairAt(Node* string, Node* length, Node* index,
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,
Node* const subject_instance_type,
Node* const search_string,
......
......@@ -9175,21 +9175,23 @@ Node* CodeStubAssembler::IsDetachedBuffer(Node* buffer) {
return IsSetWord32<JSArrayBuffer::WasNeutered>(buffer_bit_field);
}
CodeStubArguments::CodeStubArguments(CodeStubAssembler* assembler, Node* argc,
Node* fp,
CodeStubAssembler::ParameterMode mode)
CodeStubArguments::CodeStubArguments(
CodeStubAssembler* assembler, Node* argc, Node* fp,
CodeStubAssembler::ParameterMode param_mode, ReceiverMode receiver_mode)
: assembler_(assembler),
argc_mode_(mode),
argc_mode_(param_mode),
receiver_mode_(receiver_mode),
argc_(argc),
arguments_(nullptr),
fp_(fp != nullptr ? fp : assembler_->LoadFramePointer()) {
Node* offset = assembler_->ElementOffsetFromIndex(
argc_, FAST_ELEMENTS, mode,
argc_, FAST_ELEMENTS, param_mode,
(StandardFrameConstants::kFixedSlotCountAboveFp - 1) * kPointerSize);
arguments_ = assembler_->IntPtrAdd(fp_, offset);
}
Node* CodeStubArguments::GetReceiver() const {
DCHECK_EQ(receiver_mode_, ReceiverMode::kHasReceiver);
return assembler_->Load(MachineType::AnyTagged(), arguments_,
assembler_->IntPtrConstant(kPointerSize));
}
......@@ -9267,8 +9269,14 @@ void CodeStubArguments::ForEach(
}
void CodeStubArguments::PopAndReturn(Node* value) {
assembler_->PopAndReturn(
assembler_->IntPtrAdd(argc_, assembler_->IntPtrConstant(1)), value);
Node* pop_count;
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) {
......
......@@ -1527,15 +1527,21 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
class CodeStubArguments {
public:
typedef compiler::Node Node;
enum ReceiverMode { kHasReceiver, kNoReceiver };
// |argc| is an intptr value which specifies the number of arguments passed
// to the builtin excluding the receiver.
CodeStubArguments(CodeStubAssembler* assembler, Node* argc)
// to the builtin excluding the receiver. The arguments will include a
// receiver iff |receiver_mode| is kHasReceiver.
CodeStubArguments(CodeStubAssembler* assembler, Node* argc,
ReceiverMode receiver_mode = ReceiverMode::kHasReceiver)
: CodeStubArguments(assembler, argc, nullptr,
CodeStubAssembler::INTPTR_PARAMETERS) {}
// |argc| is either a smi or intptr depending on |param_mode|
CodeStubAssembler::INTPTR_PARAMETERS, receiver_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,
CodeStubAssembler::ParameterMode param_mode);
CodeStubAssembler::ParameterMode param_mode,
ReceiverMode receiver_mode = ReceiverMode::kHasReceiver);
Node* GetReceiver() const;
......@@ -1575,6 +1581,7 @@ class CodeStubArguments {
CodeStubAssembler* assembler_;
CodeStubAssembler::ParameterMode argc_mode_;
ReceiverMode receiver_mode_;
Node* argc_;
Node* arguments_;
Node* fp_;
......
......@@ -2174,11 +2174,28 @@ void BytecodeGraphBuilder::VisitToNumber() {
}
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() {
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(); }
......
......@@ -620,6 +620,22 @@ Node* CodeAssembler::TailCallRuntime(Runtime::FunctionId function,
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
#define INSTANTIATE(...) \
template V8_EXPORT_PRIVATE Node* CodeAssembler::TailCallRuntime( \
......
......@@ -351,6 +351,11 @@ class V8_EXPORT_PRIVATE CodeAssembler {
Node* TailCallRuntime(Runtime::FunctionId function, Node* context,
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>
Node* CallStub(Callable const& callable, Node* context, TArgs... args) {
Node* target = HeapConstant(callable.code());
......
......@@ -79,6 +79,7 @@ REPLACE_STUB_CALL(ToNumber)
REPLACE_STUB_CALL(ToName)
REPLACE_STUB_CALL(ToObject)
REPLACE_STUB_CALL(ToString)
REPLACE_STUB_CALL(ToPrimitiveToString)
#undef REPLACE_STUB_CALL
void JSGenericLowering::ReplaceWithStubCall(Node* node, Callable callable,
......@@ -153,6 +154,19 @@ void JSGenericLowering::LowerJSTypeOf(Node* node) {
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) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
......@@ -171,7 +185,6 @@ void JSGenericLowering::LowerJSLoadProperty(Node* node) {
}
}
void JSGenericLowering::LowerJSLoadNamed(Node* node) {
CallDescriptor::Flags flags = FrameStateFlagForCall(node);
NamedAccess const& p = NamedAccessOf(node->op());
......
......@@ -116,6 +116,29 @@ SpreadWithArityParameter const& SpreadWithArityParameterOf(Operator const* 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) {
os << p.arity() << ", " << p.frequency() << ", " << p.convert_mode() << ", "
<< p.tail_call_mode();
......@@ -590,6 +613,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(ToNumber, Operator::kNoProperties, 1, 1) \
V(ToObject, Operator::kFoldable, 1, 1) \
V(ToString, Operator::kNoProperties, 1, 1) \
V(ToPrimitiveToString, Operator::kNoProperties, 1, 1) \
V(Create, Operator::kNoProperties, 2, 1) \
V(CreateIterResultObject, Operator::kEliminatable, 2, 1) \
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
......@@ -740,6 +764,15 @@ BINARY_OP_LIST(BINARY_OP)
COMPARE_OP_LIST(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 VectorSlotPair& feedback) {
FeedbackParameter parameters(feedback);
......@@ -1011,7 +1044,6 @@ const Operator* JSOperatorBuilder::CreateArguments(CreateArgumentsType type) {
type); // parameter
}
const Operator* JSOperatorBuilder::CreateArray(size_t arity,
Handle<AllocationSite> site) {
// constructor, new_target, arg1, ..., argN
......
......@@ -619,6 +619,27 @@ std::ostream& operator<<(std::ostream&, CreateLiteralParameters const&);
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 {
public:
GeneratorStoreParameters(int register_count, SuspendFlags flags)
......@@ -684,6 +705,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* ToNumber();
const Operator* ToObject();
const Operator* ToString();
const Operator* ToPrimitiveToString();
const Operator* Create();
const Operator* CreateArguments(CreateArgumentsType type);
......@@ -766,6 +788,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* LoadMessage();
const Operator* StoreMessage();
const Operator* StringConcat(int operand_count);
// Used to implement Ignition's SuspendGenerator bytecode.
const Operator* GeneratorStore(int register_count,
SuspendFlags suspend_flags);
......
......@@ -1163,6 +1163,7 @@ Reduction JSTypedLowering::ReduceJSToStringInput(Node* input) {
}
Reduction JSTypedLowering::ReduceJSToString(Node* node) {
DCHECK_EQ(IrOpcode::kJSToString, node->opcode());
// Try to reduce the input first.
Node* const input = node->InputAt(0);
Reduction reduction = ReduceJSToStringInput(input);
......@@ -1173,6 +1174,23 @@ Reduction JSTypedLowering::ReduceJSToString(Node* node) {
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) {
DCHECK_EQ(IrOpcode::kJSToObject, node->opcode());
Node* receiver = NodeProperties::GetValueInput(node, 0);
......@@ -2244,6 +2262,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
return ReduceJSToNumber(node);
case IrOpcode::kJSToString:
return ReduceJSToString(node);
case IrOpcode::kJSToPrimitiveToString:
return ReduceJSToPrimitiveToString(node);
case IrOpcode::kJSToObject:
return ReduceJSToObject(node);
case IrOpcode::kJSTypeOf:
......
......@@ -67,6 +67,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
Reduction ReduceJSToNumber(Node* node);
Reduction ReduceJSToStringInput(Node* input);
Reduction ReduceJSToString(Node* node);
Reduction ReduceJSToPrimitiveToString(Node* node);
Reduction ReduceJSToObject(Node* node);
Reduction ReduceJSConvertReceiver(Node* node);
Reduction ReduceJSConstructForwardVarargs(Node* node);
......
......@@ -116,7 +116,8 @@
V(JSToName) \
V(JSToNumber) \
V(JSToObject) \
V(JSToString)
V(JSToString) \
V(JSToPrimitiveToString)
#define JS_OTHER_UNOP_LIST(V) \
V(JSClassOf) \
......@@ -177,6 +178,7 @@
V(JSGeneratorRestoreContinuation) \
V(JSGeneratorRestoreRegister) \
V(JSStackCheck) \
V(JSStringConcat) \
V(JSDebugger)
#define JS_OP_LIST(V) \
......
......@@ -94,6 +94,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSToNumber:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
// Call operations
case IrOpcode::kJSConstructForwardVarargs:
......
......@@ -2908,6 +2908,7 @@ class RepresentationSelector {
case IrOpcode::kJSToName:
case IrOpcode::kJSToObject:
case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
VisitInputs(node);
// Assume the output is tagged.
return SetOutput(node, MachineRepresentation::kTagged);
......
......@@ -270,6 +270,7 @@ class Typer::Visitor : public Reducer {
static Type* ToNumber(Type*, Typer*);
static Type* ToObject(Type*, Typer*);
static Type* ToString(Type*, Typer*);
static Type* ToPrimitiveToString(Type*, Typer*);
#define DECLARE_METHOD(Name) \
static Type* Name(Type* type, Typer* t) { \
return t->operation_typer_.Name(type); \
......@@ -504,6 +505,15 @@ Type* Typer::Visitor::ToString(Type* type, Typer* t) {
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* Typer::Visitor::ObjectIsDetectableCallable(Type* type, 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);
}
// JS string concatenation.
Type* Typer::Visitor::TypeJSStringConcat(Node* node) { return Type::String(); }
// JS arithmetic operators.
......@@ -1082,6 +1095,10 @@ Type* Typer::Visitor::TypeJSToString(Node* node) {
return TypeUnaryOp(node, ToString);
}
Type* Typer::Visitor::TypeJSToPrimitiveToString(Node* node) {
return TypeUnaryOp(node, ToPrimitiveToString);
}
// JS object operators.
......
......@@ -543,6 +543,16 @@ void Verifier::Visitor::Check(Node* node) {
// Type is 32 bit integral.
CheckTypeIs(node, Type::Integral32());
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:
// Type is Number or String.
CheckTypeIs(node, Type::NumberOrString());
......@@ -575,6 +585,7 @@ void Verifier::Visitor::Check(Node* node) {
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kJSToString:
case IrOpcode::kJSToPrimitiveToString:
// Type is String.
CheckTypeIs(node, Type::String());
break;
......
......@@ -439,6 +439,7 @@ bool BytecodeHasNoSideEffect(interpreter::Bytecode bytecode) {
case Bytecode::kToNumber:
case Bytecode::kToName:
// Misc.
case Bytecode::kStringConcat:
case Bytecode::kForInPrepare:
case Bytecode::kForInContinue:
case Bytecode::kForInNext:
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; }
const Register StringCompareDescriptor::LeftRegister() { return edx; }
const Register StringCompareDescriptor::RightRegister() { return eax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; }
const Register ApiGetterDescriptor::HolderRegister() { return ecx; }
const Register ApiGetterDescriptor::CallbackRegister() { return eax; }
......
......@@ -258,6 +258,20 @@ void StringCompareDescriptor::InitializePlatformSpecific(
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(
CallInterfaceDescriptorData* data) {
Register registers[] = {ArgumentRegister()};
......
......@@ -67,6 +67,7 @@ class PlatformInterfaceDescriptor;
V(StringCharAt) \
V(StringCharCodeAt) \
V(StringCompare) \
V(StringConcat) \
V(SubString) \
V(ForInPrepare) \
V(GetProperty) \
......@@ -689,7 +690,6 @@ class ArrayNArgumentsConstructorDescriptor : public CallInterfaceDescriptor {
ArrayNArgumentsConstructorDescriptor, CallInterfaceDescriptor)
};
class CompareDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kLeft, kRight)
......@@ -752,6 +752,15 @@ class StringCompareDescriptor : public CallInterfaceDescriptor {
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 {
public:
DEFINE_PARAMETERS(kString, kFrom, kTo)
......
This diff is collapsed.
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return t1; }
const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return a0; }
const Register ApiGetterDescriptor::HolderRegister() { return a0; }
const Register ApiGetterDescriptor::CallbackRegister() { return a3; }
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return a5; }
const Register StringCompareDescriptor::LeftRegister() { return a1; }
const Register StringCompareDescriptor::RightRegister() { return a0; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return a0; }
const Register ApiGetterDescriptor::HolderRegister() { return a0; }
const Register ApiGetterDescriptor::CallbackRegister() { return a3; }
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r8; }
const Register StringCompareDescriptor::LeftRegister() { return r4; }
const Register StringCompareDescriptor::RightRegister() { return r3; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r3; }
const Register ApiGetterDescriptor::HolderRegister() { return r3; }
const Register ApiGetterDescriptor::CallbackRegister() { return r6; }
......
......@@ -35,23 +35,6 @@ RUNTIME_FUNCTION(Runtime_InterpreterNewClosure) {
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
namespace {
......
......@@ -108,7 +108,6 @@ MaybeHandle<String> StringReplaceOneCharWithString(
}
}
RUNTIME_FUNCTION(Runtime_StringReplaceOneCharWithString) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
......@@ -197,7 +196,6 @@ RUNTIME_FUNCTION(Runtime_SubString) {
return *isolate->factory()->NewSubString(string, start, end);
}
RUNTIME_FUNCTION(Runtime_StringAdd) {
HandleScope scope(isolate);
DCHECK_EQ(2, args.length());
......@@ -208,6 +206,22 @@ RUNTIME_FUNCTION(Runtime_StringAdd) {
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) {
HandleScope handles(isolate);
......@@ -216,7 +230,6 @@ RUNTIME_FUNCTION(Runtime_InternalizeString) {
return *isolate->factory()->InternalizeString(string);
}
RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
......@@ -236,7 +249,6 @@ RUNTIME_FUNCTION(Runtime_StringCharCodeAtRT) {
return Smi::FromInt(subject->Get(i));
}
RUNTIME_FUNCTION(Runtime_StringCompare) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
......@@ -256,7 +268,6 @@ RUNTIME_FUNCTION(Runtime_StringCompare) {
UNREACHABLE();
}
RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
......@@ -329,7 +340,6 @@ RUNTIME_FUNCTION(Runtime_StringBuilderConcat) {
}
}
RUNTIME_FUNCTION(Runtime_StringBuilderJoin) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
......@@ -470,7 +480,6 @@ static void JoinSparseArrayWithSeparator(FixedArray* elements,
DCHECK(cursor <= buffer.length());
}
RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
HandleScope scope(isolate);
DCHECK_EQ(3, args.length());
......@@ -555,7 +564,6 @@ RUNTIME_FUNCTION(Runtime_SparseJoinWithSeparator) {
}
}
// Copies Latin1 characters to the given fixed array looking up
// 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
......@@ -586,7 +594,6 @@ static int CopyCachedOneByteCharsToArray(Heap* heap, const uint8_t* chars,
return i;
}
// Converts a String to JSArray.
// For example, "foo" => ["f", "o", "o"].
RUNTIME_FUNCTION(Runtime_StringToArray) {
......@@ -634,7 +641,6 @@ RUNTIME_FUNCTION(Runtime_StringToArray) {
return *isolate->factory()->NewJSArrayWithElements(elements);
}
RUNTIME_FUNCTION(Runtime_StringLessThan) {
HandleScope handle_scope(isolate);
DCHECK_EQ(2, args.length());
......@@ -726,7 +732,6 @@ RUNTIME_FUNCTION(Runtime_FlattenString) {
return *String::Flatten(str);
}
RUNTIME_FUNCTION(Runtime_StringCharFromCode) {
HandleScope handlescope(isolate);
DCHECK_EQ(1, args.length());
......
......@@ -229,7 +229,6 @@ namespace internal {
#define FOR_EACH_INTRINSIC_INTERPRETER(F) \
FOR_EACH_INTRINSIC_INTERPRETER_TRACE(F) \
F(InterpreterNewClosure, 4, 1) \
F(InterpreterStringConcat, -1 /* >= 2 */, 1) \
F(InterpreterAdvanceBytecodeOffset, 2, 1)
#define FOR_EACH_INTRINSIC_FUNCTION(F) \
......@@ -542,6 +541,7 @@ namespace internal {
F(StringLastIndexOf, 2, 1) \
F(SubString, 3, 1) \
F(StringAdd, 2, 1) \
F(StringConcat, -1 /* >= 2 */, 1) \
F(InternalizeString, 1, 1) \
F(StringCharCodeAtRT, 2, 1) \
F(StringCompare, 2, 1) \
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r7; }
const Register StringCompareDescriptor::LeftRegister() { return r3; }
const Register StringCompareDescriptor::RightRegister() { return r2; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return r2; }
const Register ApiGetterDescriptor::HolderRegister() { return r2; }
const Register ApiGetterDescriptor::CallbackRegister() { return r5; }
......
......@@ -186,6 +186,7 @@
'builtins/builtins-constructor-gen.h',
'builtins/builtins-constructor.h',
'builtins/builtins-conversion-gen.cc',
'builtins/builtins-conversion-gen.h',
'builtins/builtins-date-gen.cc',
'builtins/builtins-debug-gen.cc',
'builtins/builtins-forin-gen.cc',
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return r11; }
const Register StringCompareDescriptor::LeftRegister() { return rdx; }
const Register StringCompareDescriptor::RightRegister() { return rax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return rbx; }
const Register ApiGetterDescriptor::HolderRegister() { return rcx; }
const Register ApiGetterDescriptor::CallbackRegister() { return rbx; }
......
......@@ -47,6 +47,8 @@ const Register StoreTransitionDescriptor::MapRegister() { return edi; }
const Register StringCompareDescriptor::LeftRegister() { return edx; }
const Register StringCompareDescriptor::RightRegister() { return eax; }
const Register StringConcatDescriptor::ArgumentsCountRegister() { return eax; }
const Register ApiGetterDescriptor::HolderRegister() { return ecx; }
const Register ApiGetterDescriptor::CallbackRegister() { return eax; }
......
......@@ -42,6 +42,7 @@ const SharedOperator kSharedOperators[] = {
}
SHARED(ToNumber, 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(ToObject, Operator::kFoldable, 1, 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) {
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
TEST_F(JSTypedLoweringTest, JSStrictEqualWithTheHole) {
Node* const the_hole = HeapConstant(factory()->the_hole_value());
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