diff --git a/config/default.json b/config/default.json index 93cc036..21b7af6 100644 --- a/config/default.json +++ b/config/default.json @@ -1,70 +1,71 @@ { "containers": { "id": { "implementations": "impl_fulfill_cluster", "clusters": "var_cluster", "prototypes": "proto_cluster", "linkedlist": "linkedlist" }, "impl": { "solid": "solid", "onthefly": "on_the_fly" } }, "logging": { "id": "logging" }, "function-entry": "entry", "clasp": { "bindings" : { "variable": "bind", "function": "bind_func", "scope": "bind_scope", "function_demand" : "bind_function_demand", "scope_decision": "bind_scope_decision" }, "context" : { "decisions":{ "dependent": "resolution_dependency" } }, "nonevalue": "nonevalue", "ret": { "symbol": "retv", "tag": "ret" } }, "tests": { - "template": "polymorphs", + "template": "communication", "templates": { "default": "*-Adhoc.*:Compilation.full_IFStatementWithVariantType:Types.full_VariantType_Switch1", "ast": "AST.*", "adhocs": "Adhoc.*", "effects": "Effects.*", "basic": "Attachments.*", "context": "Context.*", "compilation": "Compilation.*", + "communication": "Communication.FullDirectAndGuarded1", "cfa": "CFA.*", "containers": "Containers.*", "dfa": "DFA.*", "diagnostic": "Diagnostic.*", "dsl": "Interpretation.SwitchVariantAlias-Association.*", "ExpressionSerializer": "ExpressionSerializer.*", "externc": "InterfaceExternC.*", "loops": "Loop.*", "modules": "Modules.*", "polymorphs": "Polymorphs.call1", "types": "Types.*", "vendorsAPI/clang": "ClangAPI.*", "vendorsAPI/xml2": "libxml2*" } } } diff --git a/cpp/src/CMakeLists.txt b/cpp/src/CMakeLists.txt index 61049e9..3a19723 100644 --- a/cpp/src/CMakeLists.txt +++ b/cpp/src/CMakeLists.txt @@ -1,229 +1,230 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate) cmake_policy(SET CMP0022 NEW) message("MODULES" ${CMAKE_MODULE_PATH}) # LLVM #====================== FIND_PACKAGE (LLVM REQUIRED) set(LLVM_VERSION ${LLVM_VERSION_MAJOR}.${LLVM_VERSION_MINOR}) message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") message("LLVM LIB PATH:" ${LLVM_LIBRARY_DIRS}) message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") INCLUDE_DIRECTORIES(${LLVM_INCLUDE_DIRS}) message(STATUS "INCLUDE DIR: ${LLVM_INCLUDE_DIRS}") add_definitions(${LLVM_DEFINITIONS}) message("LLVM DEFS: " ${LLVM_DEFINITIONS}) llvm_map_components_to_libnames(LLVM_LIBS core nativecodegen native executionengine mcjit support option) message("LLVM LIBS: " ${LLVM_LIBS}) # CLANG #====================== set(CLANG_LIBS clangCodeGen clangASTMatchers clangQuery clangTooling clangFrontend clangSerialization clangDriver clangParse clangSema clangAnalysis clangAST clangEdit clangLex clangBasic ) # POTASSCO #====================== set(POTASSCO_PATH "/opt/potassco/clingo" CACHE PATH "Path to potassco sources") set(POTASSCO_INCLUDE_PATH ${POTASSCO_PATH}/libgringo ${POTASSCO_PATH}/libclasp ${POTASSCO_PATH}/libclingo ${POTASSCO_PATH}/libprogram_opts ${POTASSCO_PATH}/liblp ) INCLUDE_DIRECTORIES(${POTASSCO_INCLUDE_PATH}) set(LIBCLASP_LIBS clingo clasp gringo program_opts reify lp ) message("CLASP LIBS: " ${LIBCLASP_LIBS}) # OTHER DEPENDENCIES #=========================== set(JEAYESON_INCLUDE_PATH ${CMAKE_HOME_DIRECTORY}/../vendors/jeayeson/include/ ) INCLUDE_DIRECTORIES(${JEAYESON_INCLUDE_PATH}) # COCO #=========================== set(COCO_EXECUTABLE "" CACHE PATH "Path to coco executable") set(COCO_FRAMES_PATH "" CACHE PATH "Path to coco frames") set(COCO_GRAMMAR_PATH ${CMAKE_HOME_DIRECTORY}/../grammar/) set(COCO_SOURCE_FILES_MAIN ${COCO_GRAMMAR_PATH}/main/Parser.cpp ${COCO_GRAMMAR_PATH}/main/Scanner.cpp ) set(COCO_SOURCE_FILES_MODULES ${COCO_GRAMMAR_PATH}/modules/Parser.cpp ${COCO_GRAMMAR_PATH}/modules/Scanner.cpp ) set(COCO_SOURCE_FILES ${COCO_SOURCE_FILES_MODULES} ${COCO_SOURCE_FILES_MAIN}) INCLUDE_DIRECTORIES(${COCO_GRAMMAR_PATH}) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MAIN} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar main ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/xreate.ATG ) add_custom_command(OUTPUT ${COCO_SOURCE_FILES_MODULES} COMMAND ${COCO_GRAMMAR_PATH}/gen-grammar modules ${COCO_EXECUTABLE} ${COCO_FRAMES_PATH} WORKING_DIRECTORY ${COCO_GRAMMAR_PATH} MAIN_DEPENDENCY ${COCO_GRAMMAR_PATH}/modules.ATG ) message(STATUS "COCO GRAMMAR BUILD STATUS:" ${COCO_OUTPUT}) # XREATE #====================== set(SOURCE_FILES + query/polymorph.cpp + analysis/dfagraph.cpp + pass/dfapass.cpp compilation/targetinterpretation.cpp pass/interpretationpass.cpp ast.cpp xreatemanager.cpp analysis/typeinference.cpp aux/xreatemanager-decorators.cpp compilation/operators.cpp compilation/transformations.cpp compilation/transformersaturation.cpp pass/compilepass.cpp - pass/dfapass.cpp - analysis/dfagraph.cpp pass/versionspass.cpp attachments.cpp ExternLayer.cpp analysis/cfagraph.cpp analysis/aux.cpp compilation/containers.cpp compilation/advanced.cpp clasplayer.cpp compilation/latecontextcompiler2.cpp query/context.cpp llvmlayer.cpp utils.cpp pass/abstractpass.cpp pass/cfapass.cpp pass/adhocpass.cpp contextrule.cpp query/containers.cpp analysis/DominatorsTreeAnalysisProvider.cpp aux/serialization/expressionserializer.cpp modules.cpp ) set(XREATE_INCLUDE_DIRS ${CMAKE_CURRENT_SOURCE_DIR}/ ) INCLUDE_DIRECTORIES(${XREATE_INCLUDE_DIRS}) set(XREATE_PRIVATE_INCLUDE_DIRS ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) add_library(${PROJECT_NAME} SHARED ${COCO_SOURCE_FILES} ${SOURCE_FILES}) target_link_libraries(${PROJECT_NAME}) target_include_directories(${PROJECT_NAME} INTERFACE ${XREATE_INCLUDE_DIRS} ${COCO_GRAMMAR_PATH} ${JEAYESON_INCLUDE_PATH} ${LLVM_INCLUDE_DIRS} ${POTASSCO_INCLUDE_PATH} ) get_directory_property(DEFINITIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS) message("definitions all: " ${DEFINITIONS_ALL}) target_compile_definitions(${PROJECT_NAME} INTERFACE ${DEFINITIONS_ALL}) get_directory_property(COMPILATION_OPTIONS_ALL DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_OPTIONS) message("compilations all: " ${COMPILATION_OPTIONS_ALL}) target_compile_options(${PROJECT_NAME} INTERFACE ${COMPILATION_OPTIONS_ALL}) SET_PROPERTY(TARGET ${PROJECT_NAME} PROPERTY INTERFACE_LINK_LIBRARIES ${LIBCLASP_LIBS} ${CLANG_LIBS} ${LLVM_LIBS} tbb boost_system boost_filesystem ) #${CLANG_LIBS} #set (LINK_INTERFACE_LIBRARIES "") # FUNCTION(PREPEND var prefix) # SET(listVar "") # FOREACH(f ${ARGN}) # LIST(APPEND listVar "${prefix}/${f}") # ENDFOREACH(f) # SET(${var} "${listVar}" PARENT_SCOPE) # ENDFUNCTION(PREPEND) #set(COTIRE_UNITY_SOURCE_MAXIMUM_NUMBER_OF_INCLUDES "-j4") #cotire(xreate) # MACRO (ADD_PCH_RULE _header_filename _src_list) # SET(_gch_filename "${_header_filename}.gch") # LIST(APPEND ${_src_list} ${_gch_filename}) # SET (_args ${CMAKE_CXX_FLAGS}) # LIST(APPEND _args -c ${_header_filename} -o ${_gch_filename}) # GET_DIRECTORY_PROPERTY(DIRINC INCLUDE_DIRECTORIES) # foreach (_inc ${DIRINC}) # LIST(APPEND _args "-I" ${_inc}) # endforeach(_inc ${DIRINC}) # SEPARATE_ARGUMENTS(_args) # add_custom_command(OUTPUT ${_gch_filename} # COMMAND rm -f ${_gch_filename} # COMMAND ${CMAKE_CXX_COMPILER} ${CMAKE_CXX_COMPILER_ARG1} ${_args} # DEPENDS ${_header_filename}) # ENDMACRO(ADD_PCH_RULE _header_filename _src_list) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/ast.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/llvmlayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/clasplayer.h SOURCE_FILES) # ADD_PCH_RULE (${CMAKE_HOME_DIRECTORY}/src/pass/abstractpass.h SOURCE_FILES) diff --git a/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp b/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp index 22111cd..c6a928b 100644 --- a/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp +++ b/cpp/src/analysis/DominatorsTreeAnalysisProvider.cpp @@ -1,241 +1,239 @@ /* 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: DominatorsTreeAnalysisProvider.cpp * Author: pgess * * Created on May 13, 2016, 11:39 AM */ /** * \file DominatorsTreeAnalysisProvider.h * \brief Dominators Tree analysis */ #include "analysis/cfagraph.h" #include "analysis/DominatorsTreeAnalysisProvider.h" #include "llvm/ADT/GraphTraits.h" #include "llvm/Support/GenericDomTreeConstruction.h" #include "llvm/Support/GenericDomTree.h" #include #include #include using namespace std; using namespace xreate; using namespace boost; using namespace boost::bimaps; namespace xreate{ namespace dominators { struct ControlFlowTree; struct Node { ScopePacked scope; ControlFlowTree* tree; }; /* bool operator !=(const Node& a, const Node& b){ return (a.tree != b.tree) || (a.scope != b.scope); } Node& operator++(Node& a){ ++a.scope; return a; } */ struct ControlFlowTree{ typedef bimap, multiset_of> CHILD_RELATIONS; CHILD_RELATIONS edges; std::vector nodes; Node* entry = nullptr; size_t size; ControlFlowTree(const size_t nodesCount): nodes(nodesCount), size(nodesCount){ } - static ControlFlowTree* build(const ClaspLayer* engine){ + static ControlFlowTree* build(const ClaspLayer* engine, cfa::CFAGraph* graph){ ControlFlowTree* tree = new ControlFlowTree(engine->getScopesCount()); - cfa::CFAGraph* graph = engine->dataCFA.get(); - for (const auto& edge: graph->__parentScopeRelations){ tree->edges.insert(CHILD_RELATIONS::value_type(edge.second, edge.first)); } for (const auto& edge: graph->__callRelations){ unsigned int calleeFunction = edge.right; ScopePacked caller = edge.left; auto range = graph->__parentFunctionRelations.right.equal_range(calleeFunction); for (auto& i=range.first; i!= range.second; ++i){ tree->edges.insert(CHILD_RELATIONS::value_type(caller, i->second)); } } for (size_t i=0; isize; ++i){ tree->nodes[i]= Node{(unsigned int) i, tree}; } return tree; } std::list getRootFunctions() const{ size_t idMax = size; size_t id =0; std::list results; auto i = edges.right.begin(); while (id < idMax) { if (i!= edges.right.end() && i->first == id){ i = edges.right.upper_bound(i->first); } else { results.push_back(id); } ++id; } return std::move(results); } }; }} //end of namespace xreate::dominators namespace llvm { using namespace xreate::dominators; template <> struct GraphTraits { typedef Node* nodes_iterator; typedef Node NodeType; typedef std::function Transformer; typedef typename boost::transform_iterator ChildIteratorType; static ChildIteratorType child_begin(const nodes_iterator& node) { auto range = node->tree->edges.left.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.first, x); } static ChildIteratorType child_end(const nodes_iterator& node) { auto range = node->tree->edges.left.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.second, x); } }; template <> struct GraphTraits> { typedef Node* nodes_iterator; typedef Node NodeType; typedef std::function Transformer; typedef typename boost::transform_iterator ChildIteratorType; static ChildIteratorType child_begin(const nodes_iterator& node) { auto range = node->tree->edges.right.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.first, x); } static ChildIteratorType child_end(const nodes_iterator& node) { auto range = node->tree->edges.right.equal_range(node->scope); Transformer x = [node](auto edge){return &node->tree->nodes[edge.second];}; return boost::make_transform_iterator(range.second, x); } }; template <> struct GraphTraits: public GraphTraits { static NodeType* getEntryNode(ControlFlowTree* F) { if (F->entry) return F->entry; list&& roots = F->getRootFunctions(); assert(roots.size()==1); return F->entry = &F->nodes[roots.front()]; } static nodes_iterator nodes_begin(ControlFlowTree* F) { return &F->nodes[0]; } static nodes_iterator nodes_end(ControlFlowTree* F) { return &F->nodes[F->size]; } static size_t size(ControlFlowTree* F) { return F->size; } }; } namespace xreate{ namespace dominators { class DominatorTree: public llvm::DominatorTreeBase { public: DominatorsTreeAnalysisProvider::Dominators dominators; DominatorTree(bool isPostDom): llvm::DominatorTreeBase(isPostDom) {} void run(ControlFlowTree& program){ recalculate(program); //extract dominators info for (auto& entry: DomTreeNodes){ if (!entry.getFirst()) continue; dominators.emplace(entry.getFirst()->scope, make_pair(entry.getSecond()->getDFSNumIn(), entry.getSecond()->getDFSNumOut())); } } void print(std::ostringstream& output, const std::string& atom) const { boost::format formatAtom(atom + "(%1%, range(%2%, %3%))."); for (auto entry: dominators){ output << formatAtom % (entry.first) % (entry.second.first) % (entry.second.second) << endl; } } }; void -DominatorsTreeAnalysisProvider::run(const ClaspLayer* engine){ - boost::scoped_ptr program(ControlFlowTree::build(engine)); +DominatorsTreeAnalysisProvider::run(const ClaspLayer* engine, cfa::CFAGraph* graph){ + boost::scoped_ptr program(ControlFlowTree::build(engine, graph)); treeForwardDominators->run(*program); treePostDominators->run(*program); } void DominatorsTreeAnalysisProvider::print(std::ostringstream& output) const{ treeForwardDominators->print(output, "cfa_forwdom"); treePostDominators->print(output, "cfa_postdom"); } const DominatorsTreeAnalysisProvider::Dominators& DominatorsTreeAnalysisProvider::getForwardDominators() const{ return treeForwardDominators->dominators; } const DominatorsTreeAnalysisProvider::Dominators& DominatorsTreeAnalysisProvider::getPostDominators() const{ return treePostDominators->dominators; } DominatorsTreeAnalysisProvider::DominatorsTreeAnalysisProvider() : treeForwardDominators(new DominatorTree(false)) , treePostDominators(new DominatorTree(true)) {} DominatorsTreeAnalysisProvider::~DominatorsTreeAnalysisProvider() {} }} //end of namespace xreate::dominators //void //CodeScopesTree::print(){ // typedef llvm::GraphTraits Traits; // for (size_t i=0; i" << (*j)->scope << endl; // } // } //} diff --git a/cpp/src/analysis/DominatorsTreeAnalysisProvider.h b/cpp/src/analysis/DominatorsTreeAnalysisProvider.h index 5363c6d..f0223c6 100644 --- a/cpp/src/analysis/DominatorsTreeAnalysisProvider.h +++ b/cpp/src/analysis/DominatorsTreeAnalysisProvider.h @@ -1,42 +1,42 @@ /* 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: DominatorsTreeAnalysisProvider.h * Author: pgess * * Created on May 13, 2016, 11:39 AM */ #ifndef DOMINATORSTREEANALYSISPROVIDER_H #define DOMINATORSTREEANALYSISPROVIDER_H #include "clasplayer.h" #include namespace xreate{namespace dominators{ class DominatorTree; /** \brief Dominators Analysis report */ class DominatorsTreeAnalysisProvider: public IAnalysisReport { public: typedef std::pair DominatedRange; typedef std::map Dominators; DominatorsTreeAnalysisProvider(); virtual ~DominatorsTreeAnalysisProvider(); - void run(const ClaspLayer* engine); - void print(std::ostringstream& output) const; + void run(const ClaspLayer* engine, cfa::CFAGraph* graph); + void print(std::ostringstream& output) const override; const Dominators& getForwardDominators() const; const Dominators& getPostDominators() const; private: boost::scoped_ptr treeForwardDominators; boost::scoped_ptr treePostDominators; }; }} //end of namespace xreate::dominators #endif /* DOMINATORSTREEANALYSISPROVIDER_H */ diff --git a/cpp/src/analysis/aux.cpp b/cpp/src/analysis/aux.cpp index e7e2e1f..109a6aa 100644 --- a/cpp/src/analysis/aux.cpp +++ b/cpp/src/analysis/aux.cpp @@ -1,155 +1,142 @@ /* 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/. * * aux.cpp * * Author: pgess */ /** * \file aux.h * \brief Data representation in ASP format ready for use by reasoner */ #include "aux.h" #include namespace xreate { namespace analysis { using namespace std; list multiplyLists(list> &&lists) { typedef list StringList; assert(lists.size()); StringList result(*lists.begin()); lists.pop_front(); boost::format concat("%s, %s"); for (StringList &list: lists) { StringList::const_iterator end = result.end(); for (StringList::iterator expr1I = result.begin(); expr1I != end; ++expr1I) { if (list.size() == 0) continue; StringList::const_iterator expr2I = list.begin(); for (int expr2No = 0, size = list.size() - 1; expr2No < size; ++expr2No, ++expr1I) result.push_back(str(concat %(*expr1I) %(*expr2I))); *expr1I = str(concat %(*expr1I) %(*expr2I)); } } return result; } std::list compile(const Expression &e){ list result; switch (e.op) { case Operator::CALL: { assert(e.__state == Expression::COMPOUND); if(!e.operands.size()){ result.push_back(e.getValueString()); break; } std::list> operands; std::transform(e.operands.begin(), e.operands.end(), std::inserter(operands, operands.begin()), [](const Expression &e) { return compile(e); }); list &&operands_ = multiplyLists(std::move(operands)); result.push_back(boost::str(boost::format("%1%(%2%)") % (e.getValueString()) % (boost::algorithm::join(operands_, ", ")))); break; } case Operator::NEG: { assert(e.operands.size() == 1); const Expression &op = e.operands.at(0); list &&rawOp = compile(op); assert(rawOp.size() == 1); result.push_back((boost::format("not %1%")%(rawOp.front())).str()); break; }; case Operator::NONE: { switch (e.__state) { case Expression::IDENT: result.push_back(e.getValueString()); break; case Expression::NUMBER: result.push_back(to_string(e.getValueDouble())); break; default: assert(true); } break; } default: break; } //TODO Null ad hoc ClaspLayer implementation // if (e.isNone()){ // result.push_back(e.__valueS); // } assert(result.size()); return result; } std::list compileNeg(const Expression &e){ list result; switch (e.op) { case Operator::IMPL: { assert(e.__state == Expression::COMPOUND); assert(e.operands.size() == 2); list operands1 = compile(e.operands.at(0)); list operands2 = compile(e.operands.at(1)); boost::format formatNeg("%1%, not %2%"); for (const auto &op1: operands1) for (const auto &op2: operands2) { result.push_back(boost::str(formatNeg %(op1) % (op2))); } break; } case Operator::NEG: { assert(e.operands.size() == 1); const Expression &op = e.operands.at(0); list &&rawOp = compile(op); assert(rawOp.size() == 1); result.push_back(rawOp.front()); break; }; default: assert(true); } return result; } - -boost::format -formatSymbol(const SymbolPacked& s){ - boost::format formatSymbNamed("(%1%, %2%, %3%)"); - boost::format formatSymbAnonymous("anonym(%1%, %2%)"); - - if (!s.categoryTransient){ - return formatSymbNamed % s.identifier % s.version % s.scope; - } else { - return formatSymbAnonymous % s.identifier % s.scope; - } -} - }} diff --git a/cpp/src/analysis/aux.h b/cpp/src/analysis/aux.h index e470d5d..ec6a332 100644 --- a/cpp/src/analysis/aux.h +++ b/cpp/src/analysis/aux.h @@ -1,28 +1,27 @@ /* 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: aux.h * Author: pgess * * Created on June 26, 2016, 6:49 PM */ #ifndef AUX_H #define AUX_H #include "ast.h" #include "clasplayer.h" #include #include namespace xreate { namespace analysis { std::list compile(const Expression &e); std::list compileNeg(const Expression &e); std::list multiplyLists(std::list> &&lists); - boost::format formatSymbol(const SymbolPacked& s); }} #endif /* AUX_H */ diff --git a/cpp/src/analysis/cfagraph.h b/cpp/src/analysis/cfagraph.h index 5219525..49c66d6 100644 --- a/cpp/src/analysis/cfagraph.h +++ b/cpp/src/analysis/cfagraph.h @@ -1,60 +1,60 @@ /* 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: CFAGraph.h * Author: pgess * * Created on June 27, 2016, 2:09 PM */ #ifndef CFAGRAPH_H #define CFAGRAPH_H #include "clasplayer.h" namespace xreate {namespace cfa { /** \brief Represents CFA analysis data produced by CFAPass */ class CFAGraph: public IAnalysisReport { public: typedef boost::bimap> PARENT_FUNCTION_RELATIONS; PARENT_FUNCTION_RELATIONS __parentFunctionRelations; std::map __parentScopeRelations; typedef boost::bimap< boost::bimaps::multiset_of, boost::bimaps::multiset_of, boost::bimaps::set_of_relation<> > CALL_RELATIONS; CALL_RELATIONS __callRelations; boost::bimap __nodesFunction; std::multimap __functionTags; std::multimap __scopeTags; std::multimap __contextRules; - void print(std::ostringstream& output) const; + void print(std::ostringstream& output) const override; CFAGraph(ClaspLayer* engine): __clasp(engine){} void addFunctionAnnotations(const std::string& function, const std::map& tags); void addScopeAnnotations(const ScopePacked& scope, const std::vector&tags); void addContextRules(const ScopePacked& scope, const std::vector&rules); void addCallConnection(const ScopePacked& scopeFrom, const std::string& functionTo); void addParentConnection(const ScopePacked& scope, const std::string& functionParent); void addParentConnection(const ScopePacked& scope, const ScopePacked& scopeParent); // void addScopeRetIdentifier(const ScopePacked& scope, const SymbolPacked& identifier); private: ClaspLayer* __clasp; unsigned int registerNodeFunction(const std::string& fname); }; }} //end of namespace xreate::cfa #endif /* CFAGRAPH_H */ diff --git a/cpp/src/analysis/dfagraph.cpp b/cpp/src/analysis/dfagraph.cpp index 38a9601..f5c01e5 100644 --- a/cpp/src/analysis/dfagraph.cpp +++ b/cpp/src/analysis/dfagraph.cpp @@ -1,260 +1,227 @@ /* 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: DFAGraph.h * Author: pgess * */ /** * \file dfagraph.h * \brief Data Flow Analysis(DFA) graph data * */ #include "analysis/dfagraph.h" #include "analysis/aux.h" #include - using namespace std; -namespace xreate { namespace dfa { - - void - DFAGraph::print(std::ostringstream& output) const { - std::set symbols; - - output << endl << "%\t\tStatic analysis: DFA" << endl; - - std::vector>::const_iterator i1; - std::vector::const_iterator i2; - - boost::format formatDfaConnection("dfa_connection(%1%, %2%, %3%)."); - for (i1 = this->__edges.begin(), i2 = this->__data.begin(); i1 != this->__edges.end(); ++i1, ++i2) { - string edgeName; - switch (*i2) { - case DFGConnection::WEAK: edgeName = "weak"; - break; - case DFGConnection::STRONG: edgeName = "strong"; - break; - case DFGConnection::PROTOTYPE: edgeName = "proto"; - break; - } - - output << formatDfaConnection - % analysis::formatSymbol(i1->first) - % analysis::formatSymbol(i1->second) - % edgeName - << " %" << this->__clasp->getHintForPackedSymbol(i1->first) << " - " << this->__clasp->getHintForPackedSymbol(i1->second) - << endl; - - symbols.insert(i1->first); - symbols.insert(i1->second); - } - - boost::format formatDfaDependency("dfa_dependency(%1%, %2%)."); - for (auto i = this->__dependencies.begin(); i != this->__dependencies.end(); ++i) { - output << formatDfaDependency - % analysis::formatSymbol(i->first) - % analysis::formatSymbol(i->second) - << " %" - << this->__clasp->getHintForPackedSymbol(i->first) << " - " - << this->__clasp->getHintForPackedSymbol(i->second) - << endl; - } - - boost::format formatBind("bind(%1%, %2%)."); - for (const pair& tag : this->__tags) { - for (string variant : xreate::analysis::compile(tag.second)) { - output << formatBind - % analysis::formatSymbol(tag.first) - % (variant) - << "%" << this->__clasp->getHintForPackedSymbol(tag.first) - << endl; - } - - symbols.insert(tag.first); - } - for (const SymbolPacked& s : symbols) { - output << "v(" << analysis::formatSymbol(s) << ")." - << " %" << this->__clasp->getHintForPackedSymbol(s) - << endl; - } +namespace xreate {namespace dfa { + struct VisitorNodeHash : public boost::static_visitor + { + std::size_t operator()(const xreate::SymbolPacked& node) const noexcept + { + return 2* (node.identifier + 2 * node.scope + 3 * std::abs(node.version)) + 1; } - class VisitorAddTag : public boost::static_visitor<> { - public: - - void operator()(const SymbolPacked& symbol) { - __graph->__tags.emplace(symbol, move(__tag)); - } - - void operator()(SymbolAnonymous& symbol) { - symbol.tags.push_back(move(__tag)); - } - - void operator()(const SymbolInvalid& symbol) { - assert(false && "Undefined behaviour"); - } - - VisitorAddTag(DFAGraph * const dfagraph, Expression&& tag) : - __graph(dfagraph), __tag(tag) { - } - - private: - DFAGraph * const __graph; - Expression __tag; - }; - - class VisitorAddLink : public boost::static_visitor<> { - public: - - void operator()(const SymbolPacked& nodeFrom) { - if (!__graph->isConnected(__nodeTo, nodeFrom)) { - __graph->__edges.emplace_back(__nodeTo, nodeFrom); - __graph->__data.push_back(__link); - - DFAGraph::EdgeId eid = __graph->__edges.size() - 1; - __graph->__outEdges.emplace(nodeFrom, eid); - } - } - - void operator()(const SymbolAnonymous& symbolFrom) { - switch (__link) { - case DFGConnection::WEAK: - { - //virtual symbol to hold transient annotations - SymbolPacked symbPivot = __graph->createAnonymousSymbol(symbolFrom.scope); - - __graph->addConnection(symbPivot, symbolFrom, DFGConnection::STRONG); - __graph->addConnection(__nodeTo, symbPivot, DFGConnection::WEAK); - break; - } - - case DFGConnection::STRONG: - { - for (const Expression& tag : symbolFrom.tags) { - __graph->__tags.emplace(__nodeTo, tag); - } - break; - } - - default: - assert(false && "Undefined behavior"); - } - } - - void operator()(const SymbolInvalid&) { - if (__link == DFGConnection::STRONG) return; - if (__link == DFGConnection::WEAK) return; - - assert(false && "Undefined behavior"); - } - - VisitorAddLink(DFAGraph * const dfagraph, const SymbolPacked& nodeTo, DFGConnection link) : - __graph(dfagraph), __nodeTo(nodeTo), __link(link) { - } - - private: - DFAGraph * const __graph; - SymbolPacked __nodeTo; - DFGConnection __link; - }; - - class VisitorGetDependencyConnection : public boost::static_visitor> + std::size_t operator()(const xreate::dfa::SymbolAnonymous& node) const noexcept { - public: - - list - operator()(const SymbolPacked & nodeFrom) { - return - { - nodeFrom - }; - } - - list - operator()(const SymbolAnonymous & nodeFrom) { - return nodeFrom.dependencies; - } - - list - operator()(const SymbolInvalid&) { - assert(false && "Undefined behavior"); - } - - VisitorGetDependencyConnection(DFAGraph * const g) : graph(g) { - } - DFAGraph * const graph; - }; - - class VisitorSetDependencyConnection : public boost::static_visitor<> { - public: - - void operator()(SymbolPacked& nodeTo) { - VisitorGetDependencyConnection visitorGetDepenencies(graph); - auto deps = boost::apply_visitor(visitorGetDepenencies, nodeFrom); - - for (const SymbolPacked& dep : deps) { - graph->__dependencies.emplace(nodeTo, dep); - } - } - - void operator()(SymbolAnonymous& nodeTo) { - VisitorGetDependencyConnection visitorGetDepenencies(graph); - auto deps = boost::apply_visitor(visitorGetDepenencies, nodeFrom); + return 2 * node.id; + } + }; +}} - for (const SymbolPacked& dep : deps) { - nodeTo.dependencies.push_back(dep); - } - } +std::size_t +hash::operator()(xreate::dfa::SymbolNode const& s) const noexcept +{ + return boost::apply_visitor(xreate::dfa::VisitorNodeHash(), s); +} - void operator()(SymbolInvalid&) { - assert(false && "Undefined behavior"); - } +namespace xreate { namespace dfa { - VisitorSetDependencyConnection(DFAGraph * const g, SymbolNode s) : graph(g), nodeFrom(s) { +bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2){ + return s1.id == s2.id && s1.flagIsUsed == s2.flagIsUsed; +} + +class VisitorFormatSymbol: public boost::static_visitor { +public: + + boost::format operator()(const SymbolPacked& node) const { + //boost::format formatSymbNamed("s(%1%, %2%, %3%)"); + boost::format formatSymbNamed("s%1%%2%"); + return formatSymbNamed % node.identifier /*% node.version*/ % node.scope ; + } + + boost::format operator()(const SymbolAnonymous& node) const { + //boost::format formatSymbAnonymous("anon(%1%)"); + boost::format formatSymbAnonymous("a%1%"); + return formatSymbAnonymous % node.id; + } +}; + +void +DFACallInstance::print(std::ostringstream& output) const{ + boost::format formatInstance("dfa_callinstance(%1%, %2%)."); + boost::format formatArgs("dfa_callargs(%1%, %2%, %3%)."); + boost::format formatRet("dfa_callret(%1%, %2%)."); + + output << formatInstance + % id % fnName + << endl; + + for(std::pair rec: args) { + SymbolNode argFormal(rec.first); + + output << formatArgs + % id + % boost::apply_visitor(VisitorFormatSymbol(), argFormal) + % boost::apply_visitor(VisitorFormatSymbol(), rec.second) + << endl; + } + + output << formatRet + % id + % boost::apply_visitor(VisitorFormatSymbol(), retActual) + << endl; +} + +void +DFAGraph::addDependency(const SymbolNode& node, const SymbolNode& subnode){ + __dependencies.emplace(node, subnode); +} + +void +DFAGraph::printDependencies(std::ostringstream& output) const{ + for(const SymbolNode& root: __roots){ + printDependency(output, root, root); + } +} + +void +DFAGraph::printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const { + auto range = __dependencies.equal_range(nodeCurrent); + for (auto it = range.first; it != range.second; ++it){ + if (const SymbolAnonymous* symbAnon = boost::get(&it->second)){ + if (!__usedAnonymousSymbols.count(symbAnon->id)){ + printDependency(output, it->second, nodeDependent); + continue; } - DFAGraph * const graph; - SymbolNode nodeFrom; - }; + } - bool - DFAGraph::isConnected(const SymbolPacked& identifierTo, const SymbolPacked& identifierFrom) { - auto range = __outEdges.equal_range(identifierFrom); + boost::format formatDependency("dfa_depends(%1%, %2%)."); + + output << formatDependency + % boost::apply_visitor(VisitorFormatSymbol(), nodeDependent) + % boost::apply_visitor(VisitorFormatSymbol(), it->second) + << endl; + + printDependency(output, it->second, it->second); + } +} + +void +DFAGraph::printInplaceAnnotations(SymbolNode node, const Expression& expression) { + // write down in-place expression tags: + boost::format formatBind("bind(%1%, %2%)."); + + if (expression.tags.size()) + if (boost::get(&node)){ + markSymbolsAsUsed(vector({node})); + } + + for (const pair& tag : expression.tags) { + for (const string& tagPart: xreate::analysis::compile(tag.second)) { + __output << formatBind + % boost::apply_visitor(VisitorFormatSymbol(), node) + % tagPart + << endl; + } + } +} + +void +DFAGraph::printAlias(const SymbolNode& symbolFormal, const SymbolNode& symbActual){ + boost::format formatAlias("dfa_alias(%1%, %2%)."); + __output << formatAlias + % boost::apply_visitor(VisitorFormatSymbol(), symbolFormal) + % boost::apply_visitor(VisitorFormatSymbol(), symbActual) + << endl; +} + +void +DFAGraph::printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet){ + boost::format formatRet("dfa_fnret(%1%, %2%)."); + + __output << formatRet + % function->getName() + % boost::apply_visitor(VisitorFormatSymbol(), symbolRet) + << endl; + + __roots.insert(symbolRet); +} + +void +DFAGraph::addCallInstance(DFACallInstance&& instance){ + __callInstances.push_back(std::move(instance)); +} + +void +DFAGraph::print(std::ostringstream& output) const{ + output << endl << "%\t\tStatic analysis: DFA" << endl; + + //Dependencies + printDependencies(output); + + //Add generated report + output << __output.str() << endl; + + //Call instances + for(const DFACallInstance& instance: __callInstances){ + instance.print(output); + } + + output << endl; +} + +void +DFAGraph::markSymbolsAsUsed(const std::vector& usedNodes){ + for (const SymbolNode& node: usedNodes){ + if (const SymbolAnonymous* symbol = boost::get(&node)){ + __usedAnonymousSymbols.insert(symbol->id); + } + } +} - for (std::multimap::iterator edge = range.first; edge != range.second; ++edge) { - if (__edges[edge->second].second == identifierTo) - return true; - } +bool +DFAGraph::isSymbolUsed(const SymbolNode& node) const{ + if (const SymbolAnonymous* symbol = boost::get(&node)){ + if (!__usedAnonymousSymbols.count(symbol->id)) {return false;} + } - return false; - } + return true; +} - void - DFAGraph::addConnection(const SymbolPacked& nodeTo, const SymbolNode& nodeFrom, DFGConnection link) { - VisitorAddLink visitor(this, nodeTo, link); - boost::apply_visitor(visitor, nodeFrom); - } +void +DFAGraph::printSymbols(ClaspLayer* clasp){ + std::unordered_set symbols; - void - DFAGraph::addDependencyConnection(SymbolNode& identifierTo, SymbolNode& identifierFrom) { - VisitorSetDependencyConnection visitor(this, identifierFrom); - boost::apply_visitor(visitor, identifierTo); - } + for (auto entry: __dependencies){ + if (isSymbolUsed(entry.first)) {symbols.insert(entry.first);} + if (isSymbolUsed(entry.second)) {symbols.insert(entry.second);} + } - void - DFAGraph::addAnnotation(SymbolNode& node, Expression&& tag) { - VisitorAddTag visitor(this, move(tag)); - boost::apply_visitor(visitor, node); - } + for (const SymbolNode& node : symbols) { + __output << "v(" << boost::apply_visitor(VisitorFormatSymbol(), node) << ")."; - SymbolPacked - DFAGraph::createAnonymousSymbol(const ScopePacked& scope) { - return SymbolPacked(ScopedSymbol{__countAnonymousSymbols++, 0}, scope, true); + if (const SymbolPacked* symbol = boost::get(&node)){ + __output << " %" << clasp->getHintForPackedSymbol(*symbol); } + __output << endl; + } +} }} //end of namespace xreate::dfa diff --git a/cpp/src/analysis/dfagraph.h b/cpp/src/analysis/dfagraph.h index 531151e..ff297e5 100644 --- a/cpp/src/analysis/dfagraph.h +++ b/cpp/src/analysis/dfagraph.h @@ -1,64 +1,74 @@ /* 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: dfa.h * Author: pgess * * Created on June 27, 2016, 1:50 PM */ #ifndef DFA_H #define DFA_H #include "clasplayer.h" +#include namespace xreate {namespace dfa { - struct SymbolAnonymous { - SymbolAnonymous(unsigned int symbolId): id(symbolId){} - unsigned int id; - std::list tags; - ScopePacked scope; - std::list dependencies; + bool flagIsUsed = false; }; - struct SymbolInvalid { }; - - typedef boost::variant SymbolNode; - - /** \brief Holds DFA Analysis report produced by DFAPass */ - class DFAGraph: public IAnalysisReport{ - friend class VisitorAddTag; - friend class VisitorAddLink; - friend class VisitorGetDependencyConnection; - friend class VisitorSetDependencyConnection; - - - public: - DFAGraph(ClaspLayer* engine): __clasp(engine){} - - SymbolPacked createAnonymousSymbol(const ScopePacked& scope); - void addAnnotation(SymbolNode& identifier, Expression&& tag); - void addConnection(const SymbolPacked& identifierTo, const SymbolNode& identifierFrom, DFGConnection link); - void addDependencyConnection(SymbolNode& identifierTo, SymbolNode& identifierFrom); - bool isConnected(const SymbolPacked& identifierTo, const SymbolPacked& identifierFrom); + bool operator==(const SymbolAnonymous& s1, const SymbolAnonymous& s2); - void print(std::ostringstream& output) const; - - private: - typedef unsigned int EdgeId; - std::vector> __edges; - std::multimap __outEdges; - std::vector __data; - std::multimap __tags; - std::multimap __dependencies; + typedef boost::variant SymbolNode; +}} - unsigned int __countAnonymousSymbols=0; - ClaspLayer* __clasp; +namespace std{ + template<> + struct hash{ + std::size_t operator()(xreate::dfa::SymbolNode const& s) const noexcept; }; +} + +namespace xreate {namespace dfa { +class DFACallInstance{ +public: + unsigned int id; + std::string fnName; + std::vector> args; + SymbolNode retActual; + + void print(std::ostringstream& output) const; +}; + +/** \brief Holds DFA Analysis report produced by DFAPass */ +class DFAGraph: public IAnalysisReport{ +public: + // DFAGraph(ClaspLayer* engine): __clasp(engine){} + virtual void print(std::ostringstream& output) const override; + void addCallInstance(DFACallInstance && instance); + void addDependency(const SymbolNode& node, const SymbolNode& subnodeBlock); + void printInplaceAnnotations(SymbolNode node, const Expression& expression); + void printAlias(const SymbolNode& symbolFormal, const SymbolNode& symbActual); + void printFunctionRet(ManagedFnPtr function, const SymbolNode& symbolRet); + void printDependencies(std::ostringstream& output) const; + void markSymbolsAsUsed(const std::vector& usedNodes); + void printSymbols(ClaspLayer* clasp); + +private: + mutable std::ostringstream __output; + std::list __callInstances; + std::unordered_multimap __dependencies; + std::unordered_set __usedAnonymousSymbols; + std::unordered_set __roots; + + + void printDependency(std::ostringstream& output, const SymbolNode& nodeCurrent, const SymbolNode& nodeDependent) const; + bool isSymbolUsed(const SymbolNode& node) const; +}; }} // end of namespace xreate::dfa #endif /* DFA_H */ diff --git a/cpp/src/analysis/typeinference.cpp b/cpp/src/analysis/typeinference.cpp index 3107a24..ec38c58 100644 --- a/cpp/src/analysis/typeinference.cpp +++ b/cpp/src/analysis/typeinference.cpp @@ -1,68 +1,68 @@ /* 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/. * * typeinference.cpp * * Author: pgess * Created on April 16, 2017, 10:13 AM */ /** * \file typeinference.h * \brief Type inference analysis */ #include "typeinference.h" #include "llvmlayer.h" #include "llvm/IR/Function.h" #include "llvm/IR/DerivedTypes.h" namespace xreate {namespace typeinference { //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) llvm::Value* doAutomaticTypeConversion(llvm::Value* source, llvm::Type* tyTarget, llvm::IRBuilder<>& builder){ if (tyTarget->isIntegerTy() && source->getType()->isIntegerTy()) { llvm::IntegerType* tyTargetInt = llvm::dyn_cast(tyTarget); llvm::IntegerType* tySourceInt = llvm::dyn_cast(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; } ExpandedType getType(const Expression& expression, const AST& ast){ if (expression.type.isValid()){ return ast.expandType(expression.type); } if (expression.__state == Expression::IDENT){ - Symbol s = Attachments::get(expression); + Symbol s = Attachments::get(expression); return getType(CodeScope::getDefinition(s), ast); } assert(false && "Type can't be determined for an expression"); } } } //end of namespace xreate::typeinference diff --git a/cpp/src/ast.cpp b/cpp/src/ast.cpp index 6a8eae8..70c3923 100644 --- a/cpp/src/ast.cpp +++ b/cpp/src/ast.cpp @@ -1,968 +1,976 @@ /* 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/. * * Author: pgess * File: ast.cpp */ /** * \file ast.h * \brief Syntax Tree and related code * * \sa xreate::AST */ #include "ast.h" #include "ExternLayer.h" #include "analysis/typeinference.h" #include #include //TODO BDecl. forbid multiple body declaration (ExprTyped) namespace std { std::size_t hash::operator()(xreate::ScopedSymbol const& s) const { return s.id ^ (s.version << 2); } bool equal_to::operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const { return __x.id == __y.id && __x.version == __y.version; } size_t hash::operator()(xreate::Symbol const& s) const { return hash()(s.identifier) ^ ((long int) s.scope << 1); } bool equal_to::operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const { return __x == __y; }; } using namespace std; namespace xreate { Atom::Atom(const std::wstring& value) { __value = wstring_to_utf8(value); } Atom::Atom(std::string && name) : __value(name) { } const std::string& Atom::get() const { return __value; } Atom::Atom(wchar_t* value) { //DEBT reconsider number literal recognition __value = wcstol(value, 0, 10); } Atom::Atom(int value) : __value(value) { } double Atom::get()const { return __value; } Atom::Atom(const std::wstring& value) { assert(value.size() >= 2); __value = wstring_to_utf8(value.substr(1, value.size() - 2)); } Atom::Atom(std::string && name) : __value(name) {} const std::string& Atom::get() const { return __value; } class ExpressionHints { public: static bool isStringValueValid(const Expression& e) { switch (e.__state) { case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: return true; case Expression::NUMBER: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::CALL: return true; default: return false; } } } return false; } static bool isDoubleValueValid(const Expression& e) { switch (e.__state) { case Expression::NUMBER: return true; case Expression::INVALID: assert(false); case Expression::IDENT: case Expression::STRING: case Expression::BINDING: return false; case Expression::COMPOUND: { switch (e.op) { case Operator::VARIANT: return true; default: return false; } } } return false; } }; class TypesResolver { private: const AST* ast; std::map scope; std::map signatures; ExpandedType expandType(const TypeAnnotation &t, const std::vector &args = std::vector()) { return TypesResolver(ast, scope, signatures)(t, args); } std::vector expandOperands(const std::vector& operands) { std::vector pack; pack.reserve(operands.size()); std::transform(operands.begin(), operands.end(), std::inserter(pack, pack.end()), [this](const TypeAnnotation & t) { return expandType(t); }); return pack; } public: TypesResolver(const AST* root, const std::map& scopeOuter = std::map(), std::map signaturesOuter = std::map()) : ast(root), scope(scopeOuter), signatures(signaturesOuter) { } ExpandedType operator()(const TypeAnnotation &t, const std::vector &args = std::vector()) { //assert(args.size() == t.bindings.size()); // invalid number of arguments for (size_t i = 0; i < args.size(); ++i) { scope[t.bindings.at(i)] = args.at(i); } switch (t.__operator) { case TypeOperator::ARRAY: { assert(t.__operands.size() == 1); Expanded elTy = expandType(t.__operands.at(0)); return ExpandedType(TypeAnnotation(tag_array, elTy, 0)); } case TypeOperator::STRUCT: { assert(t.__operands.size()); std::vector&& packOperands = expandOperands(t.__operands); auto typNew = TypeAnnotation(TypeOperator::STRUCT, move(packOperands)); typNew.fields = t.fields; return ExpandedType(move(typNew)); }; case TypeOperator::CALL: { std::string alias = t.__valueCustom; //find in local scope: TypeAnnotation ty; if (scope.count(alias)) { ty = scope.at(alias); } else if (ast->__indexTypeAliases.count(alias)) { ty = ast->__indexTypeAliases.at(alias); } else { assert(false && "Undefined or external type"); } std::vector&& operands = expandOperands(t.__operands); TypeAnnotation signature(TypeOperator::CALL, move(operands)); signature.__valueCustom = alias; if (signatures.count(signature)) { auto link = TypeAnnotation(TypeOperator::LINK,{}); link.conjuctionId = signatures.at(signature); return ExpandedType(move(link)); } int cid = signatures.size(); signatures[signature] = cid; TypeAnnotation tyResult = expandType(ty, operands); tyResult.conjuctionId = cid; return ExpandedType(move(tyResult)); }; case TypeOperator::CUSTOM: { std::string alias = t.__valueCustom; /* if (signatures.count(alias)) { return ExpandedType(TypeAnnotation(TypeOperator::LINK, {t})); } signatures[alias].emplace(t); */ //find in local scope: if (scope.count(alias)) { return expandType(scope.at(alias)); } // find in general scope: if (ast->__indexTypeAliases.count(alias)) { return expandType(ast->__indexTypeAliases.at(t.__valueCustom)); } //if type is unknown keep it as is. return ExpandedType(TypeAnnotation(t)); }; case TypeOperator::ACCESS: { std::string alias = t.__valueCustom; ExpandedType tyAlias = ExpandedType(TypeAnnotation()); //find in local scope: if (scope.count(alias)) { tyAlias = expandType(scope.at(alias)); //find in global scope: } else if ((ast->__indexTypeAliases.count(alias))) { tyAlias = expandType(ast->__indexTypeAliases.at(alias)); } else { assert(false && "Undefined or external type"); } assert(tyAlias->__operator == TypeOperator::STRUCT); for (const string& field : t.fields) { auto fieldIt = std::find(tyAlias->fields.begin(), tyAlias->fields.end(), field); assert(fieldIt != tyAlias->fields.end() && "unknown field"); int fieldId = fieldIt - tyAlias->fields.begin(); tyAlias = expandType(tyAlias->__operands.at(fieldId)); } return tyAlias; } case TypeOperator::VARIANT: { return ExpandedType(TypeAnnotation(t)); } case TypeOperator::NONE: { return ExpandedType(TypeAnnotation(t)); } default: assert(false); } assert(false); return ExpandedType(TypeAnnotation()); } }; TypeAnnotation::TypeAnnotation() : __operator(TypeOperator::NONE), __value(TypePrimitive::Invalid) { } TypeAnnotation::TypeAnnotation(TypePrimitive typ) : __value(typ) { } TypeAnnotation::TypeAnnotation(TypeOperator op, std::initializer_list operands) : __operator(op), __operands(operands) { } TypeAnnotation::TypeAnnotation(TypeOperator op, std::vector&& operands) : __operator(op), __operands(operands) { } TypeAnnotation::TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size) : TypeAnnotation(TypeOperator::ARRAY,{typ}) { __size = size; } bool TypeAnnotation::isValid() const { return !(__value == TypePrimitive::Invalid && __operator == TypeOperator::NONE); } bool TypeAnnotation::operator<(const TypeAnnotation& t) const { if (__operator != t.__operator) return __operator < t.__operator; if (__operator == TypeOperator::NONE) return __value < t.__value; if (__operator == TypeOperator::CALL || __operator == TypeOperator::CUSTOM || __operator == TypeOperator::ACCESS) { if (__valueCustom != t.__valueCustom) return __valueCustom < t.__valueCustom; } return __operands < t.__operands; } /* TypeAnnotation (struct_tag, std::initializer_list) {} */ void TypeAnnotation::addBindings(std::vector>&& params) { bindings.reserve(bindings.size() + params.size()); std::transform(params.begin(), params.end(), std::inserter(bindings, bindings.end()), [](const Atom& ident) { return ident.get(); }); } void TypeAnnotation::addFields(std::vector>&& listFields) { fields.reserve(fields.size() + listFields.size()); std::transform(listFields.begin(), listFields.end(), std::inserter(fields, fields.end()), [](const Atom& ident) { return ident.get(); }); } unsigned int Expression::nextVacantId = 0; Expression::Expression(const Atom& number) : Expression() { __state = NUMBER; op = Operator::NONE; __valueD = number.get(); } Expression::Expression(const Atom& a) : Expression() { __state = STRING; op = Operator::NONE; __valueS = a.get(); } Expression::Expression(const Atom &ident) : Expression() { __state = IDENT; op = Operator::NONE; __valueS = ident.get(); } Expression::Expression(const Operator &oprt, std::initializer_list params) : Expression() { __state = COMPOUND; op = oprt; if (op == Operator::CALL) { assert(params.size() > 0); Expression arg = *params.begin(); assert(arg.__state == Expression::IDENT); __valueS = std::move(arg.__valueS); operands.insert(operands.end(), params.begin() + 1, params.end()); return; } operands.insert(operands.end(), params.begin(), params.end()); } void Expression::setOp(Operator oprt) { op = oprt; switch (op) { case Operator::NONE: __state = INVALID; break; default: __state = COMPOUND; break; } } void Expression::addArg(Expression &&arg) { operands.push_back(arg); } void Expression::addTags(const std::list tags) const { std::transform(tags.begin(), tags.end(), std::inserter(this->tags, this->tags.end()), [](const Expression & tag) { return make_pair(tag.getValueString(), tag); }); } void Expression::addBindings(std::initializer_list> params) { addBindings(params.begin(), params.end()); } void Expression::bindType(TypeAnnotation t) { type = move(t); } void Expression::addBlock(ManagedScpPtr scope) { blocks.push_back(scope.operator->()); } const std::vector& Expression::getOperands() const { return operands; } double Expression::getValueDouble() const { return __valueD; } const std::string& Expression::getValueString() const { return __valueS; } void Expression::setValue(const Atom&& v) { __valueS = v.get(); } void Expression::setValueDouble(double value) { __valueD = value; } bool Expression::isValid() const { return (__state != INVALID); } bool Expression::isDefined() const { - return (__state != BINDING); + return (__state != BINDING && __state != INVALID); } Expression::Expression() : __state(INVALID), op(Operator::NONE), id(nextVacantId++) { } namespace details { namespace inconsistent { AST::AST() { Attachments::init(); - Attachments::init(); + Attachments::init(); + Attachments::init(); } void AST::addInterfaceData(const ASTInterface& interface, Expression&& data) { __interfacesData.emplace(interface, move(data)); } void AST::addDFAData(Expression &&data) { __dfadata.push_back(data); } void AST::addExternData(ExternData &&data) { __externdata.insert(__externdata.end(), data.entries.begin(), data.entries.end()); } void AST::add(Function* f) { __functions.push_back(f); __indexFunctions.emplace(f->getName(), __functions.size() - 1); } void AST::add(MetaRuleAbstract *r) { __rules.push_back(r); } void AST::add(TypeAnnotation t, Atom alias) { if (t.__operator == TypeOperator::VARIANT) { for (int i = 0, size = t.fields.size(); i < size; ++i) { __dictVariants.emplace(t.fields[i], make_pair(t, i)); } } __indexTypeAliases.emplace(alias.get(), move(t)); } ManagedScpPtr AST::add(CodeScope* scope) { this->__scopes.push_back(scope); return ManagedScpPtr(this->__scopes.size() - 1, &this->__scopes); } std::string AST::getModuleName() { const std::string name = "moduleTest"; return name; } ManagedPtr AST::findFunction(const std::string& name) { int count = __indexFunctions.count(name); if (!count) { return ManagedFnPtr::Invalid(); } assert(count == 1); auto range = __indexFunctions.equal_range(name); return ManagedPtr(range.first->second, &this->__functions); } std::list AST::getAllFunctions() const { const size_t size = __functions.size(); std::list result; for (size_t i = 0; i < size; ++i) { result.push_back(ManagedFnPtr(i, &this->__functions)); } return result; } //TASK select default specializations std::list AST::getFunctionSpecializations(const std::string& fnName) const { auto functions = __indexFunctions.equal_range(fnName); std::list result; std::transform(functions.first, functions.second, inserter(result, result.end()), [this](auto f) { return ManagedFnPtr(f.second, &this->__functions); }); return result; } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__functions); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__scopes); } template<> ManagedPtr AST::begin() { return ManagedPtr(0, &this->__rules); } void AST::recognizeVariantConstructor(Expression& function) { assert(function.op == Operator::CALL); std::string variant = function.getValueString(); if (!__dictVariants.count(variant)) { return; } auto record = __dictVariants.at(variant); const TypeAnnotation& typ = record.first; function.op = Operator::VARIANT; function.setValueDouble(record.second); function.type = typ; } Atom AST::recognizeVariantConstructor(Atom ident) { std::string variant = ident.get(); assert(__dictVariants.count(variant) && "Can't recognize variant constructor"); auto record = __dictVariants.at(variant); return Atom(record.second); } void AST::postponeIdentifier(CodeScope* scope, const Expression& id) { bucketUnrecognizedIdentifiers.emplace(scope, id); } void AST::recognizePostponedIdentifiers() { for (const auto& identifier : bucketUnrecognizedIdentifiers) { if (!identifier.first->recognizeIdentifier(identifier.second)) { //exception: Ident not found std::cout << "Unknown symbol: " << identifier.second.getValueString() << std::endl; assert(false && "Symbol not found"); } } } xreate::AST* AST::finalize() { //all finalization steps: recognizePostponedIdentifiers(); return reinterpret_cast (this); } } } //namespace details::incomplete Expanded AST::findType(const std::string& name) { // find in general scope: if (__indexTypeAliases.count(name)) return expandType(__indexTypeAliases.at(name)); //if type is unknown keep it as is. TypeAnnotation t(TypeOperator::CUSTOM,{}); t.__valueCustom = name; return ExpandedType(move(t)); } Expanded AST::expandType(const TypeAnnotation &t) const { return TypesResolver(this)(t); } ExpandedType AST::getType(const Expression& expression) { return typeinference::getType(expression, *this); } Function::Function(const Atom& name) : __entry(new CodeScope(0)) { __name = name.get(); } void Function::addTag(Expression&& tag, const TagModifier mod) { string name = tag.getValueString(); __tags.emplace(move(name), move(tag)); } const std::map& Function::getTags() const { return __tags; } CodeScope* Function::getEntryScope() const { return __entry; } void Function::addBinding(Atom && name, Expression&& argument) { __entry->addBinding(move(name), move(argument)); } const std::string& Function::getName() const { return __name; } ScopedSymbol CodeScope::registerIdentifier(const Expression& identifier) { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); auto result = __identifiers.emplace(identifier.getValueString(), __vCounter); if (result.second) { ++__vCounter; return { __vCounter - 1, version }; } return { result.first->second, version }; } bool CodeScope::recognizeIdentifier(const Expression& identifier) const { versions::VariableVersion version = Attachments::get(identifier, versions::VERSION_NONE); const std::string& name = identifier.getValueString(); //search identifier in the current block if (__identifiers.count(name)) { VNameId id = __identifiers.at(name); Symbol s; s.identifier = ScopedSymbol{id, version}; s.scope = const_cast (this); - Attachments::put(identifier, s); + Attachments::put(identifier, s); return true; } //search in the parent scope if (__parent) { return __parent->recognizeIdentifier(identifier); } return false; } ScopedSymbol CodeScope::getSymbol(const std::string& alias) { assert(__identifiers.count(alias)); VNameId id = __identifiers.at(alias); return {id, versions::VERSION_NONE }; } void CodeScope::addBinding(Expression&& var, Expression&& argument) { argument.__state = Expression::BINDING; __bindings.push_back(var.getValueString()); ScopedSymbol binding = registerIdentifier(var); __declarations[binding] = move(argument); } -void +Symbol CodeScope::addDefinition(Expression&& var, Expression&& body) { ScopedSymbol s = registerIdentifier(var); __declarations[s] = move(body); + + return Symbol{s, this}; } CodeScope::CodeScope(CodeScope* parent) : __parent(parent) { } CodeScope::~CodeScope() { } void CodeScope::setBody(const Expression &body) { assert(__declarations.count(ScopedSymbol::RetSymbol)==0 && "Attempt to reassign scope body"); __declarations[ScopedSymbol::RetSymbol] = body; } Expression& CodeScope::getBody() { return __declarations[ScopedSymbol::RetSymbol]; } const Expression& -CodeScope::getDefinition(const Symbol& symbol) { +CodeScope::getDefinition(const Symbol& symbol, bool flagAllowUndefined){ CodeScope* self = symbol.scope; - return self->getDefinition(symbol.identifier); + return self->getDefinition(symbol.identifier, flagAllowUndefined); } const Expression& -CodeScope::getDefinition(const ScopedSymbol& symbol) { - assert(__declarations.count(symbol) && "Symbol's declaration not found"); +CodeScope::getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined) const{ + static Expression expressionInvalid; + + if (!__declarations.count(symbol)){ + if (flagAllowUndefined) return expressionInvalid; + assert(false && "Symbol's declaration not found"); + } return __declarations.at(symbol); } void RuleArguments::add(const Atom &arg, DomainAnnotation typ) { emplace_back(arg.get(), typ); } void RuleGuards::add(Expression&& e) { push_back(e); } MetaRuleAbstract:: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards) : __args(std::move(args)), __guards(std::move(guards)) { } MetaRuleAbstract::~MetaRuleAbstract() { } RuleWarning:: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message) : MetaRuleAbstract(std::move(args), std::move(guards)), __message(message.get()), __condition(condition) { } RuleWarning::~RuleWarning() { } void RuleWarning::compile(ClaspLayer& layer) { //TODO restore addRuleWarning //layer.addRuleWarning(*this); } bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id < s2.id) || (s1.id == s2.id && s1.version < s2.version); } bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2) { return (s1.id == s2.id) && (s1.version == s2.version); } bool operator<(const Symbol& s1, const Symbol& s2) { return (s1.scope < s2.scope) || (s1.scope == s2.scope && s1.identifier < s2.identifier); } bool operator==(const Symbol& s1, const Symbol& s2) { return (s1.scope == s2.scope) && (s1.identifier == s2.identifier); } bool operator<(const Expression&a, const Expression&b) { if (a.__state != b.__state) return a.__state < b.__state; assert(a.__state != Expression::INVALID); switch (a.__state) { case Expression::IDENT: case Expression::STRING: return a.getValueString() < b.getValueString(); case Expression::NUMBER: return a.getValueDouble() < b.getValueDouble(); case Expression::COMPOUND: { assert(a.blocks.size() == 0); assert(b.blocks.size() == 0); if (a.op != b.op) { return a.op < b.op; } bool flagAValid = ExpressionHints::isStringValueValid(a); bool flagBValid = ExpressionHints::isStringValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueString() != b.getValueString()) { return a.getValueString() < b.getValueString(); } } flagAValid = ExpressionHints::isDoubleValueValid(a); flagBValid = ExpressionHints::isDoubleValueValid(b); if (flagAValid != flagBValid) { return flagAValid < flagBValid; } if (flagAValid) { if (a.getValueDouble() != b.getValueDouble()) { return a.getValueDouble() < b.getValueDouble(); } } if (a.operands.size() != b.operands.size()) { return (a.operands.size() < b.operands.size()); } for (size_t i = 0; i < a.operands.size(); ++i) { bool result = a.operands[i] < b.operands[i]; if (result) return true; } return false; } case Expression::BINDING: case Expression::INVALID: assert(false); } return false; } bool Expression::operator==(const Expression& other) const { if (this->__state != other.__state) return false; if (ExpressionHints::isStringValueValid(*this)) { if (this->__valueS != other.__valueS) return false; } if (ExpressionHints::isDoubleValueValid(*this)) { if (this->__valueD != other.__valueD) return false; } if (this->__state != Expression::COMPOUND) { return true; } if (this->op != other.op) { return false; } if (this->operands.size() != other.operands.size()) { return false; } for (size_t i = 0; ioperands.size(); ++i) { if (!(this->operands[i] == other.operands[i])) return false; } assert(!this->blocks.size()); assert(!other.blocks.size()); return true; } const ScopedSymbol ScopedSymbol::RetSymbol = ScopedSymbol{0, versions::VERSION_NONE}; } //end of namespace xreate diff --git a/cpp/src/ast.h b/cpp/src/ast.h index 66a0f46..cea76a7 100644 --- a/cpp/src/ast.h +++ b/cpp/src/ast.h @@ -1,711 +1,727 @@ /* 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/. * * Author: pgess * File: ast.h */ #ifndef AST_H #define AST_H #include "attachments.h" #include #include #include #include #include #include #include #include "utils.h" #include namespace llvm { class Value; } namespace xreate { struct ScopedSymbol; struct Symbol; } namespace std { template<> struct hash { std::size_t operator()(xreate::ScopedSymbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::ScopedSymbol& __x, const xreate::ScopedSymbol& __y) const; }; template<> struct hash { size_t operator()(xreate::Symbol const& s) const; }; template<> struct equal_to { bool operator()(const xreate::Symbol& __x, const xreate::Symbol& __y) const; }; } namespace xreate { struct String_t { }; struct Identifier_t { }; struct Number_t { }; struct Type_t { }; template class Atom { }; //DEBT store line:col for all atoms/identifiers template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; template<> class Atom { public: Atom(wchar_t* value); Atom(int value); double get()const; private: double __value; }; template<> class Atom { public: Atom(const std::wstring& value); Atom(std::string && name); const std::string& get() const; private: std::string __value; }; enum class TypePrimitive { Invalid, Bool, I8, I32, I64, Num, Int, Float, String }; enum class TypeOperator { NONE, CALL, CUSTOM, VARIANT, ARRAY, STRUCT, ACCESS, LINK }; struct llvm_array_tag { }; struct struct_tag { }; const llvm_array_tag tag_array = llvm_array_tag(); const struct_tag tag_struct = struct_tag(); /** * \brief Represents type to support type system * * This class represents type in denormalized form, i.e. without arguments and aliases substitution * \sa AST::expandType() */ class TypeAnnotation { public: TypeAnnotation(); TypeAnnotation(const Atom& typ); TypeAnnotation(TypePrimitive typ); TypeAnnotation(llvm_array_tag, TypeAnnotation typ, int size); TypeAnnotation(TypeOperator op, std::initializer_list operands); TypeAnnotation(TypeOperator op, std::vector&& operands); void addBindings(std::vector>&& params); void addFields(std::vector>&& listFields); bool operator<(const TypeAnnotation& t) const; // TypeAnnotation (struct_tag, std::initializer_list); bool isValid() const; TypeOperator __operator = TypeOperator::NONE; std::vector __operands; TypePrimitive __value; std::string __valueCustom; int conjuctionId = -1; //conjunction point id (relevant for recursive types) uint64_t __size = 0; std::vector fields; std::vector bindings; private: }; enum class Operator { ADD, SUB, MUL, DIV, EQU, NE, NEG, LSS, LSE, GTR, GTE, LIST, LIST_RANGE, LIST_NAMED, CALL, CALL_INTRINSIC, NONE, IMPL/* implication */, MAP, FOLD, FOLD_INF, LOOP_CONTEXT, INDEX, IF, SWITCH, SWITCH_ADHOC, SWITCH_VARIANT, CASE, CASE_DEFAULT, LOGIC_AND, ADHOC, CONTEXT_RULE, VARIANT }; class Function; class AST; class CodeScope; class MetaRuleAbstract; typedef ManagedPtr ManagedFnPtr; typedef ManagedPtr ManagedScpPtr; typedef ManagedPtr ManagedRulePtr; const ManagedScpPtr NO_SCOPE = ManagedScpPtr(UINT_MAX, 0); /** * \brief Represents every instruction in Xreate's syntax tree * \attention In case of any changes update xreate::ExpressionHints auxiliary helper as well * * Expression is generic building block of syntax tree able to hold node data * as well as child nodes as operands. Not only instructions use expression for representation in syntax tree * but annotation as well. * * Additionally, `types` as a special kind of annotations use Expression-like data structure TypeAnnotation * \sa xreate::AST, xreate::TypeAnnotation */ // struct Expression { friend class CodeScope; friend class ClaspLayer; friend class CFAPass; friend class ExpressionHints; Expression(const Operator &oprt, std::initializer_list params); Expression(const Atom& ident); Expression(const Atom& number); Expression(const Atom& a); Expression(); void setOp(Operator oprt); void addArg(Expression&& arg); void addBindings(std::initializer_list> params); void bindType(TypeAnnotation t); template void addBindings(InputIt paramsBegin, InputIt paramsEnd); void addTags(const std::list tags) const; void addBlock(ManagedScpPtr scope); const std::vector& getOperands() const; double getValueDouble() const; void setValueDouble(double value); const std::string& getValueString() const; void setValue(const Atom&& v); bool isValid() const; bool isDefined() const; bool operator==(const Expression& other) const; /** * \brief is it string, number, compound operation and so on */ enum { INVALID, COMPOUND, IDENT, NUMBER, STRING, BINDING } __state = INVALID; /** * \brief Valid for compound State. Holds type of compound operator */ Operator op; /** * \brief Unique id to identify expression within syntax tree */ unsigned int id; /** * \brief Exact meaning depends on particular instruction * \details As an example, named lists/structs hold field names in bindings */ std::vector bindings; std::map __indexBindings; /** * \brief Holds child instructions as arguments */ std::vector operands; /** * \brief Holds type of instruction's result */ TypeAnnotation type; /** * \brief Holds additional annotations */ mutable std::map tags; /** * \brief Child code blocks * \details For example, If statement holds TRUE-branch as first and FALSE-branch as second block here */ std::list blocks; private: std::string __valueS; double __valueD; static unsigned int nextVacantId; }; bool operator<(const Expression&, const Expression&); template void Expression::addBindings(InputIt paramsBegin, InputIt paramsEnd) { size_t index = bindings.size(); std::transform(paramsBegin, paramsEnd, std::inserter(bindings, bindings.end()), [&index, this] (const Atom atom) { std::string key = atom.get(); this->__indexBindings[key] = index++; return key; }); } typedef std::list ExpressionList; enum class TagModifier { NONE, ASSERT, REQUIRE }; enum class DomainAnnotation { FUNCTION, VARIABLE }; class RuleArguments : public std::vector> { public: void add(const Atom& name, DomainAnnotation typ); }; class RuleGuards : public std::vector { public: void add(Expression&& e); }; class ClaspLayer; class LLVMLayer; class MetaRuleAbstract { public: MetaRuleAbstract(RuleArguments&& args, RuleGuards&& guards); virtual ~MetaRuleAbstract(); virtual void compile(ClaspLayer& layer) = 0; protected: RuleArguments __args; RuleGuards __guards; }; class RuleWarning : public MetaRuleAbstract { friend class ClaspLayer; public: RuleWarning(RuleArguments&& args, RuleGuards&& guards, Expression&& condition, Atom&& message); virtual void compile(ClaspLayer& layer); ~RuleWarning(); private: std::string __message; Expression __condition; }; typedef unsigned int VNameId; namespace versions { typedef int VariableVersion; const VariableVersion VERSION_NONE = -2; const VariableVersion VERSION_INIT = 0; } template<> struct AttachmentsDict { typedef versions::VariableVersion Data; static const unsigned int key = 6; }; struct ScopedSymbol { VNameId id; versions::VariableVersion version; static const ScopedSymbol RetSymbol; }; struct Symbol { ScopedSymbol identifier; CodeScope * scope; }; +struct IdentifierSymbol{}; +struct SymbolAlias{}; + template<> -struct AttachmentsDict { +struct AttachmentsDict { typedef Symbol Data; static const unsigned int key = 7; }; +template<> +struct AttachmentsDict { + typedef Symbol Data; + static const unsigned int key = 9; +}; + typedef std::pair Tag; bool operator<(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator==(const ScopedSymbol& s1, const ScopedSymbol& s2); bool operator<(const Symbol& s1, const Symbol& s2); bool operator==(const Symbol& s1, const Symbol& s2); /** * \brief Represents code block and single scope of visibility * * Holds single expression as a *body* and set of variable assignments(declarations) used in body's expression * \sa xreate::AST */ class CodeScope { friend class Function; friend class PassManager; public: CodeScope(CodeScope* parent = 0); ~CodeScope(); /** \brief Set expression as a body */ void setBody(const Expression& body); /** \brief Returns current code scope body */ Expression& getBody(); /** \brief Adds variable definition to be used in body as well as in other declarations */ - void addDefinition(Expression&& var, Expression&& body); + Symbol addDefinition(Expression&& var, Expression&& body); /** \brief Returns symbols' definition */ - static const Expression& getDefinition(const Symbol& symbol); - const Expression& getDefinition(const ScopedSymbol& symbol); + static const Expression& getDefinition(const Symbol& symbol, bool flagAllowUndefined = false); + const Expression& getDefinition(const ScopedSymbol& symbol, bool flagAllowUndefined = false) const; /** \brief Adds variable defined elsewhere */ void addBinding(Expression&& var, Expression&& argument); std::vector __bindings; std::map __identifiers; CodeScope* __parent; //TODO move __definitions to SymbolsAttachments data //NOTE: definition of return type has index 0 std::unordered_map __declarations; std::vector tags; std::vector contextRules; private: VNameId __vCounter = 1; ScopedSymbol registerIdentifier(const Expression& identifier); public: bool recognizeIdentifier(const Expression& identifier) const; ScopedSymbol getSymbol(const std::string& alias); }; /** * \brief Represents single function in Xreate's syntax tree * * Holds an entry code scope and `guardContext` required for function to operate * \sa xreate::AST */ class Function { friend class Expression; friend class CodeScope; friend class AST; public: Function(const Atom& name); /** * \brief Adds function arguments */ void addBinding(Atom && name, Expression&& argument); /** * \brief Adds additional function annotations */ void addTag(Expression&& tag, const TagModifier mod); const std::string& getName() const; const std::map& getTags() const; CodeScope* getEntryScope() const; CodeScope* __entry; std::string __name; bool isPrefunction = false; //SECTIONTAG adhoc Function::isPrefunction flag Expression guardContext; Expression guard; private: std::map __tags; }; class ExternData; struct ExternEntry { std::string package; std::vector headers; }; typedef Expanded ExpandedType; enum ASTInterface { CFA, DFA, Extern, Adhoc }; struct FunctionSpecialization { std::string guard; size_t id; }; struct FunctionSpecializationQuery { std::unordered_set context; }; template<> struct AttachmentsId{ static unsigned int getId(const Expression& expression){ return expression.id; } }; template<> struct AttachmentsId{ static unsigned int getId(const Symbol& s){ return s.scope->__declarations.at(s.identifier).id; } }; template<> struct AttachmentsId{ static unsigned int getId(const ManagedFnPtr& f){ const Symbol symbolFunction{ScopedSymbol::RetSymbol, f->getEntryScope()}; return AttachmentsId::getId(symbolFunction); } }; +template<> +struct AttachmentsId{ + static unsigned int getId(const unsigned int id){ + return id; + } +}; + class TypesResolver; namespace details { namespace inconsistent { /** * \brief Syntax tree under construction in inconsistent form * * Represents Syntax Tree under construction(**inconsistent state**). * \attention Clients should use rather xreate::AST unless client's code explicitly works with Syntax Tree during construction. * * Typically instance only created by xreate::XreateManager and filled in by Parser * \sa xreate::XreateManager::prepare(std::string&&) */ class AST { friend class xreate::TypesResolver; public: AST(); /** * \brief Adds new function to AST * \param f Function to register */ void add(Function* f); /** * \brief Adds new declarative rule to AST * \param r Declarative Rule */ void add(MetaRuleAbstract* r); /** \brief Registers new code block */ ManagedScpPtr add(CodeScope* scope); /** * \brief Add new type to AST * @param t Type definition * @param alias Typer name */ void add(TypeAnnotation t, Atom alias); /** \brief Current module's name */ std::string getModuleName(); /** * \brief Looks for function with given name * \param name Function name to find * \note Requires that only one function exists under given name * \return Found function */ ManagedPtr findFunction(const std::string& name); /** \brief Returns all function in AST */ std::list getAllFunctions() const; /** * \brief Returns all specializations of a function with a given name * \param fnName function to find * \return list of found function specializations */ std::list getFunctionSpecializations(const std::string& fnName) const; /** * \return First element in Functions/Scopes/Rules list depending on template parameter * \tparam Target either Function or CodeScope or MetaRuleAbstract */ template ManagedPtr begin(); /** * \brief Performs all necessary steps after AST is built * * Performs all finzalisation steps and move AST into consistent state represented by xreate::AST * \sa xreate::AST * \return AST in consistent state */ xreate::AST* finalize(); typedef std::multimap FUNCTIONS_REGISTRY; std::vector __externdata; std::list __dfadata; //TODO move to more appropriate place std::list __rawImports; //TODO move to more appropriate place std::multimap __interfacesData; //TODO CFA data here. private: std::vector __rules; std::vector __functions; std::vector __scopes; FUNCTIONS_REGISTRY __indexFunctions; protected: std::map __indexTypeAliases; public: /** * \brief Stores DFA scheme for later use by DFA Pass * * Treats expression as a DFA scheme and feeds to a DFA Pass later * \paramn Expression DFA Scheme * \sa xreate::DFAPass */ void addDFAData(Expression&& data); /** \brief Stores data for later use by xreate::ExternLayer */ void addExternData(ExternData&& data); /** * \brief Generalized function to store particular data for later use by particular pass * \param interface Particular Interface * \param data Particular data */ void addInterfaceData(const ASTInterface& interface, Expression&& data); /**\name Symbols Recognition */ ///@{ public: //TODO revisit enums/variants, move to codescope /** * \brief Tries to find out whether expression is Variant constructor */ void recognizeVariantConstructor(Expression& function); Atom recognizeVariantConstructor(Atom ident); private: std::map> __dictVariants; public: std::set> bucketUnrecognizedIdentifiers; public: /** * \brief Postpones unrecognized identifier for future second round of recognition * \param scope Code block identifier is encountered * \param id Identifier */ void postponeIdentifier(CodeScope* scope, const Expression& id); /** \brief Second round of identifiers recognition done right after AST is fully constructed */ void recognizePostponedIdentifiers(); ///@} }; template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); template<> ManagedPtr AST::begin(); } } // namespace details::incomplete /** * \brief Xreate's Syntax Tree in consistent state * * Syntax Tree has two mutually exclusive possible states: * - inconsistent state while AST is under construction. Represented by xreate::details::inconsistent::AST * - consistent state when AST is built and finalize() is done. * * This class represents consistent state and should be used everywhere unless client's code explicitly works with AST under construction. * Consistent AST enables access to additional functions(currently related to type management). * \sa xreate::details::inconsistent::AST */ class AST : public details::inconsistent::AST { public: AST() : details::inconsistent::AST() {} /** * \brief Computes fully expanded form of type by substituting all arguments and aliases * \param t Type to expand * \return Expdanded or normal form of type * \sa TypeAnnotation */ ExpandedType expandType(const TypeAnnotation &t) const; /** * Searches type by given name * \param name Typename to search * \return Expanded or normal form of desired type * \note if type name is not found returns new undefined type with this name */ ExpandedType findType(const std::string& name); /** * Invokes Type Inference Analysis to find out expanded(normal) form expressions's type * \sa typeinference.h * \param expression * \return Type of expression */ ExpandedType getType(const Expression& expression); }; } #endif // AST_H diff --git a/cpp/src/attachments.h b/cpp/src/attachments.h index 5812a6d..40a6db7 100644 --- a/cpp/src/attachments.h +++ b/cpp/src/attachments.h @@ -1,175 +1,177 @@ /* 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/. * * Author: pgess * File: attachments.h * Date: 3/15/15 */ #ifndef _XREATE_ATTACHMENTS_H_ #define _XREATE_ATTACHMENTS_H_ #include #include #include #include namespace xreate { //Attachments dictionary template struct AttachmentsDict { // typedef void Data; - // static const unsigned int key (current unreserved - 9); + // static const unsigned int key (current unreserved - 11); //reserved attachments: // 1 containers::Implementation // 3 interpretation::InterpretationData // 5 interpretation::FunctionInterpretationData // 6 VariableVersion -// 7 Symbol +// 7 IdentifierSymbol // 8 versions::VersionImposedDependency +// 9 SymbolAlias +// 10 CallGuard Expression }; template struct AttachmentsId{ //static unsigned int getId(const Object& object); }; template class IAttachmentsContainer{ protected: virtual bool __exists(const unsigned int object)=0; virtual Data& __get(const unsigned int object)=0; virtual void __put(const unsigned int object, Data data)=0; public: template bool exists(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __exists(id); } template Data& get(const Id& object){ unsigned int id = AttachmentsId::getId(object); return __get(id); } template Data get(const Id& object, const Data& dataDefault){ unsigned int id = AttachmentsId::getId(object); if (! __exists(id)){ return dataDefault; } return __get(id); } template void put(const Id& object, Data data){ unsigned int id = AttachmentsId::getId(object); __put(id, data); } virtual ~IAttachmentsContainer(){}; }; template class AttachmentsContainerDefault: public IAttachmentsContainer{ private: std::unordered_map __data; virtual bool __exists(const unsigned int id){ return __data.count(id); } virtual Data& __get(const unsigned int id){ return __data.at(id); } virtual void __put(const unsigned int id, Data data){ auto result = __data.emplace(id, data); assert(result.second); } public: std::unordered_map& getRawStorage() { return __data; } }; class Attachments{ private: static std::vector __storage; template using Data = typename AttachmentsDict::Data; public: template static bool exists(const Id& object) { assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->exists(object); } template static Data& get(const Id& object){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object); } template static Data get(const Id& object, const Data& dataDefault){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); return self->get(object, dataDefault); } template static void put(const Id& object, Data data){ assert(AttachmentsDict::key < __storage.size()); assert(__storage.at(AttachmentsDict::key)); IAttachmentsContainer>* self = reinterpret_cast>*>(__storage.at(AttachmentsDict::key)); self->put(object, data); } template static void init(){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = new AttachmentsContainerDefault>(); } template static void init(IAttachmentsContainer>* container){ unsigned int keyStorage = AttachmentsDict::key; if (keyStorage+1 > __storage.size()){ __storage.resize(keyStorage + 1, nullptr); } __storage[keyStorage] = container; } }; } #endif //_XREATE_ATTACHMENTS_H_ diff --git a/cpp/src/clasplayer.cpp b/cpp/src/clasplayer.cpp index 8294af4..5dc47e5 100644 --- a/cpp/src/clasplayer.cpp +++ b/cpp/src/clasplayer.cpp @@ -1,387 +1,382 @@ /* 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/. * * Author: pgess * File: clasplayer.cpp */ /** * \file clasplayer.h * \brief Resoner. Wrapper over Clasp reasoner library */ #include "clasplayer.h" - -#include +#include "analysis/aux.h" #include "utils.h" + +#include #include #include -#include - -#include "analysis/aux.h" -#include "analysis/DominatorsTreeAnalysisProvider.h" -#include "analysis/cfagraph.h" -#include "analysis/dfagraph.h" +#include using namespace std; //TODO escape identifiers started with upper case symbol namespace xreate { void ClaspLayer::printWarnings(std::ostream& out) { const std::string warningTag = "warning"; auto warningsRange = __model.equal_range(warningTag); for (auto warning=warningsRange.first; warning!= warningsRange.second; ++warning) { unsigned int warningId; Gringo::Symbol params; std::tie(warningId, params) = parse(warning->second); cout << "Warning: " << __warnings.at(warningId) << " "; params.print(out); out< warnings; cout << "Model: " << endl; const string& atomBindVar = Config::get("clasp.bindings.variable"); const string& atomBindFunc = Config::get("clasp.bindings.function"); const string& atomBindScope = Config::get("clasp.bindings.scope"); for (Gringo::Symbol atom : model.atoms(clingo_show_type_atoms)) { atom.print(cout); cout <<" | "<< endl; string atomName(atom.name().c_str()); if (atomName == atomBindVar || atomName == atomBindFunc || atomName == atomBindScope){ string name = std::get<1>(parse(atom)).name().c_str(); __model.emplace(move(name), move(atom)); } __model.emplace(atomName, move(atom)); } return true; } void -ClaspLayer::setCFAData(xreate::cfa::CFAGraph* graph) { - dataCFA.reset(graph); +ClaspLayer::registerReport(IAnalysisReport* report){ + __reports.push_back(report); } void -ClaspLayer::setDFAData(xreate::dfa::DFAGraph* graph){ - dataDFA.reset(graph); +ClaspLayer::runReports(){ + for(IAnalysisReport* report: __reports){ + report->print(__partGeneral); + delete report; + } + + __reports.clear(); } void ClaspLayer::addRuleWarning(const RuleWarning &rule) { //__partGeneral << rule << endl; list domains; boost::format formatDef("%1%(%2%)"); std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(domains, domains.begin()), [&formatDef](const std::pair &argument) { string domain; switch (argument.second) { case DomainAnnotation::FUNCTION: domain = "function"; break; case DomainAnnotation::VARIABLE: domain = "variable"; break; } return boost::str(formatDef % domain % argument.first); }); list vars; std::transform(rule.__args.begin(), rule.__args.end(), std::inserter(vars, vars.begin()), [](const std::pair &argument) { return argument.first.c_str(); }); list> guardsRaw; std::transform(rule.__guards.begin(), rule.__guards.end(), std::inserter(guardsRaw, guardsRaw.begin()), [this](const Expression &guard) { return xreate::analysis::compile(guard); }); const list& guards = xreate::analysis::multiplyLists(std::move(guardsRaw)); list &&branches = xreate::analysis::compileNeg(rule.__condition); boost::format formatWarning("warning(%1%, (%2%)):- %3%, %4%, %5%."); for (const string &guardsJoined: guards) for (const string &branch: branches) { unsigned int hook = registerWarning(string(rule.__message)); __partGeneral << formatWarning %(hook) %(boost::algorithm::join(vars, ", ")) %(branch) %(guardsJoined) %(boost::algorithm::join(domains, ", ")) <__rawImports) { std::ifstream file(fn); if (!file) continue; while(!file.eof()){ string line; std::getline(file, line); out << line << endl; } } } void ClaspLayer::addRawScript(std::string&& script){ __partGeneral << script; } void ClaspLayer::run() { involveImports(); - - if (this->dataDFA){ - this->dataDFA->print(__partGeneral); - } - - if (this->dataCFA){ - this->dataCFA->print(__partGeneral); - } - - dominators::DominatorsTreeAnalysisProvider providerDominators; - providerDominators.run(this); - providerDominators.print(__partGeneral); + runReports(); ostringstream program; program << __partTags.str() << __partGeneral.str(); cout << FYEL(program.str()) << endl; std::vector args{"clingo", nullptr}; DefaultGringoModule moduleDefault; Gringo::Scripts scriptsDefault(moduleDefault); ClingoLib ctl(scriptsDefault, 0, args.data(), {}, 0); ctl.add("base", {}, program.str()); ctl.ground({{"base", {}}}, nullptr); // solve Gringo::SolveResult result = ctl.solve([this](Gringo::Model const &model) { this->handleSolution(model); return true; }, {}); if (result.satisfiable() == Gringo::SolveResult::Satisfiable) { cout << FGRN("SUCCESSFULLY") << endl; } else { cout << FRED("UNSUCCESSFULLY") << endl; } // invoke all query plugins to process clasp data for (auto q: __queries) { q.second->init(this); } } ClaspLayer::ClaspLayer() { } ClaspLayer::ModelFragment ClaspLayer::query(const std::string& atom) { if (! __model.count(atom)){ return boost::none; } return ModelFragment(__model.equal_range(atom)); } ScopePacked ClaspLayer::pack(CodeScope* const scope) { auto pos = __indexScopes.emplace(scope, __indexScopes.size()); if (pos.second) __registryScopes.push_back(scope); return pos.first->second; } size_t ClaspLayer::getScopesCount() const{ return __registryScopes.size(); } SymbolPacked ClaspLayer::pack(const Symbol& symbol, std::string hintSymbolName) { SymbolPacked result(symbol.identifier.id, symbol.identifier.version, pack(symbol.scope)); __indexSymbolNameHints.emplace(result, hintSymbolName); return result; } Symbol ClaspLayer::unpack(const SymbolPacked& symbol) { return Symbol{ScopedSymbol{symbol.identifier, symbol.version}, __registryScopes[symbol.scope]}; }; std::string ClaspLayer::getHintForPackedSymbol(const SymbolPacked& symbol){ - if (!symbol.categoryTransient) { - auto result = __indexSymbolNameHints.find(symbol); - return (result == __indexSymbolNameHints.end())? "" : result->second; - - } else { - return "anonym(" + to_string(symbol.identifier) + ")"; - } + auto result = __indexSymbolNameHints.find(symbol); + return (result == __indexSymbolNameHints.end())? "" : result->second; } bool operator==(const SymbolPacked& s1, const SymbolPacked& s2) { return s1.identifier == s2.identifier && s1.scope == s2.scope; } bool operator<(const SymbolPacked& s1, const SymbolPacked& s2) { return s1.scope < s2.scope || (s1.scope == s2.scope && s1.identifier < s2.identifier); } IQuery* ClaspLayer::registerQuery(IQuery *query, const QueryId& id) { return __queries.emplace(id, query).first->second; } IQuery* ClaspLayer::getQuery(const QueryId& id){ assert(__queries.count(id) && "Undefined query"); return __queries.at(id); } Expression ParseImplAtom::get(const Gringo::Symbol& atom) { switch (atom.type()) { case Gringo::SymbolType::Num: return Expression(atom.num()); case Gringo::SymbolType::Str: return Expression(Atom(std::string(atom.string().c_str()))); case Gringo::SymbolType::Fun: { //FUNC Expression result(Operator::CALL,{Expression(Atom(std::string(atom.name().c_str())))}); for (const Gringo::Symbol& arg : atom.args()) { result.addArg(ParseImplAtom::get(arg)); } return result; } default: { assert(false); } } } +int +ParseImplAtom::get(const Gringo::Symbol& atom) { + switch (atom.type()){ + case Gringo::SymbolType::Num: return atom.num(); + default: break; + } + + assert(false && "Inappropriate symbol type"); +} + std::string ParseImplAtom::get(const Gringo::Symbol& atom) { switch (atom.type()) { case Gringo::SymbolType::Str: return atom.string().c_str(); case Gringo::SymbolType::Fun: return atom.name().c_str(); default: break; } assert(false && "Inappropriate symbol type"); } SymbolPacked ParseImplAtom::get(const Gringo::Symbol& atom) { auto result = ClaspLayer::parse(atom); return SymbolPacked(std::get<0>(result), std::get<1>(result), std::get<2>(result)); }; Gringo::Symbol ParseImplAtom::get(const Gringo::Symbol& atom) { return atom; } std::list ParseImplAtom>::get(const Gringo::Symbol& atom){ assert (atom.type() == Gringo::SymbolType::Fun); std::list result; for (const Gringo::Symbol& arg: atom.args()) { result.push_back(ParseImplAtom::get(arg)); } return result; } } //end of xreate namespace /** * \class xreate::ClaspLayer * \brief Reasoning and logic Solver. * * Wraps external brilliant fantastic tool [Clasp solver](https://potassco.org/clasp/) * * For building *logic program* for reasoning ClaspLayer takes input from: * - Raw scripts. Client could append arbitrary ASP script to _logic program_. \ref addRawScript() * - Includes. There is possibility to specify external files with ASP scripts * to append to _logic program_. \ref involveImports() (private member) * - Diagnostic rules. Rules that produce diagnostic messages during * compilation(warnings) or even able to halt compilation with errors. * addRuleWarning(), \ref registerWarning() * - DFA data. \ref setDFAData() * - CFA data. \ref setCFAData() * - Dominators Analysis. See xreate::dominators::DominatorsTreeAnalysisProvider. * Executed by \ref run() * - Context rules. See xreate::ContextRule and general [Context Explanation](/w/concepts/context) * * Data sources implement xreate::IAnalysisReport. Generally, input could be loosely divided into three categories: * - *Internally derived* data. CFA, DFA, Dominators analyses *automatically* feed reasoner by * useful insights about data, structure and algorithms of a program * - *User provided* data. CFA, DFA, Diagnostic/Context rules feed reasoner by * annotations Developer specifically provides manually * - *External* data. Raw scripts and includes feed reasoner with third-party data * related to a different aspects of a program possibly produced by external analyzers * * Once ClaspLayer got input from all providers and logic program is fully constructed * it runs external Clasp solver and receives back desired solutions. * * Output of Clasp reasoner is recognized and accessed via *queries*. * IQuery represents an interface between reasoner's output and rest of Xreate. * Each query inherits xreate::IQuery interface. Currently there are queries as follows: * - xreate::containers::Query to catch solutions regarding Containers implementation. See [Containers Explanation](/w/concepts/containers) * - xreate::context::ContextQuery to catch solution regarding Context. See [Context Explanation](/w/concepts/context) * * \sa See xreate::dfa::DFAPass, xreate::cfa::CFAPass, xreate::IQuery, xreate::IAnalysisReport, xreate::dominators::DominatorsTreeAnalysisProvider */ \ No newline at end of file diff --git a/cpp/src/clasplayer.h b/cpp/src/clasplayer.h index 03ef0e8..f2c097f 100644 --- a/cpp/src/clasplayer.h +++ b/cpp/src/clasplayer.h @@ -1,228 +1,229 @@ /* 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/. * * Author: pgess * File: clasplayer.h */ #ifndef CLASPLAYER_H #define CLASPLAYER_H #include "ast.h" #include "contextrule.h" #include #include #include #include #include #include #include #include namespace xreate { typedef unsigned int ScopePacked; struct SymbolPacked { + SymbolPacked(){} + SymbolPacked(ScopedSymbol i, ScopePacked s): identifier(i.id), version(i.version), scope(s){} + SymbolPacked(VNameId symbolId, versions::VariableVersion symbolVersion, ScopePacked symbolScope) + : identifier(symbolId), version(symbolVersion), scope(symbolScope){} + VNameId identifier; versions::VariableVersion version; ScopePacked scope; - bool categoryTransient; - - SymbolPacked(): categoryTransient(false){} - SymbolPacked(ScopedSymbol i, ScopePacked s, bool isTransient = false): identifier(i.id), version(i.version), scope(s), categoryTransient(isTransient){} - SymbolPacked(VNameId symbolId, versions::VariableVersion symbolVersion, ScopePacked symbolScope, bool isTransient = false) - : identifier(symbolId), version(symbolVersion), scope(symbolScope), categoryTransient(isTransient){} }; bool operator==(const SymbolPacked& s1, const SymbolPacked& s2); bool operator<(const SymbolPacked& s1, const SymbolPacked& s2); enum class DFGConnection { STRONG, WEAK, PROTOTYPE }; /** \brief Designated to mark analysis results that can be composed as *logic program* */ class IAnalysisReport { public: /** \brief Composes *logic program* based on analysis data into ASP format and appends to a stream*/ - void print(std::ostringstream& output) const; + virtual void print(std::ostringstream& output) const = 0; virtual ~IAnalysisReport(){}; }; /** \brief Logic program query interface */ class IQuery { public: virtual void init(ClaspLayer* clasp) = 0; virtual ~IQuery() {} }; enum class QueryId { ContainersQuery, ContextQuery, - PtrvalidQuery + PtrvalidQuery, + PolymorphQuery }; namespace dfa{ class DFAGraph; } namespace cfa { class CFAGraph; } class ClaspLayer { friend class ContextRule; /**\name Data Providers Management */ ///@{ public: - /** \brief Provides DFA graph as output from xreate::DFAPass */ - void setDFAData(xreate::dfa::DFAGraph* graph); - - /** \brief Provides CFA graph as output from xreate::CFAPass */ - void setCFAData(xreate::cfa::CFAGraph* graph); - - /** \brief Appends arbitrary string to *logic program + void registerReport(IAnalysisReport* report); + void runReports(); + + /** \brief Appends arbitrary string to *logic program* */ void addRawScript(std::string&& script); - boost::scoped_ptr dataDFA; - boost::scoped_ptr dataCFA; - private: + std::list __reports; + /** Includes external text files to a *logic program* */ void involveImports(); ///@} /**\name Queries Management */ ///@{ public: /** \brief Adds query. See xreate::IQuery */ IQuery* registerQuery(IQuery* query, const QueryId& id); /** \brief Returns particular query. See xreate::IQuery */ IQuery* getQuery(const QueryId& id); template static std::tuple parse(const Gringo::Symbol& atom); typedef std::multimap::const_iterator ModelIterator; typedef boost::optional> ModelFragment; ModelFragment query(const std::string& atom); size_t getScopesCount() const; SymbolPacked pack(const Symbol& symbol, std::string hintSymbolName = ""); ScopePacked pack(CodeScope * const scope); Symbol unpack(const SymbolPacked& symbol); std::string getHintForPackedSymbol(const SymbolPacked& symbol); ///@} private: std::map __queries; std::multimap __model; std::map __indexSymbolNameHints; std::unordered_map __indexScopes; std::vector __registryScopes; /**\name Diagnostic */ ///@{ //TODO diagnostic move over to separate provider/query public: /** \brief Adds diagnostic rule */ void addRuleWarning(const RuleWarning &rule); /** \brief Registers diagnostic messages */ unsigned int registerWarning(std::string &&message); private: std::map __warnings; void printWarnings(std::ostream& out); ///@} ///@{ public: ClaspLayer(); /** \brief Executes reasoning */ void run(); ///@} AST *ast; private: std::ostringstream __partTags; std::ostringstream __partGeneral; bool handleSolution(Gringo::Model const &model); }; template struct ParseImplAtom { static typ get(const Gringo::Symbol& atom) { return atom.num(); } }; +template<> +struct ParseImplAtom { + static int get(const Gringo::Symbol& atom); +}; + template<> struct ParseImplAtom { static std::string get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static SymbolPacked get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static Gringo::Symbol get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom>{ static std::list get(const Gringo::Symbol& atom); }; template<> struct ParseImplAtom { static Expression get(const Gringo::Symbol& atom); }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { const size_t tupleSize = std::tuple_size::value; typedef typename std::tuple_element < tupleSize - index, Tuple>::type ElType; ElType& el = std::get < tupleSize - index > (tup); Gringo::Symbol atom = *arg; el = ParseImplAtom::get(atom); Parse_Impl ::parse(tup, ++arg); } }; template struct Parse_Impl { static void parse(Tuple& tup, Gringo::SymSpan::iterator arg) { } }; template std::tuple ClaspLayer::parse(const Gringo::Symbol& atom) { typedef std::tuple < Types...> Tuple; Tuple tup; Parse_Impl::value>::parse(tup, atom.args().first); return tup; } } //end of xreate namespace #endif diff --git a/cpp/src/compilation/advanced.cpp b/cpp/src/compilation/advanced.cpp index f7a8ddf..04db2cf 100644 --- a/cpp/src/compilation/advanced.cpp +++ b/cpp/src/compilation/advanced.cpp @@ -1,390 +1,390 @@ /* 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: InstructionsAdvanced.cpp * Author: pgess * * Created on June 26, 2016, 6:00 PM */ /** * \file advanced.h * \brief Compilation of statements that require more than one LLVM instruction */ #include "compilation/advanced.h" #include "compilation/containers.h" #include "compilation/transformersaturation.h" #include "query/context.h" #include "query/containers.h" #include "llvmlayer.h" #include "ast.h" using namespace std; using namespace llvm; using namespace xreate; using namespace xreate::containers; using namespace xreate::compilation; #define NAME(x) (hintRetVar.empty()? x : hintRetVar) #define UNUSED(x) (void)(x) #define EXPAND_CONTEXT \ LLVMLayer* llvm = context.pass->man->llvm; \ compilation::ICodeScopeUnit* scope = context.scope; \ compilation::IFunctionUnit* function = context.function; Advanced::Advanced(compilation::Context ctx) : context(ctx), tyNum(static_cast (ctx.pass->man->llvm->toLLVMType(ExpandedType(TypeAnnotation(TypePrimitive::Num))))) { } llvm::Value* Advanced::compileMapSolidOutput(const Expression &expr, const std::string hintRetVar) { EXPAND_CONTEXT UNUSED(scope); //initialization - Symbol symbolIn = Attachments::get(expr.getOperands()[0]); + Symbol symbolIn = Attachments::get(expr.getOperands()[0]); ImplementationRec implIn = containers::Query::queryImplementation(symbolIn).extract(); // impl of input list size_t size = implIn.size; CodeScope* scopeLoop = expr.blocks.front(); std::string varEl = scopeLoop->__bindings[0]; Iterator* it = Iterator::create(context, symbolIn); llvm::Value *rangeFrom = it->begin(); llvm::Value *rangeTo = it->end(); //definitions ArrayType* tyNumArray = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::Num, size)))); llvm::IRBuilder<> &builder = llvm->builder; llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "loop", function->raw); llvm::BasicBlock *blockBeforeLoop = builder.GetInsertBlock(); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "postloop", function->raw); Value* dataOut = llvm->builder.CreateAlloca(tyNumArray, ConstantInt::get(tyNum, size), NAME("map")); // * initial check Value* condBefore = builder.CreateICmpSLE(rangeFrom, rangeTo); builder.CreateCondBr(condBefore, blockLoop, blockAfterLoop); // create PHI: builder.SetInsertPoint(blockLoop); llvm::PHINode *stateLoop = builder.CreatePHI(tyNum, 2, "mapIt"); stateLoop->addIncoming(rangeFrom, blockBeforeLoop); // loop body: Value* elIn = it->get(stateLoop, varEl); compilation::ICodeScopeUnit* scopeLoopUnit = function->getScopeUnit(scopeLoop); scopeLoopUnit->bindArg(elIn, move(varEl)); Value* elOut = scopeLoopUnit->compile(); Value *pElOut = builder.CreateGEP(dataOut, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), stateLoop})); builder.CreateStore(elOut, pElOut); //next iteration preparing Value *stateLoopNext = builder.CreateAdd(stateLoop, llvm::ConstantInt::get(tyNum, 1)); stateLoop->addIncoming(stateLoopNext, builder.GetInsertBlock()); //next iteration checks: Value* condAfter = builder.CreateICmpSLE(stateLoopNext, rangeTo); builder.CreateCondBr(condAfter, blockLoop, blockAfterLoop); //finalization: builder.SetInsertPoint(blockAfterLoop); return dataOut; } Value* Advanced::compileArrayIndex(llvm::Value* aggregate, std::vector indexes, std::string hintRetVar) { EXPAND_CONTEXT UNUSED(function); UNUSED(scope); indexes.insert(indexes.begin(), llvm::ConstantInt::get(tyNum, 0)); llvm::Value *pEl = llvm->builder.CreateGEP(aggregate, llvm::ArrayRef(indexes)); return llvm->builder.CreateLoad(pEl, NAME("el")); } Value* Advanced::compileStructIndex(llvm::Value* aggregate, const ExpandedType& t, const std::string& idx) { EXPAND_CONTEXT UNUSED(scope); UNUSED(function); TypeUtils types(llvm); std::vector&& fields = types.getStructFields(t); for (unsigned i = 0, size = fields.size(); i < size; ++i) { if (fields.at(i) == idx) { //dereference pointer if (types.isPointer(t)) { llvm::Value* addr = llvm->builder.CreateConstGEP2_32(nullptr, aggregate, 0, i); return llvm->builder.CreateLoad(addr); } return llvm->builder.CreateExtractValue(aggregate, llvm::ArrayRef{i}); } } assert(false && "not found required struct field"); return nullptr; } llvm::Value* Advanced::compileFold(const Expression& fold, const std::string& hintRetVar) { EXPAND_CONTEXT assert(fold.op == Operator::FOLD); //initialization: - Symbol varInSymbol = Attachments::get(fold.getOperands()[0]); + Symbol varInSymbol = Attachments::get(fold.getOperands()[0]); Implementation info = Query::queryImplementation(varInSymbol); Iterator* it = Iterator::create(context, varInSymbol); llvm::Value* rangeBegin = it->begin(); llvm::Value* rangeEnd = it->end(); llvm::Value* accumInit = scope->process(fold.getOperands()[1]); std::string varIn = fold.getOperands()[0].getValueString(); std::string varAccum = fold.bindings[1]; std::string varEl = fold.bindings[0]; llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold", function->raw); llvm::BasicBlock *blockLoopBody = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold_body", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold_after", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm::getGlobalContext(), "fold_next", function->raw); llvm->builder.CreateBr(blockLoop); // * create phi llvm->builder.SetInsertPoint(blockLoop); llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, varAccum); accum->addIncoming(accumInit, blockBeforeLoop); llvm::PHINode *itLoop = llvm->builder.CreatePHI(rangeBegin->getType(), 2, "foldIt"); itLoop->addIncoming(rangeBegin, blockBeforeLoop); // * loop checks Value* condRange = llvm->builder.CreateICmpNE(itLoop, rangeEnd); llvm->builder.CreateCondBr(condRange, blockLoopBody, blockAfterLoop); // * loop body llvm->builder.SetInsertPoint(blockLoopBody); CodeScope* scopeLoop = fold.blocks.front(); compilation::ICodeScopeUnit* loopUnit = function->getScopeUnit(scopeLoop); Value* elIn = it->get(itLoop); loopUnit->bindArg(accum, move(varAccum)); loopUnit->bindArg(elIn, move(varEl)); Value* accumNext = loopUnit->compile(); // * Loop saturation checks bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); llvm::BasicBlock* blockSaturation = llvm->builder.GetInsertBlock(); if (!flagSaturationTriggered){ llvm->builder.CreateBr(blockNext); } // * computing next iteration state llvm->builder.SetInsertPoint(blockNext); Value *itLoopNext = it->advance(itLoop); accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); itLoop->addIncoming(itLoopNext, llvm->builder.GetInsertBlock()); llvm->builder.CreateBr(blockLoop); // * finalization: llvm->builder.SetInsertPoint(blockAfterLoop); if (!flagSaturationTriggered){ return accum; } llvm::PHINode* result = llvm->builder.CreatePHI(accumInit->getType(), 2); result->addIncoming(accum, blockLoop); result->addIncoming(accumNext, blockSaturation); return result; } llvm::Value* Advanced::compileFoldInf(const Expression& fold, const std::string& hintRetVar) { EXPAND_CONTEXT assert(fold.op == Operator::FOLD_INF); std::string accumName = fold.bindings[0]; llvm::Value* accumInit = scope->process(fold.getOperands()[0]); llvm::BasicBlock *blockBeforeLoop = llvm->builder.GetInsertBlock(); llvm::BasicBlock *blockLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "foldinf", function->raw); llvm::BasicBlock *blockNext = llvm::BasicBlock::Create(llvm::getGlobalContext(), "foldinf_next", function->raw); llvm::BasicBlock *blockAfterLoop = llvm::BasicBlock::Create(llvm::getGlobalContext(), "foldinf_post", function->raw); std::unique_ptr transformerSaturation(new TransformerSaturation(blockBeforeLoop, context.pass->managerTransformations)); llvm->builder.CreateBr(blockLoop); // * create phi llvm->builder.SetInsertPoint(blockLoop); llvm::PHINode *accum = llvm->builder.CreatePHI(accumInit->getType(), 2, accumName); accum->addIncoming(accumInit, blockBeforeLoop); // * loop body CodeScope* scopeLoop = fold.blocks.front(); compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); unitLoop->bindArg(accum, move(accumName)); Value* accumNext = unitLoop->compile(); // * Loop saturation checks bool flagSaturationTriggered = transformerSaturation->insertSaturationChecks(blockNext, blockAfterLoop, context); assert(flagSaturationTriggered); // * computing next iteration state llvm->builder.SetInsertPoint(blockNext); accum->addIncoming(accumNext, llvm->builder.GetInsertBlock()); llvm->builder.CreateBr(blockLoop); // finalization: llvm->builder.SetInsertPoint(blockAfterLoop); return accumNext; } llvm::Value* Advanced::compileIf(const Expression& exprIf, const std::string& hintRetVar) { EXPAND_CONTEXT //initialization: const Expression& condExpr = exprIf.getOperands()[0]; llvm::IRBuilder<>& builder = llvm->builder; //llvm::Type* tyResultType = llvm->toLLVMType(llvm->ast->expandType(exprIf.type)); llvm::BasicBlock *blockAfter = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifAfter", function->raw); llvm::BasicBlock *blockTrue = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifTrue", function->raw); llvm::BasicBlock *blockFalse = llvm::BasicBlock::Create(llvm::getGlobalContext(), "ifFalse", function->raw); llvm::Value* cond = scope->process(condExpr); llvm->builder.CreateCondBr(cond, blockTrue, blockFalse); builder.SetInsertPoint(blockTrue); CodeScope* scopeTrue = exprIf.blocks.front(); llvm::Value* resultTrue = function->getScopeUnit(scopeTrue)->compile(); blockTrue = builder.GetInsertBlock(); builder.CreateBr(blockAfter); builder.SetInsertPoint(blockFalse); CodeScope* scopeFalse = exprIf.blocks.back(); llvm::Value* resultFalse = function->getScopeUnit(scopeFalse)->compile(); blockFalse = builder.GetInsertBlock(); builder.CreateBr(blockAfter); builder.SetInsertPoint(blockAfter); llvm::PHINode *ret = builder.CreatePHI(resultTrue->getType(), 2, NAME("if")); ret->addIncoming(resultTrue, blockTrue); ret->addIncoming(resultFalse, blockFalse); return ret; } //TODO Switch: default variant no needed when all possible conditions are considered llvm::Value* Advanced::compileSwitch(const Expression& exprSwitch, const std::string& hintRetVar) { EXPAND_CONTEXT AST* root = context.pass->man->root; UNUSED(function); assert(exprSwitch.operands.size() >= 2); assert(exprSwitch.operands[1].op == Operator::CASE_DEFAULT && "No default case in Switch Statement"); int countCases = exprSwitch.operands.size() - 1; llvm::IRBuilder<>& builder = llvm->builder; llvm::BasicBlock* blockProlog = builder.GetInsertBlock(); llvm::BasicBlock *blockEpilog = llvm::BasicBlock::Create(llvm::getGlobalContext(), "switchAfter", function->raw); builder.SetInsertPoint(blockEpilog); llvm::Type* exprSwitchType = llvm->toLLVMType(root->getType(exprSwitch)); llvm::PHINode *ret = builder.CreatePHI(exprSwitchType, countCases, NAME("switch")); builder.SetInsertPoint(blockProlog); llvm::Value * conditionSwitch = scope->process(exprSwitch.operands[0]); llvm::BasicBlock *blockDefault = llvm::BasicBlock::Create(llvm::getGlobalContext(), "caseDefault", function->raw); llvm::SwitchInst * instructionSwitch = builder.CreateSwitch(conditionSwitch, blockDefault, countCases); for (int size = exprSwitch.operands.size(), i = 2; i < size; ++i) { llvm::BasicBlock *blockCase = llvm::BasicBlock::Create(llvm::getGlobalContext(), "case" + std::to_string(i), function->raw); llvm::Value* condCase = function->getScopeUnit(exprSwitch.operands[i].blocks.front())->compile(); builder.SetInsertPoint(blockCase); llvm::Value* resultCase = function->getScopeUnit(exprSwitch.operands[i].blocks.back())->compile(); builder.CreateBr(blockEpilog); ret->addIncoming(resultCase, builder.GetInsertBlock()); builder.SetInsertPoint(blockProlog); instructionSwitch->addCase(dyn_cast(condCase), blockCase); } //compile default block: builder.SetInsertPoint(blockDefault); CodeScope* scopeDefault = exprSwitch.operands[1].blocks.front(); llvm::Value* resultDefault = function->getScopeUnit(scopeDefault)->compile(); builder.CreateBr(blockEpilog); ret->addIncoming(resultDefault, builder.GetInsertBlock()); builder.SetInsertPoint(blockEpilog); return ret; } //TODO recognize cases to make const arrays/stored in global mem/stack alloced. llvm::Value* Advanced::compileListAsSolidArray(const Expression &expr, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(scope); UNUSED(function); AST* root = context.pass->man->root; const size_t& length = expr.getOperands().size(); const Expression& expression = expr; llvm::Value* zero = ConstantInt::get(tyNum, 0); llvm::Value* one = ConstantInt::get(tyNum, 1); ExpandedType typAggrExpanded = root->getType(expression); assert(typAggrExpanded->__operator == TypeOperator::ARRAY); llvm::Type* typEl = llvm->toLLVMType(ExpandedType(typAggrExpanded->__operands[0])); ArrayType* typAggr = (ArrayType*) llvm::ArrayType::get(typEl, length); llvm::Value* list = llvm->builder.CreateAlloca(typAggr, ConstantInt::get(Type::getInt32Ty(llvm::getGlobalContext()), length, false), hintRetVar); const std::vector& operands = expression.getOperands(); llvm::Value* addrOperand = llvm->builder.CreateGEP(typAggr, list, ArrayRef(std::vector{zero, zero})); llvm->builder.CreateStore(scope->process(operands.front()), addrOperand) ; for (auto i=++operands.begin(); i!=operands.end(); ++i){ addrOperand = llvm->builder.CreateGEP(typEl, addrOperand, ArrayRef(std::vector{one})); llvm->builder.CreateStore(scope->process(*i), addrOperand) ; } return list; // Value* listDest = l.builder.CreateAlloca(typList, ConstantInt::get(typI32, __size), *hintRetVar); // l.buil1der.CreateMemCpy(listDest, listSource, __size, 16); } llvm::Value* Advanced::compileConstantStringAsPChar(const string& data, const std::string& hintRetVar) { EXPAND_CONTEXT UNUSED(function); UNUSED(scope); Type* typPchar = PointerType::getUnqual(Type::getInt8Ty(llvm::getGlobalContext())); //ArrayType* typStr = (ArrayType*) (llvm->toLLVMType(ExpandedType(TypeAnnotation(tag_array, TypePrimitive::I8, size+1)))); /* std::vector chars; chars.reserve(size+1); for (size_t i=0; i< size; ++i){ chars[i] = ConstantInt::get(typI8, (unsigned char) data[i]); } chars[size] = ConstantInt::get(typI8, 0); */ Value* rawData = ConstantDataArray::getString(llvm::getGlobalContext(), data); Value* rawPtrData = llvm->builder.CreateAlloca(rawData->getType(), ConstantInt::get(Type::getInt32Ty(llvm::getGlobalContext()), 1, false)); llvm->builder.CreateStore(rawData, rawPtrData); return llvm->builder.CreateCast(llvm::Instruction::BitCast, rawPtrData, typPchar, hintRetVar); } diff --git a/cpp/src/compilation/containers.cpp b/cpp/src/compilation/containers.cpp index c50d43a..5735440 100644 --- a/cpp/src/compilation/containers.cpp +++ b/cpp/src/compilation/containers.cpp @@ -1,206 +1,206 @@ /* 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: containers.cpp * Author: pgess * * \file compilation/containers.h * \brief Containers compilation support. See more [details on Containers](/w/concepts/containers) */ #include "compilation/containers.h" using namespace std; using namespace llvm; using namespace xreate; using namespace xreate::containers; Iterator* Iterator::create(xreate::compilation::Context context, const xreate::Symbol& var){ const Implementation& data = Query::queryImplementation(var); switch(data.impl){ case ON_THE_FLY: return new IteratorForward(context, var, data.extract()); case SOLID: return new IteratorForward(context, var, data.extract()); default: assert(true); } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::begin() { switch(sourceDecl.op) { case xreate::Operator::LIST: { sourceRawType = llvm::Type::getInt32Ty(llvm::getGlobalContext()); return llvm::ConstantInt::get(Type::getInt32Ty(llvm::getGlobalContext()), 0); }; case xreate::Operator::LIST_RANGE:{ assert(sourceDecl.operands.size()==2); llvm::Value* result = sourceUnit->process(sourceDecl.operands.at(0)); sourceRawType = result->getType(); return result; }; default: break; } if (linkedlist){ llvm::Value* result = sourceUnit->process(sourceDecl); sourceRawType = result->getType(); return result; } assert(false); } llvm::Value* IteratorForward::end(){ switch(sourceDecl.op) { case xreate::Operator::LIST: { size_t idLast = sourceDecl.operands.size() - 1; return ConstantInt::get(sourceRawType, idLast); } case xreate::Operator::LIST_RANGE: { assert(sourceDecl.operands.size() == 2); llvm::Value* valueEndOfRange = sourceUnit->process(sourceDecl.operands.at(1)); llvm::Value* valueConstOne = llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 1); return llvm->builder.CreateAdd(valueEndOfRange, valueConstOne); }; default: break; } //return null pointer if (linkedlist){ return ConstantPointerNull::getNullValue(sourceRawType); } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::get(Value* index,const std::string& hintRetVar){ const Expression& currentDecl = CodeScope::getDefinition(current); switch (currentDecl.op) { case xreate::Operator::LIST: { //TODO re check is it right scope(source) to compile currentDecl. Provide unittests. llvm::Value* currentValue = sourceUnit->processSymbol(current); return xreate::compilation::Advanced(context).compileArrayIndex(currentValue, std::vector{index}); }; case xreate::Operator::LIST_RANGE: { return index; }; case xreate::Operator::MAP: { assert(currentDecl.getOperands().size()==1); assert(currentDecl.bindings.size()); assert(currentDecl.blocks.size()); CodeScope* scopeLoop = currentDecl.blocks.front(); std::string varEl = currentDecl.bindings[0]; - const Symbol& symbIn = Attachments::get(currentDecl.getOperands()[0]); + const Symbol& symbIn = Attachments::get(currentDecl.getOperands()[0]); auto it = std::unique_ptr(Iterator::create(context, symbIn)); Value* elIn = it->get(index, varEl); compilation::ICodeScopeUnit* unitLoop = function->getScopeUnit(scopeLoop); unitLoop->bindArg(elIn, std::move(varEl)); return unitLoop->compile(); } case xreate::Operator::NONE: { //TODO review iterator determination strategy for case of Expression::BINDING assert(currentDecl.__state==Expression::IDENT); - const Symbol& symbIn = Attachments::get(currentDecl); + const Symbol& symbIn = Attachments::get(currentDecl); auto it = std::unique_ptr(Iterator::create(context, symbIn)); return it->get(index); }; default: break; } if (linkedlist){ return index; } assert(false && "Unknown declaration"); return nullptr; } llvm::Value* IteratorForward::advance(Value* index, const std::string& hintRetVar){ switch(sourceDecl.op) { case xreate::Operator::LIST: case xreate::Operator::LIST_RANGE: return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 1), hintRetVar); default: break; } if (linkedlist){ ExpandedType tySource = llvm->ast->getType(CodeScope::getDefinition(source)); assert(tySource->__operator == TypeOperator::ARRAY && "Linked list implementation has to have ARRAY type"); assert(tySource->__operands.size()); return xreate::compilation::Advanced(context).compileStructIndex(index, ExpandedType(TypeAnnotation(tySource->__operands.at(0))), linkedlist.fieldPointer); } assert(false && "Unknown declaration"); return nullptr; } //const ImplementationRec& implementation IteratorForward::IteratorForward(const compilation::Context& ctx, const xreate::Symbol& symbolContainer, const ImplementationRec& implementation) : Iterator(), __length(implementation.size), llvm(ctx.pass->man->llvm) { __container = ctx.function->getScopeUnit(symbolContainer.scope)->processSymbol(symbolContainer); } llvm::Value* IteratorForward::begin(){ //0 return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), 0); } llvm::Value* IteratorForward::end(){ //length return llvm::ConstantInt::get(llvm::Type::getInt32Ty(llvm::getGlobalContext()), __length); } llvm::Value* IteratorForward::get(llvm::Value* index,const std::string& hintRetVar){ //GEP[index]] llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::Value* pResult = llvm->builder.CreateGEP(__container, ArrayRef(std::vector{ConstantInt::get(tyNum, 0), index})); return llvm->builder.CreateLoad(pResult, hintRetVar); } llvm::Value* IteratorForward::advance(llvm::Value* index, const std::string& hintRetVar){ //index + 1 llvm::Type* tyNum = llvm::Type::getInt32Ty(llvm::getGlobalContext()); return llvm->builder.CreateAdd(index, llvm::ConstantInt::get(tyNum, 1), hintRetVar); } diff --git a/cpp/src/compilation/polymorphcompiler.h b/cpp/src/compilation/polymorphcompiler.h index 48ff6b5..d2c933e 100644 --- a/cpp/src/compilation/polymorphcompiler.h +++ b/cpp/src/compilation/polymorphcompiler.h @@ -1,49 +1,55 @@ /* * 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: polymorphcompiler.h * Author: pgess * * Created on October 7, 2017 */ #ifndef POLYMORPHCOMPILER_H #define POLYMORPHCOMPILER_H #include "pass/compilepass.h" +#include "query/polymorph.h" namespace xreate { namespace polymorph { typedef Expression Guard; template class PolymorphCodeScopeUnit: public Parent{ public: PolymorphCodeScopeUnit(CodeScope* codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass) : Parent(codeScope, f, compilePass) {} protected: compilation::ICallStatement* findFunction(const Expression& opCall) override { - const std::string& keyAnnotationGuard = "callguard"; - if (!opCall.tags.count(keyAnnotationGuard)) return Parent::findFunction(opCall); - const Expression& annotationGuard = opCall.tags.at(keyAnnotationGuard); - assert(annotationGuard.operands.size()==1); - const Expression& guardSelected = annotationGuard.operands.at(0); - - std::map indexSpecs; + //Check does invocation require guards const std::string& nameCallee = opCall.getValueString(); const std::list& specializations = Parent::pass->man->root->getFunctionSpecializations(nameCallee); + if (specializations.size() == 1){ + if (!specializations.front()->guard.isValid()) { + return Parent::findFunction(opCall); + } + } + + assert(Attachments::exists(opCall) && "Guard required"); + const Expression& guardSelected = Attachments::get(opCall); + std::map indexSpecs; for(ManagedFnPtr specialization: specializations){ indexSpecs.emplace(specialization->guard, specialization); } - assert(indexSpecs.count(guardSelected)); + assert(indexSpecs.count(guardSelected) && "Can't found appropriate guard"); return new compilation::CallStatementRaw(Parent::pass->getFunctionUnit(indexSpecs.at(guardSelected))->compile(), Parent::pass->man->llvm); } }; +} } //end of xreate::polymorph + #endif /* POLYMORPHCOMPILER_H */ diff --git a/cpp/src/compilation/scopedecorators.h b/cpp/src/compilation/scopedecorators.h index 01cb9aa..54e7a23 100644 --- a/cpp/src/compilation/scopedecorators.h +++ b/cpp/src/compilation/scopedecorators.h @@ -1,145 +1,145 @@ /* 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: scopedecorators.h * Author: pgess * * Created on February 24, 2017, 11:35 AM */ /** * \file scopedecorators.h * \brief Basic code block compilation xreate::compilation::ICodeScopeUnit decorators */ #ifndef SCOPEDECORATORS_H #define SCOPEDECORATORS_H #include "ast.h" #include "compilation/targetinterpretation.h" #include "compilation/versions.h" #include "compilation/transformations.h" #include "compilation/polymorphcompiler.h" namespace xreate { class CompilePass; namespace compilation { class ICodeScopeUnit; class IFunctionUnit; /**\brief Caching ability for code scope compilation * \extends xreate::compilation::ICodeScopeUnit */ template class CachedScopeDecorator: public Parent{ typedef CachedScopeDecorator SELF; public: CachedScopeDecorator(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} void bindArg(llvm::Value* value, std::string&& alias) { //ensure existence of an alias assert(Parent::scope->__identifiers.count(alias)); //memorize new value for an alias ScopedSymbol id{Parent::scope->__identifiers.at(alias), versions::VERSION_NONE}; __rawVars[id] = value; } void bindArg(llvm::Value* value, const ScopedSymbol& s) { __rawVars[s] = value; } llvm::Value* compile(const std::string& hintBlockDecl="") override{ if (__rawVars.count(ScopedSymbol::RetSymbol)){ return __rawVars[ScopedSymbol::RetSymbol]; } return Parent::compile(hintBlockDecl); } llvm::Value* processSymbol(const Symbol& s, std::string hintRetVar) override{ CodeScope* scope = s.scope; SELF* self = dynamic_cast(Parent::function->getScopeUnit(scope)); if (self->__rawVars.count(s.identifier)){ return self->__rawVars[s.identifier]; } //Declaration could be overriden - Expression declaration = CodeScope::getDefinition(s); + Expression declaration = CodeScope::getDefinition(s, true); if (!declaration.isDefined()){ if (self->__declarationsOverriden.count(s.identifier)){ declaration = self->__declarationsOverriden[s.identifier]; } else { assert(false); //in case of binding there should be raws provided. } } return self->__rawVars[s.identifier] = Parent::processSymbol(s, hintRetVar); } void overrideDeclaration(const Symbol binding, Expression&& declaration){ SELF* self = dynamic_cast(Parent::function->getScopeUnit(binding.scope)); self->__declarationsOverriden.emplace(binding.identifier, std::move(declaration)); } void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __rawVars.clear(); __declarationsOverriden.clear(); __childScopes.clear(); } private: std::unordered_map __declarationsOverriden; std::unordered_map __rawVars; std::list> __childScopes; }; /**\brief Default code scope compilation functionality*/ typedef CachedScopeDecorator< polymorph::PolymorphCodeScopeUnit< - compilation::TransformationsScopeDecorator< + ::xreate::compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< versions::VersionsScopeDecorator>>>> DefaultCodeScopeUnit; } //end of compilation namespace struct CachedScopeDecoratorTag; struct VersionsScopeDecoratorTag; template<> struct DecoratorsDict{ typedef compilation::CachedScopeDecorator< polymorph::PolymorphCodeScopeUnit< compilation::TransformationsScopeDecorator< interpretation::InterpretationScopeDecorator< versions::VersionsScopeDecorator>>>> result; }; template<> struct DecoratorsDict{ typedef versions::VersionsScopeDecorator< compilation::BasicCodeScopeUnit> result; }; } //end of xreate #endif /* SCOPEDECORATORS_H */ diff --git a/cpp/src/compilation/targetinterpretation.cpp b/cpp/src/compilation/targetinterpretation.cpp index d550918..00637b1 100644 --- a/cpp/src/compilation/targetinterpretation.cpp +++ b/cpp/src/compilation/targetinterpretation.cpp @@ -1,595 +1,595 @@ /* 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: targetinterpretation.cpp * Author: pgess * * Created on June 29, 2016, 6:45 PM */ /** * \file targetinterpretation.h * \brief Interpretation support. See more [details on Interpretation](/w/concepts/dsl/) */ #include "compilation/targetinterpretation.h" #include "pass/interpretationpass.h" #include "analysis/typeinference.h" #include "llvmlayer.h" #include "compilation/scopedecorators.h" #include #include #include using namespace std; using namespace xreate::compilation; namespace xreate{ namespace interpretation{ const Expression EXPRESSION_FALSE = Expression(Atom(0)); const Expression EXPRESSION_TRUE = Expression(Atom(1)); Expression representAsAnnotation(const Gringo::Symbol& atom){ switch (atom.type()) { case Gringo::SymbolType::Num: { Expression result(Operator::VARIANT, {Expression(atom.num())}); result.setValueDouble(0); return result; } case Gringo::SymbolType::Str: { Expression result(Operator::VARIANT, {Expression(Atom(std::string(atom.string().c_str())))}); result.setValueDouble(1); return result; } case Gringo::SymbolType::Fun: { Expression fnDescription(Operator::LIST_NAMED, {}); std::list> bindings{Atom("name"), Atom("arguments")}; fnDescription.addBindings(bindings.begin(), bindings.end()); fnDescription.addArg(Expression(Atom(std::string(atom.name().c_str())))); Expression args(Operator::LIST, {}); for (const Gringo::Symbol& arg : atom.args()) { args.addArg(representAsAnnotation(arg)); } fnDescription.addArg(std::move(args)); Expression result(Operator::VARIANT, {fnDescription}); result.setValueDouble(2); return result; } default: { assert(false); } } } CodeScope* InterpretationScope::processOperatorIf(const Expression& expression){ const Expression& exprCondition = process(expression.getOperands()[0]); if (exprCondition == EXPRESSION_TRUE){ return expression.blocks.front(); } return expression.blocks.back(); } CodeScope* InterpretationScope::processOperatorSwitch(const Expression& expression) { const Expression& exprCondition = process(expression.operands[0]); bool flagHasDefault = expression.operands[1].op == Operator::CASE_DEFAULT; //TODO check that one and only one case variant is appropriate for (size_t size = expression.operands.size(), i= flagHasDefault? 2: 1; igetScope(exprCase.blocks.front())->processScope() == exprCondition){ return exprCase.blocks.back(); } } if (flagHasDefault){ const Expression& exprCaseDefault = expression.operands[1]; return exprCaseDefault.blocks.front(); } assert(false && "Switch has no appropriate variant"); return nullptr; } CodeScope* InterpretationScope::processOperatorSwitchVariant(const Expression& expression){ const Expression& condition = process(expression.operands.at(0)); assert(condition.op == Operator::VARIANT); const string identCondition = expression.bindings.front(); Expression opExpected(Atom(condition.getValueDouble())); auto itFoundValue = std::find(++expression.operands.begin(), expression.operands.end(), opExpected); assert(itFoundValue != expression.operands.end()); int indexBlock = itFoundValue - expression.operands.begin() -1; auto blockFound = expression.blocks.begin(); std::advance(blockFound, indexBlock); InterpretationScope* scopeI12n = function->getScope(*blockFound); if (condition.operands.size()) { const Expression& value = condition.operands.at(0); scopeI12n->overrideBinding(value, identCondition); } return *blockFound; } llvm::Value* InterpretationScope::compileHybrid(const InterpretationOperator& op, const Expression& expression, const Context& context){ switch(op){ case IF_INTERPRET_CONDITION: { CodeScope* scopeResult = processOperatorIf(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_INTERPRET_CONDITION:{ CodeScope* scopeResult = processOperatorSwitch(expression); llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); const Expression& condition = expression.operands.at(0); const Expression& valueCondition = process(condition); const string identCondition = expression.bindings.front(); auto scopeCompilation = Decorators::getInterface(context.function->getScopeUnit(scopeResult)); if(valueCondition.operands.size()){ //override value Symbol symbCondition{ScopedSymbol{scopeResult->__identifiers.at(identCondition), versions::VERSION_NONE}, scopeResult}; scopeCompilation->overrideDeclaration(symbCondition, Expression(valueCondition.operands.at(0))); //set correct type for binding: TypeAnnotation typeVariant = typeinference::getType(condition, *function->man->ast); int conditionIndex = valueCondition.getValueDouble(); ScopedSymbol symbolInternal = scopeResult->getSymbol(identCondition); scopeResult->__declarations[symbolInternal].bindType(typeVariant.__operands.at(conditionIndex)); } llvm::Value* result = context.function->getScopeUnit(scopeResult)->compile(); return result; } case FOLD_INTERPRET_INPUT: { //initialization const Expression& exprInput = process(expression.getOperands()[0]); assert(exprInput.op == Operator::LIST); CodeScope* scopeBody = expression.blocks.front(); const string& nameEl = expression.bindings[0]; Symbol symbEl{ScopedSymbol{scopeBody->__identifiers.at(nameEl), versions::VERSION_NONE}, scopeBody}; const std::string& idAccum = expression.bindings[1]; llvm::Value* rawAccum = context.scope->process(expression.getOperands()[1]); InterpretationScope* intrBody = function->getScope(scopeBody); auto unitBody = Decorators::getInterface(context.function->getScopeUnit(scopeBody)); const std::vector elementsInput= exprInput.getOperands(); for (size_t i=0; ireset(); unitBody->reset(); Expression exprElement = elementsInput[i]; intrBody->overrideBinding(exprElement, nameEl); unitBody->overrideDeclaration(symbEl, move(exprElement)); unitBody->bindArg(rawAccum, string(idAccum)); rawAccum = unitBody->compile(); } return rawAccum; } /* case FOLD_INF_INTERPRET_INOUT{ } */ //TODO refactor as InterpretationCallStatement class case CALL_INTERPRET_PARTIAL: { const std::string &calleeName = expression.getValueString(); ICodeScopeUnit* scopeUnitSelf = context.scope; ManagedFnPtr callee = this->function->man->ast->findFunction(calleeName); const FunctionInterpretationData& calleeData = FunctionInterpretationHelper::getSignature(callee); std::vector argsActual; PIFSignature sig; sig.declaration = callee; for(size_t no=0, size = expression.operands.size(); no < size; ++no){ const Expression& op = expression.operands[no]; if (calleeData.signature.at(no) == INTR_ONLY){ sig.bindings.push_back(process(op)); continue; } argsActual.push_back(scopeUnitSelf->process(op)); } TargetInterpretation* man = dynamic_cast(this->function->man); PIFunction* pifunction = man->getFunction(move(sig)); llvm::Function* raw = pifunction->compile(); boost::scoped_ptr statement(new CallStatementRaw(raw, man->pass->man->llvm)); return (*statement)(move(argsActual)); } default: break; } assert(false&& "Unknown hybrid operator"); return nullptr; } llvm::Value* InterpretationScope::compile(const Expression& expression, const Context& context){ const InterpretationData& data = Attachments::get(expression); if (data.op != InterpretationOperator::NONE){ return compileHybrid(data.op, expression, context); } Expression result = process(expression); return context.scope->process(result); } Expression InterpretationScope::process(const Expression& expression){ switch (expression.__state){ case Expression::INVALID: assert(false); case Expression::NUMBER: case Expression::STRING: return expression; case Expression::IDENT:{ - Symbol s = Attachments::get(expression); + Symbol s = Attachments::get(expression); return Parent::processSymbol(s); } case Expression::COMPOUND: break; default: assert(false); } switch (expression.op) { case Operator::EQU: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_TRUE; return EXPRESSION_FALSE; } case Operator::NE: { const Expression& left = process(expression.operands[0]); const Expression& right = process(expression.operands[1]); if (left == right) return EXPRESSION_FALSE; return EXPRESSION_TRUE; } case Operator::LOGIC_AND: { assert(expression.operands.size() == 1); return process (expression.operands[0]); } // case Operator::LOGIC_OR: case Operator::CALL: { const std::string &fnName = expression.getValueString(); ManagedFnPtr fnAst = this->function->man->ast->findFunction(fnName); InterpretationFunction* fnUnit = this->function->man->getFunction(fnAst); vector args; args.reserve(expression.getOperands().size()); for(size_t i=0, size = expression.getOperands().size(); iprocess(args); } case Operator::CALL_INTRINSIC: { std::string nameFunction = expression.getValueString(); if(nameFunction=="query"){ assert(expression.operands.size() == 1); assert(expression.operands.front().__state == Expression::STRING); std::string namePredicate = expression.operands.front().getValueString(); ClaspLayer::ModelFragment model = (static_cast(function->man))->pass->man->clasp->query(namePredicate); Expression result(Operator::LIST, {}); if(model) for (const auto& row: boost::make_iterator_range(model.get())) { result.addArg(representAsAnnotation(std::get<1>(row))); } return result; } else { assert(false && "Unknown intrinsic"); } } case Operator::IF:{ CodeScope* scopeResult = processOperatorIf(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH: { CodeScope* scopeResult = processOperatorSwitch(expression); return function->getScope(scopeResult)->processScope(); } case Operator::SWITCH_VARIANT: { CodeScope* scopeResult = processOperatorSwitchVariant(expression); return function->getScope(scopeResult)->processScope(); } case Operator::INDEX: { Expression exprData = process(expression.operands[0]); for (size_t keyId=1; keyIdgetScope(expression.blocks.front()); Expression accum = exprInit; for(size_t size=exprInput.getOperands().size(), i=0; ioverrideBinding(exprInput.getOperands()[i], argEl); body->overrideBinding(accum, argAccum); accum = body->processScope(); } return accum; } // case Operator::MAP: { // break; // } default: break; } return expression; } InterpretationFunction* TargetInterpretation::getFunction(IFunctionUnit* unit){ if (__dictFunctionsByUnit.count(unit)) { return __dictFunctionsByUnit.at(unit); } InterpretationFunction* f = new InterpretationFunction(unit->function, this); __dictFunctionsByUnit.emplace(unit, f); assert(__functions.emplace(unit->function.id(), f).second); return f; } PIFunction* TargetInterpretation::getFunction(PIFSignature&& sig){ auto f = __pifunctions.find(sig); if (f != __pifunctions.end()){ return f->second; } PIFunction* result = new PIFunction(PIFSignature(sig), __pifunctions.size(), this); __pifunctions.emplace(move(sig), result); assert(__dictFunctionsByUnit.emplace(result->functionUnit, result).second); return result; } InterpretationScope* TargetInterpretation::transformContext(const Context& c){ return this->getFunction(c.function)->getScope(c.scope->scope); } llvm::Value* TargetInterpretation::compile(const Expression& expression, const Context& ctx){ return transformContext(ctx)->compile(expression, ctx); } InterpretationFunction::InterpretationFunction(const ManagedFnPtr& function, Target* target) : Function(function, target) {} Expression InterpretationFunction::process(const std::vector& args){ InterpretationScope* body = getScope(__function->__entry); for(size_t i=0, size = args.size(); ioverrideBinding(args.at(i), string(body->scope->__bindings.at(i))); } return body->processScope(); } // Partial function interpretation typedef BasicFunctionUnit PIFunctionUnitParent; class PIFunctionUnit: public PIFunctionUnitParent{ public: PIFunctionUnit(ManagedFnPtr f, std::set&& arguments, size_t id, CompilePass* p) : PIFunctionUnitParent(f, p), argumentsActual(move(arguments)), __id(id) {} protected: std::vector prepareArguments(){ LLVMLayer* llvm = PIFunctionUnitParent::pass->man->llvm; AST* ast = PIFunctionUnitParent::pass->man->root; CodeScope* entry = PIFunctionUnitParent::function->__entry; std::vector signature; for(size_t no: argumentsActual){ VNameId argId = entry->__identifiers.at(entry->__bindings.at(no)); ScopedSymbol arg{argId, versions::VERSION_NONE}; signature.push_back(llvm->toLLVMType(ast->expandType(entry->__declarations.at(arg).type))); } return signature; } llvm::Function::arg_iterator prepareBindings(){ CodeScope* entry = PIFunctionUnitParent::function->__entry; ICodeScopeUnit* entryCompilation = PIFunctionUnitParent::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = PIFunctionUnitParent::raw->arg_begin(); for(size_t no: argumentsActual){ ScopedSymbol arg{entry->__identifiers.at(entry->__bindings.at(no)), versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, arg); fargsI->setName(entry->__bindings.at(no)); ++fargsI; } return fargsI; } virtual std::string prepareName(){ return PIFunctionUnitParent::prepareName() + "_" + std::to_string(__id); } private: std::set argumentsActual; size_t __id; }; PIFunction::PIFunction(PIFSignature&& sig, size_t id, TargetInterpretation* target) : InterpretationFunction(sig.declaration, target), signatureInstance(move(sig)) { const FunctionInterpretationData& functionData = FunctionInterpretationHelper::getSignature(signatureInstance.declaration); std::set argumentsActual; for (size_t no=0, size=functionData.signature.size(); no < size; ++no){ if (functionData.signature.at(no) != INTR_ONLY){ argumentsActual.insert(no); } } functionUnit = new PIFunctionUnit(signatureInstance.declaration, move(argumentsActual), id, target->pass); CodeScope* entry = signatureInstance.declaration->__entry; auto entryUnit = Decorators::getInterface<>(functionUnit->getEntry()); InterpretationScope* entryIntrp = InterpretationFunction::getScope(entry); for(size_t no=0, sigNo=0, size = entry->__bindings.size(); no < size; ++no){ if (functionData.signature.at(no) == INTR_ONLY){ entryIntrp->overrideBinding(signatureInstance.bindings[sigNo], entry->__bindings[no]); VNameId argId = entry->__identifiers.at(entry->__bindings[no]); Symbol argSymbol{ScopedSymbol{argId, versions::VERSION_NONE}, entry}; entryUnit->overrideDeclaration(argSymbol, Expression(signatureInstance.bindings[sigNo])); ++sigNo; } } } llvm::Function* PIFunction::compile(){ llvm::Function* raw = functionUnit->compile(); return raw; } bool operator<(const PIFSignature& lhs, const PIFSignature& rhs){ if (lhs.declaration.id() != rhs.declaration.id()) { return lhs.declaration.id() < rhs.declaration.id(); } return lhs.bindings < rhs.bindings; } bool operator<(const PIFSignature& lhs, PIFunction* const rhs){ return lhs < rhs->signatureInstance; } bool operator<(PIFunction* const lhs, const PIFSignature& rhs){ return lhs->signatureInstance < rhs; } }} /** \class xreate::interpretation::InterpretationFunction * * Holds list of xreate::interpretation::InterpretationScope 's focused on interpretation of individual code scopes * * There is particulat subclass PIFunction intended to represent partially interpreted functions *\sa TargetInterpretation, [Interpretation Concept](/w/concepts/dfa) */ /** \class xreate::interpretation::TargetInterpretation * * Executed during compilation and intented to preprocess eligible parts of code. * Established on [Targets Infrastructure](\ref compilation::Target) * * Holds list of InterpretationFunction / PIFunction to represent interpretation process for individual functions * * In order to be activated during compilation process there is * InterpretationScopeDecorator implementation of ICodeScopeUnit * \sa InterpretationPass, compilation::Target, [Interpretation Concept](/w/concepts/dfa) * */ \ No newline at end of file diff --git a/cpp/src/compilation/targets.h b/cpp/src/compilation/targets.h index 2da1ce4..5c39425 100644 --- a/cpp/src/compilation/targets.h +++ b/cpp/src/compilation/targets.h @@ -1,201 +1,201 @@ /* 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: targetabstract.h * Author: pgess * * Created on July 2, 2016, 1:25 PM */ /** * \file * \brief Compilation targets infrastructure */ #ifndef TARGETABSTRACT_H #define TARGETABSTRACT_H #include "ast.h" #include #include namespace xreate{ namespace compilation { template struct TargetInfo{ //typedef Result //typedef Function //typedef Scope }; template class Function; template class Target; template class Scope{ typedef typename TargetInfo::Scope Self; public: CodeScope* scope; typename TargetInfo::Result processSymbol(const Symbol& s){ CodeScope* scope = s.scope; Self* self = function->getScope(scope); if (self->__bindings.count(s.identifier)) { return self->__bindings[s.identifier]; } - const Expression& declaration = CodeScope::getDefinition(s); + const Expression& declaration = CodeScope::getDefinition(s, true); if (!declaration.isDefined()){ assert(false); //for bindings there should be result already } return self->__bindings[s.identifier] = self->process(declaration); } typename TargetInfo::Result processScope() { if (raw) return *raw; raw = process(scope->getBody()); return *raw; } // typename TargetInfo::Result // processFunction(typename TargetInfo::Function* fnRemote, const std::vector::Result>& args){ // Scope scopeRemote = fnRemote->getScope(fnRemote->__function->__entry); // // if (scopeRemote->raw){ // return scopeRemote->raw; // } // // return fnRemote->process(args); // } virtual typename TargetInfo::Result process(const Expression& expression)=0; Scope(CodeScope* codeScope, Function* f) : scope(codeScope), function(f) {} virtual ~Scope(){} void overrideBinding(typename TargetInfo::Result arg, const std::string& name){ assert(scope->__identifiers.count(name)); ScopedSymbol id{scope->__identifiers.at(name), versions::VERSION_NONE}; __bindings[id] = arg; //reset the result if any: raw.reset(); } void registerChildScope(std::shared_ptr scope){ __childScopes.push_back(scope); } void reset(){ __bindings.clear(); __childScopes.clear(); raw.reset(); } protected: Function* function=0; std::map::Result> __bindings; std::list> __childScopes; typename boost::optional::Result> raw; }; template class Function{ typedef typename TargetInfo::Result Result; typedef typename TargetInfo::Scope ConcreteScope; public: Function(const ManagedFnPtr& function, Target* target) : man(target), __function(function) {} virtual ~Function(){}; ConcreteScope* getScope(CodeScope* scope){ if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result){ return result.get(); } } std::shared_ptr unit(new ConcreteScope(scope, this)); if (scope->__parent != nullptr){ getScope(scope->__parent)->registerChildScope(unit); } else { assert(!__entryScope); __entryScope = unit; } if (!__scopes.emplace(scope, unit).second){ __scopes[scope] = unit; } return unit.get(); } virtual Result process(const std::vector& args)=0; Target* man=0; ManagedFnPtr __function; protected: std::map> __scopes; std::shared_ptr __entryScope; }; /** \brief Similar to xreate::IPass */ template class Target { typedef typename TargetInfo::Function ConcreteFunction; public: Target(AST* root): ast(root){} ConcreteFunction* getFunction(const ManagedFnPtr& function){ unsigned int id = function.id(); if (!__functions.count(id)){ ConcreteFunction* unit = new ConcreteFunction(function, this); __functions.emplace(id, unit); return unit; } return __functions.at(id); } AST* ast; virtual ~Target(){ for (const auto& entry: __functions){ delete entry.second; } } protected: std::map __functions; }; }} #endif /* TARGETABSTRACT_H */ diff --git a/cpp/src/compilation/versions.h b/cpp/src/compilation/versions.h index 6369d31..d63d03c 100644 --- a/cpp/src/compilation/versions.h +++ b/cpp/src/compilation/versions.h @@ -1,139 +1,153 @@ /* 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/. * * versions.cpp * * Author: pgess * Created on January 21, 2017, 1:24 PM */ /** * \file * \brief CodeScope's Decorator to support Versions */ #include "pass/versionspass.h" #include "pass/compilepass.h" #include "llvmlayer.h" namespace xreate { class CompilePass; namespace compilation { class ICodeScopeUnit; class IFunctionUnit; } namespace versions{ /**\brief Enables compilation of code with versioned variables * \details Dictates order of computation determined by VersionsPass * \extends xreate::compilation::ICodeScopeUnit * \sa VersionsPass, VersionsGraph */ template class VersionsScopeDecorator: public Parent{ typedef VersionsScopeDecorator SELF; public: VersionsScopeDecorator(CodeScope* codeScope, compilation::IFunctionUnit* f, CompilePass* compilePass): Parent(codeScope, f, compilePass){} virtual llvm::Value* processSymbol(const Symbol& s, std::string hintSymbol=""){ if (Attachments::exists(s)){ const std::list dependencies = Attachments::get(s); for(const Symbol& symbolDependent: dependencies){ processSymbol(symbolDependent); } } llvm::Value* result = Parent::processSymbol(s, hintSymbol); if (s.identifier.version == VERSION_INIT){ llvm::Value* storage = SELF::processIntrinsicInit(result->getType(), hintSymbol); setSymbolStorage(s, storage); processIntrinsicCopy(result, storage); return compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage); } else if (s.identifier.version != VERSION_NONE){ Symbol symbolInitVersion = getSymbolInitVersion(s); llvm::Value* storage = getSymbolStorage(symbolInitVersion); processIntrinsicCopy(result, storage); return compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateLoad(storage); } return result; } llvm::Value* processIntrinsicInit(llvm::Type* typeStorage, const std::string& hintVarDecl=""){ llvm::IntegerType* tyInt = llvm::Type::getInt32Ty(llvm::getGlobalContext()); llvm::ConstantInt* constOne = llvm::ConstantInt::get(tyInt, 1, false); return compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateAlloca(typeStorage, constOne, hintVarDecl); } void processIntrinsicCopy(llvm::Value* value, llvm::Value* storage){ compilation::ICodeScopeUnit::pass->man->llvm->builder.CreateStore(value, storage); } private: std::map __symbolStorage; static Symbol getSymbolInitVersion(const Symbol& s){ return Symbol{ScopedSymbol{s.identifier.id, VERSION_INIT}, s.scope}; } llvm::Value* getSymbolStorage(const Symbol& s){ return __symbolStorage.at(s); } void setSymbolStorage(const Symbol& s, llvm::Value* storage){ __symbolStorage[s] = storage; } }; + +template +class VersionedFunctionDecorator : public Parent { +public: + VersionedFunctionDecorator(ManagedFnPtr f, CompilePass* p) + : Parent(f, p){} + +protected: + std::vector prepareArguments() { + std::vector&& arguments = Parent::prepareArguments(); + + return arguments; + } +}; } } //end of namespace xreate::versions // llvm::Value* // processIntrinsicInitAndCopy(){ // // } //llvm::Value* //process(const Expression& expr, const std::string& hintVarDecl){ // case Operator::CALL_INTRINSIC: { // enum INRINSIC{INIT, COPY}; // // const ExpandedType& typSymbol = pass->man->root->expandType(expr.type); // // INTRINSIC op = (INTRINSIC) expr.getValueDouble(); // // switch (op){ // case INIT: { // llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol); // // // return storage; // } // // case COPY: { // llvm::Type* typSymbolRaw = l.toLLVMType(typSymbol); // llvm::value* valueOriginal = process(expr.getOperands()[0], hintVarDecl); // llvm::Value* storage = l.builder.CreateAlloca(typSymbolRaw, constOne, hintVarDecl); // llvm::Value* valueCopy = l.builder.CreateStore(valueOriginal, storage); // // return valueCopy; // } // } // return; // } //} //}; diff --git a/cpp/src/pass/abstractpass.cpp b/cpp/src/pass/abstractpass.cpp index 387efbf..17f8385 100644 --- a/cpp/src/pass/abstractpass.cpp +++ b/cpp/src/pass/abstractpass.cpp @@ -1,103 +1,103 @@ /* 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/. * * Author: pgess */ /** * \file abstractpass.h * \brief Infrastructure to iterate over AST to facilitate analysis and compilation. */ #include "abstractpass.h" #include "attachments.h" #include "xreatemanager.h" using namespace std; namespace xreate { template<> void defaultValue(){} void IPass::finish(){} IPass::IPass(PassManager *manager) : man(manager) { } template<> void AbstractPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol) { if (__visitedSymbols.isCached(symbol)) return; __visitedSymbols.setCachedValue(symbol); - const Expression& declaration = CodeScope::getDefinition(symbol); + const Expression& declaration = CodeScope::getDefinition(symbol, true); if (declaration.isDefined()){ PassContext context2 = context.updateScope(symbol.scope); process(declaration, context2, hintSymbol); } } template<> void AbstractPass::process(const Expression& expression, PassContext context, const std::string& varDecl){ if (expression.__state == Expression::COMPOUND){ for (const Expression &op: expression.getOperands()) { process(op, context); } for (CodeScope* scope: expression.blocks) { process(scope, context); } if (expression.op == Operator::CALL){ processExpressionCall(expression, context); } return; } if (expression.__state == Expression::IDENT){ assert(context.scope); - processSymbol(Attachments::get(expression), context, expression.getValueString()); + processSymbol(Attachments::get(expression), context, expression.getValueString()); } } } /** * \class xreate::IPass * * Each pass has to have IPass interface to be controllable by XreateManager. * However in most cases users should inherit minimal useful implementation xreate::AbstractPass * * \note It's usually expected that custom Pass publish processing results by one of the following means: * - xreate::Attachments for communicating with other Passes * - IAnalysisReport to feed xreate::ClaspLayer solver * * \sa xreate::XreateManager, xreate::AbstractPass */ /** * \class xreate::AbstractPass * * Iterates over %AST and provides functions to alter processing of particular %AST nodes. * Thus client should not re-implement every possible node processing * and it's enough to focus only on relevant nodes. * * Template parameter `Output` specify type of node processing result data. * * Automatically caches already visited nodes * * \note It's usually expected that custom Pass publish processing results by one of the following means: * - xreate::Attachments for communicating with other Passes * - IAnalysisReport to feed xreate::ClaspLayer solver * * * \sa xreate::XreateManager, xreate::IPass, xreate::AST */ diff --git a/cpp/src/pass/abstractpass.h b/cpp/src/pass/abstractpass.h index 5ed39cb..93322d9 100644 --- a/cpp/src/pass/abstractpass.h +++ b/cpp/src/pass/abstractpass.h @@ -1,205 +1,206 @@ /* 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/. * * Author: pgess */ #ifndef ABSTRACTPASS_H #define ABSTRACTPASS_H #include "ast.h" #include "xreatemanager.h" #include namespace xreate { /** \brief Holds current position in %AST while traversing*/ struct PassContext { CodeScope* scope = 0; ManagedFnPtr function; ManagedRulePtr rule; std::string varDecl; PassContext() {} PassContext updateScope(CodeScope* scopeNew) { PassContext context2{*this}; context2.scope = scopeNew; return context2; } ~PassContext(){} }; /** \brief Base class for all passes to inherit */ class IPass { public: IPass(PassManager* manager); /** \brief Executes pass */ virtual void run()=0; /** \brief Finalizes pass. Empty by default*/ virtual void finish(); PassManager* man; }; template Output defaultValue(); template<> void defaultValue(); /** \brief Stores processing results for already visited nodes */ template class SymbolCache: private std::map{ public: bool isCached(const Symbol& symbol){ return this->count(symbol); } Output setCachedValue(const Symbol& symbol, Output&& value){ (*this)[symbol] = value; return value; } Output getCachedValue(const Symbol& symbol){ assert(this->count(symbol)); return this->at(symbol); } }; /** \brief Set of already visited nodes */ template<> class SymbolCache: private std::set{ public: bool isCached(const Symbol& symbol){ bool result = this->count(symbol) > 0; return result; } void setCachedValue(const Symbol& symbol){ this->insert(symbol); } void getCachedValue(const Symbol& symbol){ } }; /** \brief Minimal useful IPass implementation*/ template class AbstractPass: public IPass { SymbolCache __visitedSymbols; protected: virtual Output processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol=""){ if (__visitedSymbols.isCached(symbol)) return __visitedSymbols.getCachedValue(symbol); - const Expression& declaration = CodeScope::getDefinition(symbol); + const Expression& declaration = CodeScope::getDefinition(symbol, true); if (declaration.isDefined()){ PassContext context2 = context.updateScope(symbol.scope); Output&& result = process(declaration, context2, hintSymbol); return __visitedSymbols.setCachedValue(symbol, std::move(result)); } return defaultValue(); } Output processExpressionCall(const Expression& expression, PassContext context){ const std::string &calleeName = expression.getValueString(); std::list callees = man->root->getFunctionSpecializations(calleeName); if (callees.size() == 1 && callees.front()){ return processFnCall(callees.front(), context); } else { for (const ManagedFnPtr& callee: callees){ processFnCallUncertain(callee, context); } return defaultValue(); } } SymbolCache& getSymbolCache(){ return __visitedSymbols; } public: AbstractPass(PassManager* manager) : IPass(manager){} /** \brief Processes function invocation instruction */ virtual Output processFnCall(ManagedFnPtr functionCallee, PassContext context){ return defaultValue(); } /** \brief Processes function invocation instruction in uncertain cases * \details Executed when it's impossible statically determine exact function invocation. * In this case get executed for all possible candidates */ virtual void processFnCallUncertain(ManagedFnPtr functionCallee, PassContext context) {} /** \brief Processes Logic Rule */ virtual void process(ManagedRulePtr rule) {} /** \brief Processes Function */ virtual Output process(ManagedFnPtr function) { PassContext context; context.function = function; return process(function->getEntryScope(), context); } /** \brief Processes single CodeScope */ virtual Output process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl=""){ context.scope = scope; return processSymbol(Symbol{ScopedSymbol::RetSymbol, scope}, context); } - + + //TODO expose Symbol instead of varDecl. Required by DFAPass. /** \brief Processes single Expression */ virtual Output process(const Expression& expression, PassContext context, const std::string& varDecl=""){ if (expression.__state == Expression::IDENT){ assert(context.scope); - return processSymbol(Attachments::get(expression), context, expression.getValueString()); + return processSymbol(Attachments::get(expression), context, expression.getValueString()); } assert(false); return defaultValue(); } /** \brief Executes AST traverse */ void run() { ManagedRulePtr rule = man->root->begin(); while (rule.isValid()) { process(rule); ++rule; } ManagedFnPtr f = man->root->begin(); while (f.isValid()) { process(f); ++f; } } }; template<> void AbstractPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol); template<> void AbstractPass::process(const Expression& expression, PassContext context, const std::string& hintSymbol); } #endif diff --git a/cpp/src/pass/cfapass.cpp b/cpp/src/pass/cfapass.cpp index cf2a829..f231187 100644 --- a/cpp/src/pass/cfapass.cpp +++ b/cpp/src/pass/cfapass.cpp @@ -1,119 +1,124 @@ /* 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/. * * cfapass.cpp * * Author: pgess */ /** * \file cfapass.h * \brief Control Flow Analysis(CFA) */ #include "cfapass.h" #include "analysis/cfagraph.h" +#include "analysis/DominatorsTreeAnalysisProvider.h" #include using namespace std; using namespace xreate::cfa; void CFAPass::initSignatures(){ auto range = man->root->__interfacesData.equal_range(CFA); for (auto i = range.first; i!= range.second; ++i){ __signatures.emplace(i->second.op, i->second); } } void CFAPass::run(){ initSignatures(); return AbstractPass::run(); } void CFAPass::finish() { - man->clasp->setCFAData(move(__context.graph)); + man->clasp->registerReport(__context.graph); + + dominators::DominatorsTreeAnalysisProvider* reportDominators = new dominators::DominatorsTreeAnalysisProvider(); + reportDominators->run(man->clasp, __context.graph); + man->clasp->registerReport(reportDominators); return AbstractPass::finish(); } void CFAPass::processFnCall(ManagedFnPtr function, PassContext context) { ClaspLayer* clasp = man->clasp; __context.graph->addCallConnection(clasp->pack(context.scope), function->getName()); return AbstractPass::processFnCall(function, context); } void CFAPass::processFnCallUncertain(ManagedFnPtr function, PassContext context){ ClaspLayer* clasp = man->clasp; __context.graph->addCallConnection(clasp->pack(context.scope), function->getName()); return AbstractPass::processFnCallUncertain(function, context); } void CFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){ ClaspLayer* clasp = man->clasp; CodeScope* scopeParent = context.scope; ScopePacked scopeId = clasp->pack(scope); if (scopeParent){ __context.graph->addParentConnection(scopeId, clasp->pack(scopeParent)); } else { __context.graph->addParentConnection(scopeId, context.function->getName()); } //TOTEST scope annotations //SECTIONTAG context gather scope annotations __context.graph->addScopeAnnotations(scopeId, scope->tags); __context.graph->addContextRules(scopeId, scope->contextRules); return AbstractPass::process(scope, context, hintBlockDecl); } //TOTEST scope annotations via scheme void CFAPass::process(const Expression& expression, PassContext context, const std::string& varDecl){ ClaspLayer* clasp = man->clasp; if (expression.__state == Expression::COMPOUND){ - Operator op= expression.op; + Operator op= expression.op; - if (__signatures.count(op)) { - assert(expression.blocks.size()); + if (__signatures.count(op)) { + assert(expression.blocks.size()); - for (const auto& scheme: boost::make_iterator_range(__signatures.equal_range(expression.op))) { - __context.graph->addScopeAnnotations(clasp->pack(expression.blocks.front()), scheme.second.getOperands()); - } + for (const auto& scheme: boost::make_iterator_range(__signatures.equal_range(expression.op))) { + __context.graph->addScopeAnnotations(clasp->pack(expression.blocks.front()), scheme.second.getOperands()); } + } } return AbstractPass::process(expression, context, varDecl); } void CFAPass::process(ManagedFnPtr function) { __context.graph->addFunctionAnnotations(function->getName(), function->getTags()); return AbstractPass::process(function); } CFAPass::CFAPass(PassManager* manager) : AbstractPass(manager) , __context{new CFAGraph(manager->clasp)} {} /** * \class xreate::cfa::CFAPass * \details Provides CFA, important analysis for reasoning. Iterates over AST and stores collected data in CFAGraph */ \ No newline at end of file diff --git a/cpp/src/pass/compilepass.cpp b/cpp/src/pass/compilepass.cpp index 255e5e2..66021a2 100644 --- a/cpp/src/pass/compilepass.cpp +++ b/cpp/src/pass/compilepass.cpp @@ -1,824 +1,827 @@ /* 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/. * * Author: pgess * * compilepass.cpp */ /** * \file compilepass.h * \brief Compilation pass */ #include "compilepass.h" #include "clasplayer.h" #include #include "llvmlayer.h" #include "query/containers.h" #include "query/context.h" #include "compilation/containers.h" #include "compilation/latecontextcompiler2.h" #include "ExternLayer.h" #include "pass/adhocpass.h" #include "compilation/targetinterpretation.h" #include "pass/versionspass.h" #include "compilation/scopedecorators.h" #include "compilation/adhocfunctiondecorator.h" #include "compilation/operators.h" #include "analysis/typeinference.h" #include #include #include using namespace std; using namespace llvm; //TODO use Scope //SECTIONTAG late-context FunctionDecorator namespace xreate{namespace context{ /** \brief Late Context enabled decorator for IFunctionUnit * \extends IFunctionUnit */ template class LateContextFunctionDecorator : public Parent { public: LateContextFunctionDecorator(ManagedFnPtr f, CompilePass* p) : Parent(f, p), contextCompiler(this, p) { } protected: std::vector prepareArguments() { std::vector&& 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; } llvm::Function::arg_iterator prepareBindings() { llvm::Function::arg_iterator fargsI = Parent::prepareBindings(); size_t sizeLateContextDemand = contextCompiler.getFunctionDemandSize(); if (sizeLateContextDemand) { fargsI->setName("latecontext"); contextCompiler.rawContextArgument = &*fargsI; ++fargsI; } return fargsI; } public: context::LateContextCompiler2 contextCompiler; }; }} //end of namespace xreate::context namespace xreate { namespace compilation{ std::string BasicFunctionUnit::prepareName(){ AST* ast = IFunctionUnit::pass->man->root; string name = ast->getFunctionSpecializations(IFunctionUnit::function->__name).size() > 1 ? IFunctionUnit::function->__name + std::to_string(IFunctionUnit::function.id()) : IFunctionUnit::function->__name; return name; } std::vector BasicFunctionUnit::prepareArguments() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; std::vector signature; std::transform(entry->__bindings.begin(), entry->__bindings.end(), std::inserter(signature, signature.end()), [llvm, ast, entry](const std::string & arg)->llvm::Type* { assert(entry->__identifiers.count(arg)); ScopedSymbol argid{entry->__identifiers.at(arg), versions::VERSION_NONE}; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(argid).type)); }); return signature; } llvm::Type* BasicFunctionUnit::prepareResult() { LLVMLayer* llvm = IFunctionUnit::pass->man->llvm; AST* ast = IFunctionUnit::pass->man->root; CodeScope* entry = IFunctionUnit::function->__entry; return llvm->toLLVMType(ast->expandType(entry->__declarations.at(ScopedSymbol::RetSymbol).type)); } llvm::Function::arg_iterator BasicFunctionUnit::prepareBindings() { CodeScope* entry = IFunctionUnit::function->__entry; ICodeScopeUnit* entryCompilation = IFunctionUnit::getScopeUnit(entry); llvm::Function::arg_iterator fargsI = IFunctionUnit::raw->arg_begin(); for (std::string &arg : entry->__bindings) { ScopedSymbol argid{entry->__identifiers[arg], versions::VERSION_NONE}; entryCompilation->bindArg(&*fargsI, argid); fargsI->setName(arg); ++fargsI; } return fargsI; } //DEBT compiler rigidly depends on exact definition of DefaultFunctionUnit typedef context::LateContextFunctionDecorator< adhoc::AdhocFunctionDecorator< BasicFunctionUnit>> DefaultFunctionUnit; ICodeScopeUnit::ICodeScopeUnit(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass) : pass(compilePass), function(f), scope(codeScope) { } llvm::Value* CallStatementRaw::operator()(std::vector&& args, const std::string& hintDecl) { llvm::Function* calleeInfo = dyn_cast(__callee); if (calleeInfo) { auto argsFormal = calleeInfo->args(); int pos = 0; //SECTIONTAG types/convert function ret value for (auto argFormal = argsFormal.begin(); argFormal != argsFormal.end(); ++argFormal, ++pos) { args[pos] = typeinference::doAutomaticTypeConversion(args[pos], argFormal->getType(), llvm->builder); } } return llvm->builder.CreateCall(__calleeTy, __callee, args, hintDecl); } //DESABLEDFEATURE implement inlining class CallStatementInline : public ICallStatement { public: CallStatementInline(IFunctionUnit* caller, IFunctionUnit* callee, LLVMLayer* l) : __caller(caller), __callee(callee), llvm(l) { } llvm::Value* operator()(std::vector&& args, const std::string& hintDecl) { //TOTEST inlining // CodeScopeUnit* entryCompilation = outer->getScopeUnit(function->__entry); // for(int i=0, size = args.size(); ibindArg(args.at(i), string(entryCompilation->scope->__bindings.at(i))); // } // // // return entryCompilation->compile(); return nullptr; } private: IFunctionUnit* __caller; IFunctionUnit* __callee; LLVMLayer* llvm; bool isInline() { // Symbol ret = Symbol{0, function->__entry}; // bool flagOnTheFly = SymbolAttachments::get(ret, false); //TODO consider inlining return false; } }; BasicCodeScopeUnit::BasicCodeScopeUnit(CodeScope* codeScope, IFunctionUnit* f, CompilePass* compilePass) : ICodeScopeUnit(codeScope, f, compilePass) { } llvm::Value* BasicCodeScopeUnit::processSymbol(const Symbol& s, std::string hintRetVar) { Expression declaration = CodeScope::getDefinition(s); CodeScope* scope = s.scope; ICodeScopeUnit* self = ICodeScopeUnit::function->getScopeUnit(scope); return self->process(declaration, hintRetVar); } //TASK Isolate out context functionalty in decorator //TOTEST static late context decisions //TOTEST dynamic late context decisions ICallStatement* BasicCodeScopeUnit::findFunction(const Expression& opCall) { const std::string& calleeName = opCall.getValueString(); LLVMLayer* llvm = pass->man->llvm; ClaspLayer* clasp = pass->man->clasp; DefaultFunctionUnit* function = dynamic_cast (this->function); context::ContextQuery* queryContext = pass->queryContext; const std::list& specializations = pass->man->root->getFunctionSpecializations(calleeName); //if no specializations registered - check external function if (specializations.size() == 0) { llvm::Function* external = llvm->layerExtern->lookupFunction(calleeName); llvm::outs() << "Debug/External function: " << calleeName; external->getType()->print(llvm::outs(), true); llvm::outs() << "\n"; return new CallStatementRaw(external, llvm); } //no decisions required if (specializations.size() == 1) { if (!specializations.front()->guardContext.isValid()) { return new CallStatementRaw(pass->getFunctionUnit(specializations.front())->compile(), llvm); } } //TODO move dictSpecialization over to a separate function in order to perform cache, etc. //prepare specializations dictionary std::map dictSpecializations; boost::optional variantDefault; boost::optional 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(string(atomSpecialization))), Expression(Operator::CALL, {Atom(string(calleeName))}), Atom(scopeCaller)}); const context::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 = 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) { IFunctionUnit* 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(), llvm); } //require default variant if no static decision made assert(variantDefault); llvm::Function* functionVariantDefault = this->pass->getFunctionUnit(*variantDefault)->compile(); llvm::Value* resultFn = function->contextCompiler.findFunction(calleeName, functionVariantDefault, scopeCaller); llvm::PointerType *resultPTy = cast(resultFn->getType()); llvm::FunctionType *resultFTy = cast(resultPTy->getElementType()); return new CallStatementRaw(resultFn, resultFTy, llvm); } //DISABLEDFEATURE transformations // if (pass->transformations->isAcceptable(expr)){ // return pass->transformations->transform(expr, result, ctx); // } llvm::Value* BasicCodeScopeUnit::process(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::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 = typeinference::doAutomaticTypeConversion(right, left->getType(), l.builder); break; default:; } switch (expr.op) { case Operator::ADD: { left = process(expr.operands[0]); Context context{this, function, pass}; llvm::Value* resultSU = StructUpdate::add(expr.operands[0], left, expr.operands[1], context, DEFAULT("tmp_add")); if (resultSU) return resultSU; right = process(expr.operands[1]); llvm::Value* resultAddPA = pointerarithmetic::PointerArithmetic::add(left, right, context, DEFAULT("tmp_add")); if (resultAddPA) { return resultAddPA; } 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); shared_ptr callee(findFunction(expr)); const std::string& nameCallee = expr.getValueString(); //prepare arguments std::vector 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); //TASK a) refactor CALL/ADHOC/find function //SECTIONTAG late-context propagation arg size_t calleeDemandSize = pass->queryContext->getFunctionDemand(nameCallee).size(); if (calleeDemandSize) { DefaultFunctionUnit* function = dynamic_cast (this->function); llvm::Value* argLateContext = function->contextCompiler.compileContextArgument(nameCallee, outerScopeId); args.push_back(argLateContext); } return (*callee)(move(args), DEFAULT("res_" + nameCallee)); } case Operator::IF: { return instructions.compileIf(expr, DEFAULT("tmp_if")); } case Operator::SWITCH: { return instructions.compileSwitch(expr, DEFAULT("tmp_switch")); } case Operator::LOOP_CONTEXT: { assert(false); return nullptr; //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.compileListAsSolidArray(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 ExpandedType; ExpandedType tyStructLiteral = l.ast->getType(expr); const std::vector fieldsFormal = (tyStructLiteral.get().__operator == TypeOperator::CUSTOM) ? l.layerExtern->getStructFields(l.layerExtern->lookupType(tyStructLiteral.get().__valueCustom)) : tyStructLiteral.get().fields; std::map indexFields; for (size_t i = 0, size = fieldsFormal.size(); i < size; ++i) { indexFields.emplace(fieldsFormal[i], i); } llvm::StructType* tyLiteralRaw = llvm::cast(l.toLLVMType(tyStructLiteral)); llvm::Value* record = llvm::UndefValue::get(tyLiteralRaw); 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 = process(operand); assert(result); record = l.builder.CreateInsertValue(record, result, llvm::ArrayRef({fieldId})); } return record; }; case Operator::MAP: { assert(expr.blocks.size()); return instructions.compileMapSolidOutput(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: { //TASK allow multiindex compilation assert(expr.operands.size() == 2); assert(expr.operands[0].__state == Expression::IDENT); const std::string& hintIdent = expr.operands[0].getValueString(); - Symbol s = Attachments::get(expr.operands[0]); + Symbol s = Attachments::get(expr.operands[0]); const ExpandedType& t2 = pass->man->root->getType(expr.operands[0]); llvm::Value* aggr = processSymbol(s, hintIdent); switch (t2.get().__operator) { case TypeOperator::STRUCT: case TypeOperator::CUSTOM: { std::string idxField; const Expression& idx = expr.operands.at(1); switch (idx.__state) { //named struct field case Expression::STRING: idxField = idx.getValueString(); break; //anonymous struct field case Expression::NUMBER: idxField = to_string((int) idx.getValueDouble()); break; default: assert(false && "Wrong index for a struct"); } return instructions.compileStructIndex(aggr, t2, idxField); }; case TypeOperator::ARRAY: { std::vector indexes; std::transform(++expr.operands.begin(), expr.operands.end(), std::inserter(indexes, indexes.end()), [this] (const Expression & op) { return process(op); } ); return instructions.compileArrayIndex(aggr, indexes, DEFAULT(string("el_") + hintIdent)); }; default: assert(false); } }; //SECTIONTAG adhoc actual compilation //TODO a) make sure that it's correct: function->adhocImplementation built for Entry scope and used in another scope case Operator::ADHOC: { DefaultFunctionUnit* function = dynamic_cast (this->function); assert(function->adhocImplementation && "Adhoc implementation not found"); const Expression& comm = adhoc::AdhocExpression(expr).getCommand(); CodeScope* scope = function->adhocImplementation->getCommandImplementation(comm); ICodeScopeUnit* unitScope = function->getScopeUnit(scope); //SECTIONTAG types/convert ADHOC ret convertation llvm::Type* resultTy = l.toLLVMType(pass->man->root->expandType(function->adhocImplementation->getResultType())); return typeinference::doAutomaticTypeConversion(unitScope->compile(), resultTy, l.builder); }; case Operator::CALL_INTRINSIC: { const std::string op = expr.getValueString(); if (op == "copy") { llvm::Value* result = process(expr.getOperands().at(0)); auto decoratorVersions = Decorators::getInterface(this); llvm::Value* storage = decoratorVersions->processIntrinsicInit(result->getType()); decoratorVersions->processIntrinsicCopy(result, storage); return l.builder.CreateLoad(storage, hintVarDecl); } assert(false && "undefined intrinsic"); } case Operator::VARIANT: { //TASK Variant compilation to implement assert(false&& "Variant compilation not implemented yet"); return nullptr; // const ExpandedType& typVariant = pass->man->root->getType(expr); // llvm::Type* typRaw = l.toLLVMType(typVariant); // int value = expr.getValueDouble(); // return llvm::ConstantInt::get(typRaw, value); } case Operator::NONE: assert(expr.__state != Expression::COMPOUND); switch (expr.__state) { case Expression::IDENT: { - Symbol s = Attachments::get(expr); + Symbol s = Attachments::get(expr); return processSymbol(s, expr.getValueString()); } case Expression::NUMBER: { llvm::Type* typConst; if (expr.type.isValid()) { typConst = l.toLLVMType(pass->man->root->getType(expr)); } else { typConst = llvm::Type::getInt32Ty(llvm::getGlobalContext()); } int literal = expr.getValueDouble(); return llvm::ConstantInt::get(typConst, literal); } case Expression::STRING: { return instructions.compileConstantStringAsPChar(expr.getValueString(), DEFAULT("tmp_str")); }; default: { break; } }; break; default: break; } assert(false && "Can't compile expression"); return 0; } llvm::Value* BasicCodeScopeUnit::compile(const std::string& hintBlockDecl) { if (!hintBlockDecl.empty()) { llvm::BasicBlock *block = llvm::BasicBlock::Create(llvm::getGlobalContext(), hintBlockDecl, function->raw); pass->man->llvm->builder.SetInsertPoint(block); } Symbol symbScope = Symbol{ScopedSymbol::RetSymbol, scope}; return processSymbol(symbScope); } ICodeScopeUnit::~ICodeScopeUnit() { } IFunctionUnit::~IFunctionUnit() { } llvm::Function* IFunctionUnit::compile() { if (raw != nullptr) return raw; LLVMLayer* llvm = pass->man->llvm; llvm::IRBuilder<>& builder = llvm->builder; string&& functionName = prepareName(); std::vector&& types = prepareArguments(); llvm::Type* expectedResultType = prepareResult(); llvm::FunctionType *ft = llvm::FunctionType::get(expectedResultType, types, false); raw = llvm::cast(llvm->module->getOrInsertFunction(functionName, ft)); prepareBindings(); const std::string&blockName = "entry"; llvm::BasicBlock* blockCurrent = builder.GetInsertBlock(); llvm::Value* result = getScopeUnit(function->__entry)->compile(blockName); assert(result); //SECTIONTAG types/convert function ret value builder.CreateRet(typeinference::doAutomaticTypeConversion(result, expectedResultType, llvm->builder)); if (blockCurrent) { builder.SetInsertPoint(blockCurrent); } llvm->moveToGarbage(ft); return raw; } ICodeScopeUnit* IFunctionUnit::getScopeUnit(CodeScope* scope) { if (__scopes.count(scope)) { auto result = __scopes.at(scope).lock(); if (result) { return result.get(); } } std::shared_ptr unit(pass->buildCodeScopeUnit(scope, this)); if (scope->__parent != nullptr) { auto parentUnit = Decorators::getInterface(getScopeUnit(scope->__parent)); parentUnit->registerChildScope(unit); } else { __orphanedScopes.push_back(unit); } if (!__scopes.emplace(scope, unit).second) { __scopes[scope] = unit; } return unit.get(); } ICodeScopeUnit* IFunctionUnit::getScopeUnit(ManagedScpPtr scope) { return getScopeUnit(&*scope); } ICodeScopeUnit* IFunctionUnit::getEntry() { return getScopeUnit(function->getEntryScope()); } template<> compilation::IFunctionUnit* CompilePassCustomDecorators::buildFunctionUnit(const ManagedFnPtr& function){ return new DefaultFunctionUnit(function, this); } template<> compilation::ICodeScopeUnit* CompilePassCustomDecorators::buildCodeScopeUnit(CodeScope* scope, IFunctionUnit* function){ return new DefaultCodeScopeUnit(scope, function, this); } } // emf of compilation IFunctionUnit* CompilePass::getFunctionUnit(const ManagedFnPtr& function) { unsigned int id = function.id(); if (!functions.count(id)) { IFunctionUnit* unit = buildFunctionUnit(function); functions.emplace(id, unit); return unit; } return functions.at(id); } void CompilePass::run() { managerTransformations = new TransformationsManager(); targetInterpretation = new interpretation::TargetInterpretation(this->man->root, this); queryContext = reinterpret_cast (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(model->first->second)); IFunctionUnit* 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 context::ContextQuery(), QueryId::ContextQuery); + + Attachments::init(); + clasp->registerQuery(new polymorph::PolymorphQuery(), QueryId::PolymorphQuery); } } //end of namespace xreate /** * \class xreate::CompilePass * \brief Encapsulates all compilation activities * * xreate::CompilePass iterates over xreate::AST tree and produces executable code fed by data(via xreate::Attachments) gathered by previous passes as well as data via queries(xreate::IQuery) from xreate:ClaspLayer reasoner. * Compilation's done using xreate::LLVMLayer(wrapper over LLVM toolchain) and based on following aspects: * - Containers support. See \ref compilation/containers.h * - Late Conext compilation. See xreate::context::LateContextCompiler2 * - Interpretation support. See xreate::interpretation::TargetInterpretation * - Loop saturation support. See xreate::compilation::TransformerSaturation * - External Code access. See xreate::ExternLayer(wrapper over Clang library) * * \section adaptability_sect Adaptability * xreate::CompilePass's architecture provides adaptability by employing: * - %Function Decorators to alter function-level compilation. See xreate::compilation::IFunctionUnit * - Code Block Decorators to alter code block level compilation. See xreate::compilation::ICodeScopeUnit * Default functionality defined by \ref xreate::compilation::DefaultCodeScopeUnit * - Targets to allow more versitile extensions. * Currently only xreate::interpretation::TargetInterpretation use Targets infrastructure. See xreate::compilation::Target * - %Altering Function invocation. xreate::compilation::ICallStatement * * Client able to construct compiler with desired decorators using xreate::compilation::CompilePassCustomDecorators. * As a handy alias, `CompilePassCustomDecorators` constructs default compiler * */ diff --git a/cpp/src/pass/dfapass.cpp b/cpp/src/pass/dfapass.cpp index 2186964..030a4ad 100644 --- a/cpp/src/pass/dfapass.cpp +++ b/cpp/src/pass/dfapass.cpp @@ -1,238 +1,204 @@ /* 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/. * * Author: pgess * * dfapass.cpp */ /** * \file dfapass.h * \brief Data Flow Analysis(DFA) */ //DEBT DFA represent VersionaPass in declarative form using applyDependencies // applyDependencies(expression, context, cache, decl); //DEBT DFA prepare static annotations and represent InterpretationPass in declarative form // applyStaticAnnotations(expression, context, cache, decl); //DEBT DFA Eliminate dfa schemes #include "pass/dfapass.h" -#include "analysis/dfagraph.h" - #include "xreatemanager.h" #include "clasplayer.h" #include +#include using namespace std; namespace xreate { namespace dfa { -class DfaExpressionProcessor { - std::vector operands; - std::vector blocks; - - const Expression expression; - SymbolNode result; - DFAPass * const pass; - const PassContext context; +DFAPass::DFAPass(PassManager* manager) + : AbstractPass(manager) + , graph{new DFAGraph()} + , clasp(manager->clasp) +{} -public: - DfaExpressionProcessor(const Expression& expr, SymbolNode resInitial, DFAPass * const p, const PassContext c) - : expression(expr), result(resInitial), pass(p), context(c) { +void +DFAPass::processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result){ + const string &nameCalleeFunction = expr.getValueString(); - operands.reserve(expression.getOperands().size()); - for (const Expression &op : expression.getOperands()) { - SymbolAnonymous symbOp(op.id); + //TODO implement processFnCall/Uncertain + list variantsCalleeFunction = man->root->getFunctionSpecializations(nameCalleeFunction); - operands.push_back(DfaExpressionProcessor(op, symbOp, pass, context).process()); - } + vector operands; + operands.reserve(expr.getOperands().size()); - blocks.reserve(expression.blocks.size()); - for (CodeScope* scope : expression.blocks) { - blocks.push_back(pass->process(scope, context)); - } + for (const Expression& arg: expr.getOperands()){ + operands.push_back(process(arg, context)); } - - SymbolNode - process() { - if (expression.__state == Expression::COMPOUND) { - processCompoundOp(); - - } else { - processElementaryOp(); + graph->markSymbolsAsUsed(operands); + graph->markSymbolsAsUsed(std::vector{result}); + + //Set calling relations: + for (ManagedFnPtr function: variantsCalleeFunction){ + CodeScope *scopeRemote = function->getEntryScope(); + DFACallInstance callInstance; + callInstance.id = expr.id; + callInstance.fnName = function->getName(); + + std::vector::const_iterator nodeActual = operands.begin(); + for (const std::string &identFormal: scopeRemote->__bindings){ + const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE}; + SymbolPacked symbolFormalPacked = clasp->pack(Symbol{symbolFormal, scopeRemote}); + callInstance.args.push_back(std::make_pair(symbolFormalPacked, *nodeActual)); + + graph->addDependency(symbolFormalPacked, *nodeActual); + ++nodeActual; } - applySignatureAnnotations(); - applyInPlaceAnnotations(); + callInstance.retActual = result; + SymbolNode retFormal = SymbolNode(clasp->pack(Symbol{ScopedSymbol::RetSymbol, scopeRemote})); + graph->addDependency(result, retFormal); - return result; + graph->addCallInstance(std::move(callInstance)); } -private: - void - processElementaryOp() { - switch (expression.__state) { - case Expression::IDENT: - { - SymbolPacked symbFrom = pass->processSymbol(Attachments::get(expression), context, expression.getValueString()); - - SymbolPacked* symbTo = boost::get(&result); - if (symbTo) { - pass->__context.graph->addConnection(*symbTo, SymbolNode(symbFrom), DFGConnection::STRONG); - } else { - result = SymbolNode(symbFrom); - } +} - break; - } +void +DFAPass::processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache){ + cache.operands.reserve(expression.getOperands().size()); + for (const Expression &op: expression.getOperands()) { + const SymbolNode& subnodeOperand = process(op, context); - default: break; - } + cache.operands.push_back(subnodeOperand); + graph->addDependency(node, subnodeOperand); } - void - processCompoundOp() { - switch (expression.op) { - - //DEBT provide CALL processing - // case Operator::CALL: { - // const string &nameCalleeFunction = expression.getValueString(); - // - // //TODO implement processFnCall/Uncertain - // list variantsCalleeFunction = man->root->getFunctionVariants(nameCalleeFunction); - // if (variantsCalleeFunction.size()!=1) return; - // ManagedFnPtr function= variantsCalleeFunction.front(); - // - // // set calling relations: - // CodeScope *scopeRemote = function->getEntryScope(); - // std::vector::iterator nodeActual = cache.operands.begin(); - // for (const std::string &identFormal: scopeRemote->__bindings){ - // const ScopedSymbol symbolFormal{scopeRemote->__identifiers.at(identFormal), versions::VERSION_NONE}; - // - // __context.graph->addConnection(clasp->pack(Symbol{symbolFormal, scopeRemote}, nameCalleeFunction + ":" + identFormal), *nodeActual, DFGConnection::WEAK); - // ++nodeActual; - // } - // - // //TODO add RET connection - // break; - // } - - //MAP processing: apply PROTOTYPE relation - case Operator::MAP: - { - SymbolNode nodeFrom = operands.front(); - - SymbolPacked* nodeTo = boost::get(&result); - assert(nodeTo); - - pass->__context.graph->addConnection(*nodeTo, nodeFrom, DFGConnection::PROTOTYPE); - break; - } + cache.blocks.reserve(expression.blocks.size()); + for (CodeScope* block : expression.blocks) { + const SymbolNode& subnodeBlock = process(block, context); - default: break; - } + cache.blocks.push_back(subnodeBlock); + graph->addDependency(node, subnodeBlock); } +} - void - applySignatureAnnotations() { - if (pass->__signatures.count(expression.op)) { - const Expression &scheme = pass->__signatures.at(expression.op); +SymbolNode +DFAPass::process(const Expression& expression, PassContext context, const std::string& varDecl) +{ + SymbolNode result; + if (Attachments::exists(expression)) + { + Symbol varSymbol = Attachments::get(expression); + result = clasp->pack(varSymbol, varDecl); + } else if (expression.__state == Expression::IDENT && expression.tags.size() == 0) { + Symbol varSymbol = Attachments::get(expression); + result = clasp->pack(varSymbol, expression.getValueString()); - std::vector::iterator arg = operands.begin(); - std::vector::const_iterator tag = scheme.getOperands().begin(); + } else { + result = SymbolAnonymous{expression.id}; + } - //Assign scheme RET annotation - Expression retTag = *scheme.getOperands().begin(); - if (retTag.__state != Expression::INVALID) { - pass->__context.graph->addAnnotation(result, move(retTag)); - } + graph->printInplaceAnnotations(result, expression); - ++tag; - while (tag != scheme.getOperands().end()) { - if (tag->__state != Expression::INVALID) { - pass->__context.graph->addAnnotation(*arg, Expression(*tag)); - } + switch(expression.__state){ + case Expression::COMPOUND: { + switch (expression.op) { + case Operator::CALL: + processCallInstance(expression, context, result); + break; - ++arg; - ++tag; + default: + ProcessingCache cache; + processDependencies(result, expression, context, cache); + break; } + break; + } + + case Expression::IDENT: { + SymbolNode symbIdent = AbstractPass::process(expression, context, varDecl); - // TODO add possibility to have specific signature for a particular function - // if (expression.op == Operator::CALL || expression.op == Operator::INDEX){ - // string caption = expression.getValueString(); - // operands.push_back(process(Expression(move(caption)), context, "")); - // } + if (!(result == symbIdent)) { + graph->addDependency(result, symbIdent); + graph->printAlias(result, symbIdent); + } + break; + } + case Expression::NUMBER: + case Expression::STRING: { + break; } - } - void - applyInPlaceAnnotations() { - // write down in-place expression tags: - for (pair tag : expression.tags) { - pass->__context.graph->addAnnotation(result, Expression(tag.second)); + case Expression::INVALID: + case Expression::BINDING: { + assert(false); + break; } } -}; + return result; +} -DFAPass::DFAPass(PassManager* manager) - : AbstractPass(manager) - , __context{new DFAGraph(manager->clasp)} - , clasp(manager->clasp) -{} +SymbolNode +DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl){ + for(const std::string& binding: scope->__bindings){ + Symbol bindingSymbol{scope->getSymbol(binding), scope}; + SymbolPacked bindingPacked = clasp->pack(bindingSymbol, binding); + + getSymbolCache().setCachedValue(bindingSymbol, SymbolNode(bindingPacked)); + } -SymbolPacked -DFAPass::process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl) { - const SymbolPacked& symbRet = AbstractPass::process(scope, context, hintBlockDecl); - return symbRet; + return AbstractPass::process(scope, context, hintBlockDecl); } -SymbolPacked -DFAPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol) { - const Expression& declaration = CodeScope::getDefinition(symbol); - const SymbolPacked& symbPacked = clasp->pack(symbol, hintSymbol); - DfaExpressionProcessor(declaration, symbPacked, this, context).process(); +SymbolNode +DFAPass::process(ManagedFnPtr function){ + SymbolNode result = AbstractPass::process(function); - return symbPacked; + graph->printFunctionRet(function, result); + return result; } void -DFAPass::run() { - init(); - return AbstractPass::run(); -} +DFAPass::finish(){ + clasp->registerReport(graph); -void -DFAPass::init() { - for (const Expression& scheme : man->root->__dfadata) { - __signatures.emplace(scheme.op, scheme); - } -} + //Declare symbols: + graph->printSymbols(clasp); -void -DFAPass::finish() { - clasp->setDFAData(move(__context.graph)); + AbstractPass::finish(); } } //end of namespace dfa template<> -SymbolPacked defaultValue(){ +dfa::SymbolNode defaultValue(){ assert(false); } } //xreate namespace /** * \class xreate::dfa::DFAPass * \details Provides DFA, important analysis for reasoning. Iterates over AST and stores collected data in DFAGraph */ diff --git a/cpp/src/pass/dfapass.h b/cpp/src/pass/dfapass.h index 2d4bd14..51b3eb1 100644 --- a/cpp/src/pass/dfapass.h +++ b/cpp/src/pass/dfapass.h @@ -1,50 +1,51 @@ /* 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/. * * Author: pgess * * dfapass.h * Data Flow Graph building pass */ #ifndef DFGPASS_H #define DFGPASS_H #include "abstractpass.h" #include "analysis/dfagraph.h" namespace xreate { class ClaspLayer; } namespace xreate { namespace dfa { -class DfaExpressionProcessor; - -/** \brief Data Flow Analysis Pass(%DFA) */ -class DFAPass: public AbstractPass { - friend class DfaExpressionProcessor; +struct ProcessingCache { + std::vector operands; + std::vector blocks; +}; + +/** \brief Data Flow Analysis Pass(%DFA) */ +class DFAPass: public AbstractPass { public: DFAPass(PassManager* manager); - SymbolPacked processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol="") override; - SymbolPacked process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl="") override; +protected: + virtual SymbolNode process(const Expression& expression, PassContext context, const std::string& varDecl="") override; + virtual SymbolNode process(CodeScope* scope, PassContext context, const std::string& hintBlockDecl="") override; + virtual SymbolNode process(ManagedFnPtr function) override; void init(); - void run() override; void finish() override; private: - struct - { - DFAGraph* graph; - } __context; + void processCallInstance(const Expression& expr, PassContext context, const SymbolNode& result); + void processDependencies(const SymbolNode& node, const Expression& expression, PassContext context, ProcessingCache& cache); - std::map __signatures; //DFA data for particular operators + DFAGraph* graph; ClaspLayer* clasp; }; }} //end of xreate::dfa namespace #endif diff --git a/cpp/src/pass/interpretationpass.cpp b/cpp/src/pass/interpretationpass.cpp index 61ec926..89bf2fc 100644 --- a/cpp/src/pass/interpretationpass.cpp +++ b/cpp/src/pass/interpretationpass.cpp @@ -1,522 +1,522 @@ /* 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 * * Created on July 5, 2016, 5:21 PM */ /** * \file interpretationpass.h * \brief Interpretation analysis: determines what parts of code could be interpreted */ #include "pass/interpretationpass.h" #include #include #include "ast.h" //DEBT implement InterpretationPass purely in clasp //DEBT represent InterpretationPass as general type inference using namespace std; namespace xreate { template<> interpretation::InterpretationResolution defaultValue() { return interpretation::CMPL_ONLY; } namespace interpretation { enum InterpretationQuery { QUERY_INTR_ONLY, QUERY_CMPL_ONLY }; namespace details { template 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& tags) { auto i=tags.find("interpretation"); 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=="force"){ return INTR_ONLY; } else if(cmd=="suppress"){ return CMPL_ONLY; } return ANY; } } InterpretationResolution unify(InterpretationResolution flag) { return flag; } template 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 bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); return details::checkConstraints(flag); } template bool checkConstraints(std::vector&& flags) { assert(flags.size()); InterpretationResolution flag=flags.front(); flags.pop_back(); if(details::checkConstraints(flag)){ return checkConstraints(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(e, tag); } InterpretationResolution recognizeTags(const ManagedFnPtr& f) { return details::recognizeTags(f->getTags()); } InterpretationPass::InterpretationPass(PassManager* manager) : AbstractPass(manager) { Attachments::init(); Attachments::init(); } void InterpretationPass::run() { ManagedFnPtr f=man->root->begin(); 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 op=NONE; switch(expression.__state) { case Expression::NUMBER: case Expression::STRING: { break; } case Expression::IDENT: { - resolution=Parent::processSymbol(Attachments::get(expression), context); + resolution=Parent::processSymbol(Attachments::get(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: { //TODO cope with static/dynamic context //TODO BUG here: if several variants they all are processed as CMPL careless of signature list 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 op=0, size=expression.operands.size(); op({flagCondition})){ op=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({flagInput})){ op=FOLD_INTERPRET_INPUT; flagInput=ANY; } resolution=unify(flagInput, flagAccumInit, flagBody); break; } case Operator::INDEX: { for(const Expression &op : expression.getOperands()) { resolution=unify(resolution, process(op, 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({flagHeaders})){ op=SWITCH_INTERPRET_CONDITION; flagHeaders=ANY; } //determine body resolutions resolution=flagHeaders; for(size_t size=expression.operands.size(), i=1; i({resolution})){ op=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: case Operator::LIST_NAMED: { for(const Expression &op : expression.getOperands()) { resolution=unify(resolution, process(op, 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 &op : expression.getOperands()) { process(op, context); } for(CodeScope* scope : expression.blocks) { Parent::process(scope, context); } break; } } InterpretationResolution resolutionExpected= Attachments::get(expression,{ANY, NONE}).resolution; resolution=unify(resolution, resolutionExpected); if(resolution!=resolutionExpected&&(op!=NONE||resolution==INTR_ONLY)){ Attachments::put(expression,{resolution, op}); } return resolution; } InterpretationResolution InterpretationPass::processFnCall(ManagedFnPtr function, PassContext context) { return process(function); } InterpretationResolution InterpretationPass::process(ManagedFnPtr function) { CodeScope* entry=function->getEntryScope(); std::vector 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__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(function)){ return Attachments::get(function); } FunctionInterpretationData&& data=recognizeSignature(function); Attachments::put(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__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 * * Encapsulates *Interpretation Analysis* to support [Interpretation Concept](/w/concepts/dfa) * * Recognizes program functions, expressions, instructions eligible for interpretation * and stores output in Attachments and Attachments * * There are number of instructions currently able to be interpreted: * - 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 those instructions are eligibile for *hybrid 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](/w/concepts/dfa) */ \ No newline at end of file diff --git a/cpp/src/pass/versionspass.cpp b/cpp/src/pass/versionspass.cpp index 7b2acf4..7071147 100644 --- a/cpp/src/pass/versionspass.cpp +++ b/cpp/src/pass/versionspass.cpp @@ -1,375 +1,375 @@ /* 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/. * * versionspass.cpp * * Author: pgess * Created on January 4, 2017, 3:13 PM */ /** \class xreate::versions::VersionsPass * Has two parts: * - Validates correctness of versioned variables with regard to variables lifespan * - Determines versioned variables computation order * \sa VersionsScopeDecorator, VersionsGraph, [Versions Concept](/w/concepts/versions) */ #include #include "pass/versionspass.h" namespace std{ std::size_t hash::operator()(xreate::versions::SymbolOrPlaceholder const& s) const {return std::hash()(s.symbol) + (s.flagEndOfLifePlaceholder? 9849 : 1);} bool equal_to::operator()(const xreate::versions::SymbolOrPlaceholder& __x, const xreate::versions::SymbolOrPlaceholder& __y) const { return __x.flagEndOfLifePlaceholder == __y.flagEndOfLifePlaceholder && __x.symbol == __y.symbol; } } using namespace std; namespace xreate{ namespace versions{ template<> std::list defaultValue>(){ return std::list(); }; inline std::string printSymbol(const SymbolOrPlaceholder& s){ switch(s.flagEndOfLifePlaceholder){ case SYMBOL: return string("(") + std::to_string(s.symbol.identifier.id) + ", "+ std::to_string(s.symbol.identifier.version) + ")"; case PLACEHOLDER: return string("(") + std::to_string(s.symbol.identifier.id) + ", "+ std::to_string(s.symbol.identifier.version) + ")+"; } return ""; } void VersionsGraph::__debug_print(std::ostream& output) const{ for(auto entry: __inferiors){ output << printSymbol(entry.second) << " <-" << printSymbol(entry.first) << "\n"; } } void VersionsGraph::defineEndOfLife(const Symbol& symbol, const Symbol& symbolSuccessor){ if(__dictSuccessors.count(symbol)){ assert("No version branches allowed yet" && false); } const SymbolOrPlaceholder& placeholder = getEndOfLife(symbol); auto inferiorsDeferred = __inferiors.equal_range(placeholder); std::unordered_multimap inferiorsReassigned; for (const auto& inf: boost::make_iterator_range(inferiorsDeferred)){ inferiorsReassigned.emplace(SymbolOrPlaceholder{SYMBOL, symbolSuccessor}, inf.second); } __inferiors.erase(placeholder); __inferiors.insert(inferiorsReassigned.begin(), inferiorsReassigned.end()); __inferiors.emplace(SymbolOrPlaceholder{SYMBOL, symbolSuccessor}, SymbolOrPlaceholder{SYMBOL, symbol}); __dictSuccessors.emplace(symbol, symbolSuccessor); } SymbolOrPlaceholder VersionsGraph::getEndOfLife(const Symbol& s){ if (__dictSuccessors.count(s)){ return SymbolOrPlaceholder{SYMBOL, __dictSuccessors.at(s)}; } return SymbolOrPlaceholder{PLACEHOLDER, s}; } void VersionsGraph::applyNatualDependencies(const Symbol& symbol, const std::list& dependencies){ for (const Symbol& right: dependencies){ __inferiorsNatural.emplace(symbol, right); } } void VersionsGraph::applyDependentEndOfLife(const SymbolOrPlaceholder& symbol, const list& dependencies){ for (const Symbol& right: dependencies){ auto rightEOF = getEndOfLife(right); __inferiors.emplace(rightEOF, symbol); } } bool VersionsGraph::tryEliminateEofAliases(const std::list& aliases){ if (aliases.size()==1){ return true; } boost::optional symbolActualEoF; for(const SymbolOrPlaceholder alias: aliases){ switch(alias.flagEndOfLifePlaceholder){ case SYMBOL: if(symbolActualEoF){ return false; } symbolActualEoF = alias.symbol; break; case PLACEHOLDER: continue; } } if(!symbolActualEoF){ return false; } for(const SymbolOrPlaceholder alias: aliases){ switch(alias.flagEndOfLifePlaceholder){ case SYMBOL: continue; case PLACEHOLDER: defineEndOfLife(alias.symbol, symbolActualEoF.get()); break; } } return true; } std::list VersionsGraph::extractCycle(const Path& path, const SymbolOrPlaceholder& symbolBeginning){ unsigned int posBeginning = path.at(symbolBeginning); std::list result; auto i=path.begin(); while(true){ i = std::find_if(i, path.end(), [&posBeginning](const auto& el){return el.second >=posBeginning;}); if (i!= path.end()){ result.push_back(i->first); ++i; } else {break; } } return result; } bool VersionsGraph::validateCycles(const SymbolOrPlaceholder& s, std::unordered_multimap& graph, std::unordered_set& symbolsVisited, Path& path) { if (symbolsVisited.count(s)) return true; symbolsVisited.insert(s); path.emplace(s, path.size()); if (graph.count(s)){ //iterate over imposed dependencies auto candidates = graph.equal_range(s); for (auto candidate = candidates.first; candidate != candidates.second; ++candidate){ if (path.count(candidate->second)) { std::list cycle = extractCycle(path, candidate->second); if (!tryEliminateEofAliases(cycle)) return false; continue; } if(!validateCycles(candidate->second, graph, symbolsVisited, path)) return false; } } //iterate over natural dependencies if (s.flagEndOfLifePlaceholder == SYMBOL) { auto candidates = __inferiorsNatural.equal_range(s.symbol); for (auto candidate = candidates.first; candidate != candidates.second; ++candidate){ if (path.count(SymbolOrPlaceholder{SYMBOL, candidate->second})){ return false; } if(!validateCycles(SymbolOrPlaceholder{SYMBOL,candidate->second}, graph, symbolsVisited, path)) return false; } } //check previous version if (s.flagEndOfLifePlaceholder == PLACEHOLDER){ const Symbol& candidate = s.symbol; if (path.count(SymbolOrPlaceholder{SYMBOL, candidate})){ std::list cycle = extractCycle(path, SymbolOrPlaceholder{SYMBOL, candidate}); if (!tryEliminateEofAliases(cycle)) return false; } if(!validateCycles(SymbolOrPlaceholder{SYMBOL,candidate}, graph, symbolsVisited, path)) return false; } path.erase(s); return true; } bool VersionsGraph::validateCycles(){ std::unordered_set symbolsVisited; Path path; std::unordered_multimap graph(__inferiors); std::unordered_multimap::const_iterator s; for (s = graph.begin(); s != graph.end(); ++s){ if(!validateCycles(s->first, graph, symbolsVisited, path)) return false; } return true; } bool VersionsGraph::validate(){ return validateCycles(); } std::list VersionsGraph::expandPlaceholder(const SymbolOrPlaceholder& symbol, const Symbol& symbolPrev) const{ std::list result; switch (symbol.flagEndOfLifePlaceholder){ case SYMBOL: //skip self-loops if (symbol.symbol == symbolPrev) return {}; return {symbol.symbol}; case PLACEHOLDER: for (const auto& entry: boost::make_iterator_range(__inferiors.equal_range(symbol))){ list&& childResult = expandPlaceholder(entry.second, symbolPrev); result.insert(result.end(), childResult.begin(), childResult.end()); } if (__dictSuccessors.count(symbol.symbol)){ Symbol knownSuccessor = __dictSuccessors.at(symbol.symbol); //skip alias loop if (knownSuccessor == symbolPrev) return {}; for (const auto& entry: boost::make_iterator_range(__inferiors.equal_range(SymbolOrPlaceholder{SYMBOL, knownSuccessor}))){ list&& childResult = expandPlaceholder(entry.second, knownSuccessor); result.insert(result.end(), childResult.begin(), childResult.end()); } } break; } return result; } AttachmentsContainerDefault>* VersionsGraph::representAsAttachments() const { AttachmentsContainerDefault>* container = new AttachmentsContainerDefault>(); std::map> containerData; for(const auto& entry: __inferiors){ if(entry.first.flagEndOfLifePlaceholder == PLACEHOLDER) continue; list& infs = containerData[entry.first.symbol]; list&& infsExpanded = expandPlaceholder(entry.second, entry.first.symbol); infs.insert(infs.begin(), infsExpanded.begin(), infsExpanded.end()); } for(const auto& entry: containerData){ container->put(entry.first, entry.second); } return container; } std::list VersionsPass::process(const Expression& expression, PassContext context, const std::string& hintSymbol){ if (expression.__state == Expression::COMPOUND){ std::list resultDependencies; for (const Expression &op: expression.getOperands()) { std::list deps = process(op, context); resultDependencies.insert(resultDependencies.end(), deps.begin(), deps.end()); } for (CodeScope* scope: expression.blocks) { std::list deps = Parent::process(scope, context); resultDependencies.insert(resultDependencies.end(), deps.begin(), deps.end()); } return resultDependencies; } if (expression.__state == Expression::IDENT){ - const Symbol symb = Attachments::get(expression); + const Symbol symb = Attachments::get(expression); return processSymbol(symb, context, expression.getValueString()); } return {}; } //TODO versions, check (declaration.isDefined()) before processing declaration list VersionsPass::processSymbol(const Symbol& symbol, PassContext context, const std::string& hintSymbol){ list result{symbol}; if (__symbolsVisited.exists(symbol)){ return result; } enum {MODE_ALIAS, MODE_COPY } mode = MODE_ALIAS; const Expression& declaration = CodeScope::getDefinition(symbol); if (declaration.op == Operator::CALL_INTRINSIC){ if (declaration.getValueString() == "copy"){ mode = MODE_COPY; } } if (symbol.identifier.version != VERSION_NONE){ mode = MODE_COPY; if (symbol.identifier.version > 0){ Symbol versionPrev = Symbol{ScopedSymbol{symbol.identifier.id, symbol.identifier.version-1}, symbol.scope}; __graph.defineEndOfLife(versionPrev, symbol); } } PassContext context2 = context.updateScope(symbol.scope); std::list dependencies = process(declaration, context2, hintSymbol); switch (mode) { case MODE_COPY: __graph.applyDependentEndOfLife(SymbolOrPlaceholder{SYMBOL, symbol}, dependencies); break; case MODE_ALIAS: __graph.applyDependentEndOfLife(__graph.getEndOfLife(symbol), dependencies); break; } __graph.applyNatualDependencies(symbol, dependencies); __symbolsVisited.put(symbol, true); return list{symbol}; } VersionsGraph& VersionsPass::getResultGraph(){ return __graph; } void VersionsPass::finish(){ assert(__graph.validate() && "Can't validate versions graph"); Attachments::init(__graph.representAsAttachments()); } }} //end of namespace xreate::versions diff --git a/cpp/src/query/polymorph.cpp b/cpp/src/query/polymorph.cpp new file mode 100644 index 0000000..fa4038b --- /dev/null +++ b/cpp/src/query/polymorph.cpp @@ -0,0 +1,34 @@ +/* + * 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: polymorph.cpp + * Author: pgess + * + * Created on November 9, 2017, 12:14 PM + */ + +#include "polymorph.h" + +using namespace std; +namespace xreate { namespace polymorph { + +void +PolymorphQuery::init(ClaspLayer* clasp){ + const std::string& atomGuard = "dfa_callguard"; + + ClaspLayer::ModelFragment query = clasp->query(atomGuard); + if (query){ + for (auto entry = query->first; entry!=query->second; ++entry){ + unsigned int callId; + Expression exprGuard; + + tie(callId, exprGuard)=ClaspLayer::parse(entry->second); + + Attachments::put(callId, exprGuard); + } + } +} + +}} //end of xreate::polymorph diff --git a/cpp/src/query/polymorph.h b/cpp/src/query/polymorph.h new file mode 100644 index 0000000..de67d35 --- /dev/null +++ b/cpp/src/query/polymorph.h @@ -0,0 +1,37 @@ +/* + * 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: polymorph.h + * Author: pgess + * + * Created on November 9, 2017, 12:14 PM + */ + +#ifndef POLYMORPHQUERY_H +#define POLYMORPHQUERY_H + +#include "clasplayer.h" + +namespace xreate { + struct PolymorphGuard{}; + + template<> + struct AttachmentsDict + { + typedef Expression Data; + static const unsigned int key = 10; + }; +} + +namespace xreate { namespace polymorph { +class PolymorphQuery: public IQuery { +public: + virtual void init(ClaspLayer* clasp) override; +}; + +}}//end of xreate::polymorph + +#endif /* POLYMORPHQUERY_H */ + diff --git a/cpp/tests/CMakeLists.txt b/cpp/tests/CMakeLists.txt index 46d37c2..f5211f9 100644 --- a/cpp/tests/CMakeLists.txt +++ b/cpp/tests/CMakeLists.txt @@ -1,53 +1,54 @@ cmake_minimum_required(VERSION 2.8.11) project(xreate-tests) find_package(GTest REQUIRED) INCLUDE_DIRECTORIES(${GTEST_INCLUDE_DIRS}) INCLUDE_DIRECTORIES("/usr/include/libxml2") INCLUDE_DIRECTORIES($) # TESTS #========================= FIND_PACKAGE (LLVM REQUIRED) message("LLVM_LIBRARY_DIRS: " ${LLVM_LIBRARY_DIRS}) link_directories(${LLVM_LIBRARY_DIRS}) set (LIBCLASP_PATH ${POTASSCO_PATH}/build/debug) link_directories(${LIBCLASP_PATH}) #aux_source_directory(. TEST_FILES) set(TEST_FILES + communication.cpp polymorph.cpp association.cpp main.cpp modules.cpp adhoc.cpp attachments.cpp ast.cpp cfa.cpp dfa.cpp compilation.cpp ExpressionSerializer.cpp externc.cpp context.cpp types.cpp vendorAPI/clangAPI.cpp vendorAPI/xml2.cpp vendorAPI/json.cpp containers.cpp context.cpp interpretation.cpp loops.cpp #supplemental/versions-algorithm-data_dependency.cpp effects-versions.cpp ) add_executable(${PROJECT_NAME} ${TEST_FILES}) target_link_libraries(${PROJECT_NAME} xreate ${GTEST_LIBRARIES} pthread xml2 gcov) add_custom_target (coverage COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/src/code-coverage.sh WORKING_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/cpp/tests/cfa.cpp b/cpp/tests/cfa.cpp index 13d9141..7681f73 100644 --- a/cpp/tests/cfa.cpp +++ b/cpp/tests/cfa.cpp @@ -1,122 +1,122 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ * * testsCFG.cpp * * Created on: Jul 17, 2015 * Author: pgess */ #include "xreatemanager.h" #include "pass/dfapass.h" #include "pass/cfapass.h" #include "analysis/DominatorsTreeAnalysisProvider.h" #include "gtest/gtest.h" #include #include using namespace xreate; using namespace xreate::cfa; using namespace std; TEST(CFA, testFunctionAnnotationsClasp){ string&& program = "f2 = function::int; annotationF2 {\n" " 0\n" "}\n" "\n" "f1 = function:: int; entry; annotationF1 {\n" " f2() + 10\n" "}"; details::tier1::XreateManager* man = details::tier1::XreateManager::prepare(move(program)); man->analyse(); ClaspLayer::ModelFragment answer = man->clasp->query("annotationF1"); int countNoneValue = 0; if (answer) countNoneValue = std::distance(answer->first, answer->second); EXPECT_EQ(1, countNoneValue); answer = man->clasp->query("annotationF2"); countNoneValue = 0; if (answer) countNoneValue = std::distance(answer->first, answer->second); EXPECT_EQ(1, countNoneValue); } TEST(CFA, testLoopContextExists){ details::tier1::XreateManager* man = details::tier1::XreateManager::prepare ( "interface(cfa){\n" " operator fold:: annotation1.\n" "}\n" "\n" "main = function:: int; entry {\n" " x = [1..10]:: [int].\n" " sum = loop fold (x->el:: int, 0->sum):: int {\n" " el + sum + f1()\n" " }. \n" " sum\n" "}" "case context:: annotation1 {" " f1 = function::int {\n" " x = 0:: int. " " x\n" " }" "}" ); man->analyse(); ClaspLayer::ModelFragment model = man->clasp->query("annotation1"); ScopePacked scopeIdActual = std::get<0>(ClaspLayer::parse(model->first->second)); CodeScope* scopeEntry = man->root->findFunction("main")->getEntryScope(); const Expression& exprSum = scopeEntry->getDefinition(scopeEntry->getSymbol("sum")); CodeScope* scopeExpected = exprSum.blocks.front(); ScopePacked scopeIdExpected = man->clasp->pack(scopeExpected); ASSERT_EQ(scopeIdExpected, scopeIdActual); } TEST(CFA, CFGRoots){ - std::string program = -R"CODE( - main = function::int{a()+ b()} - a= function::int {c1() + c2()} - b= function::int {c2()} - c1=function::int{0} - c2=function::int{0} -)CODE"; - - boost::scoped_ptr manager - (XreateManager::prepare(move(program))); - - manager->registerPass(new CFAPass(manager.get()) , PassId::CFGPass); - manager->executePasses(); - manager->clasp->run(); - - dominators::DominatorsTreeAnalysisProvider domProvider; - domProvider.run(manager->clasp); - - dominators::DominatorsTreeAnalysisProvider::Dominators expectedFDom= { - {0, {0, 9}} - ,{1, {1, 4}} - ,{2, {7, 8}} - ,{3, {2, 3}} - ,{4, {5, 6}} - }; - - dominators::DominatorsTreeAnalysisProvider::Dominators expectedPostDom= { - {0, {5, 6}} - ,{1, {3, 4}} - ,{2, {8, 9}} - ,{3, {1, 2}} - ,{4, {7, 10}} - }; - - ASSERT_EQ(expectedFDom, domProvider.getForwardDominators()); - ASSERT_EQ(expectedPostDom, domProvider.getPostDominators()); +// std::string program = +//R"CODE( +// main = function::int{a()+ b()} +// a= function::int {c1() + c2()} +// b= function::int {c2()} +// c1=function::int{0} +// c2=function::int{0} +//)CODE"; +// +// boost::scoped_ptr manager +// (XreateManager::prepare(move(program))); +// +// manager->registerPass(new CFAPass(manager.get()) , PassId::CFGPass); +// manager->executePasses(); +// manager->clasp->run(); +// +// dominators::DominatorsTreeAnalysisProvider domProvider; +// domProvider.run(manager->clasp); +// +// dominators::DominatorsTreeAnalysisProvider::Dominators expectedFDom= { +// {0, {0, 9}} +// ,{1, {1, 4}} +// ,{2, {7, 8}} +// ,{3, {2, 3}} +// ,{4, {5, 6}} +// }; +// +// dominators::DominatorsTreeAnalysisProvider::Dominators expectedPostDom= { +// {0, {5, 6}} +// ,{1, {3, 4}} +// ,{2, {8, 9}} +// ,{3, {1, 2}} +// ,{4, {7, 10}} +// }; +// +// ASSERT_EQ(expectedFDom, domProvider.getForwardDominators()); +// ASSERT_EQ(expectedPostDom, domProvider.getPostDominators()); } diff --git a/cpp/tests/communication.cpp b/cpp/tests/communication.cpp new file mode 100644 index 0000000..0b89e90 --- /dev/null +++ b/cpp/tests/communication.cpp @@ -0,0 +1,51 @@ +/* Any copyright is dedicated to the Public Domain. + * http://creativecommons.org/publicdomain/zero/1.0/ + * + * communication.cpp + * + * Author: pgess + * Created on October 14, 2017, 5:24 PM + */ + +#include "xreatemanager.h" +#include "gtest/gtest.h" +using namespace xreate; + +TEST(Communication, ast1){ + FILE* script = fopen("scripts/effects-communication/example1-wr.xreate", "r"); + std::unique_ptr program(XreateManager::prepare(script)); + + ASSERT_TRUE(true); + fclose(script); +} + +TEST(Communication, Analysis1){ + FILE* script = fopen("scripts/effects-communication/example1-wr.xreate", "r"); + std::unique_ptr program(details::tier1::XreateManager::prepare(script)); + fclose(script); + + program->analyse(); + ASSERT_TRUE(true); +} + +TEST(Communication, FullOnlyDirect1){ + FILE* script = fopen("scripts/effects-communication/example1-wr.xreate", "r"); + std::unique_ptr program(XreateManager::prepare(script)); + fclose(script); + + int (*programEntry)() = (int (*)())program->run(); + int result = programEntry(); + + ASSERT_EQ(1, result); +} + +TEST(Communication, FullDirectAndGuarded1){ + FILE* script = fopen("scripts/effects-communication/example2-wr.xreate", "r"); + std::unique_ptr program(XreateManager::prepare(script)); + fclose(script); + + int (*programEntry)() = (int (*)())program->run(); + int result = programEntry(); + + ASSERT_EQ(1, result); +} \ No newline at end of file diff --git a/cpp/tests/dfa.cpp b/cpp/tests/dfa.cpp index 8396abc..a695b02 100644 --- a/cpp/tests/dfa.cpp +++ b/cpp/tests/dfa.cpp @@ -1,16 +1,32 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ - * + * * DFGtests.cpp * * Created on: Jul 23, 2015 * Author: pgess */ #include "xreatemanager.h" #include "pass/dfapass.h" #include "gtest/gtest.h" using namespace xreate; using namespace std; //DEBT dfa tests: dfa scheme, dfa scheme + return value annoation (example: script/testpass/containers...) + +TEST(DFA, CallInstance1) { + std::string program= + R"CODE( + main = function::int{ + a(5, 8) + } + + a= function(x::int, y::int)::int;entry { x + y} +)CODE"; + + std::unique_ptr manager + (XreateManager::prepare(move(program))); + + manager->run(); +} \ No newline at end of file diff --git a/cpp/tests/main.cpp b/cpp/tests/main.cpp index c45667e..5186ff6 100644 --- a/cpp/tests/main.cpp +++ b/cpp/tests/main.cpp @@ -1,23 +1,23 @@ /* Any copyright is dedicated to the Public Domain. * http://creativecommons.org/publicdomain/zero/1.0/ - * + * * main.cpp * * Created on: - * Author: pgess */ #include "utils.h" #include using namespace std; using namespace xreate; int main(int argc, char **argv) { testing::GTEST_FLAG(color) = "yes"; string testsTemplate = Config::get("tests.template"); string testsFilter = Config::get(string("tests.templates.") + testsTemplate); testing::GTEST_FLAG(filter) = testsFilter; testing::InitGoogleTest(&argc, argv); return RUN_ALL_TESTS(); } diff --git a/grammar/xreate.ATG b/grammar/xreate.ATG index 012ace5..e9b8f81 100644 --- a/grammar/xreate.ATG +++ b/grammar/xreate.ATG @@ -1,658 +1,660 @@ //TODO add ListLiteral //TODO ExprTyped: assign default(none) type #include "ast.h" #include "ExternLayer.h" #include "pass/adhocpass.h" #include #include #define wprintf(format, ...) \ char __buffer[100]; \ wcstombs(__buffer, format, 100); \ fprintf(stderr, __buffer, __VA_ARGS__) using namespace std; COMPILER Xreate details::inconsistent::AST* root = nullptr; // current program unit void ensureInitalizedAST(){ if (root == nullptr) root = new details::inconsistent::AST(); } struct { std::stack scopesOld; CodeScope* scope = nullptr; } context; void pushContextScope(CodeScope* scope){ context.scopesOld.push(context.scope); context.scope = scope; } void popContextScope(){ context.scope = context.scopesOld.top(); context.scopesOld.pop(); } int nextToken() { scanner->ResetPeek(); return scanner->Peek()->kind; } bool checkTokenAfterIdent(int key){ if (la->kind != _ident) return false; return nextToken() == key; } bool checkParametersList() { return la->kind == _ident && nextToken() == _lparen; } bool checkInfix() { return la->kind == _ident && nextToken() == _ident; } bool checkIndex() { return la->kind == _ident && nextToken() == _lbrack; } bool checkFuncDecl() { if (la->kind != _ident) return false; int token2 = nextToken(); int token3 = scanner->Peek()->kind; return token2 == _assign && (token3 == _function || token3 == _pre); } bool checkAssignment() { if (la->kind != _ident) return false; scanner->ResetPeek(); int token2 = scanner->Peek()->kind; if (token2 == _lcurbrack) { scanner->Peek(); int token3 = scanner->Peek()->kind; if (token3 != _rcurbrack) return false; int token4 = scanner->Peek()->kind; return token4 == _assign; } return token2 == _assign; } void recognizeIdentifier(Expression& i){ if (!context.scope->recognizeIdentifier(i)){ root->postponeIdentifier(context.scope, i); } } enum SwitchKind{SWITCH_NORMAL, SWITCH_META}; CHARACTERS letter = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz". any = ANY - '"'. digit = "0123456789". cr = '\r'. lf = '\n'. tab = '\t'. TOKENS ident = (letter | '_') {letter | digit | '_'}. number = (digit | '-' digit) {digit}. string = '"' { any } '"'. function = "function". pre = "pre". lparen = '('. rparen = ')'. lbrack = '['. rbrack = ']'. lcurbrack = '{'. rcurbrack = '}'. equal = "==". assign = '='. implic = '-' '>'. colon = ':'. context = "context". tagcolon = "::". lse = "<=". lss = "<". gte = ">=". gtr = ">". ne1 = "!=". ne2= "<>". COMMENTS FROM "/*" TO "*/" NESTED COMMENTS FROM "//" TO lf IGNORE cr + lf + tab PRODUCTIONS Xreate = (. Function* function; ensureInitalizedAST(); .) {( RuleDecl | InterfaceData | Imprt | ContextSection | GuardSection | IF(checkFuncDecl()) FDecl (. root->add(function); .) | TDecl | SkipModulesSection )} (. .) . Ident = ident (. name = t->val; .). VarIdent = ident (. e = Expression(Atom(t->val)); .) [ lcurbrack ( ident (. SemErr(coco_string_create("var version as ident is not implemented yet")); .) | number (. Attachments::put(e, Atom(t->val).get()); .) ) rcurbrack ] . FDecl = (. std::wstring fname; std::wstring argName; TypeAnnotation typIn; TypeAnnotation typOut; bool flagIsPrefunct = false; Expression binding; .) Ident assign [pre (. flagIsPrefunct = true; .)] function (. f = new Function(fname); f->isPrefunction = flagIsPrefunct; CodeScope* entry = f->getEntryScope(); .) ['(' Ident tagcolon ExprAnnotations (. f->addBinding(Atom(argName), move(binding)); .) {',' Ident tagcolon ExprAnnotations (. f->addBinding(Atom (argName), move(binding));.) } ')'] [ tagcolon ( IF(flagIsPrefunct) FnTag | Type ) {';' FnTag }] BDecl (. entry->getBody().bindType(move(typOut));.) . ContextSection<>= (. Expression context; Function* f; .) "case" "context" tagcolon MetaSimpExpr lcurbrack { FDecl (. f->guardContext = context; root->add(f); .) } rcurbrack. GuardSection<>= (. Expression guard; Function* f; .) "guard" tagcolon MetaSimpExpr lcurbrack { FDecl (. f->guard = guard; root->add(f); .) } rcurbrack. /** * TYPES * */ TypeTerm = (. std::wstring tid; .) ("string" (. typ = TypePrimitive::String;.) | "num" (. typ = TypePrimitive::Num;.) | "int" (. typ = TypePrimitive::Int;.) | "float" (. typ = TypePrimitive::Float;.) | "bool" (. typ = TypePrimitive::Bool; .) | "i8" (. typ = TypePrimitive::I8; .) | "i32" (. typ = TypePrimitive::I32; .) | "i64" (. typ = TypePrimitive::I64; .) ). Type = (. TypeAnnotation typ2; TypePrimitive typ3; std::wstring tid, field; .) ( TList | TStruct | TVariant | TypeTerm (. typ = typ3; .) | IF (checkIndex()) Ident lbrack Ident (. typ = TypeAnnotation(TypeOperator::ACCESS, {}); typ.__valueCustom = Atom(tid).get(); typ.fields.push_back(Atom(field).get()); .) {',' Ident (. typ.fields.push_back(Atom(field).get()); .) } rbrack | Ident (. typ = TypeAnnotation(TypeOperator::CUSTOM, {}); typ.__valueCustom = Atom(tid).get(); .) ['(' Type (. typ.__operator = TypeOperator::CALL; typ.__operands.push_back(typ2); .) {',' Type (. typ.__operands.push_back(typ2); .) } ')'] ) . TList = (. TypeAnnotation ty; .) '[' Type ']' (. typ = TypeAnnotation(TypeOperator::ARRAY, {ty}); .) . TStruct = (. TypeAnnotation t; std::wstring key; size_t keyCounter=0; .) lcurbrack ( IF(checkTokenAfterIdent(_tagcolon)) Ident tagcolon Type | Type (. key = to_wstring(keyCounter++); .) ) (. typ = TypeAnnotation(TypeOperator::STRUCT, {t}); typ.fields.push_back(Atom(key).get()); .) {',' ( IF(checkTokenAfterIdent(_tagcolon)) Ident tagcolon Type | Type (. key = to_wstring(keyCounter++); .) ) (. typ.__operands.push_back(t); typ.fields.push_back(Atom(key).get()); .) } rcurbrack. TVariant= (. TypeAnnotation t, typVoid(TypeOperator::STRUCT, {}); std::vector operands; std::vector> keys; std::wstring variant; .) "variant" lcurbrack Ident (. t=typVoid; .) [tagcolon Type] (. keys.push_back(Atom(variant)); operands.push_back(t); .) {',' Ident (. t=typVoid; .) [tagcolon Type] (. keys.push_back(Atom(variant)); operands.push_back(t); .) } rcurbrack (. typ = TypeAnnotation(TypeOperator::VARIANT, {}); typ.__operands = operands; typ.addFields(std::move(keys)); .) . TDecl = (. TypeAnnotation t; std::wstring tname, arg; std::vector> args; .) Ident assign "type" ['(' Ident (. args.push_back(Atom(arg)); .) {',' Ident (. args.push_back(Atom(arg)); .) } ')'] Type'.' (. t.addBindings(move(args)); root->add(move(t), Atom(tname)); .) . ContextDecl = (. Expression tag; .) context tagcolon MetaSimpExpr (. scope->tags.push_back(tag); .) {';' MetaSimpExpr (. scope->tags.push_back(tag); .) }. VDecl = (. std::wstring vname; Expression var, value;.) - VarIdent assign ExprTyped (. f->addDefinition(move(var), move(value)); .) + VarIdent assign ExprTyped (. Symbol identSymbol = f->addDefinition(move(var), move(value)); + Attachments::put(value, identSymbol); + .) . BDecl = lcurbrack (. Expression body; pushContextScope(scope); .) {(IF(checkAssignment()) VDecl '.' | RuleContextDecl | ContextDecl'.' - | ExprTyped (. scope->setBody(body); .) + | ExprTyped (. scope->setBody(body); Attachments::put(body, Symbol{ScopedSymbol::RetSymbol, scope});.) )} rcurbrack (. popContextScope(); .) . IfDecl = (. Expression cond; ManagedScpPtr blockTrue = root->add(new CodeScope(context.scope)); ManagedScpPtr blockFalse = root->add(new CodeScope(context.scope)); .) "if" '(' Expr ')' (. e = Expression(Operator::IF, {cond}); .) tagcolon ExprAnnotations BDecl<&*blockTrue> "else" BDecl<&*blockFalse> (. e.addBlock(blockTrue); e.addBlock(blockFalse); .) . LoopDecl = (. Expression eIn, eAcc, eFilters; std::wstring varEl, varAcc, contextClass; Expression tagsEl; ManagedScpPtr block = root->add(new CodeScope(context.scope)); .) "loop" ("map" '(' Expr implic Ident (. e = Expression(Operator::MAP, {eIn}); .) tagcolon ExprAnnotations ')' tagcolon ExprAnnotations (. e.addBindings({Atom(varEl)}); block->addBinding(Atom(varEl), move(tagsEl)); .) BDecl<&*block> (. e.addBlock(block); .) |"fold" ("inf" '(' Expr implic Ident ')' (. e = Expression(Operator::FOLD_INF, {eAcc}); e.addBindings({Atom(varAcc)}); block->addBinding(Atom(varAcc), Expression()); .) tagcolon ExprAnnotations BDecl<&*block> (. e.addBlock(block); .) | '(' Expr implic Ident tagcolon ExprAnnotations ['|' Expr ] ',' Expr implic Ident')' (. e = Expression(Operator::FOLD, {eIn, eAcc}); e.addBindings({Atom(varEl), Atom(varAcc)}); .) tagcolon ExprAnnotations (. block->addBinding(Atom(varEl), move(tagsEl)); block->addBinding(Atom(varAcc), Expression()); .) BDecl<&*block> (. e.addBlock(block); .) ) | "context" '(' string (. contextClass = t->val; .) ')' BDecl<&*block> (. e = Expression(Operator::LOOP_CONTEXT, {Expression(Atom(std::move(contextClass)))}); e.addBlock(block); .) ). // Switches SwitchDecl = (. TypeAnnotation typ; eSwitch = Expression(Operator::SWITCH, {}); Expression eCondition; Expression tag;.) "switch" ( SwitchVariantDecl | lparen ExprTyped rparen tagcolon ExprAnnotations (. eSwitch.operands.push_back(eCondition);.) CaseDecl {CaseDecl} ) . CaseDecl = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); Expression condition; .) "case" ( IF(flagSwitchKind == SWITCH_META) lparen MetaSimpExpr rparen BDecl<&*scope> (. Expression exprCase(Operator::CASE, {}); exprCase.addTags({condition}); exprCase.addBlock(scope); outer.addArg(move(exprCase));.) | "default" BDecl<&*scope> (. Expression exprCase(Operator::CASE_DEFAULT, {}); exprCase.addBlock(scope); outer.operands.insert(++outer.operands.begin(), exprCase); .) | lparen CaseParams<&*scope> rparen (. ManagedScpPtr scopeBody = root->add(new CodeScope(&*scope)); Expression exprCase(Operator::CASE, {}); .) BDecl<&*scopeBody> (. exprCase.addBlock(scope); exprCase.addBlock(scopeBody); outer.addArg(move(exprCase)); .) ). CaseParams = (. Expression condition; Expression guard(Operator::LOGIC_AND, {}); pushContextScope(scope); .) ExprTyped (. guard.addArg(Expression(condition)); .) {',' ExprTyped (. guard.addArg(Expression(condition)); .) } (. scope->setBody(guard); popContextScope(); .) . SwitchVariantDecl = (. Expression varTested; std::wstring varAlias; bool flagAliasFound = false; expr = Expression(Operator::SWITCH_VARIANT, {}); .) "variant" lparen Expr [implic Ident (. flagAliasFound = true; .) ] [tagcolon ExprAnnotations] rparen tagcolon ExprAnnotations (. expr.addArg(std::move(varTested)); if (flagAliasFound) { expr.addBindings({Atom(varAlias)}); } else { if(varTested.__state != Expression::IDENT){ SemErr(coco_string_create("Switch variant expects identifier")); } expr.addBindings({Atom(string(varTested.getValueString()))}); } .) CaseVariantDecl {CaseVariantDecl} . CaseVariantDecl = (. ManagedScpPtr scope = root->add(new CodeScope(context.scope)); std::wstring key; scope->addBinding(Atom(string(expr.bindings.front())), Expression()); .) "case" lparen Ident rparen (. expr.addArg(root->recognizeVariantConstructor(Atom(std::move(key)))); .) BDecl<&*scope> (. expr.addBlock(scope); .) . IntrinsicDecl= (. std::wstring name; .) "intrinsic" Ident< name> (. outer = Expression(Operator::CALL_INTRINSIC, {}); outer.setValue(Atom(name)); .) lparen [CalleeParams] rparen . /*============================ INTERFACES ===============================*/ Imprt<> = "import" "raw" lparen string (. root->__rawImports.push_back(Atom(t->val).get()); .) rparen '.'. InterfaceData<> = "interface" '(' ( "dfa" ')' InterfaceDFA | "extern-c" ')' InterfaceExternC | "cfa" ')' InterfaceCFA | "adhoc" ')' InterfaceAdhoc ). InterfaceAdhoc<> = '{' { PrefunctionSchemeDecl } '}'. PrefunctionSchemeDecl<> = (. TypeAnnotation typReturn; std::wstring prefName; Expression exprCases; .) pre function Ident tagcolon Type lcurbrack SwitchDecl rcurbrack (. Expression prefData(Operator::CALL, {Atom(prefName), exprCases}); prefData.bindType(typReturn); root->addInterfaceData(Adhoc, move(prefData)); .). InterfaceExternC<> = (. ExternData data; .) '{' {IncludeExternDecl | LibExternDecl } '}' (. root->addExternData(move(data)); .) . LibExternDecl = (. std::wstring pkgname, libname; .) Ident assign "library" tagcolon "pkgconfig" '(' string (. pkgname = t->val; .) ')' '.' (. data.addLibrary(Atom(libname), Atom(pkgname)); .) . IncludeExternDecl = (. Expression inc; .) "include" StructLiteral '.' (. data.addIncludeDecl(move(inc)); .) . InterfaceDFA<> = '{' { InstructDecl } '}' . InstructDecl = (.Operator op; Expression tag; Expression scheme; std::vector& tags = scheme.operands; tags.push_back(Expression()); /* return value */ .) "operator" InstructAlias tagcolon '(' (.scheme.setOp(op); .) [ MetaSimpExpr (. tags.push_back(tag); .) { ',' MetaSimpExpr (. tags.push_back(tag); .) } ] ')' [ implic MetaSimpExpr (. tags[0] = tag; .) ] (. root->addDFAData(move(scheme)); .) '.'. InstructAlias = ( "map" (. op = Operator::MAP; .) | "list_range" (. op = Operator::LIST_RANGE; .) | "list" (. op = Operator::LIST; .) | "fold" (. op = Operator::FOLD; .) | "index" (. op = Operator::INDEX; .) ). InterfaceCFA<> = '{' { InstructCFADecl } '}' . InstructCFADecl<> = (.Operator op; Expression tag; Expression scheme; std::vector& tags = scheme.operands; .) "operator" InstructAlias tagcolon (. scheme.setOp(op); .) [ MetaSimpExpr (. tags.push_back(tag); .) { ',' MetaSimpExpr (. tags.push_back(tag); .) } ] '.' (. root->addInterfaceData(CFA, move(scheme)); .). /*============================ METAPROGRAMMING ===============================*/ // TagsDecl = (. Expression tag; TagModifier mod = TagModifier::NONE; .) // ':' { MetaSimpExpr (. /*f.addTag(std::move(tag), mod); */ .) // }. FnTag = (. Expression tag; TagModifier mod = TagModifier::NONE; .) MetaSimpExpr ['-' TagMod] (. f->addTag(std::move(tag), mod); .). TagMod = ( "assert" (. mod = TagModifier::ASSERT; .) | "require" (. mod = TagModifier::REQUIRE; .) ). RuleDecl<> = "rule" tagcolon (. RuleArguments args; RuleGuards guards; DomainAnnotation typ; std::wstring arg; .) '(' Ident tagcolon Domain (. args.add(arg, typ); .) {',' Ident tagcolon Domain (. args.add(arg, typ); .) } ')' ["case" RGuard {',' RGuard}] '{' RBody '}' . /* - TODO use RGuard for guards-*/ RuleContextDecl = (.Expression eHead, eGuards, eBody; .) "rule" "context" tagcolon MetaSimpExpr "case" lparen MetaSimpExpr rparen '{' MetaSimpExpr '}' (.scope->contextRules.push_back(Expression(Operator::CONTEXT_RULE, {eHead, eGuards, eBody})); .). Domain = ( "function" (. dom = DomainAnnotation::FUNCTION; .) | "variable" (. dom = DomainAnnotation::VARIABLE; .) ). RGuard= (. Expression e; .) MetaExpr (. guards.add(std::move(e)); .). MetaExpr= (.Operator op; Expression e2; .) MetaExpr2 [MetaOp MetaExpr2 (. e = Expression(op, {e, e2}); .) ]. MetaExpr2= ( '(' MetaExpr ')' | MetaSimpExpr ). MetaSimpExpr= (. std::wstring i1, infix; Expression e2; .) ( '-' MetaSimpExpr (. e = Expression(Operator::NEG, {e2}); .) | IF(checkParametersList()) Ident (. e = Expression(Operator::CALL, {Expression(Atom(i1))}); .) '(' [ MetaCalleeParams ] ')' | IF(checkInfix()) Ident Ident MetaSimpExpr (. e = Expression(Operator::CALL, {Expression(Atom(infix))}); e.addArg(Expression(Atom(i1))); e.addArg(std::move(e2)); .) | Ident (. e = Expression(Operator::CALL, {Atom(i1)}); .) ). MetaCalleeParams = (. Expression e2; .) MetaSimpExpr (. e.addArg(Expression(e2)); .) {',' MetaSimpExpr (. e.addArg(Expression(e2)); .) }. RBody = (. Expression e; std::wstring msg; .) "warning" MetaExpr ["message" string (. msg = t->val; .) ] (. root->add(new RuleWarning(RuleArguments(args), RuleGuards(guards), std::move(e), Atom(msg))); .) . MetaOp< Operator& op> = implic (. op = Operator::IMPL; .) . /*============================ Expressions ===============================*/ ExprAnnotations = (. TypeAnnotation typ; std::list tags; Expression tag; e.tags.clear();.) Type (. e.bindType(move(typ)); .) {';' MetaSimpExpr (. tags.push_back(tag); .) } (. e.addTags(tags); .) . ExprTyped = Expr [tagcolon ExprAnnotations]. Expr< Expression& e> (. Operator op; Expression e2; .) = ExprArithmAdd [ RelOp ExprArithmAdd (. e = Expression(op, {e, e2}); .) ]. ExprArithmAdd< Expression& e>= (. Operator op; Expression e2; .) ExprArithmMul< e> [ AddOp< op> ExprArithmAdd< e2> (. e = Expression(op, {e, e2});.) ]. ExprArithmMul< Expression& e> (. Operator op; Expression e2; .) = ExprPostfix< e> [ MulOp< op> ExprArithmMul< e2> (. e = Expression(op, {e, e2}); .) ]. ExprPostfix = Term [ (. e = Expression(Operator::INDEX, {e}); .) {lbrack CalleeParams rbrack } ]. Term< Expression& e> (. std::wstring name; e = Expression(); .) = (IF (checkParametersList()) Ident< name> (. e = Expression(Operator::CALL, {Atom(name)}); root->recognizeVariantConstructor(e); .) '(' [CalleeParams] ')' | VarIdent (. recognizeIdentifier(e); .) | ListLiteral (. /* tuple */.) | StructLiteral (. /* struct */.) | LoopDecl | IfDecl | SwitchDecl | AdhocDecl | IntrinsicDecl | number (. e = Expression(Atom(t->val)); .) | string (. e = Expression(Atom(t->val)); .) | "true" (. e = Expression(Atom(1)); e.bindType(TypePrimitive::Bool); .) | "false" (. e = Expression(Atom(0)); e.bindType(TypePrimitive::Bool); .) | '-' Term (. e = Expression(Operator::NEG, {e}); .) | '(' ExprTyped ')' ). StructLiteral = (. std::wstring key; Expression val; std::list> keys; size_t keyCounter=0; .) lcurbrack (IF(checkTokenAfterIdent(_assign)) Ident '=' Expr | Expr (. key = to_wstring(keyCounter++); .) ) (. keys.push_back(Atom(key)); e = Expression(Operator::LIST_NAMED, {val}); .) {',' (IF(checkTokenAfterIdent(_assign)) Ident '=' Expr | Expr (. key = to_wstring(keyCounter++); .) ) (. e.addArg(Expression(val)); keys.push_back(Atom(key)); .) } rcurbrack (. e.addBindings(keys.begin(), keys.end()); .) . ListLiteral = (. Expression eFrom, eTo; .) '[' [ Expr (. e.addArg(Expression(eFrom)); .) (".." Expr (. e.addArg(Expression(eTo)); e.setOp(Operator::LIST_RANGE); .) |{',' Expr (. e.addArg(Expression(eFrom)); .) } (. e.setOp(Operator::LIST); .) ) ] ']'. AdhocDecl = (. Expression command; .) "ad" "hoc" MetaSimpExpr (. adhoc::AdhocExpression exprAdhoc; exprAdhoc.setCommand(command); e = exprAdhoc; .). CalleeParams = (. Expression e2; .) ExprTyped (. e.addArg(Expression(e2)); .) {',' ExprTyped (. e.addArg(Expression(e2)); .) }. AddOp< Operator& op> = (. op = Operator::ADD; .) ( '+' | '-' (. op = Operator::SUB; .) ). MulOp< Operator& op> = (. op = Operator::MUL; .) ( '*' | '/' (. op = Operator::DIV; .) ). RelOp< Operator& op> = (. op = Operator::EQU; .) ( equal | (ne1 | ne2) (. op = Operator::NE; .) | lse (. op = Operator::LSE; .) | lss (. op = Operator::LSS; .) | gte (. op = Operator::GTE; .) | gtr (. op = Operator::GTR; .) ). SkipModulesSection = "module" '{' {ANY} '}'. END Xreate. diff --git a/scripts/dfa/polymorphism.lp b/scripts/dfa/polymorphism.lp new file mode 100644 index 0000000..9c11fe3 --- /dev/null +++ b/scripts/dfa/polymorphism.lp @@ -0,0 +1,15 @@ +%INPUT +% bind(Symb, callguard(..)) - binded Ann +%OUTPUT +% dfa_callguard(..) - ready for processing + +dfa_callguard(Instance, Guard):- + bind(ArgActual, callguard(Guard)); + dfa_callargs(Instance,_, ArgActual); + dfa_callret(Instance, SymbRet); + bind(SymbRet, dfa_polym(arg)). + +dfa_callguard(Instance, Guard):- + bind(SymbRet, callguard(Guard)); + dfa_callret(Instance, SymbRet); + bind(SymbRet, dfa_polym(ret)). diff --git a/scripts/dfa/propagation.lp b/scripts/dfa/propagation.lp new file mode 100644 index 0000000..ac8e81e --- /dev/null +++ b/scripts/dfa/propagation.lp @@ -0,0 +1,20 @@ +%Propagates symbol bindings registered by dfa_propagation over dfa_uppy edges +% +% INPUT: +% dfa_propagation(X) - register propagated Ann +% bind(Symb, X) - initial symbol +% +% CALCULATED: +% dfa_uppy - UPdate or coPY + +bind(SymbA, DfaAnn):- bind(SymbB, DfaAnn); dfa_propagation(DfaAnn); + 1{dfa_uppy(SymbA, SymbB); dfa_uppy(SymbB,SymbA)}; + v(SymbA); v(SymbB). + +dfa_uppy(X, Y):- bind(X, dfa_uppy(Pseudo)); bind(Y, dfa_pseudo(Pseudo)). +dfa_uppy(X, Y):- dfa_alias(X, Y). + +dfa_uppy(ArgFormal, ArgActual):- dfa_callargs(_,ArgFormal, ArgActual). +dfa_uppy(RetActual, RetFormal):- + dfa_callret(CallId, RetActual); dfa_callinstance(CallId, FnName); + dfa_fnret(FnName, RetFormal). diff --git a/scripts/effects-communication/communication.lp b/scripts/effects-communication/communication.lp new file mode 100644 index 0000000..481a564 --- /dev/null +++ b/scripts/effects-communication/communication.lp @@ -0,0 +1,77 @@ +%INPUT: +% const horizon - Max Depth of communication analysis +% comm_order(Alias)), bind(X, comm_alias(Alias) - to apply additional order constraints +% dfa_uppy from dfa-propagation + + +% INIT +%=========================================================== +comm_bind(end, end). +comm_bind(begin, begin). +comm_bind(X, Op):- bind(X, commop(Op)). + + +% ORDER +%============================================================ +comm_path(X2, X1) :- bind(X2, comm_order(Alias)); bind(X1, comm_alias(Alias)). + + +% INHERITS +%============================================================ +comm_path(X, Y) :- dfa_uppy(X, Y). + +comm_path(end, X) :- v(X); #sum{1: comm_path(Smth, X), v(Smth)}0. +comm_path(X, begin):- v(X); #sum{1: comm_path(X, Smth), v(Smth)}0. + + +% SCAN +%============================================================ +%SOURCE +scan(X, source(X), length(0)) :- comm_bind(X, write). + +scan(Z, source(X), length(Length + 1)) :- + comm_path(Z, Y); + scan(Y, source(X), length(Length)); Length < horizon; + not comm_bind(Z, _). + +scan(Z, source(X), report(Op)) :- + comm_path(Z, Y); + scan(Y, source(X), length(Length)); Length < horizon; + comm_bind(Z, Op). + +%SINK +scan(X, sink(X), length(0)) :- comm_bind(X, read). + +scan(Z, sink(X), length(Length + 1)) :- + comm_path(Y, Z); + scan(Y, sink(X), length(Length)); Length < horizon; + not comm_bind(Z, _). + +scan(Z, sink(X), report(Op)) :- + comm_path(Y, Z); + scan(Y, sink(X), length(Length)); Length < horizon; + comm_bind(Z, Op). + + +% ISSUES +%============================================================== +comm_spot_lost_end(Source) :- scan(_, source(Source), report(end)). +comm_spot_lost(Source, Spot) :- scan(Spot, source(Source), report(write)). +comm_spot_outofreach(X, Spot) :- scan(Spot, source(X), length(horizon)). +comm_spot_outofreach(X, Spot) :- scan(Spot, sink(X), length(horizon)). + +comm_spot_corrupt_null(Sink) :- scan(_, sink(Sink), report(begin_)). +comm_spot_dup(Sink, Spot) :- scan(Spot, sink(Sink), report(read)). + +comm_spot(Source):-comm_spot_lost_end(Source). +comm_spot(Source):-comm_spot_lost(Source, Spot). +comm_spot(X):-comm_spot_outofreach(X, Spot). +comm_spot(Sink):-comm_spot_corrupt_null(Sink). +comm_spot(Sink):-comm_spot_dup(Sink, Spot). + + +% IMPLEMENTATION +%============================================================= +comm_impl(Source, commDirect):- comm_bind(Source, write); not comm_spot(Source). +bind(Source, callguard(commDirect)):- comm_impl(Source, commDirect). + diff --git a/scripts/effects-communication/config.lp b/scripts/effects-communication/config.lp new file mode 100644 index 0000000..c6867d9 --- /dev/null +++ b/scripts/effects-communication/config.lp @@ -0,0 +1,2 @@ +#const horizon = 10. +dfa_propagation(callguard(commDirect)). diff --git a/scripts/effects-communication/example1-wr.xreate b/scripts/effects-communication/example1-wr.xreate new file mode 100644 index 0000000..d5d734a --- /dev/null +++ b/scripts/effects-communication/example1-wr.xreate @@ -0,0 +1,38 @@ +//Only Direct implementatino defined + +import raw ("scripts/effects-communication/communication.lp"). +import raw ("scripts/effects-communication/dfa/propagation.lp"). +import raw ("scripts/dfa/polymorphism.lp"). +import raw ("scripts/effects-communication/config.lp"). + +CommGuard = type variant{Invalid, Valid, Outdated}. + +CommDirect = type { + value:: int +}. + +CommGuarded = type { + value:: int, + state:: CommGuard +}. + +guard:: commDirect { + init=function::CommDirect{ + {value = 0} + } + + read= function(vault1:: CommDirect):: int{ + (vault1::CommDirect; commop(read))["value"] + } + + write= function(vault2:: CommDirect, valueNew:: int)::CommDirect{ + (vault2::CommDirect;dfa_pseudo(vault2)) + {value = valueNew}:: int; commop(write); dfa_uppy(vault2) + } +} + +main=function::int; entry { + x1 = init():: Comm; dfa_polym(ret). + x2 = write(x1, 1)::Comm; dfa_polym(arg). + a = read(x2):: int; dfa_polym(arg). + a +} diff --git a/scripts/effects-communication/example2-wr.xreate b/scripts/effects-communication/example2-wr.xreate new file mode 100644 index 0000000..95aba14 --- /dev/null +++ b/scripts/effects-communication/example2-wr.xreate @@ -0,0 +1,76 @@ +//Direct and Guarded implementation defined + +import raw ("scripts/effects-communication/communication.lp"). +import raw ("scripts/dfa/propagation.lp"). +import raw ("scripts/dfa/polymorphism.lp"). +import raw ("scripts/effects-communication/config.lp"). + +CommGuard = type variant{Invalid, Valid, Outdated}. + +CommDirect = type { + value:: int +}. + +CommGuarded = type { + value:: int, + state:: CommGuard +}. + +guard:: commDirect { + init=function::CommDirect{ + {value = 0} + } + + read= function(vault1:: CommDirect):: int{ + (vault1::CommDirect; commop(read))["value"] + } + + write= function(vault2:: CommDirect, valueNew:: int)::CommDirect{ + (vault2::CommDirect;dfa_pseudo(vault2)) + {value = valueNew}:: int; commop(write); dfa_uppy(vault2) + } +} + +errorRead = function:: int { -1 } + +errorWrite = function:: CommGuarded{ + { + value = -1, + state = Invalid() + } +} + +guard:: commGuarded{ + init=function::CommGuarded{ + { + value = 0, + state = Invalid() + } + } + + read=function(vault3:: CommGuarded):: int { + switch variant (vault3::CommGuarded;commop(read)):: int + case (Invalid) { errorRead() } + case (Outdated) { errorRead() } + case (Valid) { vault3["value"] } + } + + write=function(vault4:: CommGuarded, valueNew:: int)::CommGuarded{ + switch variant (vault4)::int + case (Invalid) { + {value = valueNew, state = Valid()} + } + + case (Outdated) { + {value = valueNew, state = Valid()} + } + + case (Valid) { errorWrite() } + } +} + +main=function::int; entry { + x1 = init():: Comm; dfa_polym(ret). + x2 = write(x1, 1)::Comm; dfa_polym(arg). + a = read(x2):: int; dfa_polym(arg). + a +}