Commit d0b90624 authored by Michael Starzinger's avatar Michael Starzinger Committed by Commit Bot

[turbofan] Implement early lowering based on type hints.

This allows part of typed lowering that is solely based on type-hints to
run as part of the graph construction. The lowering in question does not
inspect types and hence doesn't require the typer to have run before. We
insert the speculative simplied-level operations in favor of the generic
JavaScript-level variants.

R=bmeurer@chromium.org

Change-Id: I5f0549fc1e4ff607622ee9059e6232a32f77db2e
Reviewed-on: https://chromium-review.googlesource.com/442584Reviewed-by: 's avatarBenedikt Meurer <bmeurer@chromium.org>
Commit-Queue: Michael Starzinger <mstarzinger@chromium.org>
Cr-Commit-Position: refs/heads/master@{#43208}
parent 20c7d543
......@@ -1161,6 +1161,8 @@ v8_source_set("v8_base") {
"src/compiler/js-native-context-specialization.h",
"src/compiler/js-operator.cc",
"src/compiler/js-operator.h",
"src/compiler/js-type-hint-lowering.cc",
"src/compiler/js-type-hint-lowering.h",
"src/compiler/js-typed-lowering.cc",
"src/compiler/js-typed-lowering.h",
"src/compiler/jump-threading.cc",
......
......@@ -2748,22 +2748,22 @@ Node* AstGraphBuilder::BuildBinaryOp(Node* left, Node* right, Token::Value op,
BinaryOperationHint hint = BinaryOperationHint::kAny;
switch (op) {
case Token::BIT_OR:
js_op = javascript()->BitwiseOr(hint);
js_op = javascript()->BitwiseOr();
break;
case Token::BIT_AND:
js_op = javascript()->BitwiseAnd(hint);
js_op = javascript()->BitwiseAnd();
break;
case Token::BIT_XOR:
js_op = javascript()->BitwiseXor(hint);
js_op = javascript()->BitwiseXor();
break;
case Token::SHL:
js_op = javascript()->ShiftLeft(hint);
js_op = javascript()->ShiftLeft();
break;
case Token::SAR:
js_op = javascript()->ShiftRight(hint);
js_op = javascript()->ShiftRight();
break;
case Token::SHR:
js_op = javascript()->ShiftRightLogical(hint);
js_op = javascript()->ShiftRightLogical();
break;
case Token::ADD:
js_op = javascript()->Add(hint);
......
......@@ -8,6 +8,7 @@
#include "src/ast/scopes.h"
#include "src/compilation-info.h"
#include "src/compiler/compiler-source-position-table.h"
#include "src/compiler/js-type-hint-lowering.h"
#include "src/compiler/linkage.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
......@@ -1484,12 +1485,39 @@ void BytecodeGraphBuilder::VisitReThrow() {
MergeControlToLeaveFunction(control);
}
void BytecodeGraphBuilder::BuildBinaryOp(const Operator* js_op) {
Node* BytecodeGraphBuilder::TryBuildSimplifiedBinaryOp(const Operator* op,
Node* left, Node* right,
FeedbackSlot slot) {
Node* effect = environment()->GetEffectDependency();
Node* control = environment()->GetControlDependency();
JSTypeHintLowering type_hint_lowering(jsgraph(), feedback_vector());
Reduction early_reduction = type_hint_lowering.ReduceBinaryOperation(
op, left, right, effect, control, slot);
if (early_reduction.Changed()) {
Node* node = early_reduction.replacement();
if (node->op()->EffectOutputCount() > 0) {
environment()->UpdateEffectDependency(node);
}
return node;
}
return nullptr;
}
void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) {
PrepareEagerCheckpoint();
Node* left =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(0));
Node* right = environment()->LookupAccumulator();
Node* node = NewNode(js_op, left, right);
Node* node = nullptr;
FeedbackSlot slot = feedback_vector()->ToSlot(
bytecode_iterator().GetIndexOperand(kBinaryOperationHintIndex));
if (Node* simplified = TryBuildSimplifiedBinaryOp(op, left, right, slot)) {
node = simplified;
} else {
node = NewNode(op, left, right);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
......@@ -1549,41 +1577,44 @@ void BytecodeGraphBuilder::VisitMod() {
}
void BytecodeGraphBuilder::VisitBitwiseOr() {
BuildBinaryOp(javascript()->BitwiseOr(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->BitwiseOr());
}
void BytecodeGraphBuilder::VisitBitwiseXor() {
BuildBinaryOp(javascript()->BitwiseXor(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->BitwiseXor());
}
void BytecodeGraphBuilder::VisitBitwiseAnd() {
BuildBinaryOp(javascript()->BitwiseAnd(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->BitwiseAnd());
}
void BytecodeGraphBuilder::VisitShiftLeft() {
BuildBinaryOp(javascript()->ShiftLeft(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->ShiftLeft());
}
void BytecodeGraphBuilder::VisitShiftRight() {
BuildBinaryOp(javascript()->ShiftRight(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->ShiftRight());
}
void BytecodeGraphBuilder::VisitShiftRightLogical() {
BuildBinaryOp(javascript()->ShiftRightLogical(
GetBinaryOperationHint(kBinaryOperationHintIndex)));
BuildBinaryOp(javascript()->ShiftRightLogical());
}
void BytecodeGraphBuilder::BuildBinaryOpWithImmediate(const Operator* js_op) {
void BytecodeGraphBuilder::BuildBinaryOpWithImmediate(const Operator* op) {
PrepareEagerCheckpoint();
Node* left =
environment()->LookupRegister(bytecode_iterator().GetRegisterOperand(1));
Node* right = jsgraph()->Constant(bytecode_iterator().GetImmediateOperand(0));
Node* node = NewNode(js_op, left, right);
FeedbackSlot slot = feedback_vector()->ToSlot(
bytecode_iterator().GetIndexOperand(kBinaryOperationSmiHintIndex));
Node* node = nullptr;
if (Node* simplified = TryBuildSimplifiedBinaryOp(op, left, right, slot)) {
node = simplified;
} else {
node = NewNode(op, left, right);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
}
......@@ -1598,23 +1629,19 @@ void BytecodeGraphBuilder::VisitSubSmi() {
}
void BytecodeGraphBuilder::VisitBitwiseOrSmi() {
BuildBinaryOpWithImmediate(javascript()->BitwiseOr(
GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
BuildBinaryOpWithImmediate(javascript()->BitwiseOr());
}
void BytecodeGraphBuilder::VisitBitwiseAndSmi() {
BuildBinaryOpWithImmediate(javascript()->BitwiseAnd(
GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
BuildBinaryOpWithImmediate(javascript()->BitwiseAnd());
}
void BytecodeGraphBuilder::VisitShiftLeftSmi() {
BuildBinaryOpWithImmediate(javascript()->ShiftLeft(
GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
BuildBinaryOpWithImmediate(javascript()->ShiftLeft());
}
void BytecodeGraphBuilder::VisitShiftRightSmi() {
BuildBinaryOpWithImmediate(javascript()->ShiftRight(
GetBinaryOperationHint(kBinaryOperationSmiHintIndex)));
BuildBinaryOpWithImmediate(javascript()->ShiftRight());
}
void BytecodeGraphBuilder::VisitInc() {
......
......@@ -155,6 +155,13 @@ class BytecodeGraphBuilder {
void BuildForInNext();
void BuildInvokeIntrinsic();
// Optional early lowering to the simplified operator level. Returns the node
// representing the lowered operation or {nullptr} if no lowering available.
// Note that the result has already been wired into the environment just like
// any other invocation of {NewNode} would do.
Node* TryBuildSimplifiedBinaryOp(const Operator* op, Node* left, Node* right,
FeedbackSlot slot);
// Check the context chain for extensions, for lookup fast paths.
Environment* CheckContextExtensions(uint32_t depth);
......
......@@ -518,13 +518,7 @@ const CreateLiteralParameters& CreateLiteralParametersOf(const Operator* op) {
}
BinaryOperationHint BinaryOperationHintOf(const Operator* op) {
DCHECK(op->opcode() == IrOpcode::kJSBitwiseOr ||
op->opcode() == IrOpcode::kJSBitwiseXor ||
op->opcode() == IrOpcode::kJSBitwiseAnd ||
op->opcode() == IrOpcode::kJSShiftLeft ||
op->opcode() == IrOpcode::kJSShiftRight ||
op->opcode() == IrOpcode::kJSShiftRightLogical ||
op->opcode() == IrOpcode::kJSAdd ||
DCHECK(op->opcode() == IrOpcode::kJSAdd ||
op->opcode() == IrOpcode::kJSSubtract ||
op->opcode() == IrOpcode::kJSMultiply ||
op->opcode() == IrOpcode::kJSDivide ||
......@@ -545,6 +539,12 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
}
#define CACHED_OP_LIST(V) \
V(BitwiseOr, Operator::kNoProperties, 2, 1) \
V(BitwiseXor, Operator::kNoProperties, 2, 1) \
V(BitwiseAnd, Operator::kNoProperties, 2, 1) \
V(ShiftLeft, Operator::kNoProperties, 2, 1) \
V(ShiftRight, Operator::kNoProperties, 2, 1) \
V(ShiftRightLogical, Operator::kNoProperties, 2, 1) \
V(ToInteger, Operator::kNoProperties, 1, 1) \
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
......@@ -569,12 +569,6 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(GetSuperConstructor, Operator::kNoWrite, 1, 1)
#define BINARY_OP_LIST(V) \
V(BitwiseOr) \
V(BitwiseXor) \
V(BitwiseAnd) \
V(ShiftLeft) \
V(ShiftRight) \
V(ShiftRightLogical) \
V(Add) \
V(Subtract) \
V(Multiply) \
......
......@@ -572,12 +572,12 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* LessThanOrEqual(CompareOperationHint hint);
const Operator* GreaterThanOrEqual(CompareOperationHint hint);
const Operator* BitwiseOr(BinaryOperationHint hint);
const Operator* BitwiseXor(BinaryOperationHint hint);
const Operator* BitwiseAnd(BinaryOperationHint hint);
const Operator* ShiftLeft(BinaryOperationHint hint);
const Operator* ShiftRight(BinaryOperationHint hint);
const Operator* ShiftRightLogical(BinaryOperationHint hint);
const Operator* BitwiseOr();
const Operator* BitwiseXor();
const Operator* BitwiseAnd();
const Operator* ShiftLeft();
const Operator* ShiftRight();
const Operator* ShiftRightLogical();
const Operator* Add(BinaryOperationHint hint);
const Operator* Subtract(BinaryOperationHint hint);
const Operator* Multiply(BinaryOperationHint hint);
......
// Copyright 2017 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.
#include "src/compiler/js-type-hint-lowering.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/feedback-vector.h"
#include "src/type-hints.h"
namespace v8 {
namespace internal {
namespace compiler {
class JSSpeculativeBinopBuilder final {
public:
JSSpeculativeBinopBuilder(JSTypeHintLowering* lowering, const Operator* op,
Node* left, Node* right, Node* effect,
Node* control, FeedbackSlot slot)
: lowering_(lowering),
op_(op),
left_(left),
right_(right),
effect_(effect),
control_(control),
slot_(slot) {}
BinaryOperationHint GetBinaryOperationHint() {
DCHECK_EQ(FeedbackSlotKind::kBinaryOp, feedback_vector()->GetKind(slot_));
BinaryOpICNexus nexus(feedback_vector(), slot_);
return nexus.GetBinaryOperationFeedback();
}
bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
switch (GetBinaryOperationHint()) {
case BinaryOperationHint::kSignedSmall:
*hint = NumberOperationHint::kSignedSmall;
return true;
case BinaryOperationHint::kSigned32:
*hint = NumberOperationHint::kSigned32;
return true;
case BinaryOperationHint::kNumberOrOddball:
*hint = NumberOperationHint::kNumberOrOddball;
return true;
case BinaryOperationHint::kAny:
case BinaryOperationHint::kNone:
case BinaryOperationHint::kString:
break;
}
return false;
}
const Operator* SpeculativeNumberOp(NumberOperationHint hint) {
switch (op_->opcode()) {
case IrOpcode::kJSAdd:
return simplified()->SpeculativeNumberAdd(hint);
case IrOpcode::kJSSubtract:
return simplified()->SpeculativeNumberSubtract(hint);
case IrOpcode::kJSMultiply:
return simplified()->SpeculativeNumberMultiply(hint);
case IrOpcode::kJSDivide:
return simplified()->SpeculativeNumberDivide(hint);
case IrOpcode::kJSModulus:
return simplified()->SpeculativeNumberModulus(hint);
case IrOpcode::kJSBitwiseAnd:
return simplified()->SpeculativeNumberBitwiseAnd(hint);
case IrOpcode::kJSBitwiseOr:
return simplified()->SpeculativeNumberBitwiseOr(hint);
case IrOpcode::kJSBitwiseXor:
return simplified()->SpeculativeNumberBitwiseXor(hint);
case IrOpcode::kJSShiftLeft:
return simplified()->SpeculativeNumberShiftLeft(hint);
case IrOpcode::kJSShiftRight:
return simplified()->SpeculativeNumberShiftRight(hint);
case IrOpcode::kJSShiftRightLogical:
return simplified()->SpeculativeNumberShiftRightLogical(hint);
default:
break;
}
UNREACHABLE();
return nullptr;
}
Node* BuildSpeculativeOperator(const Operator* op) {
DCHECK_EQ(2, op->ValueInputCount());
DCHECK_EQ(1, op->EffectInputCount());
DCHECK_EQ(1, op->ControlInputCount());
DCHECK_EQ(false, OperatorProperties::HasFrameStateInput(op));
DCHECK_EQ(false, OperatorProperties::HasContextInput(op));
DCHECK_EQ(1, op->EffectOutputCount());
DCHECK_EQ(0, op->ControlOutputCount());
return graph()->NewNode(op, left_, right_, effect_, control_);
}
JSGraph* jsgraph() const { return lowering_->jsgraph(); }
Graph* graph() const { return jsgraph()->graph(); }
JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
CommonOperatorBuilder* common() { return jsgraph()->common(); }
const Handle<FeedbackVector>& feedback_vector() const {
return lowering_->feedback_vector();
}
private:
JSTypeHintLowering* lowering_;
const Operator* op_;
Node* left_;
Node* right_;
Node* effect_;
Node* control_;
FeedbackSlot slot_;
};
JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
Handle<FeedbackVector> feedback_vector)
: jsgraph_(jsgraph), feedback_vector_(feedback_vector) {}
Reduction JSTypeHintLowering::ReduceBinaryOperation(const Operator* op,
Node* left, Node* right,
Node* effect, Node* control,
FeedbackSlot slot) {
switch (op->opcode()) {
case IrOpcode::kJSBitwiseOr:
case IrOpcode::kJSBitwiseXor:
case IrOpcode::kJSBitwiseAnd:
case IrOpcode::kJSShiftLeft:
case IrOpcode::kJSShiftRight:
case IrOpcode::kJSShiftRightLogical: {
JSSpeculativeBinopBuilder b(this, op, left, right, effect, control, slot);
NumberOperationHint hint;
if (b.GetBinaryNumberOperationHint(&hint)) {
Node* node = b.BuildSpeculativeOperator(b.SpeculativeNumberOp(hint));
return Reduction(node);
}
break;
}
case IrOpcode::kJSAdd:
case IrOpcode::kJSSubtract:
case IrOpcode::kJSMultiply:
case IrOpcode::kJSDivide:
case IrOpcode::kJSModulus:
// TODO(mstarzinger): Implement speculative lowering.
break;
default:
UNREACHABLE();
break;
}
return Reduction();
}
} // namespace compiler
} // namespace internal
} // namespace v8
// Copyright 2017 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.
#ifndef V8_COMPILER_JS_TYPE_HINT_LOWERING_H_
#define V8_COMPILER_JS_TYPE_HINT_LOWERING_H_
#include "src/compiler/graph-reducer.h"
#include "src/handles.h"
namespace v8 {
namespace internal {
namespace compiler {
// Forward declarations.
class JSGraph;
// The type-hint lowering consumes feedback about data operations (i.e. unary
// and binary operations) to emit nodes using speculative simplified operators
// in favor of the generic JavaScript operators.
//
// This lowering is implemented as an early reduction and can be applied before
// nodes are placed into the initial graph. It provides the ability to shortcut
// the JavaScript-level operators and directly emit simplified-level operators
// even during initial graph building. This is the reason this lowering doesn't
// follow the interface of the reducer framework used after graph construction.
class JSTypeHintLowering {
public:
JSTypeHintLowering(JSGraph* jsgraph, Handle<FeedbackVector> feedback_vector);
// Potential reduction of binary (arithmetic, logical and shift) operations.
Reduction ReduceBinaryOperation(const Operator* op, Node* left, Node* right,
Node* effect, Node* control,
FeedbackSlot slot);
private:
friend class JSSpeculativeBinopBuilder;
JSGraph* jsgraph() const { return jsgraph_; }
const Handle<FeedbackVector>& feedback_vector() const {
return feedback_vector_;
}
JSGraph* jsgraph_;
Handle<FeedbackVector> feedback_vector_;
DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering);
};
} // namespace compiler
} // namespace internal
} // namespace v8
#endif // V8_COMPILER_JS_TYPE_HINT_LOWERING_H_
......@@ -624,11 +624,6 @@ Reduction JSTypedLowering::ReduceNumberBinop(Node* node) {
Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
JSBinopReduction r(this, node);
NumberOperationHint hint;
if (r.GetBinaryNumberOperationHint(&hint)) {
return r.ChangeToSpeculativeOperator(r.SpeculativeNumberOp(hint),
Type::Signed32());
}
if (r.BothInputsAre(Type::PlainPrimitive()) ||
!(flags() & kDeoptimizationEnabled)) {
r.ConvertInputsToNumber();
......@@ -640,12 +635,6 @@ Reduction JSTypedLowering::ReduceInt32Binop(Node* node) {
Reduction JSTypedLowering::ReduceUI32Shift(Node* node, Signedness signedness) {
JSBinopReduction r(this, node);
NumberOperationHint hint;
if (r.GetBinaryNumberOperationHint(&hint)) {
return r.ChangeToSpeculativeOperator(
r.SpeculativeNumberOp(hint),
signedness == kUnsigned ? Type::Unsigned32() : Type::Signed32());
}
if (r.BothInputsAre(Type::PlainPrimitive()) ||
!(flags() & kDeoptimizationEnabled)) {
r.ConvertInputsToNumber();
......
......@@ -648,6 +648,8 @@
'compiler/js-native-context-specialization.h',
'compiler/js-operator.cc',
'compiler/js-operator.h',
'compiler/js-type-hint-lowering.cc',
'compiler/js-type-hint-lowering.h',
'compiler/js-typed-lowering.cc',
'compiler/js-typed-lowering.h',
'compiler/jump-threading.cc',
......
......@@ -310,11 +310,11 @@ class JSBitwiseShiftTypedLoweringTester : public JSTypedLoweringTester {
public:
JSBitwiseShiftTypedLoweringTester() : JSTypedLoweringTester() {
int i = 0;
set(i++, javascript.ShiftLeft(binop_hints), true);
set(i++, javascript.ShiftLeft(), true);
set(i++, simplified.NumberShiftLeft(), false);
set(i++, javascript.ShiftRight(binop_hints), true);
set(i++, javascript.ShiftRight(), true);
set(i++, simplified.NumberShiftRight(), false);
set(i++, javascript.ShiftRightLogical(binop_hints), false);
set(i++, javascript.ShiftRightLogical(), false);
set(i++, simplified.NumberShiftRightLogical(), false);
}
static const int kNumberOps = 6;
......@@ -366,11 +366,11 @@ class JSBitwiseTypedLoweringTester : public JSTypedLoweringTester {
public:
JSBitwiseTypedLoweringTester() : JSTypedLoweringTester() {
int i = 0;
set(i++, javascript.BitwiseOr(binop_hints), true);
set(i++, javascript.BitwiseOr(), true);
set(i++, simplified.NumberBitwiseOr(), true);
set(i++, javascript.BitwiseXor(binop_hints), true);
set(i++, javascript.BitwiseXor(), true);
set(i++, simplified.NumberBitwiseXor(), true);
set(i++, javascript.BitwiseAnd(binop_hints), true);
set(i++, javascript.BitwiseAnd(), true);
set(i++, simplified.NumberBitwiseAnd(), true);
}
static const int kNumberOps = 6;
......
......@@ -265,27 +265,27 @@ TEST_F(TyperTest, TypeJSModulus) {
TEST_F(TyperTest, TypeJSBitwiseOr) {
TestBinaryBitOp(javascript_.BitwiseOr(hints_), bit_or);
TestBinaryBitOp(javascript_.BitwiseOr(), bit_or);
}
TEST_F(TyperTest, TypeJSBitwiseAnd) {
TestBinaryBitOp(javascript_.BitwiseAnd(hints_), bit_and);
TestBinaryBitOp(javascript_.BitwiseAnd(), bit_and);
}
TEST_F(TyperTest, TypeJSBitwiseXor) {
TestBinaryBitOp(javascript_.BitwiseXor(hints_), bit_xor);
TestBinaryBitOp(javascript_.BitwiseXor(), bit_xor);
}
TEST_F(TyperTest, TypeJSShiftLeft) {
TestBinaryBitOp(javascript_.ShiftLeft(hints_), shift_left);
TestBinaryBitOp(javascript_.ShiftLeft(), shift_left);
}
TEST_F(TyperTest, TypeJSShiftRight) {
TestBinaryBitOp(javascript_.ShiftRight(hints_), shift_right);
TestBinaryBitOp(javascript_.ShiftRight(), shift_right);
}
......@@ -356,9 +356,9 @@ TEST_BINARY_MONOTONICITY(LessThanOrEqual)
TEST_BINARY_MONOTONICITY(GreaterThanOrEqual)
#undef TEST_BINARY_MONOTONICITY
#define TEST_BINARY_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name(BinaryOperationHint::kAny)); \
#define TEST_BINARY_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name()); \
}
TEST_BINARY_MONOTONICITY(BitwiseOr)
TEST_BINARY_MONOTONICITY(BitwiseXor)
......@@ -366,6 +366,12 @@ TEST_BINARY_MONOTONICITY(BitwiseAnd)
TEST_BINARY_MONOTONICITY(ShiftLeft)
TEST_BINARY_MONOTONICITY(ShiftRight)
TEST_BINARY_MONOTONICITY(ShiftRightLogical)
#undef TEST_BINARY_MONOTONICITY
#define TEST_BINARY_MONOTONICITY(name) \
TEST_F(TyperTest, Monotonicity_##name) { \
TestBinaryMonotonicity(javascript_.name(BinaryOperationHint::kAny)); \
}
TEST_BINARY_MONOTONICITY(Add)
TEST_BINARY_MONOTONICITY(Subtract)
TEST_BINARY_MONOTONICITY(Multiply)
......
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