Rearranging code a bit to keep things cleaner.
This commit is contained in:
192
src/cpu/codegen/function_generator.cc
Normal file
192
src/cpu/codegen/function_generator.cc
Normal file
@@ -0,0 +1,192 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::ppc;
|
||||
using namespace xe::cpu::sdb;
|
||||
|
||||
|
||||
/**
|
||||
* This generates function code.
|
||||
* One context is created for each function to generate. Each basic block in
|
||||
* the function is created and stashed in one pass, then filled in the next.
|
||||
*
|
||||
* This context object is a stateful representation of the current machine state
|
||||
* and all accessors to registers should occur through it. By doing so it's
|
||||
* possible to exploit the SSA nature of LLVM to reuse register values within
|
||||
* a function without needing to flush to memory.
|
||||
*
|
||||
* Function calls (any branch outside of the function) will result in an
|
||||
* expensive flush of registers.
|
||||
*
|
||||
* TODO(benvanik): track arguments by looking for register reads without writes
|
||||
* TODO(benvanik): avoid flushing registers for leaf nodes
|
||||
* TODO(benvnaik): pass return value in LLVM return, not by memory
|
||||
*/
|
||||
|
||||
|
||||
FunctionGenerator::FunctionGenerator(xe_memory_ref memory, FunctionSymbol* fn,
|
||||
LLVMContext* context, Module* gen_module,
|
||||
Function* gen_fn) {
|
||||
memory_ = memory;
|
||||
fn_ = fn;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
gen_fn_ = gen_fn;
|
||||
builder_ = new IRBuilder<>(*context_);
|
||||
}
|
||||
|
||||
FunctionGenerator::~FunctionGenerator() {
|
||||
delete builder_;
|
||||
}
|
||||
|
||||
FunctionSymbol* FunctionGenerator::fn() {
|
||||
return fn_;
|
||||
}
|
||||
|
||||
llvm::LLVMContext* FunctionGenerator::context() {
|
||||
return context_;
|
||||
}
|
||||
|
||||
llvm::Module* FunctionGenerator::gen_module() {
|
||||
return gen_module_;
|
||||
}
|
||||
|
||||
llvm::Function* FunctionGenerator::gen_fn() {
|
||||
return gen_fn_;
|
||||
}
|
||||
|
||||
void FunctionGenerator::GenerateBasicBlocks() {
|
||||
// Pass 1 creates all of the blocks - this way we can branch to them.
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
||||
it != fn_->blocks.end(); ++it) {
|
||||
FunctionBlock* block = it->second;
|
||||
|
||||
char name[32];
|
||||
xesnprintfa(name, XECOUNT(name), "loc_%.8X", block->start_address);
|
||||
BasicBlock* bb = BasicBlock::Create(*context_, name, gen_fn_);
|
||||
bbs_.insert(std::pair<uint32_t, BasicBlock*>(block->start_address, bb));
|
||||
}
|
||||
|
||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
||||
it != fn_->blocks.end(); ++it) {
|
||||
FunctionBlock* block = it->second;
|
||||
GenerateBasicBlock(block, GetBasicBlock(block->start_address));
|
||||
}
|
||||
}
|
||||
|
||||
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block,
|
||||
BasicBlock* bb) {
|
||||
printf(" bb %.8X-%.8X:\n", block->start_address, block->end_address);
|
||||
|
||||
// Move the builder to this block and setup.
|
||||
builder_->SetInsertPoint(bb);
|
||||
//i->setMetadata("some.name", MDNode::get(context, MDString::get(context, pname)));
|
||||
|
||||
// Walk instructions in block.
|
||||
uint8_t* p = xe_memory_addr(memory_, 0);
|
||||
for (uint32_t ia = block->start_address; ia <= block->end_address; ia += 4) {
|
||||
InstrData i;
|
||||
i.address = ia;
|
||||
i.code = XEGETUINT32BE(p + ia);
|
||||
i.type = ppc::GetInstrType(i.code);
|
||||
if (!i.type) {
|
||||
XELOGCPU("Invalid instruction at %.8X: %.8X\n", ia, i.code);
|
||||
continue;
|
||||
}
|
||||
printf(" %.8X: %.8X %s\n", ia, i.code, i.type->name);
|
||||
|
||||
// TODO(benvanik): debugging information? source/etc?
|
||||
// builder_>SetCurrentDebugLocation(DebugLoc::get(
|
||||
// ia >> 8, ia & 0xFF, ctx->cu));
|
||||
|
||||
//emit(this, i, builder);
|
||||
}
|
||||
|
||||
//Value* tmp = builder_->getInt32(0);
|
||||
builder_->CreateRetVoid();
|
||||
|
||||
// TODO(benvanik): finish up BB
|
||||
}
|
||||
|
||||
BasicBlock* FunctionGenerator::GetBasicBlock(uint32_t address) {
|
||||
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(address);
|
||||
if (it != bbs_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Function* FunctionGenerator::GetFunction(uint32_t addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::cia_value() {
|
||||
return builder_->getInt32(cia_);
|
||||
}
|
||||
|
||||
void FunctionGenerator::FlushRegisters() {
|
||||
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::xer_value() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_xer_value(Value* value) {
|
||||
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::lr_value() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_lr_value(Value* value) {
|
||||
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::ctr_value() {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_ctr_value(Value* value) {
|
||||
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::cr_value(uint32_t n) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_cr_value(uint32_t n, Value* value) {
|
||||
//
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::gpr_value(uint32_t n) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::update_gpr_value(uint32_t n, Value* value) {
|
||||
//
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::memory_addr(uint32_t addr) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
Value* FunctionGenerator::read_memory(Value* addr, uint32_t size, bool extend) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void FunctionGenerator::write_memory(Value* addr, uint32_t size, Value* value) {
|
||||
//
|
||||
}
|
||||
234
src/cpu/codegen/module_generator.cc
Normal file
234
src/cpu/codegen/module_generator.cc
Normal file
@@ -0,0 +1,234 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2013 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include <xenia/cpu/codegen/module_generator.h>
|
||||
|
||||
#include <llvm/DIBuilder.h>
|
||||
#include <llvm/Linker.h>
|
||||
#include <llvm/PassManager.h>
|
||||
#include <llvm/DebugInfo.h>
|
||||
#include <llvm/Analysis/Verifier.h>
|
||||
#include <llvm/IR/Attributes.h>
|
||||
#include <llvm/IR/DataLayout.h>
|
||||
#include <llvm/IR/DerivedTypes.h>
|
||||
#include <llvm/IR/IRBuilder.h>
|
||||
#include <llvm/IR/LLVMContext.h>
|
||||
#include <llvm/IR/Module.h>
|
||||
#include <llvm/Transforms/IPO.h>
|
||||
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
|
||||
|
||||
#include <xenia/cpu/ppc.h>
|
||||
#include <xenia/cpu/codegen/function_generator.h>
|
||||
|
||||
|
||||
using namespace llvm;
|
||||
using namespace xe;
|
||||
using namespace xe::cpu;
|
||||
using namespace xe::cpu::codegen;
|
||||
using namespace xe::cpu::sdb;
|
||||
using namespace xe::kernel;
|
||||
|
||||
|
||||
ModuleGenerator::ModuleGenerator(
|
||||
xe_memory_ref memory, ExportResolver* export_resolver,
|
||||
UserModule* module, SymbolDatabase* sdb,
|
||||
LLVMContext* context, Module* gen_module) {
|
||||
memory_ = xe_memory_retain(memory);
|
||||
export_resolver_ = export_resolver;
|
||||
module_ = module;
|
||||
sdb_ = sdb;
|
||||
context_ = context;
|
||||
gen_module_ = gen_module;
|
||||
}
|
||||
|
||||
ModuleGenerator::~ModuleGenerator() {
|
||||
xe_memory_release(memory_);
|
||||
}
|
||||
|
||||
int ModuleGenerator::Generate() {
|
||||
std::string error_message;
|
||||
|
||||
// Setup a debug info builder.
|
||||
// This is used when creating any debug info. We may want to go more
|
||||
// fine grained than this, but for now it's something.
|
||||
xechar_t dir[2048];
|
||||
XEIGNORE(xestrcpy(dir, XECOUNT(dir), module_->path()));
|
||||
xechar_t *slash = xestrrchr(dir, '/');
|
||||
if (slash) {
|
||||
*(slash + 1) = 0;
|
||||
}
|
||||
di_builder_ = new DIBuilder(*gen_module_);
|
||||
di_builder_->createCompileUnit(
|
||||
0,
|
||||
StringRef(module_->name()),
|
||||
StringRef(dir),
|
||||
StringRef("xenia"),
|
||||
true,
|
||||
StringRef(""),
|
||||
0);
|
||||
cu_ = (MDNode*)di_builder_->getCU();
|
||||
|
||||
// Add export wrappers.
|
||||
//
|
||||
|
||||
// Add all functions.
|
||||
// We do two passes - the first creates the function signature and global
|
||||
// value (so that we can call it), the second actually builds the function.
|
||||
std::vector<FunctionSymbol*> functions;
|
||||
if (!sdb_->GetAllFunctions(functions)) {
|
||||
for (std::vector<FunctionSymbol*>::iterator it = functions.begin();
|
||||
it != functions.end(); ++it) {
|
||||
FunctionSymbol* fn = *it;
|
||||
switch (fn->type) {
|
||||
case FunctionSymbol::User:
|
||||
PrepareFunction(fn);
|
||||
break;
|
||||
case FunctionSymbol::Kernel:
|
||||
if (fn->kernel_export && fn->kernel_export->IsImplemented()) {
|
||||
AddPresentImport(fn);
|
||||
} else {
|
||||
AddMissingImport(fn);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (std::map<uint32_t, CodegenFunction*>::iterator it =
|
||||
functions_.begin(); it != functions_.end(); ++it) {
|
||||
BuildFunction(it->second);
|
||||
}
|
||||
|
||||
di_builder_->finalize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
|
||||
uint32_t address) {
|
||||
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
|
||||
if (it != functions_.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
AttributeWithIndex awi[] = {
|
||||
//AttributeWithIndex::get(context, 2, Attributes::NoCapture),
|
||||
AttributeWithIndex::get(context,
|
||||
AttributeSet::FunctionIndex, Attribute::NoUnwind),
|
||||
};
|
||||
AttributeSet attrs = AttributeSet::get(context, awi);
|
||||
|
||||
std::vector<Type*> args;
|
||||
Type* return_type = Type::getInt32Ty(context);
|
||||
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function* f = cast<Function>(m->getOrInsertFunction(
|
||||
StringRef(fn->name), ft, attrs));
|
||||
f->setCallingConv(CallingConv::C);
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
// TODO(benvanik): log errors.
|
||||
BasicBlock* block = BasicBlock::Create(context, "entry", f);
|
||||
IRBuilder<> builder(block);
|
||||
Value* tmp = builder.getInt32(0);
|
||||
builder.CreateRet(tmp);
|
||||
|
||||
OptimizeFunction(m, f);
|
||||
|
||||
//GlobalAlias *alias = new GlobalAlias(f->getType(), GlobalValue::InternalLinkage, name, f, m);
|
||||
// printf(" F %.8X %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->thunk_address, info->ordinal,
|
||||
// info->ordinal, implemented ? " " : "!!", name);
|
||||
// For values:
|
||||
// printf(" V %.8X %.3X (%3d) %s %s\n",
|
||||
// info->value_address, info->ordinal, info->ordinal,
|
||||
// implemented ? " " : "!!", name);
|
||||
}
|
||||
|
||||
void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
|
||||
// Module *m = gen_module_;
|
||||
// LLVMContext& context = m->getContext();
|
||||
|
||||
// TODO(benvanik): add import thunk code.
|
||||
}
|
||||
|
||||
void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
|
||||
Module* m = gen_module_;
|
||||
LLVMContext& context = m->getContext();
|
||||
|
||||
AttributeWithIndex awi[] = {
|
||||
//AttributeWithIndex::get(context, 2, Attributes::NoCapture),
|
||||
AttributeWithIndex::get(context,
|
||||
AttributeSet::FunctionIndex, Attribute::NoUnwind),
|
||||
};
|
||||
AttributeSet attrs = AttributeSet::get(context, awi);
|
||||
|
||||
std::vector<Type*> args;
|
||||
Type* return_type = Type::getVoidTy(context);
|
||||
|
||||
char name[64];
|
||||
char* pname = name;
|
||||
if (fn->name) {
|
||||
pname = fn->name;
|
||||
} else {
|
||||
xesnprintfa(name, XECOUNT(name), "sub_%.8X", fn->start_address);
|
||||
}
|
||||
|
||||
FunctionType* ft = FunctionType::get(return_type,
|
||||
ArrayRef<Type*>(args), false);
|
||||
Function* f = cast<Function>(
|
||||
m->getOrInsertFunction(StringRef(pname), ft, attrs));
|
||||
f->setCallingConv(CallingConv::C);
|
||||
f->setVisibility(GlobalValue::DefaultVisibility);
|
||||
|
||||
CodegenFunction* cgf = new CodegenFunction();
|
||||
cgf->symbol = fn;
|
||||
cgf->function_type = ft;
|
||||
cgf->function = f;
|
||||
functions_.insert(std::pair<uint32_t, CodegenFunction*>(
|
||||
fn->start_address, cgf));
|
||||
}
|
||||
|
||||
void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
|
||||
FunctionSymbol* fn = cgf->symbol;
|
||||
|
||||
printf("%s:\n", fn->name);
|
||||
|
||||
// Setup the generation context.
|
||||
FunctionGenerator fgen(memory_, fn, context_, gen_module_, cgf->function);
|
||||
|
||||
// Run through and generate each basic block.
|
||||
fgen.GenerateBasicBlocks();
|
||||
|
||||
// Run the optimizer on the function.
|
||||
// Doing this here keeps the size of the IR small and speeds up the later
|
||||
// passes.
|
||||
OptimizeFunction(gen_module_, cgf->function);
|
||||
}
|
||||
|
||||
void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
|
||||
FunctionPassManager pm(m);
|
||||
PassManagerBuilder pmb;
|
||||
pmb.OptLevel = 3;
|
||||
pmb.SizeLevel = 0;
|
||||
pmb.Inliner = createFunctionInliningPass();
|
||||
pmb.Vectorize = true;
|
||||
pmb.LoopVectorize = true;
|
||||
pmb.populateFunctionPassManager(pm);
|
||||
pm.add(createVerifierPass());
|
||||
pm.run(*fn);
|
||||
}
|
||||
7
src/cpu/codegen/sources.gypi
Normal file
7
src/cpu/codegen/sources.gypi
Normal file
@@ -0,0 +1,7 @@
|
||||
# Copyright 2013 Ben Vanik. All Rights Reserved.
|
||||
{
|
||||
'sources': [
|
||||
'function_generator.cc',
|
||||
'module_generator.cc',
|
||||
],
|
||||
}
|
||||
Reference in New Issue
Block a user