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

@ -7,34 +7,31 @@
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_CPU_PPC_INSTR_CONTEXT_H_ #ifndef XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#define XENIA_CPU_PPC_INSTR_CONTEXT_H_ #define XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_
#include <xenia/cpu/ppc/instr.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 <xenia/cpu/sdb.h> #include <xenia/cpu/sdb.h>
#include <xenia/cpu/ppc/instr.h>
namespace llvm {
class LLVMContext;
class Module;
class Function;
class BasicBlock;
class Value;
}
namespace xe { namespace xe {
namespace cpu { namespace cpu {
namespace ppc { namespace codegen {
class InstrContext { class FunctionGenerator {
public: public:
InstrContext(xe_memory_ref memory, sdb::FunctionSymbol* fn, FunctionGenerator(xe_memory_ref memory, sdb::FunctionSymbol* fn,
llvm::LLVMContext* context, llvm::Module* gen_module, llvm::LLVMContext* context, llvm::Module* gen_module,
llvm::Function* gen_fn); llvm::Function* gen_fn);
~InstrContext(); ~FunctionGenerator();
sdb::FunctionSymbol* fn(); sdb::FunctionSymbol* fn();
llvm::LLVMContext* context(); llvm::LLVMContext* context();
@ -47,13 +44,18 @@ public:
llvm::Function* GetFunction(uint32_t addr); llvm::Function* GetFunction(uint32_t addr);
llvm::Value* cia_value(); llvm::Value* cia_value();
llvm::Value* nia_value();
void update_nia_value(llvm::Value* value);
llvm::Value* spr_value(uint32_t n);
void update_spr_value(uint32_t n, llvm::Value* value);
llvm::Value* cr_value(); void FlushRegisters();
void update_cr_value(llvm::Value* value);
llvm::Value* xer_value();
void update_xer_value(llvm::Value* value);
llvm::Value* lr_value();
void update_lr_value(llvm::Value* value);
llvm::Value* ctr_value();
void update_ctr_value(llvm::Value* value);
llvm::Value* cr_value(uint32_t n);
void update_cr_value(uint32_t n, llvm::Value* value);
llvm::Value* gpr_value(uint32_t n); llvm::Value* gpr_value(uint32_t n);
void update_gpr_value(uint32_t n, llvm::Value* value); void update_gpr_value(uint32_t n, llvm::Value* value);
@ -70,18 +72,28 @@ private:
llvm::LLVMContext* context_; llvm::LLVMContext* context_;
llvm::Module* gen_module_; llvm::Module* gen_module_;
llvm::Function* gen_fn_; llvm::Function* gen_fn_;
// TODO(benvanik): IRBuilder/etc llvm::IRBuilder<>* builder_;
std::map<uint32_t, llvm::BasicBlock*> bbs_; std::map<uint32_t, llvm::BasicBlock*> bbs_;
// Address of the instruction being generated. // Address of the instruction being generated.
uint32_t cia_; uint32_t cia_;
struct {
llvm::Value* xer;
llvm::Value* lr;
llvm::Value* ctr;
llvm::Value* cr[4];
llvm::Value* gpr[32];
} values_;
}; };
} // namespace ppc } // namespace codegen
} // namespace cpu } // namespace cpu
} // namespace xe } // namespace xe
#endif // XENIA_CPU_PPC_INSTR_CONTEXT_H_ #endif // XENIA_CPU_CODEGEN_FUNCTION_GENERATOR_H_

View File

@ -7,8 +7,8 @@
****************************************************************************** ******************************************************************************
*/ */
#ifndef XENIA_CPU_CODEGEN_H_ #ifndef XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
#define XENIA_CPU_CODEGEN_H_ #define XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_
#include <xenia/cpu/sdb.h> #include <xenia/cpu/sdb.h>
#include <xenia/core/memory.h> #include <xenia/core/memory.h>
@ -31,25 +31,24 @@ namespace cpu {
namespace codegen { namespace codegen {
class CodegenFunction { class ModuleGenerator {
public: public:
sdb::FunctionSymbol* symbol; ModuleGenerator(
llvm::FunctionType* function_type;
llvm::Function* function;
};
class CodegenContext {
public:
CodegenContext(
xe_memory_ref memory, kernel::ExportResolver* export_resolver, xe_memory_ref memory, kernel::ExportResolver* export_resolver,
kernel::UserModule* module, sdb::SymbolDatabase* sdb, kernel::UserModule* module, sdb::SymbolDatabase* sdb,
llvm::LLVMContext* context, llvm::Module* gen_module); llvm::LLVMContext* context, llvm::Module* gen_module);
~CodegenContext(); ~ModuleGenerator();
int GenerateModule(); int Generate();
private: private:
class CodegenFunction {
public:
sdb::FunctionSymbol* symbol;
llvm::FunctionType* function_type;
llvm::Function* function;
};
CodegenFunction* GetCodegenFunction(uint32_t address); CodegenFunction* GetCodegenFunction(uint32_t address);
void AddImports(); void AddImports();
@ -78,4 +77,4 @@ private:
} // namespace xe } // namespace xe
#endif // XENIA_CPU_CODEGEN_H_ #endif // XENIA_CPU_CODEGEN_MODULE_GENERATOR_H_

View File

@ -27,7 +27,7 @@ namespace llvm {
namespace xe { namespace xe {
namespace cpu { namespace cpu {
namespace codegen { namespace codegen {
class CodegenContext; class ModuleGenerator;
} }
} }
} }
@ -53,14 +53,14 @@ private:
int Init(); int Init();
int Uninit(); int Uninit();
xe_memory_ref memory_; xe_memory_ref memory_;
shared_ptr<kernel::ExportResolver> export_resolver_; shared_ptr<kernel::ExportResolver> export_resolver_;
kernel::UserModule* module_; kernel::UserModule* module_;
shared_ptr<llvm::ExecutionEngine> engine_; shared_ptr<llvm::ExecutionEngine> engine_;
shared_ptr<sdb::SymbolDatabase> sdb_; shared_ptr<sdb::SymbolDatabase> sdb_;
shared_ptr<llvm::LLVMContext> context_; shared_ptr<llvm::LLVMContext> context_;
shared_ptr<llvm::Module> gen_module_; shared_ptr<llvm::Module> gen_module_;
auto_ptr<codegen::CodegenContext> codegen_; auto_ptr<codegen::ModuleGenerator> codegen_;
}; };

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

@ -7,7 +7,7 @@
****************************************************************************** ******************************************************************************
*/ */
#include <xenia/cpu/codegen.h> #include <xenia/cpu/codegen/module_generator.h>
#include <llvm/DIBuilder.h> #include <llvm/DIBuilder.h>
#include <llvm/Linker.h> #include <llvm/Linker.h>
@ -24,18 +24,18 @@
#include <llvm/Transforms/IPO/PassManagerBuilder.h> #include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <xenia/cpu/ppc.h> #include <xenia/cpu/ppc.h>
#include "cpu/ppc/instr_context.h" #include <xenia/cpu/codegen/function_generator.h>
using namespace llvm; using namespace llvm;
using namespace xe; using namespace xe;
using namespace xe::cpu; using namespace xe::cpu;
using namespace xe::cpu::codegen; using namespace xe::cpu::codegen;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb; using namespace xe::cpu::sdb;
using namespace xe::kernel; using namespace xe::kernel;
CodegenContext::CodegenContext( ModuleGenerator::ModuleGenerator(
xe_memory_ref memory, ExportResolver* export_resolver, xe_memory_ref memory, ExportResolver* export_resolver,
UserModule* module, SymbolDatabase* sdb, UserModule* module, SymbolDatabase* sdb,
LLVMContext* context, Module* gen_module) { LLVMContext* context, Module* gen_module) {
@ -47,11 +47,11 @@ CodegenContext::CodegenContext(
gen_module_ = gen_module; gen_module_ = gen_module;
} }
CodegenContext::~CodegenContext() { ModuleGenerator::~ModuleGenerator() {
xe_memory_release(memory_); xe_memory_release(memory_);
} }
int CodegenContext::GenerateModule() { int ModuleGenerator::Generate() {
std::string error_message; std::string error_message;
// Setup a debug info builder. // Setup a debug info builder.
@ -111,7 +111,8 @@ int CodegenContext::GenerateModule() {
return 0; return 0;
} }
CodegenFunction* CodegenContext::GetCodegenFunction(uint32_t address) { ModuleGenerator::CodegenFunction* ModuleGenerator::GetCodegenFunction(
uint32_t address) {
std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address); std::map<uint32_t, CodegenFunction*>::iterator it = functions_.find(address);
if (it != functions_.end()) { if (it != functions_.end()) {
return it->second; return it->second;
@ -119,7 +120,7 @@ CodegenFunction* CodegenContext::GetCodegenFunction(uint32_t address) {
return NULL; return NULL;
} }
void CodegenContext::AddMissingImport(FunctionSymbol* fn) { void ModuleGenerator::AddMissingImport(FunctionSymbol* fn) {
Module* m = gen_module_; Module* m = gen_module_;
LLVMContext& context = m->getContext(); LLVMContext& context = m->getContext();
@ -158,14 +159,14 @@ void CodegenContext::AddMissingImport(FunctionSymbol* fn) {
// implemented ? " " : "!!", name); // implemented ? " " : "!!", name);
} }
void CodegenContext::AddPresentImport(FunctionSymbol* fn) { void ModuleGenerator::AddPresentImport(FunctionSymbol* fn) {
// Module *m = gen_module_; // Module *m = gen_module_;
// LLVMContext& context = m->getContext(); // LLVMContext& context = m->getContext();
// TODO(benvanik): add import thunk code. // TODO(benvanik): add import thunk code.
} }
void CodegenContext::PrepareFunction(FunctionSymbol* fn) { void ModuleGenerator::PrepareFunction(FunctionSymbol* fn) {
Module* m = gen_module_; Module* m = gen_module_;
LLVMContext& context = m->getContext(); LLVMContext& context = m->getContext();
@ -177,7 +178,7 @@ void CodegenContext::PrepareFunction(FunctionSymbol* fn) {
AttributeSet attrs = AttributeSet::get(context, awi); AttributeSet attrs = AttributeSet::get(context, awi);
std::vector<Type*> args; std::vector<Type*> args;
Type* return_type = Type::getInt32Ty(context); Type* return_type = Type::getVoidTy(context);
char name[64]; char name[64];
char* pname = name; char* pname = name;
@ -202,16 +203,16 @@ void CodegenContext::PrepareFunction(FunctionSymbol* fn) {
fn->start_address, cgf)); fn->start_address, cgf));
} }
void CodegenContext::BuildFunction(CodegenFunction* cgf) { void ModuleGenerator::BuildFunction(CodegenFunction* cgf) {
FunctionSymbol* fn = cgf->symbol; FunctionSymbol* fn = cgf->symbol;
printf("%s:\n", fn->name); printf("%s:\n", fn->name);
// Setup the generation context. // Setup the generation context.
InstrContext ic(memory_, fn, context_, gen_module_, cgf->function); FunctionGenerator fgen(memory_, fn, context_, gen_module_, cgf->function);
// Run through and generate each basic block. // Run through and generate each basic block.
ic.GenerateBasicBlocks(); fgen.GenerateBasicBlocks();
// Run the optimizer on the function. // Run the optimizer on the function.
// Doing this here keeps the size of the IR small and speeds up the later // Doing this here keeps the size of the IR small and speeds up the later
@ -219,7 +220,7 @@ void CodegenContext::BuildFunction(CodegenFunction* cgf) {
OptimizeFunction(gen_module_, cgf->function); OptimizeFunction(gen_module_, cgf->function);
} }
void CodegenContext::OptimizeFunction(Module* m, Function* fn) { void ModuleGenerator::OptimizeFunction(Module* m, Function* fn) {
FunctionPassManager pm(m); FunctionPassManager pm(m);
PassManagerBuilder pmb; PassManagerBuilder pmb;
pmb.OptLevel = 3; pmb.OptLevel = 3;

View File

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

View File

@ -27,7 +27,7 @@
#include <llvm/Transforms/IPO.h> #include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h> #include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <xenia/cpu/codegen.h> #include <xenia/cpu/codegen/module_generator.h>
#include <xenia/cpu/sdb.h> #include <xenia/cpu/sdb.h>
#include "cpu/xethunk/xethunk.h" #include "cpu/xethunk/xethunk.h"
@ -115,10 +115,10 @@ int ExecModule::Prepare() {
&error_message); &error_message);
// Build the module from the source code. // Build the module from the source code.
codegen_ = auto_ptr<CodegenContext>(new CodegenContext( codegen_ = auto_ptr<ModuleGenerator>(new ModuleGenerator(
memory_, export_resolver_.get(), module_, sdb_.get(), memory_, export_resolver_.get(), module_, sdb_.get(),
context_.get(), gen_module_.get())); context_.get(), gen_module_.get()));
XEEXPECTZERO(codegen_->GenerateModule()); XEEXPECTZERO(codegen_->Generate());
// Write to cache. // Write to cache.
outs = auto_ptr<raw_ostream>(new raw_fd_ostream( outs = auto_ptr<raw_ostream>(new raw_fd_ostream(

View File

@ -1,162 +0,0 @@
/**
******************************************************************************
* 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 "cpu/ppc/instr_context.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>
using namespace llvm;
using namespace xe::cpu::ppc;
using namespace xe::cpu::sdb;
InstrContext::InstrContext(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;
}
InstrContext::~InstrContext() {
}
FunctionSymbol* InstrContext::fn() {
return fn_;
}
llvm::LLVMContext* InstrContext::context() {
return context_;
}
llvm::Module* InstrContext::gen_module() {
return gen_module_;
}
llvm::Function* InstrContext::gen_fn() {
return gen_fn_;
}
void InstrContext::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 InstrContext::GenerateBasicBlock(FunctionBlock* block, BasicBlock* bb) {
printf(" bb %.8X-%.8X:\n", block->start_address, block->end_address);
//builder.SetCurrentDebugLocation(DebugLoc::get(fn->start_address >> 8, fn->start_address & 0xFF, ctx->cu));
//i->setMetadata("some.name", MDNode::get(context, MDString::get(context, pname)));
IRBuilder<> builder(bb);
// 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);
// stash current bb?
// stash builder
//emit(this, i);
}
Value* tmp = builder.getInt32(0);
builder.CreateRet(tmp);
// TODO(benvanik): finish up BB
}
BasicBlock* InstrContext::GetBasicBlock(uint32_t address) {
std::map<uint32_t, BasicBlock*>::iterator it = bbs_.find(address);
if (it != bbs_.end()) {
return it->second;
}
return NULL;
}
Function* InstrContext::GetFunction(uint32_t addr) {
return NULL;
}
Value* InstrContext::cia_value() {
return NULL;
}
Value* InstrContext::nia_value() {
return NULL;
}
void InstrContext::update_nia_value(Value* value) {
//
}
Value* InstrContext::spr_value(uint32_t n) {
return NULL;
}
void InstrContext::update_spr_value(uint32_t n, Value* value) {
//
}
Value* InstrContext::cr_value() {
return NULL;
}
void InstrContext::update_cr_value(Value* value) {
//
}
Value* InstrContext::gpr_value(uint32_t n) {
return NULL;
}
void InstrContext::update_gpr_value(uint32_t n, Value* value) {
//
}
Value* InstrContext::memory_addr(uint32_t addr) {
return NULL;
}
Value* InstrContext::read_memory(Value* addr, uint32_t size, bool extend) {
return NULL;
}
void InstrContext::write_memory(Value* addr, uint32_t size, Value* value) {
//
}

View File

@ -2,6 +2,5 @@
{ {
'sources': [ 'sources': [
'instr.cc', 'instr.cc',
'instr_context.cc',
], ],
} }

View File

@ -1,13 +1,13 @@
# Copyright 2013 Ben Vanik. All Rights Reserved. # Copyright 2013 Ben Vanik. All Rights Reserved.
{ {
'sources': [ 'sources': [
'codegen.cc',
'exec_module.cc', 'exec_module.cc',
'processor.cc', 'processor.cc',
'sdb.cc', 'sdb.cc',
], ],
'includes': [ 'includes': [
'codegen/sources.gypi',
'ppc/sources.gypi', 'ppc/sources.gypi',
], ],
} }