Files
revng-revng/lib/Lift/InstructionTranslator.cpp
Alessandro Di Federico e64eee9402 CodeGenerator: handle partially valid instructions
Sometimes we have instructions that are, say, two bytes long but only
one of the two bytes are invalid (because they end up out of the
segment).
2024-05-15 13:19:42 +02:00

1473 lines
49 KiB
C++

/// \file InstructionTranslator.cpp
/// This file implements the logic to translate a PTC instruction in to LLVM IR.
//
// This file is distributed under the MIT License. See LICENSE.md for details.
//
#include <cstdint>
#include <fstream>
#include <queue>
#include <set>
#include <sstream>
#include "llvm/ADT/STLExtras.h"
#include "llvm/IR/Attributes.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/CFG.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Module.h"
#include "llvm/Support/Casting.h"
#include "revng/Lift/Lift.h"
#include "revng/Support/Assert.h"
#include "revng/Support/FunctionTags.h"
#include "revng/Support/IRHelpers.h"
#include "revng/Support/RandomAccessIterator.h"
#include "revng/Support/Range.h"
#include "InstructionTranslator.h"
#include "PTCInterface.h"
#include "VariableManager.h"
using namespace llvm;
static cl::opt<bool> RecordASM("record-asm",
cl::desc("create metadata for assembly"),
cl::cat(MainCategory));
using IT = InstructionTranslator;
namespace PTC {
template<bool C>
class InstructionImpl;
enum ArgumentType {
In,
Out,
Const
};
template<typename T, typename Q, bool B>
using RAI = RandomAccessIterator<T, Q, B>;
template<ArgumentType Type, bool IsCall>
class InstructionArgumentsIterator
: public RAI<uint64_t, InstructionArgumentsIterator<Type, IsCall>, false> {
public:
using base = RandomAccessIterator<uint64_t,
InstructionArgumentsIterator,
false>;
InstructionArgumentsIterator &
operator=(const InstructionArgumentsIterator &R) {
base::operator=(R);
TheInstruction = R.TheInstruction;
return *this;
}
InstructionArgumentsIterator(const InstructionArgumentsIterator &R) :
base(R), TheInstruction(R.TheInstruction) {}
InstructionArgumentsIterator(const InstructionArgumentsIterator &R,
unsigned Index) :
base(Index), TheInstruction(R.TheInstruction) {}
InstructionArgumentsIterator(PTCInstruction *TheInstruction, unsigned Index) :
base(Index), TheInstruction(TheInstruction) {}
bool isCompatible(const InstructionArgumentsIterator &R) const {
return TheInstruction == R.TheInstruction;
}
public:
uint64_t get(unsigned Index) const;
private:
PTCInstruction *TheInstruction;
};
template<>
inline uint64_t
InstructionArgumentsIterator<In, true>::get(unsigned Index) const {
return ptc_call_instruction_in_arg(&ptc, TheInstruction, Index);
}
template<>
inline uint64_t
InstructionArgumentsIterator<Const, true>::get(unsigned Index) const {
return ptc_call_instruction_const_arg(&ptc, TheInstruction, Index);
}
template<>
inline uint64_t
InstructionArgumentsIterator<Out, true>::get(unsigned Index) const {
return ptc_call_instruction_out_arg(&ptc, TheInstruction, Index);
}
template<>
inline uint64_t
InstructionArgumentsIterator<In, false>::get(unsigned Index) const {
return ptc_instruction_in_arg(&ptc, TheInstruction, Index);
}
template<>
inline uint64_t
InstructionArgumentsIterator<Const, false>::get(unsigned Index) const {
return ptc_instruction_const_arg(&ptc, TheInstruction, Index);
}
template<>
inline uint64_t
InstructionArgumentsIterator<Out, false>::get(unsigned Index) const {
return ptc_instruction_out_arg(&ptc, TheInstruction, Index);
}
template<bool IsCall>
class InstructionImpl {
private:
template<ArgumentType Type>
using arguments = InstructionArgumentsIterator<Type, IsCall>;
public:
InstructionImpl(PTCInstruction *TheInstruction) :
TheInstruction(TheInstruction),
InArguments(arguments<In>(TheInstruction, 0),
arguments<In>(TheInstruction, inArgCount())),
ConstArguments(arguments<Const>(TheInstruction, 0),
arguments<Const>(TheInstruction, constArgCount())),
OutArguments(arguments<Out>(TheInstruction, 0),
arguments<Out>(TheInstruction, outArgCount())) {}
PTCOpcode opcode() const { return TheInstruction->opc; }
std::string helperName() const {
revng_assert(IsCall);
PTCHelperDef *Helper = ptc_find_helper(&ptc, ConstArguments[0]);
revng_assert(Helper != nullptr && Helper->name != nullptr);
return std::string(Helper->name);
}
uint64_t pc() const {
revng_assert(opcode() == PTC_INSTRUCTION_op_debug_insn_start);
uint64_t PC = ConstArguments[0];
if (ConstArguments.size() > 1)
PC |= ConstArguments[1] << 32;
return PC;
}
private:
PTCInstruction *TheInstruction;
public:
const Range<InstructionArgumentsIterator<In, IsCall>> InArguments;
const Range<InstructionArgumentsIterator<Const, IsCall>> ConstArguments;
const Range<InstructionArgumentsIterator<Out, IsCall>> OutArguments;
private:
unsigned inArgCount() const;
unsigned constArgCount() const;
unsigned outArgCount() const;
};
using Instruction = InstructionImpl<false>;
using CallInstruction = InstructionImpl<true>;
template<>
inline unsigned CallInstruction::inArgCount() const {
return ptc_call_instruction_in_arg_count(&ptc, TheInstruction);
}
template<>
inline unsigned Instruction::inArgCount() const {
return ptc_instruction_in_arg_count(&ptc, TheInstruction);
}
template<>
inline unsigned CallInstruction::constArgCount() const {
return ptc_call_instruction_const_arg_count(&ptc, TheInstruction);
}
template<>
inline unsigned Instruction::constArgCount() const {
return ptc_instruction_const_arg_count(&ptc, TheInstruction);
}
template<>
inline unsigned CallInstruction::outArgCount() const {
return ptc_call_instruction_out_arg_count(&ptc, TheInstruction);
}
template<>
inline unsigned Instruction::outArgCount() const {
return ptc_instruction_out_arg_count(&ptc, TheInstruction);
}
} // namespace PTC
/// Converts a PTC condition into an LLVM predicate
///
/// \param Condition the input PTC condition.
///
/// \return the corresponding LLVM predicate.
static CmpInst::Predicate conditionToPredicate(PTCCondition Condition) {
switch (Condition) {
case PTC_COND_NEVER:
// TODO: this is probably wrong
return CmpInst::FCMP_FALSE;
case PTC_COND_ALWAYS:
// TODO: this is probably wrong
return CmpInst::FCMP_TRUE;
case PTC_COND_EQ:
return CmpInst::ICMP_EQ;
case PTC_COND_NE:
return CmpInst::ICMP_NE;
case PTC_COND_LT:
return CmpInst::ICMP_SLT;
case PTC_COND_GE:
return CmpInst::ICMP_SGE;
case PTC_COND_LE:
return CmpInst::ICMP_SLE;
case PTC_COND_GT:
return CmpInst::ICMP_SGT;
case PTC_COND_LTU:
return CmpInst::ICMP_ULT;
case PTC_COND_GEU:
return CmpInst::ICMP_UGE;
case PTC_COND_LEU:
return CmpInst::ICMP_ULE;
case PTC_COND_GTU:
return CmpInst::ICMP_UGT;
default:
revng_unreachable("Unknown comparison operator");
}
}
/// Obtains the LLVM binary operation corresponding to the specified PTC opcode.
///
/// \param Opcode the PTC opcode.
///
/// \return the LLVM binary operation matching opcode.
static Instruction::BinaryOps opcodeToBinaryOp(PTCOpcode Opcode) {
switch (Opcode) {
case PTC_INSTRUCTION_op_add_i32:
case PTC_INSTRUCTION_op_add_i64:
case PTC_INSTRUCTION_op_add2_i32:
case PTC_INSTRUCTION_op_add2_i64:
return Instruction::Add;
case PTC_INSTRUCTION_op_sub_i32:
case PTC_INSTRUCTION_op_sub_i64:
case PTC_INSTRUCTION_op_sub2_i32:
case PTC_INSTRUCTION_op_sub2_i64:
return Instruction::Sub;
case PTC_INSTRUCTION_op_mul_i32:
case PTC_INSTRUCTION_op_mul_i64:
return Instruction::Mul;
case PTC_INSTRUCTION_op_div_i32:
case PTC_INSTRUCTION_op_div_i64:
return Instruction::SDiv;
case PTC_INSTRUCTION_op_divu_i32:
case PTC_INSTRUCTION_op_divu_i64:
return Instruction::UDiv;
case PTC_INSTRUCTION_op_rem_i32:
case PTC_INSTRUCTION_op_rem_i64:
return Instruction::SRem;
case PTC_INSTRUCTION_op_remu_i32:
case PTC_INSTRUCTION_op_remu_i64:
return Instruction::URem;
case PTC_INSTRUCTION_op_and_i32:
case PTC_INSTRUCTION_op_and_i64:
return Instruction::And;
case PTC_INSTRUCTION_op_or_i32:
case PTC_INSTRUCTION_op_or_i64:
return Instruction::Or;
case PTC_INSTRUCTION_op_xor_i32:
case PTC_INSTRUCTION_op_xor_i64:
return Instruction::Xor;
case PTC_INSTRUCTION_op_shl_i32:
case PTC_INSTRUCTION_op_shl_i64:
return Instruction::Shl;
case PTC_INSTRUCTION_op_shr_i32:
case PTC_INSTRUCTION_op_shr_i64:
return Instruction::LShr;
case PTC_INSTRUCTION_op_sar_i32:
case PTC_INSTRUCTION_op_sar_i64:
return Instruction::AShr;
default:
revng_unreachable("PTC opcode is not a binary operator");
}
}
/// Returns the maximum value which can be represented with the specified number
/// of bits.
static uint64_t getMaxValue(unsigned Bits) {
if (Bits == 32)
return 0xffffffff;
else if (Bits == 64)
return 0xffffffffffffffff;
else
revng_unreachable("Not the number of bits in a integer type");
}
/// Maps an opcode the corresponding input and output register size.
///
/// \return the size, in bits, of the registers used by the opcode.
static unsigned getRegisterSize(unsigned Opcode) {
switch (Opcode) {
case PTC_INSTRUCTION_op_add2_i32:
case PTC_INSTRUCTION_op_add_i32:
case PTC_INSTRUCTION_op_andc_i32:
case PTC_INSTRUCTION_op_and_i32:
case PTC_INSTRUCTION_op_brcond2_i32:
case PTC_INSTRUCTION_op_brcond_i32:
case PTC_INSTRUCTION_op_bswap16_i32:
case PTC_INSTRUCTION_op_bswap32_i32:
case PTC_INSTRUCTION_op_deposit_i32:
case PTC_INSTRUCTION_op_div2_i32:
case PTC_INSTRUCTION_op_div_i32:
case PTC_INSTRUCTION_op_divu2_i32:
case PTC_INSTRUCTION_op_divu_i32:
case PTC_INSTRUCTION_op_eqv_i32:
case PTC_INSTRUCTION_op_ext16s_i32:
case PTC_INSTRUCTION_op_ext16u_i32:
case PTC_INSTRUCTION_op_ext8s_i32:
case PTC_INSTRUCTION_op_ext8u_i32:
case PTC_INSTRUCTION_op_ld16s_i32:
case PTC_INSTRUCTION_op_ld16u_i32:
case PTC_INSTRUCTION_op_ld8s_i32:
case PTC_INSTRUCTION_op_ld8u_i32:
case PTC_INSTRUCTION_op_ld_i32:
case PTC_INSTRUCTION_op_movcond_i32:
case PTC_INSTRUCTION_op_mov_i32:
case PTC_INSTRUCTION_op_movi_i32:
case PTC_INSTRUCTION_op_mul_i32:
case PTC_INSTRUCTION_op_muls2_i32:
case PTC_INSTRUCTION_op_mulsh_i32:
case PTC_INSTRUCTION_op_mulu2_i32:
case PTC_INSTRUCTION_op_muluh_i32:
case PTC_INSTRUCTION_op_nand_i32:
case PTC_INSTRUCTION_op_neg_i32:
case PTC_INSTRUCTION_op_nor_i32:
case PTC_INSTRUCTION_op_not_i32:
case PTC_INSTRUCTION_op_orc_i32:
case PTC_INSTRUCTION_op_or_i32:
case PTC_INSTRUCTION_op_qemu_ld_i32:
case PTC_INSTRUCTION_op_qemu_st_i32:
case PTC_INSTRUCTION_op_rem_i32:
case PTC_INSTRUCTION_op_remu_i32:
case PTC_INSTRUCTION_op_rotl_i32:
case PTC_INSTRUCTION_op_rotr_i32:
case PTC_INSTRUCTION_op_sar_i32:
case PTC_INSTRUCTION_op_setcond2_i32:
case PTC_INSTRUCTION_op_setcond_i32:
case PTC_INSTRUCTION_op_shl_i32:
case PTC_INSTRUCTION_op_shr_i32:
case PTC_INSTRUCTION_op_st16_i32:
case PTC_INSTRUCTION_op_st8_i32:
case PTC_INSTRUCTION_op_st_i32:
case PTC_INSTRUCTION_op_sub2_i32:
case PTC_INSTRUCTION_op_sub_i32:
case PTC_INSTRUCTION_op_trunc_shr_i32:
case PTC_INSTRUCTION_op_xor_i32:
return 32;
case PTC_INSTRUCTION_op_add2_i64:
case PTC_INSTRUCTION_op_add_i64:
case PTC_INSTRUCTION_op_andc_i64:
case PTC_INSTRUCTION_op_and_i64:
case PTC_INSTRUCTION_op_brcond_i64:
case PTC_INSTRUCTION_op_bswap16_i64:
case PTC_INSTRUCTION_op_bswap32_i64:
case PTC_INSTRUCTION_op_bswap64_i64:
case PTC_INSTRUCTION_op_deposit_i64:
case PTC_INSTRUCTION_op_div2_i64:
case PTC_INSTRUCTION_op_div_i64:
case PTC_INSTRUCTION_op_divu2_i64:
case PTC_INSTRUCTION_op_divu_i64:
case PTC_INSTRUCTION_op_eqv_i64:
case PTC_INSTRUCTION_op_ext16s_i64:
case PTC_INSTRUCTION_op_ext16u_i64:
case PTC_INSTRUCTION_op_ext32s_i64:
case PTC_INSTRUCTION_op_ext32u_i64:
case PTC_INSTRUCTION_op_ext8s_i64:
case PTC_INSTRUCTION_op_ext8u_i64:
case PTC_INSTRUCTION_op_ld16s_i64:
case PTC_INSTRUCTION_op_ld16u_i64:
case PTC_INSTRUCTION_op_ld32s_i64:
case PTC_INSTRUCTION_op_ld32u_i64:
case PTC_INSTRUCTION_op_ld8s_i64:
case PTC_INSTRUCTION_op_ld8u_i64:
case PTC_INSTRUCTION_op_ld_i64:
case PTC_INSTRUCTION_op_movcond_i64:
case PTC_INSTRUCTION_op_mov_i64:
case PTC_INSTRUCTION_op_movi_i64:
case PTC_INSTRUCTION_op_mul_i64:
case PTC_INSTRUCTION_op_muls2_i64:
case PTC_INSTRUCTION_op_mulsh_i64:
case PTC_INSTRUCTION_op_mulu2_i64:
case PTC_INSTRUCTION_op_muluh_i64:
case PTC_INSTRUCTION_op_nand_i64:
case PTC_INSTRUCTION_op_neg_i64:
case PTC_INSTRUCTION_op_nor_i64:
case PTC_INSTRUCTION_op_not_i64:
case PTC_INSTRUCTION_op_orc_i64:
case PTC_INSTRUCTION_op_or_i64:
case PTC_INSTRUCTION_op_qemu_ld_i64:
case PTC_INSTRUCTION_op_qemu_st_i64:
case PTC_INSTRUCTION_op_rem_i64:
case PTC_INSTRUCTION_op_remu_i64:
case PTC_INSTRUCTION_op_rotl_i64:
case PTC_INSTRUCTION_op_rotr_i64:
case PTC_INSTRUCTION_op_sar_i64:
case PTC_INSTRUCTION_op_setcond_i64:
case PTC_INSTRUCTION_op_shl_i64:
case PTC_INSTRUCTION_op_shr_i64:
case PTC_INSTRUCTION_op_st16_i64:
case PTC_INSTRUCTION_op_st32_i64:
case PTC_INSTRUCTION_op_st8_i64:
case PTC_INSTRUCTION_op_st_i64:
case PTC_INSTRUCTION_op_sub2_i64:
case PTC_INSTRUCTION_op_sub_i64:
case PTC_INSTRUCTION_op_xor_i64:
return 64;
case PTC_INSTRUCTION_op_br:
case PTC_INSTRUCTION_op_call:
case PTC_INSTRUCTION_op_debug_insn_start:
case PTC_INSTRUCTION_op_discard:
case PTC_INSTRUCTION_op_exit_tb:
case PTC_INSTRUCTION_op_goto_tb:
case PTC_INSTRUCTION_op_set_label:
return 0;
default:
revng_unreachable("Unexpected opcode");
}
}
/// Create a compare instruction given a comparison operator and the operands
///
/// \param Builder the builder to use to create the instruction.
/// \param RawCondition the PTC condition.
/// \param FirstOperand the first operand of the comparison.
/// \param SecondOperand the second operand of the comparison.
///
/// \return a compare instruction.
template<typename T>
static Value *createICmp(T &Builder,
uint64_t RawCondition,
Value *FirstOperand,
Value *SecondOperand) {
PTCCondition Condition = static_cast<PTCCondition>(RawCondition);
return Builder.CreateICmp(conditionToPredicate(Condition),
FirstOperand,
SecondOperand);
}
using LBM = IT::LabeledBlocksMap;
IT::InstructionTranslator(IRBuilder<> &Builder,
VariableManager &Variables,
JumpTargetManager &JumpTargets,
std::vector<BasicBlock *> Blocks,
bool EndianessMismatch,
ProgramCounterHandler *PCH) :
Builder(Builder),
Variables(Variables),
JumpTargets(JumpTargets),
Blocks(Blocks),
TheModule(*Builder.GetInsertBlock()->getParent()->getParent()),
TheFunction(Builder.GetInsertBlock()->getParent()),
EndianessMismatch(EndianessMismatch),
NewPCMarker(nullptr),
LastPC(MetaAddress::invalid()),
PCH(PCH) {
auto &Context = TheModule.getContext();
using FT = FunctionType;
// The newpc function call takes the following parameters:
//
// * BasicBlockID of the instruction in string form
// * instruction size
// * isJT (-1: unknown, 0: no, 1: yes)
// * inlining index
// * pointer to the disassembled instruction
// * all the local variables used by this instruction
auto *NewPCMarkerTy = FT::get(Type::getVoidTy(Context),
{ Type::getInt8PtrTy(Context),
Type::getInt64Ty(Context),
Type::getInt32Ty(Context),
Type::getInt32Ty(Context),
Type::getInt8PtrTy(Context),
Type::getInt8PtrTy(Context) },
true);
NewPCMarker = Function::Create(NewPCMarkerTy,
GlobalValue::ExternalLinkage,
"newpc",
&TheModule);
FunctionTags::Marker.addTo(NewPCMarker);
NewPCMarker->addFnAttr(Attribute::WillReturn);
NewPCMarker->addFnAttr(Attribute::NoUnwind);
NewPCMarker->addFnAttr(Attribute::NoMerge);
}
void IT::finalizeNewPCMarkers() {
size_t FixedArgCount = NewPCMarker->arg_size();
llvm::SmallVector<CallInst *, 4> CallsToRemove;
for (User *U : NewPCMarker->users()) {
auto *Call = cast<CallInst>(U);
// Report the instruction on the coverage CSV
using namespace NewPCArguments;
MetaAddress PC = addressFromNewPC(Call);
uint64_t Size = getLimitedValue(Call->getArgOperand(InstructionSize));
bool IsJT = JumpTargets.isJumpTarget(PC);
// We already finished discovering new code to translate, so we can remove
// the references to local variables as argument of the calls to newpc and
// create room for more optimizations.
if (Call->arg_size() != FixedArgCount) {
SmallVector<Value *, 8> Args;
auto *AI = Call->arg_begin();
for (size_t Idx = 0; Idx < FixedArgCount; ++Idx, ++AI)
Args.emplace_back(*AI);
auto *NewCall = CallInst::Create(NewPCMarker, Args, "", Call);
NewCall->setCallingConv(Call->getCallingConv());
NewCall->setDebugLoc(Call->getDebugLoc());
NewCall->copyMetadata(*Call);
// Note: we intentionally do not copy attributes. We do not expect to have
// any and removing those on extra arguments leads to a mysterious
// failure in verify "Attribute after last parameter".
revng_assert(Call->use_empty());
CallsToRemove.push_back(Call);
}
}
for (auto *Call : CallsToRemove)
eraseFromParent(Call);
}
SmallSet<unsigned, 1> IT::preprocess(PTCInstructionList *InstructionList) {
SmallSet<unsigned, 1> Result;
for (unsigned I = 0; I < InstructionList->instruction_count; I++) {
PTCInstruction &Instruction = InstructionList->instructions[I];
switch (Instruction.opc) {
case PTC_INSTRUCTION_op_movi_i32:
case PTC_INSTRUCTION_op_movi_i64:
case PTC_INSTRUCTION_op_mov_i32:
case PTC_INSTRUCTION_op_mov_i64:
break;
default:
continue;
}
const PTC::Instruction TheInstruction(&Instruction);
unsigned OutArg = TheInstruction.OutArguments[0];
PTCTemp *Temporary = ptc_temp_get(InstructionList, OutArg);
if (!ptc_temp_is_global(InstructionList, OutArg))
continue;
if (0 != strcmp("btarget", Temporary->name))
continue;
for (unsigned J = I + 1; J < InstructionList->instruction_count; J++) {
unsigned Opcode = InstructionList->instructions[J].opc;
if (Opcode == PTC_INSTRUCTION_op_debug_insn_start)
Result.insert(J);
}
break;
}
return Result;
}
CallInst *IT::emitNewPCCall(IRBuilder<> &Builder,
MetaAddress PC,
uint64_t Size,
Value *String) const {
PointerType *Int8PtrTy = getStringPtrType(TheModule.getContext());
auto *Int8NullPtr = ConstantPointerNull::get(Int8PtrTy);
std::vector<Value *> Args = { BasicBlockID(PC).toValue(&TheModule),
Builder.getInt64(Size),
Builder.getInt32(-1),
Builder.getInt32(0),
String != nullptr ? String : Int8NullPtr,
Int8NullPtr };
// Insert a call to NewPCMarker capturing all the local temporaries
// This prevents SROA from transforming them in SSA values, which is bad
// in case we have to split a basic block
for (AllocaInst *Local : Variables.locals())
Args.push_back(Local);
return Builder.CreateCall(NewPCMarker, Args);
}
std::tuple<IT::TranslationResult, MDNode *, MetaAddress, MetaAddress>
IT::newInstruction(PTCInstruction *Instr,
PTCInstruction *Next,
MetaAddress StartPC,
MetaAddress EndPC,
bool IsFirst,
MetaAddress AbortAt) {
using R = std::tuple<TranslationResult, MDNode *, MetaAddress, MetaAddress>;
revng_assert(Instr != nullptr);
LLVMContext &Context = TheModule.getContext();
const PTC::Instruction TheInstruction(Instr);
// A new original instruction, let's create a new metadata node
// referencing it for all the next instructions to come
MetaAddress PC = StartPC.replaceAddress(TheInstruction.pc());
// Prevent translation of non-executable code
if (not JumpTargets.isExecutableAddress(PC))
return R{ Abort, nullptr, MetaAddress::invalid(), MetaAddress::invalid() };
// Compute NextPC
MetaAddress NextPC = MetaAddress::invalid();
if (Next != nullptr)
NextPC = StartPC.replaceAddress(PTC::Instruction(Next).pc());
else
NextPC = EndPC;
PointerType *Int8PtrTy = getStringPtrType(Context);
if (AbortAt.isValid() and NextPC.addressGreaterThan(AbortAt)) {
emitNewPCCall(Builder, PC, 1, ConstantPointerNull::get(Int8PtrTy));
return R{ Abort, nullptr, MetaAddress::invalid(), MetaAddress::invalid() };
}
MDNode *MDOriginalInstr = nullptr;
Constant *String = nullptr;
if (RecordASM) {
std::stringstream OriginalStringStream;
revng_assert(NextPC - PC);
disassemble(OriginalStringStream, PC, *(NextPC - PC));
std::string OriginalString = OriginalStringStream.str();
// We don't deduplicate this string since performing a lookup each time is
// increasingly expensive and we should have relatively few collisions
std::string AddressName = JumpTargets.nameForAddress(PC);
String = getUniqueString(&TheModule, OriginalString);
auto *MDOriginalString = ConstantAsMetadata::get(String);
auto *MDPC = ConstantAsMetadata::get(PC.toValue(&TheModule));
MDOriginalInstr = MDNode::get(Context, { MDOriginalString, MDPC });
} else {
String = ConstantPointerNull::get(Int8PtrTy);
}
if (!IsFirst) {
// Check if this PC already has a block and use it
bool ShouldContinue;
BasicBlock *DivergeTo = JumpTargets.newPC(PC, ShouldContinue);
if (DivergeTo != nullptr) {
Builder.CreateBr(DivergeTo);
if (ShouldContinue) {
// The block is empty, let's fill it
Blocks.push_back(DivergeTo);
Builder.SetInsertPoint(DivergeTo);
} else {
// The block contains already translated code, early exit
return R{ Stop, MDOriginalInstr, PC, NextPC };
}
}
}
Variables.newBasicBlock();
revng_assert(NextPC - PC);
auto *Call = emitNewPCCall(Builder, PC, *(NextPC - PC), String);
if (!IsFirst) {
// Inform the JumpTargetManager about the new PC we met
BasicBlock::iterator CurrentIt = Builder.GetInsertPoint();
if (CurrentIt == Builder.GetInsertBlock()->begin())
revng_assert(JumpTargets.getBlockAt(PC) == Builder.GetInsertBlock());
else
JumpTargets.registerInstruction(PC, Call);
}
return R{ Success, MDOriginalInstr, PC, NextPC };
}
IT::TranslationResult IT::translateCall(PTCInstruction *Instr) {
const PTC::CallInstruction TheCall(Instr);
std::vector<Value *> InArgs;
for (uint64_t TemporaryId : TheCall.InArguments) {
auto *Load = Variables.load(Builder, TemporaryId);
if (Load == nullptr)
return Abort;
InArgs.push_back(Load);
}
const auto GetValueType = [](Value *Argument) { return Argument->getType(); };
auto ValueTypes = llvm::map_range(InArgs, GetValueType);
std::vector<Type *> InArgsType(ValueTypes.begin(), ValueTypes.end());
// TODO: handle multiple return arguments
revng_assert(TheCall.OutArguments.size() <= 1);
Value *ResultDestination = nullptr;
Type *ResultType = nullptr;
if (TheCall.OutArguments.size() != 0) {
ResultDestination = Variables.getOrCreate(TheCall.OutArguments[0]);
if (ResultDestination == nullptr)
return Abort;
ResultType = getVariableType(ResultDestination);
} else {
ResultType = Builder.getVoidTy();
}
auto *CalleeType = FunctionType::get(ResultType,
ArrayRef<Type *>(InArgsType),
false);
std::string HelperName = "helper_" + TheCall.helperName();
FunctionCallee FDecl = TheModule.getOrInsertFunction(HelperName, CalleeType);
FunctionTags::Helper.addTo(cast<Function>(skipCasts(FDecl.getCallee())));
CallInst *Result = Builder.CreateCall(FDecl, InArgs);
if (TheCall.OutArguments.size() != 0)
Builder.CreateStore(Result, ResultDestination);
return Success;
}
IT::TranslationResult
IT::translate(PTCInstruction *Instr, MetaAddress PC, MetaAddress NextPC) {
const PTC::Instruction TheInstruction(Instr);
std::vector<Value *> InArgs;
for (uint64_t TemporaryId : TheInstruction.InArguments) {
auto *Load = Variables.load(Builder, TemporaryId);
if (Load == nullptr)
return Abort;
InArgs.push_back(Load);
}
auto ConstArgs = TheInstruction.ConstArguments;
LastPC = PC;
auto Result = translateOpcode(TheInstruction.opcode(),
ConstArgs.toVector(),
InArgs);
// Check if there was an error while translating the instruction
if (!Result)
return Abort;
size_t OutSize = TheInstruction.OutArguments.size();
revng_assert(Result->size() == OutSize);
// TODO: use ZipIterator here
for (unsigned I = 0; I < Result->size(); I++) {
auto *Destination = Variables.getOrCreate(TheInstruction.OutArguments[I]);
if (Destination == nullptr)
return Abort;
auto *Store = Builder.CreateStore(Result.get()[I], Destination);
if (PCH->affectsPC(Store)) {
// This is a PC-related store
PCH->handleStore(Builder, Store);
} else {
// If we're writing somewhere an immediate, register it for exploration
if (auto *Constant = dyn_cast<ConstantInt>(Store->getValueOperand())) {
MetaAddress Address = JumpTargets.fromPC(Constant->getLimitedValue());
if (Address.isValid() and PC != Address and JumpTargets.isPC(Address)
and not JumpTargets.hasJT(Address)) {
JumpTargets.registerSimpleLiteral(Address);
}
}
}
}
return Success;
}
void IT::registerDirectJumps() {
for (BasicBlock *ExitBB : ExitBlocks) {
auto [Result, NextPC] = PCH->getUniqueJumpTarget(ExitBB);
if (Result == NextJumpTarget::Unique and JumpTargets.isPC(NextPC)
and not JumpTargets.hasJT(NextPC)) {
JumpTargets.registerJT(NextPC, JTReason::DirectJump);
}
}
ExitBlocks.clear();
}
ErrorOr<std::vector<Value *>>
IT::translateOpcode(PTCOpcode Opcode,
std::vector<uint64_t> ConstArguments,
std::vector<Value *> InArguments) {
LLVMContext &Context = TheModule.getContext();
unsigned RegisterSize = getRegisterSize(Opcode);
Type *RegisterType = nullptr;
if (RegisterSize == 32)
RegisterType = Builder.getInt32Ty();
else if (RegisterSize == 64)
RegisterType = Builder.getInt64Ty();
else if (RegisterSize != 0)
revng_unreachable("Unexpected register size");
using v = std::vector<Value *>;
switch (Opcode) {
case PTC_INSTRUCTION_op_movi_i32:
case PTC_INSTRUCTION_op_movi_i64:
return v{ ConstantInt::get(RegisterType, ConstArguments[0]) };
case PTC_INSTRUCTION_op_discard:
// Let's overwrite the discarded temporary with a 0
return v{ ConstantInt::get(RegisterType, 0) };
case PTC_INSTRUCTION_op_mov_i32:
case PTC_INSTRUCTION_op_mov_i64:
return v{ Builder.CreateTrunc(InArguments[0], RegisterType) };
case PTC_INSTRUCTION_op_setcond_i32:
case PTC_INSTRUCTION_op_setcond_i64: {
Value *Compare = createICmp(Builder,
ConstArguments[0],
InArguments[0],
InArguments[1]);
// TODO: convert single-bit registers to i1
return v{ Builder.CreateZExt(Compare, RegisterType) };
}
case PTC_INSTRUCTION_op_movcond_i32: // Resist the fallthrough temptation
case PTC_INSTRUCTION_op_movcond_i64: {
Value *Compare = createICmp(Builder,
ConstArguments[0],
InArguments[0],
InArguments[1]);
Value *Select = Builder.CreateSelect(Compare,
InArguments[2],
InArguments[3]);
return v{ Select };
}
case PTC_INSTRUCTION_op_qemu_ld_i32:
case PTC_INSTRUCTION_op_qemu_ld_i64:
case PTC_INSTRUCTION_op_qemu_st_i32:
case PTC_INSTRUCTION_op_qemu_st_i64: {
PTCLoadStoreArg MemoryAccess;
MemoryAccess = ptc.parse_load_store_arg(ConstArguments[0]);
// What are we supposed to do in this case?
revng_assert(MemoryAccess.access_type != PTC_MEMORY_ACCESS_UNKNOWN);
unsigned Alignment = 1;
// Load size
IntegerType *MemoryType = nullptr;
switch (ptc_get_memory_access_size(MemoryAccess.type)) {
case PTC_MO_8:
MemoryType = Builder.getInt8Ty();
break;
case PTC_MO_16:
MemoryType = Builder.getInt16Ty();
break;
case PTC_MO_32:
MemoryType = Builder.getInt32Ty();
break;
case PTC_MO_64:
MemoryType = Builder.getInt64Ty();
break;
default:
revng_unreachable("Unexpected load size");
}
// If necessary, handle endianness mismatch
// TODO: it might be a bit overkill, but it be nice to make this function
// template-parametric w.r.t. endianness mismatch
Function *BSwapFunction = nullptr;
if (MemoryType != Builder.getInt8Ty() and EndianessMismatch)
BSwapFunction = Intrinsic::getDeclaration(&TheModule,
Intrinsic::bswap,
{ MemoryType });
bool SignExtend = ptc_is_sign_extended_load(MemoryAccess.type);
Value *Pointer = nullptr;
if (Opcode == PTC_INSTRUCTION_op_qemu_ld_i32
|| Opcode == PTC_INSTRUCTION_op_qemu_ld_i64) {
Pointer = Builder.CreateIntToPtr(InArguments[0],
MemoryType->getPointerTo());
auto *Load = Builder.CreateAlignedLoad(MemoryType,
Pointer,
MaybeAlign(Alignment));
Value *Loaded = Load;
if (BSwapFunction != nullptr)
Loaded = Builder.CreateCall(BSwapFunction, Load);
if (SignExtend)
return v{ Builder.CreateSExt(Loaded, RegisterType) };
else
return v{ Builder.CreateZExt(Loaded, RegisterType) };
} else if (Opcode == PTC_INSTRUCTION_op_qemu_st_i32
|| Opcode == PTC_INSTRUCTION_op_qemu_st_i64) {
Pointer = Builder.CreateIntToPtr(InArguments[1],
MemoryType->getPointerTo());
Value *Value = Builder.CreateTrunc(InArguments[0], MemoryType);
if (BSwapFunction != nullptr)
Value = Builder.CreateCall(BSwapFunction, Value);
Builder.CreateAlignedStore(Value, Pointer, MaybeAlign(Alignment));
return v{};
} else {
revng_unreachable("Unknown load type");
}
}
case PTC_INSTRUCTION_op_ld8u_i32:
case PTC_INSTRUCTION_op_ld8s_i32:
case PTC_INSTRUCTION_op_ld16u_i32:
case PTC_INSTRUCTION_op_ld16s_i32:
case PTC_INSTRUCTION_op_ld_i32:
case PTC_INSTRUCTION_op_ld8u_i64:
case PTC_INSTRUCTION_op_ld8s_i64:
case PTC_INSTRUCTION_op_ld16u_i64:
case PTC_INSTRUCTION_op_ld16s_i64:
case PTC_INSTRUCTION_op_ld32u_i64:
case PTC_INSTRUCTION_op_ld32s_i64:
case PTC_INSTRUCTION_op_ld_i64: {
Value *Base = dyn_cast<LoadInst>(InArguments[0])->getPointerOperand();
if (Base == nullptr || !Variables.isEnv(Base)) {
// TODO: emit warning
return std::errc::invalid_argument;
}
bool Signed;
switch (Opcode) {
case PTC_INSTRUCTION_op_ld_i32:
case PTC_INSTRUCTION_op_ld_i64:
case PTC_INSTRUCTION_op_ld8u_i32:
case PTC_INSTRUCTION_op_ld16u_i32:
case PTC_INSTRUCTION_op_ld8u_i64:
case PTC_INSTRUCTION_op_ld16u_i64:
case PTC_INSTRUCTION_op_ld32u_i64:
Signed = false;
break;
case PTC_INSTRUCTION_op_ld8s_i32:
case PTC_INSTRUCTION_op_ld16s_i32:
case PTC_INSTRUCTION_op_ld8s_i64:
case PTC_INSTRUCTION_op_ld16s_i64:
case PTC_INSTRUCTION_op_ld32s_i64:
Signed = true;
break;
default:
revng_unreachable("Unexpected opcode");
}
unsigned LoadSize;
switch (Opcode) {
case PTC_INSTRUCTION_op_ld8u_i32:
case PTC_INSTRUCTION_op_ld8s_i32:
case PTC_INSTRUCTION_op_ld8u_i64:
case PTC_INSTRUCTION_op_ld8s_i64:
LoadSize = 1;
break;
case PTC_INSTRUCTION_op_ld16u_i32:
case PTC_INSTRUCTION_op_ld16s_i32:
case PTC_INSTRUCTION_op_ld16u_i64:
case PTC_INSTRUCTION_op_ld16s_i64:
LoadSize = 2;
break;
case PTC_INSTRUCTION_op_ld_i32:
case PTC_INSTRUCTION_op_ld32u_i64:
case PTC_INSTRUCTION_op_ld32s_i64:
LoadSize = 4;
break;
case PTC_INSTRUCTION_op_ld_i64:
LoadSize = 8;
break;
default:
revng_unreachable("Unexpected opcode");
}
Value *Result = Variables.loadFromEnvOffset(Builder,
LoadSize,
ConstArguments[0]);
revng_assert(Result != nullptr);
// Zero/sign extend in the target dimension
if (Signed)
return v{ Builder.CreateSExt(Result, RegisterType) };
else
return v{ Builder.CreateZExt(Result, RegisterType) };
}
case PTC_INSTRUCTION_op_st8_i32:
case PTC_INSTRUCTION_op_st16_i32:
case PTC_INSTRUCTION_op_st_i32:
case PTC_INSTRUCTION_op_st8_i64:
case PTC_INSTRUCTION_op_st16_i64:
case PTC_INSTRUCTION_op_st32_i64:
case PTC_INSTRUCTION_op_st_i64: {
unsigned StoreSize;
switch (Opcode) {
case PTC_INSTRUCTION_op_st8_i32:
case PTC_INSTRUCTION_op_st8_i64:
StoreSize = 1;
break;
case PTC_INSTRUCTION_op_st16_i32:
case PTC_INSTRUCTION_op_st16_i64:
StoreSize = 2;
break;
case PTC_INSTRUCTION_op_st_i32:
case PTC_INSTRUCTION_op_st32_i64:
StoreSize = 4;
break;
case PTC_INSTRUCTION_op_st_i64:
StoreSize = 8;
break;
default:
revng_unreachable("Unexpected opcode");
}
Value *Base = dyn_cast<LoadInst>(InArguments[1])->getPointerOperand();
if (Base == nullptr || !Variables.isEnv(Base)) {
// TODO: emit warning
return std::errc::invalid_argument;
}
auto Result = Variables.storeToEnvOffset(Builder,
StoreSize,
ConstArguments[0],
InArguments[0]);
PCH->handleStore(Builder, *Result);
return v{};
}
case PTC_INSTRUCTION_op_add_i32:
case PTC_INSTRUCTION_op_sub_i32:
case PTC_INSTRUCTION_op_mul_i32:
case PTC_INSTRUCTION_op_div_i32:
case PTC_INSTRUCTION_op_divu_i32:
case PTC_INSTRUCTION_op_rem_i32:
case PTC_INSTRUCTION_op_remu_i32:
case PTC_INSTRUCTION_op_and_i32:
case PTC_INSTRUCTION_op_or_i32:
case PTC_INSTRUCTION_op_xor_i32:
case PTC_INSTRUCTION_op_shl_i32:
case PTC_INSTRUCTION_op_shr_i32:
case PTC_INSTRUCTION_op_sar_i32:
case PTC_INSTRUCTION_op_add_i64:
case PTC_INSTRUCTION_op_sub_i64:
case PTC_INSTRUCTION_op_mul_i64:
case PTC_INSTRUCTION_op_div_i64:
case PTC_INSTRUCTION_op_divu_i64:
case PTC_INSTRUCTION_op_rem_i64:
case PTC_INSTRUCTION_op_remu_i64:
case PTC_INSTRUCTION_op_and_i64:
case PTC_INSTRUCTION_op_or_i64:
case PTC_INSTRUCTION_op_xor_i64:
case PTC_INSTRUCTION_op_shl_i64:
case PTC_INSTRUCTION_op_shr_i64:
case PTC_INSTRUCTION_op_sar_i64: {
// TODO: assert on sizes?
Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode);
Value *Operation = Builder.CreateBinOp(BinaryOp,
InArguments[0],
InArguments[1]);
return v{ Operation };
}
case PTC_INSTRUCTION_op_div2_i32:
case PTC_INSTRUCTION_op_divu2_i32:
case PTC_INSTRUCTION_op_div2_i64:
case PTC_INSTRUCTION_op_divu2_i64: {
Instruction::BinaryOps DivisionOp, RemainderOp;
if (Opcode == PTC_INSTRUCTION_op_div2_i32
|| Opcode == PTC_INSTRUCTION_op_div2_i64) {
DivisionOp = Instruction::SDiv;
RemainderOp = Instruction::SRem;
} else if (Opcode == PTC_INSTRUCTION_op_divu2_i32
|| Opcode == PTC_INSTRUCTION_op_divu2_i64) {
DivisionOp = Instruction::UDiv;
RemainderOp = Instruction::URem;
} else {
revng_unreachable("Unknown operation type");
}
// TODO: we're ignoring InArguments[1], which is the MSB
// TODO: assert on sizes?
Value *Division = Builder.CreateBinOp(DivisionOp,
InArguments[0],
InArguments[2]);
Value *Remainder = Builder.CreateBinOp(RemainderOp,
InArguments[0],
InArguments[2]);
return v{ Division, Remainder };
}
case PTC_INSTRUCTION_op_rotr_i32:
case PTC_INSTRUCTION_op_rotr_i64:
case PTC_INSTRUCTION_op_rotl_i32:
case PTC_INSTRUCTION_op_rotl_i64: {
Value *Bits = ConstantInt::get(RegisterType, RegisterSize);
Instruction::BinaryOps FirstShiftOp, SecondShiftOp;
if (Opcode == PTC_INSTRUCTION_op_rotl_i32
|| Opcode == PTC_INSTRUCTION_op_rotl_i64) {
FirstShiftOp = Instruction::Shl;
SecondShiftOp = Instruction::LShr;
} else if (Opcode == PTC_INSTRUCTION_op_rotr_i32
|| Opcode == PTC_INSTRUCTION_op_rotr_i64) {
FirstShiftOp = Instruction::LShr;
SecondShiftOp = Instruction::Shl;
} else {
revng_unreachable("Unexpected opcode");
}
Value *FirstShift = Builder.CreateBinOp(FirstShiftOp,
InArguments[0],
InArguments[1]);
Value *SecondShiftAmount = Builder.CreateSub(Bits, InArguments[1]);
Value *SecondShift = Builder.CreateBinOp(SecondShiftOp,
InArguments[0],
SecondShiftAmount);
return v{ Builder.CreateOr(FirstShift, SecondShift) };
}
case PTC_INSTRUCTION_op_deposit_i32:
case PTC_INSTRUCTION_op_deposit_i64: {
unsigned Position = ConstArguments[0];
if (Position == RegisterSize)
return v{ InArguments[0] };
unsigned Length = ConstArguments[1];
uint64_t Bits = 0;
// Thou shall not << 32
if (Length == RegisterSize)
Bits = getMaxValue(RegisterSize);
else
Bits = (1 << Length) - 1;
// result = (t1 & ~(bits << position)) | ((t2 & bits) << position)
uint64_t BaseMask = ~(Bits << Position);
Value *MaskedBase = Builder.CreateAnd(InArguments[0], BaseMask);
Value *Deposit = Builder.CreateAnd(InArguments[1], Bits);
Value *ShiftedDeposit = Builder.CreateShl(Deposit, Position);
Value *Result = Builder.CreateOr(MaskedBase, ShiftedDeposit);
return v{ Result };
}
case PTC_INSTRUCTION_op_ext8s_i32:
case PTC_INSTRUCTION_op_ext16s_i32:
case PTC_INSTRUCTION_op_ext8u_i32:
case PTC_INSTRUCTION_op_ext16u_i32:
case PTC_INSTRUCTION_op_ext8s_i64:
case PTC_INSTRUCTION_op_ext16s_i64:
case PTC_INSTRUCTION_op_ext32s_i64:
case PTC_INSTRUCTION_op_ext8u_i64:
case PTC_INSTRUCTION_op_ext16u_i64:
case PTC_INSTRUCTION_op_ext32u_i64: {
Type *SourceType = nullptr;
switch (Opcode) {
case PTC_INSTRUCTION_op_ext8s_i32:
case PTC_INSTRUCTION_op_ext8u_i32:
case PTC_INSTRUCTION_op_ext8s_i64:
case PTC_INSTRUCTION_op_ext8u_i64:
SourceType = Builder.getInt8Ty();
break;
case PTC_INSTRUCTION_op_ext16s_i32:
case PTC_INSTRUCTION_op_ext16u_i32:
case PTC_INSTRUCTION_op_ext16s_i64:
case PTC_INSTRUCTION_op_ext16u_i64:
SourceType = Builder.getInt16Ty();
break;
case PTC_INSTRUCTION_op_ext32s_i64:
case PTC_INSTRUCTION_op_ext32u_i64:
SourceType = Builder.getInt32Ty();
break;
default:
revng_unreachable("Unexpected opcode");
}
Value *Truncated = Builder.CreateTrunc(InArguments[0], SourceType);
switch (Opcode) {
case PTC_INSTRUCTION_op_ext8s_i32:
case PTC_INSTRUCTION_op_ext8s_i64:
case PTC_INSTRUCTION_op_ext16s_i32:
case PTC_INSTRUCTION_op_ext16s_i64:
case PTC_INSTRUCTION_op_ext32s_i64:
return v{ Builder.CreateSExt(Truncated, RegisterType) };
case PTC_INSTRUCTION_op_ext8u_i32:
case PTC_INSTRUCTION_op_ext8u_i64:
case PTC_INSTRUCTION_op_ext16u_i32:
case PTC_INSTRUCTION_op_ext16u_i64:
case PTC_INSTRUCTION_op_ext32u_i64:
return v{ Builder.CreateZExt(Truncated, RegisterType) };
default:
revng_unreachable("Unexpected opcode");
}
}
case PTC_INSTRUCTION_op_not_i32:
case PTC_INSTRUCTION_op_not_i64:
return v{ Builder.CreateXor(InArguments[0], getMaxValue(RegisterSize)) };
case PTC_INSTRUCTION_op_neg_i32:
case PTC_INSTRUCTION_op_neg_i64: {
auto *InitialValue = ConstantInt::get(RegisterType, 0);
return v{ Builder.CreateSub(InitialValue, InArguments[0]) };
}
case PTC_INSTRUCTION_op_andc_i32:
case PTC_INSTRUCTION_op_andc_i64:
case PTC_INSTRUCTION_op_orc_i32:
case PTC_INSTRUCTION_op_orc_i64:
case PTC_INSTRUCTION_op_eqv_i32:
case PTC_INSTRUCTION_op_eqv_i64: {
Instruction::BinaryOps ExternalOp;
switch (Opcode) {
case PTC_INSTRUCTION_op_andc_i32:
case PTC_INSTRUCTION_op_andc_i64:
ExternalOp = Instruction::And;
break;
case PTC_INSTRUCTION_op_orc_i32:
case PTC_INSTRUCTION_op_orc_i64:
ExternalOp = Instruction::Or;
break;
case PTC_INSTRUCTION_op_eqv_i32:
case PTC_INSTRUCTION_op_eqv_i64:
ExternalOp = Instruction::Xor;
break;
default:
revng_unreachable("Unexpected opcode");
}
Value *Negate = Builder.CreateXor(InArguments[1],
getMaxValue(RegisterSize));
Value *Result = Builder.CreateBinOp(ExternalOp, InArguments[0], Negate);
return v{ Result };
}
case PTC_INSTRUCTION_op_nand_i32:
case PTC_INSTRUCTION_op_nand_i64: {
Value *AndValue = Builder.CreateAnd(InArguments[0], InArguments[1]);
Value *Result = Builder.CreateXor(AndValue, getMaxValue(RegisterSize));
return v{ Result };
}
case PTC_INSTRUCTION_op_nor_i32:
case PTC_INSTRUCTION_op_nor_i64: {
Value *OrValue = Builder.CreateOr(InArguments[0], InArguments[1]);
Value *Result = Builder.CreateXor(OrValue, getMaxValue(RegisterSize));
return v{ Result };
}
case PTC_INSTRUCTION_op_bswap16_i32:
case PTC_INSTRUCTION_op_bswap32_i32:
case PTC_INSTRUCTION_op_bswap16_i64:
case PTC_INSTRUCTION_op_bswap32_i64:
case PTC_INSTRUCTION_op_bswap64_i64: {
Type *SwapType = nullptr;
switch (Opcode) {
case PTC_INSTRUCTION_op_bswap16_i32:
case PTC_INSTRUCTION_op_bswap16_i64:
SwapType = Builder.getInt16Ty();
break;
case PTC_INSTRUCTION_op_bswap32_i32:
case PTC_INSTRUCTION_op_bswap32_i64:
SwapType = Builder.getInt32Ty();
break;
case PTC_INSTRUCTION_op_bswap64_i64:
SwapType = Builder.getInt64Ty();
break;
default:
revng_unreachable("Unexpected opcode");
}
Value *Truncated = Builder.CreateTrunc(InArguments[0], SwapType);
Function *BSwapFunction = Intrinsic::getDeclaration(&TheModule,
Intrinsic::bswap,
{ SwapType });
Value *Swapped = Builder.CreateCall(BSwapFunction, Truncated);
return v{ Builder.CreateZExt(Swapped, RegisterType) };
}
case PTC_INSTRUCTION_op_set_label: {
unsigned LabelId = ptc.get_arg_label_id(ConstArguments[0]);
std::stringstream LabelSS;
LabelSS << "bb." << JumpTargets.nameForAddress(LastPC);
LabelSS << "_L" << std::dec << LabelId;
std::string Label = LabelSS.str();
BasicBlock *Fallthrough = nullptr;
if (!LabeledBasicBlocks.contains(Label)) {
Fallthrough = BasicBlock::Create(Context, Label, TheFunction);
Fallthrough->moveAfter(Builder.GetInsertBlock());
LabeledBasicBlocks[Label] = Fallthrough;
} else {
// A basic block with that label already exist
Fallthrough = LabeledBasicBlocks[Label];
// Ensure it's empty
revng_assert(Fallthrough->begin() == Fallthrough->end());
// Move it to the bottom
Fallthrough->removeFromParent();
TheFunction->insert(TheFunction->end(), Fallthrough);
}
Builder.CreateBr(Fallthrough);
Blocks.push_back(Fallthrough);
Builder.SetInsertPoint(Fallthrough);
Variables.newBasicBlock();
return v{};
}
case PTC_INSTRUCTION_op_br:
case PTC_INSTRUCTION_op_brcond_i32:
case PTC_INSTRUCTION_op_brcond2_i32:
case PTC_INSTRUCTION_op_brcond_i64: {
// We take the last constant arguments, which is the LabelId both in
// conditional and unconditional jumps
unsigned LabelId = ptc.get_arg_label_id(ConstArguments.back());
std::stringstream LabelSS;
LabelSS << "bb." << JumpTargets.nameForAddress(LastPC);
LabelSS << "_L" << std::dec << LabelId;
std::string Label = LabelSS.str();
BasicBlock *Fallthrough = BasicBlock::Create(Context,
Label + "_ft",
TheFunction);
// Look for a matching label
BasicBlock *Target = nullptr;
if (!LabeledBasicBlocks.contains(Label)) {
// No matching label, create a temporary block
Target = BasicBlock::Create(Context, Label, TheFunction);
LabeledBasicBlocks[Label] = Target;
} else {
Target = LabeledBasicBlocks[Label];
}
if (Opcode == PTC_INSTRUCTION_op_br) {
// Unconditional jump
Builder.CreateBr(Target);
} else if (Opcode == PTC_INSTRUCTION_op_brcond_i32
|| Opcode == PTC_INSTRUCTION_op_brcond_i64) {
// Conditional jump
Value *Compare = createICmp(Builder,
ConstArguments[0],
InArguments[0],
InArguments[1]);
Builder.CreateCondBr(Compare, Target, Fallthrough);
} else {
revng_unreachable("Unhandled opcode");
}
Blocks.push_back(Fallthrough);
Builder.SetInsertPoint(Fallthrough);
Variables.newBasicBlock();
return v{};
}
case PTC_INSTRUCTION_op_exit_tb: {
auto *Zero = ConstantInt::get(Type::getInt32Ty(Context), 0);
Builder.CreateCall(JumpTargets.exitTB(), { Zero });
Builder.CreateUnreachable();
ExitBlocks.push_back(Builder.GetInsertBlock());
auto *NextBB = BasicBlock::Create(Context, "", TheFunction);
Blocks.push_back(NextBB);
Builder.SetInsertPoint(NextBB);
Variables.newBasicBlock();
return v{};
}
case PTC_INSTRUCTION_op_goto_tb:
// Nothing to do here
return v{};
case PTC_INSTRUCTION_op_add2_i32:
case PTC_INSTRUCTION_op_sub2_i32:
case PTC_INSTRUCTION_op_add2_i64:
case PTC_INSTRUCTION_op_sub2_i64: {
Value *FirstOpLow = nullptr;
Value *FirstOpHigh = nullptr;
Value *SecondOpLow = nullptr;
Value *SecondOpHigh = nullptr;
IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2);
FirstOpLow = Builder.CreateZExt(InArguments[0], DestinationType);
FirstOpHigh = Builder.CreateZExt(InArguments[1], DestinationType);
SecondOpLow = Builder.CreateZExt(InArguments[2], DestinationType);
SecondOpHigh = Builder.CreateZExt(InArguments[3], DestinationType);
FirstOpHigh = Builder.CreateShl(FirstOpHigh, RegisterSize);
SecondOpHigh = Builder.CreateShl(SecondOpHigh, RegisterSize);
Value *FirstOp = Builder.CreateOr(FirstOpHigh, FirstOpLow);
Value *SecondOp = Builder.CreateOr(SecondOpHigh, SecondOpLow);
Instruction::BinaryOps BinaryOp = opcodeToBinaryOp(Opcode);
Value *Result = Builder.CreateBinOp(BinaryOp, FirstOp, SecondOp);
Value *ResultLow = Builder.CreateTrunc(Result, RegisterType);
Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize);
Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, RegisterType);
return v{ ResultLow, ResultHigh };
}
case PTC_INSTRUCTION_op_mulu2_i32:
case PTC_INSTRUCTION_op_mulu2_i64:
case PTC_INSTRUCTION_op_muls2_i32:
case PTC_INSTRUCTION_op_muls2_i64: {
IntegerType *DestinationType = Builder.getIntNTy(RegisterSize * 2);
Value *FirstOp = nullptr;
Value *SecondOp = nullptr;
if (Opcode == PTC_INSTRUCTION_op_mulu2_i32
|| Opcode == PTC_INSTRUCTION_op_mulu2_i64) {
FirstOp = Builder.CreateZExt(InArguments[0], DestinationType);
SecondOp = Builder.CreateZExt(InArguments[1], DestinationType);
} else if (Opcode == PTC_INSTRUCTION_op_muls2_i32
|| Opcode == PTC_INSTRUCTION_op_muls2_i64) {
FirstOp = Builder.CreateSExt(InArguments[0], DestinationType);
SecondOp = Builder.CreateSExt(InArguments[1], DestinationType);
} else {
revng_unreachable("Unexpected opcode");
}
Value *Result = Builder.CreateMul(FirstOp, SecondOp);
Value *ResultLow = Builder.CreateTrunc(Result, RegisterType);
Value *ShiftedResult = Builder.CreateLShr(Result, RegisterSize);
Value *ResultHigh = Builder.CreateTrunc(ShiftedResult, RegisterType);
return v{ ResultLow, ResultHigh };
}
case PTC_INSTRUCTION_op_muluh_i32:
case PTC_INSTRUCTION_op_mulsh_i32:
case PTC_INSTRUCTION_op_muluh_i64:
case PTC_INSTRUCTION_op_mulsh_i64:
case PTC_INSTRUCTION_op_setcond2_i32:
case PTC_INSTRUCTION_op_trunc_shr_i32:
revng_unreachable("Instruction not implemented");
default:
revng_unreachable("Unknown opcode");
}
}