[nci] Add Unary/Binary/Compare builtins with feedback

This CL adds the new _WithFeedback variant of unary, binary, and
compare operation builtins. Existing logic to do these operations is
refactored s.t. it can be used by both ignition bytecode handlers and
the new builtins.

Note that the new builtins are not yet used. Follow-up CLs will hook
them into generic lowering.

Bug: v8:8888
Change-Id: Id77dbe74bdf3b3806b2aefdf1abe52c3d165a3a3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2208862
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Reviewed-by: Igor Sheludko <ishell@chromium.org>
Reviewed-by: Ross McIlroy <rmcilroy@chromium.org>
Cr-Commit-Position: refs/heads/master@{#67956}
This commit is contained in:
Jakob Gruber 2020-05-25 14:29:44 +02:00 committed by Commit Bot
parent d4bb820827
commit a0e7456d38
8 changed files with 576 additions and 241 deletions

View File

@ -1683,6 +1683,8 @@ v8_source_set("v8_initializers") {
"src/ic/binary-op-assembler.h",
"src/ic/keyed-store-generic.cc",
"src/ic/keyed-store-generic.h",
"src/ic/unary-op-assembler.cc",
"src/ic/unary-op-assembler.h",
"src/interpreter/interpreter-assembler.cc",
"src/interpreter/interpreter-assembler.h",
"src/interpreter/interpreter-generator.cc",

View File

@ -608,6 +608,34 @@ namespace internal {
TFC(SameValue, Compare) \
TFC(SameValueNumbersOnly, Compare) \
\
/* Binary ops with feedback collection */ \
TFC(Add_WithFeedback, BinaryOp_WithFeedback) \
TFC(Subtract_WithFeedback, BinaryOp_WithFeedback) \
TFC(Multiply_WithFeedback, BinaryOp_WithFeedback) \
TFC(Divide_WithFeedback, BinaryOp_WithFeedback) \
TFC(Modulus_WithFeedback, BinaryOp_WithFeedback) \
TFC(Exponentiate_WithFeedback, BinaryOp_WithFeedback) \
TFC(BitwiseAnd_WithFeedback, BinaryOp_WithFeedback) \
TFC(BitwiseOr_WithFeedback, BinaryOp_WithFeedback) \
TFC(BitwiseXor_WithFeedback, BinaryOp_WithFeedback) \
TFC(ShiftLeft_WithFeedback, BinaryOp_WithFeedback) \
TFC(ShiftRight_WithFeedback, BinaryOp_WithFeedback) \
TFC(ShiftRightLogical_WithFeedback, BinaryOp_WithFeedback) \
\
/* Compare ops with feedback collection */ \
TFC(Equal_WithFeedback, Compare_WithFeedback) \
TFC(StrictEqual_WithFeedback, Compare_WithFeedback) \
TFC(LessThan_WithFeedback, Compare_WithFeedback) \
TFC(GreaterThan_WithFeedback, Compare_WithFeedback) \
TFC(LessThanOrEqual_WithFeedback, Compare_WithFeedback) \
TFC(GreaterThanOrEqual_WithFeedback, Compare_WithFeedback) \
\
/* Unary ops with feedback collection */ \
TFC(BitwiseNot_WithFeedback, UnaryOp_WithFeedback) \
TFC(Decrement_WithFeedback, UnaryOp_WithFeedback) \
TFC(Increment_WithFeedback, UnaryOp_WithFeedback) \
TFC(Negate_WithFeedback, UnaryOp_WithFeedback) \
\
/* Object */ \
/* ES #sec-object-constructor */ \
TFJ(ObjectConstructor, kDontAdaptArgumentsSentinel) \

View File

@ -6,6 +6,7 @@
#include "src/builtins/builtins.h"
#include "src/codegen/code-stub-assembler.h"
#include "src/ic/binary-op-assembler.h"
#include "src/ic/unary-op-assembler.h"
namespace v8 {
namespace internal {
@ -245,5 +246,109 @@ TF_BUILTIN(Add, AddStubAssembler) {
}
}
#define DEF_BINOP(Name, Generator) \
TF_BUILTIN(Name, CodeStubAssembler) { \
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); \
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); \
TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \
TNode<HeapObject> maybe_feedback_vector = \
CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \
TNode<UintPtrT> slot = \
UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \
\
BinaryOpAssembler binop_asm(state()); \
TNode<Object> result = binop_asm.Generator(context, lhs, rhs, slot, \
maybe_feedback_vector, false); \
\
Return(result); \
}
DEF_BINOP(Add_WithFeedback, Generate_AddWithFeedback)
DEF_BINOP(Subtract_WithFeedback, Generate_SubtractWithFeedback)
DEF_BINOP(Multiply_WithFeedback, Generate_MultiplyWithFeedback)
DEF_BINOP(Divide_WithFeedback, Generate_DivideWithFeedback)
DEF_BINOP(Modulus_WithFeedback, Generate_ModulusWithFeedback)
DEF_BINOP(Exponentiate_WithFeedback, Generate_ExponentiateWithFeedback)
DEF_BINOP(BitwiseOr_WithFeedback, Generate_BitwiseOrWithFeedback)
DEF_BINOP(BitwiseXor_WithFeedback, Generate_BitwiseXorWithFeedback)
DEF_BINOP(BitwiseAnd_WithFeedback, Generate_BitwiseAndWithFeedback)
DEF_BINOP(ShiftLeft_WithFeedback, Generate_ShiftLeftWithFeedback)
DEF_BINOP(ShiftRight_WithFeedback, Generate_ShiftRightWithFeedback)
DEF_BINOP(ShiftRightLogical_WithFeedback,
Generate_ShiftRightLogicalWithFeedback)
#undef DEF_BINOP
#define DEF_UNOP(Name, Generator) \
TF_BUILTIN(Name, CodeStubAssembler) { \
TNode<Object> value = CAST(Parameter(Descriptor::kValue)); \
TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \
TNode<HeapObject> maybe_feedback_vector = \
CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \
TNode<UintPtrT> slot = \
UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \
\
UnaryOpAssembler a(state()); \
TNode<Object> result = \
a.Generator(context, value, slot, maybe_feedback_vector); \
\
Return(result); \
}
DEF_UNOP(BitwiseNot_WithFeedback, Generate_BitwiseNotWithFeedback)
DEF_UNOP(Decrement_WithFeedback, Generate_DecrementWithFeedback)
DEF_UNOP(Increment_WithFeedback, Generate_IncrementWithFeedback)
DEF_UNOP(Negate_WithFeedback, Generate_NegateWithFeedback)
#undef DEF_UNOP
#define DEF_COMPARE(Name) \
TF_BUILTIN(Name##_WithFeedback, CodeStubAssembler) { \
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft)); \
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight)); \
TNode<Context> context = CAST(Parameter(Descriptor::kContext)); \
TNode<HeapObject> maybe_feedback_vector = \
CAST(Parameter(Descriptor::kMaybeFeedbackVector)); \
TNode<UintPtrT> slot = \
UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot)); \
\
TVARIABLE(Smi, var_type_feedback); \
TNode<Oddball> result = RelationalComparison(Operation::k##Name, lhs, rhs, \
context, &var_type_feedback); \
UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot); \
\
Return(result); \
}
DEF_COMPARE(LessThan)
DEF_COMPARE(LessThanOrEqual)
DEF_COMPARE(GreaterThan)
DEF_COMPARE(GreaterThanOrEqual)
#undef DEF_COMPARE
TF_BUILTIN(Equal_WithFeedback, CodeStubAssembler) {
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft));
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight));
TNode<Context> context = CAST(Parameter(Descriptor::kContext));
TNode<HeapObject> maybe_feedback_vector =
CAST(Parameter(Descriptor::kMaybeFeedbackVector));
TNode<UintPtrT> slot = UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot));
TVARIABLE(Smi, var_type_feedback);
TNode<Oddball> result = Equal(lhs, rhs, context, &var_type_feedback);
UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot);
Return(result);
}
TF_BUILTIN(StrictEqual_WithFeedback, CodeStubAssembler) {
TNode<Object> lhs = CAST(Parameter(Descriptor::kLeft));
TNode<Object> rhs = CAST(Parameter(Descriptor::kRight));
TNode<HeapObject> maybe_feedback_vector =
CAST(Parameter(Descriptor::kMaybeFeedbackVector));
TNode<UintPtrT> slot = UncheckedCast<UintPtrT>(Parameter(Descriptor::kSlot));
TVARIABLE(Smi, var_type_feedback);
TNode<Oddball> result = StrictEqual(lhs, rhs, &var_type_feedback);
UpdateFeedback(var_type_feedback.value(), maybe_feedback_vector, slot);
Return(result);
}
} // namespace internal
} // namespace v8

View File

@ -946,6 +946,7 @@ class CallWithSpreadDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(CallWithSpreadDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class CallWithSpread_WithFeedbackDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kTarget, kArgumentsCount, kSpread, kSlot,
@ -967,6 +968,7 @@ class CallWithArrayLikeDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(CallWithArrayLikeDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class CallWithArrayLike_WithFeedbackDescriptor
: public CallInterfaceDescriptor {
public:
@ -1002,6 +1004,7 @@ class ConstructWithSpreadDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(ConstructWithSpreadDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class ConstructWithSpread_WithFeedbackDescriptor
: public CallInterfaceDescriptor {
public:
@ -1024,6 +1027,7 @@ class ConstructWithArrayLikeDescriptor : public CallInterfaceDescriptor {
DECLARE_DESCRIPTOR(ConstructWithArrayLikeDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class ConstructWithArrayLike_WithFeedbackDescriptor
: public CallInterfaceDescriptor {
public:
@ -1465,11 +1469,12 @@ class BinaryOp_WithFeedbackDescriptor : public CallInterfaceDescriptor {
DEFINE_PARAMETERS(kLeft, kRight, kSlot, kMaybeFeedbackVector)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kLeft
MachineType::AnyTagged(), // kRight
MachineType::Int32(), // kSlot
MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kMaybeFeedbackVector
DECLARE_DESCRIPTOR(BinaryOp_WithFeedbackDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class CallTrampoline_WithFeedbackDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS_VARARGS(kFunction, kActualArgumentsCount, kSlot,
@ -1487,11 +1492,12 @@ class Compare_WithFeedbackDescriptor : public CallInterfaceDescriptor {
DEFINE_PARAMETERS(kLeft, kRight, kSlot, kMaybeFeedbackVector)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kLeft
MachineType::AnyTagged(), // kRight
MachineType::Int32(), // kSlot
MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kMaybeFeedbackVector
DECLARE_DESCRIPTOR(Compare_WithFeedbackDescriptor, CallInterfaceDescriptor)
};
// TODO(jgruber): Pass the slot as UintPtr.
class Construct_WithFeedbackDescriptor : public CallInterfaceDescriptor {
public:
// kSlot is passed in a register, kMaybeFeedbackVector on the stack.
@ -1506,7 +1512,7 @@ class UnaryOp_WithFeedbackDescriptor : public CallInterfaceDescriptor {
public:
DEFINE_PARAMETERS(kValue, kSlot, kMaybeFeedbackVector)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kValue
MachineType::Int32(), // kSlot
MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kMaybeFeedbackVector
DECLARE_DESCRIPTOR(UnaryOp_WithFeedbackDescriptor, CallInterfaceDescriptor)
};

View File

@ -50,6 +50,72 @@ class BinaryOpAssembler : public CodeStubAssembler {
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool rhs_known_smi);
TNode<Object> Generate_BitwiseOrWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kBitwiseOr, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_BitwiseXorWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kBitwiseXor, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_BitwiseAndWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kBitwiseAnd, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_ShiftLeftWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kShiftLeft, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_ShiftRightWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kShiftRight, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_ShiftRightLogicalWithFeedback(
TNode<Context> context, TNode<Object> left, TNode<Object> right,
TNode<UintPtrT> slot, TNode<HeapObject> maybe_feedback_vector,
bool /* unused */) {
TVARIABLE(Smi, feedback);
TNode<Object> result = Generate_BitwiseBinaryOpWithFeedback(
Operation::kShiftRightLogical, left, right, context, &feedback);
UpdateFeedback(feedback.value(), maybe_feedback_vector, slot);
return result;
}
TNode<Object> Generate_BitwiseBinaryOpWithFeedback(Operation bitwise_op,
TNode<Object> left,
TNode<Object> right,

View File

@ -0,0 +1,276 @@
// 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.
#include "src/ic/unary-op-assembler.h"
#include "src/common/globals.h"
namespace v8 {
namespace internal {
// Unary op helper classes.
namespace {
class UnaryNumericOpAssembler : public CodeStubAssembler {
public:
explicit UnaryNumericOpAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
protected:
virtual TNode<Number> SmiOp(TNode<Smi> smi_value,
TVariable<Smi>* var_feedback, Label* do_float_op,
TVariable<Float64T>* var_float) = 0;
virtual TNode<Float64T> FloatOp(TNode<Float64T> float_value) = 0;
virtual TNode<HeapObject> BigIntOp(TNode<Context> context,
TNode<HeapObject> bigint_value) = 0;
TNode<Object> UnaryOpWithFeedback(TNode<Context> context, TNode<Object> value,
TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
TVARIABLE(Object, var_value, value);
TVARIABLE(Object, var_result);
TVARIABLE(Float64T, var_float_value);
TVARIABLE(Smi, var_feedback, SmiConstant(BinaryOperationFeedback::kNone));
Label start(this, {&var_value, &var_feedback}), end(this);
Label do_float_op(this, &var_float_value);
Goto(&start);
// We might have to try again after ToNumeric conversion.
BIND(&start);
{
Label if_smi(this), if_heapnumber(this), if_oddball(this);
Label if_bigint(this, Label::kDeferred);
Label if_other(this, Label::kDeferred);
TNode<Object> value = var_value.value();
GotoIf(TaggedIsSmi(value), &if_smi);
TNode<HeapObject> value_heap_object = CAST(value);
TNode<Map> map = LoadMap(value_heap_object);
GotoIf(IsHeapNumberMap(map), &if_heapnumber);
TNode<Uint16T> instance_type = LoadMapInstanceType(map);
GotoIf(IsBigIntInstanceType(instance_type), &if_bigint);
Branch(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &if_oddball,
&if_other);
BIND(&if_smi);
{
var_result =
SmiOp(CAST(value), &var_feedback, &do_float_op, &var_float_value);
Goto(&end);
}
BIND(&if_heapnumber);
{
var_float_value = LoadHeapNumberValue(value_heap_object);
Goto(&do_float_op);
}
BIND(&if_bigint);
{
var_result = BigIntOp(context, value_heap_object);
CombineFeedback(&var_feedback, BinaryOperationFeedback::kBigInt);
Goto(&end);
}
BIND(&if_oddball);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
OverwriteFeedback(&var_feedback,
BinaryOperationFeedback::kNumberOrOddball);
var_value =
LoadObjectField(value_heap_object, Oddball::kToNumberOffset);
Goto(&start);
}
BIND(&if_other);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
OverwriteFeedback(&var_feedback, BinaryOperationFeedback::kAny);
var_value = CallBuiltin(Builtins::kNonNumberToNumeric, context,
value_heap_object);
Goto(&start);
}
}
BIND(&do_float_op);
{
CombineFeedback(&var_feedback, BinaryOperationFeedback::kNumber);
var_result =
AllocateHeapNumberWithValue(FloatOp(var_float_value.value()));
Goto(&end);
}
BIND(&end);
UpdateFeedback(var_feedback.value(), maybe_feedback_vector, slot);
return var_result.value();
}
};
class NegateAssembler : public UnaryNumericOpAssembler {
public:
explicit NegateAssembler(compiler::CodeAssemblerState* state)
: UnaryNumericOpAssembler(state) {}
using UnaryNumericOpAssembler::UnaryOpWithFeedback;
private:
TNode<Number> SmiOp(TNode<Smi> smi_value, TVariable<Smi>* var_feedback,
Label* do_float_op,
TVariable<Float64T>* var_float) override {
TVARIABLE(Number, var_result);
Label if_zero(this), if_min_smi(this), end(this);
// Return -0 if operand is 0.
GotoIf(SmiEqual(smi_value, SmiConstant(0)), &if_zero);
// Special-case the minimum Smi to avoid overflow.
GotoIf(SmiEqual(smi_value, SmiConstant(Smi::kMinValue)), &if_min_smi);
// Else simply subtract operand from 0.
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
var_result = SmiSub(SmiConstant(0), smi_value);
Goto(&end);
BIND(&if_zero);
CombineFeedback(var_feedback, BinaryOperationFeedback::kNumber);
var_result = MinusZeroConstant();
Goto(&end);
BIND(&if_min_smi);
*var_float = SmiToFloat64(smi_value);
Goto(do_float_op);
BIND(&end);
return var_result.value();
}
TNode<Float64T> FloatOp(TNode<Float64T> float_value) override {
return Float64Neg(float_value);
}
TNode<HeapObject> BigIntOp(TNode<Context> context,
TNode<HeapObject> bigint_value) override {
return CAST(CallRuntime(Runtime::kBigIntUnaryOp, context, bigint_value,
SmiConstant(Operation::kNegate)));
}
};
template <int kAddValue, Operation kOperation>
class IncDecAssembler : public UnaryNumericOpAssembler {
public:
explicit IncDecAssembler(compiler::CodeAssemblerState* state)
: UnaryNumericOpAssembler(state) {}
using UnaryNumericOpAssembler::UnaryOpWithFeedback;
private:
TNode<Number> SmiOp(TNode<Smi> value, TVariable<Smi>* var_feedback,
Label* do_float_op,
TVariable<Float64T>* var_float) override {
Label if_overflow(this), out(this);
TNode<Smi> result = TrySmiAdd(value, SmiConstant(kAddValue), &if_overflow);
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
Goto(&out);
BIND(&if_overflow);
*var_float = SmiToFloat64(value);
Goto(do_float_op);
BIND(&out);
return result;
}
TNode<Float64T> FloatOp(TNode<Float64T> float_value) override {
return Float64Add(float_value, Float64Constant(kAddValue));
}
TNode<HeapObject> BigIntOp(TNode<Context> context,
TNode<HeapObject> bigint_value) override {
return CAST(CallRuntime(Runtime::kBigIntUnaryOp, context, bigint_value,
SmiConstant(kOperation)));
}
};
using IncAssembler = IncDecAssembler<1, Operation::kIncrement>;
using DecAssembler = IncDecAssembler<-1, Operation::kDecrement>;
class BitwiseNotAssembler : public CodeStubAssembler {
public:
explicit BitwiseNotAssembler(compiler::CodeAssemblerState* state)
: CodeStubAssembler(state) {}
TNode<Object> BitwiseNotWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
TVARIABLE(Word32T, var_word32);
TVARIABLE(Smi, var_feedback);
TVARIABLE(BigInt, var_bigint);
TVARIABLE(Object, var_result);
Label if_number(this), if_bigint(this, Label::kDeferred), out(this);
TaggedToWord32OrBigIntWithFeedback(context, value, &if_number, &var_word32,
&if_bigint, &var_bigint, &var_feedback);
// Number case.
BIND(&if_number);
var_result =
ChangeInt32ToTagged(Signed(Word32BitwiseNot(var_word32.value())));
TNode<Smi> result_type = SelectSmiConstant(
TaggedIsSmi(var_result.value()), BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
UpdateFeedback(SmiOr(result_type, var_feedback.value()),
maybe_feedback_vector, slot);
Goto(&out);
// BigInt case.
BIND(&if_bigint);
UpdateFeedback(SmiConstant(BinaryOperationFeedback::kBigInt),
maybe_feedback_vector, slot);
var_result =
CallRuntime(Runtime::kBigIntUnaryOp, context, var_bigint.value(),
SmiConstant(Operation::kBitwiseNot));
Goto(&out);
BIND(&out);
return var_result.value();
}
};
} // namespace
TNode<Object> UnaryOpAssembler::Generate_BitwiseNotWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
// TODO(jgruber): Make this implementation more consistent with other unary
// ops (i.e. have them all use UnaryOpWithFeedback or some other mechanism).
BitwiseNotAssembler a(state_);
return a.BitwiseNotWithFeedback(context, value, slot, maybe_feedback_vector);
}
TNode<Object> UnaryOpAssembler::Generate_DecrementWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
DecAssembler a(state_);
return a.UnaryOpWithFeedback(context, value, slot, maybe_feedback_vector);
}
TNode<Object> UnaryOpAssembler::Generate_IncrementWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
IncAssembler a(state_);
return a.UnaryOpWithFeedback(context, value, slot, maybe_feedback_vector);
}
TNode<Object> UnaryOpAssembler::Generate_NegateWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector) {
NegateAssembler a(state_);
return a.UnaryOpWithFeedback(context, value, slot, maybe_feedback_vector);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,45 @@
// 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.
#ifndef V8_IC_UNARY_OP_ASSEMBLER_H_
#define V8_IC_UNARY_OP_ASSEMBLER_H_
#include "src/codegen/code-stub-assembler.h"
namespace v8 {
namespace internal {
namespace compiler {
class CodeAssemblerState;
}
class UnaryOpAssembler final {
public:
explicit UnaryOpAssembler(compiler::CodeAssemblerState* state)
: state_(state) {}
TNode<Object> Generate_BitwiseNotWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector);
TNode<Object> Generate_DecrementWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector);
TNode<Object> Generate_IncrementWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector);
TNode<Object> Generate_NegateWithFeedback(
TNode<Context> context, TNode<Object> value, TNode<UintPtrT> slot,
TNode<HeapObject> maybe_feedback_vector);
private:
compiler::CodeAssemblerState* const state_;
};
} // namespace internal
} // namespace v8
#endif // V8_IC_UNARY_OP_ASSEMBLER_H_

View File

@ -14,6 +14,7 @@
#include "src/ic/accessor-assembler.h"
#include "src/ic/binary-op-assembler.h"
#include "src/ic/ic.h"
#include "src/ic/unary-op-assembler.h"
#include "src/interpreter/bytecode-flags.h"
#include "src/interpreter/bytecodes.h"
#include "src/interpreter/interpreter-assembler.h"
@ -1083,38 +1084,17 @@ IGNITION_HANDLER(BitwiseAndSmi, InterpreterBitwiseBinaryOpAssembler) {
//
// Perform bitwise-not on the accumulator.
IGNITION_HANDLER(BitwiseNot, InterpreterAssembler) {
TNode<Object> operand = GetAccumulator();
TNode<Object> value = GetAccumulator();
TNode<Context> context = GetContext();
TNode<UintPtrT> slot_index = BytecodeOperandIdx(0);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext();
TVARIABLE(Word32T, var_word32);
TVARIABLE(Smi, var_feedback);
TVARIABLE(BigInt, var_bigint);
Label if_number(this), if_bigint(this);
TaggedToWord32OrBigIntWithFeedback(context, operand, &if_number, &var_word32,
&if_bigint, &var_bigint, &var_feedback);
UnaryOpAssembler unary_op_asm(state());
TNode<Object> result = unary_op_asm.Generate_BitwiseNotWithFeedback(
context, value, slot_index, maybe_feedback_vector);
// Number case.
BIND(&if_number);
TNode<Number> result =
ChangeInt32ToTagged(Signed(Word32BitwiseNot(var_word32.value())));
TNode<Smi> result_type = SelectSmiConstant(
TaggedIsSmi(result), BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
UpdateFeedback(SmiOr(result_type, var_feedback.value()),
maybe_feedback_vector, slot_index);
SetAccumulator(result);
Dispatch();
// BigInt case.
BIND(&if_bigint);
UpdateFeedback(SmiConstant(BinaryOperationFeedback::kBigInt),
maybe_feedback_vector, slot_index);
SetAccumulator(CallRuntime(Runtime::kBigIntUnaryOp, context,
var_bigint.value(),
SmiConstant(Operation::kBitwiseNot)));
Dispatch();
}
// ShiftLeftSmi <imm>
@ -1144,162 +1124,22 @@ IGNITION_HANDLER(ShiftRightLogicalSmi, InterpreterBitwiseBinaryOpAssembler) {
BitwiseBinaryOpWithSmi(Operation::kShiftRightLogical);
}
class UnaryNumericOpAssembler : public InterpreterAssembler {
public:
UnaryNumericOpAssembler(CodeAssemblerState* state, Bytecode bytecode,
OperandScale operand_scale)
: InterpreterAssembler(state, bytecode, operand_scale) {}
virtual ~UnaryNumericOpAssembler() = default;
// Must return a tagged value.
virtual TNode<Number> SmiOp(TNode<Smi> smi_value,
TVariable<Smi>* var_feedback, Label* do_float_op,
TVariable<Float64T>* var_float) = 0;
// Must return a Float64 value.
virtual TNode<Float64T> FloatOp(TNode<Float64T> float_value) = 0;
// Must return a tagged value.
virtual TNode<HeapObject> BigIntOp(TNode<HeapObject> bigint_value) = 0;
void UnaryOpWithFeedback() {
TVARIABLE(Object, var_value, GetAccumulator());
TVARIABLE(Object, var_result);
TVARIABLE(Float64T, var_float_value);
TVARIABLE(Smi, var_feedback, SmiConstant(BinaryOperationFeedback::kNone));
Label start(this, {&var_value, &var_feedback}), end(this);
Label do_float_op(this, &var_float_value);
Goto(&start);
// We might have to try again after ToNumeric conversion.
BIND(&start);
{
Label if_smi(this), if_heapnumber(this), if_oddball(this);
Label if_bigint(this, Label::kDeferred);
Label if_other(this, Label::kDeferred);
TNode<Object> value = var_value.value();
GotoIf(TaggedIsSmi(value), &if_smi);
TNode<HeapObject> value_heap_object = CAST(value);
TNode<Map> map = LoadMap(value_heap_object);
GotoIf(IsHeapNumberMap(map), &if_heapnumber);
TNode<Uint16T> instance_type = LoadMapInstanceType(map);
GotoIf(IsBigIntInstanceType(instance_type), &if_bigint);
Branch(InstanceTypeEqual(instance_type, ODDBALL_TYPE), &if_oddball,
&if_other);
BIND(&if_smi);
{
var_result =
SmiOp(CAST(value), &var_feedback, &do_float_op, &var_float_value);
Goto(&end);
}
BIND(&if_heapnumber);
{
var_float_value = LoadHeapNumberValue(value_heap_object);
Goto(&do_float_op);
}
BIND(&if_bigint);
{
var_result = BigIntOp(value_heap_object);
CombineFeedback(&var_feedback, BinaryOperationFeedback::kBigInt);
Goto(&end);
}
BIND(&if_oddball);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
OverwriteFeedback(&var_feedback,
BinaryOperationFeedback::kNumberOrOddball);
var_value =
LoadObjectField(value_heap_object, Oddball::kToNumberOffset);
Goto(&start);
}
BIND(&if_other);
{
// We do not require an Or with earlier feedback here because once we
// convert the value to a number, we cannot reach this path. We can
// only reach this path on the first pass when the feedback is kNone.
CSA_ASSERT(this, SmiEqual(var_feedback.value(),
SmiConstant(BinaryOperationFeedback::kNone)));
OverwriteFeedback(&var_feedback, BinaryOperationFeedback::kAny);
var_value = CallBuiltin(Builtins::kNonNumberToNumeric, GetContext(),
value_heap_object);
Goto(&start);
}
}
BIND(&do_float_op);
{
CombineFeedback(&var_feedback, BinaryOperationFeedback::kNumber);
var_result =
AllocateHeapNumberWithValue(FloatOp(var_float_value.value()));
Goto(&end);
}
BIND(&end);
TNode<UintPtrT> slot_index = BytecodeOperandIdx(0);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
UpdateFeedback(var_feedback.value(), maybe_feedback_vector, slot_index);
SetAccumulator(var_result.value());
Dispatch();
}
};
class NegateAssemblerImpl : public UnaryNumericOpAssembler {
public:
explicit NegateAssemblerImpl(CodeAssemblerState* state, Bytecode bytecode,
OperandScale operand_scale)
: UnaryNumericOpAssembler(state, bytecode, operand_scale) {}
TNode<Number> SmiOp(TNode<Smi> smi_value, TVariable<Smi>* var_feedback,
Label* do_float_op,
TVariable<Float64T>* var_float) override {
TVARIABLE(Number, var_result);
Label if_zero(this), if_min_smi(this), end(this);
// Return -0 if operand is 0.
GotoIf(SmiEqual(smi_value, SmiConstant(0)), &if_zero);
// Special-case the minimum Smi to avoid overflow.
GotoIf(SmiEqual(smi_value, SmiConstant(Smi::kMinValue)), &if_min_smi);
// Else simply subtract operand from 0.
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
var_result = SmiSub(SmiConstant(0), smi_value);
Goto(&end);
BIND(&if_zero);
CombineFeedback(var_feedback, BinaryOperationFeedback::kNumber);
var_result = MinusZeroConstant();
Goto(&end);
BIND(&if_min_smi);
*var_float = SmiToFloat64(smi_value);
Goto(do_float_op);
BIND(&end);
return var_result.value();
}
TNode<Float64T> FloatOp(TNode<Float64T> float_value) override {
return Float64Neg(float_value);
}
TNode<HeapObject> BigIntOp(TNode<HeapObject> bigint_value) override {
return CAST(CallRuntime(Runtime::kBigIntUnaryOp, GetContext(), bigint_value,
SmiConstant(Operation::kNegate)));
}
};
// Negate <feedback_slot>
//
// Perform arithmetic negation on the accumulator.
IGNITION_HANDLER(Negate, NegateAssemblerImpl) { UnaryOpWithFeedback(); }
IGNITION_HANDLER(Negate, InterpreterAssembler) {
TNode<Object> value = GetAccumulator();
TNode<Context> context = GetContext();
TNode<UintPtrT> slot_index = BytecodeOperandIdx(0);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
UnaryOpAssembler unary_op_asm(state());
TNode<Object> result = unary_op_asm.Generate_NegateWithFeedback(
context, value, slot_index, maybe_feedback_vector);
SetAccumulator(result);
Dispatch();
}
// ToName <dst>
//
@ -1345,72 +1185,39 @@ IGNITION_HANDLER(ToString, InterpreterAssembler) {
Dispatch();
}
class IncDecAssembler : public UnaryNumericOpAssembler {
public:
explicit IncDecAssembler(CodeAssemblerState* state, Bytecode bytecode,
OperandScale operand_scale)
: UnaryNumericOpAssembler(state, bytecode, operand_scale) {}
Operation op() {
DCHECK(op_ == Operation::kIncrement || op_ == Operation::kDecrement);
return op_;
}
TNode<Number> SmiOp(TNode<Smi> value, TVariable<Smi>* var_feedback,
Label* do_float_op,
TVariable<Float64T>* var_float) override {
TNode<Smi> one = SmiConstant(1);
Label if_overflow(this), if_notoverflow(this);
TNode<Smi> result = op() == Operation::kIncrement
? TrySmiAdd(value, one, &if_overflow)
: TrySmiSub(value, one, &if_overflow);
Goto(&if_notoverflow);
BIND(&if_overflow);
{
*var_float = SmiToFloat64(value);
Goto(do_float_op);
}
BIND(&if_notoverflow);
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
return result;
}
TNode<Float64T> FloatOp(TNode<Float64T> float_value) override {
return op() == Operation::kIncrement
? Float64Add(float_value, Float64Constant(1.0))
: Float64Sub(float_value, Float64Constant(1.0));
}
TNode<HeapObject> BigIntOp(TNode<HeapObject> bigint_value) override {
return CAST(CallRuntime(Runtime::kBigIntUnaryOp, GetContext(), bigint_value,
SmiConstant(op())));
}
void IncWithFeedback() {
op_ = Operation::kIncrement;
UnaryOpWithFeedback();
}
void DecWithFeedback() {
op_ = Operation::kDecrement;
UnaryOpWithFeedback();
}
private:
Operation op_ = Operation::kEqual; // Dummy initialization.
};
// Inc
//
// Increments value in the accumulator by one.
IGNITION_HANDLER(Inc, IncDecAssembler) { IncWithFeedback(); }
IGNITION_HANDLER(Inc, InterpreterAssembler) {
TNode<Object> value = GetAccumulator();
TNode<Context> context = GetContext();
TNode<UintPtrT> slot_index = BytecodeOperandIdx(0);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
UnaryOpAssembler unary_op_asm(state());
TNode<Object> result = unary_op_asm.Generate_IncrementWithFeedback(
context, value, slot_index, maybe_feedback_vector);
SetAccumulator(result);
Dispatch();
}
// Dec
//
// Decrements value in the accumulator by one.
IGNITION_HANDLER(Dec, IncDecAssembler) { DecWithFeedback(); }
IGNITION_HANDLER(Dec, InterpreterAssembler) {
TNode<Object> value = GetAccumulator();
TNode<Context> context = GetContext();
TNode<UintPtrT> slot_index = BytecodeOperandIdx(0);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
UnaryOpAssembler unary_op_asm(state());
TNode<Object> result = unary_op_asm.Generate_DecrementWithFeedback(
context, value, slot_index, maybe_feedback_vector);
SetAccumulator(result);
Dispatch();
}
// ToBooleanLogicalNot
//