|
@@ -1676,10 +1676,119 @@ static bool clone_shift(Node* shift, Matcher* matcher, Matcher::MStack& mstack, |
|
|
return false; |
|
|
} |
|
|
|
|
|
// This function identifies sub-graphs in which a 'load' node is |
|
|
// input to two different nodes, and such that it can be matched |
|
|
// with BMI instructions like blsi, blsr, etc. |
|
|
// Example : for b = -a[i] & a[i] can be matched to blsi r32, m32. |
|
|
// The graph is (AndL (SubL Con0 LoadL*) LoadL*), where LoadL* |
|
|
// refers to the same node. |
|
|
// |
|
|
// Match the generic fused operations pattern (op1 (op2 Con{ConType} mop) mop) |
|
|
// This is a temporary solution until we make DAGs expressible in ADL. |
|
|
template<typename ConType> |
|
|
class FusedPatternMatcher { |
|
|
Node* _op1_node; |
|
|
Node* _mop_node; |
|
|
int _con_op; |
|
|
|
|
|
static int match_next(Node* n, int next_op, int next_op_idx) { |
|
|
if (n->in(1) == NULL || n->in(2) == NULL) { |
|
|
return -1; |
|
|
} |
|
|
|
|
|
if (next_op_idx == -1) { // n is commutative, try rotations |
|
|
if (n->in(1)->Opcode() == next_op) { |
|
|
return 1; |
|
|
} else if (n->in(2)->Opcode() == next_op) { |
|
|
return 2; |
|
|
} |
|
|
} else { |
|
|
assert(next_op_idx > 0 && next_op_idx <= 2, "Bad argument index"); |
|
|
if (n->in(next_op_idx)->Opcode() == next_op) { |
|
|
return next_op_idx; |
|
|
} |
|
|
} |
|
|
return -1; |
|
|
} |
|
|
|
|
|
public: |
|
|
FusedPatternMatcher(Node* op1_node, Node* mop_node, int con_op) : |
|
|
_op1_node(op1_node), _mop_node(mop_node), _con_op(con_op) { } |
|
|
|
|
|
bool match(int op1, int op1_op2_idx, // op1 and the index of the op1->op2 edge, -1 if op1 is commutative |
|
|
int op2, int op2_con_idx, // op2 and the index of the op2->con edge, -1 if op2 is commutative |
|
|
typename ConType::NativeType con_value) { |
|
|
if (_op1_node->Opcode() != op1) { |
|
|
return false; |
|
|
} |
|
|
if (_mop_node->outcnt() > 2) { |
|
|
return false; |
|
|
} |
|
|
op1_op2_idx = match_next(_op1_node, op2, op1_op2_idx); |
|
|
if (op1_op2_idx == -1) { |
|
|
return false; |
|
|
} |
|
|
// Memory operation must be the other edge |
|
|
int op1_mop_idx = (op1_op2_idx & 1) + 1; |
|
|
|
|
|
// Check that the mop node is really what we want |
|
|
if (_op1_node->in(op1_mop_idx) == _mop_node) { |
|
|
Node* op2_node = _op1_node->in(op1_op2_idx); |
|
|
if (op2_node->outcnt() > 1) { |
|
|
return false; |
|
|
} |
|
|
assert(op2_node->Opcode() == op2, "Should be"); |
|
|
op2_con_idx = match_next(op2_node, _con_op, op2_con_idx); |
|
|
if (op2_con_idx == -1) { |
|
|
return false; |
|
|
} |
|
|
// Memory operation must be the other edge |
|
|
int op2_mop_idx = (op2_con_idx & 1) + 1; |
|
|
// Check that the memory operation is the same node |
|
|
if (op2_node->in(op2_mop_idx) == _mop_node) { |
|
|
// Now check the constant |
|
|
const Type* con_type = op2_node->in(op2_con_idx)->bottom_type(); |
|
|
if (con_type != Type::TOP && ConType::as_self(con_type)->get_con() == con_value) { |
|
|
return true; |
|
|
} |
|
|
} |
|
|
} |
|
|
return false; |
|
|
} |
|
|
}; |
|
|
|
|
|
static bool is_bmi_pattern(Node* n, Node* m) { |
|
|
assert(UseBMI1Instructions, "sanity"); |
|
|
if (n != NULL && m != NULL) { |
|
|
if (m->Opcode() == Op_LoadI) { |
|
|
FusedPatternMatcher<TypeInt> bmii(n, m, Op_ConI); |
|
|
return bmii.match(Op_AndI, -1, Op_SubI, 1, 0) || |
|
|
bmii.match(Op_AndI, -1, Op_AddI, -1, -1) || |
|
|
bmii.match(Op_XorI, -1, Op_AddI, -1, -1); |
|
|
} else if (m->Opcode() == Op_LoadL) { |
|
|
FusedPatternMatcher<TypeLong> bmil(n, m, Op_ConL); |
|
|
return bmil.match(Op_AndL, -1, Op_SubL, 1, 0) || |
|
|
bmil.match(Op_AndL, -1, Op_AddL, -1, -1) || |
|
|
bmil.match(Op_XorL, -1, Op_AddL, -1, -1); |
|
|
} |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Should the matcher clone input 'm' of node 'n'? |
|
|
bool Matcher::pd_clone_node(Node* n, Node* m, Matcher::MStack& mstack) { |
|
|
// If 'n' and 'm' are part of a graph for BMI instruction, clone the input 'm'. |
|
|
if (UseBMI1Instructions && is_bmi_pattern(n, m)) { |
|
|
mstack.push(m, Visit); |
|
|
return true; |
|
|
} |
|
|
return false; |
|
|
} |
|
|
|
|
|
// Should the Matcher clone shifts on addressing modes, expecting them |
|
|
// to be subsumed into complex addressing expressions or compute them |
|
|
// into registers? |
|
|
bool Matcher::clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, VectorSet& address_visited) { |
|
|
bool Matcher::pd_clone_address_expressions(AddPNode* m, Matcher::MStack& mstack, VectorSet& address_visited) { |
|
|
Node *off = m->in(AddPNode::Offset); |
|
|
if (off->is_Con()) { |
|
|
address_visited.test_set(m->_idx); // Flag as address_visited |
|
|