blob: e3d6466ce116075119c326ff2454ae87fc115477 [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.
//-------------------------------------------------------------------------------------------------------
#pragma once
namespace IR {
inline IRKind
Instr::GetKind() const
{
return this->m_kind;
}
///----------------------------------------------------------------------------
///
/// Instr::IsEntryInstr
///
///----------------------------------------------------------------------------
inline bool
Instr::IsEntryInstr() const
{
return this->GetKind() == InstrKindEntry;
}
///----------------------------------------------------------------------------
///
/// Instr::AsEntryInstr
///
/// Return this as an EntryInstr *
///
///----------------------------------------------------------------------------
inline EntryInstr *
Instr::AsEntryInstr()
{
AssertMsg(this->IsEntryInstr(), "Bad call to AsEntryInstr()");
return reinterpret_cast<EntryInstr *>(this);
}
///----------------------------------------------------------------------------
///
/// Instr::IsExitInstr
///
///----------------------------------------------------------------------------
inline bool
Instr::IsExitInstr() const
{
return this->GetKind() == InstrKindExit;
}
///----------------------------------------------------------------------------
///
/// Instr::AsExitInstr
///
/// Return this as an ExitInstr *
///
///----------------------------------------------------------------------------
inline ExitInstr *
Instr::AsExitInstr()
{
AssertMsg(this->IsExitInstr(), "Bad call to AsExitInstr()");
return reinterpret_cast<ExitInstr *>(this);
}
///----------------------------------------------------------------------------
///
/// Instr::IsBranchInstr
///
///----------------------------------------------------------------------------
inline bool
Instr::IsBranchInstr() const
{
return this->GetKind() == InstrKindBranch;
}
///----------------------------------------------------------------------------
///
/// Instr::AsBranchInstr
///
/// Return this as a BranchInstr *
///
///----------------------------------------------------------------------------
inline BranchInstr *
Instr::AsBranchInstr()
{
AssertMsg(this->IsBranchInstr(), "Bad call to AsBranchInstr()");
return reinterpret_cast<BranchInstr *>(this);
}
///----------------------------------------------------------------------------
///
/// Instr::IsLabelInstr
///
///----------------------------------------------------------------------------
__forceinline bool
Instr::IsLabelInstr() const
{
return this->GetKind() == InstrKindLabel || this->IsProfiledLabelInstr();
}
///----------------------------------------------------------------------------
///
/// Instr::AsLabelInstr
///
/// Return this as a LabelInstr *
///
///----------------------------------------------------------------------------
inline LabelInstr *
Instr::AsLabelInstr()
{
AssertMsg(this->IsLabelInstr(), "Bad call to AsLabelInstr()");
return reinterpret_cast<LabelInstr *>(this);
}
///----------------------------------------------------------------------------
///
/// Instr::AsMultiBrInstr
///
/// Return this as a MultiBrInstr *
///
///----------------------------------------------------------------------------
inline MultiBranchInstr *
BranchInstr::AsMultiBrInstr()
{
AssertMsg(this->IsMultiBranch(), "Bad call to AsMultiBrInstr()");
return static_cast<MultiBranchInstr *>(this);
}
///----------------------------------------------------------------------------
///
/// Instr::IsPragmaInstr
///
///----------------------------------------------------------------------------
inline bool
Instr::IsPragmaInstr() const
{
return this->GetKind() == InstrKindPragma;
}
inline PragmaInstr *
Instr::AsPragmaInstr()
{
AssertMsg(this->IsPragmaInstr(), "Bad call to AsPragmaInstr()");
return reinterpret_cast<PragmaInstr *>(this);
}
inline bool
Instr::IsJitProfilingInstr() const
{
return this->GetKind() == InstrKindJitProfiling;
}
inline JitProfilingInstr *
Instr::AsJitProfilingInstr()
{
AssertMsg(this->IsJitProfilingInstr(), "Bad call to AsProfiledInstr()");
return reinterpret_cast<JitProfilingInstr *>(this);
}
inline bool
Instr::IsProfiledInstr() const
{
return this->GetKind() == InstrKindProfiled;
}
inline ProfiledInstr *
Instr::AsProfiledInstr()
{
AssertMsg(this->IsProfiledInstr(), "Bad call to AsProfiledInstr()");
return reinterpret_cast<ProfiledInstr *>(this);
}
inline bool
Instr::IsProfiledLabelInstr() const
{
return this->GetKind() == InstrKindProfiledLabel;
}
inline ProfiledLabelInstr *
Instr::AsProfiledLabelInstr()
{
AssertMsg(this->IsProfiledLabelInstr(), "Bad call to AsProfiledLabelInstr()");
return reinterpret_cast<ProfiledLabelInstr *>(this);
}
inline bool
Instr::IsByteCodeUsesInstr() const
{
return GetKind() == IR::InstrKindByteCodeUses && m_opcode == Js::OpCode::ByteCodeUses;
}
inline ByteCodeUsesInstr *
Instr::AsByteCodeUsesInstr()
{
AssertMsg(this->IsByteCodeUsesInstr(), "Bad call to AsByteCodeUsesInstr()");
return (ByteCodeUsesInstr *)this;
}
///----------------------------------------------------------------------------
///
/// Instr::IsLowered
///
/// Is this instr lowered to machine dependent opcode?
///
///----------------------------------------------------------------------------
inline bool
Instr::IsLowered() const
{
return m_opcode > Js::OpCode::MDStart;
}
///----------------------------------------------------------------------------
///
/// Instr::StartsBasicBlock
///
/// Does this instruction mark the beginning of a basic block?
///
///----------------------------------------------------------------------------
inline bool
Instr::StartsBasicBlock() const
{
return this->IsLabelInstr() || this->IsEntryInstr();
}
///----------------------------------------------------------------------------
///
/// Instr::EndsBasicBlock
///
/// Does this instruction mark the end of a basic block?
///
///----------------------------------------------------------------------------
inline bool
Instr::EndsBasicBlock() const
{
return
this->IsBranchInstr() ||
this->IsExitInstr() ||
this->m_opcode == Js::OpCode::Ret ||
this->m_opcode == Js::OpCode::Throw ||
this->m_opcode == Js::OpCode::RuntimeTypeError ||
this->m_opcode == Js::OpCode::RuntimeReferenceError;
}
///----------------------------------------------------------------------------
///
/// Instr::HasFallThrough
///
/// Can execution fall through to the next instruction?
///
///----------------------------------------------------------------------------
inline bool
Instr::HasFallThrough() const
{
return (!(this->IsBranchInstr() && const_cast<Instr*>(this)->AsBranchInstr()->IsUnconditional())
&& OpCodeAttr::HasFallThrough(this->m_opcode));
}
inline bool
Instr::IsNewScObjectInstr() const
{
return this->m_opcode == Js::OpCode::NewScObject || this->m_opcode == Js::OpCode::NewScObjectNoCtor;
}
inline bool
Instr::IsInvalidInstr() const
{
return this == (Instr*)InvalidInstrLayout;
}
inline Instr*
Instr::GetInvalidInstr()
{
return (Instr*)InvalidInstrLayout;
}
///----------------------------------------------------------------------------
///
/// Instr::GetDst
///
///----------------------------------------------------------------------------
inline Opnd *
Instr::GetDst() const
{
return this->m_dst;
}
///----------------------------------------------------------------------------
///
/// Instr::GetSrc1
///
///----------------------------------------------------------------------------
inline Opnd *
Instr::GetSrc1() const
{
return this->m_src1;
}
///----------------------------------------------------------------------------
///
/// Instr::SetSrc1
///
/// Makes a copy if opnd is in use...
///
///----------------------------------------------------------------------------
inline Opnd *
Instr::SetSrc1(Opnd * newSrc)
{
AssertMsg(this->m_src1 == NULL, "Trying to overwrite existing src.");
newSrc = newSrc->Use(m_func);
this->m_src1 = newSrc;
return newSrc;
}
///----------------------------------------------------------------------------
///
/// Instr::GetSrc2
///
///----------------------------------------------------------------------------
inline Opnd *
Instr::GetSrc2() const
{
return this->m_src2;
}
///----------------------------------------------------------------------------
///
/// Instr::SetSrc2
///
/// Makes a copy if opnd is in use...
///
///----------------------------------------------------------------------------
inline Opnd *
Instr::SetSrc2(Opnd * newSrc)
{
AssertMsg(this->m_src2 == NULL, "Trying to overwrite existing src.");
newSrc = newSrc->Use(m_func);
this->m_src2 = newSrc;
return newSrc;
}
// IsInlined - true if the function that contains the instr has been inlined
inline bool
Instr::IsInlined() const
{
return this->m_func->IsInlined();
}
///----------------------------------------------------------------------------
///
/// Instr::ForEachCallDirectArgOutInstrBackward
///
/// Walks the ArgsOut of CallDirect backwards
///
///----------------------------------------------------------------------------
template <typename Fn>
inline bool
Instr::ForEachCallDirectArgOutInstrBackward(Fn fn, uint argsOpndLength) const
{
Assert(this->m_opcode == Js::OpCode::CallDirect); // Right now we call this method only for partial inlining of split, match and exec. Can be modified for other uses also.
// CallDirect src2
IR::Opnd * linkOpnd = this->GetSrc2();
// ArgOut_A_InlineSpecialized
IR::Instr * tmpInstr = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->m_instrDef;
Assert(tmpInstr->m_opcode == Js::OpCode::ArgOut_A_InlineSpecialized);
// ArgOut_A_InlineSpecialized src2; link to actual argouts.
linkOpnd = tmpInstr->GetSrc2();
uint32 argCount = linkOpnd->AsSymOpnd()->m_sym->AsStackSym()->GetArgSlotNum();
if (argCount != argsOpndLength)
{
return false;
}
while (linkOpnd->IsSymOpnd() && argCount > 0)
{
IR::SymOpnd *src2 = linkOpnd->AsSymOpnd();
StackSym *sym = src2->m_sym->AsStackSym();
Assert(sym->m_isSingleDef);
IR::Instr *argInstr = sym->m_instrDef;
Assert(argInstr->m_opcode == Js::OpCode::ArgOut_A);
if (fn(argInstr, argCount - 1))
{
return true;
}
argCount--;
linkOpnd = argInstr->GetSrc2();
}
return false;
}
///----------------------------------------------------------------------------
///
/// BranchInstr::SetTarget
///
///----------------------------------------------------------------------------
inline void
BranchInstr::SetTarget(LabelInstr * labelInstr)
{
Assert(!this->m_isMultiBranch);
if (this->m_branchTarget)
{
this->m_branchTarget->RemoveLabelRef(this);
}
if (labelInstr)
{
labelInstr->AddLabelRef(this);
}
this->m_branchTarget = labelInstr;
}
inline void
BranchInstr::ClearTarget()
{
if (this->IsMultiBranch())
{
this->AsMultiBrInstr()->ClearTarget();
}
else
{
this->SetTarget(nullptr);
}
}
///----------------------------------------------------------------------------
///
/// BranchInstr::GetTarget
///
///----------------------------------------------------------------------------
inline LabelInstr *
BranchInstr::GetTarget() const
{
return this->m_branchTarget;
}
///----------------------------------------------------------------------------
///
/// BranchInstr::IsConditional
///
///----------------------------------------------------------------------------
inline bool
BranchInstr::IsConditional() const
{
return !this->IsUnconditional();
}
inline bool
BranchInstr::IsMultiBranch() const
{
if (m_branchTarget)
{
Assert(!m_isMultiBranch);
return false;
}
else
{
Assert(m_isMultiBranch);
return true; // it's a multi branch instr
}
}
///----------------------------------------------------------------------------
///
/// BranchInstr::IsUnconditional
///
///----------------------------------------------------------------------------
inline bool
BranchInstr::IsUnconditional() const
{
if (this->IsLowered())
{
return LowererMD::IsUnconditionalBranch(this);
}
else
{
return (this->m_opcode == Js::OpCode::Br || this->m_opcode == Js::OpCode::MultiBr);
}
}
///----------------------------------------------------------------------------
///
/// MultiBranchInstr::AddtoDictionary
/// - Adds the string to the list with the targetoffset
/// In order for the dictionary to have the right value, MapBranchTargetAddress
/// needs to be called to populate the dictionary and then it'll be patched up
/// to the right values
///
///----------------------------------------------------------------------------
inline void
MultiBranchInstr::AddtoDictionary(uint32 offset, TBranchKey key, void* remoteVar)
{
Assert(this->m_kind == StrDictionary);
Assert(key);
auto dict = this->GetBranchDictionary();
dict->AddEntry(offset, key, remoteVar);
}
inline void
MultiBranchInstr::AddtoJumpTable(uint32 offset, uint32 jmpIndex)
{
Assert(this->m_kind == IntJumpTable || this->m_kind == SingleCharStrJumpTable);
Assert(jmpIndex != -1);
auto table = this->GetBranchJumpTable();
table->jmpTable[jmpIndex] = (void*)offset;
}
inline void
MultiBranchInstr::FixMultiBrDefaultTarget(uint32 targetOffset)
{
this->GetBranchJumpTable()->defaultTarget = (void *)targetOffset;
}
inline void
MultiBranchInstr::CreateBranchTargetsAndSetDefaultTarget(int size, Kind kind, uint defaultTargetOffset)
{
AssertMsg(size != 0, "The dictionary/jumpTable size should not be zero");
NativeCodeData::Allocator * allocator = this->m_func->GetNativeCodeDataAllocator();
m_kind = kind;
switch (kind)
{
case IntJumpTable:
case SingleCharStrJumpTable:
{
JitArenaAllocator * jitAllocator = this->m_func->GetTopFunc()->m_alloc;
BranchJumpTableWrapper * branchTargets = BranchJumpTableWrapper::New(jitAllocator, size);
branchTargets->defaultTarget = (void *)defaultTargetOffset;
this->m_branchTargets = branchTargets;
break;
}
case StrDictionary:
{
BranchDictionaryWrapper * branchTargets = BranchDictionaryWrapper::New(allocator, size, m_func->IsOOPJIT() ? m_func->m_alloc : nullptr);
branchTargets->defaultTarget = (void *)defaultTargetOffset;
this->m_branchTargets = branchTargets;
break;
}
default:
Assert(false);
};
}
inline MultiBranchInstr::BranchDictionaryWrapper *
MultiBranchInstr::GetBranchDictionary()
{
return reinterpret_cast<MultiBranchInstr::BranchDictionaryWrapper *>(m_branchTargets);
}
inline MultiBranchInstr::BranchJumpTable *
MultiBranchInstr::GetBranchJumpTable()
{
return reinterpret_cast<MultiBranchInstr::BranchJumpTable *>(m_branchTargets);
}
inline void
MultiBranchInstr::ChangeLabelRef(LabelInstr * oldTarget, LabelInstr * newTarget)
{
if (oldTarget)
{
oldTarget->RemoveLabelRef(this);
}
if (newTarget)
{
newTarget->AddLabelRef(this);
}
}
///----------------------------------------------------------------------------
///
/// LabelInstr::SetPC
///
///----------------------------------------------------------------------------
inline void
LabelInstr::SetPC(BYTE * pc)
{
this->m_pc.pc = pc;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::GetPC
///
///----------------------------------------------------------------------------
inline BYTE *
LabelInstr::GetPC(void) const
{
return this->m_pc.pc;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::ResetOffset
///
///----------------------------------------------------------------------------
inline void
LabelInstr::ResetOffset(uintptr_t offset)
{
AssertMsg(this->isInlineeEntryInstr, "As of now only InlineeEntryInstr overwrites the offset at encoder stage");
this->m_pc.offset = offset;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::SetOffset
///
///----------------------------------------------------------------------------
inline void
LabelInstr::SetOffset(uintptr_t offset)
{
AssertMsg(this->m_pc.offset == 0, "Overwriting existing byte offset");
this->m_pc.offset = offset;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::GetOffset
///
///----------------------------------------------------------------------------
inline uintptr_t
LabelInstr::GetOffset(void) const
{
return this->m_pc.offset;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::SetBasicBlock
///
///----------------------------------------------------------------------------
inline void
LabelInstr::SetBasicBlock(BasicBlock * block)
{
AssertMsg(this->m_block == nullptr || block == nullptr, "Overwriting existing block pointer");
this->m_block = block;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::GetBasicBlock
///
///----------------------------------------------------------------------------
inline BasicBlock *
LabelInstr::GetBasicBlock(void) const
{
return this->m_block;
}
inline void
LabelInstr::SetLoop(Loop* loop)
{
Assert(this->m_isLoopTop);
this->m_loop = loop;
}
inline Loop*
LabelInstr::GetLoop(void) const
{
Assert(this->m_isLoopTop);
return this->m_loop;
}
///----------------------------------------------------------------------------
///
/// LabelInstr::UnlinkBasicBlock
///
///----------------------------------------------------------------------------
inline void
LabelInstr::UnlinkBasicBlock(void)
{
this->m_block = nullptr;
}
inline BOOL
LabelInstr::IsUnreferenced(void) const
{
return labelRefs.Empty() && !m_hasNonBranchRef;
}
inline void
LabelInstr::SetRegion(Region * region)
{
this->m_region = region;
}
inline Region *
LabelInstr::GetRegion(void) const
{
return this->m_region;
}
} // namespace IR