Inline NumberToString

NumberToString in runtime JavaScript is inlined through a call to a stub. Currently the stub only checks the number string cache and only if the number is a smi. Code is shared with the inlining of number string cache lookup when adding a smi to a string.
Review URL: http://codereview.chromium.org/604062

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3865 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
This commit is contained in:
sgjesse@chromium.org 2010-02-16 09:41:11 +00:00
parent 7f422b2a35
commit 6093d0dbf5
11 changed files with 154 additions and 36 deletions

View File

@ -3592,6 +3592,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
// Load the argument on the stack and jump to the runtime.
Load(args->at(0));
frame_->CallRuntime(Runtime::kNumberToString, 1);
frame_->EmitPush(r0);
}
void CodeGenerator::GenerateObjectEquals(ZoneList<Expression*>* args) {
VirtualFrame::SpilledScope spilled_scope;
ASSERT(args->length() == 2);

View File

@ -398,6 +398,9 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,

View File

@ -55,6 +55,7 @@ namespace internal {
V(CounterOp) \
V(ArgumentsAccess) \
V(RegExpExec) \
V(NumberToString) \
V(CEntry) \
V(JSEntry) \
V(DebuggerStatement)

View File

@ -372,6 +372,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
{&CodeGenerator::GenerateSubString, "_SubString"},
{&CodeGenerator::GenerateStringCompare, "_StringCompare"},
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
{&CodeGenerator::GenerateNumberToString, "_NumberToString"},
};

View File

@ -1767,6 +1767,7 @@ Object* Heap::SmiOrNumberFromDouble(double value,
Object* Heap::NumberToString(Object* number) {
Counters::number_to_string_runtime.Increment();
Object* cached = GetNumberStringCache(number);
if (cached != undefined_value()) {
return cached;

View File

@ -5627,6 +5627,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
// Load the argument on the stack and call the stub.
Load(args->at(0));
NumberToStringStub stub;
Result result = frame_->CallStub(&stub, 1);
frame_->Push(&result);
}
void CodeGenerator::VisitCallRuntime(CallRuntime* node) {
if (CheckForInlineRuntimeCall(node)) {
return;
@ -7782,38 +7793,10 @@ void GenericBinaryOpStub::Generate(MacroAssembler* masm) {
__ bind(&string1_smi2);
// First argument is a string, second is a smi. Try to lookup the number
// string for the smi in the number string cache.
// Load the number string cache.
ExternalReference roots_address = ExternalReference::roots_address();
__ mov(ecx, Immediate(Heap::kNumberStringCacheRootIndex));
__ mov(ebx,
Operand::StaticArray(ecx, times_pointer_size, roots_address));
// Make the hash mask from the length of the number string cache. It
// contains two elements (number and string) for each cache entry.
__ mov(ecx, FieldOperand(ebx, FixedArray::kLengthOffset));
__ shr(ecx, 1); // Divide length by two (length is not a smi).
__ sub(Operand(ecx), Immediate(1)); // Make mask.
// Calculate the entry in the number string cache. The hash value in the
// number string cache for smis is just the smi value.
__ mov(edi, eax);
__ SmiUntag(edi);
__ and_(edi, Operand(ecx));
// Check if the entry is the smi we are looking for.
__ cmp(eax,
FieldOperand(ebx,
edi,
times_twice_pointer_size,
FixedArray::kHeaderSize));
__ IncrementCounter(equal, &Counters::string_plus_smi_hit, 1);
__ IncrementCounter(not_equal, &Counters::string_plus_smi_miss, 1);
__ j(not_equal, &string1);
NumberToStringStub::GenerateLookupNumberStringCache(
masm, eax, edi, ebx, ecx, true, &string1);
// Get the string from the cache and call the string add stub to make the
// result.
__ mov(edi,
FieldOperand(ebx,
edi,
times_twice_pointer_size,
FixedArray::kHeaderSize + kPointerSize));
// Call the string add stub to make the result.
__ EnterInternalFrame();
__ push(edx); // Original first argument.
__ push(edi); // Number to string result for second argument.
@ -8951,6 +8934,74 @@ void RegExpExecStub::Generate(MacroAssembler* masm) {
}
void NumberToStringStub::GenerateLookupNumberStringCache(MacroAssembler* masm,
Register object,
Register result,
Register scratch1,
Register scratch2,
bool object_is_smi,
Label* not_found) {
// Currently only lookup for smis. Check for smi if object is not known to be
// a smi.
if (!object_is_smi) {
ASSERT(kSmiTag == 0);
__ test(object, Immediate(kSmiTagMask));
__ j(not_zero, not_found);
}
// Use of registers. Register result is used as a temporary.
Register number_string_cache = result;
Register mask = scratch1;
Register scratch = scratch2;
// Load the number string cache.
ExternalReference roots_address = ExternalReference::roots_address();
__ mov(scratch, Immediate(Heap::kNumberStringCacheRootIndex));
__ mov(number_string_cache,
Operand::StaticArray(scratch, times_pointer_size, roots_address));
// Make the hash mask from the length of the number string cache. It
// contains two elements (number and string) for each cache entry.
__ mov(mask, FieldOperand(number_string_cache, FixedArray::kLengthOffset));
__ shr(mask, 1); // Divide length by two (length is not a smi).
__ sub(Operand(mask), Immediate(1)); // Make mask.
// Calculate the entry in the number string cache. The hash value in the
// number string cache for smis is just the smi value.
__ mov(scratch, object);
__ SmiUntag(scratch);
__ and_(scratch, Operand(mask));
// Check if the entry is the smi we are looking for.
__ cmp(object,
FieldOperand(number_string_cache,
scratch,
times_twice_pointer_size,
FixedArray::kHeaderSize));
__ j(not_equal, not_found);
// Get the result from the cache.
__ mov(result,
FieldOperand(number_string_cache,
scratch,
times_twice_pointer_size,
FixedArray::kHeaderSize + kPointerSize));
__ IncrementCounter(&Counters::number_to_string_native, 1);
}
void NumberToStringStub::Generate(MacroAssembler* masm) {
Label runtime;
__ mov(ebx, Operand(esp, kPointerSize));
// Generate code to lookup number in the number string cache.
GenerateLookupNumberStringCache(masm, ebx, eax, ebx, ecx, false, &runtime);
__ ret(1 * kPointerSize);
__ bind(&runtime);
// Handle number to string in the runtime system if not found in the cache.
__ TailCallRuntime(ExternalReference(Runtime::kNumberToString), 1, 1);
}
void CompareStub::Generate(MacroAssembler* masm) {
Label call_builtin, done;

View File

@ -580,6 +580,9 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,
@ -860,6 +863,39 @@ class StringCompareStub: public StringStubBase {
};
class NumberToStringStub: public CodeStub {
public:
NumberToStringStub() { }
// Generate code to do a lookup in the number string cache. If the number in
// the register object is found in the cache the generated code falls through
// with the result in the result register. The object and the result register
// can be the same. If the number is not found in the cache the code jumps to
// the label not_found with only the content of register object unchanged.
static void GenerateLookupNumberStringCache(MacroAssembler* masm,
Register object,
Register result,
Register scratch1,
Register scratch2,
bool object_is_smi,
Label* not_found);
private:
Major MajorKey() { return NumberToString; }
int MinorKey() { return 0; }
void Generate(MacroAssembler* masm);
const char* GetName() { return "NumberToStringStub"; }
#ifdef DEBUG
void Print() {
PrintF("NumberToStringStub\n");
}
#endif
};
} } // namespace v8::internal
#endif // V8_IA32_CODEGEN_IA32_H_

View File

@ -178,7 +178,7 @@ function STRING_ADD_LEFT(y) {
y = %_ValueOf(y);
} else {
y = IS_NUMBER(y)
? %NumberToString(y)
? %_NumberToString(y)
: %ToString(%ToPrimitive(y, NO_HINT));
}
}
@ -194,7 +194,7 @@ function STRING_ADD_RIGHT(y) {
x = %_ValueOf(x);
} else {
x = IS_NUMBER(x)
? %NumberToString(x)
? %_NumberToString(x)
: %ToString(%ToPrimitive(x, NO_HINT));
}
}
@ -529,7 +529,7 @@ function ToNumber(x) {
// ECMA-262, section 9.8, page 35.
function ToString(x) {
if (IS_STRING(x)) return x;
if (IS_NUMBER(x)) return %NumberToString(x);
if (IS_NUMBER(x)) return %_NumberToString(x);
if (IS_BOOLEAN(x)) return x ? 'true' : 'false';
if (IS_UNDEFINED(x)) return 'undefined';
return (IS_NULL(x)) ? 'null' : %ToString(%DefaultString(x));

View File

@ -168,8 +168,8 @@ namespace internal {
SC(string_compare_runtime, V8.StringCompareRuntime) \
SC(regexp_entry_runtime, V8.RegExpEntryRuntime) \
SC(regexp_entry_native, V8.RegExpEntryNative) \
SC(string_plus_smi_hit, V8.StringPlusSmiHit) \
SC(string_plus_smi_miss, V8.StringPlusSmiMiss)
SC(number_to_string_native, V8.NumberToStringNative) \
SC(number_to_string_runtime, V8.NumberToStringRuntime)
// This file contains all the v8 counters that are in use.
class Counters : AllStatic {

View File

@ -3972,6 +3972,17 @@ void CodeGenerator::GenerateRegExpExec(ZoneList<Expression*>* args) {
}
void CodeGenerator::GenerateNumberToString(ZoneList<Expression*>* args) {
ASSERT_EQ(args->length(), 1);
// Load the argument on the stack and jump to the runtime.
Load(args->at(0));
Result answer = frame_->CallRuntime(Runtime::kNumberToString, 1);
frame_->Push(&answer);
}
void CodeGenerator::GenerateStringAdd(ZoneList<Expression*>* args) {
ASSERT_EQ(2, args->length());

View File

@ -577,6 +577,9 @@ class CodeGenerator: public AstVisitor {
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
// Fast support for number to string.
void GenerateNumberToString(ZoneList<Expression*>* args);
// Simple condition analysis.
enum ConditionAnalysis {
ALWAYS_TRUE,