diff --git a/src/fsharp/Optimizer.fs b/src/fsharp/Optimizer.fs index 599392383d719ac3ccff76a01b35bf1cb52d7049..116911167ebb61e8e5902eca5e2f959ab00157d4 100644 --- a/src/fsharp/Optimizer.fs +++ b/src/fsharp/Optimizer.fs @@ -1764,7 +1764,15 @@ let TryDetectQueryQuoteAndRun cenv (expr:Expr) = //printfn "Not eliminating because no Run found" None - +let IsSystemStringConcatOverload (methRef: ILMethodRef) = + methRef.Name = "Concat" && methRef.DeclaringTypeRef.FullName = "System.String" && + methRef.ReturnType.BasicQualifiedName = "System.String" && + methRef.ArgTypes |> List.forall(fun ilty -> ilty.BasicQualifiedName = "System.String") + +let IsSystemStringConcatArray (methRef: ILMethodRef) = + methRef.Name = "Concat" && methRef.DeclaringTypeRef.FullName = "System.String" && + methRef.ReturnType.BasicQualifiedName = "System.String" && + methRef.ArgTypes.Length = 1 && methRef.ArgTypes.Head.BasicQualifiedName = "System.String[]" //------------------------------------------------------------------------- // The traversal @@ -1824,7 +1832,6 @@ let rec OptimizeExpr cenv (env:IncrementalOptimizationEnv) expr = assert ("unexpected reclink" = "") failwith "Unexpected reclink" - //------------------------------------------------------------------------- // Optimize/analyze an object expression //------------------------------------------------------------------------- @@ -1874,9 +1881,54 @@ and OptimizeInterfaceImpl cenv env baseValOpt (ty, overrides) = Info=UnknownValue} //------------------------------------------------------------------------- -// Optimize/analyze an application of an intrinsic operator to arguments +// Make and optimize String.Concat calls //------------------------------------------------------------------------- +and MakeOptimizedSystemStringConcatCall cenv env m args = + let rec optimizeArg e accArgs = + match e, accArgs with + | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, [ Expr.Op(TOp.Array, _, args, _) ], _), _ when IsSystemStringConcatArray methRef -> + optimizeArgs args accArgs + + | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, args, _), _ when IsSystemStringConcatOverload methRef -> + optimizeArgs args accArgs + + // Optimize string constants, e.g. "1" + "2" will turn into "12" + | Expr.Const(Const.String str1, _, _), Expr.Const(Const.String str2, _, _) :: accArgs -> + mkString cenv.g m (str1 + str2) :: accArgs + + | arg, _ -> arg :: accArgs + + and optimizeArgs args accArgs = + (args, accArgs) + ||> List.foldBack (fun arg accArgs -> optimizeArg arg accArgs) + + let args = optimizeArgs args [] + + let e = + match args with + | [ arg ] -> + arg + | [ arg1; arg2 ] -> + mkStaticCall_String_Concat2 cenv.g m arg1 arg2 + | [ arg1; arg2; arg3 ] -> + mkStaticCall_String_Concat3 cenv.g m arg1 arg2 arg3 + | [ arg1; arg2; arg3; arg4 ] -> + mkStaticCall_String_Concat4 cenv.g m arg1 arg2 arg3 arg4 + | args -> + let arg = mkArray (cenv.g.string_ty, args, m) + mkStaticCall_String_Concat_Array cenv.g m arg + + match e with + | Expr.Op(TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _) as op, tyargs, args, m) when IsSystemStringConcatOverload methRef || IsSystemStringConcatArray methRef -> + OptimizeExprOpReductions cenv env (op, tyargs, args, m) + | _ -> + OptimizeExpr cenv env e + +//------------------------------------------------------------------------- +// Optimize/analyze an application of an intrinsic operator to arguments +//------------------------------------------------------------------------- + and OptimizeExprOp cenv env (op, tyargs, args, m) = // Special cases @@ -1940,22 +1992,30 @@ and OptimizeExprOp cenv env (op, tyargs, args, m) = // if the types match up. | TOp.ILAsm([], [ty]), _, [a] when typeEquiv cenv.g (tyOfExpr cenv.g a) ty -> OptimizeExpr cenv env a - | _ -> - // Reductions - let args', arginfos = OptimizeExprsThenConsiderSplits cenv env args - let knownValue = - match op, arginfos with - | TOp.ValFieldGet (rf), [e1info] -> TryOptimizeRecordFieldGet cenv env (e1info, rf, tyargs, m) - | TOp.TupleFieldGet (tupInfo, n), [e1info] -> TryOptimizeTupleFieldGet cenv env (tupInfo, e1info, tyargs, n, m) - | TOp.UnionCaseFieldGet (cspec, n), [e1info] -> TryOptimizeUnionCaseGet cenv env (e1info, cspec, tyargs, n, m) - | _ -> None - match knownValue with - | Some valu -> - match TryOptimizeVal cenv env (false, valu, m) with - | Some res -> OptimizeExpr cenv env res (* discard e1 since guard ensures it has no effects *) - | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu - | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos UnknownValue + // Optimize calls when concatenating strings, e.g. "1" + "2" + "3" + "4" .. etc. + | TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, [ Expr.Op(TOp.Array, _, args, _) ] when IsSystemStringConcatArray methRef -> + MakeOptimizedSystemStringConcatCall cenv env m args + | TOp.ILCall(_, _, _, _, _, _, _, methRef, _, _, _), _, args when IsSystemStringConcatOverload methRef -> + MakeOptimizedSystemStringConcatCall cenv env m args + | _ -> + // Reductions + OptimizeExprOpReductions cenv env (op, tyargs, args, m) + +and OptimizeExprOpReductions cenv env (op, tyargs, args, m) = + let args', arginfos = OptimizeExprsThenConsiderSplits cenv env args + let knownValue = + match op, arginfos with + | TOp.ValFieldGet (rf), [e1info] -> TryOptimizeRecordFieldGet cenv env (e1info, rf, tyargs, m) + | TOp.TupleFieldGet (tupInfo, n), [e1info] -> TryOptimizeTupleFieldGet cenv env (tupInfo, e1info, tyargs, n, m) + | TOp.UnionCaseFieldGet (cspec, n), [e1info] -> TryOptimizeUnionCaseGet cenv env (e1info, cspec, tyargs, n, m) + | _ -> None + match knownValue with + | Some valu -> + match TryOptimizeVal cenv env (false, valu, m) with + | Some res -> OptimizeExpr cenv env res (* discard e1 since guard ensures it has no effects *) + | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu + | None -> OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos UnknownValue and OptimizeExprOpFallback cenv env (op, tyargs, args', m) arginfos valu = // The generic case - we may collect information, but the construction/projection doesn't disappear @@ -2682,7 +2742,7 @@ and TryInlineApplication cenv env finfo (tyargs: TType list, args: Expr list, m) // Inlining: beta reducing let expr' = MakeApplicationAndBetaReduce cenv.g (f2', f2ty, [tyargs], args', m) // Inlining: reoptimizing - Some (OptimizeExpr cenv {env with dontInline= Zset.add lambdaId env.dontInline} expr') + Some(OptimizeExpr cenv {env with dontInline= Zset.add lambdaId env.dontInline} expr') | _ -> None diff --git a/src/fsharp/TastOps.fs b/src/fsharp/TastOps.fs index dfebdb9bae0399c99e19568ace17a1eae2faf8af..c50593dd7a77b8f8e500048f6fdd053e3807179b 100644 --- a/src/fsharp/TastOps.fs +++ b/src/fsharp/TastOps.fs @@ -6366,6 +6366,18 @@ let mkIsInst ty e m = mkAsmExpr ([ isinst ], [ty], [e], [ ty ], m) let mspec_Type_GetTypeFromHandle (g: TcGlobals) = IL.mkILNonGenericStaticMethSpecInTy(g.ilg.typ_Type, "GetTypeFromHandle", [g.iltyp_RuntimeTypeHandle], g.ilg.typ_Type) let mspec_String_Length (g: TcGlobals) = mkILNonGenericInstanceMethSpecInTy (g.ilg.typ_String, "get_Length", [], g.ilg.typ_Int32) +let mspec_String_Concat2 (g: TcGlobals) = + mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String) + +let mspec_String_Concat3 (g: TcGlobals) = + mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String) + +let mspec_String_Concat4 (g: TcGlobals) = + mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String; g.ilg.typ_String ], g.ilg.typ_String) + +let mspec_String_Concat_Array (g: TcGlobals) = + mkILNonGenericStaticMethSpecInTy (g.ilg.typ_String, "Concat", [ mkILArr1DTy g.ilg.typ_String ], g.ilg.typ_String) + let fspec_Missing_Value (g: TcGlobals) = IL.mkILFieldSpecInTy(g.iltyp_Missing, "Value", g.iltyp_Missing) let mkInitializeArrayMethSpec (g: TcGlobals) = @@ -6587,6 +6599,21 @@ let mkGetStringLength g m e = /// ILCall(useCallvirt, isProtected, valu, newobj, valUseFlags, isProp, noTailCall, mref, actualTypeInst, actualMethInst, retTy) Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, true, false, mspec.MethodRef, [], [], [g.int32_ty]), [], [e], m) +let mkStaticCall_String_Concat2 g m arg1 arg2 = + let mspec = mspec_String_Concat2 g + Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2], m) + +let mkStaticCall_String_Concat3 g m arg1 arg2 arg3 = + let mspec = mspec_String_Concat3 g + Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2; arg3], m) + +let mkStaticCall_String_Concat4 g m arg1 arg2 arg3 arg4 = + let mspec = mspec_String_Concat4 g + Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg1; arg2; arg3; arg4], m) + +let mkStaticCall_String_Concat_Array g m arg = + let mspec = mspec_String_Concat_Array g + Expr.Op(TOp.ILCall(false, false, false, false, ValUseFlag.NormalValUse, false, false, mspec.MethodRef, [], [], [g.string_ty]), [], [arg], m) // Quotations can't contain any IL. // As a result, we aim to get rid of all IL generation in the typechecker and pattern match diff --git a/src/fsharp/TastOps.fsi b/src/fsharp/TastOps.fsi index 8856119d7be5c18f4c0f6f7ec2b0a2d68bacaba3..0731970c1958b856095c0cb9f8a00f3d11c9fa1a 100755 --- a/src/fsharp/TastOps.fsi +++ b/src/fsharp/TastOps.fsi @@ -1386,6 +1386,11 @@ val mkCallNewQuerySource : TcGlobals -> range -> TType -> TType -> Expr -> Expr val mkArray : TType * Exprs * range -> Expr +val mkStaticCall_String_Concat2 : TcGlobals -> range -> Expr -> Expr -> Expr +val mkStaticCall_String_Concat3 : TcGlobals -> range -> Expr -> Expr -> Expr -> Expr +val mkStaticCall_String_Concat4 : TcGlobals -> range -> Expr -> Expr -> Expr -> Expr -> Expr +val mkStaticCall_String_Concat_Array : TcGlobals -> range -> Expr -> Expr + //------------------------------------------------------------------------- // operations primarily associated with the optimization to fix // up loops to generate .NET code that does not include array bound checks diff --git a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj index 06b8a4b422a7f7121f484d739c95d2e95850f1a2..da3db8e565f970bd7679aece5337ba29830b133b 100644 --- a/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj +++ b/tests/FSharp.Compiler.UnitTests/FSharp.Compiler.UnitTests.fsproj @@ -44,7 +44,7 @@ - ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll + ..\..\..\packages\System.ValueTuple.$(SystemValueTuplePackageVersion)\lib\netstandard1.0\System.ValueTuple.dll @@ -54,6 +54,8 @@ + + diff --git a/tests/FSharp.Compiler.UnitTests/ILHelpers.fs b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs new file mode 100644 index 0000000000000000000000000000000000000000..32c4e209d8b04c152acfdc2244dec7f0c94ec6c8 --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/ILHelpers.fs @@ -0,0 +1,117 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open System +open System.IO +open System.Diagnostics + +open NUnit.Framework + +open Microsoft.FSharp.Compiler.SourceCodeServices + +module ILChecker = + + let checker = FSharpChecker.Create() + + let private (++) a b = Path.Combine(a,b) + + let private getfullpath workDir path = + let rooted = + if Path.IsPathRooted(path) then path + else Path.Combine(workDir, path) + rooted |> Path.GetFullPath + + let private fileExists workDir path = + if path |> getfullpath workDir |> File.Exists then Some path else None + + let private requireFile nm = + if fileExists __SOURCE_DIRECTORY__ nm |> Option.isSome then nm else failwith (sprintf "couldn't find %s. Running 'build test' once might solve this issue" nm) + + let private exec exe args = + let startInfo = ProcessStartInfo(exe, String.concat " " args) + startInfo.RedirectStandardError <- true + startInfo.UseShellExecute <- false + use p = Process.Start(startInfo) + p.WaitForExit() + p.StandardError.ReadToEnd(), p.ExitCode + + /// Compile the source and check to see if the expected IL exists. + /// The first line of each expected IL string is found first. + let check source expectedIL = + let SCRIPT_ROOT = __SOURCE_DIRECTORY__ + let packagesDir = SCRIPT_ROOT ++ ".." ++ ".." ++ "packages" + let Is64BitOperatingSystem = sizeof = 8 + let architectureMoniker = if Is64BitOperatingSystem then "x64" else "x86" + let ildasmExe = requireFile (packagesDir ++ ("runtime.win-" + architectureMoniker + ".Microsoft.NETCore.ILDAsm.2.0.3") ++ "runtimes" ++ ("win-" + architectureMoniker) ++ "native" ++ "ildasm.exe") + let coreclrDll = requireFile (packagesDir ++ ("runtime.win-" + architectureMoniker + ".Microsoft.NETCore.Runtime.CoreCLR.2.0.3") ++ "runtimes" ++ ("win-" + architectureMoniker) ++ "native" ++ "coreclr.dll") + + let tmp = Path.GetTempFileName() + let tmpFs = Path.ChangeExtension(tmp, ".fs") + let tmpDll = Path.ChangeExtension(tmp, ".dll") + let tmpIL = Path.ChangeExtension(tmp, ".il") + + let mutable errorMsgOpt = None + try + // ildasm requires coreclr.dll to run which has already been restored to the packages directory + File.Copy(coreclrDll, Path.GetDirectoryName(ildasmExe) ++ "coreclr.dll", overwrite=true) + + File.WriteAllText(tmpFs, source) + + let errors, exitCode = checker.Compile([| "fsc.exe"; "--optimize+"; "-o"; tmpDll; "-a"; tmpFs |]) |> Async.RunSynchronously + let errors = + String.concat "\n" (errors |> Array.map (fun x -> x.Message)) + + if exitCode = 0 then + exec ildasmExe [ sprintf "%s /out=%s" tmpDll tmpIL ] |> ignore + + let text = File.ReadAllText(tmpIL) + let blockComments = @"/\*(.*?)\*/" + let lineComments = @"//(.*?)\r?\n" + let strings = @"""((\\[^\n]|[^""\n])*)""" + let verbatimStrings = @"@(""[^""]*"")+" + let textNoComments = + System.Text.RegularExpressions.Regex.Replace(text, + blockComments + "|" + lineComments + "|" + strings + "|" + verbatimStrings, + (fun me -> + if (me.Value.StartsWith("/*") || me.Value.StartsWith("//")) then + if me.Value.StartsWith("//") then Environment.NewLine else String.Empty + else + me.Value), System.Text.RegularExpressions.RegexOptions.Singleline) + + expectedIL + |> List.iter (fun (ilCode: string) -> + let expectedLines = ilCode.Split('\n') + let startIndex = textNoComments.IndexOf(expectedLines.[0]) + if startIndex = -1 || textNoComments.Length < startIndex + ilCode.Length then + errorMsgOpt <- Some("==EXPECTED CONTAINS==\n" + ilCode + "\n") + else + let errors = ResizeArray() + let actualLines = textNoComments.Substring(startIndex, textNoComments.Length - startIndex).Split('\n') + for i = 0 to expectedLines.Length - 1 do + let expected = expectedLines.[i].Trim() + let actual = actualLines.[i].Trim() + if expected <> actual then + errors.Add(sprintf "\n==\nName: %s\n\nExpected:\t %s\nActual:\t\t %s\n==" actualLines.[0] expected actual) + + if errors.Count > 0 then + let msg = String.concat "\n" errors + "\n\n\n==EXPECTED==\n" + ilCode + "\n" + errorMsgOpt <- Some(msg + "\n\n\n==ACTUAL==\n" + String.Join("\n", actualLines, 0, expectedLines.Length)) + ) + + match errorMsgOpt with + | Some(msg) -> errorMsgOpt <- Some(msg + "\n\n\n==ENTIRE ACTUAL==\n" + textNoComments) + | _ -> () + else + errorMsgOpt <- Some(errors) + finally + try File.Delete(tmp) with | _ -> () + try File.Delete(tmpFs) with | _ -> () + try File.Delete(tmpDll) with | _ -> () + try File.Delete(tmpIL) with | _ -> () + + match errorMsgOpt with + | Some(errorMsg) -> + Assert.Fail(errorMsg) + | _ -> () + diff --git a/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs b/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs new file mode 100644 index 0000000000000000000000000000000000000000..fa18e7bb4279cfbb56a89342d3340190654b87ee --- /dev/null +++ b/tests/FSharp.Compiler.UnitTests/Language/StringConcat.fs @@ -0,0 +1,845 @@ +// Copyright (c) Microsoft Corporation. All Rights Reserved. See License.txt in the project root for license information. + +namespace FSharp.Compiler.UnitTests + +open System +open NUnit.Framework + +[] +module StringConcat = + + [] + let Optimizations () = + let baseSource = """ +module Test + +open System + +let arr = ResizeArray() + +let inline ss (x: int) = + arr.Add(x) + "_" + x.ToString() + "_" +""" + + let test1Source = """ +let test1 () = + ss 1 + ss 2 + ss 3 +""" + let test1IL = """.method public static string test1() cil managed + { + + .maxstack 7 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0007: ldc.i4.1 + IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_000d: ldstr "_" + IL_0012: ldloca.s V_0 + IL_0014: constrained. [mscorlib]System.Int32 + IL_001a: callvirt instance string [mscorlib]System.Object::ToString() + IL_001f: ldstr "_" + IL_0024: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0029: ldc.i4.2 + IL_002a: stloc.0 + IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0030: ldc.i4.2 + IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0036: ldstr "_" + IL_003b: ldloca.s V_0 + IL_003d: constrained. [mscorlib]System.Int32 + IL_0043: callvirt instance string [mscorlib]System.Object::ToString() + IL_0048: ldstr "_" + IL_004d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0052: ldc.i4.3 + IL_0053: stloc.0 + IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0059: ldc.i4.3 + IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_005f: ldstr "_" + IL_0064: ldloca.s V_0 + IL_0066: constrained. [mscorlib]System.Int32 + IL_006c: callvirt instance string [mscorlib]System.Object::ToString() + IL_0071: ldstr "_" + IL_0076: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_007b: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0080: ret + }""" + + let test2Source = """ +let test2 () = + ss 1 + ss 2 + ss 3 + ss 4 +""" + let test2IL = """.method public static string test2() cil managed + { + + .maxstack 8 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0007: ldc.i4.1 + IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_000d: ldstr "_" + IL_0012: ldloca.s V_0 + IL_0014: constrained. [mscorlib]System.Int32 + IL_001a: callvirt instance string [mscorlib]System.Object::ToString() + IL_001f: ldstr "_" + IL_0024: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0029: ldc.i4.2 + IL_002a: stloc.0 + IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0030: ldc.i4.2 + IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0036: ldstr "_" + IL_003b: ldloca.s V_0 + IL_003d: constrained. [mscorlib]System.Int32 + IL_0043: callvirt instance string [mscorlib]System.Object::ToString() + IL_0048: ldstr "_" + IL_004d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0052: ldc.i4.3 + IL_0053: stloc.0 + IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0059: ldc.i4.3 + IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_005f: ldstr "_" + IL_0064: ldloca.s V_0 + IL_0066: constrained. [mscorlib]System.Int32 + IL_006c: callvirt instance string [mscorlib]System.Object::ToString() + IL_0071: ldstr "_" + IL_0076: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_007b: ldc.i4.4 + IL_007c: stloc.0 + IL_007d: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0082: ldc.i4.4 + IL_0083: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0088: ldstr "_" + IL_008d: ldloca.s V_0 + IL_008f: constrained. [mscorlib]System.Int32 + IL_0095: callvirt instance string [mscorlib]System.Object::ToString() + IL_009a: ldstr "_" + IL_009f: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00a4: call string [mscorlib]System.String::Concat(string, + string, + string, + string) + IL_00a9: ret + }""" + + let test3Source = """ +let test3 () = + ss 1 + ss 2 + ss 3 + ss 4 + ss 5 +""" + let test3IL = """.method public static string test3() cil managed + { + + .maxstack 8 + .locals init (int32 V_0) + IL_0000: ldc.i4.5 + IL_0001: newarr [mscorlib]System.String + IL_0006: dup + IL_0007: ldc.i4.0 + IL_0008: ldc.i4.1 + IL_0009: stloc.0 + IL_000a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_000f: ldc.i4.1 + IL_0010: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0015: ldstr "_" + IL_001a: ldloca.s V_0 + IL_001c: constrained. [mscorlib]System.Int32 + IL_0022: callvirt instance string [mscorlib]System.Object::ToString() + IL_0027: ldstr "_" + IL_002c: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0031: stelem [mscorlib]System.String + IL_0036: dup + IL_0037: ldc.i4.1 + IL_0038: ldc.i4.2 + IL_0039: stloc.0 + IL_003a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_003f: ldc.i4.2 + IL_0040: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0045: ldstr "_" + IL_004a: ldloca.s V_0 + IL_004c: constrained. [mscorlib]System.Int32 + IL_0052: callvirt instance string [mscorlib]System.Object::ToString() + IL_0057: ldstr "_" + IL_005c: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0061: stelem [mscorlib]System.String + IL_0066: dup + IL_0067: ldc.i4.2 + IL_0068: ldc.i4.3 + IL_0069: stloc.0 + IL_006a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_006f: ldc.i4.3 + IL_0070: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0075: ldstr "_" + IL_007a: ldloca.s V_0 + IL_007c: constrained. [mscorlib]System.Int32 + IL_0082: callvirt instance string [mscorlib]System.Object::ToString() + IL_0087: ldstr "_" + IL_008c: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0091: stelem [mscorlib]System.String + IL_0096: dup + IL_0097: ldc.i4.3 + IL_0098: ldc.i4.4 + IL_0099: stloc.0 + IL_009a: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_009f: ldc.i4.4 + IL_00a0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00a5: ldstr "_" + IL_00aa: ldloca.s V_0 + IL_00ac: constrained. [mscorlib]System.Int32 + IL_00b2: callvirt instance string [mscorlib]System.Object::ToString() + IL_00b7: ldstr "_" + IL_00bc: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00c1: stelem [mscorlib]System.String + IL_00c6: dup + IL_00c7: ldc.i4.4 + IL_00c8: ldc.i4.5 + IL_00c9: stloc.0 + IL_00ca: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_00cf: ldc.i4.5 + IL_00d0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d5: ldstr "_" + IL_00da: ldloca.s V_0 + IL_00dc: constrained. [mscorlib]System.Int32 + IL_00e2: callvirt instance string [mscorlib]System.Object::ToString() + IL_00e7: ldstr "_" + IL_00ec: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00f1: stelem [mscorlib]System.String + IL_00f6: call string [mscorlib]System.String::Concat(string[]) + IL_00fb: ret + }""" + + let test4Source = """ +let test4 () = + ss 5 + ss 6 + ss 7 + String.Concat(ss 8, ss 9) + ss 10 + "_50_" + "_60_" + String.Concat(ss 100, String.Concat(ss 101, ss 102), ss 103) + String.Concat([|"_104_";"_105_"|]) + ss 106 +""" + let test4IL = """.method public static string test4() cil managed + { + + .maxstack 8 + .locals init (int32 V_0) + IL_0000: ldc.i4.s 13 + IL_0002: newarr [mscorlib]System.String + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldc.i4.5 + IL_000a: stloc.0 + IL_000b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0010: ldc.i4.5 + IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0016: ldstr "_" + IL_001b: ldloca.s V_0 + IL_001d: constrained. [mscorlib]System.Int32 + IL_0023: callvirt instance string [mscorlib]System.Object::ToString() + IL_0028: ldstr "_" + IL_002d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0032: stelem [mscorlib]System.String + IL_0037: dup + IL_0038: ldc.i4.1 + IL_0039: ldc.i4.6 + IL_003a: stloc.0 + IL_003b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0040: ldc.i4.6 + IL_0041: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0046: ldstr "_" + IL_004b: ldloca.s V_0 + IL_004d: constrained. [mscorlib]System.Int32 + IL_0053: callvirt instance string [mscorlib]System.Object::ToString() + IL_0058: ldstr "_" + IL_005d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0062: stelem [mscorlib]System.String + IL_0067: dup + IL_0068: ldc.i4.2 + IL_0069: ldc.i4.7 + IL_006a: stloc.0 + IL_006b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0070: ldc.i4.7 + IL_0071: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0076: ldstr "_" + IL_007b: ldloca.s V_0 + IL_007d: constrained. [mscorlib]System.Int32 + IL_0083: callvirt instance string [mscorlib]System.Object::ToString() + IL_0088: ldstr "_" + IL_008d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0092: stelem [mscorlib]System.String + IL_0097: dup + IL_0098: ldc.i4.3 + IL_0099: ldc.i4.8 + IL_009a: stloc.0 + IL_009b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_00a0: ldc.i4.8 + IL_00a1: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00a6: ldstr "_" + IL_00ab: ldloca.s V_0 + IL_00ad: constrained. [mscorlib]System.Int32 + IL_00b3: callvirt instance string [mscorlib]System.Object::ToString() + IL_00b8: ldstr "_" + IL_00bd: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00c2: stelem [mscorlib]System.String + IL_00c7: dup + IL_00c8: ldc.i4.4 + IL_00c9: ldc.i4.s 9 + IL_00cb: stloc.0 + IL_00cc: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_00d1: ldc.i4.s 9 + IL_00d3: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d8: ldstr "_" + IL_00dd: ldloca.s V_0 + IL_00df: constrained. [mscorlib]System.Int32 + IL_00e5: callvirt instance string [mscorlib]System.Object::ToString() + IL_00ea: ldstr "_" + IL_00ef: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00f4: stelem [mscorlib]System.String + IL_00f9: dup + IL_00fa: ldc.i4.5 + IL_00fb: ldc.i4.s 10 + IL_00fd: stloc.0 + IL_00fe: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0103: ldc.i4.s 10 + IL_0105: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_010a: ldstr "_" + IL_010f: ldloca.s V_0 + IL_0111: constrained. [mscorlib]System.Int32 + IL_0117: callvirt instance string [mscorlib]System.Object::ToString() + IL_011c: ldstr "_" + IL_0121: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0126: stelem [mscorlib]System.String + IL_012b: dup + IL_012c: ldc.i4.6 + IL_012d: ldstr "_50__60_" + IL_0132: stelem [mscorlib]System.String + IL_0137: dup + IL_0138: ldc.i4.7 + IL_0139: ldc.i4.s 100 + IL_013b: stloc.0 + IL_013c: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0141: ldc.i4.s 100 + IL_0143: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0148: ldstr "_" + IL_014d: ldloca.s V_0 + IL_014f: constrained. [mscorlib]System.Int32 + IL_0155: callvirt instance string [mscorlib]System.Object::ToString() + IL_015a: ldstr "_" + IL_015f: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0164: stelem [mscorlib]System.String + IL_0169: dup + IL_016a: ldc.i4.8 + IL_016b: ldc.i4.s 101 + IL_016d: stloc.0 + IL_016e: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0173: ldc.i4.s 101 + IL_0175: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_017a: ldstr "_" + IL_017f: ldloca.s V_0 + IL_0181: constrained. [mscorlib]System.Int32 + IL_0187: callvirt instance string [mscorlib]System.Object::ToString() + IL_018c: ldstr "_" + IL_0191: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0196: stelem [mscorlib]System.String + IL_019b: dup + IL_019c: ldc.i4.s 9 + IL_019e: ldc.i4.s 102 + IL_01a0: stloc.0 + IL_01a1: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_01a6: ldc.i4.s 102 + IL_01a8: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_01ad: ldstr "_" + IL_01b2: ldloca.s V_0 + IL_01b4: constrained. [mscorlib]System.Int32 + IL_01ba: callvirt instance string [mscorlib]System.Object::ToString() + IL_01bf: ldstr "_" + IL_01c4: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_01c9: stelem [mscorlib]System.String + IL_01ce: dup + IL_01cf: ldc.i4.s 10 + IL_01d1: ldc.i4.s 103 + IL_01d3: stloc.0 + IL_01d4: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_01d9: ldc.i4.s 103 + IL_01db: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_01e0: ldstr "_" + IL_01e5: ldloca.s V_0 + IL_01e7: constrained. [mscorlib]System.Int32 + IL_01ed: callvirt instance string [mscorlib]System.Object::ToString() + IL_01f2: ldstr "_" + IL_01f7: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_01fc: stelem [mscorlib]System.String + IL_0201: dup + IL_0202: ldc.i4.s 11 + IL_0204: ldstr "_104__105_" + IL_0209: stelem [mscorlib]System.String + IL_020e: dup + IL_020f: ldc.i4.s 12 + IL_0211: ldc.i4.s 106 + IL_0213: stloc.0 + IL_0214: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0219: ldc.i4.s 106 + IL_021b: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0220: ldstr "_" + IL_0225: ldloca.s V_0 + IL_0227: constrained. [mscorlib]System.Int32 + IL_022d: callvirt instance string [mscorlib]System.Object::ToString() + IL_0232: ldstr "_" + IL_0237: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_023c: stelem [mscorlib]System.String + IL_0241: call string [mscorlib]System.String::Concat(string[]) + IL_0246: ret + }""" + + let test5Source = """ +let test5 () = + ss 5 + ss 6 + ss 7 + String.Concat(ss 8, ss 9) + ss 10 + "_50_" + "_60_" + String.Concat(ss 100, (let x = String.Concat(ss 101, ss 102) in Console.WriteLine(x);x), ss 103) + String.Concat([|"_104_";"_105_"|]) + ss 106 +""" + let test5IL = """.method public static string test5() cil managed + { + + .maxstack 9 + .locals init (int32 V_0, + string V_1) + IL_0000: ldc.i4.s 12 + IL_0002: newarr [mscorlib]System.String + IL_0007: dup + IL_0008: ldc.i4.0 + IL_0009: ldc.i4.5 + IL_000a: stloc.0 + IL_000b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0010: ldc.i4.5 + IL_0011: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0016: ldstr "_" + IL_001b: ldloca.s V_0 + IL_001d: constrained. [mscorlib]System.Int32 + IL_0023: callvirt instance string [mscorlib]System.Object::ToString() + IL_0028: ldstr "_" + IL_002d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0032: stelem [mscorlib]System.String + IL_0037: dup + IL_0038: ldc.i4.1 + IL_0039: ldc.i4.6 + IL_003a: stloc.0 + IL_003b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0040: ldc.i4.6 + IL_0041: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0046: ldstr "_" + IL_004b: ldloca.s V_0 + IL_004d: constrained. [mscorlib]System.Int32 + IL_0053: callvirt instance string [mscorlib]System.Object::ToString() + IL_0058: ldstr "_" + IL_005d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0062: stelem [mscorlib]System.String + IL_0067: dup + IL_0068: ldc.i4.2 + IL_0069: ldc.i4.7 + IL_006a: stloc.0 + IL_006b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0070: ldc.i4.7 + IL_0071: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0076: ldstr "_" + IL_007b: ldloca.s V_0 + IL_007d: constrained. [mscorlib]System.Int32 + IL_0083: callvirt instance string [mscorlib]System.Object::ToString() + IL_0088: ldstr "_" + IL_008d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0092: stelem [mscorlib]System.String + IL_0097: dup + IL_0098: ldc.i4.3 + IL_0099: ldc.i4.8 + IL_009a: stloc.0 + IL_009b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_00a0: ldc.i4.8 + IL_00a1: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00a6: ldstr "_" + IL_00ab: ldloca.s V_0 + IL_00ad: constrained. [mscorlib]System.Int32 + IL_00b3: callvirt instance string [mscorlib]System.Object::ToString() + IL_00b8: ldstr "_" + IL_00bd: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00c2: stelem [mscorlib]System.String + IL_00c7: dup + IL_00c8: ldc.i4.4 + IL_00c9: ldc.i4.s 9 + IL_00cb: stloc.0 + IL_00cc: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_00d1: ldc.i4.s 9 + IL_00d3: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_00d8: ldstr "_" + IL_00dd: ldloca.s V_0 + IL_00df: constrained. [mscorlib]System.Int32 + IL_00e5: callvirt instance string [mscorlib]System.Object::ToString() + IL_00ea: ldstr "_" + IL_00ef: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00f4: stelem [mscorlib]System.String + IL_00f9: dup + IL_00fa: ldc.i4.5 + IL_00fb: ldc.i4.s 10 + IL_00fd: stloc.0 + IL_00fe: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0103: ldc.i4.s 10 + IL_0105: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_010a: ldstr "_" + IL_010f: ldloca.s V_0 + IL_0111: constrained. [mscorlib]System.Int32 + IL_0117: callvirt instance string [mscorlib]System.Object::ToString() + IL_011c: ldstr "_" + IL_0121: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0126: stelem [mscorlib]System.String + IL_012b: dup + IL_012c: ldc.i4.6 + IL_012d: ldstr "_50__60_" + IL_0132: stelem [mscorlib]System.String + IL_0137: dup + IL_0138: ldc.i4.7 + IL_0139: ldc.i4.s 100 + IL_013b: stloc.0 + IL_013c: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0141: ldc.i4.s 100 + IL_0143: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0148: ldstr "_" + IL_014d: ldloca.s V_0 + IL_014f: constrained. [mscorlib]System.Int32 + IL_0155: callvirt instance string [mscorlib]System.Object::ToString() + IL_015a: ldstr "_" + IL_015f: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0164: stelem [mscorlib]System.String + IL_0169: dup + IL_016a: ldc.i4.8 + IL_016b: ldc.i4.s 101 + IL_016d: stloc.0 + IL_016e: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0173: ldc.i4.s 101 + IL_0175: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_017a: ldstr "_" + IL_017f: ldloca.s V_0 + IL_0181: constrained. [mscorlib]System.Int32 + IL_0187: callvirt instance string [mscorlib]System.Object::ToString() + IL_018c: ldstr "_" + IL_0191: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0196: ldc.i4.s 102 + IL_0198: stloc.0 + IL_0199: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_019e: ldc.i4.s 102 + IL_01a0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_01a5: ldstr "_" + IL_01aa: ldloca.s V_0 + IL_01ac: constrained. [mscorlib]System.Int32 + IL_01b2: callvirt instance string [mscorlib]System.Object::ToString() + IL_01b7: ldstr "_" + IL_01bc: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_01c1: call string [mscorlib]System.String::Concat(string, + string) + IL_01c6: stloc.1 + IL_01c7: ldloc.1 + IL_01c8: call void [mscorlib]System.Console::WriteLine(string) + IL_01cd: ldloc.1 + IL_01ce: stelem [mscorlib]System.String + IL_01d3: dup + IL_01d4: ldc.i4.s 9 + IL_01d6: ldc.i4.s 103 + IL_01d8: stloc.0 + IL_01d9: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_01de: ldc.i4.s 103 + IL_01e0: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_01e5: ldstr "_" + IL_01ea: ldloca.s V_0 + IL_01ec: constrained. [mscorlib]System.Int32 + IL_01f2: callvirt instance string [mscorlib]System.Object::ToString() + IL_01f7: ldstr "_" + IL_01fc: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0201: stelem [mscorlib]System.String + IL_0206: dup + IL_0207: ldc.i4.s 10 + IL_0209: ldstr "_104__105_" + IL_020e: stelem [mscorlib]System.String + IL_0213: dup + IL_0214: ldc.i4.s 11 + IL_0216: ldc.i4.s 106 + IL_0218: stloc.0 + IL_0219: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_021e: ldc.i4.s 106 + IL_0220: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0225: ldstr "_" + IL_022a: ldloca.s V_0 + IL_022c: constrained. [mscorlib]System.Int32 + IL_0232: callvirt instance string [mscorlib]System.Object::ToString() + IL_0237: ldstr "_" + IL_023c: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0241: stelem [mscorlib]System.String + IL_0246: call string [mscorlib]System.String::Concat(string[]) + IL_024b: ret + }""" + + let test6Source = """ +let inline inlineStringConcat str1 str2 = str1 + str2 + +let test6 () = + inlineStringConcat (inlineStringConcat (ss 1) (ss 2)) (ss 3) + ss 4 +""" + let test6IL = """.method public static string test6() cil managed + { + + .maxstack 8 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0007: ldc.i4.1 + IL_0008: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_000d: ldstr "_" + IL_0012: ldloca.s V_0 + IL_0014: constrained. [mscorlib]System.Int32 + IL_001a: callvirt instance string [mscorlib]System.Object::ToString() + IL_001f: ldstr "_" + IL_0024: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0029: ldc.i4.2 + IL_002a: stloc.0 + IL_002b: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0030: ldc.i4.2 + IL_0031: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0036: ldstr "_" + IL_003b: ldloca.s V_0 + IL_003d: constrained. [mscorlib]System.Int32 + IL_0043: callvirt instance string [mscorlib]System.Object::ToString() + IL_0048: ldstr "_" + IL_004d: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_0052: ldc.i4.3 + IL_0053: stloc.0 + IL_0054: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0059: ldc.i4.3 + IL_005a: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_005f: ldstr "_" + IL_0064: ldloca.s V_0 + IL_0066: constrained. [mscorlib]System.Int32 + IL_006c: callvirt instance string [mscorlib]System.Object::ToString() + IL_0071: ldstr "_" + IL_0076: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_007b: ldc.i4.4 + IL_007c: stloc.0 + IL_007d: call class [mscorlib]System.Collections.Generic.List`1 Test::get_arr() + IL_0082: ldc.i4.4 + IL_0083: callvirt instance void class [mscorlib]System.Collections.Generic.List`1::Add(!0) + IL_0088: ldstr "_" + IL_008d: ldloca.s V_0 + IL_008f: constrained. [mscorlib]System.Int32 + IL_0095: callvirt instance string [mscorlib]System.Object::ToString() + IL_009a: ldstr "_" + IL_009f: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_00a4: call string [mscorlib]System.String::Concat(string, + string, + string, + string) + IL_00a9: ret + }""" + + let test7Source = """ +let test7 () = + let x = 1 + x.ToString() + x.ToString() + x.ToString() +""" + let test7IL = """.method public static string test7() cil managed + { + + .maxstack 5 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: constrained. [mscorlib]System.Int32 + IL_000a: callvirt instance string [mscorlib]System.Object::ToString() + IL_000f: ldloca.s V_0 + IL_0011: constrained. [mscorlib]System.Int32 + IL_0017: callvirt instance string [mscorlib]System.Object::ToString() + IL_001c: ldloca.s V_0 + IL_001e: constrained. [mscorlib]System.Int32 + IL_0024: callvirt instance string [mscorlib]System.Object::ToString() + IL_0029: call string [mscorlib]System.String::Concat(string, + string, + string) + IL_002e: ret + }""" + + let test8Source = """ +let test8 () = + let x = 1 + x.ToString() + x.ToString() + x.ToString() + x.ToString() +""" + let test8IL = """.method public static string test8() cil managed + { + + .maxstack 6 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldloca.s V_0 + IL_0004: constrained. [mscorlib]System.Int32 + IL_000a: callvirt instance string [mscorlib]System.Object::ToString() + IL_000f: ldloca.s V_0 + IL_0011: constrained. [mscorlib]System.Int32 + IL_0017: callvirt instance string [mscorlib]System.Object::ToString() + IL_001c: ldloca.s V_0 + IL_001e: constrained. [mscorlib]System.Int32 + IL_0024: callvirt instance string [mscorlib]System.Object::ToString() + IL_0029: ldloca.s V_0 + IL_002b: constrained. [mscorlib]System.Int32 + IL_0031: callvirt instance string [mscorlib]System.Object::ToString() + IL_0036: call string [mscorlib]System.String::Concat(string, + string, + string, + string) + IL_003b: ret + }""" + + let test9Source = """ +let test9 () = + let x = 1 + x.ToString() + x.ToString() + x.ToString() + x.ToString() + x.ToString() +""" + let test9IL = """.method public static string test9() cil managed + { + + .maxstack 6 + .locals init (int32 V_0) + IL_0000: ldc.i4.1 + IL_0001: stloc.0 + IL_0002: ldc.i4.5 + IL_0003: newarr [mscorlib]System.String + IL_0008: dup + IL_0009: ldc.i4.0 + IL_000a: ldloca.s V_0 + IL_000c: constrained. [mscorlib]System.Int32 + IL_0012: callvirt instance string [mscorlib]System.Object::ToString() + IL_0017: stelem [mscorlib]System.String + IL_001c: dup + IL_001d: ldc.i4.1 + IL_001e: ldloca.s V_0 + IL_0020: constrained. [mscorlib]System.Int32 + IL_0026: callvirt instance string [mscorlib]System.Object::ToString() + IL_002b: stelem [mscorlib]System.String + IL_0030: dup + IL_0031: ldc.i4.2 + IL_0032: ldloca.s V_0 + IL_0034: constrained. [mscorlib]System.Int32 + IL_003a: callvirt instance string [mscorlib]System.Object::ToString() + IL_003f: stelem [mscorlib]System.String + IL_0044: dup + IL_0045: ldc.i4.3 + IL_0046: ldloca.s V_0 + IL_0048: constrained. [mscorlib]System.Int32 + IL_004e: callvirt instance string [mscorlib]System.Object::ToString() + IL_0053: stelem [mscorlib]System.String + IL_0058: dup + IL_0059: ldc.i4.4 + IL_005a: ldloca.s V_0 + IL_005c: constrained. [mscorlib]System.Int32 + IL_0062: callvirt instance string [mscorlib]System.Object::ToString() + IL_0067: stelem [mscorlib]System.String + IL_006c: call string [mscorlib]System.String::Concat(string[]) + IL_0071: ret + }""" + + let sources = + [ + baseSource + test1Source + test2Source + test3Source + test4Source + test5Source + test6Source + test7Source + test8Source + test9Source + ] + let source = String.Join("", sources) + ILChecker.check source + [ + test1IL + test2IL + test3IL + test4IL + test5IL + test6IL + test7IL + test8IL + test9IL + ] diff --git a/tests/fsharp/tests.fs b/tests/fsharp/tests.fs index 4dbb5242b598271df94ee8d165a82b853c2732dc..c79abb4f7914993397647f0c3b25ee504b4a59b7 100644 --- a/tests/fsharp/tests.fs +++ b/tests/fsharp/tests.fs @@ -1878,7 +1878,7 @@ module OptimizationTests = |> Seq.filter (fun line -> line.Contains(".locals init")) |> Seq.length - log "Ran ok - optimizations removed %d textual occurrences of optimizable identifiers from target IL" numElim + log "Ran ok - optimizations removed %d textual occurrences of optimizable identifiers from target IL" numElim [] let stats () =