Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a safe way to specify the value of an object may be uninitialized because it is never used?

Disclaimer: The following is a purely academic question; I keep this code at least 100 m away from any production system. The problem posed here is something that cannot be measured in any “real life” case.

Consider the following code (godbolt link):

#include <stdlib.h>

typedef int (*func_t)(int *ptr); // functions must conform to this interface

extern int uses_the_ptr(int *ptr);
extern int doesnt_use_the_ptr(int *ptr);

int foo() {
    // actual selection is complex, there are multiple functions,
    // but I know `func` will point to a function that doesn't use the argument
    func_t func = doesnt_use_the_ptr;

    int *unused_ptr_arg = NULL; // I pay a zeroing (e.g. `xor reg reg`) in every compiler
    int *unused_ptr_arg; // UB, gcc zeroes (thanks for saving me from myself, gcc), clang doesn't
    int *unused_ptr_arg __attribute__((__unused__)); // Neither zeroing, nor UB, this is what I want

    return (*func)(unused_ptr_arg);
}

The compiler has no reasonable way to know that unused_ptr_arg is unneeded (and so the zeroing is wasted time), but I do, so I want to inform the compiler that unused_ptr_arg may have any value, such as whatever happens to be in the register that would be used for passing it to func.

Is there a way to do this? I know I’m way outside the standard, so I’ll be fine with compiler-specific extensions (especially for gcc & clang).

like image 830
12345ieee Avatar asked Apr 25 '18 10:04

12345ieee


2 Answers

Using GCC/Clang `asm` Construct

In GCC and Clang, and other compilers that support GCC’s extended assembly syntax, you can do this:

int *unused_ptr_arg;
__asm__("" : "=x" (unused_ptr_arg));

return (*func)(unused_ptr_arg);

That __asm__ construct says “Here is some assembly code to insert into the program at this point. It writes a result to unused_ptr_arg in whatever location you choose for it.” (The x constraint means the compiler may choose memory, a processor register, or anything else the machine supports.) But the actual assembly code is empty (""). So no assembly code is generated, but the compiler believes that unused_ptr_arg has been initialized. In Clang 6.0.0 and GCC 7.3 (latest versions currently at Compiler Explorer) for x86-64, this generates a jmp with no xor.

Using Standard C

Consider this:

int *unused_ptr_arg;
(void) &unused_ptr_arg;

return (*func)(unused_ptr_arg);

The purpose of (void) &unused_ptr_arg; is to take the address of unused_ptr_arg, even though the address is not used. This disables the rule in C 2011 [N1570] 6.3.2.1 2 that says behavior is undefined if a program uses the value of an uninitialized object of automatic storage duration that could have been declared with register. Because its address is taken, it could not have been declared with register, and therefore using the value is no longer undefined behavior according to this rule.

In consequence, the object has an indeterminate value. Then there is an issue of whether pointers may have a trap representation. If pointers do not have trap representations in the C implementation being used, then no trap will occur due to merely referring to the value, as when passing it as an argument.

The result with Clang 6.0.0 at Compiler Explorer is a jmp instruction with no setting of the parameter register, even if -Wall -Werror is added to the compiler options. In contrast, if the (void) line is removed, a compiler error results.

like image 162
Eric Postpischil Avatar answered Oct 01 '22 22:10

Eric Postpischil


int *unused_ptr_arg = NULL;

This is what you should be doing. You don't pay for anything. Zeroing an int is a no-op. Ok technically it's not, but practically it is. You will never ever ever see the time of this operation in your program. And I don't mean that it's so small that you won't notice it. I mean that it's so small that so many other factors and operations that are order of magnitude longer will "swallow" it.

like image 23
bolov Avatar answered Oct 01 '22 20:10

bolov