Skip to content

Commit

Permalink
(#132) CodeGen: refactoring: dynamic type size expression
Browse files Browse the repository at this point in the history
  • Loading branch information
ForNeVeR committed Dec 3, 2022
1 parent 523f62d commit fcb437e
Show file tree
Hide file tree
Showing 15 changed files with 78 additions and 30 deletions.
2 changes: 2 additions & 0 deletions Cesium.CodeGen/Contexts/AssemblyContext.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ namespace Cesium.CodeGen.Contexts;
public class AssemblyContext
{
internal AssemblyDefinition Assembly { get; }
public TargetArchitectureSet ArchitectureSet { get; }
internal AssemblyDefinition MscorlibAssembly { get; }
internal AssemblyDefinition CesiumRuntimeAssembly { get; }
public ModuleDefinition Module { get; }
Expand Down Expand Up @@ -75,6 +76,7 @@ private AssemblyContext(
CompilationOptions compilationOptions)
{
Assembly = assembly;
ArchitectureSet = compilationOptions.TargetArchitectureSet;
Module = module;
MscorlibAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CorelibAssembly);
CesiumRuntimeAssembly = AssemblyDefinition.ReadAssembly(compilationOptions.CesiumRuntime);
Expand Down
2 changes: 1 addition & 1 deletion Cesium.CodeGen/Contexts/FunctionScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ internal record FunctionScope(TranslationUnitContext Context, FunctionInfo Funct
{
public AssemblyContext AssemblyContext => Context.AssemblyContext;
public ModuleDefinition Module => Context.Module;
public TypeSystem TypeSystem => Context.TypeSystem;
public CTypeSystem CTypeSystem => Context.CTypeSystem;
public TargetArchitectureSet ArchitectureSet => AssemblyContext.ArchitectureSet;
public IReadOnlyDictionary<string, FunctionInfo> Functions => Context.Functions;
public FunctionInfo? GetFunctionInfo(string identifier) =>
Functions.GetValueOrDefault(identifier);
Expand Down
3 changes: 1 addition & 2 deletions Cesium.CodeGen/Contexts/GlobalConstructorScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,9 @@ internal record GlobalConstructorScope(TranslationUnitContext Context) : IEmitSc
private MethodDefinition? _method;
public AssemblyContext AssemblyContext => Context.AssemblyContext;
public ModuleDefinition Module => Context.Module;
public TypeSystem TypeSystem => Module.TypeSystem;

public MethodDefinition Method => _method ??= Context.AssemblyContext.GetGlobalInitializer();
public CTypeSystem CTypeSystem => Context.CTypeSystem;
public TargetArchitectureSet ArchitectureSet => AssemblyContext.ArchitectureSet;
public FunctionInfo? GetFunctionInfo(string identifier) =>
Context.Functions.GetValueOrDefault(identifier);
public IReadOnlyDictionary<string, IType> GlobalFields => AssemblyContext.GlobalFields;
Expand Down
1 change: 1 addition & 0 deletions Cesium.CodeGen/Contexts/IDeclarationScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ namespace Cesium.CodeGen.Contexts;

internal interface IDeclarationScope
{
TargetArchitectureSet ArchitectureSet { get; }
CTypeSystem CTypeSystem { get; }
FunctionInfo? GetFunctionInfo(string identifier);
IReadOnlyDictionary<string, IType> GlobalFields { get; }
Expand Down
2 changes: 2 additions & 0 deletions Cesium.CodeGen/Contexts/LoopScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ internal record LoopScope(IEmitScope Parent) : IEmitScope, IDeclarationScope
public AssemblyContext AssemblyContext => Parent.AssemblyContext;
public ModuleDefinition Module => Parent.Module;
public CTypeSystem CTypeSystem => Parent.CTypeSystem;
public TargetArchitectureSet ArchitectureSet => AssemblyContext.ArchitectureSet;

public FunctionInfo? GetFunctionInfo(string identifier)
=> ((IDeclarationScope)Parent).GetFunctionInfo(identifier);
public TranslationUnitContext Context => Parent.Context;
Expand Down
1 change: 1 addition & 0 deletions Cesium.CodeGen/Contexts/SwitchScope.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ internal record SwitchScope(IEmitScope Parent) : IEmitScope, IDeclarationScope
public AssemblyContext AssemblyContext => Parent.AssemblyContext;
public ModuleDefinition Module => Parent.Module;
public CTypeSystem CTypeSystem => Parent.CTypeSystem;
public TargetArchitectureSet ArchitectureSet => AssemblyContext.ArchitectureSet;
public FunctionInfo? GetFunctionInfo(string identifier)
=> ((IDeclarationScope)Parent).GetFunctionInfo(identifier);
public TranslationUnitContext Context => Parent.Context;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System.Diagnostics;
using Cesium.CodeGen.Contexts;
using Cesium.CodeGen.Extensions;
using Cesium.CodeGen.Ir.Expressions.Constants;
using Cesium.CodeGen.Ir.Types;
using Cesium.Core;
using Mono.Cecil.Cil;
Expand Down Expand Up @@ -36,7 +35,7 @@ public override IExpression Lower(IDeclarationScope scope)
right = new TypeCastExpression(
scope.CTypeSystem.NativeInt,
new ArithmeticBinaryOperatorExpression(
new ConstantLiteralExpression(new IntegerConstant(leftPointerType.Base.SizeInBytes)),
leftPointerType.Base.GetSizeInBytesExpression(scope.ArchitectureSet),
BinaryOperator.Multiply,
right));

Expand All @@ -48,7 +47,7 @@ public override IExpression Lower(IDeclarationScope scope)
left = new TypeCastExpression(
scope.CTypeSystem.NativeInt,
new ArithmeticBinaryOperatorExpression(
new ConstantLiteralExpression(new IntegerConstant(rightPointerType.Base.SizeInBytes)),
rightPointerType.Base.GetSizeInBytesExpression(scope.ArchitectureSet),
BinaryOperator.Multiply,
left));

Expand Down
4 changes: 3 additions & 1 deletion Cesium.CodeGen/Ir/Types/ConstType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,7 @@ namespace Cesium.CodeGen.Ir.Types;
internal record ConstType(IType Base) : IType
{
public TypeReference Resolve(TranslationUnitContext context) => Base.Resolve(context);
public int SizeInBytes => Base.SizeInBytes;

public int? GetSizeInBytes(TargetArchitectureSet arch) =>
Base.GetSizeInBytes(arch);
}
3 changes: 2 additions & 1 deletion Cesium.CodeGen/Ir/Types/FunctionType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ public TypeReference ResolvePointer(TranslationUnitContext context)
return pointer;
}

public int SizeInBytes => throw new AssertException($"Function type {this} has no defined size.");
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
throw new AssertException($"Function type {this} has no defined size.");

public override string ToString() =>
$"FunctionType {{ {nameof(Parameters)} = {Parameters}, {nameof(ReturnType)} = {ReturnType} }}";
Expand Down
33 changes: 32 additions & 1 deletion Cesium.CodeGen/Ir/Types/IType.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using Cesium.CodeGen.Contexts;
using Cesium.CodeGen.Ir.Expressions;
using Cesium.CodeGen.Ir.Expressions.Constants;
using Cesium.Core;
using Mono.Cecil;

namespace Cesium.CodeGen.Ir.Types;
Expand All @@ -15,7 +18,35 @@ internal interface IType
FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDefinition ownerType, string fieldName) =>
new(fieldName, FieldAttributes.Public, Resolve(context));

int SizeInBytes { get; }
/// <summary>Determines the size of an object of this type in bytes, if possible.</summary>
/// <param name="arch">Target architecture set.</param>
/// <returns>The size if it was possible to determine. <c>null</c> otherwise.</returns>
/// <remarks>
/// <para>
/// Certain types (mostly pointers and derivatives) may not be able to determine their size in dynamic
/// architecture.
/// </para>
/// <para>
/// In this case, only <see cref="GetSizeInBytesExpression"/> would be available to get the size dynamically.
/// </para>
/// </remarks>
int? GetSizeInBytes(TargetArchitectureSet arch);

/// <summary>
/// Generates an expression to determine the type's size in bytes in runtime. This expression will leave the only
/// value of size (32-bit integer) on the execution stack.
/// </summary>
/// <remarks>For simple types, will emit a constant.</remarks>
IExpression GetSizeInBytesExpression(TargetArchitectureSet arch)
{
var size = GetSizeInBytes(arch);
if (size == null)
throw new AssertException(
$"Cannot determine static size of type {this}, " +
$"and {nameof(GetSizeInBytesExpression)} method for dynamic calculation is not overridden.");

return new ConstantLiteralExpression(new IntegerConstant(size.Value));
}
}

/// <summary>A generated type, i.e. a type that has some bytecode to be generated once.</summary>
Expand Down
26 changes: 17 additions & 9 deletions Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,15 @@ public TypeReference Resolve(TranslationUnitContext context)

public FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDefinition ownerType, string fieldName)
{
var arch = context.AssemblyContext.ArchitectureSet;
var size = GetSizeInBytes(arch);
if (size == null)
throw new CompilationException(
$"Cannot statically determine a size of type {this} for architecture set {arch}. " +
$"This size is required to generate a field {fieldName} inside of a type {ownerType}.");

var itemType = Base.Resolve(context);
var bufferType = CreateFixedBufferType(context.Module, itemType, fieldName);
var bufferType = CreateFixedBufferType(context.Module, itemType, fieldName, size.Value);
ownerType.NestedTypes.Add(bufferType);

return new FieldDefinition(fieldName, FieldAttributes.Public, bufferType)
Expand All @@ -44,18 +51,17 @@ CustomAttribute GenerateCustomFieldAttribute()
ConstructorArguments =
{
new CustomAttributeArgument(context.Module.ImportReference(context.AssemblyContext.MscorlibAssembly.GetType("System.Type")), itemType),
new CustomAttributeArgument(context.TypeSystem.Int32, SizeInBytes)
new CustomAttributeArgument(context.TypeSystem.Int32, size.Value)
}
};
}
}

public void EmitInitializer(IEmitScope scope)
{
var arraySizeInBytes = SizeInBytes;

var method = scope.Method.Body.GetILProcessor();
method.Emit(OpCodes.Ldc_I4, arraySizeInBytes);
var expression = ((IType)this).GetSizeInBytesExpression(scope.AssemblyContext.ArchitectureSet);
expression.EmitTo(scope);
method.Emit(OpCodes.Conv_U);
if (scope is GlobalConstructorScope)
{
Expand All @@ -68,12 +74,14 @@ public void EmitInitializer(IEmitScope scope)
}
}

public int SizeInBytes => Base.SizeInBytes * Size;
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
Base.GetSizeInBytes(arch) * Size;

private TypeDefinition CreateFixedBufferType(
private static TypeDefinition CreateFixedBufferType(
ModuleDefinition module,
TypeReference fieldType,
string fieldName)
string fieldName,
int sizeInBytes)
{
// An example of what C# does for fixed int x[20]:
//
Expand All @@ -98,7 +106,7 @@ private TypeDefinition CreateFixedBufferType(
module.ImportReference(typeof(ValueType)))
{
PackingSize = 0,
ClassSize = SizeInBytes,
ClassSize = sizeInBytes,
CustomAttributes = { compilerGeneratedAttribute, unsafeValueTypeAttribute },
Fields = { new FieldDefinition("FixedElementField", FieldAttributes.Public, fieldType) }
};
Expand Down
6 changes: 1 addition & 5 deletions Cesium.CodeGen/Ir/Types/NamedType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@ public record NamedType(string TypeName) : IType
public TypeReference Resolve(TranslationUnitContext context) =>
throw new AssertException($"Type {TypeName} was never resolved.");

public int SizeInBytes =>
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
throw new AssertException($"Type {TypeName} was never resolved.");

// explicit impl while Size not implemented
public override string ToString()
=> $"NamedType {{ TypeName = {TypeName} }}";
}
7 changes: 2 additions & 5 deletions Cesium.CodeGen/Ir/Types/PointerType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,6 @@ public virtual TypeReference Resolve(TranslationUnitContext context)
return Base.Resolve(context).MakePointerType();
}

public virtual int SizeInBytes => throw new WipException(132, "Could not calculate size yet.");

// explicit impl while Size not implemented
public override string ToString()
=> $"PointerType {{ Base = {Base} }}";
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
throw new WipException(132, "Could not calculate size yet.");
}
10 changes: 9 additions & 1 deletion Cesium.CodeGen/Ir/Types/PrimitiveType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ public TypeReference Resolve(TranslationUnitContext context)
};
}

public int SizeInBytes =>
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
Kind switch
{
// Basic
Expand Down Expand Up @@ -132,6 +132,14 @@ public TypeReference Resolve(TranslationUnitContext context)
PrimitiveTypeKind.SignedLongLongInt => 8,
PrimitiveTypeKind.LongDouble => 8,

PrimitiveTypeKind.NativeInt => arch switch
{
TargetArchitectureSet.Dynamic => null,
TargetArchitectureSet.Bit32 => 4,
TargetArchitectureSet.Bit64 => 8,
_ => throw new AssertException($"Architecture set not supported: {arch}.")
},

_ => throw new AssertException($"Could not calculate size for {Kind}."),
};

Expand Down
3 changes: 2 additions & 1 deletion Cesium.CodeGen/Ir/Types/StructType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ public TypeDefinition Emit(string name, TranslationUnitContext context)
public TypeReference Resolve(TranslationUnitContext context) =>
context.GetTypeReference(this) ?? throw new CompilationException($"Type {this} was not found.");

public int SizeInBytes => throw new WipException(232, $"Could not calculate size for {this} yet.");
public int? GetSizeInBytes(TargetArchitectureSet arch) =>
throw new WipException(232, $"Could not calculate size for {this} yet.");
}

0 comments on commit fcb437e

Please sign in to comment.