| //===- subzero/src/IceInst.cpp - High-level instruction implementation ----===// |
| // |
| // The Subzero Code Generator |
| // |
| // This file is distributed under the University of Illinois Open Source |
| // License. See LICENSE.TXT for details. |
| // |
| //===----------------------------------------------------------------------===// |
| /// |
| /// \file |
| /// This file implements the Inst class, primarily the various |
| /// subclass constructors and dump routines. |
| /// |
| //===----------------------------------------------------------------------===// |
| |
| #include "IceInst.h" |
| |
| #include "IceCfg.h" |
| #include "IceCfgNode.h" |
| #include "IceLiveness.h" |
| #include "IceOperand.h" |
| #include "IceTargetLowering.h" |
| |
| namespace Ice { |
| |
| namespace { |
| |
| // Using non-anonymous struct so that array_lengthof works. |
| const struct InstArithmeticAttributes_ { |
| const char *DisplayString; |
| bool IsCommutative; |
| } InstArithmeticAttributes[] = { |
| #define X(tag, str, commutative) \ |
| { str, commutative } \ |
| , |
| ICEINSTARITHMETIC_TABLE |
| #undef X |
| }; |
| |
| // Using non-anonymous struct so that array_lengthof works. |
| const struct InstCastAttributes_ { |
| const char *DisplayString; |
| } InstCastAttributes[] = { |
| #define X(tag, str) \ |
| { str } \ |
| , |
| ICEINSTCAST_TABLE |
| #undef X |
| }; |
| |
| // Using non-anonymous struct so that array_lengthof works. |
| const struct InstFcmpAttributes_ { |
| const char *DisplayString; |
| } InstFcmpAttributes[] = { |
| #define X(tag, str) \ |
| { str } \ |
| , |
| ICEINSTFCMP_TABLE |
| #undef X |
| }; |
| |
| // Using non-anonymous struct so that array_lengthof works. |
| const struct InstIcmpAttributes_ { |
| const char *DisplayString; |
| } InstIcmpAttributes[] = { |
| #define X(tag, str) \ |
| { str } \ |
| , |
| ICEINSTICMP_TABLE |
| #undef X |
| }; |
| |
| } // end of anonymous namespace |
| |
| Inst::Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) |
| : Kind(Kind), Number(Func->newInstNumber()), Dest(Dest), MaxSrcs(MaxSrcs), |
| Srcs(Func->allocateArrayOf<Operand *>(MaxSrcs)), LiveRangesEnded(0) {} |
| |
| // Assign the instruction a new number. |
| void Inst::renumber(Cfg *Func) { |
| Number = isDeleted() ? NumberDeleted : Func->newInstNumber(); |
| } |
| |
| // Delete the instruction if its tentative Dead flag is still set |
| // after liveness analysis. |
| void Inst::deleteIfDead() { |
| if (Dead) |
| setDeleted(); |
| } |
| |
| // If Src is a Variable, it returns true if this instruction ends |
| // Src's live range. Otherwise, returns false. |
| bool Inst::isLastUse(const Operand *TestSrc) const { |
| if (LiveRangesEnded == 0) |
| return false; // early-exit optimization |
| if (const Variable *TestVar = llvm::dyn_cast<const Variable>(TestSrc)) { |
| LREndedBits Mask = LiveRangesEnded; |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| Operand *Src = getSrc(I); |
| SizeT NumVars = Src->getNumVars(); |
| for (SizeT J = 0; J < NumVars; ++J) { |
| const Variable *Var = Src->getVar(J); |
| if (Var == TestVar) { |
| // We've found where the variable is used in the instruction. |
| return Mask & 1; |
| } |
| Mask >>= 1; |
| if (Mask == 0) |
| return false; // another early-exit optimization |
| } |
| } |
| } |
| return false; |
| } |
| |
| // Given an instruction like: |
| // a = b + c + [x,y] + e |
| // which was created from OrigInst: |
| // a = b + c + d + e |
| // with SpliceAssn spliced in: |
| // d = [x,y] |
| // |
| // Reconstruct the LiveRangesEnded bitmask in this instruction by |
| // combining the LiveRangesEnded values of OrigInst and SpliceAssn. |
| // If operands d and [x,y] contain a different number of variables, |
| // then the bitmask position for e may be different in OrigInst and |
| // the current instruction, requiring extra shifts and masks in the |
| // computation. In the example above, OrigInst has variable e in bit |
| // position 3, whereas the current instruction has e in bit position 4 |
| // because [x,y] consumes 2 bitmask slots while d only consumed 1. |
| // |
| // Additionally, set HasSideEffects if either OrigInst or SpliceAssn |
| // have HasSideEffects set. |
| void Inst::spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn) { |
| HasSideEffects |= OrigInst->HasSideEffects; |
| HasSideEffects |= SpliceAssn->HasSideEffects; |
| // Find the bitmask index of SpliceAssn's dest within OrigInst. |
| Variable *SpliceDest = SpliceAssn->getDest(); |
| SizeT Index = 0; |
| for (SizeT I = 0; I < OrigInst->getSrcSize(); ++I) { |
| Operand *Src = OrigInst->getSrc(I); |
| if (Src == SpliceDest) { |
| LREndedBits LeftMask = OrigInst->LiveRangesEnded & ((1 << Index) - 1); |
| LREndedBits RightMask = OrigInst->LiveRangesEnded >> (Index + 1); |
| LiveRangesEnded = LeftMask | (SpliceAssn->LiveRangesEnded << Index) | |
| (RightMask << (Index + getSrc(I)->getNumVars())); |
| return; |
| } |
| Index += getSrc(I)->getNumVars(); |
| } |
| llvm::report_fatal_error("Failed to find splice operand"); |
| } |
| |
| void Inst::livenessLightweight(Cfg *Func, LivenessBV &Live) { |
| assert(!isDeleted()); |
| resetLastUses(); |
| VariablesMetadata *VMetadata = Func->getVMetadata(); |
| SizeT VarIndex = 0; |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| Operand *Src = getSrc(I); |
| SizeT NumVars = Src->getNumVars(); |
| for (SizeT J = 0; J < NumVars; ++J, ++VarIndex) { |
| const Variable *Var = Src->getVar(J); |
| if (VMetadata->isMultiBlock(Var)) |
| continue; |
| SizeT Index = Var->getIndex(); |
| if (Live[Index]) |
| continue; |
| Live[Index] = true; |
| setLastUse(VarIndex); |
| } |
| } |
| } |
| |
| bool Inst::liveness(InstNumberT InstNumber, LivenessBV &Live, |
| Liveness *Liveness, LiveBeginEndMap *LiveBegin, |
| LiveBeginEndMap *LiveEnd) { |
| assert(!isDeleted()); |
| |
| Dead = false; |
| if (Dest) { |
| SizeT VarNum = Liveness->getLiveIndex(Dest->getIndex()); |
| if (Live[VarNum]) { |
| if (!isDestNonKillable()) { |
| Live[VarNum] = false; |
| if (LiveBegin && Liveness->getRangeMask(Dest->getIndex())) { |
| LiveBegin->push_back(std::make_pair(VarNum, InstNumber)); |
| } |
| } |
| } else { |
| if (!hasSideEffects()) |
| Dead = true; |
| } |
| } |
| if (Dead) |
| return false; |
| // Phi arguments only get added to Live in the predecessor node, but |
| // we still need to update LiveRangesEnded. |
| bool IsPhi = llvm::isa<InstPhi>(this); |
| resetLastUses(); |
| SizeT VarIndex = 0; |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| Operand *Src = getSrc(I); |
| SizeT NumVars = Src->getNumVars(); |
| for (SizeT J = 0; J < NumVars; ++J, ++VarIndex) { |
| const Variable *Var = Src->getVar(J); |
| SizeT VarNum = Liveness->getLiveIndex(Var->getIndex()); |
| if (!Live[VarNum]) { |
| setLastUse(VarIndex); |
| if (!IsPhi) { |
| Live[VarNum] = true; |
| // For a variable in SSA form, its live range can end at |
| // most once in a basic block. However, after lowering to |
| // two-address instructions, we end up with sequences like |
| // "t=b;t+=c;a=t" where t's live range begins and ends |
| // twice. ICE only allows a variable to have a single |
| // liveness interval in a basic block (except for blocks |
| // where a variable is live-in and live-out but there is a |
| // gap in the middle). Therefore, this lowered sequence |
| // needs to represent a single conservative live range for |
| // t. Since the instructions are being traversed backwards, |
| // we make sure LiveEnd is only set once by setting it only |
| // when LiveEnd[VarNum]==0 (sentinel value). Note that it's |
| // OK to set LiveBegin multiple times because of the |
| // backwards traversal. |
| if (LiveEnd && Liveness->getRangeMask(Var->getIndex())) { |
| // Ideally, we would verify that VarNum wasn't already |
| // added in this block, but this can't be done very |
| // efficiently with LiveEnd as a vector. Instead, |
| // livenessPostprocess() verifies this after the vector |
| // has been sorted. |
| LiveEnd->push_back(std::make_pair(VarNum, InstNumber)); |
| } |
| } |
| } |
| } |
| } |
| return true; |
| } |
| |
| InstAlloca::InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes, |
| Variable *Dest) |
| : InstHighLevel(Func, Inst::Alloca, 1, Dest), AlignInBytes(AlignInBytes) { |
| // Verify AlignInBytes is 0 or a power of 2. |
| assert(AlignInBytes == 0 || llvm::isPowerOf2_32(AlignInBytes)); |
| addSource(ByteCount); |
| } |
| |
| InstArithmetic::InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest, |
| Operand *Source1, Operand *Source2) |
| : InstHighLevel(Func, Inst::Arithmetic, 2, Dest), Op(Op) { |
| addSource(Source1); |
| addSource(Source2); |
| } |
| |
| const char *InstArithmetic::getOpName(OpKind Op) { |
| size_t OpIndex = static_cast<size_t>(Op); |
| return OpIndex < InstArithmetic::_num |
| ? InstArithmeticAttributes[OpIndex].DisplayString |
| : "???"; |
| } |
| |
| bool InstArithmetic::isCommutative() const { |
| return InstArithmeticAttributes[getOp()].IsCommutative; |
| } |
| |
| InstAssign::InstAssign(Cfg *Func, Variable *Dest, Operand *Source) |
| : InstHighLevel(Func, Inst::Assign, 1, Dest) { |
| addSource(Source); |
| } |
| |
| // If TargetTrue==TargetFalse, we turn it into an unconditional |
| // branch. This ensures that, along with the 'switch' instruction |
| // semantics, there is at most one edge from one node to another. |
| InstBr::InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue_, |
| CfgNode *TargetFalse_) |
| : InstHighLevel(Func, Inst::Br, 1, nullptr), TargetFalse(TargetFalse_), |
| TargetTrue(TargetTrue_) { |
| if (TargetTrue == TargetFalse) { |
| TargetTrue = nullptr; // turn into unconditional version |
| } else { |
| addSource(Source); |
| } |
| } |
| |
| InstBr::InstBr(Cfg *Func, CfgNode *Target) |
| : InstHighLevel(Func, Inst::Br, 0, nullptr), TargetFalse(Target), |
| TargetTrue(nullptr) {} |
| |
| NodeList InstBr::getTerminatorEdges() const { |
| NodeList OutEdges; |
| OutEdges.reserve(TargetTrue ? 2 : 1); |
| OutEdges.push_back(TargetFalse); |
| if (TargetTrue) |
| OutEdges.push_back(TargetTrue); |
| return OutEdges; |
| } |
| |
| bool InstBr::repointEdges(CfgNode *OldNode, CfgNode *NewNode) { |
| bool Found = false; |
| if (TargetFalse == OldNode) { |
| TargetFalse = NewNode; |
| Found = true; |
| } |
| if (TargetTrue == OldNode) { |
| TargetTrue = NewNode; |
| Found = true; |
| } |
| return Found; |
| } |
| |
| InstCast::InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source) |
| : InstHighLevel(Func, Inst::Cast, 1, Dest), CastKind(CastKind) { |
| addSource(Source); |
| } |
| |
| InstExtractElement::InstExtractElement(Cfg *Func, Variable *Dest, |
| Operand *Source1, Operand *Source2) |
| : InstHighLevel(Func, Inst::ExtractElement, 2, Dest) { |
| addSource(Source1); |
| addSource(Source2); |
| } |
| |
| InstFcmp::InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1, |
| Operand *Source2) |
| : InstHighLevel(Func, Inst::Fcmp, 2, Dest), Condition(Condition) { |
| addSource(Source1); |
| addSource(Source2); |
| } |
| |
| InstIcmp::InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1, |
| Operand *Source2) |
| : InstHighLevel(Func, Inst::Icmp, 2, Dest), Condition(Condition) { |
| addSource(Source1); |
| addSource(Source2); |
| } |
| |
| InstInsertElement::InstInsertElement(Cfg *Func, Variable *Dest, |
| Operand *Source1, Operand *Source2, |
| Operand *Source3) |
| : InstHighLevel(Func, Inst::InsertElement, 3, Dest) { |
| addSource(Source1); |
| addSource(Source2); |
| addSource(Source3); |
| } |
| |
| InstLoad::InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr) |
| : InstHighLevel(Func, Inst::Load, 1, Dest) { |
| addSource(SourceAddr); |
| } |
| |
| InstPhi::InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest) |
| : InstHighLevel(Func, Phi, MaxSrcs, Dest) { |
| Labels = Func->allocateArrayOf<CfgNode *>(MaxSrcs); |
| } |
| |
| // TODO: A Switch instruction (and maybe others) can add duplicate |
| // edges. We may want to de-dup Phis and validate consistency (i.e., |
| // the source operands are the same for duplicate edges), though it |
| // seems the current lowering code is OK with this situation. |
| void InstPhi::addArgument(Operand *Source, CfgNode *Label) { |
| Labels[getSrcSize()] = Label; |
| addSource(Source); |
| } |
| |
| // Find the source operand corresponding to the incoming edge for the |
| // given node. TODO: This uses a linear-time search, which could be |
| // improved if it becomes a problem. |
| Operand *InstPhi::getOperandForTarget(CfgNode *Target) const { |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| if (Labels[I] == Target) |
| return getSrc(I); |
| } |
| llvm_unreachable("Phi target not found"); |
| return nullptr; |
| } |
| |
| // Updates liveness for a particular operand based on the given |
| // predecessor edge. Doesn't mark the operand as live if the Phi |
| // instruction is dead or deleted. |
| void InstPhi::livenessPhiOperand(LivenessBV &Live, CfgNode *Target, |
| Liveness *Liveness) { |
| if (isDeleted() || Dead) |
| return; |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| if (Labels[I] == Target) { |
| if (Variable *Var = llvm::dyn_cast<Variable>(getSrc(I))) { |
| SizeT SrcIndex = Liveness->getLiveIndex(Var->getIndex()); |
| if (!Live[SrcIndex]) { |
| setLastUse(I); |
| Live[SrcIndex] = true; |
| } |
| } |
| return; |
| } |
| } |
| llvm_unreachable("Phi operand not found for specified target node"); |
| } |
| |
| // Change "a=phi(...)" to "a_phi=phi(...)" and return a new |
| // instruction "a=a_phi". |
| Inst *InstPhi::lower(Cfg *Func) { |
| Variable *Dest = getDest(); |
| assert(Dest); |
| Variable *NewSrc = Func->makeVariable(Dest->getType()); |
| if (BuildDefs::dump()) |
| NewSrc->setName(Func, Dest->getName(Func) + "_phi"); |
| this->Dest = NewSrc; |
| return InstAssign::create(Func, Dest, NewSrc); |
| } |
| |
| InstRet::InstRet(Cfg *Func, Operand *RetValue) |
| : InstHighLevel(Func, Ret, RetValue ? 1 : 0, nullptr) { |
| if (RetValue) |
| addSource(RetValue); |
| } |
| |
| InstSelect::InstSelect(Cfg *Func, Variable *Dest, Operand *Condition, |
| Operand *SourceTrue, Operand *SourceFalse) |
| : InstHighLevel(Func, Inst::Select, 3, Dest) { |
| assert(typeElementType(Condition->getType()) == IceType_i1); |
| addSource(Condition); |
| addSource(SourceTrue); |
| addSource(SourceFalse); |
| } |
| |
| InstStore::InstStore(Cfg *Func, Operand *Data, Operand *Addr) |
| : InstHighLevel(Func, Inst::Store, 3, nullptr) { |
| addSource(Data); |
| addSource(Addr); |
| // The 3rd operand is a dummy placeholder for the RMW beacon. |
| addSource(Data); |
| } |
| |
| void InstStore::setRmwBeacon(Variable *Beacon) { |
| Dest = llvm::dyn_cast<Variable>(getData()); |
| Srcs[2] = Beacon; |
| } |
| |
| InstSwitch::InstSwitch(Cfg *Func, SizeT NumCases, Operand *Source, |
| CfgNode *LabelDefault) |
| : InstHighLevel(Func, Inst::Switch, 1, nullptr), LabelDefault(LabelDefault), |
| NumCases(NumCases) { |
| addSource(Source); |
| Values = Func->allocateArrayOf<uint64_t>(NumCases); |
| Labels = Func->allocateArrayOf<CfgNode *>(NumCases); |
| // Initialize in case buggy code doesn't set all entries |
| for (SizeT I = 0; I < NumCases; ++I) { |
| Values[I] = 0; |
| Labels[I] = nullptr; |
| } |
| } |
| |
| void InstSwitch::addBranch(SizeT CaseIndex, uint64_t Value, CfgNode *Label) { |
| assert(CaseIndex < NumCases); |
| Values[CaseIndex] = Value; |
| Labels[CaseIndex] = Label; |
| } |
| |
| NodeList InstSwitch::getTerminatorEdges() const { |
| NodeList OutEdges; |
| OutEdges.reserve(NumCases + 1); |
| OutEdges.push_back(LabelDefault); |
| for (SizeT I = 0; I < NumCases; ++I) { |
| OutEdges.push_back(Labels[I]); |
| } |
| std::sort(OutEdges.begin(), OutEdges.end(), |
| [](const CfgNode *x, const CfgNode *y) { |
| return x->getIndex() < y->getIndex(); |
| }); |
| auto Last = std::unique(OutEdges.begin(), OutEdges.end()); |
| OutEdges.erase(Last, OutEdges.end()); |
| return OutEdges; |
| } |
| |
| bool InstSwitch::repointEdges(CfgNode *OldNode, CfgNode *NewNode) { |
| bool Found = false; |
| if (LabelDefault == OldNode) { |
| LabelDefault = NewNode; |
| Found = true; |
| } |
| for (SizeT I = 0; I < NumCases; ++I) { |
| if (Labels[I] == OldNode) { |
| Labels[I] = NewNode; |
| Found = true; |
| } |
| } |
| return Found; |
| } |
| |
| InstUnreachable::InstUnreachable(Cfg *Func) |
| : InstHighLevel(Func, Inst::Unreachable, 0, nullptr) {} |
| |
| InstBundleLock::InstBundleLock(Cfg *Func, InstBundleLock::Option BundleOption) |
| : InstHighLevel(Func, Inst::BundleLock, 0, nullptr), |
| BundleOption(BundleOption) {} |
| |
| InstBundleUnlock::InstBundleUnlock(Cfg *Func) |
| : InstHighLevel(Func, Inst::BundleUnlock, 0, nullptr) {} |
| |
| InstFakeDef::InstFakeDef(Cfg *Func, Variable *Dest, Variable *Src) |
| : InstHighLevel(Func, Inst::FakeDef, Src ? 1 : 0, Dest) { |
| assert(Dest); |
| if (Src) |
| addSource(Src); |
| } |
| |
| InstFakeUse::InstFakeUse(Cfg *Func, Variable *Src) |
| : InstHighLevel(Func, Inst::FakeUse, 1, nullptr) { |
| assert(Src); |
| addSource(Src); |
| } |
| |
| InstFakeKill::InstFakeKill(Cfg *Func, const Inst *Linked) |
| : InstHighLevel(Func, Inst::FakeKill, 0, nullptr), Linked(Linked) {} |
| |
| InstJumpTable::InstJumpTable(Cfg *Func, SizeT NumTargets, CfgNode *Default) |
| : InstHighLevel(Func, Inst::JumpTable, 1, nullptr), |
| Id(Func->getTarget()->makeNextJumpTableNumber()), NumTargets(NumTargets) { |
| Targets = Func->allocateArrayOf<CfgNode *>(NumTargets); |
| for (SizeT I = 0; I < NumTargets; ++I) |
| Targets[I] = Default; |
| } |
| |
| bool InstJumpTable::repointEdges(CfgNode *OldNode, CfgNode *NewNode) { |
| bool Found = false; |
| for (SizeT I = 0; I < NumTargets; ++I) { |
| if (Targets[I] == OldNode) { |
| Targets[I] = NewNode; |
| Found = true; |
| } |
| } |
| return Found; |
| } |
| |
| Type InstCall::getReturnType() const { |
| if (Dest == nullptr) |
| return IceType_void; |
| return Dest->getType(); |
| } |
| |
| // ======================== Dump routines ======================== // |
| |
| void Inst::dumpDecorated(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| if (!Func->isVerbose(IceV_Deleted) && (isDeleted() || isRedundantAssign())) |
| return; |
| if (Func->isVerbose(IceV_InstNumbers)) { |
| char buf[30]; |
| InstNumberT Number = getNumber(); |
| if (Number == NumberDeleted) |
| snprintf(buf, llvm::array_lengthof(buf), "[XXX]"); |
| else |
| snprintf(buf, llvm::array_lengthof(buf), "[%3d]", Number); |
| Str << buf; |
| } |
| Str << " "; |
| if (isDeleted()) |
| Str << " //"; |
| dump(Func); |
| dumpExtras(Func); |
| Str << "\n"; |
| } |
| |
| void Inst::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " =~ "; |
| dumpSources(Func); |
| } |
| |
| void Inst::dumpExtras(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| bool First = true; |
| // Print "LIVEEND={a,b,c}" for all source operands whose live ranges |
| // are known to end at this instruction. |
| if (Func->isVerbose(IceV_Liveness)) { |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| Operand *Src = getSrc(I); |
| SizeT NumVars = Src->getNumVars(); |
| for (SizeT J = 0; J < NumVars; ++J) { |
| const Variable *Var = Src->getVar(J); |
| if (isLastUse(Var)) { |
| if (First) |
| Str << " // LIVEEND={"; |
| else |
| Str << ","; |
| Var->dump(Func); |
| First = false; |
| } |
| } |
| } |
| if (!First) |
| Str << "}"; |
| } |
| } |
| |
| void Inst::dumpSources(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| if (I > 0) |
| Str << ", "; |
| getSrc(I)->dump(Func); |
| } |
| } |
| |
| void Inst::emitSources(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrEmit(); |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| if (I > 0) |
| Str << ", "; |
| getSrc(I)->emit(Func); |
| } |
| } |
| |
| void Inst::dumpDest(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| if (getDest()) |
| getDest()->dump(Func); |
| } |
| |
| void InstAlloca::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = alloca i8, i32 "; |
| getSizeInBytes()->dump(Func); |
| if (getAlignInBytes()) |
| Str << ", align " << getAlignInBytes(); |
| } |
| |
| void InstArithmetic::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = " << InstArithmeticAttributes[getOp()].DisplayString << " " |
| << getDest()->getType() << " "; |
| dumpSources(Func); |
| } |
| |
| void InstAssign::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = " << getDest()->getType() << " "; |
| dumpSources(Func); |
| } |
| |
| void InstBr::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << "br "; |
| if (!isUnconditional()) { |
| Str << "i1 "; |
| getCondition()->dump(Func); |
| Str << ", label %" << getTargetTrue()->getName() << ", "; |
| } |
| Str << "label %" << getTargetFalse()->getName(); |
| } |
| |
| void InstCall::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| if (getDest()) { |
| dumpDest(Func); |
| Str << " = "; |
| } |
| Str << "call "; |
| if (getDest()) |
| Str << getDest()->getType(); |
| else |
| Str << "void"; |
| Str << " "; |
| getCallTarget()->dump(Func); |
| Str << "("; |
| for (SizeT I = 0; I < getNumArgs(); ++I) { |
| if (I > 0) |
| Str << ", "; |
| Str << getArg(I)->getType() << " "; |
| getArg(I)->dump(Func); |
| } |
| Str << ")"; |
| } |
| |
| const char *InstCast::getCastName(InstCast::OpKind Kind) { |
| size_t Index = static_cast<size_t>(Kind); |
| if (Index < InstCast::OpKind::_num) |
| return InstCastAttributes[Index].DisplayString; |
| llvm_unreachable("Invalid InstCast::OpKind"); |
| return "???"; |
| } |
| |
| void InstCast::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = " << getCastName(getCastKind()) << " " << getSrc(0)->getType() |
| << " "; |
| dumpSources(Func); |
| Str << " to " << getDest()->getType(); |
| } |
| |
| void InstIcmp::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = icmp " << InstIcmpAttributes[getCondition()].DisplayString << " " |
| << getSrc(0)->getType() << " "; |
| dumpSources(Func); |
| } |
| |
| void InstExtractElement::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = extractelement "; |
| Str << getSrc(0)->getType() << " "; |
| getSrc(0)->dump(Func); |
| Str << ", "; |
| Str << getSrc(1)->getType() << " "; |
| getSrc(1)->dump(Func); |
| } |
| |
| void InstInsertElement::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = insertelement "; |
| Str << getSrc(0)->getType() << " "; |
| getSrc(0)->dump(Func); |
| Str << ", "; |
| Str << getSrc(1)->getType() << " "; |
| getSrc(1)->dump(Func); |
| Str << ", "; |
| Str << getSrc(2)->getType() << " "; |
| getSrc(2)->dump(Func); |
| } |
| |
| void InstFcmp::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = fcmp " << InstFcmpAttributes[getCondition()].DisplayString << " " |
| << getSrc(0)->getType() << " "; |
| dumpSources(Func); |
| } |
| |
| void InstLoad::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Type Ty = getDest()->getType(); |
| Str << " = load " << Ty << ", " << Ty << "* "; |
| dumpSources(Func); |
| Str << ", align " << typeAlignInBytes(Ty); |
| } |
| |
| void InstStore::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Type Ty = getData()->getType(); |
| dumpDest(Func); |
| if (Dest) |
| Str << " = "; |
| Str << "store " << Ty << " "; |
| getData()->dump(Func); |
| Str << ", " << Ty << "* "; |
| getAddr()->dump(Func); |
| Str << ", align " << typeAlignInBytes(Ty); |
| if (getRmwBeacon()) { |
| Str << ", beacon "; |
| getRmwBeacon()->dump(Func); |
| } |
| } |
| |
| void InstSwitch::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Type Ty = getComparison()->getType(); |
| Str << "switch " << Ty << " "; |
| getSrc(0)->dump(Func); |
| Str << ", label %" << getLabelDefault()->getName() << " [\n"; |
| for (SizeT I = 0; I < getNumCases(); ++I) { |
| Str << " " << Ty << " " << static_cast<int64_t>(getValue(I)) |
| << ", label %" << getLabel(I)->getName() << "\n"; |
| } |
| Str << " ]"; |
| } |
| |
| void InstPhi::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = phi " << getDest()->getType() << " "; |
| for (SizeT I = 0; I < getSrcSize(); ++I) { |
| if (I > 0) |
| Str << ", "; |
| Str << "[ "; |
| getSrc(I)->dump(Func); |
| Str << ", %" << Labels[I]->getName() << " ]"; |
| } |
| } |
| |
| void InstRet::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Type Ty = hasRetValue() ? getRetValue()->getType() : IceType_void; |
| Str << "ret " << Ty; |
| if (hasRetValue()) { |
| Str << " "; |
| dumpSources(Func); |
| } |
| } |
| |
| void InstSelect::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Operand *Condition = getCondition(); |
| Operand *TrueOp = getTrueOperand(); |
| Operand *FalseOp = getFalseOperand(); |
| Str << " = select " << Condition->getType() << " "; |
| Condition->dump(Func); |
| Str << ", " << TrueOp->getType() << " "; |
| TrueOp->dump(Func); |
| Str << ", " << FalseOp->getType() << " "; |
| FalseOp->dump(Func); |
| } |
| |
| void InstUnreachable::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "unreachable"; |
| } |
| |
| void InstBundleLock::emit(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrEmit(); |
| Str << "\t.bundle_lock"; |
| switch (BundleOption) { |
| case Opt_None: |
| break; |
| case Opt_AlignToEnd: |
| Str << "\talign_to_end"; |
| break; |
| } |
| } |
| |
| void InstBundleLock::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "bundle_lock"; |
| switch (BundleOption) { |
| case Opt_None: |
| break; |
| case Opt_AlignToEnd: |
| Str << " align_to_end"; |
| break; |
| } |
| } |
| |
| void InstBundleUnlock::emit(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrEmit(); |
| Str << "\t.bundle_unlock"; |
| } |
| |
| void InstBundleUnlock::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "bundle_unlock"; |
| } |
| |
| void InstFakeDef::emit(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| // Go ahead and "emit" these for now, since they are relatively |
| // rare. |
| Ostream &Str = Func->getContext()->getStrEmit(); |
| Str << "\t# "; |
| getDest()->emit(Func); |
| Str << " = def.pseudo "; |
| emitSources(Func); |
| } |
| |
| void InstFakeDef::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| dumpDest(Func); |
| Str << " = def.pseudo "; |
| dumpSources(Func); |
| } |
| |
| void InstFakeUse::emit(const Cfg *Func) const { (void)Func; } |
| |
| void InstFakeUse::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "use.pseudo "; |
| dumpSources(Func); |
| } |
| |
| void InstFakeKill::emit(const Cfg *Func) const { (void)Func; } |
| |
| void InstFakeKill::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| if (Linked->isDeleted()) |
| Str << "// "; |
| Str << "kill.pseudo scratch_regs"; |
| } |
| |
| void InstJumpTable::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "jump table ["; |
| for (SizeT I = 0; I < NumTargets; ++I) |
| Str << "\n " << Targets[I]->getName(); |
| Str << "\n ]"; |
| } |
| |
| void InstTarget::dump(const Cfg *Func) const { |
| if (!BuildDefs::dump()) |
| return; |
| Ostream &Str = Func->getContext()->getStrDump(); |
| Str << "[TARGET] "; |
| Inst::dump(Func); |
| } |
| |
| bool checkForRedundantAssign(const Variable *Dest, const Operand *Source) { |
| const auto SrcVar = llvm::dyn_cast<const Variable>(Source); |
| if (!SrcVar) |
| return false; |
| if (Dest->hasReg() && Dest->getRegNum() == SrcVar->getRegNum()) { |
| // TODO: On x86-64, instructions like "mov eax, eax" are used to |
| // clear the upper 32 bits of rax. We need to recognize and |
| // preserve these. |
| return true; |
| } |
| if (!Dest->hasReg() && !SrcVar->hasReg() && |
| Dest->getStackOffset() == SrcVar->getStackOffset()) |
| return true; |
| return false; |
| } |
| |
| } // end of namespace Ice |