📄 gxxint.texi
字号:
choose 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.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), i386, arm andrs6000 machines. Partial support is in for all other machines, but astack unwinder called __unwind_function has to be written, and added tolibgcc2 for them. See below for details on __unwind_function. Don'texpect exception handling to work right if you optimize, in fact thecompiler will probably core dump. RTL_EXPRs for EH cond variables for&& and || exprs should probably be wrapped in UNSAVE_EXPRs, andRTL_EXPRs tweaked so that they can be unsaved, and the UNSAVE_EXPR codeshould be in the backend, or alternatively, UNSAVE_EXPR should be rippedout and exactly one finalization allowed to be expanded by the backend.I talked with kenner about this, and we have to allow multipleexpansions.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, butthey are never deleted. They should be deleted by the catch clauses.If one runs out of heap space, throwing an object will probably neverwork. This could be relaxed some by passing an __in_chrg parameter totrack who has control over the exception object. Thrown objects are notallocated on the heap when they are pointer to object types.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.@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.@itemThe backend can eliminate dead code. Any associated exception regiondescriptor that refers to fully contained code that has been eliminatedshould also be removed, although not doing this is harmless in terms ofsemantics.#end itemizeThe above is not meant to be exhaustive, but does include all things Ihave thought of so far. I am sure other limitations exist.Below are some notes on the migration of the exception handling codebackend from the C++ frontend to the backend.NOTEs are to be used to denote the start of an exception region, and theend of the region. I presume that the interface used to generate thesenotes in the backend would be two functions, start_exception_region andend_exception_region (or something like that). The frontends arerequired to call them in pairs. When marking the end of a region, anargument can be passed to indicate the handler for the marked region.This can be passed in many ways, currently a tree is used. Anotherpossibility would be insns for the handler, or a label that denotes ahandler. I have a feeling insns might be the the best way to pass it.Semantics are, if an exception is thrown inside the region, control istransfered unconditionally to the handler. If control passes throughthe handler, then the backend is to rethrow the exception, in thecontext of the end of the original region. The handler is protected bythe conventional mechanisms; it is the frontend's responsibility toprotect the handler, if special semantics are required.This is a very low level view, and it would be nice is the backendsupported a somewhat higher level view in addition to this view. Thishigher level could include source line number, name of the source file,name of the language that threw the exception and possibly the name ofthe exception. Kenner may want to rope you into doing more than justthe basics required by C++. You will have to resolve this. He may wantyou to do support for non-local gotos, first scan for exception handler,if none is found, allow the debugger to be entered, without any cleanupsbeing done. To do this, the backend would have to know the differencebetween a cleanup-rethrower, and a real handler, if would also have tohave a way to know if a handler `matches' a thrown exception, and thisis frontend specific.The UNSAVE_EXPR tree code has to be migrated to the backend. Exprs suchas TARGET_EXPRs, WITH_CLEANUP_EXPRs, CALL_EXPRs and RTL_EXPRs have to bechanged to support unsaving. This is meant to be a complete list.SAVE_EXPRs can be unsaved already. expand_decl_cleanup should bechanged to unsave it's argument, if needed. Seecp/tree.c:cp_expand_decl_cleanup, unsave_expr_now, unsave_expr, andcp/expr.c:cplus_expand_expr(case UNSAVE_EXPR:) for the UNSAVE_EXPR code.Now, as to why... because kenner already tripped over the exact sameproblem in Ada, we talked about it, he didn't like any of the solution,but yet, didn't like no solution either. He was willing to live withthe drawbacks of this solution. The drawback is unsave_expr_now. Itshould have a callback into the frontend, to allow the unsaveing offrontend special codes. The callback goes in, inplace of the call tomy_friendly_abort.The stack unwinder is one of the hardest parts to do. It is highlymachine dependent. The form that kenner seems to like was a couple ofmacros, that would do the machine dependent grunt work. One preexistingfunction that might be of some use is __builtin_return_address (). Onemacro he seemed to want was __builtin_return_address, and the otherwould do the hard work of fixing up the registers, adjusting the stackpointer, frame pointer, arg pointer and so on.The eh archive (~mrs/eh) might be good reading for understanding the Adaperspective, and some of kenners mindset, and a detailed explanation(Message-Id: <9308301130.AA10543@vlsi1.ultra.nyu.edu>) of the conceptsinvolved.Here is a guide to existing backend type code. It is all incp/except.c. Check out do_unwind, and expand_builtin_throw for currentcode on how to figure out what handler matches an exception,emit_exception_table for code on emitting the PC range table that isbuilt during compilation, expand_exception_blocks for code that emitsall the handlers at the end of a functions, end_protect to mark the endof an exception region, start_protect to mark the start of an exceptionregion, lang_interim_eh is the master hook used by the backend into theEH backend that now exists in the frontend, and expand_internal_throw toraise an exception.@node Free Store, Concept Index, Exception Handling, Top@section Free Storeoperator new [] adds a magic cookie to the beginning of arrays for whichthe number of elements will be needed by operator delete []. These arearrays of objects with destructors and arrays of objects that defineoperator delete [] with the optional size_t argument. This cookie canbe examined from a program as follows:@exampletypedef unsigned long size_t;extern "C" int printf (const char *, ...);size_t nelts (void *p)@{ struct cookie @{ size_t nelts __attribute__ ((aligned (sizeof (double)))); @}; cookie *cp = (cookie *)p; --cp; return cp->nelts;@}struct A @{ ~A() @{ @}@};main()@{ A *ap = new A[3]; printf ("%ld\n", nelts (ap));@}@end example@section LinkageThe linkage code in g++ is horribly twisted in order to meet two design goals:1) Avoid unnecessary emission of inlines and vtables.2) Support pedantic assemblers like the one in AIX.To meet the first goal, we defer emission of inlines and vtables untilthe end of the translation unit, where we can decide whether or not theyare needed, and how to emit them if they are.@node Concept Index, , Free Store, Top@section Concept Index@printindex cp@bye
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -