public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* question on common error-handling idiom
@ 2004-11-02 20:08 Chris Friesen
  2004-11-02 20:58 ` Jan Engelhardt
                   ` (3 more replies)
  0 siblings, 4 replies; 12+ messages in thread
From: Chris Friesen @ 2004-11-02 20:08 UTC (permalink / raw)
  To: Linux kernel


There's something I've been wondering about for a while.  There is a lot of code 
in linux that looks something like this:


err = -ERRORCODE
if (error condition)
	goto out;


While nice to read, it would seem that it might be more efficient to do the 
following:

if (error condition) {
	err = -ERRORCODE;
	goto out;
}


Is there any particular reason why the former is preferred?  Is the compiler 
smart enough to optimize away the additional write in the non-error path?

Chris

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:08 question on common error-handling idiom Chris Friesen
@ 2004-11-02 20:58 ` Jan Engelhardt
  2004-11-02 21:12   ` linux-os
  2004-11-02 21:12   ` Russell Miller
  2004-11-02 21:16 ` Jesper Juhl
                   ` (2 subsequent siblings)
  3 siblings, 2 replies; 12+ messages in thread
From: Jan Engelhardt @ 2004-11-02 20:58 UTC (permalink / raw)
  To: Chris Friesen; +Cc: Linux kernel

>There's something I've been wondering about for a while.  There is a lot of code
>in linux that looks something like this:
>
>err = -ERRORCODE
>if (error condition)
>	goto out;

That's because there might something as:

err = -EPERM;
if(error) { goto out; }
do something;
if(error2) { goto out; }
do something more;
if(error3) { goto out; }

Is shorter than:

if(error) { err = -EPERM; goto out; }
do something;
if(error2) { err = -EPERM; goto out; }
do something more;
if(error3) { err = -EPERM; goto out; }


>Is there any particular reason why the former is preferred?  Is the compiler

To keep it short. Because it might have been worse than just err =xxx:

if(error) {
    do this and that;
    and more;
    even more;
    more more;
    goto out;
}

Repeating that over and over is not that good. So we wrap it a little bit to do
a "staircase" deinitialization:

err = -EPERM;
if(error) { goto this_didnot_work; }
...
err = -ENOSPC;
if(error) { goto that_didnot_work; }


this_didnot_work:
  all uninitializations needed

that_didnot_work:
  all other uninit's

  return err;


So to summarize, it's done to reduce code whilst keeping the error code around
until we actually leave the function.


My € 0.02!


Jan Engelhardt
-- 
Gesellschaft für Wissenschaftliche Datenverarbeitung
Am Fassberg, 37077 Göttingen, www.gwdg.de

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:58 ` Jan Engelhardt
@ 2004-11-02 21:12   ` linux-os
  2004-11-03 10:45     ` Ross Kendall Axe
  2004-11-02 21:12   ` Russell Miller
  1 sibling, 1 reply; 12+ messages in thread
From: linux-os @ 2004-11-02 21:12 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Chris Friesen, Linux kernel

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: TEXT/PLAIN; charset=X-UNKNOWN; format=flowed, Size: 1916 bytes --]

On Tue, 2 Nov 2004, Jan Engelhardt wrote:

>> There's something I've been wondering about for a while.  There is a lot of code
>> in linux that looks something like this:
>>
>> err = -ERRORCODE
>> if (error condition)
>> 	goto out;
>
> That's because there might something as:
>
> err = -EPERM;
> if(error) { goto out; }
> do something;
> if(error2) { goto out; }
> do something more;
> if(error3) { goto out; }
>
> Is shorter than:
>
> if(error) { err = -EPERM; goto out; }
> do something;
> if(error2) { err = -EPERM; goto out; }
> do something more;
> if(error3) { err = -EPERM; goto out; }
>
>
>> Is there any particular reason why the former is preferred?  Is the compiler
>
> To keep it short. Because it might have been worse than just err =xxx:
>
> if(error) {
>    do this and that;
>    and more;
>    even more;
>    more more;
>    goto out;
> }
>
> Repeating that over and over is not that good. So we wrap it a little bit to do
> a "staircase" deinitialization:
>
> err = -EPERM;
> if(error) { goto this_didnot_work; }
> ...
> err = -ENOSPC;
> if(error) { goto that_didnot_work; }
>
>
> this_didnot_work:
>  all uninitializations needed
>
> that_didnot_work:
>  all other uninit's
>
>  return err;
>
>
> So to summarize, it's done to reduce code whilst keeping the error code around
> until we actually leave the function.
>
>
> My ÿÿ 0.02!
>
>
> Jan Engelhardt
> -- 
> Gesellschaft fÿÿr Wissenschaftliche Datenverarbeitung
> Am Fassberg, 37077 Gÿÿttingen, www.gwdg.de

I think it's just to get around the "uninitialized variable"
warning when the 'C' compiler doesn't know that it will
always be initialized.

Cheers,
Dick Johnson
Penguin : Linux version 2.6.9 on an i686 machine (5537.79 BogoMips).
  Notice : All mail here is now cached for review by John Ashcroft.
                  98.36% of all statistics are fiction.

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:58 ` Jan Engelhardt
  2004-11-02 21:12   ` linux-os
@ 2004-11-02 21:12   ` Russell Miller
  1 sibling, 0 replies; 12+ messages in thread
From: Russell Miller @ 2004-11-02 21:12 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Chris Friesen, Linux kernel

On Tuesday 02 November 2004 14:58, Jan Engelhardt wrote:

> So to summarize, it's done to reduce code whilst keeping the error code
> around until we actually leave the function.
>
I understand what you're saying, the OP did raise a point that I think is 
worth repeating, that it's an extra instruction in all but error cases.  Is 
that extra instruction worth the tradeoff?

--Russell

>
> My € 0.02!
>
>
> Jan Engelhardt

-- 

Russell Miller - rmiller@duskglow.com - Le Mars, IA
Duskglow Consulting - Helping companies just like you to succeed for ~ 10 yrs.
http://www.duskglow.com - 712-546-5886

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:08 question on common error-handling idiom Chris Friesen
  2004-11-02 20:58 ` Jan Engelhardt
@ 2004-11-02 21:16 ` Jesper Juhl
  2004-11-02 21:21   ` Oliver Neukum
  2004-11-02 21:29   ` Jan Engelhardt
  2004-11-03  8:11 ` GNicz
  2004-11-04 19:52 ` Linus Torvalds
  3 siblings, 2 replies; 12+ messages in thread
From: Jesper Juhl @ 2004-11-02 21:16 UTC (permalink / raw)
  To: Chris Friesen; +Cc: Linux kernel

On Tue, 2 Nov 2004, Chris Friesen wrote:

> 
> There's something I've been wondering about for a while.  There is a lot of
> code in linux that looks something like this:
> 
> 
> err = -ERRORCODE
> if (error condition)
> 	goto out;
> 
> 
> While nice to read, it would seem that it might be more efficient to do the
> following:
> 
> if (error condition) {
> 	err = -ERRORCODE;
> 	goto out;
> }
> 
> 
> Is there any particular reason why the former is preferred?  Is the compiler
> smart enough to optimize away the additional write in the non-error path?
> 
There are some places that do

err = -SOMEERROR;
if (some_error)
	goto out;
if (some_other_error)
	goto out;
if (another_error)
	goto out;

In that case, where there are several different conditions that need 
testing, but they all need to return the same error, setting the error 
just once seems the best approach.

but for the places that do

err = -SOMEERROR;
if (condition)
	goto out;

err = -OTHERERROR;
if (condition)
	goto out;

I would tend to agree with you that moving the setting of the error inside 
the if() would make sense.

Let's see what other people think :)


--
Jesper Juhl


^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 21:16 ` Jesper Juhl
@ 2004-11-02 21:21   ` Oliver Neukum
  2004-11-02 21:29   ` Jan Engelhardt
  1 sibling, 0 replies; 12+ messages in thread
From: Oliver Neukum @ 2004-11-02 21:21 UTC (permalink / raw)
  To: Jesper Juhl; +Cc: Chris Friesen, Linux kernel


> but for the places that do
> 
> err = -SOMEERROR;
> if (condition)
> 	goto out;
> 
> err = -OTHERERROR;
> if (condition)
> 	goto out;
> 
> I would tend to agree with you that moving the setting of the error inside 
> the if() would make sense.

The intention is to allow the compiler to turn the if into a simple
conditional branch on the assumption that gcc is not smart enough
to put the if's body out of line.

	Regards
		Oliver

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 21:16 ` Jesper Juhl
  2004-11-02 21:21   ` Oliver Neukum
@ 2004-11-02 21:29   ` Jan Engelhardt
  2004-11-02 21:48     ` Jesper Juhl
  1 sibling, 1 reply; 12+ messages in thread
From: Jan Engelhardt @ 2004-11-02 21:29 UTC (permalink / raw)
  To: Jesper Juhl; +Cc: Chris Friesen, Linux kernel

>There are some places that do
>
>err = -SOMEERROR;
>if (some_error)
>	goto out;
>if (some_other_error)
>	goto out;
>if (another_error)
>	goto out;
>
>Let's see what other people think :)

err = -ESOME;
if(some_error || some_other_error || another_error) {
	goto out;
}

Best.

Jan Engelhardt
-- 
Gesellschaft für Wissenschaftliche Datenverarbeitung
Am Fassberg, 37077 Göttingen, www.gwdg.de

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 21:29   ` Jan Engelhardt
@ 2004-11-02 21:48     ` Jesper Juhl
  2004-11-03 16:49       ` Chris Friesen
  0 siblings, 1 reply; 12+ messages in thread
From: Jesper Juhl @ 2004-11-02 21:48 UTC (permalink / raw)
  To: Jan Engelhardt; +Cc: Chris Friesen, Oliver Neukum, Linux kernel

On Tue, 2 Nov 2004, Jan Engelhardt wrote:

> >There are some places that do
> >
> >err = -SOMEERROR;
> >if (some_error)
> >	goto out;
> >if (some_other_error)
> >	goto out;
> >if (another_error)
> >	goto out;
> >
> >Let's see what other people think :)
> 
> err = -ESOME;
> if(some_error || some_other_error || another_error) {
> 	goto out;
> }
> 
> Best.
> 
Agreed, but that would potentially make something like the following (from 
fs/binfmt_elf.c::load_elf_binary) quite unreadable :

if (loc->elf_ex.e_type != ET_EXEC && loc->elf_ex.e_type != ET_DYN)
	goto out;
if (!elf_check_arch(&loc->elf_ex))
	goto out;
if (!bprm->file->f_op||!bprm->file->f_op->mmap)
	goto out;


But that's not even the most interresting case, the interresting one is 
the one that does

err = -ERR;
if (foo)
	goto out;

err = -ERROR;
if (bar)
	goto out;

where there's the potential to save an instruction by moving the err= into 
the error path.
But as Oliver Neukum pointed out gcc may not be smart enough for that to 
actually generate the best code.

Has anyone taken a look at what recent gcc's actually do with different 
variations of the constructs mentioned in this thread?


--
Jesper Juhl



^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:08 question on common error-handling idiom Chris Friesen
  2004-11-02 20:58 ` Jan Engelhardt
  2004-11-02 21:16 ` Jesper Juhl
@ 2004-11-03  8:11 ` GNicz
  2004-11-04 19:52 ` Linus Torvalds
  3 siblings, 0 replies; 12+ messages in thread
From: GNicz @ 2004-11-03  8:11 UTC (permalink / raw)
  To: linux-kernel

Some code parts is using diffirent error handling.

if(err1) {
	clenup1;
	return -ERR1;
}

...

if(err2) {
	cleanup2;
	return -ERR2;
}

Shouldn't it be converted into:

err = -ERR1;
if(err1)
	goto cleanup1;

...

err = -ERR2;
if(err2)
	goto cleanup2;

I think there should be one methond, which will be used in all kernel code.

Regards, GNicz

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 21:12   ` linux-os
@ 2004-11-03 10:45     ` Ross Kendall Axe
  0 siblings, 0 replies; 12+ messages in thread
From: Ross Kendall Axe @ 2004-11-03 10:45 UTC (permalink / raw)
  To: linux-os; +Cc: Jan Engelhardt, Chris Friesen, Linux kernel

[-- Attachment #1: Type: text/plain, Size: 430 bytes --]

linux-os wrote:

>>> There's something I've been wondering about for a while.  There is a 
>>> lot of code
>>> in linux that looks something like this:
>>>
>>> err = -ERRORCODE
>>> if (error condition)
>>>     goto out;
>>
> 
> 
> I think it's just to get around the "uninitialized variable"
> warning when the 'C' compiler doesn't know that it will
> always be initialized.
> 

gcc is smart enough to get this case right.

Ross


[-- Attachment #2: OpenPGP digital signature --]
[-- Type: application/pgp-signature, Size: 256 bytes --]

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 21:48     ` Jesper Juhl
@ 2004-11-03 16:49       ` Chris Friesen
  0 siblings, 0 replies; 12+ messages in thread
From: Chris Friesen @ 2004-11-03 16:49 UTC (permalink / raw)
  To: Jesper Juhl; +Cc: Jan Engelhardt, Oliver Neukum, Linux kernel

Jesper Juhl wrote:

> Has anyone taken a look at what recent gcc's actually do with different 
> variations of the constructs mentioned in this thread?

I did, out of curiosity:

I used the following (admittedly simplistic) code, compiled with -O2.

int bbbb(int a)
{
	int err = -5;
	if (a == 1)
		goto out;
	err=0;
out:
	return err;
}

int cccc(int a)
{
	int err=0;
	if (a == 1) {
		err = -5;
		goto out;
	}
out:
	return err;
}


With gcc 3.2.2 for x86, both constructs generated the same code:

        pushl   %ebp
         movl    %esp, %ebp
         xorl    %eax, %eax
         cmpl    $1, 8(%ebp)
         setne   %al
         leal    -5(%eax,%eax,4), %eax
         leave
         ret

With gcc 2.96 (Mandrake) however, the standard construct generated this:


	pushl	%ebp
	movl	%esp, %ebp
	subl	$4, %esp
	movl	$-5, -4(%ebp)
	cmpl	$1, 8(%ebp)
	jne	.L3
	jmp	.L4
	.p2align 4,,7
.L3:
	movl	$0, -4(%ebp)
.L4:
	movl	-4(%ebp), %eax
	movl	%eax, %eax
	movl	%ebp, %esp
	popl	%ebp
	ret



While moving the err setting into the conditional generates the following:

	pushl	%ebp
	movl	%esp, %ebp
	subl	$4, %esp
	movl	$0, -4(%ebp)
	cmpl	$1, 8(%ebp)
	jne	.L6
	movl	$-5, -4(%ebp)
	jmp	.L7
	.p2align 4,,7
.L6:
	nop
.L7:
	movl	-4(%ebp), %eax
	movl	%eax, %eax
	movl	%ebp, %esp
	popl	%ebp
	ret


For PPC, gcc 3.3.3, the standard construct gave:

         xori 3,3,1
         addic 3,3,-1
         subfe 3,3,3
         rlwinm 3,3,0,30,28
         blr

While moving the err setting into the conditional generates the following:

         xori 3,3,1
         srawi 0,3,31
         xor 3,0,3
         subf 3,3,0
         srawi 3,3,31
         andi. 3,3,5
         addi 3,3,-5
         blr


So, it looks like the standard construct can actually generate better code in 
some cases, its almost never worse, and it's certainly nicer to read.

Chris

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: question on common error-handling idiom
  2004-11-02 20:08 question on common error-handling idiom Chris Friesen
                   ` (2 preceding siblings ...)
  2004-11-03  8:11 ` GNicz
@ 2004-11-04 19:52 ` Linus Torvalds
  3 siblings, 0 replies; 12+ messages in thread
From: Linus Torvalds @ 2004-11-04 19:52 UTC (permalink / raw)
  To: Chris Friesen; +Cc: Linux kernel



On Tue, 2 Nov 2004, Chris Friesen wrote:
> 
> While nice to read, it would seem that it might be more efficient to do the 
> following:
> 
> if (error condition) {
> 	err = -ERRORCODE;
> 	goto out;
> }
> 
> 
> Is there any particular reason why the former is preferred?  Is the compiler 
> smart enough to optimize away the additional write in the non-error path?

Quite often, the additional write is _faster_ in the non-error path.

If it's a plain register, the obvious code generation for the above is

		..
		testl error-condition
		je no_error
		movl $-XXX,%eax
		jmp out;
	no_error:
		...

which is a lot slower (for the common non-error case) than

	
		movl $-XXX,%eax
		testl error-condition
		jne out

because forward branches are usually predicted not-taken when no other 
prediction exists.

You can do the same thing with

	if (unlikely(error condition)) {
		err = -ERRORCODE;
		goto out;
	}

which is hopefully even better, but the fact is, the regular

	err = -ERRORCODE;
	if (error condition)
		goto out;

is just _smaller_ and simpler and quite often more readable anyway.

It has the added advantage that it tends to stylistically match what I 
consider the proper error return behaviour, ie

	err = function_returns_errno(...)
	if (err)
		goto out;

ie it looks syntactically identical when "error condition" and the error 
value happen to be one and the same. Which is, after all, _the_ most 
common case.

So I personally tend to prefer the simple format for several reasons. 
It's small, it's consistent, and it maps well to good code generation.

The code generation part ends up being nice when something goes wrong.  
When somebody sends in an oops, I often end up having to look at the
disassembly (and no, a fancy debugger wouldn't help - I'm talking about
the disassembly of the "code" portion of the oops itself, and matching it
up with the source tree - the oops doesn't come with the whole binary),
and then having code generation match the source makes things a _lot_
easier.

Does it make sense for other projects? Dunno. But it is, as you noted, a 
common idiom in the kernel. Getting used to it just makes it even more 
readable.

			Linus

^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2004-11-04 19:58 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-11-02 20:08 question on common error-handling idiom Chris Friesen
2004-11-02 20:58 ` Jan Engelhardt
2004-11-02 21:12   ` linux-os
2004-11-03 10:45     ` Ross Kendall Axe
2004-11-02 21:12   ` Russell Miller
2004-11-02 21:16 ` Jesper Juhl
2004-11-02 21:21   ` Oliver Neukum
2004-11-02 21:29   ` Jan Engelhardt
2004-11-02 21:48     ` Jesper Juhl
2004-11-03 16:49       ` Chris Friesen
2004-11-03  8:11 ` GNicz
2004-11-04 19:52 ` Linus Torvalds

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox