Skip to content

Commit

Permalink
ARM: add support for WatchOS's compact unwind information.
Browse files Browse the repository at this point in the history
llvm-svn: 251573
  • Loading branch information
TNorthover committed Oct 28, 2015
1 parent 2253d1c commit f8e47e4
Show file tree
Hide file tree
Showing 14 changed files with 456 additions and 16 deletions.
12 changes: 11 additions & 1 deletion llvm/include/llvm/MC/MCObjectFileInfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,13 @@ class MCObjectFileInfo {
/// without an associated EH frame section.
bool SupportsCompactUnwindWithoutEHFrame;

/// Some encoding values for EH.
/// OmitDwarfIfHaveCompactUnwind - True if the target object file
/// supports having some functions with compact unwind and other with
/// dwarf unwind.
bool OmitDwarfIfHaveCompactUnwind;

/// PersonalityEncoding, LSDAEncoding, TTypeEncoding - Some encoding values
/// for EH.
unsigned PersonalityEncoding;
unsigned LSDAEncoding;
unsigned FDECFIEncoding;
Expand Down Expand Up @@ -200,6 +206,10 @@ class MCObjectFileInfo {
bool getSupportsCompactUnwindWithoutEHFrame() const {
return SupportsCompactUnwindWithoutEHFrame;
}
bool getOmitDwarfIfHaveCompactUnwind() const {
return OmitDwarfIfHaveCompactUnwind;
}

bool getCommDirectiveSupportsAlignment() const {
return CommDirectiveSupportsAlignment;
}
Expand Down
3 changes: 2 additions & 1 deletion llvm/lib/MC/MCDwarf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1536,6 +1536,7 @@ void MCDwarfFrameEmitter::Emit(MCObjectStreamer &Streamer, MCAsmBackend *MAB,

const MCSymbol *DummyDebugKey = nullptr;
NeedsEHFrameSection = !MOFI->getSupportsCompactUnwindWithoutEHFrame();
bool CanOmitDwarf = MOFI->getOmitDwarfIfHaveCompactUnwind();
for (unsigned i = 0, n = FrameArray.size(); i < n; ++i) {
const MCDwarfFrameInfo &Frame = FrameArray[i];

Expand All @@ -1545,7 +1546,7 @@ void MCDwarfFrameEmitter::Emit(MCObjectStreamer &Streamer, MCAsmBackend *MAB,
FDEEnd = nullptr;
}

if (!NeedsEHFrameSection && Frame.CompactUnwindEncoding !=
if (CanOmitDwarf && Frame.CompactUnwindEncoding !=
MOFI->getCompactUnwindDwarfEHFrameOnly())
// Don't generate an EH frame if we don't need one. I.e., it's taken care
// of by the compact unwind encoding.
Expand Down
14 changes: 12 additions & 2 deletions llvm/lib/MC/MCObjectFileInfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ static bool useCompactUnwind(const Triple &T) {
if (T.getArch() == Triple::aarch64)
return true;

// armv7k always has it.
if (T.isWatchOS())
return true;

// Use it on newer version of OS X.
if (T.isMacOSX() && !T.isMacOSXVersionLT(10, 6))
return true;
Expand All @@ -48,6 +52,9 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(Triple T) {
if (T.isOSDarwin() && T.getArch() == Triple::aarch64)
SupportsCompactUnwindWithoutEHFrame = true;

if (T.isWatchOS())
OmitDwarfIfHaveCompactUnwind = true;

PersonalityEncoding = dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel
| dwarf::DW_EH_PE_sdata4;
LSDAEncoding = FDECFIEncoding = dwarf::DW_EH_PE_pcrel;
Expand Down Expand Up @@ -193,9 +200,11 @@ void MCObjectFileInfo::initMachOMCObjectFileInfo(Triple T) {
SectionKind::getReadOnly());

if (T.getArch() == Triple::x86_64 || T.getArch() == Triple::x86)
CompactUnwindDwarfEHFrameOnly = 0x04000000;
CompactUnwindDwarfEHFrameOnly = 0x04000000; // UNWIND_X86_64_MODE_DWARF
else if (T.getArch() == Triple::aarch64)
CompactUnwindDwarfEHFrameOnly = 0x03000000;
CompactUnwindDwarfEHFrameOnly = 0x03000000; // UNWIND_ARM64_MODE_DWARF
else if (T.getArch() == Triple::arm || T.getArch() == Triple::thumb)
CompactUnwindDwarfEHFrameOnly = 0x04000000; // UNWIND_ARM_MODE_DWARF
}

// Debug Information.
Expand Down Expand Up @@ -767,6 +776,7 @@ void MCObjectFileInfo::InitMCObjectFileInfo(const Triple &TheTriple,
CommDirectiveSupportsAlignment = true;
SupportsWeakOmittedEHFrame = true;
SupportsCompactUnwindWithoutEHFrame = false;
OmitDwarfIfHaveCompactUnwind = false;

PersonalityEncoding = LSDAEncoding = FDECFIEncoding = TTypeEncoding =
dwarf::DW_EH_PE_absptr;
Expand Down
8 changes: 4 additions & 4 deletions llvm/lib/Target/ARM/ARMISelLowering.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,9 +813,9 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::STACKSAVE, MVT::Other, Expand);
setOperationAction(ISD::STACKRESTORE, MVT::Other, Expand);

if (!Subtarget->isTargetMachO()) {
// Non-MachO platforms may return values in these registers via the
// personality function.
if (!Subtarget->useSjLjEH()) {
// Platforms which do not use SjLj EH may return values in these registers
// via the personality function.
setExceptionPointerRegister(ARM::R0);
setExceptionSelectorRegister(ARM::R1);
}
Expand Down Expand Up @@ -889,7 +889,7 @@ ARMTargetLowering::ARMTargetLowering(const TargetMachine &TM,
setOperationAction(ISD::EH_SJLJ_SETJMP, MVT::i32, Custom);
setOperationAction(ISD::EH_SJLJ_LONGJMP, MVT::Other, Custom);
setOperationAction(ISD::EH_SJLJ_SETUP_DISPATCH, MVT::Other, Custom);
if (Subtarget->isTargetDarwin())
if (Subtarget->useSjLjEH())
setLibcallName(RTLIB::UNWIND_RESUME, "_Unwind_SjLj_Resume");

setOperationAction(ISD::SETCC, MVT::i32, Expand);
Expand Down
7 changes: 6 additions & 1 deletion llvm/lib/Target/ARM/ARMSubtarget.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ void ARMSubtarget::initializeEnvironment() {
UseNaClTrap = false;
GenLongCalls = false;
UnsafeFPMath = false;
UseSjLjEH = (isTargetDarwin() &&
TargetTriple.getSubArch() != Triple::ARMSubArch_v7k);
}

void ARMSubtarget::initSubtargetFeatures(StringRef CPU, StringRef FS) {
Expand Down Expand Up @@ -324,7 +326,10 @@ bool ARMSubtarget::enableAtomicExpand() const {
}

bool ARMSubtarget::useStride4VFPs(const MachineFunction &MF) const {
return isSwift() && !MF.getFunction()->optForMinSize();
// For general targets, the prologue can grow when VFPs are allocated with
// stride 4 (more vpush instructions). But WatchOS uses a compact unwind
// format which it's more important to get right.
return isTargetWatchOS() || (isSwift() && !MF.getFunction()->optForMinSize());
}

bool ARMSubtarget::useMovt(const MachineFunction &MF) const {
Expand Down
4 changes: 4 additions & 0 deletions llvm/lib/Target/ARM/ARMSubtarget.h
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,9 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
/// Target machine allowed unsafe FP math (such as use of NEON fp)
bool UnsafeFPMath;

/// UseSjLjEH - If true, the target uses SjLj exception handling (e.g. iOS).
bool UseSjLjEH;

/// stackAlignment - The minimum alignment known to hold of the stack frame on
/// entry to the function and which must be maintained by every function.
unsigned stackAlignment;
Expand Down Expand Up @@ -345,6 +348,7 @@ class ARMSubtarget : public ARMGenSubtargetInfo {
bool hasMPExtension() const { return HasMPExtension; }
bool hasDSP() const { return HasDSP; }
bool useNaClTrap() const { return UseNaClTrap; }
bool useSjLjEH() const { return UseSjLjEH; }
bool genLongCalls() const { return GenLongCalls; }

bool hasFP16() const { return HasFP16; }
Expand Down
219 changes: 218 additions & 1 deletion llvm/lib/Target/ARM/MCTargetDesc/ARMAsmBackend.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,15 @@
#include "llvm/MC/MCFixupKindInfo.h"
#include "llvm/MC/MCMachObjectWriter.h"
#include "llvm/MC/MCObjectWriter.h"
#include "llvm/MC/MCRegisterInfo.h"
#include "llvm/MC/MCSectionELF.h"
#include "llvm/MC/MCSectionMachO.h"
#include "llvm/MC/MCSubtargetInfo.h"
#include "llvm/MC/MCValue.h"
#include "llvm/Support/Debug.h"
#include "llvm/Support/ELF.h"
#include "llvm/Support/ErrorHandling.h"
#include "llvm/Support/Format.h"
#include "llvm/Support/MachO.h"
#include "llvm/Support/TargetParser.h"
#include "llvm/Support/raw_ostream.h"
Expand Down Expand Up @@ -779,6 +782,220 @@ void ARMAsmBackend::applyFixup(const MCFixup &Fixup, char *Data,
}
}

namespace CU {

/// \brief Compact unwind encoding values.
enum CompactUnwindEncodings {
UNWIND_ARM_MODE_MASK = 0x0F000000,
UNWIND_ARM_MODE_FRAME = 0x01000000,
UNWIND_ARM_MODE_FRAME_D = 0x02000000,
UNWIND_ARM_MODE_DWARF = 0x04000000,

UNWIND_ARM_FRAME_STACK_ADJUST_MASK = 0x00C00000,

UNWIND_ARM_FRAME_FIRST_PUSH_R4 = 0x00000001,
UNWIND_ARM_FRAME_FIRST_PUSH_R5 = 0x00000002,
UNWIND_ARM_FRAME_FIRST_PUSH_R6 = 0x00000004,

UNWIND_ARM_FRAME_SECOND_PUSH_R8 = 0x00000008,
UNWIND_ARM_FRAME_SECOND_PUSH_R9 = 0x00000010,
UNWIND_ARM_FRAME_SECOND_PUSH_R10 = 0x00000020,
UNWIND_ARM_FRAME_SECOND_PUSH_R11 = 0x00000040,
UNWIND_ARM_FRAME_SECOND_PUSH_R12 = 0x00000080,

UNWIND_ARM_FRAME_D_REG_COUNT_MASK = 0x00000F00,

UNWIND_ARM_DWARF_SECTION_OFFSET = 0x00FFFFFF
};

} // end CU namespace

/// Generate compact unwind encoding for the function based on the CFI
/// instructions. If the CFI instructions describe a frame that cannot be
/// encoded in compact unwind, the method returns UNWIND_ARM_MODE_DWARF which
/// tells the runtime to fallback and unwind using dwarf.
uint32_t ARMAsmBackendDarwin::generateCompactUnwindEncoding(
ArrayRef<MCCFIInstruction> Instrs) const {
DEBUG_WITH_TYPE("compact-unwind", llvm::dbgs() << "generateCU()\n");
// Only armv7k uses CFI based unwinding.
if (Subtype != MachO::CPU_SUBTYPE_ARM_V7K)
return 0;
// No .cfi directives means no frame.
if (Instrs.empty())
return 0;
// Start off assuming CFA is at SP+0.
int CFARegister = ARM::SP;
int CFARegisterOffset = 0;
// Mark savable registers as initially unsaved
DenseMap<unsigned, int> RegOffsets;
int FloatRegCount = 0;
// Process each .cfi directive and build up compact unwind info.
for (size_t i = 0, e = Instrs.size(); i != e; ++i) {
int Reg;
const MCCFIInstruction &Inst = Instrs[i];
switch (Inst.getOperation()) {
case MCCFIInstruction::OpDefCfa: // DW_CFA_def_cfa
CFARegisterOffset = -Inst.getOffset();
CFARegister = MRI.getLLVMRegNum(Inst.getRegister(), true);
break;
case MCCFIInstruction::OpDefCfaOffset: // DW_CFA_def_cfa_offset
CFARegisterOffset = -Inst.getOffset();
break;
case MCCFIInstruction::OpDefCfaRegister: // DW_CFA_def_cfa_register
CFARegister = MRI.getLLVMRegNum(Inst.getRegister(), true);
break;
case MCCFIInstruction::OpOffset: // DW_CFA_offset
Reg = MRI.getLLVMRegNum(Inst.getRegister(), true);
if (ARMMCRegisterClasses[ARM::GPRRegClassID].contains(Reg))
RegOffsets[Reg] = Inst.getOffset();
else if (ARMMCRegisterClasses[ARM::DPRRegClassID].contains(Reg)) {
RegOffsets[Reg] = Inst.getOffset();
++FloatRegCount;
} else {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << ".cfi_offset on unknown register="
<< Inst.getRegister() << "\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
break;
case MCCFIInstruction::OpRelOffset: // DW_CFA_advance_loc
// Ignore
break;
default:
// Directive not convertable to compact unwind, bail out.
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs()
<< "CFI directive not compatiable with comact "
"unwind encoding, opcode=" << Inst.getOperation()
<< "\n");
return CU::UNWIND_ARM_MODE_DWARF;
break;
}
}

// If no frame set up, return no unwind info.
if ((CFARegister == ARM::SP) && (CFARegisterOffset == 0))
return 0;

// Verify standard frame (lr/r7) was used.
if (CFARegister != ARM::R7) {
DEBUG_WITH_TYPE("compact-unwind", llvm::dbgs() << "frame register is "
<< CFARegister
<< " instead of r7\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
int StackAdjust = CFARegisterOffset - 8;
if (RegOffsets.lookup(ARM::LR) != (-4 - StackAdjust)) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs()
<< "LR not saved as standard frame, StackAdjust="
<< StackAdjust
<< ", CFARegisterOffset=" << CFARegisterOffset
<< ", lr save at offset=" << RegOffsets[14] << "\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
if (RegOffsets.lookup(ARM::R7) != (-8 - StackAdjust)) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << "r7 not saved as standard frame\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
uint32_t CompactUnwindEncoding = CU::UNWIND_ARM_MODE_FRAME;

// If var-args are used, there may be a stack adjust required.
switch (StackAdjust) {
case 0:
break;
case 4:
CompactUnwindEncoding |= 0x00400000;
break;
case 8:
CompactUnwindEncoding |= 0x00800000;
break;
case 12:
CompactUnwindEncoding |= 0x00C00000;
break;
default:
DEBUG_WITH_TYPE("compact-unwind", llvm::dbgs()
<< ".cfi_def_cfa stack adjust ("
<< StackAdjust << ") out of range\n");
return CU::UNWIND_ARM_MODE_DWARF;
}

// If r6 is saved, it must be right below r7.
static struct {
unsigned Reg;
unsigned Encoding;
} GPRCSRegs[] = {{ARM::R6, CU::UNWIND_ARM_FRAME_FIRST_PUSH_R6},
{ARM::R5, CU::UNWIND_ARM_FRAME_FIRST_PUSH_R5},
{ARM::R4, CU::UNWIND_ARM_FRAME_FIRST_PUSH_R4},
{ARM::R12, CU::UNWIND_ARM_FRAME_SECOND_PUSH_R12},
{ARM::R11, CU::UNWIND_ARM_FRAME_SECOND_PUSH_R11},
{ARM::R10, CU::UNWIND_ARM_FRAME_SECOND_PUSH_R10},
{ARM::R9, CU::UNWIND_ARM_FRAME_SECOND_PUSH_R9},
{ARM::R8, CU::UNWIND_ARM_FRAME_SECOND_PUSH_R8}};

int CurOffset = -8 - StackAdjust;
for (auto CSReg : GPRCSRegs) {
auto Offset = RegOffsets.find(CSReg.Reg);
if (Offset == RegOffsets.end())
continue;

int RegOffset = Offset->second;
if (RegOffset != CurOffset - 4) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << MRI.getName(CSReg.Reg) << " saved at "
<< RegOffset << " but only supported at "
<< CurOffset << "\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
CompactUnwindEncoding |= CSReg.Encoding;
CurOffset -= 4;
}

// If no floats saved, we are done.
if (FloatRegCount == 0)
return CompactUnwindEncoding;

// Switch mode to include D register saving.
CompactUnwindEncoding &= ~CU::UNWIND_ARM_MODE_MASK;
CompactUnwindEncoding |= CU::UNWIND_ARM_MODE_FRAME_D;

// FIXME: supporting more than 4 saved D-registers compactly would be trivial,
// but needs coordination with the linker and libunwind.
if (FloatRegCount > 4) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << "unsupported number of D registers saved ("
<< FloatRegCount << ")\n");
return CU::UNWIND_ARM_MODE_DWARF;
}

// Floating point registers must either be saved sequentially, or we defer to
// DWARF. No gaps allowed here so check that each saved d-register is
// precisely where it should be.
static unsigned FPRCSRegs[] = { ARM::D8, ARM::D10, ARM::D12, ARM::D14 };
for (int Idx = FloatRegCount - 1; Idx >= 0; --Idx) {
auto Offset = RegOffsets.find(FPRCSRegs[Idx]);
if (Offset == RegOffsets.end()) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << FloatRegCount << " D-regs saved, but "
<< MRI.getName(FPRCSRegs[Idx])
<< " not saved\n");
return CU::UNWIND_ARM_MODE_DWARF;
} else if (Offset->second != CurOffset - 8) {
DEBUG_WITH_TYPE("compact-unwind",
llvm::dbgs() << FloatRegCount << " D-regs saved, but "
<< MRI.getName(FPRCSRegs[Idx])
<< " saved at " << Offset->second
<< ", expected at " << CurOffset - 8
<< "\n");
return CU::UNWIND_ARM_MODE_DWARF;
}
CurOffset -= 8;
}

return CompactUnwindEncoding | ((FloatRegCount - 1) << 8);
}

static MachO::CPUSubTypeARM getMachOSubTypeFromArch(StringRef Arch) {
unsigned AK = ARM::parseArch(Arch);
switch (AK) {
Expand Down Expand Up @@ -821,7 +1038,7 @@ MCAsmBackend *llvm::createARMAsmBackend(const Target &T,
llvm_unreachable("unsupported object format");
case Triple::MachO: {
MachO::CPUSubTypeARM CS = getMachOSubTypeFromArch(TheTriple.getArchName());
return new ARMAsmBackendDarwin(T, TheTriple, CS);
return new ARMAsmBackendDarwin(T, TheTriple, MRI, CS);
}
case Triple::COFF:
assert(TheTriple.isOSWindows() && "non-Windows ARM COFF is not supported");
Expand Down
Loading

0 comments on commit f8e47e4

Please sign in to comment.