Index: llvm/include/llvm/Analysis/InstructionCost.h =================================================================== --- /dev/null +++ llvm/include/llvm/Analysis/InstructionCost.h @@ -0,0 +1,319 @@ +//===- InstructionCost.h ----------------------------------------*- C++ -*-===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +/// \file +/// This file defines an InstructionCost class that is used when calculating +/// the cost of an instruction, or a group of instructions. In addition to a +/// numeric value representing the cost the class also contains a state that +/// can be used to encode particular properties, i.e. a cost being invalid or +/// unknown. +/// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_ANALYSIS_INSTRUCTIONCOST_H +#define LLVM_ANALYSIS_INSTRUCTIONCOST_H + +#include "llvm/Support/raw_ostream.h" + +namespace llvm { + +template +class GenericCost { +private: + + // These states can currently be used to indicate whether a cost is valid or + // invalid. Examples of an invalid cost might be where the cost is + // prohibitively expensive and the user wants to prevent certain + // optimizations being performed. Or perhaps the cost is simply unknown + // because the operation makes no sense in certain circumstances. These states + // can be expanded in future to support other cases if necessary. + typedef enum { + Normal = 0, + Invalid + } CostState; + +private: + + T Value; + CostState State; + +public: + + GenericCost() = default; + + GenericCost(T Val) : + Value(Val), State(Normal) {} + + static GenericCost getInvalid(T Val = 0) { + GenericCost Tmp(Val); + Tmp.setInvalid(); + return Tmp; + } + + bool isInvalid() const { return State == Invalid; } + + void setInvalid() { State = Invalid; } + + // This function is intended to be used as sparingly as possible, since the + // class provides the full range of operator support required for arithmetic + // and comparisons. + T getValue() const { + return Value; + } + + // There are cases where it is necessary to convert an integer cost to a + // another cost type, for example the vectorizer does cost analysis using + // floating point values. + template + void convertTo(GenericCost &Other) const { + Other.Value = T2(Value); + Other.State = State; + } + + // For all of the arithmetic operators provided here any non-normal state is + // perpetuated and cannot be removed. Once a cost becomes invalid it stays + // invalid, and it also inherits any invalid state from the RHS. Regardless + // of the state, arithmetic and comparisons work on the actual values in the + // same way as they would on a basic type, such as integer. + + GenericCost &operator+=(const GenericCost &RHS) { + if (RHS.isInvalid()) + setInvalid(); + Value += RHS.Value; + return *this; + } + + template + GenericCost &operator+=(const RT RHS) { + GenericCost RHS2(RHS); + *this += RHS2; + return *this; + } + + GenericCost &operator-=(const GenericCost &RHS) { + if (RHS.isInvalid()) + setInvalid(); + Value -= RHS.Value; + return *this; + } + + template + GenericCost &operator-=(const RT RHS) { + GenericCost RHS2(RHS); + *this -= RHS2; + return *this; + } + + GenericCost &operator*=(const GenericCost &RHS) { + if (RHS.isInvalid()) + setInvalid(); + Value *= RHS.Value; + return *this; + } + + GenericCost &operator*=(const T RHS) { + GenericCost RHS2(RHS); + *this *= RHS2; + return *this; + } + + GenericCost &operator/=(const GenericCost &RHS) { + if (RHS.isInvalid()) + setInvalid(); + Value /= RHS.Value; + return *this; + } + + GenericCost &operator/=(const T RHS) { + GenericCost RHS2(RHS); + *this /= RHS2; + return *this; + } + + GenericCost &operator++() { + *this += 1; + return *this; + } + + GenericCost operator++(int) { + GenericCost Copy = *this; + ++*this; + return Copy; + } + + GenericCost &operator--() { + *this -= 1; + return *this; + } + + GenericCost operator--(int) { + GenericCost Copy = *this; + --*this; + return Copy; + } + + bool operator==(const GenericCost &RHS) const { + return Value == RHS.Value; + } + + bool operator==(const T RHS) const { + return Value == RHS; + } + + bool operator!=(const GenericCost &RHS) const { + return !(*this == RHS); + } + + bool operator!=(const T RHS) const { + return !(*this == RHS); + } + + bool operator<(const GenericCost &RHS) const { + return Value < RHS.Value; + } + + bool operator>(const GenericCost &RHS) const { + return RHS < *this; + } + + bool operator<=(const GenericCost &RHS) const { + return !(RHS < *this); + } + + bool operator>=(const GenericCost &RHS) const { + return !(*this < RHS); + } + + bool operator<(const T RHS) const { + return Value < RHS; + } + + bool operator<=(const T RHS) const { + return Value <= RHS; + } + + bool operator>(const T RHS) const { + return Value > RHS; + } + + bool operator>=(const T RHS) const { + return Value >= RHS; + } + + static GenericCost min(GenericCost LHS, GenericCost RHS) { + return LHS.Value < RHS.Value ? LHS : RHS; + } + + static GenericCost max(GenericCost LHS, GenericCost RHS) { + return LHS.Value > RHS.Value ? LHS : RHS; + } + + void print(raw_ostream &OS) const { + if (isInvalid()) + OS << "Invalid"; + else + OS << Value; + } +}; + +template +inline bool operator==(const LT LHS, const GenericCost &RHS) { + return RHS == LHS; +} + +template +inline bool operator!=(const LT LHS, const GenericCost &RHS) { + return RHS != LHS; +} + +template +inline GenericCost operator+(const GenericCost &LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + LHS2 += RHS; + return LHS2; +} + +template +inline GenericCost operator-(const GenericCost &LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + LHS2 -= RHS; + return LHS2; +} + +template +inline GenericCost operator*(const GenericCost &LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + LHS2 *= RHS; + return LHS2; +} + +template +inline GenericCost operator/(const GenericCost &LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + LHS2 /= RHS; + return LHS2; +} + +template +inline GenericCost operator+(const GenericCost &LHS, const RT RHS) { + GenericCost RHS2(RHS); + return LHS + RHS2; +} + +template +inline GenericCost operator-(const GenericCost &LHS, const RT RHS) { + GenericCost RHS2(RHS); + return LHS - RHS2; +} + +template +inline GenericCost operator*(const GenericCost &LHS, const RT RHS) { + GenericCost RHS2(RHS); + return LHS * RHS2; +} + +template +inline GenericCost operator/(const GenericCost &LHS, const RT RHS) { + GenericCost RHS2(RHS); + return LHS / RHS2; +} + +template +inline GenericCost operator+(const LT LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + return LHS2 + RHS; +} + +template +inline GenericCost operator-(const LT LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + return LHS2 - RHS; +} + +template +inline GenericCost operator*(const LT LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + return LHS2 * RHS; +} + +template +inline GenericCost operator/(const LT LHS, const GenericCost &RHS) { + GenericCost LHS2(LHS); + return LHS2 / RHS; +} + +template +inline raw_ostream &operator<<(raw_ostream &OS, const GenericCost &V) { + V.print(OS); + return OS; +} + +using InstructionCost = GenericCost; + +} // namespace llvm + +#endif Index: llvm/include/llvm/Analysis/TargetTransformInfo.h =================================================================== --- llvm/include/llvm/Analysis/TargetTransformInfo.h +++ llvm/include/llvm/Analysis/TargetTransformInfo.h @@ -21,6 +21,7 @@ #ifndef LLVM_ANALYSIS_TARGETTRANSFORMINFO_H #define LLVM_ANALYSIS_TARGETTRANSFORMINFO_H +#include "llvm/Analysis/InstructionCost.h" #include "llvm/IR/InstrTypes.h" #include "llvm/IR/Operator.h" #include "llvm/IR/PassManager.h" @@ -236,19 +237,26 @@ /// /// Note, this method does not cache the cost calculation and it /// can be expensive in some cases. - int getInstructionCost(const Instruction *I, enum TargetCostKind kind) const { + InstructionCost getInstructionCost(const Instruction *I, + enum TargetCostKind kind) const { + InstructionCost Cost; switch (kind) { case TCK_RecipThroughput: - return getInstructionThroughput(I); - + Cost = getInstructionThroughput(I); + break; case TCK_Latency: - return getInstructionLatency(I); - + Cost = getInstructionLatency(I); + break; case TCK_CodeSize: case TCK_SizeAndLatency: - return getUserCost(I, kind); + Cost = getUserCost(I, kind); + break; + default: + llvm_unreachable("Unknown instruction cost kind"); } - llvm_unreachable("Unknown instruction cost kind"); + if (Cost == -1) + Cost.setInvalid(); + return Cost; } /// Underlying constants for 'cost' values in this interface. Index: llvm/include/llvm/IR/DiagnosticInfo.h =================================================================== --- llvm/include/llvm/IR/DiagnosticInfo.h +++ llvm/include/llvm/IR/DiagnosticInfo.h @@ -38,6 +38,8 @@ class LLVMContext; class Module; class SMDiagnostic; +template class GenericCost; +using InstructionCost = GenericCost; /// Defines the different supported severity of a diagnostic. enum DiagnosticSeverity : char { @@ -438,6 +440,7 @@ Argument(StringRef Key, ElementCount EC); Argument(StringRef Key, bool B) : Key(Key), Val(B ? "true" : "false") {} Argument(StringRef Key, DebugLoc dl); + Argument(StringRef Key, InstructionCost C); }; /// \p PassName is the name of the pass emitting this diagnostic. \p Index: llvm/lib/Analysis/CostModel.cpp =================================================================== --- llvm/lib/Analysis/CostModel.cpp +++ llvm/lib/Analysis/CostModel.cpp @@ -57,7 +57,7 @@ /// Returns -1 if the cost is unknown. /// Note, this method does not cache the cost calculation and it /// can be expensive in some cases. - unsigned getInstructionCost(const Instruction *I) const { + InstructionCost getInstructionCost(const Instruction *I) const { return TTI->getInstructionCost(I, TargetTransformInfo::TCK_RecipThroughput); } @@ -103,8 +103,8 @@ for (BasicBlock &B : *F) { for (Instruction &Inst : B) { - unsigned Cost = TTI->getInstructionCost(&Inst, CostKind); - if (Cost != (unsigned)-1) + InstructionCost Cost = TTI->getInstructionCost(&Inst, CostKind); + if (!Cost.isInvalid()) OS << "Cost Model: Found an estimated cost of " << Cost; else OS << "Cost Model: Unknown cost"; Index: llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp =================================================================== --- llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp +++ llvm/lib/CodeGen/InterleavedLoadCombinePass.cpp @@ -1130,8 +1130,8 @@ std::set Is; std::set SVIs; - unsigned InterleavedCost; - unsigned InstructionCost = 0; + InstructionCost InterleavedCost; + InstructionCost InstructionCost = 0; // Get the interleave factor unsigned Factor = InterleavedLoad.size(); @@ -1174,6 +1174,10 @@ } } + // We need to have a valid cost in order to proceed. + if (InstructionCost.isInvalid()) + return false; + // We know that all LoadInst are within the same BB. This guarantees that // either everything or nothing is loaded. LoadInst *First = findFirstLoad(LIs); Index: llvm/lib/IR/DiagnosticInfo.cpp =================================================================== --- llvm/lib/IR/DiagnosticInfo.cpp +++ llvm/lib/IR/DiagnosticInfo.cpp @@ -16,6 +16,7 @@ #include "llvm/ADT/StringExtras.h" #include "llvm/ADT/Twine.h" #include "llvm/ADT/iterator_range.h" +#include "llvm/Analysis/InstructionCost.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constants.h" #include "llvm/IR/DebugInfoMetadata.h" @@ -220,6 +221,13 @@ EC.print(OS); } +DiagnosticInfoOptimizationBase::Argument::Argument(StringRef Key, + InstructionCost C) + : Key(std::string(Key)) { + raw_string_ostream OS(Val); + C.print(OS); +} + DiagnosticInfoOptimizationBase::Argument::Argument(StringRef Key, DebugLoc Loc) : Key(std::string(Key)), Loc(Loc) { if (Loc) { Index: llvm/lib/Transforms/IPO/HotColdSplitting.cpp =================================================================== --- llvm/lib/Transforms/IPO/HotColdSplitting.cpp +++ llvm/lib/Transforms/IPO/HotColdSplitting.cpp @@ -233,11 +233,11 @@ } /// Get the benefit score of outlining \p Region. -static int getOutliningBenefit(ArrayRef Region, - TargetTransformInfo &TTI) { +static InstructionCost getOutliningBenefit(ArrayRef Region, + TargetTransformInfo &TTI) { // Sum up the code size costs of non-terminator instructions. Tight coupling // with \ref getOutliningPenalty is needed to model the costs of terminators. - int Benefit = 0; + InstructionCost Benefit = 0; for (BasicBlock *BB : Region) for (Instruction &I : BB->instructionsWithoutDebug()) if (&I != BB->getTerminator()) @@ -324,12 +324,12 @@ // splitting. SetVector Inputs, Outputs, Sinks; CE.findInputsOutputs(Inputs, Outputs, Sinks); - int OutliningBenefit = getOutliningBenefit(Region, TTI); + InstructionCost OutliningBenefit = getOutliningBenefit(Region, TTI); int OutliningPenalty = getOutliningPenalty(Region, Inputs.size(), Outputs.size()); LLVM_DEBUG(dbgs() << "Split profitability: benefit = " << OutliningBenefit << ", penalty = " << OutliningPenalty << "\n"); - if (OutliningBenefit <= OutliningPenalty) + if (OutliningBenefit.isInvalid() || OutliningBenefit <= OutliningPenalty) return nullptr; Function *OrigF = Region[0]->getParent(); Index: llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp =================================================================== --- llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp +++ llvm/lib/Transforms/Scalar/CallSiteSplitting.cpp @@ -208,12 +208,12 @@ // instructions before the call is less then DuplicationThreshold. The // instructions before the call will be duplicated in the split blocks and // corresponding uses will be updated. - unsigned Cost = 0; + InstructionCost Cost = 0; for (auto &InstBeforeCall : llvm::make_range(CallSiteBB->begin(), CB.getIterator())) { Cost += TTI.getInstructionCost(&InstBeforeCall, TargetTransformInfo::TCK_CodeSize); - if (Cost >= DuplicationThreshold) + if (Cost.isInvalid() || Cost >= DuplicationThreshold) return false; } Index: llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp =================================================================== --- llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp +++ llvm/lib/Transforms/Vectorize/SLPVectorizer.cpp @@ -3745,17 +3745,17 @@ if (NeedToShuffleReuses) { for (unsigned Idx : E->ReuseShuffleIndices) { Instruction *I = cast(VL[Idx]); - ReuseShuffleCost -= TTI->getInstructionCost(I, CostKind); + ReuseShuffleCost -= TTI->getInstructionCost(I, CostKind).getValue(); } for (Value *V : VL) { Instruction *I = cast(V); - ReuseShuffleCost += TTI->getInstructionCost(I, CostKind); + ReuseShuffleCost += TTI->getInstructionCost(I, CostKind).getValue(); } } for (Value *V : VL) { Instruction *I = cast(V); assert(E->isOpcodeOrAlt(I) && "Unexpected main/alternate opcode"); - ScalarCost += TTI->getInstructionCost(I, CostKind); + ScalarCost += TTI->getInstructionCost(I, CostKind).getValue(); } // VecCost is equal to sum of the cost of creating 2 vectors // and the cost of creating shuffle. Index: llvm/unittests/Analysis/CMakeLists.txt =================================================================== --- llvm/unittests/Analysis/CMakeLists.txt +++ llvm/unittests/Analysis/CMakeLists.txt @@ -29,6 +29,7 @@ DomTreeUpdaterTest.cpp GlobalsModRefTest.cpp FunctionPropertiesAnalysisTest.cpp + InstructionCostTest.cpp IRSimilarityIdentifierTest.cpp IVDescriptorsTest.cpp LazyCallGraphTest.cpp Index: llvm/unittests/Analysis/InstructionCostTest.cpp =================================================================== --- /dev/null +++ llvm/unittests/Analysis/InstructionCostTest.cpp @@ -0,0 +1,47 @@ +//===- InstructionCostTest.cpp - InstructionCost tests --------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "llvm/Analysis/InstructionCost.h" +#include "gtest/gtest.h" + +using namespace llvm; + +namespace { + +struct CostTest : public testing::Test { + CostTest() {} +}; + + +} // namespace + +TEST_F(CostTest, Operators) { + InstructionCost Cost1 = 3; + InstructionCost Cost2 = -2; + InstructionCost Cost3 = 6; + InstructionCost Cost4 = InstructionCost::getInvalid(3); + InstructionCost TmpCost; + + EXPECT_NE(Cost1, Cost2); + EXPECT_GT(Cost1, Cost2); + EXPECT_EQ(Cost1, Cost4); + EXPECT_EQ(Cost1 - Cost2, 5); + EXPECT_EQ(Cost1 * Cost2, -6); + EXPECT_EQ(Cost3 / Cost1, 2); + + EXPECT_TRUE(Cost4.isInvalid()); + + TmpCost = Cost1 + Cost4; + EXPECT_TRUE(TmpCost.isInvalid()); + + TmpCost = Cost1 * Cost4; + EXPECT_TRUE(TmpCost.isInvalid()); + + EXPECT_EQ(InstructionCost::min(Cost1, Cost2), -2); + EXPECT_EQ(InstructionCost::max(Cost1, Cost3), 6); +}