interpretationpass.cpp
No OneTemporary

File Metadata

Created
Tue, Jul 7, 2:10 AM

interpretationpass.cpp

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* File: interpretationpass.cpp
* Author: pgess <v.melnychenko@xreate.org>
*
* Created on July 5, 2016, 5:21 PM
*/
/**
* \file interpretationpass.h
* \brief Interpretation analysis: determines what parts of a code could be interpreted
*/
#include "pass/interpretationpass.h"
#include <compilation/targetinterpretation.h>
#include <bits/stl_vector.h>
#include "ast.h"
//DEBT implement InterpretationPass purely in transcend
//DEBT represent InterpretationPass as general type inference
using namespace std;
namespace xreate {
template<>
interpretation::InterpretationResolution
defaultValue<interpretation::InterpretationResolution>() {
return interpretation::CMPL_ONLY;
}
namespace interpretation {
enum InterpretationQuery {
QUERY_INTR_ONLY, QUERY_CMPL_ONLY
};
namespace details {
template<InterpretationQuery FLAG_REQUIRED>
bool
checkConstraints(InterpretationResolution flag) {
return( (flag==INTR_ONLY&&FLAG_REQUIRED==QUERY_INTR_ONLY)
||(flag==CMPL_ONLY&&FLAG_REQUIRED==QUERY_CMPL_ONLY));
}
InterpretationResolution
recognizeTags(const map<std::string, Expression>& tags) {
auto i=tags.find("i12n");
if(i==tags.end()){
return ANY;
}
assert(i->second.op==Operator::CALL);
const string& cmd=i->second.operands.at(0).getValueString();
//TODO make consistent names of annotation and resolution
if(cmd=="on"){
return INTR_ONLY;
} else if(cmd=="off"){
return CMPL_ONLY;
}
return ANY;
}
}
InterpretationResolution
unify(InterpretationResolution flag) {
return flag;
}
template<typename FLAG_A, typename FLAG_B, typename... FLAGS>
InterpretationResolution
unify(FLAG_A flagA, FLAG_B flagB, FLAGS... flags) {
if(flagA==ANY){
return unify(flagB, flags...);
}
if(flagB==ANY){
return unify(flagA, flags...);
}
assert(flagA==flagB);
return flagA;
}
template<InterpretationQuery FLAG_REQUIRED>
bool
checkConstraints(std::vector<InterpretationResolution>&& flags) {
assert(flags.size());
InterpretationResolution flag=flags.front();
return details::checkConstraints<FLAG_REQUIRED>(flag);
}
template<InterpretationQuery FLAG_REQUIRED_A, InterpretationQuery FLAG_REQUIRED_B, InterpretationQuery... FLAGS>
bool
checkConstraints(std::vector<InterpretationResolution>&& flags) {
assert(flags.size());
InterpretationResolution flag=flags.front();
flags.pop_back();
if(details::checkConstraints<FLAG_REQUIRED_A>(flag)){
return checkConstraints<FLAG_REQUIRED_B, FLAGS...>(move(flags));
}
return false;
}
bool
InterpretationData::isDefault() const {
return(resolution==ANY&&op==NONE);
}
void
recognizeTags(const Expression& e) {
InterpretationData tag{details::recognizeTags(e.tags), NONE};
if(!tag.isDefault())
Attachments::put<InterpretationData>(e, tag);
}
InterpretationResolution
recognizeTags(const ManagedFnPtr& f) {
return details::recognizeTags(f->getTags());
}
InterpretationPass::InterpretationPass(PassManager* manager)
: AbstractPass(manager) {
Attachments::init<FunctionInterpretationData>();
Attachments::init<InterpretationData>();
}
void
InterpretationPass::run() {
ManagedFnPtr f=man->root->begin<Function>();
auto& visitedSymbols=getSymbolCache();
while(f.isValid()) {
const Symbol&symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()};
if(!visitedSymbols.isCached(symbolFunction)){
visitedSymbols.setCachedValue(symbolFunction, process(f));
}
++f;
}
}
InterpretationResolution
InterpretationPass::process(const Expression& expression, PassContext context, const std::string& decl) {
recognizeTags(expression);
InterpretationResolution resolution=ANY;
InterpretationOperator opNo=NONE;
switch(expression.__state) {
case Expression::NUMBER:
case Expression::STRING:
{
break;
}
case Expression::IDENT:
{
resolution=Parent::processSymbol(Attachments::get<IdentifierSymbol>(expression), context);
break;
}
case Expression::COMPOUND:
break;
default:
{
resolution=CMPL_ONLY;
break;
}
}
if(expression.__state==Expression::COMPOUND)
switch(expression.op) {
case Operator::EQU:
case Operator::NE:
{
InterpretationResolution left=process(expression.operands[0], context);
InterpretationResolution right=process(expression.operands[1], context);
resolution=unify(left, right);
break;
}
case Operator::LOGIC_AND:
{
assert(expression.operands.size()==1);
resolution=process(expression.operands[0], context);
break;
}
case Operator::CALL:
{
size_t sizeOperands = expression.operands.size();
std::vector<InterpretationResolution> operands;
operands.reserve(sizeOperands);
for(size_t opNo=0; opNo<sizeOperands; ++opNo) {
const Expression &operand=expression.operands[opNo];
operands.push_back(process(operand, context));
}
//TODO cope with static/dynamic context
//TODO BUG here: if several variants they all are processed as CMPL regardless of signature
list<ManagedFnPtr> callees=man->root->getFunctionSpecializations(expression.getValueString());
if(callees.size()!=1){
resolution=CMPL_ONLY;
break;
}
ManagedFnPtr callee=callees.front();
const Symbol& symbCalleeFunc{ScopedSymbol::RetSymbol, callee->getEntryScope()};
//recursion-aware processing:
// - skip self recursion
const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, context.function->getEntryScope()};
if(!(symbSelfFunc==symbCalleeFunc)){
InterpretationResolution resCallee=processFnCall(callee, context);
assert(resCallee!=FUNC_POSTPONED&&"Indirect recursion detected: can't decide on interpretation resolution");
resolution=unify(resolution, resCallee);
}
//check arguments compatibility
const FunctionInterpretationData& calleeSignature=FunctionInterpretationHelper::getSignature(callee);
for(size_t opNo=0; opNo<sizeOperands; ++opNo){
InterpretationResolution argActual=operands.at(opNo);
InterpretationResolution argExpected=calleeSignature.signature[opNo];
//TODO use args unification result to properly process function call
unify(argActual, argExpected);
}
if(FunctionInterpretationHelper::needPartialInterpretation(callee)){
opNo=CALL_INTERPRET_PARTIAL;
}
break;
}
case Operator::CALL_INTRINSIC:
{
resolution=CMPL_ONLY;
break;
}
case Operator::QUERY:
{
resolution=INTR_ONLY;
break;
}
case Operator::QUERY_LATE:
{
InterpretationResolution predicate=process(expression.operands[0], context);
unify(predicate, INTR_ONLY);
CodeScope* exprBody=expression.blocks.front();
const std::string& argName=expression.bindings[0];
Symbol argS = {
ScopedSymbol{exprBody->__identifiers.at(argName), versions::VERSION_NONE},
exprBody
};
getSymbolCache().setCachedValue(argS, INTR_ONLY);
Parent::process(expression.blocks.front(), context);
resolution = CMPL_ONLY;
opNo=QUERY_LATE;
break;
}
case Operator::SWITCH_LATE:
{
resolution = CMPL_ONLY;
opNo = SWITCH_LATE;
break;
}
case Operator::IF:
{
InterpretationResolution flagCondition=process(expression.getOperands()[0], context);
InterpretationResolution flagScope1=Parent::process(expression.blocks.front(), context);
InterpretationResolution flagScope2=Parent::process(expression.blocks.back(), context);
//special case: IF_INTERPRET_CONDITION
if(checkConstraints<QUERY_INTR_ONLY>({flagCondition})){
opNo=IF_INTERPRET_CONDITION;
flagCondition=ANY;
}
resolution=unify(flagCondition, flagScope1, flagScope2);
break;
}
case Operator::FOLD:
{
InterpretationResolution flagInput=process(expression.getOperands()[0], context);
InterpretationResolution flagAccumInit=process(expression.getOperands()[1], context);
CodeScope* scopeBody=expression.blocks.front();
const std::string& nameEl=expression.bindings[0];
Symbol symbEl{ScopedSymbol
{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody};
getSymbolCache().setCachedValue(symbEl, InterpretationResolution(flagInput));
const std::string& nameAccum=expression.bindings[1];
Symbol symbAccum{ScopedSymbol
{scopeBody->__identifiers.at(nameAccum), versions::VERSION_NONE}, scopeBody};
getSymbolCache().setCachedValue(symbAccum, InterpretationResolution(flagAccumInit));
InterpretationResolution flagBody=Parent::process(expression.blocks.front(), context);
//special case: FOLD_INTERPRET_INPUT
if(checkConstraints<QUERY_INTR_ONLY>({flagInput})){
opNo=FOLD_INTERPRET_INPUT;
flagInput=ANY;
}
resolution=unify(flagInput, flagAccumInit, flagBody);
break;
}
case Operator::INDEX:
{
for(const Expression &opNo : expression.getOperands()) {
resolution=unify(resolution, process(opNo, context));
}
break;
}
case Operator::SWITCH:
{
InterpretationResolution flagCondition=process(expression.operands[0], context);
bool hasDefaultCase=expression.operands[1].op==Operator::CASE_DEFAULT;
//determine conditions resolution
InterpretationResolution flagHeaders=flagCondition;
for(size_t size=expression.operands.size(), i=hasDefaultCase?2:1; i<size; ++i) {
const Expression& exprCase=expression.operands[i];
flagHeaders=unify(flagHeaders, Parent::process(exprCase.blocks.front(), context));
}
if(checkConstraints<QUERY_INTR_ONLY>({flagHeaders})){
opNo=SWITCH_INTERPRET_CONDITION;
flagHeaders=ANY;
}
//determine body resolutions
resolution=flagHeaders;
for(size_t size=expression.operands.size(), i=1; i<size; ++i) {
const Expression& exprCase=expression.operands[i];
resolution=unify(resolution, Parent::process(exprCase.blocks.back(), context));
}
break;
}
case Operator::SWITCH_VARIANT:
{
InterpretationResolution resolutionCondition=process(expression.operands.at(0), context);
resolution=resolutionCondition;
if(checkConstraints<QUERY_INTR_ONLY>({resolution})){
opNo=SWITCH_VARIANT;
resolution=ANY;
}
const string identCondition=expression.bindings.front();
for(auto scope : expression.blocks) {
//set binding resolution
ScopedSymbol symbolInternal=scope->getSymbol(identCondition);
getSymbolCache().setCachedValue(Symbol{symbolInternal, scope}, InterpretationResolution(resolutionCondition));
resolution=unify(resolution, Parent::process(scope, context));
}
for(auto scope : expression.blocks) {
resolution=unify(resolution, Parent::process(scope, context));
}
break;
}
case Operator::LIST:
{
for(const Expression &opNo : expression.getOperands()) {
resolution=unify(resolution, process(opNo, context));
}
break;
}
case Operator::VARIANT:
{
if(expression.getOperands().size()){
resolution=process(expression.getOperands().front(), context);
} else {
resolution=ANY;
}
break;
}
default:
{
resolution=CMPL_ONLY;
for(const Expression &opNo : expression.getOperands()) {
process(opNo, context);
}
for(CodeScope* scope : expression.blocks) {
Parent::process(scope, context);
}
break;
}
}
InterpretationData dataExpected=
Attachments::get<InterpretationData>(expression,{ANY, NONE});
resolution=unify(resolution, dataExpected.resolution);
if(resolution!=dataExpected.resolution || opNo != dataExpected.op ){
Attachments::put<InterpretationData>(expression,{resolution, opNo});
}
return resolution;
}
InterpretationResolution
InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context) {
return process(function);
}
InterpretationResolution
InterpretationPass::process(ManagedFnPtr function) {
CodeScope* entry=function->getEntryScope();
std::vector<std::string> arguments=entry->__bindings;
const Symbol&symbSelfFunc{ScopedSymbol::RetSymbol, function->getEntryScope()};
auto& cache=getSymbolCache();
if(cache.isCached(symbSelfFunc))
return cache.getCachedValue(symbSelfFunc);
const FunctionInterpretationData& fnSignature=FunctionInterpretationHelper::getSignature(function);
InterpretationResolution fnResolutionExpected=details::recognizeTags(function->getTags());
//mark preliminary function resolution as expected
if(fnResolutionExpected!=ANY){
cache.setCachedValue(symbSelfFunc, move(fnResolutionExpected));
} else {
// - in order to recognize indirect recursion mark this function resolution as POSTPONED
cache.setCachedValue(symbSelfFunc, FUNC_POSTPONED);
}
//set resolution for function arguments as expected
for(int argNo=0, size=arguments.size(); argNo<size; ++argNo) {
Symbol symbArg{ScopedSymbol
{entry->__identifiers.at(arguments[argNo]), versions::VERSION_NONE}, entry};
cache.setCachedValue(symbArg, InterpretationResolution(fnSignature.signature[argNo]));
}
PassContext context;
context.function=function;
context.scope=entry;
InterpretationResolution resActual=process(CodeScope::getDefinition(symbSelfFunc), context);
resActual=unify(resActual, fnResolutionExpected);
return cache.setCachedValue(symbSelfFunc, move(resActual));
}
const FunctionInterpretationData
FunctionInterpretationHelper::getSignature(ManagedFnPtr function) {
if(Attachments::exists<FunctionInterpretationData>(function)){
return Attachments::get<FunctionInterpretationData>(function);
}
FunctionInterpretationData&& data=recognizeSignature(function);
Attachments::put<FunctionInterpretationData>(function, data);
return data;
}
FunctionInterpretationData
FunctionInterpretationHelper::recognizeSignature(ManagedFnPtr function) {
CodeScope* entry=function->__entry;
FunctionInterpretationData result;
result.signature.reserve(entry->__bindings.size());
bool flagPartialInterpretation=false;
for(size_t no=0, size=entry->__bindings.size(); no<size; ++no) {
const std::string& argName=entry->__bindings[no];
Symbol symbArg{ScopedSymbol
{entry->__identifiers.at(argName), versions::VERSION_NONE}, entry};
const Expression& arg=CodeScope::getDefinition(symbArg);
InterpretationResolution argResolution=details::recognizeTags(arg.tags);
flagPartialInterpretation|=(argResolution==INTR_ONLY);
result.signature.push_back(argResolution);
}
result.flagPartialInterpretation=flagPartialInterpretation;
return result;
}
bool
FunctionInterpretationHelper::needPartialInterpretation(ManagedFnPtr function) {
const FunctionInterpretationData& data=getSignature(function);
return data.flagPartialInterpretation;
}
}
} //end of namespace xreate::interpretation
/** \class xreate::interpretation::InterpretationPass
*
* The class encapsulates *Interpretation Analysis* to support [Interpretation](/d/concepts/interpretation/).
*
* It recognizes program functions, expressions, instructions eligible for interpretation
* and stores the output in \ref Attachments<FunctionInterpretationData> and \ref Attachments<InterpretationData>
*
* There are number of instructions currently eligible for interpretation:
* - Basic literals: numbers and strings
* - Compounds: lists, structs, variants
* - Non-versioned identifiers
* - Comparison and logic operators
* - %Function calls
* - `query` intrinsic function calls
* - Branching: `if`, `loop fold`, `switch`, `switch variant` statements
*
* Some of these instructions are eligible also for *late interpretation* to allow coupling
* of compiled instructions with interpreted ones, those are:
* - Partial function calls
* - Branching: `if`, `loop fold`, `switch`, `switch variant` statements
*
* \sa xreate::interpretation::TargetInterpretation, [Interpretation Concept](/d/concepts/interpretation/)
*/

Event Timeline