#include "Symbolizer.h"
#include <cstdint>
#include <llvm/ADT/SmallPtrSet.h>
#include <llvm/IR/Constants.h>
#include <llvm/IR/GetElementPtrTypeIterator.h>
#include <llvm/IR/Intrinsics.h>
#include <llvm/Transforms/Utils/BasicBlockUtils.h>
#include "Runtime.h"
using namespace llvm;
void Symbolizer::symbolizeFunctionArguments(Function &F) {
if (F.getName() == "main")
return;
IRBuilder<> IRB(F.getEntryBlock().getFirstNonPHI());
for (auto &arg : F.args()) {
if (!arg.user_empty())
symbolicExpressions[&arg] = IRB.CreateCall(runtime.getParameterExpression,
IRB.getInt8(arg.getArgNo()));
}
}
void Symbolizer::insertBasicBlockNotification(llvm::BasicBlock &B) {
IRBuilder<> IRB(&*B.getFirstInsertionPt());
IRB.CreateCall(runtime.notifyBasicBlock, getTargetPreferredInt(&B));
}
void Symbolizer::finalizePHINodes() {
SmallPtrSet<PHINode *, 32> nodesToErase;
for (auto *phi : phiNodes) {
auto symbolicPHI = cast<PHINode>(symbolicExpressions[phi]);
if (std::all_of(phi->op_begin(), phi->op_end(), [this](Value *input) {
return (getSymbolicExpression(input) == nullptr);
})) {
nodesToErase.insert(symbolicPHI);
continue;
}
for (unsigned incoming = 0, totalIncoming = phi->getNumIncomingValues();
incoming < totalIncoming; incoming++) {
symbolicPHI->setIncomingValue(
incoming,
getSymbolicExpressionOrNull(phi->getIncomingValue(incoming)));
}
}
for (auto *symbolicPHI : nodesToErase) {
symbolicPHI->replaceAllUsesWith(
ConstantPointerNull::get(cast<PointerType>(symbolicPHI->getType())));
symbolicPHI->eraseFromParent();
}
symbolicExpressions.clear();
}
void Symbolizer::shortCircuitExpressionUses() {
for (auto &symbolicComputation : expressionUses) {
assert(!symbolicComputation.inputs.empty() &&
"Symbolic computation has no inputs");
IRBuilder<> IRB(symbolicComputation.firstInstruction);
auto *nullExpression = ConstantPointerNull::get(IRB.getInt8PtrTy());
std::vector<Value *> nullChecks;
for (const auto &input : symbolicComputation.inputs) {
nullChecks.push_back(
IRB.CreateICmpEQ(nullExpression, input.getSymbolicOperand()));
}
auto *allConcrete = nullChecks[0];
for (unsigned argIndex = 1; argIndex < nullChecks.size(); argIndex++) {
allConcrete = IRB.CreateAnd(allConcrete, nullChecks[argIndex]);
}
auto *head = symbolicComputation.firstInstruction->getParent();
auto *slowPath = SplitBlock(head, symbolicComputation.firstInstruction);
auto *tail = SplitBlock(slowPath,
symbolicComputation.lastInstruction->getNextNode());
ReplaceInstWithInst(head->getTerminator(),
BranchInst::Create(tail, slowPath, allConcrete));
auto numUnknownConcreteness = std::count_if(
symbolicComputation.inputs.begin(), symbolicComputation.inputs.end(),
[&](const Input &input) {
return (input.getSymbolicOperand() != nullExpression);
});
for (unsigned argIndex = 0; argIndex < symbolicComputation.inputs.size();
argIndex++) {
auto &argument = symbolicComputation.inputs[argIndex];
auto *originalArgExpression = argument.getSymbolicOperand();
auto *argCheckBlock = symbolicComputation.firstInstruction->getParent();
bool needRuntimeCheck = originalArgExpression != nullExpression;
if (needRuntimeCheck && (numUnknownConcreteness == 1))
continue;
if (needRuntimeCheck) {
auto *argExpressionBlock = SplitBlockAndInsertIfThen(
nullChecks[argIndex], symbolicComputation.firstInstruction,
false);
IRB.SetInsertPoint(argExpressionBlock);
} else {
IRB.SetInsertPoint(symbolicComputation.firstInstruction);
}
auto *newArgExpression =
createValueExpression(argument.concreteValue, IRB);
Value *finalArgExpression;
if (needRuntimeCheck) {
IRB.SetInsertPoint(symbolicComputation.firstInstruction);
auto *argPHI = IRB.CreatePHI(IRB.getInt8PtrTy(), 2);
argPHI->addIncoming(originalArgExpression, argCheckBlock);
argPHI->addIncoming(newArgExpression, newArgExpression->getParent());
finalArgExpression = argPHI;
} else {
finalArgExpression = newArgExpression;
}
argument.replaceOperand(finalArgExpression);
}
if (!symbolicComputation.lastInstruction->use_empty()) {
IRB.SetInsertPoint(&tail->front());
auto *finalExpression = IRB.CreatePHI(IRB.getInt8PtrTy(), 2);
symbolicComputation.lastInstruction->replaceAllUsesWith(finalExpression);
finalExpression->addIncoming(ConstantPointerNull::get(IRB.getInt8PtrTy()),
head);
finalExpression->addIncoming(
symbolicComputation.lastInstruction,
symbolicComputation.lastInstruction->getParent());
}
}
}
void Symbolizer::handleIntrinsicCall(CallBase &I) {
auto *callee = I.getCalledFunction();
switch (callee->getIntrinsicID()) {
case Intrinsic::lifetime_start:
case Intrinsic::lifetime_end:
case Intrinsic::dbg_declare:
case Intrinsic::dbg_value:
case Intrinsic::is_constant:
case Intrinsic::trap:
case Intrinsic::invariant_start:
case Intrinsic::invariant_end:
case Intrinsic::assume:
break;
case Intrinsic::memcpy: {
IRBuilder<> IRB(&I);
tryAlternative(IRB, I.getOperand(0));
tryAlternative(IRB, I.getOperand(1));
tryAlternative(IRB, I.getOperand(2));
IRB.CreateCall(runtime.memcpy,
{I.getOperand(0), I.getOperand(1),
IRB.CreateZExtOrTrunc(I.getOperand(2), intPtrType)});
break;
}
case Intrinsic::memset: {
IRBuilder<> IRB(&I);
tryAlternative(IRB, I.getOperand(0));
tryAlternative(IRB, I.getOperand(2));
IRB.CreateCall(runtime.memset,
{I.getOperand(0),
getSymbolicExpressionOrNull(I.getOperand(1)),
IRB.CreateZExtOrTrunc(I.getOperand(2), intPtrType)});
break;
}
case Intrinsic::memmove: {
IRBuilder<> IRB(&I);
tryAlternative(IRB, I.getOperand(0));
tryAlternative(IRB, I.getOperand(1));
tryAlternative(IRB, I.getOperand(2));
IRB.CreateCall(runtime.memmove,
{I.getOperand(0), I.getOperand(1),
IRB.CreateZExtOrTrunc(I.getOperand(2), intPtrType)});
break;
}
case Intrinsic::stacksave: {
break;
}
case Intrinsic::stackrestore:
break;
case Intrinsic::expect:
if (auto *expr = getSymbolicExpression(I.getArgOperand(0)))
symbolicExpressions[&I] = expr;
break;
case Intrinsic::fabs: {
IRBuilder<> IRB(&I);
auto abs = buildRuntimeCall(IRB, runtime.buildFloatAbs, I.getOperand(0));
registerSymbolicComputation(abs, &I);
break;
}
case Intrinsic::cttz:
case Intrinsic::ctpop:
case Intrinsic::ctlz: {
errs() << "Warning: losing track of symbolic expressions at bit-count "
"operation "
<< I << "\n";
break;
}
case Intrinsic::returnaddress: {
errs() << "Warning: using concrete value for return address\n";
break;
}
case Intrinsic::bswap: {
IRBuilder<> IRB(&I);
auto swapped = buildRuntimeCall(IRB, runtime.buildBswap, I.getOperand(0));
registerSymbolicComputation(swapped, &I);
break;
}
default:
errs() << "Warning: unhandled LLVM intrinsic " << callee->getName()
<< "; the result will be concretized\n";
break;
}
}
void Symbolizer::handleInlineAssembly(CallInst &I) {
if (I.getType()->isVoidTy()) {
errs() << "Warning: skipping over inline assembly " << I << '\n';
return;
}
errs() << "Warning: losing track of symbolic expressions at inline assembly "
<< I << '\n';
}
void Symbolizer::handleFunctionCall(CallBase &I, Instruction *returnPoint) {
auto *callee = I.getCalledFunction();
if (callee != nullptr && callee->isIntrinsic()) {
handleIntrinsicCall(I);
return;
}
IRBuilder<> IRB(returnPoint);
IRB.CreateCall(runtime.notifyRet, getTargetPreferredInt(&I));
IRB.SetInsertPoint(&I);
IRB.CreateCall(runtime.notifyCall, getTargetPreferredInt(&I));
if (callee == nullptr)
tryAlternative(IRB, I.getCalledOperand());
for (Use &arg : I.args())
IRB.CreateCall(runtime.setParameterExpression,
{ConstantInt::get(IRB.getInt8Ty(), arg.getOperandNo()),
getSymbolicExpressionOrNull(arg)});
if (!I.user_empty()) {
IRB.CreateCall(runtime.setReturnExpression,
ConstantPointerNull::get(IRB.getInt8PtrTy()));
IRB.SetInsertPoint(returnPoint);
symbolicExpressions[&I] = IRB.CreateCall(runtime.getReturnExpression);
}
}
void Symbolizer::visitBinaryOperator(BinaryOperator &I) {
IRBuilder<> IRB(&I);
SymFnT handler = runtime.binaryOperatorHandlers.at(I.getOpcode());
if (I.getOperand(0)->getType() == IRB.getInt1Ty()) {
switch (I.getOpcode()) {
case Instruction::And:
handler = runtime.buildBoolAnd;
break;
case Instruction::Or:
handler = runtime.buildBoolOr;
break;
case Instruction::Xor:
handler = runtime.buildBoolXor;
break;
default:
errs() << "Can't handle Boolean operator " << I << '\n';
llvm_unreachable("Unknown Boolean operator");
break;
}
}
assert(handler && "Unable to handle binary operator");
auto runtimeCall =
buildRuntimeCall(IRB, handler, {I.getOperand(0), I.getOperand(1)});
registerSymbolicComputation(runtimeCall, &I);
}
void Symbolizer::visitSelectInst(SelectInst &I) {
IRBuilder<> IRB(&I);
auto runtimeCall = buildRuntimeCall(IRB, runtime.pushPathConstraint,
{{I.getCondition(), true},
{I.getCondition(), false},
{getTargetPreferredInt(&I), false}});
registerSymbolicComputation(runtimeCall);
}
void Symbolizer::visitCmpInst(CmpInst &I) {
IRBuilder<> IRB(&I);
SymFnT handler = runtime.comparisonHandlers.at(I.getPredicate());
assert(handler && "Unable to handle icmp/fcmp variant");
auto runtimeCall =
buildRuntimeCall(IRB, handler, {I.getOperand(0), I.getOperand(1)});
registerSymbolicComputation(runtimeCall, &I);
}
void Symbolizer::visitReturnInst(ReturnInst &I) {
if (I.getReturnValue() == nullptr)
return;
IRBuilder<> IRB(&I);
IRB.CreateCall(runtime.setReturnExpression,
getSymbolicExpressionOrNull(I.getReturnValue()));
}
void Symbolizer::visitBranchInst(BranchInst &I) {
if (I.isUnconditional())
return;
IRBuilder<> IRB(&I);
auto runtimeCall = buildRuntimeCall(IRB, runtime.pushPathConstraint,
{{I.getCondition(), true},
{I.getCondition(), false},
{getTargetPreferredInt(&I), false}});
registerSymbolicComputation(runtimeCall);
}
void Symbolizer::visitIndirectBrInst(IndirectBrInst &I) {
IRBuilder<> IRB(&I);
tryAlternative(IRB, I.getAddress());
}
void Symbolizer::visitCallInst(CallInst &I) {
if (I.isInlineAsm())
handleInlineAssembly(I);
else
handleFunctionCall(I, I.getNextNode());
}
void Symbolizer::visitInvokeInst(InvokeInst &I) {
auto *newBlock = SplitCriticalEdge(I.getParent(), I.getNormalDest());
handleFunctionCall(I, newBlock != nullptr
? newBlock->getFirstNonPHI()
: I.getNormalDest()->getFirstNonPHI());
}
void Symbolizer::visitAllocaInst(AllocaInst & ) {
}
void Symbolizer::visitLoadInst(LoadInst &I) {
IRBuilder<> IRB(&I);
auto *addr = I.getPointerOperand();
tryAlternative(IRB, addr);
auto *dataType = I.getType();
auto *data = IRB.CreateCall(
runtime.readMemory,
{IRB.CreatePtrToInt(addr, intPtrType),
ConstantInt::get(intPtrType, dataLayout.getTypeStoreSize(dataType)),
ConstantInt::get(IRB.getInt8Ty(), isLittleEndian(dataType) ? 1 : 0)});
if (dataType->isFloatingPointTy()) {
data = IRB.CreateCall(runtime.buildBitsToFloat,
{data, IRB.getInt1(dataType->isDoubleTy())});
}
symbolicExpressions[&I] = data;
}
void Symbolizer::visitStoreInst(StoreInst &I) {
IRBuilder<> IRB(&I);
tryAlternative(IRB, I.getPointerOperand());
auto *data = getSymbolicExpressionOrNull(I.getValueOperand());
auto *dataType = I.getValueOperand()->getType();
if (dataType->isFloatingPointTy()) {
data = IRB.CreateCall(runtime.buildFloatToBits, data);
}
IRB.CreateCall(
runtime.writeMemory,
{IRB.CreatePtrToInt(I.getPointerOperand(), intPtrType),
ConstantInt::get(intPtrType, dataLayout.getTypeStoreSize(dataType)),
data,
ConstantInt::get(IRB.getInt8Ty(), dataLayout.isLittleEndian() ? 1 : 0)});
}
void Symbolizer::visitGetElementPtrInst(GetElementPtrInst &I) {
if (getSymbolicExpression(I.getPointerOperand()) == nullptr &&
std::all_of(I.idx_begin(), I.idx_end(), [this](Value *index) {
return (getSymbolicExpression(index) == nullptr);
})) {
return;
}
if (std::all_of(I.idx_begin(), I.idx_end(), [](Value *index) {
auto *ci = dyn_cast<ConstantInt>(index);
return (ci != nullptr && ci->isZero());
})) {
symbolicExpressions[&I] = getSymbolicExpression(I.getPointerOperand());
return;
}
IRBuilder<> IRB(&I);
SymbolicComputation symbolicComputation;
Value *currentAddress = I.getPointerOperand();
for (auto type_it = gep_type_begin(I), type_end = gep_type_end(I);
type_it != type_end; ++type_it) {
auto *index = type_it.getOperand();
std::pair<Value *, bool> addressContribution;
if (auto *structType = type_it.getStructTypeOrNull()) {
unsigned memberIndex = cast<ConstantInt>(index)->getZExtValue();
unsigned memberOffset =
dataLayout.getStructLayout(structType)->getElementOffset(memberIndex);
addressContribution = {ConstantInt::get(intPtrType, memberOffset), true};
} else {
if (auto *ci = dyn_cast<ConstantInt>(index);
ci != nullptr && ci->isZero()) {
continue;
}
unsigned elementSize =
dataLayout.getTypeAllocSize(type_it.getIndexedType());
if (auto indexWidth = index->getType()->getIntegerBitWidth();
indexWidth != ptrBits) {
symbolicComputation.merge(forceBuildRuntimeCall(
IRB, runtime.buildZExt,
{{index, true},
{ConstantInt::get(IRB.getInt8Ty(), ptrBits - indexWidth),
false}}));
symbolicComputation.merge(forceBuildRuntimeCall(
IRB, runtime.binaryOperatorHandlers[Instruction::Mul],
{{symbolicComputation.lastInstruction, false},
{ConstantInt::get(intPtrType, elementSize), true}}));
} else {
symbolicComputation.merge(forceBuildRuntimeCall(
IRB, runtime.binaryOperatorHandlers[Instruction::Mul],
{{index, true},
{ConstantInt::get(intPtrType, elementSize), true}}));
}
addressContribution = {symbolicComputation.lastInstruction, false};
}
symbolicComputation.merge(forceBuildRuntimeCall(
IRB, runtime.binaryOperatorHandlers[Instruction::Add],
{addressContribution,
{currentAddress, (currentAddress == I.getPointerOperand())}}));
currentAddress = symbolicComputation.lastInstruction;
}
registerSymbolicComputation(symbolicComputation, &I);
}
void Symbolizer::visitBitCastInst(BitCastInst &I) {
if (I.getSrcTy()->isIntegerTy() && I.getDestTy()->isFloatingPointTy()) {
IRBuilder<> IRB(&I);
auto conversion =
buildRuntimeCall(IRB, runtime.buildBitsToFloat,
{{I.getOperand(0), true},
{IRB.getInt1(I.getDestTy()->isDoubleTy()), false}});
registerSymbolicComputation(conversion, &I);
return;
}
if (I.getSrcTy()->isFloatingPointTy() && I.getDestTy()->isIntegerTy()) {
IRBuilder<> IRB(&I);
auto conversion = buildRuntimeCall(IRB, runtime.buildFloatToBits,
{{I.getOperand(0), true}});
registerSymbolicComputation(conversion);
return;
}
assert(I.getSrcTy()->isPointerTy() && I.getDestTy()->isPointerTy() &&
"Unhandled non-pointer bit cast");
if (auto *expr = getSymbolicExpression(I.getOperand(0)))
symbolicExpressions[&I] = expr;
}
void Symbolizer::visitTruncInst(TruncInst &I) {
IRBuilder<> IRB(&I);
auto trunc = buildRuntimeCall(
IRB, runtime.buildTrunc,
{{I.getOperand(0), true},
{IRB.getInt8(I.getDestTy()->getIntegerBitWidth()), false}});
registerSymbolicComputation(trunc, &I);
}
void Symbolizer::visitIntToPtrInst(IntToPtrInst &I) {
if (auto *expr = getSymbolicExpression(I.getOperand(0)))
symbolicExpressions[&I] = expr;
}
void Symbolizer::visitPtrToIntInst(PtrToIntInst &I) {
if (auto *expr = getSymbolicExpression(I.getOperand(0)))
symbolicExpressions[&I] = expr;
}
void Symbolizer::visitSIToFPInst(SIToFPInst &I) {
IRBuilder<> IRB(&I);
auto conversion =
buildRuntimeCall(IRB, runtime.buildIntToFloat,
{{I.getOperand(0), true},
{IRB.getInt1(I.getDestTy()->isDoubleTy()), false},
{ IRB.getInt1(true), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitUIToFPInst(UIToFPInst &I) {
IRBuilder<> IRB(&I);
auto conversion =
buildRuntimeCall(IRB, runtime.buildIntToFloat,
{{I.getOperand(0), true},
{IRB.getInt1(I.getDestTy()->isDoubleTy()), false},
{ IRB.getInt1(false), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitFPExtInst(FPExtInst &I) {
IRBuilder<> IRB(&I);
auto conversion =
buildRuntimeCall(IRB, runtime.buildFloatToFloat,
{{I.getOperand(0), true},
{IRB.getInt1(I.getDestTy()->isDoubleTy()), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitFPTruncInst(FPTruncInst &I) {
IRBuilder<> IRB(&I);
auto conversion =
buildRuntimeCall(IRB, runtime.buildFloatToFloat,
{{I.getOperand(0), true},
{IRB.getInt1(I.getDestTy()->isDoubleTy()), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitFPToSI(FPToSIInst &I) {
IRBuilder<> IRB(&I);
auto conversion = buildRuntimeCall(
IRB, runtime.buildFloatToSignedInt,
{{I.getOperand(0), true},
{IRB.getInt8(I.getType()->getIntegerBitWidth()), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitFPToUI(FPToUIInst &I) {
IRBuilder<> IRB(&I);
auto conversion = buildRuntimeCall(
IRB, runtime.buildFloatToUnsignedInt,
{{I.getOperand(0), true},
{IRB.getInt8(I.getType()->getIntegerBitWidth()), false}});
registerSymbolicComputation(conversion, &I);
}
void Symbolizer::visitCastInst(CastInst &I) {
auto opcode = I.getOpcode();
if (opcode != Instruction::SExt && opcode != Instruction::ZExt) {
errs() << "Warning: unhandled cast instruction " << I << '\n';
return;
}
IRBuilder<> IRB(&I);
if (I.getSrcTy()->getIntegerBitWidth() == 1) {
auto boolToBitConversion = buildRuntimeCall(
IRB, runtime.buildBoolToBits,
{{I.getOperand(0), true},
{IRB.getInt8(I.getDestTy()->getIntegerBitWidth()), false}});
registerSymbolicComputation(boolToBitConversion, &I);
} else {
SymFnT target;
switch (I.getOpcode()) {
case Instruction::SExt:
target = runtime.buildSExt;
break;
case Instruction::ZExt:
target = runtime.buildZExt;
break;
default:
llvm_unreachable("Unknown cast opcode");
}
auto symbolicCast =
buildRuntimeCall(IRB, target,
{{I.getOperand(0), true},
{IRB.getInt8(I.getDestTy()->getIntegerBitWidth() -
I.getSrcTy()->getIntegerBitWidth()),
false}});
registerSymbolicComputation(symbolicCast, &I);
}
}
void Symbolizer::visitPHINode(PHINode &I) {
phiNodes.push_back(&I);
IRBuilder<> IRB(&I);
unsigned numIncomingValues = I.getNumIncomingValues();
auto *exprPHI = IRB.CreatePHI(IRB.getInt8PtrTy(), numIncomingValues);
for (unsigned incoming = 0; incoming < numIncomingValues; incoming++) {
exprPHI->addIncoming(
ConstantPointerNull::get(cast<PointerType>(IRB.getInt8PtrTy())),
I.getIncomingBlock(incoming));
}
symbolicExpressions[&I] = exprPHI;
}
void Symbolizer::visitInsertValueInst(InsertValueInst &I) {
IRBuilder<> IRB(&I);
auto insert = buildRuntimeCall(
IRB, runtime.buildInsert,
{{I.getAggregateOperand(), true},
{I.getInsertedValueOperand(), true},
{IRB.getInt64(aggregateMemberOffset(I.getAggregateOperand()->getType(),
I.getIndices())),
false},
{IRB.getInt8(isLittleEndian(I.getInsertedValueOperand()->getType()) ? 1 : 0), false}});
registerSymbolicComputation(insert, &I);
}
void Symbolizer::visitExtractValueInst(ExtractValueInst &I) {
IRBuilder<> IRB(&I);
auto extract = buildRuntimeCall(
IRB, runtime.buildExtract,
{{I.getAggregateOperand(), true},
{IRB.getInt64(aggregateMemberOffset(I.getAggregateOperand()->getType(),
I.getIndices())),
false},
{IRB.getInt64(dataLayout.getTypeStoreSize(I.getType())), false},
{IRB.getInt8(isLittleEndian(I.getType()) ? 1 : 0), false}});
registerSymbolicComputation(extract, &I);
}
void Symbolizer::visitSwitchInst(SwitchInst &I) {
IRBuilder<> IRB(&I);
auto *condition = I.getCondition();
auto *conditionExpr = getSymbolicExpression(condition);
if (conditionExpr == nullptr)
return;
auto *haveSymbolicCondition = IRB.CreateICmpNE(
conditionExpr, ConstantPointerNull::get(IRB.getInt8PtrTy()));
auto *constraintBlock = SplitBlockAndInsertIfThen(haveSymbolicCondition, &I,
false);
IRB.SetInsertPoint(constraintBlock);
for (auto &caseHandle : I.cases()) {
auto *caseTaken = IRB.CreateICmpEQ(condition, caseHandle.getCaseValue());
auto *caseConstraint = IRB.CreateCall(
runtime.comparisonHandlers[CmpInst::ICMP_EQ],
{conditionExpr, createValueExpression(caseHandle.getCaseValue(), IRB)});
IRB.CreateCall(runtime.pushPathConstraint,
{caseConstraint, caseTaken, getTargetPreferredInt(&I)});
}
}
void Symbolizer::visitUnreachableInst(UnreachableInst & ) {
}
void Symbolizer::visitInstruction(Instruction &I) {
if (isa<LandingPadInst>(I) || isa<ResumeInst>(I))
return;
errs() << "Warning: unknown instruction " << I
<< "; the result will be concretized\n";
}
CallInst *Symbolizer::createValueExpression(Value *V, IRBuilder<> &IRB) {
auto *valueType = V->getType();
if (isa<ConstantPointerNull>(V)) {
return IRB.CreateCall(runtime.buildNullPointer, {});
}
if (valueType->isIntegerTy()) {
auto bits = valueType->getPrimitiveSizeInBits();
if (bits == 1) {
return IRB.CreateCall(runtime.buildBool, {V});
} else if (bits <= 64) {
return IRB.CreateCall(runtime.buildInteger,
{IRB.CreateZExtOrBitCast(V, IRB.getInt64Ty()),
IRB.getInt8(valueType->getPrimitiveSizeInBits())});
} else {
return IRB.CreateCall(
runtime.buildInteger128,
{IRB.CreateTrunc(IRB.CreateLShr(V, ConstantInt::get(valueType, 64)),
IRB.getInt64Ty()),
IRB.CreateTrunc(V, IRB.getInt64Ty())});
}
}
if (valueType->isFloatingPointTy()) {
return IRB.CreateCall(runtime.buildFloat,
{IRB.CreateFPCast(V, IRB.getDoubleTy()),
IRB.getInt1(valueType->isDoubleTy())});
}
if (valueType->isPointerTy()) {
return IRB.CreateCall(
runtime.buildInteger,
{IRB.CreatePtrToInt(V, IRB.getInt64Ty()), IRB.getInt8(ptrBits)});
}
if (valueType->isStructTy()) {
auto *memory = IRB.CreateAlloca(V->getType());
IRB.CreateStore(V, memory);
return IRB.CreateCall(
runtime.readMemory,
{IRB.CreatePtrToInt(memory, intPtrType),
ConstantInt::get(intPtrType,
dataLayout.getTypeStoreSize(V->getType())),
IRB.getInt8(0)});
}
llvm_unreachable("Unhandled type for constant expression");
}
Symbolizer::SymbolicComputation
Symbolizer::forceBuildRuntimeCall(IRBuilder<> &IRB, SymFnT function,
ArrayRef<std::pair<Value *, bool>> args) {
std::vector<Value *> functionArgs;
for (const auto &[arg, symbolic] : args) {
functionArgs.push_back(symbolic ? getSymbolicExpressionOrNull(arg) : arg);
}
auto *call = IRB.CreateCall(function, functionArgs);
std::vector<Input> inputs;
for (unsigned i = 0; i < args.size(); i++) {
const auto &[arg, symbolic] = args[i];
if (symbolic)
inputs.push_back({arg, i, call});
}
return SymbolicComputation(call, call, inputs);
}
void Symbolizer::tryAlternative(IRBuilder<> &IRB, Value *V) {
auto *destExpr = getSymbolicExpression(V);
if (destExpr != nullptr) {
auto *concreteDestExpr = createValueExpression(V, IRB);
auto *destAssertion =
IRB.CreateCall(runtime.comparisonHandlers[CmpInst::ICMP_EQ],
{destExpr, concreteDestExpr});
auto *pushAssertion = IRB.CreateCall(
runtime.pushPathConstraint,
{destAssertion, IRB.getInt1(true), getTargetPreferredInt(V)});
registerSymbolicComputation(SymbolicComputation(
concreteDestExpr, pushAssertion, {{V, 0, destAssertion}}));
}
}
uint64_t Symbolizer::aggregateMemberOffset(Type *aggregateType,
ArrayRef<unsigned> indices) const {
uint64_t offset = 0;
auto *indexedType = aggregateType;
for (auto index : indices) {
if (auto *structType = dyn_cast<StructType>(indexedType)) {
offset += dataLayout.getStructLayout(structType)->getElementOffset(index);
indexedType = structType->getElementType(index);
} else {
auto *arrayType = cast<ArrayType>(indexedType);
unsigned elementSize =
dataLayout.getTypeAllocSize(arrayType->getArrayElementType());
offset += elementSize * index;
indexedType = arrayType->getArrayElementType();
}
}
return offset;
}