Skip to content

Drano integrated w/ AMD llvm, basic functionality working. #9

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions llvm/lib/Transforms/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ add_subdirectory(ObjCARC)
add_subdirectory(Coroutines)
add_subdirectory(CFGuard)
add_subdirectory(HC)
add_subdirectory(UncoalescedAnalysis)
210 changes: 210 additions & 0 deletions llvm/lib/Transforms/UncoalescedAnalysis/AbstractExecutionEngine.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
#ifndef ABSTRACT_EXECUTION_ENGINE_H
#define ABSTRACT_EXECUTION_ENGINE_H

#include "AbstractState.h"
#include "AbstractValue.h"

#include "llvm/ADT/SmallVector.h"
#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Instruction.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/raw_ostream.h"
#include <list>
#include <map>
#include <string>
#include <utility>

#ifndef DEBUG_TYPE
#define DEBUG_TYPE "abstract-execution"
#endif

#define NUM_RECENT_BLOCKS 16

using namespace llvm;

// This class defines an abstract execution engine. An abstract execution engine
// takes in a program and executes the program abstractly using semantics
// defined for an abstract value.
// T is the type of abstract value used for abstract execution. It must implement
// AbstractValue<T>.
// U is the type of abstract value used for abstract execution. It must implement
// AbstractState<T>.
template<typename T, typename U>
class AbstractExecutionEngine {
static_assert(
std::is_base_of<AbstractValue<T>, T>::value,
"T must be a descendant of AbstractValue<T>"
);
static_assert(
std::is_base_of<AbstractState<T, U>, U>::value,
"U must be a descendant of AbstractState<T, U>"
);
public:
AbstractExecutionEngine()
: entryBlock_(nullptr) {}
AbstractExecutionEngine(const BasicBlock* entryBlock, U initialState)
: entryBlock_(entryBlock), initialState_(initialState) {}

virtual ~AbstractExecutionEngine() = 0;

// Queries the state before an instruction.
const U& getStateBeforeInstruction(const Instruction* inst){
return StateBeforeInstructionMap_[inst];
}

// Adds a block to execute next and the state in which the block must be
// executed.
void AddBlockToExecute(const BasicBlock* b, U st);

// Executes program (can be overriden).
virtual void Execute();

// Executes the instruction on a state and returns the state after execution.
virtual U ExecuteInstruction(const Instruction* inst,
U st) = 0;

protected:
// Entry block where the abstract execution begins.
const BasicBlock* entryBlock_;

// Initial state before execution of the program.
U initialState_;

private:
// Returns the next unit to execute.
std::pair<const BasicBlock*, U> getNextExecutionUnit(
std::list<std::pair<const BasicBlock*, U>>& worklist);

// Add block to recently executed blocks.
void AddRecentBlock(const BasicBlock* block);

// Stores some recent blocks executed by the engine.
SmallVector<const BasicBlock*, NUM_RECENT_BLOCKS> recentBlocks_;

// Records abstract state before an instruction is executed.
std::map<const Instruction*, U> StateBeforeInstructionMap_;

// Buffer to store the set of blocks that must be executed after this block
// completes execution.
std::list<std::pair<const BasicBlock*, U>> BlocksToExecuteBuffer_;
};

template<typename T, typename U>
AbstractExecutionEngine<T, U>::~AbstractExecutionEngine() {}

template<typename T, typename U>
void AbstractExecutionEngine<T, U>::AddBlockToExecute(const BasicBlock* b, U st) {
BlocksToExecuteBuffer_.push_back(std::pair<const BasicBlock*, U>(b, st));
}

// Returns a block in recentBlocks_ if found. Otherwise returns the
// first block in worklist. This optimization is useful for execution
// of loops. All blocks within the loop are given priority over blocks
// after the loop. This ensures that the blocks after the loop are
// executed only after the loop reaches a fixed point.
template<typename T, typename U>
std::pair<const BasicBlock*, U>
AbstractExecutionEngine<T, U>::getNextExecutionUnit(
std::list<std::pair<const BasicBlock*, U>>& worklist) {
for (const BasicBlock* block : recentBlocks_) {
auto listIt = find_if(worklist.begin(), worklist.end(),
[block](const std::pair<const BasicBlock*, U>& item){
if (item.first == block) return true;
else return false;
});
if (listIt != worklist.end()) {
// Block found.
auto unit = *listIt;
worklist.erase(listIt);
AddRecentBlock(unit.first);
return unit;
}
}
auto unit = worklist.front();
worklist.pop_front();
AddRecentBlock(unit.first);
return unit;
}

// Adds block to the set of recent blocks.
template<typename T, typename U>
void AbstractExecutionEngine<T, U>::AddRecentBlock(const BasicBlock* block) {
auto pos = recentBlocks_.begin();
while (*pos != block && pos != recentBlocks_.end()) ++pos;
if (pos != recentBlocks_.end()) { recentBlocks_.erase(pos); }
if (recentBlocks_.size() >= NUM_RECENT_BLOCKS) {
recentBlocks_.pop_back();
}
recentBlocks_.insert(recentBlocks_.begin(), block);
}

template<typename T, typename U>
void AbstractExecutionEngine<T, U>::Execute() {
// Worklist to execute basic blocks.
// Each worklist item consists of a basicblock and an abstract state to be
// propagated through the block.
std::list<std::pair<const BasicBlock*, U>> worklist;
worklist.push_back(std::pair<const BasicBlock*, U>(entryBlock_, initialState_));

// Execute work items in worklist.
StateBeforeInstructionMap_.clear();
while (!worklist.empty()) {
auto unit = getNextExecutionUnit(worklist);
const BasicBlock *b = unit.first; // next block to be executed.
U st = unit.second; // state before next block.
LLVM_DEBUG(errs() << "BasicBlock: " << b->getName() << "\n");

// Clear buffer.
BlocksToExecuteBuffer_.clear();
// Execute instructions within the block.
for (BasicBlock::const_iterator it = b->begin(), ite = b->end();
it != ite; ++it) {
const Instruction* I = &*it;
// If I is the first statement in the block, merge I's pre-state
// with incoming state.
if (it == b->begin()) {
if(StateBeforeInstructionMap_.find(I) !=
StateBeforeInstructionMap_.end()) {
U oldState = StateBeforeInstructionMap_[I];
U newState = oldState.mergeState(st);
// State before block unchanged; no need to execute block.
if (oldState == newState) break;

StateBeforeInstructionMap_[I] = newState;
} else {
StateBeforeInstructionMap_[I] = st;
}
} else {
StateBeforeInstructionMap_[I] = st;
}

LLVM_DEBUG(errs() << " " << *I << ", " << st.printInstructionState(I) << "\n");
st = ExecuteInstruction(I, st);
}
// Add subsequent blocks to be executed. Note that these were added to
// the buffer during the execution of instructions in the current block.
for (auto bufferIt = BlocksToExecuteBuffer_.begin(),
bufferIte = BlocksToExecuteBuffer_.end(); bufferIt != bufferIte;
++bufferIt) {
// Check if the key already exists in worklist, if so, merge the two
// work items. This is an optimization that helps scale the execution,
// at the cost of being slightly imprecise.
const BasicBlock* block = bufferIt->first;
auto listIt = find_if(worklist.begin(), worklist.end(),
[block](const std::pair<const BasicBlock*, U>& item){
if (item.first == block) return true;
else return false;
});
if (listIt != worklist.end()) {
listIt->second = listIt->second.mergeState(bufferIt->second);
} else {
worklist.push_back(std::pair<const BasicBlock*, U>(bufferIt->first,
bufferIt->second));
}
}
}
}

#undef DEBUG_TYPE

#endif /* AbstractExecutionEngine.h */
102 changes: 102 additions & 0 deletions llvm/lib/Transforms/UncoalescedAnalysis/AbstractState.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#ifndef ABSTRACT_STATE_H
#define ABSTRACT_STATE_H

#include "AbstractValue.h"

#include "llvm/IR/BasicBlock.h"
#include "llvm/IR/Value.h"
#include <map>
#include <string>

using namespace llvm;

// Defines an abstract state used by the abstract execution engine to store
// the current state of the abstract execution.
template <typename T, typename U>
class AbstractState {
static_assert(
std::is_base_of<AbstractValue<T>, T>::value,
"T must be a descendant of AbstractValue<T>"
);
public:
AbstractState() {}

virtual ~AbstractState() = 0;

void clear() { valueMap_.clear(); }

bool operator==(const AbstractState& st) const {
return (valueMap_ == st.valueMap_);
}

void operator=(const AbstractState& st) {
valueMap_ = st.valueMap_;
}

bool hasValue(const Value* in) const {
return (valueMap_.find(in) != valueMap_.end());
}

virtual T getValue(const Value* in) const {
return valueMap_.at(in);
}

virtual void setValue(const Value* in, T v) { valueMap_[in] = v; }

virtual U mergeState(const U& st) const;

// Pretty printing
virtual std::string getString() const;
virtual std::string printInstructionState(const Instruction* I) const;

private:
// Map from variables to their abstract values.
std::map<const Value*, T> valueMap_;
};

template<typename T, typename U>
AbstractState<T, U>::~AbstractState() {}

template<typename T, typename U>
U AbstractState<T, U>::mergeState(const U& st) const {
U result = st;
for (auto it = this->valueMap_.begin(), ite = this->valueMap_.end();
it != ite; ++it) {
const Value* in = it->first;
T v = it->second;
if (st.hasValue(in)) {
result.setValue(in, v.join(st.valueMap_.at(in)));
} else {
result.setValue(in, v);
}
}
return result;
}

template<typename T, typename U>
std::string AbstractState<T, U>::getString() const {
std::string s;
s.append("[");
for (auto it = valueMap_.begin(), ite = valueMap_.end(); it != ite; ++it) {
const Value* in = it->first;
T v = it->second;
s.append(in->getName().str()).append(":").append(v.getString()).append(", ");
}
s.append("]");
return s;
}

template<typename T, typename U>
std::string AbstractState<T, U>::printInstructionState(
const Instruction* I) const {
std::string s;
s.append("[");
for (unsigned i = 0; i < I->getNumOperands(); i++) {
T v = this->getValue(I->getOperand(i));
s.append(I->getOperand(i)->getName().str()).append(":").append(v.getString()).append(",");
}
s.append("]");
return s;
}

#endif /* AbstractState.h */
24 changes: 24 additions & 0 deletions llvm/lib/Transforms/UncoalescedAnalysis/AbstractValue.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#ifndef ABSTRACT_VALUE_H
#define ABSTRACT_VALUE_H

#include <string>

// This class defines an abstract value and semantics for the various
// operations performed during the abstract execution of the program.
template<typename T>
class AbstractValue {
public:
AbstractValue() {}

virtual ~AbstractValue() = 0;

virtual std::string getString() const = 0;

// Returns a merge of this value with value v.
virtual T join(const T& v) const = 0;
};

template<typename T>
AbstractValue<T>::~AbstractValue() {}

#endif /* AbstractValue.h */
30 changes: 30 additions & 0 deletions llvm/lib/Transforms/UncoalescedAnalysis/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# 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)
endif()

#set(CMAKE_BUILD_TYPE "Debug")
set(LLVM_ENABLE_PLUGINS ON)
set(LLVM_LINK_COMPONENTS
Demangle
)

add_llvm_library(LLVMUncoalescedAnalysis MODULE
InterprocUncoalescedAnalysisPass.cpp
UncoalescedAnalysisPass.cpp
GPUState.cpp
MultiplierValue.cpp
UncoalescedAnalysis.cpp

DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
Loading