Commit b6173356 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Use InternalizedString feedback abstract/strict equality comparisons.

Add machinery to Ignition and TurboFan to collect and consume
InternalizedString feedback for abstract and strict equality
comparisons. Here we can turn the comparison into a simple
pointer equality check.

R=jarin@chromium.org
BUG=v8:5786

Review-Url: https://codereview.chromium.org/2609013002
Cr-Commit-Position: refs/heads/master@{#42008}
parent 4246200c
......@@ -649,6 +649,10 @@ bool EffectControlLinearizer::TryWireInStateEffect(Node* node,
case IrOpcode::kCheckString:
state = LowerCheckString(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckInternalizedString:
state =
LowerCheckInternalizedString(node, frame_state, *effect, *control);
break;
case IrOpcode::kCheckIf:
state = LowerCheckIf(node, frame_state, *effect, *control);
break;
......@@ -1289,6 +1293,37 @@ EffectControlLinearizer::LowerCheckString(Node* node, Node* frame_state,
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckInternalizedString(Node* node,
Node* frame_state,
Node* effect,
Node* control) {
Node* value = node->InputAt(0);
Node* check0 = ObjectIsSmi(value);
control = effect =
graph()->NewNode(common()->DeoptimizeIf(DeoptimizeReason::kSmi), check0,
frame_state, effect, control);
Node* value_map = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMap()), value, effect, control);
Node* value_instance_type = effect = graph()->NewNode(
simplified()->LoadField(AccessBuilder::ForMapInstanceType()), value_map,
effect, control);
Node* check1 = graph()->NewNode(
machine()->Word32Equal(),
graph()->NewNode(
machine()->Word32And(), value_instance_type,
jsgraph()->Int32Constant(kIsNotStringMask | kIsNotInternalizedMask)),
jsgraph()->Int32Constant(kInternalizedTag));
control = effect = graph()->NewNode(
common()->DeoptimizeUnless(DeoptimizeReason::kWrongInstanceType), check1,
frame_state, effect, control);
return ValueEffectControl(value, effect, control);
}
EffectControlLinearizer::ValueEffectControl
EffectControlLinearizer::LowerCheckIf(Node* node, Node* frame_state,
Node* effect, Node* control) {
......
......@@ -76,6 +76,8 @@ class V8_EXPORT_PRIVATE EffectControlLinearizer {
Node* effect, Node* control);
ValueEffectControl LowerCheckString(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckInternalizedString(Node* node, Node* frame_state,
Node* effect, Node* control);
ValueEffectControl LowerCheckIf(Node* node, Node* frame_state, Node* effect,
Node* control);
ValueEffectControl LowerCheckedInt32Add(Node* node, Node* frame_state,
......
......@@ -585,6 +585,8 @@ struct JSOperatorGlobalCache final {
Name##Operator<CompareOperationHint::kNumberOrOddball> \
k##Name##NumberOrOddballOperator; \
Name##Operator<CompareOperationHint::kString> k##Name##StringOperator; \
Name##Operator<CompareOperationHint::kInternalizedString> \
k##Name##InternalizedStringOperator; \
Name##Operator<CompareOperationHint::kAny> k##Name##AnyOperator;
COMPARE_OP_LIST(COMPARE_OP)
#undef COMPARE_OP
......@@ -636,6 +638,8 @@ BINARY_OP_LIST(BINARY_OP)
return &cache_.k##Name##NumberOperator; \
case CompareOperationHint::kNumberOrOddball: \
return &cache_.k##Name##NumberOrOddballOperator; \
case CompareOperationHint::kInternalizedString: \
return &cache_.k##Name##InternalizedStringOperator; \
case CompareOperationHint::kString: \
return &cache_.k##Name##StringOperator; \
case CompareOperationHint::kAny: \
......
......@@ -70,12 +70,23 @@ class JSBinopReduction final {
case CompareOperationHint::kAny:
case CompareOperationHint::kNone:
case CompareOperationHint::kString:
case CompareOperationHint::kInternalizedString:
break;
}
}
return false;
}
bool IsInternalizedStringCompareOperation() {
if (lowering_->flags() & JSTypedLowering::kDeoptimizationEnabled) {
DCHECK_EQ(1, node_->op()->EffectOutputCount());
return (CompareOperationHintOf(node_->op()) ==
CompareOperationHint::kInternalizedString) &&
BothInputsMaybe(Type::InternalizedString());
}
return false;
}
// Check if a string addition will definitely result in creating a ConsString,
// i.e. if the combined length of the resulting string exceeds the ConsString
// minimum length.
......@@ -104,6 +115,25 @@ class JSBinopReduction final {
return false;
}
// Checks that both inputs are InternalizedString, and if we don't know
// statically that one side is already an InternalizedString, insert a
// CheckInternalizedString node.
void CheckInputsToInternalizedString() {
if (!left_type()->Is(Type::UniqueName())) {
Node* left_input = graph()->NewNode(
simplified()->CheckInternalizedString(), left(), effect(), control());
node_->ReplaceInput(0, left_input);
update_effect(left_input);
}
if (!right_type()->Is(Type::UniqueName())) {
Node* right_input =
graph()->NewNode(simplified()->CheckInternalizedString(), right(),
effect(), control());
node_->ReplaceInput(1, right_input);
update_effect(right_input);
}
}
void ConvertInputsToNumber() {
// To convert the inputs to numbers, we have to provide frame states
// for lazy bailouts in the ToNumber conversions.
......@@ -317,6 +347,10 @@ class JSBinopReduction final {
bool BothInputsAre(Type* t) { return LeftInputIs(t) && RightInputIs(t); }
bool BothInputsMaybe(Type* t) {
return left_type()->Maybe(t) && right_type()->Maybe(t);
}
bool OneInputCannotBe(Type* t) {
return !left_type()->Maybe(t) || !right_type()->Maybe(t);
}
......@@ -851,6 +885,13 @@ Reduction JSTypedLowering::ReduceJSEqual(Node* node, bool invert) {
JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::UniqueName())) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.IsInternalizedStringCompareOperation()) {
r.CheckInputsToInternalizedString();
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.BothInputsAre(Type::String())) {
return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
}
......@@ -934,6 +975,10 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node, bool invert) {
if (r.BothInputsAre(Type::Unique())) {
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.IsInternalizedStringCompareOperation()) {
r.CheckInputsToInternalizedString();
return r.ChangeToPureOperator(simplified()->ReferenceEqual(), invert);
}
if (r.BothInputsAre(Type::String())) {
return r.ChangeToPureOperator(simplified()->StringEqual(), invert);
}
......
......@@ -307,6 +307,7 @@
V(CheckIf) \
V(CheckMaps) \
V(CheckNumber) \
V(CheckInternalizedString) \
V(CheckString) \
V(CheckSmi) \
V(CheckHeapObject) \
......
......@@ -22,6 +22,7 @@ Reduction RedundancyElimination::Reduce(Node* node) {
case IrOpcode::kCheckFloat64Hole:
case IrOpcode::kCheckHeapObject:
case IrOpcode::kCheckIf:
case IrOpcode::kCheckInternalizedString:
case IrOpcode::kCheckNumber:
case IrOpcode::kCheckSmi:
case IrOpcode::kCheckString:
......@@ -120,7 +121,14 @@ RedundancyElimination::EffectPathChecks::AddCheck(Zone* zone,
namespace {
bool IsCompatibleCheck(Node const* a, Node const* b) {
if (a->op() != b->op()) return false;
if (a->op() != b->op()) {
if (a->opcode() == IrOpcode::kCheckInternalizedString &&
b->opcode() == IrOpcode::kCheckString) {
// CheckInternalizedString(node) implies CheckString(node)
} else {
return false;
}
}
for (int i = a->op()->ValueInputCount(); --i >= 0;) {
if (a->InputAt(i) != b->InputAt(i)) return false;
}
......
......@@ -2253,6 +2253,17 @@ class RepresentationSelector {
SetOutput(node, MachineRepresentation::kNone);
return;
}
case IrOpcode::kCheckInternalizedString: {
if (InputIs(node, Type::InternalizedString())) {
VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
if (lower()) DeferReplacement(node, node->InputAt(0));
} else {
VisitUnop(node, UseInfo::AnyTagged(),
MachineRepresentation::kTaggedPointer);
}
return;
}
case IrOpcode::kCheckNumber: {
if (InputIs(node, Type::Number())) {
if (truncation.IsUsedAsWord32()) {
......
......@@ -443,6 +443,7 @@ UnicodeEncoding UnicodeEncodingOf(const Operator* op) {
V(CheckBounds, 2, 1) \
V(CheckHeapObject, 1, 1) \
V(CheckIf, 1, 0) \
V(CheckInternalizedString, 1, 1) \
V(CheckNumber, 1, 1) \
V(CheckSmi, 1, 1) \
V(CheckString, 1, 1) \
......
......@@ -325,6 +325,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckMaps(int map_input_count);
const Operator* CheckHeapObject();
const Operator* CheckInternalizedString();
const Operator* CheckNumber();
const Operator* CheckSmi();
const Operator* CheckString();
......
......@@ -1716,6 +1716,11 @@ Type* Typer::Visitor::TypeCheckIf(Node* node) {
return nullptr;
}
Type* Typer::Visitor::TypeCheckInternalizedString(Node* node) {
Type* arg = Operand(node, 0);
return Type::Intersect(arg, Type::InternalizedString(), zone());
}
Type* Typer::Visitor::TypeCheckMaps(Node* node) {
UNREACHABLE();
return nullptr;
......
......@@ -1087,6 +1087,10 @@ void Verifier::Visitor::Check(Node* node) {
CheckValueInputIs(node, 0, Type::Boolean());
CheckNotTyped(node);
break;
case IrOpcode::kCheckInternalizedString:
CheckValueInputIs(node, 0, Type::Any());
CheckTypeIs(node, Type::InternalizedString());
break;
case IrOpcode::kCheckMaps:
// (Any, Internal, ..., Internal) -> Any
CheckValueInputIs(node, 0, Type::Any());
......
......@@ -1246,8 +1246,8 @@ class BinaryOperationFeedback {
// Type feedback is encoded in such a way that, we can combine the feedback
// at different points by performing an 'OR' operation. Type feedback moves
// to a more generic type when we combine feedback.
// kSignedSmall -> kNumber -> kAny
// kString -> kAny
// kSignedSmall -> kNumber -> kAny
// kInternalizedString -> kString -> kAny
// TODO(epertoso): consider unifying this with BinaryOperationFeedback.
class CompareOperationFeedback {
public:
......@@ -1256,8 +1256,9 @@ class CompareOperationFeedback {
kSignedSmall = 0x01,
kNumber = 0x3,
kNumberOrOddball = 0x7,
kString = 0x8,
kAny = 0x1F
kInternalizedString = 0x8,
kString = 0x18,
kAny = 0x3F
};
};
......
......@@ -1038,9 +1038,27 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op,
__ Bind(&lhs_is_not_oddball);
}
var_type_feedback.Bind(__ SelectInt32Constant(
__ IsStringInstanceType(lhs_instance_type),
CompareOperationFeedback::kString, CompareOperationFeedback::kAny));
Label lhs_is_not_string(assembler);
__ GotoUnless(__ IsStringInstanceType(lhs_instance_type),
&lhs_is_not_string);
if (Token::IsOrderedRelationalCompareOp(compare_op)) {
var_type_feedback.Bind(
__ Int32Constant(CompareOperationFeedback::kString));
} else {
var_type_feedback.Bind(__ SelectInt32Constant(
__ Word32Equal(
__ Word32And(lhs_instance_type,
__ Int32Constant(kIsNotInternalizedMask)),
__ Int32Constant(kInternalizedTag)),
CompareOperationFeedback::kInternalizedString,
CompareOperationFeedback::kString));
}
__ Goto(&gather_rhs_type);
__ Bind(&lhs_is_not_string);
var_type_feedback.Bind(
__ Int32Constant(CompareOperationFeedback::kAny));
__ Goto(&gather_rhs_type);
}
}
......@@ -1083,11 +1101,30 @@ void Interpreter::DoCompareOpWithFeedback(Token::Value compare_op,
__ Bind(&rhs_is_not_oddball);
}
var_type_feedback.Bind(__ Word32Or(
var_type_feedback.value(),
__ SelectInt32Constant(__ IsStringInstanceType(rhs_instance_type),
CompareOperationFeedback::kString,
CompareOperationFeedback::kAny)));
Label rhs_is_not_string(assembler);
__ GotoUnless(__ IsStringInstanceType(rhs_instance_type),
&rhs_is_not_string);
if (Token::IsOrderedRelationalCompareOp(compare_op)) {
var_type_feedback.Bind(__ Word32Or(
var_type_feedback.value(),
__ Int32Constant(CompareOperationFeedback::kString)));
} else {
var_type_feedback.Bind(__ Word32Or(
var_type_feedback.value(),
__ SelectInt32Constant(
__ Word32Equal(
__ Word32And(rhs_instance_type,
__ Int32Constant(kIsNotInternalizedMask)),
__ Int32Constant(kInternalizedTag)),
CompareOperationFeedback::kInternalizedString,
CompareOperationFeedback::kString)));
}
__ Goto(&update_feedback);
__ Bind(&rhs_is_not_string);
var_type_feedback.Bind(
__ Int32Constant(CompareOperationFeedback::kAny));
__ Goto(&update_feedback);
}
}
......
......@@ -128,6 +128,8 @@ CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) {
return CompareOperationHint::kNumber;
case CompareOperationFeedback::kNumberOrOddball:
return CompareOperationHint::kNumberOrOddball;
case CompareOperationFeedback::kInternalizedString:
return CompareOperationHint::kInternalizedString;
case CompareOperationFeedback::kString:
return CompareOperationHint::kString;
default:
......
......@@ -36,6 +36,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "Number";
case CompareOperationHint::kNumberOrOddball:
return os << "NumberOrOddball";
case CompareOperationHint::kInternalizedString:
return os << "InternalizedString";
case CompareOperationHint::kString:
return os << "String";
case CompareOperationHint::kAny:
......
......@@ -33,6 +33,7 @@ enum class CompareOperationHint : uint8_t {
kSignedSmall,
kNumber,
kNumberOrOddball,
kInternalizedString,
kString,
kAny
};
......
......@@ -203,6 +203,8 @@ AstType* CompareOpHintToType(CompareOperationHint hint) {
return AstType::Number();
case CompareOperationHint::kNumberOrOddball:
return AstType::NumberOrOddball();
case CompareOperationHint::kInternalizedString:
return AstType::InternalizedString();
case CompareOperationHint::kString:
return AstType::String();
case CompareOperationHint::kAny:
......
......@@ -1798,9 +1798,9 @@ TEST(InterpreterStringComparisons) {
BytecodeArrayBuilder builder(isolate, handles.main_zone(), 0, 0, 1);
Register r0(0);
builder.LoadLiteral(factory->NewStringFromAsciiChecked(lhs))
builder.LoadLiteral(factory->InternalizeUtf8String(lhs))
.StoreAccumulatorInRegister(r0)
.LoadLiteral(factory->NewStringFromAsciiChecked(rhs))
.LoadLiteral(factory->InternalizeUtf8String(rhs))
.CompareOperation(comparison, r0, vector->GetIndex(slot))
.Return();
......@@ -1813,8 +1813,11 @@ TEST(InterpreterStringComparisons) {
CompareC(comparison, inputs[i], inputs[j]));
Object* feedback = vector->Get(slot);
CHECK(feedback->IsSmi());
CHECK_EQ(CompareOperationFeedback::kString,
static_cast<Smi*>(feedback)->value());
int const expected_feedback =
Token::IsOrderedRelationalCompareOp(comparison)
? CompareOperationFeedback::kString
: CompareOperationFeedback::kInternalizedString;
CHECK_EQ(expected_feedback, static_cast<Smi*>(feedback)->value());
}
}
}
......
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