diff --git a/llvm/lib/CodeGen/MachineOutliner.cpp b/llvm/lib/CodeGen/MachineOutliner.cpp
--- a/llvm/lib/CodeGen/MachineOutliner.cpp
+++ b/llvm/lib/CodeGen/MachineOutliner.cpp
@@ -59,6 +59,9 @@
 #include "llvm/ADT/SmallSet.h"
 #include "llvm/ADT/Statistic.h"
 #include "llvm/ADT/Twine.h"
+#include "llvm/Analysis/BlockFrequencyInfo.h"
+#include "llvm/Analysis/BranchProbabilityInfo.h"
+#include "llvm/Analysis/LoopInfo.h"
 #include "llvm/Analysis/OptimizationRemarkEmitter.h"
 #include "llvm/CodeGen/LivePhysRegs.h"
 #include "llvm/CodeGen/MachineModuleInfo.h"
@@ -67,6 +70,7 @@
 #include "llvm/CodeGen/TargetInstrInfo.h"
 #include "llvm/CodeGen/TargetSubtargetInfo.h"
 #include "llvm/IR/DIBuilder.h"
+#include "llvm/IR/Dominators.h"
 #include "llvm/IR/IRBuilder.h"
 #include "llvm/IR/Mangler.h"
 #include "llvm/InitializePasses.h"
@@ -113,6 +117,10 @@
     cl::desc(
         "Number of times to rerun the outliner after the initial outline"));
 
+static cl::opt<bool> UseProfileData(
+    "machine-outliner-use-profile-data", cl::init(false), cl::Hidden,
+    cl::desc("Use profile data to avoid outlining hot blocks."));
+
 namespace {
 
 /// Maps \p MachineInstrs to unsigned integers and stores the mappings.
@@ -891,7 +899,16 @@
 
     const TargetInstrInfo *TII = MF->getSubtarget().getInstrInfo();
 
-    if (!RunOnAllFunctions && !TII->shouldOutlineFromFunctionByDefault(*MF))
+    Optional<BlockFrequencyInfo> BFI;
+    if (UseProfileData) {
+      DominatorTree DT(F);
+      LoopInfo LI(DT);
+      BranchProbabilityInfo BPI(F, LI);
+      BFI.emplace(F, BPI, LI);
+    }
+
+    if (!RunOnAllFunctions && !TII->shouldOutlineFromFunctionByDefault(*MF) &&
+        !UseProfileData)
       continue;
 
     // We have a MachineFunction. Ask the target if it's suitable for outlining.
@@ -917,6 +934,11 @@
       if (MBB.hasAddressTaken())
         continue;
 
+      // If we have profile data and want to use it, avoid outlining hot blocks.
+      if (UseProfileData &&
+          !BFI->isBlockRarelyExecuted(MBB.getBasicBlock()).getValueOr(false))
+        continue;
+
       // MBB is suitable for outlining. Map it to a list of unsigneds.
       Mapper.convertToUnsignedVec(MBB, *TII);
     }
diff --git a/llvm/test/CodeGen/AArch64/machine-outliner-profile.ll b/llvm/test/CodeGen/AArch64/machine-outliner-profile.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/CodeGen/AArch64/machine-outliner-profile.ll
@@ -0,0 +1,68 @@
+; RUN: llc -verify-machineinstrs -enable-machine-outliner -machine-outliner-use-profile-data -mtriple=aarch64-linux-gnu < %s | FileCheck %s
+
+declare void @z(i32, i32, i32, i32)
+
+; CHECK-LABEL: cold:
+define void @cold() !prof !0 {
+entry:
+; CHECK: [[OUTLINED:OUTLINED_FUNCTION_[0-9]+]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK-LABEL: hot:
+define void @hot() !prof !1 {
+entry:
+; CHECK-NOT: [[OUTLINED]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK-LABEL: small_cold:
+define void @small_cold() optsize minsize !prof !0 {
+entry:
+; CHECK: [[OUTLINED]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK-LABEL: small_hot:
+define void @small_hot() optsize minsize !prof !1 {
+entry:
+; CHECK-NOT: [[OUTLINED]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK-LABEL: no_profile:
+define void @no_profile() {
+entry:
+; CHECK-NOT: [[OUTLINED]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK-LABEL: cold2:
+define void @cold2() !prof !0 {
+entry:
+; CHECK: [[OUTLINED]]
+  tail call void @z(i32 1, i32 2, i32 3, i32 4)
+  ret void
+; CHECK: .cfi_endproc
+}
+
+; CHECK: [[OUTLINED]]:
+; CHECK-SAME: // @{{.*}} Tail Call
+; CHECK:      mov     w0, #1
+; CHECK-NEXT: mov     w1, #2
+; CHECK-NEXT: mov     w2, #3
+; CHECK-NEXT: mov     w3, #4
+; CHECK-NEXT: b       z
+
+!0 = !{!"function_entry_count", i64 0}
+!1 = !{!"function_entry_count", i64 1}