Commit 0c922d87 authored by Nico Hartmann's avatar Nico Hartmann Committed by V8 LUCI CQ

[torque] Generate asserts for C++ object definitions

This CL adds the requirements to port object definitions back to C++.
A @cppObjectDefinition is introduced to annotate classes for which
Torque shall merely generate asserts to check that offsets match between
Torque and C++.

As a first object, this CL ports Oddball back to C++.

Bug: v8:12710
Change-Id: I1304d8980f6318ffccbc2ef7284cb9d46ff579e8
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3523046Reviewed-by: 's avatarCamillo Bruni <cbruni@chromium.org>
Reviewed-by: 's avatarTobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarDominik Inführ <dinfuehr@chromium.org>
Commit-Queue: Nico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/main@{#79856}
parent bb5cc0d5
......@@ -434,6 +434,13 @@ F FUNCTION_CAST(Address addr) {
#define USES_FUNCTION_DESCRIPTORS 0
#endif
constexpr bool StaticStringsEqual(const char* s1, const char* s2) {
for (;; ++s1, ++s2) {
if (*s1 != *s2) return false;
if (*s1 == '\0') return true;
}
}
// -----------------------------------------------------------------------------
// Declarations for use in both the preparser and the rest of V8.
......
......@@ -990,9 +990,23 @@ void JSGlobalObject::JSGlobalObjectVerify(Isolate* isolate) {
}
void Oddball::OddballVerify(Isolate* isolate) {
TorqueGeneratedOddball::OddballVerify(isolate);
PrimitiveHeapObjectVerify(isolate);
CHECK(IsOddball(isolate));
Heap* heap = isolate->heap();
Object string = to_string();
VerifyPointer(isolate, string);
CHECK(string.IsString());
Object type = type_of();
VerifyPointer(isolate, type);
CHECK(type.IsString());
Object kind_value = TaggedField<Object>::load(*this, kKindOffset);
VerifyPointer(isolate, kind_value);
CHECK(kind_value.IsSmi());
Object number = to_number();
VerifyPointer(isolate, number);
CHECK(number.IsSmi() || number.IsHeapNumber());
if (number.IsHeapObject()) {
CHECK(number == ReadOnlyRoots(heap).nan_value() ||
number == ReadOnlyRoots(heap).hole_nan_value());
......
......@@ -40,6 +40,7 @@ namespace internal {
V(JSWeakRef) \
V(Map) \
V(NativeContext) \
V(Oddball) \
V(PreparseData) \
V(PromiseOnStack) \
V(PropertyArray) \
......
......@@ -172,6 +172,9 @@ VisitorId Map::GetVisitorId(Map map) {
case FEEDBACK_METADATA_TYPE:
return kVisitFeedbackMetadata;
case ODDBALL_TYPE:
return kVisitOddball;
case MAP_TYPE:
return kVisitMap;
......
......@@ -56,6 +56,7 @@ enum InstanceType : uint16_t;
V(JSWeakCollection) \
V(Map) \
V(NativeContext) \
V(Oddball) \
V(PreparseData) \
V(PromiseOnStack) \
V(PropertyArray) \
......
......@@ -105,6 +105,13 @@
} \
type holder::name(PtrComprCageBase cage_base, AcquireLoadTag) const
#define TQ_FIELD_TYPE(name, tq_type) \
static constexpr const char* k##name##TqFieldType = tq_type;
#define DECL_FIELD_OFFSET_TQ(name, value, tq_type) \
static const int k##name##Offset = value; \
TQ_FIELD_TYPE(name, tq_type)
#define DECL_SETTER(name, type) \
inline void set_##name(type value, \
WriteBarrierMode mode = UPDATE_WRITE_BARRIER);
......@@ -683,3 +690,6 @@ static_assert(sizeof(unsigned) == sizeof(uint32_t),
#define TQ_OBJECT_CONSTRUCTORS_IMPL(Type) \
inline Type::Type(Address ptr) \
: TorqueGenerated##Type<Type, Type::Super>(ptr) {}
#define TQ_CPP_OBJECT_DEFINITION_ASSERTS(_class, parent) \
template class TorqueGenerated##_class##Asserts<_class, parent>;
......@@ -1283,11 +1283,13 @@ auto BodyDescriptorApply(InstanceType type, Args&&... args) {
case HEAP_NUMBER_TYPE:
return CALL_APPLY(HeapNumber);
case BYTE_ARRAY_TYPE:
return CALL_APPLY(BigInt);
return CALL_APPLY(ByteArray);
case BIGINT_TYPE:
return CALL_APPLY(BigInt);
case ALLOCATION_SITE_TYPE:
return CALL_APPLY(AllocationSite);
case ODDBALL_TYPE:
return CALL_APPLY(Oddball);
#define MAKE_STRUCT_CASE(TYPE, Name, name) \
case TYPE: \
......
......@@ -17,18 +17,30 @@
namespace v8 {
namespace internal {
#include "torque-generated/src/objects/oddball-tq-inl.inc"
TQ_CPP_OBJECT_DEFINITION_ASSERTS(Oddball, PrimitiveHeapObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(Oddball)
OBJECT_CONSTRUCTORS_IMPL(Oddball, PrimitiveHeapObject)
CAST_ACCESSOR(Oddball)
DEF_PRIMITIVE_ACCESSORS(Oddball, to_number_raw, kToNumberRawOffset, double)
void Oddball::set_to_number_raw_as_bits(uint64_t bits) {
// Bug(v8:8875): HeapNumber's double may be unaligned.
base::WriteUnalignedValue<uint64_t>(field_address(kToNumberRawOffset), bits);
}
byte Oddball::kind() const { return TorqueGeneratedOddball::kind(); }
ACCESSORS(Oddball, to_string, String, kToStringOffset)
ACCESSORS(Oddball, to_number, Object, kToNumberOffset)
ACCESSORS(Oddball, type_of, String, kTypeOfOffset)
void Oddball::set_kind(byte value) { TorqueGeneratedOddball::set_kind(value); }
byte Oddball::kind() const {
return Smi::ToInt(TaggedField<Smi>::load(*this, kKindOffset));
}
void Oddball::set_kind(byte value) {
WRITE_FIELD(*this, kKindOffset, Smi::FromInt(value));
}
// static
Handle<Object> Oddball::ToNumber(Isolate* isolate, Handle<Oddball> input) {
......
......@@ -16,28 +16,48 @@ namespace internal {
#include "torque-generated/src/objects/oddball-tq.inc"
// The Oddball describes objects null, undefined, true, and false.
class Oddball : public TorqueGeneratedOddball<Oddball, PrimitiveHeapObject> {
class Oddball : public PrimitiveHeapObject {
public:
// [to_number_raw]: Cached raw to_number computed at startup.
DECL_PRIMITIVE_ACCESSORS(to_number_raw, double)
inline void set_to_number_raw_as_bits(uint64_t bits);
// [to_string]: Cached to_string computed at startup.
DECL_ACCESSORS(to_string, String)
// [to_number]: Cached to_number computed at startup.
DECL_ACCESSORS(to_number, Object)
// [typeof]: Cached type_of computed at startup.
DECL_ACCESSORS(type_of, String)
inline byte kind() const;
inline void set_kind(byte kind);
// Oddball has a custom verifier.
void OddballVerify(Isolate* isolate);
// ES6 section 7.1.3 ToNumber for Boolean, Null, Undefined.
V8_WARN_UNUSED_RESULT static inline Handle<Object> ToNumber(
Isolate* isolate, Handle<Oddball> input);
V8_INLINE bool ToBool(Isolate* isolate) const;
DECL_CAST(Oddball)
// Dispatched behavior.
DECL_VERIFIER(Oddball)
// Initialize the fields.
static void Initialize(Isolate* isolate, Handle<Oddball> oddball,
const char* to_string, Handle<Object> to_number,
const char* type_of, byte kind);
// Layout description.
DECL_FIELD_OFFSET_TQ(ToNumberRaw, HeapObject::kHeaderSize, "float64")
DECL_FIELD_OFFSET_TQ(ToString, kToNumberRawOffset + kDoubleSize, "String")
DECL_FIELD_OFFSET_TQ(ToNumber, kToStringOffset + kTaggedSize, "Number")
DECL_FIELD_OFFSET_TQ(TypeOf, kToNumberOffset + kTaggedSize, "String")
DECL_FIELD_OFFSET_TQ(Kind, kTypeOfOffset + kTaggedSize, "Smi")
static const int kSize = kKindOffset + kTaggedSize;
static const byte kFalse = 0;
static const byte kTrue = 1;
static const byte kNotBooleanMask = static_cast<byte>(~1);
......@@ -53,7 +73,8 @@ class Oddball : public TorqueGeneratedOddball<Oddball, PrimitiveHeapObject> {
static const byte kSelfReferenceMarker = 10;
static const byte kBasicBlockCountersMarker = 11;
class BodyDescriptor;
using BodyDescriptor =
FixedBodyDescriptor<kToStringOffset, kKindOffset, kSize>;
STATIC_ASSERT(kKindOffset == Internals::kOddballKindOffset);
STATIC_ASSERT(kNull == Internals::kNullOddballKind);
......@@ -61,7 +82,7 @@ class Oddball : public TorqueGeneratedOddball<Oddball, PrimitiveHeapObject> {
DECL_PRINTER(Oddball)
TQ_OBJECT_CONSTRUCTORS(Oddball)
OBJECT_CONSTRUCTORS(Oddball, PrimitiveHeapObject);
};
} // namespace internal
......
......@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@generateBodyDescriptor
@cppObjectDefinition
@apiExposedInstanceTypeValue(0x83)
@highestInstanceTypeWithinParentClassRange
extern class Oddball extends PrimitiveHeapObject {
......
......@@ -111,6 +111,8 @@ static const char* const ANNOTATION_EXPORT = "@export";
static const char* const ANNOTATION_DO_NOT_GENERATE_CAST = "@doNotGenerateCast";
static const char* const ANNOTATION_USE_PARENT_TYPE_CHECKER =
"@useParentTypeChecker";
static const char* const ANNOTATION_CPP_OBJECT_DEFINITION =
"@cppObjectDefinition";
// Generate C++ accessors with relaxed store semantics.
// Weak<T> and MaybeObject fields always use relaxed store.
static const char* const ANNOTATION_CPP_RELAXED_STORE = "@cppRelaxedStore";
......@@ -162,6 +164,7 @@ enum class ClassFlag {
kDoNotGenerateCast = 1 << 11,
kGenerateUniqueMap = 1 << 12,
kGenerateFactoryFunction = 1 << 13,
kCppObjectDefinition = 1 << 14,
};
using ClassFlags = base::Flags<ClassFlag>;
......
......@@ -3995,6 +3995,7 @@ class CppClassGenerator {
}
void GenerateClass();
void GenerateCppObjectDefinitionAsserts();
private:
SourcePosition Position();
......@@ -4124,8 +4125,6 @@ void CppClassGenerator::GenerateClass() {
<< "::cast(*this), "
"isolate);\n";
impl_ << "}\n\n";
}
if (type_->ShouldGenerateVerify()) {
impl_ << "\n";
}
......@@ -4241,6 +4240,36 @@ void CppClassGenerator::GenerateClass() {
}
}
void CppClassGenerator::GenerateCppObjectDefinitionAsserts() {
hdr_ << "// Definition " << Position() << "\n"
<< template_decl() << "\n"
<< "class " << gen_name_ << "Asserts {\n";
ClassFieldOffsetGenerator g(hdr_, inl_, type_, gen_name_,
type_->GetSuperClass());
for (auto f : type_->fields()) {
CurrentSourcePosition::Scope scope(f.pos);
g.RecordOffsetFor(f);
}
g.Finish();
hdr_ << "\n";
for (auto f : type_->fields()) {
std::string field = "k" + CamelifyString(f.name_and_type.name) + "Offset";
std::string type = f.name_and_type.type->SimpleName();
hdr_ << " static_assert(" << field << " == D::" << field << ",\n"
<< " \"Values of " << name_ << "::" << field
<< " defined in Torque and C++ do not match\");\n"
<< " static_assert(StaticStringsEqual(\"" << type << "\", D::k"
<< CamelifyString(f.name_and_type.name) << "TqFieldType),\n"
<< " \"Types of " << name_ << "::" << field
<< " specified in Torque and C++ do not match\");\n";
}
hdr_ << " static_assert(kSize == D::kSize);\n";
hdr_ << "};\n\n";
}
void CppClassGenerator::GenerateClassCasts() {
cpp::Class owner({cpp::TemplateParameter("D"), cpp::TemplateParameter("P")},
gen_name_);
......@@ -4704,7 +4733,9 @@ void ImplementationVisitor::GenerateClassDefinitions(
std::string name = type->ShouldGenerateCppClassDefinitions()
? type->name()
: type->GetGeneratedTNodeTypeName();
if (type->ShouldGenerateCppClassDefinitions()) {
header << "class " << name << ";\n";
}
forward_declarations << "class " << name << ";\n";
}
......@@ -4718,6 +4749,9 @@ void ImplementationVisitor::GenerateClassDefinitions(
if (type->ShouldGenerateCppClassDefinitions()) {
CppClassGenerator g(type, header, inline_header, implementation);
g.GenerateClass();
} else if (type->ShouldGenerateCppObjectDefinitionAsserts()) {
CppClassGenerator g(type, header, inline_header, implementation);
g.GenerateCppObjectDefinitionAsserts();
}
for (const Field& f : type->fields()) {
const Type* field_type = f.name_and_type.type;
......
......@@ -975,7 +975,8 @@ base::Optional<ParseResult> MakeClassDeclaration(
ANNOTATION_EXPORT, ANNOTATION_DO_NOT_GENERATE_CAST,
ANNOTATION_GENERATE_UNIQUE_MAP, ANNOTATION_GENERATE_FACTORY_FUNCTION,
ANNOTATION_HIGHEST_INSTANCE_TYPE_WITHIN_PARENT,
ANNOTATION_LOWEST_INSTANCE_TYPE_WITHIN_PARENT},
ANNOTATION_LOWEST_INSTANCE_TYPE_WITHIN_PARENT,
ANNOTATION_CPP_OBJECT_DEFINITION},
{ANNOTATION_RESERVE_BITS_IN_INSTANCE_TYPE,
ANNOTATION_INSTANCE_TYPE_VALUE});
ClassFlags flags = ClassFlag::kNone;
......@@ -1020,6 +1021,9 @@ base::Optional<ParseResult> MakeClassDeclaration(
if (annotations.Contains(ANNOTATION_LOWEST_INSTANCE_TYPE_WITHIN_PARENT)) {
flags |= ClassFlag::kLowestInstanceTypeWithinParent;
}
if (annotations.Contains(ANNOTATION_CPP_OBJECT_DEFINITION)) {
flags |= ClassFlag::kCppObjectDefinition;
}
auto is_extern = child_results->NextAs<bool>();
if (is_extern) flags |= ClassFlag::kExtern;
......
......@@ -668,16 +668,21 @@ class ClassType final : public AggregateType {
std::string GetGeneratedTNodeTypeNameImpl() const override;
bool IsExtern() const { return flags_ & ClassFlag::kExtern; }
bool ShouldGeneratePrint() const {
return !IsExtern() || (ShouldGenerateCppClassDefinitions() &&
!IsAbstract() && !HasUndefinedLayout());
if (flags_ & ClassFlag::kCppObjectDefinition) return false;
if (!IsExtern()) return true;
if (!ShouldGenerateCppClassDefinitions()) return false;
return !IsAbstract() && !HasUndefinedLayout();
}
bool ShouldGenerateVerify() const {
return !IsExtern() || (ShouldGenerateCppClassDefinitions() &&
!HasUndefinedLayout() && !IsShape());
if (flags_ & ClassFlag::kCppObjectDefinition) return false;
if (!IsExtern()) return true;
if (!ShouldGenerateCppClassDefinitions()) return false;
return !HasUndefinedLayout() && !IsShape();
}
bool ShouldGenerateBodyDescriptor() const {
return flags_ & ClassFlag::kGenerateBodyDescriptor ||
(!IsAbstract() && !IsExtern());
if (flags_ & ClassFlag::kCppObjectDefinition) return false;
if (flags_ & ClassFlag::kGenerateBodyDescriptor) return true;
return !IsAbstract() && !IsExtern();
}
bool DoNotGenerateCast() const {
return flags_ & ClassFlag::kDoNotGenerateCast;
......@@ -688,8 +693,12 @@ class ClassType final : public AggregateType {
return flags_ & ClassFlag::kHasSameInstanceTypeAsParent;
}
bool ShouldGenerateCppClassDefinitions() const {
if (flags_ & ClassFlag::kCppObjectDefinition) return false;
return (flags_ & ClassFlag::kGenerateCppClassDefinitions) || !IsExtern();
}
bool ShouldGenerateCppObjectDefinitionAsserts() const {
return flags_ & ClassFlag::kCppObjectDefinition;
}
bool ShouldGenerateFullClassDefinition() const { return !IsExtern(); }
bool ShouldGenerateUniqueMap() const {
return (flags_ & ClassFlag::kGenerateUniqueMap) ||
......
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