diff --git a/llvm/include/llvm/CodeGen/TargetInstrInfo.h b/llvm/include/llvm/CodeGen/TargetInstrInfo.h --- a/llvm/include/llvm/CodeGen/TargetInstrInfo.h +++ b/llvm/include/llvm/CodeGen/TargetInstrInfo.h @@ -1927,9 +1927,7 @@ /// Optional target hook that returns true if \p MBB is safe to outline from, /// and returns any target-specific information in \p Flags. virtual bool isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, - unsigned &Flags) const { - return true; - } + unsigned &Flags) const; /// Insert a custom frame for outlined functions. virtual void buildOutlinedFrame(MachineBasicBlock &MBB, MachineFunction &MF, diff --git a/llvm/lib/CodeGen/TargetInstrInfo.cpp b/llvm/lib/CodeGen/TargetInstrInfo.cpp --- a/llvm/lib/CodeGen/TargetInstrInfo.cpp +++ b/llvm/lib/CodeGen/TargetInstrInfo.cpp @@ -1418,3 +1418,16 @@ })) F.addFnAttr(Attribute::NoUnwind); } + +bool TargetInstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, + unsigned &Flags) const { + // Some instrumentations create special TargetOpcode at the start which + // expands to special code sequences which must be present. + auto First = MBB.getFirstNonDebugInstr(); + if (First != MBB.end() && + (First->getOpcode() == TargetOpcode::FENTRY_CALL || + First->getOpcode() == TargetOpcode::PATCHABLE_FUNCTION_ENTER)) + return false; + + return true; +} diff --git a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp --- a/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp +++ b/llvm/lib/Target/AArch64/AArch64InstrInfo.cpp @@ -7055,6 +7055,8 @@ bool AArch64InstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, unsigned &Flags) const { + if (!TargetInstrInfo::isMBBSafeToOutlineFrom(MBB, Flags)) + return false; // Check if LR is available through all of the MBB. If it's not, then set // a flag. assert(MBB.getParent()->getRegInfo().tracksLiveness() && diff --git a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp --- a/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp +++ b/llvm/lib/Target/RISCV/RISCVInstrInfo.cpp @@ -1254,7 +1254,7 @@ bool RISCVInstrInfo::isMBBSafeToOutlineFrom(MachineBasicBlock &MBB, unsigned &Flags) const { // More accurate safety checking is done in getOutliningCandidateInfo. - return true; + return TargetInstrInfo::isMBBSafeToOutlineFrom(MBB, Flags); } // Enum values indicating how an outlined call should be constructed. diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-patchable.ll b/llvm/test/CodeGen/AArch64/machine-outliner-patchable.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/AArch64/machine-outliner-patchable.ll @@ -0,0 +1,114 @@ +; RUN: llc < %s -verify-machineinstrs -enable-machine-outliner | FileCheck %s + +target triple = "aarch64-unknown-linux-gnu" + +declare void @foo(i32, i32, i32, i32) minsize + +;; TargetOpcode::FENTRY_CALL at the start of the function expands to a __fentry__ +;; call which must be present. Don't outline it. +define void @fentry0(i1 %a) nounwind "fentry-call"="true" { +; CHECK-LABEL: fentry0: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: # FEntry call +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +define void @fentry1(i1 %a) nounwind "fentry-call"="true" { +; CHECK-LABEL: fentry1: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: # FEntry call +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +;; TargetOpcode::PATCHABLE_FUNCTION_ENTER at the start of the function expands to +;; NOPs which must be present. Don't outline them. +define void @patchable0(i1 %a) nounwind "patchable-function-entry"="2" { +; CHECK-LABEL: patchable0: +; CHECK-NEXT: .Lfunc_begin0: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +define void @patchable1(i1 %a) nounwind "patchable-function-entry"="2" { +; CHECK-LABEL: patchable1: +; CHECK-NEXT: .Lfunc_begin1: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +;; Similar to "patchable-function-entry". +define void @xray0(i1 %a) nounwind "function-instrument"="xray-always" { +; CHECK-LABEL: xray0: +; CHECK-NEXT: .Lfunc_begin2: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: .Lxray_sled_0: +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +define void @xray1(i1 %a) nounwind "function-instrument"="xray-always" { +; CHECK-LABEL: xray1: +; CHECK-NEXT: .Lfunc_begin3: +; CHECK-NEXT: // %bb.0: +; CHECK-NEXT: .p2align 2 +; CHECK-NEXT: .Lxray_sled_2: +; CHECK: // %bb.1: +; CHECK-NEXT: bl OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} diff --git a/llvm/test/CodeGen/RISCV/machine-outliner-patchable.ll b/llvm/test/CodeGen/RISCV/machine-outliner-patchable.ll new file mode 100644 --- /dev/null +++ b/llvm/test/CodeGen/RISCV/machine-outliner-patchable.ll @@ -0,0 +1,77 @@ +; RUN: llc < %s -verify-machineinstrs -enable-machine-outliner | FileCheck %s + +target triple = "riscv64-unknown-linux-gnu" + +declare void @foo(i32, i32, i32, i32) minsize + +;; TargetOpcode::FENTRY_CALL at the start of the function expands to a __fentry__ +;; call which must be present. Don't outline it. +define void @fentry0(i1 %a) nounwind "fentry-call"="true" { +; CHECK-LABEL: fentry0: +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: # FEntry call +; CHECK: # %bb.1: +; CHECK-NEXT: call t0, OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +define void @fentry1(i1 %a) nounwind "fentry-call"="true" { +; CHECK-LABEL: fentry1: +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: # FEntry call +; CHECK: # %bb.1: +; CHECK-NEXT: call t0, OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +;; TargetOpcode::PATCHABLE_FUNCTION_ENTER at the start of the function expands to +;; NOPs which must be present. Don't outline them. +define void @patchable0(i1 %a) nounwind "patchable-function-entry"="2" { +; CHECK-LABEL: patchable0: +; CHECK-NEXT: .Lfunc_begin0: +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK: # %bb.1: +; CHECK-NEXT: call t0, OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +} + +define void @patchable1(i1 %a) nounwind "patchable-function-entry"="2" { +; CHECK-LABEL: patchable1: +; CHECK-NEXT: .Lfunc_begin1: +; CHECK-NEXT: # %bb.0: +; CHECK-NEXT: nop +; CHECK-NEXT: nop +; CHECK: # %bb.1: +; CHECK-NEXT: call t0, OUTLINED_FUNCTION_1 +entry: + br i1 %a, label %if.then, label %if.end +if.then: + call void @foo(i32 1, i32 2, i32 3, i32 4) + br label %if.end +if.end: + call void @foo(i32 5, i32 6, i32 7, i32 8) + ret void +}