blob: 8dd77eca231f8d9bd2309c4f054636e5a5896653 [file] [log] [blame]
//===- 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.
//
//===----------------------------------------------------------------------===//
//
// This file declares the Inst class and its target-independent
// subclasses, which 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 "IceDefs.h"
#include "IceInst.def"
#include "IceIntrinsics.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(const Inst &) = delete;
Inst &operator=(const Inst &) = delete;
public:
enum InstKind {
// Arbitrary (alphabetical) order, except put Unreachable first.
Unreachable,
Alloca,
Arithmetic,
Assign, // not part of LLVM/PNaCl bitcode
Br,
Call,
Cast,
ExtractElement,
Fcmp,
Icmp,
IntrinsicCall,
InsertElement,
Load,
Phi,
Ret,
Select,
Store,
Switch,
FakeDef, // not part of LLVM/PNaCl bitcode
FakeUse, // not part of LLVM/PNaCl bitcode
FakeKill, // not part of LLVM/PNaCl bitcode
Target // target-specific low-level ICE
// Anything >= Target is an InstTarget subclass.
};
InstKind getKind() const { return Kind; }
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 deleteIfDead();
bool hasSideEffects() const { return HasSideEffects; }
bool isDestNonKillable() const { return IsDestNonKillable; }
void setDestNonKillable() { IsDestNonKillable = true; }
Variable *getDest() const { return Dest; }
SizeT getSrcSize() const { return NumSrcs; }
Operand *getSrc(SizeT I) const {
assert(I < getSrcSize());
return Srcs[I];
}
bool isLastUse(const Operand *Src) const;
// Returns a list of out-edges corresponding to a terminator
// instruction, which is the last instruction of the block.
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. Only repoint one instance, even if the instruction has
// multiple instances of OldNode as a target.
virtual bool repointEdge(CfgNode *OldNode, CfgNode *NewNode) {
(void)OldNode;
(void)NewNode;
return false;
}
virtual bool isSimpleAssign() const { return false; }
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() {}
protected:
Inst(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest);
void addSource(Operand *Src) {
assert(Src);
assert(NumSrcs < MaxSrcs);
Srcs[NumSrcs++] = 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 *Func) { Func->deallocateArrayOf<Operand *>(Srcs); }
const InstKind Kind;
// Number is the instruction number for describing live ranges.
InstNumberT Number;
// Deleted means irrevocably deleted.
bool Deleted;
// Dead means pending deletion after liveness analysis converges.
bool Dead;
// 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;
// IsDestNonKillable means that liveness analysis shouldn't consider
// this instruction to kill the Dest variable. This is used when
// lowering produces two assignments to the same variable.
bool IsDestNonKillable;
Variable *Dest;
const SizeT MaxSrcs; // only used for assert
SizeT NumSrcs;
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.
typedef uint32_t LREndedBits; // only first 32 src operands tracked, sorry
LREndedBits LiveRangesEnded;
};
class InstHighLevel : public Inst {
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");
}
~InstHighLevel() override {}
};
// 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(const InstAlloca &) = delete;
InstAlloca &operator=(const InstAlloca &) = delete;
public:
static InstAlloca *create(Cfg *Func, Operand *ByteCount,
uint32_t AlignInBytes, Variable *Dest) {
return new (Func->allocate<InstAlloca>())
InstAlloca(Func, ByteCount, AlignInBytes, Dest);
}
uint32_t getAlignInBytes() const { return AlignInBytes; }
Operand *getSizeInBytes() const { return getSrc(0); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Alloca; }
private:
InstAlloca(Cfg *Func, Operand *ByteCount, uint32_t AlignInBytes,
Variable *Dest);
~InstAlloca() override {}
const uint32_t AlignInBytes;
};
// Binary arithmetic instruction. The source operands are captured in
// getSrc(0) and getSrc(1).
class InstArithmetic : public InstHighLevel {
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; }
static const char *getOpName(OpKind Op);
bool isCommutative() const;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) {
return Inst->getKind() == Arithmetic;
}
private:
InstArithmetic(Cfg *Func, OpKind Op, Variable *Dest, Operand *Source1,
Operand *Source2);
~InstArithmetic() override {}
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(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 isSimpleAssign() const override { return true; }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Assign; }
private:
InstAssign(Cfg *Func, Variable *Dest, Operand *Source);
~InstAssign() override {}
};
// Branch instruction. This represents both conditional and
// unconditional branches.
class InstBr : public InstHighLevel {
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 repointEdge(CfgNode *OldNode, CfgNode *NewNode) override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Br; }
private:
// Conditional branch
InstBr(Cfg *Func, Operand *Source, CfgNode *TargetTrue, CfgNode *TargetFalse);
// Unconditional branch
InstBr(Cfg *Func, CfgNode *Target);
~InstBr() override {}
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(const InstCall &) = delete;
InstCall &operator=(const InstCall &) = delete;
public:
static InstCall *create(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget, bool HasTailCall) {
// 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.
const bool HasSideEffects = true;
const InstKind Kind = Inst::Call;
return new (Func->allocate<InstCall>()) InstCall(
Func, NumArgs, Dest, CallTarget, HasTailCall, 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; }
void dump(const Cfg *Func) const;
static bool classof(const Inst *Inst) { return Inst->getKind() == Call; }
Type getReturnType() const;
protected:
InstCall(Cfg *Func, SizeT NumArgs, Variable *Dest, Operand *CallTarget,
bool HasTailCall, bool HasSideEff, InstKind Kind)
: InstHighLevel(Func, Kind, NumArgs + 1, Dest), HasTailCall(HasTailCall) {
HasSideEffects = HasSideEff;
addSource(CallTarget);
}
~InstCall() override {}
private:
bool HasTailCall;
};
// Cast instruction (a.k.a. conversion operation).
class InstCast : public InstHighLevel {
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; }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Cast; }
private:
InstCast(Cfg *Func, OpKind CastKind, Variable *Dest, Operand *Source);
~InstCast() override {}
const OpKind CastKind;
};
// ExtractElement instruction.
class InstExtractElement : public InstHighLevel {
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);
}
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) {
return Inst->getKind() == ExtractElement;
}
private:
InstExtractElement(Cfg *Func, Variable *Dest, Operand *Source1,
Operand *Source2);
~InstExtractElement() override {}
};
// Floating-point comparison instruction. The source operands are
// captured in getSrc(0) and getSrc(1).
class InstFcmp : public InstHighLevel {
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; }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Fcmp; }
private:
InstFcmp(Cfg *Func, FCond Condition, Variable *Dest, Operand *Source1,
Operand *Source2);
~InstFcmp() override {}
const FCond Condition;
};
// Integer comparison instruction. The source operands are captured
// in getSrc(0) and getSrc(1).
class InstIcmp : public InstHighLevel {
InstIcmp(const InstIcmp &) = delete;
InstIcmp &operator=(const InstIcmp &) = delete;
public:
enum ICond {
#define X(tag, 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 dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Icmp; }
private:
InstIcmp(Cfg *Func, ICond Condition, Variable *Dest, Operand *Source1,
Operand *Source2);
~InstIcmp() override {}
const ICond Condition;
};
// InsertElement instruction.
class InstInsertElement : public InstHighLevel {
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);
}
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) {
return Inst->getKind() == InsertElement;
}
private:
InstInsertElement(Cfg *Func, Variable *Dest, Operand *Source1,
Operand *Source2, Operand *Source3);
~InstInsertElement() override {}
};
// 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(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 *Inst) {
return Inst->getKind() == IntrinsicCall;
}
Intrinsics::IntrinsicInfo getIntrinsicInfo() const { return Info; }
private:
InstIntrinsicCall(Cfg *Func, SizeT NumArgs, Variable *Dest,
Operand *CallTarget, const Intrinsics::IntrinsicInfo &Info)
: InstCall(Func, NumArgs, Dest, CallTarget, false, Info.HasSideEffects,
Inst::IntrinsicCall),
Info(Info) {}
~InstIntrinsicCall() override {}
const Intrinsics::IntrinsicInfo Info;
};
// Load instruction. The source address is captured in getSrc(0).
class InstLoad : public InstHighLevel {
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); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Load; }
private:
InstLoad(Cfg *Func, Variable *Dest, Operand *SourceAddr);
~InstLoad() override {}
};
// Phi instruction. For incoming edge I, the node is Labels[I] and
// the Phi source operand is getSrc(I).
class InstPhi : public InstHighLevel {
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;
CfgNode *getLabel(SizeT Index) const { return Labels[Index]; }
void livenessPhiOperand(LivenessBV &Live, CfgNode *Target,
Liveness *Liveness);
Inst *lower(Cfg *Func);
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Phi; }
private:
InstPhi(Cfg *Func, SizeT MaxSrcs, Variable *Dest);
void destroy(Cfg *Func) override {
Func->deallocateArrayOf<CfgNode *>(Labels);
Inst::destroy(Func);
}
~InstPhi() override {}
// 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.
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(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(); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Ret; }
private:
InstRet(Cfg *Func, Operand *RetValue);
~InstRet() override {}
};
// Select instruction. The condition, true, and false operands are captured.
class InstSelect : public InstHighLevel {
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); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Select; }
private:
InstSelect(Cfg *Func, Variable *Dest, Operand *Condition, Operand *Source1,
Operand *Source2);
~InstSelect() override {}
};
// Store instruction. The address operand is captured, along with the
// data operand to be stored into the address.
class InstStore : public InstHighLevel {
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); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == Store; }
private:
InstStore(Cfg *Func, Operand *Data, Operand *Addr);
~InstStore() override {}
};
// Switch instruction. The single source operand is captured as
// getSrc(0).
class InstSwitch : public InstHighLevel {
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 repointEdge(CfgNode *OldNode, CfgNode *NewNode) override;
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->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);
}
~InstSwitch() override {}
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(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(); }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) {
return Inst->getKind() == Unreachable;
}
private:
InstUnreachable(Cfg *Func);
~InstUnreachable() override {}
};
// 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(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 {}
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == FakeDef; }
private:
InstFakeDef(Cfg *Func, Variable *Dest, Variable *Src);
~InstFakeDef() override {}
};
// 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.
class InstFakeUse : public InstHighLevel {
InstFakeUse(const InstFakeUse &) = delete;
InstFakeUse &operator=(const InstFakeUse &) = delete;
public:
static InstFakeUse *create(Cfg *Func, Variable *Src) {
return new (Func->allocate<InstFakeUse>()) InstFakeUse(Func, Src);
}
void emit(const Cfg *Func) const override;
void emitIAS(const Cfg * /* Func */) const override {}
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == FakeUse; }
private:
InstFakeUse(Cfg *Func, Variable *Src);
~InstFakeUse() override {}
};
// 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(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 {}
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() == FakeKill; }
private:
InstFakeKill(Cfg *Func, const Inst *Linked);
~InstFakeKill() override {}
// This instruction is ignored if Linked->isDeleted() is true.
const Inst *Linked;
};
// The Target instruction is the base class for all target-specific
// instructions.
class InstTarget : public Inst {
InstTarget(const InstTarget &) = delete;
InstTarget &operator=(const InstTarget &) = delete;
public:
uint32_t getEmitInstCount() const override { return 1; }
void dump(const Cfg *Func) const override;
static bool classof(const Inst *Inst) { return Inst->getKind() >= Target; }
protected:
InstTarget(Cfg *Func, InstKind Kind, SizeT MaxSrcs, Variable *Dest)
: Inst(Func, Kind, MaxSrcs, Dest) {
assert(Kind >= Target);
}
~InstTarget() override {}
};
} // end of namespace Ice
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 // SUBZERO_SRC_ICEINST_H