GCC 支持 Setjump-longjump (sjlj) 和 Dwarf2 基于表的展开 (dw2) 异常处理模型。两种型号有什么区别,如何选择合适的型号?为什么 Dwarf2 基于表的展开 (dw2) 是更有效的模型?我知道这两种型号不能混用。
2 回答
好吧,dwarf2 为每个函数建立了表,其中包含被调用者保存的寄存器是什么,它们在堆栈中保存的位置,调用堆栈中的帧指针/返回地址在哪里,以及其他一些东西。如果使用 dwarf2,编译器可以使用这些信息并有效地恢复寄存器,并在发生异常时跳转回调用者。后端需要在其实现的序言生成代码中提供信息,告诉 GCC 哪些寄存器是被调用者保存的,以及何时保存帧指针等等。
使用 setjmp/longjmp 只是一个技巧。由于 setjmp/longjmp 不知道抛出函数的结构,因此它将恢复 setjmp 保存在跳转缓冲区中的所有寄存器,即使它们没有被抛出函数覆盖。我不是这方面的专家,但我认为这显然不会有效。此外,每次启动 try 块时,都必须调用 setjmp 来设置包含已保存寄存器的缓冲区,而使用 dwarf2 时,编译器已经在编译时提供了所有必要的信息。
如果后端没有提供必要的信息,GCC 将自动回退到基于 setjmp/longjmp 的异常处理。
请注意,我不是 GCC 专家。我刚刚将工具链移植到我教授的一些简单处理器上,包括 GCC。我希望我能帮助你一点。
避免sjlj。每个“try”块都会调用 setjmp 来保存寄存器,即使没有引发异常也会影响性能。使用表,正常的控制流程不会产生执行成本。只有在引发异常时,异常处理机制才必须通过表格来确定要做什么。