bmv2
Designing your own switch target with bmv2
Loading...
Searching...
No Matches
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
92namespace bm {
93
94class P4Objects; // forward declaration for deserialize
95
96// some forward declarations for needed p4 objects
97class Packet;
98class NamedCalculation;
99class MeterArray;
100class CounterArray;
101
102// forward declaration of ActionPrimitive_
103class 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.
108class 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
145struct 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
165struct ActionEngineState;
166
167class 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.
173struct 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
267struct 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
286template <> inline
287Data &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
310template <> inline
311const 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
349template <> inline
350Field &ActionParam::to<Field &>(ActionEngineState *state) const {
351 assert(tag == ActionParam::FIELD);
352 return state->phv.get_field(field.header, field.field_offset);
353}
354
355template <> inline
356const Field &ActionParam::to<const Field &>(ActionEngineState *state) const {
357 return ActionParam::to<Field &>(state);
358}
359
360template <> inline
361Header &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
373template <> inline
374const Header &ActionParam::to<const Header &>(ActionEngineState *state) const {
375 return ActionParam::to<Header &>(state);
376}
377
378template <> inline
379HeaderStack &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
391template <> inline
392const HeaderStack &ActionParam::to<const HeaderStack &>(
393 ActionEngineState *state) const {
394 return ActionParam::to<HeaderStack &>(state);
395}
396
397template <> inline
398StackIface &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
415template <> inline
416const StackIface &ActionParam::to<const StackIface &>(
417 ActionEngineState *state) const {
418 return ActionParam::to<StackIface &>(state);
419}
420
421template <> inline
422HeaderUnion &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
434template <> inline
435const HeaderUnion &ActionParam::to<const HeaderUnion &>(
436 ActionEngineState *state) const {
437 return ActionParam::to<HeaderUnion &>(state);
438}
439
440template <> inline
441HeaderUnionStack &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
454template <> inline
455const HeaderUnionStack &ActionParam::to<const HeaderUnionStack &>(
456 ActionEngineState *state) const {
457 return ActionParam::to<HeaderUnionStack &>(state);
458}
459
460template <> inline
461const NamedCalculation &ActionParam::to<const NamedCalculation &>(
462 ActionEngineState *state) const {
463 (void) state;
464 assert(tag == ActionParam::CALCULATION);
465 return *(calculation);
466}
467
468template <> inline
469MeterArray &ActionParam::to<MeterArray &>(ActionEngineState *state) const {
470 (void) state;
471 assert(tag == ActionParam::METER_ARRAY);
472 return *(meter_array);
473}
474
475template <> inline
476const MeterArray &ActionParam::to<const MeterArray &>(
477 ActionEngineState *state) const {
478 return ActionParam::to<MeterArray &>(state);
479}
480
481template <> inline
482CounterArray &ActionParam::to<CounterArray &>(ActionEngineState *state) const {
483 (void) state;
484 assert(tag == ActionParam::COUNTER_ARRAY);
485 return *(counter_array);
486}
487
488template <> inline
489const CounterArray &ActionParam::to<const CounterArray &>(
490 ActionEngineState *state) const {
491 return ActionParam::to<CounterArray &>(state);
492}
493
494template <> inline
495RegisterArray &ActionParam::to<RegisterArray &>(
496 ActionEngineState *state) const {
497 (void) state;
498 assert(tag == ActionParam::REGISTER_ARRAY);
499 return *(register_array);
500}
501
502template <> inline
503const RegisterArray &ActionParam::to<const RegisterArray &>(
504 ActionEngineState *state) const {
505 return ActionParam::to<RegisterArray &>(state);
506}
507
508template <> inline
509ExternType *ActionParam::to<ExternType *>(ActionEngineState *state) const {
510 (void) state;
511 assert(tag == ActionParam::EXTERN_INSTANCE);
512 return extern_instance;
513}
514
515template <> inline
516const 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
525template <> inline
526const 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
538template <> inline
539const std::vector<Data>
540ActionParam::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
555template <> inline
556const std::vector<Field>
557ActionParam::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
576template <std::size_t... Indices>
577struct indices {
578 using next = indices<Indices..., sizeof...(Indices)>;
579};
580
581template <std::size_t N>
582struct build_indices {
583 using type = typename build_indices<N-1>::type::next;
584};
585
586template <>
587struct build_indices<0> {
588 using type = indices<>;
589};
590
591template <std::size_t N>
592using BuildIndices = typename build_indices<N>::type;
593
594template <typename... Args>
595struct 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
618class 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
661template <typename... Args>
662class 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
708class ActionFnEntry;
709
710class 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
734class 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
805class 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_
Definition actions.h:662
Field & get_field(const std::string &name)
Definition actions.h:678
Header & get_header(const std::string &name)
Returns a reference to the Header with name name.
Definition actions.h:683
Packet & get_packet()
Definition actions.h:695
Definition fields.h:46
Definition headers.h:147
Definition phv.h:68
Definition packet.h:98
detail::MyStack< Header > HeaderStack
Definition stacks.h:212
detail::MyStack< HeaderUnion > HeaderUnionStack
Definition stacks.h:223