Skip to content

mull-project/libirm

Repository files navigation

libirm

Low-level IR mutations for LLVM Bitcode.

Build/Install

The easiest way (not necessarily the right one):

git clone https://github.com/mull-project/libirm.git --recursive
mkdir build.dir; cd build.dir
cmake -G Ninja ../libirm
ninja all
sudo ninja install

You'll get libirm.a and required headers under /usr/local/.

Usage

libirm operates on the level of a single LLVM instruction.

To use it you need only two methods:

bool canMutate(llvm::Instruction *instruction);
void mutate(llvm::Instruction *instruction);

The code below changes every add instruction into sub instruction.

#include <irm/irm.h>

void doSmth() {
  llvm::Function *function = getTheFunctionSomewhere();
  irm::AddToSub mutation;
  for (auto &instruction : llvm::instructions(function)) {
    if (mutation.canMutate(&instruction)) {
      mutation.mutate(&instruction);
    }
  }
}

Available Mutations

The set of currently available mutation is driven by the needs of the Mull project, so it is not exhaustive.

BinaryOperatorReplacement

BinaryOperatorReplacement(llvm::Instruction::BinaryOps from,
                          llvm::Instruction::BinaryOps to)

Replaces binary operator(from) with another one (to).

Examples:

  • irm::AddToSub: add i32 %a, i32 %b -> sub i32 %a, i32 %b
  • irm::OrToAnd: or i32 %a, i32 %b -> and i32 %a, i32 %b

You can find the list of predefined mutations at BinaryOperatorReplacement.h.

You can also create any missing mutation manually:

irm::BinaryOperatorReplacement addToXor(llvm::Instruction::BinaryOps::Add,
                                        llvm::Instruction::BinaryOps::Xor);

This will yield the add i32 %a, i32 %b to xor i32 %a, i32 %b mutation.

SwapBinaryOperands

SwapBinaryOperands(llvm::Instruction::BinaryOps op)

Swaps operands of a binary operator(op).

Examples:

  • irm::SwapSubOperands: sub i32 %a, i32 %b -> sub i32 %b, i32 %a
  • irm::SwapSDivOperands: sdiv i32 %a, i32 %b -> sdiv i32 %b, i32 %a

You can find the list of predefined mutations at SwapBinaryOperands.h.

You can also create any missing mutation manually:

irm::SwapBinaryOperands swapSub(llvm::Instruction::BinaryOps::Sub);

This will yield the sub i32 %a, i32 %b to sub i32 %b, i32 %a mutation.

SwapInstructionWithOperand

SwapInstructionWithOperand(unsigned opcode, unsigned index);

Replaces all uses of the instruction of kind opcode with its index operand.

Examples:

  • irm::SwapAddWithOperand_0: add i32 %a, i32 %b -> %a
  • irm::SwapFNegWithOperand: fneg float %a -> %a

You can find the list of predefined mutations at SwapInstructionWithOperand.h.

CallReplacement

CallReplacement(ConstValueConstructor *constConstructor, llvm::Type::TypeID returnTypeId)

Replaces a call with return type returnTypeId with a constant provided by ConstValueConstructor.

For example, irm::IntCallReplacement mutation(42) replaces call i32 @bar() with 42.

You can find the list of predefined mutations at CallReplacement.h.

You can create any missing mutation manually:

irm::CallReplacement halfReplacement(new FloatingValueConstructor(18.f), llvm::Type::HalfTyID);

This yields call half @bar() -> half 18 replacement.

CmpInstPredicateReplacement

CmpInstPredicateReplacement(llvm::Instruction::OtherOps cmpType,
                            llvm::CmpInst::Predicate from,
                            llvm::CmpInst::Predicate to)

Replaces comparison predicate. Examples:

  • irm::ICMP_EQToICMP_NE: icmp eq %a, i32 %b -> icmp ne i32 %a, i32 %b
  • irm::ICMP_SGTToICMP_SGE: icmp sgt i32 %a, i32 %b -> icmp sge i32 %a, i32 %b

You can find the list of predefined mutations at CmpInstPredicateReplacement.h.

You can also create any missing mutation manually:

irm::CmpInstPredicateReplacement lessOrEqualToEqual(llvm::Instruction::FCmp,
                                                    llvm::CmpInst::FCMP_OLE,
                                                    llvm::CmpInst::FCMP_OEQ);

This will yield the fcmp ole i32 %a, i32 %b to fcmp oeq i32 %a, i32 %b mutation.

ConstantReplacement

ConstantReplacement(ConstValueConstructor *constConstructor,
                    ConstValueConstructor *zeroConstConstructor,
                    llvm::Value::ValueTy valueType,
                    unsigned operandPosition)

Replaces a constant with a value provided by constConstructor if the constant is zero. If the constant is not zero, then it is replaces by zero value provided by zeroConstConstructor.

Applies only to an operandPosition operand.

For example, irm::ConstIntReplacement mutation(42, 1) will mutate add 22, 0 to add 22, 42, while add i32 22, i32 15 mutates to add i32 22, i32 0.

Please note that only second operand is affected in both cases.

You can find the list of predefined mutations at ConstantReplacement.h.

IntrinsicReplacement

IntrinsicReplacement(llvm::Intrinsic::ID from,
                     llvm::Intrinsic::ID to)

Replaces one intrinsic(from) with another(to). It is assumed that both intrinsics have the same signature.

For example, irm::sadd_with_overflowTossub_with_overflow mutation will replace any call to llvm.sadd.with.overflow.* with llvm.ssub.with.overflow.*.

You can find the list of predefined mutations at IntrinsicReplacement.h.

You can create custom mutations:

irm::IntrinsicReplacement sinToCos(llvm::Intrinsic::int_cos,
                                   llvm::Intrinsic::int_sin);

StoreValueReplacement

StoreValueReplacement(ConstValueConstructor *constConstructor,
                      llvm::Type::TypeID typeID)

Similar to CallReplacement, but replaces a store value with a constant provided by constConstructor. For example, irm::StoreIntReplacement mutation(15) will change store i32 %x, i32* %addr to store i32 15, i32* %addr.

You can find the list of predefined mutations at StoreValueReplacement.h.

You can create any missing mutation manually:

irm::StoreValueReplacement halfReplacement(new FloatingValueConstructor(18.f),
                                           llvm::Type::HalfTyID);

VoidCallRemoval

VoidCallRemoval(bool onlyIntrinsics)

Removes a call to a void function or intrinsic. There are two shortcut classes RemoveVoidFunctionCall and RemoveVoidIntrinsicsCall, that remove only function calls and only intrinsics, respectively.

Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change. Please make sure to update tests as appropriate.

License

Apache 2

About

Low-level IR mutations for LLVM Bitcode

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •