Skip to content

Commit 7836ed6

Browse files
authored
Add IU64 and Span classes, to compute spans over all integers (#9021)
IU64 is a single numeric representation for both signed and unsigned integers: it can contain as many negative values as a signed number can, but also as many unsigned as well (so it needs more than 64 bits). The Span class is a simple representation of contiguous Spans of numbers.
1 parent 8d546dc commit 7836ed6

5 files changed

Lines changed: 874 additions & 0 deletions

File tree

src/support/iu64.h

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
/*
2+
* Copyright 2026 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_support_i65_h
18+
#define wasm_support_i65_h
19+
20+
#include <cstdint>
21+
#include <iostream>
22+
#include <limits>
23+
24+
namespace wasm {
25+
26+
// An integer capable of representing numbers in the combined range of 32 and
27+
// 64-bit integers, both signed and unsigned. That is, in the range
28+
//
29+
// std::numeric_limits<int64_t>::min() .. std::numeric_limits<uint64_t>::max()
30+
//
31+
// This is basically an i64 combined with a u64 in terms of range, hence "IU64".
32+
struct IU64 {
33+
// A 64-bit payload with an extra 65th sign bit.
34+
uint64_t value = 0;
35+
bool negative = false;
36+
37+
constexpr IU64() = default;
38+
39+
// Unsigned values are simple.
40+
constexpr IU64(uint32_t x) : value(x) {}
41+
constexpr IU64(uint64_t x) : value(x) {}
42+
43+
// Signed values need to be checked for being negative.
44+
constexpr IU64(int32_t x) {
45+
if (x >= 0) {
46+
value = x;
47+
} else {
48+
negative = true;
49+
value = -int64_t(x);
50+
}
51+
}
52+
constexpr IU64(int64_t x) {
53+
if (x >= 0) {
54+
value = x;
55+
} else {
56+
negative = true;
57+
58+
// One does not simply negate MIN_INT64.
59+
if (x == std::numeric_limits<int64_t>::min()) {
60+
value = uint64_t(1) << 63;
61+
} else {
62+
value = -int64_t(x);
63+
}
64+
}
65+
}
66+
67+
constexpr bool operator==(const IU64& other) const {
68+
return value == other.value && negative == other.negative;
69+
}
70+
constexpr bool operator!=(const IU64& other) const {
71+
return !(*this == other);
72+
}
73+
74+
constexpr bool operator<(const IU64& other) const {
75+
if (negative) {
76+
if (other.negative) {
77+
// Both negative; we are smaller if absolute value is larger.
78+
return value > other.value;
79+
} else {
80+
// Only we are negative, so we are smaller.
81+
return true;
82+
}
83+
} else {
84+
if (other.negative) {
85+
// Only the other is negative, so we are larger.
86+
return false;
87+
} else {
88+
// Both positive; we are smaller if absolute value is smaller.
89+
return value < other.value;
90+
}
91+
}
92+
}
93+
constexpr bool operator<=(const IU64& other) const {
94+
return *this < other || *this == other;
95+
}
96+
constexpr bool operator>(const IU64& other) const {
97+
return !(*this <= other);
98+
}
99+
constexpr bool operator>=(const IU64& other) const {
100+
return !(*this < other);
101+
}
102+
};
103+
104+
inline std::ostream& operator<<(std::ostream& os, const IU64& x) {
105+
if (x.negative) {
106+
os << '-';
107+
}
108+
return os << x.value;
109+
}
110+
111+
} // namespace wasm
112+
113+
namespace std {
114+
115+
template<> class numeric_limits<wasm::IU64> {
116+
public:
117+
static constexpr bool is_specialized = true;
118+
static constexpr bool is_signed = true;
119+
static constexpr bool is_integer = true;
120+
static constexpr bool is_exact = true;
121+
static constexpr bool has_infinity = false;
122+
static constexpr bool has_quiet_NaN = false;
123+
static constexpr bool has_signaling_NaN = false;
124+
static constexpr float_denorm_style has_denorm = denorm_absent;
125+
static constexpr bool has_denorm_loss = false;
126+
static constexpr float_round_style round_style = round_toward_zero;
127+
static constexpr bool is_iec559 = false;
128+
static constexpr bool is_bounded = true;
129+
static constexpr bool is_modulo = false;
130+
static constexpr int digits = 65;
131+
static constexpr int digits10 = 19;
132+
static constexpr int max_digits10 = 0;
133+
static constexpr int radix = 2;
134+
static constexpr int min_exponent = 0;
135+
static constexpr int min_exponent10 = 0;
136+
static constexpr int max_exponent = 0;
137+
static constexpr int max_exponent10 = 0;
138+
static constexpr bool traps = false;
139+
static constexpr bool tinyness_before = false;
140+
141+
static constexpr wasm::IU64 min() noexcept {
142+
return wasm::IU64(std::numeric_limits<int64_t>::min());
143+
}
144+
static constexpr wasm::IU64 lowest() noexcept { return min(); }
145+
static constexpr wasm::IU64 max() noexcept {
146+
return wasm::IU64(std::numeric_limits<uint64_t>::max());
147+
}
148+
};
149+
150+
} // namespace std
151+
152+
#endif // wasm_support_i65_h

src/support/span.h

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/*
2+
* Copyright 2026 WebAssembly Community Group participants
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#ifndef wasm_support_span_h
18+
#define wasm_support_span_h
19+
20+
#include <algorithm>
21+
#include <cassert>
22+
#include <iostream>
23+
#include <limits>
24+
25+
namespace wasm {
26+
27+
// A span of values.
28+
//
29+
// Span{min, max} means [min, max], inclusive of both sides. To represent an
30+
// empty span, we use min > max.
31+
template<typename T> struct Span {
32+
static constexpr T Min = std::numeric_limits<T>::lowest();
33+
static constexpr T Max = std::numeric_limits<T>::max();
34+
35+
T min = Min;
36+
T max = Max;
37+
38+
constexpr Span() = default;
39+
constexpr Span(T min, T max) : min(min), max(max) {}
40+
41+
// Set a single value as possible.
42+
void set(T value) { min = max = value; }
43+
44+
// To represent an empty span, we use min > max, an impossible span.
45+
void setEmpty() {
46+
*this = empty();
47+
assert(isEmpty());
48+
}
49+
50+
bool isEmpty() const { return min > max; }
51+
52+
static Span<T> empty() { return Span{Max, Min}; }
53+
54+
void setFull() {
55+
*this = Span();
56+
assert(isFull());
57+
}
58+
59+
bool isFull() const { return min == Min && max == Max; }
60+
61+
static Span<T> full() { return Span{}; }
62+
63+
// Intersect this with another span, returning a (possibly empty) span.
64+
Span<T> intersection(const Span& other) const {
65+
if (isEmpty() || other.isEmpty()) {
66+
return empty();
67+
}
68+
return Span<T>{std::max(min, other.min), std::min(max, other.max)};
69+
}
70+
71+
// Checks whether two spans have any overlap at all.
72+
bool hasOverlap(const Span& other) const {
73+
return !intersection(other).isEmpty();
74+
}
75+
76+
// Check whether we contain another span (possibly being equal).
77+
bool contains(const Span& other) const {
78+
return intersection(other) == other;
79+
}
80+
81+
bool operator==(const Span& other) const {
82+
if (isEmpty()) {
83+
return other.isEmpty();
84+
}
85+
return !other.isEmpty() && min == other.min && max == other.max;
86+
}
87+
bool operator!=(const Span& other) const { return !(*this == other); }
88+
};
89+
90+
template<typename T>
91+
inline std::ostream& operator<<(std::ostream& os, const Span<T>& span) {
92+
if (span.isEmpty()) {
93+
return os << "[empty]";
94+
}
95+
return os << '[' << span.min << ", " << span.max << ']';
96+
}
97+
98+
} // namespace wasm
99+
100+
#endif // wasm_support_span_h

test/gtest/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ set(unittest_SOURCES
1717
disjoint_sets.cpp
1818
graph.cpp
1919
int128.cpp
20+
iu64.cpp
2021
leaves.cpp
2122
glbs.cpp
2223
inplace_vector.cpp
@@ -31,6 +32,7 @@ set(unittest_SOURCES
3132
printing.cpp
3233
public-type-validator.cpp
3334
scc.cpp
35+
span.cpp
3436
stringify.cpp
3537
subtype-exprs.cpp
3638
suffix_tree.cpp

0 commit comments

Comments
 (0)