1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/vm/adlc/dfa.cpp Wed Apr 27 01:25:04 2016 +0800 1.3 @@ -0,0 +1,1041 @@ 1.4 +/* 1.5 + * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + * 1.26 + */ 1.27 + 1.28 +// DFA.CPP - Method definitions for outputting the matcher DFA from ADLC 1.29 +#include "adlc.hpp" 1.30 + 1.31 +//---------------------------Switches for debugging output--------------------- 1.32 +static bool debug_output = false; 1.33 +static bool debug_output1 = false; // top level chain rules 1.34 + 1.35 +//---------------------------Access to internals of class State---------------- 1.36 +static const char *sLeft = "_kids[0]"; 1.37 +static const char *sRight = "_kids[1]"; 1.38 + 1.39 +//---------------------------DFA productions----------------------------------- 1.40 +static const char *dfa_production = "DFA_PRODUCTION"; 1.41 +static const char *dfa_production_set_valid = "DFA_PRODUCTION__SET_VALID"; 1.42 + 1.43 +//---------------------------Production State---------------------------------- 1.44 +static const char *knownInvalid = "knownInvalid"; // The result does NOT have a rule defined 1.45 +static const char *knownValid = "knownValid"; // The result must be produced by a rule 1.46 +static const char *unknownValid = "unknownValid"; // Unknown (probably due to a child or predicate constraint) 1.47 + 1.48 +static const char *noConstraint = "noConstraint"; // No constraints seen so far 1.49 +static const char *hasConstraint = "hasConstraint"; // Within the first constraint 1.50 + 1.51 + 1.52 +//------------------------------Production------------------------------------ 1.53 +// Track the status of productions for a particular result 1.54 +class Production { 1.55 +public: 1.56 + const char *_result; 1.57 + const char *_constraint; 1.58 + const char *_valid; 1.59 + Expr *_cost_lb; // Cost lower bound for this production 1.60 + Expr *_cost_ub; // Cost upper bound for this production 1.61 + 1.62 +public: 1.63 + Production(const char *result, const char *constraint, const char *valid); 1.64 + ~Production() {}; 1.65 + 1.66 + void initialize(); // reset to be an empty container 1.67 + 1.68 + const char *valid() const { return _valid; } 1.69 + Expr *cost_lb() const { return (Expr *)_cost_lb; } 1.70 + Expr *cost_ub() const { return (Expr *)_cost_ub; } 1.71 + 1.72 + void print(); 1.73 +}; 1.74 + 1.75 + 1.76 +//------------------------------ProductionState-------------------------------- 1.77 +// Track the status of all production rule results 1.78 +// Reset for each root opcode (e.g., Op_RegI, Op_AddI, ...) 1.79 +class ProductionState { 1.80 +private: 1.81 + Dict _production; // map result of production, char*, to information or NULL 1.82 + const char *_constraint; 1.83 + 1.84 +public: 1.85 + // cmpstr does string comparisions. hashstr computes a key. 1.86 + ProductionState(Arena *arena) : _production(cmpstr, hashstr, arena) { initialize(); }; 1.87 + ~ProductionState() { }; 1.88 + 1.89 + void initialize(); // reset local and dictionary state 1.90 + 1.91 + const char *constraint(); 1.92 + void set_constraint(const char *constraint); // currently working inside of constraints 1.93 + 1.94 + const char *valid(const char *result); // unknownValid, or status for this production 1.95 + void set_valid(const char *result); // if not constrained, set status to knownValid 1.96 + 1.97 + Expr *cost_lb(const char *result); 1.98 + Expr *cost_ub(const char *result); 1.99 + void set_cost_bounds(const char *result, const Expr *cost, bool has_state_check, bool has_cost_check); 1.100 + 1.101 + // Return the Production associated with the result, 1.102 + // or create a new Production and insert it into the dictionary. 1.103 + Production *getProduction(const char *result); 1.104 + 1.105 + void print(); 1.106 + 1.107 +private: 1.108 + // Disable public use of constructor, copy-ctor, ... 1.109 + ProductionState( ) : _production(cmpstr, hashstr, Form::arena) { assert( false, "NotImplemented"); }; 1.110 + ProductionState( const ProductionState & ) : _production(cmpstr, hashstr, Form::arena) { assert( false, "NotImplemented"); }; // Deep-copy 1.111 +}; 1.112 + 1.113 + 1.114 +//---------------------------Helper Functions---------------------------------- 1.115 +// cost_check template: 1.116 +// 1) if (STATE__NOT_YET_VALID(EBXREGI) || _cost[EBXREGI] > c) { 1.117 +// 2) DFA_PRODUCTION__SET_VALID(EBXREGI, cmovI_memu_rule, c) 1.118 +// 3) } 1.119 +// 1.120 +static void cost_check(FILE *fp, const char *spaces, 1.121 + const char *arrayIdx, const Expr *cost, const char *rule, ProductionState &status) { 1.122 + bool state_check = false; // true if this production needs to check validity 1.123 + bool cost_check = false; // true if this production needs to check cost 1.124 + bool cost_is_above_upper_bound = false; // true if this production is unnecessary due to high cost 1.125 + bool cost_is_below_lower_bound = false; // true if this production replaces a higher cost production 1.126 + 1.127 + // Get information about this production 1.128 + const Expr *previous_ub = status.cost_ub(arrayIdx); 1.129 + if( !previous_ub->is_unknown() ) { 1.130 + if( previous_ub->less_than_or_equal(cost) ) { 1.131 + cost_is_above_upper_bound = true; 1.132 + if( debug_output ) { fprintf(fp, "// Previous rule with lower cost than: %s === %s_rule costs %s\n", arrayIdx, rule, cost->as_string()); } 1.133 + } 1.134 + } 1.135 + 1.136 + const Expr *previous_lb = status.cost_lb(arrayIdx); 1.137 + if( !previous_lb->is_unknown() ) { 1.138 + if( cost->less_than_or_equal(previous_lb) ) { 1.139 + cost_is_below_lower_bound = true; 1.140 + if( debug_output ) { fprintf(fp, "// Previous rule with higher cost\n"); } 1.141 + } 1.142 + } 1.143 + 1.144 + // line 1) 1.145 + // Check for validity and compare to other match costs 1.146 + const char *validity_check = status.valid(arrayIdx); 1.147 + if( validity_check == unknownValid ) { 1.148 + fprintf(fp, "%sif (STATE__NOT_YET_VALID(%s) || _cost[%s] > %s) {\n", spaces, arrayIdx, arrayIdx, cost->as_string()); 1.149 + state_check = true; 1.150 + cost_check = true; 1.151 + } 1.152 + else if( validity_check == knownInvalid ) { 1.153 + if( debug_output ) { fprintf(fp, "%s// %s KNOWN_INVALID \n", spaces, arrayIdx); } 1.154 + } 1.155 + else if( validity_check == knownValid ) { 1.156 + if( cost_is_above_upper_bound ) { 1.157 + // production cost is known to be too high. 1.158 + return; 1.159 + } else if( cost_is_below_lower_bound ) { 1.160 + // production will unconditionally overwrite a previous production that had higher cost 1.161 + } else { 1.162 + fprintf(fp, "%sif ( /* %s KNOWN_VALID || */ _cost[%s] > %s) {\n", spaces, arrayIdx, arrayIdx, cost->as_string()); 1.163 + cost_check = true; 1.164 + } 1.165 + } 1.166 + 1.167 + // line 2) 1.168 + // no need to set State vector if our state is knownValid 1.169 + const char *production = (validity_check == knownValid) ? dfa_production : dfa_production_set_valid; 1.170 + fprintf(fp, "%s %s(%s, %s_rule, %s)", spaces, production, arrayIdx, rule, cost->as_string() ); 1.171 + if( validity_check == knownValid ) { 1.172 + if( cost_is_below_lower_bound ) { fprintf(fp, "\t // overwrites higher cost rule"); } 1.173 + } 1.174 + fprintf(fp, "\n"); 1.175 + 1.176 + // line 3) 1.177 + if( cost_check || state_check ) { 1.178 + fprintf(fp, "%s}\n", spaces); 1.179 + } 1.180 + 1.181 + status.set_cost_bounds(arrayIdx, cost, state_check, cost_check); 1.182 + 1.183 + // Update ProductionState 1.184 + if( validity_check != knownValid ) { 1.185 + // set State vector if not previously known 1.186 + status.set_valid(arrayIdx); 1.187 + } 1.188 +} 1.189 + 1.190 + 1.191 +//---------------------------child_test---------------------------------------- 1.192 +// Example: 1.193 +// STATE__VALID_CHILD(_kids[0], FOO) && STATE__VALID_CHILD(_kids[1], BAR) 1.194 +// Macro equivalent to: _kids[0]->valid(FOO) && _kids[1]->valid(BAR) 1.195 +// 1.196 +static void child_test(FILE *fp, MatchList &mList) { 1.197 + if (mList._lchild) { // If left child, check it 1.198 + const char* lchild_to_upper = ArchDesc::getMachOperEnum(mList._lchild); 1.199 + fprintf(fp, "STATE__VALID_CHILD(_kids[0], %s)", lchild_to_upper); 1.200 + delete[] lchild_to_upper; 1.201 + } 1.202 + if (mList._lchild && mList._rchild) { // If both, add the "&&" 1.203 + fprintf(fp, " && "); 1.204 + } 1.205 + if (mList._rchild) { // If right child, check it 1.206 + const char* rchild_to_upper = ArchDesc::getMachOperEnum(mList._rchild); 1.207 + fprintf(fp, "STATE__VALID_CHILD(_kids[1], %s)", rchild_to_upper); 1.208 + delete[] rchild_to_upper; 1.209 + } 1.210 +} 1.211 + 1.212 +//---------------------------calc_cost----------------------------------------- 1.213 +// Example: 1.214 +// unsigned int c = _kids[0]->_cost[FOO] + _kids[1]->_cost[BAR] + 5; 1.215 +// 1.216 +Expr *ArchDesc::calc_cost(FILE *fp, const char *spaces, MatchList &mList, ProductionState &status) { 1.217 + fprintf(fp, "%sunsigned int c = ", spaces); 1.218 + Expr *c = new Expr("0"); 1.219 + if (mList._lchild) { // If left child, add it in 1.220 + const char* lchild_to_upper = ArchDesc::getMachOperEnum(mList._lchild); 1.221 + sprintf(Expr::buffer(), "_kids[0]->_cost[%s]", lchild_to_upper); 1.222 + c->add(Expr::buffer()); 1.223 + delete[] lchild_to_upper; 1.224 +} 1.225 + if (mList._rchild) { // If right child, add it in 1.226 + const char* rchild_to_upper = ArchDesc::getMachOperEnum(mList._rchild); 1.227 + sprintf(Expr::buffer(), "_kids[1]->_cost[%s]", rchild_to_upper); 1.228 + c->add(Expr::buffer()); 1.229 + delete[] rchild_to_upper; 1.230 + } 1.231 + // Add in cost of this rule 1.232 + const char *mList_cost = mList.get_cost(); 1.233 + c->add(mList_cost, *this); 1.234 + 1.235 + fprintf(fp, "%s;\n", c->as_string()); 1.236 + c->set_external_name("c"); 1.237 + return c; 1.238 +} 1.239 + 1.240 + 1.241 +//---------------------------gen_match----------------------------------------- 1.242 +void ArchDesc::gen_match(FILE *fp, MatchList &mList, ProductionState &status, Dict &operands_chained_from) { 1.243 + const char *spaces4 = " "; 1.244 + const char *spaces6 = " "; 1.245 + 1.246 + fprintf(fp, "%s", spaces4); 1.247 + // Only generate child tests if this is not a leaf node 1.248 + bool has_child_constraints = mList._lchild || mList._rchild; 1.249 + const char *predicate_test = mList.get_pred(); 1.250 + if (has_child_constraints || predicate_test) { 1.251 + // Open the child-and-predicate-test braces 1.252 + fprintf(fp, "if( "); 1.253 + status.set_constraint(hasConstraint); 1.254 + child_test(fp, mList); 1.255 + // Only generate predicate test if one exists for this match 1.256 + if (predicate_test) { 1.257 + if (has_child_constraints) { 1.258 + fprintf(fp," &&\n"); 1.259 + } 1.260 + fprintf(fp, "%s %s", spaces6, predicate_test); 1.261 + } 1.262 + // End of outer tests 1.263 + fprintf(fp," ) "); 1.264 + } else { 1.265 + // No child or predicate test needed 1.266 + status.set_constraint(noConstraint); 1.267 + } 1.268 + 1.269 + // End of outer tests 1.270 + fprintf(fp,"{\n"); 1.271 + 1.272 + // Calculate cost of this match 1.273 + const Expr *cost = calc_cost(fp, spaces6, mList, status); 1.274 + // Check against other match costs, and update cost & rule vectors 1.275 + cost_check(fp, spaces6, ArchDesc::getMachOperEnum(mList._resultStr), cost, mList._opcode, status); 1.276 + 1.277 + // If this is a member of an operand class, update the class cost & rule 1.278 + expand_opclass( fp, spaces6, cost, mList._resultStr, status); 1.279 + 1.280 + // Check if this rule should be used to generate the chains as well. 1.281 + const char *rule = /* set rule to "Invalid" for internal operands */ 1.282 + strcmp(mList._opcode,mList._resultStr) ? mList._opcode : "Invalid"; 1.283 + 1.284 + // If this rule produces an operand which has associated chain rules, 1.285 + // update the operands with the chain rule + this rule cost & this rule. 1.286 + chain_rule(fp, spaces6, mList._resultStr, cost, rule, operands_chained_from, status); 1.287 + 1.288 + // Close the child-and-predicate-test braces 1.289 + fprintf(fp, " }\n"); 1.290 + 1.291 +} 1.292 + 1.293 + 1.294 +//---------------------------expand_opclass------------------------------------ 1.295 +// Chain from one result_type to all other members of its operand class 1.296 +void ArchDesc::expand_opclass(FILE *fp, const char *indent, const Expr *cost, 1.297 + const char *result_type, ProductionState &status) { 1.298 + const Form *form = _globalNames[result_type]; 1.299 + OperandForm *op = form ? form->is_operand() : NULL; 1.300 + if( op && op->_classes.count() > 0 ) { 1.301 + if( debug_output ) { fprintf(fp, "// expand operand classes for operand: %s \n", (char *)op->_ident ); } // %%%%% Explanation 1.302 + // Iterate through all operand classes which include this operand 1.303 + op->_classes.reset(); 1.304 + const char *oclass; 1.305 + // Expr *cCost = new Expr(cost); 1.306 + while( (oclass = op->_classes.iter()) != NULL ) 1.307 + // Check against other match costs, and update cost & rule vectors 1.308 + cost_check(fp, indent, ArchDesc::getMachOperEnum(oclass), cost, result_type, status); 1.309 + } 1.310 +} 1.311 + 1.312 +//---------------------------chain_rule---------------------------------------- 1.313 +// Starting at 'operand', check if we know how to automatically generate other results 1.314 +void ArchDesc::chain_rule(FILE *fp, const char *indent, const char *operand, 1.315 + const Expr *icost, const char *irule, Dict &operands_chained_from, ProductionState &status) { 1.316 + 1.317 + // Check if we have already generated chains from this starting point 1.318 + if( operands_chained_from[operand] != NULL ) { 1.319 + return; 1.320 + } else { 1.321 + operands_chained_from.Insert( operand, operand); 1.322 + } 1.323 + if( debug_output ) { fprintf(fp, "// chain rules starting from: %s and %s \n", (char *)operand, (char *)irule); } // %%%%% Explanation 1.324 + 1.325 + ChainList *lst = (ChainList *)_chainRules[operand]; 1.326 + if (lst) { 1.327 + // printf("\nChain from <%s> at cost #%s\n",operand, icost ? icost : "_"); 1.328 + const char *result, *cost, *rule; 1.329 + for(lst->reset(); (lst->iter(result,cost,rule)) == true; ) { 1.330 + // Do not generate operands that are already available 1.331 + if( operands_chained_from[result] != NULL ) { 1.332 + continue; 1.333 + } else { 1.334 + // Compute the cost for previous match + chain_rule_cost 1.335 + // total_cost = icost + cost; 1.336 + Expr *total_cost = icost->clone(); // icost + cost 1.337 + total_cost->add(cost, *this); 1.338 + 1.339 + // Check for transitive chain rules 1.340 + Form *form = (Form *)_globalNames[rule]; 1.341 + if ( ! form->is_instruction()) { 1.342 + // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); 1.343 + // Check against other match costs, and update cost & rule vectors 1.344 + const char *reduce_rule = strcmp(irule,"Invalid") ? irule : rule; 1.345 + cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, reduce_rule, status); 1.346 + chain_rule(fp, indent, result, total_cost, irule, operands_chained_from, status); 1.347 + } else { 1.348 + // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); 1.349 + // Check against other match costs, and update cost & rule vectors 1.350 + cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, rule, status); 1.351 + chain_rule(fp, indent, result, total_cost, rule, operands_chained_from, status); 1.352 + } 1.353 + 1.354 + // If this is a member of an operand class, update class cost & rule 1.355 + expand_opclass( fp, indent, total_cost, result, status ); 1.356 + } 1.357 + } 1.358 + } 1.359 +} 1.360 + 1.361 +//---------------------------prune_matchlist----------------------------------- 1.362 +// Check for duplicate entries in a matchlist, and prune out the higher cost 1.363 +// entry. 1.364 +void ArchDesc::prune_matchlist(Dict &minimize, MatchList &mlist) { 1.365 + 1.366 +} 1.367 + 1.368 +//---------------------------buildDFA------------------------------------------ 1.369 +// DFA is a large switch with case statements for each ideal opcode encountered 1.370 +// in any match rule in the ad file. Each case has a series of if's to handle 1.371 +// the match or fail decisions. The matches test the cost function of that 1.372 +// rule, and prune any cases which are higher cost for the same reduction. 1.373 +// In order to generate the DFA we walk the table of ideal opcode/MatchList 1.374 +// pairs generated by the ADLC front end to build the contents of the case 1.375 +// statements (a series of if statements). 1.376 +void ArchDesc::buildDFA(FILE* fp) { 1.377 + int i; 1.378 + // Remember operands that are the starting points for chain rules. 1.379 + // Prevent cycles by checking if we have already generated chain. 1.380 + Dict operands_chained_from(cmpstr, hashstr, Form::arena); 1.381 + 1.382 + // Hash inputs to match rules so that final DFA contains only one entry for 1.383 + // each match pattern which is the low cost entry. 1.384 + Dict minimize(cmpstr, hashstr, Form::arena); 1.385 + 1.386 + // Track status of dfa for each resulting production 1.387 + // reset for each ideal root. 1.388 + ProductionState status(Form::arena); 1.389 + 1.390 + // Output the start of the DFA method into the output file 1.391 + 1.392 + fprintf(fp, "\n"); 1.393 + fprintf(fp, "//------------------------- Source -----------------------------------------\n"); 1.394 + // Do not put random source code into the DFA. 1.395 + // If there are constants which need sharing, put them in "source_hpp" forms. 1.396 + // _source.output(fp); 1.397 + fprintf(fp, "\n"); 1.398 + fprintf(fp, "//------------------------- Attributes -------------------------------------\n"); 1.399 + _attributes.output(fp); 1.400 + fprintf(fp, "\n"); 1.401 + fprintf(fp, "//------------------------- Macros -----------------------------------------\n"); 1.402 + // #define DFA_PRODUCTION(result, rule, cost)\ 1.403 + // _cost[ (result) ] = cost; _rule[ (result) ] = rule; 1.404 + fprintf(fp, "#define %s(result, rule, cost)\\\n", dfa_production); 1.405 + fprintf(fp, " _cost[ (result) ] = cost; _rule[ (result) ] = rule;\n"); 1.406 + fprintf(fp, "\n"); 1.407 + 1.408 + // #define DFA_PRODUCTION__SET_VALID(result, rule, cost)\ 1.409 + // DFA_PRODUCTION( (result), (rule), (cost) ); STATE__SET_VALID( (result) ); 1.410 + fprintf(fp, "#define %s(result, rule, cost)\\\n", dfa_production_set_valid); 1.411 + fprintf(fp, " %s( (result), (rule), (cost) ); STATE__SET_VALID( (result) );\n", dfa_production); 1.412 + fprintf(fp, "\n"); 1.413 + 1.414 + fprintf(fp, "//------------------------- DFA --------------------------------------------\n"); 1.415 + 1.416 + fprintf(fp, 1.417 +"// DFA is a large switch with case statements for each ideal opcode encountered\n" 1.418 +"// in any match rule in the ad file. Each case has a series of if's to handle\n" 1.419 +"// the match or fail decisions. The matches test the cost function of that\n" 1.420 +"// rule, and prune any cases which are higher cost for the same reduction.\n" 1.421 +"// In order to generate the DFA we walk the table of ideal opcode/MatchList\n" 1.422 +"// pairs generated by the ADLC front end to build the contents of the case\n" 1.423 +"// statements (a series of if statements).\n" 1.424 +); 1.425 + fprintf(fp, "\n"); 1.426 + fprintf(fp, "\n"); 1.427 + if (_dfa_small) { 1.428 + // Now build the individual routines just like the switch entries in large version 1.429 + // Iterate over the table of MatchLists, start at first valid opcode of 1 1.430 + for (i = 1; i < _last_opcode; i++) { 1.431 + if (_mlistab[i] == NULL) continue; 1.432 + // Generate the routine header statement for this opcode 1.433 + fprintf(fp, "void State::_sub_Op_%s(const Node *n){\n", NodeClassNames[i]); 1.434 + // Generate body. Shared for both inline and out-of-line version 1.435 + gen_dfa_state_body(fp, minimize, status, operands_chained_from, i); 1.436 + // End of routine 1.437 + fprintf(fp, "}\n"); 1.438 + } 1.439 + } 1.440 + fprintf(fp, "bool State::DFA"); 1.441 + fprintf(fp, "(int opcode, const Node *n) {\n"); 1.442 + fprintf(fp, " switch(opcode) {\n"); 1.443 + 1.444 + // Iterate over the table of MatchLists, start at first valid opcode of 1 1.445 + for (i = 1; i < _last_opcode; i++) { 1.446 + if (_mlistab[i] == NULL) continue; 1.447 + // Generate the case statement for this opcode 1.448 + if (_dfa_small) { 1.449 + fprintf(fp, " case Op_%s: { _sub_Op_%s(n);\n", NodeClassNames[i], NodeClassNames[i]); 1.450 + } else { 1.451 + fprintf(fp, " case Op_%s: {\n", NodeClassNames[i]); 1.452 + // Walk the list, compacting it 1.453 + gen_dfa_state_body(fp, minimize, status, operands_chained_from, i); 1.454 + } 1.455 + // Print the "break" 1.456 + fprintf(fp, " break;\n"); 1.457 + fprintf(fp, " }\n"); 1.458 + } 1.459 + 1.460 + // Generate the default case for switch(opcode) 1.461 + fprintf(fp, " \n"); 1.462 + fprintf(fp, " default:\n"); 1.463 + fprintf(fp, " tty->print(\"Default case invoked for: \\n\");\n"); 1.464 + fprintf(fp, " tty->print(\" opcode = %cd, \\\"%cs\\\"\\n\", opcode, NodeClassNames[opcode]);\n", '%', '%'); 1.465 + fprintf(fp, " return false;\n"); 1.466 + fprintf(fp, " }\n"); 1.467 + 1.468 + // Return status, indicating a successful match. 1.469 + fprintf(fp, " return true;\n"); 1.470 + // Generate the closing brace for method Matcher::DFA 1.471 + fprintf(fp, "}\n"); 1.472 + Expr::check_buffers(); 1.473 +} 1.474 + 1.475 + 1.476 +class dfa_shared_preds { 1.477 + enum { count = 4 }; 1.478 + 1.479 + static bool _found[count]; 1.480 + static const char* _type [count]; 1.481 + static const char* _var [count]; 1.482 + static const char* _pred [count]; 1.483 + 1.484 + static void check_index(int index) { assert( 0 <= index && index < count, "Invalid index"); } 1.485 + 1.486 + // Confirm that this is a separate sub-expression. 1.487 + // Only need to catch common cases like " ... && shared ..." 1.488 + // and avoid hazardous ones like "...->shared" 1.489 + static bool valid_loc(char *pred, char *shared) { 1.490 + // start of predicate is valid 1.491 + if( shared == pred ) return true; 1.492 + 1.493 + // Check previous character and recurse if needed 1.494 + char *prev = shared - 1; 1.495 + char c = *prev; 1.496 + switch( c ) { 1.497 + case ' ': 1.498 + case '\n': 1.499 + return dfa_shared_preds::valid_loc(pred, prev); 1.500 + case '!': 1.501 + case '(': 1.502 + case '<': 1.503 + case '=': 1.504 + return true; 1.505 + case '"': // such as: #line 10 "myfile.ad"\n mypredicate 1.506 + return true; 1.507 + case '|': 1.508 + if( prev != pred && *(prev-1) == '|' ) return true; 1.509 + case '&': 1.510 + if( prev != pred && *(prev-1) == '&' ) return true; 1.511 + default: 1.512 + return false; 1.513 + } 1.514 + 1.515 + return false; 1.516 + } 1.517 + 1.518 +public: 1.519 + 1.520 + static bool found(int index){ check_index(index); return _found[index]; } 1.521 + static void set_found(int index, bool val) { check_index(index); _found[index] = val; } 1.522 + static void reset_found() { 1.523 + for( int i = 0; i < count; ++i ) { _found[i] = false; } 1.524 + }; 1.525 + 1.526 + static const char* type(int index) { check_index(index); return _type[index]; } 1.527 + static const char* var (int index) { check_index(index); return _var [index]; } 1.528 + static const char* pred(int index) { check_index(index); return _pred[index]; } 1.529 + 1.530 + // Check each predicate in the MatchList for common sub-expressions 1.531 + static void cse_matchlist(MatchList *matchList) { 1.532 + for( MatchList *mList = matchList; mList != NULL; mList = mList->get_next() ) { 1.533 + Predicate* predicate = mList->get_pred_obj(); 1.534 + char* pred = mList->get_pred(); 1.535 + if( pred != NULL ) { 1.536 + for(int index = 0; index < count; ++index ) { 1.537 + const char *shared_pred = dfa_shared_preds::pred(index); 1.538 + const char *shared_pred_var = dfa_shared_preds::var(index); 1.539 + bool result = dfa_shared_preds::cse_predicate(predicate, shared_pred, shared_pred_var); 1.540 + if( result ) dfa_shared_preds::set_found(index, true); 1.541 + } 1.542 + } 1.543 + } 1.544 + } 1.545 + 1.546 + // If the Predicate contains a common sub-expression, replace the Predicate's 1.547 + // string with one that uses the variable name. 1.548 + static bool cse_predicate(Predicate* predicate, const char *shared_pred, const char *shared_pred_var) { 1.549 + bool result = false; 1.550 + char *pred = predicate->_pred; 1.551 + if( pred != NULL ) { 1.552 + char *new_pred = pred; 1.553 + for( char *shared_pred_loc = strstr(new_pred, shared_pred); 1.554 + shared_pred_loc != NULL && dfa_shared_preds::valid_loc(new_pred,shared_pred_loc); 1.555 + shared_pred_loc = strstr(new_pred, shared_pred) ) { 1.556 + // Do not modify the original predicate string, it is shared 1.557 + if( new_pred == pred ) { 1.558 + new_pred = strdup(pred); 1.559 + shared_pred_loc = strstr(new_pred, shared_pred); 1.560 + } 1.561 + // Replace shared_pred with variable name 1.562 + strncpy(shared_pred_loc, shared_pred_var, strlen(shared_pred_var)); 1.563 + } 1.564 + // Install new predicate 1.565 + if( new_pred != pred ) { 1.566 + predicate->_pred = new_pred; 1.567 + result = true; 1.568 + } 1.569 + } 1.570 + return result; 1.571 + } 1.572 + 1.573 + // Output the hoisted common sub-expression if we found it in predicates 1.574 + static void generate_cse(FILE *fp) { 1.575 + for(int j = 0; j < count; ++j ) { 1.576 + if( dfa_shared_preds::found(j) ) { 1.577 + const char *shared_pred_type = dfa_shared_preds::type(j); 1.578 + const char *shared_pred_var = dfa_shared_preds::var(j); 1.579 + const char *shared_pred = dfa_shared_preds::pred(j); 1.580 + fprintf(fp, " %s %s = %s;\n", shared_pred_type, shared_pred_var, shared_pred); 1.581 + } 1.582 + } 1.583 + } 1.584 +}; 1.585 +// shared predicates, _var and _pred entry should be the same length 1.586 +bool dfa_shared_preds::_found[dfa_shared_preds::count] 1.587 + = { false, false, false, false }; 1.588 +const char* dfa_shared_preds::_type[dfa_shared_preds::count] 1.589 + = { "int", "jlong", "intptr_t", "bool" }; 1.590 +const char* dfa_shared_preds::_var [dfa_shared_preds::count] 1.591 + = { "_n_get_int__", "_n_get_long__", "_n_get_intptr_t__", "Compile__current____select_24_bit_instr__" }; 1.592 +const char* dfa_shared_preds::_pred[dfa_shared_preds::count] 1.593 + = { "n->get_int()", "n->get_long()", "n->get_intptr_t()", "Compile::current()->select_24_bit_instr()" }; 1.594 + 1.595 + 1.596 +void ArchDesc::gen_dfa_state_body(FILE* fp, Dict &minimize, ProductionState &status, Dict &operands_chained_from, int i) { 1.597 + // Start the body of each Op_XXX sub-dfa with a clean state. 1.598 + status.initialize(); 1.599 + 1.600 + // Walk the list, compacting it 1.601 + MatchList* mList = _mlistab[i]; 1.602 + do { 1.603 + // Hash each entry using inputs as key and pointer as data. 1.604 + // If there is already an entry, keep the one with lower cost, and 1.605 + // remove the other one from the list. 1.606 + prune_matchlist(minimize, *mList); 1.607 + // Iterate 1.608 + mList = mList->get_next(); 1.609 + } while(mList != NULL); 1.610 + 1.611 + // Hoist previously specified common sub-expressions out of predicates 1.612 + dfa_shared_preds::reset_found(); 1.613 + dfa_shared_preds::cse_matchlist(_mlistab[i]); 1.614 + dfa_shared_preds::generate_cse(fp); 1.615 + 1.616 + mList = _mlistab[i]; 1.617 + 1.618 + // Walk the list again, generating code 1.619 + do { 1.620 + // Each match can generate its own chains 1.621 + operands_chained_from.Clear(); 1.622 + gen_match(fp, *mList, status, operands_chained_from); 1.623 + mList = mList->get_next(); 1.624 + } while(mList != NULL); 1.625 + // Fill in any chain rules which add instructions 1.626 + // These can generate their own chains as well. 1.627 + operands_chained_from.Clear(); // 1.628 + if( debug_output1 ) { fprintf(fp, "// top level chain rules for: %s \n", (char *)NodeClassNames[i]); } // %%%%% Explanation 1.629 + const Expr *zeroCost = new Expr("0"); 1.630 + chain_rule(fp, " ", (char *)NodeClassNames[i], zeroCost, "Invalid", 1.631 + operands_chained_from, status); 1.632 +} 1.633 + 1.634 + 1.635 + 1.636 +//------------------------------Expr------------------------------------------ 1.637 +Expr *Expr::_unknown_expr = NULL; 1.638 +char Expr::string_buffer[STRING_BUFFER_LENGTH]; 1.639 +char Expr::external_buffer[STRING_BUFFER_LENGTH]; 1.640 +bool Expr::_init_buffers = Expr::init_buffers(); 1.641 + 1.642 +Expr::Expr() { 1.643 + _external_name = NULL; 1.644 + _expr = "Invalid_Expr"; 1.645 + _min_value = Expr::Max; 1.646 + _max_value = Expr::Zero; 1.647 +} 1.648 +Expr::Expr(const char *cost) { 1.649 + _external_name = NULL; 1.650 + 1.651 + int intval = 0; 1.652 + if( cost == NULL ) { 1.653 + _expr = "0"; 1.654 + _min_value = Expr::Zero; 1.655 + _max_value = Expr::Zero; 1.656 + } 1.657 + else if( ADLParser::is_int_token(cost, intval) ) { 1.658 + _expr = cost; 1.659 + _min_value = intval; 1.660 + _max_value = intval; 1.661 + } 1.662 + else { 1.663 + assert( strcmp(cost,"0") != 0, "Recognize string zero as an int"); 1.664 + _expr = cost; 1.665 + _min_value = Expr::Zero; 1.666 + _max_value = Expr::Max; 1.667 + } 1.668 +} 1.669 + 1.670 +Expr::Expr(const char *name, const char *expression, int min_value, int max_value) { 1.671 + _external_name = name; 1.672 + _expr = expression ? expression : name; 1.673 + _min_value = min_value; 1.674 + _max_value = max_value; 1.675 + assert(_min_value >= 0 && _min_value <= Expr::Max, "value out of range"); 1.676 + assert(_max_value >= 0 && _max_value <= Expr::Max, "value out of range"); 1.677 +} 1.678 + 1.679 +Expr *Expr::clone() const { 1.680 + Expr *cost = new Expr(); 1.681 + cost->_external_name = _external_name; 1.682 + cost->_expr = _expr; 1.683 + cost->_min_value = _min_value; 1.684 + cost->_max_value = _max_value; 1.685 + 1.686 + return cost; 1.687 +} 1.688 + 1.689 +void Expr::add(const Expr *c) { 1.690 + // Do not update fields until all computation is complete 1.691 + const char *external = compute_external(this, c); 1.692 + const char *expr = compute_expr(this, c); 1.693 + int min_value = compute_min (this, c); 1.694 + int max_value = compute_max (this, c); 1.695 + 1.696 + _external_name = external; 1.697 + _expr = expr; 1.698 + _min_value = min_value; 1.699 + _max_value = max_value; 1.700 +} 1.701 + 1.702 +void Expr::add(const char *c) { 1.703 + Expr *cost = new Expr(c); 1.704 + add(cost); 1.705 +} 1.706 + 1.707 +void Expr::add(const char *c, ArchDesc &AD) { 1.708 + const Expr *e = AD.globalDefs()[c]; 1.709 + if( e != NULL ) { 1.710 + // use the value of 'c' defined in <arch>.ad 1.711 + add(e); 1.712 + } else { 1.713 + Expr *cost = new Expr(c); 1.714 + add(cost); 1.715 + } 1.716 +} 1.717 + 1.718 +const char *Expr::compute_external(const Expr *c1, const Expr *c2) { 1.719 + const char * result = NULL; 1.720 + 1.721 + // Preserve use of external name which has a zero value 1.722 + if( c1->_external_name != NULL ) { 1.723 + sprintf( string_buffer, "%s", c1->as_string()); 1.724 + if( !c2->is_zero() ) { 1.725 + strcat( string_buffer, "+"); 1.726 + strcat( string_buffer, c2->as_string()); 1.727 + } 1.728 + result = strdup(string_buffer); 1.729 + } 1.730 + else if( c2->_external_name != NULL ) { 1.731 + if( !c1->is_zero() ) { 1.732 + sprintf( string_buffer, "%s", c1->as_string()); 1.733 + strcat( string_buffer, " + "); 1.734 + } else { 1.735 + string_buffer[0] = '\0'; 1.736 + } 1.737 + strcat( string_buffer, c2->_external_name ); 1.738 + result = strdup(string_buffer); 1.739 + } 1.740 + return result; 1.741 +} 1.742 + 1.743 +const char *Expr::compute_expr(const Expr *c1, const Expr *c2) { 1.744 + if( !c1->is_zero() ) { 1.745 + sprintf( string_buffer, "%s", c1->_expr); 1.746 + if( !c2->is_zero() ) { 1.747 + strcat( string_buffer, "+"); 1.748 + strcat( string_buffer, c2->_expr); 1.749 + } 1.750 + } 1.751 + else if( !c2->is_zero() ) { 1.752 + sprintf( string_buffer, "%s", c2->_expr); 1.753 + } 1.754 + else { 1.755 + sprintf( string_buffer, "0"); 1.756 + } 1.757 + char *cost = strdup(string_buffer); 1.758 + 1.759 + return cost; 1.760 +} 1.761 + 1.762 +int Expr::compute_min(const Expr *c1, const Expr *c2) { 1.763 + int result = c1->_min_value + c2->_min_value; 1.764 + assert( result >= 0, "Invalid cost computation"); 1.765 + 1.766 + return result; 1.767 +} 1.768 + 1.769 +int Expr::compute_max(const Expr *c1, const Expr *c2) { 1.770 + int result = c1->_max_value + c2->_max_value; 1.771 + if( result < 0 ) { // check for overflow 1.772 + result = Expr::Max; 1.773 + } 1.774 + 1.775 + return result; 1.776 +} 1.777 + 1.778 +void Expr::print() const { 1.779 + if( _external_name != NULL ) { 1.780 + printf(" %s == (%s) === [%d, %d]\n", _external_name, _expr, _min_value, _max_value); 1.781 + } else { 1.782 + printf(" %s === [%d, %d]\n", _expr, _min_value, _max_value); 1.783 + } 1.784 +} 1.785 + 1.786 +void Expr::print_define(FILE *fp) const { 1.787 + assert( _external_name != NULL, "definition does not have a name"); 1.788 + assert( _min_value == _max_value, "Expect user definitions to have constant value"); 1.789 + fprintf(fp, "#define %s (%s) \n", _external_name, _expr); 1.790 + fprintf(fp, "// value == %d \n", _min_value); 1.791 +} 1.792 + 1.793 +void Expr::print_assert(FILE *fp) const { 1.794 + assert( _external_name != NULL, "definition does not have a name"); 1.795 + assert( _min_value == _max_value, "Expect user definitions to have constant value"); 1.796 + fprintf(fp, " assert( %s == %d, \"Expect (%s) to equal %d\");\n", _external_name, _min_value, _expr, _min_value); 1.797 +} 1.798 + 1.799 +Expr *Expr::get_unknown() { 1.800 + if( Expr::_unknown_expr == NULL ) { 1.801 + Expr::_unknown_expr = new Expr(); 1.802 + } 1.803 + 1.804 + return Expr::_unknown_expr; 1.805 +} 1.806 + 1.807 +bool Expr::init_buffers() { 1.808 + // Fill buffers with 0 1.809 + for( int i = 0; i < STRING_BUFFER_LENGTH; ++i ) { 1.810 + external_buffer[i] = '\0'; 1.811 + string_buffer[i] = '\0'; 1.812 + } 1.813 + 1.814 + return true; 1.815 +} 1.816 + 1.817 +bool Expr::check_buffers() { 1.818 + // returns 'true' if buffer use may have overflowed 1.819 + bool ok = true; 1.820 + for( int i = STRING_BUFFER_LENGTH - 100; i < STRING_BUFFER_LENGTH; ++i) { 1.821 + if( external_buffer[i] != '\0' || string_buffer[i] != '\0' ) { 1.822 + ok = false; 1.823 + assert( false, "Expr:: Buffer overflow"); 1.824 + } 1.825 + } 1.826 + 1.827 + return ok; 1.828 +} 1.829 + 1.830 + 1.831 +//------------------------------ExprDict--------------------------------------- 1.832 +// Constructor 1.833 +ExprDict::ExprDict( CmpKey cmp, Hash hash, Arena *arena ) 1.834 + : _expr(cmp, hash, arena), _defines() { 1.835 +} 1.836 +ExprDict::~ExprDict() { 1.837 +} 1.838 + 1.839 +// Return # of name-Expr pairs in dict 1.840 +int ExprDict::Size(void) const { 1.841 + return _expr.Size(); 1.842 +} 1.843 + 1.844 +// define inserts the given key-value pair into the dictionary, 1.845 +// and records the name in order for later output, ... 1.846 +const Expr *ExprDict::define(const char *name, Expr *expr) { 1.847 + const Expr *old_expr = (*this)[name]; 1.848 + assert(old_expr == NULL, "Implementation does not support redefinition"); 1.849 + 1.850 + _expr.Insert(name, expr); 1.851 + _defines.addName(name); 1.852 + 1.853 + return old_expr; 1.854 +} 1.855 + 1.856 +// Insert inserts the given key-value pair into the dictionary. The prior 1.857 +// value of the key is returned; NULL if the key was not previously defined. 1.858 +const Expr *ExprDict::Insert(const char *name, Expr *expr) { 1.859 + return (Expr*)_expr.Insert((void*)name, (void*)expr); 1.860 +} 1.861 + 1.862 +// Finds the value of a given key; or NULL if not found. 1.863 +// The dictionary is NOT changed. 1.864 +const Expr *ExprDict::operator [](const char *name) const { 1.865 + return (Expr*)_expr[name]; 1.866 +} 1.867 + 1.868 +void ExprDict::print_defines(FILE *fp) { 1.869 + fprintf(fp, "\n"); 1.870 + const char *name = NULL; 1.871 + for( _defines.reset(); (name = _defines.iter()) != NULL; ) { 1.872 + const Expr *expr = (const Expr*)_expr[name]; 1.873 + assert( expr != NULL, "name in ExprDict without matching Expr in dictionary"); 1.874 + expr->print_define(fp); 1.875 + } 1.876 +} 1.877 +void ExprDict::print_asserts(FILE *fp) { 1.878 + fprintf(fp, "\n"); 1.879 + fprintf(fp, " // Following assertions generated from definition section\n"); 1.880 + const char *name = NULL; 1.881 + for( _defines.reset(); (name = _defines.iter()) != NULL; ) { 1.882 + const Expr *expr = (const Expr*)_expr[name]; 1.883 + assert( expr != NULL, "name in ExprDict without matching Expr in dictionary"); 1.884 + expr->print_assert(fp); 1.885 + } 1.886 +} 1.887 + 1.888 +// Print out the dictionary contents as key-value pairs 1.889 +static void dumpekey(const void* key) { fprintf(stdout, "%s", (char*) key); } 1.890 +static void dumpexpr(const void* expr) { fflush(stdout); ((Expr*)expr)->print(); } 1.891 + 1.892 +void ExprDict::dump() { 1.893 + _expr.print(dumpekey, dumpexpr); 1.894 +} 1.895 + 1.896 + 1.897 +//------------------------------ExprDict::private------------------------------ 1.898 +// Disable public use of constructor, copy-ctor, operator =, operator == 1.899 +ExprDict::ExprDict( ) : _expr(cmpkey,hashkey), _defines() { 1.900 + assert( false, "NotImplemented"); 1.901 +} 1.902 +ExprDict::ExprDict( const ExprDict & ) : _expr(cmpkey,hashkey), _defines() { 1.903 + assert( false, "NotImplemented"); 1.904 +} 1.905 +ExprDict &ExprDict::operator =( const ExprDict &rhs) { 1.906 + assert( false, "NotImplemented"); 1.907 + _expr = rhs._expr; 1.908 + return *this; 1.909 +} 1.910 +// == compares two dictionaries; they must have the same keys (their keys 1.911 +// must match using CmpKey) and they must have the same values (pointer 1.912 +// comparison). If so 1 is returned, if not 0 is returned. 1.913 +bool ExprDict::operator ==(const ExprDict &d) const { 1.914 + assert( false, "NotImplemented"); 1.915 + return false; 1.916 +} 1.917 + 1.918 + 1.919 +//------------------------------Production------------------------------------- 1.920 +Production::Production(const char *result, const char *constraint, const char *valid) { 1.921 + initialize(); 1.922 + _result = result; 1.923 + _constraint = constraint; 1.924 + _valid = valid; 1.925 +} 1.926 + 1.927 +void Production::initialize() { 1.928 + _result = NULL; 1.929 + _constraint = NULL; 1.930 + _valid = knownInvalid; 1.931 + _cost_lb = Expr::get_unknown(); 1.932 + _cost_ub = Expr::get_unknown(); 1.933 +} 1.934 + 1.935 +void Production::print() { 1.936 + printf("%s", (_result == NULL ? "NULL" : _result ) ); 1.937 + printf("%s", (_constraint == NULL ? "NULL" : _constraint ) ); 1.938 + printf("%s", (_valid == NULL ? "NULL" : _valid ) ); 1.939 + _cost_lb->print(); 1.940 + _cost_ub->print(); 1.941 +} 1.942 + 1.943 + 1.944 +//------------------------------ProductionState-------------------------------- 1.945 +void ProductionState::initialize() { 1.946 + _constraint = noConstraint; 1.947 + 1.948 + // reset each Production currently in the dictionary 1.949 + DictI iter( &_production ); 1.950 + const void *x, *y = NULL; 1.951 + for( ; iter.test(); ++iter) { 1.952 + x = iter._key; 1.953 + y = iter._value; 1.954 + Production *p = (Production*)y; 1.955 + if( p != NULL ) { 1.956 + p->initialize(); 1.957 + } 1.958 + } 1.959 +} 1.960 + 1.961 +Production *ProductionState::getProduction(const char *result) { 1.962 + Production *p = (Production *)_production[result]; 1.963 + if( p == NULL ) { 1.964 + p = new Production(result, _constraint, knownInvalid); 1.965 + _production.Insert(result, p); 1.966 + } 1.967 + 1.968 + return p; 1.969 +} 1.970 + 1.971 +void ProductionState::set_constraint(const char *constraint) { 1.972 + _constraint = constraint; 1.973 +} 1.974 + 1.975 +const char *ProductionState::valid(const char *result) { 1.976 + return getProduction(result)->valid(); 1.977 +} 1.978 + 1.979 +void ProductionState::set_valid(const char *result) { 1.980 + Production *p = getProduction(result); 1.981 + 1.982 + // Update valid as allowed by current constraints 1.983 + if( _constraint == noConstraint ) { 1.984 + p->_valid = knownValid; 1.985 + } else { 1.986 + if( p->_valid != knownValid ) { 1.987 + p->_valid = unknownValid; 1.988 + } 1.989 + } 1.990 +} 1.991 + 1.992 +Expr *ProductionState::cost_lb(const char *result) { 1.993 + return getProduction(result)->cost_lb(); 1.994 +} 1.995 + 1.996 +Expr *ProductionState::cost_ub(const char *result) { 1.997 + return getProduction(result)->cost_ub(); 1.998 +} 1.999 + 1.1000 +void ProductionState::set_cost_bounds(const char *result, const Expr *cost, bool has_state_check, bool has_cost_check) { 1.1001 + Production *p = getProduction(result); 1.1002 + 1.1003 + if( p->_valid == knownInvalid ) { 1.1004 + // Our cost bounds are not unknown, just not defined. 1.1005 + p->_cost_lb = cost->clone(); 1.1006 + p->_cost_ub = cost->clone(); 1.1007 + } else if (has_state_check || _constraint != noConstraint) { 1.1008 + // The production is protected by a condition, so 1.1009 + // the cost bounds may expand. 1.1010 + // _cost_lb = min(cost, _cost_lb) 1.1011 + if( cost->less_than_or_equal(p->_cost_lb) ) { 1.1012 + p->_cost_lb = cost->clone(); 1.1013 + } 1.1014 + // _cost_ub = max(cost, _cost_ub) 1.1015 + if( p->_cost_ub->less_than_or_equal(cost) ) { 1.1016 + p->_cost_ub = cost->clone(); 1.1017 + } 1.1018 + } else if (has_cost_check) { 1.1019 + // The production has no condition check, but does 1.1020 + // have a cost check that could reduce the upper 1.1021 + // and/or lower bound. 1.1022 + // _cost_lb = min(cost, _cost_lb) 1.1023 + if( cost->less_than_or_equal(p->_cost_lb) ) { 1.1024 + p->_cost_lb = cost->clone(); 1.1025 + } 1.1026 + // _cost_ub = min(cost, _cost_ub) 1.1027 + if( cost->less_than_or_equal(p->_cost_ub) ) { 1.1028 + p->_cost_ub = cost->clone(); 1.1029 + } 1.1030 + } else { 1.1031 + // The costs are unconditionally set. 1.1032 + p->_cost_lb = cost->clone(); 1.1033 + p->_cost_ub = cost->clone(); 1.1034 + } 1.1035 + 1.1036 +} 1.1037 + 1.1038 +// Print out the dictionary contents as key-value pairs 1.1039 +static void print_key (const void* key) { fprintf(stdout, "%s", (char*) key); } 1.1040 +static void print_production(const void* production) { fflush(stdout); ((Production*)production)->print(); } 1.1041 + 1.1042 +void ProductionState::print() { 1.1043 + _production.print(print_key, print_production); 1.1044 +}