compilepass.cpp
No OneTemporary

File Metadata

Created
Tue, Jul 7, 4:05 PM

compilepass.cpp

#include "compilepass.h"
#include "clasplayer.h"
#include "llvmlayer.h"
#include <ast.h>
#include "query/containers.h"
#include "query/context.h"
#include "compilation/containers.h"
#include "compilation/transformations.h"
#include "ExternLayer.h"
#include "pass/adhocpass.h"
//#include "compilation/targetinterpretation.h"
#include <boost/optional.hpp>
#include <memory>
#include <iostream>
using namespace std;
using namespace xreate;
using namespace xreate::compilation;
using namespace llvm;
//TODO use Scope<TargetLlvm>
//SECTIONTAG types/convert implementation
//TODO type conversion:
//a) automatically expand types int -> bigger int; int -> floating
//b) detect exact type of `num` based on max used numeral / function type
//c) warning if need to truncate (allow/dissalow based on annotations)
namespace xreate {
llvm::Value*
doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder){
if (tyTarget->isIntegerTy() && source->getType()->isIntegerTy())
{
llvm::IntegerType* tyTargetInt = llvm::dyn_cast<IntegerType>(tyTarget);
llvm::IntegerType* tySourceInt = llvm::dyn_cast<IntegerType>(source->getType());
if (tyTargetInt->getBitWidth() < tySourceInt->getBitWidth()){
return builder.CreateCast(llvm::Instruction::Trunc, source, tyTarget);
}
if (tyTargetInt->getBitWidth() > tySourceInt->getBitWidth()){
return builder.CreateCast(llvm::Instruction::SExt, source, tyTarget);
}
}
if (source->getType()->isIntegerTy() && tyTarget->isFloatingPointTy()){
return builder.CreateCast(llvm::Instruction::SIToFP, source, tyTarget);
}
return source;
}
CodeScopeUnit::CodeScopeUnit(CodeScope* codeScope, FunctionUnit* f, CompilePass* compilePass)
: scope(codeScope), pass(compilePass), function(f)
{}
class CallStatement {
public:
virtual llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl, llvm::IRBuilder<>&) = 0;
};
class CallStatementRaw: public CallStatement{
public:
CallStatementRaw(llvm::Function* callee): __callee(callee) {}
llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl, llvm::IRBuilder<>& builder) {
auto argsFormal = __callee->args();
int pos=0;
//SECTIONTAG types/convert function ret value
for (auto argFormal = argsFormal.begin(); argFormal!=argsFormal.end(); ++argFormal, ++pos){
args[pos] = doAutomaticTypeConversion(args[pos], argFormal->getType(), builder);
}
return builder.CreateCall(__callee, args, hintDecl);
}
private:
llvm::Function* __callee;
};
//TASK implement inlining
class CallStatementInline: public CallStatement{
public:
CallStatementInline(FunctionUnit* caller, FunctionUnit* callee)
: __caller(caller), __callee(callee) {}
llvm::Value* operator() (std::vector<llvm::Value *>&& args, const std::string& hintDecl, llvm::IRBuilder<>& builder) {
//TOTEST inlining
// CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry);
// for(int i=0, size = args.size(); i<size; ++i) {
// entryCompilation->bindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i)));
// }
//
//
// return entryCompilation->compile();
}
private:
FunctionUnit* __caller;
FunctionUnit* __callee;
bool isInline(){
// Symbol ret = Symbol{0, function->__entry};
// bool flagOnTheFly = SymbolAttachments::get<IsImplementationOnTheFly>(ret, false);
//TODO consider inlining
return false;
}
};
template<class Parent>
class CallStatementLateContext: public CallStatement{
{
if (specializations.size()< 2){
return Parent...
}
}
};
}
void
CodeScopeUnit::overrideDeclaration(const Symbol binding, Expression&& declaration){
function->getScopeUnit(binding.scope)->__declarationsOverriden.emplace(binding.identifier, move(declaration));
}
class ScopeDecorator{
};
//SECTIONTAG late-context find callee function
//TOTEST static late context decisions
//TOTEST dynamic late context decisions
CallStatement*
CodeScopeUnit::findFunction(const std::string& calleeName){
LLVMLayer* llvm = pass->man->llvm;
ClaspLayer* clasp = pass->man->clasp;
ContextQuery* queryContext = pass->queryContext;
const std::list<ManagedFnPtr>& specializations = pass->man->root->getFunctionVariants(calleeName);
//if no specializations registered - check external function
if (specializations.size()==0){
llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName);
return new CallStatementRaw(external);
}
//no decisions required
if (specializations.size()==1){
if (!specializations.front()->guardContext.isValid()) {
return new CallStatementRaw( pass->getFunctionUnit(specializations.front())->compile());
}
}
//TODO move dictSpecialization over to a separate function in order to perform cache, etc.
//prepare specializations dictionary
std::map<Expression, ManagedFnPtr> dictSpecializations;
boost::optional<ManagedFnPtr> variantDefault;
boost::optional<ManagedFnPtr> variant;
for(const ManagedFnPtr& f: specializations){
const Expression& guard = f->guardContext;
//default case:
if (!guard.isValid()){
variantDefault = f;
continue;
}
assert(dictSpecializations.emplace(guard, f).second && "Found several identical specializations");
}
//check static context
ScopePacked scopeCaller = clasp->pack(this->scope);
const string atomSpecialization = "specialization";
const Expression topicSpecialization(Operator::CALL, {(Atom<Identifier_t>(string(atomSpecialization))), (Atom<Identifier_t>(string(calleeName))), (Atom<Number_t>(scopeCaller))});
const Decisions& decisions = queryContext->getFinalDecisions(scopeCaller);
if (decisions.count(topicSpecialization)){
variant = dictSpecializations.at(decisions.at(topicSpecialization));
}
//TODO check only demand for this particular topic.
size_t sizeDemand = this->function->contextCompiler.getFunctionDemandSize();
//decision made if static context found or no late context exists(and there is default variant)
bool flagHasStaticDecision = variant || (variantDefault && !sizeDemand);
//if no late context exists
if (flagHasStaticDecision) {
FunctionUnit* calleeUnit = pass->getFunctionUnit(variant? *variant: *variantDefault);
//inlining possible based on static decision only
if (calleeUnit->isInline()) {
return new CallStatementInline(function, calleeUnit);
}
return new CallStatementRaw(calleeUnit->compile());
}
//require default variant if no static decision made
assert(variantDefault);
llvm::Function* functionVariantDefault = this->pass->getFunctionUnit(*variantDefault)->compile();
return new CallStatementRaw(this->function->contextCompiler.findFunction(calleeName, functionVariantDefault, scopeCaller));
}
void
CodeScopeUnit::bindArg(llvm::Value* value, std::string&& alias)
{
//reset cached compiled value if any
raw = nullptr;
//ensure existing of an alias
assert(scope->__identifiers.count(alias));
//memorize new value for an alias
VID id = scope->__identifiers.at(alias);
__rawVars[id] = value;
}
llvm::Value*
CodeScopeUnit::process(const Expression& expr, const std::string& hintVarDecl){
Context ctx{this, this->function, this->pass};
// if (pass->targetInterpretation->isAcceptable(expr)){
// return pass->targetInterpretation->compile(expr, ctx);
// }
llvm::Value* result = processLowlevel(expr, hintVarDecl);
if (pass->transformations->isAcceptable(expr)){
return pass->transformations->transform(expr, result, ctx);
}
return result;
}
llvm::Value*
CodeScopeUnit::processLowlevel(const Expression& expr, const std::string& hintVarDecl){
#define DEFAULT(x) (hintVarDecl.empty()? x: hintVarDecl)
llvm::Value *left; llvm::Value *right;
LLVMLayer& l = *pass->man->llvm;
xreate::compilation::Advanced instructions = xreate::compilation::Advanced({this, function, pass});
switch (expr.op) {
case Operator::ADD: case Operator::SUB: case Operator::MUL:
case Operator::DIV: case Operator::EQU: case Operator::LSS:
case Operator::GTR: case Operator::NE: case Operator::LSE:
case Operator::GTE:
assert(expr.__state == Expression::COMPOUND);
assert(expr.operands.size() == 2);
left = process(expr.operands[0]);
right = process(expr.operands[1]);
//SECTIONTAG types/convert binary operation
right = doAutomaticTypeConversion(right, left->getType(), l.builder);
break;
default:;
}
switch (expr.op) {
case Operator::ADD:
return l.builder.CreateAdd(left, right, DEFAULT("tmp_add"));
break;
case Operator::SUB:
return l.builder.CreateSub(left, right, DEFAULT("tmp_sub"));
break;
case Operator::MUL:
return l.builder.CreateMul(left, right, DEFAULT("tmp_mul"));
break;
case Operator::DIV:
return l.builder.CreateSDiv(left, right, DEFAULT("tmp_div"));
break;
case Operator::EQU:
if (left->getType()->isIntegerTy()) return l.builder.CreateICmpEQ(left, right, DEFAULT("tmp_equ"));
if (left->getType()->isFloatingPointTy()) return l.builder.CreateFCmpOEQ(left, right, DEFAULT("tmp_equ"));
break;
case Operator::NE:
return l.builder.CreateICmpNE(left, right, DEFAULT("tmp_ne"));
break;
case Operator::LSS:
return l.builder.CreateICmpSLT(left, right, DEFAULT("tmp_lss"));
break;
case Operator::LSE:
return l.builder.CreateICmpSLE(left, right, DEFAULT("tmp_lse"));
break;
case Operator::GTR:
return l.builder.CreateICmpSGT(left, right, DEFAULT("tmp_gtr"));
break;
case Operator::GTE:
return l.builder.CreateICmpSGE(left, right, DEFAULT("tmp_gte"));
break;
case Operator::NEG:
left = process(expr.operands[0]);
return l.builder.CreateNeg(left, DEFAULT("tmp_neg"));
break;
case Operator::CALL: {
assert(expr.__state == Expression::COMPOUND);
std::string nameCallee = expr.getValueString();
unique_ptr<CallStatement> callee(findFunction(nameCallee));
//prepare arguments
std::vector<llvm::Value *> args;
args.reserve(expr.operands.size());
std::transform(expr.operands.begin(), expr.operands.end(), std::inserter(args, args.end()),
[this](const Expression &operand) {
return process(operand);
}
);
ScopePacked outerScopeId = pass->man->clasp->pack(this->scope);
//SECTIONTAG late-context propagation arg
size_t calleeDemandSize = pass->queryContext->getFunctionDemand(nameCallee).size();
if (calleeDemandSize){
llvm::Value* argLateContext = function->contextCompiler.compileContextArgument(nameCallee, outerScopeId);
args.push_back(argLateContext);
}
return (*callee)(move(args), DEFAULT("res_"+nameCallee), l.builder);
}
case Operator::IF:
{
return instructions.compileIf(expr, DEFAULT("tmp_if"));
}
case Operator::SWITCH:
{
return instructions.compileSwitch(expr, DEFAULT("tmp_switch"));
}
case Operator::LOOP_CONTEXT:
{
return instructions.compileLoopContext(expr, DEFAULT("tmp_loop"));
}
case Operator::LOGIC_AND: {
assert(expr.operands.size() == 1);
return process (expr.operands[0]);
}
case Operator::LIST:
{
return instructions.compileConstantArray(expr, DEFAULT("tmp_list"));
};
case Operator::LIST_RANGE:
{
assert(false); //no compilation phase for a range list
// return InstructionList(this).compileConstantArray(expr, l, hintRetVar);
};
case Operator::LIST_NAMED:
{
typedef Expanded<TypeAnnotation> ExpandedType;
ExpandedType tyRaw = l.ast->expandType(expr.type);
const std::vector<string> fields = (tyRaw.get().__operator == TypeOperator::CUSTOM)?
l.layerExtern->getStructFields(l.layerExtern->lookupType(tyRaw.get().__valueCustom))
: tyRaw.get().fields;
std::map<std::string, size_t> indexFields;
for(size_t i=0, size = fields.size(); i<size; ++i){
indexFields.emplace(fields[i], i);
}
llvm::StructType* tyRecord = llvm::cast<llvm::StructType>(l.toLLVMType(tyRaw));
llvm::Value* record = llvm::UndefValue::get(tyRecord);
for (size_t i=0; i<expr.operands.size(); ++i){
const Expression& operand = expr.operands.at(i);
unsigned int fieldId = indexFields.at(expr.bindings.at(i));
llvm::Value* result = 0;
//TODO Null ad hoc Llvm implementation
// if (operand.isNone()){
// llvm::Type* tyNullField = tyRecord->getElementType(fieldId);
// result = llvm::UndefValue::get(tyNullField);
//
// } else {
result = process(operand);
// }
assert (result);
record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef<unsigned>({fieldId}));
}
return record;
};
case Operator::MAP:
{
assert(expr.blocks.size());
return instructions.compileMapSolid(expr, DEFAULT("map"));
};
case Operator::FOLD:
{
return instructions.compileFold(expr, DEFAULT("fold"));
};
case Operator::FOLD_INF:
{
return instructions.compileFoldInf(expr, DEFAULT("fold"));
};
case Operator::INDEX:
{
//TODO allow multiindex
assert(expr.operands.size()==1);
const std::string &ident = expr.getValueString();
Symbol s = scope->findSymbol(ident);
const TypeAnnotation& t = CodeScope::findDeclaration(s).type;
const ExpandedType& t2 = pass->man->root->expandType(t);
switch (t2.get().__operator)
{
case TypeOperator::STRUCT: case TypeOperator::CUSTOM:
{
Expression idx = expr.operands.at(0);
assert(idx.__state == Expression::STRING);
std::string idxField = idx.getValueString();
llvm::Value* aggr = compileSymbol(s, ident);
return instructions.compileStructIndex(aggr, t2, idxField);
};
case TypeOperator::ARRAY: {
std::vector<llvm::Value*> indexes;
std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()),
[this] (const Expression& op){return process(op);}
);
return instructions.compileArrayIndex(s, indexes, DEFAULT(string("el_") + ident));
};
default:
assert(false);
}
};
//SECTIONTAG adhoc actual compilation
case Operator::ADHOC: {
assert(function->adhocImplementation && "Adhoc implementation not found");
string comm = expr.operands[0].getValueString();
CodeScope* scope = function->adhocImplementation->getImplementationForCommand(comm);
CodeScopeUnit* unitScope = function->getScopeUnit(scope);
return unitScope->compile();
};
case Operator::SEQUENCE: {
assert (expr.getOperands().size());
llvm::Value* result;
for(const Expression &op: expr.getOperands()){
result = process(op, "");
}
return result;
}
case Operator::NONE:
assert(expr.__state != Expression::COMPOUND);
switch (expr.__state) {
case Expression::IDENT: {
const std::string &ident = expr.getValueString();
Symbol s = scope->findSymbol(ident);
return compileSymbol(s, ident);
}
case Expression::NUMBER: {
int literal = expr.getValueDouble();
return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), literal);
}
case Expression::STRING: {
return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str"));
};
case Expression::VARIANT: {
const ExpandedType& typVariant = pass->man->root->expandType(expr.type);
llvm::Type* typRaw = l.toLLVMType(typVariant);
int value = expr.getValueDouble();
return llvm::ConstantInt::get(typRaw, value);
}
default: {
break;
}
};
break;
default: break;
}
assert(false);
return 0;
}
llvm::Value*
CodeScopeUnit::compile(const std::string& hintBlockDecl){
if (raw != nullptr) return raw;
if (!hintBlockDecl.empty()) {
llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm::getGlobalContext(), hintBlockDecl, function->raw);
pass->man->llvm->builder.SetInsertPoint(block);
}
raw = process(scope->__body);
return raw;
}
llvm::Value*
CodeScopeUnit::compileSymbol(const Symbol& s, std::string hintRetVar)
{
CodeScope* scope = s.scope;
CodeScopeUnit* self = function->getScopeUnit(scope);
if (self->__rawVars.count(s.identifier)) {
return self->__rawVars[s.identifier];
}
//compilation transformations could override symbol declarations.
Expression declaration = CodeScope::findDeclaration(s);
if (!declaration.isDefined()){
if (self->__declarationsOverriden.count(s.identifier)){
declaration = self->__declarationsOverriden[s.identifier];
} else {
assert(false); //in case of bindings there should be raws already.
}
}
return self->__rawVars[s.identifier] = self->process(declaration, hintRetVar);
}
//SECTIONTAG late-context FunctionDecorator
template<class Parent>
class LateContextFunctionDecorator: public Parent{
LateContextFunctionDecorator(ManagedFnPtr f, CompilePass* p)
: Parent(f, p), contextCompiler(this, p)
{}
std::vector<llvm::Type*> prepareArguments(){
std::vector<llvm::Type*>&& arguments = Parent::prepareArguments();
size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize();
if (sizeLateContextDemand) {
llvm::Type* ty32 = llvm::Type::getInt32Ty(llvm::getGlobalContext());
llvm::Type* tyDemand = llvm::ArrayType::get(ty32, sizeLateContextDemand);
arguments.push_back(tyDemand);
}
return arguments;
}
void prepareBindings(){
if (sizeLateContextDemand){
fargsI->setName("latecontext");
contextCompiler.rawContextArgument = &*fargsI;
}
}
protected:
LateContextCompiler2 contextCompiler;
};
//SECTIONTAG adhoc FunctionDecorator
template<class Parent>
class AdhocFunctionDecorator: public Parent{
protected:
llvm::Type* prepareResult(){
if (! function->isPrefunction){
return Parent::prepareResult();
}
AdhocPass* adhocpass = reinterpret_cast<AdhocPass*>(pass->man->getPassById(PassId::AdhocPass));
adhocImplementation = adhocpass->determineForScope(entry);
return llvm->toLLVMType(ast->expandType(adhocImplementation->getResultType()));
}
private:
AdhocScheme* adhocImplementation=nullptr; //SECTIONTAG adhoc prefunc scheme declaration
};
template<class Decorator>
class BasicFunctionDecorator: public Decorator{
protected:
std::string prepareName(){
string name = ast->getFunctionVariants(function->__name).size() > 1?
function->__name + std::to_string(function.id()) :
function->__name;
return name;
}
virtual std::vector<llvm::Type*> prepareArguments(){
std::vector<llvm::Type*> signature;
std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()),
[this, llvm, ast, entry](const std::string &arg)->llvm::Type* {
assert(entry->__identifiers.count(arg));
VID argid = entry->__identifiers.at(arg);
return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type));
});
return signature;
}
virtual llvm::Type* prepareResult(){
return llvm->toLLVMType(ast->expandType(entry->__declarations[0].type));
}
virtual void prepareBindings(){
CodeScope* entry = function->__entry;
CodeScopeUnit* entryCompilation = getScopeUnit(entry);
llvm::Function::arg_iterator fargsI = raw->arg_begin();
for (std::string &arg : entry->__bindings) {
VID argid = entry->__identifiers[arg];
entryCompilation->__rawVars[argid] = &*fargsI;
fargsI->setName(arg);
++fargsI;
}
}
};
typedef LateContextFunctionDecorator<
AdhocFunctionDecorator<
BasicFunctionDecorator<FunctionUnit>>> DefaultFunctionUnit;
llvm::Function*
FunctionUnit::compile(){
if (raw != nullptr) return raw;
LLVMLayer* llvm = pass->man->llvm;
llvm::IRBuilder<>& builder = llvm->builder;
AST* ast = pass->man->root;
string&& functionName = prepareName();
std::vector<llvm::Type*>&& types = prepareArguments();
llvm::Type* expectedResultType = prepareResult();
llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false);
raw = llvm::cast<llvm::Function>(llvm->module->getOrInsertFunction(functionName, ft));
prepareBindings();
const std::string&blockName = "entry";
llvm::BasicBlock* blockCurrent = builder.GetInsertBlock();
llvm::Value* result = entryCompilation->compile(blockName);
assert(result);
//SECTIONTAG types/convert function ret value
builder.CreateRet(doAutomaticTypeConversion(result, expectedResultType, llvm->builder));
if (blockCurrent){
builder.SetInsertPoint(blockCurrent);
}
llvm->moveToGarbage(ft);
return raw;
}
CodeScopeUnit*
FunctionUnit::getScopeUnit(CodeScope* scope){
if (!scopes.count(scope)){
CodeScopeUnit* unit = new CodeScopeUnit(scope, this, pass);
scopes.emplace(scope, std::unique_ptr<CodeScopeUnit>(unit));
}
return scopes.at(scope).get();
}
CodeScopeUnit*
FunctionUnit::getEntry(){
return getScopeUnit(function->getEntryScope());
}
CodeScopeUnit*
FunctionUnit::getScopeUnit(ManagedScpPtr scope){
return getScopeUnit(&*scope);
}
FunctionUnit*
CompilePass::getFunctionUnit(const ManagedFnPtr& function){
unsigned int id = function.id();
if (!functions.count(id)){
FunctionUnit* unit = new DefaultFunctionUnit(function, this);
functions.emplace(id, unit);
return unit;
}
return functions.at(id).get();
}
void
CompilePass::run(){
transformations = new Transformations(this);
transformations->registerTransformer(new TransformerSaturation(transformations));
//targetInterpretation = new TargetInterpretation(this->man->root);
queryContext = reinterpret_cast<ContextQuery*> (man->clasp->getQuery(QueryId::ContextQuery));
//Find out main function;
ClaspLayer::ModelFragment model = man->clasp->query(Config::get("function-entry"));
assert(model && "Error: No entry function found");
assert(model->first != model->second && "Error: Ambiguous entry function");
string nameMain = std::get<0>(ClaspLayer::parse<std::string>(model->first->second));
FunctionUnit* unitMain = getFunctionUnit(man->root->findFunction(nameMain));
entry = unitMain->compile();
}
llvm::Function*
CompilePass::getEntryFunction(){
assert(entry);
return entry;
}
void
CompilePass::prepareQueries(ClaspLayer* clasp){
clasp->registerQuery(new containers::Query(), QueryId::ContainersQuery);
clasp->registerQuery(new ContextQuery(), QueryId::ContextQuery);
}
//CODESCOPE COMPILATION PHASE
//FIND SYMBOL(compilation phase):
//if (!forceCompile)
//{
// return result;
//}
// //search in already compiled vars
//if (__rawVars.count(vId))
//{
// return result;
//}
//if (!__declarations.count(vId)) {
// //error: symbol is uncompiled scope arg
// assert(false);
//}
//const Expression& e = __declarations.at(vId);
//__rawVars[vId] = process(e, l, name);
//FIND FUNCTION
//llvm::Function*
//CompilePass::findFunction(const std::string& name){
// ManagedFnPtr calleeFunc = man->root->findFunction(name);
// assert(calleeFunc.isValid());
// return nullptr;
//}

Event Timeline