[turbofan] Introduce ToBooleanHints on ToBoolean operators.

Extract ToBoolean hints from the fullcodegen code object and put them
into the ToBoolean nodes created by the AstGraphBuilder.  We currently
do not yet consume this feedback, that will be done in a followup CL.

R=mstarzinger@chromium.org
BUG=v8:4583
LOG=n

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

Cr-Commit-Position: refs/heads/master@{#32576}
This commit is contained in:
bmeurer 2015-12-03 08:48:06 -08:00 committed by Commit bot
parent e667ae4ba9
commit 9298b43029
12 changed files with 177 additions and 37 deletions

View File

@ -5,6 +5,8 @@
#ifndef V8_BASE_FLAGS_H_
#define V8_BASE_FLAGS_H_
#include <cstddef>
#include "src/base/compiler-specific.h"
namespace v8 {
@ -30,6 +32,13 @@ class Flags final {
: mask_(static_cast<S>(flag)) {}
explicit Flags(mask_type mask) : mask_(static_cast<S>(mask)) {}
bool operator==(flag_type flag) const {
return mask_ == static_cast<S>(flag);
}
bool operator!=(flag_type flag) const {
return mask_ != static_cast<S>(flag);
}
Flags& operator&=(const Flags& flags) {
mask_ &= flags.mask_;
return *this;
@ -60,6 +69,8 @@ class Flags final {
operator mask_type() const { return mask_; }
bool operator!() const { return !mask_; }
friend size_t hash_value(const Flags& flags) { return flags.mask_; }
private:
mask_type mask_;
};

View File

@ -94,11 +94,14 @@ class AstGraphBuilder::AstValueContext final : public AstContext {
// Context to evaluate expression for a condition value (and side effects).
class AstGraphBuilder::AstTestContext final : public AstContext {
public:
explicit AstTestContext(AstGraphBuilder* owner)
: AstContext(owner, Expression::kTest) {}
AstTestContext(AstGraphBuilder* owner, TypeFeedbackId feedback_id)
: AstContext(owner, Expression::kTest), feedback_id_(feedback_id) {}
~AstTestContext() final;
void ProduceValue(Node* value) final;
Node* ConsumeValue() final;
private:
TypeFeedbackId const feedback_id_;
};
@ -927,7 +930,7 @@ void AstGraphBuilder::AstValueContext::ProduceValue(Node* value) {
void AstGraphBuilder::AstTestContext::ProduceValue(Node* value) {
environment()->Push(owner()->BuildToBoolean(value));
environment()->Push(owner()->BuildToBoolean(value, feedback_id_));
}
@ -1034,7 +1037,7 @@ void AstGraphBuilder::VisitForEffect(Expression* expr) {
void AstGraphBuilder::VisitForTest(Expression* expr) {
AstTestContext for_condition(this);
AstTestContext for_condition(this, expr->test_id());
if (!CheckStackOverflow()) {
expr->Accept(this);
} else {
@ -3011,7 +3014,7 @@ void AstGraphBuilder::VisitTypeof(UnaryOperation* expr) {
void AstGraphBuilder::VisitNot(UnaryOperation* expr) {
VisitForValue(expr->expression());
Node* operand = environment()->Pop();
Node* input = BuildToBoolean(operand);
Node* input = BuildToBoolean(operand, expr->expression()->test_id());
Node* value = NewNode(common()->Select(kMachAnyTagged), input,
jsgraph()->FalseConstant(), jsgraph()->TrueConstant());
ast_context()->ProduceValue(value);
@ -3030,7 +3033,7 @@ void AstGraphBuilder::VisitLogicalExpression(BinaryOperation* expr) {
IfBuilder compare_if(this);
VisitForValue(expr->left());
Node* condition = environment()->Top();
compare_if.If(BuildToBoolean(condition));
compare_if.If(BuildToBoolean(condition, expr->left()->test_id()));
compare_if.Then();
if (is_logical_and) {
environment()->Pop();
@ -3682,9 +3685,14 @@ Node* AstGraphBuilder::BuildLoadFeedbackVector() {
}
Node* AstGraphBuilder::BuildToBoolean(Node* input) {
Node* AstGraphBuilder::BuildToBoolean(Node* input, TypeFeedbackId feedback_id) {
if (Node* node = TryFastToBoolean(input)) return node;
return NewNode(javascript()->ToBoolean(), input);
ToBooleanHints hints;
if (!type_hint_analysis_ ||
!type_hint_analysis_->GetToBooleanHints(feedback_id, &hints)) {
hints = ToBooleanHint::kAny;
}
return NewNode(javascript()->ToBoolean(hints), input);
}

View File

@ -321,7 +321,7 @@ class AstGraphBuilder : public AstVisitor {
Node* BuildLoadImmutableObjectField(Node* object, int offset);
// Builders for automatic type conversion.
Node* BuildToBoolean(Node* input);
Node* BuildToBoolean(Node* input, TypeFeedbackId feedback_id);
Node* BuildToName(Node* input, BailoutId bailout_id);
Node* BuildToObject(Node* input, BailoutId bailout_id);

View File

@ -971,8 +971,8 @@ void BytecodeGraphBuilder::VisitDec(
void BytecodeGraphBuilder::VisitLogicalNot(
const interpreter::BytecodeArrayIterator& iterator) {
Node* value =
NewNode(javascript()->ToBoolean(), environment()->LookupAccumulator());
Node* value = NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
environment()->LookupAccumulator());
Node* node = NewNode(common()->Select(kMachAnyTagged), value,
jsgraph()->FalseConstant(), jsgraph()->TrueConstant());
environment()->BindAccumulator(node);
@ -1094,7 +1094,7 @@ void BytecodeGraphBuilder::BuildCastOperator(
void BytecodeGraphBuilder::VisitToBoolean(
const interpreter::BytecodeArrayIterator& iterator) {
BuildCastOperator(javascript()->ToBoolean(), iterator);
BuildCastOperator(javascript()->ToBoolean(ToBooleanHint::kAny), iterator);
}

View File

@ -46,6 +46,12 @@ ConvertReceiverMode ConvertReceiverModeOf(Operator const* op) {
}
ToBooleanHints ToBooleanHintsOf(Operator const* op) {
DCHECK_EQ(IrOpcode::kJSToBoolean, op->opcode());
return OpParameter<ToBooleanHints>(op);
}
size_t hash_value(TailCallMode mode) {
return base::hash_value(static_cast<unsigned>(mode));
}
@ -486,7 +492,6 @@ const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op) {
V(NotEqual, Operator::kNoProperties, 2, 1) \
V(StrictEqual, Operator::kNoThrow, 2, 1) \
V(StrictNotEqual, Operator::kNoThrow, 2, 1) \
V(ToBoolean, Operator::kEliminatable, 1, 1) \
V(ToNumber, Operator::kNoProperties, 1, 1) \
V(ToString, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
@ -717,6 +722,16 @@ const Operator* JSOperatorBuilder::Modulus(LanguageMode language_mode,
}
const Operator* JSOperatorBuilder::ToBoolean(ToBooleanHints hints) {
// TODO(turbofan): Cache most important versions of this operator.
return new (zone()) Operator1<ToBooleanHints>( //--
IrOpcode::kJSToBoolean, Operator::kEliminatable, // opcode
"JSToBoolean", // name
1, 1, 0, 1, 1, 0, // inputs/outputs
hints); // parameter
}
const Operator* JSOperatorBuilder::CallFunction(
size_t arity, LanguageMode language_mode, VectorSlotPair const& feedback,
ConvertReceiverMode convert_mode, TailCallMode tail_call_mode) {

View File

@ -44,7 +44,11 @@ size_t hash_value(VectorSlotPair const&);
// The ConvertReceiverMode is used as parameter by JSConvertReceiver operators.
ConvertReceiverMode ConvertReceiverModeOf(const Operator* op);
ConvertReceiverMode ConvertReceiverModeOf(Operator const* op);
// The ToBooleanHints are used as parameter by JSToBoolean operators.
ToBooleanHints ToBooleanHintsOf(Operator const* op);
// Defines whether tail call optimization is allowed.
@ -497,7 +501,7 @@ class JSOperatorBuilder final : public ZoneObject {
const Operator* Modulus(LanguageMode language_mode,
BinaryOperationHints hints);
const Operator* ToBoolean();
const Operator* ToBoolean(ToBooleanHints hints);
const Operator* ToNumber();
const Operator* ToString();
const Operator* ToName();

View File

@ -5,6 +5,7 @@
#include "src/compiler/type-hint-analyzer.h"
#include "src/assembler.h"
#include "src/code-stubs.h"
#include "src/compiler/type-hints.h"
#include "src/ic/ic-state.h"
@ -41,6 +42,32 @@ bool TypeHintAnalysis::GetBinaryOperationHints(
}
bool TypeHintAnalysis::GetToBooleanHints(TypeFeedbackId id,
ToBooleanHints* hints) const {
auto i = infos_.find(id);
if (i == infos_.end()) return false;
Handle<Code> code = i->second;
DCHECK_EQ(Code::TO_BOOLEAN_IC, code->kind());
ToBooleanStub stub(code->GetIsolate(), code->extra_ic_state());
// TODO(bmeurer): Replace ToBooleanStub::Types with ToBooleanHints.
#define ASSERT_COMPATIBLE(NAME, Name) \
STATIC_ASSERT(1 << ToBooleanStub::NAME == \
static_cast<int>(ToBooleanHint::k##Name))
ASSERT_COMPATIBLE(UNDEFINED, Undefined);
ASSERT_COMPATIBLE(BOOLEAN, Boolean);
ASSERT_COMPATIBLE(NULL_TYPE, Null);
ASSERT_COMPATIBLE(SMI, SmallInteger);
ASSERT_COMPATIBLE(SPEC_OBJECT, Receiver);
ASSERT_COMPATIBLE(STRING, String);
ASSERT_COMPATIBLE(SYMBOL, Symbol);
ASSERT_COMPATIBLE(HEAP_NUMBER, HeapNumber);
ASSERT_COMPATIBLE(SIMD_VALUE, SimdValue);
#undef ASSERT_COMPATIBLE
*hints = ToBooleanHints(stub.types().ToIntegral());
return true;
}
TypeHintAnalysis* TypeHintAnalyzer::Analyze(Handle<Code> code) {
DisallowHeapAllocation no_gc;
TypeHintAnalysis::Infos infos(zone());
@ -51,13 +78,13 @@ TypeHintAnalysis* TypeHintAnalyzer::Analyze(Handle<Code> code) {
Address target_address = rinfo->target_address();
Code* target = Code::GetCodeFromTargetAddress(target_address);
switch (target->kind()) {
case Code::BINARY_OP_IC: {
case Code::BINARY_OP_IC:
case Code::TO_BOOLEAN_IC: {
// Add this feedback to the {infos}.
TypeFeedbackId id(static_cast<unsigned>(rinfo->data()));
infos.insert(std::make_pair(id, handle(target, isolate)));
break;
}
default:
// Ignore the remaining code objects.
break;

View File

@ -5,18 +5,14 @@
#ifndef V8_COMPILER_TYPE_HINT_ANALYZER_H_
#define V8_COMPILER_TYPE_HINT_ANALYZER_H_
#include "src/compiler/type-hints.h"
#include "src/handles.h"
#include "src/utils.h"
#include "src/zone-containers.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class BinaryOperationHints;
// The result of analyzing type hints.
class TypeHintAnalysis final : public ZoneObject {
public:
@ -26,6 +22,7 @@ class TypeHintAnalysis final : public ZoneObject {
bool GetBinaryOperationHints(TypeFeedbackId id,
BinaryOperationHints* hints) const;
bool GetToBooleanHints(TypeFeedbackId id, ToBooleanHints* hints) const;
private:
Infos const infos_;

View File

@ -32,6 +32,52 @@ std::ostream& operator<<(std::ostream& os, BinaryOperationHints hints) {
return os << hints.left() << "*" << hints.right() << "->" << hints.result();
}
std::ostream& operator<<(std::ostream& os, ToBooleanHint hint) {
switch (hint) {
case ToBooleanHint::kNone:
return os << "None";
case ToBooleanHint::kUndefined:
return os << "Undefined";
case ToBooleanHint::kBoolean:
return os << "Boolean";
case ToBooleanHint::kNull:
return os << "Null";
case ToBooleanHint::kSmallInteger:
return os << "SmallInteger";
case ToBooleanHint::kReceiver:
return os << "Receiver";
case ToBooleanHint::kString:
return os << "String";
case ToBooleanHint::kSymbol:
return os << "Symbol";
case ToBooleanHint::kHeapNumber:
return os << "HeapNumber";
case ToBooleanHint::kSimdValue:
return os << "SimdValue";
case ToBooleanHint::kAny:
return os << "Any";
}
UNREACHABLE();
return os;
}
std::ostream& operator<<(std::ostream& os, ToBooleanHints hints) {
if (hints == ToBooleanHint::kAny) return os << "Any";
if (hints == ToBooleanHint::kNone) return os << "None";
bool first = true;
for (ToBooleanHints::mask_type i = 0; i < sizeof(i) * CHAR_BIT; ++i) {
ToBooleanHint const hint = static_cast<ToBooleanHint>(1u << i);
if (hints & hint) {
if (!first) os << "|";
first = false;
os << hint;
}
}
return os;
}
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -5,6 +5,7 @@
#ifndef V8_COMPILER_TYPE_HINTS_H_
#define V8_COMPILER_TYPE_HINTS_H_
#include "src/base/flags.h"
#include "src/utils.h"
namespace v8 {
@ -51,6 +52,31 @@ class BinaryOperationHints final {
std::ostream& operator<<(std::ostream&, BinaryOperationHints::Hint);
std::ostream& operator<<(std::ostream&, BinaryOperationHints);
// Type hints for the ToBoolean type conversion.
enum class ToBooleanHint : uint16_t {
kNone = 0u,
kUndefined = 1u << 0,
kBoolean = 1u << 1,
kNull = 1u << 2,
kSmallInteger = 1u << 3,
kReceiver = 1u << 4,
kString = 1u << 5,
kSymbol = 1u << 6,
kHeapNumber = 1u << 7,
kSimdValue = 1u << 8,
kAny = kUndefined | kBoolean | kNull | kSmallInteger | kReceiver | kString |
kSymbol | kHeapNumber | kSimdValue
};
std::ostream& operator<<(std::ostream&, ToBooleanHint);
typedef base::Flags<ToBooleanHint, uint16_t> ToBooleanHints;
std::ostream& operator<<(std::ostream&, ToBooleanHints);
DEFINE_OPERATORS_FOR_FLAGS(ToBooleanHints)
} // namespace compiler
} // namespace internal
} // namespace v8

View File

@ -71,7 +71,6 @@ const SharedOperator kSharedOperators[] = {
SHARED(NotEqual, Operator::kNoProperties, 2, 1, 1, 1, 1, 1, 2),
SHARED(StrictEqual, Operator::kNoThrow, 2, 0, 1, 1, 1, 1, 0),
SHARED(StrictNotEqual, Operator::kNoThrow, 2, 0, 1, 1, 1, 1, 0),
SHARED(ToBoolean, Operator::kEliminatable, 1, 0, 1, 0, 1, 1, 0),
SHARED(ToNumber, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToString, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),
SHARED(ToName, Operator::kNoProperties, 1, 1, 1, 1, 1, 1, 2),

View File

@ -224,8 +224,9 @@ TEST_F(JSTypedLoweringTest, ParameterWithUndefined) {
TEST_F(JSTypedLoweringTest, JSToBooleanWithBoolean) {
Node* input = Parameter(Type::Boolean(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_EQ(input, r.replacement());
}
@ -253,8 +254,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithFalsish) {
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsFalseConstant());
}
@ -268,8 +270,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
zone()),
0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
@ -278,8 +281,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithTruish) {
TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
Node* input = Parameter(Type::Range(1, V8_INFINITY, zone()), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(), IsTrueConstant());
}
@ -288,8 +292,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithNonZeroPlainNumber) {
TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
Node* input = Parameter(Type::OrderedNumber(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(r.replacement(),
IsBooleanNot(IsNumberEqual(input, IsNumberConstant(0.0))));
@ -299,8 +304,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithOrderedNumber) {
TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
Node* input = Parameter(Type::String(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_TRUE(r.Changed());
EXPECT_THAT(
r.replacement(),
@ -313,8 +319,9 @@ TEST_F(JSTypedLoweringTest, JSToBooleanWithString) {
TEST_F(JSTypedLoweringTest, JSToBooleanWithAny) {
Node* input = Parameter(Type::Any(), 0);
Node* context = Parameter(Type::Any(), 1);
Reduction r = Reduce(graph()->NewNode(javascript()->ToBoolean(), input,
context, graph()->start()));
Reduction r =
Reduce(graph()->NewNode(javascript()->ToBoolean(ToBooleanHint::kAny),
input, context, graph()->start()));
ASSERT_FALSE(r.Changed());
}