diff --git a/compiler/compile/OMRCompilation.cpp b/compiler/compile/OMRCompilation.cpp index 2a67e51ac59..af4f34c462e 100644 --- a/compiler/compile/OMRCompilation.cpp +++ b/compiler/compile/OMRCompilation.cpp @@ -286,6 +286,7 @@ OMR::Compilation::Compilation( _gpuPtxCount(0), _bitVectorPool(self()), _typeLayoutMap((LayoutComparator()), LayoutAllocator(self()->region())), + _currentILGenCallTarget(NULL), _tlsManager(*self()) { if (target != NULL) diff --git a/compiler/compile/OMRCompilation.hpp b/compiler/compile/OMRCompilation.hpp index e6a2ca838e9..7455e2cd502 100644 --- a/compiler/compile/OMRCompilation.hpp +++ b/compiler/compile/OMRCompilation.hpp @@ -74,6 +74,7 @@ namespace OMR { typedef OMR::Compilation CompilationConnector; } class TR_AOTGuardSite; class TR_BitVector; +struct TR_CallTarget; class TR_CHTable; class TR_FrontEnd; class TR_HWPRecord; @@ -1125,6 +1126,24 @@ class OMR_EXTENSIBLE Compilation */ TR::Environment& target() { return _target; } + /** + * \brief + * The inline call target, if any, for which IL is currently being generated. + * + * \return + * the call target + */ + TR_CallTarget *currentILGenCallTarget() { return _currentILGenCallTarget; } + + /** + * \brief + * Set the inline call target for which IL is currently being generated. + * + * This should be set in the inliner just prior to generating IL to inline, + * and it should be reset to null afterward. + */ + void setCurrentILGenCallTarget(TR_CallTarget *x) { _currentILGenCallTarget = x; } + private: void resetVisitCounts(vcount_t, TR::ResolvedMethodSymbol *); int16_t restoreInlineDepthUntil(int32_t stopIndex, TR_ByteCodeInfo ¤tInfo); @@ -1332,6 +1351,8 @@ class OMR_EXTENSIBLE Compilation typedef std::map TypeLayoutMap; TypeLayoutMap _typeLayoutMap; + TR_CallTarget *_currentILGenCallTarget; + /* * This must be last * NOTE: TLS for Compilation needs to be set before any object that may use it is initialized. diff --git a/compiler/il/AnyConst.hpp b/compiler/il/AnyConst.hpp new file mode 100644 index 00000000000..f5beaded7ad --- /dev/null +++ b/compiler/il/AnyConst.hpp @@ -0,0 +1,114 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution + * and is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception [1] and GNU General Public + * License, version 2 with the OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#ifndef ANYCONST_INCL +#define ANYCONST_INCL + +#include "env/KnownObjectTable.hpp" +#include "infra/Assert.hpp" +#include + +namespace TR { + +/** + * \brief A value of arbitrary type that could be the result of a node. + * + * The value is constant just in the sense that it is known at compile time. + */ +class AnyConst + { + enum Kind + { + KindInt8, + KindInt16, + KindInt32, + KindInt64, + KindFloat, + KindDouble, + KindAddress, + KindKnownObject, + }; + + Kind _kind; + union + { + float _float; + double _double; + uint64_t _integral; // for everything else + }; + + AnyConst(Kind kind, uint64_t x) : _kind(kind), _integral(x) {} + AnyConst(float x) : _kind(KindFloat), _float(x) {} + AnyConst(double x) : _kind(KindDouble), _double(x) {} + + public: + + static AnyConst makeInt8(uint8_t x) { return AnyConst(KindInt8, x); } + static AnyConst makeInt16(uint16_t x) { return AnyConst(KindInt16, x); } + static AnyConst makeInt32(uint32_t x) { return AnyConst(KindInt32, x); } + static AnyConst makeInt64(uint64_t x) { return AnyConst(KindInt64, x); } + static AnyConst makeFloat(float x) { return AnyConst(x); } + static AnyConst makeDouble(double x) { return AnyConst(x); } + + // Address: mostly for null, but can be used for pointers to data structures + // that don't move and for object references while VM access is held. + static AnyConst makeAddress(uintptr_t x) { return AnyConst(KindAddress, x); } + + static AnyConst makeKnownObject(TR::KnownObjectTable::Index i) + { + TR_ASSERT_FATAL(i != TR::KnownObjectTable::UNKNOWN, "should be known"); + return AnyConst(KindKnownObject, i); + } + + bool isInt8() const { return _kind == KindInt8; } + bool isInt16() const { return _kind == KindInt16; } + bool isInt32() const { return _kind == KindInt32; } + bool isInt64() const { return _kind == KindInt64; } + bool isFloat() const { return _kind == KindFloat; } + bool isDouble() const { return _kind == KindDouble; } + bool isAddress() const { return _kind == KindAddress; } + bool isKnownObject() const { return _kind == KindKnownObject; } + + uint8_t getInt8() const { kindAssert(KindInt8); return (uint8_t)_integral; } + uint16_t getInt16() const { kindAssert(KindInt16); return (uint16_t)_integral; } + uint32_t getInt32() const { kindAssert(KindInt32); return (uint32_t)_integral; } + uint64_t getInt64() const { kindAssert(KindInt64); return (uint64_t)_integral; } + float getFloat() const { kindAssert(KindFloat); return _float; } + double getDouble() const { kindAssert(KindDouble); return _double; } + uintptr_t getAddress() const { kindAssert(KindAddress); return (uintptr_t)_integral; } + + TR::KnownObjectTable::Index getKnownObjectIndex() const + { + kindAssert(KindKnownObject); + return (TR::KnownObjectTable::Index)_integral; + } + + private: + + void kindAssert(Kind expected) const + { + TR_ASSERT_FATAL(_kind == expected, "type mismatch"); + } + }; + +} // namespace TR + +#endif // ANYCONST_INCL diff --git a/compiler/optimizer/CallInfo.hpp b/compiler/optimizer/CallInfo.hpp index 4fed9d58d4e..1e462669dd8 100644 --- a/compiler/optimizer/CallInfo.hpp +++ b/compiler/optimizer/CallInfo.hpp @@ -33,12 +33,16 @@ #include "compile/Compilation.hpp" #include "env/TRMemory.hpp" #include "env/jittypes.h" +#include "il/AnyConst.hpp" #include "il/MethodSymbol.hpp" #include "il/Node.hpp" #include "infra/Assert.hpp" #include "infra/deque.hpp" #include "infra/Link.hpp" #include "infra/List.hpp" +#include "infra/map.hpp" +#include "infra/TRlist.hpp" +#include "optimizer/DeferredOSRAssumption.hpp" #include "optimizer/InlinerFailureReason.hpp" namespace TR { class CompilationFilters;} @@ -59,6 +63,8 @@ namespace TR { class TreeTop; } class TR_CallSite; struct TR_VirtualGuardSelection; +namespace TR { struct RequiredConst; } + class TR_CallStack : public TR_Link { public: @@ -127,13 +133,43 @@ class TR_CallStack : public TR_Link }; +namespace TR { + +/** + * \brief A constant value that was observed during call target selection. + * + * When IL is generated for a call target, it's necessary in general to repeat + * any constant folding that occurred while selecting targets recursively + * within it so that the IL is guaranteed to match what the inliner saw. + * + * This repeated folding is important whenever a value might be allowed to be + * folded despite the possibility of a later change. It also allows constants + * to be speculative by specifying the assumptions that are necessary in order + * for the folding to be correct, and by informing the IL generator of the + * locations where such assumptions have been made (though the locations are + * tracked externally to this class). + */ +struct RequiredConst + { + TR::AnyConst _value; ///< The value. + + /// The assumptions required to guarantee the value is constant, if any. + TR::list _assumptions; + + RequiredConst(const TR::AnyConst &value, TR::Region ®ion) + : _value(value), _assumptions(region) {} + }; + +} // namespace TR + struct TR_CallTarget : public TR_Link { TR_ALLOC(TR_Memory::Inliner); friend class TR_InlinerTracer; - TR_CallTarget(TR_CallSite *callsite, + TR_CallTarget(TR::Region &memRegion, + TR_CallSite *callsite, TR::ResolvedMethodSymbol *calleeSymbol, TR_ResolvedMethod *calleeMethod, TR_VirtualGuardSelection *guard, @@ -190,6 +226,12 @@ struct TR_CallTarget : public TR_Link TR_PrexArgInfo *_prexArgInfo; // used by computePrexInfo to calculate prex on generatedIL and transform IL TR_PrexArgInfo *_ecsPrexArgInfo; // used by ECS and findInlineTargets to assist in choosing correct inline targets + /** + * \brief Constant values that were observed during call target selection + * within this particular call target, keyed on bytecode index. + */ + TR::map _requiredConsts; + void addDeadCallee(TR_CallSite *cs) { _deletedCallees.add(cs); diff --git a/compiler/optimizer/DeferredOSRAssumption.hpp b/compiler/optimizer/DeferredOSRAssumption.hpp new file mode 100644 index 00000000000..8793722b330 --- /dev/null +++ b/compiler/optimizer/DeferredOSRAssumption.hpp @@ -0,0 +1,52 @@ +/******************************************************************************* + * Copyright IBM Corp. and others 2024 + * + * This program and the accompanying materials are made available under + * the terms of the Eclipse Public License 2.0 which accompanies this + * distribution and is available at https://www.eclipse.org/legal/epl-2.0/ + * or the Apache License, Version 2.0 which accompanies this distribution + * and is available at https://www.apache.org/licenses/LICENSE-2.0. + * + * This Source Code may also be made available under the following Secondary + * Licenses when the conditions for such availability set forth in the + * Eclipse Public License, v. 2.0 are satisfied: GNU General Public License, + * version 2 with the GNU Classpath Exception [1] and GNU General Public + * License, version 2 with the OpenJDK Assembly Exception [2]. + * + * [1] https://www.gnu.org/software/classpath/license.html + * [2] https://openjdk.org/legal/assembly-exception.html + * + * SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 OR GPL-2.0-only WITH OpenJDK-assembly-exception-1.0 + *******************************************************************************/ + +#ifndef DEFERRED_OSR_ASSUMPTION_INCL +#define DEFERRED_OSR_ASSUMPTION_INCL + +namespace TR { class Compilation; } + +namespace TR { + +/** + * \brief An OSR assumption that has been provisionally used, but that is not + * necessarily required by the current compilation (yet). + * + * One or more deferred OSR assumptions can be associated with an intermediate + * result that may or may not turn out to be significant. If/when that result + * is later relied upon, e.g. as the justification for an IL transformation, + * its assumptions must be committed, at which point the compilation will + * arrange to create the appropriate runtime assumption on completion. If OTOH + * the result is never used, its deferred assumptions need not be committed, + * which helps to avoid creating irrelevant runtime assumptions. + */ +struct DeferredOSRAssumption + { + /** + * \brief Update \p comp so that this assumption will be required. + * \param comp the compilation object + */ + virtual void commit(TR::Compilation *comp) = 0; + }; + +} // namespace TR + +#endif // DEFERRED_OSR_ASSUMPTION_INCL diff --git a/compiler/optimizer/Inliner.cpp b/compiler/optimizer/Inliner.cpp index 1b1014ff655..30bd3da34c4 100644 --- a/compiler/optimizer/Inliner.cpp +++ b/compiler/optimizer/Inliner.cpp @@ -4772,7 +4772,16 @@ bool TR_InlinerBase::inlineCallTarget2(TR_CallStack * callStack, TR_CallTarget * calleeSymbol->setUnsynchronised(); } + TR_ASSERT_FATAL( + comp()->currentILGenCallTarget() == NULL, + "unexpected recursive call to inlineCallTarget2 during ilgen: " + "existing target %p vs. new %p", + comp()->currentILGenCallTarget(), + calltarget); + + comp()->setCurrentILGenCallTarget(calltarget); genILSucceeded = tryToGenerateILForMethod(calleeSymbol, callerSymbol, calltarget); + comp()->setCurrentILGenCallTarget(NULL); if (wasSynchronized) calleeSymbol->setSynchronised(); @@ -5572,7 +5581,8 @@ void TR_CallSite::removeAllTargets(TR_InlinerTracer *tracer, TR_InlinerFailureRe removeTargets(tracer, 0, reason); } -TR_CallTarget::TR_CallTarget(TR_CallSite *callsite, +TR_CallTarget::TR_CallTarget(TR::Region &memRegion, + TR_CallSite *callsite, TR::ResolvedMethodSymbol *calleeSymbol, TR_ResolvedMethod *calleeMethod, TR_VirtualGuardSelection *guard, @@ -5586,7 +5596,8 @@ TR_CallTarget::TR_CallTarget(TR_CallSite *callsite, _receiverClass(receiverClass), _frequencyAdjustment(freqAdj), _prexArgInfo(NULL), - _ecsPrexArgInfo(ecsPrexArgInfo) + _ecsPrexArgInfo(ecsPrexArgInfo), + _requiredConsts(memRegion) { _weight=0; _callGraphAdjustedWeight=0; @@ -5704,7 +5715,28 @@ TR_CallSite::addTarget(TR_Memory* mem, TR_InlinerBase *inliner, TR_VirtualGuardS myPrexArgInfo = new (comp()->trHeapMemory()) TR_PrexArgInfo(_ecsPrexArgInfo, comp()->trMemory()); } - TR_CallTarget *result = new (mem,allocKind) TR_CallTarget(this,_initialCalleeSymbol,implementer,guard,receiverClass,myPrexArgInfo,ratio); + TR::Region *memRegion = NULL; + switch (allocKind) + { + case heapAlloc: + memRegion = &mem->heapMemoryRegion(); + break; + case stackAlloc: + memRegion = &mem->currentStackRegion(); + break; + default: + TR_ASSERT_FATAL(false, "unexpected alloc kind %d for call target", (int)allocKind); + } + + TR_CallTarget *result = new (*memRegion) TR_CallTarget( + *memRegion, + this, + _initialCalleeSymbol, + implementer, + guard, + receiverClass, + myPrexArgInfo, + ratio); addTarget(result); diff --git a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp index d780707aa5c..8cc49e12b64 100644 --- a/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp +++ b/compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp @@ -74,6 +74,7 @@ TR::IDT* OMR::IDTBuilder::buildIDT() comp()); TR_CallTarget *rootCallTarget = new (region()) TR_CallTarget( + region(), rootCallSite, _rootSymbol, rootMethod,