Commit 1b04772f authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] weak pointer type Weak<T> + port CreateObjectWithoutProperties

Overview:
- Change basic type hierarchy to split Tagged into StrongTagged (= Object) and
  and WeakHeapObject. This enables to emit the right CSA types (Object, MaybeObject).
- The new Weak<T> type encodes a possibly cleared weak bit pattern that
  points to type T if it's not cleared.
- Make TNode<Object> a subtype of TNode<MaybeObject> so that the generated code
  compiles on the C++ side. Drive-by change: simplify a few CSA helpers by using
  MaybeObject as a common supertype of MaybeObject and Object.
- Port CreateObjectWithoutProperties and LoadMapPrototypeInfo.

Bug: v8:7793
Change-Id: I895a6501ce3e287ea8cf4065aaff3a5535245ab4
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1889870Reviewed-by: 's avatarJakob Gruber <jgruber@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Cr-Commit-Position: refs/heads/master@{#64876}
parent 3568e444
This diff is collapsed.
......@@ -703,7 +703,6 @@ namespace internal {
TFJ(ObjectAssign, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
/* ES #sec-object.create */ \
TFJ(ObjectCreate, SharedFunctionInfo::kDontAdaptArgumentsSentinel) \
TFS(CreateObjectWithoutProperties, kPrototypeArg) \
CPP(ObjectDefineGetter) \
CPP(ObjectDefineProperties) \
CPP(ObjectDefineProperty) \
......
......@@ -1060,70 +1060,6 @@ TF_BUILTIN(ObjectPrototypeValueOf, CodeStubAssembler) {
Return(ToObject_Inline(context, receiver));
}
// ES #sec-object.create
TF_BUILTIN(CreateObjectWithoutProperties, ObjectBuiltinsAssembler) {
const TNode<Object> prototype = CAST(Parameter(Descriptor::kPrototypeArg));
const TNode<Context> context = CAST(Parameter(Descriptor::kContext));
const TNode<NativeContext> native_context = LoadNativeContext(context);
Label call_runtime(this, Label::kDeferred), prototype_null(this),
prototype_jsreceiver(this);
{
Comment("Argument check: prototype");
GotoIf(IsNull(prototype), &prototype_null);
BranchIfJSReceiver(prototype, &prototype_jsreceiver, &call_runtime);
}
TVARIABLE(Map, map);
TVARIABLE(HeapObject, properties);
Label instantiate_map(this);
BIND(&prototype_null);
{
Comment("Prototype is null");
map = CAST(LoadContextElement(
native_context, Context::SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP));
properties = AllocateNameDictionary(NameDictionary::kInitialCapacity);
Goto(&instantiate_map);
}
BIND(&prototype_jsreceiver);
{
Comment("Prototype is JSReceiver");
properties = EmptyFixedArrayConstant();
TNode<HeapObject> object_function = CAST(
LoadContextElement(native_context, Context::OBJECT_FUNCTION_INDEX));
TNode<Map> object_function_map = LoadObjectField<Map>(
object_function, JSFunction::kPrototypeOrInitialMapOffset);
map = object_function_map;
GotoIf(TaggedEqual(prototype, LoadMapPrototype(map.value())),
&instantiate_map);
Comment("Try loading the prototype info");
TNode<PrototypeInfo> prototype_info =
LoadMapPrototypeInfo(LoadMap(CAST(prototype)), &call_runtime);
TNode<MaybeObject> maybe_map = LoadMaybeWeakObjectField(
prototype_info, PrototypeInfo::kObjectCreateMapOffset);
GotoIf(TaggedEqual(maybe_map, UndefinedConstant()), &call_runtime);
map = CAST(GetHeapObjectAssumeWeak(maybe_map, &call_runtime));
Goto(&instantiate_map);
}
BIND(&instantiate_map);
{
Comment("Instantiate map");
TNode<JSObject> instance =
AllocateJSObjectFromMap(map.value(), properties.value());
Return(instance);
}
BIND(&call_runtime);
{
Comment("Call Runtime (prototype is not null/jsreceiver)");
TNode<Object> result = CallRuntime(Runtime::kObjectCreate, context,
prototype, UndefinedConstant());
Return(result);
}
}
// ES #sec-object.create
TF_BUILTIN(ObjectCreate, ObjectBuiltinsAssembler) {
int const kPrototypeArg = 0;
......
......@@ -105,7 +105,8 @@ operator '==' macro FrameTypeEquals(f1: FrameType, f2: FrameType): bool {
return TaggedEqual(f1, f2);
}
macro Cast<A: type>(implicit context: Context)(o: Frame): A labels CastError;
macro Cast<A : type extends Frame>(implicit context: Context)(o: Frame):
A labels CastError;
Cast<StandardFrame>(implicit context: Context)(f: Frame):
StandardFrame labels CastError {
const o: HeapObject =
......
......@@ -24,6 +24,9 @@ namespace runtime {
extern transitioning runtime
JSReceiverSetPrototypeOfDontThrow(implicit context:
Context)(JSReceiver, JSAny): JSAny;
extern transitioning runtime ObjectCreate(implicit context:
Context)(JSAny, JSAny): JSAny;
} // namespace runtime
namespace object {
......@@ -91,6 +94,48 @@ namespace object {
return proxy::ProxySetPrototypeOf(objectJSProxy, proto, False);
}
transitioning builtin CreateObjectWithoutProperties(
implicit context: Context)(prototype: JSAny): JSAny {
const nativeContext = LoadNativeContext(context);
try {
let map: Map;
let properties: NameDictionary|EmptyFixedArray;
typeswitch (prototype) {
case (Null): {
map = UnsafeCast<Map>(
nativeContext[SLOW_OBJECT_WITH_NULL_PROTOTYPE_MAP]);
properties = AllocateNameDictionary(kNameDictionaryInitialCapacity);
}
case (prototype: JSReceiver): {
properties = kEmptyFixedArray;
const objectFunction =
UnsafeCast<JSFunction>(nativeContext[OBJECT_FUNCTION_INDEX]);
map = UnsafeCast<Map>(objectFunction.prototype_or_initial_map);
if (prototype != map.prototype) {
const prototypeInfo =
prototype.map.PrototypeInfo() otherwise Runtime;
typeswitch (prototypeInfo.object_create_map) {
case (Undefined): {
goto Runtime;
}
case (weak_map: Weak<Map>): {
map = WeakToStrong(weak_map) otherwise Runtime;
}
}
}
}
case (JSAny): {
goto Runtime;
}
}
return AllocateJSObjectFromMap(map, properties);
}
label Runtime deferred {
return runtime::ObjectCreate(prototype, Undefined);
}
}
// ES6 section 19.1.2.11 Object.isExtensible ( O )
transitioning javascript builtin
ObjectIsExtensible(js-implicit context: Context)(object: JSAny): JSAny {
......
......@@ -1130,7 +1130,7 @@ TNode<Int32T> CodeStubAssembler::TruncateIntPtrToInt32(
return ReinterpretCast<Int32T>(value);
}
TNode<BoolT> CodeStubAssembler::TaggedIsSmi(TNode<MaybeObject> a) {
TNode<BoolT> CodeStubAssembler::TaggedIsSmi(SloppyTNode<MaybeObject> a) {
STATIC_ASSERT(kSmiTagMask < kMaxUInt32);
return Word32Equal(
Word32And(TruncateIntPtrToInt32(BitcastTaggedToWordForTagAndSmiBits(a)),
......@@ -1138,7 +1138,7 @@ TNode<BoolT> CodeStubAssembler::TaggedIsSmi(TNode<MaybeObject> a) {
Int32Constant(0));
}
TNode<BoolT> CodeStubAssembler::TaggedIsNotSmi(TNode<MaybeObject> a) {
TNode<BoolT> CodeStubAssembler::TaggedIsNotSmi(SloppyTNode<MaybeObject> a) {
return Word32BinaryNot(TaggedIsSmi(a));
}
......@@ -1749,24 +1749,6 @@ TNode<HeapObject> CodeStubAssembler::LoadMapPrototype(SloppyTNode<Map> map) {
return LoadObjectField<HeapObject>(map, Map::kPrototypeOffset);
}
TNode<PrototypeInfo> CodeStubAssembler::LoadMapPrototypeInfo(
SloppyTNode<Map> map, Label* if_no_proto_info) {
Label if_strong_heap_object(this);
CSA_ASSERT(this, IsMap(map));
TNode<MaybeObject> maybe_prototype_info =
LoadMaybeWeakObjectField(map, Map::kTransitionsOrPrototypeInfoOffset);
TVARIABLE(Object, prototype_info);
DispatchMaybeObject(maybe_prototype_info, if_no_proto_info, if_no_proto_info,
if_no_proto_info, &if_strong_heap_object,
&prototype_info);
BIND(&if_strong_heap_object);
GotoIfNot(TaggedEqual(LoadMap(CAST(prototype_info.value())),
PrototypeInfoMapConstant()),
if_no_proto_info);
return CAST(prototype_info.value());
}
TNode<IntPtrT> CodeStubAssembler::LoadMapInstanceSizeInWords(
SloppyTNode<Map> map) {
CSA_SLOW_ASSERT(this, IsMap(map));
......
......@@ -915,14 +915,9 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<Int32T> TruncateIntPtrToInt32(SloppyTNode<IntPtrT> value);
// Check a value for smi-ness
TNode<BoolT> TaggedIsSmi(TNode<MaybeObject> a);
TNode<BoolT> TaggedIsSmi(SloppyTNode<Object> a) {
return TaggedIsSmi(UncheckedCast<MaybeObject>(a));
}
TNode<BoolT> TaggedIsNotSmi(TNode<MaybeObject> a);
TNode<BoolT> TaggedIsNotSmi(SloppyTNode<Object> a) {
return TaggedIsNotSmi(UncheckedCast<MaybeObject>(a));
}
TNode<BoolT> TaggedIsSmi(SloppyTNode<MaybeObject> a);
TNode<BoolT> TaggedIsNotSmi(SloppyTNode<MaybeObject> a);
// Check that the value is a non-negative smi.
TNode<BoolT> TaggedIsPositiveSmi(SloppyTNode<Object> a);
// Check that a word has a word-aligned address.
......@@ -1071,8 +1066,10 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
return CAST(
LoadFromObject(MachineTypeOf<T>::value, reference.object, offset));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value,
template <class T,
typename std::enable_if<
std::is_convertible<TNode<T>, TNode<UntaggedT>>::value ||
std::is_same<T, MaybeObject>::value,
int>::type = 0>
TNode<T> LoadReference(Reference reference) {
TNode<IntPtrT> offset =
......@@ -1081,7 +1078,8 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
LoadFromObject(MachineTypeOf<T>::value, reference.object, offset));
}
template <class T, typename std::enable_if<
std::is_convertible<TNode<T>, TNode<Object>>::value,
std::is_convertible<TNode<T>, TNode<Object>>::value ||
std::is_same<T, MaybeObject>::value,
int>::type = 0>
void StoreReference(Reference reference, TNode<T> value) {
MachineRepresentation rep = MachineRepresentationOf<T>::value;
......@@ -1162,10 +1160,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TNode<DescriptorArray> LoadMapDescriptors(SloppyTNode<Map> map);
// Load the prototype of a map.
TNode<HeapObject> LoadMapPrototype(SloppyTNode<Map> map);
// Load the prototype info of a map. The result has to be checked if it is a
// prototype info object or not.
TNode<PrototypeInfo> LoadMapPrototypeInfo(SloppyTNode<Map> map,
Label* if_has_no_proto_info);
// Load the instance size of a Map.
TNode<IntPtrT> LoadMapInstanceSizeInWords(SloppyTNode<Map> map);
// Load the inobject properties start of a Map (valid only for JSObjects).
......@@ -1224,10 +1218,6 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
TVariable<Object>* extracted);
// See MaybeObject for semantics of these functions.
TNode<BoolT> IsStrong(TNode<MaybeObject> value);
// This variant is for overzealous checking.
TNode<BoolT> IsStrong(TNode<Object> value) {
return IsStrong(ReinterpretCast<MaybeObject>(value));
}
TNode<HeapObject> GetHeapObjectIfStrong(TNode<MaybeObject> value,
Label* if_not_strong);
......@@ -3513,10 +3503,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
// Support for printf-style debugging
void Print(const char* s);
void Print(const char* prefix, Node* tagged_value);
inline void Print(SloppyTNode<Object> tagged_value) {
return Print(nullptr, tagged_value);
}
inline void Print(TNode<MaybeObject> tagged_value) {
void Print(SloppyTNode<MaybeObject> tagged_value) {
return Print(nullptr, tagged_value);
}
......
......@@ -231,7 +231,9 @@ class int31_t {
template <class T, class U>
struct is_subtype {
static const bool value = std::is_base_of<U, T>::value;
static const bool value =
std::is_base_of<U, T>::value || (std::is_same<U, MaybeObject>::value &&
std::is_convertible<T, Object>::value);
};
template <class T1, class T2, class U>
struct is_subtype<UnionT<T1, T2>, U> {
......@@ -301,19 +303,11 @@ struct types_have_common_values<UnionT<T1, T2>, UnionT<U1, U2>> {
types_have_common_values<T2, U2>::value;
};
template <class T>
struct types_have_common_values<T, MaybeObject> {
static const bool value = types_have_common_values<T, Object>::value;
};
template <class T>
struct types_have_common_values<MaybeObject, T> {
static const bool value = types_have_common_values<Object, T>::value;
};
// TNode<T> is an SSA value with the static type tag T, which is one of the
// following:
// - a subclass of internal::Object represents a tagged type
// - MaybeObject represents the type of all tagged values, including weak
// pointers.
// - a subclass of internal::Object represents a non-weak tagged type.
// - a subclass of internal::UntaggedT represents an untagged type
// - ExternalReference
// - PairT<T1, T2> for an operation returning two values, with types T1
......
......@@ -32,6 +32,7 @@ static const char* const JSANY_TYPE_STRING = "JSAny";
static const char* const JSOBJECT_TYPE_STRING = "JSObject";
static const char* const SMI_TYPE_STRING = "Smi";
static const char* const TAGGED_TYPE_STRING = "Tagged";
static const char* const STRONG_TAGGED_TYPE_STRING = "StrongTagged";
static const char* const UNINITIALIZED_TYPE_STRING = "Uninitialized";
static const char* const RAWPTR_TYPE_STRING = "RawPtr";
static const char* const CONST_STRING_TYPE_STRING = "constexpr string";
......
......@@ -3444,14 +3444,16 @@ void GenerateClassFieldVerifier(const std::string& class_name,
if (!f.generate_verify) return;
const Type* field_type = f.name_and_type.type;
// We only verify tagged types, not raw numbers or pointers. Note that this
// must check against GetObjectType not GetTaggedType, because Uninitialized
// is a Tagged but should not be verified.
if (!field_type->IsSubtypeOf(TypeOracle::GetObjectType())) return;
// We only verify tagged types, not raw numbers or pointers.
if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) return;
// Do not verify if the field may be uninitialized.
if (TypeOracle::GetUninitializedType()->IsSubtypeOf(field_type)) return;
if (f.index) {
if (f.index->type != TypeOracle::GetSmiType()) {
ReportError("Non-SMI values are not (yet) supported as indexes.");
const Type* index_type = f.index->type;
if (index_type != TypeOracle::GetSmiType()) {
Error("Expected type Smi for indexed field but found type ", *index_type)
.Position(f.pos);
}
// We already verified the index field because it was listed earlier, so we
// can assume it's safe to read here.
......@@ -3462,9 +3464,11 @@ void GenerateClassFieldVerifier(const std::string& class_name,
cc_contents << " {\n";
}
const char* object_type = f.is_weak ? "MaybeObject" : "Object";
bool maybe_object =
!f.name_and_type.type->IsSubtypeOf(TypeOracle::GetStrongTaggedType());
const char* object_type = maybe_object ? "MaybeObject" : "Object";
const char* verify_fn =
f.is_weak ? "VerifyMaybeObjectPointer" : "VerifyPointer";
maybe_object ? "VerifyMaybeObjectPointer" : "VerifyPointer";
const char* index_offset = f.index ? "i * kTaggedSize" : "0";
// Name the local var based on the field name for nicer CHECK output.
const std::string value = f.name_and_type.name + "__value";
......@@ -3483,10 +3487,11 @@ void GenerateClassFieldVerifier(const std::string& class_name,
// the Object type because it would not check anything beyond what we already
// checked with VerifyPointer.
if (f.name_and_type.type != TypeOracle::GetObjectType()) {
std::string type_check = f.is_weak ? value + ".IsWeakOrCleared()" : "";
std::string type_check = maybe_object ? value + ".IsWeakOrCleared()" : "";
std::string strong_value =
value + (f.is_weak ? ".GetHeapObjectOrSmi()" : "");
value + (maybe_object ? ".GetHeapObjectOrSmi()" : "");
for (const std::string& runtime_type : field_type->GetRuntimeTypes()) {
if (runtime_type == "MaybeObject") continue;
if (!type_check.empty()) type_check += " || ";
type_check += strong_value + ".Is" + runtime_type + "()";
}
......
......@@ -169,6 +169,10 @@ class TypeOracle : public ContextualClass<TypeOracle> {
return Get().GetBuiltinType(TAGGED_TYPE_STRING);
}
static const Type* GetStrongTaggedType() {
return Get().GetBuiltinType(STRONG_TAGGED_TYPE_STRING);
}
static const Type* GetUninitializedType() {
return Get().GetBuiltinType(UNINITIALIZED_TYPE_STRING);
}
......
......@@ -167,12 +167,12 @@ const ClassType* TypeVisitor::ComputeType(
ReportError("Extern class must extend another type.");
}
const Type* super_type = TypeVisitor::ComputeType(*decl->super);
if (super_type != TypeOracle::GetTaggedType()) {
if (super_type != TypeOracle::GetStrongTaggedType()) {
const ClassType* super_class = ClassType::DynamicCast(super_type);
if (!super_class) {
ReportError(
"class \"", decl->name->value,
"\" must extend either Tagged or an already declared class");
"\" must extend either StrongTagged or an already declared class");
}
if (super_class->HasUndefinedLayout() &&
!(decl->flags & ClassFlag::kUndefinedLayout)) {
......@@ -283,8 +283,11 @@ void TypeVisitor::VisitClassFieldsAndMethods(
field_expression.name_and_type.type->pos);
const Type* field_type = ComputeType(field_expression.name_and_type.type);
if (!(class_declaration->flags & ClassFlag::kExtern)) {
if (!field_type->IsSubtypeOf(TypeOracle::GetTaggedType())) {
ReportError("non-extern classes do not support untagged fields");
if (!field_type->IsSubtypeOf(TypeOracle::GetObjectType())) {
ReportError(
"non-extern classes only support subtypes of type Object, but "
"found type ",
*field_type);
}
if (field_expression.weak) {
ReportError("non-extern classes do not support weak fields");
......
......@@ -241,7 +241,9 @@ class AbstractType final : public Type {
return nullptr;
}
std::vector<std::string> GetRuntimeTypes() const override { return {name()}; }
std::vector<std::string> GetRuntimeTypes() const override {
return {GetGeneratedTNodeTypeName()};
}
private:
friend class TypeOracle;
......
......@@ -27,11 +27,13 @@ namespace torque_internal {
}
}
type Tagged generates 'TNode<Object>' constexpr 'ObjectPtr';
type Smi extends Tagged generates 'TNode<Smi>' constexpr 'Smi';
type Tagged generates 'TNode<MaybeObject>' constexpr 'MaybeObject';
type StrongTagged extends Tagged generates 'TNode<Object>' constexpr 'ObjectPtr';
type Smi extends StrongTagged generates 'TNode<Smi>' constexpr 'Smi';
type Uninitialized extends Tagged;
@abstract
extern class HeapObject extends Tagged {
extern class HeapObject extends StrongTagged {
map: Map;
}
type Map extends HeapObject generates 'TNode<Map>';
......
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