Skip to content

Commit d6b4e37

Browse files
authored
Custom memory allocation for JIT and simpler native contexts. (#330)
* Add Custom memory manager support to JIT * This adds the types but doesn't yet use/test them - Future PR will add them once testing is done * Fixed declaration of native callback function pointers - Can't use bool for LLVMBool as that requires marshaling * Added debug assert for `SafeGCHandle.AddRefAndGetNativeContext` to complain in a debug build if the underlying AddRef fails for some reason. - Docs on behavior of that API are a bit dodgy and it isn't clear how it could ever provide an out parameter of non-success * Added additional preprocessor checks to disable unused code * Added `IJitMemoryAllocator` to support either MCJIT simple allocator or an OrcJitV2 Object layer. * Readme and doc comment corrections/clarifications * Updates for simpler native contexts Native contexts are effectively an opaque `void*` they can have any value that fits in a pointer. The native layers don't care about it and store the value to provide as a parameter to callback functions. The implementation of the callback has all the knowledge to interpret the intended meaning of the context value. For managed code this is an allocated GCHandle converted to a pointer. The managed code can resolve the pointer to a handle, and then the target instance to get a viable managed ref. When no more call backs can occur the context is released. This simplifies the use of callbacks as a native context can be created for any managed heap object. (Extension method added for this action) * Updated comments * Removed some dead code * Added comments to explain limits of the C# 14 keyword `extension` and why it isn't used. * Removed SafeGCHandle and MarshalGCHandles as context handles are MUCH simpler now. * Added the memory allocator base types - These are not yet used or tested and are subject to change in the future and therfore attributed as experimental. * Defined interface for materializer callbacks to allow internal implementation to hold delegates as a single unit to become a native context for call backs. * Removed unnecessary using statements
1 parent 94c0ad4 commit d6b4e37

41 files changed

Lines changed: 801 additions & 453 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/StringMarshaling/LLVMErrorRef.cs

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ public LLVMErrorTypeId TypeId
8282
}
8383

8484
return LazyMessage.IsValueCreated
85-
? LLVMGetStringErrorTypeId()
85+
? LLVMGetStringErrorTypeId() // string retrieved already so the "type" is known.
8686
: LLVMErrorTypeId.FromABI(LLVMGetErrorTypeId(DangerousGetHandle()));
8787

8888
[DllImport( LibraryName )]
@@ -135,7 +135,7 @@ public static LLVMErrorRef Create( LazyEncodedString errMsg )
135135
/// In all other cases a fully wrapped handle (<see cref="LLVMErrorRef"/>) is used via <see cref="Create(LazyEncodedString)"/>.
136136
/// </para>
137137
/// </remarks>
138-
public static unsafe nint CreateForNativeOut( LazyEncodedString errMsg )
138+
public static nint CreateForNativeOut( LazyEncodedString errMsg )
139139
{
140140
unsafe
141141
{
@@ -152,6 +152,14 @@ public static unsafe nint CreateForNativeOut( LazyEncodedString errMsg )
152152
static unsafe extern nint /*LLVMErrorRef*/ LLVMCreateStringError( byte* ErrMsg );
153153
}
154154

155+
/// <summary>Create a new <see cref="LLVMErrorRef"/> as <see cref="nint"/> from <see cref="Exception.Message"/></summary>
156+
/// <param name="ex">Exceotion to get the error message from</param>
157+
/// <inheritdoc cref="CreateForNativeOut(LazyEncodedString)"/>
158+
public static nint CreateForNativeOut( Exception ex)
159+
{
160+
return CreateForNativeOut(ex.Message);
161+
}
162+
155163
public void Dispose()
156164
{
157165
// if a message was previously realized, dispose of it now.
@@ -180,19 +188,6 @@ public static LLVMErrorRef FromABI( nint abiValue )
180188
return new(abiValue);
181189
}
182190

183-
private LLVMErrorTypeId LazyGetTypeId()
184-
{
185-
// NOTE: Native API will fail (undocumented) if error message already retrieved.
186-
// This causes a crash in a debugger trying to show this property as ToString()
187-
// is already called.
188-
return LazyMessage.IsValueCreated ? default : LLVMErrorTypeId.FromABI(LLVMGetErrorTypeId(DangerousGetHandle()));
189-
190-
[DllImport( LibraryName )]
191-
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
192-
[SuppressMessage( "Interoperability", "SYSLIB1054:Use 'LibraryImportAttribute' instead of 'DllImportAttribute' to generate P/Invoke marshalling code at compile time", Justification = "Signature is P/Invoke ready" )]
193-
static extern /*LLVMErrorTypeId*/nint LLVMGetErrorTypeId(/*LLVMErrorRef*/ nint Err );
194-
}
195-
196191
private ErrorMessageString LazyGetMessage( )
197192
{
198193
if(IsNull)

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/Core.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -421,15 +421,15 @@ public static partial class Core
421421

422422
[LibraryImport( LibraryName )]
423423
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
424-
public static unsafe partial void LLVMContextSetDiagnosticHandler( LLVMContextRefAlias C, LLVMDiagnosticHandler Handler, nint DiagnosticContext );
424+
public static unsafe partial void LLVMContextSetDiagnosticHandler( LLVMContextRefAlias C, LLVMDiagnosticHandler Handler, void* DiagnosticContext );
425425

426426
[LibraryImport( LibraryName )]
427427
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
428428
public static unsafe partial LLVMDiagnosticHandler LLVMContextGetDiagnosticHandler( LLVMContextRefAlias C );
429429

430430
[LibraryImport( LibraryName )]
431431
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
432-
public static unsafe partial nint LLVMContextGetDiagnosticContext( LLVMContextRefAlias C );
432+
public static unsafe partial void* LLVMContextGetDiagnosticContext( LLVMContextRefAlias C );
433433

434434
[LibraryImport( LibraryName )]
435435
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/ExecutionEngine.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,9 @@ namespace Ubiquity.NET.Llvm.Interop.ABI.llvm_c
1010
// Misplaced using directive; It isn't misplaced - tooling is too brain dead to know the difference between an alias and a using directive
1111
#pragma warning disable IDE0065, SA1200, SA1135
1212
using unsafe LLVMMemoryManagerAllocateCodeSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, byte* /*retVal*/>;
13-
using unsafe LLVMMemoryManagerAllocateDataSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, bool /*IsReadOnly*/, byte* /*retVal*/>;
13+
using unsafe LLVMMemoryManagerAllocateDataSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, /*LLVMBool*/ Int32 /*IsReadOnly*/, byte* /*retVal*/>;
1414
using unsafe LLVMMemoryManagerDestroyCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, void /*retVal*/ >;
15-
using unsafe LLVMMemoryManagerFinalizeMemoryCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, byte** /*ErrMsg*/, bool /*retVal*/>;
15+
using unsafe LLVMMemoryManagerFinalizeMemoryCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, byte** /*ErrMsg*/, /*LLVMBool*/ Int32 /*retVal*/>;
1616
#pragma warning restore IDE0065, SA1200, SA1135
1717

1818
[StructLayout( LayoutKind.Sequential )]

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/Orc.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ public static unsafe partial LLVMOrcSymbolStringPoolEntryRef LLVMOrcExecutionSes
207207
LazyEncodedString Name
208208
);
209209

210-
[Experimental( "LLVM001" )]
210+
[Experimental( "LLVMEXP001" )]
211211
[MethodImpl(MethodImplOptions.AggressiveInlining)]
212212
public static unsafe void LLVMOrcExecutionSessionLookup(
213213
LLVMOrcExecutionSessionRef ES,
@@ -223,7 +223,7 @@ public static unsafe void LLVMOrcExecutionSessionLookup(
223223

224224
[LibraryImport( LibraryName )]
225225
[UnmanagedCallConv( CallConvs = [ typeof( CallConvCdecl ) ] )]
226-
[Experimental("LLVMEXP001")]
226+
[Experimental("LLVMEXP002")]
227227
private static unsafe partial void LLVMOrcExecutionSessionLookup(
228228
LLVMOrcExecutionSessionRef ES,
229229
LLVMOrcLookupKind K,

src/Interop/Ubiquity.NET.Llvm.Interop/ABI/llvm-c/OrcEE.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,10 @@ namespace Ubiquity.NET.Llvm.Interop.ABI.llvm_c
66
// Misplaced using directive; It isn't misplaced - tooling is too brain dead to know the difference between an alias and a using directive
77
#pragma warning disable IDE0065, SA1200, SA1135
88
using unsafe LLVMMemoryManagerAllocateCodeSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, byte* /*retVal*/>;
9-
using unsafe LLVMMemoryManagerAllocateDataSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, bool /*IsReadOnly*/, byte* /*retVal*/>;
9+
using unsafe LLVMMemoryManagerAllocateDataSectionCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, nuint /*Size*/, uint /*Alignment*/, uint /*SectionID*/, byte* /*SectionName*/, /*LLVMBool*/ Int32 /*IsReadOnly*/, byte* /*retVal*/>;
1010
using unsafe LLVMMemoryManagerCreateContextCallback = delegate* unmanaged[Cdecl]< void* /*CtxCtx*/, void* /*retVal*/>;
1111
using unsafe LLVMMemoryManagerDestroyCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, void /*retVal*/ >;
12-
using unsafe LLVMMemoryManagerFinalizeMemoryCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, byte** /*ErrMsg*/, bool /*retVal*/>;
12+
using unsafe LLVMMemoryManagerFinalizeMemoryCallback = delegate* unmanaged[Cdecl]< void* /*Opaque*/, byte** /*ErrMsg*/, /*LLVMBool*/ Int32 /*retVal*/>;
1313
using unsafe LLVMMemoryManagerNotifyTerminatingCallback = delegate* unmanaged[Cdecl]< void* /*CtxCtx*/, void /*retVal*/>;
1414
#pragma warning restore IDE0065, SA1200, SA1135
1515

src/Ubiquity.NET.CommandLine.UT/RawApiTests.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22
// Licensed under the Apache-2.0 WITH LLVM-exception license. See the LICENSE.md file in the project root for full license information.
33

44
using System.CommandLine;
5-
using System.CommandLine.Help;
65

76
using Microsoft.VisualStudio.TestTools.UnitTesting;
87

src/Ubiquity.NET.CommandLine/ParseResultExtensions.cs

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -6,47 +6,23 @@
66
using System.CommandLine;
77
using System.CommandLine.Help;
88
using System.CommandLine.Parsing;
9-
using System.Diagnostics.CodeAnalysis;
109
using System.Linq;
1110

1211
namespace Ubiquity.NET.CommandLine
1312
{
13+
// This does NOT use the new C# 14 extension syntax due to several reasons
14+
// 1) Code lens does not work https://github.com/dotnet/roslyn/issues/79006 [Sadly marked as "not planned" - e.g., dead-end]
15+
// 2) MANY analyzers get things wrong and need to be supressed (CA1000, CA1034, and many others [SAxxxx])
16+
// 3) Many tools (like docfx don't support the new syntax yet)
17+
// 4) No clear support for Caller* attributes ([CallerArgumentExpression(...)]).
18+
//
19+
// Bottom line it's a good idea with an incomplete implementation lacking support
20+
// in the overall ecosystem. Don't use it unless you absolutely have to until all
21+
// of that is sorted out.
22+
1423
/// <summary>Utility extension methods for command line parsing</summary>
15-
[SuppressMessage( "Design", "CA1034:Nested types should not be visible", Justification = "BS, extension" )]
16-
[SuppressMessage( "Performance", "CA1822:Mark members as static", Justification = "BS, Extension" )]
1724
public static class ParseResultExtensions
1825
{
19-
#if DOCFX_BUILD_SUPPORTS_EXTENSION_KEYWORD
20-
extension(ParseResult self)
21-
{
22-
/// <summary>Gets a value indicating whether <paramref name="self"/> has any errors</summary>
23-
public bool HasErrors => self.Errors.Count > 0;
24-
25-
public HelpOption? HelpOption
26-
{
27-
get
28-
{
29-
var helpOptions = from r in self.CommandResult.RecurseWhileNotNull(r => r.Parent as CommandResult)
30-
from o in r.Command.Options.OfType<HelpOption>()
31-
select o;
32-
33-
return helpOptions.FirstOrDefault();
34-
}
35-
}
36-
37-
public VersionOption? VersionOption
38-
{
39-
get
40-
{
41-
var versionOptions = from r in self.CommandResult.RecurseWhileNotNull(r => r.Parent as CommandResult)
42-
from o in r.Command.Options.OfType<VersionOption>()
43-
select o;
44-
45-
return versionOptions.FirstOrDefault();
46-
}
47-
}
48-
}
49-
#else
5026
/// <summary>Gets a value indicating whether <paramref name="self"/> has any errors</summary>
5127
/// <param name="self">Result to test for errors</param>
5228
/// <returns>value indicating whether <paramref name="self"/> has any errors</returns>
@@ -75,7 +51,6 @@ from o in r.Command.Options.OfType<VersionOption>()
7551

7652
return versionOptions.FirstOrDefault();
7753
}
78-
#endif
7954

8055
// shamelessly "borrowed" from: https://github.com/dotnet/dotnet/blob/8c7b3dcd2bd657c11b12973f1214e7c3c616b174/src/command-line-api/src/System.CommandLine/Help/HelpBuilderExtensions.cs#L42
8156
internal static IEnumerable<T> RecurseWhileNotNull<T>( this T? source, Func<T, T?> next )

src/Ubiquity.NET.Extensions/FluentValidationExtensions.cs

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,31 @@
55
using System.ComponentModel;
66
using System.Diagnostics;
77
using System.Diagnostics.CodeAnalysis;
8-
using System.Globalization;
98
using System.Runtime.CompilerServices;
109

1110
namespace Ubiquity.NET.Extensions
1211
{
12+
// This does NOT use the new C# 14 extension syntax due to several reasons
13+
// 1) Code lens does not work https://github.com/dotnet/roslyn/issues/79006 [Sadly, marked as "not planned" - e.g., dead-end]
14+
// 2) MANY analyzers get things wrong and need to be supressed (CA1000, CA1034, and many others [SAxxxx])
15+
// 3) Many tools (like docfx) don't support the new syntax yet and it isn't clear if they will in the future.
16+
// 4) No clear support for Caller* attributes ([CallerArgumentExpression(...)]).
17+
//
18+
// Bottom line it's a good idea with an incomplete implementation lacking support
19+
// in the overall ecosystem. Don't use it unless you absolutely have to until all
20+
// of that is sorted out.
21+
1322
/// <summary>Extension class to provide Fluent validation of arguments</summary>
1423
/// <remarks>
1524
/// These are similar to many of the built-in support checks except that
1625
/// they use a `Fluent' style to allow validation of parameters used as inputs
1726
/// to other functions that ultimately produce parameters for a base constructor.
1827
/// They also serve to provide validation when using body expressions for property
19-
/// method implementations etc...
28+
/// method implementations etc... Though the C# 14 <c>field</c> keyword makes that
29+
/// use mostly a moot point.
2030
/// </remarks>
2131
public static class FluentValidationExtensions
2232
{
23-
// NOTE: These DO NOT use the new `extension` keyword syntax as it is not clear
24-
// how CallerArgumentExpression is supposed to be used for those...
25-
2633
/// <summary>Throws an exception if <paramref name="obj"/> is <see langword="null"/></summary>
2734
/// <typeparam name="T">Type of reference parameter to test for</typeparam>
2835
/// <param name="obj">Instance to test</param>
@@ -72,9 +79,12 @@ public static T ThrowIfNotDefined<T>( this T self, [CallerArgumentExpression( na
7279
where T : struct, Enum
7380
{
7481
ArgumentNullException.ThrowIfNull(self, exp);
82+
83+
// TODO: Move the exception message to a resource for globalization
84+
// This matches the overloaded constructor version but allows for reporting enums with non-int underlying type.
7585
return Enum.IsDefined( typeof(T), self )
7686
? self
77-
: throw new InvalidEnumArgumentException(exp);
87+
: throw new InvalidEnumArgumentException($"The value of argument '{exp}' ({self}) is invalid for Enum of type '{typeof(T)}'");
7888
}
7989
}
8090
}

src/Ubiquity.NET.InteropHelpers/MarshalGCHandle.cs

Lines changed: 0 additions & 46 deletions
This file was deleted.

0 commit comments

Comments
 (0)