Initial commit
authorBrenden Blanco <bblanco@plumgrid.com>
Sun, 26 Apr 2015 16:41:58 +0000 (09:41 -0700)
committerBrenden Blanco <bblanco@plumgrid.com>
Sat, 2 May 2015 03:09:36 +0000 (20:09 -0700)
Signed-off-by: Brenden Blanco <bblanco@plumgrid.com>
55 files changed:
.gitignore [new file with mode: 0644]
CMakeLists.txt
jit/CMakeLists.txt [deleted file]
jit/src/CMakeLists.txt [deleted file]
jit/src/cc/codegen_c.cc [deleted file]
jit/src/cc/codegen_c.h [deleted file]
src/CMakeLists.txt [new file with mode: 0644]
src/bpf.py [moved from jit/src/bpf.py with 70% similarity]
src/cc/CMakeLists.txt [moved from jit/src/cc/CMakeLists.txt with 90% similarity]
src/cc/bitops.c [moved from jit/src/cc/bitops.c with 100% similarity]
src/cc/bpf_common.cc [moved from jit/src/cc/bpf_common.cc with 100% similarity]
src/cc/bpf_common.h [moved from jit/src/cc/bpf_common.h with 100% similarity]
src/cc/bpf_helpers.h [moved from jit/src/cc/bpf_helpers.h with 85% similarity]
src/cc/bpf_program.cc [moved from jit/src/cc/bpf_program.cc with 94% similarity]
src/cc/bpf_program.h [moved from jit/src/cc/bpf_program.h with 100% similarity]
src/cc/codegen_llvm.cc [moved from jit/src/cc/codegen_llvm.cc with 63% similarity]
src/cc/codegen_llvm.h [moved from jit/src/cc/codegen_llvm.h with 90% similarity]
src/cc/exception.h [moved from jit/src/cc/exception.h with 100% similarity]
src/cc/lexer.h [moved from jit/src/cc/lexer.h with 98% similarity]
src/cc/lexer.ll [moved from jit/src/cc/lexer.ll with 87% similarity]
src/cc/libbpf.c [moved from jit/src/cc/libbpf.c with 63% similarity]
src/cc/node.cc [moved from jit/src/cc/node.cc with 100% similarity]
src/cc/node.h [moved from jit/src/cc/node.h with 92% similarity]
src/cc/parser.cc [moved from jit/src/cc/parser.cc with 71% similarity]
src/cc/parser.h [moved from jit/src/cc/parser.h with 57% similarity]
src/cc/parser.yy [moved from jit/src/cc/parser.yy with 85% similarity]
src/cc/printer.cc [moved from jit/src/cc/printer.cc with 92% similarity]
src/cc/printer.h [moved from jit/src/cc/printer.h with 100% similarity]
src/cc/scope.h [moved from jit/src/cc/scope.h with 64% similarity]
src/cc/type_check.cc [moved from jit/src/cc/type_check.cc with 67% similarity]
src/cc/type_check.h [moved from jit/src/cc/type_check.h with 100% similarity]
src/cc/type_helper.h [moved from jit/src/cc/type_helper.h with 100% similarity]
src/compat/include/linux/bpf.h [moved from jit/compat/include/linux/bpf.h with 94% similarity]
src/compat/include/linux/bpf_common.h [moved from jit/compat/include/linux/bpf_common.h with 90% similarity]
src/libbpf.h [moved from jit/src/libbpf.h with 97% similarity]
tests/CMakeLists.txt [new file with mode: 0644]
tests/jit/CMakeLists.txt [new file with mode: 0644]
tests/jit/kprobe.b [new file with mode: 0644]
tests/jit/loader.py
tests/jit/proto.b [new file with mode: 0644]
tests/jit/proto.dph [deleted file]
tests/jit/proto_simple.dph [deleted file]
tests/jit/test1.b [new file with mode: 0644]
tests/jit/test1.dp [deleted file]
tests/jit/test1.py [new file with mode: 0755]
tests/jit/test2.b [new file with mode: 0644]
tests/jit/test2.dp [deleted file]
tests/jit/test2.py [changed mode: 0644->0755]
tests/jit/test3.dp [deleted file]
tests/jit/test3.py [deleted file]
tests/jit/trace1.b [new file with mode: 0644]
tests/jit/trace1.py [new file with mode: 0755]
tests/jit/trace2.b [new file with mode: 0644]
tests/jit/trace2.py [new file with mode: 0755]
tests/wrapper.sh.in [new file with mode: 0755]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3819313
--- /dev/null
@@ -0,0 +1,2 @@
+*.swp
+*.swo
index 962b0fc..2bb6369 100644 (file)
@@ -1,10 +1,14 @@
 cmake_minimum_required(VERSION 2.8.7)
 
 project(bpf-tools)
+set(CMAKE_BUILD_TYPE Debug)
+
+enable_testing()
 
 find_package(BISON)
 find_package(FLEX)
 find_package(LLVM REQUIRED CONFIG)
+message(STATUS "Found LLVM: ${LLVM_INCLUDE_DIRS}")
 find_program(XXD xxd)
 if (${XXD} STREQUAL "XXD-NOTFOUND")
   message(FATAL_ERROR "program xxd not found, install vim-common")
@@ -13,8 +17,15 @@ find_program(CLANG clang)
 if (${CLANG} STREQUAL "CLANG-NOTFOUND")
   message(FATAL_ERROR "program clang not found, install clang with bpf support")
 endif()
+execute_process(COMMAND ${CLANG} --version OUTPUT_VARIABLE CLANG_VERSION_RAW)
+string(REGEX MATCH "[0-9]+[.][0-9]+[.][0-9]+" CLANG_VERSION ${CLANG_VERSION_RAW})
+message(STATUS "Found CLANG: ${CLANG} (found version \"${CLANG_VERSION}\")")
+if (CLANG_VERSION VERSION_LESS 3.6.0)
+  message(FATAL_ERROR "requires clang version >= 3.6.0, ${CLANG_VERSION} found")
+endif()
 
 set(CMAKE_C_FLAGS "-Wall")
 set(CMAKE_CXX_FLAGS "-std=c++11 -Wall")
 
-add_subdirectory(jit)
+add_subdirectory(src)
+add_subdirectory(tests)
diff --git a/jit/CMakeLists.txt b/jit/CMakeLists.txt
deleted file mode 100644 (file)
index 6831ea7..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-
-set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -flto")
-set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -flto -fno-rtti")
-
-include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat/include)
-
-add_subdirectory(src)
diff --git a/jit/src/CMakeLists.txt b/jit/src/CMakeLists.txt
deleted file mode 100644 (file)
index 1c18ad4..0000000
+++ /dev/null
@@ -1,4 +0,0 @@
-include_directories(${CMAKE_CURRENT_SOURCE_DIR})
-include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-add_subdirectory(cc)
diff --git a/jit/src/cc/codegen_c.cc b/jit/src/cc/codegen_c.cc
deleted file mode 100644 (file)
index eb55841..0000000
+++ /dev/null
@@ -1,1191 +0,0 @@
-/*
- * =====================================================================
- * Copyright (c) 2012, PLUMgrid, http://plumgrid.com
- *
- * This source is subject to the PLUMgrid License.
- * All rights reserved.
- *
- * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
- * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * PLUMgrid confidential information, delete if you are not the
- * intended recipient.
- *
- * =====================================================================
- */
-
-#include <set>
-#include <algorithm>
-#include <sstream>
-#include <assert.h>
-#include "exception.h"
-#include "cc/codegen_c.h"
-#include "cc/lexer.h"
-#include "cc/type_helper.h"
-
-namespace ebpf {
-namespace cc {
-
-using std::set;
-using std::for_each;
-using std::pair;
-using std::stringstream;
-
-template <typename... Args>
-void CodegenC::emitln(const char *fmt, Args&&... params) {
-  fprintf(out_, fmt, std::forward<Args>(params)...);
-  fprintf(out_, "\n%*s", indent_ * 2, "");
-}
-void CodegenC::emitln(const char *s) {
-  fprintf(out_, "%s", s);
-  fprintf(out_, "\n%*s", indent_ * 2, "");
-}
-
-template <typename... Args>
-void CodegenC::emit(const char *fmt, Args&&... params) {
-  fprintf(out_, fmt, std::forward<Args>(params)...);
-}
-void CodegenC::emit(const char *s) {
-  fprintf(out_, "%s", s);
-}
-
-template <typename... Args>
-void CodegenC::lnemit(const char *fmt, Args&&... params) {
-  fprintf(out_, "\n%*s", indent_ * 2, "");
-  fprintf(out_, fmt, std::forward<Args>(params)...);
-}
-void CodegenC::lnemit(const char *s) {
-  fprintf(out_, "\n%*s", indent_ * 2, "");
-  fprintf(out_, "%s", s);
-}
-
-void CodegenC::indent() {
-  fprintf(out_, "%*s", indent_ * 2, "");
-}
-
-void CodegenC::emit_comment(Node* n) {
-  // if (!n->text_.empty()) {
-  //   emitln("/* %s */", n->text_.c_str());
-  // }
-}
-
-void CodegenC::visit_block_stmt_node(BlockStmtNode* n) {
-
-  ++indent_;
-  emit("{");
-
-  // enter scope
-  auto scope = scopes_->current_var();
-  if (n->scope_) {
-    scopes_->set_current(n->scope_);
-  }
-
-
-  if (!n->stmts_.empty()) {
-    for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
-      emitln("");
-      (*it)->accept(this);
-    }
-  }
-  // exit scope
-  scopes_->set_current(scope);
-
-  --indent_;
-  emitln("");
-  emit("}");
-}
-
-void CodegenC::visit_version_stmt_node(VersionStmtNode* n) {
-  uint32_t version;
-  version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
-  emit("static const uint32_t  plumlet_version   __attribute__"
-      "((section (\".version\"), used)) = 0x%x;\n", version);
-}
-
-void CodegenC::visit_if_stmt_node(IfStmtNode* n) {
-  emit_comment(n);
-  emit("if (");
-  n->cond_->accept(this);
-  emit(") ");
-  n->true_block_->accept(this);
-  if (n->false_block_) {
-    emit(" else ");
-    n->false_block_->accept(this);
-  }
-}
-
-void CodegenC::visit_onvalid_stmt_node(OnValidStmtNode* n) {
-  auto sdecl = static_cast<StructVariableDeclStmtNode*>(n->cond_->decl_);
-  emit_comment(n);
-  // cheat a little not using n->cond_->accept(this) to prevent the dereference
-  emit("if (%s%s) ", sdecl->scope_id(), sdecl->id_->c_str());
-  n->block_->accept(this);
-  if (n->else_block_) {
-    emit(" else ");
-    n->else_block_->accept(this);
-  }
-}
-
-void CodegenC::visit_switch_stmt_node(SwitchStmtNode* n) {
-  emit_comment(n);
-  emit("switch (");
-  n->cond_->accept(this);
-  emit(") ");
-  n->block_->accept(this);
-}
-
-void CodegenC::visit_case_stmt_node(CaseStmtNode* n) {
-  if (n->value_) {
-    emit("case ");
-    n->value_->accept(this);
-  } else {
-    emit("default");
-  }
-  emit(": ");
-  ++indent_;
-  n->block_->accept(this);
-  emitln("");
-  emit("break;");
-  --indent_;
-}
-
-void CodegenC::visit_ident_expr_node(IdentExprNode* n) {
-  if (!n->decl_)
-    throw CompilerException("variable lookup failed: %s", n->name_.c_str());
-  if (n->decl_->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) {
-    if (n->sub_name_.size()) {
-      if (n->bitop_) {
-        // ident is holding a host endian number, don't use dext
-        if (n->flags_[ExprNode::IS_LHS]) {
-          emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
-        } else {
-          emit("(((%s%s->%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str(),
-              n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_);
-        }
-      } else {
-        if (n->struct_type_->id_->name_ == "_Packet" && n->sub_name_.substr(0, 3) == "arg") {
-          // convert arg1~arg8 into args[0]~args[7] assuming type_check verified the range already
-          auto arg_num = stoi(n->sub_name_.substr(3, 3));
-          if (arg_num < 5) {
-            emit("%s%s->args_lo[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 1);
-          } else {
-            emit("%s%s->args_hi[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 5);
-          }
-        } else {
-          emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
-        }
-      }
-    } else {
-      emit("*%s%s", n->decl_->scope_id(), n->c_str());
-    }
-  } else {
-    if (n->sub_name_.size()) {
-      emit("%s%s.%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str());
-    } else {
-      if (n->bitop_) {
-        // ident is holding a host endian number, don't use dext
-        if (n->flags_[ExprNode::IS_LHS]) {
-          assert(0);
-        } else {
-          emit("(((%s%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(),
-               n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_);
-        }
-      } else {
-        emit("%s%s", n->decl_->scope_id(), n->c_str());
-      }
-    }
-  }
-}
-
-void CodegenC::visit_assign_expr_node(AssignExprNode* n) {
-  if (n->bitop_) {
-    n->id_->accept(this);
-    emit(" = (");
-    n->id_->accept(this);
-    emit(" & ~((((%s)1 << %d) - 1) << %d)) | (", bits_to_uint(n->id_->bit_width_),
-         n->bitop_->bit_width_, n->bitop_->bit_offset_);
-    n->rhs_->accept(this);
-    emit(" << %d)", n->bitop_->bit_offset_);
-  } else {
-    if (n->id_->flags_[ExprNode::PROTO]) {
-      auto f = n->id_->struct_type_->field(n->id_->sub_name_);
-      emit("bpf_dins(%s%s + %zu, %zu, %zu, ", n->id_->decl_->scope_id(), n->id_->c_str(),
-           f->bit_offset_ >> 3, f->bit_offset_ & 0x7, f->bit_width_);
-      n->rhs_->accept(this);
-      emit(")");
-    } else {
-      n->id_->accept(this);
-      emit(" = ");
-      n->rhs_->accept(this);
-    }
-  }
-}
-
-void CodegenC::visit_packet_expr_node(PacketExprNode* n) {
-  auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
-  if (p) {
-    auto f = p->field(n->id_->sub_name_);
-    if (f) {
-      size_t bit_offset = f->bit_offset_;
-      size_t bit_width = f->bit_width_;
-      if (n->bitop_) {
-        bit_offset += f->bit_width_ - (n->bitop_->bit_offset_ + n->bitop_->bit_width_);
-        bit_width = std::min(bit_width - n->bitop_->bit_offset_, n->bitop_->bit_width_);
-      }
-      if (n->flags_[ExprNode::IS_LHS])
-        emit("bpf_dins_pkt(pkt, %s + %zu, %zu, %zu, ", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
-      else
-        emit("bpf_dext_pkt(pkt, %s + %zu, %zu, %zu)", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
-    } else {
-      emit("pkt->start + pkt->offset + %s", n->id_->c_str());
-    }
-  }
-}
-
-void CodegenC::visit_integer_expr_node(IntegerExprNode* n) {
-  emit("%s", n->val_.c_str());
-}
-
-void CodegenC::visit_binop_expr_node(BinopExprNode* n) {
-  n->lhs_->accept(this);
-  switch (n->op_) {
-    case Tok::TCEQ: emit(" == "); break;
-    case Tok::TCNE: emit(" != "); break;
-    case Tok::TXOR: emit(" ^ "); break;
-    case Tok::TAND: emit(" && "); break;
-    case Tok::TOR: emit(" || "); break;
-    case Tok::TMOD: emit("%");  break;
-    case Tok::TCLT: emit(" < "); break;
-    case Tok::TCLE: emit(" <= "); break;
-    case Tok::TCGT: emit(" > "); break;
-    case Tok::TCGE: emit(" >= "); break;
-    case Tok::TPLUS: emit(" + "); break;
-    case Tok::TMINUS: emit(" - "); break;
-    case Tok::TLAND: emit(" & "); break;
-    case Tok::TLOR: emit(" | "); break;
-    default: emit(" ?%d? ", n->op_); break;
-  }
-  n->rhs_->accept(this);
-}
-
-void CodegenC::visit_unop_expr_node(UnopExprNode* n) {
-  const char* s = "";
-  switch (n->op_) {
-    case Tok::TNOT: s = "!"; break;
-    case Tok::TCMPL: s = "~"; break;
-    default: {}
-  }
-  emit("%s", s);
-  n->expr_->accept(this);
-}
-
-void CodegenC::visit_bitop_expr_node(BitopExprNode* n) {
-}
-
-void CodegenC::visit_goto_expr_node(GotoExprNode* n) {
-  if (n->id_->name_ == "DONE") {
-    for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii)
-      for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj)
-        emitln("%s;", jj->c_str());
-    emit("goto DONE");
-    return;
-  }
-  string jump_label;
-  // when dealing with multistates, goto statements may be overridden
-  auto rewrite_it = proto_rewrites_.find(n->id_->full_name());
-  auto default_it = proto_rewrites_.find("");
-  if (rewrite_it != proto_rewrites_.end()) {
-    jump_label = rewrite_it->second;
-  } else if (default_it != proto_rewrites_.end()) {
-    jump_label = default_it->second;
-  } else {
-    auto state = scopes_->current_state()->lookup(n->id_->full_name(), false);
-    if (state) {
-      jump_label = state->scoped_name();
-      if (n->is_continue_) {
-        jump_label += "_continue";
-      }
-    } else {
-      state = scopes_->current_state()->lookup("EOP", false);
-      if (state) {
-        jump_label = state->scoped_name();
-      }
-    }
-  }
-  for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii)
-    for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj)
-      emitln("%s;", jj->c_str());
-  emit("goto %s", jump_label.c_str());
-}
-
-void CodegenC::emit_table_lookup(MethodCallExprNode* n) {
-  TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
-  IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  stringstream free_inst;
-  IdentExprNode* arg1;
-  StructVariableDeclStmtNode* arg1_type;
-
-  emitln("{ if (unlikely(pkt->capture)) {");
-  emitln("    bpf_capture(pkt, BPF_CAP_TABLE_LOOKUP, TABLE_ID_%s, 0);", n->id_->c_str());
-  emitln("} }");
-  emit("%s* %s_key = &", table->key_id()->c_str(), n->id_->c_str());
-  arg0->accept(this);
-  emitln(";");
-  emitln("%s *%s_element = (%s*)",
-         table->leaf_id()->c_str(), n->id_->c_str(), table->leaf_id()->c_str());
-  if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED" ||
-      table->type_id()->name_ == "LPM") {
-    emit("  bpf_table_lookup(pkt, TABLE_ID_%s, %s_key)", n->id_->c_str(), n->id_->c_str());
-    if (n->args_.size() == 2) {
-      arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
-      arg1_type = static_cast<StructVariableDeclStmtNode*>(arg1->decl_);
-      if (table->leaf_id()->name_ != arg1_type->struct_id_->name_) {
-        throw CompilerException("lookup pointer type mismatch %s != %s", table->leaf_id()->c_str(),
-                                 arg1_type->struct_id_->c_str());
-      }
-      emitln(";");
-      // cheat a little not using arg1->accept(this) to prevent the dereference
-      emit("%s%s = %s_element", arg1_type->scope_id(), arg1_type->id_->c_str(), n->id_->c_str());
-    }
-  } else {
-    throw CompilerException("lookup in table type %s unsupported", table->type_id()->c_str());
-  }
-  free_instructions_.back().push_back(free_inst.str());
-}
-
-void CodegenC::emit_table_update(MethodCallExprNode* n) {
-  TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
-  IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  IdentExprNode* arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
-  IdentExprNode* type0 = table->templates_.at(0).get();
-
-  emit("%s* %s_ukey = &", type0->c_str(), n->id_->c_str());
-  arg0->accept(this);
-  emitln(";");
-  if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
-    emit("bpf_table_update(pkt, TABLE_ID_%s, %s_ukey", n->id_->c_str(), n->id_->c_str());
-    emit(", &");
-    arg1->accept(this);
-    emitln(");");
-  } else if (table->type_id()->name_ == "LPM") {
-  }
-}
-
-void CodegenC::emit_table_delete(MethodCallExprNode* n) {
-  TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
-  IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  IdentExprNode* type0 = table->templates_.at(0).get();
-
-  emit("%s* %s_dkey = &", type0->c_str(), n->id_->c_str());
-  arg0->accept(this);
-  emitln(";");
-  if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
-    emit("bpf_table_delete(pkt, TABLE_ID_%s, %s_dkey", n->id_->c_str(), n->id_->c_str());
-    emitln(");");
-  } else if (table->type_id()->name_ == "LPM") {
-  }
-}
-
-void CodegenC::emit_channel_push_generic(MethodCallExprNode* n) {
-  /* computation of orig_length of packet:
-   * orig_lenth = pkt->length - (orig_offset - pkt->offset)
-   * push_header(N) does pkt->length += N; pkt->offset -= N;
-   * pop_header(N) does pg_may_access(N); pkt->length -=N; pkt->offset +=N;
-   *
-   * therefore push_header(); pop_header(); sequence is currently broken, ticket #930
-   */
-  emit("bpf_channel_push_packet(pkt");
-  emit(")");
-}
-
-void CodegenC::emit_channel_push(MethodCallExprNode* n) {
-  IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  StructVariableDeclStmtNode* arg0_type = static_cast<StructVariableDeclStmtNode*>(arg0->decl_);
-  emit("bpf_channel_push_struct(pkt, STRUCTID_%s, &", arg0_type->struct_id_->c_str());
-  arg0->accept(this);
-  emit(", sizeof(");
-  arg0->accept(this);
-  emit("))");
-}
-
-void CodegenC::emit_log(MethodCallExprNode* n) {
-  emitln("{ if (unlikely(pkt->capture)) {");
-  emit("    bpf_capture(pkt, BPF_CAP_LOG, %d, ", n->line_);
-  n->args_[0]->accept(this);
-  emit("); } }");
-}
-
-void CodegenC::emit_packet_forward(MethodCallExprNode* n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_forward(pkt, ");
-  n->args_[0]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_packet_replicate(MethodCallExprNode*n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_replicate(pkt, ");
-  n->args_[0]->accept(this);
-  emit(",", n->id_->c_str());
-  n->args_[1]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_packet_clone_forward(MethodCallExprNode* n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_clone_forward(pkt, ");
-  n->args_[0]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_packet_forward_self(MethodCallExprNode* n) {
-  emit("bpf_forward_self(pkt, ");
-  n->args_[0]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_packet_drop(MethodCallExprNode* n) {
-  emit("bpf_drop(pkt)");
-}
-
-void CodegenC::emit_packet_push_header(MethodCallExprNode* n) {
-  emit("if (unlikely(bpf_push_header(pkt, ");
-  n->args_[0]->accept(this);
-  if (n->args_.size() == 1) {
-    emit(", %zu, 0) != 0)) goto ERROR", n->args_[0]->struct_type_->bit_width_ >> 3);
-  } else {
-    emit(", %zu, ", n->args_[0]->struct_type_->bit_width_ >> 3);
-    n->args_[1]->accept(this);
-    emit(") != 0)) goto ERROR");
-  }
-}
-
-void CodegenC::emit_packet_pop_header(MethodCallExprNode* n) {
-  emit("if (unlikely(bpf_pop_header(pkt, ");
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
-  } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
-    n->args_[0]->accept(this);
-  }
-  emit(", 0/*todo*/) != 0)) goto ERROR");
-}
-
-void CodegenC::emit_packet_push_vlan(MethodCallExprNode* n) {
-  emit("if (unlikely(bpf_push_vlan(pkt, bpf_htons(0x8100/*ETH_P_8021Q*/), ");
-  n->args_[0]->accept(this);
-  emit(") != 0)) goto ERROR");
-}
-
-void CodegenC::emit_packet_pop_vlan(MethodCallExprNode* n) {
-  emit("if (unlikely(bpf_pop_vlan(pkt) != 0)) goto ERROR");
-}
-
-void CodegenC::emit_packet_rewrite_field(MethodCallExprNode* n) {
-  n->args_[0]->accept(this);
-  n->args_[1]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_atomic_add(MethodCallExprNode* n) {
-  emit("__sync_fetch_and_add(&");
-  n->args_[0]->accept(this);
-  emit(", ");
-  n->args_[1]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_cksum(MethodCallExprNode* n) {
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    auto v = n->args_[0]->struct_type_;
-    size_t bit_width = v->bit_width_ >> 3;
-    auto p = proto_scopes_->top_struct()->lookup(v->id_->name_, true);
-    if (p) {
-      /* should we do store_half directly? */
-      if (!n->args_[0]->flags_[ExprNode::PROTO]) {
-        emit("bpf_ntohs(bpf_checksum_pkt(pkt, %s, %zu))", v->id_->c_str(), bit_width);
-      } else {
-        emit("bpf_ntohs(bpf_checksum(");
-        n->args_[0]->accept(this);
-        emit(", %zu))", bit_width);
-      }
-    } else {
-      throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_);
-    }
-/**    emit("pg_cksum(");
-    n->args_[0]->accept(this);
-    emit(", %zu)", n->args_[0]->struct_type_->bit_width_ >> 3);**/
-  } else {
-    throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_);
-  }
-}
-
-void CodegenC::emit_incr_cksum_u16(MethodCallExprNode* n) {
-  if (n->args_.size() == 3) {
-    /* ip checksum */
-    emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[2]->accept(this);
-    emit(")))");
-  } else {
-    /* L4 checksum */
-    emit("(");
-    /* part of pseudo header */
-    n->args_[3]->accept(this);
-    emit(" ? ");
-    emit("((pkt->hw_csum == 1) ? ");
-    /* CHECKSUM_PARTIAL update pseudo only */
-    emit("bpf_ntohs(bpf_pseudo_csum_replace2(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(" : ");
-    /* CHECKSUM_NONE update normally */
-    emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(")");
-    emit(" : ");
-    /* not part of pseudo */
-    emit("((pkt->hw_csum != 1) ? ");
-    /* CHECKSUM_NONE update normally */
-    emit("bpf_ntohs(bpf_csum_replace2(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htons(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(" : ");
-    /* CHECKSUM_PARTIAL no-op */
-    n->args_[0]->accept(this);
-    emit("))");
-  }
-}
-
-void CodegenC::emit_incr_cksum_u32(MethodCallExprNode* n) {
-  if (n->args_.size() == 3) {
-    /* ip checksum */
-    emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[2]->accept(this);
-    emit(")))");
-  } else {
-    /* L4 checksum */
-    emit("(");
-    /* part of pseudo header */
-    n->args_[3]->accept(this);
-    emit(" ? ");
-    emit("((pkt->hw_csum == 1) ? ");
-    /* CHECKSUM_PARTIAL update pseudo only */
-    emit("bpf_ntohs(bpf_pseudo_csum_replace4(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(" : ");
-    /* CHECKSUM_NONE update normally */
-    emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(")");
-    emit(" : ");
-    /* not part of pseudo */
-    emit("((pkt->hw_csum != 1) ? ");
-    /* CHECKSUM_NONE updata normally */
-    emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-    n->args_[0]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[1]->accept(this);
-    emit("), bpf_htonl(");
-    n->args_[2]->accept(this);
-    emit(")))");
-    emit(" : ");
-    /* CHECKSUM_PARTIAL no-op */
-    n->args_[0]->accept(this);
-    emit("))");
-  }
-}
-
-void CodegenC::emit_lb_hash(MethodCallExprNode* n) {
-  emit("pg_lb_hash(");
-  n->args_[0]->accept(this);
-  emit(", ");
-  n->args_[1]->accept(this);
-  emit(")");
-}
-
-void CodegenC::emit_sizeof(MethodCallExprNode* n) {
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    if (n->args_[0]->struct_type_->id_->name_ == "_Packet") {
-      emit("PG_SIZEOF(pkt)");
-    } else {
-      emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
-    }
-  } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
-    emit("%zu", n->args_[0]->bit_width_ >> 3);
-  }
-}
-
-void CodegenC::emit_get_usec_time(MethodCallExprNode* n) {
-  emit("bpf_get_usec_time()");
-}
-
-void CodegenC::emit_forward_to_vnf(MethodCallExprNode*n) {
-  emitln("pkt->arg1 |= 1;");
-  emit("pkt->arg2 = ");
-  n->args_[0]->accept(this);
-  emitln(";");
-  emit("bpf_forward_to_plum(pkt, ");
-  n->args_[1]->accept(this);
-  emit(")");
-
-}
-
-void CodegenC::emit_forward_to_group(MethodCallExprNode *n) {
-
-  emit("pkt->arg2 = ");
-  n->args_[0]->accept(this);
-  emitln(";");
-  emitln("pkt->arg3 = pkt->plum_id;");
-  emit("bpf_forward_to_plum(pkt, ");
-  emit("1/*TUNNEL_PLUM_ID*/");
-  emit(")");
-
-}
-
-void CodegenC::visit_method_call_expr_node(MethodCallExprNode* n) {
-  free_instructions_.push_back(vector<string>());
-
-  if (!n->stmts_.empty()) {
-    ++indent_;
-    emitln("{");
-  }
-
-  if (n->id_->sub_name_.size()) {
-    if (n->id_->sub_name_ == "lookup") {
-      emit_table_lookup(n);
-    } else if (n->id_->sub_name_ == "update") {
-      emit_table_update(n);
-    } else if (n->id_->sub_name_ == "delete") {
-      emit_table_delete(n);
-    } else if (n->id_->sub_name_ == "replicate" && n->id_->name_ == "pkt") {
-      emit_packet_replicate(n);
-    } else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
-      emit_packet_forward(n);
-    } else if (n->id_->sub_name_ == "forward_self" && n->id_->name_ == "pkt") {
-      emit_packet_forward_self(n);
-    } else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
-      emit_packet_push_header(n);
-    } else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
-      emit_packet_pop_header(n);
-    } else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
-      emit_packet_push_vlan(n);
-    } else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
-      emit_packet_pop_vlan(n);
-    } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
-      emit_packet_rewrite_field(n);
-    } else if (n->id_->sub_name_ == "clone_forward" && n->id_->name_ == "pkt") {
-      emit_packet_clone_forward(n);
-    }
-  } else if (n->id_->name_ == "atomic_add") {
-    emit_atomic_add(n);
-  } else if (n->id_->name_ == "log") {
-    emit_log(n);
-  } else if (n->id_->name_ == "cksum") {
-    emit_cksum(n);
-  } else if (n->id_->name_ == "incr_cksum_u16") {
-    emit_incr_cksum_u16(n);
-  } else if (n->id_->name_ == "incr_cksum_u32") {
-    emit_incr_cksum_u32(n);
-  } else if (n->id_->name_ == "lb_hash") {
-    emit_lb_hash(n);
-  } else if (n->id_->name_ == "sizeof") {
-    emit_sizeof(n);
-  } else if (n->id_->name_ == "get_usec_time") {
-    emit_get_usec_time(n);
-  } else if (n->id_->name_ == "channel_push") {
-    emit_channel_push(n);
-  } else if (n->id_->name_ == "channel_push_generic") {
-    emit_channel_push_generic(n);
-  } else if (n->id_->name_ == "forward_to_vnf") {
-    emit_forward_to_vnf(n);
-  } else if (n->id_->name_ == "forward_to_group") {
-    emit_forward_to_group(n);
-  } else {
-    n->id_->accept(this);
-    emit("(");
-    for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
-      (*it)->accept(this);
-      if (it + 1 != n->args_.end()) {
-        emit(", ");
-      }
-    }
-    emit(")");
-  }
-  if (!n->stmts_.empty()) {
-    emit(";");
-    for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
-      lnemit("");
-      (*it)->accept(this);
-    }
-    for (auto it = free_instructions_.back().rbegin(); it != free_instructions_.back().rend(); ++it) {
-      lnemit("%s;", it->c_str());
-    }
-    --indent_;
-    lnemit("}");
-  }
-  free_instructions_.pop_back();
-}
-
-/// on_match
-void CodegenC::visit_match_decl_stmt_node(MatchDeclStmtNode* n) {
-  if (n->formals_.size() != 2)
-    throw CompilerException("on_match expected 2 arguments, %zu given", n->formals_.size());
-  StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
-  StructVariableDeclStmtNode* leaf_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(1).get());
-  if (!key_n || !leaf_n)
-    throw CompilerException("invalid parameter type");
-  ++indent_;
-  emitln("if (%s_element) {", n->id_->c_str());
-  emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(), key_n->scope_id(),
-         key_n->id_->c_str(), n->id_->c_str());
-  emitln("%s* %s%s = %s_element;", leaf_n->struct_id_->c_str(), leaf_n->scope_id(),
-         leaf_n->id_->c_str(), n->id_->c_str());
-  n->block_->accept(this);
-  --indent_;
-  emitln("");
-  emit("}");
-}
-
-/// on_miss
-void CodegenC::visit_miss_decl_stmt_node(MissDeclStmtNode* n) {
-  if (n->formals_.size() != 1)
-    throw CompilerException("on_match expected 1 argument, %zu given", n->formals_.size());
-  StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
-  ++indent_;
-  emitln("if (!%s_element) {", n->id_->c_str());
-  emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
-         key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
-  n->block_->accept(this);
-  --indent_;
-  emitln("");
-  emit("}");
-}
-
-void CodegenC::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) {
-  if (n->formals_.size() != 1)
-    throw CompilerException("on_failure expected 1 argument, %zu given", n->formals_.size());
-  StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
-  ++indent_;
-  emitln("/*if ((unsigned long)%s_element >= (unsigned long)-4095) {", n->id_->name_.c_str());
-  emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
-         key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
-  n->block_->accept(this);
-  --indent_;
-  emitln("");
-  emit("}*/");
-}
-
-void CodegenC::visit_expr_stmt_node(ExprStmtNode* n) {
-  emit_comment(n);
-  n->expr_->accept(this);
-  emit(";");
-}
-
-void CodegenC::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode* n) {
-  if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') {
-    return;
-  }
-  emit_comment(n);
-  if (n->struct_id_->scope_name_ == "proto") {
-    auto p = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true);
-    if (p) {
-      string var = n->scope_id() + n->id_->name_;
-      /* zero initialize array to be filled in with packet header */
-      emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;",
-           var.c_str(), ((p->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
-      for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
-        auto asn = static_cast<AssignExprNode*>(it->get());
-        if (auto f = p->field(asn->id_->sub_name_)) {
-          size_t bit_offset = f->bit_offset_;
-          size_t bit_width = f->bit_width_;
-          if (asn->bitop_) {
-            bit_offset += f->bit_width_ - (asn->bitop_->bit_offset_ + asn->bitop_->bit_width_);
-            bit_width = std::min(bit_width - asn->bitop_->bit_offset_, asn->bitop_->bit_width_);
-          }
-          emit(" bpf_dins(%s + %zu, %zu, %zu, ", var.c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width);
-          asn->rhs_->accept(this);
-          emit(");");
-        }
-      }
-    }
-  } else {
-    /* all structs must be initialized with zeros, since they're alocated on stack,
-     * if struct doesn't have gaps between fields, gcc will be smart enough to avoid redundant zeroing */
-    if (n->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) {
-      emit("%s* %s%s = 0;", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
-    } else {
-      emit("%s %s%s = {};", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
-      if (!n->init_.empty()) {
-        for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
-          emit(" ");
-          (*it)->accept(this);
-          emit(";");
-        }
-      }
-    }
-  }
-}
-
-void CodegenC::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode* n) {
-  if (n->id_->name_ == "timer_delay" || n->id_->name_ == "parsed_bytes")
-    return;
-  emit_comment(n);
-  emit("%s %s%s", bits_to_uint(n->bit_width_), n->scope_id(), n->id_->c_str());
-  if (!n->scope_id_.empty())
-    emit(" = 0");
-  if (!n->init_.empty()) {
-    emit("; ");
-    n->init_[0]->accept(this);
-  }
-  emit(";");
-}
-
-void CodegenC::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
-  emit("typedef struct {\n");
-  ++indent_;
-  for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
-    indent();
-    (*it)->accept(this);
-    emit("\n");
-  }
-  --indent_;
-  indent();
-  emit("} __attribute__((aligned(4))) ");
-  emit("%s", n->id_->c_str());
-}
-
-void CodegenC::visit_parser_state_stmt_node(ParserStateStmtNode* n) {
-  string jump_label = n->scoped_name() + "_continue";
-  emit("%s: {", jump_label.c_str());
-  ++indent_;
-  lnemit("PG_TRACE(%.14s);", jump_label.c_str());
-  if (n->next_state_) {
-    lnemit("");
-    n->next_state_->accept(this);
-  }
-  --indent_;
-  lnemit("}");
-}
-
-void CodegenC::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
-  auto scope = scopes_->current_state();
-  scopes_->set_current(n->scope_);
-  n->block_->accept(this);
-  scopes_->set_current(scope);
-}
-void CodegenC::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
-  if (!n->id_) {
-    return;
-  }
-  string jump_label = n->scoped_name();
-  ++indent_;
-  emitln("JUMP_GUARD; %s: {", jump_label.c_str());
-  emitln("PG_TRACE(%.14s);", jump_label.c_str());
-  if (auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true)) {
-    emitln("%s = parsed_bytes; /* remember the offset of this header */", n->id_->c_str());
-    emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
-    //emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
-  }
-  // collect the protocols used in this state scope and declare them
-  set<string> protos;
-  for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) {
-    if (!it->scope_) {
-      continue;
-    }
-    auto scope = scopes_->current_state();
-    scopes_->set_current(it->scope_);
-    for (auto it2 = scopes_->current_state()->obegin(); it2 != scopes_->current_state()->oend(); ++it2) {
-      if (proto_scopes_->top_struct()->lookup((*it2)->id_->name_, true)) {
-        protos.insert((*it2)->id_->name_);
-      }
-      for (auto it3 = (*it2)->subs_.begin(); it3 != (*it2)->subs_.end(); ++it3) {
-        if (proto_scopes_->top_struct()->lookup(it3->id_->name_, true)) {
-          protos.insert(it3->id_->name_);
-        }
-      }
-    }
-    scopes_->set_current(scope);
-  }
-  for (auto it = protos.begin(); it != protos.end(); ++it) {
-    emitln("uint32_t %s = 0; /* header offset */", it->c_str());
-  }
-
-  auto it = n->subs_.begin();
-  if (n->subs_.size() == 1 && it->id_->name_ == "") {
-    // this is not a multistate protocol, emit everything and finish
-    auto scope = scopes_->current_state();
-    scopes_->set_current(it->scope_);
-    it->block_->accept(this);
-    if (n->parser_) {
-      emitln("");
-      n->parser_->accept(this);
-    }
-    scopes_->set_current(scope);
-  } else {
-    if (n->parser_) {
-      for (auto it2 = n->subs_.begin(); it2 != n->subs_.end(); ++it2) {
-        proto_rewrites_[it2->id_->full_name()] = n->scoped_name() + "_" + it2->id_->name_;
-      }
-      n->parser_->accept(this);
-      proto_rewrites_.clear();
-      emitln("");
-    }
-    for (; it != n->subs_.end(); ++it) {
-      auto scope = scopes_->current_state();
-      scopes_->set_current(it->scope_);
-
-      string jump_label = n->scoped_name() + "_" + it->id_->name_;
-      ++indent_;
-      emitln("JUMP_GUARD; %s: {", jump_label.c_str());
-      emitln("PG_TRACE(%.14s);", jump_label.c_str());
-      if (auto p = proto_scopes_->top_struct()->lookup(it->id_->name_, true)) {
-        emitln("%s = pkt->offset + parsed_bytes; /* remember the offset of this header */", it->id_->c_str());
-        emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
-        emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
-      }
-      it->block_->accept(this);
-      if (it->parser_) {
-        emitln("");
-        it->parser_->accept(this);
-      }
-      --indent_;
-      emitln("");
-      emitln("}");
-
-      scopes_->set_current(scope);
-    }
-  }
-
-  --indent_;
-  emitln("");
-  emit("}");
-}
-
-void CodegenC::visit_table_decl_stmt_node(TableDeclStmtNode* n) {
-  if (n->table_type_->name_ == "Table"
-      || n->table_type_->name_ == "SharedTable") {
-    if (n->templates_.size() != 4)
-      throw CompilerException("%s expected 4 arguments, %zu given", n->table_type_->c_str(), n->templates_.size());
-    const char *key_type = n->key_id()->c_str();
-    const char *leaf_type = n->leaf_id()->c_str();
-    char buf[128];
-    if (n->type_id()->name_ == "FIXED_MATCH" || n->type_id()->name_ == "INDEXED") {
-      //emitln("struct %s_Element {", n->id_->c_str());
-      //emitln("  PG_HASH_TABLE_ELEMENT_COMMON");
-      //emitln("  %s key;", key_type);
-      //emitln("  %s leaf;", leaf_type);
-      //emitln("} __attribute__((aligned(8)));");
-      //emitln("static struct PGHashTable %s;", n->id_->c_str());
-      //emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_);
-      //emitln("PG_HASH_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s)",
-      //       table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str());
-      emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size());
-      snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_HASH, sizeof(%s), sizeof(%s), %zd, 0}, // %s",
-               table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_, n->id_->c_str());
-    } else if (n->type_id()->name_ == "LPM") {
-      //emitln("struct %s_Element {", n->id_->c_str());
-      //emitln("  PG_LPM_TABLE_ELEMENT_COMMON");
-      //emitln("  %s key;", key_type);
-      //emitln("  %s leaf;", leaf_type);
-      //emitln("} __attribute__((aligned(8)));");
-      //emitln("static struct PGLpmTable %s;", n->id_->c_str());
-      //emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_);
-      //emitln("PG_LPM_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s, %u)",
-      //       table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str(),
-      //       n->key_id()->bit_width_);
-      emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size());
-      snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_LPM, sizeof(%s), sizeof(%s), %zd, %zd}, // %s",
-               table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_,
-               n->key_id()->bit_width_, n->id_->c_str());
-    } else {
-      throw CompilerException("table type \"%s\" unknown", n->type_id()->c_str());
-    }
-    //table_inits_.push_back(n->id_->name_);
-    table_inits_.push_back(buf);
-  }
-}
-
-int CodegenC::visit(Node* root) {
-  BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
-
-
-  scopes_->set_current(scopes_->top_state());
-  scopes_->set_current(scopes_->top_var());
-
-  print_header();
-
-  b->ver_.accept(this);
-
-  for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) {
-    (*it)->accept(this);
-    emit("\n");
-  }
-
-  print_parser();
-
-  print_footer();
-
-  return 0;
-}
-
-void CodegenC::print_timer() {
-  // visit timers
-  ++indent_;
-  emitln("PG_PARSE_DECL(timer) {");
-  emitln("uint32_t timer_delay = 0;");
-  // visit function scoped variables
-  for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
-    (*it)->accept(this);
-    emitln("");
-  }
-  for (auto it = scopes_->top_timer()->obegin(); it != scopes_->top_timer()->oend(); ++it) {
-    (*it)->accept(this);
-    emitln("");
-  }
-  ++indent_;
-  emitln("DONE: {");
-  emitln("PG_TRACE(DONE);");
-  emitln("pg_timer_forward(pkt, timer_delay);");
-  --indent_;
-  emitln("return;");
-  emitln("}");
-
-  ++indent_;
-  emitln("ERROR: {");
-  emitln("PG_TRACE(ERROR);");
-  emitln("pg_drop(pkt);");
-  emitln("pg_timer_forward(pkt, timer_delay);");
-  --indent_;
-  emitln("return;");
-  --indent_;
-  emitln("}");
-  emitln("}");
-}
-
-void CodegenC::print_parser() {
-  ++indent_;
-  emitln("PG_PARSE_DECL(parse) {");
-  /* emitln("uint8_t *pp;"); */
-  emitln("uint32_t parsed_bytes = 0;");
-  emitln("uint16_t orig_offset = 0;/*pkt->offset;*/");
-
-  // visit function scoped variables
-  for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
-    (*it)->accept(this);
-    emitln("");
-  }
-
-  for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
-    if (proto_scopes_->top_struct()->lookup((*it)->id_->name_, true)) {
-      emitln("uint32_t %s = 0; /* header offset */", (*it)->id_->c_str());
-    }
-  }
-
-  /* emitln("pp = pkt->start + pkt->offset;"); */
-  emitln("goto s1_INIT;");
-
-  // finally, visit the states
-  for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
-    (*it)->accept(this);
-    emitln("");
-  }
-
-  ++indent_;
-  emitln("ERROR: {");
-  emitln("PG_TRACE(ERROR);");
-  --indent_;
-  emitln("goto CLEANUP;");
-  emitln("}");
-
-  ++indent_;
-  emitln("DONE: {");
-  emitln("PG_TRACE(DONE);");
-  --indent_;
-  emitln("goto CLEANUP;");
-  emitln("}");
-
-  ++indent_;
-  emitln("CLEANUP: {");
-  --indent_;
-  emitln("/* cleanup is done by PE */;");
-  --indent_;
-  emitln("}");
-
-  emitln("}");
-
-  //print_timer();
-}
-
-void CodegenC::print_header() {
-  if (use_pre_header_) {
-    //emit("%s", PRE_HEADER.c_str());
-    emitln("");
-  } else {
-    emitln("#include <stdint.h>");
-    emitln("#include \"../dp/linux/filter.h\"");
-    emitln("#include \"container/pg_api.h\"");
-    emitln("#include \"container/pg_defs.h\"");
-  }
-  emitln("#define JUMP_GUARD goto DONE");
-  emitln("#define PG_SIZEOF(_pkt) ((int)_pkt->length - (int)pkt->offset + orig_offset)");
-
-  int i = 0;
-  // declare structures
-  for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) {
-    if ((*it)->id_->name_ == "_Packet")
-      continue;
-    (*it)->accept(this);
-    emit(";\n");
-    emitln("#define STRUCTID_%s %d", (*it)->id_->c_str(), i++);
-  }
-  emitln("#define STRUCTID_generic %d", i);
-}
-
-void CodegenC::print_footer() {
-  //emitln("#define EXPAND_TABLES(E) \\");
-  emitln("struct bpf_table plum_tables[] = {");
-  for (auto it = table_inits_.begin(); it != table_inits_.end(); ++it) {
-    //emit("E(%s) ", it->c_str());
-    emitln("  %s", it->c_str());
-  }
-  emitln("  {0,0,0,0,0,0} // last table marker");
-  emitln("};");
-  emitln("");
-  emitln("PG_INIT");
-  emitln("PG_CLEANUP");
-}
-
-}  // namespace cc
-}  // namespace ebpf
diff --git a/jit/src/cc/codegen_c.h b/jit/src/cc/codegen_c.h
deleted file mode 100644 (file)
index 747e1ad..0000000
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * =====================================================================
- * Copyright (c) 2012, PLUMgrid, http://plumgrid.com
- *
- * This source is subject to the PLUMgrid License.
- * All rights reserved.
- *
- * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF
- * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
- * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
- * PARTICULAR PURPOSE.
- *
- * PLUMgrid confidential information, delete if you are not the
- * intended recipient.
- *
- * =====================================================================
- */
-
-#pragma once
-
-#include <stdio.h>
-#include <vector>
-#include <string>
-#include <set>
-
-#include "cc/node.h"
-#include "cc/scope.h"
-
-namespace ebpf {
-namespace cc {
-
-using std::vector;
-using std::string;
-using std::set;
-
-class CodegenC : public Visitor {
- public:
-  CodegenC(FILE* out, Scopes::Ptr scopes, Scopes::Ptr proto_scopes, bool use_pre_header)
-      : out_(out), indent_(0), tmp_reg_index_(0), scopes_(scopes),
-      proto_scopes_(proto_scopes), use_pre_header_(use_pre_header) {}
-
-#define VISIT(type, func) virtual void visit_##func(type* n);
-  EXPAND_NODES(VISIT)
-#undef VISIT
-
-  virtual int visit(Node* n);
-
-  void emit_table_lookup(MethodCallExprNode* n);
-  void emit_table_update(MethodCallExprNode* n);
-  void emit_table_delete(MethodCallExprNode* n);
-  void emit_channel_push(MethodCallExprNode* n);
-  void emit_channel_push_generic(MethodCallExprNode* n);
-  void emit_log(MethodCallExprNode* n);
-  void emit_packet_forward(MethodCallExprNode* n);
-  void emit_packet_replicate(MethodCallExprNode* n);
-  void emit_packet_clone_forward(MethodCallExprNode* n);
-  void emit_packet_forward_self(MethodCallExprNode* n);
-  void emit_packet_drop(MethodCallExprNode* n);
-  void emit_packet_broadcast(MethodCallExprNode* n);
-  void emit_packet_multicast(MethodCallExprNode* n);
-  void emit_packet_push_header(MethodCallExprNode* n);
-  void emit_packet_pop_header(MethodCallExprNode* n);
-  void emit_packet_push_vlan(MethodCallExprNode* n);
-  void emit_packet_pop_vlan(MethodCallExprNode* n);
-  void emit_packet_rewrite_field(MethodCallExprNode* n);
-  void emit_atomic_add(MethodCallExprNode* n);
-  void emit_cksum(MethodCallExprNode* n);
-  void emit_incr_cksum_u16(MethodCallExprNode* n);
-  void emit_incr_cksum_u32(MethodCallExprNode* n);
-  void emit_lb_hash(MethodCallExprNode* n);
-  void emit_sizeof(MethodCallExprNode* n);
-  void emit_get_usec_time(MethodCallExprNode* n);
-  void emit_forward_to_vnf(MethodCallExprNode* n);
-  void emit_forward_to_group(MethodCallExprNode* n);
-  void print_parser();
-  void print_timer();
-  void print_header();
-  void print_footer();
-
- private:
-  void indent();
-
-  template <typename... Args> void emitln(const char *fmt, Args&&... params);
-  template <typename... Args> void lnemit(const char *fmt, Args&&... params);
-  template <typename... Args> void emit(const char *fmt, Args&&... params);
-  void emitln(const char *s);
-  void lnemit(const char *s);
-  void emit(const char *s);
-  void emit_comment(Node* n);
-
-  FILE* out_;
-  int indent_;
-  int tmp_reg_index_;
-  Scopes::Ptr scopes_;
-  Scopes::Ptr proto_scopes_;
-  bool use_pre_header_;
-  vector<vector<string> > free_instructions_;
-  vector<string> table_inits_;
-  map<string, string> proto_rewrites_;
-};
-
-}  // namespace cc
-}  // namespace ebpf
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644 (file)
index 0000000..4761fed
--- /dev/null
@@ -0,0 +1,10 @@
+
+set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
+set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti")
+
+#include_directories(${CMAKE_CURRENT_SOURCE_DIR}/compat/include)
+
+include_directories(${CMAKE_CURRENT_SOURCE_DIR})
+include_directories(${CMAKE_CURRENT_BINARY_DIR})
+
+add_subdirectory(cc)
similarity index 70%
rename from jit/src/bpf.py
rename to src/bpf.py
index 0c06d88..774f9f7 100644 (file)
@@ -1,6 +1,5 @@
 import ctypes as ct
 import os
-import pyroute2 as pr
 
 lib = ct.cdll.LoadLibrary("libbpfprog.so")
 
@@ -35,35 +34,45 @@ lib.bpf_attach_filter.argtypes = [ct.c_int, ct.c_char_p, ct.c_uint, ct.c_ubyte,
 lib.bpf_prog_load.restype = ct.c_int
 lib.bpf_prog_load.argtypes = [ct.c_int, ct.c_void_p, ct.c_size_t,
         ct.c_char_p]
+lib.bpf_attach_kprobe.restype = ct.c_int
+lib.bpf_attach_kprobe.argtypes = [ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_int, ct.c_int, ct.c_int]
 
 class BPF(object):
     BPF_PROG_TYPE_SOCKET_FILTER = 1
-    BPF_PROG_TYPE_SCHED_CLS = 2
-    BPF_PROG_TYPE_SCHED_ACT = 3
+    BPF_PROG_TYPE_KPROBE = 2
+    BPF_PROG_TYPE_SCHED_CLS = 3
+    BPF_PROG_TYPE_SCHED_ACT = 4
     def __init__(self, name, dp_file, dph_file,
             prog_type=BPF_PROG_TYPE_SOCKET_FILTER,
             debug=0):
         self.debug = debug
         self.name = name
+        self.prog_type = prog_type
+        self.fd = {}
         self.prog = lib.bpf_program_create(dp_file.encode("ascii"),
                 dph_file.encode("ascii"), self.debug)
 
-        if self.prog == ct.c_void_p(None):
+        if self.prog == None:
             raise Exception("Failed to compile BPF program %s" % dp_file)
 
-        if lib.bpf_program_start(self.prog,
-                self.name.encode("ascii")) == ct.c_void_p(None):
+        if prog_type == BPF.BPF_PROG_TYPE_KPROBE:
+            return
+
+        self.load(self.name)
+
+    def load(self, prog_name):
+        if lib.bpf_program_start(self.prog, prog_name.encode("ascii")) == None:
             raise Exception("Unknown program %s" % self.name)
 
-        self.fd = lib.bpf_prog_load(prog_type,
-                lib.bpf_program_start(self.prog, self.name.encode("ascii")),
-                lib.bpf_program_size(self.prog, self.name.encode("ascii")),
+        self.fd[prog_name] = lib.bpf_prog_load(self.prog_type,
+                lib.bpf_program_start(self.prog, prog_name.encode("ascii")),
+                lib.bpf_program_size(self.prog, prog_name.encode("ascii")),
                 lib.bpf_program_license(self.prog))
 
-        if self.fd < 0:
+        if self.fd[prog_name] < 0:
             print((ct.c_char * 65536).in_dll(lib, "bpf_log_buf").value)
             #print(ct.c_char_p.in_dll(lib, "bpf_log_buf").value)
-            raise Exception("Failed to load BPF program %s" % dp_file)
+            raise Exception("Failed to load BPF program %s" % self.name)
 
     class Table(object):
         def __init__(self, bpf, map_fd, keytype, leaftype):
@@ -126,20 +135,39 @@ class BPF(object):
             raise Exception("Failed to find BPF Table %s" % name)
         return BPF.Table(self, map_fd, keytype, leaftype)
 
-    def attach(self, dev):
+    def attach(self, dev, prog_name=None):
+        prog_name = prog_name or self.name
         self.sock = lib.bpf_open_raw_sock(dev.encode("ascii"))
         if self.sock < 0:
             errstr = os.strerror(ct.get_errno())
             raise Exception("Failed to open raw device %s: %s" % (dev, errstr))
-        res = lib.bpf_attach_socket(self.sock, self.fd)
+        res = lib.bpf_attach_socket(self.sock, self.fd[prog_name])
         if res < 0:
             errstr = os.strerror(ct.get_errno())
             raise Exception("Failed to attach BPF to device %s: %s"
                     % (dev, errstr))
 
-    def attach_filter(self, ifindex, prio, classid):
-        res = lib.bpf_attach_filter(self.fd, self.name.encode("ascii"), ifindex, prio, classid)
+    def attach_filter(self, ifindex, prio, classid, prog_name=None):
+        prog_name = prog_name or self.name
+        res = lib.bpf_attach_filter(self.fd[prog_name], self.name.encode("ascii"), ifindex, prio, classid)
         if res < 0:
             raise Exception("Failed to filter with BPF")
 
+    def attach_kprobe(self, event, prog_name, pid=-1, cpu=0, group_fd=-1):
+        ev_name = "p_" + event.replace("+", "_")
+        desc = "p:kprobes/%s %s" % (ev_name, event)
+        res = lib.bpf_attach_kprobe(self.fd[prog_name], ev_name.encode("ascii"),
+                desc.encode("ascii"), pid, cpu, group_fd)
+        if res < 0:
+            raise Exception("Failed to attach BPF to kprobe")
+        return res
+
+    def attach_kretprobe(self, event, prog_name, pid=-1, cpu=0, group_fd=-1):
+        ev_name = "r_" + event.replace("+", "_")
+        desc = "r:kprobes/%s %s" % (ev_name, event)
+        res = lib.bpf_attach_kprobe(self.fd[prog_name], ev_name.encode("ascii"),
+                desc.encode("ascii"), pid, cpu, group_fd)
+        if res < 0:
+            raise Exception("Failed to attach BPF to kprobe")
+        return res
 
similarity index 90%
rename from jit/src/cc/CMakeLists.txt
rename to src/cc/CMakeLists.txt
index df88e42..85b5626 100644 (file)
@@ -10,9 +10,9 @@ ADD_FLEX_BISON_DEPENDENCY(Lexer Parser)
 add_custom_command(
   OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bitops.bc
   COMMAND ${CLANG}
-  ARGS -O3 -emit-llvm -S -o bitops.bc -I${CMAKE_SOURCE_DIR}/jit/compat/include
+  ARGS -O3 -emit-llvm -o bitops.bc -I${CMAKE_SOURCE_DIR}/jit/compat/include
     -c ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c
-  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c
+  DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/bitops.c ${CMAKE_CURRENT_SOURCE_DIR}/bpf_helpers.h
   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
   COMMENT "Generating bitops IR")
 add_custom_command(
similarity index 100%
rename from jit/src/cc/bitops.c
rename to src/cc/bitops.c
similarity index 100%
rename from jit/src/cc/bpf_common.h
rename to src/cc/bpf_common.h
similarity index 85%
rename from jit/src/cc/bpf_helpers.h
rename to src/cc/bpf_helpers.h
index 6ccc181..36e1dae 100644 (file)
@@ -15,6 +15,12 @@ static int (*bpf_map_update_elem)(void *map, void *key, void *value,
        (void *) BPF_FUNC_map_update_elem;
 static int (*bpf_map_delete_elem)(void *map, void *key) =
        (void *) BPF_FUNC_map_delete_elem;
+static int (*bpf_probe_read)(void *dst, unsigned long long size, void *unsafe_ptr) =
+       (void *) BPF_FUNC_probe_read;
+static unsigned long long (*bpf_ktime_get_ns)(void) =
+       (void *) BPF_FUNC_ktime_get_ns;
+static int (*bpf_trace_printk)(const char *fmt, unsigned long long fmt_size, ...) =
+       (void *) BPF_FUNC_trace_printk;
 
 /* llvm builtin functions that eBPF C program may use to
  * emit BPF_LD_ABS and BPF_LD_IND instructions
similarity index 94%
rename from jit/src/cc/bpf_program.cc
rename to src/cc/bpf_program.cc
index 6cb200a..2b61f51 100644 (file)
@@ -85,11 +85,17 @@ int BPFProgram::parse() {
 
   proto_parser_ = make_unique<ebpf::cc::Parser>(proto_filename_);
   rc = proto_parser_->parse();
-  if (rc) return rc;
+  if (rc) {
+    fprintf(stderr, "In file: %s\n", filename_.c_str());
+    return rc;
+  }
 
   parser_ = make_unique<ebpf::cc::Parser>(filename_);
   rc = parser_->parse();
-  if (rc) return rc;
+  if (rc) {
+    fprintf(stderr, "In file: %s\n", filename_.c_str());
+    return rc;
+  }
 
   //ebpf::cc::Printer printer(stderr);
   //printer.visit(parser_->root_node_);
@@ -101,10 +107,7 @@ int BPFProgram::parse() {
     exit(1);
   }
 
-  codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(),
-                                                      proto_parser_->scopes_.get(),
-                                                      /*use_pre_header*/false,
-                                                      parser_->pragma("name"));
+  codegen_ = ebpf::make_unique<ebpf::cc::CodegenLLVM>(mod_, parser_->scopes_.get(), proto_parser_->scopes_.get());
   ret = codegen_->visit(parser_->root_node_);
   if (get<0>(ret) != 0 || get<1>(ret).size()) {
     fprintf(stderr, "Codegen error @line=%d: %s\n", get<0>(ret), get<1>(ret).c_str());
@@ -182,7 +185,7 @@ int BPFProgram::finalize() {
 }
 
 uint8_t * BPFProgram::start(const string &name) const {
-  auto section = sections_.find(name);
+  auto section = sections_.find("." + name);
   if (section == sections_.end())
     return nullptr;
 
@@ -190,7 +193,7 @@ uint8_t * BPFProgram::start(const string &name) const {
 }
 
 size_t BPFProgram::size(const string &name) const {
-  auto section = sections_.find(name);
+  auto section = sections_.find("." + name);
   if (section == sections_.end())
     return 0;
 
similarity index 63%
rename from jit/src/cc/codegen_llvm.cc
rename to src/cc/codegen_llvm.cc
index 5d948d0..6ba9f23 100644 (file)
@@ -44,8 +44,6 @@
 extern "C"
 int bpf_create_map(int map_type, int key_size, int value_size, int max_entries);
 
-#define ENABLE_RELOCATIONS 0
-
 namespace ebpf {
 namespace cc {
 
@@ -64,6 +62,7 @@ using std::vector;
 // parameters), so cast it instead :(
 #define B (*((IRBuilder<> *)this->b_))
 
+// Helper class to push/pop the insert block
 class BlockStack {
  public:
   explicit BlockStack(CodegenLLVM *cc, BasicBlock *bb)
@@ -81,6 +80,7 @@ class BlockStack {
   CodegenLLVM *cc_;
 };
 
+// Helper class to push/pop switch statement insert block
 class SwitchStack {
  public:
   explicit SwitchStack(CodegenLLVM *cc, SwitchInst *sw)
@@ -95,11 +95,9 @@ class SwitchStack {
   CodegenLLVM *cc_;
 };
 
-CodegenLLVM::CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes,
-                         bool use_pre_header, const string &section)
+CodegenLLVM::CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes)
   : out_(stdout), mod_(mod), indent_(0), tmp_reg_index_(0), scopes_(scopes),
-    proto_scopes_(proto_scopes), use_pre_header_(use_pre_header),
-    section_(section), expr_(nullptr) {
+    proto_scopes_(proto_scopes), expr_(nullptr) {
   b_ = new IRBuilder<>(ctx());
 }
 CodegenLLVM::~CodegenLLVM() {
@@ -107,18 +105,6 @@ CodegenLLVM::~CodegenLLVM() {
 }
 
 template <typename... Args>
-void CodegenLLVM::emitln(const char *fmt, Args&&... params) {
-  //fprintf(out_, fmt, std::forward<Args>(params)...);
-  //fprintf(out_, "\n%*s", indent_ * 2, "");
-  //fflush(out_);
-}
-void CodegenLLVM::emitln(const char *s) {
-  //fprintf(out_, "%s", s);
-  //fprintf(out_, "\n%*s", indent_ * 2, "");
-  //fflush(out_);
-}
-
-template <typename... Args>
 void CodegenLLVM::emit(const char *fmt, Args&&... params) {
   //fprintf(out_, fmt, std::forward<Args>(params)...);
   //fflush(out_);
@@ -128,52 +114,20 @@ void CodegenLLVM::emit(const char *s) {
   //fflush(out_);
 }
 
-template <typename... Args>
-void CodegenLLVM::lnemit(const char *fmt, Args&&... params) {
-  //fprintf(out_, "\n%*s", indent_ * 2, "");
-  //fprintf(out_, fmt, std::forward<Args>(params)...);
-  //fflush(out_);
-}
-void CodegenLLVM::lnemit(const char *s) {
-  //fprintf(out_, "\n%*s", indent_ * 2, "");
-  //fprintf(out_, "%s", s);
-  //fflush(out_);
-}
-
-void CodegenLLVM::indent() {
-  //fprintf(out_, "%*s", indent_ * 2, "");
-  //fflush(out_);
-}
-
-void CodegenLLVM::emit_comment(Node *n) {
-  // if (!n->text_.empty()) {
-  //   emitln("/* %s */", n->text_.c_str());
-  // }
-}
-
 StatusTuple CodegenLLVM::visit_block_stmt_node(BlockStmtNode *n) {
 
   // enter scope
-  auto scope = scopes_->current_var();
-  if (n->scope_) {
-    scopes_->set_current(n->scope_);
-  }
+  if (n->scope_)
+    scopes_->push_var(n->scope_);
 
   if (!n->stmts_.empty()) {
     for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it)
       TRY2((*it)->accept(this));
   }
   // exit scope
-  scopes_->set_current(scope);
-
-  return mkstatus(0);
-}
+  if (n->scope_)
+    scopes_->pop_var();
 
-StatusTuple CodegenLLVM::visit_version_stmt_node(VersionStmtNode *n) {
-  uint32_t version;
-  version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
-  emit("static const uint32_t  plumlet_version   __attribute__"
-      "((section (\".version\"), used)) = 0x%x;\n", version);
   return mkstatus(0);
 }
 
@@ -184,11 +138,12 @@ StatusTuple CodegenLLVM::visit_if_stmt_node(IfStmtNode *n) {
   BasicBlock *label_end = BasicBlock::Create(ctx(), "if.end", parent);
 
   TRY2(n->cond_->accept(this));
+  Value *is_not_null = B.CreateIsNotNull(pop_expr());
 
   if (n->false_block_)
-    B.CreateCondBr(pop_expr(), label_then, label_else);
+    B.CreateCondBr(is_not_null, label_then, label_else);
   else
-    B.CreateCondBr(pop_expr(), label_then, label_end);
+    B.CreateCondBr(is_not_null, label_then, label_end);
 
   {
     BlockStack bstack(this, label_then);
@@ -256,8 +211,10 @@ StatusTuple CodegenLLVM::visit_switch_stmt_node(SwitchStmtNode *n) {
     TRY2(n->block_->accept(this));
   }
   // if other cases are terminal, erase the end label
-  if (pred_empty(label_end))
+  if (pred_empty(label_end)) {
+    B.SetInsertPoint(resolve_label("DONE"));
     label_end->eraseFromParent();
+  }
   return mkstatus(0);
 }
 
@@ -320,11 +277,9 @@ StatusTuple CodegenLLVM::visit_ident_expr_node(IdentExprNode *n) {
         }
       }
     } else {
-      emit("*%s%s", n->decl_->scope_id(), n->c_str());
       auto it = vars_.find(n->decl_);
       if (it == vars_.end()) return mkstatus_(n, "Cannot locate variable %s in vars_ table", n->c_str());
-      LoadInst *load_1 = B.CreateAlignedLoad(it->second, 4);
-      expr_ = load_1;
+      expr_ = n->is_lhs() ? it->second : (Value *)B.CreateLoad(it->second);
     }
   } else {
     if (n->sub_name_.size()) {
@@ -377,11 +332,13 @@ StatusTuple CodegenLLVM::visit_assign_expr_node(AssignExprNode *n) {
       emit(")");
       return mkstatus_(n, "unsupported");
     } else {
+      TRY2(n->rhs_->accept(this));
+      Value *rhs = pop_expr();
       TRY2(n->id_->accept(this));
       Value *lhs = pop_expr();
-      TRY2(n->rhs_->accept(this));
-      expr_ = B.CreateIntCast(expr_, cast<PointerType>(lhs->getType())->getElementType(), false);
-      B.CreateStore(pop_expr(), lhs);
+      if (!n->rhs_->is_ref())
+        rhs = B.CreateIntCast(rhs, cast<PointerType>(lhs->getType())->getElementType(), false);
+      B.CreateStore(rhs, lhs);
     }
   }
   return mkstatus(0);
@@ -389,7 +346,7 @@ StatusTuple CodegenLLVM::visit_assign_expr_node(AssignExprNode *n) {
 
 StatusTuple CodegenLLVM::lookup_var(Node *n, const string &name, Scopes::VarScope *scope,
                                     VariableDeclStmtNode **decl, Value **mem) const {
-  *decl = scope->lookup(name, false);
+  *decl = scope->lookup(name, SCOPE_GLOBAL);
   if (!*decl) return mkstatus_(n, "cannot find %s variable", name.c_str());
   auto it = vars_.find(*decl);
   if (it == vars_.end()) return mkstatus_(n, "unable to find %s memory location", name.c_str());
@@ -401,7 +358,7 @@ StatusTuple CodegenLLVM::visit_packet_expr_node(PacketExprNode *n) {
   auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
   VariableDeclStmtNode *offset_decl, *skb_decl;
   Value *offset_mem, *skb_mem;
-  TRY2(lookup_var(n, "skb", scopes_->top_var(), &skb_decl, &skb_mem));
+  TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem));
   TRY2(lookup_var(n, "$" + n->id_->name_, scopes_->current_var(), &offset_decl, &offset_mem));
 
   if (p) {
@@ -458,6 +415,17 @@ StatusTuple CodegenLLVM::visit_integer_expr_node(IntegerExprNode *n) {
   return mkstatus(0);
 }
 
+StatusTuple CodegenLLVM::visit_string_expr_node(StringExprNode *n) {
+  if (n->is_lhs()) return mkstatus_(n, "cannot assign to a string");
+
+  Value *global = B.CreateGlobalString(n->val_);
+  Value *ptr = new AllocaInst(B.getInt8Ty(), B.getInt64(n->val_.size() + 1), "", resolve_entry_stack());
+  B.CreateMemCpy(ptr, global, n->val_.size() + 1, 1);
+  expr_ = ptr;
+
+  return mkstatus(0);
+}
+
 StatusTuple CodegenLLVM::emit_short_circuit_and(BinopExprNode *n) {
   Function *parent = B.GetInsertBlock()->getParent();
   BasicBlock *label_start = B.GetInsertBlock();
@@ -586,7 +554,8 @@ StatusTuple CodegenLLVM::visit_goto_expr_node(GotoExprNode *n) {
 
 StatusTuple CodegenLLVM::visit_return_expr_node(ReturnExprNode *n) {
   TRY2(n->expr_->accept(this));
-  Value *cast_1 = B.CreateIntCast(pop_expr(), cast<PointerType>(retval_->getType())->getElementType(), true);
+  Function *parent = B.GetInsertBlock()->getParent();
+  Value *cast_1 = B.CreateIntCast(pop_expr(), parent->getReturnType(), true);
   B.CreateStore(cast_1, retval_);
   B.CreateBr(resolve_label("DONE"));
   return mkstatus(0);
@@ -595,7 +564,6 @@ StatusTuple CodegenLLVM::visit_return_expr_node(ReturnExprNode *n) {
 StatusTuple CodegenLLVM::emit_table_lookup(MethodCallExprNode *n) {
   TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
   IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  stringstream free_inst;
   IdentExprNode* arg1;
   StructVariableDeclStmtNode* arg1_type;
 
@@ -639,7 +607,6 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
   TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
   IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
   IdentExprNode* arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get());
-  IdentExprNode* type0 = table->templates_.at(0).get();
 
   auto table_fd_it = table_fds_.find(table);
   if (table_fd_it == table_fds_.end())
@@ -653,20 +620,16 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
                                         B.getInt64(table_fd_it->second));
   Value *pseudo_map_fd = pseudo_call;
 
-  emit("%s* %s_ukey = &", type0->c_str(), n->id_->c_str());
   TRY2(arg0->accept(this));
   Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
 
-  emitln(";");
   if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
-    emit("bpf_table_update(pkt, TABLE_ID_%s, %s_ukey", n->id_->c_str(), n->id_->c_str());
-    emit(", &");
     TRY2(arg1->accept(this));
     Value *value_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
 
     expr_ = B.CreateCall4(update_fn, pseudo_map_fd, key_ptr, value_ptr, B.getInt64(BPF_ANY));
-    emitln(");");
-  } else if (table->type_id()->name_ == "LPM") {
+  } else {
+    return mkstatus_(n, "unsupported");
   }
   return mkstatus(0);
 }
@@ -674,129 +637,48 @@ StatusTuple CodegenLLVM::emit_table_update(MethodCallExprNode *n) {
 StatusTuple CodegenLLVM::emit_table_delete(MethodCallExprNode *n) {
   TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_);
   IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  IdentExprNode* type0 = table->templates_.at(0).get();
 
-  emit("%s* %s_dkey = &", type0->c_str(), n->id_->c_str());
-  TRY2(arg0->accept(this));
-  emitln(";");
-  if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
-    emit("bpf_table_delete(pkt, TABLE_ID_%s, %s_dkey", n->id_->c_str(), n->id_->c_str());
-    emitln(");");
-  } else if (table->type_id()->name_ == "LPM") {
-  }
-  return mkstatus(0);
-}
+  auto table_fd_it = table_fds_.find(table);
+  if (table_fd_it == table_fds_.end())
+    return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str());
+  Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
+  if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist");
+  Function *update_fn = mod_->getFunction("bpf_map_update_elem_");
+  if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined");
 
-StatusTuple CodegenLLVM::emit_channel_push_generic(MethodCallExprNode *n) {
-  /* computation of orig_length of packet:
-   * orig_lenth = pkt->length - (orig_offset - pkt->offset)
-   * push_header(N) does pkt->length += N; pkt->offset -= N;
-   * pop_header(N) does pg_may_access(N); pkt->length -=N; pkt->offset +=N;
-   *
-   * therefore push_header(); pop_header(); sequence is currently broken, ticket #930
-   */
-  emit("bpf_channel_push_packet(pkt");
-  emit(")");
-  return mkstatus(0);
-}
+  CallInst *pseudo_call = B.CreateCall2(pseudo_fn, B.getInt64(BPF_PSEUDO_MAP_FD),
+                                        B.getInt64(table_fd_it->second));
+  Value *pseudo_map_fd = pseudo_call;
 
-StatusTuple CodegenLLVM::emit_channel_push(MethodCallExprNode *n) {
-  IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get());
-  StructVariableDeclStmtNode* arg0_type = static_cast<StructVariableDeclStmtNode*>(arg0->decl_);
-  emit("bpf_channel_push_struct(pkt, STRUCTID_%s, &", arg0_type->struct_id_->c_str());
   TRY2(arg0->accept(this));
-  emit(", sizeof(");
-  TRY2(arg0->accept(this));
-  emit("))");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_log(MethodCallExprNode *n) {
-  emitln("{ if (unlikely(pkt->capture)) {");
-  emit("    bpf_capture(pkt, BPF_CAP_LOG, %d, ", n->line_);
-  TRY2(n->args_[0]->accept(this));
-  emit("); } }");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_forward(MethodCallExprNode *n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_forward(pkt, ");
-  TRY2(n->args_[0]->accept(this));
-  emit(")");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_replicate(MethodCallExprNode*n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_replicate(pkt, ");
-  TRY2(n->args_[0]->accept(this));
-  emit(",", n->id_->c_str());
-  TRY2(n->args_[1]->accept(this));
-  emit(")");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_clone_forward(MethodCallExprNode *n) {
-  emitln("pkt->arg1 &= ~1;");
-  emit("bpf_clone_forward(pkt, ");
-  TRY2(n->args_[0]->accept(this));
-  emit(")");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_forward_self(MethodCallExprNode *n) {
-  emit("bpf_forward_self(pkt, ");
-  TRY2(n->args_[0]->accept(this));
-  emit(")");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_drop(MethodCallExprNode *n) {
-  emit("bpf_drop(pkt)");
-  return mkstatus(0);
-}
+  Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
 
-StatusTuple CodegenLLVM::emit_packet_push_header(MethodCallExprNode *n) {
-  emit("if (unlikely(bpf_push_header(pkt, ");
-  TRY2(n->args_[0]->accept(this));
-  if (n->args_.size() == 1) {
-    emit(", %zu, 0) != 0)) goto ERROR", n->args_[0]->struct_type_->bit_width_ >> 3);
+  if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") {
+    expr_ = B.CreateCall2(update_fn, pseudo_map_fd, key_ptr);
   } else {
-    emit(", %zu, ", n->args_[0]->struct_type_->bit_width_ >> 3);
-    TRY2(n->args_[1]->accept(this));
-    emit(") != 0)) goto ERROR");
+    return mkstatus_(n, "unsupported");
   }
   return mkstatus(0);
 }
 
-StatusTuple CodegenLLVM::emit_packet_pop_header(MethodCallExprNode *n) {
-  emit("if (unlikely(bpf_pop_header(pkt, ");
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
-  } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
-    TRY2(n->args_[0]->accept(this));
+StatusTuple CodegenLLVM::emit_log(MethodCallExprNode *n) {
+  vector<Value *> args;
+  auto arg = n->args_.begin();
+  TRY2((*arg)->accept(this));
+  args.push_back(pop_expr());
+  args.push_back(B.getInt64(((*arg)->bit_width_ >> 3) + 1));
+  ++arg;
+  for (; arg != n->args_.end(); ++arg) {
+    TRY2((*arg)->accept(this));
+    args.push_back(pop_expr());
   }
-  emit(", 0/*todo*/) != 0)) goto ERROR");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_packet_push_vlan(MethodCallExprNode *n) {
-  emit("if (unlikely(bpf_push_vlan(pkt, bpf_htons(0x8100/*ETH_P_8021Q*/), ");
-  TRY2(n->args_[0]->accept(this));
-  emit(") != 0)) goto ERROR");
-  return mkstatus(0);
-}
 
-StatusTuple CodegenLLVM::emit_packet_pop_vlan(MethodCallExprNode *n) {
-  emit("if (unlikely(bpf_pop_vlan(pkt) != 0)) goto ERROR");
-  return mkstatus(0);
-}
+  // int bpf_trace_printk(fmt, sizeof(fmt), ...)
+  FunctionType *printk_fn_type = FunctionType::get(B.getInt32Ty(), vector<Type *>({B.getInt8PtrTy(), B.getInt64Ty()}), true);
+  Value *printk_fn = B.CreateIntToPtr(B.getInt64(BPF_FUNC_trace_printk),
+                                         PointerType::getUnqual(printk_fn_type));
 
-StatusTuple CodegenLLVM::emit_packet_rewrite_field(MethodCallExprNode *n) {
-  TRY2(n->args_[1]->accept(this));
-  TRY2(n->args_[0]->accept(this));
-  emit(")");
+  expr_ = B.CreateCall(printk_fn, args);
   return mkstatus(0);
 }
 
@@ -810,32 +692,6 @@ StatusTuple CodegenLLVM::emit_atomic_add(MethodCallExprNode *n) {
   return mkstatus(0);
 }
 
-StatusTuple CodegenLLVM::emit_cksum(MethodCallExprNode *n) {
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    auto v = n->args_[0]->struct_type_;
-    size_t bit_width = v->bit_width_ >> 3;
-    auto p = proto_scopes_->top_struct()->lookup(v->id_->name_, true);
-    if (p) {
-      /* should we do store_half directly? */
-      if (!n->args_[0]->flags_[ExprNode::PROTO]) {
-        emit("bpf_ntohs(bpf_checksum_pkt(pkt, %s, %zu))", v->id_->c_str(), bit_width);
-      } else {
-        emit("bpf_ntohs(bpf_checksum(");
-        TRY2(n->args_[0]->accept(this));
-        emit(", %zu))", bit_width);
-      }
-    } else {
-      return mkstatus_(n, "cannot pg_cksum %d", n->args_[0]->typeof_);
-    }
-/**    emit("pg_cksum(");
-    TRY2(n->args_[0]->accept(this));
-    emit(", %zu)", n->args_[0]->struct_type_->bit_width_ >> 3);**/
-  } else {
-    return mkstatus_(n, "cannot pg_cksum %d", n->args_[0]->typeof_);
-  }
-  return mkstatus(0);
-}
-
 StatusTuple CodegenLLVM::emit_incr_cksum(MethodCallExprNode *n, size_t sz) {
   Value *is_pseudo;
   string csum_fn_str;
@@ -865,132 +721,19 @@ StatusTuple CodegenLLVM::emit_incr_cksum(MethodCallExprNode *n, size_t sz) {
 
   VariableDeclStmtNode *skb_decl;
   Value *skb_mem;
-  TRY2(lookup_var(n, "skb", scopes_->top_var(), &skb_decl, &skb_mem));
+  TRY2(lookup_var(n, "skb", scopes_->current_var(), &skb_decl, &skb_mem));
   LoadInst *skb_ptr = B.CreateLoad(skb_mem);
   Value *skb_ptr8 = B.CreateBitCast(skb_ptr, B.getInt8PtrTy());
 
   expr_ = B.CreateCall5(csum_fn, skb_ptr8, offset, old_val, new_val, flags);
-
-  // if (n->args_.size() == 3) {
-  //   /* ip checksum */
-  //   emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-  //   TRY2(n->args_[0]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[1]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[2]->accept(this));
-  //   emit(")))");
-  // } else {
-  //   /* L4 checksum */
-  //   emit("(");
-  //   /* part of pseudo header */
-  //   TRY2(n->args_[3]->accept(this));
-  //   emit(" ? ");
-  //   emit("((pkt->hw_csum == 1) ? ");
-  //   /* CHECKSUM_PARTIAL update pseudo only */
-  //   emit("bpf_ntohs(bpf_pseudo_csum_replace4(bpf_htons(");
-  //   TRY2(n->args_[0]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[1]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[2]->accept(this));
-  //   emit(")))");
-  //   emit(" : ");
-  //   /* CHECKSUM_NONE update normally */
-  //   emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-  //   TRY2(n->args_[0]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[1]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[2]->accept(this));
-  //   emit(")))");
-  //   emit(")");
-  //   emit(" : ");
-  //   /* not part of pseudo */
-  //   emit("((pkt->hw_csum != 1) ? ");
-  //   /* CHECKSUM_NONE updata normally */
-  //   emit("bpf_ntohs(bpf_csum_replace4(bpf_htons(");
-  //   TRY2(n->args_[0]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[1]->accept(this));
-  //   emit("), bpf_htonl(");
-  //   TRY2(n->args_[2]->accept(this));
-  //   emit(")))");
-  //   emit(" : ");
-  //   /* CHECKSUM_PARTIAL no-op */
-  //   TRY2(n->args_[0]->accept(this));
-  //   emit("))");
-  // }
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_lb_hash(MethodCallExprNode *n) {
-  emit("pg_lb_hash(");
-  TRY2(n->args_[0]->accept(this));
-  emit(", ");
-  TRY2(n->args_[1]->accept(this));
-  emit(")");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_sizeof(MethodCallExprNode *n) {
-  if (n->args_[0]->typeof_ == ExprNode::STRUCT) {
-    if (n->args_[0]->struct_type_->id_->name_ == "_Packet") {
-      //emit("PG_SIZEOF(pkt)");
-      emit("(int)pkt->length");
-    } else {
-      emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3);
-      expr_ = B.getInt64(n->args_[0]->struct_type_->bit_width_ >> 3);
-    }
-  } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) {
-    if (n->args_[0]->struct_type_) {
-      expr_ = B.getInt64(n->args_[0]->struct_type_->bit_width_ >> 3);
-    } else {
-      emit("%zu", n->args_[0]->bit_width_ >> 3);
-      expr_ = B.getInt64(n->args_[0]->bit_width_ >> 3);
-    }
-  }
   return mkstatus(0);
 }
 
 StatusTuple CodegenLLVM::emit_get_usec_time(MethodCallExprNode *n) {
-  emit("bpf_get_usec_time()");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_forward_to_vnf(MethodCallExprNode*n) {
-  emitln("pkt->arg1 |= 1;");
-  emit("pkt->arg2 = ");
-  TRY2(n->args_[0]->accept(this));
-  emitln(";");
-  emit("bpf_forward_to_plum(pkt, ");
-  TRY2(n->args_[1]->accept(this));
-  emit(")");
-
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::emit_forward_to_group(MethodCallExprNode *n) {
-
-  emit("pkt->arg2 = ");
-  TRY2(n->args_[0]->accept(this));
-  emitln(";");
-  emitln("pkt->arg3 = pkt->plum_id;");
-  emit("bpf_forward_to_plum(pkt, ");
-  emit("1/*TUNNEL_PLUM_ID*/");
-  emit(")");
-
   return mkstatus(0);
 }
 
 StatusTuple CodegenLLVM::visit_method_call_expr_node(MethodCallExprNode *n) {
-  free_instructions_.push_back(vector<string>());
-
-  if (!n->block_->stmts_.empty()) {
-    ++indent_;
-    emitln("{");
-  }
-
   if (n->id_->sub_name_.size()) {
     if (n->id_->sub_name_ == "lookup") {
       TRY2(emit_table_lookup(n));
@@ -998,62 +741,125 @@ StatusTuple CodegenLLVM::visit_method_call_expr_node(MethodCallExprNode *n) {
       TRY2(emit_table_update(n));
     } else if (n->id_->sub_name_ == "delete") {
       TRY2(emit_table_delete(n));
-    } else if (n->id_->sub_name_ == "replicate" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_replicate(n));
-    } else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_forward(n));
-    } else if (n->id_->sub_name_ == "forward_self" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_forward_self(n));
-    } else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_push_header(n));
-    } else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_pop_header(n));
-    } else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_push_vlan(n));
-    } else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_pop_vlan(n));
-    } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_rewrite_field(n));
-    } else if (n->id_->sub_name_ == "clone_forward" && n->id_->name_ == "pkt") {
-      TRY2(emit_packet_clone_forward(n));
     }
   } else if (n->id_->name_ == "atomic_add") {
     TRY2(emit_atomic_add(n));
   } else if (n->id_->name_ == "log") {
     TRY2(emit_log(n));
-  } else if (n->id_->name_ == "cksum") {
-    TRY2(emit_cksum(n));
-  } else if (n->id_->name_ == "incr_cksum_u16") {
-    TRY2(emit_incr_cksum(n, 2));
-  } else if (n->id_->name_ == "incr_cksum_u32") {
-    TRY2(emit_incr_cksum(n, 4));
   } else if (n->id_->name_ == "incr_cksum") {
     TRY2(emit_incr_cksum(n));
-  } else if (n->id_->name_ == "lb_hash") {
-    TRY2(emit_lb_hash(n));
-  } else if (n->id_->name_ == "sizeof") {
-    TRY2(emit_sizeof(n));
   } else if (n->id_->name_ == "get_usec_time") {
     TRY2(emit_get_usec_time(n));
-  } else if (n->id_->name_ == "channel_push") {
-    TRY2(emit_channel_push(n));
-  } else if (n->id_->name_ == "channel_push_generic") {
-    TRY2(emit_channel_push_generic(n));
-  } else if (n->id_->name_ == "forward_to_vnf") {
-    TRY2(emit_forward_to_vnf(n));
-  } else if (n->id_->name_ == "forward_to_group") {
-    TRY2(emit_forward_to_group(n));
   } else {
-    TRY2(n->id_->accept(this));
-    emit("(");
-    for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
-      TRY2((*it)->accept(this));
-      if (it + 1 != n->args_.end()) {
-        emit(", ");
+    return mkstatus_(n, "unsupported");
+  }
+  TRY2(n->block_->accept(this));
+  return mkstatus(0);
+}
+
+/* result = lookup(key)
+ * if (!result) {
+ *   update(key, {0}, BPF_NOEXIST)
+ *   result = lookup(key)
+ * }
+ */
+StatusTuple CodegenLLVM::visit_table_index_expr_node(TableIndexExprNode *n) {
+  auto table_fd_it = table_fds_.find(n->table_);
+  if (table_fd_it == table_fds_.end())
+    return mkstatus_(n, "unable to find table %s in table_fds_", n->id_->c_str());
+
+  Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
+  if (!pseudo_fn) return mkstatus_(n, "pseudo fd loader doesn't exist");
+  Function *update_fn = mod_->getFunction("bpf_map_update_elem_");
+  if (!update_fn) return mkstatus_(n, "bpf_map_update_elem_ undefined");
+  Function *lookup_fn = mod_->getFunction("bpf_map_lookup_elem_");
+  if (!lookup_fn) return mkstatus_(n, "bpf_map_lookup_elem_ undefined");
+  StructType *leaf_type;
+  TRY2(lookup_struct_type(n->table_->leaf_type_, &leaf_type));
+  PointerType *leaf_ptype = PointerType::getUnqual(leaf_type);
+
+  CallInst *pseudo_call = B.CreateCall2(pseudo_fn, B.getInt64(BPF_PSEUDO_MAP_FD),
+                                        B.getInt64(table_fd_it->second));
+  Value *pseudo_map_fd = pseudo_call;
+
+  TRY2(n->index_->accept(this));
+  Value *key_ptr = B.CreateBitCast(pop_expr(), B.getInt8PtrTy());
+
+  // result = lookup(key)
+  Value *lookup1 = B.CreateBitCast(B.CreateCall2(lookup_fn, pseudo_map_fd, key_ptr), leaf_ptype);
+
+  Value *result;
+  if (n->table_->policy_id()->name_ == "AUTO") {
+    Function *parent = B.GetInsertBlock()->getParent();
+    BasicBlock *label_start = B.GetInsertBlock();
+    BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[].then", parent);
+    BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[].end", parent);
+
+    Value *eq_zero = B.CreateIsNull(lookup1);
+    B.CreateCondBr(eq_zero, label_then, label_end);
+
+    B.SetInsertPoint(label_then);
+    // var Leaf leaf {0}
+    Value *leaf_ptr = B.CreateBitCast(new AllocaInst(leaf_type, "", resolve_entry_stack()), B.getInt8PtrTy());
+    B.CreateMemSet(leaf_ptr, B.getInt8(0), B.getInt64(n->table_->leaf_id()->bit_width_ >> 3), 1);
+    // update(key, leaf)
+    B.CreateCall4(update_fn, pseudo_map_fd, key_ptr, leaf_ptr, B.getInt64(BPF_NOEXIST));
+
+    // result = lookup(key)
+    Value *lookup2 = B.CreateBitCast(B.CreateCall2(lookup_fn, pseudo_map_fd, key_ptr), leaf_ptype);
+    B.CreateBr(label_end);
+
+    B.SetInsertPoint(label_end);
+
+    PHINode *phi = B.CreatePHI(leaf_ptype, 2);
+    phi->addIncoming(lookup1, label_start);
+    phi->addIncoming(lookup2, label_then);
+    result = phi;
+  } else if (n->table_->policy_id()->name_ == "NONE") {
+    result = lookup1;
+  }
+
+  if (n->is_lhs()) {
+    if (n->sub_decl_) {
+      Type *ptr_type = PointerType::getUnqual(B.getIntNTy(n->sub_decl_->bit_width_));
+      // u64 *errval -> uN *errval
+      Value *err_cast = B.CreateBitCast(errval_, ptr_type);
+      // if valid then &field, else &errval
+      Function *parent = B.GetInsertBlock()->getParent();
+      BasicBlock *label_start = B.GetInsertBlock();
+      BasicBlock *label_then = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.then", parent);
+      BasicBlock *label_end = BasicBlock::Create(ctx(), n->id_->name_ + "[]field.end", parent);
+
+      if (1) {
+        // the PHI implementation of this doesn't load, maybe eBPF limitation?
+        B.CreateCondBr(B.CreateIsNull(result), label_then, label_end);
+        B.SetInsertPoint(label_then);
+        B.CreateStore(B.getInt32(2), retval_);
+        B.CreateBr(resolve_label("DONE"));
+
+        B.SetInsertPoint(label_end);
+        vector<Value *> indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)});
+        expr_ = B.CreateInBoundsGEP(result, indices);
+      } else {
+        B.CreateCondBr(B.CreateIsNotNull(result), label_then, label_end);
+
+        B.SetInsertPoint(label_then);
+        vector<Value *> indices({B.getInt32(0), B.getInt32(n->sub_decl_->slot_)});
+        Value *field = B.CreateInBoundsGEP(result, indices);
+        B.CreateBr(label_end);
+
+        B.SetInsertPoint(label_end);
+        PHINode *phi = B.CreatePHI(ptr_type, 2);
+        phi->addIncoming(err_cast, label_start);
+        phi->addIncoming(field, label_then);
+        expr_ = phi;
       }
+    } else {
+      return mkstatus_(n, "unsupported");
     }
+  } else {
+    expr_ = result;
   }
-  TRY2(n->block_->accept(this));
   return mkstatus(0);
 }
 
@@ -1119,18 +925,7 @@ StatusTuple CodegenLLVM::visit_miss_decl_stmt_node(MissDeclStmtNode *n) {
 }
 
 StatusTuple CodegenLLVM::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) {
-  if (n->formals_.size() != 1)
-    return mkstatus_(n, "on_failure expected 1 argument, %zu given", n->formals_.size());
-  StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get());
-  ++indent_;
-  emitln("/*if ((unsigned long)%s_element >= (unsigned long)-4095) {", n->id_->name_.c_str());
-  emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(),
-         key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str());
-  TRY2(n->block_->accept(this));
-  --indent_;
-  emitln("");
-  emit("}*/");
-  return mkstatus(0);
+  return mkstatus_(n, "unsupported");
 }
 
 StatusTuple CodegenLLVM::visit_expr_stmt_node(ExprStmtNode *n) {
@@ -1143,16 +938,27 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
   if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') {
     return mkstatus(0);
   }
+
+  StructType *stype;
+  StructDeclStmtNode *decl;
+  TRY2(lookup_struct_type(n, &stype, &decl));
+
+  Type *ptr_stype = n->is_pointer() ? PointerType::getUnqual(stype) : (PointerType *)stype;
+  AllocaInst *ptr_a = new AllocaInst(ptr_stype, "", resolve_entry_stack());
+  vars_[n] = ptr_a;
+
   if (n->struct_id_->scope_name_ == "proto") {
-    auto p = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true);
-    if (p) {
+    if (n->is_pointer()) {
+      ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(ptr_stype));
+      B.CreateStore(const_null, ptr_a);
+    } else {
       string var = n->scope_id() + n->id_->name_;
       /* zero initialize array to be filled in with packet header */
       emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;",
-           var.c_str(), ((p->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
+           var.c_str(), ((decl->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str());
       for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
         auto asn = static_cast<AssignExprNode*>(it->get());
-        if (auto f = p->field(asn->id_->sub_name_)) {
+        if (auto f = decl->field(asn->id_->sub_name_)) {
           size_t bit_offset = f->bit_offset_;
           size_t bit_width = f->bit_width_;
           if (asn->bitop_) {
@@ -1166,32 +972,20 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
       }
     }
   } else {
-    StructDeclStmtNode *decl = scopes_->top_struct()->lookup(n->struct_id_->name_);
-    if (!decl) return mkstatus_(n, "Cannot find struct %s decl", n->id_->c_str());
-
-    auto it = structs_.find(decl);
-    if (it == structs_.end()) return mkstatus_(n, "Cannot find struct %s decl", n->id_->c_str());
-    Type *stype = n->is_pointer() ? PointerType::get(it->second, 0) : (PointerType *)it->second;
-    AllocaInst *ptr_a = new AllocaInst(stype, nullptr, "", entry_bb_);
-    vars_[n] = ptr_a;
     if (n->is_pointer()) {
       if (n->id_->name_ == "_result") {
         // special case for capturing the return value of a previous method call
-        Value *cast_1 = B.CreateBitCast(pop_expr(), stype);
+        Value *cast_1 = B.CreateBitCast(pop_expr(), ptr_stype);
         B.CreateStore(cast_1, ptr_a);
       } else {
-        ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(stype));
+        ConstantPointerNull *const_null = ConstantPointerNull::get(cast<PointerType>(ptr_stype));
         B.CreateStore(const_null, ptr_a);
       }
     } else {
       B.CreateMemSet(ptr_a, B.getInt8(0), B.getInt64(decl->bit_width_ >> 3), 1);
-      emit("%s %s%s = {};", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str());
       if (!n->init_.empty()) {
-        for (auto it = n->init_.begin(); it != n->init_.end(); ++it) {
-          emit(" ");
+        for (auto it = n->init_.begin(); it != n->init_.end(); ++it)
           TRY2((*it)->accept(this));
-          emit(";");
-        }
       }
     }
   }
@@ -1201,23 +995,14 @@ StatusTuple CodegenLLVM::visit_struct_variable_decl_stmt_node(StructVariableDecl
 StatusTuple CodegenLLVM::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) {
   if (!B.GetInsertBlock())
     return mkstatus(0);
-  if (n->id_->name_ == "timer_delay")
-    return mkstatus(0);
-  emit_comment(n);
-  emit("%s %s%s", bits_to_uint(n->bit_width_), n->scope_id(), n->id_->c_str());
 
   // uintX var = init
-  AllocaInst *ptr_a = new AllocaInst(B.getIntNTy(n->bit_width_), nullptr, n->id_->name_, entry_bb_);
+  AllocaInst *ptr_a = new AllocaInst(B.getIntNTy(n->bit_width_), n->id_->name_, resolve_entry_stack());
   vars_[n] = ptr_a;
 
   // todo
-  if (!n->scope_id_.empty())
-    emit(" = 0");
-  if (!n->init_.empty()) {
-    emit("; ");
+  if (!n->init_.empty())
     TRY2(n->init_[0]->accept(this));
-  }
-  emit(";");
   return mkstatus(0);
 }
 
@@ -1241,29 +1026,19 @@ StatusTuple CodegenLLVM::visit_parser_state_stmt_node(ParserStateStmtNode *n) {
   return mkstatus(0);
 }
 
-StatusTuple CodegenLLVM::visit_timer_decl_stmt_node(TimerDeclStmtNode *n) {
-  auto scope = scopes_->current_state();
-  scopes_->set_current(n->scope_);
-  TRY2(n->block_->accept(this));
-  scopes_->set_current(scope);
-  return mkstatus(0);
-}
 StatusTuple CodegenLLVM::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
-  if (!n->id_) {
+  if (!n->id_)
     return mkstatus(0);
-  }
   string jump_label = n->scoped_name();
   BasicBlock *label_entry = resolve_label(jump_label);
   B.SetInsertPoint(label_entry);
 
   auto it = n->subs_.begin();
 
-  auto scope = scopes_->current_state();
-  scopes_->set_current(it->scope_);
+  scopes_->push_state(it->scope_);
 
-  for (auto in = n->init_.begin(); in != n->init_.end(); ++in) {
+  for (auto in = n->init_.begin(); in != n->init_.end(); ++in)
     TRY2((*in)->accept(this));
-  }
 
   if (n->subs_.size() == 1 && it->id_->name_ == "") {
     // this is not a multistate protocol, emit everything and finish
@@ -1274,45 +1049,9 @@ StatusTuple CodegenLLVM::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
     }
   } else {
     return mkstatus_(n, "unsupported");
-    if (n->parser_) {
-      for (auto it2 = n->subs_.begin(); it2 != n->subs_.end(); ++it2) {
-        proto_rewrites_[it2->id_->full_name()] = n->scoped_name() + "_" + it2->id_->name_;
-      }
-      TRY2(n->parser_->accept(this));
-      proto_rewrites_.clear();
-      emitln("");
-    }
-    for (; it != n->subs_.end(); ++it) {
-      auto scope = scopes_->current_state();
-      scopes_->set_current(it->scope_);
-
-      string jump_label = n->scoped_name() + "_" + it->id_->name_;
-      ++indent_;
-      emitln("JUMP_GUARD; %s: {", jump_label.c_str());
-      emitln("PG_TRACE(%.14s);", jump_label.c_str());
-      if (auto p = proto_scopes_->top_struct()->lookup(it->id_->name_, true)) {
-        emitln("%s = pkt->offset + parsed_bytes; /* remember the offset of this header */", it->id_->c_str());
-        emitln("parsed_bytes += %zu;", p->bit_width_ >> 3);
-        emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */");
-      }
-      TRY2(it->block_->accept(this));
-      if (it->parser_) {
-        emitln("");
-        TRY2(it->parser_->accept(this));
-      }
-      --indent_;
-      emitln("");
-      emitln("}");
-
-      scopes_->set_current(scope);
-    }
   }
 
-  scopes_->set_current(scope);
-
-  --indent_;
-  emitln("");
-  emit("}");
+  scopes_->pop_state();
   return mkstatus(0);
 }
 
@@ -1344,7 +1083,6 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
     GlobalVariable *decl_gvar = new GlobalVariable(*mod_, decl_struct, false,
                                                    GlobalValue::ExternalLinkage, 0, n->id_->name_);
     decl_gvar->setSection("maps");
-    decl_gvar->setAlignment(4);
     vector<Constant *> struct_init = { B.getInt32(map_type), B.getInt32(key->bit_width_ / 8),
                                        B.getInt32(leaf->bit_width_ / 8), B.getInt32(n->size_)};
     Constant *const_struct = ConstantStruct::get(decl_struct, struct_init);
@@ -1352,7 +1090,7 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
     tables_[n] = decl_gvar;
 
     int map_fd = bpf_create_map(map_type, key->bit_width_ / 8, leaf->bit_width_ / 8, n->size_);
-    if (map_fd >= 0 || !ENABLE_RELOCATIONS)
+    if (map_fd >= 0)
       table_fds_[n] = map_fd;
   } else {
     return mkstatus_(n, "Table %s not implemented", n->table_type_->name_.c_str());
@@ -1360,182 +1098,133 @@ StatusTuple CodegenLLVM::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
   return mkstatus(0);
 }
 
-StatusTuple CodegenLLVM::visit(Node* root) {
-  BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
+StatusTuple CodegenLLVM::lookup_struct_type(StructDeclStmtNode *decl, StructType **stype) const {
+  auto struct_it = structs_.find(decl);
+  if (struct_it == structs_.end())
+    return mkstatus_(decl, "could not find IR for type %s", decl->id_->c_str());
+  *stype = struct_it->second;
 
+  return mkstatus(0);
+}
 
-  scopes_->set_current(scopes_->top_state());
-  scopes_->set_current(scopes_->top_var());
+StatusTuple CodegenLLVM::lookup_struct_type(VariableDeclStmtNode *n, StructType **stype,
+                                            StructDeclStmtNode **decl) const {
+  if (!n->is_struct())
+    return mkstatus_(n, "attempt to search for struct with a non-struct type %s", n->id_->c_str());
 
-  TRY2(print_header());
+  auto var = (StructVariableDeclStmtNode *)n;
+  StructDeclStmtNode *type;
+  if (var->struct_id_->scope_name_ == "proto")
+    type = proto_scopes_->top_struct()->lookup(var->struct_id_->name_, true);
+  else
+    type = scopes_->top_struct()->lookup(var->struct_id_->name_, true);
 
-  TRY2(b->ver_.accept(this));
+  if (!type) return mkstatus_(n, "could not find type %s", var->struct_id_->c_str());
 
-  for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) {
-    TRY2((*it)->accept(this));
-    emit("\n");
-  }
+  TRY2(lookup_struct_type(type, stype));
 
-  TRY2(print_parser());
+  if (decl)
+    *decl = type;
 
   return mkstatus(0);
 }
 
-StatusTuple CodegenLLVM::print_timer() {
-  // visit timers
-  ++indent_;
-  emitln("PG_PARSE_DECL(timer) {");
-  emitln("uint32_t timer_delay = 0;");
-  // visit function scoped variables
-  for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) {
-    TRY2((*it)->accept(this));
-    emitln("");
-  }
-  for (auto it = scopes_->top_timer()->obegin(); it != scopes_->top_timer()->oend(); ++it) {
-    TRY2((*it)->accept(this));
-    emitln("");
-  }
-  ++indent_;
-  emitln("DONE: {");
-  emitln("PG_TRACE(DONE);");
-  emitln("pg_timer_forward(pkt, timer_delay);");
-  --indent_;
-  emitln("return;");
-  emitln("}");
-
-  ++indent_;
-  emitln("ERROR: {");
-  emitln("PG_TRACE(ERROR);");
-  emitln("pg_drop(pkt);");
-  emitln("pg_timer_forward(pkt, timer_delay);");
-  --indent_;
-  emitln("return;");
-  --indent_;
-  emitln("}");
-  emitln("}");
-  return mkstatus(0);
-}
-
-StatusTuple CodegenLLVM::print_parser() {
-  auto skbuff_decl = scopes_->top_struct()->lookup("_skbuff");
-  if (!skbuff_decl) return mkstatus(-1, "could not find built-in struct decl _skbuff");
-  auto struct_it = structs_.find(skbuff_decl);
-  if (struct_it == structs_.end()) return mkstatus(-1, "could not find built-in type _skbuff");
-
-  // int parse(struct sk_buff *skb)
-  FunctionType *parse_fn_type = FunctionType::get(B.getInt32Ty(),
-                                                  vector<Type *>({PointerType::get(struct_it->second, 0)}),
-                                                  /*isVarArg=*/false);
-
-  Function *prog = mod_->getFunction("main");
-  if (!prog) {
-    prog = Function::Create(parse_fn_type, GlobalValue::ExternalLinkage, "main", mod_);
-    if (section_.empty()) return mkstatus(-1, "Empty section pragma");
-    prog->setSection(section_);
+StatusTuple CodegenLLVM::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
+  if (n->formals_.size() != 1)
+    return mkstatus_(n, "Functions must have exactly 1 argument, %zd given", n->formals_.size());
+
+  vector<Type *> formals;
+  for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
+    VariableDeclStmtNode *formal = it->get();
+    if (formal->is_struct()) {
+      StructType *stype;
+      TRY2(lookup_struct_type(formal, &stype));
+      formals.push_back(PointerType::getUnqual(stype));
+    } else {
+      formals.push_back(B.getIntNTy(formal->bit_width_));
+    }
   }
+  FunctionType *fn_type = FunctionType::get(B.getInt32Ty(), formals, /*isVarArg=*/false);
 
-  entry_bb_ = BasicBlock::Create(ctx(), "entry", prog);
-  labels_["entry"] = entry_bb_;
-
-  B.SetInsertPoint(entry_bb_);
-
-  auto args = prog->arg_begin();
-  Value *skb_arg = args++;
-  skb_arg->setName("skb");
-  auto skb = scopes_->top_var()->lookup("skb", true);
-  if (!skb) return mkstatus(-1, "unable to find declaration of built-in skb");
-  Type *stype = PointerType::get(struct_it->second, 0);
-  AllocaInst *ptr_skb = new AllocaInst(stype, nullptr, "skb", entry_bb_);
-  ptr_skb->setAlignment(4);
-  B.CreateStore(skb_arg, ptr_skb);
-
-  retval_ = new AllocaInst(B.getInt32Ty(), nullptr, "ret", entry_bb_);
-  B.CreateStore(B.getInt32(0), retval_);
-
-  vars_[skb] = ptr_skb;
+  Function *fn = mod_->getFunction(n->id_->name_);
+  if (fn) return mkstatus_(n, "Function %s already defined", n->id_->c_str());
+  fn = Function::Create(fn_type, GlobalValue::ExternalLinkage, n->id_->name_, mod_);
+  fn->setSection("." + n->id_->name_);
 
+  BasicBlock *label_entry = BasicBlock::Create(ctx(), "entry", fn);
+  B.SetInsertPoint(label_entry);
+  string scoped_entry_label = std::to_string((uintptr_t)fn) + "::entry";
+  labels_[scoped_entry_label] = label_entry;
   BasicBlock *label_return = resolve_label("DONE");
-
-  ++indent_;
-  emitln("PG_PARSE_DECL(parse) {");
-  /* emitln("uint8_t *pp;"); */
-  emitln("uint32_t parsed_bytes = 0;");
-  //emitln("uint16_t orig_offset = 0;/*pkt->offset;*/");
+  retval_ = new AllocaInst(fn->getReturnType(), "ret", label_entry);
+  B.CreateStore(B.getInt32(0), retval_);
+  errval_ = new AllocaInst(B.getInt64Ty(), "err", label_entry);
+  B.CreateStore(B.getInt64(0), errval_);
+
+  auto formal = n->formals_.begin();
+  for (auto arg = fn->arg_begin(); arg != fn->arg_end(); ++arg, ++formal) {
+    TRY2((*formal)->accept(this));
+    Value *ptr = vars_[formal->get()];
+    if (!ptr) return mkstatus_(n, "cannot locate memory location for arg %s", (*formal)->id_->c_str());
+    B.CreateStore(arg, ptr);
+
+    // Type *ptype;
+    // if ((*formal)->is_struct()) {
+    //   StructType *type;
+    //   TRY2(lookup_struct_type(formal->get(), &type));
+    //   ptype = PointerType::getUnqual(type);
+    // } else {
+    //   ptype = PointerType::getUnqual(B.getIntNTy((*formal)->bit_width_));
+    // }
+
+    // arg->setName((*formal)->id_->name_);
+    // AllocaInst *ptr = new AllocaInst(ptype, nullptr, (*formal)->id_->name_, label_entry);
+    // B.CreateStore(arg, ptr);
+    // vars_[formal->get()] = ptr;
+  }
 
   // visit function scoped variables
   {
-    BlockStack bstack(this, entry_bb_);
-    B.SetInsertPoint(entry_bb_);
+    scopes_->push_state(n->scope_);
+
     for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it)
       TRY2((*it)->accept(this));
-  }
 
-  for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
-    if (proto_scopes_->top_struct()->lookup((*it)->id_->name_, true)) {
-      emitln("uint32_t %s = 0; /* header offset */", (*it)->id_->c_str());
-    }
-  }
+    TRY2(n->block_->accept(this));
 
-  /* emitln("pp = pkt->start + pkt->offset;"); */
-  emitln("goto s1_INIT;");
+    scopes_->pop_state();
+    if (!B.GetInsertBlock()->getTerminator())
+      B.CreateBr(resolve_label("DONE"));
 
-  // finally, visit the states
-  for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) {
-    emitln("");
-    TRY2((*it)->accept(this));
+    // always return something
+    B.SetInsertPoint(label_return);
+    B.CreateRet(B.CreateLoad(retval_));
   }
 
-  B.SetInsertPoint(entry_bb_);
-  B.CreateBr(resolve_label("s1_INIT"));
-
-  B.SetInsertPoint(label_return);
-  expr_ = B.CreateLoad(retval_);
-  B.CreateRet(pop_expr());
+  return mkstatus(0);
+}
 
-  ++indent_;
-  emitln("ERROR: {");
-  emitln("PG_TRACE(ERROR);");
-  --indent_;
-  emitln("goto CLEANUP;");
-  emitln("}");
+StatusTuple CodegenLLVM::visit(Node* root) {
+  scopes_->set_current(scopes_->top_state());
+  scopes_->set_current(scopes_->top_var());
 
-  ++indent_;
-  emitln("DONE: {");
-  emitln("PG_TRACE(DONE);");
-  --indent_;
-  emitln("goto CLEANUP;");
-  emitln("}");
+  TRY2(print_header());
 
-  ++indent_;
-  emitln("CLEANUP: {");
-  --indent_;
-  emitln("/* cleanup is done by PE */;");
-  --indent_;
-  emitln("}");
+  for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it)
+    TRY2((*it)->accept(this));
 
-  emitln("}");
+  for (auto it = scopes_->top_func()->obegin(); it != scopes_->top_func()->oend(); ++it)
+    TRY2((*it)->accept(this));
+  //TRY2(print_parser());
 
-  //print_timer();
   return mkstatus(0);
 }
 
 StatusTuple CodegenLLVM::print_header() {
-  if (use_pre_header_) {
-    //emit("%s", PRE_HEADER.c_str());
-    emitln("");
-  } else {
-    emitln("#include <stdint.h>");
-    emitln("#include \"../dp/linux/filter.h\"");
-    emitln("#include \"container/pg_api.h\"");
-    emitln("#include \"container/pg_defs.h\"");
-  }
-  emitln("#define JUMP_GUARD goto DONE");
-  emitln("#define PG_SIZEOF(_pkt) ((int)_pkt->length - (int)_pkt->offset + 0/*orig_offset*/)");
 
   GlobalVariable *gvar_license = new GlobalVariable(*mod_, ArrayType::get(Type::getInt8Ty(ctx()), 4),
                                                     false, GlobalValue::ExternalLinkage, 0, "_license");
   gvar_license->setSection("license");
-  gvar_license->setAlignment(1);
   gvar_license->setInitializer(ConstantDataArray::getString(ctx(), "GPL", true));
 
   Function *pseudo_fn = mod_->getFunction("llvm.bpf.pseudo");
@@ -1545,16 +1234,17 @@ StatusTuple CodegenLLVM::print_header() {
         GlobalValue::ExternalLinkage, "llvm.bpf.pseudo", mod_);
   }
 
-  int i = 0;
   // declare structures
   for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) {
     if ((*it)->id_->name_ == "_Packet")
       continue;
     TRY2((*it)->accept(this));
-    emit(";\n");
-    emitln("#define STRUCTID_%s %d", (*it)->id_->c_str(), i++);
   }
-  emitln("#define STRUCTID_generic %d", i);
+  for (auto it = proto_scopes_->top_struct()->obegin(); it != proto_scopes_->top_struct()->oend(); ++it) {
+    if ((*it)->id_->name_ == "_Packet")
+      continue;
+    TRY2((*it)->accept(this));
+  }
   return mkstatus(0);
 }
 
@@ -1585,13 +1275,19 @@ Value * CodegenLLVM::pop_expr() {
 }
 
 BasicBlock * CodegenLLVM::resolve_label(const string &label) {
-  auto it = labels_.find(label);
-  if (it != labels_.end()) return it->second;
   Function *parent = B.GetInsertBlock()->getParent();
+  string scoped_label = std::to_string((uintptr_t)parent) + "::" + label;
+  auto it = labels_.find(scoped_label);
+  if (it != labels_.end()) return it->second;
   BasicBlock *label_new = BasicBlock::Create(ctx(), label, parent);
-  labels_[label] = label_new;
+  labels_[scoped_label] = label_new;
   return label_new;
 }
 
+Instruction * CodegenLLVM::resolve_entry_stack() {
+  BasicBlock *label_entry = resolve_label("entry");
+  return &label_entry->back();
+}
+
 }  // namespace cc
 }  // namespace ebpf
similarity index 90%
rename from jit/src/cc/codegen_llvm.h
rename to src/cc/codegen_llvm.h
index 945c5d4..23df35d 100644 (file)
@@ -53,8 +53,7 @@ class CodegenLLVM : public Visitor {
   friend class BlockStack;
   friend class SwitchStack;
  public:
-  CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes,
-              bool use_pre_header, const std::string &section);
+  CodegenLLVM(llvm::Module *mod, Scopes *scopes, Scopes *proto_scopes);
   virtual ~CodegenLLVM();
 
 #define VISIT(type, func) virtual STATUS_RETURN visit_##func(type* n);
@@ -94,25 +93,21 @@ class CodegenLLVM : public Visitor {
   STATUS_RETURN emit_get_usec_time(MethodCallExprNode* n);
   STATUS_RETURN emit_forward_to_vnf(MethodCallExprNode* n);
   STATUS_RETURN emit_forward_to_group(MethodCallExprNode* n);
-  STATUS_RETURN print_parser();
-  STATUS_RETURN print_timer();
   STATUS_RETURN print_header();
 
-  void indent();
   llvm::LLVMContext & ctx() const;
   llvm::Constant * const_int(uint64_t val, unsigned bits = 64, bool is_signed = false);
   llvm::Value * pop_expr();
   llvm::BasicBlock * resolve_label(const string &label);
+  llvm::Instruction * resolve_entry_stack();
   StatusTuple lookup_var(Node *n, const std::string &name, Scopes::VarScope *scope,
                          VariableDeclStmtNode **decl, llvm::Value **mem) const;
+  StatusTuple lookup_struct_type(StructDeclStmtNode *decl, llvm::StructType **stype) const;
+  StatusTuple lookup_struct_type(VariableDeclStmtNode *n, llvm::StructType **stype,
+                                 StructDeclStmtNode **decl = nullptr) const;
 
-  template <typename... Args> void emitln(const char *fmt, Args&&... params);
-  template <typename... Args> void lnemit(const char *fmt, Args&&... params);
   template <typename... Args> void emit(const char *fmt, Args&&... params);
-  void emitln(const char *s);
-  void lnemit(const char *s);
   void emit(const char *s);
-  void emit_comment(Node* n);
 
   FILE* out_;
   llvm::Module* mod_;
@@ -121,8 +116,6 @@ class CodegenLLVM : public Visitor {
   int tmp_reg_index_;
   Scopes *scopes_;
   Scopes *proto_scopes_;
-  bool use_pre_header_;
-  std::string section_;
   vector<vector<string> > free_instructions_;
   vector<string> table_inits_;
   map<string, string> proto_rewrites_;
@@ -131,10 +124,10 @@ class CodegenLLVM : public Visitor {
   map<VariableDeclStmtNode *, llvm::Value *> vars_;
   map<StructDeclStmtNode *, llvm::StructType *> structs_;
   map<string, llvm::BasicBlock *> labels_;
-  llvm::BasicBlock *entry_bb_;
   llvm::SwitchInst *cur_switch_;
   llvm::Value *expr_;
   llvm::AllocaInst *retval_;
+  llvm::AllocaInst *errval_;
 };
 
 }  // namespace cc
similarity index 100%
rename from jit/src/cc/exception.h
rename to src/cc/exception.h
similarity index 98%
rename from jit/src/cc/lexer.h
rename to src/cc/lexer.h
index 6eda36e..eb8267c 100644 (file)
@@ -82,7 +82,8 @@ class Lexer : public yyFlexLexer {
     case Tok::TRBRACK:
     case Tok::TTRUE:
     case Tok::TFALSE:
-      return true;
+      // uncomment to add implicit semicolons
+      //return true;
     default:
       break;
     }
similarity index 87%
rename from jit/src/cc/lexer.ll
rename to src/cc/lexer.ll
index 9424787..d8b5fd6 100644 (file)
@@ -31,14 +31,14 @@ std::string tmp_str_cc;
 %x STRING_
 %%
 
-\'                      {BEGIN STRING_;}
-<STRING_>\'             { BEGIN 0;
+\"                      {BEGIN STRING_;}
+<STRING_>\"             { BEGIN 0;
                         yylval_->string = new std::string(tmp_str_cc);
                         tmp_str_cc = "";
                         return Tok::TSTRING;
                         }
+<STRING_>\\n            {tmp_str_cc += "\n"; }
 <STRING_>.              {tmp_str_cc += *yytext; }
-<STRING_>\n              {tmp_str_cc += "\n"; }
 
 
 
@@ -59,10 +59,13 @@ std::string tmp_str_cc;
 "}"                     return save(Tok::TRBRACE);
 "["                     return save(Tok::TLBRACK);
 "]"                     return save(Tok::TRBRACK);
+"->"                    return save(Tok::TARROW);
 "."                     return save(Tok::TDOT);
 ","                     return save(Tok::TCOMMA);
 "+"                     return save(Tok::TPLUS);
+"++"                    return save(Tok::TINCR);
 "-"                     return save(Tok::TMINUS);
+"--"                    return save(Tok::TDECR);
 "*"                     return save(Tok::TMUL);
 "/"                     return save(Tok::TDIV);
 "%"                     return save(Tok::TMOD);
@@ -79,30 +82,31 @@ std::string tmp_str_cc;
 "|"                     return save(Tok::TLOR);
 "@"                     return save(Tok::TAT);
 
-"const"                 return save(Tok::TCONST);
-"struct"                return save(Tok::TSTRUCT);
-"var"                   return save(Tok::TVAR);
-"state"                 return save(Tok::TSTATE);
-"timer"                 return save(Tok::TTIMER);
-"goto"                  return save(Tok::TGOTO);
+"case"                  return save(Tok::TCASE);
 "continue"              return save(Tok::TCONTINUE);
+"else"                  return save(Tok::TELSE);
+"false"                 return save(Tok::TFALSE);
+"goto"                  return save(Tok::TGOTO);
+"if"                    return save(Tok::TIF);
 "next"                  return save(Tok::TNEXT);
 "on_match"              return save(Tok::TMATCH);
 "on_miss"               return save(Tok::TMISS);
 "on_failure"            return save(Tok::TFAILURE);
 "on_valid"              return save(Tok::TVALID);
-"true"                  return save(Tok::TTRUE);
-"false"                 return save(Tok::TFALSE);
-"if"                    return save(Tok::TIF);
-"else"                  return save(Tok::TELSE);
-"switch"                return save(Tok::TSWITCH);
-"case"                  return save(Tok::TCASE);
 "return"                return save(Tok::TRETURN);
+"state"                 return save(Tok::TSTATE);
+"struct"                return save(Tok::TSTRUCT);
+"switch"                return save(Tok::TSWITCH);
+"true"                  return save(Tok::TTRUE);
+"u8"                    return save(Tok::TU8);
+"u16"                   return save(Tok::TU16);
+"u32"                   return save(Tok::TU32);
+"u64"                   return save(Tok::TU64);
 
 [a-zA-Z][a-zA-Z0-9_]*   return save(Tok::TIDENTIFIER);
 [0-9]+                  return save(Tok::TINTEGER);
 0x[0-9a-fA-F]+          return save(Tok::THEXINTEGER);
 
-.                       printf("Unknown token\n"); yyterminate();
+.                       printf("Unknown token \"%s\"\n", yytext); yyterminate();
 
 %%
similarity index 63%
rename from jit/src/cc/libbpf.c
rename to src/cc/libbpf.c
index da76131..4816950 100644 (file)
@@ -1,19 +1,21 @@
 /* eBPF mini library */
-#include <stdlib.h>
-#include <stdio.h>
-#include <linux/unistd.h>
-#include <unistd.h>
-#include <string.h>
-#include <linux/netlink.h>
-#include <linux/rtnetlink.h>
-#include <linux/bpf.h>
+#include <arpa/inet.h>
 #include <errno.h>
+#include <fcntl.h>
+#include <libmnl/libmnl.h>
+#include <linux/bpf.h>
+#include <linux/if_packet.h>
+#include <linux/pkt_cls.h>
+#include <linux/perf_event.h>
+#include <linux/rtnetlink.h>
+#include <linux/unistd.h>
+#include <linux/version.h>
 #include <net/ethernet.h>
 #include <net/if.h>
-#include <linux/if_packet.h>
-#include <linux/pkt_sched.h>
-#include <arpa/inet.h>
-#include <libmnl/libmnl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+
 #include "libbpf.h"
 
 static __u64 ptr_to_u64(void *ptr)
@@ -82,8 +84,8 @@ int bpf_get_next_key(int fd, void *key, void *next_key)
 char bpf_log_buf[LOG_BUF_SIZE];
 
 int bpf_prog_load(enum bpf_prog_type prog_type,
-    const struct bpf_insn *insns, int prog_len,
-    const char *license)
+                  const struct bpf_insn *insns, int prog_len,
+                  const char *license)
 {
   union bpf_attr attr = {
     .prog_type = prog_type,
@@ -95,9 +97,14 @@ int bpf_prog_load(enum bpf_prog_type prog_type,
     .log_level = 1,
   };
 
+  attr.kern_version = LINUX_VERSION_CODE;
   bpf_log_buf[0] = 0;
 
-  return syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+  int ret = syscall(__NR_bpf, BPF_PROG_LOAD, &attr, sizeof(attr));
+  if (ret < 0) {
+    fprintf(stderr, "bpf: %s\n%s\n", strerror(errno), bpf_log_buf);
+  }
+  return ret;
 }
 
 int bpf_open_raw_sock(const char *name)
@@ -144,8 +151,8 @@ static int cb(const struct nlmsghdr *nlh, void *data) {
   }
 }
 
-int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex, uint8_t prio, uint32_t classid)
-{
+int bpf_attach_filter(int progfd, const char *prog_name,
+                      uint32_t ifindex, uint8_t prio, uint32_t classid) {
   int rc = -1;
   char buf[1024];
   struct nlmsghdr *nlh;
@@ -169,9 +176,9 @@ int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex, uint8
   tc->tcm_ifindex = ifindex;
   mnl_attr_put_strz(nlh, TCA_KIND, "bpf");
   opt = mnl_attr_nest_start(nlh, TCA_OPTIONS);
-  mnl_attr_put_u32(nlh, 6 /*TCA_BPF_FD*/, progfd);
-  mnl_attr_put_strz(nlh, 7 /*TCP_BPF_NAME*/, prog_name);
-  mnl_attr_put_u32(nlh, 3 /*TCA_BPF_CLASSID*/, classid);
+  mnl_attr_put_u32(nlh, TCA_BPF_FD, progfd);
+  mnl_attr_put_strz(nlh, TCA_BPF_NAME, prog_name);
+  mnl_attr_put_u32(nlh, TCA_BPF_CLASSID, classid);
   mnl_attr_nest_end(nlh, opt);
 
   nl = mnl_socket_open(NETLINK_ROUTE);
@@ -209,3 +216,82 @@ cleanup:
       perror("mnl_socket_close");
   return rc;
 }
+
+static int bpf_attach_tracing_event(int progfd, const char *event_path, pid_t pid, int cpu, int group_fd)
+{
+  int efd = -1, rc = -1, pfd = -1;
+  ssize_t bytes = -1;
+  char buf[256];
+  struct perf_event_attr attr = {};
+
+  snprintf(buf, sizeof(buf), "%s/id", event_path);
+  efd = open(buf, O_RDONLY, 0);
+  if (efd < 0) {
+    fprintf(stderr, "open(%s): %s\n", buf, strerror(errno));
+    goto cleanup;
+  }
+
+  bytes = read(efd, buf, sizeof(buf));
+  if (bytes <= 0 || bytes >= sizeof(buf)) {
+    fprintf(stderr, "read(%s): %s\n", buf, strerror(errno));
+    goto cleanup;
+  }
+  buf[bytes] = '\0';
+  attr.config = strtol(buf, NULL, 0);
+  attr.type = PERF_TYPE_TRACEPOINT;
+  attr.sample_type = PERF_SAMPLE_RAW;
+  attr.sample_period = 1;
+  attr.wakeup_events = 1;
+  pfd = syscall(__NR_perf_event_open, &attr, pid, cpu, group_fd, PERF_FLAG_FD_CLOEXEC);
+  if (pfd < 0) {
+    perror("perf_event_open");
+    goto cleanup;
+  }
+  if (ioctl(pfd, PERF_EVENT_IOC_SET_BPF, progfd) < 0) {
+    perror("ioctl(PERF_EVENT_IOC_SET_BPF)");
+    goto cleanup;
+  }
+  if (ioctl(pfd, PERF_EVENT_IOC_ENABLE, 0) < 0) {
+    perror("ioctl(PERF_EVENT_IOC_ENABLE)");
+    goto cleanup;
+  }
+
+  rc = pfd;
+  pfd = -1;
+
+cleanup:
+  if (efd >= 0)
+    close(efd);
+  if (pfd >= 0)
+    close(pfd);
+
+  return rc;
+}
+
+int bpf_attach_kprobe(int progfd, const char *event,
+                      const char *event_desc, pid_t pid,
+                      int cpu, int group_fd) {
+  int rc = -1, kfd = -1;
+  char buf[256];
+
+  kfd = open("/sys/kernel/debug/tracing/kprobe_events", O_WRONLY | O_APPEND, 0);
+  if (kfd < 0) {
+    perror("open(kprobe_events)");
+    goto cleanup;
+  }
+
+  if (write(kfd, event_desc, strlen(event_desc)) < 0) {
+    perror("write(kprobe_events)");
+    goto cleanup;
+  }
+
+  snprintf(buf, sizeof(buf), "/sys/kernel/debug/tracing/events/kprobes/%s", event);
+  rc = bpf_attach_tracing_event(progfd, buf, -1/*pid*/, 0/*cpu*/, -1/*group_fd*/);
+
+cleanup:
+  if (kfd >= 0)
+    close(kfd);
+
+  return rc;
+}
+
similarity index 100%
rename from jit/src/cc/node.cc
rename to src/cc/node.cc
similarity index 92%
rename from jit/src/cc/node.h
rename to src/cc/node.h
index e5821fd..50b4267 100644 (file)
@@ -67,12 +67,14 @@ typedef unique_ptr<string> String;
   EXPAND(AssignExprNode, assign_expr_node) \
   EXPAND(PacketExprNode, packet_expr_node) \
   EXPAND(IntegerExprNode, integer_expr_node) \
+  EXPAND(StringExprNode, string_expr_node) \
   EXPAND(BinopExprNode, binop_expr_node) \
   EXPAND(UnopExprNode, unop_expr_node) \
   EXPAND(BitopExprNode, bitop_expr_node) \
   EXPAND(GotoExprNode, goto_expr_node) \
   EXPAND(ReturnExprNode, return_expr_node) \
-  EXPAND(MethodCallExprNode, method_call_expr_node)
+  EXPAND(MethodCallExprNode, method_call_expr_node) \
+  EXPAND(TableIndexExprNode, table_index_expr_node)
 
 #define NODE_STATEMENTS(EXPAND) \
   EXPAND(ExprStmtNode, expr_stmt_node) \
@@ -85,13 +87,12 @@ typedef unique_ptr<string> String;
   EXPAND(IntegerVariableDeclStmtNode, integer_variable_decl_stmt_node) \
   EXPAND(StructDeclStmtNode, struct_decl_stmt_node) \
   EXPAND(StateDeclStmtNode, state_decl_stmt_node) \
-  EXPAND(TimerDeclStmtNode, timer_decl_stmt_node) \
   EXPAND(ParserStateStmtNode, parser_state_stmt_node) \
   EXPAND(MatchDeclStmtNode, match_decl_stmt_node) \
   EXPAND(MissDeclStmtNode, miss_decl_stmt_node) \
   EXPAND(FailureDeclStmtNode, failure_decl_stmt_node) \
   EXPAND(TableDeclStmtNode, table_decl_stmt_node) \
-  EXPAND(VersionStmtNode, version_stmt_node)
+  EXPAND(FuncDeclStmtNode, func_decl_stmt_node)
 
 #define EXPAND_NODES(EXPAND) \
   NODE_EXPRESSIONS(EXPAND) \
@@ -148,7 +149,7 @@ class ExprNode : public Node {
  public:
   typedef unique_ptr<ExprNode> Ptr;
   virtual StatusTuple accept(Visitor* v) = 0;
-  enum expr_type { STRUCT, INTEGER, VOID, UNKNOWN };
+  enum expr_type { STRUCT, INTEGER, STRING, VOID, UNKNOWN };
   enum prop_flag { READ = 0, WRITE, PROTO, IS_LHS, IS_REF, LAST };
   expr_type typeof_;
   StructDeclStmtNode *struct_type_;
@@ -259,6 +260,17 @@ class PacketExprNode : public ExprNode {
   explicit PacketExprNode(IdentExprNode::Ptr id) : id_(move(id)) {}
 };
 
+class StringExprNode : public ExprNode {
+ public:
+  DECLARE(StringExprNode)
+
+  string val_;
+  explicit StringExprNode(string *val) : val_(move(*val)) {
+    delete val;
+  }
+  explicit StringExprNode(const string &val) : val_(val) {}
+};
+
 class IntegerExprNode : public ExprNode {
  public:
   DECLARE(IntegerExprNode)
@@ -318,25 +330,15 @@ class ReturnExprNode : public ExprNode {
       : expr_(move(expr)) {}
 };
 
-class VersionStmtNode : public StmtNode {
-  public:
-    DECLARE(VersionStmtNode)
-
-    VersionStmtNode(const int major, const int minor, const int rev)
-      : major_(major), minor_(minor), rev_(rev) {}
-    uint32_t major_, minor_, rev_;
-};
-
 class BlockStmtNode : public StmtNode {
  public:
   DECLARE(BlockStmtNode)
 
   explicit BlockStmtNode(StmtNodeList stmts = StmtNodeList())
-    : stmts_(move(stmts)), scope_(NULL), ver_(0, 0, 0) {}
+    : stmts_(move(stmts)), scope_(NULL) {}
   ~BlockStmtNode() { delete scope_; }
   StmtNodeList stmts_;
   Scopes::VarScope* scope_;
-  VersionStmtNode ver_;
 };
 
 class MethodCallExprNode : public ExprNode {
@@ -352,6 +354,20 @@ class MethodCallExprNode : public ExprNode {
   }
 };
 
+class TableIndexExprNode : public ExprNode {
+ public:
+  DECLARE(TableIndexExprNode)
+
+  IdentExprNode::Ptr id_;
+  IdentExprNode::Ptr sub_;
+  ExprNode::Ptr index_;
+  TableDeclStmtNode *table_;
+  VariableDeclStmtNode *sub_decl_;
+  TableIndexExprNode(IdentExprNode::Ptr id, ExprNode::Ptr index)
+      : id_(move(id)), index_(move(index)), table_(nullptr), sub_decl_(nullptr)
+  {}
+};
+
 class ExprStmtNode : public StmtNode {
  public:
   DECLARE(ExprStmtNode)
@@ -532,16 +548,6 @@ class StateDeclStmtNode : public StmtNode {
   }
 };
 
-class TimerDeclStmtNode : public StateDeclStmtNode {
- public:
-  DECLARE(TimerDeclStmtNode)
-
-  BlockStmtNode::Ptr block_;
-  Scopes::StateScope* scope_;
-  explicit TimerDeclStmtNode(BlockStmtNode::Ptr block): block_(move(block)) {
-  }
-};
-
 class MatchDeclStmtNode : public StmtNode {
  public:
   DECLARE(MatchDeclStmtNode)
@@ -582,6 +588,8 @@ class TableDeclStmtNode : public StmtNode {
   IdentExprNode::Ptr table_type_;
   IdentExprNodeList templates_;
   IdentExprNode::Ptr id_;
+  StructDeclStmtNode *key_type_;
+  StructDeclStmtNode *leaf_type_;
   IdentExprNode * key_id() { return templates_.at(0).get(); }
   IdentExprNode * leaf_id() { return templates_.at(1).get(); }
   IdentExprNode * type_id() { return templates_.at(2).get(); }
@@ -590,11 +598,23 @@ class TableDeclStmtNode : public StmtNode {
   TableDeclStmtNode(IdentExprNode::Ptr table_type, IdentExprNodeList&& templates,
                     IdentExprNode::Ptr id, string* size)
       : table_type_(move(table_type)), templates_(move(templates)), id_(move(id)),
-      size_(strtoul(size->c_str(), NULL, 0)) {
+      key_type_(nullptr), leaf_type_(nullptr), size_(strtoul(size->c_str(), NULL, 0)) {
     delete size;
   }
 };
 
+class FuncDeclStmtNode : public StmtNode {
+ public:
+  DECLARE(FuncDeclStmtNode)
+
+  IdentExprNode::Ptr id_;
+  FormalList formals_;
+  BlockStmtNode::Ptr block_;
+  Scopes::StateScope* scope_;
+  FuncDeclStmtNode(IdentExprNode::Ptr id, FormalList&& formals, BlockStmtNode::Ptr block)
+      : id_(move(id)), formals_(move(formals)), block_(move(block)), scope_(NULL) {}
+};
+
 class Visitor {
  public:
   typedef StatusTuple Ret;
similarity index 71%
rename from jit/src/cc/parser.cc
rename to src/cc/parser.cc
index 9f202c8..0736f02 100644 (file)
@@ -30,47 +30,43 @@ using std::move;
 using std::string;
 using std::unique_ptr;
 
-bool Parser::variable_exists(VariableDeclStmtNode* decl, bool search_local) {
-  if (scopes_->current_var()->lookup(decl->id_->name_, search_local) == NULL) {
+bool Parser::variable_exists(VariableDeclStmtNode *decl) const {
+  if (scopes_->current_var()->lookup(decl->id_->name_, SCOPE_LOCAL) == NULL) {
     return false;
   }
   return true;
 }
 
-VariableDeclStmtNode* Parser::variable_add(VariableDeclStmtNode* decl) {
+VariableDeclStmtNode *Parser::variable_add(vector<int> *types, VariableDeclStmtNode *decl) {
 
-  if (variable_exists(decl, true)) {
+  if (variable_exists(decl)) {
     fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str());
-    assert(0 && "redeclaration of variable");
-    // how to raise a bison error from here?
-    return decl;
+    return nullptr;
   }
   decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
   scopes_->current_var()->add(decl->id_->name_, decl);
   return decl;
 }
 
-VariableDeclStmtNode* Parser::variable_add(VariableDeclStmtNode* decl, ExprNode* init_expr) {
+VariableDeclStmtNode *Parser::variable_add(vector<int> *types, VariableDeclStmtNode *decl, ExprNode *init_expr) {
   AssignExprNode::Ptr assign(new AssignExprNode(decl->id_->copy(), ExprNode::Ptr(init_expr)));
   decl->init_.push_back(move(assign));
 
-  if (variable_exists(decl, true)) {
+  if (variable_exists(decl)) {
     fprintf(stderr, "redeclaration of variable %s", decl->id_->name_.c_str());
-    assert(0 && "redeclaration of variable");
-    // how to raise a bison error from here?
-    return decl;
+    return nullptr;
   }
   decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
   scopes_->current_var()->add(decl->id_->name_, decl);
   return decl;
 }
 
-StructVariableDeclStmtNode* Parser::variable_add(StructVariableDeclStmtNode* decl, ExprNodeList* args, bool is_kv) {
+StructVariableDeclStmtNode *Parser::variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv) {
   if (is_kv) {
     // annotate the init expressions with the declared id
     for (auto arg = args->begin(); arg != args->end(); ++arg) {
       // decorate with the name of this decl
-      auto n = static_cast<AssignExprNode*>(arg->get());
+      auto n = static_cast<AssignExprNode *>(arg->get());
       n->id_->prepend_dot(decl->id_->name_);
     }
   } else {
@@ -81,32 +77,17 @@ StructVariableDeclStmtNode* Parser::variable_add(StructVariableDeclStmtNode* dec
   decl->init_ = move(*args);
   delete args;
 
-  if (variable_exists(decl, true)) {
+  if (variable_exists(decl)) {
     fprintf(stderr, "ccpg: warning: redeclaration of variable '%s'\n", decl->id_->name_.c_str());
-    // how to raise a bison error from here?
-    return decl;
+    return nullptr;
   }
   decl->scope_id_ = string("v") + std::to_string(scopes_->current_var()->id_) + string("_");
   scopes_->current_var()->add(decl->id_->name_, decl);
   return decl;
 }
 
-StmtNode* Parser::timer_add(Scopes::StateScope* scope, BlockStmtNode* body) {
-  if (scopes_->top_timer()->lookup("timer", true)) {
-    fprintf(stderr, "redeclaration of timer. Only one timer supported per plum");
-    assert(0 && "redeclaration of timer. Only one timer supported per plum");
-  }
-  auto timer = new TimerDeclStmtNode(BlockStmtNode::Ptr(body));
-
-  timer->scope_ = scope;
-
-  scopes_->top_timer()->add("timer", timer);
-
-  return timer;
-}
-
-StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id, BlockStmtNode* body) {
-  if (scopes_->current_state()->lookup(id->full_name(), true)) {
+StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id, BlockStmtNode *body) {
+  if (scopes_->current_state()->lookup(id->full_name(), SCOPE_LOCAL)) {
     fprintf(stderr, "redeclaration of state %s\n", id->full_name().c_str());
     // redeclaration
     return NULL;
@@ -122,8 +103,8 @@ StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id, BlockS
   return state;
 }
 
-StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id1, IdentExprNode* id2, BlockStmtNode* body) {
-  auto state = scopes_->current_state()->lookup(id1->full_name(), true);
+StmtNode *Parser::state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body) {
+  auto state = scopes_->current_state()->lookup(id1->full_name(), SCOPE_LOCAL);
   if (!state) {
     state = new StateDeclStmtNode(IdentExprNode::Ptr(id1), IdentExprNode::Ptr(id2), BlockStmtNode::Ptr(body));
     // add a reference to the lower scope
@@ -146,15 +127,15 @@ StmtNode* Parser::state_add(Scopes::StateScope* scope, IdentExprNode* id1, Ident
   }
 }
 
-bool Parser::table_exists(TableDeclStmtNodedecl, bool search_local) {
+bool Parser::table_exists(TableDeclStmtNode *decl, bool search_local) {
   if (scopes_->top_table()->lookup(decl->id_->name_, search_local) == NULL) {
     return false;
   }
   return true;
 }
 
-StmtNode* Parser::table_add(IdentExprNode* type, IdentExprNodeList* templates,
-                            IdentExprNode* id, string* size) {
+StmtNode *Parser::table_add(IdentExprNode *type, IdentExprNodeList *templates,
+                            IdentExprNode *id, string *size) {
   auto table = new TableDeclStmtNode(IdentExprNode::Ptr(type),
                                      move(*templates),
                                      IdentExprNode::Ptr(id), size);
@@ -166,9 +147,9 @@ StmtNode* Parser::table_add(IdentExprNode* type, IdentExprNodeList* templates,
   return table;
 }
 
-StmtNode* Parser::struct_add(IdentExprNode* type, FormalList* formals) {
+StmtNode * Parser::struct_add(IdentExprNode *type, FormalList *formals) {
   auto struct_decl = new StructDeclStmtNode(IdentExprNode::Ptr(type), move(*formals));
-  if (scopes_->top_struct()->lookup(type->name_, true) != NULL) {
+  if (scopes_->top_struct()->lookup(type->name_, SCOPE_LOCAL) != NULL) {
     fprintf(stderr, "redeclaration of struct %s\n", type->name_.c_str());
     return struct_decl;
   }
@@ -192,11 +173,7 @@ StmtNode* Parser::struct_add(IdentExprNode* type, FormalList* formals) {
   return struct_decl;
 }
 
-StmtNode* Parser::result_add(int token, IdentExprNode* id, FormalList* formals, BlockStmtNode* body) {
-  // result arguments are pass-by-reference instead of value
-  for (auto it = formals->begin(); it != formals->end(); ++it) {
-    (*it)->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
-  }
+StmtNode * Parser::result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body) {
   StmtNode *stmt = NULL;
   switch (token) {
     case Tok::TMATCH:
@@ -214,6 +191,24 @@ StmtNode* Parser::result_add(int token, IdentExprNode* id, FormalList* formals,
   return stmt;
 }
 
+StmtNode * Parser::func_add(vector<int> *types, Scopes::StateScope *scope,
+                            IdentExprNode *id, FormalList *formals, BlockStmtNode *body) {
+  auto decl = new FuncDeclStmtNode(IdentExprNode::Ptr(id), move(*formals), BlockStmtNode::Ptr(body));
+  if (scopes_->top_func()->lookup(decl->id_->name_, SCOPE_LOCAL)) {
+    fprintf(stderr, "redeclaration of func %s\n", id->name_.c_str());
+    return decl;
+  }
+  auto cur_scope = scopes_->current_var();
+  scopes_->set_current(scope);
+  for (auto it = formals->begin(); it != formals->end(); ++it)
+    if (!variable_add(nullptr, it->get()))
+      return nullptr;
+  scopes_->set_current(cur_scope);
+  decl->scope_ = scope;
+  scopes_->top_func()->add(id->name_, decl);
+  return decl;
+}
+
 void Parser::set_loc(Node *n, const BisonParser::location_type &loc) const {
   n->line_ = loc.begin.line;
   n->column_ = loc.begin.column;
@@ -222,7 +217,7 @@ void Parser::set_loc(Node *n, const BisonParser::location_type &loc) const {
 
 string Parser::pragma(const string &name) const {
   auto it = pragmas_.find(name);
-  if (it == pragmas_.end()) return "";
+  if (it == pragmas_.end()) return "main";
   return it->second;
 }
 
similarity index 57%
rename from jit/src/cc/parser.h
rename to src/cc/parser.h
index c1606f0..bcd00d5 100644 (file)
@@ -26,8 +26,9 @@
 namespace ebpf {
 namespace cc {
 
-using std::string;
 using std::pair;
+using std::string;
+using std::vector;
 
 class Parser {
  public:
@@ -40,22 +41,23 @@ class Parser {
     return parser.parse();
   }
 
-  VariableDeclStmtNode* variable_add(VariableDeclStmtNode* decl);
-  VariableDeclStmtNode* variable_add(VariableDeclStmtNode* decl, ExprNode* init_expr);
-  StructVariableDeclStmtNode* variable_add(StructVariableDeclStmtNode* decl, ExprNodeList* args, bool is_kv);
-  StmtNode* state_add(Scopes::StateScope* scope, IdentExprNode* id1, BlockStmtNode* body);
-  StmtNode* timer_add(Scopes::StateScope* scope, BlockStmtNode* body);
-  StmtNode* state_add(Scopes::StateScope* scope, IdentExprNode* id1, IdentExprNode* id2, BlockStmtNode* body);
-  StmtNode* table_add(IdentExprNode* type, IdentExprNodeList* templates, IdentExprNode* id, string* size);
-  StmtNode* struct_add(IdentExprNode* type, FormalList* formals);
-  StmtNode* result_add(int token, IdentExprNode* id, FormalList* formals, BlockStmtNode* body);
-  bool variable_exists(VariableDeclStmtNode* decl, bool search_local = true);
-  bool table_exists(TableDeclStmtNode* decl, bool search_local = true);
+  VariableDeclStmtNode * variable_add(vector<int> *types, VariableDeclStmtNode *decl);
+  VariableDeclStmtNode * variable_add(vector<int> *types, VariableDeclStmtNode *decl, ExprNode *init_expr);
+  StructVariableDeclStmtNode * variable_add(StructVariableDeclStmtNode *decl, ExprNodeList *args, bool is_kv);
+  StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, BlockStmtNode *body);
+  StmtNode * state_add(Scopes::StateScope *scope, IdentExprNode *id1, IdentExprNode *id2, BlockStmtNode *body);
+  StmtNode * func_add(std::vector<int> *types, Scopes::StateScope *scope,
+                      IdentExprNode *id, FormalList *formals, BlockStmtNode *body);
+  StmtNode * table_add(IdentExprNode *type, IdentExprNodeList *templates, IdentExprNode *id, string *size);
+  StmtNode * struct_add(IdentExprNode *type, FormalList *formals);
+  StmtNode * result_add(int token, IdentExprNode *id, FormalList *formals, BlockStmtNode *body);
+  bool variable_exists(VariableDeclStmtNode *decl) const;
+  bool table_exists(TableDeclStmtNode *decl, bool search_local = true);
   void add_pragma(const std::string& pr, const std::string& v) { pragmas_[pr] = v; }
   void set_loc(Node *n, const BisonParser::location_type &loc) const;
   std::string pragma(const std::string &name) const;
 
-  Noderoot_node_;
+  Node *root_node_;
   Scopes::Ptr scopes_;
   std::map<std::string, std::string> pragmas_;
  private:
similarity index 85%
rename from jit/src/cc/parser.yy
rename to src/cc/parser.yy
index 5ccafbe..458082b 100644 (file)
     FormalList *formals;
     VariableDeclStmtNode *decl;
     StructVariableDeclStmtNode *type_decl;
+    TableIndexExprNode *table_index;
+    std::vector<int> *type_specifiers;
     std::string* string;
     int token;
-    VersionStmtNode *ver;
 }
 
 /* Define the terminal symbols. */
 %token <string> TIDENTIFIER TINTEGER THEXINTEGER TPRAGMA TSTRING
+%token <token> TU8 TU16 TU32 TU64
 %token <token> TEQUAL TCEQ TCNE TCLT TCLE TCGT TCGE TAND TOR
 %token <token> TLPAREN TRPAREN TLBRACE TRBRACE TLBRACK TRBRACK
-%token <token> TDOT TCOMMA TPLUS TMINUS TMUL TDIV TMOD TXOR TDOLLAR TCOLON TSCOPE TNOT TSEMI TCMPL TLAND TLOR
-%token <token> TCONST TSTRUCT TVAR TSTATE TTIMER TGOTO TCONTINUE TNEXT TTRUE TFALSE TRETURN
+%token <token> TDOT TARROW TCOMMA TPLUS TMINUS TMUL TDIV TMOD TXOR TDOLLAR TCOLON TSCOPE TNOT TSEMI TCMPL TLAND TLOR
+%token <token> TSTRUCT TSTATE TFUNC TGOTO TCONTINUE TNEXT TTRUE TFALSE TRETURN
 %token <token> TIF TELSE TSWITCH TCASE
 %token <token> TMATCH TMISS TFAILURE TVALID
 %token <token> TAT
 %type <expr> expr assign_expr return_expr init_arg_kv
 %type <numeric> numeric
 %type <bitop> bitop
-%type <args> call_args init_args init_args_kv
+%type <args> call_args /*init_args*/ init_args_kv
 %type <ident_args> table_decl_args
 %type <formals> struct_decl_stmts formals
 %type <block> program block prog_decls
 %type <decl> decl_stmt int_decl ref_stmt
 %type <type_decl> type_decl ptr_decl
-%type <stmt> stmt prog_decl var_decl struct_decl state_decl timer_decl
+%type <stmt> stmt prog_decl var_decl struct_decl state_decl func_decl
 %type <stmt> table_decl table_result_stmt if_stmt switch_stmt case_stmt onvalid_stmt
 %type <var_scope> enter_varscope exit_varscope
 %type <state_scope> enter_statescope exit_statescope
 %type <stmts> stmts table_result_stmts case_stmts
 %type <call> call_expr
+%type <table_index> table_index_expr
+%type <type_specifiers> type_specifiers
 %type <stmt> pragma_decl
-%type <ver>  version_stmt
+%type <token> type_specifier
 
 /* taken from C++ operator precedence wiki page */
 %nonassoc TSCOPE
-%left TDOT TLBRACK TLBRACE TLPAREN
+%left TDOT TLBRACK TLBRACE TLPAREN TINCR TDECR
 %right TNOT TCMPL
 %left TMUL
 %left TDIV
 %%
 
 program
-  : enter_statescope enter_varscope version_stmt prog_decls exit_varscope exit_statescope
-    { parser.root_node_ = $4; $4->scope_ = $2; $4->ver_ = (*$3); delete $3;}
+  : enter_statescope enter_varscope prog_decls exit_varscope exit_statescope
+    { parser.root_node_ = $3; $3->scope_ = $2; }
   ;
 
-version_stmt
-  :     TAT TINTEGER TDOT TINTEGER TDOT TINTEGER TSEMI
-{
-  $$ = new VersionStmtNode(atoi($2->c_str()), atoi($4->c_str()), atoi($6->c_str()));
-}
-;
-
 /* program is a list of declarations */
 prog_decls
   : prog_decl
@@ -156,10 +153,10 @@ prog_decls
 prog_decl
   : var_decl TSEMI
   | struct_decl TSEMI
-  | state_decl TSEMI
-  | timer_decl TSEMI
+  | state_decl
   | table_decl TSEMI
-  | pragma_decl TSEMI
+  | pragma_decl
+  | func_decl
   ;
 
 pragma_decl
@@ -194,12 +191,12 @@ stmt
   | call_expr TLBRACE TRBRACE TSEMI  // support empty curly braces
     { $$ = new ExprStmtNode(ExprNode::Ptr($1));
       parser.set_loc($$, @$); }
-  | if_stmt TSEMI
-  | switch_stmt TSEMI
+  | if_stmt
+  | switch_stmt
   | var_decl TSEMI
     { $$ = $1; }
-  | state_decl TSEMI
-  | onvalid_stmt TSEMI
+  | state_decl
+  | onvalid_stmt
   ;
 
 call_expr
@@ -229,10 +226,10 @@ struct_decl
   ;
 
 struct_decl_stmts
-  : decl_stmt TSEMI
-    { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr($1)); }
-  | struct_decl_stmts decl_stmt TSEMI
-    { $1->push_back(VariableDeclStmtNode::Ptr($2)); }
+  : type_specifiers decl_stmt TSEMI
+    { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr($2)); }
+  | struct_decl_stmts type_specifiers decl_stmt TSEMI
+    { $1->push_back(VariableDeclStmtNode::Ptr($3)); }
   ;
 
 table_decl
@@ -251,18 +248,22 @@ table_decl_args
 state_decl
   : TSTATE scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope
     { $$ = parser.state_add($3, $2, $5); $5->scope_ = $4;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   | TSTATE scoped_ident TCOMMA TMUL enter_statescope enter_varscope block exit_varscope exit_statescope
     { $$ = parser.state_add($5, $2, new IdentExprNode(""), $7); $7->scope_ = $6;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   | TSTATE scoped_ident TCOMMA scoped_ident enter_statescope enter_varscope block exit_varscope exit_statescope
     { $$ = parser.state_add($5, $2, $4, $7); $7->scope_ = $6;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   ;
 
-timer_decl
-  : TTIMER enter_statescope enter_varscope block exit_varscope exit_statescope
-    { $$ = parser.timer_add($2, $4); $4->scope_ = $3;
+func_decl
+  : type_specifiers ident enter_statescope enter_varscope TLPAREN formals TRPAREN block exit_varscope exit_statescope
+    { $$ = parser.func_add($1, $3, $2, $6, $8); $8->scope_ = $4;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   ;
 
@@ -276,37 +277,56 @@ table_result_stmts
 table_result_stmt
   : TMATCH ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI
     { $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   | TMISS ident enter_varscope TLPAREN TRPAREN block exit_varscope TSEMI
     { $$ = parser.result_add($1, $2, new FormalList, $6); $6->scope_ = $3;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   | TFAILURE ident enter_varscope TLPAREN formals TRPAREN block exit_varscope TSEMI
     { $$ = parser.result_add($1, $2, $5, $7); delete $5; $7->scope_ = $3;
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   ;
 
 formals
-  : TVAR ptr_decl
-    { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr(parser.variable_add($2))); }
-  | formals TCOMMA TVAR ptr_decl
-    { $1->push_back(VariableDeclStmtNode::Ptr(parser.variable_add($4))); }
+  : TSTRUCT ptr_decl
+    { $$ = new FormalList; $$->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $2))); }
+  | formals TCOMMA TSTRUCT ptr_decl
+    { $1->push_back(VariableDeclStmtNode::Ptr(parser.variable_add(nullptr, $4))); }
+  ;
+
+type_specifier
+  : TU8
+  | TU16
+  | TU32
+  | TU64
+  ;
+
+type_specifiers
+  : type_specifier { $$ = new std::vector<int>; $$->push_back($1); }
+  | type_specifiers type_specifier { $$->push_back($2); }
   ;
 
 var_decl
-  : TVAR decl_stmt
-    { $$ = parser.variable_add($2);
+  : type_specifiers decl_stmt
+    { $$ = parser.variable_add($1, $2);
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
-  | TVAR int_decl TEQUAL expr
-    { $$ = parser.variable_add($2, $4);
+  | type_specifiers int_decl TEQUAL expr
+    { $$ = parser.variable_add($1, $2, $4);
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
-  | TVAR type_decl TLBRACE init_args_kv TRBRACE
-    { $$ = parser.variable_add($2, $4, true);
+  | TSTRUCT type_decl TEQUAL TLBRACE init_args_kv TRBRACE
+    { $$ = parser.variable_add($2, $5, true);
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
-  /*| TVAR type_decl TLBRACE init_args TRBRACE
-    { $$ = parser.variable_add($2, $4, false);
+  /*| TSTRUCT type_decl TEQUAL TLBRACE init_args TRBRACE
+    { $$ = parser.variable_add($2, $5, false);
       parser.set_loc($$, @$); }*/
-  | TVAR ref_stmt
-    { $$ = parser.variable_add($2);
+  | TSTRUCT ref_stmt
+    { $$ = parser.variable_add(nullptr, $2);
+      if (!$$) YYERROR;
       parser.set_loc($$, @$); }
   ;
 
@@ -331,10 +351,10 @@ ptr_decl : scoped_ident TMUL ident
   ;
 
 /* normal initializer */
-init_args
+/* init_args
   : expr { $$ = new ExprNodeList; $$->push_back(ExprNode::Ptr($1)); }
   | init_args TCOMMA expr { $$->push_back(ExprNode::Ptr($3)); }
-  ;
+  ;*/
 
 /* one or more of "field" = "expr" */
 init_args_kv
@@ -342,11 +362,11 @@ init_args_kv
   | init_args_kv TCOMMA init_arg_kv { $$->push_back(ExprNode::Ptr($3)); }
   ;
 init_arg_kv
-  : ident TEQUAL expr
-    { $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($3));
+  : TDOT ident TEQUAL expr
+    { $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($4));
       parser.set_loc($$, @$); }
-  | ident bitop TEQUAL expr
-    { $$ = new AssignExprNode(IdentExprNode::Ptr($1), ExprNode::Ptr($4)); $$->bitop_ = BitopExprNode::Ptr($2);
+  | TDOT ident bitop TEQUAL expr
+    { $$ = new AssignExprNode(IdentExprNode::Ptr($2), ExprNode::Ptr($5)); $$->bitop_ = BitopExprNode::Ptr($3);
       parser.set_loc($$, @$); }
   ;
 
@@ -354,7 +374,7 @@ if_stmt
   : TIF expr enter_varscope block exit_varscope
     { $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4));
       $4->scope_ = $3;
-        parser.set_loc($$, @$); }
+      parser.set_loc($$, @$); }
   | TIF expr enter_varscope block exit_varscope TELSE enter_varscope block exit_varscope
     { $$ = new IfStmtNode(ExprNode::Ptr($2), StmtNode::Ptr($4), StmtNode::Ptr($8));
       $4->scope_ = $3; $8->scope_ = $7;
@@ -439,6 +459,10 @@ expr
     { $$ = $1; }
   | call_expr bitop
     { $$ = $1; $$->bitop_ = BitopExprNode::Ptr($2); }
+  | table_index_expr
+    { $$ = $1; }
+  | table_index_expr TDOT ident
+    { $$ = $1; $1->sub_ = IdentExprNode::Ptr($3); }
   | any_ident
     { $$ = $1; }
   | TAT dotted_ident
@@ -464,6 +488,9 @@ expr
     { $$ = $2; }
   | TLPAREN expr TRPAREN bitop
     { $$ = $2; $$->bitop_ = BitopExprNode::Ptr($4); }
+  | TSTRING
+    { $$ = new StringExprNode($1);
+      parser.set_loc($$, @$); }
   | numeric
     { $$ = $1; }
   | numeric bitop
@@ -544,6 +571,12 @@ bitop
       parser.set_loc($$, @$); }
   ;
 
+table_index_expr
+  : dotted_ident TLBRACK ident TRBRACK
+    { $$ = new TableIndexExprNode(IdentExprNode::Ptr($1), IdentExprNode::Ptr($3));
+      parser.set_loc($$, @$); }
+  ;
+
 scoped_ident
   : ident
     { $$ = $1; }
@@ -561,6 +594,8 @@ dotted_ident
 any_ident
   : ident
     { $$ = $1; }
+  | dotted_ident TARROW TIDENTIFIER
+    { $$->append_dot(*$3); delete $3; }
   | dotted_ident TDOT TIDENTIFIER
     { $$->append_dot(*$3); delete $3; }
   | scoped_ident TSCOPE TIDENTIFIER
similarity index 92%
rename from jit/src/cc/printer.cc
rename to src/cc/printer.cc
index d9d4df5..0fcc8a1 100644 (file)
@@ -30,7 +30,6 @@ void Printer::print_indent() {
 StatusTuple Printer::visit_block_stmt_node(BlockStmtNode* n) {
   fprintf(out_, "{\n");
 
-  TRY2(n->ver_.accept(this));
   if (!n->stmts_.empty()) {
     ++indent_;
     for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
@@ -44,14 +43,6 @@ StatusTuple Printer::visit_block_stmt_node(BlockStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple Printer::visit_version_stmt_node(VersionStmtNode* n) {
-  uint32_t version;
-  version = MAKE_VERSION(n->major_, n->minor_, n->rev_);
-  fprintf(out_, "static const uint32_t  plumlet_version   __attribute__"
-      "((section (\".version\"), used)) = 0x%x;\n", version);
-  return mkstatus(0);
-}
-
 StatusTuple Printer::visit_if_stmt_node(IfStmtNode* n) {
   fprintf(out_, "if ");
   TRY2(n->cond_->accept(this));
@@ -124,6 +115,11 @@ StatusTuple Printer::visit_integer_expr_node(IntegerExprNode* n) {
   return mkstatus(0);
 }
 
+StatusTuple Printer::visit_string_expr_node(StringExprNode *n) {
+  fprintf(out_, "%s", n->val_.c_str());
+  return mkstatus(0);
+}
+
 StatusTuple Printer::visit_binop_expr_node(BinopExprNode* n) {
   TRY2(n->lhs_->accept(this));
   fprintf(out_, "%d", n->op_);
@@ -186,6 +182,13 @@ StatusTuple Printer::visit_method_call_expr_node(MethodCallExprNode* n) {
   return mkstatus(0);
 }
 
+StatusTuple Printer::visit_table_index_expr_node(TableIndexExprNode *n) {
+  fprintf(out_, "%s[", n->id_->c_str());
+  TRY2(n->index_->accept(this));
+  fprintf(out_, "]");
+  return mkstatus(0);
+}
+
 StatusTuple Printer::visit_expr_stmt_node(ExprStmtNode* n) {
   TRY2(n->expr_->accept(this));
   return mkstatus(0);
@@ -235,15 +238,6 @@ StatusTuple Printer::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple Printer::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
-  if (!n->id_) {
-    return mkstatus(0);
-  }
-  fprintf(out_, "timer ");
-  TRY2(n->id_->accept(this));
-  return mkstatus(0);
-}
-
 StatusTuple Printer::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
   if (!n->id_) {
     return mkstatus(0);
@@ -324,5 +318,20 @@ StatusTuple Printer::visit_table_decl_stmt_node(TableDeclStmtNode* n) {
   return mkstatus(0);
 }
 
+StatusTuple Printer::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
+  fprintf(out_, "func ");
+  TRY2(n->id_->accept(this));
+  fprintf(out_, "(");
+  for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
+    TRY2((*it)->accept(this));
+    if (it + 1 != n->formals_.end()) {
+      fprintf(out_, ", ");
+    }
+  }
+  fprintf(out_, ") ");
+  TRY2(n->block_->accept(this));
+  return mkstatus(0);
+}
+
 }  // namespace cc
 }  // namespace ebpf
similarity index 100%
rename from jit/src/cc/printer.h
rename to src/cc/printer.h
similarity index 64%
rename from jit/src/cc/scope.h
rename to src/cc/scope.h
index 9a68f07..0a3005e 100644 (file)
@@ -33,28 +33,30 @@ using std::pair;
 using std::unique_ptr;
 
 class StateDeclStmtNode;
-class TimerDeclStmtNode;
 class VariableDeclStmtNode;
 class TableDeclStmtNode;
 class StructDeclStmtNode;
+class FuncDeclStmtNode;
+
+enum search_type { SCOPE_LOCAL, SCOPE_GLOBAL };
 
 template <typename T>
 class Scope {
  public:
   Scope() {}
   Scope(Scope<T>* scope, int id) : parent_(scope), id_(id) {}
-  enum search_type { LOCAL, GLOBAL };
 
-  T* lookup(const string& name, bool search_local = true) {
+  T* lookup(const string &name, bool search_local = true) {
+    return lookup(name, search_local ? SCOPE_LOCAL : SCOPE_GLOBAL);
+  }
+  T * lookup(const string &name, search_type stype) {
     auto it = elems_.find(name);
-    if (it != elems_.end()) {
+    if (it != elems_.end())
       return it->second;
-    }
 
-    if (search_local || !parent_) {
-      return NULL;
-    }
-    return parent_->lookup(name, search_local);
+    if (stype == SCOPE_LOCAL || !parent_)
+      return nullptr;
+    return parent_->lookup(name, stype);
   }
   void add(const string& name, T* n) {
     elems_[name] = n;
@@ -80,23 +82,51 @@ class Scopes {
   typedef unique_ptr<Scopes> Ptr;
   typedef Scope<StructDeclStmtNode> StructScope;
   typedef Scope<StateDeclStmtNode> StateScope;
-  typedef Scope<TimerDeclStmtNode> TimerScope;
   typedef Scope<VariableDeclStmtNode> VarScope;
   typedef Scope<TableDeclStmtNode> TableScope;
+  typedef Scope<FuncDeclStmtNode> FuncScope;
 
   Scopes() : var_id__(0), state_id_(0), var_id_(0),
-    current_var_scope_(NULL), top_var_scope_(NULL),
-    current_state_scope_(NULL), top_state_scope_(NULL),
-    top_timer_scope_(new TimerScope(NULL, 1)),
-    top_struct_scope_(new StructScope(NULL, 1)),
-    top_table_scope_(new TableScope(NULL, 1)) {}
+    current_var_scope_(nullptr), top_var_scope_(nullptr),
+    current_state_scope_(nullptr), top_state_scope_(nullptr),
+    top_struct_scope_(new StructScope(nullptr, 1)),
+    top_table_scope_(new TableScope(nullptr, 1)),
+    top_func_scope_(new FuncScope(nullptr, 1)) {}
   ~Scopes() {
-    delete top_timer_scope_;
+    delete top_func_scope_;
     delete top_struct_scope_;
     delete top_table_scope_;
     delete top_state_scope_;
   }
 
+  void push_var(VarScope *scope) {
+    if (scope == top_var_scope_)
+      return;
+    scope->parent_ = current_var_scope_;
+    current_var_scope_ = scope;
+  }
+  void pop_var() {
+    if (current_var_scope_ == top_var_scope_)
+      return;
+    VarScope *old = current_var_scope_;
+    current_var_scope_ = old->parent_;
+    old->parent_ = nullptr;
+  }
+
+  void push_state(StateScope *scope) {
+    if (scope == top_state_scope_)
+      return;
+    scope->parent_ = current_state_scope_;
+    current_state_scope_ = scope;
+  }
+  void pop_state() {
+    if (current_state_scope_ == top_state_scope_)
+      return;
+    StateScope *old = current_state_scope_;
+    current_state_scope_ = old->parent_;
+    old->parent_ = nullptr;
+  }
+
   /// While building the AST, allocate a new scope
   VarScope* enter_var_scope() {
     current_var_scope_ = new VarScope(current_var_scope_, next_var_id());
@@ -125,18 +155,17 @@ class Scopes {
   }
 
   void set_current(VarScope* s) { current_var_scope_ = s; }
-  VarScope* current_var() { return current_var_scope_; }
-  VarScope* top_var() { return top_var_scope_; }
+  VarScope* current_var() const { return current_var_scope_; }
+  VarScope* top_var() const { return top_var_scope_; }
 
   void set_current(StateScope* s) { current_state_scope_ = s; }
-  StateScope* current_state() { return current_state_scope_; }
-  StateScope* top_state() { return top_state_scope_; }
-
-  TimerScope* top_timer() { return top_timer_scope_; }
+  StateScope* current_state() const { return current_state_scope_; }
+  StateScope* top_state() const { return top_state_scope_; }
 
-  StructScope* top_struct() { return top_struct_scope_; }
+  StructScope* top_struct() const { return top_struct_scope_; }
 
-  TableScope* top_table() { return top_table_scope_; }
+  TableScope* top_table() const { return top_table_scope_; }
+  FuncScope* top_func() const { return top_func_scope_; }
 
   int next_id() { return ++var_id__; }
   int next_state_id() { return ++state_id_; }
@@ -149,9 +178,9 @@ class Scopes {
   VarScope* top_var_scope_;
   StateScope* current_state_scope_;
   StateScope* top_state_scope_;
-  TimerScope* top_timer_scope_;
   StructScope* top_struct_scope_;
   TableScope* top_table_scope_;
+  FuncScope* top_func_scope_;
 };
 
 }  // namespace cc
similarity index 67%
rename from jit/src/cc/type_check.cc
rename to src/cc/type_check.cc
index 00864e6..a8aa53d 100644 (file)
@@ -28,36 +28,24 @@ namespace cc {
 using std::for_each;
 using std::set;
 
-StatusTuple TypeCheck::visit_block_stmt_node(BlockStmtNoden) {
+StatusTuple TypeCheck::visit_block_stmt_node(BlockStmtNode *n) {
   // enter scope
-  auto scope = scopes_->current_var();
-  if (n->scope_) {
-    scopes_->set_current(n->scope_);
-  }
-  n->ver_.accept(this);
+  if (n->scope_)
+    scopes_->push_var(n->scope_);
   if (!n->stmts_.empty()) {
-    for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
-      //try {
-        TRY2((*it)->accept(this));
-      //} catch (CompilerException& e) {
-      //  errors_.push_back(e.what());
-      //}
-    }
+    for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it)
+      TRY2((*it)->accept(this));
   }
 
-  // exit scope
-  scopes_->set_current(scope);
+  if (n->scope_)
+    scopes_->pop_var();
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_version_stmt_node(VersionStmtNode* n) {
-  return mkstatus(0);
-}
-
-StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode* n) {
+StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode *n) {
   TRY2(n->cond_->accept(this));
-  if (n->cond_->typeof_ != ExprNode::INTEGER)
-    return mkstatus_(n, "If condition must be a numeric type");
+  //if (n->cond_->typeof_ != ExprNode::INTEGER)
+  //  return mkstatus_(n, "If condition must be a numeric type");
   TRY2(n->true_block_->accept(this));
   if (n->false_block_) {
     TRY2(n->false_block_->accept(this));
@@ -65,7 +53,7 @@ StatusTuple TypeCheck::visit_if_stmt_node(IfStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNoden) {
+StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode *n) {
   TRY2(n->cond_->accept(this));
   auto sdecl = static_cast<StructVariableDeclStmtNode*>(n->cond_->decl_);
   if (sdecl->storage_type_ != StructVariableDeclStmtNode::STRUCT_REFERENCE)
@@ -77,7 +65,7 @@ StatusTuple TypeCheck::visit_onvalid_stmt_node(OnValidStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNoden) {
+StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode *n) {
   TRY2(n->cond_->accept(this));
   if (n->cond_->typeof_ != ExprNode::INTEGER)
     return mkstatus_(n, "Switch condition must be a numeric type");
@@ -88,7 +76,7 @@ StatusTuple TypeCheck::visit_switch_stmt_node(SwitchStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNoden) {
+StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode *n) {
   if (n->value_) {
     TRY2(n->value_->accept(this));
     if (n->value_->typeof_ != ExprNode::INTEGER)
@@ -98,8 +86,8 @@ StatusTuple TypeCheck::visit_case_stmt_node(CaseStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNoden) {
-  n->decl_ = scopes_->current_var()->lookup(n->name_, false);
+StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode *n) {
+  n->decl_ = scopes_->current_var()->lookup(n->name_, SCOPE_GLOBAL);
   if (!n->decl_)
     return mkstatus_(n, "Variable %s lookup failed", n->c_str());
 
@@ -148,7 +136,7 @@ StatusTuple TypeCheck::visit_ident_expr_node(IdentExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNoden) {
+StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode *n) {
   /// @todo check lhs is assignable
   TRY2(n->id_->accept(this));
   if (n->id_->typeof_ == ExprNode::STRUCT) {
@@ -168,7 +156,7 @@ StatusTuple TypeCheck::visit_assign_expr_node(AssignExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNoden) {
+StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode *n) {
   StructDeclStmtNode *struct_type = proto_scopes_->top_struct()->lookup(n->id_->name_, true);
   if (!struct_type)
     return mkstatus_(n, "Undefined packet header %s", n->id_->c_str());
@@ -189,13 +177,20 @@ StatusTuple TypeCheck::visit_packet_expr_node(PacketExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_integer_expr_node(IntegerExprNoden) {
+StatusTuple TypeCheck::visit_integer_expr_node(IntegerExprNode *n) {
   n->typeof_ = ExprNode::INTEGER;
   n->bit_width_ = n->bits_;
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode* n) {
+StatusTuple TypeCheck::visit_string_expr_node(StringExprNode *n) {
+  n->typeof_ = ExprNode::STRING;
+  n->flags_[ExprNode::IS_REF] = true;
+  n->bit_width_ = n->val_.size() << 3;
+  return mkstatus(0);
+}
+
+StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode *n) {
   TRY2(n->lhs_->accept(this));
   if (n->lhs_->typeof_ != ExprNode::INTEGER)
     return mkstatus_(n, "Left-hand side of binary expression must be a numeric type");
@@ -217,7 +212,7 @@ StatusTuple TypeCheck::visit_binop_expr_node(BinopExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNoden) {
+StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode *n) {
   TRY2(n->expr_->accept(this));
   if (n->expr_->typeof_ != ExprNode::INTEGER)
     return mkstatus_(n, "Unary operand must be a numeric type");
@@ -225,26 +220,26 @@ StatusTuple TypeCheck::visit_unop_expr_node(UnopExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_bitop_expr_node(BitopExprNoden) {
+StatusTuple TypeCheck::visit_bitop_expr_node(BitopExprNode *n) {
   if (n->expr_->typeof_ != ExprNode::INTEGER)
     return mkstatus_(n, "Bitop [] can only operate on numeric types");
   n->typeof_ = ExprNode::INTEGER;
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_goto_expr_node(GotoExprNoden) {
+StatusTuple TypeCheck::visit_goto_expr_node(GotoExprNode *n) {
   //n->id_->accept(this);
   n->typeof_ = ExprNode::VOID;
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_return_expr_node(ReturnExprNoden) {
+StatusTuple TypeCheck::visit_return_expr_node(ReturnExprNode *n) {
   TRY2(n->expr_->accept(this));
   n->typeof_ = ExprNode::VOID;
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::expect_method_arg(MethodCallExprNoden, size_t num, size_t num_def_args = 0) {
+StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode *n, size_t num, size_t num_def_args = 0) {
   if (num_def_args == 0) {
     if (n->args_.size() != num)
       return mkstatus_(n, "%s expected %d argument%s, %zu given", n->id_->sub_name_.c_str(),
@@ -257,7 +252,7 @@ StatusTuple TypeCheck::expect_method_arg(MethodCallExprNode* n, size_t num, size
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::check_lookup_method(MethodCallExprNoden) {
+StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode *n) {
   auto table = scopes_->top_table()->lookup(n->id_->name_);
   if (!table)
     return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
@@ -273,7 +268,7 @@ StatusTuple TypeCheck::check_lookup_method(MethodCallExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::check_update_method(MethodCallExprNoden) {
+StatusTuple TypeCheck::check_update_method(MethodCallExprNode *n) {
   auto table = scopes_->top_table()->lookup(n->id_->name_);
   if (!table)
     return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
@@ -284,7 +279,7 @@ StatusTuple TypeCheck::check_update_method(MethodCallExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::check_delete_method(MethodCallExprNoden) {
+StatusTuple TypeCheck::check_delete_method(MethodCallExprNode *n) {
   auto table = scopes_->top_table()->lookup(n->id_->name_);
   if (!table)
     return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
@@ -295,7 +290,7 @@ StatusTuple TypeCheck::check_delete_method(MethodCallExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNoden) {
+StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode *n) {
   // be sure to visit those child nodes ASAP, so their properties can
   // be propagated up to this node and be ready to be used
   for (auto it = n->args_.begin(); it != n->args_.end(); ++it) {
@@ -310,51 +305,23 @@ StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode* n) {
       TRY2(check_update_method(n));
     } else if (n->id_->sub_name_ == "delete") {
       TRY2(check_delete_method(n));
-    } else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 1));
-    } else if (n->id_->sub_name_ == "drop" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 0));
-    } else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 2, 1));
-    } else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 1));
-    } else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 1));
-    } else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 0));
-    } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") {
-      TRY2(expect_method_arg(n, 2));
-      n->args_[0]->flags_[ExprNode::IS_LHS] = true;
     }
-  } else if (n->id_->name_ == "channel_push") {
-    TRY2(expect_method_arg(n, 1));
   } else if (n->id_->name_ == "log") {
-    TRY2(expect_method_arg(n, 1));
+    if (n->args_.size() < 1)
+      return mkstatus_(n, "%s expected at least 1 argument", n->id_->c_str());
+    if (n->args_[0]->typeof_ != ExprNode::STRING)
+      return mkstatus_(n, "%s expected a string for argument 1", n->id_->c_str());
+    n->typeof_ = ExprNode::INTEGER;
+    n->bit_width_ = 32;
   } else if (n->id_->name_ == "atomic_add") {
     TRY2(expect_method_arg(n, 2));
     n->typeof_ = ExprNode::INTEGER;
     n->bit_width_ = n->args_[0]->bit_width_;
     n->args_[0]->flags_[ExprNode::IS_LHS] = true;
-  } else if (n->id_->name_ == "cksum") {
-    TRY2(expect_method_arg(n, 1));
-    n->typeof_ = ExprNode::INTEGER;
-    n->bit_width_ = 16;
-  } else if (n->id_->name_ == "incr_cksum_u16") {
-    TRY2(expect_method_arg(n, 4, 1));
-    n->typeof_ = ExprNode::INTEGER;
-    n->bit_width_ = 16;
-  } else if (n->id_->name_ == "incr_cksum_u32") {
-    TRY2(expect_method_arg(n, 4, 1));
-    n->typeof_ = ExprNode::INTEGER;
-    n->bit_width_ = 16;
   } else if (n->id_->name_ == "incr_cksum") {
     TRY2(expect_method_arg(n, 4, 1));
     n->typeof_ = ExprNode::INTEGER;
     n->bit_width_ = 16;
-  } else if (n->id_->name_ == "lb_hash") {
-    TRY2(expect_method_arg(n, 3, 1));
-    n->typeof_ = ExprNode::INTEGER;
-    n->bit_width_ = 8;
   } else if (n->id_->name_ == "sizeof") {
     TRY2(expect_method_arg(n, 1));
     n->typeof_ = ExprNode::INTEGER;
@@ -373,12 +340,32 @@ StatusTuple TypeCheck::visit_method_call_expr_node(MethodCallExprNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_expr_stmt_node(ExprStmtNode* n) {
+StatusTuple TypeCheck::visit_table_index_expr_node(TableIndexExprNode *n) {
+  n->table_ = scopes_->top_table()->lookup(n->id_->name_);
+  if (!n->table_) return mkstatus_(n, "Unknown table name %s", n->id_->c_str());
+  TRY2(n->index_->accept(this));
+  if (n->index_->struct_type_ != n->table_->key_type_)
+    return mkstatus_(n, "Key to table %s lookup must be of type %s", n->id_->c_str(), n->table_->key_id()->c_str());
+
+  if (n->sub_) {
+    n->sub_decl_ = n->table_->leaf_type_->field(n->sub_->name_);
+    if (!n->sub_decl_)
+      return mkstatus_(n, "Field %s is not a member of %s", n->sub_->c_str(), n->table_->leaf_id()->c_str());
+    n->typeof_ = ExprNode::INTEGER;
+  } else {
+    n->typeof_ = ExprNode::STRUCT;
+    n->flags_[ExprNode::IS_REF] = true;
+    n->struct_type_ = n->table_->leaf_type_;
+  }
+  return mkstatus(0);
+}
+
+StatusTuple TypeCheck::visit_expr_stmt_node(ExprStmtNode *n) {
   TRY2(n->expr_->accept(this));
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNoden) {
+StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode *n) {
   //TRY2(n->struct_id_->accept(this));
   //TRY2(n->id_->accept(this));
   if (!n->init_.empty()) {
@@ -412,7 +399,7 @@ StatusTuple TypeCheck::visit_struct_variable_decl_stmt_node(StructVariableDeclSt
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNoden) {
+StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode *n) {
   //TRY2(n->id_->accept(this));
   if (!n->init_.empty()) {
     TRY2(n->init_[0]->accept(this));
@@ -420,7 +407,7 @@ StatusTuple TypeCheck::visit_integer_variable_decl_stmt_node(IntegerVariableDecl
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNoden) {
+StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode *n) {
   //TRY2(n->id_->accept(this));
   for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) {
     TRY2((*it)->accept(this));
@@ -428,20 +415,11 @@ StatusTuple TypeCheck::visit_struct_decl_stmt_node(StructDeclStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_parser_state_stmt_node(ParserStateStmtNode* n) {
-  return mkstatus(0);
-}
-
-StatusTuple TypeCheck::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) {
-  auto timer_delay = make_unique<IntegerVariableDeclStmtNode>(make_unique<IdentExprNode>("timer_delay"), "32");
-  scopes_->current_var()->add("timer_delay", timer_delay.get());
-  n->block_->stmts_.push_back(move(timer_delay));
-
-  TRY2(n->block_->accept(this));
+StatusTuple TypeCheck::visit_parser_state_stmt_node(ParserStateStmtNode *n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNoden) {
+StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode *n) {
   if (!n->id_) {
     return mkstatus(0);
   }
@@ -476,8 +454,7 @@ StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
   }
 
   for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) {
-    auto scope = scopes_->current_state();
-    scopes_->set_current(it->scope_);
+    scopes_->push_state(it->scope_);
 
     TRY2(it->block_->accept(this));
 
@@ -497,12 +474,12 @@ StatusTuple TypeCheck::visit_state_decl_stmt_node(StateDeclStmtNode* n) {
       }
     }
 
-    scopes_->set_current(scope);
+    scopes_->pop_state();
   }
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNoden) {
+StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode *n) {
   //TRY2(n->id_->accept(this));
   for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
     TRY2((*it)->accept(this));
@@ -511,7 +488,7 @@ StatusTuple TypeCheck::visit_match_decl_stmt_node(MatchDeclStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNoden) {
+StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode *n) {
   //TRY2(n->id_->accept(this));
   for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
     TRY2((*it)->accept(this));
@@ -520,7 +497,7 @@ StatusTuple TypeCheck::visit_miss_decl_stmt_node(MissDeclStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNoden) {
+StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode *n) {
   //TRY2(n->id_->accept(this));
   for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
     TRY2((*it)->accept(this));
@@ -529,96 +506,61 @@ StatusTuple TypeCheck::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) {
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit_table_decl_stmt_node(TableDeclStmtNoden) {
-  auto key_type = scopes_->top_struct()->lookup(n->key_id()->name_, true);
-  if (!key_type)
+StatusTuple TypeCheck::visit_table_decl_stmt_node(TableDeclStmtNode *n) {
+  n->key_type_ = scopes_->top_struct()->lookup(n->key_id()->name_, true);
+  if (!n->key_type_)
     return mkstatus_(n, "Table key type %s undefined", n->key_id()->c_str());
-  n->key_id()->bit_width_ = key_type->bit_width_;
-  auto leaf_type = scopes_->top_struct()->lookup(n->leaf_id()->name_, true);
-  if (!leaf_type)
+  n->key_id()->bit_width_ = n->key_type_->bit_width_;
+  n->leaf_type_ = scopes_->top_struct()->lookup(n->leaf_id()->name_, true);
+  if (!n->leaf_type_)
     return mkstatus_(n, "Table leaf type %s undefined", n->leaf_id()->c_str());
-  n->leaf_id()->bit_width_ = leaf_type->bit_width_;
+  n->leaf_id()->bit_width_ = n->leaf_type_->bit_width_;
+  if (n->type_id()->name_ == "INDEXED" && n->policy_id()->name_ != "AUTO") {
+    fprintf(stderr, "Table %s is INDEXED, policy should be AUTO\n", n->id_->c_str());
+    n->policy_id()->name_ = "AUTO";
+  }
+  if (n->policy_id()->name_ != "AUTO" && n->policy_id()->name_ != "NONE")
+    return mkstatus_(n, "Unsupported policy type %s", n->policy_id()->c_str());
   return mkstatus(0);
 }
 
-StatusTuple TypeCheck::visit(Node* root) {
-  BlockStmtNode* b = static_cast<BlockStmtNode*>(root);
+StatusTuple TypeCheck::visit_func_decl_stmt_node(FuncDeclStmtNode *n) {
+  for (auto it = n->formals_.begin(); it != n->formals_.end(); ++it) {
+    VariableDeclStmtNode *var = it->get();
+    TRY2(var->accept(this));
+    if (var->is_struct()) {
+      if (!var->is_pointer())
+        return mkstatus_(n, "Only struct references allowed in function definitions");
+    }
+  }
+  scopes_->push_state(n->scope_);
+  TRY2(n->block_->accept(this));
+  scopes_->pop_state();
+  return mkstatus(0);
+}
 
-  if (pragmas_.count("name") == 0)
-    return mkstatus(-1, "#name <PackageName> must be defined");
+StatusTuple TypeCheck::visit(Node *root) {
+  BlockStmtNode *b = static_cast<BlockStmtNode*>(root);
 
   scopes_->set_current(scopes_->top_state());
   scopes_->set_current(scopes_->top_var());
 
-  // add builtin types and identifiers
-  if (scopes_->top_struct()->lookup("_Packet", true)) {
-    return mkstatus(-1, "_Packet already defined");
-  }
-  auto pkt_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_Packet"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("port_id"), "8"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("length"), "16"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("parsed_bytes"), "16"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("vlan_tag"), "16"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg8"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg7"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg6"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg5"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg4"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg3"), "32")); // originator_plum
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg2"), "32")); // gid
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("arg1"), "32")); // from_fabric
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.crypto_proto"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.crypto_hr"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.crypto_mark"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.crypto_spi"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.src_ip"), "32"));
-  pkt_type->stmts_.push_back(make_unique<IntegerVariableDeclStmtNode>(
-          make_unique<IdentExprNode>("tun_key.dst_ip"), "32"));
-  scopes_->top_struct()->add("_Packet", pkt_type.get());
-  b->stmts_.push_back(move(pkt_type));
-
-  if (scopes_->current_var()->lookup("pkt", true)) {
-    return mkstatus(-1, "pkt already defined");
-  }
-  auto pkt = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_Packet"),
-                                                     make_unique<IdentExprNode>("pkt"));
-  pkt->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
-  scopes_->current_var()->add("pkt", pkt.get());
-  b->stmts_.push_back(move(pkt));
-
-  // packet data in bpf socket
-  if (scopes_->top_struct()->lookup("_skbuff", true)) {
-    return mkstatus(-1, "_skbuff already defined");
-  }
-  auto skb_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"));
-  scopes_->top_struct()->add("_skbuff", skb_type.get());
-  b->stmts_.push_back(move(skb_type));
-
-  if (scopes_->current_var()->lookup("skb", true)) {
-    return mkstatus(-1, "skb already defined");
-  }
-  auto skb = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"),
-                                                     make_unique<IdentExprNode>("skb"));
-  skb->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
-  scopes_->current_var()->add("skb", skb.get());
-  b->stmts_.push_back(move(skb));
+  // // packet data in bpf socket
+  // if (scopes_->top_struct()->lookup("_skbuff", true)) {
+  //   return mkstatus(-1, "_skbuff already defined");
+  // }
+  // auto skb_type = make_unique<StructDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"));
+  // scopes_->top_struct()->add("_skbuff", skb_type.get());
+  // b->stmts_.push_back(move(skb_type));
+
+  // if (scopes_->current_var()->lookup("skb", true)) {
+  //   return mkstatus(-1, "skb already defined");
+  // }
+  // auto skb = make_unique<StructVariableDeclStmtNode>(make_unique<IdentExprNode>("_skbuff"),
+  //                                                    make_unique<IdentExprNode>("skb"));
+  // skb->storage_type_ = VariableDeclStmtNode::STRUCT_REFERENCE;
+  // scopes_->current_var()->add("skb", skb.get());
+  // b->stmts_.push_back(move(skb));
 
   // offset counter
   auto parsed_bytes = make_unique<IntegerVariableDeclStmtNode>(
similarity index 100%
rename from jit/src/cc/type_check.h
rename to src/cc/type_check.h
similarity index 94%
rename from jit/compat/include/linux/bpf.h
rename to src/compat/include/linux/bpf.h
index 23df3e7..a9ebdf5 100644 (file)
@@ -118,6 +118,7 @@ enum bpf_map_type {
 enum bpf_prog_type {
        BPF_PROG_TYPE_UNSPEC,
        BPF_PROG_TYPE_SOCKET_FILTER,
+       BPF_PROG_TYPE_KPROBE,
        BPF_PROG_TYPE_SCHED_CLS,
        BPF_PROG_TYPE_SCHED_ACT,
 };
@@ -155,6 +156,7 @@ union bpf_attr {
                __u32           log_level;      /* verbosity level of verifier */
                __u32           log_size;       /* size of user buffer */
                __aligned_u64   log_buf;        /* user supplied buffer */
+               __u32           kern_version;   /* checked when prog_type=kprobe */
        };
 } __attribute__((aligned(8)));
 
@@ -166,13 +168,16 @@ enum bpf_func_id {
        BPF_FUNC_map_lookup_elem, /* void *map_lookup_elem(&map, &key) */
        BPF_FUNC_map_update_elem, /* int map_update_elem(&map, &key, &value, flags) */
        BPF_FUNC_map_delete_elem, /* int map_delete_elem(&map, &key) */
+       BPF_FUNC_probe_read,      /* int bpf_probe_read(void *dst, int size, void *src) */
+       BPF_FUNC_ktime_get_ns,    /* u64 bpf_ktime_get_ns(void) */
+       BPF_FUNC_trace_printk,    /* int bpf_trace_printk(const char *fmt, int fmt_size, ...) */
        BPF_FUNC_get_prandom_u32, /* u32 prandom_u32(void) */
        BPF_FUNC_get_smp_processor_id, /* u32 raw_smp_processor_id(void) */
 
        /**
         * skb_store_bytes(skb, offset, from, len, flags) - store bytes into packet
         * @skb: pointer to skb
-        * @offset: offset within packet from skb->data
+        * @offset: offset within packet from skb->mac_header
         * @from: pointer where to copy bytes from
         * @len: number of bytes to store into packet
         * @flags: bit 0 - if true, recompute skb->csum
similarity index 90%
rename from jit/compat/include/linux/bpf_common.h
rename to src/compat/include/linux/bpf_common.h
index afe7433..a5c220e 100644 (file)
@@ -1,5 +1,5 @@
-#ifndef __LINUX_BPF_COMMON_H__
-#define __LINUX_BPF_COMMON_H__
+#ifndef _UAPI__LINUX_BPF_COMMON_H__
+#define _UAPI__LINUX_BPF_COMMON_H__
 
 /* Instruction classes */
 #define BPF_CLASS(code) ((code) & 0x07)
@@ -52,4 +52,4 @@
 #define BPF_MAXINSNS 4096
 #endif
 
-#endif /* __LINUX_BPF_COMMON_H__ */
+#endif /* _UAPI__LINUX_BPF_COMMON_H__ */
similarity index 97%
rename from jit/src/libbpf.h
rename to src/libbpf.h
index 1eeab11..3e2e851 100644 (file)
@@ -27,6 +27,8 @@ int bpf_attach_filter(int progfd, const char *prog_name, uint32_t ifindex,
 /* create RAW socket and bind to interface 'name' */
 int bpf_open_raw_sock(const char *name);
 
+int bpf_attach_kprobe(int progfd, const char *event, const char *event_desc, pid_t pid, int cpu, int group_fd);
+
 #define LOG_BUF_SIZE 65536
 extern char bpf_log_buf[LOG_BUF_SIZE];
 
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644 (file)
index 0000000..9550ca0
--- /dev/null
@@ -0,0 +1,5 @@
+configure_file(wrapper.sh.in "${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh" @ONLY)
+
+set(TEST_WRAPPER ${CMAKE_CURRENT_BINARY_DIR}/wrapper.sh)
+
+add_subdirectory(jit)
diff --git a/tests/jit/CMakeLists.txt b/tests/jit/CMakeLists.txt
new file mode 100644 (file)
index 0000000..e439f0f
--- /dev/null
@@ -0,0 +1,8 @@
+add_test(NAME py_test1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_test1 ${CMAKE_CURRENT_SOURCE_DIR}/test1.py namespace)
+add_test(NAME py_test2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_test2 ${CMAKE_CURRENT_SOURCE_DIR}/test2.py namespace)
+add_test(NAME py_trace1 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_trace1 ${CMAKE_CURRENT_SOURCE_DIR}/trace1.py sudo)
+add_test(NAME py_trace2 WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+  COMMAND ${TEST_WRAPPER} py_trace2 ${CMAKE_CURRENT_SOURCE_DIR}/trace2.py sudo)
diff --git a/tests/jit/kprobe.b b/tests/jit/kprobe.b
new file mode 100644 (file)
index 0000000..a29e1d2
--- /dev/null
@@ -0,0 +1,22 @@
+
+#packed "false"
+
+struct pt_regs {
+  u64 r15:64;
+  u64 r14:64;
+  u64 r13:64;
+  u64 r12:64;
+  u64 bp:64;
+  u64 bx:64;
+  u64 r11:64;
+  u64 r10:64;
+  u64 r9:64;
+  u64 r8:64;
+  u64 ax:64;
+  u64 cx:64;
+  u64 dx:64;
+  u64 si:64;
+  u64 di:64;
+};
+
+
index adf4fcb..6199f5e 100755 (executable)
@@ -1,6 +1,6 @@
 #!/usr/bin/env python
 import sys
-from src.bpf import BPF
+from bpf import BPF
 prog = BPF(sys.argv[1], sys.argv[2], sys.argv[3],
         prog_type=int(sys.argv[4]), debug=int(sys.argv[5]))
 
diff --git a/tests/jit/proto.b b/tests/jit/proto.b
new file mode 100644 (file)
index 0000000..8187418
--- /dev/null
@@ -0,0 +1,167 @@
+
+#packed "true"
+
+struct skbuff {
+  u32 len:32;
+  u32 pkt_type:32;
+  u32 mark:32;
+  u32 queue_mapping:32;
+  u32 protocol:32;
+  u32 vlan_present:32;
+  u32 vlan_tci:32;
+  u32 vlan_proto:32;
+  u32 priority:32;
+};
+
+struct ethernet {
+  u64 dst:48;
+  u64 src:48;
+  u32 type:16;
+};
+
+state ethernet {
+  switch $ethernet.type {
+    case 0x0800 {
+      next proto::ip;
+    };
+    case 0x8100 {
+      next proto::dot1q;
+    };
+    case * {
+      goto EOP;
+    };
+  }
+}
+
+
+struct dot1q {
+  u32 pri:3;
+  u32 cfi:1;
+  u32 vlanid:12;
+  u32 type:16;
+};
+
+state dot1q {
+  switch $dot1q.type {
+    case 0x0800 {
+      next proto::ip;
+    };
+    case * {
+      goto EOP;
+    };
+  }
+}
+
+
+struct ip {
+  u32 ver:4;
+  u32 hlen:4;
+  u32 tos:8;
+  u32 tlen:16;
+  u32 identification:16;
+  u32 ffo_unused:1;
+  u32 df:1;
+  u32 mf:1;
+  u32 foffset:13;
+  u32 ttl:8;
+  u32 nextp:8;
+  u32 hchecksum:16;
+  u32 src:32;
+  u32 dst:32;
+};
+
+state ip {
+  switch $ip.nextp {
+    case 6 {
+      next proto::tcp;
+    };
+    case 17 {
+      next proto::udp;
+    };
+    case 47 {
+      next proto::gre;
+    };
+    case * {
+      goto EOP;
+    };
+  }
+}
+
+
+struct udp {
+  u32 sport:16;
+  u32 dport:16;
+  u32 length:16;
+  u32 crc:16;
+};
+
+state udp {
+  switch $udp.dport {
+    case 8472 {
+      next proto::vxlan;
+    };
+    case * {
+      goto EOP;
+    };
+  }
+}
+
+struct tcp {
+  u16 src_port:16;
+  u16 dst_port:16;
+  u32 seq_num:32;
+  u32 ack_num:32;
+  u8 offset:4;
+  u8 reserved:4;
+  u8 flag_cwr:1;
+  u8 flag_ece:1;
+  u8 flag_urg:1;
+  u8 flag_ack:1;
+  u8 flag_psh:1;
+  u8 flag_rst:1;
+  u8 flag_syn:1;
+  u8 flag_fin:1;
+  u16 rcv_wnd:16;
+  u16 cksum:16;
+  u16 urg_ptr:16;
+};
+
+state tcp {
+  goto EOP;
+}
+
+struct vxlan {
+  u32 rsv1:4;
+  u32 iflag:1;
+  u32 rsv2:3;
+  u32 rsv3:24;
+  u32 key:24;
+  u32 rsv4:8;
+};
+
+state vxlan {
+  goto EOP;
+}
+
+
+struct gre {
+  u32 cflag:1;
+  u32 rflag:1;
+  u32 kflag:1;
+  u32 snflag:1;
+  u32 srflag:1;
+  u32 recurflag:3;
+  u32 reserved:5;
+  u32 vflag:3;
+  u32 protocol:16;
+  u32 key:32;
+};
+
+state gre {
+  switch $gre.protocol {
+    case * {
+      goto EOP;
+    };
+  }
+}
+
diff --git a/tests/jit/proto.dph b/tests/jit/proto.dph
deleted file mode 100644 (file)
index e1bd1f0..0000000
+++ /dev/null
@@ -1,155 +0,0 @@
-
-@1.0.0
-#packed 'true'
-
-struct ethernet {
-  dst:48
-  src:48
-  type:16
-}
-
-state ethernet {
-  switch $ethernet.type {
-    case 0x0800 {
-      next proto::ip
-    }
-    case 0x8100 {
-      next proto::dot1q
-    }
-    case * {
-      goto EOP
-    }
-  }
-}
-
-
-struct dot1q {
-  pri:3
-  cfi:1
-  vlanid:12
-  type:16
-}
-
-state dot1q {
-  switch $dot1q.type {
-    case 0x0800 {
-      next proto::ip
-    }
-    case * {
-      goto EOP
-    }
-  }
-}
-
-
-struct ip {
-  ver:4
-  hlen:4
-  tos:8
-  tlen:16
-  identification:16
-  ffo_unused:1
-  df:1
-  mf:1
-  foffset:13
-  ttl:8
-  nextp:8
-  hchecksum:16
-  src:32
-  dst:32
-}
-
-state ip {
-  switch $ip.nextp {
-    case 6 {
-      next proto::tcp
-    }
-    case 17 {
-      next proto::udp
-    }
-    case 47 {
-      next proto::gre
-    }
-    case * {
-      goto EOP
-    }
-  }
-}
-
-
-struct udp {
-  sport:16
-  dport:16
-  length:16
-  crc:16
-}
-
-state udp {
-  switch $udp.dport {
-    case 8472 {
-      next proto::vxlan
-    }
-    case * {
-      goto EOP
-    }
-  }
-}
-
-struct tcp {
-  src_port:16
-  dst_port:16
-  seq_num:32
-  ack_num:32
-  offset:4
-  reserved:4
-  flag_cwr:1
-  flag_ece:1
-  flag_urg:1
-  flag_ack:1
-  flag_psh:1
-  flag_rst:1
-  flag_syn:1
-  flag_fin:1
-  rcv_wnd:16
-  cksum:16
-  urg_ptr:16
-}
-
-state tcp {
-  goto EOP
-}
-
-struct vxlan {
-  rsv1:4
-  iflag:1
-  rsv2:3
-  rsv3:24
-  key:24
-  rsv4:8
-}
-
-state vxlan {
-  goto EOP
-}
-
-
-struct gre {
-  cflag:1
-  rflag:1
-  kflag:1
-  snflag:1
-  srflag:1
-  recurflag:3
-  reserved:5
-  vflag:3
-  protocol:16
-  key:32
-}
-
-state gre {
-  switch $gre.protocol {
-    case * {
-      goto EOP
-    }
-  }
-}
diff --git a/tests/jit/proto_simple.dph b/tests/jit/proto_simple.dph
deleted file mode 100644 (file)
index 086b02f..0000000
+++ /dev/null
@@ -1,17 +0,0 @@
-
-@1.0.0
-#packed 'true'
-
-struct ethernet {
-  dst:48
-  src:48
-  type:16
-}
-
-state ethernet {
-  switch $ethernet.type {
-    case * {
-      goto EOP
-    }
-  }
-}
diff --git a/tests/jit/test1.b b/tests/jit/test1.b
new file mode 100644 (file)
index 0000000..a2c4c5b
--- /dev/null
@@ -0,0 +1,62 @@
+#packed "false"
+
+struct IPKey {
+  u32 dip:32;
+  u32 sip:32;
+};
+struct IPLeaf {
+  u32 rx_pkts:64;
+  u32 tx_pkts:64;
+};
+Table<IPKey, IPLeaf, FIXED_MATCH, AUTO> stats(1024);
+
+u32 main(struct proto::skbuff *skb) {
+  u32 ret:32 = 0;
+
+  goto proto::ethernet;
+
+  state proto::ethernet {
+  }
+
+  state proto::dot1q {
+  }
+
+  state proto::ip {
+    u32 rx:32 = 0;
+    u32 tx:32 = 0;
+    u32 IPKey key;
+    if $ip.dst > $ip.src {
+      key.dip = $ip.dst;
+      key.sip = $ip.src;
+      rx = 1;
+      // test arbitrary return stmt
+      if false {
+        return 3;
+      }
+    } else {
+      key.dip = $ip.src;
+      key.sip = $ip.dst;
+      tx = 1;
+      ret = 1;
+    }
+    struct IPLeaf *leaf;
+    leaf = stats[key];
+    on_valid(leaf) {
+      atomic_add(leaf.rx_pkts, rx);
+      atomic_add(leaf.tx_pkts, tx);
+    }
+  }
+
+  state proto::udp {
+  }
+
+  state proto::vxlan {
+  }
+
+  state proto::gre {
+  }
+
+  state EOP {
+    return ret;
+  }
+}
diff --git a/tests/jit/test1.dp b/tests/jit/test1.dp
deleted file mode 100644 (file)
index 952b232..0000000
+++ /dev/null
@@ -1,45 +0,0 @@
-
-@1.0.0
-#name test1
-struct SomeKey {
-  foo:3
-  foo1:9
-  foo2:63
-}
-struct SomeLeaf {
-  bar:64
-}
-Table<SomeKey, SomeLeaf, FIXED_MATCH, LRU> stats(8)
-
-state INIT {
-  goto proto::ethernet
-}
-
-var ret:32 = 0
-
-state proto::ethernet {
-  var SomeKey sk2{foo = 2}
-  stats.lookup(sk2) {
-    on_match stats (var SomeLeaf *l21) {
-      atomic_add(l21.bar, 1)
-    }
-    on_miss stats () {
-      ret = 1
-    }
-  }
-  var SomeKey sk3{foo = 3}
-  stats.lookup(sk3) {
-  }
-  var SomeKey sk4{foo = 4}
-  var SomeLeaf sl4{bar = 1}
-  stats.update(sk4, sl4) { }
-  var SomeKey sk5{foo = 5}
-  var SomeLeaf sl5{bar = 1}
-  stats.update(sk5, sl5) {
-    on_failure stats (var SomeKey *k21) {}
-  }
-}
-
-state EOP {
-  return ret
-}
diff --git a/tests/jit/test1.py b/tests/jit/test1.py
new file mode 100755 (executable)
index 0000000..b717984
--- /dev/null
@@ -0,0 +1,38 @@
+#!/usr/bin/env python
+
+# test program to count the packets sent to a device in a .5
+# second period
+
+from ctypes import c_uint, c_ulong, Structure
+from netaddr import IPAddress
+from bpf import BPF
+from subprocess import check_call
+from unittest import main, TestCase
+
+class Key(Structure):
+    _fields_ = [("dip", c_uint),
+                ("sip", c_uint)]
+class Leaf(Structure):
+    _fields_ = [("rx_pkts", c_ulong),
+                ("tx_pkts", c_ulong)]
+
+class TestBPFSocket(TestCase):
+    def setUp(self):
+        self.prog = BPF("main", "test1.b", "proto.b", debug=0)
+        self.prog.attach("eth0")
+        self.stats = self.prog.table("stats", Key, Leaf)
+
+    def test_ping(self):
+        cmd = ["ping", "-f", "-c", "100", "172.16.1.1"]
+        check_call(cmd)
+        #for key in self.stats.iter():
+        #    leaf = self.stats.get(key)
+        #    print(IPAddress(key.sip), "=>", IPAddress(key.dip),
+        #          "rx", leaf.rx_pkts, "tx", leaf.tx_pkts)
+        key = Key(IPAddress("172.16.1.2").value, IPAddress("172.16.1.1").value)
+        leaf = self.stats.get(key)
+        self.assertEqual(leaf.rx_pkts, 100)
+        self.assertEqual(leaf.tx_pkts, 100)
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/jit/test2.b b/tests/jit/test2.b
new file mode 100644 (file)
index 0000000..6075961
--- /dev/null
@@ -0,0 +1,68 @@
+// test for packet modification
+
+#packed "false"
+
+struct IPKey {
+  u32 dip:32;
+  u32 sip:32;
+};
+struct IPLeaf {
+  u32 xdip:32;
+  u32 xsip:32;
+  u64 xlated_pkts:64;
+};
+Table<IPKey, IPLeaf, FIXED_MATCH, NONE> xlate(1024);
+
+u32 main (struct proto::skbuff *skb) {
+  u32 ret:32 = 1;
+
+  u32 orig_dip:32 = 0;
+  u32 orig_sip:32 = 0;
+  struct IPLeaf *xleaf;
+
+  goto proto::ethernet;
+
+  state proto::ethernet {
+  }
+
+  state proto::dot1q {
+  }
+
+  state proto::ip {
+    orig_dip = $ip.dst;
+    orig_sip = $ip.src;
+    struct IPKey key = {.dip=orig_dip, .sip=orig_sip};
+    xlate.lookup(key, xleaf) {};
+    on_valid(xleaf) {
+      incr_cksum(@ip.hchecksum, orig_dip, xleaf.xdip);
+      incr_cksum(@ip.hchecksum, orig_sip, xleaf.xsip);
+      pkt.rewrite_field($ip.dst, xleaf.xdip);
+      pkt.rewrite_field($ip.src, xleaf.xsip);
+      atomic_add(xleaf.xlated_pkts, 1);
+    }
+  }
+
+  state proto::udp {
+    on_valid(xleaf) {
+      incr_cksum(@udp.crc, orig_dip, xleaf.xdip, 1);
+      incr_cksum(@udp.crc, orig_sip, xleaf.xsip, 1);
+    }
+  }
+
+  state proto::tcp {
+    on_valid(xleaf) {
+      incr_cksum(@tcp.cksum, orig_dip, xleaf.xdip, 1);
+      incr_cksum(@tcp.cksum, orig_sip, xleaf.xsip, 1);
+    }
+  }
+
+  state proto::vxlan {
+  }
+
+  state proto::gre {
+  }
+
+  state EOP {
+    return ret;
+  }
+}
diff --git a/tests/jit/test2.dp b/tests/jit/test2.dp
deleted file mode 100644 (file)
index 2ad4768..0000000
+++ /dev/null
@@ -1,67 +0,0 @@
-
-@1.0.0
-#name 'socket1'
-#packed 'false'
-
-struct IPKey {
-  dip:32
-  sip:32
-}
-struct IPLeaf {
-  rx_pkts:64
-  tx_pkts:64
-}
-Table<IPKey, IPLeaf, FIXED_MATCH, LRU> stats(1024)
-
-var ret:32 = 0
-
-state INIT {
-  goto proto::ethernet
-}
-
-state proto::ethernet {
-}
-
-state proto::dot1q {
-}
-
-state proto::ip {
-  var rx:32 = 0
-  var tx:32 = 0
-  var IPKey key
-  if $ip.dst > $ip.src {
-    key.dip = $ip.dst
-    key.sip = $ip.src
-    rx = 1
-    if false {
-      return 3
-    }
-  } else {
-    key.dip = $ip.src
-    key.sip = $ip.dst
-    tx = 1
-    ret = 1
-  }
-  var IPLeaf *leaf
-  stats.lookup(key, leaf) {}
-  on_valid(leaf) {
-    atomic_add(leaf.rx_pkts, rx)
-    atomic_add(leaf.tx_pkts, tx)
-  } else {
-    var IPLeaf newleaf{rx_pkts = rx, tx_pkts = tx}
-    stats.update(key, newleaf) {}
-  }
-}
-
-state proto::udp {
-}
-
-state proto::vxlan {
-}
-
-state proto::gre {
-}
-
-state EOP {
-  return ret
-}
old mode 100644 (file)
new mode 100755 (executable)
index 2bbed54..914793a
@@ -1,28 +1,37 @@
 #!/usr/bin/env python
 
-# test program to count the packets sent to a device in a .5
-# second period
-
-import time
-import netaddr
-from ctypes import *
-from src.bpf import BPF
-
-prog = BPF("socket1", "tests/test2.dp", "tests/proto.dph")
+from ctypes import c_uint, c_ulonglong, Structure
+from netaddr import IPAddress
+from bpf import BPF
+from socket import socket, AF_INET, SOCK_DGRAM
+from time import sleep
+from unittest import main, TestCase
 
 class Key(Structure):
     _fields_ = [("dip", c_uint),
                 ("sip", c_uint)]
 class Leaf(Structure):
-    _fields_ = [("rx_pkts", c_ulong),
-                ("tx_pkts", c_ulong)]
+    _fields_ = [("xdip", c_uint),
+                ("xsip", c_uint),
+                ("xlated_pkts", c_ulonglong)]
 
-prog.attach("eth0")
-stats = prog.table("stats", Key, Leaf)
+class TestBPFSocket(TestCase):
+    def setUp(self):
+        self.prog = BPF("main", "test2.b", "proto.b",
+                BPF.BPF_PROG_TYPE_SCHED_CLS, debug=0)
+        with open("/sys/class/net/eth0/ifindex") as f:
+            ifindex = int(f.read())
+        self.prog.attach_filter(ifindex, 10, 1)
 
-time.sleep(0.5)
+    def test_xlate(self):
+        xlate = self.prog.table("xlate", Key, Leaf)
+        key = Key(IPAddress("172.16.1.1").value, IPAddress("172.16.1.2").value)
+        leaf = Leaf(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value, 0)
+        xlate.put(key, leaf)
+        udp = socket(AF_INET, SOCK_DGRAM)
+        udp.sendto(b"a" * 10, ("172.16.1.1", 5000))
+        leaf = xlate.get(key)
+        self.assertGreater(leaf.xlated_pkts, 0)
 
-for key in stats.iter():
-    leaf = stats.get(key)
-    print(netaddr.IPAddress(key.sip), "=>", netaddr.IPAddress(key.dip),
-          "rx", leaf.rx_pkts, "tx", leaf.tx_pkts)
+if __name__ == "__main__":
+    main()
diff --git a/tests/jit/test3.dp b/tests/jit/test3.dp
deleted file mode 100644 (file)
index 564f17c..0000000
+++ /dev/null
@@ -1,70 +0,0 @@
-// test for packet modification
-
-@1.0.0
-#name 'classifier'
-#packed 'false'
-
-struct IPKey {
-  dip:32
-  sip:32
-}
-struct IPLeaf {
-  xdip:32
-  xsip:32
-  xlated_pkts:64
-}
-Table<IPKey, IPLeaf, FIXED_MATCH, LRU> xlate(1024)
-
-var ret:32 = 1
-
-var orig_dip:32 = 0
-var orig_sip:32 = 0
-var IPLeaf *xleaf;
-
-state INIT {
-  goto proto::ethernet
-}
-
-state proto::ethernet {
-}
-
-state proto::dot1q {
-}
-
-state proto::ip {
-  orig_dip = $ip.dst
-  orig_sip = $ip.src
-  var IPKey key{dip=orig_dip, sip=orig_sip}
-  xlate.lookup(key, xleaf) {}
-  on_valid(xleaf) {
-    incr_cksum(@ip.hchecksum, orig_dip, xleaf.xdip)
-    incr_cksum(@ip.hchecksum, orig_sip, xleaf.xsip)
-    pkt.rewrite_field($ip.dst, xleaf.xdip)
-    pkt.rewrite_field($ip.src, xleaf.xsip)
-    atomic_add(xleaf.xlated_pkts, 1)
-  }
-}
-
-state proto::udp {
-  on_valid(xleaf) {
-    incr_cksum(@udp.crc, orig_dip, xleaf.xdip, 1)
-    incr_cksum(@udp.crc, orig_sip, xleaf.xsip, 1)
-  }
-}
-
-state proto::tcp {
-  on_valid(xleaf) {
-    incr_cksum(@tcp.cksum, orig_dip, xleaf.xdip, 1)
-    incr_cksum(@tcp.cksum, orig_sip, xleaf.xsip, 1)
-  }
-}
-
-state proto::vxlan {
-}
-
-state proto::gre {
-}
-
-state EOP {
-  return ret
-}
diff --git a/tests/jit/test3.py b/tests/jit/test3.py
deleted file mode 100644 (file)
index a114a26..0000000
+++ /dev/null
@@ -1,29 +0,0 @@
-#!/usr/bin/env python
-
-import time
-from netaddr import IPAddress
-from ctypes import *
-from src.bpf import BPF
-
-prog = BPF("classifier", "tests/test3.dp", "tests/proto.dph",
-        BPF.BPF_PROG_TYPE_SCHED_CLS, debug=1)
-
-class Key(Structure):
-    _fields_ = [("dip", c_uint),
-                ("sip", c_uint)]
-class Leaf(Structure):
-    _fields_ = [("xdip", c_uint),
-                ("xsip", c_uint),
-                ("xlated_pkts", c_ulonglong)]
-
-prog.attach_filter(4, 10, 1)
-xlate = prog.table("xlate", Key, Leaf)
-xlate.put(Key(IPAddress("172.16.2.1").value, IPAddress("172.16.2.2").value),
-        Leaf(IPAddress("192.168.1.1").value, IPAddress("192.168.1.2").value, 0))
-while True:
-    print("==============================")
-    for key in xlate.iter():
-        leaf = xlate.get(key)
-        print(IPAddress(key.sip), "=>", IPAddress(key.dip),
-              "xlated_pkts", leaf.xlated_pkts)
-    time.sleep(1)
diff --git a/tests/jit/trace1.b b/tests/jit/trace1.b
new file mode 100644 (file)
index 0000000..ebd2f31
--- /dev/null
@@ -0,0 +1,41 @@
+struct Ptr {
+  u64 ptr:64;
+};
+struct Counters {
+  u64 stat1:64;
+  u64 stat2:64;
+};
+Table<Ptr, Counters, FIXED_MATCH, AUTO> stats(1024);
+
+// example with on_valid syntax
+u32 sys_wr (struct proto::pt_regs *ctx) {
+  struct Ptr key = {.ptr=ctx->di};
+  struct Counters *leaf;
+  leaf = stats[key];
+  if leaf {
+    atomic_add(leaf->stat2, 1);
+  }
+  log("sys_wr: %p\n", ctx->di);
+  return 0;
+}
+
+// example with smallest available syntax
+// note: if stats[key] fails, program returns early
+u32 sys_rd (struct proto::pt_regs *ctx) {
+  struct Ptr key = {.ptr=ctx->di};
+  atomic_add(stats[key].stat1, 1);
+}
+
+// example with if/else case
+u32 sys_bpf (struct proto::pt_regs *ctx) {
+  struct Ptr key = {.ptr=ctx->di};
+  struct Counters *leaf;
+  leaf = stats[key];
+  if leaf {
+    atomic_add(leaf->stat1, 1);
+  } else {
+    log("update %llx failed\n", ctx->di);
+  }
+  return 0;
+}
+
diff --git a/tests/jit/trace1.py b/tests/jit/trace1.py
new file mode 100755 (executable)
index 0000000..6d6dcc2
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env python
+
+from ctypes import c_uint, c_ulong, Structure
+from bpf import BPF
+import os
+from time import sleep
+from unittest import main, TestCase
+
+class Key(Structure):
+    _fields_ = [("fd", c_ulong)]
+class Leaf(Structure):
+    _fields_ = [("stat1", c_ulong),
+                ("stat2", c_ulong)]
+
+class TestKprobe(TestCase):
+    def setUp(self):
+        self.prog = BPF("trace1", "trace1.b", "kprobe.b",
+                prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
+        self.prog.load("sys_wr")
+        self.prog.load("sys_rd")
+        self.prog.load("sys_bpf")
+        self.stats = self.prog.table("stats", Key, Leaf)
+        self.prog.attach_kprobe("sys_write", "sys_wr", 0, -1)
+        self.prog.attach_kprobe("sys_read", "sys_rd", 0, -1)
+        self.prog.attach_kprobe("htab_map_get_next_key", "sys_bpf")
+
+    def test_trace1(self):
+        with open("/dev/null", "a") as f:
+            for i in range(0, 100):
+                os.write(f.fileno(), b"")
+        with open("/etc/services", "r") as f:
+            for i in range(0, 200):
+                os.read(f.fileno(), 1)
+        for key in self.stats.iter():
+            leaf = self.stats.get(key)
+            print("fd %x:" % key.fd, "stat1 %d" % leaf.stat1, "stat2 %d" % leaf.stat2)
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/jit/trace2.b b/tests/jit/trace2.b
new file mode 100644 (file)
index 0000000..df5aa4a
--- /dev/null
@@ -0,0 +1,9 @@
+#include "kprobe.b"
+struct Ptr { u64 ptr:64; };
+struct Counters { u64 stat1:64; };
+Table<Ptr, Counters, FIXED_MATCH, AUTO> stats(1024);
+
+u32 count_sched (struct proto::pt_regs *ctx) {
+  struct Ptr key = {.ptr=ctx->bx};
+  atomic_add(stats[key].stat1, 1);
+}
diff --git a/tests/jit/trace2.py b/tests/jit/trace2.py
new file mode 100755 (executable)
index 0000000..014a5ec
--- /dev/null
@@ -0,0 +1,30 @@
+#!/usr/bin/env python
+
+from ctypes import c_uint, c_ulong, Structure
+from bpf import BPF
+from time import sleep
+from unittest import main, TestCase
+
+class Ptr(Structure):
+    _fields_ = [("ptr", c_ulong)]
+class Counters(Structure):
+    _fields_ = [("stat1", c_ulong)]
+
+tracing = "/sys/kernel/debug/tracing"
+class TestTracingEvent(TestCase):
+    def setUp(self):
+        self.prog = BPF("trace2", "trace2.b", "kprobe.b",
+                prog_type=BPF.BPF_PROG_TYPE_KPROBE, debug=0)
+        self.prog.load("count_sched")
+        self.stats = self.prog.table("stats", Ptr, Counters)
+        self.prog.attach_kprobe("schedule+50", "count_sched", 0, -1)
+
+    def test_sched1(self):
+        for i in range(0, 100):
+            sleep(0.01)
+        for key in self.stats.iter():
+            leaf = self.stats.get(key)
+            print("ptr %x:" % key.ptr, "stat1 %x" % leaf.stat1)
+
+if __name__ == "__main__":
+    main()
diff --git a/tests/wrapper.sh.in b/tests/wrapper.sh.in
new file mode 100755 (executable)
index 0000000..38f9cb6
--- /dev/null
@@ -0,0 +1,55 @@
+#!/bin/bash
+
+#set -x
+
+name=$1; shift
+cmd=$1; shift
+kind=$1; shift
+
+PYTHONPATH=@CMAKE_SOURCE_DIR@/src
+LD_LIBRARY_PATH=@CMAKE_BINARY_DIR@:@CMAKE_BINARY_DIR@/src/cc
+
+ns=$name
+
+function cleanup() {
+  trap - EXIT
+  if [[ "$kind" = "namespace" ]]; then
+    sudo ip netns delete $ns
+  fi
+}
+
+trap cleanup EXIT
+
+function ns_run() {
+  sudo ip netns add $ns
+  sudo ip link add $ns.in type veth peer name $ns.out
+  sudo ip link set $ns.in netns $ns
+  sudo ip netns exec $ns ip link set $ns.in name eth0
+  sudo ip netns exec $ns ip addr add dev eth0 172.16.1.2/24
+  sudo ip netns exec $ns ip link set eth0 up
+  sudo ip addr add dev $ns.out 172.16.1.1/24
+  sudo ip link set $ns.out up
+  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH ip netns exec $ns $cmd $@"
+  return $?
+}
+function sudo_run() {
+  sudo bash -c "PYTHONPATH=$PYTHONPATH LD_LIBRARY_PATH=$LD_LIBRARY_PATH $cmd $@"
+  return $?
+}
+
+case $kind in
+  namespace)
+    ns_run $@
+    ;;
+  sudo)
+    sudo_run $@
+    ;;
+  *)
+    echo "Invalid kind $kind"
+    exit 1
+    ;;
+esac
+
+[[ $? -ne 0 ]] && { echo "Failed"; exit 1; }
+
+exit 0