/* This file is part of Mailfromd.
Copyright (C) 2005-2020 Sergey Poznyakoff
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see . */
static inline void
mark_locus(NODE *node, struct mu_locus_range const **old_locus)
{
if (!*old_locus
|| !mu_locus_point_same_line (&(*old_locus)->beg, &node->locus.beg)) {
struct literal *lit = literal_lookup(node->locus.beg.mu_file);
*old_locus = &node->locus;
code_op(opcode_locus);
code_immediate(lit->off, ulong);
code_immediate(node->locus.beg.mu_line, ulong);
}
}
static void code_trycatch_exit(unsigned id);
static unsigned trycatch_last_id(void);
void
code_memref(NODE *node)
{
switch (node->v.var_ref.variable->storage_class) {
case storage_extern:
code_op(opcode_push);
code_immediate(node->v.var_ref.variable->off, long);
break;
case storage_auto:
code_op(opcode_memstk);
code_immediate(node->v.var_ref.nframes, size);
code_immediate((-node->v.var_ref.variable->off), long);
break;
case storage_param:
code_op(opcode_memstk);
code_immediate(node->v.var_ref.nframes, size);
code_immediate((node->v.var_ref.variable->off + 2), long);
break;
}
}
/* type noop */
/* Empty node, nothing to print, mark, optimize or code */
/* type string */
void
print_type_string(NODE *node, int level)
{
print_level(level);
printf("STRING: \"");
print_quoted_string(node->v.literal->text);
printf("\"\n");
}
void
mark_type_string(NODE *node)
{
node->v.literal->flags |= SYM_REFERENCED;
}
void
code_type_string(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_op(opcode_push);
code_immediate(node->v.literal->off, size);
}
/* type symbol */
void
print_type_symbol(NODE *node, int level)
{
print_level(level);
printf("SYMBOL: %s\n", node->v.literal->text);
}
void
code_type_symbol(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_op(opcode_symbol);
code_immediate(node->v.literal->off, size);
}
void
mark_type_symbol(NODE *node)
{
node->v.literal->flags |= SYM_REFERENCED;
}
/* type number */
void
print_type_number(NODE *node, int level)
{
print_level(level);
printf("NUMBER: %ld\n", node->v.number);
}
void
code_type_number(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_op(opcode_push);
code_immediate(node->v.number, long);
}
/* type if */
void
print_type_if(NODE *node, int level)
{
print_level(level);
printf("COND: \n");
print_node(node->v.cond.cond, level);
print_level(level);
printf("IFTRUE\n");
print_node_list(node->v.cond.if_true, level+1);
print_level(level);
printf("IFFALSE\n");
print_node_list(node->v.cond.if_false, level+1);
}
void
mark_type_if(NODE *node)
{
mark(node->v.cond.cond);
mark(node->v.cond.if_true);
mark(node->v.cond.if_false);
}
void
optimize_type_if(NODE *node)
{
NODE *p = node->v.cond.cond;
optimize(p);
optimize(node->v.cond.if_true);
optimize(node->v.cond.if_false);
if (p->type == node_type_number) {
NODE *head;
NODE *tail = node->next;
if (p->v.number) {
head = node->v.cond.if_true;
free_subtree(node->v.cond.if_false);
} else {
head = node->v.cond.if_false;
free_subtree(node->v.cond.if_true);
}
if (head) {
*node = *head;
free_node(head);
for (; node->next; node = node->next)
;
node->next = tail;
} else
node->type = node_type_noop;
} else if (p->type == node_type_string) {
NODE *head;
NODE *tail = p->next;
if (p->v.literal->text[0]) {
head = node->v.cond.if_true;
free_subtree(node->v.cond.if_false);
} else {
head = node->v.cond.if_false;
free_subtree(node->v.cond.if_true);
}
if (head) {
*node = *head;
free_node(head);
for (; node->next; node = node->next)
;
node->next = tail;
} else
node->type = node_type_noop;
}
}
void
code_type_if(NODE *node, struct mu_locus_range const **old_locus)
{
prog_counter_t pos1, pos2, endpos;
code_node(node->v.cond.cond);
mark_locus(node, old_locus);
code_op(opcode_bz);
pos1 = code_immediate(NULL, ptr);
traverse_tree(node->v.cond.if_true);
if (node->v.cond.if_false) {
code_op(opcode_jmp);
pos2 = code_immediate(NULL, ptr);
traverse_tree(node->v.cond.if_false);
endpos = code_get_counter ();
code_put(pos1, (pos2 - pos1), long);
code_put(pos2, (endpos - pos2 - 1), long);
} else
code_put(pos1, (code_get_counter () - pos1 - 1), long);
}
/* type bin */
void
print_type_bin(NODE *node, int level)
{
print_level(level);
print_bin_op(node->v.bin.opcode);
if (node->v.bin.opcode == bin_match
|| node->v.bin.opcode == bin_fnmatch) {
if (node->v.bin.qualifier & QUALIFIER_MX)
printf(",MX");
}
printf("\n");
print_node(node->v.bin.arg[0], level+1);
print_node(node->v.bin.arg[1], level+1);
}
void
mark_type_bin(NODE *node)
{
mark(node->v.bin.arg[0]);
mark(node->v.bin.arg[1]);
}
static void
optimize_arith(NODE *node)
{
NODE *arg0 = node->v.bin.arg[0];
NODE *arg1 = node->v.bin.arg[1];
if (arg0->type == node_type_number
&& arg1->type == node_type_number) {
switch (node->v.bin.opcode) {
case bin_add:
node->v.number = arg0->v.number + arg1->v.number;
break;
case bin_sub:
node->v.number = arg0->v.number - arg1->v.number;
break;
case bin_mul:
node->v.number = arg0->v.number * arg1->v.number;
break;
case bin_div:
if (arg1->v.number == 0) {
parse_error_locus(&node->locus,
_("division by zero"));
break;
}
node->v.number = arg0->v.number / arg1->v.number;
break;
case bin_mod:
if (arg1->v.number == 0) {
parse_error_locus(&node->locus,
_("division by zero"));
break;
}
node->v.number = arg0->v.number % arg1->v.number;
break;
case bin_logand:
node->v.number = arg0->v.number & arg1->v.number;
break;
case bin_logor:
node->v.number = arg0->v.number | arg1->v.number;
break;
case bin_logxor:
node->v.number = arg0->v.number ^ arg1->v.number;
break;
case bin_shl:
node->v.number = arg0->v.number << (unsigned long) arg1->v.number;
break;
case bin_shr:
node->v.number = arg0->v.number >> (unsigned long) arg1->v.number;
break;
default:
return;
}
node->type = node_type_number;
free_node(arg0);
free_node(arg1);
} else if (node_type(arg0) != dtype_number) {
parse_error_locus(&arg0->locus,
_("left-hand side argument to the "
"arithmetical operation is "
"of wrong data type"));
} else if (node_type(arg1) != dtype_number) {
parse_error_locus(&arg1->locus,
_("right-hand side argument to the "
"arithmetical operation is "
"of wrong data type"));
} else if (arg0->type == node_type_number) {
switch (node->v.bin.opcode) {
case bin_add:
if (arg0->v.number == 0) {
copy_node(node, arg1);
free_node(arg0);
free(arg1);
}
break;
case bin_sub:
if (arg0->v.number == 0) {
NODE *n = alloc_node(node_type_un,
&node->locus);
n->v.un.opcode = unary_minus;
n->v.un.arg = arg1;
copy_node(node, n);
free_node(arg0);
free(n);
}
break;
case bin_mul:
if (arg0->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
} else if (arg0->v.number == 1) {
copy_node(node, arg1);
free_node(arg0);
}
break;
case bin_div:
case bin_mod:
if (arg0->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
}
break;
case bin_logand:
if (arg0->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
} else if (arg0->v.number == ~(unsigned long)0) {
copy_node(node, arg1);
free_node(arg0);
}
break;
case bin_logor:
if (arg0->v.number == 0) {
copy_node(node, arg1);
free_node(arg0);
} else if (arg0->v.number == ~(unsigned long)0) {
node->type = node_type_number;
node->v.number = ~(unsigned long)0;
free_node(arg0);
free_node(arg1);
}
break;
case bin_logxor:
if (arg0->v.number == 0) {
copy_node(node, arg1);
free_node(arg0);
}
break;
case bin_shl:
case bin_shr:
if (arg0->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
}
break;
default:
return;
}
} else if (arg1->type == node_type_number) {
switch (node->v.bin.opcode) {
case bin_add:
case bin_sub:
if (arg1->v.number == 0) {
copy_node(node, arg0);
free_node(arg1);
free(arg0);
}
break;
case bin_mul:
if (arg1->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
} else if (arg1->v.number == 1) {
copy_node(node, arg0);
free_node(arg1);
}
break;
case bin_div:
if (arg1->v.number == 0) {
parse_error_locus(&node->locus,
_("division by zero"));
} else if (arg1->v.number == 1) {
copy_node(node, arg0);
free_node(arg1);
}
break;
case bin_mod:
if (arg1->v.number == 0) {
parse_error_locus(&node->locus,
_("division by zero"));
} else if (arg1->v.number == 1) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
}
break;
case bin_logand:
if (arg1->v.number == 0) {
node->type = node_type_number;
node->v.number = 0;
free_node(arg0);
free_node(arg1);
} else if (arg1->v.number == ~(unsigned long)0) {
copy_node(node, arg0);
free_node(arg1);
}
break;
case bin_logor:
if (arg1->v.number == 0) {
copy_node(node, arg0);
free_node(arg1);
} else if (arg1->v.number == ~(unsigned long)0) {
node->type = node_type_number;
node->v.number = ~(unsigned long)0;
free_node(arg0);
free_node(arg1);
}
break;
case bin_logxor:
if (arg1->v.number == 0) {
copy_node(node, arg0);
free_node(arg0);
}
break;
case bin_shl:
case bin_shr:
if (arg1->v.number == 0) {
copy_node(node, arg0);
free_node(arg1);
free(arg0);
}
break;
default:
return;
}
}
}
static int
node_boolean_value(NODE *node, int *pval)
{
switch (node->type) {
case node_type_number:
*pval = !!node->v.number;
break;
case node_type_string:
*pval = !!node->v.literal->text[0];
break;
default:
return 1;
}
return 0;
}
static int
_optimize_shortcut(NODE *node, NODE *arg)
{
int val;
if (node_boolean_value(arg, &val))
return 1;
switch (node->v.bin.opcode) {
case bin_and:
if (val)
return 1;
node->v.number = 0;
break;
case bin_or:
if (!val)
return 1;
node->v.number = 1;
break;
default:
return 1;
}
node->type = node_type_number;
free_node(node->v.bin.arg[0]);
free_node(node->v.bin.arg[1]);
return 0;
}
void
optimize_relational(NODE *node)
{
NODE *arg0 = node->v.bin.arg[0];
NODE *arg1 = node->v.bin.arg[1];
if (_optimize_shortcut(node, arg0) == 0
|| _optimize_shortcut(node, arg1) == 0)
return;
if (arg0->type == node_type_number
&& arg1->type == node_type_number) {
switch (node->v.bin.opcode) {
case bin_and:
node->v.number = arg0->v.number && arg1->v.number;
break;
case bin_or:
node->v.number = arg0->v.number || arg1->v.number;
break;
case bin_eq:
node->v.number = arg0->v.number == arg1->v.number;
break;
case bin_ne:
node->v.number = arg0->v.number != arg1->v.number;
break;
case bin_lt:
node->v.number = arg0->v.number < arg1->v.number;
break;
case bin_le:
node->v.number = arg0->v.number <= arg1->v.number;
break;
case bin_gt:
node->v.number = arg0->v.number > arg1->v.number;
break;
case bin_ge:
node->v.number = arg0->v.number >= arg1->v.number;
break;
default:
return;
}
} else if (arg0->type == node_type_string
&& arg1->type == node_type_string) {
switch (node->v.bin.opcode) {
case bin_and:
node->v.number = arg0->v.literal->text[0] != 0
&& arg1->v.literal->text[0] != 0;
break;
case bin_or:
node->v.number = arg0->v.literal->text[0] != 0
|| arg1->v.literal->text[0] != 0;
break;
case bin_eq:
/* NOTE: This case and the one below make use of the
fact that no two entries in the symbol table can
contain lexicographically equal literals */
node->v.number = arg0->v.literal == arg1->v.literal;
break;
case bin_ne:
node->v.number = arg0->v.literal != arg1->v.literal;
break;
case bin_lt:
node->v.number = strcmp(arg0->v.literal->text,
arg1->v.literal->text) < 0;
break;
case bin_le:
node->v.number = strcmp(arg0->v.literal->text,
arg1->v.literal->text) <= 0;
break;
case bin_gt:
node->v.number = strcmp(arg0->v.literal->text,
arg1->v.literal->text) > 0;
break;
case bin_ge:
node->v.number = strcmp(arg0->v.literal->text,
arg1->v.literal->text) >= 0;
break;
default:
return;
}
} else
return;
node->type = node_type_number;
free_node(arg0);
free_node(arg1);
}
static int
node_regmatch(NODE *node, struct literal *lit)
{
regex_t re;
struct sym_regex *sym = node->v.regex;
int rc;
rc = regcomp(&re, sym->lit->text, sym->regflags);
if (rc) {
char errbuf[512];
regerror(rc, &re, errbuf, sizeof(errbuf));
parse_error_locus(&node->locus,
_("cannot compile regex: %s"),
errbuf);
return 1;
}
rc = regexec(&re, lit->text, 0, NULL, 0);
regfree(&re);
return rc;
}
void
optimize_type_bin(NODE *node)
{
NODE *arg0, *arg1;
arg0 = node->v.bin.arg[0];
arg1 = node->v.bin.arg[1];
optimize(arg0);
optimize(arg1);
switch (node->v.bin.opcode) {
case bin_and:
case bin_or:
case bin_eq:
case bin_ne:
case bin_lt:
case bin_le:
case bin_gt:
case bin_ge:
optimize_relational(node);
break;
case bin_add:
case bin_sub:
case bin_mul:
case bin_div:
case bin_mod:
case bin_logand:
case bin_logor:
case bin_logxor:
case bin_shl:
case bin_shr:
optimize_arith(node);
break;
case bin_match:
if (node_type(arg0) != dtype_string) {
parse_error_locus(&arg0->locus,
_("left-hand side argument "
"to match is "
"of wrong data type"));
} else if (arg1->type == node_type_regex) {
if (arg0->type == node_type_string) {
node->v.number =
node_regmatch(arg1,
arg0->v.literal) == 0;
node->type = node_type_number;
free_node(arg0);
free_node(arg1);
}
} else if (arg1->type != node_type_regcomp) {
parse_error_locus(&arg1->locus,
_("right-hand side argument "
"to match is "
"of wrong data type "
"(should not happen)"));
}
break;
case bin_fnmatch:
if (arg0->type == node_type_string
&& arg1->type == node_type_string) {
node->v.number = fnmatch(arg1->v.literal->text,
arg0->v.literal->text,
0) == 0;
node->type = node_type_number;
free_node(arg0);
free_node(arg1);
} else if (node_type(arg0) != dtype_string) {
parse_error_locus(&arg0->locus,
_("left-hand side argument "
"to fnmatch is of wrong data type"));
} else if (node_type(arg1) != dtype_string) {
parse_error_locus(&arg1->locus,
_("right-hand side argument "
"to fnmatch is of wrong data type"));
}
break;
}
}
#define __code_cat3__(a,b,c) a ## b ## c
#define CODE_BINARY(op, node) do { \
switch (node_type(node->v.bin.arg[0])) { \
case dtype_number: \
code_op(__code_cat3__(opcode_,op,n)); \
break; \
case dtype_string: \
code_op(__code_cat3__(opcode_,op,s)); \
break; \
default: \
parse_error_locus(&node->locus, \
_("invalid argument type in binary operation")); \
break; \
} \
} while (0)
void
code_type_bin(NODE *node, struct mu_locus_range const **old_locus)
{
prog_counter_t pos1, pos2;
code_node(node->v.bin.arg[0]);
switch (node->v.bin.opcode) {
case bin_and:
mark_locus(node, old_locus);
/* cond1
if not true goto X
cond2
if true goto Y
X: push 0
goto Z
Y: push 1
Z: */
code_op(opcode_bz);
pos1 = code_immediate(NULL, ptr);
code_node(node->v.bin.arg[1]);
code_op(opcode_bnz);
pos2 = code_immediate(4, long);
code_op(opcode_push);
code_immediate(0, long);
code_op(opcode_jmp);
code_immediate(2, long);
code_op(opcode_push);
code_immediate(1, long);
code_put(pos1, (pos2 - pos1), long);
break;
case bin_or:
mark_locus(node, old_locus);
/* cond1
if true goto X
cond2
if not true goto Y
X: push 1
goto Z
Y: push 0
Z: */
code_op(opcode_bnz);
pos1 = code_immediate(NULL, ptr);
code_node(node->v.bin.arg[1]);
code_op(opcode_bz);
pos2 = code_immediate(4, long);
code_op(opcode_push);
code_immediate(1, long);
code_op(opcode_jmp);
code_immediate(2, long);
code_op(opcode_push);
code_immediate(0, long);
code_put(pos1, (pos2 - pos1), long);
break;
case bin_eq:
code_node(node->v.bin.arg[1]);
CODE_BINARY(eq, node);
break;
case bin_ne:
code_node(node->v.bin.arg[1]);
CODE_BINARY(ne, node);
break;
case bin_lt:
code_node(node->v.bin.arg[1]);
CODE_BINARY(lt, node);
break;
case bin_le:
code_node(node->v.bin.arg[1]);
CODE_BINARY(le, node);
break;
case bin_gt:
code_node(node->v.bin.arg[1]);
CODE_BINARY(gt, node);
break;
case bin_ge:
code_node(node->v.bin.arg[1]);
CODE_BINARY(ge, node);
break;
case bin_match:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
if (node->v.bin.qualifier & QUALIFIER_MX)
code_op(opcode_regmatch_mx);
else
code_op(opcode_regmatch);
break;
case bin_fnmatch:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
if (node->v.bin.qualifier & QUALIFIER_MX)
code_op(opcode_fnmatch_mx);
else
code_op(opcode_fnmatch);
break;
case bin_add:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_add);
break;
case bin_sub:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_sub);
break;
case bin_mul:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_mul);
break;
case bin_div:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_div);
break;
case bin_mod:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_mod);
break;
case bin_logand:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_logand);
break;
case bin_logor:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_logor);
break;
case bin_logxor:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_logxor);
break;
case bin_shl:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_shl);
break;
case bin_shr:
code_node(node->v.bin.arg[1]);
mark_locus(node, old_locus);
code_op(opcode_shr);
break;
default:
break;
}
}
/* type un */
void
print_type_un(NODE *node, int level)
{
print_level(level);
switch (node->v.un.opcode) {
case unary_not:
printf("NOT\n");
break;
case unary_minus:
printf("NEG\n");
break;
default:
abort();
}
print_node(node->v.un.arg, level+1);
}
void
mark_type_un(NODE *node)
{
mark(node->v.un.arg);
}
void
optimize_type_un(NODE *node)
{
NODE *p = node->v.un.arg;
optimize(p);
if (p->type == node_type_number) {
switch (node->v.un.opcode) {
case unary_not:
node->v.number = !p->v.number;
break;
case unary_minus:
node->v.number = -p->v.number;
break;
case unary_lognot:
node->v.number = ~p->v.number;
break;
}
node->type = node_type_number;
free_node(p);
}
}
void
code_type_un(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.un.arg);
mark_locus(node, old_locus);
switch (node->v.un.opcode) {
case unary_not:
code_op(opcode_not);
break;
case unary_minus:
code_op(opcode_neg);
break;
case unary_lognot:
code_op(opcode_lognot);
break;
default:
abort();
}
}
/* type result */
void
print_type_result(NODE *node, int level)
{
NODE *code, *xcode;
code = node->v.ret.code;
xcode = node->v.ret.xcode;
print_level(level);
printf("SET REPLY ");
print_stat(node->v.ret.stat);
printf("\n");
print_level(level);
printf("CODE:\n");
if (code)
print_node(code, level+1);
print_level(level);
printf("XCODE:\n");
if (xcode)
print_node(xcode, level+1);
print_level(level);
printf("MESSAGE:\n");
if (node->v.ret.message)
print_node(node->v.ret.message, level+1);
printf("\n");
}
void
mark_type_result(NODE *node)
{
mark(node->v.ret.code);
mark(node->v.ret.xcode);
mark(node->v.ret.message);
}
void
optimize_type_result(NODE *node)
{
optimize(node->v.ret.code);
optimize(node->v.ret.xcode);
optimize(node->v.ret.message);
}
static void
code_result_arg(NODE *node)
{
if (node)
code_node(node);
else {
code_op(opcode_push);
code_immediate(NULL, ptr);
}
}
static NODE *
result_argptr(NODE *arg)
{
if (arg && arg->type == node_type_string
&& arg->v.literal->text[0] == 0)
arg = NULL;
return arg;
}
void
code_type_result(NODE *node, struct mu_locus_range const **old_locus)
{
NODE *code, *xcode;
code = result_argptr(node->v.ret.code);
xcode = result_argptr(node->v.ret.xcode);
switch (node->v.ret.stat) {
case SMFIS_REJECT:
if (code && code->type == node_type_string
&& code->v.literal->text[0] != '5')
parse_error_locus(&node->locus,
_("reject code should be 5xx"));
if (xcode && xcode->type == node_type_string
&& xcode->v.literal->text[0] != '5')
parse_error_locus(&node->locus,
_("reject extended code should be 5.x.x"));
break;
case SMFIS_TEMPFAIL:
if (code && code->type == node_type_string
&& code->v.literal->text[0] != '4')
parse_error_locus(&node->locus,
_("tempfail code should be 4xx"));
if (xcode && xcode->type == node_type_string
&& xcode->v.literal->text[0] != '4')
parse_error_locus(&node->locus,
_("tempfail extended code should be 4.x.x"));
break;
default:
break;
}
code_result_arg(node->v.ret.message);
code_result_arg(xcode);
code_result_arg(code);
mark_locus(node, old_locus);
code_op(opcode_result);
code_immediate(node->v.ret.stat, int);
code_op(opcode_nil);
}
/* type header */
void
print_type_header(NODE *node, int level)
{
print_level(level);
printf("%s %s: \n", msgmod_opcode_str(node->v.hdr.opcode),
node->v.hdr.name->text);
print_node_list(node->v.hdr.value, level+1);
}
void
mark_type_header(NODE *node)
{
node->v.hdr.name->flags |= SYM_REFERENCED;
mark(node->v.hdr.value);
}
void
optimize_type_header(NODE *node)
{
optimize(node->v.hdr.value);
}
void
code_type_header(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
if (node->v.hdr.value)
code_node(node->v.hdr.value);
else {
code_op(opcode_push);
code_immediate(0, size);
}
code_op(opcode_header);
code_immediate(node->v.hdr.opcode, int);
code_immediate(node->v.hdr.name->off, size);
}
/* type builtin */
void
print_type_builtin(NODE *node, int level)
{
print_level(level);
printf("BUILTIN %s\n", node->v.builtin.builtin->name);
print_node_list_reverse(node->v.builtin.args, level+1);
}
void
mark_type_builtin(NODE *node)
{
unsigned i;
NODE *p;
struct literal *s = literal_lookup(node->v.builtin.builtin->name);
s->flags |= SYM_REFERENCED;
for (i = 0, p = node->v.builtin.args; p; i++, p = p->next)
mark(p);
}
void
optimize_type_builtin(NODE *node)
{
size_t i;
NODE *p;
for (i = 0, p = node->v.builtin.args; p; i++, p = p->next)
optimize(p);
if (strcmp(node->v.builtin.builtin->name, "interval") == 0) {
if (node->v.builtin.args->type == node_type_string) {
time_t t;
const char *endp;
if (parse_time_interval(
node->v.builtin.args->v.literal->text,
&t, &endp)) {
parse_error_locus(&node->locus,
_("unrecognized time format (near `%s')"),
endp);
return;
}
/* Replace this node */
node->type = node_type_number;
node->v.number = t;
}
}
}
void
code_type_builtin(NODE *node, struct mu_locus_range const **old_locus)
{
NODE *p;
int i;
const struct builtin *bp = node->v.builtin.builtin;
struct literal *s;
/* Pass arguments */
for (p = node->v.builtin.args, i = 0; p; p = p->next, i++)
code_node(p);
if (bp->optcount || (bp->flags & MFD_BUILTIN_VARIADIC)) {
/* Pass the number of actual arguments in a hidden arg */
code_op(opcode_push);
code_immediate(i, size);
}
mark_locus(node, old_locus);
code_op(opcode_builtin);
s = literal_lookup(node->v.builtin.builtin->name);
code_immediate(s->off, size);
code_immediate(node->v.builtin.builtin->handler, ptr);/*FIXME*/
}
/* type concat */
void
print_type_concat(NODE *node, int level)
{
print_level(level);
printf("CONCAT:\n");
print_node(node->v.concat.arg[0], level+1);
print_node(node->v.concat.arg[1], level+1);
}
void
mark_type_concat(NODE *node)
{
mark(node->v.concat.arg[0]);
mark(node->v.concat.arg[1]);
}
void
optimize_type_concat(NODE *node)
{
NODE *arg0, *arg1;
optimize(node->v.concat.arg[0]);
optimize(node->v.concat.arg[1]);
arg0 = node->v.concat.arg[0];
arg1 = node->v.concat.arg[1];
if (arg0->type == node_type_string
&& arg1->type == node_type_string) {
string_begin();
string_add(arg0->v.literal->text,
strlen(arg0->v.literal->text));
string_add(arg1->v.literal->text,
strlen(arg1->v.literal->text));
node->v.literal = string_finish();
node->type = node_type_string;
free_node(arg0);
free_node(arg1);
} else if (arg0->type == node_type_string
&& arg0->v.literal->text[0] == 0) {
copy_node(node, arg1);
free_node(arg0);
} else if (arg1->type == node_type_string
&& arg1->v.literal->text[0] == 0) {
copy_node(node, arg0);
free_node(arg1);
}
}
void
code_type_concat(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.concat.arg[0]);
code_node(node->v.concat.arg[1]);
code_op(opcode_concat);
}
/* type variable */
void
print_type_variable(NODE *node, int level)
{
print_level(level);
printf("VARIABLE %s %s %lu(%lu)\n",
storage_class_str(node->v.var_ref.variable->storage_class),
node->v.var_ref.variable->sym.name,
(unsigned long) node->v.var_ref.nframes,
(unsigned long) node->v.var_ref.variable->off);
}
void
mark_type_variable(NODE *node)
{
if (node->v.var_ref.variable->storage_class == storage_extern)
node->v.var_ref.variable->sym.flags |= SYM_REFERENCED;
}
void
code_type_variable(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_memref(node);
code_op(opcode_deref);
}
/* type asgn */
void
print_type_asgn(NODE *node, int level)
{
print_level(level);
printf("SET %s %s %lu(%lu)\n",
storage_class_str(node->v.asgn.var->storage_class),
node->v.asgn.var->sym.name,
(unsigned long) node->v.asgn.nframes,
(unsigned long) node->v.asgn.var->off);
print_node(node->v.asgn.node, level + 1);
}
void
mark_type_asgn(NODE *node)
{
/* FIXME: This is overly conservative. First of all, `referenced'
does not mean `used', so this can create useless assignments,
even if the variable in question is not referenced elsewhere: */
node->v.asgn.var->sym.flags |= SYM_REFERENCED;
/* Secondly, the node should be marked only if var is referenced
or volatile, but determining this would probably require an
extra pass. */
mark(node->v.asgn.node);
}
void
optimize_type_asgn(NODE *node)
{
optimize(node->v.asgn.node);
}
void
code_type_asgn(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.asgn.node);
node->v.asgn.var->type = node_type(node->v.asgn.node);
code_memref(node);
code_op(opcode_asgn);
}
/* type arg */
void
print_type_arg(NODE *node, int level)
{
print_level(level);
printf("ARG %u\n", node->v.arg.number);
}
void
code_argref(NODE *node)
{
code_op(opcode_memstk);
code_immediate(0, size);
code_immediate((node->v.arg.number + 2), long);
}
void
code_type_arg(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_argref(node);
code_op(opcode_deref);
}
/* type argx */
void
print_type_argx(NODE *node, int level)
{
print_level(level);
printf("ARGX\n");
print_node(node->v.argx.node, level + 1);
}
void
mark_type_argx(NODE *node)
{
mark(node->v.argx.node);
}
void
code_argxref(NODE *node)
{
code_op(opcode_push);
code_immediate(0, size);
code_node(node->v.argx.node);
code_op(opcode_push);
code_immediate((node->v.argx.nargs + 2), size);
code_op(opcode_add);
code_op(opcode_xmemstk);
}
void
code_type_argx(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_argxref(node);
code_op(opcode_deref);
}
void
optimize_type_argx(NODE *node)
{
NODE *argx = node->v.argx.node;
optimize(argx);
if (argx->type == node_type_number) {
node->type = node_type_arg;
node->v.arg.number = node->v.argx.nargs + 2;
}
}
/* type vaptr */
void
print_type_vaptr(NODE *node, int level)
{
print_level(level);
printf("VAPTR\n");
print_node(node->v.node, level + 1);
}
void
mark_type_vaptr(NODE *node)
{
mark(node->v.node);
}
void
code_type_vaptr(NODE *node, struct mu_locus_range const **old_locus)
{
NODE *arg = node->v.node;
mark_locus(node, old_locus);
switch (arg->type) {
case node_type_variable:
code_memref(arg);
break;
case node_type_arg:
code_argref(arg);
break;
case node_type_argx:
code_argxref(arg);
break;
default:
abort();
}
}
void
optimize_type_vaptr(NODE *node)
{
optimize(node->v.node);
}
char *
regex_flags_to_string(int flags, char *buf, size_t size)
{
static struct {
unsigned flag;
char *name;
} regflg[] = {
{ REG_EXTENDED, REG_EXTENDED_NAME },
{ REG_ICASE, REG_ICASE_NAME },
{ REG_NEWLINE, REG_NEWLINE_NAME }
};
char *p;
int i;
p = buf;
size--;
for (i = 0; i < NELEMS(regflg) && size > 0; i++) {
if (regflg[i].flag & flags) {
size_t len = strlen(regflg[i].name);
if (p > buf)
len++;
if (len > size)
len = size;
if (p > buf) {
*p++ = ',';
len--;
}
if (len > 0) {
memcpy(p, regflg[i].name, len);
p += len;
}
size -= len;
}
}
*p = 0;
return buf;
}
/* type regex */
void
print_type_regex(NODE *node, int level)
{
char buffer[REGEX_STRING_BUFSIZE];
print_level(level);
printf("REGEX (%s) %s\n",
regex_flags_to_string(node->v.regex->regflags, buffer,
sizeof buffer),
node->v.regex->lit->text);
}
void
mark_type_regex(NODE *node)
{
node->v.regex->lit->flags |= SYM_REFERENCED;
}
void
code_type_regex(NODE *node, struct mu_locus_range const **old_locus)
{
code_op(opcode_regex);
code_immediate(node->v.regex->index, size);
}
/* type regcomp */
void
print_type_regcomp(NODE *node, int level)
{
char buffer[REGEX_STRING_BUFSIZE];
print_level(level);
printf("REGCOMP %s:\n",
regex_flags_to_string(node->v.regcomp_data.flags,
buffer, sizeof buffer));
print_node(node->v.regcomp_data.expr, level+1);
}
void
mark_type_regcomp(NODE *node)
{
mark(node->v.regcomp_data.expr);
}
static void
save_regexp(NODE *node)
{
struct sym_regex symreg;
memset(&symreg, 0, sizeof symreg);
symreg.regflags = node->v.regcomp_data.flags;
symreg.index = 0;
register_regex(&symreg);
node->v.regcomp_data.regind = symreg.index;
}
void
optimize_type_regcomp(NODE *node)
{
int flags = node->v.regcomp_data.flags;
NODE *arg0 = node->v.regcomp_data.expr;
optimize(arg0);
if (arg0->type == node_type_string) {
node->type = node_type_regex;
node->locus = arg0->locus;
node->v.regex = install_regex(arg0->v.literal, flags);
} else {
save_regexp(node);
}
}
void
code_type_regcomp(NODE *node, struct mu_locus_range const **old_locus)
{
if (node->v.regcomp_data.regind == -1)
save_regexp(node);
code_node(node->v.regcomp_data.expr);
code_op(opcode_regcomp);
code_immediate(node->v.regcomp_data.regind, size);
}
struct trycatch_stack_entry {
int istry;
struct mu_locus_range locus;
unsigned id;
};
static mf_stack_t trycatch_stack;
static unsigned
trycatch_last_id(void)
{
struct trycatch_stack_entry ent;
if (!trycatch_stack || mf_stack_peek(trycatch_stack, 0, &ent))
return 0;
return ent.id;
}
static void
enter_trycatch(int istry, struct mu_locus_range const *locus)
{
struct trycatch_stack_entry ent;
if (!trycatch_stack)
trycatch_stack = mf_stack_create(sizeof(ent), 0);
ent.istry = istry;
mu_locus_range_init(&ent.locus);
mu_locus_range_copy(&ent.locus, locus);
ent.id = trycatch_last_id() + 1;
mf_stack_push(trycatch_stack, &ent);
}
static void
leave_trycatch()
{
struct trycatch_stack_entry ent;
if (mf_stack_pop(trycatch_stack, &ent) == 0)
mu_locus_range_deinit(&ent.locus);
}
static int
_code_trycatch_exit(void *item, void *data)
{
struct trycatch_stack_entry *ent = item;
unsigned id = *(unsigned*)data;
if (ent->id <= id)
return 1;
if (!ent->istry)
code_op(opcode_retcatch);
code_op(opcode_restex);
return 0;
}
static void
code_trycatch_exit(unsigned id)
{
if (trycatch_stack)
mf_stack_enumerate_desc(trycatch_stack, _code_trycatch_exit,
&id);
}
/* type catch */
void
print_type_catch(NODE *node, int level)
{
size_t i;
struct bitmask *bm = &node->v.catch.exmask->bm;
size_t nmax = bitmask_max(bm);
print_level(level);
printf("CATCH ");
if (node->v.catch.exmask->all)
printf("ALL");
else
for (i = 0; i < nmax; i++)
if (bitmask_isset(bm, i))
printf("%s ", mf_exception_str(i));
printf("\n");
print_node_list(node->v.catch.node, level+1);
print_level(level);
printf("END CATCH\n");
}
void
mark_type_catch(NODE *node)
{
mark(node->v.catch.node);
}
void
optimize_type_catch(NODE *node)
{
optimize(node->v.catch.node);
}
void
code_type_catch(NODE *node, struct mu_locus_range const **old_locus)
{
prog_counter_t pos1, endpos;
prog_counter_t ctr;
mark_locus(node, old_locus);
code_op(opcode_catch);
pos1 = code_immediate(NULL, ptr);
code_exmask(node->v.catch.exmask);
ctr = jump_pc;
jump_pc = 0;
enter_trycatch(0, &node->locus);
traverse_tree(node->v.catch.node);
leave_trycatch();
jump_fixup(jump_pc, code_get_counter());
jump_pc = ctr;
code_op(opcode_retcatch);
if (node->v.catch.context == context_function) {
code_op(opcode_jmp);
jump_pc = code_immediate(jump_pc, long);
} else {
code_result_arg(NULL);
code_result_arg(NULL);
code_result_arg(NULL);
code_op(opcode_result);
code_immediate(SMFIS_CONTINUE, int);
}
endpos = code_get_counter ();
code_put(pos1, (endpos - pos1), long);
}
/* type try */
void
print_type_try(NODE *node, int level)
{
print_level(level);
printf("TRY:\n");
print_node_list(node->v.try.node, level+1);
print_type_catch(node->v.try.catch, level+1);
print_level(level);
printf("END TRY\n");
}
void
mark_type_try(NODE *node)
{
mark(node->v.try.node);
mark(node->v.try.catch);
}
void
optimize_type_try(NODE *node)
{
optimize(node->v.try.node);
optimize(node->v.try.catch);
}
void
code_type_try(NODE *node, struct mu_locus_range const **old_locus)
{
prog_counter_t pos, ctr, pos1;
prog_counter_t try_jmp_pc, catch_jmp_pc;
prog_counter_t try_ret_pc, catch_ret_pc;
struct catch_node *catch = &node->v.try.catch->v.catch;
ctr = jump_pc;
code_op(opcode_saveex);
code_exmask(catch->exmask);
code_op(opcode_catch);
pos1 = code_immediate(NULL, ptr);
code_exmask(catch->exmask);
/* Compile `catch' part */
jump_pc = 0;
enter_trycatch(0, &catch->node->locus);
traverse_tree(catch->node);
leave_trycatch();
/* Normal exit from catch */
code_op(opcode_retcatch);
code_op(opcode_jmp);
catch_jmp_pc = code_immediate(NULL, ptr);
/* Target point for returns from catch block */
jump_fixup(jump_pc, code_get_counter());
code_op(opcode_retcatch);
code_op(opcode_jmp);
catch_ret_pc = code_immediate(NULL, ptr);
pos = code_get_counter ();
code_put(pos1, (pos - pos1), long);
/* Compile `try' part */
mark_locus(node, old_locus);
jump_pc = 0;
enter_trycatch(1, &node->v.try.node->locus);
traverse_tree(node->v.try.node);
leave_trycatch();
try_ret_pc = jump_pc;
code_op(opcode_jmp);
try_jmp_pc = code_immediate(NULL, ptr);
/* Restore jump_pc */
jump_pc = ctr;
/* Return part */
/* 1. Fixup return chains */
pos = code_get_counter();
jump_fixup(try_ret_pc, pos);
/* 2. Fixup the jump address for catch returns */
code_put(catch_ret_pc, (pos - catch_ret_pc - 1), long);
/* 3. Code return proper */
/* Restore exceptions */
code_op(opcode_restex);
/* Push result back on stack */
code_op(opcode_pushreg);
code_op(opcode_jmp);
jump_pc = code_immediate(jump_pc, long);
/* Continue part */
/* 1. Fixup jump addresses */
pos = code_get_counter();
code_put(try_jmp_pc, (pos - try_jmp_pc - 1), long);
code_put(catch_jmp_pc, (pos - catch_jmp_pc - 1), long);
code_op(opcode_restex);
/* FIN */
}
/* type throw */
void
print_type_throw(NODE *node, int level)
{
print_level(level);
printf("THROW %s\n", mf_exception_str(node->v.throw.code));
print_node(node->v.throw.expr, level+1);
}
void
mark_type_throw(NODE *node)
{
mark(node->v.throw.expr);
}
void
optimize_type_throw(NODE *node)
{
optimize(node->v.throw.expr);
}
void
code_type_throw(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.throw.expr);
mark_locus(node, old_locus);
code_op(opcode_throw);
code_immediate(node->v.throw.code, ulong);
}
/* type echo */
void
print_type_echo(NODE *node, int level)
{
print_level(level);
printf("ECHO:\n");
print_node(node->v.node, level+1);
}
void
mark_type_echo(NODE *node)
{
mark(node->v.node);
}
void
optimize_type_echo(NODE *node)
{
optimize(node->v.node);
}
void
code_type_echo(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.node);
mark_locus(node, old_locus);
code_op(opcode_echo);
}
/* type return */
void
print_type_return(NODE *node, int level)
{
print_level(level);
if (node->v.node) {
printf("RETURN:\n");
print_node(node->v.node, level+1);
}
}
void
mark_type_return(NODE *node)
{
mark(node->v.node);
}
void
optimize_type_return(NODE *node)
{
optimize(node->v.node);
}
void
code_type_return(NODE *node, struct mu_locus_range const **old_locus)
{
if (func->rettype == dtype_unspecified) {
mark_locus(node, old_locus);
code_op(opcode_jmp);
jump_pc = code_immediate(jump_pc, long);
} else {
code_node(node->v.node);
mark_locus(node, old_locus);
code_op(opcode_popreg);
code_op(opcode_jmp);
jump_pc = code_immediate(jump_pc, long);
}
}
/* type call */
void
print_type_call(NODE *node, int level)
{
print_level(level);
printf("CALL %s\n", node->v.call.func->sym.name);
print_node_list_reverse(node->v.call.args, level+1);
}
void
mark_type_call(NODE *node)
{
NODE *p;
struct literal *s = literal_lookup(node->v.call.func->sym.name);
s->flags |= SYM_REFERENCED;
for (p = node->v.call.args; p; p = p->next)
mark(p);
}
void
optimize_type_call(NODE *node)
{
NODE *p;
for (p = node->v.call.args; p; p = p->next)
optimize(p);
}
void
code_type_call(NODE *node, struct mu_locus_range const **old_locus)
{
NODE *p;
struct literal *s;
struct function *func = node->v.call.func;
int i;
if (func->optcount || func->varargs) {
int j;
/* Count actual arguments */
for (p = node->v.call.args, i = 0; p; p = p->next, i++)
;
/* Provide placeholders for the missing ones. This is
necessary in order to make assignments to optional
arguments possible. */
for (j = i; j < func->parmcount; j++) {
code_op(opcode_push);
code_immediate(0, ulong);
}
/* Pass actual arguments */
for (p = node->v.call.args; p; p = p->next)
code_node(p);
/* Pass the number of actual arguments in a hidden arg */
code_op(opcode_push);
code_immediate(i, long);
} else
for (p = node->v.call.args, i = 0; p; p = p->next, i++)
code_node(p);
mark_locus(node, old_locus);
if (func->entry == 0) {
parse_error("INTERNAL ERROR at %s:%d: unresolved function %s",
__FILE__, __LINE__, func->sym.name);
abort();
}
code_op(opcode_funcall);
s = literal_lookup(func->sym.name);
code_immediate(s->off, size);
code_immediate(func->entry, size);
code_op(opcode_adjust);
code_immediate((func->parmcount + (func->optcount ? 1 : 0)), ulong);
if (func->rettype != dtype_unspecified)
code_op(opcode_pushreg);
}
/* type switch */
void
print_type_switch(NODE *node, int level)
{
struct case_stmt *pcase;
print_level(level);
printf("SWITCH: \n");
print_node(node->v.switch_stmt.node, level+1);
for (pcase = node->v.switch_stmt.cases;
pcase; pcase = pcase->next) {
print_level(level+1);
if (pcase->valist) {
struct valist *vp;
printf("CASE ");
for (vp = pcase->valist; vp; vp = vp->next)
switch (vp->value.type) {
case dtype_string:
printf("\"%s\" ",
vp->value.v.literal->text);
break;
case dtype_number:
printf("%ld ",
vp->value.v.number);
break;
default:
abort();
}
} else {
printf("DEFAULT");
}
putchar('\n');
print_level(level+1);
printf("ACTION\n");
print_node_list(pcase->node, level+2);
}
}
void
mark_type_switch(NODE *node)
{
struct case_stmt *cs;
data_type_t type = node_type(node->v.switch_stmt.node);
size_t tabsize = 0;
record_switch(&node->v.switch_stmt);
mark(node->v.switch_stmt.node);
for (cs = node->v.switch_stmt.cases; cs; cs = cs->next) {
struct valist *vp;
mark(cs->node);
for (vp = cs->valist; vp; vp = vp->next) {
tabsize += 2;
if (vp->value.type != type) {
char buf[NUMERIC_BUFSIZE_BOUND];
long v;
char *p;
/* FIXME: The following code is very crude */
switch (type) {
case dtype_number:
v = strtol(vp->value.v.literal->text,
&p, 0);
if (*p) {
parse_error_locus(&cs->locus,
_("cannot convert %s to number"),
vp->value.v.literal->text);
return;
}
vp->value.v.number = v;
break;
case dtype_string:
snprintf(buf, sizeof buf, "%ld",
vp->value.v.number);
vp->value.v.literal = string_alloc(buf,
strlen(buf));
break;
default:
abort();
}
vp->value.type = type;
}
if (vp->value.type == dtype_string)
vp->value.v.literal->flags |= SYM_REFERENCED;
}
}
node->v.switch_stmt.tabsize = tabsize;
}
void
optimize_type_switch(NODE *node)
{
struct case_stmt *pcase;
data_type_t type;
NODE *p, *bp = NULL;
p = node->v.switch_stmt.node;
optimize(p);
type = node_type(p);
for (pcase = node->v.switch_stmt.cases; pcase; pcase = pcase->next) {
struct valist *vp;
long v;
char *s;
for (vp = pcase->valist; vp; vp = vp->next) {
if (vp->value.type != type) {
char buf[NUMERIC_BUFSIZE_BOUND];
/* FIXME: The following code needs
generalization */
switch (type) {
case dtype_number:
v = strtol(vp->value.v.literal->text,
&s, 0);
if (*s) {
parse_error_locus(&pcase->locus,
_("cannot convert %s to number"),
vp->value.v.literal->text);
return;
}
vp->value.v.number = v;
break;
case dtype_string:
snprintf(buf, sizeof buf, "%ld",
vp->value.v.number);
vp->value.v.literal = string_alloc(buf,
strlen(buf));
break;
default:
abort();
}
}
vp->value.type = type;
}
optimize(pcase->node);
}
if (p->type == node_type_number || p->type == node_type_string) {
for (pcase = node->v.switch_stmt.cases; pcase;
pcase = pcase->next) {
struct valist *vp;
for (vp = pcase->valist; vp; vp = vp->next) {
if (p->type == node_type_number ?
(vp->value.v.number == p->v.number)
/* FIXME: Make sure this works. It is
instead of comparing literal string
values: */
: (vp->value.v.literal == p->v.literal)) {
bp = pcase->node;
pcase->node = NULL;
break;
}
}
}
/* If no node found, use the default one */
if (!bp) {
bp = node->v.switch_stmt.cases->node;
node->v.switch_stmt.cases->node = NULL;
}
if (bp) {
NODE *tail = node->next;
*node = *bp;
free_node(bp);
for (; node->next; node = node->next)
;
node->next = tail;
/* FIXME : free branches */
}
}
}
static void
jump_fixup(prog_counter_t pos, prog_counter_t endpos)
{
while (pos) {
prog_counter_t next = (prog_counter_t)mf_cell_c_value(code_peek(pos), size); /*FIXME*/
code_put(pos, (endpos - pos - 1), long);
pos = next;
}
}
/* The following code is generated for switch statements:
popreg ; Pop the result of the previous instruction into the reg
xlat ; Look up in the table and replace reg with the new value
; If the switch selector is of string type, xlats is
; coded instead
N ; Number of elements in the xlat table
OFF ; Offset of the table in the data segment
bnz L1 ; If xlat failed, jump to the default case
L0: jreg ; Jump to the selected branch
L1:
... ; Default case
L0+off1:
... ; First branch
jmp L2
L0+off2:
... ; Second branch
jmp L2
.
.
.
L0+offN:
... ; Nth branch
L2:
*/
static void
code_switch_branches(NODE *node, data_type_t type)
{
prog_counter_t refpos, jmppos;
struct case_stmt *pcase;
size_t data_off = node->v.switch_stmt.off;
code_op(opcode_popreg);
code_op(type == dtype_number ? opcode_xlat : opcode_xlats);
code_immediate(node->v.switch_stmt.tabsize, ulong);
code_immediate(data_off, size);
code_op(opcode_bnz);
code_immediate(1, long);
refpos = code_op(opcode_jreg);
/* Generate code for the branches */
jmppos = 0;
for (pcase = node->v.switch_stmt.cases;
pcase;
pcase = pcase->next) {
struct valist *vp;
prog_counter_t pos;
pos = code_get_counter() - refpos;
traverse_tree(pcase->node);
if (pcase->next) {
code_op(opcode_jmp);
jmppos = code_immediate(jmppos, long);
}
for (vp = pcase->valist; vp; vp = vp->next) {
switch (type) {
case dtype_number:
dataseg[data_off] =
(STKVAL) vp->value.v.number;
break;
case dtype_string:
dataseg[data_off] =
(STKVAL) vp->value.v.literal->off;
break;
default:
abort();
}
dataseg[data_off + 1] = (STKVAL) pos;
data_off += 2;
}
}
/* Fix up jump offsets */
jump_fixup(jmppos, code_get_counter());
}
void
code_type_switch(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.switch_stmt.node);
code_switch_branches(node, node_type(node->v.switch_stmt.node));
}
/* Loop nesting stack */
struct loop_stack {
struct literal *ident;
prog_counter_t *endjmp;
prog_counter_t *nxtjmp;
unsigned trycatch_id;
};
static mf_stack_t loop_stack;
static int
_find_loop(void *item, void *data)
{
struct loop_stack *ent = item;
struct loop_stack *s = data;
if (ent->ident == s->ident) {
*s = *ent;
return 1;
}
return 0;
}
static int
find_loop(struct literal *lit, struct loop_stack *pret)
{
if (!loop_stack)
return 0;
else if (!lit)
return mf_stack_peek(loop_stack, 0, pret) == 0;
else {
struct loop_stack ent;
if (!pret)
pret = &ent;
pret->ident = lit;
return mf_stack_enumerate_desc(loop_stack, _find_loop, pret);
}
}
int
within_loop(struct literal *lit)
{
return find_loop(lit, NULL);
}
void
enter_loop(struct literal *lit, prog_counter_t *endptr, prog_counter_t *nxtptr)
{
struct loop_stack ent;
if (!loop_stack)
loop_stack = mf_stack_create(sizeof(struct loop_stack), 0);
ent.endjmp = endptr;
ent.nxtjmp = nxtptr;
ent.ident = lit;
ent.trycatch_id = trycatch_last_id();
mf_stack_push(loop_stack, &ent);
}
void
leave_loop()
{
mf_stack_pop(loop_stack, NULL);
}
/* type next */
void
print_type_next(NODE *node, int level)
{
print_level(level);
printf("NEXT");
if (node->v.literal)
printf(" %s", node->v.literal->text);
printf("\n");
}
void
code_type_next(NODE *node, struct mu_locus_range const **old_locus)
{
struct loop_stack ent;
mark_locus(node, old_locus);
if (find_loop(node->v.literal, &ent) == 0) {
parse_error_locus(&node->locus,
_("INTERNAL ERROR at %s:%d: cannot find loop"),
__FILE__, __LINE__);
abort();
}
code_trycatch_exit(ent.trycatch_id);
code_op(opcode_jmp);
*ent.nxtjmp = code_immediate(*ent.nxtjmp, long);
}
/* type break */
void
print_type_break(NODE *node, int level)
{
print_level(level);
printf("BREAK");
if (node->v.literal)
printf(" %s", node->v.literal->text);
printf("\n");
}
void
code_type_break(NODE *node, struct mu_locus_range const **old_locus)
{
struct loop_stack ent;
mark_locus(node, old_locus);
if (find_loop(node->v.literal, &ent) == 0) {
parse_error_locus(&node->locus,
_("INTERNAL ERROR at %s:%d: cannot find loop"),
__FILE__, __LINE__);
abort();
}
code_trycatch_exit(ent.trycatch_id);
code_op(opcode_jmp);
*ent.endjmp = code_immediate(*ent.endjmp, long);
}
/* type loop */
void
print_type_loop(NODE *node, int level)
{
print_level(level);
printf("LOOP");
if (node->v.loop.ident)
printf(" %s", node->v.loop.ident->text);
printf(":\n");
if (node->v.loop.for_stmt) {
print_level(level);
printf("FOR ");
print_node_list(node->v.loop.for_stmt, level + 1);
}
if (node->v.loop.beg_while) {
print_level(level);
printf("BEG_WHILE ");
print_node_list(node->v.loop.beg_while, level + 1);
}
if (node->v.loop.end_while) {
print_level(level);
printf("BEG_WHILE ");
print_node_list(node->v.loop.end_while, level + 1);
}
if (node->v.loop.stmt) {
print_level(level);
printf("BY ");
print_node_list(node->v.loop.stmt, level + 1);
}
print_level(level);
print_node_list(node->v.loop.body, level + 1);
}
void
mark_type_loop(NODE *node)
{
mark(node->v.loop.beg_while);
mark(node->v.loop.stmt);
mark(node->v.loop.for_stmt);
mark(node->v.loop.end_while);
mark(node->v.loop.body);
}
void
optimize_type_loop(NODE *node)
{
optimize(node->v.loop.beg_while);
if (node->v.loop.beg_while) {
if (node->v.loop.beg_while->type == node_type_number) {
if (node->v.loop.beg_while->v.number)
node->v.loop.beg_while = NULL;
else
node->type = node_type_noop;
/* FIXME: free subtrees?? */
return;
} else if (node->v.loop.beg_while->type == node_type_string) {
if (node->v.loop.beg_while->v.literal->text[0])
node->v.loop.beg_while = NULL;
else
node->type = node_type_noop;
return;
}
}
optimize(node->v.loop.stmt);
optimize(node->v.loop.for_stmt);
optimize(node->v.loop.end_while);
optimize(node->v.loop.body);
}
void
code_type_loop(NODE *node, struct mu_locus_range const **old_locus)
{
/* FIXME */
/*
.
.
.
L_begin:
[
bz L_end]
[
bz L_end]
[]
jmp L_begin
L_end:
*/
prog_counter_t begin, end, stmt, endjmp = 0, nxtjmp = 0;
mark_locus(node, old_locus);
enter_loop(node->v.loop.ident, &endjmp, &nxtjmp);
traverse_tree(node->v.loop.for_stmt);
begin = code_get_counter();
if (node->v.loop.beg_while) {
code_node(node->v.loop.beg_while);
code_op(opcode_bz);
endjmp = code_immediate(endjmp, long);
}
traverse_tree(node->v.loop.body);
stmt = code_get_counter();
if (node->v.loop.end_while) {
code_node(node->v.loop.end_while);
code_op(opcode_bz);
endjmp = code_immediate(endjmp, long);
}
traverse_tree(node->v.loop.stmt);
code_op(opcode_jmp);
code_immediate((begin - code_get_counter() - 1), long);
end = code_get_counter();
jump_fixup(endjmp, end);
jump_fixup(nxtjmp, stmt);
leave_loop();
}
/* type backref */
void
print_type_backref(NODE *node, int level)
{
print_level(level);
printf("BACKREF: %ld\n", node->v.number);
}
void
code_type_backref(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
code_op(opcode_backref);
code_immediate(node->v.number, uint);
}
/* type cast */
void
print_type_cast(NODE *node, int level)
{
print_level(level);
printf("CAST %s\n", type_to_string(node->v.cast.data_type));
print_node_list(node->v.cast.node, level+1);
}
void
mark_type_cast(NODE *node)
{
mark(node->v.cast.node);
}
void
optimize_type_cast(NODE *node)
{
NODE *p;
optimize(node->v.cast.node);
p = node->v.cast.node;
if (p->type == node_type_string) {
node->type = node_type_string;
node->v = p->v;
free_node(p);
} else if (p->type == node_type_number) {
char buf[NUMERIC_BUFSIZE_BOUND];
snprintf(buf, sizeof buf, "%ld", p->v.number);
node->v.literal = string_alloc(buf, strlen(buf));
node->type = node_type_string;
} else if (node->v.cast.data_type == dtype_number
&& node_type(p) == dtype_number) {
node->type = p->type;
node->locus = p->locus;
node->v = p->v;
free_node(p);
}
}
int
code_cast(struct mu_locus_range const *locus, data_type_t fromtype, data_type_t totype)
{
if (fromtype == totype)
return 0;
switch (fromtype) {
case dtype_unspecified:
parse_error_locus(locus,
_("expression evaluates to unspecified data type"));
return 1;
case dtype_string:
code_op(opcode_ston);
break;
case dtype_number:
case dtype_pointer:
code_op(opcode_ntos);
break;
}
return 0;
}
void
code_type_cast(NODE *node, struct mu_locus_range const **old_locus)
{
code_node(node->v.cast.node);
code_cast(&node->v.cast.node->locus,
node_type(node->v.cast.node),
node->v.cast.data_type);
}
/* type funcdecl */
void
mark_type_funcdecl(NODE *node)
{
mark(node->v.funcdecl.tree);
}
void
optimize_type_funcdecl(NODE *node)
{
optimize(node->v.funcdecl.tree);
}
void
code_type_funcdecl(NODE *node, struct mu_locus_range const **old_locus)
{
prog_counter_t pc;
func = node->v.funcdecl.func;
/* Assign the entry point early to properly
handle recursive functions */
func->entry = code_get_counter();
codegen(&pc, node->v.funcdecl.tree, func->exmask, 0,
node->v.funcdecl.auto_count);
}
/* type progdecl */
void
mark_type_progdecl(NODE *node)
{
mark(node->v.progdecl.tree);
}
void
optimize_type_progdecl(NODE *node)
{
optimize(node->v.progdecl.tree);
}
void
code_type_progdecl(NODE *node, struct mu_locus_range const **old_locus)
{
enum smtp_state tag;
tag = node->v.progdecl.tag;
if (root_node[tag]) {
parse_warning_locus(&node->locus,
_("redefinition of handler `%s'"),
state_to_string(tag));
parse_warning_locus(&root_node[tag]->locus,
_("this is the location of the previous definition"));
}
root_node[tag] = node->v.progdecl.tree;
if (codegen(&entry_point[tag],
node->v.progdecl.tree, 0, 1,
node->v.progdecl.auto_count) == 0)
milter_enable_state(tag);
}
/* type offset */
void
mark_type_offset(NODE *node)
{
if (node->v.var_ref.variable->storage_class == storage_extern)
node->v.var_ref.variable->sym.flags |= SYM_REFERENCED;
}
void
print_type_offset(NODE *node, int level)
{
print_level(level);
printf("OFFSET OF VARIABLE %s %s %lu(%lu)\n",
storage_class_str(node->v.var_ref.variable->storage_class),
node->v.var_ref.variable->sym.name,
(unsigned long) node->v.var_ref.nframes,
(unsigned long) node->v.var_ref.variable->off);
}
void
optimize_type_offset(NODE *node)
{
node->type = node_type_number;
switch (node->v.var_ref.variable->storage_class) {
case storage_extern:
node->v.number = node->v.var_ref.variable->off;
break;
case storage_auto:
node->v.number = 0;
break;
case storage_param:
node->v.number = node->v.var_ref.variable->ord;
}
}
void
code_type_offset(NODE *node, struct mu_locus_range const **old_locus)
{
mark_locus(node, old_locus);
switch (node->v.var_ref.variable->storage_class) {
case storage_extern:
code_op(opcode_push);
code_immediate(node->v.var_ref.variable->off, size);
break;
case storage_auto:
code_op(opcode_push);
code_immediate(0, size);
break;
case storage_param:
code_op(opcode_push);
code_immediate(node->v.var_ref.variable->ord, size);
}
}