This commit is contained in:
Tobias Hartmann 2016-01-18 11:02:42 +01:00
commit 40164313e3
23 changed files with 479 additions and 61 deletions

View File

@ -89,7 +89,6 @@ private:
static const TypePtr* get_address_type(PhaseGVN *phase, Node* n);
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,
Node*& adr_src, Node*& base_src, Node*& adr_dest, Node*& base_dest,
BasicType& copy_type, const Type*& value_type, bool& disjoint_bases);

View File

@ -277,6 +277,23 @@ Node *CastIINode::Ideal(PhaseGVN *phase, bool can_reshape) {
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---------------------------------------
// If input is already higher or equal to cast type, then this is an identity.

View File

@ -62,13 +62,33 @@ class ConstraintCastNode: public TypeNode {
//------------------------------CastIINode-------------------------------------
// cast integer to integer (different range)
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:
CastIINode(Node *n, const Type *t, bool carry_dependency = false)
: ConstraintCastNode(n, t, carry_dependency) {}
CastIINode(Node* n, const Type* t, bool carry_dependency = false, bool range_check_dependency = false)
: ConstraintCastNode(n, t, carry_dependency), _range_check_dependency(range_check_dependency) {
init_class_id(Class_CastII);
}
virtual int Opcode() const;
virtual uint ideal_reg() const { return Op_RegI; }
virtual const Type* Value(PhaseGVN* phase) const;
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-------------------------------------

View File

@ -277,13 +277,6 @@ class IfNode : public MultiBranchNode {
virtual uint size_of() const { return sizeof(*this); }
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
bool cmpi_folds(PhaseIterGVN* igvn);
bool is_ctrl_folds(Node* ctrl, PhaseIterGVN* igvn);

View File

@ -38,6 +38,7 @@
#include "opto/c2compiler.hpp"
#include "opto/callGenerator.hpp"
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/chaitin.hpp"
#include "opto/compile.hpp"
@ -402,6 +403,13 @@ void Compile::remove_useless_nodes(Unique_Node_List &useful) {
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
for (int i = C->expensive_count()-1; i >= 0; 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);
_predicate_opaqs = 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();
}
@ -1924,6 +1933,22 @@ void Compile::cleanup_loop_predicates(PhaseIterGVN &igvn) {
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
void Compile::inline_string_calls(bool parse_time) {
{
@ -2284,6 +2309,12 @@ void Compile::Optimize() {
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]);
PhaseMacroExpand mex(igvn);
@ -3087,6 +3118,16 @@ void Compile::final_graph_reshaping_impl( Node *n, Final_Reshape_Counts &frc) {
#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:
if (UseDivMod) {
// 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;
}
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
// 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
@ -3975,13 +4016,31 @@ Node* Compile::conv_I2X_index(PhaseGVN *phase, Node* idx, const TypeInt* sizetyp
// number. (The prior range check has ensured this.)
// This assertion is used by ConvI2LNode::Ideal.
int index_max = max_jint - 1; // array size is max_jint, index is one less
if (sizetype != NULL) index_max = sizetype->_hi - 1;
const TypeLong* lidxtype = TypeLong::make(CONST64(0), index_max, Type::WidenMax);
idx = phase->transform(new ConvI2LNode(idx, lidxtype));
if (sizetype != NULL) index_max = sizetype->_hi - 1;
const TypeInt* iidxtype = TypeInt::make(0, index_max, Type::WidenMax);
idx = constrained_convI2L(phase, idx, iidxtype, ctrl);
#endif
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
// _print_inlining_stream and transfered into the _print_inlining_list
// once we know whether inlining succeeds or not. For regular

View File

@ -400,6 +400,7 @@ class Compile : public Phase {
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*>* _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;
#ifndef PRODUCT
IdealGraphPrinter* _printer;
@ -753,7 +754,7 @@ class Compile : public Phase {
void set_congraph(ConnectionGraph* congraph) { _congraph = congraph;}
void add_macro_node(Node * n) {
//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);
}
void remove_macro_node(Node * n) {
@ -773,10 +774,23 @@ class Compile : public Phase {
}
}
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");
_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
// uncommon traps will be eliminated from the graph.
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 };
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
static bool randomized_select(int count);

View File

@ -24,6 +24,7 @@
#include "precompiled.hpp"
#include "opto/addnode.hpp"
#include "opto/castnode.hpp"
#include "opto/convertnode.hpp"
#include "opto/matcher.hpp"
#include "opto/phaseX.hpp"
@ -293,7 +294,8 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
}
#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,
// 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);
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) {
Node* x = z->in(1);
Node* y = z->in(2);
@ -374,9 +383,10 @@ Node *ConvI2LNode::Ideal(PhaseGVN *phase, bool can_reshape) {
rylo = -ryhi;
ryhi = -rylo0;
}
Node* cx = phase->transform( new ConvI2LNode(x, TypeLong::make(rxlo, rxhi, widen)) );
Node* cy = phase->transform( new ConvI2LNode(y, TypeLong::make(rylo, ryhi, widen)) );
assert(rxlo == (int)rxlo && rxhi == (int)rxhi, "x should not overflow");
assert(rylo == (int)rylo && ryhi == (int)ryhi, "y should not overflow");
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) {
case Op_AddI: return new AddLNode(cx, cy);
case Op_SubI: return new SubLNode(cx, cy);

View File

@ -1658,7 +1658,7 @@ Node* GraphKit::store_oop_to_unknown(Node* ctl,
//-------------------------array_element_address-------------------------
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 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
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)) );
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_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 ---
// 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* headerx = ConvI2X(header_size);
#ifdef _LP64
{ const TypeLong* tllen = _gvn.find_long_type(lengthx);
if (tllen != NULL && tllen->_lo < 0) {
{ const TypeInt* tilen = _gvn.find_int_type(length);
if (tilen != NULL && tilen->_lo < 0) {
// Add a manual constraint to a positive range. Cf. array_element_address.
jlong size_max = arrayOopDesc::max_array_length(T_BYTE);
if (size_max > tllen->_hi) size_max = tllen->_hi;
const TypeLong* tlcon = TypeLong::make(CONST64(0), size_max, Type::WidenMin);
lengthx = _gvn.transform( new ConvI2LNode(length, tlcon));
jint size_max = fast_size_limit;
if (size_max > tilen->_hi) size_max = tilen->_hi;
const TypeInt* tlcon = TypeInt::make(0, size_max, Type::WidenMin);
// 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
@ -3592,6 +3610,11 @@ Node* GraphKit::new_array(Node* klass_node, // array klass (maybe variable)
Node *mem = reset_memory();
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
AllocateArrayNode* alloc
= 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);
}
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");
uint idx = C->get_alias_index(TypeAryPtr::BYTES);
StrCompressedCopyNode* str = new StrCompressedCopyNode(control(), memory(idx), src, dst, count);
assert(src_type == TypeAryPtr::BYTES || src_type == TypeAryPtr::CHARS, "invalid source type");
// 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));
set_memory(res_mem, idx);
set_memory(res_mem, TypeAryPtr::BYTES);
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");
uint idx = C->get_alias_index(TypeAryPtr::BYTES);
StrInflatedCopyNode* str = new StrInflatedCopyNode(control(), memory(idx), src, dst, count);
set_memory(_gvn.transform(str), idx);
assert(dst_type == TypeAryPtr::BYTES || dst_type == TypeAryPtr::CHARS, "invalid dest type");
// Capture src and dst memory (see comment in 'compress_string').
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) {

View File

@ -634,7 +634,9 @@ class GraphKit : public Phase {
// Return addressing for an array element.
Node* array_element_address(Node* ary, Node* idx, BasicType elembt,
// 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.
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);
void store_String_value(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);
void inflate_string(Node* src, Node* dst, Node* count);
Node* capture_memory(const TypePtr* src_type, const TypePtr* dst_type);
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);
// Handy for making control flow

View File

@ -1104,7 +1104,8 @@ void IfNode::improve_address_types(Node* l, Node* r, ProjNode* fail, PhaseIterGV
if (ctrl == fail) {
Node* init_n = stack.node_at(1);
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
// ConvI2L may not be better than an existing ConvI2L

View File

@ -1359,9 +1359,9 @@ bool LibraryCallKit::inline_string_copy(bool compress) {
// 'dst_start' points to dst array + scaled offset
Node* count = NULL;
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 {
inflate_string(src_start, dst_start, length);
inflate_string(src_start, dst_start, TypeAryPtr::get_array_body_type(dst_elem), length);
}
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,
false, false, true /* mismatched */);
} 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 */);
set_result(ch);
}

View File

@ -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
// patterns with an intrisc version.
// patterns with an intrinsic version.
bool PhaseIdealLoop::do_intrinsify_fill() {
bool changed = false;
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
// 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* cast = NULL;
Node* conv = NULL;
bool found_index = false;
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;
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
if (value != head->phi()) {
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) {
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;
conv = n;
} else {
msg = "unhandled input to ConvI2L";
}
@ -2847,6 +2861,7 @@ bool PhaseIdealLoop::match_fill_loop(IdealLoopTree* lpt, Node*& store, Node*& st
// Address elements are ok
if (con) ok.set(con->_idx);
if (shift) ok.set(shift->_idx);
if (cast) ok.set(cast->_idx);
if (conv) ok.set(conv->_idx);
for (uint i = 0; msg == NULL && i < lpt->_body.size(); i++) {

View File

@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/addnode.hpp"
#include "opto/castnode.hpp"
#include "opto/connode.hpp"
#include "opto/castnode.hpp"
#include "opto/divnode.hpp"
@ -997,6 +998,9 @@ static bool merge_point_safe(Node* region) {
#ifdef _LP64
if (m->Opcode() == Op_ConvI2L)
return false;
if (m->is_CastII() && m->isa_CastII()->has_range_check()) {
return false;
}
#endif
}
}

View File

@ -25,6 +25,7 @@
#include "precompiled.hpp"
#include "libadt/vectset.hpp"
#include "memory/allocation.inline.hpp"
#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/connode.hpp"
#include "opto/loopnode.hpp"
@ -516,6 +517,11 @@ Node *Node::clone() const {
C->add_macro_node(n);
if (is_expensive())
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
debug_only( n->verify_construction() );
@ -644,6 +650,11 @@ void Node::destruct() {
if (is_expensive()) {
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()) {
as_SafePoint()->delete_replaced_nodes();
}
@ -1379,6 +1390,10 @@ static void kill_dead_code( Node *dead, PhaseIterGVN *igvn ) {
if (dead->is_expensive()) {
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);
// Kill all inputs to the dead guy
for (uint i=0; i < dead->req(); i++) {

View File

@ -51,6 +51,7 @@ class CallLeafNode;
class CallNode;
class CallRuntimeNode;
class CallStaticJavaNode;
class CastIINode;
class CatchNode;
class CatchProjNode;
class CheckCastPPNode;
@ -653,6 +654,7 @@ public:
DEFINE_CLASS_ID(Type, Node, 2)
DEFINE_CLASS_ID(Phi, Type, 0)
DEFINE_CLASS_ID(ConstraintCast, Type, 1)
DEFINE_CLASS_ID(CastII, ConstraintCast, 0)
DEFINE_CLASS_ID(CheckCastPP, Type, 2)
DEFINE_CLASS_ID(CMove, Type, 3)
DEFINE_CLASS_ID(SafePointScalarObject, Type, 4)
@ -784,6 +786,7 @@ public:
DEFINE_CLASS_QUERY(Catch)
DEFINE_CLASS_QUERY(CatchProj)
DEFINE_CLASS_QUERY(CheckCastPP)
DEFINE_CLASS_QUERY(CastII)
DEFINE_CLASS_QUERY(ConstraintCast)
DEFINE_CLASS_QUERY(ClearArray)
DEFINE_CLASS_QUERY(CMove)

View File

@ -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
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;
@ -466,12 +468,14 @@ bool Parse::create_jump_tables(Node* key_val, SwitchRange* lo, SwitchRange* hi)
// of all possible ranges for a switch statement
// The key_val input must be converted to a pointer offset and scaled.
// Compare Parse::array_addressing above.
#ifdef _LP64
// Clean the 32-bit int into a real 64-bit offset.
// Otherwise, the jint value 0 might turn into an offset of 0x0800000000.
const TypeLong* lkeytype = TypeLong::make(CONST64(0), num_cases-1, Type::WidenMin);
key_val = _gvn.transform( new ConvI2LNode(key_val, lkeytype) );
#endif
const TypeInt* ikeytype = TypeInt::make(0, num_cases, Type::WidenMin);
// Make I2L conversion control dependent to prevent it from
// 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
// than a switch value
Node *shiftWord = _gvn.MakeConX(wordSize);

View File

@ -26,6 +26,7 @@
#include "memory/allocation.inline.hpp"
#include "opto/block.hpp"
#include "opto/callnode.hpp"
#include "opto/castnode.hpp"
#include "opto/cfgnode.hpp"
#include "opto/idealGraphPrinter.hpp"
#include "opto/loopnode.hpp"
@ -1412,6 +1413,10 @@ void PhaseIterGVN::remove_globally_dead_node( Node *dead ) {
if (dead->is_expensive()) {
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())
}

View File

@ -1466,7 +1466,7 @@ void PhaseStringOpts::copy_latin1_string(GraphKit& kit, IdealKit& ideal, Node* s
// Use fast intrinsic
Node* src = kit.array_element_address(src_array, kit.intcon(0), 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 {
// No intrinsic available, use slow method
kit.inflate_string_slow(src_array, dst_array, start, __ value(count));

View File

@ -3343,6 +3343,11 @@ bool SWPointer::scaled_iv(Node* n) {
return true;
}
} 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))) {
NOT_PRODUCT(_tracer.scaled_iv_7(n);)
return true;
@ -3437,6 +3442,12 @@ bool SWPointer::offset_plus_k(Node* n, bool negate) {
if (invariant(n)) {
if (opc == Op_ConvI2L) {
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()) {
_negate_invar = negate;

View File

@ -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];
}
}

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -30,7 +30,6 @@
* sun.hotspot.WhiteBox$WhiteBoxPermission
* java.lang.invoke.RedefineTest
* Agent
* jdk.test.lib.Asserts
* @run main Agent agent.jar java.lang.invoke.RedefineTest
* @run main/othervm -Xbootclasspath/a:. -javaagent:agent.jar
* -XX:+IgnoreUnrecognizedVMOptions

View 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;
}
}

View File

@ -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.
*
* This code is free software; you can redistribute it and/or modify it
@ -21,6 +21,7 @@
* questions.
*/
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Path;
@ -42,6 +43,9 @@ public class ClassFileInstaller {
// Convert dotted class name to a path to a class file
String pathName = arg.replace('.', '/').concat(".class");
InputStream is = cl.getResourceAsStream(pathName);
if (is == null) {
throw new FileNotFoundException(pathName);
}
// Create the class file's package directory
Path p = Paths.get(pathName);