Commit f8809072 authored by Tobias Tebbi's avatar Tobias Tebbi Committed by Commit Bot

[torque] @relaxedWrite annotation; generate Context C++ class/BodyDescr.

This CL lets Torque generate the Context C++ class and BodyDescriptor
for Context.
This requires two Torque changes:

- Allow @generateBodyDescriptor on @abstract classes, since all Context
  classes share the same BodyDescriptor.

- Add a new annotation @relaxedWrite, which makes C++ setters
  use WRITE_RELAXED_FIELD instead of WRITE_FIELD.
  Attention: As a side-effect, this CL disables using
    WRITE_RELAXED_FIELD by default for all non-array fields. If this
    causes problems, we should manually add @relaxedWrite to the
    corresponding fields.

Bug: v8:7793

Change-Id: I735b310bcb36a3612d86c22efa9c0bfc108d4ca6
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2529453
Commit-Queue: Tobias Tebbi <tebbi@chromium.org>
Reviewed-by: 's avatarUlan Degenbaev <ulan@chromium.org>
Reviewed-by: 's avatarNico Hartmann <nicohartmann@chromium.org>
Cr-Commit-Position: refs/heads/master@{#71123}
parent 4b71ac6b
......@@ -22,7 +22,6 @@ namespace internal {
V(Cell) \
V(Code) \
V(CodeDataContainer) \
V(Context) \
V(CoverageInfo) \
V(DataHandler) \
V(EmbedderDataArray) \
......
......@@ -25,6 +25,8 @@
namespace v8 {
namespace internal {
#include "torque-generated/src/objects/contexts-tq-inl.inc"
OBJECT_CONSTRUCTORS_IMPL(ScriptContextTable, FixedArray)
CAST_ACCESSOR(ScriptContextTable)
......@@ -48,39 +50,20 @@ Context ScriptContextTable::get_context(int i) const {
return Context::cast(this->get(i + kFirstContextSlotIndex));
}
OBJECT_CONSTRUCTORS_IMPL(Context, HeapObject)
TQ_OBJECT_CONSTRUCTORS_IMPL(Context)
NEVER_READ_ONLY_SPACE_IMPL(Context)
CAST_ACCESSOR(Context)
SMI_ACCESSORS(Context, length, kLengthOffset)
CAST_ACCESSOR(NativeContext)
Object Context::get(int index) const {
IsolateRoot isolate = GetIsolateForPtrCompr(*this);
return get(isolate, index);
V8_INLINE Object Context::get(int index) const { return elements(index); }
V8_INLINE Object Context::get(IsolateRoot isolate, int index) const {
return elements(isolate, index);
}
Object Context::get(IsolateRoot isolate, int index) const {
DCHECK_LT(static_cast<unsigned>(index),
static_cast<unsigned>(this->length()));
return TaggedField<Object>::Relaxed_Load(isolate, *this,
OffsetOfElementAt(index));
V8_INLINE void Context::set(int index, Object value) {
set_elements(index, value);
}
void Context::set(int index, Object value) {
DCHECK_LT(static_cast<unsigned>(index),
static_cast<unsigned>(this->length()));
int offset = OffsetOfElementAt(index);
RELAXED_WRITE_FIELD(*this, offset, value);
WRITE_BARRIER(*this, offset, value);
}
void Context::set(int index, Object value, WriteBarrierMode mode) {
DCHECK_LT(static_cast<unsigned>(index),
static_cast<unsigned>(this->length()));
int offset = OffsetOfElementAt(index);
RELAXED_WRITE_FIELD(*this, offset, value);
CONDITIONAL_WRITE_BARRIER(*this, offset, value, mode);
V8_INLINE void Context::set(int index, Object value, WriteBarrierMode mode) {
set_elements(index, value, mode);
}
void Context::set_scope_info(ScopeInfo scope_info) {
......
......@@ -337,6 +337,8 @@ enum ContextLookupFlags {
V(RETAINED_MAPS, WeakArrayList, retained_maps) \
V(OSR_CODE_CACHE_INDEX, WeakFixedArray, osr_code_cache)
#include "torque-generated/src/objects/contexts-tq.inc"
// A table of all script contexts. Every loaded top-level script with top-level
// lexical declarations contributes its ScriptContext into this table.
//
......@@ -428,15 +430,10 @@ class ScriptContextTable : public FixedArray {
// Script contexts from all top-level scripts are gathered in
// ScriptContextTable.
class Context : public HeapObject {
class Context : public TorqueGeneratedContext<Context, HeapObject> {
public:
NEVER_READ_ONLY_SPACE
DECL_CAST(Context)
// [length]: length of the context.
V8_INLINE int length() const;
V8_INLINE void set_length(int value);
// Setter and getter for elements.
V8_INLINE Object get(int index) const;
V8_INLINE Object get(IsolateRoot isolate, int index) const;
......@@ -448,15 +445,9 @@ class Context : public HeapObject {
V8_INLINE Object synchronized_get(IsolateRoot isolate, int index) const;
V8_INLINE void synchronized_set(int index, Object value);
DEFINE_FIELD_OFFSET_CONSTANTS(HeapObject::kHeaderSize,
TORQUE_GENERATED_CONTEXT_FIELDS)
static const int kScopeInfoOffset = kHeaderSize;
static const int kScopeInfoOffset = kElementsOffset;
static const int kPreviousOffset = kScopeInfoOffset + kTaggedSize;
// TODO(v8:8989): [torque] Support marker constants
/* TODO(ishell): remove this fixedArray-like header size. */
static const int kFixedArrayLikeHeaderSize = kScopeInfoOffset;
static const int kStartOfTaggedFieldsOffset = kScopeInfoOffset;
/* Header size. */ \
/* TODO(ishell): use this as header size once MIN_CONTEXT_SLOTS */ \
/* is removed in favour of offset-based access to common fields. */ \
......@@ -467,7 +458,10 @@ class Context : public HeapObject {
// Garbage collection support.
V8_INLINE static constexpr int SizeFor(int length) {
return kFixedArrayLikeHeaderSize + length * kTaggedSize;
// TODO(v8:9287): This is a workaround for GCMole build failures.
int result = kElementsOffset + length * kTaggedSize;
DCHECK_EQ(TorqueGeneratedContext::SizeFor(length), result);
return result;
}
// Code Generation support.
......@@ -477,7 +471,7 @@ class Context : public HeapObject {
}
// Offset of the element from the heap object pointer.
V8_INLINE static constexpr int SlotOffset(int index) {
return SizeFor(index) - kHeapObjectTag;
return OffsetOfElementAt(index) - kHeapObjectTag;
}
// Initializes the variable slots of the context. Lexical variables that need
......@@ -647,7 +641,7 @@ class Context : public HeapObject {
DECL_PRINTER(Context)
DECL_VERIFIER(Context)
using BodyDescriptor = FlexibleBodyDescriptor<kStartOfTaggedFieldsOffset>;
class BodyDescriptor;
private:
#ifdef DEBUG
......@@ -655,7 +649,7 @@ class Context : public HeapObject {
static bool IsBootstrappingOrValidParentContext(Object object, Context kid);
#endif
OBJECT_CONSTRUCTORS(Context, HeapObject);
TQ_OBJECT_CONSTRUCTORS(Context)
};
class NativeContext : public Context {
......
......@@ -3,12 +3,17 @@
// found in the LICENSE file.
@abstract
extern class Context extends HeapObject {
@export
@customCppClass
// We normally don't generate a BodyDescriptor for an abstact class, but here we
// do since all context classes share the same BodyDescriptor.
@generateBodyDescriptor
class Context extends HeapObject {
macro GetScopeInfo(): ScopeInfo {
return *ContextSlot(this, ContextSlot::SCOPE_INFO_INDEX);
}
const length: Smi;
elements[length]: Object;
@relaxedWrite elements[length]: Object;
}
extern class AwaitContext extends Context generates 'TNode<Context>';
......
......@@ -38,7 +38,6 @@ enum InstanceType : uint16_t;
V(Cell) \
V(Code) \
V(CodeDataContainer) \
V(Context) \
V(DataHandler) \
V(EmbedderDataArray) \
V(EphemeronHashTable) \
......
......@@ -98,7 +98,6 @@ class ZoneForwardList;
V(CompilationCacheTable) \
V(ConsString) \
V(Constructor) \
V(Context) \
V(CoverageInfo) \
V(ClosureFeedbackCellArray) \
V(DataHandler) \
......
......@@ -18,7 +18,6 @@ extern class ConsString extends String {
}
@abstract
@generateBodyDescriptor
@doNotGenerateCast
extern class ExternalString extends String {
resource: ExternalPointer;
......
......@@ -931,6 +931,7 @@ struct ClassFieldExpression {
bool weak;
bool const_qualified;
bool generate_verify;
bool relaxed_write;
};
struct LabelAndTypes {
......
......@@ -103,6 +103,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";
// Generate C++ accessors with relaxed write semantics.
static const char* const ANNOTATION_RELAXED_WRITE = "@relaxedWrite";
inline bool IsConstexprName(const std::string& name) {
return name.substr(0, std::strlen(CONSTEXPR_TYPE_PREFIX)) ==
......
......@@ -3781,8 +3781,9 @@ void CppClassGenerator::GenerateClass() {
if (!index_fields.has_value()) {
hdr_ << " // SizeFor implementations not generated due to complex array "
"lengths\n\n";
} else if (!type_->IsAbstract() &&
!type_->IsSubtypeOf(TypeOracle::GetJSObjectType())) {
} else if (type_->ShouldGenerateBodyDescriptor() ||
(!type_->IsAbstract() &&
!type_->IsSubtypeOf(TypeOracle::GetJSObjectType()))) {
hdr_ << " V8_INLINE static constexpr int32_t SizeFor(";
bool first = true;
for (const Field& field : *index_fields) {
......@@ -4060,12 +4061,15 @@ void CppClassGenerator::GenerateFieldAccessorForSmi(const Field& f) {
inl_ << "int i, ";
}
inl_ << type << " value) {\n";
const char* write_macro =
f.relaxed_write ? "RELAXED_WRITE_FIELD" : "WRITE_FIELD";
if (f.index) {
GenerateBoundsDCheck(inl_, "i", type_, f);
inl_ << " int offset = " << offset << " + i * kTaggedSize;\n";
inl_ << " WRITE_FIELD(*this, offset, Smi::FromInt(value));\n";
inl_ << " " << write_macro << "(*this, offset, Smi::FromInt(value));\n";
} else {
inl_ << " WRITE_FIELD(*this, " << offset << ", Smi::FromInt(value));\n";
inl_ << " " << write_macro << "(*this, " << offset
<< ", Smi::FromInt(value));\n";
}
inl_ << "}\n\n";
}
......@@ -4105,12 +4109,6 @@ void CppClassGenerator::GenerateFieldAccessorForTagged(const Field& f) {
inl_ << type << " " << gen_name_ << "<D, P>::" << name
<< "(IsolateRoot isolate" << (f.index ? ", int i" : "") << ") const {\n";
// TODO(tebbi): The distinction between relaxed and non-relaxed accesses here
// is pretty arbitrary and just tries to preserve what was there before.
// It currently doesn't really make a difference due to concurrent marking
// turning all loads and stores to be relaxed. We should probably drop the
// distinction at some point, even though in principle non-relaxed operations
// would give us TSAN protection.
if (f.index) {
GenerateBoundsDCheck(inl_, "i", type_, f);
inl_ << " int offset = " << offset << " + i * kTaggedSize;\n";
......@@ -4135,16 +4133,15 @@ void CppClassGenerator::GenerateFieldAccessorForTagged(const Field& f) {
if (!type_check.empty()) {
inl_ << " SLOW_DCHECK(" << type_check << ");\n";
}
const char* write_macro =
strong_pointer ? (f.relaxed_write ? "RELAXED_WRITE_FIELD" : "WRITE_FIELD")
: "RELAXED_WRITE_WEAK_FIELD";
if (f.index) {
GenerateBoundsDCheck(inl_, "i", type_, f);
const char* write_macro =
strong_pointer ? "WRITE_FIELD" : "RELAXED_WRITE_WEAK_FIELD";
inl_ << " int offset = " << offset << " + i * kTaggedSize;\n";
offset = "offset";
inl_ << " " << write_macro << "(*this, offset, value);\n";
} else {
const char* write_macro =
strong_pointer ? "RELAXED_WRITE_FIELD" : "RELAXED_WRITE_WEAK_FIELD";
inl_ << " " << write_macro << "(*this, " << offset << ", value);\n";
}
const char* write_barrier = strong_pointer ? "CONDITIONAL_WRITE_BARRIER"
......
......@@ -1939,9 +1939,11 @@ base::Optional<ParseResult> MakeAnnotation(ParseResultIterator* child_results) {
}
base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
AnnotationSet annotations(child_results, {ANNOTATION_NO_VERIFIER},
AnnotationSet annotations(child_results,
{ANNOTATION_NO_VERIFIER, ANNOTATION_RELAXED_WRITE},
{ANNOTATION_IF, ANNOTATION_IFNOT});
bool generate_verify = !annotations.Contains(ANNOTATION_NO_VERIFIER);
bool relaxed_write = annotations.Contains(ANNOTATION_RELAXED_WRITE);
std::vector<ConditionalAnnotation> conditions;
base::Optional<std::string> if_condition =
annotations.GetStringParam(ANNOTATION_IF);
......@@ -1964,7 +1966,8 @@ base::Optional<ParseResult> MakeClassField(ParseResultIterator* child_results) {
std::move(conditions),
weak,
const_qualified,
generate_verify}};
generate_verify,
relaxed_write}};
}
base::Optional<ParseResult> MakeStructField(
......
......@@ -212,6 +212,7 @@ const StructType* TypeVisitor::ComputeType(
offset.SingleValue(),
false,
field.const_qualified,
false,
false};
auto optional_size = SizeOf(f.name_and_type.type);
struct_type->RegisterField(f);
......@@ -313,8 +314,7 @@ const ClassType* TypeVisitor::ComputeType(
Error("non-external classes must have defined layouts");
}
}
flags = flags | ClassFlag::kGeneratePrint | ClassFlag::kGenerateVerify |
ClassFlag::kGenerateBodyDescriptor;
flags = flags | ClassFlag::kGeneratePrint | ClassFlag::kGenerateVerify;
}
if (!(flags & ClassFlag::kExtern) &&
(flags & ClassFlag::kHasSameInstanceTypeAsParent)) {
......@@ -428,7 +428,8 @@ void TypeVisitor::VisitClassFieldsAndMethods(
class_offset.SingleValue(),
field_expression.weak,
field_expression.const_qualified,
field_expression.generate_verify});
field_expression.generate_verify,
field_expression.relaxed_write});
ResidueClass field_size = std::get<0>(field.GetFieldSizeInformation());
if (field.index) {
// Validate that a value at any index in a packed array is aligned
......
......@@ -903,9 +903,6 @@ void ClassType::GenerateSliceAccessor(size_t field_index) {
}
bool ClassType::HasStaticSize() const {
// Abstract classes don't have instances directly, so asking this question
// doesn't make sense.
DCHECK(!IsAbstract());
if (IsSubtypeOf(TypeOracle::GetJSObjectType()) && !IsShape()) return false;
return size().SingleValue().has_value();
}
......
......@@ -227,6 +227,7 @@ struct Field {
bool is_weak;
bool const_qualified;
bool generate_verify;
bool relaxed_write;
};
std::ostream& operator<<(std::ostream& os, const Field& name_and_type);
......@@ -668,8 +669,8 @@ class ClassType final : public AggregateType {
(!HasUndefinedLayout() && !IsShape()));
}
bool ShouldGenerateBodyDescriptor() const {
if (IsAbstract()) return false;
return flags_ & ClassFlag::kGenerateBodyDescriptor || !IsExtern();
return flags_ & ClassFlag::kGenerateBodyDescriptor ||
(!IsAbstract() && !IsExtern());
}
bool DoNotGenerateCast() const {
return flags_ & ClassFlag::kDoNotGenerateCast;
......
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