fold.c
1 //! @file fold.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 //! Constant folder.
25
26 #include <vif.h>
27 #include <rts-real32.h>
28
29 // A trivial calculator to compiler generated constant expressions.
30
31 // INTEGER
32 // Exponentiation is already optimised by the code generator.
33
34 static int_4 calc_int_add (char **, int_4 *);
35
36 static int_4 calc_int_fact (char **s, int_4 *val)
37 {
38 while (isspace (*s[0])) {
39 (*s)++;
40 }
41 if (isdigit ((*s)[0])) {
42 *val = strtol (*s, s, 10);
43 return TRUE;
44 } else if ((*s)[0] == '-') {
45 int_4 rc;
46 (*s)++;
47 rc = calc_int_fact (s, val);
48 *val = -*val;
49 return rc;
50 } else if ((*s)[0] == '(') {
51 int_4 rc, sub;
52 (*s)++;
53 rc = calc_int_add (s, &sub);
54 (*s)++; // Assume ')'
55 *val = sub;
56 return rc;
57 }
58 return FALSE;
59 }
60
61 static int_4 calc_int_mul (char **s, int_4 *val)
62 {
63 int_4 lval, rval;
64 if (!calc_int_fact (s, &lval)) {
65 return FALSE;
66 }
67 while (isspace (*s[0])) {
68 (*s)++;
69 }
70 while ((*s)[0] != '\0' && ((*s)[0] == '*' || (*s)[0] == '/' || (*s[0]) == '%')) {
71 char op = (*s)++[0];
72 if (!calc_int_fact (s, &rval)) {
73 return FALSE;
74 }
75 if (op == '*') {
76 lval *= rval;
77 } else if (op == '/') {
78 lval /= rval;
79 } else {
80 lval %= rval;
81 }
82 while ((*s)[0] == ' ') {
83 (*s)++;
84 }
85 }
86 *val = lval;
87 return TRUE;
88 }
89
90 static int_4 calc_int_add (char **s, int_4 *val)
91 {
92 int_4 lval, rval;
93 if (!calc_int_mul (s, &lval)) {
94 return FALSE;
95 }
96 while ((*s)[0] == ' ') {
97 (*s)++;
98 }
99 while ((*s)[0] != '\0' && ((*s)[0] == '+' || (*s)[0] == '-')) {
100 char op = (*s)++[0];
101 if (!calc_int_mul (s, &rval)) {
102 return FALSE;
103 }
104 if (op == '+') {
105 lval += rval;
106 } else {
107 lval -= rval;
108 }
109 while (isspace (*s[0])) {
110 (*s)++;
111 }
112 }
113 *val = lval;
114 return TRUE;
115 }
116
117 int_4 calc_int_4 (char *p, int_4 *val)
118 {
119 char *q = p;
120 int_4 rc = calc_int_add (&q, val) && (q[0] == '\0');
121 return rc;
122 }
123
124 void fold_int_4 (char *buf, char *p)
125 {
126 RECORD q;
127 int_4 val;
128 bufcpy (q, p, RECLN);
129 if (calc_int_4 (q, &val)) {
130 _srecordf (buf, "%d", val);
131 } else {
132 bufcpy (buf, p, RECLN);
133 }
134 }
135
136 // REAL
137 // Exponentiation is already optimised by the code generator.
138
139 static int_4 calc_real_add (char **, real_32 *);
140
141 static int_4 calc_real_fact (char **s, real_32 *val)
142 {
143 while (isspace (*s[0])) {
144 (*s)++;
145 }
146 if (isdigit ((*s)[0])) {
147 *val = strtox (*s, s);
148 if ((*s)[0] == 'q') {
149 (*s)++;
150 }
151 return TRUE;
152 } else if ((*s)[0] == '-') {
153 int_4 rc;
154 (*s)++;
155 rc = calc_real_fact (s, val);
156 *val = xneg (*val);
157 return rc;
158 } else if ((*s)[0] == '(') {
159 int_4 rc;
160 real_32 sub;
161 (*s)++;
162 rc = calc_real_add (s, &sub);
163 (*s)++; // Assume ')'
164 *val = sub;
165 return rc;
166 }
167 return FALSE;
168 }
169
170 static int_4 calc_real_mul (char **s, real_32 *val)
171 {
172 real_32 lval, rval;
173 if (!calc_real_fact (s, &lval)) {
174 return FALSE;
175 }
176 while (isspace (*s[0])) {
177 (*s)++;
178 }
179 while ((*s)[0] != '\0' && ((*s)[0] == '*' || (*s)[0] == '/')) {
180 char op = (*s)++[0];
181 if (!calc_real_fact (s, &rval)) {
182 return FALSE;
183 }
184 if (op == '*') {
185 lval = xmul (lval, rval);
186 } else if (op == '/') {
187 if (xis0 (&rval)) {
188 ERROR (1501, "division by zero", NO_TEXT);
189 return FALSE;
190 } else {
191 lval = xdiv (lval, rval);
192 }
193 }
194 while ((*s)[0] == ' ') {
195 (*s)++;
196 }
197 }
198 *val = lval;
199 return TRUE;
200 }
201
202 static int_4 calc_real_add (char **s, real_32 *val)
203 {
204 real_32 lval, rval;
205 if (!calc_real_mul (s, &lval)) {
206 return FALSE;
207 }
208 while ((*s)[0] == ' ') {
209 (*s)++;
210 }
211 while ((*s)[0] != '\0' && ((*s)[0] == '+' || (*s)[0] == '-')) {
212 char op = (*s)++[0];
213 if (!calc_real_mul (s, &rval)) {
214 return FALSE;
215 }
216 if (op == '+') {
217 lval = xadd (lval, rval, 0);
218 } else {
219 lval = xadd (lval, rval, 1);
220 }
221 while (isspace (*s[0])) {
222 (*s)++;
223 }
224 }
225 *val = lval;
226 return TRUE;
227 }
228
229 int_4 calc_real (char *p, real_32 *val)
230 {
231 char *q = p;
232 int_4 rc = calc_real_add (&q, val) && (q[0] == '\0');
233 return rc;
234 }
235
236 // COMPLEX
237
238 static int_4 calc_complex_add (char **, complex_64 *);
239
240 int skip (char **q, char *p)
241 {
242 size_t N = strlen (p);
243 if (strncmp (*q, p, N) == 0) {
244 (*q) += N;
245 return TRUE;
246 } else {
247 return FALSE;
248 }
249 }
250
251 static int_4 calc_complex_fact (char **s, complex_64 *val)
252 {
253 while (isspace (*s[0])) {
254 (*s)++;
255 }
256 (void) skip (s, "CMPLXQ");
257 (void) skip (s, "CMPLXF");
258 (void) skip (s, "CMPLX");
259 while (isspace (*s[0])) {
260 (*s)++;
261 }
262 if (isdigit ((*s)[0])) {
263 real_32 z;
264 z = strtox (*s, s);
265 if ((*s)[0] == 'q') {
266 (*s)++;
267 }
268 *val = (complex_64) {z, X_0};
269 return TRUE;
270 } else if ((*s)[0] == '-') {
271 int_4 rc;
272 (*s)++;
273 rc = calc_complex_fact (s, val);
274 *val = cxneg (*val);
275 return rc;
276 } else if ((*s)[0] == '(') {
277 int_4 rc;
278 complex_64 sub;
279 (*s)++;
280 char *t = *s;
281 if (isdigit ((*s)[0]) || (*s)[0] == '+' || (*s)[0] == '-') {
282 real_32 re, im;
283 re = strtox (*s, s);
284 if ((*s)[0] == 'q') {
285 (*s)++;
286 }
287 if ((*s)[0] == ',') {
288 (*s)++;
289 im = strtox (*s, s);
290 if ((*s)[0] == 'q') {
291 (*s)++;
292 }
293 sub = (complex_64) {re, im};
294 rc = TRUE;
295 } else {
296 *s = t;
297 rc = calc_complex_add (s, &sub);
298 }
299 } else {
300 rc = calc_complex_add (s, &sub);
301 }
302 (*s)++; // Assume ')'
303 *val = sub;
304 return rc;
305 }
306 return FALSE;
307 }
308
309 static int_4 calc_complex_mul (char **s, complex_64 *val)
310 {
311 complex_64 lval, rval;
312 if (!calc_complex_fact (s, &lval)) {
313 return FALSE;
314 }
315 while (isspace (*s[0])) {
316 (*s)++;
317 }
318 while ((*s)[0] != '\0' && ((*s)[0] == '*' || (*s)[0] == '/')) {
319 char op = (*s)++[0];
320 if (!calc_complex_fact (s, &rval)) {
321 return FALSE;
322 }
323 if (op == '*') {
324 lval = cxmul (lval, rval);
325 } else if (op == '/') {
326 lval = cxdiv (lval, rval);
327 }
328 while ((*s)[0] == ' ') {
329 (*s)++;
330 }
331 }
332 *val = lval;
333 return TRUE;
334 }
335
336 static int_4 calc_complex_add (char **s, complex_64 *val)
337 {
338 complex_64 lval, rval;
339 if (!calc_complex_mul (s, &lval)) {
340 return FALSE;
341 }
342 while ((*s)[0] == ' ') {
343 (*s)++;
344 }
345 while ((*s)[0] != '\0' && ((*s)[0] == '+' || (*s)[0] == '-')) {
346 char op = (*s)++[0];
347 if (!calc_complex_mul (s, &rval)) {
348 return FALSE;
349 }
350 if (op == '+') {
351 lval = cxsum (lval, rval);
352 } else {
353 lval = cxsub (lval, rval);
354 }
355 while (isspace (*s[0])) {
356 (*s)++;
357 }
358 }
359 *val = lval;
360 return TRUE;
361 }
362
363 int_4 calc_complex (char *p, complex_64 *val)
364 {
365 char *q = p;
366 int_4 rc = calc_complex_add (&q, val) && (q[0] == '\0');
367 return rc;
368 }
369
370 // Drivers.
371
372 char *pretty_float (char *num)
373 {
374 // Cut zero exponent and end-zeroes in floats.
375 RECORD expo;
376 RECCLR (expo);
377 char *e = strchr (num, 'e');
378 if (e != NO_TEXT) {
379 _srecordf (expo, "%s", &e[1]);
380 *e = '\0';
381 }
382 RECORD frac;
383 RECCLR (frac);
384 char *f = strchr (num, '.');
385 if (f != NO_TEXT) {
386 _srecordf (frac, "%s", &f[1]);
387 *f = '\0';
388 }
389 // Simplify exponent.
390 if (e != NO_TEXT) {
391 int expd;
392 sscanf (expo, "%d", &expd);
393 if (expd != 0) {
394 _srecordf (expo, "%d", expd);
395 }
396 }
397 // Simplify fraction.
398 while (strlen (frac) > 0 && frac[strlen (frac) - 1] == '0') {
399 frac[strlen (frac) - 1] = '\0';
400 }
401 // Compose pretty float,
402 if (strlen (frac) > 0) {
403 strcat (num, ".");
404 strcat (num, frac);
405 } else {
406 strcat (num, ".0");
407 }
408 if (strlen (expo) > 0) {
409 strcat (num, "e");
410 strcat (num, expo);
411 }
412 return num;
413 }
414
415 void pretty_number (char *num, int_4 prec, real_32 val)
416 {
417 RECORD stre, strf, fmt;
418 _srecordf (fmt, "%%.%df", prec);
419 _fprintf_real_32 (strf, fmt, val, 5, FLT256_DIG);
420 real_32 valf = strtox (strf, NO_REF_TEXT);
421 _srecordf (fmt, "%%.%de", prec);
422 _fprintf_real_32 (stre, fmt, val, 5, FLT256_DIG);
423 real_32 vale = strtox (stre, NO_REF_TEXT);
424 if (xeq (vale, valf)) {
425 _srecordf (num, "%s", pretty_float (strf));
426 } else {
427 _srecordf (num, "%s", pretty_float (stre));
428 }
429 }
430
431 void pretty_real (char *num, int_4 prec, real_32 val)
432 {
433 switch (prec) {
434 case 4: {
435 pretty_number (num, FLT_DIG + 1, val);
436 return;
437 }
438 case 8: {
439 pretty_number (num, DBL_DIG + 1, val);
440 return;
441 }
442 case 16: {
443 pretty_number (num, FLT128_DIG + 1, val);
444 bufcat (num, "q", RECLN);
445 return;
446 }
447 case 32: {
448 pretty_number (num, FLT256_DIG, val);
449 return;
450 }
451 }
452 }
453
454 void pretty_complex (char *num, int_4 prec, complex_64 cval)
455 {
456 RECORD RE, IM;
457 switch (prec) {
458 case 8: {
459 pretty_real (RE, 4, cxre (cval));
460 pretty_real (IM, 4, cxim (cval));
461 _srecordf (num, "CMPLXF (%s, %s)", RE, IM);
462 return;
463 }
464 case 16: {
465 pretty_real (RE, 8, cxre (cval));
466 pretty_real (IM, 8, cxim (cval));
467 _srecordf (num, "CMPLX (%s, %s)", RE, IM);
468 return;
469 }
470 case 32: {
471 pretty_real (RE, 16, cxre (cval));
472 pretty_real (IM, 16, cxim (cval));
473 _srecordf (num, "CMPLXQ (%s, %s)", RE, IM);
474 return;
475 }
476 }
477 }
478
479 int_4 fold_intrinsic (INTRINS *F, EXPR *lhs, EXPR *rhs)
480 {
481 if (lhs->variant != EXPR_CONST) {
482 return FALSE;
483 } else if (!valid_expr (lhs)) {
484 return FALSE;
485 } else if (rhs != NO_EXPR && rhs->variant != EXPR_CONST) {
486 return FALSE;
487 } else if (rhs != NO_EXPR && !valid_expr (rhs)) {
488 return FALSE;
489 } else if (F->f3 != NULL && rhs == NO_EXPR) {
490 complex_64 lval;
491 if (! (accept_mode (lhs->mode.type, lhs->mode.len, COMPLEX, 32) && calc_complex (lhs->str, &lval))) {
492 return FALSE;
493 }
494 complex_64 cval = (F->f3) (lval);
495 pretty_complex (lhs->str, F->alen, cval);
496 return TRUE;
497 } else {
498 RECORD num;
499 num[0] = '\0';
500 real_32 lval;
501 if (! (accept_mode (lhs->mode.type, lhs->mode.len, REAL, 16) && calc_real (lhs->str, &lval))) {
502 return FALSE;
503 }
504 if (F->f1 != NULL) {
505 // Single-argumenters.
506 pretty_real (num, 32, (F->f1) (lval));
507 }
508 if (rhs != NO_EXPR) {
509 real_32 rval;
510 if (! (accept_mode (rhs->mode.type, rhs->mode.len, REAL, 16) && calc_real (rhs->str, &rval))) {
511 return FALSE;
512 }
513 if (F->f2 != NULL) {
514 // Two-argumenters.
515 pretty_real (num, 32, (F->f2) (lval, rval));
516 }
517 }
518 if (strlen (num) > 0) {
519 strcpy (lhs->str, num);
520 return TRUE;
521 } else {
522 return FALSE;
523 }
524 }
525 }
526
527 int_4 fold_expr (EXPR *reg, int_4 expect)
528 {
529 if (reg->variant != EXPR_CONST) {
530 return FALSE;
531 } else if (!valid_expr (reg)) {
532 return FALSE;
533 } else {
534 if ((reg->mode.type == INTEGER || (reg->mode.type == REAL && reg->mode.len <= 16))) {
535 real_32 rval;
536 if (calc_real (reg->str, &rval)) {
537 if (reg->mode.type == INTEGER || expect == INTEGER) {
538 reg->mode.type = INTEGER;
539 reg->mode.len = 4;
540 _srecordf (reg->str, "%d", _xint4 (rval));
541 } else {
542 RECORD z;
543 RECCLR (z);
544 pretty_real (z, reg->mode.len, rval);
545 strcpy (reg->str, z);
546 }
547 return TRUE;
548 } else {
549 return FALSE;
550 }
551 } else if (reg->mode.type == COMPLEX && reg->mode.len <= 32) {
552 complex_64 cval;
553 if (calc_complex (reg->str, &cval)) {
554 pretty_complex (reg->str, reg->mode.len, cval);
555 return TRUE;
556 } else {
557 return FALSE;
558 }
559 } else {
560 return FALSE;
561 }
562 }
563 }
© 2002-2025 J.M. van der Veer (jmvdveer@xs4all.nl)
|