From 5b3456ce70dc4fb0bc28ff8bacb00a0ec504400b Mon Sep 17 00:00:00 2001 From: Roland Westrelin Date: Fri, 17 Jul 2026 15:02:34 +0000 Subject: [PATCH] 8388406: [BACKOUT] C2: crash in compiled code due to zero division because of widened CastII Reviewed-by: thartmann, chagedorn --- src/hotspot/share/opto/c2_globals.hpp | 4 +- src/hotspot/share/opto/castnode.cpp | 3 + src/hotspot/share/opto/cfgnode.cpp | 14 +- src/hotspot/share/opto/classes.hpp | 1 - src/hotspot/share/opto/compile.cpp | 25 +- src/hotspot/share/opto/compile.hpp | 9 +- src/hotspot/share/opto/convertnode.cpp | 14 + src/hotspot/share/opto/divnode.cpp | 14 - src/hotspot/share/opto/divnode.hpp | 13 +- src/hotspot/share/opto/loopopts.cpp | 2 +- src/hotspot/share/opto/movenode.cpp | 5 + src/hotspot/share/opto/node.cpp | 37 +- src/hotspot/share/opto/node.hpp | 13 +- src/hotspot/share/opto/parse2.cpp | 2 +- src/hotspot/share/opto/phaseX.cpp | 123 +- src/hotspot/share/opto/phaseX.hpp | 6 +- src/hotspot/share/opto/rootnode.cpp | 45 - src/hotspot/share/opto/rootnode.hpp | 31 - src/hotspot/share/opto/vectornode.cpp | 2 +- .../c2/TestDeadPathManyDeadDataNodes.java | 1301 ----------------- .../TestDivByZeroInLiveCFGPath.java | 64 - .../TestZeroDivModWidenedCastII.java | 1122 -------------- ...yAccessAboveRCAfterRCCastIIEliminated.java | 24 +- 23 files changed, 81 insertions(+), 2793 deletions(-) delete mode 100644 test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java delete mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java delete mode 100644 test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java diff --git a/src/hotspot/share/opto/c2_globals.hpp b/src/hotspot/share/opto/c2_globals.hpp index dd9288f7617..9ff88e8c310 100644 --- a/src/hotspot/share/opto/c2_globals.hpp +++ b/src/hotspot/share/opto/c2_globals.hpp @@ -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.") \ diff --git a/src/hotspot/share/opto/castnode.cpp b/src/hotspot/share/opto/castnode.cpp index ef7b4d5aef3..befa208a5e2 100644 --- a/src/hotspot/share/opto/castnode.cpp +++ b/src/hotspot/share/opto/castnode.cpp @@ -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; } diff --git a/src/hotspot/share/opto/cfgnode.cpp b/src/hotspot/share/opto/cfgnode.cpp index ed5da046608..828e5bf299f 100644 --- a/src/hotspot/share/opto/cfgnode.cpp +++ b/src/hotspot/share/opto/cfgnode.cpp @@ -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"); diff --git a/src/hotspot/share/opto/classes.hpp b/src/hotspot/share/opto/classes.hpp index c296237de37..53a72f979db 100644 --- a/src/hotspot/share/opto/classes.hpp +++ b/src/hotspot/share/opto/classes.hpp @@ -121,7 +121,6 @@ macro(CompareAndExchangeI) macro(CompareAndExchangeL) macro(CompareAndExchangeP) macro(CompareAndExchangeN) -macro(DeadPath) macro(GetAndAddB) macro(GetAndAddS) macro(GetAndAddI) diff --git a/src/hotspot/share/opto/compile.cpp b/src/hotspot/share/opto/compile.cpp index db43c6fb1c4..93d8e4c425d 100644 --- a/src/hotspot/share/opto/compile.cpp +++ b/src/hotspot/share/opto/compile.cpp @@ -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(), ""); diff --git a/src/hotspot/share/opto/compile.hpp b/src/hotspot/share/opto/compile.hpp index 73e136787f8..ab36f59a28f 100644 --- a/src/hotspot/share/opto/compile.hpp +++ b/src/hotspot/share/opto/compile.hpp @@ -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(); diff --git a/src/hotspot/share/opto/convertnode.cpp b/src/hotspot/share/opto/convertnode.cpp index d706a13feb3..a495814da61 100644 --- a/src/hotspot/share/opto/convertnode.cpp +++ b/src/hotspot/share/opto/convertnode.cpp @@ -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 ) { diff --git a/src/hotspot/share/opto/divnode.cpp b/src/hotspot/share/opto/divnode.cpp index 3b51491294e..1687ff2cade 100644 --- a/src/hotspot/share/opto/divnode.cpp +++ b/src/hotspot/share/opto/divnode.cpp @@ -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(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(phase, this); } diff --git a/src/hotspot/share/opto/divnode.hpp b/src/hotspot/share/opto/divnode.hpp index de89dcaad06..366e3fb882d 100644 --- a/src/hotspot/share/opto/divnode.hpp +++ b/src/hotspot/share/opto/divnode.hpp @@ -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--------------------------------------- diff --git a/src/hotspot/share/opto/loopopts.cpp b/src/hotspot/share/opto/loopopts.cpp index d525c274ef6..ccd53129a87 100644 --- a/src/hotspot/share/opto/loopopts.cpp +++ b/src/hotspot/share/opto/loopopts.cpp @@ -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); diff --git a/src/hotspot/share/opto/movenode.cpp b/src/hotspot/share/opto/movenode.cpp index 7d38238da2f..6b6becb434f 100644 --- a/src/hotspot/share/opto/movenode.cpp +++ b/src/hotspot/share/opto/movenode.cpp @@ -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); diff --git a/src/hotspot/share/opto/node.cpp b/src/hotspot/share/opto/node.cpp index 264216ddc6d..726a3ea1b55 100644 --- a/src/hotspot/share/opto/node.cpp +++ b/src/hotspot/share/opto/node.cpp @@ -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); +} diff --git a/src/hotspot/share/opto/node.hpp b/src/hotspot/share/opto/node.hpp index e593822c313..b3de7498e50 100644 --- a/src/hotspot/share/opto/node.hpp +++ b/src/hotspot/share/opto/node.hpp @@ -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" diff --git a/src/hotspot/share/opto/parse2.cpp b/src/hotspot/share/opto/parse2.cpp index 6e58fae51e1..9cb20cfcd00 100644 --- a/src/hotspot/share/opto/parse2.cpp +++ b/src/hotspot/share/opto/parse2.cpp @@ -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 diff --git a/src/hotspot/share/opto/phaseX.cpp b/src/hotspot/share/opto/phaseX.cpp index c124f940a27..a4d6a6c33d0 100644 --- a/src/hotspot/share/opto/phaseX.cpp +++ b/src/hotspot/share/opto/phaseX.cpp @@ -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. diff --git a/src/hotspot/share/opto/phaseX.hpp b/src/hotspot/share/opto/phaseX.hpp index 7ea7aa99142..014d16f92f6 100644 --- a/src/hotspot/share/opto/phaseX.hpp +++ b/src/hotspot/share/opto/phaseX.hpp @@ -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); diff --git a/src/hotspot/share/opto/rootnode.cpp b/src/hotspot/share/opto/rootnode.cpp index 1e5ef29e79c..60167c5436a 100644 --- a/src/hotspot/share/opto/rootnode.cpp +++ b/src/hotspot/share/opto/rootnode.cpp @@ -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); -} diff --git a/src/hotspot/share/opto/rootnode.hpp b/src/hotspot/share/opto/rootnode.hpp index 61ad317d455..76f0ec440a9 100644 --- a/src/hotspot/share/opto/rootnode.hpp +++ b/src/hotspot/share/opto/rootnode.hpp @@ -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 diff --git a/src/hotspot/share/opto/vectornode.cpp b/src/hotspot/share/opto/vectornode.cpp index 60eda1204b7..20857eed35c 100644 --- a/src/hotspot/share/opto/vectornode.cpp +++ b/src/hotspot/share/opto/vectornode.cpp @@ -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) { diff --git a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java b/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java deleted file mode 100644 index e9c5a8f7529..00000000000 --- a/test/hotspot/jtreg/compiler/c2/TestDeadPathManyDeadDataNodes.java +++ /dev/null @@ -1,1301 +0,0 @@ -/* - * Copyright (c) 2026 IBM Corporation. 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 8380166 - * @summary C2: crash in compiled code due to zero division because of widened CastII - * @run main/othervm -XX:+IgnoreUnrecognizedVMOptions -XX:+UnlockDiagnosticVMOptions - * -Xcomp -XX:CompileOnly=TestDeadPathManyDeadDataNodes::test1 - * -XX:CompileCommand=quiet - * -XX:CompileCommand=inline,TestDeadPathManyDeadDataNodes::inlined1 - * -XX:MaxRecursiveInlineLevel=1000 -XX:MaxInlineLevel=1000 - * -XX:-TieredCompilation -XX:+AlwaysIncrementalInline - * -XX:+DelayAfterInliningCutoff -XX:+IncrementalInlineForceCleanup - * -XX:NodeCountInliningCutoff=100000 -XX:+StressIGVN - * ${test.main.class} - * @run main ${test.main.class} - */ - -package compiler.c2; - -public class TestDeadPathManyDeadDataNodes { - private static int field; - private static boolean boolField2; - private static int arrayLengthField; - - public static void main(String[] args) { - Object o = new Object(); - try { - test1(false, 0); - } catch (NegativeArraySizeException nase) { - } - } - - private static int test1(boolean boolParam, int intParam) { - int length; - int res = 0; - length = -1; - for (int i = 0; i < 2; i++) { - if (boolParam) { - field = 42; - } - int[] array = new int[length]; - arrayLengthField = array.length; - while(true) { - Object o = new Object(); - int arrayLength = arrayLengthField; - arrayLengthField = 0; - switch (intParam) { - case 0: - if (boolField2) { - break; - } - field = 42; - continue; - case 1: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 2: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 3: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 4: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 5: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 6: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 7: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 8: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 9: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 10: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 11: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 12: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 13: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 14: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 15: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 16: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 17: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 18: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 19: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 20: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 21: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 22: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 23: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 24: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 25: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 26: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 27: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 28: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 29: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 30: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 31: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 32: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 33: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 34: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 35: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 36: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 37: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 38: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 39: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 40: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 41: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 42: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 43: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 44: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 45: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 46: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 47: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 48: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 49: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 50: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 51: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 52: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 53: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 54: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 55: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 56: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 57: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 58: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 59: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 60: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 61: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 62: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 63: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 64: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 65: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 66: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 67: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 68: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 69: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 70: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 71: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 72: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 73: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 74: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 75: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 76: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 77: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 78: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 79: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 80: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 81: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 82: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 83: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 84: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 85: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 86: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 87: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 88: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 89: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 90: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 91: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 92: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 93: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 94: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 95: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 96: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 97: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - case 98: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - continue; - case 99: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - continue; - default: - res += inlined1(boolParam, intParam/100, arrayLength, 92); - continue; - } - field = 42; - break; - } - length = lastInlined(); - } - return res; - } - - static int lastInlined() { - return -1; - } - - static int inlined1(boolean boolParam, int intParam, int arrayLength, int count) { - int res = 0; - switch (intParam) { - case 0: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 1: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 2: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 3: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 4: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 5: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 6: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 7: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 8: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 9: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 10: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 11: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 12: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 13: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 14: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 15: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 16: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 17: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 18: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 19: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 20: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 21: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 22: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 23: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 24: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 25: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 26: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 27: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 28: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 29: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 30: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 31: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 32: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 33: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 34: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 35: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 36: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 37: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 38: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 39: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 40: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 41: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 42: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 43: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 44: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 45: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 46: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 47: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 48: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 49: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 50: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 51: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 52: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 53: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 54: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 55: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 56: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 57: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 58: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 59: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 60: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 61: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 62: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 63: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 64: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 65: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 66: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 67: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 68: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 69: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 70: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 71: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 72: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 73: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 74: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 75: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 76: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 77: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 78: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 79: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 80: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 81: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 82: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 83: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 84: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 85: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 86: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 87: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 88: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 89: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 90: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 91: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 92: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 93: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 94: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 95: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 96: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 97: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - case 98: - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - case 99: - if (boolParam) { - res += arrayLength * 2; - } - field = 42; - return res; - default: - if (count == 0) { - if (boolParam) { - res += arrayLength * 1; - } - field = 42; - return res; - } else { - return inlined1(boolParam, intParam / 100, arrayLength, count-1); - } - } - } -} diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java deleted file mode 100644 index 6eaf3d86e71..00000000000 --- a/test/hotspot/jtreg/compiler/integerArithmetic/TestDivByZeroInLiveCFGPath.java +++ /dev/null @@ -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; - } - } -} - diff --git a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java b/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java deleted file mode 100644 index a5bc8fc9287..00000000000 --- a/test/hotspot/jtreg/compiler/integerArithmetic/TestZeroDivModWidenedCastII.java +++ /dev/null @@ -1,1122 +0,0 @@ -/* - * Copyright (c) 2026 IBM Corporation. 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 8380166 - * @summary C2: crash in compiled code due to zero division because of widened CastII - * - * @run main/othervm -XX:-TieredCompilation -XX:-UseOnStackReplacement -XX:-BackgroundCompilation - * ${test.main.class} - * @run main ${test.main.class} - * - */ - -package compiler.integerArithmetic; - -public class TestZeroDivModWidenedCastII { - private static int intField; - private static long longField; - private static volatile int volatileField; - - public static void main(String[] args) { - for (int i = 0; i < 20_000; i++) { - test1(0, 9, 1, true, false); - test1(0, 9, 1, false, false); - inlined1_2(9, 1, 1, true, 0); - inlined1_3(0, 0); - test2(0, 9, 1, true, false); - test2(0, 9, 1, false, false); - inlined2_2(9, 1, 1, true, 0); - inlined2_3(0, 0); - test3(0, 9, 1, true, false); - test3(0, 9, 1, false, false); - inlined3_2(9, 1, 1, true, 0); - inlined3_3(0, 0); - test4(0, 9, 1, true, false); - test4(0, 9, 1, false, false); - inlined4_2(9, 1, 1, true, 0); - inlined4_3(0, 0); - test5(0, 9, 1, true, false); - test5(0, 9, 1, false, false); - inlined5_2(9, 1, 1, true, 0); - inlined5_3(0, 0); - test6(0, 9, 1, true, false); - test6(0, 9, 1, false, false); - inlined6_2(9, 1, 1, true, 0); - inlined6_3(0, 0); - test7(0, 9, 1, true, false); - test7(0, 9, 1, false, false); - inlined7_2(9, 1, 1, true, 0); - inlined7_3(0, 0); - test8(0, 9, 1, true, false); - test8(0, 9, 1, false, false); - inlined8_2(9, 1, 1, true, 0); - inlined8_3(0, 0); - test9(0, 9, 1, true, false); - test9(0, 9, 1, false, false); - inlined9_2(9, 1, 1, true, 0); - inlined9_3(0, 0); - test10(0, 9, 1, false); - inlined10_2(9, 1, 1, true, 0); - inlined10_3(0, 0); - test11(0, 9, 1, false); - inlined11_2(9, 1, 1, true, 0); - inlined11_3(0, 0); - test12(0, 9, 1, false); - inlined12_2(9, 1, 1, true, 0); - inlined12_3(0, 0); - test13(0, 9, 1, false); - inlined13_2(9, 1, 1, true, 0); - inlined13_3(0, 0); - test14(0, 9, 1, false); - inlined14_2(9, 1, 1, true, 0); - inlined14_3(0, 0); - test15(0, 9, 1, false); - inlined15_2(9, 1, 1, true, 0); - inlined15_3(0, 0); - test16(0, 9, 1, false); - inlined16_2(9, 1, 1, true, 0); - inlined16_3(0, 0); - test17(0, 9, 1, false); - inlined17_2(9, 1, 1, true, 0); - inlined17_3(0, 0); - } - } - - private static void test1(int k, int j, int flag, boolean flag2, boolean flag3) { - int l = 0; - for (; l < 10; l++); - int m = inlined1_3(j, l); - - int i = inlined1(k, flag2); - j = Integer.min(j, 9); - int[] array = new int[10]; - if (flag == 0) { - throw new RuntimeException("never taken"); - } - if (flag2) { - inlined1_2(j, flag, i, flag3, m); - } else { - inlined1_2(j, flag, i, flag3, m); - } - } - - private static int inlined1_3(int j, int l) { - if (l == 10) { - j = 1; - } - return j; - } - - private static void inlined1_2(int j, int flag, int i, boolean flag3, int m) { - if (flag3) { - float[] newArray = new float[j + 1]; // j + 1 in [0..10] - // RC i