Files
avmplus/extensions/ST_nanojit_codealloc.st
dmcallis 65a0592776 Initial source code drop
Source code drop for the Actionscript virtual machine in the Adobe
Flash Player for December 2013
2013-12-02 16:04:04 -08:00

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