| //------------------------------------------------------------------------------------------------------- |
| // 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 |