Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Facilitate recording of constants observed in call target selection #7282

Merged
merged 2 commits into from
Mar 14, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/compile/OMRCompilation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ OMR::Compilation::Compilation(
_gpuPtxCount(0),
_bitVectorPool(self()),
_typeLayoutMap((LayoutComparator()), LayoutAllocator(self()->region())),
_currentILGenCallTarget(NULL),
_tlsManager(*self())
{
if (target != NULL)
Expand Down
21 changes: 21 additions & 0 deletions compiler/compile/OMRCompilation.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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 &currentInfo);
Expand Down Expand Up @@ -1332,6 +1351,8 @@ class OMR_EXTENSIBLE Compilation
typedef std::map<TR_OpaqueClassBlock *, const TR::TypeLayout *, LayoutComparator, LayoutAllocator> 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.
Expand Down
114 changes: 114 additions & 0 deletions compiler/il/AnyConst.hpp
Original file line number Diff line number Diff line change
@@ -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 <stdint.h>

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
44 changes: 43 additions & 1 deletion compiler/optimizer/CallInfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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;}
Expand All @@ -59,6 +63,8 @@ namespace TR { class TreeTop; }
class TR_CallSite;
struct TR_VirtualGuardSelection;

namespace TR { struct RequiredConst; }

class TR_CallStack : public TR_Link<TR_CallStack>
{
public:
Expand Down Expand Up @@ -127,13 +133,43 @@ class TR_CallStack : public TR_Link<TR_CallStack>

};

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<TR::DeferredOSRAssumption*, TR::Region&> _assumptions;

RequiredConst(const TR::AnyConst &value, TR::Region &region)
: _value(value), _assumptions(region) {}
};

} // namespace TR

struct TR_CallTarget : public TR_Link<TR_CallTarget>
{
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,
Expand Down Expand Up @@ -190,6 +226,12 @@ struct TR_CallTarget : public TR_Link<TR_CallTarget>
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<int32_t, TR::RequiredConst> _requiredConsts;

void addDeadCallee(TR_CallSite *cs)
{
_deletedCallees.add(cs);
Expand Down
52 changes: 52 additions & 0 deletions compiler/optimizer/DeferredOSRAssumption.hpp
Original file line number Diff line number Diff line change
@@ -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
38 changes: 35 additions & 3 deletions compiler/optimizer/Inliner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is an exception during IL gen due to an unsupported bytecode for example, I assume nothing would go wrong since we would exit the scope where the setting of "current IL gen call target" to would matter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If an exception propagates beyond this point, then yes, the compilation will fail as a whole, so the current IL gen call target won't matter anymore

Some exceptions are caught within the call (more specifically, in ResolvedMethodSymbol::genIL()), and they only cause IL generation to fail. I believe something like an unsupported bytecode would be one of those. In that case, we'd just set genILSucceeded=false and then still clear the current call target


if (wasSynchronized)
calleeSymbol->setSynchronised();
Expand Down Expand Up @@ -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,
Expand All @@ -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;
Expand Down Expand Up @@ -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);

Expand Down
1 change: 1 addition & 0 deletions compiler/optimizer/abstractinterpreter/OMRIDTBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ TR::IDT* OMR::IDTBuilder::buildIDT()
comp());

TR_CallTarget *rootCallTarget = new (region()) TR_CallTarget(
region(),
rootCallSite,
_rootSymbol,
rootMethod,
Expand Down