Commit 20f6f21c authored by Igor Sheludko's avatar Igor Sheludko Committed by Commit Bot

[builtins] Ensure constructor has a prototype slot

Drive-by-cleanup: simplify related helper functions in CSA.

Bug: chromium:1022855
Change-Id: Icb15e6a35275708af313ec5776e92be4b6ce2524
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1910939
Commit-Queue: Igor Sheludko <ishell@chromium.org>
Reviewed-by: 's avatarToon Verwaest <verwaest@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64961}
parent 4550cdf5
...@@ -180,15 +180,14 @@ TNode<JSObject> ConstructorBuiltinsAssembler::EmitFastNewObject( ...@@ -180,15 +180,14 @@ TNode<JSObject> ConstructorBuiltinsAssembler::EmitFastNewObject(
SloppyTNode<Context> context, SloppyTNode<JSFunction> target, SloppyTNode<Context> context, SloppyTNode<JSFunction> target,
SloppyTNode<JSReceiver> new_target, Label* call_runtime) { SloppyTNode<JSReceiver> new_target, Label* call_runtime) {
// Verify that the new target is a JSFunction. // Verify that the new target is a JSFunction.
Label fast(this), end(this); Label end(this);
GotoIf(HasInstanceType(new_target, JS_FUNCTION_TYPE), &fast); TNode<JSFunction> new_target_func =
Goto(call_runtime); HeapObjectToJSFunctionWithPrototypeSlot(new_target, call_runtime);
// Fast path.
BIND(&fast);
// Load the initial map and verify that it's in fact a map. // Load the initial map and verify that it's in fact a map.
TNode<Object> initial_map_or_proto = TNode<Object> initial_map_or_proto =
LoadObjectField(new_target, JSFunction::kPrototypeOrInitialMapOffset); LoadJSFunctionPrototypeOrInitialMap(new_target_func);
GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime); GotoIf(TaggedIsSmi(initial_map_or_proto), call_runtime);
GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE), GotoIf(DoesntHaveInstanceType(CAST(initial_map_or_proto), MAP_TYPE),
call_runtime); call_runtime);
......
...@@ -69,6 +69,9 @@ extern macro HeapObjectToString(HeapObject): String ...@@ -69,6 +69,9 @@ extern macro HeapObjectToString(HeapObject): String
labels CastError; labels CastError;
extern macro HeapObjectToConstructor(HeapObject): Constructor extern macro HeapObjectToConstructor(HeapObject): Constructor
labels CastError; labels CastError;
extern macro HeapObjectToJSFunctionWithPrototypeSlot(HeapObject):
JSFunctionWithPrototypeSlot
labels CastError;
extern macro HeapObjectToHeapNumber(HeapObject): HeapNumber extern macro HeapObjectToHeapNumber(HeapObject): HeapNumber
labels CastError; labels CastError;
extern macro HeapObjectToSloppyArgumentsElements(HeapObject): extern macro HeapObjectToSloppyArgumentsElements(HeapObject):
...@@ -420,6 +423,11 @@ Cast<Constructor>(o: HeapObject): Constructor ...@@ -420,6 +423,11 @@ Cast<Constructor>(o: HeapObject): Constructor
return HeapObjectToConstructor(o) otherwise CastError; return HeapObjectToConstructor(o) otherwise CastError;
} }
Cast<JSFunctionWithPrototypeSlot>(o: HeapObject): JSFunctionWithPrototypeSlot
labels CastError {
return HeapObjectToJSFunctionWithPrototypeSlot(o) otherwise CastError;
}
Cast<HeapNumber>(o: HeapObject): HeapNumber Cast<HeapNumber>(o: HeapObject): HeapNumber
labels CastError { labels CastError {
if (IsHeapNumber(o)) return %RawDownCast<HeapNumber>(o); if (IsHeapNumber(o)) return %RawDownCast<HeapNumber>(o);
......
...@@ -2741,42 +2741,38 @@ TNode<BoolT> CodeStubAssembler::IsGeneratorFunction( ...@@ -2741,42 +2741,38 @@ TNode<BoolT> CodeStubAssembler::IsGeneratorFunction(
shared_function_info, SharedFunctionInfo::kFlagsOffset, shared_function_info, SharedFunctionInfo::kFlagsOffset,
MachineType::Uint32())); MachineType::Uint32()));
return TNode<BoolT>::UncheckedCast(Word32Or( // See IsGeneratorFunction(FunctionKind kind).
Word32Or( return IsInRange(function_kind, FunctionKind::kAsyncConciseGeneratorMethod,
Word32Or( FunctionKind::kConciseGeneratorMethod);
Word32Equal(function_kind, }
Int32Constant(FunctionKind::kAsyncGeneratorFunction)),
Word32Equal( TNode<BoolT> CodeStubAssembler::IsJSFunctionWithPrototypeSlot(
function_kind, TNode<HeapObject> object) {
Int32Constant(FunctionKind::kAsyncConciseGeneratorMethod))), // Only JSFunction maps may have HasPrototypeSlotBit set.
Word32Equal(function_kind, return TNode<BoolT>::UncheckedCast(
Int32Constant(FunctionKind::kGeneratorFunction))), IsSetWord32<Map::HasPrototypeSlotBit>(LoadMapBitField(LoadMap(object))));
Word32Equal(function_kind, }
Int32Constant(FunctionKind::kConciseGeneratorMethod))));
} void CodeStubAssembler::BranchIfHasPrototypeProperty(
TNode<JSFunction> function, TNode<Int32T> function_map_bit_field,
TNode<BoolT> CodeStubAssembler::HasPrototypeSlot(TNode<JSFunction> function) { Label* if_true, Label* if_false) {
return TNode<BoolT>::UncheckedCast(IsSetWord32<Map::HasPrototypeSlotBit>(
LoadMapBitField(LoadMap(function))));
}
TNode<BoolT> CodeStubAssembler::HasPrototypeProperty(TNode<JSFunction> function,
TNode<Map> map) {
// (has_prototype_slot() && IsConstructor()) || // (has_prototype_slot() && IsConstructor()) ||
// IsGeneratorFunction(shared()->kind()) // IsGeneratorFunction(shared()->kind())
uint32_t mask = uint32_t mask =
Map::HasPrototypeSlotBit::kMask | Map::IsConstructorBit::kMask; Map::HasPrototypeSlotBit::kMask | Map::IsConstructorBit::kMask;
return TNode<BoolT>::UncheckedCast(
Word32Or(IsAllSetWord32(LoadMapBitField(map), mask), GotoIf(IsAllSetWord32(function_map_bit_field, mask), if_true);
IsGeneratorFunction(function))); Branch(IsGeneratorFunction(function), if_true, if_false);
} }
void CodeStubAssembler::GotoIfPrototypeRequiresRuntimeLookup( void CodeStubAssembler::GotoIfPrototypeRequiresRuntimeLookup(
TNode<JSFunction> function, TNode<Map> map, Label* runtime) { TNode<JSFunction> function, TNode<Map> map, Label* runtime) {
// !has_prototype_property() || has_non_instance_prototype() // !has_prototype_property() || has_non_instance_prototype()
GotoIfNot(HasPrototypeProperty(function, map), runtime); TNode<Int32T> map_bit_field = LoadMapBitField(map);
GotoIf(IsSetWord32<Map::HasNonInstancePrototypeBit>(LoadMapBitField(map)), Label next_check(this);
runtime); BranchIfHasPrototypeProperty(function, map_bit_field, &next_check, runtime);
BIND(&next_check);
GotoIf(IsSetWord32<Map::HasNonInstancePrototypeBit>(map_bit_field), runtime);
} }
TNode<HeapObject> CodeStubAssembler::LoadJSFunctionPrototype( TNode<HeapObject> CodeStubAssembler::LoadJSFunctionPrototype(
...@@ -12955,14 +12951,6 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindLessThanOrEqual( ...@@ -12955,14 +12951,6 @@ TNode<BoolT> CodeStubAssembler::IsElementsKindLessThanOrEqual(
return Int32LessThanOrEqual(target_kind, Int32Constant(reference_kind)); return Int32LessThanOrEqual(target_kind, Int32Constant(reference_kind));
} }
TNode<BoolT> CodeStubAssembler::IsElementsKindInRange(
TNode<Int32T> target_kind, ElementsKind lower_reference_kind,
ElementsKind higher_reference_kind) {
return Uint32LessThanOrEqual(
Int32Sub(target_kind, Int32Constant(lower_reference_kind)),
Int32Constant(higher_reference_kind - lower_reference_kind));
}
TNode<BoolT> CodeStubAssembler::IsDebugActive() { TNode<BoolT> CodeStubAssembler::IsDebugActive() {
TNode<Uint8T> is_debug_active = Load<Uint8T>( TNode<Uint8T> is_debug_active = Load<Uint8T>(
ExternalConstant(ExternalReference::debug_is_active_address(isolate()))); ExternalConstant(ExternalReference::debug_is_active_address(isolate())));
......
...@@ -457,6 +457,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -457,6 +457,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return CAST(heap_object); return CAST(heap_object);
} }
TNode<JSFunction> HeapObjectToJSFunctionWithPrototypeSlot(
TNode<HeapObject> heap_object, Label* fail) {
GotoIfNot(IsJSFunctionWithPrototypeSlot(heap_object), fail);
return CAST(heap_object);
}
Node* MatchesParameterMode(Node* value, ParameterMode mode); Node* MatchesParameterMode(Node* value, ParameterMode mode);
#define PARAMETER_BINOP(OpName, IntPtrOpName, SmiOpName) \ #define PARAMETER_BINOP(OpName, IntPtrOpName, SmiOpName) \
...@@ -932,6 +938,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -932,6 +938,15 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<BoolT> WordIsAligned(SloppyTNode<WordT> word, size_t alignment); TNode<BoolT> WordIsAligned(SloppyTNode<WordT> word, size_t alignment);
TNode<BoolT> WordIsPowerOfTwo(SloppyTNode<IntPtrT> value); TNode<BoolT> WordIsPowerOfTwo(SloppyTNode<IntPtrT> value);
// Check if lower_limit <= value <= higher_limit.
template <typename U>
TNode<BoolT> IsInRange(TNode<Word32T> value, U lower_limit, U higher_limit) {
DCHECK_LE(lower_limit, higher_limit);
STATIC_ASSERT(sizeof(U) <= kInt32Size);
return Uint32LessThanOrEqual(Int32Sub(value, Int32Constant(lower_limit)),
Int32Constant(higher_limit - lower_limit));
}
#if DEBUG #if DEBUG
void Bind(Label* label, AssemblerDebugInfo debug_info); void Bind(Label* label, AssemblerDebugInfo debug_info);
#endif // DEBUG #endif // DEBUG
...@@ -1479,9 +1494,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -1479,9 +1494,11 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind, TNode<Map> LoadJSArrayElementsMap(SloppyTNode<Int32T> kind,
SloppyTNode<NativeContext> native_context); SloppyTNode<NativeContext> native_context);
TNode<BoolT> HasPrototypeSlot(TNode<JSFunction> function); TNode<BoolT> IsJSFunctionWithPrototypeSlot(TNode<HeapObject> object);
TNode<BoolT> IsGeneratorFunction(TNode<JSFunction> function); TNode<BoolT> IsGeneratorFunction(TNode<JSFunction> function);
TNode<BoolT> HasPrototypeProperty(TNode<JSFunction> function, TNode<Map> map); void BranchIfHasPrototypeProperty(TNode<JSFunction> function,
TNode<Int32T> function_map_bit_field,
Label* if_true, Label* if_false);
void GotoIfPrototypeRequiresRuntimeLookup(TNode<JSFunction> function, void GotoIfPrototypeRequiresRuntimeLookup(TNode<JSFunction> function,
TNode<Map> map, Label* runtime); TNode<Map> map, Label* runtime);
// Load the "prototype" property of a JSFunction. // Load the "prototype" property of a JSFunction.
...@@ -2599,10 +2616,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -2599,10 +2616,12 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
ElementsKind reference_kind); ElementsKind reference_kind);
TNode<BoolT> IsElementsKindLessThanOrEqual(TNode<Int32T> target_kind, TNode<BoolT> IsElementsKindLessThanOrEqual(TNode<Int32T> target_kind,
ElementsKind reference_kind); ElementsKind reference_kind);
// Check if reference_kind_a <= target_kind <= reference_kind_b // Check if lower_reference_kind <= target_kind <= higher_reference_kind.
TNode<BoolT> IsElementsKindInRange(TNode<Int32T> target_kind, TNode<BoolT> IsElementsKindInRange(TNode<Int32T> target_kind,
ElementsKind lower_reference_kind, ElementsKind lower_reference_kind,
ElementsKind higher_reference_kind); ElementsKind higher_reference_kind) {
return IsInRange(target_kind, lower_reference_kind, higher_reference_kind);
}
// String helpers. // String helpers.
// Load a character from a String (might flatten a ConsString). // Load a character from a String (might flatten a ConsString).
......
...@@ -456,6 +456,9 @@ void Map::MapVerify(Isolate* isolate) { ...@@ -456,6 +456,9 @@ void Map::MapVerify(Isolate* isolate) {
.IsConsistentWithBackPointers()); .IsConsistentWithBackPointers());
SLOW_DCHECK(!FLAG_unbox_double_fields || SLOW_DCHECK(!FLAG_unbox_double_fields ||
layout_descriptor().IsConsistentWithMap(*this)); layout_descriptor().IsConsistentWithMap(*this));
// Only JSFunction maps have has_prototype_slot() bit set and constructible
// JSFunction objects must have prototype slot.
CHECK_IMPLIES(has_prototype_slot(), instance_type() == JS_FUNCTION_TYPE);
if (!may_have_interesting_symbols()) { if (!may_have_interesting_symbols()) {
CHECK(!has_named_interceptor()); CHECK(!has_named_interceptor());
CHECK(!is_dictionary_map()); CHECK(!is_dictionary_map());
......
...@@ -70,15 +70,13 @@ extern class JSFunction extends JSFunctionOrBoundFunction { ...@@ -70,15 +70,13 @@ extern class JSFunction extends JSFunctionOrBoundFunction {
@noVerifier weak prototype_or_initial_map: JSReceiver|Map; @noVerifier weak prototype_or_initial_map: JSReceiver|Map;
} }
extern macro HasPrototypeSlot(JSFunction): bool; type JSFunctionWithPrototypeSlot extends JSFunction;
macro GetDerivedMap(implicit context: Context)( macro GetDerivedMap(implicit context: Context)(
target: JSFunction, newTarget: JSReceiver): Map { target: JSFunction, newTarget: JSReceiver): Map {
try { try {
const constructor = Cast<JSFunction>(newTarget) otherwise SlowPath; const constructor =
if (!HasPrototypeSlot(constructor)) { Cast<JSFunctionWithPrototypeSlot>(newTarget) otherwise SlowPath;
goto SlowPath;
}
assert(IsConstructor(constructor)); assert(IsConstructor(constructor));
const map = const map =
Cast<Map>(constructor.prototype_or_initial_map) otherwise SlowPath; Cast<Map>(constructor.prototype_or_initial_map) otherwise SlowPath;
......
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