diff --git a/llvm/include/llvm/CodeGen/TargetLowering.h b/llvm/include/llvm/CodeGen/TargetLowering.h --- a/llvm/include/llvm/CodeGen/TargetLowering.h +++ b/llvm/include/llvm/CodeGen/TargetLowering.h @@ -4623,6 +4623,12 @@ 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 &Ops, + SelectionDAG &DAG) const; + //===--------------------------------------------------------------------===// // Div utility functions // diff --git a/llvm/include/llvm/MC/MCStreamer.h b/llvm/include/llvm/MC/MCStreamer.h --- a/llvm/include/llvm/MC/MCStreamer.h +++ b/llvm/include/llvm/MC/MCStreamer.h @@ -628,6 +628,16 @@ /// 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. /// diff --git a/llvm/include/llvm/MC/MCXCOFFStreamer.h b/llvm/include/llvm/MC/MCXCOFFStreamer.h --- a/llvm/include/llvm/MC/MCXCOFFStreamer.h +++ b/llvm/include/llvm/MC/MCXCOFFStreamer.h @@ -41,6 +41,9 @@ 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 diff --git a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp --- a/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -4865,6 +4865,8 @@ // 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 = diff --git a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp --- a/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp +++ b/llvm/lib/CodeGen/SelectionDAG/TargetLowering.cpp @@ -5256,6 +5256,12 @@ } } +void TargetLowering::CollectTargetIntrinsicOperands(const CallInst &I, + SmallVectorImpl &Ops, + SelectionDAG &DAG) const { + return; +} + std::pair TargetLowering::getRegForInlineAsmConstraint(const TargetRegisterInfo *RI, StringRef Constraint, diff --git a/llvm/lib/MC/MCAsmStreamer.cpp b/llvm/lib/MC/MCAsmStreamer.cpp --- a/llvm/lib/MC/MCAsmStreamer.cpp +++ b/llvm/lib/MC/MCAsmStreamer.cpp @@ -197,6 +197,10 @@ 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 @@ 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"; diff --git a/llvm/lib/MC/MCStreamer.cpp b/llvm/lib/MC/MCStreamer.cpp --- a/llvm/lib/MC/MCStreamer.cpp +++ b/llvm/lib/MC/MCStreamer.cpp @@ -1190,6 +1190,15 @@ 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) {} diff --git a/llvm/lib/MC/MCXCOFFStreamer.cpp b/llvm/lib/MC/MCXCOFFStreamer.cpp --- a/llvm/lib/MC/MCXCOFFStreamer.cpp +++ b/llvm/lib/MC/MCXCOFFStreamer.cpp @@ -81,6 +81,16 @@ 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); diff --git a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp --- a/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp +++ b/llvm/lib/Target/PowerPC/PPCAsmPrinter.cpp @@ -2646,6 +2646,25 @@ 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 diff --git a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp --- a/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelDAGToDAG.cpp @@ -5053,8 +5053,18 @@ 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 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 @@ 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; + } + + if (N->getNumOperands() > MDIndex) { + SDValue MDV = N->getOperand(MDIndex); + const MDNode *MD = cast(MDV)->getMD(); + assert(MD->getNumOperands() != 0 && "Empty MDNode in operands!"); + assert((isa(MD->getOperand(0)) && cast( + MD->getOperand(0))->getString().equals("ppc-trap-reason")) + && "Unsupported annotation data type!"); + for (unsigned i = 1; i < MD->getNumOperands(); i++) { + assert(isa(MD->getOperand(i)) && + "Invalid data type for annotation ppc-trap-reason!"); + OpsWithMD.push_back( + getI32Imm(std::stoi(cast( + MD->getOperand(i))->getString().str()), dl)); + } } - break; + OpsWithMD.push_back(N->getOperand(0)); // chain + CurDAG->SelectNodeTo(N, Opcode, MVT::Other, OpsWithMD); + return; } case ISD::INTRINSIC_WO_CHAIN: { diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.h b/llvm/lib/Target/PowerPC/PPCISelLowering.h --- a/llvm/lib/Target/PowerPC/PPCISelLowering.h +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.h @@ -995,6 +995,10 @@ return TargetLowering::getInlineAsmMemConstraint(ConstraintCode); } + void CollectTargetIntrinsicOperands(const CallInst &I, + SmallVectorImpl &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, diff --git a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp --- a/llvm/lib/Target/PowerPC/PPCISelLowering.cpp +++ b/llvm/lib/Target/PowerPC/PPCISelLowering.cpp @@ -16227,6 +16227,24 @@ TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); } +void PPCTargetLowering::CollectTargetIntrinsicOperands(const CallInst &I, + SmallVectorImpl &Ops, + SelectionDAG &DAG) const { + if (I.getNumOperands() <= 1) + return; + if (!isa(Ops[1].getNode())) + return; + auto IntrinsicID = cast(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, diff --git a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td --- a/llvm/lib/Target/PowerPC/PPCInstr64Bit.td +++ b/llvm/lib/Target/PowerPC/PPCInstr64Bit.td @@ -1973,9 +1973,6 @@ 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), diff --git a/llvm/lib/Target/PowerPC/PPCInstrInfo.td b/llvm/lib/Target/PowerPC/PPCInstrInfo.td --- a/llvm/lib/Target/PowerPC/PPCInstrInfo.td +++ b/llvm/lib/Target/PowerPC/PPCInstrInfo.td @@ -1770,13 +1770,13 @@ 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 @@ (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)>; diff --git a/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-td.ll b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-td.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-td.ll @@ -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 +} diff --git a/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-tw.ll b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-tw.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-tw.ll @@ -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 +}