Commit 86201b8e authored by eddietwo's avatar eddietwo

fastclassifier | specialize + uninstall

parent a508afea
......@@ -66,7 +66,8 @@ distclean:
DISTFILES = AUTHORS COPYRIGHT INSTALL LICENSE NEWS README \
doc/Makefile.in doc/click.1 doc/click.5 doc/click.o.8 \
doc/click-align.1 doc/click-install.1 doc/click-xform.1 \
doc/click-align.1 doc/click-fastclassifier.1 doc/click-install.1 \
doc/click-uninstall.1 doc/click-xform.1 \
doc/element2man.pl doc/mkelementmap.pl \
doc/Element.3 doc/Connection.3 doc/HandlerRegistry.3 \
conf/make-ip-conf.pl conf/ip.clickpat \
......
......@@ -34,7 +34,9 @@ install-man:
$(INSTALL_DATA) $(srcdir)/click.5 $(mandir)/man5/click.5
$(INSTALL_DATA) $(srcdir)/click.o.8 $(mandir)/man8/click.o.8
$(INSTALL_DATA) $(srcdir)/click-align.1 $(mandir)/man1/click-align.1
$(INSTALL_DATA) $(srcdir)/click-fastclassifier.1 $(mandir)/man1/click-fastclassifier.1
$(INSTALL_DATA) $(srcdir)/click-install.1 $(mandir)/man1/click-install.1
$(INSTALL_DATA) $(srcdir)/click-uninstall.1 $(mandir)/man1/click-uninstall.1
$(INSTALL_DATA) $(srcdir)/click-xform.1 $(mandir)/man1/click-xform.1
$(INSTALL_DATA) $(srcdir)/Element.3 $(mandir)/man3/Element.3
$(INSTALL_DATA) $(srcdir)/Connection.3 $(mandir)/man3/Connection.3
......
.\" -*- mode: nroff -*-
.ds V 1.0
.ds E " \-\-
.if t .ds E \(em
.de Sp
.if n .sp
.if t .sp 0.4
..
.de Es
.Sp
.RS 5
.nf
..
.de Ee
.fi
.RE
.PP
..
.de Rs
.RS
.Sp
..
.de Re
.Sp
.RE
..
.de M
.BR "\\$1" "(\\$2)\\$3"
..
.de RM
.RB "\\$1" "\\$2" "(\\$3)\\$4"
..
.TH CLICK-FASTCLASSIFIER 1 "9/Feb/2000" "Version \*V"
.SH NAME
click-fastclassifier \- specializes Classifier code in a Click router configuration
'
.SH SYNOPSIS
.B click-fastclassifier
.I \%[options]
.I \%[router\-file]
'
.SH DESCRIPTION
The
.B click-fastclassifier
tool speeds up a Click configuration by creating fast versions of all its
.M Classifier n
elements. It reads a router configuration file in the
.M click 5
language and creates specialized C++ source code for each Classifier
element. A Classifier is expanded into a series of if statements; for
example, the expansion of `Classifier(0/80, -)' contains this function:
.PP
.nf
inline void
FastClassifier_a_ac::length_unchecked_push(Packet *p)
{
const unsigned *data = (const unsigned *)(p->data() - 0);
step_0:
if ((data[0] & 255U) == 128U) {
output(0).push(p);
return;
}
output(1).push(p);
return;
}
.fi
.PP
After creating the source code,
.B click-fastclassifier
will optionally compile it into dynamically loadable packages. The
Classifier elements in the input configuration are changed to use new
FastClassifier elements, and the resulting configuration, plus source code
and any compiled packages, are combined into an archive and written to the
standard output. You can install such an archive into the
.M click.o 8
Linux kernel module with
.M click-install 1 :
.Sp
.nf
% click-fastclassifier -k CONFIGURATION | click-install
.fi
.Sp
The
.M click 1
user level driver can read the archives directly.
'
.SH "OPTIONS"
'
If any filename argument is a single dash "-",
.B click-align
will use the standard input or output instead, as appropriate.
'
.TP 5
.BI \-f " file"
.PD 0
.TP
.BI \-\-file " file"
Read the router configuration to transform from
.IR file .
The default is the standard input.
'
.Sp
.TP
.BI \-o " file"
.TP
.BI \-\-output " file"
Write the output router configuration to
.IR file .
The default is the standard output.
'
.Sp
.TP
.BR \-k ", " \-\-kernel
Generate a dynamically loadable package for the
.M click.o 8
Linux kernel module.
'
.Sp
.TP
.BR \-u ", " \-\-user
Generate a dynamically loadable package for the
.M click 1
user-level driver.
'
.Sp
.TP 5
.BR \-s ", " \-\-source
Output only the source code for the FastClassifier elements.
'
.Sp
.TP 5
.BR \-c ", " \-\-config
Output only the new configuration (the one that includes FastClassifiers).
'
.Sp
.TP 5
.BI \-\-help
Print usage information and exit.
'
.Sp
.TP
.BI \-\-version
Print the version number and some quickie warranty information and exit.
'
.PD
'
.SH "SEE ALSO"
.M click 1 ,
.M click-install 1 ,
.M click 5 ,
.M click.o 8 ,
.M Classifier n
'
.SH AUTHOR
.na
Eddie Kohler, eddietwo@lcs.mit.edu
.br
http://www.pdos.lcs.mit.edu/click/
'
......@@ -40,17 +40,17 @@ click-install \- installs a router configuration into the Click kernel module
.I \%[router\-file]
'
.SH DESCRIPTION
The
.B click-install
tool is a convenience program that installs a Click router configuration
.B Click-install
is a convenience program that installs a Click router configuration
into the current Linux kernel. It reads a router configuration file in the
.M click 5
language; installs the
language, installs the
.M click.o 8
kernel module if necessary; installs any dynamically loadable packages
required by the router configuration; installs the router configuration
itself by writing it to /proc/click/config; reports any errors to standard
error; and uninstalls any unused packages.
kernel module if necessary, compiles and/or installs any dynamically
loadable packages required by the router configuration, installs the router
configuration itself by writing it to /proc/click/config, and uninstalls
any unused packages. It reports any errors to standard error, including
errors reported by the Click module.
'
.SH "OPTIONS"
'
......@@ -69,6 +69,11 @@ The default is the standard input.
'
.Sp
.TP 5
.BR \-u ", " \-\-uninstall
Uninstall and reinstall the Click kernel module.
'
.Sp
.TP 5
.BI \-\-help
Print usage information and exit.
'
......@@ -80,6 +85,7 @@ Print the version number and some quickie warranty information and exit.
.PD
'
.SH "SEE ALSO"
.M click-uninstall 1 ,
.M click 5 ,
.M click.o 8
'
......
.\" -*- mode: nroff -*-
.ds V 1.0
.ds E " \-\-
.if t .ds E \(em
.de Sp
.if n .sp
.if t .sp 0.4
..
.de Es
.Sp
.RS 5
.nf
..
.de Ee
.fi
.RE
.PP
..
.de Rs
.RS
.Sp
..
.de Re
.Sp
.RE
..
.de M
.BR "\\$1" "(\\$2)\\$3"
..
.de RM
.RB "\\$1" "\\$2" "(\\$3)\\$4"
..
.TH CLICK-UNINSTALL 1 "9/Feb/2000" "Version \*V"
.SH NAME
click-uninstall \- uninstalls the Click kernel module
'
.SH SYNOPSIS
.B click-uninstall
.I \%[options]
'
.SH DESCRIPTION
.B Click-uninstall
is a convenience program that uninstalls Click from the current Linux
kernel. It uninstalls the currently running configuration, any dynamically
loadable Click packages, and the
.M click.o 8
kernel module itself.
'
.SH "OPTIONS"
'
.TP 5
.PD 0
.BI \-\-help
Print usage information and exit.
'
.Sp
.TP
.BI \-\-version
Print the version number and some quickie warranty information and exit.
'
.PD
'
.SH "SEE ALSO"
.M click-install 1 ,
.M click 5 ,
.M click.o 8
'
.SH AUTHOR
.na
Eddie Kohler, eddietwo@lcs.mit.edu
.br
http://www.pdos.lcs.mit.edu/click/
'
......@@ -75,8 +75,8 @@ Usage: %s [OPTION]... [ROUTERFILE]\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-o, --output FILE Write output to FILE.\n\
-k, --kernel Create Linux kernel module code (on by default).\n\
-u, --user Create user-level code (on by default).\n\
-k, --kernel Compile into Linux kernel binary package.\n\
-u, --user Compile into user-level binary package.\n\
-s, --source Write source code only.\n\
-c, --config Write new configuration only.\n\
--help Print this message and exit.\n\
......@@ -371,9 +371,11 @@ analyze_classifier(RouterT *r, int classifier_ei,
gen_eclass_names.push_back(class_name);
gen_cxxclass_names.push_back(cxx_name);
header << "class " << cxx_name << " : public Element { public:\n "
<< cxx_name << "() : Element(1, " << noutputs
<< ") { MOD_INC_USE_COUNT; }\n ~"
header << "class " << cxx_name << " : public Element {\n\
void specialize_away() { }\n\
public:\n "
<< cxx_name << "() { set_ninputs(1); set_noutputs(" << noutputs
<< "); MOD_INC_USE_COUNT; }\n ~"
<< cxx_name << "() { MOD_DEC_USE_COUNT; }\n\
const char *class_name() const { return \"" << class_name << "\"; }\n\
Processing default_processing() const { return PUSH; }\n "
......@@ -426,8 +428,8 @@ main(int argc, char **argv)
const char *router_file = 0;
const char *output_file = 0;
int compile_kernel = -1;
int compile_user = -1;
int compile_kernel = 0;
int compile_user = 0;
bool source_only = false;
bool config_only = false;
......@@ -495,14 +497,6 @@ particular purpose.\n");
}
done:
if (compile_kernel < 0 && compile_user < 0) {
#ifdef HAVE_LINUXMODULE_TARGET
compile_kernel = compile_user = 1;
#else
compile_user = 1;
#endif
}
RouterT *r = read_router_file(router_file, errh);
if (!r || errh->nerrors() > 0)
exit(1);
......@@ -556,8 +550,11 @@ particular purpose.\n");
header << "#ifndef CLICKSOURCE_" << package_name << "_HH\n"
<< "#define CLICKSOURCE_" << package_name << "_HH\n"
<< "#include \"clickpackage.hh\"\n#include \"element.hh\"\n";
source << "#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n"
<< "#include \"" << package_name << ".hh\"\n";
source << "#ifdef HAVE_CONFIG_H\n# include <config.h>\n#endif\n";
if (!source_only)
source << "#include \"" << package_name << ".hh\"\n";
else
source << "/* #include \"" << package_name << ".hh\" */\n";
// write Classifier programs
for (int i = 0; i < nclassifiers; i++)
......
Makefile *.d
click-install
click-install click-uninstall
......@@ -44,7 +44,7 @@ CPPFLAGS = @CPPFLAGS@ -MMD -DCLICK_TOOL
CFLAGS = @CFLAGS@
CXXFLAGS = @CXXFLAGS@
DEFS = @DEFS@ -DCLICK_LIBDIR='"$(libdir)"' \
DEFS = @DEFS@ -DCLICK_BINDIR='"$(bindir)"' -DCLICK_LIBDIR='"$(libdir)"' \
-I. -I$(top_builddir) -I$(srcdir) \
-I$(top_srcdir)/tools/lib -I$(top_srcdir)/lib
LDFLAGS = @LDFLAGS@
......
......@@ -29,10 +29,12 @@
#define HELP_OPT 300
#define VERSION_OPT 301
#define ROUTER_OPT 302
#define UNINSTALL_OPT 303
static Clp_Option options[] = {
{ "file", 'f', ROUTER_OPT, Clp_ArgString, 0 },
{ "help", 0, HELP_OPT, 0, 0 },
{ "uninstall", 'u', UNINSTALL_OPT, 0, Clp_Negate },
{ "version", 'v', VERSION_OPT, 0, 0 },
};
......@@ -56,12 +58,107 @@ Usage: %s [OPTION]... [ROUTERFILE]\n\
\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-u, --uninstall Uninstall Click from kernel, then reinstall.\n\
--help Print this message and exit.\n\
-v, --version Print version number and exit.\n\
\n\
Report bugs to <click@pdos.lcs.mit.edu>.\n", program_name);
}
static void
compile_archive_packages(RouterT *r, ErrorHandler *errh)
{
const HashMap<String, int> &requirements = r->requirement_map();
const Vector<ArchiveElement> &archive = r->archive();
HashMap<String, int> have_requirements(0);
// analyze archive
for (int i = 0; i < archive.size(); i++) {
const ArchiveElement &ae = archive[i];
if (ae.name.substring(-3) == ".cc") {
int &have = have_requirements.find_force(ae.name.substring(0, -3));
have |= 1;
} else if (ae.name.substring(-3) == ".ko") {
int &have = have_requirements.find_force(ae.name.substring(0, -3));
have |= 2;
}
}
String tmpdir;
String click_compile_prog;
ErrorHandler *cerrh = 0;
// check requirements
int thunk = 0, value; String package;
while (requirements.each(thunk, package, value))
if (value >= 0 && have_requirements[package] == 1) {
// have source, but not package; compile it
if (!cerrh) {
// initialize
cerrh = new ContextErrorHandler
(errh, "While compiling package `" + package + ".ko':");
tmpdir = click_mktmpdir(cerrh);
if (!tmpdir) exit(1);
if (chdir(tmpdir.cc()) < 0)
cerrh->fatal("cannot chdir to %s: %s", tmpdir.cc(), strerror(errno));
click_compile_prog = clickpath_find_file("click-compile", "bin", CLICK_BINDIR, cerrh);
}
// look for .hh and .cc files
String compiler_options;
String filename = package + ".hh";
int aidx = r->archive_index(filename);
if (aidx >= 0) {
FILE *f = fopen(filename, "w");
if (!f)
cerrh->fatal("%s: %s", filename.cc(), strerror(errno));
fwrite(archive[aidx].data.data(), 1, archive[aidx].data.length(), f);
fclose(f);
}
filename = package + ".cc";
assert(r->archive_index(filename) >= 0);
String source_text = r->archive(filename).data;
FILE *f = fopen(filename, "w");
if (!f)
cerrh->fatal("%s: %s", filename.cc(), strerror(errno));
fwrite(source_text.data(), 1, source_text.length(), f);
fclose(f);
// grab compiler options
if (source_text.substring(0, 17) == "// click-compile:") {
const char *s = source_text.data();
int pos = 17;
int len = source_text.length();
while (pos < len && s[pos] != '\n' && s[pos] != '\r')
pos++;
// XXX check user input for shell metas?
compiler_options = source_text.substring(17, pos - 17) + " ";
}
// run click-compile
errh->message("Compiling `%s.ko'...", package.cc());
String compile_command = click_compile_prog + " --target=kernel --package=" + package + ".ko " + compiler_options + filename;
int compile_retval = system(compile_command.cc());
if (compile_retval == 127)
cerrh->fatal("could not run `%s'", compile_command.cc());
else if (compile_retval < 0)
cerrh->fatal("could not run `%s': %s", compile_command.cc(), strerror(errno));
else if (compile_retval != 0)
cerrh->fatal("`%s' failed", compile_command.cc());
// grab object file and add to archive
ArchiveElement ae = init_archive_element(package + ".ko", 0600);
ae.data = file_string(package + ".ko", cerrh);
r->add_archive(ae);
}
if (cerrh)
delete cerrh;
}
static bool
read_package_file(String filename, HashMap<String, int> &packages,
ErrorHandler *errh)
......@@ -126,6 +223,7 @@ main(int argc, char **argv)
program_name = Clp_ProgramName(clp);
const char *router_file = 0;
bool uninstall = false;
while (1) {
int opt = Clp_Next(clp);
......@@ -154,6 +252,10 @@ particular purpose.\n");
router_file = clp->arg;
break;
case UNINSTALL_OPT:
uninstall = !clp->negated;
break;
bad_option:
case Clp_BadOption:
short_usage();
......@@ -172,6 +274,30 @@ particular purpose.\n");
exit(1);
r->flatten(errh);
// uninstall Click if requested
if (uninstall && access("/proc/click", F_OK) >= 0) {
// install blank configuration
FILE *f = fopen("/proc/click/config", "w");
if (!f)
errh->fatal("cannot install configuration: %s", strerror(errno));
fputs("// nothing\n", f);
fclose(f);
// find current packages
HashMap<String, int> active_modules(-1);
HashMap<String, int> packages(-1);
read_package_file("/proc/modules", active_modules, errh);
read_package_file("/proc/click/packages", packages, errh);
// remove packages
String to_remove = packages_to_remove(active_modules, packages);
if (to_remove) {
String cmdline = "/sbin/rmmod " + to_remove + " 2>/dev/null";
(void) system(cmdline);
}
(void) system("/sbin/rmmod click");
if (access("/proc/click", F_OK) >= 0)
errh->warning("could not uninstall Click module");
}
// check for Click module; install it if not available
if (access("/proc/click", F_OK) < 0) {
String click_o =
......@@ -188,6 +314,9 @@ particular purpose.\n");
read_package_file("/proc/modules", active_modules, errh);
read_package_file("/proc/click/packages", packages, errh);
// check for uncompiled archive packages and try to compile them
compile_archive_packages(r, errh);
// install archived objects. mark them with leading underscores.
// may require renaming to avoid clashes in `insmod'
{
......
......@@ -171,17 +171,14 @@ particular purpose.\n");
read_package_file("/proc/click/packages", packages, errh);
// remove unused packages
{
String to_remove = packages_to_remove(active_modules, packages);
if (to_remove) {
String cmdline = "/sbin/rmmod " + to_remove + " 2>/dev/null";
(void) system(cmdline);
}
String cmdline = "/sbin/rmmod click";
int retval = system(cmdline);
if (retval != 0)
errh->fatal("`rmmod click' failed");
String to_remove = packages_to_remove(active_modules, packages);
if (to_remove) {
String cmdline = "/sbin/rmmod " + to_remove + " 2>/dev/null";
(void) system(cmdline);
}
(void) system("/sbin/rmmod click");
if (access("/proc/click", F_OK) >= 0)
errh->fatal("could not uninstall Click module");
return 0;
}
......@@ -150,8 +150,8 @@ Usage: %s [OPTION]... [ROUTERFILE]\n\
Options:\n\
-f, --file FILE Read router configuration from FILE.\n\
-o, --output FILE Write output to FILE.\n\
-k, --kernel Create Linux kernel module code (on by default).\n\
-u, --user Create user-level code (on by default).\n\
-k, --kernel Compile into Linux kernel binary package.\n\
-u, --user Compile into user-level binary package.\n\
-s, --source Write source code only.\n\
-c, --config Write new configuration only.\n\
-n, --no-specialize CLASS Don't specialize element class CLASS.\n\
......@@ -181,8 +181,8 @@ main(int argc, char **argv)
const char *output_file = 0;
int source_only = 0;
int config_only = 0;
int compile_kernel = -1;
int compile_user = -1;
int compile_kernel = 0;
int compile_user = 0;
Vector<String> like1, like2;
HashMap<String, int> specializing(1);
......@@ -274,13 +274,6 @@ particular purpose.\n");
}
done:
if (compile_kernel < 0 && compile_user < 0) {
#ifdef HAVE_LINUXMODULE_TARGET
compile_kernel = compile_user = 1;
#else
compile_user = 1;
#endif
}
if (config_only)
compile_kernel = compile_user = 0;
......@@ -341,7 +334,8 @@ particular purpose.\n");
// output
StringAccum out;
out << "#ifdef HAVE_CONFIG_H\n\
out << "// click-compile: -w -fno-access-control\n\
#ifdef HAVE_CONFIG_H\n\
# include <config.h>\n\
#endif\n\
#include \"clickpackage.hh\"\n";
......
......@@ -107,6 +107,9 @@ CxxFunction::find_expr(const String &pattern, int *pos1, int *pos2,
while (tpos < tlen && ppos < plen) {
if (isspace(ps[ppos])) {
if (ppos > 0 && (isalnum(ps[ppos-1]) || ps[ppos-1] == '_')
&& (isalnum(ts[tpos]) || ts[tpos] == '_'))
break;
while (tpos < tlen && isspace(ts[tpos]))
tpos++;
ppos++;
......@@ -338,6 +341,14 @@ CxxClass::find_should_rewrite()
reach(simple_action, reached);
_should_rewrite[simple_action] = any = true;
}
int specialize_away = _fn_map["specialize_away"];
if (specialize_away >= 0) {
for (int i = 0; i < nfunctions(); i++) {
const String &n = _functions[i].name();
if (n != _name && n[0] != '~')
_should_rewrite[i] = any = true;
}
}
return any;
}
......@@ -585,13 +596,13 @@ CxxInfo::parse_function_definition(const String &text, int fn_start_p,
for (p = paren_p - 1; p > fn_start_p && isspace(s[p]); p--)
/* nada */;
int end_fn_name_p = p + 1;
while (p > fn_start_p && (isalnum(s[p]) || s[p] == '_'))
while (p >= fn_start_p && (isalnum(s[p]) || s[p] == '_' || s[p] == '~'))
p--;
String fn_name = original.substring(p + 1, end_fn_name_p - (p + 1));
String class_name;
if (p >= fn_start_p + 2 && s[p] == ':' && s[p-1] == ':') {
int end_class_name_p = p - 1;
for (p -= 2; p >= fn_start_p && (isalnum(s[p]) || s[p] == '_'); p--)
for (p -= 2; p >= fn_start_p && (isalnum(s[p]) || s[p] == '_' || s[p] == '~'); p--)
/* nada */;
if (p > fn_start_p && s[p] == ':') // nested class fns uninteresting
return close_brace_p;
......
......@@ -241,16 +241,38 @@ Specializer::create_class(SpecializedClass &spc)
CxxClass *old_cxxc = _cxxinfo.find_class(old_eti.cxx_name);
CxxClass *new_cxxc = _cxxinfo.make_class(spc.cxx_name);
assert(old_cxxc && new_cxxc);
new_cxxc->add_parent(old_cxxc);
bool specialize_away = (old_cxxc->find("specialize_away") != 0);
String parent_cxx_name = old_eti.cxx_name;
if (specialize_away) {
CxxClass *parent = old_cxxc->parent(0);
new_cxxc->add_parent(parent);
parent_cxx_name = parent->name();
} else
new_cxxc->add_parent(old_cxxc);
spc.cxxc = new_cxxc;
// add helper functions: constructor, destructor, class_name, is_a
new_cxxc->defun
(CxxFunction(spc.cxx_name, true, "", "()",
" MOD_INC_USE_COUNT; ", ""));
new_cxxc->defun
(CxxFunction("~" + spc.cxx_name, true, "", "()",
" MOD_DEC_USE_COUNT; ", ""));
if (specialize_away) {
CxxFunction *f = old_cxxc->find(old_eti.cxx_name);
CxxFunction &constructor = new_cxxc->defun
(CxxFunction(spc.cxx_name, true, "", "()", f->body(), f->clean_body()));
if (!constructor.find_expr(compile_pattern("MOD_INC_USE_COUNT")))
constructor.set_body(constructor.body() + "\nMOD_INC_USE_COUNT; ");
f = old_cxxc->find("~" + old_eti.cxx_name);
CxxFunction &destructor = new_cxxc->defun
(CxxFunction("~" + spc.cxx_name, true, "", "()", f->body(), f->clean_body()));
if (!destructor.find_expr(compile_pattern("MOD_DEC_USE_COUNT")))
destructor.set_body(destructor.body() + "\nMOD_DEC_USE_COUNT; ");
} else {
new_cxxc->defun
(CxxFunction(spc.cxx_name, true, "", "()",
" MOD_INC_USE_COUNT; ", ""));
new_cxxc->defun
(CxxFunction("~" + spc.cxx_name, true, "", "()",
" MOD_DEC_USE_COUNT; ", ""));
}
new_cxxc->defun
(CxxFunction("class_name", true, "const char *", "() const",
String(" return \"") + spc.click_name + "\"; ", ""));
......@@ -261,7 +283,7 @@ Specializer::create_class(SpecializedClass &spc)
(CxxFunction("is_a", false, "bool", "(const char *n) const",
"\n return (strcmp(n, \"" + spc.click_name + "\") == 0\n\
|| strcmp(n, \"" + old_eti.click_name + "\") == 0\n\
|| " + old_eti.cxx_name + "::is_a(n));\n", ""));
|| " + parent_cxx_name + "::is_a(n));\n", ""));
// placeholders for pull_input and push_output
new_cxxc->defun
(CxxFunction("pull_input", false, "inline Packet *",
......@@ -292,7 +314,10 @@ Specializer::create_class(SpecializedClass &spc)
bool any_checked_push = false, any_push = false, any_pull = false;
for (int i = 0; i < old_cxxc->nfunctions(); i++)
if (old_cxxc->should_rewrite(i)) {
CxxFunction &new_fn = new_cxxc->defun(old_cxxc->function(i));
const CxxFunction &old_fn = old_cxxc->function(i);
if (new_cxxc->find(old_fn.name())) // don't add again
continue;
CxxFunction &new_fn = new_cxxc->defun(old_fn);
while (new_fn.replace_expr(ninputs_pat, ninputs_repl)) ;
while (new_fn.replace_expr(noutputs_pat, noutputs_repl)) ;