[C-prog-lang-l] low level view (assembly/ABI) of structure operations

Vladimír Kotal vlada at kotalovi.cz
Fri Apr 8 21:50:46 CEST 2022


Hi all,

in the last lecture I glossed over 2 topics regarding structures w.r.t. their implementation, specifically at the assembly and ABI level.

First, structure to structure copy:

the copy is shallow and the structures contiguous in (virtual) memory, therefore this can be done as a byte-to-byte copy. Of course, the compiler will try to generate code to facilitate this as fast as possible, depending on architecture and selected optimization level. For example on x86 CPUs that have the SSE instruction set (https://en.wikipedia.org/wiki/Streaming_SIMD_Extensions), the compiler will probably exploit the XMM 128-bit registers and associated instructions to speed up the copying process. (which may actually bring its own set of problems - https://gist.github.com/vladak/d7edf765fe2763b698b06488230d5886)

In some other cases, depending on compiler/arch specific thresholds, the compiler may even generate a call to the memcpy() library function to perform the copy (a practice I found interesting when I first learned about it). The memcpy() function implementation in the C library will probably switch to a heavily optimized variant for given arch/CPU.

The Compiler Explorer is really nice tool to see how it works for different compilers/architectures/CPUs. For example see https://godbolt.org/z/Pb5h5bE5v

Note that for this particular example, I used argv to initialize the structure contents, otherwise the compiler might optimize statically initialized pieces away and the structure to structure copy may not be done at all if the result can be inferred at compile time. Also, for this piece of code, the assembly emitted by different compilers on the same CPU might be radically different - compare gcc and clang. You can also try to see what happens in different optimization levels.


Second, passing structures by value:

Q: how exactly is a structure passed to a function if the parameter passing is by value ? 
 * on x64 with the Sys V ABI <https://en.wikipedia.org/wiki/X86_calling_conventions#System_V_AMD64_ABI> it generally depends on structure size. If it can fit into 2 (64-bit) registers, it will be passed via registers (could be 64-bit registers or `xmm` registers when the structure has 2 `doubles`s)
   * if it cannot fit 2 registers (say the structure contains 3 x `unsigned long long` values), the address of the structure will be passed via stack (the caller and callee have an agreeement where to find the address of the structure - this is a copy of the structure so changing it inside the function will not change the original structure)
   * the above means that structure arguments can change how function parameters are passed, e.g. if a structure is passed via 2 registers on x64 with SysV ABI, then only 4 other arguments can be passed via registers

You can try to see how it works with something like this:

#include <stdio.h>
#include <stdlib.h>

struct foo {
	int d;
	int n;
};

void
pass_struct(struct foo in)
{
	in.d = 64;

	printf("%llu %llu\n", in.d, in.n);

	in.d = 100;
}

int
main(int argc, char *argv[])
{
	printf("sizeof (struct foo) = %zu (0x%zx)\n",
	    sizeof (struct foo),
	    sizeof (struct foo));

	struct foo foo = { .d = atoi(argv[1]), .n = atoi(argv[2]) };
	pass_struct(foo);
}


You can then try changing the ints in the structure to say unsigned long or unsigned long long, then add a third unsigned long long member and see how the generated assembly changed. For this kind of examination I'd recommend compiling with -O0.

Again, the Compiler explorer provides nice insight however in this case stepping through instructions (stepi cmd in lldb) and dumping registers (register read REG in lldb) is actually better I'd say.

Similarly, returning a structure from a function (as opposed to returning a pointer to structure) can be examined.

I hope you find this kind of exercises interesting,

Best regards,


V. Kotal
-------------- next part --------------
HTML attachment scrubbed and removed


More information about the c-prog-lang-l mailing list