v8/v8
0

[tagged-ptr] Introduce FLEXIBLE_ARRAY_MEMBER macro

Because of compiler limitations with the flexible array member
extension, in particular around subclassing, introduce a new macro which
defines the flexible array members. This also requires accessing
flexible array members using a macro, rather than with offsetof.

Bug: v8:12710
Change-Id: Ibf8ae9b20bb1a83be7374e439bff484914d7bad1
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5148173
Reviewed-by: Anton Bikineev <bikineev@chromium.org>
Auto-Submit: Leszek Swirski <leszeks@chromium.org>
Reviewed-by: Nico Hartmann <nicohartmann@chromium.org>
Commit-Queue: Leszek Swirski <leszeks@chromium.org>
Cr-Commit-Position: refs/heads/main@{#91765}
This commit is contained in:
Leszek Swirski
2024-01-04 15:50:08 +01:00
committed by V8 LUCI CQ
parent ad7450ad27
commit 79d85c3df8
15 changed files with 120 additions and 70 deletions

@ -31,15 +31,15 @@ class IntlBuiltinsAssembler : public CodeStubAssembler {
TNode<IntPtrT> PointerToSeqStringData(TNode<String> seq_string) {
CSA_DCHECK(this,
IsSequentialStringInstanceType(LoadInstanceType(seq_string)));
static_assert(offsetof(SeqOneByteString, chars_) ==
offsetof(SeqTwoByteString, chars_));
return IntPtrAdd(
BitcastTaggedToWord(seq_string),
IntPtrConstant(offsetof(SeqOneByteString, chars_) - kHeapObjectTag));
static_assert(OFFSET_OF_DATA_START(SeqOneByteString) ==
OFFSET_OF_DATA_START(SeqTwoByteString));
return IntPtrAdd(BitcastTaggedToWord(seq_string),
IntPtrConstant(OFFSET_OF_DATA_START(SeqOneByteString) -
kHeapObjectTag));
}
TNode<Uint8T> GetChar(TNode<SeqOneByteString> seq_string, int index) {
size_t effective_offset = offsetof(SeqOneByteString, chars_) +
size_t effective_offset = OFFSET_OF_DATA_START(SeqOneByteString) +
sizeof(SeqOneByteString::Char) * index -
kHeapObjectTag;
return Load<Uint8T>(seq_string, IntPtrConstant(effective_offset));
@ -50,7 +50,7 @@ class IntlBuiltinsAssembler : public CodeStubAssembler {
void JumpIfStartsWithIgnoreCase(TNode<SeqOneByteString> seq_string,
const char* pattern, Label* target) {
size_t effective_offset =
offsetof(SeqOneByteString, chars_) - kHeapObjectTag;
OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag;
TNode<Uint16T> raw =
Load<Uint16T>(seq_string, IntPtrConstant(effective_offset));
DCHECK_EQ(strlen(pattern), 2);

@ -32,11 +32,11 @@ TNode<RawPtrT> StringBuiltinsAssembler::DirectStringData(
BIND(&if_sequential);
{
static_assert(offsetof(SeqOneByteString, chars_) ==
offsetof(SeqTwoByteString, chars_));
var_data = RawPtrAdd(
ReinterpretCast<RawPtrT>(BitcastTaggedToWord(string)),
IntPtrConstant(offsetof(SeqOneByteString, chars_) - kHeapObjectTag));
static_assert(OFFSET_OF_DATA_START(SeqOneByteString) ==
OFFSET_OF_DATA_START(SeqTwoByteString));
var_data = RawPtrAdd(ReinterpretCast<RawPtrT>(BitcastTaggedToWord(string)),
IntPtrConstant(OFFSET_OF_DATA_START(SeqOneByteString) -
kHeapObjectTag));
Goto(&if_join);
}
@ -421,7 +421,7 @@ TNode<String> StringBuiltinsAssembler::StringFromSingleUTF16EncodedCodePoint(
TNode<String> value = AllocateSeqTwoByteString(2);
StoreNoWriteBarrier(
MachineRepresentation::kWord32, value,
IntPtrConstant(offsetof(SeqTwoByteString, chars_) - kHeapObjectTag),
IntPtrConstant(OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag),
codepoint);
var_result = value;
Goto(&return_result);
@ -731,7 +731,7 @@ void StringBuiltinsAssembler::GenerateStringRelationalComparison(
// Loop over the {lhs} and {rhs} strings to see if they are equal.
constexpr int kBeginOffset =
offsetof(SeqOneByteString, chars_) - kHeapObjectTag;
OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag;
TNode<IntPtrT> begin = IntPtrConstant(kBeginOffset);
TNode<IntPtrT> end = IntPtrAdd(begin, length);
TVARIABLE(IntPtrT, var_offset, begin);
@ -759,7 +759,7 @@ void StringBuiltinsAssembler::GenerateStringRelationalComparison(
GotoIf(Word64NotEqual(lhs_chunk, rhs_chunk), &char_loop);
}
var_offset = IntPtrConstant(offsetof(SeqOneByteString, chars_) -
var_offset = IntPtrConstant(OFFSET_OF_DATA_START(SeqOneByteString) -
kHeapObjectTag + kChunkSize);
Goto(&chunk_loop);
@ -1005,7 +1005,7 @@ TF_BUILTIN(StringFromCharCode, StringBuiltinsAssembler) {
// The {code16} fits into the SeqOneByteString {one_byte_result}.
TNode<IntPtrT> offset = ElementOffsetFromIndex(
var_max_index.value(), UINT8_ELEMENTS,
offsetof(SeqOneByteString, chars_) - kHeapObjectTag);
OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag);
StoreNoWriteBarrier(MachineRepresentation::kWord8, one_byte_result,
offset, code16);
var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
@ -1029,7 +1029,7 @@ TF_BUILTIN(StringFromCharCode, StringBuiltinsAssembler) {
// Write the character that caused the 8-bit to 16-bit fault.
TNode<IntPtrT> max_index_offset = ElementOffsetFromIndex(
var_max_index.value(), UINT16_ELEMENTS,
offsetof(SeqTwoByteString, chars_) - kHeapObjectTag);
OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag);
StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
max_index_offset, code16);
var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
@ -1046,7 +1046,7 @@ TF_BUILTIN(StringFromCharCode, StringBuiltinsAssembler) {
TNode<IntPtrT> offset = ElementOffsetFromIndex(
var_max_index.value(), UINT16_ELEMENTS,
offsetof(SeqTwoByteString, chars_) - kHeapObjectTag);
OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag);
StoreNoWriteBarrier(MachineRepresentation::kWord16, two_byte_result,
offset, code16);
var_max_index = IntPtrAdd(var_max_index.value(), IntPtrConstant(1));
@ -1801,9 +1801,9 @@ void StringBuiltinsAssembler::CopyStringCharacters(
ElementsKind from_kind = from_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
ElementsKind to_kind = to_one_byte ? UINT8_ELEMENTS : UINT16_ELEMENTS;
static_assert(offsetof(SeqOneByteString, chars_) ==
offsetof(SeqTwoByteString, chars_));
int header_size = offsetof(SeqOneByteString, chars_) - kHeapObjectTag;
static_assert(OFFSET_OF_DATA_START(SeqOneByteString) ==
OFFSET_OF_DATA_START(SeqTwoByteString));
int header_size = OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag;
TNode<IntPtrT> from_offset =
ElementOffsetFromIndex(from_index, from_kind, header_size);
TNode<IntPtrT> to_offset =
@ -1888,7 +1888,7 @@ TNode<String> StringBuiltinsAssembler::AllocAndCopyStringCharacters(
// in wasm.tq.
TNode<IntPtrT> start_offset = ElementOffsetFromIndex(
from_index, UINT16_ELEMENTS,
offsetof(SeqTwoByteString, chars_) - kHeapObjectTag);
OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag);
TNode<IntPtrT> end_offset = IntPtrAdd(
start_offset, ElementOffsetFromIndex(character_count, UINT16_ELEMENTS));
TNode<IntPtrT> eight_char_loop_end = IntPtrSub(

@ -7906,7 +7906,7 @@ TNode<String> CodeStubAssembler::StringFromSingleCharCode(TNode<Int32T> code) {
TNode<String> result = AllocateSeqTwoByteString(1);
StoreNoWriteBarrier(
MachineRepresentation::kWord16, result,
IntPtrConstant(offsetof(SeqTwoByteString, chars_) - kHeapObjectTag),
IntPtrConstant(OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag),
code);
var_result = result;
Goto(&if_done);
@ -8023,14 +8023,14 @@ TNode<RawPtrT> ToDirectStringAssembler::TryToSequential(
BIND(&if_issequential);
{
static_assert(offsetof(SeqOneByteString, chars_) ==
offsetof(SeqTwoByteString, chars_));
static_assert(OFFSET_OF_DATA_START(SeqOneByteString) ==
OFFSET_OF_DATA_START(SeqTwoByteString));
TNode<RawPtrT> result =
ReinterpretCast<RawPtrT>(BitcastTaggedToWord(var_string_.value()));
if (ptr_kind == PTR_TO_DATA) {
result = RawPtrAdd(
result,
IntPtrConstant(offsetof(SeqOneByteString, chars_) - kHeapObjectTag));
result = RawPtrAdd(result,
IntPtrConstant(OFFSET_OF_DATA_START(SeqOneByteString) -
kHeapObjectTag));
}
var_result = result;
Goto(&out);
@ -8044,9 +8044,9 @@ TNode<RawPtrT> ToDirectStringAssembler::TryToSequential(
TNode<String> string = var_string_.value();
TNode<RawPtrT> result = LoadExternalStringResourceDataPtr(CAST(string));
if (ptr_kind == PTR_TO_STRING) {
result = RawPtrSub(
result,
IntPtrConstant(offsetof(SeqOneByteString, chars_) - kHeapObjectTag));
result = RawPtrSub(result,
IntPtrConstant(OFFSET_OF_DATA_START(SeqOneByteString) -
kHeapObjectTag));
}
var_result = result;
Goto(&out);

@ -904,7 +904,7 @@ FieldAccess AccessBuilder::ForExternalStringResourceData() {
// static
ElementAccess AccessBuilder::ForSeqOneByteStringCharacter() {
ElementAccess access = {kTaggedBase, offsetof(SeqOneByteString, chars_),
ElementAccess access = {kTaggedBase, OFFSET_OF_DATA_START(SeqOneByteString),
TypeCache::Get()->kUint8, MachineType::Uint8(),
kNoWriteBarrier};
return access;
@ -912,7 +912,7 @@ ElementAccess AccessBuilder::ForSeqOneByteStringCharacter() {
// static
ElementAccess AccessBuilder::ForSeqTwoByteStringCharacter() {
ElementAccess access = {kTaggedBase, offsetof(SeqTwoByteString, chars_),
ElementAccess access = {kTaggedBase, OFFSET_OF_DATA_START(SeqTwoByteString),
TypeCache::Get()->kUint16, MachineType::Uint16(),
kNoWriteBarrier};
return access;

@ -263,8 +263,8 @@ void MaglevAssembler::StringFromCharCode(RegisterSnapshot register_snapshot,
register_snapshot.live_registers.set(char_code);
__ AllocateTwoByteString(register_snapshot, string, 1);
__ and_(scratch, char_code, Operand(0xFFFF));
__ strh(scratch,
FieldMemOperand(string, offsetof(SeqTwoByteString, chars_)));
__ strh(scratch, FieldMemOperand(
string, OFFSET_OF_DATA_START(SeqTwoByteString)));
if (reallocate_result) {
__ Move(result, string);
}
@ -396,7 +396,7 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
// (CharCodeAt/CodePointAt), since it cannot be the first half of a
// surrogate pair.
add(index, index,
Operand(offsetof(SeqOneByteString, chars_) - kHeapObjectTag));
Operand(OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag));
ldrb(result, MemOperand(string, index));
b(result_fits_one_byte);
@ -405,7 +405,7 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
Register scratch = instance_type;
lsl(scratch, index, Operand(1));
add(scratch, scratch,
Operand(offsetof(SeqTwoByteString, chars_) - kHeapObjectTag));
Operand(OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag));
ldrh(result, MemOperand(string, scratch));
if (mode == BuiltinStringPrototypeCharCodeOrCodePointAt::kCodePointAt) {
@ -423,7 +423,7 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
Register second_code_point = scratch;
lsl(index, index, Operand(1));
add(index, index,
Operand(offsetof(SeqTwoByteString, chars_) - kHeapObjectTag));
Operand(OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag));
ldrh(second_code_point, MemOperand(string, index));
// {index} is not needed at this point.

@ -103,7 +103,7 @@ void BuiltinStringFromCharCode::GenerateCode(MaglevAssembler* masm,
__ AllocateTwoByteString(register_snapshot(), result_string, 1);
__ Move(scratch, char_code & 0xFFFF);
__ strh(scratch, FieldMemOperand(result_string,
offsetof(SeqTwoByteString, chars_)));
OFFSET_OF_DATA_START(SeqTwoByteString)));
if (reallocate_result) {
__ Move(ToRegister(result()), result_string);
}

@ -294,8 +294,9 @@ void MaglevAssembler::StringFromCharCode(RegisterSnapshot register_snapshot,
register_snapshot.live_registers.set(char_code);
__ AllocateTwoByteString(register_snapshot, string, 1);
__ And(scratch, char_code, Immediate(0xFFFF));
__ Strh(scratch.W(),
FieldMemOperand(string, offsetof(SeqTwoByteString, chars_)));
__ Strh(
scratch.W(),
FieldMemOperand(string, OFFSET_OF_DATA_START(SeqTwoByteString)));
if (reallocate_result) {
__ Move(result, string);
}
@ -427,7 +428,7 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
// The result of one-byte string will be the same for both modes
// (CharCodeAt/CodePointAt), since it cannot be the first half of a
// surrogate pair.
Add(index, index, offsetof(SeqOneByteString, chars_) - kHeapObjectTag);
Add(index, index, OFFSET_OF_DATA_START(SeqOneByteString) - kHeapObjectTag);
Ldrb(result, MemOperand(string, index));
B(result_fits_one_byte);
@ -435,7 +436,8 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
// {instance_type} is unused from this point, so we can use as scratch.
Register scratch = instance_type;
Lsl(scratch, index, 1);
Add(scratch, scratch, offsetof(SeqTwoByteString, chars_) - kHeapObjectTag);
Add(scratch, scratch,
OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag);
Ldrh(result, MemOperand(string, scratch));
if (mode == BuiltinStringPrototypeCharCodeOrCodePointAt::kCodePointAt) {
@ -450,7 +452,8 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
Register second_code_point = scratch;
Lsl(index, index, 1);
Add(index, index, offsetof(SeqTwoByteString, chars_) - kHeapObjectTag);
Add(index, index,
OFFSET_OF_DATA_START(SeqTwoByteString) - kHeapObjectTag);
Ldrh(second_code_point, MemOperand(string, index));
// {index} is not needed at this point.

@ -107,8 +107,9 @@ void BuiltinStringFromCharCode::GenerateCode(MaglevAssembler* masm,
DCHECK(!scratch.Aliases(result_string));
__ AllocateTwoByteString(register_snapshot(), result_string, 1);
__ Move(scratch, char_code & 0xFFFF);
__ Strh(scratch.W(), FieldMemOperand(result_string,
offsetof(SeqTwoByteString, chars_)));
__ Strh(scratch.W(),
FieldMemOperand(result_string,
OFFSET_OF_DATA_START(SeqTwoByteString)));
if (reallocate_result) {
__ Move(ToRegister(result()), result_string);
}

@ -116,7 +116,7 @@ void MaglevAssembler::StringFromCharCode(RegisterSnapshot register_snapshot,
register_snapshot.live_registers.set(char_code);
__ AllocateTwoByteString(register_snapshot, result, 1);
__ andl(char_code, Immediate(0xFFFF));
__ movw(FieldOperand(result, offsetof(SeqTwoByteString, chars_)),
__ movw(FieldOperand(result, OFFSET_OF_DATA_START(SeqTwoByteString)),
char_code);
__ jmp(*done);
},
@ -241,11 +241,11 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
// (CharCodeAt/CodePointAt), since it cannot be the first half of a
// surrogate pair.
movzxbl(result, FieldOperand(string, index, times_1,
offsetof(SeqOneByteString, chars_)));
OFFSET_OF_DATA_START(SeqOneByteString)));
jmp(result_fits_one_byte);
bind(&two_byte_string);
movzxwl(result, FieldOperand(string, index, times_2,
offsetof(SeqTwoByteString, chars_)));
OFFSET_OF_DATA_START(SeqTwoByteString)));
if (mode == BuiltinStringPrototypeCharCodeOrCodePointAt::kCodePointAt) {
Register first_code_point = scratch;
@ -263,7 +263,7 @@ void MaglevAssembler::StringCharCodeOrCodePointAt(
Register second_code_point = scratch;
movzxwl(second_code_point,
FieldOperand(string, index, times_2,
offsetof(SeqTwoByteString, chars_)));
OFFSET_OF_DATA_START(SeqTwoByteString)));
// {index} is not needed at this point.
Register scratch2 = index;

@ -114,8 +114,9 @@ void BuiltinStringFromCharCode::GenerateCode(MaglevAssembler* masm,
__ LoadSingleCharacterString(result_string, char_code);
} else {
__ AllocateTwoByteString(register_snapshot(), result_string, 1);
__ movw(FieldOperand(result_string, offsetof(SeqTwoByteString, chars_)),
Immediate(char_code & 0xFFFF));
__ movw(
FieldOperand(result_string, OFFSET_OF_DATA_START(SeqTwoByteString)),
Immediate(char_code & 0xFFFF));
}
} else {
MaglevAssembler::ScratchRegisterScope temps(masm);

@ -1026,7 +1026,7 @@ uint8_t SeqOneByteString::Get(
int index, const SharedStringAccessGuardIfNeeded& access_guard) const {
USE(access_guard);
DCHECK(index >= 0 && index < length());
return chars_[index];
return chars()[index];
}
void SeqOneByteString::SeqOneByteStringSet(int index, uint16_t value) {
@ -1034,7 +1034,7 @@ void SeqOneByteString::SeqOneByteStringSet(int index, uint16_t value) {
DCHECK_GE(index, 0);
DCHECK_LT(index, length());
DCHECK_LE(value, kMaxOneByteCharCode);
chars_[index] = value;
chars()[index] = value;
}
void SeqOneByteString::SeqOneByteStringSetChars(int index,
@ -1043,18 +1043,18 @@ void SeqOneByteString::SeqOneByteStringSetChars(int index,
DisallowGarbageCollection no_gc;
DCHECK_LE(0, index);
DCHECK_LT(index + string_length, length());
void* address = static_cast<void*>(&chars_[index]);
void* address = static_cast<void*>(&chars()[index]);
memcpy(address, string, string_length);
}
Address SeqOneByteString::GetCharsAddress() const {
return reinterpret_cast<Address>(chars_);
return reinterpret_cast<Address>(&chars()[0]);
}
uint8_t* SeqOneByteString::GetChars(const DisallowGarbageCollection& no_gc) {
USE(no_gc);
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(this));
return chars_;
return chars();
}
uint8_t* SeqOneByteString::GetChars(
@ -1062,17 +1062,17 @@ uint8_t* SeqOneByteString::GetChars(
const SharedStringAccessGuardIfNeeded& access_guard) {
USE(no_gc);
USE(access_guard);
return chars_;
return chars();
}
Address SeqTwoByteString::GetCharsAddress() const {
return reinterpret_cast<Address>(chars_);
return reinterpret_cast<Address>(&chars()[0]);
}
base::uc16* SeqTwoByteString::GetChars(const DisallowGarbageCollection& no_gc) {
USE(no_gc);
DCHECK(!SharedStringAccessGuardIfNeeded::IsNeeded(this));
return chars_;
return chars();
}
base::uc16* SeqTwoByteString::GetChars(
@ -1080,20 +1080,20 @@ base::uc16* SeqTwoByteString::GetChars(
const SharedStringAccessGuardIfNeeded& access_guard) {
USE(no_gc);
USE(access_guard);
return chars_;
return chars();
}
uint16_t SeqTwoByteString::Get(
int index, const SharedStringAccessGuardIfNeeded& access_guard) const {
USE(access_guard);
DCHECK(index >= 0 && index < length());
return chars_[index];
return chars()[index];
}
void SeqTwoByteString::SeqTwoByteStringSet(int index, uint16_t value) {
DisallowGarbageCollection no_gc;
DCHECK(index >= 0 && index < length());
chars_[index] = value;
chars()[index] = value;
}
// static

@ -814,7 +814,7 @@ V8_OBJECT class SeqOneByteString : public SeqString {
friend class compiler::AccessBuilder;
friend class TorqueGeneratedSeqOneByteStringAsserts;
Char chars_[];
FLEXIBLE_ARRAY_MEMBER(Char, chars);
} V8_OBJECT_END;
template <>
@ -887,7 +887,7 @@ V8_OBJECT class SeqTwoByteString : public SeqString {
friend class compiler::AccessBuilder;
friend class TorqueGeneratedSeqTwoByteStringAsserts;
Char chars_[];
FLEXIBLE_ARRAY_MEMBER(Char, chars);
} V8_OBJECT_END;
template <>

@ -63,6 +63,46 @@ class UnalignedDoubleMember {
static_assert(alignof(UnalignedDoubleMember) == alignof(Tagged_t));
static_assert(sizeof(UnalignedDoubleMember) == sizeof(double));
// FLEXIBLE_ARRAY_MEMBER(T, name) represents a marker for a variable-sized
// suffix of members for a type.
//
// It behaves as if it were the last member of a class, and creates an accessor
// for `T* name()`.
//
// This macro is used instead of the C99 flexible array member syntax, because
//
// a) That syntax is only in C++ as an extension,
// b) On all our major compilers, it doesn't allow the class to have
// subclasses (which means it doesn't work for e.g. TaggedArrayBase or
// BigIntBase),
// c) The similar zero-length array extension _also_ doesn't allow subclasses
// on some compilers (specifically, MSVC).
#define FLEXIBLE_ARRAY_MEMBER(Type, name) \
/* Some typedefs so that error messages are a bit more transparent */ \
using Only_one_FLEXIBLE_ARRAY_MEMBER_allowed_per_class = void; \
using OFFSET_OF_DATA_START_needs_class_with_FLEXIBLE_ARRAY_MEMBER = void; \
\
Type* name() { \
static_assert(alignof(Type) <= alignof(decltype(*this))); \
return reinterpret_cast<Type*>(this + 1); \
} \
const Type* name() const { \
static_assert(alignof(Type) <= alignof(decltype(*this))); \
return reinterpret_cast<const Type*>(this + 1); \
} \
using FlexibleDataType = Type
// OFFSET_OF_DATA_START(T) returns the offset of the FLEXIBLE_ARRAY_MEMBER of
// the class T.
//
// It forces an access of a dummy typedef in the class to make sure that it is
// only used on classes with a FLEXIBLE_ARRAY_MEMBER.
#define OFFSET_OF_DATA_START(Type) \
(static_cast< \
typename Type:: \
OFFSET_OF_DATA_START_needs_class_with_FLEXIBLE_ARRAY_MEMBER>(0), \
sizeof(Type))
// This helper static class represents a tagged field of type T at offset
// kFieldOffset inside some host HeapObject.
// For full-pointer mode this type adds no overhead but when pointer

@ -4346,8 +4346,12 @@ void CppClassGenerator::GenerateCppObjectLayoutDefinitionAsserts() {
for (auto f : type_->fields()) {
std::string field_offset =
"k" + CamelifyString(f.name_and_type.name) + "Offset";
impl_ << " static_assert(" << field_offset
<< " == offsetof(" + name_ + ", " << f.name_and_type.name << "_),\n"
std::string cpp_field_offset =
f.index.has_value()
? "OFFSET_OF_DATA_START(" + name_ + ")"
: "offsetof(" + name_ + ", " + f.name_and_type.name + "_)";
impl_ << " static_assert(" << field_offset << " == " << cpp_field_offset
<< ",\n"
<< " \"Value of " << name_ << "::" << field_offset
<< " defined in Torque and offset of field " << name_
<< "::" << f.name_and_type.name << " in C++ do not match\");\n";

@ -523,8 +523,8 @@ extras_accessors = [
'Oddball, kind, int, offsetof(Oddball, kind_)',
'HeapNumber, value, double, kValueOffset',
'ExternalString, resource, Object, offsetof(ExternalString, resource_)',
'SeqOneByteString, chars, char, offsetof(SeqOneByteString, chars_)',
'SeqTwoByteString, chars, char, offsetof(SeqTwoByteString, chars_)',
'SeqOneByteString, chars, char, OFFSET_OF_DATA_START(SeqOneByteString)',
'SeqTwoByteString, chars, char, OFFSET_OF_DATA_START(SeqTwoByteString)',
'UncompiledData, inferred_name, String, kInferredNameOffset',
'UncompiledData, start_position, int32_t, kStartPositionOffset',
'UncompiledData, end_position, int32_t, kEndPositionOffset',
@ -844,7 +844,8 @@ def parse_field(call):
offset = args[2];
dtype = 'SMI'
if offset.startswith("offsetof("):
if offset.startswith("offsetof(") or offset.startswith(
"OFFSET_OF_DATA_START("):
offsetof_fields.append((klass, field, offset))
value = 'OffsetsForDebug::%s_%s' % (klass, field)
else: