diff options
author | Zhaoshi Zheng <zhaoshiz@quicinc.com> | 2022-07-08 11:48:44 -0700 |
---|---|---|
committer | Zhaoshi Zheng <zhaoshiz@quicinc.com> | 2022-08-05 11:46:41 -0700 |
commit | 99e50e583867ac35ace36f5da50b3a3ff7c51d2e (patch) | |
tree | c785bc4477778dbebfffa1429c026b570fea0f89 /llvm/lib/MC/MCWin64EH.cpp | |
parent | f1eb945f9a5037b1fac6da02405047b24c0c2de5 (diff) | |
download | llvm-99e50e583867ac35ace36f5da50b3a3ff7c51d2e.zip llvm-99e50e583867ac35ace36f5da50b3a3ff7c51d2e.tar.gz llvm-99e50e583867ac35ace36f5da50b3a3ff7c51d2e.tar.bz2 |
[WinEH][ARM64] Split Unwind Info for Fucntions Larger than 1MB
Create function segments and emit unwind info of them.
A segment must be less than 1MB and no prolog or epilog is splitted between two
segments.
This patch should generate correct, though not optimal, unwind info for large
functions. Currently it only generate pacted info (.pdata) only for functions
that are less than 1MB (single-segment functions). This is NFC from before this
patch.
The next step is to enable (.pdata) only unwind info for the first segment or
segments that have neither prolog or epilog in a multi-segment function.
Another future work item is to further split segments that require more than 255
code words or have more than 65535 epilogs.
Reference:
https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments
Differential Revision: https://reviews.llvm.org/D130049
Diffstat (limited to 'llvm/lib/MC/MCWin64EH.cpp')
-rw-r--r-- | llvm/lib/MC/MCWin64EH.cpp | 414 |
1 files changed, 291 insertions, 123 deletions
diff --git a/llvm/lib/MC/MCWin64EH.cpp b/llvm/lib/MC/MCWin64EH.cpp index ffabe0f..b22c02d 100644 --- a/llvm/lib/MC/MCWin64EH.cpp +++ b/llvm/lib/MC/MCWin64EH.cpp @@ -130,6 +130,17 @@ static void EmitUnwindCode(MCStreamer &streamer, const MCSymbol *begin, static void EmitSymbolRefWithOfs(MCStreamer &streamer, const MCSymbol *Base, + int64_t Offset) { + MCContext &Context = streamer.getContext(); + const MCConstantExpr *OffExpr = MCConstantExpr::create(Offset, Context); + const MCSymbolRefExpr *BaseRefRel = MCSymbolRefExpr::create(Base, + MCSymbolRefExpr::VK_COFF_IMGREL32, + Context); + streamer.emitValue(MCBinaryExpr::createAdd(BaseRefRel, OffExpr, Context), 4); +} + +static void EmitSymbolRefWithOfs(MCStreamer &streamer, + const MCSymbol *Base, const MCSymbol *Other) { MCContext &Context = streamer.getContext(); const MCSymbolRefExpr *BaseRef = MCSymbolRefExpr::create(Base, Context); @@ -642,26 +653,29 @@ getARM64OffsetInProlog(const std::vector<WinEH::Instruction> &Prolog, return -1; } - // If the epilog was a subset of the prolog, find its offset. if (Epilog.size() == Prolog.size()) return 0; + + // If the epilog was a subset of the prolog, find its offset. return ARM64CountOfUnwindCodes(ArrayRef<WinEH::Instruction>( &Prolog[Epilog.size()], Prolog.size() - Epilog.size())); } static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, + WinEH::FrameInfo::Segment *Seg, int PrologCodeBytes) { // Can only pack if there's one single epilog - if (info->EpilogMap.size() != 1) + if (Seg->Epilogs.size() != 1) return -1; + MCSymbol *Sym = Seg->Epilogs.begin()->first; const std::vector<WinEH::Instruction> &Epilog = - info->EpilogMap.begin()->second.Instructions; + info->EpilogMap[Sym].Instructions; // Check that the epilog actually is at the very end of the function, // otherwise it can't be packed. - uint32_t DistanceFromEnd = (uint32_t)GetAbsDifference( - streamer, info->FuncletOrFuncEnd, info->EpilogMap.begin()->first); + uint32_t DistanceFromEnd = + (uint32_t)(Seg->Offset + Seg->Length - Seg->Epilogs.begin()->second); if (DistanceFromEnd / 4 != Epilog.size()) return -1; @@ -686,7 +700,7 @@ static int checkARM64PackedEpilog(MCStreamer &streamer, WinEH::FrameInfo *info, // As we choose to express the epilog as part of the prolog, remove the // epilog from the map, so we don't try to emit its opcodes. - info->EpilogMap.clear(); + info->EpilogMap.erase(Sym); return Offset; } @@ -923,111 +937,20 @@ static bool tryARM64PackedUnwind(WinEH::FrameInfo *info, uint32_t FuncLength, return true; } -// Populate the .xdata section. The format of .xdata on ARM64 is documented at -// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling -static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, - bool TryPacked = true) { - // If this UNWIND_INFO already has a symbol, it's already been emitted. - if (info->Symbol) - return; - // If there's no unwind info here (not even a terminating UOP_End), the - // unwind info is considered bogus and skipped. If this was done in - // response to an explicit .seh_handlerdata, the associated trailing - // handler data is left orphaned in the xdata section. - if (info->empty()) { - info->EmitAttempted = true; - return; - } - if (info->EmitAttempted) { - // If we tried to emit unwind info before (due to an explicit - // .seh_handlerdata directive), but skipped it (because there was no - // valid information to emit at the time), and it later got valid unwind - // opcodes, we can't emit it here, because the trailing handler data - // was already emitted elsewhere in the xdata section. - streamer.getContext().reportError( - SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() + - " skipped due to no unwind info at the time " - "(.seh_handlerdata too early?), but the function later " - "did get unwind info that can't be emitted"); - return; - } - - simplifyARM64Opcodes(info->Instructions, false); - for (auto &I : info->EpilogMap) - simplifyARM64Opcodes(I.second.Instructions, true); - - MCContext &context = streamer.getContext(); - MCSymbol *Label = context.createTempSymbol(); - - streamer.emitValueToAlignment(4); - streamer.emitLabel(Label); - info->Symbol = Label; - - int64_t RawFuncLength; - if (!info->FuncletOrFuncEnd) { - report_fatal_error("FuncletOrFuncEnd not set"); - } else { - // FIXME: GetAbsDifference tries to compute the length of the function - // immediately, before the whole file is emitted, but in general - // that's impossible: the size in bytes of certain assembler directives - // like .align and .fill is not known until the whole file is parsed and - // relaxations are applied. Currently, GetAbsDifference fails with a fatal - // error in that case. (We mostly don't hit this because inline assembly - // specifying those directives is rare, and we don't normally try to - // align loops on AArch64.) - // - // There are two potential approaches to delaying the computation. One, - // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000", - // as long as we have some conservative estimate we could use to prove - // that we don't need to split the unwind data. Emitting the constant - // is straightforward, but there's no existing code for estimating the - // size of the function. - // - // The other approach would be to use a dedicated, relaxable fragment, - // which could grow to accommodate splitting the unwind data if - // necessary. This is more straightforward, since it automatically works - // without any new infrastructure, and it's consistent with how we handle - // relaxation in other contexts. But it would require some refactoring - // to move parts of the pdata/xdata emission into the implementation of - // a fragment. We could probably continue to encode the unwind codes - // here, but we'd have to emit the pdata, the xdata header, and the - // epilogue scopes later, since they depend on whether the we need to - // split the unwind data. - RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd, - info->Begin); - } - if (RawFuncLength > 0xFFFFF) - report_fatal_error("SEH unwind data splitting not yet implemented"); - uint32_t FuncLength = (uint32_t)RawFuncLength / 4; - uint32_t PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions); - uint32_t TotalCodeBytes = PrologCodeBytes; - - int PackedEpilogOffset = - checkARM64PackedEpilog(streamer, info, PrologCodeBytes); +static void ARM64ProcessEpilogs(WinEH::FrameInfo *info, + WinEH::FrameInfo::Segment *Seg, + uint32_t &TotalCodeBytes, + MapVector<MCSymbol *, uint32_t> &EpilogInfo) { - if (PackedEpilogOffset >= 0 && - uint32_t(PackedEpilogOffset) < PrologCodeBytes && - !info->HandlesExceptions && FuncLength <= 0x7ff && TryPacked) { - // Matching prolog/epilog and no exception handlers; check if the - // prolog matches the patterns that can be described by the packed - // format. - - // info->Symbol was already set even if we didn't actually write any - // unwind info there. Keep using that as indicator that this unwind - // info has been generated already. + std::vector<MCSymbol *> EpilogStarts; + for (auto &I : Seg->Epilogs) + EpilogStarts.push_back(I.first); - if (tryARM64PackedUnwind(info, FuncLength, PackedEpilogOffset)) - return; - } - - // Process epilogs. - MapVector<MCSymbol *, uint32_t> EpilogInfo; // Epilogs processed so far. std::vector<MCSymbol *> AddedEpilogs; - - for (auto &I : info->EpilogMap) { - MCSymbol *EpilogStart = I.first; - auto &EpilogInstrs = I.second.Instructions; + for (auto S : EpilogStarts) { + MCSymbol *EpilogStart = S; + auto &EpilogInstrs = info->EpilogMap[S].Instructions; uint32_t CodeBytes = ARM64CountOfUnwindCodes(EpilogInstrs); MCSymbol* MatchingEpilog = @@ -1038,13 +961,17 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, "Duplicate epilog not found"); EpilogInfo[EpilogStart] = EpilogInfo.lookup(MatchingEpilog); // Clear the unwind codes in the EpilogMap, so that they don't get output - // in the logic below. + // in ARM64EmitUnwindInfoForSegment(). EpilogInstrs.clear(); } else if ((PrologOffset = getARM64OffsetInProlog(info->Instructions, EpilogInstrs)) >= 0) { EpilogInfo[EpilogStart] = PrologOffset; + // If the segment doesn't have a prolog, an end_c will be emitted before + // prolog opcodes. So epilog start index in opcodes array is moved by 1. + if (!Seg->HasProlog) + EpilogInfo[EpilogStart] += 1; // Clear the unwind codes in the EpilogMap, so that they don't get output - // in the logic below. + // in ARM64EmitUnwindInfoForSegment(). EpilogInstrs.clear(); } else { EpilogInfo[EpilogStart] = TotalCodeBytes; @@ -1052,6 +979,143 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, AddedEpilogs.push_back(EpilogStart); } } +} + +static void ARM64FindSegmentsInFunction(MCStreamer &streamer, + WinEH::FrameInfo *info, + int64_t RawFuncLength) { + struct EpilogStartEnd { + MCSymbol *Start; + int64_t Offset; + int64_t End; + }; + // Record Start and End of each epilog. + SmallVector<struct EpilogStartEnd, 4> Epilogs; + for (auto &I : info->EpilogMap) { + MCSymbol *Start = I.first; + auto &Instrs = I.second.Instructions; + int64_t Offset = GetAbsDifference(streamer, Start, info->Begin); + assert((Epilogs.size() == 0 || Offset >= Epilogs.back().End) && + "Epilogs should be monotonically ordered"); + Epilogs.push_back({Start, Offset, Offset + (int64_t)Instrs.size() * 4}); + } + + unsigned E = 0; + int64_t SegLimit = 0xFFFFC; + int64_t SegOffset = 0; + + if (RawFuncLength > SegLimit) { + + int64_t RemainingLength = RawFuncLength; + + while (RemainingLength > SegLimit) { + // Try divide the function into segments, requirements: + // 1. Segment length <= 0xFFFFC; + // 2. Each Prologue or Epilogue must be fully within a segment. + int64_t SegLength = SegLimit; + int64_t SegEnd = SegOffset + SegLength; + // Keep record on symbols and offsets of epilogs in this segment. + MapVector<MCSymbol *, int64_t> EpilogsInSegment; + + while (E < Epilogs.size() && Epilogs[E].End < SegEnd) { + // Epilogs within current segment. + EpilogsInSegment[Epilogs[E].Start] = Epilogs[E].Offset; + ++E; + } + + // At this point, we have: + // 1. Put all epilogs in segments already. No action needed here; or + // 2. Found an epilog that will cross segments boundry. We need to + // move back current segment's end boundry, so the epilog is entirely + // in the next segment; or + // 3. Left at least one epilog that is entirely after this segment. + // It'll be handled by the next iteration, or the last segment. + if (E < Epilogs.size() && Epilogs[E].Offset <= SegEnd) + // Move back current Segment's end boundry. + SegLength = Epilogs[E].Offset - SegOffset; + + auto Seg = WinEH::FrameInfo::Segment( + SegOffset, SegLength, /* HasProlog */!SegOffset); + Seg.Epilogs = std::move(EpilogsInSegment); + info->Segments.push_back(Seg); + + SegOffset += SegLength; + RemainingLength -= SegLength; + } + } + + // Add the last segment when RawFuncLength > 0xFFFFC, + // or the only segment otherwise. + auto LastSeg = + WinEH::FrameInfo::Segment(SegOffset, RawFuncLength - SegOffset, + /* HasProlog */!SegOffset); + for (; E < Epilogs.size(); ++E) + LastSeg.Epilogs[Epilogs[E].Start] = Epilogs[E].Offset; + info->Segments.push_back(LastSeg); +} + +static void ARM64EmitUnwindInfoForSegment(MCStreamer &streamer, + WinEH::FrameInfo *info, + WinEH::FrameInfo::Segment &Seg, + bool TryPacked = true) { + MCContext &context = streamer.getContext(); + MCSymbol *Label = context.createTempSymbol(); + + streamer.emitValueToAlignment(4); + streamer.emitLabel(Label); + Seg.Symbol = Label; + // Use the 1st segemnt's label as function's. + if (Seg.Offset == 0) + info->Symbol = Label; + + bool HasProlog = Seg.HasProlog; + bool HasEpilogs = (Seg.Epilogs.size() != 0); + + uint32_t SegLength = (uint32_t)Seg.Length / 4; + uint32_t PrologCodeBytes = info->PrologCodeBytes; + + int PackedEpilogOffset = HasEpilogs ? + checkARM64PackedEpilog(streamer, info, &Seg, PrologCodeBytes) : -1; + + // TODO: + // 1. Enable packed unwind info (.pdata only) for multi-segment functions. + // 2. Emit packed unwind info (.pdata only) for segments that have neithor + // prolog nor epilog. + if (info->Segments.size() == 1 && PackedEpilogOffset >= 0 && + uint32_t(PackedEpilogOffset) < PrologCodeBytes && + !info->HandlesExceptions && SegLength <= 0x7ff && TryPacked) { + // Matching prolog/epilog and no exception handlers; check if the + // prolog matches the patterns that can be described by the packed + // format. + + // info->Symbol was already set even if we didn't actually write any + // unwind info there. Keep using that as indicator that this unwind + // info has been generated already. + if (tryARM64PackedUnwind(info, SegLength, PackedEpilogOffset)) + return; + } + + // If the prolog is not in this segment, we need to emit an end_c, which takes + // 1 byte, before prolog unwind ops. + if (!HasProlog) { + PrologCodeBytes += 1; + if (PackedEpilogOffset >= 0) + PackedEpilogOffset += 1; + // If a segment has neither prolog nor epilog, "With full .xdata record, + // Epilog Count = 1. Epilog Start Index points to end_c." + // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling#function-fragments + // TODO: We can remove this if testing shows zero epilog scope is ok with + // MS unwinder. + if (!HasEpilogs) + // Pack the fake epilog into phantom prolog. + PackedEpilogOffset = 0; + } + + uint32_t TotalCodeBytes = PrologCodeBytes; + + // Process epilogs. + MapVector<MCSymbol *, uint32_t> EpilogInfo; + ARM64ProcessEpilogs(info, &Seg, TotalCodeBytes, EpilogInfo); // Code Words, Epilog count, E, X, Vers, Function Length uint32_t row1 = 0x0; @@ -1060,7 +1124,7 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, if (CodeWordsMod) CodeWords++; uint32_t EpilogCount = - PackedEpilogOffset >= 0 ? PackedEpilogOffset : info->EpilogMap.size(); + PackedEpilogOffset >= 0 ? PackedEpilogOffset : Seg.Epilogs.size(); bool ExtensionWord = EpilogCount > 31 || TotalCodeBytes > 124; if (!ExtensionWord) { row1 |= (EpilogCount & 0x1F) << 22; @@ -1070,14 +1134,17 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, row1 |= 1 << 20; if (PackedEpilogOffset >= 0) // E row1 |= 1 << 21; - row1 |= FuncLength & 0x3FFFF; + row1 |= SegLength & 0x3FFFF; streamer.emitInt32(row1); // Extended Code Words, Extended Epilog Count if (ExtensionWord) { // FIXME: We should be able to split unwind info into multiple sections. if (CodeWords > 0xFF || EpilogCount > 0xFFFF) - report_fatal_error("SEH unwind data splitting not yet implemented"); + report_fatal_error( + "SEH unwind data splitting is only implemnted for large functions, " + "cases of too many code words or too many epilogs will be done later" + ); uint32_t row2 = 0x0; row2 |= (CodeWords & 0xFF) << 16; row2 |= (EpilogCount & 0xFFFF); @@ -1089,8 +1156,8 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, for (auto &I : EpilogInfo) { MCSymbol *EpilogStart = I.first; uint32_t EpilogIndex = I.second; - uint32_t EpilogOffset = - (uint32_t)GetAbsDifference(streamer, EpilogStart, info->Begin); + // Epilog offset within the Segment. + uint32_t EpilogOffset = (uint32_t)(Seg.Epilogs[EpilogStart] - Seg.Offset); if (EpilogOffset) EpilogOffset /= 4; uint32_t row3 = EpilogOffset; @@ -1099,17 +1166,23 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, } } + // Note that even for segments that have no prolog, we still need to emit + // prolog unwinding opcodes so that the unwinder knows how to unwind from + // such a segment. + // The end_c opcode at the start indicates to the unwinder that the actual + // prolog is outside of the current segment, and the unwinder shouldn't try + // to check for unwinding from a partial prolog. + if (!HasProlog) + // Emit an end_c. + streamer.emitInt8((uint8_t)0xE5); + // Emit prolog unwind instructions (in reverse order). - uint8_t numInst = info->Instructions.size(); - for (uint8_t c = 0; c < numInst; ++c) { - WinEH::Instruction inst = info->Instructions.back(); - info->Instructions.pop_back(); - ARM64EmitUnwindCode(streamer, inst); - } + for (auto Inst : llvm::reverse(info->Instructions)) + ARM64EmitUnwindCode(streamer, Inst); // Emit epilog unwind instructions - for (auto &I : info->EpilogMap) { - auto &EpilogInstrs = I.second.Instructions; + for (auto &I : Seg.Epilogs) { + auto &EpilogInstrs = info->EpilogMap[I.first].Instructions; for (const WinEH::Instruction &inst : EpilogInstrs) ARM64EmitUnwindCode(streamer, inst); } @@ -1126,6 +1199,83 @@ static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, 4); } +// Populate the .xdata section. The format of .xdata on ARM64 is documented at +// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling +static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, + bool TryPacked = true) { + // If this UNWIND_INFO already has a symbol, it's already been emitted. + if (info->Symbol) + return; + // If there's no unwind info here (not even a terminating UOP_End), the + // unwind info is considered bogus and skipped. If this was done in + // response to an explicit .seh_handlerdata, the associated trailing + // handler data is left orphaned in the xdata section. + if (info->empty()) { + info->EmitAttempted = true; + return; + } + if (info->EmitAttempted) { + // If we tried to emit unwind info before (due to an explicit + // .seh_handlerdata directive), but skipped it (because there was no + // valid information to emit at the time), and it later got valid unwind + // opcodes, we can't emit it here, because the trailing handler data + // was already emitted elsewhere in the xdata section. + streamer.getContext().reportError( + SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() + + " skipped due to no unwind info at the time " + "(.seh_handlerdata too early?), but the function later " + "did get unwind info that can't be emitted"); + return; + } + + simplifyARM64Opcodes(info->Instructions, false); + for (auto &I : info->EpilogMap) + simplifyARM64Opcodes(I.second.Instructions, true); + + int64_t RawFuncLength; + if (!info->FuncletOrFuncEnd) { + report_fatal_error("FuncletOrFuncEnd not set"); + } else { + // FIXME: GetAbsDifference tries to compute the length of the function + // immediately, before the whole file is emitted, but in general + // that's impossible: the size in bytes of certain assembler directives + // like .align and .fill is not known until the whole file is parsed and + // relaxations are applied. Currently, GetAbsDifference fails with a fatal + // error in that case. (We mostly don't hit this because inline assembly + // specifying those directives is rare, and we don't normally try to + // align loops on AArch64.) + // + // There are two potential approaches to delaying the computation. One, + // we could emit something like ".word (endfunc-beginfunc)/4+0x10800000", + // as long as we have some conservative estimate we could use to prove + // that we don't need to split the unwind data. Emitting the constant + // is straightforward, but there's no existing code for estimating the + // size of the function. + // + // The other approach would be to use a dedicated, relaxable fragment, + // which could grow to accommodate splitting the unwind data if + // necessary. This is more straightforward, since it automatically works + // without any new infrastructure, and it's consistent with how we handle + // relaxation in other contexts. But it would require some refactoring + // to move parts of the pdata/xdata emission into the implementation of + // a fragment. We could probably continue to encode the unwind codes + // here, but we'd have to emit the pdata, the xdata header, and the + // epilogue scopes later, since they depend on whether the we need to + // split the unwind data. + RawFuncLength = GetAbsDifference(streamer, info->FuncletOrFuncEnd, + info->Begin); + } + + ARM64FindSegmentsInFunction(streamer, info, RawFuncLength); + + info->PrologCodeBytes = ARM64CountOfUnwindCodes(info->Instructions); + for (auto &S : info->Segments) + ARM64EmitUnwindInfoForSegment(streamer, info, S, TryPacked); + + // Clear prolog instructions after unwind info is emitted for all segments. + info->Instructions.clear(); +} + static uint32_t ARMCountOfUnwindCodes(ArrayRef<WinEH::Instruction> Insns) { uint32_t Count = 0; for (const auto &I : Insns) { @@ -2205,6 +2355,24 @@ static void ARMEmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info, 4); } +static void ARM64EmitRuntimeFunction(MCStreamer &streamer, + const WinEH::FrameInfo *info) { + MCContext &context = streamer.getContext(); + + streamer.emitValueToAlignment(4); + for (auto &S : info->Segments) { + EmitSymbolRefWithOfs(streamer, info->Begin, S.Offset); + if (info->PackedInfo) + streamer.emitInt32(info->PackedInfo); + else + streamer.emitValue( + MCSymbolRefExpr::create(S.Symbol, MCSymbolRefExpr::VK_COFF_IMGREL32, + context), + 4); + } +} + + static void ARMEmitRuntimeFunction(MCStreamer &streamer, const WinEH::FrameInfo *info) { MCContext &context = streamer.getContext(); @@ -2241,7 +2409,7 @@ void llvm::Win64EH::ARM64UnwindEmitter::Emit(MCStreamer &Streamer) const { continue; MCSection *PData = Streamer.getAssociatedPDataSection(CFI->TextSection); Streamer.switchSection(PData); - ARMEmitRuntimeFunction(Streamer, Info); + ARM64EmitRuntimeFunction(Streamer, Info); } } |