blob: c9de29454e5ca307e78e4ee8b166f2daf5288b09 [file]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "Backend.h"
uint8 OpCodeToHash[(uint)Js::OpCode::Count];
static Js::OpCode HashToOpCode[(uint)Js::OpCode::Count];
class CSEInit
{
public:
// Initializer for OpCodeToHash and HashToOpCode maps.
CSEInit()
{
uint8 hash = 1;
for (Js::OpCode opcode = (Js::OpCode)0; opcode < Js::OpCode::Count; opcode = (Js::OpCode)(opcode + 1))
{
if (Js::OpCodeUtil::IsValidOpcode(opcode) && OpCodeAttr::CanCSE(opcode) && !OpCodeAttr::ByteCodeOnly(opcode))
{
OpCodeToHash[(int)opcode] = hash;
HashToOpCode[hash] = opcode;
hash++;
AssertMsg(hash != 0, "Too many CSE'able opcodes");
}
}
}
}CSEInit_Dummy;
bool
GlobOpt::GetHash(IR::Instr *instr, Value *src1Val, Value *src2Val, ExprAttributes exprAttributes, ExprHash *pHash)
{
Js::OpCode opcode = instr->m_opcode;
// Candidate?
if (!OpCodeAttr::CanCSE(opcode) && (opcode != Js::OpCode::StElemI_A && opcode != Js::OpCode::StElemI_A_Strict))
{
return false;
}
ValueNumber valNum1 = 0;
ValueNumber valNum2 = 0;
// Get the value number of src1 and src2
if (instr->GetSrc1())
{
if (!src1Val)
{
return false;
}
valNum1 = src1Val->GetValueNumber();
if (instr->GetSrc2())
{
if (!src2Val)
{
return false;
}
valNum2 = src2Val->GetValueNumber();
}
}
if (src1Val)
{
valNum1 = src1Val->GetValueNumber();
if (src2Val)
{
valNum2 = src2Val->GetValueNumber();
}
}
switch (opcode)
{
case Js::OpCode::Ld_I4:
case Js::OpCode::Ld_A:
// Copy-prop should handle these
return false;
case Js::OpCode::Add_I4:
opcode = Js::OpCode::Add_A;
break;
case Js::OpCode::Sub_I4:
opcode = Js::OpCode::Sub_A;
break;
case Js::OpCode::Mul_I4:
opcode = Js::OpCode::Mul_A;
break;
case Js::OpCode::Rem_I4:
opcode = Js::OpCode::Rem_A;
break;
case Js::OpCode::Div_I4:
opcode = Js::OpCode::Div_A;
break;
case Js::OpCode::Neg_I4:
opcode = Js::OpCode::Neg_A;
break;
case Js::OpCode::Not_I4:
opcode = Js::OpCode::Not_A;
break;
case Js::OpCode::And_I4:
opcode = Js::OpCode::And_A;
break;
case Js::OpCode::Or_I4:
opcode = Js::OpCode::Or_A;
break;
case Js::OpCode::Xor_I4:
opcode = Js::OpCode::Xor_A;
break;
case Js::OpCode::Shl_I4:
opcode = Js::OpCode::Shl_A;
break;
case Js::OpCode::Shr_I4:
opcode = Js::OpCode::Shr_A;
break;
case Js::OpCode::ShrU_I4:
opcode = Js::OpCode::ShrU_A;
break;
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
// Make the load available as a CSE
opcode = Js::OpCode::LdElemI_A;
break;
case Js::OpCode::CmEq_I4:
opcode = Js::OpCode::CmEq_A;
break;
case Js::OpCode::CmNeq_I4:
opcode = Js::OpCode::CmNeq_A;
break;
case Js::OpCode::CmLt_I4:
opcode = Js::OpCode::CmLt_A;
break;
case Js::OpCode::CmLe_I4:
opcode = Js::OpCode::CmLe_A;
break;
case Js::OpCode::CmGt_I4:
opcode = Js::OpCode::CmGt_A;
break;
case Js::OpCode::CmGe_I4:
opcode = Js::OpCode::CmGe_A;
break;
case Js::OpCode::CmUnLt_I4:
opcode = Js::OpCode::CmUnLt_A;
break;
case Js::OpCode::CmUnLe_I4:
opcode = Js::OpCode::CmUnLe_A;
break;
case Js::OpCode::CmUnGt_I4:
opcode = Js::OpCode::CmUnGt_A;
break;
case Js::OpCode::CmUnGe_I4:
opcode = Js::OpCode::CmUnGe_A;
break;
}
pHash->Init(opcode, valNum1, valNum2, exprAttributes);
#if DBG_DUMP
if (!pHash->IsValid() && Js::Configuration::Global.flags.Trace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
{
Output::Print(_u(" >>>> CSE: Value numbers too big to be hashed in function %s!\n"), this->func->GetJITFunctionBody()->GetDisplayName());
}
#endif
#if ENABLE_DEBUG_CONFIG_OPTIONS
if (!pHash->IsValid() && Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
{
Output::Print(_u(" >>>> CSE: Value numbers too big to be hashed in function %s!\n"), this->func->GetJITFunctionBody()->GetDisplayName());
}
#endif
return pHash->IsValid();
}
void
GlobOpt::CSEAddInstr(
BasicBlock *block,
IR::Instr *instr,
Value *dstVal,
Value *src1Val,
Value *src2Val,
Value *dstIndirIndexVal,
Value *src1IndirIndexVal)
{
ExprAttributes exprAttributes;
ExprHash hash;
if (!this->DoCSE())
{
return;
}
bool isArray = false;
switch(instr->m_opcode)
{
case Js::OpCode::LdElemI_A:
case Js::OpCode::LdArrViewElem:
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
{
// For arrays, hash the value # of the baseOpnd and indexOpnd
IR::IndirOpnd *arrayOpnd;
Value *indirIndexVal;
if (instr->m_opcode == Js::OpCode::StElemI_A || instr->m_opcode == Js::OpCode::StElemI_A_Strict)
{
if (!this->CanCSEArrayStore(instr))
{
return;
}
dstVal = src1Val;
arrayOpnd = instr->GetDst()->AsIndirOpnd();
indirIndexVal = dstIndirIndexVal;
}
else
{
// all the LdElem and Ld*ArrViewElem
arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
indirIndexVal = src1IndirIndexVal;
}
src1Val = block->globOptData.FindValue(arrayOpnd->GetBaseOpnd()->m_sym);
if(indirIndexVal)
{
src2Val = indirIndexVal;
}
else if (arrayOpnd->GetIndexOpnd())
{
src2Val = block->globOptData.FindValue(arrayOpnd->GetIndexOpnd()->m_sym);
}
else
{
return;
}
isArray = true;
// for typed array do not add instructions whose dst are guaranteed to be int or number
// as we will try to eliminate bound check for these typed arrays
if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
{
exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
}
break;
}
case Js::OpCode::Mul_I4:
// If int32 overflow is ignored, we only add MULs with 53-bit overflow check to expr map
if (instr->HasBailOutInfo() && (instr->GetBailOutKind() & IR::BailOutOnMulOverflow) &&
!instr->ShouldCheckFor32BitOverflow() && instr->ignoreOverflowBitCount != 53)
{
return;
}
// fall-through
case Js::OpCode::Neg_I4:
case Js::OpCode::Add_I4:
case Js::OpCode::Sub_I4:
case Js::OpCode::DivU_I4:
case Js::OpCode::Div_I4:
case Js::OpCode::RemU_I4:
case Js::OpCode::Rem_I4:
case Js::OpCode::ShrU_I4:
{
// Can't CSE and Add where overflow doesn't matter (and no bailout) with one where it does matter... Record whether int
// overflow or negative zero were ignored.
exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
break;
}
case Js::OpCode::InlineMathFloor:
case Js::OpCode::InlineMathCeil:
case Js::OpCode::InlineMathRound:
if (!instr->ShouldCheckForNegativeZero())
{
return;
}
break;
case Js::OpCode::Conv_Prim:
exprAttributes = ConvAttributes(instr->GetDst()->IsUnsigned(), instr->GetSrc1()->IsUnsigned());
break;
}
ValueInfo *valueInfo = NULL;
if (instr->GetDst())
{
if (!dstVal)
{
return;
}
valueInfo = dstVal->GetValueInfo();
if(valueInfo->GetSymStore() == NULL &&
!(isArray && valueInfo->HasIntConstantValue() && valueInfo->IsIntAndLikelyTagged() && instr->GetSrc1()->IsAddrOpnd()))
{
return;
}
}
if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
{
return;
}
int32 intConstantValue;
if(valueInfo && !valueInfo->GetSymStore() && valueInfo->TryGetIntConstantValue(&intConstantValue))
{
Assert(isArray);
Assert(valueInfo->IsIntAndLikelyTagged());
Assert(instr->GetSrc1()->IsAddrOpnd());
// We need a sym associated with a value in the expression value table. Hoist the address into a stack sym associated
// with the int constant value.
StackSym *const constStackSym = GetOrCreateTaggedIntConstantStackSym(intConstantValue);
instr->HoistSrc1(Js::OpCode::Ld_A, RegNOREG, constStackSym);
currentBlock->globOptData.SetValue(dstVal, constStackSym);
}
// We have a candidate. Add it to the exprToValueMap.
Value ** pVal = block->globOptData.exprToValueMap->FindOrInsertNew(hash);
*pVal = dstVal;
if (isArray)
{
block->globOptData.liveArrayValues->Set(hash);
}
if (MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
{
this->currentBlock->globOptData.hasCSECandidates = true;
// Use LiveFields to track is object.valueOf/toString could get overridden.
IR::Opnd *src1 = instr->GetSrc1();
if (src1)
{
if (src1->IsRegOpnd())
{
StackSym *varSym = src1->AsRegOpnd()->m_sym;
if (varSym->IsTypeSpec())
{
varSym = varSym->GetVarEquivSym(this->func);
}
block->globOptData.liveFields->Set(varSym->m_id);
}
IR::Opnd *src2 = instr->GetSrc2();
if (src2 && src2->IsRegOpnd())
{
StackSym *varSym = src2->AsRegOpnd()->m_sym;
if (varSym->IsTypeSpec())
{
varSym = varSym->GetVarEquivSym(this->func);
}
block->globOptData.liveFields->Set(varSym->m_id);
}
}
}
}
static void TransformIntoUnreachable(IntConstType errorCode, IR::Instr* instr)
{
instr->m_opcode = Js::OpCode::Unreachable_Void;
instr->ReplaceSrc1(IR::IntConstOpnd::New(SCODE_CODE(errorCode), TyInt32, instr->m_func));
instr->UnlinkDst();
}
void
GlobOpt::OptimizeChecks(IR::Instr * const instr)
{
IR::Opnd* src1 = instr->GetSrc1();
IR::Opnd* src2 = instr->GetSrc2();
switch (instr->m_opcode)
{
case Js::OpCode::TrapIfZero:
if (src1 && src1->IsImmediateOpnd())
{
int64 val = src1->GetImmediateValue(func);
if (val != 0)
{
instr->m_opcode = Js::OpCode::Ld_I4;
}
else
{
TransformIntoUnreachable(WASMERR_DivideByZero, instr);
InsertByteCodeUses(instr);
RemoveCodeAfterNoFallthroughInstr(instr); //remove dead code
}
}
break;
case Js::OpCode::TrapIfMinIntOverNegOne:
{
int checksLeft = 2;
if (src1 && src1->IsImmediateOpnd())
{
int64 val = src1->GetImmediateValue(func);
bool isMintInt = src1->GetSize() == 8 ? val == LONGLONG_MIN : (int32)val == INT_MIN;
if (!isMintInt)
{
instr->m_opcode = Js::OpCode::Ld_I4;
}
else
{
checksLeft--;
}
}
if (src2 && src2->IsImmediateOpnd())
{
int64 val = src2->GetImmediateValue(func);
bool isNegOne = src2->GetSize() == 8 ? val == -1 : (int32)val == -1;
if (!isNegOne)
{
instr->m_opcode = Js::OpCode::Ld_I4;
}
else
{
checksLeft--;
}
}
if (!checksLeft)
{
TransformIntoUnreachable(VBSERR_Overflow, instr);
instr->FreeSrc2();
InsertByteCodeUses(instr);
RemoveCodeAfterNoFallthroughInstr(instr); //remove dead code
}
else if (instr->m_opcode == Js::OpCode::Ld_I4)
{
instr->FreeSrc2();
}
break;
}
default:
return;
}
}
bool
GlobOpt::CSEOptimize(BasicBlock *block, IR::Instr * *const instrRef, Value **pSrc1Val, Value **pSrc2Val, Value **pSrc1IndirIndexVal, bool intMathExprOnly)
{
Assert(instrRef);
IR::Instr *&instr = *instrRef;
Assert(instr);
if (!this->DoCSE())
{
return false;
}
Value *src1Val = *pSrc1Val;
Value *src2Val = *pSrc2Val;
Value *src1IndirIndexVal = *pSrc1IndirIndexVal;
bool isArray = false;
ExprAttributes exprAttributes;
ExprHash hash;
// For arrays, hash the value # of the baseOpnd and indexOpnd
switch(instr->m_opcode)
{
case Js::OpCode::LdArrViewElem:
case Js::OpCode::LdElemI_A:
{
if(intMathExprOnly)
{
return false;
}
IR::IndirOpnd *arrayOpnd = instr->GetSrc1()->AsIndirOpnd();
src1Val = block->globOptData.FindValue(arrayOpnd->GetBaseOpnd()->m_sym);
if(src1IndirIndexVal)
{
src2Val = src1IndirIndexVal;
}
else if (arrayOpnd->GetIndexOpnd())
{
src2Val = block->globOptData.FindValue(arrayOpnd->GetIndexOpnd()->m_sym);
}
else
{
return false;
}
// for typed array do not add instructions whose dst are guaranteed to be int or number
// as we will try to eliminate bound check for these typed arrays
if (src1Val->GetValueInfo()->IsLikelyOptimizedVirtualTypedArray())
{
exprAttributes = DstIsIntOrNumberAttributes(!instr->dstIsAlwaysConvertedToInt32, !instr->dstIsAlwaysConvertedToNumber);
}
isArray = true;
break;
}
case Js::OpCode::Neg_A:
case Js::OpCode::Add_A:
case Js::OpCode::Sub_A:
case Js::OpCode::Mul_A:
case Js::OpCode::Div_A:
case Js::OpCode::Rem_A:
case Js::OpCode::ShrU_A:
// If the previously-computed matching expression ignored int overflow or negative zero, those attributes must match
// to be able to CSE this expression
if(intMathExprOnly && !ignoredIntOverflowForCurrentInstr && !ignoredNegativeZeroForCurrentInstr)
{
// Already tried CSE with default attributes
return false;
}
exprAttributes = IntMathExprAttributes(ignoredIntOverflowForCurrentInstr, ignoredNegativeZeroForCurrentInstr);
break;
case Js::OpCode::Conv_Prim:
exprAttributes = ConvAttributes(instr->GetDst()->IsUnsigned(), instr->GetSrc1()->IsUnsigned());
break;
default:
if(intMathExprOnly)
{
return false;
}
break;
}
if (!this->GetHash(instr, src1Val, src2Val, exprAttributes, &hash))
{
return false;
}
// See if we have a value for that expression
Value ** pVal = block->globOptData.exprToValueMap->Get(hash);
if (pVal == NULL)
{
return false;
}
ValueInfo *valueInfo = NULL;
Sym *symStore = NULL;
Value *val = NULL;
if (instr->GetDst())
{
if (*pVal == NULL)
{
return false;
}
val = *pVal;
// Make sure the array value is still live. We can't CSE something like:
// ... = A[i];
// B[j] = ...;
// ... = A[i];
if (isArray && !block->globOptData.liveArrayValues->Test(hash))
{
return false;
}
// See if the symStore is still valid
valueInfo = val->GetValueInfo();
symStore = valueInfo->GetSymStore();
Value * symStoreVal = NULL;
int32 intConstantValue;
if (!symStore && valueInfo->TryGetIntConstantValue(&intConstantValue))
{
// Handle:
// A[i] = 10;
// ... = A[i];
if (!isArray)
{
return false;
}
if (!valueInfo->IsIntAndLikelyTagged())
{
return false;
}
symStore = GetTaggedIntConstantStackSym(intConstantValue);
}
if(!symStore || !symStore->IsStackSym())
{
return false;
}
symStoreVal = block->globOptData.FindValue(symStore);
if (!symStoreVal || symStoreVal->GetValueNumber() != val->GetValueNumber())
{
return false;
}
val = symStoreVal;
valueInfo = val->GetValueInfo();
}
// Make sure srcs still have same values
if (instr->GetSrc1())
{
if (!src1Val)
{
return false;
}
if (hash.GetSrc1ValueNumber() != src1Val->GetValueNumber())
{
return false;
}
IR::Opnd *src1 = instr->GetSrc1();
if (src1->IsSymOpnd() && src1->AsSymOpnd()->IsPropertySymOpnd())
{
Assert(instr->m_opcode == Js::OpCode::CheckFixedFld);
IR::PropertySymOpnd *propOpnd = src1->AsSymOpnd()->AsPropertySymOpnd();
if (!propOpnd->IsTypeChecked() && !propOpnd->IsRootObjectNonConfigurableFieldLoad())
{
// Require m_CachedTypeChecked for 2 reasons:
// - We may be relying on this instruction to do a type check for a downstream reference.
// - This instruction may have a different inline cache, and thus need to check a different type,
// than the upstream check.
// REVIEW: We could process this differently somehow to get the type check on the next reference.
return false;
}
}
if (instr->GetSrc2())
{
if (!src2Val)
{
return false;
}
if (hash.GetSrc2ValueNumber() != src2Val->GetValueNumber())
{
return false;
}
}
}
bool needsBailOnImplicitCall = false;
// Need implicit call bailouts?
if (this->MayNeedBailOnImplicitCall(instr, src1Val, src2Val))
{
needsBailOnImplicitCall = true;
IR::Opnd *src1 = instr->GetSrc1();
if (instr->m_opcode != Js::OpCode::StElemI_A && instr->m_opcode != Js::OpCode::StElemI_A_Strict
&& src1 && src1->IsRegOpnd())
{
StackSym *sym1 = src1->AsRegOpnd()->m_sym;
if (block->globOptData.IsTypeSpecialized(sym1) || block->globOptData.liveInt32Syms->Test(sym1->m_id))
{
IR::Opnd *src2 = instr->GetSrc2();
if (!src2 || src2->IsImmediateOpnd())
{
needsBailOnImplicitCall = false;
}
else if (src2->IsRegOpnd())
{
StackSym *sym2 = src2->AsRegOpnd()->m_sym;
if (block->globOptData.IsTypeSpecialized(sym2) || block->globOptData.liveInt32Syms->Test(sym2->m_id))
{
needsBailOnImplicitCall = false;
}
}
}
}
}
if (needsBailOnImplicitCall)
{
IR::Opnd *src1 = instr->GetSrc1();
if (src1)
{
if (src1->IsRegOpnd())
{
StackSym *varSym1 = src1->AsRegOpnd()->m_sym;
Assert(!varSym1->IsTypeSpec());
if (!block->globOptData.liveFields->Test(varSym1->m_id))
{
return false;
}
IR::Opnd *src2 = instr->GetSrc2();
if (src2 && src2->IsRegOpnd())
{
StackSym *varSym2 = src2->AsRegOpnd()->m_sym;
Assert(!varSym2->IsTypeSpec());
if (!block->globOptData.liveFields->Test(varSym2->m_id))
{
return false;
}
}
}
}
}
// in asmjs we can have a symstore with a different type
// x = HEAPF32[i >> 2]
// y = HEAPI32[i >> 2]
if (instr->GetDst() && (instr->GetDst()->GetType() != symStore->AsStackSym()->GetType()))
{
Assert(GetIsAsmJSFunc());
return false;
}
// SIMD_JS
if (instr->m_opcode == Js::OpCode::ExtendArg_A)
{
// we don't want to CSE ExtendArgs, only the operation using them. To do that, we mimic CSE by transferring the symStore valueInfo to the dst.
IR::Opnd *dst = instr->GetDst();
Value *dstVal = this->currentBlock->globOptData.FindValue(symStore);
this->currentBlock->globOptData.SetValue(dstVal, dst);
dst->AsRegOpnd()->m_sym->CopySymAttrs(symStore->AsStackSym());
return false;
}
//
// Success, do the CSE rewrite.
//
#if DBG_DUMP
if (Js::Configuration::Global.flags.Trace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
{
Output::Print(_u(" --- CSE (%s): "), this->func->GetJITFunctionBody()->GetDisplayName());
instr->Dump();
}
#endif
#if ENABLE_DEBUG_CONFIG_OPTIONS
if (Js::Configuration::Global.flags.TestTrace.IsEnabled(Js::CSEPhase, this->func->GetSourceContextId(), this->func->GetLocalFunctionId()))
{
Output::Print(_u(" --- CSE (%s): %s\n"), this->func->GetJITFunctionBody()->GetDisplayName(), Js::OpCodeUtil::GetOpCodeName(instr->m_opcode));
}
#endif
this->CaptureByteCodeSymUses(instr);
if (!instr->GetDst())
{
instr->m_opcode = Js::OpCode::Nop;
return true;
}
AnalysisAssert(valueInfo);
IR::Opnd *cseOpnd;
cseOpnd = IR::RegOpnd::New(symStore->AsStackSym(), instr->GetDst()->GetType(), instr->m_func);
cseOpnd->SetValueType(valueInfo->Type());
cseOpnd->SetIsJITOptimizedReg(true);
if (needsBailOnImplicitCall)
{
this->CaptureNoImplicitCallUses(cseOpnd, false);
}
int32 intConstantValue;
if (valueInfo->TryGetIntConstantValue(&intConstantValue) && valueInfo->IsIntAndLikelyTagged())
{
cseOpnd->Free(func);
cseOpnd = IR::AddrOpnd::New(Js::TaggedInt::ToVarUnchecked(intConstantValue), IR::AddrOpndKindConstantVar, instr->m_func);
cseOpnd->SetValueType(valueInfo->Type());
}
*pSrc1Val = val;
{
// Profiled instructions have data that is interpreted differently based on the op code. Since we're changing the op
// code and due to other similar potential issues, always create a new instr instead of changing the existing one.
IR::Instr *const originalInstr = instr;
instr = IR::Instr::New(Js::OpCode::Ld_A, instr->GetDst(), cseOpnd, instr->m_func);
originalInstr->TransferDstAttributesTo(instr);
block->InsertInstrBefore(instr, originalInstr);
block->RemoveInstr(originalInstr);
}
*pSrc2Val = NULL;
*pSrc1IndirIndexVal = NULL;
return true;
}
void
GlobOpt::ProcessArrayValueKills(IR::Instr *instr)
{
switch (instr->m_opcode)
{
case Js::OpCode::StElemI_A:
case Js::OpCode::StElemI_A_Strict:
case Js::OpCode::DeleteElemI_A:
case Js::OpCode::DeleteElemIStrict_A:
case Js::OpCode::StFld:
case Js::OpCode::StRootFld:
case Js::OpCode::StFldStrict:
case Js::OpCode::StRootFldStrict:
case Js::OpCode::StSlot:
case Js::OpCode::StSlotChkUndecl:
case Js::OpCode::DeleteFld:
case Js::OpCode::DeleteRootFld:
case Js::OpCode::DeleteFldStrict:
case Js::OpCode::DeleteRootFldStrict:
case Js::OpCode::StArrViewElem:
// These array helpers may change A.length (and A[i] could be A.length)...
case Js::OpCode::InlineArrayPush:
case Js::OpCode::InlineArrayPop:
this->currentBlock->globOptData.liveArrayValues->ClearAll();
break;
case Js::OpCode::CallDirect:
Assert(instr->GetSrc1());
switch(instr->GetSrc1()->AsHelperCallOpnd()->m_fnHelper)
{
// These array helpers may change A[i]
case IR::HelperArray_Reverse:
case IR::HelperArray_Shift:
case IR::HelperArray_Unshift:
case IR::HelperArray_Splice:
this->currentBlock->globOptData.liveArrayValues->ClearAll();
break;
}
break;
default:
if (instr->UsesAllFields())
{
this->currentBlock->globOptData.liveArrayValues->ClearAll();
}
break;
}
}
bool
GlobOpt::NeedBailOnImplicitCallForCSE(BasicBlock const *block, bool isForwardPass)
{
return isForwardPass && block->globOptData.hasCSECandidates;
}
bool
GlobOpt::DoCSE()
{
if (PHASE_OFF(Js::CSEPhase, this->func))
{
return false;
}
if (this->IsLoopPrePass())
{
return false;
}
if (PHASE_FORCE(Js::CSEPhase, this->func))
{
// Force always turn it on
return true;
}
if (!this->DoFieldOpts(this->currentBlock->loop) && !GetIsAsmJSFunc())
{
return false;
}
return true;
}
bool
GlobOpt::CanCSEArrayStore(IR::Instr *instr)
{
IR::Opnd *arrayOpnd = instr->GetDst();
Assert(arrayOpnd->IsIndirOpnd());
IR::RegOpnd *baseOpnd = arrayOpnd->AsIndirOpnd()->GetBaseOpnd();
ValueType baseValueType(baseOpnd->GetValueType());
// Only handle definite arrays for now. Typed Arrays would require truncation of the CSE'd value.
if (!baseValueType.IsArrayOrObjectWithArray())
{
return false;
}
return true;
}
#if DBG_DUMP
void
DumpExpr(ExprHash hash)
{
Output::Print(_u("Opcode: %10s src1Val: %d src2Val: %d\n"), Js::OpCodeUtil::GetOpCodeName(HashToOpCode[(int)hash.GetOpcode()]), hash.GetSrc1ValueNumber(), hash.GetSrc2ValueNumber());
}
#endif