mittens 0.3.4 → 0.3.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +4 -0
- data/LICENSE.txt +1 -1
- data/README.md +1 -1
- data/lib/mittens/version.rb +1 -1
- data/vendor/snowball/.github/workflows/ci.yml +43 -3
- data/vendor/snowball/.github/workflows/coverage.yml +8 -5
- data/vendor/snowball/GNUmakefile +46 -16
- data/vendor/snowball/NEWS +437 -13
- data/vendor/snowball/README.rst +4 -4
- data/vendor/snowball/algorithms/earlymodernenglish.sbl +301 -0
- data/vendor/snowball/algorithms/french.sbl +1 -1
- data/vendor/snowball/algorithms/ukrainian.sbl +1251 -0
- data/vendor/snowball/compiler/analyser.c +460 -156
- data/vendor/snowball/compiler/driver.c +41 -36
- data/vendor/snowball/compiler/generator.c +24 -1
- data/vendor/snowball/compiler/generator_ada.c +46 -45
- data/vendor/snowball/compiler/generator_c.c +1094 -187
- data/vendor/snowball/compiler/generator_csharp.c +34 -11
- data/vendor/snowball/compiler/generator_dart.c +39 -11
- data/vendor/snowball/compiler/generator_go.c +6 -6
- data/vendor/snowball/compiler/generator_java.c +29 -8
- data/vendor/snowball/compiler/generator_js.c +65 -38
- data/vendor/snowball/compiler/generator_pascal.c +69 -26
- data/vendor/snowball/compiler/generator_php.c +33 -12
- data/vendor/snowball/compiler/generator_python.c +23 -4
- data/vendor/snowball/compiler/generator_rust.c +37 -13
- data/vendor/snowball/compiler/generator_zig.c +6 -6
- data/vendor/snowball/compiler/header.h +85 -14
- data/vendor/snowball/compiler/space.c +67 -38
- data/vendor/snowball/compiler/tokeniser.c +28 -16
- data/vendor/snowball/csharp/Snowball/AssemblyInfo.cs +2 -2
- data/vendor/snowball/csharp/Snowball/Stemmer.cs +1 -4
- data/vendor/snowball/cxx/generate_algorithms.pl +5 -4
- data/vendor/snowball/cxx/{utilities.cxx → snowball_runtime.cxx} +1 -1
- data/vendor/snowball/cxx/stemmer.h +4 -0
- data/vendor/snowball/cxx/stemwords.cxx +36 -6
- data/vendor/snowball/dart/lib/src/snowball.dart +2 -2
- data/vendor/snowball/dart/pubspec.yaml +1 -1
- data/vendor/snowball/doc/libstemmer_c_README +3 -0
- data/vendor/snowball/doc/libstemmer_php_README +1 -1
- data/vendor/snowball/doc/libstemmer_python_README +8 -6
- data/vendor/snowball/examples/stemwords.c +4 -14
- data/vendor/snowball/go/env.go +16 -4
- data/vendor/snowball/include/libstemmer.h +0 -3
- data/vendor/snowball/java/org/tartarus/snowball/SnowballProgram.java +4 -4
- data/vendor/snowball/javascript/base-stemmer.js +27 -40
- data/vendor/snowball/javascript/stemwords.js +34 -10
- data/vendor/snowball/libstemmer/libstemmer_c.in +5 -5
- data/vendor/snowball/libstemmer/mkmodules.pl +5 -3
- data/vendor/snowball/libstemmer/modules.txt +4 -2
- data/vendor/snowball/pascal/SnowballProgram.pas +2 -2
- data/vendor/snowball/php/base-stemmer.php +2 -2
- data/vendor/snowball/python/setup.py +5 -1
- data/vendor/snowball/python/snowballstemmer/basestemmer.py +1 -2
- data/vendor/snowball/runtime/api.c +14 -3
- data/vendor/snowball/runtime/api.h +13 -9
- data/vendor/snowball/runtime/{utilities.c → snowball_runtime.c} +115 -181
- data/vendor/snowball/runtime/snowball_runtime.h +35 -24
- data/vendor/snowball/rust/src/snowball/snowball_env.rs +1 -1
- data/vendor/snowball/tests/compilertest +3 -3
- data/vendor/snowball/tests/runtime/among.sbl +96 -2
- data/vendor/snowball/tests/runtime/attachinsert.sbl +4 -0
- data/vendor/snowball/tests/runtime/debug.out +7 -0
- data/vendor/snowball/tests/runtime/debug.sbl +9 -0
- data/vendor/snowball/tests/runtime/integertests.sbl +22 -0
- data/vendor/snowball/tests/runtime/marklimit.sbl +30 -0
- data/vendor/snowball/tests/runtime/naming.sbl +20 -0
- data/vendor/snowball/tests/runtime/reverse.sbl +11 -0
- data/vendor/snowball/tests/stemtest.c +2 -5
- data/vendor/snowball/tests/syntax/aeslicefrom.sbl +7 -0
- data/vendor/snowball/tests/syntax/canon.sbl +14 -0
- data/vendor/snowball/tests/syntax/canon.syntax +36 -0
- data/vendor/snowball/tests/syntax/inline.syntax +4 -4
- data/vendor/snowball/tests/syntax/localvar.sbl +38 -0
- data/vendor/snowball/tests/syntax/localvar.stderr +3 -0
- data/vendor/snowball/tests/syntax/localvar.syntax +76 -0
- data/vendor/snowball/tests/syntax/unused.sbl +5 -1
- data/vendor/snowball/tests/syntax/unused.stderr +3 -1
- data/vendor/snowball/zig/env.zig +24 -4
- metadata +14 -4
|
@@ -36,6 +36,20 @@ extern void print_program(struct analyser * a) {
|
|
|
36
36
|
if (a->program) print_node_(a->program, 0, "");
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
static void free_among(struct among * q) {
|
|
40
|
+
FREE(q->v);
|
|
41
|
+
FREE(q->commands);
|
|
42
|
+
FREE(q->af);
|
|
43
|
+
lose_b(q->table);
|
|
44
|
+
lose_s(q->table_endianness);
|
|
45
|
+
while (q->subtree) {
|
|
46
|
+
struct among_subtree * t = q->subtree;
|
|
47
|
+
q->subtree = q->subtree->next;
|
|
48
|
+
FREE(t);
|
|
49
|
+
}
|
|
50
|
+
FREE(q);
|
|
51
|
+
}
|
|
52
|
+
|
|
39
53
|
static struct node * new_node_at_line(struct analyser * a, int type, int line) {
|
|
40
54
|
NEW(node, p);
|
|
41
55
|
*p = (struct node){0};
|
|
@@ -309,6 +323,12 @@ static int read_AE_test(struct analyser * a) {
|
|
|
309
323
|
case c_lt:
|
|
310
324
|
case c_le:
|
|
311
325
|
return t->token;
|
|
326
|
+
case c_slicefrom:
|
|
327
|
+
// In an AE c_slicefrom (`<-`) is not a valid token, so convert
|
|
328
|
+
// it into c_lt (`<`) then c_minus (`-`) so that code such as
|
|
329
|
+
// `$x<-1` works rather than giving a syntax error.
|
|
330
|
+
push_token(t, c_minus);
|
|
331
|
+
return c_lt;
|
|
312
332
|
default:
|
|
313
333
|
unexpected_token_error(a, "integer test expression");
|
|
314
334
|
hold_token(t);
|
|
@@ -736,9 +756,11 @@ static int nodes_equivalent_(const struct node *p, const struct node *q) {
|
|
|
736
756
|
return nodes_equivalent(p->right, q->right);
|
|
737
757
|
}
|
|
738
758
|
|
|
739
|
-
static struct node * make_among(struct analyser * a, struct node * p,
|
|
759
|
+
static struct node * make_among(struct analyser * a, struct node * p,
|
|
760
|
+
int literalstring_count,
|
|
761
|
+
struct node * substring) {
|
|
740
762
|
NEW(among, x);
|
|
741
|
-
NEWVEC(amongvec, v,
|
|
763
|
+
NEWVEC(amongvec, v, literalstring_count);
|
|
742
764
|
struct node * q = p->left;
|
|
743
765
|
struct node * starter = NULL;
|
|
744
766
|
struct amongvec * w0 = v;
|
|
@@ -750,9 +772,8 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
750
772
|
|
|
751
773
|
*x = (struct among){0};
|
|
752
774
|
x->node = p;
|
|
753
|
-
x->
|
|
775
|
+
x->v = v;
|
|
754
776
|
x->shortest_size = INT_MAX;
|
|
755
|
-
x->in_routine = a->current_routine;
|
|
756
777
|
|
|
757
778
|
if (q->type == c_bra) {
|
|
758
779
|
fprintf(stderr,
|
|
@@ -780,8 +801,9 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
780
801
|
++function->uses_in_among;
|
|
781
802
|
check_routine_mode(a, function, direction);
|
|
782
803
|
if (function->among_index == 0) {
|
|
783
|
-
function->among_index = ++x->
|
|
804
|
+
function->among_index = ++x->unique_function_count;
|
|
784
805
|
}
|
|
806
|
+
++x->function_count;
|
|
785
807
|
w1->function_index = function->among_index;
|
|
786
808
|
} else {
|
|
787
809
|
w1->function = NULL;
|
|
@@ -826,7 +848,10 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
826
848
|
}
|
|
827
849
|
q = q->right;
|
|
828
850
|
}
|
|
829
|
-
if (w1-v !=
|
|
851
|
+
if (w1-v != literalstring_count) {
|
|
852
|
+
fprintf(stderr, "oh! %d %d\n", (int)(w1-v), literalstring_count);
|
|
853
|
+
exit(1);
|
|
854
|
+
}
|
|
830
855
|
x->command_count = result - 1;
|
|
831
856
|
{
|
|
832
857
|
NEWVEC(node*, commands, x->command_count);
|
|
@@ -881,7 +906,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
881
906
|
|
|
882
907
|
for (w = w0 - 1; w >= v; w--) {
|
|
883
908
|
if (w->size < size && memcmp(w->b, b, w->size * sizeof(symbol)) == 0) {
|
|
884
|
-
w0->i = w - v; /* fill in index of longest substring */
|
|
909
|
+
w0->i = (int)(w - v); /* fill in index of longest substring */
|
|
885
910
|
break;
|
|
886
911
|
}
|
|
887
912
|
}
|
|
@@ -901,7 +926,7 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
901
926
|
a->tokeniser->file, w0->line_number);
|
|
902
927
|
}
|
|
903
928
|
|
|
904
|
-
x->literalstring_count =
|
|
929
|
+
x->literalstring_count = literalstring_count;
|
|
905
930
|
p->among = x;
|
|
906
931
|
|
|
907
932
|
if (starter) {
|
|
@@ -1010,14 +1035,15 @@ static struct node * make_among(struct analyser * a, struct node * p, struct nod
|
|
|
1010
1035
|
p = and_node;
|
|
1011
1036
|
--v[0].function->uses_in_among;
|
|
1012
1037
|
}
|
|
1013
|
-
|
|
1014
|
-
FREE(x);
|
|
1015
|
-
FREE(v);
|
|
1038
|
+
free_among(x);
|
|
1016
1039
|
return p;
|
|
1017
1040
|
}
|
|
1018
1041
|
|
|
1019
|
-
if (
|
|
1020
|
-
|
|
1042
|
+
if (a->current_routine) {
|
|
1043
|
+
a->current_routine->has_among = true;
|
|
1044
|
+
if (x->function_count) {
|
|
1045
|
+
a->current_routine->has_among_function = true;
|
|
1046
|
+
}
|
|
1021
1047
|
}
|
|
1022
1048
|
|
|
1023
1049
|
x->substring = substring;
|
|
@@ -1037,7 +1063,7 @@ static struct node * read_among(struct analyser * a) {
|
|
|
1037
1063
|
struct node * substring = a->substring;
|
|
1038
1064
|
|
|
1039
1065
|
a->substring = NULL;
|
|
1040
|
-
|
|
1066
|
+
int literalstring_count = 0;
|
|
1041
1067
|
if (!get_token(a, c_bra)) return p;
|
|
1042
1068
|
while (true) {
|
|
1043
1069
|
struct node * q;
|
|
@@ -1052,7 +1078,7 @@ static struct node * read_among(struct analyser * a) {
|
|
|
1052
1078
|
} else {
|
|
1053
1079
|
hold_token(t);
|
|
1054
1080
|
}
|
|
1055
|
-
|
|
1081
|
+
literalstring_count++;
|
|
1056
1082
|
break;
|
|
1057
1083
|
case c_bra:
|
|
1058
1084
|
if (previous_token == c_bra) {
|
|
@@ -1072,11 +1098,13 @@ static struct node * read_among(struct analyser * a) {
|
|
|
1072
1098
|
previous_token = token;
|
|
1073
1099
|
continue;
|
|
1074
1100
|
case c_ket:
|
|
1075
|
-
if (
|
|
1101
|
+
if (literalstring_count == 0) {
|
|
1076
1102
|
report_error_location(a);
|
|
1077
1103
|
fprintf(stderr, "empty among(...)\n");
|
|
1078
1104
|
}
|
|
1079
|
-
if (t->error_count == 0)
|
|
1105
|
+
if (t->error_count == 0) {
|
|
1106
|
+
p = make_among(a, p, literalstring_count, substring);
|
|
1107
|
+
}
|
|
1080
1108
|
return p;
|
|
1081
1109
|
}
|
|
1082
1110
|
previous_token = token;
|
|
@@ -1125,6 +1153,41 @@ static int ae_uses_name(struct node * p, struct name * q) {
|
|
|
1125
1153
|
return 0;
|
|
1126
1154
|
}
|
|
1127
1155
|
|
|
1156
|
+
// Put integer tests nodes into a normalised form, so that we can merge
|
|
1157
|
+
// among actions with equivalent integer tests - e.g. from arabic.sbl:
|
|
1158
|
+
//
|
|
1159
|
+
// [substring] among (
|
|
1160
|
+
// // [...]
|
|
1161
|
+
// '{a}{n}' '{w}{n}' '{y}{n}' ($(len > 5) delete) // present
|
|
1162
|
+
// '{t}{m}{a}' ($(len >= 6) delete)
|
|
1163
|
+
// )
|
|
1164
|
+
static void normalise_comparison(struct node * n) {
|
|
1165
|
+
if (!n->left || !n->AE) return;
|
|
1166
|
+
if (n->left->type == c_number && n->AE->type != c_number) {
|
|
1167
|
+
// Swap operands so a number goes on the RHS.
|
|
1168
|
+
switch (n->type) {
|
|
1169
|
+
case c_gt: n->type = c_lt; break;
|
|
1170
|
+
case c_lt: n->type = c_gt; break;
|
|
1171
|
+
case c_ge: n->type = c_le; break;
|
|
1172
|
+
case c_le: n->type = c_ge; break;
|
|
1173
|
+
}
|
|
1174
|
+
struct node * p = n->left;
|
|
1175
|
+
n->left = n->AE;
|
|
1176
|
+
n->AE = p;
|
|
1177
|
+
}
|
|
1178
|
+
if (n->AE->type == c_number && abs(n->AE->number) < 32767) {
|
|
1179
|
+
if (n->type == c_gt) {
|
|
1180
|
+
// Normalise `$(n > 0)` to `$(n >= 1)`.
|
|
1181
|
+
n->type = c_ge;
|
|
1182
|
+
++n->AE->number;
|
|
1183
|
+
} else if (n->type == c_lt) {
|
|
1184
|
+
// Normalise `$(n < 1)` to `$(n <= 0)`
|
|
1185
|
+
n->type = c_le;
|
|
1186
|
+
--n->AE->number;
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
}
|
|
1190
|
+
|
|
1128
1191
|
static struct node * read_C(struct analyser * a) {
|
|
1129
1192
|
struct tokeniser * t = a->tokeniser;
|
|
1130
1193
|
int token = read_token(t);
|
|
@@ -1401,7 +1464,6 @@ static struct node * read_C(struct analyser * a) {
|
|
|
1401
1464
|
return new_node(a, token);
|
|
1402
1465
|
case c_assignto:
|
|
1403
1466
|
case c_sliceto: {
|
|
1404
|
-
check_modifyable(a);
|
|
1405
1467
|
struct node * n = new_node(a, token);
|
|
1406
1468
|
if (get_token(a, c_name)) {
|
|
1407
1469
|
name_to_node(a, n, t_string);
|
|
@@ -1501,6 +1563,14 @@ report_assumed_rel_op_error:
|
|
|
1501
1563
|
// Assume `==` was meant to try to avoid an error avalanche.
|
|
1502
1564
|
token = c_eq;
|
|
1503
1565
|
goto handle_rel_op;
|
|
1566
|
+
case c_slicefrom:
|
|
1567
|
+
// In an AE c_slicefrom (`<-`) is not a valid token, so
|
|
1568
|
+
// convert it into c_lt (`<`) then c_minus (`-`) so
|
|
1569
|
+
// that code such as `$(x<-1)` works rather than giving
|
|
1570
|
+
// a syntax error.
|
|
1571
|
+
push_token(t, c_minus);
|
|
1572
|
+
token = c_lt;
|
|
1573
|
+
// FALLTHRU
|
|
1504
1574
|
case c_eq:
|
|
1505
1575
|
case c_ne:
|
|
1506
1576
|
case c_gt:
|
|
@@ -1548,6 +1618,7 @@ handle_rel_op: ;
|
|
|
1548
1618
|
n = new_node(a, token);
|
|
1549
1619
|
n->left = lhs;
|
|
1550
1620
|
n->AE = rhs;
|
|
1621
|
+
normalise_comparison(n);
|
|
1551
1622
|
}
|
|
1552
1623
|
get_token(a, c_ket);
|
|
1553
1624
|
break;
|
|
@@ -1612,6 +1683,7 @@ handle_rel_op: ;
|
|
|
1612
1683
|
p->left = new_node(a, c_name);
|
|
1613
1684
|
p->left->name = q;
|
|
1614
1685
|
p->AE = read_AE(a, NULL, 0);
|
|
1686
|
+
normalise_comparison(p);
|
|
1615
1687
|
if (q) {
|
|
1616
1688
|
q->value_used = true;
|
|
1617
1689
|
mark_used_in(a, q, p);
|
|
@@ -1755,7 +1827,7 @@ handle_rel_op: ;
|
|
|
1755
1827
|
}
|
|
1756
1828
|
}
|
|
1757
1829
|
|
|
1758
|
-
static int next_symbol(symbol * p, symbol * W, int utf8) {
|
|
1830
|
+
static int next_symbol(const symbol * p, symbol * W, int utf8) {
|
|
1759
1831
|
if (utf8) {
|
|
1760
1832
|
int ch;
|
|
1761
1833
|
int j = get_utf8(p, & ch);
|
|
@@ -1767,7 +1839,7 @@ static int next_symbol(symbol * p, symbol * W, int utf8) {
|
|
|
1767
1839
|
}
|
|
1768
1840
|
}
|
|
1769
1841
|
|
|
1770
|
-
static symbol * alter_grouping(symbol * p, symbol * q, int style, int utf8) {
|
|
1842
|
+
static symbol * alter_grouping(symbol * p, const symbol * q, int style, int utf8) {
|
|
1771
1843
|
int j = 0;
|
|
1772
1844
|
if (style == c_plus) {
|
|
1773
1845
|
while (j < SIZE(q)) {
|
|
@@ -2045,15 +2117,21 @@ static void remove_dead_assignments(struct node * p, struct name * q) {
|
|
|
2045
2117
|
if (p->right) remove_dead_assignments(p->right, q);
|
|
2046
2118
|
}
|
|
2047
2119
|
|
|
2120
|
+
// Numeric values are picked so we can take the minimum to merge when joining
|
|
2121
|
+
// control flows.
|
|
2048
2122
|
enum {
|
|
2049
2123
|
// Not set on at least one code path leading to a use.
|
|
2050
|
-
USE_BEFORE_SET,
|
|
2124
|
+
USE_BEFORE_SET = 0,
|
|
2051
2125
|
// Need to keep checking.
|
|
2052
|
-
UNKNOWN,
|
|
2126
|
+
UNKNOWN = 1,
|
|
2053
2127
|
// Set on any code path leading to a use.
|
|
2054
|
-
SET_BEFORE_ANY_USE
|
|
2128
|
+
SET_BEFORE_ANY_USE = 2
|
|
2055
2129
|
};
|
|
2056
2130
|
|
|
2131
|
+
static inline int merge_r(int r1, int r2) {
|
|
2132
|
+
return r1 < r2 ? r1 : r2;
|
|
2133
|
+
}
|
|
2134
|
+
|
|
2057
2135
|
/* Find out if every codepath in the command with node p to a use of variable v
|
|
2058
2136
|
* sets v first.
|
|
2059
2137
|
*
|
|
@@ -2061,31 +2139,28 @@ enum {
|
|
|
2061
2139
|
* safely localised when it can, but they allow localising all variables which
|
|
2062
2140
|
* can be trivially made local in existing stemmers.
|
|
2063
2141
|
*
|
|
2064
|
-
* p:
|
|
2065
|
-
*
|
|
2066
|
-
*
|
|
2142
|
+
* p: the node of the command to check.
|
|
2143
|
+
* v: the variable to check.
|
|
2144
|
+
* catcher: pointer to USE_BEFORE_SET/UNKNOWN/SET_BEFORE_ANY_USE value to
|
|
2145
|
+
* update on signal `f`.
|
|
2067
2146
|
*/
|
|
2068
|
-
static int always_set_before_use_(struct node * p,
|
|
2069
|
-
struct name * v
|
|
2147
|
+
static int always_set_before_use_(struct node * p,
|
|
2148
|
+
struct name * v,
|
|
2149
|
+
int * catcher) {
|
|
2070
2150
|
assert(p);
|
|
2071
2151
|
switch (p->type) {
|
|
2072
2152
|
case c_call: {
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
* the nested call won't be reflected after it returns.
|
|
2078
|
-
*/
|
|
2079
|
-
return USE_BEFORE_SET;
|
|
2080
|
-
}
|
|
2081
|
-
// We know v is only referenced in the function we are checking.
|
|
2153
|
+
// We know v is only referenced in the function we are checking
|
|
2154
|
+
// (and we don't try to localise variables into recursive
|
|
2155
|
+
// functions, so we know this call can't re-enter the current
|
|
2156
|
+
// function).
|
|
2082
2157
|
return UNKNOWN;
|
|
2083
2158
|
}
|
|
2084
2159
|
case c_among: {
|
|
2085
2160
|
bool all_pass = true;
|
|
2086
2161
|
struct among * x = p->among;
|
|
2087
2162
|
for (int i = 1; i <= x->command_count; i++) {
|
|
2088
|
-
int r = always_set_before_use_(x->commands[i - 1],
|
|
2163
|
+
int r = always_set_before_use_(x->commands[i - 1], v, catcher);
|
|
2089
2164
|
if (r == USE_BEFORE_SET) return r;
|
|
2090
2165
|
all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
|
|
2091
2166
|
}
|
|
@@ -2094,49 +2169,89 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2094
2169
|
}
|
|
2095
2170
|
case c_or: {
|
|
2096
2171
|
struct node * q = p->left;
|
|
2172
|
+
assert(q);
|
|
2097
2173
|
bool all_pass = true;
|
|
2098
|
-
while (q) {
|
|
2099
|
-
|
|
2174
|
+
while (q->right) {
|
|
2175
|
+
// `or` handles signal `f` from subcommands except the last.
|
|
2176
|
+
int r_f = UNKNOWN;
|
|
2177
|
+
int r = always_set_before_use_(q, v, &r_f);
|
|
2100
2178
|
if (r == USE_BEFORE_SET) return r;
|
|
2101
|
-
|
|
2179
|
+
if (r_f == USE_BEFORE_SET) return r;
|
|
2180
|
+
all_pass = all_pass &&
|
|
2181
|
+
(r == SET_BEFORE_ANY_USE) &&
|
|
2182
|
+
(r_f == SET_BEFORE_ANY_USE);
|
|
2102
2183
|
q = q->right;
|
|
2103
2184
|
}
|
|
2185
|
+
// Signal `f` from the last subcommand propagates.
|
|
2186
|
+
int r = always_set_before_use_(q, v, catcher);
|
|
2187
|
+
if (r == USE_BEFORE_SET) return r;
|
|
2188
|
+
all_pass = all_pass && (r == SET_BEFORE_ANY_USE);
|
|
2104
2189
|
if (all_pass) return SET_BEFORE_ANY_USE;
|
|
2105
2190
|
return UNKNOWN;
|
|
2106
2191
|
}
|
|
2107
2192
|
case c_and:
|
|
2108
2193
|
case c_bra: {
|
|
2109
2194
|
struct node * q = p->left;
|
|
2195
|
+
// If we execute this command, we'll definitely execute
|
|
2196
|
+
// sub-commands up to and including the first which could
|
|
2197
|
+
// fail.
|
|
2198
|
+
while (q) {
|
|
2199
|
+
int r = always_set_before_use_(q, v, catcher);
|
|
2200
|
+
if (r != UNKNOWN) {
|
|
2201
|
+
*catcher = r;
|
|
2202
|
+
return r;
|
|
2203
|
+
}
|
|
2204
|
+
bool last = (q->possible_signals <= 0);
|
|
2205
|
+
q = q->right;
|
|
2206
|
+
if (last) break;
|
|
2207
|
+
}
|
|
2208
|
+
// Any remaining subcommands may not get executed, so we don't
|
|
2209
|
+
// update *catcher for them.
|
|
2110
2210
|
while (q) {
|
|
2111
|
-
int r = always_set_before_use_(q,
|
|
2211
|
+
int r = always_set_before_use_(q, v, catcher);
|
|
2112
2212
|
if (r != UNKNOWN) return r;
|
|
2113
2213
|
q = q->right;
|
|
2114
2214
|
}
|
|
2115
2215
|
return UNKNOWN;
|
|
2116
2216
|
}
|
|
2117
2217
|
case c_backwards:
|
|
2118
|
-
case c_not:
|
|
2119
2218
|
case c_reverse:
|
|
2120
2219
|
case c_test:
|
|
2121
|
-
return always_set_before_use_(p->left,
|
|
2122
|
-
case c_do:
|
|
2220
|
+
return always_set_before_use_(p->left, v, catcher);
|
|
2123
2221
|
case c_fail:
|
|
2222
|
+
return always_set_before_use_(p->left, v, catcher);
|
|
2223
|
+
// FIXME: Could be?
|
|
2224
|
+
// *catcher = merge_r(r, *catcher);
|
|
2225
|
+
// return UNKNOWN;
|
|
2226
|
+
case c_repeat:
|
|
2227
|
+
case c_do:
|
|
2228
|
+
case c_not:
|
|
2229
|
+
case c_try: {
|
|
2230
|
+
int r_f = UNKNOWN;
|
|
2231
|
+
int r = always_set_before_use_(p->left, v, &r_f);
|
|
2232
|
+
return merge_r(r, r_f);
|
|
2233
|
+
}
|
|
2124
2234
|
case c_gopast:
|
|
2125
2235
|
case c_goto:
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2236
|
+
// If the cursor is atlimit already the subcommand won't be
|
|
2237
|
+
// executed, so a SET_BEFORE_ANY_USE from it doesn't guarantee
|
|
2238
|
+
// that the variable will get set.
|
|
2239
|
+
if (always_set_before_use_(p->left, v, catcher) == USE_BEFORE_SET)
|
|
2129
2240
|
return USE_BEFORE_SET;
|
|
2130
2241
|
return UNKNOWN;
|
|
2131
|
-
}
|
|
2132
2242
|
case c_atleast:
|
|
2133
2243
|
case c_loop:
|
|
2134
|
-
if (always_set_before_use_(p->AE,
|
|
2244
|
+
if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
|
|
2135
2245
|
return USE_BEFORE_SET;
|
|
2136
|
-
|
|
2246
|
+
// If AE has value <= 0 the subcommand won't be executed, so a
|
|
2247
|
+
// SET_BEFORE_ANY_USE from it doesn't guarantee that the variable
|
|
2248
|
+
// will get set.
|
|
2249
|
+
if (always_set_before_use_(p->left, v, catcher) == USE_BEFORE_SET)
|
|
2250
|
+
return USE_BEFORE_SET;
|
|
2251
|
+
return UNKNOWN;
|
|
2137
2252
|
case c_assign:
|
|
2138
2253
|
// Check AE first: `x = x + 1` uses `x` before it sets it.
|
|
2139
|
-
if (always_set_before_use_(p->AE,
|
|
2254
|
+
if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
|
|
2140
2255
|
return USE_BEFORE_SET;
|
|
2141
2256
|
if (p->name == v)
|
|
2142
2257
|
return SET_BEFORE_ANY_USE;
|
|
@@ -2167,7 +2282,7 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2167
2282
|
return UNKNOWN;
|
|
2168
2283
|
case c_hop:
|
|
2169
2284
|
case c_tomark:
|
|
2170
|
-
if (always_set_before_use_(p->AE,
|
|
2285
|
+
if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET)
|
|
2171
2286
|
return USE_BEFORE_SET;
|
|
2172
2287
|
return UNKNOWN;
|
|
2173
2288
|
case c_stringassign:
|
|
@@ -2187,9 +2302,9 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2187
2302
|
case c_minus:
|
|
2188
2303
|
case c_multiply:
|
|
2189
2304
|
case c_plus: {
|
|
2190
|
-
int r = always_set_before_use_(p->left,
|
|
2305
|
+
int r = always_set_before_use_(p->left, v, catcher);
|
|
2191
2306
|
if (r != UNKNOWN) return r;
|
|
2192
|
-
return always_set_before_use_(p->right,
|
|
2307
|
+
return always_set_before_use_(p->right, v, catcher);
|
|
2193
2308
|
}
|
|
2194
2309
|
case c_eq:
|
|
2195
2310
|
case c_ne:
|
|
@@ -2197,12 +2312,12 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2197
2312
|
case c_ge:
|
|
2198
2313
|
case c_lt:
|
|
2199
2314
|
case c_le: {
|
|
2200
|
-
int r = always_set_before_use_(p->left,
|
|
2315
|
+
int r = always_set_before_use_(p->left, v, catcher);
|
|
2201
2316
|
if (r != UNKNOWN) return r;
|
|
2202
|
-
return always_set_before_use_(p->AE,
|
|
2317
|
+
return always_set_before_use_(p->AE, v, catcher);
|
|
2203
2318
|
}
|
|
2204
2319
|
case c_neg:
|
|
2205
|
-
return always_set_before_use_(p->right,
|
|
2320
|
+
return always_set_before_use_(p->right, v, catcher);
|
|
2206
2321
|
case c_lenof:
|
|
2207
2322
|
case c_sizeof:
|
|
2208
2323
|
if (p->name == v) {
|
|
@@ -2217,9 +2332,15 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2217
2332
|
case c_size:
|
|
2218
2333
|
return UNKNOWN;
|
|
2219
2334
|
case c_setlimit: {
|
|
2220
|
-
int r = always_set_before_use_(p->aux,
|
|
2335
|
+
int r = always_set_before_use_(p->aux, v, catcher);
|
|
2221
2336
|
if (r != UNKNOWN) return r;
|
|
2222
|
-
|
|
2337
|
+
r = always_set_before_use_(p->left, v, catcher);
|
|
2338
|
+
if (p->aux->possible_signals <= 0) {
|
|
2339
|
+
// aux can fail so left may not be executed.
|
|
2340
|
+
if (r == USE_BEFORE_SET) return r;
|
|
2341
|
+
return UNKNOWN;
|
|
2342
|
+
}
|
|
2343
|
+
return r;
|
|
2223
2344
|
}
|
|
2224
2345
|
case c_divideassign:
|
|
2225
2346
|
case c_minusassign:
|
|
@@ -2228,7 +2349,7 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2228
2349
|
if (p->name == v) {
|
|
2229
2350
|
return USE_BEFORE_SET;
|
|
2230
2351
|
}
|
|
2231
|
-
if (always_set_before_use_(p->AE,
|
|
2352
|
+
if (always_set_before_use_(p->AE, v, catcher) == USE_BEFORE_SET) {
|
|
2232
2353
|
return USE_BEFORE_SET;
|
|
2233
2354
|
}
|
|
2234
2355
|
return UNKNOWN;
|
|
@@ -2289,9 +2410,10 @@ static int always_set_before_use_(struct node * p, struct node * func,
|
|
|
2289
2410
|
return USE_BEFORE_SET;
|
|
2290
2411
|
}
|
|
2291
2412
|
|
|
2292
|
-
static int always_set_before_use(struct node * p, struct
|
|
2293
|
-
|
|
2294
|
-
|
|
2413
|
+
static int always_set_before_use(struct node * p, struct name * v) {
|
|
2414
|
+
int r_f = UNKNOWN;
|
|
2415
|
+
int r = always_set_before_use_(p, v, &r_f);
|
|
2416
|
+
return merge_r(r, r_f) != USE_BEFORE_SET;
|
|
2295
2417
|
}
|
|
2296
2418
|
|
|
2297
2419
|
static void remove_routine(struct analyser * a, struct name * q) {
|
|
@@ -2536,6 +2658,33 @@ static int check_possible_signals(struct analyser * a, struct node * p) {
|
|
|
2536
2658
|
}
|
|
2537
2659
|
}
|
|
2538
2660
|
|
|
2661
|
+
// Can control flow from node `p` reach routine `func`?
|
|
2662
|
+
static bool recursion_check(struct node * p, struct name * func) {
|
|
2663
|
+
while (p) {
|
|
2664
|
+
switch (p->type) {
|
|
2665
|
+
case c_call:
|
|
2666
|
+
if (p->name == func) return true;
|
|
2667
|
+
if (p->name->definition && !p->name->visited) {
|
|
2668
|
+
p->name->visited = true;
|
|
2669
|
+
if (recursion_check(p->name->definition->left, func)) return true;
|
|
2670
|
+
}
|
|
2671
|
+
break;
|
|
2672
|
+
case c_among: {
|
|
2673
|
+
struct among * x = p->among;
|
|
2674
|
+
if (x->unique_function_count == 0) break;
|
|
2675
|
+
for (int i = 0; i < x->literalstring_count; ++i) {
|
|
2676
|
+
if (x->v[i].function == func) return true;
|
|
2677
|
+
}
|
|
2678
|
+
break;
|
|
2679
|
+
}
|
|
2680
|
+
}
|
|
2681
|
+
if (p->left && recursion_check(p->left, func)) return true;
|
|
2682
|
+
if (p->aux && recursion_check(p->aux, func)) return true;
|
|
2683
|
+
p = p->right;
|
|
2684
|
+
}
|
|
2685
|
+
return false;
|
|
2686
|
+
}
|
|
2687
|
+
|
|
2539
2688
|
static void visit_routine(struct analyser * a, struct name * n);
|
|
2540
2689
|
|
|
2541
2690
|
static void visit_node(struct analyser * a, struct node * p, struct name * func) {
|
|
@@ -2559,8 +2708,8 @@ static void visit_node(struct analyser * a, struct node * p, struct name * func)
|
|
|
2559
2708
|
struct among * x = p->among;
|
|
2560
2709
|
x->used = true;
|
|
2561
2710
|
for (int i = 0; i < x->literalstring_count; ++i) {
|
|
2562
|
-
if (x->
|
|
2563
|
-
visit_routine(a, x->
|
|
2711
|
+
if (x->v[i].function)
|
|
2712
|
+
visit_routine(a, x->v[i].function);
|
|
2564
2713
|
}
|
|
2565
2714
|
for (int i = 0; i < x->command_count; ++i) {
|
|
2566
2715
|
visit_node(a, x->commands[i], func);
|
|
@@ -2636,6 +2785,68 @@ static void visit_routine(struct analyser * a, struct name * n) {
|
|
|
2636
2785
|
p->possible_signals = p->left->possible_signals;
|
|
2637
2786
|
}
|
|
2638
2787
|
|
|
2788
|
+
static struct node * clone_node(struct analyser * a, struct node * n) {
|
|
2789
|
+
if (n == NULL) return n;
|
|
2790
|
+
|
|
2791
|
+
NEW(node, p);
|
|
2792
|
+
p->among = n->among;
|
|
2793
|
+
p->type = n->type;
|
|
2794
|
+
p->mode = n->mode;
|
|
2795
|
+
p->fixed_constant = n->fixed_constant;
|
|
2796
|
+
p->possible_signals = n->possible_signals;
|
|
2797
|
+
p->name = n->name;
|
|
2798
|
+
p->literalstring = n->literalstring;
|
|
2799
|
+
p->number = n->number;
|
|
2800
|
+
p->line_number = n->line_number;
|
|
2801
|
+
|
|
2802
|
+
p->left = clone_node(a, n->left);
|
|
2803
|
+
p->aux = clone_node(a, n->aux);
|
|
2804
|
+
p->right = clone_node(a, n->right);
|
|
2805
|
+
p->AE = clone_node(a, n->AE);
|
|
2806
|
+
|
|
2807
|
+
p->next = a->nodes;
|
|
2808
|
+
a->nodes = p;
|
|
2809
|
+
return p;
|
|
2810
|
+
}
|
|
2811
|
+
|
|
2812
|
+
// Inline any calls to routine `n` in the code starting at `p`.
|
|
2813
|
+
//
|
|
2814
|
+
// Returns true if any calls were inlined. If there are no remaining calls to
|
|
2815
|
+
// `n` anywhere in the program then n->definition will be set to NULL.
|
|
2816
|
+
static bool inline_calls(struct analyser * a,
|
|
2817
|
+
struct node * p,
|
|
2818
|
+
struct name * n) {
|
|
2819
|
+
bool r = false;
|
|
2820
|
+
while (p) {
|
|
2821
|
+
if (p->name == n && p->type == c_call) {
|
|
2822
|
+
p->type = c_bra;
|
|
2823
|
+
p->name = NULL;
|
|
2824
|
+
--n->references;
|
|
2825
|
+
if (n->references < 2) {
|
|
2826
|
+
// No more uses of n, so there is no need to clone the code.
|
|
2827
|
+
// We can also stop looking for uses of n to inline.
|
|
2828
|
+
p->left = n->definition->left;
|
|
2829
|
+
n->definition = NULL;
|
|
2830
|
+
return true;
|
|
2831
|
+
}
|
|
2832
|
+
p->left = clone_node(a, n->definition->left);
|
|
2833
|
+
p = p->right;
|
|
2834
|
+
r = true;
|
|
2835
|
+
continue;
|
|
2836
|
+
}
|
|
2837
|
+
if (inline_calls(a, p->left, n)) {
|
|
2838
|
+
if (!n->definition) return true;
|
|
2839
|
+
r = true;
|
|
2840
|
+
}
|
|
2841
|
+
if (inline_calls(a, p->aux, n)) {
|
|
2842
|
+
if (!n->definition) return true;
|
|
2843
|
+
r = true;
|
|
2844
|
+
}
|
|
2845
|
+
p = p->right;
|
|
2846
|
+
}
|
|
2847
|
+
return r;
|
|
2848
|
+
}
|
|
2849
|
+
|
|
2639
2850
|
extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
2640
2851
|
read_program_(a, -1);
|
|
2641
2852
|
for (struct name * q = a->names; q; q = q->next) {
|
|
@@ -2667,62 +2878,66 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2667
2878
|
|
|
2668
2879
|
for (struct name ** n_ptr = &(a->names); *n_ptr; ) {
|
|
2669
2880
|
struct name * n = *n_ptr;
|
|
2670
|
-
// Inline some routines.
|
|
2671
|
-
//
|
|
2672
|
-
// Currently we inline routines which are "simple" and only used once.
|
|
2673
|
-
// Probably we should inline "simple" *OR* only used once, but to do
|
|
2674
|
-
// that we probably need to clone the nodes for routines used more
|
|
2675
|
-
// than once, and we need to handle updating things like
|
|
2676
|
-
// amongvar_needed (which hasn't been set at this point).
|
|
2677
|
-
//
|
|
2678
|
-
// Note: the definition of a routine is counted as a reference to its
|
|
2679
|
-
// name, so a routine only called once has 2 references.
|
|
2680
2881
|
if (n->type == t_routine &&
|
|
2681
|
-
n->references
|
|
2682
|
-
n->uses_in_among == 0 &&
|
|
2882
|
+
n->references >= 2 &&
|
|
2683
2883
|
!n->definition->left->right) {
|
|
2684
2884
|
bool inline_routine = false;
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
|
|
2688
|
-
|
|
2689
|
-
|
|
2690
|
-
|
|
2691
|
-
|
|
2692
|
-
|
|
2693
|
-
|
|
2694
|
-
|
|
2695
|
-
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
|
|
2699
|
-
|
|
2700
|
-
|
|
2701
|
-
|
|
2702
|
-
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
|
|
2706
|
-
|
|
2707
|
-
|
|
2708
|
-
|
|
2709
|
-
|
|
2710
|
-
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2885
|
+
if (n->references == 2) {
|
|
2886
|
+
// Inline any routine only called once (where that call is not
|
|
2887
|
+
// as an among function).
|
|
2888
|
+
//
|
|
2889
|
+
// Note: the definition of a routine is counted as a reference
|
|
2890
|
+
// to its name, so a routine only called once has 2 references.
|
|
2891
|
+
inline_routine = (n->uses_in_among == 0);
|
|
2892
|
+
} else {
|
|
2893
|
+
// Inline any calls to routines which is simple enough that the
|
|
2894
|
+
// generated code to call the routine is likely to be a similar
|
|
2895
|
+
// size to the inlined code that replaces it.
|
|
2896
|
+
//
|
|
2897
|
+
// Currently we inline any non-compound command (including
|
|
2898
|
+
// non-compound commands we synthesise, such as `goto` applied
|
|
2899
|
+
// to a grouping).
|
|
2900
|
+
switch (n->definition->left->type) {
|
|
2901
|
+
case c_eq:
|
|
2902
|
+
case c_ge:
|
|
2903
|
+
case c_gt:
|
|
2904
|
+
case c_le:
|
|
2905
|
+
case c_lt:
|
|
2906
|
+
case c_ne:
|
|
2907
|
+
case c_booltest:
|
|
2908
|
+
case c_not_booltest:
|
|
2909
|
+
case c_grouping:
|
|
2910
|
+
case c_non:
|
|
2911
|
+
case c_goto_grouping:
|
|
2912
|
+
case c_gopast_grouping:
|
|
2913
|
+
case c_goto_non:
|
|
2914
|
+
case c_gopast_non:
|
|
2915
|
+
case c_literalstring:
|
|
2916
|
+
case c_name:
|
|
2917
|
+
case c_true:
|
|
2918
|
+
case c_false:
|
|
2919
|
+
case c_fail:
|
|
2920
|
+
case c_set:
|
|
2921
|
+
case c_unset:
|
|
2922
|
+
case c_assign:
|
|
2923
|
+
case c_plusassign:
|
|
2924
|
+
case c_minusassign:
|
|
2925
|
+
case c_multiplyassign:
|
|
2926
|
+
case c_divideassign:
|
|
2927
|
+
case c_tomark:
|
|
2928
|
+
case c_tolimit:
|
|
2929
|
+
case c_hop:
|
|
2930
|
+
case c_next:
|
|
2931
|
+
case c_insert:
|
|
2932
|
+
case c_attach:
|
|
2933
|
+
case c_leftslice:
|
|
2934
|
+
case c_rightslice:
|
|
2935
|
+
case c_sliceto:
|
|
2936
|
+
case c_stringassign:
|
|
2937
|
+
case c_slicefrom:
|
|
2938
|
+
inline_routine = true;
|
|
2939
|
+
break;
|
|
2940
|
+
}
|
|
2726
2941
|
}
|
|
2727
2942
|
|
|
2728
2943
|
if (inline_routine) {
|
|
@@ -2732,33 +2947,67 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2732
2947
|
n->used->line_number,
|
|
2733
2948
|
SIZE(n->s), n->s);
|
|
2734
2949
|
#endif
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2950
|
+
bool remove = false;
|
|
2951
|
+
for (struct name * r = a->names; r; r = r->next) {
|
|
2952
|
+
// Only routines and externals.
|
|
2953
|
+
if (!r->definition) continue;
|
|
2954
|
+
// Don't try to inline a routine into itself!
|
|
2955
|
+
if (r == n) continue;
|
|
2956
|
+
if (inline_calls(a, r->definition->left, n)) {
|
|
2957
|
+
// If n contains an among, r now does. Similarly
|
|
2958
|
+
// for an among with functions.
|
|
2959
|
+
r->has_among |= n->has_among;
|
|
2960
|
+
r->has_among_function |= n->has_among_function;
|
|
2961
|
+
if (n->definition == NULL) {
|
|
2962
|
+
// All calls to routine `n` have now been inlined
|
|
2963
|
+
// so remove it and move on to the next candidate
|
|
2964
|
+
// for inlining.
|
|
2965
|
+
remove = true;
|
|
2966
|
+
break;
|
|
2967
|
+
}
|
|
2968
|
+
}
|
|
2969
|
+
}
|
|
2970
|
+
if (remove) {
|
|
2971
|
+
remove_routine(a, n);
|
|
2972
|
+
*n_ptr = n->next;
|
|
2973
|
+
lose_s(n->s);
|
|
2974
|
+
FREE(n);
|
|
2975
|
+
continue;
|
|
2976
|
+
}
|
|
2744
2977
|
}
|
|
2745
2978
|
}
|
|
2746
2979
|
|
|
2747
2980
|
n_ptr = &(n->next);
|
|
2748
2981
|
}
|
|
2749
2982
|
|
|
2750
|
-
// Add "functionend" nodes there so optimisations such as dead code
|
|
2751
|
-
// elimination and tail call optimisation can easily see where the function
|
|
2752
|
-
// ends.
|
|
2753
2983
|
for (struct name * name = a->names; name; name = name->next) {
|
|
2754
|
-
if (name->type != t_routine && name->type != t_external) continue;
|
|
2755
|
-
|
|
2756
2984
|
struct node * p = name->definition;
|
|
2757
2985
|
if (!p) {
|
|
2758
|
-
//
|
|
2986
|
+
// Either this name is not a routine/external, or it is missing
|
|
2987
|
+
// a definition (which gets reported later).
|
|
2759
2988
|
continue;
|
|
2760
2989
|
}
|
|
2990
|
+
|
|
2991
|
+
// Recursion is rare in real-world Snowball programs, and there are
|
|
2992
|
+
// some optimisations that we don't attempt if this flag is set.
|
|
2993
|
+
for (struct name * n = a->names; n; n = n->next) {
|
|
2994
|
+
n->visited = false;
|
|
2995
|
+
}
|
|
2996
|
+
name->recursive = recursion_check(p->left, name);
|
|
2997
|
+
#if 0
|
|
2998
|
+
if (name->recursive) {
|
|
2999
|
+
fprintf(stderr,
|
|
3000
|
+
"%s:%d: info: `%.*s` is recursive\n",
|
|
3001
|
+
a->tokeniser->file, name->definition->line_number,
|
|
3002
|
+
SIZE(name->s), name->s);
|
|
3003
|
+
}
|
|
3004
|
+
#endif
|
|
3005
|
+
|
|
3006
|
+
// Add "functionend" nodes so that optimisations such as dead code
|
|
3007
|
+
// elimination and tail call optimisation can easily see where the
|
|
3008
|
+
// function ends.
|
|
2761
3009
|
assert(p->type == c_define);
|
|
3010
|
+
assert(p->left);
|
|
2762
3011
|
assert(p->left->right == NULL);
|
|
2763
3012
|
if (p->left->type == c_bra) {
|
|
2764
3013
|
/* Put the "functionend" node at the end of the command list. */
|
|
@@ -2854,12 +3103,12 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2854
3103
|
//
|
|
2855
3104
|
// We only issue a warning about unreachability for routines here
|
|
2856
3105
|
// to avoid excess diagnostics, since other types must be used in a
|
|
2857
|
-
// routine which is
|
|
3106
|
+
// routine which is reachable (or will have been warned about as
|
|
2858
3107
|
// unused by the check above).
|
|
2859
3108
|
if (q->type == t_routine) {
|
|
2860
3109
|
fprintf(stderr, "%s:%d: warning: %s '%.*s' not reachable from any externals\n",
|
|
2861
3110
|
a->tokeniser->file,
|
|
2862
|
-
q->
|
|
3111
|
+
q->definition->line_number,
|
|
2863
3112
|
name_of_type(q->type),
|
|
2864
3113
|
SIZE(q->s), q->s);
|
|
2865
3114
|
remove_routine(a, q);
|
|
@@ -2919,11 +3168,61 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2919
3168
|
struct among * x = *a_ptr;
|
|
2920
3169
|
if (!x->used) {
|
|
2921
3170
|
*a_ptr = x->next;
|
|
3171
|
+
free_among(x);
|
|
2922
3172
|
continue;
|
|
2923
3173
|
}
|
|
2924
3174
|
|
|
2925
|
-
|
|
2926
|
-
|
|
3175
|
+
// Check for an already numbered among which is the same as this.
|
|
3176
|
+
for (struct among * y = a->amongs; y != x; y = y->next) {
|
|
3177
|
+
// Some of these are redundant to check, but doing so may allow
|
|
3178
|
+
// us to more quickly detect amongs which are different.
|
|
3179
|
+
if (y->literalstring_count != x->literalstring_count ||
|
|
3180
|
+
y->command_count != x->command_count ||
|
|
3181
|
+
y->nocommand_count != x->nocommand_count ||
|
|
3182
|
+
y->af_count != x->af_count ||
|
|
3183
|
+
y->function_count != x->function_count ||
|
|
3184
|
+
y->unique_function_count != x->unique_function_count ||
|
|
3185
|
+
y->amongvar_needed != x->amongvar_needed ||
|
|
3186
|
+
y->always_matches != x->always_matches ||
|
|
3187
|
+
y->same_action != x->same_action ||
|
|
3188
|
+
y->shortest_size != x->shortest_size ||
|
|
3189
|
+
y->longest_size != x->longest_size) {
|
|
3190
|
+
continue;
|
|
3191
|
+
}
|
|
3192
|
+
bool same = true;
|
|
3193
|
+
for (int i = 0; i != y->literalstring_count; ++i) {
|
|
3194
|
+
const struct amongvec * p = &x->v[i];
|
|
3195
|
+
const struct amongvec * q = &y->v[i];
|
|
3196
|
+
int p_size = p->size;
|
|
3197
|
+
if (p_size != q->size) {
|
|
3198
|
+
same = false;
|
|
3199
|
+
break;
|
|
3200
|
+
}
|
|
3201
|
+
if (p->function != q->function) {
|
|
3202
|
+
same = false;
|
|
3203
|
+
break;
|
|
3204
|
+
}
|
|
3205
|
+
const symbol * b_p = p->b;
|
|
3206
|
+
const symbol * b_q = q->b;
|
|
3207
|
+
if (memcmp(b_p, b_q, p_size * sizeof(symbol)) != 0) {
|
|
3208
|
+
same = false;
|
|
3209
|
+
break;
|
|
3210
|
+
}
|
|
3211
|
+
if (!nodes_equivalent(p->action, q->action)) {
|
|
3212
|
+
same = false;
|
|
3213
|
+
break;
|
|
3214
|
+
}
|
|
3215
|
+
}
|
|
3216
|
+
if (!same) continue;
|
|
3217
|
+
x->number = y->number;
|
|
3218
|
+
x->duplicate = true;
|
|
3219
|
+
break;
|
|
3220
|
+
}
|
|
3221
|
+
|
|
3222
|
+
if (!x->duplicate) {
|
|
3223
|
+
x->number = among_count++;
|
|
3224
|
+
if (x->function_count > 0) ++a->among_with_function_count;
|
|
3225
|
+
}
|
|
2927
3226
|
|
|
2928
3227
|
for (int i = 1; i <= x->command_count; i++) {
|
|
2929
3228
|
int merge_with = 0;
|
|
@@ -2949,15 +3248,15 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2949
3248
|
// and subtract one from references to command indexes after
|
|
2950
3249
|
// this one.
|
|
2951
3250
|
for (int j = 0; j < x->literalstring_count; ++j) {
|
|
2952
|
-
int diff = (x->
|
|
3251
|
+
int diff = (x->v[j].result - i);
|
|
2953
3252
|
if (diff == 0) {
|
|
2954
|
-
x->
|
|
3253
|
+
x->v[j].result = merge_with;
|
|
2955
3254
|
if (merge_with == 0) {
|
|
2956
|
-
assert(x->
|
|
2957
|
-
x->
|
|
3255
|
+
assert(x->v[j].action->type == c_bra);
|
|
3256
|
+
x->v[j].action->left = NULL;
|
|
2958
3257
|
}
|
|
2959
3258
|
} else if (diff > 0) {
|
|
2960
|
-
--x->
|
|
3259
|
+
--x->v[j].result;
|
|
2961
3260
|
}
|
|
2962
3261
|
}
|
|
2963
3262
|
memmove(x->commands + (i - 1), x->commands + i,
|
|
@@ -2973,8 +3272,6 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2973
3272
|
* find_among*() returns zero or not.
|
|
2974
3273
|
*/
|
|
2975
3274
|
x->amongvar_needed = true;
|
|
2976
|
-
if (x->in_routine)
|
|
2977
|
-
x->in_routine->amongvar_needed = true;
|
|
2978
3275
|
}
|
|
2979
3276
|
|
|
2980
3277
|
a_ptr = &(x->next);
|
|
@@ -2983,10 +3280,13 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2983
3280
|
|
|
2984
3281
|
/* Localise variables.
|
|
2985
3282
|
*
|
|
2986
|
-
* We localise variables which are only referenced in a single
|
|
2987
|
-
* (routine or external) and which are always set before being
|
|
2988
|
-
* that function (since a function could rely on a variable's
|
|
2989
|
-
* value surviving).
|
|
3283
|
+
* We localise variables which are only referenced in a single non-recursive
|
|
3284
|
+
* function (routine or external) and which are always set before being
|
|
3285
|
+
* read within that function (since a function could rely on a variable's
|
|
3286
|
+
* previous value surviving).
|
|
3287
|
+
*
|
|
3288
|
+
* This optimisation is done after inlining, since inlining can often move
|
|
3289
|
+
* all references to a variable into the same function.
|
|
2990
3290
|
*
|
|
2991
3291
|
* We could potentially localise variables referenced in multiple functions
|
|
2992
3292
|
* provided that they are always set before use in every function they are
|
|
@@ -2995,18 +3295,24 @@ extern void read_program(struct analyser * a, unsigned localise_mask) {
|
|
|
2995
3295
|
*/
|
|
2996
3296
|
memset(a->name_count, 0, sizeof(a->name_count));
|
|
2997
3297
|
for (struct name * name = a->names; name; name = name->next) {
|
|
2998
|
-
if (name->local_to
|
|
3298
|
+
if (name->local_to) {
|
|
2999
3299
|
if (localise_mask & (1 << name->type)) {
|
|
3000
3300
|
struct node * func = name->local_to->definition;
|
|
3001
|
-
if (
|
|
3301
|
+
if (name->local_to->recursive) {
|
|
3302
|
+
fprintf(stderr,
|
|
3303
|
+
"%s:%d: info: Could not localise %s `%.*s` into recursive routine `%.*s`\n",
|
|
3304
|
+
a->tokeniser->file, func->line_number,
|
|
3305
|
+
name_of_type(name->type),
|
|
3306
|
+
SIZE(name->s), name->s,
|
|
3307
|
+
SIZE(func->name->s), func->name->s);
|
|
3308
|
+
name->local_to = NULL;
|
|
3309
|
+
} else if (!always_set_before_use(func->left, name)) {
|
|
3002
3310
|
fprintf(stderr,
|
|
3003
3311
|
"%s:%d: info: Could not localise %s `%.*s` to routine `%.*s`\n",
|
|
3004
3312
|
a->tokeniser->file, func->line_number,
|
|
3005
3313
|
name_of_type(name->type),
|
|
3006
3314
|
SIZE(name->s), name->s,
|
|
3007
3315
|
SIZE(func->name->s), func->name->s);
|
|
3008
|
-
report_s(stderr, name->s);
|
|
3009
|
-
fprintf(stderr, "\n");
|
|
3010
3316
|
name->local_to = NULL;
|
|
3011
3317
|
}
|
|
3012
3318
|
} else {
|
|
@@ -3073,9 +3379,7 @@ extern void close_analyser(struct analyser * a) {
|
|
|
3073
3379
|
struct among * q = a->amongs;
|
|
3074
3380
|
while (q) {
|
|
3075
3381
|
struct among * q_next = q->next;
|
|
3076
|
-
|
|
3077
|
-
FREE(q->commands);
|
|
3078
|
-
FREE(q);
|
|
3382
|
+
free_among(q);
|
|
3079
3383
|
q = q_next;
|
|
3080
3384
|
}
|
|
3081
3385
|
}
|