mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 02:33:06 +01:00
[InstructionCost] Add saturation support.
This patch makes the operations on InstructionCost saturate, so that when costs are accumulated they saturate to <max value>. One of the compelling reasons for wanting to have saturation support is because in various places, arbitrary values are used to represent a 'high' cost, but when accumulating the cost of some set of operations or a loop, overflow is not taken into account, which may lead to unexpected results. By defining the operations to saturate, we can express the cost of something 'very expensive' as InstructionCost::getMax(). Reviewed By: kparzysz, dmgreen Differential Revision: https://reviews.llvm.org/D105108
This commit is contained in:
parent
5a37bc3d1b
commit
5d8e9991c3
@ -9,8 +9,9 @@
|
|||||||
/// This file defines an InstructionCost class that is used when calculating
|
/// 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
|
/// 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
|
/// 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
|
/// can be used to encode particular properties, such as a cost being invalid.
|
||||||
/// unknown.
|
/// Operations on InstructionCost implement saturation arithmetic, so that
|
||||||
|
/// accumulating costs on large cost-values don't overflow.
|
||||||
///
|
///
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
|
||||||
@ -18,6 +19,8 @@
|
|||||||
#define LLVM_SUPPORT_INSTRUCTIONCOST_H
|
#define LLVM_SUPPORT_INSTRUCTIONCOST_H
|
||||||
|
|
||||||
#include "llvm/ADT/Optional.h"
|
#include "llvm/ADT/Optional.h"
|
||||||
|
#include "llvm/Support/MathExtras.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
namespace llvm {
|
namespace llvm {
|
||||||
|
|
||||||
@ -27,13 +30,24 @@ class InstructionCost {
|
|||||||
public:
|
public:
|
||||||
using CostType = int;
|
using CostType = int;
|
||||||
|
|
||||||
/// These states can currently be used to indicate whether a cost is valid or
|
/// CostState describes the state of a cost.
|
||||||
/// invalid. Examples of an invalid cost might be where the cost is
|
enum CostState {
|
||||||
/// prohibitively expensive and the user wants to prevent certain
|
Valid, /// < The cost value represents a valid cost, even when the
|
||||||
/// optimizations being performed. Or perhaps the cost is simply unknown
|
/// cost-value is large.
|
||||||
/// because the operation makes no sense in certain circumstances. These
|
Invalid /// < Invalid indicates there is no way to represent the cost as a
|
||||||
/// states can be expanded in future to support other cases if necessary.
|
/// numeric value. This state exists to represent a possible issue,
|
||||||
enum CostState { Valid, Invalid };
|
/// e.g. if the cost-model knows the operation cannot be expanded
|
||||||
|
/// into a valid code-sequence by the code-generator. While some
|
||||||
|
/// passes may assert that the calculated cost must be valid, it is
|
||||||
|
/// up to individual passes how to interpret an Invalid cost. For
|
||||||
|
/// example, a transformation pass could choose not to perform a
|
||||||
|
/// transformation if the resulting cost would end up Invalid.
|
||||||
|
/// Because some passes may assert a cost is Valid, it is not
|
||||||
|
/// recommended to use Invalid costs to model 'Unknown'.
|
||||||
|
/// Note that Invalid is semantically different from a (very) high,
|
||||||
|
/// but valid cost, which intentionally indicates no issue, but
|
||||||
|
/// rather a strong preference not to select a certain operation.
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
CostType Value = 0;
|
CostType Value = 0;
|
||||||
@ -44,6 +58,9 @@ private:
|
|||||||
State = Invalid;
|
State = Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static CostType getMaxValue() { return std::numeric_limits<CostType>::max(); }
|
||||||
|
static CostType getMinValue() { return std::numeric_limits<CostType>::min(); }
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// A default constructed InstructionCost is a valid zero cost
|
// A default constructed InstructionCost is a valid zero cost
|
||||||
InstructionCost() = default;
|
InstructionCost() = default;
|
||||||
@ -51,6 +68,8 @@ public:
|
|||||||
InstructionCost(CostState) = delete;
|
InstructionCost(CostState) = delete;
|
||||||
InstructionCost(CostType Val) : Value(Val), State(Valid) {}
|
InstructionCost(CostType Val) : Value(Val), State(Valid) {}
|
||||||
|
|
||||||
|
static InstructionCost getMax() { return getMaxValue(); }
|
||||||
|
static InstructionCost getMin() { return getMinValue(); }
|
||||||
static InstructionCost getInvalid(CostType Val = 0) {
|
static InstructionCost getInvalid(CostType Val = 0) {
|
||||||
InstructionCost Tmp(Val);
|
InstructionCost Tmp(Val);
|
||||||
Tmp.setInvalid();
|
Tmp.setInvalid();
|
||||||
@ -73,13 +92,19 @@ public:
|
|||||||
|
|
||||||
/// For all of the arithmetic operators provided here any invalid state is
|
/// For all of the arithmetic operators provided here any invalid state is
|
||||||
/// perpetuated and cannot be removed. Once a cost becomes invalid it stays
|
/// perpetuated and cannot be removed. Once a cost becomes invalid it stays
|
||||||
/// invalid, and it also inherits any invalid state from the RHS. Regardless
|
/// invalid, and it also inherits any invalid state from the RHS.
|
||||||
/// of the state, arithmetic work on the actual values in the same way as they
|
/// Arithmetic work on the actual values is implemented with saturation,
|
||||||
/// would on a basic type, such as integer.
|
/// to avoid overflow when using more extreme cost values.
|
||||||
|
|
||||||
InstructionCost &operator+=(const InstructionCost &RHS) {
|
InstructionCost &operator+=(const InstructionCost &RHS) {
|
||||||
propagateState(RHS);
|
propagateState(RHS);
|
||||||
Value += RHS.Value;
|
|
||||||
|
// Saturating addition.
|
||||||
|
InstructionCost::CostType Result;
|
||||||
|
if (AddOverflow(Value, RHS.Value, Result))
|
||||||
|
Result = RHS.Value > 0 ? getMaxValue() : getMinValue();
|
||||||
|
|
||||||
|
Value = Result;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +116,12 @@ public:
|
|||||||
|
|
||||||
InstructionCost &operator-=(const InstructionCost &RHS) {
|
InstructionCost &operator-=(const InstructionCost &RHS) {
|
||||||
propagateState(RHS);
|
propagateState(RHS);
|
||||||
Value -= RHS.Value;
|
|
||||||
|
// Saturating subtract.
|
||||||
|
InstructionCost::CostType Result;
|
||||||
|
if (SubOverflow(Value, RHS.Value, Result))
|
||||||
|
Result = RHS.Value > 0 ? getMinValue() : getMaxValue();
|
||||||
|
Value = Result;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +133,17 @@ public:
|
|||||||
|
|
||||||
InstructionCost &operator*=(const InstructionCost &RHS) {
|
InstructionCost &operator*=(const InstructionCost &RHS) {
|
||||||
propagateState(RHS);
|
propagateState(RHS);
|
||||||
Value *= RHS.Value;
|
|
||||||
|
// Saturating multiply.
|
||||||
|
InstructionCost::CostType Result;
|
||||||
|
if (MulOverflow(Value, RHS.Value, Result)) {
|
||||||
|
if ((Value > 0 && RHS.Value > 0) || (Value < 0 && RHS.Value < 0))
|
||||||
|
Result = getMaxValue();
|
||||||
|
else
|
||||||
|
Result = getMinValue();
|
||||||
|
}
|
||||||
|
|
||||||
|
Value = Result;
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@
|
|||||||
|
|
||||||
#include "llvm/Support/InstructionCost.h"
|
#include "llvm/Support/InstructionCost.h"
|
||||||
#include "gtest/gtest.h"
|
#include "gtest/gtest.h"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace llvm;
|
using namespace llvm;
|
||||||
|
|
||||||
@ -75,4 +76,20 @@ TEST_F(CostTest, Operators) {
|
|||||||
|
|
||||||
EXPECT_EQ(std::min(VThree, VNegTwo), -2);
|
EXPECT_EQ(std::min(VThree, VNegTwo), -2);
|
||||||
EXPECT_EQ(std::max(VThree, VSix), 6);
|
EXPECT_EQ(std::max(VThree, VSix), 6);
|
||||||
|
|
||||||
|
// Test saturation
|
||||||
|
auto Max = InstructionCost::getMax();
|
||||||
|
auto Min = InstructionCost::getMin();
|
||||||
|
auto MinusOne = InstructionCost(-1);
|
||||||
|
auto MinusTwo = InstructionCost(-2);
|
||||||
|
auto One = InstructionCost(1);
|
||||||
|
auto Two = InstructionCost(2);
|
||||||
|
EXPECT_EQ(Max + One, Max);
|
||||||
|
EXPECT_EQ(Min + MinusOne, Min);
|
||||||
|
EXPECT_EQ(Min - One, Min);
|
||||||
|
EXPECT_EQ(Max - MinusOne, Max);
|
||||||
|
EXPECT_EQ(Max * Two, Max);
|
||||||
|
EXPECT_EQ(Min * Two, Min);
|
||||||
|
EXPECT_EQ(Max * MinusTwo, Min);
|
||||||
|
EXPECT_EQ(Min * MinusTwo, Max);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user