2

Using GCC (4.0 for me), is this legal:

if(__builtin_expect(setjmp(buf) != 0, 1))
  {
    // handle error
  }
else
  {
    // do action
  }

I found a discussion saying it caused a problem for GCC back in 2003, but I would imagine that they would have fixed it by now. The C standard says that it's illegal to use setjmp unless it's one of four conditions, the relevant one being this:

  • one operand of a relational or equality operator with the other operand an integer constant expression, with the resulting expression being the entire controlling expression of a selection or iteration statement;

But if this is a GCC extension, can I guarantee that it will work under for GCC, since it's already nonstandard functionality? I tested it and it seemed to work, though I don't know how much testing I'd have to do to actually break it. (I'm hiding the call to __builtin_expect behind a macro, which is defined as a no-op for non-GCC, so it would be perfectly legal for other compilers.)

Chris Lutz
  • 73,191
  • 16
  • 130
  • 183
  • Do you really need the `__builtin_expect` there? I would expect GCC to treat `setjmp` as a special case and optimize for the common path. There are very few occurrences of this pattern on google codesearch: http://google.com/codesearch?hl=en&lr=&q=(__builtin_expect|likely).setjmp&sbtn=Search – Giuseppe Ottaviano Jan 07 '11 at 02:02
  • @Giuseppe - Probably not, but it's always good to learn. – Chris Lutz Jan 07 '11 at 02:04
  • 1
    The whole `__builtin_expect` is a ridiculous premature optimization that uglifies code for dubious benefit. If gcc has a builtin `setjmp`, it could even optimize out the whole conditional by having `setjmp` store the address of otherwise-unreachable code for the nonzero condition in the jump buffer directly, so I think we're really dealing with Premature Optimization Considered Harmful here. – R.. GitHub STOP HELPING ICE Jan 07 '11 at 04:48
  • 1
    @R.: The code example in the question was not a complete example of the program and doesn't tell anything about how mature the project is or how this bit of code is being used (or how often it gets executed). This may be the hottest code in a CPU heavy application. – nategoose Jan 07 '11 at 20:22

2 Answers2

1

I think that what the standard was talking about was to account for doing something like this:

int x = printf("howdy");
if (setjmp(buf) != x ) {
    function_that_might_call_longjmp_with_x(buf, x);
} else {
    do_something_about_them_errors();
}

In this case you could not rely on x having the value that it was assigned in the previous line anymore. The compiler may have moved the place where x had been (reusing the register it had been in, or something), so the code that did the comparison would be looking in the wrong spot. (you could save x to another variable, and then reassign x to something else before calling the function, which might make the problem more obvious)

In your code you could have written it as:

int conditional;
conditional = setjump(buf) != 0 ;
if(__builtin_expect( conditional, 1)) {
    // handle error
} else {
    // do action
}

And I think that we can satisfy ourselves that the line of code that assigns the variable conditional meets that requirement.

nategoose
  • 12,054
  • 27
  • 42
  • Actually even your second is not defined. You must declare conditional volatile for that (to inhibit register optimization). – Joshua Jan 07 '11 at 02:34
  • 1
    @Joshua: What are you talking about? The value returned by `setjmp` is a regular integer and does not need special care. The rest of the program needs special care because the state has the opportunity to change after the call to `setjmp` was made, but before calls to `longjmp`. It's similar to writing `int x = 5, y = 6; while (y--) { printf("x == %u\n", x); x=0; }` and expecting it to always say that x is 5 because that is what it was assigned to last by line number, but you are ignoring future values (except w/ setjmp/longjmp loops the issue could be amplified). – nategoose Jan 07 '11 at 16:41
  • The spec actually says any non-volatile variable assigned after the setjmp() call is undefined after the longjmp call(). – Joshua Jan 07 '11 at 17:00
  • 2
    @Joshua: But the non-volatile variable assignment we are interested in is the one made after the `longjmp` call. We aren't interested in the version of the assignment made between the initial `setjmp` and the `longjmp` call that results in the second returning of `setjmp`. I believe you are misinterpreting the standard, and from both a theoretical and a compiler implementation perspective the return value would always be in a (temporary) variable, even if I hadn't named it, so your interpretation would make the `setjmp` and `longjmp` completely useless. – nategoose Jan 07 '11 at 20:15
  • @nategoose: There is a rule you can read at http://pubs.opengroup.org/onlinepubs/009696799/functions/setjmp.html which limits the use of `setjmp` to certain contexts, none of which allow its value to be persisted. Having restrictions is reasonable, though I think those restrictions go absurdly far. I can't think of any reason any implementations would have had any difficulty with allowing assignment to a volatile-declared automatic as a permissible scenario, but the standard doesn't seem to allow it. – supercat Sep 05 '13 at 23:58
0

But if this is a GCC extension, can I guarantee that it will work under for GCC, since it's already nonstandard functionality? I tested it and it seemed to work, though I don't know how much testing I'd have to do to actually break it. (I'm hiding the call to __builtin_expect behind a macro, which is defined as a no-op for non-GCC, so it would be perfectly legal for other compilers.)

You are correct, __builtin_expect should be a macro no-op for other compilers so the result is still defined.

Joshua
  • 40,822
  • 8
  • 72
  • 132
  • I'm not worried about other compilers. Does GCC make any guarantee that `__builtin_expect` works in this situation, or is it just something I have to hope for? – Chris Lutz Jan 07 '11 at 02:42
  • __builtin_expect is the same kind of thing as #pragma. Read the docs. – Joshua Jan 07 '11 at 03:58
  • Personally, I have every reason to think gcc will do the right thing. – Joshua Jan 07 '11 at 04:30