[PowerPC] XCOFF exception section support on the direct assembler path

This feature implements support for making entries in the exception section
on XCOFF on the direct assembly path using the ".except" pseudo-op. It also
provides functionality to lower entries (comprised of language and reason
codes) into the exception section through the use of annotation metadata
attached to llvm.ppc.trap/trapd/tw/tdw intrinsics. Integrated assembler
support will be provided in another review. https://reviews.llvm.org/D133030
needs to merge first for LIT tests

Reviewed By: shchenz, RKSimon

Differential Revision: https://reviews.llvm.org/D132146
This commit is contained in:
Paul Scoropan 2022-09-26 22:03:49 -04:00 committed by Chen Zheng
parent 1172bdecfa
commit ce004fb4f2
16 changed files with 237 additions and 14 deletions

View File

@ -4623,6 +4623,12 @@ public:
const AsmOperandInfo &OpInfo,
SelectionDAG &DAG) const;
// Targets may override this function to collect operands from the CallInst
// and for example, lower them into the SelectionDAG operands.
virtual void CollectTargetIntrinsicOperands(const CallInst &I,
SmallVectorImpl<SDValue> &Ops,
SelectionDAG &DAG) const;
//===--------------------------------------------------------------------===//
// Div utility functions
//

View File

@ -628,6 +628,16 @@ public:
/// changed at the end of assembly.
virtual void emitXCOFFRenameDirective(const MCSymbol *Name, StringRef Rename);
/// Emit an XCOFF .except directive which adds information about
/// a trap instruction to the object file exception section
///
/// \param Symbol - The function containing the trap.
/// \param Lang - The language code for the exception entry.
/// \param Reason - The reason code for the exception entry.
virtual void emitXCOFFExceptDirective(const MCSymbol *Symbol, MCSymbol *Trap,
unsigned Lang, unsigned Reason,
unsigned FunctionSize, bool hasDebug);
/// Emit a XCOFF .ref directive which creates R_REF type entry in the
/// relocation table for one or more symbols.
///

View File

@ -41,6 +41,9 @@ public:
report_fatal_error("emitXCOFFRenameDirective is not implemented yet on "
"object generation path");
}
void emitXCOFFExceptDirective(const MCSymbol *Symbol, MCSymbol *Trap,
unsigned Lang, unsigned Reason,
unsigned FunctionSize, bool hasDebug) override;
};
} // end namespace llvm

View File

@ -4865,6 +4865,8 @@ void SelectionDAGBuilder::visitTargetIntrinsic(const CallInst &I,
// Create the node.
SDValue Result;
// In some cases, custom collection of operands from CallInst I may be needed.
TLI.CollectTargetIntrinsicOperands(I, Ops, DAG);
if (IsTgtIntrinsic) {
// This is target intrinsic that touches memory
Result =

View File

@ -5256,6 +5256,12 @@ void TargetLowering::LowerAsmOperandForConstraint(SDValue Op,
}
}
void TargetLowering::CollectTargetIntrinsicOperands(const CallInst &I,
SmallVectorImpl<SDValue> &Ops,
SelectionDAG &DAG) const {
return;
}
std::pair<unsigned, const TargetRegisterClass *>
TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *RI,
StringRef Constraint,

View File

@ -197,6 +197,10 @@ public:
void emitXCOFFRefDirective(StringRef Name) override;
void emitXCOFFExceptDirective(const MCSymbol *Symbol, MCSymbol *Trap,
unsigned Lang, unsigned Reason,
unsigned FunctionSize, bool hasDebug) override;
void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override;
void emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
unsigned ByteAlignment) override;
@ -942,6 +946,17 @@ void MCAsmStreamer::emitXCOFFRefDirective(StringRef Name) {
EmitEOL();
}
void MCAsmStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol,
MCSymbol *Trap, unsigned Lang,
unsigned Reason,
unsigned FunctionSize,
bool hasDebug) {
OS << "\t.except\t";
Symbol->print(OS, MAI);
OS << ", " << Lang << ", " << Reason;
EmitEOL();
}
void MCAsmStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {
assert(MAI->hasDotTypeDotSizeDirective());
OS << "\t.size\t";

View File

@ -1190,6 +1190,15 @@ void MCStreamer::emitXCOFFRefDirective(StringRef Name) {
llvm_unreachable("emitXCOFFRefDirective is only supported on XCOFF targets");
}
void MCStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol,
MCSymbol *Trap, unsigned Lang,
unsigned Reason,
unsigned FunctionSize,
bool hasDebug) {
report_fatal_error("emitXCOFFExceptDirective is only supported on "
"XCOFF targets");
}
void MCStreamer::emitELFSize(MCSymbol *Symbol, const MCExpr *Value) {}
void MCStreamer::emitELFSymverDirective(const MCSymbol *OriginalSym,
StringRef Name, bool KeepOriginalSym) {}

View File

@ -81,6 +81,16 @@ void MCXCOFFStreamer::emitXCOFFSymbolLinkageWithVisibility(
emitSymbolAttribute(Symbol, Visibility);
}
void MCXCOFFStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol,
MCSymbol *Trap, unsigned Lang,
unsigned Reason,
unsigned FunctionSize,
bool hasDebug) {
report_fatal_error(
"emitXCOFFExceptDirective not yet supported for integrated "
"assembler path.");
}
void MCXCOFFStreamer::emitCommonSymbol(MCSymbol *Symbol, uint64_t Size,
unsigned ByteAlignment) {
getAssembler().registerSymbol(*Symbol);

View File

@ -2646,6 +2646,25 @@ void PPCAIXAsmPrinter::emitInstruction(const MachineInstr *MI) {
switch (MI->getOpcode()) {
default:
break;
case PPC::TW:
case PPC::TWI:
case PPC::TD:
case PPC::TDI: {
if (MI->getNumOperands() < 5)
break;
const MachineOperand &LangMO = MI->getOperand(3);
const MachineOperand &ReasonMO = MI->getOperand(4);
if (!LangMO.isImm() || !ReasonMO.isImm())
break;
MCSymbol *TempSym = OutContext.createNamedTempSymbol();
OutStreamer->emitLabel(TempSym);
OutStreamer->emitXCOFFExceptDirective(CurrentFnSym, TempSym,
LangMO.getImm(), ReasonMO.getImm(),
Subtarget->isPPC64() ? MI->getMF()->getInstructionCount() * 8 :
MI->getMF()->getInstructionCount() * 4,
MMI->hasDebugInfo());
break;
}
case PPC::GETtlsADDR64AIX:
case PPC::GETtlsADDR32AIX: {
// The reference to .__tls_get_addr is unknown to the assembler

View File

@ -5053,8 +5053,18 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
case ISD::INTRINSIC_VOID: {
auto IntrinsicID = N->getConstantOperandVal(1);
if (IntrinsicID == Intrinsic::ppc_tdw || IntrinsicID == Intrinsic::ppc_tw) {
unsigned Opcode = IntrinsicID == Intrinsic::ppc_tdw ? PPC::TDI : PPC::TWI;
if (IntrinsicID != Intrinsic::ppc_tdw && IntrinsicID != Intrinsic::ppc_tw &&
IntrinsicID != Intrinsic::ppc_trapd &&
IntrinsicID != Intrinsic::ppc_trap)
break;
unsigned Opcode = (IntrinsicID == Intrinsic::ppc_tdw ||
IntrinsicID == Intrinsic::ppc_trapd)
? PPC::TDI
: PPC::TWI;
SmallVector<SDValue, 4> OpsWithMD;
unsigned MDIndex;
if (IntrinsicID == Intrinsic::ppc_tdw ||
IntrinsicID == Intrinsic::ppc_tw) {
SDValue Ops[] = {N->getOperand(4), N->getOperand(2), N->getOperand(3)};
int16_t SImmOperand2;
int16_t SImmOperand3;
@ -5090,10 +5100,31 @@ void PPCDAGToDAGISel::Select(SDNode *N) {
Ops[1] = N->getOperand(3);
Ops[2] = getI32Imm(int(SImmOperand2) & 0xFFFF, dl);
}
CurDAG->SelectNodeTo(N, Opcode, MVT::Other, Ops);
return;
OpsWithMD = {Ops[0], Ops[1], Ops[2]};
MDIndex = 5;
} else {
OpsWithMD = {getI32Imm(24, dl), N->getOperand(2), getI32Imm(0, dl)};
MDIndex = 3;
}
break;
if (N->getNumOperands() > MDIndex) {
SDValue MDV = N->getOperand(MDIndex);
const MDNode *MD = cast<MDNodeSDNode>(MDV)->getMD();
assert(MD->getNumOperands() != 0 && "Empty MDNode in operands!");
assert((isa<MDString>(MD->getOperand(0)) && cast<MDString>(
MD->getOperand(0))->getString().equals("ppc-trap-reason"))
&& "Unsupported annotation data type!");
for (unsigned i = 1; i < MD->getNumOperands(); i++) {
assert(isa<MDString>(MD->getOperand(i)) &&
"Invalid data type for annotation ppc-trap-reason!");
OpsWithMD.push_back(
getI32Imm(std::stoi(cast<MDString>(
MD->getOperand(i))->getString().str()), dl));
}
}
OpsWithMD.push_back(N->getOperand(0)); // chain
CurDAG->SelectNodeTo(N, Opcode, MVT::Other, OpsWithMD);
return;
}
case ISD::INTRINSIC_WO_CHAIN: {

View File

@ -16227,6 +16227,24 @@ void PPCTargetLowering::LowerAsmOperandForConstraint(SDValue Op,
TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG);
}
void PPCTargetLowering::CollectTargetIntrinsicOperands(const CallInst &I,
SmallVectorImpl<SDValue> &Ops,
SelectionDAG &DAG) const {
if (I.getNumOperands() <= 1)
return;
if (!isa<ConstantSDNode>(Ops[1].getNode()))
return;
auto IntrinsicID = cast<ConstantSDNode>(Ops[1].getNode())->getZExtValue();
if (IntrinsicID != Intrinsic::ppc_tdw && IntrinsicID != Intrinsic::ppc_tw &&
IntrinsicID != Intrinsic::ppc_trapd && IntrinsicID != Intrinsic::ppc_trap)
return;
if (I.hasMetadata("annotation")) {
MDNode *MDN = I.getMetadata("annotation");
Ops.push_back(DAG.getMDNode(MDN));
}
}
// isLegalAddressingMode - Return true if the addressing mode represented
// by AM is legal for this target, for a load/store of the specified type.
bool PPCTargetLowering::isLegalAddressingMode(const DataLayout &DL,

View File

@ -995,6 +995,10 @@ namespace llvm {
return TargetLowering::getInlineAsmMemConstraint(ConstraintCode);
}
void CollectTargetIntrinsicOperands(const CallInst &I,
SmallVectorImpl<SDValue> &Ops,
SelectionDAG &DAG) const override;
/// isLegalAddressingMode - Return true if the addressing mode represented
/// by AM is legal for this target, for a load/store of the specified type.
bool isLegalAddressingMode(const DataLayout &DL, const AddrMode &AM,

View File

@ -1973,9 +1973,6 @@ def SLBSYNC : XForm_0<31, 338, (outs), (ins), "slbsync", IIC_SprSLBSYNC, []>;
def : Pat<(int_ppc_stdcx ForceXForm:$dst, g8rc:$A),
(STDCX g8rc:$A, ForceXForm:$dst)>;
// trapd
def : Pat<(int_ppc_trapd g8rc:$A),
(TDI 24, $A, 0)>;
def : Pat<(i64 (int_ppc_mfspr timm:$SPR)),
(MFSPR8 $SPR)>;
def : Pat<(int_ppc_mtspr timm:$SPR, g8rc:$RT),

View File

@ -1770,13 +1770,13 @@ def STWAT : X_RD5_RS5_IM5<31, 710, (outs), (ins gprc:$rS, gprc:$rA, u5imm:$FC),
let isTerminator = 1, isBarrier = 1, hasCtrlDep = 1 in
def TRAP : XForm_24<31, 4, (outs), (ins), "trap", IIC_LdStLoad, [(trap)]>;
def TWI : DForm_base<3, (outs), (ins u5imm:$to, gprc:$rA, s16imm:$imm),
def TWI : DForm_base<3, (outs), (ins u5imm:$to, gprc:$rA, s16imm:$imm, variable_ops),
"twi $to, $rA, $imm", IIC_IntTrapW, []>;
def TW : XForm_1<31, 4, (outs), (ins u5imm:$to, gprc:$rA, gprc:$rB),
def TW : XForm_1<31, 4, (outs), (ins u5imm:$to, gprc:$rA, gprc:$rB, variable_ops),
"tw $to, $rA, $rB", IIC_IntTrapW, []>;
def TDI : DForm_base<2, (outs), (ins u5imm:$to, g8rc:$rA, s16imm:$imm),
def TDI : DForm_base<2, (outs), (ins u5imm:$to, g8rc:$rA, s16imm:$imm, variable_ops),
"tdi $to, $rA, $imm", IIC_IntTrapD, []>;
def TD : XForm_1<31, 68, (outs), (ins u5imm:$to, g8rc:$rA, g8rc:$rB),
def TD : XForm_1<31, 68, (outs), (ins u5imm:$to, g8rc:$rA, g8rc:$rB, variable_ops),
"td $to, $rA, $rB", IIC_IntTrapD, []>;
def POPCNTB : XForm_11<31, 122, (outs gprc:$rA), (ins gprc:$rS),
@ -5085,8 +5085,6 @@ def : Pat<(int_ppc_stwcx ForceXForm:$dst, gprc:$A),
(STWCX gprc:$A, ForceXForm:$dst)>;
def : Pat<(int_ppc_stbcx ForceXForm:$dst, gprc:$A),
(STBCX gprc:$A, ForceXForm:$dst)>;
def : Pat<(int_ppc_trap gprc:$A),
(TWI 24, $A, 0)>;
def : Pat<(int_ppc_fcfid f64:$A),
(XSCVSXDDP $A)>;

View File

@ -0,0 +1,46 @@
; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 < %s | FileCheck %s
; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-linux-gnu \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr7 < %s | FileCheck %s
; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 < %s | FileCheck %s -check-prefix=AIX
; RUN: not --crash llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 --filetype=obj -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=OBJ
; OBJ: LLVM ERROR: emitXCOFFExceptDirective not yet supported for integrated assembler path.
!1 = !{!"ppc-trap-reason", !"1", !"2"}
declare void @llvm.ppc.trapd(i64 %a)
declare void @llvm.ppc.tdw(i64 %a, i64 %b, i32 immarg)
define dso_local void @test__trapd_annotation(i64 %a) {
; CHECK-LABEL: test__trapd_annotation:
; CHECK: # %bb.0:
; CHECK-NEXT: tdi 24, r3, 0
; CHECK-NEXT: blr
;
; AIX-LABEL: test__trapd_annotation:
; AIX: # %bb.0:
; AIX-NEXT: L..tmp0:
; AIX-NEXT: .except .test__trapd_annotation, 1, 2
; AIX-NEXT: tdi 24, r3, 0
; AIX-NEXT: blr
call void @llvm.ppc.trapd(i64 %a), !annotation !1
ret void
}
define dso_local void @test__tdw_annotation(i64 %a) {
; CHECK-LABEL: test__tdw_annotation:
; CHECK: # %bb.0:
; CHECK-NEXT: tdi 0, r3, 4
; CHECK-NEXT: blr
;
; AIX-LABEL: test__tdw_annotation:
; AIX: # %bb.0:
; AIX-NEXT: L..tmp1:
; AIX-NEXT: .except .test__tdw_annotation, 1, 2
; AIX-NEXT: tdi 0, r3, 4
; AIX-NEXT: blr
call void @llvm.ppc.tdw(i64 4, i64 %a, i32 0), !annotation !1
ret void
}

View File

@ -0,0 +1,49 @@
; RUN: llc -verify-machineinstrs -mtriple=powerpc64le-unknown-linux-gnu \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 < %s | FileCheck %s
; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-linux-gnu \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr7 < %s | FileCheck %s
; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-linux-gnu \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr7 < %s | FileCheck %s
; RUN: llc -verify-machineinstrs -mtriple=powerpc-unknown-aix \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 < %s | FileCheck %s -check-prefix=AIX
; RUN: llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 < %s | FileCheck %s -check-prefix=AIX
; RUN: not --crash llc -verify-machineinstrs -mtriple=powerpc64-unknown-aix \
; RUN: --ppc-asm-full-reg-names -mcpu=pwr8 --filetype=obj -o /dev/null %s 2>&1 | FileCheck %s -check-prefix=OBJ
; OBJ: LLVM ERROR: emitXCOFFExceptDirective not yet supported for integrated assembler path.
!1 = !{!"ppc-trap-reason", !"1", !"2"}
declare void @llvm.ppc.trap(i32 %a)
declare void @llvm.ppc.tw(i32 %a, i32 %b, i32 immarg)
define dso_local void @test__trap_annotation(i32 %a) {
; CHECK-LABEL: test__trap_annotation:
; CHECK: # %bb.0:
; CHECK-NEXT: twi 24, r3, 0
; CHECK-NEXT: blr
;
; AIX-LABEL: test__trap_annotation:
; AIX: # %bb.0:
; AIX-NEXT: L..tmp0:
; AIX-NEXT: .except .test__trap_annotation, 1, 2
; AIX-NEXT: twi 24, r3, 0
; AIX-NEXT: blr
call void @llvm.ppc.trap(i32 %a), !annotation !1
ret void
}
define dso_local void @test__tw_annotation(i32 %a) {
; CHECK-LABEL: test__tw_annotation:
; CHECK: # %bb.0:
; CHECK-NEXT: twi 0, r3, 4
; CHECK-NEXT: blr
;
; AIX-LABEL: test__tw_annotation:
; AIX: # %bb.0:
; AIX-NEXT: L..tmp1:
; AIX-NEXT: .except .test__tw_annotation, 1, 2
; AIX-NEXT: twi 0, r3, 4
; AIX-NEXT: blr
call void @llvm.ppc.tw(i32 4, i32 %a, i32 0), !annotation !1
ret void
}