Commit c587ec54 authored by Hao Xu's avatar Hao Xu Committed by V8 LUCI CQ

[CSA] Fast path Smi operands in BitwiseSmi bytecodes

In current BitwiseSmi bytecodes the code to do Smi operation is inside a
loop. This CL fast path the Smi operation by peeling the first Smi check
out of the loop, and avoid Smi->Int->Smi conversion where possible.

Drive-by fix: Add CSA_DCHECK in Smi shift to avoid unexpected use.

Bug: v8:12442
Change-Id: I1adce560fb22a4409337e2958779eccf9197e4ff
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3328784Reviewed-by: 's avatarLeszek Swirski <leszeks@chromium.org>
Reviewed-by: 's avatarIgor Sheludko <ishell@chromium.org>
Commit-Queue: Hao A Xu <hao.a.xu@intel.com>
Cr-Commit-Position: refs/heads/main@{#78764}
parent 8acdeeb4
......@@ -5558,8 +5558,8 @@ TNode<Word32T> CodeStubAssembler::TruncateTaggedToWord32(TNode<Context> context,
TNode<Object> value) {
TVARIABLE(Word32T, var_result);
Label done(this);
TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumber>(context, value,
&done, &var_result);
TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumber>(
context, value, &done, &var_result, IsKnownTaggedPointer::kNo);
BIND(&done);
return var_result.value();
}
......@@ -5571,7 +5571,8 @@ void CodeStubAssembler::TaggedToWord32OrBigInt(
TVariable<Word32T>* var_word32, Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint) {
TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>(
context, value, if_number, var_word32, if_bigint, var_maybe_bigint);
context, value, if_number, var_word32, IsKnownTaggedPointer::kNo,
if_bigint, var_maybe_bigint);
}
// Truncate {value} to word32 and jump to {if_number} if it is a Number,
......@@ -5582,14 +5583,27 @@ void CodeStubAssembler::TaggedToWord32OrBigIntWithFeedback(
TVariable<Word32T>* var_word32, Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) {
TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>(
context, value, if_number, var_word32, if_bigint, var_maybe_bigint,
var_feedback);
context, value, if_number, var_word32, IsKnownTaggedPointer::kNo,
if_bigint, var_maybe_bigint, var_feedback);
}
// Truncate {pointer} to word32 and jump to {if_number} if it is a Number,
// or find that it is a BigInt and jump to {if_bigint}. In either case,
// store the type feedback in {var_feedback}.
void CodeStubAssembler::TaggedPointerToWord32OrBigIntWithFeedback(
TNode<Context> context, TNode<HeapObject> pointer, Label* if_number,
TVariable<Word32T>* var_word32, Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) {
TaggedToWord32OrBigIntImpl<Object::Conversion::kToNumeric>(
context, pointer, if_number, var_word32, IsKnownTaggedPointer::kYes,
if_bigint, var_maybe_bigint, var_feedback);
}
template <Object::Conversion conversion>
void CodeStubAssembler::TaggedToWord32OrBigIntImpl(
TNode<Context> context, TNode<Object> value, Label* if_number,
TVariable<Word32T>* var_word32, Label* if_bigint,
TVariable<Word32T>* var_word32,
IsKnownTaggedPointer is_known_tagged_pointer, Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback) {
// We might need to loop after conversion.
TVARIABLE(Object, var_value, value);
......@@ -5597,20 +5611,22 @@ void CodeStubAssembler::TaggedToWord32OrBigIntImpl(
VariableList loop_vars({&var_value}, zone());
if (var_feedback != nullptr) loop_vars.push_back(var_feedback);
Label loop(this, loop_vars);
Goto(&loop);
BIND(&loop);
{
value = var_value.value();
Label not_smi(this), is_heap_number(this), is_oddball(this),
is_bigint(this);
GotoIf(TaggedIsNotSmi(value), &not_smi);
if (is_known_tagged_pointer == IsKnownTaggedPointer::kNo) {
GotoIf(TaggedIsNotSmi(value), &loop);
// {value} is a Smi.
*var_word32 = SmiToInt32(CAST(value));
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
Goto(if_number);
} else {
Goto(&loop);
}
BIND(&loop);
{
value = var_value.value();
Label not_smi(this), is_heap_number(this), is_oddball(this),
is_bigint(this), check_if_smi(this);
BIND(&not_smi);
TNode<HeapObject> value_heap_object = CAST(value);
TNode<Map> map = LoadMap(value_heap_object);
GotoIf(IsHeapNumberMap(map), &is_heap_number);
......@@ -5635,13 +5651,13 @@ void CodeStubAssembler::TaggedToWord32OrBigIntImpl(
: Builtin::kNonNumberToNumber;
var_value = CallBuiltin(builtin, context, value);
OverwriteFeedback(var_feedback, BinaryOperationFeedback::kAny);
Goto(&loop);
Goto(&check_if_smi);
BIND(&is_oddball);
var_value = LoadObjectField(value_heap_object, Oddball::kToNumberOffset);
OverwriteFeedback(var_feedback,
BinaryOperationFeedback::kNumberOrOddball);
Goto(&loop);
Goto(&check_if_smi);
}
BIND(&is_heap_number);
......@@ -5655,6 +5671,15 @@ void CodeStubAssembler::TaggedToWord32OrBigIntImpl(
CombineFeedback(var_feedback, BinaryOperationFeedback::kBigInt);
Goto(if_bigint);
}
BIND(&check_if_smi);
value = var_value.value();
GotoIf(TaggedIsNotSmi(value), &loop);
// {value} is a Smi.
*var_word32 = SmiToInt32(CAST(value));
CombineFeedback(var_feedback, BinaryOperationFeedback::kSignedSmall);
Goto(if_number);
}
}
......@@ -5817,6 +5842,18 @@ TNode<Number> CodeStubAssembler::ChangeInt32ToTagged(TNode<Int32T> value) {
return var_result.value();
}
TNode<Number> CodeStubAssembler::ChangeInt32ToTaggedNoOverflow(
TNode<Int32T> value) {
if (SmiValuesAre32Bits()) {
return SmiTag(ChangeInt32ToIntPtr(value));
}
DCHECK(SmiValuesAre31Bits());
TNode<Int32T> result_int32 = Int32Add(value, value);
TNode<IntPtrT> almost_tagged_value = ChangeInt32ToIntPtr(result_int32);
TNode<Smi> result = BitcastWordToTaggedSigned(almost_tagged_value);
return result;
}
TNode<Number> CodeStubAssembler::ChangeUint32ToTagged(TNode<Uint32T> value) {
Label if_overflow(this, Label::kDeferred), if_not_overflow(this),
if_join(this);
......@@ -13848,6 +13885,36 @@ TNode<Number> CodeStubAssembler::BitwiseOp(TNode<Word32T> left32,
UNREACHABLE();
}
TNode<Number> CodeStubAssembler::BitwiseSmiOp(TNode<Smi> left, TNode<Smi> right,
Operation bitwise_op) {
switch (bitwise_op) {
case Operation::kBitwiseAnd:
return SmiAnd(left, right);
case Operation::kBitwiseOr:
return SmiOr(left, right);
case Operation::kBitwiseXor:
return SmiXor(left, right);
// Smi shift left and logical shift rihgt can have (Heap)Number output, so
// perform int32 operation.
case Operation::kShiftLeft:
case Operation::kShiftRightLogical:
return BitwiseOp(SmiToInt32(left), SmiToInt32(right), bitwise_op);
// Arithmetic shift right of a Smi can't overflow to the heap number, so
// perform int32 operation but don't check for overflow.
case Operation::kShiftRight: {
TNode<Int32T> left32 = SmiToInt32(left);
TNode<Int32T> right32 = SmiToInt32(right);
if (!Word32ShiftIsSafe()) {
right32 = Word32And(right32, Int32Constant(0x1F));
}
return ChangeInt32ToTaggedNoOverflow(Word32Sar(left32, right32));
}
default:
break;
}
UNREACHABLE();
}
TNode<JSObject> CodeStubAssembler::AllocateJSIteratorResult(
TNode<Context> context, TNode<Object> value, TNode<Oddball> done) {
CSA_DCHECK(this, IsBoolean(done));
......
......@@ -616,6 +616,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
SMI_ARITHMETIC_BINOP(SmiSub, IntPtrSub, Int32Sub)
SMI_ARITHMETIC_BINOP(SmiAnd, WordAnd, Word32And)
SMI_ARITHMETIC_BINOP(SmiOr, WordOr, Word32Or)
SMI_ARITHMETIC_BINOP(SmiXor, WordXor, Word32Xor)
#undef SMI_ARITHMETIC_BINOP
TNode<IntPtrT> TryIntPtrAdd(TNode<IntPtrT> a, TNode<IntPtrT> b,
......@@ -629,28 +630,44 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Smi> TrySmiAbs(TNode<Smi> a, Label* if_overflow);
TNode<Smi> SmiShl(TNode<Smi> a, int shift) {
return BitcastWordToTaggedSigned(
TNode<Smi> result = BitcastWordToTaggedSigned(
WordShl(BitcastTaggedToWordForTagAndSmiBits(a), shift));
// Smi shift have different result to int32 shift when the inputs are not
// strictly limited. The CSA_DCHECK is to ensure valid inputs.
CSA_DCHECK(
this, TaggedEqual(result, BitwiseOp(SmiToInt32(a), Int32Constant(shift),
Operation::kShiftLeft)));
return result;
}
TNode<Smi> SmiShr(TNode<Smi> a, int shift) {
TNode<Smi> result;
if (kTaggedSize == kInt64Size) {
return BitcastWordToTaggedSigned(
result = BitcastWordToTaggedSigned(
WordAnd(WordShr(BitcastTaggedToWordForTagAndSmiBits(a), shift),
BitcastTaggedToWordForTagAndSmiBits(SmiConstant(-1))));
} else {
// For pointer compressed Smis, we want to make sure that we truncate to
// int32 before shifting, to avoid the values of the top 32-bits from
// leaking into the sign bit of the smi.
return BitcastWordToTaggedSigned(WordAnd(
result = BitcastWordToTaggedSigned(WordAnd(
ChangeInt32ToIntPtr(Word32Shr(
TruncateWordToInt32(BitcastTaggedToWordForTagAndSmiBits(a)),
shift)),
BitcastTaggedToWordForTagAndSmiBits(SmiConstant(-1))));
}
// Smi shift have different result to int32 shift when the inputs are not
// strictly limited. The CSA_DCHECK is to ensure valid inputs.
CSA_DCHECK(
this, TaggedEqual(result, BitwiseOp(SmiToInt32(a), Int32Constant(shift),
Operation::kShiftRightLogical)));
return result;
}
TNode<Smi> SmiSar(TNode<Smi> a, int shift) {
// The number of shift bits is |shift % 64| for 64-bits value and |shift %
// 32| for 32-bits value. The DCHECK is to ensure valid inputs.
DCHECK_LT(shift, 32);
if (kTaggedSize == kInt64Size) {
return BitcastWordToTaggedSigned(
WordAnd(WordSar(BitcastTaggedToWordForTagAndSmiBits(a), shift),
......@@ -752,6 +769,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> BitwiseOp(TNode<Word32T> left32, TNode<Word32T> right32,
Operation bitwise_op);
TNode<Number> BitwiseSmiOp(TNode<Smi> left32, TNode<Smi> right32,
Operation bitwise_op);
// Allocate an object of the given size.
TNode<HeapObject> AllocateInNewSpace(
......@@ -2367,6 +2386,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint,
TVariable<Smi>* var_feedback);
void TaggedPointerToWord32OrBigIntWithFeedback(
TNode<Context> context, TNode<HeapObject> pointer, Label* if_number,
TVariable<Word32T>* var_word32, Label* if_bigint,
TVariable<BigInt>* var_maybe_bigint, TVariable<Smi>* var_feedback);
TNode<Int32T> TruncateNumberToWord32(TNode<Number> value);
// Truncate the floating point value of a HeapNumber to an Int32.
......@@ -2382,6 +2405,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> ChangeFloat32ToTagged(TNode<Float32T> value);
TNode<Number> ChangeFloat64ToTagged(TNode<Float64T> value);
TNode<Number> ChangeInt32ToTagged(TNode<Int32T> value);
TNode<Number> ChangeInt32ToTaggedNoOverflow(TNode<Int32T> value);
TNode<Number> ChangeUint32ToTagged(TNode<Uint32T> value);
TNode<Number> ChangeUintPtrToTagged(TNode<UintPtrT> value);
TNode<Uint32T> ChangeNumberToUint32(TNode<Number> value);
......@@ -4065,10 +4089,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TVariable<Numeric>* var_numeric,
TVariable<Smi>* var_feedback);
enum IsKnownTaggedPointer { kNo, kYes };
template <Object::Conversion conversion>
void TaggedToWord32OrBigIntImpl(TNode<Context> context, TNode<Object> value,
Label* if_number,
TVariable<Word32T>* var_word32,
IsKnownTaggedPointer is_known_tagged_pointer,
Label* if_bigint = nullptr,
TVariable<BigInt>* var_maybe_bigint = nullptr,
TVariable<Smi>* var_feedback = nullptr);
......
......@@ -683,27 +683,55 @@ BinaryOpAssembler::Generate_BitwiseBinaryOpWithSmiOperandAndOptionalFeedback(
TVARIABLE(Smi, var_left_feedback);
TVARIABLE(Word32T, var_left_word32);
TVARIABLE(BigInt, var_left_bigint);
Label do_smi_op(this), if_bigint_mix(this, Label::kDeferred), done(this);
TaggedToWord32OrBigIntWithFeedback(context(), left, &do_smi_op,
&var_left_word32, &if_bigint_mix,
&var_left_bigint, &var_left_feedback);
BIND(&do_smi_op);
result =
BitwiseOp(var_left_word32.value(), SmiToInt32(right_smi), bitwise_op);
if (feedback) {
TNode<Smi> result_type = SelectSmiConstant(
TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
*feedback = SmiOr(result_type, var_left_feedback.value());
// Check if the {lhs} is a Smi or a HeapObject.
Label if_lhsissmi(this), if_lhsisnotsmi(this, Label::kDeferred);
Label do_number_op(this), if_bigint_mix(this), done(this);
Branch(TaggedIsSmi(left), &if_lhsissmi, &if_lhsisnotsmi);
BIND(&if_lhsissmi);
{
TNode<Smi> left_smi = CAST(left);
result = BitwiseSmiOp(left_smi, right_smi, bitwise_op);
if (feedback) {
if (IsBitwiseOutputKnownSmi(bitwise_op)) {
*feedback = SmiConstant(BinaryOperationFeedback::kSignedSmall);
} else {
*feedback = SelectSmiConstant(TaggedIsSmi(result.value()),
BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
}
}
Goto(&done);
}
Goto(&done);
BIND(&if_bigint_mix);
if (feedback) {
*feedback = var_left_feedback.value();
BIND(&if_lhsisnotsmi);
{
TNode<HeapObject> left_pointer = CAST(left);
TaggedPointerToWord32OrBigIntWithFeedback(
context(), left_pointer, &do_number_op, &var_left_word32,
&if_bigint_mix, &var_left_bigint, &var_left_feedback);
BIND(&do_number_op);
{
result =
BitwiseOp(var_left_word32.value(), SmiToInt32(right_smi), bitwise_op);
if (feedback) {
TNode<Smi> result_type = SelectSmiConstant(
TaggedIsSmi(result.value()), BinaryOperationFeedback::kSignedSmall,
BinaryOperationFeedback::kNumber);
*feedback = SmiOr(result_type, var_left_feedback.value());
}
Goto(&done);
}
BIND(&if_bigint_mix);
{
if (feedback) {
*feedback = var_left_feedback.value();
}
ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes);
}
}
ThrowTypeError(context(), MessageTemplate::kBigIntMixedTypes);
BIND(&done);
return result.value();
......
......@@ -165,6 +165,20 @@ class BinaryOpAssembler : public CodeStubAssembler {
TNode<Object> Generate_BitwiseBinaryOpWithSmiOperandAndOptionalFeedback(
Operation bitwise_op, TNode<Object> left, TNode<Object> right,
const LazyNode<Context>& context, TVariable<Smi>* feedback);
// Check if output is known to be Smi when both operands of bitwise operation
// are Smi.
bool IsBitwiseOutputKnownSmi(Operation bitwise_op) {
switch (bitwise_op) {
case Operation::kBitwiseAnd:
case Operation::kBitwiseOr:
case Operation::kBitwiseXor:
case Operation::kShiftRight:
return true;
default:
return false;
}
}
};
} // namespace internal
......
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