diff --git a/llvm/include/llvm/IR/FixedMetadataKinds.def b/llvm/include/llvm/IR/FixedMetadataKinds.def
--- a/llvm/include/llvm/IR/FixedMetadataKinds.def
+++ b/llvm/include/llvm/IR/FixedMetadataKinds.def
@@ -42,3 +42,4 @@
 LLVM_FIXED_MD_KIND(MD_vcall_visibility, "vcall_visibility", 28)
 LLVM_FIXED_MD_KIND(MD_noundef, "noundef", 29)
 LLVM_FIXED_MD_KIND(MD_annotation, "annotation", 30)
+LLVM_FIXED_MD_KIND(MD_implementedby, "implementedby", 31)
\ No newline at end of file
diff --git a/llvm/lib/Transforms/IPO/GlobalOpt.cpp b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
--- a/llvm/lib/Transforms/IPO/GlobalOpt.cpp
+++ b/llvm/lib/Transforms/IPO/GlobalOpt.cpp
@@ -45,6 +45,7 @@
 #include "llvm/IR/Instruction.h"
 #include "llvm/IR/Instructions.h"
 #include "llvm/IR/IntrinsicInst.h"
+#include "llvm/IR/Metadata.h"
 #include "llvm/IR/Module.h"
 #include "llvm/IR/Operator.h"
 #include "llvm/IR/Type.h"
@@ -2395,17 +2396,33 @@
 
   bool Changed = false;
 
+  SmallPtrSet<Function *, 4> ImplementedByUses;
   std::vector<Function *> AllCallsCold;
   for (Module::iterator FI = M.begin(), E = M.end(); FI != E;) {
     Function *F = &*FI++;
     if (hasOnlyColdCalls(*F, GetBFI))
       AllCallsCold.push_back(F);
+
+    auto MD = F->getMetadata(LLVMContext::MD_implementedby);
+    if (MD) {
+      ImplementedByUses.insert(F);
+      auto Impl =
+          cast<ValueAsMetadata>(cast<MDTuple>(MD)->getOperand(0))->getValue();
+      if (auto F2 = dyn_cast<Function>(Impl))
+        ImplementedByUses.insert(F2);
+    }
   }
 
   // Optimize functions.
   for (Module::iterator FI = M.begin(), E = M.end(); FI != E; ) {
     Function *F = &*FI++;
 
+    // Don't perform global opt on functions used within implemented by
+    // we neither want theses functions deleted, nor with a different
+    // calling convention.
+    if (ImplementedByUses.count(F))
+      continue;
+
     // Don't perform global opt pass on naked functions; we don't want fast
     // calling conventions for naked functions.
     if (F->hasFnAttribute(Attribute::Naked))
diff --git a/llvm/test/Transforms/GlobalOpt/implemented_by.ll b/llvm/test/Transforms/GlobalOpt/implemented_by.ll
new file mode 100644
--- /dev/null
+++ b/llvm/test/Transforms/GlobalOpt/implemented_by.ll
@@ -0,0 +1,26 @@
+; RUN: opt < %s -globalopt -S | FileCheck %s
+
+declare !implementedby !0 double @llvm.sin.f64(double)
+; CHECK: declare !implementedby [[sinimpl:.+]] double @llvm.sin.f64
+declare double @sin(double)
+
+define internal double @mycos(double %x) {
+  ret double %x
+}
+; Calling convention is not changed
+; CHECK: define internal double @mycos(double %x)
+
+declare !implementedby !1 double @llvm.cos.f64(double)
+; CHECK: declare !implementedby [[cosimpl:.+]] double @llvm.cos.f64
+
+define double @cos(double %x) !implementedby !1 {
+  ret double %x
+}
+; CHECK: define double @cos{{.+}} !implementedby [[cosimpl]] {
+
+!0 = !{double(double)* @sin}
+!1 = !{double(double)* @mycos}
+
+; CHECK: [[sinimpl]] = !{double (double)* @sin}
+; CHECK: [[cosimpl]] = !{double (double)* @mycos}
+