Verified Commit 8a21c93a authored by Tim Gymnich's avatar Tim Gymnich
Browse files

started implementing affine_relation

parent efc7c294
#include "affine_relation.h"
#include "llvm/IR/CFG.h"
#include <set>
using namespace llvm;
using namespace std;
......@@ -8,31 +10,134 @@ namespace pcpo {
// MARK: - Initializers
AffineRelation::AffineRelation(Function const& f) {
numberOfVariables = 999; // FIXME: figure this out.
matrix = Matrix<int>(numberOfVariables);
for (Argument const& arg: f.args()) {
index[&arg] = ++lastIndex;
}
isBottom = f.arg_empty();
AffineRelation::AffineRelation(Function const& func) {
index = createVariableIndexMap(func);
basis = {Matrix<int>(getNumberOfVariables() + 1)};
isBottom = basis.empty();
}
AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const& state, CallInst const* call) {
// TODO
assert(callee_func->arg_size() == call->getNumArgOperands());
index = state.index;
basis = state.basis;
for (Argument const& arg: callee_func->args()) {
Value* value = call->getArgOperand(arg.getArgNo());
if (value->getType()->isIntegerTy()) {
if (ConstantInt const* c = dyn_cast<ConstantInt>(value)) {
affineAssignment(&arg, 1, nullptr, c->getSExtValue());
// for (Matrix<int> m: basis) {
// vector<int> column = vector(m.getHeight(),0);
// column[0] = c->getSExtValue();
// m.setColumn(column, index[&arg]);
// }
} else {
affineAssignment(&arg, 1, value, 0);
// for (Matrix<int> m: basis) {
// // FIXME: get value from state??. Problem: state can have multiple basis. (affineAssignment)?
// m.value(index[value], index[&arg]) = 1;
// }
}
} else {
// affineAssignment(&arg, 1, &arg, 0);
// for (Matrix<int> m: basis) {
// m.value(index[&arg], index[&arg]) = 1;
// }
}
}
isBottom = basis.empty();;
}
// MARK: - AbstractState Interface
void AffineRelation::applyPHINode(BasicBlock const& bb, vector<AffineRelation> pred_values, Instruction const& phi) {
// TODO
PHINode const* phiNode = dyn_cast<PHINode>(&phi);
int i = 0;
for (BasicBlock const* pred_bb: llvm::predecessors(&bb)) {
auto& incoming_value = *phiNode->getIncomingValueForBlock(pred_bb);
auto& incoming_state = pred_values[i];
if (llvm::ConstantInt const* c = llvm::dyn_cast<llvm::ConstantInt>(&incoming_value)) {
AffineRelation acc = *this;
acc.affineAssignment(&phi, 1, nullptr, c->getSExtValue());
// AffineRelation acc = *this;
// vector<int> column = vector(getNumberOfVariables() + 1,0);
// column[0] = c->getSExtValue();
// for (auto m: acc.basis) {
// m.setColumn(column, index[&phi]);
// }
merge(Merge_op::UPPER_BOUND, acc);
} else {
AffineRelation acc = *this;
for (auto m: incoming_state.basis) {
auto val = m.column(index.at(&incoming_value));
acc.affineAssignment(&phi, 1, &incoming_value, 0);
}
// vector<int> column = incoming_state.basis.front().column(index[&incoming_value]);
// // TODO: How do we handle multiple basis?
// for (auto m: acc.basis) {
// m.setColumn(column, index[&phi]);
// }
merge(Merge_op::UPPER_BOUND, acc);
}
i++;
}
}
void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* end_block, AffineRelation const& callee_state) {
// TODO
// std::vector<Matrix> operands;
// Keep the debug output happy
// for (llvm::Value const* value : inst.operand_values()) {
// operands.push_back(getAbstractValue(*value));
// }
//iterate through all instructions of it till we find a return statement
for (Instruction const& iter_inst: *end_block) {
if (ReturnInst const* ret_inst = llvm::dyn_cast<llvm::ReturnInst>(&iter_inst)) {
Value const* ret_val = ret_inst->getReturnValue();
dbgs(4) << " Found return instruction\n";
if (callee_state.index.find(ret_val) != callee_state.index.end()) {
dbgs(4) << " Return evaluated, merging parameters\n";
affineAssignment(&inst, 1, ret_val, 0);
// FIXME: What about multiple basis?
// vector<int> returnValue = callee_state.basis.front().column(index[ret_val]);
// for (Matrix<int> m: basis) {
// m.setColumn(returnValue, index[&inst]);
// }
} else {
dbgs(4) << " Return not evaluated, setting to bottom\n";
// TODO: What should we do here
}
}
}
// debug_output(inst, operands);
}
void AffineRelation::applyReturnInst(Instruction const& inst) {
// TODO
Value const* ret_val = dyn_cast<llvm::ReturnInst>(&inst)->getReturnValue();
if (ret_val && ret_val->getType()->isIntegerTy()) {
if (ConstantInt const* c = dyn_cast<ConstantInt>(ret_val)) {
affineAssignment(&inst, 1, nullptr, c->getSExtValue());
// vector<int> column = vector(getNumberOfVariables() + 1,0);
// column[0] = c->getSExtValue();
// for (Matrix<int> m: basis) {
// m.setColumn(column, index[&inst]);
// }
} else {
affineAssignment(&inst, 1, ret_val, 0);
// FIXME: Handle multiple basis!
// vector<int> column = basis.front().column(index[ret_val]);
// for (Matrix<int> m: basis) {
// m.setColumn(column, index[&inst]);
// }
}
}
}
void AffineRelation::applyDefault(Instruction const& inst) {
......@@ -50,6 +155,11 @@ void AffineRelation::applyDefault(Instruction const& inst) {
type = dyn_cast<IntegerType>(inst.getOperand(1)->getType());
if (not type) return nonDeterminsticAssignment(&inst);
if (isa<UndefValue>(inst.getOperand(0)) || isa<UndefValue>(inst.getOperand(1))) {
return nonDeterminsticAssignment(&inst);
}
// for (llvm::Value const* value: inst.operand_values()) {
// operands.push_back(getAbstractValue(*value));
// }
......@@ -70,44 +180,85 @@ void AffineRelation::applyDefault(Instruction const& inst) {
// debug_output(inst, operands);
}
//Matrix<int> AffineRelation::getAbstractValue(Value const& value) const {
// if (llvm::Constant const* c = llvm::dyn_cast<llvm::Constant>(&value)) {
// return Matrix(1,1,c->getSExtValue);
// } else if (values.count(&value)) {
// return values.at(&value);
// } else if (isBottom) {
// // If we are at bottom, there are no values
// return Matrix();
// } else {
// // This branch should only catch 'weird' values, like things we are not handling
// return AbstractDomain {true};
// }
//}
bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) {
// TODO
if (other.isBottom) {
return false;
} else if (isBottom) {
// FIXME: is this correct?
basis = other.basis;
index = other.index;
isBottom = false;
return true;
}
switch (op) {
case Merge_op::UPPER_BOUND: return leastUpperBound(other);
default: abort();
}
}
// MARK: - Lattice Operations
bool AffineRelation::leastUpperBound(AffineRelation rhs) {
// TODO
assert(getNumberOfVariables() == rhs.getNumberOfVariables());
vector<Matrix<int>> before = basis;
vector<vector<int>> vectors;
vectors.reserve(basis.size() + rhs.basis.size());
for (Matrix<int> m: basis) {
vectors.push_back(m.toVector());
}
for (Matrix<int> m: rhs.basis) {
vectors.push_back(m.toVector());
}
// FIXME: i think this is transposing it twice. Maybe create a fast path for this kind of thing.
Matrix<int> combined = Matrix<int>(vectors).transpose();
dbgs(4) << combined;
Matrix<int> result = Matrix<int>::span(combined);
basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getHeight());
// FIXME: figure out a better way to detect changes
return before == basis;
}
// MARK: - Assignments
void AffineRelation::affineAssignment(Value const* xi, unordered_map<Value const*,int> relations, int constant) {
Matrix<int> Wr = Matrix<int>(getNumberOfVariables() + 1);
Wr(0,index.at(xi)) = constant;
for (auto [variable, factor]: relations) {
Wr(index.at(variable),index.at(xi)) = factor;
}
for (Matrix<int> matrix: basis) {
// FIXME: span(Wr)
matrix *= Wr;
}
}
void AffineRelation::affineAssignment(Value const* xi, int64_t a, Value const* xj, int64_t b) {
Matrix<int> affineTransformer = Matrix<int>(numberOfVariables);
affineTransformer(0,index[xi]) = b;
affineTransformer(index[xi],index[xi]) = 0;
if (xj != nullptr) {
affineTransformer(index[xj],index[xi]) = a;
if (xj == nullptr) {
affineAssignment(xi, {}, b);
} else {
affineAssignment(xi, {{xj,a}}, b);
}
matrix *= affineTransformer;
}
void AffineRelation::nonDeterminsticAssignment(Value const* xi) {
// TODO
Matrix<int> T0 = Matrix<int>(getNumberOfVariables() + 1);
Matrix<int> T1 = Matrix<int>(getNumberOfVariables() + 1);
T0(index.at(xi),index.at(xi)) = 0;
T0(0,index.at(xi)) = 0;
T1(index.at(xi),index.at(xi)) = 0;
T1(0,index.at(xi)) = 1;
for (Matrix<int> matrix: basis) {
// FIXME: span({T0,T1}) or even leastUpperBound
matrix *= T0;
matrix *= T1;
}
}
// MARK: - Abstract Operators
......@@ -130,8 +281,7 @@ void AffineRelation::Add(Instruction const& inst) {
auto b = dyn_cast<ConstantInt>(op2);
return affineAssignment(&inst, 1, op1, b->getSExtValue());
} else {
assert(false);
return nonDeterminsticAssignment(&inst);
return affineAssignment(&inst, {{op1,1},{op2,1}}, 0);
}
}
......@@ -153,8 +303,7 @@ void AffineRelation::Sub(Instruction const& inst) {
auto b = dyn_cast<ConstantInt>(op2);
return affineAssignment(&inst, 1, op1, -b->getSExtValue());
} else {
assert(false);
return nonDeterminsticAssignment(&inst);
return affineAssignment(&inst, {{op1,1},{op2,1}}, 0);
}
}
......@@ -176,23 +325,80 @@ void AffineRelation::Mul(Instruction const& inst) {
auto a = dyn_cast<ConstantInt>(op2);
return affineAssignment(&inst, a->getSExtValue(), op1, 0);
} else {
assert(false);
return nonDeterminsticAssignment(&inst);
}
}
// MARK: - Helpers
unordered_map<Value const*, int> createVariableIndexMap_impl(Function const& func, int& count, set<Function const*> visited_funcs) {
unordered_map<Value const*, int> map;
visited_funcs.insert(&func);
for (BasicBlock const& basic_block: func) {
for (Instruction const& inst: basic_block) {
if (isa<IntegerType>(inst.getType()) || isa<ReturnInst>(&inst)) {
count++;
map[&inst] = count;
}
if (CallInst const* call = dyn_cast<CallInst>(&inst)) {
Function const* callee_func = call->getCalledFunction();
if (callee_func->empty()) {
continue;
}
if (visited_funcs.count(callee_func) == 0) {
unordered_map<Value const*, int> callee_map = createVariableIndexMap_impl(*callee_func, count, visited_funcs);
map.insert(callee_map.begin(), callee_map.end());
}
}
}
}
return map;
}
unordered_map<Value const*, int> AffineRelation::createVariableIndexMap(Function const& func) {
int count = 0;
return createVariableIndexMap_impl(func, count, {});
}
// MARK: - debug output
void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const {
// TODO
for (auto m: basis) {
for (auto [val, idx]: index) {
if (val->hasName()) {
out << val->getName();
}
out << "\t\t";
}
out << "\n" << m << "\n";
}
}
void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const {
// TODO
for (auto m: basis) {
for (auto [val, idx]: index) {
if (val->hasName()) {
out << val->getName();
}
out << "\t\t";
}
out << "\n" << m << "\n";
}
}
void AffineRelation::debug_output(Instruction const& inst, Matrix<int> operands) {
// TODO
for (auto m: basis) {
for (auto [val, idx]: index) {
if (val->hasName()) {
dbgs(3) << val->getName();
}
dbgs(3) << "\t\t";
}
dbgs(3) << "\n" << m << "\n";
}
}
}
......@@ -11,17 +11,18 @@ namespace pcpo {
class AffineRelation {
private:
int lastIndex = 0;
int numberOfVariables = 100;
/// Only valid when `createVariableIndexMap` has been generated.
int getNumberOfVariables() { return index.size(); };
std::unordered_map<llvm::Value const*, int> createVariableIndexMap(llvm::Function const& func);
public:
std::unordered_map<llvm::Value const*, int> index;
Matrix<int> matrix;
std::vector<Matrix<int>> basis;
bool isBottom = true;
AffineRelation() = default;
AffineRelation(AffineRelation const& state) = default;
explicit AffineRelation(llvm::Function const& f);
explicit AffineRelation(llvm::Function const& func);
/// This constructor is used to initialize the state of a function call, to which parameters are passed.
/// This is the "enter" function as described in "Compiler Design: Analysis and Transformation"
explicit AffineRelation(llvm::Function const* callee_func, AffineRelation const& state, llvm::CallInst const* call);
......@@ -48,6 +49,7 @@ public:
// Abstract Assignments
void affineAssignment(llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b);
void affineAssignment(llvm::Value const* xi, std::unordered_map<llvm::Value const*,int> relations, int constant);
void nonDeterminsticAssignment(llvm::Value const* xi);
protected:
......
......@@ -11,6 +11,7 @@
#include "value_set.h"
#include "simple_interval.h"
#include "normalized_conjunction.h"
#include "affine_relation.h"
#include "fixpoint_widening.cpp"
#include "hash_utils.h"
......@@ -348,7 +349,8 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) {
// Use either the standard fixpoint algorithm or the version with widening
// executeFixpointAlgorithm<AbstractState>(M);
executeFixpointAlgorithm<NormalizedConjunction>(M);
// executeFixpointAlgorithm<NormalizedConjunction>(M);
executeFixpointAlgorithm<AffineRelation>(M);
// executeFixpointAlgorithmWidening<AbstractState>(M);
// We never change anything
......
......@@ -55,8 +55,8 @@ public:
/// Creates a matrix from a 2D vector
/// @param vectors 2D vector containing columns with rows
Matrix(vector<vector<T>> const &vectors) {
this->height = vectors.size();
this->width = vectors.front().size();
this->height = vectors.size();
this->vectors = vectors;
};
......@@ -147,7 +147,7 @@ public:
return rank;
}
/// Linear span of the matrix ... fixme
/// Basis of the linear span of the column vectors
static Matrix<T> span(Matrix<T> matrix) {
vector<vector<T>> columns;
int rank = matrix.getRank();
......@@ -157,6 +157,9 @@ public:
return Matrix(columns).transpose();
}
/// Computes the null space for the column vectors
static Matrix<T> null(Matrix<T> matrix);
/// Converts the matrix to a 1D Vector by stacking the column vectors
std::vector<T> toVector() const {
vector<T> result;
......@@ -176,7 +179,7 @@ public:
vector<Matrix<T>> reshapeColumns(int height, int width) const {
vector<Matrix<T>> result;
for (int c = 0; c < width; c++) {
for (int c = 0; c < getWidth(); c++) {
result.push_back(Matrix(column(c), height, width));
}
return result;
......
......@@ -136,7 +136,9 @@ void NormalizedConjunction::applyDefault(Instruction const& inst) {
type = dyn_cast<IntegerType>(inst.getOperand(1)->getType());
if (not type) return nonDeterminsticAssignment(&inst);
// TODO: handle undef values
for (Value const* value: inst.operand_values()) {
operands.push_back(LinearEquality(value));
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment