source: src/Common/Eval.cc@ 3c714ad

Last change on this file since 3c714ad was 8f557161, checked in by Michael Brooks <mlbrooks@…>, 2 years 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.