* [PATCH] fix sparse warning from include/linux/mmzone.h @ 2008-02-07 20:52 Harvey Harrison 2008-02-08 21:51 ` Andrew Morton 0 siblings, 1 reply; 9+ messages in thread From: Harvey Harrison @ 2008-02-07 20:52 UTC (permalink / raw) To: Andrew Morton, Linus Torvalds, Al Viro; +Cc: LKML, Ingo Molnar include/linux/mmzone.h:640:22: warning: potentially expensive pointer subtraction The code in question was doing a pointer subtraction to find the index of the zone argument in the node_zones array. This essentially boils down to: ((unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones)/sizeof(struct zone) Which can be expensive if struct zone is not a power of two. Instead, just calculate the offsets rather than the index in the array. Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> --- This shows up > 30 times in an x86 allyesconfig, would be nice to get rid of it and makes the code a little more efficient. I believe this is correct, unless there is a subtlety I have missed. include/linux/mmzone.h | 9 ++++++--- 1 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 8d8d197..cb07758 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -637,9 +637,12 @@ static inline int is_normal_idx(enum zone_type idx) static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM - int zone_idx = zone - zone->zone_pgdat->node_zones; - return zone_idx == ZONE_HIGHMEM || - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); + const int highmem_off = ZONE_HIGHMEM * sizeof(*zone); + const int movable_off = ZONE_MOVABLE * sizeof(*zone); + + int zone_off = (unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones; + return zone_off == highmem_off || + (zone_off == movable_off && zone_movable_is_highmem()); #else return 0; #endif -- 1.5.4.1219.g65b9 ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-07 20:52 [PATCH] fix sparse warning from include/linux/mmzone.h Harvey Harrison @ 2008-02-08 21:51 ` Andrew Morton 2008-02-08 22:04 ` Linus Torvalds 0 siblings, 1 reply; 9+ messages in thread From: Andrew Morton @ 2008-02-08 21:51 UTC (permalink / raw) To: Harvey Harrison; +Cc: torvalds, viro, linux-kernel, mingo On Thu, 07 Feb 2008 12:52:23 -0800 Harvey Harrison <harvey.harrison@gmail.com> wrote: > include/linux/mmzone.h:640:22: warning: potentially expensive pointer subtraction > > The code in question was doing a pointer subtraction to find the index of > the zone argument in the node_zones array. This essentially boils down to: > > ((unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones)/sizeof(struct zone) > > Which can be expensive if struct zone is not a power of two. Instead, just > calculate the offsets rather than the index in the array. > > Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> > --- > This shows up > 30 times in an x86 allyesconfig, would be nice to get > rid of it and makes the code a little more efficient. I believe this > is correct, unless there is a subtlety I have missed. > > include/linux/mmzone.h | 9 ++++++--- > 1 files changed, 6 insertions(+), 3 deletions(-) > > diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h > index 8d8d197..cb07758 100644 > --- a/include/linux/mmzone.h > +++ b/include/linux/mmzone.h > @@ -637,9 +637,12 @@ static inline int is_normal_idx(enum zone_type idx) > static inline int is_highmem(struct zone *zone) > { > #ifdef CONFIG_HIGHMEM > - int zone_idx = zone - zone->zone_pgdat->node_zones; > - return zone_idx == ZONE_HIGHMEM || > - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); > + const int highmem_off = ZONE_HIGHMEM * sizeof(*zone); > + const int movable_off = ZONE_MOVABLE * sizeof(*zone); > + > + int zone_off = (unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones; > + return zone_off == highmem_off || > + (zone_off == movable_off && zone_movable_is_highmem()); > #else > return 0; > #endif > -- hrm. For my i386 build (and that's where CONFIG_HIGHMEM really matters) this patch makes mm/page_alloc.o six bytes larger, and I don't think the change did much for readability. ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 21:51 ` Andrew Morton @ 2008-02-08 22:04 ` Linus Torvalds 2008-02-08 22:17 ` Harvey Harrison ` (3 more replies) 0 siblings, 4 replies; 9+ messages in thread From: Linus Torvalds @ 2008-02-08 22:04 UTC (permalink / raw) To: Andrew Morton; +Cc: Harvey Harrison, viro, linux-kernel, mingo On Fri, 8 Feb 2008, Andrew Morton wrote: > > #ifdef CONFIG_HIGHMEM > > - int zone_idx = zone - zone->zone_pgdat->node_zones; > > - return zone_idx == ZONE_HIGHMEM || > > - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); > > + const int highmem_off = ZONE_HIGHMEM * sizeof(*zone); > > + const int movable_off = ZONE_MOVABLE * sizeof(*zone); > > + > > + int zone_off = (unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones; > > + return zone_off == highmem_off || > > + (zone_off == movable_off && zone_movable_is_highmem()); > > hrm. For my i386 build (and that's where CONFIG_HIGHMEM really matters) > this patch makes mm/page_alloc.o six bytes larger, and I don't think the > change did much for readability. Heh, yeah. That's a very odd way to write it. It would probably make more sense to just write it as something like struct zone *base = zone->zone_pgdat->node_zones; if (zone == base + ZONE_HIGHMEM || (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); instead. The above is totally untested, of course. Linus ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 22:04 ` Linus Torvalds @ 2008-02-08 22:17 ` Harvey Harrison 2008-02-08 22:26 ` Ingo Molnar ` (2 subsequent siblings) 3 siblings, 0 replies; 9+ messages in thread From: Harvey Harrison @ 2008-02-08 22:17 UTC (permalink / raw) To: Linus Torvalds; +Cc: Andrew Morton, viro, linux-kernel, mingo On Fri, 2008-02-08 at 14:04 -0800, Linus Torvalds wrote: > > On Fri, 8 Feb 2008, Andrew Morton wrote: > > > #ifdef CONFIG_HIGHMEM > > > - int zone_idx = zone - zone->zone_pgdat->node_zones; > > > - return zone_idx == ZONE_HIGHMEM || > > > - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); > > > + const int highmem_off = ZONE_HIGHMEM * sizeof(*zone); > > > + const int movable_off = ZONE_MOVABLE * sizeof(*zone); > > > + > > > + int zone_off = (unsigned long)zone - (unsigned long)zone->zone_pgdat->node_zones; > > > + return zone_off == highmem_off || > > > + (zone_off == movable_off && zone_movable_is_highmem()); > > > > hrm. For my i386 build (and that's where CONFIG_HIGHMEM really matters) > > this patch makes mm/page_alloc.o six bytes larger, and I don't think the > > change did much for readability. > > Heh, yeah. That's a very odd way to write it. As long as you read 'odd' as 'rather dumb' > > It would probably make more sense to just write it as something like > > struct zone *base = zone->zone_pgdat->node_zones; > > if (zone == base + ZONE_HIGHMEM || > (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); s/if/return/ I think. And missing a closing brace. Unless I'm (again) missing something. Harvey ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 22:04 ` Linus Torvalds 2008-02-08 22:17 ` Harvey Harrison @ 2008-02-08 22:26 ` Ingo Molnar 2008-02-08 22:35 ` Linus Torvalds 2008-02-08 22:38 ` [PATCH] fix sparse warning from include/linux/mmzone.h Harvey Harrison 3 siblings, 0 replies; 9+ messages in thread From: Ingo Molnar @ 2008-02-08 22:26 UTC (permalink / raw) To: Linus Torvalds; +Cc: Andrew Morton, Harvey Harrison, viro, linux-kernel * Linus Torvalds <torvalds@linux-foundation.org> wrote: > Heh, yeah. That's a very odd way to write it. > > It would probably make more sense to just write it as something like > > struct zone *base = zone->zone_pgdat->node_zones; > > if (zone == base + ZONE_HIGHMEM || > (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); > > instead. missing closing parenthesis ;-) btw., doesnt this look even nicer?: if (zone == base + ZONE_HIGHMEM || (zone == base + ZONE_MOVABLE && zone_movable_is_highmem())); [ i guess it must be Friday... ;-) ] Ingo ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 22:04 ` Linus Torvalds 2008-02-08 22:17 ` Harvey Harrison 2008-02-08 22:26 ` Ingo Molnar @ 2008-02-08 22:35 ` Linus Torvalds 2008-02-13 19:56 ` Harvey Harrison 2008-02-08 22:38 ` [PATCH] fix sparse warning from include/linux/mmzone.h Harvey Harrison 3 siblings, 1 reply; 9+ messages in thread From: Linus Torvalds @ 2008-02-08 22:35 UTC (permalink / raw) To: Andrew Morton; +Cc: Harvey Harrison, viro, linux-kernel, mingo On Fri, 8 Feb 2008, Linus Torvalds wrote: > > It would probably make more sense to just write it as something like > > struct zone *base = zone->zone_pgdat->node_zones; > > if (zone == base + ZONE_HIGHMEM || > (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); Side note: while the above is more readable, gcc still isn't smart enough to notice that the two compares could be done more efficiently as a subtraction. But using an actual pointer subtraction does involve that nasty divide (well, it's nasty only for certain sizes of "struct zone"), so writing it as such is not very nice either, even if it's the most obvious way from a source code standpoint. Here's an example: /* 12-byte (non-power-of-two) example struct */ struct example { int a, b, c; }; #define ptrcmp1(ptr, base, index) \ ((ptr) == (base) + (index)) #define ptrcmp2(ptr, base, index) \ ((ptr) - (base) == (index)) #define ptrcmp3(ptr, base, index) \ ((char *)(ptr) - (char *)(base) == (index)*sizeof(*ptr)) #define test(cmp) \ if (cmp(ptr, base, 1) || cmp(ptr, base, 3)) \ printf("success\n") int test1(struct example *ptr, struct example *base) { test(ptrcmp1); } int test2(struct example *ptr, struct example *base) { test(ptrcmp2); } int test3(struct example *ptr, struct example *base) { test(ptrcmp3); } and the results for me are: test1: leaq 12(%rsi), %rax cmpq %rdi, %rax je .L15 leaq 36(%rsi), %rax cmpq %rdi, %rax je .L15 test2: subq %rsi, %rdi leaq -12(%rdi), %rax cmpq $11, %rax jbe .L11 leaq -36(%rdi), %rax cmpq $11, %rax jbe .L11 test3: subq %rsi, %rdi cmpq $12, %rdi je .L4 cmpq $36, %rdi je .L4 ie the only way to get the *nice* code generation is that ugly third alternative. YMMV depending on compiler, of course. Linus ^ permalink raw reply [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 22:35 ` Linus Torvalds @ 2008-02-13 19:56 ` Harvey Harrison 2008-02-13 20:36 ` [PATCH] remove sparse warning for mmzone.h Harvey Harrison 0 siblings, 1 reply; 9+ messages in thread From: Harvey Harrison @ 2008-02-13 19:56 UTC (permalink / raw) To: Linus Torvalds; +Cc: Andrew Morton, viro, linux-kernel, mingo Maybe I'm beating a dead horse, but here's what I came up with: include/linux/mmzone.h:640:22: warning: potentially expensive pointer subtraction Using arch/x86/mm/highmem_32.o as an example, line 82, PageHighMem expands to is_highmem. if (!PageHighMem(page)) return page_address(page); I've run through the current code, one of Linus' suggestions and Linus' alternate version of my original patch without the stupid unsigned long casts, using char *. The best version appears to be the char * version. Code size will increase by one byte for each use of is_highmem, but there is one fewer instruction (saves a sar over the base code). The reason for the code size increase is the two cmps use 32-bit compares rather than 16 bit compares in the original due to the sar beforehand. (-3 bytes for no sar +4 bytes for longer cmp) It's a small thing, if you agree that the new code is better, I'll send a patch. Original: static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM int zone_idx = zone - zone->zone_pgdat->node_zones; return zone_idx == ZONE_HIGHMEM || (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); #else return 0; #endif } 207: 2b 80 8c 07 00 00 sub 0x78c(%eax),%eax 20d: c1 f8 0b sar $0xb,%eax 210: 83 f8 02 cmp $0x2,%eax 213: 74 16 je 22b <kmap_atomic_prot+0x144> 215: 83 f8 03 cmp $0x3,%eax 218: 0f 85 8f 00 00 00 jne 2ad <kmap_atomic_prot+0x1c6> 21e: 83 3d 00 00 00 00 02 cmpl $0x2,0x0 225: 0f 85 82 00 00 00 jne 2ad <kmap_atomic_prot+0x1c6> 22b: 64 a1 00 00 00 00 mov %fs:0x0,%eax Linus' suggestion: static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM struct zone *base = zone->zone_pgdat->node_zones; return zone == base + ZONE_HIGHMEM || (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); #else return 0; #endif } 202: 8d 90 00 00 00 00 lea 0x0(%eax),%edx 208: 8b 8a 8c 07 00 00 mov 0x78c(%edx),%ecx 20e: 8d 81 00 10 00 00 lea 0x1000(%ecx),%eax 214: 39 c2 cmp %eax,%edx 216: 74 1b je 233 <kmap_atomic_prot+0x14c> 218: 8d 81 00 18 00 00 lea 0x1800(%ecx),%eax 21e: 39 c2 cmp %eax,%edx 220: 0f 85 8f 00 00 00 jne 2b5 <kmap_atomic_prot+0x1ce> 226: 83 3d 00 00 00 00 02 cmpl $0x2,0x0 22d: 0f 85 82 00 00 00 jne 2b5 <kmap_atomic_prot+0x1ce> 233: 64 a1 00 00 00 00 mov %fs:0x0,%eax An alternate suggestion (excuse the long line): static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM int zone_off = (char *)zone - (char *)zone->zone_pgdat->node_zones; return zone_off == ZONE_HIGHMEM * sizeof(*zone) || (zone_off == ZONE_MOVABLE * sizeof(*zone) && zone_movable_is_highmem()); #else return 0; #endif } 207: 2b 80 8c 07 00 00 sub 0x78c(%eax),%eax 20d: 3d 00 10 00 00 cmp $0x1000,%eax 212: 74 18 je 22c <kmap_atomic_prot+0x145> 214: 3d 00 18 00 00 cmp $0x1800,%eax 219: 0f 85 8f 00 00 00 jne 2ae <kmap_atomic_prot+0x1c7> 21f: 83 3d 00 00 00 00 02 cmpl $0x2,0x0 226: 0f 85 82 00 00 00 jne 2ae <kmap_atomic_prot+0x1c7> 22c: 64 a1 00 00 00 00 mov %fs:0x0,%eax Cheers, Harvey ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH] remove sparse warning for mmzone.h 2008-02-13 19:56 ` Harvey Harrison @ 2008-02-13 20:36 ` Harvey Harrison 0 siblings, 0 replies; 9+ messages in thread From: Harvey Harrison @ 2008-02-13 20:36 UTC (permalink / raw) To: Linus Torvalds; +Cc: Andrew Morton, viro, linux-kernel, mingo include/linux/mmzone.h:640:22: warning: potentially expensive pointer subtraction Calculate the offset into the node_zones array rather than the index using casts to (char *) and comparing against the index * sizeof(struct zone). On X86_32 this saves a sar, but code size increases by one byte per is_highmem() use due to 32-bit cmps rather than 16 bit cmps. Before: 207: 2b 80 8c 07 00 00 sub 0x78c(%eax),%eax 20d: c1 f8 0b sar $0xb,%eax 210: 83 f8 02 cmp $0x2,%eax 213: 74 16 je 22b <kmap_atomic_prot+0x144> 215: 83 f8 03 cmp $0x3,%eax 218: 0f 85 8f 00 00 00 jne 2ad <kmap_atomic_prot+0x1c6> 21e: 83 3d 00 00 00 00 02 cmpl $0x2,0x0 225: 0f 85 82 00 00 00 jne 2ad <kmap_atomic_prot+0x1c6> 22b: 64 a1 00 00 00 00 mov %fs:0x0,%eax After: 207: 2b 80 8c 07 00 00 sub 0x78c(%eax),%eax 20d: 3d 00 10 00 00 cmp $0x1000,%eax 212: 74 18 je 22c <kmap_atomic_prot+0x145> 214: 3d 00 18 00 00 cmp $0x1800,%eax 219: 0f 85 8f 00 00 00 jne 2ae <kmap_atomic_prot+0x1c7> 21f: 83 3d 00 00 00 00 02 cmpl $0x2,0x0 226: 0f 85 82 00 00 00 jne 2ae <kmap_atomic_prot+0x1c7> 22c: 64 a1 00 00 00 00 mov %fs:0x0,%eax Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> --- include/linux/mmzone.h | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 8d8d197..3109a65 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -637,9 +637,10 @@ static inline int is_normal_idx(enum zone_type idx) static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM - int zone_idx = zone - zone->zone_pgdat->node_zones; - return zone_idx == ZONE_HIGHMEM || - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); + int zone_off = (char *)zone - (char *)zone->zone_pgdat->node_zones; + return zone_off == ZONE_HIGHMEM * sizeof(*zone) || + (zone_off == ZONE_MOVABLE * sizeof(*zone) && + zone_movable_is_highmem()); #else return 0; #endif -- 1.5.4.1.1278.gc75be ^ permalink raw reply related [flat|nested] 9+ messages in thread
* Re: [PATCH] fix sparse warning from include/linux/mmzone.h 2008-02-08 22:04 ` Linus Torvalds ` (2 preceding siblings ...) 2008-02-08 22:35 ` Linus Torvalds @ 2008-02-08 22:38 ` Harvey Harrison 3 siblings, 0 replies; 9+ messages in thread From: Harvey Harrison @ 2008-02-08 22:38 UTC (permalink / raw) To: Linus Torvalds; +Cc: Andrew Morton, viro, linux-kernel, mingo include/linux/mmzone.h:640:22: warning: potentially expensive pointer subtraction Signed-off-by: Harvey Harrison <harvey.harrison@gmail.com> --- Linus, something like the following? include/linux/mmzone.h | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/include/linux/mmzone.h b/include/linux/mmzone.h index 8d8d197..7ed2922 100644 --- a/include/linux/mmzone.h +++ b/include/linux/mmzone.h @@ -637,9 +637,10 @@ static inline int is_normal_idx(enum zone_type idx) static inline int is_highmem(struct zone *zone) { #ifdef CONFIG_HIGHMEM - int zone_idx = zone - zone->zone_pgdat->node_zones; - return zone_idx == ZONE_HIGHMEM || - (zone_idx == ZONE_MOVABLE && zone_movable_is_highmem()); + struct zone *base = zone->zone_pgdat->node_zones; + + return zone == base + ZONE_HIGHMEM || + (zone == base + ZONE_MOVABLE && zone_movable_is_highmem()); #else return 0; #endif -- 1.5.4.1219.g65b9 ^ permalink raw reply related [flat|nested] 9+ messages in thread
end of thread, other threads:[~2008-02-13 20:36 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2008-02-07 20:52 [PATCH] fix sparse warning from include/linux/mmzone.h Harvey Harrison 2008-02-08 21:51 ` Andrew Morton 2008-02-08 22:04 ` Linus Torvalds 2008-02-08 22:17 ` Harvey Harrison 2008-02-08 22:26 ` Ingo Molnar 2008-02-08 22:35 ` Linus Torvalds 2008-02-13 19:56 ` Harvey Harrison 2008-02-13 20:36 ` [PATCH] remove sparse warning for mmzone.h Harvey Harrison 2008-02-08 22:38 ` [PATCH] fix sparse warning from include/linux/mmzone.h Harvey Harrison
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox