GCC 7 - The importance of a cutting-edge compiler
16 May, 2017
by Victor Rodriguez Bahena
GCC 7* was released last May 2nd and the Clear Linux* Project already uses it as the default C compiler. This latest GCC* compiler version came with new features and performance optimizations including various improvements in the diagnostics, location ranges, suggestions for misspellings, and even hints for code fixes. The following examples showcase some cool features included in GCC 7.
Loop splitting optimization
The GCC 7 release notes [1] provide the following example: Take the following loop containing an always true condition on one side of the iteration space and an always false condition on the other:
for (i = 0; i < 100; i++) {
if (i < 50)
A;
Else
B;
}
In GCC 7, the option “fsplit-loops” splits this code into two loops. The -O3 optimization level or higher includes this feature by default. Each of the two new loops iterates on just one side of the iteration space and the condition does not need to be checked inside of the loop:
for (i = 0; i < 50; i++) {
A;
}
for (; i < 100; i++) {
B;
}
The author of this change on GCC provides example codes to validate the feature [2]. The first experiments show a minimal but relevant performance improvement [3] thanks to this change.
Code hoisting optimization
This feature was merged last year [4]. It helps with the partial redundancy elimination or PRE. PRE is a compiler optimization which eliminates unnecessary and redundant expressions on some paths through of the code. It primarily reduces the code size. However, it improves the speed of the generated code as well. This feature is enabled with the “-fcode-hoisting” flag, at the -O2 optimization level or higher, and on -Os. Good examples of this feature are described on its test suite[4]. Take this example from [5]:
int test (int a, int b, int c, int g) {
int d, e;
if (a)
d = b * c;
else
d = b - c;
e = b * c + g;
return d + e;
}
At the end of the compilation it should pick up and apply PRE/CSE only to the multiplication. A simple example would be:
# a = x + j + 10
# b = x+ j
After applying the CSE improvement the compiler arranges the code as follows:
# c = x+ y
# a = c+ 10
# b = c
The performance benefits of this code hoisting optimization will be seen in multiple kinds of applications where injured partial redundancies create bottlenecks.
Detect buffer overflow and invalid memory accesses
Let’s take for example the following code:
void f (int n) {
char *d;
if (n < 1025)
d = alloca (n);
else
d = malloc (n);
…
}
When compiled with the new flag -Walloca-larger-than=1024, GCC 7 shows this warning:
warning: argument to 'alloca may be too large due to conversion from 'int' to 'long unsigned int' [-Walloca-larger-than=]
At first sight, it is not clear why the compiler warns if alloca was called to allocate memory which is automatically freed. It was called for sizes of 1kb and less, which is the limit implicit in “-Walloca-larger-than”. However, since n is signed, a negative value would result in a call to the function well in excess of the limit. Thus, the warning is triggered.
Smarter fix-it hints
Two new options have been added for printing fix-it hints. Take, for example, the following hello world code:
#include<stdio.h>
int main(){
int color;
printf("Hello World\n");
printf("%d",colour);
return 0;
}
A simple compilation with the -fdiagnostics-parseable-fixits flag results in:
$ gcc hello.c -o hello -fdiagnostics-parseable-fixits
hello.c: In function ‘main’:
hello.c:6:17: error: ‘colour’ undeclared (first use in this function); did you mean ‘color’?
printf("%d",colour);
^~~~~~
color
fix-it:"hello.c":{6:17-6:23}:"color"
hello.c:6:17: note: each undeclared identifier is reported only once for each function it appears in
The -fdiagnostics-parseable-fixits flag shows the fix-it hints in a machine-readable form, suitable for consumption by IDEs. On the other hand, the -fdiagnostics-generate-patch flag prints a patch in the "unified" format after any diagnostics are shown:
hello.c: In function ‘main’:
hello.c:6:17: error: ‘colour’ undeclared (first use in this function); did you mean ‘color’?
printf("%d",colour);
^~~~~~
color
hello.c:6:17: note: each undeclared identifier is reported only once for each function it appears in
--- hello.c
+++ hello.c
@@ -3,6 +3,6 @@
int main(){
int color;
printf("Hello World\n");
- printf("%d",colour);
+ printf("%d",color);
return 0;
}
Conclusion
With the early adoption of GCC 7, the Clear Linux Project stays on the leading edge of adoption of new features and performance optimizations. This allows us to showcase the best of Intel® architecture technology and performance, from low-level kernel features to complex applications which span the entire operating system.
Read the full list of features on the official GCC release notes [1].
[1] https://gcc.gnu.org/gcc-7/changes.html
[2] https://github.com/gcc-mirror/gcc/blob/master/gcc/tree-ssa-loop-split.c
[3] https://gcc.gnu.org/ml/gcc-patches/2015-12/msg00138.html
[4]https://github.com/gcc-mirror/gcc/commit/ad010d47fc7bf4d598eb113c286c265b266094be