blob: eb558416b8e41d7629729d0a526e559ac9f6fa6a [file] [log] [blame]
/*
* =====================================================================
* 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