From f9afa99eeb8c9b27cb3b6c1ada286057a4cbf0ef Mon Sep 17 00:00:00 2001 From: Fei Peng Date: Thu, 13 Sep 2018 16:52:42 -0700 Subject: [PATCH 1/2] Fix inconsistent Intel hardware intrinsic APIs --- .../Runtime/Intrinsics/X86/Avx2.PlatformNotSupported.cs | 2 +- .../shared/System/Runtime/Intrinsics/X86/Avx2.cs | 2 +- .../Runtime/Intrinsics/X86/Popcnt.PlatformNotSupported.cs | 4 ++-- .../shared/System/Runtime/Intrinsics/X86/Popcnt.cs | 4 ++-- .../Runtime/Intrinsics/X86/Sse2.PlatformNotSupported.cs | 4 ++-- .../shared/System/Runtime/Intrinsics/X86/Sse2.cs | 4 ++-- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.PlatformNotSupported.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.PlatformNotSupported.cs index a4b5680c7e0c..79c30d37a285 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.PlatformNotSupported.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.PlatformNotSupported.cs @@ -1787,7 +1787,7 @@ internal Avx2() { } /// __m256i _mm256_sad_epu8 (__m256i a, __m256i b) /// VPSADBW ymm, ymm, ymm/m256 /// - public static Vector256 SumAbsoluteDifferences(Vector256 left, Vector256 right) { throw new PlatformNotSupportedException(); } + public static Vector256 SumAbsoluteDifferences(Vector256 left, Vector256 right) { throw new PlatformNotSupportedException(); } /// /// __m256i _mm256_unpackhi_epi8 (__m256i a, __m256i b) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.cs index 2e949dc83688..90c5b315b772 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Avx2.cs @@ -2513,7 +2513,7 @@ public static unsafe Vector256 GatherMaskVector256(Vector256 sou /// __m256i _mm256_sad_epu8 (__m256i a, __m256i b) /// VPSADBW ymm, ymm, ymm/m256 /// - public static Vector256 SumAbsoluteDifferences(Vector256 left, Vector256 right) => SumAbsoluteDifferences(left, right); + public static Vector256 SumAbsoluteDifferences(Vector256 left, Vector256 right) => SumAbsoluteDifferences(left, right); /// /// __m256i _mm256_unpackhi_epi8 (__m256i a, __m256i b) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.PlatformNotSupported.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.PlatformNotSupported.cs index f91b1aecfe0a..bf045bd1f771 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.PlatformNotSupported.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.PlatformNotSupported.cs @@ -20,11 +20,11 @@ internal Popcnt() { } /// int _mm_popcnt_u32 (unsigned int a) /// POPCNT reg, reg/m32 /// - public static int PopCount(uint value) { throw new PlatformNotSupportedException(); } + public static uint PopCount(uint value) { throw new PlatformNotSupportedException(); } /// /// __int64 _mm_popcnt_u64 (unsigned __int64 a) /// POPCNT reg64, reg/m64 /// - public static long PopCount(ulong value) { throw new PlatformNotSupportedException(); } + public static ulong PopCount(ulong value) { throw new PlatformNotSupportedException(); } } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.cs index 057140ed061a..ba73eb23407b 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Popcnt.cs @@ -21,11 +21,11 @@ internal Popcnt() { } /// int _mm_popcnt_u32 (unsigned int a) /// POPCNT reg, reg/m32 /// - public static int PopCount(uint value) => PopCount(value); + public static uint PopCount(uint value) => PopCount(value); /// /// __int64 _mm_popcnt_u64 (unsigned __int64 a) /// POPCNT reg, reg/m64 /// - public static long PopCount(ulong value) => PopCount(value); + public static ulong PopCount(ulong value) => PopCount(value); } } diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.PlatformNotSupported.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.PlatformNotSupported.cs index 8a706ec9b885..0f9b1959b5a0 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.PlatformNotSupported.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.PlatformNotSupported.cs @@ -893,7 +893,7 @@ internal Sse2() { } /// __m128i _mm_madd_epi16 (__m128i a, __m128i b) /// PMADDWD xmm, xmm/m128 /// - public static Vector128 MultiplyHorizontalAdd(Vector128 left, Vector128 right) { throw new PlatformNotSupportedException(); } + public static Vector128 MultiplyAddAdjacent(Vector128 left, Vector128 right) { throw new PlatformNotSupportedException(); } /// /// __m128i _mm_mullo_epi16 (__m128i a, __m128i b) @@ -1074,7 +1074,7 @@ internal Sse2() { } /// __m128i _mm_sad_epu8 (__m128i a, __m128i b) /// PSADBW xmm, xmm/m128 /// - public static Vector128 SumAbsoluteDifferences(Vector128 left, Vector128 right) { throw new PlatformNotSupportedException(); } + public static Vector128 SumAbsoluteDifferences(Vector128 left, Vector128 right) { throw new PlatformNotSupportedException(); } /// /// __m128i _mm_shuffle_epi32 (__m128i a, int immediate) diff --git a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.cs b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.cs index 1aab686bd2c5..0f8b9a8f5a55 100644 --- a/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.cs +++ b/src/System.Private.CoreLib/shared/System/Runtime/Intrinsics/X86/Sse2.cs @@ -894,7 +894,7 @@ internal Sse2() { } /// __m128i _mm_madd_epi16 (__m128i a, __m128i b) /// PMADDWD xmm, xmm/m128 /// - public static Vector128 MultiplyHorizontalAdd(Vector128 left, Vector128 right) => MultiplyHorizontalAdd(left, right); + public static Vector128 MultiplyAddAdjacent(Vector128 left, Vector128 right) => MultiplyAddAdjacent(left, right); /// /// __m128i _mm_mullo_epi16 (__m128i a, __m128i b) @@ -1313,7 +1313,7 @@ public static Vector128 SetZeroVector128() where T : struct /// __m128i _mm_sad_epu8 (__m128i a, __m128i b) /// PSADBW xmm, xmm/m128 /// - public static Vector128 SumAbsoluteDifferences(Vector128 left, Vector128 right) => SumAbsoluteDifferences(left, right); + public static Vector128 SumAbsoluteDifferences(Vector128 left, Vector128 right) => SumAbsoluteDifferences(left, right); /// /// __m128i _mm_shuffle_epi32 (__m128i a, int immediate) From e618d433f71075d0e00d707475ea02400cdff145 Mon Sep 17 00:00:00 2001 From: Fei Peng Date: Thu, 13 Sep 2018 16:53:15 -0700 Subject: [PATCH 2/2] temporarily disable some hw intrinsic tests --- .../HardwareIntrinsics/X86/Popcnt/Popcnt.cs | 131 ------------------ .../X86/Popcnt/Popcnt_r.csproj | 33 ----- .../X86/Popcnt/Popcnt_ro.csproj | 33 ----- .../X86/Sse2/MultiplyHorizontalAdd.cs | 56 -------- .../X86/Sse2/MultiplyHorizontalAdd_r.csproj | 37 ----- .../X86/Sse2/MultiplyHorizontalAdd_ro.csproj | 37 ----- .../X86/Sse2/SumAbsoluteDifferences.cs | 62 --------- .../X86/Sse2/SumAbsoluteDifferences_r.csproj | 38 ----- .../X86/Sse2/SumAbsoluteDifferences_ro.csproj | 37 ----- 9 files changed, 464 deletions(-) delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_r.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_ro.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_r.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_ro.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences.cs delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_r.csproj delete mode 100644 tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_ro.csproj diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt.cs b/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt.cs deleted file mode 100644 index fd2bdf57d706..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// - -using System; -using System.Reflection; -using System.Runtime.Intrinsics.X86; - -namespace IntelHardwareIntrinsicTest -{ - class Program - { - const int Pass = 100; - const int Fail = 0; - - static int Main(string[] args) - { - ulong sl = 0; - long resl; - int testResult = Pass; - - if (!Popcnt.IsSupported || !Environment.Is64BitProcess) - { - try - { - resl = Popcnt.PopCount(sl); - Console.WriteLine("Intrinsic Popcnt.PopCount is called on non-supported hardware"); - Console.WriteLine("Popcnt.IsSupported " + Popcnt.IsSupported); - Console.WriteLine("Environment.Is64BitProcess " + Environment.Is64BitProcess); - testResult = Fail; - } - catch (PlatformNotSupportedException) - { - } - - try - { - resl = Convert.ToInt64(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { sl.GetType() }).Invoke(null, new object[] { sl })); - Console.WriteLine("Intrinsic Popcnt.PopCount is called via reflection on non-supported hardware"); - Console.WriteLine("Popcnt.IsSupported " + Popcnt.IsSupported); - Console.WriteLine("Environment.Is64BitProcess " + Environment.Is64BitProcess); - testResult = Fail; - } - catch (TargetInvocationException e) when (e.InnerException is PlatformNotSupportedException) - { - } - } - - - if (Popcnt.IsSupported) - { - if (Environment.Is64BitProcess) - { - for (int i = 0; i < longPopcntTable.Length; i++) - { - sl = longPopcntTable[i].s; - - resl = Popcnt.PopCount(sl); - if (resl != longPopcntTable[i].res) - { - Console.WriteLine("{0}: Inputs: 0x{1,16:x} Expected: 0x{3,16:x} actual: 0x{4,16:x}", - i, sl, longPopcntTable[i].res, resl); - testResult = Fail; - } - - resl = Convert.ToInt64(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { sl.GetType() }).Invoke(null, new object[] { sl })); - if (resl != longPopcntTable[i].res) - { - Console.WriteLine("{0}: Inputs: 0x{1,16:x} Expected: 0x{3,16:x} actual: 0x{4,16:x} - Reflection", - i, sl, longPopcntTable[i].res, resl); - testResult = Fail; - } - } - } - - uint si; - int resi; - for (int i = 0; i < intPopcntTable.Length; i++) - { - si = intPopcntTable[i].s; - - resi = Popcnt.PopCount(si); - if (resi != intPopcntTable[i].res) - { - Console.WriteLine("{0}: Inputs: 0x{1,16:x} Expected: 0x{3,16:x} actual: 0x{4,16:x}", - i, si, intPopcntTable[i].res, resi); - testResult = Fail; - } - - resi = Convert.ToInt32(typeof(Popcnt).GetMethod(nameof(Popcnt.PopCount), new Type[] { si.GetType() }).Invoke(null, new object[] { si })); - if (resi != intPopcntTable[i].res) - { - Console.WriteLine("{0}: Inputs: 0x{1,16:x} Expected: 0x{3,16:x} actual: 0x{4,16:x} - Reflection", - i, si, intPopcntTable[i].res, resi); - testResult = Fail; - } - } - } - - return testResult; - } - - public struct POPCNT where T : struct where U : struct - { - public T s; - public U res; - public POPCNT(T a, U r) - { - this.s = a; - this.res = r; - } - } - - public static POPCNT[] longPopcntTable = { - new POPCNT(0x0000000000000000UL, 0), - new POPCNT(0x0000000000000001UL, 1), - new POPCNT(0xffffffffffffffffUL, 64), - new POPCNT(0x8000000000000000UL, 1), - new POPCNT(0x00050000000f423fUL, 14) - }; - - public static POPCNT[] intPopcntTable = { - new POPCNT(0x00000000U, 0), - new POPCNT(0x00000001U, 1), - new POPCNT(0xffffffffU, 32), - new POPCNT(0x80000000U, 1), - new POPCNT(0x0005423fU, 10) - }; - } -} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_r.csproj deleted file mode 100644 index e32ab7ae356e..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_r.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - - - - - - - False - - - - Embedded - - - - - - - - - - - diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_ro.csproj deleted file mode 100644 index a53bf1b55986..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Popcnt/Popcnt_ro.csproj +++ /dev/null @@ -1,33 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {95DFC527-4DC1-495E-97D7-E94EE1F7140D} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - - - - - - - False - - - - Embedded - True - - - - - - - - - - diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd.cs deleted file mode 100644 index b582b25e2839..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd.cs +++ /dev/null @@ -1,56 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// - -using System; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace IntelHardwareIntrinsicTest -{ - internal static partial class Program - { - const int Pass = 100; - const int Fail = 0; - - internal static unsafe int Main(string[] args) - { - int testResult = Pass; - int testsCount = 21; - string methodUnderTestName = nameof(Sse2.MultiplyHorizontalAdd); - - if (Sse2.IsSupported) - { - Console.WriteLine($"Test started"); - - using (var shortTable = TestTableSse2.Create(testsCount)) - { - for (int i = 0; i < testsCount; i++) - { - (Vector128, Vector128) value = shortTable[i]; - var result = Sse2.MultiplyHorizontalAdd(value.Item1, value.Item2); - shortTable.SetOutArrayU(result); - } - - CheckMethodThree checkInt16 = (short x1, short x2, short y1, short y2, int z, ref int a) => - (a = (int)x1 * y1 + (int)x2 * y2) == z; - - if (!shortTable.CheckMultiplyHorizontalAdd(checkInt16)) - { - PrintError(shortTable, methodUnderTestName, "(short x1, short x2, short y1, short y2, int z, ref int a) => (a = (int)x1 * y1 + (int)x2 * y2) == z", checkInt16); - testResult = Fail; - } - } - - Console.WriteLine($"Test finished with result: {testResult}"); - } - else - { - Console.WriteLine($"Sse2.IsSupported: {Sse2.IsSupported}, skipped tests of {typeof(Sse2)}.{methodUnderTestName}"); - } - - return testResult; - } - } -} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_r.csproj deleted file mode 100644 index b253bf8fdfa3..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_r.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {EEDE2DAC-02AD-4A7B-A1ED-9D91DBA53E41} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - - False - - - - Embedded - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_ro.csproj deleted file mode 100644 index 01acf0097a03..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/MultiplyHorizontalAdd_ro.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {7FA90B82-1C1C-4D39-8032-16C9EF595F54} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - - False - - - - Embedded - True - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences.cs b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences.cs deleted file mode 100644 index 8a696a1f9e7e..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences.cs +++ /dev/null @@ -1,62 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. -// See the LICENSE file in the project root for more information. -// - -using System; -using System.Linq; -using System.Runtime.Intrinsics; -using System.Runtime.Intrinsics.X86; - -namespace IntelHardwareIntrinsicTest -{ - internal static partial class Program - { - const int Pass = 100; - const int Fail = 0; - - static unsafe int Main(string[] args) - { - int testResult = Pass; - int testsCount = 21; - string methodUnderTestName = nameof(Sse2.SumAbsoluteDifferences); - - if (Sse2.IsSupported) - { - using (var byteTable = TestTableSse2.Create(testsCount, 8.0)) - { - for (int i = 0; i < testsCount; i++) - { - (Vector128, Vector128) value = byteTable[i]; - var result = Sse2.SumAbsoluteDifferences(value.Item1, value.Item2); - byteTable.SetOutArrayU(result); - } - - CheckMethodEightOne checkByte = (Span x, Span y, long z, ref long a) => - { - short[] tmpArray = new short[8]; - for (int i = 0; i < 8; i++) - { - tmpArray[i] = (short)Math.Abs(x[i] - y[i]); - } - - foreach (short s in tmpArray) a += s; - return a == z; - }; - - if (!byteTable.CheckResult(checkByte)) - { - PrintError(byteTable, methodUnderTestName, "(Span x, Span y, long z, ref long a) => SumAbsoluteDifferences", checkByte); - testResult = Fail; - } - } - } - else - { - Console.WriteLine($"Sse2.IsSupported: {Sse2.IsSupported}, skipped tests of {typeof(Sse2)}.{methodUnderTestName}"); - } - - return testResult; - } - } -} diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_r.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_r.csproj deleted file mode 100644 index d2e8024f0e57..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_r.csproj +++ /dev/null @@ -1,38 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {5E8D9C85-4EF4-4A7E-8294-3F64C4340945} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - - False - - - - Embedded - - - - - - - - - - - - - - \ No newline at end of file diff --git a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_ro.csproj b/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_ro.csproj deleted file mode 100644 index dc198c17775a..000000000000 --- a/tests/src/JIT/HardwareIntrinsics/X86/Sse2/SumAbsoluteDifferences_ro.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Debug - AnyCPU - 2.0 - {8B32C158-D842-4D93-867B-6D9AD688AB92} - Exe - {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - ..\..\ - true - - - - - - - - False - - - - Embedded - True - - - - - - - - - - - - \ No newline at end of file