OSDev.org
https://forum.osdev.org/

GCC arguments passing and optimizations
https://forum.osdev.org/viewtopic.php?f=13&t=33830
Page 1 of 1

Author:  LIC [ Fri Aug 16, 2019 4:26 am ]
Post subject:  GCC arguments passing and optimizations

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

Here is the function:


Code:
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:

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

Author:  Octocontrabass [ Fri Aug 16, 2019 4:56 am ]
Post subject:  Re: GCC arguments passing and optimizations

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.

Author:  nullplan [ Fri Aug 16, 2019 9:23 am ]
Post subject:  Re: GCC arguments passing and optimizations

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".

Author:  LIC [ Sun Aug 18, 2019 4:24 am ]
Post subject:  Re: GCC arguments passing and optimizations

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:

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

Author:  iansjack [ Sun Aug 18, 2019 4:54 am ]
Post subject:  Re: GCC arguments passing and optimizations

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?

Author:  LIC [ Sun Aug 18, 2019 4:12 pm ]
Post subject:  Re: GCC arguments passing and optimizations

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

Author:  Solar [ Sun Aug 18, 2019 4:45 pm ]
Post subject:  Re: GCC arguments passing and optimizations

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...

Author:  nullplan [ Sun Aug 18, 2019 10:15 pm ]
Post subject:  Re: GCC arguments passing and optimizations

LIC wrote:
Here is what I did for my "printf" function and it works fine...
Code:
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.

Author:  LIC [ Tue Aug 20, 2019 3:29 am ]
Post subject:  Re: GCC arguments passing and optimizations

Ok, thank you for your replies and advice!

Page 1 of 1 All times are UTC - 6 hours
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group
http://www.phpbb.com/