From d9d18b30f5f1f02a0159444f101b630f133984af Mon Sep 17 00:00:00 2001 From: "fschneider@chromium.org" Date: Thu, 19 Nov 2009 07:41:32 +0000 Subject: [PATCH] Implement IS_OBJECT and IS_FUNCTION as inlined runtime functions. 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 | 45 ++++++++++++++++++++++++++++++++++++++++++++ src/arm/codegen-arm.h | 2 ++ src/codegen.cc | 4 +++- src/ia32/codegen-ia32.cc | 49 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ia32/codegen-ia32.h | 2 ++ src/macros.py | 5 ++--- src/x64/codegen-x64.cc | 42 +++++++++++++++++++++++++++++++++++++++++ src/x64/codegen-x64.h | 2 ++ 8 files changed, 147 insertions(+), 4 deletions(-) diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index b08615e..c62756d 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3385,6 +3385,51 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* 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* 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* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 0); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 8cbf450..30a1ae5 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -334,6 +334,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); diff --git a/src/codegen.cc b/src/codegen.cc index 6917d45..a6d5fb4 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -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"}, }; diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index 69a17cd..ac2a7a0 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -4870,6 +4870,55 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* 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* 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* args) { ASSERT(args->length() == 0); diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index 0e69a63..ebab3ca 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -512,6 +512,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); diff --git a/src/macros.py b/src/macros.py index d6a2426..5b06099 100644 --- a/src/macros.py +++ b/src/macros.py @@ -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'); diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index e2296d9..b323572 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -3616,6 +3616,48 @@ void CodeGenerator::GenerateIsArray(ZoneList* args) { } +void CodeGenerator::GenerateIsObject(ZoneList* 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* 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* args) { ASSERT(args->length() == 0); diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index 0301daf..20df41a 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -510,6 +510,8 @@ class CodeGenerator: public AstVisitor { void GenerateIsSmi(ZoneList* args); void GenerateIsNonNegativeSmi(ZoneList* args); void GenerateIsArray(ZoneList* args); + void GenerateIsObject(ZoneList* args); + void GenerateIsFunction(ZoneList* args); // Support for construct call checks. void GenerateIsConstructCall(ZoneList* args); -- 2.7.4