blob: 7266cfaf413c9a88bdca157ed67f7027d886bda4 [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// 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.
//-------------------------------------------------------------------------------------------------------
#pragma once
class ExprAttributes
{
protected:
uint32 attributes;
public:
ExprAttributes(const uint32 attributes = 0) : attributes(attributes)
{
}
uint32 Attributes() const
{
return attributes;
}
private:
static uint32 BitMask(const uint index)
{
return 1u << index;
}
protected:
void SetBitAttribute(const uint index, const bool bit)
{
if(bit)
{
attributes |= BitMask(index);
}
else
{
attributes &= ~BitMask(index);
}
}
};
class ExprHash
{
public:
ExprHash() { this->opcode = 0; }
ExprHash(int init) { Assert(init == 0); this->opcode = 0; }
void Init(Js::OpCode opcode, ValueNumber src1Val, ValueNumber src2Val, ExprAttributes exprAttributes)
{
extern uint8 OpCodeToHash[(int)Js::OpCode::Count];
uint32 opCodeHash = OpCodeToHash[(int)opcode];
this->opcode = opCodeHash;
this->src1Val = src1Val;
this->src2Val = src2Val;
this->attributes = exprAttributes.Attributes();
// Assert too many opcodes...
AssertMsg(this->opcode == (uint32)opCodeHash, "Opcode value too large for CSEs");
AssertMsg(this->attributes == exprAttributes.Attributes(), "Not enough bits for expr attributes");
// If value numbers are too large, just give up
if (this->src1Val != src1Val || this->src2Val != src2Val)
{
this->opcode = 0;
this->src1Val = 0;
this->src2Val = 0;
this->attributes = 0;
}
}
Js::OpCode GetOpcode() { return (Js::OpCode)this->opcode; }
ValueNumber GetSrc1ValueNumber() { return this->src1Val; }
ValueNumber GetSrc2ValueNumber() { return this->src2Val; }
ExprAttributes GetExprAttributes() { return this->attributes; }
bool IsValid() { return this->opcode != 0; }
operator uint() { return *(uint*)this; }
private:
uint32 opcode: 8;
uint32 src1Val: 11;
uint32 src2Val: 11;
uint32 attributes: 2;
};
#include "GlobHashTable.h"
typedef ValueHashTable<Sym *, Value *> GlobHashTable;
typedef HashBucket<Sym *, Value *> GlobHashBucket;
typedef ValueHashTable<ExprHash, Value *> ExprHashTable;
typedef HashBucket<ExprHash, Value *> ExprHashBucket;
typedef JsUtil::BaseHashSet<Value *, JitArenaAllocator> ValueSet;
struct StackLiteralInitFldData
{
const Js::PropertyIdArray * propIds;
uint currentInitFldCount;
};
typedef JsUtil::BaseDictionary<StackSym *, StackLiteralInitFldData, JitArenaAllocator> StackLiteralInitFldDataMap;
typedef SList<GlobHashBucket*, JitArenaAllocator> PRECandidatesList;
class PRECandidates
{
public:
PRECandidates() : candidatesToProcess(nullptr), candidatesBv(nullptr), candidatesList(nullptr) {}
public:
BVSparse<JitArenaAllocator> * candidatesToProcess;
BVSparse<JitArenaAllocator> * candidatesBv;
PRECandidatesList * candidatesList;
};
class GlobOptBlockData
{
friend class BasicBlock;
public:
GlobOptBlockData(Func *func) :
symToValueMap(nullptr),
exprToValueMap(nullptr),
liveFields(nullptr),
liveArrayValues(nullptr),
maybeWrittenTypeSyms(nullptr),
liveVarSyms(nullptr),
liveInt32Syms(nullptr),
liveLossyInt32Syms(nullptr),
liveFloat64Syms(nullptr),
argObjSyms(nullptr),
maybeTempObjectSyms(nullptr),
canStoreTempObjectSyms(nullptr),
valuesToKillOnCalls(nullptr),
inductionVariables(nullptr),
availableIntBoundChecks(nullptr),
startCallCount(0),
argOutCount(0),
totalOutParamCount(0),
callSequence(nullptr),
capturedValuesCandidate(nullptr),
capturedValues(nullptr),
changedSyms(nullptr),
hasCSECandidates(false),
curFunc(func),
hasDataRef(nullptr),
stackLiteralInitFldDataMap(nullptr),
globOpt(nullptr)
{
}
// Data
GlobHashTable * symToValueMap;
ExprHashTable * exprToValueMap;
BVSparse<JitArenaAllocator> * liveFields;
BVSparse<JitArenaAllocator> * liveArrayValues;
BVSparse<JitArenaAllocator> * maybeWrittenTypeSyms;
BVSparse<JitArenaAllocator> * isTempSrc;
BVSparse<JitArenaAllocator> * liveVarSyms;
BVSparse<JitArenaAllocator> * liveInt32Syms;
// 'liveLossyInt32Syms' includes only syms that contain an int value that may not fully represent the value of the
// equivalent var sym. The set (liveInt32Syms - liveLossyInt32Syms) includes only syms that contain an int value that fully
// represents the value of the equivalent var sym, such as when (a + 1) is type-specialized. Among other things, this
// bit-vector is used, based on the type of conversion that is needed, to determine whether conversion is necessary, and if
// so, whether a bailout is needed. For instance, after type-specializing (a | 0), the int32 sym of 'a' cannot be reused in
// (a + 1) during type-specialization. It needs to be converted again using a lossless conversion with a bailout.
// Conversely, a lossless int32 sym can be reused to avoid a lossy conversion.
BVSparse<JitArenaAllocator> * liveLossyInt32Syms;
BVSparse<JitArenaAllocator> * liveFloat64Syms;
BVSparse<JitArenaAllocator> * argObjSyms;
BVSparse<JitArenaAllocator> * maybeTempObjectSyms;
BVSparse<JitArenaAllocator> * canStoreTempObjectSyms;
// This is the func that this block comes from, so the inlinee if from an inlined function
Func * curFunc;
// 'valuesToKillOnCalls' includes values whose value types need to be killed upon a call. Upon a call, the value types of
// values in the set are updated and removed from the set as appropriate.
ValueSet * valuesToKillOnCalls;
InductionVariableSet * inductionVariables;
IntBoundCheckSet * availableIntBoundChecks;
// Bailout data
uint startCallCount;
uint argOutCount;
uint totalOutParamCount;
SListBase<IR::Opnd *> * callSequence;
StackLiteralInitFldDataMap * stackLiteralInitFldDataMap;
CapturedValues * capturedValuesCandidate;
CapturedValues * capturedValues;
BVSparse<JitArenaAllocator> * changedSyms;
uint inlinedArgOutSize;
bool hasCSECandidates;
private:
bool * hasDataRef;
GlobOpt * globOpt;
public:
void OnDataInitialized(JitArenaAllocator *const allocator)
{
Assert(allocator);
hasDataRef = JitAnew(allocator, bool, true);
}
void OnDataReused(GlobOptBlockData *const fromData)
{
// If a block's data is deleted, *hasDataRef will be set to false. Since these two blocks are pointing to the same data,
// they also need to point to the same has-data info.
hasDataRef = fromData->hasDataRef;
}
void OnDataUnreferenced()
{
// Other blocks may still be using the data, we should only un-reference the previous data
hasDataRef = nullptr;
}
void OnDataDeleted()
{
if (hasDataRef)
*hasDataRef = false;
OnDataUnreferenced();
}
bool HasData()
{
if (!hasDataRef)
return false;
if (*hasDataRef)
return true;
OnDataUnreferenced();
return false;
}
// Functions pulled out of GlobOpt.cpp
// Initialization/copying/moving
public:
void NullOutBlockData(GlobOpt* globOpt, Func* func);
void InitBlockData(GlobOpt* globOpt, Func* func);
void ReuseBlockData(GlobOptBlockData *fromData);
void CopyBlockData(GlobOptBlockData *fromData);
void DeleteBlockData();
void CloneBlockData(BasicBlock *const toBlock, BasicBlock *const fromBlock);
public:
void RemoveUnavailableCandidates(PRECandidates *candidates);
// Merging functions
public:
void MergeBlockData(BasicBlock *toBlockContext, BasicBlock *fromBlock, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge, bool forceTypeSpecOnLoopHeader);
private:
template <typename CaptureList, typename CapturedItemsAreEqual>
void MergeCapturedValues(SListBase<CaptureList>* toList, SListBase<CaptureList> * fromList, CapturedItemsAreEqual itemsAreEqual);
void MergeValueMaps(BasicBlock *toBlock, BasicBlock *fromBlock, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge);
Value * MergeValues(Value *toDataValue, Value *fromDataValue, Sym *fromDataSym, bool isLoopBackEdge, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge);
ValueInfo * MergeValueInfo(Value *toDataVal, Value *fromDataVal, Sym *fromDataSym, bool isLoopBackEdge, bool sameValueNumber, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge);
JsTypeValueInfo * MergeJsTypeValueInfo(JsTypeValueInfo * toValueInfo, JsTypeValueInfo * fromValueInfo, bool isLoopBackEdge, bool sameValueNumber);
ValueInfo * MergeArrayValueInfo(const ValueType mergedValueType, const ArrayValueInfo *const toDataValueInfo, const ArrayValueInfo *const fromDataValueInfo, Sym *const arraySym, BVSparse<JitArenaAllocator> *const symsRequiringCompensation, BVSparse<JitArenaAllocator> *const symsCreatedForMerge);
// Argument Tracking
public:
void TrackArgumentsSym(IR::RegOpnd const* opnd);
void ClearArgumentsSym(IR::RegOpnd const* opnd);
BOOL TestAnyArgumentsSym() const;
BOOL IsArgumentsSymID(SymID id) const;
BOOL IsArgumentsOpnd(IR::Opnd const* opnd) const;
private:
// Value Tracking
public:
Value * FindValue(Sym *sym);
Value * FindValueFromMapDirect(SymID symId);
Value * FindPropertyValue(SymID symId);
Value * FindObjectTypeValue(StackSym* typeSym);
Value * FindObjectTypeValue(SymID typeSymId);
Value * FindObjectTypeValueNoLivenessCheck(StackSym* typeSym);
Value * FindObjectTypeValueNoLivenessCheck(SymID typeSymId);
Value * FindFuturePropertyValue(PropertySym *const propertySym);
StackSym * GetCopyPropSym(Sym * sym, Value * val);
Value * InsertNewValue(Value *val, IR::Opnd *opnd);
Value * SetValue(Value *val, IR::Opnd *opnd);
void SetValue(Value *val, Sym * sym);
void ClearSymValue(Sym *sym);
private:
void SetValueToHashTable(GlobHashTable * valueNumberMap, Value *val, Sym *sym);
// Temp Tracking
public:
void MarkTempLastUse(IR::Instr *instr, IR::RegOpnd *regOpnd);
private:
// Liveness Tracking
public:
void MakeLive(StackSym *sym, const bool lossy);
// Checks if the symbol is live in this block under any type
bool IsLive(Sym const * sym) const;
bool IsTypeSpecialized(Sym const * sym) const;
bool IsSwitchInt32TypeSpecialized(IR::Instr const * instr) const;
bool IsInt32TypeSpecialized(Sym const * sym) const;
bool IsFloat64TypeSpecialized(Sym const * sym) const;
// Changed Symbol Tracking
public:
void SetChangedSym(SymID symId);
void SetChangedSym(Sym* sym);
private:
// Other
public:
void KillStateForGeneratorYield();
// Debug
public:
void DumpSymToValueMap() const;
private:
static void DumpSym(Sym *sym);
};