From fcb437ed336872a590c45edc0675f4292e71ce9b Mon Sep 17 00:00:00 2001 From: Friedrich von Never Date: Sat, 3 Dec 2022 21:21:38 +0100 Subject: [PATCH] (#132) CodeGen: refactoring: dynamic type size expression --- Cesium.CodeGen/Contexts/AssemblyContext.cs | 2 ++ Cesium.CodeGen/Contexts/FunctionScope.cs | 2 +- .../Contexts/GlobalConstructorScope.cs | 3 +- Cesium.CodeGen/Contexts/IDeclarationScope.cs | 1 + Cesium.CodeGen/Contexts/LoopScope.cs | 2 ++ Cesium.CodeGen/Contexts/SwitchScope.cs | 1 + .../ArithmeticBinaryOperatorExpression.cs | 5 ++- Cesium.CodeGen/Ir/Types/ConstType.cs | 4 ++- Cesium.CodeGen/Ir/Types/FunctionType.cs | 3 +- Cesium.CodeGen/Ir/Types/IType.cs | 33 ++++++++++++++++++- Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs | 26 ++++++++++----- Cesium.CodeGen/Ir/Types/NamedType.cs | 6 +--- Cesium.CodeGen/Ir/Types/PointerType.cs | 7 ++-- Cesium.CodeGen/Ir/Types/PrimitiveType.cs | 10 +++++- Cesium.CodeGen/Ir/Types/StructType.cs | 3 +- 15 files changed, 78 insertions(+), 30 deletions(-) diff --git a/Cesium.CodeGen/Contexts/AssemblyContext.cs b/Cesium.CodeGen/Contexts/AssemblyContext.cs index 0334c658..494d7b20 100644 --- a/Cesium.CodeGen/Contexts/AssemblyContext.cs +++ b/Cesium.CodeGen/Contexts/AssemblyContext.cs @@ -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; } @@ -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); diff --git a/Cesium.CodeGen/Contexts/FunctionScope.cs b/Cesium.CodeGen/Contexts/FunctionScope.cs index 0c6e5294..0d5086a2 100644 --- a/Cesium.CodeGen/Contexts/FunctionScope.cs +++ b/Cesium.CodeGen/Contexts/FunctionScope.cs @@ -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 Functions => Context.Functions; public FunctionInfo? GetFunctionInfo(string identifier) => Functions.GetValueOrDefault(identifier); diff --git a/Cesium.CodeGen/Contexts/GlobalConstructorScope.cs b/Cesium.CodeGen/Contexts/GlobalConstructorScope.cs index 245440a0..5f502623 100644 --- a/Cesium.CodeGen/Contexts/GlobalConstructorScope.cs +++ b/Cesium.CodeGen/Contexts/GlobalConstructorScope.cs @@ -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 GlobalFields => AssemblyContext.GlobalFields; diff --git a/Cesium.CodeGen/Contexts/IDeclarationScope.cs b/Cesium.CodeGen/Contexts/IDeclarationScope.cs index 830e1482..d00dc892 100644 --- a/Cesium.CodeGen/Contexts/IDeclarationScope.cs +++ b/Cesium.CodeGen/Contexts/IDeclarationScope.cs @@ -7,6 +7,7 @@ namespace Cesium.CodeGen.Contexts; internal interface IDeclarationScope { + TargetArchitectureSet ArchitectureSet { get; } CTypeSystem CTypeSystem { get; } FunctionInfo? GetFunctionInfo(string identifier); IReadOnlyDictionary GlobalFields { get; } diff --git a/Cesium.CodeGen/Contexts/LoopScope.cs b/Cesium.CodeGen/Contexts/LoopScope.cs index 703beaa4..1248d79b 100644 --- a/Cesium.CodeGen/Contexts/LoopScope.cs +++ b/Cesium.CodeGen/Contexts/LoopScope.cs @@ -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; diff --git a/Cesium.CodeGen/Contexts/SwitchScope.cs b/Cesium.CodeGen/Contexts/SwitchScope.cs index 89477b5f..eff89b55 100644 --- a/Cesium.CodeGen/Contexts/SwitchScope.cs +++ b/Cesium.CodeGen/Contexts/SwitchScope.cs @@ -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; diff --git a/Cesium.CodeGen/Ir/Expressions/BinaryOperators/ArithmeticBinaryOperatorExpression.cs b/Cesium.CodeGen/Ir/Expressions/BinaryOperators/ArithmeticBinaryOperatorExpression.cs index 36c51e8c..022226cf 100644 --- a/Cesium.CodeGen/Ir/Expressions/BinaryOperators/ArithmeticBinaryOperatorExpression.cs +++ b/Cesium.CodeGen/Ir/Expressions/BinaryOperators/ArithmeticBinaryOperatorExpression.cs @@ -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; @@ -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)); @@ -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)); diff --git a/Cesium.CodeGen/Ir/Types/ConstType.cs b/Cesium.CodeGen/Ir/Types/ConstType.cs index d3abe5eb..88cd8803 100644 --- a/Cesium.CodeGen/Ir/Types/ConstType.cs +++ b/Cesium.CodeGen/Ir/Types/ConstType.cs @@ -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); } diff --git a/Cesium.CodeGen/Ir/Types/FunctionType.cs b/Cesium.CodeGen/Ir/Types/FunctionType.cs index d730a61d..fb187889 100644 --- a/Cesium.CodeGen/Ir/Types/FunctionType.cs +++ b/Cesium.CodeGen/Ir/Types/FunctionType.cs @@ -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} }}"; diff --git a/Cesium.CodeGen/Ir/Types/IType.cs b/Cesium.CodeGen/Ir/Types/IType.cs index 11fc7026..057f6239 100644 --- a/Cesium.CodeGen/Ir/Types/IType.cs +++ b/Cesium.CodeGen/Ir/Types/IType.cs @@ -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; @@ -15,7 +18,35 @@ internal interface IType FieldDefinition CreateFieldOfType(TranslationUnitContext context, TypeDefinition ownerType, string fieldName) => new(fieldName, FieldAttributes.Public, Resolve(context)); - int SizeInBytes { get; } + /// Determines the size of an object of this type in bytes, if possible. + /// Target architecture set. + /// The size if it was possible to determine. null otherwise. + /// + /// + /// Certain types (mostly pointers and derivatives) may not be able to determine their size in dynamic + /// architecture. + /// + /// + /// In this case, only would be available to get the size dynamically. + /// + /// + int? GetSizeInBytes(TargetArchitectureSet arch); + + /// + /// 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. + /// + /// For simple types, will emit a constant. + 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)); + } } /// A generated type, i.e. a type that has some bytecode to be generated once. diff --git a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs index eb02f46e..25d26a2e 100644 --- a/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs +++ b/Cesium.CodeGen/Ir/Types/InPlaceArrayType.cs @@ -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) @@ -44,7 +51,7 @@ 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) } }; } @@ -52,10 +59,9 @@ CustomAttribute GenerateCustomFieldAttribute() 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) { @@ -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]: // @@ -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) } }; diff --git a/Cesium.CodeGen/Ir/Types/NamedType.cs b/Cesium.CodeGen/Ir/Types/NamedType.cs index 52a2a96b..e4406f6b 100644 --- a/Cesium.CodeGen/Ir/Types/NamedType.cs +++ b/Cesium.CodeGen/Ir/Types/NamedType.cs @@ -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} }}"; } diff --git a/Cesium.CodeGen/Ir/Types/PointerType.cs b/Cesium.CodeGen/Ir/Types/PointerType.cs index 84810e8a..bb6d2c54 100644 --- a/Cesium.CodeGen/Ir/Types/PointerType.cs +++ b/Cesium.CodeGen/Ir/Types/PointerType.cs @@ -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."); } diff --git a/Cesium.CodeGen/Ir/Types/PrimitiveType.cs b/Cesium.CodeGen/Ir/Types/PrimitiveType.cs index 910dc153..49417425 100644 --- a/Cesium.CodeGen/Ir/Types/PrimitiveType.cs +++ b/Cesium.CodeGen/Ir/Types/PrimitiveType.cs @@ -94,7 +94,7 @@ public TypeReference Resolve(TranslationUnitContext context) }; } - public int SizeInBytes => + public int? GetSizeInBytes(TargetArchitectureSet arch) => Kind switch { // Basic @@ -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}."), }; diff --git a/Cesium.CodeGen/Ir/Types/StructType.cs b/Cesium.CodeGen/Ir/Types/StructType.cs index 84a7c0da..555c1d1e 100644 --- a/Cesium.CodeGen/Ir/Types/StructType.cs +++ b/Cesium.CodeGen/Ir/Types/StructType.cs @@ -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."); }