blob: c060dab974380ba29976b364501a60431acfd330 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#pragma once
class IntBoundedValueInfo : public ValueInfo
{
private:
const IntBounds *const bounds;
// Definitely-int values are inherently not negative zero. This member variable, if true, indicates that this value was
// produced by an int-specialized instruction that prevented a negative zero result using a negative zero bailout
// (BailOutOnNegativeZero). Negative zero tracking in the dead-store phase tracks this information to see if some of these
// negative zero bailout checks can be removed.
bool wasNegativeZeroPreventedByBailout;
protected:
IntBoundedValueInfo(const ValueType type, const IntBounds *const bounds, const bool wasNegativeZeroPreventedByBailout)
:
ValueInfo(
type.IsInt()
? bounds->ConstantBounds().GetValueType()
: bounds->ConstantBounds().IsLikelyTaggable() ? type : type.ToLikelyUntaggedInt(),
ValueStructureKind::IntBounded),
bounds(bounds),
wasNegativeZeroPreventedByBailout(wasNegativeZeroPreventedByBailout)
{
Assert(type.IsLikelyInt());
Assert(Type().IsLikelyInt());
Assert(Type().IsInt() == type.IsInt());
bounds->Verify();
// Bounds for definitely int values should have relative bounds, otherwise those values should use one of the other
// value infos
Assert(bounds->RequiresIntBoundedValueInfo(Type()));
Assert(!wasNegativeZeroPreventedByBailout || type.IsInt());
Assert(!wasNegativeZeroPreventedByBailout || bounds->ConstantLowerBound() <= 0);
Assert(!wasNegativeZeroPreventedByBailout || bounds->ConstantUpperBound() >= 0);
}
public:
static IntBoundedValueInfo *New(
const ValueType type,
const IntBounds *const bounds,
const bool wasNegativeZeroPreventedByBailout,
JitArenaAllocator *const allocator)
{
Assert(allocator);
return JitAnew(allocator, IntBoundedValueInfo, type, bounds, wasNegativeZeroPreventedByBailout);
}
IntBoundedValueInfo *Copy(JitArenaAllocator *const allocator) const
{
Assert(allocator);
return JitAnew(allocator, IntBoundedValueInfo, *this);
}
public:
const IntBounds *Bounds() const
{
return bounds;
}
bool WasNegativeZeroPreventedByBailout() const
{
return wasNegativeZeroPreventedByBailout;
}
};
class LoopCount
{
private:
bool hasBeenGenerated;
// Information needed to generate the loop count instructions
// loopCountMinusOne = (left - right + offset) / minMagnitudeChange
StackSym *leftSym, *rightSym;
int offset, minMagnitudeChange;
// Information needed to use the computed loop count
StackSym *loopCountMinusOneSym;
StackSym *loopCountSym; // Not generated by default and depends on loopCountMinusOneSym
int loopCountMinusOneConstantValue;
public:
LoopCount(StackSym *const leftSym, StackSym *const rightSym, const int offset, const int minMagnitudeChange)
: leftSym(leftSym), rightSym(rightSym), offset(offset), minMagnitudeChange(minMagnitudeChange), hasBeenGenerated(false), loopCountSym(nullptr)
{
Assert(leftSym || rightSym);
Assert(!leftSym || leftSym->GetType() == TyInt32 || leftSym->GetType() == TyUint32);
Assert(!rightSym || rightSym->GetType() == TyInt32 || rightSym->GetType() == TyUint32);
Assert(minMagnitudeChange > 0);
}
LoopCount(StackSym *const loopCountMinusOneSym) : loopCountMinusOneSym(loopCountMinusOneSym), hasBeenGenerated(true), loopCountSym(nullptr)
{
Assert(loopCountMinusOneSym);
}
LoopCount(StackSym *const loopCountMinusOneSym, StackSym *const loopCountSym) :
loopCountMinusOneSym(loopCountMinusOneSym),
loopCountSym(loopCountSym),
hasBeenGenerated(true)
{
Assert(loopCountMinusOneSym);
}
LoopCount(const int loopCountMinusOneConstantValue)
: loopCountMinusOneSym(nullptr), loopCountMinusOneConstantValue(loopCountMinusOneConstantValue), hasBeenGenerated(true), loopCountSym(nullptr)
{
Assert(loopCountMinusOneConstantValue >= 0);
}
public:
bool HasBeenGenerated() const
{
return hasBeenGenerated;
}
bool HasGeneratedLoopCountSym() const
{
// Consider loop count sym generated if there is no loopCountMinusOneSym and it has been generated
return hasBeenGenerated && (loopCountSym != nullptr || loopCountMinusOneSym == nullptr);
}
StackSym *LeftSym() const
{
Assert(!HasBeenGenerated());
return leftSym;
}
StackSym *RightSym() const
{
Assert(!HasBeenGenerated());
return rightSym;
}
int Offset() const
{
Assert(!HasBeenGenerated());
return offset;
}
int MinMagnitudeChange() const
{
Assert(!HasBeenGenerated());
return minMagnitudeChange;
}
StackSym *LoopCountMinusOneSym() const
{
Assert(HasBeenGenerated());
return loopCountMinusOneSym;
}
StackSym *LoopCountSym() const
{
Assert(HasGeneratedLoopCountSym());
return loopCountSym;
}
void SetLoopCountSym(StackSym *const loopCountSym)
{
Assert(HasBeenGenerated());
Assert(loopCountSym);
this->loopCountSym = loopCountSym;
}
void SetLoopCountMinusOneSym(StackSym *const loopCountMinusOneSym)
{
Assert(!HasBeenGenerated());
Assert(loopCountMinusOneSym);
hasBeenGenerated = true;
this->loopCountMinusOneSym = loopCountMinusOneSym;
}
int LoopCountMinusOneConstantValue() const
{
Assert(!LoopCountMinusOneSym());
return loopCountMinusOneConstantValue;
}
};
class GlobOpt::AddSubConstantInfo
{
private:
StackSym *srcSym;
Value *srcValue;
bool srcValueIsLikelyConstant;
int32 offset;
public:
AddSubConstantInfo() : srcSym(nullptr)
{
}
public:
bool HasInfo() const
{
return !!srcSym;
}
StackSym *SrcSym() const
{
Assert(HasInfo());
return srcSym;
}
Value *SrcValue() const
{
Assert(HasInfo());
return srcValue;
}
bool SrcValueIsLikelyConstant() const
{
Assert(HasInfo());
return srcValueIsLikelyConstant;
}
int32 Offset() const
{
Assert(HasInfo());
return offset;
}
public:
void Set(StackSym *const srcSym, Value *const srcValue, const bool srcValueIsLikelyConstant, const int32 offset);
};
class GlobOpt::ArrayLowerBoundCheckHoistInfo
{
protected:
BasicBlock *compatibleBoundCheckBlock;
Loop *loop;
// Info populated for a compatible bound check and for hoisting out of loop
StackSym *indexSym;
int offset;
ValueNumber indexValueNumber;
// Info populated for hoisting out of loop
Value *indexValue;
IntConstantBounds indexConstantBounds; // also populated for constant index, when there's a compatible bound check
bool isLoopCountBasedBound;
// Info populated for hoisting out of loop using a loop count based bound, when the bound needs to be generated
LoopCount *loopCount;
int maxMagnitudeChange;
public:
ArrayLowerBoundCheckHoistInfo() : compatibleBoundCheckBlock(nullptr), loop(nullptr)
{
}
public:
bool HasAnyInfo() const
{
return CompatibleBoundCheckBlock() || Loop();
}
BasicBlock *CompatibleBoundCheckBlock() const
{
Assert(!(compatibleBoundCheckBlock && loop));
return compatibleBoundCheckBlock;
}
Loop *Loop() const
{
Assert(!(compatibleBoundCheckBlock && loop));
return loop;
}
StackSym *IndexSym() const
{
Assert(HasAnyInfo());
return indexSym;
}
int Offset() const
{
Assert(HasAnyInfo());
return offset;
}
void UpdateOffset(int newOffset)
{
Assert(HasAnyInfo());
offset = newOffset;
}
ValueNumber IndexValueNumber() const
{
Assert(HasAnyInfo());
Assert(IndexSym());
return indexValueNumber;
}
Value *IndexValue() const
{
Assert(Loop());
return indexValue;
}
const IntConstantBounds &IndexConstantBounds() const
{
Assert(Loop() || CompatibleBoundCheckBlock() && !IndexSym());
return indexConstantBounds;
}
bool IsLoopCountBasedBound() const
{
Assert(Loop());
return isLoopCountBasedBound;
}
LoopCount *LoopCount() const
{
Assert(Loop());
return loopCount;
}
int MaxMagnitudeChange() const
{
Assert(LoopCount());
return maxMagnitudeChange;
}
public:
void SetCompatibleBoundCheck(BasicBlock *const compatibleBoundCheckBlock, StackSym *const indexSym, const int offset, const ValueNumber indexValueNumber);
void SetLoop(::Loop *const loop, const int indexConstantValue, const bool isLoopCountBasedBound = false);
void SetLoop(::Loop *const loop, StackSym *const indexSym, const int offset, Value *const indexValue, const IntConstantBounds &indexConstantBounds, const bool isLoopCountBasedBound = false);
void SetLoopCount(::LoopCount *const loopCount, const int maxMagnitudeChange);
};
class GlobOpt::ArrayUpperBoundCheckHoistInfo : protected ArrayLowerBoundCheckHoistInfo
{
private:
typedef ArrayLowerBoundCheckHoistInfo Base;
private:
// Info populated for hoisting out of loop
Value *headSegmentLengthValue;
IntConstantBounds headSegmentLengthConstantBounds;
public:
using Base::HasAnyInfo;
using Base::CompatibleBoundCheckBlock;
using Base::Loop;
using Base::IndexSym;
using Base::Offset;
using Base::UpdateOffset;
using Base::IndexValueNumber;
using Base::IndexValue;
using Base::IndexConstantBounds;
using Base::IsLoopCountBasedBound;
using Base::LoopCount;
using Base::MaxMagnitudeChange;
public:
Value *HeadSegmentLengthValue() const
{
Assert(Loop());
return headSegmentLengthValue;
}
const IntConstantBounds &HeadSegmentLengthConstantBounds() const
{
Assert(Loop());
return headSegmentLengthConstantBounds;
}
public:
using Base::SetCompatibleBoundCheck;
using Base::SetLoopCount;
protected:
using Base::SetLoop;
public:
void SetCompatibleBoundCheck(BasicBlock *const compatibleBoundCheckBlock, const int indexConstantValue);
void SetLoop(::Loop *const loop, const int indexConstantValue, Value *const headSegmentLengthValue, const IntConstantBounds &headSegmentLengthConstantBounds, const bool isLoopCountBasedBound = false);
void SetLoop(::Loop *const loop, StackSym *const indexSym, const int offset, Value *const indexValue, const IntConstantBounds &indexConstantBounds, Value *const headSegmentLengthValue, const IntConstantBounds &headSegmentLengthConstantBounds, const bool isLoopCountBasedBound = false);
};