Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

View File

@ -0,0 +1,175 @@
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/codegen/aligned-slot-allocator.h"
#include "src/base/bits.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
class AlignedSlotAllocatorUnitTest : public ::testing::Test {
public:
AlignedSlotAllocatorUnitTest() = default;
~AlignedSlotAllocatorUnitTest() override = default;
// Helper method to test AlignedSlotAllocator::Allocate.
void Allocate(int size, int expected) {
int next = allocator_.NextSlot(size);
int result = allocator_.Allocate(size);
EXPECT_EQ(next, result); // NextSlot/Allocate are consistent.
EXPECT_EQ(expected, result);
EXPECT_EQ(0, result & (size - 1)); // result is aligned to size.
int slot_end = result + static_cast<int>(base::bits::RoundUpToPowerOfTwo32(
static_cast<uint32_t>(size)));
EXPECT_LE(slot_end, allocator_.Size()); // allocator Size is beyond slot.
}
// Helper method to test AlignedSlotAllocator::AllocateUnaligned.
void AllocateUnaligned(int size, int expected, int expected1, int expected2,
int expected4) {
int size_before = allocator_.Size();
int result = allocator_.AllocateUnaligned(size);
EXPECT_EQ(size_before, result); // AllocateUnaligned/Size are consistent.
EXPECT_EQ(expected, result);
EXPECT_EQ(result + size, allocator_.Size());
EXPECT_EQ(expected1, allocator_.NextSlot(1));
EXPECT_EQ(expected2, allocator_.NextSlot(2));
EXPECT_EQ(expected4, allocator_.NextSlot(4));
}
AlignedSlotAllocator allocator_;
};
TEST_F(AlignedSlotAllocatorUnitTest, NumSlotsForWidth) {
constexpr int kSlotBytes = AlignedSlotAllocator::kSlotSize;
for (int slot_size = 1; slot_size <= 4 * kSlotBytes; ++slot_size) {
EXPECT_EQ(AlignedSlotAllocator::NumSlotsForWidth(slot_size),
(slot_size + kSlotBytes - 1) / kSlotBytes);
}
}
TEST_F(AlignedSlotAllocatorUnitTest, Allocate1) {
Allocate(1, 0);
EXPECT_EQ(2, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 1);
EXPECT_EQ(2, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 2);
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(1, 3);
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
// Make sure we use 1-fragments.
Allocate(1, 4);
Allocate(2, 6);
Allocate(1, 5);
// Make sure we use 2-fragments.
Allocate(2, 8);
Allocate(1, 10);
Allocate(1, 11);
}
TEST_F(AlignedSlotAllocatorUnitTest, Allocate2) {
Allocate(2, 0);
EXPECT_EQ(2, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(4));
Allocate(2, 2);
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(4));
// Make sure we use 2-fragments.
Allocate(1, 4);
Allocate(2, 6);
Allocate(2, 8);
}
TEST_F(AlignedSlotAllocatorUnitTest, Allocate4) {
Allocate(4, 0);
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(2));
Allocate(1, 4);
Allocate(4, 8);
Allocate(2, 6);
Allocate(4, 12);
}
TEST_F(AlignedSlotAllocatorUnitTest, AllocateUnaligned) {
AllocateUnaligned(1, 0, 1, 2, 4);
AllocateUnaligned(1, 1, 2, 2, 4);
Allocate(1, 2);
AllocateUnaligned(2, 3, 5, 6, 8);
// Advance to leave 1- and 2- fragments below Size.
Allocate(4, 8);
// AllocateUnaligned should allocate at the end, and clear fragments.
AllocateUnaligned(0, 12, 12, 12, 12);
}
TEST_F(AlignedSlotAllocatorUnitTest, LargeAllocateUnaligned) {
AllocateUnaligned(11, 0, 11, 12, 12);
AllocateUnaligned(11, 11, 22, 22, 24);
AllocateUnaligned(13, 22, 35, 36, 36);
}
TEST_F(AlignedSlotAllocatorUnitTest, Size) {
allocator_.Allocate(1);
EXPECT_EQ(1, allocator_.Size());
// Allocate 2, leaving a fragment at 1. Size should be at 4.
allocator_.Allocate(2);
EXPECT_EQ(4, allocator_.Size());
// Allocate should consume fragment.
EXPECT_EQ(1, allocator_.Allocate(1));
// Size should still be 4.
EXPECT_EQ(4, allocator_.Size());
}
TEST_F(AlignedSlotAllocatorUnitTest, Align) {
EXPECT_EQ(0, allocator_.Align(1));
EXPECT_EQ(0, allocator_.Size());
// Allocate 1 to become misaligned.
Allocate(1, 0);
// 4-align.
EXPECT_EQ(3, allocator_.Align(4));
EXPECT_EQ(4, allocator_.NextSlot(1));
EXPECT_EQ(4, allocator_.NextSlot(2));
EXPECT_EQ(4, allocator_.NextSlot(4));
EXPECT_EQ(4, allocator_.Size());
// Allocate 2 to become misaligned.
Allocate(2, 4);
// 4-align.
EXPECT_EQ(2, allocator_.Align(4));
EXPECT_EQ(8, allocator_.NextSlot(1));
EXPECT_EQ(8, allocator_.NextSlot(2));
EXPECT_EQ(8, allocator_.NextSlot(4));
EXPECT_EQ(8, allocator_.Size());
// No change when we're already aligned.
EXPECT_EQ(0, allocator_.Align(2));
EXPECT_EQ(8, allocator_.NextSlot(1));
EXPECT_EQ(8, allocator_.NextSlot(2));
EXPECT_EQ(8, allocator_.NextSlot(4));
EXPECT_EQ(8, allocator_.Size());
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,114 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/execution/isolate.h"
#include "src/heap/factory.h"
#include "src/objects/objects-inl.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using CodeLayoutTest = TestWithContext;
namespace internal {
TEST_F(CodeLayoutTest, CodeLayoutWithoutUnwindingInfo) {
HandleScope handle_scope(i_isolate());
// "Hello, World!" in ASCII, padded to kCodeAlignment.
uint8_t buffer_array[16] = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57,
0x6F, 0x72, 0x6C, 0x64, 0x21, 0xcc, 0xcc, 0xcc};
uint8_t* buffer = &buffer_array[0];
int buffer_size = sizeof(buffer_array);
CodeDesc code_desc;
code_desc.buffer = buffer;
code_desc.buffer_size = buffer_size;
code_desc.instr_size = buffer_size;
code_desc.safepoint_table_offset = buffer_size;
code_desc.safepoint_table_size = 0;
code_desc.handler_table_offset = buffer_size;
code_desc.handler_table_size = 0;
code_desc.constant_pool_offset = buffer_size;
code_desc.constant_pool_size = 0;
code_desc.builtin_jump_table_info_offset = buffer_size;
code_desc.builtin_jump_table_info_size = 0;
code_desc.code_comments_offset = buffer_size;
code_desc.code_comments_size = 0;
code_desc.reloc_offset = buffer_size;
code_desc.reloc_size = 0;
code_desc.unwinding_info = nullptr;
code_desc.unwinding_info_size = 0;
code_desc.origin = nullptr;
DirectHandle<Code> code =
Factory::CodeBuilder(i_isolate(), code_desc, CodeKind::FOR_TESTING)
.Build();
CHECK(!code->has_unwinding_info());
CHECK_EQ(code->instruction_size(), buffer_size);
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(code->instruction_start()), buffer,
buffer_size));
CHECK_EQ(
static_cast<int>(code->instruction_end() - code->instruction_start()),
buffer_size);
}
TEST_F(CodeLayoutTest, CodeLayoutWithUnwindingInfo) {
HandleScope handle_scope(i_isolate());
// "Hello, World!" in ASCII, padded to kCodeAlignment.
uint8_t buffer_array[16] = {0x48, 0x65, 0x6C, 0x6C, 0x6F, 0x2C, 0x20, 0x57,
0x6F, 0x72, 0x6C, 0x64, 0x21, 0xcc, 0xcc, 0xcc};
// "JavaScript" in ASCII.
uint8_t unwinding_info_array[10] = {0x4A, 0x61, 0x76, 0x61, 0x53,
0x63, 0x72, 0x69, 0x70, 0x74};
uint8_t* buffer = &buffer_array[0];
int buffer_size = sizeof(buffer_array);
uint8_t* unwinding_info = &unwinding_info_array[0];
int unwinding_info_size = sizeof(unwinding_info_array);
CodeDesc code_desc;
code_desc.buffer = buffer;
code_desc.buffer_size = buffer_size;
code_desc.instr_size = buffer_size;
code_desc.safepoint_table_offset = buffer_size;
code_desc.safepoint_table_size = 0;
code_desc.handler_table_offset = buffer_size;
code_desc.handler_table_size = 0;
code_desc.constant_pool_offset = buffer_size;
code_desc.constant_pool_size = 0;
code_desc.builtin_jump_table_info_offset = buffer_size;
code_desc.builtin_jump_table_info_size = 0;
code_desc.code_comments_offset = buffer_size;
code_desc.code_comments_size = 0;
code_desc.reloc_offset = buffer_size;
code_desc.reloc_size = 0;
code_desc.unwinding_info = unwinding_info;
code_desc.unwinding_info_size = unwinding_info_size;
code_desc.origin = nullptr;
DirectHandle<Code> code =
Factory::CodeBuilder(i_isolate(), code_desc, CodeKind::FOR_TESTING)
.Build();
CHECK(code->has_unwinding_info());
CHECK_EQ(code->body_size(), buffer_size + unwinding_info_size);
CHECK_EQ(0, memcmp(reinterpret_cast<void*>(code->instruction_start()), buffer,
buffer_size));
CHECK_EQ(code->unwinding_info_size(), unwinding_info_size);
CHECK_EQ(memcmp(reinterpret_cast<void*>(code->unwinding_info_start()),
unwinding_info, unwinding_info_size),
0);
CHECK_EQ(
static_cast<int>(code->unwinding_info_end() - code->instruction_start()),
buffer_size + unwinding_info_size);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,591 @@
// Copyright 2022 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "include/v8-function.h"
#include "src/api/api-inl.h"
#include "src/codegen/code-desc.h"
#include "src/common/globals.h"
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/heap/factory.h"
#include "src/heap/spaces.h"
#include "src/libsampler/sampler.h"
#include "test/unittests/heap/heap-utils.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
using CodePagesTest = TestWithContext;
namespace internal {
namespace test_code_pages {
// We have three levels of support which have different behaviors to test.
// 1 - Have code range. ARM64 and x64
// 2 - Have code pages. ARM32 only
// 3 - Nothing - This feature does not work on other platforms.
#if defined(V8_TARGET_ARCH_ARM)
static const bool kHaveCodePages = true;
#else
static const bool kHaveCodePages = false;
#endif // defined(V8_TARGET_ARCH_ARM)
static const char* foo_source = R"(
function foo%d(a, b) {
let x = a * b;
let y = x ^ b;
let z = y / a;
return x + y - z;
};
%%PrepareFunctionForOptimization(foo%d);
foo%d(1, 2);
foo%d(1, 2);
%%OptimizeFunctionOnNextCall(foo%d);
foo%d(1, 2);
)";
std::string getFooCode(int n) {
constexpr size_t kMaxSize = 512;
char foo_replaced[kMaxSize];
EXPECT_LE(n, 999999);
snprintf(foo_replaced, kMaxSize, foo_source, n, n, n, n, n, n);
return std::string(foo_replaced);
}
namespace {
bool PagesHasExactPage(std::vector<MemoryRange>* pages, Address search_page) {
void* addr = reinterpret_cast<void*>(search_page);
auto it =
std::find_if(pages->begin(), pages->end(),
[addr](const MemoryRange& r) { return r.start == addr; });
return it != pages->end();
}
bool PagesHasExactPage(std::vector<MemoryRange>* pages, Address search_page,
size_t size) {
void* addr = reinterpret_cast<void*>(search_page);
auto it = std::find_if(pages->begin(), pages->end(),
[addr, size](const MemoryRange& r) {
return r.start == addr && r.length_in_bytes == size;
});
return it != pages->end();
}
bool PagesContainsRange(std::vector<MemoryRange>* pages, Address search_address,
size_t size) {
uint8_t* addr = reinterpret_cast<uint8_t*>(search_address);
auto it =
std::find_if(pages->begin(), pages->end(), [=](const MemoryRange& r) {
const uint8_t* page_start = reinterpret_cast<const uint8_t*>(r.start);
const uint8_t* page_end = page_start + r.length_in_bytes;
return addr >= page_start && (addr + size) <= page_end;
});
return it != pages->end();
}
bool PagesContainsAddress(std::vector<MemoryRange>* pages,
Address search_address) {
return PagesContainsRange(pages, search_address, 0);
}
} // namespace
TEST_F(CodePagesTest, CodeRangeCorrectContents) {
if (!i_isolate()->RequiresCodeRange()) return;
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
const base::AddressRegion& code_region = i_isolate()->heap()->code_region();
EXPECT_TRUE(!code_region.is_empty());
// We should only have the code range and the embedded code range.
EXPECT_EQ(2u, pages->size());
EXPECT_TRUE(
PagesHasExactPage(pages, code_region.begin(), code_region.size()));
EXPECT_TRUE(PagesHasExactPage(
pages, reinterpret_cast<Address>(i_isolate()->CurrentEmbeddedBlobCode()),
i_isolate()->CurrentEmbeddedBlobCodeSize()));
if (i_isolate()->is_short_builtin_calls_enabled()) {
// In this case embedded blob code must be included via code_region.
EXPECT_TRUE(PagesContainsRange(
pages, reinterpret_cast<Address>(i_isolate()->embedded_blob_code()),
i_isolate()->embedded_blob_code_size()));
} else {
EXPECT_TRUE(PagesHasExactPage(
pages, reinterpret_cast<Address>(i_isolate()->embedded_blob_code()),
i_isolate()->embedded_blob_code_size()));
}
}
TEST_F(CodePagesTest, CodePagesCorrectContents) {
if (!kHaveCodePages) return;
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
// There might be other pages already.
EXPECT_GE(pages->size(), 1u);
const base::AddressRegion& code_region = i_isolate()->heap()->code_region();
EXPECT_TRUE(code_region.is_empty());
// We should have the embedded code range even when there is no regular code
// range.
EXPECT_TRUE(PagesHasExactPage(
pages, reinterpret_cast<Address>(i_isolate()->embedded_blob_code()),
i_isolate()->embedded_blob_code_size()));
}
TEST_F(CodePagesTest, OptimizedCodeWithCodeRange) {
v8_flags.allow_natives_syntax = true;
if (!i_isolate()->RequiresCodeRange()) return;
HandleScope scope(i_isolate());
std::string foo_str = getFooCode(1);
RunJS(foo_str.c_str());
v8::Local<v8::Function> local_foo = v8::Local<v8::Function>::Cast(
context()->Global()->Get(context(), NewString("foo1")).ToLocalChecked());
DirectHandle<JSFunction> foo =
Cast<JSFunction>(v8::Utils::OpenDirectHandle(*local_foo));
Tagged<Code> code = foo->code(i_isolate());
// We don't produce optimized code when run with --no-turbofan and
// --no-maglev.
if (!code->is_optimized_code()) return;
Tagged<InstructionStream> foo_code = code->instruction_stream();
EXPECT_TRUE(i_isolate()->heap()->InSpace(foo_code, CODE_SPACE));
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
EXPECT_TRUE(PagesContainsAddress(pages, foo_code.address()));
}
TEST_F(CodePagesTest, OptimizedCodeWithCodePages) {
if (!kHaveCodePages) return;
// We don't want incremental marking to start which could cause the code to
// not be collected on the CollectGarbage() call.
ManualGCScope manual_gc_scope(i_isolate());
v8_flags.allow_natives_syntax = true;
const void* created_page = nullptr;
int num_foos_created = 0;
{
HandleScope scope(i_isolate());
size_t num_code_pages = 0;
size_t initial_num_code_pages = 0;
// Keep generating new code until a new code page is added to the list.
for (int n = 0; n < 999999; n++) {
// Compile and optimize the code and get a reference to it.
std::string foo_str = getFooCode(n);
char foo_name[10];
snprintf(foo_name, sizeof(foo_name), "foo%d", n);
RunJS(foo_str.c_str());
v8::Local<v8::Function> local_foo = v8::Local<v8::Function>::Cast(
context()
->Global()
->Get(context(), NewString(foo_name))
.ToLocalChecked());
DirectHandle<JSFunction> foo =
Cast<JSFunction>(v8::Utils::OpenDirectHandle(*local_foo));
// If there is baseline code, check that it's only due to
// --always-sparkplug (if this check fails, we'll have to re-think this
// test).
if (foo->shared()->HasBaselineCode()) {
EXPECT_TRUE(v8_flags.always_sparkplug);
return;
}
Tagged<Code> code = foo->code(i_isolate());
// We don't produce optimized code when run with --no-turbofan and
// --no-maglev.
if (!code->is_optimized_code()) return;
Tagged<InstructionStream> foo_code = code->instruction_stream();
EXPECT_TRUE(i_isolate()->heap()->InSpace(foo_code, CODE_SPACE));
// Check that the generated code ended up in one of the code pages
// returned by GetCodePages().
uint8_t* foo_code_ptr = reinterpret_cast<uint8_t*>(foo_code.address());
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
// Wait until after we have created the first function to take the initial
// number of pages so that this test isn't brittle to irrelevant
// implementation details.
if (n == 0) {
initial_num_code_pages = pages->size();
}
num_code_pages = pages->size();
// Check that the code object was allocation on any of the pages returned
// by GetCodePages().
auto it = std::find_if(
pages->begin(), pages->end(), [foo_code_ptr](const MemoryRange& r) {
const uint8_t* page_start =
reinterpret_cast<const uint8_t*>(r.start);
const uint8_t* page_end = page_start + r.length_in_bytes;
return foo_code_ptr >= page_start && foo_code_ptr < page_end;
});
EXPECT_NE(it, pages->end());
// Store the page that was created just for our functions - we expect it
// to be removed later.
if (num_code_pages > initial_num_code_pages) {
created_page = it->start;
num_foos_created = n + 1;
break;
}
}
CHECK_NOT_NULL(created_page);
}
// Now delete all our foos and force a GC and check that the page is removed
// from the list.
{
HandleScope scope(i_isolate());
for (int n = 0; n < num_foos_created; n++) {
char foo_name[10];
snprintf(foo_name, sizeof(foo_name), "foo%d", n);
context()
->Global()
->Set(context(), NewString(foo_name), v8::Undefined(isolate()))
.Check();
}
}
{
// The resource may not be reclaimed because of conservative stack scanning.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
InvokeMajorGC();
}
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
auto it = std::find_if(
pages->begin(), pages->end(),
[created_page](const MemoryRange& r) { return r.start == created_page; });
EXPECT_EQ(it, pages->end());
}
TEST_F(CodePagesTest, LargeCodeObject) {
// We don't want incremental marking to start which could cause the code to
// not be collected on the CollectGarbage() call.
ManualGCScope manual_gc_scope(i_isolate());
if (!i_isolate()->RequiresCodeRange() && !kHaveCodePages) return;
// Create a big function that ends up in CODE_LO_SPACE.
const int instruction_size = PageMetadata::kPageSize + 1;
EXPECT_GT(instruction_size, MemoryChunkLayout::MaxRegularCodeObjectSize());
std::unique_ptr<uint8_t[]> instructions(new uint8_t[instruction_size]);
CodeDesc desc;
desc.buffer = instructions.get();
desc.buffer_size = instruction_size;
desc.instr_size = instruction_size;
desc.reloc_size = 0;
desc.constant_pool_size = 0;
desc.unwinding_info = nullptr;
desc.unwinding_info_size = 0;
desc.origin = nullptr;
Address stale_code_address;
{
HandleScope scope(i_isolate());
DirectHandle<Code> foo_code =
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING).Build();
DirectHandle<InstructionStream> foo_istream(foo_code->instruction_stream(),
i_isolate());
EXPECT_TRUE(i_isolate()->heap()->InSpace(*foo_istream, CODE_LO_SPACE));
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
if (i_isolate()->RequiresCodeRange()) {
EXPECT_TRUE(PagesContainsAddress(pages, foo_istream->address()));
} else {
EXPECT_TRUE(PagesHasExactPage(pages, foo_istream->address()));
}
stale_code_address = foo_istream->address();
}
// Delete the large code object.
{
// The resource may not be reclaimed because of conservative stack scanning.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
InvokeMajorGC();
}
EXPECT_TRUE(
!i_isolate()->heap()->InSpaceSlow(stale_code_address, CODE_LO_SPACE));
// Check that it was removed from CodePages.
std::vector<MemoryRange>* pages = i_isolate()->GetCodePages();
EXPECT_TRUE(!PagesHasExactPage(pages, stale_code_address));
}
static constexpr size_t kBufSize = v8::Isolate::kMinCodePagesBufferSize;
class SignalSender : public sampler::Sampler {
public:
explicit SignalSender(v8::Isolate* isolate) : sampler::Sampler(isolate) {}
// Called during the signal/thread suspension.
void SampleStack(const v8::RegisterState& regs) override {
MemoryRange* code_pages_copy = code_pages_copy_.load();
CHECK_NOT_NULL(code_pages_copy);
size_t num_pages = isolate_->CopyCodePages(kBufSize, code_pages_copy);
EXPECT_LE(num_pages, kBufSize);
sample_semaphore_.Signal();
}
// Called on the sampling thread to trigger a sample. Blocks until the sample
// is finished.
void SampleIntoVector(MemoryRange output_buffer[]) {
code_pages_copy_.store(output_buffer);
DoSample();
sample_semaphore_.Wait();
code_pages_copy_.store(nullptr);
}
private:
base::Semaphore sample_semaphore_{0};
std::atomic<MemoryRange*> code_pages_copy_{nullptr};
};
class SamplingThread : public base::Thread {
public:
explicit SamplingThread(SignalSender* signal_sender)
: base::Thread(base::Thread::Options("SamplingThread")),
signal_sender_(signal_sender) {}
// Blocks until a sample is taken.
void TriggerSample() { signal_sender_->SampleIntoVector(code_pages_copy_); }
void Run() override {
while (running_.load()) {
TriggerSample();
}
}
// Called from the main thread. Blocks until a sample is taken. Not
// thread-safe so do not call while this thread is running.
static std::vector<MemoryRange> DoSynchronousSample(v8::Isolate* isolate) {
MemoryRange code_pages_copy[kBufSize];
size_t num_pages = isolate->CopyCodePages(kBufSize, code_pages_copy);
EXPECT_LE(num_pages, kBufSize);
return std::vector<MemoryRange>{code_pages_copy,
&code_pages_copy[num_pages]};
}
void Stop() { running_.store(false); }
private:
std::atomic_bool running_{true};
SignalSender* signal_sender_;
MemoryRange code_pages_copy_[kBufSize];
};
TEST_F(CodePagesTest, LargeCodeObjectWithSignalHandler) {
// We don't want incremental marking to start which could cause the code to
// not be collected on the CollectGarbage() call.
ManualGCScope manual_gc_scope(i_isolate());
if (!i_isolate()->RequiresCodeRange() && !kHaveCodePages) return;
// Create a big function that ends up in CODE_LO_SPACE.
const int instruction_size = PageMetadata::kPageSize + 1;
EXPECT_GT(instruction_size, MemoryChunkLayout::MaxRegularCodeObjectSize());
std::unique_ptr<uint8_t[]> instructions(new uint8_t[instruction_size]);
CodeDesc desc;
desc.buffer = instructions.get();
desc.buffer_size = instruction_size;
desc.instr_size = instruction_size;
desc.reloc_size = 0;
desc.constant_pool_size = 0;
desc.unwinding_info = nullptr;
desc.unwinding_info_size = 0;
desc.origin = nullptr;
Address stale_code_address;
SignalSender signal_sender(isolate());
signal_sender.Start();
// Take an initial sample.
std::vector<MemoryRange> initial_pages =
SamplingThread::DoSynchronousSample(isolate());
SamplingThread sampling_thread(&signal_sender);
sampling_thread.StartSynchronously();
{
HandleScope scope(i_isolate());
DirectHandle<Code> foo_code =
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING).Build();
DirectHandle<InstructionStream> foo_istream(foo_code->instruction_stream(),
i_isolate());
EXPECT_TRUE(i_isolate()->heap()->InSpace(*foo_istream, CODE_LO_SPACE));
// Do a synchronous sample to ensure that we capture the state with the
// extra code page.
sampling_thread.Stop();
sampling_thread.Join();
// Check that the page was added.
std::vector<MemoryRange> pages =
SamplingThread::DoSynchronousSample(isolate());
if (i_isolate()->RequiresCodeRange()) {
EXPECT_TRUE(PagesContainsAddress(&pages, foo_istream->address()));
} else {
EXPECT_TRUE(PagesHasExactPage(&pages, foo_istream->address()));
}
stale_code_address = foo_istream->address();
}
// Start async sampling again to detect threading issues.
sampling_thread.StartSynchronously();
// Delete the large code object.
{
// The resource may not be reclaimed because of conservative stack scanning.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
InvokeMajorGC();
}
EXPECT_TRUE(
!i_isolate()->heap()->InSpaceSlow(stale_code_address, CODE_LO_SPACE));
sampling_thread.Stop();
sampling_thread.Join();
std::vector<MemoryRange> pages =
SamplingThread::DoSynchronousSample(isolate());
EXPECT_TRUE(!PagesHasExactPage(&pages, stale_code_address));
signal_sender.Stop();
}
TEST_F(CodePagesTest, Sorted) {
// We don't want incremental marking to start which could cause the code to
// not be collected on the CollectGarbage() call.
ManualGCScope manual_gc_scope(i_isolate());
if (!i_isolate()->RequiresCodeRange() && !kHaveCodePages) return;
// Create a big function that ends up in CODE_LO_SPACE.
const int instruction_size = PageMetadata::kPageSize + 1;
EXPECT_GT(instruction_size, MemoryChunkLayout::MaxRegularCodeObjectSize());
std::unique_ptr<uint8_t[]> instructions(new uint8_t[instruction_size]);
CodeDesc desc;
desc.buffer = instructions.get();
desc.buffer_size = instruction_size;
desc.instr_size = instruction_size;
desc.reloc_size = 0;
desc.constant_pool_size = 0;
desc.unwinding_info = nullptr;
desc.unwinding_info_size = 0;
desc.origin = nullptr;
// Take an initial sample.
std::vector<MemoryRange> initial_pages =
SamplingThread::DoSynchronousSample(isolate());
size_t initial_num_pages = initial_pages.size();
auto compare = [](const MemoryRange& a, const MemoryRange& b) {
return a.start < b.start;
};
{
HandleScope outer_scope(i_isolate());
IndirectHandle<InstructionStream> code1, code3;
Address code2_address;
code1 =
handle(Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING)
.Build()
->instruction_stream(),
i_isolate());
EXPECT_TRUE(i_isolate()->heap()->InSpace(*code1, CODE_LO_SPACE));
{
HandleScope scope(i_isolate());
// Create three large code objects, we'll delete the middle one and check
// everything is still sorted.
DirectHandle<InstructionStream> code2(
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING)
.Build()
->instruction_stream(),
i_isolate());
EXPECT_TRUE(i_isolate()->heap()->InSpace(*code2, CODE_LO_SPACE));
code3 =
handle(Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING)
.Build()
->instruction_stream(),
i_isolate());
EXPECT_TRUE(i_isolate()->heap()->InSpace(*code3, CODE_LO_SPACE));
code2_address = code2->address();
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code1->address(), CODE_LO_SPACE));
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code2->address(), CODE_LO_SPACE));
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code3->address(), CODE_LO_SPACE));
// Check that the pages were added.
std::vector<MemoryRange> pages =
SamplingThread::DoSynchronousSample(isolate());
if (i_isolate()->RequiresCodeRange()) {
EXPECT_EQ(pages.size(), initial_num_pages);
} else {
EXPECT_EQ(pages.size(), initial_num_pages + 3);
}
EXPECT_TRUE(std::is_sorted(pages.begin(), pages.end(), compare));
code3 = scope.CloseAndEscape(code3);
}
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code1->address(), CODE_LO_SPACE));
EXPECT_TRUE(i_isolate()->heap()->InSpaceSlow(code2_address, CODE_LO_SPACE));
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code3->address(), CODE_LO_SPACE));
// Delete code2.
{
// The resource may not be reclaimed because of conservative stack
// scanning.
DisableConservativeStackScanningScopeForTesting no_stack_scanning(
i_isolate()->heap());
InvokeMajorGC();
}
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code1->address(), CODE_LO_SPACE));
EXPECT_TRUE(
!i_isolate()->heap()->InSpaceSlow(code2_address, CODE_LO_SPACE));
EXPECT_TRUE(
i_isolate()->heap()->InSpaceSlow(code3->address(), CODE_LO_SPACE));
std::vector<MemoryRange> pages =
SamplingThread::DoSynchronousSample(isolate());
if (i_isolate()->RequiresCodeRange()) {
EXPECT_EQ(pages.size(), initial_num_pages);
} else {
EXPECT_EQ(pages.size(), initial_num_pages + 2);
}
EXPECT_TRUE(std::is_sorted(pages.begin(), pages.end(), compare));
}
}
} // namespace test_code_pages
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,159 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "test/unittests/codegen/code-stub-assembler-unittest.h"
#include "src/compiler/node.h"
#include "src/execution/isolate.h"
#include "test/common/code-assembler-tester.h"
#include "test/unittests/compiler/compiler-test-utils.h"
#include "test/unittests/compiler/function-tester.h"
#include "test/unittests/compiler/node-test-utils.h"
using ::testing::_;
namespace c = v8::internal::compiler;
namespace v8 {
namespace internal {
CodeStubAssemblerTestState::CodeStubAssemblerTestState(
CodeStubAssemblerTest* test)
: compiler::CodeAssemblerState(test->i_isolate(), test->zone(),
VoidDescriptor{}, CodeKind::FOR_TESTING,
"test") {}
TARGET_TEST_F(CodeStubAssemblerTest, SmiTag) {
CodeStubAssemblerTestState state(this);
CodeStubAssemblerForTest m(&state);
TNode<IntPtrT> value = m.IntPtrConstant(44);
EXPECT_THAT(m.SmiTag(value),
IsBitcastWordToTaggedSigned(c::IsIntPtrConstant(
static_cast<intptr_t>(44) << (kSmiShiftSize + kSmiTagSize))));
EXPECT_THAT(m.SmiUntag(m.ReinterpretCast<Smi>(value)),
c::IsIntPtrConstant(static_cast<intptr_t>(44) >>
(kSmiShiftSize + kSmiTagSize)));
}
TARGET_TEST_F(CodeStubAssemblerTest, IntPtrMax) {
CodeStubAssemblerTestState state(this);
CodeStubAssemblerForTest m(&state);
{
TNode<IntPtrT> a = m.IntPtrConstant(100);
TNode<IntPtrT> b = m.IntPtrConstant(1);
TNode<IntPtrT> z = m.IntPtrMax(a, b);
EXPECT_THAT(z, c::IsIntPtrConstant(100));
}
}
TARGET_TEST_F(CodeStubAssemblerTest, IntPtrMin) {
CodeStubAssemblerTestState state(this);
CodeStubAssemblerForTest m(&state);
{
TNode<IntPtrT> a = m.IntPtrConstant(100);
TNode<IntPtrT> b = m.IntPtrConstant(1);
TNode<IntPtrT> z = m.IntPtrMin(a, b);
EXPECT_THAT(z, c::IsIntPtrConstant(1));
}
}
#define __ assembler.
namespace {
void ExpectArrayListsEqual(DirectHandle<ArrayList> array1,
DirectHandle<ArrayList> array2) {
EXPECT_EQ(array1->capacity(), array2->capacity());
EXPECT_EQ(array1->length(), array2->length());
for (int i = 0; i < array1->length(); i++) {
EXPECT_EQ(array1->get(i), array2->get(i));
}
}
} // namespace
TARGET_TEST_F(CodeStubAssemblerTest, ArrayListAllocateEquivalent) {
constexpr int L = 1;
// Tests that the CSA implementation of ArrayList behave the same as the C++
// implementation.
DirectHandle<Code> allocate_arraylist_in_csa;
{
compiler::CodeAssemblerTester tester(i_isolate(), JSParameterCount(0));
CodeStubAssembler assembler(tester.state());
TNode<ArrayList> array = __ AllocateArrayList(__ SmiConstant(L));
__ ArrayListSet(array, __ SmiConstant(0), __ UndefinedConstant());
__ Return(array);
allocate_arraylist_in_csa = tester.GenerateCodeCloseAndEscape();
}
DirectHandle<ArrayList> array1 = ArrayList::New(i_isolate(), L);
compiler::FunctionTester ft(i_isolate(), allocate_arraylist_in_csa, 0);
DirectHandle<ArrayList> array2 = ft.CallChecked<ArrayList>();
ExpectArrayListsEqual(array1, array2);
}
TARGET_TEST_F(CodeStubAssemblerTest, ArrayListAddEquivalent) {
constexpr int L = 1;
// Tests that the CSA implementation of ArrayList behave the same as the C++
// implementation.
DirectHandle<Code> allocate_arraylist_in_csa;
{
compiler::CodeAssemblerTester tester(i_isolate(), JSParameterCount(0));
CodeStubAssembler assembler(tester.state());
TNode<ArrayList> array = __ AllocateArrayList(__ SmiConstant(L));
array = __ ArrayListAdd(array, __ SmiConstant(0));
array = __ ArrayListAdd(array, __ SmiConstant(1));
array = __ ArrayListAdd(array, __ SmiConstant(2));
array = __ ArrayListAdd(array, __ SmiConstant(3));
array = __ ArrayListAdd(array, __ SmiConstant(4));
__ Return(array);
allocate_arraylist_in_csa = tester.GenerateCodeCloseAndEscape();
}
DirectHandle<ArrayList> array1 = ArrayList::New(i_isolate(), L);
for (int i = 0; i < 5; i++) {
array1 = ArrayList::Add(i_isolate(), array1, Smi::FromInt(i));
}
compiler::FunctionTester ft(i_isolate(), allocate_arraylist_in_csa, 0);
DirectHandle<ArrayList> list2 = ft.CallChecked<ArrayList>();
ExpectArrayListsEqual(array1, list2);
}
TARGET_TEST_F(CodeStubAssemblerTest, ArrayListElementsEquivalent) {
constexpr int L = 1;
// Tests that the CSA implementation of ArrayList behave the same as the C++
// implementation.
DirectHandle<Code> allocate_arraylist_in_csa;
{
compiler::CodeAssemblerTester tester(i_isolate(), JSParameterCount(0));
CodeStubAssembler assembler(tester.state());
TNode<ArrayList> list = __ AllocateArrayList(__ SmiConstant(L));
list = __ ArrayListAdd(list, __ SmiConstant(0));
list = __ ArrayListAdd(list, __ SmiConstant(1));
list = __ ArrayListAdd(list, __ SmiConstant(2));
list = __ ArrayListAdd(list, __ SmiConstant(3));
list = __ ArrayListAdd(list, __ SmiConstant(4));
__ Return(__ ArrayListElements(list));
allocate_arraylist_in_csa = tester.GenerateCodeCloseAndEscape();
}
DirectHandle<ArrayList> array1 = ArrayList::New(i_isolate(), L);
for (int i = 0; i < 5; i++) {
array1 = ArrayList::Add(i_isolate(), array1, Smi::FromInt(i));
}
DirectHandle<FixedArray> elements1 =
ArrayList::ToFixedArray(i_isolate(), array1);
compiler::FunctionTester ft(i_isolate(), allocate_arraylist_in_csa, 0);
DirectHandle<FixedArray> elements2 = ft.CallChecked<FixedArray>();
EXPECT_EQ(elements1->length(), elements2->length());
for (int i = 0; i < elements1->length(); i++) {
EXPECT_EQ(elements1->get(i), elements2->get(i));
}
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,34 @@
// Copyright 2017 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef V8_UNITTESTS_CODE_STUB_ASSEMBLER_UNITTEST_H_
#define V8_UNITTESTS_CODE_STUB_ASSEMBLER_UNITTEST_H_
#include "src/codegen/code-stub-assembler.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
class CodeStubAssemblerTest : public TestWithContextAndZone {
public:
CodeStubAssemblerTest() : TestWithContextAndZone(kCompressGraphZone) {}
~CodeStubAssemblerTest() override = default;
};
class CodeStubAssemblerTestState : public compiler::CodeAssemblerState {
public:
explicit CodeStubAssemblerTestState(CodeStubAssemblerTest* test);
};
class CodeStubAssemblerForTest : public CodeStubAssembler {
public:
explicit CodeStubAssemblerForTest(CodeStubAssemblerTestState* state)
: CodeStubAssembler(state) {}
};
} // namespace internal
} // namespace v8
#endif // V8_UNITTESTS_CODE_STUB_ASSEMBLER_UNITTEST_H_

View File

@ -0,0 +1,105 @@
// Copyright 2018 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <memory>
#include "include/v8-isolate.h"
#include "src/codegen/code-desc.h"
#include "src/execution/isolate.h"
#include "src/handles/handles-inl.h"
#include "src/heap/heap-inl.h"
#include "test/unittests/test-utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace v8 {
namespace internal {
using FactoryCodeBuilderTest = TestWithIsolate;
TEST_F(FactoryCodeBuilderTest, Factory_CodeBuilder) {
// Create a big function that ends up in CODE_LO_SPACE.
const int instruction_size =
i_isolate()->heap()->MaxRegularHeapObjectSize(AllocationType::kCode) + 1;
std::unique_ptr<uint8_t[]> instructions(new uint8_t[instruction_size]);
CodeDesc desc;
desc.buffer = instructions.get();
desc.buffer_size = instruction_size;
desc.instr_size = instruction_size;
desc.reloc_size = 0;
desc.constant_pool_size = 0;
desc.unwinding_info = nullptr;
desc.unwinding_info_size = 0;
desc.origin = nullptr;
DirectHandle<Code> code =
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING).Build();
CHECK(
i_isolate()->heap()->InSpace(code->instruction_stream(), CODE_LO_SPACE));
#if VERIFY_HEAP
Object::ObjectVerify(*code, i_isolate());
#endif
}
// This needs to be large enough to create a new nosnap Isolate, but smaller
// than kMaximalCodeRangeSize so we can recover from the OOM.
constexpr int kInstructionSize = 100 * MB;
static_assert(kInstructionSize < kMaximalCodeRangeSize ||
!kPlatformRequiresCodeRange);
size_t NearHeapLimitCallback(void* raw_bool, size_t current_heap_limit,
size_t initial_heap_limit) {
bool* oom_triggered = static_cast<bool*>(raw_bool);
*oom_triggered = true;
return kInstructionSize * 2;
}
class FactoryCodeBuilderOOMTest : public TestWithIsolate {
public:
static void SetUpTestSuite() {
v8_flags.max_old_space_size = kInstructionSize / MB / 2; // In MB.
// Keep semi-space size small so that the heuristics don't think we have
// enough combined space for the allocation.
v8_flags.max_semi_space_size = 8;
}
void SetUp() override {
isolate()->heap()->AddNearHeapLimitCallback(NearHeapLimitCallback,
&oom_triggered_);
}
bool oom_triggered() const { return oom_triggered_; }
private:
bool oom_triggered_ = false;
};
TEST_F(FactoryCodeBuilderOOMTest, Factory_CodeBuilder_BuildOOM) {
std::unique_ptr<uint8_t[]> instructions(new uint8_t[kInstructionSize]);
CodeDesc desc;
desc.instr_size = kInstructionSize;
desc.buffer = instructions.get();
const DirectHandle<Code> code =
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING).Build();
CHECK(!code.is_null());
CHECK(oom_triggered());
}
TEST_F(FactoryCodeBuilderOOMTest, Factory_CodeBuilder_TryBuildOOM) {
std::unique_ptr<uint8_t[]> instructions(new uint8_t[kInstructionSize]);
CodeDesc desc;
desc.instr_size = kInstructionSize;
desc.buffer = instructions.get();
const MaybeDirectHandle<Code> code =
Factory::CodeBuilder(i_isolate(), desc, CodeKind::FOR_TESTING).TryBuild();
CHECK(code.is_null());
CHECK(!oom_triggered());
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,167 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/codegen/register-configuration.h"
#include "testing/gtest-support.h"
namespace v8 {
namespace internal {
const MachineRepresentation kFloat32 = MachineRepresentation::kFloat32;
const MachineRepresentation kFloat64 = MachineRepresentation::kFloat64;
const MachineRepresentation kSimd128 = MachineRepresentation::kSimd128;
class RegisterConfigurationUnitTest : public ::testing::Test {
public:
RegisterConfigurationUnitTest() = default;
~RegisterConfigurationUnitTest() override = default;
};
TEST_F(RegisterConfigurationUnitTest, BasicProperties) {
const int kNumGeneralRegs = 3;
const int kNumDoubleRegs = 4;
const int kNumAllocatableGeneralRegs = 2;
const int kNumAllocatableDoubleRegs = 2;
int general_codes[kNumAllocatableGeneralRegs] = {1, 2};
int double_codes[kNumAllocatableDoubleRegs] = {2, 3};
RegisterConfiguration test(AliasingKind::kOverlap, kNumGeneralRegs,
kNumDoubleRegs, 0, 0, kNumAllocatableGeneralRegs,
kNumAllocatableDoubleRegs, 0, 0, general_codes,
double_codes);
EXPECT_EQ(test.num_general_registers(), kNumGeneralRegs);
EXPECT_EQ(test.num_double_registers(), kNumDoubleRegs);
EXPECT_EQ(test.num_allocatable_general_registers(),
kNumAllocatableGeneralRegs);
EXPECT_EQ(test.num_allocatable_double_registers(), kNumAllocatableDoubleRegs);
EXPECT_EQ(test.num_allocatable_float_registers(), kNumAllocatableDoubleRegs);
EXPECT_EQ(test.num_allocatable_simd128_registers(),
kNumAllocatableDoubleRegs);
#if V8_TARGET_ARCH_X64
EXPECT_EQ(test.num_allocatable_simd256_registers(),
kNumAllocatableDoubleRegs);
#endif
EXPECT_EQ(test.allocatable_general_codes_mask(),
(1 << general_codes[0]) | (1 << general_codes[1]));
EXPECT_EQ(test.GetAllocatableGeneralCode(0), general_codes[0]);
EXPECT_EQ(test.GetAllocatableGeneralCode(1), general_codes[1]);
EXPECT_EQ(test.allocatable_double_codes_mask(),
(1 << double_codes[0]) | (1 << double_codes[1]));
EXPECT_EQ(test.GetAllocatableFloatCode(0), double_codes[0]);
EXPECT_EQ(test.GetAllocatableDoubleCode(0), double_codes[0]);
EXPECT_EQ(test.GetAllocatableSimd128Code(0), double_codes[0]);
EXPECT_EQ(test.GetAllocatableFloatCode(1), double_codes[1]);
EXPECT_EQ(test.GetAllocatableDoubleCode(1), double_codes[1]);
EXPECT_EQ(test.GetAllocatableSimd128Code(1), double_codes[1]);
}
TEST_F(RegisterConfigurationUnitTest, CombineAliasing) {
const int kNumGeneralRegs = 3;
const int kNumDoubleRegs = 4;
const int kNumAllocatableGeneralRegs = 2;
const int kNumAllocatableDoubleRegs = 3;
int general_codes[] = {1, 2};
int double_codes[] = {2, 3, 16}; // reg 16 should not alias registers 32, 33.
RegisterConfiguration test(AliasingKind::kCombine, kNumGeneralRegs,
kNumDoubleRegs, 0, 0, kNumAllocatableGeneralRegs,
kNumAllocatableDoubleRegs, 0, 0, general_codes,
double_codes);
// There are 3 allocatable double regs, but only 2 can alias float regs.
EXPECT_EQ(test.num_allocatable_float_registers(), 4);
// Test that float registers combine in pairs to form double registers.
EXPECT_EQ(test.GetAllocatableFloatCode(0), double_codes[0] * 2);
EXPECT_EQ(test.GetAllocatableFloatCode(1), double_codes[0] * 2 + 1);
EXPECT_EQ(test.GetAllocatableFloatCode(2), double_codes[1] * 2);
EXPECT_EQ(test.GetAllocatableFloatCode(3), double_codes[1] * 2 + 1);
// There are 3 allocatable double regs, but only 2 pair to form 1 SIMD reg.
EXPECT_EQ(test.num_allocatable_simd128_registers(), 1);
// Test that even-odd pairs of double regs combine to form a SIMD reg.
EXPECT_EQ(test.GetAllocatableSimd128Code(0), double_codes[0] / 2);
// Registers alias themselves.
EXPECT_TRUE(test.AreAliases(kFloat32, 0, kFloat32, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat64, 0));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kSimd128, 0));
// Registers don't alias other registers of the same size.
EXPECT_FALSE(test.AreAliases(kFloat32, 1, kFloat32, 0));
EXPECT_FALSE(test.AreAliases(kFloat64, 1, kFloat64, 0));
EXPECT_FALSE(test.AreAliases(kSimd128, 1, kSimd128, 0));
// Float registers combine in pairs to alias a double with index / 2, and
// in 4's to alias a simd128 with index / 4.
EXPECT_TRUE(test.AreAliases(kFloat32, 0, kFloat64, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 1, kFloat64, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 0, kSimd128, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 1, kSimd128, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 2, kSimd128, 0));
EXPECT_TRUE(test.AreAliases(kFloat32, 3, kSimd128, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 0));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 1));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kFloat32, 0));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kFloat32, 1));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kFloat32, 2));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kFloat32, 3));
EXPECT_FALSE(test.AreAliases(kFloat32, 0, kFloat64, 1));
EXPECT_FALSE(test.AreAliases(kFloat32, 1, kFloat64, 1));
EXPECT_FALSE(test.AreAliases(kFloat32, 0, kSimd128, 1));
EXPECT_FALSE(test.AreAliases(kFloat32, 1, kSimd128, 1));
EXPECT_FALSE(test.AreAliases(kFloat64, 0, kSimd128, 1));
EXPECT_FALSE(test.AreAliases(kFloat64, 1, kSimd128, 1));
EXPECT_TRUE(test.AreAliases(kFloat64, 0, kFloat32, 1));
EXPECT_TRUE(test.AreAliases(kFloat64, 1, kFloat32, 2));
EXPECT_TRUE(test.AreAliases(kFloat64, 1, kFloat32, 3));
EXPECT_TRUE(test.AreAliases(kFloat64, 2, kFloat32, 4));
EXPECT_TRUE(test.AreAliases(kFloat64, 2, kFloat32, 5));
EXPECT_TRUE(test.AreAliases(kSimd128, 0, kFloat64, 1));
EXPECT_TRUE(test.AreAliases(kSimd128, 1, kFloat64, 2));
EXPECT_TRUE(test.AreAliases(kSimd128, 1, kFloat64, 3));
EXPECT_TRUE(test.AreAliases(kSimd128, 2, kFloat64, 4));
EXPECT_TRUE(test.AreAliases(kSimd128, 2, kFloat64, 5));
int alias_base_index = -1;
EXPECT_EQ(test.GetAliases(kFloat32, 0, kFloat32, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat64, 1, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat32, 0, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat32, 1, kFloat64, &alias_base_index), 1);
EXPECT_EQ(test.GetAliases(kFloat32, 2, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat32, 3, kFloat64, &alias_base_index), 1);
EXPECT_EQ(alias_base_index, 1);
EXPECT_EQ(test.GetAliases(kFloat64, 0, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 0);
EXPECT_EQ(test.GetAliases(kFloat64, 1, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 2);
// Non-allocatable codes still alias.
EXPECT_EQ(test.GetAliases(kFloat64, 2, kFloat32, &alias_base_index), 2);
EXPECT_EQ(alias_base_index, 4);
// High numbered double and simd regs don't alias nonexistent float registers.
EXPECT_EQ(
test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters / 2,
kFloat32, &alias_base_index),
0);
EXPECT_EQ(
test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters / 2 + 1,
kFloat32, &alias_base_index),
0);
EXPECT_EQ(
test.GetAliases(kFloat64, RegisterConfiguration::kMaxFPRegisters - 1,
kFloat32, &alias_base_index),
0);
}
} // namespace internal
} // namespace v8

View File

@ -0,0 +1,97 @@
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "src/codegen/source-position-table.h"
#include "test/unittests/test-utils.h"
namespace v8 {
namespace internal {
namespace interpreter {
class SourcePositionTableTest : public TestWithIsolate {
public:
SourcePositionTableTest() : zone_(isolate()->allocator(), ZONE_NAME) {}
~SourcePositionTableTest() override = default;
SourcePosition toPos(int offset) {
return SourcePosition(offset, offset % 10 - 1);
}
SourcePositionTableBuilder* builder() { return &builder_; }
private:
Zone zone_;
SourcePositionTableBuilder builder_{&zone_};
};
// Some random offsets, mostly at 'suspicious' bit boundaries.
static int offsets[] = {0, 1, 2, 3, 4, 30, 31, 32,
33, 62, 63, 64, 65, 126, 127, 128,
129, 250, 1000, 9999, 12000, 31415926};
TEST_F(SourcePositionTableTest, EncodeStatement) {
for (size_t i = 0; i < arraysize(offsets); i++) {
builder()->AddPosition(offsets[i], toPos(offsets[i]), true);
}
// To test correctness, we rely on the assertions in ToSourcePositionTable().
// (Also below.)
CHECK(!builder()->ToSourcePositionTable(isolate()).is_null());
}
TEST_F(SourcePositionTableTest, EncodeStatementDuplicates) {
for (size_t i = 0; i < arraysize(offsets); i++) {
builder()->AddPosition(offsets[i], toPos(offsets[i]), true);
builder()->AddPosition(offsets[i], toPos(offsets[i] + 1), true);
}
// To test correctness, we rely on the assertions in ToSourcePositionTable().
// (Also below.)
CHECK(!builder()->ToSourcePositionTable(isolate()).is_null());
}
TEST_F(SourcePositionTableTest, EncodeExpression) {
for (size_t i = 0; i < arraysize(offsets); i++) {
builder()->AddPosition(offsets[i], toPos(offsets[i]), false);
}
CHECK(!builder()->ToSourcePositionTable(isolate()).is_null());
}
TEST_F(SourcePositionTableTest, EncodeAscendingPositive) {
int code_offset = 0;
int source_position = 0;
for (size_t i = 0; i < arraysize(offsets); i++) {
code_offset += offsets[i];
source_position += offsets[i];
if (i % 2) {
builder()->AddPosition(code_offset, toPos(source_position), true);
} else {
builder()->AddPosition(code_offset, toPos(source_position), false);
}
}
CHECK(!builder()->ToSourcePositionTable(isolate()).is_null());
}
TEST_F(SourcePositionTableTest, EncodeAscendingNegative) {
int code_offset = 0;
// Start with a big source position, then decrement it.
int source_position = 1 << 26;
for (size_t i = 0; i < arraysize(offsets); i++) {
code_offset += offsets[i];
source_position -= offsets[i];
if (i % 2) {
builder()->AddPosition(code_offset, toPos(source_position), true);
} else {
builder()->AddPosition(code_offset, toPos(source_position), false);
}
}
CHECK(!builder()->ToSourcePositionTable(isolate()).is_null());
}
} // namespace interpreter
} // namespace internal
} // namespace v8