diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs
index 88bc0e86ad8..c4f376027ca 100644
--- a/src/fsharp/Optimizer.fs
+++ b/src/fsharp/Optimizer.fs
@@ -1256,7 +1256,7 @@ let AbstractAndRemapModulInfo msg g m (repackage, hidden) info =
 let [<Literal>] suffixForVariablesThatMayNotBeEliminated = "$cont"
 
 /// Indicates a ValRef generated to facilitate tuple eliminations
-let [<Literal>] suffixForTupleElementAssignmentTarget = "tupleElem"
+let [<Literal>] suffixForTupleElementAssignmentTarget = "$tupleElem"
 
 /// Type applications of F# "type functions" may cause side effects, e.g. 
 /// let x<'a> = printfn "hello"; typeof<'a> 
@@ -1278,7 +1278,17 @@ let ValueOfExpr expr =
       ConstExprValue(0, expr)
     else UnknownValue
 
-let IsMutableStructuralBindingForTupleElement (vref: ValRef) = vref.IsCompilerGenerated && vref.DisplayName.EndsWith suffixForTupleElementAssignmentTarget
+let IsMutableStructuralBindingForTupleElement (vref: ValRef) =
+    vref.IsCompilerGenerated &&
+    vref.LogicalName.EndsWith suffixForTupleElementAssignmentTarget
+
+let IsMutableForOutArg (vref: ValRef) =
+    vref.IsCompilerGenerated &&
+    vref.LogicalName.StartsWith(PrettyNaming.outArgCompilerGeneratedName)
+
+let IsKnownOnlyMutableBeforeUse (vref: ValRef) =
+    IsMutableStructuralBindingForTupleElement vref || 
+    IsMutableForOutArg vref
 
 //-------------------------------------------------------------------------
 // Dead binding elimination 
@@ -1582,7 +1592,7 @@ let MakeStructuralBindingTemp (v: Val) i (arg: Expr) argTy =
     ve, mkCompGenBind v arg
 
 let MakeMutableStructuralBindingForTupleElement (v: Val) i (arg: Expr) argTy =
-    let name = sprintf "%s_%d_%s" v.LogicalName i suffixForTupleElementAssignmentTarget
+    let name = sprintf "%s_%d%s" v.LogicalName i suffixForTupleElementAssignmentTarget
     let v, ve = mkMutableCompGenLocal arg.Range name argTy
     ve, mkCompGenBind v arg
 
@@ -2594,7 +2604,7 @@ and AddValEqualityInfo g m (v: ValRef) info =
     // when their address is passed to the method call. Another exception are mutable variables
     // created for tuple elimination in branching tuple bindings because they are assigned to
     // exactly once.
-    if not v.IsMutable || IsMutableStructuralBindingForTupleElement v || (v.IsCompilerGenerated && v.DisplayName.StartsWith(PrettyNaming.outArgCompilerGeneratedName)) then 
+    if not v.IsMutable || IsKnownOnlyMutableBeforeUse v then 
         { info with Info = MakeValueInfoForValue g m v info.Info }
     else
         info 
diff --git a/src/fsharp/Optimizer.fsi b/src/fsharp/Optimizer.fsi
index 0cf44522669..85504d02628 100644
--- a/src/fsharp/Optimizer.fsi
+++ b/src/fsharp/Optimizer.fsi
@@ -80,4 +80,8 @@ val UnionOptimizationInfos: seq<ImplFileOptimizationInfo> -> CcuOptimizationInfo
 /// Check if an expression has an effect
 val ExprHasEffect: TcGlobals -> Expr -> bool
 
-val internal u_CcuOptimizationInfo: ReaderState -> CcuOptimizationInfo
\ No newline at end of file
+val internal u_CcuOptimizationInfo: ReaderState -> CcuOptimizationInfo
+
+/// Indicates the value is only mutable during its initialization and before any access or capture
+val IsKnownOnlyMutableBeforeUse: ValRef -> bool
+
diff --git a/src/fsharp/autobox.fs b/src/fsharp/autobox.fs
index 92ac4845c77..d5854daa0ac 100644
--- a/src/fsharp/autobox.fs
+++ b/src/fsharp/autobox.fs
@@ -23,12 +23,15 @@ type cenv =
 
 /// Find all the mutable locals that escape a method, function or lambda expression
 let DecideEscapes syntacticArgs body =
-    let cantBeFree v = 
+    let isMutableEscape v = 
         let passedIn = ListSet.contains valEq v syntacticArgs 
-        not passedIn && (v.IsMutable && v.ValReprInfo.IsNone) 
+        not passedIn &&
+        v.IsMutable &&
+        v.ValReprInfo.IsNone &&
+        not (Optimizer.IsKnownOnlyMutableBeforeUse (mkLocalValRef v))
 
     let frees = freeInExpr CollectLocals body
-    frees.FreeLocals |> Zset.filter cantBeFree 
+    frees.FreeLocals |> Zset.filter isMutableEscape 
 
 /// Find all the mutable locals that escape a lambda expression, ignoring the arguments to the lambda
 let DecideLambda exprF cenv topValInfo expr ety z   = 
diff --git a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs
index 5aaf72f4b69..aac6ae87332 100644
--- a/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs
+++ b/tests/FSharp.Compiler.ComponentTests/EmittedIL/TupleElimination.fs
@@ -706,4 +706,78 @@ public static int z()
   IL_0012:  ldloc.1
   IL_0013:  add
   IL_0014:  ret
-}""" ]
\ No newline at end of file
+}""" ]
+
+
+
+
+    [<Fact>]
+    let ``Branching let binding of tuple with capture doesn't promote``() =
+        FSharp """
+module TupleElimination
+open System.Runtime.CompilerServices
+
+let testFunction(a,b) =
+    let x,y = printfn "hello"; b*a,a*b
+    (fun () -> x + y)
+         """
+         |> compile
+         |> shouldSucceed
+         |> verifyIL [
+         // Checks the captured 'x' and 'y' are not promoted onto the heap
+                      """
+.method public static class [FSharp.Core]Microsoft.FSharp.Core.FSharpFunc`2<class [FSharp.Core]Microsoft.FSharp.Core.Unit,int32> 
+        testFunction(int32 a,
+                     int32 b) cil managed
+{
+  
+  .maxstack  4
+  .locals init (class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit> V_0,
+           int32 V_1,
+           int32 V_2)
+  IL_0000:  ldstr      "hello"
+  IL_0005:  newobj     instance void class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`5<class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>::.ctor(string)
+  IL_000a:  stloc.0
+  IL_000b:  call       class [netstandard]System.IO.TextWriter [netstandard]System.Console::get_Out()
+  IL_0010:  ldloc.0
+  IL_0011:  call       !!0 [FSharp.Core]Microsoft.FSharp.Core.PrintfModule::PrintFormatLineToTextWriter<class [FSharp.Core]Microsoft.FSharp.Core.Unit>(class [runtime]System.IO.TextWriter,
+                                                                                                                                                       class [FSharp.Core]Microsoft.FSharp.Core.PrintfFormat`4<!!0,class [runtime]System.IO.TextWriter,class [FSharp.Core]Microsoft.FSharp.Core.Unit,class [FSharp.Core]Microsoft.FSharp.Core.Unit>)
+  IL_0016:  pop
+  IL_0017:  ldarg.1
+  IL_0018:  ldarg.0
+  IL_0019:  mul
+  IL_001a:  stloc.1
+  IL_001b:  ldarg.0
+  IL_001c:  ldarg.1
+  IL_001d:  mul
+  IL_001e:  stloc.2
+  IL_001f:  ldloc.1
+  IL_0020:  ldloc.2
+  IL_0021:  newobj     instance void TupleElimination/testFunction@7::.ctor(int32,
+                                                                            int32)
+  IL_0026:  ret
+} 
+""" 
+
+         // Checks the names of captured 'x' and 'y'. Currently these are not
+         // good names, see https://github.com/dotnet/fsharp/issues/11689,
+         // this test will need updating when this is fixed
+                      """
+  .method public strict virtual instance int32 
+          Invoke(class [FSharp.Core]Microsoft.FSharp.Core.Unit unitVar0) cil managed
+  {
+    
+    .maxstack  8
+    IL_0000:  ldarg.0
+    IL_0001:  ldfld      int32 TupleElimination/testFunction@7::patternInput_0
+    IL_0006:  ldarg.0
+    IL_0007:  ldfld      int32 TupleElimination/testFunction@7::patternInput_1
+    IL_000c:  add
+    IL_000d:  ret
+  } 
+
+""" ]
+
+
+
+
