Brenden Blanco | a94bd93 | 2015-04-26 00:56:42 -0700 | [diff] [blame^] | 1 | /* |
| 2 | * ===================================================================== |
| 3 | * Copyright (c) 2012, PLUMgrid, http://plumgrid.com |
| 4 | * |
| 5 | * This source is subject to the PLUMgrid License. |
| 6 | * All rights reserved. |
| 7 | * |
| 8 | * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF |
| 9 | * ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO |
| 10 | * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A |
| 11 | * PARTICULAR PURPOSE. |
| 12 | * |
| 13 | * PLUMgrid confidential information, delete if you are not the |
| 14 | * intended recipient. |
| 15 | * |
| 16 | * ===================================================================== |
| 17 | */ |
| 18 | |
| 19 | #include <set> |
| 20 | #include <algorithm> |
| 21 | #include <sstream> |
| 22 | #include <assert.h> |
| 23 | #include "exception.h" |
| 24 | #include "cc/codegen_c.h" |
| 25 | #include "cc/lexer.h" |
| 26 | #include "cc/type_helper.h" |
| 27 | |
| 28 | namespace ebpf { |
| 29 | namespace cc { |
| 30 | |
| 31 | using std::set; |
| 32 | using std::for_each; |
| 33 | using std::pair; |
| 34 | using std::stringstream; |
| 35 | |
| 36 | template <typename... Args> |
| 37 | void CodegenC::emitln(const char *fmt, Args&&... params) { |
| 38 | fprintf(out_, fmt, std::forward<Args>(params)...); |
| 39 | fprintf(out_, "\n%*s", indent_ * 2, ""); |
| 40 | } |
| 41 | void CodegenC::emitln(const char *s) { |
| 42 | fprintf(out_, "%s", s); |
| 43 | fprintf(out_, "\n%*s", indent_ * 2, ""); |
| 44 | } |
| 45 | |
| 46 | template <typename... Args> |
| 47 | void CodegenC::emit(const char *fmt, Args&&... params) { |
| 48 | fprintf(out_, fmt, std::forward<Args>(params)...); |
| 49 | } |
| 50 | void CodegenC::emit(const char *s) { |
| 51 | fprintf(out_, "%s", s); |
| 52 | } |
| 53 | |
| 54 | template <typename... Args> |
| 55 | void CodegenC::lnemit(const char *fmt, Args&&... params) { |
| 56 | fprintf(out_, "\n%*s", indent_ * 2, ""); |
| 57 | fprintf(out_, fmt, std::forward<Args>(params)...); |
| 58 | } |
| 59 | void CodegenC::lnemit(const char *s) { |
| 60 | fprintf(out_, "\n%*s", indent_ * 2, ""); |
| 61 | fprintf(out_, "%s", s); |
| 62 | } |
| 63 | |
| 64 | void CodegenC::indent() { |
| 65 | fprintf(out_, "%*s", indent_ * 2, ""); |
| 66 | } |
| 67 | |
| 68 | void CodegenC::emit_comment(Node* n) { |
| 69 | // if (!n->text_.empty()) { |
| 70 | // emitln("/* %s */", n->text_.c_str()); |
| 71 | // } |
| 72 | } |
| 73 | |
| 74 | void CodegenC::visit_block_stmt_node(BlockStmtNode* n) { |
| 75 | |
| 76 | ++indent_; |
| 77 | emit("{"); |
| 78 | |
| 79 | // enter scope |
| 80 | auto scope = scopes_->current_var(); |
| 81 | if (n->scope_) { |
| 82 | scopes_->set_current(n->scope_); |
| 83 | } |
| 84 | |
| 85 | |
| 86 | if (!n->stmts_.empty()) { |
| 87 | for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { |
| 88 | emitln(""); |
| 89 | (*it)->accept(this); |
| 90 | } |
| 91 | } |
| 92 | // exit scope |
| 93 | scopes_->set_current(scope); |
| 94 | |
| 95 | --indent_; |
| 96 | emitln(""); |
| 97 | emit("}"); |
| 98 | } |
| 99 | |
| 100 | void CodegenC::visit_version_stmt_node(VersionStmtNode* n) { |
| 101 | uint32_t version; |
| 102 | version = MAKE_VERSION(n->major_, n->minor_, n->rev_); |
| 103 | emit("static const uint32_t plumlet_version __attribute__" |
| 104 | "((section (\".version\"), used)) = 0x%x;\n", version); |
| 105 | } |
| 106 | |
| 107 | void CodegenC::visit_if_stmt_node(IfStmtNode* n) { |
| 108 | emit_comment(n); |
| 109 | emit("if ("); |
| 110 | n->cond_->accept(this); |
| 111 | emit(") "); |
| 112 | n->true_block_->accept(this); |
| 113 | if (n->false_block_) { |
| 114 | emit(" else "); |
| 115 | n->false_block_->accept(this); |
| 116 | } |
| 117 | } |
| 118 | |
| 119 | void CodegenC::visit_onvalid_stmt_node(OnValidStmtNode* n) { |
| 120 | auto sdecl = static_cast<StructVariableDeclStmtNode*>(n->cond_->decl_); |
| 121 | emit_comment(n); |
| 122 | // cheat a little not using n->cond_->accept(this) to prevent the dereference |
| 123 | emit("if (%s%s) ", sdecl->scope_id(), sdecl->id_->c_str()); |
| 124 | n->block_->accept(this); |
| 125 | if (n->else_block_) { |
| 126 | emit(" else "); |
| 127 | n->else_block_->accept(this); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | void CodegenC::visit_switch_stmt_node(SwitchStmtNode* n) { |
| 132 | emit_comment(n); |
| 133 | emit("switch ("); |
| 134 | n->cond_->accept(this); |
| 135 | emit(") "); |
| 136 | n->block_->accept(this); |
| 137 | } |
| 138 | |
| 139 | void CodegenC::visit_case_stmt_node(CaseStmtNode* n) { |
| 140 | if (n->value_) { |
| 141 | emit("case "); |
| 142 | n->value_->accept(this); |
| 143 | } else { |
| 144 | emit("default"); |
| 145 | } |
| 146 | emit(": "); |
| 147 | ++indent_; |
| 148 | n->block_->accept(this); |
| 149 | emitln(""); |
| 150 | emit("break;"); |
| 151 | --indent_; |
| 152 | } |
| 153 | |
| 154 | void CodegenC::visit_ident_expr_node(IdentExprNode* n) { |
| 155 | if (!n->decl_) |
| 156 | throw CompilerException("variable lookup failed: %s", n->name_.c_str()); |
| 157 | if (n->decl_->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) { |
| 158 | if (n->sub_name_.size()) { |
| 159 | if (n->bitop_) { |
| 160 | // ident is holding a host endian number, don't use dext |
| 161 | if (n->flags_[ExprNode::IS_LHS]) { |
| 162 | emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); |
| 163 | } else { |
| 164 | emit("(((%s%s->%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str(), |
| 165 | n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_); |
| 166 | } |
| 167 | } else { |
| 168 | if (n->struct_type_->id_->name_ == "_Packet" && n->sub_name_.substr(0, 3) == "arg") { |
| 169 | // convert arg1~arg8 into args[0]~args[7] assuming type_check verified the range already |
| 170 | auto arg_num = stoi(n->sub_name_.substr(3, 3)); |
| 171 | if (arg_num < 5) { |
| 172 | emit("%s%s->args_lo[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 1); |
| 173 | } else { |
| 174 | emit("%s%s->args_hi[%d]", n->decl_->scope_id(), n->c_str(), arg_num - 5); |
| 175 | } |
| 176 | } else { |
| 177 | emit("%s%s->%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); |
| 178 | } |
| 179 | } |
| 180 | } else { |
| 181 | emit("*%s%s", n->decl_->scope_id(), n->c_str()); |
| 182 | } |
| 183 | } else { |
| 184 | if (n->sub_name_.size()) { |
| 185 | emit("%s%s.%s", n->decl_->scope_id(), n->c_str(), n->sub_name_.c_str()); |
| 186 | } else { |
| 187 | if (n->bitop_) { |
| 188 | // ident is holding a host endian number, don't use dext |
| 189 | if (n->flags_[ExprNode::IS_LHS]) { |
| 190 | assert(0); |
| 191 | } else { |
| 192 | emit("(((%s%s) >> %d) & (((%s)1 << %d) - 1))", n->decl_->scope_id(), n->c_str(), |
| 193 | n->bitop_->bit_offset_, bits_to_uint(n->bitop_->bit_width_ + 1), n->bitop_->bit_width_); |
| 194 | } |
| 195 | } else { |
| 196 | emit("%s%s", n->decl_->scope_id(), n->c_str()); |
| 197 | } |
| 198 | } |
| 199 | } |
| 200 | } |
| 201 | |
| 202 | void CodegenC::visit_assign_expr_node(AssignExprNode* n) { |
| 203 | if (n->bitop_) { |
| 204 | n->id_->accept(this); |
| 205 | emit(" = ("); |
| 206 | n->id_->accept(this); |
| 207 | emit(" & ~((((%s)1 << %d) - 1) << %d)) | (", bits_to_uint(n->id_->bit_width_), |
| 208 | n->bitop_->bit_width_, n->bitop_->bit_offset_); |
| 209 | n->rhs_->accept(this); |
| 210 | emit(" << %d)", n->bitop_->bit_offset_); |
| 211 | } else { |
| 212 | if (n->id_->flags_[ExprNode::PROTO]) { |
| 213 | auto f = n->id_->struct_type_->field(n->id_->sub_name_); |
| 214 | emit("bpf_dins(%s%s + %zu, %zu, %zu, ", n->id_->decl_->scope_id(), n->id_->c_str(), |
| 215 | f->bit_offset_ >> 3, f->bit_offset_ & 0x7, f->bit_width_); |
| 216 | n->rhs_->accept(this); |
| 217 | emit(")"); |
| 218 | } else { |
| 219 | n->id_->accept(this); |
| 220 | emit(" = "); |
| 221 | n->rhs_->accept(this); |
| 222 | } |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | void CodegenC::visit_packet_expr_node(PacketExprNode* n) { |
| 227 | auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true); |
| 228 | if (p) { |
| 229 | auto f = p->field(n->id_->sub_name_); |
| 230 | if (f) { |
| 231 | size_t bit_offset = f->bit_offset_; |
| 232 | size_t bit_width = f->bit_width_; |
| 233 | if (n->bitop_) { |
| 234 | bit_offset += f->bit_width_ - (n->bitop_->bit_offset_ + n->bitop_->bit_width_); |
| 235 | bit_width = std::min(bit_width - n->bitop_->bit_offset_, n->bitop_->bit_width_); |
| 236 | } |
| 237 | if (n->flags_[ExprNode::IS_LHS]) |
| 238 | emit("bpf_dins_pkt(pkt, %s + %zu, %zu, %zu, ", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); |
| 239 | else |
| 240 | emit("bpf_dext_pkt(pkt, %s + %zu, %zu, %zu)", n->id_->c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); |
| 241 | } else { |
| 242 | emit("pkt->start + pkt->offset + %s", n->id_->c_str()); |
| 243 | } |
| 244 | } |
| 245 | } |
| 246 | |
| 247 | void CodegenC::visit_integer_expr_node(IntegerExprNode* n) { |
| 248 | emit("%s", n->val_.c_str()); |
| 249 | } |
| 250 | |
| 251 | void CodegenC::visit_binop_expr_node(BinopExprNode* n) { |
| 252 | n->lhs_->accept(this); |
| 253 | switch (n->op_) { |
| 254 | case Tok::TCEQ: emit(" == "); break; |
| 255 | case Tok::TCNE: emit(" != "); break; |
| 256 | case Tok::TXOR: emit(" ^ "); break; |
| 257 | case Tok::TAND: emit(" && "); break; |
| 258 | case Tok::TOR: emit(" || "); break; |
| 259 | case Tok::TMOD: emit("%"); break; |
| 260 | case Tok::TCLT: emit(" < "); break; |
| 261 | case Tok::TCLE: emit(" <= "); break; |
| 262 | case Tok::TCGT: emit(" > "); break; |
| 263 | case Tok::TCGE: emit(" >= "); break; |
| 264 | case Tok::TPLUS: emit(" + "); break; |
| 265 | case Tok::TMINUS: emit(" - "); break; |
| 266 | case Tok::TLAND: emit(" & "); break; |
| 267 | case Tok::TLOR: emit(" | "); break; |
| 268 | default: emit(" ?%d? ", n->op_); break; |
| 269 | } |
| 270 | n->rhs_->accept(this); |
| 271 | } |
| 272 | |
| 273 | void CodegenC::visit_unop_expr_node(UnopExprNode* n) { |
| 274 | const char* s = ""; |
| 275 | switch (n->op_) { |
| 276 | case Tok::TNOT: s = "!"; break; |
| 277 | case Tok::TCMPL: s = "~"; break; |
| 278 | default: {} |
| 279 | } |
| 280 | emit("%s", s); |
| 281 | n->expr_->accept(this); |
| 282 | } |
| 283 | |
| 284 | void CodegenC::visit_bitop_expr_node(BitopExprNode* n) { |
| 285 | } |
| 286 | |
| 287 | void CodegenC::visit_goto_expr_node(GotoExprNode* n) { |
| 288 | if (n->id_->name_ == "DONE") { |
| 289 | for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii) |
| 290 | for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj) |
| 291 | emitln("%s;", jj->c_str()); |
| 292 | emit("goto DONE"); |
| 293 | return; |
| 294 | } |
| 295 | string jump_label; |
| 296 | // when dealing with multistates, goto statements may be overridden |
| 297 | auto rewrite_it = proto_rewrites_.find(n->id_->full_name()); |
| 298 | auto default_it = proto_rewrites_.find(""); |
| 299 | if (rewrite_it != proto_rewrites_.end()) { |
| 300 | jump_label = rewrite_it->second; |
| 301 | } else if (default_it != proto_rewrites_.end()) { |
| 302 | jump_label = default_it->second; |
| 303 | } else { |
| 304 | auto state = scopes_->current_state()->lookup(n->id_->full_name(), false); |
| 305 | if (state) { |
| 306 | jump_label = state->scoped_name(); |
| 307 | if (n->is_continue_) { |
| 308 | jump_label += "_continue"; |
| 309 | } |
| 310 | } else { |
| 311 | state = scopes_->current_state()->lookup("EOP", false); |
| 312 | if (state) { |
| 313 | jump_label = state->scoped_name(); |
| 314 | } |
| 315 | } |
| 316 | } |
| 317 | for (auto ii = free_instructions_.rbegin(); ii != free_instructions_.rend(); ++ii) |
| 318 | for (auto jj = ii->rbegin(); jj != ii->rend(); ++jj) |
| 319 | emitln("%s;", jj->c_str()); |
| 320 | emit("goto %s", jump_label.c_str()); |
| 321 | } |
| 322 | |
| 323 | void CodegenC::emit_table_lookup(MethodCallExprNode* n) { |
| 324 | TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); |
| 325 | IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get()); |
| 326 | stringstream free_inst; |
| 327 | IdentExprNode* arg1; |
| 328 | StructVariableDeclStmtNode* arg1_type; |
| 329 | |
| 330 | emitln("{ if (unlikely(pkt->capture)) {"); |
| 331 | emitln(" bpf_capture(pkt, BPF_CAP_TABLE_LOOKUP, TABLE_ID_%s, 0);", n->id_->c_str()); |
| 332 | emitln("} }"); |
| 333 | emit("%s* %s_key = &", table->key_id()->c_str(), n->id_->c_str()); |
| 334 | arg0->accept(this); |
| 335 | emitln(";"); |
| 336 | emitln("%s *%s_element = (%s*)", |
| 337 | table->leaf_id()->c_str(), n->id_->c_str(), table->leaf_id()->c_str()); |
| 338 | if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED" || |
| 339 | table->type_id()->name_ == "LPM") { |
| 340 | emit(" bpf_table_lookup(pkt, TABLE_ID_%s, %s_key)", n->id_->c_str(), n->id_->c_str()); |
| 341 | if (n->args_.size() == 2) { |
| 342 | arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get()); |
| 343 | arg1_type = static_cast<StructVariableDeclStmtNode*>(arg1->decl_); |
| 344 | if (table->leaf_id()->name_ != arg1_type->struct_id_->name_) { |
| 345 | throw CompilerException("lookup pointer type mismatch %s != %s", table->leaf_id()->c_str(), |
| 346 | arg1_type->struct_id_->c_str()); |
| 347 | } |
| 348 | emitln(";"); |
| 349 | // cheat a little not using arg1->accept(this) to prevent the dereference |
| 350 | emit("%s%s = %s_element", arg1_type->scope_id(), arg1_type->id_->c_str(), n->id_->c_str()); |
| 351 | } |
| 352 | } else { |
| 353 | throw CompilerException("lookup in table type %s unsupported", table->type_id()->c_str()); |
| 354 | } |
| 355 | free_instructions_.back().push_back(free_inst.str()); |
| 356 | } |
| 357 | |
| 358 | void CodegenC::emit_table_update(MethodCallExprNode* n) { |
| 359 | TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); |
| 360 | IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get()); |
| 361 | IdentExprNode* arg1 = static_cast<IdentExprNode*>(n->args_.at(1).get()); |
| 362 | IdentExprNode* type0 = table->templates_.at(0).get(); |
| 363 | |
| 364 | emit("%s* %s_ukey = &", type0->c_str(), n->id_->c_str()); |
| 365 | arg0->accept(this); |
| 366 | emitln(";"); |
| 367 | if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") { |
| 368 | emit("bpf_table_update(pkt, TABLE_ID_%s, %s_ukey", n->id_->c_str(), n->id_->c_str()); |
| 369 | emit(", &"); |
| 370 | arg1->accept(this); |
| 371 | emitln(");"); |
| 372 | } else if (table->type_id()->name_ == "LPM") { |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | void CodegenC::emit_table_delete(MethodCallExprNode* n) { |
| 377 | TableDeclStmtNode* table = scopes_->top_table()->lookup(n->id_->name_); |
| 378 | IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get()); |
| 379 | IdentExprNode* type0 = table->templates_.at(0).get(); |
| 380 | |
| 381 | emit("%s* %s_dkey = &", type0->c_str(), n->id_->c_str()); |
| 382 | arg0->accept(this); |
| 383 | emitln(";"); |
| 384 | if (table->type_id()->name_ == "FIXED_MATCH" || table->type_id()->name_ == "INDEXED") { |
| 385 | emit("bpf_table_delete(pkt, TABLE_ID_%s, %s_dkey", n->id_->c_str(), n->id_->c_str()); |
| 386 | emitln(");"); |
| 387 | } else if (table->type_id()->name_ == "LPM") { |
| 388 | } |
| 389 | } |
| 390 | |
| 391 | void CodegenC::emit_channel_push_generic(MethodCallExprNode* n) { |
| 392 | /* computation of orig_length of packet: |
| 393 | * orig_lenth = pkt->length - (orig_offset - pkt->offset) |
| 394 | * push_header(N) does pkt->length += N; pkt->offset -= N; |
| 395 | * pop_header(N) does pg_may_access(N); pkt->length -=N; pkt->offset +=N; |
| 396 | * |
| 397 | * therefore push_header(); pop_header(); sequence is currently broken, ticket #930 |
| 398 | */ |
| 399 | emit("bpf_channel_push_packet(pkt"); |
| 400 | emit(")"); |
| 401 | } |
| 402 | |
| 403 | void CodegenC::emit_channel_push(MethodCallExprNode* n) { |
| 404 | IdentExprNode* arg0 = static_cast<IdentExprNode*>(n->args_.at(0).get()); |
| 405 | StructVariableDeclStmtNode* arg0_type = static_cast<StructVariableDeclStmtNode*>(arg0->decl_); |
| 406 | emit("bpf_channel_push_struct(pkt, STRUCTID_%s, &", arg0_type->struct_id_->c_str()); |
| 407 | arg0->accept(this); |
| 408 | emit(", sizeof("); |
| 409 | arg0->accept(this); |
| 410 | emit("))"); |
| 411 | } |
| 412 | |
| 413 | void CodegenC::emit_log(MethodCallExprNode* n) { |
| 414 | emitln("{ if (unlikely(pkt->capture)) {"); |
| 415 | emit(" bpf_capture(pkt, BPF_CAP_LOG, %d, ", n->line_); |
| 416 | n->args_[0]->accept(this); |
| 417 | emit("); } }"); |
| 418 | } |
| 419 | |
| 420 | void CodegenC::emit_packet_forward(MethodCallExprNode* n) { |
| 421 | emitln("pkt->arg1 &= ~1;"); |
| 422 | emit("bpf_forward(pkt, "); |
| 423 | n->args_[0]->accept(this); |
| 424 | emit(")"); |
| 425 | } |
| 426 | |
| 427 | void CodegenC::emit_packet_replicate(MethodCallExprNode*n) { |
| 428 | emitln("pkt->arg1 &= ~1;"); |
| 429 | emit("bpf_replicate(pkt, "); |
| 430 | n->args_[0]->accept(this); |
| 431 | emit(",", n->id_->c_str()); |
| 432 | n->args_[1]->accept(this); |
| 433 | emit(")"); |
| 434 | } |
| 435 | |
| 436 | void CodegenC::emit_packet_clone_forward(MethodCallExprNode* n) { |
| 437 | emitln("pkt->arg1 &= ~1;"); |
| 438 | emit("bpf_clone_forward(pkt, "); |
| 439 | n->args_[0]->accept(this); |
| 440 | emit(")"); |
| 441 | } |
| 442 | |
| 443 | void CodegenC::emit_packet_forward_self(MethodCallExprNode* n) { |
| 444 | emit("bpf_forward_self(pkt, "); |
| 445 | n->args_[0]->accept(this); |
| 446 | emit(")"); |
| 447 | } |
| 448 | |
| 449 | void CodegenC::emit_packet_drop(MethodCallExprNode* n) { |
| 450 | emit("bpf_drop(pkt)"); |
| 451 | } |
| 452 | |
| 453 | void CodegenC::emit_packet_push_header(MethodCallExprNode* n) { |
| 454 | emit("if (unlikely(bpf_push_header(pkt, "); |
| 455 | n->args_[0]->accept(this); |
| 456 | if (n->args_.size() == 1) { |
| 457 | emit(", %zu, 0) != 0)) goto ERROR", n->args_[0]->struct_type_->bit_width_ >> 3); |
| 458 | } else { |
| 459 | emit(", %zu, ", n->args_[0]->struct_type_->bit_width_ >> 3); |
| 460 | n->args_[1]->accept(this); |
| 461 | emit(") != 0)) goto ERROR"); |
| 462 | } |
| 463 | } |
| 464 | |
| 465 | void CodegenC::emit_packet_pop_header(MethodCallExprNode* n) { |
| 466 | emit("if (unlikely(bpf_pop_header(pkt, "); |
| 467 | if (n->args_[0]->typeof_ == ExprNode::STRUCT) { |
| 468 | emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3); |
| 469 | } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) { |
| 470 | n->args_[0]->accept(this); |
| 471 | } |
| 472 | emit(", 0/*todo*/) != 0)) goto ERROR"); |
| 473 | } |
| 474 | |
| 475 | void CodegenC::emit_packet_push_vlan(MethodCallExprNode* n) { |
| 476 | emit("if (unlikely(bpf_push_vlan(pkt, bpf_htons(0x8100/*ETH_P_8021Q*/), "); |
| 477 | n->args_[0]->accept(this); |
| 478 | emit(") != 0)) goto ERROR"); |
| 479 | } |
| 480 | |
| 481 | void CodegenC::emit_packet_pop_vlan(MethodCallExprNode* n) { |
| 482 | emit("if (unlikely(bpf_pop_vlan(pkt) != 0)) goto ERROR"); |
| 483 | } |
| 484 | |
| 485 | void CodegenC::emit_packet_rewrite_field(MethodCallExprNode* n) { |
| 486 | n->args_[0]->accept(this); |
| 487 | n->args_[1]->accept(this); |
| 488 | emit(")"); |
| 489 | } |
| 490 | |
| 491 | void CodegenC::emit_atomic_add(MethodCallExprNode* n) { |
| 492 | emit("__sync_fetch_and_add(&"); |
| 493 | n->args_[0]->accept(this); |
| 494 | emit(", "); |
| 495 | n->args_[1]->accept(this); |
| 496 | emit(")"); |
| 497 | } |
| 498 | |
| 499 | void CodegenC::emit_cksum(MethodCallExprNode* n) { |
| 500 | if (n->args_[0]->typeof_ == ExprNode::STRUCT) { |
| 501 | auto v = n->args_[0]->struct_type_; |
| 502 | size_t bit_width = v->bit_width_ >> 3; |
| 503 | auto p = proto_scopes_->top_struct()->lookup(v->id_->name_, true); |
| 504 | if (p) { |
| 505 | /* should we do store_half directly? */ |
| 506 | if (!n->args_[0]->flags_[ExprNode::PROTO]) { |
| 507 | emit("bpf_ntohs(bpf_checksum_pkt(pkt, %s, %zu))", v->id_->c_str(), bit_width); |
| 508 | } else { |
| 509 | emit("bpf_ntohs(bpf_checksum("); |
| 510 | n->args_[0]->accept(this); |
| 511 | emit(", %zu))", bit_width); |
| 512 | } |
| 513 | } else { |
| 514 | throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_); |
| 515 | } |
| 516 | /** emit("pg_cksum("); |
| 517 | n->args_[0]->accept(this); |
| 518 | emit(", %zu)", n->args_[0]->struct_type_->bit_width_ >> 3);**/ |
| 519 | } else { |
| 520 | throw CompilerException("cannot pg_cksum %d", n->args_[0]->typeof_); |
| 521 | } |
| 522 | } |
| 523 | |
| 524 | void CodegenC::emit_incr_cksum_u16(MethodCallExprNode* n) { |
| 525 | if (n->args_.size() == 3) { |
| 526 | /* ip checksum */ |
| 527 | emit("bpf_ntohs(bpf_csum_replace2(bpf_htons("); |
| 528 | n->args_[0]->accept(this); |
| 529 | emit("), bpf_htons("); |
| 530 | n->args_[1]->accept(this); |
| 531 | emit("), bpf_htons("); |
| 532 | n->args_[2]->accept(this); |
| 533 | emit(")))"); |
| 534 | } else { |
| 535 | /* L4 checksum */ |
| 536 | emit("("); |
| 537 | /* part of pseudo header */ |
| 538 | n->args_[3]->accept(this); |
| 539 | emit(" ? "); |
| 540 | emit("((pkt->hw_csum == 1) ? "); |
| 541 | /* CHECKSUM_PARTIAL update pseudo only */ |
| 542 | emit("bpf_ntohs(bpf_pseudo_csum_replace2(bpf_htons("); |
| 543 | n->args_[0]->accept(this); |
| 544 | emit("), bpf_htons("); |
| 545 | n->args_[1]->accept(this); |
| 546 | emit("), bpf_htons("); |
| 547 | n->args_[2]->accept(this); |
| 548 | emit(")))"); |
| 549 | emit(" : "); |
| 550 | /* CHECKSUM_NONE update normally */ |
| 551 | emit("bpf_ntohs(bpf_csum_replace2(bpf_htons("); |
| 552 | n->args_[0]->accept(this); |
| 553 | emit("), bpf_htons("); |
| 554 | n->args_[1]->accept(this); |
| 555 | emit("), bpf_htons("); |
| 556 | n->args_[2]->accept(this); |
| 557 | emit(")))"); |
| 558 | emit(")"); |
| 559 | emit(" : "); |
| 560 | /* not part of pseudo */ |
| 561 | emit("((pkt->hw_csum != 1) ? "); |
| 562 | /* CHECKSUM_NONE update normally */ |
| 563 | emit("bpf_ntohs(bpf_csum_replace2(bpf_htons("); |
| 564 | n->args_[0]->accept(this); |
| 565 | emit("), bpf_htons("); |
| 566 | n->args_[1]->accept(this); |
| 567 | emit("), bpf_htons("); |
| 568 | n->args_[2]->accept(this); |
| 569 | emit(")))"); |
| 570 | emit(" : "); |
| 571 | /* CHECKSUM_PARTIAL no-op */ |
| 572 | n->args_[0]->accept(this); |
| 573 | emit("))"); |
| 574 | } |
| 575 | } |
| 576 | |
| 577 | void CodegenC::emit_incr_cksum_u32(MethodCallExprNode* n) { |
| 578 | if (n->args_.size() == 3) { |
| 579 | /* ip checksum */ |
| 580 | emit("bpf_ntohs(bpf_csum_replace4(bpf_htons("); |
| 581 | n->args_[0]->accept(this); |
| 582 | emit("), bpf_htonl("); |
| 583 | n->args_[1]->accept(this); |
| 584 | emit("), bpf_htonl("); |
| 585 | n->args_[2]->accept(this); |
| 586 | emit(")))"); |
| 587 | } else { |
| 588 | /* L4 checksum */ |
| 589 | emit("("); |
| 590 | /* part of pseudo header */ |
| 591 | n->args_[3]->accept(this); |
| 592 | emit(" ? "); |
| 593 | emit("((pkt->hw_csum == 1) ? "); |
| 594 | /* CHECKSUM_PARTIAL update pseudo only */ |
| 595 | emit("bpf_ntohs(bpf_pseudo_csum_replace4(bpf_htons("); |
| 596 | n->args_[0]->accept(this); |
| 597 | emit("), bpf_htonl("); |
| 598 | n->args_[1]->accept(this); |
| 599 | emit("), bpf_htonl("); |
| 600 | n->args_[2]->accept(this); |
| 601 | emit(")))"); |
| 602 | emit(" : "); |
| 603 | /* CHECKSUM_NONE update normally */ |
| 604 | emit("bpf_ntohs(bpf_csum_replace4(bpf_htons("); |
| 605 | n->args_[0]->accept(this); |
| 606 | emit("), bpf_htonl("); |
| 607 | n->args_[1]->accept(this); |
| 608 | emit("), bpf_htonl("); |
| 609 | n->args_[2]->accept(this); |
| 610 | emit(")))"); |
| 611 | emit(")"); |
| 612 | emit(" : "); |
| 613 | /* not part of pseudo */ |
| 614 | emit("((pkt->hw_csum != 1) ? "); |
| 615 | /* CHECKSUM_NONE updata normally */ |
| 616 | emit("bpf_ntohs(bpf_csum_replace4(bpf_htons("); |
| 617 | n->args_[0]->accept(this); |
| 618 | emit("), bpf_htonl("); |
| 619 | n->args_[1]->accept(this); |
| 620 | emit("), bpf_htonl("); |
| 621 | n->args_[2]->accept(this); |
| 622 | emit(")))"); |
| 623 | emit(" : "); |
| 624 | /* CHECKSUM_PARTIAL no-op */ |
| 625 | n->args_[0]->accept(this); |
| 626 | emit("))"); |
| 627 | } |
| 628 | } |
| 629 | |
| 630 | void CodegenC::emit_lb_hash(MethodCallExprNode* n) { |
| 631 | emit("pg_lb_hash("); |
| 632 | n->args_[0]->accept(this); |
| 633 | emit(", "); |
| 634 | n->args_[1]->accept(this); |
| 635 | emit(")"); |
| 636 | } |
| 637 | |
| 638 | void CodegenC::emit_sizeof(MethodCallExprNode* n) { |
| 639 | if (n->args_[0]->typeof_ == ExprNode::STRUCT) { |
| 640 | if (n->args_[0]->struct_type_->id_->name_ == "_Packet") { |
| 641 | emit("PG_SIZEOF(pkt)"); |
| 642 | } else { |
| 643 | emit("%zu", n->args_[0]->struct_type_->bit_width_ >> 3); |
| 644 | } |
| 645 | } else if (n->args_[0]->typeof_ == ExprNode::INTEGER) { |
| 646 | emit("%zu", n->args_[0]->bit_width_ >> 3); |
| 647 | } |
| 648 | } |
| 649 | |
| 650 | void CodegenC::emit_get_usec_time(MethodCallExprNode* n) { |
| 651 | emit("bpf_get_usec_time()"); |
| 652 | } |
| 653 | |
| 654 | void CodegenC::emit_forward_to_vnf(MethodCallExprNode*n) { |
| 655 | emitln("pkt->arg1 |= 1;"); |
| 656 | emit("pkt->arg2 = "); |
| 657 | n->args_[0]->accept(this); |
| 658 | emitln(";"); |
| 659 | emit("bpf_forward_to_plum(pkt, "); |
| 660 | n->args_[1]->accept(this); |
| 661 | emit(")"); |
| 662 | |
| 663 | } |
| 664 | |
| 665 | void CodegenC::emit_forward_to_group(MethodCallExprNode *n) { |
| 666 | |
| 667 | emit("pkt->arg2 = "); |
| 668 | n->args_[0]->accept(this); |
| 669 | emitln(";"); |
| 670 | emitln("pkt->arg3 = pkt->plum_id;"); |
| 671 | emit("bpf_forward_to_plum(pkt, "); |
| 672 | emit("1/*TUNNEL_PLUM_ID*/"); |
| 673 | emit(")"); |
| 674 | |
| 675 | } |
| 676 | |
| 677 | void CodegenC::visit_method_call_expr_node(MethodCallExprNode* n) { |
| 678 | free_instructions_.push_back(vector<string>()); |
| 679 | |
| 680 | if (!n->stmts_.empty()) { |
| 681 | ++indent_; |
| 682 | emitln("{"); |
| 683 | } |
| 684 | |
| 685 | if (n->id_->sub_name_.size()) { |
| 686 | if (n->id_->sub_name_ == "lookup") { |
| 687 | emit_table_lookup(n); |
| 688 | } else if (n->id_->sub_name_ == "update") { |
| 689 | emit_table_update(n); |
| 690 | } else if (n->id_->sub_name_ == "delete") { |
| 691 | emit_table_delete(n); |
| 692 | } else if (n->id_->sub_name_ == "replicate" && n->id_->name_ == "pkt") { |
| 693 | emit_packet_replicate(n); |
| 694 | } else if (n->id_->sub_name_ == "forward" && n->id_->name_ == "pkt") { |
| 695 | emit_packet_forward(n); |
| 696 | } else if (n->id_->sub_name_ == "forward_self" && n->id_->name_ == "pkt") { |
| 697 | emit_packet_forward_self(n); |
| 698 | } else if (n->id_->sub_name_ == "push_header" && n->id_->name_ == "pkt") { |
| 699 | emit_packet_push_header(n); |
| 700 | } else if (n->id_->sub_name_ == "pop_header" && n->id_->name_ == "pkt") { |
| 701 | emit_packet_pop_header(n); |
| 702 | } else if (n->id_->sub_name_ == "push_vlan" && n->id_->name_ == "pkt") { |
| 703 | emit_packet_push_vlan(n); |
| 704 | } else if (n->id_->sub_name_ == "pop_vlan" && n->id_->name_ == "pkt") { |
| 705 | emit_packet_pop_vlan(n); |
| 706 | } else if (n->id_->sub_name_ == "rewrite_field" && n->id_->name_ == "pkt") { |
| 707 | emit_packet_rewrite_field(n); |
| 708 | } else if (n->id_->sub_name_ == "clone_forward" && n->id_->name_ == "pkt") { |
| 709 | emit_packet_clone_forward(n); |
| 710 | } |
| 711 | } else if (n->id_->name_ == "atomic_add") { |
| 712 | emit_atomic_add(n); |
| 713 | } else if (n->id_->name_ == "log") { |
| 714 | emit_log(n); |
| 715 | } else if (n->id_->name_ == "cksum") { |
| 716 | emit_cksum(n); |
| 717 | } else if (n->id_->name_ == "incr_cksum_u16") { |
| 718 | emit_incr_cksum_u16(n); |
| 719 | } else if (n->id_->name_ == "incr_cksum_u32") { |
| 720 | emit_incr_cksum_u32(n); |
| 721 | } else if (n->id_->name_ == "lb_hash") { |
| 722 | emit_lb_hash(n); |
| 723 | } else if (n->id_->name_ == "sizeof") { |
| 724 | emit_sizeof(n); |
| 725 | } else if (n->id_->name_ == "get_usec_time") { |
| 726 | emit_get_usec_time(n); |
| 727 | } else if (n->id_->name_ == "channel_push") { |
| 728 | emit_channel_push(n); |
| 729 | } else if (n->id_->name_ == "channel_push_generic") { |
| 730 | emit_channel_push_generic(n); |
| 731 | } else if (n->id_->name_ == "forward_to_vnf") { |
| 732 | emit_forward_to_vnf(n); |
| 733 | } else if (n->id_->name_ == "forward_to_group") { |
| 734 | emit_forward_to_group(n); |
| 735 | } else { |
| 736 | n->id_->accept(this); |
| 737 | emit("("); |
| 738 | for (auto it = n->args_.begin(); it != n->args_.end(); ++it) { |
| 739 | (*it)->accept(this); |
| 740 | if (it + 1 != n->args_.end()) { |
| 741 | emit(", "); |
| 742 | } |
| 743 | } |
| 744 | emit(")"); |
| 745 | } |
| 746 | if (!n->stmts_.empty()) { |
| 747 | emit(";"); |
| 748 | for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { |
| 749 | lnemit(""); |
| 750 | (*it)->accept(this); |
| 751 | } |
| 752 | for (auto it = free_instructions_.back().rbegin(); it != free_instructions_.back().rend(); ++it) { |
| 753 | lnemit("%s;", it->c_str()); |
| 754 | } |
| 755 | --indent_; |
| 756 | lnemit("}"); |
| 757 | } |
| 758 | free_instructions_.pop_back(); |
| 759 | } |
| 760 | |
| 761 | /// on_match |
| 762 | void CodegenC::visit_match_decl_stmt_node(MatchDeclStmtNode* n) { |
| 763 | if (n->formals_.size() != 2) |
| 764 | throw CompilerException("on_match expected 2 arguments, %zu given", n->formals_.size()); |
| 765 | StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get()); |
| 766 | StructVariableDeclStmtNode* leaf_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(1).get()); |
| 767 | if (!key_n || !leaf_n) |
| 768 | throw CompilerException("invalid parameter type"); |
| 769 | ++indent_; |
| 770 | emitln("if (%s_element) {", n->id_->c_str()); |
| 771 | emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(), key_n->scope_id(), |
| 772 | key_n->id_->c_str(), n->id_->c_str()); |
| 773 | emitln("%s* %s%s = %s_element;", leaf_n->struct_id_->c_str(), leaf_n->scope_id(), |
| 774 | leaf_n->id_->c_str(), n->id_->c_str()); |
| 775 | n->block_->accept(this); |
| 776 | --indent_; |
| 777 | emitln(""); |
| 778 | emit("}"); |
| 779 | } |
| 780 | |
| 781 | /// on_miss |
| 782 | void CodegenC::visit_miss_decl_stmt_node(MissDeclStmtNode* n) { |
| 783 | if (n->formals_.size() != 1) |
| 784 | throw CompilerException("on_match expected 1 argument, %zu given", n->formals_.size()); |
| 785 | StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get()); |
| 786 | ++indent_; |
| 787 | emitln("if (!%s_element) {", n->id_->c_str()); |
| 788 | emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(), |
| 789 | key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str()); |
| 790 | n->block_->accept(this); |
| 791 | --indent_; |
| 792 | emitln(""); |
| 793 | emit("}"); |
| 794 | } |
| 795 | |
| 796 | void CodegenC::visit_failure_decl_stmt_node(FailureDeclStmtNode* n) { |
| 797 | if (n->formals_.size() != 1) |
| 798 | throw CompilerException("on_failure expected 1 argument, %zu given", n->formals_.size()); |
| 799 | StructVariableDeclStmtNode* key_n = static_cast<StructVariableDeclStmtNode*>(n->formals_.at(0).get()); |
| 800 | ++indent_; |
| 801 | emitln("/*if ((unsigned long)%s_element >= (unsigned long)-4095) {", n->id_->name_.c_str()); |
| 802 | emitln("%s* %s%s = %s_key;", key_n->struct_id_->c_str(), |
| 803 | key_n->scope_id(), key_n->id_->c_str(), n->id_->c_str()); |
| 804 | n->block_->accept(this); |
| 805 | --indent_; |
| 806 | emitln(""); |
| 807 | emit("}*/"); |
| 808 | } |
| 809 | |
| 810 | void CodegenC::visit_expr_stmt_node(ExprStmtNode* n) { |
| 811 | emit_comment(n); |
| 812 | n->expr_->accept(this); |
| 813 | emit(";"); |
| 814 | } |
| 815 | |
| 816 | void CodegenC::visit_struct_variable_decl_stmt_node(StructVariableDeclStmtNode* n) { |
| 817 | if (n->struct_id_->name_ == "" || n->struct_id_->name_[0] == '_') { |
| 818 | return; |
| 819 | } |
| 820 | emit_comment(n); |
| 821 | if (n->struct_id_->scope_name_ == "proto") { |
| 822 | auto p = proto_scopes_->top_struct()->lookup(n->struct_id_->name_, true); |
| 823 | if (p) { |
| 824 | string var = n->scope_id() + n->id_->name_; |
| 825 | /* zero initialize array to be filled in with packet header */ |
| 826 | emit("uint64_t __%s[%zu] = {}; uint8_t *%s = (uint8_t*)__%s;", |
| 827 | var.c_str(), ((p->bit_width_ >> 3) + 7) >> 3, var.c_str(), var.c_str()); |
| 828 | for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { |
| 829 | auto asn = static_cast<AssignExprNode*>(it->get()); |
| 830 | if (auto f = p->field(asn->id_->sub_name_)) { |
| 831 | size_t bit_offset = f->bit_offset_; |
| 832 | size_t bit_width = f->bit_width_; |
| 833 | if (asn->bitop_) { |
| 834 | bit_offset += f->bit_width_ - (asn->bitop_->bit_offset_ + asn->bitop_->bit_width_); |
| 835 | bit_width = std::min(bit_width - asn->bitop_->bit_offset_, asn->bitop_->bit_width_); |
| 836 | } |
| 837 | emit(" bpf_dins(%s + %zu, %zu, %zu, ", var.c_str(), bit_offset >> 3, bit_offset & 0x7, bit_width); |
| 838 | asn->rhs_->accept(this); |
| 839 | emit(");"); |
| 840 | } |
| 841 | } |
| 842 | } |
| 843 | } else { |
| 844 | /* all structs must be initialized with zeros, since they're alocated on stack, |
| 845 | * if struct doesn't have gaps between fields, gcc will be smart enough to avoid redundant zeroing */ |
| 846 | if (n->storage_type_ == VariableDeclStmtNode::STRUCT_REFERENCE) { |
| 847 | emit("%s* %s%s = 0;", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str()); |
| 848 | } else { |
| 849 | emit("%s %s%s = {};", n->struct_id_->c_str(), n->scope_id(), n->id_->c_str()); |
| 850 | if (!n->init_.empty()) { |
| 851 | for (auto it = n->init_.begin(); it != n->init_.end(); ++it) { |
| 852 | emit(" "); |
| 853 | (*it)->accept(this); |
| 854 | emit(";"); |
| 855 | } |
| 856 | } |
| 857 | } |
| 858 | } |
| 859 | } |
| 860 | |
| 861 | void CodegenC::visit_integer_variable_decl_stmt_node(IntegerVariableDeclStmtNode* n) { |
| 862 | if (n->id_->name_ == "timer_delay" || n->id_->name_ == "parsed_bytes") |
| 863 | return; |
| 864 | emit_comment(n); |
| 865 | emit("%s %s%s", bits_to_uint(n->bit_width_), n->scope_id(), n->id_->c_str()); |
| 866 | if (!n->scope_id_.empty()) |
| 867 | emit(" = 0"); |
| 868 | if (!n->init_.empty()) { |
| 869 | emit("; "); |
| 870 | n->init_[0]->accept(this); |
| 871 | } |
| 872 | emit(";"); |
| 873 | } |
| 874 | |
| 875 | void CodegenC::visit_struct_decl_stmt_node(StructDeclStmtNode* n) { |
| 876 | emit("typedef struct {\n"); |
| 877 | ++indent_; |
| 878 | for (auto it = n->stmts_.begin(); it != n->stmts_.end(); ++it) { |
| 879 | indent(); |
| 880 | (*it)->accept(this); |
| 881 | emit("\n"); |
| 882 | } |
| 883 | --indent_; |
| 884 | indent(); |
| 885 | emit("} __attribute__((aligned(4))) "); |
| 886 | emit("%s", n->id_->c_str()); |
| 887 | } |
| 888 | |
| 889 | void CodegenC::visit_parser_state_stmt_node(ParserStateStmtNode* n) { |
| 890 | string jump_label = n->scoped_name() + "_continue"; |
| 891 | emit("%s: {", jump_label.c_str()); |
| 892 | ++indent_; |
| 893 | lnemit("PG_TRACE(%.14s);", jump_label.c_str()); |
| 894 | if (n->next_state_) { |
| 895 | lnemit(""); |
| 896 | n->next_state_->accept(this); |
| 897 | } |
| 898 | --indent_; |
| 899 | lnemit("}"); |
| 900 | } |
| 901 | |
| 902 | void CodegenC::visit_timer_decl_stmt_node(TimerDeclStmtNode* n) { |
| 903 | auto scope = scopes_->current_state(); |
| 904 | scopes_->set_current(n->scope_); |
| 905 | n->block_->accept(this); |
| 906 | scopes_->set_current(scope); |
| 907 | } |
| 908 | void CodegenC::visit_state_decl_stmt_node(StateDeclStmtNode* n) { |
| 909 | if (!n->id_) { |
| 910 | return; |
| 911 | } |
| 912 | string jump_label = n->scoped_name(); |
| 913 | ++indent_; |
| 914 | emitln("JUMP_GUARD; %s: {", jump_label.c_str()); |
| 915 | emitln("PG_TRACE(%.14s);", jump_label.c_str()); |
| 916 | if (auto p = proto_scopes_->top_struct()->lookup(n->id_->name_, true)) { |
| 917 | emitln("%s = parsed_bytes; /* remember the offset of this header */", n->id_->c_str()); |
| 918 | emitln("parsed_bytes += %zu;", p->bit_width_ >> 3); |
| 919 | //emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */"); |
| 920 | } |
| 921 | // collect the protocols used in this state scope and declare them |
| 922 | set<string> protos; |
| 923 | for (auto it = n->subs_.begin(); it != n->subs_.end(); ++it) { |
| 924 | if (!it->scope_) { |
| 925 | continue; |
| 926 | } |
| 927 | auto scope = scopes_->current_state(); |
| 928 | scopes_->set_current(it->scope_); |
| 929 | for (auto it2 = scopes_->current_state()->obegin(); it2 != scopes_->current_state()->oend(); ++it2) { |
| 930 | if (proto_scopes_->top_struct()->lookup((*it2)->id_->name_, true)) { |
| 931 | protos.insert((*it2)->id_->name_); |
| 932 | } |
| 933 | for (auto it3 = (*it2)->subs_.begin(); it3 != (*it2)->subs_.end(); ++it3) { |
| 934 | if (proto_scopes_->top_struct()->lookup(it3->id_->name_, true)) { |
| 935 | protos.insert(it3->id_->name_); |
| 936 | } |
| 937 | } |
| 938 | } |
| 939 | scopes_->set_current(scope); |
| 940 | } |
| 941 | for (auto it = protos.begin(); it != protos.end(); ++it) { |
| 942 | emitln("uint32_t %s = 0; /* header offset */", it->c_str()); |
| 943 | } |
| 944 | |
| 945 | auto it = n->subs_.begin(); |
| 946 | if (n->subs_.size() == 1 && it->id_->name_ == "") { |
| 947 | // this is not a multistate protocol, emit everything and finish |
| 948 | auto scope = scopes_->current_state(); |
| 949 | scopes_->set_current(it->scope_); |
| 950 | it->block_->accept(this); |
| 951 | if (n->parser_) { |
| 952 | emitln(""); |
| 953 | n->parser_->accept(this); |
| 954 | } |
| 955 | scopes_->set_current(scope); |
| 956 | } else { |
| 957 | if (n->parser_) { |
| 958 | for (auto it2 = n->subs_.begin(); it2 != n->subs_.end(); ++it2) { |
| 959 | proto_rewrites_[it2->id_->full_name()] = n->scoped_name() + "_" + it2->id_->name_; |
| 960 | } |
| 961 | n->parser_->accept(this); |
| 962 | proto_rewrites_.clear(); |
| 963 | emitln(""); |
| 964 | } |
| 965 | for (; it != n->subs_.end(); ++it) { |
| 966 | auto scope = scopes_->current_state(); |
| 967 | scopes_->set_current(it->scope_); |
| 968 | |
| 969 | string jump_label = n->scoped_name() + "_" + it->id_->name_; |
| 970 | ++indent_; |
| 971 | emitln("JUMP_GUARD; %s: {", jump_label.c_str()); |
| 972 | emitln("PG_TRACE(%.14s);", jump_label.c_str()); |
| 973 | if (auto p = proto_scopes_->top_struct()->lookup(it->id_->name_, true)) { |
| 974 | emitln("%s = pkt->offset + parsed_bytes; /* remember the offset of this header */", it->id_->c_str()); |
| 975 | emitln("parsed_bytes += %zu;", p->bit_width_ >> 3); |
| 976 | emitln("if (!pg_may_access(pkt, parsed_bytes)) goto ERROR; /* pull data from fragments to access this header */"); |
| 977 | } |
| 978 | it->block_->accept(this); |
| 979 | if (it->parser_) { |
| 980 | emitln(""); |
| 981 | it->parser_->accept(this); |
| 982 | } |
| 983 | --indent_; |
| 984 | emitln(""); |
| 985 | emitln("}"); |
| 986 | |
| 987 | scopes_->set_current(scope); |
| 988 | } |
| 989 | } |
| 990 | |
| 991 | --indent_; |
| 992 | emitln(""); |
| 993 | emit("}"); |
| 994 | } |
| 995 | |
| 996 | void CodegenC::visit_table_decl_stmt_node(TableDeclStmtNode* n) { |
| 997 | if (n->table_type_->name_ == "Table" |
| 998 | || n->table_type_->name_ == "SharedTable") { |
| 999 | if (n->templates_.size() != 4) |
| 1000 | throw CompilerException("%s expected 4 arguments, %zu given", n->table_type_->c_str(), n->templates_.size()); |
| 1001 | const char *key_type = n->key_id()->c_str(); |
| 1002 | const char *leaf_type = n->leaf_id()->c_str(); |
| 1003 | char buf[128]; |
| 1004 | if (n->type_id()->name_ == "FIXED_MATCH" || n->type_id()->name_ == "INDEXED") { |
| 1005 | //emitln("struct %s_Element {", n->id_->c_str()); |
| 1006 | //emitln(" PG_HASH_TABLE_ELEMENT_COMMON"); |
| 1007 | //emitln(" %s key;", key_type); |
| 1008 | //emitln(" %s leaf;", leaf_type); |
| 1009 | //emitln("} __attribute__((aligned(8)));"); |
| 1010 | //emitln("static struct PGHashTable %s;", n->id_->c_str()); |
| 1011 | //emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_); |
| 1012 | //emitln("PG_HASH_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s)", |
| 1013 | // table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str()); |
| 1014 | emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size()); |
| 1015 | snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_HASH, sizeof(%s), sizeof(%s), %zd, 0}, // %s", |
| 1016 | table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_, n->id_->c_str()); |
| 1017 | } else if (n->type_id()->name_ == "LPM") { |
| 1018 | //emitln("struct %s_Element {", n->id_->c_str()); |
| 1019 | //emitln(" PG_LPM_TABLE_ELEMENT_COMMON"); |
| 1020 | //emitln(" %s key;", key_type); |
| 1021 | //emitln(" %s leaf;", leaf_type); |
| 1022 | //emitln("} __attribute__((aligned(8)));"); |
| 1023 | //emitln("static struct PGLpmTable %s;", n->id_->c_str()); |
| 1024 | //emitln("#define N_BUCKETS_%s %zu", n->id_->c_str(), n->size_); |
| 1025 | //emitln("PG_LPM_TABLE_DECL(%d, %s, sizeof(%s), sizeof(struct %s_Element), N_BUCKETS_%s, %u)", |
| 1026 | // table_inits_.size(), n->id_->c_str(), key_type, n->id_->c_str(), n->id_->c_str(), |
| 1027 | // n->key_id()->bit_width_); |
| 1028 | emitln("#define TABLE_ID_%s %zd", n->id_->c_str(), table_inits_.size()); |
| 1029 | snprintf(buf, sizeof(buf), "[%zd] = {%zd, PG_TABLE_LPM, sizeof(%s), sizeof(%s), %zd, %zd}, // %s", |
| 1030 | table_inits_.size(), table_inits_.size(), key_type, leaf_type, n->size_, |
| 1031 | n->key_id()->bit_width_, n->id_->c_str()); |
| 1032 | } else { |
| 1033 | throw CompilerException("table type \"%s\" unknown", n->type_id()->c_str()); |
| 1034 | } |
| 1035 | //table_inits_.push_back(n->id_->name_); |
| 1036 | table_inits_.push_back(buf); |
| 1037 | } |
| 1038 | } |
| 1039 | |
| 1040 | int CodegenC::visit(Node* root) { |
| 1041 | BlockStmtNode* b = static_cast<BlockStmtNode*>(root); |
| 1042 | |
| 1043 | |
| 1044 | scopes_->set_current(scopes_->top_state()); |
| 1045 | scopes_->set_current(scopes_->top_var()); |
| 1046 | |
| 1047 | print_header(); |
| 1048 | |
| 1049 | b->ver_.accept(this); |
| 1050 | |
| 1051 | for (auto it = scopes_->top_table()->obegin(); it != scopes_->top_table()->oend(); ++it) { |
| 1052 | (*it)->accept(this); |
| 1053 | emit("\n"); |
| 1054 | } |
| 1055 | |
| 1056 | print_parser(); |
| 1057 | |
| 1058 | print_footer(); |
| 1059 | |
| 1060 | return 0; |
| 1061 | } |
| 1062 | |
| 1063 | void CodegenC::print_timer() { |
| 1064 | // visit timers |
| 1065 | ++indent_; |
| 1066 | emitln("PG_PARSE_DECL(timer) {"); |
| 1067 | emitln("uint32_t timer_delay = 0;"); |
| 1068 | // visit function scoped variables |
| 1069 | for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) { |
| 1070 | (*it)->accept(this); |
| 1071 | emitln(""); |
| 1072 | } |
| 1073 | for (auto it = scopes_->top_timer()->obegin(); it != scopes_->top_timer()->oend(); ++it) { |
| 1074 | (*it)->accept(this); |
| 1075 | emitln(""); |
| 1076 | } |
| 1077 | ++indent_; |
| 1078 | emitln("DONE: {"); |
| 1079 | emitln("PG_TRACE(DONE);"); |
| 1080 | emitln("pg_timer_forward(pkt, timer_delay);"); |
| 1081 | --indent_; |
| 1082 | emitln("return;"); |
| 1083 | emitln("}"); |
| 1084 | |
| 1085 | ++indent_; |
| 1086 | emitln("ERROR: {"); |
| 1087 | emitln("PG_TRACE(ERROR);"); |
| 1088 | emitln("pg_drop(pkt);"); |
| 1089 | emitln("pg_timer_forward(pkt, timer_delay);"); |
| 1090 | --indent_; |
| 1091 | emitln("return;"); |
| 1092 | --indent_; |
| 1093 | emitln("}"); |
| 1094 | emitln("}"); |
| 1095 | } |
| 1096 | |
| 1097 | void CodegenC::print_parser() { |
| 1098 | ++indent_; |
| 1099 | emitln("PG_PARSE_DECL(parse) {"); |
| 1100 | /* emitln("uint8_t *pp;"); */ |
| 1101 | emitln("uint32_t parsed_bytes = 0;"); |
| 1102 | emitln("uint16_t orig_offset = 0;/*pkt->offset;*/"); |
| 1103 | |
| 1104 | // visit function scoped variables |
| 1105 | for (auto it = scopes_->current_var()->obegin(); it != scopes_->current_var()->oend(); ++it) { |
| 1106 | (*it)->accept(this); |
| 1107 | emitln(""); |
| 1108 | } |
| 1109 | |
| 1110 | for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) { |
| 1111 | if (proto_scopes_->top_struct()->lookup((*it)->id_->name_, true)) { |
| 1112 | emitln("uint32_t %s = 0; /* header offset */", (*it)->id_->c_str()); |
| 1113 | } |
| 1114 | } |
| 1115 | |
| 1116 | /* emitln("pp = pkt->start + pkt->offset;"); */ |
| 1117 | emitln("goto s1_INIT;"); |
| 1118 | |
| 1119 | // finally, visit the states |
| 1120 | for (auto it = scopes_->current_state()->obegin(); it != scopes_->current_state()->oend(); ++it) { |
| 1121 | (*it)->accept(this); |
| 1122 | emitln(""); |
| 1123 | } |
| 1124 | |
| 1125 | ++indent_; |
| 1126 | emitln("ERROR: {"); |
| 1127 | emitln("PG_TRACE(ERROR);"); |
| 1128 | --indent_; |
| 1129 | emitln("goto CLEANUP;"); |
| 1130 | emitln("}"); |
| 1131 | |
| 1132 | ++indent_; |
| 1133 | emitln("DONE: {"); |
| 1134 | emitln("PG_TRACE(DONE);"); |
| 1135 | --indent_; |
| 1136 | emitln("goto CLEANUP;"); |
| 1137 | emitln("}"); |
| 1138 | |
| 1139 | ++indent_; |
| 1140 | emitln("CLEANUP: {"); |
| 1141 | --indent_; |
| 1142 | emitln("/* cleanup is done by PE */;"); |
| 1143 | --indent_; |
| 1144 | emitln("}"); |
| 1145 | |
| 1146 | emitln("}"); |
| 1147 | |
| 1148 | //print_timer(); |
| 1149 | } |
| 1150 | |
| 1151 | void CodegenC::print_header() { |
| 1152 | if (use_pre_header_) { |
| 1153 | //emit("%s", PRE_HEADER.c_str()); |
| 1154 | emitln(""); |
| 1155 | } else { |
| 1156 | emitln("#include <stdint.h>"); |
| 1157 | emitln("#include \"../dp/linux/filter.h\""); |
| 1158 | emitln("#include \"container/pg_api.h\""); |
| 1159 | emitln("#include \"container/pg_defs.h\""); |
| 1160 | } |
| 1161 | emitln("#define JUMP_GUARD goto DONE"); |
| 1162 | emitln("#define PG_SIZEOF(_pkt) ((int)_pkt->length - (int)pkt->offset + orig_offset)"); |
| 1163 | |
| 1164 | int i = 0; |
| 1165 | // declare structures |
| 1166 | for (auto it = scopes_->top_struct()->obegin(); it != scopes_->top_struct()->oend(); ++it) { |
| 1167 | if ((*it)->id_->name_ == "_Packet") |
| 1168 | continue; |
| 1169 | (*it)->accept(this); |
| 1170 | emit(";\n"); |
| 1171 | emitln("#define STRUCTID_%s %d", (*it)->id_->c_str(), i++); |
| 1172 | } |
| 1173 | emitln("#define STRUCTID_generic %d", i); |
| 1174 | } |
| 1175 | |
| 1176 | void CodegenC::print_footer() { |
| 1177 | //emitln("#define EXPAND_TABLES(E) \\"); |
| 1178 | emitln("struct bpf_table plum_tables[] = {"); |
| 1179 | for (auto it = table_inits_.begin(); it != table_inits_.end(); ++it) { |
| 1180 | //emit("E(%s) ", it->c_str()); |
| 1181 | emitln(" %s", it->c_str()); |
| 1182 | } |
| 1183 | emitln(" {0,0,0,0,0,0} // last table marker"); |
| 1184 | emitln("};"); |
| 1185 | emitln(""); |
| 1186 | emitln("PG_INIT"); |
| 1187 | emitln("PG_CLEANUP"); |
| 1188 | } |
| 1189 | |
| 1190 | } // namespace cc |
| 1191 | } // namespace ebpf |