This commit is contained in:
nub31
2025-06-26 11:50:37 +02:00
parent 493f188be4
commit 3cb8a5b4c9
12 changed files with 255 additions and 193 deletions

View File

@@ -1,4 +1,4 @@
namespace c namespace c
// extern func printf(fmt: ^u8, ...args: any): void extern func printf(fmt: cstring, arg: u64): void
extern func puts(fmt: cstring) extern func puts(fmt: cstring)

View File

@@ -5,35 +5,39 @@ struct Human {
} }
export func main(args: []cstring): i64 { export func main(args: []cstring): i64 {
let human: Human // let human: Human
human = alloc Human { // human = alloc Human {
name = "member" // name = "member"
} // }
c::puts(human.name) // c::puts(human.name)
c::puts("literal") // c::puts("literal")
let x: cstring // let x: cstring
x = "variable" // x = "variable"
c::puts(x) // c::puts(x)
let y: func(cstring) // let y: func(cstring)
y = c::puts // y = c::puts
y("proxy") // y("proxy")
func(){ c::puts("anon") }() // func(){ c::puts("anon") }()
let z: func() // let z: func()
z = func() { c::puts("anon variable") } // z = func() { c::puts("anon variable") }
z() // z()
c::printf("%d\n", "test".count)
// c::puts("test")
return 0 return 0
} }

View File

@@ -14,13 +14,11 @@
<ProjectReference Include="..\Syntax\Syntax.csproj" /> <ProjectReference Include="..\Syntax\Syntax.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup>
</ItemGroup>
<ItemGroup> <ItemGroup>
<EmbeddedResource Include="Runtime\entry.s" /> <EmbeddedResource Include="Runtime\entry.s" />
<EmbeddedResource Include="Runtime\nub_mem.s" /> <EmbeddedResource Include="Runtime\nub_mem.s" />
<EmbeddedResource Include="Runtime\nub_panic.s" /> <EmbeddedResource Include="Runtime\nub_panic.s" />
<EmbeddedResource Include="Runtime\nub_cstring.s" />
<EmbeddedResource Include="Runtime\nub_string.s" /> <EmbeddedResource Include="Runtime\nub_string.s" />
</ItemGroup> </ItemGroup>

View File

@@ -0,0 +1,17 @@
.intel_syntax noprefix
.text
.globl nub_cstring_length
# func nub_cstring_length(string: cstring): u64
nub_cstring_length:
xor rax, rax
count_loop:
cmp byte ptr [rdi + rax], 0
je done
inc rax
jmp count_loop
done:
ret

View File

@@ -6,6 +6,7 @@
nub_memcpy: nub_memcpy:
mov rcx, rdx mov rcx, rdx
rep movsb rep movsb
ret
.text .text
.globl nub_memset .globl nub_memset

View File

@@ -1,80 +1,32 @@
.intel_syntax noprefix .intel_syntax noprefix
.equ SYS_MMAP, 9
.text .text
.globl nub_strlen .globl nub_string_length
# func nub_strlen(string: cstring): u64 # func nub_string_length(string: string): u64
nub_strlen: nub_string_length:
test rdi, rdi mov rsi, [rdi] # Length of string in bytes
jz null_string
xor rax, rax add rdi, 8 # Start of bytes
strlen_loop: xor rax, rax # Character count
cmp byte ptr [rdi + rax], 0 xor rdx, rdx # Current byte position
je strlen_exit
test rsi, rsi
jz _done
_count_loop:
cmp rdx, rsi
jge _done
mov dl, [rdi + rdx]
and dl, 0b11000000
cmp dl, 0b10000000
je _skip_byte
inc rax inc rax
jmp strlen_loop
null_string: _skip_byte:
xor rax, rax inc rdx
strlen_exit: jmp _count_loop
ret
_done:
.text
.globl nub_cstring_to_string
# func nub_cstring_to_string(string: cstring): []u8
nub_cstring_to_string:
push rbx
push r12
push r13
mov rbx, rdi # Save original pointer
call nub_strlen
mov r12, rax # r12 = string length
# Calculate total space needed: 8 bytes (length) + string length
mov r13, r12
add r13, 8 # r13 = total bytes needed
# Round up to page size (4096 bytes) for mmap
add r13, 4095 # Add page_size - 1
and r13, -4096 # Align to page boundary
mov rax, SYS_MMAP
xor rdi, rdi # addr = 0 (let kernel choose)
mov rsi, r13 # length = aligned size
mov rdx, 3 # prot = PROT_READ | PROT_WRITE
mov r10, 34 # flags = MAP_PRIVATE | MAP_ANONYMOUS (0x22)
mov r8, -1 # fd = -1
xor r9, r9 # offset = 0
syscall
# Check if mmap failed
cmp rax, -1
je mmap_failed
mov r13, rax # r13 = pointer to mapped memory
# Store length at beginning of mapped memory
mov [r13], r12 # Store string length
# Copy string data if not empty
test r12, r12 # Check if length is 0
jz copy_done
lea rdi, [r13 + 8]
mov rsi, rbx
mov rcx, r12
call nub_memcpy
copy_done:
mov rax, r13
jmp function_exit
mmap_failed:
xor rax, rax
function_exit:
pop r13
pop r12
pop rbx
ret ret

View File

@@ -16,6 +16,7 @@ public static class QBEGenerator
private static StringBuilder _builder = new(); private static StringBuilder _builder = new();
private static List<CStringLiteral> _cStringLiterals = []; private static List<CStringLiteral> _cStringLiterals = [];
private static List<StringLiteral> _stringLiterals = [];
private static Stack<string> _breakLabels = []; private static Stack<string> _breakLabels = [];
private static Stack<string> _continueLabels = []; private static Stack<string> _continueLabels = [];
private static Queue<(BoundAnonymousFuncNode Func, string Name)> _anonymousFunctions = []; private static Queue<(BoundAnonymousFuncNode Func, string Name)> _anonymousFunctions = [];
@@ -25,6 +26,7 @@ public static class QBEGenerator
private static int _labelIndex; private static int _labelIndex;
private static int _anonymousFuncIndex; private static int _anonymousFuncIndex;
private static int _cStringLiteralIndex; private static int _cStringLiteralIndex;
private static int _stringLiteralIndex;
private static bool _codeIsReachable = true; private static bool _codeIsReachable = true;
public static string Emit(BoundSyntaxTree syntaxTree, BoundDefinitionTable definitionTable) public static string Emit(BoundSyntaxTree syntaxTree, BoundDefinitionTable definitionTable)
@@ -34,6 +36,7 @@ public static class QBEGenerator
_builder = new StringBuilder(); _builder = new StringBuilder();
_cStringLiterals = []; _cStringLiterals = [];
_stringLiterals = [];
_breakLabels = []; _breakLabels = [];
_continueLabels = []; _continueLabels = [];
_anonymousFunctions = []; _anonymousFunctions = [];
@@ -43,6 +46,7 @@ public static class QBEGenerator
_labelIndex = 0; _labelIndex = 0;
_anonymousFuncIndex = 0; _anonymousFuncIndex = 0;
_cStringLiteralIndex = 0; _cStringLiteralIndex = 0;
_stringLiteralIndex = 0;
_codeIsReachable = true; _codeIsReachable = true;
foreach (var structDef in _definitionTable.GetStructs()) foreach (var structDef in _definitionTable.GetStructs())
@@ -68,6 +72,12 @@ public static class QBEGenerator
_builder.AppendLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}"); _builder.AppendLine($"data {cStringLiteral.Name} = {{ b \"{cStringLiteral.Value}\", b 0 }}");
} }
foreach (var stringLiteral in _stringLiterals)
{
var bytes = Encoding.UTF8.GetBytes(stringLiteral.Value).Select(b => $"b {b}");
_builder.AppendLine($"data {stringLiteral.Name} = {{ l {stringLiteral.Value.Length}, {string.Join(", ", bytes)} }}");
}
return _builder.ToString(); return _builder.ToString();
} }
@@ -86,6 +96,11 @@ public static class QBEGenerator
return $"$cstring{++_cStringLiteralIndex}"; return $"$cstring{++_cStringLiteralIndex}";
} }
private static string StringName()
{
return $"$string{++_stringLiteralIndex}";
}
private static string FuncName(BoundFuncDefinition funcDef) private static string FuncName(BoundFuncDefinition funcDef)
{ {
return funcDef switch return funcDef switch
@@ -128,6 +143,7 @@ public static class QBEGenerator
NubFixedArrayType => "storel", NubFixedArrayType => "storel",
NubFuncType => "storel", NubFuncType => "storel",
NubCStringType => "storel", NubCStringType => "storel",
NubStringType => "storel",
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions") _ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
}; };
} }
@@ -157,6 +173,7 @@ public static class QBEGenerator
NubFixedArrayType => "loadl", NubFixedArrayType => "loadl",
NubFuncType => "loadl", NubFuncType => "loadl",
NubCStringType => "loadl", NubCStringType => "loadl",
NubStringType => "loadl",
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions") _ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
}; };
} }
@@ -186,6 +203,7 @@ public static class QBEGenerator
NubFixedArrayType => "=l", NubFixedArrayType => "=l",
NubFuncType => "=l", NubFuncType => "=l",
NubCStringType => "=l", NubCStringType => "=l",
NubStringType => "=l",
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables") _ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
}; };
} }
@@ -213,6 +231,7 @@ public static class QBEGenerator
case NubPointerType: case NubPointerType:
case NubArrayType: case NubArrayType:
case NubCStringType: case NubCStringType:
case NubStringType:
case NubFuncType: case NubFuncType:
{ {
return 8; return 8;
@@ -241,12 +260,10 @@ public static class QBEGenerator
{ {
return primitiveType.Kind switch return primitiveType.Kind switch
{ {
PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 => 8, PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 or PrimitiveTypeKind.F64 => 8,
PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.Bool => 4, PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.F32 or PrimitiveTypeKind.Bool => 4,
PrimitiveTypeKind.I16 or PrimitiveTypeKind.U16 => 2, PrimitiveTypeKind.I16 or PrimitiveTypeKind.U16 => 2,
PrimitiveTypeKind.I8 or PrimitiveTypeKind.U8 => 1, PrimitiveTypeKind.I8 or PrimitiveTypeKind.U8 => 1,
PrimitiveTypeKind.F64 => 8,
PrimitiveTypeKind.F32 => 4,
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
} }
@@ -273,6 +290,7 @@ public static class QBEGenerator
case NubPointerType: case NubPointerType:
case NubArrayType: case NubArrayType:
case NubCStringType: case NubCStringType:
case NubStringType:
case NubFuncType: case NubFuncType:
{ {
return 8; return 8;
@@ -354,6 +372,7 @@ public static class QBEGenerator
NubFixedArrayType => "l", NubFixedArrayType => "l",
NubFuncType => "l", NubFuncType => "l",
NubCStringType => "l", NubCStringType => "l",
NubStringType => "l",
_ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type") _ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type")
}); });
_builder.Append(' '); _builder.Append(' ');
@@ -386,6 +405,7 @@ public static class QBEGenerator
NubFixedArrayType => "l", NubFixedArrayType => "l",
NubFuncType => "l", NubFuncType => "l",
NubCStringType => "l", NubCStringType => "l",
NubStringType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type") _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used as a function parameter type")
}; };
@@ -424,7 +444,7 @@ public static class QBEGenerator
} }
} }
parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type))); parameterVars.Add(new Variable(parameter.Name, new Val(parameterName, parameter.Type, ValKind.Immediate)));
} }
EmitBlock(body, parameterVars); EmitBlock(body, parameterVars);
@@ -468,6 +488,7 @@ public static class QBEGenerator
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}", NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
NubFuncType => "l", NubFuncType => "l",
NubCStringType => "l", NubCStringType => "l",
NubStringType => "l",
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs") _ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
}; };
_builder.Append(qbeType + ", "); _builder.Append(qbeType + ", ");
@@ -657,7 +678,7 @@ public static class QBEGenerator
{ {
var tmp = VarName(); var tmp = VarName();
_builder.AppendLine($" {tmp} =l alloc8 {SizeOf(variableDeclaration.Type)}"); _builder.AppendLine($" {tmp} =l alloc8 {SizeOf(variableDeclaration.Type)}");
_variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type))); _variables.Push(new Variable(variableDeclaration.Name, new Val(tmp, variableDeclaration.Type, ValKind.Pointer)));
} }
private static void EmitVariableAssignment(BoundVariableAssignmentNode variableAssignment) private static void EmitVariableAssignment(BoundVariableAssignmentNode variableAssignment)
@@ -741,7 +762,7 @@ public static class QBEGenerator
var pointer = EmitArrayIndexPointer(arrayIndexAccess); var pointer = EmitArrayIndexPointer(arrayIndexAccess);
var outputName = VarName(); var outputName = VarName();
_builder.AppendLine($" {outputName} {QBEAssign(arrayIndexAccess.Type)} {QBELoad(arrayIndexAccess.Type)} {pointer}"); _builder.AppendLine($" {outputName} {QBEAssign(arrayIndexAccess.Type)} {QBELoad(arrayIndexAccess.Type)} {pointer}");
return new Val(outputName, arrayIndexAccess.Type); return new Val(outputName, arrayIndexAccess.Type, ValKind.Immediate);
} }
private static void EmitArrayBoundsCheck(string array, string index) private static void EmitArrayBoundsCheck(string array, string index)
@@ -786,7 +807,7 @@ public static class QBEGenerator
_builder.AppendLine($" {dataPointer} =l add {arrayPointer}, 8"); _builder.AppendLine($" {dataPointer} =l add {arrayPointer}, 8");
_builder.AppendLine($" call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})"); _builder.AppendLine($" call $nub_memset(l {dataPointer}, w 0, l {capacityInBytes})");
return new Val(arrayPointer, arrayInitializer.Type); return new Val(arrayPointer, arrayInitializer.Type, ValKind.Immediate);
} }
private static Val EmitDereference(BoundDereferenceNode dereference) private static Val EmitDereference(BoundDereferenceNode dereference)
@@ -794,7 +815,7 @@ public static class QBEGenerator
var result = EmitUnwrap(EmitExpression(dereference.Expression)); var result = EmitUnwrap(EmitExpression(dereference.Expression));
var outputName = VarName(); var outputName = VarName();
_builder.AppendLine($" {outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}"); _builder.AppendLine($" {outputName} {QBEAssign(dereference.Type)} {QBELoad(dereference.Type)} {result}");
return new Val(outputName, dereference.Type); return new Val(outputName, dereference.Type, ValKind.Immediate);
} }
private static Val EmitAddressOf(BoundAddressOfNode addressOf) private static Val EmitAddressOf(BoundAddressOfNode addressOf)
@@ -802,31 +823,36 @@ public static class QBEGenerator
switch (addressOf.Expression) switch (addressOf.Expression)
{ {
case BoundArrayIndexAccessNode arrayIndexAccess: case BoundArrayIndexAccessNode arrayIndexAccess:
{
var pointer = EmitArrayIndexPointer(arrayIndexAccess); var pointer = EmitArrayIndexPointer(arrayIndexAccess);
return new Val(pointer, addressOf.Type); return new Val(pointer, addressOf.Type, ValKind.Immediate);
}
case BoundDereferenceNode dereference: case BoundDereferenceNode dereference:
{
return EmitExpression(dereference.Expression); return EmitExpression(dereference.Expression);
}
case BoundIdentifierNode identifier: case BoundIdentifierNode identifier:
{
if (identifier.Namespace.HasValue) if (identifier.Namespace.HasValue)
{ {
throw new NotSupportedException("There is nothing to address in another namespace"); throw new NotSupportedException("There is nothing to address in another namespace");
} }
return _variables.Single(x => x.Name == identifier.Name).Val; return _variables.Single(x => x.Name == identifier.Name).Val;
case BoundMemberAccessNode memberAccess: }
var ptr = EmitMemberAccessPointer(memberAccess);
return new Val(ptr, addressOf.Type);
default: default:
{
throw new ArgumentOutOfRangeException(); throw new ArgumentOutOfRangeException();
} }
} }
}
private static Val EmitBinaryExpression(BoundBinaryExpressionNode binaryExpression) private static Val EmitBinaryExpression(BoundBinaryExpressionNode binaryExpression)
{ {
var left = EmitUnwrap(EmitExpression(binaryExpression.Left)); var left = EmitUnwrap(EmitExpression(binaryExpression.Left));
var right = EmitUnwrap(EmitExpression(binaryExpression.Right)); var right = EmitUnwrap(EmitExpression(binaryExpression.Right));
var outputName = VarName(); var outputName = VarName();
var output = new Val(outputName, binaryExpression.Type); var output = new Val(outputName, binaryExpression.Type, ValKind.Immediate);
switch (binaryExpression.Operator) switch (binaryExpression.Operator)
{ {
@@ -1051,45 +1077,81 @@ public static class QBEGenerator
} }
private static Val EmitLiteral(BoundLiteralNode literal) private static Val EmitLiteral(BoundLiteralNode literal)
{
if (literal.Type.IsInteger)
{ {
switch (literal.Kind) switch (literal.Kind)
{ {
case LiteralKind.Integer: case LiteralKind.Integer:
return new Val(literal.Literal, literal.Type, ValKind.Literal); {
case LiteralKind.Float: if (literal.Type.IsFloat32)
return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Literal); {
default: var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
throw new ArgumentOutOfRangeException(); var bits = BitConverter.SingleToInt32Bits(value);
} return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
} }
if (literal.Type.IsFloat64) if (literal.Type.IsFloat64)
{ {
var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture); var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture);
var bits = BitConverter.DoubleToInt64Bits(value); var bits = BitConverter.DoubleToInt64Bits(value);
return new Val(bits.ToString(), literal.Type, ValKind.Literal); return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
}
if (literal.Type.IsInteger)
{
return new Val(literal.Literal, literal.Type, ValKind.Immediate);
}
break;
}
case LiteralKind.Float:
{
if (literal.Type.IsInteger)
{
return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Immediate);
} }
if (literal.Type.IsFloat32) if (literal.Type.IsFloat32)
{ {
var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture); var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
var bits = BitConverter.SingleToInt32Bits(value); var bits = BitConverter.SingleToInt32Bits(value);
return new Val(bits.ToString(), literal.Type, ValKind.Literal); return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
} }
switch (literal.Kind) if (literal.Type.IsFloat64)
{ {
var value = double.Parse(literal.Literal, CultureInfo.InvariantCulture);
var bits = BitConverter.DoubleToInt64Bits(value);
return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
}
break;
}
case LiteralKind.String: case LiteralKind.String:
{
if (literal.Type.IsString)
{
var stringLiteral = new StringLiteral(literal.Literal, StringName());
_stringLiterals.Add(stringLiteral);
return new Val(stringLiteral.Name, literal.Type, ValKind.Immediate);
}
if (literal.Type.IsCString)
{
var cStringLiteral = new CStringLiteral(literal.Literal, CStringName()); var cStringLiteral = new CStringLiteral(literal.Literal, CStringName());
_cStringLiterals.Add(cStringLiteral); _cStringLiterals.Add(cStringLiteral);
return new Val(cStringLiteral.Name, literal.Type, ValKind.Literal); return new Val(cStringLiteral.Name, literal.Type, ValKind.Immediate);
case LiteralKind.Bool:
return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Literal);
default:
throw new ArgumentOutOfRangeException();
} }
break;
}
case LiteralKind.Bool:
{
if (literal.Type.IsBool)
{
return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Immediate);
}
break;
}
}
throw new NotSupportedException($"Cannot create literal of kind '{literal.Kind}' for type {literal.Type}");
} }
private static Val EmitStructInitializer(BoundStructInitializerNode structInitializer) private static Val EmitStructInitializer(BoundStructInitializerNode structInitializer)
@@ -1124,7 +1186,7 @@ public static class QBEGenerator
} }
} }
return new Val(output, structInitializer.StructType); return new Val(output, structInitializer.StructType, ValKind.Immediate);
} }
private static Val EmitUnaryExpression(BoundUnaryExpressionNode unaryExpression) private static Val EmitUnaryExpression(BoundUnaryExpressionNode unaryExpression)
@@ -1140,16 +1202,16 @@ public static class QBEGenerator
{ {
case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }: case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }:
_builder.AppendLine($" {outputName} =l neg {operand}"); _builder.AppendLine($" {outputName} =l neg {operand}");
return new Val(outputName, unaryExpression.Type); return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }: case NubPrimitiveType { Kind: PrimitiveTypeKind.I32 or PrimitiveTypeKind.I16 or PrimitiveTypeKind.I8 }:
_builder.AppendLine($" {outputName} =w neg {operand}"); _builder.AppendLine($" {outputName} =w neg {operand}");
return new Val(outputName, unaryExpression.Type); return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }: case NubPrimitiveType { Kind: PrimitiveTypeKind.F64 }:
_builder.AppendLine($" {outputName} =d neg {operand}"); _builder.AppendLine($" {outputName} =d neg {operand}");
return new Val(outputName, unaryExpression.Type); return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }: case NubPrimitiveType { Kind: PrimitiveTypeKind.F32 }:
_builder.AppendLine($" {outputName} =s neg {operand}"); _builder.AppendLine($" {outputName} =s neg {operand}");
return new Val(outputName, unaryExpression.Type); return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
} }
break; break;
@@ -1160,7 +1222,7 @@ public static class QBEGenerator
{ {
case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }: case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }:
_builder.AppendLine($" {outputName} =w xor {operand}, 1"); _builder.AppendLine($" {outputName} =w xor {operand}, 1");
return new Val(outputName, unaryExpression.Type); return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
} }
break; break;
@@ -1174,19 +1236,32 @@ public static class QBEGenerator
throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported"); throw new NotSupportedException($"Unary operator {unaryExpression.Operator} for type {unaryExpression.Operand.Type} not supported");
} }
private static string EmitMemberAccessPointer(BoundMemberAccessNode memberAccess) private static Val EmitMemberAccess(BoundMemberAccessNode memberAccess)
{ {
var item = EmitUnwrap(EmitExpression(memberAccess.Expression)); var item = EmitUnwrap(EmitExpression(memberAccess.Expression));
var output = VarName();
switch (memberAccess.Expression.Type) switch (memberAccess.Expression.Type)
{ {
case NubArrayType: case NubArrayType:
{ {
if (memberAccess.Member == "count") if (memberAccess.Member == "count")
{ {
return item; _builder.AppendLine($" {output} =l loadl {item}");
break;
} }
throw new UnreachableException(nameof(memberAccess.Member)); throw new UnreachableException();
}
case NubStringType:
{
_builder.AppendLine($" {output} =l call $nub_string_length(l {item})");
break;
}
case NubCStringType:
{
_builder.AppendLine($" {output} =l call $nub_cstring_length(l {item})");
break;
} }
case NubStructType structType: case NubStructType structType:
{ {
@@ -1195,22 +1270,16 @@ public static class QBEGenerator
var offsetName = VarName(); var offsetName = VarName();
_builder.AppendLine($" {offsetName} =l add {item}, {offset}"); _builder.AppendLine($" {offsetName} =l add {item}, {offset}");
return offsetName; _builder.AppendLine($" {output} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {item}");
break;
} }
default: default:
{ {
throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type)); throw new ArgumentOutOfRangeException();
}
} }
} }
private static Val EmitMemberAccess(BoundMemberAccessNode memberAccess) return new Val(output, memberAccess.Type, ValKind.Immediate);
{
var pointer = EmitMemberAccessPointer(memberAccess);
var output = VarName();
_builder.AppendLine($" {output} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {pointer}");
return new Val(output, memberAccess.Type);
} }
private static Val EmitFixedArrayInitializer(BoundFixedArrayInitializerNode fixedArrayInitializer) private static Val EmitFixedArrayInitializer(BoundFixedArrayInitializerNode fixedArrayInitializer)
@@ -1227,7 +1296,7 @@ public static class QBEGenerator
var dataSize = totalSize - 8; var dataSize = totalSize - 8;
_builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {dataSize})"); _builder.AppendLine($" call $nub_memset(l {dataPtr}, w 0, l {dataSize})");
return new Val(outputName, fixedArrayInitializer.Type); return new Val(outputName, fixedArrayInitializer.Type, ValKind.Immediate);
} }
private static Val EmitFuncCall(BoundFuncCallNode funcCall) private static Val EmitFuncCall(BoundFuncCallNode funcCall)
@@ -1264,6 +1333,7 @@ public static class QBEGenerator
NubFixedArrayType => "l", NubFixedArrayType => "l",
NubFuncType => "l", NubFuncType => "l",
NubCStringType => "l", NubCStringType => "l",
NubStringType => "l",
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls") _ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
}; };
parameterStrings.Add($"{qbeType} {result}"); parameterStrings.Add($"{qbeType} {result}");
@@ -1275,12 +1345,12 @@ public static class QBEGenerator
{ {
var outputName = VarName(); var outputName = VarName();
_builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})"); _builder.AppendLine($" {outputName} {QBEAssign(funcCall.Type)} call {funcPointer}({string.Join(", ", parameterStrings)})");
return new Val(outputName, funcCall.Type); return new Val(outputName, funcCall.Type, ValKind.Immediate);
} }
else else
{ {
_builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})"); _builder.AppendLine($" call {funcPointer}({string.Join(", ", parameterStrings)})");
return new Val(string.Empty, funcCall.Type); return new Val(string.Empty, funcCall.Type, ValKind.Immediate);
} }
} }
@@ -1306,9 +1376,9 @@ public static class QBEGenerator
switch (val.Kind) switch (val.Kind)
{ {
case ValKind.Func: case ValKind.Func:
case ValKind.Literal: case ValKind.Immediate:
return val.Name; return val.Name;
case ValKind.Variable: case ValKind.Pointer:
if (IsPointerType(val.Type)) if (IsPointerType(val.Type))
{ {
return val.Name; return val.Name;
@@ -1325,6 +1395,12 @@ public static class QBEGenerator
} }
} }
internal class StringLiteral(string value, string name)
{
public string Value { get; } = value;
public string Name { get; } = name;
}
internal class CStringLiteral(string value, string name) internal class CStringLiteral(string value, string name)
{ {
public string Value { get; } = value; public string Value { get; } = value;
@@ -1337,7 +1413,7 @@ internal class Variable(string name, Val val)
public Val Val { get; } = val; public Val Val { get; } = val;
} }
internal class Val(string name, NubType type, ValKind kind = ValKind.Variable) internal class Val(string name, NubType type, ValKind kind)
{ {
public string Name { get; } = name; public string Name { get; } = name;
public NubType Type { get; } = type; public NubType Type { get; } = type;
@@ -1352,6 +1428,6 @@ internal class Val(string name, NubType type, ValKind kind = ValKind.Variable)
internal enum ValKind internal enum ValKind
{ {
Func, Func,
Variable, Pointer,
Literal Immediate
} }

View File

@@ -39,6 +39,6 @@ public record AnonymousFuncNode(IEnumerable<Token> Tokens, List<FuncParameter> P
public record AddressOfNode(IEnumerable<Token> Tokens, LValueNode Expression) : ExpressionNode(Tokens); public record AddressOfNode(IEnumerable<Token> Tokens, LValueNode Expression) : ExpressionNode(Tokens);
public record FixedArrayInitializerNode(IEnumerable<Token> Tokens, NubType ElementType, int Capacity) : ExpressionNode(Tokens); public record FixedArrayInitializerNode(IEnumerable<Token> Tokens, NubType ElementType, int Capacity) : ExpressionNode(Tokens);
public record LiteralNode(IEnumerable<Token> Tokens, string Literal, LiteralKind Kind) : ExpressionNode(Tokens); public record LiteralNode(IEnumerable<Token> Tokens, string Literal, LiteralKind Kind) : ExpressionNode(Tokens);
public record MemberAccessNode(IEnumerable<Token> Tokens, ExpressionNode Expression, string Member) : LValueNode(Tokens); public record MemberAccessNode(IEnumerable<Token> Tokens, ExpressionNode Expression, string Member) : ExpressionNode(Tokens);
public record StructInitializerNode(IEnumerable<Token> Tokens, NubStructType StructType, Dictionary<string, ExpressionNode> Initializers) : ExpressionNode(Tokens); public record StructInitializerNode(IEnumerable<Token> Tokens, NubStructType StructType, Dictionary<string, ExpressionNode> Initializers) : ExpressionNode(Tokens);
public record DereferenceNode(IEnumerable<Token> Tokens, ExpressionNode Expression) : LValueNode(Tokens); public record DereferenceNode(IEnumerable<Token> Tokens, ExpressionNode Expression) : LValueNode(Tokens);

View File

@@ -668,7 +668,7 @@ public static class Parser
if (name.Value == "string") if (name.Value == "string")
{ {
return new NubArrayType(NubPrimitiveType.U8); return new NubStringType();
} }
if (name.Value == "cstring") if (name.Value == "cstring")

View File

@@ -347,7 +347,7 @@ public static class Binder
{ {
LiteralKind.Integer => NubPrimitiveType.I64, LiteralKind.Integer => NubPrimitiveType.I64,
LiteralKind.Float => NubPrimitiveType.F64, LiteralKind.Float => NubPrimitiveType.F64,
LiteralKind.String => new NubCStringType(), LiteralKind.String => new NubStringType(),
LiteralKind.Bool => NubPrimitiveType.Bool, LiteralKind.Bool => NubPrimitiveType.Bool,
_ => throw new ArgumentOutOfRangeException() _ => throw new ArgumentOutOfRangeException()
}; };
@@ -364,10 +364,12 @@ public static class Binder
switch (boundExpression.Type) switch (boundExpression.Type)
{ {
case NubArrayType: case NubArrayType:
case NubStringType:
case NubCStringType:
{ {
if (expression.Member == "count") if (expression.Member == "count")
{ {
type = NubPrimitiveType.I64; type = NubPrimitiveType.U64;
} }
break; break;

View File

@@ -16,6 +16,6 @@ public record BoundAnonymousFuncNode(IEnumerable<Token> Tokens, NubType Type, Li
public record BoundAddressOfNode(IEnumerable<Token> Tokens, NubType Type, BoundLValueNode Expression) : BoundExpressionNode(Tokens, Type); public record BoundAddressOfNode(IEnumerable<Token> Tokens, NubType Type, BoundLValueNode Expression) : BoundExpressionNode(Tokens, Type);
public record BoundFixedArrayInitializerNode(IEnumerable<Token> Tokens, NubType Type, NubType ElementType, int Capacity) : BoundExpressionNode(Tokens, Type); public record BoundFixedArrayInitializerNode(IEnumerable<Token> Tokens, NubType Type, NubType ElementType, int Capacity) : BoundExpressionNode(Tokens, Type);
public record BoundLiteralNode(IEnumerable<Token> Tokens, NubType Type, string Literal, LiteralKind Kind) : BoundExpressionNode(Tokens, Type); public record BoundLiteralNode(IEnumerable<Token> Tokens, NubType Type, string Literal, LiteralKind Kind) : BoundExpressionNode(Tokens, Type);
public record BoundMemberAccessNode(IEnumerable<Token> Tokens, NubType Type, BoundExpressionNode Expression, string Member) : BoundLValueNode(Tokens, Type); public record BoundMemberAccessNode(IEnumerable<Token> Tokens, NubType Type, BoundExpressionNode Expression, string Member) : BoundExpressionNode(Tokens, Type);
public record BoundStructInitializerNode(IEnumerable<Token> Tokens, NubType Type, NubStructType StructType, Dictionary<string, BoundExpressionNode> Initializers) : BoundExpressionNode(Tokens, Type); public record BoundStructInitializerNode(IEnumerable<Token> Tokens, NubType Type, NubStructType StructType, Dictionary<string, BoundExpressionNode> Initializers) : BoundExpressionNode(Tokens, Type);
public record BoundDereferenceNode(IEnumerable<Token> Tokens, NubType Type, BoundExpressionNode Expression) : BoundLValueNode(Tokens, Type); public record BoundDereferenceNode(IEnumerable<Token> Tokens, NubType Type, BoundExpressionNode Expression) : BoundLValueNode(Tokens, Type);

View File

@@ -4,21 +4,6 @@ namespace Syntax.Typing;
public abstract class NubType public abstract class NubType
{ {
public static bool IsCompatibleWith(NubType sourceType, NubType targetType)
{
if (targetType is NubAnyType || sourceType.Equals(targetType))
{
return true;
}
if (sourceType is NubFixedArrayType fixedArray && targetType is NubArrayType array && IsCompatibleWith(fixedArray.ElementType, array.ElementType))
{
return true;
}
return false;
}
public bool IsInteger => this is NubPrimitiveType public bool IsInteger => this is NubPrimitiveType
{ {
Kind: PrimitiveTypeKind.I8 Kind: PrimitiveTypeKind.I8
@@ -45,6 +30,15 @@ public abstract class NubType
public bool IsVoid => this is NubVoidType; public bool IsVoid => this is NubVoidType;
public bool IsString => this is NubStringType;
public bool IsCString => this is NubCStringType;
public bool IsBool => this is NubPrimitiveType
{
Kind: PrimitiveTypeKind.Bool
};
public abstract override bool Equals(object? obj); public abstract override bool Equals(object? obj);
public abstract override int GetHashCode(); public abstract override int GetHashCode();
public abstract override string ToString(); public abstract override string ToString();
@@ -54,7 +48,7 @@ public class NubCStringType : NubType
{ {
public override bool Equals(object? obj) public override bool Equals(object? obj)
{ {
return obj is NubCStringType other; return obj is NubCStringType;
} }
public override int GetHashCode() public override int GetHashCode()
@@ -68,6 +62,24 @@ public class NubCStringType : NubType
} }
} }
public class NubStringType : NubType
{
public override bool Equals(object? obj)
{
return obj is NubStringType;
}
public override int GetHashCode()
{
return "string".GetHashCode();
}
public override string ToString()
{
return "string";
}
}
public class NubFuncType(NubType returnType, List<NubType> parameters) : NubType public class NubFuncType(NubType returnType, List<NubType> parameters) : NubType
{ {
public NubType ReturnType { get; } = returnType; public NubType ReturnType { get; } = returnType;