Use -fno-unit-at-a-time if gcc supports it
PATCH 1/1 Use -fno-unit-at-a-time if gcc supports it
Hallo,
gcc 3.4 current has switched to default -fno-unit-at-a-time mode for -O2. The 3.3-Hammer branch compiler used in some distributions also does this.
Unfortunately the kernel doesn't compile with unit-at-a-time currently, it cannot tolerate the reordering of functions in relation to inline assembly.
This patch just turns it off when gcc supports the option.
I only did it for i386 for now. The problem is actually not i386 specific (other architectures break too), so it may make sense to move the check_gcc stuff into the main Makefile and do it for everybody.
-Andi
arch/i386/Makefile renamed from arch/i386/Makefile-o +4 -0
@@ -26,6 +26,10 @@ # prevent gcc from keeping the stack 16 byte aligned CFLAGS += $(call check_gcc,-mpreferred-stack-boundary=2,) +# gcc 3.4/3.3-hammer support -funit-at-a-time mode, but the Kernel is not ready +# for it yet +CFLAGS += $(call check_gcc,-fno-unit-at-a-time,) + align := $(subst -functions=0,,$(call check_gcc,-falign-functions=0,-malign-functions=0)) cflags-$(CONFIG_M386) += -march=i386
Quoted 7 lines · Andi Kleen
Hallo,
gcc 3.4 current has switched to default -fno-unit-at-a-time mode for -O2. The 3.3-Hammer branch compiler used in some distributions also does this.
Unfortunately the kernel doesn't compile with unit-at-a-time currently,
Did you mean -funit-at-a-time, rather than the converse?
Quoted 10 lines · Aaron Lehmann
Quoted 7 lines · Andi Kleen
Hallo,
gcc 3.4 current has switched to default -fno-unit-at-a-time mode for -O2. The 3.3-Hammer branch compiler used in some distributions also does this.
Unfortunately the kernel doesn't compile with unit-at-a-time currently,
Did you mean -funit-at-a-time, rather than the converse?
Yep, sorry for the confusion.
It defaults to -funit-at-a-time now, but the kernel must use
-fno-unit-at-a-time
-Andi
Quoted 9 lines
Hallo,
gcc 3.4 current has switched to default -fno-unit-at-a-time mode for -O2. The 3.3-Hammer branch compiler used in some distributions also does this.
Unfortunately the kernel doesn't compile with unit-at-a-time currently, it cannot tolerate the reordering of functions in relation to inline assembly.
How much work would be to fix kernel in this regard?
Are there some cases where this is esential? Kernel would be nice target to whole program optimization and GCC is not that far from it right now.
Honza
Quoted 22 lines
This patch just turns it off when gcc supports the option.
I only did it for i386 for now. The problem is actually not i386 specific (other architectures break too), so it may make sense to move the
check_gccstuff into the mainMakefileand do it for everybody.(quoted diff) +0
-1-Andiarch/i386/Makefile renamed from arch/i386/Makefile-o +4
-0@@ -26,6 +26,10 @@ # prevent gcc from keeping the stack 16 byte aligned CFLAGS += $(call check_gcc,-mpreferred-stack-boundary=2,) +# gcc 3.4/3.3-hammer support -funit-at-a-time mode, but the Kernel is not ready +# for it yet +CFLAGS += $(call check_gcc,-fno-unit-at-a-time,) + align := $(subst -functions=0,,$(call check_gcc,-falign-functions=0,-malign-functions=0)) cflags-$(CONFIG_M386) += -march=i386
Quoted 4 lines · Andi Kleen
Unfortunately the kernel doesn't compile with unit-at-a-time currently, it cannot tolerate the reordering of functions in relation to inline assembly.
What is the problem exactly? Is it the exception table getting unordered? We _could_ just sort it at boot-time (or, even better, at build time after the final link) instead...
Linus
Quoted 9 lines · Linus Torvalds
Quoted 4 lines · Andi Kleen
Unfortunately the kernel doesn't compile with unit-at-a-time currently, it cannot tolerate the reordering of functions in relation to inline assembly.
What is the problem exactly? Is it the exception table getting unordered? We _could_ just sort it at boot-time (or, even better, at build time after the final link) instead...
The problem is that unit-at-a-time sees all functions used and finds some static functions/variables that are not called anywhere and therefore drops them, making a smaller binary. Since GCC does not look into inline assembler, anything referenced from inline assembler only, will be treated as not used and therefore removed.
You have to options:
- use attribute ((used)) (implemented since GCC 3.2) to tell GCC that a function/variable should never be removed
- use -fno-unit-at-a-time.
Since unit-at-a-time has better inlining heuristics the better way is to add the used attribute - but that takes some time. The short-term solution would be to add the compiler flag,
Andreas
Signature · 3 lines
Andreas Jaeger, [email protected], http://www.suse.de/~aj SuSE Linux AG, Deutschherrnstr. 15-19, 90429 Nürnberg, Germany GPG fingerprint = 93A3 365E CE47 B889 DF7F FED1 389A 563C C272 A126
Quoted 3 lines · Andreas Jaeger
Since unit-at-a-time has better inlining heuristics the better way is to add the used attribute - but that takes some time. The short-term solution would be to add the compiler flag,
Won't we get a linker error if a static symbol is used but optimized-away? It shouldn't be hard to fix the n linker errors that crop up.
And why are we using static symbols in inline assembly outside of the compilation scope?
Anyhow, if it generates an error, this isn't hard to fix.
Here is the start...
Robert Love
include/linux/compiler.h +13 -0
@@ -74,6 +74,19 @@#define __attribute_pure__/* unimplemented */ #endif +/* +* As of gcc 3.2, we can mark a function as 'used' and gcc will assume that, +* even if it does not find a reference to it in any compilation unit. We +* need this for gcc 3.4 and beyond, which can optimize on a program-wide +* scope, and not just one file at a time, to avoid static symbols being +* discarded. +*/ +#if (__GNUC__ ==3 && __GNUC_MINOR__ >1 ) || __GNUC__ >3 +#define __attribute_used__ __attribute__((used)) +#else +#define __attribute_used__/* unimplemented */ +#endif +/* This macro obfuscates arithmetic on a variable address so that gcc shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE (ptr, off) \
Quoted 10 lines · Robert Love
Quoted 3 lines · Andreas Jaeger
Since unit-at-a-time has better inlining heuristics the better way is to add the used attribute - but that takes some time. The short-term solution would be to add the compiler flag,
Won't we get a linker error if a static symbol is used but optimized-away? It shouldn't be hard to fix the n linker errors that crop up.
Yes, we would get a linker error.
Quoted 2 lines
And why are we using static symbols in inline assembly outside of the compilation scope?
Don't know.
Quoted 1 line
Anyhow, if it generates an error, this isn't hard to fix.
Just lots of places...
Quoted 12 lines
Here is the start...
Robert Love
include/linux/compiler.h +1
-0@@ -74,6 +74,19 @@#define __attribute_pure__/* unimplemented */ #endif +/*
include/linux/compiler.h line 87
Quoted 10 lines
include/linux/compiler.h +10
-0+* As of gcc 3.2, we can mark a function as 'used' and gcc will assume that, +* even if it does not find a reference to it in any compilation unit. We +* need this for gcc 3.4 and beyond, which can optimize on a program-wide +* scope, and not just one file at a time, to avoid static symbols being +* discarded. +*/ +#if (__GNUC__ ==3 && __GNUC_MINOR__ >1 ) || __GNUC__ >3 +#define __attribute_used__ __attribute__((used)) +#else +#define __attribute_used__/* unimplemented */
In glibc we have for the else case:
# define __attribute_used__ __attribute__ ((__unused__))
This might reduce warnings about unused functions. But this change is not critical IMO, so your patch looks fine!
include/linux/compiler.h line 92
Quoted 5 lines
include/linux/compiler.h +2
-0+#endif +/* This macro obfuscates arithmetic on a variable address so that gcc shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE (ptr, off) \
Andreas
Signature · 3 lines
Andreas Jaeger, [email protected], http://www.suse.de/~aj SuSE Linux AG, Deutschherrnstr. 15-19, 90429 Nürnberg, Germany GPG fingerprint = 93A3 365E CE47 B889 DF7F FED1 389A 563C C272 A126
Quoted 18 lines · Andreas Jaeger
Quoted 9 lines · Linus Torvalds
Quoted 4 lines · Andi Kleen
Unfortunately the kernel doesn't compile with unit-at-a-time currently, it cannot tolerate the reordering of functions in relation to inline assembly.
What is the problem exactly? Is it the exception table getting unordered? We _could_ just sort it at boot-time (or, even better, at build time after the final link) instead...
The problem is that unit-at-a-time sees all functions used and finds some static functions/variables that are not called anywhere and therefore drops them, making a smaller binary. Since GCC does not look into inline assembler, anything referenced from inline assembler only, will be treated as not used and therefore removed.
Quoted 3 lines
You have to options:
- use attribute ((used)) (implemented since GCC 3.2) to tell GCC that a function/variable should never be removed
To be precise, implemented since GCC 3.2 for functions and since GCC 3.3 for variables.
Jakub
Quoted 1 line
How much work would be to fix kernel in this regard?
The big problem is that -funit-at-a-time is not widely used yet, so even if we fix the kernel at some point it would likely get broken again all the time by people who use older kernels (= most kernel developers currently)
Quoted 3 lines
Are there some cases where this is esential? Kernel would be nice target to whole program optimization and GCC is not that far from it right now.
I'm not sure that is that good an idea. When I was still hacking TCP I especially moved some stuff out-of-line in the fast path to avoid register pressure. Otherwise gcc would inline rarely used sub functions and completely mess up the register allocation in the fast path. Of course just a call alone messes up the registers somewhat because of its clobbers, but a full inlining is usually worse.
That was a long time ago, of course the code has significantly changed by then.
I suspect that is true for a lot of core kernel code - everything that is worth inlining is already inlined and for the rest it doesn't matter.
On the other hand a lot of driver code seems to be written without manual consideration for inline. For that it may be worth it. But then I would consider core kernel code to be more important than driver code.
Also I fear cross module inlining would expose a lot of latent bugs (missing barriers etc.) when the optimizer becomes more aggressive. I'm not saying this would be a bad thing, just that it may be a lot of work to fix (both for compiler and kernel people)
-Andi
Quoted 4 lines
You have to options:
- use attribute ((used)) (implemented since GCC 3.2) to tell GCC that a function/variable should never be removed
- use -fno-unit-at-a-time.
Another problem is the way 32bit emulation is implemented in many 64bit ports (all copying from sparc64) and now unified. This assumes an ordering between global functions and global assembly too.
Not for i386 though. I think Andrew has already done some cleanups in this area recently too, but it may still be dubious.
-Andi
Quoted 6 lines · Andi Kleen
I'm not sure that is that good an idea. When I was still hacking TCP I especially moved some stuff out-of-line in the fast path to avoid register pressure. Otherwise gcc would inline rarely used sub functions and completely mess up the register allocation in the fast path. Of course just a call alone messes up the registers somewhat because of its clobbers, but a full inlining is usually worse.
[...]
Quoted 2 lines
I suspect that is true for a lot of core kernel code - everything that is worth inlining is already inlined and for the rest it doesn't matter.
Definitely , agreed. In fact, we are moving in the opposite direction:
looking into what we can un-inline...
Quoted 4 lines
On the other hand a lot of driver code seems to be written without manual consideration for inline. For that it may be worth it. But then I would consider core kernel code to be more important than driver code.
Modern network drivers seem fairly aware of it ;-)
Quoted 4 lines
Also I fear cross module inlining would expose a lot of latent bugs (missing barriers etc.) when the optimizer becomes more aggressive. I'm not saying this would be a bad thing, just that it may be a lot of work to fix (both for compiler and kernel people)
Agreed.
Jeff
Quoted 10 lines · Andreas Jaeger
Quoted 5 lines
Since unit-at-a-time has better inlining heuristics the better way is to add the used attribute - but that takes some time. The short-term solution would be to add the compiler flag,
Won't we get a linker error if a static symbol is used but optimized-away? It shouldn't be hard to fix the n linker errors that crop up.
Yes, you get linker error.
You may also run into misscompilation assuiming that function is static and it is both called by hand in asm and by function call and there is missing attribute used and asmlinkage definition. In that case GCC would conclude to change into register calling convention on i386 breaking asm code.
I would expect this to be rare as functions tends to be used either by assembly or by normal code but not by both.
Quoted 3 lines
And why are we using static symbols in inline assembly outside of the compilation scope?
The toplevel asm statements are common source of this at least in glibc. I didn't look much into the kernel sources.
I would be very happy if someone did look on that. It may be well possible that implementing tricks you do currently with toplevel asm staements would need further extensions in GCC now and it would be nice to know about that.
For instance it used to be possible to force function to go into given section by changing the section by hand, but now you have to use section attribute (that is cleaner anyway)
Quoted 17 lines
Anyhow, if it generates an error, this isn't hard to fix.
Here is the start...
Robert Love
include/linux/compiler.h +3
-0@@ -74,6 +74,19 @@#define __attribute_pure__/* unimplemented */ #endif +/* +* As of gcc 3.2, we can mark a function as 'used' and gcc will assume that, +* even if it does not find a reference to it in any compilation unit. We
include/linux/compiler.h line 89
Quoted 10 lines
include/linux/compiler.h +10
-0+* need this for gcc 3.4 and beyond, which can optimize on a program-wide +* scope, and not just one file at a time, to avoid static symbols being +* discarded. +*/ +#if (__GNUC__ ==3 && __GNUC_MINOR__ >1 ) || __GNUC__ >3 +#define __attribute_used__ __attribute__((used)) +#else +#define __attribute_used__/* unimplemented */ +#endif +
I believe there is little trick - attribute used works either for variables or functions. Functions can be marked as used only for GCC 3.4+ if I am right, so you may need __attribute_used_function__ and __attribute_used_variable__ macros for that.
Honza
Quoted 5 lines
include/linux/compiler.h +0
-0/* This macro obfuscates arithmetic on a variable address so that gcc shouldn't recognize the original var, and make assumptions about it */ #define RELOC_HIDE (ptr, off) \
Quoted 11 lines
Quoted 1 line
How much work would be to fix kernel in this regard?
The big problem is that -funit-at-a-time is not widely used yet, so even if we fix the kernel at some point it would likely get broken again all the time by people who use older kernels (= most kernel developers currently)
Quoted 3 lines
Are there some cases where this is esential? Kernel would be nice target to whole program optimization and GCC is not that far from it right now.
Quoted 6 lines
I'm not sure that is that good an idea. When I was still hacking TCP I especially moved some stuff out-of-line in the fast path to avoid register pressure. Otherwise gcc would inline rarely used sub functions and completely mess up the register allocation in the fast path. Of course just a call alone messes up the registers somewhat because of its clobbers, but a full inlining is usually worse.
You can use -O2 and rely on inline done by hand. I can add option -fno-inline-functions-called-once. That should avoid such a problems. Anyway it would be nice to mark functions that exist for this reason by noinline attribute so compiler knows about it, but that is different story.
Quoted 5 lines
Also I fear cross module inlining would expose a lot of latent bugs (missing barriers etc.) when the optimizer becomes more aggressive. I'm not saying this would be a bad thing, just that it may be a lot of work to fix (both for compiler and kernel people)
Some of this should be already tested by folks using Intel compiler I would hope.
Honza
Quoted 3 lines
-Andi
Andreas Jaeger wrote:
[...]
Quoted 6 lines
The problem is that unit-at-a-time sees all functions used and finds some static functions/variables that are not called anywhere and therefore drops them, making a smaller binary. Since GCC does not look into inline assembler, anything referenced from inline assembler only, will be treated as not used and therefore removed.
Quoted 9 lines
You have to options:
- use attribute ((used)) (implemented since GCC 3.2) to tell GCC that a function/variable should never be removed
- use -fno-unit-at-a-time.Since unit-at-a-time has better inlining heuristics the better way is to add the used attribute - but that takes some time. The short-term solution would be to add the compiler flag,
Seems to me that a better solution is to mark the assembly code in question so gcc knows that is somehow calls the function. That still allows optimizing away the function whenever the assembly itself is left out. (Module not compiled or similiar) Marking the function "used" includes it anyway.
I realize this way probably isn' supported right now, but people are talking about changing gcc so I mentioned it as an ideal way.
Helge Hafting