[ptr-compr] Use 32-bit operations in CSA::NumberToString implementation
... to make it "smi-corrupting" decompression-friendly. Also add a cctest for the CSA implementation. Bug: v8:9706 Change-Id: I1f1b0aa1b40832a0c2ce81658da316b3e442189c Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1796802 Reviewed-by: Toon Verwaest <verwaest@chromium.org> Commit-Queue: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#63674}
This commit is contained in:
parent
1304b3c986
commit
0f704b102f
@ -7700,22 +7700,22 @@ TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) {
|
|||||||
return var_result.value();
|
return var_result.value();
|
||||||
}
|
}
|
||||||
|
|
||||||
TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
|
TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input,
|
||||||
|
Label* bailout) {
|
||||||
TVARIABLE(String, result);
|
TVARIABLE(String, result);
|
||||||
TVARIABLE(Smi, smi_input);
|
TVARIABLE(Smi, smi_input);
|
||||||
Label runtime(this, Label::kDeferred), if_smi(this), if_heap_number(this),
|
Label if_smi(this), if_heap_number(this), done(this, &result);
|
||||||
done(this, &result);
|
|
||||||
|
|
||||||
// Load the number string cache.
|
// Load the number string cache.
|
||||||
TNode<FixedArray> number_string_cache = NumberStringCacheConstant();
|
TNode<FixedArray> number_string_cache = NumberStringCacheConstant();
|
||||||
|
|
||||||
// Make the hash mask from the length of the number string cache. It
|
// Make the hash mask from the length of the number string cache. It
|
||||||
// contains two elements (number and string) for each cache entry.
|
// contains two elements (number and string) for each cache entry.
|
||||||
// TODO(ishell): cleanup mask handling.
|
TNode<IntPtrT> number_string_cache_length =
|
||||||
TNode<IntPtrT> mask =
|
LoadAndUntagFixedArrayBaseLength(number_string_cache);
|
||||||
BitcastTaggedSignedToWord(LoadFixedArrayBaseLength(number_string_cache));
|
TNode<Int32T> one = Int32Constant(1);
|
||||||
TNode<IntPtrT> one = IntPtrConstant(1);
|
TNode<Word32T> mask = Int32Sub(
|
||||||
mask = IntPtrSub(mask, one);
|
Word32Shr(TruncateWordToInt32(number_string_cache_length), one), one);
|
||||||
|
|
||||||
GotoIfNot(TaggedIsSmi(input), &if_heap_number);
|
GotoIfNot(TaggedIsSmi(input), &if_heap_number);
|
||||||
smi_input = CAST(input);
|
smi_input = CAST(input);
|
||||||
@ -7733,29 +7733,28 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
|
|||||||
LoadObjectField<Int32T>(heap_number_input, HeapNumber::kValueOffset);
|
LoadObjectField<Int32T>(heap_number_input, HeapNumber::kValueOffset);
|
||||||
TNode<Int32T> high = LoadObjectField<Int32T>(
|
TNode<Int32T> high = LoadObjectField<Int32T>(
|
||||||
heap_number_input, HeapNumber::kValueOffset + kIntSize);
|
heap_number_input, HeapNumber::kValueOffset + kIntSize);
|
||||||
TNode<Word32T> hash = Word32Xor(low, high);
|
TNode<Word32T> hash = Word32And(Word32Xor(low, high), mask);
|
||||||
TNode<IntPtrT> word_hash = WordShl(ChangeInt32ToIntPtr(hash), one);
|
TNode<IntPtrT> entry_index =
|
||||||
TNode<WordT> index =
|
Signed(ChangeUint32ToWord(Int32Add(hash, hash)));
|
||||||
WordAnd(word_hash, WordSar(mask, SmiShiftBitsConstant()));
|
|
||||||
|
|
||||||
// Cache entry's key must be a heap number
|
// Cache entry's key must be a heap number
|
||||||
TNode<Object> number_key =
|
TNode<Object> number_key =
|
||||||
UnsafeLoadFixedArrayElement(number_string_cache, index);
|
UnsafeLoadFixedArrayElement(number_string_cache, entry_index);
|
||||||
GotoIf(TaggedIsSmi(number_key), &runtime);
|
GotoIf(TaggedIsSmi(number_key), bailout);
|
||||||
TNode<HeapObject> number_key_heap_object = CAST(number_key);
|
TNode<HeapObject> number_key_heap_object = CAST(number_key);
|
||||||
GotoIfNot(IsHeapNumber(number_key_heap_object), &runtime);
|
GotoIfNot(IsHeapNumber(number_key_heap_object), bailout);
|
||||||
|
|
||||||
// Cache entry's key must match the heap number value we're looking for.
|
// Cache entry's key must match the heap number value we're looking for.
|
||||||
TNode<Int32T> low_compare = LoadObjectField<Int32T>(
|
TNode<Int32T> low_compare = LoadObjectField<Int32T>(
|
||||||
number_key_heap_object, HeapNumber::kValueOffset);
|
number_key_heap_object, HeapNumber::kValueOffset);
|
||||||
TNode<Int32T> high_compare = LoadObjectField<Int32T>(
|
TNode<Int32T> high_compare = LoadObjectField<Int32T>(
|
||||||
number_key_heap_object, HeapNumber::kValueOffset + kIntSize);
|
number_key_heap_object, HeapNumber::kValueOffset + kIntSize);
|
||||||
GotoIfNot(Word32Equal(low, low_compare), &runtime);
|
GotoIfNot(Word32Equal(low, low_compare), bailout);
|
||||||
GotoIfNot(Word32Equal(high, high_compare), &runtime);
|
GotoIfNot(Word32Equal(high, high_compare), bailout);
|
||||||
|
|
||||||
// Heap number match, return value from cache entry.
|
// Heap number match, return value from cache entry.
|
||||||
result = CAST(
|
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,
|
||||||
UnsafeLoadFixedArrayElement(number_string_cache, index, kTaggedSize));
|
kTaggedSize));
|
||||||
Goto(&done);
|
Goto(&done);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -7763,17 +7762,28 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
|
|||||||
{
|
{
|
||||||
Comment("NumberToString - Smi");
|
Comment("NumberToString - Smi");
|
||||||
// Load the smi key, make sure it matches the smi we're looking for.
|
// Load the smi key, make sure it matches the smi we're looking for.
|
||||||
TNode<Object> smi_index = BitcastWordToTagged(WordAnd(
|
TNode<Word32T> hash = Word32And(SmiToInt32(smi_input.value()), mask);
|
||||||
WordShl(BitcastTaggedSignedToWord(smi_input.value()), one), mask));
|
TNode<IntPtrT> entry_index =
|
||||||
|
Signed(ChangeUint32ToWord(Int32Add(hash, hash)));
|
||||||
TNode<Object> smi_key = UnsafeLoadFixedArrayElement(
|
TNode<Object> smi_key = UnsafeLoadFixedArrayElement(
|
||||||
number_string_cache, smi_index, 0, SMI_PARAMETERS);
|
number_string_cache, entry_index, 0, INTPTR_PARAMETERS);
|
||||||
GotoIf(TaggedNotEqual(smi_key, smi_input.value()), &runtime);
|
GotoIf(TaggedNotEqual(smi_key, smi_input.value()), bailout);
|
||||||
|
|
||||||
// Smi match, return value from cache entry.
|
// Smi match, return value from cache entry.
|
||||||
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, smi_index,
|
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,
|
||||||
kTaggedSize, SMI_PARAMETERS));
|
kTaggedSize, INTPTR_PARAMETERS));
|
||||||
Goto(&done);
|
Goto(&done);
|
||||||
}
|
}
|
||||||
|
BIND(&done);
|
||||||
|
return result.value();
|
||||||
|
}
|
||||||
|
|
||||||
|
TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
|
||||||
|
TVARIABLE(String, result);
|
||||||
|
Label runtime(this, Label::kDeferred), done(this, &result);
|
||||||
|
|
||||||
|
result = NumberToString(input, &runtime);
|
||||||
|
Goto(&done);
|
||||||
|
|
||||||
BIND(&runtime);
|
BIND(&runtime);
|
||||||
{
|
{
|
||||||
|
@ -2598,6 +2598,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
|
|||||||
TNode<Number> StringToNumber(TNode<String> input);
|
TNode<Number> StringToNumber(TNode<String> input);
|
||||||
// Convert a Number to a String.
|
// Convert a Number to a String.
|
||||||
TNode<String> NumberToString(TNode<Number> input);
|
TNode<String> NumberToString(TNode<Number> input);
|
||||||
|
TNode<String> NumberToString(TNode<Number> input, Label* bailout);
|
||||||
|
|
||||||
// Convert a Non-Number object to a Number.
|
// Convert a Non-Number object to a Number.
|
||||||
TNode<Number> NonNumberToNumber(
|
TNode<Number> NonNumberToNumber(
|
||||||
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
|
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
|
||||||
|
@ -123,6 +123,62 @@ TEST(CallCFunctionWithCallerSavedRegisters) {
|
|||||||
CHECK_EQ(3, Handle<Smi>::cast(result)->value());
|
CHECK_EQ(3, Handle<Smi>::cast(result)->value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST(NumberToString) {
|
||||||
|
Isolate* isolate(CcTest::InitIsolateOnce());
|
||||||
|
Factory* factory = isolate->factory();
|
||||||
|
|
||||||
|
const int kNumParams = 1;
|
||||||
|
CodeAssemblerTester asm_tester(isolate, kNumParams);
|
||||||
|
CodeStubAssembler m(asm_tester.state());
|
||||||
|
|
||||||
|
{
|
||||||
|
TNode<Number> input = m.CAST(m.Parameter(0));
|
||||||
|
|
||||||
|
Label bailout(&m);
|
||||||
|
m.Return(m.NumberToString(input, &bailout));
|
||||||
|
|
||||||
|
m.BIND(&bailout);
|
||||||
|
m.Return(m.UndefinedConstant());
|
||||||
|
}
|
||||||
|
|
||||||
|
FunctionTester ft(asm_tester.GenerateCode(), kNumParams);
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
double inputs[] = {
|
||||||
|
1, 2, 42, 153, -1, -100, 0, 51095154, -1241950,
|
||||||
|
std::nan("-1"), std::nan("1"), std::nan("2"),
|
||||||
|
-std::numeric_limits<double>::infinity(),
|
||||||
|
std::numeric_limits<double>::infinity(),
|
||||||
|
-0.0, -0.001, -0.5, -0.999, -1.0,
|
||||||
|
0.0, 0.001, 0.5, 0.999, 1.0,
|
||||||
|
-2147483647.9, -2147483648.0, -2147483648.5, -2147483648.9, // SmiMin.
|
||||||
|
2147483646.9, 2147483647.0, 2147483647.5, 2147483647.9, // SmiMax.
|
||||||
|
-4294967295.9, -4294967296.0, -4294967296.5, -4294967297.0, // - 2^32.
|
||||||
|
4294967295.9, 4294967296.0, 4294967296.5, 4294967297.0, // 2^32.
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
const int kFullCacheSize = isolate->heap()->MaxNumberToStringCacheSize();
|
||||||
|
const int test_count = arraysize(inputs);
|
||||||
|
for (int i = 0; i < test_count; i++) {
|
||||||
|
int cache_length_before_addition = factory->number_string_cache()->length();
|
||||||
|
Handle<Object> input = factory->NewNumber(inputs[i]);
|
||||||
|
Handle<String> expected = factory->NumberToString(input);
|
||||||
|
|
||||||
|
Handle<Object> result = ft.Call(input).ToHandleChecked();
|
||||||
|
if (result->IsUndefined(isolate)) {
|
||||||
|
// Query may fail if cache was resized, in which case the entry is not
|
||||||
|
// added to the cache.
|
||||||
|
CHECK_LT(cache_length_before_addition, kFullCacheSize);
|
||||||
|
CHECK_EQ(factory->number_string_cache()->length(), kFullCacheSize);
|
||||||
|
expected = factory->NumberToString(input);
|
||||||
|
result = ft.Call(input).ToHandleChecked();
|
||||||
|
}
|
||||||
|
CHECK(!result->IsUndefined(isolate));
|
||||||
|
CHECK_EQ(*expected, *result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
void CheckToUint32Result(uint32_t expected, Handle<Object> result) {
|
void CheckToUint32Result(uint32_t expected, Handle<Object> result) {
|
||||||
|
Loading…
Reference in New Issue
Block a user