Source code drop for the Actionscript virtual machine in the Adobe Flash Player for December 2013
196 lines
6.6 KiB
Smalltalk
196 lines
6.6 KiB
Smalltalk
// -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*-
|
|
// vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5)
|
|
//
|
|
// This Source Code Form is subject to the terms of the Mozilla Public
|
|
// License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
%%component nanojit
|
|
%%category codealloc
|
|
|
|
%%prefix
|
|
|
|
// This conditionalization is clumsy, but we don't seem to have any other
|
|
// mechanism to enable a selftest conditionally upon the configuration.
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
using nanojit::NIns;
|
|
using nanojit::CodeList;
|
|
using nanojit::CodeAlloc;
|
|
|
|
static bool verbose = false;
|
|
|
|
class Randomizer {
|
|
private:
|
|
|
|
TRandomFast seed;
|
|
|
|
public:
|
|
|
|
Randomizer()
|
|
{
|
|
MathUtils::initRandom(&seed);
|
|
}
|
|
|
|
// Return integer in range [min,max).
|
|
|
|
uint32_t range(uint32_t min, uint32_t max)
|
|
{
|
|
return min + MathUtils::Random(max - min, &seed);
|
|
}
|
|
};
|
|
|
|
class CodeAllocDriver {
|
|
public:
|
|
|
|
struct CodeListProfile {
|
|
uint32_t largefreq; // 1 in 'largefreq' chance codelist will be "large"
|
|
uint32_t smallmin; // Minimum size of "small" codelist
|
|
uint32_t smallmax; // Maximum size of "small" codelist
|
|
uint32_t largemin; // Minimum size of "large" codelist
|
|
uint32_t largemax; // Maximum size of "large" coelist
|
|
};
|
|
|
|
struct AllocationProfile {
|
|
uint32_t limit; // Largest block allocation permitted, zero for no limit
|
|
uint32_t leftfreq; // 1 in 'leftfreq' chance returned space will start at beginning of block
|
|
};
|
|
|
|
CodeAllocDriver(uint32_t max_codelists, CodeListProfile& codelist_profile, AllocationProfile& alloc_profile);
|
|
|
|
void run(uint32_t iterations);
|
|
|
|
private:
|
|
|
|
Randomizer rand;
|
|
uint32_t n_active; // Maximum number of simultaneously-allocated codelists
|
|
CodeListProfile cp; // Parameters controlling distribution of codelist sizes
|
|
AllocationProfile ap; // Parameters controlling allocation of each codelist
|
|
|
|
void makeCodeList(uint32_t index, CodeAlloc& alloc, CodeList*& code);
|
|
};
|
|
|
|
CodeAllocDriver::CodeAllocDriver(uint32_t max_codelists, CodeListProfile& codelist_profile, AllocationProfile& alloc_profile)
|
|
: n_active(max_codelists), cp(codelist_profile), ap(alloc_profile)
|
|
{}
|
|
|
|
// Create a codelist at the specified index in the codelist pool.
|
|
// The length of the codelist is generated pseudo-randomly.
|
|
|
|
void CodeAllocDriver::makeCodeList(uint32_t index, CodeAlloc& alloc, CodeList*& code)
|
|
{
|
|
NIns* start;
|
|
NIns* end;
|
|
|
|
int32_t size = ((rand.range(0, cp.largefreq) > 0)
|
|
? rand.range(cp.smallmin, cp.smallmax)
|
|
: rand.range(cp.largemin, cp.largemax));
|
|
if (verbose)
|
|
AvmLog("code-heap-test: codelist %d size %d\n", index, size);
|
|
while (size > 0) {
|
|
alloc.alloc(start, end, ap.limit);
|
|
uint32_t blksize = uint32_t(end - start);
|
|
if (verbose)
|
|
AvmLog("code-heap-test: alloc %x-%x %d\n", start, start, blksize);
|
|
size -= blksize;
|
|
if (size < 0) {
|
|
// Last allocated block was not completely used, so we will return some of it.
|
|
// We choose randomly whether to return a prefix of the block, or a segment out
|
|
// of the middle. This doesn't correspond precisely to normal usage in nanojit.
|
|
// Note that if we are not retaining at least two bytes, we cannot split in the middle.
|
|
int32_t excess = -size;
|
|
if (rand.range(0, ap.leftfreq) > 0 || blksize - excess < 2) {
|
|
// Hole starts at left
|
|
// Note that hole at right is deliberately not handled in CodegenAlloc.cpp, results in assert.
|
|
if (verbose)
|
|
AvmLog("code-heap-test: hole l %x-%x %d\n", start, start + excess, excess);
|
|
alloc.addRemainder(code, start, end, start, start + excess);
|
|
} else {
|
|
// Hole will go in middle
|
|
uint32_t offset = rand.range(1, blksize - excess);
|
|
if (verbose)
|
|
AvmLog("code-heap-test: hole m %x-%x %d\n", start + offset, start + offset + excess, excess);
|
|
alloc.addRemainder(code, start, end, start + offset, start + offset + excess);
|
|
}
|
|
} else {
|
|
// Add entire block to codelist.
|
|
CodeAlloc::add(code, start, end);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Repeatedly construct and destroy codelists.
|
|
// We maintain a pool of codelists. On each iteration, we replace one
|
|
// of the existing codelists with a new one, freeing the old.
|
|
|
|
void CodeAllocDriver::run(uint32_t iterations)
|
|
{
|
|
nanojit::Config config;
|
|
// Enable page protection flag checking during test code
|
|
config.check_page_flags = true;
|
|
|
|
CodeAlloc alloc(&config);
|
|
|
|
CodeList** codelists = mmfx_new_array(CodeList*, n_active);
|
|
|
|
VMPI_memset(codelists, 0, n_active * sizeof(CodeList*));
|
|
|
|
for (uint32_t i = 0; i < iterations; i++) {
|
|
uint32_t victim = rand.range(0, n_active);
|
|
alloc.freeAll(codelists[victim]);
|
|
makeCodeList(victim, alloc, codelists[victim]);
|
|
//alloc.logStats();
|
|
debug_only( alloc.sanity_check(); )
|
|
}
|
|
|
|
mmfx_delete_array(codelists);
|
|
}
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
%%methods
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
typedef CodeAllocDriver::CodeListProfile CodeListProfile;
|
|
typedef CodeAllocDriver::AllocationProfile AllocationProfile;
|
|
|
|
static CodeListProfile cp = { 5, 1, 1*1024, 1, 16*1024 };
|
|
|
|
static AllocationProfile ap[] = { { 0, 2 },
|
|
#ifndef NANOJIT_64BIT
|
|
// 128-byte minimum is too small on 64-bit platorms.
|
|
{ 128, 2 },
|
|
#endif
|
|
{ 512, 2 },
|
|
{ 1024, 2 },
|
|
{ 2048, 2 },
|
|
{ 4096, 2 } };
|
|
|
|
static uint32_t n_ap = sizeof(ap) / sizeof(AllocationProfile);
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
%%test allocfree
|
|
|
|
#ifdef VMCFG_NANOJIT
|
|
|
|
for (uint32_t i = 0; i < n_ap; i++) {
|
|
CodeAllocDriver* driver = mmfx_new(CodeAllocDriver(20, cp, ap[i]));
|
|
#if defined(DEBUG) && !(defined(VMCFG_IA32) || defined(VMCFG_AMD64))
|
|
// This test is very slow in debug builds, due to the calls to sanity_check().
|
|
// Run an abbreviated version of the test except on desktop platforms.
|
|
driver->run(200);
|
|
#else
|
|
driver->run(20000);
|
|
#endif
|
|
mmfx_delete(driver);
|
|
}
|
|
|
|
#endif /* VMCFG_NANOJIT */
|
|
|
|
// We pass if we don't crash or assert.
|
|
%%verify true
|
|
|