This commit is contained in:
nub31
2025-09-16 18:22:20 +02:00
parent c9e34ae7e2
commit d907d1183f
7 changed files with 349 additions and 422 deletions

View File

@@ -8,6 +8,8 @@ namespace NubLang.Generation.QBE;
public class QBEGenerator
{
private const int PTR_SIZE = 8;
private readonly QBEWriter _writer;
private readonly List<DefinitionNode> _definitions;
private readonly HashSet<StructTypeNode> _structTypes;
@@ -20,7 +22,6 @@ public class QBEGenerator
private int _labelIndex;
private int _cStringLiteralIndex;
private int _stringLiteralIndex;
private bool _codeIsReachable = true;
public QBEGenerator(List<DefinitionNode> definitions, HashSet<StructTypeNode> structTypes)
{
@@ -39,7 +40,6 @@ public class QBEGenerator
_labelIndex = 0;
_cStringLiteralIndex = 0;
_stringLiteralIndex = 0;
_codeIsReachable = true;
_writer.Comment("========== Builtin functions ==========");
@@ -142,77 +142,125 @@ public class QBEGenerator
private static string QBEAssign(TypeNode type)
{
if (type.IsSimpleType(out var simpleType, out _))
return type switch
{
return simpleType.StorageSize switch
{
StorageSize.I8 or StorageSize.U8 or StorageSize.I16 or StorageSize.U16 or StorageSize.I32 or StorageSize.U32 => "=w",
StorageSize.I64 or StorageSize.U64 => "=l",
StorageSize.F32 => "=s",
StorageSize.F64 => "=d",
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
};
}
return "=l";
IntTypeNode { Width: <= 32 } => "=w",
IntTypeNode { Width: 64 } => "=l",
FloatTypeNode { Width: 32 } => "=s",
FloatTypeNode { Width: 64 } => "=d",
BoolTypeNode => "=w",
PointerTypeNode => "=l",
FuncTypeNode => "=l",
CStringTypeNode => "=l",
StringTypeNode => "=l",
ArrayTypeNode => "=l",
StructTypeNode => throw new InvalidOperationException("Structs are not loaded/stored directly"),
VoidTypeNode => throw new InvalidOperationException("Void has no assignment"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
};
}
private void EmitStore(TypeNode type, string value, string destination)
{
string store;
if (type.IsSimpleType(out var simpleType, out _))
var store = type switch
{
store = simpleType.StorageSize switch
{
StorageSize.I8 or StorageSize.U8 => "storeb",
StorageSize.I16 or StorageSize.U16 => "storeh",
StorageSize.I32 or StorageSize.U32 => "storew",
StorageSize.I64 or StorageSize.U64 => "storel",
StorageSize.F32 => "stores",
StorageSize.F64 => "stored",
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
};
}
else
{
store = "storel";
}
BoolTypeNode => "storeb",
IntTypeNode { Width: 8 } => "storeb",
IntTypeNode { Width: 16 } => "storeh",
IntTypeNode { Width: 32 } => "storew",
IntTypeNode { Width: 64 } => "storel",
FloatTypeNode { Width: 32 } => "stores",
FloatTypeNode { Width: 64 } => "stored",
PointerTypeNode => "storel",
FuncTypeNode => "storel",
CStringTypeNode => "storel",
StringTypeNode => "storel",
ArrayTypeNode => "storel",
StructTypeNode => throw new InvalidOperationException("Struct stores must use blit/memcpy"),
VoidTypeNode => throw new InvalidOperationException("Cannot store void"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
};
_writer.Indented($"{store} {value}, {destination}");
}
private string EmitLoad(TypeNode type, string from)
{
string load;
if (type.IsSimpleType(out var simpleType, out _))
string load = type switch
{
load = simpleType.StorageSize switch
{
StorageSize.I64 or StorageSize.U64 => "loadl",
StorageSize.I32 or StorageSize.U32 => "loadw",
StorageSize.I16 => "loadsh",
StorageSize.I8 => "loadsb",
StorageSize.U16 => "loaduh",
StorageSize.U8 => "loadub",
StorageSize.F64 => "loadd",
StorageSize.F32 => "loads",
_ => throw new ArgumentOutOfRangeException(nameof(simpleType.StorageSize))
};
}
else
{
load = "loadl";
}
BoolTypeNode => "loadub",
IntTypeNode { Signed: true, Width: 8 } => "loadsb",
IntTypeNode { Signed: true, Width: 16 } => "loadsh",
IntTypeNode { Signed: true, Width: 32 } => "loadw",
IntTypeNode { Signed: true, Width: 64 } => "loadl",
IntTypeNode { Signed: false, Width: 8 } => "loadsb",
IntTypeNode { Signed: false, Width: 16 } => "loadsh",
IntTypeNode { Signed: false, Width: 32 } => "loadw",
IntTypeNode { Signed: false, Width: 64 } => "loadl",
FloatTypeNode { Width: 32 } => "loads",
FloatTypeNode { Width: 64 } => "loadd",
PointerTypeNode => "loadl",
FuncTypeNode => "loadl",
CStringTypeNode => "loadl",
StringTypeNode => "loadl",
ArrayTypeNode => "loadl",
StructTypeNode => throw new InvalidOperationException("Struct loads must use blit/memcpy"),
VoidTypeNode => throw new InvalidOperationException("Cannot load void"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type {type.GetType()}")
};
var into = TmpName();
_writer.Indented($"{into} {QBEAssign(type)} {load} {from}");
return into;
}
private static string StructDefQBEType(TypeNode type)
{
return type switch
{
BoolTypeNode => "b",
IntTypeNode { Width: 8 } => "b",
IntTypeNode { Width: 16 } => "h",
IntTypeNode { Width: 32 } => "w",
IntTypeNode { Width: 64 } => "l",
FloatTypeNode { Width: 32 } => "s",
FloatTypeNode { Width: 64 } => "d",
PointerTypeNode => "l",
FuncTypeNode => "l",
CStringTypeNode => "l",
StringTypeNode => "l",
ArrayTypeNode => "l",
StructTypeNode st => StructTypeName(st.Module, st.Name),
VoidTypeNode => throw new InvalidOperationException("Void has no QBE type"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
private string FuncQBETypeName(TypeNode type)
{
return type switch
{
BoolTypeNode => "ub",
IntTypeNode { Signed: true, Width: 8 } => "sb",
IntTypeNode { Signed: true, Width: 16 } => "sh",
IntTypeNode { Signed: false, Width: 8 } => "ub",
IntTypeNode { Signed: false, Width: 16 } => "uh",
IntTypeNode { Width: 32 } => "w",
IntTypeNode { Width: 64 } => "l",
FloatTypeNode { Width: 32 } => "s",
FloatTypeNode { Width: 64 } => "d",
PointerTypeNode => "l",
FuncTypeNode => "l",
CStringTypeNode => "l",
StringTypeNode => "l",
ArrayTypeNode => "l",
StructTypeNode st => StructTypeName(st.Module, st.Name),
VoidTypeNode => throw new InvalidOperationException("Void has no QBE type"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
private void EmitMemset(string destination, int value, string length)
{
_writer.Indented($"call $.memset(l {destination}, l {value}, l {length})");
@@ -250,109 +298,100 @@ public class QBEGenerator
private void EmitCopyInto(ExpressionNode source, string destination)
{
// Simple types are passed in registers and can therefore just be stored
if (source.Type.IsSimpleType(out var simpleType, out var complexType))
if (source.Type.IsScalar)
{
var value = EmitExpression(source);
EmitStore(simpleType, value, destination);
EmitStore(source.Type, EmitExpression(source), destination);
return;
}
// Structs has known sizes at compile time
if (complexType is StructTypeNode)
if (source.Type.IsValueType)
{
var value = EmitExpression(source);
_writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}");
_writer.Indented($"blit {value}, {destination}, {SizeOf(source.Type)}");
return;
}
// The rest of the complex types has unknown sizes
else
{
var value = EmitExpression(source);
var size = complexType switch
{
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
CStringTypeNode => EmitCStringSizeInBytes(value),
StringTypeNode => EmitStringSizeInBytes(value),
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
};
var buffer = TmpName();
_writer.Indented($"{buffer} =l alloc8 {size}");
EmitMemcpy(value, buffer, size);
EmitStore(complexType, buffer, destination);
switch (source.Type)
{
case ArrayTypeNode arrayType:
{
var value = EmitExpression(source);
var size = EmitArraySizeInBytes(arrayType, value);
var buffer = TmpName();
_writer.Indented($"{buffer} =l alloc8 {size}");
EmitMemcpy(value, buffer, size);
EmitStore(arrayType, buffer, destination);
return;
}
case CStringTypeNode:
{
var value = EmitExpression(source);
var size = EmitCStringSizeInBytes(value);
var buffer = TmpName();
_writer.Indented($"{buffer} =l alloc8 {size}");
EmitMemcpy(value, buffer, size);
EmitStore(source.Type, buffer, destination);
return;
}
case StringTypeNode:
{
var value = EmitExpression(source);
var size = EmitStringSizeInBytes(value);
var buffer = TmpName();
_writer.Indented($"{buffer} =l alloc8 {size}");
EmitMemcpy(value, buffer, size);
EmitStore(source.Type, buffer, destination);
return;
}
case VoidTypeNode:
throw new InvalidOperationException("Cannot copy void");
default:
throw new ArgumentOutOfRangeException(nameof(source.Type), $"Unknown type {source.Type}");
}
}
private string EmitCopy(ExpressionNode source)
{
// Allowlist for types which are safe to not copy
if (source is ArrayInitializerNode or StructInitializerNode or LiteralNode)
if (source is RValueExpressionNode || source.Type.IsScalar)
{
return EmitExpression(source);
}
// Simple types are passed in registers and therefore always copied
if (source.Type.IsSimpleType(out _, out var complexType))
{
return EmitExpression(source);
}
// For the rest, we figure out the size of the type and shallow copy them
var value = EmitExpression(source);
var destination = TmpName();
// Structs has known sizes at compile time
if (complexType is StructTypeNode)
if (source.Type.IsValueType)
{
var size = SizeOf(complexType);
var size = SizeOf(source.Type);
_writer.Indented($"{destination} =l alloc8 {size}");
_writer.Indented($"blit {value}, {destination}, {size}");
return destination;
}
// The rest of the complex types has unknown sizes
else
{
var size = complexType switch
{
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
CStringTypeNode => EmitCStringSizeInBytes(value),
StringTypeNode => EmitStringSizeInBytes(value),
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
};
_writer.Indented($"{destination} =l alloc8 {size}");
EmitMemcpy(value, destination, size);
switch (source.Type)
{
case ArrayTypeNode arrayType:
var arraySize = EmitArraySizeInBytes(arrayType, value);
_writer.Indented($"{destination} =l alloc8 {arraySize}");
EmitMemcpy(value, destination, arraySize);
break;
case CStringTypeNode:
var cstrSize = EmitCStringSizeInBytes(value);
_writer.Indented($"{destination} =l alloc8 {cstrSize}");
EmitMemcpy(value, destination, cstrSize);
break;
case StringTypeNode:
var strSize = EmitStringSizeInBytes(value);
_writer.Indented($"{destination} =l alloc8 {strSize}");
EmitMemcpy(value, destination, strSize);
break;
default:
throw new InvalidOperationException($"Cannot copy type {source.Type}");
}
return destination;
}
// Utility to create QBE type names for function parameters and return types
private string FuncQBETypeName(TypeNode type)
{
if (type.IsSimpleType(out var simpleType, out var complexType))
{
return simpleType.StorageSize switch
{
StorageSize.I64 or StorageSize.U64 => "l",
StorageSize.I32 or StorageSize.U32 => "w",
StorageSize.I16 => "sh",
StorageSize.I8 => "sb",
StorageSize.U16 => "uh",
StorageSize.U8 => "ub",
StorageSize.F64 => "d",
StorageSize.F32 => "s",
_ => throw new ArgumentOutOfRangeException()
};
}
if (complexType is StructTypeNode structType)
{
return StructTypeName(structType.Module, structType.Name);
}
return "l";
}
private void EmitStructType(StructTypeNode structType)
{
// todo(nub31): qbe expects structs to be declared in order. We must Check the dependencies of the struct to see if a type need to be declared before this one
@@ -365,31 +404,6 @@ public class QBEGenerator
}
_writer.WriteLine(" }");
return;
string StructDefQBEType(TypeNode type)
{
if (type.IsSimpleType(out var simpleType, out var complexType))
{
return simpleType.StorageSize switch
{
StorageSize.I64 or StorageSize.U64 => "l",
StorageSize.I32 or StorageSize.U32 => "w",
StorageSize.I16 or StorageSize.U16 => "h",
StorageSize.I8 or StorageSize.U8 => "b",
StorageSize.F64 => "d",
StorageSize.F32 => "s",
_ => throw new ArgumentOutOfRangeException()
};
}
if (complexType is StructTypeNode childStructType)
{
return StructTypeName(childStructType.Module, childStructType.Name);
}
return "l";
}
}
private void EmitStructDefinition(StructNode structDef)
@@ -477,12 +491,6 @@ public class QBEGenerator
EmitBlock(funcDef.Body);
// Implicit return for void functions if no explicit return has been set
if (funcDef.Signature.ReturnType is VoidTypeNode && funcDef.Body.Statements.LastOrDefault() is not ReturnNode)
{
_writer.Indented("ret");
}
_writer.WriteLine("}");
_writer.NewLine();
}
@@ -491,13 +499,8 @@ public class QBEGenerator
{
foreach (var statement in block.Statements)
{
if (_codeIsReachable)
{
EmitStatement(statement);
}
EmitStatement(statement);
}
_codeIsReachable = true;
}
private void EmitStatement(StatementNode statement)
@@ -505,13 +508,13 @@ public class QBEGenerator
switch (statement)
{
case AssignmentNode assignment:
EmitAssignment(assignment);
EmitCopyInto(assignment.Value, EmitAddressOf(assignment.Target));
break;
case BreakNode:
EmitBreak();
_writer.Indented($"jmp {_breakLabels.Peek()}");
break;
case ContinueNode:
EmitContinue();
_writer.Indented($"jmp {_continueLabels.Peek()}");
break;
case IfNode ifStatement:
EmitIf(ifStatement);
@@ -536,23 +539,6 @@ public class QBEGenerator
}
}
private void EmitAssignment(AssignmentNode assignment)
{
EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target));
}
private void EmitBreak()
{
_writer.Indented($"jmp {_breakLabels.Peek()}");
_codeIsReachable = false;
}
private void EmitContinue()
{
_writer.Indented($"jmp {_continueLabels.Peek()}");
_codeIsReachable = false;
}
private void EmitIf(IfNode ifStatement)
{
var trueLabel = LabelName();
@@ -666,60 +652,47 @@ public class QBEGenerator
_writer.WriteLine(endLabel);
}
private string EmitExpression(ExpressionNode expression)
private string EmitExpression(ExpressionNode expr)
{
return expression switch
return expr switch
{
ArrayInitializerNode arrayInitializer => EmitArrayInitializer(arrayInitializer),
StructInitializerNode structInitializer => EmitStructInitializer(structInitializer),
AddressOfNode addressOf => EmitAddressOf(addressOf),
DereferenceNode dereference => EmitDereference(dereference),
BinaryExpressionNode binary => EmitBinaryExpression(binary),
FuncCallNode funcCall => EmitFuncCall(funcCall),
ConvertIntNode convertInt => EmitConvertInt(convertInt),
ConvertFloatNode convertFloat => EmitConvertFloat(convertFloat),
VariableIdentifierNode identifier => EmitVariableIdentifier(identifier),
FuncIdentifierNode funcIdentifier => EmitFuncIdentifier(funcIdentifier),
FuncParameterIdentifierNode funcParameterIdentifier => EmitParameterFuncIdentifier(funcParameterIdentifier),
LiteralNode literal => EmitLiteral(literal),
UnaryExpressionNode unaryExpression => EmitUnaryExpression(unaryExpression),
StructFieldAccessNode structFieldAccess => EmitStructFieldAccess(structFieldAccess),
StructFuncCallNode structFuncCall => EmitStructFuncCall(structFuncCall),
ArrayIndexAccessNode arrayIndex => EmitArrayIndexAccess(arrayIndex),
_ => throw new ArgumentOutOfRangeException(nameof(expression))
RValueExpressionNode rValue => EmitRValue(rValue),
LValueExpressionNode lValue => EmitLValue(lValue),
_ => throw new ArgumentOutOfRangeException(nameof(expr))
};
}
private string EmitFuncIdentifier(FuncIdentifierNode funcIdent)
private string EmitRValue(RValueExpressionNode rValue)
{
return FuncName(funcIdent.Module, funcIdent.Name, funcIdent.ExternSymbol);
return rValue switch
{
AddressOfNode expr => EmitAddressOf(expr.LValue),
ArrayInitializerNode expr => EmitArrayInitializer(expr),
BinaryExpressionNode expr => EmitBinaryExpression(expr),
ConvertFloatNode expr => EmitConvertFloat(expr),
ConvertIntNode expr => EmitConvertInt(expr),
DereferenceNode expr => EmitLoad(expr.Type, EmitExpression(expr.Expression)),
FuncCallNode expr => EmitFuncCall(expr),
FuncIdentifierNode expr => FuncName(expr.Module, expr.Name, expr.ExternSymbol),
FuncParameterIdentifierNode expr => $"%{expr.Name}",
LiteralNode expr => EmitLiteral(expr),
StructFuncCallNode expr => EmitStructFuncCall(expr),
StructInitializerNode expr => EmitStructInitializer(expr),
UnaryExpressionNode expr => EmitUnaryExpression(expr),
_ => throw new ArgumentOutOfRangeException(nameof(rValue))
};
}
private string EmitVariableIdentifier(VariableIdentifierNode variableIdent)
private string EmitLValue(LValueExpressionNode lValue)
{
var address = EmitAddressOfVariableIdent(variableIdent);
if (variableIdent.Type is StructTypeNode)
var address = EmitAddressOf(lValue);
if (lValue.Type is { IsValueType: true, IsScalar: false })
{
return address;
}
return EmitLoad(variableIdent.Type, address);
}
private string EmitParameterFuncIdentifier(FuncParameterIdentifierNode funcParameterIdent)
{
return "%" + funcParameterIdent.Name;
}
private string EmitArrayIndexAccess(ArrayIndexAccessNode arrayIndexAccess)
{
var address = EmitAddressOfArrayIndexAccess(arrayIndexAccess);
if (arrayIndexAccess.Type is StructTypeNode)
{
return address;
}
return EmitLoad(arrayIndexAccess.Type, address);
return EmitLoad(lValue.Type, address);
}
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
@@ -743,30 +716,14 @@ public class QBEGenerator
return arrayPointer;
}
private string EmitDereference(DereferenceNode dereference)
private string EmitAddressOf(LValueExpressionNode lval)
{
var address = EmitExpression(dereference.Expression);
if (dereference.Type is StructTypeNode)
{
return address;
}
return EmitLoad(dereference.Type, address);
}
private string EmitAddressOf(AddressOfNode addressOf)
{
return EmitAddressOfLValue(addressOf.LValue);
}
private string EmitAddressOfLValue(LValueExpressionNode addressOf)
{
return addressOf switch
return lval switch
{
ArrayIndexAccessNode arrayIndexAccess => EmitAddressOfArrayIndexAccess(arrayIndexAccess),
StructFieldAccessNode structFieldAccess => EmitAddressOfStructFieldAccess(structFieldAccess),
VariableIdentifierNode variableIdent => EmitAddressOfVariableIdent(variableIdent),
_ => throw new ArgumentOutOfRangeException(nameof(addressOf))
VariableIdentifierNode variableIdent => $"%{variableIdent.Name}",
_ => throw new ArgumentOutOfRangeException(nameof(lval))
};
}
@@ -794,11 +751,6 @@ public class QBEGenerator
return address;
}
private string EmitAddressOfVariableIdent(VariableIdentifierNode variableIdent)
{
return "%" + variableIdent.Name;
}
private string EmitBinaryExpression(BinaryExpressionNode binaryExpression)
{
var left = EmitExpression(binaryExpression.Left);
@@ -1116,17 +1068,6 @@ public class QBEGenerator
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
}
private string EmitStructFieldAccess(StructFieldAccessNode structFieldAccess)
{
var address = EmitAddressOfStructFieldAccess(structFieldAccess);
if (structFieldAccess.Type is StructTypeNode)
{
return address;
}
return EmitLoad(structFieldAccess.Type, address);
}
private string EmitStructFuncCall(StructFuncCallNode structFuncCall)
{
var func = StructFuncName(structFuncCall.StructType.Module, structFuncCall.StructType.Name, structFuncCall.Name);
@@ -1236,19 +1177,16 @@ public class QBEGenerator
{
return type switch
{
SimpleTypeNode simple => simple.StorageSize switch
{
StorageSize.Void => 0,
StorageSize.I8 or StorageSize.U8 => 1,
StorageSize.I16 or StorageSize.U16 => 2,
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
},
CStringTypeNode => 8,
StringTypeNode => 8,
ArrayTypeNode => 8,
IntTypeNode intType => intType.Width / 8,
FloatTypeNode fType => fType.Width / 8,
BoolTypeNode => 1,
PointerTypeNode => PTR_SIZE,
FuncTypeNode => PTR_SIZE,
StructTypeNode structType => CalculateStructSize(structType),
VoidTypeNode => throw new InvalidOperationException("Void type has no size"),
CStringTypeNode => PTR_SIZE,
StringTypeNode => PTR_SIZE,
ArrayTypeNode => PTR_SIZE,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
@@ -1272,23 +1210,21 @@ public class QBEGenerator
{
return type switch
{
SimpleTypeNode simple => simple.StorageSize switch
{
StorageSize.Void => 1,
StorageSize.I8 or StorageSize.U8 => 1,
StorageSize.I16 or StorageSize.U16 => 2,
StorageSize.I32 or StorageSize.U32 or StorageSize.F32 => 4,
StorageSize.I64 or StorageSize.U64 or StorageSize.F64 => 8,
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown storage size: {simple.StorageSize}")
},
CStringTypeNode => 8,
StringTypeNode => 8,
ArrayTypeNode => 8,
StructTypeNode structType => CalculateStructAlignment(structType),
IntTypeNode intType => intType.Width / 8,
FloatTypeNode fType => fType.Width / 8,
BoolTypeNode => 1,
PointerTypeNode => PTR_SIZE,
FuncTypeNode => PTR_SIZE,
StructTypeNode st => CalculateStructAlignment(st),
CStringTypeNode => PTR_SIZE,
StringTypeNode => PTR_SIZE,
ArrayTypeNode => PTR_SIZE,
VoidTypeNode => throw new InvalidOperationException("Void has no alignment"),
_ => throw new ArgumentOutOfRangeException(nameof(type), $"Unknown type: {type.GetType()}")
};
}
private static int CalculateStructAlignment(StructTypeNode structType)
{
var maxAlignment = 1;
@@ -1307,26 +1243,27 @@ public class QBEGenerator
return (offset + alignment - 1) & ~(alignment - 1);
}
private static int OffsetOf(StructTypeNode structDef, string member)
private static int OffsetOf(StructTypeNode type, string member)
{
var offset = 0;
foreach (var field in structDef.Fields)
foreach (var field in type.Fields)
{
var fieldAlignment = AlignmentOf(field.Type);
offset = AlignTo(offset, fieldAlignment);
if (field.Name == member)
{
return offset;
}
var fieldAlignment = AlignmentOf(field.Type);
offset = AlignTo(offset, fieldAlignment);
offset += SizeOf(field.Type);
}
throw new UnreachableException($"Member '{member}' not found in struct");
}
private string TmpName()
{
return $"%.t{++_tmpIndex}";