From 918d25f8c8a83148c63ee4f8e2960a5d6a305b78 Mon Sep 17 00:00:00 2001 From: nub31 Date: Sun, 15 Mar 2026 01:38:59 +0100 Subject: [PATCH] perhaps --- compiler/Generator.cs | 565 ++++++++++++++++++++---------------------- compiler/Program.cs | 7 +- 2 files changed, 269 insertions(+), 303 deletions(-) diff --git a/compiler/Generator.cs b/compiler/Generator.cs index 88cf815..16bc27a 100644 --- a/compiler/Generator.cs +++ b/compiler/Generator.cs @@ -17,6 +17,106 @@ public class Generator this.entryPoint = entryPoint; } + const string NUB_H_CONTENTS = + """ + #include + #include + #include + + typedef struct + { + char *data; + size_t length; + size_t ref; + uint32_t flags; + } string; + + typedef string *string_ptr; + + #define FLAG_STRING_LITERAL 1 + + static inline void string_rc_inc(string_ptr str) + { + if (str == NULL) + return; + + if (str->flags & FLAG_STRING_LITERAL) + return; + + str->ref += 1; + } + + static inline void string_rc_dec(string_ptr str) + { + if (str == NULL) + return; + + if (str->flags & FLAG_STRING_LITERAL) + return; + + if (str->ref == 0) + return; + + str->ref -= 1; + + if (str->ref == 0) + { + free(str->data); + free(str); + } + } + + static inline string_ptr string_concat(string_ptr left, string_ptr right) + { + size_t new_length = left->length + right->length; + + string_ptr result = (string_ptr)malloc(sizeof(string)); + result->data = (char*)malloc(new_length + 1); + + memcpy(result->data, left->data, left->length); + memcpy(result->data + left->length, right->data, right->length); + + result->data[new_length] = '\0'; + result->length = new_length; + result->ref = 1; + result->flags = 0; + + return result; + } + + static inline string_ptr string_from_cstr(char *cstr) + { + size_t len = strlen(cstr); + + string_ptr result = (string_ptr)malloc(sizeof(string)); + result->data = (char*)malloc(len + 1); + + memcpy(result->data, cstr, len + 1); + + result->length = len; + result->ref = 1; + result->flags = 0; + + return result; + } + + #define da_append(xs, x) \ + do \ + { \ + if ((xs)->count >= (xs)->capacity) \ + { \ + if ((xs)->capacity == 0) \ + (xs)->capacity = 256; \ + else \ + (xs)->capacity *= 2; \ + (xs)->items = realloc((xs)->items, (xs)->capacity * sizeof(*(xs)->items)); \ + } \ + \ + (xs)->items[(xs)->count++] = (x); \ + } while (0) + + """; + private readonly List functions; private readonly ModuleGraph moduleGraph; private readonly string? entryPoint; @@ -29,16 +129,18 @@ public class Generator private string Emit() { + var outPath = ".build"; + var fileName = "out.c"; + if (entryPoint != null) { - writer.WriteLine($$""" - int main(int argc, char *argv[]) - { - return {{entryPoint}}(); - } - - """); - + writer.WriteLine("int main(int argc, char *argv[])"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine($"return {entryPoint}();"); + } + writer.WriteLine("}"); writer.WriteLine(); } @@ -47,9 +149,7 @@ public class Generator if (!moduleGraph.TryResolveIdentifier(function.Module, function.Name.Ident, true, out var info)) throw new UnreachableException($"Module graph does not have info about the function {function.Module}::{function.Name.Ident}. This should have been caught earlier"); - if (info.Source == Module.DefinitionSource.Imported || info.Extern) - writer.Write("extern "); - else if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported) + if (info.Source == Module.DefinitionSource.Internal && !info.Extern && !info.Exported) writer.Write("static "); var parameters = function.Parameters.Select(x => CType(x.Type, x.Name.Ident)); @@ -66,7 +166,7 @@ public class Generator writer.WriteLine(); } - var implementations = writer.ToString(); + var definitions = writer.ToString(); writer = new IndentedTextWriter(); @@ -85,163 +185,12 @@ public class Generator writer.WriteLine($"{CType(fn.ReturnType, info.MangledName)}({string.Join(", ", fn.Parameters.Select(p => CType(p)))});"); else writer.WriteLine($"{CType(info.Type, info.MangledName)};"); + + writer.WriteLine(); } } } - var declarations = writer.ToString(); - - writer = new IndentedTextWriter(); - - writer.WriteLine(""" -#include -#include -#include -#include - -typedef struct -{ - char *data; - size_t length; - uint32_t ref; - uint32_t flags; -} string; - -typedef uint8_t u8; -typedef int8_t i8; - -typedef uint16_t u16; -typedef int16_t i16; - -typedef uint32_t u32; -typedef int32_t i32; - -typedef uint64_t u64; -typedef int64_t i64; - -#define FLAG_STRING_LITERAL 1 - -#define RC_DBG(fmt, ...) fprintf(stderr, "[RC] " fmt "\n", ##__VA_ARGS__) - -static size_t rc_allocs = 0; -static size_t rc_frees = 0; - -__attribute__((destructor)) -static void string_debug_report(void) -{ - fprintf(stderr, "[RC] REPORT allocs=%zu frees=%zu leaks=%zu\n", rc_allocs, rc_frees, rc_allocs - rc_frees); -} - -static inline void string_rc_inc(string *str) -{ - if (str == NULL) - { - RC_DBG("INC null string"); - return; - } - - if (str->flags & FLAG_STRING_LITERAL) - return; - - str->ref += 1; - - RC_DBG("INC: str=%p ref=%u \"%s\"", (void *)str, str->ref, str->data); -} - -static inline void string_rc_dec(string *str) -{ - if (str == NULL) - { - RC_DBG("WRN: DEC null string"); - return; - } - - if (str->flags & FLAG_STRING_LITERAL) - return; - - if (str->ref == 0) - { - RC_DBG("ERR: DEC on zero refcount str=%p", (void *)str); - return; - } - - str->ref -= 1; - - RC_DBG("DEC: str=%p ref=%u \"%s\"", (void *)str, str->ref, str->data); - - if (str->ref == 0) - { - rc_frees++; - RC_DBG("FREE: str=%p data=%p \"%s\"", (void *)str, (void *)str->data, str->data); - - free(str->data); - free(str); - } -} - -static inline string *string_concat(string *left, string *right) -{ - size_t new_length = left->length + right->length; - - string *result = (string *)malloc(sizeof(string)); - result->data = (char *)malloc(new_length + 1); - - memcpy(result->data, left->data, left->length); - memcpy(result->data + left->length, right->data, right->length); - - result->data[new_length] = '\0'; - result->length = new_length; - result->ref = 1; - result->flags = 0; - - rc_allocs++; - RC_DBG("NEW string_concat: str=%p ref=%u \"%s\"", (void *)result, result->ref, result->data); - - return result; -} - -static inline string *string_from_cstr(char *cstr) -{ - size_t len = strlen(cstr); - - string *result = (string *)malloc(sizeof(string)); - result->data = (char *)malloc(len + 1); - - memcpy(result->data, cstr, len + 1); - - result->length = len; - result->ref = 1; - result->flags = 0; - - rc_allocs++; - RC_DBG("NEW string_from_cstr: str=%p ref=%u \"%s\"", (void *)result, result->ref, result->data); - - return result; -} - -#define da_append(xs, x) \ - do \ - { \ - if ((xs)->count >= (xs)->capacity) \ - { \ - if ((xs)->capacity == 0) (xs)->capacity = 256; \ - else (xs)->capacity *= 2; \ - (xs)->items = realloc((xs)->items, (xs)->capacity*sizeof(*(xs)->items)); \ - } \ - \ - (xs)->items[(xs)->count++] = (x); \ - } \ - while (0) - -"""); - - while (referencedTypes.Count != 0) - { - var type = referencedTypes.ElementAt(0); - EmitTypeDefinitionIfNotEmitted(type); - referencedTypes.Remove(type); - } - foreach (var (name, value) in referencedStringLiterals) { writer.WriteLine @@ -258,9 +207,35 @@ static inline string *string_from_cstr(char *cstr) ); } - var header = writer.ToString(); + var declarations = writer.ToString(); - return $"{header}\n{declarations}\n{implementations}"; + writer = new IndentedTextWriter(); + + while (referencedTypes.Count != 0) + { + var type = referencedTypes.ElementAt(0); + EmitTypeDefinitionIfNotEmitted(type); + referencedTypes.Remove(type); + } + + var types = writer.ToString(); + + var sb = new StringBuilder(); + + sb.AppendLine("#include \"nub.h\""); + sb.AppendLine(); + sb.AppendLine(types); + sb.AppendLine(declarations); + sb.AppendLine(definitions); + + Directory.CreateDirectory(outPath); + + var filePath = Path.Combine(outPath, fileName); + + File.WriteAllText(Path.Combine(outPath, "nub.h"), NUB_H_CONTENTS); + File.WriteAllText(filePath, sb.ToString()); + + return filePath; } private void EmitTypeDefinitionIfNotEmitted(NubType type) @@ -270,109 +245,10 @@ static inline string *string_from_cstr(char *cstr) emittedTypes.Add(type); + var name = TypeName(type); + switch (type) { - case NubTypeArray arrayType: - { - EmitTypeDefinitionIfNotEmitted(arrayType.ElementType); - - var name = typeNames[arrayType]; - - writer.WriteLine("typedef struct"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("size_t count;"); - writer.WriteLine("size_t capacity;"); - writer.WriteLine($"{CType(arrayType.ElementType)} *items;"); - writer.WriteLine("uint32_t ref;"); - } - writer.WriteLine($"}} {name};"); - writer.WriteLine(); - - writer.WriteLine($$""" -static inline void {{name}}_rc_inc({{name}} *array) -{ - if (array == NULL) - { - RC_DBG("WRN: INC null array"); - return; - } - - array->ref += 1; - - RC_DBG("INC: array=%p ref=%u", (void *)array, array->ref); -} - -"""); - - writer.WriteLine($"static inline void {name}_rc_dec({name} *array)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("if (array == NULL)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("RC_DBG(\"WRN: DEC null array\");"); - writer.WriteLine("return;"); - } - writer.WriteLine("}"); - writer.WriteLine(); - writer.WriteLine("if (array->ref == 0)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("RC_DBG(\"ERR: DEC on zero refcount\");"); - writer.WriteLine("return;"); - } - writer.WriteLine("}"); - writer.WriteLine(); - writer.WriteLine("array->ref -= 1;"); - writer.WriteLine(); - writer.WriteLine("RC_DBG(\"DEC: array=%p ref=%u\", (void*)array, array->ref);"); - writer.WriteLine(); - writer.WriteLine("if (array->ref == 0)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine("for (size_t i = 0; i < array->count; ++i)"); - writer.WriteLine("{"); - using (writer.Indent()) - { - EmitCopyDestructor("array->items[i]", arrayType.ElementType); - } - writer.WriteLine("}"); - writer.WriteLine(); - writer.WriteLine("rc_frees++;"); - writer.WriteLine("RC_DBG(\"FREE: array=%p data=%p\", (void *)array, (void *)array->items);"); - writer.WriteLine(); - writer.WriteLine("free(array);"); - } - writer.WriteLine("}"); - } - writer.WriteLine("}"); - writer.WriteLine(); - - writer.WriteLine($"static inline {name} *{name}_make()"); - writer.WriteLine("{"); - using (writer.Indent()) - { - writer.WriteLine($"{name} *array = ({name} *)malloc(sizeof({name}));"); - writer.WriteLine("array->ref = 1;"); - writer.WriteLine("array->count = 0;"); - writer.WriteLine("array->capacity = 0;"); - writer.WriteLine("array->items = NULL;"); - writer.WriteLine(); - writer.WriteLine("rc_allocs++;"); - writer.WriteLine($"RC_DBG(\"NEW {name}_make: array=%p ref=%u\", (void *)array, array->ref);"); - writer.WriteLine("return array;"); - } - writer.WriteLine("}"); - writer.WriteLine(); - - break; - } case NubTypeStruct structType: { if (!moduleGraph.TryResolveType(structType.Module, structType.Name, true, out var info) || info is not Module.TypeInfoStruct structInfo) @@ -393,7 +269,7 @@ static inline void {{name}}_rc_inc({{name}} *array) writer.WriteLine($"{CType(field.Type, field.Name)};"); } } - writer.WriteLine($"}} {typeNames[structType]};"); + writer.WriteLine($"}} {name};"); writer.WriteLine(); break; @@ -412,7 +288,7 @@ static inline void {{name}}_rc_inc({{name}} *array) writer.WriteLine($"{CType(field.Type, field.Name)};"); } } - writer.WriteLine($"}} {typeNames[anonymousStructType]};"); + writer.WriteLine($"}} {name};"); writer.WriteLine(); break; @@ -449,7 +325,7 @@ static inline void {{name}}_rc_inc({{name}} *array) } writer.WriteLine("};"); } - writer.WriteLine($"}} {typeNames[enumType]};"); + writer.WriteLine($"}} {name};"); writer.WriteLine(); break; @@ -457,6 +333,97 @@ static inline void {{name}}_rc_inc({{name}} *array) case NubTypeEnumVariant variantType: { EmitTypeDefinitionIfNotEmitted(variantType.EnumType); + break; + } + case NubTypePointer pointerType: + { + EmitTypeDefinitionIfNotEmitted(pointerType.To); + writer.WriteLine($"typedef {TypeName(pointerType.To)} *{name};"); + break; + } + case NubTypeFunc funcType: + { + EmitTypeDefinitionIfNotEmitted(funcType.ReturnType); + foreach (var parameterType in funcType.Parameters) + EmitTypeDefinitionIfNotEmitted(parameterType); + + writer.WriteLine($"typedef {TypeName(funcType.ReturnType)} (*)({string.Join(", ", funcType.Parameters.Select(TypeName))}) {name};"); + + break; + } + case NubTypeArray arrayType: + { + EmitTypeDefinitionIfNotEmitted(arrayType.ElementType); + + var backingName = Tmp(); + + writer.WriteLine("typedef struct"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine("size_t count;"); + writer.WriteLine("size_t capacity;"); + writer.WriteLine($"{CType(arrayType.ElementType)} *items;"); + writer.WriteLine("uint32_t ref;"); + } + writer.WriteLine($"}} {backingName};"); + writer.WriteLine(); + + writer.WriteLine($"typedef {backingName} *{name};"); + writer.WriteLine(); + + writer.WriteLine($"static inline void {name}_rc_inc({name} array)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine("if (array == NULL) return;"); + writer.WriteLine("array->ref += 1;"); + } + writer.WriteLine("}"); + writer.WriteLine(); + + writer.WriteLine($"static inline void {name}_rc_dec({name} array)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine("if (array == NULL) return;"); + writer.WriteLine("if (array->ref == 0) return;"); + writer.WriteLine(); + writer.WriteLine("array->ref -= 1;"); + writer.WriteLine(); + writer.WriteLine("if (array->ref == 0)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine("for (size_t i = 0; i < array->count; ++i)"); + writer.WriteLine("{"); + using (writer.Indent()) + { + EmitCopyDestructor("array->items[i]", arrayType.ElementType); + } + writer.WriteLine("}"); + writer.WriteLine(); + writer.WriteLine("free(array);"); + } + writer.WriteLine("}"); + } + writer.WriteLine("}"); + writer.WriteLine(); + + writer.WriteLine($"static inline {name} {name}_make()"); + writer.WriteLine("{"); + using (writer.Indent()) + { + writer.WriteLine($"{name} array = ({name})malloc(sizeof({backingName}));"); + writer.WriteLine("array->ref = 1;"); + writer.WriteLine("array->count = 0;"); + writer.WriteLine("array->capacity = 0;"); + writer.WriteLine("array->items = NULL;"); + writer.WriteLine("return array;"); + } + writer.WriteLine("}"); + writer.WriteLine(); + break; } } @@ -801,7 +768,7 @@ static inline void {{name}}_rc_inc({{name}} *array) var name = Tmp(); scopes.Peek().DeconstructableNames.Add((name, expression.Type)); - writer.WriteLine($"{CType(expression.Type, name)} = {typeNames[expression.Type]}_make();"); + writer.WriteLine($"{CType(expression.Type, name)} = {TypeName(expression.Type)}_make();"); foreach (var value in expression.Values) { @@ -869,39 +836,39 @@ static inline void {{name}}_rc_inc({{name}} *array) return tmp; } - public string CType(NubType node, string? varName = null) - { - referencedTypes.Add(node); - - return node switch - { - NubTypeVoid => "void" + (varName != null ? $" {varName}" : ""), - NubTypeBool => "bool" + (varName != null ? $" {varName}" : ""), - NubTypeStruct type => CTypeNamed(type, varName), - NubTypeAnonymousStruct type => CTypeNamed(type, varName), - NubTypeEnum type => CTypeNamed(type, varName), - NubTypeEnumVariant type => CType(type.EnumType, varName), - NubTypeSInt type => $"i{type.Width}" + (varName != null ? $" {varName}" : ""), - NubTypeUInt type => $"u{type.Width}" + (varName != null ? $" {varName}" : ""), - NubTypePointer type => CType(type.To) + (varName != null ? $" *{varName}" : "*"), - NubTypeString type => "string" + (varName != null ? $" *{varName}" : "*"), - NubTypeFunc type => $"{CType(type.ReturnType)} (*{varName})({string.Join(", ", type.Parameters.Select(p => CType(p)))})", - NubTypeArray type => $"{CTypeNamed(type)}" + (varName != null ? $" *{varName}" : "*"), - _ => throw new ArgumentOutOfRangeException(nameof(node), node, null) - }; - } - private readonly Dictionary typeNames = []; - private string CTypeNamed(NubType type, string? varName = null) + private string TypeName(NubType type) { if (!typeNames.TryGetValue(type, out var name)) { - name = Tmp(); + name = type switch + { + NubTypeVoid => "void", + NubTypeBool => "bool", + NubTypeStruct => Tmp(), + NubTypeAnonymousStruct => Tmp(), + NubTypeEnum => Tmp(), + NubTypeEnumVariant t => TypeName(t.EnumType), + NubTypeSInt t => $"int{t.Width}_t", + NubTypeUInt t => $"uint{t.Width}_t", + NubTypePointer => Tmp(), + NubTypeString => "string_ptr", + NubTypeFunc => Tmp(), + NubTypeArray => Tmp(), + _ => throw new NotImplementedException(), + }; + typeNames[type] = name; } - return $"{name}{(varName != null ? $" {varName}" : "")}"; + return name; + } + + private string CType(NubType node, string? varName = null) + { + referencedTypes.Add(node); + return $"{TypeName(node)}" + (varName != null ? $" {varName}" : ""); } private string Tmp() @@ -936,7 +903,7 @@ static inline void {{name}}_rc_inc({{name}} *array) { case NubTypeArray arrayType: { - writer.WriteLine($"{typeNames[arrayType]}_rc_inc({value});"); + writer.WriteLine($"{TypeName(type)}_rc_inc({value});"); break; } case NubTypeString: @@ -1012,7 +979,7 @@ static inline void {{name}}_rc_inc({{name}} *array) { case NubTypeArray arrayType: { - writer.WriteLine($"{typeNames[arrayType]}_rc_dec({value});"); + writer.WriteLine($"{TypeName(type)}_rc_dec({value});"); break; } case NubTypeString: diff --git a/compiler/Program.cs b/compiler/Program.cs index de22558..8234f95 100644 --- a/compiler/Program.cs +++ b/compiler/Program.cs @@ -147,18 +147,17 @@ if (!compileLib) entryPoint = info.MangledName; } -var output = Generator.Emit(functions, moduleGraph, entryPoint); -File.WriteAllText(".build/out.c", output); +var outFile = Generator.Emit(functions, moduleGraph, entryPoint); if (compileLib) { - Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-c", "-o", ".build/out.o", ".build/out.c", .. archivePaths]).WaitForExit(); + Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-c", "-o", ".build/out.o", outFile, .. archivePaths]).WaitForExit(); Process.Start("ar", ["rcs", ".build/out.a", ".build/out.o"]).WaitForExit(); NubLib.Pack(".build/out.nublib", ".build/out.a", Manifest.Create(moduleGraph)); } else { - Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-o", ".build/out", ".build/out.c", .. archivePaths]).WaitForExit(); + Process.Start("gcc", ["-Og", "-g", "-fno-builtin", "-o", ".build/out", outFile, .. archivePaths]).WaitForExit(); } return 0;