//===- 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 /// \brief Implements the Inst class, primarily the various subclass /// constructors and dump routines. /// //===----------------------------------------------------------------------===// #include "IceInst.h" #include "IceCfg.h" #include "IceCfgNode.h" #include "IceInstVarIter.h" #include "IceLiveness.h" #include "IceOperand.h" #include "IceTargetLowering.h" #include "llvm/Support/Format.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; InstIcmp::ICond Reverse; } InstIcmpAttributes[] = { #define X(tag, reverse, str) \ { str, InstIcmp::ICond::reverse } \ , 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), LiveRangesEnded(0) { Srcs.reserve(MaxSrcs); } const char *Inst::getInstName() const { if (!BuildDefs::dump()) return "???"; switch (Kind) { #define X(InstrKind, name) \ case InstrKind: \ return name X(Unreachable, "unreachable"); X(Alloca, "alloca"); X(Arithmetic, "arithmetic"); X(Br, "br"); X(Call, "call"); X(Cast, "cast"); X(ExtractElement, "extractelement"); X(Fcmp, "fcmp"); X(Icmp, "icmp"); X(IntrinsicCall, "intrinsiccall"); X(InsertElement, "insertelement"); X(Load, "load"); X(Phi, "phi"); X(Ret, "ret"); X(Select, "select"); X(Store, "store"); X(Switch, "switch"); X(Assign, "assign"); X(Breakpoint, "break"); X(BundleLock, "bundlelock"); X(BundleUnlock, "bundleunlock"); X(FakeDef, "fakedef"); X(FakeUse, "fakeuse"); X(FakeKill, "fakekill"); X(JumpTable, "jumptable"); X(ShuffleVector, "shufflevector"); #undef X default: assert(Kind >= Target); return "target"; } } // 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 (auto *TestVar = llvm::dyn_cast<const Variable>(TestSrc)) { LREndedBits Mask = LiveRangesEnded; FOREACH_VAR_IN_INST(Var, *this) { 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"); } bool Inst::isMemoryWrite() const { llvm::report_fatal_error("Attempt to call base Inst::isMemoryWrite() method"); } void Inst::livenessLightweight(Cfg *Func, LivenessBV &Live) { assert(!isDeleted()); resetLastUses(); VariablesMetadata *VMetadata = Func->getVMetadata(); FOREACH_VAR_IN_INST(Var, *this) { if (VMetadata->isMultiBlock(Var)) continue; SizeT Index = Var->getIndex(); if (Live[Index]) continue; Live[Index] = true; setLastUse(IndexOfVarInInst(Var)); } } bool Inst::liveness(InstNumberT InstNumber, LivenessBV &Live, Liveness *Liveness, LiveBeginEndMap *LiveBegin, LiveBeginEndMap *LiveEnd) { assert(!isDeleted()); Dead = false; if (Dest && !Dest->isRematerializable()) { SizeT VarNum = Liveness->getLiveIndex(Dest->getIndex()); if (Live[VarNum]) { if (!isDestRedefined()) { 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(); FOREACH_VAR_IN_INST(Var, *this) { if (Var->isRematerializable()) continue; SizeT VarNum = Liveness->getLiveIndex(Var->getIndex()); if (!Live[VarNum]) { setLastUse(IndexOfVarInInst(Var)); 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, Variable *Dest, Operand *ByteCount, uint32_t AlignInBytes) : 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::getInstName() const { if (!BuildDefs::dump()) return "???"; return InstArithmeticAttributes[getOp()].DisplayString; } const char *InstArithmetic::getOpName(OpKind Op) { return Op < InstArithmetic::_num ? InstArithmeticAttributes[Op].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); } bool InstAssign::isVarAssign() const { return llvm::isa<Variable>(getSrc(0)); } // 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 (auto *Constant = llvm::dyn_cast<ConstantInteger32>(Source)) { int32_t C32 = Constant->getValue(); if (C32 != 0) { TargetFalse = TargetTrue; } TargetTrue = nullptr; // turn into unconditional version } else 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.reserve(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) { assert(Label); Labels.push_back(Label); addSource(Source); } // Find the source operand corresponding to the incoming edge for the given // node. 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; } // Replace the source operand corresponding to the incoming edge for the given // node by a zero of the appropriate type. void InstPhi::clearOperandForTarget(CfgNode *Target) { for (SizeT I = 0; I < getSrcSize(); ++I) { if (getLabel(I) == Target) { Type Ty = Dest->getType(); Srcs[I] = Target->getCfg()->getContext()->getConstantZero(Ty); return; } } llvm_unreachable("Phi target not found"); } // 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 (auto *Var = llvm::dyn_cast<Variable>(getSrc(I))) { if (!Var->isRematerializable()) { 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() + "_phi"); if (auto *NewSrc64On32 = llvm::dyn_cast<Variable64On32>(NewSrc)) NewSrc64On32->initHiLo(Func); 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); } Variable *InstStore::getRmwBeacon() const { return llvm::dyn_cast<Variable>(getSrc(2)); } 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); assert(LabelDefault); OutEdges.push_back(LabelDefault); for (SizeT I = 0; I < NumCases; ++I) { assert(Labels[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, uint32_t Weight) : InstHighLevel(Func, Inst::FakeUse, Weight, nullptr) { assert(Src); for (uint32_t i = 0; i < Weight; ++i) addSource(Src); } InstFakeKill::InstFakeKill(Cfg *Func, const Inst *Linked) : InstHighLevel(Func, Inst::FakeKill, 0, nullptr), Linked(Linked) {} InstShuffleVector::InstShuffleVector(Cfg *Func, Variable *Dest, Operand *Src0, Operand *Src1) : InstHighLevel(Func, Inst::ShuffleVector, 2, Dest), NumIndexes(typeNumElements(Dest->getType())) { addSource(Src0); addSource(Src1); Indexes = Func->allocateArrayOf<ConstantInteger32 *>(NumIndexes); } namespace { GlobalString makeName(Cfg *Func, const SizeT Id) { const auto FuncName = Func->getFunctionName(); auto *Ctx = Func->getContext(); if (FuncName.hasStdString()) return GlobalString::createWithString( Ctx, ".L" + FuncName.toString() + "$jumptable$__" + std::to_string(Id)); return GlobalString::createWithString( Ctx, "$J" + std::to_string(FuncName.getID()) + "_" + std::to_string(Id)); } } // end of anonymous namespace InstJumpTable::InstJumpTable(Cfg *Func, SizeT NumTargets, CfgNode *Default) : InstHighLevel(Func, Inst::JumpTable, 1, nullptr), Id(Func->getTarget()->makeNextJumpTableNumber()), NumTargets(NumTargets), Name(makeName(Func, Id)), FuncName(Func->getFunctionName()) { 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; } JumpTableData InstJumpTable::toJumpTableData(Assembler *Asm) const { JumpTableData::TargetList TargetList(NumTargets); for (SizeT i = 0; i < NumTargets; ++i) { const SizeT Index = Targets[i]->getIndex(); TargetList[i] = Asm->getCfgNodeLabel(Index)->getPosition(); } return JumpTableData(Name, FuncName, Id, TargetList); } 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)) { InstNumberT Number = getNumber(); if (Number == NumberDeleted) Str << "[XXX]"; else Str << llvm::format("[%3d]", Number); } 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 << " =~ " << getInstName() << " "; 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)) { FOREACH_VAR_IN_INST(Var, *this) { 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 << " = " << getInstName() << " " << 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) { if (Kind < InstCast::OpKind::_num) return InstCastAttributes[Kind].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 << "\t" "align_to_end"; break; case Opt_PadToEnd: Str << "\t" "align_to_end /* pad_to_end */"; break; } Str << "\n"; } 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; case Opt_PadToEnd: Str << " pad_to_end"; break; } } void InstBundleUnlock::emit(const Cfg *Func) const { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrEmit(); Str << "\t.bundle_unlock"; Str << "\n"; } 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"; if (getSrcSize() > 0) Str << " "; emitSources(Func); Str << "\n"; } 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 InstShuffleVector::dump(const Cfg *Func) const { if (!BuildDefs::dump()) return; Ostream &Str = Func->getContext()->getStrDump(); Str << "shufflevector "; dumpDest(Func); Str << " = "; dumpSources(Func); for (SizeT I = 0; I < NumIndexes; ++I) { Str << ", "; Indexes[I]->dump(Func); } Str << "\n"; } 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); } InstBreakpoint::InstBreakpoint(Cfg *Func) : InstHighLevel(Func, Inst::Breakpoint, 0, nullptr) {} void InstIcmp::reverseConditionAndOperands() { Condition = InstIcmpAttributes[Condition].Reverse; std::swap(Srcs[0], Srcs[1]); } bool checkForRedundantAssign(const Variable *Dest, const Operand *Source) { const auto *SrcVar = llvm::dyn_cast<const Variable>(Source); if (SrcVar == nullptr) 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()) { if (!Dest->hasStackOffset() || !SrcVar->hasStackOffset()) { // If called before stack slots have been assigned (i.e. as part of the // dump() routine), conservatively return false. return false; } if (Dest->getStackOffset() != SrcVar->getStackOffset()) { return false; } return true; } // For a "v=t" assignment where t has a register, v has a stack slot, and v // has a LinkedTo stack root, and v and t share the same LinkedTo root, return // true. This is because this assignment is effectively reassigning the same // value to the original LinkedTo stack root. if (SrcVar->hasReg() && Dest->hasStackOffset() && Dest->getLinkedToStackRoot() != nullptr && Dest->getLinkedToRoot() == SrcVar->getLinkedToRoot()) { return true; } return false; } } // end of namespace Ice