Filter: Store variables and function arguments on stack

This commit is contained in:
Jan Maria Matejka 2019-05-21 16:33:37 +00:00
parent 20c6ea70cc
commit 96d757c13f
11 changed files with 125 additions and 195 deletions

View file

@ -116,7 +116,8 @@ struct symbol {
const struct filter *filter; /* For SYM_FILTER */ const struct filter *filter; /* For SYM_FILTER */
struct rtable_config *table; /* For SYM_TABLE */ struct rtable_config *table; /* For SYM_TABLE */
struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */ struct f_dynamic_attr *attribute; /* For SYM_ATTRIBUTE */
struct f_val *val; /* For SYM_CONSTANT or SYM_VARIABLE */ struct f_val *val; /* For SYM_CONSTANT */
uint offset; /* For SYM_VARIABLE */
}; };
char name[0]; char name[0];

View file

@ -70,7 +70,7 @@ CF_DECLS
struct f_dynamic_attr fda; struct f_dynamic_attr fda;
struct f_static_attr fsa; struct f_static_attr fsa;
struct f_lval flv; struct f_lval flv;
const struct f_line *fl; struct f_line *fl;
const struct filter *f; const struct filter *f;
struct f_tree *e; struct f_tree *e;
struct f_trie *trie; struct f_trie *trie;

View file

@ -15,6 +15,8 @@ CF_HDR
CF_DEFINES CF_DEFINES
static uint decls_count;
static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; } static inline u32 pair(u32 a, u32 b) { return (a << 16) | b; }
static inline u32 pair_a(u32 p) { return p >> 16; } static inline u32 pair_a(u32 p) { return p >> 16; }
static inline u32 pair_b(u32 p) { return p & 0xFFFF; } static inline u32 pair_b(u32 p) { return p & 0xFFFF; }
@ -398,8 +400,8 @@ assert_assign(struct f_lval *lval, struct f_inst *expr, const char *start, const
struct f_inst *setter, *getter, *checker; struct f_inst *setter, *getter, *checker;
switch (lval->type) { switch (lval->type) {
case F_LVAL_VARIABLE: case F_LVAL_VARIABLE:
setter = f_new_inst(FI_SET, expr, lval->sym); setter = f_new_inst(FI_VAR_SET, expr, lval->sym);
getter = f_new_inst(FI_VARIABLE, lval->sym); getter = f_new_inst(FI_VAR_GET, lval->sym);
break; break;
case F_LVAL_PREFERENCE: case F_LVAL_PREFERENCE:
setter = f_new_inst(FI_PREF_SET, expr); setter = f_new_inst(FI_PREF_SET, expr);
@ -446,14 +448,14 @@ CF_KEYWORDS(FUNCTION, PRINT, PRINTN, UNSET, RETURN,
%nonassoc THEN %nonassoc THEN
%nonassoc ELSE %nonassoc ELSE
%type <xp> cmds_int function_body declsn function_params %type <xp> cmds_int
%type <x> term block cmd cmds constant constructor print_one print_list var_list var_listn function_call symbol_value bgp_path_expr bgp_path bgp_path_tail one_decl decls %type <x> term block cmd cmds constant constructor print_one print_list var_list function_call symbol_value bgp_path_expr bgp_path bgp_path_tail
%type <fda> dynamic_attr %type <fda> dynamic_attr
%type <fsa> static_attr %type <fsa> static_attr
%type <f> filter where_filter %type <f> filter where_filter
%type <fl> filter_body %type <fl> filter_body function_body
%type <flv> lvalue %type <flv> lvalue
%type <i> type %type <i> type function_params
%type <ecs> ec_kind %type <ecs> ec_kind
%type <fret> break_command %type <fret> break_command
%type <i32> cnum %type <i32> cnum
@ -553,43 +555,24 @@ type:
} }
; ;
one_decl: /* Declarations with ';' at the end */
type CF_SYM_VOID { decls:
struct f_val * val = cfg_alloc(sizeof(struct f_val)); /* EMPTY */
val->type = T_VOID; | declsn ';'
$2 = cf_define_symbol($2, SYM_VARIABLE | $1, val, val);
DBG( "New variable %s type %x\n", $2->name, $1 );
$$ = f_new_inst(FI_SET, NULL, $2);
}
;
/* Decls with ';' at the end. Beware; these are reversed. */
decls: /* EMPTY */ { $$ = NULL; }
| one_decl ';' decls {
$$ = $1;
$$->next = $3;
}
; ;
/* Declarations that have no ';' at the end. */ /* Declarations that have no ';' at the end. */
declsn: one_decl { $$[0] = $$[1] = $1; } declsn:
| one_decl ';' declsn { type CF_SYM_VOID {
$3[1]->next = $1; cf_define_symbol($2, SYM_VARIABLE | $1, offset, decls_count++);
$$[1] = $3[1] = $1; }
$$[0] = $3[0]; | declsn ';' type CF_SYM_VOID {
if (decls_count >= 0xff) cf_error("Too many declarations, at most 255 allowed");
cf_define_symbol($4, SYM_VARIABLE | $3, offset, decls_count++);
} }
; ;
filter_body: filter_body: { decls_count = 0; } function_body { $$ = $2; } ;
function_body {
if ($1[0]) {
const struct f_inst *inst[2] = { $1[0], $1[1] };
$$ = f_postfixify_concat(inst, 2);
}
else
$$ = f_postfixify($1[1]);
}
;
filter: filter:
CF_SYM_KNOWN { CF_SYM_KNOWN {
@ -611,14 +594,14 @@ where_filter:
; ;
function_params: function_params:
'(' declsn ')' { $$[0] = $2[0]; $$[1] = $2[1]; } '(' declsn ')' { $$ = decls_count; }
| '(' ')' { $$[0] = $$[1] = NULL; } | '(' ')' { $$ = 0; }
; ;
function_body: function_body:
decls '{' cmds '}' { decls '{' cmds '}' {
$$[0] = $1 ? f_clear_local_vars($1) : NULL; $$ = f_postfixify($3);
$$[1] = $3; $$->vars = decls_count;
} }
; ;
@ -627,33 +610,11 @@ function_def:
FUNCTION CF_SYM_VOID { DBG( "Beginning of function %s\n", $2->name ); FUNCTION CF_SYM_VOID { DBG( "Beginning of function %s\n", $2->name );
$2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL); $2 = cf_define_symbol($2, SYM_FUNCTION, function, NULL);
cf_push_scope($2); cf_push_scope($2);
decls_count = 0;
} function_params function_body { } function_params function_body {
const struct f_inst *catlist[4]; $5->vars -= $4;
uint count = 0; $5->args = $4;
$2->function = $5;
/* Argument setters */
if ($4[0])
catlist[count++] = $4[0];
/* Local var clearers */
if ($5[0])
catlist[count++] = $5[0];
/* Return void if no return is needed */
catlist[count++] = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
/* Function body itself */
if ($5[1])
catlist[count++] = $5[1];
struct f_line *fl = f_postfixify_concat(catlist, count);
fl->args = 0;
for (const struct f_inst *arg = $4[0]; arg; arg = arg->next)
fl->args++;
$2->function = fl;
cf_pop_scope(); cf_pop_scope();
} }
; ;
@ -862,9 +823,33 @@ constructor:
; ;
/* This generates the function_call variable list backwards. */
var_list: /* EMPTY */ { $$ = NULL; }
| term { $$ = $1; }
| var_list ',' term { $$ = $3; $$->next = $1; }
function_call: function_call:
CF_SYM_KNOWN '(' var_list ')' { CF_SYM_KNOWN '(' var_list ')' {
$$ = f_new_inst(FI_CALL, $1, $3); if ($1->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
struct f_inst *fc = f_new_inst(FI_CALL, $1);
uint args = 0;
while ($3) {
args++;
struct f_inst *tmp = $3->next;
$3->next = fc;
fc = $3;
$3 = tmp;
}
if (args != $1->function->args)
cf_error("Function call '%s' got %u arguments, need %u arguments.",
$1->name, args, $1->function->args);
$$ = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
$$->next = fc;
} }
; ;
@ -872,8 +857,10 @@ symbol_value: CF_SYM_KNOWN
{ {
switch ($1->class) { switch ($1->class) {
case SYM_CONSTANT_RANGE: case SYM_CONSTANT_RANGE:
$$ = f_new_inst(FI_CONSTANT_DEFINED, $1);
break;
case SYM_VARIABLE_RANGE: case SYM_VARIABLE_RANGE:
$$ = f_new_inst(FI_VARIABLE, $1); $$ = f_new_inst(FI_VAR_GET, $1);
break; break;
case SYM_ATTRIBUTE: case SYM_ATTRIBUTE:
$$ = f_new_inst(FI_EA_GET, *$1->attribute); $$ = f_new_inst(FI_EA_GET, *$1->attribute);
@ -988,19 +975,6 @@ print_list: /* EMPTY */ { $$ = NULL; }
} }
; ;
var_listn: term {
$$ = $1;
}
| term ',' var_listn {
$$ = $1;
$$->next = $3;
}
;
var_list: /* EMPTY */ { $$ = NULL; }
| var_listn { $$ = $1; }
;
cmd: cmd:
IF term THEN block { IF term THEN block {
$$ = f_new_inst(FI_CONDITION, $2, $4, NULL); $$ = f_new_inst(FI_CONDITION, $2, $4, NULL);
@ -1011,7 +985,7 @@ cmd:
| CF_SYM_KNOWN '=' term ';' { | CF_SYM_KNOWN '=' term ';' {
switch ($1->class) { switch ($1->class) {
case SYM_VARIABLE_RANGE: case SYM_VARIABLE_RANGE:
$$ = f_new_inst(FI_SET, $3, $1); $$ = f_new_inst(FI_VAR_SET, $3, $1);
break; break;
case SYM_ATTRIBUTE: case SYM_ATTRIBUTE:
$$ = f_new_inst(FI_EA_SET, $3, *$1->attribute); $$ = f_new_inst(FI_EA_SET, $3, *$1->attribute);

View file

@ -17,8 +17,8 @@
/* Internal types */ /* Internal types */
enum f_type { enum f_type {
/* Do not use type of zero, that way we'll see errors easier. */ /* Nothing. Simply nothing. */
T_VOID = 1, T_VOID = 0,
/* User visible types, which fit in int */ /* User visible types, which fit in int */
T_INT = 0x10, T_INT = 0x10,

View file

@ -209,6 +209,7 @@ do {
estk.item[estk.cnt].pos = 0; estk.item[estk.cnt].pos = 0;
estk.item[estk.cnt].line = $1; estk.item[estk.cnt].line = $1;
estk.item[estk.cnt].ventry = vstk.cnt; estk.item[estk.cnt].ventry = vstk.cnt;
estk.item[estk.cnt].vbase = estk.item[estk.cnt-1].vbase;
estk.item[estk.cnt].emask = 0; estk.item[estk.cnt].emask = 0;
estk.cnt++; estk.cnt++;
} while (0)m4_dnl } while (0)m4_dnl
@ -377,7 +378,7 @@ FID_WR_PUT(4)
/* Filter instruction structure for config */ /* Filter instruction structure for config */
struct f_inst { struct f_inst {
const struct f_inst *next; /* Next instruction */ struct f_inst *next; /* Next instruction */
enum f_instruction_code fi_code; /* Instruction code */ enum f_instruction_code fi_code; /* Instruction code */
int size; /* How many instructions are underneath */ int size; /* How many instructions are underneath */
int lineno; /* Line number */ int lineno; /* Line number */

View file

@ -261,23 +261,28 @@
} }
/* Set to indirect value prepared in v1 */ /* Set to indirect value prepared in v1 */
INST(FI_SET, 1, 0) { INST(FI_VAR_SET, 1, 0) {
ARG_ANY(2); ARG_ANY(2);
SYMBOL(1); SYMBOL(1);
if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID)) if ((sym->class != (SYM_VARIABLE | v1.type)) && (v1.type != T_VOID))
{ {
/* IP->Quad implicit conversion */ /* IP->Quad implicit conversion */
if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1)) if ((sym->class == (SYM_VARIABLE | T_QUAD)) && val_is_ip4(&v1))
{ v1 = (struct f_val) {
*(sym->val) = (struct f_val) {
.type = T_QUAD, .type = T_QUAD,
.val.i = ipa_to_u32(v1.val.ip), .val.i = ipa_to_u32(v1.val.ip),
}; };
break; else
} runtime( "Assigning to variable of incompatible type" );
runtime( "Assigning to variable of incompatible type" );
} }
*(sym->val) = v1;
vstk.val[curline.vbase + sym->offset] = v1;
}
INST(FI_VAR_GET, 0, 1) {
SYMBOL(1);
res = vstk.val[curline.vbase + sym->offset];
RESULT_OK;
} }
/* some constants have value in a[1], some in *a[0].p, strange. */ /* some constants have value in a[1], some in *a[0].p, strange. */
@ -301,7 +306,7 @@
res = whati->val; res = whati->val;
RESULT_OK; RESULT_OK;
} }
INST(FI_VARIABLE, 0, 1) { INST(FI_CONSTANT_DEFINED, 0, 1) {
FID_STRUCT_IN FID_STRUCT_IN
const struct symbol *sym; const struct symbol *sym;
FID_LINE_IN FID_LINE_IN
@ -314,18 +319,9 @@
FID_POSTFIXIFY_BODY FID_POSTFIXIFY_BODY
item->valp = (item->sym = what->sym)->val; item->valp = (item->sym = what->sym)->val;
FID_SAME_BODY FID_SAME_BODY
if (strcmp(f1->sym->name, f2->sym->name) || (f1->sym->class != f2->sym->class)) return 0; if (strcmp(f1->sym->name, f2->sym->name) || !val_same(f1->sym->val, f2->sym->val)) return 0;
FID_DUMP_BODY FID_DUMP_BODY
switch (item->sym->class) { debug("%sconstant %s with value %s\n", INDENT, item->sym->name, val_dump(item->valp));
case SYM_CONSTANT_RANGE:
debug("%sconstant %s with value %s\n", INDENT, item->sym->name, val_dump(item->valp));
break;
case SYM_VARIABLE_RANGE:
debug("%svariable %s with current value %s\n", INDENT, item->sym->name, val_dump(item->valp));
break;
default:
bug("Symbol %s of type %d doesn't reference a value", item->sym->name, item->sym->class);
}
FID_ALL FID_ALL
res = *whati->valp; res = *whati->valp;
@ -768,56 +764,31 @@
else else
runtime("Can't return non-bool from non-function"); runtime("Can't return non-bool from non-function");
/* Set the value stack position */ /* Set the value stack position, overwriting the former implicit void */
vstk.cnt = estk.item[estk.cnt].ventry; vstk.cnt = estk.item[estk.cnt].ventry - 1;
/* Copy the return value */ /* Copy the return value */
RESULT_VAL(vstk.val[retpos]); RESULT_VAL(vstk.val[retpos]);
} }
INST(FI_CALL, 0, 1) { INST(FI_CALL, 0, 1) {
FID_LINE_IN SYMBOL;
const struct f_line *args;
const struct f_line *body;
struct symbol *sym;
FID_STRUCT_IN
struct symbol *sym;
const struct f_inst *args;
FID_NEW_ARGS
, struct symbol * sym
, const struct f_inst *args
FID_NEW_BODY
if (sym->class != SYM_FUNCTION)
cf_error("You can't call something which is not a function. Really.");
uint count = 0; /* Push the body on stack */
for (const struct f_inst *inst = args; inst; inst = inst->next) LINEX(sym->function);
count++;
if (count != sym->function->args)
cf_error("Function %s takes %u arguments, got %u.", sym->name, sym->function->args, count);
what->sym = sym;
what->args = args;
FID_DUMP_BODY
debug("%scalling %s with following args\n", INDENT, item->sym->name);
f_dump_line(item->args, indent + 1);
FID_POSTFIXIFY_BODY
item->args = f_postfixify(what->args);
item->body = (item->sym = what->sym)->function;
FID_SAME_BODY
/* To be done better */
if (strcmp(f1->sym->name, f2->sym->name)) return 0;
if (!f_same(f1->args, f2->args)) return 0;
if (!f_same(f1->body, f2->body)) return 0;
FID_ALL
/* First push the body on stack */
LINEX(whati->body);
curline.emask |= FE_RETURN; curline.emask |= FE_RETURN;
/* Before this instruction was called, there was the T_VOID
* automatic return value pushed on value stack and also
* sym->function->args function arguments. Setting the
* vbase to point to first argument. */
ASSERT(curline.ventry >= sym->function->args);
curline.ventry -= sym->function->args;
curline.vbase = curline.ventry;
/* Then push the arguments */ /* Storage for local variables */
LINEX(whati->args); memset(&(vstk.val[vstk.cnt]), 0, sizeof(struct f_val) * sym->function->vars);
vstk.cnt += sym->function->vars;
} }
INST(FI_DROP_RESULT, 1, 0) { INST(FI_DROP_RESULT, 1, 0) {

View file

@ -63,13 +63,12 @@ enum f_instruction_flags {
/* Convert the instruction back to the enum name */ /* Convert the instruction back to the enum name */
const char *f_instruction_name(enum f_instruction_code fi); const char *f_instruction_name(enum f_instruction_code fi);
struct f_inst *f_clear_local_vars(struct f_inst *decls);
/* Filter structures for execution */ /* Filter structures for execution */
/* Line of instructions to be unconditionally executed one after another */ /* Line of instructions to be unconditionally executed one after another */
struct f_line { struct f_line {
uint len; /* Line length */ uint len; /* Line length */
u8 args; /* Function: Args required */ u8 args; /* Function: Args required */
u8 vars;
struct f_line_item items[0]; /* The items themselves */ struct f_line_item items[0]; /* The items themselves */
}; };

View file

@ -30,11 +30,6 @@ filter_name(const struct filter *filter)
return filter->sym->name; return filter->sym->name;
} }
void f_inst_next(struct f_inst *first, const struct f_inst *append)
{
first->next = append;
}
struct filter *f_new_where(const struct f_inst *where) struct filter *f_new_where(const struct f_inst *where)
{ {
struct f_inst acc = { struct f_inst acc = {
@ -67,23 +62,6 @@ struct filter *f_new_where(const struct f_inst *where)
return f; return f;
} }
struct f_inst *f_clear_local_vars(struct f_inst *decls)
{
/* Prepend instructions to clear local variables */
struct f_inst *head = NULL;
for (const struct f_inst *si = decls; si; si = si->next) {
struct f_inst *cur = f_new_inst(FI_CONSTANT, (struct f_val) { .type = T_VOID });
if (head)
f_inst_next(cur, head);
else
f_inst_next(cur, si);
head = cur; /* The first FI_CONSTANT put there */
}
return head;
}
#define CA_KEY(n) n->name, n->fda.type #define CA_KEY(n) n->name, n->fda.type
#define CA_NEXT(n) n->next #define CA_NEXT(n) n->next
#define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb)) #define CA_EQ(na,ta,nb,tb) (!strcmp(na,nb) && (ta == tb))

View file

@ -135,6 +135,8 @@ static struct tbf rl_runtime_err = TBF_DEFAULT_LOG_LIMITS;
static enum filter_return static enum filter_return
interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val) interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
{ {
/* No arguments allowed */
ASSERT(line->args == 0);
#define F_VAL_STACK_MAX 4096 #define F_VAL_STACK_MAX 4096
/* Value stack for execution */ /* Value stack for execution */
@ -145,8 +147,12 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
/* The stack itself is intentionally kept as-is for performance reasons. /* The stack itself is intentionally kept as-is for performance reasons.
* Do NOT rewrite this to initialization by struct literal. It's slow. * Do NOT rewrite this to initialization by struct literal. It's slow.
*/ *
vstk.cnt = 0; * Reserving space for local variables. */
vstk.cnt = line->vars;
memset(vstk.val, 0, sizeof(struct f_val) * line->vars);
#define F_EXEC_STACK_MAX 4096 #define F_EXEC_STACK_MAX 4096
/* Exception bits */ /* Exception bits */
@ -160,6 +166,7 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
const struct f_line *line; /* The line that is being executed */ const struct f_line *line; /* The line that is being executed */
uint pos; /* Instruction index in the line */ uint pos; /* Instruction index in the line */
uint ventry; /* Value stack depth on entry */ uint ventry; /* Value stack depth on entry */
uint vbase; /* Where to index variable positions from */
enum f_exception emask; /* Exception mask */ enum f_exception emask; /* Exception mask */
} item[F_EXEC_STACK_MAX]; } item[F_EXEC_STACK_MAX];
uint cnt; /* Current stack size; 0 for empty */ uint cnt; /* Current stack size; 0 for empty */
@ -181,7 +188,6 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
while (curline.pos < curline.line->len) { while (curline.pos < curline.line->len) {
const struct f_line_item *what = &(curline.line->items[curline.pos++]); const struct f_line_item *what = &(curline.line->items[curline.pos++]);
switch (what->fi_code) { switch (what->fi_code) {
#define res vstk.val[vstk.cnt] #define res vstk.val[vstk.cnt]
#define v1 vstk.val[vstk.cnt] #define v1 vstk.val[vstk.cnt]
@ -207,26 +213,28 @@ interpret(struct filter_state *fs, const struct f_line *line, struct f_val *val)
#undef ACCESS_EATTRS #undef ACCESS_EATTRS
} }
} }
/* End of current line. Drop local variables before exiting. */
vstk.cnt -= curline.line->vars;
vstk.cnt -= curline.line->args;
estk.cnt--; estk.cnt--;
} }
switch (vstk.cnt) { if (vstk.cnt == 0) {
case 0: if (val) {
if (val) { log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack");
log_rl(&rl_runtime_err, L_ERR "filters: No value left on stack");
return F_ERROR;
}
return F_NOP;
case 1:
if (val) {
*val = vstk.val[0];
return F_NOP;
}
/* fallthrough */
default:
log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", vstk.cnt);
return F_ERROR; return F_ERROR;
}
return F_NOP;
} }
if (val && (vstk.cnt == 1)) {
*val = vstk.val[0];
return F_NOP;
}
log_rl(&rl_runtime_err, L_ERR "Too many items left on stack: %u", vstk.cnt);
return F_ERROR;
} }

View file

@ -56,8 +56,7 @@ run_function(const void *arg)
} }
linpool *tmp = lp_new_default(&root_pool); linpool *tmp = lp_new_default(&root_pool);
struct f_val res; enum filter_return fret = f_eval(t->fn, tmp, NULL);
enum filter_return fret = f_eval(t->fn, tmp, &res);
rfree(tmp); rfree(tmp);
return (fret < F_REJECT); return (fret < F_REJECT);

View file

@ -7,8 +7,7 @@
router id 62.168.0.1; router id 62.168.0.1;
/* We have to setup any protocol */ /* We have to setup any protocol */
protocol static { ipv4; } protocol device { }