Commit 3e407093 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Properly implement %_ClassOf intrinsic.

The %_ClassOf intrinsic roughly corresponds to the deprecated ES5
[[Class]] internal property, and should not be used anymore ideally.
However since we still have quite a couple of uses of this intrinsic
in the self hosted JavaScript builtins, we would tank some builtins
like Map, Set, WeakMap, WeakSet, etc. quite significantly unless we
also support this intrinsic until the builtins are all migrated to
C++/CSA builtins.

R=yangguo@chromium.org
BUG=v8:5267

Review-Url: https://codereview.chromium.org/2647833004
Cr-Commit-Position: refs/heads/master@{#42530}
parent 4a88dfba
......@@ -474,6 +474,17 @@ void Builtins::Generate_ToObject(compiler::CodeAssemblerState* state) {
assembler.Return(object);
}
// Deprecated ES5 [[Class]] internal property (used to implement %_ClassOf).
void Builtins::Generate_ClassOf(compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
typedef TypeofDescriptor Descriptor;
CodeStubAssembler assembler(state);
Node* object = assembler.Parameter(Descriptor::kObject);
assembler.Return(assembler.ClassOf(object));
}
// ES6 section 12.5.5 typeof operator
void Builtins::Generate_Typeof(compiler::CodeAssemblerState* state) {
typedef compiler::Node Node;
......
......@@ -208,6 +208,7 @@ namespace internal {
TFS(ToString, BUILTIN, kNoExtraICState, TypeConversion) \
TFS(ToInteger, BUILTIN, kNoExtraICState, TypeConversion) \
TFS(ToLength, BUILTIN, kNoExtraICState, TypeConversion) \
TFS(ClassOf, BUILTIN, kNoExtraICState, Typeof) \
TFS(Typeof, BUILTIN, kNoExtraICState, Typeof) \
TFS(GetSuperConstructor, BUILTIN, kNoExtraICState, TypeConversion) \
\
......
......@@ -235,6 +235,7 @@ TFS_BUILTIN(ToLength)
TFS_BUILTIN(ToName)
TFS_BUILTIN(ToNumber)
TFS_BUILTIN(ToObject)
TFS_BUILTIN(ClassOf)
TFS_BUILTIN(Typeof)
TFS_BUILTIN(InstanceOf)
TFS_BUILTIN(OrdinaryHasInstance)
......
......@@ -128,6 +128,7 @@ class V8_EXPORT_PRIVATE CodeFactory final {
static Callable StringGreaterThanOrEqual(Isolate* isolate);
static Callable SubString(Isolate* isolate);
static Callable ClassOf(Isolate* isolate);
static Callable Typeof(Isolate* isolate);
static Callable GetSuperConstructor(Isolate* isolate);
......
......@@ -7764,6 +7764,57 @@ Node* CodeStubAssembler::HasProperty(
return result.value();
}
Node* CodeStubAssembler::ClassOf(Node* value) {
Variable var_result(this, MachineRepresentation::kTaggedPointer);
Label if_function(this, Label::kDeferred), if_object(this, Label::kDeferred),
if_primitive(this, Label::kDeferred), return_result(this);
// Check if {value} is a Smi.
GotoIf(TaggedIsSmi(value), &if_primitive);
Node* value_map = LoadMap(value);
Node* value_instance_type = LoadMapInstanceType(value_map);
// Check if {value} is a JSFunction or JSBoundFunction.
STATIC_ASSERT(LAST_TYPE == LAST_FUNCTION_TYPE);
GotoIf(Uint32LessThanOrEqual(Int32Constant(FIRST_FUNCTION_TYPE),
value_instance_type),
&if_function);
// Check if {value} is a primitive HeapObject.
STATIC_ASSERT(LAST_TYPE == LAST_JS_RECEIVER_TYPE);
GotoIf(Uint32LessThan(value_instance_type,
Int32Constant(FIRST_JS_RECEIVER_TYPE)),
&if_primitive);
// Load the {value}s constructor, and check that it's a JSFunction.
Node* constructor = LoadMapConstructor(value_map);
GotoUnless(IsJSFunction(constructor), &if_object);
// Return the instance class name for the {constructor}.
Node* shared_info =
LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset);
Node* instance_class_name = LoadObjectField(
shared_info, SharedFunctionInfo::kInstanceClassNameOffset);
var_result.Bind(instance_class_name);
Goto(&return_result);
Bind(&if_function);
var_result.Bind(LoadRoot(Heap::kFunction_stringRootIndex));
Goto(&return_result);
Bind(&if_object);
var_result.Bind(LoadRoot(Heap::kObject_stringRootIndex));
Goto(&return_result);
Bind(&if_primitive);
var_result.Bind(NullConstant());
Goto(&return_result);
Bind(&return_result);
return var_result.value();
}
Node* CodeStubAssembler::Typeof(Node* value, Node* context) {
Variable result_var(this, MachineRepresentation::kTagged);
......
......@@ -1139,6 +1139,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler : public compiler::CodeAssembler {
Runtime::FunctionId fallback_runtime_function_id = Runtime::kHasProperty);
Node* ForInFilter(Node* key, Node* object, Node* context);
Node* ClassOf(Node* object);
Node* Typeof(Node* value, Node* context);
Node* GetSuperConstructor(Node* value, Node* context);
......
......@@ -143,6 +143,15 @@ void JSGenericLowering::LowerJSToBoolean(Node* node) {
Operator::kEliminatable);
}
void JSGenericLowering::LowerJSClassOf(Node* node) {
// The %_ClassOf intrinsic doesn't need the current context.
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
Callable callable = CodeFactory::ClassOf(isolate());
node->AppendInput(zone(), graph()->start());
ReplaceWithStubCall(node, callable, CallDescriptor::kNoAllocate,
Operator::kEliminatable);
}
void JSGenericLowering::LowerJSTypeOf(Node* node) {
// The typeof operator doesn't need the current context.
NodeProperties::ReplaceContextInput(node, jsgraph()->NoContextConstant());
......
......@@ -82,6 +82,8 @@ Reduction JSIntrinsicLowering::Reduce(Node* node) {
return ReduceStringGetRawHashField(node);
case Runtime::kInlineTheHole:
return ReduceTheHole(node);
case Runtime::kInlineClassOf:
return ReduceClassOf(node);
default:
break;
}
......@@ -339,6 +341,13 @@ Reduction JSIntrinsicLowering::ReduceTheHole(Node* node) {
return Replace(value);
}
Reduction JSIntrinsicLowering::ReduceClassOf(Node* node) {
RelaxEffectsAndControls(node);
node->TrimInputCount(2);
NodeProperties::ChangeOp(node, javascript()->ClassOf());
return Changed(node);
}
Reduction JSIntrinsicLowering::Change(Node* node, const Operator* op, Node* a,
Node* b) {
RelaxControls(node);
......
......@@ -67,6 +67,10 @@ class V8_EXPORT_PRIVATE JSIntrinsicLowering final
Reduction ReduceStringGetRawHashField(Node* node);
Reduction ReduceTheHole(Node* node);
// TODO(turbofan): JavaScript builtins support; drop once all uses of
// %_ClassOf in JavaScript builtins are eliminated.
Reduction ReduceClassOf(Node* node);
Reduction Change(Node* node, const Operator* op);
Reduction Change(Node* node, const Operator* op, Node* a, Node* b);
Reduction Change(Node* node, const Operator* op, Node* a, Node* b, Node* c);
......
......@@ -528,6 +528,7 @@ CompareOperationHint CompareOperationHintOf(const Operator* op) {
V(CreateIterResultObject, Operator::kEliminatable, 2, 1) \
V(CreateKeyValueArray, Operator::kEliminatable, 2, 1) \
V(HasProperty, Operator::kNoProperties, 2, 1) \
V(ClassOf, Operator::kPure, 1, 1) \
V(TypeOf, Operator::kPure, 1, 1) \
V(InstanceOf, Operator::kNoProperties, 2, 1) \
V(OrdinaryHasInstance, Operator::kNoProperties, 2, 1) \
......
......@@ -590,6 +590,7 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
const Operator* LoadModule(int32_t cell_index);
const Operator* StoreModule(int32_t cell_index);
const Operator* ClassOf();
const Operator* TypeOf();
const Operator* InstanceOf();
const Operator* OrdinaryHasInstance();
......
......@@ -171,6 +171,7 @@ bool Linkage::NeedsFrameStateInput(Runtime::FunctionId function) {
return false;
// Some inline intrinsics are also safe to call without a FrameState.
case Runtime::kInlineClassOf:
case Runtime::kInlineCreateIterResultObject:
case Runtime::kInlineFixedArrayGet:
case Runtime::kInlineFixedArraySet:
......
......@@ -120,6 +120,7 @@
V(JSToString)
#define JS_OTHER_UNOP_LIST(V) \
V(JSClassOf) \
V(JSTypeOf)
#define JS_SIMPLE_UNOP_LIST(V) \
......
......@@ -1052,6 +1052,9 @@ Type* Typer::Visitor::JSModulusTyper(Type* lhs, Type* rhs, Typer* t) {
// JS unary operators.
Type* Typer::Visitor::TypeJSClassOf(Node* node) {
return Type::InternalizedStringOrNull();
}
Type* Typer::Visitor::TypeJSTypeOf(Node* node) {
return Type::InternalizedString();
......@@ -1561,9 +1564,8 @@ Type* Typer::Visitor::TypeJSCallRuntime(Node* node) {
return TypeUnaryOp(node, ToObject);
case Runtime::kInlineToString:
return TypeUnaryOp(node, ToString);
case Runtime::kClassOf:
case Runtime::kInlineClassOf:
return Type::Union(Type::InternalizedString(), Type::Null(), zone());
return Type::InternalizedStringOrNull();
case Runtime::kHasInPrototypeChain:
return Type::Boolean();
default:
......
......@@ -148,6 +148,7 @@ namespace compiler {
V(String, kInternalizedString | kOtherString) \
V(UniqueName, kSymbol | kInternalizedString) \
V(Name, kSymbol | kString) \
V(InternalizedStringOrNull, kInternalizedString | kNull) \
V(BooleanOrNumber, kBoolean | kNumber) \
V(BooleanOrNullOrNumber, kBooleanOrNumber | kNull) \
V(BooleanOrNullOrUndefined, kBoolean | kNull | kUndefined) \
......
......@@ -636,9 +636,13 @@ void Verifier::Visitor::Check(Node* node) {
// Type is Boolean.
CheckTypeIs(node, Type::Boolean());
break;
case IrOpcode::kJSClassOf:
// Type is InternaliedString \/ Null.
CheckTypeIs(node, Type::InternalizedStringOrNull());
break;
case IrOpcode::kJSTypeOf:
// Type is String.
CheckTypeIs(node, Type::String());
// Type is InternalizedString.
CheckTypeIs(node, Type::InternalizedString());
break;
case IrOpcode::kJSGetSuperConstructor:
// We don't check the input for Type::Function because
......
......@@ -331,62 +331,8 @@ Node* IntrinsicsHelper::ValueOf(Node* args_reg, Node* arg_count,
Node* IntrinsicsHelper::ClassOf(Node* args_reg, Node* arg_count,
Node* context) {
InterpreterAssembler::Variable return_value(assembler_,
MachineRepresentation::kTagged);
InterpreterAssembler::Label done(assembler_), null(assembler_),
function(assembler_), non_function_constructor(assembler_);
Node* object = __ LoadRegister(args_reg);
// If the object is not a JSReceiver, we return null.
__ GotoIf(__ TaggedIsSmi(object), &null);
STATIC_ASSERT(LAST_JS_RECEIVER_TYPE == LAST_TYPE);
Node* is_js_receiver = CompareInstanceType(object, FIRST_JS_RECEIVER_TYPE,
kInstanceTypeGreaterThanOrEqual);
__ GotoUnless(is_js_receiver, &null);
// Return 'Function' for JSFunction and JSBoundFunction objects.
Node* is_function = CompareInstanceType(object, FIRST_FUNCTION_TYPE,
kInstanceTypeGreaterThanOrEqual);
STATIC_ASSERT(LAST_FUNCTION_TYPE == LAST_TYPE);
__ GotoIf(is_function, &function);
// Check if the constructor in the map is a JS function.
Node* constructor = __ LoadMapConstructor(__ LoadMap(object));
Node* constructor_is_js_function =
CompareInstanceType(constructor, JS_FUNCTION_TYPE, kInstanceTypeEqual);
__ GotoUnless(constructor_is_js_function, &non_function_constructor);
// Grab the instance class name from the constructor function.
Node* shared =
__ LoadObjectField(constructor, JSFunction::kSharedFunctionInfoOffset);
return_value.Bind(
__ LoadObjectField(shared, SharedFunctionInfo::kInstanceClassNameOffset));
__ Goto(&done);
// Non-JS objects have class null.
__ Bind(&null);
{
return_value.Bind(__ LoadRoot(Heap::kNullValueRootIndex));
__ Goto(&done);
}
// Functions have class 'Function'.
__ Bind(&function);
{
return_value.Bind(__ LoadRoot(Heap::kFunction_stringRootIndex));
__ Goto(&done);
}
// Objects with a non-function constructor have class 'Object'.
__ Bind(&non_function_constructor);
{
return_value.Bind(__ LoadRoot(Heap::kObject_stringRootIndex));
__ Goto(&done);
}
__ Bind(&done);
return return_value.value();
Node* value = __ LoadRegister(args_reg);
return __ ClassOf(value);
}
void IntrinsicsHelper::AbortIfArgCountMismatch(int expected, Node* actual) {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment