├── .gitignore ├── .travis.yml ├── Makefile.am ├── README ├── algebraic_simplifier.cc ├── algebraic_simplifier.h ├── array_validator.h ├── ast_visitor.cc ├── ast_visitor.h ├── autogen.sh ├── bool_to_int.h ├── chipmunk_code_generator.cc ├── chipmunk_code_generator.h ├── clang_utility_functions.cc ├── clang_utility_functions.h ├── compiler_pass.h ├── configure.ac ├── cse.cc ├── cse.h ├── csi.cc ├── csi.h ├── dep.svg ├── desugar_compound_assignment.cc ├── desugar_compound_assignment.h ├── domino.cc ├── domino_to_chipmunk.cc ├── examples ├── bloom_filter.c ├── codel.c ├── codel.py ├── conga.c ├── conga.py ├── deterministic_sampling.c ├── ewma.c ├── flowlet_switching.c ├── flowlet_switching_simple.c ├── hashes.h ├── heavy_hitters.c ├── iso646.h ├── learning_bloom_filter.c ├── meter.c ├── meter.py ├── muxes.sk ├── rcp.c └── rel_ops.sk ├── expr_flattener_handler.cc ├── expr_flattener_handler.h ├── expr_prop.cc ├── expr_prop.h ├── gen_used_fields.cc ├── gen_used_fields.h ├── graph.h ├── if_conversion_handler.cc ├── if_conversion_handler.h ├── informal_grammar.txt ├── int_type_checker.h ├── libgraphviz_example.c ├── partitioning.cc ├── partitioning.h ├── pkt_func_transform.cc ├── pkt_func_transform.h ├── redundancy_remover.cc ├── redundancy_remover.h ├── set_idioms.h ├── sketch_backend.cc ├── sketch_backend.h ├── ssa.cc ├── ssa.h ├── stateful_flanks.cc ├── stateful_flanks.h ├── tests ├── Makefile.am ├── array_example.c ├── array_with_index_vars.c ├── complicated_scc_test.cc ├── echo_test.cc ├── expr_flattener_test.cc ├── for_loop.c ├── graph.dot ├── graph_dot_constructor.cc ├── hello.c ├── if_converter_test.cc ├── if_else.c ├── if_else_if.c ├── if_with_2_stmts.c ├── if_without_brace.c ├── impl_casts_int.c ├── local_var.c ├── nested_ifs.c ├── packet.c ├── packet_if_else.c ├── return.c ├── scc_test.cc ├── single_if.c ├── stateful.c ├── switch.c └── while_loop.c ├── third_party ├── Makefile.am ├── assert_exception.h ├── exception.hh ├── file_descriptor.cc ├── file_descriptor.hh ├── gtest-all.cpp ├── gtest-main.cc ├── gtest.h ├── temp_file.cc └── temp_file.hh ├── unique_identifiers.cc ├── unique_identifiers.h ├── util.cc ├── util.h └── validator.h /.gitignore: -------------------------------------------------------------------------------- 1 | a.out 2 | *.o 3 | .deps/ 4 | Makefile 5 | Makefile.in 6 | aclocal.m4 7 | autom4te.cache/ 8 | compile 9 | config.guess 10 | config.h 11 | config.h.in 12 | config.h.in~ 13 | config.log 14 | config.status 15 | config.sub 16 | configure 17 | depcomp 18 | install-sh 19 | libtool 20 | ltmain.sh 21 | missing 22 | stamp-h1 23 | *.Tpo 24 | domino 25 | domino_to_chipmunk 26 | *.a 27 | *.so 28 | test-driver 29 | echo_test 30 | *.log 31 | *.trs 32 | if_converter_test 33 | strength_reducer_test 34 | expr_flattener_test 35 | scc_test 36 | complicated_scc_test 37 | graph_dot_constructor 38 | __pycache__ 39 | *.spec 40 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: cpp 2 | sudo: required 3 | services: docker 4 | 5 | addons: 6 | apt: 7 | sources: 8 | - ubuntu-toolchain-r-test 9 | packages: 10 | - g++-4.9 11 | 12 | before_script: 13 | - curl -sSL http://llvm.org/apt/llvm-snapshot.gpg.key | sudo -E apt-key add - 14 | - sudo -E add-apt-repository -y 'deb http://llvm.org/apt/precise/ llvm-toolchain-precise-3.5 main' 15 | - sudo apt-get update -qq 16 | - sudo apt-get install -y -qq libclang-3.5-dev 17 | - sudo apt-get install -y -qq llvm-3.5-dev 18 | - sudo apt-get install -y -qq libgraphviz-dev 19 | 20 | script: "git clone https://github.com/anirudhSK/banzai.git; cd banzai; ./autogen.sh; ./configure CXX='g++-4.9'; sudo make install; cd ..; ./autogen.sh && ./configure CXX='g++-4.9' && make && make check" 21 | -------------------------------------------------------------------------------- /Makefile.am: -------------------------------------------------------------------------------- 1 | SUBDIRS = third_party . tests 2 | 3 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) 4 | AM_CPPFLAGS = -isystem $(CLANG_DEV_LIBS)/include/ 5 | AM_LDFLAGS = -L $(CLANG_DEV_LIBS)/lib 6 | 7 | # There are some strange circular deps within clang requiring us to list libraries twice 8 | # https://github.com/eliben/llvm-clang-samples/blob/master/Makefile#L71 9 | CLANG_LLVM_LIBS = -lclangAST -lclangAnalysis -lclangBasic -lclangDriver -lclangEdit -lclangFrontend -lclangFrontendTool -lclangLex -lclangParse -lclangSema -lclangSerialization\ 10 | -lclangAST -lclangAnalysis -lclangBasic -lclangDriver -lclangEdit -lclangFrontend -lclangFrontendTool -lclangLex -lclangParse -lclangSema -lclangSerialization\ 11 | -lLLVMOption -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMSupport -lz -lpthread -ltermcap -ldl 12 | 13 | common_source = clang_utility_functions.cc clang_utility_functions.h unique_identifiers.h unique_identifiers.cc pkt_func_transform.h pkt_func_transform.cc graph.h expr_flattener_handler.cc expr_flattener_handler.h expr_prop.cc stateful_flanks.cc ssa.cc partitioning.cc if_conversion_handler.cc if_conversion_handler.h ssa.h partitioning.h stateful_flanks.h expr_prop.h util.cc util.h assert_exception.h desugar_compound_assignment.h desugar_compound_assignment.cc int_type_checker.h algebraic_simplifier.cc algebraic_simplifier.h array_validator.h redundancy_remover.cc redundancy_remover.h sketch_backend.cc sketch_backend.h csi.cc csi.h cse.cc cse.h gen_used_fields.cc gen_used_fields.h validator.h bool_to_int.h ast_visitor.cc ast_visitor.h chipmunk_code_generator.cc chipmunk_code_generator.h 14 | 15 | noinst_LIBRARIES = libdomino.a 16 | bin_PROGRAMS = domino domino_to_chipmunk 17 | 18 | libdomino_a_SOURCES = $(common_source) 19 | 20 | domino_CXXFLAGS = $(PICKY_CXXFLAGS) -fno-rtti 21 | domino_SOURCES = domino.cc 22 | domino_LDADD = $(CLANG_LLVM_LIBS) libdomino.a $(srcdir)/third_party/libmahimahi.a 23 | 24 | domino_to_chipmunk_CXXFLAGS = $(PICKY_CXXFLAGS) -fno-rtti 25 | domino_to_chipmunk_SOURCES = domino_to_chipmunk.cc 26 | domino_to_chipmunk_LDADD = $(CLANG_LLVM_LIBS) libdomino.a $(srcdir)/third_party/libmahimahi.a 27 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | 1. Ensure you have python3 (> 3.5.1) git g++ (>4.9) build-essential autotools-dev libncurses5-dev autoconf libtool and zlib1g-dev 2 | (Use a package manager like macports or apt-get to get them.) 3 | 2. Get clang + llvm from 4 | http://llvm.org/releases/3.5.0/clang+llvm-3.5.0-macosx-apple-darwin.tar.xz (OS X) 5 | http://llvm.org/releases/3.5.0/clang+llvm-3.5.0-x86_64-linux-gnu-ubuntu-14.04.tar.xz (Ubuntu) 6 | Similar files exist for other distributions as well, though this has only been tested so far on Ubuntu and OS X. 7 | 3. Install sketch from https://people.csail.mit.edu/asolar/sketch-1.6.9.tar.gz, 8 | add the sketch binary to the path. 9 | 4. Install banzai from https://github.com/packet-transactions/banzai.git 10 | 5. git clone https://github.com/packet-transactions/domino-compiler.git 11 | 6. ./autogen.sh 12 | 7. ./configure CLANG_DEV_LIBS= 13 | (make sure CLANG_DEV_LIBS is set to an absolute file system path) 14 | 8. make 15 | 9. make check 16 | 10. sudo make install (if you want to install it system wide). 17 | -------------------------------------------------------------------------------- /algebraic_simplifier.cc: -------------------------------------------------------------------------------- 1 | #include "algebraic_simplifier.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | #include "clang_utility_functions.h" 5 | 6 | using namespace clang; 7 | 8 | std::string AlgebraicSimplifier::ast_visit_bin_op(const clang::BinaryOperator * bin_op) { 9 | if (can_be_simplified(bin_op)) { 10 | return simplify_simple_bin_op(bin_op); 11 | } else { 12 | return ast_visit_stmt(bin_op->getLHS()) + std::string(bin_op->getOpcodeStr()) + ast_visit_stmt(bin_op->getRHS()); 13 | } 14 | } 15 | 16 | std::string AlgebraicSimplifier::ast_visit_cond_op(const clang::ConditionalOperator * cond_op) { 17 | if (isa(cond_op->getCond()) and dyn_cast(cond_op->getCond())->getValue().getSExtValue() == 1) { 18 | return ast_visit_stmt(cond_op->getTrueExpr()); 19 | } else if (isa(cond_op->getCond()) and dyn_cast(cond_op->getCond())->getValue().getSExtValue() == 0) { 20 | return ast_visit_stmt(cond_op->getFalseExpr()); 21 | } else { 22 | return ast_visit_stmt(cond_op->getCond()) + " ? " 23 | + ast_visit_stmt(cond_op->getTrueExpr()) + " : " 24 | + ast_visit_stmt(cond_op->getFalseExpr()); 25 | } 26 | } 27 | 28 | bool AlgebraicSimplifier::can_be_simplified(const BinaryOperator * bin_op) const { 29 | assert_exception(bin_op); 30 | const auto * lhs = bin_op->getLHS(); 31 | const auto * rhs = bin_op->getRHS(); 32 | return isa(lhs) or isa(rhs); 33 | } 34 | 35 | std::string AlgebraicSimplifier::simplify_simple_bin_op(const BinaryOperator * bin_op) const { 36 | assert_exception(can_be_simplified(bin_op)); 37 | const auto opcode = bin_op->getOpcode(); 38 | const auto * lhs = bin_op->getLHS(); 39 | const auto * rhs = bin_op->getRHS(); 40 | if (opcode == clang::BinaryOperatorKind::BO_Mul or opcode == clang::BinaryOperatorKind::BO_LAnd) { 41 | if (isa(lhs) and dyn_cast(lhs)->getValue().getSExtValue() == 1) { 42 | // 1 * anything = anything 43 | // 1 && anything = anything 44 | return clang_stmt_printer(rhs); 45 | } else if (isa(rhs) and dyn_cast(rhs)->getValue().getSExtValue() == 1) { 46 | // anything * 1 = anything 47 | // anything && 1 = anything 48 | return clang_stmt_printer(lhs); 49 | } 50 | } 51 | 52 | if (opcode == clang::BinaryOperatorKind::BO_Add or opcode == clang::BinaryOperatorKind::BO_Or) { 53 | if (isa(lhs) and dyn_cast(lhs)->getValue().getSExtValue() == 0) { 54 | // 0 + anything = anything 55 | // 0 || anything = anything 56 | return clang_stmt_printer(rhs); 57 | } else if (isa(rhs) and dyn_cast(rhs)->getValue().getSExtValue() == 0) { 58 | // anything + 0 = anything 59 | // anything || 0 = anything 60 | return clang_stmt_printer(lhs); 61 | } 62 | } 63 | 64 | return clang_stmt_printer(bin_op->getLHS()) + std::string(bin_op->getOpcodeStr()) + clang_stmt_printer(bin_op->getRHS()); 65 | } 66 | -------------------------------------------------------------------------------- /algebraic_simplifier.h: -------------------------------------------------------------------------------- 1 | #ifndef ALGEBRAIC_SIMPLIFIER_H_ 2 | #define ALGEBRAIC_SIMPLIFIER_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | class AlgebraicSimplifier : public AstVisitor { 7 | protected: 8 | /// Simplify a binary operator using 9 | /// algebraic rewrite rules. 10 | std::string ast_visit_bin_op(const clang::BinaryOperator * bin_op) override; 11 | 12 | /// Simplify a conditional operator using 13 | /// algebraic rewrite rules. 14 | std::string ast_visit_cond_op(const clang::ConditionalOperator * cond_op) override; 15 | 16 | private: 17 | /// Check whether a bin_op can be simplified 18 | /// i.e. one of its arguments is a constant 19 | bool can_be_simplified(const clang::BinaryOperator * bin_op) const; 20 | 21 | /// Simplify a bin op where the LHS and RHS are both simple 22 | // i.e. IntegerLiteral, MemberExpr, or DeclRefExpr 23 | std::string simplify_simple_bin_op(const clang::BinaryOperator * bin_op) const; 24 | }; 25 | 26 | #endif // ALGEBRAIC_SIMPLIFIER_H_ 27 | -------------------------------------------------------------------------------- /array_validator.h: -------------------------------------------------------------------------------- 1 | #ifndef ARRAY_VALIDATOR_H_ 2 | #define ARRAY_VALIDATOR_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | #include "clang/AST/Expr.h" 7 | 8 | #include "third_party/assert_exception.h" 9 | #include "clang_utility_functions.h" 10 | 11 | /// Check that an array expression for a particular array 12 | /// is indexed only by a MemberRefExpr (a packet field). 13 | /// TODO: Also check that this MemberRefExpr isn't ever reassigned. 14 | /// We need to do this soon because otherwise, stateful_flanks will 15 | /// end up with painful input that it won't know how to handle. 16 | class ArrayValidator : public AstVisitor { 17 | protected: 18 | /// Touch only array subscript operator 19 | std::string ast_visit_array_subscript_expr(const clang::ArraySubscriptExpr * array_subscript_expr) override { 20 | assert_exception(array_subscript_expr); 21 | bool check = clang::isa(array_subscript_expr->getIdx()) and 22 | clang::isa(clang::dyn_cast(array_subscript_expr->getIdx())->getSubExpr()); 23 | if (check == false) { 24 | throw std::logic_error("Only packet fields are allowed as array indices.\n" 25 | "The expression " + clang_stmt_printer(array_subscript_expr) + "\n" + 26 | "uses an index " + clang_stmt_printer(array_subscript_expr->getIdx()) + "\n" + 27 | "of type " + std::string(array_subscript_expr->getIdx()->getStmtClassName())); 28 | } else { 29 | // Delegate to base class 30 | return AstVisitor::ast_visit_array_subscript_expr(array_subscript_expr); 31 | } 32 | } 33 | }; 34 | 35 | #endif // ARRAY_VALIDATOR_H_ 36 | -------------------------------------------------------------------------------- /ast_visitor.cc: -------------------------------------------------------------------------------- 1 | #include "ast_visitor.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "clang/AST/Expr.h" 7 | 8 | #include "third_party/assert_exception.h" 9 | 10 | #include "clang_utility_functions.h" 11 | #include "pkt_func_transform.h" 12 | 13 | using namespace std::placeholders; 14 | using namespace clang; 15 | 16 | std::string AstVisitor::ast_visit_transform(const TranslationUnitDecl * tu_decl) { 17 | return pkt_func_transform(tu_decl, std::bind(&AstVisitor::ast_visit_helper, this, _1, _2)); 18 | } 19 | 20 | std::pair> AstVisitor::ast_visit_helper(const clang::CompoundStmt * body, 21 | const std::string & pkt_name __attribute__((unused))) { 22 | return std::make_pair("{" + ast_visit_stmt(body) + "}", std::vector()); 23 | } 24 | 25 | std::string AstVisitor::ast_visit_comp_stmt(const CompoundStmt * comp_stmt) { 26 | assert_exception(comp_stmt); 27 | std::string ret; 28 | for (const auto & child : comp_stmt->children()) 29 | ret += ast_visit_stmt(child) + ";"; 30 | return ret; 31 | } 32 | 33 | std::string AstVisitor::ast_visit_if_stmt(const IfStmt * if_stmt) { 34 | assert_exception(if_stmt); 35 | std::string ret; 36 | ret += "if (" + ast_visit_stmt(if_stmt->getCond()) + ") {" + ast_visit_stmt(if_stmt->getThen()) + "; }"; 37 | if (if_stmt->getElse() != nullptr) { 38 | ret += "else {" + ast_visit_stmt(if_stmt->getElse()) + "; }"; 39 | } 40 | return ret; 41 | } 42 | 43 | std::string AstVisitor::ast_visit_bin_op(const BinaryOperator * bin_op) { 44 | assert_exception(bin_op); 45 | return ast_visit_stmt(bin_op->getLHS()) + std::string(bin_op->getOpcodeStr()) + ast_visit_stmt(bin_op->getRHS()); 46 | } 47 | 48 | std::string AstVisitor::ast_visit_cond_op(const ConditionalOperator * cond_op) { 49 | assert_exception(cond_op); 50 | return ast_visit_stmt(cond_op->getCond()) + " ? " 51 | + ast_visit_stmt(cond_op->getTrueExpr()) + " : " 52 | + ast_visit_stmt(cond_op->getFalseExpr()); 53 | } 54 | 55 | std::string AstVisitor::ast_visit_func_call(const CallExpr * call_expr) { 56 | assert_exception(call_expr); 57 | std::string ret = clang_stmt_printer(call_expr->getCallee()) + "("; 58 | for (const auto * child : call_expr->arguments()) { 59 | const auto child_str = ast_visit_stmt(child); 60 | ret += child_str + ","; 61 | } 62 | ret.back() = ')'; 63 | return ret; 64 | } 65 | 66 | std::string AstVisitor::ast_visit_member_expr(const MemberExpr * member_expr) { 67 | assert_exception(member_expr); 68 | return clang_stmt_printer(member_expr); 69 | } 70 | 71 | std::string AstVisitor::ast_visit_decl_ref_expr(const DeclRefExpr * decl_ref_expr) { 72 | assert_exception(decl_ref_expr); 73 | return clang_stmt_printer(decl_ref_expr); 74 | } 75 | 76 | std::string AstVisitor::ast_visit_array_subscript_expr(const ArraySubscriptExpr * array_subscript_expr) { 77 | assert_exception(array_subscript_expr); 78 | return clang_stmt_printer(array_subscript_expr); 79 | } 80 | 81 | std::string AstVisitor::ast_visit_integer_literal(const IntegerLiteral * integer_literal) { 82 | assert_exception(integer_literal); 83 | return clang_stmt_printer(integer_literal); 84 | } 85 | 86 | std::string AstVisitor::ast_visit_un_op(const UnaryOperator * un_op) { 87 | assert_exception(un_op->isArithmeticOp()); 88 | const auto opcode_str = std::string(UnaryOperator::getOpcodeStr(un_op->getOpcode())); 89 | assert_exception(opcode_str == "!"); 90 | return opcode_str + ast_visit_stmt(un_op->getSubExpr()); 91 | } 92 | 93 | std::string AstVisitor::ast_visit_implicit_cast(const ImplicitCastExpr * implicit_cast) { 94 | assert_exception(implicit_cast); 95 | return ast_visit_stmt(implicit_cast->getSubExpr()); 96 | } 97 | 98 | std::string AstVisitor::ast_visit_stmt(const Stmt * stmt) { 99 | assert_exception(stmt); 100 | std::string ret; 101 | if(isa(stmt)) { 102 | return ast_visit_comp_stmt(dyn_cast(stmt)); 103 | } else if (isa(stmt)) { 104 | return ast_visit_if_stmt(dyn_cast(stmt)); 105 | } else if (isa(stmt)) { 106 | return ast_visit_bin_op(dyn_cast(stmt)); 107 | } else if (isa(stmt)) { 108 | return ast_visit_cond_op(dyn_cast(stmt)); 109 | } else if (isa(stmt)) { 110 | return ast_visit_member_expr(dyn_cast(stmt)); 111 | } else if (isa(stmt)) { 112 | return ast_visit_decl_ref_expr(dyn_cast(stmt)); 113 | } else if (isa(stmt)) { 114 | return ast_visit_integer_literal(dyn_cast(stmt)); 115 | } else if (isa(stmt)) { 116 | return ast_visit_array_subscript_expr(dyn_cast(stmt)); 117 | } else if (isa(stmt)) { 118 | return "(" + ast_visit_stmt(dyn_cast(stmt)->getSubExpr()) + ")"; 119 | } else if (isa(stmt)) { 120 | return ast_visit_un_op(dyn_cast(stmt)); 121 | } else if (isa(stmt)) { 122 | return ast_visit_implicit_cast(dyn_cast(stmt)); 123 | } else if (isa(stmt)) { 124 | return ast_visit_func_call(dyn_cast(stmt)); 125 | } else if (isa(stmt)) { 126 | return ";"; 127 | } else { 128 | throw std::logic_error("ast_visit error: the statement\n" 129 | + clang_stmt_printer(stmt) 130 | + "\nis of type " 131 | + std::string(stmt->getStmtClassName()) 132 | + ", which isn't allowed in domino"); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /ast_visitor.h: -------------------------------------------------------------------------------- 1 | #ifndef AST_VISITOR_H_ 2 | #define AST_VISITOR_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "clang/AST/Stmt.h" 8 | #include "clang/AST/Decl.h" 9 | #include "clang/AST/Expr.h" 10 | 11 | class AstVisitor { 12 | public: 13 | /// Entry point from SinglePass, 14 | /// which immediately delegates to ast_visit_helper 15 | virtual std::string ast_visit_transform(const clang::TranslationUnitDecl * tu_decl); 16 | 17 | /// Need destructor to be virtual and accessible, g++ whines otherwise 18 | virtual ~AstVisitor() {}; 19 | 20 | /// Virtual methods for each node type 21 | protected: 22 | /// Visit a compound statement (braces in C) 23 | virtual std::string ast_visit_comp_stmt(const clang::CompoundStmt * comp_stmt); 24 | 25 | /// Visit an if statement (if, else-if, in C) 26 | virtual std::string ast_visit_if_stmt(const clang::IfStmt * if_stmt); 27 | 28 | /// Visit a binary operator (a = b, a + b, a * b) 29 | virtual std::string ast_visit_bin_op(const clang::BinaryOperator * bin_op); 30 | 31 | /// Visit a conditional operator (a ? b : c, not including GNU's extension) 32 | virtual std::string ast_visit_cond_op(const clang::ConditionalOperator * cond_op); 33 | 34 | /// Visit a function call expression (f(1, 2, 3)) 35 | virtual std::string ast_visit_func_call(const clang::CallExpr * call_expr); 36 | 37 | /// Visit a member expression (packet variables of the form pkt.x) 38 | virtual std::string ast_visit_member_expr(const clang::MemberExpr * member_expr); 39 | 40 | /// Visit a state variable expression (any access to global state) 41 | virtual std::string ast_visit_decl_ref_expr(const clang::DeclRefExpr * decl_ref_expr); 42 | 43 | /// Visit integer constants (-1L, 2U, 123, etc.) 44 | virtual std::string ast_visit_integer_literal(const clang::IntegerLiteral * integer_literal); 45 | 46 | /// Visit an array subscript node (a[3]) 47 | virtual std::string ast_visit_array_subscript_expr(const clang::ArraySubscriptExpr * array_subscript_expr); 48 | 49 | /// Visit a unary operator (-2 or ! false) 50 | virtual std::string ast_visit_un_op(const clang::UnaryOperator * un_op); 51 | 52 | /// Visit an implicit cast node, 53 | /// These captures casts such as LValue to RValue 54 | /// and casts between integers types, 55 | /// which happen implicitly in the C standard 56 | virtual std::string ast_visit_implicit_cast(const clang::ImplicitCastExpr * implicit_cast); 57 | 58 | /// Visit a clang::Stmt recursively 59 | /// This is protected so that derived classes can call it recursively, 60 | /// but not virtual because the grammar is fixed and ast_visit simply 61 | /// captures a walk down the AST 62 | std::string ast_visit_stmt(const clang::Stmt * stmt); 63 | 64 | private: 65 | /// Wrapper around recursive function ast_visit_helper 66 | std::pair> ast_visit_helper(const clang::CompoundStmt * body, 67 | const std::string & pkt_name __attribute__((unused))); 68 | }; 69 | 70 | #endif // AST_VISITOR_H_ 71 | -------------------------------------------------------------------------------- /autogen.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec autoreconf -fi 4 | -------------------------------------------------------------------------------- /bool_to_int.h: -------------------------------------------------------------------------------- 1 | #ifndef BOOL_TO_INT_H_ 2 | #define BOOL_TO_INT_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | #include "clang/AST/Expr.h" 7 | 8 | #include "third_party/assert_exception.h" 9 | #include "clang_utility_functions.h" 10 | 11 | /// Convert bools of the form pkt.tmp ? x : y 12 | /// to (pkt.tmp != 0) ? x = y 13 | class BoolToInt : public AstVisitor { 14 | protected: 15 | /// Touch only conditonal operators, leave the 16 | /// rest to the base class AstVisitor 17 | std::string ast_visit_cond_op(const clang::ConditionalOperator * cond_op) override { 18 | assert_exception(cond_op); 19 | // Make sure the condition within cond_op is a MemberExpr 20 | assert_exception(clang::isa(cond_op->getCond()->IgnoreParenImpCasts())); 21 | return "(" + clang_stmt_printer(cond_op->getCond()) + " != 0) ? " 22 | + ast_visit_stmt(cond_op->getTrueExpr()) + " : " 23 | + ast_visit_stmt(cond_op->getFalseExpr()); 24 | } 25 | }; 26 | 27 | #endif // BOOL_TO_INT_H_ 28 | -------------------------------------------------------------------------------- /chipmunk_code_generator.cc: -------------------------------------------------------------------------------- 1 | #include "chipmunk_code_generator.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | #include "clang_utility_functions.h" 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | using namespace clang; 12 | 13 | std::string ChipmunkCodeGenerator::ast_visit_transform(const clang::TranslationUnitDecl * tu_decl) { 14 | // TODO: Need to check if we have more than one packet func per tu_decl and report an error if so. 15 | for (const auto * decl : dyn_cast(tu_decl)->decls()) { 16 | if (isa(decl) and (is_packet_func(dyn_cast(decl)))) { 17 | //record body part first 18 | std::string body_part = ast_visit_stmt(dyn_cast(decl)->getBody()); 19 | print_map(); 20 | return "|StateAndPacket| program (|StateAndPacket| state_and_packet) {" + body_part + " return state_and_packet;\n}"; 21 | } 22 | } 23 | assert_exception(false); 24 | } 25 | 26 | std::string ChipmunkCodeGenerator::ast_visit_decl_ref_expr(const clang::DeclRefExpr * decl_ref_expr) { 27 | assert_exception(decl_ref_expr); 28 | std::string s = clang_stmt_printer(decl_ref_expr); 29 | std::map::iterator it; 30 | it = c_to_sk.find(s); 31 | if (it == c_to_sk.end()){ 32 | std::string name; 33 | //stateless 34 | if (s.find('.')!=std::string::npos){ 35 | // Should never get here. 36 | assert_exception(false); 37 | } 38 | else{ 39 | name = "state_and_packet.state_" + std::to_string(count_stateful); 40 | count_stateful++; 41 | c_to_sk[s] = name; 42 | } 43 | } 44 | return c_to_sk[s]; 45 | } 46 | 47 | std::string ChipmunkCodeGenerator::ast_visit_member_expr(const clang::MemberExpr * member_expr) { 48 | assert_exception(member_expr); 49 | std::string s = clang_stmt_printer(member_expr); 50 | std::map::iterator it; 51 | it = c_to_sk.find(s); 52 | if (it == c_to_sk.end()){ 53 | std::string name; 54 | //stateless 55 | if (s.find('.')!=std::string::npos && s.find('[')==std::string::npos){ 56 | name = "state_and_packet.pkt_" + std::to_string(count_stateless); 57 | count_stateless++; 58 | } 59 | else{ 60 | // Should never get here. 61 | assert_exception(false); 62 | } 63 | c_to_sk[s] = name; 64 | } 65 | return c_to_sk[s]; 66 | } 67 | 68 | std::string ChipmunkCodeGenerator::ast_visit_array_subscript_expr(const clang::ArraySubscriptExpr * array_subscript_expr){ 69 | assert_exception(array_subscript_expr); 70 | std::string s = clang_stmt_printer(array_subscript_expr); 71 | 72 | std::map::iterator it; 73 | it = c_to_sk.find(s); 74 | if (it == c_to_sk.end()){ 75 | std::string name; 76 | if (s.find('[')==std::string::npos){ 77 | // Should never get here. 78 | assert_exception(false); 79 | }else{ 80 | //stateless 81 | name = "state_and_packet.state_" + std::to_string(count_stateful); 82 | count_stateful++; 83 | c_to_sk[s] = name; 84 | } 85 | } 86 | return c_to_sk[s]; 87 | } 88 | 89 | void ChipmunkCodeGenerator::print_map(){ 90 | std::cout << "// Output the rename map:" << std::endl; 91 | std::cout << "// stateless variable rename list: \n\n"; 92 | // output the rename map in order 93 | int stateless = 0; 94 | while (stateless != count_stateless){ 95 | for(std::map::const_iterator it = c_to_sk.begin();it != c_to_sk.end(); ++it){ 96 | if (it->second.find("state_and_packet.pkt_" + std::to_string(stateless))!=std::string::npos){ 97 | std::cout << "// " <second << " = " << it->first << "\n"; 98 | stateless++; 99 | break; 100 | } 101 | } 102 | } 103 | std::cout << std::endl; 104 | std::cout << "// stateful variable rename list: \n\n"; 105 | 106 | int stateful = 0; 107 | while (stateful != count_stateful){ 108 | for(std::map::const_iterator it = c_to_sk.begin();it != c_to_sk.end(); ++it){ 109 | if (it->second.find("state_and_packet.state_" + std::to_string(stateful))!=std::string::npos){ 110 | std::cout << "// " <second << " = " << it->first << "\n"; 111 | stateful++; 112 | break; 113 | } 114 | } 115 | } 116 | std::cout << std::endl; 117 | } 118 | -------------------------------------------------------------------------------- /chipmunk_code_generator.h: -------------------------------------------------------------------------------- 1 | #ifndef CHIPMUNK_CODE_GENERATOR_H_ 2 | #define CHIPMUNK_CODE_GENERATOR_H_ 3 | 4 | #include "ast_visitor.h" 5 | #include 6 | #include 7 | 8 | class ChipmunkCodeGenerator : public AstVisitor { 9 | public: 10 | std::string ast_visit_transform(const clang::TranslationUnitDecl * tu_decl) override; 11 | void print_map(); //cout the content of map 12 | 13 | protected: 14 | std::string ast_visit_member_expr(const clang::MemberExpr * member_expr) override; 15 | std::string ast_visit_array_subscript_expr(const clang::ArraySubscriptExpr * array_subscript_expr) override; 16 | std::string ast_visit_decl_ref_expr(const clang::DeclRefExpr * decl_ref_expr) override; 17 | 18 | int count_stateful = 0; 19 | int count_stateless = 0; 20 | 21 | // TODO: This strange typedef is to address an issue with gcc: 22 | // https://stackoverflow.com/a/28803798/1152801 23 | typedef std::map DominoToSketchRenamer; 24 | DominoToSketchRenamer c_to_sk = DominoToSketchRenamer(); 25 | }; 26 | 27 | #endif // CHIPMUNK_CODE_GENERATOR_H_ 28 | -------------------------------------------------------------------------------- /clang_utility_functions.cc: -------------------------------------------------------------------------------- 1 | #include "clang_utility_functions.h" 2 | 3 | #include 4 | 5 | #include "llvm/Support/raw_ostream.h" 6 | #include "clang/Basic/LangOptions.h" 7 | #include "clang/AST/PrettyPrinter.h" 8 | #include "clang/AST/Expr.h" 9 | #include "clang/AST/Stmt.h" 10 | 11 | #include "third_party/assert_exception.h" 12 | #include "set_idioms.h" 13 | 14 | using namespace clang; 15 | 16 | std::string clang_stmt_printer(const clang::Stmt * stmt) { 17 | assert_exception(stmt != nullptr); 18 | 19 | // Required for pretty printing 20 | clang::LangOptions LangOpts; 21 | LangOpts.CPlusPlus = true; 22 | clang::PrintingPolicy Policy(LangOpts); 23 | 24 | std::string str; 25 | llvm::raw_string_ostream rso(str); 26 | stmt->printPretty(rso, nullptr, Policy); 27 | return str; 28 | } 29 | 30 | std::string clang_value_decl_printer(const clang::ValueDecl * value_decl) { 31 | // Required for pretty printing 32 | clang::LangOptions LangOpts; 33 | LangOpts.CPlusPlus = true; 34 | clang::PrintingPolicy Policy(LangOpts); 35 | 36 | std::string str; 37 | llvm::raw_string_ostream rso(str); 38 | value_decl->printName(rso); 39 | return str; 40 | } 41 | 42 | std::string clang_decl_printer(const clang::Decl * decl) { 43 | // Required for pretty printing 44 | clang::LangOptions LangOpts; 45 | LangOpts.CPlusPlus = true; 46 | clang::PrintingPolicy Policy(LangOpts); 47 | 48 | std::string str; 49 | llvm::raw_string_ostream rso(str); 50 | decl->print(rso); 51 | return str; 52 | } 53 | 54 | bool is_packet_func(const clang::FunctionDecl * func_decl) { 55 | // Not sure what we would get out of functions with zero args 56 | assert_exception(func_decl->getNumParams() >= 1); 57 | return func_decl->getNumParams() == 1 58 | and func_decl->getParamDecl(0)->getType().getAsString() == "struct Packet"; 59 | } 60 | 61 | std::string replace_var_helper(const Expr * expr, const std::map & repl_map) { 62 | const std::string var_name = clang_stmt_printer(expr); 63 | if (repl_map.find(var_name) != repl_map.end()) { 64 | return repl_map.at(var_name); 65 | } else { 66 | return var_name; 67 | } 68 | } 69 | 70 | std::set identifier_census(const clang::TranslationUnitDecl * decl, const VariableTypeSelector & var_selector) { 71 | std::set identifiers = {}; 72 | assert_exception(decl != nullptr); 73 | 74 | // Get all decls by dyn casting decl into a DeclContext 75 | for (const auto * child_decl : dyn_cast(decl)->decls()) { 76 | assert_exception(child_decl); 77 | assert_exception(child_decl->isDefinedOutsideFunctionOrMethod()); 78 | if (isa(child_decl)) { 79 | // add current fields in struct to identifiers 80 | if (var_selector.at(VariableType::PACKET)) { 81 | for (const auto * field_decl : dyn_cast(child_decl)->decls()) 82 | identifiers.emplace(dyn_cast(field_decl)->getName()); 83 | } 84 | } else if (isa(child_decl)) { 85 | if (var_selector.at(VariableType::FUNCTION_PARAMETER)) { 86 | // add function name 87 | identifiers.emplace(dyn_cast(child_decl)->getName()); 88 | // add all function parameters 89 | for (const auto * parm_decl : dyn_cast(child_decl)->parameters()) { 90 | identifiers.emplace(dyn_cast(parm_decl)->getName()); 91 | } 92 | } 93 | } else if (isa(child_decl)) { 94 | const auto * underlying_type = dyn_cast(child_decl)->getType().getTypePtrOrNull(); 95 | if (isa(underlying_type)) { 96 | if (var_selector.at(VariableType::STATE_ARRAY)) { 97 | identifiers.emplace(dyn_cast(child_decl)->getName()); 98 | } 99 | } else if (isa(underlying_type) and dyn_cast(underlying_type)->isInteger()) { 100 | if (var_selector.at(VariableType::STATE_SCALAR)) { 101 | identifiers.emplace(dyn_cast(child_decl)->getName()); 102 | } 103 | } else { 104 | throw std::logic_error("We don't support this: state variable " 105 | + clang_value_decl_printer(dyn_cast(child_decl)) 106 | + " has type " + std::string(dyn_cast(child_decl)->getName())); 107 | } 108 | } else { 109 | // We can't remove TypedefDecl from the AST for some reason. 110 | assert_exception(isa(child_decl)); 111 | } 112 | } 113 | return identifiers; 114 | } 115 | 116 | std::set gen_var_list(const Stmt * stmt, const VariableTypeSelector & var_selector) { 117 | // Recursively scan stmt to generate a set of strings representing 118 | // either packet fields or state variables used within stmt 119 | assert_exception(stmt); 120 | std::set ret; 121 | if (isa(stmt)) { 122 | for (const auto & child : stmt->children()) { 123 | ret = ret + gen_var_list(child, var_selector); 124 | } 125 | return ret; 126 | } else if (isa(stmt)) { 127 | const auto * if_stmt = dyn_cast(stmt); 128 | if (if_stmt->getElse() != nullptr) { 129 | return gen_var_list(if_stmt->getCond(), var_selector) + gen_var_list(if_stmt->getThen(), var_selector) + gen_var_list(if_stmt->getElse(), var_selector); 130 | } else { 131 | return gen_var_list(if_stmt->getCond(), var_selector) + gen_var_list(if_stmt->getThen(), var_selector); 132 | } 133 | } else if (isa(stmt)) { 134 | const auto * bin_op = dyn_cast(stmt); 135 | return gen_var_list(bin_op->getLHS(), var_selector) + gen_var_list(bin_op->getRHS(), var_selector); 136 | } else if (isa(stmt)) { 137 | const auto * cond_op = dyn_cast(stmt); 138 | return gen_var_list(cond_op->getCond(), var_selector) + gen_var_list(cond_op->getTrueExpr(), var_selector) + gen_var_list(cond_op->getFalseExpr(), var_selector); 139 | } else if (isa(stmt)) { 140 | return (var_selector.at(VariableType::PACKET)) 141 | ? std::set{clang_stmt_printer(stmt)} 142 | : std::set(); 143 | } else if (isa(stmt)) { 144 | return (var_selector.at(VariableType::STATE_SCALAR)) 145 | ? std::set{clang_stmt_printer(stmt)} 146 | : std::set(); 147 | } else if (isa(stmt)) { 148 | // We return array name here, not array name subscript index. 149 | // We assume that the array name is a unique identifier because 150 | // per-packet we only ever access one address in the array. 151 | if (var_selector.at(VariableType::STATE_ARRAY)) { 152 | return std::set{clang_stmt_printer(dyn_cast(stmt)->getBase())}; 153 | } else if (var_selector.at(VariableType::PACKET)) { 154 | return std::set{clang_stmt_printer(dyn_cast(stmt)->getIdx())}; 155 | } else { 156 | return std::set(); 157 | } 158 | } else if (isa(stmt) or isa(stmt)) { 159 | return std::set(); 160 | } else if (isa(stmt)) { 161 | return gen_var_list(dyn_cast(stmt)->getSubExpr(), var_selector); 162 | } else if (isa(stmt)) { 163 | const auto * un_op = dyn_cast(stmt); 164 | assert_exception(un_op->isArithmeticOp()); 165 | const auto opcode_str = std::string(UnaryOperator::getOpcodeStr(un_op->getOpcode())); 166 | assert_exception(opcode_str == "!"); 167 | return gen_var_list(un_op->getSubExpr(), var_selector); 168 | } else if (isa(stmt)) { 169 | return gen_var_list(dyn_cast(stmt)->getSubExpr(), var_selector); 170 | } else if (isa(stmt)) { 171 | const auto * call_expr = dyn_cast(stmt); 172 | std::set ret; 173 | for (const auto * child : call_expr->arguments()) { 174 | const auto child_uses = gen_var_list(child, var_selector); 175 | ret = ret + child_uses; 176 | } 177 | return ret; 178 | } else { 179 | throw std::logic_error("gen_var_list cannot handle stmt of type " + std::string(stmt->getStmtClassName())); 180 | } 181 | } 182 | 183 | std::string generate_scalar_func_def(const FunctionDecl * func_decl) { 184 | // Yet another C quirk. Adding a semicolon after a function definition 185 | // is caught by -pedantic, not adding a semicolon after a function declaration 186 | // without a definition is not permitted in C :) 187 | assert_exception(func_decl); 188 | assert_exception(not is_packet_func(func_decl)); 189 | const bool has_body = func_decl->hasBody(); 190 | return clang_decl_printer(func_decl) + (has_body ? "" : ";"); 191 | } 192 | 193 | std::string gen_pkt_fields(const TranslationUnitDecl * tu_decl) { 194 | std::string ret = ""; 195 | for (const auto & field : identifier_census(tu_decl, 196 | {{VariableType::PACKET, true}, 197 | {VariableType::STATE_SCALAR, false}, 198 | {VariableType::FUNCTION_PARAMETER, false}, 199 | {VariableType::STATE_ARRAY, false}})) { 200 | ret += field + "\n"; 201 | } 202 | return ret; 203 | } 204 | 205 | std::string replace_vars(const clang::Expr * expr, 206 | const std::map & repl_map, 207 | const VariableTypeSelector & var_selector) { 208 | assert_exception(expr); 209 | if (isa(expr)) { 210 | return "(" + replace_vars(dyn_cast(expr)->getSubExpr(), repl_map, var_selector) + ")"; 211 | } else if (isa(expr)) { 212 | return replace_vars(dyn_cast(expr)->getSubExpr(), repl_map, var_selector); 213 | } else if (isa(expr)) { 214 | const auto * un_op = dyn_cast(expr); 215 | assert_exception(un_op->isArithmeticOp()); 216 | const auto opcode_str = std::string(UnaryOperator::getOpcodeStr(un_op->getOpcode())); 217 | assert_exception(opcode_str == "!"); 218 | return opcode_str + replace_vars(un_op->getSubExpr(), repl_map, var_selector); 219 | } else if (isa(expr)) { 220 | const auto * cond_op = dyn_cast(expr); 221 | 222 | const auto cond_str = replace_vars(cond_op->getCond(), repl_map, var_selector); 223 | const auto true_str = replace_vars(cond_op->getTrueExpr(), repl_map, var_selector); 224 | const auto false_str = replace_vars(cond_op->getFalseExpr(), repl_map, var_selector); 225 | 226 | return cond_str + " ? " + true_str + " : " + false_str; 227 | } else if (isa(expr)) { 228 | const auto * bin_op = dyn_cast(expr); 229 | const auto lhs_str = replace_vars(bin_op->getLHS(), repl_map, var_selector); 230 | const auto rhs_str = replace_vars(bin_op->getRHS(), repl_map, var_selector); 231 | return lhs_str + std::string(BinaryOperator::getOpcodeStr(bin_op->getOpcode())) + rhs_str; 232 | } else if (isa(expr)) { 233 | if (var_selector.at(VariableType::STATE_SCALAR)) return replace_var_helper(expr, repl_map); 234 | else return clang_stmt_printer(expr); 235 | } else if (isa(expr)) { 236 | if (var_selector.at(VariableType::PACKET)) return replace_var_helper(expr, repl_map); 237 | else return clang_stmt_printer(expr); 238 | } else if (isa(expr)) { 239 | const auto * array_op = dyn_cast(expr); 240 | if (var_selector.at(VariableType::STATE_ARRAY)) return replace_var_helper(expr, repl_map); 241 | else if (var_selector.at(VariableType::PACKET)) return clang_stmt_printer(array_op->getBase()) + "[" + replace_vars(array_op->getIdx(), repl_map, var_selector) + "]"; 242 | else return clang_stmt_printer(expr); 243 | } else if (isa(expr)) { 244 | const auto * call_expr = dyn_cast(expr); 245 | std::string ret = clang_stmt_printer(call_expr->getCallee()) + "("; 246 | for (const auto * child : call_expr->arguments()) { 247 | const auto child_str = replace_vars(child, repl_map, var_selector); 248 | ret += child_str + ","; 249 | } 250 | ret.back() = ')'; 251 | return ret; 252 | } else if (isa(expr)){ 253 | return clang_stmt_printer(expr); 254 | } else { 255 | throw std::logic_error("replace_vars cannot handle expr " + std::string(clang_stmt_printer(expr)) + " of type " + std::string(expr->getStmtClassName())); 256 | } 257 | } 258 | 259 | bool is_in_ssa(const CompoundStmt * compound_stmt) { 260 | std::set assigned_vars; 261 | for (const auto * child : compound_stmt->children()) { 262 | assert_exception(isa(child)); 263 | const auto * bin_op = dyn_cast(child); 264 | assert_exception(bin_op->isAssignmentOp()); 265 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 266 | const auto pair = assigned_vars.emplace(clang_stmt_printer(lhs)); 267 | if (pair.second == false) { 268 | return false;; 269 | } 270 | } 271 | return true; 272 | } 273 | -------------------------------------------------------------------------------- /clang_utility_functions.h: -------------------------------------------------------------------------------- 1 | #ifndef CLANG_UTILITY_FUNCTIONS_H_ 2 | #define CLANG_UTILITY_FUNCTIONS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "clang/AST/Decl.h" 9 | #include "clang/AST/Stmt.h" 10 | 11 | /// Enum class to represent Variable type 12 | /// PACKET is for the names of all packet fields (in identifier_census) 13 | /// and packet fields prefixed with "pkt." (in gen_var_list) 14 | /// STATE_SCALAR is for all scalar state variables 15 | /// STATE_ARRAY is for all array state variables 16 | /// FUNCTION_PARAMETER is for the function name and function parameters 17 | enum class VariableType {PACKET, STATE_SCALAR, STATE_ARRAY, FUNCTION_PARAMETER}; 18 | 19 | /// Map from VariableType to bool, 20 | /// denoting whether a variable should be selected or not 21 | typedef std::map VariableTypeSelector; 22 | 23 | /// General puprose printer for clang stmts 24 | /// Everything executable subclasses from clang::Stmt, including clang::Expr 25 | std::string clang_stmt_printer(const clang::Stmt * stmt); 26 | 27 | /// Print name of a value declaration (http://clang.llvm.org/doxygen/classclang_1_1ValueDecl.html#details) 28 | /// We use it to print: 29 | /// 1. Field names within a packet structure. 30 | /// 2. Packet parameter names passed to packet functions. 31 | /// 3. State variable names. 32 | std::string clang_value_decl_printer(const clang::ValueDecl * value_decl); 33 | 34 | /// Print all kinds of clang declarations 35 | /// This is best used when we want to pass through certain statements unchanged. 36 | /// It prints the entire declaration (along with the definition if it accompanies the declaration). 37 | std::string clang_decl_printer(const clang::Decl * decl); 38 | 39 | /// Is this a packet function: does it have struct Packet as an argument 40 | bool is_packet_func(const clang::FunctionDecl * func_decl); 41 | 42 | /// Return the current set of identifiers 43 | /// so that we can generate unique names afterwards 44 | std::set identifier_census(const clang::TranslationUnitDecl * decl, 45 | const VariableTypeSelector & var_selector = 46 | {{VariableType::PACKET, true}, {VariableType::FUNCTION_PARAMETER, true}, {VariableType::STATE_SCALAR, true}, {VariableType::STATE_ARRAY, true}}); 47 | 48 | /// Determine all variables (either packet or state) used within a clang::Stmt, 49 | std::set gen_var_list(const clang::Stmt * stmt, 50 | const VariableTypeSelector & var_selector = 51 | {{VariableType::PACKET, true}, 52 | {VariableType::STATE_SCALAR, true}, 53 | {VariableType::STATE_ARRAY, true}} 54 | ); 55 | 56 | /// Generate scalar function declarations, 57 | /// including function definitions if provided 58 | std::string generate_scalar_func_def(const clang::FunctionDecl * func_decl); 59 | 60 | /// List out all packet fields in a translation unit, 61 | /// by first calling identifier_census and then serializing the result 62 | std::string gen_pkt_fields(const clang::TranslationUnitDecl * tu_decl); 63 | 64 | /// Replace a particular variable 65 | /// using a replacement map 66 | std::string replace_var_helper(const clang::Expr * expr, const std::map & repl_map); 67 | 68 | /// Replace a specific string with a new string within expr 69 | /// Using var_selector to determine which variables to replace 70 | std::string replace_vars(const clang::Expr * expr, 71 | const std::map & repl_map, 72 | const VariableTypeSelector & var_selector); 73 | 74 | /// Check if program is in SSA form 75 | bool is_in_ssa(const clang::CompoundStmt * compound_stmt); 76 | 77 | #endif // CLANG_UTILITY_FUNCTIONS_H_ 78 | -------------------------------------------------------------------------------- /compiler_pass.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPILER_PASS_H_ 2 | #define COMPILER_PASS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "clang/AST/ASTConsumer.h" 13 | #include "clang/AST/ASTContext.h" 14 | #include "clang/Basic/Diagnostic.h" 15 | #include "clang/Basic/FileManager.h" 16 | #include "clang/Basic/SourceManager.h" 17 | #include "clang/Basic/TargetOptions.h" 18 | #include "clang/Basic/TargetInfo.h" 19 | #include "clang/Frontend/CompilerInstance.h" 20 | #include "clang/Lex/Preprocessor.h" 21 | #include "clang/Parse/ParseAST.h" 22 | #include "llvm/Support/Host.h" 23 | #include "llvm/ADT/SmallString.h" 24 | 25 | #include "third_party/temp_file.hh" 26 | #include "third_party/assert_exception.h" 27 | 28 | #include "clang_utility_functions.h" 29 | 30 | 31 | /// Convenience typedef for transforming a translation unit 32 | /// This is where all the interesting stuff happens: if conversion, pipelining, etc. 33 | /// TranslationUnitDecl is the clang class for traversing the AST of a parsed translation unit. 34 | /// The return type is a string, because clang doesn't allow you to construct or modify ASTs. 35 | template 36 | using Transformer = std::function; 37 | 38 | /// Abstract base class for a pass of the Clang compiler, 39 | /// a function object that takes a string corresponding to a translation unit 40 | /// and returns another string as another translation unit. 41 | /// I would have preferred to use a more articulate representation 42 | /// such as an Abstract Syntax Tree to represent the program. 43 | /// Unfortunately, clang has very poor support for creating ASTs. It's 44 | /// best to treat ASTs as intermediate read-only form. 45 | class CompilerPass { 46 | public: 47 | /// Run the compiler pass on a string and return a new string 48 | virtual std::string operator()(const std::string &) = 0; 49 | 50 | /// Virtual destructor to shut up g++ 51 | virtual ~CompilerPass() {}; 52 | }; 53 | 54 | /// Single pass over a translation unit. 55 | /// Most code here is based on 56 | /// http://eli.thegreenplace.net/2012/06/08/basic-source-to-source-transformation-with-clang 57 | template 58 | class SinglePass : public CompilerPass { 59 | public: 60 | /// Construct a SinglePass using a Transformer object 61 | SinglePass(const Transformer & t_transformer, const Args... t_args); 62 | 63 | /// Execute SinglePass object overriding function call operator 64 | std::string operator() (const std::string & string_to_parse) final override; 65 | 66 | private: 67 | class MyASTConsumer : public clang::ASTConsumer { 68 | public: 69 | MyASTConsumer(const Transformer & t_transformer, const Args... t_args) : transformer_(t_transformer), args_(std::make_tuple(t_args...)) {}; 70 | 71 | /// Override the method that gets called for the translation unit 72 | virtual void HandleTranslationUnit(clang::ASTContext & context) override { 73 | const auto * tu_decl = context.getTranslationUnitDecl(); 74 | assert_exception(llvm::isa(tu_decl)); 75 | 76 | // Bind first argument alone (http://stackoverflow.com/questions/11902840/binding-member-functions-in-a-variadic-fashion) 77 | auto partial_fn = [tu_decl, this] (const Args... t_args) { return this->transformer_(tu_decl, t_args...); }; 78 | 79 | // Now pack the args_ tuple object into a parameter pack (http://en.cppreference.com/w/cpp/experimental/apply) 80 | output_ = std::experimental::apply(partial_fn, args_); 81 | } 82 | 83 | /// Get previously stored output 84 | auto output() const { return output_; } 85 | private: 86 | /// Temporary holding area for output 87 | std::string output_ = {}; 88 | 89 | /// Transformer function 90 | Transformer transformer_; 91 | 92 | /// Temporary storage for arguments 93 | std::tuple args_; 94 | }; 95 | /// Instantiate MyASTConsumer using supplied transformer 96 | MyASTConsumer my_ast_consumer_; 97 | 98 | /// SimpleDiag, TODO: Add FixIt hints and other goodies to make it comparable to gcc/clang 99 | class SimpleDiag : public clang::DiagnosticConsumer { 100 | void HandleDiagnostic(clang::DiagnosticsEngine::Level DiagLevel __attribute__((unused)), 101 | const clang::Diagnostic & diagnostic) override { 102 | llvm::SmallString<100> OutStr; 103 | diagnostic.FormatDiagnostic(OutStr); 104 | throw std::logic_error("Clang compiler error: " + std::string(OutStr.str())); 105 | } 106 | }; 107 | SimpleDiag simple_diag_ = {}; 108 | 109 | /// TempFile to hold string to be parsed 110 | /// This is really a workaround for the fact that the 111 | /// entry points into clang's libraries are files on disk 112 | TempFile temp_file_; 113 | }; 114 | 115 | template 116 | SinglePass::SinglePass(const Transformer & t_transformer, const Args... t_args) 117 | : my_ast_consumer_(t_transformer, t_args...), 118 | temp_file_("tmp", ".c") {} 119 | 120 | template 121 | std::string SinglePass::operator()(const std::string & string_to_parse) { 122 | // Write string_to_parse into temp_file_ 123 | temp_file_.write(string_to_parse); 124 | 125 | // clang::CompilerInstance will hold the instance of the Clang compiler for us, 126 | // managing the various objects needed to run the compiler. 127 | clang::CompilerInstance TheCompInst; 128 | TheCompInst.getDiagnosticOpts().Warnings.emplace_back("all"); 129 | TheCompInst.createDiagnostics(& simple_diag_, false); 130 | TheCompInst.getLangOpts().CPlusPlus = 0; 131 | TheCompInst.getLangOpts().LineComment = 1; 132 | 133 | // Initialize target info with the default triple for our platform. 134 | auto TO = std::make_shared(); 135 | TO->Triple = llvm::sys::getDefaultTargetTriple(); 136 | clang::TargetInfo *TI = 137 | clang::TargetInfo::CreateTargetInfo(TheCompInst.getDiagnostics(), TO); 138 | TheCompInst.setTarget(TI); 139 | 140 | TheCompInst.createFileManager(); 141 | clang::FileManager &FileMgr = TheCompInst.getFileManager(); 142 | TheCompInst.createSourceManager(FileMgr); 143 | clang::SourceManager &SourceMgr = TheCompInst.getSourceManager(); 144 | TheCompInst.createPreprocessor(clang::TU_Module); 145 | TheCompInst.createASTContext(); 146 | 147 | // Set the main file handled by the source manager to the input file. 148 | const clang::FileEntry *FileIn = FileMgr.getFile(temp_file_.name().c_str()); 149 | SourceMgr.setMainFileID( 150 | SourceMgr.createFileID(FileIn, clang::SourceLocation(), clang::SrcMgr::C_User)); 151 | TheCompInst.getDiagnosticClient().BeginSourceFile( 152 | TheCompInst.getLangOpts(), &TheCompInst.getPreprocessor()); 153 | 154 | // Parse the file to AST, registering my_ast_consumer_ as the AST consumer. 155 | ParseAST(TheCompInst.getPreprocessor(), &my_ast_consumer_, 156 | TheCompInst.getASTContext()); 157 | 158 | return my_ast_consumer_.output(); 159 | } 160 | 161 | // Run a SinglePass repeatedly until the output converges to a fixed point 162 | template 163 | class FixedPointPass : public CompilerPass { 164 | public: 165 | /// Construct a FixedPointPass 166 | FixedPointPass(const ArgType & arg) 167 | : arg_(arg) {} 168 | 169 | /// Execute FixedPointPass object 170 | std::string operator() (const std::string & string_to_parse) final override { 171 | std::string old_output = string_to_parse; 172 | std::string new_output = ""; 173 | while (true) { 174 | new_output = PassType(arg_)(old_output); 175 | if (new_output == old_output) break; 176 | old_output = new_output; 177 | } 178 | return new_output; 179 | } 180 | 181 | private: 182 | /// Store argument to pass for future use 183 | /// TODO: Figure out how to store a parameter pack in the future. 184 | /// Or maybe don't do something that crazy ... 185 | ArgType arg_; 186 | }; 187 | 188 | // Set of passes that are to be run in a particular order 189 | class CompoundPass : public CompilerPass { 190 | public: 191 | /// Construct a CompoundPass 192 | CompoundPass(const std::vector> & t_transforms) 193 | : transforms_(t_transforms) {} 194 | 195 | /// Execute CompoundPass object 196 | std::string operator() (const std::string & string_to_parse) final override { 197 | std::string output = string_to_parse; 198 | for (const auto & transform : transforms_) { 199 | output = SinglePass<>(transform)(output); 200 | } 201 | return output; 202 | } 203 | 204 | private: 205 | /// Store t_transorms for future use 206 | std::vector> transforms_; 207 | }; 208 | 209 | // Vector of Transform functions, each transform function runs within a CompilerPass 210 | typedef std::vector TransformVector; 211 | 212 | #endif // COMPILER_PASS_H_ 213 | -------------------------------------------------------------------------------- /configure.ac: -------------------------------------------------------------------------------- 1 | AC_PREREQ([2.61]) 2 | AC_INIT([domino], [0.1], [anirudh@csail.mit.edu]) 3 | AM_INIT_AUTOMAKE([foreign subdir-objects]) 4 | AC_CONFIG_SRCDIR([ssa.cc]) 5 | AC_CONFIG_HEADERS([config.h]) 6 | 7 | # Checks for programs. 8 | AC_PROG_CXX 9 | AC_PROG_RANLIB 10 | 11 | # Add picky CXXFLAGS 12 | CPPFLAGS="-std=c++14 -pthread -D__STDC_LIMIT_MACROS -D__STDC_CONSTANT_MACROS -D_GLIBCXX_USE_CXX11_ABI=0" 13 | PICKY_CXXFLAGS="-pedantic -Wconversion -Wsign-conversion -Wall -Wextra -Weffc++ -Werror" 14 | AC_SUBST([PICKY_CXXFLAGS]) 15 | 16 | # Checks for header files. 17 | AC_LANG_PUSH(C++) 18 | 19 | # clang-3.5 development libraries and headers 20 | AC_ARG_VAR(CLANG_DEV_LIBS, Root directory of Clang development libraries and headers) 21 | AC_SUBST([CLANG_DEV_LIBS]) 22 | 23 | # Search for g++, because we shell out to g++ when generating pisa code 24 | AC_ARG_VAR([GPLUSPLUS], [path to g++]) 25 | AC_PATH_PROGS([GPLUSPLUS], [g++], [no], [$PATH$PATH_SEPARATOR/sbin$PATH_SEPARATOR/usr/sbin$PATH_SEPARATOR/bin$PATH_SEPARATOR/usr/bin]) 26 | if test "$GPLUSPLUS" = "no"; then 27 | AC_MSG_ERROR([g++ is required]) 28 | fi 29 | AC_DEFINE_UNQUOTED([GPLUSPLUS], ["$GPLUSPLUS"], [path to g++]) 30 | 31 | AC_CHECK_HEADERS([algorithm array cmath queue \ 32 | cstdio string sys/stat.h sys/types.h ctime tuple unistd.h unordered_map \ 33 | utility vector], [], [AC_MSG_ERROR([Missing header file])]) 34 | 35 | # Checks for typedefs, structures, and compiler characteristics. 36 | AC_TYPE_SIZE_T 37 | AC_TYPE_UINT64_T 38 | AC_LANG_POP(C++) 39 | 40 | AC_CONFIG_FILES([Makefile 41 | third_party/Makefile 42 | tests/Makefile]) 43 | 44 | AC_OUTPUT 45 | -------------------------------------------------------------------------------- /cse.cc: -------------------------------------------------------------------------------- 1 | #include "cse.h" 2 | 3 | #include 4 | 5 | #include "third_party/assert_exception.h" 6 | 7 | #include "clang_utility_functions.h" 8 | #include "pkt_func_transform.h" 9 | 10 | using namespace clang; 11 | 12 | std::string cse_transform(const TranslationUnitDecl * tu_decl) { 13 | return pkt_func_transform(tu_decl, cse_body); 14 | } 15 | 16 | std::pair> cse_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))) { 17 | std::string transformed_body = ""; 18 | std::map assigned; 19 | 20 | for (const auto * child : function_body->children()) { 21 | assert_exception(isa(child)); 22 | assert_exception(dyn_cast(child)->isAssignmentOp()); 23 | const auto * bin_op = dyn_cast(child); 24 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 25 | const auto * rhs = bin_op->getRHS()->IgnoreParenImpCasts(); 26 | 27 | if (assigned.find(clang_stmt_printer(lhs)) == assigned.end()) { 28 | assigned[clang_stmt_printer(lhs)] = true; 29 | transformed_body += clang_stmt_printer(lhs) + "=" + clang_stmt_printer(rhs) + ";"; 30 | } 31 | } 32 | return std::make_pair("{" + transformed_body + "}", std::vector()); 33 | } 34 | -------------------------------------------------------------------------------- /cse.h: -------------------------------------------------------------------------------- 1 | #ifndef CSE_H_ 2 | #define CSE_H_ 3 | 4 | #include 5 | #include 6 | #include "clang/AST/Expr.h" 7 | 8 | // Entry point to Common Subexpression Elimination (CSE) 9 | std::string cse_transform(const clang::TranslationUnitDecl * tu_decl); 10 | 11 | // Main function for CSE from a function body 12 | std::pair> cse_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__ ((unused))); 13 | 14 | #endif // CSE_H_ 15 | -------------------------------------------------------------------------------- /csi.cc: -------------------------------------------------------------------------------- 1 | #include "csi.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "third_party/assert_exception.h" 7 | 8 | #include "clang_utility_functions.h" 9 | #include "pkt_func_transform.h" 10 | #include "unique_identifiers.h" 11 | 12 | using namespace clang; 13 | using std::placeholders::_1; 14 | using std::placeholders::_2; 15 | 16 | std::string csi_transform(const TranslationUnitDecl * tu_decl) { 17 | return pkt_func_transform(tu_decl, csi_body); 18 | } 19 | 20 | std::pair> csi_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))) { 21 | std::string transformed_body = ""; 22 | 23 | // Vector of newly created packet temporaries 24 | std::vector new_decls = {}; 25 | 26 | // Check that it's in ssa. 27 | assert_exception(is_in_ssa(function_body)); 28 | 29 | // Populate variable to expression map for all packet variables 30 | VarMap var_map; 31 | for (const auto * child : function_body->children()) { 32 | assert_exception(isa(child)); 33 | const auto * bin_op = dyn_cast(child); 34 | assert_exception(bin_op->isAssignmentOp()); 35 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 36 | // Consider only packet variables. 37 | if (isa(lhs)) { 38 | assert_exception(var_map.find(clang_stmt_printer(dyn_cast(lhs))) == var_map.end()); 39 | var_map[clang_stmt_printer(dyn_cast(lhs))] = bin_op->getRHS()->IgnoreParenImpCasts(); 40 | } 41 | } 42 | 43 | // Now check for equality among all MemberExprs. 44 | // Scan MemberExprs in lexical order so that we can identify the last of them. 45 | // Add equal MemberExprs to a vector of vectors to create equivalence partitions. 46 | std::vector> partitions; 47 | for (const auto * child : function_body->children()) { 48 | // Extract packet variable 49 | assert_exception(isa(child)); 50 | const auto * bin_op = dyn_cast(child); 51 | assert_exception(bin_op->isAssignmentOp()); 52 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 53 | const std::string pkt_var = clang_stmt_printer(lhs); 54 | 55 | // Check if this packet variable to equal to any packet variable 56 | // in any equivalence partition. If so, add it to that partition 57 | // If no such partition exists, create a new partition. 58 | bool found_partition = false; 59 | for (auto & partition : partitions) { 60 | if (std::find_if(partition.begin(), partition.end(), 61 | [pkt_var, & var_map] (const auto & var) { return check_pkt_var(var, pkt_var, var_map); }) 62 | != partition.end()) { 63 | partition.emplace_back(pkt_var); 64 | found_partition = true; 65 | break; 66 | } 67 | } 68 | 69 | // Create a new partition for this variable alone 70 | if (found_partition == false) { 71 | partitions.emplace_back(std::vector({pkt_var})); 72 | } 73 | } 74 | 75 | // Now use one exemplar variable for each partition, by creating a replacement map for the rest. 76 | // We pick the last one in lexical order to preserve SSAs renames 77 | std::map repl_map; 78 | for (const auto & partition : partitions) { 79 | if (partition.size() > 1) { 80 | // Set it up so that all variables in the equivalence class 81 | // are renamed to the last variable (this preserves the SSA renames for jayhawk). 82 | std::string repl_candidate = partition.back(); 83 | for (const auto & pkt_var : partition) { 84 | repl_map[pkt_var] = repl_candidate; 85 | } 86 | } 87 | } 88 | 89 | // Now carry out replacements. 90 | for (const auto * child : function_body->children()) { 91 | assert_exception(isa(child)); 92 | assert_exception(dyn_cast(child)->isAssignmentOp()); 93 | transformed_body += replace_vars(dyn_cast(child)->getLHS(), repl_map, 94 | {{VariableType::STATE_SCALAR, false}, {VariableType::STATE_ARRAY, false}, {VariableType::PACKET, true}}) + 95 | " = " + 96 | replace_vars(dyn_cast(child)->getRHS(), repl_map, 97 | {{VariableType::STATE_SCALAR, false}, {VariableType::STATE_ARRAY, false}, {VariableType::PACKET, true}}) + 98 | ";"; 99 | } 100 | 101 | return std::make_pair("{" + transformed_body + "}", std::vector()); 102 | } 103 | 104 | bool check_pkt_var(const std::string & pkt1, const std::string & pkt2, 105 | const VarMap & var_map) { 106 | if (pkt1 == pkt2) { 107 | // Base case, string comparison 108 | return true; 109 | } else if ((var_map.find(pkt1) != var_map.end()) and (var_map.find(pkt2) != var_map.end())) { 110 | // Recursion (strictly this is mutual recursion because check_expr calls check_bin_op or check_un_op, 111 | // which then calls check_pkt_var. 112 | return check_expr(var_map.at(pkt1), var_map.at(pkt2), var_map); 113 | } else { 114 | // If either doesn't exist in var maps (it's an input variable), just return false. 115 | return false; 116 | } 117 | } 118 | 119 | bool check_expr(const Expr * expr1, const Expr * expr2, 120 | const VarMap & var_map) { 121 | if (isa(expr1) and isa(expr2)) { 122 | return check_bin_op(dyn_cast(expr1), dyn_cast(expr2), var_map); 123 | } else if (isa(expr1) and isa(expr2)) { 124 | // TODO: This string compare is a hack, but should work. 125 | return clang_stmt_printer(expr1) == clang_stmt_printer(expr2); 126 | } else if (isa(expr1) and isa(expr2)) { 127 | return check_un_op(dyn_cast(expr1), dyn_cast(expr2), var_map); 128 | } else if (isa(expr1) and isa(expr2)) { 129 | return check_call_expr(dyn_cast(expr1), dyn_cast(expr2), var_map); 130 | } else { 131 | // TODO: We don't check conditional ops for equality right now. 132 | // This is correct, although pessimal. 133 | return false; 134 | } 135 | } 136 | 137 | bool check_call_expr(const CallExpr * ce1, const CallExpr * ce2, 138 | const VarMap & var_map) { 139 | unsigned int num_args1 = ce1->getNumArgs(); 140 | unsigned int num_args2 = ce2->getNumArgs(); 141 | 142 | // If number of arguments isn't the same, return right away. 143 | if (num_args1 != num_args2) { 144 | return false; 145 | } 146 | 147 | // If function names aren't the same, return right away. 148 | if (clang_stmt_printer(ce1->getCallee()) != clang_stmt_printer(ce2->getCallee())) { 149 | return false; 150 | } 151 | 152 | assert_exception(num_args1 == num_args2); 153 | for (uint8_t i = 0; i < num_args1; i++) { 154 | const auto * arg1 = ce1->getArg(i)->IgnoreParenImpCasts(); 155 | assert_exception(isa(arg1)); 156 | 157 | const auto * arg2 = ce2->getArg(i)->IgnoreParenImpCasts(); 158 | assert_exception(isa(arg2)); 159 | 160 | if (not check_pkt_var(clang_stmt_printer(arg1), clang_stmt_printer(arg2), 161 | var_map)) { 162 | return false; 163 | } 164 | } 165 | 166 | // Everything checks out 167 | return true; 168 | } 169 | 170 | bool check_un_op(const UnaryOperator * un1, const UnaryOperator * un2, 171 | const VarMap & var_map) { 172 | // If opcode isn't equal return right away 173 | if (un1->getOpcode() != un2->getOpcode()) return false; 174 | 175 | // Extract arguments 176 | const auto * un1_sub_expr = un1->getSubExpr()->IgnoreParenImpCasts(); 177 | const auto * un2_sub_expr = un2->getSubExpr()->IgnoreParenImpCasts(); 178 | assert_exception(isa(un1_sub_expr) or isa(un1_sub_expr)); 179 | assert_exception(isa(un2_sub_expr) or isa(un2_sub_expr)); 180 | 181 | // Recursively check for equality 182 | if (isa(un1_sub_expr) and isa(un2_sub_expr)) return check_pkt_var(clang_stmt_printer(un1_sub_expr), clang_stmt_printer(un2_sub_expr), var_map); 183 | else if (isa(un1_sub_expr) and isa(un2_sub_expr)) return clang_stmt_printer(un1_sub_expr) == clang_stmt_printer(un2_sub_expr); 184 | else return false; 185 | } 186 | 187 | bool check_bin_op(const BinaryOperator * bin1, const BinaryOperator * bin2, 188 | const VarMap & var_map) { 189 | // If opcodes don't match up, return right away. 190 | if (bin1->getOpcode() != bin2->getOpcode()) return false; 191 | 192 | // Extract arguments 193 | const auto * lhs1 = bin1->getLHS()->IgnoreParenImpCasts(); 194 | const auto * lhs2 = bin2->getLHS()->IgnoreParenImpCasts(); 195 | assert_exception(isa(lhs1) or isa(lhs1) or isa(lhs1) or isa(lhs1)); 196 | assert_exception(isa(lhs2) or isa(lhs2) or isa(lhs2) or isa(lhs2)); 197 | 198 | const auto * rhs1 = bin1->getRHS()->IgnoreParenImpCasts(); 199 | const auto * rhs2 = bin2->getRHS()->IgnoreParenImpCasts(); 200 | assert_exception(isa(rhs1) or isa(rhs1) or isa(rhs1) or isa(rhs1)); 201 | assert_exception(isa(rhs2) or isa(rhs2) or isa(rhs2) or isa(rhs2)); 202 | 203 | // Make sure the arguments are of the same type 204 | if (std::string(lhs1->getStmtClassName()) != std::string(lhs2->getStmtClassName())) return false; 205 | if (std::string(rhs1->getStmtClassName()) != std::string(rhs2->getStmtClassName())) return false; 206 | 207 | if (isa(lhs1) and isa(lhs2) and isa(rhs1) and isa(rhs2)) { 208 | return check_pkt_var(clang_stmt_printer(dyn_cast(lhs1)), clang_stmt_printer(dyn_cast(lhs2)), var_map) and 209 | check_pkt_var(clang_stmt_printer(dyn_cast(rhs1)), clang_stmt_printer(dyn_cast(rhs2)), var_map); 210 | } else if (isa(lhs1) and isa(lhs2) and isa(rhs1) and isa(rhs2)) { 211 | return check_pkt_var(clang_stmt_printer(dyn_cast(lhs1)), clang_stmt_printer(dyn_cast(lhs2)), var_map) and 212 | (clang_stmt_printer(rhs1) == clang_stmt_printer(rhs2)); 213 | } else if (isa(lhs1) and isa(lhs2) and isa(rhs1) and isa(rhs2)) { 214 | return (clang_stmt_printer(lhs1) == clang_stmt_printer(lhs2)) and 215 | check_pkt_var(clang_stmt_printer(dyn_cast(rhs1)), clang_stmt_printer(dyn_cast(rhs2)), var_map); 216 | } else if (isa(lhs1) and isa(lhs2) and isa(rhs1) and isa(rhs2)) { 217 | return (clang_stmt_printer(lhs1) == clang_stmt_printer(lhs2)) and 218 | (clang_stmt_printer(rhs1) == clang_stmt_printer(rhs2)); 219 | } else if (isa(lhs1) and isa(lhs2) and isa(rhs1) and isa(rhs2)) { 220 | return check_call_expr(dyn_cast(lhs1), dyn_cast(lhs2), var_map) and 221 | (clang_stmt_printer(rhs1) == clang_stmt_printer(rhs2)); 222 | } else { 223 | return false; 224 | } 225 | } 226 | -------------------------------------------------------------------------------- /csi.h: -------------------------------------------------------------------------------- 1 | #ifndef CSI_H_ 2 | #define CSI_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "clang/AST/Expr.h" 9 | 10 | /// Map from variable names to their expressions 11 | /// This is preprocessing so that we can compute the ASTs for each variable. 12 | typedef std::map VarMap; 13 | 14 | /// Common subexpression identification (CSI) entry point 15 | std::string csi_transform(const clang::TranslationUnitDecl * tu_decl); 16 | 17 | /// Main function for CSI that rewrites function body by 18 | /// identifying common sub expressions. 19 | std::pair> csi_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))); 20 | 21 | /// Check if two packet variables are equal recursively 22 | /// by comparing their expression trees. 23 | /// Execution starts at check_pkt_var, which calls check_expr, which in turn 24 | /// calls one of check_bin_op or check_un_op, 25 | /// which could then recursively call check_pkt_var till it bottoms out. 26 | bool check_pkt_var(const std::string & pkt1, const std::string & pkt2, 27 | const VarMap & var_map); 28 | 29 | /// Check if two expressions are equal by delegating 30 | /// to one of check_bin_op or check_un_op. 31 | /// In the future, we can add Conditionals, CallExpr, ArraySubscriptExpr 32 | /// and so on. 33 | bool check_expr(const clang::Expr * expr1, const clang::Expr * expr2, 34 | const VarMap & var_map); 35 | 36 | /// Check if two function call expressions are equal recursively 37 | /// by calling check_pkt_var on corresponding pairs of arguments. 38 | bool check_call_expr(const clang::CallExpr * ce1, const clang::CallExpr * ce2, 39 | const VarMap & var_map); 40 | 41 | /// Check if two unary operators are equal recursively 42 | /// by calling check_pkt_var on the sub expression of the unary operator 43 | bool check_un_op(const clang::UnaryOperator * un1, const clang::UnaryOperator * un2, 44 | const VarMap & var_map); 45 | 46 | /// Check if two binary ops are equal recursively 47 | /// by calling check_pkt_var on each of the operands. 48 | bool check_bin_op(const clang::BinaryOperator * bin1, const clang::BinaryOperator * bin2, 49 | const VarMap & var_map); 50 | 51 | #endif // CSI_H_ 52 | -------------------------------------------------------------------------------- /dep.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 16 | 22 | 27 | 28 | 34 | 39 | 40 | 46 | 51 | 52 | 58 | 63 | 64 | 70 | 75 | 76 | 82 | 87 | 88 | 94 | 99 | 100 | 106 | 111 | 112 | 118 | 123 | 124 | 130 | 135 | 136 | 142 | 147 | 148 | 154 | 159 | 160 | 161 | 163 | 164 | 166 | image/svg+xml 167 | 169 | 170 | 171 | 172 | 173 | 175 | pkt.last_time0 = last_time; pkt.next_hop0 = next_hop; 195 | pkt.tmp1 = pkt.sport * pkt.dport; 204 | pkt.new_hop = pkt.tmp1 % 10; 213 | pkt.tmp2 = pkt.arrival_time - pkt.last_time0; 222 | pkt.tmp0 = pkt.tmp2 > 5; 231 | pkt.next_hop00 = pkt.tmp0 ? (pkt.new_hop) : pkt.next_hop0; 240 | pkt.last_time00 = pkt.arrival_time; 249 | pkt.next_hop = pkt.next_hop00;; 258 | last_time = pkt.last_time00; 267 | next_hop = pkt.next_hop00; 276 | 280 | 284 | 288 | 292 | 296 | 300 | 304 | 308 | 312 | 316 | 320 | 324 | 328 | 329 | 330 | -------------------------------------------------------------------------------- /desugar_compound_assignment.cc: -------------------------------------------------------------------------------- 1 | #include "desugar_compound_assignment.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | #include "clang_utility_functions.h" 5 | 6 | using namespace clang; 7 | 8 | BinaryOperator::Opcode DesugarCompAssignment::get_underlying_op(const BinaryOperator::Opcode & comp_asgn_op) const { 9 | // This is a clang quirk: 10 | // opcodes for all operators with compound assignment variants 11 | // aren't contiguous. 12 | if (comp_asgn_op >= BinaryOperator::Opcode::BO_MulAssign and 13 | comp_asgn_op <= BinaryOperator::Opcode::BO_ShrAssign) { 14 | return static_cast(static_cast(comp_asgn_op) - 19); 15 | } else if (comp_asgn_op >= BinaryOperator::Opcode::BO_AndAssign and 16 | comp_asgn_op <= BinaryOperator::Opcode::BO_OrAssign) { 17 | return static_cast(static_cast(comp_asgn_op) - 13); 18 | } else { 19 | throw std::logic_error("Got opcode that get_underlying_op can't handle: " + std::string(BinaryOperator::getOpcodeStr(comp_asgn_op))); 20 | } 21 | } 22 | 23 | std::string DesugarCompAssignment::ast_visit_bin_op(const BinaryOperator * bin_op) { 24 | assert_exception(bin_op); 25 | std::string ret; 26 | if (isa(bin_op)) { 27 | // Handle compound assignments alone 28 | return ast_visit_stmt(bin_op->getLHS()) + "=" + ast_visit_stmt(bin_op->getLHS()) 29 | + std::string(BinaryOperator::getOpcodeStr(get_underlying_op(bin_op->getOpcode()))) 30 | + ast_visit_stmt(bin_op->getRHS()); 31 | } else { 32 | // Delegate to default base class method 33 | return AstVisitor::ast_visit_bin_op(bin_op); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /desugar_compound_assignment.h: -------------------------------------------------------------------------------- 1 | #ifndef DESUGAR_COMPOUND_ASSIGNMENT_H_ 2 | #define DESUGAR_COMPOUND_ASSIGNMENT_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | class DesugarCompAssignment : public AstVisitor { 7 | protected: 8 | /// Desugar compound assignment, by replacing: 9 | /// E1 op= E2 with E1 = E1 op E2 10 | /// This is almost correct (quoting from cppreference.com) 11 | /// "except that the expression E1 is evaluated only once 12 | ///and that it behaves as a single operation with respect 13 | ///to indeterminately-sequenced function calls (e.g. in f(a+= b, g()), 14 | /// the += is either not started at all or is 15 | ///completed as seen from inside g())." 16 | /// I don't think that's reasonable code anyway, so for 17 | /// now I am ignoring it (TODO). 18 | std::string ast_visit_bin_op(const clang::BinaryOperator * bin_op) override; 19 | 20 | private: 21 | /// get_underlying_op from a compound assignment operator 22 | clang::BinaryOperator::Opcode get_underlying_op(const clang::BinaryOperator::Opcode & comp_asgn_op) const; 23 | }; 24 | 25 | #endif // DESUGAR_COMPOUND_ASSIGNMENT_H_ 26 | -------------------------------------------------------------------------------- /domino.cc: -------------------------------------------------------------------------------- 1 | #include "if_conversion_handler.h" 2 | #include "ssa.h" 3 | #include "expr_prop.h" 4 | #include "partitioning.h" 5 | #include "stateful_flanks.h" 6 | #include "expr_flattener_handler.h" 7 | #include "algebraic_simplifier.h" 8 | #include "bool_to_int.h" 9 | #include "desugar_compound_assignment.h" 10 | #include "int_type_checker.h" 11 | #include "array_validator.h" 12 | #include "validator.h" 13 | #include "redundancy_remover.h" 14 | #include "sketch_backend.h" 15 | #include "cse.h" 16 | #include "csi.h" 17 | #include "gen_used_fields.h" 18 | 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #include "third_party/assert_exception.h" 28 | 29 | #include "util.h" 30 | #include "pkt_func_transform.h" 31 | #include "compiler_pass.h" 32 | 33 | // For the _1, and _2 in std::bind 34 | // (Partial Function Application) 35 | using std::placeholders::_1; 36 | using std::placeholders::_2; 37 | 38 | // Convenience typedefs 39 | typedef std::string PassName; 40 | typedef std::function(void)> PassFunctor; 41 | typedef std::map PassFactory; 42 | typedef std::vector PassFunctorVector; 43 | 44 | // Both SinglePass and Transformer take a parameter pack as template 45 | // arguments to allow additional arguments to the function carrying out 46 | // the transformation (e.g., atom templates and pipeline width and depth in sketch_backend) 47 | // Unfortunately, you can't provide a default value for such parameter packs 48 | // As http://en.cppreference.com/w/cpp/language/template_parameters#Default_template_arguments says 49 | // "Defaults can be specified for any kind of template parameter (type, non-type, or template), but not to parameter packs." 50 | // This is my clunky workaround. 51 | typedef Transformer<> DefaultTransformer; 52 | typedef SinglePass<> DefaultSinglePass; 53 | 54 | // Map to store factory for all passes 55 | static PassFactory all_passes; 56 | 57 | void populate_passes() { 58 | // We need to explicitly call populate_passes instead of using an initializer list 59 | // to populate PassMap all_passes because initializer lists don't play well with move-only 60 | // types like unique_ptrs (http://stackoverflow.com/questions/9618268/initializing-container-of-unique-ptrs-from-initializer-list-fails-with-gcc-4-7) 61 | all_passes["cse"] =[] () { return std::make_unique>>(std::vector({csi_transform, cse_transform})); }; 62 | all_passes["redundancy_remover"]=[] () { return std::make_unique>(redundancy_remover_transform); }; 63 | all_passes["array_validator"] = [] () { return std::make_unique(std::bind(& ArrayValidator::ast_visit_transform, ArrayValidator(), _1)); }; 64 | all_passes["validator"] = [] () { return std::make_unique(std::bind(& Validator::ast_visit_transform, Validator(), _1)); }; 65 | all_passes["int_type_checker"] = [] () { return std::make_unique(std::bind(& IntTypeChecker::ast_visit_transform, IntTypeChecker(), _1)); }; 66 | all_passes["desugar_comp_asgn"]= [] () { return std::make_unique(std::bind(& DesugarCompAssignment::ast_visit_transform, DesugarCompAssignment(), _1)); }; 67 | all_passes["if_converter"] = [] () { return std::make_unique(std::bind(& IfConversionHandler::transform, IfConversionHandler(), _1)); }; 68 | all_passes["algebra_simplify"] = [] () { return std::make_unique(std::bind(& AlgebraicSimplifier::ast_visit_transform, AlgebraicSimplifier(), _1)); }; 69 | all_passes["bool_to_int"] = [] () { return std::make_unique(std::bind(& BoolToInt::ast_visit_transform, BoolToInt(), _1));}; 70 | all_passes["expr_flattener"] = [] () { return std::make_unique>(std::bind(& ExprFlattenerHandler::transform, ExprFlattenerHandler(), _1)); }; 71 | all_passes["expr_propagater"] = [] () { return std::make_unique(expr_prop_transform); }; 72 | all_passes["stateful_flanks"] = [] () { return std::make_unique(stateful_flank_transform); }; 73 | all_passes["ssa"] = [] () { return std::make_unique(ssa_transform); }; 74 | all_passes["echo"] = [] () { return std::make_unique(clang_decl_printer); }; 75 | all_passes["gen_used_fields"] = [] () { return std::make_unique(gen_used_field_transform); }; 76 | } 77 | 78 | PassFunctor get_pass_functor(const std::string & pass_name, const PassFactory & pass_factory) { 79 | if (pass_factory.find(pass_name) != pass_factory.end()) { 80 | // This is a bit misleading because we don't have deep const correctness 81 | return pass_factory.at(pass_name); 82 | } else { 83 | throw std::logic_error("Unknown pass " + pass_name); 84 | } 85 | } 86 | 87 | std::string all_passes_as_string(const PassFactory & pass_factory) { 88 | std::string ret; 89 | for (const auto & pass_pair : pass_factory) 90 | ret += pass_pair.first + "\n"; 91 | return ret; 92 | } 93 | 94 | void print_usage() { 95 | std::cerr << "Usage: domino " << std::endl; 96 | std::cerr << "List of passes: " << std::endl; 97 | std::cerr << all_passes_as_string(all_passes); 98 | } 99 | 100 | int main(int argc, const char **argv) { 101 | try { 102 | // Block out SIGINT, because we can't handle it properly 103 | signal(SIGINT, SIG_IGN); 104 | 105 | // Populate all passes 106 | populate_passes(); 107 | 108 | // Default pass list 109 | const auto default_pass_list = "int_type_checker,desugar_comp_asgn,if_converter,algebra_simplify,array_validator,stateful_flanks,ssa,expr_propagater,expr_flattener,cse"; 110 | 111 | if (argc >= 5) { 112 | // Get cmdline args 113 | const auto string_to_parse = file_to_str(std::string(argv[1])); 114 | const auto atom_template_file = std::string(argv[2]); 115 | const auto pipeline_depth = std::atoi(argv[3]); 116 | if (pipeline_depth <= 0) throw std::logic_error("Pipeline depth (" + std::string(argv[3]) + ") must be a positive integer"); 117 | const auto pipeline_width = std::atoi(argv[4]); 118 | if (pipeline_width <= 0) throw std::logic_error("Pipeline width (" + std::string(argv[4]) + ") must be a positive integer"); 119 | const auto run_sketch_prepocessor = ((argc >= 6) and (std::string(argv[5]) == "yes")) ? true : false; 120 | const auto pass_list = (argc == 7) ? split(std::string(argv[6]), ","): split(default_pass_list, ","); 121 | 122 | if (argc > 7) { 123 | print_usage(); 124 | return EXIT_FAILURE; 125 | } 126 | 127 | // add all preprocessing passes 128 | PassFunctorVector passes_to_run; 129 | for (const auto & pass_name : pass_list) passes_to_run.emplace_back(get_pass_functor(pass_name, all_passes)); 130 | 131 | // add partitioning pass 132 | passes_to_run.emplace_back([pipeline_depth, pipeline_width] () { return std::make_unique>(partitioning_transform, pipeline_depth, pipeline_width); } ); 133 | 134 | // add the passes for the sketch preprocessor and backend 135 | if (run_sketch_prepocessor) passes_to_run.emplace_back([] () { return std::make_unique(sketch_preprocessor); }); 136 | passes_to_run.emplace_back([atom_template_file] () { return std::make_unique>(sketch_backend_transform, atom_template_file); }); 137 | 138 | /// Process them one after the other 139 | std::cout << std::accumulate(passes_to_run.begin(), passes_to_run.end(), string_to_parse, [] (const auto & current_output, const auto & pass_functor __attribute__((unused))) 140 | { return (*pass_functor())(current_output); }); 141 | 142 | return EXIT_SUCCESS; 143 | } else { 144 | print_usage(); 145 | return EXIT_FAILURE; 146 | } 147 | } catch (const std::exception & e) { 148 | std::cerr << "Caught exception in main " << std::endl << e.what() << std::endl; 149 | return EXIT_FAILURE; 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /domino_to_chipmunk.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include "chipmunk_code_generator.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | #include "third_party/assert_exception.h" 11 | 12 | #include "util.h" 13 | #include "pkt_func_transform.h" 14 | #include "compiler_pass.h" 15 | 16 | // For the _1, and _2 in std::bind 17 | // (Partial Function Application) 18 | using std::placeholders::_1; 19 | 20 | void print_usage() { 21 | std::cerr << "Usage: domino_to_chipmunk " << std::endl; 22 | } 23 | 24 | int main(int argc, const char **argv) { 25 | try { 26 | // Block out SIGINT, because we can't handle it properly 27 | signal(SIGINT, SIG_IGN); 28 | 29 | if (argc == 2) { 30 | const auto string_to_parse = file_to_str(std::string(argv[1])); 31 | 32 | auto chipmunk_code_generator = SinglePass<>(std::bind(& ChipmunkCodeGenerator::ast_visit_transform, 33 | ChipmunkCodeGenerator(), _1)); 34 | 35 | std::cout << "/* \n// Original program: \n" + string_to_parse + " */\n" << std::endl; 36 | 37 | std::string sketch_program = chipmunk_code_generator(string_to_parse); 38 | std::cout << sketch_program << std::endl; 39 | 40 | return EXIT_SUCCESS; 41 | } else { 42 | print_usage(); 43 | return EXIT_FAILURE; 44 | } 45 | } catch (const std::exception & e) { 46 | std::cerr << "Caught exception in main " << std::endl << e.what() << std::endl; 47 | return EXIT_FAILURE; 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/bloom_filter.c: -------------------------------------------------------------------------------- 1 | #include "hashes.h" // For all the hash functions we need 2 | 3 | struct Packet { 4 | int sport; 5 | int dport; 6 | int member; 7 | int bloom_op; // bloom_op = 1 is test, bloom_op = 0 is add 8 | int filter1_idx; 9 | int filter2_idx; 10 | int filter3_idx; 11 | }; 12 | 13 | #define NUM_ENTRIES 256 14 | 15 | int filter1[NUM_ENTRIES] = {0}; 16 | int filter2[NUM_ENTRIES] = {0}; 17 | int filter3[NUM_ENTRIES] = {0}; 18 | 19 | void func(struct Packet pkt) { 20 | pkt.filter1_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 21 | pkt.filter2_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 22 | pkt.filter3_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 23 | if (pkt.bloom_op) { 24 | pkt.member = (filter1[pkt.filter1_idx] && 25 | filter2[pkt.filter2_idx] && 26 | filter3[pkt.filter3_idx]); 27 | } else { 28 | filter1[pkt.filter1_idx] = 1; 29 | filter2[pkt.filter2_idx] = 1; 30 | filter3[pkt.filter3_idx] = 1; 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /examples/codel.c: -------------------------------------------------------------------------------- 1 | #define TARGET 5 2 | #define INTERVAL 100 3 | 4 | int first_above_time = 0; 5 | int dropping = 0; 6 | int drop_next = 0; 7 | int count = 0; 8 | 9 | struct Packet { 10 | int tick; 11 | int enq_tick; 12 | int deq_tick; 13 | int ok_to_drop; 14 | int drop; 15 | int sojourn_time; 16 | }; 17 | 18 | int mysqrt(int val) { 19 | // TODO: Fill this up 20 | return val + 1; 21 | } 22 | 23 | void func(struct Packet pkt) { 24 | // dodeque method 25 | pkt.sojourn_time = pkt.deq_tick - pkt.enq_tick; 26 | if (pkt.sojourn_time < TARGET) { 27 | first_above_time = 0; 28 | } else { 29 | if (first_above_time == 0) { 30 | first_above_time = pkt.tick + INTERVAL; 31 | } else if (pkt.tick >= first_above_time) { 32 | pkt.ok_to_drop = 1; 33 | } 34 | } 35 | 36 | // Hysterisis 37 | if (dropping) { 38 | if (! pkt.ok_to_drop) { 39 | dropping = 0; 40 | } 41 | if (pkt.tick >= drop_next && dropping) { 42 | pkt.drop = 1; 43 | count += 1; 44 | drop_next = drop_next + INTERVAL / mysqrt(count); 45 | } 46 | } else if (pkt.ok_to_drop) { 47 | pkt.drop = 1; 48 | dropping = 1; 49 | if (count > 2 && pkt.tick - drop_next < 8 * INTERVAL) { 50 | count = count - 2; 51 | } else { 52 | count = 1; 53 | drop_next = pkt.tick + INTERVAL / mysqrt(count); 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /examples/codel.py: -------------------------------------------------------------------------------- 1 | TARGET = 5 2 | INTERVAL = 100 3 | first_above_time = 0 4 | dropping = 0 5 | drop_next = 0 6 | count = 0 7 | 8 | def func(pkt): 9 | pkt.sojourn_time = pkt.deq_tick - pkt.enq_tick 10 | if (pkt.sojourn_time < TARGET): 11 | first_above_time = 0 12 | else: 13 | if (first_above_time == 0): 14 | first_above_time = pkt.tick + INTERVAL 15 | elif (pkt.tick >= first_above_time): 16 | pkt.ok_to_drop = 1 17 | 18 | if (dropping): 19 | if (not pkt.ok_to_drop): 20 | dropping = 0 21 | if (pkt.tick >= drop_next and dropping): 22 | pkt.drop = 1 23 | count += 1 24 | drop_next = drop_next + INTERVAL / mysqrt(count) 25 | elif (pkt.ok_to_drop): 26 | pkt.drop = 1 27 | dropping = 1 28 | if (count > 2 and pkt.tick - drop_next < 8 * INTERVAL): 29 | count = count - 2 30 | else: 31 | count = 1 32 | drop_next = pkt.tick + INTERVAL / mysqrt(count) 33 | -------------------------------------------------------------------------------- /examples/conga.c: -------------------------------------------------------------------------------- 1 | // This is a packet reflected back from a destination leaf (d) 2 | // (confusingly called the src field in the packet) 3 | // informing the source leaf (s) what the max util (util) on 4 | // a particular path (path_id) to d is. 5 | // This helps s maintain at any point, the max. util on the best path to d 6 | // and the id of the path to d as well. 7 | struct Packet { 8 | int util; 9 | int path_id; 10 | int src; 11 | int best_path_util_idx; 12 | int best_path_idx; 13 | }; 14 | 15 | int best_path_util[256] = {100}; // Utilization information for each destination, Initially at 100% for everyone. 16 | int best_path[256] = {-1}; // Next hop / path information for each destination 17 | 18 | void func(struct Packet p) { 19 | p.best_path_util_idx = p.src < 0 ? 0 : p.src % 256; 20 | p.best_path_idx = p.src < 0 ? 0 : p.src % 256; 21 | if (p.util < best_path_util[p.best_path_util_idx]) { 22 | best_path_util[p.best_path_util_idx] = p.util; 23 | best_path[p.best_path_idx] = p.path_id; 24 | } else if (p.path_id == best_path[p.best_path_idx]) { 25 | best_path_util[p.best_path_util_idx] = p.util; 26 | // TODO: I guess we aren't switching to another path in 27 | // case the utilization on the best path went up. 28 | } 29 | } 30 | 31 | // Also, CONGA has multiple transactions, while we are only dealing with 32 | // the hardest of these transactions here. 33 | -------------------------------------------------------------------------------- /examples/conga.py: -------------------------------------------------------------------------------- 1 | best_path_util = [100] * 256 2 | best_path = [-1] * 256 3 | 4 | def func(p): 5 | p.best_path_util_idx = p.src 6 | p.best_path_idx = p.src 7 | if (p.util < best_path_util[p.best_path_util_idx]): 8 | best_path_util[p.best_path_util_idx] = p.util 9 | best_path[p.best_path_idx] = p.path_id 10 | elif (p.path_id == best_path[p.best_path_idx]): 11 | best_path_util[p.best_path_util_idx] = p.util 12 | -------------------------------------------------------------------------------- /examples/deterministic_sampling.c: -------------------------------------------------------------------------------- 1 | // Sample every 100th packet in a flow 2 | #define N 100 3 | 4 | struct Packet { 5 | int sample; 6 | }; 7 | 8 | int count = 0; 9 | 10 | void func(struct Packet pkt) { 11 | if (count == N - 1) { 12 | pkt.sample = 1; 13 | count = 0; 14 | } else { 15 | pkt.sample = 0; 16 | count = count + 1; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/ewma.c: -------------------------------------------------------------------------------- 1 | #define EWMA_GAIN 10 2 | #define SCALE 100 3 | int current_val = 0; 4 | int initialized = 0; 5 | 6 | struct Packet { 7 | int val; 8 | int new_val; 9 | int tmp_scaled_val; 10 | }; 11 | 12 | void func(struct Packet p) { 13 | if (! initialized) { 14 | current_val = p.val; 15 | initialized = 1; 16 | } else { 17 | current_val = (current_val * EWMA_GAIN + (SCALE - EWMA_GAIN) * p.val) / SCALE; 18 | } 19 | p.new_val = current_val; 20 | } 21 | -------------------------------------------------------------------------------- /examples/flowlet_switching.c: -------------------------------------------------------------------------------- 1 | #include "hashes.h" 2 | #define NUM_FLOWLETS 8000 3 | #define FLOWLET_THRESHOLD 5 4 | 5 | struct Packet { 6 | int src_port; 7 | int dst_port; 8 | int src_addr; 9 | int dst_addr; 10 | int protocol; 11 | int new_hop; 12 | int arrival_time; 13 | int next_hop; 14 | int last_time_idx; 15 | int saved_hop_idx; 16 | }; 17 | 18 | int last_time [NUM_FLOWLETS] = {0}; 19 | int saved_hop [NUM_FLOWLETS] = {0}; 20 | 21 | void flowlet(struct Packet pkt) { 22 | pkt.new_hop = hash6(pkt.src_port, pkt.dst_port, 23 | pkt.src_addr, pkt.dst_addr, 24 | pkt.protocol, pkt.arrival_time); 25 | pkt.last_time_idx = hash5(pkt.src_port, pkt.dst_port, 26 | pkt.src_addr, pkt.dst_addr, 27 | pkt.protocol) % NUM_FLOWLETS; 28 | pkt.saved_hop_idx = hash5(pkt.src_port, pkt.dst_port, 29 | pkt.src_addr, pkt.dst_addr, 30 | pkt.protocol) % NUM_FLOWLETS; 31 | if (pkt.arrival_time - last_time[pkt.last_time_idx] > 32 | FLOWLET_THRESHOLD) { 33 | saved_hop[pkt.saved_hop_idx] = pkt.new_hop; 34 | } 35 | last_time[pkt.last_time_idx] = pkt.arrival_time; 36 | pkt.next_hop = saved_hop[pkt.saved_hop_idx]; 37 | } 38 | -------------------------------------------------------------------------------- /examples/flowlet_switching_simple.c: -------------------------------------------------------------------------------- 1 | #include "hashes.h" 2 | 3 | #define NUM_FLOWLETS 8000 4 | #define THRESHOLD 5 5 | #define NUM_HOPS 10 6 | 7 | struct Packet { 8 | int sport; 9 | int dport; 10 | int new_hop; 11 | int arrival; 12 | int next_hop; 13 | int id; // array index 14 | }; 15 | 16 | int last_time [NUM_FLOWLETS] = {0}; 17 | int saved_hop [NUM_FLOWLETS] = {0}; 18 | 19 | void flowlet(struct Packet pkt) { 20 | pkt.new_hop = hash3(pkt.sport, 21 | pkt.dport, 22 | pkt.arrival) 23 | % NUM_HOPS; 24 | 25 | pkt.id = hash2(pkt.sport, 26 | pkt.dport) 27 | % NUM_FLOWLETS; 28 | 29 | if (pkt.arrival - 30 | last_time[pkt.id] > 31 | THRESHOLD) { 32 | saved_hop[pkt.id] = pkt.new_hop; 33 | } 34 | 35 | last_time[pkt.id] = pkt.arrival; 36 | pkt.next_hop = saved_hop[pkt.id]; 37 | } 38 | -------------------------------------------------------------------------------- /examples/hashes.h: -------------------------------------------------------------------------------- 1 | // This code was taken from the P4 behavioral model simulator (v1 and v2) 2 | 3 | /* This code was adapted from: 4 | http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code */ 5 | 6 | unsigned int reflect(unsigned int data, int nBits) { 7 | unsigned int reflection = 0x00000000; 8 | int bit; 9 | 10 | /* 11 | * Reflect the data about the center bit. 12 | */ 13 | for (bit = 0; bit < nBits; ++bit) { 14 | /* 15 | * If the LSB bit is set, set the reflection of it. 16 | */ 17 | if (data & 0x01) { 18 | reflection |= (unsigned int)(1 << ((nBits - 1) - bit)); 19 | } 20 | data = (data >> 1); 21 | } 22 | 23 | return reflection; 24 | } 25 | 26 | /* TODO: try to implement something a bit more generic that will cover 27 | programmable CRCs */ 28 | 29 | static inline unsigned short int crc16(unsigned char *buf, int len) { 30 | /* generating from my Python script gen_crc_tables inspired from the C code at: 31 | http://www.barrgroup.com/Embedded-Systems/How-To/CRC-Calculation-C-Code */ 32 | 33 | static const unsigned short int table_crc16[256] = { 34 | 0x0000, 0x8005, 0x800F, 0x000A, 0x801B, 0x001E, 0x0014, 0x8011, 35 | 0x8033, 0x0036, 0x003C, 0x8039, 0x0028, 0x802D, 0x8027, 0x0022, 36 | 0x8063, 0x0066, 0x006C, 0x8069, 0x0078, 0x807D, 0x8077, 0x0072, 37 | 0x0050, 0x8055, 0x805F, 0x005A, 0x804B, 0x004E, 0x0044, 0x8041, 38 | 0x80C3, 0x00C6, 0x00CC, 0x80C9, 0x00D8, 0x80DD, 0x80D7, 0x00D2, 39 | 0x00F0, 0x80F5, 0x80FF, 0x00FA, 0x80EB, 0x00EE, 0x00E4, 0x80E1, 40 | 0x00A0, 0x80A5, 0x80AF, 0x00AA, 0x80BB, 0x00BE, 0x00B4, 0x80B1, 41 | 0x8093, 0x0096, 0x009C, 0x8099, 0x0088, 0x808D, 0x8087, 0x0082, 42 | 0x8183, 0x0186, 0x018C, 0x8189, 0x0198, 0x819D, 0x8197, 0x0192, 43 | 0x01B0, 0x81B5, 0x81BF, 0x01BA, 0x81AB, 0x01AE, 0x01A4, 0x81A1, 44 | 0x01E0, 0x81E5, 0x81EF, 0x01EA, 0x81FB, 0x01FE, 0x01F4, 0x81F1, 45 | 0x81D3, 0x01D6, 0x01DC, 0x81D9, 0x01C8, 0x81CD, 0x81C7, 0x01C2, 46 | 0x0140, 0x8145, 0x814F, 0x014A, 0x815B, 0x015E, 0x0154, 0x8151, 47 | 0x8173, 0x0176, 0x017C, 0x8179, 0x0168, 0x816D, 0x8167, 0x0162, 48 | 0x8123, 0x0126, 0x012C, 0x8129, 0x0138, 0x813D, 0x8137, 0x0132, 49 | 0x0110, 0x8115, 0x811F, 0x011A, 0x810B, 0x010E, 0x0104, 0x8101, 50 | 0x8303, 0x0306, 0x030C, 0x8309, 0x0318, 0x831D, 0x8317, 0x0312, 51 | 0x0330, 0x8335, 0x833F, 0x033A, 0x832B, 0x032E, 0x0324, 0x8321, 52 | 0x0360, 0x8365, 0x836F, 0x036A, 0x837B, 0x037E, 0x0374, 0x8371, 53 | 0x8353, 0x0356, 0x035C, 0x8359, 0x0348, 0x834D, 0x8347, 0x0342, 54 | 0x03C0, 0x83C5, 0x83CF, 0x03CA, 0x83DB, 0x03DE, 0x03D4, 0x83D1, 55 | 0x83F3, 0x03F6, 0x03FC, 0x83F9, 0x03E8, 0x83ED, 0x83E7, 0x03E2, 56 | 0x83A3, 0x03A6, 0x03AC, 0x83A9, 0x03B8, 0x83BD, 0x83B7, 0x03B2, 57 | 0x0390, 0x8395, 0x839F, 0x039A, 0x838B, 0x038E, 0x0384, 0x8381, 58 | 0x0280, 0x8285, 0x828F, 0x028A, 0x829B, 0x029E, 0x0294, 0x8291, 59 | 0x82B3, 0x02B6, 0x02BC, 0x82B9, 0x02A8, 0x82AD, 0x82A7, 0x02A2, 60 | 0x82E3, 0x02E6, 0x02EC, 0x82E9, 0x02F8, 0x82FD, 0x82F7, 0x02F2, 61 | 0x02D0, 0x82D5, 0x82DF, 0x02DA, 0x82CB, 0x02CE, 0x02C4, 0x82C1, 62 | 0x8243, 0x0246, 0x024C, 0x8249, 0x0258, 0x825D, 0x8257, 0x0252, 63 | 0x0270, 0x8275, 0x827F, 0x027A, 0x826B, 0x026E, 0x0264, 0x8261, 64 | 0x0220, 0x8225, 0x822F, 0x022A, 0x823B, 0x023E, 0x0234, 0x8231, 65 | 0x8213, 0x0216, 0x021C, 0x8219, 0x0208, 0x820D, 0x8207, 0x0202 66 | }; 67 | 68 | int byte; 69 | unsigned short int remainder = 0x0000; 70 | unsigned short int final_xor_value = 0x0000; 71 | int data; 72 | for(byte = 0; byte < len; byte++) { 73 | data = (int)(reflect(buf[byte], 8)) ^ (remainder >> 8); 74 | remainder = (unsigned short int)(table_crc16[data] ^ (remainder << 8)); 75 | } 76 | return (unsigned short int)(reflect(remainder, 16) ^ final_xor_value); 77 | } 78 | 79 | // Hash two values 80 | int hash2(int a, int b) { 81 | unsigned char buf[2 * sizeof(int)]; 82 | *(int *)buf = a; 83 | *((int *)(buf + sizeof(int))) = b; 84 | return crc16(buf, 2 * sizeof(int)); 85 | } 86 | 87 | // Hash three values 88 | int hash3(int a, int b, int c) { 89 | unsigned char buf[3 * sizeof(int)]; 90 | *(int *)buf = a; 91 | *((int *)(buf + sizeof(int))) = b; 92 | *((int *)(buf + 2 * sizeof(int))) = c; 93 | return crc16(buf, 3 * sizeof(int)); 94 | } 95 | 96 | // Hash five values 97 | int hash5(int a, int b, int c, int d, int e) { 98 | // Pack a and b into a char buffer 99 | unsigned char buf[5 * sizeof(int)]; 100 | *((int *)(buf + 0 * sizeof(int))) = a; 101 | *((int *)(buf + 1 * sizeof(int))) = b; 102 | *((int *)(buf + 2 * sizeof(int))) = c; 103 | *((int *)(buf + 3 * sizeof(int))) = d; 104 | *((int *)(buf + 4 * sizeof(int))) = e; 105 | return crc16(buf, 5 * sizeof(int)); 106 | } 107 | 108 | // Hash six values 109 | int hash6(int a, int b, int c, int d, int e, int f) { 110 | // Pack a and b into a char buffer 111 | unsigned char buf[6 * sizeof(int)]; 112 | *((int *)(buf + 0 * sizeof(int))) = a; 113 | *((int *)(buf + 1 * sizeof(int))) = b; 114 | *((int *)(buf + 2 * sizeof(int))) = c; 115 | *((int *)(buf + 3 * sizeof(int))) = d; 116 | *((int *)(buf + 4 * sizeof(int))) = e; 117 | *((int *)(buf + 5 * sizeof(int))) = f; 118 | return crc16(buf, 6 * sizeof(int)); 119 | } 120 | -------------------------------------------------------------------------------- /examples/heavy_hitters.c: -------------------------------------------------------------------------------- 1 | #include "hashes.h" 2 | 3 | #define low_th 100 4 | #define hi_th 1000 5 | 6 | struct Packet { 7 | int sport; 8 | int dport; 9 | int is_not_heavy_hitter; 10 | int sketch1_idx; 11 | int sketch2_idx; 12 | int sketch3_idx; 13 | }; 14 | 15 | #define NUM_ENTRIES 4096 16 | 17 | int sketch_cnt_1[NUM_ENTRIES] = {0}; 18 | int sketch_cnt_2[NUM_ENTRIES] = {0}; 19 | int sketch_cnt_3[NUM_ENTRIES] = {0}; 20 | 21 | void func(struct Packet p) { 22 | p.sketch1_idx = hash2(p.sport, p.dport) % NUM_ENTRIES; 23 | p.sketch2_idx = hash2(p.sport, p.dport) % NUM_ENTRIES; 24 | p.sketch3_idx = hash2(p.sport, p.dport) % NUM_ENTRIES; 25 | if (sketch_cnt_1[p.sketch1_idx] > low_th && sketch_cnt_1[p.sketch1_idx] < hi_th && 26 | sketch_cnt_2[p.sketch2_idx] > low_th && sketch_cnt_2[p.sketch2_idx] < hi_th && 27 | sketch_cnt_3[p.sketch3_idx] > low_th && sketch_cnt_3[p.sketch3_idx] < hi_th) { 28 | p.is_not_heavy_hitter = 0; 29 | } else { 30 | p.is_not_heavy_hitter = 1; 31 | } 32 | sketch_cnt_1[p.sketch1_idx]+= 1; 33 | sketch_cnt_2[p.sketch2_idx]+= 1; 34 | sketch_cnt_3[p.sketch3_idx]+= 1; 35 | } 36 | -------------------------------------------------------------------------------- /examples/iso646.h: -------------------------------------------------------------------------------- 1 | /* Copyright (C) 1997-2014 Free Software Foundation, Inc. 2 | 3 | This file is part of GCC. 4 | 5 | GCC is free software; you can redistribute it and/or modify 6 | it under the terms of the GNU General Public License as published by 7 | the Free Software Foundation; either version 3, or (at your option) 8 | any later version. 9 | 10 | GCC is distributed in the hope that it will be useful, 11 | but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | GNU General Public License for more details. 14 | 15 | Under Section 7 of GPL version 3, you are granted additional 16 | permissions described in the GCC Runtime Library Exception, version 17 | 3.1, as published by the Free Software Foundation. 18 | 19 | You should have received a copy of the GNU General Public License and 20 | a copy of the GCC Runtime Library Exception along with this program; 21 | see the files COPYING3 and COPYING.RUNTIME respectively. If not, see 22 | . */ 23 | 24 | /* 25 | * ISO C Standard: 7.9 Alternative spellings 26 | */ 27 | 28 | #ifndef _ISO646_H 29 | #define _ISO646_H 30 | 31 | #ifndef __cplusplus 32 | #define and && 33 | #define and_eq &= 34 | #define bitand & 35 | #define bitor | 36 | #define compl ~ 37 | #define not ! 38 | #define not_eq != 39 | #define or || 40 | #define or_eq |= 41 | #define xor ^ 42 | #define xor_eq ^= 43 | #endif 44 | 45 | #endif 46 | -------------------------------------------------------------------------------- /examples/learning_bloom_filter.c: -------------------------------------------------------------------------------- 1 | #include "hashes.h" 2 | 3 | struct Packet { 4 | int sport; 5 | int dport; 6 | int member; 7 | int filter1_idx; 8 | int filter2_idx; 9 | int filter3_idx; 10 | }; 11 | 12 | #define NUM_ENTRIES 256 13 | 14 | int filter1[NUM_ENTRIES] = {0}; 15 | int filter2[NUM_ENTRIES] = {0}; 16 | int filter3[NUM_ENTRIES] = {0}; 17 | 18 | void func(struct Packet pkt) { 19 | pkt.filter1_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 20 | pkt.filter2_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 21 | pkt.filter3_idx = hash2(pkt.sport, pkt.dport) % NUM_ENTRIES; 22 | pkt.member = (filter1[pkt.filter1_idx] && 23 | filter2[pkt.filter2_idx] && 24 | filter3[pkt.filter3_idx]); 25 | 26 | filter1[pkt.filter1_idx] = 1; 27 | filter2[pkt.filter2_idx] = 1; 28 | filter3[pkt.filter3_idx] = 1; 29 | } 30 | -------------------------------------------------------------------------------- /examples/meter.c: -------------------------------------------------------------------------------- 1 | #define PBS 2000000000 2 | #define CBS 2000000000 3 | 4 | #define PIR 2 5 | #define CIR 1 6 | 7 | struct Packet { 8 | int size; 9 | int color; 10 | int time; 11 | }; 12 | 13 | int tp = PBS; 14 | int tc = CBS; 15 | int last_time = 0; 16 | 17 | void func(struct Packet pkt) { 18 | if (tp < pkt.size) { 19 | pkt.color = 1; 20 | } else if (tc < pkt.size) { 21 | pkt.color = 2; 22 | tp = tp - pkt.size; 23 | } else { 24 | pkt.color = 3; 25 | tp = tp - pkt.size; 26 | tc = tc - pkt.size; 27 | } 28 | 29 | // Refill logic 30 | tp = tp + PIR * (pkt.time - last_time); 31 | if (tp > PBS) tp = PBS; 32 | 33 | tc = tc + CIR * (pkt.time - last_time); 34 | if (tc > PBS) tc = CBS; 35 | 36 | last_time = pkt.time; 37 | 38 | } 39 | -------------------------------------------------------------------------------- /examples/meter.py: -------------------------------------------------------------------------------- 1 | PBS=2000000000 2 | CBS=2000000000 3 | PIR=2 4 | CIR=1 5 | tp=PBS 6 | tc=CBS 7 | last_time=0 8 | 9 | def func(pkt): 10 | if (tp < pkt.size): 11 | pkt.color = 1 12 | elif (tc < pkt.size): 13 | pkt.color = 2 14 | tp = tp - pkt.size 15 | else: 16 | pkt.color = 3 17 | tp = tp - pkt.size 18 | tc = tc - pkt.size 19 | 20 | tp = tp + PIR * (pkt.time - last_time) 21 | if (tp > PBS): 22 | tp = PBS 23 | 24 | tc = tc + CIR * (pkt.time - last_time) 25 | if (tc > PBS): 26 | tc = CBS 27 | 28 | last_time = pkt.time 29 | -------------------------------------------------------------------------------- /examples/muxes.sk: -------------------------------------------------------------------------------- 1 | generator int mux2(int op1, int op2) { 2 | int choice = ??(1); 3 | if (choice == 0) return op1; 4 | else if (choice == 1) return op2; 5 | } 6 | 7 | generator int mux3(int op1, int op2, int op3) { 8 | int choice = ??(2); 9 | if (choice == 0) return op1; 10 | else if (choice == 1) return op2; 11 | else if (choice == 2) return op3; 12 | else assert(false); 13 | } 14 | 15 | generator int mux4(int op1, int op2, int op3, int op4) { 16 | int choice = ??(2); 17 | if (choice == 0) return op1; 18 | else if (choice == 1) return op2; 19 | else if (choice == 2) return op3; 20 | else { 21 | assert (choice == 3); 22 | return op4; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /examples/rcp.c: -------------------------------------------------------------------------------- 1 | #define MAX_ALLOWABLE_RTT 100 2 | 3 | // Total number of bytes seen so far. 4 | int input_traffic_Bytes = 0; 5 | 6 | // Sum of rtt so far 7 | int sum_rtt_Tr = 0; 8 | 9 | // Number of packets with a valid RTT 10 | int num_pkts_with_rtt = 0; 11 | 12 | struct Packet { 13 | int size_bytes; 14 | int rtt; 15 | }; 16 | 17 | void func(struct Packet pkt) { 18 | input_traffic_Bytes += pkt.size_bytes; 19 | if (pkt.rtt < MAX_ALLOWABLE_RTT) { 20 | sum_rtt_Tr += pkt.rtt; 21 | num_pkts_with_rtt += 1; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /examples/rel_ops.sk: -------------------------------------------------------------------------------- 1 | generator bit rel_op(int operand1, int operand2) { 2 | int opcode = ??(2); 3 | if (opcode == 0) { 4 | return operand1 != operand2; 5 | } else if (opcode == 1) { 6 | return operand1 < operand2; 7 | } else if (opcode == 2) { 8 | return operand1 > operand2; 9 | } else { 10 | assert(opcode == 3); 11 | return operand1 == operand2; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /expr_flattener_handler.cc: -------------------------------------------------------------------------------- 1 | #include "expr_flattener_handler.h" 2 | 3 | #include 4 | 5 | #include "third_party/assert_exception.h" 6 | 7 | #include "clang_utility_functions.h" 8 | #include "pkt_func_transform.h" 9 | 10 | using namespace clang; 11 | using std::placeholders::_1; 12 | using std::placeholders::_2; 13 | 14 | std::string ExprFlattenerHandler::transform(const TranslationUnitDecl * tu_decl) { 15 | unique_identifiers_ = UniqueIdentifiers(identifier_census(tu_decl)); 16 | return pkt_func_transform(tu_decl, std::bind(& ExprFlattenerHandler::flatten_body, this, _1, _2)); 17 | } 18 | 19 | std::pair> 20 | ExprFlattenerHandler::flatten_body(const Stmt * function_body, const std::string & pkt_name) const { 21 | assert_exception(function_body); 22 | 23 | std::string output = ""; 24 | std::vector new_decls = {}; 25 | 26 | // iterate through function body 27 | assert_exception(isa(function_body)); 28 | for (const auto & child : function_body->children()) { 29 | assert_exception(isa(child)); 30 | const auto * bin_op = dyn_cast(child); 31 | assert_exception(bin_op->isAssignmentOp()); 32 | const auto ret = flatten(bin_op->getRHS(), pkt_name); 33 | 34 | // First add new definitions 35 | output += ret.new_defs; 36 | 37 | // Then add flattened expression 38 | output += clang_stmt_printer(bin_op->getLHS()) + " = " + ret.flat_expr + ";"; 39 | 40 | // Then append new pkt var declarations 41 | new_decls.insert(new_decls.end(), ret.new_decls.begin(), ret.new_decls.end()); 42 | } 43 | 44 | return make_pair("{" + output + "}", new_decls); 45 | } 46 | 47 | bool ExprFlattenerHandler::is_atomic_expr(const clang::Expr * expr) const { 48 | expr = expr->IgnoreParenImpCasts(); 49 | return isa(expr) or isa(expr) or isa(expr) or isa(expr) or isa(expr); 50 | } 51 | 52 | bool ExprFlattenerHandler::is_flat(const clang::Expr * expr) const { 53 | expr = expr->IgnoreParenImpCasts(); 54 | assert_exception(expr); 55 | if (isa(expr)) { 56 | return is_atomic_expr(dyn_cast(expr)->getSubExpr()); 57 | } else if (isa(expr)) { 58 | const auto * cond_op = dyn_cast(expr); 59 | return is_atomic_expr(cond_op->getCond()) and is_atomic_expr(cond_op->getTrueExpr()) and is_atomic_expr(cond_op->getFalseExpr()); 60 | } else if (isa(expr)) { 61 | return is_atomic_expr(dyn_cast(expr)->getLHS()) and 62 | is_atomic_expr(dyn_cast(expr)->getRHS()); 63 | } else { 64 | assert_exception(is_atomic_expr(expr)); 65 | return true; 66 | } 67 | } 68 | 69 | FlattenResult ExprFlattenerHandler::flatten(const clang::Expr * expr, const std::string & pkt_name) const { 70 | expr = expr->IgnoreParenImpCasts(); 71 | if (is_flat(expr)) { 72 | return {clang_stmt_printer(expr), "", {}}; 73 | } else { 74 | if (isa(expr)) { 75 | return flatten_cond_op(dyn_cast(expr), pkt_name); 76 | } else if (isa(expr)) { 77 | return flatten_bin_op(dyn_cast(expr), pkt_name); 78 | } else { 79 | assert_exception(false); 80 | return {"", "", {}}; 81 | } 82 | } 83 | } 84 | 85 | FlattenResult ExprFlattenerHandler::flatten_bin_op(const BinaryOperator * bin_op, const std::string & pkt_name) const { 86 | assert_exception(not is_flat(bin_op)); 87 | const auto ret_lhs = flatten_to_atomic_expr(bin_op->getLHS(), pkt_name); 88 | const auto ret_rhs = flatten_to_atomic_expr(bin_op->getRHS(), pkt_name); 89 | 90 | // Join all declarations 91 | std::vector all_decls; 92 | all_decls.insert(all_decls.begin(), ret_lhs.new_decls.begin(), ret_lhs.new_decls.end()); 93 | all_decls.insert(all_decls.begin(), ret_rhs.new_decls.begin(), ret_rhs.new_decls.end()); 94 | 95 | return {ret_lhs.flat_expr + std::string(BinaryOperator::getOpcodeStr(bin_op->getOpcode())) + ret_rhs.flat_expr, 96 | ret_lhs.new_defs + ret_rhs.new_defs, 97 | all_decls}; 98 | } 99 | 100 | FlattenResult ExprFlattenerHandler::flatten_cond_op(const ConditionalOperator * cond_op, const std::string & pkt_name) const { 101 | assert_exception(not is_flat(cond_op)); 102 | const auto ret_cond = flatten_to_atomic_expr(cond_op->getCond(), pkt_name); 103 | const auto ret_true = flatten_to_atomic_expr(cond_op->getTrueExpr(), pkt_name); 104 | const auto ret_false = flatten_to_atomic_expr(cond_op->getFalseExpr(), pkt_name); 105 | 106 | // Join all declarations 107 | std::vector all_decls; 108 | all_decls.insert(all_decls.begin(), ret_cond.new_decls.begin(), ret_cond.new_decls.end()); 109 | all_decls.insert(all_decls.begin(), ret_true.new_decls.begin(), ret_true.new_decls.end()); 110 | all_decls.insert(all_decls.begin(), ret_false.new_decls.begin(), ret_false.new_decls.end()); 111 | 112 | return {ret_cond.flat_expr + " ? " + ret_true.flat_expr + " : " + ret_false.flat_expr, 113 | ret_cond.new_defs + ret_true.new_defs + ret_false.new_defs, 114 | all_decls}; 115 | } 116 | 117 | FlattenResult ExprFlattenerHandler::flatten_to_atomic_expr(const Expr * expr, const std::string & pkt_name) const { 118 | if (is_atomic_expr(expr)) { 119 | return {clang_stmt_printer(expr), "", {}}; 120 | } else { 121 | const auto flat_var_member = unique_identifiers_.get_unique_identifier(); 122 | const auto flat_var_decl = expr->getType().getAsString() + " " + flat_var_member + ";"; 123 | const auto pkt_flat_variable = pkt_name + "." + flat_var_member; 124 | const auto pkt_flat_var_def = pkt_flat_variable + " = " + clang_stmt_printer(expr) + ";"; 125 | return {pkt_flat_variable, pkt_flat_var_def, {flat_var_decl}}; 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /expr_flattener_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPR_FLATTENER_HANDLER_H_ 2 | #define EXPR_FLATTENER_HANDLER_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "clang/AST/Stmt.h" 13 | #include "clang/AST/Expr.h" 14 | 15 | #include "unique_identifiers.h" 16 | 17 | struct FlattenResult { 18 | std::string flat_expr; 19 | std::string new_defs; 20 | std::vector new_decls; 21 | }; 22 | 23 | /// Flatten expressions using temporaries so that every 24 | /// statement is of the form x = y op z, where x, y, z are atomic 25 | class ExprFlattenerHandler { 26 | public: 27 | /// Function supplied to SinglePass 28 | std::string transform(const clang::TranslationUnitDecl * tu_decl); 29 | 30 | /// Flatten function body 31 | std::pair> flatten_body(const clang::Stmt * function_body, const std::string & pkt_name) const; 32 | 33 | private: 34 | /// Flatten expression 35 | /// (http://www.cs.cornell.edu/projects/polyglot/api2/polyglot/visit/ExpressionFlattener.html) 36 | /// Returns a FlattenResult 37 | /// The first element is a flattened expression, one for which is_flat() returns true 38 | /// The second element is a vector of new definitions 39 | /// The third element is a vector of new packet variable declarations 40 | /// flatten maintains the invariant that the returned expression is flat, 41 | /// but it might create new definitions that aren't flat yet. 42 | /// We may need to run expr_flatten_prog multiple times until we reach a fixed point. 43 | FlattenResult flatten(const clang::Expr *expr, const std::string & pkt_name) const; 44 | 45 | /// Is expression flat? 46 | bool is_flat(const clang::Expr * expr) const; 47 | 48 | /// Is expression atomic? 49 | bool is_atomic_expr(const clang::Expr * expr) const; 50 | 51 | /// Flatten expr to atomic expression if it isn't already atomic 52 | /// , creating a temporary variable is required 53 | FlattenResult flatten_to_atomic_expr(const clang::Expr * expr, const std::string & pkt_name) const; 54 | 55 | /// Flatten conditional op by calling flatten_to_atomic_expr 56 | /// on its three constituents 57 | FlattenResult flatten_cond_op(const clang::ConditionalOperator * cond_op, const std::string & pkt_name) const; 58 | 59 | /// Flatten binary op by calling flatten_to_atomic_expr 60 | /// on the left and right halves 61 | FlattenResult flatten_bin_op(const clang::BinaryOperator * bin_op, const std::string & pkt_name) const; 62 | 63 | /// Object that generates unique identifiers 64 | UniqueIdentifiers unique_identifiers_ = UniqueIdentifiers(std::set()); 65 | }; 66 | 67 | #endif // EXPR_FLATTENER_HANDLER_H_ 68 | -------------------------------------------------------------------------------- /expr_prop.cc: -------------------------------------------------------------------------------- 1 | #include "expr_prop.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | 5 | #include "pkt_func_transform.h" 6 | #include "clang_utility_functions.h" 7 | 8 | using namespace clang; 9 | 10 | std::string expr_prop_transform(const clang::TranslationUnitDecl * tu_decl) { 11 | return pkt_func_transform(tu_decl, expr_prop_fn_body); 12 | } 13 | 14 | std::pair> expr_prop_fn_body(const CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))) { 15 | // Do not run if this is not in SSA 16 | if (not is_in_ssa(function_body)) { 17 | throw std::logic_error("Expression propagation can be run only after SSA. It relies on variables not being redefined\n"); 18 | } 19 | 20 | // Maintain map from variable name (packet or state variable) 21 | // to a string representing its expression, for expression propagation. 22 | std::map var_to_expr; 23 | 24 | // Rewrite function body 25 | std::string transformed_body = ""; 26 | assert_exception(function_body); 27 | for (const auto & child : function_body->children()) { 28 | assert_exception(isa(child)); 29 | const auto * bin_op = dyn_cast(child); 30 | assert_exception(bin_op->isAssignmentOp()); 31 | 32 | // Strip off parenthesis and casts for LHS and RHS 33 | const auto * rhs = bin_op->getRHS()->IgnoreParenImpCasts(); 34 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 35 | 36 | // Populate var_to_expr, so long as 37 | // 1. The lhs variable doesn't appear within the rhs expression. 38 | // 2. The rhs expression isn't a (or doesn't contain a) DeclRefExpr 39 | // We don't want to be propagating DeclRefExpr's or ArraySubscriptExpr because these are 40 | // the syntactic construct for state variables and propagating state variables 41 | // destroys the property that state variables are only ever read at the top of the program 42 | // The partitiioning pass relies on this property. 43 | const auto & var_list = gen_var_list(rhs); 44 | if (var_list.find(clang_stmt_printer(lhs)) == var_list.end() and 45 | (not isa(rhs)) and 46 | (not isa(rhs))) { 47 | var_to_expr[clang_stmt_printer(lhs)] = clang_stmt_printer(rhs); 48 | } 49 | 50 | if ((isa(rhs) or isa(rhs)) and (var_to_expr.find(clang_stmt_printer(rhs)) != var_to_expr.end())) { 51 | // If rhs is a packet/state variable, replace it with its current expr 52 | transformed_body += clang_stmt_printer(lhs) + "=" + var_to_expr.at(clang_stmt_printer(rhs)) + ";"; 53 | } else { 54 | // Pass through 55 | transformed_body += clang_stmt_printer(lhs) + "=" + clang_stmt_printer(rhs) + ";"; 56 | } 57 | } 58 | return std::make_pair("{" + transformed_body + "}", std::vector()); 59 | } 60 | -------------------------------------------------------------------------------- /expr_prop.h: -------------------------------------------------------------------------------- 1 | #ifndef EXPR_PROP_H_ 2 | #define EXPR_PROP_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include "clang/AST/AST.h" 8 | 9 | /// Entry point from SinglePass to expression 10 | /// propagater, calls expr_prop_fn_body immediately 11 | std::string expr_prop_transform(const clang::TranslationUnitDecl * tu_decl); 12 | 13 | /// Expr propagation: replace y = b + c; a = y; 14 | /// with y=b+c; a=b+c; In some sense we are inverting 15 | /// common subexpression elimination 16 | std::pair> expr_prop_fn_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))); 17 | 18 | #endif // EXPR_PROP_H_ 19 | -------------------------------------------------------------------------------- /gen_used_fields.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "gen_used_fields.h" 4 | 5 | #include "third_party/assert_exception.h" 6 | 7 | #include "clang_utility_functions.h" 8 | #include "pkt_func_transform.h" 9 | #include "util.h" 10 | 11 | using namespace clang; 12 | 13 | std::string gen_used_field_transform(const clang::TranslationUnitDecl * tu_decl) { 14 | for (const auto * child_decl : dyn_cast(tu_decl)->decls()) { 15 | assert_exception(child_decl); 16 | if (isa(child_decl) and (is_packet_func(dyn_cast(child_decl)))) { 17 | const auto * function_decl = dyn_cast(child_decl); 18 | 19 | // Extract function signature 20 | assert_exception(function_decl->getNumParams() >= 1); 21 | const auto * pkt_param = function_decl->getParamDecl(0); 22 | const auto pkt_type = function_decl->getParamDecl(0)->getType().getAsString(); 23 | const auto pkt_name = clang_value_decl_printer(pkt_param); 24 | 25 | // Transform function body 26 | return gen_used_field_body(dyn_cast(function_decl->getBody()), pkt_name); 27 | } 28 | } 29 | assert_exception(false); 30 | return ""; 31 | } 32 | 33 | std::string gen_used_field_body(const clang::CompoundStmt * function_body, 34 | const std::string & pkt_name __attribute__((unused))) { 35 | const auto var_list = gen_var_list(function_body, 36 | {{VariableType::PACKET, true}, 37 | {VariableType::STATE_SCALAR, false}, 38 | {VariableType::STATE_ARRAY, false}}); 39 | std::string ret = ""; 40 | for (const auto & var : var_list) { 41 | std::vector splits = split(var, "\\."); 42 | assert_exception(splits.size() == 2); 43 | ret += splits.at(1) + "\n"; 44 | } 45 | return ret; 46 | } 47 | -------------------------------------------------------------------------------- /gen_used_fields.h: -------------------------------------------------------------------------------- 1 | #ifndef GEN_USED_FIELDS_H_ 2 | #define GEN_USED_FIELDS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "clang/AST/Stmt.h" 8 | #include "clang/AST/Decl.h" 9 | 10 | /// Entry point from SinglePass, 11 | /// immediately delegating to gen_used_field_body 12 | std::string gen_used_field_transform(const clang::TranslationUnitDecl * tu_decl); 13 | 14 | /// Scan function body and generate list of used fields as a string 15 | /// This is used by jayhawk for test packet generation 16 | std::string gen_used_field_body(const clang::CompoundStmt * function_body, const std::string & pkt_name __attribute__((unused))); 17 | 18 | #endif // GEN_USED_FIELDS_H_ 19 | -------------------------------------------------------------------------------- /if_conversion_handler.cc: -------------------------------------------------------------------------------- 1 | #include "if_conversion_handler.h" 2 | 3 | #include 4 | 5 | #include "third_party/assert_exception.h" 6 | 7 | #include "pkt_func_transform.h" 8 | #include "clang_utility_functions.h" 9 | 10 | using namespace clang; 11 | using std::placeholders::_1; 12 | using std::placeholders::_2; 13 | 14 | std::string IfConversionHandler::transform(const TranslationUnitDecl * tu_decl) { 15 | unique_identifiers_ = UniqueIdentifiers(identifier_census(tu_decl)); 16 | return pkt_func_transform(tu_decl, std::bind(& IfConversionHandler::if_convert_body, this, _1, _2)); 17 | } 18 | 19 | std::pair> IfConversionHandler::if_convert_body(const Stmt * function_body, const std::string & pkt_name) const { 20 | assert_exception(function_body); 21 | 22 | std::string output_ = ""; 23 | std::vector new_decls_ = {}; 24 | 25 | // 1 is the C representation for true 26 | if_convert(output_, new_decls_, "1", function_body, pkt_name); 27 | return make_pair("{" + output_ + "}", new_decls_); 28 | } 29 | 30 | void IfConversionHandler::if_convert(std::string & current_stream, 31 | std::vector & current_decls, 32 | const std::string & predicate, 33 | const Stmt * stmt, 34 | const std::string & pkt_name) const { 35 | if (isa(stmt)) { 36 | for (const auto & child : stmt->children()) { 37 | if_convert(current_stream, current_decls, predicate, child, pkt_name); 38 | } 39 | } else if (isa(stmt)) { 40 | const auto * if_stmt = dyn_cast(stmt); 41 | 42 | if (if_stmt->getConditionVariableDeclStmt()) { 43 | throw std::logic_error("We don't yet handle declarations within the test portion of an if\n"); 44 | } 45 | 46 | // Create temporary variable to hold the if condition 47 | const auto condition_type_name = if_stmt->getCond()->getType().getAsString(); 48 | const auto cond_variable = unique_identifiers_.get_unique_identifier(); 49 | const auto cond_var_decl = condition_type_name + " " + cond_variable + ";"; 50 | 51 | // Add cond var decl to the packet structure, so that all decls accumulate there 52 | current_decls.emplace_back(cond_var_decl); 53 | 54 | // Add assignment to new packet temporary here, 55 | // predicating it with the current predicate 56 | const auto pkt_cond_variable = pkt_name + "." + cond_variable; 57 | current_stream += pkt_cond_variable + " = (" + predicate + " ? (" + clang_stmt_printer(if_stmt->getCond()) + ") : 0);"; 58 | 59 | // Create predicates for if and else block 60 | auto pred_within_if_block = "(" + predicate + " && " + pkt_cond_variable + ")"; 61 | auto pred_within_else_block = "(" + predicate + " && !" + pkt_cond_variable + ")"; 62 | 63 | // If convert statements within getThen block to ternary operators. 64 | if_convert(current_stream, current_decls, pred_within_if_block, if_stmt->getThen(), pkt_name); 65 | 66 | // If there is a getElse block, handle it recursively again 67 | if (if_stmt->getElse() != nullptr) { 68 | if_convert(current_stream, current_decls, pred_within_else_block, if_stmt->getElse(), pkt_name); 69 | } 70 | } else if (isa(stmt)) { 71 | current_stream += if_convert_atomic_stmt(dyn_cast(stmt), predicate); 72 | } else if (isa(stmt)) { 73 | // Just append statement as is, but check that this only happens at the 74 | // top level i.e. when predicate = "1" or true 75 | assert_exception(predicate == "1"); 76 | current_stream += clang_stmt_printer(stmt); 77 | return; 78 | } else if (isa(stmt)) { 79 | // Do nothing 80 | return; 81 | } else { 82 | throw std::logic_error("Cannot handle stmt " + clang_stmt_printer(stmt) + " of type " + std::string(stmt->getStmtClassName())); 83 | assert_exception(false); 84 | } 85 | } 86 | 87 | std::string IfConversionHandler::if_convert_atomic_stmt(const BinaryOperator * stmt, 88 | const std::string & predicate) const { 89 | assert_exception(stmt); 90 | assert_exception(stmt->isAssignmentOp()); 91 | assert_exception(not stmt->isCompoundAssignmentOp()); 92 | 93 | // Create predicated version of BinaryOperator 94 | const std::string lhs = clang_stmt_printer(dyn_cast(stmt)->getLHS()); 95 | const std::string rhs = "(" + predicate + " ? (" + clang_stmt_printer(stmt->getRHS()) + ") : " + lhs + ")"; 96 | return (lhs + " = " + rhs + ";"); 97 | } 98 | -------------------------------------------------------------------------------- /if_conversion_handler.h: -------------------------------------------------------------------------------- 1 | #ifndef IF_CONVERSION_HANDLER_H_ 2 | #define IF_CONVERSION_HANDLER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "clang/AST/Decl.h" 10 | #include "clang/AST/Stmt.h" 11 | #include "clang/AST/Expr.h" 12 | 13 | #include "unique_identifiers.h" 14 | 15 | /// Rewrite if statements into ternary operators 16 | /// and recursively get rid of all branches. 17 | class IfConversionHandler { 18 | public: 19 | /// Transform function itself, entry point to SinglePass 20 | std::string transform(const clang::TranslationUnitDecl * tu_decl); 21 | 22 | /// If convert function body 23 | std::pair> if_convert_body(const clang::Stmt * function_body, const std::string & pkt_name) const; 24 | 25 | private: 26 | /// if_convert current clang::Stmt 27 | /// Takes as input current if-converted program, 28 | /// current predicate, and the stmt itself (the AST) 29 | void if_convert(std::string & current_stream, 30 | std::vector & current_decls, 31 | const std::string & predicate, 32 | const clang::Stmt * stmt, 33 | const std::string & pkt_name) const; 34 | 35 | /// If-convert an atomic (clang::BinaryOperator) statement 36 | /// with a conditional version of it 37 | std::string if_convert_atomic_stmt(const clang::BinaryOperator * stmt, 38 | const std::string & predicate) const; 39 | 40 | /// Unique identifier generator 41 | UniqueIdentifiers unique_identifiers_ = UniqueIdentifiers(std::set()); 42 | }; 43 | 44 | #endif // IF_CONVERSION_HANDLER_H_ 45 | -------------------------------------------------------------------------------- /informal_grammar.txt: -------------------------------------------------------------------------------- 1 | Source grammar: 2 | -------------------------- 3 | 4 | 1. All explicit declarations are outside the function 5 | and refer to persistent, stateful variables. 6 | 2. All local variables are manipulated only through 7 | packet attributes, which are rewritten into local variables anyway. 8 | It's our very own form of highly simplified scalar replacement. 9 | 3. Only assignments, conditional operators, 10 | and other simple binary operators 11 | (addition, multiplication, subtraction, comparison, boolean) 12 | 4. Only if/else-if/else control flow. 13 | 5. No arrays, heaps, switch cases, goto, setjmp, function calls. 14 | 6. No spurious semicolons. 15 | 16 | Target grammar: 17 | ---------------------------- 18 | 1. if-converted version of source. 19 | -------------------------------------------------------------------------------- /int_type_checker.h: -------------------------------------------------------------------------------- 1 | #ifndef INT_TYPE_CHECKER_H_ 2 | #define INT_TYPE_CHECKER_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | #include "clang/AST/Expr.h" 7 | 8 | #include "third_party/assert_exception.h" 9 | #include "clang_utility_functions.h" 10 | 11 | class IntTypeChecker : public AstVisitor { 12 | protected: 13 | /// Checks that there are no ImplicitCastExprs that invoke an IntegralCast 14 | /// (in some ways, a poor man's version of Wconversion and Wsign-conversion) 15 | std::string ast_visit_implicit_cast(const clang::ImplicitCastExpr * implicit_cast) { 16 | assert_exception(implicit_cast); 17 | bool check = implicit_cast->getCastKind() == clang::CastKind::CK_IntegralCast 18 | or implicit_cast->getCastKind() == clang::CastKind::CK_IntegralToBoolean; 19 | if (check == true) { 20 | throw std::logic_error("Found ImplicitCastExpr with a cast of type " + 21 | std::string(implicit_cast->getCastKindName()) + 22 | " in expression " + 23 | clang_stmt_printer(implicit_cast)); 24 | } 25 | return AstVisitor::ast_visit_implicit_cast(implicit_cast); 26 | } 27 | }; 28 | 29 | #endif // INT_TYPE_CHECKER_H_ 30 | -------------------------------------------------------------------------------- /libgraphviz_example.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | int main() { 6 | FILE * fp = fopen("test", "r"); 7 | assert(fp); 8 | Agraph_t * graph = agread(fp, NULL); 9 | assert(graph); 10 | printf("Number of nodes is %d\n", agnnodes(graph)); 11 | printf("Number of edges is %d\n", agnedges(graph)); 12 | 13 | /* Iterate through nodes */ 14 | Agnode_t * node = agfstnode(graph); 15 | while (node != NULL) { 16 | assert(node); 17 | printf("Iterating through yet another node\n"); 18 | char * label = agget(node, "label"); 19 | printf("Node label is %s\n", label); 20 | Agedge_t * edge = agfstout(graph, node); 21 | while (edge != NULL) { 22 | assert(edge); 23 | printf("Iterating through yet another edge \n"); 24 | assert(agtail(edge) == node); 25 | edge = agnxtout(graph, edge); 26 | } 27 | 28 | /* Move on to the next node */ 29 | printf("\n"); 30 | node = agnxtnode(graph, node); 31 | } 32 | 33 | /* Free graph structure */ 34 | agclose(graph); 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /partitioning.cc: -------------------------------------------------------------------------------- 1 | #include "partitioning.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | 5 | #include "util.h" 6 | #include "clang_utility_functions.h" 7 | #include "unique_identifiers.h" 8 | #include "set_idioms.h" 9 | 10 | using namespace clang; 11 | 12 | std::string inst_block_printer(const InstBlock & iblock) { 13 | std::string ret = ""; 14 | assert_exception(not iblock.empty()); 15 | for (auto & op : iblock) ret += clang_stmt_printer(op) + ";\n"; 16 | return ret; 17 | } 18 | 19 | Graph handle_state_vars(const std::vector & stmt_vector, const Graph & dep_graph) { 20 | Graph ret = dep_graph; 21 | std::map state_reads; 22 | std::map state_writes; 23 | for (const auto * stmt : stmt_vector) { 24 | const auto * lhs = stmt->getLHS()->IgnoreParenImpCasts(); 25 | const auto * rhs = stmt->getRHS()->IgnoreParenImpCasts(); 26 | // At this stage, after stateful_flanks has been run, the only 27 | // way state variables (scalar or array-based) appear is either on the LHS or on the RHS 28 | // and they appear by themselves (not as part of another expression) 29 | // Which is why we don't need to recursively traverse an AST to check for state vars 30 | if (isa(rhs) or isa(rhs)) { 31 | // Should see exactly one read to a state variable 32 | assert_exception(state_reads.find(clang_stmt_printer(rhs)) == state_reads.end()); 33 | state_reads[clang_stmt_printer(rhs)] = stmt; 34 | } else if (isa(lhs) or isa(lhs)) { 35 | // Should see exactly one write to a state variable 36 | assert_exception(state_writes.find(clang_stmt_printer(lhs)) == state_writes.end()); 37 | state_writes[clang_stmt_printer(lhs)] = stmt; 38 | const auto state_var = clang_stmt_printer(lhs); 39 | // Check state_var exists in both maps 40 | assert_exception(state_reads.find(state_var) != state_reads.end()); 41 | assert_exception(state_writes.find(state_var) != state_writes.end()); 42 | ret.add_edge(state_reads.at(state_var), state_writes.at(state_var)); 43 | ret.add_edge(state_writes.at(state_var), state_reads.at(state_var)); 44 | } 45 | } 46 | 47 | // Check that there are pairs of reads and writes for every state variable 48 | for (const auto & pair : state_reads) { 49 | if (state_writes.find(pair.first) == state_writes.end()) { 50 | throw std::logic_error(pair.first + " has a read that isn't paired with a write "); 51 | } 52 | } 53 | return ret; 54 | } 55 | 56 | bool op_reads_var(const BinaryOperator * op, const Expr * var) { 57 | assert_exception(op); 58 | assert_exception(var); 59 | 60 | // We only check packet variables here because handle_state_vars 61 | // takes care of state variables. 62 | auto read_vars = gen_var_list(op->getRHS(), {{VariableType::PACKET, true}, 63 | {VariableType::STATE_SCALAR, false}, 64 | {VariableType::STATE_ARRAY, false}}); 65 | 66 | // If the LHS is an array subscript expression, we need to check inside the subscript as well 67 | if (isa(op->getLHS())) { 68 | const auto * array_op = dyn_cast(op->getLHS()); 69 | const auto read_vars_lhs = gen_var_list(array_op->getIdx(), {{VariableType::PACKET, true}, 70 | {VariableType::STATE_SCALAR, false}, 71 | {VariableType::STATE_ARRAY, false}}); 72 | read_vars = read_vars + read_vars_lhs; 73 | } 74 | 75 | return (read_vars.find(clang_stmt_printer(var)) != read_vars.end()); 76 | } 77 | 78 | bool depends(const BinaryOperator * op1, const BinaryOperator * op2) { 79 | // If op1 succeeds op2 in program order, 80 | // return false right away 81 | if (not (op1->getLocStart() < op2->getLocStart())) { 82 | return false; 83 | } 84 | 85 | // op1 writes the same variable that op2 writes (Write After Write) 86 | if (clang_stmt_printer(op1->getLHS()) == clang_stmt_printer(op2->getLHS())) { 87 | throw std::logic_error("Cannot have Write-After-Write dependencies in SSA form from " + clang_stmt_printer(op1) + " to " + clang_stmt_printer(op2) + "\n"); 88 | } 89 | 90 | // op1 reads a variable that op2 writes (Write After Read) 91 | if (op_reads_var(op1, op2->getLHS())) { 92 | // Make an exception for state variables. There is no way around this. 93 | // There is no need to add this edge, because handle_state_vars() does 94 | // this already. 95 | if (isa(op2->getLHS()) or isa(op2->getLHS())) { 96 | return false; 97 | } else { 98 | throw std::logic_error("Cannot have Write-After-Read dependencies in SSA form from " + clang_stmt_printer(op1) + " to " + clang_stmt_printer(op2) + "\n"); 99 | } 100 | } 101 | 102 | // op1 writes a variable (LHS) that op2 reads. (Read After Write) 103 | return (op_reads_var(op2, op1->getLHS())); 104 | } 105 | 106 | std::map> generate_partitions(const CompoundStmt * function_body) { 107 | // Verify that it's in SSA 108 | if (not is_in_ssa(function_body)) { 109 | throw std::logic_error("Partitioning will run only after program is in SSA form. This program isn't."); 110 | } 111 | // Append to a vector of const BinaryOperator * 112 | // in order of statement occurence. 113 | std::vector stmt_vector; 114 | for (const auto * child : function_body->children()) { 115 | assert_exception(isa(child)); 116 | const auto * bin_op = dyn_cast(child); 117 | assert_exception(bin_op->isAssignmentOp()); 118 | stmt_vector.emplace_back(bin_op); 119 | } 120 | 121 | // Dependency graph creation 122 | Graph dep_graph(clang_stmt_printer); 123 | for (const auto * stmt : stmt_vector) { 124 | dep_graph.add_node(stmt); 125 | } 126 | 127 | // Handle state variables specially 128 | dep_graph = handle_state_vars(stmt_vector, dep_graph); 129 | 130 | // Now add all Read After Write Dependencies, comparing a statement only with 131 | // a successor statement 132 | for (uint32_t i = 0; i < stmt_vector.size(); i++) { 133 | for (uint32_t j = i + 1; j < stmt_vector.size(); j++) { 134 | if (depends(stmt_vector.at(i), stmt_vector.at(j))) { 135 | dep_graph.add_edge(stmt_vector.at(i), stmt_vector.at(j)); 136 | } 137 | } 138 | } 139 | 140 | // Eliminate nodes with no outgoing or incoming edge 141 | std::set nodes_to_remove; 142 | for (const auto & node : dep_graph.node_set()) { 143 | if (dep_graph.pred_map().at(node).empty() and 144 | dep_graph.succ_map().at(node).empty()) { 145 | nodes_to_remove.emplace(node); 146 | } 147 | } 148 | for (const auto & node : nodes_to_remove) { 149 | dep_graph.remove_singleton_node(node); 150 | } 151 | 152 | std::cerr << dep_graph << std::endl; 153 | 154 | // Condense (https://en.wikipedia.org/wiki/Strongly_connected_component) 155 | // dep_graph after collapsing strongly connected components into one node 156 | // Pass a function to order statements within the sccs 157 | const auto & condensed_graph = dep_graph.condensation([] (const BinaryOperator * op1, const BinaryOperator * op2) 158 | {return op1->getLocStart() < op2->getLocStart();}); 159 | 160 | // Partition condensed graph using critical path scheduling 161 | const auto & partitioning = condensed_graph.critical_path_schedule(); 162 | 163 | // Output partition into valid C code, one for each timestamp 164 | std::map> codelet_bodies; 165 | std::vector> sorted_pairs(partitioning.begin(), partitioning.end()); 166 | std::sort(sorted_pairs.begin(), sorted_pairs.end(), [] (const auto & x, const auto & y) { return x.second < y.second; }); 167 | std::for_each(sorted_pairs.begin(), sorted_pairs.end(), [&codelet_bodies] (const auto & pair) 168 | { if (codelet_bodies.find(pair.second) == codelet_bodies.end()) codelet_bodies[pair.second] = std::vector(); 169 | codelet_bodies.at(pair.second).emplace_back(pair.first); }); 170 | 171 | 172 | // Draw pipeline 173 | uint32_t max_stage_id = 0; 174 | uint32_t max_codelet_id = 0; 175 | uint32_t num_codelets = 0; 176 | PipelineDrawing codelets_for_drawing; 177 | for (const auto & body_pair : codelet_bodies) { 178 | uint32_t codelet_id = 0; 179 | const uint32_t stage_id = body_pair.first; 180 | for (const auto & codelet_body : body_pair.second) { 181 | codelets_for_drawing[stage_id][codelet_id] = codelet_body; 182 | max_codelet_id = std::max(max_codelet_id, codelet_id); 183 | codelet_id++; 184 | num_codelets++; 185 | } 186 | max_stage_id = std::max(max_stage_id, stage_id); 187 | } 188 | std::cerr << draw_pipeline(codelets_for_drawing, condensed_graph) << std::endl; 189 | std::cout << "Total of " + std::to_string(max_stage_id + 1) + " stages" << std::endl; 190 | std::cout << "Maximum of " + std::to_string(max_codelet_id + 1) + " codelets/stage" << std::endl; 191 | std::cout << "Total of " << num_codelets << " codelets" << std::endl; 192 | return codelet_bodies; 193 | } 194 | 195 | std::string draw_pipeline(const PipelineDrawing & codelets_for_drawing, const Graph & condensed_graph) { 196 | // Preamble for dot (node shape, fontsize etc) 197 | std::string ret = "digraph pipeline_diagram {splines=true node [shape = box style=\"rounded,filled\" fontsize = 10];\n"; 198 | const uint32_t scale_x = 250; 199 | const uint32_t scale_y = 75; 200 | 201 | // Print out nodes 202 | for (const auto & stageid_with_codelet_map : codelets_for_drawing) { 203 | const uint32_t stage_id = stageid_with_codelet_map.first; 204 | for (const auto & codelet_pair : stageid_with_codelet_map.second) { 205 | const uint32_t codelet_id = codelet_pair.first; 206 | const auto codelet = codelets_for_drawing.at(stage_id).at(codelet_id); 207 | const auto codelet_as_str = inst_block_printer(codelet); 208 | ret += hash_string(codelet_as_str) + " [label = \"" 209 | + codelet_as_str + "\"" 210 | + " pos = \"" 211 | + std::to_string(scale_x * stage_id) + "," + std::to_string(scale_y * codelet_id) + "\"" 212 | + " fillcolor=" + (codelet.size() > 1 ? "darkturquoise" : "white") 213 | + "];\n"; 214 | } 215 | } 216 | 217 | // Print out edges 218 | for (const auto & node_pair : condensed_graph.succ_map()) 219 | for (const auto & neighbor : node_pair.second) 220 | ret += hash_string(inst_block_printer(node_pair.first)) + " -> " + 221 | hash_string(inst_block_printer(neighbor)) + " ;\n"; 222 | ret += "}"; 223 | return ret; 224 | } 225 | 226 | std::string partitioning_transform(const TranslationUnitDecl * tu_decl, const uint32_t pipeline_depth, const uint32_t pipeline_width) { 227 | const auto & id_set = identifier_census(tu_decl); 228 | 229 | // Storage for returned string 230 | std::string ret; 231 | 232 | // Create unique identifier generator 233 | UniqueIdentifiers unique_identifiers(id_set); 234 | 235 | for (const auto * child_decl : dyn_cast(tu_decl)->decls()) { 236 | assert_exception(child_decl); 237 | if (isa(child_decl) or 238 | isa(child_decl)) { 239 | // Pass through these declarations as is 240 | ret += clang_decl_printer(child_decl) + ";"; 241 | } else if (isa(child_decl) and (not is_packet_func(dyn_cast(child_decl)))) { 242 | ret += generate_scalar_func_def(dyn_cast(child_decl)); 243 | } else if (isa(child_decl) and (is_packet_func(dyn_cast(child_decl)))) { 244 | const auto * function_decl = dyn_cast(child_decl); 245 | 246 | // Extract function signature 247 | assert_exception(function_decl->getNumParams() >= 1); 248 | const auto * pkt_param = function_decl->getParamDecl(0); 249 | const auto pkt_type = function_decl->getParamDecl(0)->getType().getAsString(); 250 | const auto pkt_name = clang_value_decl_printer(pkt_param); 251 | 252 | // Transform function body 253 | const auto codelet_bodies = generate_partitions(dyn_cast(function_decl->getBody())); 254 | 255 | // Create codelet functions with new bodies, encode stage_id and codelet_id within function signature. 256 | for (const auto & body_pair : codelet_bodies) { 257 | uint32_t codelet_id = 0; 258 | const uint32_t stage_id = body_pair.first; 259 | for (const auto & codelet_body : body_pair.second) { 260 | const auto codelet_body_as_str = function_decl->getReturnType().getAsString() + " " + 261 | "_codelet_" + std::to_string(stage_id) + "_" + std::to_string(codelet_id) + 262 | "( " + pkt_type + " " + pkt_name + ") { " + 263 | inst_block_printer(codelet_body) + "}\n"; 264 | codelet_id++; 265 | ret += codelet_body_as_str; 266 | } 267 | } 268 | 269 | // count number of stages in the pipeline and max stage width 270 | uint32_t max_stage_id = 0; 271 | uint32_t max_codelet_id = 0; 272 | for (const auto & body_pair : codelet_bodies) { 273 | uint32_t codelet_id = 0; 274 | const uint32_t stage_id = body_pair.first; 275 | for (const auto & codelet_body __attribute__ ((unused)) : body_pair.second) { 276 | max_codelet_id = std::max(max_codelet_id, codelet_id); 277 | codelet_id++; 278 | } 279 | max_stage_id = std::max(max_stage_id, stage_id); 280 | } 281 | if ((max_stage_id + 1) > pipeline_depth) { 282 | const auto diagnostics = "// Pipeline depth of " + std::to_string(max_stage_id + 1) + " exceeds allowed pipeline depth of " + std::to_string(pipeline_depth); 283 | throw std::logic_error(diagnostics); 284 | } 285 | if ((max_codelet_id + 1) > pipeline_width) { 286 | const auto diagnostics = "// Pipeline width of " + std::to_string(max_codelet_id + 1) + " exceeds allowed pipeline width of " + std::to_string(pipeline_width); 287 | throw std::logic_error(diagnostics); 288 | } 289 | } 290 | } 291 | return ret; 292 | } 293 | -------------------------------------------------------------------------------- /partitioning.h: -------------------------------------------------------------------------------- 1 | #ifndef PARTITIONING_H_ 2 | #define PARTITIONING_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "clang/AST/Expr.h" 9 | #include "clang/AST/Decl.h" 10 | 11 | #include "graph.h" 12 | 13 | /// Typedef for a set of BinaryOperator's sequenced one after the other 14 | typedef std::vector InstBlock; 15 | 16 | /// Typedef for a data structure mapping 17 | /// from stage_id + codelet_id 18 | /// to InstBlock 19 | /// for diagramming them out in a dot file 20 | typedef std::map> PipelineDrawing; 21 | 22 | /// Printer for an InstBlock 23 | std::string inst_block_printer(const InstBlock & iblock); 24 | 25 | /// Identify statements that read from and write to state 26 | /// And create a back edge from the write back to the read. 27 | /// This is really the crux of the compiler: 28 | /// back edges from stateful writes to the next stateful read. 29 | Graph handle_state_vars(const std::vector & stmt_vector, const Graph & dep_graph); 30 | 31 | /// Does a particular operation read a variable 32 | bool op_reads_var(const clang::BinaryOperator * op, const clang::Expr * var); 33 | 34 | /// Is there a dependence from op1 to op2? 35 | /// Requiring op1 to be executed before op2? 36 | bool depends(const clang::BinaryOperator * op1, const clang::BinaryOperator * op2); 37 | 38 | /// Print out dependency graph once Stongly Connected Components 39 | /// have been condensed together to form a DAG. 40 | /// Also partition the code based on the dependency graph 41 | /// and generate a function declaration with a body for each partition 42 | /// as a string for each timestamp 43 | std::map> generate_partitions(const clang::CompoundStmt * function_body); 44 | 45 | /// Return pipeline diagram as a dot file 46 | std::string draw_pipeline(const PipelineDrawing & codelets_for_drawing, const Graph & condensed_graph); 47 | 48 | /// Entry point to partitioning logic, called by SinglePass 49 | std::string partitioning_transform(const clang::TranslationUnitDecl * tu_decl, const uint32_t pipeline_depth, const uint32_t pipeline_width); 50 | 51 | #endif // PARTITIONING_H_ 52 | -------------------------------------------------------------------------------- /pkt_func_transform.cc: -------------------------------------------------------------------------------- 1 | #include "pkt_func_transform.h" 2 | 3 | #include 4 | 5 | #include "third_party/assert_exception.h" 6 | 7 | #include "clang_utility_functions.h" 8 | 9 | using namespace clang; 10 | 11 | int get_order(const Decl * decl) { 12 | assert_exception(decl != nullptr); 13 | if (isa(decl)) return 1; 14 | else if (isa(decl) and (not is_packet_func(dyn_cast(decl)))) return 2; 15 | else if (isa(decl) and (is_packet_func(dyn_cast(decl)))) return 3; 16 | else if (isa(decl)) return 4; 17 | else if (isa(decl)) return 5; 18 | else { 19 | throw std::logic_error("get_order in pkt_func_transform.cc cannot handle decl " + clang_decl_printer(decl) + " of type " + std::string(decl->getDeclKindName())); 20 | } 21 | } 22 | 23 | std::string pkt_func_transform(const TranslationUnitDecl * tu_decl, 24 | const FuncBodyTransform & func_body_transform) { 25 | // Accumulate all declarations 26 | std::vector all_decls; 27 | for (const auto * decl : dyn_cast(tu_decl)->decls()) 28 | all_decls.emplace_back(decl); 29 | 30 | // Sort all_decls 31 | std::sort(all_decls.begin(), 32 | all_decls.end(), 33 | [] (const auto * decl1, const auto * decl2) 34 | { return get_order(decl1) < get_order(decl2); }); 35 | 36 | // Loop through sorted vector of declarations 37 | std::string state_var_str = ""; 38 | std::string scalar_func_str = ""; 39 | std::string pkt_func_str = ""; 40 | std::string record_decl_str = ""; 41 | std::vector new_decls; 42 | for (const auto * child_decl : all_decls) { 43 | assert_exception(child_decl); 44 | if (isa(child_decl)) { 45 | state_var_str += clang_decl_printer(child_decl) + ";"; 46 | } else if ((isa(child_decl) and (not is_packet_func(dyn_cast(child_decl))))) { 47 | scalar_func_str += generate_scalar_func_def(dyn_cast(child_decl)); 48 | } else if (isa(child_decl) and (is_packet_func(dyn_cast(child_decl)))) { 49 | const auto * function_decl = dyn_cast(child_decl); 50 | 51 | // Extract function signature 52 | assert_exception(function_decl->getNumParams() >= 1); 53 | const auto * pkt_param = function_decl->getParamDecl(0); 54 | const auto pkt_type = function_decl->getParamDecl(0)->getType().getAsString(); 55 | const auto pkt_name = clang_value_decl_printer(pkt_param); 56 | 57 | // Transform function body 58 | const auto transform_pair = func_body_transform(dyn_cast(function_decl->getBody()), pkt_name); 59 | const auto transformed_body = transform_pair.first; 60 | new_decls = transform_pair.second; 61 | 62 | // Rewrite function with new body 63 | pkt_func_str += function_decl->getReturnType().getAsString() + " " + 64 | function_decl->getNameInfo().getName().getAsString() + 65 | "( " + pkt_type + " " + pkt_name + ") " + 66 | transformed_body; 67 | } else if (isa(child_decl)) { 68 | // Open struct definition 69 | assert_exception(dyn_cast(child_decl)->isStruct()); 70 | record_decl_str += "struct " + dyn_cast(child_decl)->getNameAsString() + "{\n"; 71 | 72 | // acummulate current fields in struct 73 | for (const auto * field_decl : dyn_cast(child_decl)->decls()) 74 | record_decl_str += dyn_cast(field_decl)->getType().getAsString() + " " + clang_value_decl_printer(dyn_cast(field_decl)) + ";"; 75 | 76 | // Add newly created fields 77 | for (const auto & new_decl : new_decls) 78 | record_decl_str += new_decl; 79 | 80 | // Close struct definition 81 | record_decl_str += "};"; 82 | } 83 | } 84 | return state_var_str + scalar_func_str + record_decl_str + pkt_func_str; 85 | } 86 | -------------------------------------------------------------------------------- /pkt_func_transform.h: -------------------------------------------------------------------------------- 1 | #ifndef PKT_FUNC_TRANSFORM_H_ 2 | #define PKT_FUNC_TRANSFORM_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "clang/AST/Decl.h" 10 | #include "clang/AST/Stmt.h" 11 | 12 | /// Order of parsing declarations (state, packets, state functions, packet functions) 13 | int get_order(const clang::Decl * decl); 14 | 15 | /// Convenience typedef for a function that transforms a function body 16 | typedef std::function>(const clang::CompoundStmt *, const std::string & pkt_name)> FuncBodyTransform; 17 | 18 | /// Tranform a translation unit by modifying packet functions 19 | /// alone. Pass through the rest as such without modifications. 20 | std::string pkt_func_transform(const clang::TranslationUnitDecl * tu_decl, 21 | const FuncBodyTransform & func_body_transform); 22 | 23 | #endif // PKT_FUNC_TRANSFORM_H_ 24 | -------------------------------------------------------------------------------- /redundancy_remover.cc: -------------------------------------------------------------------------------- 1 | #include "redundancy_remover.h" 2 | 3 | #include 4 | 5 | #include "clang/AST/Expr.h" 6 | 7 | #include "third_party/assert_exception.h" 8 | 9 | #include "clang_utility_functions.h" 10 | #include "pkt_func_transform.h" 11 | 12 | using namespace clang; 13 | 14 | std::string redundancy_remover_transform(const clang::TranslationUnitDecl * tu_decl) { 15 | return pkt_func_transform(tu_decl, redundancy_remover_helper); 16 | } 17 | 18 | std::pair> redundancy_remover_helper(const clang::CompoundStmt * body, 19 | const std::string & pkt_name __attribute__((unused))) { 20 | return std::make_pair("{" + redundancy_remover_stmt(body) + "}", std::vector()); 21 | } 22 | 23 | std::string redundancy_remover_stmt(const Stmt * stmt) { 24 | assert_exception(stmt); 25 | if(isa(stmt)) { 26 | const auto * comp_stmt = dyn_cast(stmt); 27 | 28 | // Check if you nest exactly one CompoundStmt 29 | if (comp_stmt->size() == 1 and isa(comp_stmt->body_back())) { 30 | std::cerr << "// Nesting exactly one CompoundStmt" << std::endl; 31 | return clang_stmt_printer(comp_stmt->body_back()); // My version of libclang doesn't have a body_front() 32 | } else { 33 | std::string ret; 34 | for (const auto & stmt : comp_stmt->body()) { 35 | ret += redundancy_remover_stmt(stmt) + ";"; 36 | } 37 | return ret; 38 | } 39 | } else if (isa(stmt)) { 40 | const auto * cond_op = dyn_cast(stmt); 41 | return redundancy_remover_stmt(cond_op->getCond()) + " ? " 42 | + redundancy_remover_stmt(cond_op->getTrueExpr()) + " : " 43 | + redundancy_remover_stmt(cond_op->getFalseExpr()); 44 | } else if (isa(stmt)) { 45 | const auto * bin_op = dyn_cast(stmt); 46 | return redundancy_remover_stmt(bin_op->getLHS()) + std::string(bin_op->getOpcodeStr()) + redundancy_remover_stmt(bin_op->getRHS()); 47 | } else if (isa(stmt) or isa(stmt) or isa(stmt)) { 48 | return clang_stmt_printer(stmt); 49 | } else if (isa(stmt)) { 50 | const auto * array_op = dyn_cast(stmt); 51 | return redundancy_remover_stmt(array_op->getBase()) + "[" + redundancy_remover_stmt(array_op->getIdx()) + "]"; 52 | } else if (isa(stmt)) { 53 | const auto * paren_expr = dyn_cast(stmt); 54 | if (isa(paren_expr->getSubExpr()) or 55 | isa(paren_expr->getSubExpr()) or 56 | isa(paren_expr->getSubExpr()) or 57 | isa(paren_expr->getSubExpr())) { 58 | return clang_stmt_printer(paren_expr->getSubExpr()); 59 | } 60 | else return "(" + redundancy_remover_stmt(paren_expr->getSubExpr()) + ")"; 61 | } else if (isa(stmt)) { 62 | const auto * un_op = dyn_cast(stmt); 63 | assert_exception(un_op->isArithmeticOp()); 64 | const auto opcode_str = std::string(UnaryOperator::getOpcodeStr(un_op->getOpcode())); 65 | assert_exception(opcode_str == "!"); 66 | return opcode_str + redundancy_remover_stmt(un_op->getSubExpr()); 67 | } else if (isa(stmt)) { 68 | return redundancy_remover_stmt(dyn_cast(stmt)->getSubExpr()); 69 | } else if (isa(stmt)) { 70 | const auto * call_expr = dyn_cast(stmt); 71 | std::string ret = clang_stmt_printer(call_expr->getCallee()) + "("; 72 | for (const auto * child : call_expr->arguments()) { 73 | const auto child_str = redundancy_remover_stmt(child); 74 | ret += child_str + ","; 75 | } 76 | ret.back() = ')'; 77 | return ret; 78 | } else { 79 | throw std::logic_error("redundancy_remover_stmt cannot handle stmt of type " + std::string(stmt->getStmtClassName())); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /redundancy_remover.h: -------------------------------------------------------------------------------- 1 | #ifndef REDUNDANCY_REMOVER_H_ 2 | #define REDUNDANCY_REMOVER_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include "clang/AST/Stmt.h" 8 | #include "clang/AST/Decl.h" 9 | 10 | /// Entry point from FixedPointPass, 11 | /// which immediately delegates to redundancy_remover_helper 12 | std::string redundancy_remover_transform(const clang::TranslationUnitDecl * tu_decl); 13 | 14 | // helper function to initiate recursive call of redundancy_remover_helper 15 | // on function body 16 | std::pair> redundancy_remover_helper(const clang::CompoundStmt * body, 17 | const std::string & pkt_name __attribute__((unused))); 18 | 19 | // Recurse on stmt and continue simplifying by replacing all combinations 20 | // of ParenExpr(ParenExpr) with a single ParenExpr 21 | // and all combinations of CompoundStmt(CompoundStmt) with a single CompoundStmt 22 | std::string redundancy_remover_stmt(const clang::Stmt * stmt); 23 | 24 | #endif // REDUNDANCY_REMOVER_H_ 25 | -------------------------------------------------------------------------------- /set_idioms.h: -------------------------------------------------------------------------------- 1 | #ifndef SET_IDIOMS_H_ 2 | #define SET_IDIOMS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | template 10 | std::ostream & operator<<(std::ostream & out, const std::set & set) { 11 | out << "{"; 12 | for (const auto & node : set) { 13 | out << node << " "; 14 | } 15 | out << "}"; 16 | return out; 17 | } 18 | 19 | // Define more idiomatic set union as '+' operator 20 | template 21 | std::set operator+(const std::set & a, const std::set & b) { 22 | std::vector temp; 23 | std::set_union(a.begin(), a.end(), 24 | b.begin(), b.end(), 25 | std::back_inserter(temp)); 26 | return std::set(temp.begin(), temp.end()); 27 | } 28 | 29 | // Define more idiomatic set difference as '-' operator 30 | template 31 | std::set operator-(const std::set & a, const std::set & b) { 32 | std::vector temp; 33 | std::set_difference(a.begin(), a.end(), 34 | b.begin(), b.end(), 35 | std::back_inserter(temp)); 36 | return std::set(temp.begin(), temp.end()); 37 | } 38 | 39 | // Define more idiomatic set intersection as '*' operator 40 | template 41 | std::set operator*(const std::set & a, const std::set & b) { 42 | std::vector temp; 43 | std::set_intersection(a.begin(), a.end(), 44 | b.begin(), b.end(), 45 | std::back_inserter(temp)); 46 | return std::set(temp.begin(), temp.end()); 47 | } 48 | 49 | #endif // SET_IDIOMS_H_ 50 | -------------------------------------------------------------------------------- /sketch_backend.h: -------------------------------------------------------------------------------- 1 | #ifndef SKETCH_BACKEND_H_ 2 | #define SKETCH_BACKEND_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "clang/AST/Decl.h" 10 | #include "clang/AST/Stmt.h" 11 | #include "clang/AST/Expr.h" 12 | 13 | /// String representing a packet field like "p.x" 14 | typedef std::string PktField; 15 | 16 | /// String represent a scalar variable name like "x" 17 | typedef std::string ScalarVarName; 18 | 19 | struct PacketFieldPair { 20 | std::set incoming_fields = {}; 21 | std::set defined_fields = {}; 22 | }; 23 | 24 | /// Extract packet fields from function body 25 | /// i.e. non-array subscript packet fields that are 26 | /// either "external" arguments to the codelet (incoming_fields) or 27 | /// are defined by the codelet body (defined_fields) 28 | PacketFieldPair extract_packet_fields(const clang::Stmt * function_body); 29 | 30 | /// Core logic that coalesces arguments in a codelet if they are correlated 31 | std::string coalesce_args(const clang::Stmt * function_body, 32 | const std::map> & providers, 33 | const std::map & provider_expressions); 34 | 35 | /// Transform codelets by reducing the number of incoming packet 36 | /// fields if there are correlations between the packet fields 37 | std::string sketch_preprocessor(const clang::TranslationUnitDecl * tu_decl); 38 | 39 | /// Transform code blocks (or) equivalently, strongly connected 40 | /// components into SKETCH specifications. 41 | /// SKETCH specifications can then be fed to the SKETCH compiler to 42 | /// synthesize configurations for atoms 43 | /// (represented as sketches with holes). 44 | std::string sketch_backend_transform(const clang::TranslationUnitDecl * tu_decl, const std::string t_atom_template); 45 | 46 | /// Transfrom a Stmt representing a function body 47 | /// into a SKETCH specification. 48 | std::string create_sketch_spec(const clang::Stmt * function_body, 49 | const std::string & spec_name); 50 | 51 | /// Collect all state variable expressions recursively from a clang::Stmt 52 | /// TODO: This is very similar to gen_var_list with a few minor differences. 53 | /// All of these are symptoms that we need a recursive traversal mechansim 54 | /// that is generic and spans all these AST traversal functions. 55 | std::set collect_state_vars(const clang::Stmt * function_body); 56 | 57 | #endif // SKETCH_BACKEND_H_ 58 | -------------------------------------------------------------------------------- /ssa.cc: -------------------------------------------------------------------------------- 1 | #include "ssa.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #include "third_party/assert_exception.h" 9 | 10 | #include "clang/AST/Expr.h" 11 | 12 | #include "clang_utility_functions.h" 13 | #include "unique_identifiers.h" 14 | #include "pkt_func_transform.h" 15 | 16 | using namespace clang; 17 | using std::placeholders::_1; 18 | using std::placeholders::_2; 19 | 20 | std::string ssa_transform(const TranslationUnitDecl * tu_decl) { 21 | const auto & id_set = identifier_census(tu_decl); 22 | return pkt_func_transform(tu_decl, std::bind(ssa_rewrite_fn_body, _1, _2, id_set)); 23 | } 24 | 25 | std::pair> ssa_rewrite_fn_body(const CompoundStmt * function_body, const std::string & pkt_name, const std::set & id_set) { 26 | // Vector of newly created packet temporaries 27 | std::vector new_decls = {}; 28 | 29 | // Create unique identifier set 30 | UniqueIdentifiers unique_identifiers(id_set); 31 | 32 | // All indices where every packet variable is defined/written/is on the LHS. 33 | // We choose to rename ALL definitions/writes of a packet variable 34 | // rather than just the redefinitions because there might 35 | // be reads preceding the first definition/write 36 | // and it's correct to rename all definitions. 37 | std::map> def_locs; 38 | int index = 0; 39 | for (const auto * child : function_body->children()) { 40 | assert_exception(isa(child)); 41 | const auto * bin_op = dyn_cast(child); 42 | assert_exception(bin_op->isAssignmentOp()); 43 | 44 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 45 | 46 | if (isa(lhs)) { 47 | if (def_locs.find(clang_stmt_printer(lhs)) == def_locs.end()) { 48 | def_locs[clang_stmt_printer(lhs)] = {index}; 49 | } else { 50 | def_locs.at(clang_stmt_printer(lhs)).emplace_back(index); 51 | } 52 | } 53 | index++; 54 | } 55 | 56 | // Now, do the renaming, storing output in function_body 57 | std::string function_body_str; 58 | index = 0; 59 | 60 | // Map from packet variable name to replacement used for renaming 61 | std::map current_packet_var_replacements; 62 | 63 | // Map from field name to replacement used for renaming 64 | // (mirrors current_packet_var_replacements, but instead of storing p.x, 65 | // we store x). 66 | std::map current_packet_field_replacements; 67 | 68 | for (const auto * child : function_body->children()) { 69 | assert_exception(isa(child)); 70 | const auto * bin_op = dyn_cast(child); 71 | assert_exception(bin_op->isAssignmentOp()); 72 | 73 | // First rewrite RHS using whatever replacements we currently have 74 | const std::string rhs_str = replace_vars(bin_op->getRHS(), current_packet_var_replacements, 75 | {{VariableType::STATE_SCALAR, false}, {VariableType::STATE_ARRAY, false}, {VariableType::PACKET, true}}); 76 | 77 | // Now look at LHS 78 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 79 | if (isa(lhs)) { 80 | // Assert that this definition and current value of index show up in def_locs 81 | const std::string lhs_var = clang_stmt_printer(lhs); 82 | assert_exception(def_locs.find(lhs_var) != def_locs.end()); 83 | assert_exception(std::find(def_locs.at(lhs_var).begin(), def_locs.at(lhs_var).end(), index) != def_locs.at(lhs_var).end()); 84 | 85 | // Modify replacements 86 | const auto var_type = dyn_cast(lhs)->getMemberDecl()->getType().getAsString(); 87 | const auto new_tmp_var = unique_identifiers.get_unique_identifier(dyn_cast(lhs)->getMemberDecl()->getNameAsString()); 88 | const auto var_decl = var_type + " " + new_tmp_var + ";"; 89 | new_decls.emplace_back(var_decl); 90 | current_packet_var_replacements[lhs_var] = pkt_name + "." + new_tmp_var; 91 | current_packet_field_replacements[dyn_cast(lhs)->getMemberDecl()->getNameAsString()] = new_tmp_var; 92 | } 93 | 94 | // Now rewrite LHS 95 | assert_exception(bin_op->getLHS()); 96 | const std::string lhs_str = replace_vars(bin_op->getLHS(), current_packet_var_replacements, 97 | {{VariableType::STATE_SCALAR, false}, {VariableType::STATE_ARRAY, false}, {VariableType::PACKET, true}}); 98 | function_body_str += lhs_str + " = " + rhs_str + ";"; 99 | index++; 100 | } 101 | 102 | // Print out the final replacements, i.e. the value of current_packet_var_replacements at this point 103 | for (const auto & repl_pair : current_packet_field_replacements) 104 | std::cerr << "// " + repl_pair.first << " " << repl_pair.second << std::endl; 105 | 106 | return std::make_pair("{" + function_body_str + "}", new_decls); 107 | } 108 | -------------------------------------------------------------------------------- /ssa.h: -------------------------------------------------------------------------------- 1 | #ifndef SSA_H_ 2 | #define SSA_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include "clang/AST/Decl.h" 9 | 10 | // Static Single-Assignment form for function body, excluding the final write 11 | // in the write epilogue to state variables. This guarantees that each packet 12 | // variable is assigned exactly once. If it is assigned more than once, perform 13 | // simple renaming. SSA is very simple in domino because we have no branches and no phi nodes. 14 | std::string ssa_transform(const clang::TranslationUnitDecl * tu_decl); 15 | 16 | /// Helper function that does most of the heavy lifting in SSA, by rewriting the function body into SSA form. 17 | std::pair> ssa_rewrite_fn_body(const clang::CompoundStmt * function_body, const std::string & pkt_name, const std::set & id_set); 18 | 19 | #endif // SSA_H_ 20 | -------------------------------------------------------------------------------- /stateful_flanks.cc: -------------------------------------------------------------------------------- 1 | #include "stateful_flanks.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "third_party/assert_exception.h" 7 | 8 | #include "clang/AST/Expr.h" 9 | 10 | #include "clang_utility_functions.h" 11 | #include "unique_identifiers.h" 12 | #include "pkt_func_transform.h" 13 | 14 | using namespace clang; 15 | using std::placeholders::_1; 16 | using std::placeholders::_2; 17 | 18 | std::pair> add_stateful_flanks(const CompoundStmt * function_body, const std::string & pkt_name, const std::set & id_set) { 19 | // Vector of newly created packet temporaries 20 | std::vector new_decls = {}; 21 | 22 | // Generate unique identifiers for packet temporaries, 23 | // when replacing state variables with packet variables 24 | UniqueIdentifiers unique_identifiers(id_set); 25 | 26 | // Run through function_body, storing all lhs->rhs mappings 27 | // i.e. variable (packet, state scalar, or state array) to expression 28 | typedef std::string VariableName; 29 | typedef std::string Expression; 30 | std::map var_expr_map; 31 | for (const auto * child : function_body->children()) { 32 | assert_exception(isa(child)); 33 | const auto * bin_op = dyn_cast(child); 34 | assert_exception(bin_op->isAssignmentOp()); 35 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 36 | const auto * rhs = bin_op->getRHS()->IgnoreParenImpCasts(); 37 | var_expr_map[clang_stmt_printer(lhs)] = clang_stmt_printer(rhs); 38 | } 39 | 40 | // 1. Identify all stateful variables in the program. 41 | // 2. Create a read prologue for all of them: each state variable is read into a packet temporary. 42 | // 3. Create a write epilogue for all of them: the packet temporary from 2. is written back into state. 43 | // 4. Populate a state variable table to replace state variables with temporaries in the function body. 44 | std::string read_prologue = ""; 45 | std::string write_epilogue = ""; 46 | std::map state_var_table; 47 | std::set subscript_vars; 48 | for (const auto * child : function_body->children()) { 49 | assert_exception(isa(child)); 50 | const auto * bin_op = dyn_cast(child); 51 | assert_exception(bin_op->isAssignmentOp()); 52 | 53 | // Strip off parenthesis and casts on lhs 54 | const auto * lhs = bin_op->getLHS()->IgnoreParenImpCasts(); 55 | 56 | // If lhs is a DeclRefExpr or an ArraySubscriptExpr, it's a stateful variable 57 | // It's sufficient to inspect LHS alone to determine stateful variables, 58 | // because we are assuming a stateful variable is written at least once. 59 | // Otherwise, I don't see the point of a stateful variable. 60 | if (isa(lhs) or isa(lhs)) { 61 | const std::string state_var = clang_stmt_printer(lhs); 62 | if (state_var_table.find(state_var) == state_var_table.end()) { 63 | const auto var_type = isa(lhs) ? dyn_cast(lhs)->getDecl()->getType().getAsString() : 64 | dyn_cast(lhs)->getType().getAsString(); 65 | const auto new_tmp_var = unique_identifiers.get_unique_identifier(isa(lhs) ? state_var 66 | : clang_stmt_printer(dyn_cast(lhs)->getBase())); 67 | const auto var_decl = var_type + " " + new_tmp_var + ";"; 68 | new_decls.emplace_back(var_decl); 69 | 70 | const auto pkt_tmp_var = pkt_name + "." + new_tmp_var; 71 | 72 | // Read from a state variable into a packet temporary 73 | // If it's an array, read index into a newly created packet temporary and move to prologue too 74 | if (isa(lhs)) { 75 | assert_exception(isa(dyn_cast(lhs)->getIdx()->IgnoreParenImpCasts())); 76 | const auto subscript_var = clang_stmt_printer(dyn_cast(lhs)->getIdx()); 77 | subscript_vars.emplace(subscript_var); 78 | const auto pkt_field_in_subscript = dyn_cast(dyn_cast(lhs)->getIdx()->IgnoreParenImpCasts())->getMemberDecl()->getNameAsString(); 79 | 80 | // Create a temporary variable for subscript_var 81 | const auto new_tmp_var_for_subscript = unique_identifiers.get_unique_identifier(pkt_field_in_subscript); 82 | const auto subscript_var_decl = "int " + new_tmp_var_for_subscript + ";"; 83 | new_decls.emplace_back(subscript_var_decl); 84 | 85 | // Prefix it with "packet." 86 | const auto new_subscript_var = pkt_name + "." + new_tmp_var_for_subscript; 87 | 88 | // Create read and write flanks for it 89 | if (var_expr_map.find(subscript_var) == var_expr_map.end()) { 90 | throw std::logic_error(" subscript_var " + subscript_var + " not found in var_expr_map"); 91 | } 92 | read_prologue += new_subscript_var + " = " + var_expr_map.at(subscript_var) + ";"; 93 | read_prologue += pkt_tmp_var + " = " + replace_subscript_expr(dyn_cast(lhs), new_subscript_var) + ";"; 94 | write_epilogue += replace_subscript_expr(dyn_cast(lhs), new_subscript_var) + " = " + pkt_tmp_var + ";"; 95 | 96 | // Print out the rename of subscript_var for jayhawk to use 97 | std::cerr << "// " << pkt_field_in_subscript << " " << new_tmp_var_for_subscript << std::endl; 98 | } else { 99 | read_prologue += pkt_tmp_var + " = " + state_var + ";"; 100 | write_epilogue += state_var + " = " + pkt_tmp_var + ";"; 101 | } 102 | state_var_table[state_var] = pkt_tmp_var; 103 | } 104 | } 105 | } 106 | 107 | // Now, replace all occurences of the stateful variables throughout the code 108 | std::string function_body_str; 109 | for (const auto * child : function_body->children()) { 110 | assert_exception(isa(child)); 111 | const auto * bin_op = dyn_cast(child); 112 | assert_exception(bin_op->isAssignmentOp()); 113 | 114 | if (subscript_vars.find(clang_stmt_printer(bin_op->getLHS())) != subscript_vars.end()) { 115 | // This is a subscript variable, it's already been moved into the prologue 116 | continue; 117 | } 118 | 119 | function_body_str += replace_vars(bin_op->getLHS(), state_var_table, {{VariableType::STATE_SCALAR, true}, {VariableType::STATE_ARRAY, true}, {VariableType::PACKET, false}}) + " = " 120 | + replace_vars(bin_op->getRHS(), state_var_table, {{VariableType::STATE_SCALAR, true}, {VariableType::STATE_ARRAY, true}, {VariableType::PACKET, false}}) + ";"; 121 | } 122 | 123 | return std::make_pair("{" + read_prologue + "\n\n" + function_body_str + "\n\n" + write_epilogue + "}", new_decls); 124 | } 125 | 126 | std::string replace_subscript_expr(const ArraySubscriptExpr * array_op, const std::string & new_subscript) { 127 | assert_exception(array_op); 128 | assert_exception(isa(array_op->getIdx()->IgnoreParenImpCasts())); 129 | return clang_stmt_printer(array_op->getBase()) + "[" + new_subscript + "]"; 130 | } 131 | 132 | std::string stateful_flank_transform(const TranslationUnitDecl * tu_decl) { 133 | const auto & id_set = identifier_census(tu_decl); 134 | return pkt_func_transform(tu_decl, std::bind(add_stateful_flanks, _1, _2, id_set)); 135 | } 136 | -------------------------------------------------------------------------------- /stateful_flanks.h: -------------------------------------------------------------------------------- 1 | #ifndef STATEFUL_FLANKS_H_ 2 | #define STATEFUL_FLANKS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include "clang/AST/Decl.h" 10 | #include "clang/AST/Stmt.h" 11 | #include "clang/AST/Expr.h" 12 | 13 | /// Intermediate representation where we have a read prologue in which 14 | /// all state variables are read into temporary variables. Then the rest 15 | /// of the program operates on these temporary variables. We close the program 16 | /// with a write epilogue that takes temporary variables and writes them into state variables again 17 | std::pair> add_stateful_flanks(const clang::CompoundStmt * function_body, const std::string & pkt_name, const std::set & id_set); 18 | 19 | /// Replace subscript expression in an array with the supplied new_subscript 20 | /// string. Turns a[anything] into a[new_subscript] 21 | std::string replace_subscript_expr(const clang::ArraySubscriptExpr * array_op, const std::string & new_subscript); 22 | 23 | // Entry point to code that adds stateful flanks to a function body 24 | std::string stateful_flank_transform(const clang::TranslationUnitDecl * tu_decl); 25 | 26 | #endif // STATEFUL_FLANKS_H_ 27 | -------------------------------------------------------------------------------- /tests/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) -isystem $(srcdir)/../third_party/ 2 | AM_CPPFLAGS = -isystem $(CLANG_DEV_LIBS)/include/ 3 | AM_LDFLAGS = -L $(CLANG_DEV_LIBS)/lib 4 | 5 | # There are some strange circular deps within clang requiring us to list libraries twice 6 | # https://github.com/eliben/llvm-clang-samples/blob/master/Makefile#L71 7 | CLANG_LLVM_LIBS = -lclangAST -lclangAnalysis -lclangBasic -lclangDriver -lclangEdit -lclangFrontend -lclangFrontendTool -lclangLex -lclangParse -lclangSema -lclangSerialization\ 8 | -lclangAST -lclangAnalysis -lclangBasic -lclangDriver -lclangEdit -lclangFrontend -lclangFrontendTool -lclangLex -lclangParse -lclangSema -lclangSerialization\ 9 | -lLLVMOption -lLLVMMCParser -lLLVMBitReader -lLLVMMC -lLLVMSupport -lz -lpthread -ltermcap -ldl 10 | 11 | LDADD = $(CLANG_LLVM_LIBS) $(srcdir)/../third_party/libgtest.a $(srcdir)/../libdomino.a $(srcdir)/../third_party/libmahimahi.a -lpthread 12 | 13 | # Define unit tests 14 | TESTS = echo_test if_converter_test expr_flattener_test scc_test complicated_scc_test 15 | check_PROGRAMS = $(TESTS) 16 | 17 | # domino tests 18 | echo_test_SOURCES = echo_test.cc 19 | echo_test_CXXFLAGS = $(AM_CXXFLAGS) -fno-rtti 20 | if_converter_test_SOURCES = if_converter_test.cc 21 | if_converter_test_CXXFLAGS = $(AM_CXXFLAGS) -fno-rtti 22 | expr_flattener_test_SOURCES = expr_flattener_test.cc 23 | expr_flattener_test_CXXFLAGS = $(AM_CXXFLAGS) -fno-rtti 24 | scc_test_SOURCES = scc_test.cc 25 | scc_test_CXXFLAGS = $(AM_CXXFLAGS) -fno-rtti 26 | complicated_scc_test_SOURCES = complicated_scc_test.cc 27 | complicated_scc_test_CXXFLAGS = $(AM_CXXFLAGS) -fno-rtti 28 | -------------------------------------------------------------------------------- /tests/array_example.c: -------------------------------------------------------------------------------- 1 | int a[256]; 2 | 3 | struct Packet { 4 | int x; 5 | }; 6 | 7 | void func(struct Packet p) { 8 | a[0] = a[0] + 1; 9 | } 10 | -------------------------------------------------------------------------------- /tests/array_with_index_vars.c: -------------------------------------------------------------------------------- 1 | int a[256]; 2 | struct Packet {int x;}; 3 | 4 | void func(struct Packet p) { 5 | a[p.x] = 1; 6 | a[p.x] = a[p.x] + 1; 7 | } 8 | -------------------------------------------------------------------------------- /tests/complicated_scc_test.cc: -------------------------------------------------------------------------------- 1 | #include "../graph.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "gtest.h" 7 | 8 | TEST(SccTest, Simple) { 9 | Graph g([] (const std::string & x) { return x; }); 10 | g.add_node("3357753222134796045"); 11 | g.add_node("941518681376349552"); 12 | g.add_node("13971683083290263089"); 13 | g.add_node("12084232970677807325"); 14 | g.add_node("9439046497947219020"); 15 | g.add_node("10068515029147533478"); 16 | g.add_node("7671566943364943083"); 17 | g.add_node("13654097157440985242"); 18 | g.add_node("2665757309026036634"); 19 | g.add_node("3559450766265662919"); 20 | g.add_node("18378408929752357014"); 21 | g.add_node("17699076361709972611"); 22 | g.add_node("4479026691834579114"); 23 | g.add_node("3606313706927141800"); 24 | g.add_edge("3357753222134796045" , "13971683083290263089"); 25 | g.add_edge("3357753222134796045" , "12084232970677807325"); 26 | g.add_edge("3357753222134796045" , "4479026691834579114"); 27 | g.add_edge("941518681376349552" , "9439046497947219020"); 28 | g.add_edge("941518681376349552" , "3606313706927141800"); 29 | g.add_edge("13971683083290263089" , "12084232970677807325"); 30 | g.add_edge("13971683083290263089" , "9439046497947219020"); 31 | g.add_edge("13971683083290263089" , "10068515029147533478"); 32 | g.add_edge("13971683083290263089" , "2665757309026036634"); 33 | g.add_edge("12084232970677807325" , "17699076361709972611"); 34 | g.add_edge("9439046497947219020" , "3606313706927141800"); 35 | g.add_edge("10068515029147533478" , "13654097157440985242"); 36 | g.add_edge("7671566943364943083" , "13654097157440985242"); 37 | g.add_edge("13654097157440985242" , "18378408929752357014"); 38 | g.add_edge("2665757309026036634" , "3559450766265662919"); 39 | g.add_edge("3559450766265662919" , "18378408929752357014"); 40 | g.add_edge("18378408929752357014" , "17699076361709972611"); 41 | g.add_edge("17699076361709972611" , "4479026691834579114"); 42 | g.add_edge("4479026691834579114", "3357753222134796045"); 43 | g.add_edge("3606313706927141800", "941518681376349552"); 44 | 45 | // Print original 46 | std::cerr << g << std::endl; 47 | 48 | // Print condensed graph 49 | std::cerr << g.condensation(std::less()) << std::endl; 50 | } 51 | -------------------------------------------------------------------------------- /tests/echo_test.cc: -------------------------------------------------------------------------------- 1 | #include "gtest.h" 2 | #include "../compiler_pass.h" 3 | #include "../clang_utility_functions.h" 4 | #include "../util.h" 5 | 6 | TEST(EchoTests, Echo) { 7 | auto echo_pass = SinglePass<>(clang_decl_printer); 8 | const std::string input = "int a;"; 9 | const std::string output = echo_pass(input); 10 | ASSERT_EQ(compare_after_removing_space(output, input), true); 11 | } 12 | -------------------------------------------------------------------------------- /tests/expr_flattener_test.cc: -------------------------------------------------------------------------------- 1 | #include "../expr_flattener_handler.h" 2 | 3 | #include 4 | 5 | #include "gtest.h" 6 | #include "../compiler_pass.h" 7 | #include "../clang_utility_functions.h" 8 | #include "../util.h" 9 | 10 | using std::placeholders::_1; 11 | 12 | void test_expr_flattener(const std::string & input, const std::string & expected_output) { 13 | auto expr_flattener_pass = FixedPointPass, Transformer<>>(std::bind(& ExprFlattenerHandler::transform, ExprFlattenerHandler(), _1)); 14 | const auto output = expr_flattener_pass(input); 15 | std::cerr << "Actual output is " << output << std::endl; 16 | std::cerr << "Expected output is " << expected_output << std::endl; 17 | ASSERT_EQ(compare_after_removing_space(expected_output, output), true); 18 | } 19 | 20 | TEST(ExprFlattenerTests, Trivial) { 21 | test_expr_flattener("int a;", "int a;"); 22 | } 23 | 24 | TEST(ExprFlattenerTests, AlreadyFlat) { 25 | test_expr_flattener("struct Packet {int x; int y;}; void func(struct Packet p) { p.x = p.y + 1; }", "struct Packet {int x; int y;}; void func(struct Packet p) { p.x = p.y + 1; }"); 26 | } 27 | 28 | TEST(ExprFlattenerTests, OnePass) { 29 | test_expr_flattener("struct Packet {int x; int y; int z;}; void func(struct Packet p) { p.x = p.y + 1 + p.z; }", "struct Packet {int x; int y; int z; int tmp0;}; void func(struct Packet p) { p.tmp0 = p.y + 1; p.x = p.tmp0 + p.z;}"); 30 | } 31 | 32 | TEST(ExprFlattenerTests, TwoPass) { 33 | test_expr_flattener("struct Packet {int x; int y; int z;}; void func(struct Packet p) { p.x = p.y + 1 + p.z + 1; }", "struct Packet {int x; int y; int z; int tmp0; int tmp1;}; void func (struct Packet p) { p.tmp1 = p.y + 1; p.tmp0 = p.tmp1 + p.z; p.x = p.tmp0 + 1;}"); 34 | } 35 | -------------------------------------------------------------------------------- /tests/for_loop.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | }; 3 | 4 | int x; 5 | 6 | void test(struct Packet pkt) { 7 | for(x = 1; x < 10; x++) ; 8 | } 9 | -------------------------------------------------------------------------------- /tests/graph.dot: -------------------------------------------------------------------------------- 1 | digraph graph_output { 2 | splines=true 3 | node [shape = box style="rounded,filled"]; 4 | root [label = "pifo_root" fillcolor=white]; 5 | left [label = "pifo_left" fillcolor=white]; 6 | right [label = "pifo_right" fillcolor=white]; 7 | root -> left; 8 | root -> right; 9 | } 10 | -------------------------------------------------------------------------------- /tests/graph_dot_constructor.cc: -------------------------------------------------------------------------------- 1 | #include "../graph.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "gtest.h" 7 | 8 | TEST(GraphDotTest, Simple) { 9 | Graph g("./graph.dot"); 10 | 11 | // Print original 12 | std::cerr << g << std::endl; 13 | } 14 | -------------------------------------------------------------------------------- /tests/hello.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | 7 | } 8 | -------------------------------------------------------------------------------- /tests/if_converter_test.cc: -------------------------------------------------------------------------------- 1 | #include "../if_conversion_handler.h" 2 | 3 | #include 4 | 5 | #include "gtest.h" 6 | #include "../compiler_pass.h" 7 | #include "../clang_utility_functions.h" 8 | #include "../util.h" 9 | 10 | using std::placeholders::_1; 11 | 12 | void test_if_converter(const std::string & input, const std::string & expected_output) { 13 | auto if_converter_pass = SinglePass<>(std::bind(& IfConversionHandler::transform, IfConversionHandler(), _1)); 14 | const auto output = if_converter_pass(input); 15 | std::cerr << "Actual output is " << output << std::endl; 16 | std::cerr << "Expected output is " << expected_output << std::endl; 17 | ASSERT_EQ(compare_after_removing_space(expected_output, output), true); 18 | } 19 | 20 | TEST(IfConverterTests, Trivial) { 21 | test_if_converter("int a;", "int a;"); 22 | } 23 | 24 | TEST(IfConverterTests, NoIfStmt) { 25 | test_if_converter("int a; struct Packet { int x; }; void func(struct Packet p) { p.x = 2; }", 26 | "int a; struct Packet { int x; }; void func(struct Packet p) { p.x = (1 ? (2) : p.x); }"); 27 | } 28 | 29 | TEST(IfConverterTests, OneIfStmt) { 30 | test_if_converter("int a; struct Packet { int x; }; void func(struct Packet p) { if(p.x) p.x = 2; }", 31 | "int a; struct Packet { int x; int tmp0; }; void func(struct Packet p) { p.tmp0 = (1 ? (p.x) : 0); p.x = ((1 && p.tmp0) ? (2) : p.x); }"); 32 | } 33 | 34 | TEST(IfConverterTests, IfElseStmt) { 35 | test_if_converter("struct Packet {int x;}; void func(struct Packet p) { if(p.x) p.x = 2; else p.x = 3;}", 36 | "struct Packet { int x; int tmp0; }; void func(struct Packet p) { p.tmp0 = (1 ? (p.x) : 0); p.x = ((1 && p.tmp0) ? (2) : p.x); p.x = ((1 && ! p.tmp0) ? (3) : p.x); }"); 37 | } 38 | 39 | TEST(IfConverterTests, IfElseIf) { 40 | test_if_converter("struct Packet {int x;}; void func(struct Packet p) { if(p.x) p.x = 2; else if (p.x + 2) p.x = 3;}", 41 | "struct Packet {int x; int tmp0; int tmp1;}; void func(struct Packet p) { p.tmp0 = (1 ? (p.x) : 0); p.x = ((1 && p.tmp0) ? (2) : p.x); p.tmp1 = ((1 && !p.tmp0) ? (p.x + 2) : 0); p.x = (((1 && !p.tmp0) && p.tmp1) ? (3) : p.x);}"); 42 | } 43 | 44 | TEST(IfConverterTests, NestedIf) { 45 | test_if_converter("struct Packet {int x; int y; int z;}; void func(struct Packet p) { if(p.x) if(p.y) p.z = 2; }", 46 | "struct Packet {int x; int y; int z; int tmp0; int tmp1;}; void func(struct Packet p) { p.tmp0 = (1 ? (p.x) : 0); p.tmp1 = ((1 && p.tmp0) ? (p.y) : 0); p.z = (((1 && p.tmp0) && p.tmp1) ? (2) : p.z); }"); 47 | } 48 | -------------------------------------------------------------------------------- /tests/if_else.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | pkt.a = 0; 7 | if (1) { 8 | pkt.a = 0; 9 | } else { 10 | pkt.a = 1; 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/if_else_if.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | if (1) { 7 | } else if (2) { 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/if_with_2_stmts.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | if (1) { 7 | pkt.a = 5 ? pkt.a : 1; 8 | pkt.a = 0; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/if_without_brace.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int x; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | if(1) pkt.x = 1; 7 | } 8 | -------------------------------------------------------------------------------- /tests/impl_casts_int.c: -------------------------------------------------------------------------------- 1 | int y; 2 | unsigned int x; 3 | 4 | struct Packet {}; 5 | 6 | void func(struct Packet p) { 7 | y = x; 8 | } 9 | -------------------------------------------------------------------------------- /tests/local_var.c: -------------------------------------------------------------------------------- 1 | struct Packet {}; 2 | 3 | void func(struct Packet p) { 4 | int x = 1; 5 | x++; 6 | } 7 | -------------------------------------------------------------------------------- /tests/nested_ifs.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | if (1) { 7 | pkt.a = 1; 8 | if (2) { 9 | pkt.a = 2; 10 | } 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /tests/packet.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int x; 3 | int y; 4 | }; 5 | 6 | void func(struct Packet p) { 7 | p.x = 1; 8 | p.y = 2; 9 | } 10 | -------------------------------------------------------------------------------- /tests/packet_if_else.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int x; 3 | int y; 4 | int z; 5 | int w; 6 | }; 7 | 8 | void func(struct Packet p) { 9 | p.x = 1; 10 | p.y = 2; 11 | if (p.x == 3) { 12 | if (p.y == 5) { 13 | p.z = 3; 14 | } else { 15 | p.w = 2; 16 | } 17 | } else { 18 | p.x = p.y + 23; 19 | p.w = p.x; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /tests/return.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | }; 3 | 4 | int x; 5 | 6 | void test(struct Packet pkt) { 7 | return; 8 | } 9 | -------------------------------------------------------------------------------- /tests/scc_test.cc: -------------------------------------------------------------------------------- 1 | #include "../graph.h" 2 | 3 | #include 4 | #include 5 | 6 | #include "gtest.h" 7 | 8 | TEST(SccTest, Simple) { 9 | Graph g([] (const int & x) { return std::to_string(x); }); 10 | g.add_node(1); 11 | g.add_node(2); 12 | g.add_node(3); 13 | g.add_edge(1, 2); 14 | g.add_edge(2, 3); 15 | g.add_edge(3, 1); 16 | 17 | // Add another back edge 18 | g.add_edge(3, 2); 19 | 20 | // Get SCCs 21 | auto sccs = g.scc(); 22 | for (const auto & comp : sccs) { 23 | for (const auto & node : comp) { 24 | std::cerr << node << ","; 25 | } 26 | std::cerr << std::endl; 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /tests/single_if.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | int a; 3 | }; 4 | 5 | void foo(struct Packet pkt) { 6 | if (1) { 7 | pkt.a = 0; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/stateful.c: -------------------------------------------------------------------------------- 1 | int x = 0; 2 | 3 | struct Packet { 4 | int a; 5 | int b; 6 | int c; 7 | }; 8 | 9 | void foo(struct Packet p) { 10 | if (x) { 11 | x = x + 1; 12 | } 13 | 14 | p.a = x; 15 | p.b = x + 1; 16 | } 17 | -------------------------------------------------------------------------------- /tests/switch.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | }; 3 | 4 | int x; 5 | 6 | void test(struct Packet pkt) { 7 | switch(x) { 8 | default: ; 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/while_loop.c: -------------------------------------------------------------------------------- 1 | struct Packet { 2 | }; 3 | 4 | int x; 5 | 6 | void test(struct Packet pkt) { 7 | while(x); 8 | } 9 | -------------------------------------------------------------------------------- /third_party/Makefile.am: -------------------------------------------------------------------------------- 1 | AM_CXXFLAGS = $(PICKY_CXXFLAGS) 2 | 3 | noinst_LIBRARIES = libmahimahi.a libgtest.a 4 | libmahimahi_a_SOURCES = exception.hh file_descriptor.hh file_descriptor.cc temp_file.hh temp_file.cc assert_exception.hh 5 | 6 | # Remove our picky C flags to compile gtest 7 | libgtest_a_SOURCES = gtest-all.cpp gtest.h gtest-main.cc 8 | libgtest_a_CXXFLAGS = 9 | -------------------------------------------------------------------------------- /third_party/assert_exception.h: -------------------------------------------------------------------------------- 1 | #ifndef ASSERT_EXCEPTION_H_ 2 | #define ASSERT_EXCEPTION_H_ 3 | 4 | #include 5 | #include 6 | 7 | class AssertException : public std::exception { 8 | public: 9 | AssertException(const std::string & t_error_message) 10 | : std::exception(), 11 | assert_message_(t_error_message) {} 12 | 13 | const char * what(void) const noexcept override { return assert_message_.c_str(); } 14 | private: 15 | std::string assert_message_; 16 | }; 17 | 18 | # define assert_exception(expr) if (not (expr)) { throw AssertException(\ 19 | " assert failed with expr " + std::string(__STRING(expr)) + "\n" + \ 20 | " in function " + std::string(__PRETTY_FUNCTION__) + "\n" + \ 21 | " in file " + std::string(__FILE__) + "\n" + \ 22 | " at line number " + std::to_string(__LINE__)); } 23 | 24 | 25 | 26 | #endif // ASSERT_EXCEPTION_H_ 27 | -------------------------------------------------------------------------------- /third_party/exception.hh: -------------------------------------------------------------------------------- 1 | /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef EXCEPTION_HH 4 | #define EXCEPTION_HH 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | class tagged_error : public std::system_error 14 | { 15 | private: 16 | std::string attempt_and_error_; 17 | 18 | public: 19 | tagged_error( const std::error_category & category, 20 | const std::string & s_attempt, 21 | const int error_code ) 22 | : system_error( error_code, category ), 23 | attempt_and_error_( s_attempt + ": " + std::system_error::what() ) 24 | {} 25 | 26 | const char * what( void ) const noexcept override 27 | { 28 | return attempt_and_error_.c_str(); 29 | } 30 | }; 31 | 32 | class unix_error : public tagged_error 33 | { 34 | public: 35 | unix_error( const std::string & s_attempt, 36 | const int s_errno = errno ) 37 | : tagged_error( std::system_category(), s_attempt, s_errno ) 38 | {} 39 | }; 40 | 41 | inline void print_exception( const std::exception & e, std::ostream & output = std::cerr ) 42 | { 43 | output << "Died on " << abi::__cxa_demangle( typeid( e ).name(), nullptr, nullptr, nullptr ) << ": " << e.what() << std::endl; 44 | } 45 | 46 | /* error-checking wrapper for most syscalls */ 47 | template 48 | inline ReturnType SystemCall( const std::string & s_attempt, const ReturnType return_value ) 49 | { 50 | static_assert(std::is_integral::value, "Integer type required."); 51 | if ( return_value >= 0 ) { 52 | return return_value; 53 | } 54 | 55 | throw unix_error( s_attempt ); 56 | } 57 | 58 | #endif 59 | -------------------------------------------------------------------------------- /third_party/file_descriptor.cc: -------------------------------------------------------------------------------- 1 | /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #include "file_descriptor.hh" 4 | #include "exception.hh" 5 | 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | const size_t FileDescriptor::BUFFER_SIZE = 1024 * 1024; 12 | 13 | /* construct from fd number */ 14 | FileDescriptor::FileDescriptor( const int fd ) 15 | : fd_( fd ), 16 | eof_( false ), 17 | read_count_( 0 ), 18 | write_count_( 0 ) 19 | { 20 | if ( fd_ <= 2 ) { /* make sure not overwriting stdout/stderr */ 21 | throw unix_error( "FileDescriptor: fd <= 2" ); 22 | } 23 | 24 | /* set close-on-exec flag so our file descriptors 25 | aren't passed on to unrelated children (like a shell) */ 26 | SystemCall( "fcntl FD_CLOEXEC", fcntl( fd_, F_SETFD, FD_CLOEXEC ) ); 27 | } 28 | 29 | /* move constructor */ 30 | FileDescriptor::FileDescriptor( FileDescriptor && other ) 31 | : fd_( other.fd_ ), 32 | eof_( other.eof_ ), 33 | read_count_( other.read_count_ ), 34 | write_count_( other.write_count_ ) 35 | { 36 | /* mark other file descriptor as inactive */ 37 | other.fd_ = -1; 38 | } 39 | 40 | /* destructor */ 41 | FileDescriptor::~FileDescriptor() 42 | { 43 | if ( fd_ < 0 ) { /* has already been moved away */ 44 | return; 45 | } 46 | 47 | try { 48 | SystemCall( "close", close( fd_ ) ); 49 | } catch ( const exception & e ) { /* don't throw from destructor */ 50 | print_exception( e ); 51 | } 52 | } 53 | 54 | /* attempt to write a portion of a string */ 55 | string::const_iterator FileDescriptor::write( const string::const_iterator & begin, 56 | const string::const_iterator & end ) 57 | { 58 | if ( begin >= end ) { 59 | throw runtime_error( "nothing to write" ); 60 | } 61 | 62 | ssize_t bytes_written = SystemCall( "write", ::write( fd_, &*begin, static_cast(end - begin) ) ); 63 | if ( bytes_written == 0 ) { 64 | throw runtime_error( "write returned 0" ); 65 | } 66 | 67 | register_write(); 68 | 69 | return begin + bytes_written; 70 | } 71 | 72 | /* read method */ 73 | string FileDescriptor::read( const size_t limit ) 74 | { 75 | char buffer[ BUFFER_SIZE ]; 76 | 77 | ssize_t bytes_read = SystemCall( "read", ::read( fd_, buffer, min( BUFFER_SIZE, limit ) ) ); 78 | if ( bytes_read == 0 ) { 79 | set_eof(); 80 | } 81 | 82 | register_read(); 83 | 84 | return string( buffer, static_cast::size_type>(bytes_read) ); 85 | } 86 | 87 | /* write method */ 88 | string::const_iterator FileDescriptor::write( const std::string & buffer, const bool write_all ) 89 | { 90 | auto it = buffer.begin(); 91 | 92 | do { 93 | it = write( it, buffer.end() ); 94 | } while ( write_all and (it != buffer.end()) ); 95 | 96 | return it; 97 | } 98 | -------------------------------------------------------------------------------- /third_party/file_descriptor.hh: -------------------------------------------------------------------------------- 1 | /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef FILE_DESCRIPTOR_HH 4 | #define FILE_DESCRIPTOR_HH 5 | 6 | #include 7 | 8 | /* Unix file descriptors (sockets, files, etc.) */ 9 | class FileDescriptor 10 | { 11 | private: 12 | int fd_; 13 | bool eof_; 14 | 15 | unsigned int read_count_, write_count_; 16 | 17 | /* maximum size of a read */ 18 | const static size_t BUFFER_SIZE; 19 | 20 | protected: 21 | void register_read( void ) { read_count_++; } 22 | void register_write( void ) { write_count_++; } 23 | void set_eof( void ) { eof_ = true; } 24 | 25 | public: 26 | /* construct from fd number */ 27 | FileDescriptor( const int fd ); 28 | 29 | /* move constructor */ 30 | FileDescriptor( FileDescriptor && other ); 31 | 32 | /* destructor */ 33 | virtual ~FileDescriptor(); 34 | 35 | /* accessors */ 36 | const int & fd_num( void ) const { return fd_; } 37 | const bool & eof( void ) const { return eof_; } 38 | unsigned int read_count( void ) const { return read_count_; } 39 | unsigned int write_count( void ) const { return write_count_; } 40 | 41 | /* read and write methods */ 42 | std::string read( const size_t limit = BUFFER_SIZE ); 43 | std::string::const_iterator write( const std::string & buffer, const bool write_all = true ); 44 | std::string::const_iterator write( const std::string::const_iterator & begin, 45 | const std::string::const_iterator & end ); 46 | 47 | /* forbid copying FileDescriptor objects or assigning them */ 48 | FileDescriptor( const FileDescriptor & other ) = delete; 49 | const FileDescriptor & operator=( const FileDescriptor & other ) = delete; 50 | }; 51 | 52 | #endif /* FILE_DESCRIPTOR_HH */ 53 | -------------------------------------------------------------------------------- /third_party/gtest-main.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | 7 | int main(int argc, char* argv[]) { 8 | ::testing::InitGoogleTest(&argc, argv); 9 | return RUN_ALL_TESTS(); 10 | } 11 | -------------------------------------------------------------------------------- /third_party/temp_file.cc: -------------------------------------------------------------------------------- 1 | /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "temp_file.hh" 8 | #include "exception.hh" 9 | #include "assert_exception.h" 10 | 11 | using namespace std; 12 | 13 | vector to_mutable( const string & str ) 14 | { 15 | vector< char > ret; 16 | for ( const auto & ch : str ) { 17 | ret.push_back( ch ); 18 | } 19 | ret.push_back( 0 ); /* null terminate */ 20 | 21 | return ret; 22 | } 23 | 24 | UniqueFile::UniqueFile( const string & filename_template, const string & suffix ) 25 | : mutable_temp_filename_( to_mutable( filename_template + "XXXXXX" + suffix ) ), 26 | fd_( SystemCall( "mkstemps", mkstemps( &mutable_temp_filename_[ 0 ], static_cast(suffix.size()) ) ) ), 27 | moved_away_( false ) 28 | { 29 | } 30 | 31 | /* unlike UniqueFile, a TempFile is deleted when object destroyed */ 32 | TempFile::~TempFile() 33 | { 34 | if ( moved_away_ ) { return; } 35 | 36 | try { 37 | SystemCall( "unlink " + name(), unlink( name().c_str() ) ); 38 | } catch ( const exception & e ) { 39 | print_exception( e ); 40 | } 41 | } 42 | 43 | void UniqueFile::write( const string & contents ) 44 | { 45 | assert_exception( not moved_away_ ); 46 | 47 | fd_.write( contents ); 48 | } 49 | 50 | UniqueFile::UniqueFile( UniqueFile && other ) 51 | : mutable_temp_filename_( other.mutable_temp_filename_ ), 52 | fd_( move( other.fd_ ) ), 53 | moved_away_( false ) 54 | { 55 | other.moved_away_ = true; 56 | } 57 | 58 | string UniqueFile::name( void ) const 59 | { 60 | assert_exception( mutable_temp_filename_.size() > 1 ); 61 | return string( mutable_temp_filename_.begin(), mutable_temp_filename_.end() - 1 ); 62 | } 63 | -------------------------------------------------------------------------------- /third_party/temp_file.hh: -------------------------------------------------------------------------------- 1 | /* -*-mode:c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ 2 | 3 | #ifndef TEMP_FILE_HH 4 | #define TEMP_FILE_HH 5 | 6 | #include 7 | #include 8 | 9 | #include "file_descriptor.hh" 10 | 11 | class UniqueFile 12 | { 13 | private: 14 | std::vector mutable_temp_filename_; 15 | FileDescriptor fd_; 16 | 17 | protected: 18 | bool moved_away_; 19 | 20 | public: 21 | UniqueFile( const std::string & filename_template, const std::string & suffix ); 22 | virtual ~UniqueFile() {} 23 | 24 | std::string name( void ) const; 25 | 26 | void write( const std::string & contents ); 27 | 28 | FileDescriptor & fd( void ) { return fd_; } 29 | 30 | /* ban copying */ 31 | UniqueFile( const UniqueFile & other ) = delete; 32 | UniqueFile & operator=( const UniqueFile & other ) = delete; 33 | 34 | /* allow move constructor */ 35 | UniqueFile( UniqueFile && other ); 36 | 37 | /* ... but not move assignment operator */ 38 | UniqueFile & operator=( UniqueFile && other ) = delete; 39 | }; 40 | 41 | /* TempFile is deleted when object destroyed */ 42 | class TempFile : public UniqueFile 43 | { 44 | public: 45 | using UniqueFile::UniqueFile; 46 | 47 | /* allow move constructor */ 48 | TempFile( TempFile && other ) : UniqueFile( std::move( other ) ) {} 49 | 50 | ~TempFile(); 51 | }; 52 | 53 | #endif 54 | -------------------------------------------------------------------------------- /unique_identifiers.cc: -------------------------------------------------------------------------------- 1 | #include "unique_identifiers.h" 2 | 3 | #include "third_party/assert_exception.h" 4 | 5 | std::string UniqueIdentifiers::get_unique_identifier(const std::string & prefix) const { 6 | // Propose prefix_x, 7 | // where x starts at id_suffix_.at(prefix) + 1 8 | // and keeps incrementing until at least some prefix_x 9 | // is unique and does not belong to id_set_ 10 | if (id_suffix_.find(prefix) == id_suffix_.end()) { 11 | id_suffix_[prefix] = -1; 12 | } 13 | id_suffix_.at(prefix)++; 14 | std::string candidate = prefix + std::to_string(id_suffix_.at(prefix)); 15 | while (id_set_.find(candidate) != id_set_.end()) { 16 | id_suffix_.at(prefix)++; 17 | candidate = prefix + std::to_string(id_suffix_.at(prefix)); 18 | } 19 | assert_exception(id_set_.find(candidate) == id_set_.end()); 20 | id_set_.emplace(candidate); 21 | return candidate; 22 | } 23 | -------------------------------------------------------------------------------- /unique_identifiers.h: -------------------------------------------------------------------------------- 1 | #ifndef UNIQUE_IDENTIFIERS_H_ 2 | #define UNIQUE_IDENTIFIERS_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | class UniqueIdentifiers { 9 | public: 10 | /// Constructor 11 | UniqueIdentifiers(const std::set & initial_set) : id_set_(initial_set) {} 12 | 13 | /// Get unique identifier 14 | std::string get_unique_identifier(const std::string & prefix = "tmp") const; 15 | 16 | private: 17 | /// Set of identifier names 18 | /// passed to the function in constructor 19 | mutable std::set id_set_; 20 | 21 | /// Last unique id suffix handed out for every prefix 22 | mutable std::map id_suffix_ = {}; 23 | }; 24 | 25 | #endif // UNIQUE_IDENTIFIERS_H_ 26 | -------------------------------------------------------------------------------- /util.cc: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | std::string get_file_name(const int argc, const char ** argv) { 12 | std::string ret; 13 | if (argc < 2) { 14 | std::cerr << "Usage: " << argv[0] << " file_name " << std::endl; 15 | exit(1); 16 | ret = ""; 17 | } else { 18 | ret = std::string(argv[1]); 19 | } 20 | return ret; 21 | } 22 | 23 | std::string file_to_str(const std::string & file_name) { 24 | // Taken from: 25 | // http://stackoverflow.com/questions/2912520/read-file-contents-into-a-string-in-c 26 | std::ifstream ifs(file_name); 27 | if (not ifs.good()) throw std::logic_error("Cannot read from " + file_name + ", maybe it doesn't exist?"); 28 | return std::string((std::istreambuf_iterator(ifs)), 29 | (std::istreambuf_iterator())); 30 | } 31 | 32 | std::vector split(const std::string & input, const std::string & regex_str) { 33 | std::regex regex_object(regex_str); 34 | std::sregex_token_iterator first{input.begin(), input.end(), regex_object, -1}, last; 35 | return {first, last}; 36 | } 37 | 38 | std::string hash_string (const std::string & str) { 39 | return std::to_string(std::hash()(str)); 40 | } 41 | 42 | bool compare_after_removing_space(const std::string & s1, const std::string & s2) { 43 | auto s1_tmp = s1; 44 | auto s2_tmp = s2; 45 | s1_tmp.erase(std::remove_if(s1_tmp.begin(), s1_tmp.end(), isspace), s1_tmp.end()); 46 | s2_tmp.erase(std::remove_if(s2_tmp.begin(), s2_tmp.end(), isspace), s2_tmp.end()); 47 | return s1_tmp == s2_tmp; 48 | } 49 | -------------------------------------------------------------------------------- /util.h: -------------------------------------------------------------------------------- 1 | #ifndef UTIL_H_ 2 | #define UTIL_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | /// Get file name from command line, exit if not available 11 | std::string get_file_name(const int argc, const char ** argv); 12 | 13 | /// Read a file's contents into a string 14 | std::string file_to_str(const std::string & file_name); 15 | 16 | /// Split string based on another string used as delimiter 17 | /// using C++11's regex_token_iterator 18 | /// Based on http://en.cppreference.com/w/cpp/regex/regex_token_iterator 19 | /// and http://stackoverflow.com/a/9437426/1152801 20 | std::vector split(const std::string & input, const std::string & regex_str); 21 | 22 | /// Hash string into unique ID and turn that into a string 23 | std::string hash_string (const std::string & str); 24 | 25 | /// Compare two strings after removing all forms of whitespace 26 | bool compare_after_removing_space(const std::string & s1, const std::string & s2); 27 | 28 | #endif // UTIL_H_ 29 | -------------------------------------------------------------------------------- /validator.h: -------------------------------------------------------------------------------- 1 | #ifndef VALIDATOR_H_ 2 | #define VALIDATOR_H_ 3 | 4 | #include "ast_visitor.h" 5 | 6 | class Validator : public AstVisitor {}; 7 | 8 | #endif // VALIDATOR_H_ 9 | --------------------------------------------------------------------------------