[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