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