1
0
Fork 0
mirror of https://github.com/ganelson/inform.git synced 2024-07-03 07:24:58 +03:00
inform7/inter/codegen-module/Chapter 2/Eliminate Redundant Matter.w
2021-04-15 22:42:28 +01:00

144 lines
6.1 KiB
OpenEdge ABL

[CodeGen::Eliminate::] Eliminate Redundant Matter.
To remove (for example) functions which we can prove will never be called or
referred to as values.
@h Pipeline stage.
Experience shows that around 20 per cent of the code generated by Inform 7
consists of functions which are never in fact needed. Probably diligent
book-keeping at the generation stage could reduce some of that, but other
redundancies arise because of functions assimilated from the template, and
we don't have much control over that. This stage removes everything that
isn't used.
=
void CodeGen::Eliminate::create_pipeline_stage(void) {
CodeGen::Stage::new(I"eliminate-redundant-code",
CodeGen::Eliminate::run_pipeline_stage, NO_STAGE_ARG, FALSE);
}
int CodeGen::Eliminate::run_pipeline_stage(pipeline_step *step) {
inter_tree *I = step->repository;
InterTree::traverse(I, CodeGen::Eliminate::package_preserver, NULL, NULL, PACKAGE_IST);
InterTree::traverse(I, CodeGen::Eliminate::package_destroyer, NULL, NULL, PACKAGE_IST);
return TRUE;
}
@h Preservation.
We operate on a presumption of guilt: any package which we cannot prove is
needed will be deleted. |Main_fn| is clearly needed, since it is where
execution will begin. A handful of other functions, not called yet but
needed in the final compilation stage (and which replace stubs which would
otherwise be provided by the veneer), must also be included. Command
packages contain grammar for command verbs typed at run-time: these affect
an essential data structure (i.e., the parser grammar) implicitly, and so
we can't detect the dependency here. So we require all command packages to
be included.
=
void CodeGen::Eliminate::package_preserver(inter_tree *I, inter_tree_node *P, void *state) {
inter_package *pack = Inter::Package::defined_by_frame(P);
inter_symbol *ptype = Inter::Packages::type(pack);
if (ptype == command_ptype_symbol)
CodeGen::Eliminate::require(pack, NULL, I"it's a _command package");
else if (ptype == property_ptype_symbol) {
text_stream *N = Inter::Packages::name(pack);
if (Str::eq(N, I"workflag_prop"))
CodeGen::Eliminate::require(pack, NULL, I"it's workflag");
if (Str::eq(N, I"pluralname_prop"))
CodeGen::Eliminate::require(pack, NULL, I"it's pluralname");
if (Str::eq(N, I"ambigpluralname_prop"))
CodeGen::Eliminate::require(pack, NULL, I"it's ambigpluralname");
if (Str::eq(N, I"proper_prop"))
CodeGen::Eliminate::require(pack, NULL, I"it's proper");
}
else if (ptype == plain_ptype_symbol) {
text_stream *N = Inter::Packages::name(pack);
if (Str::eq(N, I"attributed_property_offsets_arr"))
CodeGen::Eliminate::require(pack, NULL, I"it's attributed_property_offsets_arr");
if (Str::eq(N, I"valued_property_offsets_arr"))
CodeGen::Eliminate::require(pack, NULL, I"it's valued_property_offsets_arr");
if (Str::eq(N, I"FBNA_PROP_NUMBER_con"))
CodeGen::Eliminate::require(pack, NULL, I"it's FBNA_PROP_NUMBER");
}
else if (ptype == function_ptype_symbol) {
text_stream *N = Inter::Packages::name(pack);
if (Str::eq(N, I"Main_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's Main");
else if (Str::eq(N, I"DefArt_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's a veneer replacement");
else if (Str::eq(N, I"CDefArt_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's a veneer replacement");
else if (Str::eq(N, I"IndefArt_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's a veneer replacement");
else if (Str::eq(N, I"CIndefArt_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's a veneer replacement");
else if (Str::eq(N, I"EnglishNumber_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's a veneer replacement");
else if (Str::eq(N, I"DebugAttribute_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's DebugAttribute");
else if (Str::eq(N, I"TestScriptSub_fn"))
CodeGen::Eliminate::require(pack, NULL, I"it's TestScriptSub");
}
}
@ Once you need a package, what else do you need?
=
void CodeGen::Eliminate::require(inter_package *pack, inter_package *witness, text_stream *reason) {
if ((pack->package_flags) & USED_PACKAGE_FLAG) return;
pack->package_flags |= USED_PACKAGE_FLAG;
if (witness) {
LOGIF(ELIMINATION, "Need $6 because of $6 (because %S)\n", pack, witness, reason);
} else {
LOGIF(ELIMINATION, "Need $6 (because %S)\n", pack, reason);
}
@<If you need a package, you need its parent@>;
@<If you need a package, you need its external dependencies@>;
@<If you need a function or action, you need its internal resources@>;
}
@<If you need a package, you need its parent@> =
inter_package *parent = Inter::Packages::parent(pack);
if (parent) CodeGen::Eliminate::require(parent, pack, I"it's the parent");
@<If you need a package, you need its external dependencies@> =
inter_symbols_table *tab = Inter::Packages::scope(pack);
for (int i=0; i<tab->size; i++) {
inter_symbol *symb = tab->symbol_array[i];
if ((symb) && (symb->equated_to)) {
inter_symbol *to = symb;
while ((to) && (to->equated_to)) to = to->equated_to;
inter_package *needed = to->owning_table->owning_package;
CodeGen::Eliminate::require(needed, pack, I"it's an external symbol");
}
}
@<If you need a function or action, you need its internal resources@> =
text_stream *rationale = NULL;
inter_symbol *ptype = Inter::Packages::type(pack);
if (ptype == function_ptype_symbol) rationale = I"it's a _function block";
if (ptype == action_ptype_symbol) rationale = I"it's an _action subpackage";
if (rationale) {
inter_tree_node *D = Inter::Packages::definition(pack);
LOOP_THROUGH_INTER_CHILDREN(C, D) {
if (C->W.data[ID_IFLD] == PACKAGE_IST) {
inter_package *P = Inter::Package::defined_by_frame(C);
CodeGen::Eliminate::require(P, pack, rationale);
}
}
}
@h Destruction.
Whatever has not been preserved, is now destroyed.
=
void CodeGen::Eliminate::package_destroyer(inter_tree *I, inter_tree_node *P, void *state) {
inter_package *pack = Inter::Package::defined_by_frame(P);
if ((pack) && ((pack->package_flags & USED_PACKAGE_FLAG) == 0)) {
LOGIF(ELIMINATION, "Striking unused package $6 (type %S)\n",
pack, Inter::Packages::type(pack)->symbol_name);
InterTree::remove_node(P);
}
}