[llvm-bugs] [Bug 38108] New: Wrong optimizations and missed optimizations involving pointers to subobjects

via llvm-bugs llvm-bugs at lists.llvm.org
Mon Jul 9 09:39:10 PDT 2018


https://bugs.llvm.org/show_bug.cgi?id=38108

            Bug ID: 38108
           Summary: Wrong optimizations and missed optimizations involving
                    pointers to subobjects
           Product: clang
           Version: 6.0
          Hardware: PC
                OS: Linux
            Status: NEW
          Severity: normal
          Priority: P
         Component: LLVM Codegen
          Assignee: unassignedclangbugs at nondot.org
          Reporter: cuoq at trust-in-soft.com
                CC: llvm-bugs at lists.llvm.org

Please consider the functions below and the code generated by Clang 6.0.0 and
trunk according to Matt Godbolt's Compiler Explorer
(https://godbolt.org/g/Z4auAE ):

____
// C code
#include <string.h>
#include <stddef.h>

struct s {
    int a;
    char t[8];
    int b;
} s;

int r1, r2, r3, r4;
void f(char *);

void std_fun(int x, int z) {
  char *p = s.t;
  r1 = s.a;
  r2 = s.b;
  memset(p+x, 0, z);
  r3 = s.a;
  r4 = s.b;
}

void std_fun_twist(int x, int z) {
  char *p = (char*)&s + offsetof(struct s, t);
  r1 = s.a;
  r2 = s.b;
  memset(p+x, 0, z);
  r3 = s.a;
  r4 = s.b;
}

void custom_fun(void) {
  struct s s;
  char *p = s.t;
  r1 = s.a;
  r2 = s.b;
  f(p);
  r3 = s.a;
  r4 = s.b;
}

void signed_ptr_offset(int x) {
  char *p = s.t;
  r1 = s.a;
  r2 = s.b;
  p[x]=1;
  r3 = s.a;
  r4 = s.b;
}

void signed_ptr_offset_twist(int x) {
  char *p = (char*)&s + offsetof(struct s, t);
  r1 = s.a;
  r2 = s.b;
  p[x]=1;
  r3 = s.a;
  r4 = s.b;
}

void array(int x) {
  r1 = s.a;
  r2 = s.b;
  s.t[x]=1;
  r3 = s.a;
  r4 = s.b;
}
_______
Generated Assembly (Clang 6.0.0):

std_fun:                                # @std_fun
        pushq   %rbx
        movl    s(%rip), %ebx
        movl    %ebx, r1(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r2(%rip)
        movslq  %edi, %rax
        leaq    s+4(%rax), %rdi
        movslq  %esi, %rdx
        xorl    %esi, %esi
        callq   memset
        movl    %ebx, r3(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r4(%rip)
        popq    %rbx
        retq
std_fun_twist:                          # @std_fun_twist
        pushq   %rbx
        movl    s(%rip), %ebx
        movl    %ebx, r1(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r2(%rip)
        movslq  %edi, %rax
        leaq    s+4(%rax), %rdi
        movslq  %esi, %rdx
        xorl    %esi, %esi
        callq   memset
        movl    %ebx, r3(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r4(%rip)
        popq    %rbx
        retq
custom_fun:                             # @custom_fun
        subq    $24, %rsp
        leaq    12(%rsp), %rdi
        callq   f
        movl    8(%rsp), %eax
        movl    20(%rsp), %ecx
        movl    %eax, r3(%rip)
        movl    %ecx, r4(%rip)
        addq    $24, %rsp
        retq
signed_ptr_offset:                      # @signed_ptr_offset
        movl    s(%rip), %eax
        movl    %eax, r1(%rip)
        movl    s+12(%rip), %ecx
        movslq  %edi, %rdx
        movb    $1, s+4(%rdx)
        movl    %ecx, r2(%rip)
        movl    %eax, r3(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r4(%rip)
        retq
signed_ptr_offset_twist:                # @signed_ptr_offset_twist
        movl    s(%rip), %eax
        movl    %eax, r1(%rip)
        movl    s+12(%rip), %ecx
        movslq  %edi, %rdx
        movb    $1, s+4(%rdx)
        movl    %ecx, r2(%rip)
        movl    %eax, r3(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r4(%rip)
        retq
array:                                  # @array
        movl    s(%rip), %eax
        movl    %eax, r1(%rip)
        movl    s+12(%rip), %ecx
        movslq  %edi, %rdx
        movb    $1, s+4(%rdx)
        movl    %ecx, r2(%rip)
        movl    %eax, r3(%rip)
        movl    s+12(%rip), %eax
        movl    %eax, r4(%rip)
        retq
___________

The point of interest for each function is whether Clang generates code to
reload the value of the struct members a and b after the struct has been
accessed, the access being made from a pointer initially pointing to member t.
The values are to be stored in variables r3 and r4. A sequence such as the
following indicates that Clang considers that the value of the member a must be
the same after the call to memset as it was before:

        callq   memset
        movl    %ebx, r3(%rip)

Note that the variable p is always a pointer to char, and s is always a
variable, which means that the functions cannot be said to be incorrect because
of “strict aliasing” violations. Incidentally, adding "-fno-strict-aliasing" to
the commandline does not change the generated code.

1/ WRONG OPTIMIZATION

In the functions std_fun_twist and signed_ptr_offset_twist, r3 is assigned
without reloading the member a. I do not think that there exist any possible
justification for this optimization in a real compiler used for, for instance,
OS code. Some programmers will use char pointers to access the representation
of structs. The struct's representation is an array of bytes
(https://port70.net/~nsz/c/c11/n1570.html#6.2.6.1p2 ) and these functions are
only doing valid pointer arithmetics within this array.

2/ MISSED OPTIMIZATION

In the function named array, Clang reloads the member b to assign to variable
r4. I think it would be valid to treat any argument x to that function not
between 0 and 7 as causing undefined behavior, and therefore to reuse the value
loaded in %ecx. This is an optimization that GCC already does.

3/ BORDERLINE CASES

While it is difficult to infer intention from absence of optimization, I think
that GCC avoids optimizing the other functions on purpose, making a distinction
between direct array subscripting and use of an intermediate pointer that can
be interpreter as a pointer inside the entire struct. While the C standards do
not distinguish between the two, I think this is a valuable heuristic and I
hope that Clang will align to GCC's behavior in these cases. Regardless of the
decision, each example is either a wrong optimization or a missed optimization,
since Clang always reload the member b and assumes that the member a was not
modified.

-- 
You are receiving this mail because:
You are on the CC list for the bug.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-bugs/attachments/20180709/dd926c25/attachment.html>


More information about the llvm-bugs mailing list