v8
V8 is Google’s open source high-performance JavaScript and WebAssembly engine, written in C++.
Loading...
Searching...
No Matches
typer.h
Go to the documentation of this file.
1// Copyright 2023 the V8 project authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#ifndef V8_COMPILER_TURBOSHAFT_TYPER_H_
6#define V8_COMPILER_TURBOSHAFT_TYPER_H_
7
8#include <limits>
9
10#include "src/base/logging.h"
11#include "src/base/vector.h"
15
17
18// Returns the array's least element, ignoring NaN.
19// There must be at least one non-NaN element.
20// Any -0 is converted to 0.
21template <typename T, size_t N>
22T array_min(const std::array<T, N>& a) {
23 DCHECK_NE(0, N);
24 T x = +std::numeric_limits<T>::infinity();
25 for (size_t i = 0; i < N; ++i) {
26 if (!std::isnan(a[i])) {
27 x = std::min(a[i], x);
28 }
29 }
30 DCHECK(!std::isnan(x));
31 return x == T{0} ? T{0} : x; // -0 -> 0
32}
33
34// Returns the array's greatest element, ignoring NaN.
35// There must be at least one non-NaN element.
36// Any -0 is converted to 0.
37template <typename T, size_t N>
38T array_max(const std::array<T, N>& a) {
39 DCHECK_NE(0, N);
40 T x = -std::numeric_limits<T>::infinity();
41 for (size_t i = 0; i < N; ++i) {
42 if (!std::isnan(a[i])) {
43 x = std::max(a[i], x);
44 }
45 }
46 DCHECK(!std::isnan(x));
47 return x == T{0} ? T{0} : x; // -0 -> 0
48}
49
50template <size_t Bits>
52 static_assert(Bits == 32 || Bits == 64);
56 static constexpr word_t max = std::numeric_limits<word_t>::max();
57
58 static type_t FromElements(ElementsVector elements, Zone* zone) {
59 base::sort(elements);
60 auto it = std::unique(elements.begin(), elements.end());
61 elements.pop_back(std::distance(it, elements.end()));
62 DCHECK(!elements.empty());
63 if (elements.size() <= type_t::kMaxSetSize) {
64 return type_t::Set(elements, zone);
65 }
66
67 auto range = MakeRange(base::VectorOf(elements));
68 auto result = type_t::Range(range.first, range.second, zone);
69 DCHECK(
70 base::all_of(elements, [&](word_t e) { return result.Contains(e); }));
71 return result;
72 }
73
74 static std::pair<word_t, word_t> MakeRange(const type_t& t) {
75 if (t.is_range()) return t.range();
76 DCHECK(t.is_set());
77 return MakeRange(t.set_elements());
78 }
79
80 // This function tries to find a somewhat reasonable range for a given set of
81 // values. If the elements span no more than half of the range, we just
82 // construct the range from min(elements) to max(elements) Otherwise, we
83 // consider a wrapping range because it is likely that there is a larger gap
84 // in the middle of the elements. For that, we start with a wrapping range
85 // from max(elements) to min(elements) and then incrementally add another
86 // element either by increasing the 'to' or decreasing the 'from' of the
87 // range, whichever leads to a smaller range.
88 static std::pair<word_t, word_t> MakeRange(
90 DCHECK(!elements.empty());
92 if (elements[elements.size() - 1] - elements[0] <= max / 2) {
93 // Construct a non-wrapping range.
94 return {elements[0], elements[elements.size() - 1]};
95 }
96 // Construct a wrapping range.
97 size_t from_index = elements.size() - 1;
98 size_t to_index = 0;
99 while (to_index + 1 < from_index) {
100 if ((elements[to_index + 1] - elements[to_index]) <
101 (elements[from_index] - elements[from_index - 1])) {
102 ++to_index;
103 } else {
104 --from_index;
105 }
106 }
107 return {elements[from_index], elements[to_index]};
108 }
109
110 static word_t distance(const std::pair<word_t, word_t>& range) {
111 return distance(range.first, range.second);
112 }
113 static word_t distance(word_t from, word_t to) {
114 return is_wrapping(from, to) ? (max - from + to) : to - from;
115 }
116
117 static bool is_wrapping(const std::pair<word_t, word_t>& range) {
118 return is_wrapping(range.first, range.second);
119 }
120 static bool is_wrapping(word_t from, word_t to) { return from > to; }
121
122 static type_t Add(const type_t& lhs, const type_t& rhs, Zone* zone) {
123 if (lhs.is_any() || rhs.is_any()) return type_t::Any();
124
125 // If both sides are decently small sets, we produce the product set (which
126 // we convert to a range if it exceeds the set limit).
127 if (lhs.is_set() && rhs.is_set()) {
128 ElementsVector result_elements;
129 for (int i = 0; i < lhs.set_size(); ++i) {
130 for (int j = 0; j < rhs.set_size(); ++j) {
131 result_elements.push_back(lhs.set_element(i) + rhs.set_element(j));
132 }
133 }
134 return FromElements(std::move(result_elements), zone);
135 }
136
137 // Otherwise just construct a range.
138 std::pair<word_t, word_t> x = MakeRange(lhs);
139 std::pair<word_t, word_t> y = MakeRange(rhs);
140
141 // If the result would not be a complete range, we compute it.
142 // Check: (lhs.to - lhs.from + 1) + rhs.to - rhs.from < max
143 // =====> (lhs.to - lhs.from + 1) < max - rhs.to + rhs.from
144 // =====> (lhs.to - lhs.from + 1) < max - (rhs.to - rhs.from)
145 if (distance(x) + 1 < max - distance(y)) {
146 return type_t::Range(x.first + y.first, x.second + y.second, zone);
147 }
148
149 return type_t::Any();
150 }
151
152 static type_t Subtract(const type_t& lhs, const type_t& rhs, Zone* zone) {
153 if (lhs.is_any() || rhs.is_any()) return type_t::Any();
154
155 // If both sides are decently small sets, we produce the product set (which
156 // we convert to a range if it exceeds the set limit).
157 if (lhs.is_set() && rhs.is_set()) {
158 ElementsVector result_elements;
159 for (int i = 0; i < lhs.set_size(); ++i) {
160 for (int j = 0; j < rhs.set_size(); ++j) {
161 result_elements.push_back(lhs.set_element(i) - rhs.set_element(j));
162 }
163 }
164 return FromElements(std::move(result_elements), zone);
165 }
166
167 // Otherwise just construct a range.
168 std::pair<word_t, word_t> x = MakeRange(lhs);
169 std::pair<word_t, word_t> y = MakeRange(rhs);
170
171 if (!is_wrapping(x) && !is_wrapping(y)) {
172 // If the result would not be a complete range, we compute it.
173 // Check: (lhs.to - lhs.from + 1) + rhs.to - rhs.from < max
174 // =====> (lhs.to - lhs.from + 1) < max - rhs.to + rhs.from
175 // =====> (lhs.to - lhs.from + 1) < max - (rhs.to - rhs.from)
176 if (distance(x) + 1 < max - distance(y)) {
177 return type_t::Range(x.first - y.second, x.second - y.first, zone);
178 }
179 }
180
181 // TODO(nicohartmann@): Improve the wrapping cases.
182 return type_t::Any();
183 }
184
185 static Word32Type UnsignedLessThan(const type_t& lhs, const type_t& rhs,
186 Zone* zone) {
187 bool can_be_true = lhs.unsigned_min() < rhs.unsigned_max();
188 bool can_be_false = lhs.unsigned_max() >= rhs.unsigned_min();
189
190 if (!can_be_true) return Word32Type::Constant(0);
191 if (!can_be_false) return Word32Type::Constant(1);
192 return Word32Type::Set({0, 1}, zone);
193 }
194
196 const type_t& rhs, Zone* zone) {
197 bool can_be_true = lhs.unsigned_min() <= rhs.unsigned_max();
198 bool can_be_false = lhs.unsigned_max() > rhs.unsigned_min();
199
200 if (!can_be_true) return Word32Type::Constant(0);
201 if (!can_be_false) return Word32Type::Constant(1);
202 return Word32Type::Set({0, 1}, zone);
203 }
204
205 // Computes the ranges to which the sides of the unsigned comparison (lhs <
206 // rhs) can be restricted when the comparison is true. When the comparison is
207 // true, we learn: lhs cannot be >= rhs.max and rhs cannot be <= lhs.min.
208 static std::pair<Type, Type> RestrictionForUnsignedLessThan_True(
209 const type_t& lhs, const type_t& rhs, Zone* zone) {
210 Type restrict_lhs;
211 if (rhs.unsigned_max() == 0) {
212 // There is no value for lhs that could make (lhs < 0) true.
213 restrict_lhs = Type::None();
214 } else {
215 restrict_lhs = type_t::Range(0, next_smaller(rhs.unsigned_max()), zone);
216 }
217
218 Type restrict_rhs;
219 if (lhs.unsigned_min() == max) {
220 // There is no value for rhs that could make (max < rhs) true.
221 restrict_rhs = Type::None();
222 } else {
223 restrict_rhs = type_t::Range(next_larger(lhs.unsigned_min()), max, zone);
224 }
225
226 return {restrict_lhs, restrict_rhs};
227 }
228
229 // Computes the ranges to which the sides of the unsigned comparison (lhs <
230 // rhs) can be restricted when the comparison is false. When the comparison is
231 // false, we learn: lhs cannot be < rhs.min and rhs cannot be > lhs.max.
232 static std::pair<Type, Type> RestrictionForUnsignedLessThan_False(
233 const type_t& lhs, const type_t& rhs, Zone* zone) {
234 return {type_t::Range(rhs.unsigned_min(), max, zone),
235 type_t::Range(0, lhs.unsigned_max(), zone)};
236 }
237
238 // Computes the ranges to which the sides of the unsigned comparison (lhs <=
239 // rhs) can be restricted when the comparison is true. When the comparison is
240 // true, we learn: lhs cannot be > rhs.max and rhs cannot be < lhs.min.
241 static std::pair<Type, Type> RestrictionForUnsignedLessThanOrEqual_True(
242 const type_t& lhs, const type_t& rhs, Zone* zone) {
243 return {type_t::Range(0, rhs.unsigned_max(), zone),
244 type_t::Range(lhs.unsigned_min(), max, zone)};
245 }
246
247 // Computes the ranges to which the sides of the unsigned comparison (lhs <=
248 // rhs) can be restricted when the comparison is false. When the comparison is
249 // false, we learn: lhs cannot be <= rhs.min and rhs cannot be >= lhs.max.
251 const type_t& lhs, const type_t& rhs, Zone* zone) {
252 Type restrict_lhs;
253 if (rhs.unsigned_min() == max) {
254 // There is no value for lhs that could make (lhs <= max) false.
255 restrict_lhs = Type::None();
256 } else {
257 restrict_lhs = type_t::Range(next_larger(rhs.unsigned_min()), max, zone);
258 }
259
260 Type restrict_rhs;
261 if (lhs.unsigned_max() == 0) {
262 // There is no value for rhs that could make (0 <= rhs) false.
263 restrict_rhs = Type::None();
264 } else {
265 restrict_rhs = type_t::Range(0, next_smaller(lhs.unsigned_max()), zone);
266 }
267
268 return {restrict_lhs, restrict_rhs};
269 }
270
271 // WidenMaximal widens one of the boundary to the extreme immediately.
272 static type_t WidenMaximal(const type_t& old_type, const type_t& new_type,
273 Zone* zone) {
274 if (new_type.is_any()) return new_type;
275 if (old_type.is_wrapping() || new_type.is_wrapping()) return type_t::Any();
276
277 word_t result_from = new_type.unsigned_min();
278 if (result_from < old_type.unsigned_min()) result_from = 0;
279 word_t result_to = new_type.unsigned_max();
280 if (result_to > old_type.unsigned_max()) {
281 result_to = std::numeric_limits<word_t>::max();
282 }
283 return type_t::Range(result_from, result_to, zone);
284 }
285
286 // Performs exponential widening, which means that the number of values
287 // described by the resulting type is at least doubled with respect to the
288 // {old_type}. If {new_type} is already twice the size of {old_type},
289 // {new_type} may be returned directly.
290 static type_t WidenExponential(const type_t& old_type, type_t new_type,
291 Zone* zone) {
292 if (new_type.is_any()) return new_type;
293 word_t old_from, old_to, new_from, new_to;
294 if (old_type.is_set()) {
295 const word_t old_size = old_type.set_size();
296 if (new_type.is_set()) {
297 const word_t new_size = new_type.set_size();
298 if (new_size >= 2 * old_size) return new_type;
299 std::tie(new_from, new_to) = MakeRange(new_type);
300 } else {
301 DCHECK(new_type.is_range());
302 std::tie(new_from, new_to) = new_type.range();
303 }
304 if (distance(new_from, new_to) >= 2 * old_size) {
305 return type_t::Range(new_from, new_to, zone);
306 }
307 std::tie(old_from, old_to) = MakeRange(old_type);
308 } else {
309 DCHECK(old_type.is_range());
310 std::tie(old_from, old_to) = old_type.range();
311 if (new_type.is_set()) {
312 std::tie(new_from, new_to) = MakeRange(new_type);
313 } else {
314 DCHECK(new_type.is_range());
315 std::tie(new_from, new_to) = new_type.range();
316 }
317 }
318
319 // If the old type is already quite large, we go to full range.
320 if (distance(old_from, old_to) >= std::numeric_limits<word_t>::max() / 4) {
321 return type_t::Any();
322 }
323
324 const word_t min_size = 2 * (distance(old_from, old_to) + 1);
325 if (distance(new_from, new_to) >= min_size) {
326 return type_t::Range(new_from, new_to, zone);
327 }
328
329 // If old is wrapping (and so is new).
330 if (is_wrapping(old_from, old_to)) {
331 DCHECK(is_wrapping(new_from, new_to));
332 if (new_from < old_from) {
333 DCHECK_LE(old_to, new_to);
334 // We widen the `from` (although `to` might have grown, too).
335 DCHECK_LT(new_to, min_size);
336 word_t result_from =
337 std::numeric_limits<word_t>::max() - (min_size - new_to);
338 DCHECK_LT(result_from, new_from);
339 DCHECK_LE(min_size, distance(result_from, new_to));
340 return type_t::Range(result_from, new_to, zone);
341 } else {
342 DCHECK_EQ(old_from, new_from);
343 // We widen the `to`.
344 DCHECK_LT(std::numeric_limits<word_t>::max() - new_from, min_size);
345 word_t result_to =
346 min_size - (std::numeric_limits<word_t>::max() - new_from);
347 DCHECK_GT(result_to, new_to);
348 DCHECK_LE(min_size, distance(new_from, result_to));
349 return type_t::Range(new_from, result_to, zone);
350 }
351 }
352
353 // If old is not wrapping, but new is.
354 if (is_wrapping(new_from, new_to)) {
355 if (new_to < old_to) {
356 // If wrapping was caused by to growing over max, grow `to` further
357 // (although `from` might have grown, too).
358 DCHECK_LT(std::numeric_limits<word_t>::max() - new_from, min_size);
359 word_t result_to =
360 min_size - (std::numeric_limits<word_t>::max() - new_from);
361 DCHECK_LT(new_to, result_to);
362 return type_t::Range(new_from, result_to, zone);
363 } else {
364 DCHECK_LT(old_from, new_from);
365 // If wrapping was caused by `from` growing below 0, grow `from`
366 // further.
367 DCHECK_LT(new_to, min_size);
368 word_t result_from =
369 std::numeric_limits<word_t>::max() - (min_size - new_to);
370 DCHECK_LT(result_from, new_from);
371 return type_t::Range(result_from, new_to, zone);
372 }
373 }
374
375 // Neither old nor new is wrapping.
376 if (new_from < old_from) {
377 DCHECK_LE(old_to, new_to);
378 // Check if we can widen the `from`.
379 if (new_to >= min_size) {
380 // We can decrease `from` without going below 0.
381 word_t result_from = new_to - min_size;
382 DCHECK_LT(result_from, new_from);
383 return type_t::Range(result_from, new_to, zone);
384 } else {
385 // We cannot grow `from` enough, so we also have to grow `to`.
386 return type_t::Range(0, min_size, zone);
387 }
388 } else {
389 DCHECK_EQ(old_from, new_from);
390 // Check if we can widen the `to`.
391 if (new_from <= std::numeric_limits<word_t>::max() - min_size) {
392 // We can increase `to` without going above max.
393 word_t result_to = new_from + min_size;
394 DCHECK_GT(result_to, new_to);
395 return type_t::Range(new_from, result_to, zone);
396 } else {
397 // We cannot grow `to` enough, so we also have to grow `from`.
398 return type_t::Range(std::numeric_limits<word_t>::max() - min_size,
399 std::numeric_limits<word_t>::max(), zone);
400 }
401 }
402 }
403};
404
405template <size_t Bits>
407 static_assert(Bits == 32 || Bits == 64);
408 using float_t = std::conditional_t<Bits == 32, float, double>;
410 static constexpr float_t inf = std::numeric_limits<float_t>::infinity();
411 static constexpr int kSetThreshold = type_t::kMaxSetSize;
412
413 static type_t Range(float_t min, float_t max, uint32_t special_values,
414 Zone* zone) {
415 DCHECK_LE(min, max);
417 (special_values & type_t::kMinusZero));
419 (special_values & type_t::kMinusZero));
420 if (min == max) return Set({min + float_t{0}}, special_values, zone);
421 return type_t::Range(min, max, special_values, zone);
422 }
423
424 static type_t Set(std::vector<float_t> elements, uint32_t special_values,
425 Zone* zone) {
426 base::sort(elements);
427 elements.erase(std::unique(elements.begin(), elements.end()),
428 elements.end());
429 if (base::erase_if(elements, [](float_t v) { return std::isnan(v); }) > 0) {
430 special_values |= type_t::kNaN;
431 }
432 if (base::erase_if(elements, [](float_t v) { return IsMinusZero(v); }) >
433 0) {
434 special_values |= type_t::kMinusZero;
435 }
436 if (elements.empty()) {
437 DCHECK_NE(0, special_values);
438 return type_t::OnlySpecialValues(special_values);
439 }
440 return type_t::Set(elements, special_values, zone);
441 }
442
443 // Check if the elements in the set are all integers. This ignores special
444 // values (NaN, -0)!
445 static bool IsIntegerSet(const type_t& t) {
446 if (!t.is_set()) return false;
447 int size = t.set_size();
448 DCHECK_LT(0, size);
449
450 float_t unused_ipart;
451 float_t min = t.set_element(0);
452 if (std::modf(min, &unused_ipart) != 0.0) return false;
453 if (min == -inf) return false;
454 float_t max = t.set_element(size - 1);
455 if (std::modf(max, &unused_ipart) != 0.0) return false;
456 if (max == inf) return false;
457
458 for (int i = 1; i < size - 1; ++i) {
459 if (std::modf(t.set_element(i), &unused_ipart) != 0.0) return false;
460 }
461 return true;
462 }
463
464 static bool IsZeroish(const type_t& l) {
465 return l.has_nan() || l.has_minus_zero() || l.Contains(0);
466 }
467
468 // Tries to construct the product of two sets where values are generated using
469 // {combine}. Returns Type::Invalid() if a set cannot be constructed (e.g.
470 // because the result exceeds the maximal number of set elements).
471 static Type ProductSet(const type_t& l, const type_t& r,
472 uint32_t special_values, Zone* zone,
473 std::function<float_t(float_t, float_t)> combine) {
474 DCHECK(l.is_set());
475 DCHECK(r.is_set());
476
477 std::vector<float_t> results;
478 auto CombineWithLeft = [&](float_t left) {
479 for (int j = 0; j < r.set_size(); ++j) {
480 results.push_back(combine(left, r.set_element(j)));
481 }
482 if (r.has_minus_zero()) results.push_back(combine(left, -0.0));
483 if (r.has_nan()) results.push_back(combine(left, nan_v<Bits>));
484 };
485
486 for (int i = 0; i < l.set_size(); ++i) {
487 CombineWithLeft(l.set_element(i));
488 }
489 if (l.has_minus_zero()) CombineWithLeft(-0.0);
490 if (l.has_nan()) CombineWithLeft(nan_v<Bits>);
491
492 if (base::erase_if(results, [](float_t v) { return std::isnan(v); }) > 0) {
493 special_values |= type_t::kNaN;
494 }
495 if (base::erase_if(results, [](float_t v) { return IsMinusZero(v); }) > 0) {
496 special_values |= type_t::kMinusZero;
497 }
498 base::sort(results);
499 auto it = std::unique(results.begin(), results.end());
500 if (std::distance(results.begin(), it) > kSetThreshold)
501 return Type::Invalid();
502 results.erase(it, results.end());
503 if (results.empty()) return type_t::OnlySpecialValues(special_values);
504 return Set(std::move(results), special_values, zone);
505 }
506
507 static Type Add(type_t l, type_t r, Zone* zone) {
508 // Addition can return NaN if either input can be NaN or we try to compute
509 // the sum of two infinities of opposite sign.
510 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
511 bool maybe_nan = l.has_nan() || r.has_nan();
512
513 // Addition can yield minus zero only if both inputs can be minus zero.
514 bool maybe_minuszero = true;
515 if (l.has_minus_zero()) {
517 } else {
518 maybe_minuszero = false;
519 }
520 if (r.has_minus_zero()) {
522 } else {
523 maybe_minuszero = false;
524 }
525
526 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
527 (maybe_minuszero ? type_t::kMinusZero : 0);
528 // If both sides are decently small sets, we produce the product set.
529 auto combine = [](float_t a, float_t b) { return a + b; };
530 if (l.is_set() && r.is_set()) {
531 auto result = ProductSet(l, r, special_values, zone, combine);
532 if (!result.IsInvalid()) return result;
533 }
534
535 // Otherwise just construct a range.
536 auto [l_min, l_max] = l.minmax();
537 auto [r_min, r_max] = r.minmax();
538
539 std::array<float_t, 4> results;
540 results[0] = l_min + r_min;
541 results[1] = l_min + r_max;
542 results[2] = l_max + r_min;
543 results[3] = l_max + r_max;
544
545 int nans = 0;
546 for (int i = 0; i < 4; ++i) {
547 if (std::isnan(results[i])) ++nans;
548 }
549 if (nans > 0) {
550 special_values |= type_t::kNaN;
551 if (nans >= 4) {
552 // All combinations of inputs produce NaN.
553 return type_t::OnlySpecialValues(special_values);
554 }
555 }
556 const float_t result_min = array_min(results);
557 const float_t result_max = array_max(results);
558 return Range(result_min, result_max, special_values, zone);
559 }
560
561 static Type Subtract(type_t l, type_t r, Zone* zone) {
562 // Subtraction can return NaN if either input can be NaN or we try to
563 // compute the sum of two infinities of opposite sign.
564 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
565 bool maybe_nan = l.has_nan() || r.has_nan();
566
567 // Subtraction can yield minus zero if {lhs} can be minus zero and {rhs}
568 // can be zero.
569 bool maybe_minuszero = false;
570 if (l.has_minus_zero()) {
572 maybe_minuszero = r.Contains(0);
573 }
574 if (r.has_minus_zero()) {
576 }
577
578 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
579 (maybe_minuszero ? type_t::kMinusZero : 0);
580 // If both sides are decently small sets, we produce the product set.
581 auto combine = [](float_t a, float_t b) { return a - b; };
582 if (l.is_set() && r.is_set()) {
583 auto result = ProductSet(l, r, special_values, zone, combine);
584 if (!result.IsInvalid()) return result;
585 }
586
587 // Otherwise just construct a range.
588 auto [l_min, l_max] = l.minmax();
589 auto [r_min, r_max] = r.minmax();
590
591 std::array<float_t, 4> results;
592 results[0] = l_min - r_min;
593 results[1] = l_min - r_max;
594 results[2] = l_max - r_min;
595 results[3] = l_max - r_max;
596
597 int nans = 0;
598 for (int i = 0; i < 4; ++i) {
599 if (std::isnan(results[i])) ++nans;
600 }
601 if (nans > 0) {
602 special_values |= type_t::kNaN;
603 if (nans >= 4) {
604 // All combinations of inputs produce NaN.
605 return type_t::NaN();
606 }
607 }
608 const float_t result_min = array_min(results);
609 const float_t result_max = array_max(results);
610 return Range(result_min, result_max, special_values, zone);
611 }
612
613 static Type Multiply(type_t l, type_t r, Zone* zone) {
614 // Multiplication propagates NaN:
615 // NaN * x = NaN (regardless of sign of x)
616 // 0 * Infinity = NaN (regardless of signs)
617 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
618 bool maybe_nan = l.has_nan() || r.has_nan() ||
619 (IsZeroish(l) && (r.min() == -inf || r.max() == inf)) ||
620 (IsZeroish(r) && (l.min() == -inf || r.max() == inf));
621
622 // Try to rule out -0.
623 bool maybe_minuszero = l.has_minus_zero() || r.has_minus_zero() ||
624 (IsZeroish(l) && r.min() < 0.0) ||
625 (IsZeroish(r) && l.min() < 0.0);
626 if (l.has_minus_zero()) {
628 }
629 if (r.has_minus_zero()) {
631 }
632
633 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
634 (maybe_minuszero ? type_t::kMinusZero : 0);
635 // If both sides are decently small sets, we produce the product set.
636 auto combine = [](float_t a, float_t b) { return a * b; };
637 if (l.is_set() && r.is_set()) {
638 auto result = ProductSet(l, r, special_values, zone, combine);
639 if (!result.IsInvalid()) return result;
640 }
641
642 // Otherwise just construct a range.
643 auto [l_min, l_max] = l.minmax();
644 auto [r_min, r_max] = r.minmax();
645
646 std::array<float_t, 4> results;
647 results[0] = l_min * r_min;
648 results[1] = l_min * r_max;
649 results[2] = l_max * r_min;
650 results[3] = l_max * r_max;
651
652 for (int i = 0; i < 4; ++i) {
653 if (std::isnan(results[i])) {
654 return type_t::Any();
655 }
656 }
657
658 float_t result_min = array_min(results);
659 float_t result_max = array_max(results);
660 if (result_min <= 0.0 && 0.0 <= result_max &&
661 (l_min < 0.0 || r_min < 0.0)) {
662 special_values |= type_t::kMinusZero;
663 // Remove -0.
664 result_min += 0.0;
665 result_max += 0.0;
666 }
667 // 0 * V8_INFINITY is NaN, regardless of sign
668 if (((l_min == -inf || l_max == inf) && (r_min <= 0.0 && 0.0 <= r_max)) ||
669 ((r_min == -inf || r_max == inf) && (l_min <= 0.0 && 0.0 <= l_max))) {
670 special_values |= type_t::kNaN;
671 }
672
673 type_t type = Range(result_min, result_max, special_values, zone);
674 return type;
675 }
676
677 static Type Divide(const type_t& l, const type_t& r, Zone* zone) {
678 // Division is tricky, so all we do is try ruling out -0 and NaN.
679 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
680
681 // If both sides are decently small sets, we produce the product set.
682 auto combine = [](float_t a, float_t b) {
683 if V8_UNLIKELY (!std::isfinite(a) && !std::isfinite(b)) {
684 return nan_v<Bits>;
685 }
686 if V8_UNLIKELY (IsMinusZero(b)) {
687 // +-0 / -0 ==> NaN
688 if (a == 0 || std::isnan(a)) return nan_v<Bits>;
689 return a > 0 ? -inf : inf;
690 }
691 if V8_UNLIKELY (b == 0) {
692 // +-0 / 0 ==> NaN
693 if (a == 0 || std::isnan(a)) return nan_v<Bits>;
694 return a > 0 ? inf : -inf;
695 }
696 return a / b;
697 };
698 if (l.is_set() && r.is_set()) {
699 auto result = ProductSet(l, r, 0, zone, combine);
700 if (!result.IsInvalid()) return result;
701 }
702
703 auto [l_min, l_max] = l.minmax();
704 auto [r_min, r_max] = r.minmax();
705
706 bool maybe_nan =
707 l.has_nan() || IsZeroish(r) ||
708 ((l_min == -inf || l_max == inf) && (r_min == -inf || r_max == inf));
709
710 // Try to rule out -0.
711 bool maybe_minuszero =
712 // -0 / r (r > 0)
713 (l.has_minus_zero() && r_max > 0)
714 // 0 / r (r < 0)
715 || (l.Contains(0) && r_min < 0)
716 // -0.0..01 / r (r > 1)
717 || (l.Contains(0) && l_min < 0 && r_max > 1)
718 // 0.0..01 / r (r < -1)
719 || (l.Contains(0) && l_max >= 0 && r_min < -1)
720 // l / large (l < 0)
721 || (l_max < 0 && detail::is_minus_zero(l_max / r_max))
722 // l / -large (l > 0)
723 || (l_min > 0 && detail::is_minus_zero(l_min / r_min));
724
725 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
726 (maybe_minuszero ? type_t::kMinusZero : 0);
727
728 const bool r_all_positive = r_min >= 0 && !r.has_minus_zero();
729 const bool r_all_negative = r_max < 0;
730
731 // If r doesn't span 0, we can try to compute a more precise type.
732 if (r_all_positive || r_all_negative) {
733 // If r does not contain 0 or -0, we can compute a range.
734 if (r_min > 0 && !r.has_minus_zero()) {
735 std::array<float_t, 4> results;
736 results[0] = l_min / r_min;
737 results[1] = l_min / r_max;
738 results[2] = l_max / r_min;
739 results[3] = l_max / r_max;
740
741 for (float_t res : results) {
742 if (std::isnan(res)) return type_t::Any();
743 }
744
745 const float_t result_min = array_min(results);
746 const float_t result_max = array_max(results);
747 return Range(result_min, result_max, special_values, zone);
748 }
749
750 // Otherwise we try to check for the sign of the result.
751 if (l_max < 0) {
752 if (r_all_positive) {
753 // All values are negative.
754 return Range(-inf, next_smaller(float_t{0}), special_values, zone);
755 } else {
756 DCHECK(r_all_negative);
757 // All values are positive.
758 return Range(0, inf, special_values, zone);
759 }
760 } else if (l_min >= 0 && !l.has_minus_zero()) {
761 if (r_all_positive) {
762 // All values are positive.
763 DCHECK_EQ(special_values & type_t::kMinusZero, 0);
764 return Range(0, inf, special_values, zone);
765 } else {
766 DCHECK(r_all_negative);
767 // All values are negative.
768 return Range(-inf, next_smaller(float_t{0}), special_values, zone);
769 }
770 }
771 }
772
773 // Otherwise we give up on a precise type.
774 return type_t::Any(special_values);
775 }
776
777 static Type Modulus(type_t l, type_t r, Zone* zone) {
778 // Modulus can yield NaN if either {lhs} or {rhs} are NaN, or
779 // {lhs} is not finite, or the {rhs} is a zero value.
780 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
781 bool maybe_nan =
782 l.has_nan() || IsZeroish(r) || l.min() == -inf || l.max() == inf;
783
784 // Deal with -0 inputs, only the signbit of {lhs} matters for the result.
785 bool maybe_minuszero = l.min() < 0;
786 if (l.has_minus_zero()) {
787 maybe_minuszero = true;
789 }
790 if (r.has_minus_zero()) {
792 }
793
794 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
795 (maybe_minuszero ? type_t::kMinusZero : 0);
796 // For integer inputs {l} and {r} we can infer a precise type.
797 if (IsIntegerSet(l) && IsIntegerSet(r)) {
798 auto [l_min, l_max] = l.minmax();
799 auto [r_min, r_max] = r.minmax();
800 // l % r is:
801 // - never greater than abs(l)
802 // - never greater than abs(r) - 1
803 auto l_abs = std::max(std::abs(l_min), std::abs(l_max));
804 auto r_abs = std::max(std::abs(r_min), std::abs(r_max));
805 // If rhs is 0, we can only produce NaN.
806 if (r_abs == 0) return type_t::NaN();
807 r_abs -= 1;
808 auto abs = std::min(l_abs, r_abs);
809 float_t min = 0.0, max = 0.0;
810 if (l_min >= 0.0) {
811 // {l} positive.
812 max = abs;
813 } else if (l_max <= 0.0) {
814 // {l} negative.
815 min = 0.0 - abs;
816 } else {
817 // {l} positive or negative.
818 min = 0.0 - abs;
819 max = abs;
820 }
821 if (min == max) return Set({min}, special_values, zone);
822 return Range(min, max, special_values, zone);
823 }
824
825 // Otherwise, we give up.
826 return type_t::Any(special_values);
827 }
828
829 static Type Min(type_t l, type_t r, Zone* zone) {
830 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
831 bool maybe_nan = l.has_nan() || r.has_nan();
832
833 // In order to ensure monotonicity of the computation below, we additionally
834 // pretend +0 is present (for simplicity on both sides).
835 bool maybe_minuszero = false;
836 if (l.has_minus_zero() && !(r.max() < 0.0)) {
837 maybe_minuszero = true;
839 }
840 if (r.has_minus_zero() && !(l.max() < 0.0)) {
841 maybe_minuszero = true;
843 }
844
845 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
846 (maybe_minuszero ? type_t::kMinusZero : 0);
847 // If both sides are decently small sets, we produce the product set.
848 auto combine = [](float_t a, float_t b) { return std::min(a, b); };
849 if (l.is_set() && r.is_set()) {
850 // TODO(nicohartmann@): There is a faster way to compute this set.
851 auto result = ProductSet(l, r, special_values, zone, combine);
852 if (!result.IsInvalid()) return result;
853 }
854
855 // Otherwise just construct a range.
856 auto [l_min, l_max] = l.minmax();
857 auto [r_min, r_max] = r.minmax();
858
859 auto min = std::min(l_min, r_min);
860 auto max = std::min(l_max, r_max);
861 return Range(min, max, special_values, zone);
862 }
863
864 static Type Max(type_t l, type_t r, Zone* zone) {
865 if (l.is_only_nan() || r.is_only_nan()) return type_t::NaN();
866 bool maybe_nan = l.has_nan() || r.has_nan();
867
868 // In order to ensure monotonicity of the computation below, we additionally
869 // pretend +0 is present (for simplicity on both sides).
870 bool maybe_minuszero = false;
871 if (l.has_minus_zero() && !(r.min() > 0.0)) {
872 maybe_minuszero = true;
874 }
875 if (r.has_minus_zero() && !(l.min() > 0.0)) {
876 maybe_minuszero = true;
878 }
879
880 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
881 (maybe_minuszero ? type_t::kMinusZero : 0);
882 // If both sides are decently small sets, we produce the product set.
883 auto combine = [](float_t a, float_t b) { return std::max(a, b); };
884 if (l.is_set() && r.is_set()) {
885 // TODO(nicohartmann@): There is a faster way to compute this set.
886 auto result = ProductSet(l, r, special_values, zone, combine);
887 if (!result.IsInvalid()) return result;
888 }
889
890 // Otherwise just construct a range.
891 auto [l_min, l_max] = l.minmax();
892 auto [r_min, r_max] = r.minmax();
893
894 auto min = std::max(l_min, r_min);
895 auto max = std::max(l_max, r_max);
896 return Range(min, max, special_values, zone);
897 }
898
899 static Type Power(const type_t& l, const type_t& r, Zone* zone) {
900 // x ** NaN => Nan.
901 if (r.is_only_nan()) return type_t::NaN();
902 // x ** +-0 => 1.
903 if (r.is_constant(0) || r.is_only_minus_zero()) return type_t::Constant(1);
904 if (l.is_only_nan()) {
905 // NaN ** 0 => 1.
906 if (r.Contains(0) || r.has_minus_zero()) {
907 return type_t::Set({1}, type_t::kNaN, zone);
908 }
909 // NaN ** x => NaN (x != +-0).
910 return type_t::NaN();
911 }
912 bool maybe_nan = l.has_nan() || r.has_nan();
913 // +-1 ** +-Infinity => NaN.
914 if (r.Contains(-inf) || r.Contains(inf)) {
915 if (l.Contains(1) || l.Contains(-1)) maybe_nan = true;
916 }
917
918 // a ** b produces NaN if a < 0 && b is fraction.
919 if (l.min() < 0.0 && !IsIntegerSet(r)) maybe_nan = true;
920
921 // Precise checks for when the result can be -0 is difficult, because of
922 // large (negative) exponents. To be safe we add -0 whenever the left hand
923 // side can be negative. We might refine this when necessary.
924 bool maybe_minus_zero = l.min() < 0.0 || l.has_minus_zero();
925 uint32_t special_values = (maybe_nan ? type_t::kNaN : 0) |
926 (maybe_minus_zero ? type_t::kMinusZero : 0) |
927 l.special_values();
928
929 // If both sides are decently small sets, we produce the product set.
930 auto combine = [](float_t a, float_t b) { return std::pow(a, b); };
931 if (l.is_set() && r.is_set()) {
932 auto result = ProductSet(l, r, special_values, zone, combine);
933 if (!result.IsInvalid()) return result;
934 }
935
936 // TODO(nicohartmann@): Maybe we can produce a more precise range here.
937 return type_t::Any(special_values);
938 }
939
940 static Type Atan2(const type_t& l, const type_t& r, Zone* zone) {
941 // TODO(nicohartmann@): Maybe we can produce a more precise range here.
942 return type_t::Any();
943 }
944
945 static Type LessThan(const type_t& lhs, const type_t& rhs, Zone* zone) {
946 bool can_be_true = false;
947 bool can_be_false = false;
948 if (lhs.is_only_special_values()) {
949 if (lhs.has_minus_zero()) {
950 can_be_true = !rhs.is_only_special_values() && rhs.max() > 0.0;
951 can_be_false = rhs.min() <= 0.0;
952 } else {
953 DCHECK(lhs.is_only_nan());
954 }
955 } else if (rhs.is_only_special_values()) {
956 if (rhs.has_minus_zero()) {
957 can_be_true = lhs.min() < 0.0;
958 can_be_false = lhs.max() >= 0.0;
959 } else {
960 DCHECK(rhs.is_only_nan());
961 }
962 } else {
963 // Both sides have at least one non-special value. We don't have to treat
964 // special values here, because nan has been taken care of already and
965 // -0.0 is included in min/max.
966 can_be_true = lhs.min() < rhs.max();
967 can_be_false = lhs.max() >= rhs.min();
968 }
969
970 // Consider NaN.
971 can_be_false = can_be_false || lhs.has_nan() || rhs.has_nan();
972
973 if (!can_be_true) return Word32Type::Constant(0);
974 if (!can_be_false) return Word32Type::Constant(1);
975 return Word32Type::Set({0, 1}, zone);
976 }
977
978 static Type LessThanOrEqual(const type_t& lhs, const type_t& rhs,
979 Zone* zone) {
980 bool can_be_true = false;
981 bool can_be_false = false;
982 if (lhs.is_only_special_values()) {
983 if (lhs.has_minus_zero()) {
984 can_be_true = (!rhs.is_only_special_values() && rhs.max() >= 0.0) ||
985 rhs.has_minus_zero();
986 can_be_false = rhs.min() < 0.0;
987 } else {
988 DCHECK(lhs.is_only_nan());
989 }
990 } else if (rhs.is_only_special_values()) {
991 if (rhs.has_minus_zero()) {
992 can_be_true = (!lhs.is_only_special_values() && lhs.min() <= 0.0) ||
993 lhs.has_minus_zero();
994 can_be_false = lhs.max() > 0.0;
995 } else {
996 DCHECK(rhs.is_only_nan());
997 }
998 } else {
999 // Both sides have at least one non-special value. We don't have to treat
1000 // special values here, because nan has been taken care of already and
1001 // -0.0 is included in min/max.
1002 can_be_true = can_be_true || lhs.min() <= rhs.max();
1003 can_be_false = can_be_false || lhs.max() > rhs.min();
1004 }
1005
1006 // Consider NaN.
1007 can_be_false = can_be_false || lhs.has_nan() || rhs.has_nan();
1008
1009 if (!can_be_true) return Word32Type::Constant(0);
1010 if (!can_be_false) return Word32Type::Constant(1);
1011 return Word32Type::Set({0, 1}, zone);
1012 }
1013
1015 const type_t& rhs, Zone* zone) {
1016 bool can_be_true = lhs.unsigned_min() <= rhs.unsigned_max();
1017 bool can_be_false = lhs.unsigned_max() > rhs.unsigned_min();
1018
1019 if (!can_be_true) return Word32Type::Constant(0);
1020 if (!can_be_false) return Word32Type::Constant(1);
1021 return Word32Type::Set({0, 1}, zone);
1022 }
1023
1024 // Computes the ranges to which the sides of the comparison (lhs < rhs) can be
1025 // restricted when the comparison is true. When the comparison is true, we
1026 // learn: lhs cannot be >= rhs.max and rhs cannot be <= lhs.min and neither
1027 // can be NaN.
1028 static std::pair<Type, Type> RestrictionForLessThan_True(const type_t& lhs,
1029 const type_t& rhs,
1030 Zone* zone) {
1031 // If either side is only NaN, this comparison can never be true.
1032 if (lhs.is_only_nan() || rhs.is_only_nan()) {
1033 return {Type::None(), Type::None()};
1034 }
1035
1036 Type restrict_lhs;
1037 if (rhs.max() == -inf) {
1038 // There is no value for lhs that could make (lhs < -inf) true.
1039 restrict_lhs = Type::None();
1040 } else {
1041 const auto max = next_smaller(rhs.max());
1042 uint32_t sv = max >= 0 ? type_t::kMinusZero : type_t::kNoSpecialValues;
1043 restrict_lhs = type_t::Range(-inf, max, sv, zone);
1044 }
1045
1046 Type restrict_rhs;
1047 if (lhs.min() == inf) {
1048 // There is no value for rhs that could make (inf < rhs) true.
1049 restrict_rhs = Type::None();
1050 } else {
1051 const auto min = next_larger(lhs.min());
1052 uint32_t sv = min <= 0 ? type_t::kMinusZero : type_t::kNoSpecialValues;
1053 restrict_rhs = type_t::Range(min, inf, sv, zone);
1054 }
1055
1056 return {restrict_lhs, restrict_rhs};
1057 }
1058
1059 // Computes the ranges to which the sides of the comparison (lhs < rhs) can be
1060 // restricted when the comparison is false. When the comparison is false, we
1061 // learn: lhs cannot be < rhs.min and rhs cannot be > lhs.max.
1062 static std::pair<Type, Type> RestrictionForLessThan_False(const type_t& lhs,
1063 const type_t& rhs,
1064 Zone* zone) {
1065 Type restrict_lhs;
1066 if (rhs.has_nan()) {
1067 restrict_lhs = type_t::Any();
1068 } else {
1069 uint32_t lhs_sv =
1070 type_t::kNaN |
1072 restrict_lhs = type_t::Range(rhs.min(), inf, lhs_sv, zone);
1073 }
1074
1075 Type restrict_rhs;
1076 if (lhs.has_nan()) {
1077 restrict_rhs = type_t::Any();
1078 } else {
1079 uint32_t rhs_sv =
1080 type_t::kNaN |
1082 restrict_rhs = type_t::Range(-inf, lhs.max(), rhs_sv, zone);
1083 }
1084
1085 return {restrict_lhs, restrict_rhs};
1086 }
1087
1088 // Computes the ranges to which the sides of the comparison (lhs <= rhs) can
1089 // be restricted when the comparison is true. When the comparison is true, we
1090 // learn: lhs cannot be > rhs.max and rhs cannot be < lhs.min and neither can
1091 // be NaN.
1092 static std::pair<Type, Type> RestrictionForLessThanOrEqual_True(
1093 const type_t& lhs, const type_t& rhs, Zone* zone) {
1094 // If either side is only NaN, this comparison can never be true.
1095 if (lhs.is_only_nan() || rhs.is_only_nan()) {
1096 return {Type::None(), Type::None()};
1097 }
1098
1099 uint32_t lhs_sv =
1101 uint32_t rhs_sv =
1103 return {type_t::Range(-inf, rhs.max(), lhs_sv, zone),
1104 type_t::Range(lhs.min(), inf, rhs_sv, zone)};
1105 }
1106
1107 // Computes the ranges to which the sides of the comparison (lhs <= rhs) can
1108 // be restricted when the comparison is false. When the comparison is false,
1109 // we learn: lhs cannot be <= rhs.min and rhs cannot be >= lhs.max.
1110 static std::pair<Type, Type> RestrictionForLessThanOrEqual_False(
1111 const type_t& lhs, const type_t& rhs, Zone* zone) {
1112 Type restrict_lhs;
1113 if (rhs.has_nan()) {
1114 restrict_lhs = type_t::Any();
1115 } else if (rhs.min() == inf) {
1116 // The only value for lhs that could make (lhs <= inf) false is NaN.
1117 restrict_lhs = type_t::NaN();
1118 } else {
1119 const auto min = next_larger(rhs.min());
1120 uint32_t sv = type_t::kNaN |
1122 restrict_lhs = type_t::Range(min, inf, sv, zone);
1123 }
1124
1125 Type restrict_rhs;
1126 if (lhs.has_nan()) {
1127 restrict_rhs = type_t::Any();
1128 } else if (lhs.max() == -inf) {
1129 // The only value for rhs that could make (-inf <= rhs) false is NaN.
1130 restrict_rhs = type_t::NaN();
1131 } else {
1132 const auto max = next_smaller(lhs.max());
1133 uint32_t sv = type_t::kNaN |
1135 restrict_rhs = type_t::Range(-inf, max, sv, zone);
1136 }
1137
1138 return {restrict_lhs, restrict_rhs};
1139 }
1140};
1141
1142class Typer {
1143 public:
1145 switch (rep.value()) {
1147 return Word32Type::Any();
1149 return Word64Type::Any();
1151 return Float32Type::Any();
1153 return Float64Type::Any();
1154
1159 // TODO(nicohartmann@): Support these representations.
1160 return Type::Any();
1161 }
1162 }
1163
1166 DCHECK_LT(0, reps.size());
1167 if (reps.size() == 1) return TypeForRepresentation(reps[0]);
1168 base::SmallVector<Type, 4> tuple_types;
1169 for (auto rep : reps) tuple_types.push_back(TypeForRepresentation(rep));
1170 return TupleType::Tuple(base::VectorOf(tuple_types), zone);
1171 }
1172
1174 switch (kind) {
1176 if (value.float32.is_nan()) return Float32Type::NaN();
1177 if (IsMinusZero(value.float32.get_scalar()))
1178 return Float32Type::MinusZero();
1179 return Float32Type::Constant(value.float32.get_scalar());
1181 if (value.float64.is_nan()) return Float64Type::NaN();
1182 if (IsMinusZero(value.float64.get_scalar()))
1183 return Float64Type::MinusZero();
1184 return Float64Type::Constant(value.float64.get_scalar());
1186 return Word32Type::Constant(static_cast<uint32_t>(value.integral));
1188 return Word64Type::Constant(static_cast<uint64_t>(value.integral));
1189 default:
1190 // TODO(nicohartmann@): Support remaining {kind}s.
1191 return Type::Any();
1192 }
1193 }
1194
1195 static Type TypeProjection(const Type& input, uint16_t idx) {
1196 if (input.IsNone()) return Type::None();
1197 if (!input.IsTuple()) return Type::Any();
1198 const TupleType& tuple = input.AsTuple();
1199 DCHECK_LT(idx, tuple.size());
1200 return tuple.element(idx);
1201 }
1202
1203 static Type TypeWordBinop(Type left_type, Type right_type,
1205 Zone* zone) {
1206 DCHECK(!left_type.IsInvalid());
1207 DCHECK(!right_type.IsInvalid());
1208
1209 if (rep == WordRepresentation::Word32()) {
1210 switch (kind) {
1212 return TypeWord32Add(left_type, right_type, zone);
1214 return TypeWord32Sub(left_type, right_type, zone);
1215 default:
1216 // TODO(nicohartmann@): Support remaining {kind}s.
1217 return Word32Type::Any();
1218 }
1219 } else {
1221 switch (kind) {
1223 return TypeWord64Add(left_type, right_type, zone);
1225 return TypeWord64Sub(left_type, right_type, zone);
1226 default:
1227 // TODO(nicohartmann@): Support remaining {kind}s.
1228 return Word64Type::Any();
1229 }
1230 }
1231 }
1232
1233 static Type TypeWord32Add(const Type& lhs, const Type& rhs, Zone* zone) {
1234 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1235 auto l = TruncateWord32Input(lhs, true, zone);
1236 auto r = TruncateWord32Input(rhs, true, zone);
1237 return WordOperationTyper<32>::Add(l, r, zone);
1238 }
1239
1240 static Type TypeWord32Sub(const Type& lhs, const Type& rhs, Zone* zone) {
1241 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1242 auto l = TruncateWord32Input(lhs, true, zone);
1243 auto r = TruncateWord32Input(rhs, true, zone);
1244 return WordOperationTyper<32>::Subtract(l, r, zone);
1245 }
1246
1247 static Type TypeWord64Add(const Type& lhs, const Type& rhs, Zone* zone) {
1248 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1249 if (!InputIs(lhs, Type::Kind::kWord64) ||
1251 return Word64Type::Any();
1252 }
1253 const auto& l = lhs.AsWord64();
1254 const auto& r = rhs.AsWord64();
1255
1256 return WordOperationTyper<64>::Add(l, r, zone);
1257 }
1258
1259 static Type TypeWord64Sub(const Type& lhs, const Type& rhs, Zone* zone) {
1260 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1261 if (!InputIs(lhs, Type::Kind::kWord64) ||
1263 return Word64Type::Any();
1264 }
1265
1266 const auto& l = lhs.AsWord64();
1267 const auto& r = rhs.AsWord64();
1268
1269 return WordOperationTyper<64>::Subtract(l, r, zone);
1270 }
1271
1272 static Type TypeFloatBinop(Type left_type, Type right_type,
1274 Zone* zone) {
1275 DCHECK(!left_type.IsInvalid());
1276 DCHECK(!right_type.IsInvalid());
1277
1278#define FLOAT_BINOP(op, bits) \
1279 case FloatBinopOp::Kind::k##op: \
1280 return TypeFloat##bits##op(left_type, right_type, zone);
1281
1282 if (rep == FloatRepresentation::Float32()) {
1283 switch (kind) {
1284 FLOAT_BINOP(Add, 32)
1285 FLOAT_BINOP(Sub, 32)
1286 FLOAT_BINOP(Mul, 32)
1287 FLOAT_BINOP(Div, 32)
1288 FLOAT_BINOP(Mod, 32)
1289 FLOAT_BINOP(Min, 32)
1290 FLOAT_BINOP(Max, 32)
1291 FLOAT_BINOP(Power, 32)
1292 FLOAT_BINOP(Atan2, 32)
1293 }
1294 } else {
1296 switch (kind) {
1297 FLOAT_BINOP(Add, 64)
1298 FLOAT_BINOP(Sub, 64)
1299 FLOAT_BINOP(Mul, 64)
1300 FLOAT_BINOP(Div, 64)
1301 FLOAT_BINOP(Mod, 64)
1302 FLOAT_BINOP(Min, 64)
1303 FLOAT_BINOP(Max, 64)
1304 FLOAT_BINOP(Power, 64)
1305 FLOAT_BINOP(Atan2, 64)
1306 }
1307 }
1308
1309#undef FLOAT_BINOP
1310 }
1311
1312#define FLOAT_BINOP(op, bits, float_typer_handler) \
1313 static Type TypeFloat##bits##op(const Type& lhs, const Type& rhs, \
1314 Zone* zone) { \
1315 if (lhs.IsNone() || rhs.IsNone()) return Type::None(); \
1316 if (!InputIs(lhs, Type::Kind::kFloat##bits) || \
1317 !InputIs(rhs, Type::Kind::kFloat##bits)) { \
1318 return Float##bits##Type::Any(); \
1319 } \
1320 const auto& l = lhs.AsFloat##bits(); \
1321 const auto& r = rhs.AsFloat##bits(); \
1322 return FloatOperationTyper<bits>::float_typer_handler(l, r, zone); \
1323 }
1324
1325 // Float32 operations
1326 FLOAT_BINOP(Add, 32, Add)
1327 FLOAT_BINOP(Sub, 32, Subtract)
1328 FLOAT_BINOP(Mul, 32, Multiply)
1329 FLOAT_BINOP(Div, 32, Divide)
1330 FLOAT_BINOP(Mod, 32, Modulus)
1331 FLOAT_BINOP(Min, 32, Min)
1332 FLOAT_BINOP(Max, 32, Max)
1333 FLOAT_BINOP(Power, 32, Power)
1334 FLOAT_BINOP(Atan2, 32, Atan2)
1335 // Float64 operations
1336 FLOAT_BINOP(Add, 64, Add)
1337 FLOAT_BINOP(Sub, 64, Subtract)
1338 FLOAT_BINOP(Mul, 64, Multiply)
1339 FLOAT_BINOP(Div, 64, Divide)
1340 FLOAT_BINOP(Mod, 64, Modulus)
1341 FLOAT_BINOP(Min, 64, Min)
1342 FLOAT_BINOP(Max, 64, Max)
1343 FLOAT_BINOP(Power, 64, Power)
1344 FLOAT_BINOP(Atan2, 64, Atan2)
1345#undef FLOAT_BINOP
1346
1347 static Type TypeOverflowCheckedBinop(const Type& left_type,
1348 const Type& right_type,
1350 WordRepresentation rep, Zone* zone) {
1351 DCHECK(!left_type.IsInvalid());
1352 DCHECK(!right_type.IsInvalid());
1353
1354 if (rep == WordRepresentation::Word32()) {
1355 switch (kind) {
1357 return TypeWord32OverflowCheckedAdd(left_type, right_type, zone);
1360 // TODO(nicohartmann@): Support these.
1362 Word32Type::Set({0, 1}, zone), zone);
1363 }
1364 } else {
1366 switch (kind) {
1370 // TODO(nicohartmann@): Support these.
1372 Word32Type::Set({0, 1}, zone), zone);
1373 }
1374 }
1375 }
1376
1377 static Type TypeWord32OverflowCheckedAdd(const Type& lhs, const Type& rhs,
1378 Zone* zone) {
1379 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1380 auto l = TruncateWord32Input(lhs, true, zone);
1381 auto r = TruncateWord32Input(rhs, true, zone);
1382
1383 auto value = WordOperationTyper<32>::Add(l, r, zone);
1384 // We check for signed overflow and if the topmost bits of both opperands
1385 // are 0, we know that the result cannot overflow.
1386 if ((0xC0000000 & l.unsigned_max()) == 0 &&
1387 (0xC0000000 & r.unsigned_max()) == 0) {
1388 // Cannot overflow.
1389 return TupleType::Tuple(value, Word32Type::Constant(0), zone);
1390 }
1391 // Special case for two constant inputs to figure out the overflow.
1392 if (l.is_constant() && r.is_constant()) {
1393 constexpr uint32_t msb_mask = 0x80000000;
1394 DCHECK(value.is_constant());
1395 uint32_t l_msb = (*l.try_get_constant()) & msb_mask;
1396 uint32_t r_msb = (*r.try_get_constant()) & msb_mask;
1397 if (l_msb != r_msb) {
1398 // Different sign bits can never lead to an overflow.
1399 return TupleType::Tuple(value, Word32Type::Constant(0), zone);
1400 }
1401 uint32_t value_msb = (*value.try_get_constant()) & msb_mask;
1402 const uint32_t overflow = value_msb == l_msb ? 0 : 1;
1403 return TupleType::Tuple(value, Word32Type::Constant(overflow), zone);
1404 }
1405 // Otherwise we accept some imprecision.
1406 return TupleType::Tuple(value, Word32Type::Set({0, 1}, zone), zone);
1407 }
1408
1409 static Type TypeComparison(const Type& lhs, const Type& rhs,
1411 ComparisonOp::Kind kind, Zone* zone) {
1412 switch (rep.value()) {
1414 return TypeWord32Comparison(lhs, rhs, kind, zone);
1416 return TypeWord64Comparison(lhs, rhs, kind, zone);
1418 return TypeFloat32Comparison(lhs, rhs, kind, zone);
1420 return TypeFloat64Comparison(lhs, rhs, kind, zone);
1425 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1426 // TODO(nicohartmann@): Support those cases.
1427 return Word32Type::Set({0, 1}, zone);
1428 }
1429 }
1430
1431 static Type TypeWord32Comparison(const Type& lhs, const Type& rhs,
1432 ComparisonOp::Kind kind, Zone* zone) {
1433 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1434 auto l = TruncateWord32Input(lhs, true, zone);
1435 auto r = TruncateWord32Input(rhs, true, zone);
1436 switch (kind) {
1440 // TODO(nicohartmann@): Support this.
1441 return Word32Type::Set({0, 1}, zone);
1446 }
1447 UNREACHABLE();
1448 }
1449
1450 static Type TypeWord64Comparison(const Type& lhs, const Type& rhs,
1451 ComparisonOp::Kind kind, Zone* zone) {
1452 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1453 switch (kind) {
1457 // TODO(nicohartmann@): Support this.
1458 return Word32Type::Set({0, 1}, zone);
1461 rhs.AsWord64(), zone);
1464 lhs.AsWord64(), rhs.AsWord64(), zone);
1465 }
1466 UNREACHABLE();
1467 }
1468
1469 static Type TypeFloat32Comparison(const Type& lhs, const Type& rhs,
1470 ComparisonOp::Kind kind, Zone* zone) {
1471 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1472 switch (kind) {
1474 // TODO(nicohartmann@): Support this.
1475 return Word32Type::Set({0, 1}, zone);
1478 rhs.AsFloat32(), zone);
1481 rhs.AsFloat32(), zone);
1484 UNREACHABLE();
1485 }
1486 }
1487
1488 static Type TypeFloat64Comparison(const Type& lhs, const Type& rhs,
1489 ComparisonOp::Kind kind, Zone* zone) {
1490 if (lhs.IsNone() || rhs.IsNone()) return Type::None();
1491 switch (kind) {
1493 // TODO(nicohartmann@): Support this.
1494 return Word32Type::Set({0, 1}, zone);
1497 rhs.AsFloat64(), zone);
1500 rhs.AsFloat64(), zone);
1503 UNREACHABLE();
1504 }
1505 }
1506
1508 // We cannot infer much, but the lower bound of the word32 is also the lower
1509 // bound of the word64 type.
1510 if (t.is_wrapping()) return Word64Type::Any();
1511 return Word64Type::Range(static_cast<uint64_t>(t.unsigned_min()),
1512 std::numeric_limits<uint64_t>::max(), zone);
1513 }
1514
1516 bool implicit_word64_narrowing,
1517 Zone* zone) {
1518 DCHECK(!input.IsInvalid());
1519 DCHECK(!input.IsNone());
1520
1521 if (input.IsAny()) {
1522 if (allow_invalid_inputs()) return Word32Type::Any();
1523 } else if (input.IsWord32()) {
1524 return input.AsWord32();
1525 } else if (input.IsWord64() && implicit_word64_narrowing) {
1526 // The input is implicitly converted to word32.
1527 const auto& w64 = input.AsWord64();
1528 if (w64.is_set()) {
1530 for (uint64_t e : w64.set_elements()) {
1531 elements.push_back(static_cast<uint32_t>(e));
1532 }
1533 return WordOperationTyper<32>::FromElements(std::move(elements), zone);
1534 }
1535
1536 if (w64.is_any() || w64.is_wrapping()) return Word32Type::Any();
1537
1538 if (w64.range_to() <= std::numeric_limits<uint32_t>::max()) {
1539 DCHECK_LE(w64.range_from(), std::numeric_limits<uint32_t>::max());
1540 return Word32Type::Range(static_cast<uint32_t>(w64.range_from()),
1541 static_cast<uint32_t>(w64.range_to()), zone);
1542 }
1543
1544 // TODO(nicohartmann@): Might compute a more precise range here.
1545 return Word32Type::Any();
1546 }
1547
1548 FATAL("Missing proper type for TruncateWord32Input. Type is: %s",
1549 input.ToString().c_str());
1550 }
1551
1553 public:
1554 // type_getter_t has to provide the type for a given input index.
1555 using type_getter_t = std::function<Type(OpIndex)>;
1556 // type_refiner_t is called with those arguments:
1557 // - OpIndex: index of the operation whose type is refined by the branch.
1558 // - Type: the refined type of the operation (after refinement, guaranteed
1559 // to be a subtype of the original type).
1560 using type_refiner_t = std::function<void(OpIndex, const Type&)>;
1561
1563 : type_getter_(type_getter), type_refiner_(type_refiner) {
1566 }
1567
1568 void RefineTypes(const Operation& condition, bool then_branch, Zone* zone);
1569
1570 private:
1571 template <bool allow_implicit_word64_truncation>
1572 Type RefineWord32Type(const Type& type, const Type& refinement,
1573 Zone* zone) {
1574 // If refinement is Type::None(), the operation/branch is unreachable.
1575 if (refinement.IsNone()) return Type::None();
1576 DCHECK(refinement.IsWord32());
1577 if constexpr (allow_implicit_word64_truncation) {
1578 // Turboshaft allows implicit trunction of Word64 values to Word32. When
1579 // an operation on Word32 representation computes a refinement type,
1580 // this is going to be a Type::Word32() even if the actual {type} was
1581 // Word64 before truncation. To correctly refine this type, we need to
1582 // extend the {refinement} to Word64 such that it reflects the
1583 // corresponding values in the original type (before truncation) before
1584 // we intersect.
1585 if (type.IsWord64()) {
1586 return Word64Type::Intersect(
1587 type.AsWord64(),
1588 Typer::ExtendWord32ToWord64(refinement.AsWord32(), zone),
1590 }
1591 }
1592 // We limit the values of {type} to those in {refinement}.
1593 return Word32Type::Intersect(type.AsWord32(), refinement.AsWord32(),
1595 zone);
1596 }
1597
1600 };
1601
1602 static bool InputIs(const Type& input, Type::Kind expected) {
1603 if (input.IsInvalid()) {
1604 if (allow_invalid_inputs()) return false;
1605 } else if (input.kind() == expected) {
1606 return true;
1607 } else if (input.IsAny()) {
1608 if (allow_invalid_inputs()) return false;
1609 }
1610
1611 std::stringstream s;
1612 s << expected;
1613 FATAL("Missing proper type (%s). Type is: %s", s.str().c_str(),
1614 input.ToString().c_str());
1615 }
1616
1617 // For now we allow invalid inputs (which will then just lead to very generic
1618 // typing). Once all operations are implemented, we are going to disable this.
1619 static bool allow_invalid_inputs() { return true; }
1620};
1621
1622} // namespace v8::internal::compiler::turboshaft
1623
1624#endif // V8_COMPILER_TURBOSHAFT_TYPER_H_
Builtins::Kind kind
Definition builtins.cc:40
void pop_back(size_t count=1)
size_t size() const
constexpr bool empty() const
Definition vector.h:73
constexpr size_t size() const
Definition vector.h:70
static constexpr FloatRepresentation Float32()
static constexpr FloatRepresentation Float64()
static FloatType OnlySpecialValues(uint32_t special_values)
Definition types.h:532
static FloatType Range(float_t min, float_t max, Zone *zone)
Definition types.h:551
static FloatType Constant(float_t constant)
Definition types.h:620
static FloatType Set(const base::SmallVector< const float_t, N > &elements, Zone *zone)
Definition types.h:566
static FloatType LeastUpperBound(const FloatType &lhs, const FloatType &rhs, Zone *zone)
Definition types.cc:538
static constexpr RegisterRepresentation Compressed()
static constexpr RegisterRepresentation Simd128()
static constexpr RegisterRepresentation Word32()
static constexpr RegisterRepresentation Float64()
static constexpr RegisterRepresentation Float32()
static constexpr RegisterRepresentation Simd256()
static constexpr RegisterRepresentation Word64()
static constexpr RegisterRepresentation Tagged()
static TupleType Tuple(const Type &element0, const Type &element1, Zone *zone)
Definition types.h:813
const Type & element(int index) const
Definition types.h:836
const TupleType & AsTuple() const
Definition types.h:880
const Word32Type & AsWord32() const
Definition types.h:860
const Float32Type & AsFloat32() const
Definition types.h:870
const Word64Type & AsWord64() const
Definition types.h:865
const Float64Type & AsFloat64() const
Definition types.h:875
void RefineTypes(const Operation &condition, bool then_branch, Zone *zone)
Definition typer.cc:9
std::function< void(OpIndex, const Type &)> type_refiner_t
Definition typer.h:1560
Type RefineWord32Type(const Type &type, const Type &refinement, Zone *zone)
Definition typer.h:1572
BranchRefinements(type_getter_t type_getter, type_refiner_t type_refiner)
Definition typer.h:1562
static Type TypeConstant(ConstantOp::Kind kind, ConstantOp::Storage value)
Definition typer.h:1173
static Type TypeWordBinop(Type left_type, Type right_type, WordBinopOp::Kind kind, WordRepresentation rep, Zone *zone)
Definition typer.h:1203
static Type TypeWord64Add(const Type &lhs, const Type &rhs, Zone *zone)
Definition typer.h:1247
static Type TypeOverflowCheckedBinop(const Type &left_type, const Type &right_type, OverflowCheckedBinopOp::Kind kind, WordRepresentation rep, Zone *zone)
Definition typer.h:1347
static Type TypeFloat64Comparison(const Type &lhs, const Type &rhs, ComparisonOp::Kind kind, Zone *zone)
Definition typer.h:1488
static Type TypeWord32Comparison(const Type &lhs, const Type &rhs, ComparisonOp::Kind kind, Zone *zone)
Definition typer.h:1431
static Type TypeProjection(const Type &input, uint16_t idx)
Definition typer.h:1195
static Type TypeWord32Add(const Type &lhs, const Type &rhs, Zone *zone)
Definition typer.h:1233
static Type TypeWord32OverflowCheckedAdd(const Type &lhs, const Type &rhs, Zone *zone)
Definition typer.h:1377
static Type TypeComparison(const Type &lhs, const Type &rhs, RegisterRepresentation rep, ComparisonOp::Kind kind, Zone *zone)
Definition typer.h:1409
static Type TypeFloat32Comparison(const Type &lhs, const Type &rhs, ComparisonOp::Kind kind, Zone *zone)
Definition typer.h:1469
static Type TypeWord64Sub(const Type &lhs, const Type &rhs, Zone *zone)
Definition typer.h:1259
static Type TypeForRepresentation(base::Vector< const RegisterRepresentation > reps, Zone *zone)
Definition typer.h:1164
static Type TypeForRepresentation(RegisterRepresentation rep)
Definition typer.h:1144
static Type TypeWord64Comparison(const Type &lhs, const Type &rhs, ComparisonOp::Kind kind, Zone *zone)
Definition typer.h:1450
static bool InputIs(const Type &input, Type::Kind expected)
Definition typer.h:1602
static Type TypeFloatBinop(Type left_type, Type right_type, FloatBinopOp::Kind kind, FloatRepresentation rep, Zone *zone)
Definition typer.h:1272
static Word64Type ExtendWord32ToWord64(const Word32Type &t, Zone *zone)
Definition typer.h:1507
static Type TypeWord32Sub(const Type &lhs, const Type &rhs, Zone *zone)
Definition typer.h:1240
static Word32Type TruncateWord32Input(const Type &input, bool implicit_word64_narrowing, Zone *zone)
Definition typer.h:1515
word_t set_element(int index) const
Definition types.h:438
static Type Intersect(const WordType &lhs, const WordType &rhs, ResolutionMode resolution_mode, Zone *zone)
Definition types.cc:344
static WordType Constant(word_t constant)
Definition types.h:409
std::pair< word_t, word_t > range() const
Definition types.h:430
static WordType Range(word_t from, word_t to, Zone *zone)
Definition types.h:342
static WordType Set(const base::SmallVector< word_t, N > &elements, Zone *zone)
Definition types.h:371
std::optional< TNode< JSArray > > a
ZoneVector< RpoNumber > & result
Point from
int x
Point to
int s
Definition mul-fft.cc:297
int r
Definition mul-fft.cc:298
constexpr Vector< T > VectorOf(T *start, size_t size)
Definition vector.h:360
size_t erase_if(C &container, const P &predicate)
bool all_of(const C &container, const P &predicate)
void sort(C &container)
bool is_unique_and_sorted(const T &container)
Definition types.h:55
T array_min(const std::array< T, N > &a)
Definition typer.h:22
typename detail::TypeForBits< Bits >::uint_type uint_type
Definition types.h:157
constexpr float_type< Bits > nan_v
Definition types.h:161
T array_max(const std::array< T, N > &a)
Definition typer.h:38
constexpr int N
static bool IsMinusZero(double value)
#define FATAL(...)
Definition logging.h:47
#define DCHECK_LE(v1, v2)
Definition logging.h:490
#define DCHECK_IMPLIES(v1, v2)
Definition logging.h:493
#define DCHECK_NE(v1, v2)
Definition logging.h:486
#define DCHECK(condition)
Definition logging.h:482
#define DCHECK_LT(v1, v2)
Definition logging.h:489
#define DCHECK_EQ(v1, v2)
Definition logging.h:485
#define DCHECK_GT(v1, v2)
Definition logging.h:487
std::conditional_t< Bits==32, float, double > float_t
Definition typer.h:408
static Type Modulus(type_t l, type_t r, Zone *zone)
Definition typer.h:777
static std::pair< Type, Type > RestrictionForLessThan_True(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:1028
static Type Min(type_t l, type_t r, Zone *zone)
Definition typer.h:829
static type_t Range(float_t min, float_t max, uint32_t special_values, Zone *zone)
Definition typer.h:413
static type_t Set(std::vector< float_t > elements, uint32_t special_values, Zone *zone)
Definition typer.h:424
static Type Power(const type_t &l, const type_t &r, Zone *zone)
Definition typer.h:899
static Type Add(type_t l, type_t r, Zone *zone)
Definition typer.h:507
static Type Subtract(type_t l, type_t r, Zone *zone)
Definition typer.h:561
static Type Divide(const type_t &l, const type_t &r, Zone *zone)
Definition typer.h:677
static std::pair< Type, Type > RestrictionForLessThanOrEqual_True(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:1092
static Type Atan2(const type_t &l, const type_t &r, Zone *zone)
Definition typer.h:940
static std::pair< Type, Type > RestrictionForLessThanOrEqual_False(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:1110
static Word32Type UnsignedLessThanOrEqual(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:1014
static Type ProductSet(const type_t &l, const type_t &r, uint32_t special_values, Zone *zone, std::function< float_t(float_t, float_t)> combine)
Definition typer.h:471
static Type LessThanOrEqual(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:978
static Type Max(type_t l, type_t r, Zone *zone)
Definition typer.h:864
static Type LessThan(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:945
static std::pair< Type, Type > RestrictionForLessThan_False(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:1062
static Type Multiply(type_t l, type_t r, Zone *zone)
Definition typer.h:613
static type_t Subtract(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:152
static type_t FromElements(ElementsVector elements, Zone *zone)
Definition typer.h:58
static std::pair< Type, Type > RestrictionForUnsignedLessThan_True(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:208
static bool is_wrapping(const std::pair< word_t, word_t > &range)
Definition typer.h:117
static Word32Type UnsignedLessThan(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:185
static Word32Type UnsignedLessThanOrEqual(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:195
static std::pair< Type, Type > RestrictionForUnsignedLessThanOrEqual_False(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:250
static std::pair< word_t, word_t > MakeRange(base::Vector< const word_t > elements)
Definition typer.h:88
static std::pair< Type, Type > RestrictionForUnsignedLessThanOrEqual_True(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:241
static type_t WidenMaximal(const type_t &old_type, const type_t &new_type, Zone *zone)
Definition typer.h:272
static word_t distance(word_t from, word_t to)
Definition typer.h:113
static bool is_wrapping(word_t from, word_t to)
Definition typer.h:120
static word_t distance(const std::pair< word_t, word_t > &range)
Definition typer.h:110
static type_t Add(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:122
static std::pair< Type, Type > RestrictionForUnsignedLessThan_False(const type_t &lhs, const type_t &rhs, Zone *zone)
Definition typer.h:232
static type_t WidenExponential(const type_t &old_type, type_t new_type, Zone *zone)
Definition typer.h:290
static std::pair< word_t, word_t > MakeRange(const type_t &t)
Definition typer.h:74
#define FLOAT_BINOP(op, bits)
Definition typer.h:1312
#define V8_UNLIKELY(condition)
Definition v8config.h:660
wasm::ValueType type