Skip to content

Commit 5d4cce8

Browse files
authored
Multiple commits to ultimately support removal of SafeHandle for global handles (#321)
* Adapted to new model with handle generation in this repo * Sets the stage for removing use of SafeHandle as it makes the generation of the handle code local to this repo so it's easier to maintain. * Converted generator to using Ubiquity.NET.CommandLine for args parsing. * Added scripting to generate the wrappers off-line * Added specialized target to generate the response file for the generator. * The project has a package reference to the LibLLVM package so that it can find the headers to parse. * Adjusted stylecop.json to get expected behavior with regard to internal interfaces. * See: https://github.com/DotNetAnalyzers/ StyleCopAnalyzers/issues/3100 * Fixed `EnsureBuildOutputPaths` target in `Directory.Build.targets` so it actually creates the `NuGet` folder even in IDE builds. * Removed unused 'Using" namespace statements. * Renamed `LLJit` for consistency * Misc. Updates to support testing/validation of ref counting problems * Fixed debug record tests to operate without asserts in debug mode * Added wrapper to re-order API parameters for `LLVMOrcJITLookup()` * Renamed `GlobalHandleBaseExtensions.CloneWithAddref()` to `GlobalHandleBaseExtensions.CloneWithManagedAddRef()` to help express and clarify the intent. * Fixed links in docs for clarity * Updated dictionary ignored words list * Clarified/Corrected doc comments * Updated to resolve latest analyzer issues * Commiting changes for ref counting * Not yet complete but allows merging stashed changes for removal of safehandles. * Removing safe handles avoids confusion on addref for managed wrapper and addref for the native ABI handle. These are NOT the same and can't be used interchangeably for `StringPoolEntry` handles as some APIs assume "move" semantics, others have only temp access, while others will perform an addref on their own [It's all VERY confusing and poorly documented - but users of this library should not care about these subtleties. A major point of this lib is to resolve them consistently. * Final remove of safe handle for global handles * Global handles are IDisposable structs * While this is sometimes considered controversial it is primarily to leverage built in support for analyzers and use of I Disposable. Additionally, it makes the disposal consistent across the handle types. * Fixed a LOT of issues with reference counting of `SymbolStringPoolEntry` * Handling of those was not correct in a LOT of places. This was ignored in a release build of the native libraries, but with a debug build it asserted... a lot! 🤦 This fixes #320 * ContextHandle was renamed to WrappedHandle for both global and aliased handles. * Code generator was updated to use a single template that generates code for struct handles from the same template. IFF it is a global handle will it be generated with IDisposable support. Ultimately the structs are just a wrapper around a `nint`. * Added support for C#14 field keyword to make some code simpler and easier to read/maintain. * due to bugs in support of `extension` that keyword IS NOT used. It was tried, even in the VS2026 preview, it's just too broken in the IDE and analyzers etc... to be worth the hassle. * It isn't necessary for anything other than properties or statics so not all that useful in the end. * Moved constant enumerations from LibLLVMValueKind to allow debugger to show original value not named symbolic. * Fixed handling of `LLVMErrorRef` it had ref count and crash issues that were identified in debug builds and testing of conversion to value type for handles. * `LLVMErrorRef` itself is a ref type `class` as it is mutable to handle the weird ownership semantics of the error object and message. The wrapper types handle all the idiosyncrasies of this low-level weird behavior. * Added special marshaling behavior for the error ref as it isn't like the other handles * Added Correct support for TypeId. Technically the only supported form is a string. Other values include an ID but there's nothing available in the LLVM-C API to do anything with them. (It's all very C++ specific). Fortunately, they are generally swallowed internally and the only form that is supposed to surface out of the C API is that of a string. * Added extension property to interop handle `LLVMValueRef` to allow getting the kind of value. * This is mostly used for debugging to find the type of a value while debugging. It isn't generally useful and not exposed from upper layers. * Added support for getting symbols where the symbol name itself includes the `:` character. Parsing of the string representation is flaky at best and really just a diagnostic/debug tool. (But it was a really helpful one!) * Added Dispose of the ICodeGenerator to common Kaleidoscope REPL so that it will trigger cleanup (and possible debug asserts for ref count issues) * Continued application of pattern for naming internal handle in wrapper types. * Member is named `Handle` and for types that need to transfer ownership to native there is an `InvalidateAfterMove` to allow for scenarios where move only happens on success. * Added SrcGeneration library to support source generation and `IndentedTextWriter` extensions. * This allows for code only replacement for T4 templates. While T4 is great for some cases it can get unruly and downright impenetrable for more complex cases. Thus, this support library allows for common cases of C# code generators. * The handle generator doesn't currently use this as it uses T4 with a fairly simple template but that may change over time. It is used by `ScopeStack` to format the stack as nested data string for diagnostics.
1 parent ce69e03 commit 5d4cce8

File tree

226 files changed

+11293
-1311
lines changed

Some content is hidden

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

226 files changed

+11293
-1311
lines changed

Directory.Build.targets

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
<Project InitialTargets="VerifyProjectSettings;ShowBuildParameters">
1+
<Project InitialTargets="VerifyProjectSettings;ShowBuildParameters;EnsureBuildOutputPaths">
22
<!--
33
Since Nuget.config is configured to include the build output location this
44
will ensure the folder exists during restore so that it won't fail.
55
-->
6-
<Target Name="EnsureBuildOutputPaths" BeforeTargets="Restore;Build;Rebuild">
6+
<Target Name="EnsureBuildOutputPaths" Condition="!Exists($(PackageOutputPath))">
77
<MakeDir Directories="$(PackageOutputPath)"/>
88
</Target>
99

Directory.Packages.props

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
<ItemGroup>
77
<GlobalPackageReference Include="Ubiquity.NET.Versioning.Build.Tasks" Version="5.0.7-alpha.0.1" />
88
<GlobalPackageReference Include="IDisposableAnalyzers" Version="4.0.8" Condition="'$(NoCommonAnalyzers)' !=' true'" />
9-
<GlobalPackageReference Include="MustUseRetVal" Version="0.0.2" />
9+
<GlobalPackageReference Include="MustUseRetVal" Version="0.0.2" Condition="'$(NoCommonAnalyzers)' !=' true'" />
10+
1011
<!--
1112
NOTE: This analyzer is sadly, perpetually in "pre-release mode". There have been many issues/discussion on the point
1213
and it has all fallen on deaf ears. So policies regarding "NO-Prerelease" components need to be overruled on this one
@@ -34,10 +35,13 @@
3435

3536
<!-- Common packages for solution -->
3637
<PackageVersion Include="System.Linq.Async" Version="6.0.3" />
38+
<PackageVersion Include="Ubiquity.NET.LibLLVM" Version="20.1.8" />
3739
<PackageVersion Include="Ubiquity.NET.Versioning" Version="6.0.2-beta" />
3840
<PackageVersion Include="Antlr4BuildTasks" Version="12.10.0" />
3941
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.13.1" />
4042
<PackageVersion Include="OpenSoftware.DgmlBuilder" Version="2.1.0" />
43+
<PackageVersion Include="CppSharp" Version="1.1.5.3168" />
44+
<PackageVersion Include="System.CodeDom" Version="9.0.7" />
4145

4246
<!-- Tests all use the same framework versions -->
4347
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.14.1" />

Generate-HandleWrappers.ps1

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<#
2+
.SYNOPSIS
3+
Generates the wrappers for handles from the headers in the Ubiquity.NET.LibLLVM NuGet Package
4+
5+
.DESCRIPTION
6+
This is used 'off-line' (by a developer) to generate the source for the handle wrappers. It is
7+
NOT run during an automated build as the generator itself is dependent on the CppSharp library
8+
that only supports the X64 architecture. Automated builds may (at some point in the future)
9+
run on any architecture supported by .NET so cannot generate the sources at build time. A developer
10+
machine generating the wrappers is assumed X64 (Windows, Linux, or Mac)
11+
12+
.PARAMETER SkipRun
13+
This parameter is used for inner loop testing to generate the response file so the paths
14+
match the actual version of the package used. It skips actually building/running the
15+
generator so that a developer can debug the run of that stage.
16+
#>
17+
Param(
18+
[switch]$SkipRun
19+
)
20+
21+
Push-Location $PSScriptRoot
22+
$oldPath = $env:Path
23+
try
24+
{
25+
$ErrorActionPreference = 'stop'
26+
$generatorProj = '.\src\Interop\LlvmBindingsGenerator\LlvmBindingsGenerator.csproj'
27+
$rspPath = Join-Path $PSScriptRoot 'generator.rsp'
28+
29+
Write-Information "Generating response file: $rspPath via GenerateResponseFile target in $generatorProj"
30+
dotnet msbuild -restore -target:GenerateResponseFile -property:HandleGeneratorResponeFilePath=`""$rspPath"`" $generatorProj
31+
32+
if(!$SkipRun)
33+
{
34+
Write-Information 'Generating Handle wrapper source...'
35+
dotnet run --no-restore --project $generatorProj -- @$rspPath
36+
}
37+
}
38+
catch
39+
{
40+
# Everything from the official docs to the various articles in the blog-sphere say this isn't needed
41+
# and in fact it is redundant - They're all WRONG! By re-throwing the exception the original location
42+
# information is retained and the error reported will include the correct source file and line number
43+
# data for the error. Without this, only the error message is retained and the location information is
44+
# Line 1, Column 1, of the outer most script file, which is, of course, completely useless.
45+
throw
46+
}
47+
finally
48+
{
49+
Pop-Location
50+
$env:Path = $oldPath
51+
}

IgnoredWords.dic

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ attrib
1616
Attribs
1717
AttributeSet
1818
Attrs
19+
automatable
1920
baz
2021
binaryop
2122
binlogs
@@ -32,6 +33,7 @@ buildtransitive
3233
builtinop
3334
byref
3435
byval
36+
canonicalization
3537
castable
3638
cibuild
3739
Cmp
@@ -67,11 +69,13 @@ foo
6769
fullsrc
6870
func
6971
getelementptr
72+
getters
7073
gh
7174
github
7275
Globalization
7376
Hashtable
7477
Identifier
78+
ifunc
7579
Impl
7680
initializer
7781
inline
@@ -86,12 +90,15 @@ LibLLVM
8690
Llilum
8791
llvm
8892
llvmversion
93+
lookups
8994
LValue
9095
malloc
9196
marshallers
9297
marshalling
98+
materializer
9399
memcopy
94100
memcpy
101+
memset
95102
metadata
96103
Mips
97104
msbuild
@@ -106,6 +113,7 @@ Nullable
106113
Nvidia
107114
online
108115
optimizenone
116+
outdent
109117
pages
110118
paren
111119
perf
@@ -119,6 +127,7 @@ readonly
119127
refactor
120128
refcount
121129
referenceable
130+
Relocations
122131
repl
123132
repo
124133
RMW
@@ -136,10 +145,12 @@ structs
136145
Subrange
137146
Sym
138147
telliam
148+
templated
139149
tl
140150
trx
141151
typdef
142152
Typedef
153+
typedefs
143154
typelib
144155
typeof
145156
uid
@@ -151,6 +162,7 @@ Unhandled
151162
uniqued
152163
uniqueing
153164
unmarshal
165+
unreferenced
154166
Unshipped
155167
userdefinedop
156168
Users

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"sdk": {
3-
"version": "9.0.201",
3+
"version": "9.0.305",
44
"rollForward": "latestMinor",
55
"allowPrerelease": false
66
},

src/Interop/InteropTests/ABI/libllvm-c/DataLayoutBindingsTests.cs

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
using Microsoft.VisualStudio.TestTools.UnitTesting;
55

66
using Ubiquity.NET.InteropHelpers;
7+
using Ubiquity.NET.Llvm.Interop.ABI.StringMarshaling;
78

89
using static Ubiquity.NET.Llvm.Interop.ABI.libllvm_c.DataLayoutBindings;
10+
using static Ubiquity.NET.Llvm.Interop.ABI.llvm_c.Error;
911

1012
namespace Ubiquity.NET.Llvm.Interop.ABI.libllvm_c.UT
1113
{
@@ -15,11 +17,15 @@ public class DataLayoutBindingsTests
1517
[TestMethod]
1618
public void LibLLVMParseDataLayoutTest( )
1719
{
18-
using(var errorRef = LibLLVMParseDataLayout( "badlayout"u8, out LLVMTargetDataRef retVal ))
19-
using(retVal)
20+
using(LLVMErrorRef errorRef = LibLLVMParseDataLayout( "badlayout"u8, out LLVMTargetDataRef retVal ))
2021
{
21-
Assert.IsTrue( retVal.IsInvalid );
22+
Assert.IsTrue( retVal.IsNull );
2223
Assert.IsTrue( errorRef.Failed );
24+
Assert.IsFalse( errorRef.Success);
25+
Assert.IsFalse( errorRef.IsNull );
26+
Assert.IsTrue( errorRef.IsString );
27+
Assert.AreEqual(LLVMGetStringErrorTypeId(), errorRef.TypeId);
28+
Assert.AreNotEqual( 0, errorRef.DangerousGetHandle() );
2329
string errMsg = errorRef.ToString();
2430
Assert.IsFalse( string.IsNullOrWhiteSpace( errMsg ), "Failure should have an error message" );
2531
}
@@ -31,7 +37,7 @@ public void LibLLVMParseDataLayoutTest( )
3137
{
3238
Assert.IsFalse( errorRef.Failed );
3339
Assert.IsTrue( errorRef.Success );
34-
Assert.IsFalse( retVal.IsInvalid );
40+
Assert.IsFalse( retVal.IsNull );
3541
string errMsg = errorRef.ToString();
3642
Assert.IsTrue( string.IsNullOrWhiteSpace( errMsg ), "Valid layout should NOT have an error message" );
3743
}

src/Interop/InteropTests/ContextHandleMarshallerTests.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ public class ContextHandleMarshallerTests
1111
[TestMethod]
1212
public void ConvertToManagedTest( )
1313
{
14-
var handle = ContextHandleMarshaller<LibLLVMMDOperandRef>.ConvertToManaged(NativeABIValue);
14+
var handle = WrappedHandleMarshaller<LibLLVMMDOperandRef>.ConvertToManaged(NativeABIValue);
1515
Assert.IsFalse( handle.IsNull );
1616
Assert.AreEqual( NativeABIValue, handle.DangerousGetHandle() );
1717
Assert.AreEqual( NativeABIValue, (nint)handle );
@@ -24,7 +24,7 @@ public void ConvertToUnmanagedTest( )
2424

2525
// Validate FromABI() method AND verify assumptions made in subsequent asserts...
2626
Assert.AreEqual( NativeABIValue, handle.DangerousGetHandle() );
27-
nint abiHandleVal = ContextHandleMarshaller<LibLLVMMDOperandRef>.ConvertToUnmanaged(handle);
27+
nint abiHandleVal = WrappedHandleMarshaller<LibLLVMMDOperandRef>.ConvertToUnmanaged(handle);
2828
Assert.AreEqual( NativeABIValue, abiHandleVal );
2929
}
3030

src/Interop/InteropTests/DebugRecordTests.cs

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
// Copyright (c) Ubiquity.NET Contributors. All rights reserved.
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

4+
using System;
5+
46
using Microsoft.VisualStudio.TestTools.UnitTesting;
57

68
using Ubiquity.NET.Llvm.Interop.ABI.libllvm_c;
@@ -60,6 +62,24 @@ public void DebugRecordEnumerationSucceeds( )
6062
// Now attach debug records to the malloc.
6163
// first create the information to attach (It's a lot...)
6264
using LLVMDIBuilderRef diBuilder = LLVMCreateDIBuilder(module);
65+
LLVMMetadataRef diFile = LLVMDIBuilderCreateFile(diBuilder, "testfile.cs"u8, Environment.CurrentDirectory);
66+
LLVMMetadataRef diCU = LLVMDIBuilderCreateCompileUnit(
67+
diBuilder,
68+
LLVMDWARFSourceLanguage.LLVMDWARFSourceLanguageC_sharp,
69+
diFile,
70+
Producer: null,
71+
isOptimized: false,
72+
Flags: null,
73+
RuntimeVer: 0,
74+
SplitName: null,
75+
LLVMDWARFEmissionKind.LLVMDWARFEmissionFull,
76+
DWOId: 0,
77+
SplitDebugInlining: false,
78+
DebugInfoForProfiling: false,
79+
SysRoot: null,
80+
SDK: null
81+
);
82+
6383
LLVMMetadataRef int32DiType = LLVMDIBuilderCreateBasicType(
6484
diBuilder,
6585
"int32_t"u8,
@@ -76,7 +96,7 @@ public void DebugRecordEnumerationSucceeds( )
7696
Scope: default,
7797
Name: "TestFunc"u8,
7898
LinkageName: "_TestFunc"u8,
79-
File: default,
99+
File: diFile,
80100
LineNo: 0,
81101
Ty: diFuncType,
82102
IsLocalToUnit: true,
@@ -85,11 +105,23 @@ public void DebugRecordEnumerationSucceeds( )
85105
LLVMDIFlags.LLVMDIFlagPrivate,
86106
IsOptimized: false
87107
);
108+
109+
var diLocalVar = LLVMDIBuilderCreateAutoVariable(
110+
diBuilder,
111+
scope,
112+
Name: string.Empty,
113+
File: diFile,
114+
LineNo: 0,
115+
int32PtrDiType,
116+
AlwaysPreserve: false,
117+
LLVMDIFlags.LLVMDIFlagArtificial,
118+
4);
119+
88120
LLVMMetadataRef diLocation = LLVMDIBuilderCreateDebugLocation(ctx, 123,4, scope, default);
89121

90122
// Now attach the record to the result of the malloc call
91123
// and retest the status as it should be different now
92-
LLVMDbgRecordRef dbgRecord = LLVMDIBuilderInsertDbgValueRecordBefore(diBuilder, mallocInst, int32PtrDiType, emptyExpression, diLocation, mallocInst);
124+
LLVMDbgRecordRef dbgRecord = LLVMDIBuilderInsertDbgValueRecordBefore(diBuilder, mallocInst, diLocalVar, emptyExpression, diLocation, mallocInst);
93125
Assert.IsTrue( LibLLVMHasDbgRecords( mallocInst ) );
94126

95127
// this should not crash now... [It shouldn't ever but that's another story...]

src/Interop/InteropTests/GlobalHandleBaseTests.cs

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

0 commit comments

Comments
 (0)