From f1ffd5025741ced81095f7ffb64c4ffeb2d13432 Mon Sep 17 00:00:00 2001 From: "kasperl@chromium.org" Date: Mon, 29 Jun 2009 10:45:16 +0000 Subject: [PATCH] Optimize %ClassOf() on IA-32 and use it instead of the custom %HasXXXClass() calls. Review URL: http://codereview.chromium.org/151018 git-svn-id: http://v8.googlecode.com/svn/branches/bleeding_edge@2293 ce2b1a6d-e550-0410-aec6-3dcde31c8c00 --- src/arm/codegen-arm.cc | 9 +++++++ src/arm/codegen-arm.h | 3 ++- src/codegen.cc | 1 + src/ia32/codegen-ia32.cc | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ src/ia32/codegen-ia32.h | 3 ++- src/macros.py | 16 ++++++------ src/mirror-delay.js | 2 +- src/runtime.cc | 42 ------------------------------- src/runtime.h | 8 ------ src/runtime.js | 6 ++--- src/string.js | 4 +-- src/v8natives.js | 15 ++++++------ src/x64/codegen-x64.cc | 9 +++++++ src/x64/codegen-x64.h | 3 ++- 14 files changed, 110 insertions(+), 75 deletions(-) diff --git a/src/arm/codegen-arm.cc b/src/arm/codegen-arm.cc index eeea560..10c5d82 100644 --- a/src/arm/codegen-arm.cc +++ b/src/arm/codegen-arm.cc @@ -3180,6 +3180,15 @@ void CodeGenerator::VisitCallNew(CallNew* node) { } +void CodeGenerator::GenerateClassOf(ZoneList* args) { + VirtualFrame::SpilledScope spilled_scope; + ASSERT(args->length() == 1); + LoadAndSpill(args->at(0)); // Load the object. + frame_->CallRuntime(Runtime::kClassOf, 1); + frame_->EmitPush(r0); +} + + void CodeGenerator::GenerateValueOf(ZoneList* args) { VirtualFrame::SpilledScope spilled_scope; ASSERT(args->length() == 1); diff --git a/src/arm/codegen-arm.h b/src/arm/codegen-arm.h index 2048a17..5ddecd3 100644 --- a/src/arm/codegen-arm.h +++ b/src/arm/codegen-arm.h @@ -340,7 +340,8 @@ class CodeGenerator: public AstVisitor { void GenerateArgumentsLength(ZoneList* args); void GenerateArgumentsAccess(ZoneList* args); - // Support for accessing the value field of an object (used by Date). + // Support for accessing the class and value fields of an object. + void GenerateClassOf(ZoneList* args); void GenerateValueOf(ZoneList* args); void GenerateSetValueOf(ZoneList* args); diff --git a/src/codegen.cc b/src/codegen.cc index 6a41b9d..b7297d7 100644 --- a/src/codegen.cc +++ b/src/codegen.cc @@ -419,6 +419,7 @@ CodeGenerator::InlineRuntimeLUT CodeGenerator::kInlineRuntimeLUT[] = { {&CodeGenerator::GenerateIsConstructCall, "_IsConstructCall"}, {&CodeGenerator::GenerateArgumentsLength, "_ArgumentsLength"}, {&CodeGenerator::GenerateArgumentsAccess, "_Arguments"}, + {&CodeGenerator::GenerateClassOf, "_ClassOf"}, {&CodeGenerator::GenerateValueOf, "_ValueOf"}, {&CodeGenerator::GenerateSetValueOf, "_SetValueOf"}, {&CodeGenerator::GenerateFastCharCodeAt, "_FastCharCodeAt"}, diff --git a/src/ia32/codegen-ia32.cc b/src/ia32/codegen-ia32.cc index fa407c2..948c123 100644 --- a/src/ia32/codegen-ia32.cc +++ b/src/ia32/codegen-ia32.cc @@ -5022,6 +5022,70 @@ void CodeGenerator::GenerateArgumentsLength(ZoneList* args) { } +void CodeGenerator::GenerateClassOf(ZoneList* args) { + ASSERT(args->length() == 1); + JumpTarget leave, null, function, non_function_constructor; + Load(args->at(0)); // Load the object. + Result obj = frame_->Pop(); + obj.ToRegister(); + frame_->Spill(obj.reg()); + + // If the object is a smi, we return null. + __ test(obj.reg(), Immediate(kSmiTagMask)); + null.Branch(zero); + + // Check that the object is a JS object but take special care of JS + // functions to make sure they have 'Function' as their class. + { Result tmp = allocator()->Allocate(); + __ mov(obj.reg(), FieldOperand(obj.reg(), HeapObject::kMapOffset)); + __ movzx_b(tmp.reg(), FieldOperand(obj.reg(), Map::kInstanceTypeOffset)); + __ cmp(tmp.reg(), FIRST_JS_OBJECT_TYPE); + null.Branch(less); + + // As long as JS_FUNCTION_TYPE is the last instance type and it is + // right after LAST_JS_OBJECT_TYPE, we can avoid checking for + // LAST_JS_OBJECT_TYPE. + ASSERT(LAST_TYPE == JS_FUNCTION_TYPE); + ASSERT(JS_FUNCTION_TYPE == LAST_JS_OBJECT_TYPE + 1); + __ cmp(tmp.reg(), JS_FUNCTION_TYPE); + function.Branch(equal); + } + + // Check if the constructor in the map is a function. + { Result tmp = allocator()->Allocate(); + __ mov(obj.reg(), FieldOperand(obj.reg(), Map::kConstructorOffset)); + __ CmpObjectType(obj.reg(), JS_FUNCTION_TYPE, tmp.reg()); + non_function_constructor.Branch(not_equal); + } + + // The map register now contains the constructor function. Grab the + // instance class name from there. + __ mov(obj.reg(), + FieldOperand(obj.reg(), JSFunction::kSharedFunctionInfoOffset)); + __ mov(obj.reg(), + FieldOperand(obj.reg(), SharedFunctionInfo::kInstanceClassNameOffset)); + frame_->Push(&obj); + leave.Jump(); + + // Functions have class 'Function'. + function.Bind(); + frame_->Push(Factory::function_class_symbol()); + leave.Jump(); + + // Objects with a non-function constructor have class 'Object'. + non_function_constructor.Bind(); + frame_->Push(Factory::Object_symbol()); + leave.Jump(); + + // Non-JS objects have class null. + null.Bind(); + frame_->Push(Factory::null_value()); + + // All done. + leave.Bind(); +} + + void CodeGenerator::GenerateValueOf(ZoneList* args) { ASSERT(args->length() == 1); JumpTarget leave; diff --git a/src/ia32/codegen-ia32.h b/src/ia32/codegen-ia32.h index a7e0b06..5cd50b8 100644 --- a/src/ia32/codegen-ia32.h +++ b/src/ia32/codegen-ia32.h @@ -529,7 +529,8 @@ class CodeGenerator: public AstVisitor { void GenerateArgumentsLength(ZoneList* args); void GenerateArgumentsAccess(ZoneList* args); - // Support for accessing the value field of an object (used by Date). + // Support for accessing the class and value fields of an object. + void GenerateClassOf(ZoneList* args); void GenerateValueOf(ZoneList* args); void GenerateSetValueOf(ZoneList* args); diff --git a/src/macros.py b/src/macros.py index fdbdb58..a3f896d 100644 --- a/src/macros.py +++ b/src/macros.py @@ -82,13 +82,15 @@ macro IS_NUMBER(arg) = (typeof(arg) === 'number'); macro IS_STRING(arg) = (typeof(arg) === 'string'); macro IS_OBJECT(arg) = (typeof(arg) === 'object'); macro IS_BOOLEAN(arg) = (typeof(arg) === 'boolean'); -macro IS_REGEXP(arg) = %HasRegExpClass(arg); -macro IS_ARRAY(arg) = %HasArrayClass(arg); -macro IS_DATE(arg) = %HasDateClass(arg); -macro IS_NUMBER_WRAPPER(arg) = %HasNumberClass(arg); -macro IS_STRING_WRAPPER(arg) = %HasStringClass(arg); -macro IS_ERROR(arg) = (%ClassOf(arg) === 'Error'); -macro IS_SCRIPT(arg) = (%ClassOf(arg) === 'Script'); +macro IS_ARRAY(arg) = (%_IsArray(arg)); +macro IS_REGEXP(arg) = (%_ClassOf(arg) === 'RegExp'); +macro IS_DATE(arg) = (%_ClassOf(arg) === 'Date'); +macro IS_NUMBER_WRAPPER(arg) = (%_ClassOf(arg) === 'Number'); +macro IS_STRING_WRAPPER(arg) = (%_ClassOf(arg) === 'String'); +macro IS_BOOLEAN_WRAPPER(arg) = (%_ClassOf(arg) === 'Boolean'); +macro IS_ERROR(arg) = (%_ClassOf(arg) === 'Error'); +macro IS_SCRIPT(arg) = (%_ClassOf(arg) === 'Script'); +macro IS_ARGUMENTS(arg) = (%_ClassOf(arg) === 'Arguments'); macro FLOOR(arg) = %Math_floor(arg); # Inline macros. Use %IS_VAR to make sure arg is evaluated only once. diff --git a/src/mirror-delay.js b/src/mirror-delay.js index d0e8aa4..76ae75b 100644 --- a/src/mirror-delay.js +++ b/src/mirror-delay.js @@ -580,7 +580,7 @@ inherits(ObjectMirror, ValueMirror); ObjectMirror.prototype.className = function() { - return %ClassOf(this.value_); + return %_ClassOf(this.value_); }; diff --git a/src/runtime.cc b/src/runtime.cc index dcff28b..2555ba4 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -413,48 +413,6 @@ static Object* Runtime_ClassOf(Arguments args) { } -static Object* Runtime_HasStringClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::String_symbol())); -} - - -static Object* Runtime_HasDateClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Date_symbol())); -} - - -static Object* Runtime_HasArrayClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Array_symbol())); -} - - -static Object* Runtime_HasFunctionClass(Arguments args) { - return Heap::ToBoolean( - args[0]->HasSpecificClassOf(Heap::function_class_symbol())); -} - - -static Object* Runtime_HasNumberClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Number_symbol())); -} - - -static Object* Runtime_HasBooleanClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::Boolean_symbol())); -} - - -static Object* Runtime_HasArgumentsClass(Arguments args) { - return Heap::ToBoolean( - args[0]->HasSpecificClassOf(Heap::Arguments_symbol())); -} - - -static Object* Runtime_HasRegExpClass(Arguments args) { - return Heap::ToBoolean(args[0]->HasSpecificClassOf(Heap::RegExp_symbol())); -} - - static Object* Runtime_IsInPrototypeChain(Arguments args) { NoHandleAllocation ha; ASSERT(args.length() == 2); diff --git a/src/runtime.h b/src/runtime.h index 15dd9b4..3c09495 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -173,14 +173,6 @@ namespace internal { F(GetScript, 1) \ \ F(ClassOf, 1) \ - F(HasDateClass, 1) \ - F(HasStringClass, 1) \ - F(HasArrayClass, 1) \ - F(HasFunctionClass, 1) \ - F(HasNumberClass, 1) \ - F(HasBooleanClass, 1) \ - F(HasArgumentsClass, 1) \ - F(HasRegExpClass, 1) \ F(SetCode, 2) \ \ F(CreateApiFunction, 1) \ diff --git a/src/runtime.js b/src/runtime.js index df26b88..25cc5ba 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -394,7 +394,7 @@ function APPLY_PREPARE(args) { // First check whether length is a positive Smi and args is an // array. This is the fast case. If this fails, we do the slow case // that takes care of more eventualities. - if (%_IsArray(args)) { + if (IS_ARRAY(args)) { length = args.length; if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) { return length; @@ -415,9 +415,7 @@ function APPLY_PREPARE(args) { } // Make sure the arguments list has the right type. - if (args != null && - !%HasArrayClass(args) && - !%HasArgumentsClass(args)) { + if (args != null && !IS_ARRAY(args) && !IS_ARGUMENTS(args)) { throw %MakeTypeError('apply_wrong_args', []); } diff --git a/src/string.js b/src/string.js index 604f925..6164eb8 100644 --- a/src/string.js +++ b/src/string.js @@ -46,7 +46,7 @@ // ECMA-262 section 15.5.4.2 function StringToString() { - if (!IS_STRING(this) && !%HasStringClass(this)) + if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) throw new $TypeError('String.prototype.toString is not generic'); return %_ValueOf(this); } @@ -54,7 +54,7 @@ function StringToString() { // ECMA-262 section 15.5.4.3 function StringValueOf() { - if (!IS_STRING(this) && !%HasStringClass(this)) + if (!IS_STRING(this) && !IS_STRING_WRAPPER(this)) throw new $TypeError('String.prototype.valueOf is not generic'); return %_ValueOf(this); } diff --git a/src/v8natives.js b/src/v8natives.js index 97363e9..841c920 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -192,7 +192,7 @@ $Object.prototype.constructor = $Object; // ECMA-262 - 15.2.4.2 function ObjectToString() { - var c = %ClassOf(this); + var c = %_ClassOf(this); // Hide Arguments from the outside. if (c === 'Arguments') c = 'Object'; return "[object " + c + "]"; @@ -311,7 +311,7 @@ SetupObject(); function BooleanToString() { // NOTE: Both Boolean objects and values can enter here as // 'this'. This is not as dictated by ECMA-262. - if (!IS_BOOLEAN(this) && !%HasBooleanClass(this)) + if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) throw new $TypeError('Boolean.prototype.toString is not generic'); return ToString(%_ValueOf(this)); } @@ -320,7 +320,7 @@ function BooleanToString() { function BooleanValueOf() { // NOTE: Both Boolean objects and values can enter here as // 'this'. This is not as dictated by ECMA-262. - if (!IS_BOOLEAN(this) && !%HasBooleanClass(this)) + if (!IS_BOOLEAN(this) && !IS_BOOLEAN_WRAPPER(this)) throw new $TypeError('Boolean.prototype.valueOf is not generic'); return %_ValueOf(this); } @@ -365,7 +365,7 @@ function NumberToString(radix) { // 'this'. This is not as dictated by ECMA-262. var number = this; if (!IS_NUMBER(this)) { - if (!%HasNumberClass(this)) + if (!IS_NUMBER_WRAPPER(this)) throw new $TypeError('Number.prototype.toString is not generic'); // Get the value of this number in case it's an object. number = %_ValueOf(this); @@ -395,7 +395,7 @@ function NumberToLocaleString() { function NumberValueOf() { // NOTE: Both Number objects and values can enter here as // 'this'. This is not as dictated by ECMA-262. - if (!IS_NUMBER(this) && !%HasNumberClass(this)) + if (!IS_NUMBER(this) && !IS_NUMBER_WRAPPER(this)) throw new $TypeError('Number.prototype.valueOf is not generic'); return %_ValueOf(this); } @@ -502,10 +502,9 @@ SetupNumber(); $Function.prototype.constructor = $Function; function FunctionSourceString(func) { - // NOTE: Both Function objects and values can enter here as - // 'func'. This is not as dictated by ECMA-262. - if (!IS_FUNCTION(func) && !%HasFunctionClass(func)) + if (!IS_FUNCTION(func)) { throw new $TypeError('Function.prototype.toString is not generic'); + } var source = %FunctionGetSourceCode(func); if (!IS_STRING(source)) { diff --git a/src/x64/codegen-x64.cc b/src/x64/codegen-x64.cc index 24e3ec3..e2cb95f 100644 --- a/src/x64/codegen-x64.cc +++ b/src/x64/codegen-x64.cc @@ -3430,6 +3430,15 @@ void CodeGenerator::GenerateFastMathOp(MathOp op, ZoneList* args) { } +void CodeGenerator::GenerateClassOf(ZoneList* args) { + // TODO(X64): Optimize this like it's done on IA-32. + ASSERT(args->length() == 1); + Load(args->at(0)); // Load the object. + Result result = frame_->CallRuntime(Runtime::kClassOf, 1); + frame_->Push(&result); +} + + void CodeGenerator::GenerateSetValueOf(ZoneList* args) { ASSERT(args->length() == 2); JumpTarget leave; diff --git a/src/x64/codegen-x64.h b/src/x64/codegen-x64.h index a174c51..4488558 100644 --- a/src/x64/codegen-x64.h +++ b/src/x64/codegen-x64.h @@ -529,7 +529,8 @@ class CodeGenerator: public AstVisitor { void GenerateArgumentsLength(ZoneList* args); void GenerateArgumentsAccess(ZoneList* args); - // Support for accessing the value field of an object (used by Date). + // Support for accessing the class and value fields of an object. + void GenerateClassOf(ZoneList* args); void GenerateValueOf(ZoneList* args); void GenerateSetValueOf(ZoneList* args); -- 2.7.4