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:
parent
7f422b2a35
commit
6093d0dbf5
@ -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);
|
||||
|
@ -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,
|
||||
|
@ -55,6 +55,7 @@ namespace internal {
|
||||
V(CounterOp) \
|
||||
V(ArgumentsAccess) \
|
||||
V(RegExpExec) \
|
||||
V(NumberToString) \
|
||||
V(CEntry) \
|
||||
V(JSEntry) \
|
||||
V(DebuggerStatement)
|
||||
|
@ -372,6 +372,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
|
||||
{&CodeGenerator::GenerateSubString, "_SubString"},
|
||||
{&CodeGenerator::GenerateStringCompare, "_StringCompare"},
|
||||
{&CodeGenerator::GenerateRegExpExec, "_RegExpExec"},
|
||||
{&CodeGenerator::GenerateNumberToString, "_NumberToString"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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_
|
||||
|
@ -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));
|
||||
|
@ -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 {
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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,
|
||||
|
Loading…
Reference in New Issue
Block a user