...
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
namespace c
|
||||
|
||||
// extern func printf(fmt: ^u8, ...args: any): void
|
||||
extern func printf(fmt: cstring, arg: u64): void
|
||||
extern func puts(fmt: cstring)
|
||||
|
||||
@@ -5,35 +5,39 @@ struct Human {
|
||||
}
|
||||
|
||||
export func main(args: []cstring): i64 {
|
||||
let human: Human
|
||||
// let human: Human
|
||||
|
||||
human = alloc Human {
|
||||
name = "member"
|
||||
}
|
||||
// human = alloc Human {
|
||||
// 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
|
||||
}
|
||||
|
||||
@@ -14,13 +14,11 @@
|
||||
<ProjectReference Include="..\Syntax\Syntax.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<EmbeddedResource Include="Runtime\entry.s" />
|
||||
<EmbeddedResource Include="Runtime\nub_mem.s" />
|
||||
<EmbeddedResource Include="Runtime\nub_panic.s" />
|
||||
<EmbeddedResource Include="Runtime\nub_cstring.s" />
|
||||
<EmbeddedResource Include="Runtime\nub_string.s" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
17
src/CLI/Runtime/nub_cstring.s
Normal file
17
src/CLI/Runtime/nub_cstring.s
Normal 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
|
||||
@@ -6,6 +6,7 @@
|
||||
nub_memcpy:
|
||||
mov rcx, rdx
|
||||
rep movsb
|
||||
ret
|
||||
|
||||
.text
|
||||
.globl nub_memset
|
||||
|
||||
@@ -1,80 +1,32 @@
|
||||
.intel_syntax noprefix
|
||||
|
||||
.equ SYS_MMAP, 9
|
||||
|
||||
.text
|
||||
.globl nub_strlen
|
||||
# func nub_strlen(string: cstring): u64
|
||||
nub_strlen:
|
||||
test rdi, rdi
|
||||
jz null_string
|
||||
xor rax, rax
|
||||
strlen_loop:
|
||||
cmp byte ptr [rdi + rax], 0
|
||||
je strlen_exit
|
||||
.globl nub_string_length
|
||||
# func nub_string_length(string: string): u64
|
||||
nub_string_length:
|
||||
mov rsi, [rdi] # Length of string in bytes
|
||||
|
||||
add rdi, 8 # Start of bytes
|
||||
xor rax, rax # Character count
|
||||
xor rdx, rdx # Current byte position
|
||||
|
||||
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
|
||||
jmp strlen_loop
|
||||
null_string:
|
||||
xor rax, rax
|
||||
strlen_exit:
|
||||
ret
|
||||
|
||||
.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
|
||||
|
||||
_skip_byte:
|
||||
inc rdx
|
||||
jmp _count_loop
|
||||
|
||||
_done:
|
||||
ret
|
||||
|
||||
@@ -16,6 +16,7 @@ public static class QBEGenerator
|
||||
|
||||
private static StringBuilder _builder = new();
|
||||
private static List<CStringLiteral> _cStringLiterals = [];
|
||||
private static List<StringLiteral> _stringLiterals = [];
|
||||
private static Stack<string> _breakLabels = [];
|
||||
private static Stack<string> _continueLabels = [];
|
||||
private static Queue<(BoundAnonymousFuncNode Func, string Name)> _anonymousFunctions = [];
|
||||
@@ -25,6 +26,7 @@ public static class QBEGenerator
|
||||
private static int _labelIndex;
|
||||
private static int _anonymousFuncIndex;
|
||||
private static int _cStringLiteralIndex;
|
||||
private static int _stringLiteralIndex;
|
||||
private static bool _codeIsReachable = true;
|
||||
|
||||
public static string Emit(BoundSyntaxTree syntaxTree, BoundDefinitionTable definitionTable)
|
||||
@@ -34,6 +36,7 @@ public static class QBEGenerator
|
||||
|
||||
_builder = new StringBuilder();
|
||||
_cStringLiterals = [];
|
||||
_stringLiterals = [];
|
||||
_breakLabels = [];
|
||||
_continueLabels = [];
|
||||
_anonymousFunctions = [];
|
||||
@@ -43,6 +46,7 @@ public static class QBEGenerator
|
||||
_labelIndex = 0;
|
||||
_anonymousFuncIndex = 0;
|
||||
_cStringLiteralIndex = 0;
|
||||
_stringLiteralIndex = 0;
|
||||
_codeIsReachable = true;
|
||||
|
||||
foreach (var structDef in _definitionTable.GetStructs())
|
||||
@@ -68,6 +72,12 @@ public static class QBEGenerator
|
||||
_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();
|
||||
}
|
||||
|
||||
@@ -86,6 +96,11 @@ public static class QBEGenerator
|
||||
return $"$cstring{++_cStringLiteralIndex}";
|
||||
}
|
||||
|
||||
private static string StringName()
|
||||
{
|
||||
return $"$string{++_stringLiteralIndex}";
|
||||
}
|
||||
|
||||
private static string FuncName(BoundFuncDefinition funcDef)
|
||||
{
|
||||
return funcDef switch
|
||||
@@ -128,6 +143,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "storel",
|
||||
NubFuncType => "storel",
|
||||
NubCStringType => "storel",
|
||||
NubStringType => "storel",
|
||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in store instructions")
|
||||
};
|
||||
}
|
||||
@@ -157,6 +173,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "loadl",
|
||||
NubFuncType => "loadl",
|
||||
NubCStringType => "loadl",
|
||||
NubStringType => "loadl",
|
||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in load instructions")
|
||||
};
|
||||
}
|
||||
@@ -186,6 +203,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "=l",
|
||||
NubFuncType => "=l",
|
||||
NubCStringType => "=l",
|
||||
NubStringType => "=l",
|
||||
_ => throw new NotSupportedException($"'{type}' type cannot be used in variables")
|
||||
};
|
||||
}
|
||||
@@ -213,6 +231,7 @@ public static class QBEGenerator
|
||||
case NubPointerType:
|
||||
case NubArrayType:
|
||||
case NubCStringType:
|
||||
case NubStringType:
|
||||
case NubFuncType:
|
||||
{
|
||||
return 8;
|
||||
@@ -241,12 +260,10 @@ public static class QBEGenerator
|
||||
{
|
||||
return primitiveType.Kind switch
|
||||
{
|
||||
PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 => 8,
|
||||
PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.Bool => 4,
|
||||
PrimitiveTypeKind.I64 or PrimitiveTypeKind.U64 or PrimitiveTypeKind.F64 => 8,
|
||||
PrimitiveTypeKind.I32 or PrimitiveTypeKind.U32 or PrimitiveTypeKind.F32 or PrimitiveTypeKind.Bool => 4,
|
||||
PrimitiveTypeKind.I16 or PrimitiveTypeKind.U16 => 2,
|
||||
PrimitiveTypeKind.I8 or PrimitiveTypeKind.U8 => 1,
|
||||
PrimitiveTypeKind.F64 => 8,
|
||||
PrimitiveTypeKind.F32 => 4,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
@@ -273,6 +290,7 @@ public static class QBEGenerator
|
||||
case NubPointerType:
|
||||
case NubArrayType:
|
||||
case NubCStringType:
|
||||
case NubStringType:
|
||||
case NubFuncType:
|
||||
{
|
||||
return 8;
|
||||
@@ -354,6 +372,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "l",
|
||||
NubFuncType => "l",
|
||||
NubCStringType => "l",
|
||||
NubStringType => "l",
|
||||
_ => throw new NotSupportedException($"'{returnType}' type cannot be used as a function return type")
|
||||
});
|
||||
_builder.Append(' ');
|
||||
@@ -386,6 +405,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "l",
|
||||
NubFuncType => "l",
|
||||
NubCStringType => "l",
|
||||
NubStringType => "l",
|
||||
_ => 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);
|
||||
@@ -468,6 +488,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType fixedArrayType => $"b {SizeOf(fixedArrayType)}",
|
||||
NubFuncType => "l",
|
||||
NubCStringType => "l",
|
||||
NubStringType => "l",
|
||||
_ => throw new NotSupportedException($"'{structDefinitionField.Type}' type cannot be used in structs")
|
||||
};
|
||||
_builder.Append(qbeType + ", ");
|
||||
@@ -657,7 +678,7 @@ public static class QBEGenerator
|
||||
{
|
||||
var tmp = VarName();
|
||||
_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)
|
||||
@@ -741,7 +762,7 @@ public static class QBEGenerator
|
||||
var pointer = EmitArrayIndexPointer(arrayIndexAccess);
|
||||
var outputName = VarName();
|
||||
_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)
|
||||
@@ -786,7 +807,7 @@ public static class QBEGenerator
|
||||
_builder.AppendLine($" {dataPointer} =l add {arrayPointer}, 8");
|
||||
_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)
|
||||
@@ -794,7 +815,7 @@ public static class QBEGenerator
|
||||
var result = EmitUnwrap(EmitExpression(dereference.Expression));
|
||||
var outputName = VarName();
|
||||
_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)
|
||||
@@ -802,22 +823,27 @@ public static class QBEGenerator
|
||||
switch (addressOf.Expression)
|
||||
{
|
||||
case BoundArrayIndexAccessNode arrayIndexAccess:
|
||||
{
|
||||
var pointer = EmitArrayIndexPointer(arrayIndexAccess);
|
||||
return new Val(pointer, addressOf.Type);
|
||||
return new Val(pointer, addressOf.Type, ValKind.Immediate);
|
||||
}
|
||||
case BoundDereferenceNode dereference:
|
||||
{
|
||||
return EmitExpression(dereference.Expression);
|
||||
}
|
||||
case BoundIdentifierNode identifier:
|
||||
{
|
||||
if (identifier.Namespace.HasValue)
|
||||
{
|
||||
throw new NotSupportedException("There is nothing to address in another namespace");
|
||||
}
|
||||
|
||||
return _variables.Single(x => x.Name == identifier.Name).Val;
|
||||
case BoundMemberAccessNode memberAccess:
|
||||
var ptr = EmitMemberAccessPointer(memberAccess);
|
||||
return new Val(ptr, addressOf.Type);
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -826,7 +852,7 @@ public static class QBEGenerator
|
||||
var left = EmitUnwrap(EmitExpression(binaryExpression.Left));
|
||||
var right = EmitUnwrap(EmitExpression(binaryExpression.Right));
|
||||
var outputName = VarName();
|
||||
var output = new Val(outputName, binaryExpression.Type);
|
||||
var output = new Val(outputName, binaryExpression.Type, ValKind.Immediate);
|
||||
|
||||
switch (binaryExpression.Operator)
|
||||
{
|
||||
@@ -1052,44 +1078,80 @@ public static class QBEGenerator
|
||||
|
||||
private static Val EmitLiteral(BoundLiteralNode literal)
|
||||
{
|
||||
if (literal.Type.IsInteger)
|
||||
{
|
||||
switch (literal.Kind)
|
||||
{
|
||||
case LiteralKind.Integer:
|
||||
return new Val(literal.Literal, literal.Type, ValKind.Literal);
|
||||
case LiteralKind.Float:
|
||||
return new Val(literal.Literal.Split(".").First(), literal.Type, ValKind.Literal);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
|
||||
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.Literal);
|
||||
}
|
||||
|
||||
if (literal.Type.IsFloat32)
|
||||
{
|
||||
var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
|
||||
var bits = BitConverter.SingleToInt32Bits(value);
|
||||
return new Val(bits.ToString(), literal.Type, ValKind.Literal);
|
||||
}
|
||||
|
||||
switch (literal.Kind)
|
||||
{
|
||||
case LiteralKind.Integer:
|
||||
{
|
||||
if (literal.Type.IsFloat32)
|
||||
{
|
||||
var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
|
||||
var bits = BitConverter.SingleToInt32Bits(value);
|
||||
return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
var value = float.Parse(literal.Literal, CultureInfo.InvariantCulture);
|
||||
var bits = BitConverter.SingleToInt32Bits(value);
|
||||
return new Val(bits.ToString(), literal.Type, ValKind.Immediate);
|
||||
}
|
||||
|
||||
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:
|
||||
var cStringLiteral = new CStringLiteral(literal.Literal, CStringName());
|
||||
_cStringLiterals.Add(cStringLiteral);
|
||||
return new Val(cStringLiteral.Name, literal.Type, ValKind.Literal);
|
||||
{
|
||||
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());
|
||||
_cStringLiterals.Add(cStringLiteral);
|
||||
return new Val(cStringLiteral.Name, literal.Type, ValKind.Immediate);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case LiteralKind.Bool:
|
||||
return new Val(bool.Parse(literal.Literal) ? "1" : "0", literal.Type, ValKind.Literal);
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
{
|
||||
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)
|
||||
@@ -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)
|
||||
@@ -1140,16 +1202,16 @@ public static class QBEGenerator
|
||||
{
|
||||
case NubPrimitiveType { Kind: PrimitiveTypeKind.I64 }:
|
||||
_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 }:
|
||||
_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 }:
|
||||
_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 }:
|
||||
_builder.AppendLine($" {outputName} =s neg {operand}");
|
||||
return new Val(outputName, unaryExpression.Type);
|
||||
return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1160,7 +1222,7 @@ public static class QBEGenerator
|
||||
{
|
||||
case NubPrimitiveType { Kind: PrimitiveTypeKind.Bool }:
|
||||
_builder.AppendLine($" {outputName} =w xor {operand}, 1");
|
||||
return new Val(outputName, unaryExpression.Type);
|
||||
return new Val(outputName, unaryExpression.Type, ValKind.Immediate);
|
||||
}
|
||||
|
||||
break;
|
||||
@@ -1174,43 +1236,50 @@ public static class QBEGenerator
|
||||
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 output = VarName();
|
||||
|
||||
switch (memberAccess.Expression.Type)
|
||||
{
|
||||
case NubArrayType:
|
||||
{
|
||||
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:
|
||||
{
|
||||
var structDefinition = _definitionTable.LookupStruct(structType.Namespace, structType.Name).GetValue();
|
||||
var offset = OffsetOf(structDefinition, memberAccess.Member);
|
||||
|
||||
|
||||
var offsetName = VarName();
|
||||
_builder.AppendLine($" {offsetName} =l add {item}, {offset}");
|
||||
return offsetName;
|
||||
_builder.AppendLine($" {output} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {item}");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
throw new ArgumentOutOfRangeException(nameof(memberAccess.Expression.Type));
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static Val EmitMemberAccess(BoundMemberAccessNode memberAccess)
|
||||
{
|
||||
var pointer = EmitMemberAccessPointer(memberAccess);
|
||||
|
||||
var output = VarName();
|
||||
_builder.AppendLine($" {output} {QBEAssign(memberAccess.Type)} {QBELoad(memberAccess.Type)} {pointer}");
|
||||
return new Val(output, memberAccess.Type);
|
||||
|
||||
return new Val(output, memberAccess.Type, ValKind.Immediate);
|
||||
}
|
||||
|
||||
private static Val EmitFixedArrayInitializer(BoundFixedArrayInitializerNode fixedArrayInitializer)
|
||||
@@ -1227,7 +1296,7 @@ public static class QBEGenerator
|
||||
var dataSize = totalSize - 8;
|
||||
_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)
|
||||
@@ -1264,6 +1333,7 @@ public static class QBEGenerator
|
||||
NubFixedArrayType => "l",
|
||||
NubFuncType => "l",
|
||||
NubCStringType => "l",
|
||||
NubStringType => "l",
|
||||
_ => throw new NotSupportedException($"'{parameter.Type}' type cannot be used in function calls")
|
||||
};
|
||||
parameterStrings.Add($"{qbeType} {result}");
|
||||
@@ -1275,12 +1345,12 @@ public static class QBEGenerator
|
||||
{
|
||||
var outputName = VarName();
|
||||
_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
|
||||
{
|
||||
_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)
|
||||
{
|
||||
case ValKind.Func:
|
||||
case ValKind.Literal:
|
||||
case ValKind.Immediate:
|
||||
return val.Name;
|
||||
case ValKind.Variable:
|
||||
case ValKind.Pointer:
|
||||
if (IsPointerType(val.Type))
|
||||
{
|
||||
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)
|
||||
{
|
||||
public string Value { get; } = value;
|
||||
@@ -1337,7 +1413,7 @@ internal class Variable(string name, Val 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 NubType Type { get; } = type;
|
||||
@@ -1352,6 +1428,6 @@ internal class Val(string name, NubType type, ValKind kind = ValKind.Variable)
|
||||
internal enum ValKind
|
||||
{
|
||||
Func,
|
||||
Variable,
|
||||
Literal
|
||||
Pointer,
|
||||
Immediate
|
||||
}
|
||||
@@ -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 FixedArrayInitializerNode(IEnumerable<Token> Tokens, NubType ElementType, int Capacity) : 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 DereferenceNode(IEnumerable<Token> Tokens, ExpressionNode Expression) : LValueNode(Tokens);
|
||||
|
||||
@@ -668,7 +668,7 @@ public static class Parser
|
||||
|
||||
if (name.Value == "string")
|
||||
{
|
||||
return new NubArrayType(NubPrimitiveType.U8);
|
||||
return new NubStringType();
|
||||
}
|
||||
|
||||
if (name.Value == "cstring")
|
||||
|
||||
@@ -347,7 +347,7 @@ public static class Binder
|
||||
{
|
||||
LiteralKind.Integer => NubPrimitiveType.I64,
|
||||
LiteralKind.Float => NubPrimitiveType.F64,
|
||||
LiteralKind.String => new NubCStringType(),
|
||||
LiteralKind.String => new NubStringType(),
|
||||
LiteralKind.Bool => NubPrimitiveType.Bool,
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
@@ -364,10 +364,12 @@ public static class Binder
|
||||
switch (boundExpression.Type)
|
||||
{
|
||||
case NubArrayType:
|
||||
case NubStringType:
|
||||
case NubCStringType:
|
||||
{
|
||||
if (expression.Member == "count")
|
||||
{
|
||||
type = NubPrimitiveType.I64;
|
||||
type = NubPrimitiveType.U64;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
@@ -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 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 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 BoundDereferenceNode(IEnumerable<Token> Tokens, NubType Type, BoundExpressionNode Expression) : BoundLValueNode(Tokens, Type);
|
||||
|
||||
@@ -4,21 +4,6 @@ namespace Syntax.Typing;
|
||||
|
||||
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
|
||||
{
|
||||
Kind: PrimitiveTypeKind.I8
|
||||
@@ -45,6 +30,15 @@ public abstract class NubType
|
||||
|
||||
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 int GetHashCode();
|
||||
public abstract override string ToString();
|
||||
@@ -54,7 +48,7 @@ public class NubCStringType : NubType
|
||||
{
|
||||
public override bool Equals(object? obj)
|
||||
{
|
||||
return obj is NubCStringType other;
|
||||
return obj is NubCStringType;
|
||||
}
|
||||
|
||||
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 NubType ReturnType { get; } = returnType;
|
||||
|
||||
Reference in New Issue
Block a user