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:
Alexis Engelke
2024-01-20 13:17:05 +01:00
parent caa79d127c
commit 296bd05750
15 changed files with 214 additions and 171 deletions
+1 -1
View File
@@ -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 -1
View File
@@ -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 -1
View File
@@ -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
View File
@@ -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.
+2
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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 &regs[0 + idx];
case ArchReg::RegKind::FLAG:
assert(idx < 7);
return &regs[1 + idx];
@@ -257,6 +292,7 @@ Register* RegFile::impl::AccessReg(ArchReg reg) {
assert(idx < 32);
return &regs[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
View File
@@ -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
View File
@@ -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
View File
@@ -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;
}
+6 -3
View File
@@ -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;
+1 -1
View File
@@ -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);
+1 -2
View File
@@ -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.