diff --git a/docs/LangRef.rst b/docs/LangRef.rst index 03a8387d9d1..6724a401903 100644 --- a/docs/LangRef.rst +++ b/docs/LangRef.rst @@ -19605,6 +19605,35 @@ This intrinsic is lowered to code which is intended to cause an execution trap with the intention of requesting the attention of a debugger. +'``llvm.ubsantrap``' Intrinsic +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Syntax: +""""""" + +:: + + declare void @llvm.ubsantrap(i8 immarg) cold noreturn nounwind + +Overview: +""""""""" + +The '``llvm.ubsantrap``' intrinsic. + +Arguments: +"""""""""" + +An integer describing the kind of failure detected. + +Semantics: +"""""""""" + +This intrinsic is lowered to code which is intended to cause an execution trap, +embedding the argument into encoding of that trap somehow to discriminate +crashes if possible. + +Equivalent to ``@llvm.trap`` for targets that do not support this behaviour. + '``llvm.stackprotector``' Intrinsic ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/include/llvm/CodeGen/ISDOpcodes.h b/include/llvm/CodeGen/ISDOpcodes.h index 2cacb572a0e..adbcb7cc75b 100644 --- a/include/llvm/CodeGen/ISDOpcodes.h +++ b/include/llvm/CodeGen/ISDOpcodes.h @@ -1012,6 +1012,9 @@ enum NodeType { /// DEBUGTRAP - Trap intended to get the attention of a debugger. DEBUGTRAP, + /// UBSANTRAP - Trap with an immediate describing the kind of sanitizer failure. + UBSANTRAP, + /// PREFETCH - This corresponds to a prefetch intrinsic. The first operand /// is the chain. The other operands are the address to prefetch, /// read / write specifier, locality specifier and instruction / data cache diff --git a/include/llvm/IR/Intrinsics.td b/include/llvm/IR/Intrinsics.td index 9e64a61cf48..71047910345 100644 --- a/include/llvm/IR/Intrinsics.td +++ b/include/llvm/IR/Intrinsics.td @@ -1255,6 +1255,8 @@ def int_trap : Intrinsic<[], [], [IntrNoReturn, IntrCold]>, GCCBuiltin<"__builtin_trap">; def int_debugtrap : Intrinsic<[]>, GCCBuiltin<"__builtin_debugtrap">; +def int_ubsantrap : Intrinsic<[], [llvm_i8_ty], + [IntrNoReturn, IntrCold, ImmArg>]>; // Support for dynamic deoptimization (or de-specialization) def int_experimental_deoptimize : Intrinsic<[llvm_any_ty], [llvm_vararg_ty], diff --git a/include/llvm/Target/TargetSelectionDAG.td b/include/llvm/Target/TargetSelectionDAG.td index e37cdc8d301..7ba9f11962c 100644 --- a/include/llvm/Target/TargetSelectionDAG.td +++ b/include/llvm/Target/TargetSelectionDAG.td @@ -212,6 +212,8 @@ def SDTCatchret : SDTypeProfile<0, 2, [ // catchret def SDTNone : SDTypeProfile<0, 0, []>; // ret, trap +def SDTUBSANTrap : SDTypeProfile<0, 1, []>; // ubsantrap + def SDTLoad : SDTypeProfile<1, 1, [ // load SDTCisPtrTy<1> ]>; @@ -575,6 +577,8 @@ def trap : SDNode<"ISD::TRAP" , SDTNone, [SDNPHasChain, SDNPSideEffect]>; def debugtrap : SDNode<"ISD::DEBUGTRAP" , SDTNone, [SDNPHasChain, SDNPSideEffect]>; +def ubsantrap : SDNode<"ISD::UBSANTRAP" , SDTUBSANTrap, + [SDNPHasChain, SDNPSideEffect]>; def prefetch : SDNode<"ISD::PREFETCH" , SDTPrefetch, [SDNPHasChain, SDNPMayLoad, SDNPMayStore, diff --git a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp index 5386754c317..361ab78d845 100644 --- a/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp +++ b/lib/CodeGen/SelectionDAG/LegalizeDAG.cpp @@ -1107,6 +1107,18 @@ void SelectionDAGLegalize::LegalizeOp(SDNode *Node) { // They'll be converted to Copy(To/From)Reg. Action = TargetLowering::Legal; break; + case ISD::UBSANTRAP: + Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0)); + if (Action == TargetLowering::Expand) { + // replace ISD::UBSANTRAP with ISD::TRAP + SDValue NewVal; + NewVal = DAG.getNode(ISD::TRAP, SDLoc(Node), Node->getVTList(), + Node->getOperand(0)); + ReplaceNode(Node, NewVal.getNode()); + LegalizeOp(NewVal.getNode()); + return; + } + break; case ISD::DEBUGTRAP: Action = TLI.getOperationAction(Node->getOpcode(), Node->getValueType(0)); if (Action == TargetLowering::Expand) { diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp index f3bce354624..e95464c460d 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGBuilder.cpp @@ -6481,6 +6481,7 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, setValue(&I, getValue(I.getArgOperand(0))); return; + case Intrinsic::ubsantrap: case Intrinsic::debugtrap: case Intrinsic::trap: { StringRef TrapFuncName = @@ -6488,12 +6489,31 @@ void SelectionDAGBuilder::visitIntrinsicCall(const CallInst &I, .getAttribute(AttributeList::FunctionIndex, "trap-func-name") .getValueAsString(); if (TrapFuncName.empty()) { - ISD::NodeType Op = (Intrinsic == Intrinsic::trap) ? - ISD::TRAP : ISD::DEBUGTRAP; - DAG.setRoot(DAG.getNode(Op, sdl,MVT::Other, getRoot())); + switch (Intrinsic) { + case Intrinsic::trap: + DAG.setRoot(DAG.getNode(ISD::TRAP, sdl, MVT::Other, getRoot())); + break; + case Intrinsic::debugtrap: + DAG.setRoot(DAG.getNode(ISD::DEBUGTRAP, sdl, MVT::Other, getRoot())); + break; + case Intrinsic::ubsantrap: + DAG.setRoot(DAG.getNode( + ISD::UBSANTRAP, sdl, MVT::Other, getRoot(), + DAG.getTargetConstant( + cast(I.getArgOperand(0))->getZExtValue(), sdl, + MVT::i32))); + break; + default: llvm_unreachable("unknown trap intrinsic"); + } return; } TargetLowering::ArgListTy Args; + if (Intrinsic == Intrinsic::ubsantrap) { + Args.push_back(TargetLoweringBase::ArgListEntry()); + Args[0].Val = I.getArgOperand(0); + Args[0].Node = getValue(Args[0].Val); + Args[0].Ty = Args[0].Val->getType(); + } TargetLowering::CallLoweringInfo CLI(DAG); CLI.setDebugLoc(sdl).setChain(getRoot()).setLibCallee( diff --git a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp index 17a4a5cbf02..34c47e2408f 100644 --- a/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp +++ b/lib/CodeGen/SelectionDAG/SelectionDAGDumper.cpp @@ -394,6 +394,7 @@ std::string SDNode::getOperationName(const SelectionDAG *G) const { case ISD::STACKRESTORE: return "stackrestore"; case ISD::TRAP: return "trap"; case ISD::DEBUGTRAP: return "debugtrap"; + case ISD::UBSANTRAP: return "ubsantrap"; case ISD::LIFETIME_START: return "lifetime.start"; case ISD::LIFETIME_END: return "lifetime.end"; case ISD::PSEUDO_PROBE: diff --git a/lib/CodeGen/TargetLoweringBase.cpp b/lib/CodeGen/TargetLoweringBase.cpp index 7dfd76ce5de..58543b48a99 100644 --- a/lib/CodeGen/TargetLoweringBase.cpp +++ b/lib/CodeGen/TargetLoweringBase.cpp @@ -886,6 +886,8 @@ void TargetLoweringBase::initActions() { // On most systems, DEBUGTRAP and TRAP have no difference. The "Expand" // here is to inform DAG Legalizer to replace DEBUGTRAP with TRAP. setOperationAction(ISD::DEBUGTRAP, MVT::Other, Expand); + + setOperationAction(ISD::UBSANTRAP, MVT::Other, Expand); } MVT TargetLoweringBase::getScalarShiftAmountTy(const DataLayout &DL, diff --git a/lib/Target/AArch64/AArch64ISelLowering.cpp b/lib/Target/AArch64/AArch64ISelLowering.cpp index 517f5e96515..cca31a701d5 100644 --- a/lib/Target/AArch64/AArch64ISelLowering.cpp +++ b/lib/Target/AArch64/AArch64ISelLowering.cpp @@ -816,6 +816,7 @@ AArch64TargetLowering::AArch64TargetLowering(const TargetMachine &TM, // Trap. setOperationAction(ISD::TRAP, MVT::Other, Legal); setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); + setOperationAction(ISD::UBSANTRAP, MVT::Other, Legal); // We combine OR nodes for bitfield operations. setTargetDAGCombine(ISD::OR); diff --git a/lib/Target/AArch64/AArch64InstrInfo.td b/lib/Target/AArch64/AArch64InstrInfo.td index 88392b258d9..ca13729a54e 100644 --- a/lib/Target/AArch64/AArch64InstrInfo.td +++ b/lib/Target/AArch64/AArch64InstrInfo.td @@ -6685,6 +6685,16 @@ def : Pat<(i32 (trunc GPR64sp:$src)), def : Pat<(trap), (BRK 1)>; def : Pat<(debugtrap), (BRK 0xF000)>; +def ubsan_trap_xform : SDNodeXFormgetTargetConstant(N->getZExtValue() | ('U' << 8), SDLoc(N), MVT::i32); +}]>; + +def ubsan_trap_imm : TImmLeaf(Imm); +}], ubsan_trap_xform>; + +def : Pat<(ubsantrap ubsan_trap_imm:$kind), (BRK ubsan_trap_imm:$kind)>; + // Multiply high patterns which multiply the lower subvector using smull/umull // and the upper subvector with smull2/umull2. Then shuffle the high the high // part of both results together. diff --git a/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp b/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp index abb0cb7b429..8d75acd9f18 100644 --- a/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp +++ b/lib/Target/AArch64/GISel/AArch64InstructionSelector.cpp @@ -4832,6 +4832,10 @@ bool AArch64InstructionSelector::selectIntrinsicWithSideEffects( case Intrinsic::debugtrap: MIRBuilder.buildInstr(AArch64::BRK, {}, {}).addImm(0xF000); break; + case Intrinsic::ubsantrap: + MIRBuilder.buildInstr(AArch64::BRK, {}, {}) + .addImm(I.getOperand(0).getImm() | ('U' << 8)); + break; } I.eraseFromParent(); diff --git a/lib/Target/X86/X86ISelLowering.cpp b/lib/Target/X86/X86ISelLowering.cpp index cb23f270c15..e97f4b12323 100644 --- a/lib/Target/X86/X86ISelLowering.cpp +++ b/lib/Target/X86/X86ISelLowering.cpp @@ -494,6 +494,7 @@ X86TargetLowering::X86TargetLowering(const X86TargetMachine &TM, setOperationAction(ISD::TRAP, MVT::Other, Legal); setOperationAction(ISD::DEBUGTRAP, MVT::Other, Legal); + setOperationAction(ISD::UBSANTRAP, MVT::Other, Legal); // VASTART needs to be custom lowered to use the VarArgsFrameIndex setOperationAction(ISD::VASTART , MVT::Other, Custom); diff --git a/lib/Target/X86/X86InstrSystem.td b/lib/Target/X86/X86InstrSystem.td index 2f48a43b573..f57ca7ffec7 100644 --- a/lib/Target/X86/X86InstrSystem.td +++ b/lib/Target/X86/X86InstrSystem.td @@ -49,6 +49,7 @@ let Uses = [EFLAGS] in def INT3 : I<0xcc, RawFrm, (outs), (ins), "int3", [(int_x86_int (i8 3))]>; } // SchedRW +def UBSAN_UD1 : PseudoI<(outs), (ins i32imm:$kind), [(ubsantrap (i32 timm:$kind))]>; // The long form of "int $3" turns into int3 as a size optimization. // FIXME: This doesn't work because InstAlias can't match immediate constants. //def : InstAlias<"int\t$3", (INT3)>; diff --git a/lib/Target/X86/X86MCInstLower.cpp b/lib/Target/X86/X86MCInstLower.cpp index db015b1d903..6602d819929 100644 --- a/lib/Target/X86/X86MCInstLower.cpp +++ b/lib/Target/X86/X86MCInstLower.cpp @@ -2599,6 +2599,15 @@ void X86AsmPrinter::emitInstruction(const MachineInstr *MI) { } return; } + case X86::UBSAN_UD1: + EmitAndCountInstruction(MCInstBuilder(X86::UD1Lm) + .addReg(X86::EAX) + .addReg(X86::EAX) + .addImm(1) + .addReg(X86::NoRegister) + .addImm(MI->getOperand(0).getImm()) + .addReg(X86::NoRegister)); + return; } MCInst TmpInst; diff --git a/test/CodeGen/AArch64/ubsantrap.ll b/test/CodeGen/AArch64/ubsantrap.ll new file mode 100644 index 00000000000..a51be2e5e1b --- /dev/null +++ b/test/CodeGen/AArch64/ubsantrap.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=arm64-apple-ios %s -o - | FileCheck %s + +define void @test_ubsantrap() { +; CHECK-LABEL: test_ubsantrap +; CHECK: brk #0x550c + call void @llvm.ubsantrap(i8 12) + ret void +} + +define void @test_ubsantrap_function() { +; CHECK-LABEL: test_ubsantrap_function: +; CHECK: mov w0, #12 +; CHECK: bl _wibble + call void @llvm.ubsantrap(i8 12) "trap-func-name"="wibble" + ret void +} + +declare void @llvm.ubsantrap(i8) diff --git a/test/CodeGen/X86/ubsantrap.ll b/test/CodeGen/X86/ubsantrap.ll new file mode 100644 index 00000000000..ed9e50bc7ca --- /dev/null +++ b/test/CodeGen/X86/ubsantrap.ll @@ -0,0 +1,18 @@ +; RUN: llc -mtriple=x86_64-linux-gnu %s -o - | FileCheck %s + +define void @test_ubsantrap() { +; CHECK-LABEL: test_ubsantrap +; CHECK: ud1l 12(%eax), %eax + call void @llvm.ubsantrap(i8 12) + ret void +} + +define void @test_ubsantrap_function() { +; CHECK-LABEL: test_ubsantrap_function: +; CHECK: movl $12, %edi +; CHECK: callq wibble + call void @llvm.ubsantrap(i8 12) "trap-func-name"="wibble" + ret void +} + +declare void @llvm.ubsantrap(i8)