Changeset View
Changeset View
Standalone View
Standalone View
llvm/lib/MC/MCWin64EH.cpp
Show First 20 Lines • Show All 538 Lines • ▼ Show 20 Lines | for (unsigned i = 0; i < Instrs.size(); ++i) | ||||
} | } | ||||
if (Match) | if (Match) | ||||
return EpilogStart; | return EpilogStart; | ||||
} | } | ||||
return nullptr; | return nullptr; | ||||
} | } | ||||
static void simplifyOpcodes(std::vector<WinEH::Instruction> &Instructions, | |||||
bool Reverse) { | |||||
unsigned PrevOffset = -1; | |||||
unsigned PrevRegister = -1; | |||||
auto VisitInstruction = [&](WinEH::Instruction &Inst) { | |||||
// Convert 2-byte opcodes into equivalent 1-byte ones. | |||||
if (Inst.Operation == Win64EH::UOP_SaveRegP && Inst.Register == 29) { | |||||
Inst.Operation = Win64EH::UOP_SaveFPLR; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveRegPX && | |||||
Inst.Register == 29) { | |||||
Inst.Operation = Win64EH::UOP_SaveFPLRX; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveRegPX && | |||||
Inst.Register == 19) { | |||||
Inst.Operation = Win64EH::UOP_SaveR19R20X; | |||||
efriedma: I think at least save_r19r20_x has a different range from the corresponding long opcode. | |||||
mstorsjoAuthorUnsubmitted Oh, good catch! Will fix. That will probably complicate one of the later patches though... mstorsjo: Oh, good catch! Will fix. That will probably complicate one of the later patches though... | |||||
} else if (Inst.Operation == Win64EH::UOP_AddFP && Inst.Offset == 0) { | |||||
Inst.Operation = Win64EH::UOP_SetFP; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveRegP && | |||||
Inst.Register == PrevRegister + 2 && | |||||
Inst.Offset == PrevOffset + 16) { | |||||
Inst.Operation = Win64EH::UOP_SaveNext; | |||||
// Intentionally not creating UOP_SaveNext for float register pairs, | |||||
// as current versions of Windows (up to at least 20.04) is buggy | |||||
// regarding SaveNext for float pairs. | |||||
efriedmaUnsubmitted Not Done ReplyInline ActionsCan we file a spec bug with Microsoft? efriedma: Can we file a spec bug with Microsoft? | |||||
mstorsjoAuthorUnsubmitted I discussed it with @TomTan and he said they've noticed it themselves, and they're planning to ship a fix later (late this year in insider previews, and they've noticed that neither MSVC nor Clang actually have produced this opcode so far), and they've also got some documentation update regarding these opcodes coming up. (The docs also say that another save_next after the last integer register will start restoring float registers, but it doesn't work that way in practice.) mstorsjo: I discussed it with @TomTan and he said they've noticed it themselves, and they're planning to… | |||||
} | |||||
// Update info about the previous instruction, for detecting if | |||||
// the next one can be made a UOP_SaveNext | |||||
if (Inst.Operation == Win64EH::UOP_SaveR19R20X) { | |||||
PrevOffset = 0; | |||||
PrevRegister = 19; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveRegPX) { | |||||
PrevOffset = 0; | |||||
PrevRegister = Inst.Register; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveRegP) { | |||||
PrevOffset = Inst.Offset; | |||||
PrevRegister = Inst.Register; | |||||
} else if (Inst.Operation == Win64EH::UOP_SaveNext) { | |||||
PrevRegister += 2; | |||||
PrevOffset += 16; | |||||
} else { | |||||
PrevRegister = -1; | |||||
PrevOffset = -1; | |||||
} | |||||
}; | |||||
// Iterate over instructions in a forward order (for prologues), | |||||
// backwards for epilogues (i.e. always reverse compared to how the | |||||
// opcodes are stored). | |||||
if (Reverse) { | |||||
for (auto It = Instructions.rbegin(); It != Instructions.rend(); It++) | |||||
VisitInstruction(*It); | |||||
} else { | |||||
for (WinEH::Instruction &Inst : Instructions) | |||||
VisitInstruction(Inst); | |||||
} | |||||
} | |||||
// Populate the .xdata section. The format of .xdata on ARM64 is documented at | // Populate the .xdata section. The format of .xdata on ARM64 is documented at | ||||
// https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling | // https://docs.microsoft.com/en-us/cpp/build/arm64-exception-handling | ||||
static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { | static void ARM64EmitUnwindInfo(MCStreamer &streamer, WinEH::FrameInfo *info) { | ||||
// If this UNWIND_INFO already has a symbol, it's already been emitted. | // If this UNWIND_INFO already has a symbol, it's already been emitted. | ||||
if (info->Symbol) | if (info->Symbol) | ||||
return; | return; | ||||
// If there's no unwind info here (not even a terminating UOP_End), the | // 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 | // unwind info is considered bogus and skipped. If this was done in | ||||
Show All 12 Lines | if (info->EmitAttempted) { | ||||
streamer.getContext().reportError( | streamer.getContext().reportError( | ||||
SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() + | SMLoc(), "Earlier .seh_handlerdata for " + info->Function->getName() + | ||||
" skipped due to no unwind info at the time " | " skipped due to no unwind info at the time " | ||||
"(.seh_handlerdata too early?), but the function later " | "(.seh_handlerdata too early?), but the function later " | ||||
"did get unwind info that can't be emitted"); | "did get unwind info that can't be emitted"); | ||||
return; | return; | ||||
} | } | ||||
simplifyOpcodes(info->Instructions, false); | |||||
for (auto &I : info->EpilogMap) | |||||
simplifyOpcodes(I.second, true); | |||||
MCContext &context = streamer.getContext(); | MCContext &context = streamer.getContext(); | ||||
MCSymbol *Label = context.createTempSymbol(); | MCSymbol *Label = context.createTempSymbol(); | ||||
streamer.emitValueToAlignment(4); | streamer.emitValueToAlignment(4); | ||||
streamer.emitLabel(Label); | streamer.emitLabel(Label); | ||||
info->Symbol = Label; | info->Symbol = Label; | ||||
int64_t RawFuncLength; | int64_t RawFuncLength; | ||||
▲ Show 20 Lines • Show All 192 Lines • Show Last 20 Lines |
I think at least save_r19r20_x has a different range from the corresponding long opcode.