Skip to content

Commit

Permalink
WIP
Browse files Browse the repository at this point in the history
  • Loading branch information
kekyo committed Apr 30, 2024
1 parent 9337569 commit eef39e0
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -29,21 +29,6 @@

// =============== CLASS MEMBERS DECLARATION ===================

.class private abstract auto ansi sealed beforefieldinit '<source>$'
extends [mscorlib]System.Object
{
.method public static vararg int32 va(int32 A_0,
int64 A_1,
float64 A_2) cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: ldc.i4.s 123
IL_0002: ret
} // end of method '<source>$'::va

} // end of class '<source>$'

.class public abstract auto ansi sealed C.text
extends [mscorlib]System.Object
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@

// .NET IL Disassembler. Version 8.0.0



// Metadata version: v4.0.30319
.assembly extern mscorlib
{
.publickey = (00 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 )
.ver 4:0:0:0
}
.assembly output
{
.custom instance void [mscorlib]System.Runtime.Versioning.TargetFrameworkAttribute::.ctor(string) = ( 01 00 1A 2E 4E 45 54 46 72 61 6D 65 77 6F 72 6B // ....NETFramework
2C 56 65 72 73 69 6F 6E 3D 76 34 2E 35 00 00 ) // ,Version=v4.5..
.ver 1:0:0:0
}
.module output.dll
.imagebase 0x00400000
.file alignment 0x00000200
.stackreserve 0x00100000
.subsystem 0x0003 // WINDOWS_CUI
.corflags 0x00000001 // ILONLY


// =============== CLASS MEMBERS DECLARATION ===================

.class private abstract auto ansi sealed beforefieldinit '<source>$'
extends [mscorlib]System.Object
{
.method public static vararg int32 va(int32 A_0,
int64 A_1,
float64 A_2) cil managed
{
// Code size 3 (0x3)
.maxstack 8
IL_0000: ldc.i4.s 123
IL_0002: ret
} // end of method '<source>$'::va

} // end of class '<source>$'

.class public abstract auto ansi sealed C.text
extends [mscorlib]System.Object
{
.method public static int32 main() cil managed
{
// Code size 32 (0x20)
.maxstack 8
IL_0000: ldc.i4.s 123
IL_0002: ldc.i8 0x1c8
IL_000b: ldc.r8 123.456
IL_0014: ldstr "ABC"
IL_0019: ldc.i4.1
IL_001a: call vararg int32 '<source>$'::va(int32,
int64,
float64,
...,
string,
bool)
IL_001f: ret
} // end of method text::main

} // end of class C.text


// =============================================================

// *********** DISASSEMBLY COMPLETE ***********************
15 changes: 15 additions & 0 deletions chibild/chibild.core.Tests/LinkerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1104,6 +1104,21 @@ call ret3

[Test]
public Task CallCAbiTargetFunctionWithVariadic()
{
var actual = Run(@"
.function public int32() main
ldc.i4.s 123
ldc.i8 456
ldc.r8 123.456
ldstr ""ABC""
ldc.i4.1
call int32(int32,int64,float64,string,bool) va
ret");
return Verify(actual);
}

[Test]
public Task CallCAbiTargetFunctionWithVariadicSameObject()
{
var actual = Run(@"
.function public int32() main
Expand Down
146 changes: 75 additions & 71 deletions chibild/chibild.core/Generating/AssemblyInputFragment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,63 +17,16 @@
using System.Linq;
using System.Threading.Tasks;
using System.Diagnostics;
using MethodCallingConvention = Mono.Cecil.MethodCallingConvention;

namespace chibild.Generating;

internal sealed class AssemblyInputFragment :
InputFragment
{
// Partial matching function naming and signatures.
private sealed class FunctionKey :
IEquatable<FunctionKey>
{
public readonly string Name;
public readonly FunctionSignatureNode? Signature;

public FunctionKey(string name, FunctionSignatureNode? signature)
{
this.Name = name;
this.Signature = signature;
}

public bool Equals(FunctionKey? other)
{
// Required match naming.
if (other is { } rhs && this.Name.Equals(rhs.Name))
{
// Compare signature when both defined.
if (this.Signature is { } ls && rhs.Signature is { } rs)
{
// Signature equality.
return ls.Equals(rs);
}
else
{
// If one of or all are null, it is assumed to be a match.
return true;
}
}
return false;
}

public override bool Equals(object? obj) =>
obj is FunctionKey rhs && this.Equals(rhs);

public override int GetHashCode() =>
this.Name.GetHashCode() ^
this.Signature?.GetHashCode() ?? 0;

public override string ToString() =>
this.Signature is { } signature ?
$"{signature.ReturnType} {this.Name}({string.Join(",", (object[])signature.Parameters)})" :
$"{this.Name}()";
}

//////////////////////////////////////////////////////////////

private readonly Dictionary<string, TypeDefinition> types;
private readonly Dictionary<string, FieldDefinition> fields;
private readonly Dictionary<FunctionKey, MethodDefinition> methods;
private readonly Dictionary<string, MethodDefinition[]> methods;
private readonly Dictionary<string, ModuleDefinition> resolvedModules = new();

private AssemblyInputFragment(
Expand All @@ -82,7 +35,7 @@ private AssemblyInputFragment(
AssemblyDefinition assembly,
Dictionary<string, TypeDefinition> types,
Dictionary<string, FieldDefinition> fields,
Dictionary<FunctionKey, MethodDefinition> methods) :
Dictionary<string, MethodDefinition[]> methods) :
base(baseInputPath, relativePath)
{
this.Assembly = assembly;
Expand All @@ -106,14 +59,52 @@ public override bool ContainsVariableAndSchedule(
IdentityNode variable) =>
this.fields.ContainsKey(variable.Identity);

public override bool ContainsFunctionAndSchedule(
IdentityNode function,
FunctionSignatureNode? signature)
private static bool TryGetMatchedMethodIndex(
FunctionSignatureNode signature,
MethodDefinition[] overloads,
out int index)
{
var key = new FunctionKey(function.Identity, signature);
return this.methods.ContainsKey(key);
for (index = 0; index < overloads.Length; index++)
{
var overload = overloads[index];

// Match exactly.
if (overload.Parameters.
Select(p => p.ParameterType.FullName).
SequenceEqual(signature.Parameters.Select(p => p.ParameterType.CilTypeName)))
{
return true;
}
}

for (index = 0; index < overloads.Length; index++)
{
var overload = overloads[index];

// Match partially when overload is variadic.
if (overload.CallingConvention == MethodCallingConvention.VarArg)
{
if (overload.Parameters.
Select(p => p.ParameterType.FullName).
SequenceEqual(signature.Parameters.
Take(overload.Parameters.Count).
Select(p => p.ParameterType.CilTypeName)))
{
return true;
}
}
}

index = -1;
return false;
}

public override bool ContainsFunctionAndSchedule(
IdentityNode function,
FunctionSignatureNode? signature) =>
this.methods.TryGetValue(function.Identity, out var overloads) &&
(signature == null || TryGetMatchedMethodIndex(signature, overloads, out _));

//////////////////////////////////////////////////////////////

private ModuleDefinition ResovleOnFallbackModule(
Expand Down Expand Up @@ -200,25 +191,39 @@ public override bool TryGetMethod(
ModuleDefinition fallbackModule,
out MethodReference mr)
{
var key = new FunctionKey(function.Identity, signature);
if (this.methods.TryGetValue(key, out var md))
if (!this.methods.TryGetValue(function.Identity, out var overloads))
{
mr = null!;
return false;
}

// Resolve on fallback assembly resolver.
MethodReference ResolveOnFallbackModule(MethodDefinition md)
{
// Resolve on fallback assembly resolver.
var exactModule = this.ResovleOnFallbackModule(fallbackModule, md);
if (exactModule.GetType(md.DeclaringType.FullName) is { } ftd &&
ftd.Methods.FirstOrDefault(m => CecilUtilities.Equals(m, md)) is { } fmd)
{
this.methods[key] = fmd;

mr = fmd;
return true;
return fmd;
}
else
{
Debug.Fail($"Could not resolve a method on fallback assembly: {md.DeclaringType.FullName}.{md.Name}({signature})");
return md;
}
}

if (signature == null)
{
mr = ResolveOnFallbackModule(overloads[0]);
return true;
}
if (TryGetMatchedMethodIndex(signature, overloads, out var index))
{
mr = ResolveOnFallbackModule(overloads[index]);
return true;
}

mr = null!;
return false;
}
Expand Down Expand Up @@ -308,20 +313,19 @@ static IEnumerable<TypeDefinition> IterateTypesDescendants(TypeDefinition type)
Where(method =>
method.IsStatic &&
method.DeclaringType.FullName is "C.text").
Select(method =>
(
key: new FunctionKey(method.Name, null),
method
)).
Select(method => (name: method.Name, method)).
Concat(targetMethods.
Select(method =>
(
key: new FunctionKey(
$"{method.DeclaringType.FullName}.{method.Name}",
TypeGenerator.GetFunctionSignature(method)),
name: $"{method.DeclaringType.FullName}.{method.Name}",
method
))).
ToDictionary(entry => entry.key, entry => entry.method);
GroupBy(
entry => entry.name,
entry => entry.method).
ToDictionary(
g => g.Key,
g => g.OrderByDescending(method => method.Parameters.Count).ToArray());

return new(
baseInputPath,
Expand Down
4 changes: 3 additions & 1 deletion chibild/chibild.core/Generating/CodeGenerator_Consumer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -470,7 +470,9 @@ private void ConsumeInstructions(
foreach (var parameter in m.Parameters)
{
emr.Parameters.Add(new(
parameter.Name, parameter.Attributes, parameter.ParameterType));
parameter.Name,
parameter.Attributes,
parameter.ParameterType));
}

// Append sentinel parameters each lack parameter types.
Expand Down
Loading

0 comments on commit eef39e0

Please sign in to comment.