Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

InteropGen Refactor #64

Merged
merged 32 commits into from
Sep 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
f60d90b
Bail from interop if no args are provided
peter-r-g Sep 10, 2024
faf497e
Use arrays instead of lists in ThreadDispatcher
peter-r-g Sep 10, 2024
7858c51
Seal ThreadDispatcher<T>
peter-r-g Sep 10, 2024
b622ba3
Use atomic operation for _threadsCompleted
peter-r-g Sep 10, 2024
1f214e7
Make StopwatchLog a ref struct
peter-r-g Sep 10, 2024
bac2a98
Slight rework of parsing units
peter-r-g Sep 10, 2024
839fdff
Small optimizations to Parser.GetUnits
peter-r-g Sep 10, 2024
7c09269
Apply formatting fixes
peter-r-g Sep 10, 2024
5407dcc
Minor change
peter-r-g Sep 10, 2024
f86dc59
Use array instead of list in ManagedCodeGenerator
peter-r-g Sep 10, 2024
b88847c
Use collection expressions
peter-r-g Sep 10, 2024
0ab2755
Apply formatting fixes
peter-r-g Sep 10, 2024
76f4173
Move log to extension
peter-r-g Sep 10, 2024
bfdedc6
Backport TaskPool from Mocha 2
peter-r-g Sep 12, 2024
4453063
Refactor TaskPool
peter-r-g Sep 12, 2024
fb4903b
Remove InteropGen ThreadDispatcher
peter-r-g Sep 12, 2024
4e0b5f0
Reference Mocha.Common in InteropGen
peter-r-g Sep 12, 2024
88b3ce4
Fix not generating UnmanagedArgs for namespaces
peter-r-g Sep 12, 2024
1b135f7
Minor change
peter-r-g Sep 12, 2024
a5ec4fe
Bump logging NuGet packages
peter-r-g Sep 12, 2024
72867b1
Make Utils lookup table frozen
peter-r-g Sep 12, 2024
5fa682b
Use Path combining instead of hardcoded paths
peter-r-g Sep 12, 2024
1d8cd85
Revert "Make StopwatchLog a ref struct"
peter-r-g Sep 12, 2024
c1bba75
Use async methods for entry point
peter-r-g Sep 12, 2024
1f25db5
Minor change
peter-r-g Sep 12, 2024
14c5608
Fix leftover hardcoded path
peter-r-g Sep 12, 2024
26333f2
Apply formatting fixes
peter-r-g Sep 12, 2024
ed1d7c6
Add a couple performance traces
peter-r-g Sep 13, 2024
8b4aee5
Skip parsing function bodies
peter-r-g Sep 13, 2024
98dc215
Fix InteropGen getting stuck in an unusable state
peter-r-g Sep 13, 2024
831e677
Rewrite parsing files
peter-r-g Sep 13, 2024
4aa189c
Seal TaskPool
peter-r-g Sep 13, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 62 additions & 0 deletions Source/Mocha.Common/Utils/TaskPool.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
namespace Mocha.Common;

public sealed class TaskPool<T>
{
public delegate Task TaskCallback( T[] taskQueue );
public delegate void Continuation();

private readonly Task[] _tasks;

private TaskPool( T[] queue, TaskCallback taskStart )
{
var maxTasks = (int)(Environment.ProcessorCount * 0.5) - 1;
var batchSize = queue.Length / maxTasks;

if ( batchSize == 0 )
batchSize = 1;

var batched = queue
.Select( ( value, index ) => new
{
Value = value,
Index = index
} )
.GroupBy( p => p.Index / batchSize )
.Select( g => g.Select( p => p.Value ).ToArray() )
.ToArray();

_tasks = new Task[batched.Length];
for ( int i = 0; i < batched.Length; i++ )
{
var taskQueue = batched[i];

_tasks[i] = Task.Run( () => taskStart( taskQueue ) );
}
}

public static TaskPool<T> Dispatch( T[] queue, TaskCallback taskStart )
{
return new TaskPool<T>( queue, taskStart );
}

public static TaskPool<T> Dispatch( IEnumerable<T> queue, TaskCallback taskStart )
{
return new TaskPool<T>( queue.ToArray(), taskStart );
}

public TaskPool<T> Then( Continuation continuation )
{
Task.WhenAll( _tasks ).ContinueWith( t => continuation() ).Wait();
return this;
}

public async Task WaitForCompleteAsync()
{
await Task.WhenAll( _tasks );
}

public void WaitForComplete()
{
WaitForCompleteAsync().Wait();
}
}
20 changes: 11 additions & 9 deletions Source/MochaTool.InteropGen/CodeGen/ManagedCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,8 @@ internal static string GenerateCode( IEnumerable<IUnit> units )
{
switch ( unit )
{
case Class c when c.IsNamespace:
GenerateNamespaceCode( writer, c );
case Namespace n:
GenerateNamespaceCode( writer, n );
break;
case Class c:
GenerateClassCode( writer, c );
Expand Down Expand Up @@ -95,10 +95,11 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
//
// Gather everything we need into nice lists
//
List<string> decls = new();
var decls = new string[c.Methods.Length];

foreach ( var method in c.Methods )
for ( var i = 0; i < decls.Length; i++ )
{
var method = c.Methods[i];
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;

Expand Down Expand Up @@ -128,7 +129,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
// any parameters. The return type is the last type argument passed to
// the delegate.
//
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;" );
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{c.Name}_{name}MethodPtr;";
}

//
Expand Down Expand Up @@ -269,15 +270,16 @@ private static void GenerateStructCode( IndentedTextWriter writer, Struct s )
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="ns">The namespace to write code for.</param>
private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace ns )
{
//
// Gather everything we need into nice lists
//
List<string> decls = new();
var decls = new string[ns.Methods.Length];

foreach ( var method in ns.Methods )
for ( var i = 0; i < decls.Length; i++ )
{
var method = ns.Methods[i];
var returnType = Utils.GetManagedType( method.ReturnType );
var name = method.Name;

Expand All @@ -298,7 +300,7 @@ private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
// any parameters. The return type is the last type argument passed to
// the delegate.
//
decls.Add( $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;" );
decls[i] = $"private static {delegateSignature} _{name} = ({delegateSignature})Mocha.Common.Global.UnmanagedArgs.__{ns.Name}_{name}MethodPtr;";
}

//
Expand Down
15 changes: 7 additions & 8 deletions Source/MochaTool.InteropGen/CodeGen/NativeCodeGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,19 +38,18 @@ internal static string GenerateCode( string headerPath, IEnumerable<IUnit> units
writer.WriteLine();

writer.WriteLine( "#pragma once" );
writer.WriteLine( $"#include \"..\\{headerPath}\"" );
writer.WriteLine( $"#include \"{Path.Combine( "..", headerPath )}\"" );

writer.WriteLine();

foreach ( var unit in units )
{
if ( unit is not Class c )
continue;

if ( c.IsNamespace )
GenerateNamespaceCode( writer, c );
else
if ( unit is Namespace n )
GenerateNamespaceCode( writer, n );
else if ( unit is Class c )
GenerateClassCode( writer, c );
else
continue;

writer.WriteLine();
}
Expand Down Expand Up @@ -116,7 +115,7 @@ private static void GenerateClassCode( IndentedTextWriter writer, Class c )
/// </summary>
/// <param name="writer">The writer to append the code to.</param>
/// <param name="ns">The namespace to write code for.</param>
private static void GenerateNamespaceCode( IndentedTextWriter writer, Class ns )
private static void GenerateNamespaceCode( IndentedTextWriter writer, Namespace ns )
{
foreach ( var method in ns.Methods )
{
Expand Down
9 changes: 9 additions & 0 deletions Source/MochaTool.InteropGen/Extensions/ILoggerExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ internal static partial class ILoggerExtensions
Message = "Generating C# <--> C++ interop code..." )]
internal static partial void LogIntro( this ILogger logger );

/// <summary>
/// Logs an error about missing required args.
/// </summary>
/// <param name="logger">The <see cref="ILogger"/> instance to log to.</param>
[LoggerMessage( EventId = -1,
Level = LogLevel.Error,
Message = "The base directory to generate code from is required to run this tool" )]
internal static partial void LogIntroError( this ILogger logger );

/// <summary>
/// Logs a timed operation to the user.
/// </summary>
Expand Down
6 changes: 3 additions & 3 deletions Source/MochaTool.InteropGen/MochaTool.InteropGen.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,9 @@
<ItemGroup>
<PackageReference Include="ClangSharp" Version="17.0.1" />
<PackageReference Include="libClangSharp.runtime.win-x64" Version="17.0.4" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
</ItemGroup>

<ItemGroup>
Expand Down
36 changes: 11 additions & 25 deletions Source/MochaTool.InteropGen/Parsing/Class.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Represents a class or namespace in C++.
/// Represents a class in C++.
/// </summary>
internal sealed class Class : IUnit
internal sealed class Class : IContainerUnit
{
/// <inheritdoc/>
public string Name { get; }
/// <inheritdoc/>
public bool IsNamespace { get; }

/// <inheritdoc/>
public ImmutableArray<Variable> Fields { get; }
Expand All @@ -20,14 +18,12 @@ internal sealed class Class : IUnit
/// <summary>
/// Initializes a new instance of <see cref="Class"/>.
/// </summary>
/// <param name="name">The name of the class or namespace.</param>
/// <param name="isNamespace">Whether or not it is a class or namespace.</param>
/// <param name="name">The name of the class.</param>
/// <param name="fields">All of the fields that are contained.</param>
/// <param name="methods">All of the methods that are contained.</param>
private Class( string name, bool isNamespace, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
private Class( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
Name = name;
IsNamespace = isNamespace;

Fields = fields;
Methods = methods;
Expand All @@ -40,7 +36,7 @@ private Class( string name, bool isNamespace, in ImmutableArray<Variable> fields
/// <returns>A new instance of the <see cref="Class"/> with the fields given.</returns>
internal Class WithFields( in ImmutableArray<Variable> fields )
{
return new Class( Name, IsNamespace, fields, Methods );
return new Class( Name, fields, Methods );
}

/// <summary>
Expand All @@ -50,7 +46,7 @@ internal Class WithFields( in ImmutableArray<Variable> fields )
/// <returns>A new instance of the <see cref="Class"/> with the methods given.</returns>
internal Class WithMethods( in ImmutableArray<Method> methods )
{
return new Class( Name, IsNamespace, Fields, methods );
return new Class( Name, Fields, methods );
}

/// <inheritdoc/>
Expand All @@ -60,9 +56,9 @@ public override string ToString()
}

/// <inheritdoc/>
IUnit IUnit.WithFields( in ImmutableArray<Variable> fields ) => WithFields( fields );
IContainerUnit IContainerUnit.WithFields( in ImmutableArray<Variable> fields ) => WithFields( fields );
/// <inheritdoc/>
IUnit IUnit.WithMethods( in ImmutableArray<Method> methods ) => WithMethods( methods );
IContainerUnit IContainerUnit.WithMethods( in ImmutableArray<Method> methods ) => WithMethods( methods );

/// <summary>
/// Returns a new instance of <see cref="Class"/>.
Expand All @@ -71,20 +67,10 @@ public override string ToString()
/// <param name="fields">The fields contained in the class.</param>
/// <param name="methods">The methods contained in the class.</param>
/// <returns>A new instance of <see cref="Class"/>.</returns>
internal static Class NewClass( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
internal static Class Create( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
return new Class( name, false, fields, methods );
return new Class( name, fields, methods );
}

/// <summary>
/// Returns a new instance of <see cref="Class"/> as a namespace.
/// </summary>
/// <param name="name">The name of the namespace.</param>
/// <param name="fields">The fields contained in the namespace.</param>
/// <param name="methods">The methods contained in the namespace.</param>
/// <returns>A new instance of <see cref="Class"/> as a namespace.</returns>
internal static Class NewNamespace( string name, in ImmutableArray<Variable> fields, in ImmutableArray<Method> methods )
{
return new Class( name, true, fields, methods );
}

}
81 changes: 81 additions & 0 deletions Source/MochaTool.InteropGen/Parsing/ContainerBuilder.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
using System.Collections.Immutable;

namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Builds a representation of a C++ container.
/// </summary>
internal sealed class ContainerBuilder
{
/// <summary>
/// The type of container that is being built.
/// </summary>
internal ContainerType Type { get; }
/// <summary>
/// The name of the container.
/// </summary>
internal string Name { get; }

/// <summary>
/// Whether or not the container has any items within it.
/// </summary>
internal bool IsEmpty => fields.Count == 0 && methods.Count == 0;

private readonly ImmutableArray<Variable>.Builder fields;
private readonly ImmutableArray<Method>.Builder methods;

/// <summary>
/// Initializes a new instance of <see cref="ContainerBuilder"/>.
/// </summary>
/// <param name="type">The type of the container.</param>
/// <param name="name">The name of the container.</param>
internal ContainerBuilder( ContainerType type, string name )
{
Type = type;
Name = name;

fields = ImmutableArray.CreateBuilder<Variable>();
methods = ImmutableArray.CreateBuilder<Method>();
}

/// <summary>
/// Adds a new field to the container.
/// </summary>
/// <param name="field">The field to add.</param>
/// <returns>The same instance of <see cref="ContainerBuilder"/>.</returns>
internal ContainerBuilder AddField( Variable field )
{
fields.Add( field );
return this;
}

/// <summary>
/// Adds a new method to the container.
/// </summary>
/// <param name="method">The method to add.</param>
/// <returns>The same instance of <see cref="ContainerBuilder"/>.</returns>
internal ContainerBuilder AddMethod( Method method )
{
methods.Add( method );
return this;
}

/// <summary>
/// Constructs a new instance of the container.
/// </summary>
/// <returns>A new container instance that implements the <see cref="IContainerUnit"/> interface.</returns>
/// <exception cref="ArgumentOutOfRangeException">Thrown when trying to build a container with an invalid type.</exception>
internal IContainerUnit Build()
{
fields.Capacity = fields.Count;
methods.Capacity = methods.Count;

return Type switch
{
ContainerType.Class => Class.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
ContainerType.Namespace => Namespace.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
ContainerType.Struct => Struct.Create( Name, fields.MoveToImmutable(), methods.MoveToImmutable() ),
_ => throw new ArgumentOutOfRangeException()
};
}
}
12 changes: 12 additions & 0 deletions Source/MochaTool.InteropGen/Parsing/ContainerType.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace MochaTool.InteropGen.Parsing;

/// <summary>
/// Defines a type of container defined in C++.
/// </summary>
internal enum ContainerType : byte
{
Class,
Namespace,
Struct,
Invalid = 255
}
Loading
Loading