void EHStreamer::computePadMap( const SmallVectorImpl<const LandingPadInfo *> &LandingPads, RangeMapType &PadMap) { // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced so we can put them in the LSDA. for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } }
/// EmitExceptionTable - Emit landing pads and actions. /// /// The general organization of the table is complex, but the basic concepts are /// easy. First there is a header which describes the location and organization /// of the three components that follow. /// /// 1. The landing pad site information describes the range of code covered by /// the try. In our case it's an accumulation of the ranges covered by the /// invokes in the try. There is also a reference to the landing pad that /// handles the exception once processed. Finally an index into the actions /// table. /// 2. The action table, in our case, is composed of pairs of type IDs and next /// action offset. Starting with the action index from the landing pad /// site, each type ID is checked for a match to the current exception. If /// it matches then the exception and type id are passed on to the landing /// pad. Otherwise the next action is looked up. This chain is terminated /// with a next action of zero. If no type id is found then the frame is /// unwound and handling continues. /// 3. Type ID table contains references to all the C++ typeinfo for all /// catches in the function. This tables is reverse indexed base 1. void DwarfException::EmitExceptionTable() { const std::vector<const GlobalVariable *> &TypeInfos = MMI->getTypeInfos(); const std::vector<unsigned> &FilterIds = MMI->getFilterIds(); const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads(); // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector<const LandingPadInfo *, 64> LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), PadLT); // Compute the actions table and gather the first action index for each // landing pad site. SmallVector<ActionEntry, 32> Actions; SmallVector<unsigned, 64> FirstActions; unsigned SizeActions=ComputeActionsTable(LandingPads, Actions, FirstActions); // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced when using DWARF exception handling. RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } // Compute the call-site table. SmallVector<CallSiteEntry, 64> CallSites; ComputeCallSiteTable(CallSites, PadMap, LandingPads, FirstActions); // Final tallies. // Call sites. bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; bool HaveTTData = IsSJLJ ? (!TypeInfos.empty() || !FilterIds.empty()) : true; unsigned CallSiteTableLength; if (IsSJLJ) CallSiteTableLength = 0; else { unsigned SiteStartSize = 4; // dwarf::DW_EH_PE_udata4 unsigned SiteLengthSize = 4; // dwarf::DW_EH_PE_udata4 unsigned LandingPadSize = 4; // dwarf::DW_EH_PE_udata4 CallSiteTableLength = CallSites.size() * (SiteStartSize + SiteLengthSize + LandingPadSize); } for (unsigned i = 0, e = CallSites.size(); i < e; ++i) { CallSiteTableLength += MCAsmInfo::getULEB128Size(CallSites[i].Action); if (IsSJLJ) CallSiteTableLength += MCAsmInfo::getULEB128Size(i); } // Type infos. const MCSection *LSDASection = Asm->getObjFileLowering().getLSDASection(); unsigned TTypeEncoding; unsigned TypeFormatSize; if (!HaveTTData) { // For SjLj exceptions, if there is no TypeInfo, then we just explicitly say // that we're omitting that bit. TTypeEncoding = dwarf::DW_EH_PE_omit; // dwarf::DW_EH_PE_absptr TypeFormatSize = Asm->getDataLayout().getPointerSize(); } else { // Okay, we have actual filters or typeinfos to emit. As such, we need to // pick a type encoding for them. We're about to emit a list of pointers to // typeinfo objects at the end of the LSDA. However, unless we're in static // mode, this reference will require a relocation by the dynamic linker. // // Because of this, we have a couple of options: // // 1) If we are in -static mode, we can always use an absolute reference // from the LSDA, because the static linker will resolve it. // // 2) Otherwise, if the LSDA section is writable, we can output the direct // reference to the typeinfo and allow the dynamic linker to relocate // it. Since it is in a writable section, the dynamic linker won't // have a problem. // // 3) Finally, if we're in PIC mode and the LDSA section isn't writable, // we need to use some form of indirection. For example, on Darwin, // we can output a statically-relocatable reference to a dyld stub. The // offset to the stub is constant, but the contents are in a section // that is updated by the dynamic linker. This is easy enough, but we // need to tell the personality function of the unwinder to indirect // through the dyld stub. // // FIXME: When (3) is actually implemented, we'll have to emit the stubs // somewhere. This predicate should be moved to a shared location that is // in target-independent code. // TTypeEncoding = Asm->getObjFileLowering().getTTypeEncoding(); TypeFormatSize = Asm->GetSizeOfEncodedValue(TTypeEncoding); } // Begin the exception table. // Sometimes we want not to emit the data into separate section (e.g. ARM // EHABI). In this case LSDASection will be NULL. if (LSDASection) Asm->OutStreamer.SwitchSection(LSDASection); Asm->EmitAlignment(2); // Emit the LSDA. MCSymbol *GCCETSym = Asm->OutContext.GetOrCreateSymbol(Twine("GCC_except_table")+ Twine(Asm->getFunctionNumber())); Asm->OutStreamer.EmitLabel(GCCETSym); Asm->OutStreamer.EmitLabel(Asm->GetTempSymbol("exception", Asm->getFunctionNumber())); if (IsSJLJ) Asm->OutStreamer.EmitLabel(Asm->GetTempSymbol("_LSDA_", Asm->getFunctionNumber())); // Emit the LSDA header. Asm->EmitEncodingByte(dwarf::DW_EH_PE_omit, "@LPStart"); Asm->EmitEncodingByte(TTypeEncoding, "@TType"); // The type infos need to be aligned. GCC does this by inserting padding just // before the type infos. However, this changes the size of the exception // table, so you need to take this into account when you output the exception // table size. However, the size is output using a variable length encoding. // So by increasing the size by inserting padding, you may increase the number // of bytes used for writing the size. If it increases, say by one byte, then // you now need to output one less byte of padding to get the type infos // aligned. However this decreases the size of the exception table. This // changes the value you have to output for the exception table size. Due to // the variable length encoding, the number of bytes used for writing the // length may decrease. If so, you then have to increase the amount of // padding. And so on. If you look carefully at the GCC code you will see that // it indeed does this in a loop, going on and on until the values stabilize. // We chose another solution: don't output padding inside the table like GCC // does, instead output it before the table. unsigned SizeTypes = TypeInfos.size() * TypeFormatSize; unsigned CallSiteTableLengthSize = MCAsmInfo::getULEB128Size(CallSiteTableLength); unsigned TTypeBaseOffset = sizeof(int8_t) + // Call site format CallSiteTableLengthSize + // Call site table length size CallSiteTableLength + // Call site table length SizeActions + // Actions size SizeTypes; unsigned TTypeBaseOffsetSize = MCAsmInfo::getULEB128Size(TTypeBaseOffset); unsigned TotalSize = sizeof(int8_t) + // LPStart format sizeof(int8_t) + // TType format (HaveTTData ? TTypeBaseOffsetSize : 0) + // TType base offset size TTypeBaseOffset; // TType base offset unsigned SizeAlign = (4 - TotalSize) & 3; if (HaveTTData) { // Account for any extra padding that will be added to the call site table // length. Asm->EmitULEB128(TTypeBaseOffset, "@TType base offset", SizeAlign); SizeAlign = 0; } bool VerboseAsm = Asm->OutStreamer.isVerboseAsm(); // SjLj Exception handling if (IsSJLJ) { Asm->EmitEncodingByte(dwarf::DW_EH_PE_udata4, "Call site"); // Add extra padding if it wasn't added to the TType base offset. Asm->EmitULEB128(CallSiteTableLength, "Call site table length", SizeAlign); // Emit the landing pad site information. unsigned idx = 0; for (SmallVectorImpl<CallSiteEntry>::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I, ++idx) { const CallSiteEntry &S = *I; // Offset of the landing pad, counted in 16-byte bundles relative to the // @LPStart address. if (VerboseAsm) { Asm->OutStreamer.AddComment(">> Call Site " + Twine(idx) + " <<"); Asm->OutStreamer.AddComment(" On exception at call site "+Twine(idx)); } Asm->EmitULEB128(idx); // Offset of the first associated action record, relative to the start of // the action table. This value is biased by 1 (1 indicates the start of // the action table), and 0 indicates that there are no actions. if (VerboseAsm) { if (S.Action == 0) Asm->OutStreamer.AddComment(" Action: cleanup"); else Asm->OutStreamer.AddComment(" Action: " + Twine((S.Action - 1) / 2 + 1)); } Asm->EmitULEB128(S.Action); } } else { // DWARF Exception handling assert(Asm->MAI->isExceptionHandlingDwarf()); // The call-site table is a list of all call sites that may throw an // exception (including C++ 'throw' statements) in the procedure // fragment. It immediately follows the LSDA header. Each entry indicates, // for a given call, the first corresponding action record and corresponding // landing pad. // // The table begins with the number of bytes, stored as an LEB128 // compressed, unsigned integer. The records immediately follow the record // count. They are sorted in increasing call-site address. Each record // indicates: // // * The position of the call-site. // * The position of the landing pad. // * The first action record for that call site. // // A missing entry in the call-site table indicates that a call is not // supposed to throw. // Emit the landing pad call site table. Asm->EmitEncodingByte(dwarf::DW_EH_PE_udata4, "Call site"); // Add extra padding if it wasn't added to the TType base offset. Asm->EmitULEB128(CallSiteTableLength, "Call site table length", SizeAlign); unsigned Entry = 0; for (SmallVectorImpl<CallSiteEntry>::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I) { const CallSiteEntry &S = *I; MCSymbol *EHFuncBeginSym = Asm->GetTempSymbol("eh_func_begin", Asm->getFunctionNumber()); MCSymbol *BeginLabel = S.BeginLabel; if (BeginLabel == 0) BeginLabel = EHFuncBeginSym; MCSymbol *EndLabel = S.EndLabel; if (EndLabel == 0) EndLabel = Asm->GetTempSymbol("eh_func_end", Asm->getFunctionNumber()); // Offset of the call site relative to the previous call site, counted in // number of 16-byte bundles. The first call site is counted relative to // the start of the procedure fragment. if (VerboseAsm) Asm->OutStreamer.AddComment(">> Call Site " + Twine(++Entry) + " <<"); Asm->EmitLabelDifference(BeginLabel, EHFuncBeginSym, 4); if (VerboseAsm) Asm->OutStreamer.AddComment(Twine(" Call between ") + BeginLabel->getName() + " and " + EndLabel->getName()); Asm->EmitLabelDifference(EndLabel, BeginLabel, 4); // Offset of the landing pad, counted in 16-byte bundles relative to the // @LPStart address. if (!S.PadLabel) { if (VerboseAsm) Asm->OutStreamer.AddComment(" has no landing pad"); Asm->OutStreamer.EmitIntValue(0, 4/*size*/, 0/*addrspace*/); } else { if (VerboseAsm) Asm->OutStreamer.AddComment(Twine(" jumps to ") + S.PadLabel->getName()); Asm->EmitLabelDifference(S.PadLabel, EHFuncBeginSym, 4); } // Offset of the first associated action record, relative to the start of // the action table. This value is biased by 1 (1 indicates the start of // the action table), and 0 indicates that there are no actions. if (VerboseAsm) { if (S.Action == 0) Asm->OutStreamer.AddComment(" On action: cleanup"); else Asm->OutStreamer.AddComment(" On action: " + Twine((S.Action - 1) / 2 + 1)); } Asm->EmitULEB128(S.Action); } } // Emit the Action Table. int Entry = 0; for (SmallVectorImpl<ActionEntry>::const_iterator I = Actions.begin(), E = Actions.end(); I != E; ++I) { const ActionEntry &Action = *I; if (VerboseAsm) { // Emit comments that decode the action table. Asm->OutStreamer.AddComment(">> Action Record " + Twine(++Entry) + " <<"); } // Type Filter // // Used by the runtime to match the type of the thrown exception to the // type of the catch clauses or the types in the exception specification. if (VerboseAsm) { if (Action.ValueForTypeID > 0) Asm->OutStreamer.AddComment(" Catch TypeInfo " + Twine(Action.ValueForTypeID)); else if (Action.ValueForTypeID < 0) Asm->OutStreamer.AddComment(" Filter TypeInfo " + Twine(Action.ValueForTypeID)); else Asm->OutStreamer.AddComment(" Cleanup"); } Asm->EmitSLEB128(Action.ValueForTypeID); // Action Record // // Self-relative signed displacement in bytes of the next action record, // or 0 if there is no next action record. if (VerboseAsm) { if (Action.NextAction == 0) { Asm->OutStreamer.AddComment(" No further actions"); } else { unsigned NextAction = Entry + (Action.NextAction + 1) / 2; Asm->OutStreamer.AddComment(" Continue to action "+Twine(NextAction)); } } Asm->EmitSLEB128(Action.NextAction); } // Emit the Catch TypeInfos. if (VerboseAsm && !TypeInfos.empty()) { Asm->OutStreamer.AddComment(">> Catch TypeInfos <<"); Asm->OutStreamer.AddBlankLine(); Entry = TypeInfos.size(); } for (std::vector<const GlobalVariable *>::const_reverse_iterator I = TypeInfos.rbegin(), E = TypeInfos.rend(); I != E; ++I) { const GlobalVariable *GV = *I; if (VerboseAsm) Asm->OutStreamer.AddComment("TypeInfo " + Twine(Entry--)); if (GV) Asm->EmitReference(GV, TTypeEncoding); else Asm->OutStreamer.EmitIntValue(0,Asm->GetSizeOfEncodedValue(TTypeEncoding), 0); } // Emit the Exception Specifications. if (VerboseAsm && !FilterIds.empty()) { Asm->OutStreamer.AddComment(">> Filter TypeInfos <<"); Asm->OutStreamer.AddBlankLine(); Entry = 0; } for (std::vector<unsigned>::const_iterator I = FilterIds.begin(), E = FilterIds.end(); I < E; ++I) { unsigned TypeID = *I; if (VerboseAsm) { --Entry; if (TypeID != 0) Asm->OutStreamer.AddComment("FilterInfo " + Twine(Entry)); } Asm->EmitULEB128(TypeID); } Asm->EmitAlignment(2); }
unsigned char* JITDwarfEmitter::EmitExceptionTable(MachineFunction* MF, unsigned char* StartFunction, unsigned char* EndFunction) const { assert(MMI && "MachineModuleInfo not registered!"); // Map all labels and get rid of any dead landing pads. MMI->TidyLandingPads(JCE->getLabelLocations()); const std::vector<const GlobalVariable *> &TypeInfos = MMI->getTypeInfos(); const std::vector<unsigned> &FilterIds = MMI->getFilterIds(); const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads(); if (PadInfos.empty()) return 0; // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector<const LandingPadInfo *, 64> LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), PadLT); // Negative type ids index into FilterIds, positive type ids index into // TypeInfos. The value written for a positive type id is just the type // id itself. For a negative type id, however, the value written is the // (negative) byte offset of the corresponding FilterIds entry. The byte // offset is usually equal to the type id, because the FilterIds entries // are written using a variable width encoding which outputs one byte per // entry as long as the value written is not too large, but can differ. // This kind of complication does not occur for positive type ids because // type infos are output using a fixed width encoding. // FilterOffsets[i] holds the byte offset corresponding to FilterIds[i]. SmallVector<int, 16> FilterOffsets; FilterOffsets.reserve(FilterIds.size()); int Offset = -1; for(std::vector<unsigned>::const_iterator I = FilterIds.begin(), E = FilterIds.end(); I != E; ++I) { FilterOffsets.push_back(Offset); Offset -= MCAsmInfo::getULEB128Size(*I); } // Compute the actions table and gather the first action index for each // landing pad site. SmallVector<ActionEntry, 32> Actions; SmallVector<unsigned, 64> FirstActions; FirstActions.reserve(LandingPads.size()); int FirstAction = 0; unsigned SizeActions = 0; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LP = LandingPads[i]; const std::vector<int> &TypeIds = LP->TypeIds; const unsigned NumShared = i ? SharedTypeIds(LP, LandingPads[i-1]) : 0; unsigned SizeSiteActions = 0; if (NumShared < TypeIds.size()) { unsigned SizeAction = 0; ActionEntry *PrevAction = 0; if (NumShared) { const unsigned SizePrevIds = LandingPads[i-1]->TypeIds.size(); assert(Actions.size()); PrevAction = &Actions.back(); SizeAction = MCAsmInfo::getSLEB128Size(PrevAction->NextAction) + MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); for (unsigned j = NumShared; j != SizePrevIds; ++j) { SizeAction -= MCAsmInfo::getSLEB128Size(PrevAction->ValueForTypeID); SizeAction += -PrevAction->NextAction; PrevAction = PrevAction->Previous; } } // Compute the actions. for (unsigned I = NumShared, M = TypeIds.size(); I != M; ++I) { int TypeID = TypeIds[I]; assert(-1-TypeID < (int)FilterOffsets.size() && "Unknown filter id!"); int ValueForTypeID = TypeID < 0 ? FilterOffsets[-1 - TypeID] : TypeID; unsigned SizeTypeID = MCAsmInfo::getSLEB128Size(ValueForTypeID); int NextAction = SizeAction ? -(SizeAction + SizeTypeID) : 0; SizeAction = SizeTypeID + MCAsmInfo::getSLEB128Size(NextAction); SizeSiteActions += SizeAction; ActionEntry Action = {ValueForTypeID, NextAction, PrevAction}; Actions.push_back(Action); PrevAction = &Actions.back(); } // Record the first action of the landing pad site. FirstAction = SizeActions + SizeSiteActions - SizeAction + 1; } // else identical - re-use previous FirstAction FirstActions.push_back(FirstAction); // Compute this sites contribution to size. SizeActions += SizeSiteActions; } // Compute the call-site table. Entries must be ordered by address. SmallVector<CallSiteEntry, 64> CallSites; RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j=0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } bool MayThrow = false; MCSymbol *LastLabel = 0; for (MachineFunction::const_iterator I = MF->begin(), E = MF->end(); I != E; ++I) { for (MachineBasicBlock::const_iterator MI = I->begin(), E = I->end(); MI != E; ++MI) { if (!MI->isLabel()) { MayThrow |= MI->getDesc().isCall(); continue; } MCSymbol *BeginLabel = MI->getOperand(0).getMCSymbol(); assert(BeginLabel && "Invalid label!"); if (BeginLabel == LastLabel) MayThrow = false; RangeMapType::iterator L = PadMap.find(BeginLabel); if (L == PadMap.end()) continue; PadRange P = L->second; const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && "Inconsistent landing pad map!"); // If some instruction between the previous try-range and this one may // throw, create a call-site entry with no landing pad for the region // between the try-ranges. if (MayThrow) { CallSiteEntry Site = {LastLabel, BeginLabel, 0, 0}; CallSites.push_back(Site); } LastLabel = LandingPad->EndLabels[P.RangeIndex]; CallSiteEntry Site = {BeginLabel, LastLabel, LandingPad->LandingPadLabel, FirstActions[P.PadIndex]}; assert(Site.BeginLabel && Site.EndLabel && Site.PadLabel && "Invalid landing pad!"); // Try to merge with the previous call-site. if (CallSites.size()) { CallSiteEntry &Prev = CallSites.back(); if (Site.PadLabel == Prev.PadLabel && Site.Action == Prev.Action) { // Extend the range of the previous entry. Prev.EndLabel = Site.EndLabel; continue; } } // Otherwise, create a new call-site. CallSites.push_back(Site); } } // If some instruction between the previous try-range and the end of the // function may throw, create a call-site entry with no landing pad for the // region following the try-range. if (MayThrow) { CallSiteEntry Site = {LastLabel, 0, 0, 0}; CallSites.push_back(Site); } // Final tallies. unsigned SizeSites = CallSites.size() * (sizeof(int32_t) + // Site start. sizeof(int32_t) + // Site length. sizeof(int32_t)); // Landing pad. for (unsigned i = 0, e = CallSites.size(); i < e; ++i) SizeSites += MCAsmInfo::getULEB128Size(CallSites[i].Action); unsigned SizeTypes = TypeInfos.size() * TD->getPointerSize(); unsigned TypeOffset = sizeof(int8_t) + // Call site format // Call-site table length MCAsmInfo::getULEB128Size(SizeSites) + SizeSites + SizeActions + SizeTypes; // Begin the exception table. JCE->emitAlignmentWithFill(4, 0); // Asm->EOL("Padding"); unsigned char* DwarfExceptionTable = (unsigned char*)JCE->getCurrentPCValue(); // Emit the header. JCE->emitByte(dwarf::DW_EH_PE_omit); // Asm->EOL("LPStart format (DW_EH_PE_omit)"); JCE->emitByte(dwarf::DW_EH_PE_absptr); // Asm->EOL("TType format (DW_EH_PE_absptr)"); JCE->emitULEB128Bytes(TypeOffset); // Asm->EOL("TType base offset"); JCE->emitByte(dwarf::DW_EH_PE_udata4); // Asm->EOL("Call site format (DW_EH_PE_udata4)"); JCE->emitULEB128Bytes(SizeSites); // Asm->EOL("Call-site table length"); // Emit the landing pad site information. for (unsigned i = 0; i < CallSites.size(); ++i) { CallSiteEntry &S = CallSites[i]; intptr_t BeginLabelPtr = 0; intptr_t EndLabelPtr = 0; if (!S.BeginLabel) { BeginLabelPtr = (intptr_t)StartFunction; JCE->emitInt32(0); } else { BeginLabelPtr = JCE->getLabelAddress(S.BeginLabel); JCE->emitInt32(BeginLabelPtr - (intptr_t)StartFunction); } // Asm->EOL("Region start"); if (!S.EndLabel) EndLabelPtr = (intptr_t)EndFunction; else EndLabelPtr = JCE->getLabelAddress(S.EndLabel); JCE->emitInt32(EndLabelPtr - BeginLabelPtr); //Asm->EOL("Region length"); if (!S.PadLabel) { JCE->emitInt32(0); } else { unsigned PadLabelPtr = JCE->getLabelAddress(S.PadLabel); JCE->emitInt32(PadLabelPtr - (intptr_t)StartFunction); } // Asm->EOL("Landing pad"); JCE->emitULEB128Bytes(S.Action); // Asm->EOL("Action"); } // Emit the actions. for (unsigned I = 0, N = Actions.size(); I != N; ++I) { ActionEntry &Action = Actions[I]; JCE->emitSLEB128Bytes(Action.ValueForTypeID); //Asm->EOL("TypeInfo index"); JCE->emitSLEB128Bytes(Action.NextAction); //Asm->EOL("Next action"); } // Emit the type ids. for (unsigned M = TypeInfos.size(); M; --M) { const GlobalVariable *GV = TypeInfos[M - 1]; if (GV) { if (TD->getPointerSize() == sizeof(int32_t)) JCE->emitInt32((intptr_t)Jit.getOrEmitGlobalVariable(GV)); else JCE->emitInt64((intptr_t)Jit.getOrEmitGlobalVariable(GV)); } else { if (TD->getPointerSize() == sizeof(int32_t)) JCE->emitInt32(0); else JCE->emitInt64(0); } // Asm->EOL("TypeInfo"); } // Emit the filter typeids. for (unsigned j = 0, M = FilterIds.size(); j < M; ++j) { unsigned TypeID = FilterIds[j]; JCE->emitULEB128Bytes(TypeID); //Asm->EOL("Filter TypeInfo index"); } JCE->emitAlignmentWithFill(4, 0); return DwarfExceptionTable; }
/// Compute the call-site table. The entry for an invoke has a try-range /// containing the call, a non-zero landing pad, and an appropriate action. The /// entry for an ordinary call has a try-range containing the call and zero for /// the landing pad and the action. Calls marked 'nounwind' have no entry and /// must not be contained in the try-range of any entry - they form gaps in the /// table. Entries must be ordered by try-range address. void EHStreamer:: computeCallSiteTable(SmallVectorImpl<CallSiteEntry> &CallSites, const SmallVectorImpl<const LandingPadInfo *> &LandingPads, const SmallVectorImpl<unsigned> &FirstActions) { // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced so we can put them in the LSDA. RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } // The end label of the previous invoke or nounwind try-range. MCSymbol *LastLabel = nullptr; // Whether there is a potentially throwing instruction (currently this means // an ordinary call) between the end of the previous try-range and now. bool SawPotentiallyThrowing = false; // Whether the last CallSite entry was for an invoke. bool PreviousIsInvoke = false; bool IsSJLJ = Asm->MAI->getExceptionHandlingType() == ExceptionHandling::SjLj; // Visit all instructions in order of address. for (const auto &MBB : *Asm->MF) { for (const auto &MI : MBB) { if (!MI.isEHLabel()) { if (MI.isCall()) SawPotentiallyThrowing |= !callToNoUnwindFunction(&MI); continue; } // End of the previous try-range? MCSymbol *BeginLabel = MI.getOperand(0).getMCSymbol(); if (BeginLabel == LastLabel) SawPotentiallyThrowing = false; // Beginning of a new try-range? RangeMapType::const_iterator L = PadMap.find(BeginLabel); if (L == PadMap.end()) // Nope, it was just some random label. continue; const PadRange &P = L->second; const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && "Inconsistent landing pad map!"); // For Dwarf exception handling (SjLj handling doesn't use this). If some // instruction between the previous try-range and this one may throw, // create a call-site entry with no landing pad for the region between the // try-ranges. if (SawPotentiallyThrowing && !IsSJLJ) { CallSiteEntry Site = { LastLabel, BeginLabel, nullptr, 0 }; CallSites.push_back(Site); PreviousIsInvoke = false; } LastLabel = LandingPad->EndLabels[P.RangeIndex]; assert(BeginLabel && LastLabel && "Invalid landing pad!"); if (!LandingPad->LandingPadLabel) { // Create a gap. PreviousIsInvoke = false; } else { // This try-range is for an invoke. CallSiteEntry Site = { BeginLabel, LastLabel, LandingPad->LandingPadLabel, FirstActions[P.PadIndex] }; // Try to merge with the previous call-site. SJLJ doesn't do this if (PreviousIsInvoke && !IsSJLJ) { CallSiteEntry &Prev = CallSites.back(); if (Site.PadLabel == Prev.PadLabel && Site.Action == Prev.Action) { // Extend the range of the previous entry. Prev.EndLabel = Site.EndLabel; continue; } } // Otherwise, create a new call-site. if (!IsSJLJ) CallSites.push_back(Site); else { // SjLj EH must maintain the call sites in the order assigned // to them by the SjLjPrepare pass. unsigned SiteNo = MMI->getCallSiteBeginLabel(BeginLabel); if (CallSites.size() < SiteNo) CallSites.resize(SiteNo); CallSites[SiteNo - 1] = Site; } PreviousIsInvoke = true; } } } // If some instruction between the previous try-range and the end of the // function may throw, create a call-site entry with no landing pad for the // region following the try-range. if (SawPotentiallyThrowing && !IsSJLJ) { CallSiteEntry Site = { LastLabel, nullptr, nullptr, 0 }; CallSites.push_back(Site); } }
/// EmitExceptionTable - Emit landing pads and actions. /// /// The general organization of the table is complex, but the basic concepts are /// easy. First there is a header which describes the location and organization /// of the three components that follow. /// /// 1. The landing pad site information describes the range of code covered by /// the try. In our case it's an accumulation of the ranges covered by the /// invokes in the try. There is also a reference to the landing pad that /// handles the exception once processed. Finally an index into the actions /// table. /// 2. The action table, in our case, is composed of pairs of type IDs and next /// action offset. Starting with the action index from the landing pad /// site, each type ID is checked for a match to the current exception. If /// it matches then the exception and type id are passed on to the landing /// pad. Otherwise the next action is looked up. This chain is terminated /// with a next action of zero. If no type id is found the the frame is /// unwound and handling continues. /// 3. Type ID table contains references to all the C++ typeinfo for all /// catches in the function. This tables is reversed indexed base 1. void DwarfException::EmitExceptionTable() { const std::vector<GlobalVariable *> &TypeInfos = MMI->getTypeInfos(); const std::vector<unsigned> &FilterIds = MMI->getFilterIds(); const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads(); if (PadInfos.empty()) return; // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector<const LandingPadInfo *, 64> LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), PadLT); // Compute the actions table and gather the first action index for each // landing pad site. SmallVector<ActionEntry, 32> Actions; SmallVector<unsigned, 64> FirstActions; unsigned SizeActions = ComputeActionsTable(LandingPads, Actions, FirstActions); // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced when using Dwarf exception handling. RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { unsigned BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } // Compute the call-site table. SmallVector<CallSiteEntry, 64> CallSites; ComputeCallSiteTable(CallSites, PadMap, LandingPads, FirstActions); // Final tallies. // Call sites. const unsigned SiteStartSize = sizeof(int32_t); // DW_EH_PE_udata4 const unsigned SiteLengthSize = sizeof(int32_t); // DW_EH_PE_udata4 const unsigned LandingPadSize = sizeof(int32_t); // DW_EH_PE_udata4 unsigned SizeSites; bool HaveTTData = (TAI->getExceptionHandlingType() == ExceptionHandling::SjLj) ? (!TypeInfos.empty() || !FilterIds.empty()) : true; if (TAI->getExceptionHandlingType() == ExceptionHandling::SjLj) { SizeSites = 0; } else SizeSites = CallSites.size() * (SiteStartSize + SiteLengthSize + LandingPadSize); for (unsigned i = 0, e = CallSites.size(); i < e; ++i) { SizeSites += TargetAsmInfo::getULEB128Size(CallSites[i].Action); if (TAI->getExceptionHandlingType() == ExceptionHandling::SjLj) SizeSites += TargetAsmInfo::getULEB128Size(i); } // Type infos. const unsigned TypeInfoSize = TD->getPointerSize(); // DW_EH_PE_absptr unsigned SizeTypes = TypeInfos.size() * TypeInfoSize; unsigned TypeOffset = sizeof(int8_t) + // Call site format TargetAsmInfo::getULEB128Size(SizeSites) + // Call-site table length SizeSites + SizeActions + SizeTypes; unsigned TotalSize = sizeof(int8_t) + // LPStart format sizeof(int8_t) + // TType format (HaveTTData ? TargetAsmInfo::getULEB128Size(TypeOffset) : 0) + // TType base offset TypeOffset; unsigned SizeAlign = (4 - TotalSize) & 3; // Begin the exception table. const MCSection *LSDASection = Asm->getObjFileLowering().getLSDASection(); Asm->OutStreamer.SwitchSection(LSDASection); Asm->EmitAlignment(2, 0, 0, false); O << "GCC_except_table" << SubprogramCount << ":\n"; for (unsigned i = 0; i != SizeAlign; ++i) { Asm->EmitInt8(0); Asm->EOL("Padding"); } EmitLabel("exception", SubprogramCount); if (TAI->getExceptionHandlingType() == ExceptionHandling::SjLj) { std::string SjLjName = "_lsda_"; SjLjName += MF->getFunction()->getName().str(); EmitLabel(SjLjName.c_str(), 0); } // Emit the header. Asm->EmitInt8(dwarf::DW_EH_PE_omit); Asm->EOL("@LPStart format (DW_EH_PE_omit)"); #if 0 if (TypeInfos.empty() && FilterIds.empty()) { // If there are no typeinfos or filters, there is nothing to emit, optimize // by specifying the "omit" encoding. Asm->EmitInt8(dwarf::DW_EH_PE_omit); Asm->EOL("@TType format (DW_EH_PE_omit)"); } else { // Okay, we have actual filters or typeinfos to emit. As such, we need to // pick a type encoding for them. We're about to emit a list of pointers to // typeinfo objects at the end of the LSDA. However, unless we're in static // mode, this reference will require a relocation by the dynamic linker. // // Because of this, we have a couple of options: // 1) If we are in -static mode, we can always use an absolute reference // from the LSDA, because the static linker will resolve it. // 2) Otherwise, if the LSDA section is writable, we can output the direct // reference to the typeinfo and allow the dynamic linker to relocate // it. Since it is in a writable section, the dynamic linker won't // have a problem. // 3) Finally, if we're in PIC mode and the LDSA section isn't writable, // we need to use some form of indirection. For example, on Darwin, // we can output a statically-relocatable reference to a dyld stub. The // offset to the stub is constant, but the contents are in a section // that is updated by the dynamic linker. This is easy enough, but we // need to tell the personality function of the unwinder to indirect // through the dyld stub. // // FIXME: When this is actually implemented, we'll have to emit the stubs // somewhere. This predicate should be moved to a shared location that is // in target-independent code. // if (LSDASection->isWritable() || Asm->TM.getRelocationModel() == Reloc::Static) { Asm->EmitInt8(DW_EH_PE_absptr); Asm->EOL("TType format (DW_EH_PE_absptr)"); } else { Asm->EmitInt8(DW_EH_PE_pcrel | DW_EH_PE_indirect | DW_EH_PE_sdata4); Asm->EOL("TType format (DW_EH_PE_pcrel | DW_EH_PE_indirect" " | DW_EH_PE_sdata4)"); } Asm->EmitULEB128Bytes(TypeOffset); Asm->EOL("TType base offset"); } #else // For SjLj exceptions, if there is no TypeInfo, then we just explicitly // say that we're omitting that bit. // FIXME: does this apply to Dwarf also? The above #if 0 implies yes? if (!HaveTTData) { Asm->EmitInt8(dwarf::DW_EH_PE_omit); Asm->EOL("@TType format (DW_EH_PE_omit)"); } else { Asm->EmitInt8(dwarf::DW_EH_PE_absptr); Asm->EOL("@TType format (DW_EH_PE_absptr)"); Asm->EmitULEB128Bytes(TypeOffset); Asm->EOL("@TType base offset"); } #endif // SjLj Exception handilng if (TAI->getExceptionHandlingType() == ExceptionHandling::SjLj) { Asm->EmitInt8(dwarf::DW_EH_PE_udata4); Asm->EOL("Call site format (DW_EH_PE_udata4)"); Asm->EmitULEB128Bytes(SizeSites); Asm->EOL("Call site table length"); // Emit the landing pad site information. unsigned idx = 0; for (SmallVectorImpl<CallSiteEntry>::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I, ++idx) { const CallSiteEntry &S = *I; // Offset of the landing pad, counted in 16-byte bundles relative to the // @LPStart address. Asm->EmitULEB128Bytes(idx); Asm->EOL("Landing pad"); // Offset of the first associated action record, relative to the start of // the action table. This value is biased by 1 (1 indicates the start of // the action table), and 0 indicates that there are no actions. Asm->EmitULEB128Bytes(S.Action); Asm->EOL("Action"); } } else { // DWARF Exception handling assert(TAI->getExceptionHandlingType() == ExceptionHandling::Dwarf); // The call-site table is a list of all call sites that may throw an // exception (including C++ 'throw' statements) in the procedure // fragment. It immediately follows the LSDA header. Each entry indicates, // for a given call, the first corresponding action record and corresponding // landing pad. // // The table begins with the number of bytes, stored as an LEB128 // compressed, unsigned integer. The records immediately follow the record // count. They are sorted in increasing call-site address. Each record // indicates: // // * The position of the call-site. // * The position of the landing pad. // * The first action record for that call site. // // A missing entry in the call-site table indicates that a call is not // supposed to throw. Such calls include: // // * Calls to destructors within cleanup code. C++ semantics forbids these // calls to throw. // * Calls to intrinsic routines in the standard library which are known // not to throw (sin, memcpy, et al). // // If the runtime does not find the call-site entry for a given call, it // will call `terminate()'. // Emit the landing pad call site table. Asm->EmitInt8(dwarf::DW_EH_PE_udata4); Asm->EOL("Call site format (DW_EH_PE_udata4)"); Asm->EmitULEB128Bytes(SizeSites); Asm->EOL("Call site table size"); for (SmallVectorImpl<CallSiteEntry>::const_iterator I = CallSites.begin(), E = CallSites.end(); I != E; ++I) { const CallSiteEntry &S = *I; const char *BeginTag; unsigned BeginNumber; if (!S.BeginLabel) { BeginTag = "eh_func_begin"; BeginNumber = SubprogramCount; } else { BeginTag = "label"; BeginNumber = S.BeginLabel; } // Offset of the call site relative to the previous call site, counted in // number of 16-byte bundles. The first call site is counted relative to // the start of the procedure fragment. EmitSectionOffset(BeginTag, "eh_func_begin", BeginNumber, SubprogramCount, true, true); Asm->EOL("Region start"); if (!S.EndLabel) EmitDifference("eh_func_end", SubprogramCount, BeginTag, BeginNumber, true); else EmitDifference("label", S.EndLabel, BeginTag, BeginNumber, true); Asm->EOL("Region length"); // Offset of the landing pad, counted in 16-byte bundles relative to the // @LPStart address. if (!S.PadLabel) Asm->EmitInt32(0); else EmitSectionOffset("label", "eh_func_begin", S.PadLabel, SubprogramCount, true, true); Asm->EOL("Landing pad"); // Offset of the first associated action record, relative to the start of // the action table. This value is biased by 1 (1 indicates the start of // the action table), and 0 indicates that there are no actions. Asm->EmitULEB128Bytes(S.Action); Asm->EOL("Action"); } } // Emit the Action Table. for (SmallVectorImpl<ActionEntry>::const_iterator I = Actions.begin(), E = Actions.end(); I != E; ++I) { const ActionEntry &Action = *I; // Type Filter // // Used by the runtime to match the type of the thrown exception to the // type of the catch clauses or the types in the exception specification. Asm->EmitSLEB128Bytes(Action.ValueForTypeID); Asm->EOL("TypeInfo index"); // Action Record // // Self-relative signed displacement in bytes of the next action record, // or 0 if there is no next action record. Asm->EmitSLEB128Bytes(Action.NextAction); Asm->EOL("Next action"); } // Emit the Catch Clauses. The code for the catch clauses following the same // try is similar to a switch statement. The catch clause action record // informs the runtime about the type of a catch clause and about the // associated switch value. // // Action Record Fields: // // * Filter Value // Positive value, starting at 1. Index in the types table of the // __typeinfo for the catch-clause type. 1 is the first word preceding // TTBase, 2 is the second word, and so on. Used by the runtime to check // if the thrown exception type matches the catch-clause type. Back-end // generated switch statements check against this value. // // * Next // Signed offset, in bytes from the start of this field, to the next // chained action record, or zero if none. // // The order of the action records determined by the next field is the order // of the catch clauses as they appear in the source code, and must be kept in // the same order. As a result, changing the order of the catch clause would // change the semantics of the program. for (std::vector<GlobalVariable *>::const_reverse_iterator I = TypeInfos.rbegin(), E = TypeInfos.rend(); I != E; ++I) { const GlobalVariable *GV = *I; PrintRelDirective(); if (GV) { std::string GLN; O << Asm->getGlobalLinkName(GV, GLN); } else { O << "0"; } Asm->EOL("TypeInfo"); } // Emit the Type Table. for (std::vector<unsigned>::const_iterator I = FilterIds.begin(), E = FilterIds.end(); I < E; ++I) { unsigned TypeID = *I; Asm->EmitULEB128Bytes(TypeID); Asm->EOL("Filter TypeInfo index"); } Asm->EmitAlignment(2, 0, 0, false); }
/// PrepareMonoLSDA - Collect information needed by EmitMonoLSDA /// /// This function collects information available only during EndFunction which is needed /// by EmitMonoLSDA and stores it into EHFrameInfo. It is the same as the /// beginning of EmitExceptionTable. /// void DwarfMonoException::PrepareMonoLSDA(FunctionEHFrameInfo *EHFrameInfo) { const std::vector<const GlobalVariable *> &TypeInfos = MMI->getTypeInfos(); const std::vector<LandingPadInfo> &PadInfos = MMI->getLandingPads(); const MachineFunction *MF = Asm->MF; // Sort the landing pads in order of their type ids. This is used to fold // duplicate actions. SmallVector<const LandingPadInfo *, 64> LandingPads; LandingPads.reserve(PadInfos.size()); for (unsigned i = 0, N = PadInfos.size(); i != N; ++i) LandingPads.push_back(&PadInfos[i]); std::sort(LandingPads.begin(), LandingPads.end(), [](const LandingPadInfo *L, const LandingPadInfo *R) { return L->TypeIds < R->TypeIds; }); // Invokes and nounwind calls have entries in PadMap (due to being bracketed // by try-range labels when lowered). Ordinary calls do not, so appropriate // try-ranges for them need be deduced when using DWARF exception handling. RangeMapType PadMap; for (unsigned i = 0, N = LandingPads.size(); i != N; ++i) { const LandingPadInfo *LandingPad = LandingPads[i]; for (unsigned j = 0, E = LandingPad->BeginLabels.size(); j != E; ++j) { MCSymbol *BeginLabel = LandingPad->BeginLabels[j]; assert(!PadMap.count(BeginLabel) && "Duplicate landing pad labels!"); PadRange P = { i, j }; PadMap[BeginLabel] = P; } } // Compute the call-site table. SmallVector<MonoCallSiteEntry, 64> CallSites; MCSymbol *LastLabel = 0; for (MachineFunction::const_iterator I = MF->begin(), E = MF->end(); I != E; ++I) { for (MachineBasicBlock::const_iterator MI = I->begin(), E = I->end(); MI != E; ++MI) { if (!MI->isLabel()) { continue; } MCSymbol *BeginLabel = MI->getOperand(0).getMCSymbol(); assert(BeginLabel && "Invalid label!"); RangeMapType::iterator L = PadMap.find(BeginLabel); if (L == PadMap.end()) continue; PadRange P = L->second; const LandingPadInfo *LandingPad = LandingPads[P.PadIndex]; assert(BeginLabel == LandingPad->BeginLabels[P.RangeIndex] && "Inconsistent landing pad map!"); // Mono emits one landing pad for each CLR exception clause, // and the type info contains the clause index assert (LandingPad->TypeIds.size() == 1); assert (LandingPad->LandingPadLabel); LastLabel = LandingPad->EndLabels[P.RangeIndex]; MonoCallSiteEntry Site = {BeginLabel, LastLabel, LandingPad->LandingPadLabel, LandingPad->TypeIds [0]}; assert(Site.BeginLabel && Site.EndLabel && Site.PadLabel && "Invalid landing pad!"); // FIXME: This doesn't work because it includes ranges outside clauses #if 0 // Try to merge with the previous call-site. if (CallSites.size()) { MonoCallSiteEntry &Prev = CallSites.back(); if (Site.PadLabel == Prev.PadLabel && Site.TypeID == Prev.TypeID) { // Extend the range of the previous entry. Prev.EndLabel = Site.EndLabel; continue; } } #endif // Otherwise, create a new call-site. CallSites.push_back(Site); } } // // Compute a mapping from method names to their AOT method index // if (FuncIndexes.size () == 0) { const Module *m = MMI->getModule (); NamedMDNode *indexes = m->getNamedMetadata ("mono.function_indexes"); if (indexes) { for (unsigned int i = 0; i < indexes->getNumOperands (); ++i) { MDNode *n = indexes->getOperand (i); MDString *s = (MDString*)n->getOperand (0); ConstantInt *idx = (ConstantInt*)n->getOperand (1); FuncIndexes.GetOrCreateValue (s->getString (), (int)idx->getLimitedValue () + 1); } } } MonoEHFrameInfo *MonoEH = &EHFrameInfo->MonoEH; // Save information for EmitMonoLSDA MonoEH->MF = Asm->MF; MonoEH->FunctionNumber = Asm->getFunctionNumber(); MonoEH->CallSites.insert(MonoEH->CallSites.begin(), CallSites.begin(), CallSites.end()); MonoEH->TypeInfos = TypeInfos; MonoEH->PadInfos = PadInfos; MonoEH->MonoMethodIdx = FuncIndexes.lookup (Asm->MF->getFunction ()->getName ()) - 1; //outs()<<"A:"<<Asm->MF->getFunction()->getName() << " " << MonoEH->MonoMethodIdx << "\n"; int ThisSlot = Asm->MF->getMonoInfo()->getThisStackSlot(); if (ThisSlot != -1) { unsigned FrameReg; MonoEH->ThisOffset = Asm->MF->getTarget ().getSubtargetImpl ()->getFrameLowering ()->getFrameIndexReference (*Asm->MF, ThisSlot, FrameReg); MonoEH->FrameReg = Asm->MF->getTarget ().getSubtargetImpl ()->getRegisterInfo ()->getDwarfRegNum (FrameReg, true); } else { MonoEH->FrameReg = -1; } }