Well, dwarf2 builds up tables for every function, that contain what the callee saved registers are and where in the stack they are saved, and where the frame pointer/return address in the callstack is, and some other stuff. If you use dwarf2, the compiler can use those informations and effectively restore registers, and jump back to callers in the event of an exception. The backends need to provide informations in their implementations' prologue generating code, to tell GCC which registers are callee-saved, and when the frame pointer was saved and such stuff.
Using setjmp/longjmp is just a hack. Since setjmp/longjmp does not know about the structure of the function throwing, it will restore all registers saved in the jump-buffer by setjmp, even if they were not overridden by the throwing function. I'm not really an expert for this, but i think it's obvious that this will not be efficient. Also, everytime you start a try block, setjmp has to be called to set up the buffer containing the saved registers, while when using dwarf2, the compiler already provides all necassary informations at compile time.
If the backends do not provide the necassary information, GCC will automatically fall-back to setjmp/longjmp based exception handling.
Note i'm not a GCC expert. I just ported the toolchain to some easy processor of my professor, including GCC. I hope i could help you a bit.