|  | //===- subzero/src/IceInst.h - High-level instructions ----------*- C++ -*-===// | 
|  | // | 
|  | //                        The Subzero Code Generator | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  | /// | 
|  | /// \file | 
|  | /// \brief Declares the Inst class and its target-independent subclasses. | 
|  | /// | 
|  | /// These represent the high-level Vanilla ICE instructions and map roughly 1:1 | 
|  | /// to LLVM instructions. | 
|  | /// | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #ifndef SUBZERO_SRC_ICEINST_H | 
|  | #define SUBZERO_SRC_ICEINST_H | 
|  |  | 
|  | #include "IceCfg.h" | 
|  | #include "IceDefs.h" | 
|  | #include "IceInst.def" | 
|  | #include "IceIntrinsics.h" | 
|  | #include "IceOperand.h" | 
|  | #include "IceSwitchLowering.h" | 
|  | #include "IceTypes.h" | 
|  |  | 
|  | // TODO: The Cfg structure, and instructions in particular, need to be | 
|  | // validated for things like valid operand types, valid branch targets, proper | 
|  | // ordering of Phi and non-Phi instructions, etc. Most of the validity checking | 
|  | // will be done in the bitcode reader. We need a list of everything that should | 
|  | // be validated, and tests for each. | 
|  |  | 
|  | namespace Ice { | 
|  |  | 
|  | /// Base instruction class for ICE. Inst has two subclasses: InstHighLevel and | 
|  | /// InstTarget. High-level ICE instructions inherit from InstHighLevel, and | 
|  | /// low-level (target-specific) ICE instructions inherit from InstTarget. | 
|  | class Inst : public llvm::ilist_node<Inst> { | 
|  | Inst() = delete; | 
|  | Inst(const Inst &) = delete; | 
|  | Inst &operator=(const Inst &) = delete; | 
|  |  | 
|  | public: | 
|  | enum InstKind { | 
|  | // Arbitrary (alphabetical) order, except put Unreachable first. | 
|  | Unreachable, | 
|  | Alloca, | 
|  | Arithmetic, | 
|  | Br, | 
|  | Call, | 
|  | Cast, | 
|  | ExtractElement, | 
|  | Fcmp, | 
|  | Icmp, | 
|  | IntrinsicCall, | 
|  | InsertElement, | 
|  | Load, | 
|  | Phi, | 
|  | Ret, | 
|  | Select, | 
|  | Store, | 
|  | Switch, | 
|  | Assign,        // not part of LLVM/PNaCl bitcode | 
|  | Breakpoint,    // not part of LLVM/PNaCl bitcode | 
|  | BundleLock,    // not part of LLVM/PNaCl bitcode | 
|  | BundleUnlock,  // not part of LLVM/PNaCl bitcode | 
|  | FakeDef,       // not part of LLVM/PNaCl bitcode | 
|  | FakeUse,       // not part of LLVM/PNaCl bitcode | 
|  | FakeKill,      // not part of LLVM/PNaCl bitcode | 
|  | JumpTable,     // not part of LLVM/PNaCl bitcode | 
|  | ShuffleVector, // not part of LLVM/PNaCl bitcode | 
|  | // Anything >= Target is an InstTarget subclass. Note that the value-spaces | 
|  | // are shared across targets. To avoid confusion over the definition of | 
|  | // shared values, an object specific to one target should never be passed | 
|  | // to a different target. | 
|  | Target, | 
|  | Target_Max = std::numeric_limits<uint8_t>::max(), | 
|  | }; | 
|  | static_assert(Target <= Target_Max, "Must not be above max."); | 
|  | InstKind getKind() const { return Kind; } | 
|  | virtual const char *getInstName() const; | 
|  |  | 
|  | InstNumberT getNumber() const { return Number; } | 
|  | void renumber(Cfg *Func); | 
|  | enum { | 
|  | NumberDeleted = -1, | 
|  | NumberSentinel = 0, | 
|  | NumberInitial = 2, | 
|  | NumberExtended = NumberInitial - 1 | 
|  | }; | 
|  |  | 
|  | bool isDeleted() const { return Deleted; } | 
|  | void setDeleted() { Deleted = true; } | 
|  | void setDead(bool Value = true) { Dead = Value; } | 
|  | void deleteIfDead(); | 
|  |  | 
|  | bool hasSideEffects() const { return HasSideEffects; } | 
|  |  | 
|  | bool isDestRedefined() const { return IsDestRedefined; } | 
|  | void setDestRedefined() { IsDestRedefined = true; } | 
|  |  | 
|  | Variable *getDest() const { return Dest; } | 
|  |  | 
|  | SizeT getSrcSize() const { return Srcs.size(); } | 
|  | Operand *getSrc(SizeT I) const { | 
|  | assert(I < getSrcSize()); | 
|  | return Srcs[I]; | 
|  | } | 
|  | void replaceSource(SizeT Index, Operand *Replacement) { | 
|  | assert(Index < getSrcSize()); | 
|  | assert(!isDeleted()); | 
|  | Srcs[Index] = Replacement; | 
|  | } | 
|  |  | 
|  | bool isLastUse(const Operand *Src) const; | 
|  | void spliceLivenessInfo(Inst *OrigInst, Inst *SpliceAssn); | 
|  |  | 
|  | /// Returns a list of out-edges corresponding to a terminator instruction, | 
|  | /// which is the last instruction of the block. The list must not contain | 
|  | /// duplicates. | 
|  | virtual NodeList getTerminatorEdges() const { | 
|  | // All valid terminator instructions override this method. For the default | 
|  | // implementation, we assert in case some CfgNode is constructed without a | 
|  | // terminator instruction at the end. | 
|  | llvm_unreachable( | 
|  | "getTerminatorEdges() called on a non-terminator instruction"); | 
|  | return NodeList(); | 
|  | } | 
|  | virtual bool isUnconditionalBranch() const { return false; } | 
|  | /// If the instruction is a branch-type instruction with OldNode as a target, | 
|  | /// repoint it to NewNode and return true, otherwise return false. Repoint all | 
|  | /// instances of OldNode as a target. | 
|  | virtual bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) { | 
|  | (void)OldNode; | 
|  | (void)NewNode; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /// Returns true if the instruction is equivalent to a simple | 
|  | /// "var_dest=var_src" assignment where the dest and src are both variables. | 
|  | virtual bool isVarAssign() const { return false; } | 
|  |  | 
|  | /// Returns true if the instruction has a possible side effect of changing | 
|  | /// memory, in which case a memory load should not be reordered with respect | 
|  | /// to this instruction.  It should really be pure virtual, but we can't | 
|  | /// because of g++ and llvm::ilist<>, so we implement it as | 
|  | /// report_fatal_error(). | 
|  | virtual bool isMemoryWrite() const; | 
|  |  | 
|  | /// Returns true if the (target-specific) instruction represents an | 
|  | /// intra-block label, i.e. branch target.  This is meant primarily for | 
|  | /// Cfg::splitLocalVars(). | 
|  | virtual bool isLabel() const { return false; } | 
|  | /// If the (target-specific) instruction represents an intra-block branch to | 
|  | /// some Label instruction, return that Label branch target instruction; | 
|  | /// otherwise return nullptr. | 
|  | virtual const Inst *getIntraBlockBranchTarget() const { return nullptr; } | 
|  |  | 
|  | void livenessLightweight(Cfg *Func, LivenessBV &Live); | 
|  | /// Calculates liveness for this instruction. Returns true if this instruction | 
|  | /// is (tentatively) still live and should be retained, and false if this | 
|  | /// instruction is (tentatively) dead and should be deleted. The decision is | 
|  | /// tentative until the liveness dataflow algorithm has converged, and then a | 
|  | /// separate pass permanently deletes dead instructions. | 
|  | bool liveness(InstNumberT InstNumber, LivenessBV &Live, Liveness *Liveness, | 
|  | LiveBeginEndMap *LiveBegin, LiveBeginEndMap *LiveEnd); | 
|  |  | 
|  | /// Get the number of native instructions that this instruction ultimately | 
|  | /// emits. By default, high-level instructions don't result in any native | 
|  | /// instructions, and a target-specific instruction results in a single native | 
|  | /// instruction. | 
|  | virtual uint32_t getEmitInstCount() const { return 0; } | 
|  | // TODO(stichnot): Change Inst back to abstract once the g++ build issue is | 
|  | // fixed. llvm::ilist<Ice::Inst> doesn't work under g++ because the | 
|  | // resize(size_t, Ice::Inst) method is incorrectly declared and thus doesn't | 
|  | // allow the abstract class Ice::Inst. The method should be declared | 
|  | // resize(size_t, const Ice::Inst &). virtual void emit(const Cfg *Func) | 
|  | // const = 0; virtual void emitIAS(const Cfg *Func) const = 0; | 
|  | virtual void emit(const Cfg *) const { | 
|  | llvm_unreachable("emit on abstract class"); | 
|  | } | 
|  | virtual void emitIAS(const Cfg *Func) const { emit(Func); } | 
|  | virtual void dump(const Cfg *Func) const; | 
|  | virtual void dumpExtras(const Cfg *Func) const; | 
|  | void dumpDecorated(const Cfg *Func) const; | 
|  | void emitSources(const Cfg *Func) const; | 
|  | void dumpSources(const Cfg *Func) const; | 
|  | void dumpDest(const Cfg *Func) const; | 
|  | virtual bool isRedundantAssign() const { return false; } | 
|  |  | 
|  | virtual ~Inst() = default; | 
|  | void replaceDest(Variable *Var) { Dest = Var; } | 
|  |  | 
|  | void operator delete(void *Ptr, std::size_t Size) { | 
|  | assert(CfgAllocatorTraits::current() != nullptr); | 
|  | CfgAllocatorTraits::current()->Deallocate(Ptr, Size); | 
|  | llvm::report_fatal_error("Inst unexpectedly deleted"); | 
|  | } | 
|  |  | 
|  | inline void* getExternalData() const { return externalData; } | 
|  | inline void setExternalData(void* data) { externalData = data; } | 
|  |  | 
|  | protected: | 
|  | Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest); | 
|  | void addSource(Operand *Src) { | 
|  | assert(Src); | 
|  | Srcs.push_back(Src); | 
|  | } | 
|  | void setLastUse(SizeT VarIndex) { | 
|  | if (VarIndex < CHAR_BIT * sizeof(LiveRangesEnded)) | 
|  | LiveRangesEnded |= (((LREndedBits)1u) << VarIndex); | 
|  | } | 
|  | void resetLastUses() { LiveRangesEnded = 0; } | 
|  | /// The destroy() method lets the instruction cleanly release any memory that | 
|  | /// was allocated via the Cfg's allocator. | 
|  | virtual void destroy(Cfg *) {} | 
|  |  | 
|  | const InstKind Kind; | 
|  | /// Number is the instruction number for describing live ranges. | 
|  | InstNumberT Number; | 
|  | /// Deleted means irrevocably deleted. | 
|  | bool Deleted = false; | 
|  | /// Dead means one of two things depending on context: (1) pending deletion | 
|  | /// after liveness analysis converges, or (2) marked for deletion during | 
|  | /// lowering due to a folded bool operation. | 
|  | bool Dead = false; | 
|  | /// HasSideEffects means the instruction is something like a function call or | 
|  | /// a volatile load that can't be removed even if its Dest variable is not | 
|  | /// live. | 
|  | bool HasSideEffects = false; | 
|  | /// IsDestRedefined indicates that this instruction is not the first | 
|  | /// definition of Dest in the basic block.  The effect is that liveness | 
|  | /// analysis shouldn't consider this instruction to be the start of Dest's | 
|  | /// live range; rather, there is some other instruction earlier in the basic | 
|  | /// block with the same Dest.  This is maintained because liveness analysis | 
|  | /// has an invariant (primarily for performance reasons) that any Variable's | 
|  | /// live range recorded in a basic block has at most one start and at most one | 
|  | /// end. | 
|  | bool IsDestRedefined = false; | 
|  | /// External data can be set by an optimizer to compute and retain any | 
|  | /// information related to the current instruction. All the memory used to | 
|  | /// store this information must be managed by the optimizer. | 
|  | void* externalData = nullptr; | 
|  |  | 
|  | Variable *Dest; | 
|  | const SizeT MaxSrcs; // only used for assert | 
|  |  | 
|  | CfgVector<Operand *> Srcs; | 
|  |  | 
|  | /// LiveRangesEnded marks which Variables' live ranges end in this | 
|  | /// instruction. An instruction can have an arbitrary number of source | 
|  | /// operands (e.g. a call instruction), and each source operand can contain 0 | 
|  | /// or 1 Variable (and target-specific operands could contain more than 1 | 
|  | /// Variable). All the variables in an instruction are conceptually flattened | 
|  | /// and each variable is mapped to one bit position of the LiveRangesEnded bit | 
|  | /// vector. Only the first CHAR_BIT * sizeof(LREndedBits) variables are | 
|  | /// tracked this way. | 
|  | using LREndedBits = uint32_t; // only first 32 src operands tracked, sorry | 
|  | LREndedBits LiveRangesEnded; | 
|  | }; | 
|  |  | 
|  | class InstHighLevel : public Inst { | 
|  | InstHighLevel() = delete; | 
|  | InstHighLevel(const InstHighLevel &) = delete; | 
|  | InstHighLevel &operator=(const InstHighLevel &) = delete; | 
|  |  | 
|  | protected: | 
|  | InstHighLevel(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) | 
|  | : Inst(Func, Kind, MaxSrcs, Dest) {} | 
|  | void emit(const Cfg * /*Func*/) const override { | 
|  | llvm_unreachable("emit() called on a non-lowered instruction"); | 
|  | } | 
|  | void emitIAS(const Cfg * /*Func*/) const override { | 
|  | llvm_unreachable("emitIAS() called on a non-lowered instruction"); | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// Alloca instruction. This captures the size in bytes as getSrc(0), and the | 
|  | /// required alignment in bytes. The alignment must be either 0 (no alignment | 
|  | /// required) or a power of 2. | 
|  | class InstAlloca : public InstHighLevel { | 
|  | InstAlloca() = delete; | 
|  | InstAlloca(const InstAlloca &) = delete; | 
|  | InstAlloca &operator=(const InstAlloca &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstAlloca *create(Cfg *Func, Variable *Dest, Operand *ByteCount, | 
|  | uint32_t AlignInBytes) { | 
|  | return new (Func->allocate<InstAlloca>()) | 
|  | InstAlloca(Func, Dest, ByteCount, AlignInBytes); | 
|  | } | 
|  | uint32_t getAlignInBytes() const { return AlignInBytes; } | 
|  | Operand *getSizeInBytes() const { return getSrc(0); } | 
|  | bool getKnownFrameOffset() const { return KnownFrameOffset; } | 
|  | void setKnownFrameOffset() { KnownFrameOffset = true; } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Alloca; } | 
|  |  | 
|  | private: | 
|  | InstAlloca(Cfg *Func, Variable *Dest, Operand *ByteCount, | 
|  | uint32_t AlignInBytes); | 
|  |  | 
|  | const uint32_t AlignInBytes; | 
|  | bool KnownFrameOffset = false; | 
|  | }; | 
|  |  | 
|  | /// Binary arithmetic instruction. The source operands are captured in getSrc(0) | 
|  | /// and getSrc(1). | 
|  | class InstArithmetic : public InstHighLevel { | 
|  | InstArithmetic() = delete; | 
|  | InstArithmetic(const InstArithmetic &) = delete; | 
|  | InstArithmetic &operator=(const InstArithmetic &) = delete; | 
|  |  | 
|  | public: | 
|  | enum OpKind { | 
|  | #define X(tag, str, commutative) tag, | 
|  | ICEINSTARITHMETIC_TABLE | 
|  | #undef X | 
|  | _num | 
|  | }; | 
|  |  | 
|  | static InstArithmetic *create(Cfg *Func, OpKind Op, Variable *Dest, | 
|  | Operand *Source1, Operand *Source2) { | 
|  | return new (Func->allocate<InstArithmetic>()) | 
|  | InstArithmetic(Func, Op, Dest, Source1, Source2); | 
|  | } | 
|  | OpKind getOp() const { return Op; } | 
|  |  | 
|  | virtual const char *getInstName() const override; | 
|  |  | 
|  | static const char *getOpName(OpKind Op); | 
|  | bool isCommutative() const; | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == Arithmetic; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2); | 
|  |  | 
|  | const OpKind Op; | 
|  | }; | 
|  |  | 
|  | /// Assignment instruction. The source operand is captured in getSrc(0). This is | 
|  | /// not part of the LLVM bitcode, but is a useful abstraction for some of the | 
|  | /// lowering. E.g., if Phi instruction lowering happens before target lowering, | 
|  | /// or for representing an Inttoptr instruction, or as an intermediate step for | 
|  | /// lowering a Load instruction. | 
|  | class InstAssign : public InstHighLevel { | 
|  | InstAssign() = delete; | 
|  | InstAssign(const InstAssign &) = delete; | 
|  | InstAssign &operator=(const InstAssign &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstAssign *create(Cfg *Func, Variable *Dest, Operand *Source) { | 
|  | return new (Func->allocate<InstAssign>()) InstAssign(Func, Dest, Source); | 
|  | } | 
|  | bool isVarAssign() const override; | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Assign; } | 
|  |  | 
|  | private: | 
|  | InstAssign(Cfg *Func, Variable *Dest, Operand *Source); | 
|  | }; | 
|  |  | 
|  | /// Branch instruction. This represents both conditional and unconditional | 
|  | /// branches. | 
|  | class InstBr : public InstHighLevel { | 
|  | InstBr() = delete; | 
|  | InstBr(const InstBr &) = delete; | 
|  | InstBr &operator=(const InstBr &) = delete; | 
|  |  | 
|  | public: | 
|  | /// Create a conditional branch. If TargetTrue==TargetFalse, it is optimized | 
|  | /// to an unconditional branch. | 
|  | static InstBr *create(Cfg *Func, Operand *Source, CfgNode *TargetTrue, | 
|  | CfgNode *TargetFalse) { | 
|  | return new (Func->allocate<InstBr>()) | 
|  | InstBr(Func, Source, TargetTrue, TargetFalse); | 
|  | } | 
|  | /// Create an unconditional branch. | 
|  | static InstBr *create(Cfg *Func, CfgNode *Target) { | 
|  | return new (Func->allocate<InstBr>()) InstBr(Func, Target); | 
|  | } | 
|  | bool isUnconditional() const { return getTargetTrue() == nullptr; } | 
|  | Operand *getCondition() const { | 
|  | assert(!isUnconditional()); | 
|  | return getSrc(0); | 
|  | } | 
|  | CfgNode *getTargetTrue() const { return TargetTrue; } | 
|  | CfgNode *getTargetFalse() const { return TargetFalse; } | 
|  | CfgNode *getTargetUnconditional() const { | 
|  | assert(isUnconditional()); | 
|  | return getTargetFalse(); | 
|  | } | 
|  | NodeList getTerminatorEdges() const override; | 
|  | bool isUnconditionalBranch() const override { return isUnconditional(); } | 
|  | bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override; | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Br; } | 
|  |  | 
|  | private: | 
|  | /// Conditional branch | 
|  | InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue, CfgNode *TargetFalse); | 
|  | /// Unconditional branch | 
|  | InstBr(Cfg *Func, CfgNode *Target); | 
|  |  | 
|  | CfgNode *TargetFalse; /// Doubles as unconditional branch target | 
|  | CfgNode *TargetTrue;  /// nullptr if unconditional branch | 
|  | }; | 
|  |  | 
|  | /// Call instruction. The call target is captured as getSrc(0), and arg I is | 
|  | /// captured as getSrc(I+1). | 
|  | class InstCall : public InstHighLevel { | 
|  | InstCall() = delete; | 
|  | InstCall(const InstCall &) = delete; | 
|  | InstCall &operator=(const InstCall &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest, | 
|  | Operand *CallTarget, bool HasTailCall, | 
|  | bool IsTargetHelperCall = false) { | 
|  | /// Set HasSideEffects to true so that the call instruction can't be | 
|  | /// dead-code eliminated. IntrinsicCalls can override this if the particular | 
|  | /// intrinsic is deletable and has no side-effects. | 
|  | constexpr bool HasSideEffects = true; | 
|  | constexpr InstKind Kind = Inst::Call; | 
|  | return new (Func->allocate<InstCall>()) | 
|  | InstCall(Func, NumArgs, Dest, CallTarget, HasTailCall, | 
|  | IsTargetHelperCall, HasSideEffects, Kind); | 
|  | } | 
|  | void addArg(Operand *Arg) { addSource(Arg); } | 
|  | Operand *getCallTarget() const { return getSrc(0); } | 
|  | Operand *getArg(SizeT I) const { return getSrc(I + 1); } | 
|  | SizeT getNumArgs() const { return getSrcSize() - 1; } | 
|  | bool isTailcall() const { return HasTailCall; } | 
|  | bool isTargetHelperCall() const { return IsTargetHelperCall; } | 
|  | bool isMemoryWrite() const override { return true; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Call; } | 
|  | Type getReturnType() const; | 
|  |  | 
|  | protected: | 
|  | InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget, | 
|  | bool HasTailCall, bool IsTargetHelperCall, bool HasSideEff, | 
|  | InstKind Kind) | 
|  | : InstHighLevel(Func, Kind, NumArgs + 1, Dest), HasTailCall(HasTailCall), | 
|  | IsTargetHelperCall(IsTargetHelperCall) { | 
|  | HasSideEffects = HasSideEff; | 
|  | addSource(CallTarget); | 
|  | } | 
|  |  | 
|  | private: | 
|  | const bool HasTailCall; | 
|  | const bool IsTargetHelperCall; | 
|  | }; | 
|  |  | 
|  | /// Cast instruction (a.k.a. conversion operation). | 
|  | class InstCast : public InstHighLevel { | 
|  | InstCast() = delete; | 
|  | InstCast(const InstCast &) = delete; | 
|  | InstCast &operator=(const InstCast &) = delete; | 
|  |  | 
|  | public: | 
|  | enum OpKind { | 
|  | #define X(tag, str) tag, | 
|  | ICEINSTCAST_TABLE | 
|  | #undef X | 
|  | _num | 
|  | }; | 
|  |  | 
|  | static const char *getCastName(OpKind Kind); | 
|  |  | 
|  | static InstCast *create(Cfg *Func, OpKind CastKind, Variable *Dest, | 
|  | Operand *Source) { | 
|  | return new (Func->allocate<InstCast>()) | 
|  | InstCast(Func, CastKind, Dest, Source); | 
|  | } | 
|  | OpKind getCastKind() const { return CastKind; } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Cast; } | 
|  |  | 
|  | private: | 
|  | InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source); | 
|  |  | 
|  | const OpKind CastKind; | 
|  | }; | 
|  |  | 
|  | /// ExtractElement instruction. | 
|  | class InstExtractElement : public InstHighLevel { | 
|  | InstExtractElement() = delete; | 
|  | InstExtractElement(const InstExtractElement &) = delete; | 
|  | InstExtractElement &operator=(const InstExtractElement &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstExtractElement *create(Cfg *Func, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2) { | 
|  | return new (Func->allocate<InstExtractElement>()) | 
|  | InstExtractElement(Func, Dest, Source1, Source2); | 
|  | } | 
|  |  | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == ExtractElement; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstExtractElement(Cfg *Func, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2); | 
|  | }; | 
|  |  | 
|  | /// Floating-point comparison instruction. The source operands are captured in | 
|  | /// getSrc(0) and getSrc(1). | 
|  | class InstFcmp : public InstHighLevel { | 
|  | InstFcmp() = delete; | 
|  | InstFcmp(const InstFcmp &) = delete; | 
|  | InstFcmp &operator=(const InstFcmp &) = delete; | 
|  |  | 
|  | public: | 
|  | enum FCond { | 
|  | #define X(tag, str) tag, | 
|  | ICEINSTFCMP_TABLE | 
|  | #undef X | 
|  | _num | 
|  | }; | 
|  |  | 
|  | static InstFcmp *create(Cfg *Func, FCond Condition, Variable *Dest, | 
|  | Operand *Source1, Operand *Source2) { | 
|  | return new (Func->allocate<InstFcmp>()) | 
|  | InstFcmp(Func, Condition, Dest, Source1, Source2); | 
|  | } | 
|  | FCond getCondition() const { return Condition; } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Fcmp; } | 
|  |  | 
|  | private: | 
|  | InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2); | 
|  |  | 
|  | const FCond Condition; | 
|  | }; | 
|  |  | 
|  | /// Integer comparison instruction. The source operands are captured in | 
|  | /// getSrc(0) and getSrc(1). | 
|  | class InstIcmp : public InstHighLevel { | 
|  | InstIcmp() = delete; | 
|  | InstIcmp(const InstIcmp &) = delete; | 
|  | InstIcmp &operator=(const InstIcmp &) = delete; | 
|  |  | 
|  | public: | 
|  | enum ICond { | 
|  | #define X(tag, inverse, str) tag, | 
|  | ICEINSTICMP_TABLE | 
|  | #undef X | 
|  | _num | 
|  | }; | 
|  |  | 
|  | static InstIcmp *create(Cfg *Func, ICond Condition, Variable *Dest, | 
|  | Operand *Source1, Operand *Source2) { | 
|  | return new (Func->allocate<InstIcmp>()) | 
|  | InstIcmp(Func, Condition, Dest, Source1, Source2); | 
|  | } | 
|  | ICond getCondition() const { return Condition; } | 
|  | void reverseConditionAndOperands(); | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Icmp; } | 
|  |  | 
|  | private: | 
|  | InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2); | 
|  |  | 
|  | ICond Condition; | 
|  | }; | 
|  |  | 
|  | /// InsertElement instruction. | 
|  | class InstInsertElement : public InstHighLevel { | 
|  | InstInsertElement() = delete; | 
|  | InstInsertElement(const InstInsertElement &) = delete; | 
|  | InstInsertElement &operator=(const InstInsertElement &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstInsertElement *create(Cfg *Func, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2, Operand *Source3) { | 
|  | return new (Func->allocate<InstInsertElement>()) | 
|  | InstInsertElement(Func, Dest, Source1, Source2, Source3); | 
|  | } | 
|  |  | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == InsertElement; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstInsertElement(Cfg *Func, Variable *Dest, Operand *Source1, | 
|  | Operand *Source2, Operand *Source3); | 
|  | }; | 
|  |  | 
|  | /// Call to an intrinsic function. The call target is captured as getSrc(0), and | 
|  | /// arg I is captured as getSrc(I+1). | 
|  | class InstIntrinsicCall : public InstCall { | 
|  | InstIntrinsicCall() = delete; | 
|  | InstIntrinsicCall(const InstIntrinsicCall &) = delete; | 
|  | InstIntrinsicCall &operator=(const InstIntrinsicCall &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstIntrinsicCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest, | 
|  | Operand *CallTarget, | 
|  | const Intrinsics::IntrinsicInfo &Info) { | 
|  | return new (Func->allocate<InstIntrinsicCall>()) | 
|  | InstIntrinsicCall(Func, NumArgs, Dest, CallTarget, Info); | 
|  | } | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == IntrinsicCall; | 
|  | } | 
|  |  | 
|  | Intrinsics::IntrinsicInfo getIntrinsicInfo() const { return Info; } | 
|  | bool isMemoryWrite() const override { | 
|  | return getIntrinsicInfo().IsMemoryWrite; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest, | 
|  | Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info) | 
|  | : InstCall(Func, NumArgs, Dest, CallTarget, false, false, | 
|  | Info.HasSideEffects, Inst::IntrinsicCall), | 
|  | Info(Info) {} | 
|  |  | 
|  | const Intrinsics::IntrinsicInfo Info; | 
|  | }; | 
|  |  | 
|  | /// Load instruction. The source address is captured in getSrc(0). | 
|  | class InstLoad : public InstHighLevel { | 
|  | InstLoad() = delete; | 
|  | InstLoad(const InstLoad &) = delete; | 
|  | InstLoad &operator=(const InstLoad &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstLoad *create(Cfg *Func, Variable *Dest, Operand *SourceAddr, | 
|  | uint32_t Align = 1) { | 
|  | // TODO(kschimpf) Stop ignoring alignment specification. | 
|  | (void)Align; | 
|  | return new (Func->allocate<InstLoad>()) InstLoad(Func, Dest, SourceAddr); | 
|  | } | 
|  | Operand *getSourceAddress() const { return getSrc(0); } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Load; } | 
|  |  | 
|  | private: | 
|  | InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr); | 
|  | }; | 
|  |  | 
|  | /// Phi instruction. For incoming edge I, the node is Labels[I] and the Phi | 
|  | /// source operand is getSrc(I). | 
|  | class InstPhi : public InstHighLevel { | 
|  | InstPhi() = delete; | 
|  | InstPhi(const InstPhi &) = delete; | 
|  | InstPhi &operator=(const InstPhi &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstPhi *create(Cfg *Func, SizeT MaxSrcs, Variable *Dest) { | 
|  | return new (Func->allocate<InstPhi>()) InstPhi(Func, MaxSrcs, Dest); | 
|  | } | 
|  | void addArgument(Operand *Source, CfgNode *Label); | 
|  | Operand *getOperandForTarget(CfgNode *Target) const; | 
|  | void clearOperandForTarget(CfgNode *Target); | 
|  | CfgNode *getLabel(SizeT Index) const { return Labels[Index]; } | 
|  | void setLabel(SizeT Index, CfgNode *Label) { Labels[Index] = Label; } | 
|  | void livenessPhiOperand(LivenessBV &Live, CfgNode *Target, | 
|  | Liveness *Liveness); | 
|  | Inst *lower(Cfg *Func); | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Phi; } | 
|  |  | 
|  | private: | 
|  | InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest); | 
|  | void destroy(Cfg *Func) override { Inst::destroy(Func); } | 
|  |  | 
|  | /// Labels[] duplicates the InEdges[] information in the enclosing CfgNode, | 
|  | /// but the Phi instruction is created before InEdges[] is available, so it's | 
|  | /// more complicated to share the list. | 
|  | CfgVector<CfgNode *> Labels; | 
|  | }; | 
|  |  | 
|  | /// Ret instruction. The return value is captured in getSrc(0), but if there is | 
|  | /// no return value (void-type function), then getSrcSize()==0 and | 
|  | /// hasRetValue()==false. | 
|  | class InstRet : public InstHighLevel { | 
|  | InstRet() = delete; | 
|  | InstRet(const InstRet &) = delete; | 
|  | InstRet &operator=(const InstRet &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstRet *create(Cfg *Func, Operand *RetValue = nullptr) { | 
|  | return new (Func->allocate<InstRet>()) InstRet(Func, RetValue); | 
|  | } | 
|  | bool hasRetValue() const { return getSrcSize(); } | 
|  | Operand *getRetValue() const { | 
|  | assert(hasRetValue()); | 
|  | return getSrc(0); | 
|  | } | 
|  | NodeList getTerminatorEdges() const override { return NodeList(); } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Ret; } | 
|  |  | 
|  | private: | 
|  | InstRet(Cfg *Func, Operand *RetValue); | 
|  | }; | 
|  |  | 
|  | /// Select instruction.  The condition, true, and false operands are captured. | 
|  | class InstSelect : public InstHighLevel { | 
|  | InstSelect() = delete; | 
|  | InstSelect(const InstSelect &) = delete; | 
|  | InstSelect &operator=(const InstSelect &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstSelect *create(Cfg *Func, Variable *Dest, Operand *Condition, | 
|  | Operand *SourceTrue, Operand *SourceFalse) { | 
|  | return new (Func->allocate<InstSelect>()) | 
|  | InstSelect(Func, Dest, Condition, SourceTrue, SourceFalse); | 
|  | } | 
|  | Operand *getCondition() const { return getSrc(0); } | 
|  | Operand *getTrueOperand() const { return getSrc(1); } | 
|  | Operand *getFalseOperand() const { return getSrc(2); } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Select; } | 
|  |  | 
|  | private: | 
|  | InstSelect(Cfg *Func, Variable *Dest, Operand *Condition, Operand *Source1, | 
|  | Operand *Source2); | 
|  | }; | 
|  |  | 
|  | /// Store instruction. The address operand is captured, along with the data | 
|  | /// operand to be stored into the address. | 
|  | class InstStore : public InstHighLevel { | 
|  | InstStore() = delete; | 
|  | InstStore(const InstStore &) = delete; | 
|  | InstStore &operator=(const InstStore &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstStore *create(Cfg *Func, Operand *Data, Operand *Addr, | 
|  | uint32_t Align = 1) { | 
|  | // TODO(kschimpf) Stop ignoring alignment specification. | 
|  | (void)Align; | 
|  | return new (Func->allocate<InstStore>()) InstStore(Func, Data, Addr); | 
|  | } | 
|  | Operand *getAddr() const { return getSrc(1); } | 
|  | Operand *getData() const { return getSrc(0); } | 
|  | Variable *getRmwBeacon() const; | 
|  | void setRmwBeacon(Variable *Beacon); | 
|  | bool isMemoryWrite() const override { return true; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Store; } | 
|  |  | 
|  | private: | 
|  | InstStore(Cfg *Func, Operand *Data, Operand *Addr); | 
|  | }; | 
|  |  | 
|  | /// Switch instruction. The single source operand is captured as getSrc(0). | 
|  | class InstSwitch : public InstHighLevel { | 
|  | InstSwitch() = delete; | 
|  | InstSwitch(const InstSwitch &) = delete; | 
|  | InstSwitch &operator=(const InstSwitch &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstSwitch *create(Cfg *Func, SizeT NumCases, Operand *Source, | 
|  | CfgNode *LabelDefault) { | 
|  | return new (Func->allocate<InstSwitch>()) | 
|  | InstSwitch(Func, NumCases, Source, LabelDefault); | 
|  | } | 
|  | Operand *getComparison() const { return getSrc(0); } | 
|  | CfgNode *getLabelDefault() const { return LabelDefault; } | 
|  | SizeT getNumCases() const { return NumCases; } | 
|  | uint64_t getValue(SizeT I) const { | 
|  | assert(I < NumCases); | 
|  | return Values[I]; | 
|  | } | 
|  | CfgNode *getLabel(SizeT I) const { | 
|  | assert(I < NumCases); | 
|  | return Labels[I]; | 
|  | } | 
|  | void addBranch(SizeT CaseIndex, uint64_t Value, CfgNode *Label); | 
|  | NodeList getTerminatorEdges() const override; | 
|  | bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override; | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == Switch; } | 
|  |  | 
|  | private: | 
|  | InstSwitch(Cfg *Func, SizeT NumCases, Operand *Source, CfgNode *LabelDefault); | 
|  | void destroy(Cfg *Func) override { | 
|  | Func->deallocateArrayOf<uint64_t>(Values); | 
|  | Func->deallocateArrayOf<CfgNode *>(Labels); | 
|  | Inst::destroy(Func); | 
|  | } | 
|  |  | 
|  | CfgNode *LabelDefault; | 
|  | SizeT NumCases;   /// not including the default case | 
|  | uint64_t *Values; /// size is NumCases | 
|  | CfgNode **Labels; /// size is NumCases | 
|  | }; | 
|  |  | 
|  | /// Unreachable instruction. This is a terminator instruction with no operands. | 
|  | class InstUnreachable : public InstHighLevel { | 
|  | InstUnreachable() = delete; | 
|  | InstUnreachable(const InstUnreachable &) = delete; | 
|  | InstUnreachable &operator=(const InstUnreachable &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstUnreachable *create(Cfg *Func) { | 
|  | return new (Func->allocate<InstUnreachable>()) InstUnreachable(Func); | 
|  | } | 
|  | NodeList getTerminatorEdges() const override { return NodeList(); } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == Unreachable; | 
|  | } | 
|  |  | 
|  | private: | 
|  | explicit InstUnreachable(Cfg *Func); | 
|  | }; | 
|  |  | 
|  | /// BundleLock instruction.  There are no operands. Contains an option | 
|  | /// indicating whether align_to_end is specified. | 
|  | class InstBundleLock : public InstHighLevel { | 
|  | InstBundleLock() = delete; | 
|  | InstBundleLock(const InstBundleLock &) = delete; | 
|  | InstBundleLock &operator=(const InstBundleLock &) = delete; | 
|  |  | 
|  | public: | 
|  | enum Option { Opt_None, Opt_AlignToEnd, Opt_PadToEnd }; | 
|  | static InstBundleLock *create(Cfg *Func, Option BundleOption) { | 
|  | return new (Func->allocate<InstBundleLock>()) | 
|  | InstBundleLock(Func, BundleOption); | 
|  | } | 
|  | void emit(const Cfg *Func) const override; | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | Option getOption() const { return BundleOption; } | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == BundleLock; | 
|  | } | 
|  |  | 
|  | private: | 
|  | Option BundleOption; | 
|  | InstBundleLock(Cfg *Func, Option BundleOption); | 
|  | }; | 
|  |  | 
|  | /// BundleUnlock instruction. There are no operands. | 
|  | class InstBundleUnlock : public InstHighLevel { | 
|  | InstBundleUnlock() = delete; | 
|  | InstBundleUnlock(const InstBundleUnlock &) = delete; | 
|  | InstBundleUnlock &operator=(const InstBundleUnlock &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstBundleUnlock *create(Cfg *Func) { | 
|  | return new (Func->allocate<InstBundleUnlock>()) InstBundleUnlock(Func); | 
|  | } | 
|  | void emit(const Cfg *Func) const override; | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == BundleUnlock; | 
|  | } | 
|  |  | 
|  | private: | 
|  | explicit InstBundleUnlock(Cfg *Func); | 
|  | }; | 
|  |  | 
|  | /// FakeDef instruction. This creates a fake definition of a variable, which is | 
|  | /// how we represent the case when an instruction produces multiple results. | 
|  | /// This doesn't happen with high-level ICE instructions, but might with lowered | 
|  | /// instructions. For example, this would be a way to represent condition flags | 
|  | /// being modified by an instruction. | 
|  | /// | 
|  | /// It's generally useful to set the optional source operand to be the dest | 
|  | /// variable of the instruction that actually produces the FakeDef dest. | 
|  | /// Otherwise, the original instruction could be dead-code eliminated if its | 
|  | /// dest operand is unused, and therefore the FakeDef dest wouldn't be properly | 
|  | /// initialized. | 
|  | class InstFakeDef : public InstHighLevel { | 
|  | InstFakeDef() = delete; | 
|  | InstFakeDef(const InstFakeDef &) = delete; | 
|  | InstFakeDef &operator=(const InstFakeDef &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstFakeDef *create(Cfg *Func, Variable *Dest, | 
|  | Variable *Src = nullptr) { | 
|  | return new (Func->allocate<InstFakeDef>()) InstFakeDef(Func, Dest, Src); | 
|  | } | 
|  | void emit(const Cfg *Func) const override; | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == FakeDef; } | 
|  |  | 
|  | private: | 
|  | InstFakeDef(Cfg *Func, Variable *Dest, Variable *Src); | 
|  | }; | 
|  |  | 
|  | /// FakeUse instruction. This creates a fake use of a variable, to keep the | 
|  | /// instruction that produces that variable from being dead-code eliminated. | 
|  | /// This is useful in a variety of lowering situations. The FakeUse instruction | 
|  | /// has no dest, so it can itself never be dead-code eliminated.  A weight can | 
|  | /// be provided to provide extra bias to the register allocator - for simplicity | 
|  | /// of implementation, weight=N is handled by holding N copies of the variable | 
|  | /// as source operands. | 
|  | class InstFakeUse : public InstHighLevel { | 
|  | InstFakeUse() = delete; | 
|  | InstFakeUse(const InstFakeUse &) = delete; | 
|  | InstFakeUse &operator=(const InstFakeUse &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstFakeUse *create(Cfg *Func, Variable *Src, uint32_t Weight = 1) { | 
|  | return new (Func->allocate<InstFakeUse>()) InstFakeUse(Func, Src, Weight); | 
|  | } | 
|  | void emit(const Cfg *Func) const override; | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() == FakeUse; } | 
|  |  | 
|  | private: | 
|  | InstFakeUse(Cfg *Func, Variable *Src, uint32_t Weight); | 
|  | }; | 
|  |  | 
|  | /// FakeKill instruction. This "kills" a set of variables by modeling a trivial | 
|  | /// live range at this instruction for each (implicit) variable. The primary use | 
|  | /// is to indicate that scratch registers are killed after a call, so that the | 
|  | /// register allocator won't assign a scratch register to a variable whose live | 
|  | /// range spans a call. | 
|  | /// | 
|  | /// The FakeKill instruction also holds a pointer to the instruction that kills | 
|  | /// the set of variables, so that if that linked instruction gets dead-code | 
|  | /// eliminated, the FakeKill instruction will as well. | 
|  | class InstFakeKill : public InstHighLevel { | 
|  | InstFakeKill() = delete; | 
|  | InstFakeKill(const InstFakeKill &) = delete; | 
|  | InstFakeKill &operator=(const InstFakeKill &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstFakeKill *create(Cfg *Func, const Inst *Linked) { | 
|  | return new (Func->allocate<InstFakeKill>()) InstFakeKill(Func, Linked); | 
|  | } | 
|  | const Inst *getLinked() const { return Linked; } | 
|  | void emit(const Cfg *Func) const override; | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == FakeKill; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstFakeKill(Cfg *Func, const Inst *Linked); | 
|  |  | 
|  | /// This instruction is ignored if Linked->isDeleted() is true. | 
|  | const Inst *Linked; | 
|  | }; | 
|  |  | 
|  | /// ShuffleVector instruction. This represents a shuffle operation on vector | 
|  | /// types. This instruction is not part of the PNaCl bitcode: it is generated | 
|  | /// by Subzero when it matches the pattern used by pnacl-clang when compiling | 
|  | /// to bitcode. | 
|  | class InstShuffleVector : public InstHighLevel { | 
|  | InstShuffleVector() = delete; | 
|  | InstShuffleVector(const InstShuffleVector &) = delete; | 
|  | InstShuffleVector &operator=(const InstShuffleVector &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstShuffleVector *create(Cfg *Func, Variable *Dest, Operand *Src0, | 
|  | Operand *Src1) { | 
|  | return new (Func->allocate<InstShuffleVector>()) | 
|  | InstShuffleVector(Func, Dest, Src0, Src1); | 
|  | } | 
|  |  | 
|  | SizeT getNumIndexes() const { return NumIndexes; } | 
|  |  | 
|  | void addIndex(ConstantInteger32 *Index) { | 
|  | assert(CurrentIndex < NumIndexes); | 
|  | Indexes[CurrentIndex++] = Index; | 
|  | } | 
|  |  | 
|  | ConstantInteger32 *getIndex(SizeT Pos) const { | 
|  | assert(Pos < NumIndexes); | 
|  | return Indexes[Pos]; | 
|  | } | 
|  |  | 
|  | int32_t getIndexValue(SizeT Pos) const { return getIndex(Pos)->getValue(); } | 
|  |  | 
|  | bool indexesAre(int32_t i0, int32_t i1, int32_t i2, int32_t i3) const { | 
|  | static constexpr SizeT ExpectedNumElements = 4; | 
|  | assert(ExpectedNumElements == getNumIndexes()); | 
|  | (void)ExpectedNumElements; | 
|  |  | 
|  | return getIndexValue(0) == i0 && getIndexValue(1) == i1 && | 
|  | getIndexValue(2) == i2 && getIndexValue(3) == i3; | 
|  | } | 
|  |  | 
|  | bool indexesAre(int32_t i0, int32_t i1, int32_t i2, int32_t i3, int32_t i4, | 
|  | int32_t i5, int32_t i6, int32_t i7) const { | 
|  | static constexpr SizeT ExpectedNumElements = 8; | 
|  | assert(ExpectedNumElements == getNumIndexes()); | 
|  | (void)ExpectedNumElements; | 
|  |  | 
|  | return getIndexValue(0) == i0 && getIndexValue(1) == i1 && | 
|  | getIndexValue(2) == i2 && getIndexValue(3) == i3 && | 
|  | getIndexValue(4) == i4 && getIndexValue(5) == i5 && | 
|  | getIndexValue(6) == i6 && getIndexValue(7) == i7; | 
|  | } | 
|  |  | 
|  | bool indexesAre(int32_t i0, int32_t i1, int32_t i2, int32_t i3, int32_t i4, | 
|  | int32_t i5, int32_t i6, int32_t i7, int32_t i8, int32_t i9, | 
|  | int32_t i10, int32_t i11, int32_t i12, int32_t i13, | 
|  | int32_t i14, int32_t i15) const { | 
|  | static constexpr SizeT ExpectedNumElements = 16; | 
|  | assert(ExpectedNumElements == getNumIndexes()); | 
|  | (void)ExpectedNumElements; | 
|  |  | 
|  | return getIndexValue(0) == i0 && getIndexValue(1) == i1 && | 
|  | getIndexValue(2) == i2 && getIndexValue(3) == i3 && | 
|  | getIndexValue(4) == i4 && getIndexValue(5) == i5 && | 
|  | getIndexValue(6) == i6 && getIndexValue(7) == i7 && | 
|  | getIndexValue(8) == i8 && getIndexValue(9) == i9 && | 
|  | getIndexValue(10) == i10 && getIndexValue(11) == i11 && | 
|  | getIndexValue(12) == i12 && getIndexValue(13) == i13 && | 
|  | getIndexValue(14) == i14 && getIndexValue(15) == i15; | 
|  | } | 
|  |  | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == ShuffleVector; | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstShuffleVector(Cfg *Func, Variable *Dest, Operand *Src0, Operand *Src1); | 
|  |  | 
|  | void destroy(Cfg *Func) override { | 
|  | Func->deallocateArrayOf<ConstantInteger32 *>(Indexes); | 
|  | Inst::destroy(Func); | 
|  | } | 
|  |  | 
|  | ConstantInteger32 **Indexes; | 
|  | SizeT CurrentIndex = 0; | 
|  | const SizeT NumIndexes; | 
|  | }; | 
|  |  | 
|  | /// JumpTable instruction. This represents a jump table that will be stored in | 
|  | /// the .rodata section. This is used to track and repoint the target CfgNodes | 
|  | /// which may change, for example due to splitting for phi lowering. | 
|  | class InstJumpTable : public InstHighLevel { | 
|  | InstJumpTable() = delete; | 
|  | InstJumpTable(const InstJumpTable &) = delete; | 
|  | InstJumpTable &operator=(const InstJumpTable &) = delete; | 
|  |  | 
|  | public: | 
|  | static InstJumpTable *create(Cfg *Func, SizeT NumTargets, CfgNode *Default) { | 
|  | return new (Func->allocate<InstJumpTable>()) | 
|  | InstJumpTable(Func, NumTargets, Default); | 
|  | } | 
|  | void addTarget(SizeT TargetIndex, CfgNode *Target) { | 
|  | assert(TargetIndex < NumTargets); | 
|  | Targets[TargetIndex] = Target; | 
|  | } | 
|  | bool repointEdges(CfgNode *OldNode, CfgNode *NewNode) override; | 
|  | SizeT getId() const { return Id; } | 
|  | SizeT getNumTargets() const { return NumTargets; } | 
|  | CfgNode *getTarget(SizeT I) const { | 
|  | assert(I < NumTargets); | 
|  | return Targets[I]; | 
|  | } | 
|  | bool isMemoryWrite() const override { return false; } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == JumpTable; | 
|  | } | 
|  | // Creates a JumpTableData struct (used for ELF emission) that represents this | 
|  | // InstJumpTable. | 
|  | JumpTableData toJumpTableData(Assembler *Asm) const; | 
|  |  | 
|  | // InstJumpTable is just a placeholder for the switch targets, and it does not | 
|  | // need to emit any code, so we redefine emit and emitIAS to do nothing. | 
|  | void emit(const Cfg *) const override {} | 
|  | void emitIAS(const Cfg * /* Func */) const override {} | 
|  |  | 
|  | const std::string getName() const { | 
|  | assert(Name.hasStdString()); | 
|  | return Name.toString(); | 
|  | } | 
|  |  | 
|  | std::string getSectionName() const { | 
|  | return JumpTableData::createSectionName(FuncName); | 
|  | } | 
|  |  | 
|  | private: | 
|  | InstJumpTable(Cfg *Func, SizeT NumTargets, CfgNode *Default); | 
|  | void destroy(Cfg *Func) override { | 
|  | Func->deallocateArrayOf<CfgNode *>(Targets); | 
|  | Inst::destroy(Func); | 
|  | } | 
|  |  | 
|  | const SizeT Id; | 
|  | const SizeT NumTargets; | 
|  | CfgNode **Targets; | 
|  | GlobalString Name; // This JumpTable's name in the output. | 
|  | GlobalString FuncName; | 
|  | }; | 
|  |  | 
|  | /// This instruction inserts an unconditional breakpoint. | 
|  | /// | 
|  | /// On x86, this assembles into an INT 3 instruction. | 
|  | /// | 
|  | /// This instruction is primarily meant for debugging the code generator. | 
|  | class InstBreakpoint : public InstHighLevel { | 
|  | public: | 
|  | InstBreakpoint() = delete; | 
|  | InstBreakpoint(const InstBreakpoint &) = delete; | 
|  | InstBreakpoint &operator=(const InstBreakpoint &) = delete; | 
|  |  | 
|  | explicit InstBreakpoint(Cfg *Func); | 
|  | bool isMemoryWrite() const override { return false; } | 
|  |  | 
|  | public: | 
|  | static InstBreakpoint *create(Cfg *Func) { | 
|  | return new (Func->allocate<InstBreakpoint>()) InstBreakpoint(Func); | 
|  | } | 
|  |  | 
|  | static bool classof(const Inst *Instr) { | 
|  | return Instr->getKind() == Breakpoint; | 
|  | } | 
|  | }; | 
|  |  | 
|  | /// The Target instruction is the base class for all target-specific | 
|  | /// instructions. | 
|  | class InstTarget : public Inst { | 
|  | InstTarget() = delete; | 
|  | InstTarget(const InstTarget &) = delete; | 
|  | InstTarget &operator=(const InstTarget &) = delete; | 
|  |  | 
|  | public: | 
|  | uint32_t getEmitInstCount() const override { return 1; } | 
|  | bool isMemoryWrite() const override { | 
|  | return true; // conservative answer | 
|  | } | 
|  | void dump(const Cfg *Func) const override; | 
|  | static bool classof(const Inst *Instr) { return Instr->getKind() >= Target; } | 
|  |  | 
|  | protected: | 
|  | InstTarget(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest) | 
|  | : Inst(Func, Kind, MaxSrcs, Dest) { | 
|  | assert(Kind >= Target); | 
|  | assert(Kind <= Target_Max); | 
|  | } | 
|  | }; | 
|  |  | 
|  | bool checkForRedundantAssign(const Variable *Dest, const Operand *Source); | 
|  |  | 
|  | } // end of namespace Ice | 
|  |  | 
|  | #ifdef PNACL_LLVM | 
|  | namespace llvm { | 
|  |  | 
|  | /// Override the default ilist traits so that Inst's private ctor and deleted | 
|  | /// dtor aren't invoked. | 
|  | template <> | 
|  | struct ilist_traits<Ice::Inst> : public ilist_default_traits<Ice::Inst> { | 
|  | Ice::Inst *createSentinel() const { | 
|  | return static_cast<Ice::Inst *>(&Sentinel); | 
|  | } | 
|  | static void destroySentinel(Ice::Inst *) {} | 
|  | Ice::Inst *provideInitialHead() const { return createSentinel(); } | 
|  | Ice::Inst *ensureHead(Ice::Inst *) const { return createSentinel(); } | 
|  | static void noteHead(Ice::Inst *, Ice::Inst *) {} | 
|  | void deleteNode(Ice::Inst *) {} | 
|  |  | 
|  | private: | 
|  | mutable ilist_half_node<Ice::Inst> Sentinel; | 
|  | }; | 
|  |  | 
|  | } // end of namespace llvm | 
|  | #endif // PNACL_LLVM | 
|  |  | 
|  | namespace Ice { | 
|  |  | 
|  | inline InstList::iterator instToIterator(Inst *Instr) { | 
|  | #ifdef PNACL_LLVM | 
|  | return Instr; | 
|  | #else  // !PNACL_LLVM | 
|  | return Instr->getIterator(); | 
|  | #endif // !PNACL_LLVM | 
|  | } | 
|  |  | 
|  | inline Inst *iteratorToInst(InstList::iterator Iter) { return &*Iter; } | 
|  |  | 
|  | inline const Inst *iteratorToInst(InstList::const_iterator Iter) { | 
|  | return &*Iter; | 
|  | } | 
|  |  | 
|  | inline InstList::iterator | 
|  | reverseToForwardIterator(InstList::reverse_iterator RI) { | 
|  | #ifdef PNACL_LLVM | 
|  | return RI.base(); | 
|  | #else  // !PNACL_LLVM | 
|  | return ++RI.getReverse(); | 
|  | #endif // !PNACL_LLVM | 
|  | } | 
|  |  | 
|  | } // end of namespace Ice | 
|  |  | 
|  | #endif // SUBZERO_SRC_ICEINST_H |