mirror of
https://github.com/revng/revng
synced 2026-06-21 14:07:57 +00:00
341 lines
13 KiB
C++
341 lines
13 KiB
C++
//
|
|
// Copyright rev.ng Srls. See LICENSE.md for details.
|
|
//
|
|
|
|
#include <array>
|
|
|
|
#include "llvm/IR/IRBuilder.h"
|
|
#include "llvm/IR/Instruction.h"
|
|
#include "llvm/IR/Instructions.h"
|
|
#include "llvm/IR/LLVMContext.h"
|
|
#include "llvm/IR/PatternMatch.h"
|
|
#include "llvm/Pass.h"
|
|
|
|
#include "revng/Support/Assert.h"
|
|
#include "revng/Support/OpaqueFunctionsPool.h"
|
|
|
|
#include "revng-c/Support/FunctionTags.h"
|
|
#include "revng-c/Support/IRHelpers.h"
|
|
|
|
using namespace llvm;
|
|
|
|
static cl::opt<std::string> LanguageName("language",
|
|
cl::desc("Language for operator "
|
|
"precedence table"),
|
|
cl::Hidden,
|
|
cl::init("c"));
|
|
|
|
namespace {
|
|
constexpr unsigned getInstructionLLVMOpcodeCount() {
|
|
#define LAST_OTHER_INST(NR) return NR;
|
|
#include "llvm/IR/Instruction.def"
|
|
}
|
|
} // namespace
|
|
|
|
enum Associativity : unsigned { NONE = 0, LEFT_TO_RIGHT, RIGHT_TO_LEFT };
|
|
|
|
enum CustomInstruction : unsigned {
|
|
AddressOf = getInstructionLLVMOpcodeCount() + 1,
|
|
Assignment = getInstructionLLVMOpcodeCount() + 2,
|
|
Cast = getInstructionLLVMOpcodeCount() + 3,
|
|
Indirection = getInstructionLLVMOpcodeCount() + 4,
|
|
MemberAccess = getInstructionLLVMOpcodeCount() + 5
|
|
};
|
|
|
|
struct InstToOpPrec {
|
|
unsigned InstructionOpcode;
|
|
int Precedence;
|
|
Associativity Associativity;
|
|
|
|
constexpr InstToOpPrec(unsigned InstructionOpcode,
|
|
int Precedence,
|
|
enum Associativity Associativity) :
|
|
InstructionOpcode(InstructionOpcode),
|
|
Precedence(Precedence),
|
|
Associativity(Associativity) {}
|
|
};
|
|
|
|
// Table that maps LLVM opcodes to the equivalent C operator precedence priority
|
|
static constexpr std::array<const InstToOpPrec, 35>
|
|
LLVMOpcodeToCOpPrecedenceArray{
|
|
{ { InstToOpPrec(CustomInstruction::Assignment, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Select, 1, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Or, 2, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Xor, 3, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::And, 4, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::ICmp, 5, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FCmp, 5, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Shl, 6, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::LShr, 6, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::AShr, 6, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Add, 7, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FAdd, 7, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Sub, 7, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FSub, 7, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Mul, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FMul, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::UDiv, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::SDiv, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FDiv, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::URem, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::SRem, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FRem, 8, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FNeg, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Load, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::SExt, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::ZExt, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Trunc, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::IntToPtr, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::PtrToInt, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::BitCast, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::GetElementPtr, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::AddressOf, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::Indirection, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::Cast, 9, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::MemberAccess, 10, LEFT_TO_RIGHT) } }
|
|
};
|
|
|
|
static constexpr std::array<const InstToOpPrec, 35>
|
|
LLVMOpcodeToNopOpPrecedenceArray{
|
|
{ { InstToOpPrec(CustomInstruction::Assignment, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Select, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Or, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Xor, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::And, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::ICmp, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FCmp, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Shl, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::LShr, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::AShr, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Add, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FAdd, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Sub, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FSub, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::Mul, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FMul, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::UDiv, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::SDiv, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FDiv, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::URem, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::SRem, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FRem, 0, LEFT_TO_RIGHT) },
|
|
{ InstToOpPrec(Instruction::FNeg, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Load, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::SExt, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::ZExt, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::Trunc, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::IntToPtr, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::PtrToInt, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::BitCast, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(Instruction::GetElementPtr, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::AddressOf, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::Indirection, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::Cast, 0, RIGHT_TO_LEFT) },
|
|
{ InstToOpPrec(CustomInstruction::MemberAccess, 0, LEFT_TO_RIGHT) } }
|
|
};
|
|
|
|
static auto
|
|
findOpcode(const std::array<const InstToOpPrec, 35> *Table, unsigned Opcode) {
|
|
return find_if(*Table, [&](const auto &Elem) {
|
|
return Elem.InstructionOpcode == Opcode;
|
|
});
|
|
}
|
|
|
|
static bool isCustomOpcode(Instruction *I) {
|
|
auto *CalledFunc = cast<CallInst>(I)->getCalledFunction();
|
|
if (!CalledFunc)
|
|
return false;
|
|
|
|
if (FunctionTags::AddressOf.isTagOf(CalledFunc)
|
|
|| FunctionTags::AssignmentMarker.isTagOf(CalledFunc)
|
|
|| FunctionTags::Assign.isTagOf(CalledFunc)
|
|
|| (FunctionTags::ModelCast.isTagOf(CalledFunc))
|
|
|| (FunctionTags::ModelGEP.isTagOf(CalledFunc)))
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
static unsigned getCustomOpcode(Instruction *I) {
|
|
auto *CalledFunc = cast<CallInst>(I)->getCalledFunction();
|
|
|
|
if (FunctionTags::AddressOf.isTagOf(CalledFunc))
|
|
return CustomInstruction::AddressOf;
|
|
else if (FunctionTags::AssignmentMarker.isTagOf(CalledFunc)
|
|
or FunctionTags::Assign.isTagOf(CalledFunc))
|
|
return CustomInstruction::Assignment;
|
|
else if (FunctionTags::ModelCast.isTagOf(CalledFunc))
|
|
return CustomInstruction::Cast;
|
|
else if (FunctionTags::ModelGEP.isTagOf(CalledFunc)) {
|
|
if (cast<CallInst>(I)->getNumArgOperands() > 2)
|
|
return CustomInstruction::MemberAccess;
|
|
return CustomInstruction::Indirection;
|
|
}
|
|
|
|
revng_abort();
|
|
}
|
|
|
|
static unsigned getOpcode(Instruction *I) {
|
|
if (isa<CallInst>(I))
|
|
if (isCustomOpcode(I))
|
|
return getCustomOpcode(I);
|
|
|
|
return I->getOpcode();
|
|
}
|
|
|
|
struct OperatorPrecedenceResolutionPass : public llvm::FunctionPass {
|
|
private:
|
|
const std::array<const InstToOpPrec, 35>
|
|
*LLVMOpcodeToLangOpPrecedenceArray = nullptr;
|
|
|
|
public:
|
|
static char ID;
|
|
|
|
OperatorPrecedenceResolutionPass() : FunctionPass(ID) {
|
|
if (LanguageName == "C" || LanguageName == "c")
|
|
LLVMOpcodeToLangOpPrecedenceArray = &LLVMOpcodeToCOpPrecedenceArray;
|
|
else if (LanguageName == "NOP" || LanguageName == "nop")
|
|
LLVMOpcodeToLangOpPrecedenceArray = &LLVMOpcodeToNopOpPrecedenceArray;
|
|
revng_assert(LLVMOpcodeToLangOpPrecedenceArray);
|
|
}
|
|
|
|
bool runOnFunction(llvm::Function &F) override;
|
|
|
|
void getAnalysisUsage(llvm::AnalysisUsage &AU) const override {
|
|
AU.setPreservesCFG();
|
|
}
|
|
|
|
public:
|
|
bool needsParentheses(Instruction *I, Use &U);
|
|
};
|
|
|
|
enum OperandSide : unsigned { LHS = 0, RHS };
|
|
|
|
using OPRP = OperatorPrecedenceResolutionPass;
|
|
|
|
bool OPRP::needsParentheses(Instruction *I, Use &U) {
|
|
// We need to decide when to emit parentheses (by default, always, most
|
|
// conservative choice), yet, there are some cases, e.g. w/ binary
|
|
// operator instructions, in which we can lean on their precedence &
|
|
// associativity to verify whether we need to emit parentheses or
|
|
// not for sure.
|
|
bool VerifyParentheses = false;
|
|
|
|
// Isn't the use an instruction? Will not emit parentheses
|
|
if (!isa<Instruction>(U.get()))
|
|
return false;
|
|
|
|
// Verify emission of parentheses for binary operators, load and cast
|
|
// instructions. Always emit parentheses when encountering calls.
|
|
if (isa<BinaryOperator>(I) || isa<CmpInst>(I) || isa<ICmpInst>(I)
|
|
|| isa<SelectInst>(I) || isa<LoadInst>(I) || isa<CastInst>(I))
|
|
VerifyParentheses = true;
|
|
else if (isa<AllocaInst>(I) || isa<InsertElementInst>(I)
|
|
|| isa<ExtractElementInst>(I) || isa<InsertValueInst>(I)
|
|
|| isa<ExtractValueInst>(I) || isa<ShuffleVectorInst>(I)
|
|
|| isa<StoreInst>(I) || isa<BranchInst>(I) || isa<CallBrInst>(I)
|
|
|| isa<IndirectBrInst>(I) || isa<ReturnInst>(I)
|
|
|| isa<IntrinsicInst>(I))
|
|
return false;
|
|
|
|
// Does the current instruction represent a custom operator?
|
|
if (isa<CallInst>(I) && isCustomOpcode(I)) {
|
|
switch (getCustomOpcode(I)) {
|
|
case CustomInstruction::AddressOf:
|
|
case CustomInstruction::Indirection:
|
|
case CustomInstruction::MemberAccess:
|
|
VerifyParentheses = (U.getOperandNo() == 1);
|
|
break;
|
|
case CustomInstruction::Assignment:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
Instruction *Ins = cast<Instruction>(U.get());
|
|
|
|
// Skip parenthesizing the expression when the Use is an `Assignment`
|
|
if (isa<CallInst>(Ins) && isCustomOpcode(Ins)
|
|
&& getCustomOpcode(Ins) == CustomInstruction::Assignment)
|
|
return false;
|
|
|
|
// No need to emit parentheses when the operand is a custom operator or a
|
|
// general instruction of normal function call. This may be subject to change
|
|
// in the future, up to the addition of new operators.
|
|
if (isa<CallInst>(I) && !isCustomOpcode(I)
|
|
&& cast<CallInst>(I)->getCalledFunction() && isa<Instruction>(Ins))
|
|
return false;
|
|
|
|
if (VerifyParentheses) {
|
|
auto CurrentOpIt = findOpcode(LLVMOpcodeToLangOpPrecedenceArray,
|
|
getOpcode(I));
|
|
revng_assert(CurrentOpIt != nullptr);
|
|
|
|
auto OperandOpIt = findOpcode(LLVMOpcodeToLangOpPrecedenceArray,
|
|
getOpcode(Ins));
|
|
|
|
if (OperandOpIt != LLVMOpcodeToLangOpPrecedenceArray->end()) {
|
|
if (CurrentOpIt->Precedence < OperandOpIt->Precedence) {
|
|
return false;
|
|
} else if (CurrentOpIt->Precedence == OperandOpIt->Precedence) {
|
|
OperandSide Side = U.getOperandNo() == 0 ? LHS : RHS;
|
|
// Binary operator?
|
|
if (I->getNumOperands() != 1 && !isa<CallInst>(I)) {
|
|
if (CurrentOpIt->Associativity == LEFT_TO_RIGHT && Side != RHS)
|
|
return false;
|
|
else if (CurrentOpIt->Associativity == RIGHT_TO_LEFT && Side != LHS)
|
|
return false;
|
|
} else {
|
|
// If the instruction operator is unary, and the associativity is
|
|
// alike, no parentheses are needed.
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool OPRP::runOnFunction(Function &F) {
|
|
OpaqueFunctionsPool<Type *> ParenthesesPool(F.getParent(), false);
|
|
initParenthesesPool(ParenthesesPool);
|
|
|
|
std::vector<std::pair<Instruction *, Use *>> InstructionsToBeParenthesized;
|
|
for (BasicBlock &BB : F)
|
|
for (Instruction &I : BB)
|
|
for (Use &Op : I.operands())
|
|
if (needsParentheses(&I, Op))
|
|
InstructionsToBeParenthesized.emplace_back(&I, &Op);
|
|
|
|
if (InstructionsToBeParenthesized.empty())
|
|
return false;
|
|
|
|
IRBuilder<> Builder(F.getContext());
|
|
for (const auto &[I, Op] : InstructionsToBeParenthesized) {
|
|
Builder.SetInsertPoint(I);
|
|
Instruction *Ins = cast<Instruction>(Op->get());
|
|
|
|
Type *OpToReplaceType = I->getOperand(Op->getOperandNo())->getType();
|
|
|
|
auto *ParenthesisFunction = ParenthesesPool.get(OpToReplaceType,
|
|
OpToReplaceType,
|
|
{ Ins->getType() },
|
|
"parentheses");
|
|
Value *Call = Builder.CreateCall(ParenthesisFunction, { Ins });
|
|
I->setOperand(Op->getOperandNo(), Call);
|
|
}
|
|
|
|
// OPRP has executed for this function
|
|
F.setMetadata(ExplicitParenthesesMDName, MDNode::get(F.getContext(), {}));
|
|
|
|
return true;
|
|
}
|
|
|
|
char OPRP::ID = 0;
|
|
|
|
static RegisterPass<OPRP> X("operatorprecedence-resolution",
|
|
"A simple pass that resolves precedence of "
|
|
"operators with parentheses appropriately.",
|
|
false,
|
|
false);
|