source: src/Common/Eval.cc @ 4269d1b

Last change on this file since 4269d1b was 8f557161, checked in by Michael Brooks <mlbrooks@…>, 17 months ago

Clarify and fix accuracy in eval public API, on reporting "unable to evaluate."

While the eval internals always used the flag pair valid and cfavalid almost correctly, the public interface exposed the outcome as a single flag, and the various interpretations of this flag were a mess.

Old cfacc treated sizeof(whatever) in some contexts as "known to be zero," which is wrong.
The generally correct answer, which new cfacc now uses is, "unknown now, but GCC will see it as a fine constant."
New tests/eval.cfa captures this fact: is runnable and would fail on old cfacc; it passes with new cfacc.

  • Property mode set to 100644
File size: 9.9 KB
Line 
1//
2// Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo
3//
4// The contents of this file are covered under the licence agreement in the
5// file "LICENCE" distributed with Cforall.
6//
7// Eval.cc -- Evaluate parts of the ast at compile time.
8//
9// Author           : Richard C. Bilson
10// Created On       : Mon May 18 07:44:20 2015
11// Last Modified By : Peter A. Buhr
12// Last Modified On : Sat Aug  6 12:11:59 2022
13// Update Count     : 119
14//
15
16#include "Eval.h"
17
18#include <utility> // for pair
19
20#include "AST/Inspect.hpp"
21#include "Common/PassVisitor.h"
22#include "CodeGen/OperatorTable.h"                                              // access: OperatorInfo
23#include "AST/Pass.hpp"
24#include "InitTweak/InitTweak.h"
25#include "SynTree/Expression.h"
26
27//-------------------------------------------------------------
28// Old AST
29struct EvalOld : public WithShortCircuiting {
30        long long int value = 0;                                                        // compose the result of the constant expression
31        bool valid = true;                                                                      // true => constant expression and value is the result
32                                                                                                                // false => not constant expression, e.g., ++i
33        bool cfavalid = true;                                                           // true => constant expression and value computable
34                                                                                                                // false => constant expression but value not computable, e.g., sizeof(int)
35
36        void previsit( const BaseSyntaxNode * ) { visit_children = false; }
37        void postvisit( const BaseSyntaxNode * ) { valid = false; }
38
39        void postvisit( const SizeofExpr * ) {
40        }
41
42        void postvisit( const ConstantExpr * expr ) {
43                value = expr->intValue();
44        }
45
46        void postvisit( const CastExpr * expr ) {
47                auto arg = eval(expr->arg);
48                valid = arg.second;
49                value = arg.first;
50                // TODO: perform type conversion on value if valid
51        }
52
53        void postvisit( const VariableExpr * const expr ) {
54                if ( EnumInstType * inst = dynamic_cast<EnumInstType *>(expr->result) ) {
55                        if ( EnumDecl * decl = inst->baseEnum ) {
56                                if ( decl->valueOf( expr->var, value ) ) { // value filled by valueOf
57                                        return;
58                                }
59                        }
60                }
61                valid = false;
62        }
63
64        void postvisit( const ApplicationExpr * expr ) {
65                DeclarationWithType * function = InitTweak::getFunction(const_cast<ApplicationExpr *>(expr));
66                if ( ! function || function->linkage != LinkageSpec::Intrinsic ) { valid = false; return; }
67                const std::string & fname = function->name;
68                assertf( expr->args.size() == 1 || expr->args.size() == 2, "Intrinsic function with %zd arguments: %s", expr->args.size(), fname.c_str() );
69                std::pair<long long int, bool> arg1, arg2;
70                arg1 = eval(expr->args.front());
71                valid = valid && arg1.second;
72                if ( ! valid ) return;
73                if ( expr->args.size() == 2 ) {
74                        arg2 = eval(expr->args.back());
75                        valid = valid && arg2.second;
76                        if ( ! valid ) return;
77                }
78                if (fname == "?+?") {
79                        value = arg1.first + arg2.first;
80                } else if (fname == "?-?") {
81                        value = arg1.first - arg2.first;
82                } else if (fname == "?*?") {
83                        value = arg1.first * arg2.first;
84                } else if (fname == "?/?") {
85                        value = arg1.first / arg2.first;
86                } else if (fname == "?%?") {
87                        value = arg1.first % arg2.first;
88                } else {
89                        valid = false;
90                }
91                // TODO: implement other intrinsic functions
92        }
93};
94
95//-------------------------------------------------------------
96// New AST
97struct EvalNew : public ast::WithShortCircuiting {
98        Evaluation result = { 0, true, true };
99
100        void previsit( const ast::Node * ) { visit_children = false; }
101        void postvisit( const ast::Node * ) { result.isEvaluableInGCC = result.hasKnownValue = false; }
102
103        void postvisit( const ast::UntypedExpr * ) {
104                assertf( false, "UntypedExpr in constant expression evaluation" ); // FIX ME, resolve variable
105        }
106
107        void postvisit( const ast::ConstantExpr * expr ) {      // only handle int constants
108                result.knownValue = expr->intValue();
109                result.hasKnownValue = true;
110                result.isEvaluableInGCC = true;
111        }
112
113        void postvisit( const ast::SizeofExpr * ) {
114                result.hasKnownValue = false;
115                result.isEvaluableInGCC = true;
116        }
117
118        void postvisit( const ast::AlignofExpr * ) {
119                result.hasKnownValue = false;
120                result.isEvaluableInGCC = true;
121        }
122
123        void postvisit( const ast::OffsetofExpr * ) {
124                result.hasKnownValue = false;
125                result.isEvaluableInGCC = true;
126        }
127
128        void postvisit( const ast::LogicalExpr * expr ) {
129                Evaluation arg1, arg2;
130                arg1 = eval( expr->arg1 );
131                result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
132                if ( ! result.isEvaluableInGCC ) return;
133                arg2 = eval( expr->arg2 );
134                result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
135                if ( ! result.isEvaluableInGCC ) return;
136
137                result.hasKnownValue &= arg1.hasKnownValue;
138                result.hasKnownValue &= arg2.hasKnownValue;
139                if ( ! result.hasKnownValue ) return;
140
141                if ( expr->isAnd ) {
142                        result.knownValue = arg1.knownValue && arg2.knownValue;
143                } else {
144                        result.knownValue = arg1.knownValue || arg2.knownValue;
145                } // if
146        }
147
148        void postvisit( const ast::ConditionalExpr * expr ) {
149                Evaluation arg1, arg2, arg3;
150                arg1 = eval( expr->arg1 );
151                result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
152                if ( ! result.isEvaluableInGCC ) return;
153                arg2 = eval( expr->arg2 );
154                result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
155                if ( ! result.isEvaluableInGCC ) return;
156                arg3 = eval( expr->arg3 );
157                result.isEvaluableInGCC &= arg3.isEvaluableInGCC;
158                if ( ! result.isEvaluableInGCC ) return;
159
160                result.hasKnownValue &= arg1.hasKnownValue;
161                result.hasKnownValue &= arg2.hasKnownValue;
162                result.hasKnownValue &= arg3.hasKnownValue;
163                if ( ! result.hasKnownValue ) return;
164
165                result.knownValue = arg1.knownValue ? arg2.knownValue : arg3.knownValue;
166        }
167
168        void postvisit( const ast::CastExpr * expr ) {         
169                // cfa-cc generates a cast before every constant and many other places, e.g., (int)3,
170                // so we must use the value from the cast argument, even though we lack any basis for evaluating wraparound effects, etc
171                result = eval(expr->arg);
172        }
173
174        void postvisit( const ast::VariableExpr * expr ) {
175                result.hasKnownValue = false;
176                result.isEvaluableInGCC = false;
177                if ( const ast::EnumInstType * inst = dynamic_cast<const ast::EnumInstType *>(expr->result.get()) ) {
178                        if ( const ast::EnumDecl * decl = inst->base ) {
179                                result.isEvaluableInGCC = true;
180                                result.hasKnownValue = decl->valueOf( expr->var, result.knownValue ); // result.knownValue filled by valueOf
181                        }
182                }
183        }
184
185        void postvisit( const ast::ApplicationExpr * expr ) {
186                const ast::DeclWithType * function = ast::getFunction(expr);
187                if ( ! function || function->linkage != ast::Linkage::Intrinsic ) { 
188                        result.isEvaluableInGCC = false;
189                        result.hasKnownValue = false;
190                        return;
191                }
192                const std::string & fname = function->name;
193                assertf( expr->args.size() == 1 || expr->args.size() == 2, "Intrinsic function with %zd arguments: %s", expr->args.size(), fname.c_str() );
194
195                if ( expr->args.size() == 1 ) {
196                        // pre/postfix operators ++ and -- => assignment, which is not constant
197                        Evaluation arg1;
198                        arg1 = eval(expr->args.front());
199                        result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
200                        if ( ! result.isEvaluableInGCC ) return;
201
202                        result.hasKnownValue &= arg1.hasKnownValue;
203                        if ( ! result.hasKnownValue ) return;
204
205                        if (fname == "+?") {
206                                result.knownValue = arg1.knownValue;
207                        } else if (fname == "-?") {
208                                result.knownValue = -arg1.knownValue;
209                        } else if (fname == "~?") {
210                                result.knownValue = ~arg1.knownValue;
211                        } else if (fname == "!?") {
212                                result.knownValue = ! arg1.knownValue;
213                        } else {
214                                result.isEvaluableInGCC = false;
215                                result.hasKnownValue = false;
216                        } // if
217                } else { // => expr->args.size() == 2
218                        // infix assignment operators => assignment, which is not constant
219                        Evaluation arg1, arg2;
220                        arg1 = eval(expr->args.front());
221                        result.isEvaluableInGCC &= arg1.isEvaluableInGCC;
222                        if ( ! result.isEvaluableInGCC ) return;
223                        arg2 = eval(expr->args.back());
224                        result.isEvaluableInGCC &= arg2.isEvaluableInGCC;
225                        if ( ! result.isEvaluableInGCC ) return;
226
227                        result.hasKnownValue &= arg1.hasKnownValue;
228                        result.hasKnownValue &= arg2.hasKnownValue;
229                        if ( ! result.hasKnownValue ) return;
230
231                        if (fname == "?+?") {
232                                result.knownValue = arg1.knownValue + arg2.knownValue;
233                        } else if (fname == "?-?") {
234                                result.knownValue = arg1.knownValue - arg2.knownValue;
235                        } else if (fname == "?*?") {
236                                result.knownValue = arg1.knownValue * arg2.knownValue;
237                        } else if (fname == "?/?") {
238                                if ( arg2.knownValue ) result.knownValue = arg1.knownValue / arg2.knownValue;
239                        } else if (fname == "?%?") {
240                                if ( arg2.knownValue ) result.knownValue = arg1.knownValue % arg2.knownValue;
241                        } else if (fname == "?<<?") {
242                                result.knownValue = arg1.knownValue << arg2.knownValue;
243                        } else if (fname == "?>>?") {
244                                result.knownValue = arg1.knownValue >> arg2.knownValue;
245                        } else if (fname == "?<?") {
246                                result.knownValue = arg1.knownValue < arg2.knownValue;
247                        } else if (fname == "?>?") {
248                                result.knownValue = arg1.knownValue > arg2.knownValue;
249                        } else if (fname == "?<=?") {
250                                result.knownValue = arg1.knownValue <= arg2.knownValue;
251                        } else if (fname == "?>=?") {
252                                result.knownValue = arg1.knownValue >= arg2.knownValue;
253                        } else if (fname == "?==?") {
254                                result.knownValue = arg1.knownValue == arg2.knownValue;
255                        } else if (fname == "?!=?") {
256                                result.knownValue = arg1.knownValue != arg2.knownValue;
257                        } else if (fname == "?&?") {
258                                result.knownValue = arg1.knownValue & arg2.knownValue;
259                        } else if (fname == "?^?") {
260                                result.knownValue = arg1.knownValue ^ arg2.knownValue;
261                        } else if (fname == "?|?") {
262                                result.knownValue = arg1.knownValue | arg2.knownValue;
263                        } else {
264                                result.isEvaluableInGCC = false;
265                                result.hasKnownValue = false;
266                        }
267                } // if
268                // TODO: implement other intrinsic functions
269        }
270};
271
272std::pair<long long int, bool> eval( const Expression * expr ) {
273        PassVisitor<EvalOld> ev;
274        if ( expr ) {
275                expr->accept( ev );
276                return std::make_pair( ev.pass.value, ev.pass.valid );
277        } else {
278                return std::make_pair( 0, false );
279        }
280}
281
282Evaluation eval( const ast::Expr * expr ) {
283        if ( expr ) {
284
285                return ast::Pass<EvalNew>::read(expr);
286                // Evaluation ret = ast::Pass<EvalNew>::read(expr);
287                // ret.knownValue = 777;
288                // return ret;
289
290        } else {
291                return { 0, false, false };
292        }
293}
294
295// Local Variables: //
296// tab-width: 4 //
297// mode: c++ //
298// compile-command: "make install" //
299// End: //
Note: See TracBrowser for help on using the repository browser.