Commit 120d4333 authored by Nico Hartmann's avatar Nico Hartmann Committed by Commit Bot

Reland "[turbofan] Improve equality on NumberOrOddball"

This is a reland of 6204768b

The original issue exposed the problem that NumberEqual performs
implicit conversion of oddballs to numbers, which is incorrect for
abstract equality comparison (i.e. 0 == null must not be true).

This reland fixes this by applying the following steps:
* Introduced a new kNumberOrBoolean value for CompareOperationFeedback,
  CompareOperationHint, TypeCheckKind and CheckedTaggedInputMode.
* In CodeStubAssembler::Equal: Further distinguish between boolean and
  non-boolean oddballs and set feedback accoringly.
* In JSTypedLowering: Construct [Speculative]NumberEqual operator with
  CompareOperationHint::kNumberOrBoolean, when this feedback is present.
  JSOperatorBuilder and operator cache are extended accordingly.
* In SimplifiedLowering: Propagate a UseInfo with new
  TypeCheckKind::kNumberOrBoolean.
* This leads to the generation of CheckedTaggedToFloat64 in
  RepresentationChanger with new CheckedTaggedInputMode::kNumberOrBoolean.
* In EffectControlLinearizer: Handle this new mode. Accept and convert
  number and boolean and deopt for rest.

Original change's description:
> [turbofan] Improve equality on NumberOrOddball
>
> This CL cleans up CompareOperationFeedback by replacing it with a
> composable set of flags. The interpreter is changed to collect
> more specific feedback for abstract equality, especially if oddballs
> are involved.
>
> TurboFan is changed to construct SpeculativeNumberEqual operator
> instead of the generic JSEqual in many more cases. This change has
> shown a local speedup of a factor of 3-10, because the specific
> operator is way faster than calling into the generic builtin, but
> it also enables additional optimizations, further improving
> runtime performance.
>
> Bug: v8:5660
> Change-Id: I856752caa707e9a4f742c6e7a9c75552fb431d28
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2162854
> Reviewed-by: Mythri Alle <mythria@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#67645}

TBR: tebbi@chromium.org
Bug: v8:5660
Change-Id: I12e733149a1d2773cafb781a1d4b10aa1eb242a7
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2193713
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarMythri Alle <mythria@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#68037}
parent 0dc1a2d8
This diff is collapsed.
......@@ -1339,31 +1339,46 @@ 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 -> kNumberOrOddball -> kAny
// kReceiver -> kReceiverOrNullOrUndefined -> kAny
// kInternalizedString -> kString -> kAny
// kSymbol -> kAny
// kBigInt -> kAny
//
// at different points by performing an 'OR' operation.
// This is distinct from BinaryOperationFeedback on purpose, because the
// feedback that matters differs greatly as well as the way it is consumed.
class CompareOperationFeedback {
public:
enum {
kNone = 0x000,
kSignedSmall = 0x001,
kNumber = 0x003,
kNumberOrOddball = 0x007,
kInternalizedString = 0x008,
kString = 0x018,
kSymbol = 0x020,
kBigInt = 0x040,
kReceiver = 0x080,
kReceiverOrNullOrUndefined = 0x180,
kAny = 0x1ff
kSignedSmallFlag = 1 << 0,
kOtherNumberFlag = 1 << 1,
kBooleanFlag = 1 << 2,
kNullOrUndefinedFlag = 1 << 3,
kInternalizedStringFlag = 1 << 4,
kOtherStringFlag = 1 << 5,
kSymbolFlag = 1 << 6,
kBigIntFlag = 1 << 7,
kReceiverFlag = 1 << 8,
kAnyMask = 0x1FF,
};
public:
enum Type {
kNone = 0,
kBoolean = kBooleanFlag,
kNullOrUndefined = kNullOrUndefinedFlag,
kOddball = kBoolean | kNullOrUndefined,
kSignedSmall = kSignedSmallFlag,
kNumber = kSignedSmall | kOtherNumberFlag,
kNumberOrBoolean = kNumber | kBoolean,
kNumberOrOddball = kNumber | kOddball,
kInternalizedString = kInternalizedStringFlag,
kString = kInternalizedString | kOtherStringFlag,
kReceiver = kReceiverFlag,
kReceiverOrNullOrUndefined = kReceiver | kNullOrUndefined,
kBigInt = kBigIntFlag,
kSymbol = kSymbolFlag,
kAny = kAnyMask,
};
};
......
......@@ -1389,6 +1389,7 @@ void CodeAssemblerLabel::MergeVariables() {
}
// If the following asserts, then you've jumped to a label without a bound
// variable along that path that expects to merge its value into a phi.
// This can also occur if a label is bound that is never jumped to.
DCHECK(variable_phis_.find(var) == variable_phis_.end() ||
count == merge_count_);
USE(count);
......
......@@ -2700,6 +2700,20 @@ Node* EffectControlLinearizer::BuildCheckedHeapNumberOrOddballToFloat64(
check_number, frame_state);
break;
}
case CheckTaggedInputMode::kNumberOrBoolean: {
auto check_done = __ MakeLabel();
__ GotoIf(check_number, &check_done);
__ DeoptimizeIfNot(DeoptimizeReason::kNotANumberOrBoolean, feedback,
__ TaggedEqual(value_map, __ BooleanMapConstant()),
frame_state);
STATIC_ASSERT_FIELD_OFFSETS_EQUAL(HeapNumber::kValueOffset,
Oddball::kToNumberRawOffset);
__ Goto(&check_done);
__ Bind(&check_done);
break;
}
case CheckTaggedInputMode::kNumberOrOddball: {
auto check_done = __ MakeLabel();
......
......@@ -707,9 +707,11 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
#define BINARY_OP_LIST(V) V(Add)
#define COMPARE_EQUAL_OP_LIST(V) \
V(Equal, Operator::kNoProperties) \
V(StrictEqual, Operator::kPure)
#define COMPARE_OP_LIST(V) \
V(Equal, Operator::kNoProperties) \
V(StrictEqual, Operator::kPure) \
V(LessThan, Operator::kNoProperties) \
V(GreaterThan, Operator::kNoProperties) \
V(LessThanOrEqual, Operator::kNoProperties) \
......@@ -776,6 +778,12 @@ struct JSOperatorGlobalCache final {
k##Name##ReceiverOrNullOrUndefinedOperator; \
Name##Operator<CompareOperationHint::kAny> k##Name##AnyOperator;
COMPARE_OP_LIST(COMPARE_OP)
COMPARE_EQUAL_OP_LIST(COMPARE_OP)
// Only equality provides an additional operator for NumberOrBoolean.
EqualOperator<CompareOperationHint::kNumberOrBoolean>
kEqualNumberOrBooleanOperator;
StrictEqualOperator<CompareOperationHint::kNumberOrBoolean>
kStrictEqualNumberOrBooleanOperator;
#undef COMPARE_OP
};
......@@ -840,6 +848,9 @@ UNARY_OP_LIST(UNARY_OP)
return &cache_.k##Name##SignedSmallOperator; \
case CompareOperationHint::kNumber: \
return &cache_.k##Name##NumberOperator; \
case CompareOperationHint::kNumberOrBoolean: \
/* Not supported for operations other than equality */ \
UNIMPLEMENTED(); \
case CompareOperationHint::kNumberOrOddball: \
return &cache_.k##Name##NumberOrOddballOperator; \
case CompareOperationHint::kInternalizedString: \
......@@ -863,6 +874,40 @@ UNARY_OP_LIST(UNARY_OP)
COMPARE_OP_LIST(COMPARE_OP)
#undef COMPARE_OP
#define COMPARE_EQUAL_OP(Name, ...) \
const Operator* JSOperatorBuilder::Name(CompareOperationHint hint) { \
switch (hint) { \
case CompareOperationHint::kNone: \
return &cache_.k##Name##NoneOperator; \
case CompareOperationHint::kSignedSmall: \
return &cache_.k##Name##SignedSmallOperator; \
case CompareOperationHint::kNumber: \
return &cache_.k##Name##NumberOperator; \
case CompareOperationHint::kNumberOrBoolean: \
return &cache_.k##Name##NumberOrBooleanOperator; \
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::kSymbol: \
return &cache_.k##Name##SymbolOperator; \
case CompareOperationHint::kBigInt: \
return &cache_.k##Name##BigIntOperator; \
case CompareOperationHint::kReceiver: \
return &cache_.k##Name##ReceiverOperator; \
case CompareOperationHint::kReceiverOrNullOrUndefined: \
return &cache_.k##Name##ReceiverOrNullOrUndefinedOperator; \
case CompareOperationHint::kAny: \
return &cache_.k##Name##AnyOperator; \
} \
UNREACHABLE(); \
return nullptr; \
}
COMPARE_EQUAL_OP_LIST(COMPARE_EQUAL_OP)
#undef COMPARE_EQUAL_OP
const Operator* JSOperatorBuilder::StoreDataPropertyInLiteral(
const FeedbackSource& feedback) {
FeedbackParameter parameters(feedback);
......@@ -1438,6 +1483,7 @@ Handle<ScopeInfo> ScopeInfoOf(const Operator* op) {
#undef BINARY_OP_LIST
#undef CACHED_OP_LIST
#undef COMPARE_OP_LIST
#undef COMPARE_EQUAL_OP_LIST
} // namespace compiler
} // namespace internal
......
......@@ -97,6 +97,9 @@ class JSSpeculativeBinopBuilder final {
case CompareOperationHint::kNumber:
*hint = NumberOperationHint::kNumber;
return true;
case CompareOperationHint::kNumberOrBoolean:
*hint = NumberOperationHint::kNumberOrBoolean;
return true;
case CompareOperationHint::kNumberOrOddball:
*hint = NumberOperationHint::kNumberOrOddball;
return true;
......
......@@ -45,6 +45,9 @@ class JSBinopReduction final {
case CompareOperationHint::kNumber:
*hint = NumberOperationHint::kNumber;
return true;
case CompareOperationHint::kNumberOrBoolean:
*hint = NumberOperationHint::kNumberOrBoolean;
return true;
case CompareOperationHint::kNumberOrOddball:
*hint = NumberOperationHint::kNumberOrOddball;
return true;
......@@ -887,7 +890,14 @@ Reduction JSTypedLowering::ReduceJSStrictEqual(Node* node) {
if (r.BothInputsAre(Type::Signed32()) ||
r.BothInputsAre(Type::Unsigned32())) {
return r.ChangeToPureOperator(simplified()->NumberEqual());
} else if (r.GetCompareNumberOperationHint(&hint)) {
} else if (r.GetCompareNumberOperationHint(&hint) &&
hint != NumberOperationHint::kNumberOrOddball &&
hint != NumberOperationHint::kNumberOrBoolean) {
// SpeculativeNumberEqual performs implicit conversion of oddballs to
// numbers, so me must not generate it for strict equality with respective
// hint.
DCHECK(hint == NumberOperationHint::kNumber ||
hint == NumberOperationHint::kSignedSmall);
return r.ChangeToSpeculativeOperator(
simplified()->SpeculativeNumberEqual(hint), Type::Boolean());
} else if (r.BothInputsAre(Type::Number())) {
......
......@@ -11,6 +11,7 @@
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/machine-operator.h"
#include "src/compiler/node-matchers.h"
#include "src/compiler/simplified-operator.h"
#include "src/compiler/type-cache.h"
#include "src/heap/factory-inl.h"
......@@ -662,6 +663,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
switch (use_info.type_check()) {
case TypeCheckKind::kNone:
case TypeCheckKind::kNumber:
case TypeCheckKind::kNumberOrBoolean:
case TypeCheckKind::kNumberOrOddball:
return jsgraph()->Float64Constant(m.Value());
case TypeCheckKind::kBigInt:
......@@ -695,6 +697,7 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
} else if (output_rep == MachineRepresentation::kBit) {
CHECK(output_type.Is(Type::Boolean()));
if (use_info.truncation().TruncatesOddballAndBigIntToNumber() ||
use_info.type_check() == TypeCheckKind::kNumberOrBoolean ||
use_info.type_check() == TypeCheckKind::kNumberOrOddball) {
op = machine()->ChangeUint32ToFloat64();
} else {
......@@ -707,9 +710,16 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
}
} else if (IsAnyTagged(output_rep)) {
if (output_type.Is(Type::Undefined())) {
return jsgraph()->Float64Constant(
std::numeric_limits<double>::quiet_NaN());
if (use_info.type_check() == TypeCheckKind::kNumberOrBoolean) {
Node* unreachable = InsertUnconditionalDeopt(
use_node, DeoptimizeReason::kNotANumberOrBoolean);
return jsgraph()->graph()->NewNode(
jsgraph()->common()->DeadValue(MachineRepresentation::kFloat64),
unreachable);
} else {
return jsgraph()->Float64Constant(
std::numeric_limits<double>::quiet_NaN());
}
} else if (output_rep == MachineRepresentation::kTaggedSigned) {
node = InsertChangeTaggedSignedToInt32(node);
op = machine()->ChangeInt32ToFloat64();
......@@ -732,6 +742,9 @@ Node* RepresentationChanger::GetFloat64RepresentationFor(
!output_type.Maybe(Type::BooleanOrNullOrNumber()))) {
op = simplified()->CheckedTaggedToFloat64(CheckTaggedInputMode::kNumber,
use_info.feedback());
} else if (use_info.type_check() == TypeCheckKind::kNumberOrBoolean) {
op = simplified()->CheckedTaggedToFloat64(
CheckTaggedInputMode::kNumberOrBoolean, use_info.feedback());
} else if (use_info.type_check() == TypeCheckKind::kNumberOrOddball) {
op = simplified()->CheckedTaggedToFloat64(
CheckTaggedInputMode::kNumberOrOddball, use_info.feedback());
......
......@@ -119,6 +119,7 @@ enum class TypeCheckKind : uint8_t {
kSigned32,
kSigned64,
kNumber,
kNumberOrBoolean,
kNumberOrOddball,
kHeapObject,
kBigInt,
......@@ -137,6 +138,8 @@ inline std::ostream& operator<<(std::ostream& os, TypeCheckKind type_check) {
return os << "Signed64";
case TypeCheckKind::kNumber:
return os << "Number";
case TypeCheckKind::kNumberOrBoolean:
return os << "NumberOrBoolean";
case TypeCheckKind::kNumberOrOddball:
return os << "NumberOrOddball";
case TypeCheckKind::kHeapObject:
......@@ -266,6 +269,12 @@ class UseInfo {
return UseInfo(MachineRepresentation::kWord32, Truncation::Word32(),
TypeCheckKind::kNumber, feedback);
}
static UseInfo CheckedNumberOrBooleanAsFloat64(
IdentifyZeros identify_zeros, const FeedbackSource& feedback) {
return UseInfo(MachineRepresentation::kFloat64,
Truncation::Any(identify_zeros),
TypeCheckKind::kNumberOrBoolean, feedback);
}
static UseInfo CheckedNumberOrOddballAsFloat64(
IdentifyZeros identify_zeros, const FeedbackSource& feedback) {
return UseInfo(MachineRepresentation::kFloat64,
......
......@@ -105,6 +105,9 @@ UseInfo CheckedUseInfoAsWord32FromHint(
return UseInfo::CheckedSigned32AsWord32(identify_zeros, feedback);
case NumberOperationHint::kNumber:
return UseInfo::CheckedNumberAsWord32(feedback);
case NumberOperationHint::kNumberOrBoolean:
// Not used currently.
UNREACHABLE();
case NumberOperationHint::kNumberOrOddball:
return UseInfo::CheckedNumberOrOddballAsWord32(feedback);
}
......@@ -122,6 +125,8 @@ UseInfo CheckedUseInfoAsFloat64FromHint(
UNREACHABLE();
case NumberOperationHint::kNumber:
return UseInfo::CheckedNumberAsFloat64(identify_zeros, feedback);
case NumberOperationHint::kNumberOrBoolean:
return UseInfo::CheckedNumberOrBooleanAsFloat64(identify_zeros, feedback);
case NumberOperationHint::kNumberOrOddball:
return UseInfo::CheckedNumberOrOddballAsFloat64(identify_zeros, feedback);
}
......@@ -2083,6 +2088,7 @@ class RepresentationSelector {
// hint with Oddball feedback here.
DCHECK_NE(IrOpcode::kSpeculativeNumberEqual, node->opcode());
V8_FALLTHROUGH;
case NumberOperationHint::kNumberOrBoolean:
case NumberOperationHint::kNumber:
VisitBinop<T>(node,
CheckedUseInfoAsFloat64FromHint(
......@@ -3302,6 +3308,7 @@ class RepresentationSelector {
MachineRepresentation::kWord32, Type::Signed32());
break;
case NumberOperationHint::kNumber:
case NumberOperationHint::kNumberOrBoolean:
case NumberOperationHint::kNumberOrOddball:
VisitUnop<T>(
node, CheckedUseInfoAsFloat64FromHint(p.hint(), p.feedback()),
......
......@@ -304,6 +304,8 @@ std::ostream& operator<<(std::ostream& os, CheckTaggedInputMode mode) {
switch (mode) {
case CheckTaggedInputMode::kNumber:
return os << "Number";
case CheckTaggedInputMode::kNumberOrBoolean:
return os << "NumberOrBoolean";
case CheckTaggedInputMode::kNumberOrOddball:
return os << "NumberOrOddball";
}
......@@ -532,6 +534,8 @@ std::ostream& operator<<(std::ostream& os, NumberOperationHint hint) {
return os << "Signed32";
case NumberOperationHint::kNumber:
return os << "Number";
case NumberOperationHint::kNumberOrBoolean:
return os << "NumberOrBoolean";
case NumberOperationHint::kNumberOrOddball:
return os << "NumberOrOddball";
}
......@@ -1045,6 +1049,8 @@ struct SimplifiedOperatorGlobalCache final {
};
CheckedTaggedToFloat64Operator<CheckTaggedInputMode::kNumber>
kCheckedTaggedToFloat64NumberOperator;
CheckedTaggedToFloat64Operator<CheckTaggedInputMode::kNumberOrBoolean>
kCheckedTaggedToFloat64NumberOrBooleanOperator;
CheckedTaggedToFloat64Operator<CheckTaggedInputMode::kNumberOrOddball>
kCheckedTaggedToFloat64NumberOrOddballOperator;
......@@ -1157,6 +1163,8 @@ struct SimplifiedOperatorGlobalCache final {
k##Name##NumberOrOddballOperator;
SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP)
#undef SPECULATIVE_NUMBER_BINOP
SpeculativeNumberEqualOperator<NumberOperationHint::kNumberOrBoolean>
kSpeculativeNumberEqualNumberOrBooleanOperator;
template <NumberOperationHint kHint>
struct SpeculativeToNumberOperator final
......@@ -1402,6 +1410,8 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64(
switch (mode) {
case CheckTaggedInputMode::kNumber:
return &cache_.kCheckedTaggedToFloat64NumberOperator;
case CheckTaggedInputMode::kNumberOrBoolean:
return &cache_.kCheckedTaggedToFloat64NumberOrBooleanOperator;
case CheckTaggedInputMode::kNumberOrOddball:
return &cache_.kCheckedTaggedToFloat64NumberOrOddballOperator;
}
......@@ -1418,6 +1428,9 @@ const Operator* SimplifiedOperatorBuilder::CheckedTruncateTaggedToWord32(
switch (mode) {
case CheckTaggedInputMode::kNumber:
return &cache_.kCheckedTruncateTaggedToWord32NumberOperator;
case CheckTaggedInputMode::kNumberOrBoolean:
// Not used currently.
UNREACHABLE();
case CheckTaggedInputMode::kNumberOrOddball:
return &cache_.kCheckedTruncateTaggedToWord32NumberOrOddballOperator;
}
......@@ -1541,6 +1554,9 @@ const Operator* SimplifiedOperatorBuilder::SpeculativeToNumber(
return &cache_.kSpeculativeToNumberSigned32Operator;
case NumberOperationHint::kNumber:
return &cache_.kSpeculativeToNumberNumberOperator;
case NumberOperationHint::kNumberOrBoolean:
// Not used currently.
UNREACHABLE();
case NumberOperationHint::kNumberOrOddball:
return &cache_.kSpeculativeToNumberNumberOrOddballOperator;
}
......@@ -1778,14 +1794,38 @@ const Operator* SimplifiedOperatorBuilder::AllocateRaw(
return &cache_.k##Name##Signed32Operator; \
case NumberOperationHint::kNumber: \
return &cache_.k##Name##NumberOperator; \
case NumberOperationHint::kNumberOrBoolean: \
/* Not used currenly. */ \
UNREACHABLE(); \
case NumberOperationHint::kNumberOrOddball: \
return &cache_.k##Name##NumberOrOddballOperator; \
} \
UNREACHABLE(); \
return nullptr; \
}
SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP)
SIMPLIFIED_SPECULATIVE_NUMBER_BINOP_LIST(SPECULATIVE_NUMBER_BINOP)
SPECULATIVE_NUMBER_BINOP(SpeculativeNumberLessThan)
SPECULATIVE_NUMBER_BINOP(SpeculativeNumberLessThanOrEqual)
#undef SPECULATIVE_NUMBER_BINOP
const Operator* SimplifiedOperatorBuilder::SpeculativeNumberEqual(
NumberOperationHint hint) {
switch (hint) {
case NumberOperationHint::kSignedSmall:
return &cache_.kSpeculativeNumberEqualSignedSmallOperator;
case NumberOperationHint::kSignedSmallInputs:
return &cache_.kSpeculativeNumberEqualSignedSmallInputsOperator;
case NumberOperationHint::kSigned32:
return &cache_.kSpeculativeNumberEqualSigned32Operator;
case NumberOperationHint::kNumber:
return &cache_.kSpeculativeNumberEqualNumberOperator;
case NumberOperationHint::kNumberOrBoolean:
return &cache_.kSpeculativeNumberEqualNumberOrBooleanOperator;
case NumberOperationHint::kNumberOrOddball:
return &cache_.kSpeculativeNumberEqualNumberOrOddballOperator;
}
UNREACHABLE();
return nullptr;
}
#define ACCESS_OP_LIST(V) \
V(LoadField, FieldAccess, Operator::kNoWrite, 1, 1, 1) \
......
......@@ -312,6 +312,7 @@ Handle<FeedbackCell> FeedbackCellOf(const Operator* op);
enum class CheckTaggedInputMode : uint8_t {
kNumber,
kNumberOrBoolean,
kNumberOrOddball,
};
......@@ -507,6 +508,7 @@ enum class NumberOperationHint : uint8_t {
kSignedSmallInputs, // Inputs were Smi, output was Number.
kSigned32, // Inputs were Signed32, output was Number.
kNumber, // Inputs were Number, output was Number.
kNumberOrBoolean, // Inputs were Number or Boolean, output was Number.
kNumberOrOddball, // Inputs were Number or Oddball, output was Number.
};
......
......@@ -42,6 +42,7 @@ namespace internal {
V(NotAJavaScriptObject, "not a JavaScript object") \
V(NotAJavaScriptObjectOrNullOrUndefined, \
"not a JavaScript object, Null or Undefined") \
V(NotANumberOrBoolean, "not a Number or Boolean") \
V(NotANumberOrOddball, "not a Number or Oddball") \
V(NotAnArrayIndex, "not an array index") \
V(NotASmi, "not a Smi") \
......
......@@ -233,32 +233,47 @@ BinaryOperationHint BinaryOperationHintFromFeedback(int type_feedback) {
}
// Helper function to transform the feedback to CompareOperationHint.
template <CompareOperationFeedback::Type Feedback>
bool Is(int type_feedback) {
return !(type_feedback & ~Feedback);
}
CompareOperationHint CompareOperationHintFromFeedback(int type_feedback) {
switch (type_feedback) {
case CompareOperationFeedback::kNone:
return CompareOperationHint::kNone;
case CompareOperationFeedback::kSignedSmall:
return CompareOperationHint::kSignedSmall;
case CompareOperationFeedback::kNumber:
return CompareOperationHint::kNumber;
case CompareOperationFeedback::kNumberOrOddball:
return CompareOperationHint::kNumberOrOddball;
case CompareOperationFeedback::kInternalizedString:
return CompareOperationHint::kInternalizedString;
case CompareOperationFeedback::kString:
return CompareOperationHint::kString;
case CompareOperationFeedback::kSymbol:
return CompareOperationHint::kSymbol;
case CompareOperationFeedback::kBigInt:
return CompareOperationHint::kBigInt;
case CompareOperationFeedback::kReceiver:
return CompareOperationHint::kReceiver;
case CompareOperationFeedback::kReceiverOrNullOrUndefined:
return CompareOperationHint::kReceiverOrNullOrUndefined;
default:
return CompareOperationHint::kAny;
if (Is<CompareOperationFeedback::kNone>(type_feedback)) {
return CompareOperationHint::kNone;
}
UNREACHABLE();
if (Is<CompareOperationFeedback::kSignedSmall>(type_feedback)) {
return CompareOperationHint::kSignedSmall;
} else if (Is<CompareOperationFeedback::kNumber>(type_feedback)) {
return CompareOperationHint::kNumber;
} else if (Is<CompareOperationFeedback::kNumberOrBoolean>(type_feedback)) {
return CompareOperationHint::kNumberOrBoolean;
}
if (Is<CompareOperationFeedback::kInternalizedString>(type_feedback)) {
return CompareOperationHint::kInternalizedString;
} else if (Is<CompareOperationFeedback::kString>(type_feedback)) {
return CompareOperationHint::kString;
}
if (Is<CompareOperationFeedback::kReceiver>(type_feedback)) {
return CompareOperationHint::kReceiver;
} else if (Is<CompareOperationFeedback::kReceiverOrNullOrUndefined>(
type_feedback)) {
return CompareOperationHint::kReceiverOrNullOrUndefined;
}
if (Is<CompareOperationFeedback::kBigInt>(type_feedback)) {
return CompareOperationHint::kBigInt;
}
if (Is<CompareOperationFeedback::kSymbol>(type_feedback)) {
return CompareOperationHint::kSymbol;
}
DCHECK(Is<CompareOperationFeedback::kAny>(type_feedback));
return CompareOperationHint::kAny;
}
// Helper function to transform the feedback to ForInHint.
......
......@@ -39,6 +39,8 @@ std::ostream& operator<<(std::ostream& os, CompareOperationHint hint) {
return os << "SignedSmall";
case CompareOperationHint::kNumber:
return os << "Number";
case CompareOperationHint::kNumberOrBoolean:
return os << "NumberOrBoolean";
case CompareOperationHint::kNumberOrOddball:
return os << "NumberOrOddball";
case CompareOperationHint::kInternalizedString:
......
......@@ -35,6 +35,7 @@ enum class CompareOperationHint : uint8_t {
kNone,
kSignedSmall,
kNumber,
kNumberOrBoolean,
kNumberOrOddball,
kInternalizedString,
kString,
......
......@@ -2087,7 +2087,6 @@ TEST(InterpreterMixedComparisons) {
LoadStringAndAddSpace(&builder, &ast_factory, rhs_cstr,
string_add_slot);
}
break;
} else {
CHECK_EQ(which_side, kLhsIsString);
// Comparison with String on the lhs and HeapNumber on the rhs.
......@@ -2121,9 +2120,19 @@ TEST(InterpreterMixedComparisons) {
if (tester.HasFeedbackMetadata()) {
MaybeObject feedback = callable.vector().Get(slot);
CHECK(feedback->IsSmi());
// Comparison with a number and string collects kAny feedback.
CHECK_EQ(CompareOperationFeedback::kAny,
feedback->ToSmi().value());
if (kComparisonTypes[c] == Token::Value::EQ) {
// For sloppy equality, we have more precise feedback.
CHECK_EQ(
CompareOperationFeedback::kNumber |
(string_type == kInternalizedStringConstant
? CompareOperationFeedback::kInternalizedString
: CompareOperationFeedback::kString),
feedback->ToSmi().value());
} else {
// Comparison with a number and string collects kAny feedback.
CHECK_EQ(CompareOperationFeedback::kAny,
feedback->ToSmi().value());
}
}
}
}
......
// Copyright 2020 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.
// Flags: --allow-natives-syntax --opt --no-always-opt
// smi == boolean
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(2, true));
assertTrue(foo(1, true));
assertTrue(foo(0, false));
assertFalse(foo(2, false));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(2, true));
assertTrue(foo(1, true));
assertTrue(foo(0, false));
assertFalse(foo(2, false));
assertOptimized(foo);
assertFalse(foo(0, null));
assertUnoptimized(foo);
})();
// boolean == smi
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(true, 2));
assertTrue(foo(true, 1));
assertTrue(foo(false, 0));
assertFalse(foo(false, 2));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(true, 2));
assertTrue(foo(true, 1));
assertTrue(foo(false, 0));
assertFalse(foo(false, 2));
assertOptimized(foo);
assertFalse(foo(null, 0));
assertUnoptimized(foo);
})();
// smi == undefined
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(2, undefined));
assertFalse(foo(0, undefined));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(2, undefined));
assertFalse(foo(0, undefined));
assertOptimized(foo);
})();
// undefined == smi
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(undefined, 2));
assertFalse(foo(undefined, 0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(undefined, 2));
assertFalse(foo(undefined, 0));
assertOptimized(foo);
})();
// smi == null
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(2, null));
assertFalse(foo(0, null));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(2, null));
assertFalse(foo(0, null));
assertOptimized(foo);
})();
// null == smi
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(null, 2));
assertFalse(foo(null, 0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(null, 2));
assertFalse(foo(null, 0));
assertOptimized(foo);
})();
// smi == oddball
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(2, null));
assertFalse(foo(0, undefined));
assertFalse(foo(0, true));
assertTrue(foo(1, true));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(2, null));
assertFalse(foo(0, undefined));
assertFalse(foo(0, true));
assertTrue(foo(1, true));
assertOptimized(foo);
})();
// oddball == smi
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(null, 2));
assertFalse(foo(undefined, 0));
assertFalse(foo(true, 0));
assertTrue(foo(true, 1));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(null, 2));
assertFalse(foo(undefined, 0));
assertFalse(foo(true, 0));
assertTrue(foo(true, 1));
assertOptimized(foo);
})();
// number == boolean
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(-2.8, true));
assertTrue(foo(1, true));
assertTrue(foo(-0.0, false));
assertFalse(foo(2, false));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(-2.8, true));
assertTrue(foo(1, true));
assertTrue(foo(-0.0, false));
assertFalse(foo(2, false));
assertOptimized(foo);
assertFalse(foo(0, null));
assertUnoptimized(foo);
})();
// boolean == number
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(true, -2.8));
assertTrue(foo(true, 1));
assertTrue(foo(false, -0.0));
assertFalse(foo(false, 2));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(true, -2.8));
assertTrue(foo(true, 1));
assertTrue(foo(false, -0.0));
assertFalse(foo(false, 2));
assertOptimized(foo);
assertFalse(foo(null, 0));
assertUnoptimized(foo);
})();
// number == undefined
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(-2.8, undefined));
assertFalse(foo(-0.0, undefined));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(-2.8, undefined));
assertFalse(foo(-0.0, undefined));
assertOptimized(foo);
})();
// undefined == number
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(undefined, -2.8));
assertFalse(foo(undefined, -0.0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(undefined, -2.8));
assertFalse(foo(undefined, -0.0));
assertOptimized(foo);
})();
// number == null
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(-2.8, null));
assertFalse(foo(-0.0, null));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(-2.8, null));
assertFalse(foo(-0.0, null));
assertOptimized(foo);
})();
// null == number
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(null, -2.8));
assertFalse(foo(null, -0.0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(null, -2.8));
assertFalse(foo(null, -0.0));
assertOptimized(foo);
})();
// number == oddball
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(-2.8, null));
assertFalse(foo(-0.0, undefined));
assertFalse(foo(0, true));
assertTrue(foo(1.0, true));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(-2.8, null));
assertFalse(foo(-0.0, undefined));
assertFalse(foo(0, true));
assertTrue(foo(1.0, true));
assertOptimized(foo);
})();
// oddball == number
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertFalse(foo(null, -2.8));
assertFalse(foo(undefined, -0.0));
assertFalse(foo(true, 0));
assertTrue(foo(true, 1.0));
%OptimizeFunctionOnNextCall(foo);
assertFalse(foo(null, -2.8));
assertFalse(foo(undefined, -0.0));
assertFalse(foo(true, 0));
assertTrue(foo(true, 1.0));
assertOptimized(foo);
})();
// oddball == oddball
(function() {
function foo(a, b) { return a == b; }
%PrepareFunctionForOptimization(foo);
assertTrue(foo(null, null));
assertTrue(foo(undefined, undefined));
assertTrue(foo(false, false));
assertTrue(foo(true, true));
assertTrue(foo(undefined, null));
assertFalse(foo(undefined, false));
assertFalse(foo(null, false));
assertFalse(foo(true, undefined));
assertFalse(foo(true, null));
assertFalse(foo(true, false));
%OptimizeFunctionOnNextCall(foo);
assertTrue(foo(null, null));
assertOptimized(foo);
assertTrue(foo(undefined, undefined));
assertTrue(foo(false, false));
assertTrue(foo(true, true));
assertTrue(foo(undefined, null));
assertFalse(foo(undefined, false));
assertFalse(foo(null, false));
assertFalse(foo(true, undefined));
assertFalse(foo(true, null));
assertFalse(foo(true, false));
assertOptimized(foo);
})();
// Copyright 2020 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.
// Flags: --opt --allow-natives-syntax
(function() {
function test(a, b) {
return a === b;
}
%PrepareFunctionForOptimization(test);
assertTrue(test(undefined, undefined));
assertTrue(test(undefined, undefined));
%OptimizeFunctionOnNextCall(test);
assertTrue(test(undefined, undefined));
})();
(function() {
function test(a, b) {
return a === b;
}
%PrepareFunctionForOptimization(test);
assertTrue(test(true, true));
assertTrue(test(true, true));
%OptimizeFunctionOnNextCall(test);
assertFalse(test(true, 1));
})();
(function() {
function test(a, b) {
return a == b;
}
%PrepareFunctionForOptimization(test);
assertTrue(test(true, true));
assertTrue(test(true, true));
%OptimizeFunctionOnNextCall(test);
assertTrue(test(true, 1));
})();
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