Now tracking register accesses for each function.
This commit is contained in:
parent
d1333db404
commit
d3054839b7
|
@ -91,7 +91,8 @@ public:
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void GenerateSharedBlocks();
|
void GenerateSharedBlocks();
|
||||||
void GenerateBasicBlock(sdb::FunctionBlock* block, llvm::BasicBlock* bb);
|
void PrepareBasicBlock(sdb::FunctionBlock* block);
|
||||||
|
void GenerateBasicBlock(sdb::FunctionBlock* block);
|
||||||
|
|
||||||
void setup_xer();
|
void setup_xer();
|
||||||
void setup_lr();
|
void setup_lr();
|
||||||
|
@ -120,6 +121,7 @@ private:
|
||||||
// Address of the instruction being generated.
|
// Address of the instruction being generated.
|
||||||
uint32_t cia_;
|
uint32_t cia_;
|
||||||
|
|
||||||
|
ppc::InstrAccessBits access_bits_;
|
||||||
struct {
|
struct {
|
||||||
llvm::Value* indirection_target;
|
llvm::Value* indirection_target;
|
||||||
llvm::Value* indirection_cia;
|
llvm::Value* indirection_cia;
|
||||||
|
|
|
@ -250,6 +250,25 @@ typedef struct {
|
||||||
} InstrOperand;
|
} InstrOperand;
|
||||||
|
|
||||||
|
|
||||||
|
class InstrAccessBits {
|
||||||
|
public:
|
||||||
|
InstrAccessBits() : spr(0), cr(0), gpr(0), fpr(0) {}
|
||||||
|
|
||||||
|
// Bitmasks derived from the accesses to registers.
|
||||||
|
// Format is 2 bits for each register, even bits indicating reads and odds
|
||||||
|
// indicating writes.
|
||||||
|
uint64_t spr; // fpcsr/ctr/lr/xer
|
||||||
|
uint64_t cr; // cr7/6/5/4/3/2/1/0
|
||||||
|
uint64_t gpr; // r31-0
|
||||||
|
uint64_t fpr; // f31-0
|
||||||
|
|
||||||
|
void Clear();
|
||||||
|
void Extend(InstrAccessBits& other);
|
||||||
|
void MarkAccess(InstrRegister& reg);
|
||||||
|
void Dump(std::string& out_str);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
class InstrDisasm {
|
class InstrDisasm {
|
||||||
public:
|
public:
|
||||||
enum Flags {
|
enum Flags {
|
||||||
|
@ -263,6 +282,7 @@ public:
|
||||||
char info[64];
|
char info[64];
|
||||||
std::vector<InstrOperand> operands;
|
std::vector<InstrOperand> operands;
|
||||||
std::vector<InstrRegister> special_registers;
|
std::vector<InstrRegister> special_registers;
|
||||||
|
InstrAccessBits access_bits;
|
||||||
|
|
||||||
void Init(std::string name, std::string info, uint32_t flags);
|
void Init(std::string name, std::string info, uint32_t flags);
|
||||||
void AddLR(InstrRegister::Access access);
|
void AddLR(InstrRegister::Access access);
|
||||||
|
@ -274,11 +294,6 @@ public:
|
||||||
void AddUImmOperand(uint64_t value, size_t width, std::string display = "");
|
void AddUImmOperand(uint64_t value, size_t width, std::string display = "");
|
||||||
int Finish();
|
int Finish();
|
||||||
|
|
||||||
// TODO(benvanik): fast checks
|
|
||||||
uint64_t reg_mask;
|
|
||||||
uint64_t gpr_mask;
|
|
||||||
uint64_t fpr_mask;
|
|
||||||
|
|
||||||
void Dump(std::string& str, size_t pad = 8);
|
void Dump(std::string& str, size_t pad = 8);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -845,12 +845,22 @@ XEEMITTER(dcbst, 0x7C00006C, X )(FunctionGenerator& g, IRBuilder<>& b, I
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XEDISASMR(dcbt, 0x7C00022C, X )(InstrData& i, InstrDisasm& d) {
|
||||||
|
d.Init("dcbt", "Data Cache Block Touch", 0);
|
||||||
|
// TODO
|
||||||
|
return d.Finish();
|
||||||
|
}
|
||||||
XEEMITTER(dcbt, 0x7C00022C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(dcbt, 0x7C00022C, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||||
// No-op for now.
|
// No-op for now.
|
||||||
// TODO(benvanik): use @llvm.prefetch
|
// TODO(benvanik): use @llvm.prefetch
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XEDISASMR(dcbtst, 0x7C0001EC, X )(InstrData& i, InstrDisasm& d) {
|
||||||
|
d.Init("dcbtst", "Data Cache Block Touch for Store", 0);
|
||||||
|
// TODO
|
||||||
|
return d.Finish();
|
||||||
|
}
|
||||||
XEEMITTER(dcbtst, 0x7C0001EC, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
XEEMITTER(dcbtst, 0x7C0001EC, X )(FunctionGenerator& g, IRBuilder<>& b, InstrData& i) {
|
||||||
// No-op for now.
|
// No-op for now.
|
||||||
// TODO(benvanik): use @llvm.prefetch
|
// TODO(benvanik): use @llvm.prefetch
|
||||||
|
@ -947,8 +957,8 @@ void RegisterEmitCategoryMemory() {
|
||||||
XEREGISTEREMITTER(stfsx, 0x7C00052E);
|
XEREGISTEREMITTER(stfsx, 0x7C00052E);
|
||||||
XEREGISTEREMITTER(dcbf, 0x7C0000AC);
|
XEREGISTEREMITTER(dcbf, 0x7C0000AC);
|
||||||
XEREGISTEREMITTER(dcbst, 0x7C00006C);
|
XEREGISTEREMITTER(dcbst, 0x7C00006C);
|
||||||
XEREGISTEREMITTER(dcbt, 0x7C00022C);
|
XEREGISTERINSTR(dcbt, 0x7C00022C);
|
||||||
XEREGISTEREMITTER(dcbtst, 0x7C0001EC);
|
XEREGISTERINSTR(dcbtst, 0x7C0001EC);
|
||||||
XEREGISTEREMITTER(dcbz, 0x7C0007EC);
|
XEREGISTEREMITTER(dcbz, 0x7C0007EC);
|
||||||
XEREGISTEREMITTER(icbi, 0x7C0007AC);
|
XEREGISTEREMITTER(icbi, 0x7C0007AC);
|
||||||
}
|
}
|
||||||
|
|
|
@ -61,6 +61,8 @@ FunctionGenerator::FunctionGenerator(
|
||||||
external_indirection_block_ = NULL;
|
external_indirection_block_ = NULL;
|
||||||
bb_ = NULL;
|
bb_ = NULL;
|
||||||
|
|
||||||
|
access_bits_.Clear();
|
||||||
|
|
||||||
locals_.indirection_target = NULL;
|
locals_.indirection_target = NULL;
|
||||||
locals_.indirection_cia = NULL;
|
locals_.indirection_cia = NULL;
|
||||||
|
|
||||||
|
@ -146,21 +148,18 @@ void FunctionGenerator::GenerateBasicBlocks() {
|
||||||
return_block_ = BasicBlock::Create(*context_, "return", gen_fn_);
|
return_block_ = BasicBlock::Create(*context_, "return", gen_fn_);
|
||||||
|
|
||||||
// Pass 1 creates all of the blocks - this way we can branch to them.
|
// Pass 1 creates all of the blocks - this way we can branch to them.
|
||||||
|
// We also track registers used so that when know which ones to fill/spill.
|
||||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
||||||
it != fn_->blocks.end(); ++it) {
|
it != fn_->blocks.end(); ++it) {
|
||||||
FunctionBlock* block = it->second;
|
FunctionBlock* block = it->second;
|
||||||
|
PrepareBasicBlock(block);
|
||||||
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));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pass 2 fills in instructions.
|
// Pass 2 fills in instructions.
|
||||||
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
for (std::map<uint32_t, FunctionBlock*>::iterator it = fn_->blocks.begin();
|
||||||
it != fn_->blocks.end(); ++it) {
|
it != fn_->blocks.end(); ++it) {
|
||||||
FunctionBlock* block = it->second;
|
FunctionBlock* block = it->second;
|
||||||
GenerateBasicBlock(block, GetBasicBlock(block->start_address));
|
GenerateBasicBlock(block);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Setup the shared return/indirection/etc blocks now that we know all the
|
// Setup the shared return/indirection/etc blocks now that we know all the
|
||||||
|
@ -215,10 +214,49 @@ void FunctionGenerator::GenerateSharedBlocks() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block,
|
void FunctionGenerator::PrepareBasicBlock(FunctionBlock* block) {
|
||||||
BasicBlock* bb) {
|
// Create the basic block that will end up getting filled during
|
||||||
|
// generation.
|
||||||
|
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));
|
||||||
|
|
||||||
|
// Scan and disassemble each instruction in the block to get accurate
|
||||||
|
// register access bits. In the future we could do other optimization checks
|
||||||
|
// in this pass.
|
||||||
|
// TODO(benvanik): perhaps we want to stash this for each basic block?
|
||||||
|
// We could use this for faster checking of cr/ca checks/etc.
|
||||||
|
InstrAccessBits access_bits;
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Ignore unknown or ones with no disassembler fn.
|
||||||
|
if (!i.type || !i.type->disassemble) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
ppc::InstrDisasm d;
|
||||||
|
i.type->disassemble(i, d);
|
||||||
|
|
||||||
|
// Accumulate access bits.
|
||||||
|
access_bits.Extend(d.access_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add in access bits to function access bits.
|
||||||
|
access_bits_.Extend(access_bits);
|
||||||
|
}
|
||||||
|
|
||||||
|
void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block) {
|
||||||
IRBuilder<>& b = *builder_;
|
IRBuilder<>& b = *builder_;
|
||||||
|
|
||||||
|
BasicBlock* bb = GetBasicBlock(block->start_address);
|
||||||
|
XEASSERTNOTNULL(bb);
|
||||||
|
|
||||||
printf(" bb %.8X-%.8X:\n", block->start_address, block->end_address);
|
printf(" bb %.8X-%.8X:\n", block->start_address, block->end_address);
|
||||||
|
|
||||||
fn_block_ = block;
|
fn_block_ = block;
|
||||||
|
@ -268,7 +306,7 @@ void FunctionGenerator::GenerateBasicBlock(FunctionBlock* block,
|
||||||
d.Dump(disasm);
|
d.Dump(disasm);
|
||||||
printf(" %.8X: %.8X %s\n", ia, i.code, disasm.c_str());
|
printf(" %.8X: %.8X %s\n", ia, i.code, disasm.c_str());
|
||||||
} else {
|
} else {
|
||||||
printf(" %.8X: %.8X %s\n", ia, i.code, i.type->name);
|
printf(" %.8X: %.8X %s ???\n", ia, i.code, i.type->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(benvanik): debugging information? source/etc?
|
// TODO(benvanik): debugging information? source/etc?
|
||||||
|
|
|
@ -17,9 +17,136 @@
|
||||||
using namespace xe::cpu::ppc;
|
using namespace xe::cpu::ppc;
|
||||||
|
|
||||||
|
|
||||||
|
void InstrAccessBits::Clear() {
|
||||||
|
spr = cr = gpr = fpr = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrAccessBits::Extend(InstrAccessBits& other) {
|
||||||
|
spr |= other.spr;
|
||||||
|
cr |= other.cr;
|
||||||
|
gpr |= other.gpr;
|
||||||
|
fpr |= other.fpr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrAccessBits::MarkAccess(InstrRegister& reg) {
|
||||||
|
uint64_t bits = 0;
|
||||||
|
if (reg.access & InstrRegister::kRead) {
|
||||||
|
bits |= 0x1;
|
||||||
|
}
|
||||||
|
if (reg.access & InstrRegister::kWrite) {
|
||||||
|
bits |= 0x2;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (reg.set) {
|
||||||
|
case InstrRegister::kXER:
|
||||||
|
spr |= bits << (2 * 0);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kLR:
|
||||||
|
spr |= bits << (2 * 1);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kCTR:
|
||||||
|
spr |= bits << (2 * 2);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kCR:
|
||||||
|
cr |= bits << (2 * reg.ordinal);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kFPSCR:
|
||||||
|
spr |= bits << (2 * 3);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kGPR:
|
||||||
|
gpr |= bits << (2 * reg.ordinal);
|
||||||
|
break;
|
||||||
|
case InstrRegister::kFPR:
|
||||||
|
fpr |= bits << (2 * reg.ordinal);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
case InstrRegister::kVMX:
|
||||||
|
XEASSERTALWAYS();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstrAccessBits::Dump(std::string& out_str) {
|
||||||
|
std::stringstream str;
|
||||||
|
if (spr) {
|
||||||
|
uint64_t spr_t = spr;
|
||||||
|
if (spr_t & 0x3) {
|
||||||
|
str << "XER [";
|
||||||
|
str << ((spr_t & 1) ? "R" : " ");
|
||||||
|
str << ((spr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
spr_t >>= 2;
|
||||||
|
if (spr_t & 0x3) {
|
||||||
|
str << "LR [";
|
||||||
|
str << ((spr_t & 1) ? "R" : " ");
|
||||||
|
str << ((spr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
spr_t >>= 2;
|
||||||
|
if (spr_t & 0x3) {
|
||||||
|
str << "CTR [";
|
||||||
|
str << ((spr_t & 1) ? "R" : " ");
|
||||||
|
str << ((spr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
spr_t >>= 2;
|
||||||
|
if (spr_t & 0x3) {
|
||||||
|
str << "FPCSR [";
|
||||||
|
str << ((spr_t & 1) ? "R" : " ");
|
||||||
|
str << ((spr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
spr_t >>= 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cr) {
|
||||||
|
uint64_t cr_t = cr;
|
||||||
|
for (size_t n = 0; n < 8; n++) {
|
||||||
|
if (cr_t & 0x3) {
|
||||||
|
str << "cr" << n << " [";
|
||||||
|
str << ((cr_t & 1) ? "R" : " ");
|
||||||
|
str << ((cr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
cr_t >>= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (gpr) {
|
||||||
|
uint64_t gpr_t = gpr;
|
||||||
|
for (size_t n = 0; n < 32; n++) {
|
||||||
|
if (gpr_t & 0x3) {
|
||||||
|
str << "r" << n << " [";
|
||||||
|
str << ((gpr_t & 1) ? "R" : " ");
|
||||||
|
str << ((gpr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
gpr_t >>= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fpr) {
|
||||||
|
uint64_t fpr_t = fpr;
|
||||||
|
for (size_t n = 0; n < 32; n++) {
|
||||||
|
if (fpr_t & 0x3) {
|
||||||
|
str << "f" << n << " [";
|
||||||
|
str << ((fpr_t & 1) ? "R" : " ");
|
||||||
|
str << ((fpr_t & 2) ? "W" : " ");
|
||||||
|
str << "] ";
|
||||||
|
}
|
||||||
|
fpr_t >>= 2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out_str = str.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void InstrDisasm::Init(std::string name, std::string info, uint32_t flags) {
|
void InstrDisasm::Init(std::string name, std::string info, uint32_t flags) {
|
||||||
operands.clear();
|
operands.clear();
|
||||||
special_registers.clear();
|
special_registers.clear();
|
||||||
|
access_bits.Clear();
|
||||||
|
|
||||||
if (flags & InstrDisasm::kOE) {
|
if (flags & InstrDisasm::kOE) {
|
||||||
name += "o";
|
name += "o";
|
||||||
|
@ -173,11 +300,16 @@ void InstrDisasm::AddUImmOperand(uint64_t value, size_t width,
|
||||||
}
|
}
|
||||||
|
|
||||||
int InstrDisasm::Finish() {
|
int InstrDisasm::Finish() {
|
||||||
// TODO(benvanik): setup fast checks
|
for (std::vector<InstrOperand>::iterator it = operands.begin();
|
||||||
reg_mask = 0;
|
it != operands.end(); ++it) {
|
||||||
gpr_mask = 0;
|
if (it->type == InstrOperand::kRegister) {
|
||||||
fpr_mask = 0;
|
access_bits.MarkAccess(it->reg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (std::vector<InstrRegister>::iterator it = special_registers.begin();
|
||||||
|
it != special_registers.end(); ++it) {
|
||||||
|
access_bits.MarkAccess(*it);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue