Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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