/* * Copyright (c) 1997, 2022, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. * * This code is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License * version 2 for more details (a copy is included in the LICENSE file that * accompanied this code). * * You should have received a copy of the GNU General Public License version * 2 along with this work; if not, write to the Free Software Foundation, * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// DFA.CPP - Method definitions for outputting the matcher DFA from ADLC #include"adlc.hpp"
//---------------------------Switches for debugging output--------------------- staticbool debug_output = false; staticbool debug_output1 = false; // top level chain rules
//---------------------------Production State---------------------------------- staticconstchar *knownInvalid = "knownInvalid"; // The result does NOT have a rule defined staticconstchar *knownValid = "knownValid"; // The result must be produced by a rule staticconstchar *unknownValid = "unknownValid"; // Unknown (probably due to a child or predicate constraint)
staticconstchar *noConstraint = "noConstraint"; // No constraints seen so far staticconstchar *hasConstraint = "hasConstraint"; // Within the first constraint
//------------------------------Production------------------------------------ // Track the status of productions for a particular result class Production { public: constchar *_result; constchar *_constraint; constchar *_valid;
Expr *_cost_lb; // Cost lower bound for this production
Expr *_cost_ub; // Cost upper bound for this production
//------------------------------ProductionState-------------------------------- // Track the status of all production rule results // Reset for each root opcode (e.g., Op_RegI, Op_AddI, ...) class ProductionState { private:
Dict _production; // map result of production, char*, to information or NULL constchar *_constraint;
void initialize(); // reset local and dictionary state
constchar *constraint(); void set_constraint(constchar *constraint); // currently working inside of constraints
constchar *valid(constchar *result); // unknownValid, or status for this production void set_valid(constchar *result); // if not constrained, set status to knownValid
// Return the Production associated with the result, // or create a new Production and insert it into the dictionary.
Production *getProduction(constchar *result);
//---------------------------Helper Functions---------------------------------- // cost_check template: // 1) if (STATE__NOT_YET_VALID(EBXREGI) || _cost[EBXREGI] > c) { // 2) DFA_PRODUCTION(EBXREGI, cmovI_memu_rule, c) // 3) } // staticvoid cost_check(FILE *fp, constchar *spaces, constchar *arrayIdx, const Expr *cost, constchar *rule, ProductionState &status) { bool state_check = false; // true if this production needs to check validity bool cost_check = false; // true if this production needs to check cost bool cost_is_above_upper_bound = false; // true if this production is unnecessary due to high cost bool cost_is_below_lower_bound = false; // true if this production replaces a higher cost production
// Get information about this production const Expr *previous_ub = status.cost_ub(arrayIdx); if( !previous_ub->is_unknown() ) { if( previous_ub->less_than_or_equal(cost) ) {
cost_is_above_upper_bound = true; if( debug_output ) { fprintf(fp, "// Previous rule with lower cost than: %s === %s_rule costs %s\n", arrayIdx, rule, cost->as_string()); }
}
}
// Update ProductionState if( validity_check != knownValid ) { // set State vector if not previously known
status.set_valid(arrayIdx);
}
}
//---------------------------child_test---------------------------------------- // Example: // STATE__VALID_CHILD(_kids[0], FOO) && STATE__VALID_CHILD(_kids[1], BAR) // Macro equivalent to: _kids[0]->valid(FOO) && _kids[1]->valid(BAR) // staticvoid child_test(FILE *fp, MatchList &mList, bool is_vector_unary_op) { if (mList._lchild) { // If left child, check it constchar* lchild_to_upper = ArchDesc::getMachOperEnum(mList._lchild);
fprintf(fp, "STATE__VALID_CHILD(_kids[0], %s)", lchild_to_upper); delete[] lchild_to_upper;
if (mList._rchild) { // If both, add the "&&"
fprintf(fp, " && ");
} elseif (is_vector_unary_op) { // If unpredicated vector unary operation, add one extra check, i.e. right // child should be NULL, to distinguish from the predicated version.
fprintf(fp, " && _kids[1] == NULL");
}
} if (mList._rchild) { // If right child, check it constchar* rchild_to_upper = ArchDesc::getMachOperEnum(mList._rchild);
fprintf(fp, "STATE__VALID_CHILD(_kids[1], %s)", rchild_to_upper); delete[] rchild_to_upper;
}
}
//---------------------------calc_cost----------------------------------------- // Example: // unsigned int c = _kids[0]->_cost[FOO] + _kids[1]->_cost[BAR] + 5; //
Expr *ArchDesc::calc_cost(FILE *fp, constchar *spaces, MatchList &mList, ProductionState &status) {
fprintf(fp, "%sunsigned int c = ", spaces);
Expr *c = new Expr("0"); if (mList._lchild) { // If left child, add it in constchar* lchild_to_upper = ArchDesc::getMachOperEnum(mList._lchild);
sprintf(Expr::buffer(), "_kids[0]->_cost[%s]", lchild_to_upper);
c->add(Expr::buffer()); delete[] lchild_to_upper;
} if (mList._rchild) { // If right child, add it in constchar* rchild_to_upper = ArchDesc::getMachOperEnum(mList._rchild);
sprintf(Expr::buffer(), "_kids[1]->_cost[%s]", rchild_to_upper);
c->add(Expr::buffer()); delete[] rchild_to_upper;
} // Add in cost of this rule constchar *mList_cost = mList.get_cost();
c->add(mList_cost, *this);
fprintf(fp, "%s", spaces4); // Only generate child tests if this is not a leaf node bool has_child_constraints = mList._lchild || mList._rchild; constchar *predicate_test = mList.get_pred(); if (has_child_constraints || predicate_test) { // Open the child-and-predicate-test braces
fprintf(fp, "if( ");
status.set_constraint(hasConstraint);
child_test(fp, mList, is_vector_unary_op); // Only generate predicate test if one exists for this match if (predicate_test) { if (has_child_constraints) {
fprintf(fp," &&\n");
}
fprintf(fp, "%s %s", spaces6, predicate_test);
} // End of outer tests
fprintf(fp," ) ");
} else { // No child or predicate test needed
status.set_constraint(noConstraint);
}
// End of outer tests
fprintf(fp,"{\n");
// Calculate cost of this match const Expr *cost = calc_cost(fp, spaces6, mList, status); // Check against other match costs, and update cost & rule vectors
cost_check(fp, spaces6, ArchDesc::getMachOperEnum(mList._resultStr), cost, mList._opcode, status);
// If this is a member of an operand class, update the class cost & rule
expand_opclass( fp, spaces6, cost, mList._resultStr, status);
// Check if this rule should be used to generate the chains as well. constchar *rule = /* set rule to "Invalid" for internal operands */
strcmp(mList._opcode, mList._resultStr) ? mList._opcode : "Invalid";
// If this rule produces an operand which has associated chain rules, // update the operands with the chain rule + this rule cost & this rule.
chain_rule(fp, spaces6, mList._resultStr, cost, rule, operands_chained_from, status);
// Close the child-and-predicate-test braces
fprintf(fp, " }\n");
}
//---------------------------expand_opclass------------------------------------ // Chain from one result_type to all other members of its operand class void ArchDesc::expand_opclass(FILE *fp, constchar *indent, const Expr *cost, constchar *result_type, ProductionState &status) { const Form *form = _globalNames[result_type];
OperandForm *op = form ? form->is_operand() : NULL; if( op && op->_classes.count() > 0 ) { if( debug_output ) { fprintf(fp, "// expand operand classes for operand: %s \n", (char *)op->_ident ); } // %%%%% Explanation // Iterate through all operand classes which include this operand
op->_classes.reset(); constchar *oclass; // Expr *cCost = new Expr(cost); while( (oclass = op->_classes.iter()) != NULL ) // Check against other match costs, and update cost & rule vectors
cost_check(fp, indent, ArchDesc::getMachOperEnum(oclass), cost, result_type, status);
}
}
//---------------------------chain_rule---------------------------------------- // Starting at 'operand', check if we know how to automatically generate other results void ArchDesc::chain_rule(FILE *fp, constchar *indent, constchar *operand, const Expr *icost, constchar *irule, Dict &operands_chained_from, ProductionState &status) {
// Check if we have already generated chains from this starting point if( operands_chained_from[operand] != NULL ) { return;
} else {
operands_chained_from.Insert( operand, operand);
} if( debug_output ) { fprintf(fp, "// chain rules starting from: %s and %s \n", (char *)operand, (char *)irule); } // %%%%% Explanation
ChainList *lst = (ChainList *)_chainRules[operand]; if (lst) { // printf("\nChain from <%s> at cost #%s\n",operand, icost ? icost : "_"); constchar *result, *cost, *rule; for(lst->reset(); (lst->iter(result,cost,rule)) == true; ) { // Do not generate operands that are already available if( operands_chained_from[result] != NULL ) { continue;
} else { // Compute the cost for previous match + chain_rule_cost // total_cost = icost + cost;
Expr *total_cost = icost->clone(); // icost + cost
total_cost->add(cost, *this);
// Check for transitive chain rules
Form *form = (Form *)_globalNames[rule]; if ( ! form->is_instruction()) { // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); // Check against other match costs, and update cost & rule vectors constchar *reduce_rule = strcmp(irule,"Invalid") ? irule : rule;
cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, reduce_rule, status);
chain_rule(fp, indent, result, total_cost, irule, operands_chained_from, status);
} else { // printf(" result=%s cost=%s rule=%s\n", result, total_cost, rule); // Check against other match costs, and update cost & rule vectors
cost_check(fp, indent, ArchDesc::getMachOperEnum(result), total_cost, rule, status);
chain_rule(fp, indent, result, total_cost, rule, operands_chained_from, status);
}
// If this is a member of an operand class, update class cost & rule
expand_opclass( fp, indent, total_cost, result, status );
}
}
}
}
//---------------------------prune_matchlist----------------------------------- // Check for duplicate entries in a matchlist, and prune out the higher cost // entry. void ArchDesc::prune_matchlist(Dict &minimize, MatchList &mlist) {
}
//---------------------------buildDFA------------------------------------------ // DFA is a large switch with case statements for each ideal opcode encountered // in any match rule in the ad file. Each case has a series of if's to handle // the match or fail decisions. The matches test the cost function of that // rule, and prune any cases which are higher cost for the same reduction. // In order to generate the DFA we walk the table of ideal opcode/MatchList // pairs generated by the ADLC front end to build the contents of the case // statements (a series of if statements). void ArchDesc::buildDFA(FILE* fp) { int i; // Remember operands that are the starting points for chain rules. // Prevent cycles by checking if we have already generated chain.
Dict operands_chained_from(cmpstr, hashstr, Form::arena);
// Hash inputs to match rules so that final DFA contains only one entry for // each match pattern which is the low cost entry.
Dict minimize(cmpstr, hashstr, Form::arena);
// Track status of dfa for each resulting production // reset for each ideal root.
ProductionState status(Form::arena);
// Output the start of the DFA method into the output file
fprintf(fp, "\n");
fprintf(fp, "//------------------------- Source -----------------------------------------\n"); // Do not put random source code into the DFA. // If there are constants which need sharing, put them in "source_hpp" forms. // _source.output(fp);
fprintf(fp, "\n");
fprintf(fp, "//------------------------- Attributes -------------------------------------\n");
_attributes.output(fp);
fprintf(fp, "\n");
fprintf(fp, "//------------------------- Macros -----------------------------------------\n");
fprintf(fp, "#define DFA_PRODUCTION(result, rule, cost)\\\n");
fprintf(fp, " assert(rule < (1 << 15), \"too many rules\"); _cost[ (result) ] = cost; _rule[ (result) ] = (rule << 1) | 0x1;\n");
fprintf(fp, "\n");
fprintf(fp, "// DFA is a large switch with case statements for each ideal opcode encountered\n" "// in any match rule in the ad file. Each case has a series of if's to handle\n" "// the match or fail decisions. The matches test the cost function of that\n" "// rule, and prune any cases which are higher cost for the same reduction.\n" "// In order to generate the DFA we walk the table of ideal opcode/MatchList\n" "// pairs generated by the ADLC front end to build the contents of the case\n" "// statements (a series of if statements).\n"
);
fprintf(fp, "\n");
fprintf(fp, "\n"); if (_dfa_small) { // Now build the individual routines just like the switch entries in large version // Iterate over the table of MatchLists, start at first valid opcode of 1 for (i = 1; i < _last_opcode; i++) { if (_mlistab[i] == NULL) continue; // Generate the routine header statement for this opcode
fprintf(fp, "void State::_sub_Op_%s(const Node *n){\n", NodeClassNames[i]); // Generate body. Shared for both inline and out-of-line version
gen_dfa_state_body(fp, minimize, status, operands_chained_from, i); // End of routine
fprintf(fp, "}\n");
}
}
fprintf(fp, "bool State::DFA");
fprintf(fp, "(int opcode, const Node *n) {\n");
fprintf(fp, " switch(opcode) {\n");
// Iterate over the table of MatchLists, start at first valid opcode of 1 for (i = 1; i < _last_opcode; i++) { if (_mlistab[i] == NULL) continue; // Generate the case statement for this opcode if (_dfa_small) {
fprintf(fp, " case Op_%s: { _sub_Op_%s(n);\n", NodeClassNames[i], NodeClassNames[i]);
} else {
fprintf(fp, " case Op_%s: {\n", NodeClassNames[i]); // Walk the list, compacting it
gen_dfa_state_body(fp, minimize, status, operands_chained_from, i);
} // Print the "break"
fprintf(fp, " break;\n");
fprintf(fp, " }\n");
}
staticvoid check_index(int index) { assert( 0 <= index && index < count, "Invalid index"); }
// Confirm that this is a separate sub-expression. // Only need to catch common cases like " ... && shared ..." // and avoid hazardous ones like "...->shared" staticbool valid_loc(char *pred, char *shared) { // start of predicate is valid if( shared == pred ) returntrue;
// Check previous character and recurse if needed char *prev = shared - 1; char c = *prev; switch( c ) { case' ': case'\n': return dfa_shared_preds::valid_loc(pred, prev); case'!': case'(': case'<': case'=': returntrue; case'"': // such as: #line 10 "myfile.ad"\n mypredicate returntrue; case'|': if (prev != pred && *(prev-1) == '|') returntrue; break; case'&': if (prev != pred && *(prev-1) == '&') returntrue; break; default: returnfalse;
}
// Check each predicate in the MatchList for common sub-expressions staticvoid cse_matchlist(MatchList *matchList) { for( MatchList *mList = matchList; mList != NULL; mList = mList->get_next() ) {
Predicate* predicate = mList->get_pred_obj(); char* pred = mList->get_pred(); if( pred != NULL ) { for(int index = 0; index < count; ++index ) { constchar *shared_pred = dfa_shared_preds::pred(index); constchar *shared_pred_var = dfa_shared_preds::var(index); bool result = dfa_shared_preds::cse_predicate(predicate, shared_pred, shared_pred_var); if( result ) dfa_shared_preds::set_found(index, true);
}
}
}
}
// If the Predicate contains a common sub-expression, replace the Predicate's // string with one that uses the variable name. staticbool cse_predicate(Predicate* predicate, constchar *shared_pred, constchar *shared_pred_var) { bool result = false; char *pred = predicate->_pred; if( pred != NULL ) { char *new_pred = pred; for( char *shared_pred_loc = strstr(new_pred, shared_pred);
shared_pred_loc != NULL && dfa_shared_preds::valid_loc(new_pred,shared_pred_loc);
shared_pred_loc = strstr(new_pred, shared_pred) ) { // Do not modify the original predicate string, it is shared if( new_pred == pred ) {
new_pred = strdup(pred);
shared_pred_loc = strstr(new_pred, shared_pred);
} // Replace shared_pred with variable name
strncpy(shared_pred_loc, shared_pred_var, strlen(shared_pred_var));
} // Install new predicate if( new_pred != pred ) {
predicate->_pred = new_pred;
result = true;
}
} return result;
}
// Output the hoisted common sub-expression if we found it in predicates staticvoid generate_cse(FILE *fp) { for(int j = 0; j < count; ++j ) { if( dfa_shared_preds::found(j) ) { constchar *shared_pred_type = dfa_shared_preds::type(j); constchar *shared_pred_var = dfa_shared_preds::var(j); constchar *shared_pred = dfa_shared_preds::pred(j);
fprintf(fp, " %s %s = %s;\n", shared_pred_type, shared_pred_var, shared_pred);
}
}
}
}; // shared predicates, _var and _pred entry should be the same length bool dfa_shared_preds::_found[dfa_shared_preds::count] = { false, false, false IA32_ONLY(COMMA false) }; constchar* dfa_shared_preds::_type [dfa_shared_preds::count] = { "int", "jlong", "intptr_t" IA32_ONLY(COMMA "bool") }; constchar* dfa_shared_preds::_var [dfa_shared_preds::count] = { "_n_get_int__", "_n_get_long__", "_n_get_intptr_t__" IA32_ONLY(COMMA "Compile__current____select_24_bit_instr__") }; constchar* dfa_shared_preds::_pred [dfa_shared_preds::count] = { "n->get_int()", "n->get_long()", "n->get_intptr_t()" IA32_ONLY(COMMA "Compile::current()->select_24_bit_instr()") };
// Helper method to check whether a node is vector unary operation. staticbool is_vector_unary_op_name(constchar* op_name) { staticconstchar* vector_unary_op_list[] = { "AbsVB", "AbsVS", "AbsVI", "AbsVL", "AbsVF", "AbsVD", "NegVI", "NegVL", "NegVF", "NegVD", "SqrtVF", "SqrtVD", "PopCountVI", "PopCountVL", "CountLeadingZerosV", "CountTrailingZerosV", "ReverseV", "ReverseBytesV", "MaskAll", "VectorLoadMask", "VectorMaskFirstTrue"
}; int cnt = sizeof(vector_unary_op_list) / sizeof(char*); for (int i = 0; i < cnt; i++) { if (strcmp(op_name, vector_unary_op_list[i]) == 0) { returntrue;
}
} returnfalse;
}
void ArchDesc::gen_dfa_state_body(FILE* fp, Dict &minimize, ProductionState &status, Dict &operands_chained_from, int i) { // Start the body of each Op_XXX sub-dfa with a clean state.
status.initialize();
// Walk the list, compacting it
MatchList* mList = _mlistab[i]; do { // Hash each entry using inputs as key and pointer as data. // If there is already an entry, keep the one with lower cost, and // remove the other one from the list.
prune_matchlist(minimize, *mList); // Iterate
mList = mList->get_next();
} while(mList != NULL);
// Hoist previously specified common sub-expressions out of predicates
dfa_shared_preds::reset_found();
dfa_shared_preds::cse_matchlist(_mlistab[i]);
dfa_shared_preds::generate_cse(fp);
// Walk the list again, generating code do { // Each match can generate its own chains
operands_chained_from.Clear();
gen_match(fp, *mList, status, operands_chained_from, is_vector_unary_op);
mList = mList->get_next();
} while(mList != NULL); // Fill in any chain rules which add instructions // These can generate their own chains as well.
operands_chained_from.Clear(); // if( debug_output1 ) { fprintf(fp, "// top level chain rules for: %s \n", (char *)NodeClassNames[i]); } // %%%%% Explanation const Expr *zeroCost = new Expr("0");
chain_rule(fp, " ", (char *)NodeClassNames[i], zeroCost, "Invalid",
operands_chained_from, status);
}
void Expr::add(constchar *c, ArchDesc &AD) { const Expr *e = AD.globalDefs()[c]; if( e != NULL ) { // use the value of 'c' defined in <arch>.ad
add(e);
} else {
Expr *cost = new Expr(c);
add(cost);
}
}
int Expr::compute_max(const Expr *c1, const Expr *c2) { int v1 = c1->_max_value; int v2 = c2->_max_value;
// Check for overflow without producing UB. If v2 is positive // and not larger than Max, the subtraction cannot underflow.
assert(0 <= v2 && v2 <= Expr::Max, "sanity"); if (v1 > Expr::Max - v2) { return Expr::Max;
}
// Return # of name-Expr pairs in dict int ExprDict::Size(void) const { return _expr.Size();
}
// define inserts the given key-value pair into the dictionary, // and records the name in order for later output, ... const Expr *ExprDict::define(constchar *name, Expr *expr) { const Expr *old_expr = (*this)[name];
assert(old_expr == NULL, "Implementation does not support redefinition");
_expr.Insert(name, expr);
_defines.addName(name);
return old_expr;
}
// Insert inserts the given key-value pair into the dictionary. The prior // value of the key is returned; NULL if the key was not previously defined. const Expr *ExprDict::Insert(constchar *name, Expr *expr) { return (Expr*)_expr.Insert((void*)name, (void*)expr);
}
// Finds the value of a given key; or NULL if not found. // The dictionary is NOT changed. const Expr *ExprDict::operator [](constchar *name) const { return (Expr*)_expr[name];
}
//------------------------------ExprDict::private------------------------------ // Disable public use of constructor, copy-ctor, operator =, operator ==
ExprDict::ExprDict( ) : _expr(cmpkey,hashkey), _defines() {
assert( false, "NotImplemented");
}
ExprDict::ExprDict( const ExprDict & ) : _expr(cmpkey,hashkey), _defines() {
assert( false, "NotImplemented");
}
ExprDict &ExprDict::operator =( const ExprDict &rhs) {
assert( false, "NotImplemented");
_expr = rhs._expr; return *this;
} // == compares two dictionaries; they must have the same keys (their keys // must match using CmpKey) and they must have the same values (pointer // comparison). If so 1 is returned, if not 0 is returned. bool ExprDict::operator ==(const ExprDict &d) const {
assert( false, "NotImplemented"); returnfalse;
}
// reset each Production currently in the dictionary
DictI iter( &_production ); constvoid *x, *y = NULL; for( ; iter.test(); ++iter) {
x = iter._key;
y = iter._value;
Production *p = (Production*)y; if( p != NULL ) {
p->initialize();
}
}
}
Production *ProductionState::getProduction(constchar *result) {
Production *p = (Production *)_production[result]; if( p == NULL ) {
p = new Production(result, _constraint, knownInvalid);
_production.Insert(result, p);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.