[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:
Igor Sheludko 2019-09-11 11:53:03 +02:00 committed by Commit Bot
parent 1304b3c986
commit 0f704b102f
3 changed files with 93 additions and 25 deletions

View File

@ -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);
{ {

View File

@ -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,

View File

@ -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) {