OR-Tools  9.6
saturated_arithmetic.h
Go to the documentation of this file.
1 // Copyright 2010-2022 Google LLC
2 // Licensed under the Apache License, Version 2.0 (the "License");
3 // you may not use this file except in compliance with the License.
4 // You may obtain a copy of the License at
5 //
6 // http://www.apache.org/licenses/LICENSE-2.0
7 //
8 // Unless required by applicable law or agreed to in writing, software
9 // distributed under the License is distributed on an "AS IS" BASIS,
10 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11 // See the License for the specific language governing permissions and
12 // limitations under the License.
13 
14 #ifndef OR_TOOLS_UTIL_SATURATED_ARITHMETIC_H_
15 #define OR_TOOLS_UTIL_SATURATED_ARITHMETIC_H_
16 
17 #include <cstdint>
18 #include <limits>
19 
20 #include "absl/base/casts.h"
22 #include "ortools/util/bitset.h"
23 
24 // This file contains implementations for saturated addition, subtraction and
25 // multiplication.
26 // Currently, there are three versions of the code.
27 // The code using the built-ins provided since GCC 5.0 now compiles really well
28 // with clang/LLVM on both x86_64 and ARM. It should therefore be the standard,
29 // but not for multiplication, see below.
30 //
31 // For example, on ARM, only 4 instructions are needed, two of which are
32 // additions that can be executed in parallel.
33 //
34 // On x86_64, we're keeping the code with inline assembly for GCC as GCC does
35 // manage to compile the code with built-ins properly.
36 // On x86_64, the product of two 64-bit registers is a 128-bit integer
37 // stored in two 64-bit registers. It's the carry flag that is set when the
38 // result exceeds 64 bits, not the overflow flag. Since the built-in uses the
39 // overflow flag, we have to resort on the assembly-based version of the code.
40 //
41 // Sadly, MSVC does not support the built-ins nor does it support inline
42 // assembly. We have to rely on the generic, C++ only version of the code which
43 // is much slower.
44 //
45 // TODO(user): make this implementation the default everywhere.
46 // TODO(user): investigate the code generated by MSVC.
47 
48 namespace operations_research {
49 
50 // Checks if x is equal to the min or the max value of an int64_t.
51 inline bool AtMinOrMaxInt64(int64_t x) {
52  return x == std::numeric_limits<int64_t>::min() ||
54 }
55 
56 // Note(user): -kint64min != kint64max, but kint64max == ~kint64min.
57 inline int64_t CapOpp(int64_t v) { return v == kint64min ? ~v : -v; }
58 
59 inline int64_t CapAbs(int64_t v) {
61  : (v < 0 ? -v : v);
62 }
63 
64 // ---------- Overflow utility functions ----------
65 
66 // Implement two's complement addition and subtraction on int64s.
67 //
68 // The C and C++ standards specify that the overflow of signed integers is
69 // undefined. This is because of the different possible representations that may
70 // be used for signed integers (one's complement, two's complement, sign and
71 // magnitude). Such overflows are detected by Address Sanitizer with
72 // -fsanitize=signed-integer-overflow.
73 //
74 // Simple, portable overflow detection on current machines relies on
75 // these two functions. For example, if the sign of the sum of two positive
76 // integers is negative, there has been an overflow.
77 //
78 // Note that the static assert will break if the code is compiled on machines
79 // which do not use two's complement.
80 inline int64_t TwosComplementAddition(int64_t x, int64_t y) {
81  static_assert(static_cast<uint64_t>(-1LL) == ~0ULL,
82  "The target architecture does not use two's complement.");
83  return absl::bit_cast<int64_t>(static_cast<uint64_t>(x) +
84  static_cast<uint64_t>(y));
85 }
86 
87 inline int64_t TwosComplementSubtraction(int64_t x, int64_t y) {
88  static_assert(static_cast<uint64_t>(-1LL) == ~0ULL,
89  "The target architecture does not use two's complement.");
90  return absl::bit_cast<int64_t>(static_cast<uint64_t>(x) -
91  static_cast<uint64_t>(y));
92 }
93 
94 // Helper function that returns true if an overflow has occurred in computing
95 // sum = x + y. sum is expected to be computed elsewhere.
96 inline bool AddHadOverflow(int64_t x, int64_t y, int64_t sum) {
97  // Overflow cannot occur if operands have different signs.
98  // It can only occur if sign(x) == sign(y) and sign(sum) != sign(x),
99  // which is equivalent to: sign(x) != sign(sum) && sign(y) != sign(sum).
100  // This is captured when the expression below is negative.
101  DCHECK_EQ(sum, TwosComplementAddition(x, y));
102  return ((x ^ sum) & (y ^ sum)) < 0;
103 }
104 
105 inline bool SubHadOverflow(int64_t x, int64_t y, int64_t diff) {
106  // This is the same reasoning as for AddHadOverflow. We have x = diff + y.
107  // The formula is the same, with 'x' and diff exchanged.
108  DCHECK_EQ(diff, TwosComplementSubtraction(x, y));
109  return AddHadOverflow(diff, y, x);
110 }
111 
112 // A note on overflow treatment.
113 // kint64min and kint64max are treated as infinity.
114 // Thus if the computation overflows, the result is always kint64m(ax/in).
115 //
116 // Note(user): this is actually wrong: when computing A-B, if A is kint64max
117 // and B is finite, then A-B won't be kint64max: overflows aren't sticky.
118 // TODO(user): consider making some operations overflow-sticky, some others
119 // not, but make an explicit choice throughout.
120 inline bool AddOverflows(int64_t x, int64_t y) {
121  return AddHadOverflow(x, y, TwosComplementAddition(x, y));
122 }
123 
124 inline int64_t SubOverflows(int64_t x, int64_t y) {
125  return SubHadOverflow(x, y, TwosComplementSubtraction(x, y));
126 }
127 
128 // Performs *b += a and returns false iff the addition overflow or underflow.
129 // This function only works for typed integer type (IntType<>).
130 template <typename IntegerType>
131 bool SafeAddInto(IntegerType a, IntegerType* b) {
132  const int64_t x = a.value();
133  const int64_t y = b->value();
134  const int64_t sum = TwosComplementAddition(x, y);
135  if (AddHadOverflow(x, y, sum)) return false;
136  *b = sum;
137  return true;
138 }
139 
140 // Returns kint64max if x >= 0 and kint64min if x < 0.
141 inline int64_t CapWithSignOf(int64_t x) {
142  // return kint64max if x >= 0 or kint64max + 1 (== kint64min) if x < 0.
143  return TwosComplementAddition(kint64max, static_cast<int64_t>(x < 0));
144 }
145 
146 // The following implementations are here for GCC because it does not
147 // compiled the built-ins correctly. The code is either too long without
148 // branches or contains jumps. These implementations are probably optimal
149 // on x86_64.
150 #if defined(__GNUC__) && !defined(__clang_) && defined(__x86_64__)
151 inline int64_t CapAddAsm(int64_t x, int64_t y) {
152  const int64_t cap = CapWithSignOf(x);
153  int64_t result = x;
154  // clang-format off
155  asm volatile( // 'volatile': ask compiler optimizer "keep as is".
156  "\t" "addq %[y],%[result]"
157  "\n\t" "cmovoq %[cap],%[result]" // Conditional move if overflow.
158  : [result] "=r"(result) // Output
159  : "[result]" (result), [y] "r"(y), [cap] "r"(cap) // Input.
160  : "cc" /* Clobbered registers */ );
161  // clang-format on
162  return result;
163 }
164 
165 inline int64_t CapSubAsm(int64_t x, int64_t y) {
166  const int64_t cap = CapWithSignOf(x);
167  int64_t result = x;
168  // clang-format off
169  asm volatile( // 'volatile': ask compiler optimizer "keep as is".
170  "\t" "subq %[y],%[result]"
171  "\n\t" "cmovoq %[cap],%[result]" // Conditional move if overflow.
172  : [result] "=r"(result) // Output
173  : "[result]" (result), [y] "r"(y), [cap] "r"(cap) // Input.
174  : "cc" /* Clobbered registers */ );
175  // clang-format on
176  return result;
177 }
178 
179 // Note that on x86_64, we have to use this code because it's the carry flag
180 // that is set when the product of two 64-bit integers does not fit in 64-bit.
181 inline int64_t CapProdAsm(int64_t x, int64_t y) {
182  // cap = kint64max if x and y have the same sign, cap = kint64min
183  // otherwise.
184  const int64_t cap = CapWithSignOf(x ^ y);
185  int64_t result = x;
186  // Here, we use the fact that imul of two signed 64-integers returns a 128-bit
187  // result -- we care about the lower 64 bits. More importantly, imul also sets
188  // the carry flag if 64 bits were not enough.
189  // We therefore use cmovc to return cap if the carry was set.
190  // clang-format off
191  asm volatile( // 'volatile': ask compiler optimizer "keep as is".
192  "\n\t" "imulq %[y],%[result]"
193  "\n\t" "cmovcq %[cap],%[result]" // Conditional move if carry.
194  : [result] "=r"(result) // Output
195  : "[result]" (result), [y] "r"(y), [cap] "r"(cap) // Input.
196  : "cc" /* Clobbered registers */);
197  // clang-format on
198  return result;
199 }
200 #endif
201 
202 // Simple implementations which use the built-ins provided by both GCC and
203 // clang. clang compiles to very good code for both x86_64 and ARM. This is the
204 // preferred implementation in general.
205 #if defined(__clang__)
206 inline int64_t CapAddBuiltIn(int64_t x, int64_t y) {
207  const int64_t cap = CapWithSignOf(x);
208  int64_t result;
209  const bool overflowed = __builtin_add_overflow(x, y, &result);
210  return overflowed ? cap : result;
211 }
212 
213 inline int64_t CapSubBuiltIn(int64_t x, int64_t y) {
214  const int64_t cap = CapWithSignOf(x);
215  int64_t result;
216  const bool overflowed = __builtin_sub_overflow(x, y, &result);
217  return overflowed ? cap : result;
218 }
219 
220 // As said above, this is useless on x86_64.
221 inline int64_t CapProdBuiltIn(int64_t x, int64_t y) {
222  const int64_t cap = CapWithSignOf(x ^ y);
223  int64_t result;
224  const bool overflowed = __builtin_mul_overflow(x, y, &result);
225  return overflowed ? cap : result;
226 }
227 #endif
228 
229 // Generic implementations. They are very good for addition and subtraction,
230 // less so for multiplication.
231 inline int64_t CapAddGeneric(int64_t x, int64_t y) {
232  const int64_t result = TwosComplementAddition(x, y);
233  return AddHadOverflow(x, y, result) ? CapWithSignOf(x) : result;
234 }
235 
236 inline int64_t CapSubGeneric(int64_t x, int64_t y) {
237  const int64_t result = TwosComplementSubtraction(x, y);
238  return SubHadOverflow(x, y, result) ? CapWithSignOf(x) : result;
239 }
240 
241 namespace cap_prod_util {
242 // Returns an unsigned int equal to the absolute value of n, in a way that
243 // will not produce overflows.
244 inline uint64_t uint_abs(int64_t n) {
245  return n < 0 ? ~static_cast<uint64_t>(n) + 1 : static_cast<uint64_t>(n);
246 }
247 } // namespace cap_prod_util
248 
249 // The generic algorithm computes a bound on the number of bits necessary to
250 // store the result. For this it uses the position of the most significant bits
251 // of each of the arguments.
252 // If the result needs at least 64 bits, then return a capped value.
253 // If the result needs at most 63 bits, then return the product.
254 // Otherwise, the result may use 63 or 64 bits: compute the product
255 // as a uint64_t, and cap it if necessary.
256 inline int64_t CapProdGeneric(int64_t x, int64_t y) {
257  const uint64_t a = cap_prod_util::uint_abs(x);
258  const uint64_t b = cap_prod_util::uint_abs(y);
259  // Let MSB(x) denote the most significant bit of x. We have:
260  // MSB(x) + MSB(y) <= MSB(x * y) <= MSB(x) + MSB(y) + 1
261  const int msb_sum =
263  const int kMaxBitIndexInInt64 = 63;
264  if (msb_sum <= kMaxBitIndexInInt64 - 2) return x * y;
265  // Catch a == 0 or b == 0 now, as MostSignificantBitPosition64(0) == 0.
266  // TODO(user): avoid this by writing function Log2(a) with Log2(0) == -1.
267  if (a == 0 || b == 0) return 0;
268  const int64_t cap = CapWithSignOf(x ^ y);
269  if (msb_sum >= kMaxBitIndexInInt64) return cap;
270  // The corner case is when msb_sum == 62, i.e. at least 63 bits will be
271  // needed to store the product. The following product will never overflow
272  // on uint64_t, since msb_sum == 62.
273  const uint64_t u_prod = a * b;
274  // The overflow cases are captured by one of the following conditions:
275  // (cap >= 0 && u_prod >= static_cast<uint64_t>(kint64max) or
276  // (cap < 0 && u_prod >= static_cast<uint64_t>(kint64min)).
277  // These can be optimized as follows (and if the condition is false, it is
278  // safe to compute x * y.
279  if (u_prod >= static_cast<uint64_t>(cap)) return cap;
280  const int64_t abs_result = absl::bit_cast<int64_t>(u_prod);
281  return cap < 0 ? -abs_result : abs_result;
282 }
283 
284 inline int64_t CapAdd(int64_t x, int64_t y) {
285 #if defined(__GNUC__) && !defined(__clang__) && defined(__x86_64__)
286  return CapAddAsm(x, y);
287 #elif defined(__clang__)
288  return CapAddBuiltIn(x, y);
289 #else
290  return CapAddGeneric(x, y);
291 #endif
292 }
293 
294 inline void CapAddTo(int64_t x, int64_t* y) { *y = CapAdd(*y, x); }
295 
296 inline int64_t CapSub(int64_t x, int64_t y) {
297 #if defined(__GNUC__) && !defined(__clang__) && defined(__x86_64__)
298  return CapSubAsm(x, y);
299 #elif defined(__clang__)
300  return CapSubBuiltIn(x, y);
301 #else
302  return CapSubGeneric(x, y);
303 #endif
304 }
305 
306 inline int64_t CapProd(int64_t x, int64_t y) {
307 #if defined(__GNUC__) && defined(__x86_64__)
308  // On x86_64, the product of two 64-bit registeres is a 128-bit integer,
309  // stored in two 64-bit registers. It's the carry flag that is set when the
310  // result exceeds 64 bits, not the overflow flag. We therefore have to resort
311  // to the assembly-based version of the code.
312  return CapProdAsm(x, y);
313 #elif defined(__clang__)
314  return CapProdBuiltIn(x, y);
315 #else
316  return CapProdGeneric(x, y);
317 #endif
318 }
319 
320 } // namespace operations_research
321 
322 #endif // OR_TOOLS_UTIL_SATURATED_ARITHMETIC_H_
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
int64_t b
int64_t a
static const int64_t kint64max
static const int64_t kint64min
Collection of objects used to extend the Constraint Solver library.
int64_t SubOverflows(int64_t x, int64_t y)
bool AtMinOrMaxInt64(int64_t x)
bool AddHadOverflow(int64_t x, int64_t y, int64_t sum)
int64_t CapAdd(int64_t x, int64_t y)
void CapAddTo(int64_t x, int64_t *y)
int64_t CapWithSignOf(int64_t x)
int64_t TwosComplementAddition(int64_t x, int64_t y)
int64_t CapSub(int64_t x, int64_t y)
int64_t CapAddGeneric(int64_t x, int64_t y)
bool AddOverflows(int64_t x, int64_t y)
int64_t CapProd(int64_t x, int64_t y)
bool SubHadOverflow(int64_t x, int64_t y, int64_t diff)
int64_t TwosComplementSubtraction(int64_t x, int64_t y)
int64_t CapAbs(int64_t v)
int64_t CapProdGeneric(int64_t x, int64_t y)
bool SafeAddInto(IntegerType a, IntegerType *b)
int64_t CapSubGeneric(int64_t x, int64_t y)
int64_t CapOpp(int64_t v)
int MostSignificantBitPosition64(uint64_t n)
Definition: bitset.h:232