blob: 5821aed64524d605af0364896602bf3613685028 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
#if ENABLE_DEBUG_CONFIG_OPTIONS && DBG_DUMP
#define TRACE_PHASE_VERBOSE(phase, indent, ...) \
if(PHASE_VERBOSE_TRACE(phase, this->func)) \
{ \
for(int i = 0; i < static_cast<int>(indent); ++i) \
{ \
Output::Print(_u(" ")); \
} \
Output::Print(__VA_ARGS__); \
Output::Flush(); \
}
#else
#define TRACE_PHASE_VERBOSE(phase, indent, ...)
#endif
void GlobOpt::AddSubConstantInfo::Set(
StackSym *const srcSym,
Value *const srcValue,
const bool srcValueIsLikelyConstant,
const int32 offset)
{
Assert(srcSym);
Assert(!srcSym->IsTypeSpec());
Assert(srcValue);
Assert(srcValue->GetValueInfo()->IsLikelyInt());
this->srcSym = srcSym;
this->srcValue = srcValue;
this->srcValueIsLikelyConstant = srcValueIsLikelyConstant;
this->offset = offset;
}
void GlobOpt::ArrayLowerBoundCheckHoistInfo::SetCompatibleBoundCheck(
BasicBlock *const compatibleBoundCheckBlock,
StackSym *const indexSym,
const int offset,
const ValueNumber indexValueNumber)
{
Assert(!Loop());
Assert(compatibleBoundCheckBlock);
Assert(indexSym);
Assert(!indexSym->IsTypeSpec());
Assert(indexValueNumber != InvalidValueNumber);
this->compatibleBoundCheckBlock = compatibleBoundCheckBlock;
this->indexSym = indexSym;
this->offset = offset;
this->indexValueNumber = indexValueNumber;
}
void GlobOpt::ArrayLowerBoundCheckHoistInfo::SetLoop(
::Loop *const loop,
const int indexConstantValue,
const bool isLoopCountBasedBound)
{
Assert(!CompatibleBoundCheckBlock());
Assert(loop);
this->loop = loop;
indexSym = nullptr;
offset = 0;
indexValue = nullptr;
indexConstantBounds = IntConstantBounds(indexConstantValue, indexConstantValue);
this->isLoopCountBasedBound = isLoopCountBasedBound;
loopCount = nullptr;
}
void GlobOpt::ArrayLowerBoundCheckHoistInfo::SetLoop(
::Loop *const loop,
StackSym *const indexSym,
const int indexOffset,
const int offset,
Value *const indexValue,
const IntConstantBounds &indexConstantBounds,
const bool isLoopCountBasedBound)
{
Assert(!CompatibleBoundCheckBlock());
Assert(loop);
Assert(indexSym);
Assert(!indexSym->IsTypeSpec());
Assert(indexValue);
this->loop = loop;
this->indexSym = indexSym;
this->indexOffset = indexOffset;
this->offset = offset;
this->indexValueNumber = indexValue->GetValueNumber();
this->indexValue = indexValue;
this->indexConstantBounds = indexConstantBounds;
this->isLoopCountBasedBound = isLoopCountBasedBound;
loopCount = nullptr;
}
void GlobOpt::ArrayLowerBoundCheckHoistInfo::SetLoopCount(::LoopCount *const loopCount, const int maxMagnitudeChange)
{
Assert(Loop());
Assert(loopCount);
Assert(maxMagnitudeChange != 0);
this->loopCount = loopCount;
this->maxMagnitudeChange = maxMagnitudeChange;
}
void GlobOpt::ArrayUpperBoundCheckHoistInfo::SetCompatibleBoundCheck(
BasicBlock *const compatibleBoundCheckBlock,
const int indexConstantValue)
{
Assert(!Loop());
Assert(compatibleBoundCheckBlock);
this->compatibleBoundCheckBlock = compatibleBoundCheckBlock;
indexSym = nullptr;
offset = -1; // simulate < instead of <=
indexConstantBounds = IntConstantBounds(indexConstantValue, indexConstantValue);
}
void GlobOpt::ArrayUpperBoundCheckHoistInfo::SetLoop(
::Loop *const loop,
const int indexConstantValue,
Value *const headSegmentLengthValue,
const IntConstantBounds &headSegmentLengthConstantBounds,
const bool isLoopCountBasedBound)
{
Assert(!CompatibleBoundCheckBlock());
Assert(loop);
Assert(headSegmentLengthValue);
SetLoop(loop, indexConstantValue, isLoopCountBasedBound);
offset = -1; // simulate < instead of <=
this->headSegmentLengthValue = headSegmentLengthValue;
this->headSegmentLengthConstantBounds = headSegmentLengthConstantBounds;
}
void GlobOpt::ArrayUpperBoundCheckHoistInfo::SetLoop(
::Loop *const loop,
StackSym *const indexSym,
const int indexOffset,
const int offset,
Value *const indexValue,
const IntConstantBounds &indexConstantBounds,
Value *const headSegmentLengthValue,
const IntConstantBounds &headSegmentLengthConstantBounds,
const bool isLoopCountBasedBound)
{
Assert(headSegmentLengthValue);
SetLoop(loop, indexSym, indexOffset, offset, indexValue, indexConstantBounds, isLoopCountBasedBound);
this->headSegmentLengthValue = headSegmentLengthValue;
this->headSegmentLengthConstantBounds = headSegmentLengthConstantBounds;
}
void GlobOpt::UpdateIntBoundsForEqualBranch(
Value *const src1Value,
Value *const src2Value,
const int32 src2ConstantValue)
{
Assert(src1Value);
if(!DoPathDependentValues() || (src2Value && src1Value->GetValueNumber() == src2Value->GetValueNumber()))
{
return;
}
#if DBG
if(!IsLoopPrePass() && DoAggressiveIntTypeSpec() && DoConstFold())
{
IntConstantBounds src1ConstantBounds, src2ConstantBounds;
AssertVerify(src1Value->GetValueInfo()->TryGetIntConstantBounds(&src1ConstantBounds, true));
if(src2Value)
{
AssertVerify(src2Value->GetValueInfo()->TryGetIntConstantBounds(&src2ConstantBounds, true));
}
else
{
src2ConstantBounds = IntConstantBounds(src2ConstantValue, src2ConstantValue);
}
Assert(
!ValueInfo::IsEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
Assert(
!ValueInfo::IsNotEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
}
#endif
SetPathDependentInfo(
true,
PathDependentInfo(PathDependentRelationship::Equal, src1Value, src2Value, src2ConstantValue));
SetPathDependentInfo(
false,
PathDependentInfo(PathDependentRelationship::NotEqual, src1Value, src2Value, src2ConstantValue));
}
void GlobOpt::UpdateIntBoundsForNotEqualBranch(
Value *const src1Value,
Value *const src2Value,
const int32 src2ConstantValue)
{
Assert(src1Value);
if(!DoPathDependentValues() || (src2Value && src1Value->GetValueNumber() == src2Value->GetValueNumber()))
{
return;
}
#if DBG
if(!IsLoopPrePass() && DoAggressiveIntTypeSpec() && DoConstFold())
{
IntConstantBounds src1ConstantBounds, src2ConstantBounds;
AssertVerify(src1Value->GetValueInfo()->TryGetIntConstantBounds(&src1ConstantBounds, true));
if(src2Value)
{
AssertVerify(src2Value->GetValueInfo()->TryGetIntConstantBounds(&src2ConstantBounds, true));
}
else
{
src2ConstantBounds = IntConstantBounds(src2ConstantValue, src2ConstantValue);
}
Assert(
!ValueInfo::IsEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
Assert(
!ValueInfo::IsNotEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
}
#endif
SetPathDependentInfo(
true, PathDependentInfo(PathDependentRelationship::NotEqual, src1Value, src2Value, src2ConstantValue));
SetPathDependentInfo(
false, PathDependentInfo(PathDependentRelationship::Equal, src1Value, src2Value, src2ConstantValue));
}
void GlobOpt::UpdateIntBoundsForGreaterThanOrEqualBranch(Value *const src1Value, Value *const src2Value)
{
Assert(src1Value);
Assert(src2Value);
if(!DoPathDependentValues() || src1Value->GetValueNumber() == src2Value->GetValueNumber())
{
return;
}
#if DBG
if(!IsLoopPrePass() && DoAggressiveIntTypeSpec() && DoConstFold())
{
IntConstantBounds src1ConstantBounds, src2ConstantBounds;
AssertVerify(src1Value->GetValueInfo()->TryGetIntConstantBounds(&src1ConstantBounds, true));
AssertVerify(src2Value->GetValueInfo()->TryGetIntConstantBounds(&src2ConstantBounds, true));
Assert(
!ValueInfo::IsGreaterThanOrEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
Assert(
!ValueInfo::IsLessThan(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
}
#endif
SetPathDependentInfo(true, PathDependentInfo(PathDependentRelationship::GreaterThanOrEqual, src1Value, src2Value));
SetPathDependentInfo(false, PathDependentInfo(PathDependentRelationship::LessThan, src1Value, src2Value));
}
void GlobOpt::UpdateIntBoundsForGreaterThanBranch(Value *const src1Value, Value *const src2Value)
{
return UpdateIntBoundsForLessThanBranch(src2Value, src1Value);
}
void GlobOpt::UpdateIntBoundsForLessThanOrEqualBranch(Value *const src1Value, Value *const src2Value)
{
return UpdateIntBoundsForGreaterThanOrEqualBranch(src2Value, src1Value);
}
void GlobOpt::UpdateIntBoundsForLessThanBranch(Value *const src1Value, Value *const src2Value)
{
Assert(src1Value);
Assert(src2Value);
if(!DoPathDependentValues() || src1Value->GetValueNumber() == src2Value->GetValueNumber())
{
return;
}
#if DBG
if(!IsLoopPrePass() && DoAggressiveIntTypeSpec() && DoConstFold())
{
IntConstantBounds src1ConstantBounds, src2ConstantBounds;
AssertVerify(src1Value->GetValueInfo()->TryGetIntConstantBounds(&src1ConstantBounds, true));
AssertVerify(src2Value->GetValueInfo()->TryGetIntConstantBounds(&src2ConstantBounds, true));
Assert(
!ValueInfo::IsGreaterThanOrEqualTo(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
Assert(
!ValueInfo::IsLessThan(
src1Value,
src1ConstantBounds.LowerBound(),
src1ConstantBounds.UpperBound(),
src2Value,
src2ConstantBounds.LowerBound(),
src2ConstantBounds.UpperBound()));
}
#endif
SetPathDependentInfo(true, PathDependentInfo(PathDependentRelationship::LessThan, src1Value, src2Value));
SetPathDependentInfo(false, PathDependentInfo(PathDependentRelationship::GreaterThanOrEqual, src1Value, src2Value));
}
IntBounds *GlobOpt::GetIntBoundsToUpdate(
const ValueInfo *const valueInfo,
const IntConstantBounds &constantBounds,
const bool isSettingNewBound,
const bool isBoundConstant,
const bool isSettingUpperBound,
const bool isExplicit)
{
Assert(valueInfo);
Assert(valueInfo->IsLikelyInt());
if(!DoTrackRelativeIntBounds())
{
return nullptr;
}
if(valueInfo->IsIntBounded())
{
const IntBounds *const bounds = valueInfo->AsIntBounded()->Bounds();
if(bounds->RequiresIntBoundedValueInfo(valueInfo->Type()))
{
return bounds->Clone();
}
}
if(valueInfo->IsInt())
{
if(constantBounds.IsConstant())
{
// Don't start tracking relative bounds for int constant values, just retain existing relative bounds. Will use
// IntConstantValueInfo instead.
return nullptr;
}
if(isBoundConstant)
{
// There are no relative bounds to track
if(!(isSettingUpperBound && isExplicit))
{
// We are not setting a constant upper bound that is established explicitly, will use IntRangeValueInfo instead
return nullptr;
}
}
else if(!isSettingNewBound)
{
// New relative bounds are not being set, will use IntRangeValueInfo instead
return nullptr;
}
return IntBounds::New(constantBounds, false, alloc);
}
return nullptr;
}
ValueInfo *GlobOpt::UpdateIntBoundsForEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
Assert(value || constantBounds.IsConstant());
Assert(boundValue || boundConstantBounds.IsConstant());
if(!value)
{
return nullptr;
}
Assert(!boundValue || value->GetValueNumber() != boundValue->GetValueNumber());
ValueInfo *const valueInfo = value->GetValueInfo();
IntBounds *const bounds =
GetIntBoundsToUpdate(valueInfo, constantBounds, true, boundConstantBounds.IsConstant(), true, isExplicit);
if(bounds)
{
if(boundValue)
{
const ValueNumber valueNumber = value->GetValueNumber();
bounds->SetLowerBound(valueNumber, boundValue, isExplicit);
bounds->SetUpperBound(valueNumber, boundValue, isExplicit);
}
else
{
bounds->SetLowerBound(boundConstantBounds.LowerBound());
bounds->SetUpperBound(boundConstantBounds.LowerBound(), isExplicit);
}
if(bounds->RequiresIntBoundedValueInfo(valueInfo->Type()))
{
return NewIntBoundedValueInfo(valueInfo, bounds);
}
bounds->Delete();
}
if(!valueInfo->IsInt())
{
return nullptr;
}
const int32 newMin = max(constantBounds.LowerBound(), boundConstantBounds.LowerBound());
const int32 newMax = min(constantBounds.UpperBound(), boundConstantBounds.UpperBound());
return newMin <= newMax ? NewIntRangeValueInfo(valueInfo, newMin, newMax) : nullptr;
}
ValueInfo *GlobOpt::UpdateIntBoundsForNotEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
Assert(value || constantBounds.IsConstant());
Assert(boundValue || boundConstantBounds.IsConstant());
if(!value)
{
return nullptr;
}
Assert(!boundValue || value->GetValueNumber() != boundValue->GetValueNumber());
ValueInfo *const valueInfo = value->GetValueInfo();
IntBounds *const bounds =
GetIntBoundsToUpdate(
valueInfo,
constantBounds,
false,
boundConstantBounds.IsConstant(),
boundConstantBounds.IsConstant() && boundConstantBounds.LowerBound() == constantBounds.UpperBound(),
isExplicit);
if(bounds)
{
if(boundValue
? bounds->SetIsNot(boundValue, isExplicit)
: bounds->SetIsNot(boundConstantBounds.LowerBound(), isExplicit))
{
if(bounds->RequiresIntBoundedValueInfo(valueInfo->Type()))
{
return NewIntBoundedValueInfo(valueInfo, bounds);
}
}
else
{
bounds->Delete();
return nullptr;
}
bounds->Delete();
}
if(!valueInfo->IsInt() || !boundConstantBounds.IsConstant())
{
return nullptr;
}
const int32 constantBound = boundConstantBounds.LowerBound();
// The value is not equal to a constant, so narrow the range if the constant is equal to the value's lower or upper bound
int32 newMin = constantBounds.LowerBound(), newMax = constantBounds.UpperBound();
if(constantBound == newMin)
{
Assert(newMin <= newMax);
if(newMin == newMax)
{
return nullptr;
}
++newMin;
}
else if(constantBound == newMax)
{
Assert(newMin <= newMax);
if(newMin == newMax)
{
return nullptr;
}
--newMax;
}
else
{
return nullptr;
}
return NewIntRangeValueInfo(valueInfo, newMin, newMax);
}
ValueInfo *GlobOpt::UpdateIntBoundsForGreaterThanOrEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
return UpdateIntBoundsForGreaterThanOrEqual(value, constantBounds, boundValue, boundConstantBounds, 0, isExplicit);
}
ValueInfo *GlobOpt::UpdateIntBoundsForGreaterThanOrEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const int boundOffset,
const bool isExplicit)
{
Assert(value || constantBounds.IsConstant());
Assert(boundValue || boundConstantBounds.IsConstant());
if(!value)
{
return nullptr;
}
Assert(!boundValue || value->GetValueNumber() != boundValue->GetValueNumber());
ValueInfo *const valueInfo = value->GetValueInfo();
IntBounds *const bounds =
GetIntBoundsToUpdate(valueInfo, constantBounds, true, boundConstantBounds.IsConstant(), false, isExplicit);
if(bounds)
{
if(boundValue)
{
bounds->SetLowerBound(value->GetValueNumber(), boundValue, boundOffset, isExplicit);
}
else
{
bounds->SetLowerBound(boundConstantBounds.LowerBound(), boundOffset);
}
if(bounds->RequiresIntBoundedValueInfo(valueInfo->Type()))
{
return NewIntBoundedValueInfo(valueInfo, bounds);
}
bounds->Delete();
}
if(!valueInfo->IsInt())
{
return nullptr;
}
int32 adjustedBoundMin;
if(boundOffset == 0)
{
adjustedBoundMin = boundConstantBounds.LowerBound();
}
else if(boundOffset == 1)
{
if(boundConstantBounds.LowerBound() + 1 <= boundConstantBounds.LowerBound())
{
return nullptr;
}
adjustedBoundMin = boundConstantBounds.LowerBound() + 1;
}
else if(Int32Math::Add(boundConstantBounds.LowerBound(), boundOffset, &adjustedBoundMin))
{
return nullptr;
}
const int32 newMin = max(constantBounds.LowerBound(), adjustedBoundMin);
return
newMin <= constantBounds.UpperBound()
? NewIntRangeValueInfo(valueInfo, newMin, constantBounds.UpperBound())
: nullptr;
}
ValueInfo *GlobOpt::UpdateIntBoundsForGreaterThan(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
return UpdateIntBoundsForGreaterThanOrEqual(value, constantBounds, boundValue, boundConstantBounds, 1, isExplicit);
}
ValueInfo *GlobOpt::UpdateIntBoundsForLessThanOrEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
return UpdateIntBoundsForLessThanOrEqual(value, constantBounds, boundValue, boundConstantBounds, 0, isExplicit);
}
ValueInfo *GlobOpt::UpdateIntBoundsForLessThanOrEqual(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const int boundOffset,
const bool isExplicit)
{
Assert(value || constantBounds.IsConstant());
Assert(boundValue || boundConstantBounds.IsConstant());
if(!value)
{
return nullptr;
}
Assert(!boundValue || value->GetValueNumber() != boundValue->GetValueNumber());
ValueInfo *const valueInfo = value->GetValueInfo();
IntBounds *const bounds =
GetIntBoundsToUpdate(valueInfo, constantBounds, true, boundConstantBounds.IsConstant(), true, isExplicit);
if(bounds)
{
if(boundValue)
{
bounds->SetUpperBound(value->GetValueNumber(), boundValue, boundOffset, isExplicit);
}
else
{
bounds->SetUpperBound(boundConstantBounds.LowerBound(), boundOffset, isExplicit);
}
if(bounds->RequiresIntBoundedValueInfo(valueInfo->Type()))
{
return NewIntBoundedValueInfo(valueInfo, bounds);
}
bounds->Delete();
}
if(!valueInfo->IsInt())
{
return nullptr;
}
int32 adjustedBoundMax;
if(boundOffset == 0)
{
adjustedBoundMax = boundConstantBounds.UpperBound();
}
else if(boundOffset == -1)
{
if(boundConstantBounds.UpperBound() - 1 >= boundConstantBounds.UpperBound())
{
return nullptr;
}
adjustedBoundMax = boundConstantBounds.UpperBound() - 1;
}
else if(Int32Math::Add(boundConstantBounds.UpperBound(), boundOffset, &adjustedBoundMax))
{
return nullptr;
}
const int32 newMax = min(constantBounds.UpperBound(), adjustedBoundMax);
return
newMax >= constantBounds.LowerBound()
? NewIntRangeValueInfo(valueInfo, constantBounds.LowerBound(), newMax)
: nullptr;
}
ValueInfo *GlobOpt::UpdateIntBoundsForLessThan(
Value *const value,
const IntConstantBounds &constantBounds,
Value *const boundValue,
const IntConstantBounds &boundConstantBounds,
const bool isExplicit)
{
return UpdateIntBoundsForLessThanOrEqual(value, constantBounds, boundValue, boundConstantBounds, -1, isExplicit);
}
void GlobOpt::TrackIntSpecializedAddSubConstant(
IR::Instr *const instr,
const AddSubConstantInfo *const addSubConstantInfo,
Value *const dstValue,
const bool updateSourceBounds)
{
Assert(instr);
Assert(dstValue);
if(addSubConstantInfo)
{
Assert(addSubConstantInfo->HasInfo());
Assert(!ignoredIntOverflowForCurrentInstr);
do
{
if(!IsLoopPrePass() || !DoBoundCheckHoist())
{
break;
}
Assert(
instr->m_opcode == Js::OpCode::Incr_A ||
instr->m_opcode == Js::OpCode::Decr_A ||
instr->m_opcode == Js::OpCode::Add_A ||
instr->m_opcode == Js::OpCode::Sub_A);
StackSym *sym = instr->GetDst()->AsRegOpnd()->m_sym;
bool isPostfixIncDecPattern = false;
if(addSubConstantInfo->SrcSym() != sym)
{
// Check for the following patterns.
//
// This pattern is used for postfix inc/dec operators:
// s2 = Conv_Num s1
// s1 = Inc s2
//
// This pattern is used for prefix inc/dec operators:
// s2 = Inc s1
// s1 = Ld s2
IR::Instr *const prevInstr = instr->m_prev;
Assert(prevInstr);
if(prevInstr->m_opcode == Js::OpCode::Conv_Num &&
prevInstr->GetSrc1()->IsRegOpnd() &&
prevInstr->GetSrc1()->AsRegOpnd()->m_sym == sym &&
prevInstr->GetDst()->AsRegOpnd()->m_sym == addSubConstantInfo->SrcSym())
{
// s2 will get a new value number, since Conv_Num cannot transfer in the prepass. For the purposes of
// induction variable tracking however, it doesn't matter, so record this case and use s1's value in the
// current block.
isPostfixIncDecPattern = true;
}
else
{
IR::Instr *const nextInstr = instr->m_next;
Assert(nextInstr);
if(nextInstr->m_opcode != Js::OpCode::Ld_A ||
!nextInstr->GetSrc1()->IsRegOpnd() ||
nextInstr->GetSrc1()->AsRegOpnd()->m_sym != sym)
{
break;
}
sym = addSubConstantInfo->SrcSym();
if(nextInstr->GetDst()->AsRegOpnd()->m_sym != sym)
{
break;
}
// In the prefix inc/dec pattern, the result of Ld currently gets a new value number, which will cause the
// induction variable info to become indeterminate. Indicate that the value number should be updated in the
// induction variable info.
// Consider: Remove this once loop prepass value transfer scheme is fixed
updateInductionVariableValueNumber = true;
}
}
// Track induction variable info
ValueNumber srcValueNumber;
if(isPostfixIncDecPattern)
{
Value *const value = this->currentBlock->globOptData.FindValue(sym);
Assert(value);
srcValueNumber = value->GetValueNumber();
}
else
{
srcValueNumber = addSubConstantInfo->SrcValue()->GetValueNumber();
}
InductionVariableSet *const inductionVariables = currentBlock->globOptData.inductionVariables;
Assert(inductionVariables);
InductionVariable *inductionVariable;
if(!inductionVariables->TryGetReference(sym->m_id, &inductionVariable))
{
// Only track changes in the current loop's prepass. In subsequent prepasses, the info is only being propagated
// for use by the parent loop, so changes in the current loop have already been tracked.
if(prePassLoop != currentBlock->loop)
{
updateInductionVariableValueNumber = false;
break;
}
// Ensure that the sym is live in the landing pad, and that its value has not changed in an unknown way yet
Value *const landingPadValue = currentBlock->loop->landingPad->globOptData.FindValue(sym);
if(!landingPadValue || srcValueNumber != landingPadValue->GetValueNumber())
{
updateInductionVariableValueNumber = false;
break;
}
inductionVariables->Add(
InductionVariable(sym, dstValue->GetValueNumber(), addSubConstantInfo->Offset()));
break;
}
if(!inductionVariable->IsChangeDeterminate())
{
updateInductionVariableValueNumber = false;
break;
}
if(srcValueNumber != inductionVariable->SymValueNumber())
{
// The sym's value has changed since the last time induction variable info was recorded for it. Due to the
// unknown change, mark the info as indeterminate.
inductionVariable->SetChangeIsIndeterminate();
updateInductionVariableValueNumber = false;
break;
}
// Only track changes in the current loop's prepass. In subsequent prepasses, the info is only being propagated for
// use by the parent loop, so changes in the current loop have already been tracked. Induction variable value
// numbers are updated as changes occur, but their change bounds are preserved from the first prepass over the loop.
inductionVariable->SetSymValueNumber(dstValue->GetValueNumber());
if(prePassLoop != currentBlock->loop)
{
break;
}
if(!inductionVariable->Add(addSubConstantInfo->Offset()))
{
updateInductionVariableValueNumber = false;
}
} while(false);
if(!this->IsLoopPrePass() && updateSourceBounds && addSubConstantInfo->Offset() != IntConstMin)
{
// Track bounds for add or sub with a constant. For instance, consider (b = a + 2). The value of 'b' should track
// that it is equal to (the value of 'a') + 2. That part has been done above. Similarly, the value of 'a' should
// also track that it is equal to (the value of 'b') - 2.
Value *const value = addSubConstantInfo->SrcValue();
const ValueInfo *const valueInfo = value->GetValueInfo();
Assert(valueInfo->IsInt());
IntConstantBounds constantBounds;
AssertVerify(valueInfo->TryGetIntConstantBounds(&constantBounds));
IntBounds *const bounds =
GetIntBoundsToUpdate(
valueInfo,
constantBounds,
true,
dstValue->GetValueInfo()->HasIntConstantValue(),
true,
true);
if(bounds)
{
const ValueNumber valueNumber = value->GetValueNumber();
const int32 dstOffset = -addSubConstantInfo->Offset();
bounds->SetLowerBound(valueNumber, dstValue, dstOffset, true);
bounds->SetUpperBound(valueNumber, dstValue, dstOffset, true);
ChangeValueInfo(nullptr, value, NewIntBoundedValueInfo(valueInfo, bounds));
}
}
return;
}
if(!updateInductionVariableValueNumber)
{
return;
}
// See comment above where this is set to true
// Consider: Remove this once loop prepass value transfer scheme is fixed
updateInductionVariableValueNumber = false;
Assert(IsLoopPrePass());
Assert(instr->m_opcode == Js::OpCode::Ld_A);
Assert(
instr->m_prev->m_opcode == Js::OpCode::Incr_A ||
instr->m_prev->m_opcode == Js::OpCode::Decr_A ||
instr->m_prev->m_opcode == Js::OpCode::Add_A ||
instr->m_prev->m_opcode == Js::OpCode::Sub_A);
Assert(instr->m_prev->GetDst()->AsRegOpnd()->m_sym == instr->GetSrc1()->AsRegOpnd()->m_sym);
InductionVariable *inductionVariable;
AssertVerify(currentBlock->globOptData.inductionVariables->TryGetReference(instr->GetDst()->AsRegOpnd()->m_sym->m_id, &inductionVariable));
inductionVariable->SetSymValueNumber(dstValue->GetValueNumber());
}
void GlobOpt::CloneBoundCheckHoistBlockData(
BasicBlock *const toBlock,
GlobOptBlockData *const toData,
BasicBlock *const fromBlock,
GlobOptBlockData *const fromData)
{
Assert(DoBoundCheckHoist());
Assert(toBlock);
Assert(toData);
//Assert(toData == &toBlock->globOptData || toData == &currentBlock->globOptData);
Assert(fromBlock);
Assert(fromData);
Assert(fromData == &fromBlock->globOptData);
Assert(fromData->availableIntBoundChecks);
toData->availableIntBoundChecks = fromData->availableIntBoundChecks->Clone();
if(toBlock->isLoopHeader)
{
Assert(fromBlock == toBlock->loop->landingPad);
if(prePassLoop == toBlock->loop)
{
// When the current prepass loop is the current loop, the loop header's induction variable set needs to start off
// empty to track changes in the current loop
toData->inductionVariables = JitAnew(alloc, InductionVariableSet, alloc);
return;
}
if(!IsLoopPrePass())
{
return;
}
// After the prepass on this loop, if we're still in a prepass, this must be an inner loop. Merge the landing pad info
// for use by the parent loop.
Assert(fromBlock->loop);
Assert(fromData->inductionVariables);
toData->inductionVariables = fromData->inductionVariables->Clone();
return;
}
if(!toBlock->loop || !IsLoopPrePass())
{
return;
}
Assert(fromBlock->loop);
Assert(toBlock->loop->IsDescendentOrSelf(fromBlock->loop));
Assert(fromData->inductionVariables);
toData->inductionVariables = fromData->inductionVariables->Clone();
}
void GlobOpt::MergeBoundCheckHoistBlockData(
BasicBlock *const toBlock,
GlobOptBlockData *const toData,
BasicBlock *const fromBlock,
GlobOptBlockData *const fromData)
{
Assert(DoBoundCheckHoist());
Assert(toBlock);
Assert(toData);
//Assert(toData == &toBlock->globOptData || toData == &currentBlock->globOptData);
Assert(fromBlock);
Assert(fromData);
Assert(fromData == &fromBlock->globOptData);
Assert(toData->availableIntBoundChecks);
for(auto it = toData->availableIntBoundChecks->GetIteratorWithRemovalSupport(); it.IsValid(); it.MoveNext())
{
const IntBoundCheck &toDataIntBoundCheck = it.CurrentValue();
const IntBoundCheck *fromDataIntBoundCheck;
if(!fromData->availableIntBoundChecks->TryGetReference(
toDataIntBoundCheck.CompatibilityId(),
&fromDataIntBoundCheck) ||
fromDataIntBoundCheck->Instr() != toDataIntBoundCheck.Instr())
{
it.RemoveCurrent();
}
}
InductionVariableSet *mergeInductionVariablesInto;
if(toBlock->isLoopHeader)
{
Assert(fromBlock->loop == toBlock->loop); // The flow is such that you cannot have back-edges from an inner loop
if(IsLoopPrePass())
{
// Collect info for the parent loop. Any changes to induction variables in this inner loop need to be expanded in
// the same direction for the parent loop, so merge expanded info from back-edges. Info about induction variables
// that changed before the loop but not inside the loop, can be kept intact because the landing pad dominates the
// loop.
Assert(prePassLoop != toBlock->loop);
Assert(fromData->inductionVariables);
Assert(toData->inductionVariables);
InductionVariableSet *const mergedInductionVariables = toData->inductionVariables;
for(auto it = fromData->inductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable backEdgeInductionVariable = it.CurrentValue();
backEdgeInductionVariable.ExpandInnerLoopChange();
StackSym *const sym = backEdgeInductionVariable.Sym();
InductionVariable *mergedInductionVariable;
if(mergedInductionVariables->TryGetReference(sym->m_id, &mergedInductionVariable))
{
mergedInductionVariable->Merge(backEdgeInductionVariable);
continue;
}
// Ensure that the sym is live in the parent loop's landing pad, and that its value has not changed in an
// unknown way between the parent loop's landing pad and the current loop's landing pad.
Value *const parentLandingPadValue = currentBlock->loop->parent->landingPad->globOptData.FindValue(sym);
if(!parentLandingPadValue)
{
continue;
}
Value *const landingPadValue = currentBlock->loop->landingPad->globOptData.FindValue(sym);
Assert(landingPadValue);
if(landingPadValue->GetValueNumber() == parentLandingPadValue->GetValueNumber())
{
mergedInductionVariables->Add(backEdgeInductionVariable);
}
}
const InductionVariableSet *const fromDataInductionVariables = fromData->inductionVariables;
for (auto it = mergedInductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable &mergedInductionVariable = it.CurrentValueReference();
if (!mergedInductionVariable.IsChangeDeterminate())
{
continue;
}
StackSym *const sym = mergedInductionVariable.Sym();
const InductionVariable *fromDataInductionVariable;
if (fromDataInductionVariables->TryGetReference(sym->m_id, &fromDataInductionVariable))
{
continue;
}
// Process the set of symbols that are induction variables due to prior loops that share the same parent loop, but are not induction variables in the current loop
// If the current loop is initializing such carried over induction variables, then their value numbers will differ from the current loop's landing pad
// Such induction variables should be marked as indeterminate going forward, such the induction variable analysis accurately flows to the parent loop.
Value *const fromDataValue = fromData->FindValue(sym);
if (fromDataValue)
{
Value *const landingPadValue = toBlock->loop->landingPad->globOptData.FindValue(sym);
if (landingPadValue && fromDataValue->GetValueNumber() != landingPadValue->GetValueNumber())
{
mergedInductionVariable.SetChangeIsIndeterminate();
}
}
}
return;
}
// Collect info for the current loop. We want to merge only the back-edge info without the landing pad info, such that
// the loop's induction variable set reflects changes made inside this loop.
Assert(fromData->inductionVariables);
InductionVariableSet *&loopInductionVariables = toBlock->loop->inductionVariables;
if(!loopInductionVariables)
{
loopInductionVariables = fromData->inductionVariables->Clone();
return;
}
mergeInductionVariablesInto = loopInductionVariables;
}
else if(toBlock->loop && IsLoopPrePass())
{
Assert(fromBlock->loop);
Assert(toBlock->loop->IsDescendentOrSelf(fromBlock->loop));
mergeInductionVariablesInto = toData->inductionVariables;
}
else
{
return;
}
const InductionVariableSet *const fromDataInductionVariables = fromData->inductionVariables;
InductionVariableSet *const mergedInductionVariables = mergeInductionVariablesInto;
Assert(fromDataInductionVariables);
Assert(mergedInductionVariables);
for(auto it = mergedInductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable &mergedInductionVariable = it.CurrentValueReference();
if(!mergedInductionVariable.IsChangeDeterminate())
{
continue;
}
StackSym *const sym = mergedInductionVariable.Sym();
const InductionVariable *fromDataInductionVariable;
if(fromDataInductionVariables->TryGetReference(sym->m_id, &fromDataInductionVariable))
{
mergedInductionVariable.Merge(*fromDataInductionVariable);
continue;
}
// Ensure that the sym is live in the landing pad, and that its value has not changed in an unknown way yet on the path
// where the sym is not already marked as an induction variable.
Value *const fromDataValue = fromData->FindValue(sym);
if(fromDataValue)
{
Value *const landingPadValue = toBlock->loop->landingPad->globOptData.FindValue(sym);
if(landingPadValue && fromDataValue->GetValueNumber() == landingPadValue->GetValueNumber())
{
mergedInductionVariable.Merge(InductionVariable(sym, ZeroValueNumber, 0));
continue;
}
}
mergedInductionVariable.SetChangeIsIndeterminate();
}
for(auto it = fromDataInductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
const InductionVariable &fromDataInductionVariable = it.CurrentValue();
StackSym *const sym = fromDataInductionVariable.Sym();
if(mergedInductionVariables->ContainsKey(sym->m_id))
{
continue;
}
// Ensure that the sym is live in the landing pad, and that its value has not changed in an unknown way yet on the path
// where the sym is not already marked as an induction variable.
bool indeterminate = true;
Value *const toDataValue = toData->FindValue(sym);
if(toDataValue)
{
Value *const landingPadValue = toBlock->loop->landingPad->globOptData.FindValue(sym);
if(landingPadValue && toDataValue->GetValueNumber() == landingPadValue->GetValueNumber())
{
indeterminate = false;
}
}
InductionVariable mergedInductionVariable(sym, ZeroValueNumber, 0);
if(indeterminate)
{
mergedInductionVariable.SetChangeIsIndeterminate();
}
else
{
mergedInductionVariable.Merge(fromDataInductionVariable);
}
mergedInductionVariables->Add(mergedInductionVariable);
}
}
void GlobOpt::DetectUnknownChangesToInductionVariables(GlobOptBlockData *const blockData)
{
Assert(DoBoundCheckHoist());
Assert(IsLoopPrePass());
Assert(blockData);
Assert(blockData->inductionVariables);
// Check induction variable value numbers, and mark those that changed in an unknown way as indeterminate. They must remain
// in the set though, for merging purposes.
for(auto it = blockData->inductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable &inductionVariable = it.CurrentValueReference();
if(!inductionVariable.IsChangeDeterminate())
{
continue;
}
Value *const value = blockData->FindValue(inductionVariable.Sym());
if(!value || value->GetValueNumber() != inductionVariable.SymValueNumber())
{
inductionVariable.SetChangeIsIndeterminate();
}
}
}
void GlobOpt::SetInductionVariableValueNumbers(GlobOptBlockData *const blockData)
{
Assert(DoBoundCheckHoist());
Assert(IsLoopPrePass());
//Assert(blockData == &this->currentBlock->globOptData);
Assert(blockData->inductionVariables);
// Now that all values have been merged, update value numbers in the induction variable info.
for(auto it = blockData->inductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable &inductionVariable = it.CurrentValueReference();
if(!inductionVariable.IsChangeDeterminate())
{
continue;
}
Value *const value = blockData->FindValue(inductionVariable.Sym());
if(value)
{
inductionVariable.SetSymValueNumber(value->GetValueNumber());
}
else
{
inductionVariable.SetChangeIsIndeterminate();
}
}
}
void GlobOpt::FinalizeInductionVariables(Loop *const loop, GlobOptBlockData *const headerData)
{
Assert(DoBoundCheckHoist());
Assert(!IsLoopPrePass());
Assert(loop);
Assert(loop->GetHeadBlock() == currentBlock);
Assert(loop->inductionVariables);
Assert(currentBlock->isLoopHeader);
//Assert(headerData == &this->currentBlock->globOptData);
// Clean up induction variables and for each, install a relationship between its values inside and outside the loop.
GlobOptBlockData &landingPadBlockData = loop->landingPad->globOptData;
for(auto it = loop->inductionVariables->GetIterator(); it.IsValid(); it.MoveNext())
{
InductionVariable &inductionVariable = it.CurrentValueReference();
if(!inductionVariable.IsChangeDeterminate())
{
continue;
}
if(!inductionVariable.IsChangeUnidirectional())
{
inductionVariable.SetChangeIsIndeterminate();
continue;
}
StackSym *const sym = inductionVariable.Sym();
if(!headerData->IsInt32TypeSpecialized(sym))
{
inductionVariable.SetChangeIsIndeterminate();
continue;
}
Assert(landingPadBlockData.IsInt32TypeSpecialized(sym));
Value *const value = headerData->FindValue(sym);
if(!value)
{
inductionVariable.SetChangeIsIndeterminate();
continue;
}
Value *const landingPadValue = landingPadBlockData.FindValue(sym);
Assert(landingPadValue);
IntConstantBounds constantBounds, landingPadConstantBounds;
AssertVerify(value->GetValueInfo()->TryGetIntConstantBounds(&constantBounds));
AssertVerify(landingPadValue->GetValueInfo()->TryGetIntConstantBounds(&landingPadConstantBounds, true));
// For an induction variable i, update the value of i inside the loop to indicate that it is bounded by the value of i
// just before the loop.
if(inductionVariable.ChangeBounds().LowerBound() >= 0)
{
ValueInfo *const newValueInfo =
UpdateIntBoundsForGreaterThanOrEqual(value, constantBounds, landingPadValue, landingPadConstantBounds, true);
ChangeValueInfo(nullptr, value, newValueInfo);
if(inductionVariable.ChangeBounds().UpperBound() == 0)
{
AssertVerify(newValueInfo->TryGetIntConstantBounds(&constantBounds, true));
}
}
if(inductionVariable.ChangeBounds().UpperBound() <= 0)
{
ValueInfo *const newValueInfo =
UpdateIntBoundsForLessThanOrEqual(value, constantBounds, landingPadValue, landingPadConstantBounds, true);
ChangeValueInfo(nullptr, value, newValueInfo);
}
}
}
void
GlobOpt::InvalidateInductionVariables(IR::Instr * instr)
{
Assert(instr->GetDst() != nullptr && instr->GetDst()->IsRegOpnd());
// Induction variables are always var syms.
StackSym * dstSym = instr->GetDst()->AsRegOpnd()->m_sym;
if (!dstSym->IsVar())
{
dstSym = dstSym->GetVarEquivSym(this->func);
}
// If this is an induction variable, then treat it the way the prepass would have if it had seen
// the assignment and the resulting change to the value number, and mark it as indeterminate.
for (Loop * loop = this->currentBlock->loop; loop; loop = loop->parent)
{
InductionVariable *iv = nullptr;
if (loop->inductionVariables && loop->inductionVariables->TryGetReference(dstSym->m_id, &iv))
{
iv->SetChangeIsIndeterminate();
}
}
}
GlobOpt::SymBoundType GlobOpt::DetermineSymBoundOffsetOrValueRelativeToLandingPad(
StackSym *const sym,
const bool landingPadValueIsLowerBound,
ValueInfo *const valueInfo,
const IntBounds *const bounds,
GlobOptBlockData *const landingPadGlobOptBlockData,
int *const boundOffsetOrValueRef)
{
Assert(sym);
Assert(!sym->IsTypeSpec());
Assert(valueInfo);
Assert(landingPadGlobOptBlockData);
Assert(boundOffsetOrValueRef);
Assert(valueInfo->IsInt());
int constantValue;
if(valueInfo->TryGetIntConstantValue(&constantValue))
{
// The sym's constant value is the constant bound value, so just return that. This is possible in loops such as
// for(; i === 1; ++i){...}, where 'i' is an induction variable but has a constant value inside the loop, or in blocks
// inside the loop such as if(i === 1){...}
*boundOffsetOrValueRef = constantValue;
return SymBoundType::VALUE;
}
if (bounds)
{
Value *const landingPadValue = landingPadGlobOptBlockData->FindValue(sym);
Assert(landingPadValue);
Assert(landingPadValue->GetValueInfo()->IsInt());
int landingPadConstantValue;
const ValueRelativeOffset* bound = nullptr;
const RelativeIntBoundSet& boundSet = landingPadValueIsLowerBound ? bounds->RelativeLowerBounds() : bounds->RelativeUpperBounds();
if (landingPadValue->GetValueInfo()->TryGetIntConstantValue(&landingPadConstantValue))
{
// The sym's bound already takes the landing pad constant value into consideration, unless the landing pad value was
// updated to have a more aggressive range (and hence, now a constant value) as part of hoisting a bound check or some
// other hoisting operation. The sym's bound also takes into consideration the change to the sym so far inside the loop,
// and the landing pad constant value does not, so use the sym's bound by default.
int constantBound = landingPadValueIsLowerBound ? bounds->ConstantLowerBound() : bounds->ConstantUpperBound();
if(landingPadValueIsLowerBound ? landingPadConstantValue > constantBound : landingPadConstantValue < constantBound)
{
// The landing pad value became a constant value as part of a hoisting operation. The landing pad constant value is
// a more aggressive bound, so use that instead, and take into consideration the change to the sym so far inside the
// loop, using the relative bound to the landing pad value.
if (!boundSet.TryGetReference(landingPadValue->GetValueNumber(), &bound))
{
return SymBoundType::UNKNOWN;
}
constantBound = landingPadConstantValue + bound->Offset();
}
*boundOffsetOrValueRef = constantBound;
return SymBoundType::VALUE;
}
if (!boundSet.TryGetReference(landingPadValue->GetValueNumber(), &bound))
{
return SymBoundType::UNKNOWN;
}
*boundOffsetOrValueRef = bound->Offset();
return SymBoundType::OFFSET;
}
AssertVerify(
landingPadValueIsLowerBound
? valueInfo->TryGetIntConstantLowerBound(boundOffsetOrValueRef)
: valueInfo->TryGetIntConstantUpperBound(boundOffsetOrValueRef));
return SymBoundType::VALUE;
}
void GlobOpt::DetermineDominatingLoopCountableBlock(Loop *const loop, BasicBlock *const headerBlock)
{
Assert(DoLoopCountBasedBoundCheckHoist());
Assert(!IsLoopPrePass());
Assert(loop);
Assert(headerBlock);
Assert(headerBlock->isLoopHeader);
Assert(headerBlock->loop == loop);
// Determine if the loop header has a unique successor that is inside the loop. If so, then all other paths out of the loop
// header exit the loop, allowing a loop count to be established and used from the unique in-loop successor block.
Assert(!loop->dominatingLoopCountableBlock);
FOREACH_SUCCESSOR_BLOCK(successor, headerBlock)
{
if(successor->loop != loop)
{
Assert(!successor->loop || successor->loop->IsDescendentOrSelf(loop->parent));
continue;
}
if(loop->dominatingLoopCountableBlock)
{
// Found a second successor inside the loop
loop->dominatingLoopCountableBlock = nullptr;
break;
}
loop->dominatingLoopCountableBlock = successor;
} NEXT_SUCCESSOR_BLOCK;
}
void GlobOpt::DetermineLoopCount(Loop *const loop)
{
Assert(DoLoopCountBasedBoundCheckHoist());
Assert(loop);
GlobOptBlockData &landingPadBlockData = loop->landingPad->globOptData;
const InductionVariableSet *const inductionVariables = loop->inductionVariables;
Assert(inductionVariables);
for(auto inductionVariablesIterator = inductionVariables->GetIterator(); inductionVariablesIterator.IsValid(); inductionVariablesIterator.MoveNext())
{
InductionVariable &inductionVariable = inductionVariablesIterator.CurrentValueReference();
if(!inductionVariable.IsChangeDeterminate())
{
continue;
}
// Determine the minimum-magnitude change per iteration, and verify that the change is nonzero and finite
Assert(inductionVariable.IsChangeUnidirectional());
int minMagnitudeChange = inductionVariable.ChangeBounds().LowerBound();
if(minMagnitudeChange >= 0)
{
if(minMagnitudeChange == 0 || minMagnitudeChange == IntConstMax)
{
continue;
}
}
else
{
minMagnitudeChange = inductionVariable.ChangeBounds().UpperBound();
Assert(minMagnitudeChange <= 0);
if(minMagnitudeChange == 0 || minMagnitudeChange == IntConstMin)
{
continue;
}
}
StackSym *const inductionVariableVarSym = inductionVariable.Sym();
if(!this->currentBlock->globOptData.IsInt32TypeSpecialized(inductionVariableVarSym))
{
inductionVariable.SetChangeIsIndeterminate();
continue;
}
Assert(landingPadBlockData.IsInt32TypeSpecialized(inductionVariableVarSym));
Value *const inductionVariableValue = this->currentBlock->globOptData.FindValue(inductionVariableVarSym);
if(!inductionVariableValue)
{
inductionVariable.SetChangeIsIndeterminate();
continue;
}
ValueInfo *const inductionVariableValueInfo = inductionVariableValue->GetValueInfo();
const IntBounds *const inductionVariableBounds =
inductionVariableValueInfo->IsIntBounded() ? inductionVariableValueInfo->AsIntBounded()->Bounds() : nullptr;
// Look for an invariant bound in the direction of change
StackSym *boundBaseVarSym = nullptr;
int boundOffset = 0;
{
bool foundBound = false;
if(inductionVariableBounds)
{
// Look for a relative bound
for(auto it =
(
minMagnitudeChange >= 0
? inductionVariableBounds->RelativeUpperBounds()
: inductionVariableBounds->RelativeLowerBounds()
).GetIterator();
it.IsValid();
it.MoveNext())
{
const ValueRelativeOffset &bound = it.CurrentValue();
StackSym *currentBoundBaseVarSym = bound.BaseSym();
if(!currentBoundBaseVarSym || !landingPadBlockData.IsInt32TypeSpecialized(currentBoundBaseVarSym))
{
continue;
}
Value *const boundBaseValue = this->currentBlock->globOptData.FindValue(currentBoundBaseVarSym);
const ValueNumber boundBaseValueNumber = bound.BaseValueNumber();
if(!boundBaseValue || boundBaseValue->GetValueNumber() != boundBaseValueNumber)
{
continue;
}
Value *const landingPadBoundBaseValue = landingPadBlockData.FindValue(currentBoundBaseVarSym);
if(!landingPadBoundBaseValue || landingPadBoundBaseValue->GetValueNumber() != boundBaseValueNumber)
{
continue;
}
if (foundBound)
{
// We used to pick the first usable bound we saw in this list, but the list contains both
// the loop counter's bound *and* relative bounds of the primary bound. These secondary bounds
// are not guaranteed to be correct, so if the bound we found on a previous iteration is itself
// a bound for the current bound, then choose the current bound.
if (!boundBaseValue->GetValueInfo()->IsIntBounded())
{
continue;
}
// currentBoundBaseVarSym has relative bounds of its own. If we find the saved boundBaseVarSym
// in currentBoundBaseVarSym's relative bounds list, let currentBoundBaseVarSym be the
// chosen bound.
const IntBounds *const currentBounds = boundBaseValue->GetValueInfo()->AsIntBounded()->Bounds();
bool foundSecondaryBound = false;
for (auto it2 =
(
minMagnitudeChange >= 0
? currentBounds->RelativeUpperBounds()
: currentBounds->RelativeLowerBounds()
).GetIterator();
it2.IsValid();
it2.MoveNext())
{
const ValueRelativeOffset &bound2 = it2.CurrentValue();
if (bound2.BaseSym() == boundBaseVarSym)
{
// boundBaseVarSym is a secondary bound. Use currentBoundBaseVarSym instead.
foundSecondaryBound = true;
break;
}
}
if (!foundSecondaryBound)
{
// boundBaseVarSym is not a relative bound of currentBoundBaseVarSym, so continue
// to use boundBaseVarSym.
continue;
}
}
boundBaseVarSym = bound.BaseSym();
boundOffset = bound.Offset();
foundBound = true;
}
}
if(!foundBound)
{
// No useful relative bound found; look for a constant bound. Exclude large constant bounds established implicitly by
// <, <=, >, and >=. For example, for a loop condition (i < n), if 'n' is not invariant and hence can't be used,
// 'i' will still have a constant upper bound of (int32 max - 1) that should be excluded as it's too large. Any
// other constant bounds must have been established explicitly by the loop condition, and are safe to use.
boundBaseVarSym = nullptr;
if(minMagnitudeChange >= 0)
{
if(inductionVariableBounds)
{
boundOffset = inductionVariableBounds->ConstantUpperBound();
}
else
{
AssertVerify(inductionVariableValueInfo->TryGetIntConstantUpperBound(&boundOffset));
}
if(boundOffset >= IntConstMax - 1)
{
continue;
}
}
else
{
if(inductionVariableBounds)
{
boundOffset = inductionVariableBounds->ConstantLowerBound();
}
else
{
AssertVerify(inductionVariableValueInfo->TryGetIntConstantLowerBound(&boundOffset));
}
if(boundOffset <= IntConstMin + 1)
{
continue;
}
}
}
}
// Determine if the induction variable already changed in the loop, and by how much
int inductionVariableOffset = 0;
StackSym *inductionVariableSymToAdd = nullptr;
SymBoundType symBoundType = DetermineSymBoundOffsetOrValueRelativeToLandingPad(
inductionVariableVarSym,
minMagnitudeChange >= 0,
inductionVariableValueInfo,
inductionVariableBounds,
&landingPadBlockData,
&inductionVariableOffset);
if (symBoundType == SymBoundType::VALUE)
{
// The bound value is constant
inductionVariableSymToAdd = nullptr;
}
else if (symBoundType == SymBoundType::OFFSET)
{
// The bound value is not constant, the offset needs to be added to the induction variable in the landing pad
inductionVariableSymToAdd = inductionVariableVarSym->GetInt32EquivSym(nullptr);
Assert(inductionVariableSymToAdd);
}
else
{
Assert(symBoundType == SymBoundType::UNKNOWN);
// We were unable to determine the sym bound offset or value
continue;
}
// Int operands are required
StackSym *boundBaseSym;
if(boundBaseVarSym)
{
boundBaseSym = boundBaseVarSym->IsVar() ? boundBaseVarSym->GetInt32EquivSym(nullptr) : boundBaseVarSym;
Assert(boundBaseSym);
Assert(boundBaseSym->GetType() == TyInt32 || boundBaseSym->GetType() == TyUint32);
}
else
{
boundBaseSym = nullptr;
}
// The loop count is computed as follows. We're actually computing the loop count minus one, because the value is used
// to determine the bound of a secondary induction variable in its direction of change, and at that point the secondary
// induction variable's information already accounts for changes in the first loop iteration.
//
// If the induction variable increases in the loop:
// loopCountMinusOne = (upperBound - inductionVariable) / abs(minMagnitudeChange)
// Or more precisely:
// loopCountMinusOne =
// ((boundBase - inductionVariable) + (boundOffset - inductionVariableOffset)) / abs(minMagnitudeChange)
//
// If the induction variable decreases in the loop, the subtract operands are just reversed to yield a nonnegative
// number, and the rest is similar. The two offsets are also constant and can be folded. So in general:
// loopCountMinusOne = (left - right + offset) / abs(minMagnitudeChange)
// Determine the left and right information
StackSym *leftSym, *rightSym;
int leftOffset, rightOffset;
if(minMagnitudeChange >= 0)
{
leftSym = boundBaseSym;
leftOffset = boundOffset;
rightSym = inductionVariableSymToAdd;
rightOffset = inductionVariableOffset;
}
else
{
minMagnitudeChange = -minMagnitudeChange;
leftSym = inductionVariableSymToAdd;
leftOffset = inductionVariableOffset;
rightSym = boundBaseSym;
rightOffset = boundOffset;
}
// Determine the combined offset, and save the info necessary to generate the loop count
int offset;
if(Int32Math::Sub(leftOffset, rightOffset, &offset))
{
continue;
}
void *const loopCountBuffer = JitAnewArray(this->func->GetTopFunc()->m_fg->alloc, byte, sizeof(LoopCount));
if(!rightSym)
{
if(!leftSym)
{
loop->loopCount = new(loopCountBuffer) LoopCount(offset / minMagnitudeChange);
break;
}
if(offset == 0 && minMagnitudeChange == 1)
{
loop->loopCount = new(loopCountBuffer) LoopCount(leftSym);
break;
}
}
loop->loopCount = new(loopCountBuffer) LoopCount(leftSym, rightSym, offset, minMagnitudeChange);
break;
}
}
void GlobOpt::GenerateLoopCount(Loop *const loop, LoopCount *const loopCount)
{
Assert(DoLoopCountBasedBoundCheckHoist());
Assert(loop);
Assert(loopCount);
Assert(loopCount == loop->loopCount);
Assert(!loopCount->HasBeenGenerated());
// loopCountMinusOne = (left - right + offset) / minMagnitudeChange
// Prepare the landing pad for bailouts and instruction insertion
BailOutInfo *const bailOutInfo = loop->bailOutInfo;
Assert(bailOutInfo);
const IR::BailOutKind bailOutKind = IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck;
IR::Instr *const insertBeforeInstr = bailOutInfo->bailOutInstr;
Assert(insertBeforeInstr);
Func *const func = bailOutInfo->bailOutFunc;
// intermediateValue = left - right
IR::IntConstOpnd *offset =
loopCount->Offset() == 0 ? nullptr : IR::IntConstOpnd::New(loopCount->Offset(), TyInt32, func, true);
StackSym *const rightSym = loopCount->RightSym();
StackSym *intermediateValueSym;
if(rightSym)
{
IR::BailOutInstr *const instr = IR::BailOutInstr::New(Js::OpCode::Sub_I4, bailOutKind, bailOutInfo, func);
instr->SetSrc2(IR::RegOpnd::New(rightSym, rightSym->GetType(), func));
instr->GetSrc2()->SetIsJITOptimizedReg(true);
StackSym *const leftSym = loopCount->LeftSym();
if(leftSym)
{
// intermediateValue = left - right
instr->SetSrc1(IR::RegOpnd::New(leftSym, leftSym->GetType(), func));
instr->GetSrc1()->SetIsJITOptimizedReg(true);
}
else if(offset)
{
// intermediateValue = offset - right
instr->SetSrc1(offset);
offset = nullptr;
}
else
{
// intermediateValue = -right
instr->m_opcode = Js::OpCode::Neg_I4;
instr->SetSrc1(instr->UnlinkSrc2());
}
intermediateValueSym = StackSym::New(TyInt32, func);
instr->SetDst(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetDst()->SetIsJITOptimizedReg(true);
instr->SetByteCodeOffset(insertBeforeInstr);
insertBeforeInstr->InsertBefore(instr);
}
else
{
// intermediateValue = left
Assert(loopCount->LeftSym());
intermediateValueSym = loopCount->LeftSym();
}
// intermediateValue += offset
if(offset)
{
IR::BailOutInstr *const instr = IR::BailOutInstr::New(Js::OpCode::Add_I4, bailOutKind, bailOutInfo, func);
instr->SetSrc1(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetSrc1()->SetIsJITOptimizedReg(true);
if(offset->GetValue() < 0 && offset->GetValue() != IntConstMin)
{
instr->m_opcode = Js::OpCode::Sub_I4;
offset->SetValue(-offset->GetValue());
}
instr->SetSrc2(offset);
if(intermediateValueSym == loopCount->LeftSym())
{
intermediateValueSym = StackSym::New(TyInt32, func);
}
instr->SetDst(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetDst()->SetIsJITOptimizedReg(true);
instr->SetByteCodeOffset(insertBeforeInstr);
insertBeforeInstr->InsertBefore(instr);
}
// intermediateValue /= minMagnitudeChange
const int minMagnitudeChange = loopCount->MinMagnitudeChange();
if(minMagnitudeChange != 1)
{
IR::Instr *const instr = IR::Instr::New(Js::OpCode::Div_I4, func);
instr->SetSrc1(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetSrc1()->SetIsJITOptimizedReg(true);
Assert(minMagnitudeChange != 0); // bailout is not needed
instr->SetSrc2(IR::IntConstOpnd::New(minMagnitudeChange, TyInt32, func, true));
if(intermediateValueSym == loopCount->LeftSym())
{
intermediateValueSym = StackSym::New(TyInt32, func);
}
instr->SetDst(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetDst()->SetIsJITOptimizedReg(true);
instr->SetByteCodeOffset(insertBeforeInstr);
insertBeforeInstr->InsertBefore(instr);
}
else
{
Assert(intermediateValueSym != loopCount->LeftSym());
}
// loopCountMinusOne = intermediateValue
loopCount->SetLoopCountMinusOneSym(intermediateValueSym);
}
void GlobOpt::GenerateLoopCountPlusOne(Loop *const loop, LoopCount *const loopCount)
{
Assert(loop);
Assert(loopCount);
Assert(loopCount == loop->loopCount);
if (loopCount->HasGeneratedLoopCountSym())
{
return;
}
if (!loopCount->HasBeenGenerated())
{
GenerateLoopCount(loop, loopCount);
}
Assert(loopCount->HasBeenGenerated());
// If this is null then the loop count is a constant and there is nothing more to do here
if (loopCount->LoopCountMinusOneSym())
{
// Prepare the landing pad for bailouts and instruction insertion
BailOutInfo *const bailOutInfo = loop->bailOutInfo;
Assert(bailOutInfo);
IR::Instr *const insertBeforeInstr = bailOutInfo->bailOutInstr;
Assert(insertBeforeInstr);
Func *const func = bailOutInfo->bailOutFunc;
IRType type = loopCount->LoopCountMinusOneSym()->GetType();
// loop count is off by one, so add one
IR::RegOpnd *loopCountOpnd = IR::RegOpnd::New(type, func);
IR::RegOpnd *minusOneOpnd = IR::RegOpnd::New(loopCount->LoopCountMinusOneSym(), type, func);
minusOneOpnd->SetIsJITOptimizedReg(true);
IR::Instr* incrInstr = IR::Instr::New(Js::OpCode::Add_I4,
loopCountOpnd,
minusOneOpnd,
IR::IntConstOpnd::New(1, type, func, true),
func);
insertBeforeInstr->InsertBefore(incrInstr);
// Incrementing to 1 can overflow - add a bounds check bailout here
incrInstr->ConvertToBailOutInstr(bailOutInfo, IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck);
loopCount->SetLoopCountSym(loopCountOpnd->GetStackSym());
}
}
void GlobOpt::GenerateSecondaryInductionVariableBound(
Loop *const loop,
StackSym *const inductionVariableSym,
LoopCount *const loopCount,
const int maxMagnitudeChange,
const bool needsMagnitudeAdjustment,
StackSym *const boundSym)
{
Assert(loop);
Assert(inductionVariableSym);
Assert(inductionVariableSym->GetType() == TyInt32 || inductionVariableSym->GetType() == TyUint32);
Assert(loopCount);
Assert(loopCount == loop->loopCount);
Assert(loopCount->LoopCountMinusOneSym());
Assert(maxMagnitudeChange != 0);
Assert(maxMagnitudeChange >= -InductionVariable::ChangeMagnitudeLimitForLoopCountBasedHoisting);
Assert(maxMagnitudeChange <= InductionVariable::ChangeMagnitudeLimitForLoopCountBasedHoisting);
Assert(boundSym);
Assert(boundSym->IsInt32());
// bound = inductionVariable + loopCountMinusOne * maxMagnitudeChange
// Prepare the landing pad for bailouts and instruction insertion
BailOutInfo *const bailOutInfo = loop->bailOutInfo;
Assert(bailOutInfo);
const IR::BailOutKind bailOutKind = IR::BailOutOnFailedHoistedLoopCountBasedBoundCheck;
IR::Instr *const insertBeforeInstr = bailOutInfo->bailOutInstr;
Assert(insertBeforeInstr);
Func *const func = bailOutInfo->bailOutFunc;
StackSym* loopCountSym = nullptr;
// If indexOffset < maxMagnitudeChange, we need to account for the difference between them in the bound check
// i.e. BoundCheck: inductionVariable + loopCountMinusOne * maxMagnitudeChange + maxMagnitudeChange - indexOffset <= length - offset
// Since the BoundCheck instruction already deals with offset, we can simplify this to
// BoundCheck: inductionVariable + loopCount * maxMagnitudeChange <= length + indexOffset - offset
if (needsMagnitudeAdjustment)
{
GenerateLoopCountPlusOne(loop, loopCount);
loopCountSym = loopCount->LoopCountSym();
}
else
{
loopCountSym = loopCount->LoopCountMinusOneSym();
}
// intermediateValue = loopCount * maxMagnitudeChange
StackSym *intermediateValueSym;
if(maxMagnitudeChange == 1 || maxMagnitudeChange == -1)
{
intermediateValueSym = loopCountSym;
}
else
{
IR::BailOutInstr *const instr = IR::BailOutInstr::New(Js::OpCode::Mul_I4, bailOutKind, bailOutInfo, func);
instr->SetSrc1(
IR::RegOpnd::New(loopCountSym, loopCountSym->GetType(), func));
instr->GetSrc1()->SetIsJITOptimizedReg(true);
instr->SetSrc2(IR::IntConstOpnd::New(maxMagnitudeChange, TyInt32, func, true));
intermediateValueSym = boundSym;
instr->SetDst(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetDst()->SetIsJITOptimizedReg(true);
instr->SetByteCodeOffset(insertBeforeInstr);
insertBeforeInstr->InsertBefore(instr);
}
// bound = intermediateValue + inductionVariable
{
IR::BailOutInstr *const instr = IR::BailOutInstr::New(Js::OpCode::Add_I4, bailOutKind, bailOutInfo, func);
instr->SetSrc1(IR::RegOpnd::New(intermediateValueSym, intermediateValueSym->GetType(), func));
instr->GetSrc1()->SetIsJITOptimizedReg(true);
instr->SetSrc2(IR::RegOpnd::New(inductionVariableSym, inductionVariableSym->GetType(), func));
instr->GetSrc2()->SetIsJITOptimizedReg(true);
if(maxMagnitudeChange == -1)
{
// bound = inductionVariable - intermediateValue[loopCount]
instr->m_opcode = Js::OpCode::Sub_I4;
instr->SwapOpnds();
}
instr->SetDst(IR::RegOpnd::New(boundSym, boundSym->GetType(), func));
instr->GetDst()->SetIsJITOptimizedReg(true);
instr->SetByteCodeOffset(insertBeforeInstr);
insertBeforeInstr->InsertBefore(instr);
}
}
void GlobOpt::DetermineArrayBoundCheckHoistability(
bool needLowerBoundCheck,
bool needUpperBoundCheck,
ArrayLowerBoundCheckHoistInfo &lowerHoistInfo,
ArrayUpperBoundCheckHoistInfo &upperHoistInfo,
const bool isJsArray,
StackSym *const indexSym,
Value *const indexValue,
const IntConstantBounds &indexConstantBounds,
StackSym *const headSegmentLengthSym,
Value *const headSegmentLengthValue,
const IntConstantBounds &headSegmentLengthConstantBounds,
Loop *const headSegmentLengthInvariantLoop,
bool &failedToUpdateCompatibleLowerBoundCheck,
bool &failedToUpdateCompatibleUpperBoundCheck)
{
Assert(DoBoundCheckHoist());
Assert(needLowerBoundCheck || needUpperBoundCheck);
Assert(!lowerHoistInfo.HasAnyInfo());
Assert(!upperHoistInfo.HasAnyInfo());
Assert(!indexSym == !indexValue);
Assert(!needUpperBoundCheck || headSegmentLengthSym);
Assert(!headSegmentLengthSym == !headSegmentLengthValue);
Assert(!failedToUpdateCompatibleLowerBoundCheck);
Assert(!failedToUpdateCompatibleUpperBoundCheck);
Loop *const currentLoop = currentBlock->loop;
if(!indexValue)
{
Assert(!needLowerBoundCheck);
Assert(needUpperBoundCheck);
Assert(indexConstantBounds.IsConstant());
// The index is a constant value, so a bound check on it can be hoisted as far as desired. Just find a compatible bound
// check that is already available, or the loop in which the head segment length is invariant.
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
2,
_u("Index is constant, looking for a compatible upper bound check\n"));
const int indexConstantValue = indexConstantBounds.LowerBound();
Assert(indexConstantValue != IntConstMax);
const IntBoundCheck *compatibleBoundCheck;
if(currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
IntBoundCheckCompatibilityId(ZeroValueNumber, headSegmentLengthValue->GetValueNumber()),
&compatibleBoundCheck))
{
// We need:
// index < headSegmentLength
// Normalize the offset such that:
// 0 <= headSegmentLength + compatibleBoundCheckOffset
// Where (compatibleBoundCheckOffset = -1 - index), and -1 is to simulate < instead of <=.
const int compatibleBoundCheckOffset = -1 - indexConstantValue;
if(compatibleBoundCheck->SetBoundOffset(compatibleBoundCheckOffset))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Found in block %u\n"),
compatibleBoundCheck->Block()->GetBlockNum());
upperHoistInfo.SetCompatibleBoundCheck(compatibleBoundCheck->Block(), indexConstantValue);
return;
}
failedToUpdateCompatibleUpperBoundCheck = true;
}
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Not found\n"));
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 2, _u("Looking for invariant head segment length\n"));
Loop *invariantLoop;
Value *landingPadHeadSegmentLengthValue = nullptr;
if(headSegmentLengthInvariantLoop)
{
invariantLoop = headSegmentLengthInvariantLoop;
landingPadHeadSegmentLengthValue =
invariantLoop->landingPad->globOptData.FindValue(headSegmentLengthSym);
}
else if(currentLoop)
{
invariantLoop = nullptr;
for(Loop *loop = currentLoop; loop; loop = loop->parent)
{
GlobOptBlockData &landingPadBlockData = loop->landingPad->globOptData;
Value *const value = landingPadBlockData.FindValue(headSegmentLengthSym);
if(!value)
{
break;
}
invariantLoop = loop;
landingPadHeadSegmentLengthValue = value;
}
if(!invariantLoop)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Not found\n"));
return;
}
}
else
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Not found, block is not in a loop\n"));
return;
}
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Found in loop %u landing pad block %u\n"),
invariantLoop->GetLoopNumber(),
invariantLoop->landingPad->GetBlockNum());
IntConstantBounds landingPadHeadSegmentLengthConstantBounds;
AssertVerify(
landingPadHeadSegmentLengthValue
->GetValueInfo()
->TryGetIntConstantBounds(&landingPadHeadSegmentLengthConstantBounds));
if(isJsArray)
{
// index >= headSegmentLength is currently not possible for JS arrays (except when index == int32 max, which is
// covered above).
Assert(
!ValueInfo::IsGreaterThanOrEqualTo(
nullptr,
indexConstantValue,
indexConstantValue,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound()));
}
else if(
ValueInfo::IsGreaterThanOrEqualTo(
nullptr,
indexConstantValue,
indexConstantValue,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound()))
{
// index >= headSegmentLength in the landing pad, can't use the index sym. This is possible for typed arrays through
// conditions on array.length in user code.
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 2, _u("Index >= head segment length\n"));
return;
}
upperHoistInfo.SetLoop(
invariantLoop,
indexConstantValue,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds);
return;
}
Assert(!indexConstantBounds.IsConstant());
ValueInfo *const indexValueInfo = indexValue->GetValueInfo();
const IntBounds *const indexBounds = indexValueInfo->IsIntBounded() ? indexValueInfo->AsIntBounded()->Bounds() : nullptr;
{
// See if a compatible bound check is already available
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
2,
_u("Looking for compatible bound checks for index bounds\n"));
bool searchingLower = needLowerBoundCheck;
bool searchingUpper = needUpperBoundCheck;
Assert(searchingLower || searchingUpper);
bool foundLowerBoundCheck = false;
const IntBoundCheck *lowerBoundCheck = nullptr;
ValueNumber lowerHoistBlockIndexValueNumber = InvalidValueNumber;
int lowerBoundOffset = 0;
if(searchingLower &&
currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
IntBoundCheckCompatibilityId(ZeroValueNumber, indexValue->GetValueNumber()),
&lowerBoundCheck))
{
if(lowerBoundCheck->SetBoundOffset(0))
{
foundLowerBoundCheck = true;
lowerHoistBlockIndexValueNumber = indexValue->GetValueNumber();
lowerBoundOffset = 0;
searchingLower = false;
}
else
{
failedToUpdateCompatibleLowerBoundCheck = true;
}
}
bool foundUpperBoundCheck = false;
const IntBoundCheck *upperBoundCheck = nullptr;
ValueNumber upperHoistBlockIndexValueNumber = InvalidValueNumber;
int upperBoundOffset = 0;
if(searchingUpper &&
currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
IntBoundCheckCompatibilityId(indexValue->GetValueNumber(), headSegmentLengthValue->GetValueNumber()),
&upperBoundCheck))
{
if(upperBoundCheck->SetBoundOffset(-1)) // -1 is to simulate < instead of <=
{
foundUpperBoundCheck = true;
upperHoistBlockIndexValueNumber = indexValue->GetValueNumber();
upperBoundOffset = 0;
searchingUpper = false;
}
else
{
failedToUpdateCompatibleUpperBoundCheck = true;
}
}
if(indexBounds)
{
searchingLower = searchingLower && indexBounds->RelativeLowerBounds().Count() != 0;
searchingUpper = searchingUpper && indexBounds->RelativeUpperBounds().Count() != 0;
if(searchingLower || searchingUpper)
{
for(auto it = currentBlock->globOptData.availableIntBoundChecks->GetIterator(); it.IsValid(); it.MoveNext())
{
const IntBoundCheck &boundCheck = it.CurrentValue();
if(searchingLower && boundCheck.LeftValueNumber() == ZeroValueNumber)
{
lowerHoistBlockIndexValueNumber = boundCheck.RightValueNumber();
const ValueRelativeOffset *bound;
if(indexBounds->RelativeLowerBounds().TryGetReference(lowerHoistBlockIndexValueNumber, &bound))
{
// We need:
// 0 <= boundBase + boundOffset
const int offset = bound->Offset();
if(boundCheck.SetBoundOffset(offset))
{
foundLowerBoundCheck = true;
lowerBoundCheck = &boundCheck;
lowerBoundOffset = offset;
searchingLower = false;
if(!searchingUpper)
{
break;
}
}
else
{
failedToUpdateCompatibleLowerBoundCheck = true;
}
}
}
if(searchingUpper && boundCheck.RightValueNumber() == headSegmentLengthValue->GetValueNumber())
{
upperHoistBlockIndexValueNumber = boundCheck.LeftValueNumber();
const ValueRelativeOffset *bound;
if(indexBounds->RelativeUpperBounds().TryGetReference(upperHoistBlockIndexValueNumber, &bound))
{
// We need:
// boundBase + boundOffset < headSegmentLength
// Normalize the offset such that:
// boundBase <= headSegmentLength + compatibleBoundCheckOffset
// Where (compatibleBoundCheckOffset = -1 - boundOffset), and -1 is to simulate < instead of <=.
const int offset = -1 - bound->Offset();
if(boundCheck.SetBoundOffset(offset))
{
foundUpperBoundCheck = true;
upperBoundCheck = &boundCheck;
upperBoundOffset = bound->Offset();
searchingUpper = false;
if(!searchingLower)
{
break;
}
}
else
{
failedToUpdateCompatibleUpperBoundCheck = true;
}
}
}
}
}
}
if(foundLowerBoundCheck)
{
// A bound check takes the form src1 <= src2 + dst
Assert(lowerBoundCheck->Instr()->GetSrc2());
Assert(
lowerBoundCheck->Instr()->GetSrc2()->AsRegOpnd()->m_sym->GetType() == TyInt32 ||
lowerBoundCheck->Instr()->GetSrc2()->AsRegOpnd()->m_sym->GetType() == TyUint32);
StackSym *boundCheckIndexSym = lowerBoundCheck->Instr()->GetSrc2()->AsRegOpnd()->m_sym;
if(boundCheckIndexSym->IsTypeSpec())
{
boundCheckIndexSym = boundCheckIndexSym->GetVarEquivSym(nullptr);
Assert(boundCheckIndexSym);
}
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Found lower bound (s%u + %d) in block %u\n"),
boundCheckIndexSym->m_id,
lowerBoundOffset,
lowerBoundCheck->Block()->GetBlockNum());
lowerHoistInfo.SetCompatibleBoundCheck(
lowerBoundCheck->Block(),
boundCheckIndexSym,
lowerBoundOffset,
lowerHoistBlockIndexValueNumber);
Assert(!searchingLower);
needLowerBoundCheck = false;
if(!needUpperBoundCheck)
{
return;
}
}
if(foundUpperBoundCheck)
{
// A bound check takes the form src1 <= src2 + dst
Assert(upperBoundCheck->Instr()->GetSrc1());
Assert(
upperBoundCheck->Instr()->GetSrc1()->AsRegOpnd()->m_sym->GetType() == TyInt32 ||
upperBoundCheck->Instr()->GetSrc1()->AsRegOpnd()->m_sym->GetType() == TyUint32);
StackSym *boundCheckIndexSym = upperBoundCheck->Instr()->GetSrc1()->AsRegOpnd()->m_sym;
if(boundCheckIndexSym->IsTypeSpec())
{
boundCheckIndexSym = boundCheckIndexSym->GetVarEquivSym(nullptr);
Assert(boundCheckIndexSym);
}
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Found upper bound (s%u + %d) in block %u\n"),
boundCheckIndexSym->m_id,
upperBoundOffset,
upperBoundCheck->Block()->GetBlockNum());
upperHoistInfo.SetCompatibleBoundCheck(
upperBoundCheck->Block(),
boundCheckIndexSym,
-1 - upperBoundOffset,
upperHoistBlockIndexValueNumber);
Assert(!searchingUpper);
needUpperBoundCheck = false;
if(!needLowerBoundCheck)
{
return;
}
}
Assert(needLowerBoundCheck || needUpperBoundCheck);
Assert(!needLowerBoundCheck || !lowerHoistInfo.CompatibleBoundCheckBlock());
Assert(!needUpperBoundCheck || !upperHoistInfo.CompatibleBoundCheckBlock());
}
if(!currentLoop)
{
return;
}
// Check if the index sym is invariant in the loop, or if the index value in the landing pad is a lower/upper bound of the
// index value in the current block
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 2, _u("Looking for invariant index or index bounded by itself\n"));
bool searchingLower = needLowerBoundCheck, searchingUpper = needUpperBoundCheck;
for(Loop *loop = currentLoop; loop; loop = loop->parent)
{
GlobOptBlockData &landingPadBlockData = loop->landingPad->globOptData;
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Trying loop %u landing pad block %u\n"),
loop->GetLoopNumber(),
loop->landingPad->GetBlockNum());
Value *const landingPadIndexValue = landingPadBlockData.FindValue(indexSym);
if(!landingPadIndexValue)
{
break;
}
IntConstantBounds landingPadIndexConstantBounds;
const bool landingPadIndexValueIsLikelyInt =
landingPadIndexValue->GetValueInfo()->TryGetIntConstantBounds(&landingPadIndexConstantBounds, true);
int lowerOffset = 0, upperOffset = 0;
if(indexValue->GetValueNumber() == landingPadIndexValue->GetValueNumber())
{
Assert(landingPadIndexValueIsLikelyInt);
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Index is invariant\n"));
}
else
{
if(!landingPadIndexValueIsLikelyInt)
{
break;
}
if(searchingLower)
{
if(lowerHoistInfo.Loop() && indexValue->GetValueNumber() == lowerHoistInfo.IndexValueNumber())
{
// Prefer using the invariant sym
needLowerBoundCheck = searchingLower = false;
if(!needUpperBoundCheck)
{
return;
}
if(!searchingUpper)
{
break;
}
}
else
{
bool foundBound = false;
if(indexBounds)
{
const ValueRelativeOffset *bound;
if(indexBounds->RelativeLowerBounds().TryGetReference(landingPadIndexValue->GetValueNumber(), &bound))
{
foundBound = true;
lowerOffset = bound->Offset();
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found lower bound (index + %d)\n"),
lowerOffset);
}
}
if(!foundBound)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Lower bound was not found\n"));
searchingLower = false;
if(!searchingUpper)
{
break;
}
}
}
}
if(searchingUpper)
{
if(upperHoistInfo.Loop() && indexValue->GetValueNumber() == upperHoistInfo.IndexValueNumber())
{
// Prefer using the invariant sym
needUpperBoundCheck = searchingUpper = false;
if(!needLowerBoundCheck)
{
return;
}
if(!searchingLower)
{
break;
}
}
else
{
bool foundBound = false;
if(indexBounds)
{
const ValueRelativeOffset *bound;
if(indexBounds->RelativeUpperBounds().TryGetReference(landingPadIndexValue->GetValueNumber(), &bound))
{
foundBound = true;
upperOffset = bound->Offset();
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found upper bound (index + %d)\n"),
upperOffset);
}
}
if(!foundBound)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Upper bound was not found\n"));
searchingUpper = false;
if(!searchingLower)
{
break;
}
}
}
}
}
if(searchingLower)
{
if(ValueInfo::IsLessThan(
landingPadIndexValue,
landingPadIndexConstantBounds.LowerBound(),
landingPadIndexConstantBounds.UpperBound(),
nullptr,
0,
0))
{
// index < 0 in the landing pad; can't use the index sym
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Index < 0\n"));
searchingLower = false;
if(!searchingUpper)
{
break;
}
}
else
{
lowerHoistInfo.SetLoop(
loop,
indexSym,
lowerOffset,
lowerOffset,
landingPadIndexValue,
landingPadIndexConstantBounds);
}
}
if(!searchingUpper)
{
continue;
}
// Check if the head segment length sym is available in the landing pad
Value *const landingPadHeadSegmentLengthValue = landingPadBlockData.FindValue(headSegmentLengthSym);
if(!landingPadHeadSegmentLengthValue)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Head segment length is not invariant\n"));
searchingUpper = false;
if(!searchingLower)
{
break;
}
continue;
}
IntConstantBounds landingPadHeadSegmentLengthConstantBounds;
AssertVerify(
landingPadHeadSegmentLengthValue
->GetValueInfo()
->TryGetIntConstantBounds(&landingPadHeadSegmentLengthConstantBounds));
if(ValueInfo::IsGreaterThanOrEqualTo(
landingPadIndexValue,
landingPadIndexConstantBounds.LowerBound(),
landingPadIndexConstantBounds.UpperBound(),
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound()))
{
// index >= headSegmentLength in the landing pad; can't use the index sym
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Index >= head segment length\n"));
searchingUpper = false;
if(!searchingLower)
{
break;
}
continue;
}
// We need:
// boundBase + boundOffset < headSegmentLength
// Normalize the offset such that:
// boundBase <= headSegmentLength + offset
// Where (offset = -1 - boundOffset), and -1 is to simulate < instead of <=.
int indexOffset = upperOffset;
upperOffset = -1 - upperOffset;
upperHoistInfo.SetLoop(
loop,
indexSym,
indexOffset,
upperOffset,
landingPadIndexValue,
landingPadIndexConstantBounds,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds);
}
if(needLowerBoundCheck && lowerHoistInfo.Loop())
{
needLowerBoundCheck = false;
if(!needUpperBoundCheck)
{
return;
}
}
if(needUpperBoundCheck && upperHoistInfo.Loop())
{
needUpperBoundCheck = false;
if(!needLowerBoundCheck)
{
return;
}
}
// Find an invariant lower/upper bound of the index that can be used for hoisting the bound checks
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 2, _u("Looking for invariant index bounds\n"));
searchingLower = needLowerBoundCheck;
searchingUpper = needUpperBoundCheck;
for(Loop *loop = currentLoop; loop; loop = loop->parent)
{
GlobOptBlockData &landingPadBlockData = loop->landingPad->globOptData;
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Trying loop %u landing pad block %u\n"),
loop->GetLoopNumber(),
loop->landingPad->GetBlockNum());
Value *landingPadHeadSegmentLengthValue = nullptr;
IntConstantBounds landingPadHeadSegmentLengthConstantBounds;
if(searchingUpper)
{
// Check if the head segment length sym is available in the landing pad
landingPadHeadSegmentLengthValue = landingPadBlockData.FindValue(headSegmentLengthSym);
if(landingPadHeadSegmentLengthValue)
{
AssertVerify(
landingPadHeadSegmentLengthValue
->GetValueInfo()
->TryGetIntConstantBounds(&landingPadHeadSegmentLengthConstantBounds));
}
else
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Head segment length is not invariant\n"));
searchingUpper = false;
if(!searchingLower)
{
break;
}
}
}
// Look for a relative bound
if(indexBounds)
{
for(int j = 0; j < 2; ++j)
{
const bool searchingRelativeLowerBounds = j == 0;
if(!(searchingRelativeLowerBounds ? searchingLower : searchingUpper))
{
continue;
}
for(auto it =
(
searchingRelativeLowerBounds
? indexBounds->RelativeLowerBounds()
: indexBounds->RelativeUpperBounds()
).GetIterator();
it.IsValid();
it.MoveNext())
{
const ValueRelativeOffset &indexBound = it.CurrentValue();
StackSym *const indexBoundBaseSym = indexBound.BaseSym();
if(!indexBoundBaseSym)
{
continue;
}
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found %S bound (s%u + %d)\n"),
searchingRelativeLowerBounds ? "lower" : "upper",
indexBoundBaseSym->m_id,
indexBound.Offset());
if(!indexBound.WasEstablishedExplicitly())
{
// Don't use a bound that was not established explicitly, as it may be too aggressive. For instance, an
// index sym used in an array will obtain an upper bound of the array's head segment length - 1. That
// bound is not established explicitly because the bound assertion is not enforced by the source code.
// Rather, it is assumed and enforced by the JIT using bailout check. Incrementing the index and using
// it in a different array may otherwise cause it to use the first array's head segment length as the
// upper bound on which to do the bound check against the second array, and that bound check would
// always fail when the arrays are the same size.
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Bound was established implicitly\n"));
continue;
}
Value *const landingPadIndexBoundBaseValue = landingPadBlockData.FindValue(indexBoundBaseSym);
if(!landingPadIndexBoundBaseValue ||
landingPadIndexBoundBaseValue->GetValueNumber() != indexBound.BaseValueNumber())
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Bound is not invariant\n"));
continue;
}
IntConstantBounds landingPadIndexBoundBaseConstantBounds;
AssertVerify(
landingPadIndexBoundBaseValue
->GetValueInfo()
->TryGetIntConstantBounds(&landingPadIndexBoundBaseConstantBounds, true));
int offset = indexBound.Offset();
if(searchingRelativeLowerBounds)
{
if(offset == IntConstMin ||
ValueInfo::IsLessThan(
landingPadIndexBoundBaseValue,
landingPadIndexBoundBaseConstantBounds.LowerBound(),
landingPadIndexBoundBaseConstantBounds.UpperBound(),
nullptr,
-offset,
-offset))
{
// indexBoundBase + indexBoundOffset < 0; can't use this bound
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Bound < 0\n"));
continue;
}
lowerHoistInfo.SetLoop(
loop,
indexBoundBaseSym,
offset,
offset,
landingPadIndexBoundBaseValue,
landingPadIndexBoundBaseConstantBounds);
break;
}
if(ValueInfo::IsLessThanOrEqualTo(
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound(),
landingPadIndexBoundBaseValue,
landingPadIndexBoundBaseConstantBounds.LowerBound(),
landingPadIndexBoundBaseConstantBounds.UpperBound(),
offset))
{
// indexBoundBase + indexBoundOffset >= headSegmentLength; can't use this bound
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Bound >= head segment length\n"));
continue;
}
// We need:
// boundBase + boundOffset < headSegmentLength
// Normalize the offset such that:
// boundBase <= headSegmentLength + offset
// Where (offset = -1 - boundOffset), and -1 is to simulate < instead of <=.
int indexOffset = offset;
offset = -1 - offset;
upperHoistInfo.SetLoop(
loop,
indexBoundBaseSym,
indexOffset,
offset,
landingPadIndexBoundBaseValue,
landingPadIndexBoundBaseConstantBounds,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds);
break;
}
}
}
if(searchingLower && lowerHoistInfo.Loop() != loop)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Lower bound was not found\n"));
searchingLower = false;
if(!searchingUpper)
{
break;
}
}
if(searchingUpper && upperHoistInfo.Loop() != loop)
{
// No useful relative bound found; look for a constant bound if the index is an induction variable. Exclude constant
// bounds of non-induction variables because those bounds may have been established through means other than a loop
// exit condition, such as math or bitwise operations. Exclude constant bounds established implicitly by <,
// <=, >, and >=. For example, for a loop condition (i < n - 1), if 'n' is not invariant and hence can't be used,
// 'i' will still have a constant upper bound of (int32 max - 2) that should be excluded.
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Relative upper bound was not found\n"));
const InductionVariable *indexInductionVariable;
if(!upperHoistInfo.Loop() &&
currentLoop->inductionVariables &&
currentLoop->inductionVariables->TryGetReference(indexSym->m_id, &indexInductionVariable) &&
indexInductionVariable->IsChangeDeterminate())
{
if(!(indexBounds && indexBounds->WasConstantUpperBoundEstablishedExplicitly()))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Constant upper bound was established implicitly\n"));
}
else
{
// See if a compatible bound check is already available
const int indexConstantBound = indexBounds->ConstantUpperBound();
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found constant upper bound %d, looking for a compatible bound check\n"),
indexConstantBound);
const IntBoundCheck *boundCheck;
if(currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
IntBoundCheckCompatibilityId(ZeroValueNumber, headSegmentLengthValue->GetValueNumber()),
&boundCheck))
{
// We need:
// indexConstantBound < headSegmentLength
// Normalize the offset such that:
// 0 <= headSegmentLength + compatibleBoundCheckOffset
// Where (compatibleBoundCheckOffset = -1 - indexConstantBound), and -1 is to simulate < instead of <=.
const int compatibleBoundCheckOffset = -1 - indexConstantBound;
if(boundCheck->SetBoundOffset(compatibleBoundCheckOffset))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
5,
_u("Found in block %u\n"),
boundCheck->Block()->GetBlockNum());
upperHoistInfo.SetCompatibleBoundCheck(boundCheck->Block(), indexConstantBound);
needUpperBoundCheck = searchingUpper = false;
if(!needLowerBoundCheck)
{
return;
}
if(!searchingLower)
{
break;
}
}
else
{
failedToUpdateCompatibleUpperBoundCheck = true;
}
}
if(searchingUpper)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 5, _u("Not found\n"));
upperHoistInfo.SetLoop(
loop,
indexConstantBound,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds);
}
}
}
else if(!upperHoistInfo.Loop())
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Index is not an induction variable, not using constant upper bound\n"));
}
if(searchingUpper && upperHoistInfo.Loop() != loop)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Upper bound was not found\n"));
searchingUpper = false;
if(!searchingLower)
{
break;
}
}
}
}
if(needLowerBoundCheck && lowerHoistInfo.Loop())
{
needLowerBoundCheck = false;
if(!needUpperBoundCheck)
{
return;
}
}
if(needUpperBoundCheck && upperHoistInfo.Loop())
{
needUpperBoundCheck = false;
if(!needLowerBoundCheck)
{
return;
}
}
// Try to use the loop count to calculate a missing lower/upper bound that in turn can be used for hoisting a bound check
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
2,
_u("Looking for loop count based bound for loop %u landing pad block %u\n"),
currentLoop->GetLoopNumber(),
currentLoop->landingPad->GetBlockNum());
LoopCount *const loopCount = currentLoop->loopCount;
if(!loopCount)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Loop was not counted\n"));
return;
}
const InductionVariable *indexInductionVariable;
if(!currentLoop->inductionVariables ||
!currentLoop->inductionVariables->TryGetReference(indexSym->m_id, &indexInductionVariable) ||
!indexInductionVariable->IsChangeDeterminate())
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Index is not an induction variable\n"));
return;
}
// Determine the maximum-magnitude change per iteration, and verify that the change is reasonably finite
Assert(indexInductionVariable->IsChangeUnidirectional());
GlobOptBlockData &landingPadBlockData = currentLoop->landingPad->globOptData;
int maxMagnitudeChange = indexInductionVariable->ChangeBounds().UpperBound();
Value *landingPadHeadSegmentLengthValue;
IntConstantBounds landingPadHeadSegmentLengthConstantBounds;
if(maxMagnitudeChange > 0)
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Index's maximum-magnitude change per iteration is %d\n"),
maxMagnitudeChange);
if(!needUpperBoundCheck || maxMagnitudeChange > InductionVariable::ChangeMagnitudeLimitForLoopCountBasedHoisting)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Change magnitude is too large\n"));
return;
}
// Check whether the head segment length is available in the landing pad
landingPadHeadSegmentLengthValue = landingPadBlockData.FindValue(headSegmentLengthSym);
Assert(!headSegmentLengthInvariantLoop || landingPadHeadSegmentLengthValue);
if(!landingPadHeadSegmentLengthValue)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Head segment length is not invariant\n"));
return;
}
AssertVerify(
landingPadHeadSegmentLengthValue
->GetValueInfo()
->TryGetIntConstantBounds(&landingPadHeadSegmentLengthConstantBounds));
}
else
{
maxMagnitudeChange = indexInductionVariable->ChangeBounds().LowerBound();
Assert(maxMagnitudeChange < 0);
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Index's maximum-magnitude change per iteration is %d\n"),
maxMagnitudeChange);
if(!needLowerBoundCheck || maxMagnitudeChange < -InductionVariable::ChangeMagnitudeLimitForLoopCountBasedHoisting)
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Change magnitude is too large\n"));
return;
}
landingPadHeadSegmentLengthValue = nullptr;
}
// Determine if the index already changed in the loop, and by how much
int indexOffset = 0;
StackSym *indexSymToAdd = nullptr;
SymBoundType symBoundType = DetermineSymBoundOffsetOrValueRelativeToLandingPad(
indexSym,
maxMagnitudeChange >= 0,
indexValueInfo,
indexBounds,
&currentLoop->landingPad->globOptData,
&indexOffset);
if (symBoundType == SymBoundType::VALUE)
{
// The bound value is constant
indexSymToAdd = nullptr;
}
else if (symBoundType == SymBoundType::OFFSET)
{
// The bound value is not constant, the offset needs to be added to the index sym in the landing pad
indexSymToAdd = indexSym;
}
else
{
Assert(symBoundType == SymBoundType::UNKNOWN);
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Unable to determine the sym bound offset or value\n"));
return;
}
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Index's offset from landing pad is %d\n"), indexOffset);
// The secondary induction variable bound is computed as follows:
// bound = index + indexOffset + loopCountMinusOne * maxMagnitudeChange
//
// If the loop count is constant, (inductionVariableOffset + loopCount * maxMagnitudeChange) can be folded into an offset:
// bound = index + offset
int offset;
StackSym *indexLoopCountBasedBoundBaseSym = nullptr;
Value *indexLoopCountBasedBoundBaseValue = nullptr;
IntConstantBounds indexLoopCountBasedBoundBaseConstantBounds;
bool generateLoopCountBasedIndexBound;
if(!loopCount->HasBeenGenerated() || loopCount->LoopCountMinusOneSym())
{
if(loopCount->HasBeenGenerated())
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Loop count is assigned to s%u\n"),
loopCount->LoopCountMinusOneSym()->m_id);
}
else
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Loop count has not been generated yet\n"));
}
offset = indexOffset;
// Check if there is already a loop count based bound sym for the index. If not, generate it.
do
{
const SymID indexSymId = indexSym->m_id;
SymIdToStackSymMap *&loopCountBasedBoundBaseSyms = currentLoop->loopCountBasedBoundBaseSyms;
if(!loopCountBasedBoundBaseSyms)
{
loopCountBasedBoundBaseSyms = JitAnew(alloc, SymIdToStackSymMap, alloc);
}
else if(loopCountBasedBoundBaseSyms->TryGetValue(indexSymId, &indexLoopCountBasedBoundBaseSym))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Loop count based bound is assigned to s%u\n"),
indexLoopCountBasedBoundBaseSym->m_id);
indexLoopCountBasedBoundBaseValue = landingPadBlockData.FindValue(indexLoopCountBasedBoundBaseSym);
Assert(indexLoopCountBasedBoundBaseValue);
AssertVerify(
indexLoopCountBasedBoundBaseValue
->GetValueInfo()
->TryGetIntConstantBounds(&indexLoopCountBasedBoundBaseConstantBounds));
generateLoopCountBasedIndexBound = false;
break;
}
indexLoopCountBasedBoundBaseSym = StackSym::New(TyInt32, func);
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Assigning s%u to the loop count based bound\n"),
indexLoopCountBasedBoundBaseSym->m_id);
loopCountBasedBoundBaseSyms->Add(indexSymId, indexLoopCountBasedBoundBaseSym);
indexLoopCountBasedBoundBaseValue = NewValue(ValueInfo::New(alloc, ValueType::GetInt(true)));
landingPadBlockData.SetValue(indexLoopCountBasedBoundBaseValue, indexLoopCountBasedBoundBaseSym);
indexLoopCountBasedBoundBaseConstantBounds = IntConstantBounds(IntConstMin, IntConstMax);
generateLoopCountBasedIndexBound = true;
} while(false);
}
else
{
// The loop count is constant, fold (indexOffset + loopCountMinusOne * maxMagnitudeChange)
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Loop count is constant, folding\n"));
int loopCountMinusOnePlusOne = 0;
if (Int32Math::Add(loopCount->LoopCountMinusOneConstantValue(), 1, &loopCountMinusOnePlusOne) ||
Int32Math::Mul(loopCountMinusOnePlusOne, maxMagnitudeChange, &offset) ||
Int32Math::Add(offset, indexOffset, &offset))
{
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Folding failed\n"));
return;
}
if(!indexSymToAdd)
{
// The loop count based bound is constant
const int loopCountBasedConstantBound = offset;
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
3,
_u("Loop count based bound is constant: %d\n"),
loopCountBasedConstantBound);
if(maxMagnitudeChange < 0)
{
if(loopCountBasedConstantBound < 0)
{
// Can't use this bound
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Bound < 0\n"));
return;
}
lowerHoistInfo.SetLoop(currentLoop, loopCountBasedConstantBound, true);
return;
}
// loopCountBasedConstantBound >= headSegmentLength is currently not possible, except when
// loopCountBasedConstantBound == int32 max
Assert(
loopCountBasedConstantBound == IntConstMax ||
!ValueInfo::IsGreaterThanOrEqualTo(
nullptr,
loopCountBasedConstantBound,
loopCountBasedConstantBound,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound()));
// See if a compatible bound check is already available
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Looking for a compatible bound check\n"));
const IntBoundCheck *boundCheck;
if(currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
IntBoundCheckCompatibilityId(ZeroValueNumber, headSegmentLengthValue->GetValueNumber()),
&boundCheck))
{
// We need:
// loopCountBasedConstantBound < headSegmentLength
// Normalize the offset such that:
// 0 <= headSegmentLength + compatibleBoundCheckOffset
// Where (compatibleBoundCheckOffset = -1 - loopCountBasedConstantBound), and -1 is to simulate < instead of <=.
const int compatibleBoundCheckOffset = -1 - loopCountBasedConstantBound;
if(boundCheck->SetBoundOffset(compatibleBoundCheckOffset, true))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found in block %u\n"),
boundCheck->Block()->GetBlockNum());
upperHoistInfo.SetCompatibleBoundCheck(boundCheck->Block(), loopCountBasedConstantBound);
return;
}
failedToUpdateCompatibleUpperBoundCheck = true;
}
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Not found\n"));
upperHoistInfo.SetLoop(
currentLoop,
loopCountBasedConstantBound,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds,
true);
return;
}
// The loop count based bound is not constant; we need to add the offset of the index sym in the landing pad. Instead
// of adding though, we will treat the index sym as the loop count based bound base sym and adjust the offset that will
// be used in the bound check itself.
indexLoopCountBasedBoundBaseSym = indexSymToAdd;
indexLoopCountBasedBoundBaseValue = landingPadBlockData.FindValue(indexSymToAdd);
Assert(indexLoopCountBasedBoundBaseValue);
AssertVerify(
indexLoopCountBasedBoundBaseValue
->GetValueInfo()
->TryGetIntConstantBounds(&indexLoopCountBasedBoundBaseConstantBounds));
generateLoopCountBasedIndexBound = false;
}
if(maxMagnitudeChange >= 0)
{
// We need:
// indexLoopCountBasedBoundBase + indexOffset < headSegmentLength
// Normalize the offset such that:
// indexLoopCountBasedBoundBase <= headSegmentLength + offset
// Where (offset = -1 - indexOffset), and -1 is to simulate < instead of <=.
offset = -1 - offset;
}
if(!generateLoopCountBasedIndexBound)
{
if(maxMagnitudeChange < 0)
{
if(offset != IntConstMax &&
ValueInfo::IsGreaterThanOrEqualTo(
nullptr,
0,
0,
indexLoopCountBasedBoundBaseValue,
indexLoopCountBasedBoundBaseConstantBounds.LowerBound(),
indexLoopCountBasedBoundBaseConstantBounds.UpperBound(),
offset + 1)) // + 1 to simulate > instead of >=
{
// loopCountBasedBound < 0, can't use this bound
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Bound < 0\n"));
return;
}
}
else if(
ValueInfo::IsGreaterThanOrEqualTo(
indexLoopCountBasedBoundBaseValue,
indexLoopCountBasedBoundBaseConstantBounds.LowerBound(),
indexLoopCountBasedBoundBaseConstantBounds.UpperBound(),
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds.LowerBound(),
landingPadHeadSegmentLengthConstantBounds.UpperBound(),
offset))
{
// loopCountBasedBound >= headSegmentLength, can't use this bound
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Bound >= head segment length\n"));
return;
}
// See if a compatible bound check is already available
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 3, _u("Looking for a compatible bound check\n"));
const ValueNumber indexLoopCountBasedBoundBaseValueNumber = indexLoopCountBasedBoundBaseValue->GetValueNumber();
const IntBoundCheck *boundCheck;
if(currentBlock->globOptData.availableIntBoundChecks->TryGetReference(
maxMagnitudeChange < 0
? IntBoundCheckCompatibilityId(
ZeroValueNumber,
indexLoopCountBasedBoundBaseValueNumber)
: IntBoundCheckCompatibilityId(
indexLoopCountBasedBoundBaseValueNumber,
headSegmentLengthValue->GetValueNumber()),
&boundCheck))
{
if(boundCheck->SetBoundOffset(offset, true))
{
TRACE_PHASE_VERBOSE(
Js::Phase::BoundCheckHoistPhase,
4,
_u("Found in block %u\n"),
boundCheck->Block()->GetBlockNum());
if(maxMagnitudeChange < 0)
{
lowerHoistInfo.SetCompatibleBoundCheck(
boundCheck->Block(),
indexLoopCountBasedBoundBaseSym,
offset,
indexLoopCountBasedBoundBaseValueNumber);
}
else
{
upperHoistInfo.SetCompatibleBoundCheck(
boundCheck->Block(),
indexLoopCountBasedBoundBaseSym,
offset,
indexLoopCountBasedBoundBaseValueNumber);
}
return;
}
(maxMagnitudeChange < 0 ? failedToUpdateCompatibleLowerBoundCheck : failedToUpdateCompatibleUpperBoundCheck) = true;
}
TRACE_PHASE_VERBOSE(Js::Phase::BoundCheckHoistPhase, 4, _u("Not found\n"));
}
if(maxMagnitudeChange < 0)
{
lowerHoistInfo.SetLoop(
currentLoop,
indexLoopCountBasedBoundBaseSym,
indexOffset,
offset,
indexLoopCountBasedBoundBaseValue,
indexLoopCountBasedBoundBaseConstantBounds,
true);
if(generateLoopCountBasedIndexBound)
{
lowerHoistInfo.SetLoopCount(loopCount, maxMagnitudeChange);
}
return;
}
upperHoistInfo.SetLoop(
currentLoop,
indexLoopCountBasedBoundBaseSym,
indexOffset,
offset,
indexLoopCountBasedBoundBaseValue,
indexLoopCountBasedBoundBaseConstantBounds,
landingPadHeadSegmentLengthValue,
landingPadHeadSegmentLengthConstantBounds,
true);
if(generateLoopCountBasedIndexBound)
{
upperHoistInfo.SetLoopCount(loopCount, maxMagnitudeChange);
}
}
#if DBG
void
GlobOpt::EmitIntRangeChecks(IR::Instr* instr, IR::Opnd* opnd)
{
if (!opnd ||
(!opnd->IsRegOpnd() && !opnd->IsIndirOpnd()) ||
(opnd->IsIndirOpnd() && !opnd->AsIndirOpnd()->GetIndexOpnd()))
{
return;
}
IR::RegOpnd * regOpnd = opnd->IsRegOpnd() ? opnd->AsRegOpnd() : opnd->AsIndirOpnd()->GetIndexOpnd();
if (!(regOpnd->IsInt32() || regOpnd->IsUInt32()))
{
return;
}
StackSym * sym = regOpnd->GetStackSym();
if (sym->IsTypeSpec())
{
sym = sym->GetVarEquivSym_NoCreate();
}
Value * value = CurrentBlockData()->FindValue(sym);
if (!value)
{
return;
}
int32 lowerBound = INT_MIN;
int32 upperBound = INT_MAX;
if (value->GetValueInfo()->IsIntBounded())
{
lowerBound = value->GetValueInfo()->AsIntBounded()->Bounds()->ConstantLowerBound();
upperBound = value->GetValueInfo()->AsIntBounded()->Bounds()->ConstantUpperBound();
}
else if (value->GetValueInfo()->IsIntRange())
{
lowerBound = value->GetValueInfo()->AsIntRange()->LowerBound();
upperBound = value->GetValueInfo()->AsIntRange()->UpperBound();
}
else
{
return;
}
const auto EmitBoundCheck = [&](Js::OpCode opcode, int32 bound)
{
IR::Opnd * boundOpnd = IR::IntConstOpnd::New(bound, TyInt32, instr->m_func, true /*dontEncode*/);
IR::Instr * boundCheckInstr = IR::Instr::New(opcode, instr->m_func);
boundCheckInstr->SetSrc1(regOpnd);
boundCheckInstr->SetSrc2(boundOpnd);
instr->InsertBefore(boundCheckInstr);
};
if (lowerBound > INT_MIN)
{
EmitBoundCheck(Js::OpCode::CheckLowerIntBound, lowerBound);
}
if (upperBound < INT_MAX)
{
EmitBoundCheck(Js::OpCode::CheckUpperIntBound, upperBound);
}
}
void
GlobOpt::EmitIntRangeChecks(IR::Instr* instr)
{
// currently validating for dst only if its IndirOpnd
EmitIntRangeChecks(instr, instr->GetSrc1());
if (instr->GetDst()->IsIndirOpnd())
{
EmitIntRangeChecks(instr, instr->GetDst());
}
}
#endif