mirror of
https://github.com/openjdk/jdk.git
synced 2026-07-22 00:49:00 +00:00
8388406: [BACKOUT] C2: crash in compiled code due to zero division because of widened CastII
Reviewed-by: thartmann, chagedorn
This commit is contained in:
parent
9e9fae6584
commit
5b3456ce70
@ -922,8 +922,8 @@
|
||||
"Use StoreStore barrier instead of Release barrier at the end " \
|
||||
"of constructors") \
|
||||
\
|
||||
develop(bool, KillPathsReachableByDeadDataNode, true, \
|
||||
"When a data node becomes top, make paths where the node is " \
|
||||
develop(bool, KillPathsReachableByDeadTypeNode, true, \
|
||||
"When a Type node becomes top, make paths where the node is " \
|
||||
"used dead by replacing them with a Halt node. Turning this off " \
|
||||
"could corrupt the graph in rare cases and should be used with " \
|
||||
"care.") \
|
||||
|
||||
@ -111,6 +111,9 @@ Node* ConstraintCastNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (in(0) != nullptr && remove_dead_region(phase, can_reshape)) {
|
||||
return this;
|
||||
}
|
||||
if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) {
|
||||
return TypeNode::Ideal(phase, can_reshape);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
@ -693,13 +693,14 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
if (add_to_worklist) {
|
||||
igvn->add_users_to_worklist(this); // Check for further allowed opts
|
||||
}
|
||||
uint edges_removed;
|
||||
for (DUIterator_Last imin, i = last_outs(imin); i >= imin; i -= edges_removed) {
|
||||
edges_removed = 1;
|
||||
for (DUIterator_Last imin, i = last_outs(imin); i >= imin; --i) {
|
||||
Node* n = last_out(i);
|
||||
igvn->hash_delete(n); // Remove from worklist before modifying edges
|
||||
if (n->outcnt() == 0) {
|
||||
edges_removed = n->replace_edge(this, phase->C->top(), igvn);
|
||||
int uses_found = n->replace_edge(this, phase->C->top(), igvn);
|
||||
if (uses_found > 1) { // (--i) done at the end of the loop.
|
||||
i -= (uses_found - 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if( n->is_Phi() ) { // Collapse all Phis
|
||||
@ -718,7 +719,10 @@ Node *RegionNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
else if( n->is_Region() ) { // Update all incoming edges
|
||||
assert(n != this, "Must be removed from DefUse edges");
|
||||
edges_removed = n->replace_edge(this, parent_ctrl, igvn);
|
||||
int uses_found = n->replace_edge(this, parent_ctrl, igvn);
|
||||
if (uses_found > 1) { // (--i) done at the end of the loop.
|
||||
i -= (uses_found - 1);
|
||||
}
|
||||
}
|
||||
else {
|
||||
assert(n->in(0) == this, "Expect RegionNode to be control parent");
|
||||
|
||||
@ -121,7 +121,6 @@ macro(CompareAndExchangeI)
|
||||
macro(CompareAndExchangeL)
|
||||
macro(CompareAndExchangeP)
|
||||
macro(CompareAndExchangeN)
|
||||
macro(DeadPath)
|
||||
macro(GetAndAddB)
|
||||
macro(GetAndAddS)
|
||||
macro(GetAndAddI)
|
||||
|
||||
@ -312,8 +312,6 @@ void Compile::identify_useful_nodes(Unique_Node_List &useful) {
|
||||
// If 'top' is cached, declare it useful to preserve cached node
|
||||
if (cached_top_node()) { useful.push(cached_top_node()); }
|
||||
|
||||
if (dead_path()) { useful.push(dead_path()); }
|
||||
|
||||
// Push all useful nodes onto the list, breadthfirst
|
||||
for( uint next = 0; next < useful.size(); ++next ) {
|
||||
assert( next < unique(), "Unique useful nodes < total nodes");
|
||||
@ -390,7 +388,7 @@ void Compile::remove_useless_node(Node* dead) {
|
||||
// it reachable by adding use edges. So, we will NOT count Con nodes
|
||||
// as dead to be conservative about the dead node count at any
|
||||
// given time.
|
||||
if (!dead->is_Con() && dead != dead_path()) {
|
||||
if (!dead->is_Con()) {
|
||||
record_dead_node(dead->_idx);
|
||||
}
|
||||
if (dead->is_macro()) {
|
||||
@ -686,7 +684,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
_node_arena_one(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena_two(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena(&_node_arena_one),
|
||||
_dead_path(nullptr),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler, Arena::Tag::tag_type),
|
||||
_initial_gvn(nullptr),
|
||||
@ -757,7 +754,6 @@ Compile::Compile(ciEnv* ci_env, ciMethod* target, int osr_bci,
|
||||
}
|
||||
|
||||
Init(/*do_aliasing=*/ true);
|
||||
set_dead_path(new DeadPathNode());
|
||||
|
||||
print_compile_messages();
|
||||
|
||||
@ -967,7 +963,6 @@ Compile::Compile(ciEnv* ci_env,
|
||||
_node_arena_one(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena_two(mtCompiler, Arena::Tag::tag_node),
|
||||
_node_arena(&_node_arena_one),
|
||||
_dead_path(nullptr),
|
||||
_mach_constant_base_node(nullptr),
|
||||
_Compile_types(mtCompiler, Arena::Tag::tag_type),
|
||||
_initial_gvn(nullptr),
|
||||
@ -2635,9 +2630,6 @@ void Compile::Optimize() {
|
||||
}
|
||||
}
|
||||
|
||||
// Unique DeadPath node should not be used anymore
|
||||
_dead_path = nullptr;
|
||||
|
||||
print_method(PHASE_OPTIMIZE_FINISHED, 2);
|
||||
DEBUG_ONLY(set_phase_optimize_finished();)
|
||||
}
|
||||
@ -3946,21 +3938,6 @@ void Compile::final_graph_reshaping_main_switch(Node* n, Final_Reshape_Counts& f
|
||||
break;
|
||||
}
|
||||
#endif
|
||||
case Op_DeadPath: {
|
||||
// The CFG inputs are dead paths. Replace the DeadPath with a Region and insert a Halt node.
|
||||
assert(n->req() > 1, "why not removed if no input other than itself?");
|
||||
RegionNode* r = new RegionNode(n->req());
|
||||
for (uint i = 1; i < n->req(); ++i) {
|
||||
r->set_req(i, n->in(i));
|
||||
}
|
||||
n->disconnect_inputs(this);
|
||||
Node* frame = start()->proj_out(TypeFunc::FramePtr);
|
||||
stringStream ss;
|
||||
ss.print("dead path discovered by data nodes during igvn");
|
||||
Node* halt = new HaltNode(r, frame, ss.as_string(comp_arena()));
|
||||
root()->set_req(root()->find_edge(n), halt);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
assert(!n->is_Call(), "");
|
||||
assert(!n->is_Mem(), "");
|
||||
|
||||
@ -57,7 +57,6 @@ class CallStaticJavaNode;
|
||||
class CloneMap;
|
||||
class CompilationFailureInfo;
|
||||
class ConnectionGraph;
|
||||
class DeadPathNode;
|
||||
class IdealGraphPrinter;
|
||||
class InlineTree;
|
||||
class Matcher;
|
||||
@ -428,7 +427,7 @@ public:
|
||||
private:
|
||||
RootNode* _root; // Unique root of compilation, or null after bail-out.
|
||||
Node* _top; // Unique top node. (Reset by various phases.)
|
||||
DeadPathNode* _dead_path; // Unique DeadPath node
|
||||
|
||||
Node* _immutable_memory; // Initial memory state
|
||||
|
||||
Node* _recent_alloc_obj;
|
||||
@ -898,12 +897,6 @@ public:
|
||||
Arena* old_arena() { return (&_node_arena_one == _node_arena) ? &_node_arena_two : &_node_arena_one; }
|
||||
RootNode* root() const { return _root; }
|
||||
void set_root(RootNode* r) { _root = r; }
|
||||
DeadPathNode* dead_path() const { return _dead_path; }
|
||||
|
||||
void set_dead_path(DeadPathNode* dead_path) {
|
||||
assert(_dead_path == nullptr, "can only set once");
|
||||
_dead_path = dead_path;
|
||||
}
|
||||
StartNode* start() const; // (Derived from root.)
|
||||
void verify_start(StartNode* s) const NOT_DEBUG_RETURN;
|
||||
Node* immutable_memory();
|
||||
|
||||
@ -755,6 +755,13 @@ bool Compile::push_thru_add(PhaseGVN* phase, Node* z, const TypeInteger* tz, con
|
||||
|
||||
//------------------------------Ideal------------------------------------------
|
||||
Node* ConvI2LNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) {
|
||||
Node* progress = TypeNode::Ideal(phase, can_reshape);
|
||||
if (progress != nullptr) {
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
const TypeLong* this_type = this->type()->is_long();
|
||||
if (can_reshape && !phase->C->post_loop_opts_phase()) {
|
||||
// makes sure we run ::Value to potentially remove type assertion after loop opts
|
||||
@ -857,6 +864,13 @@ const Type* ConvL2INode::Value(PhaseGVN* phase) const {
|
||||
// Return a node which is more "ideal" than the current node.
|
||||
// Blow off prior masking to int
|
||||
Node* ConvL2INode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (in(1) != nullptr && phase->type(in(1)) != Type::TOP) {
|
||||
Node* progress = TypeNode::Ideal(phase, can_reshape);
|
||||
if (progress != nullptr) {
|
||||
return progress;
|
||||
}
|
||||
}
|
||||
|
||||
Node *andl = in(1);
|
||||
uint andl_op = andl->Opcode();
|
||||
if( andl_op == Op_AndL ) {
|
||||
|
||||
@ -1031,10 +1031,6 @@ const Type* UDivINode::Value(PhaseGVN* phase) const {
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
|
||||
if (t2 == TypeInt::ZERO) {
|
||||
return Type::TOP;
|
||||
}
|
||||
|
||||
// x/x == 1 since we always generate the dynamic divisor check for 0.
|
||||
if (in(1) == in(2)) {
|
||||
return TypeInt::ONE;
|
||||
@ -1071,10 +1067,6 @@ const Type* UDivLNode::Value(PhaseGVN* phase) const {
|
||||
if( t1 == Type::TOP ) return Type::TOP;
|
||||
if( t2 == Type::TOP ) return Type::TOP;
|
||||
|
||||
if (t2 == TypeLong::ZERO) {
|
||||
return Type::TOP;
|
||||
}
|
||||
|
||||
// x/x == 1 since we always generate the dynamic divisor check for 0.
|
||||
if (in(1) == in(2)) {
|
||||
return TypeLong::ONE;
|
||||
@ -1388,9 +1380,6 @@ Node* UModINode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
const Type* UModINode::Value(PhaseGVN* phase) const {
|
||||
if (phase->type(in(2)) == TypeInt::ZERO) {
|
||||
return Type::TOP;
|
||||
}
|
||||
return unsigned_mod_value<TypeInt, juint, jint>(phase, this);
|
||||
}
|
||||
|
||||
@ -1531,9 +1520,6 @@ Node *UModLNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
|
||||
const Type* UModLNode::Value(PhaseGVN* phase) const {
|
||||
if (phase->type(in(2)) == TypeLong::ZERO) {
|
||||
return Type::TOP;
|
||||
}
|
||||
return unsigned_mod_value<TypeLong, julong, jlong>(phase, this);
|
||||
}
|
||||
|
||||
|
||||
@ -40,9 +40,7 @@ private:
|
||||
bool _pinned;
|
||||
|
||||
protected:
|
||||
DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {
|
||||
init_class_id(Class_DivModInteger);
|
||||
}
|
||||
DivModIntegerNode(Node* c, Node* dividend, Node* divisor) : Node(c, dividend, divisor), _pinned(false) {}
|
||||
|
||||
private:
|
||||
virtual uint size_of() const override { return sizeof(DivModIntegerNode); }
|
||||
@ -54,15 +52,6 @@ private:
|
||||
res->_pinned = true;
|
||||
return res;
|
||||
}
|
||||
|
||||
public:
|
||||
const TypeInteger* zero() const {
|
||||
if (bottom_type() == TypeInt::INT) {
|
||||
return TypeInt::ZERO;
|
||||
}
|
||||
assert(bottom_type() == TypeLong::LONG, "should be int or long");
|
||||
return TypeLong::ZERO;
|
||||
}
|
||||
};
|
||||
|
||||
//------------------------------DivINode---------------------------------------
|
||||
|
||||
@ -1725,7 +1725,7 @@ void PhaseIdealLoop::try_sink_out_of_loop(Node* n) {
|
||||
!n->is_OpaqueTemplateAssertionPredicate() &&
|
||||
!is_raw_to_oop_cast && // don't extend live ranges of raw oops
|
||||
n->Opcode() != Op_CreateEx &&
|
||||
(KillPathsReachableByDeadDataNode || !n->is_Type())
|
||||
(KillPathsReachableByDeadTypeNode || !n->is_Type())
|
||||
) {
|
||||
Node *n_ctrl = get_ctrl(n);
|
||||
IdealLoopTree *n_loop = get_loop(n_ctrl);
|
||||
|
||||
@ -90,6 +90,11 @@ Node *CMoveNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
phase->type(in(IfTrue)) == Type::TOP) {
|
||||
return nullptr;
|
||||
}
|
||||
Node* progress = TypeNode::Ideal(phase, can_reshape);
|
||||
if (progress != nullptr) {
|
||||
return progress;
|
||||
}
|
||||
|
||||
// Check for Min/Max patterns. This is called before constants are pushed to the right input, as that transform can
|
||||
// make BoolTests non-canonical.
|
||||
Node* minmax = Ideal_minmax(phase, this);
|
||||
|
||||
@ -597,7 +597,6 @@ void Node::setup_is_top() {
|
||||
//------------------------------~Node------------------------------------------
|
||||
// Fancy destructor; eagerly attempt to reclaim Node numberings and storage
|
||||
void Node::destruct(PhaseValues* phase) {
|
||||
assert(this != Compile::current()->dead_path(), "we want to keep the unique DeadPath node around");
|
||||
Compile* compile = (phase != nullptr) ? phase->C : Compile::current();
|
||||
if (phase != nullptr && phase->is_IterGVN()) {
|
||||
phase->is_IterGVN()->_worklist.remove(this);
|
||||
@ -736,14 +735,11 @@ void Node::out_grow(uint len) {
|
||||
//------------------------------is_dead----------------------------------------
|
||||
bool Node::is_dead() const {
|
||||
// Mach and pinch point nodes may look like dead.
|
||||
if (is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) || this == Compile::current()->dead_path()) {
|
||||
if( is_top() || is_Mach() || (Opcode() == Op_Node && _outcnt > 0) )
|
||||
return false;
|
||||
}
|
||||
for (uint i = 0; i < _max; i++) {
|
||||
if (_in[i] != nullptr) {
|
||||
for( uint i = 0; i < _max; i++ )
|
||||
if( _in[i] != nullptr )
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -3182,11 +3178,10 @@ uint TypeNode::ideal_reg() const {
|
||||
return _type->ideal_reg();
|
||||
}
|
||||
|
||||
void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) {
|
||||
void TypeNode::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str) {
|
||||
Node* c = ctrl_use->in(j);
|
||||
Node* top = igvn->C->top();
|
||||
if (c != top) {
|
||||
igvn->replace_input_of(ctrl_use, j, top);
|
||||
if (igvn->type(c) != Type::TOP) {
|
||||
igvn->replace_input_of(ctrl_use, j, igvn->C->top());
|
||||
create_halt_path(igvn, c, loop, phase_str);
|
||||
}
|
||||
}
|
||||
@ -3198,18 +3193,14 @@ void Node::make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_u
|
||||
// constant folds and the control flow that leads to the Type node becomes unreachable. There are cases where that
|
||||
// doesn't happen, however. They are handled here by following uses of the Type node until a CFG or a Phi to find dead
|
||||
// paths. The dead paths are then replaced by a Halt node.
|
||||
void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) {
|
||||
void TypeNode::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str) {
|
||||
Unique_Node_List wq;
|
||||
wq.push(this);
|
||||
for (uint i = 0; i < wq.size(); ++i) {
|
||||
Node* n = wq.at(i);
|
||||
if (n->is_CFG()) {
|
||||
n->remove_dead_region(igvn, true);
|
||||
}
|
||||
for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) {
|
||||
Node* u = n->fast_out(k);
|
||||
if (u->is_CFG()) {
|
||||
wq.push(u);
|
||||
assert(!u->is_Region(), "Can't reach a Region without going through a Phi");
|
||||
make_path_dead(igvn, loop, u, 0, phase_str);
|
||||
} else if (u->is_Phi()) {
|
||||
@ -3229,7 +3220,7 @@ void Node::make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, c
|
||||
}
|
||||
}
|
||||
|
||||
void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) {
|
||||
void TypeNode::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const {
|
||||
Node* frame = new ParmNode(igvn->C->start(), TypeFunc::FramePtr);
|
||||
if (loop == nullptr) {
|
||||
igvn->register_new_node_with_optimizer(frame);
|
||||
@ -3248,3 +3239,15 @@ void Node::create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, c
|
||||
}
|
||||
igvn->add_input_to(igvn->C->root(), halt);
|
||||
}
|
||||
|
||||
Node* TypeNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (KillPathsReachableByDeadTypeNode && can_reshape && Value(phase) == Type::TOP) {
|
||||
PhaseIterGVN* igvn = phase->is_IterGVN();
|
||||
Node* top = igvn->C->top();
|
||||
ResourceMark rm;
|
||||
make_paths_from_here_dead(igvn, nullptr, "igvn");
|
||||
return top;
|
||||
}
|
||||
|
||||
return Node::Ideal(phase, can_reshape);
|
||||
}
|
||||
|
||||
@ -82,7 +82,6 @@ class CountedLoopEndNode;
|
||||
class DecodeNarrowPtrNode;
|
||||
class DecodeNNode;
|
||||
class DecodeNKlassNode;
|
||||
class DivModIntegerNode;
|
||||
class EncodeNarrowPtrNode;
|
||||
class EncodePNode;
|
||||
class EncodePKlassNode;
|
||||
@ -830,9 +829,8 @@ public:
|
||||
DEFINE_CLASS_ID(LShift, Node, 21)
|
||||
DEFINE_CLASS_ID(Neg, Node, 22)
|
||||
DEFINE_CLASS_ID(ReachabilityFence, Node, 23)
|
||||
DEFINE_CLASS_ID(DivModInteger, Node, 24)
|
||||
|
||||
_max_classes = ClassMask_DivModInteger
|
||||
_max_classes = ClassMask_Neg
|
||||
};
|
||||
#undef DEFINE_CLASS_ID
|
||||
|
||||
@ -949,7 +947,6 @@ public:
|
||||
DEFINE_CLASS_QUERY(DecodeNarrowPtr)
|
||||
DEFINE_CLASS_QUERY(DecodeN)
|
||||
DEFINE_CLASS_QUERY(DecodeNKlass)
|
||||
DEFINE_CLASS_QUERY(DivModInteger)
|
||||
DEFINE_CLASS_QUERY(EncodeNarrowPtr)
|
||||
DEFINE_CLASS_QUERY(EncodeP)
|
||||
DEFINE_CLASS_QUERY(EncodePKlass)
|
||||
@ -1504,10 +1501,6 @@ public:
|
||||
uint _del_tick; // Bumped when a deletion happens..
|
||||
#endif
|
||||
#endif
|
||||
void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str);
|
||||
|
||||
static void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str);
|
||||
void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str);
|
||||
};
|
||||
|
||||
inline bool not_a_node(const Node* n) {
|
||||
@ -2205,13 +2198,17 @@ public:
|
||||
init_class_id(Class_Type);
|
||||
}
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
|
||||
virtual const Type *bottom_type() const;
|
||||
virtual uint ideal_reg() const;
|
||||
|
||||
void make_path_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, Node* ctrl_use, uint j, const char* phase_str);
|
||||
#ifndef PRODUCT
|
||||
virtual void dump_spec(outputStream *st) const;
|
||||
virtual void dump_compact_spec(outputStream *st) const;
|
||||
#endif
|
||||
void make_paths_from_here_dead(PhaseIterGVN* igvn, PhaseIdealLoop* loop, const char* phase_str);
|
||||
void create_halt_path(PhaseIterGVN* igvn, Node* c, PhaseIdealLoop* loop, const char* phase_str) const;
|
||||
};
|
||||
|
||||
#include "opto/opcodes.hpp"
|
||||
|
||||
@ -1843,7 +1843,7 @@ void Parse::sharpen_type_after_if(BoolTest::mask btest,
|
||||
const Type* obj_type = _gvn.type(obj);
|
||||
const Type* tboth = obj_type->filter_speculative(cast_type);
|
||||
assert(tboth->higher_equal(obj_type) && tboth->higher_equal(cast_type), "sanity");
|
||||
if (tboth == Type::TOP && KillPathsReachableByDeadDataNode) {
|
||||
if (tboth == Type::TOP && KillPathsReachableByDeadTypeNode) {
|
||||
// Let dead type node cleaning logic prune effectively dead path for us.
|
||||
// CheckCastPP::Value() == TOP and it will trigger the cleanup during GVN.
|
||||
// Don't materialize the cast when cleanup is disabled, because
|
||||
|
||||
@ -32,7 +32,6 @@
|
||||
#include "opto/castnode.hpp"
|
||||
#include "opto/cfgnode.hpp"
|
||||
#include "opto/convertnode.hpp"
|
||||
#include "opto/divnode.hpp"
|
||||
#include "opto/idealGraphPrinter.hpp"
|
||||
#include "opto/loopnode.hpp"
|
||||
#include "opto/machnode.hpp"
|
||||
@ -2198,107 +2197,6 @@ Node *PhaseIterGVN::transform( Node *n ) {
|
||||
return transform_old(n);
|
||||
}
|
||||
|
||||
DeadPathNode* PhaseIterGVN::dead_path() {
|
||||
DeadPathNode* dead_path_node = C->dead_path();
|
||||
if (!dead_path_node->is_active()) {
|
||||
dead_path_node->activate(this);
|
||||
}
|
||||
assert(C->root()->find_edge(dead_path_node) > 0, "should be reachable from root");
|
||||
return dead_path_node;
|
||||
}
|
||||
|
||||
|
||||
// If dead_node is a data node, all CFG nodes reachable from dead_node are dead cfg paths. This method follows uses from
|
||||
// dead_node until it encounters a cfg node or a phi and eagerly kills these dead cfg paths. This is needed because, in
|
||||
// some corner cases, a data node dies but some data paths that use it (and are unreachable at runtime) are not proven
|
||||
// dead by igvn, possibly leading to incorrect IR graphs.
|
||||
// Also see comment at DeadPathNode declaration.
|
||||
void PhaseIterGVN::make_dependent_paths_dead_if_top(Node* dead_node, const Type* t) {
|
||||
if (t != Type::TOP) {
|
||||
return;
|
||||
}
|
||||
if (!KillPathsReachableByDeadDataNode) {
|
||||
return;
|
||||
}
|
||||
// dead_node is going dead, follow uses
|
||||
ResourceMark rm;
|
||||
Unique_Node_List wq;
|
||||
wq.push(dead_node);
|
||||
for (uint i = 0; i < wq.size(); i++) {
|
||||
Node* n = wq.at(i);
|
||||
if (n != dead_node && (n->is_Phi() || n->is_CFG())) {
|
||||
continue;
|
||||
}
|
||||
for (DUIterator_Fast kmax, k = n->fast_outs(kmax); k < kmax; k++) {
|
||||
Node* u = n->fast_out(k);
|
||||
wq.push(u);
|
||||
}
|
||||
}
|
||||
for (uint i = 0; i < wq.size(); i++) {
|
||||
Node* n = wq.at(i);
|
||||
if (n->is_Phi()) {
|
||||
Node* region = n->in(0);
|
||||
// Find out through which of the Phi's input, we reached that Phi and mark the corresponding CFG path dead
|
||||
for (uint j = 1; j < n->req(); j++) {
|
||||
Node* in = n->in(j);
|
||||
// We don't follow uses beyond Phis so if 'in' is a Phi (unless it's dead_node), we couldn't reach this Phi through it
|
||||
if (in == dead_node || (in != nullptr && !in->is_Phi() && wq.member(in))) {
|
||||
if (!region->is_top() && region->in(j) != nullptr && !region->in(j)->is_top()) {
|
||||
// We reached this CFG path through data nodes, record it in dead path to later insert a Halt node, if it
|
||||
// doesn't die in the meantime
|
||||
dead_path()->add_req(region->in(j));
|
||||
_worklist.push(dead_path());
|
||||
replace_input_of(region, j, C->top());
|
||||
}
|
||||
replace_input_of(n, j, C->top());
|
||||
if (in->outcnt() == 0) {
|
||||
remove_dead_node(in, NodeOrigin::Graph);
|
||||
}
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (n == dead_node) {
|
||||
continue;
|
||||
}
|
||||
// We don't want to follow CFG nodes but is_CFG() can return false for a cfg projection if its input is top. So
|
||||
// there's no foolproof way of telling if dead_node is a cfg or not and as a consequence we can reach a Region.
|
||||
if (n->is_Region()) {
|
||||
// Find out through which of the Region's input, we reached that Region and mark it dead
|
||||
for (uint j = 1; j < n->req(); j++) {
|
||||
Node* in = n->in(j);
|
||||
// We don't follow uses beyond Regions so if 'in' is a Region, we couldn't reach this Region through it
|
||||
if (in != nullptr && !in->is_Region() && wq.member(in)) {
|
||||
replace_input_of(n, j, C->top());
|
||||
in->remove_dead_region(this, true);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// If we reached this CFG node through a data input...
|
||||
if (n->is_CFG()) {
|
||||
Node* control_input = n->in(0);
|
||||
if (control_input != nullptr && !control_input->is_top()) {
|
||||
// record it in dead path to later insert a Halt node, if it doesn't die in the meantime
|
||||
dead_path()->add_req(control_input);
|
||||
_worklist.push(dead_path());
|
||||
replace_input_of(n, 0, C->top());
|
||||
}
|
||||
n->remove_dead_region(this, true);
|
||||
continue;
|
||||
}
|
||||
if (n->outcnt() == 0) {
|
||||
remove_dead_node(n, NodeOrigin::Graph);
|
||||
}
|
||||
}
|
||||
#ifdef ASSERT
|
||||
for (uint i = 0; i < wq.size(); i++) {
|
||||
Node* n = wq.at(i);
|
||||
assert(n->is_Region() || n->is_Phi() || n->is_CFG() || n->outcnt() == 0, "node should be dead now");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
Node *PhaseIterGVN::transform_old(Node* n) {
|
||||
NOT_PRODUCT(set_transforms());
|
||||
// Remove 'n' from hash table in case it gets modified
|
||||
@ -2390,7 +2288,6 @@ Node *PhaseIterGVN::transform_old(Node* n) {
|
||||
}
|
||||
// If 'k' computes a constant, replace it with a constant
|
||||
if (t->singleton() && !k->is_Con()) {
|
||||
make_dependent_paths_dead_if_top(k, t);
|
||||
set_progress();
|
||||
Node* con = makecon(t); // Make a constant
|
||||
add_users_to_worklist(k);
|
||||
@ -3060,14 +2957,10 @@ void PhaseCCP::analyze_step(Unique_Node_List& worklist, Node* n) {
|
||||
set_type(n, new_type);
|
||||
push_child_nodes_to_worklist(worklist, n);
|
||||
}
|
||||
if (KillPathsReachableByDeadDataNode && n->is_Type() && new_type == Type::TOP) {
|
||||
if (KillPathsReachableByDeadTypeNode && n->is_Type() && new_type == Type::TOP) {
|
||||
// Keep track of Type nodes to kill CFG paths that use Type
|
||||
// nodes that become dead.
|
||||
_maybe_top_type_or_div_mod_nodes.push(n);
|
||||
}
|
||||
if (KillPathsReachableByDeadDataNode && new_type == Type::TOP && n->is_DivModInteger() &&
|
||||
type(n->in(2)) == n->as_DivModInteger()->zero()) {
|
||||
_maybe_top_type_or_div_mod_nodes.push(n);
|
||||
_maybe_top_type_nodes.push(n);
|
||||
}
|
||||
}
|
||||
|
||||
@ -3363,16 +3256,16 @@ Node *PhaseCCP::transform( Node *n ) {
|
||||
// track all visited nodes, so that we can remove the complement
|
||||
Unique_Node_List useful;
|
||||
|
||||
if (KillPathsReachableByDeadDataNode) {
|
||||
for (uint i = 0; i < _maybe_top_type_or_div_mod_nodes.size(); ++i) {
|
||||
Node* data_node = _maybe_top_type_or_div_mod_nodes.at(i);
|
||||
if (type(data_node) == Type::TOP) {
|
||||
if (KillPathsReachableByDeadTypeNode) {
|
||||
for (uint i = 0; i < _maybe_top_type_nodes.size(); ++i) {
|
||||
Node* type_node = _maybe_top_type_nodes.at(i);
|
||||
if (type(type_node) == Type::TOP) {
|
||||
ResourceMark rm;
|
||||
data_node->make_paths_from_here_dead(this, nullptr, "ccp");
|
||||
type_node->as_Type()->make_paths_from_here_dead(this, nullptr, "ccp");
|
||||
}
|
||||
}
|
||||
} else {
|
||||
assert(_maybe_top_type_or_div_mod_nodes.size() == 0, "we don't need type nodes");
|
||||
assert(_maybe_top_type_nodes.size() == 0, "we don't need type nodes");
|
||||
}
|
||||
|
||||
// Initialize the traversal.
|
||||
|
||||
@ -501,10 +501,6 @@ protected:
|
||||
// Usually returns new_type. Returns old_type if new_type is only a slight
|
||||
// improvement, such that it would take many (>>10) steps to reach 2**32.
|
||||
|
||||
DeadPathNode* dead_path();
|
||||
|
||||
void make_dependent_paths_dead_if_top(Node* dead_node, const Type* t);
|
||||
|
||||
public:
|
||||
|
||||
PhaseIterGVN(PhaseIterGVN* igvn); // Used by CCP constructor
|
||||
@ -699,7 +695,7 @@ protected:
|
||||
// Should be replaced with combined CCP & GVN someday.
|
||||
class PhaseCCP : public PhaseIterGVN {
|
||||
Unique_Node_List _root_and_safepoints;
|
||||
Unique_Node_List _maybe_top_type_or_div_mod_nodes;
|
||||
Unique_Node_List _maybe_top_type_nodes;
|
||||
// Non-recursive. Use analysis to transform single Node.
|
||||
virtual Node* transform_once(Node* n);
|
||||
|
||||
|
||||
@ -90,48 +90,3 @@ const Type* HaltNode::Value(PhaseGVN* phase) const {
|
||||
const RegMask &HaltNode::out_RegMask() const {
|
||||
return RegMask::EMPTY;
|
||||
}
|
||||
|
||||
Node* DeadPathNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
assert(unique_ctrl_out() == phase->C->root(), "only referenced from root");
|
||||
assert(can_reshape, "only used once igvn executes");
|
||||
bool modified = false;
|
||||
for (uint i = 1; i < req(); i++) { // For all inputs
|
||||
// Check for and remove dead inputs
|
||||
if (phase->type(in(i)) == Type::TOP) {
|
||||
del_req(i--); // Delete TOP inputs
|
||||
modified = true;
|
||||
}
|
||||
}
|
||||
if (req() == 1 && is_active()) {
|
||||
assert(modified, "only if some inputs were removed");
|
||||
deactivate();
|
||||
}
|
||||
return modified ? this : nullptr;
|
||||
}
|
||||
|
||||
const Type* DeadPathNode::Value(PhaseGVN* phase) const {
|
||||
if (req() == 1) {
|
||||
return Type::TOP;
|
||||
}
|
||||
return bottom_type();
|
||||
}
|
||||
|
||||
void DeadPathNode::activate(PhaseIterGVN* igvn) {
|
||||
assert(Compile::current()->root()->find_edge(this) < 0, "should be disconnected from root");
|
||||
set_req(0, this);
|
||||
// If an entire subgraph died such as with Node::remove_dead_region(), some dead inputs to the DeadPath node will have
|
||||
// been left behind
|
||||
while (req() > 1) {
|
||||
uint last = req() - 1;
|
||||
assert(in(last) == nullptr || in(last)->is_top(), "only dead inputs should remain");
|
||||
del_req(last);
|
||||
}
|
||||
Node* root_node = Compile::current()->root();
|
||||
root_node->add_req(this);
|
||||
igvn->_worklist.push(root_node);
|
||||
igvn->set_type(this, bottom_type());
|
||||
}
|
||||
|
||||
void DeadPathNode::deactivate() {
|
||||
set_req(0, nullptr);
|
||||
}
|
||||
|
||||
@ -69,35 +69,4 @@ public:
|
||||
virtual uint match_edge(uint idx) const { return 0; }
|
||||
};
|
||||
|
||||
|
||||
// This node collects paths that are found dead by PhaseIterGVN::make_dependent_paths_dead_if_top()
|
||||
|
||||
// There is a single DeadPath node for the lifetime of optimizations. It's initially not active (i.e. unreachable from
|
||||
// the IR graph). When a cfg path becomes dead it's added as an input to the unique DeadPath node. If after some
|
||||
// optimizations run, the DeadPath node gets disconnected, it's not destroyed. It becomes inactive and can possibly be
|
||||
// activated again on a subsequent igvn. When optimizations are over, the DeadPath node, if it is active, is expanded to
|
||||
// a Region and Halt node in Compile::final_graph_reshaping().
|
||||
|
||||
// Rather than having this dedicated node, igvn could add a Halt node everytime it finds a dead cfg path from a data
|
||||
// node. What's likely, however, is that as igvn progresses, that same cfg path is found dead by following cfg edges.
|
||||
// The Halt node then becomes dead. To avoid this unnecessary cycle of creation of a Halt node only to have it be found
|
||||
// dead shortly after, dead cfg paths are added to the unique DeadPath node.
|
||||
class DeadPathNode : public RegionNode {
|
||||
public:
|
||||
DeadPathNode() : RegionNode(1) {
|
||||
deactivate();
|
||||
assert(Compile::current()->dead_path() == nullptr, "only one");
|
||||
}
|
||||
virtual int Opcode() const;
|
||||
virtual const Type* bottom_type() const { return Type::BOTTOM; }
|
||||
virtual Node* Identity(PhaseGVN* phase) { return this; }
|
||||
virtual Node* Ideal(PhaseGVN* phase, bool can_reshape);
|
||||
virtual const Type* Value(PhaseGVN* phase) const;
|
||||
bool is_active() const {
|
||||
return in(0) == this;
|
||||
}
|
||||
void activate(PhaseIterGVN* igvn);
|
||||
void deactivate();
|
||||
};
|
||||
|
||||
#endif // SHARE_OPTO_ROOTNODE_HPP
|
||||
|
||||
@ -2391,7 +2391,7 @@ Node* VectorMaskOpNode::Ideal(PhaseGVN* phase, bool can_reshape) {
|
||||
if (n != nullptr) {
|
||||
return n;
|
||||
}
|
||||
return nullptr;
|
||||
return TypeNode::Ideal(phase, can_reshape);
|
||||
}
|
||||
|
||||
Node* VectorMaskCastNode::Identity(PhaseGVN* phase) {
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,64 +0,0 @@
|
||||
|
||||
/*
|
||||
* Copyright (c) 2026, 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 8383815
|
||||
* @summary C2: assert(false) failed: malformed IfNode with 1 outputs
|
||||
* @run main/othervm -XX:CompileCommand=compileonly,${test.main.class}*::* -XX:-TieredCompilation -Xbatch -XX:PerMethodTrapLimit=0 ${test.main.class}
|
||||
* @run main ${test.main.class}
|
||||
*/
|
||||
|
||||
package compiler.integerArithmetic;
|
||||
|
||||
public class TestDivByZeroInLiveCFGPath {
|
||||
static long lFld;
|
||||
static int iArr[] = new int[400];
|
||||
|
||||
public static void main(String[] strArr) {
|
||||
for (int i = 0; i < 10; i++) {
|
||||
test();
|
||||
}
|
||||
}
|
||||
|
||||
static void test() {
|
||||
int x;
|
||||
for (int i = 9; i < 100; ++i) {
|
||||
int j = 100;
|
||||
while (--j > 0) {
|
||||
iArr[1] = (int) lFld;
|
||||
}
|
||||
try {
|
||||
iArr[1] = (5 / j);
|
||||
x = (i / iArr[8]);
|
||||
} catch (ArithmeticException a_e) {
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 18; i < 50; i++) {
|
||||
iArr[2] += lFld;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@ -27,19 +27,17 @@
|
||||
* @summary [REDO] C2: crash in compiled code because of dependency on removed range check CastIIs
|
||||
*
|
||||
* @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation
|
||||
* -XX:CompileCommand=dontinline,${test.main.class}::notInlined
|
||||
* ${test.main.class}
|
||||
* -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined
|
||||
* TestArrayAccessAboveRCAfterRCCastIIEliminated
|
||||
* @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation
|
||||
* -XX:CompileCommand=dontinline,${test.main.class}::notInlined
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM ${test.main.class}
|
||||
* @run main ${test.main.class}
|
||||
* @run main/othervm -XX:CompileCommand=dontinline,${test.main.class}::notInlined
|
||||
* ${test.main.class}
|
||||
* -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined
|
||||
* -XX:+UnlockDiagnosticVMOptions -XX:+StressGCM TestArrayAccessAboveRCAfterRCCastIIEliminated
|
||||
* @run main TestArrayAccessAboveRCAfterRCCastIIEliminated
|
||||
* @run main/othervm -XX:CompileCommand=dontinline,TestArrayAccessAboveRCAfterRCCastIIEliminated::notInlined
|
||||
* TestArrayAccessAboveRCAfterRCCastIIEliminated
|
||||
*
|
||||
*/
|
||||
|
||||
package compiler.rangechecks;
|
||||
|
||||
public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
private static int intField;
|
||||
private static long longField;
|
||||
@ -484,7 +482,7 @@ public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
}
|
||||
}
|
||||
|
||||
// Widened range check cast type after loop opts causes control dependency to be lost
|
||||
// Range check cast type widen after loop opts causes control dependency to be lost
|
||||
private static void test14(int i, int j, int flag, boolean flag2) {
|
||||
int l = 0;
|
||||
for (; l < 10; l++);
|
||||
@ -492,14 +490,12 @@ public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
int[] array = new int[10];
|
||||
notInlined(array);
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
if (flag2) {
|
||||
float[] newArray = new float[10];
|
||||
newArray[i+j] = 42; // i+j in [0, 9]
|
||||
float[] otherArray = new float[i+j]; // i+j in [0, max]
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
intField = array[otherArray.length];
|
||||
} else {
|
||||
@ -507,7 +503,6 @@ public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
newArray[i+j] = 42; // i+j in [0, 9]
|
||||
float[] otherArray = new float[i+j]; // i+j in [0, max]
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
intField = array[otherArray.length];
|
||||
}
|
||||
@ -528,14 +523,12 @@ public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
int[] array = new int[10];
|
||||
notInlined(array);
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
if (flag2) {
|
||||
float[] newArray = new float[10];
|
||||
newArray[i+j] = 42; // i+j in [0, 9]
|
||||
float[] otherArray = new float[i+j]; // i+j in [0, max]
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
intField = array[otherArray.length];
|
||||
} else {
|
||||
@ -543,7 +536,6 @@ public class TestArrayAccessAboveRCAfterRCCastIIEliminated {
|
||||
newArray[i+j] = 42; // i+j in [0, 9]
|
||||
float[] otherArray = new float[i+j]; // i+j in [0, max]
|
||||
if (flag == 0) {
|
||||
throw new RuntimeException("never taken");
|
||||
}
|
||||
intField = array[otherArray.length];
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user