Merge
This commit is contained in:
commit
40164313e3
@ -89,7 +89,6 @@ private:
|
|||||||
static const TypePtr* get_address_type(PhaseGVN *phase, Node* n);
|
static const TypePtr* get_address_type(PhaseGVN *phase, Node* n);
|
||||||
|
|
||||||
Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count);
|
Node* try_clone_instance(PhaseGVN *phase, bool can_reshape, int count);
|
||||||
Node* conv_I2X_offset(PhaseGVN *phase, Node* offset, const TypeAryPtr* ary_t);
|
|
||||||
bool prepare_array_copy(PhaseGVN *phase, bool can_reshape,
|
bool prepare_array_copy(PhaseGVN *phase, bool can_reshape,
|
||||||
Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest,
|
Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest,
|
||||||
BasicType& copy_type, const Type*& value_type, bool& disjoint_bases);
|
BasicType& copy_type, const Type*& value_type, bool& disjoint_bases);
|
||||||
|
@ -277,6 +277,23 @@ Node *CastIINode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint CastIINode::cmp(const Node &n) const {
|
||||||
|
return ConstraintCastNode::cmp(n) && ((CastIINode&)n)._range_check_dependency == _range_check_dependency;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint CastIINode::size_of() const {
|
||||||
|
return sizeof(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PRODUCT
|
||||||
|
void CastIINode::dump_spec(outputStream* st) const {
|
||||||
|
ConstraintCastNode::dump_spec(st);
|
||||||
|
if (_range_check_dependency) {
|
||||||
|
st->print(" range check dependency");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
//------------------------------Identity---------------------------------------
|
//------------------------------Identity---------------------------------------
|
||||||
// If input is already higher or equal to cast type, then this is an identity.
|
// If input is already higher or equal to cast type, then this is an identity.
|
||||||
|
@ -62,13 +62,33 @@ class ConstraintCastNode: public TypeNode {
|
|||||||
//------------------------------CastIINode-------------------------------------
|
//------------------------------CastIINode-------------------------------------
|
||||||
// cast integer to integer (different range)
|
// cast integer to integer (different range)
|
||||||
class CastIINode: public ConstraintCastNode {
|
class CastIINode: public ConstraintCastNode {
|
||||||
|
protected:
|
||||||
|
// Is this node dependent on a range check?
|
||||||
|
const bool _range_check_dependency;
|
||||||
|
virtual uint cmp(const Node &n) const;
|
||||||
|
virtual uint size_of() const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CastIINode(Node *n, const Type *t, bool carry_dependency = false)
|
CastIINode(Node* n, const Type* t, bool carry_dependency = false, bool range_check_dependency = false)
|
||||||
: ConstraintCastNode(n, t, carry_dependency) {}
|
: ConstraintCastNode(n, t, carry_dependency), _range_check_dependency(range_check_dependency) {
|
||||||
|
init_class_id(Class_CastII);
|
||||||
|
}
|
||||||
virtual int Opcode() const;
|
virtual int Opcode() const;
|
||||||
virtual uint ideal_reg() const { return Op_RegI; }
|
virtual uint ideal_reg() const { return Op_RegI; }
|
||||||
virtual const Type* Value(PhaseGVN* phase) const;
|
virtual const Type* Value(PhaseGVN* phase) const;
|
||||||
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
virtual Node *Ideal(PhaseGVN *phase, bool can_reshape);
|
||||||
|
const bool has_range_check() {
|
||||||
|
#ifdef _LP64
|
||||||
|
return _range_check_dependency;
|
||||||
|
#else
|
||||||
|
assert(!_range_check_dependency, "Should not have range check dependency");
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef PRODUCT
|
||||||
|
virtual void dump_spec(outputStream* st) const;
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
//------------------------------CastPPNode-------------------------------------
|
//------------------------------CastPPNode-------------------------------------
|
||||||
|
@ -277,13 +277,6 @@ class IfNode : public MultiBranchNode {
|
|||||||
virtual uint size_of() const { return sizeof(*this); }
|
virtual uint size_of() const { return sizeof(*this); }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ProjNode* range_check_trap_proj() {
|
|
||||||
int flip_test = 0;
|
|
||||||
Node* l = NULL;
|
|
||||||
Node* r = NULL;
|
|
||||||
return range_check_trap_proj(flip_test, l, r);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper methods for fold_compares
|
// Helper methods for fold_compares
|
||||||
bool cmpi_folds(PhaseIterGVN* igvn);
|
bool cmpi_folds(PhaseIterGVN* igvn);
|
||||||
bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);
|
bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);
|
||||||
|
@ -38,6 +38,7 @@
|
|||||||
#include "opto/c2compiler.hpp"
|
#include "opto/c2compiler.hpp"
|
||||||
#include "opto/callGenerator.hpp"
|
#include "opto/callGenerator.hpp"
|
||||||
#include "opto/callnode.hpp"
|
#include "opto/callnode.hpp"
|
||||||
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/cfgnode.hpp"
|
#include "opto/cfgnode.hpp"
|
||||||
#include "opto/chaitin.hpp"
|
#include "opto/chaitin.hpp"
|
||||||
#include "opto/compile.hpp"
|
#include "opto/compile.hpp"
|
||||||
@ -402,6 +403,13 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
|
|||||||
remove_macro_node(n);
|
remove_macro_node(n);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Remove useless CastII nodes with range check dependency
|
||||||
|
for (int i = range_check_cast_count() - 1; i >= 0; i--) {
|
||||||
|
Node* cast = range_check_cast_node(i);
|
||||||
|
if (!useful.member(cast)) {
|
||||||
|
remove_range_check_cast(cast);
|
||||||
|
}
|
||||||
|
}
|
||||||
// Remove useless expensive node
|
// Remove useless expensive node
|
||||||
for (int i = C->expensive_count()-1; i >= 0; i--) {
|
for (int i = C->expensive_count()-1; i >= 0; i--) {
|
||||||
Node* n = C->expensive_node(i);
|
Node* n = C->expensive_node(i);
|
||||||
@ -1178,6 +1186,7 @@ void Compile::Init(int aliaslevel) {
|
|||||||
_macro_nodes = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
_macro_nodes = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
||||||
_predicate_opaqs = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
_predicate_opaqs = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
||||||
_expensive_nodes = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
_expensive_nodes = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
||||||
|
_range_check_casts = new(comp_arena()) GrowableArray<Node*>(comp_arena(), 8, 0, NULL);
|
||||||
register_library_intrinsics();
|
register_library_intrinsics();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1924,6 +1933,22 @@ void Compile::cleanup_loop_predicates(PhaseIterGVN &igvn) {
|
|||||||
assert(predicate_count()==0, "should be clean!");
|
assert(predicate_count()==0, "should be clean!");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Compile::add_range_check_cast(Node* n) {
|
||||||
|
assert(n->isa_CastII()->has_range_check(), "CastII should have range check dependency");
|
||||||
|
assert(!_range_check_casts->contains(n), "duplicate entry in range check casts");
|
||||||
|
_range_check_casts->append(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove all range check dependent CastIINodes.
|
||||||
|
void Compile::remove_range_check_casts(PhaseIterGVN &igvn) {
|
||||||
|
for (int i = range_check_cast_count(); i > 0; i--) {
|
||||||
|
Node* cast = range_check_cast_node(i-1);
|
||||||
|
assert(cast->isa_CastII()->has_range_check(), "CastII should have range check dependency");
|
||||||
|
igvn.replace_node(cast, cast->in(1));
|
||||||
|
}
|
||||||
|
assert(range_check_cast_count() == 0, "should be empty");
|
||||||
|
}
|
||||||
|
|
||||||
// StringOpts and late inlining of string methods
|
// StringOpts and late inlining of string methods
|
||||||
void Compile::inline_string_calls(bool parse_time) {
|
void Compile::inline_string_calls(bool parse_time) {
|
||||||
{
|
{
|
||||||
@ -2284,6 +2309,12 @@ void Compile::Optimize() {
|
|||||||
PhaseIdealLoop::verify(igvn);
|
PhaseIdealLoop::verify(igvn);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (range_check_cast_count() > 0) {
|
||||||
|
// No more loop optimizations. Remove all range check dependent CastIINodes.
|
||||||
|
C->remove_range_check_casts(igvn);
|
||||||
|
igvn.optimize();
|
||||||
|
}
|
||||||
|
|
||||||
{
|
{
|
||||||
TracePhase tp("macroExpand", &timers[_t_macroExpand]);
|
TracePhase tp("macroExpand", &timers[_t_macroExpand]);
|
||||||
PhaseMacroExpand mex(igvn);
|
PhaseMacroExpand mex(igvn);
|
||||||
@ -3087,6 +3118,16 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
|
|||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifdef ASSERT
|
||||||
|
case Op_CastII:
|
||||||
|
// Verify that all range check dependent CastII nodes were removed.
|
||||||
|
if (n->isa_CastII()->has_range_check()) {
|
||||||
|
n->dump(3);
|
||||||
|
assert(false, "Range check dependent CastII node was not removed");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
#endif
|
||||||
|
|
||||||
case Op_ModI:
|
case Op_ModI:
|
||||||
if (UseDivMod) {
|
if (UseDivMod) {
|
||||||
// Check if a%b and a/b both exist
|
// Check if a%b and a/b both exist
|
||||||
@ -3962,7 +4003,7 @@ int Compile::static_subtype_check(ciKlass* superk, ciKlass* subk) {
|
|||||||
return SSC_full_test;
|
return SSC_full_test;
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* Compile::conv_I2X_index(PhaseGVN *phase, Node* idx, const TypeInt* sizetype) {
|
Node* Compile::conv_I2X_index(PhaseGVN* phase, Node* idx, const TypeInt* sizetype, Node* ctrl) {
|
||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
// The scaled index operand to AddP must be a clean 64-bit value.
|
// The scaled index operand to AddP must be a clean 64-bit value.
|
||||||
// Java allows a 32-bit int to be incremented to a negative
|
// Java allows a 32-bit int to be incremented to a negative
|
||||||
@ -3975,13 +4016,31 @@ Node* Compile::conv_I2X_index(PhaseGVN *phase, Node* idx, const TypeInt* sizetyp
|
|||||||
// number. (The prior range check has ensured this.)
|
// number. (The prior range check has ensured this.)
|
||||||
// This assertion is used by ConvI2LNode::Ideal.
|
// This assertion is used by ConvI2LNode::Ideal.
|
||||||
int index_max = max_jint - 1; // array size is max_jint, index is one less
|
int index_max = max_jint - 1; // array size is max_jint, index is one less
|
||||||
if (sizetype != NULL) index_max = sizetype->_hi - 1;
|
if (sizetype != NULL) index_max = sizetype->_hi - 1;
|
||||||
const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax);
|
const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax);
|
||||||
idx = phase->transform(new ConvI2LNode(idx, lidxtype));
|
idx = constrained_convI2L(phase, idx, iidxtype, ctrl);
|
||||||
#endif
|
#endif
|
||||||
return idx;
|
return idx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check)
|
||||||
|
Node* Compile::constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl) {
|
||||||
|
if (ctrl != NULL) {
|
||||||
|
// Express control dependency by a CastII node with a narrow type.
|
||||||
|
value = new CastIINode(value, itype, false, true /* range check dependency */);
|
||||||
|
// Make the CastII node dependent on the control input to prevent the narrowed ConvI2L
|
||||||
|
// node from floating above the range check during loop optimizations. Otherwise, the
|
||||||
|
// ConvI2L node may be eliminated independently of the range check, causing the data path
|
||||||
|
// to become TOP while the control path is still there (although it's unreachable).
|
||||||
|
value->set_req(0, ctrl);
|
||||||
|
// Save CastII node to remove it after loop optimizations.
|
||||||
|
phase->C->add_range_check_cast(value);
|
||||||
|
value = phase->transform(value);
|
||||||
|
}
|
||||||
|
const TypeLong* ltype = TypeLong::make(itype->_lo, itype->_hi, itype->_widen);
|
||||||
|
return phase->transform(new ConvI2LNode(value, ltype));
|
||||||
|
}
|
||||||
|
|
||||||
// The message about the current inlining is accumulated in
|
// The message about the current inlining is accumulated in
|
||||||
// _print_inlining_stream and transfered into the _print_inlining_list
|
// _print_inlining_stream and transfered into the _print_inlining_list
|
||||||
// once we know whether inlining succeeds or not. For regular
|
// once we know whether inlining succeeds or not. For regular
|
||||||
|
@ -400,6 +400,7 @@ class Compile : public Phase {
|
|||||||
GrowableArray<Node*>* _macro_nodes; // List of nodes which need to be expanded before matching.
|
GrowableArray<Node*>* _macro_nodes; // List of nodes which need to be expanded before matching.
|
||||||
GrowableArray<Node*>* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates.
|
GrowableArray<Node*>* _predicate_opaqs; // List of Opaque1 nodes for the loop predicates.
|
||||||
GrowableArray<Node*>* _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common
|
GrowableArray<Node*>* _expensive_nodes; // List of nodes that are expensive to compute and that we'd better not let the GVN freely common
|
||||||
|
GrowableArray<Node*>* _range_check_casts; // List of CastII nodes with a range check dependency
|
||||||
ConnectionGraph* _congraph;
|
ConnectionGraph* _congraph;
|
||||||
#ifndef PRODUCT
|
#ifndef PRODUCT
|
||||||
IdealGraphPrinter* _printer;
|
IdealGraphPrinter* _printer;
|
||||||
@ -753,7 +754,7 @@ class Compile : public Phase {
|
|||||||
void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;}
|
void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;}
|
||||||
void add_macro_node(Node * n) {
|
void add_macro_node(Node * n) {
|
||||||
//assert(n->is_macro(), "must be a macro node");
|
//assert(n->is_macro(), "must be a macro node");
|
||||||
assert(!_macro_nodes->contains(n), " duplicate entry in expand list");
|
assert(!_macro_nodes->contains(n), "duplicate entry in expand list");
|
||||||
_macro_nodes->append(n);
|
_macro_nodes->append(n);
|
||||||
}
|
}
|
||||||
void remove_macro_node(Node * n) {
|
void remove_macro_node(Node * n) {
|
||||||
@ -773,10 +774,23 @@ class Compile : public Phase {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
void add_predicate_opaq(Node * n) {
|
void add_predicate_opaq(Node * n) {
|
||||||
assert(!_predicate_opaqs->contains(n), " duplicate entry in predicate opaque1");
|
assert(!_predicate_opaqs->contains(n), "duplicate entry in predicate opaque1");
|
||||||
assert(_macro_nodes->contains(n), "should have already been in macro list");
|
assert(_macro_nodes->contains(n), "should have already been in macro list");
|
||||||
_predicate_opaqs->append(n);
|
_predicate_opaqs->append(n);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Range check dependent CastII nodes that can be removed after loop optimizations
|
||||||
|
void add_range_check_cast(Node* n);
|
||||||
|
void remove_range_check_cast(Node* n) {
|
||||||
|
if (_range_check_casts->contains(n)) {
|
||||||
|
_range_check_casts->remove(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Node* range_check_cast_node(int idx) const { return _range_check_casts->at(idx); }
|
||||||
|
int range_check_cast_count() const { return _range_check_casts->length(); }
|
||||||
|
// Remove all range check dependent CastIINodes.
|
||||||
|
void remove_range_check_casts(PhaseIterGVN &igvn);
|
||||||
|
|
||||||
// remove the opaque nodes that protect the predicates so that the unused checks and
|
// remove the opaque nodes that protect the predicates so that the unused checks and
|
||||||
// uncommon traps will be eliminated from the graph.
|
// uncommon traps will be eliminated from the graph.
|
||||||
void cleanup_loop_predicates(PhaseIterGVN &igvn);
|
void cleanup_loop_predicates(PhaseIterGVN &igvn);
|
||||||
@ -1292,7 +1306,12 @@ class Compile : public Phase {
|
|||||||
enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test };
|
enum { SSC_always_false, SSC_always_true, SSC_easy_test, SSC_full_test };
|
||||||
int static_subtype_check(ciKlass* superk, ciKlass* subk);
|
int static_subtype_check(ciKlass* superk, ciKlass* subk);
|
||||||
|
|
||||||
static Node* conv_I2X_index(PhaseGVN *phase, Node* offset, const TypeInt* sizetype);
|
static Node* conv_I2X_index(PhaseGVN* phase, Node* offset, const TypeInt* sizetype,
|
||||||
|
// Optional control dependency (for example, on range check)
|
||||||
|
Node* ctrl = NULL);
|
||||||
|
|
||||||
|
// Convert integer value to a narrowed long type dependent on ctrl (for example, a range check)
|
||||||
|
static Node* constrained_convI2L(PhaseGVN* phase, Node* value, const TypeInt* itype, Node* ctrl);
|
||||||
|
|
||||||
// Auxiliary method for randomized fuzzing/stressing
|
// Auxiliary method for randomized fuzzing/stressing
|
||||||
static bool randomized_select(int count);
|
static bool randomized_select(int count);
|
||||||
|
@ -24,6 +24,7 @@
|
|||||||
|
|
||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "opto/addnode.hpp"
|
#include "opto/addnode.hpp"
|
||||||
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/convertnode.hpp"
|
#include "opto/convertnode.hpp"
|
||||||
#include "opto/matcher.hpp"
|
#include "opto/matcher.hpp"
|
||||||
#include "opto/phaseX.hpp"
|
#include "opto/phaseX.hpp"
|
||||||
@ -293,7 +294,8 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
// Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) ,
|
// Convert ConvI2L(AddI(x, y)) to AddL(ConvI2L(x), ConvI2L(y)) or
|
||||||
|
// ConvI2L(CastII(AddI(x, y))) to AddL(ConvI2L(CastII(x)), ConvI2L(CastII(y))),
|
||||||
// but only if x and y have subranges that cannot cause 32-bit overflow,
|
// but only if x and y have subranges that cannot cause 32-bit overflow,
|
||||||
// under the assumption that x+y is in my own subrange this->type().
|
// under the assumption that x+y is in my own subrange this->type().
|
||||||
|
|
||||||
@ -317,6 +319,13 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
|
|
||||||
Node* z = in(1);
|
Node* z = in(1);
|
||||||
int op = z->Opcode();
|
int op = z->Opcode();
|
||||||
|
Node* ctrl = NULL;
|
||||||
|
if (op == Op_CastII && z->as_CastII()->has_range_check()) {
|
||||||
|
// Skip CastII node but save control dependency
|
||||||
|
ctrl = z->in(0);
|
||||||
|
z = z->in(1);
|
||||||
|
op = z->Opcode();
|
||||||
|
}
|
||||||
if (op == Op_AddI || op == Op_SubI) {
|
if (op == Op_AddI || op == Op_SubI) {
|
||||||
Node* x = z->in(1);
|
Node* x = z->in(1);
|
||||||
Node* y = z->in(2);
|
Node* y = z->in(2);
|
||||||
@ -374,9 +383,10 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
|||||||
rylo = -ryhi;
|
rylo = -ryhi;
|
||||||
ryhi = -rylo0;
|
ryhi = -rylo0;
|
||||||
}
|
}
|
||||||
|
assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow");
|
||||||
Node* cx = phase->transform( new ConvI2LNode(x, TypeLong::make(rxlo, rxhi, widen)) );
|
assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow");
|
||||||
Node* cy = phase->transform( new ConvI2LNode(y, TypeLong::make(rylo, ryhi, widen)) );
|
Node* cx = phase->C->constrained_convI2L(phase, x, TypeInt::make(rxlo, rxhi, widen), ctrl);
|
||||||
|
Node* cy = phase->C->constrained_convI2L(phase, y, TypeInt::make(rylo, ryhi, widen), ctrl);
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case Op_AddI: return new AddLNode(cx, cy);
|
case Op_AddI: return new AddLNode(cx, cy);
|
||||||
case Op_SubI: return new SubLNode(cx, cy);
|
case Op_SubI: return new SubLNode(cx, cy);
|
||||||
|
@ -1658,7 +1658,7 @@ Node* GraphKit::store_oop_to_unknown(Node* ctl,
|
|||||||
|
|
||||||
//-------------------------array_element_address-------------------------
|
//-------------------------array_element_address-------------------------
|
||||||
Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt,
|
Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt,
|
||||||
const TypeInt* sizetype) {
|
const TypeInt* sizetype, Node* ctrl) {
|
||||||
uint shift = exact_log2(type2aelembytes(elembt));
|
uint shift = exact_log2(type2aelembytes(elembt));
|
||||||
uint header = arrayOopDesc::base_offset_in_bytes(elembt);
|
uint header = arrayOopDesc::base_offset_in_bytes(elembt);
|
||||||
|
|
||||||
@ -1671,7 +1671,7 @@ Node* GraphKit::array_element_address(Node* ary, Node* idx, BasicType elembt,
|
|||||||
|
|
||||||
// must be correct type for alignment purposes
|
// must be correct type for alignment purposes
|
||||||
Node* base = basic_plus_adr(ary, header);
|
Node* base = basic_plus_adr(ary, header);
|
||||||
idx = Compile::conv_I2X_index(&_gvn, idx, sizetype);
|
idx = Compile::conv_I2X_index(&_gvn, idx, sizetype, ctrl);
|
||||||
Node* scale = _gvn.transform( new LShiftXNode(idx, intcon(shift)) );
|
Node* scale = _gvn.transform( new LShiftXNode(idx, intcon(shift)) );
|
||||||
return basic_plus_adr(ary, base, scale);
|
return basic_plus_adr(ary, base, scale);
|
||||||
}
|
}
|
||||||
@ -3506,10 +3506,6 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
|
|||||||
|
|
||||||
Node* initial_slow_cmp = _gvn.transform( new CmpUNode( length, intcon( fast_size_limit ) ) );
|
Node* initial_slow_cmp = _gvn.transform( new CmpUNode( length, intcon( fast_size_limit ) ) );
|
||||||
Node* initial_slow_test = _gvn.transform( new BoolNode( initial_slow_cmp, BoolTest::gt ) );
|
Node* initial_slow_test = _gvn.transform( new BoolNode( initial_slow_cmp, BoolTest::gt ) );
|
||||||
if (initial_slow_test->is_Bool()) {
|
|
||||||
// Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick.
|
|
||||||
initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Size Computation ---
|
// --- Size Computation ---
|
||||||
// array_size = round_to_heap(array_header + (length << elem_shift));
|
// array_size = round_to_heap(array_header + (length << elem_shift));
|
||||||
@ -3555,13 +3551,35 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
|
|||||||
Node* lengthx = ConvI2X(length);
|
Node* lengthx = ConvI2X(length);
|
||||||
Node* headerx = ConvI2X(header_size);
|
Node* headerx = ConvI2X(header_size);
|
||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
{ const TypeLong* tllen = _gvn.find_long_type(lengthx);
|
{ const TypeInt* tilen = _gvn.find_int_type(length);
|
||||||
if (tllen != NULL && tllen->_lo < 0) {
|
if (tilen != NULL && tilen->_lo < 0) {
|
||||||
// Add a manual constraint to a positive range. Cf. array_element_address.
|
// Add a manual constraint to a positive range. Cf. array_element_address.
|
||||||
jlong size_max = arrayOopDesc::max_array_length(T_BYTE);
|
jint size_max = fast_size_limit;
|
||||||
if (size_max > tllen->_hi) size_max = tllen->_hi;
|
if (size_max > tilen->_hi) size_max = tilen->_hi;
|
||||||
const TypeLong* tlcon = TypeLong::make(CONST64(0), size_max, Type::WidenMin);
|
const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin);
|
||||||
lengthx = _gvn.transform( new ConvI2LNode(length, tlcon));
|
|
||||||
|
// Only do a narrow I2L conversion if the range check passed.
|
||||||
|
IfNode* iff = new IfNode(control(), initial_slow_test, PROB_MIN, COUNT_UNKNOWN);
|
||||||
|
_gvn.transform(iff);
|
||||||
|
RegionNode* region = new RegionNode(3);
|
||||||
|
_gvn.set_type(region, Type::CONTROL);
|
||||||
|
lengthx = new PhiNode(region, TypeLong::LONG);
|
||||||
|
_gvn.set_type(lengthx, TypeLong::LONG);
|
||||||
|
|
||||||
|
// Range check passed. Use ConvI2L node with narrow type.
|
||||||
|
Node* passed = IfFalse(iff);
|
||||||
|
region->init_req(1, passed);
|
||||||
|
// Make I2L conversion control dependent to prevent it from
|
||||||
|
// floating above the range check during loop optimizations.
|
||||||
|
lengthx->init_req(1, C->constrained_convI2L(&_gvn, length, tlcon, passed));
|
||||||
|
|
||||||
|
// Range check failed. Use ConvI2L with wide type because length may be invalid.
|
||||||
|
region->init_req(2, IfTrue(iff));
|
||||||
|
lengthx->init_req(2, ConvI2X(length));
|
||||||
|
|
||||||
|
set_control(region);
|
||||||
|
record_for_igvn(region);
|
||||||
|
record_for_igvn(lengthx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -3592,6 +3610,11 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
|
|||||||
Node *mem = reset_memory();
|
Node *mem = reset_memory();
|
||||||
set_all_memory(mem); // Create new memory state
|
set_all_memory(mem); // Create new memory state
|
||||||
|
|
||||||
|
if (initial_slow_test->is_Bool()) {
|
||||||
|
// Hide it behind a CMoveI, or else PhaseIdealLoop::split_up will get sick.
|
||||||
|
initial_slow_test = initial_slow_test->as_Bool()->as_int_value(&_gvn);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the AllocateArrayNode and its result projections
|
// Create the AllocateArrayNode and its result projections
|
||||||
AllocateArrayNode* alloc
|
AllocateArrayNode* alloc
|
||||||
= new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT),
|
= new AllocateArrayNode(C, AllocateArrayNode::alloc_type(TypeInt::INT),
|
||||||
@ -4340,20 +4363,51 @@ void GraphKit::store_String_coder(Node* ctrl, Node* str, Node* value) {
|
|||||||
value, T_BYTE, coder_field_idx, MemNode::unordered);
|
value, T_BYTE, coder_field_idx, MemNode::unordered);
|
||||||
}
|
}
|
||||||
|
|
||||||
Node* GraphKit::compress_string(Node* src, Node* dst, Node* count) {
|
// Capture src and dst memory state with a MergeMemNode
|
||||||
|
Node* GraphKit::capture_memory(const TypePtr* src_type, const TypePtr* dst_type) {
|
||||||
|
if (src_type == dst_type) {
|
||||||
|
// Types are equal, we don't need a MergeMemNode
|
||||||
|
return memory(src_type);
|
||||||
|
}
|
||||||
|
MergeMemNode* merge = MergeMemNode::make(map()->memory());
|
||||||
|
record_for_igvn(merge); // fold it up later, if possible
|
||||||
|
int src_idx = C->get_alias_index(src_type);
|
||||||
|
int dst_idx = C->get_alias_index(dst_type);
|
||||||
|
merge->set_memory_at(src_idx, memory(src_idx));
|
||||||
|
merge->set_memory_at(dst_idx, memory(dst_idx));
|
||||||
|
return merge;
|
||||||
|
}
|
||||||
|
|
||||||
|
Node* GraphKit::compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count) {
|
||||||
assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported");
|
assert(Matcher::match_rule_supported(Op_StrCompressedCopy), "Intrinsic not supported");
|
||||||
uint idx = C->get_alias_index(TypeAryPtr::BYTES);
|
assert(src_type == TypeAryPtr::BYTES || src_type == TypeAryPtr::CHARS, "invalid source type");
|
||||||
StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), memory(idx), src, dst, count);
|
// If input and output memory types differ, capture both states to preserve
|
||||||
|
// the dependency between preceding and subsequent loads/stores.
|
||||||
|
// For example, the following program:
|
||||||
|
// StoreB
|
||||||
|
// compress_string
|
||||||
|
// LoadB
|
||||||
|
// has this memory graph (use->def):
|
||||||
|
// LoadB -> compress_string -> CharMem
|
||||||
|
// ... -> StoreB -> ByteMem
|
||||||
|
// The intrinsic hides the dependency between LoadB and StoreB, causing
|
||||||
|
// the load to read from memory not containing the result of the StoreB.
|
||||||
|
// The correct memory graph should look like this:
|
||||||
|
// LoadB -> compress_string -> MergeMem(CharMem, StoreB(ByteMem))
|
||||||
|
Node* mem = capture_memory(src_type, TypeAryPtr::BYTES);
|
||||||
|
StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), mem, src, dst, count);
|
||||||
Node* res_mem = _gvn.transform(new SCMemProjNode(str));
|
Node* res_mem = _gvn.transform(new SCMemProjNode(str));
|
||||||
set_memory(res_mem, idx);
|
set_memory(res_mem, TypeAryPtr::BYTES);
|
||||||
return str;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphKit::inflate_string(Node* src, Node* dst, Node* count) {
|
void GraphKit::inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count) {
|
||||||
assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported");
|
assert(Matcher::match_rule_supported(Op_StrInflatedCopy), "Intrinsic not supported");
|
||||||
uint idx = C->get_alias_index(TypeAryPtr::BYTES);
|
assert(dst_type == TypeAryPtr::BYTES || dst_type == TypeAryPtr::CHARS, "invalid dest type");
|
||||||
StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), memory(idx), src, dst, count);
|
// Capture src and dst memory (see comment in 'compress_string').
|
||||||
set_memory(_gvn.transform(str), idx);
|
Node* mem = capture_memory(TypeAryPtr::BYTES, dst_type);
|
||||||
|
StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), mem, src, dst, count);
|
||||||
|
set_memory(_gvn.transform(str), dst_type);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) {
|
void GraphKit::inflate_string_slow(Node* src, Node* dst, Node* start, Node* count) {
|
||||||
|
@ -634,7 +634,9 @@ class GraphKit : public Phase {
|
|||||||
// Return addressing for an array element.
|
// Return addressing for an array element.
|
||||||
Node* array_element_address(Node* ary, Node* idx, BasicType elembt,
|
Node* array_element_address(Node* ary, Node* idx, BasicType elembt,
|
||||||
// Optional constraint on the array size:
|
// Optional constraint on the array size:
|
||||||
const TypeInt* sizetype = NULL);
|
const TypeInt* sizetype = NULL,
|
||||||
|
// Optional control dependency (for example, on range check)
|
||||||
|
Node* ctrl = NULL);
|
||||||
|
|
||||||
// Return a load of array element at idx.
|
// Return a load of array element at idx.
|
||||||
Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype);
|
Node* load_array_element(Node* ctl, Node* ary, Node* idx, const TypeAryPtr* arytype);
|
||||||
@ -881,8 +883,9 @@ class GraphKit : public Phase {
|
|||||||
Node* load_String_coder(Node* ctrl, Node* str);
|
Node* load_String_coder(Node* ctrl, Node* str);
|
||||||
void store_String_value(Node* ctrl, Node* str, Node* value);
|
void store_String_value(Node* ctrl, Node* str, Node* value);
|
||||||
void store_String_coder(Node* ctrl, Node* str, Node* value);
|
void store_String_coder(Node* ctrl, Node* str, Node* value);
|
||||||
Node* compress_string(Node* src, Node* dst, Node* count);
|
Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type);
|
||||||
void inflate_string(Node* src, Node* dst, Node* count);
|
Node* compress_string(Node* src, const TypeAryPtr* src_type, Node* dst, Node* count);
|
||||||
|
void inflate_string(Node* src, Node* dst, const TypeAryPtr* dst_type, Node* count);
|
||||||
void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count);
|
void inflate_string_slow(Node* src, Node* dst, Node* start, Node* count);
|
||||||
|
|
||||||
// Handy for making control flow
|
// Handy for making control flow
|
||||||
|
@ -1104,7 +1104,8 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV
|
|||||||
if (ctrl == fail) {
|
if (ctrl == fail) {
|
||||||
Node* init_n = stack.node_at(1);
|
Node* init_n = stack.node_at(1);
|
||||||
assert(init_n->Opcode() == Op_ConvI2L, "unexpected first node");
|
assert(init_n->Opcode() == Op_ConvI2L, "unexpected first node");
|
||||||
Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size);
|
// Create a new narrow ConvI2L node that is dependent on the range check
|
||||||
|
Node* new_n = igvn->C->conv_I2X_index(igvn, l, array_size, fail);
|
||||||
|
|
||||||
// The type of the ConvI2L may be widen and so the new
|
// The type of the ConvI2L may be widen and so the new
|
||||||
// ConvI2L may not be better than an existing ConvI2L
|
// ConvI2L may not be better than an existing ConvI2L
|
||||||
|
@ -1359,9 +1359,9 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
|
|||||||
// 'dst_start' points to dst array + scaled offset
|
// 'dst_start' points to dst array + scaled offset
|
||||||
Node* count = NULL;
|
Node* count = NULL;
|
||||||
if (compress) {
|
if (compress) {
|
||||||
count = compress_string(src_start, dst_start, length);
|
count = compress_string(src_start, TypeAryPtr::get_array_body_type(src_elem), dst_start, length);
|
||||||
} else {
|
} else {
|
||||||
inflate_string(src_start, dst_start, length);
|
inflate_string(src_start, dst_start, TypeAryPtr::get_array_body_type(dst_elem), length);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (alloc != NULL) {
|
if (alloc != NULL) {
|
||||||
@ -1587,7 +1587,7 @@ bool LibraryCallKit::inline_string_char_access(bool is_store) {
|
|||||||
(void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered,
|
(void) store_to_memory(control(), adr, ch, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered,
|
||||||
false, false, true /* mismatched */);
|
false, false, true /* mismatched */);
|
||||||
} else {
|
} else {
|
||||||
ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, MemNode::unordered,
|
ch = make_load(control(), adr, TypeInt::CHAR, T_CHAR, TypeAryPtr::BYTES, MemNode::unordered,
|
||||||
LoadNode::DependsOnlyOnTest, false, false, true /* mismatched */);
|
LoadNode::DependsOnlyOnTest, false, false, true /* mismatched */);
|
||||||
set_result(ch);
|
set_result(ch);
|
||||||
}
|
}
|
||||||
|
@ -2660,7 +2660,7 @@ bool IdealLoopTree::iteration_split( PhaseIdealLoop *phase, Node_List &old_new )
|
|||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
// Process all the loops in the loop tree and replace any fill
|
// Process all the loops in the loop tree and replace any fill
|
||||||
// patterns with an intrisc version.
|
// patterns with an intrinsic version.
|
||||||
bool PhaseIdealLoop::do_intrinsify_fill() {
|
bool PhaseIdealLoop::do_intrinsify_fill() {
|
||||||
bool changed = false;
|
bool changed = false;
|
||||||
for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) {
|
for (LoopTreeIterator iter(_ltree_root); !iter.done(); iter.next()) {
|
||||||
@ -2758,8 +2758,9 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the address expression can be handled. It should be
|
// Make sure the address expression can be handled. It should be
|
||||||
// head->phi * elsize + con. head->phi might have a ConvI2L.
|
// head->phi * elsize + con. head->phi might have a ConvI2L(CastII()).
|
||||||
Node* elements[4];
|
Node* elements[4];
|
||||||
|
Node* cast = NULL;
|
||||||
Node* conv = NULL;
|
Node* conv = NULL;
|
||||||
bool found_index = false;
|
bool found_index = false;
|
||||||
int count = store->in(MemNode::Address)->as_AddP()->unpack_offsets(elements, ARRAY_SIZE(elements));
|
int count = store->in(MemNode::Address)->as_AddP()->unpack_offsets(elements, ARRAY_SIZE(elements));
|
||||||
@ -2774,6 +2775,12 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st
|
|||||||
conv = value;
|
conv = value;
|
||||||
value = value->in(1);
|
value = value->in(1);
|
||||||
}
|
}
|
||||||
|
if (value->Opcode() == Op_CastII &&
|
||||||
|
value->as_CastII()->has_range_check()) {
|
||||||
|
// Skip range check dependent CastII nodes
|
||||||
|
cast = value;
|
||||||
|
value = value->in(1);
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
if (value != head->phi()) {
|
if (value != head->phi()) {
|
||||||
msg = "unhandled shift in address";
|
msg = "unhandled shift in address";
|
||||||
@ -2786,9 +2793,16 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (n->Opcode() == Op_ConvI2L && conv == NULL) {
|
} else if (n->Opcode() == Op_ConvI2L && conv == NULL) {
|
||||||
if (n->in(1) == head->phi()) {
|
conv = n;
|
||||||
|
n = n->in(1);
|
||||||
|
if (n->Opcode() == Op_CastII &&
|
||||||
|
n->as_CastII()->has_range_check()) {
|
||||||
|
// Skip range check dependent CastII nodes
|
||||||
|
cast = n;
|
||||||
|
n = n->in(1);
|
||||||
|
}
|
||||||
|
if (n == head->phi()) {
|
||||||
found_index = true;
|
found_index = true;
|
||||||
conv = n;
|
|
||||||
} else {
|
} else {
|
||||||
msg = "unhandled input to ConvI2L";
|
msg = "unhandled input to ConvI2L";
|
||||||
}
|
}
|
||||||
@ -2847,6 +2861,7 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st
|
|||||||
// Address elements are ok
|
// Address elements are ok
|
||||||
if (con) ok.set(con->_idx);
|
if (con) ok.set(con->_idx);
|
||||||
if (shift) ok.set(shift->_idx);
|
if (shift) ok.set(shift->_idx);
|
||||||
|
if (cast) ok.set(cast->_idx);
|
||||||
if (conv) ok.set(conv->_idx);
|
if (conv) ok.set(conv->_idx);
|
||||||
|
|
||||||
for (uint i = 0; msg == NULL && i < lpt->_body.size(); i++) {
|
for (uint i = 0; msg == NULL && i < lpt->_body.size(); i++) {
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "opto/addnode.hpp"
|
#include "opto/addnode.hpp"
|
||||||
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/connode.hpp"
|
#include "opto/connode.hpp"
|
||||||
#include "opto/castnode.hpp"
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/divnode.hpp"
|
#include "opto/divnode.hpp"
|
||||||
@ -997,6 +998,9 @@ static bool merge_point_safe(Node* region) {
|
|||||||
#ifdef _LP64
|
#ifdef _LP64
|
||||||
if (m->Opcode() == Op_ConvI2L)
|
if (m->Opcode() == Op_ConvI2L)
|
||||||
return false;
|
return false;
|
||||||
|
if (m->is_CastII() && m->isa_CastII()->has_range_check()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
#include "precompiled.hpp"
|
#include "precompiled.hpp"
|
||||||
#include "libadt/vectset.hpp"
|
#include "libadt/vectset.hpp"
|
||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/cfgnode.hpp"
|
#include "opto/cfgnode.hpp"
|
||||||
#include "opto/connode.hpp"
|
#include "opto/connode.hpp"
|
||||||
#include "opto/loopnode.hpp"
|
#include "opto/loopnode.hpp"
|
||||||
@ -516,6 +517,11 @@ Node *Node::clone() const {
|
|||||||
C->add_macro_node(n);
|
C->add_macro_node(n);
|
||||||
if (is_expensive())
|
if (is_expensive())
|
||||||
C->add_expensive_node(n);
|
C->add_expensive_node(n);
|
||||||
|
// If the cloned node is a range check dependent CastII, add it to the list.
|
||||||
|
CastIINode* cast = n->isa_CastII();
|
||||||
|
if (cast != NULL && cast->has_range_check()) {
|
||||||
|
C->add_range_check_cast(cast);
|
||||||
|
}
|
||||||
|
|
||||||
n->set_idx(C->next_unique()); // Get new unique index as well
|
n->set_idx(C->next_unique()); // Get new unique index as well
|
||||||
debug_only( n->verify_construction() );
|
debug_only( n->verify_construction() );
|
||||||
@ -644,6 +650,11 @@ void Node::destruct() {
|
|||||||
if (is_expensive()) {
|
if (is_expensive()) {
|
||||||
compile->remove_expensive_node(this);
|
compile->remove_expensive_node(this);
|
||||||
}
|
}
|
||||||
|
CastIINode* cast = isa_CastII();
|
||||||
|
if (cast != NULL && cast->has_range_check()) {
|
||||||
|
compile->remove_range_check_cast(cast);
|
||||||
|
}
|
||||||
|
|
||||||
if (is_SafePoint()) {
|
if (is_SafePoint()) {
|
||||||
as_SafePoint()->delete_replaced_nodes();
|
as_SafePoint()->delete_replaced_nodes();
|
||||||
}
|
}
|
||||||
@ -1379,6 +1390,10 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) {
|
|||||||
if (dead->is_expensive()) {
|
if (dead->is_expensive()) {
|
||||||
igvn->C->remove_expensive_node(dead);
|
igvn->C->remove_expensive_node(dead);
|
||||||
}
|
}
|
||||||
|
CastIINode* cast = dead->isa_CastII();
|
||||||
|
if (cast != NULL && cast->has_range_check()) {
|
||||||
|
igvn->C->remove_range_check_cast(cast);
|
||||||
|
}
|
||||||
igvn->C->record_dead_node(dead->_idx);
|
igvn->C->record_dead_node(dead->_idx);
|
||||||
// Kill all inputs to the dead guy
|
// Kill all inputs to the dead guy
|
||||||
for (uint i=0; i < dead->req(); i++) {
|
for (uint i=0; i < dead->req(); i++) {
|
||||||
|
@ -51,6 +51,7 @@ class CallLeafNode;
|
|||||||
class CallNode;
|
class CallNode;
|
||||||
class CallRuntimeNode;
|
class CallRuntimeNode;
|
||||||
class CallStaticJavaNode;
|
class CallStaticJavaNode;
|
||||||
|
class CastIINode;
|
||||||
class CatchNode;
|
class CatchNode;
|
||||||
class CatchProjNode;
|
class CatchProjNode;
|
||||||
class CheckCastPPNode;
|
class CheckCastPPNode;
|
||||||
@ -653,6 +654,7 @@ public:
|
|||||||
DEFINE_CLASS_ID(Type, Node, 2)
|
DEFINE_CLASS_ID(Type, Node, 2)
|
||||||
DEFINE_CLASS_ID(Phi, Type, 0)
|
DEFINE_CLASS_ID(Phi, Type, 0)
|
||||||
DEFINE_CLASS_ID(ConstraintCast, Type, 1)
|
DEFINE_CLASS_ID(ConstraintCast, Type, 1)
|
||||||
|
DEFINE_CLASS_ID(CastII, ConstraintCast, 0)
|
||||||
DEFINE_CLASS_ID(CheckCastPP, Type, 2)
|
DEFINE_CLASS_ID(CheckCastPP, Type, 2)
|
||||||
DEFINE_CLASS_ID(CMove, Type, 3)
|
DEFINE_CLASS_ID(CMove, Type, 3)
|
||||||
DEFINE_CLASS_ID(SafePointScalarObject, Type, 4)
|
DEFINE_CLASS_ID(SafePointScalarObject, Type, 4)
|
||||||
@ -784,6 +786,7 @@ public:
|
|||||||
DEFINE_CLASS_QUERY(Catch)
|
DEFINE_CLASS_QUERY(Catch)
|
||||||
DEFINE_CLASS_QUERY(CatchProj)
|
DEFINE_CLASS_QUERY(CatchProj)
|
||||||
DEFINE_CLASS_QUERY(CheckCastPP)
|
DEFINE_CLASS_QUERY(CheckCastPP)
|
||||||
|
DEFINE_CLASS_QUERY(CastII)
|
||||||
DEFINE_CLASS_QUERY(ConstraintCast)
|
DEFINE_CLASS_QUERY(ConstraintCast)
|
||||||
DEFINE_CLASS_QUERY(ClearArray)
|
DEFINE_CLASS_QUERY(ClearArray)
|
||||||
DEFINE_CLASS_QUERY(CMove)
|
DEFINE_CLASS_QUERY(CMove)
|
||||||
|
@ -166,7 +166,9 @@ Node* Parse::array_addressing(BasicType type, int vals, const Type* *result2) {
|
|||||||
// Check for always knowing you are throwing a range-check exception
|
// Check for always knowing you are throwing a range-check exception
|
||||||
if (stopped()) return top();
|
if (stopped()) return top();
|
||||||
|
|
||||||
Node* ptr = array_element_address(ary, idx, type, sizetype);
|
// Make array address computation control dependent to prevent it
|
||||||
|
// from floating above the range check during loop optimizations.
|
||||||
|
Node* ptr = array_element_address(ary, idx, type, sizetype, control());
|
||||||
|
|
||||||
if (result2 != NULL) *result2 = elemtype;
|
if (result2 != NULL) *result2 = elemtype;
|
||||||
|
|
||||||
@ -466,12 +468,14 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi)
|
|||||||
// of all possible ranges for a switch statement
|
// of all possible ranges for a switch statement
|
||||||
// The key_val input must be converted to a pointer offset and scaled.
|
// The key_val input must be converted to a pointer offset and scaled.
|
||||||
// Compare Parse::array_addressing above.
|
// Compare Parse::array_addressing above.
|
||||||
#ifdef _LP64
|
|
||||||
// Clean the 32-bit int into a real 64-bit offset.
|
// Clean the 32-bit int into a real 64-bit offset.
|
||||||
// Otherwise, the jint value 0 might turn into an offset of 0x0800000000.
|
// Otherwise, the jint value 0 might turn into an offset of 0x0800000000.
|
||||||
const TypeLong* lkeytype = TypeLong::make(CONST64(0), num_cases-1, Type::WidenMin);
|
const TypeInt* ikeytype = TypeInt::make(0, num_cases, Type::WidenMin);
|
||||||
key_val = _gvn.transform( new ConvI2LNode(key_val, lkeytype) );
|
// Make I2L conversion control dependent to prevent it from
|
||||||
#endif
|
// floating above the range check during loop optimizations.
|
||||||
|
key_val = C->conv_I2X_index(&_gvn, key_val, ikeytype, control());
|
||||||
|
|
||||||
// Shift the value by wordsize so we have an index into the table, rather
|
// Shift the value by wordsize so we have an index into the table, rather
|
||||||
// than a switch value
|
// than a switch value
|
||||||
Node *shiftWord = _gvn.MakeConX(wordSize);
|
Node *shiftWord = _gvn.MakeConX(wordSize);
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
#include "memory/allocation.inline.hpp"
|
#include "memory/allocation.inline.hpp"
|
||||||
#include "opto/block.hpp"
|
#include "opto/block.hpp"
|
||||||
#include "opto/callnode.hpp"
|
#include "opto/callnode.hpp"
|
||||||
|
#include "opto/castnode.hpp"
|
||||||
#include "opto/cfgnode.hpp"
|
#include "opto/cfgnode.hpp"
|
||||||
#include "opto/idealGraphPrinter.hpp"
|
#include "opto/idealGraphPrinter.hpp"
|
||||||
#include "opto/loopnode.hpp"
|
#include "opto/loopnode.hpp"
|
||||||
@ -1412,6 +1413,10 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
|
|||||||
if (dead->is_expensive()) {
|
if (dead->is_expensive()) {
|
||||||
C->remove_expensive_node(dead);
|
C->remove_expensive_node(dead);
|
||||||
}
|
}
|
||||||
|
CastIINode* cast = dead->isa_CastII();
|
||||||
|
if (cast != NULL && cast->has_range_check()) {
|
||||||
|
C->remove_range_check_cast(cast);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} // while (_stack.is_nonempty())
|
} // while (_stack.is_nonempty())
|
||||||
}
|
}
|
||||||
|
@ -1466,7 +1466,7 @@ void PhaseStringOpts::copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* s
|
|||||||
// Use fast intrinsic
|
// Use fast intrinsic
|
||||||
Node* src = kit.array_element_address(src_array, kit.intcon(0), T_BYTE);
|
Node* src = kit.array_element_address(src_array, kit.intcon(0), T_BYTE);
|
||||||
Node* dst = kit.array_element_address(dst_array, start, T_BYTE);
|
Node* dst = kit.array_element_address(dst_array, start, T_BYTE);
|
||||||
kit.inflate_string(src, dst, __ value(count));
|
kit.inflate_string(src, dst, TypeAryPtr::BYTES, __ value(count));
|
||||||
} else {
|
} else {
|
||||||
// No intrinsic available, use slow method
|
// No intrinsic available, use slow method
|
||||||
kit.inflate_string_slow(src_array, dst_array, start, __ value(count));
|
kit.inflate_string_slow(src_array, dst_array, start, __ value(count));
|
||||||
|
@ -3343,6 +3343,11 @@ bool SWPointer::scaled_iv(Node* n) {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
} else if (opc == Op_ConvI2L) {
|
} else if (opc == Op_ConvI2L) {
|
||||||
|
if (n->in(1)->Opcode() == Op_CastII &&
|
||||||
|
n->in(1)->as_CastII()->has_range_check()) {
|
||||||
|
// Skip range check dependent CastII nodes
|
||||||
|
n = n->in(1);
|
||||||
|
}
|
||||||
if (scaled_iv_plus_offset(n->in(1))) {
|
if (scaled_iv_plus_offset(n->in(1))) {
|
||||||
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
|
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
|
||||||
return true;
|
return true;
|
||||||
@ -3437,6 +3442,12 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) {
|
|||||||
if (invariant(n)) {
|
if (invariant(n)) {
|
||||||
if (opc == Op_ConvI2L) {
|
if (opc == Op_ConvI2L) {
|
||||||
n = n->in(1);
|
n = n->in(1);
|
||||||
|
if (n->Opcode() == Op_CastII &&
|
||||||
|
n->as_CastII()->has_range_check()) {
|
||||||
|
// Skip range check dependent CastII nodes
|
||||||
|
assert(invariant(n), "sanity");
|
||||||
|
n = n->in(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (n->bottom_type()->isa_int()) {
|
if (n->bottom_type()->isa_int()) {
|
||||||
_negate_invar = negate;
|
_negate_invar = negate;
|
||||||
|
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
import jdk.test.lib.Asserts;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8144212
|
||||||
|
* @summary Check for correct memory flow with the String compress/inflate intrinsics.
|
||||||
|
* @library /testlibrary
|
||||||
|
* @run main TestStringIntrinsicMemoryFlow
|
||||||
|
*/
|
||||||
|
public class TestStringIntrinsicMemoryFlow {
|
||||||
|
|
||||||
|
public static void main(String[] args) {
|
||||||
|
for (int i = 0; i < 100_000; ++i) {
|
||||||
|
String s = "MyString";
|
||||||
|
char[] c = {'M'};
|
||||||
|
char res = testInflate1(s);
|
||||||
|
Asserts.assertEquals(res, 'M', "testInflate1 failed");
|
||||||
|
res = testInflate2(s);
|
||||||
|
Asserts.assertEquals(res, (char)42, "testInflate2 failed");
|
||||||
|
res = testCompress1(c);
|
||||||
|
Asserts.assertEquals(res, 'M', "testCompress1 failed");
|
||||||
|
byte resB = testCompress2(c);
|
||||||
|
Asserts.assertEquals(resB, (byte)42, "testCompress2 failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char testInflate1(String s) {
|
||||||
|
char c[] = new char[1];
|
||||||
|
// Inflate String from byte[] to char[]
|
||||||
|
s.getChars(0, 1, c, 0);
|
||||||
|
// Read char[] memory written by inflate intrinsic
|
||||||
|
return c[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char testInflate2(String s) {
|
||||||
|
char c1[] = new char[1];
|
||||||
|
char c2[] = new char[1];
|
||||||
|
c2[0] = 42;
|
||||||
|
// Inflate String from byte[] to char[]
|
||||||
|
s.getChars(0, 1, c1, 0);
|
||||||
|
// Read char[] memory written before inflation
|
||||||
|
return c2[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private static char testCompress1(char[] c) {
|
||||||
|
// Compress String from char[] to byte[]
|
||||||
|
String s = new String(c);
|
||||||
|
// Read the memory written by compress intrinsic
|
||||||
|
return s.charAt(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte testCompress2(char[] c) {
|
||||||
|
byte b1[] = new byte[1];
|
||||||
|
b1[0] = 42;
|
||||||
|
// Compress String from char[] to byte[]
|
||||||
|
new String(c);
|
||||||
|
// Read byte[] memory written before compression
|
||||||
|
return b1[0];
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -30,7 +30,6 @@
|
|||||||
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
* sun.hotspot.WhiteBox$WhiteBoxPermission
|
||||||
* java.lang.invoke.RedefineTest
|
* java.lang.invoke.RedefineTest
|
||||||
* Agent
|
* Agent
|
||||||
* jdk.test.lib.Asserts
|
|
||||||
* @run main Agent agent.jar java.lang.invoke.RedefineTest
|
* @run main Agent agent.jar java.lang.invoke.RedefineTest
|
||||||
* @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar
|
* @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar
|
||||||
* -XX:+IgnoreUnrecognizedVMOptions
|
* -XX:+IgnoreUnrecognizedVMOptions
|
||||||
|
100
hotspot/test/compiler/loopopts/TestLoopPeeling.java
Normal file
100
hotspot/test/compiler/loopopts/TestLoopPeeling.java
Normal file
@ -0,0 +1,100 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016, 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* @test
|
||||||
|
* @bug 8078262
|
||||||
|
* @summary Tests correct dominator information after loop peeling.
|
||||||
|
* @run main/othervm -Xcomp -XX:CompileCommand=compileonly,TestLoopPeeling::test* TestLoopPeeling
|
||||||
|
*/
|
||||||
|
public class TestLoopPeeling {
|
||||||
|
|
||||||
|
public int[] array = new int[100];
|
||||||
|
|
||||||
|
public static void main(String args[]) {
|
||||||
|
TestLoopPeeling test = new TestLoopPeeling();
|
||||||
|
try {
|
||||||
|
test.testArrayAccess(0, 1);
|
||||||
|
test.testArrayAllocation(0, 1);
|
||||||
|
} catch (Exception e) {
|
||||||
|
// Ignore exceptions
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void testArrayAccess(int index, int inc) {
|
||||||
|
int storeIndex = -1;
|
||||||
|
|
||||||
|
for (; index < 10; index += inc) {
|
||||||
|
// This loop invariant check triggers loop peeling because it can
|
||||||
|
// be moved out of the loop (see 'IdealLoopTree::policy_peeling').
|
||||||
|
if (inc == 42) return;
|
||||||
|
|
||||||
|
// This loop variant usage of LShiftL( ConvI2L( Phi(storeIndex) ) )
|
||||||
|
// prevents the split if optimization that would otherwise clone the
|
||||||
|
// LShiftL and ConvI2L nodes and assign them to their corresponding array
|
||||||
|
// address computation (see 'PhaseIdealLoop::split_if_with_blocks_post').
|
||||||
|
if (storeIndex > 0 && array[storeIndex] == 42) return;
|
||||||
|
|
||||||
|
if (index == 42) {
|
||||||
|
// This store and the corresponding range check are moved out of the
|
||||||
|
// loop and both used after old loop and the peeled iteration exit.
|
||||||
|
// For the peeled iteration, storeIndex is always -1 and the ConvI2L
|
||||||
|
// is replaced by TOP. However, the range check is not folded because
|
||||||
|
// we don't do the split if optimization in PhaseIdealLoop2.
|
||||||
|
// As a result, we have a (dead) control path from the peeled iteration
|
||||||
|
// to the StoreI but the data path is removed.
|
||||||
|
array[storeIndex] = 1;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
storeIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] testArrayAllocation(int index, int inc) {
|
||||||
|
int allocationCount = -1;
|
||||||
|
byte[] result;
|
||||||
|
|
||||||
|
for (; index < 10; index += inc) {
|
||||||
|
// This loop invariant check triggers loop peeling because it can
|
||||||
|
// be moved out of the loop (see 'IdealLoopTree::policy_peeling').
|
||||||
|
if (inc == 42) return null;
|
||||||
|
|
||||||
|
if (index == 42) {
|
||||||
|
// This allocation and the corresponding size check are moved out of the
|
||||||
|
// loop and both used after old loop and the peeled iteration exit.
|
||||||
|
// For the peeled iteration, allocationCount is always -1 and the ConvI2L
|
||||||
|
// is replaced by TOP. However, the size check is not folded because
|
||||||
|
// we don't do the split if optimization in PhaseIdealLoop2.
|
||||||
|
// As a result, we have a (dead) control path from the peeled iteration
|
||||||
|
// to the allocation but the data path is removed.
|
||||||
|
result = new byte[allocationCount];
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
allocationCount++;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
|
* Copyright (c) 2016, Oracle and/or its affiliates. All rights reserved.
|
||||||
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
||||||
*
|
*
|
||||||
* This code is free software; you can redistribute it and/or modify it
|
* This code is free software; you can redistribute it and/or modify it
|
||||||
@ -21,6 +21,7 @@
|
|||||||
* questions.
|
* questions.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.nio.file.Files;
|
import java.nio.file.Files;
|
||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
@ -42,6 +43,9 @@ public class ClassFileInstaller {
|
|||||||
// Convert dotted class name to a path to a class file
|
// Convert dotted class name to a path to a class file
|
||||||
String pathName = arg.replace('.', '/').concat(".class");
|
String pathName = arg.replace('.', '/').concat(".class");
|
||||||
InputStream is = cl.getResourceAsStream(pathName);
|
InputStream is = cl.getResourceAsStream(pathName);
|
||||||
|
if (is == null) {
|
||||||
|
throw new FileNotFoundException(pathName);
|
||||||
|
}
|
||||||
|
|
||||||
// Create the class file's package directory
|
// Create the class file's package directory
|
||||||
Path p = Paths.get(pathName);
|
Path p = Paths.get(pathName);
|
||||||
|
Loading…
Reference in New Issue
Block a user