[libc-commits] [libc] [libc] Add inet_ntop (PR #204143)
Pavel Labath via libc-commits
libc-commits at lists.llvm.org
Tue Jun 23 06:46:05 PDT 2026
================
@@ -61,5 +68,160 @@ cpp::optional<in_addr_t> inet_addr(const char *cp) {
return Endian::to_big_endian(result);
}
+namespace {
+
+size_t ipv4_num_bytes(cpp::span<const uint8_t> src) {
+ size_t result = 8; // four digits, three dots and '\0'
+ for (unsigned i = 0; i < 4; ++i)
+ result += (src[i] >= 10) + (src[i] >= 100);
+ return result;
+}
+
+size_t ipv4_to_str_unchecked(cpp::span<const uint8_t> src,
+ cpp::span<char> dst) {
+ size_t pos = 0;
+ for (unsigned i = 0; i < 4; ++i) {
+ uint8_t val = src[i];
+ if (val >= 100) {
+ uint8_t cent = val / 100;
+ val -= cent * 100;
+ uint8_t dec = val / 10;
+ dst[pos++] = '0' + cent;
+ dst[pos++] = '0' + dec;
+ dst[pos++] = '0' + val % 10;
+ } else if (val >= 10) {
+ uint8_t dec = val / 10;
+ dst[pos++] = '0' + dec;
+ dst[pos++] = '0' + val % 10;
+ } else {
+ dst[pos++] = '0' + val;
+ }
+ dst[pos++] = i < 3 ? '.' : '\0';
+ }
+ return pos;
+}
+
+size_t ipv6_to_str_unchecked(const struct in6_addr &src, cpp::span<char> dst) {
+ // Find the longest run of zeroes to compress to "::"
+ struct Run {
+ unsigned start = 0;
+ unsigned len = 0;
+ };
+ Run best, current;
+ for (unsigned i = 0; i < 8; ++i) {
+ uint16_t val = src.s6_addr16[i];
+ if (val == 0) {
+ ++current.len;
+ } else {
+ // In case of ties, the first sequence wins.
+ if (current.len > best.len)
+ best = current;
+ current = {i + 1, 0};
+ }
+ }
+ if (current.len > best.len)
+ best = current;
+
+ bool is_mapped =
+ best.start == 0 &&
+ (best.len == 6 || (best.len == 5 && src.s6_addr16[5] == 0xffff));
+ unsigned num_words = is_mapped ? 6 : 8;
+
+ char *pos = dst.data();
+ auto append_word = [&](unsigned i) {
+ uint16_t word = Endian::from_big_endian(src.s6_addr16[i]);
+ static constexpr char DIGITS[] = "0123456789abcdef";
----------------
labath wrote:
Unfortunately, this kills the performance of this function (going from 49 to 91 ns for a random ipv6).
>From what I can tell, the reason is that this lambda is not getting inlined anymore. The final generated code looks mostly the same. There's a small difference in that the in that the compiler generates two lookup tables (it uses a different one for the first character because zero is not a valid first digit), but I don't think that's the main reason for it. Going by the cost estimates, I think what happens is that the inliner runs before the pass which constructs the lookup tables, and so the inliner sees the big switch-case statement.
```
remark: '__llvm_libc_23_0_0_git::net::(anonymous namespace)::ipv6_to_str_unchecked(in6_addr const&, __llvm_libc_23_0_0_git::cpp::span<char>)::$_0::operator()(unsigned int) const' inlined into '__llvm_libc_23_0_0_git::net::(anonymous namespace)::ipv6_to_str_unchecked(in6_addr const&, __llvm_libc_23_0_0_git::cpp::span<char>)' with (cost=225, threshold=325) at callsite ipv6_to_str_unchecked:57:5; [-Rpass=inline]
remark: '__llvm_libc_23_0_0_git::net::(anonymous namespace)::ipv6_to_str_unchecked(in6_addr const&, __llvm_libc_23_0_0_git::cpp::span<char>)::$_0::operator()(unsigned int) const' not inlined into '__llvm_libc_23_0_0_git::net::(anonymous namespace)::ipv6_to_str_unchecked(in6_addr const&, __llvm_libc_23_0_0_git::cpp::span<char>)' because too costly to inline (cost=1105, threshold=325) [-Rpass-missed=inline]
```
Slapping `__attribute__((always_inline))` on the lambda (another complication: `[[gnu::always_inline]]` cannot be applied to lambdas, at least until c++23), regains the performance and *maybe* even makes it better (45ns).
I can also get the function to be inlined by reducing the number of int_to_b36_char calls (we go over the limit if there's more than two of them). Something like
```
uint16_t word = Endian::from_big_endian(src.s6_addr16[i]);
unsigned shift = (word >= 0x10) + (word >= 0x100) + (word >= 0x1000);
do {
*pos++ = internal::int_to_b36_char((word >> (shift * 4)) & 0xf);
} while (shift-- > 0);
```
does the trick, but gets only 61ns.
I think the best options are either leaving the code as-is, or going with int_to_b36_char+always_inline. Any preferences?
https://github.com/llvm/llvm-project/pull/204143
More information about the libc-commits
mailing list