mirror of
https://github.com/aengelke/rellume
synced 2026-06-21 13:46:57 +00:00
regfile: Handle PC specially
PC is not an ordinary register and has strong connections to the lifted CFG. Avoid relying on information encoded in (unused) LLVM-IR instructions for lifting and store this explicitly.
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
[
|
||||
{"name": "pc", "size": 8, "reg": ["IP", "I64"], "export": true},
|
||||
{"name": "pc", "size": 8, "reg": ["INVALID", "I64"], "export": true},
|
||||
{"name": "n", "size": 1, "reg": ["FLAG(1)", "I1"]},
|
||||
{"name": "z", "size": 1, "reg": ["FLAG(0)", "I1"]},
|
||||
{"name": "c", "size": 1, "reg": ["FLAG(3)", "I1"]},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[
|
||||
{"name": "rip", "size": 8, "reg": ["IP", "I64"], "export": true},
|
||||
{"name": "rip", "size": 8, "reg": ["INVALID", "I64"], "export": true},
|
||||
{"name": "x0", "size": 8, "reg": ["GP(0)", "I64"]},
|
||||
{"name": "x1", "size": 8, "reg": ["GP(1)", "I64"], "export": true},
|
||||
{"name": "x2", "size": 8, "reg": ["GP(2)", "I64"], "export": true},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
[
|
||||
{"name": "rip", "size": 8, "reg": ["IP", "I64"], "export": true},
|
||||
{"name": "rip", "size": 8, "reg": ["INVALID", "I64"], "export": true},
|
||||
{"name": "rax", "size": 8, "reg": ["GP(0)", "I64"], "export": true},
|
||||
{"name": "rcx", "size": 8, "reg": ["GP(1)", "I64"], "export": true},
|
||||
{"name": "rdx", "size": 8, "reg": ["GP(2)", "I64"], "export": true},
|
||||
|
||||
+10
-17
@@ -53,12 +53,11 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
|
||||
// Add instruction marker
|
||||
if (cfg.instr_marker) {
|
||||
llvm::Value* rip = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::StringRef str_ref{reinterpret_cast<const char*>(&inst),
|
||||
sizeof(FdInstr)};
|
||||
llvm::MDString* md = llvm::MDString::get(irb.getContext(), str_ref);
|
||||
llvm::Value* md_val = llvm::MetadataAsValue::get(irb.getContext(), md);
|
||||
irb.CreateCall(cfg.instr_marker, {rip, md_val});
|
||||
irb.CreateCall(cfg.instr_marker, {AddrIPRel(), md_val});
|
||||
}
|
||||
|
||||
// Check overridden implementations first.
|
||||
@@ -92,7 +91,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
case farmdec::A64_ADRP: {
|
||||
// Scaling to page granularity is already handled in farmdec, but we need to mask out
|
||||
// the bottom 12 bits.
|
||||
auto masked_pc = irb.CreateAnd(GetReg(ArchReg::IP, Facet::I64), irb.getInt64(~(uint64_t)4095));
|
||||
auto masked_pc = irb.CreateAnd(AddrIPRel(), irb.getInt64(~(uint64_t)4095));
|
||||
SetGp(a64.rd, /*w32=*/false, irb.CreateAdd(masked_pc, irb.getInt64(a64.offset)));
|
||||
break;
|
||||
}
|
||||
@@ -222,7 +221,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
SetGp(a64.rd, w32, Shift(GetGp(a64.rn, w32), farmdec::SH_ROR, a64.imm));
|
||||
break;
|
||||
case farmdec::A64_BCOND:
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(IsTrue(fad_get_cond(a64.flags)), PCRel(a64.offset), PCRel(inst.len())));
|
||||
SetIPCond(IsTrue(fad_get_cond(a64.flags)), inst.start() + a64.offset, inst.end());
|
||||
return true;
|
||||
case farmdec::A64_SVC:
|
||||
// SVC has an immediate, but cfg.syscall_implementation takes only a CPU state pointer.
|
||||
@@ -354,7 +353,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
SetIP(inst.start() + a64.offset);
|
||||
return true;
|
||||
case farmdec::A64_BR:
|
||||
SetReg(ArchReg::IP, GetGp(a64.rn, false));
|
||||
SetIP(GetGp(a64.rn, false));
|
||||
return true;
|
||||
case farmdec::A64_BL:
|
||||
case farmdec::A64_BLR: {
|
||||
@@ -365,7 +364,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
SetGp(30, false, ret_addr); // X30: Link Register (LR)
|
||||
|
||||
if (a64.op == farmdec::A64_BLR)
|
||||
SetReg(ArchReg::IP, GetGp(a64.rn, false));
|
||||
SetIP(GetGp(a64.rn, false));
|
||||
else
|
||||
SetIP(inst.start() + a64.offset);
|
||||
|
||||
@@ -374,9 +373,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
|
||||
// The external function call may manipulate the PC in non-obvious ways (e.g. exceptions).
|
||||
// See also the comment in the x86_64 LiftCall method.
|
||||
llvm::Value* cont_addr = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::Value* eq = irb.CreateICmpEQ(cont_addr, ret_addr);
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(eq, ret_addr, cont_addr)); // common case
|
||||
SetIPCallret(inst.end());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -384,7 +381,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
if (cfg.call_ret_clobber_flags)
|
||||
SetFlagUndef({ArchReg::OF, ArchReg::SF, ArchReg::ZF, ArchReg::CF});
|
||||
|
||||
SetReg(ArchReg::IP, GetGp(a64.rn, false));
|
||||
SetIP(GetGp(a64.rn, false));
|
||||
|
||||
if (cfg.call_function) {
|
||||
// If we are in call-ret-lifting mode, forcefully return. Otherwise, we
|
||||
@@ -397,9 +394,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
// CBZ: rt == 0; CBNZ: rt != 0
|
||||
auto pred = (a64.op == farmdec::A64_CBZ) ? llvm::CmpInst::Predicate::ICMP_EQ : llvm::CmpInst::Predicate::ICMP_NE;
|
||||
auto do_branch = irb.CreateICmp(pred, GetGp(a64.rt, w32), irb.getIntN(bits, 0));
|
||||
auto on_true = PCRel(a64.offset);
|
||||
auto on_false = PCRel(inst.len()); // next instr
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(do_branch, on_true, on_false));
|
||||
SetIPCond(do_branch, inst.start() + a64.offset, inst.end());
|
||||
return true;
|
||||
}
|
||||
case farmdec::A64_TBZ:
|
||||
@@ -411,9 +406,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
// TBZ: bit == 0; TBNZ: bit != 0
|
||||
auto pred = (a64.op == farmdec::A64_TBZ) ? llvm::CmpInst::Predicate::ICMP_EQ : llvm::CmpInst::Predicate::ICMP_NE;
|
||||
auto do_branch = irb.CreateICmp(pred, bit, irb.getIntN(bits, 0));
|
||||
auto on_true = PCRel(a64.tbz.offset);
|
||||
auto on_false = PCRel(inst.len()); // next instr
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(do_branch, on_true, on_false));
|
||||
SetIPCond(do_branch, inst.start() + a64.tbz.offset, inst.end());
|
||||
return true;
|
||||
}
|
||||
case farmdec::A64_UDIV: {
|
||||
@@ -1132,7 +1125,7 @@ llvm::AtomicOrdering Lifter::Ordering(farmdec::MemOrdering mo) {
|
||||
|
||||
// Returns PC-relative address as i64, suitable for storing into PC again.
|
||||
llvm::Value* Lifter::PCRel(uint64_t off) {
|
||||
return AddrIPRel(off, Facet::I64);
|
||||
return AddrIPRel(off);
|
||||
}
|
||||
|
||||
// Dispatches to the correct addressing mode handler.
|
||||
|
||||
@@ -199,6 +199,7 @@ static void Unpack(CallConv cconv, BasicBlock* bb, FunctionInfo& fi, F get_from_
|
||||
RegFile& regfile = *bb->GetRegFile();
|
||||
llvm::IRBuilder<> irb(regfile.GetInsertBlock());
|
||||
|
||||
regfile.SetPC(irb.CreateLoad(irb.getInt64Ty(), fi.sptr_raw));
|
||||
for (const auto& [sptr_idx, off, reg, facet] : CPUStructEntries(cconv)) {
|
||||
if (reg.Kind() == ArchReg::RegKind::INVALID)
|
||||
continue;
|
||||
@@ -310,6 +311,7 @@ void CallConv::OptimizePacks(FunctionInfo& fi, BasicBlock* entry) {
|
||||
if (!regfile.StartsClean())
|
||||
regset |= bb_map.lookup(pack.bb).first;
|
||||
llvm::IRBuilder<> irb(pack.packBefore);
|
||||
irb.CreateStore(regfile.GetPCValue(fi.pc_base_value, fi.pc_base_addr), fi.sptr_raw);
|
||||
for (const auto& [sptr_idx, off, reg, facet] : CPUStructEntries(*this)) {
|
||||
if (reg.Kind() == ArchReg::RegKind::INVALID)
|
||||
continue;
|
||||
|
||||
+19
-41
@@ -40,6 +40,7 @@
|
||||
#include <llvm/IR/Function.h>
|
||||
#include <llvm/IR/GlobalValue.h>
|
||||
#include <llvm/IR/InstIterator.h>
|
||||
#include <llvm/IR/Instructions.h>
|
||||
#include <llvm/IR/Intrinsics.h>
|
||||
#include <llvm/IR/Type.h>
|
||||
#include <llvm/IR/Verifier.h>
|
||||
@@ -61,7 +62,6 @@ class LiftHelper {
|
||||
std::unique_ptr<ArchBasicBlock> exit_block;
|
||||
|
||||
ArchBasicBlock& ResolveAddr(uint64_t addr);
|
||||
ArchBasicBlock& ResolveAddr(llvm::Value* addr);
|
||||
|
||||
public:
|
||||
LiftHelper(Function* func) : func(func) {}
|
||||
@@ -70,6 +70,8 @@ public:
|
||||
};
|
||||
|
||||
ArchBasicBlock& LiftHelper::ResolveAddr(uint64_t addr) {
|
||||
if (!addr)
|
||||
return *exit_block;
|
||||
auto block_it = block_map.find(addr);
|
||||
if (block_it != block_map.end())
|
||||
return *(block_it->second);
|
||||
@@ -90,37 +92,6 @@ ArchBasicBlock& LiftHelper::ResolveAddr(uint64_t addr) {
|
||||
return *(block_map[addr] = std::move(ab));
|
||||
}
|
||||
|
||||
ArchBasicBlock& LiftHelper::ResolveAddr(llvm::Value* addr) {
|
||||
uint64_t addr_skew = 0;
|
||||
|
||||
// Strip off the base_rip from the expression.
|
||||
// Trivial case: no offset has ever been added.
|
||||
if (addr == fi.pc_base_value) {
|
||||
addr_skew = fi.pc_base_addr;
|
||||
addr = llvm::ConstantInt::get(addr->getType(), 0);
|
||||
}
|
||||
// We can either have an instruction...
|
||||
if (auto binop = llvm::dyn_cast<llvm::BinaryOperator>(addr)) {
|
||||
if (binop->getOpcode() == llvm::Instruction::Add &&
|
||||
binop->getOperand(0) == fi.pc_base_value) {
|
||||
addr_skew = fi.pc_base_addr;
|
||||
addr = binop->getOperand(1);
|
||||
}
|
||||
}
|
||||
// ... or a constant expression for this.
|
||||
if (auto expr = llvm::dyn_cast<llvm::ConstantExpr>(addr)) {
|
||||
if (expr->getOpcode() == llvm::Instruction::Add &&
|
||||
expr->getOperand(0) == fi.pc_base_value) {
|
||||
addr_skew = fi.pc_base_addr;
|
||||
addr = expr->getOperand(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (auto const_addr = llvm::dyn_cast<llvm::ConstantInt>(addr))
|
||||
return ResolveAddr(addr_skew + const_addr->getZExtValue());
|
||||
return *exit_block;
|
||||
}
|
||||
|
||||
llvm::Function* LiftHelper::Lift() {
|
||||
LLConfig* cfg = func->cfg;
|
||||
llvm::LLVMContext& ctx = func->mod->getContext();
|
||||
@@ -186,7 +157,7 @@ llvm::Function* LiftHelper::Lift() {
|
||||
fi.pc_base_value = nullptr;
|
||||
} else {
|
||||
RegFile* entry_rf = entry_block->GetInsertBlock()->GetRegFile();
|
||||
fi.pc_base_value = entry_rf->GetReg(ArchReg::IP, Facet::I64);
|
||||
fi.pc_base_value = entry_rf->GetPCValue(nullptr, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -220,19 +191,26 @@ llvm::Function* LiftHelper::Lift() {
|
||||
cur_ab->BranchTo(*exit_block);
|
||||
continue;
|
||||
}
|
||||
llvm::Value* next_rip = regfile->GetReg(ArchReg::IP, Facet::I64);
|
||||
if (auto select = llvm::dyn_cast<llvm::SelectInst>(next_rip)) {
|
||||
cur_ab->BranchTo(select->getCondition(),
|
||||
ResolveAddr(select->getTrueValue()),
|
||||
ResolveAddr(select->getFalseValue()));
|
||||
} else {
|
||||
cur_ab->BranchTo(ResolveAddr(next_rip));
|
||||
}
|
||||
auto [cond, addr1, addr2] = regfile->GetPCBranch(fi.pc_base_value, fi.pc_base_addr);
|
||||
if (auto cst = llvm::dyn_cast<llvm::ConstantInt>(cond))
|
||||
cur_ab->BranchTo(ResolveAddr(cst->isZero() ? addr2 : addr1));
|
||||
else
|
||||
cur_ab->BranchTo(cond, ResolveAddr(addr1), ResolveAddr(addr2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit_block->GetInsertBlock()->InitRegFile(cfg->arch, phi_mode, /*seal=*/true);
|
||||
{
|
||||
llvm::BasicBlock* exitbb = *exit_block->GetInsertBlock();
|
||||
const auto& preds = exit_block->GetInsertBlock()->Predecessors();
|
||||
auto phi = llvm::PHINode::Create(llvm::Type::getInt64Ty(ctx), preds.size(), "", exitbb);
|
||||
for (BasicBlock* pred : preds) {
|
||||
auto predpc = pred->GetRegFile()->GetPCValue(fi.pc_base_value, fi.pc_base_addr);
|
||||
phi->addIncoming(predpc, *pred);
|
||||
}
|
||||
exit_block->GetInsertBlock()->GetRegFile()->SetPC(phi);
|
||||
}
|
||||
|
||||
// Exit block packs values together and optionally returns something.
|
||||
if (cfg->tail_function) {
|
||||
|
||||
@@ -31,37 +31,6 @@
|
||||
|
||||
namespace rellume {
|
||||
|
||||
void LifterBase::SetIP(uint64_t inst_addr, bool nofold) {
|
||||
llvm::Value* rip;
|
||||
if (fi.pc_base_value) {
|
||||
llvm::Value* off = irb.getInt64(inst_addr - fi.pc_base_addr);
|
||||
rip = irb.CreateAdd(fi.pc_base_value, off);
|
||||
} else {
|
||||
rip = irb.getInt64(inst_addr);
|
||||
}
|
||||
if (nofold) {
|
||||
auto bitcast = llvm::Instruction::BitCast;
|
||||
rip = irb.Insert(llvm::CastInst::Create(bitcast, rip, rip->getType()));
|
||||
}
|
||||
SetReg(ArchReg::IP, rip);
|
||||
}
|
||||
|
||||
llvm::Value* LifterBase::AddrIPRel(uint64_t off, Facet facet) {
|
||||
llvm::Value* rip = GetReg(ArchReg::IP, facet);
|
||||
llvm::Value* rip_off = irb.getIntN(facet.Size(), off);
|
||||
|
||||
// For position independent code, RIP has the structure "base_rip + off"
|
||||
// where "off" is defined from the instruction address. Simplify
|
||||
// expressions by attaching the constant offset to the second operand.
|
||||
if (auto binop = llvm::dyn_cast<llvm::BinaryOperator>(rip)) {
|
||||
if (binop->getOpcode() == llvm::Instruction::Add) {
|
||||
auto base_off = irb.CreateAdd(binop->getOperand(1), rip_off);
|
||||
return irb.CreateAdd(binop->getOperand(0), base_off);
|
||||
}
|
||||
}
|
||||
return irb.CreateAdd(rip, rip_off);
|
||||
}
|
||||
|
||||
llvm::Value* LifterBase::AddrConst(uint64_t addr) {
|
||||
if (addr == 0)
|
||||
return llvm::ConstantPointerNull::get(irb.getPtrTy());
|
||||
|
||||
+18
-2
@@ -95,7 +95,21 @@ protected:
|
||||
SetReg(reg, undef);
|
||||
}
|
||||
}
|
||||
void SetIP(uint64_t inst_addr, bool nofold = false);
|
||||
void SetIP(uint64_t inst_addr, bool nofold = false) {
|
||||
if (nofold)
|
||||
regfile->SetPC(irb.getInt64(inst_addr));
|
||||
else
|
||||
regfile->SetPC(inst_addr);
|
||||
}
|
||||
void SetIP(llvm::Value* addr) {
|
||||
regfile->SetPC(addr);
|
||||
}
|
||||
void SetIPCond(llvm::Value* cond, uint64_t addr1, uint64_t addr2) {
|
||||
regfile->SetPCCond(cond, addr1, addr2);
|
||||
}
|
||||
void SetIPCallret(uint64_t retaddr) {
|
||||
regfile->SetPCCallret(AddrIPRel(), retaddr);
|
||||
}
|
||||
|
||||
void SetInsertBlock(BasicBlock* block) {
|
||||
ablock.SetInsertBlock(block);
|
||||
@@ -103,7 +117,9 @@ protected:
|
||||
irb.SetInsertPoint(regfile->GetInsertBlock());
|
||||
}
|
||||
|
||||
llvm::Value* AddrIPRel(uint64_t off, Facet facet);
|
||||
llvm::Value* AddrIPRel(uint64_t off = 0) {
|
||||
return regfile->GetPCValue(fi.pc_base_value, fi.pc_base_addr, off);
|
||||
}
|
||||
llvm::Value* AddrConst(uint64_t addr);
|
||||
|
||||
void CallExternalFunction(llvm::Function* fn);
|
||||
|
||||
+96
-7
@@ -46,8 +46,6 @@ namespace rellume {
|
||||
|
||||
unsigned RegisterSetBitIdx(ArchReg reg) {
|
||||
switch (reg.Kind()) {
|
||||
case ArchReg::RegKind::IP:
|
||||
return 0;
|
||||
case ArchReg::RegKind::FLAG:
|
||||
return 1 + reg.Index();
|
||||
case ArchReg::RegKind::GP:
|
||||
@@ -218,11 +216,45 @@ public:
|
||||
void Merge(ArchReg reg, llvm::Value* v);
|
||||
void Set(ArchReg reg, Transform transform, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3);
|
||||
|
||||
void SetPC(uint64_t addr) {
|
||||
pc = PCReg{PCReg::Const, nullptr, addr, 0};
|
||||
}
|
||||
void SetPC(llvm::Value* addr) {
|
||||
pc = PCReg{PCReg::Value, addr, 0, 0};
|
||||
}
|
||||
void SetPCCond(llvm::Value* cond, uint64_t addr1, uint64_t addr2) {
|
||||
pc = PCReg{PCReg::CondBr, cond, addr1, addr2};
|
||||
}
|
||||
void SetPCCallret(llvm::Value* addr, uint64_t check) {
|
||||
pc = PCReg{PCReg::Check, addr, check, 0};
|
||||
}
|
||||
llvm::Value* GetPCValue(llvm::Value* pcBase, uint64_t pcBaseAddr, uint64_t offset);
|
||||
std::tuple<llvm::Value*, uint64_t, uint64_t> GetPCBranch(llvm::Value* pcBase, uint64_t pcBaseAddr);
|
||||
|
||||
RegisterSet& DirtyRegs() { return dirty_regs; }
|
||||
bool StartsClean() { return !parent && !phiDescs; }
|
||||
|
||||
private:
|
||||
struct PCReg {
|
||||
enum Mode {
|
||||
Uninitialized,
|
||||
/// An arbitrary, opaque LLVM-IR value
|
||||
Value,
|
||||
/// Offset to pc_base
|
||||
Const,
|
||||
/// Conditional branch, two offsets to pc_base
|
||||
CondBr,
|
||||
/// Check for callret mode, branch(value == offset, offset, value)
|
||||
Check
|
||||
};
|
||||
Mode mode = Uninitialized;
|
||||
llvm::Value* llvmVal;
|
||||
uint64_t offset1;
|
||||
uint64_t offset2;
|
||||
};
|
||||
|
||||
llvm::IRBuilder<> irb;
|
||||
PCReg pc;
|
||||
std::array<Register, 72> regs;
|
||||
|
||||
RegFile* parent = nullptr;
|
||||
@@ -239,14 +271,17 @@ private:
|
||||
/// dirty and missing parts if necessary. Returns the index into register
|
||||
/// values with the same or next larger size.
|
||||
std::pair<Register*, unsigned> GetRegFold(ArchReg reg, unsigned fullSize);
|
||||
|
||||
llvm::Value* AddrPCBase(llvm::Value* pcBase, uint64_t pcBaseAddr, uint64_t addr) {
|
||||
if (pcBase)
|
||||
return irb.CreateAdd(pcBase, irb.getInt64(addr - pcBaseAddr));
|
||||
return irb.getInt64(addr);
|
||||
}
|
||||
};
|
||||
|
||||
Register* RegFile::impl::AccessReg(ArchReg reg) {
|
||||
unsigned idx = reg.Index();
|
||||
switch (reg.Kind()) {
|
||||
case ArchReg::RegKind::IP:
|
||||
assert(idx < 1);
|
||||
return ®s[0 + idx];
|
||||
case ArchReg::RegKind::FLAG:
|
||||
assert(idx < 7);
|
||||
return ®s[1 + idx];
|
||||
@@ -257,6 +292,7 @@ Register* RegFile::impl::AccessReg(ArchReg reg) {
|
||||
assert(idx < 32);
|
||||
return ®s[40 + idx];
|
||||
default:
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
@@ -265,8 +301,6 @@ Facet RegFile::impl::NativeFacet(ArchReg reg) {
|
||||
switch (reg.Kind()) {
|
||||
case ArchReg::RegKind::GP:
|
||||
return Facet::I64;
|
||||
case ArchReg::RegKind::IP:
|
||||
return Facet::I64;
|
||||
case ArchReg::RegKind::FLAG:
|
||||
if (reg == ArchReg::PF)
|
||||
return Facet::I8;
|
||||
@@ -546,6 +580,43 @@ void RegFile::impl::Set(ArchReg reg, Transform transform, llvm::Value* v1,
|
||||
dirty_regs[RegisterSetBitIdx(reg)] = true;
|
||||
}
|
||||
|
||||
llvm::Value* RegFile::impl::GetPCValue(llvm::Value* pcBase, uint64_t pcBaseAddr, uint64_t offset) {
|
||||
switch (pc.mode) {
|
||||
case PCReg::Value:
|
||||
case PCReg::Check:
|
||||
if (offset)
|
||||
return irb.CreateAdd(pc.llvmVal, irb.getInt64(offset));
|
||||
return pc.llvmVal;
|
||||
case PCReg::Const:
|
||||
return AddrPCBase(pcBase, pcBaseAddr, pc.offset1 + offset);
|
||||
case PCReg::CondBr:
|
||||
return irb.CreateSelect(pc.llvmVal,
|
||||
AddrPCBase(pcBase, pcBaseAddr, pc.offset1 + offset),
|
||||
AddrPCBase(pcBase, pcBaseAddr, pc.offset2 + offset));
|
||||
default:
|
||||
assert(false);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
std::tuple<llvm::Value*, uint64_t, uint64_t> RegFile::impl::GetPCBranch(llvm::Value* pcBase, uint64_t pcBaseAddr) {
|
||||
switch (pc.mode) {
|
||||
case PCReg::Value:
|
||||
return std::make_tuple(irb.getTrue(), 0, 0);
|
||||
case PCReg::Const:
|
||||
return std::make_tuple(irb.getTrue(), pc.offset1, 0);
|
||||
case PCReg::CondBr:
|
||||
return std::make_tuple(pc.llvmVal, pc.offset1, pc.offset2);
|
||||
case PCReg::Check: {
|
||||
auto cond = irb.CreateICmpEQ(pc.llvmVal, AddrPCBase(pcBase, pcBaseAddr, pc.offset1));
|
||||
return std::make_tuple(cond, pc.offset1, 0);
|
||||
}
|
||||
default:
|
||||
assert(false);
|
||||
return std::make_tuple(nullptr, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
RegFile::RegFile(Arch arch, llvm::BasicBlock* bb) : pimpl{std::make_unique<impl>(arch, bb)} {}
|
||||
RegFile::~RegFile() {}
|
||||
|
||||
@@ -563,6 +634,24 @@ void RegFile::Merge(ArchReg reg, llvm::Value* value) {
|
||||
void RegFile::Set(ArchReg reg, Transform t, llvm::Value* v1, llvm::Value* v2, llvm::Value* v3) {
|
||||
pimpl->Set(reg, t, v1, v2, v3);
|
||||
}
|
||||
void RegFile::SetPC(uint64_t addr) {
|
||||
pimpl->SetPC(addr);
|
||||
}
|
||||
void RegFile::SetPC(llvm::Value* addr) {
|
||||
pimpl->SetPC(addr);
|
||||
}
|
||||
void RegFile::SetPCCond(llvm::Value* cond, uint64_t addr1, uint64_t addr2) {
|
||||
pimpl->SetPCCond(cond, addr1, addr2);
|
||||
}
|
||||
void RegFile::SetPCCallret(llvm::Value* addr, uint64_t check) {
|
||||
pimpl->SetPCCallret(addr, check);
|
||||
}
|
||||
std::tuple<llvm::Value*, uint64_t, uint64_t> RegFile::GetPCBranch(llvm::Value* pcBase, uint64_t pcBaseAddr) {
|
||||
return pimpl->GetPCBranch(pcBase, pcBaseAddr);
|
||||
}
|
||||
llvm::Value* RegFile::GetPCValue(llvm::Value* pcBase, uint64_t pcBaseAddr, uint64_t offset) {
|
||||
return pimpl->GetPCValue(pcBase, pcBaseAddr, offset);
|
||||
}
|
||||
RegisterSet& RegFile::DirtyRegs() { return pimpl->DirtyRegs(); }
|
||||
bool RegFile::StartsClean() { return pimpl->StartsClean(); }
|
||||
|
||||
|
||||
+8
-3
@@ -42,7 +42,6 @@ public:
|
||||
enum class RegKind : uint8_t {
|
||||
INVALID = 0,
|
||||
GP, // 64-bit
|
||||
IP, // 64-bit
|
||||
FLAG, // status flag
|
||||
VEC, // >= 128-bit
|
||||
};
|
||||
@@ -79,7 +78,6 @@ public:
|
||||
}
|
||||
|
||||
static const ArchReg INVALID;
|
||||
static const ArchReg IP;
|
||||
static const ArchReg ZF, SF, PF, CF, OF, AF, DF;
|
||||
// x86-64-specific names ignored by other archs
|
||||
static const ArchReg RAX, RCX, RDX, RBX, RSP, RBP, RSI, RDI;
|
||||
@@ -89,7 +87,6 @@ public:
|
||||
};
|
||||
|
||||
constexpr const ArchReg ArchReg::INVALID{ArchReg::RegKind::INVALID, 0};
|
||||
constexpr const ArchReg ArchReg::IP{ArchReg::RegKind::IP, 0};
|
||||
constexpr const ArchReg ArchReg::RAX = ArchReg::GP(0);
|
||||
constexpr const ArchReg ArchReg::RCX = ArchReg::GP(1);
|
||||
constexpr const ArchReg ArchReg::RDX = ArchReg::GP(2);
|
||||
@@ -157,6 +154,14 @@ public:
|
||||
/// Set full register with given, lazily evaluated, transformation
|
||||
void Set(ArchReg reg, Transform t, llvm::Value* v1, llvm::Value* v2 = nullptr, llvm::Value* v3 = nullptr);
|
||||
|
||||
void SetPC(uint64_t addr);
|
||||
void SetPC(llvm::Value* addr);
|
||||
void SetPCCond(llvm::Value* cond, uint64_t addr1, uint64_t addr2);
|
||||
void SetPCCallret(llvm::Value* addr, uint64_t check);
|
||||
llvm::Value* GetPCValue(llvm::Value* pcBase, uint64_t pcBaseAddr, uint64_t offset = 0);
|
||||
/// Tuple of branch cond (or null), then addr (or 0), else addr (or 0)
|
||||
std::tuple<llvm::Value*, uint64_t, uint64_t> GetPCBranch(llvm::Value* pcBase, uint64_t pcBaseAddr);
|
||||
|
||||
/// Modified registers not yet recorded in a CallConvPack in the FunctionInfo.
|
||||
RegisterSet& DirtyRegs();
|
||||
bool StartsClean();
|
||||
|
||||
+9
-18
@@ -119,10 +119,7 @@ public:
|
||||
void LiftBranch(const Instr& inst, llvm::CmpInst::Predicate pred) {
|
||||
const FrvInst* rvi = inst;
|
||||
auto cond = irb.CreateICmp(pred, LoadGp(rvi->rs1), LoadGp(rvi->rs2));
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(cond,
|
||||
AddrIPRel(rvi->imm, Facet::I64),
|
||||
AddrIPRel(inst.len(), Facet::I64)
|
||||
));
|
||||
SetIPCond(cond, inst.start() + rvi->imm, inst.end());
|
||||
}
|
||||
void LiftDivRem(const FrvInst* rvi, llvm::Instruction::BinaryOps op,
|
||||
Facet f) {
|
||||
@@ -297,12 +294,11 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
|
||||
// TODO: Add instruction marker
|
||||
if (cfg.instr_marker) {
|
||||
llvm::Value* rip = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::StringRef str_ref{reinterpret_cast<const char*>(rvi),
|
||||
sizeof(FrvInst)};
|
||||
llvm::MDString* md = llvm::MDString::get(irb.getContext(), str_ref);
|
||||
llvm::Value* md_val = llvm::MetadataAsValue::get(irb.getContext(), md);
|
||||
irb.CreateCall(cfg.instr_marker, {rip, md_val});
|
||||
irb.CreateCall(cfg.instr_marker, {AddrIPRel(), md_val});
|
||||
}
|
||||
|
||||
// Check overridden implementations first.
|
||||
@@ -320,13 +316,11 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
|
||||
case FRV_JAL:
|
||||
case FRV_JALR: {
|
||||
llvm::Value* ret_addr = AddrIPRel(inst.len(), Facet::I64);
|
||||
if (rvi->rs1 != FRV_REG_INV) {
|
||||
auto tgt = irb.CreateAdd(LoadGp(rvi->rs1, Facet::I64), irb.getInt64(rvi->imm));
|
||||
SetReg(ArchReg::IP, tgt);
|
||||
} else {
|
||||
SetReg(ArchReg::IP, AddrIPRel(rvi->imm, Facet::I64));
|
||||
}
|
||||
llvm::Value* ret_addr = AddrIPRel(inst.len());
|
||||
if (rvi->rs1 != FRV_REG_INV)
|
||||
SetIP(irb.CreateAdd(LoadGp(rvi->rs1, Facet::I64), irb.getInt64(rvi->imm)));
|
||||
else
|
||||
SetIP(inst.start() + rvi->imm);
|
||||
StoreGp(rvi->rd, ret_addr);
|
||||
|
||||
// For discussion, see x86-64 lifting of call/ret.
|
||||
@@ -337,10 +331,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
ForceReturn();
|
||||
} else if (rdl && (!rs1l || rvi->rs1 == rvi->rd)) {
|
||||
CallExternalFunction(cfg.call_function);
|
||||
llvm::Value* cont_addr = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::Value* eq = irb.CreateICmpEQ(cont_addr, ret_addr);
|
||||
// This allows for optimization of the common case (equality).
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(eq, ret_addr, cont_addr));
|
||||
SetIPCallret(inst.end());
|
||||
}
|
||||
}
|
||||
return true;
|
||||
@@ -365,7 +356,7 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
break;
|
||||
|
||||
case FRV_LUI: StoreGp(rvi->rd, irb.getInt64(rvi->imm)); break;
|
||||
case FRV_AUIPC: StoreGp(rvi->rd, AddrIPRel(rvi->imm, Facet::I64)); break;
|
||||
case FRV_AUIPC: StoreGp(rvi->rd, AddrIPRel(rvi->imm)); break;
|
||||
|
||||
case FRV_LB: LiftLoad(rvi, llvm::Instruction::SExt, Facet::I8); break;
|
||||
case FRV_LBU: LiftLoad(rvi, llvm::Instruction::ZExt, Facet::I8); break;
|
||||
|
||||
+41
-43
@@ -429,25 +429,30 @@ void Lifter::LiftLea(const Instr& inst) {
|
||||
assert(inst.op(0).is_reg());
|
||||
assert(inst.op(1).is_mem());
|
||||
|
||||
// Compute as integer
|
||||
unsigned addrsz = inst.op(1).addrsz() * 8;
|
||||
Facet facet = Facet{Facet::I}.Resolve(addrsz);
|
||||
llvm::Value* res = nullptr;
|
||||
if (inst.op(1).off())
|
||||
res = irb.getIntN(addrsz, inst.op(1).off());
|
||||
if (unsigned scale = inst.op(1).scale()) {
|
||||
llvm::Value* offset = GetReg(MapReg(inst.op(1).index()), facet);
|
||||
if (scale > 1) {
|
||||
unsigned shift = scale == 2 ? 1 : scale == 4 ? 2 : 3;
|
||||
offset = irb.CreateShl(offset, irb.getIntN(addrsz, shift));
|
||||
if (inst.op(1).base().ri == FD_REG_IP) {
|
||||
res = AddrIPRel(inst.op(1).off());
|
||||
} else {
|
||||
// Compute as integer
|
||||
unsigned addrsz = inst.op(1).addrsz() * 8;
|
||||
Facet facet = Facet{Facet::I}.Resolve(addrsz);
|
||||
if (inst.op(1).off())
|
||||
res = irb.getIntN(addrsz, inst.op(1).off());
|
||||
if (unsigned scale = inst.op(1).scale()) {
|
||||
llvm::Value* offset = GetReg(MapReg(inst.op(1).index()), facet);
|
||||
if (scale > 1) {
|
||||
unsigned shift = scale == 2 ? 1 : scale == 4 ? 2 : 3;
|
||||
offset = irb.CreateShl(offset, irb.getIntN(addrsz, shift));
|
||||
}
|
||||
res = res ? irb.CreateAdd(offset, res) : offset;
|
||||
}
|
||||
res = res ? irb.CreateAdd(offset, res) : offset;
|
||||
if (inst.op(1).base()){
|
||||
llvm::Value* reg = GetReg(MapReg(inst.op(1).base()), facet);
|
||||
res = res ? irb.CreateAdd(reg, res) : reg;
|
||||
}
|
||||
res = res ? res : irb.getIntN(addrsz, 0);
|
||||
}
|
||||
if (inst.op(1).base()){
|
||||
llvm::Value* reg = GetReg(MapReg(inst.op(1).base()), facet);
|
||||
res = res ? irb.CreateAdd(reg, res) : reg;
|
||||
}
|
||||
res = res ? res : irb.getIntN(addrsz, 0);
|
||||
|
||||
|
||||
llvm::Type* op_type = irb.getIntNTy(inst.op(0).bits());
|
||||
OpStoreGp(inst.op(0), irb.CreateZExtOrTrunc(res, op_type));
|
||||
@@ -572,24 +577,21 @@ void Lifter::LiftBswap(const Instr& inst) {
|
||||
|
||||
void Lifter::LiftJmp(const Instr& inst) {
|
||||
// Force default data segment, 3e is notrack.
|
||||
SetReg(ArchReg::IP, OpLoad(inst.op(0), Facet::I64, ALIGN_NONE, FD_REG_DS));
|
||||
if (inst.op(0).is_pcrel())
|
||||
SetIP(inst.end() + inst.op(0).pcrel());
|
||||
else
|
||||
SetIP(OpLoad(inst.op(0), Facet::I64, ALIGN_NONE, FD_REG_DS));
|
||||
}
|
||||
|
||||
void Lifter::LiftJcc(const Instr& inst, Condition cond) {
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(FlagCond(cond),
|
||||
OpLoad(inst.op(0), Facet::I64),
|
||||
GetReg(ArchReg::IP, Facet::I64)
|
||||
));
|
||||
SetIPCond(FlagCond(cond), inst.end() + inst.op(0).pcrel(), inst.end());
|
||||
}
|
||||
|
||||
void Lifter::LiftJcxz(const Instr& inst) {
|
||||
unsigned sz = inst.addrsz();
|
||||
llvm::Value* cx = GetReg(ArchReg::RCX, Facet::In(sz * 8));
|
||||
llvm::Value* cond = irb.CreateICmpEQ(cx, irb.getIntN(sz*8, 0));
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(cond,
|
||||
OpLoad(inst.op(0), Facet::I64),
|
||||
GetReg(ArchReg::IP, Facet::I64)
|
||||
));
|
||||
SetIPCond(cond, inst.end() + inst.op(0).pcrel(), inst.end());
|
||||
}
|
||||
|
||||
void Lifter::LiftLoop(const Instr& inst) {
|
||||
@@ -607,10 +609,7 @@ void Lifter::LiftLoop(const Instr& inst) {
|
||||
else if (inst.type() == FDI_LOOPNZ)
|
||||
cond = irb.CreateAnd(cond, irb.CreateNot(GetFlag(ArchReg::ZF)));
|
||||
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(cond,
|
||||
OpLoad(inst.op(0), Facet::I64),
|
||||
GetReg(ArchReg::IP, Facet::I64)
|
||||
));
|
||||
SetIPCond(cond, inst.end() + inst.op(0).pcrel(), inst.end());
|
||||
}
|
||||
|
||||
void Lifter::LiftCall(const Instr& inst) {
|
||||
@@ -619,10 +618,13 @@ void Lifter::LiftCall(const Instr& inst) {
|
||||
ArchReg::CF});
|
||||
|
||||
// Force default data segment, 3e is notrack.
|
||||
llvm::Value* new_rip = OpLoad(inst.op(0), Facet::I, ALIGN_NONE, FD_REG_DS);
|
||||
llvm::Value* ret_addr = GetReg(ArchReg::IP, Facet::I64);
|
||||
StackPush(ret_addr);
|
||||
SetReg(ArchReg::IP, new_rip);
|
||||
llvm::Value* new_rip;
|
||||
if (inst.op(0).is_pcrel())
|
||||
new_rip = AddrIPRel(inst.op(0).pcrel());
|
||||
else
|
||||
new_rip = OpLoad(inst.op(0), Facet::I, ALIGN_NONE, FD_REG_DS);
|
||||
StackPush(AddrIPRel()); // return address
|
||||
SetIP(new_rip);
|
||||
|
||||
if (cfg.call_function) {
|
||||
CallExternalFunction(cfg.call_function);
|
||||
@@ -632,10 +634,7 @@ void Lifter::LiftCall(const Instr& inst) {
|
||||
// We will continue with the tail_function (if specified) and enlarge
|
||||
// our shadow stack; and things will be slow. If someone uses such
|
||||
// constructs often or on a critical path, they get what they deserve.
|
||||
llvm::Value* cont_addr = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::Value* eq = irb.CreateICmpEQ(cont_addr, ret_addr);
|
||||
// This allows for optimization of the common case (equality).
|
||||
SetReg(ArchReg::IP, irb.CreateSelect(eq, ret_addr, cont_addr));
|
||||
SetIPCallret(inst.end());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -645,7 +644,7 @@ void Lifter::LiftRet(const Instr& inst) {
|
||||
SetFlagUndef({ArchReg::OF, ArchReg::SF, ArchReg::ZF, ArchReg::AF, ArchReg::PF,
|
||||
ArchReg::CF});
|
||||
|
||||
SetReg(ArchReg::IP, StackPop());
|
||||
SetIP(StackPop());
|
||||
|
||||
if (inst.op(0)) {
|
||||
llvm::Value* rsp = GetReg(ArchReg::RSP, Facet::PTR);
|
||||
@@ -661,7 +660,7 @@ void Lifter::LiftRet(const Instr& inst) {
|
||||
}
|
||||
|
||||
void Lifter::LiftSyscall(const Instr& inst) {
|
||||
SetReg(ArchReg::RCX, GetReg(ArchReg::IP, Facet::I64));
|
||||
SetReg(ArchReg::RCX, AddrIPRel());
|
||||
SetReg(ArchReg::GP(11), FlagAsReg(64));
|
||||
|
||||
if (cfg.syscall_implementation)
|
||||
@@ -717,7 +716,7 @@ Lifter::RepInfo Lifter::RepBegin(const Instr& inst) {
|
||||
if (info.mode != RepInfo::NO_REP) {
|
||||
info.loop_block = ablock.AddBlock();
|
||||
info.cont_block = ablock.AddBlock();
|
||||
info.ip = GetReg(ArchReg::IP, Facet::I64);
|
||||
info.ip = inst.end();
|
||||
|
||||
llvm::Value* count = GetReg(ArchReg::RCX, Facet::I64);
|
||||
llvm::Value* zero = llvm::Constant::getNullValue(count->getType());
|
||||
@@ -768,7 +767,7 @@ void Lifter::RepEnd(RepInfo info) {
|
||||
|
||||
// Ensure that we don't generate an unused PHI node which may end up in the
|
||||
// register file if the REP is at the end of a basic block.
|
||||
SetReg(ArchReg::IP, info.ip);
|
||||
SetIP(info.ip);
|
||||
}
|
||||
|
||||
void Lifter::LiftLods(const Instr& inst) {
|
||||
@@ -788,7 +787,6 @@ void Lifter::LiftStos(const Instr& inst) {
|
||||
auto di = GetReg(ArchReg::RDI, Facet::PTR);
|
||||
auto cx = GetReg(ArchReg::RCX, Facet::I64);
|
||||
auto ax = GetReg(ArchReg::RAX, Facet::I8);
|
||||
auto ip = GetReg(ArchReg::IP, Facet::I64);
|
||||
|
||||
auto* df0_block = ablock.AddBlock();
|
||||
auto* df1_block = ablock.AddBlock();
|
||||
@@ -811,7 +809,7 @@ void Lifter::LiftStos(const Instr& inst) {
|
||||
|
||||
SetInsertBlock(cont_block);
|
||||
SetReg(ArchReg::RCX, irb.getInt64(0));
|
||||
SetReg(ArchReg::IP, ip);
|
||||
SetIP(inst.end());
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -46,8 +46,9 @@
|
||||
namespace rellume::x86_64 {
|
||||
|
||||
ArchReg Lifter::MapReg(const Instr::Reg reg) {
|
||||
assert(reg.rt != FD_RT_GPL || reg.ri != FD_REG_IP);
|
||||
if (reg.rt == FD_RT_GPL)
|
||||
return reg.ri == FD_REG_IP ? ArchReg::IP : ArchReg::GP(reg.ri);
|
||||
return ArchReg::GP(reg.ri);
|
||||
else if (reg.rt == FD_RT_GPH)
|
||||
return ArchReg::GP(reg.ri - FD_REG_AH);
|
||||
else if (reg.rt == FD_RT_VEC)
|
||||
@@ -91,6 +92,10 @@ llvm::Value* Lifter::OpAddr(const Instr::Op op, llvm::Type* element_type,
|
||||
return irb.CreateIntToPtr(res, irb.getPtrTy(addrspace));
|
||||
}
|
||||
|
||||
if (op.base().ri == FD_REG_IP) {
|
||||
return irb.CreateIntToPtr(AddrIPRel(op.off()), irb.getPtrTy());
|
||||
}
|
||||
|
||||
llvm::Type* scale_type = nullptr;
|
||||
if (op.scale() * 8u == element_type->getPrimitiveSizeInBits())
|
||||
scale_type = element_type;
|
||||
@@ -155,8 +160,6 @@ llvm::Value* Lifter::OpLoad(const Instr::Op op, Facet facet,
|
||||
facet = facet.Resolve(op.bits());
|
||||
if (op.is_imm()) {
|
||||
return irb.getIntN(op.bits(), op.imm());
|
||||
} else if (op.is_pcrel()) {
|
||||
return AddrIPRel(op.pcrel(), facet);
|
||||
} else if (op.is_reg()) {
|
||||
if (facet == Facet::I8 && op.reg().rt == FD_RT_GPH)
|
||||
facet = Facet::I8H;
|
||||
|
||||
@@ -111,7 +111,7 @@ private:
|
||||
llvm::Type* ty;
|
||||
llvm::Value* di;
|
||||
llvm::Value* si;
|
||||
llvm::Value* ip;
|
||||
uint64_t ip;
|
||||
};
|
||||
RepInfo RepBegin(const Instr& inst);
|
||||
void RepEnd(RepInfo info);
|
||||
|
||||
@@ -51,12 +51,11 @@ bool Lifter::Lift(const Instr& inst) {
|
||||
|
||||
// Add instruction marker
|
||||
if (cfg.instr_marker) {
|
||||
llvm::Value* rip = GetReg(ArchReg::IP, Facet::I64);
|
||||
llvm::StringRef str_ref{reinterpret_cast<const char*>(&inst),
|
||||
sizeof(FdInstr)};
|
||||
llvm::MDString* md = llvm::MDString::get(irb.getContext(), str_ref);
|
||||
llvm::Value* md_val = llvm::MetadataAsValue::get(irb.getContext(), md);
|
||||
irb.CreateCall(cfg.instr_marker, {rip, md_val});
|
||||
irb.CreateCall(cfg.instr_marker, {AddrIPRel(), md_val});
|
||||
}
|
||||
|
||||
// Check overridden implementations first.
|
||||
|
||||
Reference in New Issue
Block a user