[XCOFF][AIX] Generate LSDA data and compact unwind section on AIX

Summary:
AIX uses the existing EH infrastructure in clang and llvm.
The major differences would be
1. AIX do not have CFI instructions.
2. AIX uses a new personality routine, named __xlcxx_personality_v1.
   It doesn't use the GCC personality rountine, because the
   interoperability is not there yet on AIX.
3. AIX do not use eh_frame sections. Instead, it would use a eh_info
section (compat unwind section) to store the information about
personality routine and LSDA data address.

Reviewed By: daltenty, hubert.reinterpretcast

Differential Revision: https://reviews.llvm.org/D91455
This commit is contained in:
jasonliu 2020-12-02 14:48:52 +00:00
parent 9d6d24c250
commit a65d8c5d72
20 changed files with 319 additions and 10 deletions

View File

@ -612,6 +612,7 @@ struct EHPersonality {
static const EHPersonality MSVC_C_specific_handler;
static const EHPersonality MSVC_CxxFrameHandler3;
static const EHPersonality GNU_Wasm_CPlusPlus;
static const EHPersonality XL_CPlusPlus;
/// Does this personality use landingpads or the family of pad instructions
/// designed to form funclets?

View File

@ -113,6 +113,8 @@ const EHPersonality
EHPersonality::MSVC_CxxFrameHandler3 = { "__CxxFrameHandler3", nullptr };
const EHPersonality
EHPersonality::GNU_Wasm_CPlusPlus = { "__gxx_wasm_personality_v0", nullptr };
const EHPersonality EHPersonality::XL_CPlusPlus = {"__xlcxx_personality_v1",
nullptr};
static const EHPersonality &getCPersonality(const TargetInfo &Target,
const LangOptions &L) {
@ -161,6 +163,8 @@ static const EHPersonality &getCXXPersonality(const TargetInfo &Target,
const llvm::Triple &T = Target.getTriple();
if (T.isWindowsMSVCEnvironment())
return EHPersonality::MSVC_CxxFrameHandler3;
if (T.isOSAIX())
return EHPersonality::XL_CPlusPlus;
if (L.SjLjExceptions)
return EHPersonality::GNU_CPlusPlus_SJLJ;
if (L.DWARFExceptions)

View File

@ -12,6 +12,9 @@
// RUN: %clang_cc1 -triple i686-unknown-windows-gnu -fexceptions -fseh-exceptions -fcxx-exceptions -S -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-GNU-SEH
// RUN: %clang_cc1 -triple i686-unknown-windows-gnu -fexceptions -fsjlj-exceptions -fcxx-exceptions -S -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-GNU-SJLJ
// RUN: %clang_cc1 -triple powerpc-unknown-aix-xcoff -fexceptions -fcxx-exceptions -S -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-AIX
// RUN: %clang_cc1 -triple powerpc64-unknown-aix-xcoff -fexceptions -fcxx-exceptions -S -emit-llvm %s -o - | FileCheck %s -check-prefix CHECK-AIX
extern void g();
// CHECK-GNU: personality i8* bitcast (i32 (...)* @__gxx_personality_v0 to i8*)
@ -21,6 +24,8 @@ extern void g();
// CHECK-WIN: personality i8* bitcast (i32 (...)* @__CxxFrameHandler3 to i8*)
// CHECK-AIX: personality i8* bitcast (i32 (...)* @__xlcxx_personality_v1 to i8*)
void f() {
try {
g();

View File

@ -32,7 +32,8 @@ enum class EHPersonality {
MSVC_CXX,
CoreCLR,
Rust,
Wasm_CXX
Wasm_CXX,
XL_CXX
};
/// See if the given exception handling personality function is one

View File

@ -603,7 +603,7 @@ public:
unsigned GetSizeOfEncodedValue(unsigned Encoding) const;
/// Emit reference to a ttype global with a specified encoding.
void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) const;
virtual void emitTTypeReference(const GlobalValue *GV, unsigned Encoding);
/// Emit a reference to a symbol for use in dwarf. Different object formats
/// represent this in different ways. Some use a relocation others encode

View File

@ -22,6 +22,7 @@ enum class ExceptionHandling {
ARM, ///< ARM EHABI
WinEH, ///< Windows Exception Handling
Wasm, ///< WebAssembly Exception Handling
AIX, ///< AIX Exception Handling
};
enum class DebugCompressionType {

View File

@ -39,6 +39,7 @@ EHPersonality llvm::classifyEHPersonality(const Value *Pers) {
.Case("ProcessCLRException", EHPersonality::CoreCLR)
.Case("rust_eh_personality", EHPersonality::Rust)
.Case("__gxx_wasm_personality_v0", EHPersonality::Wasm_CXX)
.Case("__xlcxx_personality_v1", EHPersonality::XL_CXX)
.Default(EHPersonality::Unknown);
}
@ -57,6 +58,8 @@ StringRef llvm::getEHPersonalityName(EHPersonality Pers) {
case EHPersonality::CoreCLR: return "ProcessCLRException";
case EHPersonality::Rust: return "rust_eh_personality";
case EHPersonality::Wasm_CXX: return "__gxx_wasm_personality_v0";
case EHPersonality::XL_CXX:
return "__xlcxx_personality_v1";
case EHPersonality::Unknown: llvm_unreachable("Unknown EHPersonality!");
}

View File

@ -0,0 +1,82 @@
//===-- CodeGen/AsmPrinter/AIXException.cpp - AIX Exception Impl ----------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file contains support for writing AIX exception info into asm files.
//
//===----------------------------------------------------------------------===//
#include "DwarfException.h"
#include "llvm/CodeGen/AsmPrinter.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/MC/MCSectionXCOFF.h"
#include "llvm/MC/MCStreamer.h"
#include "llvm/Target/TargetLoweringObjectFile.h"
#include "llvm/Target/TargetMachine.h"
namespace llvm {
AIXException::AIXException(AsmPrinter *A) : DwarfCFIExceptionBase(A) {}
void AIXException::emitExceptionInfoTable(const MCSymbol *LSDA,
const MCSymbol *PerSym) {
// Generate EH Info Table.
// The EH Info Table, aka, 'compat unwind section' on AIX, have the following
// format: struct eh_info_t {
// unsigned version; /* EH info verion 0 */
// #if defined(__64BIT__)
// char _pad[4]; /* padding */
// #endif
// unsigned long lsda; /* Pointer to LSDA */
// unsigned long personality; /* Pointer to the personality routine */
// }
Asm->OutStreamer->SwitchSection(
Asm->getObjFileLowering().getCompactUnwindSection());
MCSymbol *EHInfoLabel = MMI->getContext().getOrCreateSymbol(
"__ehinfo." + Twine(Asm->getFunctionNumber()));
Asm->OutStreamer->emitLabel(EHInfoLabel);
// Version number.
Asm->emitInt32(0);
const DataLayout &DL = MMI->getModule()->getDataLayout();
const unsigned PointerSize = DL.getPointerSize();
// Add necessary paddings in 64 bit mode.
Asm->OutStreamer->emitValueToAlignment(PointerSize);
// LSDA location.
Asm->OutStreamer->emitValue(MCSymbolRefExpr::create(LSDA, Asm->OutContext),
PointerSize);
// Personality routine.
Asm->OutStreamer->emitValue(MCSymbolRefExpr::create(PerSym, Asm->OutContext),
PointerSize);
}
void AIXException::endFunction(const MachineFunction *MF) {
const Function &F = MF->getFunction();
bool HasLandingPads = !MF->getLandingPads().empty();
const Function *Per = nullptr;
if (F.hasPersonalityFn())
Per = dyn_cast<Function>(F.getPersonalityFn()->stripPointerCasts());
bool EmitEHBlock =
HasLandingPads || (F.hasPersonalityFn() &&
!isNoOpWithoutInvoke(classifyEHPersonality(Per)) &&
F.needsUnwindTableEntry());
if (!EmitEHBlock)
return;
const MCSymbol *LSDALabel = emitExceptionTable();
const MCSymbol *PerSym = Asm->TM.getSymbol(Per);
emitExceptionInfoTable(LSDALabel, PerSym);
}
} // End of namespace llvm

View File

@ -380,6 +380,9 @@ bool AsmPrinter::doInitialization(Module &M) {
case ExceptionHandling::Wasm:
ES = new WasmException(this);
break;
case ExceptionHandling::AIX:
ES = new AIXException(this);
break;
}
if (ES)
Handlers.emplace_back(std::unique_ptr<EHStreamer>(ES), EHTimerName,

View File

@ -98,6 +98,12 @@ static const char *DecodeDWARFEncoding(unsigned Encoding) {
case dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_pcrel | dwarf::DW_EH_PE_sdata8
:
return "indirect pcrel sdata8";
case dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_datarel |
dwarf::DW_EH_PE_sdata4:
return "indirect datarel sdata4";
case dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_datarel |
dwarf::DW_EH_PE_sdata8:
return "indirect datarel sdata8";
}
return "<unknown encoding>";
@ -138,8 +144,7 @@ unsigned AsmPrinter::GetSizeOfEncodedValue(unsigned Encoding) const {
}
}
void AsmPrinter::emitTTypeReference(const GlobalValue *GV,
unsigned Encoding) const {
void AsmPrinter::emitTTypeReference(const GlobalValue *GV, unsigned Encoding) {
if (GV) {
const TargetLoweringObjectFile &TLOF = getObjFileLowering();

View File

@ -1,6 +1,7 @@
add_llvm_component_library(LLVMAsmPrinter
AccelTable.cpp
AddressPool.cpp
AIXException.cpp
ARMException.cpp
AsmPrinter.cpp
AsmPrinterDwarf.cpp

View File

@ -92,6 +92,20 @@ public:
/// Gather and emit post-function exception information.
void endFunction(const MachineFunction *) override;
};
class LLVM_LIBRARY_VISIBILITY AIXException : public DwarfCFIExceptionBase {
/// This is AIX's compat unwind section, which unwinder would use
/// to find the location of LSDA area and personality rountine.
void emitExceptionInfoTable(const MCSymbol *LSDA, const MCSymbol *PerSym);
public:
AIXException(AsmPrinter *A);
void endModule() override {}
void beginFunction(const MachineFunction *MF) override {}
void endFunction(const MachineFunction *MF) override;
};
} // End of namespace llvm
#endif

View File

@ -288,11 +288,13 @@ void EHStreamer::computeCallSiteTable(
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 && Asm->MAI->usesCFIForEH()) {
// For Dwarf and AIX 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 &&
(Asm->MAI->usesCFIForEH() ||
Asm->MAI->getExceptionHandlingType() == ExceptionHandling::AIX)) {
CallSites.push_back({LastLabel, BeginLabel, nullptr, 0});
PreviousIsInvoke = false;
}

View File

@ -2272,9 +2272,13 @@ MCSection *TargetLoweringObjectFileXCOFF::getSectionForConstant(
void TargetLoweringObjectFileXCOFF::Initialize(MCContext &Ctx,
const TargetMachine &TgtM) {
TargetLoweringObjectFile::Initialize(Ctx, TgtM);
TTypeEncoding = 0;
TTypeEncoding =
dwarf::DW_EH_PE_indirect | dwarf::DW_EH_PE_datarel |
(TgtM.getTargetTriple().isArch32Bit() ? dwarf::DW_EH_PE_sdata4
: dwarf::DW_EH_PE_sdata8);
PersonalityEncoding = 0;
LSDAEncoding = 0;
CallSiteEncoding = dwarf::DW_EH_PE_udata4;
}
MCSection *TargetLoweringObjectFileXCOFF::getStaticCtorSection(

View File

@ -735,6 +735,7 @@ void TargetPassConfig::addPassesToHandleExceptions() {
LLVM_FALLTHROUGH;
case ExceptionHandling::DwarfCFI:
case ExceptionHandling::ARM:
case ExceptionHandling::AIX:
addPass(createDwarfEHPass(getOptLevel()));
break;
case ExceptionHandling::WinEH:

View File

@ -37,6 +37,8 @@ MCAsmInfoXCOFF::MCAsmInfoXCOFF() {
HasDotTypeDotSizeDirective = false;
UseIntegratedAssembler = false;
NeedsFunctionDescriptors = true;
ExceptionsType = ExceptionHandling::AIX;
}
bool MCAsmInfoXCOFF::isAcceptableChar(char C) const {

View File

@ -883,6 +883,14 @@ void MCObjectFileInfo::initXCOFFMCObjectFileInfo(const Triple &T) {
// The TOC-base always has 0 size, but 4 byte alignment.
TOCBaseSection->setAlignment(Align(4));
LSDASection = Ctx->getXCOFFSection(".gcc_except_table",
XCOFF::StorageMappingClass::XMC_RO,
XCOFF::XTY_SD, SectionKind::getReadOnly());
CompactUnwindSection =
Ctx->getXCOFFSection(".eh_info_table", XCOFF::StorageMappingClass::XMC_RW,
XCOFF::XTY_SD, SectionKind::getData());
// DWARF sections for XCOFF are not csects. They are special STYP_DWARF
// sections, and the individual DWARF sections are distinguished by their
// section subtype.

View File

@ -193,6 +193,8 @@ public:
void emitInstruction(const MachineInstr *MI) override;
bool doFinalization(Module &M) override;
void emitTTypeReference(const GlobalValue *GV, unsigned Encoding) override;
};
} // end anonymous namespace
@ -2056,6 +2058,23 @@ void PPCAIXAsmPrinter::emitXXStructorList(const DataLayout &DL,
}
}
void PPCAIXAsmPrinter::emitTTypeReference(const GlobalValue *GV,
unsigned Encoding) {
if (GV) {
MCSymbol *TypeInfoSym = TM.getSymbol(GV);
MCSymbol *TOCEntry = lookUpOrCreateTOCEntry(TypeInfoSym);
const MCSymbol *TOCBaseSym =
cast<MCSectionXCOFF>(getObjFileLowering().getTOCBaseSection())
->getQualNameSymbol();
auto &Ctx = OutStreamer->getContext();
const MCExpr *Exp =
MCBinaryExpr::createSub(MCSymbolRefExpr::create(TOCEntry, Ctx),
MCSymbolRefExpr::create(TOCBaseSym, Ctx), Ctx);
OutStreamer->emitValue(Exp, GetSizeOfEncodedValue(Encoding));
} else
OutStreamer->emitIntValue(0, GetSizeOfEncodedValue(Encoding));
}
// Return a pass that prints the PPC assembly code for a MachineFunction to the
// given output stream.
static AsmPrinter *

View File

@ -3109,6 +3109,7 @@ static bool isCatchAll(EHPersonality Personality, Constant *TypeInfo) {
case EHPersonality::MSVC_CXX:
case EHPersonality::CoreCLR:
case EHPersonality::Wasm_CXX:
case EHPersonality::XL_CXX:
return TypeInfo->isNullValue();
}
llvm_unreachable("invalid enum");

View File

@ -0,0 +1,152 @@
; RUN: llc -verify-machineinstrs -mtriple powerpc-ibm-aix-xcoff -mcpu=pwr4 \
; RUN: -mattr=-altivec < %s | \
; RUN: FileCheck --check-prefixes=ASM,ASM32 %s
; RUN: llc -verify-machineinstrs -mtriple powerpc64-ibm-aix-xcoff -mcpu=pwr4 \
; RUN: -mattr=-altivec < %s | \
; RUN: FileCheck --check-prefixes=ASM,ASM64 %s
@_ZTIi = external constant i8*
define void @_Z9throwFuncv() {
entry:
%exception = call i8* @__cxa_allocate_exception(i32 4) #2
%0 = bitcast i8* %exception to i32*
store i32 1, i32* %0, align 16
call void @__cxa_throw(i8* %exception, i8* bitcast (i8** @_ZTIi to i8*), i8* null) #3
unreachable
}
; ASM: ._Z9throwFuncv:
; ASM: bl .__cxa_allocate_exception[PR]
; ASM: nop
; ASM32: lwz 4, L..C0(2)
; ASM64: ld 4, L..C0(2)
; ASM: bl .__cxa_throw[PR]
; ASM: nop
define i32 @_Z9catchFuncv() personality i8* bitcast (i32 (...)* @__xlcxx_personality_v1 to i8*) {
entry:
%retval = alloca i32, align 4
%exn.slot = alloca i8*, align 4
%ehselector.slot = alloca i32, align 4
%0 = alloca i32, align 4
invoke void @_Z9throwFuncv()
to label %invoke.cont unwind label %lpad
invoke.cont: ; preds = %entry
br label %try.cont
lpad: ; preds = %entry
%1 = landingpad { i8*, i32 }
catch i8* bitcast (i8** @_ZTIi to i8*)
%2 = extractvalue { i8*, i32 } %1, 0
store i8* %2, i8** %exn.slot, align 4
%3 = extractvalue { i8*, i32 } %1, 1
store i32 %3, i32* %ehselector.slot, align 4
br label %catch.dispatch
catch.dispatch: ; preds = %lpad
%sel = load i32, i32* %ehselector.slot, align 4
%4 = call i32 @llvm.eh.typeid.for(i8* bitcast (i8** @_ZTIi to i8*)) #2
%matches = icmp eq i32 %sel, %4
br i1 %matches, label %catch, label %eh.resume
catch: ; preds = %catch.dispatch
%exn = load i8*, i8** %exn.slot, align 4
%5 = call i8* @__cxa_begin_catch(i8* %exn) #2
%6 = bitcast i8* %5 to i32*
%7 = load i32, i32* %6, align 4
store i32 %7, i32* %0, align 4
store i32 2, i32* %retval, align 4
call void @__cxa_end_catch() #2
br label %return
try.cont: ; preds = %invoke.cont
store i32 1, i32* %retval, align 4
br label %return
return: ; preds = %try.cont, %catch
%8 = load i32, i32* %retval, align 4
ret i32 %8
eh.resume: ; preds = %catch.dispatch
%exn1 = load i8*, i8** %exn.slot, align 4
%sel2 = load i32, i32* %ehselector.slot, align 4
%lpad.val = insertvalue { i8*, i32 } undef, i8* %exn1, 0
%lpad.val3 = insertvalue { i8*, i32 } %lpad.val, i32 %sel2, 1
resume { i8*, i32 } %lpad.val3
}
; ASM: ._Z9catchFuncv:
; ASM: L..func_begin0:
; ASM: # %bb.0: # %entry
; ASM: mflr 0
; ASM: L..tmp0:
; ASM: bl ._Z9throwFuncv
; ASM: nop
; ASM: L..tmp1:
; ASM: # %bb.1: # %invoke.cont
; ASM: li 3, 1
; ASM: L..BB1_2: # %return
; ASM: mtlr 0
; ASM: blr
; ASM: L..BB1_3: # %lpad
; ASM: L..tmp2:
; ASM: bl .__cxa_begin_catch[PR]
; ASM: nop
; ASM: bl .__cxa_end_catch[PR]
; ASM: nop
; ASM: b L..BB1_2
; ASM: L..func_end0:
; ASM: .csect .gcc_except_table[RO],2
; ASM: .align 2
; ASM: GCC_except_table1:
; ASM: L..exception0:
; ASM: .byte 255 # @LPStart Encoding = omit
; ASM32: .byte 187 # @TType Encoding = indirect datarel sdata4
; ASM64: .byte 188 # @TType Encoding = indirect datarel sdata8
; ASM: .uleb128 L..ttbase0-L..ttbaseref0
; ASM: L..ttbaseref0:
; ASM: .byte 3 # Call site Encoding = udata4
; ASM: .uleb128 L..cst_end0-L..cst_begin0
; ASM: L..cst_begin0:
; ASM: .vbyte 4, L..tmp0-L..func_begin0 # >> Call Site 1 <<
; ASM: .vbyte 4, L..tmp1-L..tmp0 # Call between L..tmp0 and L..tmp1
; ASM: .vbyte 4, L..tmp2-L..func_begin0 # jumps to L..tmp2
; ASM: .byte 1 # On action: 1
; ASM: .vbyte 4, L..tmp1-L..func_begin0 # >> Call Site 2 <<
; ASM: .vbyte 4, L..func_end0-L..tmp1 # Call between L..tmp1 and L..func_end0
; ASM: .vbyte 4, 0 # has no landing pad
; ASM: .byte 0 # On action: cleanup
; ASM: L..cst_end0:
; ASM: .byte 1 # >> Action Record 1 <<
; ASM: # Catch TypeInfo 1
; ASM: .byte 0 # No further actions
; ASM: .align 2
; ASM: # >> Catch TypeInfos <<
; ASM32: .vbyte 4, L..C0-TOC[TC0] # TypeInfo 1
; ASM64: .vbyte 8, L..C0-TOC[TC0] # TypeInfo 1
; ASM: L..ttbase0:
; ASM: .align 2
; ASM: .csect .eh_info_table[RW],2
; ASM: __ehinfo.1:
; ASM: .vbyte 4, 0
; ASM32: .align 2
; ASM32: .vbyte 4, GCC_except_table1
; ASM32: .vbyte 4, __xlcxx_personality_v1[DS]
; ASM64: .align 3
; ASM64: .vbyte 8, GCC_except_table1
; ASM64: .vbyte 8, __xlcxx_personality_v1[DS]
; ASM: .toc
; ASM: L..C0:
; ASM: .tc _ZTIi[TC],_ZTIi[UA]
declare i8* @__cxa_allocate_exception(i32)
declare void @__cxa_throw(i8*, i8*, i8*)
declare i32 @__xlcxx_personality_v1(...)
declare i32 @llvm.eh.typeid.for(i8*)
declare i8* @__cxa_begin_catch(i8*)
declare void @__cxa_end_catch()