Page Menu
Home
Xreate
Search
Configure Global Search
Log In
Docs
Questions
Repository
Issues
Patches
Internal API
Files
F3995591
compilepass.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Tue, Jul 7, 4:05 PM
Size
25 KB
Mime Type
text/x-c++
Expires
Thu, Jul 9, 4:05 PM (1 d, 6 m)
Engine
blob
Format
Raw Data
Handle
271670
Attached To
rXR Xreate
compilepass.cpp
View Options
#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
Log In to Comment