bmv2
Designing your own switch target with bmv2
actions.h
Go to the documentation of this file.
1 /* Copyright 2013-2019 Barefoot Networks, Inc.
2  * Copyright 2019 VMware, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Antonin Bas
19  *
20  */
21 
69 
70 
71 #ifndef BM_BM_SIM_ACTIONS_H_
72 #define BM_BM_SIM_ACTIONS_H_
73 
74 #include <functional>
75 #include <iosfwd>
76 #include <limits>
77 #include <memory>
78 #include <string>
79 #include <tuple>
80 #include <unordered_map>
81 #include <utility>
82 #include <vector>
83 
84 #include <cassert>
85 
86 #include "phv.h"
87 #include "named_p4object.h"
88 #include "expressions.h"
89 #include "stateful.h"
90 #include "_assert.h"
91 
92 namespace bm {
93 
94 class P4Objects; // forward declaration for deserialize
95 
96 // some forward declarations for needed p4 objects
97 class Packet;
98 class NamedCalculation;
99 class MeterArray;
100 class CounterArray;
101 
102 // forward declaration of ActionPrimitive_
103 class ActionPrimitive_;
104 
105 // Maps primitive names to primitive implementations (functors).
106 // Targets are responsible for defining their own primitives and registering
107 // them in this map using the REGISTER_PRIMITIVE(primitive_name) macro.
108 class ActionOpcodesMap {
109  public:
110  using ActionPrimitiveFactoryFn =
111  std::function<std::unique_ptr<ActionPrimitive_>()>;
112  static ActionOpcodesMap *get_instance();
113  bool register_primitive(const char *name, ActionPrimitiveFactoryFn primitive);
114 
115  std::unique_ptr<ActionPrimitive_> get_primitive(const std::string &name);
116  private:
117  ActionOpcodesMap();
118 
119  // Maps primitive names to their implementation.
120  // The same pointer is used system-wide, even if a primitive is called from
121  // different actions. As such, one has to be careful if some state is
122  // maintained byt the primitive functor.
123  std::unordered_map<std::string, ActionPrimitiveFactoryFn> map_{};
124 };
125 
128 #define REGISTER_PRIMITIVE(primitive_name) \
129  bool primitive_name##_create_ = \
130  bm::ActionOpcodesMap::get_instance()->register_primitive( \
131  #primitive_name, \
132  [](){ return std::unique_ptr<::bm::ActionPrimitive_>( \
133  new primitive_name()); })
134 
138 #define REGISTER_PRIMITIVE_W_NAME(primitive_name, primitive) \
139  bool primitive##_create_ = \
140  bm::ActionOpcodesMap::get_instance()->register_primitive( \
141  primitive_name, \
142  [](){ return std::unique_ptr<::bm::ActionPrimitive_>( \
143  new primitive()); })
144 
145 struct ActionData {
146  const Data &get(int offset) const { return action_data[offset]; }
147 
148  void push_back_action_data(const Data &data) {
149  action_data.push_back(data);
150  }
151 
152  void push_back_action_data(unsigned int data) {
153  action_data.emplace_back(data);
154  }
155 
156  void push_back_action_data(const char *bytes, int nbytes) {
157  action_data.emplace_back(bytes, nbytes);
158  }
159 
160  size_t size() const { return action_data.size(); }
161 
162  std::vector<Data> action_data{};
163 };
164 
165 struct ActionEngineState;
166 
167 class ExternType;
168 
169 // A simple tagged union, which holds the definition of an action parameter. For
170 // example, if an action parameter is a field, this struct will hold the header
171 // id and the offset for this field. If an action parameter is a meter array,
172 // the struct will hold a raw, non-owning pointer to the MeterArray object.
173 struct ActionParam {
174  // some old P4 primitives take a calculation as a parameter, I don't know if I
175  // will keep it around but for now I need it
176  enum {CONST, FIELD, HEADER, ACTION_DATA, REGISTER_REF, REGISTER_GEN,
177  HEADER_STACK, LAST_HEADER_STACK_FIELD,
178  CALCULATION,
179  METER_ARRAY, COUNTER_ARRAY, REGISTER_ARRAY,
180  EXPRESSION, EXPRESSION_HEADER, EXPRESSION_HEADER_STACK,
181  EXPRESSION_HEADER_UNION, EXPRESSION_HEADER_UNION_STACK,
182  EXTERN_INSTANCE,
183  STRING,
184  HEADER_UNION, HEADER_UNION_STACK, PARAMS_VECTOR, FIELD_LIST} tag;
185 
186  union {
187  unsigned int const_offset;
188 
189  struct {
190  header_id_t header;
191  int field_offset;
192  } field;
193 
194  header_id_t header;
195 
196  unsigned int action_data_offset;
197 
198  // In theory, if registers cannot be resized, I could directly store a
199  // pointer to the correct register cell, i.e. &(*array)[idx]. However, this
200  // gives me more flexibility in case I want to be able to resize the
201  // registers arbitrarily in the future.
202  struct {
203  RegisterArray *array;
204  unsigned int idx;
205  } register_ref;
206 
207  struct {
208  RegisterArray *array;
209  ArithExpression *idx;
210  } register_gen;
211 
212  header_stack_id_t header_stack;
213 
214  // for lists of numerical values, used for the log_msg primitive and nothing
215  // else at the moment
216  struct {
217  unsigned int start;
218  unsigned int end;
219  } params_vector;
220 
221  struct {
222  unsigned int start;
223  unsigned int end;
224  } field_list;
225 
226  // special case when trying to access a field in the last header of a stack
227  struct {
228  header_stack_id_t header_stack;
229  int field_offset;
230  } stack_field;
231 
232  // non owning pointer
233  const NamedCalculation *calculation;
234 
235  // non owning pointer
236  MeterArray *meter_array;
237 
238  // non owning pointer
239  CounterArray *counter_array;
240 
241  // non owning pointer
242  RegisterArray *register_array;
243 
244  struct {
245  unsigned int offset; // only used for arithmetic ("Data") expressions
246  // non owning pointer
247  // in theory, could be an owning pointer, but the union makes this
248  // complicated, so instead the ActionFn keeps a vector of owning pointers
249  Expression *ptr;
250  } expression;
251 
252  ExternType *extern_instance;
253 
254  // I use a pointer here to avoid complications with the union; the string
255  // memory is owned by ActionFn (just like for Expression above)
256  const std::string *str;
257 
258  header_union_stack_id_t header_union_stack;
259 
260  header_union_id_t header_union;
261  };
262 
263  // convert to the correct type when calling a primitive
264  template <typename T> T to(ActionEngineState *state) const;
265 };
266 
267 struct ActionEngineState {
268  Packet &pkt;
269  PHV &phv;
270  const ActionData &action_data;
271  const std::vector<Data> &const_values;
272  const std::vector<ActionParam> &parameters_vector;
273  const std::vector<ActionParam> &field_list;
274 
275  ActionEngineState(Packet *pkt,
276  const ActionData &action_data,
277  const std::vector<Data> &const_values,
278  const std::vector<ActionParam> &parameters_vector,
279  const std::vector<ActionParam> &field_list);
280 };
281 
282 // template specializations for ActionParam "casting"
283 // they have to be declared outside of the class declaration, and "inline" is
284 // necessary to avoid linker errors
285 
286 template <> inline
287 Data &ActionParam::to<Data &>(ActionEngineState *state) const {
288  static thread_local Data data_temp;
289 
290  switch (tag) {
291  case ActionParam::FIELD:
292  return state->phv.get_field(field.header, field.field_offset);
293  case ActionParam::REGISTER_REF:
294  return register_ref.array->at(register_ref.idx);
295  case ActionParam::REGISTER_GEN:
296  register_gen.idx->eval_arith(state->phv, &data_temp,
297  state->action_data.action_data);
298  return register_ref.array->at(data_temp.get<size_t>());
299  case ActionParam::EXPRESSION:
300  return expression.ptr->eval_arith_lvalue(&state->phv,
301  state->action_data.action_data);
302  case ActionParam::LAST_HEADER_STACK_FIELD:
303  return state->phv.get_header_stack(stack_field.header_stack).get_last()
304  .get_field(stack_field.field_offset);
305  default:
306  _BM_UNREACHABLE("Default switch case should not be reachable");
307  }
308 }
309 
310 template <> inline
311 const Data &ActionParam::to<const Data &>(ActionEngineState *state) const {
312  // thread_local sounds like a good choice here
313  // alternative would be to have one vector for each ActionFnEntry
314  static thread_local unsigned int data_temps_size = 4;
315  static thread_local std::vector<Data> data_temps(data_temps_size);
316 
317  switch (tag) {
318  case ActionParam::CONST:
319  return state->const_values[const_offset];
320  case ActionParam::FIELD:
321  return state->phv.get_field(field.header, field.field_offset);
322  case ActionParam::ACTION_DATA:
323  return state->action_data.get(action_data_offset);
324  case ActionParam::REGISTER_REF:
325  return register_ref.array->at(register_ref.idx);
326  case ActionParam::REGISTER_GEN:
327  register_gen.idx->eval_arith(state->phv, &data_temps[0],
328  state->action_data.action_data);
329  return register_ref.array->at(data_temps[0].get<size_t>());
330  case ActionParam::EXPRESSION:
331  while (data_temps_size <= expression.offset) {
332  data_temps.emplace_back();
333  data_temps_size++;
334  }
335  expression.ptr->eval_arith(state->phv, &data_temps[expression.offset],
336  state->action_data.action_data);
337  return data_temps[expression.offset];
338  case ActionParam::LAST_HEADER_STACK_FIELD:
339  return state->phv.get_header_stack(stack_field.header_stack).get_last()
340  .get_field(stack_field.field_offset);
341  default:
342  _BM_UNREACHABLE("Default switch case should not be reachable");
343  }
344 }
345 
346 // TODO(antonin): maybe I should use meta-programming for const type handling,
347 // but I cannot think of an easy way, so I am leaving it as is for now
348 
349 template <> inline
350 Field &ActionParam::to<Field &>(ActionEngineState *state) const {
351  assert(tag == ActionParam::FIELD);
352  return state->phv.get_field(field.header, field.field_offset);
353 }
354 
355 template <> inline
356 const Field &ActionParam::to<const Field &>(ActionEngineState *state) const {
357  return ActionParam::to<Field &>(state);
358 }
359 
360 template <> inline
361 Header &ActionParam::to<Header &>(ActionEngineState *state) const {
362  switch (tag) {
363  case ActionParam::HEADER:
364  return state->phv.get_header(header);
365  case ActionParam::EXPRESSION_HEADER:
366  return expression.ptr->eval_header(&state->phv,
367  state->action_data.action_data);
368  default:
369  _BM_UNREACHABLE("Default switch case should not be reachable");
370  }
371 }
372 
373 template <> inline
374 const Header &ActionParam::to<const Header &>(ActionEngineState *state) const {
375  return ActionParam::to<Header &>(state);
376 }
377 
378 template <> inline
379 HeaderStack &ActionParam::to<HeaderStack &>(ActionEngineState *state) const {
380  switch (tag) {
381  case ActionParam::HEADER_STACK:
382  return state->phv.get_header_stack(header_stack);
383  case ActionParam::EXPRESSION_HEADER_STACK:
384  return expression.ptr->eval_header_stack(&state->phv,
385  state->action_data.action_data);
386  default:
387  _BM_UNREACHABLE("Default switch case should not be reachable");
388  }
389 }
390 
391 template <> inline
392 const HeaderStack &ActionParam::to<const HeaderStack &>(
393  ActionEngineState *state) const {
394  return ActionParam::to<HeaderStack &>(state);
395 }
396 
397 template <> inline
398 StackIface &ActionParam::to<StackIface &>(ActionEngineState *state) const {
399  switch (tag) {
400  case HEADER_STACK:
401  return state->phv.get_header_stack(header_stack);
402  case ActionParam::EXPRESSION_HEADER_STACK:
403  return expression.ptr->eval_header_stack(
404  &state->phv, state->action_data.action_data);
405  case HEADER_UNION_STACK:
406  return state->phv.get_header_union_stack(header_union_stack);
407  case ActionParam::EXPRESSION_HEADER_UNION_STACK:
408  return expression.ptr->eval_header_union_stack(
409  &state->phv, state->action_data.action_data);
410  default:
411  _BM_UNREACHABLE("Default switch case should not be reachable");
412  }
413 }
414 
415 template <> inline
416 const StackIface &ActionParam::to<const StackIface &>(
417  ActionEngineState *state) const {
418  return ActionParam::to<StackIface &>(state);
419 }
420 
421 template <> inline
422 HeaderUnion &ActionParam::to<HeaderUnion &>(ActionEngineState *state) const {
423  switch (tag) {
424  case ActionParam::HEADER_UNION:
425  return state->phv.get_header_union(header_union);
426  case ActionParam::EXPRESSION_HEADER_UNION:
427  return expression.ptr->eval_header_union(&state->phv,
428  state->action_data.action_data);
429  default:
430  _BM_UNREACHABLE("Default switch case should not be reachable");
431  }
432 }
433 
434 template <> inline
435 const HeaderUnion &ActionParam::to<const HeaderUnion &>(
436  ActionEngineState *state) const {
437  return ActionParam::to<HeaderUnion &>(state);
438 }
439 
440 template <> inline
441 HeaderUnionStack &ActionParam::to<HeaderUnionStack &>(
442  ActionEngineState *state) const {
443  switch (tag) {
444  case ActionParam::HEADER_UNION_STACK:
445  return state->phv.get_header_union_stack(header_union_stack);
446  case ActionParam::EXPRESSION_HEADER_UNION_STACK:
447  return expression.ptr->eval_header_union_stack(
448  &state->phv, state->action_data.action_data);
449  default:
450  _BM_UNREACHABLE("Default switch case should not be reachable");
451  }
452 }
453 
454 template <> inline
455 const HeaderUnionStack &ActionParam::to<const HeaderUnionStack &>(
456  ActionEngineState *state) const {
457  return ActionParam::to<HeaderUnionStack &>(state);
458 }
459 
460 template <> inline
461 const NamedCalculation &ActionParam::to<const NamedCalculation &>(
462  ActionEngineState *state) const {
463  (void) state;
464  assert(tag == ActionParam::CALCULATION);
465  return *(calculation);
466 }
467 
468 template <> inline
469 MeterArray &ActionParam::to<MeterArray &>(ActionEngineState *state) const {
470  (void) state;
471  assert(tag == ActionParam::METER_ARRAY);
472  return *(meter_array);
473 }
474 
475 template <> inline
476 const MeterArray &ActionParam::to<const MeterArray &>(
477  ActionEngineState *state) const {
478  return ActionParam::to<MeterArray &>(state);
479 }
480 
481 template <> inline
482 CounterArray &ActionParam::to<CounterArray &>(ActionEngineState *state) const {
483  (void) state;
484  assert(tag == ActionParam::COUNTER_ARRAY);
485  return *(counter_array);
486 }
487 
488 template <> inline
489 const CounterArray &ActionParam::to<const CounterArray &>(
490  ActionEngineState *state) const {
491  return ActionParam::to<CounterArray &>(state);
492 }
493 
494 template <> inline
495 RegisterArray &ActionParam::to<RegisterArray &>(
496  ActionEngineState *state) const {
497  (void) state;
498  assert(tag == ActionParam::REGISTER_ARRAY);
499  return *(register_array);
500 }
501 
502 template <> inline
503 const RegisterArray &ActionParam::to<const RegisterArray &>(
504  ActionEngineState *state) const {
505  return ActionParam::to<RegisterArray &>(state);
506 }
507 
508 template <> inline
509 ExternType *ActionParam::to<ExternType *>(ActionEngineState *state) const {
510  (void) state;
511  assert(tag == ActionParam::EXTERN_INSTANCE);
512  return extern_instance;
513 }
514 
515 template <> inline
516 const std::string &ActionParam::to<const std::string &>(
517  ActionEngineState *state) const {
518  assert(tag == ActionParam::STRING);
519  (void) state;
520  return *str;
521 }
522 
523 // just a convenience function, I expect the version above to be the most used
524 // one
525 template <> inline
526 const char *ActionParam::to<const char *>(ActionEngineState *state) const {
527  assert(tag == ActionParam::STRING);
528  (void) state;
529  return str->c_str();
530 }
531 
532 // used for primitives that take as a parameter a "list" of values. Currently we
533 // only support "const std::vector<Data>" as the type used by the C++ primitive
534 // and currently this is only used by log_msg.
535 // TODO(antonin): more types (e.g. "const std::vector<const Data &>") if needed
536 // we can support list of other value types as well, e.g. list of headers
537 // we are not limited to using a vector as the data structure either
538 template <> inline
539 const std::vector<Data>
540 ActionParam::to<const std::vector<Data>>(ActionEngineState *state) const {
541  _BM_ASSERT(tag == ActionParam::PARAMS_VECTOR && "not a params vector");
542  std::vector<Data> vec;
543 
544  for (auto i = params_vector.start ; i < params_vector.end ; i++) {
545  // re-use previously-defined cast method; note that we use to<const Data &>
546  // and not to<const Data>, as it does not exists
547  // if something in the parameters_vector cannot be cast to "const Data &",
548  // the code will assert
549  vec.push_back(state->parameters_vector[i].to<const Data &>(state));
550  }
551 
552  return vec;
553 }
554 
555 template <> inline
556 const std::vector<Field>
557 ActionParam::to<const std::vector<Field>>(ActionEngineState *state) const {
558  _BM_ASSERT(tag == ActionParam::FIELD_LIST && "not a field list");
559  std::vector<Field> vec;
560 
561  for (auto i = field_list.start ; i < field_list.end ; i++) {
562  // re-use previously-defined cast method; note that we use to<const Field &>
563  // and not to<const Field>, as it does not exists
564  // if something in the field list cannot be cast to "const Field &",
565  // the code will assert
566  vec.push_back(state->field_list[i].to<const Field &>(state));
567  }
568 
569  return vec;
570 }
571 
572 /* This is adapted from stack overflow code:
573  http://stackoverflow.com/questions/11044504/any-solution-to-unpack-a-vector-to-function-arguments-in-c
574 */
575 
576 template <std::size_t... Indices>
577 struct indices {
578  using next = indices<Indices..., sizeof...(Indices)>;
579 };
580 
581 template <std::size_t N>
582 struct build_indices {
583  using type = typename build_indices<N-1>::type::next;
584 };
585 
586 template <>
587 struct build_indices<0> {
588  using type = indices<>;
589 };
590 
591 template <std::size_t N>
592 using BuildIndices = typename build_indices<N>::type;
593 
594 template <typename... Args>
595 struct unpack_caller {
596  private:
597  template <typename T, size_t... I>
598  void call(T *pObj, ActionEngineState *state,
599  const ActionParam *args,
600  const indices<I...>) {
601  // I guess we can have an unused parameter warning if an instantiation has
602  // an empty indices list
603  (void) state; (void) args;
604 #define ELEM_TYPE typename std::tuple_element<I, std::tuple<Args...>>::type
605  (*pObj)(args[I].to<ELEM_TYPE>(state)...);
606 #undef ELEM_TYPE
607  }
608 
609  public:
610  template <typename T>
611  void operator () (T* pObj, ActionEngineState *state,
612  const ActionParam *args){
613  // assert(args.size() == sizeof...(Args)); // just to be sure
614  call(pObj, state, args, BuildIndices<sizeof...(Args)>{});
615  }
616 };
617 
618 class ActionPrimitive_ {
619  public:
620  virtual ~ActionPrimitive_() { }
621 
622  virtual void execute(
623  ActionEngineState *state,
624  const ActionParam *args) = 0;
625 
626  virtual size_t get_num_params() const = 0;
627 
628  virtual size_t get_jump_offset(size_t current_offset) const {
629  return current_offset + 1;
630  }
631 
632  void _set_p4objects(P4Objects *p4objects) {
633  this->p4objects = p4objects;
634  }
635 
636  void set_source_info(SourceInfo *source_info) {
637  call_source_info = source_info;
638  }
639 
640  protected:
641  // This used to be regular members in ActionPrimitive, but there could be a
642  // race condition. Making them thread_local solves the issue. I moved these
643  // from ActionPrimitive because g++4.8 has a bug when using thread_local class
644  // variables with templates
645  // (https://gcc.gnu.org/bugzilla/show_bug.cgi?id=60056).
646  static thread_local Packet *pkt;
647  static thread_local PHV *phv;
648  static thread_local SourceInfo *call_source_info;
649 
650  P4Objects *get_p4objects() {
651  return p4objects;
652  }
653 
654  private:
655  P4Objects *p4objects{nullptr};
656 };
657 
661 template <typename... Args>
662 class ActionPrimitive : public ActionPrimitive_ {
663  public:
664  void execute(ActionEngineState *state, const ActionParam *args) override {
665  phv = &(state->phv);
666  pkt = &(state->pkt);
667  caller(this, state, args);
668  }
669 
670  // cannot make it constexpr because it is virtual
671  size_t get_num_params() const override { return sizeof...(Args); }
672 
673  virtual void operator ()(Args...) = 0;
674 
675  protected:
678  Field &get_field(const std::string &name) {
679  return phv->get_field(name);
680  }
681 
683  Header &get_header(const std::string &name) {
684  return phv->get_header(name);
685  }
686 
687  // in practice, regular primitives should not have to use this
688  PHV &get_phv() {
689  return *phv;
690  }
691 
692  // so far used only for meter arrays and counter arrays
696  return *pkt;
697  }
698 
699  private:
700  unpack_caller<Args...> caller;
701  // See thread_local members in ActionPrimitive_ class
702  // PHV *phv;
703  // Packet *pkt;
704 };
705 
706 
707 // forward declaration
708 class ActionFnEntry;
709 
710 class ActionPrimitiveCall {
711  public:
712  explicit ActionPrimitiveCall(
713  ActionPrimitive_ *primitive, size_t param_offset,
714  std::unique_ptr<SourceInfo> source_info = nullptr);
715 
716  void execute(ActionEngineState *state, const ActionParam *args) const;
717 
718  size_t get_num_params() const;
719 
720  size_t get_param_offset() const { return param_offset; }
721 
722  size_t get_jump_offset(size_t current_offset) const {
723  return primitive->get_jump_offset(current_offset);
724  }
725 
726  SourceInfo *get_source_info() const { return source_info.get(); }
727 
728  private:
729  ActionPrimitive_ *primitive;
730  size_t param_offset;
731  std::unique_ptr<SourceInfo> source_info;
732 };
733 
734 class ActionFn : public NamedP4Object {
735  friend class ActionFnEntry;
736 
737  public:
738  ActionFn(const std::string &name, p4object_id_t id, size_t num_params);
739 
740  ActionFn(const std::string &name, p4object_id_t id, size_t num_params,
741  std::unique_ptr<SourceInfo> source_info);
742 
743  // these parameter_push_back_* methods are not very well named. They are used
744  // to push arguments to the primitives; and are independent of the actual
745  // parameters for the P4 action
746  void parameter_push_back_field(header_id_t header, int field_offset);
747  void parameter_push_back_header(header_id_t header);
748  void parameter_push_back_header_stack(header_stack_id_t header_stack);
749  void parameter_push_back_last_header_stack_field(
750  header_stack_id_t header_stack, int field_offset);
751  void parameter_push_back_header_union(header_union_id_t header_union);
752  void parameter_push_back_header_union_stack(
753  header_union_stack_id_t header_union_stack);
754  void parameter_push_back_const(const Data &data);
755  void parameter_push_back_action_data(int action_data_offset);
756  void parameter_push_back_register_ref(RegisterArray *register_array,
757  unsigned int idx);
758  void parameter_push_back_register_gen(RegisterArray *register_array,
759  std::unique_ptr<ArithExpression> idx);
760  void parameter_push_back_calculation(const NamedCalculation *calculation);
761  void parameter_push_back_meter_array(MeterArray *meter_array);
762  void parameter_push_back_counter_array(CounterArray *counter_array);
763  void parameter_push_back_register_array(RegisterArray *register_array);
764  void parameter_push_back_expression(std::unique_ptr<Expression> expr);
765  void parameter_push_back_expression(std::unique_ptr<Expression> expr,
766  ExprType expr_type);
767  void parameter_push_back_extern_instance(ExternType *extern_instance);
768  void parameter_push_back_string(const std::string &str);
769 
770  // These methods are used when we need to push a "vector of parameters"
771  // (i.e. when a primitive, such as log_msg, uses a list of values as one of
772  // its parameters). First parameter_start_vector is called to signal the
773  // beginning of the "vector of parameters", then the parameter_push_back_*
774  // methods are called as usual, and finally parameter_end_vector is called to
775  // signal the end.
776  void parameter_start_vector();
777  void parameter_end_vector();
778  void parameter_start_field_list();
779  void parameter_end_field_list();
780 
781  void push_back_primitive(ActionPrimitive_ *primitive,
782  std::unique_ptr<SourceInfo> source_info = nullptr);
783 
784  void grab_register_accesses(RegisterSync *register_sync) const;
785 
786  size_t get_num_params() const;
787 
788  private:
789  using ParameterList = std::vector<ActionParam>;
790 
791  std::vector<ActionPrimitiveCall> primitives{};
792  ParameterList params{};
793  ParameterList sub_params{};
794  RegisterSync register_sync{};
795  std::vector<Data> const_values{};
796  // should I store the objects in the vector, instead of pointers?
797  std::vector<std::unique_ptr<Expression> > expressions{};
798  std::vector<std::string> strings{};
799  size_t num_params;
800 
801  private:
802  static size_t nb_data_tmps;
803 };
804 
805 class ActionFnEntry {
806  public:
807  ActionFnEntry() { }
808 
809  ActionFnEntry(const ActionFn *action_fn, ActionData action_data)
810  : action_fn(action_fn), action_data(std::move(action_data)) { }
811 
812  explicit ActionFnEntry(const ActionFn *action_fn)
813  : action_fn(action_fn) { }
814 
815  // log event, notify debugger if needed, lock registers, then call execute()
816  void operator()(Packet *pkt) const;
817 
818  void execute(Packet *pkt) const;
819 
820  void push_back_action_data(const Data &data);
821 
822  void push_back_action_data(unsigned int data);
823 
824  void push_back_action_data(const char *bytes, int nbytes);
825 
826  size_t action_data_size() const { return action_data.size(); }
827 
828  const Data &get_action_data_at(int offset) const {
829  return action_data.get(offset);
830  }
831 
832  const ActionData &get_action_data() const {
833  return action_data;
834  }
835 
836  void dump(std::ostream *stream) const;
837 
838  void serialize(std::ostream *out) const;
839  void deserialize(std::istream *in, const P4Objects &objs);
840 
841  p4object_id_t get_action_id() const {
842  if (!action_fn) return std::numeric_limits<p4object_id_t>::max();
843  return action_fn->get_id();
844  }
845 
846  const ActionFn *get_action_fn() const {
847  return action_fn;
848  }
849 
850  ActionFnEntry(const ActionFnEntry &other) = default;
851  ActionFnEntry &operator=(const ActionFnEntry &other) = default;
852 
853  // NOLINTNEXTLINE(whitespace/operators)
854  ActionFnEntry(ActionFnEntry &&other) /*noexcept*/ = default;
855  // NOLINTNEXTLINE(whitespace/operators)
856  ActionFnEntry &operator=(ActionFnEntry &&other) /*noexcept*/ = default;
857 
858  private:
859  const ActionFn *action_fn{nullptr};
860  ActionData action_data{};
861 };
862 
863 } // namespace bm
864 
865 #endif // BM_BM_SIM_ACTIONS_H_
bm::ActionPrimitive::get_packet
Packet & get_packet()
Definition: actions.h:695
named_p4object.h
bm::ActionPrimitive
Definition: actions.h:662
bm::Packet
Definition: packet.h:98
bm::ActionPrimitive::get_field
Field & get_field(const std::string &name)
Definition: actions.h:678
stateful.h
bm::Field
Definition: fields.h:46
bm::HeaderStack
detail::MyStack< Header > HeaderStack
Definition: stacks.h:212
bm::Header
Definition: headers.h:147
bm::HeaderUnionStack
detail::MyStack< HeaderUnion > HeaderUnionStack
Definition: stacks.h:223
bm::PHV
Definition: phv.h:68
phv.h
bm::ActionPrimitive::get_header
Header & get_header(const std::string &name)
Returns a reference to the Header with name name.
Definition: actions.h:683