//===- subzero/src/IceTargetLowering.h - Lowering interface -----*- 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 TargetLowering, LoweringContext, and TargetDataLowering /// classes. /// /// TargetLowering is an abstract class used to drive the translation/lowering /// process. LoweringContext maintains a context for lowering each instruction, /// offering conveniences such as iterating over non-deleted instructions. /// TargetDataLowering is an abstract class used to drive the lowering/emission /// of global initializers, external global declarations, and internal constant /// pools. /// //===----------------------------------------------------------------------===// #ifndef SUBZERO_SRC_ICETARGETLOWERING_H #define SUBZERO_SRC_ICETARGETLOWERING_H #include "IceBitVector.h" #include "IceCfgNode.h" #include "IceDefs.h" #include "IceInst.h" // for the names of the Inst subtypes #include "IceOperand.h" #include "IceRegAlloc.h" #include "IceTypes.h" #include <utility> namespace Ice { // UnimplementedError is defined as a macro so that we can get actual line // numbers. #define UnimplementedError(Flags) \ do { \ if (!static_cast<const ClFlags &>(Flags).getSkipUnimplemented()) { \ /* Use llvm_unreachable instead of report_fatal_error, which gives \ better stack traces. */ \ llvm_unreachable("Not yet implemented"); \ abort(); \ } \ } while (0) // UnimplementedLoweringError is similar in style to UnimplementedError. Given // a TargetLowering object pointer and an Inst pointer, it adds appropriate // FakeDef and FakeUse instructions to try maintain liveness consistency. #define UnimplementedLoweringError(Target, Instr) \ do { \ if (getFlags().getSkipUnimplemented()) { \ (Target)->addFakeDefUses(Instr); \ } else { \ /* Use llvm_unreachable instead of report_fatal_error, which gives \ better stack traces. */ \ llvm_unreachable( \ (std::string("Not yet implemented: ") + Instr->getInstName()) \ .c_str()); \ abort(); \ } \ } while (0) /// LoweringContext makes it easy to iterate through non-deleted instructions in /// a node, and insert new (lowered) instructions at the current point. Along /// with the instruction list container and associated iterators, it holds the /// current node, which is needed when inserting new instructions in order to /// track whether variables are used as single-block or multi-block. class LoweringContext { LoweringContext(const LoweringContext &) = delete; LoweringContext &operator=(const LoweringContext &) = delete; public: LoweringContext() = default; ~LoweringContext() = default; void init(CfgNode *Node); Inst *getNextInst() const { if (Next == End) return nullptr; return iteratorToInst(Next); } Inst *getNextInst(InstList::iterator &Iter) const { advanceForward(Iter); if (Iter == End) return nullptr; return iteratorToInst(Iter); } CfgNode *getNode() const { return Node; } bool atEnd() const { return Cur == End; } InstList::iterator getCur() const { return Cur; } InstList::iterator getNext() const { return Next; } InstList::iterator getEnd() const { return End; } void insert(Inst *Instr); template <typename Inst, typename... Args> Inst *insert(Args &&... A) { auto *New = Inst::create(Node->getCfg(), std::forward<Args>(A)...); insert(New); return New; } Inst *getLastInserted() const; void advanceCur() { Cur = Next; } void advanceNext() { advanceForward(Next); } void setCur(InstList::iterator C) { Cur = C; } void setNext(InstList::iterator N) { Next = N; } void rewind(); void setInsertPoint(const InstList::iterator &Position) { Next = Position; } void availabilityReset(); void availabilityUpdate(); Variable *availabilityGet(Operand *Src) const; private: /// Node is the argument to Inst::updateVars(). CfgNode *Node = nullptr; Inst *LastInserted = nullptr; /// Cur points to the current instruction being considered. It is guaranteed /// to point to a non-deleted instruction, or to be End. InstList::iterator Cur; /// Next doubles as a pointer to the next valid instruction (if any), and the /// new-instruction insertion point. It is also updated for the caller in case /// the lowering consumes more than one high-level instruction. It is /// guaranteed to point to a non-deleted instruction after Cur, or to be End. // TODO: Consider separating the notion of "next valid instruction" and "new // instruction insertion point", to avoid confusion when previously-deleted // instructions come between the two points. InstList::iterator Next; /// Begin is a copy of Insts.begin(), used if iterators are moved backward. InstList::iterator Begin; /// End is a copy of Insts.end(), used if Next needs to be advanced. InstList::iterator End; /// LastDest and LastSrc capture the parameters of the last "Dest=Src" simple /// assignment inserted (provided Src is a variable). This is used for simple /// availability analysis. Variable *LastDest = nullptr; Variable *LastSrc = nullptr; void skipDeleted(InstList::iterator &I) const; void advanceForward(InstList::iterator &I) const; }; /// A helper class to advance the LoweringContext at each loop iteration. class PostIncrLoweringContext { PostIncrLoweringContext() = delete; PostIncrLoweringContext(const PostIncrLoweringContext &) = delete; PostIncrLoweringContext &operator=(const PostIncrLoweringContext &) = delete; public: explicit PostIncrLoweringContext(LoweringContext &Context) : Context(Context) {} ~PostIncrLoweringContext() { Context.advanceCur(); Context.advanceNext(); } private: LoweringContext &Context; }; /// TargetLowering is the base class for all backends in Subzero. In addition to /// implementing the abstract methods in this class, each concrete target must /// also implement a named constructor in its own namespace. For instance, for /// X8632 we have: /// /// namespace X8632 { /// void createTargetLowering(Cfg *Func); /// } class TargetLowering { TargetLowering() = delete; TargetLowering(const TargetLowering &) = delete; TargetLowering &operator=(const TargetLowering &) = delete; public: static void staticInit(GlobalContext *Ctx); // Each target must define a public static method: // static void staticInit(GlobalContext *Ctx); static bool shouldBePooled(const class Constant *C); static Type getPointerType(); static std::unique_ptr<TargetLowering> createLowering(TargetArch Target, Cfg *Func); virtual std::unique_ptr<Assembler> createAssembler() const = 0; void translate() { switch (Func->getOptLevel()) { case Opt_m1: translateOm1(); break; case Opt_0: translateO0(); break; case Opt_1: translateO1(); break; case Opt_2: translateO2(); break; } } virtual void translateOm1() { Func->setError("Target doesn't specify Om1 lowering steps."); } virtual void translateO0() { Func->setError("Target doesn't specify O0 lowering steps."); } virtual void translateO1() { Func->setError("Target doesn't specify O1 lowering steps."); } virtual void translateO2() { Func->setError("Target doesn't specify O2 lowering steps."); } /// Generates calls to intrinsics for operations the Target can't handle. void genTargetHelperCalls(); /// Tries to do address mode optimization on a single instruction. void doAddressOpt(); /// Randomly insert NOPs. void doNopInsertion(RandomNumberGenerator &RNG); /// Lowers a single non-Phi instruction. void lower(); /// Inserts and lowers a single high-level instruction at a specific insertion /// point. void lowerInst(CfgNode *Node, InstList::iterator Next, InstHighLevel *Instr); /// Does preliminary lowering of the set of Phi instructions in the current /// node. The main intention is to do what's needed to keep the unlowered Phi /// instructions consistent with the lowered non-Phi instructions, e.g. to /// lower 64-bit operands on a 32-bit target. virtual void prelowerPhis() {} /// Tries to do branch optimization on a single instruction. Returns true if /// some optimization was done. virtual bool doBranchOpt(Inst * /*I*/, const CfgNode * /*NextNode*/) { return false; } virtual SizeT getNumRegisters() const = 0; /// Returns a variable pre-colored to the specified physical register. This is /// generally used to get very direct access to the register such as in the /// prolog or epilog or for marking scratch registers as killed by a call. If /// a Type is not provided, a target-specific default type is used. virtual Variable *getPhysicalRegister(RegNumT RegNum, Type Ty = IceType_void) = 0; /// Returns a printable name for the register. virtual const char *getRegName(RegNumT RegNum, Type Ty) const = 0; virtual bool hasFramePointer() const { return false; } virtual void setHasFramePointer() = 0; virtual RegNumT getStackReg() const = 0; virtual RegNumT getFrameReg() const = 0; virtual RegNumT getFrameOrStackReg() const = 0; virtual size_t typeWidthInBytesOnStack(Type Ty) const = 0; virtual uint32_t getStackAlignment() const = 0; virtual bool needsStackPointerAlignment() const { return false; } virtual void reserveFixedAllocaArea(size_t Size, size_t Align) = 0; virtual int32_t getFrameFixedAllocaOffset() const = 0; virtual uint32_t maxOutArgsSizeBytes() const { return 0; } // Addressing relative to frame pointer differs in MIPS compared to X86/ARM // since MIPS decrements its stack pointer prior to saving it in the frame // pointer register. virtual uint32_t getFramePointerOffset(uint32_t CurrentOffset, uint32_t Size) const { return -(CurrentOffset + Size); } /// Return whether a 64-bit Variable should be split into a Variable64On32. virtual bool shouldSplitToVariable64On32(Type Ty) const = 0; /// Return whether a Vector Variable should be split into a VariableVecOn32. virtual bool shouldSplitToVariableVecOn32(Type Ty) const { (void)Ty; return false; } bool hasComputedFrame() const { return HasComputedFrame; } /// Returns true if this function calls a function that has the "returns /// twice" attribute. bool callsReturnsTwice() const { return CallsReturnsTwice; } void setCallsReturnsTwice(bool RetTwice) { CallsReturnsTwice = RetTwice; } SizeT makeNextLabelNumber() { return NextLabelNumber++; } SizeT makeNextJumpTableNumber() { return NextJumpTableNumber++; } LoweringContext &getContext() { return Context; } Cfg *getFunc() const { return Func; } GlobalContext *getGlobalContext() const { return Ctx; } enum RegSet { RegSet_None = 0, RegSet_CallerSave = 1 << 0, RegSet_CalleeSave = 1 << 1, RegSet_StackPointer = 1 << 2, RegSet_FramePointer = 1 << 3, RegSet_All = ~RegSet_None }; using RegSetMask = uint32_t; virtual SmallBitVector getRegisterSet(RegSetMask Include, RegSetMask Exclude) const = 0; /// Get the set of physical registers available for the specified Variable's /// register class, applying register restrictions from the command line. virtual const SmallBitVector & getRegistersForVariable(const Variable *Var) const = 0; /// Get the set of *all* physical registers available for the specified /// Variable's register class, *not* applying register restrictions from the /// command line. virtual const SmallBitVector & getAllRegistersForVariable(const Variable *Var) const = 0; virtual const SmallBitVector &getAliasesForRegister(RegNumT) const = 0; void regAlloc(RegAllocKind Kind); void postRegallocSplitting(const SmallBitVector &RegMask); virtual void makeRandomRegisterPermutation(llvm::SmallVectorImpl<RegNumT> &Permutation, const SmallBitVector &ExcludeRegisters, uint64_t Salt) const = 0; /// Get the minimum number of clusters required for a jump table to be /// considered. virtual SizeT getMinJumpTableSize() const = 0; virtual void emitJumpTable(const Cfg *Func, const InstJumpTable *JumpTable) const = 0; virtual void emitVariable(const Variable *Var) const = 0; void emitWithoutPrefix(const ConstantRelocatable *CR, const char *Suffix = "") const; virtual void emit(const ConstantInteger32 *C) const = 0; virtual void emit(const ConstantInteger64 *C) const = 0; virtual void emit(const ConstantFloat *C) const = 0; virtual void emit(const ConstantDouble *C) const = 0; virtual void emit(const ConstantUndef *C) const = 0; virtual void emit(const ConstantRelocatable *CR) const = 0; /// Performs target-specific argument lowering. virtual void lowerArguments() = 0; virtual void initNodeForLowering(CfgNode *) {} virtual void addProlog(CfgNode *Node) = 0; virtual void addEpilog(CfgNode *Node) = 0; /// Create a properly-typed "mov" instruction. This is primarily for local /// variable splitting. virtual Inst *createLoweredMove(Variable *Dest, Variable *SrcVar) { // TODO(stichnot): make pure virtual by implementing for all targets (void)Dest; (void)SrcVar; llvm::report_fatal_error("createLoweredMove() unimplemented"); return nullptr; } virtual ~TargetLowering() = default; private: // This control variable is used by AutoBundle (RAII-style bundle // locking/unlocking) to prevent nested bundles. bool AutoBundling = false; /// This indicates whether we are in the genTargetHelperCalls phase, and /// therefore can do things like scalarization. bool GeneratingTargetHelpers = false; // _bundle_lock(), and _bundle_unlock(), were made private to force subtargets // to use the AutoBundle helper. void _bundle_lock(InstBundleLock::Option BundleOption = InstBundleLock::Opt_None) { Context.insert<InstBundleLock>(BundleOption); } void _bundle_unlock() { Context.insert<InstBundleUnlock>(); } protected: /// AutoBundle provides RIAA-style bundling. Sub-targets are expected to use /// it when emitting NaCl Bundles to ensure proper bundle_unlocking, and /// prevent nested bundles. /// /// AutoBundle objects will emit a _bundle_lock during construction (but only /// if sandboxed code generation was requested), and a bundle_unlock() during /// destruction. By carefully scoping objects of this type, Subtargets can /// ensure proper bundle emission. class AutoBundle { AutoBundle() = delete; AutoBundle(const AutoBundle &) = delete; AutoBundle &operator=(const AutoBundle &) = delete; public: explicit AutoBundle(TargetLowering *Target, InstBundleLock::Option Option = InstBundleLock::Opt_None); ~AutoBundle(); private: TargetLowering *const Target; const bool NeedSandboxing; }; explicit TargetLowering(Cfg *Func); // Applies command line filters to TypeToRegisterSet array. static void filterTypeToRegisterSet( GlobalContext *Ctx, int32_t NumRegs, SmallBitVector TypeToRegisterSet[], size_t TypeToRegisterSetSize, std::function<std::string(RegNumT)> getRegName, std::function<const char *(RegClass)> getRegClassName); virtual void lowerAlloca(const InstAlloca *Instr) = 0; virtual void lowerArithmetic(const InstArithmetic *Instr) = 0; virtual void lowerAssign(const InstAssign *Instr) = 0; virtual void lowerBr(const InstBr *Instr) = 0; virtual void lowerBreakpoint(const InstBreakpoint *Instr) = 0; virtual void lowerCall(const InstCall *Instr) = 0; virtual void lowerCast(const InstCast *Instr) = 0; virtual void lowerFcmp(const InstFcmp *Instr) = 0; virtual void lowerExtractElement(const InstExtractElement *Instr) = 0; virtual void lowerIcmp(const InstIcmp *Instr) = 0; virtual void lowerInsertElement(const InstInsertElement *Instr) = 0; virtual void lowerIntrinsicCall(const InstIntrinsicCall *Instr) = 0; virtual void lowerLoad(const InstLoad *Instr) = 0; virtual void lowerPhi(const InstPhi *Instr) = 0; virtual void lowerRet(const InstRet *Instr) = 0; virtual void lowerSelect(const InstSelect *Instr) = 0; virtual void lowerShuffleVector(const InstShuffleVector *Instr) = 0; virtual void lowerStore(const InstStore *Instr) = 0; virtual void lowerSwitch(const InstSwitch *Instr) = 0; virtual void lowerUnreachable(const InstUnreachable *Instr) = 0; virtual void lowerOther(const Inst *Instr); virtual void genTargetHelperCallFor(Inst *Instr) = 0; virtual uint32_t getCallStackArgumentsSizeBytes(const InstCall *Instr) = 0; /// Opportunity to modify other instructions to help Address Optimization virtual void doAddressOptOther() {} virtual void doAddressOptLoad() {} virtual void doAddressOptStore() {} virtual void doAddressOptLoadSubVector() {} virtual void doAddressOptStoreSubVector() {} virtual void doMockBoundsCheck(Operand *) {} virtual void randomlyInsertNop(float Probability, RandomNumberGenerator &RNG) = 0; /// This gives the target an opportunity to post-process the lowered expansion /// before returning. virtual void postLower() {} /// When the SkipUnimplemented flag is set, addFakeDefUses() gets invoked by /// the UnimplementedLoweringError macro to insert fake uses of all the /// instruction variables and a fake def of the instruction dest, in order to /// preserve integrity of liveness analysis. void addFakeDefUses(const Inst *Instr); /// Find (non-SSA) instructions where the Dest variable appears in some source /// operand, and set the IsDestRedefined flag. This keeps liveness analysis /// consistent. void markRedefinitions(); /// Make a pass over the Cfg to determine which variables need stack slots and /// place them in a sorted list (SortedSpilledVariables). Among those, vars, /// classify the spill variables as local to the basic block vs global /// (multi-block) in order to compute the parameters GlobalsSize and /// SpillAreaSizeBytes (represents locals or general vars if the coalescing of /// locals is disallowed) along with alignments required for variables in each /// area. We rely on accurate VMetadata in order to classify a variable as /// global vs local (otherwise the variable is conservatively global). The /// in-args should be initialized to 0. /// /// This is only a pre-pass and the actual stack slot assignment is handled /// separately. /// /// There may be target-specific Variable types, which will be handled by /// TargetVarHook. If the TargetVarHook returns true, then the variable is /// skipped and not considered with the rest of the spilled variables. void getVarStackSlotParams(VarList &SortedSpilledVariables, SmallBitVector &RegsUsed, size_t *GlobalsSize, size_t *SpillAreaSizeBytes, uint32_t *SpillAreaAlignmentBytes, uint32_t *LocalsSlotsAlignmentBytes, std::function<bool(Variable *)> TargetVarHook); /// Calculate the amount of padding needed to align the local and global areas /// to the required alignment. This assumes the globals/locals layout used by /// getVarStackSlotParams and assignVarStackSlots. void alignStackSpillAreas(uint32_t SpillAreaStartOffset, uint32_t SpillAreaAlignmentBytes, size_t GlobalsSize, uint32_t LocalsSlotsAlignmentBytes, uint32_t *SpillAreaPaddingBytes, uint32_t *LocalsSlotsPaddingBytes); /// Make a pass through the SortedSpilledVariables and actually assign stack /// slots. SpillAreaPaddingBytes takes into account stack alignment padding. /// The SpillArea starts after that amount of padding. This matches the scheme /// in getVarStackSlotParams, where there may be a separate multi-block global /// var spill area and a local var spill area. void assignVarStackSlots(VarList &SortedSpilledVariables, size_t SpillAreaPaddingBytes, size_t SpillAreaSizeBytes, size_t GlobalsAndSubsequentPaddingSize, bool UsesFramePointer); /// Sort the variables in Source based on required alignment. The variables /// with the largest alignment need are placed in the front of the Dest list. void sortVarsByAlignment(VarList &Dest, const VarList &Source) const; InstCall *makeHelperCall(RuntimeHelper FuncID, Variable *Dest, SizeT MaxSrcs); void _set_dest_redefined() { Context.getLastInserted()->setDestRedefined(); } bool shouldOptimizeMemIntrins(); void scalarizeArithmetic(InstArithmetic::OpKind K, Variable *Dest, Operand *Src0, Operand *Src1); /// Generalizes scalarizeArithmetic to support other instruction types. /// /// insertScalarInstruction is a function-like object with signature /// (Variable *Dest, Variable *Src0, Variable *Src1) -> Instr *. template <typename... Operands, typename F = std::function<Inst *(Variable *, Operands *...)>> void scalarizeInstruction(Variable *Dest, F insertScalarInstruction, Operands *... Srcs) { assert(GeneratingTargetHelpers && "scalarizeInstruction called during incorrect phase"); const Type DestTy = Dest->getType(); assert(isVectorType(DestTy)); const Type DestElementTy = typeElementType(DestTy); const SizeT NumElements = typeNumElements(DestTy); Variable *T = Func->makeVariable(DestTy); if (auto *VarVecOn32 = llvm::dyn_cast<VariableVecOn32>(T)) { VarVecOn32->initVecElement(Func); auto *Undef = ConstantUndef::create(Ctx, DestTy); Context.insert<InstAssign>(T, Undef); } else { Context.insert<InstFakeDef>(T); } for (SizeT I = 0; I < NumElements; ++I) { auto *Index = Ctx->getConstantInt32(I); auto makeExtractThunk = [this, Index, NumElements](Operand *Src) { return [this, Index, NumElements, Src]() { (void)NumElements; assert(typeNumElements(Src->getType()) == NumElements); const auto ElementTy = typeElementType(Src->getType()); auto *Op = Func->makeVariable(ElementTy); Context.insert<InstExtractElement>(Op, Src, Index); return Op; }; }; // Perform the operation as a scalar operation. auto *Res = Func->makeVariable(DestElementTy); auto *Arith = applyToThunkedArgs(insertScalarInstruction, Res, makeExtractThunk(Srcs)...); genTargetHelperCallFor(Arith); Variable *DestT = Func->makeVariable(DestTy); Context.insert<InstInsertElement>(DestT, T, Res, Index); T = DestT; } Context.insert<InstAssign>(Dest, T); } // applyToThunkedArgs is used by scalarizeInstruction. Ideally, we would just // call insertScalarInstruction(Res, Srcs...), but C++ does not specify // evaluation order which means this leads to an unpredictable final // output. Instead, we wrap each of the Srcs in a thunk and these // applyToThunkedArgs functions apply the thunks in a well defined order so we // still get well-defined output. Inst *applyToThunkedArgs( std::function<Inst *(Variable *, Variable *)> insertScalarInstruction, Variable *Res, std::function<Variable *()> thunk0) { auto *Src0 = thunk0(); return insertScalarInstruction(Res, Src0); } Inst * applyToThunkedArgs(std::function<Inst *(Variable *, Variable *, Variable *)> insertScalarInstruction, Variable *Res, std::function<Variable *()> thunk0, std::function<Variable *()> thunk1) { auto *Src0 = thunk0(); auto *Src1 = thunk1(); return insertScalarInstruction(Res, Src0, Src1); } Inst *applyToThunkedArgs( std::function<Inst *(Variable *, Variable *, Variable *, Variable *)> insertScalarInstruction, Variable *Res, std::function<Variable *()> thunk0, std::function<Variable *()> thunk1, std::function<Variable *()> thunk2) { auto *Src0 = thunk0(); auto *Src1 = thunk1(); auto *Src2 = thunk2(); return insertScalarInstruction(Res, Src0, Src1, Src2); } /// SandboxType enumerates all possible sandboxing strategies that enum SandboxType { ST_None, ST_NaCl, ST_Nonsfi, }; static SandboxType determineSandboxTypeFromFlags(const ClFlags &Flags); Cfg *Func; GlobalContext *Ctx; bool HasComputedFrame = false; bool CallsReturnsTwice = false; SizeT NextLabelNumber = 0; SizeT NextJumpTableNumber = 0; LoweringContext Context; const SandboxType SandboxingType = ST_None; const static constexpr char *H_getIP_prefix = "__Sz_getIP_"; }; /// TargetDataLowering is used for "lowering" data including initializers for /// global variables, and the internal constant pools. It is separated out from /// TargetLowering because it does not require a Cfg. class TargetDataLowering { TargetDataLowering() = delete; TargetDataLowering(const TargetDataLowering &) = delete; TargetDataLowering &operator=(const TargetDataLowering &) = delete; public: static std::unique_ptr<TargetDataLowering> createLowering(GlobalContext *Ctx); virtual ~TargetDataLowering(); virtual void lowerGlobals(const VariableDeclarationList &Vars, const std::string &SectionSuffix) = 0; virtual void lowerConstants() = 0; virtual void lowerJumpTables() = 0; virtual void emitTargetRODataSections() {} protected: void emitGlobal(const VariableDeclaration &Var, const std::string &SectionSuffix); /// For now, we assume .long is the right directive for emitting 4 byte emit /// global relocations. However, LLVM MIPS usually uses .4byte instead. /// Perhaps there is some difference when the location is unaligned. static const char *getEmit32Directive() { return ".long"; } explicit TargetDataLowering(GlobalContext *Ctx) : Ctx(Ctx) {} GlobalContext *Ctx; }; /// TargetHeaderLowering is used to "lower" the header of an output file. It /// writes out the target-specific header attributes. E.g., for ARM this writes /// out the build attributes (float ABI, etc.). class TargetHeaderLowering { TargetHeaderLowering() = delete; TargetHeaderLowering(const TargetHeaderLowering &) = delete; TargetHeaderLowering &operator=(const TargetHeaderLowering &) = delete; public: static std::unique_ptr<TargetHeaderLowering> createLowering(GlobalContext *Ctx); virtual ~TargetHeaderLowering(); virtual void lower() {} protected: explicit TargetHeaderLowering(GlobalContext *Ctx) : Ctx(Ctx) {} GlobalContext *Ctx; }; } // end of namespace Ice #endif // SUBZERO_SRC_ICETARGETLOWERING_H