Implemented one-char cache lookup in generated code.
authorvitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 26 Feb 2010 20:14:20 +0000 (20:14 +0000)
committervitalyr@chromium.org <vitalyr@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Fri, 26 Feb 2010 20:14:20 +0000 (20:14 +0000)
This speeds up string,charAt(n) and string[n].

Review URL: http://codereview.chromium.org/660184

git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@3980 ce2b1a6d-e550-0410-aec6-3dcde31c8c00

14 files changed:
src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/codegen.cc
src/heap.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/mips/codegen-mips.cc
src/mips/codegen-mips.h
src/runtime.js
src/string.js
src/x64/codegen-x64.cc
src/x64/codegen-x64.h
test/mjsunit/string-charat.js
test/mjsunit/string-index.js

index 6126898bbee446cd3dc47ba2fbfb953a65351a5a..ce84678667ffa073a75b25841cfd8b3b33a71219 100644 (file)
@@ -3414,6 +3414,44 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+  Comment(masm_, "[ GenerateCharFromCode");
+  ASSERT(args->length() == 1);
+
+  LoadAndSpill(args->at(0));
+  frame_->EmitPop(r0);
+
+  JumpTarget slow_case;
+  JumpTarget exit;
+
+  // Fast case of Heap::LookupSingleCharacterStringFromCode.
+  ASSERT(kSmiTag == 0);
+  ASSERT(kSmiShiftSize == 0);
+  ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+  __ tst(r0, Operand(kSmiTagMask |
+                     ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+  slow_case.Branch(nz);
+
+  ASSERT(kSmiTag == 0);
+  __ mov(r1, Operand(Factory::single_character_string_cache()));
+  __ add(r1, r1, Operand(r0, LSL, kPointerSizeLog2 - kSmiTagSize));
+  __ ldr(r1, MemOperand(r1, FixedArray::kHeaderSize - kHeapObjectTag));
+  __ LoadRoot(ip, Heap::kUndefinedValueRootIndex);
+  __ cmp(r1, ip);
+  slow_case.Branch(eq);
+
+  frame_->EmitPush(r1);
+  exit.Jump();
+
+  slow_case.Bind();
+  frame_->EmitPush(r0);
+  frame_->CallRuntime(Runtime::kCharFromCode, 1);
+  frame_->EmitPush(r0);
+
+  exit.Bind();
+}
+
+
 void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
   VirtualFrame::SpilledScope spilled_scope;
   ASSERT(args->length() == 1);
index 5ddd45e196d4491f1f868c2835447b3626d51afc..39f61b4c61d3acee644f77c40fe3959fbb69abb4 100644 (file)
@@ -370,6 +370,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for charCodeAt(n).
   void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
 
+  // Fast support for string.charAt(n) and string[n].
+  void GenerateCharFromCode(ZoneList<Expression*>* args);
+
   // Fast support for object equality testing.
   void GenerateObjectEquals(ZoneList<Expression*>* args);
 
index dc9f517158dd9d69e0ce1e6784b1b41e4f344a8a..9257ed44669cef3c35ee951270f5a9434ac8d5c9 100644 (file)
@@ -369,6 +369,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateValueOf, "_ValueOf"},
   {&CodeGenerator::GenerateSetValueOf, "_SetValueOf"},
   {&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"},
+  {&CodeGenerator::GenerateCharFromCode, "_CharFromCode"},
   {&CodeGenerator::GenerateObjectEquals, "_ObjectEquals"},
   {&CodeGenerator::GenerateLog, "_Log"},
   {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"},
index 157578da247a9537180a65266b72bc1e2df19950..859b5a38d45d4d78898908c5c01beae682e565d6 100644 (file)
@@ -1655,7 +1655,7 @@ bool Heap::CreateInitialObjects() {
   if (InitializeNumberStringCache()->IsFailure()) return false;
 
   // Allocate cache for single character strings.
-  obj = AllocateFixedArray(String::kMaxAsciiCharCode+1);
+  obj = AllocateFixedArray(String::kMaxAsciiCharCode+1, TENURED);
   if (obj->IsFailure()) return false;
   set_single_character_string_cache(FixedArray::cast(obj));
 
index 42ab6a88df6914938abd820805cc5a7e446da570..16c610a373ee36d001ecf194624e6fdb17ec5a2b 100644 (file)
@@ -5597,6 +5597,54 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+  Comment(masm_, "[ GenerateCharFromCode");
+  ASSERT(args->length() == 1);
+
+  Load(args->at(0));
+  Result code = frame_->Pop();
+  code.ToRegister();
+  ASSERT(code.is_valid());
+
+  Result temp = allocator()->Allocate();
+  ASSERT(temp.is_valid());
+
+  JumpTarget slow_case;
+  JumpTarget exit;
+
+  // Fast case of Heap::LookupSingleCharacterStringFromCode.
+  ASSERT(kSmiTag == 0);
+  ASSERT(kSmiShiftSize == 0);
+  ASSERT(IsPowerOf2(String::kMaxAsciiCharCode + 1));
+  __ test(code.reg(),
+          Immediate(kSmiTagMask |
+                    ((~String::kMaxAsciiCharCode) << kSmiTagSize)));
+  slow_case.Branch(not_zero, &code, not_taken);
+
+  __ Set(temp.reg(), Immediate(Factory::single_character_string_cache()));
+  ASSERT(kSmiTag == 0);
+  ASSERT(kSmiTagSize == 1);
+  ASSERT(kSmiShiftSize == 0);
+  // At this point code register contains smi tagged ascii char code.
+  __ mov(temp.reg(), FieldOperand(temp.reg(),
+                                  code.reg(), times_half_pointer_size,
+                                  FixedArray::kHeaderSize));
+  __ cmp(temp.reg(), Factory::undefined_value());
+  slow_case.Branch(equal, &code, not_taken);
+  code.Unuse();
+
+  frame_->Push(&temp);
+  exit.Jump();
+
+  slow_case.Bind(&code);
+  frame_->Push(&code);
+  Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
+  frame_->Push(&result);
+
+  exit.Bind();
+}
+
+
 void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
   ASSERT(args->length() == 1);
   Load(args->at(0));
index 06066115ddef834609c494577ee3c094fce4b50c..3681d77c44ee72e1bf9c8b93d8c6621500171408 100644 (file)
@@ -563,6 +563,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for charCodeAt(n).
   void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
 
+  // Fast support for string.charAt(n) and string[n].
+  void GenerateCharFromCode(ZoneList<Expression*>* args);
+
   // Fast support for object equality testing.
   void GenerateObjectEquals(ZoneList<Expression*>* args);
 
index 1446ff5f63579127940991dd0ead357f40e17d18..01b5816f8edab49b424afde7ea88baa37c620b64 100644 (file)
@@ -305,6 +305,11 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+  UNIMPLEMENTED_MIPS();
+}
+
+
 void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
   UNIMPLEMENTED_MIPS();
 }
index c6abb8231c87b166877b55fc00b31d7b93423523..1d7c27452ac6eda50c51e9c25c256716c7633f7f 100644 (file)
@@ -227,6 +227,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for charCodeAt(n).
   void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
 
+  // Fast support for string.charAt(n) and string[n].
+  void GenerateCharFromCode(ZoneList<Expression*>* args);
+
   // Fast support for object equality testing.
   void GenerateObjectEquals(ZoneList<Expression*>* args);
 
index e9d98487721a07a1abe004fbde6377b0710b5c64..d7770414a3b2376c408abdc271025de80629885c 100644 (file)
@@ -478,7 +478,7 @@ function STRING_CHAR_AT(pos) {
   if (!%_IsSmi(char_code)) {
     return %StringCharAt(this, pos);
   }
-  return %CharFromCode(char_code);
+  return %_CharFromCode(char_code);
 }
 
 
index 49f403de7da8140bb8aeebd9d87c00c329850a0d..531c6a9b949bbb4c897c761926fcd9164cbbb024 100644 (file)
@@ -69,7 +69,7 @@ function StringCharAt(pos) {
     if (index >= subject.length || index < 0) return "";
     char_code = %StringCharCodeAt(subject, index);
   }
-  return %CharFromCode(char_code);
+  return %_CharFromCode(char_code);
 }
 
 
@@ -184,7 +184,7 @@ function SubString(string, start, end) {
     if (!%_IsSmi(char_code)) {
       char_code = %StringCharCodeAt(string, start);
     }
-    return %CharFromCode(char_code);
+    return %_CharFromCode(char_code);
   }
   return %_SubString(string, start, end);
 }
@@ -726,7 +726,7 @@ function StringTrimRight() {
 // ECMA-262, section 15.5.3.2
 function StringFromCharCode(code) {
   var n = %_ArgumentsLength();
-  if (n == 1) return %CharFromCode(ToNumber(code) & 0xffff)
+  if (n == 1) return %_CharFromCode(ToNumber(code) & 0xffff)
 
   // NOTE: This is not super-efficient, but it is necessary because we
   // want to avoid converting to numbers from within the virtual
index d2df5a875fe962c57d3d01d7d1f3b5ff806787c6..0725e311b85f34a4002086520d530221a70137ff 100644 (file)
@@ -3877,6 +3877,49 @@ void CodeGenerator::GenerateFastCharCodeAt(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateCharFromCode(ZoneList<Expression*>* args) {
+  Comment(masm_, "[ GenerateCharFromCode");
+  ASSERT(args->length() == 1);
+
+  Load(args->at(0));
+  Result code = frame_->Pop();
+  code.ToRegister();
+  ASSERT(code.is_valid());
+
+  Result temp = allocator()->Allocate();
+  ASSERT(temp.is_valid());
+
+  JumpTarget slow_case;
+  JumpTarget exit;
+
+  // Fast case of Heap::LookupSingleCharacterStringFromCode.
+  Condition is_smi = __ CheckSmi(code.reg());
+  slow_case.Branch(NegateCondition(is_smi), &code, not_taken);
+
+  __ SmiToInteger32(kScratchRegister, code.reg());
+  __ cmpl(kScratchRegister, Immediate(String::kMaxAsciiCharCode));
+  slow_case.Branch(above, &code, not_taken);
+
+  __ Move(temp.reg(), Factory::single_character_string_cache());
+  __ movq(temp.reg(), FieldOperand(temp.reg(),
+                                   kScratchRegister, times_pointer_size,
+                                   FixedArray::kHeaderSize));
+  __ CompareRoot(temp.reg(), Heap::kUndefinedValueRootIndex);
+  slow_case.Branch(equal, &code, not_taken);
+  code.Unuse();
+
+  frame_->Push(&temp);
+  exit.Jump();
+
+  slow_case.Bind(&code);
+  frame_->Push(&code);
+  Result result = frame_->CallRuntime(Runtime::kCharFromCode, 1);
+  frame_->Push(&result);
+
+  exit.Bind();
+}
+
+
 void CodeGenerator::GenerateIsNonNegativeSmi(ZoneList<Expression*>* args) {
   ASSERT(args->length() == 1);
   Load(args->at(0));
index 744be57be535a228cee3beda30fda53dd988303b..62f82a28e9aa57de2a534a3284794228dce0f465 100644 (file)
@@ -547,6 +547,9 @@ class CodeGenerator: public AstVisitor {
   // Fast support for charCodeAt(n).
   void GenerateFastCharCodeAt(ZoneList<Expression*>* args);
 
+  // Fast support for string.charAt(n) and string[n].
+  void GenerateCharFromCode(ZoneList<Expression*>* args);
+
   // Fast support for object equality testing.
   void GenerateObjectEquals(ZoneList<Expression*>* args);
 
index 8ec8f1e08f5b3b7c40ecffaabac410f45b9db378..d1989dfd731177e50c4bd3dcbdd585995901b16e 100644 (file)
@@ -51,3 +51,16 @@ assertEquals(116, s.charCodeAt(NaN));
 assertTrue(isNaN(s.charCodeAt(-1)));
 assertTrue(isNaN(s.charCodeAt(4)));
 
+// Make sure enough of the one-char string cache is filled.
+var alpha = ['@'];
+for (var i = 1; i < 128; i++) {
+  var c = String.fromCharCode(i);
+  alpha[i] = c.charAt(0);
+}
+var alphaStr = alpha.join("");
+
+// Now test chars.
+for (var i = 1; i < 128; i++) {
+  assertEquals(alpha[i], alphaStr.charAt(i));
+  assertEquals(String.fromCharCode(i), alphaStr.charAt(i));
+}
index 2256286eece5e1dc072784b932eac2d909ecc6c4..c6b26a85eea65b22f82bdf742d02e435bb51ccb8 100644 (file)
@@ -152,3 +152,17 @@ assertEquals('o', S2);
 var s2 = (s[-2] = 't');
 assertEquals('undefined', typeof(s[-2]));
 assertEquals('t', s2);
+
+// Make sure enough of the one-char string cache is filled.
+var alpha = ['@'];
+for (var i = 1; i < 128; i++) {
+  var c = String.fromCharCode(i);
+  alpha[i] = c[0];
+}
+var alphaStr = alpha.join("");
+
+// Now test chars.
+for (var i = 1; i < 128; i++) {
+  assertEquals(alpha[i], alphaStr[i]);
+  assertEquals(String.fromCharCode(i), alphaStr[i]);
+}