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
This commit is contained in:
parent
3cf9ce4736
commit
d9d18b30f5
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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"},
|
||||
};
|
||||
|
||||
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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');
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user