Commit 0f704b10 authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[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/+/1796802Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63674}
parent 1304b3c9
......@@ -7700,22 +7700,22 @@ TNode<Number> CodeStubAssembler::StringToNumber(TNode<String> input) {
return var_result.value();
}
TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input,
Label* bailout) {
TVARIABLE(String, result);
TVARIABLE(Smi, smi_input);
Label runtime(this, Label::kDeferred), if_smi(this), if_heap_number(this),
done(this, &result);
Label if_smi(this), if_heap_number(this), done(this, &result);
// Load the number string cache.
TNode<FixedArray> number_string_cache = NumberStringCacheConstant();
// Make the hash mask from the length of the number string cache. It
// contains two elements (number and string) for each cache entry.
// TODO(ishell): cleanup mask handling.
TNode<IntPtrT> mask =
BitcastTaggedSignedToWord(LoadFixedArrayBaseLength(number_string_cache));
TNode<IntPtrT> one = IntPtrConstant(1);
mask = IntPtrSub(mask, one);
TNode<IntPtrT> number_string_cache_length =
LoadAndUntagFixedArrayBaseLength(number_string_cache);
TNode<Int32T> one = Int32Constant(1);
TNode<Word32T> mask = Int32Sub(
Word32Shr(TruncateWordToInt32(number_string_cache_length), one), one);
GotoIfNot(TaggedIsSmi(input), &if_heap_number);
smi_input = CAST(input);
......@@ -7733,29 +7733,28 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
LoadObjectField<Int32T>(heap_number_input, HeapNumber::kValueOffset);
TNode<Int32T> high = LoadObjectField<Int32T>(
heap_number_input, HeapNumber::kValueOffset + kIntSize);
TNode<Word32T> hash = Word32Xor(low, high);
TNode<IntPtrT> word_hash = WordShl(ChangeInt32ToIntPtr(hash), one);
TNode<WordT> index =
WordAnd(word_hash, WordSar(mask, SmiShiftBitsConstant()));
TNode<Word32T> hash = Word32And(Word32Xor(low, high), mask);
TNode<IntPtrT> entry_index =
Signed(ChangeUint32ToWord(Int32Add(hash, hash)));
// Cache entry's key must be a heap number
TNode<Object> number_key =
UnsafeLoadFixedArrayElement(number_string_cache, index);
GotoIf(TaggedIsSmi(number_key), &runtime);
UnsafeLoadFixedArrayElement(number_string_cache, entry_index);
GotoIf(TaggedIsSmi(number_key), bailout);
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.
TNode<Int32T> low_compare = LoadObjectField<Int32T>(
number_key_heap_object, HeapNumber::kValueOffset);
TNode<Int32T> high_compare = LoadObjectField<Int32T>(
number_key_heap_object, HeapNumber::kValueOffset + kIntSize);
GotoIfNot(Word32Equal(low, low_compare), &runtime);
GotoIfNot(Word32Equal(high, high_compare), &runtime);
GotoIfNot(Word32Equal(low, low_compare), bailout);
GotoIfNot(Word32Equal(high, high_compare), bailout);
// Heap number match, return value from cache entry.
result = CAST(
UnsafeLoadFixedArrayElement(number_string_cache, index, kTaggedSize));
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,
kTaggedSize));
Goto(&done);
}
......@@ -7763,17 +7762,28 @@ TNode<String> CodeStubAssembler::NumberToString(TNode<Number> input) {
{
Comment("NumberToString - Smi");
// Load the smi key, make sure it matches the smi we're looking for.
TNode<Object> smi_index = BitcastWordToTagged(WordAnd(
WordShl(BitcastTaggedSignedToWord(smi_input.value()), one), mask));
TNode<Word32T> hash = Word32And(SmiToInt32(smi_input.value()), mask);
TNode<IntPtrT> entry_index =
Signed(ChangeUint32ToWord(Int32Add(hash, hash)));
TNode<Object> smi_key = UnsafeLoadFixedArrayElement(
number_string_cache, smi_index, 0, SMI_PARAMETERS);
GotoIf(TaggedNotEqual(smi_key, smi_input.value()), &runtime);
number_string_cache, entry_index, 0, INTPTR_PARAMETERS);
GotoIf(TaggedNotEqual(smi_key, smi_input.value()), bailout);
// Smi match, return value from cache entry.
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, smi_index,
kTaggedSize, SMI_PARAMETERS));
result = CAST(UnsafeLoadFixedArrayElement(number_string_cache, entry_index,
kTaggedSize, INTPTR_PARAMETERS));
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);
{
......
......@@ -2598,6 +2598,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Number> StringToNumber(TNode<String> input);
// Convert a Number to a String.
TNode<String> NumberToString(TNode<Number> input);
TNode<String> NumberToString(TNode<Number> input, Label* bailout);
// Convert a Non-Number object to a Number.
TNode<Number> NonNumberToNumber(
SloppyTNode<Context> context, SloppyTNode<HeapObject> input,
......
......@@ -123,6 +123,62 @@ TEST(CallCFunctionWithCallerSavedRegisters) {
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 {
void CheckToUint32Result(uint32_t expected, Handle<Object> result) {
......
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