From b1dbbb964a4daba07fe13c4150133f1d5d41ceb0 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:54:40 -0700 Subject: [PATCH 1/7] [release/8.0-rc2] [browser] Fix SIMD+EH check (#92439) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Move simd+eh check after emcripten provided build time values * Call cwraps.mono_wasm_abort from runtimeHelpers.abort when cwraps are ready (onRuntimeInitializedAsync) * Assign early mono_wasm_exit and abort even earlier * Fire feature check before awaiting wasm download, but await it after. * Whitespaces --------- Co-authored-by: Marek FiĊĦera --- src/mono/wasm/runtime/globals.ts | 6 +++++ src/mono/wasm/runtime/startup.ts | 38 ++++++++++++++++++-------------- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/mono/wasm/runtime/globals.ts b/src/mono/wasm/runtime/globals.ts index 66948642bbc55c..88be75543ab4e5 100644 --- a/src/mono/wasm/runtime/globals.ts +++ b/src/mono/wasm/runtime/globals.ts @@ -66,6 +66,12 @@ export function setRuntimeGlobals(globalObjects: GlobalObjects) { beforeOnRuntimeInitialized: createPromiseController(), afterOnRuntimeInitialized: createPromiseController(), afterPostRun: createPromiseController(), + mono_wasm_exit: () => { + throw new Error("Mono shutdown"); + }, + abort: (reason: any) => { + throw reason; + } }); Object.assign(globalObjects.module.config!, {}) as any; diff --git a/src/mono/wasm/runtime/startup.ts b/src/mono/wasm/runtime/startup.ts index e6f8d80ef82f48..d8eab74d8b2500 100644 --- a/src/mono/wasm/runtime/startup.ts +++ b/src/mono/wasm/runtime/startup.ts @@ -40,15 +40,7 @@ import { assertNoProxies } from "./gc-handles"; const MONO_PTHREAD_POOL_SIZE = 4; export async function configureRuntimeStartup(): Promise { - if (linkerWasmEnableSIMD) { - mono_assert(await loaderHelpers.simd(), "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); - } - if (linkerWasmEnableEH) { - mono_assert(await loaderHelpers.exceptions(), "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); - } - await init_polyfills_async(); - await checkMemorySnapshotSize(); } @@ -240,6 +232,15 @@ async function onRuntimeInitializedAsync(userOnRuntimeInitialized: () => void) { // wait for previous stage await runtimeHelpers.afterPreRun.promise; mono_log_debug("onRuntimeInitialized"); + + runtimeHelpers.mono_wasm_exit = cwraps.mono_wasm_exit; + runtimeHelpers.abort = (reason: any) => { + if (!loaderHelpers.is_exited()) { + cwraps.mono_wasm_abort(); + } + throw reason; + }; + const mark = startMeasure(); // signal this stage, this will allow pending assets to allocate memory runtimeHelpers.beforeOnRuntimeInitialized.promise_control.resolve(); @@ -361,13 +362,6 @@ function mono_wasm_pre_init_essential(isWorker: boolean): void { } init_c_exports(); - runtimeHelpers.mono_wasm_exit = cwraps.mono_wasm_exit; - runtimeHelpers.abort = (reason: any) => { - if (!loaderHelpers.is_exited()) { - cwraps.mono_wasm_abort(); - } - throw reason; - }; cwraps_internal(INTERNAL); if (WasmEnableLegacyJsInterop && !linkerDisableLegacyJsInterop) { cwraps_mono_api(MONO); @@ -386,7 +380,6 @@ async function mono_wasm_pre_init_essential_async(): Promise { mono_log_debug("mono_wasm_pre_init_essential_async"); Module.addRunDependency("mono_wasm_pre_init_essential_async"); - if (MonoWasmThreads) { preAllocatePThreadWorkerPool(MONO_PTHREAD_POOL_SIZE, runtimeHelpers.config); } @@ -466,8 +459,12 @@ async function instantiate_wasm_module( await runtimeHelpers.beforePreInit.promise; Module.addRunDependency("instantiate_wasm_module"); + const wasmFeaturePromise = ensureUsedWasmFeatures(); + replace_linker_placeholders(imports); const assetToLoad = await loaderHelpers.wasmDownloadPromise.promise; + + await wasmFeaturePromise; await instantiate_wasm_asset(assetToLoad, imports, successCallback); assetToLoad.pendingDownloadInternal = null as any; // GC assetToLoad.pendingDownload = null as any; // GC @@ -499,6 +496,15 @@ async function instantiate_wasm_module( Module.removeRunDependency("instantiate_wasm_module"); } +async function ensureUsedWasmFeatures() { + if (linkerWasmEnableSIMD) { + mono_assert(await loaderHelpers.simd(), "This browser/engine doesn't support WASM SIMD. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); + } + if (linkerWasmEnableEH) { + mono_assert(await loaderHelpers.exceptions(), "This browser/engine doesn't support WASM exception handling. Please use a modern version. See also https://aka.ms/dotnet-wasm-features"); + } +} + async function mono_wasm_before_memory_snapshot() { const mark = startMeasure(); if (runtimeHelpers.loadedMemorySnapshotSize) { From 63d0c6454c7f9530140bc7aa9d97790996f21d08 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 18:58:50 -0700 Subject: [PATCH 2/7] [release/8.0-rc2] Address feedback and fix some TensorPrimitives issues (#92437) * Address feedback and fix some TensorPrimitives issues - Added a few APIs based on initial feedback: Abs (vectorized), Log2, and element-wise Max/Min{Magnitude} - Renamed L2Normalize to Norm - Fixed semantics of Min/MaxMagnitude to return original value rather than the absolute value - Renamed a few helper types for consistency - Added tests * Add a few more uses of Tolerance --------- Co-authored-by: Stephen Toub --- .../ref/System.Numerics.Tensors.cs | 8 +- .../Numerics/Tensors/TensorPrimitives.cs | 147 ++++++++- .../Tensors/TensorPrimitives.netcore.cs | 12 +- .../Tensors/TensorPrimitives.netstandard.cs | 12 +- .../tests/TensorPrimitivesTests.cs | 285 +++++++++++++++--- 5 files changed, 410 insertions(+), 54 deletions(-) diff --git a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs index 50eaa00160e5cb..99bd4703574e55 100644 --- a/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs +++ b/src/libraries/System.Numerics.Tensors/ref/System.Numerics.Tensors.cs @@ -8,6 +8,7 @@ namespace System.Numerics.Tensors { public static partial class TensorPrimitives { + public static void Abs(System.ReadOnlySpan x, System.Span destination) { } public static void Add(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { } public static void Add(System.ReadOnlySpan x, float y, System.Span destination) { } public static void AddMultiply(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan multiplier, System.Span destination) { } @@ -24,12 +25,17 @@ public static void Exp(System.ReadOnlySpan x, System.Span destinat public static int IndexOfMaxMagnitude(System.ReadOnlySpan x) { throw null; } public static int IndexOfMin(System.ReadOnlySpan x) { throw null; } public static int IndexOfMinMagnitude(System.ReadOnlySpan x) { throw null; } - public static float L2Normalize(System.ReadOnlySpan x) { throw null; } + public static float Norm(System.ReadOnlySpan x) { throw null; } public static void Log(System.ReadOnlySpan x, System.Span destination) { } + public static void Log2(System.ReadOnlySpan x, System.Span destination) { } public static float Max(System.ReadOnlySpan x) { throw null; } + public static void Max(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { throw null; } public static float MaxMagnitude(System.ReadOnlySpan x) { throw null; } + public static void MaxMagnitude(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { throw null; } public static float Min(System.ReadOnlySpan x) { throw null; } + public static void Min(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { throw null; } public static float MinMagnitude(System.ReadOnlySpan x) { throw null; } + public static void MinMagnitude(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { throw null; } public static void Multiply(System.ReadOnlySpan x, System.ReadOnlySpan y, System.Span destination) { } public static void Multiply(System.ReadOnlySpan x, float y, System.Span destination) { } public static void MultiplyAdd(System.ReadOnlySpan x, System.ReadOnlySpan y, System.ReadOnlySpan addend, System.Span destination) { } diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs index a400095b08ad39..d28d4bacafdb8e 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.cs @@ -93,6 +93,14 @@ public static void Divide(ReadOnlySpan x, float y, Span destinatio public static void Negate(ReadOnlySpan x, Span destination) => InvokeSpanIntoSpan(x, destination); + /// Computes the element-wise result of: MathF.Abs(). + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// This method effectively does [i] = MathF.Abs([i]). + public static void Abs(ReadOnlySpan x, Span destination) => + InvokeSpanIntoSpan(x, destination); + /// Computes the element-wise result of: ( + ) * . /// The first tensor, represented as a span. /// The second tensor, represented as a span. @@ -200,6 +208,24 @@ public static void Log(ReadOnlySpan x, Span destination) } } + /// Computes the element-wise result of: log2(). + /// The tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Destination is too short. + /// This method effectively does [i] = .Log2([i]). + public static void Log2(ReadOnlySpan x, Span destination) + { + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + for (int i = 0; i < x.Length; i++) + { + destination[i] = Log2(x[i]); + } + } + /// Computes the element-wise result of: cosh(). /// The tensor, represented as a span. /// The destination tensor, represented as a span. @@ -318,9 +344,9 @@ public static float Dot(ReadOnlySpan x, ReadOnlySpan y) // BLAS1: /// /// The first tensor, represented as a span. /// The L2 norm. - public static float L2Normalize(ReadOnlySpan x) // BLAS1: nrm2 + public static float Norm(ReadOnlySpan x) // BLAS1: nrm2 { - return MathF.Sqrt(Aggregate(0f, x)); + return MathF.Sqrt(Aggregate(0f, x)); } /// @@ -345,7 +371,7 @@ public static void SoftMax(ReadOnlySpan x, Span destination) for (int i = 0; i < x.Length; i++) { - expSum += MathF.Pow((float)Math.E, x[i]); + expSum += MathF.Exp(x[i]); } for (int i = 0; i < x.Length; i++) @@ -421,6 +447,31 @@ public static float Max(ReadOnlySpan x) return result; } + /// Computes the element-wise result of: MathF.Max(, ). + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of '' must be same as length of ''. + /// Destination is too short. + /// This method effectively does [i] = MathF.Max([i], [i]). + public static void Max(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + if (x.Length != y.Length) + { + ThrowHelper.ThrowArgument_SpansMustHaveSameLength(); + } + + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + for (int i = 0; i < x.Length; i++) + { + destination[i] = MathF.Max(x[i], y[i]); + } + } + /// Computes the minimum element in . /// The tensor, represented as a span. /// The minimum element in . @@ -464,6 +515,31 @@ public static float Min(ReadOnlySpan x) return result; } + /// Computes the element-wise result of: MathF.Min(, ). + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of '' must be same as length of ''. + /// Destination is too short. + /// This method effectively does [i] = MathF.Min([i], [i]). + public static void Min(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + if (x.Length != y.Length) + { + ThrowHelper.ThrowArgument_SpansMustHaveSameLength(); + } + + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + for (int i = 0; i < x.Length; i++) + { + destination[i] = MathF.Min(x[i], y[i]); + } + } + /// Computes the maximum magnitude of any element in . /// The tensor, represented as a span. /// The maximum magnitude of any element in . @@ -508,7 +584,32 @@ public static float MaxMagnitude(ReadOnlySpan x) } } - return resultMag; + return result; + } + + /// Computes the element-wise result of: MathF.MaxMagnitude(, ). + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of '' must be same as length of ''. + /// Destination is too short. + /// This method effectively does [i] = MathF.MaxMagnitude([i], [i]). + public static void MaxMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + if (x.Length != y.Length) + { + ThrowHelper.ThrowArgument_SpansMustHaveSameLength(); + } + + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + for (int i = 0; i < x.Length; i++) + { + destination[i] = MaxMagnitude(x[i], y[i]); + } } /// Computes the minimum magnitude of any element in . @@ -522,6 +623,7 @@ public static float MinMagnitude(ReadOnlySpan x) ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); } + float result = float.PositiveInfinity; float resultMag = float.PositiveInfinity; for (int i = 0; i < x.Length; i++) @@ -543,16 +645,43 @@ public static float MinMagnitude(ReadOnlySpan x) if (currentMag < resultMag) { + result = current; resultMag = currentMag; } } else if (IsNegative(current)) { + result = current; resultMag = currentMag; } } - return resultMag; + return result; + } + + /// Computes the element-wise result of: MathF.MinMagnitude(, ). + /// The first tensor, represented as a span. + /// The second tensor, represented as a span. + /// The destination tensor, represented as a span. + /// Length of '' must be same as length of ''. + /// Destination is too short. + /// This method effectively does [i] = MathF.MinMagnitude([i], [i]). + public static void MinMagnitude(ReadOnlySpan x, ReadOnlySpan y, Span destination) + { + if (x.Length != y.Length) + { + ThrowHelper.ThrowArgument_SpansMustHaveSameLength(); + } + + if (x.Length > destination.Length) + { + ThrowHelper.ThrowArgument_DestinationTooShort(); + } + + for (int i = 0; i < x.Length; i++) + { + destination[i] = MinMagnitude(x[i], y[i]); + } } /// Computes the index of the maximum element in . @@ -744,14 +873,14 @@ public static unsafe int IndexOfMinMagnitude(ReadOnlySpan x) /// The tensor, represented as a span. /// The result of adding all elements in , or zero if is empty. public static float Sum(ReadOnlySpan x) => - Aggregate(0f, x); + Aggregate(0f, x); /// Computes the sum of the squares of every element in . /// The tensor, represented as a span. /// The result of adding every element in multiplied by itself, or zero if is empty. /// This method effectively does .Sum(.Multiply(, )). public static float SumOfSquares(ReadOnlySpan x) => - Aggregate(0f, x); + Aggregate(0f, x); /// Computes the sum of the absolute values of every element in . /// The tensor, represented as a span. @@ -761,7 +890,7 @@ public static float SumOfSquares(ReadOnlySpan x) => /// This method corresponds to the asum method defined by BLAS1. /// public static float SumOfMagnitudes(ReadOnlySpan x) => - Aggregate(0f, x); + Aggregate(0f, x); /// Computes the product of all elements in . /// The tensor, represented as a span. @@ -774,7 +903,7 @@ public static float Product(ReadOnlySpan x) ThrowHelper.ThrowArgument_SpansMustBeNonEmpty(); } - return Aggregate(1.0f, x); + return Aggregate(1.0f, x); } /// Computes the product of the element-wise result of: + . diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs index ae5af404ac1aff..8eb1769d5eaee8 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netcore.cs @@ -51,6 +51,12 @@ public static void ConvertToSingle(ReadOnlySpan source, Span destin private static bool IsNegative(float f) => float.IsNegative(f); + private static float MaxMagnitude(float x, float y) => MathF.MaxMagnitude(x, y); + + private static float MinMagnitude(float x, float y) => MathF.MinMagnitude(x, y); + + private static float Log2(float x) => MathF.Log2(x); + private static float CosineSimilarityCore(ReadOnlySpan x, ReadOnlySpan y) { // Compute the same as: @@ -1184,7 +1190,7 @@ public static float Invoke(Vector512 x) #endif } - private readonly struct LoadIdentity : IUnaryOperator + private readonly struct IdentityOperator : IUnaryOperator { public static float Invoke(float x) => x; public static Vector128 Invoke(Vector128 x) => x; @@ -1194,7 +1200,7 @@ public static float Invoke(Vector512 x) #endif } - private readonly struct LoadSquared : IUnaryOperator + private readonly struct SquaredOperator : IUnaryOperator { public static float Invoke(float x) => x * x; public static Vector128 Invoke(Vector128 x) => x * x; @@ -1204,7 +1210,7 @@ public static float Invoke(Vector512 x) #endif } - private readonly struct LoadAbsolute : IUnaryOperator + private readonly struct AbsoluteOperator : IUnaryOperator { public static float Invoke(float x) => MathF.Abs(x); diff --git a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs index ba3fc69bab527f..ed8b3aea0d560f 100644 --- a/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs +++ b/src/libraries/System.Numerics.Tensors/src/System/Numerics/Tensors/TensorPrimitives.netstandard.cs @@ -10,6 +10,12 @@ public static partial class TensorPrimitives { private static unsafe bool IsNegative(float f) => *(int*)&f < 0; + private static float MaxMagnitude(float x, float y) => MathF.Abs(x) >= MathF.Abs(y) ? x : y; + + private static float MinMagnitude(float x, float y) => MathF.Abs(x) < MathF.Abs(y) ? x : y; + + private static float Log2(float x) => MathF.Log(x, 2); + private static float CosineSimilarityCore(ReadOnlySpan x, ReadOnlySpan y) { // Compute the same as: @@ -551,19 +557,19 @@ public Vector Invoke(Vector x, Vector y) public Vector Invoke(Vector x, Vector y, Vector z) => (x * y) + z; } - private readonly struct LoadIdentity : IUnaryOperator + private readonly struct IdentityOperator : IUnaryOperator { public float Invoke(float x) => x; public Vector Invoke(Vector x) => x; } - private readonly struct LoadSquared : IUnaryOperator + private readonly struct SquaredOperator : IUnaryOperator { public float Invoke(float x) => x * x; public Vector Invoke(Vector x) => x * x; } - private readonly struct LoadAbsolute : IUnaryOperator + private readonly struct AbsoluteOperator : IUnaryOperator { public float Invoke(float x) => MathF.Abs(x); diff --git a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs index 181d152e6ae979..777ab49609856e 100644 --- a/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs +++ b/src/libraries/System.Numerics.Tensors/tests/TensorPrimitivesTests.cs @@ -15,6 +15,9 @@ public static partial class TensorPrimitivesTests { private const double Tolerance = 0.0001; + public static IEnumerable TensorLengthsIncluding0 => + TensorLengths.Concat(new object[][] { [0] }); + public static IEnumerable TensorLengths => from length in Enumerable.Range(1, 128) select new object[] { length }; @@ -45,7 +48,7 @@ private static float NextSingle() } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void AddTwoTensors(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -83,7 +86,7 @@ public static void AddTwoTensors_ThrowsForTooShortDestination(int tensorLength) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void AddTensorAndScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -110,7 +113,7 @@ public static void AddTensorAndScalar_ThrowsForTooShortDestination(int tensorLen } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void SubtractTwoTensors(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -148,7 +151,7 @@ public static void SubtractTwoTensors_ThrowsForTooShortDestination(int tensorLen } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void SubtractTensorAndScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -175,7 +178,7 @@ public static void SubtractTensorAndScalar_ThrowsForTooShortDestination(int tens } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void MultiplyTwoTensors(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -213,7 +216,7 @@ public static void MultiplyTwoTensors_ThrowsForTooShortDestination(int tensorLen } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void MultiplyTensorAndScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -240,7 +243,7 @@ public static void MultiplyTensorAndScalar_ThrowsForTooShortDestination(int tens } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void DivideTwoTensors(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -278,7 +281,7 @@ public static void DivideTwoTensors_ThrowsForTooShortDestination(int tensorLengt } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void DivideTensorAndScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -305,7 +308,7 @@ public static void DivideTensorAndScalar_ThrowsForTooShortDestination(int tensor } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void NegateTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -330,7 +333,7 @@ public static void NegateTensor_ThrowsForTooShortDestination(int tensorLength) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void AddTwoTensorsAndMultiplyWithThirdTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -383,7 +386,7 @@ public static void AddTwoTensorsAndMultiplyWithThirdTensor_ThrowsForTooShortDest } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void AddTwoTensorsAndMultiplyWithScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -424,7 +427,7 @@ public static void AddTwoTensorsAndMultiplyWithScalar_ThrowsForTooShortDestinati } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void AddTensorAndScalarAndMultiplyWithTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -465,7 +468,7 @@ public static void AddTensorAndScalarAndMultiplyWithTensor_ThrowsForTooShortDest } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void MultiplyTwoTensorsAndAddWithThirdTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -518,7 +521,7 @@ public static void MultiplyTwoTensorsAndAddWithThirdTensor_ThrowsForTooShortDest } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void MultiplyTwoTensorsAndAddWithScalar(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -547,7 +550,7 @@ public static void MultiplyTwoTensorsAndAddWithScalar_ThrowsForTooShortDestinati } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void MultiplyTensorAndScalarAndAddWithTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -576,7 +579,7 @@ public static void MultiplyTensorAndScalarAndAddWithTensor_ThrowsForTooShortDest } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void ExpTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -601,7 +604,7 @@ public static void ExpTensor_ThrowsForTooShortDestination(int tensorLength) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void LogTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -625,8 +628,33 @@ public static void LogTensor_ThrowsForTooShortDestination(int tensorLength) AssertExtensions.Throws("destination", () => TensorPrimitives.Log(x, destination)); } + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void Log2(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.Log2(x, destination); + + for (int i = 0; i < tensorLength; i++) + { + Assert.Equal(MathF.Log(x[i], 2), destination[i], Tolerance); + } + } + [Theory] [MemberData(nameof(TensorLengths))] + public static void Log2_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.Log2(x, destination)); + } + + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] public static void CoshTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -651,7 +679,7 @@ public static void CoshTensor_ThrowsForTooShortDestination(int tensorLength) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void SinhTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -676,7 +704,7 @@ public static void SinhTensor_ThrowsForTooShortDestination(int tensorLength) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void TanhTensor(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -809,7 +837,7 @@ public static void Dot_KnownValues(float[] x, float[] y, float expectedResult) } [Theory] - [MemberData(nameof(TensorLengths))] + [MemberData(nameof(TensorLengthsIncluding0))] public static void Dot(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -830,14 +858,14 @@ public static void Dot(int tensorLength) [InlineData(new float[] { 3 }, 3)] [InlineData(new float[] { 3, 4, 1, 2 }, 5.477226)] [InlineData(new float[] { }, 0f)] - public static void L2Normalize_KnownValues(float[] x, float expectedResult) + public static void Norm_KnownValues(float[] x, float expectedResult) { - Assert.Equal(expectedResult, TensorPrimitives.L2Normalize(x), Tolerance); + Assert.Equal(expectedResult, TensorPrimitives.Norm(x), Tolerance); } [Theory] - [MemberData(nameof(TensorLengths))] - public static void L2Normalize(int tensorLength) + [MemberData(nameof(TensorLengthsIncluding0))] + public static void Norm(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); @@ -847,7 +875,7 @@ public static void L2Normalize(int tensorLength) sumOfSquares += x[i] * x[i]; } - Assert.Equal(Math.Sqrt(sumOfSquares), TensorPrimitives.L2Normalize(x), Tolerance); + Assert.Equal(Math.Sqrt(sumOfSquares), TensorPrimitives.Norm(x), Tolerance); } [Theory] @@ -1160,6 +1188,44 @@ public static void Max_Negative0LesserThanPositive0() Assert.Equal(1, TensorPrimitives.Max([-1, -0f, 1])); } + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void Max_TwoTensors(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.Max(x, y, destination); + + for (int i = 0; i < tensorLength; i++) + { + Assert.Equal(MathF.Max(x[i], y[i]), destination[i], Tolerance); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void Max_TwoTensors_ThrowsForMismatchedLengths(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => TensorPrimitives.Max(x, y, destination)); + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void Max_TwoTensors_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.Max(x, y, destination)); + } + [Fact] public static void MaxMagnitude_ThrowsForEmpty() { @@ -1172,14 +1238,16 @@ public static void MaxMagnitude(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); - Assert.Equal(Enumerable.Max(MemoryMarshal.ToEnumerable(x.Memory), MathF.Abs), TensorPrimitives.MaxMagnitude(x)); - - float max = 0; - foreach (float f in x.Span) + int index = 0; + for (int i = 0; i < x.Length; i++) { - max = Math.Max(max, MathF.Abs(f)); + if (MathF.Abs(x[i]) >= MathF.Abs(x[index])) + { + index = i; + } } - Assert.Equal(max, TensorPrimitives.MaxMagnitude(x)); + + Assert.Equal(x[index], TensorPrimitives.MaxMagnitude(x), Tolerance); } [Theory] @@ -1199,12 +1267,50 @@ public static void MaxMagnitude_Negative0LesserThanPositive0() { Assert.Equal(+0f, TensorPrimitives.MaxMagnitude([-0f, +0f])); Assert.Equal(+0f, TensorPrimitives.MaxMagnitude([+0f, -0f])); - Assert.Equal(1, TensorPrimitives.MaxMagnitude([-1, -0f])); + Assert.Equal(-1, TensorPrimitives.MaxMagnitude([-1, -0f])); Assert.Equal(1, TensorPrimitives.MaxMagnitude([-1, -0f, 1])); Assert.Equal(0f, TensorPrimitives.MaxMagnitude([-0f, -0f, -0f, -0f, -0f, 0f])); Assert.Equal(1, TensorPrimitives.MaxMagnitude([-0f, -0f, -0f, -0f, -1, -0f, 0f, 1])); } + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void MaxMagnitude_TwoTensors(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.MaxMagnitude(x, y, destination); + + for (int i = 0; i < tensorLength; i++) + { + Assert.Equal(MathF.Abs(x[i]) >= MathF.Abs(y[i]) ? x[i] : y[i], destination[i], Tolerance); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void MaxMagnitude_TwoTensors_ThrowsForMismatchedLengths(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => TensorPrimitives.MaxMagnitude(x, y, destination)); + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void MaxMagnitude_TwoTensors_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.MaxMagnitude(x, y, destination)); + } + [Fact] public static void Min_ThrowsForEmpty() { @@ -1248,6 +1354,44 @@ public static void Min_Negative0LesserThanPositive0() Assert.Equal(-1, TensorPrimitives.Min([-1, -0f, 1])); } + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void Min_TwoTensors(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.Min(x, y, destination); + + for (int i = 0; i < tensorLength; i++) + { + Assert.Equal(MathF.Min(x[i], y[i]), destination[i], Tolerance); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void Min_TwoTensors_ThrowsForMismatchedLengths(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => TensorPrimitives.Min(x, y, destination)); + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void Min_TwoTensors_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.Min(x, y, destination)); + } + [Fact] public static void MinMagnitude_ThrowsForEmpty() { @@ -1260,14 +1404,16 @@ public static void MinMagnitude(int tensorLength) { using BoundedMemory x = CreateAndFillTensor(tensorLength); - Assert.Equal(Enumerable.Min(MemoryMarshal.ToEnumerable(x.Memory), MathF.Abs), TensorPrimitives.MinMagnitude(x)); - - float min = float.PositiveInfinity; - foreach (float f in x.Span) + int index = 0; + for (int i = 0; i < x.Length; i++) { - min = Math.Min(min, MathF.Abs(f)); + if (MathF.Abs(x[i]) < MathF.Abs(x[index])) + { + index = i; + } } - Assert.Equal(min, TensorPrimitives.MinMagnitude(x)); + + Assert.Equal(x[index], TensorPrimitives.MinMagnitude(x), Tolerance); } [Theory] @@ -1291,6 +1437,44 @@ public static void MinMagnitude_Negative0LesserThanPositive0() Assert.Equal(0, TensorPrimitives.MinMagnitude([-1, -0f, 1])); } + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void MinMagnitude_TwoTensors(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.MinMagnitude(x, y, destination); + + for (int i = 0; i < tensorLength; i++) + { + Assert.Equal(MathF.Abs(x[i]) < MathF.Abs(y[i]) ? x[i] : y[i], destination[i], Tolerance); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void MinMagnitude_TwoTensors_ThrowsForMismatchedLengths(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength - 1); + using BoundedMemory destination = CreateTensor(tensorLength); + + Assert.Throws(() => TensorPrimitives.MinMagnitude(x, y, destination)); + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void MinMagnitude_TwoTensors_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory y = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.MinMagnitude(x, y, destination)); + } + [Fact] public static void Product_ThrowsForEmpty() { @@ -1480,5 +1664,30 @@ public static void SumOfMagnitudes_KnownValues() Assert.Equal(6, TensorPrimitives.SumOfMagnitudes([-3, 0, 3])); Assert.Equal(float.NaN, TensorPrimitives.SumOfMagnitudes([-3, float.NaN, 3])); } + + [Theory] + [MemberData(nameof(TensorLengthsIncluding0))] + public static void Abs(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength); + + TensorPrimitives.Abs(x, destination); + + for (int i = 0; i < x.Length; i++) + { + Assert.Equal(MathF.Abs(x[i]), destination[i], Tolerance); + } + } + + [Theory] + [MemberData(nameof(TensorLengths))] + public static void Abs_ThrowsForTooShortDestination(int tensorLength) + { + using BoundedMemory x = CreateAndFillTensor(tensorLength); + using BoundedMemory destination = CreateTensor(tensorLength - 1); + + AssertExtensions.Throws("destination", () => TensorPrimitives.Abs(x, destination)); + } } } From 40ccd8b848225e001d3066a898da3bbca734e320 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Thu, 21 Sep 2023 20:20:42 -0700 Subject: [PATCH 3/7] [release/8.0-rc2] Make HostModel PEUtils always read/write little endian (#92441) * Make HostModel PEUtils always read/write little endian * PR feeback - helper methods --------- Co-authored-by: Elinor Fung --- .../AppHost/PEUtils.cs | 121 ++++++------------ .../AppHostUpdateTests.cs | 6 +- 2 files changed, 44 insertions(+), 83 deletions(-) diff --git a/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs b/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs index 0d0b33ed55569e..1bfc80fcfca492 100644 --- a/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs +++ b/src/installer/managed/Microsoft.NET.HostModel/AppHost/PEUtils.cs @@ -1,8 +1,11 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System; +using System.Buffers.Binary; using System.IO; using System.IO.MemoryMappedFiles; +using System.Reflection.PortableExecutable; namespace Microsoft.NET.HostModel.AppHost { @@ -15,29 +18,13 @@ public static class PEUtils /// true if the accessor represents a PE image, false otherwise. internal static unsafe bool IsPEImage(MemoryMappedViewAccessor accessor) { - byte* pointer = null; + if (accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint)) + return false; - try - { - accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); - byte* bytes = pointer + accessor.PointerOffset; - - // https://en.wikipedia.org/wiki/Portable_Executable - // Validate that we're looking at Windows PE file - if (((ushort*)bytes)[0] != PEOffsets.DosImageSignature - || accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint)) - { - return false; - } - return true; - } - finally - { - if (pointer != null) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - } - } + // https://en.wikipedia.org/wiki/Portable_Executable + // Validate that we're looking at Windows PE file + ushort signature = AsLittleEndian(accessor.ReadUInt16(0)); + return signature == PEOffsets.DosImageSignature; } public static bool IsPEImage(string filePath) @@ -60,40 +47,15 @@ public static bool IsPEImage(string filePath) /// The memory accessor which has the apphost file opened. internal static unsafe void SetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor) { - byte* pointer = null; - - try - { - accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); - byte* bytes = pointer + accessor.PointerOffset; - - // https://en.wikipedia.org/wiki/Portable_Executable - uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0]; - - if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort)) - { - throw new AppHostNotPEFileException("Subsystem offset out of file range."); - } - - ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem)); - - // https://docs.microsoft.com/en-us/windows/desktop/Debug/pe-format#windows-subsystem - // The subsystem of the prebuilt apphost should be set to CUI - if (subsystem[0] != (ushort)PEOffsets.Subsystem.WindowsCui) - { - throw new AppHostNotCUIException(subsystem[0]); - } - - // Set the subsystem to GUI - subsystem[0] = (ushort)PEOffsets.Subsystem.WindowsGui; - } - finally - { - if (pointer != null) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - } - } + // https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem + // The subsystem of the prebuilt apphost should be set to CUI + uint peHeaderOffset; + ushort subsystem = GetWindowsSubsystem(accessor, out peHeaderOffset); + if (subsystem != (ushort)Subsystem.WindowsCui) + throw new AppHostNotCUIException(subsystem); + + // Set the subsystem to GUI + accessor.Write(peHeaderOffset + PEOffsets.PEHeader.Subsystem, AsLittleEndian((ushort)Subsystem.WindowsGui)); } public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath) @@ -113,32 +75,7 @@ public static unsafe void SetWindowsGraphicalUserInterfaceBit(string filePath) /// The memory accessor which has the apphost file opened. internal static unsafe ushort GetWindowsGraphicalUserInterfaceBit(MemoryMappedViewAccessor accessor) { - byte* pointer = null; - - try - { - accessor.SafeMemoryMappedViewHandle.AcquirePointer(ref pointer); - byte* bytes = pointer + accessor.PointerOffset; - - // https://en.wikipedia.org/wiki/Portable_Executable - uint peHeaderOffset = ((uint*)(bytes + PEOffsets.DosStub.PESignatureOffset))[0]; - - if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort)) - { - throw new AppHostNotPEFileException("Subsystem offset out of file range."); - } - - ushort* subsystem = ((ushort*)(bytes + peHeaderOffset + PEOffsets.PEHeader.Subsystem)); - - return subsystem[0]; - } - finally - { - if (pointer != null) - { - accessor.SafeMemoryMappedViewHandle.ReleasePointer(); - } - } + return GetWindowsSubsystem(accessor, out _); } public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath) @@ -151,5 +88,25 @@ public static unsafe ushort GetWindowsGraphicalUserInterfaceBit(string filePath) } } } + + private static ushort GetWindowsSubsystem(MemoryMappedViewAccessor accessor, out uint peHeaderOffset) + { + // https://en.wikipedia.org/wiki/Portable_Executable + if (accessor.Capacity < PEOffsets.DosStub.PESignatureOffset + sizeof(uint)) + throw new AppHostNotPEFileException("PESignature offset out of file range."); + + peHeaderOffset = AsLittleEndian(accessor.ReadUInt32(PEOffsets.DosStub.PESignatureOffset)); + if (accessor.Capacity < peHeaderOffset + PEOffsets.PEHeader.Subsystem + sizeof(ushort)) + throw new AppHostNotPEFileException("Subsystem offset out of file range."); + + // https://learn.microsoft.com/windows/win32/debug/pe-format#windows-subsystem + return AsLittleEndian(accessor.ReadUInt16(peHeaderOffset + PEOffsets.PEHeader.Subsystem)); + } + + private static ushort AsLittleEndian(ushort value) + => BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); + + private static uint AsLittleEndian(uint value) + => BitConverter.IsLittleEndian ? value : BinaryPrimitives.ReverseEndianness(value); } } diff --git a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs index 232410be8f2596..b4d038b99b5ce3 100644 --- a/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs +++ b/src/installer/tests/Microsoft.NET.HostModel.Tests/Microsoft.NET.HostModel.AppHost.Tests/AppHostUpdateTests.cs @@ -11,6 +11,7 @@ using Microsoft.NET.HostModel.AppHost; using Microsoft.DotNet.CoreSetup.Test; using System.Diagnostics; +using System.Reflection.PortableExecutable; namespace Microsoft.NET.HostModel.Tests { @@ -111,7 +112,9 @@ public void ItCanSetWindowsGUISubsystem() BitConverter .ToUInt16(File.ReadAllBytes(destinationFilePath), SubsystemOffset) .Should() - .Be(2); + .Be((ushort)Subsystem.WindowsGui); + + Assert.Equal((ushort)Subsystem.WindowsGui, PEUtils.GetWindowsGraphicalUserInterfaceBit(destinationFilePath)); } } @@ -153,6 +156,7 @@ public void ItFailsToSetGUISubsystemWithWrongDefault() string destinationFilePath = Path.Combine(testDirectory.Path, "DestinationAppHost.exe.mock"); string appBinaryFilePath = "Test/App/Binary/Path.dll"; + Assert.Equal(42, PEUtils.GetWindowsGraphicalUserInterfaceBit(sourceAppHostMock)); Assert.Throws(() => HostWriter.CreateAppHost( sourceAppHostMock, From fed0f788e7c0e24a78e11d708279eaa242098a11 Mon Sep 17 00:00:00 2001 From: "dotnet-maestro[bot]" <42748379+dotnet-maestro[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 08:45:56 -0700 Subject: [PATCH 4/7] Update dependencies from https://github.com/dotnet/emsdk build 20230921.3 (#92454) Microsoft.NET.Workload.Emscripten.Current.Manifest-8.0.100.Transport From Version 8.0.0-rc.2.23469.4 -> To Version 8.0.0-rc.2.23471.3 Co-authored-by: dotnet-maestro[bot] --- eng/Version.Details.xml | 4 ++-- eng/Versions.props | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/eng/Version.Details.xml b/eng/Version.Details.xml index 9bedd07535a17d..2582a60b745fef 100644 --- a/eng/Version.Details.xml +++ b/eng/Version.Details.xml @@ -90,9 +90,9 @@ 89be445dd4936157533ad96bafb95f701430653a - + https://github.com/dotnet/emsdk - ea0e8e8214e9acc0cba7e78a836ed6656f788d11 + 190be5fe0709de5a3507448151c7fc84abff8628 diff --git a/eng/Versions.props b/eng/Versions.props index a7d36117e12b76..38c9e5fd4f1ab9 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -238,7 +238,7 @@ Note: when the name is updated, make sure to update dependency name in eng/pipelines/common/xplat-setup.yml like - DarcDependenciesChanged.Microsoft_NET_Workload_Emscripten_Current_Manifest-8_0_100_Transport --> - 8.0.0-rc.2.23469.4 + 8.0.0-rc.2.23471.3 $(MicrosoftNETWorkloadEmscriptenCurrentManifest80100TransportVersion) 1.1.87-gba258badda From 0717e52526d8a45907f07cf7860d1290f8064558 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:14:27 -0700 Subject: [PATCH 5/7] [release/8.0-rc2] Ensure that embedded broadcast checks the base type of the parent node (#92459) * Ensure that embedded broadcast checks the base type of the parent node * Ensure the regression test exits if AVX2 is not supported * Also handle embedded broadcasts for mismatched memory sizes --------- Co-authored-by: Tanner Gooding --- src/coreclr/jit/lowerxarch.cpp | 19 ++++++-- .../JitBlue/Runtime_92357/Runtime_92357.cs | 47 +++++++++++++++++++ .../Runtime_92357/Runtime_92357.csproj | 8 ++++ 3 files changed, 70 insertions(+), 4 deletions(-) create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.cs create mode 100644 src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.csproj diff --git a/src/coreclr/jit/lowerxarch.cpp b/src/coreclr/jit/lowerxarch.cpp index 30c2a832b94fc1..319238aaec628f 100644 --- a/src/coreclr/jit/lowerxarch.cpp +++ b/src/coreclr/jit/lowerxarch.cpp @@ -8202,10 +8202,12 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* parentNode, GenTre case NI_AVX2_BroadcastScalarToVector256: case NI_AVX512F_BroadcastScalarToVector512: { - var_types baseType = hwintrinsic->GetSimdBaseType(); - if (varTypeIsSmall(baseType)) + var_types parentBaseType = parentNode->GetSimdBaseType(); + var_types childBaseType = hwintrinsic->GetSimdBaseType(); + + if (varTypeIsSmall(parentBaseType) || (genTypeSize(parentBaseType) != genTypeSize(childBaseType))) { - // early return if the base type is not embedded broadcast compatible. + // early return if either base type is not embedded broadcast compatible. return false; } @@ -8213,7 +8215,7 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* parentNode, GenTre if (intrinsicId == NI_SSE3_MoveAndDuplicate) { // NI_SSE3_MoveAndDuplicate is for Vector128 only. - assert(baseType == TYP_DOUBLE); + assert(childBaseType == TYP_DOUBLE); } if (comp->compOpportunisticallyDependsOn(InstructionSet_AVX512F_VL) && @@ -8246,6 +8248,15 @@ bool Lowering::IsContainableHWIntrinsicOp(GenTreeHWIntrinsic* parentNode, GenTre case NI_AVX_BroadcastScalarToVector128: case NI_AVX_BroadcastScalarToVector256: { + var_types parentBaseType = parentNode->GetSimdBaseType(); + var_types childBaseType = hwintrinsic->GetSimdBaseType(); + + if (varTypeIsSmall(parentBaseType) || (genTypeSize(parentBaseType) != genTypeSize(childBaseType))) + { + // early return if either base type is not embedded broadcast compatible. + return false; + } + return parentNode->OperIsEmbBroadcastCompatible(); } diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.cs b/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.cs new file mode 100644 index 00000000000000..4704441bacce6c --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.cs @@ -0,0 +1,47 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.Runtime.CompilerServices; +using System.Runtime.Intrinsics; +using System.Runtime.Intrinsics.X86; +using Xunit; + +public static class Runtime_92357 +{ + [Fact] + [MethodImpl(MethodImplOptions.AggressiveOptimization)] + public static void Problem() + { + if (!Avx2.IsSupported) + { + return; + } + + int y1 = 5; + + Vector256 actual1 = Test1(Vector256.Create((short)1), ref y1); + Vector256 expected1 = Vector256.Create(10, 0, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0, 10, 0); + + Assert.Equal(expected1, actual1); + + long y2 = 5; + + Vector256 actual2 = Test2(Vector256.Create((int)1), ref y2); + Vector256 expected2 = Vector256.Create(10, 0, 10, 0, 10, 0, 10, 0); + + Assert.Equal(expected2, actual2); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static Vector256 Test1(Vector256 x, ref int y) + { + return Avx2.MultiplyLow(x + x, Vector256.Create(y).AsInt16()); + } + + [MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.AggressiveOptimization)] + public static Vector256 Test2(Vector256 x, ref long y) + { + return Avx2.MultiplyLow(x + x, Vector256.Create(y).AsInt32()); + } +} diff --git a/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.csproj b/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.csproj new file mode 100644 index 00000000000000..15edd99711a1a4 --- /dev/null +++ b/src/tests/JIT/Regression/JitBlue/Runtime_92357/Runtime_92357.csproj @@ -0,0 +1,8 @@ + + + True + + + + + \ No newline at end of file From 873b3cca0a6ac461f1f6df70622e3b4703e6e493 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:16:16 -0700 Subject: [PATCH 6/7] [release/8.0-rc2] Do not call SignalSession on invalid session IDs (#92444) * Update EventPipeEventDispatcher.cs * Update EventPipeEventDispatcher.cs --------- Co-authored-by: David Mason --- .../Tracing/EventPipeEventDispatcher.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs index efd4b8cfb656dd..708c5afc1bc70e 100644 --- a/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs +++ b/src/libraries/System.Private.CoreLib/src/System/Diagnostics/Tracing/EventPipeEventDispatcher.cs @@ -142,12 +142,15 @@ private void SetStopDispatchTask() { Debug.Assert(Monitor.IsEntered(m_dispatchControlLock)); - if (m_dispatchTask != null) + if (m_dispatchTaskCancellationSource?.IsCancellationRequested ?? true) { - Debug.Assert(m_dispatchTaskCancellationSource != null); - m_dispatchTaskCancellationSource?.Cancel(); - EventPipeInternal.SignalSession(m_sessionID); + return; } + + Debug.Assert(m_sessionID != 0); + m_dispatchTaskCancellationSource.Cancel(); + EventPipeInternal.SignalSession(m_sessionID); + m_sessionID = 0; } private unsafe void DispatchEventsToEventListeners(ulong sessionID, DateTime syncTimeUtc, long syncTimeQPC, long timeQPCFrequency, Task? previousDispatchTask, CancellationToken token) @@ -187,8 +190,12 @@ private unsafe void DispatchEventsToEventListeners(ulong sessionID, DateTime syn } } - // Disable the old session. This can happen asynchronously since we aren't using the old session anymore - EventPipeInternal.Disable(sessionID); + lock (m_dispatchControlLock) + { + // Disable the old session. This can happen asynchronously since we aren't using the old session + // anymore. We take the lock to make sure we don't call SignalSession on an invalid session ID. + EventPipeInternal.Disable(sessionID); + } } /// From 974edf97294f3fb1b74cfddaf0275a657886224b Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Fri, 22 Sep 2023 10:25:30 -0700 Subject: [PATCH 7/7] [release/8.0-rc2] Ensure Bind can handle null from GetSection (#92477) * Ensure Bind can handle null from GetSection IConfiguration instances may return a null value from GetSection. We were not handling this and would throw a NullReferenceException. * Address feedback * Remove Moq from ConfigBinder tests --------- Co-authored-by: Eric StJohn --- .../src/ConfigurationBinder.cs | 5 +++ .../ConfigurationBinderTests.TestClasses.cs | 6 +++ .../tests/Common/ConfigurationBinderTests.cs | 38 ++++++++++++++++++- 3 files changed, 47 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs index 74365d8084aa3d..dfc35d80208553 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/src/ConfigurationBinder.cs @@ -298,6 +298,11 @@ private static void BindInstance( return; } + if (config is null) + { + return; + } + var section = config as IConfigurationSection; string? configValue = section?.Value; if (configValue != null && TryConvertValue(type, configValue, section?.Path, out object? convertedValue, out Exception? error)) diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs index f47cdbe6dbbb54..48474033ec1f8e 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.TestClasses.cs @@ -888,5 +888,11 @@ public int MyIntProperty } } + public class SimplePoco + { + public string A { get; set; } + public string B { get; set; } + } + } } diff --git a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs index 7c955e789184c8..7e635c1f0bdaa0 100644 --- a/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs +++ b/src/libraries/Microsoft.Extensions.Configuration.Binder/tests/Common/ConfigurationBinderTests.cs @@ -11,6 +11,7 @@ #if BUILDING_SOURCE_GENERATOR_TESTS using Microsoft.Extensions.Configuration; #endif +using Microsoft.Extensions.Configuration.Memory; using Microsoft.Extensions.Configuration.Test; using Xunit; @@ -1767,7 +1768,7 @@ public void EnsureCallingThePropertySetter() Assert.Equal(0, options.OtherCodeNullable); Assert.Equal("default", options.OtherCodeString); Assert.Null(options.OtherCodeNull); - Assert.Null(options.OtherCodeUri); + Assert.Null(options.OtherCodeUri); } [Fact] @@ -2238,7 +2239,7 @@ void TestUntypedOverloads(IConfiguration? configuration, string? key) Assert.Throws(() => configuration.GetValue(typeof(GeolocationClass), key, new GeolocationClass())); Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key)); Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, defaultValue: null)); - Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, default(Geolocation))); + Assert.Throws(() => configuration.GetValue(typeof(Geolocation), key, default(Geolocation))); } } @@ -2404,5 +2405,38 @@ public void SharedChildInstance() config.GetSection("A").Bind(instance); Assert.Equal("localhost", instance.ConnectionString); } + + [Fact] + public void CanBindToMockConfigurationSection() + { + const string expectedA = "hello"; + + var configSource = new MemoryConfigurationSource() + { + InitialData = new Dictionary() + { + [$":{nameof(SimplePoco.A)}"] = expectedA, + } + }; + var configRoot = new MockConfigurationRoot(new[] { configSource.Build(null) }); + var configSection = new ConfigurationSection(configRoot, string.Empty); + + SimplePoco result = new(); + configSection.Bind(result); + + Assert.Equal(expectedA, result.A); + Assert.Equal(default(string), result.B); + } + + // a mock configuration root that will return null for undefined Sections, + // as is common when Configuration interfaces are mocked + class MockConfigurationRoot : ConfigurationRoot, IConfigurationRoot + { + public MockConfigurationRoot(IList providers) : base(providers) + { } + + IConfigurationSection IConfiguration.GetSection(string key) => + this[key] is null ? null : new ConfigurationSection(this, key); + } } }