Implement IS_OBJECT and IS_FUNCTION as inlined runtime functions.
authorfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 19 Nov 2009 07:41:32 +0000 (07:41 +0000)
committerfschneider@chromium.org <fschneider@chromium.org@ce2b1a6d-e550-0410-aec6-3dcde31c8c00>
Thu, 19 Nov 2009 07:41:32 +0000 (07:41 +0000)
Summary:
This change fixes a performance regression introduced by the special
handling of regular expressions in typeof expressions.
As a result we regain ~8% speedup on 3d-raytrace and ~13% on boyer
(vs bleeding edge)

Description:
The macros IS_OBJECT and IS_FUNCTION are frequently used in the
JS runtime functions.
By introducing new inlined runtime functions %_IsFunction and %_IsObject
we avoid invoking the more expensive %_ClassOf function plus comparing
its result to a string.

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

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

src/arm/codegen-arm.cc
src/arm/codegen-arm.h
src/codegen.cc
src/ia32/codegen-ia32.cc
src/ia32/codegen-ia32.h
src/macros.py
src/x64/codegen-x64.cc
src/x64/codegen-x64.h

index b08615e613e89ff266a56a8a97de7933aec627c9..c62756d5a53337a779f5a1edafbb308602d7eb61 100644 (file)
@@ -3385,6 +3385,51 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
+  VirtualFrame::SpilledScope spilled_scope;
+  ASSERT(args->length() == 1);
+  LoadAndSpill(args->at(0));
+  frame_->EmitPop(r1);
+  __ tst(r1, Operand(kSmiTagMask));
+  false_target()->Branch(eq);
+
+  __ LoadRoot(ip, Heap::kNullValueRootIndex);
+  __ cmp(r1, ip);
+  true_target()->Branch(eq);
+
+  Register map_reg = r2;
+  __ ldr(map_reg, FieldMemOperand(r1, HeapObject::kMapOffset));
+  // Undetectable objects behave like undefined when tested with typeof.
+  __ ldrb(r1, FieldMemOperand(map_reg, Map::kBitFieldOffset));
+  __ and_(r1, r1, Operand(1 << Map::kIsUndetectable));
+  __ cmp(r1, Operand(1 << Map::kIsUndetectable));
+  false_target()->Branch(eq);
+
+  __ ldrb(r1, FieldMemOperand(map_reg, Map::kInstanceTypeOffset));
+  __ cmp(r1, Operand(FIRST_JS_OBJECT_TYPE));
+  false_target()->Branch(lt);
+  __ cmp(r1, Operand(LAST_JS_OBJECT_TYPE));
+  cc_reg_ = le;
+}
+
+
+void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (%_ClassOf(arg) === 'Function')
+  VirtualFrame::SpilledScope spilled_scope;
+  ASSERT(args->length() == 1);
+  LoadAndSpill(args->at(0));
+  frame_->EmitPop(r0);
+  __ tst(r0, Operand(kSmiTagMask));
+  false_target()->Branch(eq);
+  Register map_reg = r2;
+  __ CompareObjectType(r0, map_reg, r1, JS_FUNCTION_TYPE);
+  cc_reg_ = eq;
+}
+
+
 void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
   VirtualFrame::SpilledScope spilled_scope;
   ASSERT(args->length() == 0);
index 8cbf450f32633d936b2deabbcce8ca27176136fa..30a1ae57299286a1ef9c70a964ea45ccd4d5e290 100644 (file)
@@ -334,6 +334,8 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsObject(ZoneList<Expression*>* args);
+  void GenerateIsFunction(ZoneList<Expression*>* args);
 
   // Support for construct call checks.
   void GenerateIsConstructCall(ZoneList<Expression*>* args);
index 6917d459c70efd50b622b7a2ce9bed002c1326dc..a6d5fb47f94569c85acd17f9f3e80ac99b901c2b 100644 (file)
@@ -343,7 +343,9 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = {
   {&CodeGenerator::GenerateLog, "_Log"},
   {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"},
   {&CodeGenerator::GenerateMathSin, "_Math_sin"},
-  {&CodeGenerator::GenerateMathCos, "_Math_cos"}
+  {&CodeGenerator::GenerateMathCos, "_Math_cos"},
+  {&CodeGenerator::GenerateIsObject, "_IsObject"},
+  {&CodeGenerator::GenerateIsFunction, "_IsFunction"},
 };
 
 
index 69a17cd9b5d24425a716aa81b461ba4950b8527d..ac2a7a0262435c3e7e3288ec1932fd9cf075ea56 100644 (file)
@@ -4870,6 +4870,55 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result obj = frame_->Pop();
+  obj.ToRegister();
+
+  __ test(obj.reg(), Immediate(kSmiTagMask));
+  destination()->false_target()->Branch(zero);
+  __ cmp(obj.reg(), Factory::null_value());
+  destination()->true_target()->Branch(equal);
+
+  Result map = allocator()->Allocate();
+  ASSERT(map.is_valid());
+  __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
+  // Undetectable objects behave like undefined when tested with typeof.
+  __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kBitFieldOffset));
+  __ test(map.reg(), Immediate(1 << Map::kIsUndetectable));
+  destination()->false_target()->Branch(not_zero);
+  __ mov(map.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset));
+  __ movzx_b(map.reg(), FieldOperand(map.reg(), Map::kInstanceTypeOffset));
+  __ cmp(map.reg(), FIRST_JS_OBJECT_TYPE);
+  destination()->false_target()->Branch(less);
+  __ cmp(map.reg(), LAST_JS_OBJECT_TYPE);
+  obj.Unuse();
+  map.Unuse();
+  destination()->Split(less_equal);
+}
+
+
+void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (%_ClassOf(arg) === 'Function')
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result obj = frame_->Pop();
+  obj.ToRegister();
+  __ test(obj.reg(), Immediate(kSmiTagMask));
+  destination()->false_target()->Branch(zero);
+  Result temp = allocator()->Allocate();
+  ASSERT(temp.is_valid());
+  __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, temp.reg());
+  obj.Unuse();
+  temp.Unuse();
+  destination()->Split(equal);
+}
+
+
 void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
   ASSERT(args->length() == 0);
 
index 0e69a63d8989a6dfa37e17e1975e556f3cbe06c2..ebab3caa126625b177800af092c41c733c18908e 100644 (file)
@@ -512,6 +512,8 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsObject(ZoneList<Expression*>* args);
+  void GenerateIsFunction(ZoneList<Expression*>* args);
 
   // Support for construct call checks.
   void GenerateIsConstructCall(ZoneList<Expression*>* args);
index d6a2426c5a0dcd457368a80359b9023f27266a09..5b06099a850f2abcb968a67a95c39a3a3eb5d87c 100644 (file)
@@ -79,11 +79,10 @@ macro IS_NULL_OR_UNDEFINED(arg) = (arg == null);
 macro IS_UNDEFINED(arg)         = (typeof(arg) === 'undefined');
 macro IS_NUMBER(arg)            = (typeof(arg) === 'number');
 macro IS_STRING(arg)            = (typeof(arg) === 'string');
-macro IS_OBJECT(arg)            = (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp');
 macro IS_BOOLEAN(arg)           = (typeof(arg) === 'boolean');
+macro IS_OBJECT(arg)            = (%_IsObject(arg));
 macro IS_ARRAY(arg)             = (%_IsArray(arg));
-# IS_FUNCTION uses %_ClassOf rather than typeof so as to exclude regexps.
-macro IS_FUNCTION(arg)          = (%_ClassOf(arg) === 'Function');
+macro IS_FUNCTION(arg)          = (%_IsFunction(arg));
 macro IS_REGEXP(arg)            = (%_ClassOf(arg) === 'RegExp');
 macro IS_DATE(arg)              = (%_ClassOf(arg) === 'Date');
 macro IS_NUMBER_WRAPPER(arg)    = (%_ClassOf(arg) === 'Number');
index e2296d9bd951678f268ca8fd1cb410d529acf031..b32357266593ce8189b49f655be972e8746087ba 100644 (file)
@@ -3616,6 +3616,48 @@ void CodeGenerator::GenerateIsArray(ZoneList<Expression*>* args) {
 }
 
 
+void CodeGenerator::GenerateIsObject(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (typeof(arg) === 'object' || %_ClassOf(arg) == 'RegExp')
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result obj = frame_->Pop();
+  obj.ToRegister();
+  Condition is_smi = masm_->CheckSmi(obj.reg());
+  destination()->false_target()->Branch(is_smi);
+
+  __ Move(kScratchRegister, Factory::null_value());
+  __ cmpq(obj.reg(), kScratchRegister);
+  destination()->true_target()->Branch(equal);
+
+  __ movq(kScratchRegister, FieldOperand(obj.reg(), HeapObject::kMapOffset));
+  // Undetectable objects behave like undefined when tested with typeof.
+  __ testb(FieldOperand(kScratchRegister, Map::kBitFieldOffset),
+          Immediate(1 << Map::kIsUndetectable));
+  destination()->false_target()->Branch(not_zero);
+  __ CmpInstanceType(kScratchRegister, FIRST_JS_OBJECT_TYPE);
+  destination()->false_target()->Branch(less);
+  __ CmpInstanceType(kScratchRegister, LAST_JS_OBJECT_TYPE);
+  obj.Unuse();
+  destination()->Split(less_equal);
+}
+
+
+void CodeGenerator::GenerateIsFunction(ZoneList<Expression*>* args) {
+  // This generates a fast version of:
+  // (%_ClassOf(arg) === 'Function')
+  ASSERT(args->length() == 1);
+  Load(args->at(0));
+  Result obj = frame_->Pop();
+  obj.ToRegister();
+  Condition is_smi = masm_->CheckSmi(obj.reg());
+  destination()->false_target()->Branch(is_smi);
+  __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, kScratchRegister);
+  obj.Unuse();
+  destination()->Split(equal);
+}
+
+
 void CodeGenerator::GenerateIsConstructCall(ZoneList<Expression*>* args) {
   ASSERT(args->length() == 0);
 
index 0301daf3d70d8dff3497213d96ed85ad085099ce..20df41a50b6684a1498fa03b5448cc102d4dbce2 100644 (file)
@@ -510,6 +510,8 @@ class CodeGenerator: public AstVisitor {
   void GenerateIsSmi(ZoneList<Expression*>* args);
   void GenerateIsNonNegativeSmi(ZoneList<Expression*>* args);
   void GenerateIsArray(ZoneList<Expression*>* args);
+  void GenerateIsObject(ZoneList<Expression*>* args);
+  void GenerateIsFunction(ZoneList<Expression*>* args);
 
   // Support for construct call checks.
   void GenerateIsConstructCall(ZoneList<Expression*>* args);