diff --git a/src/hotspot/share/adlc/formssel.cpp b/src/hotspot/share/adlc/formssel.cpp index 5802217c1c1c8..79779782c2a3b 100644 --- a/src/hotspot/share/adlc/formssel.cpp +++ b/src/hotspot/share/adlc/formssel.cpp @@ -453,14 +453,6 @@ Form::DataType InstructForm::is_ideal_store() const { return _matrule->is_ideal_store(); } -// Return 'true' if this instruction matches an ideal vector node -bool InstructForm::is_vector() const { - if( _matrule == nullptr ) return false; - - return _matrule->is_vector(); -} - - // Return the input register that must match the output register // If this is not required, return 0 uint InstructForm::two_address(FormDict &globals) { @@ -767,51 +759,6 @@ int InstructForm::memory_operand(FormDict &globals) const { return NO_MEMORY_OPERAND; } -// This instruction captures the machine-independent bottom_type -// Expected use is for pointer vs oop determination for LoadP -bool InstructForm::captures_bottom_type(FormDict &globals) const { - if (_matrule && _matrule->_rChild && - (!strcmp(_matrule->_rChild->_opType,"CastPP") || // new result type - !strcmp(_matrule->_rChild->_opType,"CastDD") || - !strcmp(_matrule->_rChild->_opType,"CastFF") || - !strcmp(_matrule->_rChild->_opType,"CastII") || - !strcmp(_matrule->_rChild->_opType,"CastLL") || - !strcmp(_matrule->_rChild->_opType,"CastVV") || - !strcmp(_matrule->_rChild->_opType,"CastX2P") || // new result type - !strcmp(_matrule->_rChild->_opType,"DecodeN") || - !strcmp(_matrule->_rChild->_opType,"EncodeP") || - !strcmp(_matrule->_rChild->_opType,"DecodeNKlass") || - !strcmp(_matrule->_rChild->_opType,"EncodePKlass") || - !strcmp(_matrule->_rChild->_opType,"LoadN") || - !strcmp(_matrule->_rChild->_opType,"LoadNKlass") || - !strcmp(_matrule->_rChild->_opType,"CreateEx") || // type of exception - !strcmp(_matrule->_rChild->_opType,"CheckCastPP") || - !strcmp(_matrule->_rChild->_opType,"GetAndSetP") || - !strcmp(_matrule->_rChild->_opType,"GetAndSetN") || - !strcmp(_matrule->_rChild->_opType,"RotateLeft") || - !strcmp(_matrule->_rChild->_opType,"RotateRight") || -#if INCLUDE_SHENANDOAHGC - !strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeP") || - !strcmp(_matrule->_rChild->_opType,"ShenandoahCompareAndExchangeN") || -#endif - !strcmp(_matrule->_rChild->_opType,"StrInflatedCopy") || - !strcmp(_matrule->_rChild->_opType,"VectorCmpMasked")|| - !strcmp(_matrule->_rChild->_opType,"VectorMaskGen")|| - !strcmp(_matrule->_rChild->_opType,"VerifyVectorAlignment")|| - !strcmp(_matrule->_rChild->_opType,"CompareAndExchangeP") || - !strcmp(_matrule->_rChild->_opType,"CompareAndExchangeN"))) return true; - else if ( is_ideal_load() == Form::idealP ) return true; - else if ( is_ideal_store() != Form::none ) return true; - - if (needs_base_oop_edge(globals)) return true; - - if (is_vector()) return true; - if (is_mach_constant()) return true; - - return false; -} - - // Access instr_cost attribute or return null. const char* InstructForm::cost() { for (Attribute* cur = _attribs; cur != nullptr; cur = (Attribute*)cur->_next) { @@ -1181,9 +1128,6 @@ const char *InstructForm::mach_base_class(FormDict &globals) const { } else if (is_mach_constant()) { return "MachConstantNode"; - } - else if (captures_bottom_type(globals)) { - return "MachTypeNode"; } else { return "MachNode"; } @@ -4337,58 +4281,6 @@ Form::DataType MatchRule::is_ideal_load() const { return ideal_load; } -bool MatchRule::is_vector() const { - static const char *vector_list[] = { - "AddVB","AddVS","AddVI","AddVL","AddVHF","AddVF","AddVD", - "SubVB","SubVS","SubVI","SubVL","SubVHF","SubVF","SubVD", - "MulVB","MulVS","MulVI","MulVL","MulVHF","MulVF","MulVD", - "DivVHF","DivVF","DivVD", - "AbsVB","AbsVS","AbsVI","AbsVL","AbsVF","AbsVD", - "NegVF","NegVD","NegVI","NegVL", - "SqrtVD","SqrtVF","SqrtVHF", - "AndV" ,"XorV" ,"OrV", - "MaxV", "MinV", "MinVHF", "MaxVHF", "UMinV", "UMaxV", - "CompressV", "ExpandV", "CompressM", "CompressBitsV", "ExpandBitsV", - "AddReductionVI", "AddReductionVL", - "AddReductionVHF", "AddReductionVF", "AddReductionVD", - "MulReductionVI", "MulReductionVL", - "MulReductionVHF", "MulReductionVF", "MulReductionVD", - "MaxReductionV", "MinReductionV", - "AndReductionV", "OrReductionV", "XorReductionV", - "MulAddVS2VI", "MacroLogicV", - "LShiftCntV","RShiftCntV", - "LShiftVB","LShiftVS","LShiftVI","LShiftVL", - "RShiftVB","RShiftVS","RShiftVI","RShiftVL", - "URShiftVB","URShiftVS","URShiftVI","URShiftVL", - "Replicate","ReverseV","ReverseBytesV", - "RoundDoubleModeV","RotateLeftV" , "RotateRightV", "LoadVector","StoreVector", - "LoadVectorGather", "StoreVectorScatter", "LoadVectorGatherMasked", "StoreVectorScatterMasked", - "SelectFromTwoVector", "VectorTest", "VectorLoadMask", "VectorStoreMask", "VectorBlend", "VectorInsert", - "VectorRearrange", "VectorLoadShuffle", "VectorLoadConst", - "VectorCastB2X", "VectorCastS2X", "VectorCastI2X", - "VectorCastL2X", "VectorCastF2X", "VectorCastD2X", "VectorCastF2HF", "VectorCastHF2F", - "VectorUCastB2X", "VectorUCastS2X", "VectorUCastI2X", - "VectorMaskWrapper","VectorMaskCmp","VectorReinterpret","LoadVectorMasked","StoreVectorMasked", - "FmaVD", "FmaVF", "FmaVHF", "PopCountVI", "PopCountVL", "PopulateIndex", "VectorLongToMask", - "CountLeadingZerosV", "CountTrailingZerosV", "SignumVF", "SignumVD", "SaturatingAddV", "SaturatingSubV", - // Next are vector mask ops. - "MaskAll", "AndVMask", "OrVMask", "XorVMask", "VectorMaskCast", - "RoundVF", "RoundVD", - // Next are not supported currently. - "PackB","PackS","PackI","PackL","PackF","PackD","Pack2L","Pack2D", - "ExtractB","ExtractUB","ExtractC","ExtractS","ExtractI","ExtractL","ExtractF","ExtractD" - }; - int cnt = sizeof(vector_list)/sizeof(char*); - if (_rChild) { - const char *opType = _rChild->_opType; - for (int i=0; iadd_req(inst%d->in(%d)); // unmatched ideal edge\n", inst_num, unmatched_edge); } - // If new instruction captures bottom type - if( root_form->captures_bottom_type(globals) ) { - // Get bottom type from instruction whose result we are replacing - fprintf(fp, " root->_bottom_type = inst%d->bottom_type();\n", inst_num); - } + // Get bottom type from instruction whose result we are replacing + fprintf(fp, " root->_bottom_type = inst%d->bottom_type();\n", inst_num); // Define result register and result operand fprintf(fp, " ra_->set_oop (root, ra_->is_oop(inst%d));\n", inst_num); fprintf(fp, " ra_->set_pair(root->_idx, ra_->get_reg_second(inst%d), ra_->get_reg_first(inst%d));\n", inst_num, inst_num); @@ -1587,11 +1584,8 @@ void ArchDesc::defineExpand(FILE *fp, InstructForm *node) { fprintf(fp, " ((MachIfNode*)n%d)->_fcnt = _fcnt;\n", cnt); } - // Fill in the bottom_type where requested - if (node->captures_bottom_type(_globalNames) && - new_inst->captures_bottom_type(_globalNames)) { - fprintf(fp, " ((MachTypeNode*)n%d)->_bottom_type = bottom_type();\n", cnt); - } + // Fill in the bottom_type + fprintf(fp, " n%d->_bottom_type = bottom_type();\n", cnt); const char *resultOper = new_inst->reduce_result(); fprintf(fp," n%d->set_opnd_array(0, state->MachOperGenerator(%s));\n", @@ -3965,13 +3959,15 @@ void ArchDesc::buildMachNode(FILE *fp_cpp, InstructForm *inst, const char *inden } } - // Fill in the bottom_type where requested - if (inst->captures_bottom_type(_globalNames)) { - if (strncmp("MachCall", inst->mach_base_class(_globalNames), strlen("MachCall")) != 0 - && strncmp("MachIf", inst->mach_base_class(_globalNames), strlen("MachIf")) != 0) { - fprintf(fp_cpp, "%s node->_bottom_type = _leaf->bottom_type();\n", indent); - } + // Fill in the bottom_type + if (inst->_matrule != nullptr && strcmp(inst->_matrule->_opType, "PrefetchAllocation") == 0) { + // Special case, with AllocatePrefetchStyle == 3, this should be Type::MEMORY, but the graph + // seems unsound, needs further investigation + fprintf(fp_cpp, "%s node->_bottom_type = Type::ABIO;\n", indent); + } else { + fprintf(fp_cpp, "%s node->_bottom_type = _leaf->bottom_type();\n", indent); } + if( inst->is_ideal_if() ) { fprintf(fp_cpp, "%s node->_prob = _leaf->as_If()->_prob;\n", indent); fprintf(fp_cpp, "%s node->_fcnt = _leaf->as_If()->_fcnt;\n", indent); @@ -4026,10 +4022,8 @@ void InstructForm::define_cisc_version(ArchDesc& AD, FILE* fp_cpp) { fprintf(fp_cpp, "MachNode *%sNode::cisc_version(int offset) {\n", this->_ident); // Create the MachNode object fprintf(fp_cpp, " %sNode *node = new %sNode();\n", name, name); - // Fill in the bottom_type where requested - if ( this->captures_bottom_type(AD.globalNames()) ) { - fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); - } + // Fill in the bottom_type + fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); uint cur_num_opnds = num_opnds(); if (cur_num_opnds > 1 && cur_num_opnds != num_unique_opnds()) { @@ -4075,10 +4069,9 @@ void InstructForm::define_short_branch_methods(ArchDesc& AD, FILE* fp_cpp) { fprintf(fp_cpp, " node->_prob = _prob;\n"); fprintf(fp_cpp, " node->_fcnt = _fcnt;\n"); } - // Fill in the bottom_type where requested - if ( this->captures_bottom_type(AD.globalNames()) ) { - fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); - } + + // Fill in the bottom_type + fprintf(fp_cpp, " node->_bottom_type = bottom_type();\n"); fprintf(fp_cpp, "\n"); // Short branch version must use same node index for access diff --git a/src/hotspot/share/adlc/output_h.cpp b/src/hotspot/share/adlc/output_h.cpp index 6cb82f4df7f74..f7389b5a1b1f0 100644 --- a/src/hotspot/share/adlc/output_h.cpp +++ b/src/hotspot/share/adlc/output_h.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1998, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1998, 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 @@ -1841,112 +1841,6 @@ void ArchDesc::declareClasses(FILE *fp) { fprintf(fp," virtual const Pipeline *pipeline() const;\n"); } - // Generate virtual function for MachNodeX::bottom_type when necessary - // - // Note on accuracy: Pointer-types of machine nodes need to be accurate, - // or else alias analysis on the matched graph may produce bad code. - // Moreover, the aliasing decisions made on machine-node graph must be - // no less accurate than those made on the ideal graph, or else the graph - // may fail to schedule. (Reason: Memory ops which are reordered in - // the ideal graph might look interdependent in the machine graph, - // thereby removing degrees of scheduling freedom that the optimizer - // assumed would be available.) - // - // %%% We should handle many of these cases with an explicit ADL clause: - // instruct foo() %{ ... bottom_type(TypeRawPtr::BOTTOM); ... %} - if( data_type != Form::none ) { - // A constant's bottom_type returns a Type containing its constant value - - // !!!!! - // Convert all ints, floats, ... to machine-independent TypeXs - // as is done for pointers - // - // Construct appropriate constant type containing the constant value. - fprintf(fp," virtual const class Type *bottom_type() const {\n"); - switch( data_type ) { - case Form::idealI: - fprintf(fp," return TypeInt::make(opnd_array(1)->constant());\n"); - break; - case Form::idealP: - case Form::idealN: - case Form::idealNKlass: - fprintf(fp," return opnd_array(1)->type();\n"); - break; - case Form::idealD: - fprintf(fp," return TypeD::make(opnd_array(1)->constantD());\n"); - break; - case Form::idealH: - fprintf(fp," return TypeH::make(opnd_array(1)->constantH());\n"); - break; - case Form::idealF: - fprintf(fp," return TypeF::make(opnd_array(1)->constantF());\n"); - break; - case Form::idealL: - fprintf(fp," return TypeLong::make(opnd_array(1)->constantL());\n"); - break; - default: - assert( false, "Unimplemented()" ); - break; - } - fprintf(fp," };\n"); - } -/* else if ( instr->_matrule && instr->_matrule->_rChild && - ( strcmp("ConvF2I",instr->_matrule->_rChild->_opType)==0 - || strcmp("ConvD2I",instr->_matrule->_rChild->_opType)==0 ) ) { - // !!!!! !!!!! - // Provide explicit bottom type for conversions to int - // On Intel the result operand is a stackSlot, untyped. - fprintf(fp," virtual const class Type *bottom_type() const {"); - fprintf(fp, " return TypeInt::INT;"); - fprintf(fp, " };\n"); - }*/ - else if( instr->is_ideal_copy() && - !strcmp(instr->_matrule->_lChild->_opType,"stackSlotP") ) { - // !!!!! - // Special hack for ideal Copy of pointer. Bottom type is oop or not depending on input. - fprintf(fp," const Type *bottom_type() const { return in(1)->bottom_type(); } // Copy?\n"); - } - else if( instr->is_ideal_loadPC() ) { - // LoadPCNode provides the return address of a call to native code. - // Define its bottom type to be TypeRawPtr::BOTTOM instead of TypePtr::BOTTOM - // since it is a pointer to an internal VM location and must have a zero offset. - // Allocation detects derived pointers, in part, by their non-zero offsets. - fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // LoadPC?\n"); - } - else if( instr->is_ideal_box() ) { - // BoxNode provides the address of a stack slot. - // Define its bottom type to be TypeRawPtr::BOTTOM instead of TypePtr::BOTTOM - // This prevents raise_above_anti_dependences from complaining. It will - // complain if it sees that the pointer base is TypePtr::BOTTOM since - // it doesn't understand what that might alias. - fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // Box?\n"); - } - else if (instr->_matrule && instr->_matrule->_rChild && - (!strcmp(instr->_matrule->_rChild->_opType,"CMoveP") || !strcmp(instr->_matrule->_rChild->_opType,"CMoveN")) ) { - int offset = 1; - // Special special hack to see if the Cmp? has been incorporated in the conditional move - MatchNode *rl = instr->_matrule->_rChild->_lChild; - if (rl && !strcmp(rl->_opType, "Binary") && rl->_rChild && strncmp(rl->_rChild->_opType, "Cmp", 3) == 0) { - offset = 2; - fprintf(fp," const Type *bottom_type() const { if (req() == 3) return in(2)->bottom_type();\n\tconst Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // %s\n", - offset, offset+1, offset+1, instr->_matrule->_rChild->_opType); - } else { - // Special hack for ideal CMove; ideal type depends on inputs - fprintf(fp," const Type *bottom_type() const { const Type *t = in(oper_input_base()+%d)->bottom_type(); return (req() <= oper_input_base()+%d) ? t : t->meet(in(oper_input_base()+%d)->bottom_type()); } // %s\n", - offset, offset+1, offset+1, instr->_matrule->_rChild->_opType); - } - } - else if (instr->is_tls_instruction()) { - // Special hack for tlsLoadP - fprintf(fp," const Type *bottom_type() const { return TypeRawPtr::BOTTOM; } // tlsLoadP\n"); - } - else if ( instr->is_ideal_if() ) { - fprintf(fp," const Type *bottom_type() const { return TypeTuple::IFBOTH; } // matched IfNode\n"); - } - else if ( instr->is_ideal_membar() ) { - fprintf(fp," const Type *bottom_type() const { return TypeTuple::MEMBAR; } // matched MemBar\n"); - } - // Check where 'ideal_type' must be customized /* if ( instr->_matrule && instr->_matrule->_rChild && diff --git a/src/hotspot/share/opto/machnode.cpp b/src/hotspot/share/opto/machnode.cpp index ec861865ff590..c35861df73505 100644 --- a/src/hotspot/share/opto/machnode.cpp +++ b/src/hotspot/share/opto/machnode.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -561,7 +561,11 @@ void MachNode::dump_spec(outputStream *st) const { if (barrier_data() != 0) { st->print(" barrier("); BarrierSet::barrier_set()->barrier_set_c2()->dump_barrier_data(this, st); - st->print(") "); + st->print(")"); + } + if (_bottom_type != nullptr) { + st->print(" "); + _bottom_type->dump_on(st); } } @@ -572,19 +576,6 @@ void MachNode::dump_format(PhaseRegAlloc *ra, outputStream *st) const { } #endif -//============================================================================= -#ifndef PRODUCT -void MachTypeNode::dump_spec(outputStream *st) const { - MachNode::dump_spec(st); - if (_bottom_type != nullptr) { - _bottom_type->dump_on(st); - } else { - st->print(" null"); - } -} -#endif - - //============================================================================= int MachConstantNode::constant_offset() { // Bind the offset lazily. diff --git a/src/hotspot/share/opto/machnode.hpp b/src/hotspot/share/opto/machnode.hpp index b60313b7f7583..daa2aad960ad1 100644 --- a/src/hotspot/share/opto/machnode.hpp +++ b/src/hotspot/share/opto/machnode.hpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 1997, 2025, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1997, 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 @@ -220,7 +220,7 @@ class MachOper : public ResourceObj { // ADLC inherit from this class. class MachNode : public Node { public: - MachNode() : Node((uint)0), _barrier(0), _num_opnds(0), _opnds(nullptr) { + MachNode() : Node((uint)0), _bottom_type(nullptr), _barrier(0), _num_opnds(0), _opnds(nullptr) { init_class_id(Class_Mach); } // Required boilerplate @@ -282,6 +282,9 @@ class MachNode : public Node { // output have choices - but they must use the same choice. virtual uint two_adr( ) const { return 0; } + // Capture the type of the matched ideal node + const Type* _bottom_type; + // The GC might require some barrier metadata for machine code emission. uint8_t _barrier; @@ -331,7 +334,17 @@ class MachNode : public Node { virtual MachNode *Expand( State *, Node_List &proj_list, Node* mem ) { return this; } // Bottom_type call; value comes from operand0 - virtual const class Type *bottom_type() const { return _opnds[0]->type(); } + virtual const Type* bottom_type() const { + if (_bottom_type != nullptr) { + return _bottom_type; + } + const Type* res = _opnds[0]->type(); + // The type system around pointers is complex, do not rely on operand type then + assert(res != nullptr, "must be not null"); + assert(is_MachTemp() || res->isa_ptr() == nullptr, "must not be a pointer"); + return res; + } + virtual uint ideal_reg() const { const Type *t = _opnds[0]->type(); if (t == TypeInt::CC) { @@ -418,20 +431,6 @@ class MachIdealNode : public MachNode { virtual const class Type *bottom_type() const { return _opnds == nullptr ? Type::CONTROL : MachNode::bottom_type(); } }; -//------------------------------MachTypeNode---------------------------- -// Machine Nodes that need to retain a known Type. -class MachTypeNode : public MachNode { - virtual uint size_of() const { return sizeof(*this); } // Size is bigger -public: - MachTypeNode( ) {} - const Type *_bottom_type; - - virtual const class Type *bottom_type() const { return _bottom_type; } -#ifndef PRODUCT - virtual void dump_spec(outputStream *st) const; -#endif -}; - //------------------------------MachBreakpointNode---------------------------- // Machine breakpoint or interrupt Node class MachBreakpointNode : public MachIdealNode { @@ -477,12 +476,12 @@ class MachConstantBaseNode : public MachIdealNode { //------------------------------MachConstantNode------------------------------- // Machine node that holds a constant which is stored in the constant table. -class MachConstantNode : public MachTypeNode { +class MachConstantNode : public MachNode { protected: ConstantTable::Constant _constant; // This node's constant. public: - MachConstantNode() : MachTypeNode() { + MachConstantNode() : MachNode() { init_class_id(Class_MachConstant); } diff --git a/src/hotspot/share/opto/matcher.hpp b/src/hotspot/share/opto/matcher.hpp index 4de41d6f2ef25..3ad109d292af9 100644 --- a/src/hotspot/share/opto/matcher.hpp +++ b/src/hotspot/share/opto/matcher.hpp @@ -38,7 +38,6 @@ class Compile; class Node; class MachNode; -class MachTypeNode; class MachOper; //---------------------------Matcher------------------------------------------- diff --git a/test/hotspot/jtreg/compiler/types/TestCMovePMachType.java b/test/hotspot/jtreg/compiler/types/TestCMovePMachType.java new file mode 100644 index 0000000000000..48069fd900eba --- /dev/null +++ b/test/hotspot/jtreg/compiler/types/TestCMovePMachType.java @@ -0,0 +1,161 @@ +/* + * 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. + */ +package compiler.types; + +/* + * @test + * @bug 8379667 + * @summary C2 crashes due to deep recursion in cmovP_regNode::bottom_type + * @run main/othervm -Xbatch -XX:CompileCommand=memlimit,${test.main.class}::test,50M~crash ${test.main.class} + */ +public class TestCMovePMachType { + interface I0 {} + interface I1 {} + interface I2 {} + interface I3 {} + interface I4 {} + interface I5 {} + interface I6 {} + interface I7 {} + interface I8 {} + interface I9 {} + interface I10 {} + interface I11 {} + interface I12 {} + interface I13 {} + interface I14 {} + interface I15 {} + interface I16 {} + interface I17 {} + interface I18 {} + interface I19 {} + interface IA {} + interface IB {} + + static class P implements I0, I1, I2, I3, I4, I5, I6, I7, I8, I9, I10, I11, I12, I13, I14, I15, I16, I17, I18, I19 { + int v; + } + + static class A extends P implements IA {} + static class B extends P implements IB {} + + public static void main(String[] args) { + A a = new A(); + B b = new B(); + for (int i = 0; i < 20000; i++) { + test(a, b, false, false); + test(a, b, false, true); + test(a, b, true, false); + test(a, b, true, true); + } + } + + // This method is compiled into a giant chain of CMovePs, which leads to a deep recursion when + // invoking Node::bottom_type on the corresponding MachNodes + private static int test(A p1, B p2, boolean b1, boolean b2) { + P p = b1 ? p1 : p2; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + p = b2 ? p : p2; + p = b1 ? p : p1; + int r = p.v; + p.v = 0; + return r; + } +}