[code] Move the unwinding info into metadata area

Semantically, the unwinding info is a variable-size metadata table
with untagged (i.e. no relocation needed) contents, packed inside Code
objects. This is just like other metadata tables (safepoint table,
handler table, constant pool, code comments); but for historical
reasons it's been treated differently so far. Unlike these other
tables, the unwinding info was located *after* InstructionEnd, and its
size was written to the first 8 bytes after InstructionEnd.

This CL makes unwinding info handling more consistent with other
metadata tables by writing its offset into a dedicated
kUnwindingInfoOffsetOffset header slot, and by moving the actual data
inside the [InstructionStart,InstructionEnd[ area. In follow-up CLs,
this area will be split into dedicated instruction- and metadata
areas.

A picture is worth 1000 words, before:

 +--------------------------+  <-- raw_instruction_start()
 |       instructions       |
 |           ...            |
 +--------------------------+
 |     embedded metadata    |  <-- safepoint_table_offset()
 |           ...            |  <-- handler_table_offset()
 |                          |  <-- constant_pool_offset()
 |                          |  <-- code_comments_offset()
 |    padding to the next   |
 |  8-byte aligned address  |
 +--------------------------+  <-- raw_instruction_end()
 |   [unwinding_info_size]  |
 |        as uint64_t       |
 +--------------------------+  <-- unwinding_info_start()
 |       unwinding info     |
 |            ...           |
 +--------------------------+  <-- unwinding_info_end()

After:

 +--------------------------+  <-- raw_instruction_start()
 |       instructions       |
 |           ...            |
 +--------------------------+
 |     embedded metadata    |  <-- safepoint_table_offset()
 |           ...            |  <-- handler_table_offset()
 |                          |  <-- constant_pool_offset()
 |                          |  <-- code_comments_offset()
 |                          |  <-- unwinding_info_offset()
 |                          |
 +--------------------------+  <-- raw_instruction_end()

Bug: v8:11036
Change-Id: I649708821acc5365186ca2c9cff2669fc3e91fd3
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2484795
Reviewed-by: Dominik Inführ <dinfuehr@chromium.org>
Reviewed-by: Leszek Swirski <leszeks@chromium.org>
Commit-Queue: Jakob Gruber <jgruber@chromium.org>
Cr-Commit-Position: refs/heads/master@{#70640}
This commit is contained in:
Jakob Gruber 2020-10-19 15:12:27 +02:00 committed by Commit Bot
parent fe1c9190f4
commit c5379162dc
7 changed files with 110 additions and 151 deletions

View File

@ -16,11 +16,11 @@ namespace internal {
// the buffer and grows backward. Inlined metadata sections may exist
// at the end of the instructions.
//
// │<--------------- buffer_size ----------------------------------->│
// │<---------------- instr_size ------------->│ │<-reloc_size->│
// ├───────────────────────────────────────────┼──────┼──────────────┤
// │ instructions │ data │ free │ reloc info │
// ├───────────────────────────────────────────┴──────┴──────────────┘
// |<--------------- buffer_size ----------------------------------->|
// |<---------------- instr_size ------------->| |<-reloc_size->|
// |--------------+----------------------------+------+--------------|
// | instructions | data | free | reloc info |
// +--------------+----------------------------+------+--------------+
// TODO(jgruber): Add a single chokepoint for specifying the instruction area
// layout (i.e. the order of inlined metadata fields).
@ -73,6 +73,11 @@ class CodeDesc {
byte* unwinding_info = nullptr;
int unwinding_info_size = 0;
int unwinding_info_offset() const {
// TODO(jgruber,v8:11036): Remove this function once unwinding_info setup
// is more consistent with other metadata tables.
return code_comments_offset + code_comments_size;
}
Assembler* origin = nullptr;
};

View File

@ -390,9 +390,8 @@ size_t Isolate::HashIsolateForEmbeddedBlob() {
// trampolines. Other data fields must remain the same.
STATIC_ASSERT(Code::kInstructionSizeOffset == Code::kDataStart);
STATIC_ASSERT(Code::kFlagsOffset == Code::kInstructionSizeOffsetEnd + 1);
STATIC_ASSERT(Code::kSafepointTableOffsetOffset ==
Code::kFlagsOffsetEnd + 1);
static constexpr int kStartOffset = Code::kSafepointTableOffsetOffset;
STATIC_ASSERT(Code::kBuiltinIndexOffset == Code::kFlagsOffsetEnd + 1);
static constexpr int kStartOffset = Code::kBuiltinIndexOffset;
for (int j = kStartOffset; j < Code::kUnalignedHeaderSize; j++) {
hash = base::hash_combine(hash, size_t{code_ptr[j]});

View File

@ -70,16 +70,9 @@ namespace internal {
namespace {
int ComputeCodeObjectSize(const CodeDesc& desc) {
bool has_unwinding_info = desc.unwinding_info != nullptr;
DCHECK((has_unwinding_info && desc.unwinding_info_size > 0) ||
(!has_unwinding_info && desc.unwinding_info_size == 0));
int body_size = desc.instr_size;
int unwinding_info_size_field_size = kInt64Size;
if (has_unwinding_info) {
body_size = RoundUp(body_size, kInt64Size) + desc.unwinding_info_size +
unwinding_info_size_field_size;
}
int object_size = Code::SizeFor(RoundUp(body_size, kObjectAlignment));
// TODO(jgruber,v8:11036): Distinguish instruction and metadata areas.
int object_size = Code::SizeFor(
Code::AlignedBodySizeFor(desc.instr_size + desc.unwinding_info_size));
DCHECK(IsAligned(static_cast<intptr_t>(object_size), kCodeAlignment));
return object_size;
}
@ -172,12 +165,13 @@ MaybeHandle<Code> Factory::CodeBuilder::BuildInternal(
}
constexpr bool kIsNotOffHeapTrampoline = false;
const bool has_unwinding_info = code_desc_.unwinding_info != nullptr;
code->set_raw_instruction_size(code_desc_.instr_size);
// TODO(jgruber,v8:11036): Distinguish instruction and metadata areas.
code->set_raw_instruction_size(code_desc_.instr_size +
code_desc_.unwinding_info_size);
code->set_relocation_info(*reloc_info);
code->initialize_flags(kind_, has_unwinding_info, is_turbofanned_,
stack_slots_, kIsNotOffHeapTrampoline);
code->initialize_flags(kind_, is_turbofanned_, stack_slots_,
kIsNotOffHeapTrampoline);
code->set_builtin_index(builtin_index_);
code->set_inlined_bytecode_size(inlined_bytecode_size_);
code->set_code_data_container(*data_container, kReleaseStore);
@ -187,6 +181,7 @@ MaybeHandle<Code> Factory::CodeBuilder::BuildInternal(
code->set_handler_table_offset(code_desc_.handler_table_offset);
code->set_constant_pool_offset(code_desc_.constant_pool_offset);
code->set_code_comments_offset(code_desc_.code_comments_offset);
code->set_unwinding_info_offset(code_desc_.unwinding_info_offset());
// Allow self references to created code object by patching the handle to
// point to the newly allocated Code object.
@ -2083,14 +2078,14 @@ Handle<Code> Factory::NewOffHeapTrampolineFor(Handle<Code> code,
const bool set_is_off_heap_trampoline = true;
const int stack_slots =
code->has_safepoint_info() ? code->stack_slots() : 0;
result->initialize_flags(code->kind(), code->has_unwinding_info(),
code->is_turbofanned(), stack_slots,
result->initialize_flags(code->kind(), code->is_turbofanned(), stack_slots,
set_is_off_heap_trampoline);
result->set_builtin_index(code->builtin_index());
result->set_safepoint_table_offset(code->safepoint_table_offset());
result->set_handler_table_offset(code->handler_table_offset());
result->set_constant_pool_offset(code->constant_pool_offset());
result->set_code_comments_offset(code->code_comments_offset());
result->set_unwinding_info_offset(code->unwinding_info_offset());
// Replace the newly generated trampoline's RelocInfo ByteArray with the
// canonical one stored in the roots to avoid duplicating it for every

View File

@ -174,6 +174,7 @@ INT_ACCESSORS(Code, raw_instruction_size, kInstructionSizeOffset)
INT_ACCESSORS(Code, safepoint_table_offset, kSafepointTableOffsetOffset)
INT_ACCESSORS(Code, handler_table_offset, kHandlerTableOffsetOffset)
INT_ACCESSORS(Code, code_comments_offset, kCodeCommentsOffsetOffset)
INT32_ACCESSORS(Code, unwinding_info_offset, kUnwindingInfoOffsetOffset)
#define CODE_ACCESSORS(name, type, offset) \
ACCESSORS_CHECKED2(Code, name, type, offset, true, \
!ObjectInYoungGeneration(value))
@ -198,14 +199,18 @@ void Code::WipeOutHeader() {
}
void Code::clear_padding() {
// Clear the padding between the header and `raw_instruction_start`.
if (FIELD_SIZE(kOptionalPaddingOffset) != 0) {
memset(reinterpret_cast<void*>(address() + kOptionalPaddingOffset), 0,
FIELD_SIZE(kOptionalPaddingOffset));
}
Address data_end =
has_unwinding_info() ? unwinding_info_end() : raw_instruction_end();
memset(reinterpret_cast<void*>(data_end), 0,
CodeSize() - (data_end - address()));
// Clear the padding after `raw_instruction_end`.
// TODO(jgruber,v8:11036): Distinguish instruction and metadata areas.
DCHECK_EQ(unwinding_info_offset() + unwinding_info_size(), InstructionSize());
size_t trailing_padding_size = body_size() - raw_instruction_size();
memset(reinterpret_cast<void*>(raw_instruction_end()), 0,
trailing_padding_size);
}
ByteArray Code::SourcePositionTable() const {
@ -247,37 +252,10 @@ Address Code::InstructionEnd() const {
return raw_instruction_end();
}
int Code::GetUnwindingInfoSizeOffset() const {
DCHECK(has_unwinding_info());
return RoundUp(kHeaderSize + raw_instruction_size(), kInt64Size);
}
int Code::unwinding_info_size() const {
DCHECK(has_unwinding_info());
return static_cast<int>(ReadField<uint64_t>(GetUnwindingInfoSizeOffset()));
}
void Code::set_unwinding_info_size(int value) {
DCHECK(has_unwinding_info());
WriteField<uint64_t>(GetUnwindingInfoSizeOffset(), value);
}
Address Code::unwinding_info_start() const {
DCHECK(has_unwinding_info());
return FIELD_ADDR(*this, GetUnwindingInfoSizeOffset()) + kInt64Size;
}
Address Code::unwinding_info_end() const {
DCHECK(has_unwinding_info());
return unwinding_info_start() + unwinding_info_size();
}
int Code::body_size() const {
int unpadded_body_size =
has_unwinding_info()
? static_cast<int>(unwinding_info_end() - raw_instruction_start())
: raw_instruction_size();
return RoundUp(unpadded_body_size, kObjectAlignment);
// TODO(jgruber,v8:11036): Distinguish instruction and metadata areas.
DCHECK_EQ(unwinding_info_offset() + unwinding_info_size(), InstructionSize());
return AlignedBodySizeFor(raw_instruction_size());
}
int Code::SizeIncludingMetadata() const {
@ -319,6 +297,10 @@ bool Code::contains(Address inner_pointer) {
int Code::ExecutableSize() const {
// Check that the assumptions about the layout of the code object holds.
// TODO(jgruber,v8:11036): It's unclear what this function should return.
// Currently, it counts the header, instructions, and metadata tables as
// 'executable'. See also ExecutableInstructionSize which counts only
// instructions.
DCHECK_EQ(static_cast<int>(raw_instruction_start() - address()),
Code::kHeaderSize);
return raw_instruction_size() + Code::kHeaderSize;
@ -339,13 +321,11 @@ CodeKind Code::kind() const {
return KindField::decode(ReadField<uint32_t>(kFlagsOffset));
}
void Code::initialize_flags(CodeKind kind, bool has_unwinding_info,
bool is_turbofanned, int stack_slots,
void Code::initialize_flags(CodeKind kind, bool is_turbofanned, int stack_slots,
bool is_off_heap_trampoline) {
CHECK(0 <= stack_slots && stack_slots < StackSlotsField::kMax);
DCHECK(!CodeKindIsInterpretedJSFunction(kind));
uint32_t flags = HasUnwindingInfoField::encode(has_unwinding_info) |
KindField::encode(kind) |
uint32_t flags = KindField::encode(kind) |
IsTurbofannedField::encode(is_turbofanned) |
StackSlotsField::encode(stack_slots) |
IsOffHeapTrampoline::encode(is_off_heap_trampoline);
@ -378,10 +358,6 @@ inline bool Code::has_tagged_params() const {
kind() != CodeKind::C_WASM_ENTRY && kind() != CodeKind::WASM_FUNCTION;
}
inline bool Code::has_unwinding_info() const {
return HasUnwindingInfoField::decode(ReadField<uint32_t>(kFlagsOffset));
}
inline bool Code::is_turbofanned() const {
return IsTurbofannedField::decode(ReadField<uint32_t>(kFlagsOffset));
}
@ -562,6 +538,19 @@ Address Code::code_comments() const {
return InstructionStart() + code_comments_offset();
}
Address Code::unwinding_info_start() const {
return InstructionStart() + unwinding_info_offset();
}
Address Code::unwinding_info_end() const { return InstructionEnd(); }
int Code::unwinding_info_size() const {
DCHECK_GE(unwinding_info_end(), unwinding_info_start());
return static_cast<int>(unwinding_info_end() - unwinding_info_start());
}
bool Code::has_unwinding_info() const { return unwinding_info_size() > 0; }
Code Code::GetCodeFromTargetAddress(Address address) {
{
// TODO(jgruber,v8:6666): Support embedded builtins here. We'd need to pass

View File

@ -62,8 +62,8 @@ int Code::constant_pool_size() const {
bool Code::has_constant_pool() const { return constant_pool_size() > 0; }
int Code::code_comments_size() const {
DCHECK_GE(InstructionSize() - code_comments_offset(), 0);
return InstructionSize() - code_comments_offset();
DCHECK_GE(unwinding_info_offset() - code_comments_offset(), 0);
return unwinding_info_offset() - code_comments_offset();
}
bool Code::has_code_comments() const { return code_comments_size() > 0; }
@ -88,22 +88,19 @@ void Code::Relocate(intptr_t delta) {
}
void Code::FlushICache() const {
// TODO(jgruber,v8:11036): This should likely flush only actual instructions,
// not metadata.
FlushInstructionCache(raw_instruction_start(), raw_instruction_size());
}
void Code::CopyFromNoFlush(Heap* heap, const CodeDesc& desc) {
// Copy code.
// TODO(jgruber,v8:11036): Distinguish instruction and metadata areas.
CopyBytes(reinterpret_cast<byte*>(raw_instruction_start()), desc.buffer,
static_cast<size_t>(desc.instr_size));
// Copy unwinding info, if any.
if (desc.unwinding_info) {
DCHECK_GT(desc.unwinding_info_size, 0);
set_unwinding_info_size(desc.unwinding_info_size);
CopyBytes(reinterpret_cast<byte*>(unwinding_info_start()),
desc.unwinding_info,
static_cast<size_t>(desc.unwinding_info_size));
}
// TODO(jgruber,v8:11036): Merge with the above.
CopyBytes(reinterpret_cast<byte*>(raw_instruction_start() + desc.instr_size),
desc.unwinding_info, static_cast<size_t>(desc.unwinding_info_size));
// Copy reloc info.
CopyRelocInfoToByteArray(unchecked_relocation_info(), desc);

View File

@ -168,6 +168,20 @@ class Code : public HeapObject {
// reserved in the code prologue.
inline int stack_slots() const;
// The body of all Code objects has the following layout.
//
// +--------------------------+ <-- raw_instruction_start()
// | instructions |
// | ... |
// +--------------------------+
// | embedded metadata | <-- safepoint_table_offset()
// | ... | <-- handler_table_offset()
// | | <-- constant_pool_offset()
// | | <-- code_comments_offset()
// | | <-- unwinding_info_offset()
// | |
// +--------------------------+ <-- raw_instruction_end()
// [safepoint_table_offset]: If {has_safepoint_info()}, the offset in the
// instruction stream where the safepoint table starts.
inline int safepoint_table_offset() const;
@ -198,6 +212,14 @@ class Code : public HeapObject {
V8_EXPORT_PRIVATE int code_comments_size() const;
V8_EXPORT_PRIVATE bool has_code_comments() const;
// [unwinding_info_offset]: Offset of the unwinding info section.
inline int32_t unwinding_info_offset() const;
inline void set_unwinding_info_offset(int32_t offset);
inline Address unwinding_info_start() const;
inline Address unwinding_info_end() const;
inline int unwinding_info_size() const;
inline bool has_unwinding_info() const;
// The size of the executable instruction area, without embedded metadata.
int ExecutableInstructionSize() const;
@ -261,9 +283,8 @@ class Code : public HeapObject {
inline void clear_padding();
// Initialize the flags field. Similar to clear_padding above this ensure that
// the snapshot content is deterministic.
inline void initialize_flags(CodeKind kind, bool has_unwinding_info,
bool is_turbofanned, int stack_slots,
bool is_off_heap_trampoline);
inline void initialize_flags(CodeKind kind, bool is_turbofanned,
int stack_slots, bool is_off_heap_trampoline);
// Convert a target address into a code object.
static inline Code GetCodeFromTargetAddress(Address address);
@ -289,12 +310,14 @@ class Code : public HeapObject {
inline Address InstructionEnd() const;
V8_EXPORT_PRIVATE Address OffHeapInstructionEnd() const;
// Returns the size of the instructions, padding, relocation and unwinding
// information.
// Returns the (padded) body size, including instructions and metadata.
inline int body_size() const;
static int AlignedBodySizeFor(int unaligned_body_size) {
return RoundUp(unaligned_body_size, kObjectAlignment);
}
// Returns the size of code and its metadata. This includes the size of code
// relocation information, deoptimization data and handler table.
// relocation information, deoptimization data.
inline int SizeIncludingMetadata() const;
// Returns the address of the first relocation info (read backwards!).
@ -303,51 +326,6 @@ class Code : public HeapObject {
// Returns the address right after the relocation info (read backwards!).
inline byte* relocation_end() const;
// The body of all code objects has the following layout.
//
// +--------------------------+ <-- raw_instruction_start()
// | instructions |
// | ... |
// +--------------------------+
// | embedded metadata | <-- safepoint_table_offset()
// | ... | <-- handler_table_offset()
// | | <-- constant_pool_offset()
// | | <-- code_comments_offset()
// | |
// +--------------------------+ <-- raw_instruction_end()
//
// If has_unwinding_info() is false, raw_instruction_end() points to the first
// memory location after the end of the code object. Otherwise, the body
// continues as follows:
//
// +--------------------------+
// | padding to the next |
// | 8-byte aligned address |
// +--------------------------+ <-- raw_instruction_end()
// | [unwinding_info_size] |
// | as uint64_t |
// +--------------------------+ <-- unwinding_info_start()
// | unwinding info |
// | ... |
// +--------------------------+ <-- unwinding_info_end()
//
// and unwinding_info_end() points to the first memory location after the end
// of the code object.
// [has_unwinding_info]: Whether this code object has unwinding information.
// If it doesn't, unwinding_information_start() will point to invalid data.
inline bool has_unwinding_info() const;
// [unwinding_info_size]: Size of the unwinding information.
inline int unwinding_info_size() const;
inline void set_unwinding_info_size(int value);
// Returns the address of the unwinding information, if any.
inline Address unwinding_info_start() const;
// Returns the address right after the end of the unwinding information.
inline Address unwinding_info_end() const;
// Code entry point.
inline Address entry() const;
@ -424,13 +402,15 @@ class Code : public HeapObject {
V(kDataStart, 0) \
V(kInstructionSizeOffset, kIntSize) \
V(kFlagsOffset, kInt32Size) \
V(kBuiltinIndexOffset, kIntSize) \
V(kInlinedBytecodeSizeOffset, kIntSize) \
/* Offsets describing inline metadata tables. */ \
V(kSafepointTableOffsetOffset, kIntSize) \
V(kHandlerTableOffsetOffset, kIntSize) \
V(kConstantPoolOffsetOffset, \
FLAG_enable_embedded_constant_pool ? kIntSize : 0) \
V(kCodeCommentsOffsetOffset, kIntSize) \
V(kBuiltinIndexOffset, kIntSize) \
V(kInlinedBytecodeSizeOffset, kIntSize) \
V(kUnwindingInfoOffsetOffset, kInt32Size) \
V(kUnalignedHeaderSize, 0) \
/* Add padding to align the instruction start following right after */ \
/* the Code object header. */ \
@ -443,35 +423,32 @@ class Code : public HeapObject {
// This documents the amount of free space we have in each Code object header
// due to padding for code alignment.
#if V8_TARGET_ARCH_ARM64
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 16 : 28;
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 12 : 24;
#elif V8_TARGET_ARCH_MIPS64
static constexpr int kHeaderPaddingSize = 28;
static constexpr int kHeaderPaddingSize = 24;
#elif V8_TARGET_ARCH_X64
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 16 : 28;
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 12 : 24;
#elif V8_TARGET_ARCH_ARM
static constexpr int kHeaderPaddingSize = 16;
static constexpr int kHeaderPaddingSize = 12;
#elif V8_TARGET_ARCH_IA32
static constexpr int kHeaderPaddingSize = 16;
static constexpr int kHeaderPaddingSize = 12;
#elif V8_TARGET_ARCH_MIPS
static constexpr int kHeaderPaddingSize = 16;
static constexpr int kHeaderPaddingSize = 12;
#elif V8_TARGET_ARCH_PPC64
static constexpr int kHeaderPaddingSize =
FLAG_enable_embedded_constant_pool ? (COMPRESS_POINTERS_BOOL ? 12 : 24)
: (COMPRESS_POINTERS_BOOL ? 16 : 28);
FLAG_enable_embedded_constant_pool ? (COMPRESS_POINTERS_BOOL ? 8 : 20)
: (COMPRESS_POINTERS_BOOL ? 12 : 24);
#elif V8_TARGET_ARCH_S390X
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 16 : 28;
static constexpr int kHeaderPaddingSize = COMPRESS_POINTERS_BOOL ? 12 : 24;
#else
#error Unknown architecture.
#endif
STATIC_ASSERT(FIELD_SIZE(kOptionalPaddingOffset) == kHeaderPaddingSize);
inline int GetUnwindingInfoSizeOffset() const;
class BodyDescriptor;
// Flags layout. base::BitField<type, shift, size>.
#define CODE_FLAGS_BIT_FIELDS(V, _) \
V(HasUnwindingInfoField, bool, 1, _) \
V(KindField, CodeKind, 4, _) \
V(IsTurbofannedField, bool, 1, _) \
V(StackSlotsField, int, 24, _) \
@ -479,7 +456,7 @@ class Code : public HeapObject {
DEFINE_BIT_FIELDS(CODE_FLAGS_BIT_FIELDS)
#undef CODE_FLAGS_BIT_FIELDS
STATIC_ASSERT(kCodeKindCount <= KindField::kNumValues);
STATIC_ASSERT(CODE_FLAGS_BIT_FIELDS_Ranges::kBitsCount == 31);
STATIC_ASSERT(CODE_FLAGS_BIT_FIELDS_Ranges::kBitsCount == 30);
STATIC_ASSERT(CODE_FLAGS_BIT_FIELDS_Ranges::kBitsCount <=
FIELD_SIZE(kFlagsOffset) * kBitsPerByte);

View File

@ -47,8 +47,8 @@ TEST(CodeLayoutWithoutUnwindingInfo) {
CHECK_EQ(code->raw_instruction_size(), buffer_size);
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(code->raw_instruction_start()),
buffer, buffer_size));
CHECK_EQ(code->raw_instruction_end() - code->address(),
Code::kHeaderSize + buffer_size);
CHECK_EQ(code->raw_instruction_end() - code->raw_instruction_start(),
buffer_size);
}
TEST(CodeLayoutWithUnwindingInfo) {
@ -91,18 +91,15 @@ TEST(CodeLayoutWithUnwindingInfo) {
.Build();
CHECK(code->has_unwinding_info());
CHECK_EQ(code->raw_instruction_size(), buffer_size);
CHECK_EQ(code->raw_instruction_size(), buffer_size + unwinding_info_size);
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(code->raw_instruction_start()),
buffer, buffer_size));
CHECK(IsAligned(code->GetUnwindingInfoSizeOffset(), 8));
CHECK_EQ(code->unwinding_info_size(), unwinding_info_size);
CHECK(IsAligned(code->unwinding_info_start(), 8));
CHECK_EQ(memcmp(reinterpret_cast<void*>(code->unwinding_info_start()),
unwinding_info, unwinding_info_size),
0);
CHECK_EQ(code->unwinding_info_end() - code->address(),
Code::kHeaderSize + RoundUp(buffer_size, kInt64Size) + kInt64Size +
unwinding_info_size);
CHECK_EQ(code->unwinding_info_end() - code->raw_instruction_start(),
buffer_size + unwinding_info_size);
}
} // namespace internal