gxxint.texi

来自「GCC编译器源代码」· TEXI 代码 · 共 1,820 行 · 第 1/5 页

TEXI
1,820
字号
Unlike the others, this ambiguity is not recognized by the Working Paper.@node  Copying Objects, Exception Handling, Parser, Top@section Copying ObjectsThe generated copy assignment operator in g++ does not currently do theright thing for multiple inheritance involving virtual bases; it justcalls the copy assignment operators for its direct bases.  What itshould probably do is:1) Split up the copy assignment operator for all classes that havevbases into "copy my vbases" and "copy everything else" parts.  Or dothe trickiness that the constructors do to ensure that vbases don't getinitialized by intermediate bases.2) Wander through the class lattice, find all vbases for which nointermediate base has a user-defined copy assignment operator, and calltheir "copy everything else" routines.  If not all of my vbases satisfythis criterion, warn, because this may be surprising behavior.3) Call the "copy everything else" routine for my direct bases.If we only have one direct base, we can just foist everything off ontothem.This issue is currently under discussion in the core reflector(2/28/94).@node  Exception Handling, Free Store, Copying Objects, Top@section Exception HandlingNote, exception handling in g++ is still under development.  This section describes the mapping of C++ exceptions in the C++front-end, into the back-end exception handling framework.The basic mechanism of exception handling in the back-end isunwind-protect a la elisp.  This is a general, robust, and languageindependent representation for exceptions.The C++ front-end exceptions are mapping into the unwind-protectsemantics by the C++ front-end.  The mapping is describe below.When -frtti is used, rtti is used to do exception object type checking,when it isn't used, the encoded name for the type of the object beingthrown is used instead.  All code that originates exceptions, even codethat throws exceptions as a side effect, like dynamic casting, and allcode that catches exceptions must be compiled with either -frtti, or-fno-rtti.  It is not possible to mix rtti base exception handlingobjects with code that doesn't use rtti.  The exceptions to this, arecode that doesn't catch or throw exceptions, catch (...), and code thatjust rethrows an exception.Currently we use the normal mangling used in building functions names(int's are "i", const char * is PCc) to build the non-rtti base typedescriptors for exception handling.  These descriptors are just plainNULL terminated strings, and internally they are passed around as char*.In C++, all cleanups should be protected by exception regions.  Theregion starts just after the reason why the cleanup is created hasended.  For example, with an automatic variable, that has a constructor,it would be right after the constructor is run.  The region ends justbefore the finalization is expanded.  Since the backend may expand thecleanup multiple times along different paths, once for normal end of theregion, once for non-local gotos, once for returns, etc, the backendmust take special care to protect the finalization expansion, if theexpansion is for any other reason than normal region end, and it is`inline' (it is inside the exception region).  The backend can eitherchoose to move them out of line, or it can created an exception regionover the finalization to protect it, and in the handler associated withit, it would not run the finalization as it otherwise would have, butrather just rethrow to the outer handler, careful to skip the normalhandler for the original region.In Ada, they will use the more runtime intensive approach of havingfewer regions, but at the cost of additional work at run time, to keep alist of things that need cleanups.  When a variable has finishedconstruction, they add the cleanup to the list, when the come to the endof the lifetime of the variable, the run the list down.  If the take ahit before the section finishes normally, they examine the list foractions to perform.  I hope they add this logic into the back-end, as itwould be nice to get that alternative approach in C++.On an rs6000, xlC stores exception objects on that stack, under the tryblock.  When is unwinds down into a handler, the frame pointer isadjusted back to the normal value for the frame in which the handlerresides, and the stack pointer is left unchanged from the time at whichthe object was thrown.  This is so that there is always someplace forthe exception object, and nothing can overwrite it, once we startthrowing.  The only bad part, is that the stack remains large.The below points out some things that work in g++'s exception handling.All completely constructed temps and local variables are cleaned up inall unwinded scopes.  Completely constructed parts of partiallyconstructed objects are cleaned up.  This includes partially builtarrays.  Exception specifications are now handled.  Thrown objects arenow cleaned up all the time.  We can now tell if we have an activeexception being thrown or not (__eh_type != 0).  We use this to callterminate if someone does a throw; without there being an activeexception object.  uncaught_exception () works.  Exception handlingshould work right if you optimize.  Exception handling should work with-fpic or -fPIC.The below points out some flaws in g++'s exception handling, as it nowstands.Only exact type matching or reference matching of throw types works when-fno-rtti is used.  Only works on a SPARC (like Suns) (both -mflat and-mno-flat models work), SPARClite, Hitachi SH, i386, arm, rs6000,PowerPC, Alpha, mips, VAX, m68k and z8k machines.  SPARC v9 may notwork.  HPPA is mostly done, but throwing between a shared library anduser code doesn't yet work.  Some targets have support for data-drivenunwinding.  Partial support is in for all other machines, but a stackunwinder called __unwind_function has to be written, and added tolibgcc2 for them.  The new EH code doesn't rely upon the__unwind_function for C++ code, instead it creates per functionunwinders right inside the function, unfortunately, on many platformsthe definition of RETURN_ADDR_RTX in the tm.h file for the machine portis wrong.  See below for details on __unwind_function.  RTL_EXPRs for EHcond variables for && and || exprs should probably be wrapped inUNSAVE_EXPRs, and RTL_EXPRs tweaked so that they can be unsaved.We only do pointer conversions on exception matching a la 15.3 p2 case3: `A handler with type T, const T, T&, or const T& is a match for athrow-expression with an object of type E if [3]T is a pointer type andE is a pointer type that can be converted to T by a standard pointerconversion (_conv.ptr_) not involving conversions to pointers to privateor protected base classes.' when -frtti is given.We don't call delete on new expressions that die because the ctor threwan exception.  See except/18 for a test case.15.2 para 13: The exception being handled should be rethrown if controlreaches the end of a handler of the function-try-block of a constructoror destructor, right now, it is not.15.2 para 12: If a return statement appears in a handler offunction-try-block of a constructor, the program is ill-formed, but thisisn't diagnosed.15.2 para 11: If the handlers of a function-try-block contain a jumpinto the body of a constructor or destructor, the program is ill-formed,but this isn't diagnosed.15.2 para 9: Check that the fully constructed base classes and membersof an object are destroyed before entering the handler of afunction-try-block of a constructor or destructor for that object.build_exception_variant should sort the incoming list, so that itimplements set compares, not exact list equality.  Type smashing shouldsmash exception specifications using set union.Thrown objects are usually allocated on the heap, in the usual way.  Ifone runs out of heap space, throwing an object will probably never work.This could be relaxed some by passing an __in_chrg parameter to trackwho has control over the exception object.  Thrown objects are notallocated on the heap when they are pointer to object types.  We shouldextend it so that all small (<4*sizeof(void*)) objects are storeddirectly, instead of allocated on the heap.When the backend returns a value, it can create new exception regionsthat need protecting.  The new region should rethrow the object incontext of the last associated cleanup that ran to completion.The structure of the code that is generated for C++ exception handlingcode is shown below:@exampleLn:					throw value;        copy value onto heap        jump throw (Ln, id, address of copy of value on heap)                                        try @{+Lstart:	the start of the main EH region|...						...+Lend:		the end of the main EH region                                        @} catch (T o) @{						...1                                        @}Lresume:        nop	used to make sure there is something before                the next region ends, if there is one...                                     ...        jump Ldone[Lmainhandler:    handler for the region Lstart-Lend	cleanup] zero or more, depending upon automatic vars with dtors+Lpartial:|        jump Lover+Lhere:        rethrow (Lhere, same id, same obj);Lterm:		handler for the region Lpartial-Lhere        call terminateLover:[ [        call throw_type_match        if (eq) @{ ] these lines disappear when there is no catch condition+Lsregion2:|	...1|	jump Lresume|Lhandler:	handler for the region Lsregion2-Leregion2|	rethrow (Lresume, same id, same obj);+Leregion2        @}] there are zero or more of these sections, depending upon how many  catch clauses there are----------------------------- expand_end_all_catch --------------------------                here we have fallen off the end of all catch                clauses, so we rethrow to outer        rethrow (Lresume, same id, same obj);----------------------------- expand_end_all_catch --------------------------[L1:     maybe throw routine] depending upon if we have expanded it or notLdone:        retstart_all_catch emits labels: Lresume, @end exampleThe __unwind_function takes a pointer to the throw handler, and isexpected to pop the stack frame that was built to call it, as well asthe frame underneath and then jump to the throw handler.  It mustrestore all registers to their proper values as well as all othermachine state as determined by the context in which we are unwindinginto.  The way I normally start is to compile:        void *g;        foo(void* a) @{ g = a; @}with -S, and change the thing that alters the PC (return, or retusually) to not alter the PC, making sure to leave all other semantics(like adjusting the stack pointer, or frame pointers) in.  After that,replicate the prologue once more at the end, again, changing the PCaltering instructions, and finally, at the very end, jump to `g'.It takes about a week to write this routine, if someone wants tovolunteer to write this routine for any architecture, exception supportfor that architecture will be added to g++.  Please send in those codedonations.  One other thing that needs to be done, is to double checkthat __builtin_return_address (0) works.@subsection Specific TargetsFor the alpha, the __unwind_function will be something resembling:@examplevoid__unwind_function(void *ptr)@{  /* First frame */  asm ("ldq $15, 8($30)"); /* get the saved frame ptr; 15 is fp, 30 is sp */  asm ("bis $15, $15, $30"); /* reload sp with the fp we found */  /* Second frame */  asm ("ldq $15, 8($30)"); /* fp */  asm ("bis $15, $15, $30"); /* reload sp with the fp we found */  /* Return */  asm ("ret $31, ($16), 1"); /* return to PTR, stored in a0 */@}@end example@noindentHowever, there are a few problems preventing it from working.  First ofall, the gcc-internal function @code{__builtin_return_address} needs towork given an argument of 0 for the alpha.  As it stands as of August30th, 1995, the code for @code{BUILT_IN_RETURN_ADDRESS} in @file{expr.c}will definitely not work on the alpha.  Instead, we need to definethe macros @code{DYNAMIC_CHAIN_ADDRESS} (maybe),@code{RETURN_ADDR_IN_PREVIOUS_FRAME}, and definitely need a newdefinition for @code{RETURN_ADDR_RTX}.In addition (and more importantly), we need a way to reliably find theframe pointer on the alpha.  The use of the value 8 above to restore theframe pointer (register 15) is incorrect.  On many systems, the framepointer is consistently offset to a specific point on the stack.  On thealpha, however, the frame pointer is pushed last.  First the returnaddress is stored, then any other registers are saved (e.g., @code{s0}),and finally the frame pointer is put in place.  So @code{fp} could havean offset of 8, but if the calling function saved any registers at all,they add to the offset.The only places the frame size is noted are with the @samp{.frame}directive, for use by the debugger and the OSF exception handling model(useless to us), and in the initial computation of the new value for@code{sp}, the stack pointer.  For example, the function may start with:@examplelda $30,-32($30).frame $15,32,$26,0@end example @noindentThe 32 above is exactly the value we need.  With this, we can be surethat the frame pointer is stored 8 bytes less---in this case, at 24(sp)).The drawback is that there is no way that I (Brendan) have found to letus discover the size of a previous frame @emph{inside} the definitionof @code{__unwind_function}.So to accomplish exception handling support on the alpha, we need twothings: first, a way to figure out where the frame pointer was stored,and second, a functional @code{__builtin_return_address} implementationfor except.c to be able to use it.Or just support DWARF 2 unwind info.@subsection New Backend Exception SupportThis subsection discusses various aspects of the design of thedata-driven model being implemented for the exception handling backend.The goal is to generate enough data during the compilation of user code,such that we can dynamically unwind through functions at run time with asingle routine (@code{__throw}) that lives in libgcc.a, built by thecompiler, and dispatch into associated exception handlers.This information is generated by the DWARF 2 debugging backend, andincludes all of the information __throw needs to unwind an arbitraryframe.  It specifies where all of the saved registers and the returnaddress can be found at any point in the function.Major disadvantages when enabling exceptions are:@itemize @bullet@itemCode that uses caller saved registers, can't, when flow can betransferred into that code from an exception handler.  In high performancecode this should not usually be true, so the effects should be minimal.@end itemize@subsection Backend Exception SupportThe backend must be extended to fully support exceptions.  Right nowthere are a few hooks into the alpha exception handling backend thatresides in the C++ frontend from that backend that allows exceptionhandling to work in g++.  An exception region is a segment of generatedcode that has a handler associated with it.  The exception regions aredenoted in the generated code as address ranges denoted by a starting PCvalue and an ending PC value of the region.  Some of the limitationswith this scheme are:@itemize @bullet@itemThe backend replicates insns for such things as loop unrolling andfunction inlining.  Right now, there are no hooks into the frontend'sexception handling backend to handle the replication of insns.  Whenreplication happens, a new exception region descriptor needs to begenerated for the new region.@itemThe backend expects to be able to rearrange code, for things like jumpoptimization.  Any rearranging of the code needs have exception regiondescriptors updated appropriately.

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?