little debug/profiling tool cleanups

- Can convert raw code to dylib in place.
- Can track one fewer pointer for JIT code: no need
  for both in-memory and dylib JITs at the same time.
- Remove disused fOriginalProgram.
- Split Program::eval/interpret to make profiles clearer.

Change-Id: I66d64a68016f1d1d7d19d5002927e9e277970612
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/264816
Commit-Queue: Mike Klein <mtklein@google.com>
Auto-Submit: Mike Klein <mtklein@google.com>
Reviewed-by: Herb Derby <herb@google.com>
This commit is contained in:
Mike Klein 2020-01-16 14:13:17 -06:00 committed by Skia Commit-Bot
parent 56a48bb1aa
commit e3193ad4e1
2 changed files with 39 additions and 47 deletions

View File

@ -1571,12 +1571,9 @@ namespace skvm {
}
void Program::eval(int n, void* args[]) const {
const int nargs = (int)fStrides.size();
if (const void* b = fDylibEntry ? fDylibEntry
: fJITBuf) {
if (const void* b = fJITEntry) {
void** a = args;
switch (nargs) {
switch (fStrides.size()) {
case 0: return ((void(*)(int ))b)(n );
case 1: return ((void(*)(int,void* ))b)(n,a[0] );
case 2: return ((void(*)(int,void*,void* ))b)(n,a[0],a[1] );
@ -1588,6 +1585,10 @@ namespace skvm {
}
}
this->interpret(n, args);
}
void Program::interpret(int n, void* args[]) const {
// We'll operate in SIMT style, knocking off K-size chunks from n while possible.
constexpr int K = 16;
using I32 = skvx::Vec<K, int>;
@ -1631,7 +1632,7 @@ namespace skvm {
return regs[id];
};
auto arg = [&](int ix) {
SkASSERT(0 <= ix && ix < nargs);
SkASSERT(0 <= ix && ix < (int)fStrides.size());
return args[ix];
};
@ -1859,25 +1860,23 @@ namespace skvm {
}
bool Program::hasJIT() const {
return fJITBuf != nullptr;
return fJITEntry != nullptr;
}
void Program::dropJIT() {
#if defined(SKVM_JIT)
if (fJITBuf) {
munmap(fJITBuf, fJITSize);
}
if (fDylibHandle) {
dlclose(fDylibHandle);
if (fDylib) {
dlclose(fDylib);
} else if (fJITEntry) {
munmap(fJITEntry, fJITSize);
}
#else
SkASSERT(!this->hasJIT());
#endif
fJITBuf = nullptr;
fJITSize = 0;
fDylibHandle = nullptr;
fDylibEntry = nullptr;
fJITEntry = nullptr;
fJITSize = 0;
fDylib = nullptr;
}
Program::~Program() { this->dropJIT(); }
@ -1887,12 +1886,10 @@ namespace skvm {
fRegs = other.fRegs;
fLoop = other.fLoop;
fStrides = std::move(other.fStrides);
fOriginalProgram = std::move(other.fOriginalProgram);
std::swap(fJITBuf , other.fJITBuf);
std::swap(fJITSize , other.fJITSize);
std::swap(fDylibHandle, other.fDylibHandle);
std::swap(fDylibEntry , other.fDylibEntry);
std::swap(fJITEntry, other.fJITEntry);
std::swap(fJITSize , other.fJITSize);
std::swap(fDylib , other.fDylib);
}
Program& Program::operator=(Program&& other) {
@ -1900,12 +1897,10 @@ namespace skvm {
fRegs = other.fRegs;
fLoop = other.fLoop;
fStrides = std::move(other.fStrides);
fOriginalProgram = std::move(other.fOriginalProgram);
std::swap(fJITBuf , other.fJITBuf);
std::swap(fJITSize , other.fJITSize);
std::swap(fDylibHandle, other.fDylibHandle);
std::swap(fDylibEntry , other.fDylibEntry);
std::swap(fJITEntry, other.fJITEntry);
std::swap(fJITSize , other.fJITSize);
std::swap(fDylib , other.fDylib);
return *this;
}
@ -1915,7 +1910,6 @@ namespace skvm {
const std::vector<int>& strides,
const char* debug_name)
: fStrides(strides)
, fOriginalProgram(instructions)
{
this->setupInterpreter(instructions);
#if 1 && defined(SKVM_JIT)
@ -2066,7 +2060,6 @@ namespace skvm {
#if 0
SkDebugfStream stream;
this->dump(&stream);
dump_builder_program(fOriginalProgram, &stream);
return true;
#else
return false;
@ -2658,38 +2651,39 @@ namespace skvm {
// Allocate space that we can remap as executable.
const size_t page = sysconf(_SC_PAGESIZE);
fJITSize = ((a.size() + page - 1) / page) * page; // mprotect works at page granularity.
fJITBuf = mmap(nullptr,fJITSize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1,0);
fJITEntry = mmap(nullptr,fJITSize, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1,0);
// Assemble the program for real.
a = Assembler{fJITBuf};
a = Assembler{fJITEntry};
SkAssertResult(this->jit(instructions, try_hoisting, &a));
SkASSERT(a.size() <= fJITSize);
// Remap as executable, and flush caches on platforms that need that.
mprotect(fJITBuf, fJITSize, PROT_READ|PROT_EXEC);
__builtin___clear_cache((char*)fJITBuf,
(char*)fJITBuf + fJITSize);
mprotect(fJITEntry, fJITSize, PROT_READ|PROT_EXEC);
__builtin___clear_cache((char*)fJITEntry,
(char*)fJITEntry + fJITSize);
// For profiling and debugging, it's helpful to have this code loaded
// dynamically rather than just jumping info fJITBuf.
// dynamically rather than just jumping info fJITEntry.
if (gSkVMJITViaDylib) {
// Dump the raw program binary.
SkString path = SkStringPrintf("/tmp/%s.XXXXXX", debug_name);
int fd = mkstemp(path.writable_str());
::write(fd, fJITBuf, a.size());
::write(fd, fJITEntry, a.size());
close(fd);
// Convert it to an shared library with a single symbol "skvm_jit":
this->dropJIT(); // (unmap and null out fJITEntry.)
// Convert it in-place to a dynamic library with a single symbol "skvm_jit":
SkString cmd = SkStringPrintf(
"echo '.global _skvm_jit\n_skvm_jit: .incbin \"%s\"'"
" | clang -x assembler -shared - -o %s.lib",
" | clang -x assembler -shared - -o %s",
path.c_str(), path.c_str());
system(cmd.c_str());
// Load that dynamic library and look up skvm_jit().
path += ".lib";
fDylibHandle = dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL);
fDylibEntry = dlsym(fDylibHandle, "skvm_jit");
fDylib = dlopen(path.c_str(), RTLD_NOW|RTLD_LOCAL);
fJITEntry = dlsym(fDylib, "skvm_jit");
}
}
#endif

View File

@ -639,6 +639,8 @@ namespace skvm {
void setupInterpreter(const std::vector<Builder::Instruction>&);
void setupJIT (const std::vector<Builder::Instruction>&, const char* debug_name);
void interpret(int n, void* args[]) const;
bool jit(const std::vector<Builder::Instruction>&,
bool try_hoisting,
Assembler*) const;
@ -648,13 +650,9 @@ namespace skvm {
int fLoop = 0;
std::vector<int> fStrides;
// We only hang onto these to help debugging.
std::vector<Builder::Instruction> fOriginalProgram;
void* fJITBuf = nullptr;
size_t fJITSize = 0;
void* fDylibHandle = nullptr;
void* fDylibEntry = nullptr;
void* fJITEntry = nullptr;
size_t fJITSize = 0;
void* fDylib = nullptr;
};
// TODO: control flow