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 @@ -4561,6 +4561,11 @@ const AsmOperandInfo &OpInfo, SelectionDAG &DAG) const; + // Lower target intrinsic instruction. + virtual void LowerTargetIntrinsic(const CallInst &I, SDValue &Op, + SmallVector &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, + const unsigned Lang, + const unsigned Reason); + /// 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,11 @@ report_fatal_error("emitXCOFFRenameDirective is not implemented yet on " "object generation path"); } + void emitXCOFFExceptDirective(const MCSymbol *Symbol, const unsigned Lang, + const unsigned Reason) override { + report_fatal_error("emitXCOFFExceptDirective is not implemented yet on " + "object generation path"); + } }; } // 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 @@ -4835,6 +4835,7 @@ // Create the node. SDValue Result; + TLI.LowerTargetIntrinsic(I, Result, 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 @@ -5178,6 +5178,12 @@ return SDValue(); } +void TargetLowering::LowerTargetIntrinsic(const CallInst &I, SDValue &Op, + SmallVector &Ops, + SelectionDAG &DAG) const { + return; +} + /// Lower the specified operand into the Ops vector. /// If it is invalid, don't add anything to Ops. void TargetLowering::LowerAsmOperandForConstraint(SDValue Op, 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 @@ -195,6 +195,10 @@ void emitXCOFFRenameDirective(const MCSymbol *Name, StringRef Rename) override; + void emitXCOFFExceptDirective(const MCSymbol *Symbol, + const unsigned Lang, + const unsigned Reason) override; + void emitXCOFFRefDirective(StringRef Name) override; void emitELFSize(MCSymbol *Symbol, const MCExpr *Value) override; @@ -934,6 +938,15 @@ EmitEOL(); } +void MCAsmStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol, + const unsigned Lang, + const unsigned Reason) { + OS << "\t.except\t"; + Symbol->print(OS, MAI); + OS << ", " << Lang << ", " << Reason; + EmitEOL(); +} + void MCAsmStreamer::emitXCOFFRefDirective(StringRef Name) { OS << "\t.ref " << Name; EmitEOL(); 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 @@ -1186,6 +1186,13 @@ "XCOFF targets"); } +void MCStreamer::emitXCOFFExceptDirective(const MCSymbol *Symbol, + const unsigned Lang, + const unsigned Reason) { + llvm_unreachable("emitXCOFFExceptDirective is only supported on " + "XCOFF targets"); +} + void MCStreamer::emitXCOFFRefDirective(StringRef Name) { llvm_unreachable("emitXCOFFRefDirective is only supported on XCOFF targets"); } 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 @@ -2653,6 +2653,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); + // Ops 3 and 4 may be implicit registers in some scenarios where + // Lang and Reason aren't lowered. In this case we want to break. + if ((LangMO.isReg() && LangMO.isImplicit()) || + (ReasonMO.isReg() && ReasonMO.isImplicit())) + break; + assert ((LangMO.isImm() && ReasonMO.isImm()) && + "Language and reason type for trap instruction should be integer."); + OutStreamer->emitXCOFFExceptDirective(CurrentFnSym, LangMO.getImm(), + ReasonMO.getImm()); + 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 LowerTargetIntrinsic(const CallInst &I, SDValue &Op, + SmallVector &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 @@ -16230,6 +16230,25 @@ TargetLowering::LowerAsmOperandForConstraint(Op, Constraint, Ops, DAG); } +void PPCTargetLowering::LowerTargetIntrinsic(const CallInst &I, SDValue &Op, + SmallVector &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)); + } + return; +} + // 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 @@ -1957,9 +1957,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), @@ -5077,8 +5077,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-64bit.ll b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-64bit.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations-64bit.ll @@ -0,0 +1,44 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; 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 is not implemented yet on object generation 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: .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: .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.ll b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/PowerPC/builtins-ppc-xlcompat-trap-annotations.ll @@ -0,0 +1,50 @@ +; NOTE: Assertions have been autogenerated by utils/update_llc_test_checks.py +; 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-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: 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-ibm-aix-xcoff \ +; RUN: --ppc-asm-full-reg-names -mcpu=pwr7 < %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 is not implemented yet on object generation 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: .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: .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 +}