From b49db366cd203013ca640e944ae57ad7946e0af6 Mon Sep 17 00:00:00 2001 From: Dmytro Yakymets Date: Tue, 5 Nov 2019 16:05:15 +0100 Subject: [PATCH 001/142] Started handling function calls; so far it prints out the function name and its first parameter. --- CMakeLists.txt | 17 ++++++++++++++--- samples/basic_function.c | 13 +++++++++++++ src/callstring.cpp | 14 ++++++++++++++ src/callstring.h | 16 ++++++++++++++++ src/fixpoint.cpp | 1 + src/fixpoint_widening.cpp | 2 ++ src/value_set.h | 8 +++++++- 7 files changed, 67 insertions(+), 4 deletions(-) create mode 100644 samples/basic_function.c create mode 100644 src/callstring.cpp create mode 100644 src/callstring.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f6a113..902a376 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,10 +5,19 @@ # set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Hello.exports) # endif() #endif() +cmake_minimum_required(VERSION 3.10) -if(WIN32 OR CYGWIN) - set(LLVM_LINK_COMPONENTS Core Support) -endif() +find_package(LLVM REQUIRED CONFIG) +add_definitions(${LLVM_DEFINITIONS}) +include_directories(${LLVM_INCLUDE_DIRS}) + +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") +message(STATUS "${LLVM_CMAKE_DIR}") + +include(AddLLVM) # For older LLVM < 8.0 replace first line with this # add_llvm_loadable_module(llvm-pain @@ -19,6 +28,8 @@ add_llvm_library(llvm-pain MODULE src/value_set.h src/simple_interval.cpp src/simple_interval.h + src/callstring.cpp + src/callstring.h DEPENDS intrinsics_gen PLUGIN_TOOL diff --git a/samples/basic_function.c b/samples/basic_function.c new file mode 100644 index 0000000..d4544b7 --- /dev/null +++ b/samples/basic_function.c @@ -0,0 +1,13 @@ +int incr(int a) { + a++; + return a; +} + +int main() { + int a = 1; + int b = 0; + a++; + b++; + b = incr(a); + return a + b; +} \ No newline at end of file diff --git a/src/callstring.cpp b/src/callstring.cpp new file mode 100644 index 0000000..92f36f8 --- /dev/null +++ b/src/callstring.cpp @@ -0,0 +1,14 @@ +#include "callstring.h" +#include "simple_interval.h" + +namespace pcpo { + + void InterpretCall(llvm::CallInst const* call, std::vector& operands) { + dbgs(3) << "Call performed\nArguments:\n"; + dbgs(3) << call->getArgOperand(0)->getName() << " "; + dbgs(3) << operands[0] << "\n"; + + } + +} + diff --git a/src/callstring.h b/src/callstring.h new file mode 100644 index 0000000..044e599 --- /dev/null +++ b/src/callstring.h @@ -0,0 +1,16 @@ +#pragma once + +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/CFG.h" +#include "llvm/IR/InstrTypes.h" +#include "llvm/IR/Instructions.h" + +#include "global.h" +#include "simple_interval.h" + +namespace pcpo { + + void InterpretCall(llvm::CallInst const* call, std::vector& operands); + +} \ No newline at end of file diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index f5c40dd..4029d62 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -9,6 +9,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "global.h" +#include "callstring.h" #include "fixpoint_widening.cpp" #include "value_set.h" #include "simple_interval.h" diff --git a/src/fixpoint_widening.cpp b/src/fixpoint_widening.cpp index e9e65fb..5b05532 100644 --- a/src/fixpoint_widening.cpp +++ b/src/fixpoint_widening.cpp @@ -8,6 +8,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "global.h" +#include "callstring.h" #include "value_set.h" #include "simple_interval.h" @@ -62,6 +63,7 @@ void executeFixpointAlgorithmWidening(llvm::Module& M) { dbgs(1) << " Function " << f.getName() << " is external, skipping..."; continue; } + if (f.getName() != "main") continue; // Register basic blocks for (llvm::BasicBlock& bb: f) { diff --git a/src/value_set.h b/src/value_set.h index 1d5d04b..3bb2b49 100644 --- a/src/value_set.h +++ b/src/value_set.h @@ -10,6 +10,7 @@ #include "llvm/IR/Instructions.h" #include "global.h" +#include "callstring.h" namespace pcpo { @@ -98,6 +99,7 @@ public: // Go through each instruction of the basic block and apply it to the state for (llvm::Instruction const& inst: bb) { + // If the result of the instruction is not used, there is no reason to compute // it. (There are no side-effects in LLVM IR. (I hope.)) if (inst.use_empty()) continue; @@ -121,7 +123,11 @@ public: operands.push_back(pred_value); // Keep the debug output happy } - } else { + } + else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { + InterpretCall(call, operands); + } + else { for (llvm::Value const* value: inst.operand_values()) { operands.push_back(getAbstractValue(*value)); } -- GitLab From 3daf9f21b72aa248de85b8e8ede431c631fd498a Mon Sep 17 00:00:00 2001 From: Florian Stamer Date: Wed, 20 Nov 2019 14:25:34 +0100 Subject: [PATCH 002/142] Added partially_evaluated and finished_evaluation flags, created a little todo in the fixpoint_callstring.cpp --- samples/add-1-float.c | 14 ++ samples/add-1.c | 7 +- samples/add-2.c | 4 +- samples/add-3.c | 4 +- samples/add-4.c | 4 +- samples/for.c | 18 +-- samples/func-test-1.c | 30 ++++ samples/func-test-2.c | 10 ++ samples/func-test-3.c | 20 +++ samples/func-test-4.c | 11 ++ samples/func-test-5.c | 12 ++ samples/func-test-for.c | 26 ++++ samples/func-test-rec-endless.c | 8 + samples/func-test-rec.c | 15 ++ samples/func-test-switch.c | 26 ++++ samples/gcd.c | 5 + samples/goto.c | 4 +- src/fixpoint.cpp | 224 +++++++++++++++++++++------ src/fixpoint_widening.cpp | 260 ++++++++++++++++++++++---------- src/general.h | 49 ++++++ src/simple_interval.cpp | 45 +++--- src/value_set.h | 241 ++++++++++++++++++++++------- 22 files changed, 819 insertions(+), 218 deletions(-) create mode 100644 samples/add-1-float.c create mode 100644 samples/func-test-1.c create mode 100644 samples/func-test-2.c create mode 100644 samples/func-test-3.c create mode 100644 samples/func-test-4.c create mode 100644 samples/func-test-5.c create mode 100644 samples/func-test-for.c create mode 100644 samples/func-test-rec-endless.c create mode 100644 samples/func-test-rec.c create mode 100644 samples/func-test-switch.c create mode 100644 src/general.h diff --git a/samples/add-1-float.c b/samples/add-1-float.c new file mode 100644 index 0000000..6a6111e --- /dev/null +++ b/samples/add-1-float.c @@ -0,0 +1,14 @@ +int main() { + float a = 1; a++; // a={2} + float b = 2; b++; // b={3} + float c; + c += 1; // c=T + + if(c == a){ // c=a={2} + c += 1; // c={3} + a = c; // a={3} + } + + // a={2,3} + return 0; // ret={5,6} +} diff --git a/samples/add-1.c b/samples/add-1.c index fb657e6..c0d7177 100644 --- a/samples/add-1.c +++ b/samples/add-1.c @@ -11,4 +11,9 @@ int test() { // a={2,3} return a + b; // ret={5,6} -} \ No newline at end of file +} + +int main() { + int x = test(); + return 0; +} diff --git a/samples/add-2.c b/samples/add-2.c index 6db9f4d..ae9ab5a 100644 --- a/samples/add-2.c +++ b/samples/add-2.c @@ -1,4 +1,4 @@ -int test() { +int main() { int a = 1; a++; // a={2} int b = 2; b++; // b={3} int c = 3; c++; // c={4} @@ -10,4 +10,4 @@ int test() { // a={2} return a + b; // ret={5} -} \ No newline at end of file +} diff --git a/samples/add-3.c b/samples/add-3.c index a2ef595..47b96d2 100644 --- a/samples/add-3.c +++ b/samples/add-3.c @@ -1,4 +1,4 @@ -int test() { +int main() { int a = 1; a++; // a={2} int b = 2; b++; // b={3} int c = 3; c++; // c={4} @@ -10,4 +10,4 @@ int test() { // a={5} return a + b; // ret={8} -} \ No newline at end of file +} diff --git a/samples/add-4.c b/samples/add-4.c index b781031..c9c86d9 100644 --- a/samples/add-4.c +++ b/samples/add-4.c @@ -1,4 +1,4 @@ -int test() { +int main() { int a = 1; a++; // a={2} int b = 2; b++; // b={3} int c = 3; c++; // c={4} @@ -10,4 +10,4 @@ int test() { // a={12} return a + b; // ret={5} -} \ No newline at end of file +} diff --git a/samples/for.c b/samples/for.c index 013a51a..addc742 100644 --- a/samples/for.c +++ b/samples/for.c @@ -2,15 +2,15 @@ #include // test program: // simple loop +int xex(int b) { + return b + 9; +} -int main(int argc, char const *argv[]) { - int x = 42; - //x += 7; - int y = 2; - - for(int i=0; i + +int a(int in) { + printf("In a()\n"); + int a_val = in - 1; + if (in == 0) { + a_val = 4; + return a_val; + } + a_val = a_val - 3; + if (a_val > 2) { + return 2; + } else { + if ( a_val == in) { + return 0; + } + return a_val; + } +} + +int b(int in) { + int b_val = in +3; + return b_val; +} + +int main(int argc, char const *argv[]) { + int x = a(4); + int y = b(x); + return y+1; +} diff --git a/samples/func-test-2.c b/samples/func-test-2.c new file mode 100644 index 0000000..4cfd628 --- /dev/null +++ b/samples/func-test-2.c @@ -0,0 +1,10 @@ +int a(int input) { + return 17; +} + +int main() { + int x = 10; + x++; + int y = a(x); + return y; +} diff --git a/samples/func-test-3.c b/samples/func-test-3.c new file mode 100644 index 0000000..5f5e97b --- /dev/null +++ b/samples/func-test-3.c @@ -0,0 +1,20 @@ +int b(int x, int y) { + if (y == 0) { + return x; + } + return y - x; +} + +int a(int x, int y, int z) { + int l = x + y; + int m = b(3, z); + return l - m; +} + +int main(int argc, char const* argv[]) { + int l = 4; + int n = 2; + int x = a(n, l, 1); // a(2,4,1) = 2+4-b(3,1) = 6-(1-3) = 8 + int y = b(x, l); // b(8, 4) = 4-8 = -4 + return x + y; // 8-4 = 4 +} diff --git a/samples/func-test-4.c b/samples/func-test-4.c new file mode 100644 index 0000000..232885c --- /dev/null +++ b/samples/func-test-4.c @@ -0,0 +1,11 @@ +int a(int b, int c) { + if (b > c) { + return c; + } + return b; +} + +int main(int argc, char const* argv[]) { + int x = a(3, 4); + return 0; +} diff --git a/samples/func-test-5.c b/samples/func-test-5.c new file mode 100644 index 0000000..b1a3e78 --- /dev/null +++ b/samples/func-test-5.c @@ -0,0 +1,12 @@ +int a(int in) { + in++; + return in; +} + +int main() { + int x_1 = a(0); // 1 + int x_2 = a(1); // 2 + int x_3 = a(2); // 3 + int x_4 = a(3); // 4 + return x_1 + x_2 + x_3 + x_4; // 10 +} diff --git a/samples/func-test-for.c b/samples/func-test-for.c new file mode 100644 index 0000000..b79fad9 --- /dev/null +++ b/samples/func-test-for.c @@ -0,0 +1,26 @@ +int a(int in) { + in++; + return in; +} + +int main() { + int x = 0; + x++; + for (int i = 0; i < 4; i++) { + switch(i) { + case 0: + x += a(i); + break; + case 1: + x += a(i); + break; + case 2: + x += a(i); + break; + case 3: + x += a(i); + break; + } + } + return x; +} diff --git a/samples/func-test-rec-endless.c b/samples/func-test-rec-endless.c new file mode 100644 index 0000000..5d7cd6e --- /dev/null +++ b/samples/func-test-rec-endless.c @@ -0,0 +1,8 @@ +int a(int in) { + return a(in+1); +} + +int main() { + int x = a(5); + return x; +} diff --git a/samples/func-test-rec.c b/samples/func-test-rec.c new file mode 100644 index 0000000..9ca10c7 --- /dev/null +++ b/samples/func-test-rec.c @@ -0,0 +1,15 @@ +#include + +int a(int in) { + if (in == 0) { + return in; + } + int y = a(in - 1); + return y; +} + + +int main(int argc, char const *argv[]) { + int x = a(3); + return x; +} diff --git a/samples/func-test-switch.c b/samples/func-test-switch.c new file mode 100644 index 0000000..b79fad9 --- /dev/null +++ b/samples/func-test-switch.c @@ -0,0 +1,26 @@ +int a(int in) { + in++; + return in; +} + +int main() { + int x = 0; + x++; + for (int i = 0; i < 4; i++) { + switch(i) { + case 0: + x += a(i); + break; + case 1: + x += a(i); + break; + case 2: + x += a(i); + break; + case 3: + x += a(i); + break; + } + } + return x; +} diff --git a/samples/gcd.c b/samples/gcd.c index c9623cc..69ec386 100644 --- a/samples/gcd.c +++ b/samples/gcd.c @@ -11,3 +11,8 @@ int gcd() { // expected solution 89 return a; } + +int main() { + int x = gcd(); + return x; +} diff --git a/samples/goto.c b/samples/goto.c index f039aa4..9aa14d4 100644 --- a/samples/goto.c +++ b/samples/goto.c @@ -1,8 +1,8 @@ -int test(){ +int main(){ int i = 0; loop: i++; if(i<10) goto loop; return i; -} \ No newline at end of file +} diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 4029d62..4391b29 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -8,12 +8,14 @@ #include "llvm/IR/Dominators.h" #include "llvm/Analysis/LoopInfo.h" +#include "general.h" #include "global.h" #include "callstring.h" #include "fixpoint_widening.cpp" #include "value_set.h" #include "simple_interval.h" + namespace pcpo { static llvm::RegisterPass Y("painpass", "AbstractInterpretation Pass"); @@ -36,12 +38,30 @@ public: // assuming the parameters can be anything. explicit AbstractStateDummy(llvm::Function const& f) {} - // Applies the changes needed to reflect executing the instructions in the basic block. Before + // Initialise the state of a function call with parameters of the caller. + // This is the "enter" function as described in "Compiler Design: Analysis and Transformation" + explicit AbstractStateDummy(llvm::Function const* callee_func, AbstractStateDummy const& state, + llvm::CallInst const* call) {} + + // Apply functions apply the changes needed to reflect executing the instructions in the basic block. Before // this operation is called, the state is the one upon entering bb, afterwards it should be (an // upper bound of) the state leaving the basic block. // predecessors contains the outgoing state for all the predecessors, in the same order as they // are listed in llvm::predecessors(bb). - void apply(llvm::BasicBlock const& bb, std::vector const& predecessors) {}; + + // Applies instructions within the PHI node, needed for merging + void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, + llvm::Instruction const& inst) {}; + + // This is the "combine" function as described in "Compiler Design: Analysis and Transformation" + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, + AbstractStateDummy const& callee_state) {}; + + // Evaluates return instructions, needed for the main function and the debug output + void applyReturnInst(llvm::Instruction const& inst) {}; + + // Handles all cases different from the three above + void applyDefault(llvm::Instruction const& inst) {}; // This 'merges' two states, which is the operation we do fixpoint iteration over. Currently, // there are three possibilities for op: @@ -71,9 +91,10 @@ public: void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; }; -// Run the simple fixpoint algorithm. AbstractState should implement the interface documented in -// AbstractStateDummy (no need to subclass or any of that, just implement the methods with the right -// signatures and take care to fulfil the contracts outlines above). + +// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface +// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods +// with the right signatures and take care to fulfil the contracts outlines above). // Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in // fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as // well. @@ -86,8 +107,8 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data // per node to execute the fixpoint algorithm. struct Node { - int id; llvm::BasicBlock const* bb; + llvm::BasicBlock const* callstring; AbstractState state; bool update_scheduled = false; // Whether the node is already in the worklist @@ -97,40 +118,38 @@ void executeFixpointAlgorithm(llvm::Module const& M) { llvm::Function const* func_entry = nullptr; }; - std::vector nodes; - std::unordered_map nodeIdMap; // Maps basic blocks to the ids of their corresponding nodes - std::vector worklist; // Contains the ids of nodes that need to be processed + std::unordered_map nodes; + std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed - // TODO: Check what this does for release clang, probably write out a warning - dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; + // We only consider the main function in the beginning. If no main exists, nothing is evaluated! + llvm::Function const* main_func = M.getFunction("main"); - for (llvm::Function const& f: M.functions()) { - // Check for external (i.e. declared but not defined) functions - if (f.empty()) { - dbgs(1) << " Function " << f.getName() << " is external, skipping..."; - continue; - } + //creating dummy block for callstrings of the main block, since the main function is not called from another function + llvm::BasicBlock const* dummy_block = llvm::BasicBlock::Create(M.getContext(), "dummy"); - // Register basic blocks - for (llvm::BasicBlock const& bb: f) { - dbgs(1) << " Found basic block " << bb.getName() << '\n'; + // TODO: Check what this does for release clang, probably write out a warning + dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; - Node node; - node.id = nodes.size(); // Assign new id - node.bb = &bb; - // node.state is default initialised (to bottom) + // Register basic blocks of the main function + for (llvm::BasicBlock const& bb: *main_func) { + dbgs(1) << " Found basic block main." << bb.getName() << '\n'; - nodeIdMap[node.bb] = node.id; - nodes.push_back(node); - } + Node node; + node.bb = &bb; + node.callstring = dummy_block; + // node.state is default initialised (to bottom) - // Push the initial block into the worklist - int entry_id = nodeIdMap.at(&f.getEntryBlock()); - worklist.push_back(entry_id); - nodes[entry_id].update_scheduled = true; - nodes[entry_id].func_entry = &f; + // nodes of main block have the callstring of a dummy block + nodes[std::make_tuple(&bb, dummy_block)] = node; } + // Push the initial block into the worklist + auto init_element = std::make_tuple(&main_func->getEntryBlock(), dummy_block); + worklist.push_back(init_element); + nodes[init_element].update_scheduled = true; + nodes[init_element].state = AbstractState {*main_func}; + nodes[init_element].func_entry = main_func; + dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; @@ -139,15 +158,18 @@ void executeFixpointAlgorithm(llvm::Module const& M) { worklist.pop_back(); node.update_scheduled = false; - dbgs(1) << "\nIteration " << iter << ", considering basic block " << node.bb->getName() << '\n'; + dbgs(1) << "\nIteration " << iter << ", considering basic block " + << _bb_to_str(node.bb) << " with callstring " + << _bb_to_str(node.callstring) << '\n'; AbstractState state_new; // Set to bottom if (node.func_entry) { dbgs(1) << " Merging function parameters, is entry block\n"; - AbstractState state_entry {*node.func_entry}; - state_new.merge(Merge_op::UPPER_BOUND, state_entry); + // if it is the entry node, then its state should be top + state_new.isBottom = false; + state_new.merge(Merge_op::UPPER_BOUND, node.state); } dbgs(1) << " Merge of " << llvm::pred_size(node.bb) @@ -156,9 +178,9 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Collect the predecessors std::vector predecessors; for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { - dbgs(3) << " Merging basic block " << bb->getName() << '\n'; + dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - AbstractState state_branched {nodes[nodeIdMap[bb]].state}; + AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; state_branched.branch(*bb, *node.bb); state_new.merge(Merge_op::UPPER_BOUND, state_branched); predecessors.push_back(state_branched); @@ -168,7 +190,112 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Apply the basic block dbgs(3) << " Applying basic block\n"; - state_new.apply(*node.bb, predecessors); + + if (state_new.isBottom) { + dbgs(3) << " Basic block is unreachable, everything is bottom\n"; + } else { + // Applies all instrucions of a basic block + for (llvm::Instruction const& inst: *node.bb) { + + // Handles return instructions + if (llvm::dyn_cast(&inst)) { + state_new.applyReturnInst(inst); + } + + // If the result of the instruction is not used, there is no reason to compute + // it. (There are no side-effects in LLVM IR. (I hope.)) + if (inst.use_empty()) { + // Except for call instructions, we still want to get that information + if (not llvm::dyn_cast(&inst)) { + dbgs(3) << " Empty use of instruction, skipping...\n"; + continue; + } + } + + // Handles merging points + if (llvm::dyn_cast(&inst)) { + + state_new.applyPHINode(*node.bb, predecessors, inst); + + // Handles function calls + } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { + + // Checks if an input parameter for the callee is bottom. If so, + // then skip the calculation of the call instruction for now + if (state_new.checkOperandsForBottom(inst)) continue; + + llvm::Function const* callee_func = call->getCalledFunction(); + + // Checks for functions, such as printf and skips them + if (callee_func->empty()) { + dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; + continue; + } + + auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.bb); + bool changed; + + // Checks whether a node with key [%callee entry block, %caller basic block], + // i.e. an entry block with callstring of caller basic block, exists. + // If not, all nodes with their corrosponding keys are initilized for the callee function. + if (nodes.find(callee_element) == nodes.end()) { + // Check if abstract_state of call.bb is bottom or not + dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; + + // Register basic blocks + for (llvm::BasicBlock const& bb : *callee_func) { + dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; + + Node callee_node; + callee_node.bb = &bb; + callee_node.callstring = node.bb; + // node.state is default initialised (to bottom) + + nodes[std::make_tuple(&bb, node.bb)] = callee_node; + } + + nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; + nodes[callee_element].func_entry = callee_func; + changed = true; + } else { + AbstractState state_update{ callee_func, state_new, call }; + changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); + } + + //Getting the last block + llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); + auto end_element = std::make_tuple(end_block, node.bb); + + state_new.applyCallInst(inst, end_block, nodes[end_element].state); + + // If input parameters have changed, we want to interpret the function once again + // and reevaluate the nodes of possible callers. + if (changed) { + for (std::pair& i : nodes) { + if (std::get<0>(i.first) == node.bb and not i.second.update_scheduled) { + dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; + worklist.push_back(i.first); + i.second.update_scheduled = true; + } + } + + // Checks if the key of the callee functions entry node is already on the worklist, + // this is necessary for recursions. + if (not nodes[callee_element].update_scheduled) { + worklist.push_back(callee_element); + nodes[callee_element].update_scheduled = true; + + dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; + } else { + dbgs(3) << " Callee already on worklist, nothing to add...\n"; + } + } + } else { + if (state_new.checkOperandsForBottom(inst)) continue; + state_new.applyDefault(inst); + } + } + } // Merge the state back into the node dbgs(3) << " Merging with stored state\n"; @@ -184,12 +311,13 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Something changed and we will need to update the successors for (llvm::BasicBlock const* succ_bb: llvm::successors(node.bb)) { - Node& succ = nodes[nodeIdMap[succ_bb]]; + auto succ_key = std::make_tuple(succ_bb, node.callstring); + Node& succ = nodes[succ_key]; if (not succ.update_scheduled) { - worklist.push_back(succ.id); + worklist.push_back(succ_key); succ.update_scheduled = true; - dbgs(3) << " Adding " << succ_bb->getName() << " to worklist\n"; + dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; } } } @@ -197,21 +325,22 @@ void executeFixpointAlgorithm(llvm::Module const& M) { if (!worklist.empty()) { dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; } - + // Output the final result dbgs(0) << "\nFinal result:\n"; - for (Node const& i: nodes) { - dbgs(0) << i.bb->getName() << ":\n"; - i.state.printOutgoing(*i.bb, dbgs(0), 2); + for (std::pair i: nodes) { + dbgs(0) << _bb_key_to_str(i.first) << ":\n"; + i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); } + } bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening - //executeFixpointAlgorithm (M); - executeFixpointAlgorithmWidening(M); + // executeFixpointAlgorithm (M); + executeFixpointAlgorithmWidening(M); // We never change anything return false; @@ -222,5 +351,6 @@ void AbstractInterpretationPass::getAnalysisUsage(llvm::AnalysisUsage& info) con info.setPreservesAll(); } + } /* end of namespace pcpo */ diff --git a/src/fixpoint_widening.cpp b/src/fixpoint_widening.cpp index 5b05532..482af2e 100644 --- a/src/fixpoint_widening.cpp +++ b/src/fixpoint_widening.cpp @@ -1,4 +1,3 @@ - #include #include @@ -14,101 +13,98 @@ namespace pcpo { -// Run the fixpoint algorithm using widening and narrowing. Note that a lot of code in here is -// duplicated from executeFixpointAlgorithm. If you just want to understand the basic fixpoint -// iteration, you should take a look at that instead. -// The interface for AbstractState is the same as for the simple fixpoint (documented in -// AbstractStateDummy), except that is needs to support the merge operations WIDEN and NARROW, as -// you can probably guess. +// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface +// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods +// with the right signatures and take care to fulfil the contracts outlines above). +// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in +// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as +// well. // Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I // recommend Meld.) template void executeFixpointAlgorithmWidening(llvm::Module& M) { constexpr int iterations_max = 1000; - constexpr int widen_after = 2; // Number of iteration after which we switch to widening. + constexpr int widen_after = 2; // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data // per node to execute the fixpoint algorithm. struct Node { - int id; - llvm::BasicBlock* bb; + llvm::BasicBlock const* bb; + llvm::BasicBlock const* callstring; AbstractState state; bool update_scheduled = false; // Whether the node is already in the worklist // If this is set, the algorithm will add the initial values from the parameters of the // function to the incoming values, which is the correct thing to do for initial basic // blocks. - llvm::Function* func_entry = nullptr; + llvm::Function const* func_entry = nullptr; - bool should_widen = false; // Whether we want to widen at this node - int change_count = 0; // How often has node changed during iterations + bool should_widen = false; // Whether we want to widen at this node + int change_count = 0; // How often has node changed during iterations }; - std::vector nodes; - std::unordered_map nodeIdMap; // Maps basic blocks to the ids of their corresponding nodes - std::vector worklist; // Contains the ids of nodes that need to be processed + std::unordered_map nodes; + std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed bool phase_narrowing = false; // If this is set, we are in the narrowing phase of the fixpoint algorithm + // We only consider the main function in the beginning. If no main exists, nothing is evaluated! + llvm::Function* main_func = M.getFunction("main"); + + //creating dummy block for callstrings of the main block, since the main function is not called from another function + llvm::BasicBlock const* dummy_block = llvm::BasicBlock::Create(M.getContext(), "dummy"); + + // TODO: Check what this does for release clang, probably write out a warning dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Push dummy element indicating the end of the widening phase of the fixpoint algorithm. As the // worklist is processed in a LIFO order, this will be the last element coming out, indicating // that the worklist is empty. Once that happens, we have obtained a valid solution (using // widening) and can start to apply narrowing. - worklist.push_back(-1); - - for (llvm::Function& f: M.functions()) { - // Check for external (i.e. declared but not defined) functions - if (f.empty()) { - dbgs(1) << " Function " << f.getName() << " is external, skipping..."; - continue; - } - if (f.getName() != "main") continue; + worklist.push_back(std::make_tuple(dummy_block, dummy_block)); - // Register basic blocks - for (llvm::BasicBlock& bb: f) { - dbgs(1) << " Found basic block " << bb.getName() << '\n'; + // Register basic blocks + for (llvm::BasicBlock const& bb: *main_func) { + dbgs(1) << " Found basic block main." << bb.getName() << '\n'; - Node node; - node.id = nodes.size(); // Assign new id - node.bb = &bb; - // node.state is default initialised (to bottom) + Node node; + node.bb = &bb; + node.callstring = dummy_block; + // node.state is default initialised (to bottom) - nodeIdMap[node.bb] = node.id; - nodes.push_back(node); - } - - // Gather information about loops in the function. (We only want to widen a single node for - // each loop, as that is enough to guarantee fast termination.) - llvm::LoopInfoBase loopInfoBase; - loopInfoBase.analyze(llvm::DominatorTree {f}); - for (llvm::Loop* loop: loopInfoBase) { - // We want to widen only the conditions of the loops - nodes[nodeIdMap.at(loop->getHeader())].should_widen = true; - dbgs(1) << " Enabling widening for basic block " << loop->getHeader()->getName() << '\n'; - } + // nodes of main block have the callstring of a dummy block + nodes[std::make_tuple(&bb, dummy_block)] = node; + } - // Push the initial block into the worklist - int entry_id = nodeIdMap.at(&f.getEntryBlock()); - worklist.push_back(entry_id); - nodes[entry_id].update_scheduled = true; - nodes[entry_id].func_entry = &f; + // Gather information about loops in the function. (We only want to widen a single node for + // each loop, as that is enough to guarantee fast termination.) + llvm::LoopInfoBase loopInfoBase; + loopInfoBase.analyze(llvm::DominatorTree{ *main_func }); + for (llvm::Loop* loop : loopInfoBase) { + // We want to widen only the conditions of the loops + nodes[std::make_tuple(loop->getHeader(), dummy_block)].should_widen = true; + dbgs(1) << " Enabling widening for basic block " << loop->getHeader()->getName() << '\n'; } + // Push the initial block into the worklist + auto init_element = std::make_tuple(&main_func->getEntryBlock(), dummy_block); + worklist.push_back(init_element); + nodes[init_element].update_scheduled = true; + nodes[init_element].state = AbstractState {*main_func}; + nodes[init_element].func_entry = main_func; + dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; + // Check whether we have reached the end of the widening phase for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { - - // Check whether we have reached the end of the widening phase - if (worklist.back() == -1) { + if (worklist.back() == std::make_tuple(dummy_block, dummy_block)) { worklist.pop_back(); phase_narrowing = true; dbgs(1) << "\nStarting narrowing in iteration " << iter << "\n"; // We need to consider all nodes once more. - for (Node const& i: nodes) { - worklist.push_back(i.id); + for (auto const& i : nodes) { + worklist.push_back(i.first); } --iter; @@ -119,15 +115,18 @@ void executeFixpointAlgorithmWidening(llvm::Module& M) { worklist.pop_back(); node.update_scheduled = false; - dbgs(1) << "\nIteration " << iter << ", considering basic block " << node.bb->getName() << '\n'; + dbgs(1) << "\nIteration " << iter << ", considering basic block " + << _bb_to_str(node.bb) << " with callstring " + << _bb_to_str(node.callstring) << '\n'; AbstractState state_new; // Set to bottom if (node.func_entry) { dbgs(1) << " Merging function parameters, is entry block\n"; - AbstractState state_entry {*node.func_entry}; - state_new.merge(Merge_op::UPPER_BOUND, state_entry); + // if it is the entry node, then its state should be top + state_new.isBottom = false; + state_new.merge(Merge_op::UPPER_BOUND, node.state); } dbgs(1) << " Merge of " << llvm::pred_size(node.bb) @@ -135,25 +134,126 @@ void executeFixpointAlgorithmWidening(llvm::Module& M) { // Collect the predecessors std::vector predecessors; - for (llvm::BasicBlock* bb: llvm::predecessors(node.bb)) { - dbgs(3) << " Merging basic block " << bb->getName() << '\n'; + for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { + dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - AbstractState state_branched {nodes[nodeIdMap[bb]].state}; + AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; state_branched.branch(*bb, *node.bb); state_new.merge(Merge_op::UPPER_BOUND, state_branched); predecessors.push_back(state_branched); } - dbgs(2) << " Relevant incoming state\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); + dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); // Apply the basic block dbgs(3) << " Applying basic block\n"; - state_new.apply(*node.bb, predecessors); + + if (state_new.isBottom) { + dbgs(3) << " Basic block is unreachable, everything is bottom\n"; + } else { + for (llvm::Instruction const& inst: *node.bb) { + + // Handles return instructions + if (llvm::dyn_cast(&inst)) { + state_new.applyReturnInst(inst); + } + + // If the result of the instruction is not used, there is no reason to compute + // it. (There are no side-effects in LLVM IR. (I hope.)) + if (inst.use_empty()) { + // Except for call instructions, we still want to get that information + if (not llvm::dyn_cast(&inst)) { + dbgs(3) << " Empty use of instruction, skipping...\n"; + continue; + } + } + + // Handles merging points + if (llvm::PHINode const* phi = llvm::dyn_cast(&inst)) { + + state_new.applyPHINode(*node.bb, predecessors, inst); + + // Handles function calls + } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { + + // Checks if an input parameter for the callee is bottom. If so, + // then skip the calculation of the call instruction for now + if (state_new.checkOperandsForBottom(inst)) continue; + + llvm::Function const* callee_func = call->getCalledFunction(); + + // Checks for functions, such as printf and skips them + if (callee_func->empty()) { + dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; + continue; + } + + auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.bb); + bool changed; + + // Checks whether a node with key [%callee entry block, %caller basic block], + // i.e. an entry block with callstring of caller basic block, exists. + // If not, all nodes with their corrosponding keys are initilized for the callee function. + if (nodes.find(callee_element) == nodes.end()) { + // Check if abstract_state of call.bb is bottom or not + dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; + + // Register basic blocks + for (llvm::BasicBlock const& bb : *callee_func) { + dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; + + Node callee_node; + callee_node.bb = &bb; + callee_node.callstring = node.bb; + // node.state is default initialised (to bottom) + + nodes[std::make_tuple(&bb, node.bb)] = callee_node; + } + + nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; + nodes[callee_element].func_entry = callee_func; + changed = true; + } else { + AbstractState state_update{ callee_func, state_new, call }; + changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); + } + + //Getting the last block + llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); + auto end_element = std::make_tuple(end_block, node.bb); + + state_new.applyCallInst(inst, end_block, nodes[end_element].state); + + // If input parameters have changed, we want to interpret the function once again + // and reevaluate the nodes of possible callers. + if (changed) { + for (std::pair& i : nodes) { + if (std::get<0>(i.first) == node.bb and not i.second.update_scheduled) { + dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; + worklist.push_back(i.first); + i.second.update_scheduled = true; + } + } + + // Checks if the key of the callee functions entry node is already on the worklist, + // this is necessary for recursions. + if (not nodes[callee_element].update_scheduled) { + worklist.push_back(callee_element); + nodes[callee_element].update_scheduled = true; + + dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; + } else { + dbgs(3) << " Callee already on worklist, nothing to add...\n"; + } + } + } else { + if (state_new.checkOperandsForBottom(inst)) continue; + state_new.applyDefault(inst); + } + } + } // Merge the state back into the node - dbgs(3) << " Merging with stored state\n"; - - // We need to figure out what operation to apply. Merge_op::Type op; if (not phase_narrowing) { if (node.should_widen and node.change_count >= widen_after) { @@ -165,27 +265,31 @@ void executeFixpointAlgorithmWidening(llvm::Module& M) { op = Merge_op::NARROW; } - // Now do the actual operation + dbgs(3) << " Merging with stored state\n"; bool changed = node.state.merge(op, state_new); - dbgs(2) << " Outgoing state\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); + dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); // No changes, so no need to do anything else if (not changed) continue; ++node.change_count; - + + dbgs(2) << " Node change count:"; + dbgs(2) << node.change_count << "\n"; + dbgs(2) << " State changed, notifying " << llvm::succ_size(node.bb) << (llvm::succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); // Something changed and we will need to update the successors - for (llvm::BasicBlock* succ_bb: llvm::successors(node.bb)) { - Node& succ = nodes[nodeIdMap[succ_bb]]; + for (llvm::BasicBlock const* succ_bb: llvm::successors(node.bb)) { + auto succ_key = std::make_tuple(succ_bb, node.callstring); + Node& succ = nodes[succ_key]; if (not succ.update_scheduled) { - worklist.push_back(succ.id); + worklist.push_back(succ_key); succ.update_scheduled = true; - dbgs(3) << " Adding " << succ_bb->getName() << " to worklist\n"; + dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; } } } @@ -193,13 +297,15 @@ void executeFixpointAlgorithmWidening(llvm::Module& M) { if (!worklist.empty()) { dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; } - + // Output the final result dbgs(0) << "\nFinal result:\n"; - for (Node const& i: nodes) { - dbgs(0) << i.bb->getName() << ":\n"; - i.state.printOutgoing(*i.bb, dbgs(0), 2); + for (std::pair i: nodes) { + dbgs(0) << _bb_key_to_str(i.first) << ":\n"; + i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); } + } } /* end of namespace pcpo */ + diff --git a/src/general.h b/src/general.h new file mode 100644 index 0000000..4f519ee --- /dev/null +++ b/src/general.h @@ -0,0 +1,49 @@ +#include "llvm/ADT/Hashing.h" + +// C++ unordered_maps don't support tuples as keys, which is why one has to define the hash function for said tuple. +// If weird behaviours are observed, chances are high, that this hash function is not working properly, as we don't know +// if the llvm::hash_combine function works for all cases. So far it did (hopefully in the future as well). +namespace std { + + template <> + struct hash> + { + std::size_t operator()(const std::tuple& k) const + { + llvm::hash_code hash_code = llvm::hash_combine(std::get<0>(k), std::get<1>(k)); + return hash_code; + } + }; + +} + +namespace pcpo { + + // The definition of the tuple, for ease of writing. First element is the basic block we currently evaluate. + // Second element is the callstring, i.e. the basic block from which the function is called, which the + // first element is part of. (It's complicated to put in words). + // The callstring is normally represented by the caller function, as described in "Compiler Design: Analysis and Transformation", + // but since this code operates on BasicBlocks instead of the whole function, we set the callstring to the BasicBlock, + // from which the function call is performed. + typedef std::tuple bb_key; + + // Helper functions for debug output, pretty much self explanatory. + std::string _bb_to_str(llvm::BasicBlock const* bb) { + std::string str = "%"; + if (llvm::Function const* f = bb->getParent()) { + str.append(f->getName()); + str.append("."); + } + str.append(bb->getName()); + return str; + } + + std::string _bb_key_to_str(bb_key key) { + std::string str = "["; + str.append(_bb_to_str(std::get<0>(key))); + str.append(", "); + str.append(_bb_to_str(std::get<1>(key))); + str.append("]"); + return str; + } +} diff --git a/src/simple_interval.cpp b/src/simple_interval.cpp index b43ec0f..fb60d3c 100644 --- a/src/simple_interval.cpp +++ b/src/simple_interval.cpp @@ -3,25 +3,27 @@ namespace pcpo { SimpleInterval::SimpleInterval(llvm::Constant const& constant) { - if (llvm::ConstantInt const* c = llvm::dyn_cast(&constant)) { - state = NORMAL; - begin = c->getValue(); - end = c->getValue(); - return; - } - // Depending on how you want to handle undef values, you might want to consider them as any - // value (e.g. 0). - // if (llvm::UndefValue const* c = llvm::dyn_cast(&constant)) { - // if (llvm::IntegerType const* ty = llvm::dyn_cast(c->getType())) { - // state = NORMAL; - // begin = APInt::getNullValue(ty->getBitWidth()); - // end = begin; - // } - // state = TOP; - // return; - // } + if (llvm::ConstantInt const* c = llvm::dyn_cast(&constant)) { + state = NORMAL; + begin = c->getValue(); + end = c->getValue(); + return; + } + + + // Depending on how you want to handle undef values, you might want to consider them as any + // value (e.g. 0). + // if (llvm::UndefValue const* c = llvm::dyn_cast(&constant)) { + // if (llvm::IntegerType const* ty = llvm::dyn_cast(c->getType())) { + // state = NORMAL; + // begin = APInt::getNullValue(ty->getBitWidth()); + // end = begin; + // } + // state = TOP; + // return; + // } + state = TOP; - state = TOP; } SimpleInterval::SimpleInterval(APInt _begin, APInt _end) { @@ -31,7 +33,6 @@ SimpleInterval::SimpleInterval(APInt _begin, APInt _end) { end = _end; } - // Internal helper functions using APInt = llvm::APInt; @@ -107,6 +108,12 @@ SimpleInterval SimpleInterval::interpret( // We only deal with integer types llvm::IntegerType const* type = llvm::dyn_cast(inst.getType()); if (not type) return SimpleInterval {true}; + + type = llvm::dyn_cast(inst.getOperand(0)->getType()); + if (not type) return SimpleInterval {true}; + + type = llvm::dyn_cast(inst.getOperand(1)->getType()); + if (not type) return SimpleInterval {true}; unsigned bitWidth = inst.getOperand(0)->getType()->getIntegerBitWidth(); assert(bitWidth == inst.getOperand(1)->getType()->getIntegerBitWidth()); diff --git a/src/value_set.h b/src/value_set.h index 3bb2b49..074216a 100644 --- a/src/value_set.h +++ b/src/value_set.h @@ -8,6 +8,7 @@ #include "llvm/IR/CFG.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" +#include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h" #include "global.h" #include "callstring.h" @@ -89,69 +90,163 @@ public: isBottom = false; } - void apply(llvm::BasicBlock const& bb, std::vector const& pred_values) { - if (isBottom) { - dbgs(3) << " Basic block is unreachable, everything is bottom\n"; - return; + // 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 AbstractStateValueSet(llvm::Function const* callee_func, AbstractStateValueSet const& state, + llvm::CallInst const* call) { + assert(callee_func->arg_size() == call->getNumArgOperands()); + for (llvm::Argument const& arg: callee_func->args()) { + llvm::Value* value = call->getArgOperand(arg.getArgNo()); + if (value->getType()->isIntegerTy()) { + if (llvm::Constant const* c = llvm::dyn_cast(value)) { + values[&arg] = AbstractDomain {*c}; + } else { + values[&arg] = state.values.at(value); + } + } else { + values[&arg] = AbstractDomain {true}; + } } - + isBottom = false; + } + + // Handles the evaluation of merging points + void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, + llvm::Instruction const& inst) { std::vector operands; + AbstractDomain inst_result; - // Go through each instruction of the basic block and apply it to the state - for (llvm::Instruction const& inst: bb) { + llvm::PHINode const* phi = llvm::dyn_cast(&inst); - // If the result of the instruction is not used, there is no reason to compute - // it. (There are no side-effects in LLVM IR. (I hope.)) - if (inst.use_empty()) continue; + // Phi nodes are handled here, to get the precise values of the predecessors + + for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) { + // Find the predecessor corresponding to the block of the phi node + unsigned block = 0; + for (llvm::BasicBlock const* pred_bb: llvm::predecessors(&bb)) { + if (pred_bb == phi->getIncomingBlock(i)) break; + ++block; + } - AbstractDomain inst_result; - - if (llvm::PHINode const* phi = llvm::dyn_cast(&inst)) { - // Phi nodes are handled here, to get the precise values of the predecessors - - for (unsigned i = 0; i < phi->getNumIncomingValues(); ++i) { - // Find the predecessor corresponding to the block of the phi node - unsigned block = 0; - for (llvm::BasicBlock const* pred_bb: llvm::predecessors(&bb)) { - if (pred_bb == phi->getIncomingBlock(i)) break; - ++block; - } - - // Take the union of the values - AbstractDomain pred_value = pred_values[block].getAbstractValue(*phi->getIncomingValue(i)); - inst_result = AbstractDomain::merge(Merge_op::UPPER_BOUND, inst_result, pred_value); - - operands.push_back(pred_value); // Keep the debug output happy - } - } - else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { - InterpretCall(call, operands); - } - else { - for (llvm::Value const* value: inst.operand_values()) { - operands.push_back(getAbstractValue(*value)); + // Take the union of the values + AbstractDomain pred_value = pred_values[block].getAbstractValue(*phi->getIncomingValue(i)); + inst_result = AbstractDomain::merge(Merge_op::UPPER_BOUND, inst_result, pred_value); + + operands.push_back(pred_value); // Keep the debug output happy + } + + values[&inst] = inst_result; + + debug_output(inst, operands); + } + + // Handles the evaluation of function calls + // This is the "combine" function as described in "Compiler Design: Analysis and Transformation" + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, + AbstractStateValueSet const& callee_state) { + std::vector 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 (llvm::Instruction const& iter_inst: *end_block) { + if (llvm::ReturnInst const* ret_inst = llvm::dyn_cast(&iter_inst)) { + llvm::Value const* ret_val = ret_inst->getReturnValue(); + dbgs(4) << " Found return instruction\n"; + + if (callee_state.values.find(ret_val) != callee_state.values.end()) { + dbgs(4) << " Return evaluated, merging parameters\n"; + values[&inst] = callee_state.values.at(ret_val); + } else { + dbgs(4) << " Return not evaluated, setting to bottom\n"; + values[&inst] = AbstractDomain{}; // Initializes the return value to Bottom } + } + } + + debug_output(inst, operands); + } - // Compute the result of the operation - inst_result = AbstractDomain::interpret(inst, operands); + // Handles the evaluation of return instructions + void applyReturnInst(llvm::Instruction const& inst) { + llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); + if (ret_val->getType()->isIntegerTy()) { + if (llvm::Constant const* c = llvm::dyn_cast(ret_val)) { + values[&inst] = AbstractDomain{ *c }; + } else if (values.find(ret_val) != values.end()) { + values[&inst] = values.at(ret_val); + } else { + values[&inst] = AbstractDomain{}; // Initializes the return value to Bottom } - - values[&inst] = inst_result; - - dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; - {int i = 0; - for (llvm::Value const* value: inst.operand_values()) { - if (i) dbgs(3) << ", "; - if (value->getName().size()) dbgs(3) << '%' << value->getName() << " = "; - dbgs(3) << operands[i]; - ++i; - }} - dbgs(3) << '\n'; + } else { + values[&inst] = AbstractDomain{ true }; + } + } - operands.clear(); + // Handles the evaluation of all other instructions + void applyDefault(llvm::Instruction const& inst) { + std::vector operands; + + for (llvm::Value const* value: inst.operand_values()) { + operands.push_back(getAbstractValue(*value)); } + + // Compute the result of the operation + values[&inst] = AbstractDomain::interpret(inst, operands); + + debug_output(inst, operands); } - + + // Used for debug output + void debug_output(llvm::Instruction const& inst, std::vector operands) { + dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; + {int i = 0; + for (llvm::Value const* value: inst.operand_values()) { + if (i) dbgs(3) << ", "; + if (value->getName().size()) dbgs(3) << '%' << value->getName() << " = "; + dbgs(3) << operands[i]; + ++i; + }} + dbgs(3) << '\n'; + } + + // Checks whether at least one of the operands is bottom -- in such a case we + // set the result to bottom as well + bool checkOperandsForBottom(llvm::Instruction const& inst) { + std::vector operands; + + for (llvm::Value const* value : inst.operand_values()) { + operands.push_back(getAbstractValue(*value)); + } + + for (llvm::Value const* value : inst.operand_values()) + if (llvm::Constant const* c = llvm::dyn_cast(value)) { + + } else + if (values[value].isBottom()) { + values[&inst] = AbstractDomain{}; + + dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; + {int i = 0; + for (llvm::Value const* value : inst.operand_values()) { + if (i) dbgs(3) << ", "; + if (value->getName().size()) dbgs(3) << '%' << value->getName() << " = "; + dbgs(3) << operands[i]; + ++i; + }} + dbgs(3) << '\n'; + + operands.clear(); + + return true; + } + return false; + } + + bool merge(Merge_op::Type op, AbstractStateValueSet const& other) { bool changed = false; @@ -174,10 +269,10 @@ public: values[i.first] = v; changed = true; - + if (checkValueForBottom(4, i.first)) return changed; } - if (changed) checkForBottom(4); + //if (changed) checkForBottom(4); return changed; } @@ -253,17 +348,24 @@ public: if (values.count(&lhs) && values.count(&rhs)) { dbgs(3) << " Values restricted to %" << lhs.getName() << " = " << values[&lhs] << " and %" << rhs.getName() << " = " << values[&rhs] << '\n'; + if (!checkValueForBottom(6, &lhs)) checkValueForBottom(6, &rhs); } else if (values.count(&lhs)) { dbgs(3) << " Value restricted to %" << lhs.getName() << " = " << values[&lhs] << '\n'; + checkValueForBottom(6, &lhs); } else if (values.count(&rhs)) { dbgs(3) << " Value restricted to %" << rhs.getName() << " = " << values[&rhs] << '\n'; + checkValueForBottom(6, &rhs); } else { dbgs(3) << " No restrictions were derived.\n"; } // This cannot happen when doing UPPER_BOUND or WIDEN, but for NARROW it is possible, so // check just in case. - checkForBottom(6); + + //This was created before interprocedural analysis was added, therefore it might work wrong + //Perhaps, one should check each value for bottom separately, see bool checkValueForBottom() + //if (!checkValueForBottom(6, &lhs)) checkValueForBottom(6, &rhs); + //checkForBottom(4); } void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { @@ -290,9 +392,15 @@ public: } void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { for (auto const& i: values) { - out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; + if (llvm::ReturnInst::classof(i.first)) { + out.indent(indentation) << " = " << i.second << '\n'; + } else { + out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; + } } - if (values.size() == 0) { + if (isBottom) { + out.indent(indentation) << "bottom\n"; + } else if (values.size() == 0) { out.indent(indentation) << "\n"; } }; @@ -314,6 +422,10 @@ public: // If any of our values is bottom, then we are bottom as well. So this function checks that and // normalises our value. Returns whether this changed our value (i.e. we are now bottom). + + // Actually the statement above is correct if we have only one function. If not, then we can + // have values that are bottom but do not belong to the current function => the function + // is not bottom. Use bool checkValueForBottom() if you want to check the values separately. bool checkForBottom(int indent = 0) { if (isBottom) return false; @@ -329,6 +441,21 @@ public: } return false; } + + // For a specific value checks whether it is a bottom. See bool checkForBottom for more + // information + bool checkValueForBottom(int indent, llvm::Value const* value) { + if (isBottom) return false; + if (values[value] == AbstractDomain{}) { + dbgs(3).indent(indent) << "Variable %" << value->getName() << " is bottom, so the state is as well.\n"; + + values.clear(); + isBottom = true; + + return true; + } + return false; + } }; } /* end of namespace pcpo */ -- GitLab From 5833bcb6304241f774145988b3114ab4397864bf Mon Sep 17 00:00:00 2001 From: Florian Stamer Date: Wed, 20 Nov 2019 14:29:43 +0100 Subject: [PATCH 003/142] Fixed CMake --- CMakeLists.txt | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 902a376..3f6a113 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -5,19 +5,10 @@ # set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Hello.exports) # endif() #endif() -cmake_minimum_required(VERSION 3.10) -find_package(LLVM REQUIRED CONFIG) -add_definitions(${LLVM_DEFINITIONS}) -include_directories(${LLVM_INCLUDE_DIRS}) - -message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") -message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") - -list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") -message(STATUS "${LLVM_CMAKE_DIR}") - -include(AddLLVM) +if(WIN32 OR CYGWIN) + set(LLVM_LINK_COMPONENTS Core Support) +endif() # For older LLVM < 8.0 replace first line with this # add_llvm_loadable_module(llvm-pain @@ -28,8 +19,6 @@ add_llvm_library(llvm-pain MODULE src/value_set.h src/simple_interval.cpp src/simple_interval.h - src/callstring.cpp - src/callstring.h DEPENDS intrinsics_gen PLUGIN_TOOL -- GitLab From d142c1edd6a73a086b50dee880702c64c9d437f4 Mon Sep 17 00:00:00 2001 From: "Dr. Michael Petter" Date: Thu, 28 Nov 2019 15:27:04 +0100 Subject: [PATCH 004/142] WS 2019 authors added --- README.md | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 8308436..12fff9f 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Program Optimization Lab 2018 +# Program Optimization Lab 2019 -Implements an LLVM analysis pass using abstract interpretation. +Implements an LLVM interprocedural analysis pass using abstract interpretation. ## Build @@ -42,14 +42,19 @@ If there are errors regarding missing header files, you probably need to rebuild The `run.py` script contains everything, up to and including the kitchen sink. It can run the samples, build, run the debugger, as well as build and run the tests. Just read its help message to get all the good stuff. I want to highlight the `-n` option, which causes it to just print out the commands it would run. This is great to just copy-paste the relevant ones into your terminal (or IDE). -## Authors +## Authors Lab Course WS 2019/20 + +* Florian Stamer +* Dmytro Yakymets + +## Authors Lab Course WS 2018/19 * Ramona Brückl * Philipp Czerner ([github](https://github.com/suyjuris/), [mail](mailto:philipp.czerner@nicze.de)) * Tim Gymnich * Thomas Frank -### Authors of previous semesters +## Authors Lab Course SS 2018 * Julian Erhard * Jakob Gottfriedsen * Peter Munch -- GitLab From ccdf9e5012c278a75425038a4c67b3fe4f536db5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Dec 2019 17:47:18 +0100 Subject: [PATCH 005/142] added xcode option to build scripts --- init.py | 8 +++++--- run.py | 23 ++++++++++++++++------- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/init.py b/init.py index 61010ec..8d44731 100755 --- a/init.py +++ b/init.py @@ -14,11 +14,13 @@ parser = argparse.ArgumentParser(description='Setup the project. This creates th parser.add_argument('--llvm-path', help='path to the LLVM build directory, containing a file bin/opt.') parser.add_argument('--llvm-src', help='path to the LLVM source directory, containing lib/Analysis ') parser.add_argument('--clang-path', help='path to the clang build direcotry, containing a file bin/clang') +parser.add_argument('--xcode', action='store_true', help='for builds using xcode') args = parser.parse_args() llvm_path = args.llvm_path llvm_src = args.llvm_src clang_path = args.clang_path +xcode = args.xcode project_dir = os.path.dirname(os.path.abspath(sys.argv[0])) project_name = 'AbstractInterpretation' @@ -63,9 +65,9 @@ if clang_path is None: clang_path = llvm_path print('clang-path not specified, defaulting to ' + clang_path) -opt = llvm_path + '/bin/opt' +opt = llvm_path + ['', '/Debug'][xcode] + '/bin/opt' llvm_dest = llvm_src + '/lib/Analysis' -clang = clang_path + '/bin/clang' +clang = clang_path + ['', '/Debug'][xcode] + '/bin/clang' if not os.path.isfile(opt): print('Error: no opt exists at ' + opt + ' (maybe you forgot to build LLVM?)') @@ -88,7 +90,7 @@ except FileExistsError: # Write the configuration for the run.py script config_file_name = project_dir + '/.config'; config_file = open(config_file_name, 'w') -config_file.write(llvm_src+'\n'+llvm_path+'\n'+clang_path+'\n') +config_file.write(llvm_src+'\n'+llvm_path+'\n'+clang_path+'\n'+['make\n', 'xcode\n'][xcode]) config_file.close() print('Wrote configuration to %s' % (config_file_name,)) diff --git a/run.py b/run.py index f0e6370..87c6c0b 100755 --- a/run.py +++ b/run.py @@ -23,12 +23,16 @@ config_file = open(".config", "r") lines = config_file.readlines() llvm_path = lines[1].strip() clang_path = lines[2].strip() +try: + xcode = lines[3].strip() == 'xcode' +except IndexError: + xcode = false config_file.close() -opt = llvm_path + '/bin/opt' -llvm_dis = llvm_path + '/bin/llvm-dis' -llvm_config = llvm_path + '/bin/llvm-config' -clang = clang_path + '/bin/clang' +opt = llvm_path + ['', '/Debug'][xcode] + '/bin/opt' +llvm_dis = llvm_path + ['', '/Debug'][xcode] + '/bin/llvm-dis' +llvm_config = llvm_path + ['', '/Debug'][xcode] + '/bin/llvm-config' +clang = clang_path + ['', '/Debug'][xcode] + '/bin/clang' cmake = 'cmake' gdb = 'gdb' lldb = 'lldb' @@ -57,7 +61,7 @@ else: print('Error: Unknown platform ' + platform.system()) sys.exit(4) -pass_lib = llvm_path + "/lib/llvm-pain" + libeext +pass_lib = llvm_path + ['', '/Debug'][xcode] + "/lib/llvm-pain" + libeext pass_name = "painpass" make_target = "llvm-pain" @@ -140,8 +144,13 @@ def main(): print("Error: " + f_orig +" not found!") continue print("Processing file " + fname + " ...") - - run([clang, '-O0', '-emit-llvm', f_orig, '-Xclang', '-disable-O0-optnone', '-c', '-o', f_bc]) + + if platform.system() == 'Darwin': + includes = subprocess.getoutput('xcrun --show-sdk-path') + run([clang, '--sysroot', includes, '-O0', '-emit-llvm', f_orig, '-Xclang', '-disable-O0-optnone', '-c', '-o', f_bc]) + else: + run([clang, '-O0', '-emit-llvm', f_orig, '-Xclang', '-disable-O0-optnone', '-c', '-o', f_bc]) + run([opt, '-mem2reg', f_bc, '-o', f_optbc]) run([llvm_dis, f_optbc, '-o', f_optll]) run(['rm', f_bc, f_optbc]) -- GitLab From faa78f7dbb5c0fd4a47010e840941136d681a297 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 31 Dec 2019 16:22:40 +0100 Subject: [PATCH 006/142] leastUpperBound --- CMakeLists.txt | 2 + src/normalized_conjunction.cpp | 511 +++++++++++++++++++++++++++++++++ src/normalized_conjunction.h | 72 +++++ 3 files changed, 585 insertions(+) create mode 100644 src/normalized_conjunction.cpp create mode 100644 src/normalized_conjunction.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f6a113..cbb028c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,8 @@ add_llvm_library(llvm-pain MODULE src/value_set.h src/simple_interval.cpp src/simple_interval.h + src/normalized_conjunction.cpp + src/normalized_conjunction.h DEPENDS intrinsics_gen PLUGIN_TOOL diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp new file mode 100644 index 0000000..5ad1b85 --- /dev/null +++ b/src/normalized_conjunction.cpp @@ -0,0 +1,511 @@ +#include "normalized_conjunction.h" +#include +#include + +namespace pcpo { + +using APInt = llvm::APInt; + +// MARK: Initializers + +NormalizedConjunction::NormalizedConjunction(llvm::Constant const& constant) { + if (llvm::ConstantInt const* c = llvm::dyn_cast(&constant)) { + state = NORMAL; + // Watch out for signed/unsigend APInts in future + Equality eq = {&constant, APInt(64, 0), nullptr, c->getValue()}; + equalaties = {{&constant,eq}}; + } else { + state = TOP; + } +} + +NormalizedConjunction::NormalizedConjunction(std::map equalaties) { + this->equalaties = equalaties; + state = NORMAL; +} + +// MARK: AbstractDomain interface + +NormalizedConjunction NormalizedConjunction::interpret( + llvm::Instruction const& inst, std::vector const& operands +) { + // FIXME: maybe use nonDeterministic assignment here. + if (operands.size() != 2) return NormalizedConjunction {true}; + + // We only deal with integer types + llvm::IntegerType const* type = llvm::dyn_cast(inst.getType()); + // FIXME: maybe use nonDeterministic assignment here. + if (not type) return NormalizedConjunction {true}; + + type = llvm::dyn_cast(inst.getOperand(0)->getType()); + // FIXME: maybe use nonDeterministic assignment here. + if (not type) return NormalizedConjunction {true}; + + type = llvm::dyn_cast(inst.getOperand(1)->getType()); + // FIXME: maybe use nonDeterministic assignment here. + if (not type) return NormalizedConjunction {true}; + + unsigned bitWidth = inst.getOperand(0)->getType()->getIntegerBitWidth(); + assert(bitWidth == inst.getOperand(1)->getType()->getIntegerBitWidth()); + + NormalizedConjunction a = operands[0]; + NormalizedConjunction b = operands[1]; + + // Abstract Effect of statements: + + // [xi := ?] + // default + + // [xi := b] + // This will never occur due to SSA Form. + + // [xi := xi] + // [xi := xj] + // Those will never occur due to SSA Form. + + // [xi := xi + b] [xi := xi - b] + // Cannot occur due to SSA + + // [xi := xj + b] [xi := xj - b] + + // [xi := a * xi] [xi := a / xi] + // Cannot occur due to SSA + + // [xi := a * xj] [xi := a / xj] + + // [xi := a * xi + b] [xi := a * xi - b] [xi := a / xi + b] [xi := a / xi - b] + // Cannot occur due to SSA + + // [xi := a * xj + b] [xi := a * xj - b] [xi := a / xj + b] [xi := a / xj - b] + // Those will never occur due to SSA Form. + + switch (inst.getOpcode()) { + case llvm::Instruction::Add: + return NormalizedConjunction::Add(inst, a, b); + case llvm::Instruction::Sub: + return NormalizedConjunction::Sub(inst, a, b); + case llvm::Instruction::Mul: + return NormalizedConjunction::Mul(inst, a, b); + case llvm::Instruction::SDiv: + case llvm::Instruction::UDiv: + default: + return nonDeterminsticAssignment(inst, a, b); + } +} + +NormalizedConjunction NormalizedConjunction::refineBranch(llvm::CmpInst::Predicate pred, llvm::Value const& lhs, llvm::Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { + + // TODO + + assert(false && "Not implemented"); + + return nullptr; +} + + +NormalizedConjunction NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b) { + if (a.isBottom()) return b; + if (b.isBottom()) return a; + if (a.isTop() || b.isTop()) return NormalizedConjunction {true}; + + // Note that T is handled above, so no need to convert the inputs + + switch (op) { + case Merge_op::UPPER_BOUND: + return leastUpperBound(a, b); + case Merge_op::WIDEN: + assert(false && "not implemented"); + case Merge_op::NARROW: + assert(false && "not implmented"); + default: + assert(false && "invalid op value"); + return NormalizedConjunction {true}; + } +} + + // TODO: getter / setter for equlaties to keep equalaties normalized + + +// MARK: Lattice Operations + +NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjunction lhs, NormalizedConjunction rhs) { + // set of all occuring variables in E1 and E2 + std::set vars, varsE1, varsE2; + std::set E1, E2; + + auto mapToSeccond = [](std::pair p){ return p.second; }; + std::transform(lhs.equalaties.begin(), lhs.equalaties.end(), std::inserter(E1, E1.end()), mapToSeccond); + std::transform(rhs.equalaties.begin(), rhs.equalaties.end(), std::inserter(E2, E2.end()), mapToSeccond); + + auto mapToY = [](Equality eq){ return eq.y; }; + std::transform(E1.begin(), E1.end(), std::inserter(varsE1, varsE1.end()), mapToY); + std::transform(E2.begin(), E2.end(), std::inserter(varsE2, varsE2.end()), mapToY); + std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); + + std::set dX1, dX2; + + std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); + std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); + + // extend E1 by trivial equalities + for (auto d: dX1) { + Equality eq = {d, APInt(64,0), d, APInt(64,0)}; + E1.insert(eq); + } + + // extend E2 by trivial equalities + for (auto d: dX2) { + Equality eq = {d, APInt(64,0), d, APInt(64,0)}; + E2.insert(eq); + } + + // XO / E'0: set of variables where the right hand side in E1 and E2 coincide + std::set X0; + { + std::set_intersection(E1.begin(), E1.end(), E2.begin(), E2.end(), std::inserter(X0, X0.end())); + + // Remove trivial equalities + std::set filteredX0; + auto filterTrivialEqualaties = [](Equality eq){return eq.y != eq.x;}; + std::copy_if(X0.begin(), X0.end(), std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); + X0 = filteredX0; + + } + // X1 / E'1: set of variables where the right hand side is constant but does not coincide in E1 and E2 + std::set X1; + { + std::set> differentConstants; + + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); + + for (auto eq1: E1) { + for (auto eq2: E2) { + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (eq1.isConstant() && eq2.isConstant() && eq1.b != eq2.b) { + differentConstants.insert({eq1,eq2}); + } + } + } + + // pop first element + std::pair h = *differentConstants.begin(); + differentConstants.erase(differentConstants.begin()); + + for (auto i: differentConstants) { + // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) + // y = m * x + b + auto y = i.first.y; + APInt m = (h.second.b - h.first.b).sdiv((i.second.b - i.first.b)); + auto x = h.first.x; + APInt b = -m * h.first.b + i.first.b; + Equality eq = {y, m, x, b}; + X1.insert(eq); + } + } + // X2 / E'2: set of variables where the right hand side of E1 is constant but the rhs of E2 contains a variable. + std::set X2; + { + std::set> differentConstants; + + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); + + for (auto eq1: E1) { + for (auto eq2: E2) { + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (eq1.isConstant() && !eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); + } + } + } + + std::vector>> Pi2; + + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.second.x == jpair.second.x; + bool condition2 = (ipair.first.b - ipair.second.b).sdiv(ipair.second.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.second.a); + if (condition1 && condition2) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); + } + } + Pi2.push_back(equivalenceClass); + } + + // form equaltites for partitions in Pi2 + for (auto q: Pi2) { + std::pair h = *q.begin(); + q.erase(q.begin()); + for (auto i: q) { + // xi = ai/ah * xh + ( bi - (ai * bh) / ah) + auto y = i.first.y; + auto m = i.second.a.sdiv(h.second.b); + auto x = h.first.y; + auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + Equality eq = {y, m, x, b}; + X2.insert(eq); + } + } + + } + // X3 / E'3: set of variables where the right hand side of E1 contains a variable and E2 contains a constant. + std::set X3; + { + std::set> differentConstants; + + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); + + for (auto eq1: E1) { + for (auto eq2: E2) { + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (!eq1.isConstant() && eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); + } + } + } + + std::vector>> Pi3; + + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.second.x == jpair.second.x; + bool condition2 = (ipair.first.b - ipair.second.b).sdiv(ipair.second.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.second.a); + if (condition1 && condition2) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); + } + } + Pi3.push_back(equivalenceClass); + } + + // form equaltites for partitions in Pi3 + for (auto q: Pi3) { + std::pair h = *q.begin(); + q.erase(q.begin()); + for (auto i: q) { + // xi = ai/ah * xh + ( bi - (ai * bh) / ah) + auto y = i.first.y; + auto m = i.second.a.sdiv(h.second.b); + auto x = h.first.y; + auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + Equality eq = {y, m, x, b}; + X3.insert(eq); + } + } + + } + + + // X4 / E'4: + std::set X4; + { + std::set> differentConstants; + + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); + + for (auto eq1: E1) { + for (auto eq2: E2) { + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (!eq1.isConstant() && !eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); + } + } + } + + std::vector>> Pi4; + + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.first.x == jpair.first.x && ipair.second.x == jpair.second.x; + bool condition2 = ipair.second.a.sdiv(ipair.first.a) == jpair.second.a.sdiv(jpair.first.a); + bool condition3 = (ipair.first.b - ipair.second.b).sdiv(ipair.first.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.first.a); + if (condition1 && condition2 && condition3) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); + } + } + Pi4.push_back(equivalenceClass); + } + + // form equaltites for partitions in Pi4 + for (auto q: Pi4) { + std::pair h = *q.begin(); + q.erase(q.begin()); + for (auto i: q) { + // xi = ai/ah * xh + ( bi - (ai * bh) / ah) + auto y = i.first.y; + auto m = i.second.a.sdiv(h.second.b); + auto x = h.first.y; + auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + Equality eq = {y, m, x, b}; + X3.insert(eq); + } + } + + + } + + // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 + + + std::set leastUpperBound; + leastUpperBound.insert(X0.begin(), X0.end()); + leastUpperBound.insert(X1.begin(), X1.end()); + leastUpperBound.insert(X2.begin(), X2.end()); + leastUpperBound.insert(X3.begin(), X3.end()); + leastUpperBound.insert(X4.begin(), X4.end()); + + std::map result; + + auto addMapping = [](Equality eq){ return std::make_pair(eq.y,eq); }; + std::transform(leastUpperBound.begin(), leastUpperBound.end(), std::inserter(result, result.end()), addMapping); + + return NormalizedConjunction(result); +} + +// MARK: Abstract Operations + +// [xi := ?]# E = Exists# xi in E +NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); + + auto i = result.equalaties[&inst]; + if (i.x != &inst && i.a != APInt(64,0)) { + result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; + } else { + // find all equations using xi + std::map X; + auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; + std::copy_if(result.equalaties.begin(), result.equalaties.end(), std::inserter(X, X.end()), predicate); + // pop first element + std::pair k = *X.begin(); + X.erase(X.begin()); + for (auto l: X) { + // FIXME: modify result instead of X which will get dropped later and not be used anymore + // use std::find_if iterator instead of copy if + l.second.a = APInt(64,1); + l.second.x = k.second.y; + l.second.b = l.second.b - k.second.b; + } + k.second.a = APInt(64,1); + k.second.x = k.second.y; + k.second.b = APInt(64,0); + result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; + } + return result; +} + +// TODO: [xi := xj + x] +NormalizedConjunction NormalizedConjunction::Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); + auto i = result.equalaties[&inst]; + + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + llvm::Value const* j; + llvm::ConstantInt const* b; + + if (llvm::isa(op1) && llvm::isa(op2)) { + b = llvm::dyn_cast(op1); + j = op2; + } else if (llvm::isa(op2) && llvm::isa(op1)) { + b = llvm::dyn_cast(op2); + j = op1; + } else { + assert(false && "One operand has to be constant"); + } + + auto jj = result.equalaties[j]; + + if (i > jj) { + result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, i.b + jj.b};; + } else { + // Filter results + auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; + for (auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), pred); + it != result.equalaties.end(); + it = std::find_if(++it, result.equalaties.end(), pred)) { + auto& k = it->second; + result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b - i.b - jj.b}; + } + result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, -i.b - jj.b}; + } + + return result; +} + +// TODO: [xi := xj - x] +NormalizedConjunction NormalizedConjunction::Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); + auto i = result.equalaties[&inst]; + + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + llvm::Value const* j; + llvm::ConstantInt const* b; + + if (llvm::isa(op1) && llvm::isa(op2)) { + b = llvm::dyn_cast(op1); + j = op2; + } else if (llvm::isa(op2) && llvm::isa(op1)) { + b = llvm::dyn_cast(op2); + j = op1; + } else { + assert(false && "One operand has to be constant"); + } + + auto jj = result.equalaties[j]; + + if (i > jj) { + result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, jj.b - i.b};; + } else { + // Filter results + auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; + for (auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), pred); + it != result.equalaties.end(); + it = std::find_if(++it, result.equalaties.end(), pred)) { + auto& k = it->second; + result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b + i.b - jj.b}; + } + result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, i.b - jj.b}; + } + + return result; +} + +// [xi := a * xj] +NormalizedConjunction NormalizedConjunction::Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); + + // TODO + assert(false && "not implemented"); + + return result; +} + +} + + diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h new file mode 100644 index 0000000..b2c0674 --- /dev/null +++ b/src/normalized_conjunction.h @@ -0,0 +1,72 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "global.h" +#include + +namespace pcpo { + +class NormalizedConjunction { + + public: + enum State: char { + INVALID, BOTTOM = 1, NORMAL = 2, TOP = 4 + }; + // TODO: consider making this a computed value based on the equalaties + char state; + struct Equality { + // y = a * x + b + llvm::Value const* y; + llvm::APInt a; + llvm::Value const* x; + llvm::APInt b; + + inline bool operator<(Equality const& rhs) const { + return y < rhs.y; + }; + + inline bool operator>(Equality const& rhs) const { + return y > rhs.y; + }; + + inline bool operator==(Equality const& rhs) const { + return y == rhs.y && a == rhs.a && x == rhs.x && b == rhs.b; + }; + + bool isConstant() const { return x == nullptr; }; + }; + std::map equalaties; + + // AbstractDomain interface + NormalizedConjunction(bool isTop = false): state{isTop ? TOP : BOTTOM} {} + NormalizedConjunction(llvm::Constant const& constant); + static NormalizedConjunction interpret( + llvm::Instruction const& inst, std::vector const& operands + ); + static NormalizedConjunction refineBranch( + llvm::CmpInst::Predicate pred, llvm::Value const& lhs, llvm::Value const& rhs, + NormalizedConjunction a, NormalizedConjunction b + ); + static NormalizedConjunction merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b); + + // utils + bool isTop() const { return state == TOP; }; + bool isBottom() const { return state == BOTTOM; }; + + private: + NormalizedConjunction(std::map equalaties); + + static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); + static NormalizedConjunction nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + static NormalizedConjunction Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + static NormalizedConjunction Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); +}; + +} -- GitLab From 4c71385701c4d29902444efac64d7e2e4310f074 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 7 Jan 2020 10:34:45 +0100 Subject: [PATCH 007/142] fixpoint --- src/fixpoint.cpp | 7 +- src/fixpoint_two_var_eq.cpp | 259 +++++++++++++++++++++++++++++++++ src/normalized_conjunction.cpp | 85 +++++++---- src/normalized_conjunction.h | 10 +- 4 files changed, 332 insertions(+), 29 deletions(-) create mode 100644 src/fixpoint_two_var_eq.cpp diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 4391b29..60281fb 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -12,8 +12,10 @@ #include "global.h" #include "callstring.h" #include "fixpoint_widening.cpp" +#include "fixpoint_two_var_eq.cpp" #include "value_set.h" #include "simple_interval.h" +#include "normalized_conjunction.h" namespace pcpo { @@ -337,10 +339,13 @@ void executeFixpointAlgorithm(llvm::Module const& M) { bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; + using AbstractState2 = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening // executeFixpointAlgorithm (M); - executeFixpointAlgorithmWidening(M); +// executeFixpointAlgorithmWidening(M); + + executeFixpointAlgorithmTwoVarEq(M); // We never change anything return false; diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp new file mode 100644 index 0000000..3db7a53 --- /dev/null +++ b/src/fixpoint_two_var_eq.cpp @@ -0,0 +1,259 @@ +#include +#include + +#include "llvm/IR/CFG.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Dominators.h" +#include "llvm/Analysis/LoopInfo.h" + +#include "global.h" +#include "callstring.h" +#include "value_set.h" + +namespace pcpo { + +// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface +// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods +// with the right signatures and take care to fulfil the contracts outlines above). +// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in +// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as +// well. +// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I +// recommend Meld.) +template +void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { + constexpr int iterations_max = 1000; + + // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data + // per node to execute the fixpoint algorithm. + struct Node { + llvm::BasicBlock const* bb; + llvm::BasicBlock const* callstring; + AbstractState state; + bool update_scheduled = false; // Whether the node is already in the worklist + + // If this is set, the algorithm will add the initial values from the parameters of the + // function to the incoming values, which is the correct thing to do for initial basic + // blocks. + llvm::Function const* func_entry = nullptr; + }; + + std::unordered_map nodes; + std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed + + // We only consider the main function in the beginning. If no main exists, nothing is evaluated! + llvm::Function const* main_func = M.getFunction("main"); + + //creating dummy block for callstrings of the main block, since the main function is not called from another function + llvm::BasicBlock const* dummy_block = llvm::BasicBlock::Create(M.getContext(), "dummy"); + + // TODO: Check what this does for release clang, probably write out a warning + dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; + + // Register basic blocks of the main function + for (llvm::BasicBlock const& bb: *main_func) { + dbgs(1) << " Found basic block main." << bb.getName() << '\n'; + + Node node; + node.bb = &bb; + node.callstring = dummy_block; + // node.state is default initialised (to bottom) + + // nodes of main block have the callstring of a dummy block + nodes[std::make_tuple(&bb, dummy_block)] = node; + } + + // Push the initial block into the worklist + auto init_element = std::make_tuple(&main_func->getEntryBlock(), dummy_block); + worklist.push_back(init_element); + nodes[init_element].update_scheduled = true; + nodes[init_element].state = AbstractState {*main_func}; + nodes[init_element].func_entry = main_func; + + dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") + << ". Starting fixpoint iteration...\n"; + + for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { + Node& node = nodes[worklist.back()]; + worklist.pop_back(); + node.update_scheduled = false; + + dbgs(1) << "\nIteration " << iter << ", considering basic block " + << _bb_to_str(node.bb) << " with callstring " + << _bb_to_str(node.callstring) << '\n'; + + AbstractState state_new; // Set to bottom + + if (node.func_entry) { + dbgs(1) << " Merging function parameters, is entry block\n"; + + // if it is the entry node, then its state should be top + state_new.isBottom = false; + state_new.merge(Merge_op::UPPER_BOUND, node.state); + } + + dbgs(1) << " Merge of " << llvm::pred_size(node.bb) + << (llvm::pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); + + // Collect the predecessors + std::vector predecessors; + for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { + dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; + + AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; + state_branched.branch(*bb, *node.bb); + state_new.merge(Merge_op::UPPER_BOUND, state_branched); + predecessors.push_back(state_branched); + } + + dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); + + // Apply the basic block + dbgs(3) << " Applying basic block\n"; + + if (state_new.isBottom) { + dbgs(3) << " Basic block is unreachable, everything is bottom\n"; + } else { + // Applies all instrucions of a basic block + for (llvm::Instruction const& inst: *node.bb) { + + // Handles return instructions + if (llvm::dyn_cast(&inst)) { + state_new.applyReturnInst(inst); + } + + // If the result of the instruction is not used, there is no reason to compute + // it. (There are no side-effects in LLVM IR. (I hope.)) + if (inst.use_empty()) { + // Except for call instructions, we still want to get that information + if (not llvm::dyn_cast(&inst)) { + dbgs(3) << " Empty use of instruction, skipping...\n"; + continue; + } + } + + // Handles merging points + if (llvm::dyn_cast(&inst)) { + + state_new.applyPHINode(*node.bb, predecessors, inst); + + // Handles function calls + } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { + + // Checks if an input parameter for the callee is bottom. If so, + // then skip the calculation of the call instruction for now + if (state_new.checkOperandsForBottom(inst)) continue; + + llvm::Function const* callee_func = call->getCalledFunction(); + + // Checks for functions, such as printf and skips them + if (callee_func->empty()) { + dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; + continue; + } + + auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.bb); + bool changed; + + // Checks whether a node with key [%callee entry block, %caller basic block], + // i.e. an entry block with callstring of caller basic block, exists. + // If not, all nodes with their corrosponding keys are initilized for the callee function. + if (nodes.find(callee_element) == nodes.end()) { + // Check if abstract_state of call.bb is bottom or not + dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; + + // Register basic blocks + for (llvm::BasicBlock const& bb : *callee_func) { + dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; + + Node callee_node; + callee_node.bb = &bb; + callee_node.callstring = node.bb; + // node.state is default initialised (to bottom) + + nodes[std::make_tuple(&bb, node.bb)] = callee_node; + } + + nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; + nodes[callee_element].func_entry = callee_func; + changed = true; + } else { + AbstractState state_update{ callee_func, state_new, call }; + changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); + } + + //Getting the last block + llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); + auto end_element = std::make_tuple(end_block, node.bb); + + state_new.applyCallInst(inst, end_block, nodes[end_element].state); + + // If input parameters have changed, we want to interpret the function once again + // and reevaluate the nodes of possible callers. + if (changed) { + for (std::pair& i : nodes) { + if (std::get<0>(i.first) == node.bb and not i.second.update_scheduled) { + dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; + worklist.push_back(i.first); + i.second.update_scheduled = true; + } + } + + // Checks if the key of the callee functions entry node is already on the worklist, + // this is necessary for recursions. + if (not nodes[callee_element].update_scheduled) { + worklist.push_back(callee_element); + nodes[callee_element].update_scheduled = true; + + dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; + } else { + dbgs(3) << " Callee already on worklist, nothing to add...\n"; + } + } + } else { + if (state_new.checkOperandsForBottom(inst)) continue; + state_new.applyDefault(inst); + } + } + } + + // Merge the state back into the node + dbgs(3) << " Merging with stored state\n"; + bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); + + dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); + + // No changes, so no need to do anything else + if (not changed) continue; + + dbgs(2) << " State changed, notifying " << llvm::succ_size(node.bb) + << (llvm::succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); + + // Something changed and we will need to update the successors + for (llvm::BasicBlock const* succ_bb: llvm::successors(node.bb)) { + auto succ_key = std::make_tuple(succ_bb, node.callstring); + Node& succ = nodes[succ_key]; + if (not succ.update_scheduled) { + worklist.push_back(succ_key); + succ.update_scheduled = true; + + dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; + } + } + } + + if (!worklist.empty()) { + dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; + } + + // Output the final result + dbgs(0) << "\nFinal result:\n"; + for (std::pair i: nodes) { + dbgs(0) << _bb_key_to_str(i.first) << ":\n"; + i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); + } + +} + +} /* end of namespace pcpo */ + diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 5ad1b85..66a3483 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -95,11 +95,8 @@ NormalizedConjunction NormalizedConjunction::interpret( NormalizedConjunction NormalizedConjunction::refineBranch(llvm::CmpInst::Predicate pred, llvm::Value const& lhs, llvm::Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { - // TODO - - assert(false && "Not implemented"); - - return nullptr; + // Do nothing + return a; } @@ -134,12 +131,12 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti std::set E1, E2; auto mapToSeccond = [](std::pair p){ return p.second; }; - std::transform(lhs.equalaties.begin(), lhs.equalaties.end(), std::inserter(E1, E1.end()), mapToSeccond); - std::transform(rhs.equalaties.begin(), rhs.equalaties.end(), std::inserter(E2, E2.end()), mapToSeccond); + llvm::transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); + llvm::transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); auto mapToY = [](Equality eq){ return eq.y; }; - std::transform(E1.begin(), E1.end(), std::inserter(varsE1, varsE1.end()), mapToY); - std::transform(E2.begin(), E2.end(), std::inserter(varsE2, varsE2.end()), mapToY); + llvm::transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); + llvm::transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); std::set dX1, dX2; @@ -388,29 +385,24 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // [xi := ?]# E = Exists# xi in E NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); - + auto result = leastUpperBound(lhs, rhs);APInt(64,0); auto i = result.equalaties[&inst]; - if (i.x != &inst && i.a != APInt(64,0)) { + if (i.x != &inst && i.b != APInt(64,0)) { result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; } else { // find all equations using xi - std::map X; auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; - std::copy_if(result.equalaties.begin(), result.equalaties.end(), std::inserter(X, X.end()), predicate); - // pop first element - std::pair k = *X.begin(); - X.erase(X.begin()); - for (auto l: X) { - // FIXME: modify result instead of X which will get dropped later and not be used anymore - // use std::find_if iterator instead of copy if - l.second.a = APInt(64,1); - l.second.x = k.second.y; - l.second.b = l.second.b - k.second.b; + auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), predicate); + if (it != result.equalaties.end()) { + Equality k = (*it).second; + for (it = std::find_if(++it, result.equalaties.end(), predicate); + it != result.equalaties.end(); + it = std::find_if(++it, result.equalaties.end(), predicate)) { + auto& l = it->second; + result.equalaties[l.y] = {l.y, APInt(64,1), k.y, l.b - k.b}; + } + result.equalaties[k.y] = {k.y, APInt(64,1), k.y, APInt(64,0)}; } - k.second.a = APInt(64,1); - k.second.x = k.second.y; - k.second.b = APInt(64,0); result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; } return result; @@ -506,6 +498,47 @@ NormalizedConjunction NormalizedConjunction::Mul(llvm::Instruction const& inst, return result; } +// MARK: Utils + +bool NormalizedConjunction::isNormalized() const { + bool result = true; + for (auto eq: equalaties) { + if (eq.second.isConstant()) { + auto containsY = [&eq](std::pair pair){ return pair.second.x == eq.second.y; }; + result &= llvm::none_of(equalaties, containsY); + } else { + auto occursElseWhere = [&eq](std::pair pair){ return eq.second.y != pair.second.y && eq.second.y == pair.second.x; }; + result &= llvm::none_of(equalaties, occursElseWhere); + } + } + + auto jGreaterI = [](std::pair pair){ return pair.second.y > pair.second.x;}; + result &= llvm::all_of(equalaties, jGreaterI); + return result; +} + +// MARK: Operators + +bool NormalizedConjunction::operator==(NormalizedConjunction rhs) const { + return state == NORMAL + ? rhs.state == NORMAL and equalaties == rhs.equalaties + : state == rhs.state; +} + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NormalizedConjunction a) { + if (a.isBottom()) { + os << "[]"; + } else if(a.isTop()) { + os << "T"; + } else { + for (auto eq: a.equalaties) { + os << eq.first << " = " << eq.second.a << " * " << eq.second.x << " + " << eq.second.b; + } + } + return os; +} + + } diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index b2c0674..30c99b5 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -56,8 +56,12 @@ class NormalizedConjunction { static NormalizedConjunction merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b); // utils - bool isTop() const { return state == TOP; }; - bool isBottom() const { return state == BOTTOM; }; + bool isTop() const { return state == TOP; }; /* return equalities.empty() */ + bool isBottom() const { return state == BOTTOM; }; /* never? */ + bool isNormalized() const; + + bool operator==(NormalizedConjunction other) const; + bool operator!=(NormalizedConjunction other) const {return !(*this == other);} private: NormalizedConjunction(std::map equalaties); @@ -69,4 +73,6 @@ class NormalizedConjunction { static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); }; +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NormalizedConjunction a); + } -- GitLab From caac975d66d8e9c5d8ff5efbac40e77ac30bde12 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 00:23:42 +0100 Subject: [PATCH 008/142] using namespace llvm --- src/normalized_conjunction.cpp | 112 ++++++++++++++++----------------- 1 file changed, 54 insertions(+), 58 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 66a3483..068eacd 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -4,12 +4,12 @@ namespace pcpo { -using APInt = llvm::APInt; +using namespace llvm; // MARK: Initializers -NormalizedConjunction::NormalizedConjunction(llvm::Constant const& constant) { - if (llvm::ConstantInt const* c = llvm::dyn_cast(&constant)) { +NormalizedConjunction::NormalizedConjunction(Constant const& constant) { + if (ConstantInt const* c = dyn_cast(&constant)) { state = NORMAL; // Watch out for signed/unsigend APInts in future Equality eq = {&constant, APInt(64, 0), nullptr, c->getValue()}; @@ -19,7 +19,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Constant const& constant) { } } -NormalizedConjunction::NormalizedConjunction(std::map equalaties) { +NormalizedConjunction::NormalizedConjunction(std::map equalaties) { this->equalaties = equalaties; state = NORMAL; } @@ -27,21 +27,21 @@ NormalizedConjunction::NormalizedConjunction(std::map const& operands + Instruction const& inst, std::vector const& operands ) { // FIXME: maybe use nonDeterministic assignment here. if (operands.size() != 2) return NormalizedConjunction {true}; // We only deal with integer types - llvm::IntegerType const* type = llvm::dyn_cast(inst.getType()); + IntegerType const* type = dyn_cast(inst.getType()); // FIXME: maybe use nonDeterministic assignment here. if (not type) return NormalizedConjunction {true}; - type = llvm::dyn_cast(inst.getOperand(0)->getType()); + type = dyn_cast(inst.getOperand(0)->getType()); // FIXME: maybe use nonDeterministic assignment here. if (not type) return NormalizedConjunction {true}; - type = llvm::dyn_cast(inst.getOperand(1)->getType()); + type = dyn_cast(inst.getOperand(1)->getType()); // FIXME: maybe use nonDeterministic assignment here. if (not type) return NormalizedConjunction {true}; @@ -80,20 +80,20 @@ NormalizedConjunction NormalizedConjunction::interpret( // Those will never occur due to SSA Form. switch (inst.getOpcode()) { - case llvm::Instruction::Add: + case Instruction::Add: return NormalizedConjunction::Add(inst, a, b); - case llvm::Instruction::Sub: + case Instruction::Sub: return NormalizedConjunction::Sub(inst, a, b); - case llvm::Instruction::Mul: + case Instruction::Mul: return NormalizedConjunction::Mul(inst, a, b); - case llvm::Instruction::SDiv: - case llvm::Instruction::UDiv: + case Instruction::SDiv: + case Instruction::UDiv: default: return nonDeterminsticAssignment(inst, a, b); } } -NormalizedConjunction NormalizedConjunction::refineBranch(llvm::CmpInst::Predicate pred, llvm::Value const& lhs, llvm::Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { +NormalizedConjunction NormalizedConjunction::refineBranch(CmpInst::Predicate pred, Value const& lhs, Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { // Do nothing return a; @@ -127,19 +127,19 @@ NormalizedConjunction NormalizedConjunction::merge(Merge_op::Type op, Normalized NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjunction lhs, NormalizedConjunction rhs) { // set of all occuring variables in E1 and E2 - std::set vars, varsE1, varsE2; + std::set vars, varsE1, varsE2; std::set E1, E2; - auto mapToSeccond = [](std::pair p){ return p.second; }; - llvm::transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); - llvm::transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); + auto mapToSeccond = [](std::pair p){ return p.second; }; + transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); + transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); auto mapToY = [](Equality eq){ return eq.y; }; - llvm::transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); - llvm::transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); + transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); + transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); - std::set dX1, dX2; + std::set dX1, dX2; std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); @@ -164,7 +164,7 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // Remove trivial equalities std::set filteredX0; auto filterTrivialEqualaties = [](Equality eq){return eq.y != eq.x;}; - std::copy_if(X0.begin(), X0.end(), std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); + copy_if(X0, std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); X0 = filteredX0; } @@ -373,10 +373,10 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti leastUpperBound.insert(X3.begin(), X3.end()); leastUpperBound.insert(X4.begin(), X4.end()); - std::map result; + std::map result; auto addMapping = [](Equality eq){ return std::make_pair(eq.y,eq); }; - std::transform(leastUpperBound.begin(), leastUpperBound.end(), std::inserter(result, result.end()), addMapping); + transform(leastUpperBound, std::inserter(result, result.end()), addMapping); return NormalizedConjunction(result); } @@ -384,14 +384,13 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // MARK: Abstract Operations // [xi := ?]# E = Exists# xi in E -NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs);APInt(64,0); auto i = result.equalaties[&inst]; if (i.x != &inst && i.b != APInt(64,0)) { result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; } else { // find all equations using xi - auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; + auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), predicate); if (it != result.equalaties.end()) { Equality k = (*it).second; @@ -409,21 +408,21 @@ NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(llvm::Ins } // TODO: [xi := xj + x] -NormalizedConjunction NormalizedConjunction::Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { +NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - llvm::Value const* j; - llvm::ConstantInt const* b; + Value const* j; + ConstantInt const* b; - if (llvm::isa(op1) && llvm::isa(op2)) { - b = llvm::dyn_cast(op1); + if (isa(op1) && isa(op2)) { + b = dyn_cast(op1); j = op2; - } else if (llvm::isa(op2) && llvm::isa(op1)) { - b = llvm::dyn_cast(op2); + } else if (isa(op2) && isa(op1)) { + b = dyn_cast(op2); j = op1; } else { assert(false && "One operand has to be constant"); @@ -435,12 +434,11 @@ NormalizedConjunction NormalizedConjunction::Add(llvm::Instruction const& inst, result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, i.b + jj.b};; } else { // Filter results - auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; - for (auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), pred); - it != result.equalaties.end(); - it = std::find_if(++it, result.equalaties.end(), pred)) { - auto& k = it->second; result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b - i.b - jj.b}; + auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; + + for (auto kpair: make_filter_range(result.equalaties, pred)) { + auto k = kpair.second; } result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, -i.b - jj.b}; } @@ -449,21 +447,21 @@ NormalizedConjunction NormalizedConjunction::Add(llvm::Instruction const& inst, } // TODO: [xi := xj - x] -NormalizedConjunction NormalizedConjunction::Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { +NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - llvm::Value const* j; - llvm::ConstantInt const* b; + Value const* j; + ConstantInt const* b; - if (llvm::isa(op1) && llvm::isa(op2)) { - b = llvm::dyn_cast(op1); + if (isa(op1) && isa(op2)) { + b = dyn_cast(op1); j = op2; - } else if (llvm::isa(op2) && llvm::isa(op1)) { - b = llvm::dyn_cast(op2); + } else if (isa(op2) && isa(op1)) { + b = dyn_cast(op2); j = op1; } else { assert(false && "One operand has to be constant"); @@ -475,12 +473,10 @@ NormalizedConjunction NormalizedConjunction::Sub(llvm::Instruction const& inst, result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, jj.b - i.b};; } else { // Filter results - auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; - for (auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), pred); - it != result.equalaties.end(); - it = std::find_if(++it, result.equalaties.end(), pred)) { - auto& k = it->second; result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b + i.b - jj.b}; + auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; + for (auto kpair: make_filter_range(result.equalaties, pred)) { + auto& k = kpair.second; } result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, i.b - jj.b}; } @@ -489,7 +485,7 @@ NormalizedConjunction NormalizedConjunction::Sub(llvm::Instruction const& inst, } // [xi := a * xj] -NormalizedConjunction NormalizedConjunction::Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { +NormalizedConjunction NormalizedConjunction::Mul(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); // TODO @@ -504,16 +500,16 @@ bool NormalizedConjunction::isNormalized() const { bool result = true; for (auto eq: equalaties) { if (eq.second.isConstant()) { - auto containsY = [&eq](std::pair pair){ return pair.second.x == eq.second.y; }; - result &= llvm::none_of(equalaties, containsY); + auto containsY = [&eq](std::pair pair){ return pair.second.x == eq.second.y; }; + result &= none_of(equalaties, containsY); } else { - auto occursElseWhere = [&eq](std::pair pair){ return eq.second.y != pair.second.y && eq.second.y == pair.second.x; }; - result &= llvm::none_of(equalaties, occursElseWhere); + auto occursElseWhere = [&eq](std::pair pair){ return eq.second.y != pair.second.y && eq.second.y == pair.second.x; }; + result &= none_of(equalaties, occursElseWhere); } } - auto jGreaterI = [](std::pair pair){ return pair.second.y > pair.second.x;}; - result &= llvm::all_of(equalaties, jGreaterI); + auto jGreaterI = [](std::pair pair){ return pair.second.y > pair.second.x;}; + result &= all_of(equalaties, jGreaterI); return result; } @@ -525,7 +521,7 @@ bool NormalizedConjunction::operator==(NormalizedConjunction rhs) const { : state == rhs.state; } -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NormalizedConjunction a) { +raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { if (a.isBottom()) { os << "[]"; } else if(a.isTop()) { -- GitLab From 53dbddb856a76716c7af129dab9fc66f485b8b39 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 00:24:27 +0100 Subject: [PATCH 009/142] fix for loop --- src/normalized_conjunction.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 068eacd..6745cda 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -174,9 +174,10 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti std::set> differentConstants; assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - - for (auto eq1: E1) { - for (auto eq2: E2) { + + for (itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { + auto eq1 = *itE1; + auto eq2 = *itE2; assert(eq1.y == eq2.y && "left hand side of equations should be the same"); if (eq1.isConstant() && eq2.isConstant() && eq1.b != eq2.b) { differentConstants.insert({eq1,eq2}); -- GitLab From ec6b70e1cdc9072c7b5ebe6abf74f0db4639e8b5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 00:24:36 +0100 Subject: [PATCH 010/142] int vs APInt --- src/normalized_conjunction.cpp | 89 ++++++++++++++++++---------------- src/normalized_conjunction.h | 5 +- 2 files changed, 51 insertions(+), 43 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 6745cda..6e5cb4f 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -12,11 +12,12 @@ NormalizedConjunction::NormalizedConjunction(Constant const& constant) { if (ConstantInt const* c = dyn_cast(&constant)) { state = NORMAL; // Watch out for signed/unsigend APInts in future - Equality eq = {&constant, APInt(64, 0), nullptr, c->getValue()}; + Equality eq = {&constant, 0, nullptr, c->getValue().getSExtValue()}; equalaties = {{&constant,eq}}; } else { state = TOP; } + } NormalizedConjunction::NormalizedConjunction(std::map equalaties) { @@ -146,13 +147,13 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // extend E1 by trivial equalities for (auto d: dX1) { - Equality eq = {d, APInt(64,0), d, APInt(64,0)}; + Equality eq = {d, 1, d, 0}; E1.insert(eq); } // extend E2 by trivial equalities for (auto d: dX2) { - Equality eq = {d, APInt(64,0), d, APInt(64,0)}; + Equality eq = {d, 1, d, 0}; E2.insert(eq); } @@ -183,22 +184,24 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti differentConstants.insert({eq1,eq2}); } } - } - - // pop first element - std::pair h = *differentConstants.begin(); - differentConstants.erase(differentConstants.begin()); - - for (auto i: differentConstants) { - // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) - // y = m * x + b - auto y = i.first.y; - APInt m = (h.second.b - h.first.b).sdiv((i.second.b - i.first.b)); - auto x = h.first.x; - APInt b = -m * h.first.b + i.first.b; - Equality eq = {y, m, x, b}; - X1.insert(eq); - } + + if (!differentConstants.empty()) { + // pop first element + std::pair h = *differentConstants.begin(); + differentConstants.erase(differentConstants.begin()); + + for (auto i: differentConstants) { + // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) + // y = m * x + b + auto y = i.first.y; + int64_t m = (h.second.b - h.first.b) / ((i.second.b - i.first.b)); + auto x = h.first.x; + int64_t b = -m * h.first.b + i.first.b; + Equality eq = {y, m, x, b}; + X1.insert(eq); + } + } + } // X2 / E'2: set of variables where the right hand side of E1 is constant but the rhs of E2 contains a variable. std::set X2; @@ -224,11 +227,12 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti std::pair ipair = *iterator; equivalenceClass.insert(ipair); iterator++; + // FIXME: Make sure this doesnt casue any trouble! for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { std::pair jpair = *tempit; bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b).sdiv(ipair.second.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.second.a); + bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); if (condition1 && condition2) { equivalenceClass.insert(jpair); differentConstants.erase(tempit); @@ -244,9 +248,9 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) auto y = i.first.y; - auto m = i.second.a.sdiv(h.second.b); + auto m = i.second.a / h.second.b; auto x = h.first.y; - auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + auto b = i.second.b - (i.second.a * h.second.b) / h.second.a; Equality eq = {y, m, x, b}; X2.insert(eq); } @@ -281,7 +285,7 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { std::pair jpair = *tempit; bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b).sdiv(ipair.second.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.second.a); + bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); if (condition1 && condition2) { equivalenceClass.insert(jpair); differentConstants.erase(tempit); @@ -297,9 +301,9 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) auto y = i.first.y; - auto m = i.second.a.sdiv(h.second.b); + auto m = i.second.a / (h.second.b); auto x = h.first.y; - auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); Equality eq = {y, m, x, b}; X3.insert(eq); } @@ -336,8 +340,8 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { std::pair jpair = *tempit; bool condition1 = ipair.first.x == jpair.first.x && ipair.second.x == jpair.second.x; - bool condition2 = ipair.second.a.sdiv(ipair.first.a) == jpair.second.a.sdiv(jpair.first.a); - bool condition3 = (ipair.first.b - ipair.second.b).sdiv(ipair.first.a) == (jpair.first.b - jpair.second.b).sdiv(jpair.first.a); + bool condition2 = ipair.second.a / (ipair.first.a) == jpair.second.a / (jpair.first.a); + bool condition3 = (ipair.first.b - ipair.second.b) / (ipair.first.a) == (jpair.first.b - jpair.second.b) / (jpair.first.a); if (condition1 && condition2 && condition3) { equivalenceClass.insert(jpair); differentConstants.erase(tempit); @@ -353,9 +357,9 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) auto y = i.first.y; - auto m = i.second.a.sdiv(h.second.b); + auto m = i.second.a / (h.second.b); auto x = h.first.y; - auto b = i.second.b - (i.second.a * h.second.b).sdiv(h.second.a); + auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); Equality eq = {y, m, x, b}; X3.insert(eq); } @@ -385,10 +389,11 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // MARK: Abstract Operations // [xi := ?]# E = Exists# xi in E - auto result = leastUpperBound(lhs, rhs);APInt(64,0); +NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; - if (i.x != &inst && i.b != APInt(64,0)) { - result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; + if (i.x != &inst && i.b != 0) { + result.equalaties[&inst] = {&inst, 1, &inst, 0}; } else { // find all equations using xi auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; @@ -399,11 +404,11 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti it != result.equalaties.end(); it = std::find_if(++it, result.equalaties.end(), predicate)) { auto& l = it->second; - result.equalaties[l.y] = {l.y, APInt(64,1), k.y, l.b - k.b}; + result.equalaties[l.y] = {l.y, 1, k.y, l.b - k.b}; } - result.equalaties[k.y] = {k.y, APInt(64,1), k.y, APInt(64,0)}; + result.equalaties[k.y] = {k.y, 1, k.y, 0}; } - result.equalaties[&inst] = {&inst, APInt(64,1), &inst, APInt(64,0)}; + result.equalaties[&inst] = {&inst, 1, &inst, 0}; } return result; } @@ -432,16 +437,18 @@ NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, Norma auto jj = result.equalaties[j]; if (i > jj) { - result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, i.b + jj.b};; + result.equalaties[i.y] = {i.y, 1, jj.x, i.b + jj.b};; } else { // Filter results - result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b - i.b - jj.b}; auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; for (auto kpair: make_filter_range(result.equalaties, pred)) { auto k = kpair.second; + result.equalaties[k.y] = {k.y, 1, i.y, k.b - i.b - jj.b}; } - result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, -i.b - jj.b}; + + auto bitWidht = std::max(i.b, jj.b); + result.equalaties[jj.y] = {jj.y, 1, i.y, -i.b - jj.b}; } return result; @@ -471,15 +478,15 @@ NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, Normal auto jj = result.equalaties[j]; if (i > jj) { - result.equalaties[i.y] = {i.y, APInt(64,1), jj.x, jj.b - i.b};; + result.equalaties[i.y] = {i.y, 1, jj.x, jj.b - i.b};; } else { // Filter results - result.equalaties[k.y] = {k.y, APInt(64,1), i.y, k.b + i.b - jj.b}; auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; for (auto kpair: make_filter_range(result.equalaties, pred)) { auto& k = kpair.second; + result.equalaties[k.y] = {k.y, 1, i.y, k.b + i.b - jj.b}; } - result.equalaties[jj.y] = {jj.y, APInt(64,1), i.y, i.b - jj.b}; + result.equalaties[jj.y] = {jj.y, 1, i.y, i.b - jj.b}; } return result; diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 30c99b5..08fdcc9 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -23,9 +23,10 @@ class NormalizedConjunction { struct Equality { // y = a * x + b llvm::Value const* y; - llvm::APInt a; + // APInt would be nicer, but our anlysis doesnt care about bit width + int64_t a; llvm::Value const* x; - llvm::APInt b; + int64_t b; inline bool operator<(Equality const& rhs) const { return y < rhs.y; -- GitLab From 5d8df6e5948a7faf70338c772a129ec87d33db8a Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 01:10:27 +0100 Subject: [PATCH 011/142] refactor --- src/normalized_conjunction.cpp | 337 +++++++++++++++++---------------- src/normalized_conjunction.h | 8 + 2 files changed, 180 insertions(+), 165 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 6e5cb4f..90becb4 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -121,95 +121,62 @@ NormalizedConjunction NormalizedConjunction::merge(Merge_op::Type op, Normalized } } - // TODO: getter / setter for equlaties to keep equalaties normalized +// MARK: Helpers - -// MARK: Lattice Operations - -NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjunction lhs, NormalizedConjunction rhs) { - // set of all occuring variables in E1 and E2 - std::set vars, varsE1, varsE2; - std::set E1, E2; - - auto mapToSeccond = [](std::pair p){ return p.second; }; - transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); - transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); - - auto mapToY = [](Equality eq){ return eq.y; }; - transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); - transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); - std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); - - std::set dX1, dX2; - - std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); - std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); - - // extend E1 by trivial equalities - for (auto d: dX1) { - Equality eq = {d, 1, d, 0}; - E1.insert(eq); - } - - // extend E2 by trivial equalities - for (auto d: dX2) { - Equality eq = {d, 1, d, 0}; - E2.insert(eq); - } - - // XO / E'0: set of variables where the right hand side in E1 and E2 coincide +/// XO / E'0: set of variables where the right hand side in E1 and E2 coincide +std::set NormalizedConjunction::computeX0(std::set const& E1, std::set const& E2) { std::set X0; - { std::set_intersection(E1.begin(), E1.end(), E2.begin(), E2.end(), std::inserter(X0, X0.end())); - // Remove trivial equalities std::set filteredX0; auto filterTrivialEqualaties = [](Equality eq){return eq.y != eq.x;}; copy_if(X0, std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); - X0 = filteredX0; - } - // X1 / E'1: set of variables where the right hand side is constant but does not coincide in E1 and E2 + return filteredX0; +} + +/// X1 / E'1: set of variables where the right hand side is constant but does not coincide in E1 and E2 +std::set NormalizedConjunction::computeX1(std::set const& E1, std::set const& E2) { std::set X1; - { std::set> differentConstants; assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - - for (itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { - auto eq1 = *itE1; - auto eq2 = *itE2; - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (eq1.isConstant() && eq2.isConstant() && eq1.b != eq2.b) { - differentConstants.insert({eq1,eq2}); - } + + for (auto itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { + auto eq1 = *itE1; + auto eq2 = *itE2; + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (eq1.isConstant() && eq2.isConstant() && eq1.b != eq2.b) { + differentConstants.insert({eq1,eq2}); } + } + + if (!differentConstants.empty()) { + // pop first element + std::pair h = *differentConstants.begin(); + differentConstants.erase(differentConstants.begin()); - if (!differentConstants.empty()) { - // pop first element - std::pair h = *differentConstants.begin(); - differentConstants.erase(differentConstants.begin()); - - for (auto i: differentConstants) { - // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) - // y = m * x + b - auto y = i.first.y; - int64_t m = (h.second.b - h.first.b) / ((i.second.b - i.first.b)); - auto x = h.first.x; - int64_t b = -m * h.first.b + i.first.b; - Equality eq = {y, m, x, b}; - X1.insert(eq); - } + for (auto i: differentConstants) { + // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) + // y = a * x + b + auto y = i.first.y; + int64_t a = (h.second.b - h.first.b) / ((i.second.b - i.first.b)); + auto x = h.first.x; + int64_t b = -a * h.first.b + i.first.b; + Equality eq = {y, a, x, b}; + X1.insert(eq); } - } - // X2 / E'2: set of variables where the right hand side of E1 is constant but the rhs of E2 contains a variable. + return X1; +} + +/// X2 / E'2: set of variables where the right hand side of E1 is constant but the rhs of E2 contains a variable. +std::set NormalizedConjunction::computeX2(std::set const& E1, std::set const& E2) { std::set X2; - { std::set> differentConstants; - + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - + for (auto eq1: E1) { for (auto eq2: E2) { assert(eq1.y == eq2.y && "left hand side of equations should be the same"); @@ -221,29 +188,29 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti std::vector>> Pi2; - // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { - std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; - - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { - std::pair jpair = *tempit; - bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); - if (condition1 && condition2) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit); - } + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.second.x == jpair.second.x; + bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); + if (condition1 && condition2) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); } - Pi2.push_back(equivalenceClass); + } + Pi2.push_back(equivalenceClass); } - + // form equaltites for partitions in Pi2 for (auto q: Pi2) { - std::pair h = *q.begin(); + auto h = *q.begin(); q.erase(q.begin()); for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) @@ -256,14 +223,16 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti } } - } - // X3 / E'3: set of variables where the right hand side of E1 contains a variable and E2 contains a constant. + return X2; +} + +/// X3 / E'3: set of variables where the right hand side of E1 contains a variable and E2 contains a constant. +std::set NormalizedConjunction::computeX3(std::set const& E1, std::set const E2) { std::set X3; - { std::set> differentConstants; - + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - + for (auto eq1: E1) { for (auto eq2: E2) { assert(eq1.y == eq2.y && "left hand side of equations should be the same"); @@ -275,28 +244,28 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti std::vector>> Pi3; - // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { - std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { - std::pair jpair = *tempit; - bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); - if (condition1 && condition2) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit); - } + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.second.x == jpair.second.x; + bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); + if (condition1 && condition2) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); } - Pi3.push_back(equivalenceClass); + } + Pi3.push_back(equivalenceClass); } - + // form equaltites for partitions in Pi3 for (auto q: Pi3) { - std::pair h = *q.begin(); + auto h = *q.begin(); q.erase(q.begin()); for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) @@ -309,67 +278,108 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti } } - } + return X3; +} + +std::set NormalizedConjunction::computeX4(std::set const& E1, std::set const& E2) { + std::set X4; + std::set> differentConstants; + assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - // X4 / E'4: - std::set X4; - { - std::set> differentConstants; - - assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - - for (auto eq1: E1) { - for (auto eq2: E2) { - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (!eq1.isConstant() && !eq2.isConstant()) { - differentConstants.insert({eq1,eq2}); - } + for (auto eq1: E1) { + for (auto eq2: E2) { + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (!eq1.isConstant() && !eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); } } - - std::vector>> Pi4; - - // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { - std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { - std::pair jpair = *tempit; - bool condition1 = ipair.first.x == jpair.first.x && ipair.second.x == jpair.second.x; - bool condition2 = ipair.second.a / (ipair.first.a) == jpair.second.a / (jpair.first.a); - bool condition3 = (ipair.first.b - ipair.second.b) / (ipair.first.a) == (jpair.first.b - jpair.second.b) / (jpair.first.a); - if (condition1 && condition2 && condition3) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit); - } + } + + std::vector>> Pi4; + + // partition differentConstants + for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + std::set> equivalenceClass; + std::pair ipair = *iterator; + equivalenceClass.insert(ipair); + iterator++; + // FIXME: Make sure this doesnt casue any trouble! + for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + std::pair jpair = *tempit; + bool condition1 = ipair.first.x == jpair.first.x && ipair.second.x == jpair.second.x; + bool condition2 = ipair.second.a / (ipair.first.a) == jpair.second.a / (jpair.first.a); + bool condition3 = (ipair.first.b - ipair.second.b) / (ipair.first.a) == (jpair.first.b - jpair.second.b) / (jpair.first.a); + if (condition1 && condition2 && condition3) { + equivalenceClass.insert(jpair); + differentConstants.erase(tempit); } - Pi4.push_back(equivalenceClass); } - - // form equaltites for partitions in Pi4 - for (auto q: Pi4) { - std::pair h = *q.begin(); - q.erase(q.begin()); - for (auto i: q) { - // xi = ai/ah * xh + ( bi - (ai * bh) / ah) - auto y = i.first.y; - auto m = i.second.a / (h.second.b); - auto x = h.first.y; - auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); - Equality eq = {y, m, x, b}; - X3.insert(eq); - } + Pi4.push_back(equivalenceClass); + } + + // form equaltites for partitions in Pi4 + for (auto q: Pi4) { + auto h = *q.begin(); + q.erase(q.begin()); + for (auto i: q) { + // xi = ai/ah * xh + ( bi - (ai * bh) / ah) + auto y = i.first.y; + auto m = i.second.a / (h.second.b); + auto x = h.first.y; + auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); + Equality eq = {y, m, x, b}; + X4.insert(eq); } + } + + return X4; +} + +// MARK: Lattice Operations + +NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjunction lhs, NormalizedConjunction rhs) { + // set of all occuring variables in E1 and E2 + std::set vars, varsE1, varsE2; + std::set E1, E2; + + auto mapToSeccond = [](std::pair p){ return p.second; }; + transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); + transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); + + auto mapToY = [](Equality eq){ return eq.y; }; + transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); + transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); + std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); + + std::set dX1, dX2; + + std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); + std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); - + // extend E1 by trivial equalities + for (auto d: dX1) { + Equality eq = {d, 1, d, 0}; + E1.insert(eq); } - // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 + // extend E2 by trivial equalities + for (auto d: dX2) { + Equality eq = {d, 1, d, 0}; + E2.insert(eq); + } + // XO / E'0: set of variables where the right hand side in E1 and E2 coincide + std::set X0 = computeX0(E1, E2); + + + // FIXME: function computeX2(a,b) == computeX3(b,a) remove one of them + std::set X1 = computeX1(E1, E2); + std::set X2 = computeX2(E1, E2); + std::set X3 = computeX3(E1, E2); + std::set X4 = computeX4(E1, E2); + + // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 std::set leastUpperBound; leastUpperBound.insert(X0.begin(), X0.end()); @@ -542,7 +552,4 @@ raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { return os; } - } - - diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 08fdcc9..249e6ea 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -9,6 +9,7 @@ #include "global.h" #include +#include namespace pcpo { @@ -72,6 +73,13 @@ class NormalizedConjunction { static NormalizedConjunction Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + + // Helpers + static std::set computeX0(std::set const& E1, std::set const& E2); + static std::set computeX1(std::set const& E1, std::set const& E2); + static std::set computeX2(std::set const& E1, std::set const& E2); + static std::set computeX3(std::set const& E1, std::set const E2); + static std::set computeX4(std::set const& E1, std::set const& E2); }; llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NormalizedConjunction a); -- GitLab From ff9d47e377f3a240ae3ff5ce0e1250df449928ed Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 01:20:25 +0100 Subject: [PATCH 012/142] fixed iterators --- src/normalized_conjunction.cpp | 39 ++++++++++++++++------------------ 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 90becb4..fa4f966 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -177,12 +177,12 @@ std::set NormalizedConjunction::computeX2(std:: assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - for (auto eq1: E1) { - for (auto eq2: E2) { - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (eq1.isConstant() && !eq2.isConstant()) { - differentConstants.insert({eq1,eq2}); - } + for (auto itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { + auto eq1 = *itE1; + auto eq2 = *itE2; + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (eq1.isConstant() && !eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); } } @@ -222,7 +222,6 @@ std::set NormalizedConjunction::computeX2(std:: X2.insert(eq); } } - return X2; } @@ -233,12 +232,12 @@ std::set NormalizedConjunction::computeX3(std:: assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - for (auto eq1: E1) { - for (auto eq2: E2) { - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (!eq1.isConstant() && eq2.isConstant()) { - differentConstants.insert({eq1,eq2}); - } + for (auto itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { + auto eq1 = *itE1; + auto eq2 = *itE2; + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (!eq1.isConstant() && eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); } } @@ -277,7 +276,6 @@ std::set NormalizedConjunction::computeX3(std:: X3.insert(eq); } } - return X3; } @@ -287,12 +285,12 @@ std::set NormalizedConjunction::computeX4(std:: assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - for (auto eq1: E1) { - for (auto eq2: E2) { - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (!eq1.isConstant() && !eq2.isConstant()) { - differentConstants.insert({eq1,eq2}); - } + for (auto itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { + auto eq1 = *itE1; + auto eq2 = *itE2; + assert(eq1.y == eq2.y && "left hand side of equations should be the same"); + if (!eq1.isConstant() && !eq2.isConstant()) { + differentConstants.insert({eq1,eq2}); } } @@ -332,7 +330,6 @@ std::set NormalizedConjunction::computeX4(std:: X4.insert(eq); } } - return X4; } -- GitLab From 0d65d54920ad4c9b8bdfd8c3de267b70af43f149 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 8 Jan 2020 01:24:34 +0100 Subject: [PATCH 013/142] cleanup --- src/normalized_conjunction.cpp | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index fa4f966..18c6885 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -17,7 +17,6 @@ NormalizedConjunction::NormalizedConjunction(Constant const& constant) { } else { state = TOP; } - } NormalizedConjunction::NormalizedConjunction(std::map equalaties) { @@ -95,7 +94,6 @@ NormalizedConjunction NormalizedConjunction::interpret( } NormalizedConjunction NormalizedConjunction::refineBranch(CmpInst::Predicate pred, Value const& lhs, Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { - // Do nothing return a; } @@ -368,8 +366,6 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // XO / E'0: set of variables where the right hand side in E1 and E2 coincide std::set X0 = computeX0(E1, E2); - - // FIXME: function computeX2(a,b) == computeX3(b,a) remove one of them std::set X1 = computeX1(E1, E2); std::set X2 = computeX2(E1, E2); @@ -399,6 +395,7 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; + if (i.x != &inst && i.b != 0) { result.equalaties[&inst] = {&inst, 1, &inst, 0}; } else { @@ -424,10 +421,8 @@ NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instructi NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; - auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - Value const* j; ConstantInt const* b; @@ -453,8 +448,6 @@ NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, Norma auto k = kpair.second; result.equalaties[k.y] = {k.y, 1, i.y, k.b - i.b - jj.b}; } - - auto bitWidht = std::max(i.b, jj.b); result.equalaties[jj.y] = {jj.y, 1, i.y, -i.b - jj.b}; } @@ -465,10 +458,8 @@ NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, Norma NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; - auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - Value const* j; ConstantInt const* b; @@ -495,7 +486,6 @@ NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, Normal } result.equalaties[jj.y] = {jj.y, 1, i.y, i.b - jj.b}; } - return result; } -- GitLab From ff9d4ff829c9002ad3f7b7b1451ec7a68bbd3718 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 12 Jan 2020 02:36:20 +0100 Subject: [PATCH 014/142] fixed some stuff --- src/normalized_conjunction.cpp | 133 +++++++++++++++++++++++++-------- src/normalized_conjunction.h | 6 ++ 2 files changed, 107 insertions(+), 32 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 18c6885..579ac3b 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -12,7 +12,7 @@ NormalizedConjunction::NormalizedConjunction(Constant const& constant) { if (ConstantInt const* c = dyn_cast(&constant)) { state = NORMAL; // Watch out for signed/unsigend APInts in future - Equality eq = {&constant, 0, nullptr, c->getValue().getSExtValue()}; + Equality eq = {&constant, 1, nullptr, c->getValue().getSExtValue()}; equalaties = {{&constant,eq}}; } else { state = TOP; @@ -248,13 +248,15 @@ std::set NormalizedConjunction::computeX3(std:: equivalenceClass.insert(ipair); iterator++; // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { + for (auto tempit = iterator; tempit != differentConstants.end();) { std::pair jpair = *tempit; bool condition1 = ipair.second.x == jpair.second.x; bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); if (condition1 && condition2) { equivalenceClass.insert(jpair); - differentConstants.erase(tempit); + differentConstants.erase(tempit++); + } else { + tempit++; } } Pi3.push_back(equivalenceClass); @@ -389,13 +391,13 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti return NormalizedConjunction(result); } -// MARK: Abstract Operations +// MARK: Abstract Assignments // [xi := ?]# E = Exists# xi in E NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto i = result.equalaties[&inst]; - + if (i.x != &inst && i.b != 0) { result.equalaties[&inst] = {&inst, 1, &inst, 0}; } else { @@ -417,41 +419,83 @@ NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instructi return result; } -// TODO: [xi := xj + x] +/// [xi := ?] +NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(NormalizedConjunction E, Value const* xi) { + assert(xi != nullptr && "xi cannot be NULL"); + auto i = E.equalaties[xi]; + + if (xi != i.x && i.b != 0) { + E.equalaties[xi] = {xi, 1, xi, 0}; + } else { + // find all equations using xi + auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; + auto it = std::find_if(E.equalaties.begin(), E.equalaties.end(), predicate); + if (it != E.equalaties.end()) { + Equality k = (*it).second; + for (it = std::find_if(++it, E.equalaties.end(), predicate); + it != E.equalaties.end(); + it = std::find_if(++it, E.equalaties.end(), predicate)) { + auto& l = it->second; + E.equalaties[l.y] = {l.y, 1, k.y, l.b - k.b}; + } + E.equalaties[k.y] = {k.y, 1, k.y, 0}; + } + E.equalaties[xi] = {xi, 1, xi, 0}; + } + return E; +} + +/// [xi := a * xj + b] +NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunction E, Value const* xi, int64_t a, Value const* xj, int64_t b) { + assert(xi != nullptr && "xi cannot be NULL"); + + // make sure xj exists + auto xjS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].x : nullptr; + auto bS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].b : 0; + + if (xi > xjS) { + E.equalaties[xi] = {xi, a, xjS, bS + b}; + return E; + } else { + auto pred = [&xjS](std::pair p){ return p.second.x == xjS && p.second.y != xjS; }; + for (auto xk: make_filter_range(E.equalaties, pred)) { + E.equalaties[xk.second.y] = {xk.second.y, a, xi, xk.second.b - b - bS}; + } + E.equalaties[xjS] = {xjS, a, xi, -b - bS}; + } + return E; +} + +// MARK: Abstract Operations + +// [xi := xj + c] +// [xi := xj + xk] +// [xi := cj + ck] NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); - auto i = result.equalaties[&inst]; auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - Value const* j; - ConstantInt const* b; if (isa(op1) && isa(op2)) { - b = dyn_cast(op1); - j = op2; + auto b = dyn_cast(op1); + return linearAssignment(result, &inst, 1, op2, b->getSExtValue()); } else if (isa(op2) && isa(op1)) { - b = dyn_cast(op2); - j = op1; - } else { - assert(false && "One operand has to be constant"); - } - - auto jj = result.equalaties[j]; - - if (i > jj) { - result.equalaties[i.y] = {i.y, 1, jj.x, i.b + jj.b};; - } else { - // Filter results - auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; - - for (auto kpair: make_filter_range(result.equalaties, pred)) { - auto k = kpair.second; - result.equalaties[k.y] = {k.y, 1, i.y, k.b - i.b - jj.b}; + auto b = dyn_cast(op2); + return linearAssignment(result, &inst, 1, op1, b->getSExtValue()); + } else if (isa(op1) && isa(op2)) { + if (result.equalaties[op1].isConstant()) { + return linearAssignment(result, &inst, 1, op2, result.equalaties[op1].b); + } else if (result.equalaties[op2].isConstant()) { + return linearAssignment(result, &inst, 1, op1, result.equalaties[op2].b); + } else { + return nonDeterminsticAssignment(inst, lhs, rhs); } - result.equalaties[jj.y] = {jj.y, 1, i.y, -i.b - jj.b}; + } else if (isa(op1) && (isa(op2))) { + return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b + result.equalaties[op2].b); + } else { + assert(false); + return nonDeterminsticAssignment(inst, lhs, rhs); } - - return result; } // TODO: [xi := xj - x] @@ -533,7 +577,32 @@ raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { os << "T"; } else { for (auto eq: a.equalaties) { - os << eq.first << " = " << eq.second.a << " * " << eq.second.x << " + " << eq.second.b; + // FIXME: Hanlde nullptr + + if (eq.second.y != nullptr && eq.second.y->hasName()) { + os << eq.second.y->getName() << " = "; + } else if (eq.second.y != nullptr) { + os << eq.second.y << " = "; + } else { + os << " = "; + } + + if (eq.second.x != nullptr) { + if (eq.second.x->hasName()) { + os << eq.second.a << " * " << eq.second.x->getName(); + } else { + os << eq.second.a << " * " << eq.second.x; + } + if (eq.second.b > 0) { + os << " + " << eq.second.b; + } else if (eq.second.b < 0) { + os << eq.second.b; + } + } else { + os << eq.second.b; + } + + os << "\n"; } } return os; diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 249e6ea..be08a85 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -62,6 +62,7 @@ class NormalizedConjunction { bool isBottom() const { return state == BOTTOM; }; /* never? */ bool isNormalized() const; + // TODO: [] operator ? bool operator==(NormalizedConjunction other) const; bool operator!=(NormalizedConjunction other) const {return !(*this == other);} @@ -69,7 +70,12 @@ class NormalizedConjunction { NormalizedConjunction(std::map equalaties); static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); + + // Assignments static NormalizedConjunction nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + static NormalizedConjunction linearAssignment(NormalizedConjunction E, llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); + static NormalizedConjunction nonDeterminsticAssignment(NormalizedConjunction E, llvm::Value const* xi); + static NormalizedConjunction Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); -- GitLab From 2848ddd78e6a91cc50223ea1012713c89a3861b8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 12 Jan 2020 02:36:33 +0100 Subject: [PATCH 015/142] Improved build system --- CMakeLists.txt | 145 ++++++++++++++++++++++++++++++---- test/simple_interval_test.cpp | 21 +++-- 2 files changed, 140 insertions(+), 26 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index cbb028c..6864a73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,18 +1,102 @@ -# If we don't need RTTI or EH, there's no reason to export anything -# from the hello plugin. -#if( NOT LLVM_REQUIRES_RTTI ) -# if( NOT LLVM_REQUIRES_EH ) -# set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Hello.exports) -# endif() -#endif() - -if(WIN32 OR CYGWIN) - set(LLVM_LINK_COMPONENTS Core Support) +cmake_minimum_required(VERSION 3.4.3) + +project(PAIN + VERSION 1.0.0 + LANGUAGES C CXX +) + +set(CMAKE_CXX_STANDARD 17) + +set (PROJECT_DESCRIPTION "Implements an LLVM analysis pass using abstract interpretation.") +set (PROJECT_HOMEPAGE_URL "https://versioncontrolseidl.in.tum.de/petter/llvm-abstractinterpretation") + +if (NOT PATH_TO_LLVM) + message(FATAL_ERROR " + The cmake is supposed to be called with PATH_TO_LLVM pointing to + a precompiled version of LLVM or to to the source code of LLVM + Examples: + cmake -G \"${CMAKE_GENERATOR}\" -DPATH_TO_LLVM=/opt/llvm-9.0.1 ${CMAKE_SOURCE_DIR} + cmake -G \"${CMAKE_GENERATOR}\" -DPATH_TO_LLVM=/llvm-project/llvm ${CMAKE_SOURCE_DIR} +") endif() -# For older LLVM < 8.0 replace first line with this -# add_llvm_loadable_module(llvm-pain -add_llvm_library(llvm-pain MODULE +if (NOT IS_ABSOLUTE ${PATH_TO_LLVM}) + # Convert relative path to absolute path + get_filename_component(PATH_TO_LLVM + "${PATH_TO_LLVM}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") +endif() + +set (BUILD_AGAINST_PRECOMPILED_LLVM TRUE) +if (EXISTS ${PATH_TO_LLVM}/CMakeLists.txt) + set (BUILD_AGAINST_PRECOMPILED_LLVM FALSE) +endif() + +# This enables assertions for Release builds. +# https://stackoverflow.com/questions/22140520/how-to-enable-assert-in-cmake-release-mode +string(REPLACE "-DNDEBUG" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}") + +if (${BUILD_AGAINST_PRECOMPILED_LLVM}) + set (search_paths + ${PATH_TO_LLVM} + ${PATH_TO_LLVM}/lib/cmake + ${PATH_TO_LLVM}/lib/cmake/llvm + ${PATH_TO_LLVM}/lib/cmake/clang + ${PATH_TO_LLVM}/share/clang/cmake/ + ${PATH_TO_LLVM}/share/llvm/cmake/ + ) + + find_package(LLVM REQUIRED CONFIG PATHS ${search_paths} NO_DEFAULT_PATH) + find_package(Clang REQUIRED CONFIG PATHS ${search_paths} NO_DEFAULT_PATH) + + list(APPEND CMAKE_MODULE_PATH "${LLVM_CMAKE_DIR}") + +else() + + set (LLVM_ENABLE_PROJECTS "clang" CACHE BOOL "Build only Clang when building against monorepo" FORCE) + set (LLVM_TARGETS_TO_BUILD "host" CACHE STRING "Only build targets for host architecture" FORCE) + #set (BUILD_SHARED_LIBS "On" CACHE BOOL "Link LLVM libraries dynamically" FORCE) + + add_subdirectory(${PATH_TO_LLVM} llvm-build) + + if (NOT TARGET clangTooling) + message(FATAL_ERROR " + Cannot find clangTooling target. Did you forget to clone clang sources? + Clean CMake cache and make sure they are available at: + ${PATH_TO_LLVM}/tools/clang") + endif() + + # Normally, include paths provided by LLVMConfig.cmake + # In this case we can 'steal' them from real targets + get_target_property(llvm_support_includes LLVMSupport INCLUDE_DIRECTORIES) + get_target_property(clang_tooling_includes clangTooling INCLUDE_DIRECTORIES) + set(LLVM_INCLUDE_DIRS ${llvm_support_includes} ${clang_tooling_includes}) + list(REMOVE_DUPLICATES LLVM_INCLUDE_DIRS) + + # Manually include the llvm CMake modules + list(APPEND CMAKE_MODULE_PATH + "${PATH_TO_LLVM}/cmake" + "${PATH_TO_LLVM}/cmake/modules" + ) + +endif() + +include(LLVM-Config) +include(HandleLLVMOptions) +include(AddLLVM) + +if ("LLVM" IN_LIST LLVM_AVAILABLE_LIBS) + set (LLVM_AVAILABLE_LIBS + LLVM + ) +else() + set (LLVM_AVAILABLE_LIBS + LLVMSupport + LLVMCore + LLVMAnalysis + ) +endif() + +set(pain_sources src/fixpoint.cpp src/fixpoint.h src/value_set.cpp @@ -21,8 +105,41 @@ add_llvm_library(llvm-pain MODULE src/simple_interval.h src/normalized_conjunction.cpp src/normalized_conjunction.h +) + +add_llvm_library(llvm-pain MODULE + ${pain_sources} DEPENDS intrinsics_gen PLUGIN_TOOL opt - ) +) + +target_include_directories(llvm-pain PRIVATE + ${LLVM_INCLUDE_DIRS} +) + +add_llvm_executable(simple_interval_test + test/simple_interval_test.cpp + ${pain_sources} + DEPENDS + intrinsics_gen +) + + target_include_directories(simple_interval_test PRIVATE + ${LLVM_INCLUDE_DIRS} +) + +target_link_libraries(simple_interval_test + ${LLVM_AVAILABLE_LIBS} +) + +enable_testing() + +add_test(NAME intervalAnalysisTest + COMMAND opt --load $ --painpass -S /Users/tim/Developer/ba/llvm-abstractinterpretation/output/add-1.c.ll +) + +add_test(NAME simpleIntervalTest + COMMAND simple_interval_test +) \ No newline at end of file diff --git a/test/simple_interval_test.cpp b/test/simple_interval_test.cpp index fa0d7d4..566e134 100644 --- a/test/simple_interval_test.cpp +++ b/test/simple_interval_test.cpp @@ -1,8 +1,7 @@ - #include #include -#include "simple_interval.h" +#include "../src/simple_interval.h" // Standard integer types using s64 = std::int64_t; @@ -229,19 +228,17 @@ void testSimpleDomain(u32 w, u32 iters, u64* errs) { u64 error_count; int main() { using namespace pcpo; - u64 iters = 64; + u64 iters = 512; // Use this to reproduce a failing example more quickly. Simply insert the // last random hash the script outputs and the correct bitwidth. //rand_state = 0xe596fd2a27fe71c7ull; //testSimpleDomain(16, iters, &error_count); - while (true) { - testSimpleDomain( 8, iters, &error_count); - testSimpleDomain(16, iters, &error_count); - testSimpleDomain(17, iters, &error_count); - testSimpleDomain(32, iters, &error_count); - testSimpleDomain(64, iters, &error_count); - iters *= 2; - } -} + testSimpleDomain( 8, iters, &error_count); + testSimpleDomain(16, iters, &error_count); + testSimpleDomain(17, iters, &error_count); + testSimpleDomain(32, iters, &error_count); + testSimpleDomain(64, iters, &error_count); + iters *= 2; +} \ No newline at end of file -- GitLab From 1ea1bfa5d3506cee8a2d460427a89ae21d1a8192 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 12 Jan 2020 03:36:35 +0100 Subject: [PATCH 016/142] fixed xcode build --- CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6864a73..757aabd 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,8 @@ else() "${PATH_TO_LLVM}/cmake/modules" ) + set(LLVM_MAIN_SRC_DIR ${PATH_TO_LLVM}) + endif() include(LLVM-Config) -- GitLab From 124232a835a0d5427942d7d85246548fe1f360ed Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 12 Jan 2020 15:19:52 +0100 Subject: [PATCH 017/142] fixed path for test --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 757aabd..81d29e2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -139,7 +139,7 @@ target_link_libraries(simple_interval_test enable_testing() add_test(NAME intervalAnalysisTest - COMMAND opt --load $ --painpass -S /Users/tim/Developer/ba/llvm-abstractinterpretation/output/add-1.c.ll + COMMAND opt --load $ --painpass -S ${CMAKE_SOURCE_DIR}/output/add-1.c.ll ) add_test(NAME simpleIntervalTest -- GitLab From 151d146af079983316b4510041fc95b983e213ad Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 13 Jan 2020 18:31:58 +0100 Subject: [PATCH 018/142] added tests --- CMakeLists.txt | 19 ++++++++ src/normalized_conjunction.h | 7 ++- test/normalized_conjunction_test.cpp | 68 ++++++++++++++++++++++++++++ 3 files changed, 90 insertions(+), 4 deletions(-) create mode 100644 test/normalized_conjunction_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 81d29e2..618703d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -136,6 +136,21 @@ target_link_libraries(simple_interval_test ${LLVM_AVAILABLE_LIBS} ) +add_llvm_executable(normalized_conjunction_test + test/normalized_conjunction_test.cpp + ${pain_sources} + DEPENDS + intrinsics_gen +) + + target_include_directories(normalized_conjunction_test PRIVATE + ${LLVM_INCLUDE_DIRS} +) + +target_link_libraries(normalized_conjunction_test + ${LLVM_AVAILABLE_LIBS} +) + enable_testing() add_test(NAME intervalAnalysisTest @@ -144,4 +159,8 @@ add_test(NAME intervalAnalysisTest add_test(NAME simpleIntervalTest COMMAND simple_interval_test +) + +add_test(NAME normalizedConjunctionTest + COMMAND normalized_conjunction_test ) \ No newline at end of file diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index be08a85..a990c11 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -47,6 +47,7 @@ class NormalizedConjunction { // AbstractDomain interface NormalizedConjunction(bool isTop = false): state{isTop ? TOP : BOTTOM} {} + NormalizedConjunction(std::map equalaties); NormalizedConjunction(llvm::Constant const& constant); static NormalizedConjunction interpret( llvm::Instruction const& inst, std::vector const& operands @@ -56,7 +57,8 @@ class NormalizedConjunction { NormalizedConjunction a, NormalizedConjunction b ); static NormalizedConjunction merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b); - + static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); + // utils bool isTop() const { return state == TOP; }; /* return equalities.empty() */ bool isBottom() const { return state == BOTTOM; }; /* never? */ @@ -67,9 +69,6 @@ class NormalizedConjunction { bool operator!=(NormalizedConjunction other) const {return !(*this == other);} private: - NormalizedConjunction(std::map equalaties); - - static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); // Assignments static NormalizedConjunction nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp new file mode 100644 index 0000000..c5d7a15 --- /dev/null +++ b/test/normalized_conjunction_test.cpp @@ -0,0 +1,68 @@ +#include +#include +#include +#include +#include + +#include "../src/normalized_conjunction.h" + +using namespace pcpo; +using namespace llvm; + +int main() { + + Value *x1 = (Value *) 1; + Value *x2 = (Value *) 2; + Value *x3 = (Value *) 3; + Value *x4 = (Value *) 4; + Value *x5 = (Value *) 5; + Value *x6 = (Value *) 6; + Value *x7 = (Value *) 7; + Value *x8 = (Value *) 8; + Value *x9 = (Value *) 9; + Value *x10 = (Value *) 10; + Value *x11 = (Value *) 11; + Value *x12 = (Value *) 12; + + std::map E1 = { + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x1, 0}}, + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x1, 15}}, + {x6, {x6, 1, x1, 3}}, + {x7, {x7, 1, x1, 2}}, + {x8, {x8, 7, x1, 15}}, + {x9, {x9, 1, nullptr, 0}}, + {x10, {x10, 1, nullptr, 2}}, + {x11, {x11, 1, nullptr, 1}}, + {x12, {x12, 1, nullptr, 3}} + }; + + std::map E2 = { + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x2, -5}}, + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x2, 0}}, + {x6, {x6, 1, x2, 1}}, + {x7, {x7, 1, x2, 0}}, + {x8, {x8, 21, x2, -20}}, + {x9, {x9, 1, nullptr, 1}}, + {x10, {x10, 1, nullptr, 4}}, + {x11, {x11, 2, x1, -3}}, + {x12, {x12, 4, x1, -5}} + }; + + std::map expected = { + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x3, 15}}, + {x7, {x7, 1, x6, -1}}, + {x10, {x10, 2, x9, 2}}, + {x12, {x12, 2, x11, 1}} + }; + + NormalizedConjunction result = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); + + return result.equalaties == expected; +} -- GitLab From 3980fae19b5992b098ff1fd10b5461196e6f5664 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 13 Jan 2020 20:45:02 +0100 Subject: [PATCH 019/142] improved tests --- test/normalized_conjunction_test.cpp | 181 ++++++++++++++++++++------- 1 file changed, 135 insertions(+), 46 deletions(-) diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index c5d7a15..901ec7a 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -9,51 +9,66 @@ using namespace pcpo; using namespace llvm; -int main() { - - Value *x1 = (Value *) 1; - Value *x2 = (Value *) 2; - Value *x3 = (Value *) 3; - Value *x4 = (Value *) 4; - Value *x5 = (Value *) 5; - Value *x6 = (Value *) 6; - Value *x7 = (Value *) 7; - Value *x8 = (Value *) 8; - Value *x9 = (Value *) 9; - Value *x10 = (Value *) 10; - Value *x11 = (Value *) 11; - Value *x12 = (Value *) 12; - - std::map E1 = { - {x1, {x1, 1, x1, 0}}, - {x2, {x2, 1, x2, 0}}, - {x3, {x3, 1, x1, 0}}, - {x4, {x4, 3, x2, 5}}, - {x5, {x5, 3, x1, 15}}, - {x6, {x6, 1, x1, 3}}, - {x7, {x7, 1, x1, 2}}, - {x8, {x8, 7, x1, 15}}, - {x9, {x9, 1, nullptr, 0}}, - {x10, {x10, 1, nullptr, 2}}, - {x11, {x11, 1, nullptr, 1}}, - {x12, {x12, 1, nullptr, 3}} - }; - std::map E2 = { - {x1, {x1, 1, x1, 0}}, - {x2, {x2, 1, x2, 0}}, - {x3, {x3, 1, x2, -5}}, - {x4, {x4, 3, x2, 5}}, - {x5, {x5, 3, x2, 0}}, - {x6, {x6, 1, x2, 1}}, - {x7, {x7, 1, x2, 0}}, - {x8, {x8, 21, x2, -20}}, - {x9, {x9, 1, nullptr, 1}}, - {x10, {x10, 1, nullptr, 4}}, - {x11, {x11, 2, x1, -3}}, - {x12, {x12, 4, x1, -5}} - }; - +class NormalizedConjunctionTest: NormalizedConjunction { + +public: + static bool runTestAll(); + static bool runTestX0(); + static bool runTestX1(); + static bool runTestX2(); + static bool runTestX4(); +}; + +const Value *x1 = (Value *) 1; +const Value *x2 = (Value *) 2; +const Value *x3 = (Value *) 3; +const Value *x4 = (Value *) 4; +const Value *x5 = (Value *) 5; +const Value *x6 = (Value *) 6; +const Value *x7 = (Value *) 7; +const Value *x8 = (Value *) 8; +const Value *x9 = (Value *) 9; +const Value *x10 = (Value *) 10; +const Value *x11 = (Value *) 11; +const Value *x12 = (Value *) 12; + +const std::map E1 = { + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x1, 0}}, + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x1, 15}}, + {x6, {x6, 1, x1, 3}}, + {x7, {x7, 1, x1, 2}}, + {x8, {x8, 7, x1, 15}}, + {x9, {x9, 1, nullptr, 0}}, + {x10, {x10, 1, nullptr, 2}}, + {x11, {x11, 1, nullptr, 1}}, + {x12, {x12, 1, nullptr, 3}} +}; + +const std::map E2 = { + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x2, -5}}, + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x2, 0}}, + {x6, {x6, 1, x2, 1}}, + {x7, {x7, 1, x2, 0}}, + {x8, {x8, 21, x2, -20}}, + {x9, {x9, 1, nullptr, 1}}, + {x10, {x10, 1, nullptr, 4}}, + {x11, {x11, 2, x1, -3}}, + {x12, {x12, 4, x1, -5}} +}; + +auto mapToSeccond = [](std::pair p){ return p.second; }; + + +bool NormalizedConjunctionTest::runTestAll() { + std::cout << "Testing all: "; + bool result = false; std::map expected = { {x4, {x4, 3, x2, 5}}, {x5, {x5, 3, x3, 15}}, @@ -62,7 +77,81 @@ int main() { {x12, {x12, 2, x11, 1}} }; - NormalizedConjunction result = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); + NormalizedConjunction leastUpperBound = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); + + result = leastUpperBound.equalaties == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runTestX0() { + std::cout << "Testing X0: "; + bool result = true; + + std::set expected = { + {x4, 3, x2, 5} + }; + + std::set E1Set, E2Set; + transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); + transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); + + auto actual = NormalizedConjunction::computeX0(E1Set, E2Set); - return result.equalaties == expected; + result = actual == expected; + std::cout << (result ? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runTestX1() { + std::cout << "Testing X1: "; + bool result = false; + + std::set expected = { + {x10, 2, x9, 2} + }; + + std::set E1Set, E2Set; + transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); + transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); + + auto actual = NormalizedConjunction::computeX1(E1Set, E2Set); + + result = actual == expected; + std::cout << (result ? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runTestX2() { + std::cout << "Testing X2: "; + bool result = false; + + std::map expected = { + + }; + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runTestX4() { + std::cout << "Testing X4: "; + bool result = false; + + std::map expected = { + + }; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +int main() { + + return !(NormalizedConjunctionTest::runTestX0() + && NormalizedConjunctionTest::runTestX1() +// && NormalizedConjunctionTest::runTestX2() +// && NormalizedConjunctionTest::runTestX4() +// && NormalizedConjunctionTest::runTestAll() + ); } -- GitLab From 763e9e2cc24f4f0de5b33d561791a90da296d3e8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 13 Jan 2020 20:45:23 +0100 Subject: [PATCH 020/142] fixed bugs in X0 and X1 --- src/normalized_conjunction.cpp | 6 +++--- src/normalized_conjunction.h | 22 +++++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 579ac3b..5ee432a 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -127,7 +127,7 @@ std::set NormalizedConjunction::computeX0(std:: std::set_intersection(E1.begin(), E1.end(), E2.begin(), E2.end(), std::inserter(X0, X0.end())); // Remove trivial equalities std::set filteredX0; - auto filterTrivialEqualaties = [](Equality eq){return eq.y != eq.x;}; + auto filterTrivialEqualaties = [](Equality eq){ return eq.y != eq.x;}; copy_if(X0, std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); return filteredX0; @@ -158,8 +158,8 @@ std::set NormalizedConjunction::computeX1(std:: // find a linear equation that contains both points P1(c(1)i, c(1)h) and P2(c(2)i, c(2)h) // y = a * x + b auto y = i.first.y; - int64_t a = (h.second.b - h.first.b) / ((i.second.b - i.first.b)); - auto x = h.first.x; + int64_t a = ((i.second.b - i.first.b)) / (h.second.b - h.first.b); + auto x = h.first.y; int64_t b = -a * h.first.b + i.first.b; Equality eq = {y, a, x, b}; X1.insert(eq); diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index a990c11..449a65b 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -30,11 +30,27 @@ class NormalizedConjunction { int64_t b; inline bool operator<(Equality const& rhs) const { - return y < rhs.y; + if (y == rhs.y) { + if (a == rhs.a) { + if (x == rhs.x) { + if (b == rhs.b) { + return false; + } else { + return b < rhs.b; + } + } else { + return x < rhs.x; + } + } else { + return a < rhs.a; + } + } else { + return y < rhs.y; + } }; inline bool operator>(Equality const& rhs) const { - return y > rhs.y; + return *this < rhs; }; inline bool operator==(Equality const& rhs) const { @@ -69,7 +85,6 @@ class NormalizedConjunction { bool operator!=(NormalizedConjunction other) const {return !(*this == other);} private: - // Assignments static NormalizedConjunction nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction linearAssignment(NormalizedConjunction E, llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); @@ -80,6 +95,7 @@ class NormalizedConjunction { static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); // Helpers + protected: static std::set computeX0(std::set const& E1, std::set const& E2); static std::set computeX1(std::set const& E1, std::set const& E2); static std::set computeX2(std::set const& E1, std::set const& E2); -- GitLab From feb85b3e7c2f8d8b7ea8664fdf5f1dcd0131ded1 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 13 Jan 2020 23:30:15 +0100 Subject: [PATCH 021/142] fixed X2 X4 --- src/normalized_conjunction.cpp | 67 ++++++++++++++++------------ src/normalized_conjunction.h | 4 ++ test/normalized_conjunction_test.cpp | 32 +++++++++---- 3 files changed, 66 insertions(+), 37 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 5ee432a..cf04c50 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -186,23 +186,28 @@ std::set NormalizedConjunction::computeX2(std:: std::vector>> Pi2; - // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + + for (auto jt = differentConstants.begin(); jt != differentConstants.end();) { std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; + auto j = *jt; + equivalenceClass.insert(j); + jt = differentConstants.erase(jt); - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { - std::pair jpair = *tempit; - bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); + // partition differentConstants + // FIXME: performance can be increase by remoing i after assigning it to a equivalence class + for (auto it = jt; it != differentConstants.end(); ) { + auto i = *it; + bool condition1 = i.second.x == j.second.x; + bool condition2 = (i.first.b - i.second.b) / (i.second.a) == (j.first.b - j.second.b) / (j.second.a); if (condition1 && condition2) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit); + equivalenceClass.insert(i); + it = differentConstants.erase(it); + jt = differentConstants.begin(); + } else { + it++; } } + Pi2.push_back(equivalenceClass); } @@ -213,10 +218,10 @@ std::set NormalizedConjunction::computeX2(std:: for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) auto y = i.first.y; - auto m = i.second.a / h.second.b; + auto a = i.second.a / h.second.a; auto x = h.first.y; auto b = i.second.b - (i.second.a * h.second.b) / h.second.a; - Equality eq = {y, m, x, b}; + Equality eq = {y, a, x, b}; X2.insert(eq); } } @@ -289,7 +294,7 @@ std::set NormalizedConjunction::computeX4(std:: auto eq1 = *itE1; auto eq2 = *itE2; assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (!eq1.isConstant() && !eq2.isConstant()) { + if (!eq1.isConstant() && !eq2.isConstant() && eq1 != eq2) { differentConstants.insert({eq1,eq2}); } } @@ -297,22 +302,26 @@ std::set NormalizedConjunction::computeX4(std:: std::vector>> Pi4; // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { + for (auto it = differentConstants.begin(); it != differentConstants.end();) { std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end(); tempit++) { - std::pair jpair = *tempit; - bool condition1 = ipair.first.x == jpair.first.x && ipair.second.x == jpair.second.x; - bool condition2 = ipair.second.a / (ipair.first.a) == jpair.second.a / (jpair.first.a); - bool condition3 = (ipair.first.b - ipair.second.b) / (ipair.first.a) == (jpair.first.b - jpair.second.b) / (jpair.first.a); + std::pair i = *it; + equivalenceClass.insert(i); + it = differentConstants.erase(it); + + for (auto jt = it; jt != differentConstants.end(); ) { + std::pair j = *jt; + bool condition1 = i.first.x == j.first.x && i.second.x == j.second.x; + bool condition2 = i.second.a / (i.first.a) == j.second.a / (j.first.a); + bool condition3 = (i.first.b - i.second.b) / (i.first.a) == (j.first.b - j.second.b) / (j.first.a); if (condition1 && condition2 && condition3) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit); + equivalenceClass.insert(j); + jt = differentConstants.erase(jt); + it = differentConstants.begin(); + } else { + jt++; } } + Pi4.push_back(equivalenceClass); } @@ -323,10 +332,10 @@ std::set NormalizedConjunction::computeX4(std:: for (auto i: q) { // xi = ai/ah * xh + ( bi - (ai * bh) / ah) auto y = i.first.y; - auto m = i.second.a / (h.second.b); + auto a = i.second.a / h.second.a; auto x = h.first.y; auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); - Equality eq = {y, m, x, b}; + Equality eq = {y, a, x, b}; X4.insert(eq); } } diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 449a65b..2d4d69e 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -57,6 +57,10 @@ class NormalizedConjunction { return y == rhs.y && a == rhs.a && x == rhs.x && b == rhs.b; }; + inline bool operator!=(Equality const& rhs) const { + return !(*this == rhs); + }; + bool isConstant() const { return x == nullptr; }; }; std::map equalaties; diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index 901ec7a..a4f89b8 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -77,9 +77,9 @@ bool NormalizedConjunctionTest::runTestAll() { {x12, {x12, 2, x11, 1}} }; - NormalizedConjunction leastUpperBound = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); + auto actual = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); - result = leastUpperBound.equalaties == expected; + result = actual.equalaties == expected; std::cout << (result? "success" : "failed") << "\n"; return result; @@ -127,9 +127,17 @@ bool NormalizedConjunctionTest::runTestX2() { std::cout << "Testing X2: "; bool result = false; - std::map expected = { - + std::set expected = { + {x12, 2, x11, 1} }; + + std::set E1Set, E2Set; + transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); + transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); + + auto actual = NormalizedConjunctionTest::computeX2(E1Set, E2Set); + + result = actual == expected; std::cout << (result? "success" : "failed") << "\n"; return result; } @@ -138,10 +146,18 @@ bool NormalizedConjunctionTest::runTestX4() { std::cout << "Testing X4: "; bool result = false; - std::map expected = { - + std::set expected = { + {x5, 3, x3, 15}, + {x7, 1, x6, -1} }; + std::set E1Set, E2Set; + transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); + transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); + + auto actual = NormalizedConjunctionTest::computeX4(E1Set, E2Set); + + result = actual == expected; std::cout << (result? "success" : "failed") << "\n"; return result; } @@ -150,8 +166,8 @@ int main() { return !(NormalizedConjunctionTest::runTestX0() && NormalizedConjunctionTest::runTestX1() -// && NormalizedConjunctionTest::runTestX2() -// && NormalizedConjunctionTest::runTestX4() + && NormalizedConjunctionTest::runTestX2() + && NormalizedConjunctionTest::runTestX4() // && NormalizedConjunctionTest::runTestAll() ); } -- GitLab From 479a18273433f65abbcac0cbec36204937f7377d Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 13 Jan 2020 23:32:11 +0100 Subject: [PATCH 022/142] fixed X3 --- src/normalized_conjunction.cpp | 60 +--------------------------- src/normalized_conjunction.h | 1 - test/normalized_conjunction_test.cpp | 2 +- 3 files changed, 2 insertions(+), 61 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index cf04c50..a762927 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -186,7 +186,6 @@ std::set NormalizedConjunction::computeX2(std:: std::vector>> Pi2; - for (auto jt = differentConstants.begin(); jt != differentConstants.end();) { std::set> equivalenceClass; auto j = *jt; @@ -194,7 +193,6 @@ std::set NormalizedConjunction::computeX2(std:: jt = differentConstants.erase(jt); // partition differentConstants - // FIXME: performance can be increase by remoing i after assigning it to a equivalence class for (auto it = jt; it != differentConstants.end(); ) { auto i = *it; bool condition1 = i.second.x == j.second.x; @@ -228,62 +226,6 @@ std::set NormalizedConjunction::computeX2(std:: return X2; } -/// X3 / E'3: set of variables where the right hand side of E1 contains a variable and E2 contains a constant. -std::set NormalizedConjunction::computeX3(std::set const& E1, std::set const E2) { - std::set X3; - std::set> differentConstants; - - assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); - - for (auto itE1 = E1.begin(), itE2 = E2.begin(); itE1 != E1.end() && itE2 != E2.end(); ++itE1, ++itE2) { - auto eq1 = *itE1; - auto eq2 = *itE2; - assert(eq1.y == eq2.y && "left hand side of equations should be the same"); - if (!eq1.isConstant() && eq2.isConstant()) { - differentConstants.insert({eq1,eq2}); - } - } - - std::vector>> Pi3; - - // partition differentConstants - for (auto iterator = differentConstants.begin(); iterator != differentConstants.end();) { - std::set> equivalenceClass; - std::pair ipair = *iterator; - equivalenceClass.insert(ipair); - iterator++; - // FIXME: Make sure this doesnt casue any trouble! - for (auto tempit = iterator; tempit != differentConstants.end();) { - std::pair jpair = *tempit; - bool condition1 = ipair.second.x == jpair.second.x; - bool condition2 = (ipair.first.b - ipair.second.b) / (ipair.second.a) == (jpair.first.b - jpair.second.b) / (jpair.second.a); - if (condition1 && condition2) { - equivalenceClass.insert(jpair); - differentConstants.erase(tempit++); - } else { - tempit++; - } - } - Pi3.push_back(equivalenceClass); - } - - // form equaltites for partitions in Pi3 - for (auto q: Pi3) { - auto h = *q.begin(); - q.erase(q.begin()); - for (auto i: q) { - // xi = ai/ah * xh + ( bi - (ai * bh) / ah) - auto y = i.first.y; - auto m = i.second.a / (h.second.b); - auto x = h.first.y; - auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); - Equality eq = {y, m, x, b}; - X3.insert(eq); - } - } - return X3; -} - std::set NormalizedConjunction::computeX4(std::set const& E1, std::set const& E2) { std::set X4; std::set> differentConstants; @@ -380,7 +322,7 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // FIXME: function computeX2(a,b) == computeX3(b,a) remove one of them std::set X1 = computeX1(E1, E2); std::set X2 = computeX2(E1, E2); - std::set X3 = computeX3(E1, E2); + std::set X3 = computeX2(E2, E1); std::set X4 = computeX4(E1, E2); // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 2d4d69e..65270f0 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -103,7 +103,6 @@ class NormalizedConjunction { static std::set computeX0(std::set const& E1, std::set const& E2); static std::set computeX1(std::set const& E1, std::set const& E2); static std::set computeX2(std::set const& E1, std::set const& E2); - static std::set computeX3(std::set const& E1, std::set const E2); static std::set computeX4(std::set const& E1, std::set const& E2); }; diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index a4f89b8..50e1b6a 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -168,6 +168,6 @@ int main() { && NormalizedConjunctionTest::runTestX1() && NormalizedConjunctionTest::runTestX2() && NormalizedConjunctionTest::runTestX4() -// && NormalizedConjunctionTest::runTestAll() + && NormalizedConjunctionTest::runTestAll() ); } -- GitLab From b2c3b613bf998d0f7389ed666ba68d401124048e Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 15 Jan 2020 18:24:04 +0100 Subject: [PATCH 023/142] fixed nonDeterministic assigment --- src/normalized_conjunction.cpp | 120 ++++++++------------------- src/normalized_conjunction.h | 9 +- test/normalized_conjunction_test.cpp | 52 +++++++++++- 3 files changed, 91 insertions(+), 90 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index a762927..84db3ae 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -11,7 +11,6 @@ using namespace llvm; NormalizedConjunction::NormalizedConjunction(Constant const& constant) { if (ConstantInt const* c = dyn_cast(&constant)) { state = NORMAL; - // Watch out for signed/unsigend APInts in future Equality eq = {&constant, 1, nullptr, c->getValue().getSExtValue()}; equalaties = {{&constant,eq}}; } else { @@ -21,7 +20,7 @@ NormalizedConjunction::NormalizedConjunction(Constant const& constant) { NormalizedConjunction::NormalizedConjunction(std::map equalaties) { this->equalaties = equalaties; - state = NORMAL; + state = equalaties.empty() ? TOP : NORMAL; } // MARK: AbstractDomain interface @@ -29,55 +28,26 @@ NormalizedConjunction::NormalizedConjunction(std::map eq NormalizedConjunction NormalizedConjunction::interpret( Instruction const& inst, std::vector const& operands ) { - // FIXME: maybe use nonDeterministic assignment here. - if (operands.size() != 2) return NormalizedConjunction {true}; + NormalizedConjunction a = operands[0]; + NormalizedConjunction b = operands[1]; + + if (operands.size() != 2) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); // We only deal with integer types IntegerType const* type = dyn_cast(inst.getType()); // FIXME: maybe use nonDeterministic assignment here. - if (not type) return NormalizedConjunction {true}; + if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); type = dyn_cast(inst.getOperand(0)->getType()); // FIXME: maybe use nonDeterministic assignment here. - if (not type) return NormalizedConjunction {true}; + if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); type = dyn_cast(inst.getOperand(1)->getType()); // FIXME: maybe use nonDeterministic assignment here. - if (not type) return NormalizedConjunction {true}; + if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); unsigned bitWidth = inst.getOperand(0)->getType()->getIntegerBitWidth(); assert(bitWidth == inst.getOperand(1)->getType()->getIntegerBitWidth()); - - NormalizedConjunction a = operands[0]; - NormalizedConjunction b = operands[1]; - - // Abstract Effect of statements: - - // [xi := ?] - // default - - // [xi := b] - // This will never occur due to SSA Form. - - // [xi := xi] - // [xi := xj] - // Those will never occur due to SSA Form. - - // [xi := xi + b] [xi := xi - b] - // Cannot occur due to SSA - - // [xi := xj + b] [xi := xj - b] - - // [xi := a * xi] [xi := a / xi] - // Cannot occur due to SSA - - // [xi := a * xj] [xi := a / xj] - - // [xi := a * xi + b] [xi := a * xi - b] [xi := a / xi + b] [xi := a / xi - b] - // Cannot occur due to SSA - - // [xi := a * xj + b] [xi := a * xj - b] [xi := a / xj + b] [xi := a / xj - b] - // Those will never occur due to SSA Form. switch (inst.getOpcode()) { case Instruction::Add: @@ -89,7 +59,7 @@ NormalizedConjunction NormalizedConjunction::interpret( case Instruction::SDiv: case Instruction::UDiv: default: - return nonDeterminsticAssignment(inst, a, b); + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); } } @@ -100,9 +70,9 @@ NormalizedConjunction NormalizedConjunction::refineBranch(CmpInst::Predicate pre NormalizedConjunction NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b) { - if (a.isBottom()) return b; - if (b.isBottom()) return a; - if (a.isTop() || b.isTop()) return NormalizedConjunction {true}; +// if (a.isBottom()) return b; +// if (b.isBottom()) return a; +// if (a.isTop() || b.isTop()) return NormalizedConjunction {true}; // Note that T is handled above, so no need to convert the inputs @@ -344,52 +314,26 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti // MARK: Abstract Assignments -// [xi := ?]# E = Exists# xi in E -NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); - auto i = result.equalaties[&inst]; - - if (i.x != &inst && i.b != 0) { - result.equalaties[&inst] = {&inst, 1, &inst, 0}; - } else { - // find all equations using xi - auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; - auto it = std::find_if(result.equalaties.begin(), result.equalaties.end(), predicate); - if (it != result.equalaties.end()) { - Equality k = (*it).second; - for (it = std::find_if(++it, result.equalaties.end(), predicate); - it != result.equalaties.end(); - it = std::find_if(++it, result.equalaties.end(), predicate)) { - auto& l = it->second; - result.equalaties[l.y] = {l.y, 1, k.y, l.b - k.b}; - } - result.equalaties[k.y] = {k.y, 1, k.y, 0}; - } - result.equalaties[&inst] = {&inst, 1, &inst, 0}; - } - return result; -} - /// [xi := ?] NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(NormalizedConjunction E, Value const* xi) { assert(xi != nullptr && "xi cannot be NULL"); - auto i = E.equalaties[xi]; + auto xj = E.equalaties[xi].x; - if (xi != i.x && i.b != 0) { + if (xi != xj && xj != 0) { E.equalaties[xi] = {xi, 1, xi, 0}; } else { // find all equations using xi - auto predicate = [&i](std::pair p){ return p.second.x = i.y;}; + auto predicate = [&xi](std::pair p){ return p.second.x == xi && p.second.y != xi ;}; auto it = std::find_if(E.equalaties.begin(), E.equalaties.end(), predicate); if (it != E.equalaties.end()) { - Equality k = (*it).second; + auto xk = (*it).second; for (it = std::find_if(++it, E.equalaties.end(), predicate); it != E.equalaties.end(); it = std::find_if(++it, E.equalaties.end(), predicate)) { - auto& l = it->second; - E.equalaties[l.y] = {l.y, 1, k.y, l.b - k.b}; + auto& xl = it->second; + E.equalaties[xl.y] = {xl.y, 1, xk.y, xl.b - xk.b}; } - E.equalaties[k.y] = {k.y, 1, k.y, 0}; + E.equalaties[xk.y] = {xk.y, 1, xk.y, 0}; } E.equalaties[xi] = {xi, 1, xi, 0}; } @@ -439,13 +383,13 @@ NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, Norma } else if (result.equalaties[op2].isConstant()) { return linearAssignment(result, &inst, 1, op1, result.equalaties[op2].b); } else { - return nonDeterminsticAssignment(inst, lhs, rhs); + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); } } else if (isa(op1) && (isa(op2))) { return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b + result.equalaties[op2].b); } else { assert(false); - return nonDeterminsticAssignment(inst, lhs, rhs); + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); } } @@ -516,9 +460,9 @@ bool NormalizedConjunction::isNormalized() const { // MARK: Operators bool NormalizedConjunction::operator==(NormalizedConjunction rhs) const { - return state == NORMAL - ? rhs.state == NORMAL and equalaties == rhs.equalaties - : state == rhs.state; + return getState() == NORMAL + ? rhs.getState() == NORMAL and equalaties == rhs.equalaties + : getState() == rhs.getState(); } raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { @@ -527,9 +471,13 @@ raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { } else if(a.isTop()) { os << "T"; } else { - for (auto eq: a.equalaties) { - // FIXME: Hanlde nullptr - + if (a.equalaties.size() > 0) { + os << "{ "; + } else { + os << "{ }"; + } + for (auto it = a.equalaties.begin(); it != a.equalaties.end(); it++) { + auto eq = *it; if (eq.second.y != nullptr && eq.second.y->hasName()) { os << eq.second.y->getName() << " = "; } else if (eq.second.y != nullptr) { @@ -553,7 +501,11 @@ raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { os << eq.second.b; } - os << "\n"; + if (std::next(it) == a.equalaties.end()) { + os << " }"; + } else { + os << ", "; + } } } return os; diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 65270f0..c2b4702 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -64,6 +64,7 @@ class NormalizedConjunction { bool isConstant() const { return x == nullptr; }; }; std::map equalaties; + State getState() const { return equalaties.empty() ? TOP : NORMAL; }; // AbstractDomain interface NormalizedConjunction(bool isTop = false): state{isTop ? TOP : BOTTOM} {} @@ -80,17 +81,16 @@ class NormalizedConjunction { static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); // utils - bool isTop() const { return state == TOP; }; /* return equalities.empty() */ - bool isBottom() const { return state == BOTTOM; }; /* never? */ + bool isTop() const { return getState() == TOP; }; /* return equalities.empty() */ + bool isBottom() const { return getState() == BOTTOM; }; /* never? */ bool isNormalized() const; // TODO: [] operator ? bool operator==(NormalizedConjunction other) const; bool operator!=(NormalizedConjunction other) const {return !(*this == other);} - private: + protected: // Assignments - static NormalizedConjunction nonDeterminsticAssignment(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); static NormalizedConjunction linearAssignment(NormalizedConjunction E, llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); static NormalizedConjunction nonDeterminsticAssignment(NormalizedConjunction E, llvm::Value const* xi); @@ -99,7 +99,6 @@ class NormalizedConjunction { static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); // Helpers - protected: static std::set computeX0(std::set const& E1, std::set const& E2); static std::set computeX1(std::set const& E1, std::set const& E2); static std::set computeX2(std::set const& E1, std::set const& E2); diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index 50e1b6a..2c7b4b1 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -18,6 +18,8 @@ public: static bool runTestX1(); static bool runTestX2(); static bool runTestX4(); + static bool runNonDeterministicAssignmentTest1(); + static bool runNonDeterministicAssignmentTest2(); }; const Value *x1 = (Value *) 1; @@ -162,6 +164,52 @@ bool NormalizedConjunctionTest::runTestX4() { return result; } +bool NormalizedConjunctionTest::runNonDeterministicAssignmentTest1() { + std::cout << "Testing non deterministic Assignment 1: "; + bool result = false; + + NormalizedConjunction E = NormalizedConjunction({ + {x1, {x1, 1, nullptr, 4}}, + {x2, {x2, 1, nullptr, 2}} + }); + + auto expected = NormalizedConjunction({ + {x1, {x1, 1, nullptr, 4}}, + {x2, {x2, 1, x2, 0}}, + }); + + auto actual = NormalizedConjunctionTest::nonDeterminsticAssignment(E, x2); + + result = actual == expected; + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() { + std::cout << "Testing non deterministic Assignment 2: "; + bool result = false; + + NormalizedConjunction E = NormalizedConjunction({ + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x1, 2}}, + {x3, {x3, 1, x2, 4}}, + {x4, {x4, 1, x1, 10}} + }); + + auto expected = NormalizedConjunction({ + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x2, 4}}, + {x4, {x4, 1, x2, 8}} + }); + + auto actual = NormalizedConjunctionTest::nonDeterminsticAssignment(E, x1); + + result = actual == expected; + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + int main() { return !(NormalizedConjunctionTest::runTestX0() @@ -169,5 +217,7 @@ int main() { && NormalizedConjunctionTest::runTestX2() && NormalizedConjunctionTest::runTestX4() && NormalizedConjunctionTest::runTestAll() - ); + && NormalizedConjunctionTest::runNonDeterministicAssignmentTest1() + && NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() + ); } -- GitLab From 3dd61d1af64cc3295d9d56cd1984dd977bc1748c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 15 Jan 2020 22:00:05 +0100 Subject: [PATCH 024/142] Added addition tests --- src/normalized_conjunction.cpp | 2 + test/normalized_conjunction_test.cpp | 57 ++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 84db3ae..c028294 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -344,6 +344,8 @@ NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(Normalize NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunction E, Value const* xi, int64_t a, Value const* xj, int64_t b) { assert(xi != nullptr && "xi cannot be NULL"); + E = nonDeterminsticAssignment(E, xi); + // make sure xj exists auto xjS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].x : nullptr; auto bS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].b : 0; diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index 2c7b4b1..eb1f132 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -20,6 +20,8 @@ public: static bool runTestX4(); static bool runNonDeterministicAssignmentTest1(); static bool runNonDeterministicAssignmentTest2(); + static bool runLinearAssignmentTest1(); + static bool runLinearAssignmentTest2(); }; const Value *x1 = (Value *) 1; @@ -210,6 +212,59 @@ bool NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() { return result; } +bool NormalizedConjunctionTest::runLinearAssignmentTest1() { + std::cout << "Testing linear Assignment 1: "; + bool result = false; + + NormalizedConjunction E = NormalizedConjunction({ + {x1, {x1, 1, nullptr, 2}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x2, 3}} + + }); + + auto expected = NormalizedConjunction({ + {x1, {x1, 1, nullptr, 2}}, + {x2, {x2, 1, nullptr, 5}}, + {x3, {x3, 1, x3, 0}} + }); + + auto actual = NormalizedConjunctionTest::linearAssignment(E, x2, 1, x1, 3); + + result = actual == expected; + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool NormalizedConjunctionTest::runLinearAssignmentTest2() { + std::cout << "Testing linear Assignment 2: "; + bool result = false; + + NormalizedConjunction E = NormalizedConjunction({ + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x1, 4}}, + {x3, {x3, 1, x3, 0}}, + {x4, {x4, 1, x3, 10}}, + {x5 ,{x5, 1, x3, -4}}, + {x6, {x6, 1, x3, 1}} + }); + + auto expected = NormalizedConjunction({ + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x2, -11}}, + {x4, {x4, 1, x2, -1}}, + {x5, {x5, 1, x2, -15}}, + {x6, {x6, 1, x2, -10}} + }); + + auto actual = NormalizedConjunctionTest::linearAssignment(E, x2, 1, x4, 1); + + result = actual == expected; + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + int main() { return !(NormalizedConjunctionTest::runTestX0() @@ -219,5 +274,7 @@ int main() { && NormalizedConjunctionTest::runTestAll() && NormalizedConjunctionTest::runNonDeterministicAssignmentTest1() && NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() + && NormalizedConjunctionTest::runLinearAssignmentTest1() + && NormalizedConjunctionTest::runLinearAssignmentTest2() ); } -- GitLab From 5d2fa90a3208745f4c3ae096f1706b7ef502b7e5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 15 Jan 2020 23:03:13 +0100 Subject: [PATCH 025/142] Added linear terms to linearAssignment --- src/normalized_conjunction.cpp | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index c028294..7a82cf8 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -349,16 +349,22 @@ NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunct // make sure xj exists auto xjS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].x : nullptr; auto bS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].b : 0; + auto aS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].a : 1; + + if (!(a % aS == 0 && (-bS - b) % aS == 0)) { + // Precison loss due to int division! Abort + return E; + } if (xi > xjS) { - E.equalaties[xi] = {xi, a, xjS, bS + b}; + E.equalaties[xi] = {xi, aS * a, xjS, a * bS + b}; return E; } else { auto pred = [&xjS](std::pair p){ return p.second.x == xjS && p.second.y != xjS; }; for (auto xk: make_filter_range(E.equalaties, pred)) { - E.equalaties[xk.second.y] = {xk.second.y, a, xi, xk.second.b - b - bS}; + E.equalaties[xk.second.y] = {xk.second.y, xk.second.a * a/aS, xi, (-bS - b) / aS + xk.second.b}; } - E.equalaties[xjS] = {xjS, a, xi, -b - bS}; + E.equalaties[xjS] = {xjS, a/aS, xi, (-bS - b) / aS}; } return E; } -- GitLab From d59223a3e59c2b1cebd6e1253d3b5a3cf115e8ad Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 15 Jan 2020 23:20:00 +0100 Subject: [PATCH 026/142] fixed add mull and sub operators --- src/normalized_conjunction.cpp | 102 ++++++++++++++++++++++----------- 1 file changed, 67 insertions(+), 35 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 7a82cf8..6216383 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -371,28 +371,35 @@ NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunct // MARK: Abstract Operations -// [xi := xj + c] +// [xi := xj + b] // [xi := xj + xk] -// [xi := cj + ck] +// [xi := bj + bk] NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); + // [xi := b + xj] if (isa(op1) && isa(op2)) { auto b = dyn_cast(op1); return linearAssignment(result, &inst, 1, op2, b->getSExtValue()); + // [xi := xj + b] } else if (isa(op2) && isa(op1)) { auto b = dyn_cast(op2); return linearAssignment(result, &inst, 1, op1, b->getSExtValue()); + // [xi := xj + xk] } else if (isa(op1) && isa(op2)) { + // [xi := bj + xk] if (result.equalaties[op1].isConstant()) { return linearAssignment(result, &inst, 1, op2, result.equalaties[op1].b); + // [xi := xj + bk] } else if (result.equalaties[op2].isConstant()) { return linearAssignment(result, &inst, 1, op1, result.equalaties[op2].b); + // [xi := xj + xk] } else { return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); } + // [xi := bj + bk] } else if (isa(op1) && (isa(op2))) { return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b + result.equalaties[op2].b); } else { @@ -403,47 +410,72 @@ NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, Norma // TODO: [xi := xj - x] NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { + auto result = leastUpperBound(lhs, rhs); + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + // [xi := b - xj] + if (isa(op1) && isa(op2)) { + auto b = dyn_cast(op1); + return linearAssignment(result, &inst, 1, op2, -b->getSExtValue()); + // [xi := xj - b] + } else if (isa(op2) && isa(op1)) { + auto b = dyn_cast(op2); + return linearAssignment(result, &inst, 1, op1, -b->getSExtValue()); + // [xi := xj - xk] + } else if (isa(op1) && isa(op2)) { + // [xi := bj - xk] + if (result.equalaties[op1].isConstant()) { + return linearAssignment(result, &inst, 1, op2, -result.equalaties[op1].b); + // [xi := xj - bk] + } else if (result.equalaties[op2].isConstant()) { + return linearAssignment(result, &inst, 1, op1, -result.equalaties[op2].b); + // [xi := xj - xk] + } else { + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + } + // [xi := bj + bk] + } else if (isa(op1) && (isa(op2))) { + return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b - result.equalaties[op2].b); + } else { + assert(false); + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + } +} + +// [xi := a * xj] +NormalizedConjunction NormalizedConjunction::Mul(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { auto result = leastUpperBound(lhs, rhs); - auto i = result.equalaties[&inst]; auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - Value const* j; - ConstantInt const* b; + // [xi := a * xj] if (isa(op1) && isa(op2)) { - b = dyn_cast(op1); - j = op2; + auto a = dyn_cast(op1); + return linearAssignment(result, &inst, a->getSExtValue(), op2, 0); + // [xi := xj * a] } else if (isa(op2) && isa(op1)) { - b = dyn_cast(op2); - j = op1; - } else { - assert(false && "One operand has to be constant"); - } - - auto jj = result.equalaties[j]; - - if (i > jj) { - result.equalaties[i.y] = {i.y, 1, jj.x, jj.b - i.b};; - } else { - // Filter results - auto pred = [&jj](std::pair p){ return p.second.x == jj.y && p.second.y != jj.y;}; - for (auto kpair: make_filter_range(result.equalaties, pred)) { - auto& k = kpair.second; - result.equalaties[k.y] = {k.y, 1, i.y, k.b + i.b - jj.b}; + auto a = dyn_cast(op2); + return linearAssignment(result, &inst, a->getSExtValue(), op1, 0); + // [xi := xj * xk] + } else if (isa(op1) && isa(op2)) { + // [xi := aj * xk] + if (result.equalaties[op1].isConstant()) { + return linearAssignment(result, &inst, result.equalaties[op1].b, op2, 0); + // [xi := xj * ak] + } else if (result.equalaties[op2].isConstant()) { + return linearAssignment(result, &inst, result.equalaties[op1].b, op1, 0); + // [xi := xj * xk] + } else { + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); } - result.equalaties[jj.y] = {jj.y, 1, i.y, i.b - jj.b}; + // [xi := aj * ak] + } else if (isa(op1) && (isa(op2))) { + return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b * result.equalaties[op2].b); + } else { + assert(false); + return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); } - return result; -} - -// [xi := a * xj] -NormalizedConjunction NormalizedConjunction::Mul(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); - - // TODO - assert(false && "not implemented"); - - return result; } // MARK: Utils -- GitLab From 1aa2dc7155f347456e5c09969f67cf402e6cd1a5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 17 Jan 2020 17:13:55 +0100 Subject: [PATCH 027/142] added for loop sample --- samples/for-loop-1.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 samples/for-loop-1.c diff --git a/samples/for-loop-1.c b/samples/for-loop-1.c new file mode 100644 index 0000000..a6a1022 --- /dev/null +++ b/samples/for-loop-1.c @@ -0,0 +1,33 @@ + + +int x_arr[100]; +int y_arr[100]; + +int res[100]; +int *res_ptr; + +int *x_ptr; +int *y_ptr; + +void access() { + int x = *x_ptr; + int y = *y_ptr; + *res_ptr = x + y; + res_ptr++; +} + + +int main() { + int i; + x_ptr = &x_arr[0]; + y_ptr = &y_arr[0]; + res_ptr = &res[0]; + + for (i=0; i<100; i++) { + access(); + x_ptr++; + y_ptr++; + } +} + +// (x_ptr = 8 * i + x_arr[0]) && (Y_ptr = 4 * i + y_arr[0]) \ No newline at end of file -- GitLab From 13bb56ecc763b105c5bb9750748fb1bc1aa06216 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 17 Jan 2020 17:14:05 +0100 Subject: [PATCH 028/142] removed stale comment --- src/normalized_conjunction.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 6216383..26def36 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -287,9 +287,7 @@ NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjuncti E2.insert(eq); } - // XO / E'0: set of variables where the right hand side in E1 and E2 coincide std::set X0 = computeX0(E1, E2); - // FIXME: function computeX2(a,b) == computeX3(b,a) remove one of them std::set X1 = computeX1(E1, E2); std::set X2 = computeX2(E1, E2); std::set X3 = computeX2(E2, E1); -- GitLab From 1035e4c313bf6a01a9e27db31a98592f1dd4c11f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 17 Jan 2020 18:36:02 +0100 Subject: [PATCH 029/142] Improved cmake --- CMakeLists.txt | 26 +++++++++++--------------- 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 618703d..0f634f9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,53 +100,49 @@ endif() set(pain_sources src/fixpoint.cpp - src/fixpoint.h src/value_set.cpp - src/value_set.h src/simple_interval.cpp - src/simple_interval.h src/normalized_conjunction.cpp +) + +set(pain_headers + src/fixpoint.h + src/value_set.h + src/simple_interval.h src/normalized_conjunction.h ) +include_directories(${LLVM_INCLUDE_DIRS}) + add_llvm_library(llvm-pain MODULE ${pain_sources} + ${pain_headers} DEPENDS intrinsics_gen PLUGIN_TOOL opt ) -target_include_directories(llvm-pain PRIVATE - ${LLVM_INCLUDE_DIRS} -) - add_llvm_executable(simple_interval_test test/simple_interval_test.cpp + ${pain_headers} ${pain_sources} DEPENDS intrinsics_gen ) - target_include_directories(simple_interval_test PRIVATE - ${LLVM_INCLUDE_DIRS} -) - target_link_libraries(simple_interval_test ${LLVM_AVAILABLE_LIBS} ) add_llvm_executable(normalized_conjunction_test test/normalized_conjunction_test.cpp + ${pain_headers} ${pain_sources} DEPENDS intrinsics_gen ) - target_include_directories(normalized_conjunction_test PRIVATE - ${LLVM_INCLUDE_DIRS} -) - target_link_libraries(normalized_conjunction_test ${LLVM_AVAILABLE_LIBS} ) -- GitLab From f319716908face5ae032ce9339118ddb5a58f4a0 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 18 Jan 2020 01:19:40 +0100 Subject: [PATCH 030/142] refactor --- CMakeLists.txt | 2 + src/fixpoint.cpp | 6 +- src/fixpoint_two_var_eq.cpp | 16 +- src/linear_equality.cpp | 61 +++ src/linear_equality.h | 62 +++ src/normalized_conjunction.cpp | 547 ++++++++++++++------------- src/normalized_conjunction.h | 147 +++---- test/normalized_conjunction_test.cpp | 46 +-- 8 files changed, 492 insertions(+), 395 deletions(-) create mode 100644 src/linear_equality.cpp create mode 100644 src/linear_equality.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 0f634f9..1ca1734 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ set(pain_sources src/value_set.cpp src/simple_interval.cpp src/normalized_conjunction.cpp + src/linear_equality.cpp ) set(pain_headers @@ -110,6 +111,7 @@ set(pain_headers src/value_set.h src/simple_interval.h src/normalized_conjunction.h + src/linear_equality.h ) include_directories(${LLVM_INCLUDE_DIRS}) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 60281fb..250222a 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -339,19 +339,17 @@ void executeFixpointAlgorithm(llvm::Module const& M) { bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; - using AbstractState2 = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening - // executeFixpointAlgorithm (M); + executeFixpointAlgorithm (M); // executeFixpointAlgorithmWidening(M); - executeFixpointAlgorithmTwoVarEq(M); +// executeFixpointAlgorithmTwoVarEq(M); // We never change anything return false; } - void AbstractInterpretationPass::getAnalysisUsage(llvm::AnalysisUsage& info) const { info.setPreservesAll(); } diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp index 3db7a53..cf91e05 100644 --- a/src/fixpoint_two_var_eq.cpp +++ b/src/fixpoint_two_var_eq.cpp @@ -88,7 +88,6 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { dbgs(1) << " Merging function parameters, is entry block\n"; // if it is the entry node, then its state should be top - state_new.isBottom = false; state_new.merge(Merge_op::UPPER_BOUND, node.state); } @@ -99,7 +98,7 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { std::vector predecessors; for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - + AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; state_branched.branch(*bb, *node.bb); state_new.merge(Merge_op::UPPER_BOUND, state_branched); @@ -111,9 +110,6 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { // Apply the basic block dbgs(3) << " Applying basic block\n"; - if (state_new.isBottom) { - dbgs(3) << " Basic block is unreachable, everything is bottom\n"; - } else { // Applies all instrucions of a basic block for (llvm::Instruction const& inst: *node.bb) { @@ -133,16 +129,15 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { } // Handles merging points - if (llvm::dyn_cast(&inst)) { - - state_new.applyPHINode(*node.bb, predecessors, inst); + if (llvm::PHINode const* phi = llvm::dyn_cast(&inst)) { + + state_new.applyPHINode(*node.bb, predecessors, phi); // Handles function calls } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { // Checks if an input parameter for the callee is bottom. If so, // then skip the calculation of the call instruction for now - if (state_new.checkOperandsForBottom(inst)) continue; llvm::Function const* callee_func = call->getCalledFunction(); @@ -211,10 +206,9 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { } } } else { - if (state_new.checkOperandsForBottom(inst)) continue; state_new.applyDefault(inst); } - } + } // Merge the state back into the node diff --git a/src/linear_equality.cpp b/src/linear_equality.cpp new file mode 100644 index 0000000..07ca8b1 --- /dev/null +++ b/src/linear_equality.cpp @@ -0,0 +1,61 @@ +#include "linear_equality.h" + + +namespace pcpo { + +using namespace llvm; + +LinearEquality::LinearEquality(Value const* y) { + this->y = y; + this->a = 1; + this->x = y; + this->b = 0; +} + +LinearEquality::LinearEquality(Value const* y, int64_t a, Value const* x, int64_t b) { + this->y = y; + this->a = a; + this->x = x; + this->b = b; +} + +LinearEquality::LinearEquality(ConstantInt const* y) { + this->y = y; + this->a = 1; + this->x = nullptr; + this->b = y->getSExtValue(); +} + +raw_ostream& operator<<(raw_ostream& os, LinearEquality a) { + os << "{ "; + if (a.y != nullptr && a.y->hasName()) { + os << a.y->getName() << " = "; + } else if (a.y != nullptr) { + os << a.y << " = "; + } else { + os << " = "; + } + + if (a.x != nullptr) { + if (a.x->hasName()) { + os << a.a << " * " << a.x->getName(); + } else { + os << a.a << " * " << a.x; + } + + if (a.b > 0) { + os << " + " << a.b; + } else if (a.b < 0) { + os << a.b; + } + } else { + os << a.b; + } + + os << " }"; + + return os; +} + +} + diff --git a/src/linear_equality.h b/src/linear_equality.h new file mode 100644 index 0000000..41ae22f --- /dev/null +++ b/src/linear_equality.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include + +#include "global.h" + +namespace pcpo { + +class LinearEquality { + +public: + LinearEquality() = default; + LinearEquality(LinearEquality const&) = default; + LinearEquality(llvm::Value const* y); + LinearEquality(llvm::Value const* y, int64_t a, llvm::Value const* x, int64_t b); + LinearEquality(llvm::ConstantInt const* y); + // y = a * x + b + llvm::Value const* y; + // APInt would be nicer, but our anlysis doesnt care about bit width + int64_t a; + llvm::Value const* x; + int64_t b; + + inline bool operator<(LinearEquality const& rhs) const { + if (y == rhs.y) { + if (a == rhs.a) { + if (x == rhs.x) { + if (b == rhs.b) { + return false; + } else { + return b < rhs.b; + } + } else { + return x < rhs.x; + } + } else { + return a < rhs.a; + } + } else { + return y < rhs.y; + } + }; + + inline bool operator>(LinearEquality const& rhs) const { + return *this < rhs; + }; + + inline bool operator==(LinearEquality const& rhs) const { + return y == rhs.y && a == rhs.a && x == rhs.x && b == rhs.b; + }; + + inline bool operator!=(LinearEquality const& rhs) const { + return !(*this == rhs); + }; + + bool isConstant() const { return x == nullptr; }; +}; + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, LinearEquality a); + +} diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 26def36..5c3f7fd 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -1,112 +1,209 @@ +// +// normalized_conjunction.cpp +// ADTTests +// +// Created by Tim Gymnich on 17.1.20. +// + #include "normalized_conjunction.h" -#include -#include + +using namespace llvm; namespace pcpo { -using namespace llvm; +// MARK: - Initializers -// MARK: Initializers +NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { + for (llvm::Argument const& arg: f.args()) { + values[&arg] = LinearEquality(&arg); + } +} -NormalizedConjunction::NormalizedConjunction(Constant const& constant) { - if (ConstantInt const* c = dyn_cast(&constant)) { - state = NORMAL; - Equality eq = {&constant, 1, nullptr, c->getValue().getSExtValue()}; - equalaties = {{&constant,eq}}; - } else { - state = TOP; +NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call) { + assert(callee_func->arg_size() == call->getNumArgOperands()); + for (llvm::Argument const& arg: callee_func->args()) { + llvm::Value* value = call->getArgOperand(arg.getArgNo()); + if (value->getType()->isIntegerTy()) { + if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { + values[&arg] = LinearEquality(c); + } else { + values[&arg] = state.values.at(value); + } + } } } -NormalizedConjunction::NormalizedConjunction(std::map equalaties) { - this->equalaties = equalaties; - state = equalaties.empty() ? TOP : NORMAL; +NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { + this->values = equalaties; } -// MARK: AbstractDomain interface -NormalizedConjunction NormalizedConjunction::interpret( - Instruction const& inst, std::vector const& operands -) { - NormalizedConjunction a = operands[0]; - NormalizedConjunction b = operands[1]; +// MARK: - AbstractState Interface + +/// Handles the evaluation of merging points +void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, + llvm::PHINode const *phi) { + std::vector operands; + + // Phi nodes are handled here, to get the precise values of the predecessors + for (auto& incoming_state: pred_values) { + merge(Merge_op::UPPER_BOUND, incoming_state); + operands.push_back(incoming_state); // Keep the debug output happy + } +} + +void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state) { + std::vector operands; + +// // Keep the debug output happy +// for (llvm::Value const* value : inst.operand_values()) { +// operands.push_back(EqualityFoo(value)); +// } + + //iterate through all instructions of it till we find a return statement + for (auto& iter_inst: *end_block) { + if (llvm::ReturnInst const* ret_inst = llvm::dyn_cast(&iter_inst)) { + llvm::Value const* ret_val = ret_inst->getReturnValue(); + dbgs(4) << "\t\tFound return instruction\n"; + if (callee_state.values.find(ret_val) != callee_state.values.end()) { + dbgs(4) << "\t\tReturn evaluated, merging parameters\n"; + values[&inst] = callee_state.values.at(ret_val); + } else { + dbgs(4) << "\t\tReturn not evaluated, setting to bottom\n"; + } + } + } +// debug_output(inst, operands); +} + +void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { + llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); + if (ret_val->getType()->isIntegerTy()) { + if (llvm::ConstantInt const* c = llvm::dyn_cast(ret_val)) { + values[&inst] = LinearEquality(c); + } else if (values.find(ret_val) != values.end()) { + values[&inst] = values.at(ret_val); + } + } +} + +void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { + std::vector operands; - if (operands.size() != 2) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); + if (inst.getNumOperands() != 2) return nonDeterminsticAssignment(&inst); // We only deal with integer types IntegerType const* type = dyn_cast(inst.getType()); - // FIXME: maybe use nonDeterministic assignment here. - if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); + if (not type) return nonDeterminsticAssignment(&inst); type = dyn_cast(inst.getOperand(0)->getType()); - // FIXME: maybe use nonDeterministic assignment here. - if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); + if (not type) return nonDeterminsticAssignment(&inst); type = dyn_cast(inst.getOperand(1)->getType()); - // FIXME: maybe use nonDeterministic assignment here. - if (not type) return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); + if (not type) return nonDeterminsticAssignment(&inst); - unsigned bitWidth = inst.getOperand(0)->getType()->getIntegerBitWidth(); - assert(bitWidth == inst.getOperand(1)->getType()->getIntegerBitWidth()); + for (llvm::Value const* value: inst.operand_values()) { + operands.push_back(LinearEquality(value)); + } switch (inst.getOpcode()) { case Instruction::Add: - return NormalizedConjunction::Add(inst, a, b); + return Add(inst); case Instruction::Sub: - return NormalizedConjunction::Sub(inst, a, b); + return Sub(inst); case Instruction::Mul: - return NormalizedConjunction::Mul(inst, a, b); + return Mul(inst); case Instruction::SDiv: case Instruction::UDiv: default: - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(a,b), &inst); + return nonDeterminsticAssignment(&inst); } + + debug_output(inst, operands); } -NormalizedConjunction NormalizedConjunction::refineBranch(CmpInst::Predicate pred, Value const& lhs, Value const& rhs, NormalizedConjunction a, NormalizedConjunction b) { - // Do nothing - return a; +bool NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction const& other) { + + switch (op) { + case Merge_op::UPPER_BOUND: return leastUpperBound(other); + default: abort(); + } } +// MARK: Lattice Operations -NormalizedConjunction NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b) { -// if (a.isBottom()) return b; -// if (b.isBottom()) return a; -// if (a.isTop() || b.isTop()) return NormalizedConjunction {true}; +bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { + // set of all occuring variables in E1 and E2 + std::set vars, varsE1, varsE2; + std::set E1, E2; - // Note that T is handled above, so no need to convert the inputs + auto mapToSeccond = [](std::pair p){ return p.second; }; + transform(values, std::inserter(E1, E1.end()), mapToSeccond); + transform(rhs.values, std::inserter(E2, E2.end()), mapToSeccond); - switch (op) { - case Merge_op::UPPER_BOUND: - return leastUpperBound(a, b); - case Merge_op::WIDEN: - assert(false && "not implemented"); - case Merge_op::NARROW: - assert(false && "not implmented"); - default: - assert(false && "invalid op value"); - return NormalizedConjunction {true}; + auto mapToY = [](LinearEquality eq){ return eq.y; }; + transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); + transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); + std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); + + std::set dX1, dX2; + + std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); + std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); + + // extend E1 by trivial equalities + for (auto d: dX1) { + LinearEquality eq = {d, 1, d, 0}; + E1.insert(eq); } -} + + // extend E2 by trivial equalities + for (auto d: dX2) { + LinearEquality eq = {d, 1, d, 0}; + E2.insert(eq); + } + + std::set X0 = computeX0(E1, E2); + std::set X1 = computeX1(E1, E2); + std::set X2 = computeX2(E1, E2); + std::set X3 = computeX2(E2, E1); + std::set X4 = computeX4(E1, E2); + + // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 + + std::set leastUpperBound; + leastUpperBound.insert(X0.begin(), X0.end()); + leastUpperBound.insert(X1.begin(), X1.end()); + leastUpperBound.insert(X2.begin(), X2.end()); + leastUpperBound.insert(X3.begin(), X3.end()); + leastUpperBound.insert(X4.begin(), X4.end()); + + std::unordered_map result; + + auto addMapping = [](LinearEquality eq){ return std::make_pair(eq.y,eq); }; + transform(leastUpperBound, std::inserter(result, result.end()), addMapping); -// MARK: Helpers + values = result; + + return true; +} /// XO / E'0: set of variables where the right hand side in E1 and E2 coincide -std::set NormalizedConjunction::computeX0(std::set const& E1, std::set const& E2) { - std::set X0; +std::set NormalizedConjunction::computeX0(std::set const& E1, std::set const& E2) { + std::set X0; std::set_intersection(E1.begin(), E1.end(), E2.begin(), E2.end(), std::inserter(X0, X0.end())); // Remove trivial equalities - std::set filteredX0; - auto filterTrivialEqualaties = [](Equality eq){ return eq.y != eq.x;}; + std::set filteredX0; + auto filterTrivialEqualaties = [](LinearEquality eq){ return eq.y != eq.x;}; copy_if(X0, std::inserter(filteredX0, filteredX0.end()), filterTrivialEqualaties); return filteredX0; } /// X1 / E'1: set of variables where the right hand side is constant but does not coincide in E1 and E2 -std::set NormalizedConjunction::computeX1(std::set const& E1, std::set const& E2) { - std::set X1; - std::set> differentConstants; +std::set NormalizedConjunction::computeX1(std::set const& E1, std::set const& E2) { + std::set X1; + std::set> differentConstants; assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); @@ -121,7 +218,7 @@ std::set NormalizedConjunction::computeX1(std:: if (!differentConstants.empty()) { // pop first element - std::pair h = *differentConstants.begin(); + std::pair h = *differentConstants.begin(); differentConstants.erase(differentConstants.begin()); for (auto i: differentConstants) { @@ -131,7 +228,7 @@ std::set NormalizedConjunction::computeX1(std:: int64_t a = ((i.second.b - i.first.b)) / (h.second.b - h.first.b); auto x = h.first.y; int64_t b = -a * h.first.b + i.first.b; - Equality eq = {y, a, x, b}; + LinearEquality eq = {y, a, x, b}; X1.insert(eq); } } @@ -139,9 +236,9 @@ std::set NormalizedConjunction::computeX1(std:: } /// X2 / E'2: set of variables where the right hand side of E1 is constant but the rhs of E2 contains a variable. -std::set NormalizedConjunction::computeX2(std::set const& E1, std::set const& E2) { - std::set X2; - std::set> differentConstants; +std::set NormalizedConjunction::computeX2(std::set const& E1, std::set const& E2) { + std::set X2; + std::set> differentConstants; assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); @@ -154,10 +251,10 @@ std::set NormalizedConjunction::computeX2(std:: } } - std::vector>> Pi2; + std::vector>> Pi2; for (auto jt = differentConstants.begin(); jt != differentConstants.end();) { - std::set> equivalenceClass; + std::set> equivalenceClass; auto j = *jt; equivalenceClass.insert(j); jt = differentConstants.erase(jt); @@ -189,16 +286,16 @@ std::set NormalizedConjunction::computeX2(std:: auto a = i.second.a / h.second.a; auto x = h.first.y; auto b = i.second.b - (i.second.a * h.second.b) / h.second.a; - Equality eq = {y, a, x, b}; + LinearEquality eq = {y, a, x, b}; X2.insert(eq); } } return X2; } -std::set NormalizedConjunction::computeX4(std::set const& E1, std::set const& E2) { - std::set X4; - std::set> differentConstants; +std::set NormalizedConjunction::computeX4(std::set const& E1, std::set const& E2) { + std::set X4; + std::set> differentConstants; assert(E1.size() == E2.size() && "E1 and E2 should have the same set of variables in the same order"); @@ -211,17 +308,17 @@ std::set NormalizedConjunction::computeX4(std:: } } - std::vector>> Pi4; + std::vector>> Pi4; // partition differentConstants for (auto it = differentConstants.begin(); it != differentConstants.end();) { - std::set> equivalenceClass; - std::pair i = *it; + std::set> equivalenceClass; + std::pair i = *it; equivalenceClass.insert(i); it = differentConstants.erase(it); for (auto jt = it; jt != differentConstants.end(); ) { - std::pair j = *jt; + std::pair j = *jt; bool condition1 = i.first.x == j.first.x && i.second.x == j.second.x; bool condition2 = i.second.a / (i.first.a) == j.second.a / (j.first.a); bool condition3 = (i.first.b - i.second.b) / (i.first.a) == (j.first.b - j.second.b) / (j.first.a); @@ -247,124 +344,66 @@ std::set NormalizedConjunction::computeX4(std:: auto a = i.second.a / h.second.a; auto x = h.first.y; auto b = i.second.b - (i.second.a * h.second.b) / (h.second.a); - Equality eq = {y, a, x, b}; + LinearEquality eq = {y, a, x, b}; X4.insert(eq); } } return X4; } -// MARK: Lattice Operations - -NormalizedConjunction NormalizedConjunction::leastUpperBound(NormalizedConjunction lhs, NormalizedConjunction rhs) { - // set of all occuring variables in E1 and E2 - std::set vars, varsE1, varsE2; - std::set E1, E2; - - auto mapToSeccond = [](std::pair p){ return p.second; }; - transform(lhs.equalaties, std::inserter(E1, E1.end()), mapToSeccond); - transform(rhs.equalaties, std::inserter(E2, E2.end()), mapToSeccond); - - auto mapToY = [](Equality eq){ return eq.y; }; - transform(E1, std::inserter(varsE1, varsE1.end()), mapToY); - transform(E2, std::inserter(varsE2, varsE2.end()), mapToY); - std::set_union(varsE1.begin(), varsE1.end(), varsE2.begin(), varsE2.end(), std::inserter(vars, vars.end())); - - std::set dX1, dX2; - - std::set_difference(vars.begin(), vars.end(), varsE1.begin(), varsE1.end(), std::inserter(dX1, dX1.end())); - std::set_difference(vars.begin(), vars.end(), varsE2.begin(), varsE2.end(), std::inserter(dX2, dX2.end())); - - // extend E1 by trivial equalities - for (auto d: dX1) { - Equality eq = {d, 1, d, 0}; - E1.insert(eq); - } - - // extend E2 by trivial equalities - for (auto d: dX2) { - Equality eq = {d, 1, d, 0}; - E2.insert(eq); - } - - std::set X0 = computeX0(E1, E2); - std::set X1 = computeX1(E1, E2); - std::set X2 = computeX2(E1, E2); - std::set X3 = computeX2(E2, E1); - std::set X4 = computeX4(E1, E2); - - // E1 U E2 = E'0 AND E'1 AND E'2 AND E'3 AND E'4 - - std::set leastUpperBound; - leastUpperBound.insert(X0.begin(), X0.end()); - leastUpperBound.insert(X1.begin(), X1.end()); - leastUpperBound.insert(X2.begin(), X2.end()); - leastUpperBound.insert(X3.begin(), X3.end()); - leastUpperBound.insert(X4.begin(), X4.end()); - - std::map result; - - auto addMapping = [](Equality eq){ return std::make_pair(eq.y,eq); }; - transform(leastUpperBound, std::inserter(result, result.end()), addMapping); - - return NormalizedConjunction(result); -} - // MARK: Abstract Assignments /// [xi := ?] -NormalizedConjunction NormalizedConjunction::nonDeterminsticAssignment(NormalizedConjunction E, Value const* xi) { +void NormalizedConjunction::nonDeterminsticAssignment( Value const* xi) { assert(xi != nullptr && "xi cannot be NULL"); - auto xj = E.equalaties[xi].x; + auto xj = values[xi].x; if (xi != xj && xj != 0) { - E.equalaties[xi] = {xi, 1, xi, 0}; + values[xi] = {xi, 1, xi, 0}; } else { // find all equations using xi - auto predicate = [&xi](std::pair p){ return p.second.x == xi && p.second.y != xi ;}; - auto it = std::find_if(E.equalaties.begin(), E.equalaties.end(), predicate); - if (it != E.equalaties.end()) { + auto predicate = [&xi](std::pair p){ return p.second.x == xi && p.second.y != xi ;}; + auto it = std::find_if(values.begin(), values.end(), predicate); + if (it != values.end()) { auto xk = (*it).second; - for (it = std::find_if(++it, E.equalaties.end(), predicate); - it != E.equalaties.end(); - it = std::find_if(++it, E.equalaties.end(), predicate)) { + for (it = std::find_if(++it, values.end(), predicate); + it != values.end(); + it = std::find_if(++it, values.end(), predicate)) { auto& xl = it->second; - E.equalaties[xl.y] = {xl.y, 1, xk.y, xl.b - xk.b}; + values[xl.y] = {xl.y, 1, xk.y, xl.b - xk.b}; } - E.equalaties[xk.y] = {xk.y, 1, xk.y, 0}; + values[xk.y] = {xk.y, 1, xk.y, 0}; } - E.equalaties[xi] = {xi, 1, xi, 0}; + values[xi] = {xi, 1, xi, 0}; } - return E; } /// [xi := a * xj + b] -NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunction E, Value const* xi, int64_t a, Value const* xj, int64_t b) { +void NormalizedConjunction::linearAssignment(Value const* xi, int64_t a, Value const* xj, int64_t b) { assert(xi != nullptr && "xi cannot be NULL"); - E = nonDeterminsticAssignment(E, xi); + nonDeterminsticAssignment(xi); // make sure xj exists - auto xjS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].x : nullptr; - auto bS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].b : 0; - auto aS = E.equalaties.find(xj) != E.equalaties.end() ? E.equalaties[xj].a : 1; + auto xjS = values.find(xj) != values.end() ? values[xj].x : nullptr; + auto bS = values.find(xj) != values.end() ? values[xj].b : 0; + auto aS = values.find(xj) != values.end() ? values[xj].a : 1; if (!(a % aS == 0 && (-bS - b) % aS == 0)) { // Precison loss due to int division! Abort - return E; + return; } if (xi > xjS) { - E.equalaties[xi] = {xi, aS * a, xjS, a * bS + b}; - return E; + values[xi] = {xi, aS * a, xjS, a * bS + b}; + return; } else { - auto pred = [&xjS](std::pair p){ return p.second.x == xjS && p.second.y != xjS; }; - for (auto xk: make_filter_range(E.equalaties, pred)) { - E.equalaties[xk.second.y] = {xk.second.y, xk.second.a * a/aS, xi, (-bS - b) / aS + xk.second.b}; + auto pred = [&xjS](std::pair p){ return p.second.x == xjS && p.second.y != xjS; }; + for (auto xk: make_filter_range(values, pred)) { + values[xk.second.y] = {xk.second.y, xk.second.a * a/aS, xi, (-bS - b) / aS + xk.second.b}; } - E.equalaties[xjS] = {xjS, a/aS, xi, (-bS - b) / aS}; + values[xjS] = {xjS, a/aS, xi, (-bS - b) / aS}; } - return E; } // MARK: Abstract Operations @@ -372,181 +411,159 @@ NormalizedConjunction NormalizedConjunction::linearAssignment(NormalizedConjunct // [xi := xj + b] // [xi := xj + xk] // [xi := bj + bk] -NormalizedConjunction NormalizedConjunction::Add(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); +void NormalizedConjunction::Add(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); + // [xi := bj + bk] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return linearAssignment(&inst, 1, nullptr, b1->getSExtValue() + b2->getSExtValue() ); // [xi := b + xj] - if (isa(op1) && isa(op2)) { + } else if (isa(op1) && isa(op2)) { auto b = dyn_cast(op1); - return linearAssignment(result, &inst, 1, op2, b->getSExtValue()); + return linearAssignment(&inst, 1, op2, b->getSExtValue()); // [xi := xj + b] } else if (isa(op2) && isa(op1)) { auto b = dyn_cast(op2); - return linearAssignment(result, &inst, 1, op1, b->getSExtValue()); + return linearAssignment(&inst, 1, op1, b->getSExtValue()); // [xi := xj + xk] } else if (isa(op1) && isa(op2)) { // [xi := bj + xk] - if (result.equalaties[op1].isConstant()) { - return linearAssignment(result, &inst, 1, op2, result.equalaties[op1].b); + if (values[op1].isConstant()) { + return linearAssignment(&inst, 1, op2, values[op1].b); // [xi := xj + bk] - } else if (result.equalaties[op2].isConstant()) { - return linearAssignment(result, &inst, 1, op1, result.equalaties[op2].b); + } else if (values[op2].isConstant()) { + return linearAssignment(&inst, 1, op1, values[op2].b); // [xi := xj + xk] } else { - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } // [xi := bj + bk] - } else if (isa(op1) && (isa(op2))) { - return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b + result.equalaties[op2].b); } else { assert(false); - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } } -// TODO: [xi := xj - x] -NormalizedConjunction NormalizedConjunction::Sub(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); +/// [xi := xj - x] +void NormalizedConjunction::Sub(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); - // [xi := b - xj] - if (isa(op1) && isa(op2)) { + // [xi := bj - bk] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return linearAssignment(&inst, 1, nullptr, b1->getSExtValue() - b2->getSExtValue() ); + // [xi := b - xj] + } else if (isa(op1) && isa(op2)) { auto b = dyn_cast(op1); - return linearAssignment(result, &inst, 1, op2, -b->getSExtValue()); + return linearAssignment(&inst, 1, op2, -b->getSExtValue()); // [xi := xj - b] } else if (isa(op2) && isa(op1)) { auto b = dyn_cast(op2); - return linearAssignment(result, &inst, 1, op1, -b->getSExtValue()); + return linearAssignment(&inst, 1, op1, -b->getSExtValue()); // [xi := xj - xk] } else if (isa(op1) && isa(op2)) { // [xi := bj - xk] - if (result.equalaties[op1].isConstant()) { - return linearAssignment(result, &inst, 1, op2, -result.equalaties[op1].b); + if (values[op1].isConstant()) { + return linearAssignment(&inst, 1, op2, -values[op1].b); // [xi := xj - bk] - } else if (result.equalaties[op2].isConstant()) { - return linearAssignment(result, &inst, 1, op1, -result.equalaties[op2].b); + } else if (values[op2].isConstant()) { + return linearAssignment(&inst, 1, op1, -values[op2].b); // [xi := xj - xk] } else { - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } - // [xi := bj + bk] - } else if (isa(op1) && (isa(op2))) { - return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b - result.equalaties[op2].b); } else { assert(false); - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } } // [xi := a * xj] -NormalizedConjunction NormalizedConjunction::Mul(Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs) { - auto result = leastUpperBound(lhs, rhs); +void NormalizedConjunction::Mul(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); + // [xi := aj * ak] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return linearAssignment(&inst, 1, nullptr, b1->getSExtValue() * b2->getSExtValue() ); // [xi := a * xj] - if (isa(op1) && isa(op2)) { + } else if (isa(op1) && isa(op2)) { auto a = dyn_cast(op1); - return linearAssignment(result, &inst, a->getSExtValue(), op2, 0); + return linearAssignment(&inst, a->getSExtValue(), op2, 0); // [xi := xj * a] } else if (isa(op2) && isa(op1)) { auto a = dyn_cast(op2); - return linearAssignment(result, &inst, a->getSExtValue(), op1, 0); + return linearAssignment(&inst, a->getSExtValue(), op1, 0); // [xi := xj * xk] } else if (isa(op1) && isa(op2)) { // [xi := aj * xk] - if (result.equalaties[op1].isConstant()) { - return linearAssignment(result, &inst, result.equalaties[op1].b, op2, 0); + if (values[op1].isConstant()) { + return linearAssignment(&inst, values[op1].b, op2, 0); // [xi := xj * ak] - } else if (result.equalaties[op2].isConstant()) { - return linearAssignment(result, &inst, result.equalaties[op1].b, op1, 0); + } else if (values[op2].isConstant()) { + return linearAssignment(&inst, values[op1].b, op1, 0); // [xi := xj * xk] } else { - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } - // [xi := aj * ak] - } else if (isa(op1) && (isa(op2))) { - return linearAssignment(result, &inst, 1, nullptr, result.equalaties[op1].b * result.equalaties[op2].b); } else { assert(false); - return nonDeterminsticAssignment(NormalizedConjunction::leastUpperBound(lhs,rhs), &inst); + return nonDeterminsticAssignment(&inst); } } -// MARK: Utils - -bool NormalizedConjunction::isNormalized() const { - bool result = true; - for (auto eq: equalaties) { - if (eq.second.isConstant()) { - auto containsY = [&eq](std::pair pair){ return pair.second.x == eq.second.y; }; - result &= none_of(equalaties, containsY); - } else { - auto occursElseWhere = [&eq](std::pair pair){ return eq.second.y != pair.second.y && eq.second.y == pair.second.x; }; - result &= none_of(equalaties, occursElseWhere); - } - } - - auto jGreaterI = [](std::pair pair){ return pair.second.y > pair.second.x;}; - result &= all_of(equalaties, jGreaterI); - return result; +// MARK: Debug + +void NormalizedConjunction::debug_output(llvm::Instruction const& inst, std::vector operands) { + dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; + {int i = 0; + for (llvm::Value const* value: inst.operand_values()) { + if (i) dbgs(3) << ", "; + if (value->getName().size()) dbgs(3) << '%' << value->getName() << " = "; + dbgs(3) << operands[i]; + ++i; + }} + dbgs(3) << '\n'; } -// MARK: Operators +void NormalizedConjunction::printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { + // @Speed: This is quadratic, could be linear + bool nothing = true; + for (std::pair const& i: values) { + bool read = false; + bool written = false; + for (llvm::Instruction const& inst: bb) { + if (&inst == i.first) written = true; + for (llvm::Value const* v: inst.operand_values()) { + if (v == i.first) read = true; + } + } -bool NormalizedConjunction::operator==(NormalizedConjunction rhs) const { - return getState() == NORMAL - ? rhs.getState() == NORMAL and equalaties == rhs.equalaties - : getState() == rhs.getState(); + if (read and not written) { + out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; + nothing = false; + } + } + if (nothing) { + out.indent(indentation) << "\n"; + } } -raw_ostream& operator<<(raw_ostream& os, NormalizedConjunction a) { - if (a.isBottom()) { - os << "[]"; - } else if(a.isTop()) { - os << "T"; - } else { - if (a.equalaties.size() > 0) { - os << "{ "; +void NormalizedConjunction::printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { + for (auto const& i: values) { + if (llvm::ReturnInst::classof(i.first)) { + out.indent(indentation) << " = " << i.second << '\n'; } else { - os << "{ }"; - } - for (auto it = a.equalaties.begin(); it != a.equalaties.end(); it++) { - auto eq = *it; - if (eq.second.y != nullptr && eq.second.y->hasName()) { - os << eq.second.y->getName() << " = "; - } else if (eq.second.y != nullptr) { - os << eq.second.y << " = "; - } else { - os << " = "; - } - - if (eq.second.x != nullptr) { - if (eq.second.x->hasName()) { - os << eq.second.a << " * " << eq.second.x->getName(); - } else { - os << eq.second.a << " * " << eq.second.x; - } - if (eq.second.b > 0) { - os << " + " << eq.second.b; - } else if (eq.second.b < 0) { - os << eq.second.b; - } - } else { - os << eq.second.b; - } - - if (std::next(it) == a.equalaties.end()) { - os << " }"; - } else { - os << ", "; - } + out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; } } - return os; } -} +} /* end of namespace pcpo */ diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index c2b4702..b4a2343 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -1,110 +1,71 @@ +// +// conjunction.h +// PAIN +// +// Created by Tim Gymnich on 17.1.20. +// + #pragma once -#include -#include +#include +#include +#include + #include -#include -#include -#include #include "global.h" -#include -#include +#include "linear_equality.h" namespace pcpo { class NormalizedConjunction { - - public: - enum State: char { - INVALID, BOTTOM = 1, NORMAL = 2, TOP = 4 - }; - // TODO: consider making this a computed value based on the equalaties - char state; - struct Equality { - // y = a * x + b - llvm::Value const* y; - // APInt would be nicer, but our anlysis doesnt care about bit width - int64_t a; - llvm::Value const* x; - int64_t b; - - inline bool operator<(Equality const& rhs) const { - if (y == rhs.y) { - if (a == rhs.a) { - if (x == rhs.x) { - if (b == rhs.b) { - return false; - } else { - return b < rhs.b; - } - } else { - return x < rhs.x; - } - } else { - return a < rhs.a; - } - } else { - return y < rhs.y; - } - }; - - inline bool operator>(Equality const& rhs) const { - return *this < rhs; - }; - - inline bool operator==(Equality const& rhs) const { - return y == rhs.y && a == rhs.a && x == rhs.x && b == rhs.b; - }; - - inline bool operator!=(Equality const& rhs) const { - return !(*this == rhs); - }; - - bool isConstant() const { return x == nullptr; }; - }; - std::map equalaties; - State getState() const { return equalaties.empty() ? TOP : NORMAL; }; - - // AbstractDomain interface - NormalizedConjunction(bool isTop = false): state{isTop ? TOP : BOTTOM} {} - NormalizedConjunction(std::map equalaties); - NormalizedConjunction(llvm::Constant const& constant); - static NormalizedConjunction interpret( - llvm::Instruction const& inst, std::vector const& operands - ); - static NormalizedConjunction refineBranch( - llvm::CmpInst::Predicate pred, llvm::Value const& lhs, llvm::Value const& rhs, - NormalizedConjunction a, NormalizedConjunction b - ); - static NormalizedConjunction merge(Merge_op::Type op, NormalizedConjunction a, NormalizedConjunction b); - static NormalizedConjunction leastUpperBound(NormalizedConjunction E1, NormalizedConjunction E2); +public: + std::unordered_map values; - // utils - bool isTop() const { return getState() == TOP; }; /* return equalities.empty() */ - bool isBottom() const { return getState() == BOTTOM; }; /* never? */ - bool isNormalized() const; + NormalizedConjunction() = default; + NormalizedConjunction(NormalizedConjunction const& state) = default; + NormalizedConjunction(std::unordered_map equalaties); - // TODO: [] operator ? - bool operator==(NormalizedConjunction other) const; - bool operator!=(NormalizedConjunction other) const {return !(*this == other);} + explicit NormalizedConjunction(llvm::Function const& f); + /// 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 NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call); - protected: - // Assignments - static NormalizedConjunction linearAssignment(NormalizedConjunction E, llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); - static NormalizedConjunction nonDeterminsticAssignment(NormalizedConjunction E, llvm::Value const* xi); - - static NormalizedConjunction Add(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); - static NormalizedConjunction Sub(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); - static NormalizedConjunction Mul(llvm::Instruction const& inst, NormalizedConjunction lhs, NormalizedConjunction rhs); + /// Handles the evaluation of merging points + void applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, llvm::PHINode const* phi); + /// Handles the evaluation of function calls + /// This is the "combine" function as described in "Compiler Design: Analysis and Transformation" + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state); + /// Handles the evaluation of return instructions + void applyReturnInst(llvm::Instruction const& inst); + /// Handles the evaluation of all other instructions + void applyDefault(llvm::Instruction const& inst); + bool merge(Merge_op::Type op, NormalizedConjunction const& other); + void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) { return; }; + bool leastUpperBound(NormalizedConjunction rhs); + + void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; + void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; + + // Abstract Assignments + void linearAssignment(llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); + void nonDeterminsticAssignment(llvm::Value const* xi); + +protected: + // Abstract Operators + void Add(llvm::Instruction const& inst); + void Sub(llvm::Instruction const& inst); + void Mul(llvm::Instruction const& inst); + + /// Used for debug output + void debug_output(llvm::Instruction const& inst, std::vector operands); - // Helpers - static std::set computeX0(std::set const& E1, std::set const& E2); - static std::set computeX1(std::set const& E1, std::set const& E2); - static std::set computeX2(std::set const& E1, std::set const& E2); - static std::set computeX4(std::set const& E1, std::set const& E2); + // Helpers + static std::set computeX0(std::set const& E1, std::set const& E2); + static std::set computeX1(std::set const& E1, std::set const& E2); + static std::set computeX2(std::set const& E1, std::set const& E2); + static std::set computeX4(std::set const& E1, std::set const& E2); }; -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NormalizedConjunction a); } diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index eb1f132..7c72255 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -5,6 +5,7 @@ #include #include "../src/normalized_conjunction.h" +#include "../src/linear_equality.h" using namespace pcpo; using namespace llvm; @@ -37,7 +38,7 @@ const Value *x10 = (Value *) 10; const Value *x11 = (Value *) 11; const Value *x12 = (Value *) 12; -const std::map E1 = { +const std::unordered_map E1 = { {x1, {x1, 1, x1, 0}}, {x2, {x2, 1, x2, 0}}, {x3, {x3, 1, x1, 0}}, @@ -52,7 +53,7 @@ const std::map E1 = { {x12, {x12, 1, nullptr, 3}} }; -const std::map E2 = { +const std::unordered_map E2 = { {x1, {x1, 1, x1, 0}}, {x2, {x2, 1, x2, 0}}, {x3, {x3, 1, x2, -5}}, @@ -67,13 +68,13 @@ const std::map E2 = { {x12, {x12, 4, x1, -5}} }; -auto mapToSeccond = [](std::pair p){ return p.second; }; +auto mapToSeccond = [](std::pair p){ return p.second; }; bool NormalizedConjunctionTest::runTestAll() { std::cout << "Testing all: "; bool result = false; - std::map expected = { + std::unordered_map expected = { {x4, {x4, 3, x2, 5}}, {x5, {x5, 3, x3, 15}}, {x7, {x7, 1, x6, -1}}, @@ -81,9 +82,10 @@ bool NormalizedConjunctionTest::runTestAll() { {x12, {x12, 2, x11, 1}} }; - auto actual = NormalizedConjunction::leastUpperBound(NormalizedConjunction(E1), NormalizedConjunction(E2)); + auto actual = NormalizedConjunction(E1); + actual.leastUpperBound(NormalizedConjunction(E2)); - result = actual.equalaties == expected; + result = actual.values == expected; std::cout << (result? "success" : "failed") << "\n"; return result; @@ -93,11 +95,11 @@ bool NormalizedConjunctionTest::runTestX0() { std::cout << "Testing X0: "; bool result = true; - std::set expected = { + std::set expected = { {x4, 3, x2, 5} }; - std::set E1Set, E2Set; + std::set E1Set, E2Set; transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); @@ -112,11 +114,11 @@ bool NormalizedConjunctionTest::runTestX1() { std::cout << "Testing X1: "; bool result = false; - std::set expected = { + std::set expected = { {x10, 2, x9, 2} }; - std::set E1Set, E2Set; + std::set E1Set, E2Set; transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); @@ -131,11 +133,11 @@ bool NormalizedConjunctionTest::runTestX2() { std::cout << "Testing X2: "; bool result = false; - std::set expected = { + std::set expected = { {x12, 2, x11, 1} }; - std::set E1Set, E2Set; + std::set E1Set, E2Set; transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); @@ -150,12 +152,12 @@ bool NormalizedConjunctionTest::runTestX4() { std::cout << "Testing X4: "; bool result = false; - std::set expected = { + std::set expected = { {x5, 3, x3, 15}, {x7, 1, x6, -1} }; - std::set E1Set, E2Set; + std::set E1Set, E2Set; transform(E1, std::inserter(E1Set, E1Set.end()), mapToSeccond); transform(E2, std::inserter(E2Set, E2Set.end()), mapToSeccond); @@ -180,9 +182,9 @@ bool NormalizedConjunctionTest::runNonDeterministicAssignmentTest1() { {x2, {x2, 1, x2, 0}}, }); - auto actual = NormalizedConjunctionTest::nonDeterminsticAssignment(E, x2); + E.nonDeterminsticAssignment(x2); - result = actual == expected; + result = E.values == expected.values; std::cout << (result? "success" : "failed") << "\n"; return result; } @@ -205,9 +207,9 @@ bool NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() { {x4, {x4, 1, x2, 8}} }); - auto actual = NormalizedConjunctionTest::nonDeterminsticAssignment(E, x1); + E.nonDeterminsticAssignment(x1); - result = actual == expected; + result = E.values == expected.values; std::cout << (result? "success" : "failed") << "\n"; return result; } @@ -229,9 +231,9 @@ bool NormalizedConjunctionTest::runLinearAssignmentTest1() { {x3, {x3, 1, x3, 0}} }); - auto actual = NormalizedConjunctionTest::linearAssignment(E, x2, 1, x1, 3); + E.linearAssignment(x2, 1, x1, 3); - result = actual == expected; + result = E.values == expected.values; std::cout << (result? "success" : "failed") << "\n"; return result; } @@ -258,9 +260,9 @@ bool NormalizedConjunctionTest::runLinearAssignmentTest2() { {x6, {x6, 1, x2, -10}} }); - auto actual = NormalizedConjunctionTest::linearAssignment(E, x2, 1, x4, 1); + E.linearAssignment(x2, 1, x4, 1); - result = actual == expected; + result = E.values == expected.values; std::cout << (result? "success" : "failed") << "\n"; return result; } -- GitLab From e4335487b5831a877dba0e83cd42350fb24d3f84 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 20 Jan 2020 13:42:11 +0100 Subject: [PATCH 031/142] fixed small issues --- src/fixpoint.cpp | 4 ++-- src/normalized_conjunction.cpp | 4 +++- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 250222a..fe87ff3 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -341,10 +341,10 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening - executeFixpointAlgorithm (M); +// executeFixpointAlgorithm (M); // executeFixpointAlgorithmWidening(M); -// executeFixpointAlgorithmTwoVarEq(M); + executeFixpointAlgorithmTwoVarEq(M); // We never change anything return false; diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 5c3f7fd..ca398f0 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -183,9 +183,11 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { auto addMapping = [](LinearEquality eq){ return std::make_pair(eq.y,eq); }; transform(leastUpperBound, std::inserter(result, result.end()), addMapping); + bool changed = values != result; + values = result; - return true; + return changed; } /// XO / E'0: set of variables where the right hand side in E1 and E2 coincide -- GitLab From a59c8ab3e3b9cb707cf5d557d6b0759090cba997 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 20 Jan 2020 19:01:23 +0100 Subject: [PATCH 032/142] fixed headers --- CMakeLists.txt | 5 +++++ src/fixpoint.cpp | 6 ++++-- src/fixpoint_two_var_eq.cpp | 1 + src/fixpoint_two_var_eq.h | 12 ++++++++++++ src/fixpoint_widening.cpp | 1 + src/general.h | 5 +++-- 6 files changed, 26 insertions(+), 4 deletions(-) create mode 100644 src/fixpoint_two_var_eq.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ca1734..7361ef3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -100,6 +100,8 @@ endif() set(pain_sources src/fixpoint.cpp + src/fixpoint_widening.cpp + src/fixpoint_two_var_eq.cpp src/value_set.cpp src/simple_interval.cpp src/normalized_conjunction.cpp @@ -112,6 +114,9 @@ set(pain_headers src/simple_interval.h src/normalized_conjunction.h src/linear_equality.h + src/general.h + src/global.h + src/callstring.h ) include_directories(${LLVM_INCLUDE_DIRS}) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index fe87ff3..0b9f944 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -11,12 +11,14 @@ #include "general.h" #include "global.h" #include "callstring.h" -#include "fixpoint_widening.cpp" -#include "fixpoint_two_var_eq.cpp" + #include "value_set.h" #include "simple_interval.h" #include "normalized_conjunction.h" +#include "fixpoint_widening.cpp" +#include "fixpoint_two_var_eq.cpp" + namespace pcpo { diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp index cf91e05..f471391 100644 --- a/src/fixpoint_two_var_eq.cpp +++ b/src/fixpoint_two_var_eq.cpp @@ -7,6 +7,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "global.h" +#include "general.h" #include "callstring.h" #include "value_set.h" diff --git a/src/fixpoint_two_var_eq.h b/src/fixpoint_two_var_eq.h new file mode 100644 index 0000000..d6d6aff --- /dev/null +++ b/src/fixpoint_two_var_eq.h @@ -0,0 +1,12 @@ +#pragma once + +#include "llvm/Pass.h" +#include "fixpoint.h" +#include "normalized_conjunction.h" + +namespace pcpo { + +template +void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M); + +} /* end of namespace pcpo */ diff --git a/src/fixpoint_widening.cpp b/src/fixpoint_widening.cpp index 482af2e..feb2749 100644 --- a/src/fixpoint_widening.cpp +++ b/src/fixpoint_widening.cpp @@ -7,6 +7,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "global.h" +#include "general.h" #include "callstring.h" #include "value_set.h" #include "simple_interval.h" diff --git a/src/general.h b/src/general.h index 4f519ee..09854a0 100644 --- a/src/general.h +++ b/src/general.h @@ -1,3 +1,4 @@ +#pragma once #include "llvm/ADT/Hashing.h" // C++ unordered_maps don't support tuples as keys, which is why one has to define the hash function for said tuple. @@ -28,7 +29,7 @@ namespace pcpo { typedef std::tuple bb_key; // Helper functions for debug output, pretty much self explanatory. - std::string _bb_to_str(llvm::BasicBlock const* bb) { + static std::string _bb_to_str(llvm::BasicBlock const* bb) { std::string str = "%"; if (llvm::Function const* f = bb->getParent()) { str.append(f->getName()); @@ -38,7 +39,7 @@ namespace pcpo { return str; } - std::string _bb_key_to_str(bb_key key) { + static std::string _bb_key_to_str(bb_key key) { std::string str = "["; str.append(_bb_to_str(std::get<0>(key))); str.append(", "); -- GitLab From c93be0b7b62c3bb6c77eda6b931db7137c934dab Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 20 Jan 2020 21:25:11 +0100 Subject: [PATCH 033/142] refactor fixpoint --- src/fixpoint_two_var_eq.cpp | 369 +++++++++++++++++++----------------- 1 file changed, 194 insertions(+), 175 deletions(-) diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp index f471391..b349990 100644 --- a/src/fixpoint_two_var_eq.cpp +++ b/src/fixpoint_two_var_eq.cpp @@ -13,63 +13,203 @@ namespace pcpo { -// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface -// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods -// with the right signatures and take care to fulfil the contracts outlines above). -// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in -// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as -// well. -// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I -// recommend Meld.) +using namespace llvm; +using namespace std; + + +// A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data +// per node to execute the fixpoint algorithm. template -void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { - constexpr int iterations_max = 1000; +struct Node { + BasicBlock const* bb; + BasicBlock const* callstring; + AbstractState state = {}; + bool update_scheduled = false; // Whether the node is already in the worklist + + // If this is set, the algorithm will add the initial values from the parameters of the + // function to the incoming values, which is the correct thing to do for initial basic + // blocks. + Function const* func_entry = nullptr; +}; + +template +static void register_basic_blocks(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes) { + for (BasicBlock const& bb: *main_func) { + dbgs(1) << " Found basic block main." << bb.getName() << '\n'; + // nodes of main block have the callstring of a dummy block + nodes[{&bb, dummy_block}] = { &bb, dummy_block }; + } +} + +template +static void push_to_worklist(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes, vector &worklist) { + auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); + worklist.push_back(init_element); + nodes[init_element].update_scheduled = true; + nodes[init_element].state = AbstractState {*main_func}; + nodes[init_element].func_entry = main_func; +} + +template +static vector collect_predecessors(Node &node, unordered_map> &nodes, AbstractState &state_new) { + vector predecessors; + for (BasicBlock const* bb: llvm::predecessors(node.bb)) { + dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; + + AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; + state_branched.branch(*bb, *node.bb); + state_new.merge(Merge_op::UPPER_BOUND, state_branched); + predecessors.push_back(state_branched); + } + return predecessors; +} + +template +static void apply_instructions(Node &node, unordered_map> &nodes, vector &predecessors, AbstractState state_new, vector &worklist) { + using Node = Node; + + for (Instruction const& inst: *node.bb) { + + // Handles return instructions + if (isa(inst)) { + state_new.applyReturnInst(inst); + } + + // If the result of the instruction is not used, there is no reason to compute + // it. (There are no side-effects in LLVM IR. (I hope.)) + // Except for call instructions, we still want to get that information + if (inst.use_empty() && not isa(inst)) { + dbgs(3) << " Empty use of instruction, skipping...\n"; + } else if (PHINode const* phi = dyn_cast(&inst)) { + // Handles merging points + state_new.applyPHINode(*node.bb, predecessors, phi); + + // Handles function calls + } else if (CallInst const* call = dyn_cast(&inst)) { + // Checks if an input parameter for the callee is bottom. If so, + // then skip the calculation of the call instruction for now + + Function const* callee_func = call->getCalledFunction(); + + // Checks for functions, such as printf and skips them + if (callee_func->empty()) { + dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; + continue; + } + + auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.bb); + bool changed = false; + + // Checks whether a node with key [%callee entry block, %caller basic block], + // i.e. an entry block with callstring of caller basic block, exists. + // If not, all nodes with their corrosponding keys are initilized for the callee function. + if (nodes.find(callee_element) == nodes.end()) { + // Check if abstract_state of call.bb is bottom or not + dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; + + // Register basic blocks + for (BasicBlock const& bb : *callee_func) { + dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; + + Node callee_node = {&bb, node.bb}; + nodes[make_tuple(&bb, node.bb)] = callee_node; + } + + nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; + nodes[callee_element].func_entry = callee_func; + changed = true; + } else { + AbstractState state_update{ callee_func, state_new, call }; + changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); + } + + //Getting the last block + BasicBlock const* end_block = &*prev(callee_func->end()); + auto end_element = make_tuple(end_block, node.bb); + + state_new.applyCallInst(inst, end_block, nodes[end_element].state); + + // If input parameters have changed, we want to interpret the function once again + // and reevaluate the nodes of possible callers. + if (changed) { + for (pair& i : nodes) { + if (get<0>(i.first) == node.bb and not i.second.update_scheduled) { + dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; + worklist.push_back(i.first); + i.second.update_scheduled = true; + } + } + + // Checks if the key of the callee functions entry node is already on the worklist, + // this is necessary for recursions. + if (not nodes[callee_element].update_scheduled) { + worklist.push_back(callee_element); + nodes[callee_element].update_scheduled = true; + + dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; + } else { + dbgs(3) << " Callee already on worklist, nothing to add...\n"; + } + } + } else { + state_new.applyDefault(inst); + } + } +} + +template +static void update_successors(Node &node, unordered_map> &nodes, vector &worklist) { + for (BasicBlock const* succ_bb: successors(node.bb)) { + auto succ_key = make_tuple(succ_bb, node.callstring); + Node& succ = nodes[succ_key]; + if (not succ.update_scheduled) { + worklist.push_back(succ_key); + succ.update_scheduled = true; + dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; + } + } +} - // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data - // per node to execute the fixpoint algorithm. - struct Node { - llvm::BasicBlock const* bb; - llvm::BasicBlock const* callstring; - AbstractState state; - bool update_scheduled = false; // Whether the node is already in the worklist +template +static void output_final_result(unordered_map> const& nodes) { + dbgs(0) << "\nFinal result:\n"; + for (pair> i: nodes) { + dbgs(0) << _bb_key_to_str(i.first) << ":\n"; + i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); + } +} - // If this is set, the algorithm will add the initial values from the parameters of the - // function to the incoming values, which is the correct thing to do for initial basic - // blocks. - llvm::Function const* func_entry = nullptr; - }; +/// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface +/// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods +/// with the right signatures and take care to fulfil the contracts outlines above). +/// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in +/// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as +/// well. +/// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I +/// recommend Meld.) +template +void executeFixpointAlgorithmTwoVarEq(Module const& M) { + using Node = Node; + + constexpr int iterations_max = 1000; - std::unordered_map nodes; - std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed + unordered_map nodes; + vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed // We only consider the main function in the beginning. If no main exists, nothing is evaluated! - llvm::Function const* main_func = M.getFunction("main"); + Function const* main_func = M.getFunction("main"); //creating dummy block for callstrings of the main block, since the main function is not called from another function - llvm::BasicBlock const* dummy_block = llvm::BasicBlock::Create(M.getContext(), "dummy"); + BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); // TODO: Check what this does for release clang, probably write out a warning dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Register basic blocks of the main function - for (llvm::BasicBlock const& bb: *main_func) { - dbgs(1) << " Found basic block main." << bb.getName() << '\n'; - - Node node; - node.bb = &bb; - node.callstring = dummy_block; - // node.state is default initialised (to bottom) - - // nodes of main block have the callstring of a dummy block - nodes[std::make_tuple(&bb, dummy_block)] = node; - } + register_basic_blocks(dummy_block, main_func, nodes); // Push the initial block into the worklist - auto init_element = std::make_tuple(&main_func->getEntryBlock(), dummy_block); - worklist.push_back(init_element); - nodes[init_element].update_scheduled = true; - nodes[init_element].state = AbstractState {*main_func}; - nodes[init_element].func_entry = main_func; + push_to_worklist(dummy_block, main_func, nodes, worklist); dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; @@ -86,155 +226,39 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { AbstractState state_new; // Set to bottom if (node.func_entry) { - dbgs(1) << " Merging function parameters, is entry block\n"; - + dbgs(1) << " Merging function parameters, is entry block\n"; // if it is the entry node, then its state should be top state_new.merge(Merge_op::UPPER_BOUND, node.state); } - dbgs(1) << " Merge of " << llvm::pred_size(node.bb) - << (llvm::pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); + dbgs(1) << " Merge of " << pred_size(node.bb) + << (pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); // Collect the predecessors - std::vector predecessors; - for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { - dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - - AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; - state_branched.branch(*bb, *node.bb); - state_new.merge(Merge_op::UPPER_BOUND, state_branched); - predecessors.push_back(state_branched); - } + vector predecessors = collect_predecessors(node, nodes, state_new); dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); // Apply the basic block dbgs(3) << " Applying basic block\n"; - // Applies all instrucions of a basic block - for (llvm::Instruction const& inst: *node.bb) { - - // Handles return instructions - if (llvm::dyn_cast(&inst)) { - state_new.applyReturnInst(inst); - } - - // If the result of the instruction is not used, there is no reason to compute - // it. (There are no side-effects in LLVM IR. (I hope.)) - if (inst.use_empty()) { - // Except for call instructions, we still want to get that information - if (not llvm::dyn_cast(&inst)) { - dbgs(3) << " Empty use of instruction, skipping...\n"; - continue; - } - } - - // Handles merging points - if (llvm::PHINode const* phi = llvm::dyn_cast(&inst)) { - - state_new.applyPHINode(*node.bb, predecessors, phi); - - // Handles function calls - } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { - - // Checks if an input parameter for the callee is bottom. If so, - // then skip the calculation of the call instruction for now - - llvm::Function const* callee_func = call->getCalledFunction(); - - // Checks for functions, such as printf and skips them - if (callee_func->empty()) { - dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; - continue; - } - - auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.bb); - bool changed; - - // Checks whether a node with key [%callee entry block, %caller basic block], - // i.e. an entry block with callstring of caller basic block, exists. - // If not, all nodes with their corrosponding keys are initilized for the callee function. - if (nodes.find(callee_element) == nodes.end()) { - // Check if abstract_state of call.bb is bottom or not - dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; - - // Register basic blocks - for (llvm::BasicBlock const& bb : *callee_func) { - dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; - - Node callee_node; - callee_node.bb = &bb; - callee_node.callstring = node.bb; - // node.state is default initialised (to bottom) - - nodes[std::make_tuple(&bb, node.bb)] = callee_node; - } - - nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; - nodes[callee_element].func_entry = callee_func; - changed = true; - } else { - AbstractState state_update{ callee_func, state_new, call }; - changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); - } - - //Getting the last block - llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); - auto end_element = std::make_tuple(end_block, node.bb); - - state_new.applyCallInst(inst, end_block, nodes[end_element].state); - - // If input parameters have changed, we want to interpret the function once again - // and reevaluate the nodes of possible callers. - if (changed) { - for (std::pair& i : nodes) { - if (std::get<0>(i.first) == node.bb and not i.second.update_scheduled) { - dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; - worklist.push_back(i.first); - i.second.update_scheduled = true; - } - } - - // Checks if the key of the callee functions entry node is already on the worklist, - // this is necessary for recursions. - if (not nodes[callee_element].update_scheduled) { - worklist.push_back(callee_element); - nodes[callee_element].update_scheduled = true; - - dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; - } else { - dbgs(3) << " Callee already on worklist, nothing to add...\n"; - } - } - } else { - state_new.applyDefault(inst); - } - - } + // Applies all instrucions of a basic block + apply_instructions(node, nodes, predecessors, state_new, worklist); // Merge the state back into the node - dbgs(3) << " Merging with stored state\n"; + dbgs(3) << " Merging with stored state\n"; bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); - dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); + dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); // No changes, so no need to do anything else if (not changed) continue; - dbgs(2) << " State changed, notifying " << llvm::succ_size(node.bb) - << (llvm::succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); + dbgs(2) << " State changed, notifying " << succ_size(node.bb) + << (succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); // Something changed and we will need to update the successors - for (llvm::BasicBlock const* succ_bb: llvm::successors(node.bb)) { - auto succ_key = std::make_tuple(succ_bb, node.callstring); - Node& succ = nodes[succ_key]; - if (not succ.update_scheduled) { - worklist.push_back(succ_key); - succ.update_scheduled = true; - - dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; - } - } + update_successors(node, nodes, worklist); } if (!worklist.empty()) { @@ -242,12 +266,7 @@ void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M) { } // Output the final result - dbgs(0) << "\nFinal result:\n"; - for (std::pair i: nodes) { - dbgs(0) << _bb_key_to_str(i.first) << ":\n"; - i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); - } - + output_final_result(nodes); } } /* end of namespace pcpo */ -- GitLab From f5ca842549d71eba7b180ab0710e39bf53650a6b Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 28 Jan 2020 17:53:22 +0100 Subject: [PATCH 034/142] renamed fields in node --- src/fixpoint.cpp | 69 +++++++++++++++++++++++++----------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 0b9f944..31bf585 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -95,6 +95,19 @@ public: void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; }; +template +struct Node { + llvm::BasicBlock const* basic_block; + llvm::BasicBlock const* callstring; + AbstractState state = {}; + bool update_scheduled = false; // Whether the node is already in the worklist + + // If this is set, the algorithm will add the initial values from the parameters of the + // function to the incoming values, which is the correct thing to do for initial basic + // blocks. + llvm::Function const* func_entry = nullptr; +}; + // Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface // documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods @@ -107,20 +120,10 @@ public: template void executeFixpointAlgorithm(llvm::Module const& M) { constexpr int iterations_max = 1000; + using Node = Node; // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data // per node to execute the fixpoint algorithm. - struct Node { - llvm::BasicBlock const* bb; - llvm::BasicBlock const* callstring; - AbstractState state; - bool update_scheduled = false; // Whether the node is already in the worklist - - // If this is set, the algorithm will add the initial values from the parameters of the - // function to the incoming values, which is the correct thing to do for initial basic - // blocks. - llvm::Function const* func_entry = nullptr; - }; std::unordered_map nodes; std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed @@ -139,7 +142,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(1) << " Found basic block main." << bb.getName() << '\n'; Node node; - node.bb = &bb; + node.basic_block = &bb; node.callstring = dummy_block; // node.state is default initialised (to bottom) @@ -163,7 +166,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { node.update_scheduled = false; dbgs(1) << "\nIteration " << iter << ", considering basic block " - << _bb_to_str(node.bb) << " with callstring " + << _bb_to_str(node.basic_block) << " with callstring " << _bb_to_str(node.callstring) << '\n'; AbstractState state_new; // Set to bottom @@ -176,21 +179,21 @@ void executeFixpointAlgorithm(llvm::Module const& M) { state_new.merge(Merge_op::UPPER_BOUND, node.state); } - dbgs(1) << " Merge of " << llvm::pred_size(node.bb) - << (llvm::pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); + dbgs(1) << " Merge of " << llvm::pred_size(node.basic_block) + << (llvm::pred_size(node.basic_block) != 1 ? " predecessors.\n" : " predecessor.\n"); // Collect the predecessors std::vector predecessors; - for (llvm::BasicBlock const* bb: llvm::predecessors(node.bb)) { + for (llvm::BasicBlock const* bb: llvm::predecessors(node.basic_block)) { dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; - state_branched.branch(*bb, *node.bb); + state_branched.branch(*bb, *node.basic_block); state_new.merge(Merge_op::UPPER_BOUND, state_branched); predecessors.push_back(state_branched); } - dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); + dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.basic_block, dbgs(2), 4); // Apply the basic block dbgs(3) << " Applying basic block\n"; @@ -199,7 +202,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(3) << " Basic block is unreachable, everything is bottom\n"; } else { // Applies all instrucions of a basic block - for (llvm::Instruction const& inst: *node.bb) { + for (llvm::Instruction const& inst: *node.basic_block) { // Handles return instructions if (llvm::dyn_cast(&inst)) { @@ -219,7 +222,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Handles merging points if (llvm::dyn_cast(&inst)) { - state_new.applyPHINode(*node.bb, predecessors, inst); + state_new.applyPHINode(*node.basic_block, predecessors, inst); // Handles function calls } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { @@ -236,7 +239,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { continue; } - auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.bb); + auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.basic_block); bool changed; // Checks whether a node with key [%callee entry block, %caller basic block], @@ -251,11 +254,11 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; Node callee_node; - callee_node.bb = &bb; - callee_node.callstring = node.bb; + callee_node.basic_block = &bb; + callee_node.callstring = node.basic_block; // node.state is default initialised (to bottom) - nodes[std::make_tuple(&bb, node.bb)] = callee_node; + nodes[std::make_tuple(&bb, node.basic_block)] = callee_node; } nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; @@ -268,7 +271,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { //Getting the last block llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); - auto end_element = std::make_tuple(end_block, node.bb); + auto end_element = std::make_tuple(end_block, node.basic_block); state_new.applyCallInst(inst, end_block, nodes[end_element].state); @@ -276,7 +279,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // and reevaluate the nodes of possible callers. if (changed) { for (std::pair& i : nodes) { - if (std::get<0>(i.first) == node.bb and not i.second.update_scheduled) { + if (std::get<0>(i.first) == node.basic_block and not i.second.update_scheduled) { dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; worklist.push_back(i.first); i.second.update_scheduled = true; @@ -305,16 +308,16 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(3) << " Merging with stored state\n"; bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); - dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); + dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); // No changes, so no need to do anything else if (not changed) continue; - dbgs(2) << " State changed, notifying " << llvm::succ_size(node.bb) - << (llvm::succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); + dbgs(2) << " State changed, notifying " << llvm::succ_size(node.basic_block) + << (llvm::succ_size(node.basic_block) != 1 ? " successors\n" : " successor\n"); // Something changed and we will need to update the successors - for (llvm::BasicBlock const* succ_bb: llvm::successors(node.bb)) { + for (llvm::BasicBlock const* succ_bb: llvm::successors(node.basic_block)) { auto succ_key = std::make_tuple(succ_bb, node.callstring); Node& succ = nodes[succ_key]; if (not succ.update_scheduled) { @@ -334,7 +337,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(0) << "\nFinal result:\n"; for (std::pair i: nodes) { dbgs(0) << _bb_key_to_str(i.first) << ":\n"; - i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); + i.second.state.printOutgoing(*i.second.basic_block, dbgs(0), 2); } } @@ -343,10 +346,10 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening -// executeFixpointAlgorithm (M); + executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); - executeFixpointAlgorithmTwoVarEq(M); +// executeFixpointAlgorithmTwoVarEq(M); // We never change anything return false; -- GitLab From 41d17c0079a03bbfd98ad27ef98e2c5885147cbb Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 28 Jan 2020 17:56:36 +0100 Subject: [PATCH 035/142] added template parameters --- src/fixpoint.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 31bf585..4f569f7 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -117,9 +117,11 @@ struct Node { // well. // Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I // recommend Meld.) -template +template void executeFixpointAlgorithm(llvm::Module const& M) { - constexpr int iterations_max = 1000; using Node = Node; // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data @@ -176,7 +178,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // if it is the entry node, then its state should be top state_new.isBottom = false; - state_new.merge(Merge_op::UPPER_BOUND, node.state); + state_new.merge(merge_op, node.state); } dbgs(1) << " Merge of " << llvm::pred_size(node.basic_block) @@ -189,7 +191,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; state_branched.branch(*bb, *node.basic_block); - state_new.merge(Merge_op::UPPER_BOUND, state_branched); + state_new.merge(merge_op, state_branched); predecessors.push_back(state_branched); } @@ -266,7 +268,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { changed = true; } else { AbstractState state_update{ callee_func, state_new, call }; - changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); + changed = nodes[callee_element].state.merge(merge_op, state_update); } //Getting the last block @@ -306,7 +308,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Merge the state back into the node dbgs(3) << " Merging with stored state\n"; - bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); + bool changed = node.state.merge(merge_op, state_new); dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); -- GitLab From 9a4b29a3b83df00e9bea56f5b88140fdc70c4891 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 28 Jan 2020 18:01:22 +0100 Subject: [PATCH 036/142] using namespace llvm and std --- src/fixpoint.cpp | 71 +++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 34 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 4f569f7..2c2bc70 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -22,6 +22,9 @@ namespace pcpo { +using namespace llvm; +using namespace std; + static llvm::RegisterPass Y("painpass", "AbstractInterpretation Pass"); char AbstractInterpretationPass::ID; @@ -97,15 +100,15 @@ public: template struct Node { - llvm::BasicBlock const* basic_block; - llvm::BasicBlock const* callstring; + BasicBlock const* basic_block; + BasicBlock const* callstring; AbstractState state = {}; bool update_scheduled = false; // Whether the node is already in the worklist // If this is set, the algorithm will add the initial values from the parameters of the // function to the incoming values, which is the correct thing to do for initial basic // blocks. - llvm::Function const* func_entry = nullptr; + Function const* func_entry = nullptr; }; @@ -121,26 +124,26 @@ template -void executeFixpointAlgorithm(llvm::Module const& M) { +void executeFixpointAlgorithm(Module const& M) { using Node = Node; // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data // per node to execute the fixpoint algorithm. - std::unordered_map nodes; - std::vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed + unordered_map nodes; + vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed // We only consider the main function in the beginning. If no main exists, nothing is evaluated! - llvm::Function const* main_func = M.getFunction("main"); + Function const* main_func = M.getFunction("main"); //creating dummy block for callstrings of the main block, since the main function is not called from another function - llvm::BasicBlock const* dummy_block = llvm::BasicBlock::Create(M.getContext(), "dummy"); + BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); // TODO: Check what this does for release clang, probably write out a warning dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Register basic blocks of the main function - for (llvm::BasicBlock const& bb: *main_func) { + for (BasicBlock const& bb: *main_func) { dbgs(1) << " Found basic block main." << bb.getName() << '\n'; Node node; @@ -149,11 +152,11 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // node.state is default initialised (to bottom) // nodes of main block have the callstring of a dummy block - nodes[std::make_tuple(&bb, dummy_block)] = node; + nodes[make_tuple(&bb, dummy_block)] = node; } // Push the initial block into the worklist - auto init_element = std::make_tuple(&main_func->getEntryBlock(), dummy_block); + auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); worklist.push_back(init_element); nodes[init_element].update_scheduled = true; nodes[init_element].state = AbstractState {*main_func}; @@ -181,15 +184,15 @@ void executeFixpointAlgorithm(llvm::Module const& M) { state_new.merge(merge_op, node.state); } - dbgs(1) << " Merge of " << llvm::pred_size(node.basic_block) - << (llvm::pred_size(node.basic_block) != 1 ? " predecessors.\n" : " predecessor.\n"); + dbgs(1) << " Merge of " << pred_size(node.basic_block) + << (pred_size(node.basic_block) != 1 ? " predecessors.\n" : " predecessor.\n"); // Collect the predecessors - std::vector predecessors; - for (llvm::BasicBlock const* bb: llvm::predecessors(node.basic_block)) { + vector predecessors; + for (BasicBlock const* bb: llvm::predecessors(node.basic_block)) { dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - AbstractState state_branched {nodes[std::make_tuple(bb, node.callstring)].state}; + AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; state_branched.branch(*bb, *node.basic_block); state_new.merge(merge_op, state_branched); predecessors.push_back(state_branched); @@ -204,10 +207,10 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(3) << " Basic block is unreachable, everything is bottom\n"; } else { // Applies all instrucions of a basic block - for (llvm::Instruction const& inst: *node.basic_block) { + for (Instruction const& inst: *node.basic_block) { // Handles return instructions - if (llvm::dyn_cast(&inst)) { + if (isa(&inst)) { state_new.applyReturnInst(inst); } @@ -215,25 +218,25 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // it. (There are no side-effects in LLVM IR. (I hope.)) if (inst.use_empty()) { // Except for call instructions, we still want to get that information - if (not llvm::dyn_cast(&inst)) { + if (not isa(&inst)) { dbgs(3) << " Empty use of instruction, skipping...\n"; continue; } } // Handles merging points - if (llvm::dyn_cast(&inst)) { + if (isa(&inst)) { state_new.applyPHINode(*node.basic_block, predecessors, inst); // Handles function calls - } else if (llvm::CallInst const* call = llvm::dyn_cast(&inst)) { + } else if (CallInst const* call = dyn_cast(&inst)) { // Checks if an input parameter for the callee is bottom. If so, // then skip the calculation of the call instruction for now if (state_new.checkOperandsForBottom(inst)) continue; - llvm::Function const* callee_func = call->getCalledFunction(); + Function const* callee_func = call->getCalledFunction(); // Checks for functions, such as printf and skips them if (callee_func->empty()) { @@ -241,7 +244,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { continue; } - auto callee_element = std::make_tuple(&callee_func->getEntryBlock(), node.basic_block); + auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.basic_block); bool changed; // Checks whether a node with key [%callee entry block, %caller basic block], @@ -252,7 +255,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; // Register basic blocks - for (llvm::BasicBlock const& bb : *callee_func) { + for (BasicBlock const& bb : *callee_func) { dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; Node callee_node; @@ -260,7 +263,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { callee_node.callstring = node.basic_block; // node.state is default initialised (to bottom) - nodes[std::make_tuple(&bb, node.basic_block)] = callee_node; + nodes[make_tuple(&bb, node.basic_block)] = callee_node; } nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; @@ -272,16 +275,16 @@ void executeFixpointAlgorithm(llvm::Module const& M) { } //Getting the last block - llvm::BasicBlock const* end_block = &*std::prev(callee_func->end()); - auto end_element = std::make_tuple(end_block, node.basic_block); + BasicBlock const* end_block = &*prev(callee_func->end()); + auto end_element = make_tuple(end_block, node.basic_block); state_new.applyCallInst(inst, end_block, nodes[end_element].state); // If input parameters have changed, we want to interpret the function once again // and reevaluate the nodes of possible callers. if (changed) { - for (std::pair& i : nodes) { - if (std::get<0>(i.first) == node.basic_block and not i.second.update_scheduled) { + for (pair& i : nodes) { + if (get<0>(i.first) == node.basic_block and not i.second.update_scheduled) { dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; worklist.push_back(i.first); i.second.update_scheduled = true; @@ -315,12 +318,12 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // No changes, so no need to do anything else if (not changed) continue; - dbgs(2) << " State changed, notifying " << llvm::succ_size(node.basic_block) - << (llvm::succ_size(node.basic_block) != 1 ? " successors\n" : " successor\n"); + dbgs(2) << " State changed, notifying " << succ_size(node.basic_block) + << (succ_size(node.basic_block) != 1 ? " successors\n" : " successor\n"); // Something changed and we will need to update the successors - for (llvm::BasicBlock const* succ_bb: llvm::successors(node.basic_block)) { - auto succ_key = std::make_tuple(succ_bb, node.callstring); + for (BasicBlock const* succ_bb: successors(node.basic_block)) { + auto succ_key = make_tuple(succ_bb, node.callstring); Node& succ = nodes[succ_key]; if (not succ.update_scheduled) { worklist.push_back(succ_key); @@ -337,7 +340,7 @@ void executeFixpointAlgorithm(llvm::Module const& M) { // Output the final result dbgs(0) << "\nFinal result:\n"; - for (std::pair i: nodes) { + for (pair i: nodes) { dbgs(0) << _bb_key_to_str(i.first) << ":\n"; i.second.state.printOutgoing(*i.second.basic_block, dbgs(0), 2); } -- GitLab From a84b52aa3eda07fd8144cdea820c08132f931547 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 29 Jan 2020 22:34:27 +0100 Subject: [PATCH 037/142] Implemented some hash functions --- src/fixpoint.cpp | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 2c2bc70..71d75b6 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -20,6 +20,44 @@ #include "fixpoint_two_var_eq.cpp" +namespace std { + +template +struct hash> { + size_t operator()(tuple const& t) const { + size_t seed = 0; + apply([&seed](const auto&... item) {(( seed = llvm::hash_combine(seed, item) ), ...);}, t); + return seed; + } +}; + +template +struct hash> { + size_t operator()(pair const& in) const { + return llvm::hash_value(in); + } +}; + + +template +struct hash> { + size_t operator()(vector const& in) const { + return llvm::hash_combine_range(in.begin(), in.end()); + } +}; + +} + +namespace llvm { + +template +static hash_code hash_value(std::vector const& in) { + return hash_combine_range(in.begin(), in.end()); +} + +} + + namespace pcpo { using namespace llvm; -- GitLab From 2dd628dc30da1e3c0c0d9a070f123674e7032492 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 29 Jan 2020 22:46:44 +0100 Subject: [PATCH 038/142] removed two var eq for now --- src/fixpoint_two_var_eq.cpp | 546 ++++++++++++++++++------------------ 1 file changed, 273 insertions(+), 273 deletions(-) diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp index b349990..66a21c4 100644 --- a/src/fixpoint_two_var_eq.cpp +++ b/src/fixpoint_two_var_eq.cpp @@ -1,273 +1,273 @@ -#include -#include - -#include "llvm/IR/CFG.h" -#include "llvm/IR/Module.h" -#include "llvm/IR/Dominators.h" -#include "llvm/Analysis/LoopInfo.h" - -#include "global.h" -#include "general.h" -#include "callstring.h" -#include "value_set.h" - -namespace pcpo { - -using namespace llvm; -using namespace std; - - -// A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data -// per node to execute the fixpoint algorithm. -template -struct Node { - BasicBlock const* bb; - BasicBlock const* callstring; - AbstractState state = {}; - bool update_scheduled = false; // Whether the node is already in the worklist - - // If this is set, the algorithm will add the initial values from the parameters of the - // function to the incoming values, which is the correct thing to do for initial basic - // blocks. - Function const* func_entry = nullptr; -}; - -template -static void register_basic_blocks(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes) { - for (BasicBlock const& bb: *main_func) { - dbgs(1) << " Found basic block main." << bb.getName() << '\n'; - // nodes of main block have the callstring of a dummy block - nodes[{&bb, dummy_block}] = { &bb, dummy_block }; - } -} - -template -static void push_to_worklist(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes, vector &worklist) { - auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); - worklist.push_back(init_element); - nodes[init_element].update_scheduled = true; - nodes[init_element].state = AbstractState {*main_func}; - nodes[init_element].func_entry = main_func; -} - -template -static vector collect_predecessors(Node &node, unordered_map> &nodes, AbstractState &state_new) { - vector predecessors; - for (BasicBlock const* bb: llvm::predecessors(node.bb)) { - dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - - AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; - state_branched.branch(*bb, *node.bb); - state_new.merge(Merge_op::UPPER_BOUND, state_branched); - predecessors.push_back(state_branched); - } - return predecessors; -} - -template -static void apply_instructions(Node &node, unordered_map> &nodes, vector &predecessors, AbstractState state_new, vector &worklist) { - using Node = Node; - - for (Instruction const& inst: *node.bb) { - - // Handles return instructions - if (isa(inst)) { - state_new.applyReturnInst(inst); - } - - // If the result of the instruction is not used, there is no reason to compute - // it. (There are no side-effects in LLVM IR. (I hope.)) - // Except for call instructions, we still want to get that information - if (inst.use_empty() && not isa(inst)) { - dbgs(3) << " Empty use of instruction, skipping...\n"; - } else if (PHINode const* phi = dyn_cast(&inst)) { - // Handles merging points - state_new.applyPHINode(*node.bb, predecessors, phi); - - // Handles function calls - } else if (CallInst const* call = dyn_cast(&inst)) { - // Checks if an input parameter for the callee is bottom. If so, - // then skip the calculation of the call instruction for now - - Function const* callee_func = call->getCalledFunction(); - - // Checks for functions, such as printf and skips them - if (callee_func->empty()) { - dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; - continue; - } - - auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.bb); - bool changed = false; - - // Checks whether a node with key [%callee entry block, %caller basic block], - // i.e. an entry block with callstring of caller basic block, exists. - // If not, all nodes with their corrosponding keys are initilized for the callee function. - if (nodes.find(callee_element) == nodes.end()) { - // Check if abstract_state of call.bb is bottom or not - dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; - - // Register basic blocks - for (BasicBlock const& bb : *callee_func) { - dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; - - Node callee_node = {&bb, node.bb}; - nodes[make_tuple(&bb, node.bb)] = callee_node; - } - - nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; - nodes[callee_element].func_entry = callee_func; - changed = true; - } else { - AbstractState state_update{ callee_func, state_new, call }; - changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); - } - - //Getting the last block - BasicBlock const* end_block = &*prev(callee_func->end()); - auto end_element = make_tuple(end_block, node.bb); - - state_new.applyCallInst(inst, end_block, nodes[end_element].state); - - // If input parameters have changed, we want to interpret the function once again - // and reevaluate the nodes of possible callers. - if (changed) { - for (pair& i : nodes) { - if (get<0>(i.first) == node.bb and not i.second.update_scheduled) { - dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; - worklist.push_back(i.first); - i.second.update_scheduled = true; - } - } - - // Checks if the key of the callee functions entry node is already on the worklist, - // this is necessary for recursions. - if (not nodes[callee_element].update_scheduled) { - worklist.push_back(callee_element); - nodes[callee_element].update_scheduled = true; - - dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; - } else { - dbgs(3) << " Callee already on worklist, nothing to add...\n"; - } - } - } else { - state_new.applyDefault(inst); - } - } -} - -template -static void update_successors(Node &node, unordered_map> &nodes, vector &worklist) { - for (BasicBlock const* succ_bb: successors(node.bb)) { - auto succ_key = make_tuple(succ_bb, node.callstring); - Node& succ = nodes[succ_key]; - if (not succ.update_scheduled) { - worklist.push_back(succ_key); - succ.update_scheduled = true; - dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; - } - } -} - -template -static void output_final_result(unordered_map> const& nodes) { - dbgs(0) << "\nFinal result:\n"; - for (pair> i: nodes) { - dbgs(0) << _bb_key_to_str(i.first) << ":\n"; - i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); - } -} - -/// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface -/// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods -/// with the right signatures and take care to fulfil the contracts outlines above). -/// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in -/// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as -/// well. -/// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I -/// recommend Meld.) -template -void executeFixpointAlgorithmTwoVarEq(Module const& M) { - using Node = Node; - - constexpr int iterations_max = 1000; - - unordered_map nodes; - vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed - - // We only consider the main function in the beginning. If no main exists, nothing is evaluated! - Function const* main_func = M.getFunction("main"); - - //creating dummy block for callstrings of the main block, since the main function is not called from another function - BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); - - // TODO: Check what this does for release clang, probably write out a warning - dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; - - // Register basic blocks of the main function - register_basic_blocks(dummy_block, main_func, nodes); - - // Push the initial block into the worklist - push_to_worklist(dummy_block, main_func, nodes, worklist); - - dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") - << ". Starting fixpoint iteration...\n"; - - for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { - Node& node = nodes[worklist.back()]; - worklist.pop_back(); - node.update_scheduled = false; - - dbgs(1) << "\nIteration " << iter << ", considering basic block " - << _bb_to_str(node.bb) << " with callstring " - << _bb_to_str(node.callstring) << '\n'; - - AbstractState state_new; // Set to bottom - - if (node.func_entry) { - dbgs(1) << " Merging function parameters, is entry block\n"; - // if it is the entry node, then its state should be top - state_new.merge(Merge_op::UPPER_BOUND, node.state); - } - - dbgs(1) << " Merge of " << pred_size(node.bb) - << (pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); - - // Collect the predecessors - vector predecessors = collect_predecessors(node, nodes, state_new); - - dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); - - // Apply the basic block - dbgs(3) << " Applying basic block\n"; - - // Applies all instrucions of a basic block - apply_instructions(node, nodes, predecessors, state_new, worklist); - - // Merge the state back into the node - dbgs(3) << " Merging with stored state\n"; - bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); - - dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); - - // No changes, so no need to do anything else - if (not changed) continue; - - dbgs(2) << " State changed, notifying " << succ_size(node.bb) - << (succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); - - // Something changed and we will need to update the successors - update_successors(node, nodes, worklist); - } - - if (!worklist.empty()) { - dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; - } - - // Output the final result - output_final_result(nodes); -} - -} /* end of namespace pcpo */ - +//#include +//#include +// +//#include "llvm/IR/CFG.h" +//#include "llvm/IR/Module.h" +//#include "llvm/IR/Dominators.h" +//#include "llvm/Analysis/LoopInfo.h" +// +//#include "global.h" +//#include "general.h" +//#include "callstring.h" +//#include "value_set.h" +// +//namespace pcpo { +// +//using namespace llvm; +//using namespace std; +// +// +//// A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data +//// per node to execute the fixpoint algorithm. +//template +//struct Node { +// BasicBlock const* bb; +// BasicBlock const* callstring; +// AbstractState state = {}; +// bool update_scheduled = false; // Whether the node is already in the worklist +// +// // If this is set, the algorithm will add the initial values from the parameters of the +// // function to the incoming values, which is the correct thing to do for initial basic +// // blocks. +// Function const* func_entry = nullptr; +//}; +// +//template +//static void register_basic_blocks(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes) { +// for (BasicBlock const& bb: *main_func) { +// dbgs(1) << " Found basic block main." << bb.getName() << '\n'; +// // nodes of main block have the callstring of a dummy block +// nodes[{&bb, dummy_block}] = { &bb, dummy_block }; +// } +//} +// +//template +//static void push_to_worklist(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes, vector &worklist) { +// auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); +// worklist.push_back(init_element); +// nodes[init_element].update_scheduled = true; +// nodes[init_element].state = AbstractState {*main_func}; +// nodes[init_element].func_entry = main_func; +//} +// +//template +//static vector collect_predecessors(Node &node, unordered_map> &nodes, AbstractState &state_new) { +// vector predecessors; +// for (BasicBlock const* bb: llvm::predecessors(node.bb)) { +// dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; +// +// AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; +// state_branched.branch(*bb, *node.bb); +// state_new.merge(Merge_op::UPPER_BOUND, state_branched); +// predecessors.push_back(state_branched); +// } +// return predecessors; +//} +// +//template +//static void apply_instructions(Node &node, unordered_map> &nodes, vector &predecessors, AbstractState state_new, vector &worklist) { +// using Node = Node; +// +// for (Instruction const& inst: *node.bb) { +// +// // Handles return instructions +// if (isa(inst)) { +// state_new.applyReturnInst(inst); +// } +// +// // If the result of the instruction is not used, there is no reason to compute +// // it. (There are no side-effects in LLVM IR. (I hope.)) +// // Except for call instructions, we still want to get that information +// if (inst.use_empty() && not isa(inst)) { +// dbgs(3) << " Empty use of instruction, skipping...\n"; +// } else if (PHINode const* phi = dyn_cast(&inst)) { +// // Handles merging points +// state_new.applyPHINode(*node.bb, predecessors, phi); +// +// // Handles function calls +// } else if (CallInst const* call = dyn_cast(&inst)) { +// // Checks if an input parameter for the callee is bottom. If so, +// // then skip the calculation of the call instruction for now +// +// Function const* callee_func = call->getCalledFunction(); +// +// // Checks for functions, such as printf and skips them +// if (callee_func->empty()) { +// dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; +// continue; +// } +// +// auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.bb); +// bool changed = false; +// +// // Checks whether a node with key [%callee entry block, %caller basic block], +// // i.e. an entry block with callstring of caller basic block, exists. +// // If not, all nodes with their corrosponding keys are initilized for the callee function. +// if (nodes.find(callee_element) == nodes.end()) { +// // Check if abstract_state of call.bb is bottom or not +// dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; +// +// // Register basic blocks +// for (BasicBlock const& bb : *callee_func) { +// dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; +// +// Node callee_node = {&bb, node.bb}; +// nodes[make_tuple(&bb, node.bb)] = callee_node; +// } +// +// nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; +// nodes[callee_element].func_entry = callee_func; +// changed = true; +// } else { +// AbstractState state_update{ callee_func, state_new, call }; +// changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); +// } +// +// //Getting the last block +// BasicBlock const* end_block = &*prev(callee_func->end()); +// auto end_element = make_tuple(end_block, node.bb); +// +// state_new.applyCallInst(inst, end_block, nodes[end_element].state); +// +// // If input parameters have changed, we want to interpret the function once again +// // and reevaluate the nodes of possible callers. +// if (changed) { +// for (pair& i : nodes) { +// if (get<0>(i.first) == node.bb and not i.second.update_scheduled) { +// dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; +// worklist.push_back(i.first); +// i.second.update_scheduled = true; +// } +// } +// +// // Checks if the key of the callee functions entry node is already on the worklist, +// // this is necessary for recursions. +// if (not nodes[callee_element].update_scheduled) { +// worklist.push_back(callee_element); +// nodes[callee_element].update_scheduled = true; +// +// dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; +// } else { +// dbgs(3) << " Callee already on worklist, nothing to add...\n"; +// } +// } +// } else { +// state_new.applyDefault(inst); +// } +// } +//} +// +//template +//static void update_successors(Node &node, unordered_map> &nodes, vector &worklist) { +// for (BasicBlock const* succ_bb: successors(node.bb)) { +// auto succ_key = make_tuple(succ_bb, node.callstring); +// Node& succ = nodes[succ_key]; +// if (not succ.update_scheduled) { +// worklist.push_back(succ_key); +// succ.update_scheduled = true; +// dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; +// } +// } +//} +// +//template +//static void output_final_result(unordered_map> const& nodes) { +// dbgs(0) << "\nFinal result:\n"; +// for (pair> i: nodes) { +// dbgs(0) << _bb_key_to_str(i.first) << ":\n"; +// i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); +// } +//} +// +///// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface +///// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods +///// with the right signatures and take care to fulfil the contracts outlines above). +///// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in +///// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as +///// well. +///// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I +///// recommend Meld.) +//template +//void executeFixpointAlgorithmTwoVarEq(Module const& M) { +// using Node = Node; +// +// constexpr int iterations_max = 1000; +// +// unordered_map nodes; +// vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed +// +// // We only consider the main function in the beginning. If no main exists, nothing is evaluated! +// Function const* main_func = M.getFunction("main"); +// +// //creating dummy block for callstrings of the main block, since the main function is not called from another function +// BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); +// +// // TODO: Check what this does for release clang, probably write out a warning +// dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; +// +// // Register basic blocks of the main function +// register_basic_blocks(dummy_block, main_func, nodes); +// +// // Push the initial block into the worklist +// push_to_worklist(dummy_block, main_func, nodes, worklist); +// +// dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") +// << ". Starting fixpoint iteration...\n"; +// +// for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { +// Node& node = nodes[worklist.back()]; +// worklist.pop_back(); +// node.update_scheduled = false; +// +// dbgs(1) << "\nIteration " << iter << ", considering basic block " +// << _bb_to_str(node.bb) << " with callstring " +// << _bb_to_str(node.callstring) << '\n'; +// +// AbstractState state_new; // Set to bottom +// +// if (node.func_entry) { +// dbgs(1) << " Merging function parameters, is entry block\n"; +// // if it is the entry node, then its state should be top +// state_new.merge(Merge_op::UPPER_BOUND, node.state); +// } +// +// dbgs(1) << " Merge of " << pred_size(node.bb) +// << (pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); +// +// // Collect the predecessors +// vector predecessors = collect_predecessors(node, nodes, state_new); +// +// dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); +// +// // Apply the basic block +// dbgs(3) << " Applying basic block\n"; +// +// // Applies all instrucions of a basic block +// apply_instructions(node, nodes, predecessors, state_new, worklist); +// +// // Merge the state back into the node +// dbgs(3) << " Merging with stored state\n"; +// bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); +// +// dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); +// +// // No changes, so no need to do anything else +// if (not changed) continue; +// +// dbgs(2) << " State changed, notifying " << succ_size(node.bb) +// << (succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); +// +// // Something changed and we will need to update the successors +// update_successors(node, nodes, worklist); +// } +// +// if (!worklist.empty()) { +// dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; +// } +// +// // Output the final result +// output_final_result(nodes); +//} +// +//} /* end of namespace pcpo */ +// -- GitLab From 1d94c833e73ff5e362c463dedf5502ac0ccdc361 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 00:55:46 +0100 Subject: [PATCH 039/142] prepared data structures for callstring n [2] --- src/fixpoint.cpp | 61 +++++++++++++++++++++++++----------------------- 1 file changed, 32 insertions(+), 29 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 71d75b6..43337f4 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -136,10 +136,13 @@ public: void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; }; +using Callstring = vector; +using NodeKey = pair; + template struct Node { BasicBlock const* basic_block; - BasicBlock const* callstring; + Callstring callstring; AbstractState state = {}; bool update_scheduled = false; // Whether the node is already in the worklist @@ -168,8 +171,8 @@ void executeFixpointAlgorithm(Module const& M) { // A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data // per node to execute the fixpoint algorithm. - unordered_map nodes; - vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed + unordered_map nodes; + vector worklist; // We only consider the main function in the beginning. If no main exists, nothing is evaluated! Function const* main_func = M.getFunction("main"); @@ -186,16 +189,16 @@ void executeFixpointAlgorithm(Module const& M) { Node node; node.basic_block = &bb; - node.callstring = dummy_block; + node.callstring = {dummy_block}; // node.state is default initialised (to bottom) // nodes of main block have the callstring of a dummy block - nodes[make_tuple(&bb, dummy_block)] = node; + nodes[{{dummy_block}, &bb}] = node; } // Push the initial block into the worklist - auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); - worklist.push_back(init_element); + NodeKey init_element = {{dummy_block}, &main_func->getEntryBlock()}; + worklist.push_back(&nodes[init_element]); nodes[init_element].update_scheduled = true; nodes[init_element].state = AbstractState {*main_func}; nodes[init_element].func_entry = main_func; @@ -204,13 +207,13 @@ void executeFixpointAlgorithm(Module const& M) { << ". Starting fixpoint iteration...\n"; for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { - Node& node = nodes[worklist.back()]; + Node& node = *worklist.back(); worklist.pop_back(); node.update_scheduled = false; dbgs(1) << "\nIteration " << iter << ", considering basic block " << _bb_to_str(node.basic_block) << " with callstring " - << _bb_to_str(node.callstring) << '\n'; + << "_bb_to_str(node.callstring)" << '\n'; AbstractState state_new; // Set to bottom @@ -230,7 +233,7 @@ void executeFixpointAlgorithm(Module const& M) { for (BasicBlock const* bb: llvm::predecessors(node.basic_block)) { dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; - AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; + AbstractState state_branched {nodes[{{node.callstring}, bb}].state}; state_branched.branch(*bb, *node.basic_block); state_new.merge(merge_op, state_branched); predecessors.push_back(state_branched); @@ -282,7 +285,7 @@ void executeFixpointAlgorithm(Module const& M) { continue; } - auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.basic_block); + NodeKey callee_element = {{node.basic_block}, &callee_func->getEntryBlock()}; bool changed; // Checks whether a node with key [%callee entry block, %caller basic block], @@ -298,10 +301,10 @@ void executeFixpointAlgorithm(Module const& M) { Node callee_node; callee_node.basic_block = &bb; - callee_node.callstring = node.basic_block; + callee_node.callstring = {node.basic_block}; // node.state is default initialised (to bottom) - nodes[make_tuple(&bb, node.basic_block)] = callee_node; + nodes[{{node.basic_block}, &bb}] = callee_node; } nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; @@ -314,28 +317,28 @@ void executeFixpointAlgorithm(Module const& M) { //Getting the last block BasicBlock const* end_block = &*prev(callee_func->end()); - auto end_element = make_tuple(end_block, node.basic_block); - + NodeKey end_element = {{node.basic_block}, end_block}; state_new.applyCallInst(inst, end_block, nodes[end_element].state); // If input parameters have changed, we want to interpret the function once again // and reevaluate the nodes of possible callers. if (changed) { - for (pair& i : nodes) { - if (get<0>(i.first) == node.basic_block and not i.second.update_scheduled) { - dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; - worklist.push_back(i.first); - i.second.update_scheduled = true; + for (auto& [key, value]: nodes) { + if (key.first[0] == node.basic_block and not value.update_scheduled) { + dbgs(3) << " Adding possible caller " << "_bb_key_to_str(i.first)" << " to worklist\n"; + worklist.push_back(&value); + value.update_scheduled = true; } } // Checks if the key of the callee functions entry node is already on the worklist, // this is necessary for recursions. if (not nodes[callee_element].update_scheduled) { - worklist.push_back(callee_element); - nodes[callee_element].update_scheduled = true; + auto& elem = nodes[callee_element]; + worklist.push_back(&elem); + elem.update_scheduled = true; - dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; + dbgs(3) << " Adding callee " << "_bb_key_to_str(callee_element)" << " to worklist\n"; } else { dbgs(3) << " Callee already on worklist, nothing to add...\n"; } @@ -361,13 +364,13 @@ void executeFixpointAlgorithm(Module const& M) { // Something changed and we will need to update the successors for (BasicBlock const* succ_bb: successors(node.basic_block)) { - auto succ_key = make_tuple(succ_bb, node.callstring); + NodeKey succ_key = {{node.callstring}, succ_bb}; Node& succ = nodes[succ_key]; if (not succ.update_scheduled) { - worklist.push_back(succ_key); + worklist.push_back(&succ); succ.update_scheduled = true; - dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; + dbgs(3) << " Adding " << "_bb_key_to_str(succ_key)" << " to worklist\n"; } } } @@ -378,9 +381,9 @@ void executeFixpointAlgorithm(Module const& M) { // Output the final result dbgs(0) << "\nFinal result:\n"; - for (pair i: nodes) { - dbgs(0) << _bb_key_to_str(i.first) << ":\n"; - i.second.state.printOutgoing(*i.second.basic_block, dbgs(0), 2); + for (auto const& [key, node]: nodes) { + dbgs(0) << "_bb_key_to_str(key)" << ":\n"; + node.state.printOutgoing(*node.basic_block, dbgs(0), 2); } } -- GitLab From 7bf388f4e850574419289d4b1531815496f56522 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 01:31:42 +0100 Subject: [PATCH 040/142] rewrite util funcs with << operator --- src/fixpoint.cpp | 39 ++++++++++++++++++++++++++++++--------- 1 file changed, 30 insertions(+), 9 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 43337f4..48f780b 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -8,7 +8,6 @@ #include "llvm/IR/Dominators.h" #include "llvm/Analysis/LoopInfo.h" -#include "general.h" #include "global.h" #include "callstring.h" @@ -139,6 +138,28 @@ public: using Callstring = vector; using NodeKey = pair; +// MARK: - To String + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BasicBlock const& basic_block) { + os << "%"; + if (llvm::Function const* f = basic_block.getParent()) { + os << f->getName() << "."; + } + return os << basic_block.getName(); +} + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Callstring const& callstring) { + for (auto call : callstring) { + os << *call << " -> "; + } + return os; +} + +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NodeKey const& key) { + return os << "[" << *key.second << "," << key.first << "]"; +} + + template struct Node { BasicBlock const* basic_block; @@ -212,8 +233,8 @@ void executeFixpointAlgorithm(Module const& M) { node.update_scheduled = false; dbgs(1) << "\nIteration " << iter << ", considering basic block " - << _bb_to_str(node.basic_block) << " with callstring " - << "_bb_to_str(node.callstring)" << '\n'; + << *node.basic_block << " with callstring " + << node.callstring << '\n'; AbstractState state_new; // Set to bottom @@ -231,7 +252,7 @@ void executeFixpointAlgorithm(Module const& M) { // Collect the predecessors vector predecessors; for (BasicBlock const* bb: llvm::predecessors(node.basic_block)) { - dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; + dbgs(3) << " Merging basic block " << bb << '\n'; AbstractState state_branched {nodes[{{node.callstring}, bb}].state}; state_branched.branch(*bb, *node.basic_block); @@ -297,7 +318,7 @@ void executeFixpointAlgorithm(Module const& M) { // Register basic blocks for (BasicBlock const& bb : *callee_func) { - dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; + dbgs(4) << " Found basic block " << bb << '\n'; Node callee_node; callee_node.basic_block = &bb; @@ -325,7 +346,7 @@ void executeFixpointAlgorithm(Module const& M) { if (changed) { for (auto& [key, value]: nodes) { if (key.first[0] == node.basic_block and not value.update_scheduled) { - dbgs(3) << " Adding possible caller " << "_bb_key_to_str(i.first)" << " to worklist\n"; + dbgs(3) << " Adding possible caller " << key << " to worklist\n"; worklist.push_back(&value); value.update_scheduled = true; } @@ -338,7 +359,7 @@ void executeFixpointAlgorithm(Module const& M) { worklist.push_back(&elem); elem.update_scheduled = true; - dbgs(3) << " Adding callee " << "_bb_key_to_str(callee_element)" << " to worklist\n"; + dbgs(3) << " Adding callee " << callee_element << " to worklist\n"; } else { dbgs(3) << " Callee already on worklist, nothing to add...\n"; } @@ -370,7 +391,7 @@ void executeFixpointAlgorithm(Module const& M) { worklist.push_back(&succ); succ.update_scheduled = true; - dbgs(3) << " Adding " << "_bb_key_to_str(succ_key)" << " to worklist\n"; + dbgs(3) << " Adding " << succ_key << " to worklist\n"; } } } @@ -382,7 +403,7 @@ void executeFixpointAlgorithm(Module const& M) { // Output the final result dbgs(0) << "\nFinal result:\n"; for (auto const& [key, node]: nodes) { - dbgs(0) << "_bb_key_to_str(key)" << ":\n"; + dbgs(0) << key << ":\n"; node.state.printOutgoing(*node.basic_block, dbgs(0), 2); } -- GitLab From 22fadf598423a64d7ba9dbe3a43fb76d68216b7e Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 02:44:16 +0100 Subject: [PATCH 041/142] use function in callstring instead of basic block --- src/fixpoint.cpp | 47 ++++++++++++++++++++++++++--------------------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 48f780b..d51d969 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -135,7 +135,7 @@ public: void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; }; -using Callstring = vector; +using Callstring = vector; using NodeKey = pair; // MARK: - To String @@ -150,7 +150,7 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BasicBlock const& basic_blo llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Callstring const& callstring) { for (auto call : callstring) { - os << *call << " -> "; + os << call->getName() << " -> "; } return os; } @@ -163,14 +163,20 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, NodeKey const& key) { template struct Node { BasicBlock const* basic_block; + /// Function calls the lead to this basic block. The last element is always the current function. Callstring callstring; AbstractState state = {}; bool update_scheduled = false; // Whether the node is already in the worklist - // If this is set, the algorithm will add the initial values from the parameters of the - // function to the incoming values, which is the correct thing to do for initial basic - // blocks. - Function const* func_entry = nullptr; + /// Check wether this basic block is the entry block of its function. + bool isEntry() const { + return basic_block == &function()->getEntryBlock(); + } + + /// Function in which this basic block is located. + Function const* function() const { + return callstring.back(); + } }; @@ -198,9 +204,6 @@ void executeFixpointAlgorithm(Module const& M) { // We only consider the main function in the beginning. If no main exists, nothing is evaluated! Function const* main_func = M.getFunction("main"); - //creating dummy block for callstrings of the main block, since the main function is not called from another function - BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); - // TODO: Check what this does for release clang, probably write out a warning dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; @@ -210,19 +213,18 @@ void executeFixpointAlgorithm(Module const& M) { Node node; node.basic_block = &bb; - node.callstring = {dummy_block}; + node.callstring = {main_func}; // node.state is default initialised (to bottom) // nodes of main block have the callstring of a dummy block - nodes[{{dummy_block}, &bb}] = node; + nodes[{{main_func}, &bb}] = node; } // Push the initial block into the worklist - NodeKey init_element = {{dummy_block}, &main_func->getEntryBlock()}; + NodeKey init_element = {{main_func}, &main_func->getEntryBlock()}; worklist.push_back(&nodes[init_element]); nodes[init_element].update_scheduled = true; nodes[init_element].state = AbstractState {*main_func}; - nodes[init_element].func_entry = main_func; dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; @@ -238,7 +240,7 @@ void executeFixpointAlgorithm(Module const& M) { AbstractState state_new; // Set to bottom - if (node.func_entry) { + if (node.isEntry()) { dbgs(1) << " Merging function parameters, is entry block\n"; // if it is the entry node, then its state should be top @@ -252,7 +254,7 @@ void executeFixpointAlgorithm(Module const& M) { // Collect the predecessors vector predecessors; for (BasicBlock const* bb: llvm::predecessors(node.basic_block)) { - dbgs(3) << " Merging basic block " << bb << '\n'; + dbgs(3) << " Merging basic block " << *bb << '\n'; AbstractState state_branched {nodes[{{node.callstring}, bb}].state}; state_branched.branch(*bb, *node.basic_block); @@ -306,7 +308,11 @@ void executeFixpointAlgorithm(Module const& M) { continue; } - NodeKey callee_element = {{node.basic_block}, &callee_func->getEntryBlock()}; + Callstring new_callstring = node.callstring; + new_callstring.push_back(callee_func); + + + NodeKey callee_element = {new_callstring, &callee_func->getEntryBlock()}; bool changed; // Checks whether a node with key [%callee entry block, %caller basic block], @@ -322,14 +328,13 @@ void executeFixpointAlgorithm(Module const& M) { Node callee_node; callee_node.basic_block = &bb; - callee_node.callstring = {node.basic_block}; + callee_node.callstring = new_callstring; // node.state is default initialised (to bottom) - nodes[{{node.basic_block}, &bb}] = callee_node; + nodes[{new_callstring, &bb}] = callee_node; } nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; - nodes[callee_element].func_entry = callee_func; changed = true; } else { AbstractState state_update{ callee_func, state_new, call }; @@ -338,14 +343,14 @@ void executeFixpointAlgorithm(Module const& M) { //Getting the last block BasicBlock const* end_block = &*prev(callee_func->end()); - NodeKey end_element = {{node.basic_block}, end_block}; + NodeKey end_element = {new_callstring, end_block}; state_new.applyCallInst(inst, end_block, nodes[end_element].state); // If input parameters have changed, we want to interpret the function once again // and reevaluate the nodes of possible callers. if (changed) { for (auto& [key, value]: nodes) { - if (key.first[0] == node.basic_block and not value.update_scheduled) { + if (key.second == node.basic_block and not value.update_scheduled) { dbgs(3) << " Adding possible caller " << key << " to worklist\n"; worklist.push_back(&value); value.update_scheduled = true; -- GitLab From 513bbc2cf399fd2882f1b586673a60821bdbf5f8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 03:02:07 +0100 Subject: [PATCH 042/142] nice printing of callstrings --- src/fixpoint.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index d51d969..f8cba0b 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -150,7 +150,10 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, BasicBlock const& basic_blo llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Callstring const& callstring) { for (auto call : callstring) { - os << call->getName() << " -> "; + os << call->getName(); + if (call != callstring.back()) { + os << " -> "; + } } return os; } -- GitLab From dc7836af48c99e03c64517424a7e7deb0fb52355 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 03:19:44 +0100 Subject: [PATCH 043/142] moved AbstractStateDummy into its own file --- CMakeLists.txt | 3 +- src/abstract_state.h | 74 ++++++++++++++++++++++++++++++++++++++++++++ src/fixpoint.cpp | 66 --------------------------------------- 3 files changed, 76 insertions(+), 67 deletions(-) create mode 100644 src/abstract_state.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 7361ef3..ad1e884 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ set(pain_headers src/general.h src/global.h src/callstring.h + src/abstract_state.h ) include_directories(${LLVM_INCLUDE_DIRS}) @@ -166,4 +167,4 @@ add_test(NAME simpleIntervalTest add_test(NAME normalizedConjunctionTest COMMAND normalized_conjunction_test -) \ No newline at end of file +) diff --git a/src/abstract_state.h b/src/abstract_state.h new file mode 100644 index 0000000..b6abc2b --- /dev/null +++ b/src/abstract_state.h @@ -0,0 +1,74 @@ +#pragma once + +#include "llvm/IR/CFG.h" + +namespace pcpo { + +class AbstractStateDummy { +public: + // This has to initialise the state to bottom. + AbstractStateDummy() = default; + + // Creates a copy of the state. Using the default copy-constructor should be fine here, but if + // some members do something weird you maybe want to implement this as + // AbstractState().merge(state) + AbstractStateDummy(AbstractStateDummy const& state) = default; + + // Initialise the state to the incoming state of the function. This should do something like + // assuming the parameters can be anything. + explicit AbstractStateDummy(llvm::Function const& f) {} + + // Initialise the state of a function call with parameters of the caller. + // This is the "enter" function as described in "Compiler Design: Analysis and Transformation" + explicit AbstractStateDummy(llvm::Function const* callee_func, AbstractStateDummy const& state, + llvm::CallInst const* call) {} + + // Apply functions apply the changes needed to reflect executing the instructions in the basic block. Before + // this operation is called, the state is the one upon entering bb, afterwards it should be (an + // upper bound of) the state leaving the basic block. + // predecessors contains the outgoing state for all the predecessors, in the same order as they + // are listed in llvm::predecessors(bb). + + // Applies instructions within the PHI node, needed for merging + void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, + llvm::Instruction const& inst) {}; + + // This is the "combine" function as described in "Compiler Design: Analysis and Transformation" + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, + AbstractStateDummy const& callee_state) {}; + + // Evaluates return instructions, needed for the main function and the debug output + void applyReturnInst(llvm::Instruction const& inst) {}; + + // Handles all cases different from the three above + void applyDefault(llvm::Instruction const& inst) {}; + + // This 'merges' two states, which is the operation we do fixpoint iteration over. Currently, + // there are three possibilities for op: + // 1. UPPER_BOUND: This has to return some upper bound of itself and other, with more precise + // bounds being preferred. + // 2. WIDEN: Same as UPPER_BOUND, but this operation should sacrifice precision to converge + // quickly. Returning T would be fine, though maybe not optimal. For example for intervals, + // an implementation could ensure to double the size of the interval. + // 3. NARROW: Return a value between the intersection of the state and other, and the + // state. In pseudocode: + // intersect(state, other) <= narrow(state, other) <= state + // For all of the above, this operation returns whether the state changed as a result. + // IMPORTANT: The simple fixpoint algorithm only performs UPPER_BOUND, so you do not need to + // implement the others if you just use that one. (The more advanced algorithm in + // fixpoint_widening.cpp uses all three operations.) + bool merge(Merge_op::Type op, AbstractStateDummy const& other) { return false; }; + + // Restrict the set of values to the one that allows 'from' to branch towards + // 'towards'. Starting with the state when exiting from, this should compute (an upper bound of) + // the possible values that would reach the block towards. Doing nothing thus is a valid + // implementation. + void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) {}; + + // Functions to generate the debug output. printIncoming should output the state as of entering + // the basic block, printOutcoming the state when leaving it. + void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; + void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; +}; + +} diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index f8cba0b..32846f2 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -68,72 +68,6 @@ char AbstractInterpretationPass::ID; int debug_level = DEBUG_LEVEL; // from global.hpp -class AbstractStateDummy { -public: - // This has to initialise the state to bottom. - AbstractStateDummy() = default; - - // Creates a copy of the state. Using the default copy-constructor should be fine here, but if - // some members do something weird you maybe want to implement this as - // AbstractState().merge(state) - AbstractStateDummy(AbstractStateDummy const& state) = default; - - // Initialise the state to the incoming state of the function. This should do something like - // assuming the parameters can be anything. - explicit AbstractStateDummy(llvm::Function const& f) {} - - // Initialise the state of a function call with parameters of the caller. - // This is the "enter" function as described in "Compiler Design: Analysis and Transformation" - explicit AbstractStateDummy(llvm::Function const* callee_func, AbstractStateDummy const& state, - llvm::CallInst const* call) {} - - // Apply functions apply the changes needed to reflect executing the instructions in the basic block. Before - // this operation is called, the state is the one upon entering bb, afterwards it should be (an - // upper bound of) the state leaving the basic block. - // predecessors contains the outgoing state for all the predecessors, in the same order as they - // are listed in llvm::predecessors(bb). - - // Applies instructions within the PHI node, needed for merging - void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, - llvm::Instruction const& inst) {}; - - // This is the "combine" function as described in "Compiler Design: Analysis and Transformation" - void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, - AbstractStateDummy const& callee_state) {}; - - // Evaluates return instructions, needed for the main function and the debug output - void applyReturnInst(llvm::Instruction const& inst) {}; - - // Handles all cases different from the three above - void applyDefault(llvm::Instruction const& inst) {}; - - // This 'merges' two states, which is the operation we do fixpoint iteration over. Currently, - // there are three possibilities for op: - // 1. UPPER_BOUND: This has to return some upper bound of itself and other, with more precise - // bounds being preferred. - // 2. WIDEN: Same as UPPER_BOUND, but this operation should sacrifice precision to converge - // quickly. Returning T would be fine, though maybe not optimal. For example for intervals, - // an implementation could ensure to double the size of the interval. - // 3. NARROW: Return a value between the intersection of the state and other, and the - // state. In pseudocode: - // intersect(state, other) <= narrow(state, other) <= state - // For all of the above, this operation returns whether the state changed as a result. - // IMPORTANT: The simple fixpoint algorithm only performs UPPER_BOUND, so you do not need to - // implement the others if you just use that one. (The more advanced algorithm in - // fixpoint_widening.cpp uses all three operations.) - bool merge(Merge_op::Type op, AbstractStateDummy const& other) { return false; }; - - // Restrict the set of values to the one that allows 'from' to branch towards - // 'towards'. Starting with the state when exiting from, this should compute (an upper bound of) - // the possible values that would reach the block towards. Doing nothing thus is a valid - // implementation. - void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) {}; - - // Functions to generate the debug output. printIncoming should output the state as of entering - // the basic block, printOutcoming the state when leaving it. - void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; - void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const {}; -}; using Callstring = vector; using NodeKey = pair; -- GitLab From b4cd7534be82a398e88a0073b571ebbfd6089a67 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 03:19:55 +0100 Subject: [PATCH 044/142] renamed bb to basic_block --- src/fixpoint.cpp | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 32846f2..34fdb5a 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -145,16 +145,16 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Register basic blocks of the main function - for (BasicBlock const& bb: *main_func) { - dbgs(1) << " Found basic block main." << bb.getName() << '\n'; + for (BasicBlock const& basic_block: *main_func) { + dbgs(1) << " Found basic block main." << basic_block.getName() << '\n'; Node node; - node.basic_block = &bb; + node.basic_block = &basic_block; node.callstring = {main_func}; // node.state is default initialised (to bottom) // nodes of main block have the callstring of a dummy block - nodes[{{main_func}, &bb}] = node; + nodes[{{main_func}, &basic_block}] = node; } // Push the initial block into the worklist @@ -190,11 +190,11 @@ void executeFixpointAlgorithm(Module const& M) { // Collect the predecessors vector predecessors; - for (BasicBlock const* bb: llvm::predecessors(node.basic_block)) { - dbgs(3) << " Merging basic block " << *bb << '\n'; + for (BasicBlock const* basic_block: llvm::predecessors(node.basic_block)) { + dbgs(3) << " Merging basic block " << *basic_block << '\n'; - AbstractState state_branched {nodes[{{node.callstring}, bb}].state}; - state_branched.branch(*bb, *node.basic_block); + AbstractState state_branched {nodes[{{node.callstring}, basic_block}].state}; + state_branched.branch(*basic_block, *node.basic_block); state_new.merge(merge_op, state_branched); predecessors.push_back(state_branched); } @@ -260,15 +260,15 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; // Register basic blocks - for (BasicBlock const& bb : *callee_func) { - dbgs(4) << " Found basic block " << bb << '\n'; + for (BasicBlock const& basic_block : *callee_func) { + dbgs(4) << " Found basic block " << basic_block << '\n'; Node callee_node; - callee_node.basic_block = &bb; + callee_node.basic_block = &basic_block; callee_node.callstring = new_callstring; // node.state is default initialised (to bottom) - nodes[{new_callstring, &bb}] = callee_node; + nodes[{new_callstring, &basic_block}] = callee_node; } nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; -- GitLab From aa7b4cc3fa5558950db839c453300585371d5c1e Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 23:28:33 +0100 Subject: [PATCH 045/142] put hash utils in separate file --- CMakeLists.txt | 1 + src/fixpoint.cpp | 40 +--------------------------------------- src/hash_utils.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 45 insertions(+), 39 deletions(-) create mode 100644 src/hash_utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index ad1e884..5176aa8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -118,6 +118,7 @@ set(pain_headers src/global.h src/callstring.h src/abstract_state.h + src/hash_utils.h ) include_directories(${LLVM_INCLUDE_DIRS}) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 34fdb5a..44d77a3 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -17,45 +17,7 @@ #include "fixpoint_widening.cpp" #include "fixpoint_two_var_eq.cpp" - - -namespace std { - -template -struct hash> { - size_t operator()(tuple const& t) const { - size_t seed = 0; - apply([&seed](const auto&... item) {(( seed = llvm::hash_combine(seed, item) ), ...);}, t); - return seed; - } -}; - -template -struct hash> { - size_t operator()(pair const& in) const { - return llvm::hash_value(in); - } -}; - - -template -struct hash> { - size_t operator()(vector const& in) const { - return llvm::hash_combine_range(in.begin(), in.end()); - } -}; - -} - -namespace llvm { - -template -static hash_code hash_value(std::vector const& in) { - return hash_combine_range(in.begin(), in.end()); -} - -} - +#include "hash_utils.h" namespace pcpo { diff --git a/src/hash_utils.h b/src/hash_utils.h new file mode 100644 index 0000000..8433038 --- /dev/null +++ b/src/hash_utils.h @@ -0,0 +1,43 @@ +#pragma once + +#include +#include + +#include "llvm/ADT/Hashing.h" + +namespace std { + +template +struct hash> { + size_t operator()(tuple const& t) const { + size_t seed = 0; + apply([&seed](const auto&... item) {(( seed = llvm::hash_combine(seed, item) ), ...);}, t); + return seed; + } +}; + +template +struct hash> { + size_t operator()(pair const& in) const { + return llvm::hash_value(in); + } +}; + + +template +struct hash> { + size_t operator()(vector const& in) const { + return llvm::hash_combine_range(in.begin(), in.end()); + } +}; + +} + +namespace llvm { + +template +static hash_code hash_value(std::vector const& in) { + return hash_combine_range(in.begin(), in.end()); +} + +} -- GitLab From ba0af8447f54f22baae8ea8d33ef1ea776ff549f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 23:31:01 +0100 Subject: [PATCH 046/142] removed unused header file callstring.h --- CMakeLists.txt | 1 - src/callstring.h | 16 ---------------- src/fixpoint.cpp | 1 - src/fixpoint_two_var_eq.cpp | 1 - src/fixpoint_widening.cpp | 1 - src/value_set.h | 1 - 6 files changed, 21 deletions(-) delete mode 100644 src/callstring.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5176aa8..b776213 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -116,7 +116,6 @@ set(pain_headers src/linear_equality.h src/general.h src/global.h - src/callstring.h src/abstract_state.h src/hash_utils.h ) diff --git a/src/callstring.h b/src/callstring.h deleted file mode 100644 index 044e599..0000000 --- a/src/callstring.h +++ /dev/null @@ -1,16 +0,0 @@ -#pragma once - -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constant.h" -#include "llvm/IR/CFG.h" -#include "llvm/IR/InstrTypes.h" -#include "llvm/IR/Instructions.h" - -#include "global.h" -#include "simple_interval.h" - -namespace pcpo { - - void InterpretCall(llvm::CallInst const* call, std::vector& operands); - -} \ No newline at end of file diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 44d77a3..60c41ad 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -9,7 +9,6 @@ #include "llvm/Analysis/LoopInfo.h" #include "global.h" -#include "callstring.h" #include "value_set.h" #include "simple_interval.h" diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp index 66a21c4..db03983 100644 --- a/src/fixpoint_two_var_eq.cpp +++ b/src/fixpoint_two_var_eq.cpp @@ -8,7 +8,6 @@ // //#include "global.h" //#include "general.h" -//#include "callstring.h" //#include "value_set.h" // //namespace pcpo { diff --git a/src/fixpoint_widening.cpp b/src/fixpoint_widening.cpp index feb2749..175d5ec 100644 --- a/src/fixpoint_widening.cpp +++ b/src/fixpoint_widening.cpp @@ -8,7 +8,6 @@ #include "global.h" #include "general.h" -#include "callstring.h" #include "value_set.h" #include "simple_interval.h" diff --git a/src/value_set.h b/src/value_set.h index 074216a..26a8104 100644 --- a/src/value_set.h +++ b/src/value_set.h @@ -11,7 +11,6 @@ #include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h" #include "global.h" -#include "callstring.h" namespace pcpo { -- GitLab From e078877e1c6c6d1417541eefd7d49b062fc3d79d Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 23:37:02 +0100 Subject: [PATCH 047/142] removed unecessary default case --- src/simple_interval.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/simple_interval.cpp b/src/simple_interval.cpp index fb60d3c..2e60e11 100644 --- a/src/simple_interval.cpp +++ b/src/simple_interval.cpp @@ -232,9 +232,6 @@ SimpleInterval SimpleInterval::merge(Merge_op::Type op, SimpleInterval a, Simple case Merge_op::UPPER_BOUND: return a._upperBound(b)._makeTopSpecial(); case Merge_op::WIDEN: return a._widen (b)._makeTopSpecial(); case Merge_op::NARROW: return a._narrow (b)._makeTopSpecial(); - default: - assert(false /* invalid op value */); - return SimpleInterval {true}; } } -- GitLab From 7683978d61ea98e4c0d376d17c0dad45decac268 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 30 Jan 2020 23:53:15 +0100 Subject: [PATCH 048/142] removed unused includes --- src/fixpoint.cpp | 2 -- src/simple_interval.h | 1 - src/value_set.h | 4 ---- 3 files changed, 7 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 60c41ad..e024949 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -5,8 +5,6 @@ #include "llvm/IR/CFG.h" #include "llvm/IR/Module.h" -#include "llvm/IR/Dominators.h" -#include "llvm/Analysis/LoopInfo.h" #include "global.h" diff --git a/src/simple_interval.h b/src/simple_interval.h index 0a622bc..7039263 100644 --- a/src/simple_interval.h +++ b/src/simple_interval.h @@ -1,7 +1,6 @@ #pragma once #include -#include #include #include "global.h" diff --git a/src/value_set.h b/src/value_set.h index 26a8104..075c32a 100644 --- a/src/value_set.h +++ b/src/value_set.h @@ -3,12 +3,8 @@ #include #include -#include "llvm/IR/BasicBlock.h" -#include "llvm/IR/Constant.h" #include "llvm/IR/CFG.h" -#include "llvm/IR/InstrTypes.h" #include "llvm/IR/Instructions.h" -#include "llvm/Transforms/Utils/UnifyFunctionExitNodes.h" #include "global.h" -- GitLab From 3b919389efa5aab400a5448a7735c2331f83bd80 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 31 Jan 2020 02:42:14 +0100 Subject: [PATCH 049/142] implemented limiting of callstack depth --- src/fixpoint.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index e024949..be614f4 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -75,6 +75,18 @@ struct Node { } }; +Callstring callstring_from(Callstring const& callstring, int max_length) { + Callstring new_callstring; + for (auto call: callstring) { + if (--max_length > 0) { + new_callstring.push_back(call); + } else { + return new_callstring; + } + } + return new_callstring; +} + // Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface // documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods @@ -204,7 +216,7 @@ void executeFixpointAlgorithm(Module const& M) { continue; } - Callstring new_callstring = node.callstring; + Callstring new_callstring = callstring_from(node.callstring, callstack_depth); new_callstring.push_back(callee_func); -- GitLab From 85b49a8f89a3ac960f49c98a067f7c5317de5cf5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 7 Feb 2020 01:18:47 +0100 Subject: [PATCH 050/142] fixed counter --- src/fixpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index be614f4..712842c 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -78,7 +78,7 @@ struct Node { Callstring callstring_from(Callstring const& callstring, int max_length) { Callstring new_callstring; for (auto call: callstring) { - if (--max_length > 0) { + if (max_length-- > 0) { new_callstring.push_back(call); } else { return new_callstring; -- GitLab From c53bdf3ecaca8b958662346e676aec48f5f413cf Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 7 Feb 2020 01:19:44 +0100 Subject: [PATCH 051/142] extracted register function --- src/fixpoint.cpp | 64 ++++++++++++++++++++++++------------------------ 1 file changed, 32 insertions(+), 32 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 712842c..67503c2 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -75,7 +75,7 @@ struct Node { } }; -Callstring callstring_from(Callstring const& callstring, int max_length) { +Callstring callstring_for(Function const* function, Callstring const& callstring, int max_length) { Callstring new_callstring; for (auto call: callstring) { if (max_length-- > 0) { @@ -84,10 +84,37 @@ Callstring callstring_from(Callstring const& callstring, int max_length) { return new_callstring; } } + new_callstring.push_back(function); return new_callstring; } +template +vector*> register_function(llvm::Function const* function, Callstring const& callstring, int callstack_depth, unordered_map> &nodes) { + Callstring new_callstring = callstring_for(function, callstring, callstack_depth); + vector*> inserted_nodes; + for (BasicBlock const& basic_block: *function) { + dbgs(1) << " Found basic block: " << basic_block.getName() << '\n'; + NodeKey key = {new_callstring, &basic_block}; + Node node = {&basic_block, new_callstring}; + if (node.isEntry()) { + node.state = AbstractState {*node.function()}; + } + nodes[key] = node; + inserted_nodes.push_back(&nodes[{new_callstring, &basic_block}]); + } + return inserted_nodes; +} + +template + void add_to_worklist(vector*> &nodes, vector*> &worklist) { + for (Node* node : nodes) { + node->update_scheduled = true; + worklist.push_back(node); + } +} + + // Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface // documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods // with the right signatures and take care to fulfil the contracts outlines above). @@ -116,23 +143,8 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Register basic blocks of the main function - for (BasicBlock const& basic_block: *main_func) { - dbgs(1) << " Found basic block main." << basic_block.getName() << '\n'; - - Node node; - node.basic_block = &basic_block; - node.callstring = {main_func}; - // node.state is default initialised (to bottom) - - // nodes of main block have the callstring of a dummy block - nodes[{{main_func}, &basic_block}] = node; - } - - // Push the initial block into the worklist - NodeKey init_element = {{main_func}, &main_func->getEntryBlock()}; - worklist.push_back(&nodes[init_element]); - nodes[init_element].update_scheduled = true; - nodes[init_element].state = AbstractState {*main_func}; + auto registered = register_function(main_func, {}, callstack_depth, nodes); + add_to_worklist(registered, worklist); dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; @@ -216,9 +228,7 @@ void executeFixpointAlgorithm(Module const& M) { continue; } - Callstring new_callstring = callstring_from(node.callstring, callstack_depth); - new_callstring.push_back(callee_func); - + Callstring new_callstring = callstring_for(callee_func, node.callstring, callstack_depth); NodeKey callee_element = {new_callstring, &callee_func->getEntryBlock()}; bool changed; @@ -230,17 +240,7 @@ void executeFixpointAlgorithm(Module const& M) { // Check if abstract_state of call.bb is bottom or not dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; - // Register basic blocks - for (BasicBlock const& basic_block : *callee_func) { - dbgs(4) << " Found basic block " << basic_block << '\n'; - - Node callee_node; - callee_node.basic_block = &basic_block; - callee_node.callstring = new_callstring; - // node.state is default initialised (to bottom) - - nodes[{new_callstring, &basic_block}] = callee_node; - } + register_function(callee_func, node.callstring, callstack_depth, nodes); nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; changed = true; -- GitLab From ff909dae85641ff9cc91bab806d92c03f5af4ef8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 9 Feb 2020 00:46:40 +0100 Subject: [PATCH 052/142] adding all basic blocks of function to worklist --- src/fixpoint.cpp | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 67503c2..7d4936e 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -143,8 +143,8 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; // Register basic blocks of the main function - auto registered = register_function(main_func, {}, callstack_depth, nodes); - add_to_worklist(registered, worklist); + auto main_basic_blocks = register_function(main_func, {}, callstack_depth, nodes); + add_to_worklist(main_basic_blocks, worklist); dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") << ". Starting fixpoint iteration...\n"; @@ -231,6 +231,7 @@ void executeFixpointAlgorithm(Module const& M) { Callstring new_callstring = callstring_for(callee_func, node.callstring, callstack_depth); NodeKey callee_element = {new_callstring, &callee_func->getEntryBlock()}; + vector callee_basic_blocks; bool changed; // Checks whether a node with key [%callee entry block, %caller basic block], @@ -240,7 +241,7 @@ void executeFixpointAlgorithm(Module const& M) { // Check if abstract_state of call.bb is bottom or not dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; - register_function(callee_func, node.callstring, callstack_depth, nodes); + callee_basic_blocks = register_function(callee_func, node.callstring, callstack_depth, nodes); nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; changed = true; @@ -267,14 +268,15 @@ void executeFixpointAlgorithm(Module const& M) { // Checks if the key of the callee functions entry node is already on the worklist, // this is necessary for recursions. - if (not nodes[callee_element].update_scheduled) { - auto& elem = nodes[callee_element]; - worklist.push_back(&elem); - elem.update_scheduled = true; - - dbgs(3) << " Adding callee " << callee_element << " to worklist\n"; - } else { - dbgs(3) << " Callee already on worklist, nothing to add...\n"; + for (Node* elem: callee_basic_blocks) { + if (!elem->update_scheduled) { + worklist.push_back(elem); + elem->update_scheduled = true; + + dbgs(3) << " Adding callee " << elem->callstring << " to worklist\n"; + } else { + dbgs(3) << " Callee already on worklist, nothing to add...\n"; + } } } } else { -- GitLab From 8afaf82afd8d6473e1e61be7c8a5e4bc789c07e3 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 9 Feb 2020 13:30:39 +0100 Subject: [PATCH 053/142] fixed order (rpo) of registered basic_blocks --- src/fixpoint.cpp | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 7d4936e..3dd9277 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -16,6 +16,8 @@ #include "fixpoint_two_var_eq.cpp" #include "hash_utils.h" +#include "llvm/ADT/PostOrderIterator.h" + namespace pcpo { using namespace llvm; @@ -93,15 +95,21 @@ template vector*> register_function(llvm::Function const* function, Callstring const& callstring, int callstack_depth, unordered_map> &nodes) { Callstring new_callstring = callstring_for(function, callstring, callstack_depth); vector*> inserted_nodes; - for (BasicBlock const& basic_block: *function) { - dbgs(1) << " Found basic block: " << basic_block.getName() << '\n'; - NodeKey key = {new_callstring, &basic_block}; - Node node = {&basic_block, new_callstring}; + + for (po_iterator I = po_begin(&function->getEntryBlock()), + IE = po_end(&function->getEntryBlock()); + I != IE; ++I) { + + BasicBlock const* basic_block = *I; + + dbgs(1) << " Found basic block: " << basic_block->getName() << '\n'; + NodeKey key = {new_callstring, basic_block}; + Node node = {basic_block, new_callstring}; if (node.isEntry()) { node.state = AbstractState {*node.function()}; } + inserted_nodes.push_back(&nodes[key]); nodes[key] = node; - inserted_nodes.push_back(&nodes[{new_callstring, &basic_block}]); } return inserted_nodes; } -- GitLab From 75a597ad2314ae35f54f2d955e7d451b32292187 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 10 Feb 2020 14:30:44 +0100 Subject: [PATCH 054/142] fixed handling of void returns --- src/normalized_conjunction.cpp | 2 +- src/value_set.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index ca398f0..dc4658d 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -78,7 +78,7 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); - if (ret_val->getType()->isIntegerTy()) { + if (ret_val && ret_val->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(ret_val)) { values[&inst] = LinearEquality(c); } else if (values.find(ret_val) != values.end()) { diff --git a/src/value_set.h b/src/value_set.h index 075c32a..15d8c64 100644 --- a/src/value_set.h +++ b/src/value_set.h @@ -168,7 +168,7 @@ public: // Handles the evaluation of return instructions void applyReturnInst(llvm::Instruction const& inst) { llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); - if (ret_val->getType()->isIntegerTy()) { + if (ret_val && ret_val->getType()->isIntegerTy()) { if (llvm::Constant const* c = llvm::dyn_cast(ret_val)) { values[&inst] = AbstractDomain{ *c }; } else if (values.find(ret_val) != values.end()) { -- GitLab From 6e7aab83acbfe5200884de76f308ae98bc0555c6 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 10 Feb 2020 16:21:12 +0100 Subject: [PATCH 055/142] lllvm:phiNode -> llvm::instruction --- src/normalized_conjunction.cpp | 2 +- src/normalized_conjunction.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index dc4658d..20a5d42 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -42,7 +42,7 @@ NormalizedConjunction::NormalizedConjunction(std::unordered_map pred_values, - llvm::PHINode const *phi) { + llvm::Instruction const& phi) { std::vector operands; // Phi nodes are handled here, to get the precise values of the predecessors diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index b4a2343..476ef93 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -21,6 +21,7 @@ namespace pcpo { class NormalizedConjunction { public: std::unordered_map values; + bool isBottom = false; NormalizedConjunction() = default; NormalizedConjunction(NormalizedConjunction const& state) = default; @@ -32,7 +33,7 @@ public: explicit NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call); /// Handles the evaluation of merging points - void applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, llvm::PHINode const* phi); + void applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, llvm::Instruction const& phi); /// Handles the evaluation of function calls /// This is the "combine" function as described in "Compiler Design: Analysis and Transformation" void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state); -- GitLab From f5bd28eb08f486ed8c56f02a0eb3fb2bc9cb3f9f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 21 Feb 2020 13:25:08 +0100 Subject: [PATCH 056/142] fixed debug output --- src/fixpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 3dd9277..011e1a2 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -281,7 +281,7 @@ void executeFixpointAlgorithm(Module const& M) { worklist.push_back(elem); elem->update_scheduled = true; - dbgs(3) << " Adding callee " << elem->callstring << " to worklist\n"; + dbgs(3) << " Adding callee " << *elem->basic_block << " " << elem->callstring << " to worklist\n"; } else { dbgs(3) << " Callee already on worklist, nothing to add...\n"; } -- GitLab From 2df3ddcc60c8afbd07402cdbe01a44dbc5a18a2a Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 25 Feb 2020 18:49:28 +0100 Subject: [PATCH 057/142] added merge test --- test/normalized_conjunction_test.cpp | 42 ++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index 7c72255..c99555c 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -15,6 +15,7 @@ class NormalizedConjunctionTest: NormalizedConjunction { public: static bool runTestAll(); + static bool runTestMerge(); static bool runTestX0(); static bool runTestX1(); static bool runTestX2(); @@ -91,6 +92,46 @@ bool NormalizedConjunctionTest::runTestAll() { return result; } +bool NormalizedConjunctionTest::runTestMerge() { + std::cout << "Testing all: "; + bool result = false; + std::unordered_map expected = { + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x3, 15}}, + {x7, {x7, 1, x6, -1}}, + {x10, {x10, 2, x9, 2}}, + {x12, {x12, 2, x11, 1}} + }; + + std::unordered_map x = { + {x1, {x1, 1, x1, 0}}, + {x2, {x2, 1, x2, 0}}, + {x3, {x3, 1, x1, 0}}, + {x4, {x4, 3, x2, 5}}, + {x5, {x5, 3, x1, 15}}, + {x6, {x6, 1, x1, 3}}, + {x7, {x7, 1, x1, 2}}, + {x8, {x8, 7, x1, 15}}, + {x9, {x9, 1, nullptr, 0}}, + {x10, {x10, 1, nullptr, 2}}, + {x11, {x11, 1, nullptr, 1}}, + {x12, {x12, 1, nullptr, 3}} + }; + + std::unordered_map y = { + + }; + + auto actual = NormalizedConjunction(x); + auto other = NormalizedConjunction(y); + actual.merge(Merge_op::UPPER_BOUND, other); + + result = actual.values == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + bool NormalizedConjunctionTest::runTestX0() { std::cout << "Testing X0: "; bool result = true; @@ -274,6 +315,7 @@ int main() { && NormalizedConjunctionTest::runTestX2() && NormalizedConjunctionTest::runTestX4() && NormalizedConjunctionTest::runTestAll() + && NormalizedConjunctionTest::runTestMerge() && NormalizedConjunctionTest::runNonDeterministicAssignmentTest1() && NormalizedConjunctionTest::runNonDeterministicAssignmentTest2() && NormalizedConjunctionTest::runLinearAssignmentTest1() -- GitLab From 28942d73ce0c1e8ccc27d4e901c08c3fca06fac2 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 25 Feb 2020 18:49:47 +0100 Subject: [PATCH 058/142] simplified example --- samples/for.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/samples/for.c b/samples/for.c index addc742..2e19bda 100644 --- a/samples/for.c +++ b/samples/for.c @@ -1,5 +1,3 @@ -#include -#include // test program: // simple loop int xex(int b) { @@ -7,7 +5,7 @@ int xex(int b) { } -int main(int argc, char const *argv[]) { +int main() { int x = -7; x++; for (int i = 0; i < 500; i++) -- GitLab From c6edcbef974250711746503ee6ab3d2e722a9792 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 25 Feb 2020 18:50:33 +0100 Subject: [PATCH 059/142] fixed bottom --- src/normalized_conjunction.cpp | 14 +++++++++++++- src/normalized_conjunction.h | 2 +- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 20a5d42..54e9c11 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -17,6 +17,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { for (llvm::Argument const& arg: f.args()) { values[&arg] = LinearEquality(&arg); } + isBottom = false; } NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call) { @@ -31,10 +32,12 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, } } } + isBottom = false; } NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { this->values = equalaties; + isBottom = equalaties.empty(); } @@ -123,7 +126,16 @@ void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { } bool NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction const& other) { - + bool changed = false; + + if (other.isBottom) { + return false; + } else if (isBottom) { + values = other.values; + isBottom = false; + return true; + } + switch (op) { case Merge_op::UPPER_BOUND: return leastUpperBound(other); default: abort(); diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 476ef93..b52062b 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -21,7 +21,7 @@ namespace pcpo { class NormalizedConjunction { public: std::unordered_map values; - bool isBottom = false; + bool isBottom = true; NormalizedConjunction() = default; NormalizedConjunction(NormalizedConjunction const& state) = default; -- GitLab From e73653a612d146c263eb756b48fa982e811a6a89 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 25 Feb 2020 18:50:52 +0100 Subject: [PATCH 060/142] fixed phi --- src/normalized_conjunction.cpp | 30 +++++++++++++++++++++++++----- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 54e9c11..138e29a 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -7,6 +7,8 @@ #include "normalized_conjunction.h" +#include "llvm/IR/CFG.h" + using namespace llvm; namespace pcpo { @@ -46,13 +48,31 @@ NormalizedConjunction::NormalizedConjunction(std::unordered_map pred_values, llvm::Instruction const& phi) { - std::vector operands; + llvm::PHINode const* phiNode = llvm::dyn_cast(&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]; - // Phi nodes are handled here, to get the precise values of the predecessors - for (auto& incoming_state: pred_values) { - merge(Merge_op::UPPER_BOUND, incoming_state); - operands.push_back(incoming_state); // Keep the debug output happy +// LinearEquality pred_value = incoming_state.getAbstractValue(incoming_value); + + if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { + linearAssignment(&phi, 1, nullptr, c->getSExtValue()); + } else { + LinearEquality pred_value = incoming_state.values[&incoming_value]; + linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); + } + + i++; } + +// // Phi nodes are handled here, to get the precise values of the predecessors +// for (auto& incoming_state: pred_values) { +// merge(Merge_op::UPPER_BOUND, incoming_state); +// operands.push_back(incoming_state); // Keep the debug output happy +// } } void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state) { -- GitLab From d22d6e9d8cd2568624bde8f74578e01b877a497d Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 26 Feb 2020 18:28:33 +0100 Subject: [PATCH 061/142] added sample code generator to cmake --- CMakeLists.txt | 73 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 72 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b776213..9f5806a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ target_link_libraries(normalized_conjunction_test enable_testing() add_test(NAME intervalAnalysisTest - COMMAND opt --load $ --painpass -S ${CMAKE_SOURCE_DIR}/output/add-1.c.ll + COMMAND opt --load $ --painpass -S ${CMAKE_SOURCE_DIR}/foo/add-1.ll ) add_test(NAME simpleIntervalTest @@ -168,3 +168,74 @@ add_test(NAME simpleIntervalTest add_test(NAME normalizedConjunctionTest COMMAND normalized_conjunction_test ) + +set(SAMPLES + add-1-float + add-1 + add-2 + add-3 + add-4 + basic_function + branching + cmp-two-variables-1 + cmp-two-variables-2 + euler-48 + euler + example + for-loop-1 + for + func-test-1 + func-test-2 + func-test-3 + func-test-4 + func-test-5 + func-test-for + func-test-rec-endless + func-test-rec + func-test-switch + gcd + goto + if-and + if-multiple-of-4 + if-then-else-2 + if-then-else-complicated + if-then-else-two-constraints + if-then-else + multiple-functions + ops + switch-2 + switch-3 + switch-two-labels + switch + while-1 + while-2 + while-bigger-steps + while-neg +) + +file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/output) + +if (APPLE) + +foreach(src ${SAMPLES}) + add_custom_command( + TARGET normalized_conjunction_test + COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/foo/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/foo/${src}.bc -o ${CMAKE_SOURCE_DIR}/foo/${src}.ll + DEPENDS clang opt + ) +endforeach(src) + +else() + +foreach(src ${SAMPLES}) + add_custom_command( + TARGET normalized_conjunction_test + COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/foo/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/foo/${src}.bc -o ${CMAKE_SOURCE_DIR}/foo/${src}.ll + DEPENDS clang opt + ) +endforeach(src) + + +endif() -- GitLab From 9fae05753fa004c916060eb9954bd3455f14db4c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Wed, 26 Feb 2020 18:30:55 +0100 Subject: [PATCH 062/142] fixed variable naming --- CMakeLists.txt | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9f5806a..25a536f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -98,7 +98,7 @@ else() ) endif() -set(pain_sources +set(PAIN_SOURCES src/fixpoint.cpp src/fixpoint_widening.cpp src/fixpoint_two_var_eq.cpp @@ -108,7 +108,7 @@ set(pain_sources src/linear_equality.cpp ) -set(pain_headers +set(PAIN_HEADERS src/fixpoint.h src/value_set.h src/simple_interval.h @@ -123,8 +123,8 @@ set(pain_headers include_directories(${LLVM_INCLUDE_DIRS}) add_llvm_library(llvm-pain MODULE - ${pain_sources} - ${pain_headers} + ${PAIN_SOURCES} + ${PAIN_HEADERS} DEPENDS intrinsics_gen PLUGIN_TOOL @@ -133,8 +133,8 @@ add_llvm_library(llvm-pain MODULE add_llvm_executable(simple_interval_test test/simple_interval_test.cpp - ${pain_headers} - ${pain_sources} + ${PAIN_HEADERS} + ${PAIN_SOURCES} DEPENDS intrinsics_gen ) @@ -145,8 +145,8 @@ target_link_libraries(simple_interval_test add_llvm_executable(normalized_conjunction_test test/normalized_conjunction_test.cpp - ${pain_headers} - ${pain_sources} + ${PAIN_HEADERS} + ${PAIN_SOURCES} DEPENDS intrinsics_gen ) -- GitLab From edef65109621e914ed2a69495c073910c1a86918 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 28 Feb 2020 14:01:05 +0100 Subject: [PATCH 063/142] fixed folder name --- CMakeLists.txt | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 25a536f..585b11c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -158,7 +158,7 @@ target_link_libraries(normalized_conjunction_test enable_testing() add_test(NAME intervalAnalysisTest - COMMAND opt --load $ --painpass -S ${CMAKE_SOURCE_DIR}/foo/add-1.ll + COMMAND opt --load $ --painpass -S ${CMAKE_SOURCE_DIR}/output/add-1.ll ) add_test(NAME simpleIntervalTest @@ -220,8 +220,8 @@ if (APPLE) foreach(src ${SAMPLES}) add_custom_command( TARGET normalized_conjunction_test - COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/foo/${src}.bc - COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/foo/${src}.bc -o ${CMAKE_SOURCE_DIR}/foo/${src}.ll + COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll DEPENDS clang opt ) endforeach(src) @@ -231,8 +231,8 @@ else() foreach(src ${SAMPLES}) add_custom_command( TARGET normalized_conjunction_test - COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/foo/${src}.bc - COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/foo/${src}.bc -o ${CMAKE_SOURCE_DIR}/foo/${src}.ll + COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll DEPENDS clang opt ) endforeach(src) -- GitLab From d066e1619ab8a5c23059fc88f79d2ae5d1b9d81c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 28 Feb 2020 15:53:37 +0100 Subject: [PATCH 064/142] fixed test name --- test/normalized_conjunction_test.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index c99555c..899c2f3 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -93,7 +93,7 @@ bool NormalizedConjunctionTest::runTestAll() { } bool NormalizedConjunctionTest::runTestMerge() { - std::cout << "Testing all: "; + std::cout << "Testing merge: "; bool result = false; std::unordered_map expected = { {x4, {x4, 3, x2, 5}}, -- GitLab From 311e85a6b0fb00e5a2f0427750ee2b377727dd58 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 28 Feb 2020 15:53:59 +0100 Subject: [PATCH 065/142] improved sample ir generation --- CMakeLists.txt | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 585b11c..9b13cf9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -127,6 +127,7 @@ add_llvm_library(llvm-pain MODULE ${PAIN_HEADERS} DEPENDS intrinsics_gen + irgen PLUGIN_TOOL opt ) @@ -136,6 +137,7 @@ add_llvm_executable(simple_interval_test ${PAIN_HEADERS} ${PAIN_SOURCES} DEPENDS + irgen intrinsics_gen ) @@ -148,6 +150,7 @@ add_llvm_executable(normalized_conjunction_test ${PAIN_HEADERS} ${PAIN_SOURCES} DEPENDS + irgen intrinsics_gen ) @@ -213,16 +216,19 @@ set(SAMPLES while-neg ) +add_custom_target(irgen) + file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/output) if (APPLE) foreach(src ${SAMPLES}) add_custom_command( - TARGET normalized_conjunction_test - COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc - COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll + TARGET irgen + COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll DEPENDS clang opt + COMMENT "Generating LLVM IR for example ${src}" ) endforeach(src) @@ -230,12 +236,13 @@ else() foreach(src ${SAMPLES}) add_custom_command( - TARGET normalized_conjunction_test - COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc - COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll + TARGET irgen + COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc + COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll DEPENDS clang opt + COMMENT "Generating LLVM IR for example ${src}" + ) endforeach(src) - endif() -- GitLab From 1d8af22f8b939b47dd888f2a59d2c7eded194d4d Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 28 Feb 2020 16:58:45 +0100 Subject: [PATCH 066/142] fixed tests --- test/normalized_conjunction_test.cpp | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/test/normalized_conjunction_test.cpp b/test/normalized_conjunction_test.cpp index 899c2f3..8872674 100644 --- a/test/normalized_conjunction_test.cpp +++ b/test/normalized_conjunction_test.cpp @@ -95,13 +95,6 @@ bool NormalizedConjunctionTest::runTestAll() { bool NormalizedConjunctionTest::runTestMerge() { std::cout << "Testing merge: "; bool result = false; - std::unordered_map expected = { - {x4, {x4, 3, x2, 5}}, - {x5, {x5, 3, x3, 15}}, - {x7, {x7, 1, x6, -1}}, - {x10, {x10, 2, x9, 2}}, - {x12, {x12, 2, x11, 1}} - }; std::unordered_map x = { {x1, {x1, 1, x1, 0}}, @@ -126,7 +119,7 @@ bool NormalizedConjunctionTest::runTestMerge() { auto other = NormalizedConjunction(y); actual.merge(Merge_op::UPPER_BOUND, other); - result = actual.values == expected; + result = actual.values == x; std::cout << (result? "success" : "failed") << "\n"; return result; -- GitLab From bb9ede94e43081a4481b990486f28360375c11c8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 1 Mar 2020 16:31:44 +0100 Subject: [PATCH 067/142] function without args is bottom --- src/normalized_conjunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 138e29a..a860870 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -19,7 +19,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { for (llvm::Argument const& arg: f.args()) { values[&arg] = LinearEquality(&arg); } - isBottom = false; + isBottom = f.arg_empty(); } NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call) { -- GitLab From af7f9c207b7b39bd1906c4cde9ced7bb19682c22 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 1 Mar 2020 17:05:36 +0100 Subject: [PATCH 068/142] fixed function call initializer --- src/normalized_conjunction.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index a860870..8c2ed45 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -28,9 +28,10 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, llvm::Value* value = call->getArgOperand(arg.getArgNo()); if (value->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { - values[&arg] = LinearEquality(c); + values[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; } else { - values[&arg] = state.values.at(value); + LinearEquality value_equality = state.values.at(value); + values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; } } } -- GitLab From a2f98b8e2864e14bf8734858a871e0d71299a4bf Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 1 Mar 2020 19:10:58 +0100 Subject: [PATCH 069/142] setting state before merge prevents merge --- src/fixpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 011e1a2..d84d096 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -172,8 +172,8 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(1) << " Merging function parameters, is entry block\n"; // if it is the entry node, then its state should be top - state_new.isBottom = false; state_new.merge(merge_op, node.state); + state_new.isBottom = false; } dbgs(1) << " Merge of " << pred_size(node.basic_block) -- GitLab From e587f830c028af125131c3c881893973d6869375 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 2 Mar 2020 14:53:14 +0100 Subject: [PATCH 070/142] improved build speed --- CMakeLists.txt | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 9b13cf9..e82528f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -216,7 +216,16 @@ set(SAMPLES while-neg ) -add_custom_target(irgen) +list(TRANSFORM SAMPLES APPEND ".c" OUTPUT_VARIABLE SAMPLES_C) +list(TRANSFORM SAMPLES APPEND ".bc" OUTPUT_VARIABLE SAMPLES_BC) +list(TRANSFORM SAMPLES APPEND ".ll" OUTPUT_VARIABLE SAMPLES_LL) +list(TRANSFORM SAMPLES_LL PREPEND "${CMAKE_SOURCE_DIR}/output/") +list(TRANSFORM SAMPLES_BC PREPEND "${CMAKE_SOURCE_DIR}/output/") +list(TRANSFORM SAMPLES_C PREPEND "${CMAKE_SOURCE_DIR}/samples/") + +add_custom_target(irgen + DEPENDS ${SAMPLES_LL} ${SAMPLES_BC} +) file(MAKE_DIRECTORY ${CMAKE_SOURCE_DIR}/output) @@ -224,10 +233,10 @@ if (APPLE) foreach(src ${SAMPLES}) add_custom_command( - TARGET irgen + OUTPUT ${CMAKE_SOURCE_DIR}/output/${src}.ll COMMAND clang --sysroot ${CMAKE_OSX_SYSROOT} -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll - DEPENDS clang opt + DEPENDS clang opt ${CMAKE_SOURCE_DIR}/samples/${src}.c COMMENT "Generating LLVM IR for example ${src}" ) endforeach(src) @@ -236,12 +245,11 @@ else() foreach(src ${SAMPLES}) add_custom_command( - TARGET irgen + OUTPUT ${CMAKE_SOURCE_DIR}/output/${src}.ll COMMAND clang -O0 -emit-llvm ${CMAKE_SOURCE_DIR}/samples/${src}.c -Xclang -disable-O0-optnone -c -o ${CMAKE_SOURCE_DIR}/output/${src}.bc COMMAND opt -S -mem2reg ${CMAKE_SOURCE_DIR}/output/${src}.bc -o ${CMAKE_SOURCE_DIR}/output/${src}.ll - DEPENDS clang opt + DEPENDS clang opt ${CMAKE_SOURCE_DIR}/samples/${src}.c COMMENT "Generating LLVM IR for example ${src}" - ) endforeach(src) -- GitLab From 42cf82ffe37647bf1a12073e7259e3b5d8fda880 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 2 Mar 2020 18:35:35 +0100 Subject: [PATCH 071/142] improved phi --- src/normalized_conjunction.cpp | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 8c2ed45..5b62857 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -31,6 +31,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, values[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; } else { LinearEquality value_equality = state.values.at(value); + LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; } } @@ -57,23 +58,18 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector auto& incoming_value = *phiNode->getIncomingValueForBlock(pred_bb); auto& incoming_state = pred_values[i]; -// LinearEquality pred_value = incoming_state.getAbstractValue(incoming_value); - if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { linearAssignment(&phi, 1, nullptr, c->getSExtValue()); } else { - LinearEquality pred_value = incoming_state.values[&incoming_value]; - linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); + if (incoming_state.values.count(&incoming_value) != 0) { + LinearEquality pred_value = incoming_state.values[&incoming_value]; + linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); + } else { + nonDeterminsticAssignment(&phi); + } } - i++; } - -// // Phi nodes are handled here, to get the precise values of the predecessors -// for (auto& incoming_state: pred_values) { -// merge(Merge_op::UPPER_BOUND, incoming_state); -// operands.push_back(incoming_state); // Keep the debug output happy -// } } void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state) { -- GitLab From a95e02525d9affca5f9c11b1360eed93b6ddd98e Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 3 Mar 2020 13:45:07 +0100 Subject: [PATCH 072/142] debug changes **revert this** --- src/fixpoint.cpp | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index d84d096..55943ae 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -105,9 +105,9 @@ vector*> register_function(llvm::Function const* function, C dbgs(1) << " Found basic block: " << basic_block->getName() << '\n'; NodeKey key = {new_callstring, basic_block}; Node node = {basic_block, new_callstring}; - if (node.isEntry()) { - node.state = AbstractState {*node.function()}; - } +// if (node.isEntry()) { +// node.state = AbstractState {*node.function()}; +// } inserted_nodes.push_back(&nodes[key]); nodes[key] = node; } @@ -226,7 +226,7 @@ void executeFixpointAlgorithm(Module const& M) { // Checks if an input parameter for the callee is bottom. If so, // then skip the calculation of the call instruction for now - if (state_new.checkOperandsForBottom(inst)) continue; +// if (state_new.checkOperandsForBottom(inst)) continue; Function const* callee_func = call->getCalledFunction(); @@ -254,8 +254,22 @@ void executeFixpointAlgorithm(Module const& M) { nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; changed = true; } else { - AbstractState state_update{ callee_func, state_new, call }; - changed = nodes[callee_element].state.merge(merge_op, state_update); + //update callee + NormalizedConjunction before = nodes[callee_element].state; + for (llvm::Argument const& arg: callee_func->args()) { + llvm::Value* value = call->getArgOperand(arg.getArgNo()); + if (value->getType()->isIntegerTy()) { + if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { + nodes[callee_element].state.values[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; + } else { + LinearEquality value_equality = state_new.values.at(value); + LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; + nodes[callee_element].state.values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; + } + } + } + nodes[callee_element].state.isBottom = false; + changed = before.values != nodes[callee_element].state.values; } //Getting the last block @@ -288,7 +302,7 @@ void executeFixpointAlgorithm(Module const& M) { } } } else { - if (state_new.checkOperandsForBottom(inst)) continue; +// if (state_new.checkOperandsForBottom(inst)) continue; state_new.applyDefault(inst); } } @@ -336,10 +350,10 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening - executeFixpointAlgorithm(M); +// executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); -// executeFixpointAlgorithmTwoVarEq(M); + executeFixpointAlgorithm(M); // We never change anything return false; -- GitLab From 364149a07dd69ca6dfe9b98408f2e2b200b428da Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 3 Mar 2020 15:51:55 +0100 Subject: [PATCH 073/142] formating --- src/normalized_conjunction.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 5b62857..9df69b9 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -159,7 +159,7 @@ bool NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction const } } -// MARK: Lattice Operations +// MARK: - Lattice Operations bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { // set of all occuring variables in E1 and E2 @@ -219,6 +219,8 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { return changed; } +// MARK: Helpers + /// XO / E'0: set of variables where the right hand side in E1 and E2 coincide std::set NormalizedConjunction::computeX0(std::set const& E1, std::set const& E2) { std::set X0; @@ -382,7 +384,7 @@ std::set NormalizedConjunction::computeX4(std::set Date: Fri, 6 Mar 2020 00:52:08 +0100 Subject: [PATCH 074/142] formating --- src/normalized_conjunction.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 9df69b9..03b2fa0 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -387,7 +387,7 @@ std::set NormalizedConjunction::computeX4(std::set operands) { dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; -- GitLab From b9cdca6dfe5559f16ab9da7f302da491c18796f7 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 00:52:18 +0100 Subject: [PATCH 075/142] added lldbinit --- .lldbinit | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .lldbinit diff --git a/.lldbinit b/.lldbinit new file mode 100644 index 0000000..68ce1e0 --- /dev/null +++ b/.lldbinit @@ -0,0 +1,2 @@ +command regex dump 's/(.+)/expression -O -l c++ -- ((llvm::Value*) %1 )->dump()/' +command alias d dump \ No newline at end of file -- GitLab From 4b54cd7513dfd6abe177258d40e3cd82ceefab6f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 00:53:12 +0100 Subject: [PATCH 076/142] improved debug output --- src/fixpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 55943ae..f004a53 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -211,7 +211,7 @@ void executeFixpointAlgorithm(Module const& M) { if (inst.use_empty()) { // Except for call instructions, we still want to get that information if (not isa(&inst)) { - dbgs(3) << " Empty use of instruction, skipping...\n"; + dbgs(3) << " Empty use of instruction, " << inst.getOpcodeName() << " skipping...\n"; continue; } } -- GitLab From ee31d33a58c44ce037fc8f79a4b36a96d8c7c005 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 00:53:43 +0100 Subject: [PATCH 077/142] Added [] operator for NormalizedConjunction --- src/fixpoint.cpp | 6 +++--- src/normalized_conjunction.cpp | 10 ++++++++++ src/normalized_conjunction.h | 3 +++ 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index f004a53..a75c07a 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -260,11 +260,11 @@ void executeFixpointAlgorithm(Module const& M) { llvm::Value* value = call->getArgOperand(arg.getArgNo()); if (value->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { - nodes[callee_element].state.values[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; + nodes[callee_element].state[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; } else { - LinearEquality value_equality = state_new.values.at(value); + LinearEquality value_equality = state_new[value]; LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; - nodes[callee_element].state.values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; + nodes[callee_element].state[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; } } } diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 03b2fa0..de13022 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -553,6 +553,16 @@ void NormalizedConjunction::Mul(Instruction const& inst) { } } +// MARK: - Operators + +LinearEquality& NormalizedConjunction::operator[](Value const* value) { + if (values.count(value) == 0) { + LinearEquality eq = {value, 1, value, 0}; + values[value] = eq; + } + return values[value]; +} + // MARK: - Debug void NormalizedConjunction::debug_output(llvm::Instruction const& inst, std::vector operands) { diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index b52062b..beb64d8 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -51,6 +51,9 @@ public: // Abstract Assignments void linearAssignment(llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); void nonDeterminsticAssignment(llvm::Value const* xi); + + // Operators + LinearEquality& operator[](llvm::Value const*); protected: // Abstract Operators -- GitLab From 34d5e60b65b720a8f51c483e875e39b47734a3ea Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 00:55:39 +0100 Subject: [PATCH 078/142] Added set of validVariables --- src/normalized_conjunction.cpp | 31 ++++++++++++++++++++++++------- src/normalized_conjunction.h | 1 + 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index de13022..b910b01 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -18,6 +18,7 @@ namespace pcpo { NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { for (llvm::Argument const& arg: f.args()) { values[&arg] = LinearEquality(&arg); + validVariables.insert(&arg); } isBottom = f.arg_empty(); } @@ -34,6 +35,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; } + validVariables.insert(&arg); } } isBottom = false; @@ -42,6 +44,9 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { this->values = equalaties; isBottom = equalaties.empty(); + for (auto& [key, value]: equalaties) { + validVariables.insert(key); + } } @@ -88,6 +93,7 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B if (callee_state.values.find(ret_val) != callee_state.values.end()) { dbgs(4) << "\t\tReturn evaluated, merging parameters\n"; values[&inst] = callee_state.values.at(ret_val); + validVariables.insert(&inst); } else { dbgs(4) << "\t\tReturn not evaluated, setting to bottom\n"; } @@ -105,6 +111,7 @@ void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { values[&inst] = values.at(ret_val); } } + validVariables.insert(&inst); } void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { @@ -143,12 +150,11 @@ void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { } bool NormalizedConjunction::merge(Merge_op::Type op, NormalizedConjunction const& other) { - bool changed = false; - if (other.isBottom) { return false; } else if (isBottom) { values = other.values; + validVariables = other.validVariables; isBottom = false; return true; } @@ -182,14 +188,22 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { // extend E1 by trivial equalities for (auto d: dX1) { - LinearEquality eq = {d, 1, d, 0}; - E1.insert(eq); + if (validVariables.count(d) > 0) { + LinearEquality eq = {d, 1, d, 0}; + E1.insert(eq); + } else { + E1.insert(rhs.values[d]); + } } // extend E2 by trivial equalities for (auto d: dX2) { - LinearEquality eq = {d, 1, d, 0}; - E2.insert(eq); + if (rhs.validVariables.count(d) > 0) { + LinearEquality eq = {d, 1, d, 0}; + E2.insert(eq); + } else { + E2.insert(values[d]); + } } std::set X0 = computeX0(E1, E2); @@ -215,6 +229,7 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { bool changed = values != result; values = result; + validVariables.insert(rhs.validVariables.begin(), rhs.validVariables.end()); return changed; } @@ -390,6 +405,7 @@ std::set NormalizedConjunction::computeX4(std::set values; + std::set validVariables; bool isBottom = true; NormalizedConjunction() = default; -- GitLab From 8c0cdc93d31f58625899cc509f7e1fb23927f1c5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 00:55:56 +0100 Subject: [PATCH 079/142] fixed applyPhi --- src/normalized_conjunction.cpp | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index b910b01..06ee6e8 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -64,14 +64,18 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector auto& incoming_state = pred_values[i]; if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { - linearAssignment(&phi, 1, nullptr, c->getSExtValue()); - } else { - if (incoming_state.values.count(&incoming_value) != 0) { - LinearEquality pred_value = incoming_state.values[&incoming_value]; - linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); - } else { - nonDeterminsticAssignment(&phi); - } + NormalizedConjunction acc = *this; + acc.linearAssignment(&phi, 1, nullptr, c->getSExtValue()); + merge(Merge_op::UPPER_BOUND, acc); + } else if (incoming_state.values.count(&incoming_value) != 0) { + NormalizedConjunction acc = *this; + LinearEquality pred_value = incoming_state.values[&incoming_value]; + acc.linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); + merge(Merge_op::UPPER_BOUND, acc); +// } else { +// NormalizedConjunction acc = *this; +// acc.nonDeterminsticAssignment(&phi); +// merge(Merge_op::UPPER_BOUND, acc); } i++; } -- GitLab From ce9d684fe9196918997f5c0eeca4555e5cf4007e Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 01:27:05 +0100 Subject: [PATCH 080/142] fixed apply callInst and applyRetInst --- src/normalized_conjunction.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 06ee6e8..dc207aa 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -96,7 +96,8 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B dbgs(4) << "\t\tFound return instruction\n"; if (callee_state.values.find(ret_val) != callee_state.values.end()) { dbgs(4) << "\t\tReturn evaluated, merging parameters\n"; - values[&inst] = callee_state.values.at(ret_val); + LinearEquality retEq = callee_state.values.at(ret_val); + values[&inst] = {&inst, retEq.a, retEq.x, retEq.b}; validVariables.insert(&inst); } else { dbgs(4) << "\t\tReturn not evaluated, setting to bottom\n"; @@ -112,7 +113,8 @@ void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { if (llvm::ConstantInt const* c = llvm::dyn_cast(ret_val)) { values[&inst] = LinearEquality(c); } else if (values.find(ret_val) != values.end()) { - values[&inst] = values.at(ret_val); + LinearEquality eq = values.at(ret_val); + values[&inst] = {&inst, eq.a, eq.x, eq.b}; } } validVariables.insert(&inst); -- GitLab From b4dd24e438ba40aa31c2dd0575029e238a290d11 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 13:45:59 +0100 Subject: [PATCH 081/142] added get --- src/normalized_conjunction.cpp | 68 ++++++++++++++++++---------------- src/normalized_conjunction.h | 1 + 2 files changed, 37 insertions(+), 32 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index dc207aa..595e60c 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -17,7 +17,7 @@ namespace pcpo { NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { for (llvm::Argument const& arg: f.args()) { - values[&arg] = LinearEquality(&arg); + get(&arg) = LinearEquality(&arg); validVariables.insert(&arg); } isBottom = f.arg_empty(); @@ -29,11 +29,11 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, llvm::Value* value = call->getArgOperand(arg.getArgNo()); if (value->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { - values[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; + get(&arg) = { &arg, 1 , nullptr, c->getSExtValue() }; } else { LinearEquality value_equality = state.values.at(value); LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; - values[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; + get(&arg) = { &arg, value_equality.a , value_equality.x, value_equality.b }; } validVariables.insert(&arg); } @@ -69,7 +69,7 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector merge(Merge_op::UPPER_BOUND, acc); } else if (incoming_state.values.count(&incoming_value) != 0) { NormalizedConjunction acc = *this; - LinearEquality pred_value = incoming_state.values[&incoming_value]; + LinearEquality pred_value = incoming_state[&incoming_value]; acc.linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); merge(Merge_op::UPPER_BOUND, acc); // } else { @@ -97,7 +97,7 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B if (callee_state.values.find(ret_val) != callee_state.values.end()) { dbgs(4) << "\t\tReturn evaluated, merging parameters\n"; LinearEquality retEq = callee_state.values.at(ret_val); - values[&inst] = {&inst, retEq.a, retEq.x, retEq.b}; + get(&inst) = {&inst, retEq.a, retEq.x, retEq.b}; validVariables.insert(&inst); } else { dbgs(4) << "\t\tReturn not evaluated, setting to bottom\n"; @@ -111,10 +111,10 @@ void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); if (ret_val && ret_val->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(ret_val)) { - values[&inst] = LinearEquality(c); + get(&inst) = LinearEquality(c); } else if (values.find(ret_val) != values.end()) { LinearEquality eq = values.at(ret_val); - values[&inst] = {&inst, eq.a, eq.x, eq.b}; + get(&inst) = {&inst, eq.a, eq.x, eq.b}; } } validVariables.insert(&inst); @@ -198,7 +198,7 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { LinearEquality eq = {d, 1, d, 0}; E1.insert(eq); } else { - E1.insert(rhs.values[d]); + E1.insert(rhs[d]); } } @@ -208,7 +208,7 @@ bool NormalizedConjunction::leastUpperBound(NormalizedConjunction rhs) { LinearEquality eq = {d, 1, d, 0}; E2.insert(eq); } else { - E2.insert(values[d]); + E2.insert(operator[](d)); } } @@ -410,11 +410,11 @@ std::set NormalizedConjunction::computeX4(std::set p){ return p.second.x == xi && p.second.y != xi ;}; @@ -425,11 +425,11 @@ void NormalizedConjunction::nonDeterminsticAssignment(Value const* xi) { it != values.end(); it = std::find_if(++it, values.end(), predicate)) { auto& xl = it->second; - values[xl.y] = {xl.y, 1, xk.y, xl.b - xk.b}; + get(xl.y) = {xl.y, 1, xk.y, xl.b - xk.b}; } - values[xk.y] = {xk.y, 1, xk.y, 0}; + get(xk.y) = {xk.y, 1, xk.y, 0}; } - values[xi] = {xi, 1, xi, 0}; + get(xi) = {xi, 1, xi, 0}; } } @@ -442,9 +442,9 @@ void NormalizedConjunction::linearAssignment(Value const* xi, int64_t a, Value c validVariables.insert(xi); // make sure xj exists - auto xjS = values.find(xj) != values.end() ? values[xj].x : nullptr; - auto bS = values.find(xj) != values.end() ? values[xj].b : 0; - auto aS = values.find(xj) != values.end() ? values[xj].a : 1; + auto xjS = values.find(xj) != values.end() ? get(xj).x : nullptr; + auto bS = values.find(xj) != values.end() ? get(xj).b : 0; + auto aS = values.find(xj) != values.end() ? get(xj).a : 1; if (!(a % aS == 0 && (-bS - b) % aS == 0)) { // Precison loss due to int division! Abort @@ -452,14 +452,14 @@ void NormalizedConjunction::linearAssignment(Value const* xi, int64_t a, Value c } if (xi > xjS) { - values[xi] = {xi, aS * a, xjS, a * bS + b}; + get(xi) = {xi, aS * a, xjS, a * bS + b}; return; } else { auto pred = [&xjS](std::pair p){ return p.second.x == xjS && p.second.y != xjS; }; for (auto xk: make_filter_range(values, pred)) { - values[xk.second.y] = {xk.second.y, xk.second.a * a/aS, xi, (-bS - b) / aS + xk.second.b}; + get(xk.second.y) = {xk.second.y, xk.second.a * a/aS, xi, (-bS - b) / aS + xk.second.b}; } - values[xjS] = {xjS, a/aS, xi, (-bS - b) / aS}; + get(xjS) = {xjS, a/aS, xi, (-bS - b) / aS}; } } @@ -488,11 +488,11 @@ void NormalizedConjunction::Add(Instruction const& inst) { // [xi := xj + xk] } else if (isa(op1) && isa(op2)) { // [xi := bj + xk] - if (values[op1].isConstant()) { - return linearAssignment(&inst, 1, op2, values[op1].b); + if (get(op1).isConstant()) { + return linearAssignment(&inst, 1, op2, get(op1).b); // [xi := xj + bk] - } else if (values[op2].isConstant()) { - return linearAssignment(&inst, 1, op1, values[op2].b); + } else if (get(op2).isConstant()) { + return linearAssignment(&inst, 1, op1, get(op2).b); // [xi := xj + xk] } else { return nonDeterminsticAssignment(&inst); @@ -525,11 +525,11 @@ void NormalizedConjunction::Sub(Instruction const& inst) { // [xi := xj - xk] } else if (isa(op1) && isa(op2)) { // [xi := bj - xk] - if (values[op1].isConstant()) { - return linearAssignment(&inst, 1, op2, -values[op1].b); + if (get(op1).isConstant()) { + return linearAssignment(&inst, 1, op2, -get(op1).b); // [xi := xj - bk] - } else if (values[op2].isConstant()) { - return linearAssignment(&inst, 1, op1, -values[op2].b); + } else if (get(op2).isConstant()) { + return linearAssignment(&inst, 1, op1, -get(op2).b); // [xi := xj - xk] } else { return nonDeterminsticAssignment(&inst); @@ -561,11 +561,11 @@ void NormalizedConjunction::Mul(Instruction const& inst) { // [xi := xj * xk] } else if (isa(op1) && isa(op2)) { // [xi := aj * xk] - if (values[op1].isConstant()) { - return linearAssignment(&inst, values[op1].b, op2, 0); + if (get(op1).isConstant()) { + return linearAssignment(&inst, get(op1).b, op2, 0); // [xi := xj * ak] - } else if (values[op2].isConstant()) { - return linearAssignment(&inst, values[op1].b, op1, 0); + } else if (get(op2).isConstant()) { + return linearAssignment(&inst, get(op1).b, op1, 0); // [xi := xj * xk] } else { return nonDeterminsticAssignment(&inst); @@ -579,6 +579,10 @@ void NormalizedConjunction::Mul(Instruction const& inst) { // MARK: - Operators LinearEquality& NormalizedConjunction::operator[](Value const* value) { + return get(value); +} + +LinearEquality& NormalizedConjunction::get(Value const* value) { if (values.count(value) == 0) { LinearEquality eq = {value, 1, value, 0}; values[value] = eq; diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 0c19022..509125e 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -55,6 +55,7 @@ public: // Operators LinearEquality& operator[](llvm::Value const*); + LinearEquality& get(llvm::Value const*); protected: // Abstract Operators -- GitLab From e813fd4d63f17001009b6847cfad73707bd3ae29 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 14:40:26 +0100 Subject: [PATCH 082/142] collecting basic blocks of callee func --- src/fixpoint.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index a75c07a..f68d402 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -256,6 +256,16 @@ void executeFixpointAlgorithm(Module const& M) { } else { //update callee NormalizedConjunction before = nodes[callee_element].state; + + // Collect all basic blocks of callee_func + for (po_iterator I = po_begin(&callee_func->getEntryBlock()), + IE = po_end(&callee_func->getEntryBlock()); + I != IE; ++I) { + BasicBlock const* basic_block = *I; + NodeKey key = {new_callstring, basic_block}; + callee_basic_blocks.push_back(&nodes[key]); + } + for (llvm::Argument const& arg: callee_func->args()) { llvm::Value* value = call->getArgOperand(arg.getArgNo()); if (value->getType()->isIntegerTy()) { @@ -269,6 +279,7 @@ void executeFixpointAlgorithm(Module const& M) { } } nodes[callee_element].state.isBottom = false; + //FIXME: somethingn is wrong here! This doesnt allways change ! check changed!!! changed = before.values != nodes[callee_element].state.values; } -- GitLab From ad88b41b77022721a4c36ba4a81b42f662712d25 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 19:03:57 +0100 Subject: [PATCH 083/142] fixed linear assignment --- src/normalized_conjunction.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 595e60c..0cb5b79 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -442,7 +442,7 @@ void NormalizedConjunction::linearAssignment(Value const* xi, int64_t a, Value c validVariables.insert(xi); // make sure xj exists - auto xjS = values.find(xj) != values.end() ? get(xj).x : nullptr; + auto xjS = values.find(xj) != values.end() ? get(xj).x : xj; auto bS = values.find(xj) != values.end() ? get(xj).b : 0; auto aS = values.find(xj) != values.end() ? get(xj).a : 1; -- GitLab From dd04bff6c3668987480fbdcd5b8305d67a999471 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 19:04:51 +0100 Subject: [PATCH 084/142] fixed handling of call inst --- src/fixpoint.cpp | 17 ++--------------- src/normalized_conjunction.cpp | 4 +++- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index f68d402..81aacb4 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -266,21 +266,8 @@ void executeFixpointAlgorithm(Module const& M) { callee_basic_blocks.push_back(&nodes[key]); } - for (llvm::Argument const& arg: callee_func->args()) { - llvm::Value* value = call->getArgOperand(arg.getArgNo()); - if (value->getType()->isIntegerTy()) { - if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { - nodes[callee_element].state[&arg] = { &arg, 1 , nullptr, c->getSExtValue() }; - } else { - LinearEquality value_equality = state_new[value]; - LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; - nodes[callee_element].state[&arg] = { &arg, value_equality.a , value_equality.x, value_equality.b }; - } - } - } - nodes[callee_element].state.isBottom = false; - //FIXME: somethingn is wrong here! This doesnt allways change ! check changed!!! - changed = before.values != nodes[callee_element].state.values; + AbstractState state_update{ callee_func, state_new, call }; + changed = nodes[callee_element].state.merge(merge_op, state_update); } //Getting the last block diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 0cb5b79..0da9f09 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -30,10 +30,12 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, if (value->getType()->isIntegerTy()) { if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { get(&arg) = { &arg, 1 , nullptr, c->getSExtValue() }; - } else { + } else if (state.values.count(value) > 0) { LinearEquality value_equality = state.values.at(value); LinearEquality eq = { &arg, value_equality.a , value_equality.x, value_equality.b }; get(&arg) = { &arg, value_equality.a , value_equality.x, value_equality.b }; + } else { + get(&arg) = { &arg, 1 , value, 0 }; } validVariables.insert(&arg); } -- GitLab From 077f4af31af5748d1d5bc73a793317e93ee82aff Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 19:05:23 +0100 Subject: [PATCH 085/142] fixed phi node --- src/normalized_conjunction.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 0da9f09..7c08550 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -74,10 +74,10 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector LinearEquality pred_value = incoming_state[&incoming_value]; acc.linearAssignment(&phi, pred_value.a, pred_value.x, pred_value.b); merge(Merge_op::UPPER_BOUND, acc); -// } else { -// NormalizedConjunction acc = *this; -// acc.nonDeterminsticAssignment(&phi); -// merge(Merge_op::UPPER_BOUND, acc); + } else { + NormalizedConjunction acc = *this; + acc.nonDeterminsticAssignment(&phi); + merge(Merge_op::UPPER_BOUND, acc); } i++; } -- GitLab From b6291fcecb78103b0a96c7165935e3970b816d84 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 19:07:48 +0100 Subject: [PATCH 086/142] Revert "debug changes **revert this**" This reverts commit 275e95d215225be5afff14e046e7366486474e94. # Conflicts: # src/fixpoint.cpp --- src/fixpoint.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 81aacb4..a204fb2 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -105,9 +105,9 @@ vector*> register_function(llvm::Function const* function, C dbgs(1) << " Found basic block: " << basic_block->getName() << '\n'; NodeKey key = {new_callstring, basic_block}; Node node = {basic_block, new_callstring}; -// if (node.isEntry()) { -// node.state = AbstractState {*node.function()}; -// } + if (node.isEntry()) { + node.state = AbstractState {*node.function()}; + } inserted_nodes.push_back(&nodes[key]); nodes[key] = node; } @@ -226,7 +226,7 @@ void executeFixpointAlgorithm(Module const& M) { // Checks if an input parameter for the callee is bottom. If so, // then skip the calculation of the call instruction for now -// if (state_new.checkOperandsForBottom(inst)) continue; + if (state_new.checkOperandsForBottom(inst)) continue; Function const* callee_func = call->getCalledFunction(); @@ -300,7 +300,7 @@ void executeFixpointAlgorithm(Module const& M) { } } } else { -// if (state_new.checkOperandsForBottom(inst)) continue; + if (state_new.checkOperandsForBottom(inst)) continue; state_new.applyDefault(inst); } } @@ -348,10 +348,10 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; // Use either the standard fixpoint algorithm or the version with widening -// executeFixpointAlgorithm(M); + executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); - executeFixpointAlgorithm(M); +// executeFixpointAlgorithmTwoVarEq(M); // We never change anything return false; -- GitLab From 245d23fb59b8116cf232778426a9bf428619c659 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 19:08:44 +0100 Subject: [PATCH 087/142] fixed type name --- src/fixpoint.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index a204fb2..197b963 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -255,7 +255,7 @@ void executeFixpointAlgorithm(Module const& M) { changed = true; } else { //update callee - NormalizedConjunction before = nodes[callee_element].state; + AbstractState before = nodes[callee_element].state; // Collect all basic blocks of callee_func for (po_iterator I = po_begin(&callee_func->getEntryBlock()), -- GitLab From 1245b96ae72fd984578118d3ee653ea69f0f0836 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 22:01:59 +0100 Subject: [PATCH 088/142] added bool checkOperandsForBottom() --- src/fixpoint.cpp | 7 +++---- src/normalized_conjunction.h | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 197b963..92edd72 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -347,12 +347,11 @@ void executeFixpointAlgorithm(Module const& M) { bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { using AbstractState = AbstractStateValueSet; - // Use either the standard fixpoint algorithm or the version with widening - executeFixpointAlgorithm(M); +// Use either the standard fixpoint algorithm or the version with widening +// executeFixpointAlgorithm(M); + executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); -// executeFixpointAlgorithmTwoVarEq(M); - // We never change anything return false; } diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index 509125e..a839fb8 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -45,6 +45,8 @@ public: bool merge(Merge_op::Type op, NormalizedConjunction const& other); void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) { return; }; bool leastUpperBound(NormalizedConjunction rhs); + + bool checkOperandsForBottom(llvm::Instruction const& inst) { return false; } void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; -- GitLab From fa3d3b1f03cc0cdcd81d437c333d38de5e8272f3 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 6 Mar 2020 23:59:35 +0100 Subject: [PATCH 089/142] Added ValueFormatter for llvm::Value --- .lldbinit | 2 -- .lldbinit.in | 3 +++ CMakeLists.txt | 2 ++ lldb/ValueFormatter.py | 16 ++++++++++++++++ 4 files changed, 21 insertions(+), 2 deletions(-) delete mode 100644 .lldbinit create mode 100644 .lldbinit.in create mode 100644 lldb/ValueFormatter.py diff --git a/.lldbinit b/.lldbinit deleted file mode 100644 index 68ce1e0..0000000 --- a/.lldbinit +++ /dev/null @@ -1,2 +0,0 @@ -command regex dump 's/(.+)/expression -O -l c++ -- ((llvm::Value*) %1 )->dump()/' -command alias d dump \ No newline at end of file diff --git a/.lldbinit.in b/.lldbinit.in new file mode 100644 index 0000000..fd3bec7 --- /dev/null +++ b/.lldbinit.in @@ -0,0 +1,3 @@ +command regex dump 's/(.+)/expression -O -l c++ -- ((llvm::Value*) %1 )->dump()/' +command alias d dump +command script import ${CMAKE_SOURCE_DIR}/lldb/ValueFormatter.py \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index e82528f..6f6458e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -254,3 +254,5 @@ foreach(src ${SAMPLES}) endforeach(src) endif() + +configure_file(.lldbinit.in .lldbinit) \ No newline at end of file diff --git a/lldb/ValueFormatter.py b/lldb/ValueFormatter.py new file mode 100644 index 0000000..28900c5 --- /dev/null +++ b/lldb/ValueFormatter.py @@ -0,0 +1,16 @@ +import lldb + +def ValueFormatter(valobj, internal_dict): + frame = lldb.debugger.GetSelectedTarget().GetProcess().GetSelectedThread().GetSelectedFrame() + options = lldb.SBExpressionOptions() + options.SetLanguage(lldb.eLanguageTypeObjC_plus_plus) + options.SetTrapExceptions(False) + options.SetTimeoutInMicroSeconds(5000000) # 5s + + addr = valobj.GetValue() + expr = frame.EvaluateExpression('((llvm::Value*) {0})->getName().data()'.format(addr), options) + name = expr.GetSummary() + return '{0}'.format(name) + +def __lldb_init_module(debugger, internal_dict): + debugger.HandleCommand("type summary add -F " + __name__ + ".ValueFormatter llvm::Value") -- GitLab From a3fa370de0db1f6c64ce4349eb1b510e7f43bbe1 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 8 Mar 2020 13:25:43 +0100 Subject: [PATCH 090/142] removed fixpoint_two_var_eq --- CMakeLists.txt | 1 - src/fixpoint_two_var_eq.cpp | 272 ------------------------------------ src/fixpoint_two_var_eq.h | 12 -- 3 files changed, 285 deletions(-) delete mode 100644 src/fixpoint_two_var_eq.cpp delete mode 100644 src/fixpoint_two_var_eq.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 6f6458e..14932f3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -101,7 +101,6 @@ endif() set(PAIN_SOURCES src/fixpoint.cpp src/fixpoint_widening.cpp - src/fixpoint_two_var_eq.cpp src/value_set.cpp src/simple_interval.cpp src/normalized_conjunction.cpp diff --git a/src/fixpoint_two_var_eq.cpp b/src/fixpoint_two_var_eq.cpp deleted file mode 100644 index db03983..0000000 --- a/src/fixpoint_two_var_eq.cpp +++ /dev/null @@ -1,272 +0,0 @@ -//#include -//#include -// -//#include "llvm/IR/CFG.h" -//#include "llvm/IR/Module.h" -//#include "llvm/IR/Dominators.h" -//#include "llvm/Analysis/LoopInfo.h" -// -//#include "global.h" -//#include "general.h" -//#include "value_set.h" -// -//namespace pcpo { -// -//using namespace llvm; -//using namespace std; -// -// -//// A node in the control flow graph, i.e. a basic block. Here, we need a bit of additional data -//// per node to execute the fixpoint algorithm. -//template -//struct Node { -// BasicBlock const* bb; -// BasicBlock const* callstring; -// AbstractState state = {}; -// bool update_scheduled = false; // Whether the node is already in the worklist -// -// // If this is set, the algorithm will add the initial values from the parameters of the -// // function to the incoming values, which is the correct thing to do for initial basic -// // blocks. -// Function const* func_entry = nullptr; -//}; -// -//template -//static void register_basic_blocks(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes) { -// for (BasicBlock const& bb: *main_func) { -// dbgs(1) << " Found basic block main." << bb.getName() << '\n'; -// // nodes of main block have the callstring of a dummy block -// nodes[{&bb, dummy_block}] = { &bb, dummy_block }; -// } -//} -// -//template -//static void push_to_worklist(BasicBlock const* dummy_block, Function const* main_func, unordered_map> &nodes, vector &worklist) { -// auto init_element = make_tuple(&main_func->getEntryBlock(), dummy_block); -// worklist.push_back(init_element); -// nodes[init_element].update_scheduled = true; -// nodes[init_element].state = AbstractState {*main_func}; -// nodes[init_element].func_entry = main_func; -//} -// -//template -//static vector collect_predecessors(Node &node, unordered_map> &nodes, AbstractState &state_new) { -// vector predecessors; -// for (BasicBlock const* bb: llvm::predecessors(node.bb)) { -// dbgs(3) << " Merging basic block " << _bb_to_str(bb) << '\n'; -// -// AbstractState state_branched {nodes[make_tuple(bb, node.callstring)].state}; -// state_branched.branch(*bb, *node.bb); -// state_new.merge(Merge_op::UPPER_BOUND, state_branched); -// predecessors.push_back(state_branched); -// } -// return predecessors; -//} -// -//template -//static void apply_instructions(Node &node, unordered_map> &nodes, vector &predecessors, AbstractState state_new, vector &worklist) { -// using Node = Node; -// -// for (Instruction const& inst: *node.bb) { -// -// // Handles return instructions -// if (isa(inst)) { -// state_new.applyReturnInst(inst); -// } -// -// // If the result of the instruction is not used, there is no reason to compute -// // it. (There are no side-effects in LLVM IR. (I hope.)) -// // Except for call instructions, we still want to get that information -// if (inst.use_empty() && not isa(inst)) { -// dbgs(3) << " Empty use of instruction, skipping...\n"; -// } else if (PHINode const* phi = dyn_cast(&inst)) { -// // Handles merging points -// state_new.applyPHINode(*node.bb, predecessors, phi); -// -// // Handles function calls -// } else if (CallInst const* call = dyn_cast(&inst)) { -// // Checks if an input parameter for the callee is bottom. If so, -// // then skip the calculation of the call instruction for now -// -// Function const* callee_func = call->getCalledFunction(); -// -// // Checks for functions, such as printf and skips them -// if (callee_func->empty()) { -// dbgs(3) << " Function " << callee_func->getName() << " is external, skipping...\n"; -// continue; -// } -// -// auto callee_element = make_tuple(&callee_func->getEntryBlock(), node.bb); -// bool changed = false; -// -// // Checks whether a node with key [%callee entry block, %caller basic block], -// // i.e. an entry block with callstring of caller basic block, exists. -// // If not, all nodes with their corrosponding keys are initilized for the callee function. -// if (nodes.find(callee_element) == nodes.end()) { -// // Check if abstract_state of call.bb is bottom or not -// dbgs(3) << " No information regarding function call %" << call->getCalledFunction()->getName() << "\n"; -// -// // Register basic blocks -// for (BasicBlock const& bb : *callee_func) { -// dbgs(4) << " Found basic block " << _bb_to_str(&bb) << '\n'; -// -// Node callee_node = {&bb, node.bb}; -// nodes[make_tuple(&bb, node.bb)] = callee_node; -// } -// -// nodes[callee_element].state = AbstractState{ callee_func, state_new, call }; -// nodes[callee_element].func_entry = callee_func; -// changed = true; -// } else { -// AbstractState state_update{ callee_func, state_new, call }; -// changed = nodes[callee_element].state.merge(Merge_op::UPPER_BOUND, state_update); -// } -// -// //Getting the last block -// BasicBlock const* end_block = &*prev(callee_func->end()); -// auto end_element = make_tuple(end_block, node.bb); -// -// state_new.applyCallInst(inst, end_block, nodes[end_element].state); -// -// // If input parameters have changed, we want to interpret the function once again -// // and reevaluate the nodes of possible callers. -// if (changed) { -// for (pair& i : nodes) { -// if (get<0>(i.first) == node.bb and not i.second.update_scheduled) { -// dbgs(3) << " Adding possible caller " << _bb_key_to_str(i.first) << " to worklist\n"; -// worklist.push_back(i.first); -// i.second.update_scheduled = true; -// } -// } -// -// // Checks if the key of the callee functions entry node is already on the worklist, -// // this is necessary for recursions. -// if (not nodes[callee_element].update_scheduled) { -// worklist.push_back(callee_element); -// nodes[callee_element].update_scheduled = true; -// -// dbgs(3) << " Adding callee " << _bb_key_to_str(callee_element) << " to worklist\n"; -// } else { -// dbgs(3) << " Callee already on worklist, nothing to add...\n"; -// } -// } -// } else { -// state_new.applyDefault(inst); -// } -// } -//} -// -//template -//static void update_successors(Node &node, unordered_map> &nodes, vector &worklist) { -// for (BasicBlock const* succ_bb: successors(node.bb)) { -// auto succ_key = make_tuple(succ_bb, node.callstring); -// Node& succ = nodes[succ_key]; -// if (not succ.update_scheduled) { -// worklist.push_back(succ_key); -// succ.update_scheduled = true; -// dbgs(3) << " Adding " << _bb_key_to_str(succ_key) << " to worklist\n"; -// } -// } -//} -// -//template -//static void output_final_result(unordered_map> const& nodes) { -// dbgs(0) << "\nFinal result:\n"; -// for (pair> i: nodes) { -// dbgs(0) << _bb_key_to_str(i.first) << ":\n"; -// i.second.state.printOutgoing(*i.second.bb, dbgs(0), 2); -// } -//} -// -///// Run the simple fixpoint algorithm with callstrings. AbstractState should implement the interface -///// documented in AbstractStateDummy (no need to subclass or any of that, just implement the methods -///// with the right signatures and take care to fulfil the contracts outlines above). -///// Note that a lot of this code is duplicated in executeFixpointAlgorithmWidening in -///// fixpoint_widening.cpp, so if you fix any bugs in here, they probably should be fixed there as -///// well. -///// Tip: Look at a diff of fixpoint.cpp and fixpoint_widening.cpp with a visual diff tool (I -///// recommend Meld.) -//template -//void executeFixpointAlgorithmTwoVarEq(Module const& M) { -// using Node = Node; -// -// constexpr int iterations_max = 1000; -// -// unordered_map nodes; -// vector worklist; // Contains the tuples of basic block and callstring, which is the key of a node, that need to be processed -// -// // We only consider the main function in the beginning. If no main exists, nothing is evaluated! -// Function const* main_func = M.getFunction("main"); -// -// //creating dummy block for callstrings of the main block, since the main function is not called from another function -// BasicBlock const* dummy_block = BasicBlock::Create(M.getContext(), "dummy"); -// -// // TODO: Check what this does for release clang, probably write out a warning -// dbgs(1) << "Initialising fixpoint algorithm, collecting basic blocks\n"; -// -// // Register basic blocks of the main function -// register_basic_blocks(dummy_block, main_func, nodes); -// -// // Push the initial block into the worklist -// push_to_worklist(dummy_block, main_func, nodes, worklist); -// -// dbgs(1) << "\nWorklist initialised with " << worklist.size() << (worklist.size() != 1 ? " entries" : " entry") -// << ". Starting fixpoint iteration...\n"; -// -// for (int iter = 0; !worklist.empty() and iter < iterations_max; ++iter) { -// Node& node = nodes[worklist.back()]; -// worklist.pop_back(); -// node.update_scheduled = false; -// -// dbgs(1) << "\nIteration " << iter << ", considering basic block " -// << _bb_to_str(node.bb) << " with callstring " -// << _bb_to_str(node.callstring) << '\n'; -// -// AbstractState state_new; // Set to bottom -// -// if (node.func_entry) { -// dbgs(1) << " Merging function parameters, is entry block\n"; -// // if it is the entry node, then its state should be top -// state_new.merge(Merge_op::UPPER_BOUND, node.state); -// } -// -// dbgs(1) << " Merge of " << pred_size(node.bb) -// << (pred_size(node.bb) != 1 ? " predecessors.\n" : " predecessor.\n"); -// -// // Collect the predecessors -// vector predecessors = collect_predecessors(node, nodes, state_new); -// -// dbgs(2) << " Relevant incoming state is:\n"; state_new.printIncoming(*node.bb, dbgs(2), 4); -// -// // Apply the basic block -// dbgs(3) << " Applying basic block\n"; -// -// // Applies all instrucions of a basic block -// apply_instructions(node, nodes, predecessors, state_new, worklist); -// -// // Merge the state back into the node -// dbgs(3) << " Merging with stored state\n"; -// bool changed = node.state.merge(Merge_op::UPPER_BOUND, state_new); -// -// dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.bb, dbgs(2), 4); -// -// // No changes, so no need to do anything else -// if (not changed) continue; -// -// dbgs(2) << " State changed, notifying " << succ_size(node.bb) -// << (succ_size(node.bb) != 1 ? " successors\n" : " successor\n"); -// -// // Something changed and we will need to update the successors -// update_successors(node, nodes, worklist); -// } -// -// if (!worklist.empty()) { -// dbgs(0) << "Iteration terminated due to exceeding loop count.\n"; -// } -// -// // Output the final result -// output_final_result(nodes); -//} -// -//} /* end of namespace pcpo */ -// diff --git a/src/fixpoint_two_var_eq.h b/src/fixpoint_two_var_eq.h deleted file mode 100644 index d6d6aff..0000000 --- a/src/fixpoint_two_var_eq.h +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -#include "llvm/Pass.h" -#include "fixpoint.h" -#include "normalized_conjunction.h" - -namespace pcpo { - -template -void executeFixpointAlgorithmTwoVarEq(llvm::Module const& M); - -} /* end of namespace pcpo */ -- GitLab From ba28d59d73e10074937f9b6ce27f035984e1acb6 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 8 Mar 2020 13:58:35 +0100 Subject: [PATCH 091/142] replaced list(TRANSFORM ... APPEND ".c" ...) with custom macro for compatibility with older cmake versions --- CMakeLists.txt | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 14932f3..cfcf823 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -215,12 +215,27 @@ set(SAMPLES while-neg ) -list(TRANSFORM SAMPLES APPEND ".c" OUTPUT_VARIABLE SAMPLES_C) -list(TRANSFORM SAMPLES APPEND ".bc" OUTPUT_VARIABLE SAMPLES_BC) -list(TRANSFORM SAMPLES APPEND ".ll" OUTPUT_VARIABLE SAMPLES_LL) -list(TRANSFORM SAMPLES_LL PREPEND "${CMAKE_SOURCE_DIR}/output/") -list(TRANSFORM SAMPLES_BC PREPEND "${CMAKE_SOURCE_DIR}/output/") -list(TRANSFORM SAMPLES_C PREPEND "${CMAKE_SOURCE_DIR}/samples/") +# Older CMake version do not support list transformations +macro(list_transform_prepend in prefix) + foreach(f ${${in}}) + list(APPEND temp "${prefix}${f}") + endforeach() + set(${in} "${temp}") + unset(temp) +endmacro() + +macro(list_transform_append in suffix out) + foreach(f ${${in}}) + list(APPEND ${out} "${f}${suffix}") + endforeach() +endmacro() + +list_transform_append(SAMPLES ".c" SAMPLES_C) +list_transform_append(SAMPLES ".bc" SAMPLES_BC) +list_transform_append(SAMPLES ".ll" SAMPLES_LL) +list_transform_prepend(SAMPLES_LL "${CMAKE_SOURCE_DIR}/output/") +list_transform_prepend(SAMPLES_BC "${CMAKE_SOURCE_DIR}/output/") +list_transform_prepend(SAMOLES_C "${CMAKE_SOURCE_DIR}/samples/") add_custom_target(irgen DEPENDS ${SAMPLES_LL} ${SAMPLES_BC} -- GitLab From b00263feb68984d92b175f84867ce749fcd8c072 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 8 Mar 2020 13:59:28 +0100 Subject: [PATCH 092/142] removed unused include --- src/fixpoint.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 92edd72..9ae3aa1 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -13,7 +13,6 @@ #include "normalized_conjunction.h" #include "fixpoint_widening.cpp" -#include "fixpoint_two_var_eq.cpp" #include "hash_utils.h" #include "llvm/ADT/PostOrderIterator.h" -- GitLab From 9b53b2cd76cff16f66dfa929eee6f75dc8bc685c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 10 Mar 2020 03:49:43 +0100 Subject: [PATCH 093/142] added matrix.h --- CMakeLists.txt | 15 +++ src/matrix.h | 272 +++++++++++++++++++++++++++++++++++++++++++ test/matrix_test.cpp | 238 +++++++++++++++++++++++++++++++++++++ 3 files changed, 525 insertions(+) create mode 100644 src/matrix.h create mode 100644 test/matrix_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index cfcf823..934e7bc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,6 +117,7 @@ set(PAIN_HEADERS src/global.h src/abstract_state.h src/hash_utils.h + src/matrix.h ) include_directories(${LLVM_INCLUDE_DIRS}) @@ -157,6 +158,16 @@ target_link_libraries(normalized_conjunction_test ${LLVM_AVAILABLE_LIBS} ) +add_llvm_executable(matrix_test + test/matrix_test.cpp + ${PAIN_HEADERS} + ${PAIN_SOURCES} +) + +target_link_libraries(matrix_test + ${LLVM_AVAILABLE_LIBS} +) + enable_testing() add_test(NAME intervalAnalysisTest @@ -171,6 +182,10 @@ add_test(NAME normalizedConjunctionTest COMMAND normalized_conjunction_test ) +add_test(NAME matrixTest + COMMAND matrix_test +) + set(SAMPLES add-1-float add-1 diff --git a/src/matrix.h b/src/matrix.h new file mode 100644 index 0000000..c9cb329 --- /dev/null +++ b/src/matrix.h @@ -0,0 +1,272 @@ +#pragma once + +#include "global.h" + +#include + +using namespace std; + +namespace pcpo { + +/// Matrix. Row and column are indexed beginning at 0 +template class Matrix { +public: + //MARK: - Initializers + /// Creates a matrix with dimensions height x width and initalizes its values to `value` + /// @param width Width of the matrix + /// @param height Height of the matrix + /// @param value Initial value for each element + Matrix(int height, int width, T value) { + this->width = width; + this->height = height; + this->vectors.reserve(width); + for (int i = 0; i < height; i++) { + vector vector(width,value); + vectors.push_back(vector); + } + }; + + /// Creates a matrix with dimensions height x width and initalizes its values to 0 + /// @param width Width of the matrix + /// @param height Height of the matrix + Matrix(int height, int width) : Matrix(height, width, 0) {}; + + Matrix(Matrix const& matrix) = default; + + /// Creates an identity matrix with dimension eye x eye + /// @param eye dimension of the matrix + Matrix(int eye){ + this->width = eye; + this->height = eye; + this->vectors.reserve(width); + for (int i = 0; i < height; i++) { + vector vector(width,0); + vector[i] = 1; + vectors[i] = vector; + } + }; + + /// Creates a matrix from a 2D vector + /// @param vectors 2D vector containing columns with rows + Matrix(vector> const &vectors) { + this->height = vectors.size(); + this->width = vectors.front().size(); + this->vectors = vectors; + }; + + /// Creates a vector from a std::vector + /// @param vector the vector + Matrix(vector const &vector) { + std::vector> vectors = {vector}; + this->vectors = vectors; + }; + + // MARK: - Properties + + /// The height of the matrix (number of rows) + int getHeight() const { return height; }; + /// The width of the matrix (number of columns) + int getWidth() const { return width; }; + + // MARK: - Matrix operations + + /// Transposes the matrix + Matrix transpose() const { + Matrix result = Matrix(width, height); + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + result.vectors[j][i] = vectors[i][j]; + } + } + return result; + }; + + /// Transforms the matrix to reduced row echelon form + Matrix echelon() const { + Matrix result = Matrix(*this); + int pivot = 0; + for (int row = 0; row < height; row++) { + if (pivot >= width) { return result; } + int i = row; + while (result.vectors[i][pivot] == 0) { + if (++i >= height) { + i = row; + if (++pivot >= width) { return result; } + } + } + result.swap_rows(i, row); + result.divide_row(row, result.vectors[row][pivot]); + for (i = 0; i < height; i++) { + if (i != row) { + result.add_multiple_row(i, row, -result.vectors[i][pivot]); + } + } + } + return result; + }; + + + /// Linear span of the matrix ... fixme + Matrix span() const; + + /// Returns a vector with the elements of the row at index i. The returned row cannot be modified. + /// @param i Index of the row to return. + vector& row(int i) { return vectors[i]; }; + + /// Returns the column at index i. The returned column can be modified. + /// @param i Index of the column to return + vector column(int i) const { + vector row; + row.reserve(width); + for (vector const& x : vectors) { + row.push_back(x[i]); + } + return row; + } + + // MARK: - Operators + + T& operator()(int i, int j) { return vectors[i][j]; }; + + Matrix& operator *=(Matrix const& rhs) { + assert(width == rhs.height); + Matrix result = Matrix(height,rhs.width); + for (int i = 0; i < height; i++) { + for (int k = 0; k < width; k++) { + for (int j = 0; j < rhs.width; j++) { + result.vectors[i][j] += vectors[i][k] * rhs.vectors[k][j]; + } + } + } + this->vectors = result.vectors; + this->width = result.width; + this->height = result.height; + return *this; + }; + + Matrix& operator *=(T scalar) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + vectors[i][j] *= scalar; + } + } + return *this; + }; + + + Matrix& operator +=(Matrix const& rhs) { + assert(rhs.width == width && rhs.height == height); + for (int i=0;i& operator +=(T scalar) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + vectors[i][j] += scalar; + } + } + return *this; + }; + + Matrix& operator -=(Matrix const& rhs) { + assert(rhs.width == width && rhs.height == height); + for (int i = 0; i < height; i++) { + for (int j= 0; j < width; j++) { + vectors[i][j] -= rhs.vectors[i][j]; + } + } + return *this; + }; + + Matrix& operator -=(int scalar) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + vectors[i][j] -= scalar; + } + } + return *this; + }; + + bool operator==(Matrix const& rhs) const { + return rhs.vectors == rhs.vectors && width == rhs.width && height == rhs.height; + }; + +protected: + vector> vectors; + int width; + int height; + + // MARK: - Echelon helpers + + /// Swaps two rows + /// @param a index of the first row + /// @param b index of the second row + void swap_rows(int a, int b) { vectors[a].swap(vectors[b]); }; + + /// Divides a row by a constant + /// @param row index of the row to divide + /// @param quotient quotient to divide the row by + void divide_row(int row, int quotient) { + for (int column = 0; column < width; column++) { + vectors[row][column] /= quotient; + } + }; + + /// Adds a multiple of row b to a row a + /// @param a Row to add a multiple of b to + /// @param b Row to be added to row a + /// @param factor Factor to multiply row b with when adding it to row a + void add_multiple_row(int a, int b, int factor) { + for (int column = 0; column < width; column++) { + vectors[a][column] += vectors[b][column] * factor; + } + }; + + // MARK: - Utils + + /// Greates common divisor. + /// @param lhs + /// @param rhs + static int ggT(int lhs, int rhs) { + int h; + if (lhs == 0) { return abs(rhs); } + if (rhs == 0) { return abs(lhs); } + + do { + h = lhs % rhs; + lhs = rhs; + rhs = h; + } while (rhs != 0); + + return abs(lhs); + }; + + /// Least common multiple. + /// @param lhs + /// @param rhs + static int kgV(int lhs, int rhs) { return (lhs * rhs) / ggT(lhs, rhs); }; + + +}; + + +template +inline Matrix operator*(Matrix lhs, Matrix const& rhs) { return lhs *= rhs; }; +template +inline Matrix operator*(Matrix lhs, T scalar) { return lhs *= scalar; }; +template +inline Matrix operator+(Matrix lhs, Matrix const& rhs) { return lhs += rhs; }; +template +inline Matrix operator+(Matrix lhs, T scalar) { return lhs += scalar; }; +template +inline Matrix operator-(Matrix lhs, Matrix const& rhs) { return lhs -= rhs; }; +template +inline Matrix operator-(Matrix lhs, T scalar) { return lhs -= scalar; }; + +} + diff --git a/test/matrix_test.cpp b/test/matrix_test.cpp new file mode 100644 index 0000000..9842b89 --- /dev/null +++ b/test/matrix_test.cpp @@ -0,0 +1,238 @@ +#include +#include +#include +#include +#include + +#include "../src/matrix.h" + +using namespace std; +using namespace pcpo; + +template +class MatrixTest: Matrix { + +public: + static bool runTestMul1(); + static bool runTestMul2(); + static bool runTestTranspose1(); + static bool runTestTranspose2(); + static bool runTestEchelon1(); + static bool runTestEchelon2(); + static bool runTestEchelon3(); +}; + +template +bool MatrixTest::runTestMul1() { + std::cout << "Testing multiplication 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + std::vector> b = { + {4,29,0}, + {-1,27,2}, + {100,5,3} + }; + + std::vector> expected = { + {700,172,29}, + {803,233,34}, + {906,294,39} + }; + + auto actual = Matrix(a) * Matrix(b); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestMul2() { + std::cout << "Testing multiplication 2: "; + bool result = false; + + std::vector> a = { + {1,6,11}, + {2,7,12}, + {3,8,13}, + {4,9,14}, + {5,10,-9} + }; + + std::vector> b = { + {43,45,1,9}, + {224,7,-2,24}, + {12,1,13,-6} + }; + + std::vector> expected = { + {1519,87,132,87}, + {1798,139,144,114}, + {2077,191,156,141}, + {2356,243,168,168}, + {2347,295,-132,339} + }; + + auto actual = Matrix(a) * Matrix(b); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestTranspose1() { + std::cout << "Testing transpose 1: "; + bool result = false; + + std::vector> a = { + {1,2,3}, + {4,5,6}, + {7,8,9} + }; + + std::vector> expected = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + auto matrix = Matrix(a); + auto actual = matrix.transpose(); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestTranspose2() { + std::cout << "Testing transpose 2: "; + bool result = false; + + std::vector> a = { + {1,3}, + {2,4}, + {3,5} + }; + + std::vector> expected = { + {1,2,3}, + {4,5,6} + }; + + auto matrix = Matrix(a); + auto actual = matrix.transpose(); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestEchelon1() { + std::cout << "Testing echelon 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + std::vector> expected = { + {1,0,-1}, + {0,1,2}, + {0,0,0} + }; + + auto matrix = Matrix(a); + auto actual = matrix.echelon(); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestEchelon2() { + std::cout << "Testing echelon 2: "; + bool result = false; + + std::vector> a = { + {1,2,1}, + {1,4,8}, + {1,6,3} + }; + + std::vector> expected = { + {1,0,0}, + {0,1,0}, + {0,0,1} + }; + + auto matrix = Matrix(a); + auto actual = matrix.echelon(); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestEchelon3() { + std::cout << "Testing echelon 3: "; + bool result = false; + + std::vector> a = { + {1,2,4}, + {2,4,8}, + {4,8,16} + }; + + std::vector> expected = { + {1,2,4}, + {0,0,0}, + {0,0,0} + }; + + auto matrix = Matrix(a); + auto actual = matrix.echelon(); + + auto x = Matrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + + +int main() { + return !(MatrixTest::runTestMul1() + && MatrixTest::runTestMul2() + && MatrixTest::runTestTranspose1() + && MatrixTest::runTestTranspose2() + && MatrixTest::runTestEchelon1() + && MatrixTest::runTestEchelon2() + && MatrixTest::runTestEchelon3() + ); +}; + -- GitLab From 9e842c6de4698285d5967f0c5addc50f348bf9f2 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 10 Mar 2020 04:40:25 +0100 Subject: [PATCH 094/142] add getRank() and span() --- src/matrix.h | 29 +++++++++++-- test/matrix_test.cpp | 100 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 126 insertions(+), 3 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index c9cb329..99fa115 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -105,15 +105,38 @@ public: return result; }; + /// The rank of the matrix + int getRank() const { + Matrix e = echelon(); + int rank = 0; + for (int row = 0; row < height; row++) { + for (int column = 0; column < width; column++) { + if ((e.vectors[row][column] == 0) && (column == width - 1)) { + return rank; + } else if (e.vectors[row][column] != 0) { + break; + } + } + rank++; + } + return rank; + } /// Linear span of the matrix ... fixme - Matrix span() const; + Matrix span() const { + vector> columns; + int rank = getRank(); + for (int col = 0; col& row(int i) { return vectors[i]; }; - /// Returns the column at index i. The returned column can be modified. + /// Returns the column at index i. The returned column cannot be modified. /// @param i Index of the column to return vector column(int i) const { vector row; diff --git a/test/matrix_test.cpp b/test/matrix_test.cpp index 9842b89..0574fdb 100644 --- a/test/matrix_test.cpp +++ b/test/matrix_test.cpp @@ -20,6 +20,10 @@ public: static bool runTestEchelon1(); static bool runTestEchelon2(); static bool runTestEchelon3(); + static bool runTestRank1(); + static bool runTestRank2(); + static bool runTestRank3(); + static bool runTestSpan1(); }; template @@ -224,6 +228,98 @@ bool MatrixTest::runTestEchelon3() { return result; } +template +bool MatrixTest::runTestRank1() { + std::cout << "Testing rank 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + int expected = 2; + + auto matrix = Matrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestRank2() { + std::cout << "Testing rank 2: "; + bool result = false; + + std::vector> a = { + {1,2,4}, + {2,4,8}, + {4,8,16} + }; + + int expected = 1; + + auto matrix = Matrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestRank3() { + std::cout << "Testing rank 3: "; + bool result = false; + + std::vector> a = { + {1,2,1}, + {1,4,8}, + {1,6,3} + }; + + int expected = 3; + + auto matrix = Matrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestSpan1() { + std::cout << "Testing span 1: "; + bool result = false; + + std::vector> a = { + {1,1,4}, + {0,1,4}, + {1,0,0} + }; + + std::vector> expected = { + {1,1}, + {0,1}, + {1,0} + }; + + auto matrix = Matrix(a); + auto actual = matrix.span(); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + int main() { return !(MatrixTest::runTestMul1() @@ -233,6 +329,10 @@ int main() { && MatrixTest::runTestEchelon1() && MatrixTest::runTestEchelon2() && MatrixTest::runTestEchelon3() + && MatrixTest::runTestRank1() + && MatrixTest::runTestRank2() + && MatrixTest::runTestRank3() + && MatrixTest::runTestSpan1() ); }; -- GitLab From b68dd74c49daef61baacddcd140e36410f1941bb Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 10 Mar 2020 19:13:03 +0100 Subject: [PATCH 095/142] added default intializer --- src/matrix.h | 1 + 1 file changed, 1 insertion(+) diff --git a/src/matrix.h b/src/matrix.h index 99fa115..69538be 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -31,6 +31,7 @@ public: /// @param height Height of the matrix Matrix(int height, int width) : Matrix(height, width, 0) {}; + Matrix() = default; Matrix(Matrix const& matrix) = default; /// Creates an identity matrix with dimension eye x eye -- GitLab From a17818e3b3c02c9143a46c473597908ab7cc4155 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Tue, 10 Mar 2020 19:13:25 +0100 Subject: [PATCH 096/142] started implementing affine relation --- CMakeLists.txt | 2 + src/affine_relation.cpp | 198 ++++++++++++++++++++++++++++++++++++++++ src/affine_relation.h | 66 ++++++++++++++ 3 files changed, 266 insertions(+) create mode 100644 src/affine_relation.cpp create mode 100644 src/affine_relation.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 934e7bc..2576898 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,6 +105,7 @@ set(PAIN_SOURCES src/simple_interval.cpp src/normalized_conjunction.cpp src/linear_equality.cpp + src/affine_relation.cpp ) set(PAIN_HEADERS @@ -117,6 +118,7 @@ set(PAIN_HEADERS src/global.h src/abstract_state.h src/hash_utils.h + src/affine_relation.h src/matrix.h ) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp new file mode 100644 index 0000000..f528f2f --- /dev/null +++ b/src/affine_relation.cpp @@ -0,0 +1,198 @@ +#include "affine_relation.h" +#include "llvm/IR/CFG.h" + +using namespace llvm; +using namespace std; + +namespace pcpo { + +// MARK: - Initializers + +AffineRelation::AffineRelation(Function const& f) { + numberOfVariables = 999; // FIXME: figure this out. + matrix = Matrix(numberOfVariables); + for (Argument const& arg: f.args()) { + index[&arg] = ++lastIndex; + } + isBottom = f.arg_empty(); +} + +AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const& state, CallInst const* call) { + // TODO +} + +// MARK: - AbstractState Interface + +void AffineRelation::applyPHINode(BasicBlock const& bb, vector pred_values, Instruction const& phi) { + // TODO +} + +void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* end_block, AffineRelation const& callee_state) { + // TODO +} + +void AffineRelation::applyReturnInst(Instruction const& inst) { + // TODO +} + +void AffineRelation::applyDefault(Instruction const& inst) { +// std::vector operands; + + if (inst.getNumOperands() != 2) return nonDeterminsticAssignment(&inst); + + // We only deal with integer types + IntegerType const* type = dyn_cast(inst.getType()); + if (not type) return nonDeterminsticAssignment(&inst); + + type = dyn_cast(inst.getOperand(0)->getType()); + if (not type) return nonDeterminsticAssignment(&inst); + + type = dyn_cast(inst.getOperand(1)->getType()); + if (not type) return nonDeterminsticAssignment(&inst); + +// for (llvm::Value const* value: inst.operand_values()) { +// operands.push_back(getAbstractValue(*value)); +// } + + switch (inst.getOpcode()) { + case Instruction::Add: + return Add(inst); + case Instruction::Sub: + return Sub(inst); + case Instruction::Mul: + return Mul(inst); + case Instruction::SDiv: + case Instruction::UDiv: + default: + return nonDeterminsticAssignment(&inst); + } + +// debug_output(inst, operands); +} + +//Matrix AffineRelation::getAbstractValue(Value const& value) const { +// if (llvm::Constant const* c = llvm::dyn_cast(&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 +} + +// MARK: - Lattice Operations + +bool AffineRelation::leastUpperBound(AffineRelation rhs) { + // TODO +} + +// MARK: - Assignments + +void AffineRelation::affineAssignment(Value const* xi, int64_t a, Value const* xj, int64_t b) { + Matrix affineTransformer = Matrix(numberOfVariables); + affineTransformer(0,index[xi]) = b; + affineTransformer(index[xi],index[xi]) = 0; + if (xj != nullptr) { + affineTransformer(index[xj],index[xi]) = a; + } + matrix *= affineTransformer; +} + +void AffineRelation::nonDeterminsticAssignment(Value const* xi) { + // TODO +} + +// MARK: - Abstract Operators + +void AffineRelation::Add(Instruction const& inst) { + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + // [xi := bj + bk] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return affineAssignment(&inst, 1, nullptr, b1->getSExtValue() + b2->getSExtValue() ); + // [xi := b + xj] + } else if (isa(op1) && isa(op2)) { + auto b = dyn_cast(op1); + return affineAssignment(&inst, 1, op2, b->getSExtValue()); + // [xi := xj + b] + } else if (isa(op2) && isa(op1)) { + auto b = dyn_cast(op2); + return affineAssignment(&inst, 1, op1, b->getSExtValue()); + } else { + assert(false); + return nonDeterminsticAssignment(&inst); + } +} + +void AffineRelation::Sub(Instruction const& inst) { + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + // [xi := bj - bk] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return affineAssignment(&inst, 1, nullptr, b1->getSExtValue() - b2->getSExtValue() ); + // [xi := b - xj] + } else if (isa(op1) && isa(op2)) { + auto b = dyn_cast(op1); + return affineAssignment(&inst, 1, op2, -b->getSExtValue()); + // [xi := xj - b] + } else if (isa(op2) && isa(op1)) { + auto b = dyn_cast(op2); + return affineAssignment(&inst, 1, op1, -b->getSExtValue()); + } else { + assert(false); + return nonDeterminsticAssignment(&inst); + } +} + +void AffineRelation::Mul(Instruction const& inst) { + auto op1 = inst.getOperand(0); + auto op2 = inst.getOperand(1); + + // [xi := aj * ak] + if (isa(op1) && (isa(op2))) { + auto b1 = dyn_cast(op1); + auto b2 = dyn_cast(op2); + return affineAssignment(&inst, 1, nullptr, b1->getSExtValue() * b2->getSExtValue() ); + // [xi := a * xj] + } else if (isa(op1) && isa(op2)) { + auto a = dyn_cast(op1); + return affineAssignment(&inst, a->getSExtValue(), op2, 0); + // [xi := xj * a] + } else if (isa(op2) && isa(op1)) { + auto a = dyn_cast(op2); + return affineAssignment(&inst, a->getSExtValue(), op1, 0); + } else { + assert(false); + return nonDeterminsticAssignment(&inst); + } +} + +// MARK: - debug output + +void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { + // TODO +} + +void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { + // TODO +} + +void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { + // TODO +} + +} diff --git a/src/affine_relation.h b/src/affine_relation.h new file mode 100644 index 0000000..fe14466 --- /dev/null +++ b/src/affine_relation.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include "global.h" +#include "matrix.h" + +#include + +namespace pcpo { + +class AffineRelation { +private: + int lastIndex = 0; + int numberOfVariables = 100; +public: + std::unordered_map index; + Matrix matrix; + bool isBottom = true; + + AffineRelation() = default; + AffineRelation(AffineRelation const& state) = default; + + explicit AffineRelation(llvm::Function const& f); + /// 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); + + /// Handles the evaluation of merging points + void applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, llvm::Instruction const& phi); + /// Handles the evaluation of function calls + /// This is the "combine" function as described in "Compiler Design: Analysis and Transformation" + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, AffineRelation const& callee_state); + /// Handles the evaluation of return instructions + void applyReturnInst(llvm::Instruction const& inst); + /// Handles the evaluation of all other instructions + void applyDefault(llvm::Instruction const& inst); + bool merge(Merge_op::Type op, AffineRelation const& other); + void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) { return; }; + bool leastUpperBound(AffineRelation rhs); + + bool checkOperandsForBottom(llvm::Instruction const& inst) { return false; } + +// Matrix AffineRelation::getAbstractValue(llvm::Value const& value) const; + + void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; + void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; + + // Abstract Assignments + void affineAssignment(llvm::Value const* xi, int64_t a, llvm::Value const* xj, int64_t b); + void nonDeterminsticAssignment(llvm::Value const* xi); + +protected: + // Abstract Operators + void Add(llvm::Instruction const& inst); + void Sub(llvm::Instruction const& inst); + void Mul(llvm::Instruction const& inst); + + /// Used for debug output + void debug_output(llvm::Instruction const& inst, Matrix operands); + + Matrix createTransformationMatrix(llvm::Instruction const& inst); +}; + + +} -- GitLab From cbdf66c65387f2875508e72fc899e9fa59fe6bda Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 12 Mar 2020 22:05:56 +0100 Subject: [PATCH 097/142] Added empty() toVector() and reshape() --- src/matrix.h | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/matrix.h b/src/matrix.h index 69538be..402d8a0 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -68,6 +68,8 @@ public: int getHeight() const { return height; }; /// The width of the matrix (number of columns) int getWidth() const { return width; }; + /// Returns true when the matrix is empty + bool empty() const { return getWidth() == 0 && getHeight() == 0; }; // MARK: - Matrix operations @@ -133,6 +135,32 @@ public: return Matrix(columns).transpose(); } + /// Converts the matrix to a 1D Vector by stacking the column vectors + Matrix toVector() const { + vector result; + result.reserve(getWidth() * getHeight()); + for (vector vector: vectors) { + result.insert(result.end(), vector.begin(), vector.end()); + } + return Matrix(result); + } + + /// Converts a 1D Vector to a Matrix with given dimensions + /// @param rows number of rows + /// @param columns number of columns + Matrix reshape(int rows, int columns) const { + assert(getWidth() == 1 && getHeight() == rows * columns); + vector> result; + result.reserve(rows); + for (int row = 0; row < rows; row++) { + vector rowVector; + rowVector.reserve(columns); + for (int column = 0; column < columns; column++) { + rowVector.push_back(value(row,column)); + } + } + return Matrix(result); + } /// Returns a vector with the elements of the row at index i. The returned row can be modified. /// @param i Index of the row to return. vector& row(int i) { return vectors[i]; }; -- GitLab From 3d4562ef18a6a565780459781c900f98c03ee9e8 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 12 Mar 2020 22:08:59 +0100 Subject: [PATCH 098/142] replaced direct access to vector with getter --- src/matrix.h | 54 ++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index 402d8a0..aa8932d 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -78,7 +78,7 @@ public: Matrix result = Matrix(width, height); for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - result.vectors[j][i] = vectors[i][j]; + result.value(j,i) = value(i,j); } } return result; @@ -91,17 +91,17 @@ public: for (int row = 0; row < height; row++) { if (pivot >= width) { return result; } int i = row; - while (result.vectors[i][pivot] == 0) { + while (result.value(i,pivot) == 0) { if (++i >= height) { i = row; if (++pivot >= width) { return result; } } } result.swap_rows(i, row); - result.divide_row(row, result.vectors[row][pivot]); + result.divide_row(row, result.value(row,pivot)); for (i = 0; i < height; i++) { if (i != row) { - result.add_multiple_row(i, row, -result.vectors[i][pivot]); + result.add_multiple_row(i, row, -result.value(i,pivot)); } } } @@ -114,9 +114,9 @@ public: int rank = 0; for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { - if ((e.vectors[row][column] == 0) && (column == width - 1)) { + if ((e.value(row,column) == 0) && (column == width - 1)) { return rank; - } else if (e.vectors[row][column] != 0) { + } else if (e.value(row,column) != 0) { break; } } @@ -161,13 +161,35 @@ public: } return Matrix(result); } + + /// Returns the value at row i and column j + /// @param row + /// @param column + T& value(int row, int column) { + assert(row < getHeight() && column < getHeight()); + return vectors[row][column]; + }; + + /// Returns the value at row i and column j + /// @param row + /// @param column + T const& value(int row, int column) const { + assert(row < getHeight() && column < getHeight()); + return vectors[row][column]; + }; + + /// Returns a vector with the elements of the row at index i. The returned row can be modified. /// @param i Index of the row to return. - vector& row(int i) { return vectors[i]; }; + vector& row(int i) { + assert(i < getHeight()); + return vectors[i]; + }; /// Returns the column at index i. The returned column cannot be modified. /// @param i Index of the column to return vector column(int i) const { + assert(i < getWidth()); vector row; row.reserve(width); for (vector const& x : vectors) { @@ -178,7 +200,7 @@ public: // MARK: - Operators - T& operator()(int i, int j) { return vectors[i][j]; }; + T& operator()(int i, int j) { return value(i,j); }; Matrix& operator *=(Matrix const& rhs) { assert(width == rhs.height); @@ -186,7 +208,7 @@ public: for (int i = 0; i < height; i++) { for (int k = 0; k < width; k++) { for (int j = 0; j < rhs.width; j++) { - result.vectors[i][j] += vectors[i][k] * rhs.vectors[k][j]; + result.value(i,j) += value(i,k) * rhs.value(k,j); } } } @@ -199,7 +221,7 @@ public: Matrix& operator *=(T scalar) { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - vectors[i][j] *= scalar; + value(i,j) *= scalar; } } return *this; @@ -210,7 +232,7 @@ public: assert(rhs.width == width && rhs.height == height); for (int i=0;i& operator +=(T scalar) { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - vectors[i][j] += scalar; + value(i,j) += scalar; } } return *this; @@ -229,7 +251,7 @@ public: assert(rhs.width == width && rhs.height == height); for (int i = 0; i < height; i++) { for (int j= 0; j < width; j++) { - vectors[i][j] -= rhs.vectors[i][j]; + value(i,j) -= rhs.value(i,j); } } return *this; @@ -238,7 +260,7 @@ public: Matrix& operator -=(int scalar) { for (int i = 0; i < height; i++) { for (int j = 0; j < width; j++) { - vectors[i][j] -= scalar; + value(i,j) -= scalar; } } return *this; @@ -265,7 +287,7 @@ protected: /// @param quotient quotient to divide the row by void divide_row(int row, int quotient) { for (int column = 0; column < width; column++) { - vectors[row][column] /= quotient; + value(row,column) /= quotient; } }; @@ -275,7 +297,7 @@ protected: /// @param factor Factor to multiply row b with when adding it to row a void add_multiple_row(int a, int b, int factor) { for (int column = 0; column < width; column++) { - vectors[a][column] += vectors[b][column] * factor; + value(a,column) += value(b,column) * factor; } }; -- GitLab From d4c75a85cdae734cd031c0c4ebd2da7cef04618a Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 12 Mar 2020 22:09:17 +0100 Subject: [PATCH 099/142] changed span to static func --- src/matrix.h | 10 +++++----- test/matrix_test.cpp | 3 ++- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index aa8932d..ef8c74a 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -126,11 +126,11 @@ public: } /// Linear span of the matrix ... fixme - Matrix span() const { + static Matrix span(Matrix matrix) { vector> columns; - int rank = getRank(); + int rank = matrix.getRank(); for (int col = 0; col::runTestSpan1() { }; auto matrix = Matrix(a); - auto actual = matrix.span(); + auto actual = Matrix::span(matrix); + result = actual == Matrix(expected); -- GitLab From bb7135879519af611adf2278c4ade467406e2413 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Thu, 12 Mar 2020 22:45:59 +0100 Subject: [PATCH 100/142] removed python scripts which were replaced by cmake --- init.py | 115 ------------------------------- run.py | 208 -------------------------------------------------------- 2 files changed, 323 deletions(-) delete mode 100755 init.py delete mode 100755 run.py diff --git a/init.py b/init.py deleted file mode 100755 index 8d44731..0000000 --- a/init.py +++ /dev/null @@ -1,115 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 - -import argparse -import os -import subprocess -import sys - -if sys.version_info[0] < 3: - print("Error: This script only supports Python 3") - sys.exit(5) - -parser = argparse.ArgumentParser(description='Setup the project. This creates the necessary symbolic links in the LLVM source code and adds the entries into the right CMakeList.txt. Also initialises the configuration for the run.py script.') -parser.add_argument('--llvm-path', help='path to the LLVM build directory, containing a file bin/opt.') -parser.add_argument('--llvm-src', help='path to the LLVM source directory, containing lib/Analysis ') -parser.add_argument('--clang-path', help='path to the clang build direcotry, containing a file bin/clang') -parser.add_argument('--xcode', action='store_true', help='for builds using xcode') -args = parser.parse_args() - -llvm_path = args.llvm_path -llvm_src = args.llvm_src -clang_path = args.clang_path -xcode = args.xcode - -project_dir = os.path.dirname(os.path.abspath(sys.argv[0])) -project_name = 'AbstractInterpretation' - -cmake = 'cmake' - -if llvm_path is None: - # Try to guess the correct path - d = os.path.abspath(project_dir) - while os.path.dirname(d) != d: - d = os.path.dirname(d) - for i in os.listdir(d): - if os.path.isfile(d + '/' + i + '/bin/opt'): - llvm_path = d + '/' + i - print('Auto-detecting llvm-path as ' + llvm_path) - break - if llvm_path is not None: break - else: - print('Error: No llvm-path specified (use --llvm-path)') - parser.print_help() - sys.exit(1) - -if llvm_src is None: - # Try to guess the correct path - d = os.path.abspath(project_dir) - while os.path.dirname(d) != d: - d = os.path.dirname(d) - for i in os.listdir(d): - if os.path.isfile(d + '/' + i + '/cmake_install.cmake'): - continue - if os.path.isdir(d + '/' + i + '/lib/Analysis'): - llvm_src = d + '/' + i - print('Auto-detecting llvm-src as ' + llvm_src) - break - if llvm_src is not None: break - else: - print('Error: No llvm-src specified (use --llvm-src)') - parser.print_help() - sys.exit(1) - -if clang_path is None: - clang_path = llvm_path - print('clang-path not specified, defaulting to ' + clang_path) - -opt = llvm_path + ['', '/Debug'][xcode] + '/bin/opt' -llvm_dest = llvm_src + '/lib/Analysis' -clang = clang_path + ['', '/Debug'][xcode] + '/bin/clang' - -if not os.path.isfile(opt): - print('Error: no opt exists at ' + opt + ' (maybe you forgot to build LLVM?)') - sys.exit(2) -if not os.path.isdir(llvm_dest): - print('Error: directory does not exist, at ' + llvm_dest) - sys.exit(2) -if not os.path.isfile(clang): - print('Error: no clang exists at ' + clang) - sys.exit(2) - -# Create the symbolic link in the LLVM sources -try: - link_name = llvm_dest + '/' + project_name - os.symlink(project_dir, link_name, target_is_directory=True) - print('Created symbolic link from %s to %s' % (link_name, project_dir)) -except FileExistsError: - print('Symlink already exists') - -# Write the configuration for the run.py script -config_file_name = project_dir + '/.config'; -config_file = open(config_file_name, 'w') -config_file.write(llvm_src+'\n'+llvm_path+'\n'+clang_path+'\n'+['make\n', 'xcode\n'][xcode]) -config_file.close() -print('Wrote configuration to %s' % (config_file_name,)) - -# Adjust CMakeLists.txt -cmake_file_name = llvm_dest + '/CMakeLists.txt' -line_to_insert = 'add_subdirectory(%s)\n' % (project_name,) -cmake_file = open(cmake_file_name, 'r') -lines = cmake_file.readlines() -needs_changes = line_to_insert not in lines -cmake_file.close() -if needs_changes: - cmake_file = open(cmake_file_name, 'a') - cmake_file.write('\n' + line_to_insert) - cmake_file.close() - print('CMakeLists.txt modified, at %s' % (cmake_file_name,)) - - # Also regenerate the CMake cache - print('Rebuilding cmake cache') - subprocess.run([cmake, llvm_path], check=True) -else: - print('CMakeLists.txt is fine') - diff --git a/run.py b/run.py deleted file mode 100755 index 87c6c0b..0000000 --- a/run.py +++ /dev/null @@ -1,208 +0,0 @@ -#!/usr/bin/python3 -# coding: utf-8 - -import argparse -import os -import platform -import shlex -import subprocess -import sys - -if sys.version_info[0] < 3: - print("Error: This script only supports Python 3") - sys.exit(5) - -project_dir = os.path.abspath(os.path.dirname(sys.argv[0])) -os.chdir(project_dir) - -if not os.path.isfile(".config"): - print("No config file was found. Please run init.py first!") - sys.exit(-1) - -config_file = open(".config", "r") -lines = config_file.readlines() -llvm_path = lines[1].strip() -clang_path = lines[2].strip() -try: - xcode = lines[3].strip() == 'xcode' -except IndexError: - xcode = false -config_file.close() - -opt = llvm_path + ['', '/Debug'][xcode] + '/bin/opt' -llvm_dis = llvm_path + ['', '/Debug'][xcode] + '/bin/llvm-dis' -llvm_config = llvm_path + ['', '/Debug'][xcode] + '/bin/llvm-config' -clang = clang_path + ['', '/Debug'][xcode] + '/bin/clang' -cmake = 'cmake' -gdb = 'gdb' -lldb = 'lldb' - -CXX = os.environ.get('CXX', 'c++') - -if not os.path.isfile(opt): - print('Error: no opt exists at ' + opt + ' (maybe you forgot to build LLVM?)') - sys.exit(2) -if not os.path.isfile(llvm_dis): - print('Error: no llvm-dis exists at ' + llvm_dis + ' (maybe you forgot to build LLVM?)') - sys.exit(2) -if not os.path.isfile(clang): - print('Error: no clang exists at ' + clang) - sys.exit(2) - -if platform.system() == 'Linux': - libeext = '.so' -elif platform.system() == 'Windows': - libeext = '.dll' - print('Error: Windows is not supported. (You can try to delete this error and proceed at your own risk.') - sys.exit(4) -elif platform.system() == 'Darwin': - libeext = '.dylib' -else: - print('Error: Unknown platform ' + platform.system()) - sys.exit(4) - -pass_lib = llvm_path + ['', '/Debug'][xcode] + "/lib/llvm-pain" + libeext -pass_name = "painpass" -make_target = "llvm-pain" - -samples = project_dir + '/samples' - -def main(): - def run(arg, cwd=None, redirect=None): - if not args.only_print: - try: - if redirect: - f = open(redirect, 'w') - subprocess.run(arg, cwd=cwd, stdout=f, stderr=f, check=True) - f.close() - else: - subprocess.run(arg, cwd=cwd, check=True) - except subprocess.CalledProcessError as e: - print('Error: while executing ' + str(e.cmd)) - if redirect: - f.close() - print(open(redirect, 'r').read()) - sys.exit(3) - else: - cmd = ' '.join(shlex.quote(i) for i in arg) - if redirect: - cmd += ' > %s 2>&1' % (shlex.quote(redirect),) - if cwd is None: - print(' ' + cmd) - else: - print(' ( cd %s && %s )' % (shlex.quote(cwd), cmd)) - - parser = argparse.ArgumentParser() - parser.add_argument("file", help="run only the specfied files", nargs='*') - parser.add_argument("-n", dest='only_print', help="only print the commands, do not execute anything", action="store_true") - parser.add_argument("-v", dest='show_output', help="show output on stdout", action="store_true") - parser.add_argument("--cfg", dest='view_cfg', help="show llvm control flow graph", action="store_true") - parser.add_argument("--make", dest='do_make', help="call make before executing the script", action="store_true") - parser.add_argument("--only-make", dest='do_make_only', help="only call make, do not execute any samples", action="store_true") - parser.add_argument("--gdb", dest='do_gdb', help="open the gdb debugger for the specified file", action="store_true") # might not work with mac OS try lldb - parser.add_argument("--lldb", dest='do_lldb', help="open the lldb debugger for the specified file", action="store_true") - parser.add_argument("--run-test", dest='run_test', help="run the test for SimpleInterval", action="store_true") - parser.add_argument("--use-cxx", metavar='path', dest='use_cxx', help="use as c++ compiler when building the test") - args = parser.parse_args() - - # If no files are specified, set it to all .c files in current directory - files = args.file - if args.run_test: - if files: - print('Error: you are trying to both run the test and a file. This does not really make sense.') - sys.exit(4) - elif args.do_gdb: - if len(files) != 1: - print('Error: you are trying to run the debugger on multiple files. This does not really make sense, just specify a single one.') - sys.exit(4) - elif args.do_make_only: - args.do_make = True - files = [] - args.run_test = False - elif not files: - files = [i for i in os.listdir(samples) if i.endswith('.c')] - - if args.do_make: - print("Building %s..." % (make_target,)) - run([cmake, '--build', llvm_path, '--target', make_target]) - - if not os.path.isfile(pass_lib): - print('Error: Could not find shared library ' + pass_lib) - print('Please build the project (for example by running this script with the option --make') - sys.exit(7) - - os.makedirs('output', exist_ok=True) - - for fname in files: - f_orig = 'samples/%s' % (fname,) - f_bc = 'output/%s-tmp.bc' % (fname,) - f_optbc = 'output/%s.bc' % (fname,) - f_optll = 'output/%s.ll' % (fname,) - f_out = 'output/%s.out' % (fname,) - - if not os.path.isfile(f_orig): - print("Error: " + f_orig +" not found!") - continue - print("Processing file " + fname + " ...") - - if platform.system() == 'Darwin': - includes = subprocess.getoutput('xcrun --show-sdk-path') - run([clang, '--sysroot', includes, '-O0', '-emit-llvm', f_orig, '-Xclang', '-disable-O0-optnone', '-c', '-o', f_bc]) - else: - run([clang, '-O0', '-emit-llvm', f_orig, '-Xclang', '-disable-O0-optnone', '-c', '-o', f_bc]) - - run([opt, '-mem2reg', f_bc, '-o', f_optbc]) - run([llvm_dis, f_optbc, '-o', f_optll]) - run(['rm', f_bc, f_optbc]) - - redir = None if args.show_output else f_out - args_add = [] - if args.view_cfg: - args_add.append('--view-cfg') - - base_args = ['-load', pass_lib, '-'+pass_name, '-S'] + args_add + ['-o', '/dev/null', f_optll] - if not args.do_gdb: - run([opt] + base_args, redirect=redir) - else: - break_at = 'pcpo::AbstractInterpretationPass::runOnModule(llvm::Module&)' - if not args.only_print: - print('In a moment, gdb is going to read in the symbols of opt. As you might notice, that takes a long time. So, here is a tip: Just restart the program using r (no need to specify arguments). Even if you rebuild the project, that is in a shared library and will thus be reloaded the next time you start the program.') - run([gdb, '-q', opt, '-ex', 'r ' + ' '.join(map(shlex.quote, base_args))]) - - if not args.do_lldb: - run([opt] + base_args, redirect=redir) - else: - break_at = 'pcpo::AbstractInterpretationPass::runOnModule(llvm::Module&)' - run([lldb, opt, '--', ' '.join(map(shlex.quote, base_args))]) - - if args.run_test: - if not os.path.isfile(llvm_config): - print('Error: no llvm-config exists at ' + llvm_config + ' (maybe you forgot to build LLVM?)') - sys.exit(2) - - os.makedirs('build', exist_ok=True) - - cxx = CXX - if args.use_cxx is not None: - cxx = args.use_cxx - - print('Info: Building the test using %s' % (cxx,)) - - cxxflags = subprocess.run([llvm_config, '--cxxflags'], stdout=subprocess.PIPE).stdout.decode('ascii').split() - ldflags = subprocess.run([llvm_config, '--ldflags' ], stdout=subprocess.PIPE).stdout.decode('ascii').split() - libs = subprocess.run([llvm_config, '--libs', 'analysis'], stdout=subprocess.PIPE).stdout.decode('ascii').split() - if platform.system() == 'Darwin': - libs += '-lz -ldl -lpthread -lm -lcurses'.split() - else: - libs += '-lz -lrt -ldl -ltinfo -lpthread -lm'.split() - - run([cxx, 'test/simple_interval_test.cpp', 'src/simple_interval.cpp', '-Isrc', '-fmax-errors=2'] + cxxflags - + ['-o', 'build/SimpleIntervalTest'] + ldflags + libs) - - try: - run(['build/SimpleIntervalTest']) - except KeyboardInterrupt: - pass - -if __name__ == "__main__": - main() -- GitLab From fc4fae4611cd1091029e00bff70e13bd6bcc9cc3 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 00:16:10 +0100 Subject: [PATCH 101/142] implemented << operator --- src/matrix.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/matrix.h b/src/matrix.h index ef8c74a..ddf186f 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -342,5 +342,17 @@ inline Matrix operator-(Matrix lhs, Matrix const& rhs) { return lhs -= template inline Matrix operator-(Matrix lhs, T scalar) { return lhs -= scalar; }; +template +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix matrix) { + for (int row = 0; row < matrix.getHeight(); row++) { + os << "[\t"; + for (int column = 0; column < matrix.getWidth(); column++) { + os << matrix(row,column) << " \t"; + } + os << "]\n"; + } + return os; +}; + } -- GitLab From 4f5f49b99b5b18b029c9b3c6049e7b2f5ee38b5f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 00:49:05 +0100 Subject: [PATCH 102/142] using namespace llvm --- src/normalized_conjunction.cpp | 54 +++++++++++++++++----------------- 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 7c08550..656549f 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -15,20 +15,20 @@ namespace pcpo { // MARK: - Initializers -NormalizedConjunction::NormalizedConjunction(llvm::Function const& f) { - for (llvm::Argument const& arg: f.args()) { +NormalizedConjunction::NormalizedConjunction(Function const& f) { + for (Argument const& arg: f.args()) { get(&arg) = LinearEquality(&arg); validVariables.insert(&arg); } isBottom = f.arg_empty(); } -NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, NormalizedConjunction const& state, llvm::CallInst const* call) { +NormalizedConjunction::NormalizedConjunction(Function const* callee_func, NormalizedConjunction const& state, CallInst const* call) { assert(callee_func->arg_size() == call->getNumArgOperands()); - for (llvm::Argument const& arg: callee_func->args()) { - llvm::Value* value = call->getArgOperand(arg.getArgNo()); + for (Argument const& arg: callee_func->args()) { + Value* value = call->getArgOperand(arg.getArgNo()); if (value->getType()->isIntegerTy()) { - if (llvm::ConstantInt const* c = llvm::dyn_cast(value)) { + if (ConstantInt const* c = dyn_cast(value)) { get(&arg) = { &arg, 1 , nullptr, c->getSExtValue() }; } else if (state.values.count(value) > 0) { LinearEquality value_equality = state.values.at(value); @@ -43,7 +43,7 @@ NormalizedConjunction::NormalizedConjunction(llvm::Function const* callee_func, isBottom = false; } -NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { +NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { this->values = equalaties; isBottom = equalaties.empty(); for (auto& [key, value]: equalaties) { @@ -55,9 +55,9 @@ NormalizedConjunction::NormalizedConjunction(std::unordered_map pred_values, - llvm::Instruction const& phi) { - llvm::PHINode const* phiNode = llvm::dyn_cast(&phi); +void NormalizedConjunction::applyPHINode(BasicBlock const& bb, std::vector pred_values, + Instruction const& phi) { + PHINode const* phiNode = dyn_cast(&phi); int i = 0; @@ -65,7 +65,7 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector auto& incoming_value = *phiNode->getIncomingValueForBlock(pred_bb); auto& incoming_state = pred_values[i]; - if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { + if (ConstantInt const* c = dyn_cast(&incoming_value)) { NormalizedConjunction acc = *this; acc.linearAssignment(&phi, 1, nullptr, c->getSExtValue()); merge(Merge_op::UPPER_BOUND, acc); @@ -83,7 +83,7 @@ void NormalizedConjunction::applyPHINode(llvm::BasicBlock const& bb, std::vector } } -void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, NormalizedConjunction const& callee_state) { +void NormalizedConjunction::applyCallInst(Instruction const& inst, BasicBlock const* end_block, NormalizedConjunction const& callee_state) { std::vector operands; // // Keep the debug output happy @@ -93,8 +93,8 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B //iterate through all instructions of it till we find a return statement for (auto& iter_inst: *end_block) { - if (llvm::ReturnInst const* ret_inst = llvm::dyn_cast(&iter_inst)) { - llvm::Value const* ret_val = ret_inst->getReturnValue(); + if (ReturnInst const* ret_inst = dyn_cast(&iter_inst)) { + Value const* ret_val = ret_inst->getReturnValue(); dbgs(4) << "\t\tFound return instruction\n"; if (callee_state.values.find(ret_val) != callee_state.values.end()) { dbgs(4) << "\t\tReturn evaluated, merging parameters\n"; @@ -109,10 +109,10 @@ void NormalizedConjunction::applyCallInst(llvm::Instruction const& inst, llvm::B // debug_output(inst, operands); } -void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { - llvm::Value const* ret_val = llvm::dyn_cast(&inst)->getReturnValue(); +void NormalizedConjunction::applyReturnInst(Instruction const& inst) { + Value const* ret_val = dyn_cast(&inst)->getReturnValue(); if (ret_val && ret_val->getType()->isIntegerTy()) { - if (llvm::ConstantInt const* c = llvm::dyn_cast(ret_val)) { + if (ConstantInt const* c = dyn_cast(ret_val)) { get(&inst) = LinearEquality(c); } else if (values.find(ret_val) != values.end()) { LinearEquality eq = values.at(ret_val); @@ -122,7 +122,7 @@ void NormalizedConjunction::applyReturnInst(llvm::Instruction const& inst) { validVariables.insert(&inst); } -void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { +void NormalizedConjunction::applyDefault(Instruction const& inst) { std::vector operands; if (inst.getNumOperands() != 2) return nonDeterminsticAssignment(&inst); @@ -137,7 +137,7 @@ void NormalizedConjunction::applyDefault(llvm::Instruction const& inst) { type = dyn_cast(inst.getOperand(1)->getType()); if (not type) return nonDeterminsticAssignment(&inst); - for (llvm::Value const* value: inst.operand_values()) { + for (Value const* value: inst.operand_values()) { operands.push_back(LinearEquality(value)); } @@ -594,10 +594,10 @@ LinearEquality& NormalizedConjunction::get(Value const* value) { // MARK: - Debug -void NormalizedConjunction::debug_output(llvm::Instruction const& inst, std::vector operands) { +void NormalizedConjunction::debug_output(Instruction const& inst, std::vector operands) { dbgs(3).indent(2) << inst << " // " << values.at(&inst) << ", args "; {int i = 0; - for (llvm::Value const* value: inst.operand_values()) { + for (Value const* value: inst.operand_values()) { if (i) dbgs(3) << ", "; if (value->getName().size()) dbgs(3) << '%' << value->getName() << " = "; dbgs(3) << operands[i]; @@ -606,15 +606,15 @@ void NormalizedConjunction::debug_output(llvm::Instruction const& inst, std::vec dbgs(3) << '\n'; } -void NormalizedConjunction::printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { +void NormalizedConjunction::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation = 0) const { // @Speed: This is quadratic, could be linear bool nothing = true; - for (std::pair const& i: values) { + for (std::pair const& i: values) { bool read = false; bool written = false; - for (llvm::Instruction const& inst: bb) { + for (Instruction const& inst: bb) { if (&inst == i.first) written = true; - for (llvm::Value const* v: inst.operand_values()) { + for (Value const* v: inst.operand_values()) { if (v == i.first) read = true; } } @@ -629,9 +629,9 @@ void NormalizedConjunction::printIncoming(llvm::BasicBlock const& bb, llvm::raw_ } } -void NormalizedConjunction::printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation = 0) const { +void NormalizedConjunction::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation = 0) const { for (auto const& i: values) { - if (llvm::ReturnInst::classof(i.first)) { + if (ReturnInst::classof(i.first)) { out.indent(indentation) << " = " << i.second << '\n'; } else { out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; -- GitLab From 17f19bc049a6c38b8e0db0f7246052ffb0ff7718 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 02:09:16 +0100 Subject: [PATCH 103/142] added reshapeColumns and changed return type of toVector() --- src/matrix.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index ddf186f..b80a36d 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -136,13 +136,13 @@ public: } /// Converts the matrix to a 1D Vector by stacking the column vectors - Matrix toVector() const { + std::vector toVector() const { vector result; result.reserve(getWidth() * getHeight()); for (vector vector: vectors) { result.insert(result.end(), vector.begin(), vector.end()); } - return Matrix(result); + return result; } /// Converts a 1D Vector to a Matrix with given dimensions @@ -161,6 +161,13 @@ public: } return Matrix(result); } + + vector> reshapeColumns(int height, int width) const { + vector> result; + for (int c = 0; c < width; c++) { + result.push_back(column(c).reshape(height, width)); + } + } /// Returns the value at row i and column j /// @param row -- GitLab From 1dbdcbc18019a41518dc2fd45ad4b1b07d443e6d Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 22:22:29 +0100 Subject: [PATCH 104/142] moved properties to top of file --- src/matrix.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index b80a36d..a0b85a9 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -10,6 +10,11 @@ namespace pcpo { /// Matrix. Row and column are indexed beginning at 0 template class Matrix { +protected: + vector> vectors; + int width; + int height; + public: //MARK: - Initializers /// Creates a matrix with dimensions height x width and initalizes its values to `value` @@ -278,9 +283,6 @@ public: }; protected: - vector> vectors; - int width; - int height; // MARK: - Echelon helpers -- GitLab From efc7c294fe04875479c10acfe3f7d1c8f3bce7ae Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 22:23:00 +0100 Subject: [PATCH 105/142] improved reshape --- src/matrix.h | 45 ++++++++++++++++++++++++++++++--------------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index a0b85a9..72f0a6c 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -48,7 +48,7 @@ public: for (int i = 0; i < height; i++) { vector vector(width,0); vector[i] = 1; - vectors[i] = vector; + vectors.push_back(vector); } }; @@ -67,6 +67,23 @@ public: this->vectors = vectors; }; + Matrix(vector const &values, int rows, int columns) { + assert(int(values.size()) == rows * columns); + vector> result; + result.reserve(rows); + for (int row = 0; row < rows; row++) { + vector rowVector; + rowVector.reserve(columns); + for (int column = 0; column < columns; column++) { + rowVector.push_back(values[row * rows + column]); + } + result.push_back(rowVector); + } + this->vectors = result; + this->width = columns; + this->height = rows; + }; + // MARK: - Properties /// The height of the matrix (number of rows) @@ -154,24 +171,15 @@ public: /// @param rows number of rows /// @param columns number of columns Matrix reshape(int rows, int columns) const { - assert(getWidth() == 1 && getHeight() == rows * columns); - vector> result; - result.reserve(rows); - for (int row = 0; row < rows; row++) { - vector rowVector; - rowVector.reserve(columns); - for (int column = 0; column < columns; column++) { - rowVector.push_back(value(row,column)); - } - } - return Matrix(result); - } - + return Matrix(vectors.front(), rows, columns); + }; + vector> reshapeColumns(int height, int width) const { vector> result; for (int c = 0; c < width; c++) { - result.push_back(column(c).reshape(height, width)); + result.push_back(Matrix(column(c), height, width)); } + return result; } /// Returns the value at row i and column j @@ -210,6 +218,13 @@ public: return row; } + void setColumn(vector const& vector, int column) { + assert(int(vector.size()) == height); + for (int row = 0; row < height; row++) { + value(row,column) = vector[row]; + } + } + // MARK: - Operators T& operator()(int i, int j) { return value(i,j); }; -- GitLab From 8a21c93a5ec568832d1badf342fb9729a190b8ab Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 13 Mar 2020 23:29:37 +0100 Subject: [PATCH 106/142] started implementing affine_relation --- src/affine_relation.cpp | 284 ++++++++++++++++++++++++++++----- src/affine_relation.h | 10 +- src/fixpoint.cpp | 4 +- src/matrix.h | 9 +- src/normalized_conjunction.cpp | 4 +- 5 files changed, 263 insertions(+), 48 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index f528f2f..f7eece2 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -1,6 +1,8 @@ #include "affine_relation.h" #include "llvm/IR/CFG.h" +#include + 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(numberOfVariables); - for (Argument const& arg: f.args()) { - index[&arg] = ++lastIndex; - } - isBottom = f.arg_empty(); +AffineRelation::AffineRelation(Function const& func) { + index = createVariableIndexMap(func); + basis = {Matrix(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(value)) { + affineAssignment(&arg, 1, nullptr, c->getSExtValue()); +// for (Matrix m: basis) { +// vector column = vector(m.getHeight(),0); +// column[0] = c->getSExtValue(); +// m.setColumn(column, index[&arg]); +// } + } else { + affineAssignment(&arg, 1, value, 0); +// for (Matrix 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 m: basis) { +// m.value(index[&arg], index[&arg]) = 1; +// } + } + } + isBottom = basis.empty();; } // MARK: - AbstractState Interface void AffineRelation::applyPHINode(BasicBlock const& bb, vector pred_values, Instruction const& phi) { - // TODO + PHINode const* phiNode = dyn_cast(&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(&incoming_value)) { + AffineRelation acc = *this; + acc.affineAssignment(&phi, 1, nullptr, c->getSExtValue()); + +// AffineRelation acc = *this; +// vector 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 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 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(&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 returnValue = callee_state.basis.front().column(index[ret_val]); +// for (Matrix 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(&inst)->getReturnValue(); + if (ret_val && ret_val->getType()->isIntegerTy()) { + if (ConstantInt const* c = dyn_cast(ret_val)) { + affineAssignment(&inst, 1, nullptr, c->getSExtValue()); +// vector column = vector(getNumberOfVariables() + 1,0); +// column[0] = c->getSExtValue(); +// for (Matrix m: basis) { +// m.setColumn(column, index[&inst]); +// } + } else { + affineAssignment(&inst, 1, ret_val, 0); + // FIXME: Handle multiple basis! +// vector column = basis.front().column(index[ret_val]); +// for (Matrix 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(inst.getOperand(1)->getType()); if (not type) return nonDeterminsticAssignment(&inst); + if (isa(inst.getOperand(0)) || isa(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 AffineRelation::getAbstractValue(Value const& value) const { -// if (llvm::Constant const* c = llvm::dyn_cast(&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> before = basis; + vector> vectors; + vectors.reserve(basis.size() + rhs.basis.size()); + for (Matrix m: basis) { + vectors.push_back(m.toVector()); + } + + for (Matrix 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 combined = Matrix(vectors).transpose(); + dbgs(4) << combined; + Matrix result = Matrix::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 relations, int constant) { + Matrix Wr = Matrix(getNumberOfVariables() + 1); + Wr(0,index.at(xi)) = constant; + for (auto [variable, factor]: relations) { + Wr(index.at(variable),index.at(xi)) = factor; + } + + for (Matrix matrix: basis) { + // FIXME: span(Wr) + matrix *= Wr; + } +} + void AffineRelation::affineAssignment(Value const* xi, int64_t a, Value const* xj, int64_t b) { - Matrix affineTransformer = Matrix(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 T0 = Matrix(getNumberOfVariables() + 1); + Matrix T1 = Matrix(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 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(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(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(op2); return affineAssignment(&inst, a->getSExtValue(), op1, 0); } else { - assert(false); return nonDeterminsticAssignment(&inst); } } +// MARK: - Helpers + +unordered_map createVariableIndexMap_impl(Function const& func, int& count, set visited_funcs) { + unordered_map map; + visited_funcs.insert(&func); + for (BasicBlock const& basic_block: func) { + for (Instruction const& inst: basic_block) { + if (isa(inst.getType()) || isa(&inst)) { + count++; + map[&inst] = count; + } + if (CallInst const* call = dyn_cast(&inst)) { + Function const* callee_func = call->getCalledFunction(); + if (callee_func->empty()) { + continue; + } + if (visited_funcs.count(callee_func) == 0) { + unordered_map callee_map = createVariableIndexMap_impl(*callee_func, count, visited_funcs); + map.insert(callee_map.begin(), callee_map.end()); + } + } + } + } + return map; +} + +unordered_map 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 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"; + } } } diff --git a/src/affine_relation.h b/src/affine_relation.h index fe14466..0e0f0ea 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -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 createVariableIndexMap(llvm::Function const& func); public: std::unordered_map index; - Matrix matrix; + std::vector> 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 relations, int constant); void nonDeterminsticAssignment(llvm::Value const* xi); protected: diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 9ae3aa1..f815352 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -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(M); - executeFixpointAlgorithm(M); +// executeFixpointAlgorithm(M); + executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); // We never change anything diff --git a/src/matrix.h b/src/matrix.h index 72f0a6c..7ed4ad6 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -55,8 +55,8 @@ public: /// Creates a matrix from a 2D vector /// @param vectors 2D vector containing columns with rows Matrix(vector> 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 span(Matrix matrix) { vector> columns; int rank = matrix.getRank(); @@ -157,6 +157,9 @@ public: return Matrix(columns).transpose(); } + /// Computes the null space for the column vectors + static Matrix null(Matrix matrix); + /// Converts the matrix to a 1D Vector by stacking the column vectors std::vector toVector() const { vector result; @@ -176,7 +179,7 @@ public: vector> reshapeColumns(int height, int width) const { vector> 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; diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 656549f..31d8b05 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -136,7 +136,9 @@ void NormalizedConjunction::applyDefault(Instruction const& inst) { type = dyn_cast(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)); } -- GitLab From 607000230387b066992271672269c2858ca902a1 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 14 Mar 2020 18:28:20 +0100 Subject: [PATCH 107/142] handle undef values with non-deterministic assignments --- src/normalized_conjunction.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 31d8b05..5c9433e 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -137,8 +137,10 @@ void NormalizedConjunction::applyDefault(Instruction const& inst) { type = dyn_cast(inst.getOperand(1)->getType()); if (not type) return nonDeterminsticAssignment(&inst); - // TODO: handle undef values - + if (isa(inst.getOperand(0)) || isa(inst.getOperand(1))) { + return nonDeterminsticAssignment(&inst); + } + for (Value const* value: inst.operand_values()) { operands.push_back(LinearEquality(value)); } -- GitLab From 783b351348b0ba305b4ec3b0aad35fc2b7e1eb91 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 14 Mar 2020 18:30:09 +0100 Subject: [PATCH 108/142] removed commented out code --- src/affine_relation.cpp | 54 ++++------------------------------------- 1 file changed, 5 insertions(+), 49 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index f7eece2..bf512c7 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -26,26 +26,12 @@ AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const if (value->getType()->isIntegerTy()) { if (ConstantInt const* c = dyn_cast(value)) { affineAssignment(&arg, 1, nullptr, c->getSExtValue()); -// for (Matrix m: basis) { -// vector column = vector(m.getHeight(),0); -// column[0] = c->getSExtValue(); -// m.setColumn(column, index[&arg]); -// } } else { affineAssignment(&arg, 1, value, 0); -// for (Matrix 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 m: basis) { -// m.value(index[&arg], index[&arg]) = 1; -// } } } - isBottom = basis.empty();; + isBottom = basis.empty(); } // MARK: - AbstractState Interface @@ -61,13 +47,6 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector p if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { AffineRelation acc = *this; acc.affineAssignment(&phi, 1, nullptr, c->getSExtValue()); - -// AffineRelation acc = *this; -// vector 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; @@ -75,11 +54,6 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector p auto val = m.column(index.at(&incoming_value)); acc.affineAssignment(&phi, 1, &incoming_value, 0); } -// vector 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++; @@ -102,14 +76,7 @@ void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* en 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 returnValue = callee_state.basis.front().column(index[ret_val]); -// for (Matrix m: basis) { -// m.setColumn(returnValue, index[&inst]); -// } } else { dbgs(4) << " Return not evaluated, setting to bottom\n"; // TODO: What should we do here @@ -124,18 +91,8 @@ void AffineRelation::applyReturnInst(Instruction const& inst) { if (ret_val && ret_val->getType()->isIntegerTy()) { if (ConstantInt const* c = dyn_cast(ret_val)) { affineAssignment(&inst, 1, nullptr, c->getSExtValue()); -// vector column = vector(getNumberOfVariables() + 1,0); -// column[0] = c->getSExtValue(); -// for (Matrix m: basis) { -// m.setColumn(column, index[&inst]); -// } } else { affineAssignment(&inst, 1, ret_val, 0); - // FIXME: Handle multiple basis! -// vector column = basis.front().column(index[ret_val]); -// for (Matrix m: basis) { -// m.setColumn(column, index[&inst]); -// } } } } @@ -231,8 +188,7 @@ void AffineRelation::affineAssignment(Value const* xi, unordered_map matrix: basis) { - // FIXME: span(Wr) - matrix *= Wr; + matrix *= Matrix::span(Wr); } } @@ -363,8 +319,8 @@ unordered_map AffineRelation::createVariableIndexMap(Function // MARK: - debug output void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { - // TODO for (auto m: basis) { + out << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { out << val->getName(); @@ -376,8 +332,8 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i } void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { - // TODO for (auto m: basis) { + out << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { out << val->getName(); @@ -389,8 +345,8 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i } void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { - // TODO for (auto m: basis) { + dbgs(3) << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { dbgs(3) << val->getName(); -- GitLab From 371ad5aa72b59f48a47ccb4ffa1c0c7fd395e477 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 14 Mar 2020 19:09:51 +0100 Subject: [PATCH 109/142] debug output formating --- src/affine_relation.cpp | 15 +++++++++------ src/matrix.h | 2 +- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index bf512c7..26e92e1 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -323,9 +323,10 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i out << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { - out << val->getName(); + out << val->getName() << "\t\t"; + } else { + dbgs(3) << "<>" << "\t\t"; } - out << "\t\t"; } out << "\n" << m << "\n"; } @@ -336,9 +337,10 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i out << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { - out << val->getName(); + out << val->getName() << "\t\t"; + } else { + dbgs(3) << "<>" << "\t\t"; } - out << "\t\t"; } out << "\n" << m << "\n"; } @@ -349,9 +351,10 @@ void AffineRelation::debug_output(Instruction const& inst, Matrix operands) dbgs(3) << "\t"; for (auto [val, idx]: index) { if (val->hasName()) { - dbgs(3) << val->getName(); + dbgs(3) << val->getName() << "\t\t"; + } else { + dbgs(3) << "<>" << "\t\t"; } - dbgs(3) << "\t\t"; } dbgs(3) << "\n" << m << "\n"; } diff --git a/src/matrix.h b/src/matrix.h index 7ed4ad6..0ac0595 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -374,7 +374,7 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix matrix) { for (int row = 0; row < matrix.getHeight(); row++) { os << "[\t"; for (int column = 0; column < matrix.getWidth(); column++) { - os << matrix(row,column) << " \t"; + os << matrix(row,column) << "\t\t"; } os << "]\n"; } -- GitLab From 5a6c26ffbcf1633b70c888fc82ab99e5866235cf Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 14 Mar 2020 23:34:52 +0100 Subject: [PATCH 110/142] improved debug output --- src/affine_relation.cpp | 18 +++++++++--------- src/fixpoint.cpp | 2 +- src/global.h | 3 ++- src/matrix.h | 10 +++++++--- 4 files changed, 19 insertions(+), 14 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 26e92e1..c8abbab 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -320,12 +320,12 @@ unordered_map AffineRelation::createVariableIndexMap(Function void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { for (auto m: basis) { - out << "\t"; + out << llvm::left_justify("", 8); for (auto [val, idx]: index) { if (val->hasName()) { - out << val->getName() << "\t\t"; + out << llvm::left_justify(val->getName(), 6); } else { - dbgs(3) << "<>" << "\t\t"; + out << llvm::left_justify("<>", 6); } } out << "\n" << m << "\n"; @@ -334,12 +334,12 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { for (auto m: basis) { - out << "\t"; + out << llvm::left_justify("", 8); for (auto [val, idx]: index) { if (val->hasName()) { - out << val->getName() << "\t\t"; + out << llvm::left_justify(val->getName(), 6); } else { - dbgs(3) << "<>" << "\t\t"; + out << llvm::left_justify("<>", 6); } } out << "\n" << m << "\n"; @@ -348,12 +348,12 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { for (auto m: basis) { - dbgs(3) << "\t"; + dbgs(3) << llvm::left_justify("", 8); for (auto [val, idx]: index) { if (val->hasName()) { - dbgs(3) << val->getName() << "\t\t"; + dbgs(3) << llvm::left_justify(val->getName(), 6); } else { - dbgs(3) << "<>" << "\t\t"; + dbgs(3) << llvm::left_justify("<>", 6); } } dbgs(3) << "\n" << m << "\n"; diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index f815352..28919ca 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -310,7 +310,7 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(3) << " Merging with stored state\n"; bool changed = node.state.merge(merge_op, state_new); - dbgs(2) << " Outgoing state is:\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); + dbgs(2) << " Outgoing state " << (changed ? "changd" : "didn't change") << ":\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); // No changes, so no need to do anything else if (not changed) continue; diff --git a/src/global.h b/src/global.h index 7775c16..9ae8403 100644 --- a/src/global.h +++ b/src/global.h @@ -1,6 +1,7 @@ #pragma once -#include "llvm/Support/raw_ostream.h" +#include +#include namespace pcpo { diff --git a/src/matrix.h b/src/matrix.h index 0ac0595..1cb3766 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -372,11 +372,15 @@ inline Matrix operator-(Matrix lhs, T scalar) { return lhs -= scalar; }; template llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix matrix) { for (int row = 0; row < matrix.getHeight(); row++) { - os << "[\t"; + os << "[ "; for (int column = 0; column < matrix.getWidth(); column++) { - os << matrix(row,column) << "\t\t"; + if (column == matrix.getWidth() - 1) { + os << llvm::format("%d", matrix(row,column)); + } else { + os << llvm::format("%-6d", matrix(row,column)); + } } - os << "]\n"; + os << " ]\n"; } return os; }; -- GitLab From d2f76261d043d94ceb5f4708530263c664087de5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 14 Mar 2020 23:35:55 +0100 Subject: [PATCH 111/142] fixed some bugs and removed debug log --- src/affine_relation.cpp | 5 ++--- src/fixpoint.cpp | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index c8abbab..32aa829 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -170,12 +170,11 @@ bool AffineRelation::leastUpperBound(AffineRelation rhs) { } // FIXME: i think this is transposing it twice. Maybe create a fast path for this kind of thing. Matrix combined = Matrix(vectors).transpose(); - dbgs(4) << combined; Matrix result = Matrix::span(combined); basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getHeight()); // FIXME: figure out a better way to detect changes - return before == basis; + return before != basis; } // MARK: - Assignments @@ -187,7 +186,7 @@ void AffineRelation::affineAssignment(Value const* xi, unordered_map matrix: basis) { + for (Matrix& matrix: basis) { matrix *= Matrix::span(Wr); } } diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 28919ca..7126df2 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -310,7 +310,7 @@ void executeFixpointAlgorithm(Module const& M) { dbgs(3) << " Merging with stored state\n"; bool changed = node.state.merge(merge_op, state_new); - dbgs(2) << " Outgoing state " << (changed ? "changd" : "didn't change") << ":\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); + dbgs(2) << " Outgoing state " << (changed ? "changed" : "didn't change") << ":\n"; state_new.printOutgoing(*node.basic_block, dbgs(2), 4); // No changes, so no need to do anything else if (not changed) continue; -- GitLab From bec634cabdba2016abb35e49c94c58de04cba8bc Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 01:51:30 +0100 Subject: [PATCH 112/142] fixed debug output --- src/affine_relation.cpp | 20 +++++++++++++++++--- src/affine_relation.h | 2 +- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 32aa829..a0b9aae 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -317,10 +317,20 @@ unordered_map AffineRelation::createVariableIndexMap(Function // MARK: - debug output +unordered_map reverseMap(unordered_map map) { + unordered_map reversed; + for (auto [key, value]: map) { + reversed[value] = key; + } + return reversed; +} + void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { + auto reversed = reverseMap(index); for (auto m: basis) { out << llvm::left_justify("", 8); - for (auto [val, idx]: index) { + for (int i = 1; i <= getNumberOfVariables(); i++) { + auto val = reversed[i]; if (val->hasName()) { out << llvm::left_justify(val->getName(), 6); } else { @@ -332,9 +342,11 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i } void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { + auto reversed = reverseMap(index); for (auto m: basis) { out << llvm::left_justify("", 8); - for (auto [val, idx]: index) { + for (int i = 1; i <= getNumberOfVariables(); i++) { + auto val = reversed[i]; if (val->hasName()) { out << llvm::left_justify(val->getName(), 6); } else { @@ -346,9 +358,11 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i } void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { + auto reversed = reverseMap(index); for (auto m: basis) { dbgs(3) << llvm::left_justify("", 8); - for (auto [val, idx]: index) { + for (int i = 1; i <= getNumberOfVariables(); i++) { + auto val = reversed[i]; if (val->hasName()) { dbgs(3) << llvm::left_justify(val->getName(), 6); } else { diff --git a/src/affine_relation.h b/src/affine_relation.h index 0e0f0ea..e37b7dc 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -12,7 +12,7 @@ namespace pcpo { class AffineRelation { private: /// Only valid when `createVariableIndexMap` has been generated. - int getNumberOfVariables() { return index.size(); }; + int getNumberOfVariables() const { return index.size(); }; std::unordered_map createVariableIndexMap(llvm::Function const& func); public: std::unordered_map index; -- GitLab From 747c6eb2b6c4390d2918a8e3c139fc249c5e846c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 01:51:43 +0100 Subject: [PATCH 113/142] added some const --- src/affine_relation.cpp | 6 +++--- src/affine_relation.h | 4 ++-- src/matrix.h | 10 +++++----- src/normalized_conjunction.cpp | 2 +- src/normalized_conjunction.h | 2 +- 5 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index a0b9aae..3e543af 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -36,7 +36,7 @@ AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const // MARK: - AbstractState Interface -void AffineRelation::applyPHINode(BasicBlock const& bb, vector pred_values, Instruction const& phi) { +void AffineRelation::applyPHINode(BasicBlock const& bb, vector const& pred_values, Instruction const& phi) { PHINode const* phiNode = dyn_cast(&phi); int i = 0; @@ -156,7 +156,7 @@ bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) { // MARK: - Lattice Operations -bool AffineRelation::leastUpperBound(AffineRelation rhs) { +bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { assert(getNumberOfVariables() == rhs.getNumberOfVariables()); vector> before = basis; vector> vectors; @@ -317,7 +317,7 @@ unordered_map AffineRelation::createVariableIndexMap(Function // MARK: - debug output -unordered_map reverseMap(unordered_map map) { +unordered_map reverseMap(unordered_map const& map) { unordered_map reversed; for (auto [key, value]: map) { reversed[value] = key; diff --git a/src/affine_relation.h b/src/affine_relation.h index e37b7dc..f3eb6d3 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -28,7 +28,7 @@ public: explicit AffineRelation(llvm::Function const* callee_func, AffineRelation const& state, llvm::CallInst const* call); /// Handles the evaluation of merging points - void applyPHINode(llvm::BasicBlock const& bb, std::vector pred_values, llvm::Instruction const& phi); + void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, llvm::Instruction const& phi); /// Handles the evaluation of function calls /// This is the "combine" function as described in "Compiler Design: Analysis and Transformation" void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, AffineRelation const& callee_state); @@ -38,7 +38,7 @@ public: void applyDefault(llvm::Instruction const& inst); bool merge(Merge_op::Type op, AffineRelation const& other); void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) { return; }; - bool leastUpperBound(AffineRelation rhs); + bool leastUpperBound(AffineRelation const& rhs); bool checkOperandsForBottom(llvm::Instruction const& inst) { return false; } diff --git a/src/matrix.h b/src/matrix.h index 1cb3766..a8545fb 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -148,7 +148,7 @@ public: } /// Basis of the linear span of the column vectors - static Matrix span(Matrix matrix) { + static Matrix span(Matrix const& matrix) { vector> columns; int rank = matrix.getRank(); for (int col = 0; col null(Matrix matrix); + static Matrix null(Matrix const& matrix); /// Converts the matrix to a 1D Vector by stacking the column vectors std::vector toVector() const { @@ -370,14 +370,14 @@ template inline Matrix operator-(Matrix lhs, T scalar) { return lhs -= scalar; }; template -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix matrix) { +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix const& matrix) { for (int row = 0; row < matrix.getHeight(); row++) { os << "[ "; for (int column = 0; column < matrix.getWidth(); column++) { if (column == matrix.getWidth() - 1) { - os << llvm::format("%d", matrix(row,column)); + os << llvm::format("%d", matrix.value(row,column)); } else { - os << llvm::format("%-6d", matrix(row,column)); + os << llvm::format("%-6d", matrix.value(row,column)); } } os << " ]\n"; diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 5c9433e..83d64ec 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -43,7 +43,7 @@ NormalizedConjunction::NormalizedConjunction(Function const* callee_func, Normal isBottom = false; } -NormalizedConjunction::NormalizedConjunction(std::unordered_map equalaties) { +NormalizedConjunction::NormalizedConjunction(std::unordered_map const& equalaties) { this->values = equalaties; isBottom = equalaties.empty(); for (auto& [key, value]: equalaties) { diff --git a/src/normalized_conjunction.h b/src/normalized_conjunction.h index a839fb8..4d77d93 100644 --- a/src/normalized_conjunction.h +++ b/src/normalized_conjunction.h @@ -26,7 +26,7 @@ public: NormalizedConjunction() = default; NormalizedConjunction(NormalizedConjunction const& state) = default; - NormalizedConjunction(std::unordered_map equalaties); + NormalizedConjunction(std::unordered_map const& equalaties); explicit NormalizedConjunction(llvm::Function const& f); /// This constructor is used to initialize the state of a function call, to which parameters are passed. -- GitLab From 9f169433931d36d99e4f5a98d1ac768dd6a8e14a Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 16:43:00 +0100 Subject: [PATCH 114/142] better debug log --- src/affine_relation.cpp | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 3e543af..fe3bceb 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -327,6 +327,10 @@ unordered_map reverseMap(unordered_map cons void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { auto reversed = reverseMap(index); + if (basis.empty()) { + dbgs(3) << "[]"; + return; + } for (auto m: basis) { out << llvm::left_justify("", 8); for (int i = 1; i <= getNumberOfVariables(); i++) { @@ -343,6 +347,10 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { auto reversed = reverseMap(index); + if (basis.empty()) { + dbgs(3) << "[]"; + return; + } for (auto m: basis) { out << llvm::left_justify("", 8); for (int i = 1; i <= getNumberOfVariables(); i++) { @@ -359,6 +367,10 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { auto reversed = reverseMap(index); + if (basis.empty()) { + dbgs(3) << "[]"; + return; + } for (auto m: basis) { dbgs(3) << llvm::left_justify("", 8); for (int i = 1; i <= getNumberOfVariables(); i++) { -- GitLab From 11e05c3ed263ccee454e12d04236c84aa4a0af77 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 16:43:46 +0100 Subject: [PATCH 115/142] better assertions and fixed some initalizers --- src/matrix.h | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index a8545fb..a42d82f 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -55,7 +55,8 @@ public: /// Creates a matrix from a 2D vector /// @param vectors 2D vector containing columns with rows Matrix(vector> const &vectors) { - this->width = vectors.front().size(); + assert(all_of(vectors.begin(), vectors.end(), [&vectors](vector vec){ return vec.size() == vectors.front().size(); })); + this->width = vectors.empty() ? 0 : vectors.front().size(); this->height = vectors.size(); this->vectors = vectors; }; @@ -64,6 +65,8 @@ public: /// @param vector the vector Matrix(vector const &vector) { std::vector> vectors = {vector}; + this->width = vector.size(); + this->height = vector.empty() ? 0 : 1; this->vectors = vectors; }; @@ -151,7 +154,7 @@ public: static Matrix span(Matrix const& matrix) { vector> columns; int rank = matrix.getRank(); - for (int col = 0; col reshape(int rows, int columns) const { - return Matrix(vectors.front(), rows, columns); + assert(rows > 0 && columns > 0); + Matrix t = transpose(); + return Matrix(t.vectors.front(), rows, columns); }; vector> reshapeColumns(int height, int width) const { -- GitLab From e4587a583d796cb3200ebcb8c9f07fb6f53edbd5 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 19:08:49 +0100 Subject: [PATCH 116/142] improved debug log --- src/affine_relation.cpp | 29 ++++++++++++++++++++++++++--- src/affine_relation.h | 4 ++-- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index fe3bceb..25421f8 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -1,4 +1,6 @@ #include "affine_relation.h" +#include "global.h" + #include "llvm/IR/CFG.h" #include @@ -328,7 +330,7 @@ unordered_map reverseMap(unordered_map cons void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { auto reversed = reverseMap(index); if (basis.empty()) { - dbgs(3) << "[]"; + dbgs(3) << "[]\n"; return; } for (auto m: basis) { @@ -348,7 +350,7 @@ void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { auto reversed = reverseMap(index); if (basis.empty()) { - dbgs(3) << "[]"; + dbgs(3) << "[]\n"; return; } for (auto m: basis) { @@ -368,7 +370,7 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { auto reversed = reverseMap(index); if (basis.empty()) { - dbgs(3) << "[]"; + dbgs(3) << "[]\n"; return; } for (auto m: basis) { @@ -385,4 +387,25 @@ void AffineRelation::debug_output(Instruction const& inst, Matrix operands) } } + +raw_ostream& operator<<(raw_ostream& os, AffineRelation const& relation) { + auto reversed = reverseMap(relation.index); + if (relation.basis.empty()) { + return os << "[]\n"; + } + for (auto m: relation.basis) { + os << left_justify("", 8); + for (int i = 1; i <= int(relation.index.size()); i++) { + auto val = reversed[i]; + if (val->hasName()) { + os << left_justify(val->getName(), 6); + } else { + os << left_justify("<>", 6); + } + } + os << "\n" << m << "\n"; + } + return os; +} + } diff --git a/src/affine_relation.h b/src/affine_relation.h index f3eb6d3..aa2cf39 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -42,8 +42,6 @@ public: bool checkOperandsForBottom(llvm::Instruction const& inst) { return false; } -// Matrix AffineRelation::getAbstractValue(llvm::Value const& value) const; - void printIncoming(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; @@ -64,5 +62,7 @@ protected: Matrix createTransformationMatrix(llvm::Instruction const& inst); }; +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, AffineRelation const& relation); + } -- GitLab From ee5705ec07e1a6c5959a035ab54cd3d34aacef82 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 22:00:47 +0100 Subject: [PATCH 117/142] Added tests for double --- test/matrix_test.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/test/matrix_test.cpp b/test/matrix_test.cpp index c41bd6f..6184bb4 100644 --- a/test/matrix_test.cpp +++ b/test/matrix_test.cpp @@ -312,7 +312,7 @@ bool MatrixTest::runTestSpan1() { }; auto matrix = Matrix(a); - auto actual = Matrix::span(matrix); + auto actual = Matrix::span(matrix); result = actual == Matrix(expected); @@ -334,6 +334,16 @@ int main() { && MatrixTest::runTestRank2() && MatrixTest::runTestRank3() && MatrixTest::runTestSpan1() + && MatrixTest::runTestMul2() + && MatrixTest::runTestTranspose1() + && MatrixTest::runTestTranspose2() + && MatrixTest::runTestEchelon1() + && MatrixTest::runTestEchelon2() + && MatrixTest::runTestEchelon3() + && MatrixTest::runTestRank1() + && MatrixTest::runTestRank2() + && MatrixTest::runTestRank3() + && MatrixTest::runTestSpan1() ); }; -- GitLab From 5fa47d72622bd4e9580df620f013ba920efee2e1 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 22:01:45 +0100 Subject: [PATCH 118/142] Improved printing of doubles --- src/matrix.h | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index a42d82f..d4fbb30 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -3,6 +3,7 @@ #include "global.h" #include +#include using namespace std; @@ -379,10 +380,18 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix const& matrix) { for (int row = 0; row < matrix.getHeight(); row++) { os << "[ "; for (int column = 0; column < matrix.getWidth(); column++) { - if (column == matrix.getWidth() - 1) { - os << llvm::format("%d", matrix.value(row,column)); + if constexpr (std::is_floating_point_v) { + if (column == matrix.getWidth() - 1) { + os << llvm::format("%f", matrix.value(row,column)); + } else { + os << llvm::format("%-6.2f", matrix.value(row,column)); + } } else { - os << llvm::format("%-6d", matrix.value(row,column)); + if (column == matrix.getWidth() - 1) { + os << llvm::format("%d", matrix.value(row,column)); + } else { + os << llvm::format("%-6d", matrix.value(row,column)); + } } } os << " ]\n"; -- GitLab From c6f85c076ab84ccafc69b9d21c76ddf4d64630cf Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 22:02:45 +0100 Subject: [PATCH 119/142] Fixed some bugs in span and rank and added a print method --- src/matrix.h | 24 +++++++++++++++++------- test/matrix_test.cpp | 6 +++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index d4fbb30..c743aff 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -111,7 +111,7 @@ public: }; /// Transforms the matrix to reduced row echelon form - Matrix echelon() const { + Matrix echelonForm() const { Matrix result = Matrix(*this); int pivot = 0; for (int row = 0; row < height; row++) { @@ -136,7 +136,7 @@ public: /// The rank of the matrix int getRank() const { - Matrix e = echelon(); + Matrix e = echelonForm(); int rank = 0; for (int row = 0; row < height; row++) { for (int column = 0; column < width; column++) { @@ -153,12 +153,13 @@ public: /// Basis of the linear span of the column vectors static Matrix span(Matrix const& matrix) { - vector> columns; - int rank = matrix.getRank(); - for (int col = 0; col < rank; col++) { - columns.push_back(matrix.column(col)); + vector> rows; + Matrix te = matrix.transpose().echelonForm(); + int rank = te.getRank(); + for (int row = 0; row < rank; row++) { + rows.push_back(te.row(row)); } - return Matrix(columns).transpose(); + return Matrix(rows).transpose(); } /// Computes the null space for the column vectors @@ -215,6 +216,11 @@ public: return vectors[i]; }; + vector row(int i) const { + assert(i < getHeight()); + return vectors[i]; + }; + /// Returns the column at index i. The returned column cannot be modified. /// @param i Index of the column to return vector column(int i) const { @@ -306,6 +312,10 @@ public: return rhs.vectors == rhs.vectors && width == rhs.width && height == rhs.height; }; + void print() const { + dbgs(4) << *this; + } + protected: // MARK: - Echelon helpers diff --git a/test/matrix_test.cpp b/test/matrix_test.cpp index 6184bb4..360bfb6 100644 --- a/test/matrix_test.cpp +++ b/test/matrix_test.cpp @@ -165,7 +165,7 @@ bool MatrixTest::runTestEchelon1() { }; auto matrix = Matrix(a); - auto actual = matrix.echelon(); + auto actual = matrix.echelonForm(); auto x = Matrix(expected); result = actual == x; @@ -192,7 +192,7 @@ bool MatrixTest::runTestEchelon2() { }; auto matrix = Matrix(a); - auto actual = matrix.echelon(); + auto actual = matrix.echelonForm(); auto x = Matrix(expected); result = actual == x; @@ -219,7 +219,7 @@ bool MatrixTest::runTestEchelon3() { }; auto matrix = Matrix(a); - auto actual = matrix.echelon(); + auto actual = matrix.echelonForm(); auto x = Matrix(expected); result = actual == x; -- GitLab From 6fe9bbb6f0d15257c98f1771dc8186587d3d3dad Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sun, 15 Mar 2020 22:20:45 +0100 Subject: [PATCH 120/142] Added support for matrices with different value types --- src/affine_relation.cpp | 39 +++++++++++++++++++++++---------------- src/affine_relation.h | 13 ++++++++----- src/matrix.h | 4 ++-- 3 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 25421f8..58695b2 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -14,7 +14,7 @@ namespace pcpo { AffineRelation::AffineRelation(Function const& func) { index = createVariableIndexMap(func); - basis = {Matrix(getNumberOfVariables() + 1)}; + basis = {Matrix(getNumberOfVariables() + 1)}; isBottom = basis.empty(); } @@ -160,19 +160,19 @@ bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) { bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { assert(getNumberOfVariables() == rhs.getNumberOfVariables()); - vector> before = basis; - vector> vectors; + vector> before = basis; + vector> vectors; vectors.reserve(basis.size() + rhs.basis.size()); - for (Matrix m: basis) { + for (Matrix m: basis) { vectors.push_back(m.toVector()); } - for (Matrix m: rhs.basis) { + for (Matrix 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 combined = Matrix(vectors).transpose(); - Matrix result = Matrix::span(combined); + Matrix combined = Matrix(vectors).transpose(); + Matrix result = Matrix::span(combined); basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getHeight()); // FIXME: figure out a better way to detect changes @@ -181,19 +181,26 @@ bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { // MARK: - Assignments -void AffineRelation::affineAssignment(Value const* xi, unordered_map relations, int constant) { - Matrix Wr = Matrix(getNumberOfVariables() + 1); +void AffineRelation::affineAssignment(Value const* xi, unordered_map relations, T constant) { + Matrix Wr = Matrix(getNumberOfVariables() + 1); + Wr(index.at(xi),index.at(xi)) = 0; Wr(0,index.at(xi)) = constant; + for (auto [variable, factor]: relations) { Wr(index.at(variable),index.at(xi)) = factor; } - for (Matrix& matrix: basis) { - matrix *= Matrix::span(Wr); + // FIXME: this seems quite inefficient + Matrix vector = Matrix(Wr.toVector()).transpose(); + Matrix vectorSpan = Matrix::span(vector); + Wr = vectorSpan.reshape(Wr.getHeight(), Wr.getWidth()); + + for (Matrix& matrix: basis) { + matrix *= Wr; } } -void AffineRelation::affineAssignment(Value const* xi, int64_t a, Value const* xj, int64_t b) { +void AffineRelation::affineAssignment(Value const* xi, T a, Value const* xj, T b) { if (xj == nullptr) { affineAssignment(xi, {}, b); } else { @@ -202,8 +209,8 @@ void AffineRelation::affineAssignment(Value const* xi, int64_t a, Value const* x } void AffineRelation::nonDeterminsticAssignment(Value const* xi) { - Matrix T0 = Matrix(getNumberOfVariables() + 1); - Matrix T1 = Matrix(getNumberOfVariables() + 1); + Matrix T0 = Matrix(getNumberOfVariables() + 1); + Matrix T1 = Matrix(getNumberOfVariables() + 1); T0(index.at(xi),index.at(xi)) = 0; T0(0,index.at(xi)) = 0; @@ -211,7 +218,7 @@ void AffineRelation::nonDeterminsticAssignment(Value const* xi) { T1(index.at(xi),index.at(xi)) = 0; T1(0,index.at(xi)) = 1; - for (Matrix matrix: basis) { + for (Matrix matrix: basis) { // FIXME: span({T0,T1}) or even leastUpperBound matrix *= T0; matrix *= T1; @@ -367,7 +374,7 @@ void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int i } } -void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { +void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { auto reversed = reverseMap(index); if (basis.empty()) { dbgs(3) << "[]\n"; diff --git a/src/affine_relation.h b/src/affine_relation.h index aa2cf39..76b8135 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -10,13 +10,16 @@ namespace pcpo { class AffineRelation { + + using T = int; + private: /// Only valid when `createVariableIndexMap` has been generated. int getNumberOfVariables() const { return index.size(); }; std::unordered_map createVariableIndexMap(llvm::Function const& func); public: std::unordered_map index; - std::vector> basis; + std::vector> basis; bool isBottom = true; AffineRelation() = default; @@ -46,8 +49,8 @@ public: void printOutgoing(llvm::BasicBlock const& bb, llvm::raw_ostream& out, int indentation) const; // 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 relations, int constant); + void affineAssignment(llvm::Value const* xi, T a, llvm::Value const* xj, T b); + void affineAssignment(llvm::Value const* xi, std::unordered_map relations, T constant); void nonDeterminsticAssignment(llvm::Value const* xi); protected: @@ -57,9 +60,9 @@ protected: void Mul(llvm::Instruction const& inst); /// Used for debug output - void debug_output(llvm::Instruction const& inst, Matrix operands); + void debug_output(llvm::Instruction const& inst, Matrix operands); - Matrix createTransformationMatrix(llvm::Instruction const& inst); + Matrix createTransformationMatrix(llvm::Instruction const& inst); }; llvm::raw_ostream& operator<<(llvm::raw_ostream& os, AffineRelation const& relation); diff --git a/src/matrix.h b/src/matrix.h index c743aff..98cc26e 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -392,9 +392,9 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix const& matrix) { for (int column = 0; column < matrix.getWidth(); column++) { if constexpr (std::is_floating_point_v) { if (column == matrix.getWidth() - 1) { - os << llvm::format("%f", matrix.value(row,column)); + os << llvm::format("%.f", matrix.value(row,column)); } else { - os << llvm::format("%-6.2f", matrix.value(row,column)); + os << llvm::format("%-6.f", matrix.value(row,column)); } } else { if (column == matrix.getWidth() - 1) { -- GitLab From d4b240818dddec3cf7da3c7a3583c91507a4a8b1 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 02:53:05 +0100 Subject: [PATCH 121/142] Fixed some bugs where int was not replaced by T (leading to int /float conversion errors). Improved performance of span. Fixed layout of returned vector from toVector() --- src/matrix.h | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index 98cc26e..0d7f7e6 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -156,6 +156,7 @@ public: vector> rows; Matrix te = matrix.transpose().echelonForm(); int rank = te.getRank(); + rows.reserve(rank); for (int row = 0; row < rank; row++) { rows.push_back(te.row(row)); } @@ -169,8 +170,10 @@ public: std::vector toVector() const { vector result; result.reserve(getWidth() * getHeight()); - for (vector vector: vectors) { - result.insert(result.end(), vector.begin(), vector.end()); + for (int column = 0; column < getWidth(); column++) { + for (int row = 0; row < getHeight(); row++) { + result.push_back(value(row,column)); + } } return result; } @@ -328,7 +331,7 @@ protected: /// Divides a row by a constant /// @param row index of the row to divide /// @param quotient quotient to divide the row by - void divide_row(int row, int quotient) { + void divide_row(int row, T quotient) { for (int column = 0; column < width; column++) { value(row,column) /= quotient; } @@ -338,7 +341,7 @@ protected: /// @param a Row to add a multiple of b to /// @param b Row to be added to row a /// @param factor Factor to multiply row b with when adding it to row a - void add_multiple_row(int a, int b, int factor) { + void add_multiple_row(int a, int b, T factor) { for (int column = 0; column < width; column++) { value(a,column) += value(b,column) * factor; } -- GitLab From 7eb7a208db64bac27675c8d06e5ddd26ea2139fc Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 02:54:22 +0100 Subject: [PATCH 122/142] Added tests for affine relation --- CMakeLists.txt | 17 +++++++++++++++++ test/affine_relation_test.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 test/affine_relation_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 2576898..83dc0cb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -160,6 +160,19 @@ target_link_libraries(normalized_conjunction_test ${LLVM_AVAILABLE_LIBS} ) +add_llvm_executable(affine_relation_test + test/affine_relation_test.cpp + ${PAIN_HEADERS} + ${PAIN_SOURCES} + DEPENDS + irgen + intrinsics_gen +) + +target_link_libraries(affine_relation_test + ${LLVM_AVAILABLE_LIBS} +) + add_llvm_executable(matrix_test test/matrix_test.cpp ${PAIN_HEADERS} @@ -184,6 +197,10 @@ add_test(NAME normalizedConjunctionTest COMMAND normalized_conjunction_test ) +add_test(NAME affineRelationTest + COMMAND affine_relation_test +) + add_test(NAME matrixTest COMMAND matrix_test ) diff --git a/test/affine_relation_test.cpp b/test/affine_relation_test.cpp new file mode 100644 index 0000000..a74dd5d --- /dev/null +++ b/test/affine_relation_test.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include +#include + +#include "../src/affine_relation.h" + +using namespace std; +using namespace pcpo; + +template +class AffineRelationTest: AffineRelation { + +public: + +}; + + +int main() { + return true; +}; + + -- GitLab From 68f025d96a10d6c5111368bdb68aa8e6113ddc2c Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 16:03:00 +0100 Subject: [PATCH 123/142] added some tests --- test/affine_relation_test.cpp | 73 +++++++++++++++++++++++++++++++++-- 1 file changed, 69 insertions(+), 4 deletions(-) diff --git a/test/affine_relation_test.cpp b/test/affine_relation_test.cpp index a74dd5d..8867f00 100644 --- a/test/affine_relation_test.cpp +++ b/test/affine_relation_test.cpp @@ -9,16 +9,81 @@ using namespace std; using namespace pcpo; -template -class AffineRelationTest: AffineRelation { +class AffineRelationTest: AffineRelation { public: - + static bool runTestLeastUpperBound1(); + static bool runTestLeastUpperBound2(); }; +bool AffineRelationTest::runTestLeastUpperBound1() { + std::cout << "Testing least upper bound 1: "; + bool result = false; + + AffineRelation r1 = AffineRelation(); + r1.isBottom = false; + r1.basis = {Matrix(4)}; + + AffineRelation r2 = AffineRelation(); + r2.isBottom = false; + r2.basis = {Matrix(4)}; + + AffineRelation expected = AffineRelation(); + expected.basis = {Matrix(4)}; + + + auto actual = r1.leastUpperBound(r2); + + result = r1.basis == expected.basis && !actual && !r1.isBottom; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool AffineRelationTest::runTestLeastUpperBound2() { + std::cout << "Testing least upper bound 2: "; + bool result = false; + + AffineRelation r1 = AffineRelation(); + r1.isBottom = false; + Matrix b1 = Matrix(4); + b1(0,1) = 1; + b1(2,1) = 1; + r1.basis = {b1}; + + AffineRelation r2 = AffineRelation(); + r2.isBottom = false; + Matrix b2 = Matrix(4); + b2(0,3) = 1; + r2.basis = {b2}; + + AffineRelation expected = AffineRelation(); + Matrix e1 = Matrix(4); + e1(0,3) = 1; + Matrix e2 = Matrix(4); + e2(0,0) = 0; + e2(1,1) = 0; + e2(2,2) = 0; + e2(3,3) = 0; + e2(0,1) = 1; + e2(2,1) = 1; + e2(0,3) = -1; + expected.basis = {e1, e2}; + + + auto actual = r1.leastUpperBound(r2); + + result = r1.basis == expected.basis && actual && !r1.isBottom; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + int main() { - return true; + return !(AffineRelationTest::runTestLeastUpperBound1() + && AffineRelationTest::runTestLeastUpperBound2() + ); }; -- GitLab From 61b2e8eb30d9513bfce28bccfffb76995035c5cf Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 16:03:11 +0100 Subject: [PATCH 124/142] patch --- src/matrix.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/matrix.h b/src/matrix.h index 0d7f7e6..c4361d3 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -183,8 +183,9 @@ public: /// @param columns number of columns Matrix reshape(int rows, int columns) const { assert(rows > 0 && columns > 0); + // FIXME: Performance Matrix t = transpose(); - return Matrix(t.vectors.front(), rows, columns); + return Matrix(t.vectors.front(), rows, columns).transpose(); }; vector> reshapeColumns(int height, int width) const { -- GitLab From 910d9befc7ccc0e2b030d3d3aec1a2acd51351dc Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 16:03:26 +0100 Subject: [PATCH 125/142] made T public --- src/affine_relation.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/affine_relation.h b/src/affine_relation.h index 76b8135..d5fe351 100644 --- a/src/affine_relation.h +++ b/src/affine_relation.h @@ -10,14 +10,14 @@ namespace pcpo { class AffineRelation { - - using T = int; - private: /// Only valid when `createVariableIndexMap` has been generated. int getNumberOfVariables() const { return index.size(); }; std::unordered_map createVariableIndexMap(llvm::Function const& func); public: + /// Type used for Matrix values. + using T = double; + std::unordered_map index; std::vector> basis; bool isBottom = true; -- GitLab From fd51877702e79b2876e8c38afc0b228b089daa42 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 18:03:42 +0100 Subject: [PATCH 126/142] fixed printing of floats --- src/matrix.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/matrix.h b/src/matrix.h index c4361d3..d41525b 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -396,9 +396,9 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix const& matrix) { for (int column = 0; column < matrix.getWidth(); column++) { if constexpr (std::is_floating_point_v) { if (column == matrix.getWidth() - 1) { - os << llvm::format("%.f", matrix.value(row,column)); + os << llvm::format("%g", matrix.value(row,column)); } else { - os << llvm::format("%-6.f", matrix.value(row,column)); + os << llvm::format("%-6g", matrix.value(row,column)); } } else { if (column == matrix.getWidth() - 1) { -- GitLab From b061ce162a9fbbef2833bcc5a091e852e1e5494b Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 18:03:51 +0100 Subject: [PATCH 127/142] fixed reshape --- src/matrix.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/matrix.h b/src/matrix.h index d41525b..ab3c5b9 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -191,7 +191,7 @@ public: vector> reshapeColumns(int height, int width) const { vector> result; for (int c = 0; c < getWidth(); c++) { - result.push_back(Matrix(column(c), height, width)); + result.push_back(Matrix(column(c), height, width).transpose()); } return result; } -- GitLab From c44347e6d5a2cbfa5b52362023679bbddf839d07 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 16 Mar 2020 19:04:17 +0100 Subject: [PATCH 128/142] comments --- src/affine_relation.cpp | 74 +++------------------------------- src/normalized_conjunction.cpp | 6 --- 2 files changed, 6 insertions(+), 74 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 58695b2..8e6f3ce 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -45,7 +45,7 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector c for (BasicBlock const* pred_bb: llvm::predecessors(&bb)) { auto& incoming_value = *phiNode->getIncomingValueForBlock(pred_bb); auto& incoming_state = pred_values[i]; - + // Predecessor states should have been merged before. This is just bookkeeping. if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { AffineRelation acc = *this; acc.affineAssignment(&phi, 1, nullptr, c->getSExtValue()); @@ -63,12 +63,8 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector c } void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* end_block, AffineRelation const& callee_state) { -// std::vector operands; - -// Keep the debug output happy -// for (llvm::Value const* value : inst.operand_values()) { -// operands.push_back(getAbstractValue(*value)); -// } + // State changes from function call were not merged with the oredecessors. + // So we have to do more than just bookkeeping. //iterate through all instructions of it till we find a return statement for (Instruction const& iter_inst: *end_block) { @@ -85,7 +81,6 @@ void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* en } } } -// debug_output(inst, operands); } void AffineRelation::applyReturnInst(Instruction const& inst) { @@ -100,8 +95,6 @@ void AffineRelation::applyReturnInst(Instruction const& inst) { } void AffineRelation::applyDefault(Instruction const& inst) { -// std::vector operands; - if (inst.getNumOperands() != 2) return nonDeterminsticAssignment(&inst); // We only deal with integer types @@ -118,11 +111,6 @@ void AffineRelation::applyDefault(Instruction const& inst) { return nonDeterminsticAssignment(&inst); } - -// for (llvm::Value const* value: inst.operand_values()) { -// operands.push_back(getAbstractValue(*value)); -// } - switch (inst.getOpcode()) { case Instruction::Add: return Add(inst); @@ -135,8 +123,6 @@ void AffineRelation::applyDefault(Instruction const& inst) { default: return nonDeterminsticAssignment(&inst); } - -// debug_output(inst, operands); } bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) { @@ -335,63 +321,15 @@ unordered_map reverseMap(unordered_map cons } void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { - auto reversed = reverseMap(index); - if (basis.empty()) { - dbgs(3) << "[]\n"; - return; - } - for (auto m: basis) { - out << llvm::left_justify("", 8); - for (int i = 1; i <= getNumberOfVariables(); i++) { - auto val = reversed[i]; - if (val->hasName()) { - out << llvm::left_justify(val->getName(), 6); - } else { - out << llvm::left_justify("<>", 6); - } - } - out << "\n" << m << "\n"; - } + out << *this; } void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { - auto reversed = reverseMap(index); - if (basis.empty()) { - dbgs(3) << "[]\n"; - return; - } - for (auto m: basis) { - out << llvm::left_justify("", 8); - for (int i = 1; i <= getNumberOfVariables(); i++) { - auto val = reversed[i]; - if (val->hasName()) { - out << llvm::left_justify(val->getName(), 6); - } else { - out << llvm::left_justify("<>", 6); - } - } - out << "\n" << m << "\n"; - } + out << *this; } void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { - auto reversed = reverseMap(index); - if (basis.empty()) { - dbgs(3) << "[]\n"; - return; - } - for (auto m: basis) { - dbgs(3) << llvm::left_justify("", 8); - for (int i = 1; i <= getNumberOfVariables(); i++) { - auto val = reversed[i]; - if (val->hasName()) { - dbgs(3) << llvm::left_justify(val->getName(), 6); - } else { - dbgs(3) << llvm::left_justify("<>", 6); - } - } - dbgs(3) << "\n" << m << "\n"; - } + dbgs(3) << *this; } diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index 83d64ec..fe9e77f 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -86,11 +86,6 @@ void NormalizedConjunction::applyPHINode(BasicBlock const& bb, std::vector operands; -// // Keep the debug output happy -// for (llvm::Value const* value : inst.operand_values()) { -// operands.push_back(EqualityFoo(value)); -// } - //iterate through all instructions of it till we find a return statement for (auto& iter_inst: *end_block) { if (ReturnInst const* ret_inst = dyn_cast(&iter_inst)) { @@ -106,7 +101,6 @@ void NormalizedConjunction::applyCallInst(Instruction const& inst, BasicBlock co } } } -// debug_output(inst, operands); } void NormalizedConjunction::applyReturnInst(Instruction const& inst) { -- GitLab From 065e587e9e0ef9359e8329c7acb69b557fef2c6f Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 27 Mar 2020 00:19:53 +0100 Subject: [PATCH 129/142] fixed some small bugs --- src/affine_relation.cpp | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 8e6f3ce..e7a1957 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -195,6 +195,8 @@ void AffineRelation::affineAssignment(Value const* xi, T a, Value const* xj, T b } void AffineRelation::nonDeterminsticAssignment(Value const* xi) { + if (index.count(xi) == 0) return; + Matrix T0 = Matrix(getNumberOfVariables() + 1); Matrix T1 = Matrix(getNumberOfVariables() + 1); @@ -281,7 +283,7 @@ void AffineRelation::Mul(Instruction const& inst) { // MARK: - Helpers -unordered_map createVariableIndexMap_impl(Function const& func, int& count, set visited_funcs) { +unordered_map createVariableIndexMap_impl(Function const& func, int& count, set& visited_funcs) { unordered_map map; visited_funcs.insert(&func); for (BasicBlock const& basic_block: func) { @@ -296,8 +298,14 @@ unordered_map createVariableIndexMap_impl(Function const& fun continue; } if (visited_funcs.count(callee_func) == 0) { + for (Argument const& arg: callee_func->args()) { + if (isa(arg.getType())) { + count++; + map[&arg] = count; + } + } unordered_map callee_map = createVariableIndexMap_impl(*callee_func, count, visited_funcs); - map.insert(callee_map.begin(), callee_map.end()); + map.merge(callee_map); } } } @@ -307,7 +315,8 @@ unordered_map createVariableIndexMap_impl(Function const& fun unordered_map AffineRelation::createVariableIndexMap(Function const& func) { int count = 0; - return createVariableIndexMap_impl(func, count, {}); + set visited_funcs = {}; + return createVariableIndexMap_impl(func, count, visited_funcs); } // MARK: - debug output @@ -341,7 +350,7 @@ raw_ostream& operator<<(raw_ostream& os, AffineRelation const& relation) { for (auto m: relation.basis) { os << left_justify("", 8); for (int i = 1; i <= int(relation.index.size()); i++) { - auto val = reversed[i]; + auto val = reversed.at(i); if (val->hasName()) { os << left_justify(val->getName(), 6); } else { -- GitLab From fc9dfc4b5432964d908511081c67a2768ab6f179 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 27 Mar 2020 01:13:15 +0100 Subject: [PATCH 130/142] fixed non-deterministic assignment --- src/affine_relation.cpp | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index e7a1957..d0eddd3 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -31,6 +31,8 @@ AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const } else { affineAssignment(&arg, 1, value, 0); } + } else { + nonDeterminsticAssignment(&arg); } } isBottom = basis.empty(); @@ -77,7 +79,6 @@ void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* en affineAssignment(&inst, 1, ret_val, 0); } else { dbgs(4) << " Return not evaluated, setting to bottom\n"; - // TODO: What should we do here } } } @@ -160,7 +161,7 @@ bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { Matrix combined = Matrix(vectors).transpose(); Matrix result = Matrix::span(combined); - basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getHeight()); + basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getWidth()); // FIXME: figure out a better way to detect changes return before != basis; } @@ -206,10 +207,19 @@ void AffineRelation::nonDeterminsticAssignment(Value const* xi) { T1(index.at(xi),index.at(xi)) = 0; T1(0,index.at(xi)) = 1; - for (Matrix matrix: basis) { - // FIXME: span({T0,T1}) or even leastUpperBound - matrix *= T0; - matrix *= T1; + vector> assignment_vectors; + assignment_vectors.push_back(T0.toVector()); + assignment_vectors.push_back(T1.toVector()); + + Matrix combined = Matrix(assignment_vectors).transpose(); + Matrix result = Matrix::span(combined); + + vector> span = result.reshapeColumns(T0.getHeight(), T0.getWidth()); + + for (Matrix& matrix_state: basis) { + for (Matrix const& matrix_assignment: span) { + matrix_state *= matrix_assignment; + } } } -- GitLab From 86534c591e34d2ea7c504724b5bf08eb94da3549 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 27 Mar 2020 02:27:39 +0100 Subject: [PATCH 131/142] no longer using namespace std; --- src/affine_relation.cpp | 4 +++- src/fixpoint.cpp | 6 ++++-- src/matrix.h | 40 +++++++++++++++++++--------------------- 3 files changed, 26 insertions(+), 24 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index d0eddd3..7eb5e14 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -6,7 +6,9 @@ #include using namespace llvm; -using namespace std; +using std::vector; +using std::unordered_map; +using std::set; namespace pcpo { diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 7126df2..5150ac0 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -21,7 +21,9 @@ namespace pcpo { using namespace llvm; -using namespace std; +using std::vector; +using std::pair; +using std::unordered_map; static llvm::RegisterPass Y("painpass", "AbstractInterpretation Pass"); @@ -271,7 +273,7 @@ void executeFixpointAlgorithm(Module const& M) { } //Getting the last block - BasicBlock const* end_block = &*prev(callee_func->end()); + BasicBlock const* end_block = &*std::prev(callee_func->end()); NodeKey end_element = {new_callstring, end_block}; state_new.applyCallInst(inst, end_block, nodes[end_element].state); diff --git a/src/matrix.h b/src/matrix.h index ab3c5b9..5866c2b 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -5,14 +5,12 @@ #include #include -using namespace std; - namespace pcpo { /// Matrix. Row and column are indexed beginning at 0 template class Matrix { protected: - vector> vectors; + std::vector> vectors; int width; int height; @@ -27,7 +25,7 @@ public: this->height = height; this->vectors.reserve(width); for (int i = 0; i < height; i++) { - vector vector(width,value); + std::vector vector(width,value); vectors.push_back(vector); } }; @@ -47,7 +45,7 @@ public: this->height = eye; this->vectors.reserve(width); for (int i = 0; i < height; i++) { - vector vector(width,0); + std::vector vector(width,0); vector[i] = 1; vectors.push_back(vector); } @@ -55,8 +53,8 @@ public: /// Creates a matrix from a 2D vector /// @param vectors 2D vector containing columns with rows - Matrix(vector> const &vectors) { - assert(all_of(vectors.begin(), vectors.end(), [&vectors](vector vec){ return vec.size() == vectors.front().size(); })); + Matrix(std::vector> const &vectors) { + assert(all_of(vectors.begin(), vectors.end(), [&vectors](std::vector vec){ return vec.size() == vectors.front().size(); })); this->width = vectors.empty() ? 0 : vectors.front().size(); this->height = vectors.size(); this->vectors = vectors; @@ -64,19 +62,19 @@ public: /// Creates a vector from a std::vector /// @param vector the vector - Matrix(vector const &vector) { + Matrix(std::vector const &vector) { std::vector> vectors = {vector}; this->width = vector.size(); this->height = vector.empty() ? 0 : 1; this->vectors = vectors; }; - Matrix(vector const &values, int rows, int columns) { + Matrix(std::vector const &values, int rows, int columns) { assert(int(values.size()) == rows * columns); - vector> result; + std::vector> result; result.reserve(rows); for (int row = 0; row < rows; row++) { - vector rowVector; + std::vector rowVector; rowVector.reserve(columns); for (int column = 0; column < columns; column++) { rowVector.push_back(values[row * rows + column]); @@ -153,7 +151,7 @@ public: /// Basis of the linear span of the column vectors static Matrix span(Matrix const& matrix) { - vector> rows; + std::vector> rows; Matrix te = matrix.transpose().echelonForm(); int rank = te.getRank(); rows.reserve(rank); @@ -168,7 +166,7 @@ public: /// Converts the matrix to a 1D Vector by stacking the column vectors std::vector toVector() const { - vector result; + std::vector result; result.reserve(getWidth() * getHeight()); for (int column = 0; column < getWidth(); column++) { for (int row = 0; row < getHeight(); row++) { @@ -188,8 +186,8 @@ public: return Matrix(t.vectors.front(), rows, columns).transpose(); }; - vector> reshapeColumns(int height, int width) const { - vector> result; + std::vector> reshapeColumns(int height, int width) const { + std::vector> result; for (int c = 0; c < getWidth(); c++) { result.push_back(Matrix(column(c), height, width).transpose()); } @@ -215,29 +213,29 @@ public: /// Returns a vector with the elements of the row at index i. The returned row can be modified. /// @param i Index of the row to return. - vector& row(int i) { + std::vector& row(int i) { assert(i < getHeight()); return vectors[i]; }; - vector row(int i) const { + std::vector row(int i) const { assert(i < getHeight()); return vectors[i]; }; /// Returns the column at index i. The returned column cannot be modified. /// @param i Index of the column to return - vector column(int i) const { + std::vector column(int i) const { assert(i < getWidth()); - vector row; + std::vector row; row.reserve(width); - for (vector const& x : vectors) { + for (std::vector const& x : vectors) { row.push_back(x[i]); } return row; } - void setColumn(vector const& vector, int column) { + void setColumn(std::vector const& vector, int column) { assert(int(vector.size()) == height); for (int row = 0; row < height; row++) { value(row,column) = vector[row]; -- GitLab From 64a5f548fe442dffd1976a28ae02ab472b398298 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 28 Mar 2020 00:48:30 +0100 Subject: [PATCH 132/142] added nondeterministic assignment to applyReturnInst --- src/affine_relation.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 7eb5e14..50cd6ba 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -94,6 +94,8 @@ void AffineRelation::applyReturnInst(Instruction const& inst) { } else { affineAssignment(&inst, 1, ret_val, 0); } + } else { + nonDeterminsticAssignment(&inst); } } -- GitLab From 0e236555d249c99257f1822509a4dcb267ab2657 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 28 Mar 2020 00:48:39 +0100 Subject: [PATCH 133/142] added example-1.c --- CMakeLists.txt | 1 + samples/example-1.c | 20 ++++++++++++++++++++ 2 files changed, 21 insertions(+) create mode 100644 samples/example-1.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 83dc0cb..af24f27 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -247,6 +247,7 @@ set(SAMPLES while-2 while-bigger-steps while-neg + example-1 ) # Older CMake version do not support list transformations diff --git a/samples/example-1.c b/samples/example-1.c new file mode 100644 index 0000000..05ed299 --- /dev/null +++ b/samples/example-1.c @@ -0,0 +1,20 @@ +volatile int x1,x2,x3; +volatile int condition; + + +void P() { + if (condition) { + x1 = x1 + x2 + 1; + x3 = x3 + 1; + P(); + x1 = x1 - x2; + } +} + + +int main() { + x2 = x1; + x3 = 0; + P(); + x1 = 1 - x2 - x3; +} \ No newline at end of file -- GitLab From 30debb9fbf22f3229e238647dd1de5211c52d978 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Mon, 30 Mar 2020 04:01:42 +0200 Subject: [PATCH 134/142] fixed a bug that would set a to zero --- src/normalized_conjunction.cpp | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index fe9e77f..e27e6a5 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -553,19 +553,37 @@ void NormalizedConjunction::Mul(Instruction const& inst) { // [xi := a * xj] } else if (isa(op1) && isa(op2)) { auto a = dyn_cast(op1); - return linearAssignment(&inst, a->getSExtValue(), op2, 0); + int64_t a_val = a->getSExtValue(); + if (a_val == 0) { + return linearAssignment(&inst, 1, nullptr, 0); + } else { + return linearAssignment(&inst, a_val, op2, 0); + } // [xi := xj * a] } else if (isa(op2) && isa(op1)) { auto a = dyn_cast(op2); - return linearAssignment(&inst, a->getSExtValue(), op1, 0); + int64_t a_val = a->getSExtValue(); + if (a_val == 0) { + return linearAssignment(&inst, 1, nullptr, 0); + } else { + return linearAssignment(&inst, a_val, op2, 0); + } // [xi := xj * xk] } else if (isa(op1) && isa(op2)) { // [xi := aj * xk] if (get(op1).isConstant()) { - return linearAssignment(&inst, get(op1).b, op2, 0); + if (get(op1).b == 0) { + return linearAssignment(&inst, 1, nullptr, 0); + } else { + return linearAssignment(&inst, get(op1).b, op2, 0); + } // [xi := xj * ak] } else if (get(op2).isConstant()) { - return linearAssignment(&inst, get(op1).b, op1, 0); + if (get(op2).b == 0) { + return linearAssignment(&inst, 1, nullptr, 0); + } else { + return linearAssignment(&inst, get(op2).b, op1, 0); + } // [xi := xj * xk] } else { return nonDeterminsticAssignment(&inst); -- GitLab From cdd89b09a361b3316a06b72cc17c8cd373bc1847 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 4 Apr 2020 17:25:18 +0200 Subject: [PATCH 135/142] comments --- src/affine_relation.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index 50cd6ba..dd21e0f 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -134,7 +134,6 @@ bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) { if (other.isBottom) { return false; } else if (isBottom) { - // FIXME: is this correct? basis = other.basis; index = other.index; isBottom = false; @@ -172,6 +171,7 @@ bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { // MARK: - Assignments +// xi = a1x1 + ... + anxn + a0 void AffineRelation::affineAssignment(Value const* xi, unordered_map relations, T constant) { Matrix Wr = Matrix(getNumberOfVariables() + 1); Wr(index.at(xi),index.at(xi)) = 0; @@ -191,6 +191,7 @@ void AffineRelation::affineAssignment(Value const* xi, unordered_map Date: Sat, 4 Apr 2020 22:37:16 +0200 Subject: [PATCH 136/142] fixed span efficiency --- src/affine_relation.cpp | 12 ++++++------ src/matrix.h | 6 ++++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/src/affine_relation.cpp b/src/affine_relation.cpp index dd21e0f..c1286d9 100644 --- a/src/affine_relation.cpp +++ b/src/affine_relation.cpp @@ -161,8 +161,8 @@ bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { vectors.push_back(m.toVector()); } // FIXME: i think this is transposing it twice. Maybe create a fast path for this kind of thing. - Matrix combined = Matrix(vectors).transpose(); - Matrix result = Matrix::span(combined); + Matrix combined = Matrix(vectors); + Matrix result = Matrix::span(combined, true); basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getWidth()); // FIXME: figure out a better way to detect changes @@ -182,8 +182,8 @@ void AffineRelation::affineAssignment(Value const* xi, unordered_map vector = Matrix(Wr.toVector()).transpose(); - Matrix vectorSpan = Matrix::span(vector); + Matrix vector = Matrix(Wr.toVector()); + Matrix vectorSpan = Matrix::span(vector, true); Wr = vectorSpan.reshape(Wr.getHeight(), Wr.getWidth()); for (Matrix& matrix: basis) { @@ -217,8 +217,8 @@ void AffineRelation::nonDeterminsticAssignment(Value const* xi) { assignment_vectors.push_back(T0.toVector()); assignment_vectors.push_back(T1.toVector()); - Matrix combined = Matrix(assignment_vectors).transpose(); - Matrix result = Matrix::span(combined); + Matrix combined = Matrix(assignment_vectors); + Matrix result = Matrix::span(combined, true); vector> span = result.reshapeColumns(T0.getHeight(), T0.getWidth()); diff --git a/src/matrix.h b/src/matrix.h index 5866c2b..f6bc352 100644 --- a/src/matrix.h +++ b/src/matrix.h @@ -150,9 +150,11 @@ public: } /// Basis of the linear span of the column vectors - static Matrix span(Matrix const& matrix) { + static Matrix span(Matrix const& matrix, bool transposed = false) { std::vector> rows; - Matrix te = matrix.transpose().echelonForm(); + // if matrix is already transposed don't do it again + Matrix t = transposed ? matrix : matrix.transpose(); + Matrix te = t.echelonForm(); int rank = te.getRank(); rows.reserve(rank); for (int row = 0; row < rank; row++) { -- GitLab From da25a9cbabd5a938ea1f95ea7e97f0b7f6cd31d0 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 11 Apr 2020 22:07:25 +0200 Subject: [PATCH 137/142] added sparse matrix --- CMakeLists.txt | 51 +- samples/A.c | 144 +++++ samples/B.c | 313 +++++++++++ samples/C.c | 143 +++++ samples/D.c | 285 ++++++++++ src/fixpoint.cpp | 4 +- src/general.h | 12 +- src/hash_utils.h | 6 +- src/linear_equality.h | 1 + ...ffine_relation.cpp => linear_subspace.cpp} | 185 ++++--- src/{affine_relation.h => linear_subspace.h} | 35 +- src/normalized_conjunction.cpp | 4 + src/{matrix.h => simple_matrix.h} | 96 +++- src/sparse_matrix.h | 521 ++++++++++++++++++ test/affine_relation_test.cpp | 89 --- test/linear_subspace_test.cpp | 89 +++ ...matrix_test.cpp => simple_matrix_test.cpp} | 185 ++++++- test/sparse_matrix_test.cpp | 515 +++++++++++++++++ 18 files changed, 2461 insertions(+), 217 deletions(-) create mode 100644 samples/A.c create mode 100644 samples/B.c create mode 100644 samples/C.c create mode 100644 samples/D.c rename src/{affine_relation.cpp => linear_subspace.cpp} (67%) rename src/{affine_relation.h => linear_subspace.h} (64%) rename src/{matrix.h => simple_matrix.h} (80%) create mode 100644 src/sparse_matrix.h delete mode 100644 test/affine_relation_test.cpp create mode 100644 test/linear_subspace_test.cpp rename test/{matrix_test.cpp => simple_matrix_test.cpp} (65%) create mode 100644 test/sparse_matrix_test.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index af24f27..8318162 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -105,7 +105,7 @@ set(PAIN_SOURCES src/simple_interval.cpp src/normalized_conjunction.cpp src/linear_equality.cpp - src/affine_relation.cpp + src/linear_subspace.cpp ) set(PAIN_HEADERS @@ -118,8 +118,9 @@ set(PAIN_HEADERS src/global.h src/abstract_state.h src/hash_utils.h - src/affine_relation.h - src/matrix.h + src/linear_subspace.h + src/simple_matrix.h + src/sparse_matrix.h ) include_directories(${LLVM_INCLUDE_DIRS}) @@ -134,6 +135,10 @@ add_llvm_library(llvm-pain MODULE opt ) +# +# Tests +# + add_llvm_executable(simple_interval_test test/simple_interval_test.cpp ${PAIN_HEADERS} @@ -160,8 +165,8 @@ target_link_libraries(normalized_conjunction_test ${LLVM_AVAILABLE_LIBS} ) -add_llvm_executable(affine_relation_test - test/affine_relation_test.cpp +add_llvm_executable(linear_subspace_test + test/linear_subspace_test.cpp ${PAIN_HEADERS} ${PAIN_SOURCES} DEPENDS @@ -169,17 +174,27 @@ add_llvm_executable(affine_relation_test intrinsics_gen ) -target_link_libraries(affine_relation_test +target_link_libraries(linear_subspace_test ${LLVM_AVAILABLE_LIBS} ) -add_llvm_executable(matrix_test - test/matrix_test.cpp +add_llvm_executable(simple_matrix_test + test/simple_matrix_test.cpp ${PAIN_HEADERS} ${PAIN_SOURCES} ) -target_link_libraries(matrix_test +target_link_libraries(simple_matrix_test + ${LLVM_AVAILABLE_LIBS} +) + +add_llvm_executable(sparse_matrix_test + test/sparse_matrix_test.cpp + ${PAIN_HEADERS} + ${PAIN_SOURCES} +) + +target_link_libraries(sparse_matrix_test ${LLVM_AVAILABLE_LIBS} ) @@ -198,13 +213,21 @@ add_test(NAME normalizedConjunctionTest ) add_test(NAME affineRelationTest - COMMAND affine_relation_test + COMMAND linear_subspace_test ) -add_test(NAME matrixTest - COMMAND matrix_test +add_test(NAME simpleMatrixTest + COMMAND simple_matrix_test ) +add_test(NAME sparseMatrixTest + COMMAND sparse_matrix_test +) + +# +# Samples +# + set(SAMPLES add-1-float add-1 @@ -248,6 +271,10 @@ set(SAMPLES while-bigger-steps while-neg example-1 + A + B + C + D ) # Older CMake version do not support list transformations diff --git a/samples/A.c b/samples/A.c new file mode 100644 index 0000000..dff0c3a --- /dev/null +++ b/samples/A.c @@ -0,0 +1,144 @@ +/* + * This is a RANDOMLY GENERATED PROGRAM. + * + * Generator: csmith 2.3.0 + * Git version: 30dccd7 + * Options: --output A.c --no-argc --no-arrays --no-bitfields --no-checksum --no-comma-operators --no-compound-assignment --no-divs --no-float --no-structs --no-unions --no-packed-struct --no-pointers --no-builtins --no-math64 --no-longlong --no-checksum --no-safe-math --max-block-size 2 --max-funcs 2 + * Seed: 7117265377608335715 + */ + + +#define NO_LONGLONG + +#define int8_t signed char +#define uint8_t unsigned char + +#define int16_t short +#define uint16_t unsigned short + +#define int32_t int +#define uint32_t unsigned + +#define int64_t long long +#define uint64_t unsigned long long + +static inline void +platform_main_end (uint64_t x, int flag) +{ +#ifndef NOT_PRINT_CHECKSUM + if (!flag) { +#ifdef NO_PRINTF + int i; + my_puts ("checksum = "); + for (i=0; i<16; i++) { + put_hex (x & 0xf); + x >>= 4; + } + putchar ('\n'); +#else + printf ("checksum = %llx\n", x); +#endif + } +#endif +} +volatile uint32_t csmith_sink_ = 0; + +static long __undefined; + +/* --- Struct/Union Declarations --- */ +/* --- GLOBAL VARIABLES --- */ +static int32_t g_2 = (-1); +static int32_t g_3 = 0x245F1DBE; +static int16_t g_62 = 0xE6DE; +static int32_t g_65 = 0; +static int8_t g_66 = (-5); + + +/* --- FORWARD DECLARATIONS --- */ +static uint16_t func_1(void); +static int8_t func_16(const int16_t p_17, uint8_t p_18, int8_t p_19, int32_t p_20, uint32_t p_21); + + +/* --- FUNCTIONS --- */ +/* ------------------------------------------ */ +/* + * reads : g_2 g_3 g_66 + * writes: g_3 g_62 g_65 g_66 g_2 + */ +static uint16_t func_1(void) +{ /* block id: 0 */ + const uint16_t l_22 = 65526U; + int32_t l_32 = (-1); + g_2 = ((g_3 = g_2) && ((uint8_t)(((((g_66 = ((((uint8_t)g_2 % (uint8_t)((uint16_t)((uint16_t)(0x8951 >= (-1)) >> (uint16_t)((int8_t)(g_65 = ((int8_t)func_16(l_22, ((uint16_t)(l_32 = (l_22 && (g_2 || ((0U ^ ((uint16_t)(((((uint8_t)((uint8_t)(+g_3) << (uint8_t)3) >> (uint8_t)0) <= 0U) <= g_3) <= 0xAF9CA3EA) + (uint16_t)g_2)) == l_22)))) << (uint16_t)g_3), l_22, g_2, g_2) >> (int8_t)g_2)) >> (int8_t)l_22)) >> (uint16_t)2)) <= g_2) & 0U)) | l_22) || l_32) > g_2) ^ 1) + (uint8_t)l_22)); + return g_66; +} + + +/* ------------------------------------------ */ +/* + * reads : g_2 g_3 + * writes: g_62 g_3 + */ +static int8_t func_16(const int16_t p_17, uint8_t p_18, int8_t p_19, int32_t p_20, uint32_t p_21) +{ /* block id: 3 */ + uint32_t l_35 = 4294967293U; + uint16_t l_41 = 1U; + int32_t l_61 = 0xBF2A6BC6; + int32_t l_63 = 4; + int32_t l_64 = 0x4FFA2B87; + g_3 = (((uint8_t)(((l_35 && ((int16_t)(!l_35) + (int16_t)((uint16_t)(((0x6D23205E <= (l_64 = ((l_63 = ((l_41 = p_19) || ((int32_t)((int16_t)(g_62 = ((int8_t)(((((uint8_t)((int16_t)((p_19 = p_20) ^ 0U) - (int16_t)(((int16_t)0xCCFE >> (int16_t)8) < g_2)) << (uint8_t)((uint16_t)(((l_61 = ((int8_t)((int8_t)(!p_18) - (int8_t)g_3) >> (int8_t)5)) && l_35) & p_20) << (uint16_t)1)) != 0xD9) <= l_35) > 1) % (int8_t)g_2)) * (int16_t)p_18) + (int32_t)p_20))) & l_35))) == p_17) || p_20) << (uint16_t)g_2))) == g_3) && 0x412C2192) - (uint8_t)9) > p_21); + return p_21; +} + + + + +/* ---------------------------------------- */ +int main (void) +{ + int print_hash_value = 0; + platform_main_begin(); + func_1(); + csmith_sink_ = g_2; + csmith_sink_ = g_3; + csmith_sink_ = g_62; + csmith_sink_ = g_65; + csmith_sink_ = g_66; + platform_main_end(0,0); + return 0; +} + +/************************ statistics ************************* +XXX max struct depth: 0 +breakdown: + depth: 0, occurrence: 12 +XXX total union variables: 0 + +XXX max expression depth: 35 +breakdown: + depth: 1, occurrence: 4 + depth: 35, occurrence: 2 + +XXX total number of pointers: 0 + +XXX times a non-volatile is read: 41 +XXX times a non-volatile is write: 12 +XXX times a volatile is read: 0 +XXX times read thru a pointer: 0 +XXX times a volatile is write: 0 +XXX times written thru a pointer: 0 +XXX times a volatile is available for access: 0 +XXX percentage of non-volatile access: 100 + +XXX forward jumps: 0 +XXX backward jumps: 0 + +XXX stmts: 4 +XXX max block depth: 0 +breakdown: + depth: 0, occurrence: 4 + +XXX percentage a fresh-made variable is used: 22.6 +XXX percentage an existing variable is used: 77.4 +********************* end of statistics **********************/ + diff --git a/samples/B.c b/samples/B.c new file mode 100644 index 0000000..1d0d9bf --- /dev/null +++ b/samples/B.c @@ -0,0 +1,313 @@ +/* + * This is a RANDOMLY GENERATED PROGRAM. + * + * Generator: csmith 2.3.0 + * Git version: 30dccd7 + * Options: --output B.c --no-argc --no-arrays --no-bitfields --no-checksum --no-comma-operators --no-compound-assignment --no-divs --no-float --no-structs --no-unions --no-packed-struct --no-pointers --no-builtins --no-math64 --no-longlong --no-checksum --no-safe-math --max-block-size 2 --max-funcs 4 + * Seed: 2537522965051140364 + */ + + +#define NO_LONGLONG + +#define int8_t signed char +#define uint8_t unsigned char + +#define int16_t short +#define uint16_t unsigned short + +#define int32_t int +#define uint32_t unsigned + +#define int64_t long long +#define uint64_t unsigned long long + +static inline void +platform_main_end (uint64_t x, int flag) +{ +#ifndef NOT_PRINT_CHECKSUM + if (!flag) { +#ifdef NO_PRINTF + int i; + my_puts ("checksum = "); + for (i=0; i<16; i++) { + put_hex (x & 0xf); + x >>= 4; + } + putchar ('\n'); +#else + printf ("checksum = %llx\n", x); +#endif + } +#endif +} +volatile uint32_t csmith_sink_ = 0; + +static long __undefined; + +/* --- Struct/Union Declarations --- */ +/* --- GLOBAL VARIABLES --- */ +static uint32_t g_6 = 0x964A53AA; +static uint32_t g_74 = 4294967292U; +static int16_t g_75 = 3; +static uint16_t g_79 = 1U; +static uint32_t g_95 = 0U; +static int32_t g_106 = 0x4023A1BF; +static int32_t g_116 = 0x105BA918; +static int32_t g_139 = 0xB1B1B7DE; +static uint16_t g_168 = 0x385D; +static int16_t g_204 = 5; +static uint16_t g_209 = 0x5A12; +static int32_t g_233 = (-1); +static uint16_t g_268 = 0U; +static volatile int32_t g_296 = 2;/* VOLATILE GLOBAL g_296 */ +static uint32_t g_298 = 6U; + + +/* --- FORWARD DECLARATIONS --- */ +static int32_t func_1(void); +static uint8_t func_8(uint32_t p_9, uint32_t p_10, int32_t p_11, int16_t p_12, int8_t p_13); +static int32_t func_15(uint32_t p_16, uint32_t p_17, int8_t p_18, uint16_t p_19); +static int8_t func_20(uint16_t p_21, uint32_t p_22, uint32_t p_23); + + +/* --- FUNCTIONS --- */ +/* ------------------------------------------ */ +/* + * reads : g_6 g_74 g_75 g_79 g_95 g_116 g_106 g_139 g_168 g_204 g_209 g_233 g_268 g_296 + * writes: g_75 g_79 g_95 g_106 g_74 g_116 g_139 g_168 g_204 g_209 g_233 g_268 g_298 + */ +static int32_t func_1(void) +{ /* block id: 0 */ + uint32_t l_7 = 0x3099CFE0; + int32_t l_14 = 0x7BEDEDB5; + int32_t l_36 = (-5); + int32_t l_37 = 0xD76270E7; + int32_t l_38 = (-1); + uint32_t l_39 = 0x26A9D9E1; + uint8_t l_203 = 0x4E; + uint32_t l_234 = 0U; + g_139 = ((int8_t)((int8_t)(l_7 = g_6) - (int8_t)func_8(g_6, l_14, (g_79 = func_15(g_6, g_6, func_20(((((int16_t)((int8_t)l_14 >> (int8_t)1) * (int16_t)((int8_t)((int8_t)((l_38 = ((l_37 = ((uint32_t)((uint16_t)((l_36 = g_6) > g_6) + (uint16_t)1) - (uint32_t)g_6)) > 0x53A38D06)) >= l_14) << (int8_t)0) % (int8_t)g_6)) | g_6) == l_39), l_39, g_6), g_6)), l_14, l_14)) - (int8_t)1U); + if (((uint32_t)g_6 - (uint32_t)l_36)) + { /* block id: 59 */ + int8_t l_161 = 0xCE; + g_116 = ((int16_t)(g_106 || 0xB671) >> (int16_t)9); + if ((l_37 = ((uint16_t)g_6 + (uint16_t)(9U != ((uint8_t)(g_168 = ((int8_t)(-(uint32_t)((int8_t)(((uint16_t)(((int8_t)((uint8_t)0x57 * (uint8_t)(((g_139 && (((g_106 == ((int8_t)((l_37 < l_161) < ((uint32_t)(((int32_t)(g_95 >= ((int32_t)(g_116 = (g_74 & l_161)) - (int32_t)4294967290U)) - (int32_t)1) || 9U) + (uint32_t)l_161)) >> (int8_t)4)) != l_161) <= l_38)) || 0x3E) <= 0x93B69C79)) % (int8_t)g_106) == 0U) + (uint16_t)3) || g_75) << (int8_t)6)) >> (int8_t)5)) + (uint8_t)g_75))))) + { /* block id: 64 */ + int32_t l_171 = 1; + g_106 = ((uint8_t)l_171 << (uint8_t)4); + } + else + { /* block id: 66 */ + return g_168; + } + } + else + { /* block id: 69 */ + uint8_t l_201 = 250U; + int32_t l_205 = 0x5094A51A; + int32_t l_215 = 6; + int32_t l_287 = 0xAE1B71C9; + int32_t l_297 = 0; + if (g_106) + { /* block id: 70 */ + int16_t l_184 = 0x6D60; + int16_t l_202 = (-9); + int32_t l_225 = 0x87346C39; + int32_t l_235 = 0x077A8CD5; + if (((((g_6 != ((int16_t)((uint32_t)g_139 - (uint32_t)(l_205 = (g_204 = (g_74 = ((int32_t)((int32_t)(((l_14 = ((uint8_t)(l_38 = ((int16_t)l_184 + (int16_t)g_6)) % (uint8_t)((int16_t)(l_202 = ((int16_t)((((int8_t)((uint8_t)(((int16_t)(l_37 = (((int32_t)((((uint16_t)0x42E7 << (uint16_t)(l_184 && 0xF7E8)) > (((uint8_t)l_184 >> (uint8_t)3) && g_116)) <= l_201) + (int32_t)g_79) <= g_95)) >> (int16_t)l_184) < 0x52EA) - (uint8_t)g_116) * (int8_t)l_201) & 0xBA) | 0) * (int16_t)0x072C)) - (int16_t)1U))) != l_203) == g_79) % (int32_t)g_74) + (int32_t)g_95))))) * (int16_t)l_39)) != g_139) == g_75) != g_116)) + { /* block id: 78 */ + g_116 = (-3); + } + else + { /* block id: 80 */ + for (g_95 = 0; (g_95 <= 11); g_95 += 1) + { /* block id: 83 */ + const int32_t l_208 = 0x5ADCE77D; + g_209 = (l_208 <= g_168); + } + } + l_38 = (l_235 = (!((uint8_t)((int8_t)(((l_215 <= ((int16_t)(249U ^ (((uint16_t)(((int16_t)(g_74 || (~((int8_t)(g_233 = ((((l_225 = l_37) || g_204) && g_106) ^ ((int8_t)(((int8_t)g_75 % (int8_t)((int16_t)g_139 >> (int16_t)12)) ^ (-(uint16_t)(g_79 && g_6))) >> (int8_t)g_6))) << (int8_t)3))) + (int16_t)1U) <= 0xE64536AC) * (uint16_t)g_204) == g_116)) * (int16_t)g_75)) <= 0xE7461C8C) != l_184) << (int8_t)3) >> (uint8_t)l_234))); + } + else + { /* block id: 91 */ + return l_205; + } + if (((uint32_t)g_79 - (uint32_t)g_74)) + { /* block id: 94 */ + uint32_t l_255 = 0x295DAE69; + int32_t l_269 = 0xEBDA9155; + l_205 = ((int32_t)(((int16_t)(((int8_t)((~(((int16_t)((l_205 && g_116) == ((int8_t)((uint16_t)((g_106 = (((uint8_t)((int16_t)(g_204 = l_255) * (int16_t)((l_269 = ((int32_t)((l_201 | (((0x62 | (g_209 | ((((uint16_t)((int8_t)((uint8_t)(g_268 = (((((uint8_t)((int8_t)(l_215 > l_255) % (int8_t)g_233) - (uint8_t)0xBC) < 1U) || (-1)) >= g_116)) % (uint8_t)g_95) % (int8_t)g_75) << (uint16_t)l_255) && g_79) <= (-1)))) || 0xD10CE58A) >= l_39)) & l_255) % (int32_t)l_255)) && g_139)) + (uint8_t)0x85) && 4294967295U)) | l_201) + (uint16_t)0xD827) * (int8_t)g_6)) + (int16_t)l_201) < g_233)) && 0x87BBF4A9) << (int8_t)1) == g_168) * (int16_t)g_95) && g_268) + (int32_t)l_234); + g_116 = g_139; + } + else + { /* block id: 101 */ + uint32_t l_277 = 4294967295U; + g_116 = ((uint8_t)(g_298 = ((l_297 = ((uint8_t)(~((uint16_t)(((g_106 = (l_277 = 0x7E71CC03)) >= (+(((((uint8_t)(l_203 ^ 4U) * (uint8_t)((int8_t)((uint8_t)((uint8_t)(l_205 = l_234) * (uint8_t)0U) + (uint8_t)((l_287 = (l_14 = g_139)) >= (0 <= ((uint16_t)7U >> (uint16_t)11)))) << (int8_t)1)) == ((uint32_t)((int8_t)((uint32_t)((0xECE6 > l_201) & g_168) - (uint32_t)g_6) % (int8_t)l_201) + (uint32_t)g_296)) || g_204) == 0x3FA2BBC1))) != 0x824737F3) << (uint16_t)g_168)) >> (uint8_t)l_37)) < g_204)) - (uint8_t)l_215); + } + } + return l_36; +} + + +/* ------------------------------------------ */ +/* + * reads : g_79 g_6 g_74 g_75 g_95 g_116 + * writes: g_95 g_106 g_74 g_75 g_116 + */ +static uint8_t func_8(uint32_t p_9, uint32_t p_10, int32_t p_11, int16_t p_12, int8_t p_13) +{ /* block id: 25 */ + uint32_t l_98 = 1U; + int32_t l_107 = 0x1B5D7EC3; + int32_t l_108 = 1; + uint32_t l_130 = 0U; + int8_t l_132 = 0x9F; + l_108 = ((uint8_t)p_13 % (uint8_t)(((uint8_t)((int8_t)((uint16_t)((((l_107 = ((uint16_t)p_10 << (uint16_t)((((-(int8_t)(((int8_t)(p_10 ^ ((g_95 = ((int8_t)0x6C >> (int8_t)2)) ^ ((int16_t)(((g_79 <= (-1)) >= l_98) & ((uint8_t)p_12 * (uint8_t)((g_106 = ((((((uint16_t)(((uint16_t)(~l_98) + (uint16_t)p_13) < p_11) * (uint16_t)p_10) > g_6) & 0x87233ED7) ^ g_6) >= 0xE5)) > p_11))) << (int16_t)p_10))) << (int8_t)l_98) & 2)) | p_9) || p_10) | 0x83BD))) < l_98) ^ l_98) == p_13) + (uint16_t)l_98) << (int8_t)3) << (uint8_t)4) && 1)); + if (p_9) + { /* block id: 30 */ + for (p_11 = 24; (p_11 == 14); --p_11) + { /* block id: 33 */ + if (p_9) + break; + g_106 = p_10; + } + } + else + { /* block id: 37 */ + for (g_74 = 0; (g_74 > 46); g_74++) + { /* block id: 40 */ + int16_t l_129 = 0xED4C; + for (g_75 = 0; (g_75 < 12); g_75 += 1) + { /* block id: 43 */ + const uint8_t l_131 = 0x0C; + g_106 = (0x7436 && ((((+(g_75 != ((((0x20E1BCB0 & (g_116 = g_95)) | (4 > (((int16_t)((int16_t)(((uint16_t)((uint32_t)((uint32_t)(((uint16_t)6U - (uint16_t)((((((-1) <= l_129) >= (((((l_130 = l_108) ^ 1) < 1) ^ g_95) <= l_131)) ^ p_10) && 3U) >= l_108)) == p_11) + (uint32_t)p_11) % (uint32_t)p_11) * (uint16_t)p_12) >= 4U) >> (int16_t)g_74) >> (int16_t)15) || l_129))) != 0xE99C) == l_131))) > l_132) != g_79) == l_132)); + g_116 = (((uint8_t)((int8_t)(g_116 == p_11) + (int8_t)p_10) - (uint8_t)l_131) ^ l_129); + } + } + for (p_13 = (-19); (p_13 != (-2)); p_13++) + { /* block id: 52 */ + l_108 = g_95; + return l_107; + } + } + return l_107; +} + + +/* ------------------------------------------ */ +/* + * reads : g_6 g_74 g_75 + * writes: g_75 + */ +static int32_t func_15(uint32_t p_16, uint32_t p_17, int8_t p_18, uint16_t p_19) +{ /* block id: 10 */ + uint32_t l_50 = 0x2097756E; + int32_t l_76 = (-1); + if ((l_50 = (p_18 < g_6))) + { /* block id: 12 */ + int8_t l_51 = (-4); + int32_t l_56 = (-3); + int32_t l_77 = 0xD7B9758A; + l_51 = g_6; + l_77 = (l_76 = ((uint16_t)0x0270 * (uint16_t)((int32_t)(g_6 > ((((l_56 = g_6) ^ ((uint8_t)g_6 >> (uint8_t)(p_18 = (g_75 = (0xBB <= (((uint8_t)0xB0 * (uint8_t)(((int8_t)((int8_t)(((int8_t)(~(((((((int16_t)((uint8_t)((uint16_t)(p_19 = (p_17 > l_51)) << (uint16_t)13) - (uint8_t)p_18) << (int16_t)l_50) < g_6) ^ 0x88841F9F) && g_74) > p_16) && 0x6485393F)) + (int8_t)g_74) >= 4U) * (int8_t)p_16) >> (int8_t)1) & p_17)) == p_16)))))) & l_50) && g_75)) - (int32_t)g_74))); + } + else + { /* block id: 20 */ + uint32_t l_78 = 4294967293U; + l_78 = 0xA659E808; + } + return p_18; +} + + +/* ------------------------------------------ */ +/* + * reads : g_6 + * writes: + */ +static int8_t func_20(uint16_t p_21, uint32_t p_22, uint32_t p_23) +{ /* block id: 5 */ + uint16_t l_46 = 0x91CB; + int32_t l_47 = 0x1CAF1E2C; + int32_t l_48 = 0xE7BAA9D7; + int32_t l_49 = 0x3FD90538; + l_49 = (((int16_t)(p_22 != (((int8_t)((l_46 ^ ((((((l_47 = 1U) != ((g_6 >= l_46) >= (0U & (g_6 | p_23)))) < ((l_48 = (0x24B13B10 <= p_23)) > p_23)) == p_23) ^ l_46) || g_6)) & p_23) % (int8_t)g_6) | 0)) >> (int16_t)l_46) ^ l_46); + return g_6; +} + + + + +/* ---------------------------------------- */ +int main (void) +{ + int print_hash_value = 0; + platform_main_begin(); + func_1(); + csmith_sink_ = g_6; + csmith_sink_ = g_74; + csmith_sink_ = g_75; + csmith_sink_ = g_79; + csmith_sink_ = g_95; + csmith_sink_ = g_106; + csmith_sink_ = g_116; + csmith_sink_ = g_139; + csmith_sink_ = g_168; + csmith_sink_ = g_204; + csmith_sink_ = g_209; + csmith_sink_ = g_233; + csmith_sink_ = g_268; + csmith_sink_ = g_296; + csmith_sink_ = g_298; + platform_main_end(0,0); + return 0; +} + +/************************ statistics ************************* +XXX max struct depth: 0 +breakdown: + depth: 0, occurrence: 55 +XXX total union variables: 0 + +XXX max expression depth: 40 +breakdown: + depth: 1, occurrence: 34 + depth: 2, occurrence: 9 + depth: 3, occurrence: 2 + depth: 5, occurrence: 1 + depth: 21, occurrence: 1 + depth: 25, occurrence: 1 + depth: 29, occurrence: 1 + depth: 30, occurrence: 2 + depth: 31, occurrence: 2 + depth: 34, occurrence: 1 + depth: 36, occurrence: 1 + depth: 40, occurrence: 1 + +XXX total number of pointers: 0 + +XXX times a non-volatile is read: 210 +XXX times a non-volatile is write: 65 +XXX times a volatile is read: 1 +XXX times read thru a pointer: 0 +XXX times a volatile is write: 0 +XXX times written thru a pointer: 0 +XXX times a volatile is available for access: 2 +XXX percentage of non-volatile access: 99.6 + +XXX forward jumps: 0 +XXX backward jumps: 0 + +XXX stmts: 38 +XXX max block depth: 4 +breakdown: + depth: 0, occurrence: 10 + depth: 1, occurrence: 10 + depth: 2, occurrence: 13 + depth: 3, occurrence: 4 + depth: 4, occurrence: 1 + +XXX percentage a fresh-made variable is used: 20.5 +XXX percentage an existing variable is used: 79.5 +********************* end of statistics **********************/ + diff --git a/samples/C.c b/samples/C.c new file mode 100644 index 0000000..d042aaa --- /dev/null +++ b/samples/C.c @@ -0,0 +1,143 @@ +/* + * This is a RANDOMLY GENERATED PROGRAM. + * + * Generator: csmith 2.3.0 + * Git version: 30dccd7 + * Options: --output C.c --no-argc --no-arrays --no-bitfields --no-checksum --no-comma-operators --no-compound-assignment --no-divs --no-float --no-structs --no-unions --no-packed-struct --no-pointers --no-builtins --no-math64 --no-longlong --no-checksum --no-safe-math --max-block-size 4 --max-funcs 2 + * Seed: 4773875934381650930 + */ + + +#define NO_LONGLONG + +#define int8_t signed char +#define uint8_t unsigned char + +#define int16_t short +#define uint16_t unsigned short + +#define int32_t int +#define uint32_t unsigned + +#define int64_t long long +#define uint64_t unsigned long long + +static inline void +platform_main_end (uint64_t x, int flag) +{ +#ifndef NOT_PRINT_CHECKSUM + if (!flag) { +#ifdef NO_PRINTF + int i; + my_puts ("checksum = "); + for (i=0; i<16; i++) { + put_hex (x & 0xf); + x >>= 4; + } + putchar ('\n'); +#else + printf ("checksum = %llx\n", x); +#endif + } +#endif +} +volatile uint32_t csmith_sink_ = 0; + +static long __undefined; + +/* --- Struct/Union Declarations --- */ +/* --- GLOBAL VARIABLES --- */ +static uint8_t g_2 = 0xFE; +static int16_t g_6 = 0xAB60; +static uint8_t g_40 = 1U; +static int32_t g_43 = 0x933DF47B; +static int32_t g_44 = 0x6C987F9B; + + +/* --- FORWARD DECLARATIONS --- */ +static const uint8_t func_1(void); +static const uint16_t func_12(const uint32_t p_13, int32_t p_14, uint32_t p_15, int32_t p_16, int16_t p_17); + + +/* --- FUNCTIONS --- */ +/* ------------------------------------------ */ +/* + * reads : g_2 + * writes: g_6 g_40 g_43 g_44 + */ +static const uint8_t func_1(void) +{ /* block id: 0 */ + uint16_t l_3 = 0x13DC; + int32_t l_11 = 1; + g_44 = (g_43 = ((l_3 = g_2) == ((l_11 = (65535U >= (((int16_t)((g_6 = g_2) <= ((uint32_t)((l_11 ^ l_11) || (((l_11 | func_12(l_11, ((((((uint16_t)((uint16_t)(((int16_t)g_2 - (int16_t)((uint8_t)l_11 * (uint8_t)3U)) ^ g_2) - (uint16_t)l_11) + (uint16_t)l_11) >= 4294967294U) & g_2) > (-3)) > 0x58B39D20), l_11, l_11, g_2)) ^ l_11) != g_2)) - (uint32_t)l_11)) >> (int16_t)g_2) && l_11))) >= 7U))); + return l_11; +} + + +/* ------------------------------------------ */ +/* + * reads : g_2 + * writes: g_40 + */ +static const uint16_t func_12(const uint32_t p_13, int32_t p_14, uint32_t p_15, int32_t p_16, int16_t p_17) +{ /* block id: 3 */ + int32_t l_32 = 0x7E784B52; + int32_t l_39 = (-9); + int32_t l_41 = 0x46221EB1; + int32_t l_42 = 0; + l_42 = ((g_2 || g_2) | ((((uint8_t)((int8_t)g_2 >> (int8_t)1) - (uint8_t)(l_41 = (p_15 ^ ((uint32_t)(g_2 <= (l_32 <= (((uint8_t)((uint16_t)((0x02 & ((g_40 = ((int16_t)(((((p_17 = 0xE8EA) > (((((l_39 = (g_2 || g_2)) || 0x96DAD8BE) ^ 0xE9F8) <= l_32) && l_39)) < g_2) <= p_14) == g_2) - (int16_t)1)) & p_13)) >= l_32) + (uint16_t)g_2) << (uint8_t)l_32) != l_32))) - (uint32_t)l_32)))) > g_2) == p_15)); + return g_2; +} + + + + +/* ---------------------------------------- */ +int main (void) +{ + int print_hash_value = 0; + platform_main_begin(); + func_1(); + csmith_sink_ = g_2; + csmith_sink_ = g_6; + csmith_sink_ = g_40; + csmith_sink_ = g_43; + csmith_sink_ = g_44; + platform_main_end(0,0); + return 0; +} + +/************************ statistics ************************* +XXX max struct depth: 0 +breakdown: + depth: 0, occurrence: 11 +XXX total union variables: 0 + +XXX max expression depth: 31 +breakdown: + depth: 1, occurrence: 4 + depth: 31, occurrence: 2 + +XXX total number of pointers: 0 + +XXX times a non-volatile is read: 43 +XXX times a non-volatile is write: 10 +XXX times a volatile is read: 0 +XXX times read thru a pointer: 0 +XXX times a volatile is write: 0 +XXX times written thru a pointer: 0 +XXX times a volatile is available for access: 0 +XXX percentage of non-volatile access: 100 + +XXX forward jumps: 0 +XXX backward jumps: 0 + +XXX stmts: 4 +XXX max block depth: 0 +breakdown: + depth: 0, occurrence: 4 + +XXX percentage a fresh-made variable is used: 20.8 +XXX percentage an existing variable is used: 79.2 +********************* end of statistics **********************/ + diff --git a/samples/D.c b/samples/D.c new file mode 100644 index 0000000..46e8c87 --- /dev/null +++ b/samples/D.c @@ -0,0 +1,285 @@ +/* + * This is a RANDOMLY GENERATED PROGRAM. + * + * Generator: csmith 2.3.0 + * Git version: 30dccd7 + * Options: --output D.c --no-argc --no-arrays --no-bitfields --no-checksum --no-comma-operators --no-compound-assignment --no-divs --no-float --no-structs --no-unions --no-packed-struct --no-pointers --no-builtins --no-math64 --no-longlong --no-checksum --no-safe-math --max-block-size 4 --max-funcs 4 + * Seed: 9511787161989909427 + */ + + +#define NO_LONGLONG + +#define int8_t signed char +#define uint8_t unsigned char + +#define int16_t short +#define uint16_t unsigned short + +#define int32_t int +#define uint32_t unsigned + +#define int64_t long long +#define uint64_t unsigned long long + +static inline void +platform_main_end (uint64_t x, int flag) +{ +#ifndef NOT_PRINT_CHECKSUM + if (!flag) { +#ifdef NO_PRINTF + int i; + my_puts ("checksum = "); + for (i=0; i<16; i++) { + put_hex (x & 0xf); + x >>= 4; + } + putchar ('\n'); +#else + printf ("checksum = %llx\n", x); +#endif + } +#endif +} +volatile uint32_t csmith_sink_ = 0; + +static long __undefined; + +/* --- Struct/Union Declarations --- */ +/* --- GLOBAL VARIABLES --- */ +static volatile int8_t g_2 = 0xF6;/* VOLATILE GLOBAL g_2 */ +static volatile uint32_t g_3 = 4294967292U;/* VOLATILE GLOBAL g_3 */ +static int32_t g_5 = 0x5BF25CBD; +static uint16_t g_26 = 0xF215; +static uint32_t g_56 = 9U; +static uint8_t g_57 = 0x2D; +static int32_t g_108 = 3; +static const volatile uint8_t g_112 = 251U;/* VOLATILE GLOBAL g_112 */ +static uint8_t g_124 = 0x67; +static int8_t g_196 = 0xBB; +static int32_t g_208 = 0x85F6FCF5; +static uint8_t g_210 = 0U; +static uint32_t g_216 = 1U; + + +/* --- FORWARD DECLARATIONS --- */ +static uint16_t func_1(void); +static uint16_t func_10(uint32_t p_11); +static uint32_t func_12(int16_t p_13, int32_t p_14, uint16_t p_15, int32_t p_16, uint32_t p_17); +static const uint8_t func_24(int32_t p_25); + + +/* --- FUNCTIONS --- */ +/* ------------------------------------------ */ +/* + * reads : g_2 g_3 g_5 g_26 g_56 g_57 g_112 g_108 g_124 g_208 g_210 + * writes: g_3 g_5 g_26 g_56 g_57 g_108 g_124 g_196 g_210 g_216 + */ +static uint16_t func_1(void) +{ /* block id: 0 */ + int32_t l_4 = 0x79536444; + g_3 = g_2; + g_5 = l_4; + l_4 = (((int16_t)((uint16_t)func_10(func_12(((int32_t)g_3 % (int32_t)((g_5 || ((l_4 && g_5) >= g_5)) ^ g_5)), (g_108 = (((uint8_t)l_4 >> (uint8_t)(((uint8_t)func_24((((g_26 = (0x4F || (0 != g_5))) | g_5) && 0xB002891B)) >> (uint8_t)1) ^ l_4)) < g_5)), l_4, l_4, g_5)) - (uint16_t)l_4) + (int16_t)l_4) >= 0x75CCD888); + return l_4; +} + + +/* ------------------------------------------ */ +/* + * reads : g_3 g_57 g_124 g_208 g_5 g_56 g_112 g_26 g_210 g_108 + * writes: g_196 g_210 g_5 g_26 g_216 + */ +static uint16_t func_10(uint32_t p_11) +{ /* block id: 71 */ + uint16_t l_191 = 0xE18E; + int8_t l_209 = 0x3C; + int32_t l_211 = 0xD98C0EBE; + int32_t l_212 = 0x9A0713E2; + int16_t l_233 = 0xB477; + l_212 = ((uint16_t)g_3 >> (uint16_t)(g_26 = ((g_5 = (l_211 = ((uint16_t)((l_191 & g_57) | ((((((g_57 != (((uint16_t)l_191 - (uint16_t)(g_196 = 0x6ADF)) < (((int32_t)((int8_t)(g_210 = (((int16_t)(((g_124 == ((uint16_t)((((((p_11 && (((int8_t)(((((+0x99) & (-6)) && p_11) == g_208) <= 1U) >> (int8_t)g_5) <= p_11)) & g_57) || l_209) >= p_11) & g_124) < l_209) >> (uint16_t)p_11)) && 0x764A) >= 4294967289U) << (int16_t)p_11) == g_56)) + (int8_t)g_5) - (int32_t)0xAB7E1749) <= p_11))) && g_208) ^ l_209) ^ l_191) != l_191) > p_11)) + (uint16_t)g_5))) || l_211))); + l_211 = ((int8_t)((+(((9 > (1U >= (((g_208 && (((g_216 = 4294967295U) || (-2)) || (g_57 && g_112))) != ((uint8_t)((int32_t)((int16_t)(l_212 = ((((((0 == g_56) < l_212) && l_191) != g_26) >= 0xD89F8139) != g_210)) >> (int16_t)l_211) - (int32_t)1) >> (uint8_t)l_209)) >= l_209))) ^ g_208) >= 0)) ^ l_211) >> (int8_t)1); + l_211 = (p_11 ^ 0x73); + l_212 = (((((int16_t)g_108 + (int16_t)(l_211 < ((uint8_t)(((uint16_t)(((int8_t)l_191 % (int8_t)g_124) & (((int16_t)l_233 - (int16_t)p_11) >= g_26)) << (uint16_t)0) == ((uint16_t)(~1U) - (uint16_t)l_211)) + (uint8_t)l_209))) >= g_56) & 1) <= 0xE4F4); + return p_11; +} + + +/* ------------------------------------------ */ +/* + * reads : g_112 g_56 g_26 g_108 g_124 g_5 g_57 g_2 + * writes: g_124 g_5 + */ +static uint32_t func_12(int16_t p_13, int32_t p_14, uint16_t p_15, int32_t p_16, uint32_t p_17) +{ /* block id: 50 */ + int8_t l_109 = 0xC3; + int32_t l_117 = 1; + int32_t l_125 = 0xCEED78FB; + int32_t l_138 = 0x81864D21; + int32_t l_139 = 0x0732D581; + int32_t l_140 = 0; + const uint32_t l_147 = 4294967295U; + int32_t l_185 = 1; + uint32_t l_186 = 0x2304E23E; +lbl_154: + g_5 = ((l_109 = 65529U) ^ (((uint8_t)(g_112 > ((g_56 | ((int8_t)(l_117 = ((uint8_t)((l_117 & l_117) < ((uint32_t)(0x12 | l_117) % (uint32_t)((int8_t)p_15 << (int8_t)4))) * (uint8_t)((uint32_t)(g_124 = (p_15 | 0x16A80D17)) % (uint32_t)g_26))) << (int8_t)0)) >= l_125)) % (uint8_t)(-1)) | p_16)); + if ((l_125 = ((uint16_t)((uint32_t)((l_125 & ((l_140 = (p_13 || (g_112 >= ((uint8_t)(((l_117 = (l_117 ^ (g_124 = ((((g_108 <= g_26) == (((uint8_t)((g_26 && (l_139 = ((l_138 = ((((uint8_t)((((int16_t)(g_124 == ((g_5 != p_17) > (-1))) % (int16_t)p_16) && 0) & 0x31) % (uint8_t)0xC3) & 8) > p_15)) && l_117))) != g_57) - (uint8_t)0) <= l_109)) > g_5) || l_109)))) & (-1)) >= 2) * (uint8_t)l_125)))) | l_109)) != g_56) - (uint32_t)(-1)) << (uint16_t)8))) + { /* block id: 61 */ + return g_56; + } + else + { /* block id: 63 */ + int16_t l_152 = 1; + int32_t l_153 = 0x11538544; + l_153 = (((int16_t)(((p_15 == 0xCE37C20D) && ((p_14 != p_17) == (((uint8_t)(((g_57 | (((int16_t)l_147 << (int16_t)11) && ((int16_t)g_108 >> (int16_t)2))) && (((((uint8_t)g_124 * (uint8_t)g_56) == 0xF4) > l_152) && p_16)) < p_17) * (uint8_t)p_14) < 0U))) && p_17) << (int16_t)g_26) | g_2); + } + if (g_57) + goto lbl_154; + l_138 = ((uint8_t)(l_140 = (g_2 | (((int32_t)((uint16_t)((uint16_t)((int8_t)((int32_t)g_56 - (int32_t)(((l_117 = ((int16_t)((int8_t)((uint32_t)g_108 - (uint32_t)(1 && ((int8_t)(((~((((255U && 4U) >= ((uint8_t)p_14 * (uint8_t)(((int8_t)p_15 >> (int8_t)4) ^ ((uint8_t)(((-(uint8_t)(((int16_t)(p_14 > l_138) >> (int16_t)l_138) == p_14)) || l_117) & l_185) >> (uint8_t)p_15)))) != 0xBF24FAA9) && l_186)) && 65528U) & 0U) << (int8_t)g_56))) * (int8_t)l_147) * (int16_t)1U)) && (-1)) & g_124)) % (int8_t)l_125) >> (uint16_t)6) - (uint16_t)65535U) - (int32_t)0x622BFC7E) ^ 0x20E4BD68))) + (uint8_t)l_125); + return g_124; +} + + +/* ------------------------------------------ */ +/* + * reads : g_26 g_56 g_5 g_57 + * writes: g_26 g_56 g_57 + */ +static const uint8_t func_24(int32_t p_25) +{ /* block id: 4 */ + const uint32_t l_29 = 0xDB88DCFD; + uint32_t l_60 = 0x6A8B5F6E; + int32_t l_80 = (-1); + int32_t l_104 = (-5); + uint16_t l_105 = 65535U; + for (g_26 = (-23); (g_26 == 21); g_26 += 9) + { /* block id: 7 */ + int16_t l_54 = 0xD42F; + int32_t l_65 = 0x9A4A91A3; + uint32_t l_78 = 0U; + if (p_25) + { /* block id: 8 */ + uint32_t l_30 = 1U; + l_30 = l_29; + for (p_25 = 0; (p_25 < 17); p_25 += 9) + { /* block id: 12 */ + uint32_t l_33 = 0U; + uint32_t l_51 = 0U; + if (((l_33 = 1) ^ ((int8_t)0x5C + (int8_t)(+l_30)))) + { /* block id: 14 */ + uint32_t l_52 = 0x0A82B5DF; + int32_t l_53 = 1; + for (l_33 = (-30); (l_33 == 7); l_33++) + { /* block id: 17 */ + l_53 = (l_52 = ((int16_t)((int16_t)(((int8_t)0xEE + (int8_t)0x3F) && ((g_26 && ((((int32_t)(l_30 > g_26) + (int32_t)((int16_t)(0x35D5 < ((uint8_t)0x0B << (uint8_t)2)) + (int16_t)l_29)) > p_25) != l_51)) < g_26)) << (int16_t)6) + (int16_t)0x9BA1)); + if (g_26) + continue; + } + return p_25; + } + else + { /* block id: 23 */ + int32_t l_55 = 0x1FE01AF0; + g_56 = (l_55 = l_54); + return g_56; + } + } + if (p_25) + continue; + p_25 = (((l_30 <= (((0xD6AE4D7D >= l_54) | ((g_57 = g_5) == ((uint16_t)l_29 - (uint16_t)p_25))) == l_60)) >= ((((int16_t)p_25 >> (int16_t)g_56) != p_25) >= l_30)) == l_30); + } + else + { /* block id: 32 */ + int32_t l_77 = 0; + int32_t l_79 = 0xE18B70F7; + l_65 = ((uint8_t)(g_57 = 0x03) << (uint8_t)g_26); + l_80 = (((l_79 = ((l_65 = (0x7C && l_65)) > (((uint8_t)(g_57 = 250U) >> (uint8_t)(5 ^ (0x08 & (((p_25 >= (((uint8_t)(0x59A0 <= ((+(0xB1 | (((uint16_t)((int8_t)l_29 >> (int8_t)p_25) * (uint16_t)g_26) <= l_77))) >= p_25)) * (uint8_t)l_77) != g_56)) ^ l_77) != l_78)))) < g_56))) != 0xF3BEA0A5) == p_25); + } + } + p_25 = (0 | ((((int16_t)(((int8_t)p_25 % (int8_t)((int8_t)(~0x0E) - (int8_t)((int16_t)0x56B3 * (int16_t)((uint16_t)0U << (uint16_t)13)))) || g_5) - (int16_t)(-10)) || (((int16_t)(l_80 = (0 < (((-(uint16_t)(!(((((int8_t)((int8_t)(((int8_t)(((int16_t)((g_57 == p_25) != p_25) * (int16_t)l_29) >= 0x2441043A) - (int8_t)3) && p_25) % (int8_t)255U) - (int8_t)g_26) | l_60) > (-1)) || g_56))) && 0x84107A49) >= l_104))) * (int16_t)65535U) <= 0xE8DF01DD)) > l_105)); + for (g_26 = (-22); (g_26 <= 30); ++g_26) + { /* block id: 45 */ + p_25 = g_56; + } + return p_25; +} + + + + +/* ---------------------------------------- */ +int main (void) +{ + int print_hash_value = 0; + platform_main_begin(); + func_1(); + csmith_sink_ = g_2; + csmith_sink_ = g_3; + csmith_sink_ = g_5; + csmith_sink_ = g_26; + csmith_sink_ = g_56; + csmith_sink_ = g_57; + csmith_sink_ = g_108; + csmith_sink_ = g_112; + csmith_sink_ = g_124; + csmith_sink_ = g_196; + csmith_sink_ = g_208; + csmith_sink_ = g_210; + csmith_sink_ = g_216; + platform_main_end(0,0); + return 0; +} + +/************************ statistics ************************* +XXX max struct depth: 0 +breakdown: + depth: 0, occurrence: 46 +XXX total union variables: 0 + +XXX max expression depth: 40 +breakdown: + depth: 1, occurrence: 33 + depth: 2, occurrence: 6 + depth: 3, occurrence: 1 + depth: 4, occurrence: 1 + depth: 13, occurrence: 1 + depth: 14, occurrence: 1 + depth: 15, occurrence: 1 + depth: 19, occurrence: 1 + depth: 20, occurrence: 1 + depth: 23, occurrence: 1 + depth: 24, occurrence: 1 + depth: 26, occurrence: 1 + depth: 27, occurrence: 1 + depth: 33, occurrence: 1 + depth: 36, occurrence: 1 + depth: 40, occurrence: 1 + +XXX total number of pointers: 0 + +XXX times a non-volatile is read: 187 +XXX times a non-volatile is write: 50 +XXX times a volatile is read: 8 +XXX times read thru a pointer: 0 +XXX times a volatile is write: 1 +XXX times written thru a pointer: 0 +XXX times a volatile is available for access: 23 +XXX percentage of non-volatile access: 96.3 + +XXX forward jumps: 0 +XXX backward jumps: 1 + +XXX stmts: 35 +XXX max block depth: 5 +breakdown: + depth: 0, occurrence: 18 + depth: 1, occurrence: 4 + depth: 2, occurrence: 6 + depth: 3, occurrence: 1 + depth: 4, occurrence: 4 + depth: 5, occurrence: 2 + +XXX percentage a fresh-made variable is used: 19.2 +XXX percentage an existing variable is used: 80.8 +********************* end of statistics **********************/ + diff --git a/src/fixpoint.cpp b/src/fixpoint.cpp index 5150ac0..5abe447 100644 --- a/src/fixpoint.cpp +++ b/src/fixpoint.cpp @@ -11,7 +11,7 @@ #include "value_set.h" #include "simple_interval.h" #include "normalized_conjunction.h" -#include "affine_relation.h" +#include "linear_subspace.h" #include "fixpoint_widening.cpp" #include "hash_utils.h" @@ -352,7 +352,7 @@ bool AbstractInterpretationPass::runOnModule(llvm::Module& M) { // Use either the standard fixpoint algorithm or the version with widening // executeFixpointAlgorithm(M); // executeFixpointAlgorithm(M); - executeFixpointAlgorithm(M); + executeFixpointAlgorithm(M); // executeFixpointAlgorithmWidening(M); // We never change anything diff --git a/src/general.h b/src/general.h index 09854a0..6fe9a6e 100644 --- a/src/general.h +++ b/src/general.h @@ -1,5 +1,6 @@ #pragma once #include "llvm/ADT/Hashing.h" +#include "llvm/IR/CFG.h" // C++ unordered_maps don't support tuples as keys, which is why one has to define the hash function for said tuple. // If weird behaviours are observed, chances are high, that this hash function is not working properly, as we don't know @@ -14,8 +15,15 @@ namespace std { llvm::hash_code hash_code = llvm::hash_combine(std::get<0>(k), std::get<1>(k)); return hash_code; } - }; - + }; + +template +struct hash> { + std::size_t operator()(const std::pair &pair) const { + return llvm::hash_combine(pair.first, pair.second); + } +}; + } namespace pcpo { diff --git a/src/hash_utils.h b/src/hash_utils.h index 8433038..66dc876 100644 --- a/src/hash_utils.h +++ b/src/hash_utils.h @@ -16,9 +16,9 @@ struct hash> { } }; -template -struct hash> { - size_t operator()(pair const& in) const { +template +struct hash> { + size_t operator()(pair const& in) const { return llvm::hash_value(in); } }; diff --git a/src/linear_equality.h b/src/linear_equality.h index 41ae22f..a6f2451 100644 --- a/src/linear_equality.h +++ b/src/linear_equality.h @@ -55,6 +55,7 @@ public: }; bool isConstant() const { return x == nullptr; }; + bool isTrivial() const { return x == y; }; }; llvm::raw_ostream& operator<<(llvm::raw_ostream& os, LinearEquality a); diff --git a/src/affine_relation.cpp b/src/linear_subspace.cpp similarity index 67% rename from src/affine_relation.cpp rename to src/linear_subspace.cpp index c1286d9..82a822f 100644 --- a/src/affine_relation.cpp +++ b/src/linear_subspace.cpp @@ -1,4 +1,4 @@ -#include "affine_relation.h" +#include "linear_subspace.h" #include "global.h" #include "llvm/IR/CFG.h" @@ -14,13 +14,13 @@ namespace pcpo { // MARK: - Initializers -AffineRelation::AffineRelation(Function const& func) { +LinearSubspace::LinearSubspace(Function const& func) { index = createVariableIndexMap(func); - basis = {Matrix(getNumberOfVariables() + 1)}; - isBottom = basis.empty(); + basis = {MatrixType(getNumberOfVariables() + 1)}; + isBottom = true; } -AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const& state, CallInst const* call) { +LinearSubspace::LinearSubspace(Function const* callee_func, LinearSubspace const& state, CallInst const* call) { assert(callee_func->arg_size() == call->getNumArgOperands()); index = state.index; basis = state.basis; @@ -33,16 +33,14 @@ AffineRelation::AffineRelation(Function const* callee_func, AffineRelation const } else { affineAssignment(&arg, 1, value, 0); } - } else { - nonDeterminsticAssignment(&arg); } } - isBottom = basis.empty(); + isBottom = true; } // MARK: - AbstractState Interface -void AffineRelation::applyPHINode(BasicBlock const& bb, vector const& pred_values, Instruction const& phi) { +void LinearSubspace::applyPHINode(BasicBlock const& bb, vector const& pred_values, Instruction const& phi) { PHINode const* phiNode = dyn_cast(&phi); int i = 0; @@ -51,11 +49,11 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector c auto& incoming_state = pred_values[i]; // Predecessor states should have been merged before. This is just bookkeeping. if (llvm::ConstantInt const* c = llvm::dyn_cast(&incoming_value)) { - AffineRelation acc = *this; + LinearSubspace acc = *this; acc.affineAssignment(&phi, 1, nullptr, c->getSExtValue()); merge(Merge_op::UPPER_BOUND, acc); } else { - AffineRelation acc = *this; + LinearSubspace acc = *this; for (auto m: incoming_state.basis) { auto val = m.column(index.at(&incoming_value)); acc.affineAssignment(&phi, 1, &incoming_value, 0); @@ -66,27 +64,34 @@ void AffineRelation::applyPHINode(BasicBlock const& bb, vector c } } -void AffineRelation::applyCallInst(Instruction const& inst, BasicBlock const* end_block, AffineRelation const& callee_state) { +void LinearSubspace::applyCallInst(Instruction const& inst, BasicBlock const* end_block, LinearSubspace const& callee_state) { // State changes from function call were not merged with the oredecessors. // So we have to do more than just bookkeeping. //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(&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); - } else { - dbgs(4) << " Return not evaluated, setting to bottom\n"; - } - } + + if (callee_state.isBottom) { + isBottom = true; + } else { + basis = callee_state.basis; } + +// for (Instruction const& iter_inst: *end_block) { +// if (ReturnInst const* ret_inst = llvm::dyn_cast(&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); +// } else { +// dbgs(4) << " Return not evaluated, setting to bottom\n"; +// } +// } +// } } -void AffineRelation::applyReturnInst(Instruction const& inst) { +void LinearSubspace::applyReturnInst(Instruction const& inst) { Value const* ret_val = dyn_cast(&inst)->getReturnValue(); if (ret_val && ret_val->getType()->isIntegerTy()) { if (ConstantInt const* c = dyn_cast(ret_val)) { @@ -99,7 +104,7 @@ void AffineRelation::applyReturnInst(Instruction const& inst) { } } -void AffineRelation::applyDefault(Instruction const& inst) { +void LinearSubspace::applyDefault(Instruction const& inst) { if (inst.getNumOperands() != 2) return nonDeterminsticAssignment(&inst); // We only deal with integer types @@ -130,41 +135,49 @@ void AffineRelation::applyDefault(Instruction const& inst) { } } -bool AffineRelation::merge(Merge_op::Type op, AffineRelation const& other) { - if (other.isBottom) { +bool LinearSubspace::merge(Merge_op::Type op, LinearSubspace const& other) { + index.insert(other.index.begin(), other.index.end()); + + if (isBottom && other.isBottom) { + basis = other.basis; return false; - } else if (isBottom) { + } else if (isBottom && !other.isBottom) { basis = other.basis; - index = other.index; isBottom = false; return true; - } - - switch (op) { - case Merge_op::UPPER_BOUND: return leastUpperBound(other); - default: abort(); + } else if (!isBottom && other.isBottom) { + return false; + } else if (!isBottom && !other.isBottom) { + switch (op) { + case Merge_op::UPPER_BOUND: return leastUpperBound(other); + default: abort(); + } } } // MARK: - Lattice Operations -bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { +bool LinearSubspace::leastUpperBound(LinearSubspace const& rhs) { assert(getNumberOfVariables() == rhs.getNumberOfVariables()); - vector> before = basis; + vector before = basis; vector> vectors; vectors.reserve(basis.size() + rhs.basis.size()); - for (Matrix m: basis) { + for (MatrixType m: basis) { vectors.push_back(m.toVector()); } - for (Matrix m: rhs.basis) { + for (MatrixType 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 combined = Matrix(vectors); - Matrix result = Matrix::span(combined, true); - basis = result.reshapeColumns(basis.front().getHeight(), basis.front().getWidth()); + if (vectors.empty()) { + return false; + } + + MatrixType combined = MatrixType(vectors); + MatrixType result = MatrixType::span(combined, true); + + basis = result.reshapeColumns(getHeight(), getWidth()); // FIXME: figure out a better way to detect changes return before != basis; } @@ -172,27 +185,32 @@ bool AffineRelation::leastUpperBound(AffineRelation const& rhs) { // MARK: - Assignments // xi = a1x1 + ... + anxn + a0 -void AffineRelation::affineAssignment(Value const* xi, unordered_map relations, T constant) { - Matrix Wr = Matrix(getNumberOfVariables() + 1); - Wr(index.at(xi),index.at(xi)) = 0; - Wr(0,index.at(xi)) = constant; +void LinearSubspace::affineAssignment(Value const* xi, unordered_map relations, T constant) { + MatrixType Wr = MatrixType(getNumberOfVariables() + 1); + Wr.setValue(index.at(xi),index.at(xi), 0); + Wr.setValue(0,index.at(xi), constant); for (auto [variable, factor]: relations) { - Wr(index.at(variable),index.at(xi)) = factor; + Wr.setValue(index.at(variable),index.at(xi), factor); } // FIXME: this seems quite inefficient - Matrix vector = Matrix(Wr.toVector()); - Matrix vectorSpan = Matrix::span(vector, true); + MatrixType vector = MatrixType(Wr.toVector()); + MatrixType vectorSpan = MatrixType::span(vector, true); Wr = vectorSpan.reshape(Wr.getHeight(), Wr.getWidth()); - for (Matrix& matrix: basis) { + + if (basis.empty()) { + basis.push_back(Wr); + } + + for (MatrixType& matrix: basis) { matrix *= Wr; } } // xi = a * xj + b -void AffineRelation::affineAssignment(Value const* xi, T a, Value const* xj, T b) { +void LinearSubspace::affineAssignment(Value const* xi, T a, Value const* xj, T b) { if (xj == nullptr) { affineAssignment(xi, {}, b); } else { @@ -201,29 +219,34 @@ void AffineRelation::affineAssignment(Value const* xi, T a, Value const* xj, T b } // xi = ? -void AffineRelation::nonDeterminsticAssignment(Value const* xi) { +void LinearSubspace::nonDeterminsticAssignment(Value const* xi) { + return; if (index.count(xi) == 0) return; - Matrix T0 = Matrix(getNumberOfVariables() + 1); - Matrix T1 = Matrix(getNumberOfVariables() + 1); + MatrixType T0 = MatrixType(getNumberOfVariables() + 1); + MatrixType T1 = MatrixType(getNumberOfVariables() + 1); - T0(index.at(xi),index.at(xi)) = 0; - T0(0,index.at(xi)) = 0; + T0.setValue(index.at(xi),index.at(xi), 0); + T0.setValue(0,index.at(xi), 0); - T1(index.at(xi),index.at(xi)) = 0; - T1(0,index.at(xi)) = 1; + T1.setValue(index.at(xi),index.at(xi), 0); + T1.setValue(0,index.at(xi), 1); vector> assignment_vectors; assignment_vectors.push_back(T0.toVector()); assignment_vectors.push_back(T1.toVector()); - Matrix combined = Matrix(assignment_vectors); - Matrix result = Matrix::span(combined, true); + MatrixType combined = MatrixType(assignment_vectors); + MatrixType result = MatrixType::span(combined, true); + + vector span = result.reshapeColumns(T0.getHeight(), T0.getWidth()); - vector> span = result.reshapeColumns(T0.getHeight(), T0.getWidth()); + if (basis.empty()) { + basis = span; + } - for (Matrix& matrix_state: basis) { - for (Matrix const& matrix_assignment: span) { + for (MatrixType& matrix_state: basis) { + for (MatrixType const& matrix_assignment: span) { matrix_state *= matrix_assignment; } } @@ -231,7 +254,7 @@ void AffineRelation::nonDeterminsticAssignment(Value const* xi) { // MARK: - Abstract Operators -void AffineRelation::Add(Instruction const& inst) { +void LinearSubspace::Add(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); @@ -253,7 +276,7 @@ void AffineRelation::Add(Instruction const& inst) { } } -void AffineRelation::Sub(Instruction const& inst) { +void LinearSubspace::Sub(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); @@ -275,7 +298,7 @@ void AffineRelation::Sub(Instruction const& inst) { } } -void AffineRelation::Mul(Instruction const& inst) { +void LinearSubspace::Mul(Instruction const& inst) { auto op1 = inst.getOperand(0); auto op2 = inst.getOperand(1); @@ -329,7 +352,7 @@ unordered_map createVariableIndexMap_impl(Function const& fun return map; } -unordered_map AffineRelation::createVariableIndexMap(Function const& func) { +unordered_map LinearSubspace::createVariableIndexMap(Function const& func) { int count = 0; set visited_funcs = {}; return createVariableIndexMap_impl(func, count, visited_funcs); @@ -345,20 +368,36 @@ unordered_map reverseMap(unordered_map cons return reversed; } -void AffineRelation::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { - out << *this; +void LinearSubspace::print() const { + dbgs(3) << *this; } -void AffineRelation::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { +void LinearSubspace::printIncoming(BasicBlock const& bb, raw_ostream& out, int indentation) const { out << *this; } -void AffineRelation::debug_output(Instruction const& inst, Matrix operands) { +void LinearSubspace::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation) const { + MatrixType nullspace = MatrixType::null(MatrixType(this->basis)); + + auto reversed = reverseMap(index); + for (int i = 1; i <= int(index.size()); i++) { + auto val = reversed.at(i); + if (val->hasName()) { + out << left_justify(val->getName(), 6); + } else { + out << left_justify("<>", 6); + } + } + + out << "\n" << nullspace; +} + +void LinearSubspace::debug_output(Instruction const& inst, MatrixType operands) { dbgs(3) << *this; } -raw_ostream& operator<<(raw_ostream& os, AffineRelation const& relation) { +raw_ostream& operator<<(raw_ostream& os, LinearSubspace const& relation) { auto reversed = reverseMap(relation.index); if (relation.basis.empty()) { return os << "[]\n"; diff --git a/src/affine_relation.h b/src/linear_subspace.h similarity index 64% rename from src/affine_relation.h rename to src/linear_subspace.h index d5fe351..89a12e8 100644 --- a/src/affine_relation.h +++ b/src/linear_subspace.h @@ -3,13 +3,14 @@ #include #include "global.h" -#include "matrix.h" +#include "simple_matrix.h" +#include "sparse_matrix.h" #include namespace pcpo { -class AffineRelation { +class LinearSubspace { private: /// Only valid when `createVariableIndexMap` has been generated. int getNumberOfVariables() const { return index.size(); }; @@ -17,31 +18,35 @@ private: public: /// Type used for Matrix values. using T = double; + using MatrixType = SparseMatrix; std::unordered_map index; - std::vector> basis; + std::vector basis; bool isBottom = true; + int getWidth() { return index.size() + 1; }; + int getHeight() { return index.size() + 1; }; - AffineRelation() = default; - AffineRelation(AffineRelation const& state) = default; + LinearSubspace() = default; + LinearSubspace(LinearSubspace const& state) = default; + virtual ~LinearSubspace() = default; - explicit AffineRelation(llvm::Function const& func); + explicit LinearSubspace(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); + explicit LinearSubspace(llvm::Function const* callee_func, LinearSubspace const& state, llvm::CallInst const* call); /// Handles the evaluation of merging points - void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, llvm::Instruction const& phi); + void applyPHINode(llvm::BasicBlock const& bb, std::vector const& pred_values, llvm::Instruction const& phi); /// Handles the evaluation of function calls /// This is the "combine" function as described in "Compiler Design: Analysis and Transformation" - void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, AffineRelation const& callee_state); + void applyCallInst(llvm::Instruction const& inst, llvm::BasicBlock const* end_block, LinearSubspace const& callee_state); /// Handles the evaluation of return instructions void applyReturnInst(llvm::Instruction const& inst); /// Handles the evaluation of all other instructions void applyDefault(llvm::Instruction const& inst); - bool merge(Merge_op::Type op, AffineRelation const& other); + bool merge(Merge_op::Type op, LinearSubspace const& other); void branch(llvm::BasicBlock const& from, llvm::BasicBlock const& towards) { return; }; - bool leastUpperBound(AffineRelation const& rhs); + bool leastUpperBound(LinearSubspace const& rhs); bool checkOperandsForBottom(llvm::Instruction const& inst) { return false; } @@ -53,6 +58,8 @@ public: void affineAssignment(llvm::Value const* xi, std::unordered_map relations, T constant); void nonDeterminsticAssignment(llvm::Value const* xi); + virtual void print() const; + protected: // Abstract Operators void Add(llvm::Instruction const& inst); @@ -60,12 +67,12 @@ protected: void Mul(llvm::Instruction const& inst); /// Used for debug output - void debug_output(llvm::Instruction const& inst, Matrix operands); + void debug_output(llvm::Instruction const& inst, MatrixType operands); - Matrix createTransformationMatrix(llvm::Instruction const& inst); + MatrixType createTransformationMatrix(llvm::Instruction const& inst); }; -llvm::raw_ostream& operator<<(llvm::raw_ostream& os, AffineRelation const& relation); +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, LinearSubspace const& relation); } diff --git a/src/normalized_conjunction.cpp b/src/normalized_conjunction.cpp index e27e6a5..7c2b8d2 100644 --- a/src/normalized_conjunction.cpp +++ b/src/normalized_conjunction.cpp @@ -645,14 +645,18 @@ void NormalizedConjunction::printIncoming(BasicBlock const& bb, raw_ostream& out } } + void NormalizedConjunction::printOutgoing(BasicBlock const& bb, raw_ostream& out, int indentation = 0) const { + int nrOfNonTrivialEquations = 0; for (auto const& i: values) { if (ReturnInst::classof(i.first)) { out.indent(indentation) << " = " << i.second << '\n'; } else { out.indent(indentation) << '%' << i.first->getName() << " = " << i.second << '\n'; } + nrOfNonTrivialEquations += !i.second.isTrivial(); } + out.indent(indentation) << nrOfNonTrivialEquations << " non-trivial equations\n"; } } /* end of namespace pcpo */ diff --git a/src/matrix.h b/src/simple_matrix.h similarity index 80% rename from src/matrix.h rename to src/simple_matrix.h index f6bc352..a1a9188 100644 --- a/src/matrix.h +++ b/src/simple_matrix.h @@ -4,6 +4,7 @@ #include #include +#include namespace pcpo { @@ -37,6 +38,8 @@ public: Matrix() = default; Matrix(Matrix const& matrix) = default; + virtual ~Matrix() = default; + /// Creates an identity matrix with dimension eye x eye /// @param eye dimension of the matrix @@ -60,7 +63,7 @@ public: this->vectors = vectors; }; - /// Creates a vector from a std::vector + /// Creates a column vector from a std::vector /// @param vector the vector Matrix(std::vector const &vector) { std::vector> vectors = {vector}; @@ -71,21 +74,31 @@ public: Matrix(std::vector const &values, int rows, int columns) { assert(int(values.size()) == rows * columns); - std::vector> result; - result.reserve(rows); + vectors.reserve(rows); for (int row = 0; row < rows; row++) { std::vector rowVector; rowVector.reserve(columns); for (int column = 0; column < columns; column++) { rowVector.push_back(values[row * rows + column]); } - result.push_back(rowVector); + vectors.push_back(rowVector); } - this->vectors = result; this->width = columns; this->height = rows; }; + Matrix(std::vector const &vec) { + assert(all_of(vec.begin(), vec.end(), [&vec](Matrix m){ return m.getWidth() == vec.front().getWidth(); })); + this->height = 0; + int size = std::accumulate(vec.begin(), vec.end(), 0, [](int acc, Matrix m){ return acc + m.getHeight(); }); + vectors.reserve(size); + for (auto const &m: vec) { + vectors.insert(vectors.end(), m.vectors.begin(), m.vectors.end()); + this->height += m.height; + } + this->width = vec.empty() ? 0 : vec.front().width; + } + // MARK: - Properties /// The height of the matrix (number of rows) @@ -108,17 +121,30 @@ public: return result; }; + /// Transposes the matrix in place + void transposed() { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + value(j,i) = value(i,j); + } + } + } + /// Transforms the matrix to reduced row echelon form Matrix echelonForm() const { Matrix result = Matrix(*this); int pivot = 0; for (int row = 0; row < height; row++) { - if (pivot >= width) { return result; } + if (pivot >= width) { + return result; + } int i = row; while (result.value(i,pivot) == 0) { if (++i >= height) { i = row; - if (++pivot >= width) { return result; } + if (++pivot >= width) { + return result; + } } } result.swap_rows(i, row); @@ -164,7 +190,44 @@ public: } /// Computes the null space for the column vectors - static Matrix null(Matrix const& matrix); + static Matrix null(Matrix const& matrix) { + auto rref = matrix.echelonForm(); + std::vector nonPivotColumns; + nonPivotColumns.reserve(matrix.getWidth()); + int offset = 0; + + for (int row = 0; row < rref.getWidth(); row++) { + for (int column = offset; column < rref.getWidth(); column++) { + if ((row < rref.getHeight() && rref(row,column) == 0) || row >= rref.getHeight()) { + std::vector vector(rref.getWidth()); + vector[column] = -1; + rref.vectors.insert(rref.vectors.begin()+row, vector); + nonPivotColumns.push_back(column); + rref.height += 1; + offset += 1; + row += 1; + if (row > rref.getHeight()) { + break; + } + } else if (row < rref.getHeight() && rref(row,column) == 1) { + offset += 1; + break; + } + } + } + + rref.height = rref.getWidth(); + rref.vectors.erase(rref.vectors.begin() + rref.getWidth(), rref.vectors.end()); + + std::vector> columns; + + // pick columns for result + for (auto column: nonPivotColumns) { + columns.push_back(rref.column(column)); + } + + return Matrix(columns).transpose(); + } /// Converts the matrix to a 1D Vector by stacking the column vectors std::vector toVector() const { @@ -183,7 +246,6 @@ public: /// @param columns number of columns Matrix reshape(int rows, int columns) const { assert(rows > 0 && columns > 0); - // FIXME: Performance Matrix t = transpose(); return Matrix(t.vectors.front(), rows, columns).transpose(); }; @@ -212,6 +274,13 @@ public: return vectors[row][column]; }; + /// Sets the value at row i and column j + /// @param row + /// @param column + /// @param value + void setValue(int row, int column, T val) { + value(row,column) = val; + } /// Returns a vector with the elements of the row at index i. The returned row can be modified. /// @param i Index of the row to return. @@ -313,10 +382,10 @@ public: }; bool operator==(Matrix const& rhs) const { - return rhs.vectors == rhs.vectors && width == rhs.width && height == rhs.height; + return vectors == rhs.vectors && width == rhs.width && height == rhs.height; }; - void print() const { + virtual void print() const { dbgs(4) << *this; } @@ -410,6 +479,11 @@ llvm::raw_ostream& operator<<(llvm::raw_ostream& os, Matrix const& matrix) { } os << " ]\n"; } + + if (matrix.getWidth() == 0 && matrix.getHeight() == 0) { + os << "[]\n"; + } + return os; }; diff --git a/src/sparse_matrix.h b/src/sparse_matrix.h new file mode 100644 index 0000000..f050088 --- /dev/null +++ b/src/sparse_matrix.h @@ -0,0 +1,521 @@ +#pragma once + +#include "global.h" +#include "general.h" + +#include +#include +#include +#include + +namespace pcpo { + +/// Matrix. Row and column are indexed beginning at 0 +template class SparseMatrix { +protected: + // (row,column) -> T + std::unordered_map,T> values; + int width; + int height; + bool _transposed = false; + int getEstimatedSize() const { return width * height / 4; }; + +public: + //MARK: - Initializers + /// Creates a matrix with dimensions height x width and initalizes its values to `value` + /// @param width Width of the matrix + /// @param height Height of the matrix + /// @param value Initial value for each element + SparseMatrix(int height, int width, T value) { + this->width = width; + this->height = height; + if (value == 0) return; + values.reserve(height*value); + for (int row = 0; row < height; row++) { + for (int column = 0; column < width; column++) { + values[{row,column}] = value; + } + } + } + + /// Creates a matrix with dimensions height x width and initalizes its values to 0 + /// @param width Width of the matrix + /// @param height Height of the matrix + SparseMatrix(int height, int width) : SparseMatrix(height, width, 0) {}; + + SparseMatrix() = default; + SparseMatrix(SparseMatrix const& matrix) = default; + virtual ~SparseMatrix() = default; + + /// Creates an identity matrix with dimension eye x eye + /// @param eye dimension of the matrix + SparseMatrix(int eye) { + this->width = eye; + this->height = eye; + values.reserve(eye); + for (int i = 0; i < eye; i++) { + values[{i,i}] = 1; + } + } + + /// Creates a matrix from a 2D vector + /// @param vectors 2D vector containing columns with rows + SparseMatrix(std::vector> const &vectors) { + assert(all_of(vectors.begin(), vectors.end(), [&vectors](std::vector vec){ return vec.size() == vectors.front().size(); })); + this->width = vectors.empty() ? 0 : vectors.front().size(); + this->height = vectors.size(); + values.reserve(getEstimatedSize()); + for (int row = 0; row < int(vectors.size()); row++) { + auto rowVector = vectors[row]; + for (auto column = 0; column < int(rowVector.size()); column++) { + T val = rowVector[column]; + if (val != 0) { + values[{row,column}] = val; + } + } + } + } + + SparseMatrix(std::unordered_map,T> const &values, int width, int height) { + this->width = width; + this->height = height; + this->values = values; + } + + /// Creates a column vector from a std::vector + /// @param vector the vector + SparseMatrix(std::vector const &vector) { + this->width = vector.size(); + this->height = vector.empty() ? 0 : 1; + values.reserve(getEstimatedSize()); + for (int column = 0; column < int(vector.size()); column++) { + T val = vector[column]; + if (val != 0) { + values[{0,column}] = val; + } + } + } + + SparseMatrix(std::vector const &vs, int rows, int columns) { + assert(int(vs.size()) == rows * columns); + this->width = columns; + this->height = rows; + values.reserve(getEstimatedSize()); + for (int row = 0; row < rows; row++) { + for (int column = 0; column < columns; column++) { + T val = vs[row * rows + column]; + if (val != 0) { + values[std::pair{row,column}] = val; + } + } + } + } + + SparseMatrix(std::vector const &vec) { + assert(all_of(vec.begin(), vec.end(), [&vec](SparseMatrix m){ return m.getWidth() == vec.front().getWidth(); })); + std::unordered_map,T> val; + this->height = 0; + int estimated_size = std::accumulate(vec.begin(), vec.end(), 0, [](int acc, SparseMatrix m){ return acc + m.getEstimatedSize(); }); + values.reserve(estimated_size); + for (auto m: vec) { + for (auto const [key, value]: m.values) { + if (m._transposed) { + values[{key.second + this->height, key.first}] = value; + } else { + values[{key.first + this->height, key.second}] = value; + } + } + this->height += m.getHeight(); + } + this->width = vec.empty() ? 0 : vec.front().width; + } + + // MARK: - Properties + + /// The height of the matrix (number of rows) + int getHeight() const { return height; }; + /// The width of the matrix (number of columns) + int getWidth() const { return width; }; + /// Returns true when the matrix is empty + bool empty() const { return getWidth() == 0 && getHeight() == 0; }; + + // MARK: - Matrix operations + + /// Returns the transposed matrix + SparseMatrix transpose() const { + SparseMatrix result = SparseMatrix(*this); + result.transposed(); + return result; + } + + /// Transposes the matrix + void transposed() { + _transposed = !_transposed; + int oldWildth = width; + width = height; + height = oldWildth; + } + + /// Transforms the matrix to reduced row echelon form + SparseMatrix echelonForm() const { + SparseMatrix result = SparseMatrix(*this); + int pivot = 0; + for (int row = 0; row < height; row++) { + if (pivot >= width) { return result; } + int i = row; + while (result.value(i,pivot) == 0) { + if (++i >= height) { + i = row; + if (++pivot >= width) { return result; } + } + } + result.swap_rows(i, row); + result.divide_row(row, result.value(row,pivot)); + for (i = 0; i < height; i++) { + if (i != row) { + result.add_multiple_row(i, row, -result.value(i,pivot)); + } + } + } + return result; + } + + /// The rank of the matrix + int getRank() const { + SparseMatrix e = echelonForm(); + int rank = 0; + for (int row = 0; row < height; row++) { + for (int column = 0; column < width; column++) { + if ((e.value(row,column) == 0) && (column == width - 1)) { + return rank; + } else if (e.value(row,column) != 0) { + break; + } + } + rank++; + } + return rank; + } + + /// Basis of the linear span of the column vectors + static SparseMatrix span(SparseMatrix const& matrix, bool transposed = false) { + std::vector> rows; + SparseMatrix t = transposed ? matrix : matrix.transpose(); + SparseMatrix te = t.echelonForm(); + int rank = te.getRank(); + rows.reserve(rank); + for (int row = 0; row < rank; row++) { + rows.push_back(te.row(row)); + } + return SparseMatrix(rows).transpose(); + } + + /// Computes the null space for the column vectors + static SparseMatrix null(SparseMatrix const& matrix) { + auto rref = matrix.echelonForm(); + + std::unordered_map,T> result; + result.reserve(matrix.getEstimatedSize()); + int index = 0; + int offset = 0; + std::unordered_map index_map; + index_map.reserve(matrix.getWidth()); + + for (int row = 0; row < rref.getWidth(); row++) { + for (int column = offset; column < rref.getWidth(); column++) { + if (row < rref.getHeight() && rref.value(row,column) == 1) { + offset += 1; + break; + } else if ((row < rref.getHeight() && rref.value(row,column) == 0) || row >= rref.getHeight()) { + // insert -1 + result[{column,index}] = -1; + index_map[column] = true; + + // copy everyting above -1. Below is guranteed to be zero due to rref. + int of = 0; + for (int i = 0; i < std::min(row,rref.getHeight()) + of; i++) { + if (index_map.count(i) == 0) { + T value = rref.value(i-of,column); + if (value != 0) { + result[{i,index}] = value; + } + } else { + of += 1; + } + } + + index += 1; + offset += 1; + if (row >= rref.getHeight()) { + break; + } + } + } + } + return SparseMatrix(result,index,result.empty() ? 0 : rref.getWidth()); + } + + /// Converts the matrix to a 1D Vector by stacking the column vectors + std::vector toVector() const { + std::vector result; + result.reserve(getWidth() * getHeight()); + for (int column = 0; column < getWidth(); column++) { + for (int row = 0; row < getHeight(); row++) { + result.push_back(value(row,column)); + } + } + return result; + } + + /// Converts a 1D Vector to a Matrix with given dimensions + /// @param rows number of rows + /// @param columns number of columns + SparseMatrix reshape(int rows, int columns) const { + assert(rows > 0 && columns > 0); + return SparseMatrix(column(0), rows, columns).transpose(); + } + + std::vector> reshapeColumns(int height, int width) const { + std::vector> result; + result.reserve(getWidth()); + for (int c = 0; c < getWidth(); c++) { + result.push_back(SparseMatrix(column(c), height, width).transpose()); + } + return result; + } + + /// Sets the value at row i and column j + /// @param row + /// @param column + /// @param value + void setValue(int row, int column, T value) { + auto index = _transposed ? std::pair{column,row} : std::pair{row,column}; + if (value != 0) { + values[index] = value; + } else { + values.erase(index); + } + } + + /// Returns the value at row i and column j + /// @param row + /// @param column + T value(int row, int column) const { + assert(row < height && column < width); + auto index = _transposed ? std::pair{column,row} : std::pair{row,column}; + auto it = values.find(index); + + if (it == values.end()) { + return 0; + } + return it->second; + } + + /// Returns a vector with the elements of the row at index i. The returned row cannot be modified. + /// @param i Index of the row to return. + std::vector row(int i) { + assert(i < height); + std::vector result; + result.reserve(width); + for (int column = 0; column < width; column++) { + result.push_back(value(i,column)); + } + return result; + } + + /// Returns the column at index i. The returned column cannot be modified. + /// @param i Index of the column to return + std::vector column(int i) const { + assert(i < width); + std::vector result; + result.reserve(height); + for (int row = 0; row < height; row++) { + result.push_back(value(row,i)); + } + return result; + } + + void setColumn(std::vector const& vector, int column) { + assert(int(vector.size()) == height && column < width); + for (int row = 0; row < height; row++) { + value(row,column) = vector[row]; + } + } + + // MARK: - Operators + + T operator()(int i, int j) { return value(i,j); }; + + SparseMatrix& operator *=(SparseMatrix const& rhs) { + assert(width == rhs.height); + SparseMatrix result = SparseMatrix(height,rhs.width); + for (int i = 0; i < height; i++) { + for (int k = 0; k < width; k++) { + for (int j = 0; j < rhs.width; j++) { + T val = result.value(i,j) + value(i,k) * rhs.value(k,j); + result.setValue(i,j, val); + } + } + } + this->values = result.values; + this->width = result.width; + this->height = result.height; + this->_transposed = result._transposed; + return *this; + } + + SparseMatrix& operator *=(T scalar) { + for (T& value : values) { + value *= scalar; + } + return *this; + } + + SparseMatrix& operator +=(SparseMatrix const& rhs) { + assert(rhs.width == width && rhs.height == height); + for (int i=0;i& operator +=(T scalar) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + setValue(i,j, value(i,j) + scalar); + } + } + return *this; + } + + SparseMatrix& operator -=(SparseMatrix const& rhs) { + assert(rhs.width == width && rhs.height == height); + for (int i = 0; i < height; i++) { + for (int j= 0; j < width; j++) { + setValue(i,j, value(i,j) - rhs.value(i,j)); + } + } + return *this; + } + + SparseMatrix& operator -=(int scalar) { + for (int i = 0; i < height; i++) { + for (int j = 0; j < width; j++) { + setValue(i,j, value(i,j) - scalar); + } + } + return *this; + } + + bool operator==(SparseMatrix const& rhs) const { + if (rhs._transposed != _transposed && width == rhs.width && height == rhs.height) { + for (int row = 0; row < getHeight(); row++) { + for (int column = 0; column < getWidth(); column++) { + if (value(row, column) != rhs.value(row,column)) { + return false; + } + } + } + return true; + } + return values == rhs.values && width == rhs.width && height == rhs.height; + } + + virtual void print() const { + dbgs(4) << *this; + } + +protected: + + // MARK: - Echelon helpers + + /// Swaps two rows + /// @param a index of the first row + /// @param b index of the second row + void swap_rows(int a, int b) { + for (int column = 0; column < width; column++) { + auto nha = values.extract({a,column}); + auto nhb = values.extract({b,column}); + // check if node handle is not empty + if (!nha.empty()) { + nha.key() = {b,column}; + values.insert(std::move(nha)); + } + if (!nhb.empty()) { + nhb.key() = {a,column}; + values.insert(std::move(nhb)); + } + } + } + + /// Divides a row by a constant + /// @param row index of the row to divide + /// @param quotient quotient to divide the row by + void divide_row(int row, T quotient) { + for (int column = 0; column < width; column++) { + if (values.count({row,column}) != 0) { + setValue(row, column, value(row,column) / quotient); + } + } + } + + /// Adds a multiple of row b to a row a + /// @param a Row to add a multiple of b to + /// @param b Row to be added to row a + /// @param factor Factor to multiply row b with when adding it to row a + void add_multiple_row(int a, int b, T factor) { + for (int column = 0; column < width; column++) { + if (values.count({b,column}) != 0) { + setValue(a,column, value(a,column) + value(b,column) * factor); + } + } + } +}; + + +template +inline SparseMatrix operator*(SparseMatrix lhs, SparseMatrix const& rhs) { return lhs *= rhs; }; +template +inline SparseMatrix operator*(SparseMatrix lhs, T scalar) { return lhs *= scalar; }; +template +inline SparseMatrix operator+(SparseMatrix lhs, SparseMatrix const& rhs) { return lhs += rhs; }; +template +inline SparseMatrix operator+(SparseMatrix lhs, T scalar) { return lhs += scalar; }; +template +inline SparseMatrix operator-(SparseMatrix lhs, SparseMatrix const& rhs) { return lhs -= rhs; }; +template +inline SparseMatrix operator-(SparseMatrix lhs, T scalar) { return lhs -= scalar; }; + +template +llvm::raw_ostream& operator<<(llvm::raw_ostream& os, SparseMatrix const& matrix) { + for (int row = 0; row < matrix.getHeight(); row++) { + os << "[ "; + for (int column = 0; column < matrix.getWidth(); column++) { + if constexpr (std::is_floating_point_v) { + if (column == matrix.getWidth() - 1) { + os << llvm::format("%g", matrix.value(row,column)); + } else { + os << llvm::format("%-6g", matrix.value(row,column)); + } + } else { + if (column == matrix.getWidth() - 1) { + os << llvm::format("%d", matrix.value(row,column)); + } else { + os << llvm::format("%-6d", matrix.value(row,column)); + } + } + } + os << " ]\n"; + } + + if (matrix.getWidth() == 0 && matrix.getHeight() == 0) { + os << "[]\n"; + } + + return os; +}; + +} + diff --git a/test/affine_relation_test.cpp b/test/affine_relation_test.cpp deleted file mode 100644 index 8867f00..0000000 --- a/test/affine_relation_test.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include -#include -#include -#include -#include - -#include "../src/affine_relation.h" - -using namespace std; -using namespace pcpo; - -class AffineRelationTest: AffineRelation { - -public: - static bool runTestLeastUpperBound1(); - static bool runTestLeastUpperBound2(); -}; - -bool AffineRelationTest::runTestLeastUpperBound1() { - std::cout << "Testing least upper bound 1: "; - bool result = false; - - AffineRelation r1 = AffineRelation(); - r1.isBottom = false; - r1.basis = {Matrix(4)}; - - AffineRelation r2 = AffineRelation(); - r2.isBottom = false; - r2.basis = {Matrix(4)}; - - AffineRelation expected = AffineRelation(); - expected.basis = {Matrix(4)}; - - - auto actual = r1.leastUpperBound(r2); - - result = r1.basis == expected.basis && !actual && !r1.isBottom; - - std::cout << (result? "success" : "failed") << "\n"; - return result; -} - -bool AffineRelationTest::runTestLeastUpperBound2() { - std::cout << "Testing least upper bound 2: "; - bool result = false; - - AffineRelation r1 = AffineRelation(); - r1.isBottom = false; - Matrix b1 = Matrix(4); - b1(0,1) = 1; - b1(2,1) = 1; - r1.basis = {b1}; - - AffineRelation r2 = AffineRelation(); - r2.isBottom = false; - Matrix b2 = Matrix(4); - b2(0,3) = 1; - r2.basis = {b2}; - - AffineRelation expected = AffineRelation(); - Matrix e1 = Matrix(4); - e1(0,3) = 1; - Matrix e2 = Matrix(4); - e2(0,0) = 0; - e2(1,1) = 0; - e2(2,2) = 0; - e2(3,3) = 0; - e2(0,1) = 1; - e2(2,1) = 1; - e2(0,3) = -1; - expected.basis = {e1, e2}; - - - auto actual = r1.leastUpperBound(r2); - - result = r1.basis == expected.basis && actual && !r1.isBottom; - - std::cout << (result? "success" : "failed") << "\n"; - return result; -} - - -int main() { - return !(AffineRelationTest::runTestLeastUpperBound1() - && AffineRelationTest::runTestLeastUpperBound2() - ); -}; - - diff --git a/test/linear_subspace_test.cpp b/test/linear_subspace_test.cpp new file mode 100644 index 0000000..34f251f --- /dev/null +++ b/test/linear_subspace_test.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include + +#include "../src/linear_subspace.h" + +using namespace std; +using namespace pcpo; + +class LinearSubspaceTest: LinearSubspace { + +public: + static bool runTestLeastUpperBound1(); + static bool runTestLeastUpperBound2(); +}; + +bool LinearSubspaceTest::runTestLeastUpperBound1() { + std::cout << "Testing least upper bound 1: "; + bool result = false; + + LinearSubspace r1 = LinearSubspace(); + r1.isBottom = false; + r1.basis = {MatrixType(4)}; + + LinearSubspace r2 = LinearSubspace(); + r2.isBottom = false; + r2.basis = {MatrixType(4)}; + + LinearSubspace expected = LinearSubspace(); + expected.basis = {MatrixType(4)}; + + + auto actual = r1.leastUpperBound(r2); + + result = r1.basis == expected.basis && !actual && !r1.isBottom; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +bool LinearSubspaceTest::runTestLeastUpperBound2() { + std::cout << "Testing least upper bound 2: "; + bool result = false; + + LinearSubspace r1 = LinearSubspace(); + r1.isBottom = false; + MatrixType b1 = MatrixType(4); + b1.setValue(0,1, 1); + b1.setValue(2,1, 1); + r1.basis = {b1}; + + LinearSubspace r2 = LinearSubspace(); + r2.isBottom = false; + MatrixType b2 = MatrixType(4); + b2.setValue(0,3, 1); + r2.basis = {b2}; + + LinearSubspace expected = LinearSubspace(); + MatrixType e1 = MatrixType(4); + e1.setValue(0,3, 1); + MatrixType e2 = MatrixType(4); + e2.setValue(0,0, 0); + e2.setValue(1,1, 0); + e2.setValue(2,2, 0); + e2.setValue(3,3, 0); + e2.setValue(0,1, 1); + e2.setValue(2,1, 1); + e2.setValue(0,3, -1); + expected.basis = {e1, e2}; + + + auto actual = r1.leastUpperBound(r2); + + result = r1.basis == expected.basis && actual && !r1.isBottom; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + + +int main() { + return !(LinearSubspaceTest::runTestLeastUpperBound1() + && LinearSubspaceTest::runTestLeastUpperBound2() + ); +}; + + diff --git a/test/matrix_test.cpp b/test/simple_matrix_test.cpp similarity index 65% rename from test/matrix_test.cpp rename to test/simple_matrix_test.cpp index 360bfb6..aad3980 100644 --- a/test/matrix_test.cpp +++ b/test/simple_matrix_test.cpp @@ -4,7 +4,7 @@ #include #include -#include "../src/matrix.h" +#include "../src/simple_matrix.h" using namespace std; using namespace pcpo; @@ -24,6 +24,11 @@ public: static bool runTestRank2(); static bool runTestRank3(); static bool runTestSpan1(); + static bool runTestNull1(); + static bool runTestNull2(); + static bool runTestNull3(); + static bool runTestNull4(); + static bool runTestNull5(); }; template @@ -78,11 +83,11 @@ bool MatrixTest::runTestMul2() { }; std::vector> expected = { - {1519,87,132,87}, - {1798,139,144,114}, - {2077,191,156,141}, - {2356,243,168,168}, - {2347,295,-132,339} + {1519,98,132,87}, + {1798,151,144,114}, + {2077,204,156,141}, + {2356,257,168,168}, + {2347,286,-132,339} }; auto actual = Matrix(a) * Matrix(b); @@ -127,9 +132,9 @@ bool MatrixTest::runTestTranspose2() { bool result = false; std::vector> a = { - {1,3}, - {2,4}, - {3,5} + {1,4}, + {2,5}, + {3,6} }; std::vector> expected = { @@ -306,9 +311,9 @@ bool MatrixTest::runTestSpan1() { }; std::vector> expected = { - {1,1}, + {1,0}, {0,1}, - {1,0} + {1,-1} }; auto matrix = Matrix(a); @@ -321,6 +326,159 @@ bool MatrixTest::runTestSpan1() { return result; } +template +bool MatrixTest::runTestNull1() { + std::cout << "Testing nullspace 1: "; + bool result = false; + + + std::vector> a = { + {1,0,0}, + {0,1,0}, + {0,0,1} + }; + + auto matrix = Matrix(a); + auto actual = Matrix::null(matrix); + Matrix expected = Matrix({}); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestNull2() { + std::cout << "Testing nullspace 2: "; + bool result = false; + + + std::vector> a = { + {1,-10, -24, -42}, + {1,-8,-18,-32}, + {-2,20,51,87} + }; + + std::vector> b = { + {2}, + {2}, + {1}, + {-1} + }; + + auto matrix = Matrix(a); + auto actual = Matrix::null(matrix); + auto expected = Matrix(b); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestNull3() { + std::cout << "Testing nullspace 3: "; + bool result = false; + + + std::vector> a = { + {0,1,0,0,-2,-13}, + {0,0,0,1, 2, 5}, + {0,0,1,0, 1, 9} + }; + + std::vector> b = { + {-1, 0, 0}, + {0, -2, -13}, + {0, 1, 9}, + {0, 2, 5}, + {0, -1, 0}, + {0, 0, -1} + }; + + auto matrix = Matrix(a); + auto actual = Matrix::null(matrix); + auto expected = Matrix(b); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestNull4() { + std::cout << "Testing nullspace 4: "; + bool result = false; + + + std::vector> a = { + {0,0,1,0,0,0,0,-2,-13}, + {0,0,0,0,0,0,1, 2, 5}, + {0,0,0,0,0,1,0, 1, 9} + }; + + std::vector> b = { + {-1,0,0,0, 0, 0}, + {0,-1,0,0, 0, 0}, + {0,0,0,0, -2,-13}, + {0,0,-1,0, 0, 0}, + {0,0,0,-1, 0, 0}, + {0,0,0,0,1,9}, + {0,0,0,0,2,5}, + {0,0,0,0,-1, 0}, + {0,0,0,0, 0, -1} + }; + + auto matrix = Matrix(a); + auto actual = Matrix::null(matrix); + auto expected = Matrix(b); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool MatrixTest::runTestNull5() { + std::cout << "Testing nullspace 5: "; + bool result = false; + + + std::vector> a = { + {0,1,1}, + {0,0,1}, + {0,0,0} + }; + + std::vector> b = { + {0,0,0}, + {0,0,1}, + {0,0,0} + }; + + std::vector> ans = { + {-1}, + {0}, + {0} + }; + + + auto A = Matrix(a); + auto B = Matrix(b); + auto actual = Matrix::null(Matrix(std::vector{A,B})); + auto expected = Matrix(ans); + + result = actual == Matrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + + int main() { return !(MatrixTest::runTestMul1() @@ -344,6 +502,11 @@ int main() { && MatrixTest::runTestRank2() && MatrixTest::runTestRank3() && MatrixTest::runTestSpan1() + && MatrixTest::runTestNull1() + && MatrixTest::runTestNull2() + && MatrixTest::runTestNull3() + && MatrixTest::runTestNull4() + && MatrixTest::runTestNull5() ); }; diff --git a/test/sparse_matrix_test.cpp b/test/sparse_matrix_test.cpp new file mode 100644 index 0000000..53162cb --- /dev/null +++ b/test/sparse_matrix_test.cpp @@ -0,0 +1,515 @@ +#include +#include +#include +#include +#include + +#include "../src/sparse_matrix.h" + +using namespace std; +using namespace pcpo; + +template +class SparseMatrixTest: SparseMatrix { + +public: + static bool runTestMul1(); + static bool runTestMul2(); + static bool runTestTranspose1(); + static bool runTestTranspose2(); + static bool runTestEchelon1(); + static bool runTestEchelon2(); + static bool runTestEchelon3(); + static bool runTestRank1(); + static bool runTestRank2(); + static bool runTestRank3(); + static bool runTestSpan1(); + static bool runTestNull1(); + static bool runTestNull2(); + static bool runTestNull3(); + static bool runTestNull4(); + static bool runTestNull5(); +}; + +template +bool SparseMatrixTest::runTestMul1() { + std::cout << "Testing multiplication 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + std::vector> b = { + {4,29,0}, + {-1,27,2}, + {100,5,3} + }; + + std::vector> expected = { + {700,172,29}, + {803,233,34}, + {906,294,39} + }; + + auto actual = SparseMatrix(a) * SparseMatrix(b); + + auto x = SparseMatrix(expected); + result = actual == x; + + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestMul2() { + std::cout << "Testing multiplication 2: "; + bool result = false; + + std::vector> a = { + {1,6,11}, + {2,7,12}, + {3,8,13}, + {4,9,14}, + {5,10,-9} + }; + + std::vector> b = { + {43,45,1,9}, + {224,7,-2,24}, + {12,1,13,-6} + }; + + std::vector> expected = { + {1519,98,132,87}, + {1798,151,144,114}, + {2077,204,156,141}, + {2356,257,168,168}, + {2347,286,-132,339} + }; + + auto actual = SparseMatrix(a) * SparseMatrix(b); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestTranspose1() { + std::cout << "Testing transpose 1: "; + bool result = false; + + std::vector> a = { + {1,2,3}, + {4,5,6}, + {7,8,9} + }; + + std::vector> expected = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + auto matrix = SparseMatrix(a); + + auto actual = matrix.transpose(); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestTranspose2() { + std::cout << "Testing transpose 2: "; + bool result = false; + + std::vector> a = { + {1,4}, + {2,5}, + {3,6} + }; + + std::vector> expected = { + {1,2,3}, + {4,5,6} + }; + + auto matrix = SparseMatrix(a); + auto actual = matrix.transpose(); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestEchelon1() { + std::cout << "Testing echelon 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + std::vector> expected = { + {1,0,-1}, + {0,1,2}, + {0,0,0} + }; + + auto matrix = SparseMatrix(a); + auto actual = matrix.echelonForm(); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestEchelon2() { + std::cout << "Testing echelon 2: "; + bool result = false; + + std::vector> a = { + {1,2,1}, + {1,4,8}, + {1,6,3} + }; + + std::vector> expected = { + {1,0,0}, + {0,1,0}, + {0,0,1} + }; + + auto matrix = SparseMatrix(a); + auto actual = matrix.echelonForm(); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestEchelon3() { + std::cout << "Testing echelon 3: "; + bool result = false; + + std::vector> a = { + {1,2,4}, + {2,4,8}, + {4,8,16} + }; + + std::vector> expected = { + {1,2,4}, + {0,0,0}, + {0,0,0} + }; + + auto matrix = SparseMatrix(a); + auto actual = matrix.echelonForm(); + + auto x = SparseMatrix(expected); + result = actual == x; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestRank1() { + std::cout << "Testing rank 1: "; + bool result = false; + + std::vector> a = { + {1,4,7}, + {2,5,8}, + {3,6,9} + }; + + int expected = 2; + + auto matrix = SparseMatrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestRank2() { + std::cout << "Testing rank 2: "; + bool result = false; + + std::vector> a = { + {1,2,4}, + {2,4,8}, + {4,8,16} + }; + + int expected = 1; + + auto matrix = SparseMatrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestRank3() { + std::cout << "Testing rank 3: "; + bool result = false; + + std::vector> a = { + {1,2,1}, + {1,4,8}, + {1,6,3} + }; + + int expected = 3; + + auto matrix = SparseMatrix(a); + auto actual = matrix.getRank(); + + result = actual == expected; + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestSpan1() { + std::cout << "Testing span 1: "; + bool result = false; + + std::vector> a = { + {1,1,4}, + {0,1,4}, + {1,0,0} + }; + + std::vector> expected = { + {1,0}, + {0,1}, + {1,-1} + }; + + auto matrix = SparseMatrix(a); + auto actual = SparseMatrix::span(matrix); + + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestNull1() { + std::cout << "Testing nullspace 1: "; + bool result = false; + + + std::vector> a = { + {1,0,0}, + {0,1,0}, + {0,0,1} + }; + + auto matrix = SparseMatrix(a); + auto actual = SparseMatrix::null(matrix); + SparseMatrix expected = SparseMatrix({}); + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestNull2() { + std::cout << "Testing nullspace 2: "; + bool result = false; + + + std::vector> a = { + {1,-10, -24, -42}, + {1,-8,-18,-32}, + {-2,20,51,87} + }; + + std::vector> b = { + {2}, + {2}, + {1}, + {-1} + }; + + auto matrix = SparseMatrix(a); + auto actual = SparseMatrix::null(matrix); + auto expected = SparseMatrix(b); + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestNull3() { + std::cout << "Testing nullspace 3: "; + bool result = false; + + + std::vector> a = { + {0,1,0,0,-2,-13}, + {0,0,0,1, 2, 5}, + {0,0,1,0, 1, 9} + }; + + std::vector> b = { + {-1, 0, 0}, + {0, -2, -13}, + {0, 1, 9}, + {0, 2, 5}, + {0, -1, 0}, + {0, 0, -1} + }; + + auto matrix = SparseMatrix(a); + auto actual = SparseMatrix::null(matrix); + auto expected = SparseMatrix(b); + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestNull4() { + std::cout << "Testing nullspace 4: "; + bool result = false; + + + std::vector> a = { + {0,0,1,0,0,0,0,-2,-13}, + {0,0,0,0,0,0,1, 2, 5}, + {0,0,0,0,0,1,0, 1, 9} + }; + + std::vector> b = { + {-1,0,0,0, 0, 0}, + {0,-1,0,0, 0, 0}, + {0,0,0,0, -2,-13}, + {0,0,-1,0, 0, 0}, + {0,0,0,-1, 0, 0}, + {0,0,0,0,1,9}, + {0,0,0,0,2,5}, + {0,0,0,0,-1, 0}, + {0,0,0,0, 0, -1} + }; + + auto matrix = SparseMatrix(a); + auto actual = SparseMatrix::null(matrix); + auto expected = SparseMatrix(b); + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + +template +bool SparseMatrixTest::runTestNull5() { + std::cout << "Testing nullspace 5: "; + bool result = false; + + + std::vector> a = { + {0,1,1}, + {0,0,1}, + {0,0,0} + }; + + std::vector> b = { + {0,0,0}, + {0,0,1}, + {0,0,0} + }; + + std::vector> ans = { + {-1}, + {0}, + {0} + }; + + + auto A = SparseMatrix(a); + auto B = SparseMatrix(b); + auto actual = SparseMatrix::null(SparseMatrix(std::vector{A,B})); + auto expected = SparseMatrix(ans); + + result = actual == SparseMatrix(expected); + + std::cout << (result? "success" : "failed") << "\n"; + return result; +} + + + +int main() { + return !(SparseMatrixTest::runTestMul1() + && SparseMatrixTest::runTestMul2() + && SparseMatrixTest::runTestTranspose1() + && SparseMatrixTest::runTestTranspose2() + && SparseMatrixTest::runTestEchelon1() + && SparseMatrixTest::runTestEchelon2() + && SparseMatrixTest::runTestEchelon3() + && SparseMatrixTest::runTestRank1() + && SparseMatrixTest::runTestRank2() + && SparseMatrixTest::runTestRank3() + && SparseMatrixTest::runTestSpan1() + && SparseMatrixTest::runTestMul2() + && SparseMatrixTest::runTestTranspose1() + && SparseMatrixTest::runTestTranspose2() + && SparseMatrixTest::runTestEchelon1() + && SparseMatrixTest::runTestEchelon2() + && SparseMatrixTest::runTestEchelon3() + && SparseMatrixTest::runTestRank1() + && SparseMatrixTest::runTestRank2() + && SparseMatrixTest::runTestRank3() + && SparseMatrixTest::runTestSpan1() + && SparseMatrixTest::runTestNull1() + && SparseMatrixTest::runTestNull2() + && SparseMatrixTest::runTestNull3() + && SparseMatrixTest::runTestNull4() + && SparseMatrixTest::runTestNull5() + ); +}; + + -- GitLab From a7872c48d16929f809eb04a94ea6c5f0301f89e4 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Fri, 1 May 2020 17:44:10 +0200 Subject: [PATCH 138/142] fixed tests --- CMakeLists.txt | 2 +- test/linear_subspace_test.cpp | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8318162..90999c1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -212,7 +212,7 @@ add_test(NAME normalizedConjunctionTest COMMAND normalized_conjunction_test ) -add_test(NAME affineRelationTest +add_test(NAME linearSubspaceTest COMMAND linear_subspace_test ) diff --git a/test/linear_subspace_test.cpp b/test/linear_subspace_test.cpp index 34f251f..97a51cd 100644 --- a/test/linear_subspace_test.cpp +++ b/test/linear_subspace_test.cpp @@ -16,6 +16,16 @@ public: static bool runTestLeastUpperBound2(); }; +const llvm::Value *x1 = (llvm::Value *) 1; +const llvm::Value *x2 = (llvm::Value *) 2; +const llvm::Value *x3 = (llvm::Value *) 3; + +const std::unordered_map mock_index = { + {x1, 1}, + {x2, 2}, + {x3, 3}, +}; + bool LinearSubspaceTest::runTestLeastUpperBound1() { std::cout << "Testing least upper bound 1: "; bool result = false; @@ -23,10 +33,12 @@ bool LinearSubspaceTest::runTestLeastUpperBound1() { LinearSubspace r1 = LinearSubspace(); r1.isBottom = false; r1.basis = {MatrixType(4)}; + r1.index = mock_index; LinearSubspace r2 = LinearSubspace(); r2.isBottom = false; r2.basis = {MatrixType(4)}; + r2.index = mock_index; LinearSubspace expected = LinearSubspace(); expected.basis = {MatrixType(4)}; @@ -50,12 +62,14 @@ bool LinearSubspaceTest::runTestLeastUpperBound2() { b1.setValue(0,1, 1); b1.setValue(2,1, 1); r1.basis = {b1}; + r1.index = mock_index; LinearSubspace r2 = LinearSubspace(); r2.isBottom = false; MatrixType b2 = MatrixType(4); b2.setValue(0,3, 1); r2.basis = {b2}; + r2.index = mock_index; LinearSubspace expected = LinearSubspace(); MatrixType e1 = MatrixType(4); -- GitLab From 2856c8674e9d7cd574f37115d28abfb23a696b62 Mon Sep 17 00:00:00 2001 From: Tim Gymnich Date: Sat, 2 May 2020 17:26:49 +0200 Subject: [PATCH 139/142] clean up --- src/linear_subspace.cpp | 20 -------------------- 1 file changed, 20 deletions(-) diff --git a/src/linear_subspace.cpp b/src/linear_subspace.cpp index 82a822f..d6b8441 100644 --- a/src/linear_subspace.cpp +++ b/src/linear_subspace.cpp @@ -55,7 +55,6 @@ void LinearSubspace::applyPHINode(BasicBlock const& bb, vector c } else { LinearSubspace acc = *this; for (auto m: incoming_state.basis) { - auto val = m.column(index.at(&incoming_value)); acc.affineAssignment(&phi, 1, &incoming_value, 0); } merge(Merge_op::UPPER_BOUND, acc); @@ -65,30 +64,11 @@ void LinearSubspace::applyPHINode(BasicBlock const& bb, vector c } void LinearSubspace::applyCallInst(Instruction const& inst, BasicBlock const* end_block, LinearSubspace const& callee_state) { - // State changes from function call were not merged with the oredecessors. - // So we have to do more than just bookkeeping. - - //iterate through all instructions of it till we find a return statement - if (callee_state.isBottom) { isBottom = true; } else { basis = callee_state.basis; } - -// for (Instruction const& iter_inst: *end_block) { -// if (ReturnInst const* ret_inst = llvm::dyn_cast(&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); -// } else { -// dbgs(4) << " Return not evaluated, setting to bottom\n"; -// } -// } -// } } void LinearSubspace::applyReturnInst(Instruction const& inst) { -- GitLab From 3219b8f3a9762e506fad354c1581123ae3837017 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 12 May 2020 12:06:35 +0200 Subject: [PATCH 140/142] tested on Ubuntu 20.04 and updated make instructions --- CMakeLists.txt | 10 ++++---- README.md | 62 ++++++++++++++++++++++++++++---------------------- 2 files changed, 40 insertions(+), 32 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 90999c1..ef3b289 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -149,7 +149,7 @@ add_llvm_executable(simple_interval_test ) target_link_libraries(simple_interval_test - ${LLVM_AVAILABLE_LIBS} + PRIVATE ${LLVM_AVAILABLE_LIBS} ) add_llvm_executable(normalized_conjunction_test @@ -162,7 +162,7 @@ add_llvm_executable(normalized_conjunction_test ) target_link_libraries(normalized_conjunction_test - ${LLVM_AVAILABLE_LIBS} + PRIVATE ${LLVM_AVAILABLE_LIBS} ) add_llvm_executable(linear_subspace_test @@ -175,7 +175,7 @@ add_llvm_executable(linear_subspace_test ) target_link_libraries(linear_subspace_test - ${LLVM_AVAILABLE_LIBS} + PRIVATE ${LLVM_AVAILABLE_LIBS} ) add_llvm_executable(simple_matrix_test @@ -185,7 +185,7 @@ add_llvm_executable(simple_matrix_test ) target_link_libraries(simple_matrix_test - ${LLVM_AVAILABLE_LIBS} + PRIVATE ${LLVM_AVAILABLE_LIBS} ) add_llvm_executable(sparse_matrix_test @@ -195,7 +195,7 @@ add_llvm_executable(sparse_matrix_test ) target_link_libraries(sparse_matrix_test - ${LLVM_AVAILABLE_LIBS} + PRIVATE ${LLVM_AVAILABLE_LIBS} ) enable_testing() diff --git a/README.md b/README.md index 12fff9f..a5c4703 100644 --- a/README.md +++ b/README.md @@ -1,46 +1,54 @@ -# Program Optimization Lab 2019 +# Program Optimization Lab 202X -Implements an LLVM interprocedural analysis pass using abstract interpretation. +Implementing an LLVM analysis framework based upon the Seidl Program Optimization Lecture. ## Build -Get the LLVM source code from [here](http://releases.llvm.org/download.html). Then get clang as well, into `llvm/tools`. Create a build directory somewhere, initialise CMake, and build. For example - - # From your llvm-9.0.0-src, or whatever the version is now - wget http://releases.llvm.org/9.0.0/llvm-9.0.0.src.tar.xz - tar xf llvm-9.0.0.src.tar.xz +### Build against a system-wide installed LLVM +Install the LLVM packages from your distro's package manager, e.g. Ubuntu 20.04: + + # install the necessary LLVM packages + sudo apt install cmake clang libclang-10-dev llvm-10-dev + # now continue by building the project + git clone https://versioncontrolseidl.in.tum.de/petter/llvm-abstractinterpretation.git + cd llvm-abstractinterpretation + git checkout origin/equalities + mkdir build + cd build + cmake -G "Unix Makefiles" -DPATH_TO_LLVM=/usr/lib/llvm-10 .. + make + +You can do this, however the precompiled LLVM binaries come without symbol names, thus debugging +might be a little harder this way. Alterntively consider the following route: + +### Build against custom downloaded LLVM Sources +Get the LLVM source code from [here](https://releases.llvm.org/download.html). Then get clang as well, into `llvm/tools`. Create a build directory somewhere, initialise CMake, and build. For example + + # From llvm-10.0.0-src, or whatever the version is now + wget https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/llvm-10.0.0.src.tar.xz + tar xf llvm-10.0.0.src.tar.xz # now also download clang - cd llvm-9.0.0.src/tools - wget http://releases.llvm.org/9.0.0/cfe-9.0.0.src.tar.xz - tar xf cfe-9.0.0.src.tar.xz - mv cfe-9.0.0.src clang + cd llvm-10.0.0.src/tools + wget https://github.com/llvm/llvm-project/releases/download/llvmorg-10.0.0/clang-10.0.0.src.tar.xz + tar xf clang-10.0.0.src.tar.xz + mv clang-10.0.0.src clang cd ../.. # now continue by building LLVM mkdir llvm_build cd llvm_build - cmake ../llvm-?.?.?-src -DLLVM_TARGETS_TO_BUILD=X86 - make -j2 + # important: Don't forget to restrict to X86, otherwise prepare for a day of compiling + cmake ../llvm-10.0.0-src -DLLVM_TARGETS_TO_BUILD=X86 + make -j4 +On a 4 core i7-8550U this may take up to 3h. The parallel make may run out of memory at the end. You can restart it sequentially by issuing another `make -j1`. -Now we can initalise the repository. - - cd .. - git clone ssh://git@github.com/PUT/THE/CORRECT/REPOSITORY/IN/HERE.git - cd PUT/THE/CORRECT/REPOSITORY/IN/HERE - python3 init.py - -The script should be able to find your LLVM and clang. If it is not, you need to specify them by hand. - -At last, let us compile and run the samples. - - python3 run.py --make If there are errors regarding missing header files, you probably need to rebuild llvm. -## Useful things +## Author during Bachelor Thesis 2019/20 -The `run.py` script contains everything, up to and including the kitchen sink. It can run the samples, build, run the debugger, as well as build and run the tests. Just read its help message to get all the good stuff. I want to highlight the `-n` option, which causes it to just print out the commands it would run. This is great to just copy-paste the relevant ones into your terminal (or IDE). +* Tim Gymnich ## Authors Lab Course WS 2019/20 -- GitLab From 5e9ca9ec81b3e913c90fda67d0ce3f7e653684c3 Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 12 May 2020 12:33:12 +0200 Subject: [PATCH 141/142] more precise timings added --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index a5c4703..2676f13 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,11 @@ Get the LLVM source code from [here](https://releases.llvm.org/download.html). T cd llvm_build # important: Don't forget to restrict to X86, otherwise prepare for a day of compiling cmake ../llvm-10.0.0-src -DLLVM_TARGETS_TO_BUILD=X86 + # 4x parallelized make, which will probably fail due to RAM consumption make -j4 + # make -j1 in order to catch up, where the parallel make aborted -On a 4 core i7-8550U this may take up to 3h. -The parallel make may run out of memory at the end. You can restart it sequentially by issuing another `make -j1`. +On a 4 core i7-8550U with 16GB RAM this may take up to 1:15h, with an additional 0:15h for a sequentially run make ( `make -j1` ) to account for a poor man's RAM equipment. If there are errors regarding missing header files, you probably need to rebuild llvm. -- GitLab From 23575a39634f12fb26c8528dbca197979bd24aef Mon Sep 17 00:00:00 2001 From: Michael Petter Date: Tue, 12 May 2020 16:07:31 +0200 Subject: [PATCH 142/142] both build instructions tested and working --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index 2676f13..96a71f1 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,6 @@ Install the LLVM packages from your distro's package manager, e.g. Ubuntu 20.04: # now continue by building the project git clone https://versioncontrolseidl.in.tum.de/petter/llvm-abstractinterpretation.git cd llvm-abstractinterpretation - git checkout origin/equalities mkdir build cd build cmake -G "Unix Makefiles" -DPATH_TO_LLVM=/usr/lib/llvm-10 .. @@ -42,7 +41,7 @@ Get the LLVM source code from [here](https://releases.llvm.org/download.html). T make -j4 # make -j1 in order to catch up, where the parallel make aborted -On a 4 core i7-8550U with 16GB RAM this may take up to 1:15h, with an additional 0:15h for a sequentially run make ( `make -j1` ) to account for a poor man's RAM equipment. +On a 4 core i7-8550U with 16GB RAM this may take up to 3:00h for a sequentially run make ( `make -j1` ) to account for a poor man's RAM equipment. Also, the build will need at least 50GB of disk space, be sure to have enough room... If there are errors regarding missing header files, you probably need to rebuild llvm. -- GitLab