Commit 06b79ee9 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[bigint,compiler] Support bigints in negation (-) operator.

This introduces a JSNegate operator and lowers it either to a
speculative multiplication with -1 (when we have Number feedback)
or to a stub call. The stub is also new.

R=jarin@chromium.org

Bug: v8:6791
Change-Id: I8e20333fe49cc6088d2d10777be982e42eed2412
Reviewed-on: https://chromium-review.googlesource.com/774718
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#49538}
parent 2993e484
......@@ -701,6 +701,7 @@ namespace internal {
TFC(Equal, Compare, 1) \
TFC(SameValue, Compare, 1) \
TFC(StrictEqual, Compare, 1) \
TFS(Negate, kValue) \
\
/* Object */ \
TFJ(ObjectConstructor, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
......
......@@ -59,6 +59,10 @@ class NumberBuiltinsAssembler : public CodeStubAssembler {
Return(RelationalComparison(op, lhs, rhs, context));
}
template <typename Descriptor>
void UnaryOp(Variable* var_input, Label* do_smi, Label* do_double,
Variable* var_input_double, Label* do_bigint);
template <typename Descriptor>
void BinaryOp(Label* smis, Variable* var_left, Variable* var_right,
Label* doubles, Variable* var_left_double,
......@@ -592,6 +596,35 @@ TF_BUILTIN(Add, AddStubAssembler) {
}
}
template <typename Descriptor>
void NumberBuiltinsAssembler::UnaryOp(Variable* var_input, Label* do_smi,
Label* do_double,
Variable* var_input_double,
Label* do_bigint) {
DCHECK_EQ(var_input->rep(), MachineRepresentation::kTagged);
DCHECK_EQ(var_input_double->rep(), MachineRepresentation::kFloat64);
Node* context = Parameter(Descriptor::kContext);
var_input->Bind(Parameter(Descriptor::kValue));
// We might need to loop for ToNumeric conversion.
Label loop(this, {var_input});
Goto(&loop);
BIND(&loop);
Node* input = var_input->value();
Label not_number(this);
GotoIf(TaggedIsSmi(input), do_smi);
GotoIfNot(IsHeapNumber(input), &not_number);
var_input_double->Bind(LoadHeapNumberValue(input));
Goto(do_double);
BIND(&not_number);
GotoIf(IsBigInt(input), do_bigint);
var_input->Bind(CallBuiltin(Builtins::kNonNumberToNumeric, context, input));
Goto(&loop);
}
template <typename Descriptor>
void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
Variable* var_right, Label* doubles,
......@@ -600,12 +633,14 @@ void NumberBuiltinsAssembler::BinaryOp(Label* smis, Variable* var_left,
Label* bigints) {
DCHECK_EQ(var_left->rep(), MachineRepresentation::kTagged);
DCHECK_EQ(var_right->rep(), MachineRepresentation::kTagged);
DCHECK_EQ(var_left_double->rep(), MachineRepresentation::kFloat64);
DCHECK_EQ(var_right_double->rep(), MachineRepresentation::kFloat64);
Node* context = Parameter(Descriptor::kContext);
var_left->Bind(Parameter(Descriptor::kLeft));
var_right->Bind(Parameter(Descriptor::kRight));
// We might need to loop for ToNumber conversions.
// We might need to loop for ToNumeric conversions.
Label loop(this, {var_left, var_right});
Goto(&loop);
BIND(&loop);
......@@ -713,6 +748,31 @@ TF_BUILTIN(Subtract, NumberBuiltinsAssembler) {
}
}
TF_BUILTIN(Negate, NumberBuiltinsAssembler) {
VARIABLE(var_input, MachineRepresentation::kTagged);
VARIABLE(var_input_double, MachineRepresentation::kFloat64);
Label do_smi(this), do_double(this), do_bigint(this);
UnaryOp<Descriptor>(&var_input, &do_smi, &do_double, &var_input_double,
&do_bigint);
BIND(&do_smi);
{ Return(SmiMul(var_input.value(), SmiConstant(-1))); }
BIND(&do_double);
{
Node* value = Float64Mul(var_input_double.value(), Float64Constant(-1));
Return(AllocateHeapNumberWithValue(value));
}
BIND(&do_bigint);
{
Node* context = Parameter(Descriptor::kContext);
Return(CallRuntime(Runtime::kBigIntUnaryOp, context, var_input.value(),
SmiConstant(Operation::kNegate)));
}
}
TF_BUILTIN(Multiply, NumberBuiltinsAssembler) {
VARIABLE(var_left, MachineRepresentation::kTagged);
VARIABLE(var_right, MachineRepresentation::kTagged);
......
......@@ -2086,24 +2086,20 @@ CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
void BytecodeGraphBuilder::VisitNegate() {
PrepareEagerCheckpoint();
// TODO(adamk): Create a JSNegate operator, as this desugaring is
// invalid for BigInts.
const Operator* op = javascript()->Multiply();
Node* operand = environment()->LookupAccumulator();
Node* multiplier = jsgraph()->SmiConstant(-1);
FeedbackSlot slot = feedback_vector()->ToSlot(
bytecode_iterator().GetIndexOperand(kUnaryOperationHintIndex));
const Operator* op = javascript()->Negate();
Node* operand = environment()->LookupAccumulator();
JSTypeHintLowering::LoweringResult lowering =
TryBuildSimplifiedBinaryOp(op, operand, multiplier, slot);
TryBuildSimplifiedUnaryOp(op, operand, slot);
if (lowering.IsExit()) return;
Node* node = nullptr;
if (lowering.IsSideEffectFree()) {
node = lowering.value();
} else {
DCHECK(!lowering.Changed());
node = NewNode(op, operand, multiplier);
node = NewNode(op, operand);
}
environment()->BindAccumulator(node, Environment::kAttachFrameState);
......@@ -2994,6 +2990,19 @@ void BytecodeGraphBuilder::BuildJumpIfJSReceiver() {
BuildJumpIf(condition);
}
JSTypeHintLowering::LoweringResult
BytecodeGraphBuilder::TryBuildSimplifiedUnaryOp(const Operator* op,
Node* operand,
FeedbackSlot slot) {
Node* effect = environment()->GetEffectDependency();
Node* control = environment()->GetControlDependency();
JSTypeHintLowering::LoweringResult result =
type_hint_lowering().ReduceUnaryOperation(op, operand, effect, control,
slot);
ApplyEarlyReduction(result);
return result;
}
JSTypeHintLowering::LoweringResult
BytecodeGraphBuilder::TryBuildSimplifiedBinaryOp(const Operator* op, Node* left,
Node* right,
......
......@@ -183,6 +183,8 @@ class BytecodeGraphBuilder {
// Optional early lowering to the simplified operator level. Note that
// the result has already been wired into the environment just like
// any other invocation of {NewNode} would do.
JSTypeHintLowering::LoweringResult TryBuildSimplifiedUnaryOp(
const Operator* op, Node* operand, FeedbackSlot slot);
JSTypeHintLowering::LoweringResult TryBuildSimplifiedBinaryOp(
const Operator* op, Node* left, Node* right, FeedbackSlot slot);
JSTypeHintLowering::LoweringResult TryBuildSimplifiedForInNext(
......
......@@ -70,6 +70,7 @@ REPLACE_STUB_CALL(LessThan)
REPLACE_STUB_CALL(LessThanOrEqual)
REPLACE_STUB_CALL(GreaterThan)
REPLACE_STUB_CALL(GreaterThanOrEqual)
REPLACE_STUB_CALL(Negate)
REPLACE_STUB_CALL(HasProperty)
REPLACE_STUB_CALL(Equal)
REPLACE_STUB_CALL(ToInteger)
......
......@@ -577,6 +577,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(Multiply, Operator::kNoProperties, 2, 1) \
V(Divide, Operator::kNoProperties, 2, 1) \
V(Modulus, Operator::kNoProperties, 2, 1) \
V(Negate, Operator::kNoProperties, 1, 1) \
V(ToInteger, Operator::kNoProperties, 1, 1) \
V(ToLength, Operator::kNoProperties, 1, 1) \
V(ToName, Operator::kNoProperties, 1, 1) \
......
......@@ -652,6 +652,8 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* Divide();
const Operator* Modulus();
const Operator* Negate();
const Operator* ToInteger();
const Operator* ToLength();
const Operator* ToName();
......
......@@ -212,6 +212,35 @@ JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
Flags flags)
: jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
const Operator* op, Node* operand, Node* effect, Node* control,
FeedbackSlot slot) const {
switch (op->opcode()) {
case IrOpcode::kJSNegate: {
DCHECK(!slot.IsInvalid());
BinaryOpICNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
return LoweringResult::Exit(node);
}
// Lower to a speculative multiplication with -1 if we have some kind of
// Number feedback.
JSSpeculativeBinopBuilder b(this, jsgraph()->javascript()->Multiply(),
operand, jsgraph()->SmiConstant(-1), effect,
control, slot);
if (Node* node = b.TryBuildNumberBinop()) {
return LoweringResult::SideEffectFree(node, node, control);
}
break;
}
default:
UNREACHABLE();
break;
}
return LoweringResult::NoChange();
}
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
const Operator* op, Node* left, Node* right, Node* effect, Node* control,
FeedbackSlot slot) const {
......
......@@ -99,6 +99,11 @@ class JSTypeHintLowering {
Node* control_;
};
// Potential reduction of unary operations (e.g. negation).
LoweringResult ReduceUnaryOperation(const Operator* op, Node* operand,
Node* effect, Node* control,
FeedbackSlot slot) const;
// Potential reduction of binary (arithmetic, logical, shift and relational
// comparison) operations.
LoweringResult ReduceBinaryOperation(const Operator* op, Node* left,
......
......@@ -426,6 +426,20 @@ Reduction JSTypedLowering::ReduceSpeculativeNumberAdd(Node* node) {
return NoChange();
}
Reduction JSTypedLowering::ReduceJSNegate(Node* node) {
Node* input = NodeProperties::GetValueInput(node, 0);
Type* input_type = NodeProperties::GetType(input);
if (input_type->Is(Type::PlainPrimitive())) {
// JSNegate(x) => NumberMultiply(ToNumber(x), -1)
node->InsertInput(graph()->zone(), 1, jsgraph()->SmiConstant(-1));
NodeProperties::ChangeOp(node, javascript()->Multiply());
JSBinopReduction r(this, node);
r.ConvertInputsToNumber();
return r.ChangeToPureOperator(r.NumberOp(), Type::Number());
}
return NoChange();
}
Reduction JSTypedLowering::ReduceJSAdd(Node* node) {
JSBinopReduction r(this, node);
if (r.BothInputsAre(Type::Number())) {
......@@ -2064,6 +2078,8 @@ Reduction JSTypedLowering::Reduce(Node* node) {
case IrOpcode::kJSDivide:
case IrOpcode::kJSModulus:
return ReduceNumberBinop(node);
case IrOpcode::kJSNegate:
return ReduceJSNegate(node);
case IrOpcode::kJSHasInPrototypeChain:
return ReduceJSHasInPrototypeChain(node);
case IrOpcode::kJSOrdinaryHasInstance:
......
......@@ -40,6 +40,7 @@ class V8_EXPORT_PRIVATE JSTypedLowering final
friend class JSBinopReduction;
Reduction ReduceJSAdd(Node* node);
Reduction ReduceJSNegate(Node* node);
Reduction ReduceJSComparison(Node* node);
Reduction ReduceJSLoadNamed(Node* node);
Reduction ReduceJSHasInPrototypeChain(Node* node);
......
......@@ -124,7 +124,9 @@
V(JSToObject) \
V(JSToString)
#define JS_SIMPLE_UNOP_LIST(V) JS_CONVERSION_UNOP_LIST(V)
#define JS_SIMPLE_UNOP_LIST(V) \
JS_CONVERSION_UNOP_LIST(V) \
V(JSNegate)
#define JS_OBJECT_OP_LIST(V) \
V(JSCreate) \
......
......@@ -113,6 +113,7 @@ bool OperatorProperties::HasFrameStateInput(const Operator* op) {
case IrOpcode::kJSStackCheck:
case IrOpcode::kJSDebugger:
case IrOpcode::kJSGetSuperConstructor:
case IrOpcode::kJSNegate:
return true;
default:
......
......@@ -2088,8 +2088,8 @@ class RepresentationSelector {
case IrOpcode::kSpeculativeNumberShiftRightLogical: {
// ToNumber(x) can throw if x is either a Receiver or a Symbol, so we
// can only eliminate an unused speculative number operation if we know
// that the inputs are PlainPrimitive, which excludes everything that's
// might have side effects or throws during a ToNumber conversion.
// that the inputs are PlainPrimitive, which excludes everything that
// might have side effects or throw during a ToNumber conversion.
if (BothInputsAre(node, Type::PlainPrimitive())) {
if (truncation.IsUnused()) return VisitUnused(node);
}
......@@ -3045,6 +3045,7 @@ class RepresentationSelector {
JS_CONTEXT_OP_LIST(OPCODE_CASE)
JS_OTHER_OP_LIST(OPCODE_CASE)
#undef OPCODE_CASE
case IrOpcode::kJSNegate:
case IrOpcode::kJSToInteger:
case IrOpcode::kJSToLength:
case IrOpcode::kJSToName:
......
......@@ -264,6 +264,8 @@ class Typer::Visitor : public Reducer {
static ComparisonOutcome Invert(ComparisonOutcome, Typer*);
static Type* FalsifyUndefined(ComparisonOutcome, Typer*);
static Type* Negate(Type*, Typer*);
static Type* ToPrimitive(Type*, Typer*);
static Type* ToBoolean(Type*, Typer*);
static Type* ToInteger(Type*, Typer*);
......@@ -424,6 +426,14 @@ Type* Typer::Visitor::FalsifyUndefined(ComparisonOutcome outcome, Typer* t) {
return t->singleton_true_;
}
Type* Typer::Visitor::Negate(Type* type, Typer* t) {
type = ToNumeric(type, t);
if (type->Is(Type::Number())) {
return NumberMultiply(type, t->cache_.kSingletonMinusOne, t);
}
return Type::Numeric();
}
// Type conversion.
Type* Typer::Visitor::ToPrimitive(Type* type, Typer* t) {
......@@ -1069,6 +1079,10 @@ Type* Typer::Visitor::JSModulusTyper(Type* lhs, Type* rhs, Typer* t) {
// JS unary operators.
Type* Typer::Visitor::TypeJSNegate(Node* node) {
return TypeUnaryOp(node, Negate);
}
Type* Typer::Visitor::TypeClassOf(Node* node) {
return Type::InternalizedStringOrNull();
}
......
......@@ -612,6 +612,11 @@ void Verifier::Visitor::Check(Node* node, const AllNodes& all) {
CheckTypeIs(node, Type::Number());
break;
case IrOpcode::kJSNegate:
// Type is Numeric.
CheckTypeIs(node, Type::Numeric());
break;
case IrOpcode::kToBoolean:
// Type is Boolean.
CheckTypeIs(node, Type::Boolean());
......
......@@ -38,6 +38,8 @@ namespace internal {
"Insufficient type feedback for generic named access") \
V(InsufficientTypeFeedbackForGenericKeyedAccess, \
"Insufficient type feedback for generic keyed access") \
V(InsufficientTypeFeedbackForUnaryOperation, \
"Insufficient type feedback for unary operation") \
V(KeyIsNegative, "key is negative") \
V(LostPrecision, "lost precision") \
V(LostPrecisionOrNaN, "lost precision or NaN") \
......
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