]>
Commit | Line | Data |
---|---|---|
4f082dfa MM |
1 | m4_divert(-1)m4_dnl |
2 | # | |
3 | # BIRD -- Construction of per-instruction structures | |
4 | # | |
5 | # (c) 2018 Maria Matejka <mq@jmq.cz> | |
6 | # | |
7 | # Can be freely distributed and used under the terms of the GNU GPL. | |
8 | # | |
550a6488 MM |
9 | # THIS IS A M4 MACRO FILE GENERATING 3 FILES ALTOGETHER. |
10 | # KEEP YOUR HANDS OFF UNLESS YOU KNOW WHAT YOU'RE DOING. | |
11 | # EDITING AND DEBUGGING THIS FILE MAY DAMAGE YOUR BRAIN SERIOUSLY. | |
4f082dfa | 12 | # |
550a6488 MM |
13 | # But you're welcome to read and edit and debug if you aren't scared. |
14 | # | |
15 | # Uncomment the following line to get exhaustive debug output. | |
16 | # m4_debugmode(aceflqtx) | |
17 | # | |
18 | # How it works: | |
19 | # 1) Instruction to code conversion (uses diversions 100..199) | |
20 | # 2) Code wrapping (uses diversions 1..99) | |
21 | # 3) Final preparation (uses diversions 200..299) | |
22 | # 4) Shipout | |
23 | # | |
24 | # See below for detailed description. | |
4f082dfa | 25 | # |
4f082dfa | 26 | # |
550a6488 MM |
27 | # 1) Instruction to code conversion |
28 | # The code provided in f-inst.c between consecutive INST() calls | |
29 | # is interleaved for many different places. It is here processed | |
30 | # and split into separate instances where split-by-instruction | |
31 | # happens. These parts are stored in temporary diversions listed: | |
4f082dfa | 32 | # |
ea4f55e3 | 33 | # 101 content of per-inst struct |
04160812 MM |
34 | # 102 constructor arguments |
35 | # 103 constructor body | |
de12cd18 | 36 | # 104 dump line item content |
550a6488 MM |
37 | # (there may be nothing in dump-line content and |
38 | # it must be handled specially in phase 2) | |
23e3b1e6 | 39 | # 105 linearize body |
132529ce | 40 | # 106 comparator body |
d1039926 MM |
41 | # 107 struct f_line_item content |
42 | # 108 interpreter body | |
04160812 | 43 | # |
550a6488 | 44 | # Here are macros to allow you to _divert to the right directions. |
4f082dfa MM |
45 | m4_define(FID_STRUCT_IN, `m4_divert(101)') |
46 | m4_define(FID_NEW_ARGS, `m4_divert(102)') | |
47 | m4_define(FID_NEW_BODY, `m4_divert(103)') | |
dd4d4095 | 48 | m4_define(FID_DUMP_BODY, `m4_divert(104)m4_define([[FID_DUMP_BODY_EXISTS]])') |
550a6488 | 49 | m4_define(FID_LINEARIZE_BODY, `m4_divert(105)') |
132529ce | 50 | m4_define(FID_SAME_BODY, `m4_divert(106)') |
ea4f55e3 | 51 | m4_define(FID_LINE_IN, `m4_divert(107)') |
d1039926 | 52 | m4_define(FID_INTERPRET_BODY, `m4_divert(108)') |
4f082dfa | 53 | |
550a6488 MM |
54 | # Sometimes you want slightly different code versions in different |
55 | # outputs. | |
56 | # Use FID_HIC(code for inst-gen.h, code for inst-gen.c, code for inst-interpret.c) | |
57 | # and put it into [[ ]] quotes if it shall contain commas. | |
b40c0f02 MM |
58 | m4_define(FID_HIC, `m4_ifelse(TARGET, [[H]], [[$1]], TARGET, [[I]], [[$2]], TARGET, [[C]], [[$3]])') |
59 | ||
550a6488 | 60 | # In interpreter code, this is quite common. |
b40c0f02 MM |
61 | m4_define(FID_INTERPRET_EXEC, `FID_HIC(,[[FID_INTERPRET_BODY()]],[[m4_divert(-1)]])') |
62 | m4_define(FID_INTERPRET_NEW, `FID_HIC(,[[m4_divert(-1)]],[[FID_INTERPRET_BODY()]])') | |
550a6488 MM |
63 | |
64 | # If the instruction is never converted to constant, the interpret | |
65 | # code is not produced at all for constructor | |
b40c0f02 MM |
66 | m4_define(NEVER_CONSTANT, `m4_define([[INST_NEVER_CONSTANT]])') |
67 | m4_define(FID_IFCONST, `m4_ifdef([[INST_NEVER_CONSTANT]],[[$2]],[[$1]])') | |
87bd7cd7 | 68 | |
550a6488 MM |
69 | # If the instruction has some attributes (here called members), |
70 | # these are typically carried with the instruction from constructor | |
71 | # to interpreter. This yields a line of code everywhere on the path. | |
72 | # FID_MEMBER is a macro to help with this task. | |
4f082dfa | 73 | m4_define(FID_MEMBER, `m4_dnl |
84c58aab MM |
74 | FID_LINE_IN()m4_dnl |
75 | $1 $2; | |
76 | FID_STRUCT_IN()m4_dnl | |
77 | $1 $2; | |
78 | FID_NEW_ARGS()m4_dnl | |
79 | , $1 $2 | |
263fa2c4 | 80 | FID_NEW_BODY()m4_dnl |
63f49457 | 81 | whati->$2 = $2; |
263fa2c4 | 82 | FID_LINEARIZE_BODY()m4_dnl |
b40c0f02 MM |
83 | item->$2 = whati->$2; |
84 | m4_ifelse($3,,,[[ | |
263fa2c4 | 85 | FID_SAME_BODY()m4_dnl |
b40c0f02 | 86 | if ($3) return 0; |
132529ce | 87 | ]]) |
b40c0f02 | 88 | m4_ifelse($4,,,[[ |
263fa2c4 | 89 | FID_DUMP_BODY()m4_dnl |
f634adc7 | 90 | debug("%s" $4 "\n", INDENT, $5); |
d1039926 | 91 | ]]) |
263fa2c4 | 92 | FID_INTERPRET_EXEC()m4_dnl |
b40c0f02 | 93 | const $1 $2 = whati->$2 |
550a6488 | 94 | FID_INTERPRET_BODY') |
4f082dfa | 95 | |
550a6488 MM |
96 | # Instruction arguments are needed only until linearization is done. |
97 | # This puts the arguments into the filter line to be executed before | |
98 | # the instruction itself. | |
99 | # | |
100 | # To achieve this, ARG_ANY must be called before anything writes into | |
101 | # the instruction line as it moves the instruction pointer forward. | |
d1039926 | 102 | m4_define(ARG_ANY, ` |
84c58aab MM |
103 | FID_STRUCT_IN()m4_dnl |
104 | struct f_inst * f$1; | |
105 | FID_NEW_ARGS()m4_dnl | |
106 | , struct f_inst * f$1 | |
87512e97 | 107 | FID_NEW_BODY()m4_dnl |
63f49457 | 108 | whati->f$1 = f$1; |
b40c0f02 MM |
109 | for (const struct f_inst *child = f$1; child; child = child->next) { |
110 | what->size += child->size; | |
111 | FID_IFCONST([[ | |
112 | if (child->fi_code != FI_CONSTANT) | |
113 | constargs = 0; | |
114 | ]]) | |
115 | } | |
23e3b1e6 | 116 | FID_LINEARIZE_BODY |
b40c0f02 | 117 | pos = linearize(dest, whati->f$1, pos); |
550a6488 | 118 | FID_INTERPRET_BODY()') |
d1039926 | 119 | |
c0999a14 MM |
120 | # Some instructions accept variable number of arguments. |
121 | m4_define(VARARG, ` | |
122 | FID_NEW_ARGS()m4_dnl | |
123 | , struct f_inst * fvar | |
124 | FID_STRUCT_IN()m4_dnl | |
125 | struct f_inst * fvar; | |
126 | uint varcount; | |
127 | FID_LINE_IN()m4_dnl | |
128 | uint varcount; | |
129 | FID_NEW_BODY()m4_dnl | |
130 | whati->varcount = 0; | |
131 | whati->fvar = fvar; | |
132 | for (const struct f_inst *child = fvar; child; child = child->next, whati->varcount++) { | |
133 | what->size += child->size; | |
134 | FID_IFCONST([[ | |
135 | if (child->fi_code != FI_CONSTANT) | |
136 | constargs = 0; | |
137 | ]]) | |
138 | } | |
139 | FID_IFCONST([[ | |
140 | const struct f_inst **items = NULL; | |
d65a926a | 141 | if (constargs && whati->varcount) { |
c0999a14 MM |
142 | items = alloca(whati->varcount * sizeof(struct f_inst *)); |
143 | const struct f_inst *child = fvar; | |
144 | for (uint i=0; child; i++) | |
145 | child = (items[i] = child)->next; | |
146 | } | |
147 | ]]) | |
148 | FID_LINEARIZE_BODY()m4_dnl | |
149 | pos = linearize(dest, whati->fvar, pos); | |
150 | item->varcount = whati->varcount; | |
151 | FID_DUMP_BODY()m4_dnl | |
152 | debug("%snumber of varargs %u\n", INDENT, item->varcount); | |
153 | FID_SAME_BODY()m4_dnl | |
154 | if (f1->varcount != f2->varcount) return 0; | |
155 | FID_INTERPRET_BODY() | |
156 | FID_HIC(,[[ | |
157 | if (fstk->vcnt < whati->varcount) runtime("Stack underflow"); | |
158 | fstk->vcnt -= whati->varcount; | |
159 | ]],) | |
160 | ') | |
161 | ||
550a6488 | 162 | # Some arguments need to check their type. After that, ARG_ANY is called. |
26194bd6 | 163 | m4_define(ARG, `ARG_ANY($1) ARG_TYPE($1,$2)') |
ef8c4574 OZ |
164 | m4_define(ARG_TYPE, `ARG_TYPE_STATIC($1,$2) ARG_TYPE_DYNAMIC($1,$2)') |
165 | ||
166 | m4_define(ARG_TYPE_STATIC, ` | |
6fbcd891 | 167 | FID_NEW_BODY()m4_dnl |
c00c20a7 | 168 | if (f$1->type && (f$1->type != ($2)) && !f_const_promotion(f$1, ($2))) |
87512e97 OZ |
169 | cf_error("Argument $1 of %s must be of type %s, got type %s", |
170 | f_instruction_name(what->fi_code), f_type_name($2), f_type_name(f$1->type)); | |
ef8c4574 OZ |
171 | FID_INTERPRET_BODY()') |
172 | ||
173 | m4_define(ARG_TYPE_DYNAMIC, ` | |
263fa2c4 | 174 | FID_INTERPRET_EXEC()m4_dnl |
26194bd6 | 175 | if (v$1.type != ($2)) |
87512e97 OZ |
176 | runtime("Argument $1 of %s must be of type %s, got type %s", |
177 | f_instruction_name(what->fi_code), f_type_name($2), f_type_name(v$1.type)); | |
178 | FID_INTERPRET_BODY()') | |
179 | ||
180 | m4_define(ARG_SAME_TYPE, ` | |
181 | FID_NEW_BODY()m4_dnl | |
182 | if (f$1->type && f$2->type && (f$1->type != f$2->type) && | |
183 | !f_const_promotion(f$2, f$1->type) && !f_const_promotion(f$1, f$2->type)) | |
184 | cf_error("Arguments $1 and $2 of %s must be of the same type", f_instruction_name(what->fi_code)); | |
550a6488 | 185 | FID_INTERPRET_BODY()') |
d1039926 | 186 | |
550a6488 MM |
187 | # Executing another filter line. This replaces the recursion |
188 | # that was needed in the former implementation. | |
189 | m4_define(LINEX, `FID_INTERPRET_EXEC()LINEX_($1)FID_INTERPRET_NEW()return $1 FID_INTERPRET_BODY()') | |
b40c0f02 | 190 | m4_define(LINEX_, `do { |
1757a6fc MM |
191 | fstk->estk[fstk->ecnt].pos = 0; |
192 | fstk->estk[fstk->ecnt].line = $1; | |
193 | fstk->estk[fstk->ecnt].ventry = fstk->vcnt; | |
194 | fstk->estk[fstk->ecnt].vbase = fstk->estk[fstk->ecnt-1].vbase; | |
195 | fstk->estk[fstk->ecnt].emask = 0; | |
196 | fstk->ecnt++; | |
b40c0f02 | 197 | } while (0)') |
d1039926 | 198 | |
ea4f55e3 | 199 | m4_define(LINE, ` |
84c58aab MM |
200 | FID_LINE_IN()m4_dnl |
201 | const struct f_line * fl$1; | |
202 | FID_STRUCT_IN()m4_dnl | |
203 | struct f_inst * f$1; | |
204 | FID_NEW_ARGS()m4_dnl | |
205 | , struct f_inst * f$1 | |
263fa2c4 | 206 | FID_NEW_BODY()m4_dnl |
63f49457 | 207 | whati->f$1 = f$1; |
263fa2c4 | 208 | FID_DUMP_BODY()m4_dnl |
ea4f55e3 | 209 | f_dump_line(item->fl$1, indent + 1); |
263fa2c4 | 210 | FID_LINEARIZE_BODY()m4_dnl |
63f49457 | 211 | item->fl$1 = f_linearize(whati->f$1); |
263fa2c4 | 212 | FID_SAME_BODY()m4_dnl |
ea4f55e3 | 213 | if (!f_same(f1->fl$1, f2->fl$1)) return 0; |
263fa2c4 | 214 | FID_INTERPRET_EXEC()m4_dnl |
d1039926 | 215 | do { if (whati->fl$1) { |
b40c0f02 MM |
216 | LINEX_(whati->fl$1); |
217 | } } while(0) | |
263fa2c4 | 218 | FID_INTERPRET_NEW()m4_dnl |
b40c0f02 | 219 | return whati->f$1 |
550a6488 | 220 | FID_INTERPRET_BODY()') |
d1039926 | 221 | |
550a6488 MM |
222 | # Some of the instructions have a result. These constructions |
223 | # state the result and put it to the right place. | |
6fbcd891 OZ |
224 | m4_define(RESULT, `RESULT_TYPE([[$1]]) RESULT_([[$1]],[[$2]],[[$3]])') |
225 | m4_define(RESULT_, `RESULT_VAL([[ (struct f_val) { .type = $1, .val.$2 = $3 } ]])') | |
b40c0f02 MM |
226 | m4_define(RESULT_VAL, `FID_HIC(, [[do { res = $1; fstk->vcnt++; } while (0)]], |
227 | [[return fi_constant(what, $1)]])') | |
f74d1976 | 228 | m4_define(RESULT_VOID, `RESULT_VAL([[ (struct f_val) { .type = T_VOID } ]])') |
d1039926 | 229 | |
6fbcd891 OZ |
230 | m4_define(ERROR, |
231 | `m4_errprint(m4___file__:m4___line__: $* | |
232 | )m4_m4exit(1)') | |
233 | ||
87512e97 | 234 | # This macro specifies result type and makes there are no conflicting definitions |
6fbcd891 OZ |
235 | m4_define(RESULT_TYPE, |
236 | `m4_ifdef([[INST_RESULT_TYPE]], | |
237 | [[m4_ifelse(INST_RESULT_TYPE,$1,,[[ERROR([[Multiple type definitons]])]])]], | |
87512e97 OZ |
238 | [[m4_define(INST_RESULT_TYPE,$1) RESULT_TYPE_($1)]])') |
239 | ||
240 | m4_define(RESULT_TYPE_, ` | |
241 | FID_NEW_BODY()m4_dnl | |
242 | what->type = $1; | |
243 | FID_INTERPRET_BODY()') | |
6fbcd891 | 244 | |
550a6488 | 245 | # Some common filter instruction members |
f634adc7 MM |
246 | m4_define(SYMBOL, `FID_MEMBER(struct symbol *, sym, [[strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)]], "symbol %s", item->sym->name)') |
247 | m4_define(RTC, `FID_MEMBER(struct rtable_config *, rtc, [[strcmp(f1->rtc->name, f2->rtc->name)]], "route table %s", item->rtc->name)') | |
b40c0f02 MM |
248 | m4_define(STATIC_ATTR, `FID_MEMBER(struct f_static_attr, sa, f1->sa.sa_code != f2->sa.sa_code,,)') |
249 | m4_define(DYNAMIC_ATTR, `FID_MEMBER(struct f_dynamic_attr, da, f1->da.ea_code != f2->da.ea_code,,)') | |
547be53b | 250 | m4_define(ACCESS_RTE, `FID_HIC(,[[do { if (!fs->rte) runtime("No route to access"); } while (0)]],NEVER_CONSTANT())') |
de12cd18 | 251 | |
550a6488 MM |
252 | # 2) Code wrapping |
253 | # The code produced in 1xx temporary diversions is a raw code without | |
254 | # any auxiliary commands and syntactical structures around. When the | |
255 | # instruction is done, INST_FLUSH is called. More precisely, it is called | |
256 | # at the beginning of INST() call and at the end of file. | |
257 | # | |
258 | # INST_FLUSH picks all the temporary diversions, wraps their content | |
259 | # into appropriate headers and structures and saves them into global | |
260 | # diversions listed: | |
261 | # | |
262 | # 4 enum fi_code | |
263 | # 5 enum fi_code to string | |
264 | # 6 dump line item | |
265 | # 7 dump line item callers | |
266 | # 8 linearize | |
267 | # 9 same (filter comparator) | |
268 | # 1 union in struct f_inst | |
269 | # 3 constructors + interpreter | |
270 | # | |
271 | # These global diversions contain blocks of code that can be directly | |
272 | # put into the final file, yet it still can't be written out now as | |
273 | # every instruction writes to all of these diversions. | |
274 | ||
84c58aab MM |
275 | # Code wrapping diversion names. Here we want an explicit newline |
276 | # after the C comment. | |
277 | m4_define(FID_ZONE, `m4_divert($1) /* $2 for INST_NAME() */ | |
278 | ') | |
550a6488 MM |
279 | m4_define(FID_INST, `FID_ZONE(1, Instruction structure for config)') |
280 | m4_define(FID_LINE, `FID_ZONE(2, Instruction structure for interpreter)') | |
281 | m4_define(FID_NEW, `FID_ZONE(3, Constructor)') | |
282 | m4_define(FID_ENUM, `FID_ZONE(4, Code enum)') | |
283 | m4_define(FID_ENUM_STR, `FID_ZONE(5, Code enum to string)') | |
284 | m4_define(FID_DUMP, `FID_ZONE(6, Dump line)') | |
285 | m4_define(FID_DUMP_CALLER, `FID_ZONE(7, Dump line caller)') | |
286 | m4_define(FID_LINEARIZE, `FID_ZONE(8, Linearize)') | |
287 | m4_define(FID_SAME, `FID_ZONE(9, Comparison)') | |
288 | ||
289 | # This macro does all the code wrapping. See inline comments. | |
290 | m4_define(INST_FLUSH, `m4_ifdef([[INST_NAME]], [[ | |
84c58aab MM |
291 | FID_ENUM()m4_dnl Contents of enum fi_code { ... } |
292 | INST_NAME(), | |
293 | FID_ENUM_STR()m4_dnl Contents of const char * indexed by enum fi_code | |
294 | [INST_NAME()] = "INST_NAME()", | |
295 | FID_INST()m4_dnl Anonymous structure inside struct f_inst | |
296 | struct { | |
297 | m4_undivert(101)m4_dnl | |
298 | } i_[[]]INST_NAME(); | |
299 | FID_LINE()m4_dnl Anonymous structure inside struct f_line_item | |
300 | struct { | |
301 | m4_undivert(107)m4_dnl | |
302 | } i_[[]]INST_NAME(); | |
303 | FID_NEW()m4_dnl Constructor and interpreter code together | |
550a6488 | 304 | FID_HIC( |
84c58aab | 305 | [[m4_dnl Public declaration of constructor in H file |
550a6488 | 306 | struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code |
84c58aab | 307 | m4_undivert(102)m4_dnl |
550a6488 | 308 | );]], |
84c58aab | 309 | [[m4_dnl The one case in The Big Switch inside interpreter |
550a6488 MM |
310 | case INST_NAME(): |
311 | #define whati (&(what->i_]]INST_NAME()[[)) | |
312 | m4_ifelse(m4_eval(INST_INVAL() > 0), 1, [[if (fstk->vcnt < INST_INVAL()) runtime("Stack underflow"); fstk->vcnt -= INST_INVAL(); ]]) | |
263fa2c4 | 313 | m4_undivert(108)m4_dnl |
550a6488 MM |
314 | #undef whati |
315 | break; | |
316 | ]], | |
84c58aab | 317 | [[m4_dnl Constructor itself |
550a6488 | 318 | struct f_inst *f_new_inst_]]INST_NAME()[[(enum f_instruction_code fi_code |
263fa2c4 | 319 | m4_undivert(102)m4_dnl |
550a6488 MM |
320 | ) |
321 | { | |
322 | /* Allocate the structure */ | |
323 | struct f_inst *what = fi_new(fi_code); | |
324 | FID_IFCONST([[uint constargs = 1;]]) | |
325 | ||
326 | /* Initialize all the members */ | |
327 | #define whati (&(what->i_]]INST_NAME()[[)) | |
263fa2c4 | 328 | m4_undivert(103)m4_dnl |
550a6488 MM |
329 | |
330 | /* If not constant, return the instruction itself */ | |
331 | FID_IFCONST([[if (!constargs)]]) | |
332 | return what; | |
333 | ||
334 | /* Try to pre-calculate the result */ | |
263fa2c4 | 335 | FID_IFCONST([[m4_undivert(108)]])m4_dnl |
550a6488 MM |
336 | #undef whati |
337 | } | |
338 | ]]) | |
339 | ||
84c58aab | 340 | FID_DUMP_CALLER()m4_dnl Case in another big switch used in instruction dumping (debug) |
550a6488 MM |
341 | case INST_NAME(): f_dump_line_item_]]INST_NAME()[[(item, indent + 1); break; |
342 | ||
84c58aab | 343 | FID_DUMP()m4_dnl The dumper itself |
550a6488 MM |
344 | m4_ifdef([[FID_DUMP_BODY_EXISTS]], |
345 | [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item_, const int indent)]], | |
346 | [[static inline void f_dump_line_item_]]INST_NAME()[[(const struct f_line_item *item UNUSED, const int indent UNUSED)]]) | |
347 | m4_undefine([[FID_DUMP_BODY_EXISTS]]) | |
348 | { | |
349 | #define item (&(item_->i_]]INST_NAME()[[)) | |
263fa2c4 | 350 | m4_undivert(104)m4_dnl |
550a6488 MM |
351 | #undef item |
352 | } | |
353 | ||
84c58aab | 354 | FID_LINEARIZE()m4_dnl The linearizer |
550a6488 MM |
355 | case INST_NAME(): { |
356 | #define whati (&(what->i_]]INST_NAME()[[)) | |
357 | #define item (&(dest->items[pos].i_]]INST_NAME()[[)) | |
263fa2c4 | 358 | m4_undivert(105)m4_dnl |
550a6488 MM |
359 | #undef whati |
360 | #undef item | |
361 | dest->items[pos].fi_code = what->fi_code; | |
362 | dest->items[pos].lineno = what->lineno; | |
363 | break; | |
364 | } | |
365 | ||
84c58aab | 366 | FID_SAME()m4_dnl This code compares two f_line"s while reconfiguring |
550a6488 MM |
367 | case INST_NAME(): |
368 | #define f1 (&(f1_->i_]]INST_NAME()[[)) | |
369 | #define f2 (&(f2_->i_]]INST_NAME()[[)) | |
263fa2c4 | 370 | m4_undivert(106)m4_dnl |
550a6488 MM |
371 | #undef f1 |
372 | #undef f2 | |
373 | break; | |
374 | ||
84c58aab | 375 | m4_divert(-1)FID_FLUSH(101,200)m4_dnl And finally this flushes all the unused diversions |
550a6488 MM |
376 | ]])') |
377 | ||
378 | m4_define(INST, `m4_dnl This macro is called on beginning of each instruction. | |
379 | INST_FLUSH()m4_dnl First, old data is flushed | |
380 | m4_define([[INST_NAME]], [[$1]])m4_dnl Then we store instruction name, | |
6fbcd891 OZ |
381 | m4_define([[INST_INVAL]], [[$2]])m4_dnl instruction input value count, |
382 | m4_undefine([[INST_NEVER_CONSTANT]])m4_dnl reset NEVER_CONSTANT trigger, | |
383 | m4_undefine([[INST_RESULT_TYPE]])m4_dnl and reset RESULT_TYPE value. | |
84c58aab | 384 | FID_INTERPRET_BODY()m4_dnl By default, every code is interpreter code. |
550a6488 MM |
385 | ') |
386 | ||
387 | # 3) Final preparation | |
388 | # | |
389 | # Now we prepare all the code around the global diversions. | |
390 | # It must be here, not in m4wrap, as we want M4 to mark the code | |
391 | # by #line directives correctly, not to claim that every single line | |
392 | # is at the beginning of the m4wrap directive. | |
393 | # | |
394 | # This part is split by the final file. | |
395 | # H for inst-gen.h | |
396 | # I for inst-interpret.c | |
397 | # C for inst-gen.c | |
398 | # | |
399 | # So we in cycle: | |
400 | # A. open a diversion | |
401 | # B. send there some code | |
402 | # C. close that diversion | |
403 | # D. flush a global diversion | |
404 | # E. open another diversion and goto B. | |
405 | # | |
406 | # Final diversions | |
407 | # 200+ completed text before it is flushed to output | |
408 | ||
409 | # This is a list of output diversions | |
de12cd18 | 410 | m4_define(FID_WR_PUT_LIST) |
550a6488 MM |
411 | |
412 | # This macro does the steps C to E, see before. | |
64bb1346 | 413 | m4_define(FID_WR_PUT_ALSO, `m4_define([[FID_WR_PUT_LIST]],FID_WR_PUT_LIST()[[FID_WR_DPUT(]]FID_WR_DIDX[[)FID_WR_DPUT(]]$1[[)]])m4_define([[FID_WR_DIDX]],m4_eval(FID_WR_DIDX+1))m4_divert(FID_WR_DIDX)') |
de12cd18 | 414 | |
550a6488 | 415 | # These macros do the splitting between H/I/C |
64bb1346 MM |
416 | m4_define(FID_WR_DIRECT, `m4_ifelse(TARGET,[[$1]],[[FID_WR_INIT()]],[[FID_WR_STOP()]])') |
417 | m4_define(FID_WR_INIT, `m4_define([[FID_WR_DIDX]],200)m4_define([[FID_WR_PUT]],[[FID_WR_PUT_ALSO($]][[@)]])m4_divert(200)') | |
418 | m4_define(FID_WR_STOP, `m4_define([[FID_WR_PUT]])m4_divert(-1)') | |
4f082dfa | 419 | |
550a6488 MM |
420 | # Here is the direct code to be put into the output files |
421 | # together with the undiversions, being hidden under FID_WR_PUT() | |
422 | ||
04160812 | 423 | m4_changequote([[,]]) |
64bb1346 | 424 | FID_WR_DIRECT(I) |
236828d0 | 425 | FID_WR_PUT(3) |
64bb1346 | 426 | FID_WR_DIRECT(C) |
c376555c MM |
427 | |
428 | #if defined(__GNUC__) && __GNUC__ >= 6 | |
429 | #pragma GCC diagnostic push | |
430 | #pragma GCC diagnostic ignored "-Wmisleading-indentation" | |
431 | #endif | |
432 | ||
87bd7cd7 MM |
433 | #include "nest/bird.h" |
434 | #include "filter/filter.h" | |
435 | #include "filter/f-inst.h" | |
b256f241 | 436 | |
b256f241 MM |
437 | /* Instruction codes to string */ |
438 | static const char * const f_instruction_name_str[] = { | |
439 | FID_WR_PUT(5) | |
440 | }; | |
441 | ||
442 | const char * | |
87512e97 | 443 | f_instruction_name_(enum f_instruction_code fi) |
b256f241 MM |
444 | { |
445 | if (fi < (sizeof(f_instruction_name_str) / sizeof(f_instruction_name_str[0]))) | |
446 | return f_instruction_name_str[fi]; | |
447 | else | |
448 | bug("Got unknown instruction code: %d", fi); | |
449 | } | |
450 | ||
4212c0e7 MM |
451 | static inline struct f_inst * |
452 | fi_new(enum f_instruction_code fi_code) | |
453 | { | |
454 | struct f_inst *what = cfg_allocz(sizeof(struct f_inst)); | |
455 | what->lineno = ifs->lino; | |
456 | what->size = 1; | |
457 | what->fi_code = fi_code; | |
458 | return what; | |
459 | } | |
460 | ||
b40c0f02 MM |
461 | static inline struct f_inst * |
462 | fi_constant(struct f_inst *what, struct f_val val) | |
463 | { | |
464 | what->fi_code = FI_CONSTANT; | |
465 | what->i_FI_CONSTANT.val = val; | |
466 | return what; | |
467 | } | |
468 | ||
c00c20a7 OZ |
469 | static int |
470 | f_const_promotion(struct f_inst *arg, enum f_type want) | |
471 | { | |
472 | if (arg->fi_code != FI_CONSTANT) | |
473 | return 0; | |
474 | ||
475 | struct f_val *c = &arg->i_FI_CONSTANT.val; | |
476 | ||
477 | if ((c->type == T_IP) && ipa_is_ip4(c->val.ip) && (want == T_QUAD)) { | |
478 | *c = (struct f_val) { | |
479 | .type = T_QUAD, | |
480 | .val.i = ipa_to_u32(c->val.ip), | |
481 | }; | |
482 | return 1; | |
483 | } | |
484 | ||
485 | return 0; | |
486 | } | |
487 | ||
b40c0f02 MM |
488 | #define v1 whati->f1->i_FI_CONSTANT.val |
489 | #define v2 whati->f2->i_FI_CONSTANT.val | |
490 | #define v3 whati->f3->i_FI_CONSTANT.val | |
c0999a14 | 491 | #define vv(i) items[i]->i_FI_CONSTANT.val |
b40c0f02 MM |
492 | #define runtime(fmt, ...) cf_error("filter preevaluation, line %d: " fmt, ifs->lino, ##__VA_ARGS__) |
493 | #define fpool cfg_mem | |
494 | #define falloc(size) cfg_alloc(size) | |
de12cd18 MM |
495 | /* Instruction constructors */ |
496 | FID_WR_PUT(3) | |
b40c0f02 MM |
497 | #undef v1 |
498 | #undef v2 | |
499 | #undef v3 | |
c0999a14 | 500 | #undef vv |
de12cd18 MM |
501 | |
502 | /* Line dumpers */ | |
503 | #define INDENT (((const char *) f_dump_line_indent_str) + sizeof(f_dump_line_indent_str) - (indent) - 1) | |
504 | static const char f_dump_line_indent_str[] = " "; | |
de12cd18 MM |
505 | |
506 | FID_WR_PUT(6) | |
507 | ||
ea4f55e3 | 508 | void f_dump_line(const struct f_line *dest, uint indent) |
de12cd18 MM |
509 | { |
510 | if (!dest) { | |
511 | debug("%sNo filter line (NULL)\n", INDENT); | |
512 | return; | |
513 | } | |
514 | debug("%sFilter line %p (len=%u)\n", INDENT, dest, dest->len); | |
515 | for (uint i=0; i<dest->len; i++) { | |
516 | const struct f_line_item *item = &dest->items[i]; | |
87512e97 | 517 | debug("%sInstruction %s at line %u\n", INDENT, f_instruction_name_(item->fi_code), item->lineno); |
de12cd18 MM |
518 | switch (item->fi_code) { |
519 | FID_WR_PUT(7) | |
520 | default: bug("Unknown instruction %x in f_dump_line", item->fi_code); | |
521 | } | |
522 | } | |
523 | debug("%sFilter line %p dump done\n", INDENT, dest); | |
524 | } | |
525 | ||
23e3b1e6 | 526 | /* Linearize */ |
dd4d4095 | 527 | static uint |
63f49457 | 528 | linearize(struct f_line *dest, const struct f_inst *what, uint pos) |
dd4d4095 | 529 | { |
63f49457 MM |
530 | for ( ; what; what = what->next) { |
531 | switch (what->fi_code) { | |
dd4d4095 MM |
532 | FID_WR_PUT(8) |
533 | } | |
534 | pos++; | |
535 | } | |
536 | return pos; | |
537 | } | |
538 | ||
539 | struct f_line * | |
23e3b1e6 | 540 | f_linearize_concat(const struct f_inst * const inst[], uint count) |
dd4d4095 MM |
541 | { |
542 | uint len = 0; | |
543 | for (uint i=0; i<count; i++) | |
544 | for (const struct f_inst *what = inst[i]; what; what = what->next) | |
545 | len += what->size; | |
546 | ||
547 | struct f_line *out = cfg_allocz(sizeof(struct f_line) + sizeof(struct f_line_item)*len); | |
548 | ||
549 | for (uint i=0; i<count; i++) | |
23e3b1e6 | 550 | out->len = linearize(out, inst[i], out->len); |
dd4d4095 | 551 | |
87512e97 | 552 | #ifdef LOCAL_DEBUG |
dd4d4095 MM |
553 | f_dump_line(out, 0); |
554 | #endif | |
555 | return out; | |
556 | } | |
557 | ||
132529ce MM |
558 | /* Filter line comparison */ |
559 | int | |
560 | f_same(const struct f_line *fl1, const struct f_line *fl2) | |
561 | { | |
562 | if ((!fl1) && (!fl2)) | |
563 | return 1; | |
564 | if ((!fl1) || (!fl2)) | |
565 | return 0; | |
566 | if (fl1->len != fl2->len) | |
567 | return 0; | |
568 | for (uint i=0; i<fl1->len; i++) { | |
ea4f55e3 MM |
569 | #define f1_ (&(fl1->items[i])) |
570 | #define f2_ (&(fl2->items[i])) | |
571 | if (f1_->fi_code != f2_->fi_code) | |
132529ce | 572 | return 0; |
ea4f55e3 | 573 | if (f1_->flags != f2_->flags) |
132529ce MM |
574 | return 0; |
575 | ||
ea4f55e3 | 576 | switch(f1_->fi_code) { |
132529ce MM |
577 | FID_WR_PUT(9) |
578 | } | |
579 | } | |
ea4f55e3 MM |
580 | #undef f1_ |
581 | #undef f2_ | |
132529ce MM |
582 | return 1; |
583 | } | |
584 | ||
c376555c MM |
585 | #if defined(__GNUC__) && __GNUC__ >= 6 |
586 | #pragma GCC diagnostic pop | |
587 | #endif | |
132529ce | 588 | |
64bb1346 | 589 | FID_WR_DIRECT(H) |
de12cd18 MM |
590 | /* Filter instruction codes */ |
591 | enum f_instruction_code { | |
84c58aab | 592 | FID_WR_PUT(4)m4_dnl |
ea4f55e3 | 593 | } PACKED; |
4f082dfa | 594 | |
ea4f55e3 | 595 | /* Filter instruction structure for config */ |
4f082dfa | 596 | struct f_inst { |
96d757c1 | 597 | struct f_inst *next; /* Next instruction */ |
4f082dfa | 598 | enum f_instruction_code fi_code; /* Instruction code */ |
6fbcd891 | 599 | enum f_type type; /* Type of returned value, if known */ |
4f082dfa MM |
600 | int size; /* How many instructions are underneath */ |
601 | int lineno; /* Line number */ | |
ea4f55e3 | 602 | union { |
84c58aab | 603 | FID_WR_PUT(1)m4_dnl |
ea4f55e3 MM |
604 | }; |
605 | }; | |
606 | ||
607 | /* Filter line item */ | |
608 | struct f_line_item { | |
609 | enum f_instruction_code fi_code; /* What to do */ | |
610 | enum f_instruction_flags flags; /* Flags, instruction-specific */ | |
611 | uint lineno; /* Where */ | |
4f082dfa | 612 | union { |
84c58aab | 613 | FID_WR_PUT(2)m4_dnl |
4f082dfa MM |
614 | }; |
615 | }; | |
616 | ||
617 | /* Instruction constructors */ | |
04160812 MM |
618 | FID_WR_PUT(3) |
619 | m4_divert(-1) | |
550a6488 MM |
620 | |
621 | # 4) Shipout | |
622 | # | |
623 | # Everything is prepared in FID_WR_PUT_LIST now. Let's go! | |
624 | ||
04160812 MM |
625 | m4_changequote(`,') |
626 | ||
550a6488 | 627 | # Flusher auxiliary macro |
b40c0f02 | 628 | m4_define(FID_FLUSH, `m4_ifelse($1,$2,,[[m4_undivert($1)FID_FLUSH(m4_eval($1+1),$2)]])') |
550a6488 MM |
629 | |
630 | # Defining the macro used in FID_WR_PUT_LIST | |
64bb1346 MM |
631 | m4_define(FID_WR_DPUT, `m4_undivert($1)') |
632 | ||
550a6488 | 633 | # After the code is read and parsed, we: |
b40c0f02 | 634 | m4_m4wrap(`INST_FLUSH()m4_divert(0)FID_WR_PUT_LIST()m4_divert(-1)FID_FLUSH(1,200)') |
4f082dfa MM |
635 | |
636 | m4_changequote([[,]]) | |
550a6488 MM |
637 | # And now M4 is going to parse f-inst.c, fill the diversions |
638 | # and after the file is done, the content of m4_m4wrap (see before) | |
639 | # is executed. |