...
This commit is contained in:
@@ -166,9 +166,63 @@ public class QBEGenerator
|
||||
return into;
|
||||
}
|
||||
|
||||
private void EmitMemset(string destination, int value, string length)
|
||||
{
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var continueLabel = LabelName();
|
||||
var doneLabel = LabelName();
|
||||
var condition = TmpName();
|
||||
_writer.Indented($"{condition} =w cultl {count}, {length}");
|
||||
_writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}");
|
||||
|
||||
_writer.WriteLine(continueLabel);
|
||||
|
||||
var destinationAddress = TmpName();
|
||||
_writer.Indented($"{destinationAddress} =l add {destination}, {count}");
|
||||
|
||||
_writer.Indented($"storeb {value}, {destinationAddress}");
|
||||
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
|
||||
_writer.WriteLine(doneLabel);
|
||||
}
|
||||
|
||||
private void EmitMemcpy(string source, string destination, string length)
|
||||
{
|
||||
_writer.Indented($"call $nub_memcpy(l {source}, l {destination}, l {length})");
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var continueLabel = LabelName();
|
||||
var doneLabel = LabelName();
|
||||
var condition = TmpName();
|
||||
_writer.Indented($"{condition} =w cultl {count}, {length}");
|
||||
_writer.Indented($"jnz {condition}, {continueLabel}, {doneLabel}");
|
||||
|
||||
_writer.WriteLine(continueLabel);
|
||||
|
||||
var sourceAddress = TmpName();
|
||||
_writer.Indented($"{sourceAddress} =l add {source}, {count}");
|
||||
|
||||
var destinationAddress = TmpName();
|
||||
_writer.Indented($"{destinationAddress} =l add {destination}, {count}");
|
||||
|
||||
var value = TmpName();
|
||||
_writer.Indented($"{value} =w loadub {sourceAddress}");
|
||||
_writer.Indented($"storeb {value}, {destinationAddress}");
|
||||
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
|
||||
_writer.WriteLine(doneLabel);
|
||||
}
|
||||
|
||||
private string EmitArraySizeInBytes(ArrayTypeNode type, string array)
|
||||
@@ -182,10 +236,27 @@ public class QBEGenerator
|
||||
|
||||
private string EmitCStringSizeInBytes(string cstring)
|
||||
{
|
||||
var size = TmpName();
|
||||
_writer.Indented($"{size} =l call $nub_cstring_length(l {cstring})");
|
||||
_writer.Indented($"{size} =l add {size}, 1");
|
||||
return size;
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l copy 0");
|
||||
|
||||
var loopLabel = LabelName();
|
||||
_writer.WriteLine(loopLabel);
|
||||
|
||||
var address = TmpName();
|
||||
_writer.Indented($"{address} =l add {cstring}, {count}");
|
||||
|
||||
var value = TmpName();
|
||||
_writer.Indented($"{value} =w loadub {address}");
|
||||
|
||||
var notZeroLabel = LabelName();
|
||||
var zeroLabel = LabelName();
|
||||
_writer.Indented($"jnz {value}, {notZeroLabel}, {zeroLabel}");
|
||||
_writer.WriteLine(notZeroLabel);
|
||||
_writer.Indented($"{count} =l add {count}, 1");
|
||||
_writer.Indented($"jmp {loopLabel}");
|
||||
_writer.WriteLine(zeroLabel);
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
private string EmitStringSizeInBytes(string nubstring)
|
||||
@@ -196,74 +267,42 @@ public class QBEGenerator
|
||||
return size;
|
||||
}
|
||||
|
||||
private bool EmitTryMoveInto(ExpressionNode source, string destinationLValue)
|
||||
private void EmitCopyInto(ExpressionNode source, string destination)
|
||||
{
|
||||
switch (source)
|
||||
{
|
||||
case ArrayInitializerNode arrayInitializer:
|
||||
{
|
||||
EmitStore(source.Type, EmitArrayInitializer(arrayInitializer), destinationLValue);
|
||||
return true;
|
||||
}
|
||||
case StructInitializerNode structInitializer:
|
||||
{
|
||||
EmitStructInitializer(structInitializer, destinationLValue);
|
||||
return true;
|
||||
}
|
||||
case ConvertToInterfaceNode convertToInterface:
|
||||
{
|
||||
EmitConvertToInterface(convertToInterface, destinationLValue);
|
||||
return true;
|
||||
}
|
||||
case LiteralNode { Kind: LiteralKind.String } literal:
|
||||
{
|
||||
EmitStore(source.Type, EmitLiteral(literal), destinationLValue);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void EmitCopyIntoOrInitialize(ExpressionNode source, string destinationLValue)
|
||||
{
|
||||
// If the source is a value which is not used yet such as an array/struct initializer or literal, we can skip copying
|
||||
if (EmitTryMoveInto(source, destinationLValue))
|
||||
// Simple types are passed in registers and can therefore just be stored
|
||||
if (source.Type.IsSimpleType(out var simpleType, out var complexType))
|
||||
{
|
||||
var value = EmitExpression(source);
|
||||
EmitStore(simpleType, value, destination);
|
||||
return;
|
||||
}
|
||||
|
||||
var value = EmitExpression(source);
|
||||
|
||||
if (source.Type.IsSimpleType(out var simpleType, out var complexType))
|
||||
// Structs and interfaces has known sizes at compile time
|
||||
if (complexType is StructTypeNode or InterfaceTypeNode)
|
||||
{
|
||||
EmitStore(simpleType, value, destinationLValue);
|
||||
var value = EmitExpression(source);
|
||||
_writer.Indented($"blit {value}, {destination}, {SizeOf(complexType)}");
|
||||
}
|
||||
// The rest of the complex types has unknown sizes
|
||||
else
|
||||
{
|
||||
if (complexType is StructTypeNode or InterfaceTypeNode)
|
||||
var value = EmitExpression(source);
|
||||
var size = complexType switch
|
||||
{
|
||||
EmitMemcpy(value, destinationLValue, SizeOf(complexType).ToString());
|
||||
}
|
||||
else
|
||||
{
|
||||
var size = complexType switch
|
||||
{
|
||||
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
|
||||
CStringTypeNode => EmitCStringSizeInBytes(value),
|
||||
StringTypeNode => EmitStringSizeInBytes(value),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
|
||||
};
|
||||
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, destinationLValue);
|
||||
}
|
||||
var buffer = TmpName();
|
||||
_writer.Indented($"{buffer} =l alloc8 {size}");
|
||||
EmitMemcpy(value, buffer, size);
|
||||
EmitStore(complexType, buffer, destination);
|
||||
}
|
||||
}
|
||||
|
||||
private string EmitCreateCopy(ExpressionNode source)
|
||||
private string EmitCopy(ExpressionNode source)
|
||||
{
|
||||
// Allowlist for types which are safe to not copy
|
||||
if (source is ArrayInitializerNode or StructInitializerNode or ConvertToInterfaceNode or LiteralNode)
|
||||
@@ -279,19 +318,30 @@ public class QBEGenerator
|
||||
|
||||
// For the rest, we figure out the size of the type and shallow copy them
|
||||
var value = EmitExpression(source);
|
||||
var size = complexType switch
|
||||
{
|
||||
ArrayTypeNode arrayType => EmitArraySizeInBytes(arrayType, value),
|
||||
CStringTypeNode => EmitCStringSizeInBytes(value),
|
||||
StringTypeNode => EmitStringSizeInBytes(value),
|
||||
InterfaceTypeNode interfaceType => SizeOf(interfaceType).ToString(),
|
||||
StructTypeNode structType => SizeOf(structType).ToString(),
|
||||
_ => throw new ArgumentOutOfRangeException(nameof(source.Type))
|
||||
};
|
||||
|
||||
var destination = TmpName();
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
EmitMemcpy(value, destination, size);
|
||||
|
||||
// Structs and interfaces has known sizes at compile time
|
||||
if (complexType is StructTypeNode or InterfaceTypeNode)
|
||||
{
|
||||
var size = SizeOf(complexType);
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
_writer.Indented($"blit {value}, {destination}, {size}");
|
||||
}
|
||||
// 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);
|
||||
}
|
||||
|
||||
return destination;
|
||||
}
|
||||
|
||||
@@ -460,11 +510,11 @@ public class QBEGenerator
|
||||
|
||||
private void EmitStatement(StatementNode statement)
|
||||
{
|
||||
var tokens = statement.Tokens.ToArray();
|
||||
if (tokens.Length != 0)
|
||||
{
|
||||
_writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
|
||||
}
|
||||
// var tokens = statement.Tokens.ToArray();
|
||||
// if (tokens.Length != 0)
|
||||
// {
|
||||
// _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
|
||||
// }
|
||||
|
||||
switch (statement)
|
||||
{
|
||||
@@ -499,7 +549,7 @@ public class QBEGenerator
|
||||
|
||||
private void EmitAssignment(AssignmentNode assignment)
|
||||
{
|
||||
EmitCopyIntoOrInitialize(assignment.Value, EmitAddressOfLValue(assignment.Target));
|
||||
EmitCopyInto(assignment.Value, EmitAddressOfLValue(assignment.Target));
|
||||
}
|
||||
|
||||
private void EmitBreak()
|
||||
@@ -554,7 +604,7 @@ public class QBEGenerator
|
||||
|
||||
if (variableDeclaration.Assignment.HasValue)
|
||||
{
|
||||
EmitCopyIntoOrInitialize(variableDeclaration.Assignment.Value, name);
|
||||
EmitCopyInto(variableDeclaration.Assignment.Value, name);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -581,11 +631,11 @@ public class QBEGenerator
|
||||
|
||||
private string EmitExpression(ExpressionNode expression)
|
||||
{
|
||||
var tokens = expression.Tokens.ToArray();
|
||||
if (tokens.Length != 0)
|
||||
{
|
||||
_writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
|
||||
}
|
||||
// var tokens = expression.Tokens.ToArray();
|
||||
// if (tokens.Length != 0)
|
||||
// {
|
||||
// _writer.WriteLine($"dbgloc {tokens[0].FileSpan.Span.Start.Line}");
|
||||
// }
|
||||
|
||||
return expression switch
|
||||
{
|
||||
@@ -623,30 +673,6 @@ public class QBEGenerator
|
||||
return EmitLoad(arrayIndexAccess.Type, address);
|
||||
}
|
||||
|
||||
private void EmitArrayBoundsCheck(string array, string index)
|
||||
{
|
||||
var count = TmpName();
|
||||
_writer.Indented($"{count} =l loadl {array}");
|
||||
|
||||
var isNegative = TmpName();
|
||||
_writer.Indented($"{isNegative} =w csltl {index}, 0");
|
||||
|
||||
var isOob = TmpName();
|
||||
_writer.Indented($"{isOob} =w csgel {index}, {count}");
|
||||
|
||||
var anyOob = TmpName();
|
||||
_writer.Indented($"{anyOob} =w or {isNegative}, {isOob}");
|
||||
|
||||
var oobLabel = LabelName();
|
||||
var notOobLabel = LabelName();
|
||||
_writer.Indented($"jnz {anyOob}, {oobLabel}, {notOobLabel}");
|
||||
|
||||
_writer.Indented(oobLabel);
|
||||
_writer.Indented($"call $nub_panic_array_oob()");
|
||||
|
||||
_writer.Indented(notOobLabel);
|
||||
}
|
||||
|
||||
private string EmitArrayInitializer(ArrayInitializerNode arrayInitializer)
|
||||
{
|
||||
var capacity = EmitExpression(arrayInitializer.Capacity);
|
||||
@@ -663,7 +689,7 @@ public class QBEGenerator
|
||||
|
||||
var dataPointer = TmpName();
|
||||
_writer.Indented($"{dataPointer} =l add {arrayPointer}, 8");
|
||||
_writer.Indented($"call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})");
|
||||
EmitMemset(dataPointer, 0, capacityInBytes);
|
||||
|
||||
return arrayPointer;
|
||||
}
|
||||
@@ -700,8 +726,6 @@ public class QBEGenerator
|
||||
var array = EmitExpression(arrayIndexAccess.Target);
|
||||
var index = EmitExpression(arrayIndexAccess.Index);
|
||||
|
||||
EmitArrayBoundsCheck(array, index);
|
||||
|
||||
var elementType = ((ArrayTypeNode)arrayIndexAccess.Target.Type).ElementType;
|
||||
|
||||
var offset = TmpName();
|
||||
@@ -1006,16 +1030,13 @@ public class QBEGenerator
|
||||
throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}");
|
||||
}
|
||||
|
||||
private string EmitStructInitializer(StructInitializerNode structInitializer, string? destination = null)
|
||||
private string EmitStructInitializer(StructInitializerNode structInitializer)
|
||||
{
|
||||
var structDef = _definitionTable.LookupStruct(structInitializer.StructType.Name);
|
||||
|
||||
if (destination == null)
|
||||
{
|
||||
destination = TmpName();
|
||||
var size = SizeOf(structInitializer.StructType);
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
}
|
||||
var destination = TmpName();
|
||||
var size = SizeOf(structInitializer.StructType);
|
||||
_writer.Indented($"{destination} =l alloc8 {size}");
|
||||
|
||||
foreach (var field in structDef.Fields)
|
||||
{
|
||||
@@ -1031,7 +1052,7 @@ public class QBEGenerator
|
||||
|
||||
var offset = TmpName();
|
||||
_writer.Indented($"{offset} =l add {destination}, {OffsetOf(structDef, field.Name)}");
|
||||
EmitCopyIntoOrInitialize(valueExpression, offset);
|
||||
EmitCopyInto(valueExpression, offset);
|
||||
}
|
||||
|
||||
return destination;
|
||||
@@ -1106,7 +1127,7 @@ public class QBEGenerator
|
||||
|
||||
foreach (var parameter in structFuncCall.Parameters)
|
||||
{
|
||||
var copy = EmitCreateCopy(parameter);
|
||||
var copy = EmitCopy(parameter);
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
|
||||
}
|
||||
|
||||
@@ -1148,7 +1169,7 @@ public class QBEGenerator
|
||||
|
||||
foreach (var parameter in interfaceFuncCall.Parameters)
|
||||
{
|
||||
var copy = EmitCreateCopy(parameter);
|
||||
var copy = EmitCopy(parameter);
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
|
||||
}
|
||||
|
||||
@@ -1165,7 +1186,7 @@ public class QBEGenerator
|
||||
}
|
||||
}
|
||||
|
||||
private string EmitConvertToInterface(ConvertToInterfaceNode convertToInterface, string? destination = null)
|
||||
private string EmitConvertToInterface(ConvertToInterfaceNode convertToInterface)
|
||||
{
|
||||
var implementation = EmitExpression(convertToInterface.Implementation);
|
||||
|
||||
@@ -1180,11 +1201,8 @@ public class QBEGenerator
|
||||
vtableOffset += interfaceImplementation.Functions.Count * 8;
|
||||
}
|
||||
|
||||
if (destination == null)
|
||||
{
|
||||
destination = TmpName();
|
||||
_writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
|
||||
}
|
||||
var destination = TmpName();
|
||||
_writer.Indented($"{destination} =l alloc8 {SizeOf(convertToInterface.InterfaceType)}");
|
||||
|
||||
var interfaceVtablePointer = TmpName();
|
||||
_writer.Indented($"{interfaceVtablePointer} =l add {StructVtableName(convertToInterface.StructType.Name)}, {vtableOffset}");
|
||||
@@ -1258,7 +1276,7 @@ public class QBEGenerator
|
||||
|
||||
foreach (var parameter in funcCall.Parameters)
|
||||
{
|
||||
var copy = EmitCreateCopy(parameter);
|
||||
var copy = EmitCopy(parameter);
|
||||
parameterStrings.Add($"{FuncQBETypeName(parameter.Type)} {copy}");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user