HValue* shared_info,
HValue* native_context);
+ HValue* CheckString(HValue* input, bool convert);
+
private:
HValue* BuildArraySingleArgumentConstructor(JSArrayBuilder* builder);
HValue* BuildArrayNArgumentsConstructor(JSArrayBuilder* builder,
}
+HValue* CodeStubGraphBuilderBase::CheckString(HValue* input, bool convert) {
+ if (!convert) return BuildCheckString(input);
+ IfBuilder if_inputissmi(this);
+ HValue* inputissmi = if_inputissmi.If<HIsSmiAndBranch>(input);
+ if_inputissmi.Then();
+ {
+ // Convert the input smi to a string.
+ Push(BuildNumberToString(input, Type::SignedSmall()));
+ }
+ if_inputissmi.Else();
+ {
+ HValue* input_map =
+ Add<HLoadNamedField>(input, inputissmi, HObjectAccess::ForMap());
+ HValue* input_instance_type = Add<HLoadNamedField>(
+ input_map, inputissmi, HObjectAccess::ForMapInstanceType());
+ IfBuilder if_inputisstring(this);
+ if_inputisstring.If<HCompareNumericAndBranch>(
+ input_instance_type, Add<HConstant>(FIRST_NONSTRING_TYPE), Token::LT);
+ if_inputisstring.Then();
+ {
+ // The input is already a string.
+ Push(input);
+ }
+ if_inputisstring.Else();
+ {
+ // Convert to primitive first (if necessary), see
+ // ES6 section 12.7.3 The Addition operator.
+ IfBuilder if_inputisprimitive(this);
+ STATIC_ASSERT(FIRST_PRIMITIVE_TYPE == FIRST_TYPE);
+ if_inputisprimitive.If<HCompareNumericAndBranch>(
+ input_instance_type, Add<HConstant>(LAST_PRIMITIVE_TYPE), Token::LTE);
+ if_inputisprimitive.Then();
+ {
+ // The input is already a primitive.
+ Push(input);
+ }
+ if_inputisprimitive.Else();
+ {
+ // TODO(bmeurer): Add support for fast ToPrimitive conversion using
+ // a dedicated ToPrimitiveStub.
+ Add<HPushArguments>(input);
+ Push(Add<HCallRuntime>(Runtime::FunctionForId(Runtime::kToPrimitive),
+ 1));
+ }
+ if_inputisprimitive.End();
+ // Convert the primitive to a string value.
+ ToStringDescriptor descriptor(isolate());
+ ToStringStub stub(isolate());
+ HValue* values[] = {context(), Pop()};
+ Push(AddUncasted<HCallWithDescriptor>(
+ Add<HConstant>(stub.GetCode()), 0, descriptor,
+ Vector<HValue*>(values, arraysize(values))));
+ }
+ if_inputisstring.End();
+ }
+ if_inputissmi.End();
+ return Pop();
+}
+
+
template <>
HValue* CodeStubGraphBuilder<StringAddStub>::BuildCodeInitializedStub() {
StringAddStub* stub = casted_stub();
// Make sure that both arguments are strings if not known in advance.
if ((flags & STRING_ADD_CHECK_LEFT) == STRING_ADD_CHECK_LEFT) {
- left = BuildCheckString(left);
+ left =
+ CheckString(left, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
}
if ((flags & STRING_ADD_CHECK_RIGHT) == STRING_ADD_CHECK_RIGHT) {
- right = BuildCheckString(right);
+ right =
+ CheckString(right, (flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT);
}
return BuildStringAdd(left, right, HAllocationMode(pretenure_flag));
return os << "CheckRight";
case STRING_ADD_CHECK_BOTH:
return os << "CheckBoth";
+ case STRING_ADD_CONVERT_LEFT:
+ return os << "ConvertLeft";
+ case STRING_ADD_CONVERT_RIGHT:
+ return os << "ConvertRight";
+ case STRING_ADD_CONVERT:
+ break;
}
UNREACHABLE();
return os;
// Check right parameter.
STRING_ADD_CHECK_RIGHT = 1 << 1,
// Check both parameters.
- STRING_ADD_CHECK_BOTH = STRING_ADD_CHECK_LEFT | STRING_ADD_CHECK_RIGHT
+ STRING_ADD_CHECK_BOTH = STRING_ADD_CHECK_LEFT | STRING_ADD_CHECK_RIGHT,
+ // Convert parameters when check fails (instead of throwing an exception).
+ STRING_ADD_CONVERT = 1 << 2,
+ STRING_ADD_CONVERT_LEFT = STRING_ADD_CHECK_LEFT | STRING_ADD_CONVERT,
+ STRING_ADD_CONVERT_RIGHT = STRING_ADD_CHECK_RIGHT | STRING_ADD_CONVERT
};
}
private:
- class StringAddFlagsBits : public BitField<StringAddFlags, 0, 2> {};
- class PretenureFlagBits : public BitField<PretenureFlag, 2, 1> {};
+ class StringAddFlagsBits : public BitField<StringAddFlags, 0, 3> {};
+ class PretenureFlagBits : public BitField<PretenureFlag, 3, 1> {};
void PrintBaseName(std::ostream& os) const override; // NOLINT
static const int kRight = 1;
private:
- class StringAddFlagsBits: public BitField<StringAddFlags, 0, 2> {};
- class PretenureFlagBits: public BitField<PretenureFlag, 2, 1> {};
+ class StringAddFlagsBits : public BitField<StringAddFlags, 0, 3> {};
+ class PretenureFlagBits : public BitField<PretenureFlag, 3, 1> {};
void PrintBaseName(std::ostream& os) const override; // NOLINT
HInstruction* HStringAdd::New(Isolate* isolate, Zone* zone, HValue* context,
- HValue* left, HValue* right, Strength strength,
+ HValue* left, HValue* right,
PretenureFlag pretenure_flag,
StringAddFlags flags,
Handle<AllocationSite> allocation_site) {
}
}
}
- return new (zone) HStringAdd(context, left, right, strength, pretenure_flag,
- flags, allocation_site);
+ return new (zone)
+ HStringAdd(context, left, right, pretenure_flag, flags, allocation_site);
}
public:
static HInstruction* New(
Isolate* isolate, Zone* zone, HValue* context, HValue* left,
- HValue* right, Strength strength = Strength::WEAK,
- PretenureFlag pretenure_flag = NOT_TENURED,
+ HValue* right, PretenureFlag pretenure_flag = NOT_TENURED,
StringAddFlags flags = STRING_ADD_CHECK_BOTH,
Handle<AllocationSite> allocation_site = Handle<AllocationSite>::null());
}
private:
- HStringAdd(HValue* context, HValue* left, HValue* right, Strength strength,
+ HStringAdd(HValue* context, HValue* left, HValue* right,
PretenureFlag pretenure_flag, StringAddFlags flags,
Handle<AllocationSite> allocation_site)
- : HBinaryOperation(context, left, right, strength, HType::String()),
+ : HBinaryOperation(context, left, right, Strength::WEAK, HType::String()),
flags_(flags),
pretenure_flag_(pretenure_flag) {
set_representation(Representation::Tagged());
- SetFlag(kUseGVN);
+ if ((flags & STRING_ADD_CONVERT) == STRING_ADD_CONVERT) {
+ SetAllSideEffects();
+ ClearFlag(kUseGVN);
+ } else {
+ SetChangesFlag(kNewSpacePromotion);
+ SetFlag(kUseGVN);
+ }
SetDependsOnFlag(kMaps);
- SetChangesFlag(kNewSpacePromotion);
if (FLAG_trace_pretenuring) {
PrintF("HStringAdd with AllocationSite %p %s\n",
allocation_site.is_null()
}
}
- // No side-effects except possible allocation:
- bool IsDeletable() const override { return true; }
+ bool IsDeletable() const final {
+ return (flags_ & STRING_ADD_CONVERT) != STRING_ADD_CONVERT;
+ }
const StringAddFlags flags_;
const PretenureFlag pretenure_flag_;
left = BuildNumberToString(left, left_type);
} else if (!left_type->Is(Type::String())) {
DCHECK(right_type->Is(Type::String()));
- // TODO(bmeurer): We might want to optimize this, because we already
- // know that the right hand side is a string.
- Add<HPushArguments>(left, right);
- return AddUncasted<HCallRuntime>(Runtime::FunctionForId(Runtime::kAdd),
- 2);
+ return AddUncasted<HStringAdd>(
+ left, right, allocation_mode.GetPretenureMode(),
+ STRING_ADD_CONVERT_LEFT, allocation_mode.feedback_site());
}
// Convert right argument as necessary.
right = BuildNumberToString(right, right_type);
} else if (!right_type->Is(Type::String())) {
DCHECK(left_type->Is(Type::String()));
- // TODO(bmeurer): We might want to optimize this, because we already
- // know that the left hand side is a string.
- Add<HPushArguments>(left, right);
- return AddUncasted<HCallRuntime>(Runtime::FunctionForId(Runtime::kAdd),
- 2);
+ return AddUncasted<HStringAdd>(
+ left, right, allocation_mode.GetPretenureMode(),
+ STRING_ADD_CONVERT_RIGHT, allocation_mode.feedback_site());
}
}
if (!right_string.is_null() && right_string->length() == 0) return left;
if (!left_string.is_null() && !right_string.is_null()) {
return AddUncasted<HStringAdd>(
- left, right, strength, allocation_mode.GetPretenureMode(),
+ left, right, allocation_mode.GetPretenureMode(),
STRING_ADD_CHECK_NONE, allocation_mode.feedback_site());
}
// Fallback to using the string add stub.
return AddUncasted<HStringAdd>(
- left, right, strength, allocation_mode.GetPretenureMode(),
- STRING_ADD_CHECK_NONE, allocation_mode.feedback_site());
+ left, right, allocation_mode.GetPretenureMode(), STRING_ADD_CHECK_NONE,
+ allocation_mode.feedback_site());
}
if (graph()->info()->IsStub()) {
CHECK_ALIVE(VisitForValue(call->arguments()->at(1)));
HValue* right = Pop();
HValue* left = Pop();
- HInstruction* result =
- NewUncasted<HStringAdd>(left, right, strength(function_language_mode()));
+ HInstruction* result = NewUncasted<HStringAdd>(left, right);
return ast_context()->ReturnInstruction(result, call->id());
}