Add initial hydrogenized NewStringAddStub.

The new stub is enabled via the --new-string-add flag, which is
disabled by default. For now, it's only a stripped down version
of the native StringAddStub, it's still work-in-progress.

BUG=v8:2990
R=mvstanton@chromium.org

Review URL: https://codereview.chromium.org/61893009

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@17635 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
bmeurer@chromium.org 2013-11-12 10:21:08 +00:00
parent 51d637a073
commit 6f75e92902
21 changed files with 814 additions and 31 deletions

View File

@ -302,6 +302,17 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
}
void NewStringAddStub::InitializeInterfaceDescriptor(
Isolate* isolate,
CodeStubInterfaceDescriptor* descriptor) {
static Register registers[] = { r1, r0 };
descriptor->register_param_count_ = 2;
descriptor->register_params_ = registers;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kStringAdd)->entry;
}
#define __ ACCESS_MASM(masm)

View File

@ -3705,11 +3705,21 @@ void FullCodeGenerator::EmitStringCharAt(CallRuntime* expr) {
void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
if (FLAG_new_string_add) {
VisitForStackValue(args->at(0));
VisitForAccumulatorValue(args->at(1));
__ pop(r1);
NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
__ CallStub(&stub);
} else {
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
}
context()->Plug(r0);
}

View File

@ -2413,8 +2413,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), cp);
LOperand* left = UseRegisterAtStart(instr->left());
LOperand* right = UseRegisterAtStart(instr->right());
LOperand* left = FLAG_new_string_add
? UseFixed(instr->left(), r1)
: UseRegisterAtStart(instr->left());
LOperand* right = FLAG_new_string_add
? UseFixed(instr->right(), r0)
: UseRegisterAtStart(instr->right());
return MarkAsCall(
DefineFixed(new(zone()) LStringAdd(context, left, right), r0),
instr);

View File

@ -4562,10 +4562,18 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(cp));
__ push(ToRegister(instr->left()));
__ push(ToRegister(instr->right()));
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
if (FLAG_new_string_add) {
ASSERT(ToRegister(instr->left()).is(r1));
ASSERT(ToRegister(instr->right()).is(r0));
NewStringAddStub stub(instr->hydrogen()->flags(),
isolate()->heap()->GetPretenureMode());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
} else {
__ push(ToRegister(instr->left()));
__ push(ToRegister(instr->right()));
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
}
}

View File

@ -971,6 +971,38 @@ Handle<Code> BinaryOpStub::GenerateCode(Isolate* isolate) {
}
template <>
HValue* CodeStubGraphBuilder<NewStringAddStub>::BuildCodeInitializedStub() {
NewStringAddStub* stub = casted_stub();
StringAddFlags flags = stub->flags();
PretenureFlag pretenure_flag = stub->pretenure_flag();
HValue* left = GetParameter(NewStringAddStub::kLeft);
HValue* right = GetParameter(NewStringAddStub::kRight);
// Make sure that both arguments are strings if not known in advance.
if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
IfBuilder if_leftnotstring(this);
if_leftnotstring.IfNot<HIsStringAndBranch>(left);
if_leftnotstring.Then();
if_leftnotstring.Deopt("Expected string for LHS of string addition");
}
if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
IfBuilder if_rightnotstring(this);
if_rightnotstring.IfNot<HIsStringAndBranch>(right);
if_rightnotstring.Then();
if_rightnotstring.Deopt("Expected string for RHS of string addition");
}
return BuildStringAdd(left, right, pretenure_flag);
}
Handle<Code> NewStringAddStub::GenerateCode(Isolate* isolate) {
return DoGenerateCode(isolate, this);
}
template <>
HValue* CodeStubGraphBuilder<ToBooleanStub>::BuildCodeInitializedStub() {
ToBooleanStub* stub = casted_stub();

View File

@ -682,6 +682,21 @@ Handle<Type> BinaryOpStub::GetResultType(Isolate* isolate) const {
}
void NewStringAddStub::PrintBaseName(StringStream* stream) {
stream->Add("NewStringAddStub");
if ((flags() & STRING_ADD_CHECK_BOTH) == STRING_ADD_CHECK_BOTH) {
stream->Add("_CheckBoth");
} else if ((flags() & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
stream->Add("_CheckLeft");
} else if ((flags() & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
stream->Add("_CheckRight");
}
if (pretenure_flag() == TENURED) {
stream->Add("_Tenured");
}
}
InlineCacheState ICCompareStub::GetICState() {
CompareIC::State state = Max(left_, right_);
switch (state) {
@ -1134,6 +1149,13 @@ void FastNewClosureStub::InstallDescriptors(Isolate* isolate) {
}
// static
void NewStringAddStub::InstallDescriptors(Isolate* isolate) {
NewStringAddStub stub(STRING_ADD_CHECK_NONE, NOT_TENURED);
InstallDescriptor(isolate, &stub);
}
ArrayConstructorStub::ArrayConstructorStub(Isolate* isolate)
: argument_count_(ANY) {
ArrayConstructorStubBase::GenerateStubsAheadOfTime(isolate);

View File

@ -43,6 +43,7 @@ namespace internal {
V(CallConstruct) \
V(BinaryOp) \
V(StringAdd) \
V(NewStringAdd) \
V(SubString) \
V(StringCompare) \
V(Compare) \
@ -1179,6 +1180,47 @@ class BinaryOpStub: public HydrogenCodeStub {
};
// TODO(bmeurer): Rename to StringAddStub once we dropped the old StringAddStub.
class NewStringAddStub V8_FINAL : public HydrogenCodeStub {
public:
NewStringAddStub(StringAddFlags flags, PretenureFlag pretenure_flag)
: bit_field_(StringAddFlagsBits::encode(flags) |
PretenureFlagBits::encode(pretenure_flag)) {}
StringAddFlags flags() const {
return StringAddFlagsBits::decode(bit_field_);
}
PretenureFlag pretenure_flag() const {
return PretenureFlagBits::decode(bit_field_);
}
virtual Handle<Code> GenerateCode(Isolate* isolate) V8_OVERRIDE;
virtual void InitializeInterfaceDescriptor(
Isolate* isolate,
CodeStubInterfaceDescriptor* descriptor) V8_OVERRIDE;
static void InstallDescriptors(Isolate* isolate);
// Parameters accessed via CodeStubGraphBuilder::GetParameter()
static const int kLeft = 0;
static const int kRight = 1;
private:
class StringAddFlagsBits: public BitField<StringAddFlags, 0, 2> {};
class PretenureFlagBits: public BitField<PretenureFlag, 2, 1> {};
uint32_t bit_field_;
virtual Major MajorKey() V8_OVERRIDE { return NewStringAdd; }
virtual int NotMissMinorKey() V8_OVERRIDE { return bit_field_; }
virtual void PrintBaseName(StringStream* stream) V8_OVERRIDE;
DISALLOW_COPY_AND_ASSIGN(NewStringAddStub);
};
class ICCompareStub: public PlatformCodeStub {
public:
ICCompareStub(Token::Value op,

View File

@ -338,6 +338,8 @@ DEFINE_bool(omit_map_checks_for_leaf_maps, true,
"do not emit check maps for constant values that have a leaf map, "
"deoptimize the optimized code if the layout of the maps changes.")
DEFINE_bool(new_string_add, false, "enable new string addition")
// Experimental profiler changes.
DEFINE_bool(experimental_profiler, true, "enable all profiler experiments")
DEFINE_bool(watch_ic_patching, false, "profiler considers IC stability")

View File

@ -5808,6 +5808,12 @@ class HObjectAccess V8_FINAL {
FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
}
static HObjectAccess ForStringHashField() {
return HObjectAccess(kInobject,
String::kHashFieldOffset,
Representation::Integer32());
}
static HObjectAccess ForStringLength() {
STATIC_ASSERT(String::kMaxLength <= Smi::kMaxValue);
return HObjectAccess(
@ -5816,6 +5822,14 @@ class HObjectAccess V8_FINAL {
FLAG_track_fields ? Representation::Smi() : Representation::Tagged());
}
static HObjectAccess ForConsStringFirst() {
return HObjectAccess(kInobject, ConsString::kFirstOffset);
}
static HObjectAccess ForConsStringSecond() {
return HObjectAccess(kInobject, ConsString::kSecondOffset);
}
static HObjectAccess ForPropertiesPointer() {
return HObjectAccess(kInobject, JSObject::kPropertiesOffset);
}
@ -5863,6 +5877,12 @@ class HObjectAccess V8_FINAL {
Representation::UInteger8());
}
static HObjectAccess ForMapInstanceType() {
return HObjectAccess(kInobject,
Map::kInstanceTypeOffset,
Representation::UInteger8());
}
static HObjectAccess ForPropertyCellValue() {
return HObjectAccess(kInobject, PropertyCell::kValueOffset);
}

View File

@ -1469,6 +1469,342 @@ HValue* HGraphBuilder::BuildNumberToString(HValue* object,
}
HValue* HGraphBuilder::BuildSeqStringSizeFor(HValue* length,
String::Encoding encoding) {
STATIC_ASSERT((SeqString::kHeaderSize & kObjectAlignmentMask) == 0);
HValue* size = length;
if (encoding == String::TWO_BYTE_ENCODING) {
size = Add<HShl>(length, graph()->GetConstant1());
size->ClearFlag(HValue::kCanOverflow);
size->SetFlag(HValue::kUint32);
}
size = Add<HAdd>(size, Add<HConstant>(static_cast<int32_t>(
SeqString::kHeaderSize + kObjectAlignmentMask)));
size->ClearFlag(HValue::kCanOverflow);
size = Add<HBitwise>(
Token::BIT_AND, size, Add<HConstant>(static_cast<int32_t>(
~kObjectAlignmentMask)));
return size;
}
void HGraphBuilder::BuildCopySeqStringChars(HValue* src,
HValue* src_offset,
String::Encoding src_encoding,
HValue* dst,
HValue* dst_offset,
String::Encoding dst_encoding,
HValue* length) {
ASSERT(dst_encoding != String::ONE_BYTE_ENCODING ||
src_encoding == String::ONE_BYTE_ENCODING);
LoopBuilder loop(this, context(), LoopBuilder::kPostIncrement);
HValue* index = loop.BeginBody(graph()->GetConstant0(), length, Token::LT);
{
HValue* src_index = Add<HAdd>(src_offset, index);
HValue* value = Add<HSeqStringGetChar>(src_encoding, src, src_index);
HValue* dst_index = Add<HAdd>(dst_offset, index);
Add<HSeqStringSetChar>(dst_encoding, dst, dst_index, value);
}
loop.EndBody();
}
HValue* HGraphBuilder::BuildUncheckedStringAdd(HValue* left,
HValue* right,
PretenureFlag pretenure_flag) {
// Determine the string lengths.
HValue* left_length = Add<HLoadNamedField>(
left, HObjectAccess::ForStringLength());
HValue* right_length = Add<HLoadNamedField>(
right, HObjectAccess::ForStringLength());
// Check if we concatenated the strings here, or if we have to resort to the
// runtime function.
HIfContinuation handled(graph()->CreateBasicBlock(),
graph()->CreateBasicBlock());
// Check if both parameters do not exceed half the max string length, because
// exceptionally long strings should be handled in the runtime. Unfortunately
// we cannot actually check whether the combined length of both strings
// exceeds String::kMaxLength (because of unclear results from the
// representation inference phase), so we use a pessimistic approach here
// instead, checking that the length of either substring does not exceed half
// of String::kMaxLength.
HConstant* max_length = Add<HConstant>(String::kMaxLength / 2);
IfBuilder if_nooverflow(this);
if_nooverflow.If<HCompareNumericAndBranch>(
left_length, max_length, Token::LTE);
if_nooverflow.AndIf<HCompareNumericAndBranch>(
right_length, max_length, Token::LTE);
if_nooverflow.Then();
{
// Determine the string instance types.
HLoadNamedField* left_instance_type = Add<HLoadNamedField>(
Add<HLoadNamedField>(left, HObjectAccess::ForMap()),
HObjectAccess::ForMapInstanceType());
HLoadNamedField* right_instance_type = Add<HLoadNamedField>(
Add<HLoadNamedField>(right, HObjectAccess::ForMap()),
HObjectAccess::ForMapInstanceType());
// Compute difference of instance types.
HValue* xored_instance_types = Add<HBitwise>(
Token::BIT_XOR, left_instance_type, right_instance_type);
// Compute the length of the resulting string.
HValue* length = Add<HAdd>(left_length, right_length);
// Check if we should create a cons string.
IfBuilder if_createcons(this);
if_createcons.If<HCompareNumericAndBranch>(
length, Add<HConstant>(ConsString::kMinLength), Token::GTE);
if_createcons.Then();
{
// Allocate the cons string object. HAllocate does not care whether we
// pass CONS_STRING_TYPE or CONS_ASCII_STRING_TYPE here, so we just use
// CONS_STRING_TYPE here. Below we decide whether the cons string is
// one-byte or two-byte and set the appropriate map.
HAllocate* string = Add<HAllocate>(Add<HConstant>(ConsString::kSize),
HType::String(), pretenure_flag,
CONS_STRING_TYPE);
// Compute the intersection of instance types.
HValue* anded_instance_types = Add<HBitwise>(
Token::BIT_AND, left_instance_type, right_instance_type);
// We create a one-byte cons string if
// 1. both strings are one-byte, or
// 2. at least one of the strings is two-byte, but happens to contain only
// one-byte characters.
// To do this, we check
// 1. if both strings are one-byte, or if the one-byte data hint is set in
// both strings, or
// 2. if one of the strings has the one-byte data hint set and the other
// string is one-byte.
IfBuilder if_onebyte(this);
STATIC_ASSERT(kOneByteStringTag != 0);
STATIC_ASSERT(kOneByteDataHintMask != 0);
if_onebyte.If<HCompareNumericAndBranch>(
Add<HBitwise>(
Token::BIT_AND, anded_instance_types,
Add<HConstant>(static_cast<int32_t>(
kStringEncodingMask | kOneByteDataHintMask))),
graph()->GetConstant0(), Token::NE);
if_onebyte.Or();
STATIC_ASSERT(kOneByteStringTag != 0 &&
kOneByteDataHintTag != 0 &&
kOneByteDataHintTag != kOneByteStringTag);
if_onebyte.If<HCompareNumericAndBranch>(
Add<HBitwise>(
Token::BIT_AND, xored_instance_types,
Add<HConstant>(static_cast<int32_t>(
kOneByteStringTag | kOneByteDataHintTag))),
Add<HConstant>(static_cast<int32_t>(
kOneByteStringTag | kOneByteDataHintTag)), Token::EQ);
if_onebyte.Then();
{
// We can safely skip the write barrier for storing the map here.
Handle<Map> map = isolate()->factory()->cons_ascii_string_map();
AddStoreMapConstantNoWriteBarrier(string, map);
}
if_onebyte.Else();
{
// We can safely skip the write barrier for storing the map here.
Handle<Map> map = isolate()->factory()->cons_string_map();
AddStoreMapConstantNoWriteBarrier(string, map);
}
if_onebyte.End();
// Initialize the cons string fields.
Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
Add<HConstant>(String::kEmptyHashField));
Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(), length);
Add<HStoreNamedField>(string, HObjectAccess::ForConsStringFirst(), left);
Add<HStoreNamedField>(string, HObjectAccess::ForConsStringSecond(),
right);
// Cons string is result.
Push(string);
}
if_createcons.Else();
{
// Compute union of instance types.
HValue* ored_instance_types = Add<HBitwise>(
Token::BIT_OR, left_instance_type, right_instance_type);
// Check if both strings have the same encoding and both are
// sequential.
IfBuilder if_sameencodingandsequential(this);
if_sameencodingandsequential.If<HCompareNumericAndBranch>(
Add<HBitwise>(
Token::BIT_AND, xored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
graph()->GetConstant0(), Token::EQ);
if_sameencodingandsequential.And();
STATIC_ASSERT(kSeqStringTag == 0);
if_sameencodingandsequential.If<HCompareNumericAndBranch>(
Add<HBitwise>(
Token::BIT_AND, ored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringRepresentationMask))),
graph()->GetConstant0(), Token::EQ);
if_sameencodingandsequential.Then();
{
// Check if the result is a one-byte string.
IfBuilder if_onebyte(this);
STATIC_ASSERT(kOneByteStringTag != 0);
if_onebyte.If<HCompareNumericAndBranch>(
Add<HBitwise>(
Token::BIT_AND, ored_instance_types,
Add<HConstant>(static_cast<int32_t>(kStringEncodingMask))),
graph()->GetConstant0(), Token::NE);
if_onebyte.Then();
{
// Calculate the number of bytes needed for the characters in the
// string while observing object alignment.
HValue* size = BuildSeqStringSizeFor(
length, String::ONE_BYTE_ENCODING);
// Allocate the ASCII string object.
Handle<Map> map = isolate()->factory()->ascii_string_map();
HAllocate* string = Add<HAllocate>(size, HType::String(),
pretenure_flag, ASCII_STRING_TYPE);
string->set_known_initial_map(map);
// We can safely skip the write barrier for storing map here.
AddStoreMapConstantNoWriteBarrier(string, map);
// Copy bytes from the left string.
BuildCopySeqStringChars(
left, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
string, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
left_length);
// Copy bytes from the right string.
BuildCopySeqStringChars(
right, graph()->GetConstant0(), String::ONE_BYTE_ENCODING,
string, left_length, String::ONE_BYTE_ENCODING,
right_length);
// Return the string.
Push(string);
}
if_onebyte.Else();
{
// Calculate the number of bytes needed for the characters in the
// string while observing object alignment.
HValue* size = BuildSeqStringSizeFor(
length, String::TWO_BYTE_ENCODING);
// Allocate the two-byte string object.
Handle<Map> map = isolate()->factory()->string_map();
HAllocate* string = Add<HAllocate>(size, HType::String(),
pretenure_flag, STRING_TYPE);
string->set_known_initial_map(map);
// We can safely skip the write barrier for storing map here.
AddStoreMapConstantNoWriteBarrier(string, map);
// Copy bytes from the left string.
BuildCopySeqStringChars(
left, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
string, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
left_length);
// Copy bytes from the right string.
BuildCopySeqStringChars(
right, graph()->GetConstant0(), String::TWO_BYTE_ENCODING,
string, left_length, String::TWO_BYTE_ENCODING,
right_length);
// Return the string.
Push(string);
}
if_onebyte.End();
// Initialize the (common) string fields.
HValue* string = Pop();
Add<HStoreNamedField>(string, HObjectAccess::ForStringHashField(),
Add<HConstant>(String::kEmptyHashField));
Add<HStoreNamedField>(string, HObjectAccess::ForStringLength(),
length);
Push(string);
}
if_sameencodingandsequential.JoinContinuation(&handled);
}
if_createcons.JoinContinuation(&handled);
}
if_nooverflow.JoinContinuation(&handled);
// Check if the strings were concatenated successfully, otherwise fallback to
// add the strings in the runtime.
IfBuilder if_handled(this, &handled);
if_handled.Then();
{
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
}
if_handled.Else();
{
// Fallback to the runtime to add the two strings.
Add<HPushArgument>(left);
Add<HPushArgument>(right);
Push(Add<HCallRuntime>(isolate()->factory()->empty_string(),
Runtime::FunctionForId(Runtime::kStringAdd),
2));
}
if_handled.End();
return Pop();
}
HValue* HGraphBuilder::BuildStringAdd(HValue* left,
HValue* right,
PretenureFlag pretenure_flag) {
// Determine the string lengths.
HValue* left_length = Add<HLoadNamedField>(
left, HObjectAccess::ForStringLength());
HValue* right_length = Add<HLoadNamedField>(
right, HObjectAccess::ForStringLength());
// Check if left string is empty.
IfBuilder if_leftisempty(this);
if_leftisempty.If<HCompareNumericAndBranch>(
left_length, graph()->GetConstant0(), Token::EQ);
if_leftisempty.Then();
{
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
// Just return the right string.
Push(right);
}
if_leftisempty.Else();
{
// Check if right string is empty.
IfBuilder if_rightisempty(this);
if_rightisempty.If<HCompareNumericAndBranch>(
right_length, graph()->GetConstant0(), Token::EQ);
if_rightisempty.Then();
{
// Count the native string addition.
AddIncrementCounter(isolate()->counters()->string_add_native());
// Just return the left string.
Push(left);
}
if_rightisempty.Else();
{
// Concatenate the two non-empty strings.
Push(BuildUncheckedStringAdd(left, right, pretenure_flag));
}
if_rightisempty.End();
}
if_leftisempty.End();
return Pop();
}
HInstruction* HGraphBuilder::BuildUncheckedMonomorphicElementAccess(
HValue* checked_object,
HValue* key,

View File

@ -1276,6 +1276,26 @@ class HGraphBuilder {
HValue* BuildNumberToString(HValue* object, Handle<Type> type);
// Computes the size for a sequential string of the given length and encoding.
HValue* BuildSeqStringSizeFor(HValue* length,
String::Encoding encoding);
// Copies characters from one sequential string to another.
void BuildCopySeqStringChars(HValue* src,
HValue* src_offset,
String::Encoding src_encoding,
HValue* dst,
HValue* dst_offset,
String::Encoding dst_encoding,
HValue* length);
// Both operands are non-empty strings.
HValue* BuildUncheckedStringAdd(HValue* left,
HValue* right,
PretenureFlag pretenure_flag);
// Both operands are strings.
HValue* BuildStringAdd(HValue* left,
HValue* right,
PretenureFlag pretenure_flag);
HInstruction* BuildUncheckedMonomorphicElementAccess(
HValue* checked_object,
HValue* key,
@ -1298,7 +1318,13 @@ class HGraphBuilder {
HLoadNamedField* BuildLoadNamedField(HValue* object, HObjectAccess access);
HInstruction* AddLoadNamedField(HValue* object, HObjectAccess access);
HInstruction* BuildLoadStringLength(HValue* object, HValue* checked_value);
HStoreNamedField* AddStoreMapConstant(HValue* object, Handle<Map>);
HStoreNamedField* AddStoreMapConstant(HValue* object, Handle<Map> map);
HStoreNamedField* AddStoreMapConstantNoWriteBarrier(HValue* object,
Handle<Map> map) {
HStoreNamedField* store_map = AddStoreMapConstant(object, map);
store_map->SkipWriteBarrier();
return store_map;
}
HLoadNamedField* AddLoadElements(HValue* object);
bool MatchRotateRight(HValue* left,

View File

@ -306,6 +306,17 @@ void BinaryOpStub::InitializeInterfaceDescriptor(
}
void NewStringAddStub::InitializeInterfaceDescriptor(
Isolate* isolate,
CodeStubInterfaceDescriptor* descriptor) {
static Register registers[] = { edx, eax };
descriptor->register_param_count_ = 2;
descriptor->register_params_ = registers;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kStringAdd)->entry;
}
#define __ ACCESS_MASM(masm)

View File

@ -3671,11 +3671,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
if (FLAG_new_string_add) {
VisitForStackValue(args->at(0));
VisitForAccumulatorValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
__ pop(edx);
NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
__ CallStub(&stub);
} else {
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
}
context()->Plug(eax);
}

View File

@ -4551,6 +4551,10 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
if (operand_value->IsRegister()) {
Register value = ToRegister(operand_value);
__ Store(value, operand, representation);
} else if (representation.IsInteger32()) {
Immediate immediate = ToImmediate(operand_value, representation);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
__ mov(operand, immediate);
} else {
Handle<Object> handle_value = ToHandle(operand_value);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
@ -4997,10 +5001,18 @@ void LCodeGen::DoDeferredStringCharFromCode(LStringCharFromCode* instr) {
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(esi));
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
if (FLAG_new_string_add) {
ASSERT(ToRegister(instr->left()).is(edx));
ASSERT(ToRegister(instr->right()).is(eax));
NewStringAddStub stub(instr->hydrogen()->flags(),
isolate()->heap()->GetPretenureMode());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
} else {
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
}
}

View File

@ -2485,8 +2485,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), esi);
LOperand* left = UseOrConstantAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
LOperand* left = FLAG_new_string_add
? UseFixed(instr->left(), edx)
: UseOrConstantAtStart(instr->left());
LOperand* right = FLAG_new_string_add
? UseFixed(instr->right(), eax)
: UseOrConstantAtStart(instr->right());
LStringAdd* string_add = new(zone()) LStringAdd(context, left, right);
return MarkAsCall(DefineFixed(string_add, eax), instr);
}

View File

@ -2328,6 +2328,7 @@ bool Isolate::Init(Deserializer* des) {
InternalArrayConstructorStubBase::InstallDescriptors(this);
FastNewClosureStub::InstallDescriptors(this);
NumberToStringStub::InstallDescriptors(this);
NewStringAddStub::InstallDescriptors(this);
}
if (FLAG_sweeper_threads > 0) {

View File

@ -302,6 +302,17 @@ void ElementsTransitionAndStoreStub::InitializeInterfaceDescriptor(
}
void NewStringAddStub::InitializeInterfaceDescriptor(
Isolate* isolate,
CodeStubInterfaceDescriptor* descriptor) {
static Register registers[] = { rdx, rax };
descriptor->register_param_count_ = 2;
descriptor->register_params_ = registers;
descriptor->deoptimization_handler_ =
Runtime::FunctionForId(Runtime::kStringAdd)->entry;
}
#define __ ACCESS_MASM(masm)

View File

@ -3628,11 +3628,20 @@ void FullCodeGenerator::EmitStringAdd(CallRuntime* expr) {
ZoneList<Expression*>* args = expr->arguments();
ASSERT_EQ(2, args->length());
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
if (FLAG_new_string_add) {
VisitForStackValue(args->at(0));
VisitForAccumulatorValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
__ pop(rdx);
NewStringAddStub stub(STRING_ADD_CHECK_BOTH, NOT_TENURED);
__ CallStub(&stub);
} else {
VisitForStackValue(args->at(0));
VisitForStackValue(args->at(1));
StringAddStub stub(STRING_ADD_CHECK_BOTH);
__ CallStub(&stub);
}
context()->Plug(rax);
}

View File

@ -4077,6 +4077,10 @@ void LCodeGen::DoStoreNamedField(LStoreNamedField* instr) {
if (operand_value->IsRegister()) {
Register value = ToRegister(operand_value);
__ Store(FieldOperand(write_register, offset), value, representation);
} else if (representation.IsInteger32()) {
int32_t value = ToInteger32(operand_value);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
__ movl(FieldOperand(write_register, offset), Immediate(value));
} else {
Handle<Object> handle_value = ToHandle(operand_value);
ASSERT(!instr->hydrogen()->NeedsWriteBarrier());
@ -4400,10 +4404,18 @@ void LCodeGen::DoTrapAllocationMemento(LTrapAllocationMemento* instr) {
void LCodeGen::DoStringAdd(LStringAdd* instr) {
ASSERT(ToRegister(instr->context()).is(rsi));
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
if (FLAG_new_string_add) {
ASSERT(ToRegister(instr->left()).is(rdx));
ASSERT(ToRegister(instr->right()).is(rax));
NewStringAddStub stub(instr->hydrogen()->flags(),
isolate()->heap()->GetPretenureMode());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
} else {
EmitPushTaggedOperand(instr->left());
EmitPushTaggedOperand(instr->right());
StringAddStub stub(instr->hydrogen()->flags());
CallCode(stub.GetCode(isolate()), RelocInfo::CODE_TARGET, instr);
}
}

View File

@ -2339,8 +2339,12 @@ LInstruction* LChunkBuilder::DoStoreNamedGeneric(HStoreNamedGeneric* instr) {
LInstruction* LChunkBuilder::DoStringAdd(HStringAdd* instr) {
LOperand* context = UseFixed(instr->context(), rsi);
LOperand* left = UseOrConstantAtStart(instr->left());
LOperand* right = UseOrConstantAtStart(instr->right());
LOperand* left = FLAG_new_string_add
? UseFixed(instr->left(), rdx)
: UseOrConstantAtStart(instr->left());
LOperand* right = FLAG_new_string_add
? UseFixed(instr->right(), rax)
: UseOrConstantAtStart(instr->right());
return MarkAsCall(
DefineFixed(new(zone()) LStringAdd(context, left, right), rax), instr);
}

View File

@ -0,0 +1,197 @@
// Copyright 2013 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following
// disclaimer in the documentation and/or other materials provided
// with the distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived
// from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Flags: --new-string-add
assertEquals("ab", "a" + "b", "ll");
assertEquals("12", "1" + "2", "dd");
assertEquals("123", "1" + "2" + "3", "ddd");
assertEquals("123", 1 + "2" + "3", "ndd");
assertEquals("123", "1" + 2 + "3", "dnd");
assertEquals("123", "1" + "2" + 3, "ddn");
assertEquals("123", "1" + 2 + 3, "dnn");
assertEquals("123", 1 + "2" + 3, "ndn");
assertEquals("33", 1 + 2 + "3", "nnd");
var x = "1";
assertEquals("12", x + 2, "vn");
assertEquals("12", x + "2", "vd");
assertEquals("21", 2 + x, "nv");
assertEquals("21", "2" + x, "dv");
var y = "2";
assertEquals("12", x + y, "vdvd");
x = 1;
assertEquals("12", x + y, "vnvd");
y = 2;
assertEquals(3, x + y, "vnvn");
x = "1";
assertEquals("12", x + y, "vdvn");
y = "2";
assertEquals("12", x + y, "vdvd2");
(function(x, y) {
var z = "3";
var w = "4";
assertEquals("11", x + x, "xx");
assertEquals("12", x + y, "xy");
assertEquals("13", x + z, "xz");
assertEquals("14", x + w, "xw");
assertEquals("21", y + x, "yx");
assertEquals("22", y + y, "yy");
assertEquals("23", y + z, "yz");
assertEquals("24", y + w, "yw");
assertEquals("31", z + x, "zx");
assertEquals("32", z + y, "zy");
assertEquals("33", z + z, "zz");
assertEquals("34", z + w, "zw");
assertEquals("41", w + x, "wx");
assertEquals("42", w + y, "wy");
assertEquals("43", w + z, "wz");
assertEquals("44", w + w, "ww");
(function(){x = 1; z = 3;})();
assertEquals(2, x + x, "x'x");
assertEquals("12", x + y, "x'y");
assertEquals(4, x + z, "x'z'");
assertEquals("14", x + w, "x'w");
assertEquals("21", y + x, "yx'");
assertEquals("22", y + y, "yy");
assertEquals("23", y + z, "yz'");
assertEquals("24", y + w, "yw");
assertEquals(4, z + x, "z'x'");
assertEquals("32", z + y, "z'y");
assertEquals(6, z + z, "z'z'");
assertEquals("34", z + w, "z'w");
assertEquals("41", w + x, "wx'");
assertEquals("42", w + y, "wy");
assertEquals("43", w + z, "wz'");
assertEquals("44", w + w, "ww");
})("1", "2");
assertEquals("142", "1" + new Number(42), "sN");
assertEquals("421", new Number(42) + "1", "Ns");
assertEquals(84, new Number(42) + new Number(42), "NN");
assertEquals("142", "1" + new String("42"), "sS");
assertEquals("421", new String("42") + "1", "Ss");
assertEquals("142", "1" + new String("42"), "sS");
assertEquals("4242", new String("42") + new String("42"), "SS");
assertEquals("1true", "1" + true, "sb");
assertEquals("true1", true + "1", "bs");
assertEquals(2, true + true, "bs");
assertEquals("1true", "1" + new Boolean(true), "sB");
assertEquals("true1", new Boolean(true) + "1", "Bs");
assertEquals(2, new Boolean(true) + new Boolean(true), "Bs");
assertEquals("1undefined", "1" + void 0, "sv");
assertEquals("undefined1", (void 0) + "1", "vs");
assertTrue(isNaN(void 0 + void 0), "vv");
assertEquals("1null", "1" + null, "su");
assertEquals("null1", null + "1", "us");
assertEquals(0, null + null, "uu");
(function (i) {
// Check that incoming frames are merged correctly.
var x;
var y;
var z;
var w;
switch (i) {
case 1: x = 42; y = "stry"; z = "strz"; w = 42; break;
default: x = "strx", y = 42; z = "strz"; w = 42; break;
}
var resxx = x + x;
var resxy = x + y;
var resxz = x + z;
var resxw = x + w;
var resyx = y + x;
var resyy = y + y;
var resyz = y + z;
var resyw = y + w;
var reszx = z + x;
var reszy = z + y;
var reszz = z + z;
var reszw = z + w;
var reswx = w + x;
var reswy = w + y;
var reswz = w + z;
var resww = w + w;
assertEquals(84, resxx, "swxx");
assertEquals("42stry", resxy, "swxy");
assertEquals("42strz", resxz, "swxz");
assertEquals(84, resxw, "swxw");
assertEquals("stry42", resyx, "swyx");
assertEquals("strystry", resyy, "swyy");
assertEquals("strystrz", resyz, "swyz");
assertEquals("stry42", resyw, "swyw");
assertEquals("strz42", reszx, "swzx");
assertEquals("strzstry", reszy, "swzy");
assertEquals("strzstrz", reszz, "swzz");
assertEquals("strz42", reszw, "swzw");
assertEquals(84, reswx, "swwx");
assertEquals("42stry", reswy, "swwy");
assertEquals("42strz", reswz, "swwz");
assertEquals(84, resww, "swww");
})(1);
// Generate ascii and non ascii strings from length 0 to 20.
var ascii = 'aaaaaaaaaaaaaaaaaaaa';
var non_ascii = '\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234\u1234';
assertEquals(20, ascii.length);
assertEquals(20, non_ascii.length);
var a = Array(21);
var b = Array(21);
for (var i = 0; i <= 20; i++) {
a[i] = ascii.substring(0, i);
b[i] = non_ascii.substring(0, i);
}
// Add ascii and non-ascii strings generating strings with length from 0 to 20.
for (var i = 0; i <= 20; i++) {
for (var j = 0; j < i; j++) {
assertEquals(a[i], a[j] + a[i - j])
assertEquals(b[i], b[j] + b[i - j])
}
}