> When you pass this to a function, it does not fit in a single register.

Depends on the architecture. On amd64 (the architecture I'm typing this comment on), a 128-bit struct, like defined above, will fit in registers — yes, in 64-bit registers. The amd64 ABI[1] will split them across two registers. For example, if we take the above struct and use it,

void foo() {
ipv6_addr addr = { { 1, 2, 3, 4, 5, 6, 7, 8, }, };
bar(addr);
}

This compiles to:

movabsq	$578437695752307201, %rdi
xorl	%esi, %esi
jmp	bar

The nasty looking number is the initialization; it's easier to see in hex:

In [1]: hex(578437695752307201)
Out[1]: '0x807060504030201'

That ends up in %rdi, the first of the amd64 arg-passing registers. The next half is zeros, so it goes in %rsi (the compiler just zeros it). Then we "call" bar. (There is tail-call optimization here)

The non-optimized version is similar, but more drawn out:

# for some reason we zero it first
movq	$0, -16(%rbp)
movq	$0, -8(%rbp)
# then we init it again… okay gcc.
movb	$1, -16(%rbp)
movb	$2, -15(%rbp)
movb	$3, -14(%rbp)
movb	$4, -13(%rbp)
movb	$5, -12(%rbp)
movb	$6, -11(%rbp)
movb	$7, -10(%rbp)
movb	$8, -9(%rbp)
# move the struct into rdx/rax
movq	-16(%rbp), %rdx
movq	-8(%rbp), %rax
# just to move it into the arg-passing registers
movq	%rdx, %rdi
movq	%rax, %rsi
# call the function
call	bar

> It's much slower than IPv4 to deal with! 64 bit addresses wouldn't have had this problem and still would have enough space for everyone on earth.

It's twice as wide, yes, but that doesn't necessarily mean you're needing to pass it in memory.

> still would have enough space for everyone on earth.

IP addresses aren't just numbers assigned to machines. They must also — effectively — encode the route to that machine; you can sort of imagine it as a tree, with each bit giving you successively more local directions on the Internet. Now, of course, that's not exactly how it works, but the point is that some parts of that tree are going to be sparsely populated. Having a wider address space means you can make bigger, but not necessarily full, allocations in the address space, allowing you to keep whole networks together in a common prefixes, which results in simpler routing tables. (At least, that's the idea as I understand it.)

And it's not just 1 per person: I have more than one device, and I move between networks during the course of a day. If all of the networks I frequented actually had IPv6, I'd bog down at least 6–8 IPv6 addresses during the course of a boring day, and that's not counting stuff like networking equipment.

It's not just a simple question of "are there enough bits to assign each person an address"

> The equality operator doesn't work, nor do other logical operators.

On amd64 at least, __int128_t exists, I believe, and I think it should come with operators. In higher-level languages, you can define < and == simply enough.

Now, I grant that the above is highly architecture specific, and there definitely exist architectures where this doesn't apply. And those, yes, 128-bit will be slower than a theoretical 64-bit. I just don't think it's worth worrying about.

[1]: https://software.intel.com/sites/default/files/article/40212...