Revision: 3335 Author: [email protected] Date: Wed Nov 18 23:41:32 2009 Log: 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 http://code.google.com/p/v8/source/detail?r=3335 Modified: /branches/bleeding_edge/src/arm/codegen-arm.cc /branches/bleeding_edge/src/arm/codegen-arm.h /branches/bleeding_edge/src/codegen.cc /branches/bleeding_edge/src/ia32/codegen-ia32.cc /branches/bleeding_edge/src/ia32/codegen-ia32.h /branches/bleeding_edge/src/macros.py /branches/bleeding_edge/src/x64/codegen-x64.cc /branches/bleeding_edge/src/x64/codegen-x64.h ======================================= --- /branches/bleeding_edge/src/arm/codegen-arm.cc Wed Nov 18 04:14:21 2009 +++ /branches/bleeding_edge/src/arm/codegen-arm.cc Wed Nov 18 23:41:32 2009 @@ -3383,6 +3383,51 @@ answer.Bind(); cc_reg_ = eq; } + + +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) { ======================================= --- /branches/bleeding_edge/src/arm/codegen-arm.h Mon Nov 16 13:59:31 2009 +++ /branches/bleeding_edge/src/arm/codegen-arm.h Wed Nov 18 23:41:32 2009 @@ -334,6 +334,8 @@ 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); ======================================= --- /branches/bleeding_edge/src/codegen.cc Mon Nov 16 13:59:31 2009 +++ /branches/bleeding_edge/src/codegen.cc Wed Nov 18 23:41:32 2009 @@ -343,7 +343,9 @@ {&CodeGenerator::GenerateLog, "_Log"}, {&CodeGenerator::GenerateRandomPositiveSmi, "_RandomPositiveSmi"}, {&CodeGenerator::GenerateMathSin, "_Math_sin"}, - {&CodeGenerator::GenerateMathCos, "_Math_cos"} + {&CodeGenerator::GenerateMathCos, "_Math_cos"}, + {&CodeGenerator::GenerateIsObject, "_IsObject"}, + {&CodeGenerator::GenerateIsFunction, "_IsFunction"}, }; ======================================= --- /branches/bleeding_edge/src/ia32/codegen-ia32.cc Tue Nov 17 02:28:04 2009 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.cc Wed Nov 18 23:41:32 2009 @@ -4868,6 +4868,55 @@ temp.Unuse(); destination()->Split(equal); } + + +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) { ======================================= --- /branches/bleeding_edge/src/ia32/codegen-ia32.h Mon Nov 16 15:11:19 2009 +++ /branches/bleeding_edge/src/ia32/codegen-ia32.h Wed Nov 18 23:41:32 2009 @@ -512,6 +512,8 @@ 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); ======================================= --- /branches/bleeding_edge/src/macros.py Thu Nov 5 08:08:48 2009 +++ /branches/bleeding_edge/src/macros.py Wed Nov 18 23:41:32 2009 @@ -79,11 +79,10 @@ 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'); ======================================= --- /branches/bleeding_edge/src/x64/codegen-x64.cc Wed Nov 18 02:20:24 2009 +++ /branches/bleeding_edge/src/x64/codegen-x64.cc Wed Nov 18 23:41:32 2009 @@ -3614,6 +3614,48 @@ value.Unuse(); destination()->Split(equal); } + + +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) { ======================================= --- /branches/bleeding_edge/src/x64/codegen-x64.h Mon Nov 16 13:59:31 2009 +++ /branches/bleeding_edge/src/x64/codegen-x64.h Wed Nov 18 23:41:32 2009 @@ -510,6 +510,8 @@ 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); --~--~---------~--~----~------------~-------~--~----~ v8-dev mailing list [email protected] http://groups.google.com/group/v8-dev -~----------~----~----~----~------~----~------~--~---
