Skip to content

Commit 3240596

Browse files
committed
Start working on a generator for pointer types
1 parent 547d561 commit 3240596

File tree

3 files changed

+202
-0
lines changed

3 files changed

+202
-0
lines changed

SilkX.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Playground", "sources\Playg
8686
EndProject
8787
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Silk.NET.Core.UnitTests", "tests\Silk.NET.Core.UnitTests\Silk.NET.Core.UnitTests.csproj", "{A87E1861-07E4-4B7A-9173-0853370A7D4E}"
8888
EndProject
89+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "pointergen", "eng\pointergen\pointergen.csproj", "{397C8E56-697C-4771-8322-951F51E50064}"
90+
EndProject
8991
Global
9092
GlobalSection(SolutionConfigurationPlatforms) = preSolution
9193
Debug|Any CPU = Debug|Any CPU
@@ -140,6 +142,10 @@ Global
140142
{A87E1861-07E4-4B7A-9173-0853370A7D4E}.Debug|Any CPU.Build.0 = Debug|Any CPU
141143
{A87E1861-07E4-4B7A-9173-0853370A7D4E}.Release|Any CPU.ActiveCfg = Release|Any CPU
142144
{A87E1861-07E4-4B7A-9173-0853370A7D4E}.Release|Any CPU.Build.0 = Release|Any CPU
145+
{397C8E56-697C-4771-8322-951F51E50064}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
146+
{397C8E56-697C-4771-8322-951F51E50064}.Debug|Any CPU.Build.0 = Debug|Any CPU
147+
{397C8E56-697C-4771-8322-951F51E50064}.Release|Any CPU.ActiveCfg = Release|Any CPU
148+
{397C8E56-697C-4771-8322-951F51E50064}.Release|Any CPU.Build.0 = Release|Any CPU
143149
EndGlobalSection
144150
GlobalSection(SolutionProperties) = preSolution
145151
HideSolutionNode = FALSE
@@ -161,6 +167,7 @@ Global
161167
{131C09C1-BF4D-47C1-AF13-4A7E30866B1E} = {DD29EA8F-B1A6-45AA-8D2E-B38DA56D9EF6}
162168
{48F43535-3AFC-45E7-A98D-C2609B3B9757} = {DD29EA8F-B1A6-45AA-8D2E-B38DA56D9EF6}
163169
{A87E1861-07E4-4B7A-9173-0853370A7D4E} = {A5578D12-9E77-4647-8C22-0DBD17760BFF}
170+
{397C8E56-697C-4771-8322-951F51E50064} = {475AEF7B-0154-4989-AF82-97E3A95A96AF}
164171
EndGlobalSection
165172
GlobalSection(ExtensibilityGlobals) = postSolution
166173
SolutionGuid = {78D2CF6A-60A1-43E3-837B-00B73C9DA384}

eng/pointergen/Program.cs

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
// See https://aka.ms/new-console-template for more information
2+
3+
static IEnumerable<IEnumerable<bool>> MutabilityCombinations(int current)
4+
{
5+
for (bool isFirstMut = false, done = false; !done; (isFirstMut, done) = (!isFirstMut, isFirstMut || done))
6+
{
7+
if (current == 0)
8+
{
9+
yield return Enumerable.Repeat(isFirstMut, 1);
10+
}
11+
else
12+
{
13+
foreach (var nextMutabilityCombinations in MutabilityCombinations(current - 1))
14+
{
15+
yield return Enumerable.Repeat(isFirstMut, 1).Concat(nextMutabilityCombinations);
16+
}
17+
}
18+
}
19+
}
20+
21+
if (!File.Exists("pointergen.csproj"))
22+
{
23+
throw new InvalidOperationException("Run this in the pointergen directory.");
24+
}
25+
26+
const int maxIndirection = 3;
27+
for (var indirectionLevels = 0; indirectionLevels < maxIndirection; indirectionLevels++)
28+
{
29+
var mutabilityCombinations = MutabilityCombinations(indirectionLevels).Select(x => x.ToArray()).ToArray();
30+
foreach (var mutabilityCombination in mutabilityCombinations)
31+
{
32+
for (bool typed = false, done = false; !done; (typed, done) = (!typed, typed || done))
33+
{
34+
Console.WriteLine(Writer.TypeName(mutabilityCombination));
35+
}
36+
}
37+
}
38+
39+
class Writer : IDisposable
40+
{
41+
private const string PREAMBLE = @"""// Licensed to the .NET Foundation under one or more agreements.
42+
// The .NET Foundation licenses this file to you under the MIT license.
43+
44+
// ============================================= THIS FILE IS AUTOGENERATED ============================================
45+
// ================================ Please make any edits in eng/pointergen/Program.cs! ================================
46+
// ============================================= THIS FILE IS AUTOGENERATED ============================================
47+
48+
using System.Diagnostics.CodeAnalysis;
49+
using System.Runtime.CompilerServices;
50+
using System.Runtime.InteropServices;
51+
using System.Text;
52+
using InlineIL;
53+
54+
namespace Silk.NET.Core;
55+
56+
""";
57+
58+
private StreamWriter _sw;
59+
private string _typeName;
60+
private bool[] _mutabilityMatrix;
61+
private bool _isTyped;
62+
private string _indent = string.Empty;
63+
64+
public Writer(bool[] mutabilityMatrix, bool typed)
65+
{
66+
_typeName = TypeName(mutabilityMatrix);
67+
_sw = new StreamWriter($"../../sources/Core/Pointers/{_typeName}{(typed ? "`1" : string.Empty)}.gen.cs");
68+
_mutabilityMatrix = mutabilityMatrix;
69+
_isTyped = typed;
70+
_sw.WriteLine(PREAMBLE);
71+
}
72+
73+
public void WriteLine(string chars) => _sw.WriteLine(_indent + chars);
74+
75+
public void Write()
76+
{
77+
WriteLine(
78+
$"public partial readonly ref struct {_typeName}{(_isTyped ? "<T> where T: unmanaged" : string.Empty)}");
79+
WriteLine("{");
80+
_indent += " ";
81+
WriteLine("/// <summary>");
82+
WriteLine("/// The underlying reference.");
83+
WriteLine("/// </summary>");
84+
var roRef = _mutabilityMatrix[0] ? string.Empty : " readonly";
85+
var inOrRef = _mutabilityMatrix[0] ? "ref" : "in";
86+
var lesser = _mutabilityMatrix.Length == 1
87+
? string.Empty
88+
: $"{TypeName(_mutabilityMatrix.AsSpan()[1..])}{(_isTyped ? "<T>" : string.Empty)}";
89+
WriteLine(_mutabilityMatrix.Length > 1 || !_isTyped
90+
? $"public readonly ref{roRef} byte InteriorRef;"
91+
: $"public readonly ref{roRef} T Ref;\n");
92+
93+
WriteLine("/// <summary>");
94+
WriteLine("/// Creates a pointer with the given underlying ref.");
95+
WriteLine("/// </summary>");
96+
WriteLine("/// <param name=\"ref\">The underlying ref.</param>");
97+
var thisTypeRef = _isTyped
98+
? $"TypeRef.Type(typeof({_typeName}<>).MakeGenericType(typeof(T)))"
99+
: $"TypeRef.Type(typeof({_typeName}))";
100+
if (_mutabilityMatrix.Length == 1)
101+
{
102+
WriteLine(_isTyped
103+
? $"public {_typeName}({inOrRef} T @ref) => Ref = ref Unsafe.AsRef(in @ref);\n"
104+
: $"private {_typeName}({inOrRef} byte @ref) => InteriorRef = ref Unsafe.AsRef(in @ref);\n");
105+
}
106+
else
107+
{
108+
WriteLine($"public {_typeName}({inOrRef} {lesser} @ref)");
109+
WriteLine("{");
110+
_indent += " ";
111+
WriteLine("IL.Emit.Ldarg_0();");
112+
WriteLine("IL.Emit.Ldarg_1();");
113+
WriteLine($"IL.Emit.Stfld(FieldRef.Field({thisTypeRef}, nameof(InteriorRef))));");
114+
WriteLine("IL.Emit.Ret();");
115+
WriteLine("throw IL.Unreachable();");
116+
_indent = _indent[..^4];
117+
WriteLine("}\n");
118+
}
119+
120+
WriteLine("/// <summary>");
121+
WriteLine("/// Creates a pointer with the given underlying ref.");
122+
WriteLine("/// </summary>");
123+
WriteLine("/// <param name=\"ref\">The underlying ref.</param>");
124+
WriteLine("/// <returns>The created pointer.</returns>");
125+
if (_mutabilityMatrix.Length == 1)
126+
{
127+
WriteLine($"public static {_typeName}{(_isTyped ? "<T>" : string.Empty)} " +
128+
$"Create{(_isTyped ? string.Empty : "<T>")}({inOrRef} T @ref)" +
129+
(_isTyped
130+
? $" => new({inOrRef} @ref);\n"
131+
: $" => new({inOrRef} Unsafe.As<T, byte>(ref Unsafe.AsRef(in @ref)));\n"));
132+
}
133+
else
134+
{
135+
WriteLine(
136+
$"public static {_typeName}{(_isTyped ? "<T>" : string.Empty)} Create" +
137+
$"({inOrRef} {lesser} @ref) " +
138+
$"=> new({inOrRef} @ref);\n");
139+
}
140+
141+
WriteLine("/// <summary>");
142+
WriteLine("/// Gets the underlying reference.");
143+
WriteLine("/// </summary>");
144+
WriteLine("/// <returns>The underlying reference.</returns>");
145+
WriteLine("/// <remarks>");
146+
WriteLine($"/// This function allows a <see cref=\"{_typeName}{(_isTyped ? "{T}" : string.Empty)}\"/> " +
147+
"to be used in a <c>fixed</c> statement.");
148+
WriteLine("/// </remarks>");
149+
if (_mutabilityMatrix.Length == 1)
150+
{
151+
WriteLine($"public ref{(_mutabilityMatrix[0] ? string.Empty : " readonly")} " +
152+
$"{(_isTyped ? "<T>" : string.Empty)} GetPinnableReference() => ref " +
153+
$"{(_isTyped ? "Ref" : "InteriorRef")};\n");
154+
}
155+
else
156+
{
157+
WriteLine(
158+
$"public ref{(_mutabilityMatrix[0] ? string.Empty : " readonly")} " +
159+
$"{(_isTyped ? "T" : "void")}{new string('*', _mutabilityMatrix.Length - 1)} GetPinnableReference()");
160+
WriteLine("{");
161+
_indent += " ";
162+
WriteLine("IL.Emit.Ldarg_0();");
163+
WriteLine($"IL.Emit.Ldfld(FieldRef.Field({thisTypeRef}, nameof(InteriorRef))));");
164+
WriteLine("IL.Emit.Ret();");
165+
WriteLine("throw IL.Unreachable();");
166+
_indent = _indent[..^4];
167+
WriteLine("}\n");
168+
}
169+
170+
}
171+
172+
internal static string TypeName(ReadOnlySpan<bool> mutabilityMatrix)
173+
{
174+
Span<char> ret = stackalloc char[mutabilityMatrix.Length * 3];
175+
for (var i = 0; i < mutabilityMatrix.Length; i++)
176+
{
177+
(mutabilityMatrix[i] ? "Mut" : "Ptr").CopyTo(ret[(i * 3)..((i + 1) * 3)]);
178+
}
179+
180+
return ret.ToString();
181+
}
182+
183+
public void Dispose() => _sw.Dispose();
184+
}

eng/pointergen/pointergen.csproj

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<RootNamespace>PointerGenerator</RootNamespace>
9+
</PropertyGroup>
10+
11+
</Project>

0 commit comments

Comments
 (0)