Author | Jakob Wakeling <[email protected]> |
Date | 2023-07-12 10:36:29 |
Commit | 8afca84b873628ea66a00a16be614d515f23f10c |
Parent | d2c465aaf479e4049cc7a79f14fc09a72a69f178 |
Implement strings, cstring(), and len()
Diffstat
M | README.md | | | 6 | ++++-- |
M | examples/hello.g | | | 11 | ++++++++--- |
M | src/analyse.c | | | 12 | ++++++------ |
M | src/init.c | | | 11 | ++++++++++- |
M | src/lex.c | | | 5 | ++++- |
M | src/llvm.c | | | 209 | +++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------------- |
M | src/llvm.h | | | 5 | +++-- |
A | src/llvm/attr.c | | | 33 | +++++++++++++++++++++++++++++++++ |
A | src/llvm/attr.def | | | 4 | ++++ |
A | src/llvm/llvm.h | | | 22 | ++++++++++++++++++++++ |
M | src/log.c | | | 1 | + |
M | src/log.h | | | 7 | +++++-- |
M | src/main.c | | | 15 | ++++++++++----- |
M | src/parse.c | | | 13 | +++++++++---- |
M | src/parse.h | | | 4 | ++-- |
M | src/type.c | | | 11 | ++++++----- |
M | src/type.h | | | 20 | ++++++++++---------- |
17 files changed, 278 insertions, 111 deletions
diff --git a/README.md b/README.md index 6054900..ab9df5a 100644 --- a/README.md +++ b/README.md @@ -53,6 +53,8 @@ command. The second command will output an executable file, *a.out* by default. - [x] Implement pointers - [x] Implement arrays - [ ] Implement variable length arrays (?) +- [ ] Implement structs +- [ ] Implement unions - [x] Implement expressions - [x] Implement type casting - [ ] Implement type casting to pointers and arrays @@ -66,13 +68,13 @@ command. The second command will output an executable file, *a.out* by default. - [ ] Implement `defer` - [ ] Implement `errdefer` (?) - [ ] Implement variadic procedure arguments -- [ ] Implement first class strings +- [x] Implement first class strings - [x] Implement syscalls - [ ] Implement generics of some kind - [ ] Implement module definition - [ ] Implement module use - [ ] Implement foreign code calling -- [ ] Optional types using `?` prefix +- [ ] Optional types using `?` prefix (?) - [ ] Error or types using `!` prefix (?) - ... - [ ] Re-write compiler in **G** diff --git a/examples/hello.g b/examples/hello.g index 62f4cde..ecfebc2 100644 --- a/examples/hello.g +++ b/examples/hello.g @@ -1,4 +1,9 @@ -main :: proc() -> sint { - hello : [14]u8 = { 'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', '\n' }; - return #syscall(uint(1), sint(1), ptr(hello), uint(14)); +print :: proc(s: string) { + #syscall(uint(1), sint(1), cstring(s), len(s)); + return; +} + +main :: proc() -> u8 { + print("Hello, World!\n"); + return 0; } diff --git a/src/analyse.c b/src/analyse.c index fb1830e..0178a82 100644 --- a/src/analyse.c +++ b/src/analyse.c @@ -107,6 +107,7 @@ static void analyse_stmt_decl(ast *a, syt *st) { /* TODO */ } } + else if (value_type->k == TY_STRING) { if (A.t == NULL) { A.t = C[0]->t; } return; } else { note("TODO", A.ln, A.cl, -1, "Unhandled value kind %s", ast_ks[C[0]->k]); } } @@ -165,9 +166,8 @@ static void analyse_expr(ast *a, syt *st) { A.k = AK_CAST; A.t = sym->t; if (CL > 2) { note("TODO", A.ln, A.cl, 0, "Type casts must only have a single argument"); } } - - for (UINT i = 1; i < CL; i += 1) { analyse_expr(C[i], st); } - } break; + else if (sym->k == AK_PROC) { A.k = AK_BUILTIN; A.t = sym->t; } + } goto childs; case AK_SUBS: { assert(CL == 2); @@ -177,9 +177,9 @@ static void analyse_expr(ast *a, syt *st) { } break; case AK_PROC: { analyse_expr_proc(a, st); } break; case AK_HASH: { analyse_expr_hash(a, st); } break; - case AK_OP_ADO: { A.t = type_ptr(ast_type(C[0], st), 1); } break; - case AK_OP_DRF: { A.t = ast_type(C[0], st)->base; } break; - default: { for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); }} break; + case AK_OP_ADO: { A.t = type_ptr(ast_type(C[0], st), 1); } goto childs; + case AK_OP_DRF: { A.t = ast_type(C[0], st)->base; } goto childs; + childs: default: { for (UINT i = 0; i < CL; i += 1) { analyse_expr(C[i], st); }} break; } } diff --git a/src/init.c b/src/init.c index 542a9b0..c3a0dc1 100644 --- a/src/init.c +++ b/src/init.c @@ -11,7 +11,7 @@ #include <string.h> static ast kwds[] = { - { AK_TYPE, 0, 0, 0, "ptr", &TYPE(TY_PTR), { 0 }, NULL }, + { AK_TYPE, 0, 0, 0, "ptr", &TYPE(TY_PTR), { 0 }, NULL }, /* Boolean Types */ { AK_TYPE, 0, 0, 0, "bool", &TYPE(TY_BOOL), { 0 }, NULL }, @@ -41,6 +41,15 @@ static ast kwds[] = { { AK_TYPE, 0, 0, 0, "f64", &TYPE(TY_F64), { 0 }, NULL }, { AK_TYPE, 0, 0, 0, "f128", &TYPE(TY_F128), { 0 }, NULL }, + /* Character and String Types */ + { AK_TYPE, 0, 0, 0, "char", &TYPE(TY_CHAR), { 0 }, NULL }, + { AK_TYPE, 0, 0, 0, "rune", &TYPE(TY_RUNE), { 0 }, NULL }, + { AK_TYPE, 0, 0, 0, "string", &TYPE(TY_STRING), { 0 }, NULL }, + + /* Builtins */ + { AK_PROC, 0, 0, 0, "cstring", &(type){ TY_PTR, TF_PTR, -1, "", &TYPE(TY_U8) }, { 0 }, NULL }, + { AK_PROC, 0, 0, 0, "len", &TYPE(TY_UINT), { 0 }, NULL }, + { AK_VOID, 0, 0, 0, NULL, NULL, { 0 }, NULL } }; diff --git a/src/lex.c b/src/lex.c index 722ea95..c240889 100644 --- a/src/lex.c +++ b/src/lex.c @@ -115,7 +115,7 @@ tok lex_next(lex *l) { for (P += 1; is_alpha(C) || is_digit_dec(C); P += 1); if (C == '.') { T.k = TK_FLT; P += 1; for (P += 1; is_digit_dec(C); P += 1); } - /* TODO lex exponent part of real numbers */ + if (C == 'e') { P += (C == '+' || C == '-') ? 2 : 1; for (P += 1; is_digit_dec(C); P += 1); } UINT sl = P - s; CL += sl; @@ -286,6 +286,7 @@ void lex_debug(lex *l) { /* Parse an integer string into a value. */ static inline u64 parse_int(char *s) { + /* TODO lex exponent part of numbers */ register u64 v = 0; u64 c; register UINT b = 10; if (s[0] == '0') switch (s[1]) { @@ -309,6 +310,7 @@ static inline u64 parse_int(char *s) { } static inline f128 parse_flt(char *s) { + /* TODO lex exponent part of numbers */ register f128 v = 0; u64 c; char *endptr; v = strtold(s, &endptr); diff --git a/src/llvm.c b/src/llvm.c index b2a1eca..6522299 100644 --- a/src/llvm.c +++ b/src/llvm.c @@ -4,6 +4,7 @@ // All rights reserved. #include "llvm.h" +#include "llvm/llvm.h" #include "log.h" #include "parse.h" #include "symbol.h" @@ -38,12 +39,12 @@ static LLVMValueRef llvm_stmt_for(ast *a, syt *st); static LLVMValueRef llvm_expr(ast *a, syt *st, bool load); static LLVMValueRef llvm_expr_proc(ast *a, syt *st); static LLVMValueRef llvm_expr_cast(ast *a, syt *st); +static LLVMValueRef llvm_expr_builtin(ast *a, syt *st); static LLVMValueRef llvm_expr_hash(ast *a, syt *st); +static LLVMValueRef llvm_str(ast *a, syt *st, bool load); static LLVMValueRef llvm_arr(ast *a, syt *st); -static inline void llvm_init(char *file); -static inline void llvm_free(void); static LLVMTypeRef llvm_type(type *t); static LLVMValueRef llvm_ival(type *t); @@ -52,66 +53,70 @@ static LLVMValueRef llvm_ival(type *t); #define CL (a->c.al) /* AST child array length shorthand */ /* Generate IR from an AST with LLVM. */ -void llvm(ast *a, char *file) { - llvm_init(file); - - /* Generate IR for all child nodes */ - for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); } - - char *err; - LLVMVerifyModule(llvm_module, LLVMAbortProcessAction, &err); - LLVMDisposeMessage(err); - - char *triple = LLVMGetDefaultTargetTriple(); - LLVMSetTarget(llvm_module, triple); - - LLVMInitializeAllTargetInfos(); - LLVMInitializeAllTargets(); - LLVMInitializeAllTargetMCs(); - LLVMInitializeAllAsmParsers(); - LLVMInitializeAllAsmPrinters(); - - LLVMTargetRef target; LLVMGetTargetFromTriple(triple, &target, &err); - if (!target) { error(2, "LLVMGetTargetFromTriple: %s", err); } - - LLVMTargetMachineRef machine = LLVMCreateTargetMachine( - target, triple, "generic", "", LLVMCodeGenLevelNone, - LLVMRelocDefault, LLVMCodeModelDefault - ); - - LLVMTargetMachineEmitToFile(machine, llvm_module, "llvm.o", LLVMObjectFile, &err); - - LLVMDisposeTargetMachine(machine); llvm_free(); -} - -void llvm_bitcode(ast *a, char *file) { - llvm_init(file); +void llvm(ast *a, char *file, llvm_action action) { + llvm_context = LLVMGetGlobalContext(); + llvm_module = LLVMModuleCreateWithName((file_name = file)); + llvm_builder = LLVMCreateBuilder(); /* Generate IR for all child nodes */ for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); } - if (LLVMWriteBitcodeToFile(llvm_module, "llvm.bc")) { - error(2, "LLVMWriteBitcodeToFile failure"); - } + char *err = NULL; bool failed = false; - char *err = NULL; - LLVMVerifyModule(llvm_module, LLVMAbortProcessAction, &err); - LLVMDisposeMessage(err); llvm_free(); -} - -void llvm_ir(ast *a, char *file) { - llvm_init(file); + failed = LLVMVerifyModule(llvm_module, LLVMPrintMessageAction, &err); + LLVMDisposeMessage(err); err = NULL; - /* Generate IR for all child nodes */ - for (UINT i = 0; i < a->c.al; i += 1) { llvm_stmt_decl(a->c.a[i], &a->st); } - - if (LLVMPrintModuleToFile(llvm_module, "llvm.ll", NULL)) { - error(2, "LLVMPrintModuleToFile failure"); + switch (action) { + case llvm_obj: case llvm_asm: { + // if (failed) { break; } /* Don't compile after failure */ + + char *triple = LLVMGetDefaultTargetTriple(); + printf("Target: %s\n", triple); + LLVMSetTarget(llvm_module, triple); + + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargets(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllAsmParsers(); + LLVMInitializeAllAsmPrinters(); + + LLVMTargetRef target = NULL; + + if (LLVMGetTargetFromTriple(triple, &target, &err)) { + panic("LLVMGetTargetFromTriple: %s", err); + } + if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL; + + LLVMTargetMachineRef machine = LLVMCreateTargetMachine( + target, triple, "generic", "", LLVMCodeGenLevelNone, + LLVMRelocPIC, LLVMCodeModelDefault + ); + + char *outfile = action == llvm_obj ? "llvm.o" : "llvm.s"; + int outform = action == llvm_obj ? LLVMObjectFile : LLVMAssemblyFile; + if (LLVMTargetMachineEmitToFile(machine, llvm_module, outfile, outform, &err)) { + panic("LLVMTargetMachineEmitToFile: %s", err); + } + if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL; + + LLVMDisposeTargetMachine(machine); + } break; + case llvm_ir: { + if (LLVMPrintModuleToFile(llvm_module, "llvm.ll", &err)) { + panic("LLVMPrintModuleToFile: %s", err); + } + if (err != NULL) { panic("%s", err); } LLVMDisposeMessage(err); err = NULL; + } break; + case llvm_bc: { + if (LLVMWriteBitcodeToFile(llvm_module, "llvm.bc")) { + panic("LLVMWriteBitcodeToFile"); + } + } break; } - char *err = NULL; - LLVMVerifyModule(llvm_module, LLVMAbortProcessAction, &err); - LLVMDisposeMessage(err); llvm_free(); + LLVMDisposeBuilder(llvm_builder); + LLVMDisposeModule(llvm_module); + LLVMShutdown(); } /* Generate IR for a statement. */ @@ -157,11 +162,20 @@ static LLVMValueRef llvm_stmt_decl(ast *a, syt *st) { if (A.p->k == AK_PROG) /* Global */ { LLVMValueRef v = LLVMAddGlobal(llvm_module, llvm_type(A.t), ""); LLVMSetInitializer(v, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t)); + LLVMSetVisibility(v, LLVMHiddenVisibility); /* TODO configurable visibility */ A.llvm_v = v; } else /* Local */ { LLVMValueRef v = LLVMBuildAlloca(llvm_builder, llvm_type(A.t), ""); - LLVMBuildStore(llvm_builder, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), v); + + if (A.t->k == TY_STRING) { + LLVMBuildMemCpy( + llvm_builder, v, 8, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), 8, + LLVMConstInt(LLVMIntType(64), 16, false) + ); + } + else { LLVMBuildStore(llvm_builder, CL ? llvm_expr(C[0], st, true) : llvm_ival(A.t), v); } + A.llvm_v = v; } } @@ -240,6 +254,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) { case AK_BOOL: { return LLVMConstInt(llvm_type(a->t), a->v_bool, false); } break; case AK_INT: { return LLVMConstInt(llvm_type(a->t), a->v_int, false); } break; case AK_FLT: { return LLVMConstReal(llvm_type(a->t), a->v_flt); } break; + case AK_STR: { return llvm_str(a, st, load); } break; case AK_ARR: { return llvm_arr(a, st); } break; case AK_PROC: { return llvm_expr_proc(a, st); } break; case AK_CAST: { return llvm_expr_cast(a, st); } break; @@ -259,6 +274,7 @@ static LLVMValueRef llvm_expr(ast *a, syt *st, bool load) { LLVMValueRef args[CL - 1]; for (UINT i = 1; i < CL; i += 1) { args[i - 1] = llvm_expr(C[i], st, true); } return LLVMBuildCall2(llvm_builder, sym->llvm_t, sym->llvm_v, args, CL - 1, ""); } break; + case AK_BUILTIN: { return llvm_expr_builtin(a, st); } break; case AK_HASH_SYSCALL: case AK_HASH: { return llvm_expr_hash(a, st); } break; case AK_SUBS: { @@ -467,6 +483,33 @@ static LLVMValueRef llvm_expr_cast(ast *a, syt *st) { return NULL; } +/* Generate IR for a builtin. */ +static LLVMValueRef llvm_expr_builtin(ast *a, syt *st) { + assert(A.k == AK_BUILTIN); + + ast *sym = syt_search_h(st, C[0]->h, C[0]->s); + if (sym == NULL) { note(file_name, A.ln, A.cl, -1, "Undefined builtin \"%s\" (llvm:llvm_expr_builtin)", C[0]->s); } + + if (strncmp(sym->s, "cstring", 7) == 0) { + assert(CL == 2); + if (ast_type(C[1], st)->k != TY_STRING) { panic("cstring builtin only takes strings, not \"%s\"s", ast_type(C[1], st)->s); } + + LLVMValueRef idc[2] = { LLVMConstInt(LLVMIntType(32), 0, false), LLVMConstInt(LLVMIntType(32), 0, false) }; + LLVMValueRef v = LLVMBuildInBoundsGEP2(llvm_builder, llvm_type(ast_type(C[1], st)), llvm_expr(C[1], st, false), idc, 2, ""); + return LLVMBuildLoad2(llvm_builder, LLVMPointerType(LLVMIntType(8), 0), v, ""); + } + else if (strncmp(sym->s, "len", 3) == 0) { + assert(CL == 2); + if (ast_type(C[1], st)->k != TY_STRING) { panic("len builtin only takes strings, not \"%s\"s", ast_type(C[1], st)->s); } + + LLVMValueRef idc[2] = { LLVMConstInt(LLVMIntType(32), 0, false), LLVMConstInt(LLVMIntType(32), 1, false) }; + LLVMValueRef v = LLVMBuildInBoundsGEP2(llvm_builder, llvm_type(ast_type(C[1], st)), llvm_expr(C[1], st, false), idc, 2, ""); + return LLVMBuildLoad2(llvm_builder, LLVMIntType(64), v, ""); + } + + return NULL; +} + /* Generate IR for a hash procedure. */ static LLVMValueRef llvm_expr_hash(ast *a, syt *st) { assert(A.k == AK_HASH_SYSCALL); @@ -503,6 +546,26 @@ static LLVMValueRef llvm_expr_hash(ast *a, syt *st) { return LLVMBuildCall2(llvm_builder, func_type, inline_asm, args, CL, ""); } +/* Generate IR for a string constant. */ +static LLVMValueRef llvm_str(ast *a, syt *st, bool load) { + assert(A.k == AK_STR); + + LLVMValueRef va[2] = { + LLVMBuildGlobalString(llvm_builder, A.s, "string"), + LLVMConstInt(LLVMIntType(64), strlen(A.s), false) + }; + + LLVMValueRef vs = LLVMAddGlobal(llvm_module, llvm_type(&TYPE(TY_STRING)), "string"); + LLVMSetInitializer(vs, LLVMConstNamedStruct(llvm_type(&TYPE(TY_STRING)), va, 2)); + LLVMSetGlobalConstant(vs, true); + LLVMSetLinkage(vs, LLVMPrivateLinkage); + LLVMSetUnnamedAddress(vs, LLVMGlobalUnnamedAddr); + LLVMSetAlignment(vs, 8); + + if (!load) { return vs; } + else { return LLVMBuildLoad2(llvm_builder, llvm_type(&TYPE(TY_STRING)), vs, ""); } +} + /* Generate IR for an array. */ static LLVMValueRef llvm_arr(ast *a, syt *st) { assert(A.k == AK_ARR); @@ -514,21 +577,6 @@ static LLVMValueRef llvm_arr(ast *a, syt *st) { return LLVMConstArray(llvm_type(C[0]->t), va, CL); } -/* Initialise LLVM. */ -static inline void llvm_init(char *file) { - file_name = file; - llvm_context = LLVMGetGlobalContext(); - llvm_module = LLVMModuleCreateWithName(file_name); - llvm_builder = LLVMCreateBuilder(); -} - -/* Uninitialise LLVM. */ -static inline void llvm_free(void) { - LLVMDisposeBuilder(llvm_builder); llvm_builder = NULL; - LLVMDisposeModule(llvm_module); llvm_module = NULL; - LLVMShutdown(); llvm_context = NULL; -} - /* Return the appropriate LLVMTypeRef for a G type. */ static LLVMTypeRef llvm_type(type *t) { switch (t->k) { @@ -552,6 +600,13 @@ static LLVMTypeRef llvm_type(type *t) { case TY_F32: { return LLVMFloatType(); } break; case TY_F64: { return LLVMDoubleType(); } break; case TY_F128: { return LLVMFP128Type(); } break; + case TY_STRING: { + static LLVMTypeRef str_t = NULL; if (str_t == NULL) { + str_t = LLVMStructCreateNamed(llvm_context, "string"); + LLVMTypeRef ta[2] = { LLVMPointerType(LLVMIntType(8), 0), LLVMIntType(64) }; + LLVMStructSetBody(str_t, ta, 2, false); + } return str_t; + } break; default: { return NULL; } break; } } @@ -582,6 +637,18 @@ static LLVMValueRef llvm_ival(type *t) { case TY_F32: { return LLVMConstReal(LLVMFloatType(), 0.0); } break; case TY_F64: { return LLVMConstReal(LLVMDoubleType(), 0.0); } break; case TY_F128: { return LLVMConstReal(LLVMFP128Type(), 0.0); } break; + case TY_STRING: { + static LLVMValueRef vs = NULL; if (vs == NULL) { + LLVMValueRef va[2] = { + LLVMBuildGlobalString(llvm_builder, "", ""), + LLVMConstInt(LLVMIntType(64), 0, false) + }; + + vs = LLVMAddGlobal(llvm_module, llvm_type(&TYPE(TY_STRING)), ""); + LLVMSetInitializer(vs, LLVMConstNamedStruct(llvm_type(&TYPE(TY_STRING)), va, 2)); + LLVMSetGlobalConstant(vs, true); + } return vs; + } default: { return NULL; } break; } } diff --git a/src/llvm.h b/src/llvm.h index 3349152..83b4f67 100644 --- a/src/llvm.h +++ b/src/llvm.h @@ -8,8 +8,8 @@ #include "parse.h" -extern void llvm(ast *a, char *file); -extern void llvm_bitcode(ast *a, char *file); -extern void llvm_ir(ast *a, char *file); +typedef enum { llvm_obj, llvm_asm, llvm_bc, llvm_ir } llvm_action; + +extern void llvm(ast *a, char *file, llvm_action o); #endif // G_LLVM_H_CZUMSHFW diff --git a/src/llvm/attr.c b/src/llvm/attr.c new file mode 100644 index 0000000..4aab1c2 --- /dev/null +++ b/src/llvm/attr.c @@ -0,0 +1,33 @@ +// attr.c +// LLVM attributes source file for G +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#include "../log.h" +#include "../util/util.h" +#include "llvm.h" + +#include <llvm-c/Core.h> +#include <llvm-c/Types.h> + +#include <string.h> + +#define ATTR(a, b) b, +static char *llvm_func_attr_s[] = { +#include "attr.def" +}; +#undef ATTR + +/* Add an attribute to a function, returns -1 if the attribute does not exist. */ +int LLVMAddFuncAttr(LLVMContextRef C, LLVMValueRef F, llvm_func_attr attr, unsigned index) { + u32 ak = LLVMGetEnumAttributeKindForName(llvm_func_attr_s[attr], strlen(llvm_func_attr_s[attr])); + if (ak == 0) { dprint(NULL, 0, 0, "Attribute \"%s\" does not exist", llvm_func_attr_s[attr]); return -1; } + + LLVMAttributeRef ar = LLVMCreateEnumAttribute(C, ak, 0); + LLVMAddAttributeAtIndex(F, index, ar); return 0; +} + +void LLVMAddStringAttr(LLVMContextRef C, LLVMValueRef F, const char *k, const char *v, unsigned index) { + LLVMAttributeRef ar = LLVMCreateStringAttribute(C, k, strlen(k), v, strlen(v)); + LLVMAddAttributeAtIndex(F, index, ar); +} diff --git a/src/llvm/attr.def b/src/llvm/attr.def new file mode 100644 index 0000000..bfefc91 --- /dev/null +++ b/src/llvm/attr.def @@ -0,0 +1,4 @@ +ATTR(NOINLINE, "noinline") +ATTR(NOUNWIND, "nounwind") +ATTR(OPTNONE, "optnone") +ATTR(UWTABLE, "uwtable") diff --git a/src/llvm/llvm.h b/src/llvm/llvm.h new file mode 100644 index 0000000..fa43176 --- /dev/null +++ b/src/llvm/llvm.h @@ -0,0 +1,22 @@ +// llvm/llvm.h +// LLVM header file for G +// Copyright (C) 2023, Jakob Wakeling +// All rights reserved. + +#ifndef G_LLVM_LLVM_H_ZW0CA5L6 +#define G_LLVM_LLVM_H_ZW0CA5L6 + +#include "../util/util.h" + +#include <llvm-c/Types.h> + +#define ATTR(a, b) LLVM_ATTR_##a, +typedef enum { +#include "attr.def" +} llvm_func_attr; +#undef ATTR + +extern int LLVMAddFuncAttr(LLVMContextRef C, LLVMValueRef F, llvm_func_attr attr, unsigned index); +extern void LLVMAddStringAttr(LLVMContextRef C, LLVMValueRef F, const char *k, const char *v, unsigned index); + +#endif // G_LLVM_LLVM_H_ZW0CA5L6 diff --git a/src/log.c b/src/log.c index 2d6184d..58b469c 100644 --- a/src/log.c +++ b/src/log.c @@ -22,6 +22,7 @@ void note(const char *file, UINT ln, UINT cl, sint level, const char *format, .. if (file) { fprintf(stderr, "%s:%lu:%lu: ", file, ln + 1, cl + 1); } if (level <= -1) { fprintf(stderr, "fatal: "); } + else if (level == 9) { fprintf(stderr, "debug: "); } else if (level == 0) { fprintf(stderr, "error: "); } else if (level <= 3) { fprintf(stderr, "warning: "); } else if (level >= 4) { fprintf(stderr, "note: "); } diff --git a/src/log.h b/src/log.h index 8ca274e..6f56c89 100644 --- a/src/log.h +++ b/src/log.h @@ -16,12 +16,14 @@ extern sint log_level, log_limit; extern void note(const char *file, UINT ln, UINT cl, sint level, const char *format, ...); extern bool has_error(void); +extern noreturn void __panic(const char *file, UINT ln, const char *format, ...); + #ifdef NDEBUG +#define dprint(file, ln, cl, format, ...) ((void)0) #define panic(format, ...) (__panic(NULL, 0, format __VA_OPT__(,) __VA_ARGS__)) #else +#define dprint(file, ln, cl, format, ...) (note(file, ln, cl, 9, format __VA_OPT__(,) __VA_ARGS__)) #define panic(format, ...) (__panic(__FILE__, __LINE__, format __VA_OPT__(,) __VA_ARGS__)) #endif -extern noreturn void __panic(const char *file, UINT ln, const char *format, ...); - #endif // G_LOG_H_1RPM5P9E diff --git a/src/main.c b/src/main.c index 9c52165..fde1694 100644 --- a/src/main.c +++ b/src/main.c @@ -24,7 +24,9 @@ static struct lop lops[] = { static bool bflag = false, Bflag = false, cflag = false; static bool Eflag = false, pflag = false, Pflag = false; -static bool qflag = false; +static bool qflag = false, Sflag = false; + +static int verbosity = 0; static void compile(const char *file, char *src, UINT len); static void compile_file(const char *file); @@ -37,7 +39,7 @@ static void hlp(void); static void ver(void); int main(int ac, char *av[]) { A0 = av[0]; - struct opt opt = OPTGET_INIT; opt.str = "bBcEf:O:pPqW:"; opt.lops = lops; + struct opt opt = OPTGET_INIT; opt.str = "bBcEf:O:pPqSvW:"; opt.lops = lops; for (int o; (o = optget(&opt, av, 1)) != -1;) switch (o) { case 'b': { bflag = true; } break; /* Output LLVM IR files */ case 'B': { Bflag = true; } break; /* Output LLVM bitcode files */ @@ -48,6 +50,8 @@ int main(int ac, char *av[]) { A0 = av[0]; case 'p': { pflag = true; } break; /* Output parser AST */ case 'P': { Pflag = true; } break; /* Output analyser AST */ case 'q': { qflag = true; } break; /* Silence certain outputs (for benchmarking) */ + case 'S': { Sflag = true; } break; /* Output assembly files */ + case 'v': { verbosity += 1; } break; /* Increase verbosity */ case 'W': { opt_W(opt.arg); } break; /* Configure warnings and errors */ case 256: { hlp(); } return 0; case 257: { ver(); } return 0; @@ -74,9 +78,10 @@ static void compile(const char * file, char *src, UINT len) { if (Pflag) { if (!qflag) { ast_print(a, 0); } goto end; } if (has_error()) { exit(1); } - if (bflag) { llvm_ir(a, strdup(file)); } - else if (Bflag) { llvm_bitcode(a, strdup(file)); } - else { llvm(a, strdup(file)); } + if (bflag) { llvm(a, strdup(file), llvm_ir); } + else if (Bflag) { llvm(a, strdup(file), llvm_bc); } + else if (Sflag) { llvm(a, strdup(file), llvm_asm); } + else { llvm(a, strdup(file), llvm_obj); } end:; return; } diff --git a/src/parse.c b/src/parse.c index f96234c..71035ca 100644 --- a/src/parse.c +++ b/src/parse.c @@ -33,9 +33,9 @@ char *ast_ks[] = { "AK_ASSIGN", "AK_AS_ADD", "AK_AS_SUB", "AK_AS_MUL", "AK_AS_DIV", "AK_AS_MOD", - "AK_ID", "AK_CALL", "AK_BOOL", "AK_INT", "AK_FLT", "AK_ARR", "AK_SUBS", + "AK_ID", "AK_CALL", "AK_BOOL", "AK_INT", "AK_FLT", "AK_ARR", "AK_SUBS", "AK_STR", - "AK_HASH", "AK_HASH_SYSCALL" + "AK_HASH", "AK_HASH_SYSCALL", "AK_BUILTIN" }; static ast *parse_stmt(lex *l, syt *st); @@ -71,7 +71,7 @@ inline void ast_free(ast **a) { if (a == NULL || *a == NULL) { return; } if ((*a)->s != NULL) { free((*a)->s); } if ((*a)->c.a != NULL) { free((*a)->c.a); } - free(*a); a = NULL; /* TODO free LLVM pointers? */ + free(*a); a = NULL; } /* Push a child AST node to an AST node. */ @@ -307,6 +307,10 @@ static ast *parse_expr(lex *l, syt *st, s32 o) { boolean: { left->t = &TYPE(TY_BOOL); lex_next(l); } break; case TK_INT: { left = parse_int(l, st); } break; case TK_FLT: { left = parse_flt(l, st); } break; + case TK_STR: { + left = ast_init(AK_STR, T.ln, T.cl); left->h = T.h; + left->s = strdup_or_fail(T.s); left->t = &TYPE(TY_STRING); lex_next(l); + } break; case TK_LBRACE: { return parse_expr_compound(l, st); } break; case TK_PROC: { return parse_expr_proc(l, st); } break; case TK_HASH: { @@ -537,7 +541,7 @@ static s32 ast_precedence(ast_k ak) { /* Duplicate a string or fail. */ static inline char *strdup_or_fail(const char *s) { register char *r = strdup(s); - if (r == NULL) { error(1, "%s", SERR); } + if (r == NULL) { panic("%s", SERR); } return r; } diff --git a/src/parse.h b/src/parse.h index 8e4c781..fc75a35 100644 --- a/src/parse.h +++ b/src/parse.h @@ -29,9 +29,9 @@ typedef enum { AK_ASSIGN, AK_AS_ADD, AK_AS_SUB, AK_AS_MUL, AK_AS_DIV, AK_AS_MOD, - AK_ID, AK_CALL, AK_BOOL, AK_INT, AK_FLT, AK_ARR, AK_SUBS, + AK_ID, AK_CALL, AK_BOOL, AK_INT, AK_FLT, AK_ARR, AK_SUBS, AK_STR, - AK_HASH, AK_HASH_SYSCALL + AK_HASH, AK_HASH_SYSCALL, AK_BUILTIN } ast_k; /* diff --git a/src/type.c b/src/type.c index 8247b38..0bf3f62 100644 --- a/src/type.c +++ b/src/type.c @@ -95,10 +95,10 @@ type types[] = { { TY_Q256BE, TF_CLX | TF_SIGN | TF_BE, 32, "q256be" }, { TY_Q512BE, TF_CLX | TF_SIGN | TF_BE, 4, "q512be" }, - { TY_BYTE, TF_INT, 1, "byte" }, - { TY_CHAR, TF_INT | TF_CHAR, 1, "char" }, - { TY_RUNE, TF_INT | TF_CHAR, 4, "rune" }, - { TY_STR, TF_STR, -1, "str" }, + { TY_BYTE, TF_INT, 1, "byte" }, + { TY_CHAR, TF_INT | TF_CHAR, 1, "char" }, + { TY_RUNE, TF_INT | TF_CHAR, 4, "rune" }, + { TY_STRING, TF_STRING, 16, "string" }, }; static type_a types_a = { NULL, 0, 0 }; @@ -195,5 +195,5 @@ inline bool is_com(type *t1, type *t2) { if (is_int(t1) && is_int(t2)) { return true; } if (is_flt(t1) && is_flt(t2)) { return true; } - return false; /* TODO */ + return false; } diff --git a/src/type.h b/src/type.h index 31f8b6f..32d8296 100644 --- a/src/type.h +++ b/src/type.h @@ -18,8 +18,8 @@ typedef enum { TY_UINT, TY_U8, TY_U16, TY_U32, TY_U64, TY_U128, TY_SINT, TY_S8, TY_S16, TY_S32, TY_S64, TY_S128, - TY_F16, TY_F32, TY_F64, TY_F128, - TY_C32, TY_C64, TY_C128, TY_C256, + TY_F16, TY_F32, TY_F64, TY_F128, + TY_C32, TY_C64, TY_C128, TY_C256, TY_Q64, TY_Q128, TY_Q256, TY_Q512, TY_U16LE, TY_U32LE, TY_U64LE, TY_U128LE, @@ -37,19 +37,19 @@ typedef enum { TY_Q64LE, TY_Q128LE, TY_Q256LE, TY_Q512LE, TY_Q64BE, TY_Q128BE, TY_Q256BE, TY_Q512BE, - TY_BYTE, TY_CHAR, TY_RUNE, TY_STR, + TY_BYTE, TY_CHAR, TY_RUNE, TY_STRING, } type_k; typedef enum { - TF_NULL = BIT(0), TF_PTR = BIT(1), - TF_BOOL = BIT(2), TF_INT = BIT(3), - TF_FLT = BIT(4), TF_CLX = BIT(5), - TF_QTN = BIT(6), TF_SIGN = BIT(7), + TF_NULL = BIT(0), TF_PTR = BIT(1), + TF_BOOL = BIT(2), TF_INT = BIT(3), + TF_FLT = BIT(4), TF_CLX = BIT(5), + TF_QTN = BIT(6), TF_SIGN = BIT(7), - TF_LE = BIT(14), TF_BE = BIT(15), - TF_CHAR = BIT(16), TF_STR = BIT(17), + TF_LE = BIT(14), TF_BE = BIT(15), + TF_CHAR = BIT(16), TF_STRING = BIT(17), - TF_NUM = TF_INT | TF_FLT | TF_CLX, + TF_NUM = TF_INT | TF_FLT | TF_CLX, } type_f; /* k : Kind, f : Flags, l : Length, s : String */