diff --git a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md index 24ce8c5a07b..d8658f4ce49 100644 --- a/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md +++ b/docs/release-notes/.FSharp.Compiler.Service/9.0.100.md @@ -18,6 +18,7 @@ * Fix IsUnionCaseTester throwing for non-methods/properties [#17301](https://github.com/dotnet/fsharp/pull/17634) * Consider `open type` used when the type is an enum and any of the enum cases is used unqualified. ([PR #17628](https://github.com/dotnet/fsharp/pull/17628)) * Guard for possible StackOverflowException when typechecking non recursive modules and namespaces ([PR #17654](https://github.com/dotnet/fsharp/pull/17654)) +* Fixes for the optimization of simple mappings in array and list comprehensions. ([Issue #17708](https://github.com/dotnet/fsharp/issues/17708), [PR #17711](https://github.com/dotnet/fsharp/pull/17711)) ### Added diff --git a/src/Compiler/Optimize/LowerComputedCollections.fs b/src/Compiler/Optimize/LowerComputedCollections.fs index 98b01086cc6..98101af36fe 100644 --- a/src/Compiler/Optimize/LowerComputedCollections.fs +++ b/src/Compiler/Optimize/LowerComputedCollections.fs @@ -373,60 +373,70 @@ module Array = else any ilTy /// Makes the equivalent of an inlined call to Array.map. - let mkMap g m (mBody, _spFor, _spIn, mFor, mIn, spInWhile) srcArray srcIlTy destIlTy overallElemTy loopVal body = - let len = mkLdlen g mIn srcArray - let arrayTy = mkArrayType g overallElemTy - - /// (# "newarr !0" type ('T) count : 'T array #) - let array = - mkAsmExpr - ( - [I_newarr (ILArrayShape.SingleDimensional, destIlTy)], - [], - [len], - [arrayTy], - m - ) - - let ldelem = mkIlInstr g I_ldelem (fun ilTy -> I_ldelem_any (ILArrayShape.SingleDimensional, ilTy)) srcIlTy - let stelem = mkIlInstr g I_stelem (fun ilTy -> I_stelem_any (ILArrayShape.SingleDimensional, ilTy)) destIlTy - - let mapping = - mkCompGenLetIn m (nameof array) arrayTy array (fun (_, array) -> - mkCompGenLetMutableIn mFor "i" g.int32_ty (mkTypedZero g mIn g.int32_ty) (fun (iVal, i) -> - let body = - // Rebind the loop val to pull directly from the source array. - let body = mkInvisibleLet mBody loopVal (mkAsmExpr ([ldelem], [], [srcArray; i], [loopVal.val_type], mBody)) body - - // destArray[i] <- body srcArray[i] - let setArrSubI = mkAsmExpr ([stelem], [], [array; i; body], [], mIn) - - // i <- i + 1 - let incrI = mkValSet mIn (mkLocalValRef iVal) (mkAsmExpr ([AI_add], [], [i; mkTypedOne g mIn g.int32_ty], [g.int32_ty], mIn)) - - mkSequential mIn setArrSubI incrI - - let guard = mkILAsmClt g mFor i (mkLdlen g mFor array) + let mkMap g m (mBody, _spFor, _spIn, mFor, mIn, spInWhile) srcArray srcIlTy destIlTy overallElemTy (loopVal: Val) body = + mkCompGenLetIn m (nameof srcArray) (tyOfExpr g srcArray) srcArray (fun (_, srcArray) -> + let len = mkLdlen g mIn srcArray + let arrayTy = mkArrayType g overallElemTy + + /// (# "newarr !0" type ('T) count : 'T array #) + let array = + mkAsmExpr + ( + [I_newarr (ILArrayShape.SingleDimensional, destIlTy)], + [], + [len], + [arrayTy], + m + ) - let loop = - mkWhile - g - ( - spInWhile, - NoSpecialWhileLoopMarker, - guard, - body, - mIn - ) + let ldelem = mkIlInstr g I_ldelem (fun ilTy -> I_ldelem_any (ILArrayShape.SingleDimensional, ilTy)) srcIlTy + let stelem = mkIlInstr g I_stelem (fun ilTy -> I_stelem_any (ILArrayShape.SingleDimensional, ilTy)) destIlTy - // while i < array.Length do
done - // array - mkSequential m loop array + let mapping = + mkCompGenLetIn m (nameof array) arrayTy array (fun (_, array) -> + mkCompGenLetMutableIn mFor "i" g.int32_ty (mkTypedZero g mIn g.int32_ty) (fun (iVal, i) -> + let body = + // If the loop val is used in the loop body, + // rebind it to pull directly from the source array. + // Otherwise, don't bother reading from the source array at all. + let body = + let freeLocals = (freeInExpr CollectLocals body).FreeLocals + + if freeLocals.Contains loopVal then + mkInvisibleLet mBody loopVal (mkAsmExpr ([ldelem], [], [srcArray; i], [loopVal.val_type], mBody)) body + else + body + + // destArray[i] <- body srcArray[i] + let setArrSubI = mkAsmExpr ([stelem], [], [array; i; body], [], mIn) + + // i <- i + 1 + let incrI = mkValSet mIn (mkLocalValRef iVal) (mkAsmExpr ([AI_add], [], [i; mkTypedOne g mIn g.int32_ty], [g.int32_ty], mIn)) + + mkSequential mIn setArrSubI incrI + + let guard = mkILAsmClt g mFor i (mkLdlen g mFor array) + + let loop = + mkWhile + g + ( + spInWhile, + NoSpecialWhileLoopMarker, + guard, + body, + mIn + ) + + // while i < array.Length do done + // array + mkSequential m loop array + ) ) - ) - // Add a debug point at the `for`, before anything gets evaluated. - Expr.DebugPoint (DebugPointAtLeafExpr.Yes mFor, mapping) + // Add a debug point at the `for`, before anything gets evaluated. + Expr.DebugPoint (DebugPointAtLeafExpr.Yes mFor, mapping) + ) /// Whether to check for overflow when converting a value to a native int. [