Rearranging code a bit to keep things cleaner.

This commit is contained in:
Ben Vanik
2013-01-21 10:58:52 -08:00
parent b91d550ef1
commit 95a8be078b
10 changed files with 281 additions and 233 deletions

View 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) {
//
}

View 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);
}

View File

@@ -0,0 +1,7 @@
# Copyright 2013 Ben Vanik. All Rights Reserved.
{
'sources': [
'function_generator.cc',
'module_generator.cc',
],
}