GCC arguments passing and optimizations

Programming, for all ages and all languages.
Post Reply
LIC
Member
Member
Posts: 44
Joined: Mon Jun 04, 2018 8:10 am
Libera.chat IRC: lic

GCC arguments passing and optimizations

Post by LIC »

Hi, I have a problem with passing arguments to a function.

Here is the function:

Code: Select all

void test(uint32_t a, uint32_t b)
{
	uint32_t *arg = &a;

	for (uint32_t i = 0; i < 4; ++i) {
		printf("%h: %h\n", arg + i, *(arg + i));
	}
}
and the call to that function:

Code: Select all


test(0, 1);

This code is part of my kernel and the stack is setup at 0x20000 when the kernel is loaded. I ran this code with different levels of optimization and here are the outputs:


With O1 or no optimization:
0x0001ffe0: 0x00000000
0x0001ffe4: 0x00000001

Here the second argument is passed correctly.


With O2 or O3 optimization:
0x0001ffec: 0x00000000
0x0001fff0: 0x000025bc

Here the second argument is not passed and 0x25bc is just some random stuff.

It seems that with O2/O3 optimization GCC notices that I am not using the second argument in the function and just does not pass it. The reason I am using the pointer to access arguments is that I further would like to use a variable number of arguments.
Is there any kind of attribute to use on the function or another trick to force GCC to pass arguments even if there are not used? I would of course like to keep optimizations.

Regards
Octocontrabass
Member
Member
Posts: 5568
Joined: Mon Mar 25, 2013 7:01 pm

Re: GCC arguments passing and optimizations

Post by Octocontrabass »

Attempting to access one object from a pointer to another object is undefined behavior.

If you want to pass a variable number of arguments, you can use stdarg.h in your kernel.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: GCC arguments passing and optimizations

Post by nullplan »

To expand on Octo's answer: arg points to an array of only one element (namely a). Thus it is not permissible to calculate the pointer "arg + i" for i >= 2, and it is not permissible to dereference that pointer even for i >= 1. You are allowed to calculate the pointer to "one element past the end" of the array, but not to dereference it.

I also find it weird that you only get two lines of output. Should be four lines, according to the program. And "%h" is not a valid format string. I think you mean "%x".
Carpe diem!
LIC
Member
Member
Posts: 44
Joined: Mon Jun 04, 2018 8:10 am
Libera.chat IRC: lic

Re: GCC arguments passing and optimizations

Post by LIC »

Hi and thanks for your replies. I get indeed get 4 lines of output but I only wrote 2 in my post as the last two ones were just random numbers and the printf function I use is a printf function I coded myself, that's why I am using "%h" and not "%x".

How can I use a variable number of arguments without using any library then?

Here is what I did for my "printf" function and it works fine...

Code: Select all


extern void printf(const char *format, ...)
{
	uint32_t *curr_arg = (uint32_t *)&format + 1;

	while (*format) {
		// despecialization
		if (*format == '\\') {
			screen_putc(*(++format));
		}

		// special format
		else if (*format == '%') {
			switch (*(++format)) {
				case 'i':
				case 'd':
					printi(*curr_arg++);
					break;

				case 'h':
					printh(*curr_arg++);
					break;

				case 'c':
					screen_putc(*(char *)(curr_arg++));
					break;

				case 's':
					print(*(char **)(curr_arg++));
					break;

				case 'k':
					screen_set_attr(*(uchar_t *)(curr_arg++));
					break;

				default:
					screen_putc('?');
			}
		}

		// normal character
		else {
			screen_putc(*format);
		}

		format++;
	}
}

Why does it work with the printf function and not with my test function?

Regards
User avatar
iansjack
Member
Member
Posts: 4703
Joined: Sat Mar 31, 2012 3:07 am
Location: Chichester, UK

Re: GCC arguments passing and optimizations

Post by iansjack »

LIC wrote:How can I use a variable number of arguments without using any library then?
"stdarg.h" is a header file, not a library. Why wouldn't you want to use it?
LIC
Member
Member
Posts: 44
Joined: Mon Jun 04, 2018 8:10 am
Libera.chat IRC: lic

Re: GCC arguments passing and optimizations

Post by LIC »

Okay, I tried to understand stdarg.h, are variable arguments management built into the compiler?
Also why is my "printf" function working correctly?

Thanks for your replies
User avatar
Solar
Member
Member
Posts: 7615
Joined: Thu Nov 16, 2006 12:01 pm
Location: Germany
Contact:

Re: GCC arguments passing and optimizations

Post by Solar »

LIC wrote:Okay, I tried to understand stdarg.h, are variable arguments management built into the compiler?
For x86_64, it is not possible to implement <stdarg.h> functionality with pure C code. The first couple of parameters of a function are passed in registers. So you'll have to rely on the compiler to support you. G++ and clang do this via the following list of builtin functions:
  • __builtin_va_arg( ap, type )
  • __builtin_va_copy( dest, src )
  • __builtin_va_end( ap )
  • __builtin_va_start( ap, parmN )
and the typedef
  • __builtin_va_list
Besides, implementing a function "printf()" (i.e., a function with a name reserved by the standard) and then implementing non-standard behavior (%h) is a very bad idea. If you don't want to stick to the standard (why not?), then please use function names distinct from the ISO/IEC 9899 standard functions ("principle of least surprise").

And finally, have you considered adapting one of the existing standard libraries, instead of rolling your own? No offense intended, but the way you attempted to brute-force your variable argument list through undefined behavior makes me doubt you have the wherewithal to make a homegrown standard library fly. Actually, it's usually best to achieve quite some competence in user space programming before trying to tackle something like printf() (easily one of the most complex functions in the standard), or kernel-space programming...
Last edited by Solar on Tue Aug 20, 2019 7:52 am, edited 2 times in total.
Every good solution is obvious once you've found it.
nullplan
Member
Member
Posts: 1790
Joined: Wed Aug 30, 2017 8:24 am

Re: GCC arguments passing and optimizations

Post by nullplan »

LIC wrote:Here is what I did for my "printf" function and it works fine...

Code: Select all

 uint32_t *curr_arg = (uint32_t *)&format + 1;
Well, then you are very lucky indeed. That pointer is invalid. On AMD64, as well as most other archs, the arguments are passed in the registers, so the only way to have their address is for the compiler to spill them to stack. So the address will just be some random stuff somewhere on the stack. So just use va_arg(), please.

To answer your question, it worked because you were lucky, and the precise conditions applied that made it work. But it was fragile, and next compiler release might have already broken it. The likely difference between the working example here and the non-working one you posted first is the ellipsis.
Carpe diem!
LIC
Member
Member
Posts: 44
Joined: Mon Jun 04, 2018 8:10 am
Libera.chat IRC: lic

Re: GCC arguments passing and optimizations

Post by LIC »

Ok, thank you for your replies and advice!
Post Reply