Jit/compiler.cpp (215 lines of code) (raw):
// Copyright (c) Facebook, Inc. and its affiliates. (http://www.facebook.com)
#include "Jit/compiler.h"
#include "Python.h"
#include "Jit/disassembler.h"
#include "Jit/hir/analysis.h"
#include "Jit/hir/builder.h"
#include "Jit/hir/optimization.h"
#include "Jit/hir/preload.h"
#include "Jit/hir/printer.h"
#include "Jit/hir/ssa.h"
#include "Jit/jit_time_log.h"
#include "Jit/log.h"
#include <json.hpp>
#include <chrono>
#include <fstream>
namespace jit {
ThreadedCompileContext g_threaded_compile_context;
void CompiledFunction::Disassemble() const {
JIT_CHECK(false, "Disassemble() cannot be called in a release build.");
}
void CompiledFunction::PrintHIR() const {
JIT_CHECK(false, "PrintHIR() cannot be called in a release build.");
}
void CompiledFunctionDebug::Disassemble() const {
disassemble(
reinterpret_cast<const char*>(entry_point()),
GetCodeSize(),
reinterpret_cast<vma_t>(entry_point()));
}
void CompiledFunctionDebug::PrintHIR() const {
jit::hir::HIRPrinter printer;
printer.Print(*irfunc_.get());
}
struct PassTimer {
explicit PassTimer() : start(std::chrono::steady_clock::now()) {}
std::size_t finish() {
auto end = std::chrono::steady_clock::now();
return std::chrono::duration_cast<std::chrono::nanoseconds>(end - start)
.count();
}
std::chrono::steady_clock::time_point start;
};
template <typename T>
static void runPass(hir::Function& func, PostPassFunction callback) {
T pass;
COMPILE_TIMER(func.compilation_phase_timer,
pass.name(),
JIT_LOGIF(
g_dump_hir_passes,
"HIR for %s before pass %s:\n%s",
func.fullname,
pass.name(),
func);
PassTimer timer;
pass.Run(func);
std::size_t time_ns = timer.finish();
callback(func, pass.name(), time_ns);
JIT_LOGIF(
g_dump_hir_passes,
"HIR for %s after pass %s:\n%s",
func.fullname,
pass.name(),
func);
JIT_DCHECK(
funcTypeChecks(func, std::cerr),
"Function %s failed type checking after pass %s:\n%s",
func.fullname,
pass.name(),
func);)
}
void Compiler::runPasses(jit::hir::Function& irfunc) {
PostPassFunction callback = [](hir::Function&, const char*, std::size_t) {};
runPasses(irfunc, callback);
}
void Compiler::runPasses(
jit::hir::Function& irfunc,
PostPassFunction callback) {
// SSAify must come first; nothing but SSAify should ever see non-SSA HIR.
runPass<jit::hir::SSAify>(irfunc, callback);
runPass<jit::hir::Simplify>(irfunc, callback);
runPass<jit::hir::DynamicComparisonElimination>(irfunc, callback);
runPass<jit::hir::GuardTypeRemoval>(irfunc, callback);
runPass<jit::hir::PhiElimination>(irfunc, callback);
if (_PyJIT_IsHIRInlinerEnabled()) {
runPass<jit::hir::InlineFunctionCalls>(irfunc, callback);
runPass<jit::hir::Simplify>(irfunc, callback);
}
runPass<jit::hir::DeadCodeElimination>(irfunc, callback);
runPass<jit::hir::RefcountInsertion>(irfunc, callback);
JIT_LOGIF(
g_dump_final_hir, "Optimized HIR for %s:\n%s", irfunc.fullname, irfunc);
}
std::unique_ptr<CompiledFunction> Compiler::Compile(
BorrowedRef<PyFunctionObject> func) {
JIT_CHECK(PyFunction_Check(func), "Expected PyFunctionObject");
JIT_CHECK(
!g_threaded_compile_context.compileRunning(),
"multi-thread compile must preload first");
return Compile(jit::hir::Preloader(func));
}
std::unique_ptr<CompiledFunction> Compiler::Compile(
const jit::hir::Preloader& preloader) {
const std::string& fullname = preloader.fullname();
if (!PyDict_CheckExact(preloader.globals())) {
JIT_DLOG(
"Refusing to compile %s: globals is a %.200s, not a dict",
fullname,
Py_TYPE(preloader.globals())->tp_name);
return nullptr;
}
PyObject* builtins = PyEval_GetBuiltins();
if (!PyDict_CheckExact(preloader.builtins())) {
JIT_DLOG(
"Refusing to compile %s: builtins is a %.200s, not a dict",
fullname,
Py_TYPE(builtins)->tp_name);
return nullptr;
}
JIT_DLOG("Compiling %s @ %p", fullname, preloader.code());
std::unique_ptr<CompilationPhaseTimer> compilation_phase_timer{nullptr};
if (captureCompilationTimeFor(fullname)) {
compilation_phase_timer = std::make_unique<CompilationPhaseTimer>(fullname);
compilation_phase_timer->start("Overall compilation");
compilation_phase_timer->start("Lowering into HIR");
}
std::unique_ptr<jit::hir::Function> irfunc(jit::hir::buildHIR(preloader));
if (nullptr != compilation_phase_timer) {
compilation_phase_timer->end();
}
if (irfunc == nullptr) {
JIT_DLOG("Lowering to HIR failed %s", fullname);
return nullptr;
}
if (g_dump_hir) {
JIT_LOG("Initial HIR for %s:\n%s", fullname, *irfunc);
}
if (nullptr != compilation_phase_timer) {
irfunc->setCompilationPhaseTimer(std::move(compilation_phase_timer));
}
std::unique_ptr<nlohmann::json> json{nullptr};
if (g_dump_hir_passes_json != nullptr) {
// TODO(emacs): For inlined functions, grab the sources from all the
// different functions inlined.
json.reset(new nlohmann::json());
nlohmann::json passes;
hir::JSONPrinter hir_printer;
passes.emplace_back(hir_printer.PrintSource(*irfunc));
passes.emplace_back(hir_printer.PrintBytecode(*irfunc));
PostPassFunction dump =
[&hir_printer, &passes](
hir::Function& func, const char* pass_name, std::size_t time_ns) {
hir_printer.Print(passes, func, pass_name, time_ns);
};
COMPILE_TIMER(
irfunc->compilation_phase_timer,
"HIR transformations",
Compiler::runPasses(*irfunc, dump))
(*json)["fullname"] = fullname;
(*json)["cols"] = passes;
} else {
COMPILE_TIMER(
irfunc->compilation_phase_timer,
"HIR transformations",
Compiler::runPasses(*irfunc))
}
auto ngen = ngen_factory_(irfunc.get());
if (ngen == nullptr) {
return nullptr;
}
if (g_dump_hir_passes_json != nullptr) {
ngen->SetJSONOutput(json.get());
}
void* entry = nullptr;
COMPILE_TIMER(
irfunc->compilation_phase_timer,
"Native code Generation",
entry = ngen->GetEntryPoint())
if (entry == nullptr) {
JIT_DLOG("Generating native code for %s failed", fullname);
return nullptr;
}
JIT_DLOG("Finished compiling %s", fullname);
if (nullptr != irfunc->compilation_phase_timer) {
irfunc->compilation_phase_timer->end();
irfunc->setCompilationPhaseTimer(nullptr);
}
int func_size = ngen->GetCompiledFunctionSize();
int stack_size = ngen->GetCompiledFunctionStackSize();
int spill_stack_size = ngen->GetCompiledFunctionSpillStackSize();
if (g_dump_hir_passes_json != nullptr) {
std::string filename =
fmt::format("{}/function_{}.json", g_dump_hir_passes_json, fullname);
std::ofstream json_file;
json_file.open(
filename,
std::ios_base::out | std::ios_base::trunc | std::ios_base::binary);
json_file << json->dump() << std::endl;
json_file.close();
}
if (g_debug) {
irfunc->setCompilationPhaseTimer(nullptr);
return std::make_unique<CompiledFunctionDebug>(
reinterpret_cast<vectorcallfunc>(entry),
ngen->codeRuntime(),
func_size,
stack_size,
spill_stack_size,
std::move(irfunc),
std::move(ngen));
} else {
return std::make_unique<CompiledFunction>(
reinterpret_cast<vectorcallfunc>(entry),
ngen->codeRuntime(),
func_size,
stack_size,
spill_stack_size);
}
}
} // namespace jit