equivalence.c
1 //! @file equivalence.c
2 //! @author J. Marcel van der Veer
3 //
4 //! @section Copyright
5 //
6 // This file is part of VIF - vintage FORTRAN compiler.
7 // Copyright 2020-2025 J. Marcel van der Veer <algol68g@xs4all.nl>.
8 //
9 //! @section License
10 //
11 // This program is free software; you can redistribute it and/or modify it
12 // under the terms of the GNU General Public License as published by the
13 // Free Software Foundation; either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful, but
17 // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
18 // or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
19 // more details. You should have received a copy of the GNU General Public
20 // License along with this program. If not, see <http://www.gnu.org/licenses/>.
21
22 //! @section Synopsis
23 //!
24 //! Compile EQUIVALENCE.
25
26 // This code compiles pairwise equivalence statements of the form
27 //
28 // EQUIVALENCE (A, B), ...
29 //
30 // Multiple equivalence was allowed by vintage Fortran,
31 // but hardly used (if at all) in practice.
32 // Anyway, VIF compiles
33 //
34 // EQUIVALENCE (A, B, C, D)
35 //
36 // as (Aho, Sethi, Ullman)
37 //
38 // EQUIVALENCE (B, A), (C, A), (D, A)
39 //
40 // Not all EQUIVALENCE statements may work in VIF as VIF neither emulates the
41 // FORTRAN memory model, nor combines equivalence statements. This hardly has
42 // consequences for compiling existing vintage code. Programmers apparently
43 // only need(ed) basic equivalence facilities.
44
45 #include <vif.h>
46
47 static void eq_compute_row_size (IDENT *idf, int_4 *val)
48 {
49 // Compute size of row in bytes.
50 NEW_RECORD (buf);
51 compute_row_size (buf, idf);
52 if (EQUAL (buf, "VARY")) {
53 ERROR (1301, "varying row in equivalence", NO_TEXT);
54 }
55 if (!is_int4 (buf, val)) {
56 (*val) = 0;
57 }
58 }
59
60 static void eq_var_any (EXPR lhs, EXPR rhs, int_4 *N)
61 {
62 // Link a variable to either variable or row.
63 IDENT *lid = lhs.idf, *rid = rhs.idf; NEW_RECORD (str);
64 // Redirect if possible.
65 if (lid->equiv != NO_IDENT) {
66 if (rid->equiv == NO_IDENT) {
67 eq_var_any (rhs, lhs, N);
68 } else {
69 _srecordf (str, "(%s, %s)", FTN_NAME (lid), FTN_NAME (rid));
70 ERROR (1302, "cannot alias", str);
71 }
72 return;
73 }
74 // Peephole optimization.
75 NEW_RECORD (target);
76 NEW_RECORD (buf);
77 if (IS_SCALAR (rid->mode)) {
78 _srecordf (target, "&(%s)", rhs.str);
79 } else {
80 if (!is_int4 (rhs.elem, &rhs.value)) {
81 SYNTAX (1303, "rhs must have a constant index");
82 return;
83 }
84 if (rhs.value == 0) {
85 _srecordf (target, "%s", idf_full_c_name (buf, rid));
86 } else {
87 _srecordf (target, "&(%s)", rhs.str);
88 }
89 }
90 // Make alias.
91 if (rid->common != LOCAL) {
92 cpp_direct (nprocs, prelin, REFDECL);
93 _srecordf (str, "static %s %s = %s %s;\n", wtype (&lhs.mode, NOARG, NOFUN), ptr_to_array (lhs.idf, CONST, NOCAST, ACTUAL), ptr_to_array (lhs.idf, NOCONST, CAST, ACTUAL), target);
94 code (nprocs, REFDECL, str);
95 lid->const_ref = TRUE;
96 } else if (lhs.mode.save == STATIC) {
97 if (rhs.mode.save == STATIC) {
98 cpp_direct (nprocs, prelin, REFDECL);
99 _srecordf (str, "static %s %s = %s %s;\n", wtype (&lhs.mode, NOARG, NOFUN), ptr_to_array (lhs.idf, CONST, NOCAST, ACTUAL), ptr_to_array (lhs.idf, NOCONST, CAST, ACTUAL), target);
100 code (nprocs, REFDECL, str);
101 lid->const_ref = TRUE;
102 } else {
103 cpp_direct (nprocs, prelin, EQUIV);
104 _srecordf (str, "%s = %s %s;\n", lhs.str, ptr_to_array (lhs.idf, NOCONST, CAST, ACTUAL), target);
105 code (nprocs, EQUIV, str);
106 N++;
107 }
108 } else {
109 cpp_direct (nprocs, prelin, BODY);
110 _srecordf (str, "%s = %s %s;\n", lhs.str, ptr_to_array (lhs.idf, NOCONST, CAST, ACTUAL), target);
111 code (nprocs, BODY, str);
112 }
113 }
114
115 static void eq_row_row (EXPR lhs, EXPR rhs, int_4 *N)
116 {
117 // Link a row to a row.
118 IDENT *lid = lhs.idf, *rid = rhs.idf; NEW_RECORD (str);
119 NEW_RECORD (index); NEW_RECORD (buf);
120 // Peephole optimization.
121 if (lhs.value == rhs.value) {
122 _srecordf (index, "%s", idf_full_c_name (buf, rid));
123 } else {
124 _srecordf (index, "&(%s[%d])", idf_full_c_name (buf, rid), rhs.value - lhs.value);
125 }
126 // Make alias.
127 if (rid->common != LOCAL) {
128 cpp_direct (nprocs, prelin, DECL);
129 _srecordf (str, "static %s %s = %s %s;\n", wtype (&lhs.mode, NOARG, NOFUN), ptr_to_array (lid, CONST, NOCAST, ACTUAL), ptr_to_array (lid, NOCONST, CAST, ACTUAL), index);
130 code (nprocs, DECL, str);
131 lid->const_ref = TRUE;
132 } else {
133 _srecordf (str, "%s = %s %s;\n", C_NAME (lid), ptr_to_array (lid, NOCONST, CAST, ACTUAL), index);
134 if (lhs.mode.save == STATIC) {
135 cpp_direct (nprocs, prelin, EQUIV);
136 code (nprocs, EQUIV, str);
137 N++;
138 } else {
139 cpp_direct (nprocs, prelin, BODY);
140 code (nprocs, BODY, str);
141 }
142 }
143 }
144
145 static void eq_link (EXPR lhs, EXPR rhs, int_4 *N)
146 {
147 IDENT *lid = lhs.idf, *rid = rhs.idf;
148 // Oops!
149 if (lid == NO_IDENT || rid == NO_IDENT) {
150 SYNTAX (1304, "equivalence statement");
151 return;
152 }
153 // ROW -> ROW is mostly OK.
154 if (IS_ROW (lid->mode) && IS_ROW (rid->mode)) {
155 if (!is_int4 (lhs.elem, &lhs.value)) {
156 SYNTAX (1305, "lhs must have a constant index");
157 return;
158 }
159 if (!is_int4 (rhs.elem, &rhs.value)) {
160 SYNTAX (1306, "rhs must have a constant index");
161 return;
162 }
163 int_4 lsz, rsz;
164 eq_compute_row_size (lid, &lsz);
165 eq_compute_row_size (rid, &rsz);
166 // We want the alias to fit in the target.
167 if (rhs.idf->common == LOCAL && (rhs.value <= lhs.value) && (rsz <= lsz - lhs.value)) {
168 rid->alias = lid;
169 lid->equiv = rid;
170 eq_row_row (rhs, lhs, N);
171 } else if (lhs.idf->common == LOCAL && (rhs.value >= lhs.value) && (lsz <= rsz - rhs.value)) {
172 lid->alias = rid;
173 rid->equiv = lid;
174 eq_row_row (lhs, rhs, N);
175 } else {
176 // Alias sticks out to the right.
177 ERROR (1307, "equivalence", "cannot equivalence");
178 }
179 return;
180 }
181 // ROW -> VAR is VAR -> ROW.
182 if (IS_ROW (lid->mode) && IS_SCALAR (rid->mode)) {
183 eq_link (rhs, lhs, N);
184 return;
185 }
186 // Shuffle to avoid multiple equivalencing if possible.
187 if (lid->equiv != NO_IDENT) {
188 if (rid->equiv == NO_IDENT) {
189 eq_link (rhs, lhs, N);
190 return;
191 } else {
192 NEW_RECORD (str);
193 _srecordf (str, "(%s, %s)", FTN_NAME (lid), FTN_NAME (rid));
194 ERROR (1308, "cannot alias", str);
195 }
196 }
197 // VAR -> VAR is OK.
198 if (IS_SCALAR (lid->mode) && IS_SCALAR (rid->mode)) {
199 lid->alias = rid;
200 rid->equiv = lid;
201 eq_var_any (lhs, rhs, N);
202 }
203 // VAR -> ROW is OK.
204 if (IS_SCALAR (lid->mode) && IS_ROW (rid->mode)) {
205 lid->alias = rid;
206 rid->equiv = lid;
207 eq_var_any (lhs, rhs, N);
208 }
209 }
210
211 void equivalence (void)
212 {
213 // EQUIVALENCE by aliasing already allocated variables or arrays.
214 // We already know that parentheses are balanced.
215 int_4 rc, N = 0;
216 int_4 epatch = code (nprocs, EQUIV, NO_TEXT);
217 while ((rc = scan (EXPECT_NONE)) != END_OF_LINE) {
218 if (TOKEN (",")) {
219 continue;
220 } else if (TOKEN ("(")) {
221 continue;
222 } else if (rc != WORD) {
223 EXPECT (1309, "identifier");
224 } else {
225 IDENT *lid, *rid;
226 EXPR lhs, rhs;
227 MODE lmode, rmode;
228 rid = impl_decl (curlex, &lmode);
229 if (rid->alias != NO_IDENT) {
230 ERROR (1310, "cannot alias", curlex);
231 }
232 factor (&rhs);
233 rc = scan (EXPECT_NONE);
234 if (!TOKEN (",")) {
235 EXPECT (1311, ",");
236 }
237 // Loop resolving multiple equivalence.
238 while (TOKEN (",")) {
239 rc = scan (EXPECT_NONE);
240 if (rc != WORD) {
241 EXPECT (1312, "variable");
242 }
243 lid = impl_decl (curlex, &rmode);
244 factor (&lhs);
245 rc = scan (EXPECT_NONE);
246 // Mistakes.
247 if (lid == NO_IDENT || rid == NO_IDENT) {
248 break;
249 }
250 if (lid->mode.save != rid->mode.save) {
251 ERROR (1313, "aliasing static and automatic", NO_TEXT);
252 break;
253 }
254 if (lid->common != LOCAL && rid->common != LOCAL) {
255 ERROR (1314, "lhs and rhs are in common block", NO_TEXT);
256 break;
257 }
258 // If a row is denoted as variable, address of first element is implied.
259 // Padding "[0]" simplifies code generation.
260 if (lhs.variant == EXPR_VAR && IS_ROW (lid->mode)) {
261 bufcat (lhs.str, "[0]", RECLN);
262 _srecordf (lhs.elem, "0");
263 lhs.value = 0;
264 }
265 if (rhs.variant == EXPR_VAR && IS_ROW (rid->mode)) {
266 bufcat (rhs.str, "[0]", RECLN);
267 _srecordf (rhs.elem, "0");
268 rhs.value = 0;
269 }
270 // Warning!
271 if (lid->mode.type != rid->mode.type || lid->mode.len != rid->mode.len) {
272 NEW_RECORD (str);
273 _srecordf (str, "equivalence (%s, %s)", qtype (&(rhs.mode)), qtype (&(lhs.mode)));
274 WARNING (1315, "mixed types", str);
275 }
276 // Equivalence in correct order.
277 if (lid->common == LOCAL) {
278 eq_link (lhs, rhs, &N);
279 } else if (rid->common == LOCAL) {
280 eq_link (rhs, lhs, &N);
281 } else {
282 eq_link (lhs, rhs, &N);
283 }
284 }
285 }
286 }
287 if (N > 0) {
288 NEW_RECORD (str);
289 cpp_direct (nprocs, prelin, EQUIV);
290 _srecordf (str, "if (__calls[%d].calls == 0) {\n", nprocs - 1);
291 patch (epatch, str);
292 code (nprocs, EQUIV, "}\n");
293 }
294 }
295
296 void decl_equiv (void)
297 {
298 int_4 go_on = TRUE;
299 while (go_on) {
300 SAVE_POS;
301 int_4 rc = scan (EXPECT_NONE);
302 if (rc == DECLAR) {
303 skip_card (FALSE);
304 } else if (TOKEN ("implicit")) {
305 skip_card (FALSE);
306 } else if (TOKEN ("save")) {
307 skip_card (FALSE);
308 } else if (TOKEN ("automatic")) {
309 skip_card (FALSE);
310 } else if (TOKEN ("parameter")) {
311 skip_card (FALSE);
312 } else if (TOKEN ("common")) {
313 skip_card (FALSE);
314 } else if (TOKEN ("dimension")) {
315 skip_card (FALSE);
316 } else if (TOKEN ("equivalence")) {
317 equivalence ();
318 skip_card (FALSE);
319 } else if (TOKEN ("external")) {
320 skip_card (FALSE);
321 } else if (TOKEN ("intrinsic")) {
322 skip_card (FALSE);
323 } else if (TOKEN ("data")) {
324 skip_card (FALSE);
325 } else if (rc == WORD && is_macro_decl (curlex)) {
326 skip_card (FALSE);
327 } else if (strlen (curlex) > 0) {
328 // Backspace and done.
329 RESTORE_POS;
330 go_on = FALSE;
331 }
332 }
333 }
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|