From 990918b609c1b303b074604e56fb738db1b80e6a Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Thu, 14 Nov 2019 17:23:56 +0100 Subject: [PATCH] LLVM Codegen --- CMakeLists.txt | 145 +++++++- resources/testfragments/super_simple.c3 | 48 +++ src/build/build_options.c | 40 +- src/build/build_options.h | 12 + src/compiler/ast.c | 85 ++++- src/compiler/casts.c | 10 +- src/compiler/codegen.c | 6 + src/compiler/compiler.c | 17 +- src/compiler/compiler.h | 1 + src/compiler/compiler_internal.h | 103 +++++- src/compiler/diagnostics.c | 76 ++-- src/compiler/dwarf.h | 284 ++++++++++++++ src/compiler/enums.h | 43 ++- src/compiler/expr_analysis.c | 2 + src/compiler/lexer.c | 121 +++--- src/compiler/llvm_codegen.c | 136 +++++++ src/compiler/llvm_codegen_debug_info.c | 83 +++++ src/compiler/llvm_codegen_expr.c | 379 +++++++++++++++++++ src/compiler/llvm_codegen_function.c | 187 ++++++++++ src/compiler/llvm_codegen_internal.h | 96 +++++ src/compiler/llvm_codegen_module.c | 121 ++++++ src/compiler/llvm_codegen_stmt.c | 471 ++++++++++++++++++++++++ src/compiler/llvm_codegen_type.c | 188 ++++++++++ src/compiler/parser.c | 14 +- src/compiler/semantic_analyser.c | 89 ++++- src/compiler/source_file.c | 70 +++- src/compiler/target.c | 100 +++++ src/compiler/types.c | 108 +++++- src/compiler_tests/tests.c | 28 ++ src/utils/file_utils.c | 20 + src/utils/lib.h | 7 +- src/utils/stringutils.c | 24 ++ 32 files changed, 2911 insertions(+), 203 deletions(-) create mode 100644 resources/testfragments/super_simple.c3 create mode 100644 src/compiler/dwarf.h create mode 100644 src/compiler/llvm_codegen.c create mode 100644 src/compiler/llvm_codegen_debug_info.c create mode 100644 src/compiler/llvm_codegen_expr.c create mode 100644 src/compiler/llvm_codegen_function.c create mode 100644 src/compiler/llvm_codegen_internal.h create mode 100644 src/compiler/llvm_codegen_module.c create mode 100644 src/compiler/llvm_codegen_stmt.c create mode 100644 src/compiler/llvm_codegen_type.c create mode 100644 src/compiler/target.c create mode 100644 src/utils/stringutils.c diff --git a/CMakeLists.txt b/CMakeLists.txt index 0172b843d..3dcfae407 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,134 @@ cmake_minimum_required(VERSION 3.13) -project(c3c C) -set(CMAKE_CXX_FLAGS_RELEASE "-O3") +project(c3c) +#set(CMAKE_CXX_FLAGS_RELEASE "-O3") + +find_package(LLVM REQUIRED CONFIG) + +message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}") +message(STATUS "Using LLVMConfig.cmake in: ${LLVM_DIR}") + +include_directories(${LLVM_INCLUDE_DIRS}) +add_definitions(${LLVM_DEFINITIONS}) + set(CMAKE_C_STANDARD 11) +set(LLVM_LINK_COMPONENTS + Analysis + BitReader + Core + ExecutionEngine + InstCombine + Interpreter + MC + MCDisassembler + MCJIT + Object + OrcJIT + RuntimeDyld + ScalarOpts + Support + Target + TransformUtils + native + nativecodegen + AsmPrinter + AArch64Info + AArch64Desc + AArch64AsmParser + AArch64Disassembler + AArch64CodeGen + ARMInfo + ARMDesc + ARMAsmParser + ARMDisassembler + ARMCodeGen + AMDGPUInfo + AMDGPUAsmParser + AMDGPUCodeGen + BPFInfo + BPFAsmParser + BPFDisassembler + BPFCodeGen + HexagonInfo + HexagonDesc + HexagonAsmParser + HexagonDisassembler + HexagonCodeGen + LanaiInfo + LanaiDesc + LanaiAsmParser + LanaiDisassembler + LanaiCodeGen + MSP430Info + MSP430Desc + MSP430AsmParser + MSP430Disassembler + MSP430CodeGen + MipsInfo + MipsDesc + MipsAsmParser + MipsDisassembler + MipsCodeGen + NVPTXInfo + NVPTXCodeGen + PowerPCInfo + PowerPCDesc + PowerPCAsmParser + PowerPCDisassembler + PowerPCCodeGen + RISCVInfo + RISCVDesc + RISCVAsmParser + RISCVDisassembler + RISCVCodeGen + RISCVUtils + SparcInfo + SparcDesc + SparcAsmParser + SparcDisassembler + SparcCodeGen + SystemZInfo + SystemZDesc + SystemZAsmParser + SystemZDisassembler + SystemZCodeGen + WebAssemblyInfo + WebAssemblyDesc + WebAssemblyAsmParser + WebAssemblyDisassembler + WebAssemblyCodeGen + X86Info + X86Desc + X86AsmParser + X86Disassembler + X86CodeGen + X86Utils + XCoreInfo + XCoreDesc + XCoreDisassembler + XCoreCodeGen + ) +# XRay WindowsManifest Symbolize JITLink MCA LTO Passes ObjCARCOpts +#LineEditor LibDriver Interpreter FuzzMutate MCJIT ExecutionEngine +#RuntimeDyld DlltoolDriver Option DebugInfoGSYM Coverage Coroutines +# +# +# +# NVPTXDesc +# +# +# H +#BPFDesc ARMUtils +#AMDGPUDisassembler MIRParser ipo Instrumentation +#Vectorize Linker IRReader AsmParser AMDGPUDesc AMDGPUUtils +# MCDisassembler GlobalISel SelectionDAG +# DebugInfoDWARF CodeGen Target ScalarOpts InstCombine +#AggressiveInstCombine TransformUtils BitWriter Analysis ProfileData Object +#TextAPI BitReader Core Remarks BitstreamReader AArch64AsmParser MCParser +# MC DebugInfoCodeView DebugInfoMSF BinaryFormat AArch64Utils +# Support Demangle +llvm_map_components_to_libnames(llvm_libs support core irreader ${LLVM_LINK_COMPONENTS}) + include_directories( "${CMAKE_SOURCE_DIR}/src/" "${CMAKE_SOURCE_DIR}/build/") @@ -25,6 +151,19 @@ add_executable(c3c src/compiler/source_file.c src/compiler/diagnostics.c src/compiler/ast.c - src/compiler/bigint.c src/compiler/bigint.h src/compiler/context.c src/compiler/codegen.c src/compiler/expr_analysis.c src/compiler/enums.h src/compiler/casts.c src/compiler/compiler.h src/compiler/types.c src/compiler/module.c) + src/compiler/bigint.c + src/compiler/bigint.h + src/compiler/context.c + src/compiler/codegen.c + src/compiler/expr_analysis.c + src/compiler/enums.h + src/compiler/casts.c + src/compiler/target.c + src/compiler/compiler.h + src/compiler/types.c + src/compiler/module.c + src/compiler/llvm_codegen.c src/utils/stringutils.c src/compiler/dwarf.h src/compiler/llvm_codegen_stmt.c src/compiler/llvm_codegen_internal.h src/compiler/llvm_codegen_expr.c src/compiler/llvm_codegen_debug_info.c src/compiler/llvm_codegen_module.c src/compiler/llvm_codegen_type.c src/compiler/llvm_codegen_function.c) target_compile_options(c3c PRIVATE -Werror -Wall -Wextra -Wno-unused-function -Wno-unused-variable -Wno-unused-parameter) + +target_link_libraries(c3c ${llvm_libs}) \ No newline at end of file diff --git a/resources/testfragments/super_simple.c3 b/resources/testfragments/super_simple.c3 new file mode 100644 index 000000000..83f016ee8 --- /dev/null +++ b/resources/testfragments/super_simple.c3 @@ -0,0 +1,48 @@ +module bar; + +func void test(int x) +{ + int i = x; + switch (i + 1) + { + case 1: + i = i + 1; + next; + case 3: + case 2: + i = i + 2; + case 5: + default: + i = i * 100; + case 7: + } + return; +} + +func int test3() +{ + return 1; +} +typedef func void(int) as Foo; +//typedef int as Foo; + +func void test2(int* x, int y, int z) +{ + int i = 3; + uint ui = 2; + int j = 129; + float f = 23.2; + f = f + 1.0; + f = f - 1.0; + f = f * 2.0; + f = f / 3.0; + i = i * 2; + ui = ui * 2; + i = i / 2; + ui = ui / 2; + i = i + 1; + ui = ui + 1; + i = i - 1; + ui = ui - 1; + // TODO x + 1; +} \ No newline at end of file diff --git a/src/build/build_options.c b/src/build/build_options.c index b86161730..ca4e86d04 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -13,7 +13,6 @@ #include "../utils/errors.h" -static const char* DEFAULT_TARGET = "default"; static const int DEFAULT_SYMTAB_SIZE = 64 * 1024; static const int MAX_SYMTAB_SIZE = 1024 * 1024; @@ -52,6 +51,11 @@ static void usage(void) OUTPUT(" --symtab - Sets the preferred symtab size."); OUTPUT(" -E - Lex only."); OUTPUT(" -P - Only parse and output the AST as S-expressions."); + OUTPUT(" -O0 - Optimizations off."); + OUTPUT(" -O1 - Simple optimizations only."); + OUTPUT(" -O2 - Default optimization level."); + OUTPUT(" -Os - Optimize for size."); + OUTPUT(" -O3 - Aggressive optimization."); } @@ -117,7 +121,7 @@ static void parse_optional_target() { if (at_end() || next_is_opt()) { - build_options.target = DEFAULT_TARGET; + build_options.target = NULL; } else { @@ -199,6 +203,32 @@ static void parse_option() { case 'h': break; + case 'O': + if (build_options.optimization_level != OPTIMIZATION_NOT_SET) + { + FAIL_WITH_ERR("Multiple optimization levels were set."); + } + if (match_shortopt("O0")) + { + build_options.optimization_level = OPTIMIZATION_NONE; + } + else if (match_shortopt("O1")) + { + build_options.optimization_level = OPTIMIZATION_LESS; + } + else if (match_shortopt("O2") || match_shortopt("Os")) + { + build_options.optimization_level = OPTIMIZATION_DEFAULT; + } + else if (match_shortopt("O3")) + { + build_options.optimization_level = OPTIMIZATION_AGGRESSIVE; + } + else + { + FAIL_WITH_ERR("Invalid optimization level."); + } + return; case 'E': if (build_options.compile_option != COMPILE_NORMAL) { @@ -270,6 +300,8 @@ void parse_arguments(int argc, const char *argv[]) build_options.clonglong_size = sizeof(long long); build_options.pointer_size = sizeof(void *); build_options.path = "."; + build_options.optimization_level = OPTIMIZATION_NOT_SET; + build_options.debug_info = true; build_options.command = COMMAND_MISSING; build_options.symtab_size = DEFAULT_SYMTAB_SIZE; build_options.files = VECNEW(const char *, MAX_FILES); @@ -312,4 +344,8 @@ void parse_arguments(int argc, const char *argv[]) { FAIL_WITH_ERR("No command found."); } + if (build_options.optimization_level == OPTIMIZATION_NOT_SET) + { + build_options.optimization_level = OPTIMIZATION_DEFAULT; + } } diff --git a/src/build/build_options.h b/src/build/build_options.h index 195e5ea55..ea0bb518c 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -69,6 +69,15 @@ typedef enum COMPILE_OUTPUT_AST, } CompileOption; +typedef enum +{ + OPTIMIZATION_NOT_SET, + OPTIMIZATION_NONE, + OPTIMIZATION_LESS, + OPTIMIZATION_DEFAULT, + OPTIMIZATION_AGGRESSIVE +} OptimizationLevel; + typedef struct { const char* lib_dir[MAX_LIB_DIRS]; @@ -77,6 +86,7 @@ typedef struct const char* project_name; const char* target; const char* path; + const char* cpu; CompilerCommand command; uint32_t symtab_size; CompileOption compile_option; @@ -87,6 +97,8 @@ typedef struct int clong_size; int clonglong_size; int clongdouble_size; + OptimizationLevel optimization_level; + bool debug_info; } BuildOptions; diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 3295cc640..00c3fc01b 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -99,20 +99,62 @@ Type* type_get_unsigned(Type *type) */ +BinaryOp binary_op[256] = { + [TOKEN_STAR] = BINARYOP_MULT, + [TOKEN_DIV] = BINARYOP_DIV, + [TOKEN_PLUS] = BINARYOP_ADD, + [TOKEN_MINUS] = BINARYOP_SUB, + [TOKEN_MOD] = BINARYOP_MOD, + [TOKEN_SHL] = BINARYOP_SHL, + [TOKEN_SHR] = BINARYOP_SHR, + [TOKEN_AND] = BINARYOP_AND, + [TOKEN_OR] = BINARYOP_OR, + [TOKEN_AMP] = BINARYOP_BIT_AND, + [TOKEN_BIT_OR] = BINARYOP_BIT_OR, + [TOKEN_BIT_XOR] = BINARYOP_BIT_XOR, + [TOKEN_EQEQ] = BINARYOP_EQ, + [TOKEN_NOT_EQUAL] = BINARYOP_NE, + [TOKEN_LESS] = BINARYOP_LT, + [TOKEN_LESS_EQ] = BINARYOP_LE, + [TOKEN_GREATER] = BINARYOP_GT, + [TOKEN_GREATER_EQ] = BINARYOP_GE, + [TOKEN_EQ] = BINARYOP_ASSIGN, + [TOKEN_MULT_ASSIGN] = BINARYOP_MULT_ASSIGN, + [TOKEN_PLUS_ASSIGN] = BINARYOP_ADD_ASSIGN, + [TOKEN_MINUS_ASSIGN] = BINARYOP_SUB_ASSIGN, + [TOKEN_DIV_ASSIGN] = BINARYOP_DIV_ASSIGN, + [TOKEN_MOD_ASSIGN] = BINARYOP_MOD_ASSIGN, + [TOKEN_AND_ASSIGN] = BINARYOP_AND_ASSIGN, + [TOKEN_OR_ASSIGN] = BINARYOP_OR_ASSIGN, + [TOKEN_BIT_AND_ASSIGN] = BINARYOP_BIT_AND_ASSIGN, + [TOKEN_BIT_OR_ASSIGN] = BINARYOP_BIT_OR_ASSIGN, + [TOKEN_BIT_XOR_ASSIGN] = BINARYOP_BIT_XOR_ASSIGN, + [TOKEN_SHR_ASSIGN] = BINARYOP_SHR_ASSIGN, + [TOKEN_SHL_ASSIGN] = BINARYOP_SHL_ASSIGN, +}; + + +static BinaryOp assign_binop[256] = { + [BINARYOP_MULT_ASSIGN] = BINARYOP_MULT, + [BINARYOP_ADD_ASSIGN] = BINARYOP_ADD, + [BINARYOP_SUB_ASSIGN] = BINARYOP_SUB, + [BINARYOP_DIV_ASSIGN] = BINARYOP_DIV, + [BINARYOP_MOD_ASSIGN] = BINARYOP_MOD, + [BINARYOP_AND_ASSIGN] = BINARYOP_AND, + [BINARYOP_OR_ASSIGN] = BINARYOP_OR, + [BINARYOP_BIT_AND_ASSIGN] = BINARYOP_BIT_AND, + [BINARYOP_BIT_OR_ASSIGN] = BINARYOP_BIT_OR, + [BINARYOP_BIT_XOR_ASSIGN] = BINARYOP_BIT_XOR, + [BINARYOP_SHR_ASSIGN] = BINARYOP_SHR, + [BINARYOP_SHL_ASSIGN] = BINARYOP_SHL, +}; + +BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op) +{ + return assign_binop[(int)binary_op]; +} + AssignOp assign_op[256] = { - [TOKEN_EQ] = ASSIGNOP_ASSIGN, - [TOKEN_MULT_ASSIGN] = ASSIGNOP_MULT_ASSIGN, - [TOKEN_PLUS_ASSIGN] = ASSIGNOP_ADD_ASSIGN, - [TOKEN_MINUS_ASSIGN] = ASSIGNOP_SUB_ASSIGN, - [TOKEN_DIV_ASSIGN] = ASSIGNOP_DIV_ASSIGN, - [TOKEN_MOD_ASSIGN] = ASSIGNOP_MOD_ASSIGN, - [TOKEN_AND_ASSIGN] = ASSIGNOP_AND_ASSIGN, - [TOKEN_OR_ASSIGN] = ASSIGNOP_OR_ASSIGN, - [TOKEN_BIT_AND_ASSIGN] = ASSIGNOP_BIT_AND_ASSIGN, - [TOKEN_BIT_OR_ASSIGN] = ASSIGNOP_BIT_OR_ASSIGN, - [TOKEN_BIT_XOR_ASSIGN] = ASSIGNOP_BIT_XOR_ASSIGN, - [TOKEN_SHR_ASSIGN] = ASSIGNOP_SHR_ASSIGN, - [TOKEN_SHL_ASSIGN] = ASSIGNOP_SHL_ASSIGN, }; UnaryOp unary_op[256] = { @@ -141,6 +183,20 @@ TokenType assignop_to_token(AssignOp type) return TOKEN_INVALID_TOKEN; } +BinaryOp binaryop_from_token(TokenType type) +{ + return binary_op[type]; +} + +TokenType binaryop_to_token(BinaryOp type) +{ + for (unsigned i = 0; i < 256; i++) + { + if (binary_op[i] == type) return (TokenType)i; + } + return TOKEN_INVALID_TOKEN; +} + UnaryOp unaryop_from_token(TokenType type) { return unary_op[type]; @@ -183,6 +239,9 @@ void fprint_type_recursive(FILE *file, Type *type, int indent) case TYPE_POISONED: fprintf(file, "(POISON)\n"); return; + case TYPE_FUNC: + fprintf(file, "(FUNC %s)\n", type->func.signature->mangled_signature); + break; case TYPE_USER_DEFINED: if (type->resolve_status == RESOLVE_DONE) { diff --git a/src/compiler/casts.c b/src/compiler/casts.c index 0ed7dbb5f..c775e5051 100644 --- a/src/compiler/casts.c +++ b/src/compiler/casts.c @@ -59,7 +59,7 @@ bool ptxi(Expr* left, Type *canonical, Type *type, CastType cast_type) left->type = type; return true; } - insert_cast(left, CAST_PTRINT, canonical); + insert_cast(left, CAST_PTRXI, canonical); return true; } @@ -503,10 +503,10 @@ Type *ARITHMETIC_PROMOTION[19][19] = { { &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_i32, &t_i32, &t_i32, &t_i64, &t_i32, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // int { &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_i64, &t_f32, &t_f64, &t_err, &t_err, &t_isz, &t_err, &t_err, &t_err }, // long { &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_i8, &t_i16, &t_i32, &t_i64, &t_ixx, &t_f32, &t_f64, &t_fxx, &t_err, &t_isz, &t_err, &t_err, &t_err }, // ixx - { &t_u8, &t_i8, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u8, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // byte - { &t_u16, &t_i16, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u16, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ushort - { &t_u32, &t_i32, &t_i32, &t_i32, &t_i64, &t_uxx, &t_u32, &t_u32, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uint - { &t_u64, &t_i64, &t_i64, &t_i64, &t_i64, &t_uxx, &t_u64, &t_u64, &t_u64, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ulong + { &t_u8, &t_i8, &t_i16, &t_i32, &t_i64, &t_u8, &t_u8, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // byte + { &t_u16, &t_i16, &t_i16, &t_i32, &t_i64, &t_u16, &t_u16, &t_u16, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ushort + { &t_u32, &t_i32, &t_i32, &t_i32, &t_i64, &t_u32, &t_u32, &t_u32, &t_u32, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uint + { &t_u64, &t_i64, &t_i64, &t_i64, &t_i64, &t_u64, &t_u64, &t_u64, &t_u64, &t_i64, &t_uxx, &t_f32, &t_f64, &t_err, &t_err, &t_usz, &t_err, &t_err, &t_err }, // ulong { &t_u1, &t_i8, &t_i16, &t_i32, &t_i64, &t_uxx, &t_u8, &t_u16, &t_u32, &t_u64, &t_uxx, &t_f32, &t_f64, &t_fxx, &t_err, &t_usz, &t_err, &t_err, &t_err }, // uxx { &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f32, &t_f64, &t_f32, &t_err, &t_err, &t_err, &t_err, &t_err }, // float { &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_f64, &t_err, &t_err, &t_err, &t_err, &t_err }, // double diff --git a/src/compiler/codegen.c b/src/compiler/codegen.c index 964f871e2..d85ac4280 100644 --- a/src/compiler/codegen.c +++ b/src/compiler/codegen.c @@ -94,6 +94,9 @@ static void print_typename(FILE *file, Type *type) print_typename(file, type->base); fprintf(file, "[%zu]", type->len); break; + case TYPE_FUNC: + fprintf(file, "%s", type->name_loc.string); + break; case TYPE_EXPRESSION: UNREACHABLE } @@ -179,6 +182,9 @@ static inline void codegen_emit_const_expr_raw(Context *context, Expr *expr) case TYPE_VOID: UNREACHABLE break; + case TYPE_FUNC: + TODO + break; case TYPE_BOOL: assert(expr->const_expr.type == CONST_BOOL); PRINTF(expr->const_expr.b ? "true" : "false"); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index 915876567..56f23e392 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -51,7 +51,6 @@ void compiler_parse() void compiler_compile() { - builtin_setup(); Context **contexts = NULL; VECEACH(build_options.files, i) { @@ -63,6 +62,7 @@ void compiler_compile() vec_add(contexts, context); parse_file(context); } + /* const char *printf = "printf"; TokenType t_type = TOKEN_IDENT; const char *interned = symtab_add(printf, (uint32_t) 6, fnv1a(printf, (uint32_t)6), &t_type); @@ -75,7 +75,7 @@ void compiler_compile() decl->func.function_signature.rtype = type_void; decl->resolve_status = RESOLVE_DONE; context_register_global_decl(contexts[0], decl); - +*/ VECEACH(contexts, i) { sema_analysis_pass_conditional_compilation(contexts[i]); @@ -88,21 +88,16 @@ void compiler_compile() VECEACH(contexts, i) { Context *context = contexts[i]; - char buffer[255]; - sprintf(buffer, "%s_test.c", context->module_name.string); - FILE *f = fopen(buffer,"w"); - fprintf(f, "#include \n#include \n"); - context->codegen_output = f; - codegen(context); - fclose(f); - sprintf(buffer, "cc %s_test.c && ./a.out", context->module_name.string); - system(buffer); + llvm_codegen(context); } exit(EXIT_SUCCESS); } void compile_file() { + target_setup(); + builtin_setup(); + if (!vec_size(build_options.files)) error_exit("No files to compile."); switch (build_options.compile_option) { diff --git a/src/compiler/compiler.h b/src/compiler/compiler.h index cc465f184..9f5aaed69 100644 --- a/src/compiler/compiler.h +++ b/src/compiler/compiler.h @@ -6,3 +6,4 @@ void compiler_init(); void compile_file(); + diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 385ef7615..7f389a3a5 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -67,12 +67,24 @@ typedef struct typedef struct { const char *contents; - const char *name; + char *name; + char *dir_path; const char *full_path; SourceLoc start_id; SourceLoc end_id; + SourceLoc *line_start; + SourceLoc current_line_start; + int last_line_found; } File; +typedef struct +{ + File *file; + uint32_t line; + uint32_t col; + SourceLoc loc; + const char *start; +} SourcePosition; typedef struct { @@ -80,6 +92,32 @@ typedef struct Token sub_module; } Path; +typedef struct +{ + unsigned bitsize : 16; + unsigned char bytesize; +} TypeBuiltin; + +typedef struct +{ + Path *path; +} TypeUnresolved; + +typedef struct +{ + Type *base; + union + { + Expr *unresolved_len; + size_t len; + }; +} TypeArray; + +typedef struct +{ + struct _FunctionSignature *signature; +} TypeFunc; + struct _Type { TypeKind type_kind : 6; @@ -87,21 +125,15 @@ struct _Type Type *canonical; Token name_loc; Type **ptr_like_canonical; + void *backend_type; + void *backend_debug_type; union { Decl *decl; - struct - { - unsigned bitsize : 16; - unsigned char bytesize; - } builtin; - struct - { - Path *path; - } unresolved; + TypeBuiltin builtin; + TypeUnresolved unresolved; Expr *unresolved_type_expr; - struct - { + struct { Type *base; union { @@ -109,6 +141,7 @@ struct _Type size_t len; }; }; + TypeFunc func; }; }; @@ -147,6 +180,8 @@ typedef struct _VarDecl VarDeclKind kind : 3; Type *type; Expr *init_expr; + void *backend_ref; + void *backend_debug_ref; } VarDecl; @@ -177,12 +212,13 @@ typedef struct } EnumDecl; -typedef struct +typedef struct _FunctionSignature { bool variadic : 1; Type *rtype; Decl** params; Token *throws; + const char *mangled_signature; } FunctionSignature; typedef struct @@ -197,6 +233,9 @@ typedef struct FunctionSignature function_signature; Ast *body; FuncAnnotations *annotations; + Decl **locals; + Ast **labels; + void *backend_value; } FuncDecl; typedef struct @@ -237,6 +276,7 @@ typedef struct _Decl DeclKind decl_kind : 6; Visibility visibility : 2; ResolveStatus resolve_status : 2; + bool is_packed : 1; /* bool is_exported : 1; bool is_used : 1; bool is_used_public : 1; @@ -441,6 +481,7 @@ typedef struct bool is_used : 1; struct _Ast *defer; struct _Ast *in_defer; + void *backend_value; } AstLabelStmt; typedef struct @@ -482,6 +523,7 @@ typedef struct }; }; Ast *block; + void *backend_value; } AstCaseStmt; typedef struct @@ -493,6 +535,7 @@ typedef struct typedef struct { + Ast **init; Ast *cond; Ast *incr; Ast *body; @@ -645,7 +688,7 @@ typedef struct _Context { Token module_name; Token* module_parameters; - File * file; + File* file; Decl** imports; Module *module; STable local_symbols; @@ -691,7 +734,7 @@ extern Diagnostics diagnostics; extern Token next_tok; extern Token tok; -extern Type *type_bool, *type_void, *type_string, *type_voidptr, *type_voidref; +extern Type *type_bool, *type_void, *type_string, *type_voidptr; extern Type *type_float, *type_double; extern Type *type_char, *type_short, *type_int, *type_long, *type_isize; extern Type *type_byte, *type_ushort, *type_uint, *type_ulong, *type_usize; @@ -775,7 +818,7 @@ bool cast(Expr *expr, Type *to_type, CastType cast_type); bool cast_arithmetic(Expr *expr, Expr *other, const char *action); bool cast_to_runtime(Expr *expr); - +void llvm_codegen(Context *context); void codegen(Context *context); bool sema_analyse_expr(Context *context, Expr *expr); @@ -798,6 +841,7 @@ bool context_add_local(Context *context, Decl *decl); Decl *decl_new(DeclKind decl_kind, Token name, Visibility visibility); Decl *decl_new_user_defined_type(Token name, DeclKind decl_type, Visibility visibility); Decl *decl_new_var(Token name, Type *type, VarDeclKind kind, Visibility visibility); + static inline bool decl_ok(Decl *decl) { return decl->decl_kind != DECL_POISONED; } static inline bool decl_poison(Decl *decl) { decl->decl_kind = DECL_POISONED; decl->resolve_status = RESOLVE_DONE; return false; } static inline bool decl_is_struct_type(Decl *decl) { return decl->decl_kind == DECL_UNION || decl->decl_kind == DECL_STRUCT; } @@ -880,7 +924,9 @@ void sema_shadow_error(Decl *decl, Decl *old); File *source_file_load(const char *filename, bool *already_loaded); File *source_file_from_position(SourceLoc loc); - +void source_file_append_line_end(File *file, SourceLoc loc); +SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc); +SourcePosition source_file_find_position(SourceLoc loc); void stable_init(STable *table, uint32_t initial_size); void *stable_set(STable *table, const char *key, void *value); @@ -891,6 +937,10 @@ void stable_clear(STable *table); void symtab_init(uint32_t max_size); const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); +void target_setup(); +int target_alloca_addr_space(); +void *target_data_layout(); + #define TOKEN_MAX_LENGTH 0xFFFF #define TOK2VARSTR(_token) _token.span.length, _token.start bool token_is_type(TokenType type); @@ -908,6 +958,8 @@ Type *type_unsigned_int_by_size(int bitsize); bool type_is_subtype(Type *type, Type *possible_subtype); const char *type_to_error_string(Type *type); size_t type_size(Type *canonical); +void type_append_signature_name(Type *type, char *dst, size_t *offset); + static inline bool type_is_builtin(TypeKind kind) { return kind >= TYPE_VOID && kind <= TYPE_FXX; } static inline bool type_is_signed(Type *type) { return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_IXX; } static inline bool type_is_unsigned(Type *type) { return type->type_kind >= TYPE_U8 && type->type_kind <= TYPE_UXX; } @@ -920,6 +972,19 @@ static inline bool type_is_integer(Type *type) return type->type_kind >= TYPE_I8 && type->type_kind <= TYPE_UXX; } +static inline bool type_is_float(Type *type) +{ + assert(type == type->canonical); + return type->type_kind >= TYPE_F32 && type->type_kind <= TYPE_FXX; +} + +static inline bool type_convert_will_trunc(Type *destination, Type *source) +{ + assert(type_is_builtin(destination->canonical->type_kind)); + assert(type_is_builtin(source->canonical->type_kind)); + return (unsigned)destination->canonical->builtin.bitsize < (unsigned)source->canonical->builtin.bitsize; +} + static inline bool type_is_number(Type *type) { assert(type == type->canonical); @@ -932,6 +997,10 @@ static inline bool type_is_number(Type *type) AssignOp assignop_from_token(TokenType type); UnaryOp unaryop_from_token(TokenType type); +BinaryOp binaryop_from_token(TokenType type); +BinaryOp binaryop_assign_base_op(BinaryOp assign_binary_op); + + Decl *struct_find_name(Decl *decl, const char* name); diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 480315774..cdb07b121 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -28,68 +28,44 @@ typedef enum static void print_error(SourceRange source_range, const char *message, PrintType print_type) { - File *file = source_file_from_position(source_range.loc); - - const char *content = file->contents; - const char *error_start = file->contents + source_range.loc - file->start_id; + SourcePosition position = source_file_find_position(source_range.loc); const static int LINES_SHOWN = 4; - const char *linestarts[LINES_SHOWN]; - for (int i = 0; i < LINES_SHOWN; i++) linestarts[i] = NULL; - const char *current = content; - linestarts[0] = content; - unsigned line = 1; - while (current < error_start) - { - if (current[0] == '\n') - { - line++; - linestarts[3] = linestarts[2]; - linestarts[2] = linestarts[1]; - linestarts[1] = linestarts[0]; - linestarts[0] = current + 1; - } - current++; - } - - const char *end = NULL; - while (!end) - { - switch (current[0]) - { - case '\n': - case '\0': - end = current; - break; - default: - current++; - break; - } - } - - unsigned max_line_length = (int)round(log10(line)) + 1; + unsigned max_line_length = (int)round(log10(position.line)) + 1; char number_buffer[20]; snprintf(number_buffer, 20, "%%%dd: %%.*s\n", max_line_length); - for (unsigned i = 3; i > 0; i--) + // Insert end in case it's not yet there. + for (SourceLoc s = position.loc; s < position.file->end_id; s++) { - int line_number = (int)line - i; - const char *start = linestarts[i]; - if (start == NULL) continue; - const char *line_end = linestarts[i - 1]; - eprintf(number_buffer, line_number, line_end - start - 1, start); + if ((position.file->contents + s - position.file->start_id)[0] == '\n') + { + source_file_append_line_end(position.file, s); + break; + } + } + size_t lines_in_file = vec_size(position.file->line_start); + for (unsigned i = LINES_SHOWN; i > 0; i--) + { + if (position.line < i) continue; + uint32_t line_number = position.line + 1 - i; + SourceLoc line_start = position.file->line_start[line_number - 1]; + + SourceLoc line_end = line_number == lines_in_file ? position.file->end_id : + position.file->line_start[line_number]; + uint32_t line_len = line_end - line_start - 1; + eprintf(number_buffer, line_number, line_len, position.file->contents + line_start - position.file->start_id); } - eprintf(number_buffer, line, end - linestarts[0], linestarts[0]); eprintf(" "); for (unsigned i = 0; i < max_line_length; i++) { eprintf(" "); } - for (unsigned i = 0; i < error_start - linestarts[0]; i++) + for (unsigned i = 0; i < position.col - 1; i++) { - if (linestarts[0][i] == '\t') + if (position.start[i] == '\t') { eprintf("\t"); } @@ -107,13 +83,13 @@ static void print_error(SourceRange source_range, const char *message, PrintType switch (print_type) { case PRINT_TYPE_ERROR: - eprintf("(%s:%d) Error: %s\n", file->name, line, message); + eprintf("(%s:%d) Error: %s\n", position.file->name, position.line, message); break; case PRINT_TYPE_PREV: - eprintf("(%s:%d) %s\n", file->name, line, message); + eprintf("(%s:%d) %s\n", position.file->name, position.line, message); break; case PRINT_TYPE_WARN: - eprintf("(%s:%d) Warning: %s\n", file->name, line, message); + eprintf("(%s:%d) Warning: %s\n", position.file->name, position.line, message); break; default: UNREACHABLE diff --git a/src/compiler/dwarf.h b/src/compiler/dwarf.h new file mode 100644 index 000000000..a5eaf6f1d --- /dev/null +++ b/src/compiler/dwarf.h @@ -0,0 +1,284 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#define DW_TAG_array_type 0x01 +#define DW_TAG_class_type 0x02 +#define DW_TAG_entry_point 0x03 +#define DW_TAG_enumeration_type 0x04 +#define DW_TAG_formal_parameter 0x05 +#define DW_TAG_imported_declaration 0x08 +#define DW_TAG_label 0x0a +#define DW_TAG_lexical_block 0x0b +#define DW_TAG_member 0x0d +#define DW_TAG_pointer_type 0x0f +#define DW_TAG_reference_type 0x10 +#define DW_TAG_compile_unit 0x11 +#define DW_TAG_string_type 0x12 +#define DW_TAG_structure_type 0x13 +#define DW_TAG_subroutine_type 0x15 +#define DW_TAG_typedef 0x16 +#define DW_TAG_union_type 0x17 +#define DW_TAG_unspecified_parameters 0x18 +#define DW_TAG_variant 0x19 +#define DW_TAG_common_block 0x1a +#define DW_TAG_common_inclusion 0x1b +#define DW_TAG_inheritance 0x1c +#define DW_TAG_inlined_subroutine 01x1d +#define DW_TAG_module 0x1e +#define DW_TAG_ptr_to_member_type 0x1f +#define DW_TAG_set_type 0x20 +#define DW_TAG_subrange_type 0x21 +#define DW_TAG_with_stmt 0x22 +#define DW_TAG_access_declaration 0x23 +#define DW_TAG_base_type 0x24 +#define DW_TAG_catch_block 0x25 +#define DW_TAG_const_type 0x26 +#define DW_TAG_constant 0x27 +#define DW_TAG_enumerator 0x28 +#define DW_TAG_file_type 0x29 +#define DW_TAG_friend 0x2a +#define DW_TAG_namelist 0x2b +#define DW_TAG_namelist_item 0x2c +#define DW_TAG_packed_type 0x2d +#define DW_TAG_subprogram 0x2e +#define DW_TAG_template_type_param 0x2f +#define DW_TAG_template_value_param 0x30 +#define DW_TAG_thrown_type 0x31 +#define DW_TAG_try_block 0x32 +#define DW_TAG_variant_part 0x33 +#define DW_TAG_variable 0x34 +#define DW_TAG_volatile_type 0x35 + +#define DW_CHILDREN_no 0 +#define DW_CHILDREN_yes 1 + +#define DW_LANG_C89 0x0001 + +#define DW_AT_sibling 0x01 +#define DW_AT_location 0x02 +#define DW_AT_name 0x03 +#define DW_AT_ordering 0x09 +#define DW_AT_byte_size 0x0b +#define DW_AT_bit_offset 0x0c +#define DW_AT_bit_size 0x0d +#define DW_AT_stmt_list 0x10 +#define DW_AT_low_pc 0x11 +#define DW_AT_high_pc 0x12 +#define DW_AT_language 0x13 +#define DW_AT_discr 0x15 +#define DW_AT_discr_value 0x16 +#define DW_AT_visibility 0x17 +#define DW_AT_import 0x18 +#define DW_AT_string_length 0x19 +#define DW_AT_common_reference 0x1a +#define DW_AT_comp_dir 0x1b +#define DW_AT_const_value 0x1c +#define DW_AT_containing_type 0x1d +#define DW_AT_default_value 0x1e +#define DW_AT_inline 0x20 +#define DW_AT_is_optional 0x21 +#define DW_AT_lower_bound 0x22 +#define DW_AT_producer 0x25 +#define DW_AT_prototyped 0x27 +#define DW_AT_return_addr 0x2a +#define DW_AT_start_scope 0x2c +#define DW_AT_stride_size 0x2e +#define DW_AT_upper_bound 0x2f +#define DW_AT_abstract_origin 0x31 +#define DW_AT_accessibility 0x32 +#define DW_AT_address_class 0x33 +#define DW_AT_artificial 0x34 +#define DW_AT_base_types 0x35 +#define DW_AT_calling_convention 0x36 +#define DW_AT_count 0x37 +#define DW_AT_data_member_location 0x38 +#define DW_AT_decl_column 0x39 +#define DW_AT_decl_file 0x3a +#define DW_AT_decl_line 0x3b +#define DW_AT_declaration 0x3c +#define DW_AT_discr_list 0x3d +#define DW_AT_encoding 0x3e +#define DW_AT_external 0x3f +#define DW_AT_frame_base 0x40 +#define DW_AT_friend 0x41 +#define DW_AT_identifier_case 0x42 +#define DW_AT_macro_info 0x43 +#define DW_AT_namelist_item 0x44 +#define DW_AT_priority 0x45 +#define DW_AT_segment 0x46 +#define DW_AT_specification 0x47 +#define DW_AT_static_link 0x48 +#define DW_AT_type 0x49 +#define DW_AT_use_location 0x4a +#define DW_AT_variable_parameter 0x4b +#define DW_AT_virtuality 0x4c +#define DW_AT_vtable_elem_location 0x4d +#define DW_AT_lo_user 0x2000 +#define DW_AT_hi_user 0x3fff + +#define DW_AT_user_block 0x2650 +#define DW_AT_user_level 0x2651 + +#define DW_FORM_addr 0x01 +#define DW_FORM_block2 0x03 +#define DW_FORM_block4 0x04 +#define DW_FORM_data2 0x05 +#define DW_FORM_data4 0x06 +#define DW_FORM_data8 0x07 +#define DW_FORM_string 0x08 +#define DW_FORM_block 0x09 +#define DW_FORM_block1 0x0a +#define DW_FORM_data1 0x0b +#define DW_FORM_flag 0x0c +#define DW_FORM_sdata 0x0d +#define DW_FORM_strp 0x0e +#define DW_FORM_udata 0x0f +#define DW_FORM_ref_addr 0x10 +#define DW_FORM_ref1 0x11 +#define DW_FORM_ref2 0x12 +#define DW_FORM_ref4 0x13 +#define DW_FORM_ref_udata 0x15 +#define DW_FORM_indirect 0x16 + +#define DW_OP_addr 0x03 +#define DW_OP_deref 0x06 +#define DW_OP_const1u 0x08 +#define DW_OP_const1s 0x09 +#define DW_OP_const2u 0x0a +#define DW_OP_const2s 0x0b +#define DW_OP_const4u 0x0c +#define DW_OP_const4s 0x0d +#define DW_OP_const8u 0x0e +#define DW_OP_const8s 0x0f +#define DW_OP_constu 0x10 +#define DW_OP_consts 0x11 +#define DW_OP_dup 0x12 +#define DW_OP_drop 0x13 +#define DW_OP_over 0x14 +#define DW_OP_pick 0x15 +#define DW_OP_swap 0x16 +#define DW_OP_rot 0x17 +#define DW_OP_xderef 0x18 +#define DW_OP_abs 0x19 +#define DW_OP_and 0x1a +#define DW_OP_div 0x1b +#define DW_OP_minus 0x1c +#define DW_OP_mod 0x1d +#define DW_OP_mul 0x1e +#define DW_OP_neg 0x1f +#define DW_OP_not 0x20 +#define DW_OP_or 0x21 +#define DW_OP_plus 0x22 +#define DW_OP_plus_uconst 0x23 +#define DW_OP_shl 0x24 +#define DW_OP_shr 0x25 +#define DW_OP_shra 0x26 +#define DW_OP_xor 0x27 +#define DW_OP_skip 0x2f +#define DW_OP_bra 0x28 +#define DW_OP_eq 0x29 +#define DW_OP_ge 0x2a +#define DW_OP_gt 0x2b +#define DW_OP_le 0x2c +#define DW_OP_lt 0x2d +#define DW_OP_ne 0x2e +#define DW_OP_lit0 0x30 +#define DW_OP_reg0 0x50 +#define DW_OP_breg0 0x70 +#define DW_OP_breg1 0x71 +#define DW_OP_breg2 0x72 +#define DW_OP_breg3 0x73 +#define DW_OP_breg4 0x74 +#define DW_OP_breg5 0x75 +#define DW_OP_breg6 0x76 +#define DW_OP_breg7 0x77 +#define DW_OP_breg8 0x78 +#define DW_OP_breg9 0x79 +#define DW_OP_breg10 0x7a +#define DW_OP_breg11 0x7b +#define DW_OP_breg12 0x7c +#define DW_OP_breg13 0x7d +#define DW_OP_breg14 0x7e +#define DW_OP_breg15 0x7f +#define DW_OP_breg16 0x80 +#define DW_OP_breg17 0x81 +#define DW_OP_breg18 0x82 +#define DW_OP_breg19 0x83 +#define DW_OP_breg20 0x84 +#define DW_OP_breg21 0x85 +#define DW_OP_breg22 0x86 +#define DW_OP_breg23 0x87 +#define DW_OP_breg24 0x88 +#define DW_OP_breg25 0x89 +#define DW_OP_breg26 0x8a +#define DW_OP_breg27 0x8b +#define DW_OP_breg28 0x8c +#define DW_OP_breg29 0x8d +#define DW_OP_breg30 0x8e +#define DW_OP_breg31 0x8f +#define DW_OP_regx 0x90 +#define DW_OP_fbreg 0x91 +#define DW_OP_bregx 0x92 +#define DW_OP_piece 0x93 +#define DW_OP_deref_size 0x94 +#define DW_OP_xderef_size 0x95 +#define DW_OP_nop 0x96 + +#define DW_ATE_address 0x1 +#define DW_ATE_boolean 0x2 +#define DW_ATE_complex_float 0x3 +#define DW_ATE_float 0x4 +#define DW_ATE_signed 0x5 +#define DW_ATE_signed_char 0x6 +#define DW_ATE_unsigned 0x7 +#define DW_ATE_unsigned_char 0x8 + +#define DW_CC_normal 0x1 +#define DW_CC_program 0x2 +#define DW_CC_nocall 0x3 + +#define DW_LNS_copy 1 +#define DW_LNS_advance_pc 2 +#define DW_LNS_advance_line 3 +#define DW_LNS_set_file 4 +#define DW_LNS_set_column 5 +#define DW_LNS_negate_stmt 6 +#define DW_LNS_set_basic_block 7 +#define DW_LNS_const_add_pc 8 +#define DW_LNS_fixed_advance_pc 9 + +#define DW_LNE_end_sequence 1 +#define DW_LNE_set_address 2 +#define DW_LNE_define_file 3 + +#define DW_MACINFO_define 1 +#define DW_MACINFO_undef 2 +#define DW_MACINFO_start_file 3 +#define DW_MACINFO_end_file 4 +#define DW_MACINFO_vendor_ext 255 + +#define DW_CFA_advance_loc (1 << 6) +#define DW_CFA_offset (2 << 6) +#define DW_CFA_restore (3 << 6) +#define DW_CFA_set_loc 0x01 +#define DW_CFA_advance_loc1 0x02 +#define DW_CFA_advance_loc2 0x03 +#define DW_CFA_advance_loc4 0x04 +#define DW_CFA_offset_extended 0x05 +#define DW_CFA_restore_extended 0x06 +#define DW_CFA_undefined 0x07 +#define DW_CFA_same_value 0x08 +#define DW_CFA_register 0x09 +#define DW_CFA_remember_state 0x0a +#define DW_CFA_restore_state 0x0b +#define DW_CFA_def_cfa 0x0c +#define DW_CFA_def_cfa_register 0x0d +#define DW_CFA_def_cfa_offset 0x0e +#define DW_CFA_nop 0x0f +#define DW_CFA_lo_user 0x1c +#define DW_CFA_hi_user 0x3f + +#define DWARF_PRODUCER_NAME "C3 Compiler" \ No newline at end of file diff --git a/src/compiler/enums.h b/src/compiler/enums.h index 6d93ea3fd..80d3ae811 100644 --- a/src/compiler/enums.h +++ b/src/compiler/enums.h @@ -22,6 +22,43 @@ typedef enum ASSIGNOP_SHL_ASSIGN, } AssignOp; +typedef enum +{ + BINARYOP_ERROR, + BINARYOP_MULT, + BINARYOP_SUB, + BINARYOP_ADD, + BINARYOP_DIV, + BINARYOP_MOD, + BINARYOP_SHR, + BINARYOP_SHL, + BINARYOP_BIT_OR, + BINARYOP_BIT_XOR, + BINARYOP_BIT_AND, + BINARYOP_AND, + BINARYOP_OR, + BINARYOP_GT, + BINARYOP_GE, + BINARYOP_LT, + BINARYOP_LE, + BINARYOP_NE, + BINARYOP_EQ, + // Only "assign" BINOPS after this point + BINARYOP_ASSIGN, + BINARYOP_MULT_ASSIGN, + BINARYOP_ADD_ASSIGN, + BINARYOP_SUB_ASSIGN, + BINARYOP_DIV_ASSIGN, + BINARYOP_MOD_ASSIGN, + BINARYOP_AND_ASSIGN, + BINARYOP_OR_ASSIGN, + BINARYOP_BIT_AND_ASSIGN, + BINARYOP_BIT_OR_ASSIGN, + BINARYOP_BIT_XOR_ASSIGN, + BINARYOP_SHR_ASSIGN, + BINARYOP_SHL_ASSIGN, +} BinaryOp; + typedef enum { AST_POISONED, @@ -78,15 +115,15 @@ typedef enum typedef enum { CAST_ERROR, - CAST_TRUNC, CAST_PTRPTR, - CAST_PTRINT, + CAST_PTRXI, CAST_VARRPTR, CAST_ARRPTR, CAST_STRPTR, CAST_PTRBOOL, CAST_BOOLINT, CAST_BOOLFP, + CAST_FPBOOL, CAST_INTBOOL, CAST_FPFP, CAST_FPSI, @@ -219,7 +256,6 @@ typedef enum typedef enum { LEXER_STATE_NORMAL, - LEXER_STATE_DEFERED_PARSING, LEXER_STATE_DOCS_PARSE, LEXER_STATE_DOCS_PARSE_DIRECTIVE, } LexerState; @@ -467,6 +503,7 @@ typedef enum TYPE_F32, TYPE_F64, TYPE_FXX, + TYPE_FUNC, TYPE_USER_DEFINED, TYPE_POINTER, TYPE_STRING, diff --git a/src/compiler/expr_analysis.c b/src/compiler/expr_analysis.c index 59e226a9d..1e10c9219 100644 --- a/src/compiler/expr_analysis.c +++ b/src/compiler/expr_analysis.c @@ -726,6 +726,7 @@ static bool sema_expr_analyse_shl_assign(Context *context, Expr *expr, Expr *lef static bool sema_expr_analyse_and(Context *context, Expr *expr, Expr *left, Expr *right) { + expr->type = type_bool; if (!cast(left, type_bool, CAST_TYPE_IMPLICIT)) return false; if (!cast(right, type_bool, CAST_TYPE_IMPLICIT)) return false; if (both_const(left, right)) @@ -905,6 +906,7 @@ static bool sema_expr_analyse_not(Context *context, Expr *expr, Expr *inner) case TYPE_INC_ARRAY: case TYPE_EXPRESSION: UNREACHABLE + case TYPE_FUNC: case TYPE_ARRAY: case TYPE_POINTER: case TYPE_VARARRAY: diff --git a/src/compiler/lexer.c b/src/compiler/lexer.c index d87066f74..dc20587bf 100644 --- a/src/compiler/lexer.c +++ b/src/compiler/lexer.c @@ -46,6 +46,11 @@ static inline void backtrack() lexer.current--; } +void lexer_store_line_end(void) +{ + source_file_append_line_end(lexer.current_file, lexer.current_file->start_id + lexer.current - lexer.file_begin); +} + void lexer_store_state(void) { lexer.stored.current = lexer.current; @@ -102,7 +107,7 @@ static Token error_token(const char *message, ...) return token; } -static Token make_token(TokenType type) +static Token make_token(TokenType type, const char *string) { size_t token_size = lexer.current - lexer.lexing_start; if (token_size > TOKEN_MAX_LENGTH) return error_token("Token exceeding max length"); @@ -110,7 +115,7 @@ static Token make_token(TokenType type) { .type = type, .span = { .loc = (SourceLoc) (lexer.current_file->start_id + (lexer.lexing_start - lexer.file_begin)), .length = token_size }, - .start = lexer.lexing_start + .start = string }; } @@ -167,6 +172,7 @@ SkipWhitespaceResult skip_whitespace() case '\0': return WHITESPACE_FOUND_EOF; case '\n': + lexer_store_line_end(); // If we are currently parsing docs, then end of line is considered meaningful. if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE) return WHITESPACE_FOUND_DOCS_EOL; case ' ': @@ -231,7 +237,7 @@ SkipWhitespaceResult skip_whitespace() // --- Normal scanning methods start here -static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, bool ends_with_bang) +static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, bool ends_with_bang, const char *start) { uint32_t hash = FNV1a(prev(), FNV1_SEED); while (is_alphanum_(peek())) @@ -243,7 +249,7 @@ static inline Token scan_prefixed_ident(TokenType type, TokenType no_ident_type, hash = FNV1a(next(), hash); } uint32_t len = (uint32_t)(lexer.current - lexer.lexing_start); - if (len == 1) return make_token(no_ident_type); + if (len == 1) return make_token(no_ident_type, start); const char* interned = symtab_add(lexer.lexing_start, len, hash, &type); return make_string_token(type, interned); } @@ -259,12 +265,6 @@ static inline void scan_skipped_ident() // we split identifiers into 2 types + find keywords. static inline Token scan_ident(void) { - // If we're in ignore keywords state, simply skip stuff. - if (lexer.lexer_state == LEXER_STATE_DEFERED_PARSING) - { - scan_skipped_ident(); - return make_token(TOKEN_IDENT); - } TokenType type = 0; uint32_t hash = FNV1_SEED; @@ -323,7 +323,7 @@ static Token scan_oct(void) char o = next(); // Skip the o if (!is_oct(next())) return error_token("An expression starting with '0%c' would expect to be followed by octal numbers (0-7).", o); while (is_oct_or_(peek())) next(); - return make_token(TOKEN_INTEGER); + return make_token(TOKEN_INTEGER, lexer.lexing_start); } @@ -336,7 +336,7 @@ Token scan_binary(void) "did you try to write a hex value but forgot the '0x'?", b); } while (is_binary_or_(peek())) next(); - return make_token(TOKEN_INTEGER); + return make_token(TOKEN_INTEGER, lexer.lexing_start); } #define PARSE_SPECIAL_NUMBER(is_num, is_num_with_underscore, exp, EXP) \ @@ -362,7 +362,7 @@ if (c == (exp) || c == (EXP)) \ while (is_num(peek())) next(); \ } \ if (prev() == '_') return error_token("The number ended with '_', but that character needs to be between, not after, digits."); \ -return make_token(is_float ? TOKEN_FLOAT : TOKEN_INTEGER) +return make_token(is_float ? TOKEN_FLOAT : TOKEN_INTEGER, lexer.lexing_start) static inline Token scan_hex(void) { @@ -446,7 +446,7 @@ static inline Token scan_char() { error_token("Character literals may only be 1, 2 or 8 characters wide."); } - return make_token(TOKEN_INTEGER); + return make_token(TOKEN_INTEGER, lexer.lexing_start); } static inline Token scan_string() @@ -464,7 +464,7 @@ static inline Token scan_string() return error_token("Reached the end looking for '\"'. Did you forget it?"); } } - return make_token(TOKEN_STRING); + return make_token(TOKEN_STRING, lexer.lexing_start); } static inline void skip_docs_whitespace() @@ -489,7 +489,7 @@ static inline void skip_docs_whitespace() static inline Token scan_docs_directive(void) { match_assert('@'); - Token token = scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, false); + Token token = scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, false, "@"); assert(token.type != TOKEN_AT); lexer.lexer_state = LEXER_STATE_DOCS_PARSE_DIRECTIVE; return token; @@ -516,7 +516,7 @@ static inline Token scan_docs(void) // Return end lexer.lexer_state = LEXER_STATE_NORMAL; - return make_token(TOKEN_DOCS_END); + return make_token(TOKEN_DOCS_END, "*/"); } // Otherwise continue consuming @@ -548,14 +548,15 @@ static inline Token scan_docs(void) // We found the end, so just make a token out of the rest. // Note that this line will not get a linebreak at the end. - if (peek_next() == '/') return make_token(TOKEN_DOCS_LINE); + if (peek_next() == '/') return make_token(TOKEN_DOCS_LINE, "*"); // Otherwise it's just something in the text, so continue. next(); break; case '\n': // Normal line of text. + lexer_store_line_end(); next(); - return make_token(TOKEN_DOCS_LINE); + return make_token(TOKEN_DOCS_LINE, "\n"); case '\0': return error_token("The document ended without finding the end of the doc comment. " "Did you forget a '*/' somewhere?"); @@ -586,15 +587,15 @@ Token lexer_scan_token(void) // and switch state. skip(3); lexer.lexer_state = LEXER_STATE_DOCS_PARSE; - return make_token(TOKEN_DOCS_START); + return make_token(TOKEN_DOCS_START, "/**"); case WHITESPACE_COMMENT_REACHED_EOF: return error_token("Reached the end looking for '*/'. Did you forget it somewhere?"); case WHITESPACE_FOUND_EOF: - return make_token(TOKEN_EOF); + return make_token(TOKEN_EOF, "\n"); case WHITESPACE_FOUND_DOCS_EOL: skip(1); lexer.lexer_state = LEXER_STATE_DOCS_PARSE; - return make_token(TOKEN_DOCS_EOL); + return make_token(TOKEN_DOCS_EOL, "\n"); case WHITESPACE_SKIPPED_OK: break; } @@ -603,78 +604,78 @@ Token lexer_scan_token(void) switch (c) { case '@': - return scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, true); + return scan_prefixed_ident(TOKEN_AT_IDENT, TOKEN_AT, true, "@"); case '\'': return scan_char(); case '"': return scan_string(); case '#': - return scan_prefixed_ident(TOKEN_HASH_IDENT, TOKEN_HASH, false); + return scan_prefixed_ident(TOKEN_HASH_IDENT, TOKEN_HASH, false, "#"); case '$': - return scan_prefixed_ident(TOKEN_CT_IDENT, TOKEN_DOLLAR, false); + return scan_prefixed_ident(TOKEN_CT_IDENT, TOKEN_DOLLAR, false, "$"); case ',': - return make_token(TOKEN_COMMA); + return make_token(TOKEN_COMMA, ","); case ';': - return make_token(TOKEN_EOS); + return make_token(TOKEN_EOS, ";"); case '{': - return make_token(TOKEN_LBRACE); + return make_token(TOKEN_LBRACE, "{"); case '}': - return make_token(match(')') ? TOKEN_RPARBRA : TOKEN_RBRACE); + return match(')') ? make_token(TOKEN_RPARBRA, "})") : make_token(TOKEN_RBRACE, "})"); case '(': - return make_token(match('{') ? TOKEN_LPARBRA : TOKEN_LPAREN); + return match('{') ? make_token(TOKEN_LPARBRA, "({") : make_token(TOKEN_LPAREN, ")"); case ')': - return make_token(TOKEN_RPAREN); + return make_token(TOKEN_RPAREN, ")"); case '[': - return make_token(TOKEN_LBRACKET); + return make_token(TOKEN_LBRACKET, "["); case ']': - return make_token(TOKEN_RBRACKET); + return make_token(TOKEN_RBRACKET, "]"); case '.': - if (match('.')) return make_token(match('.') ? TOKEN_ELIPSIS : TOKEN_DOTDOT); - return make_token(TOKEN_DOT); + if (match('.')) return match('.') ? make_token(TOKEN_ELIPSIS, "...") : make_token(TOKEN_DOTDOT, ".."); + return make_token(TOKEN_DOT, "."); case '~': - return make_token(TOKEN_BIT_NOT); + return make_token(TOKEN_BIT_NOT, "~"); case ':': - return make_token(match(':') ? TOKEN_SCOPE : TOKEN_COLON); + return match(':') ? make_token(TOKEN_SCOPE, "::") : make_token(TOKEN_COLON, ":"); case '!': - return make_token(match('=') ? TOKEN_NOT_EQUAL : TOKEN_NOT); + return match('=') ? make_token(TOKEN_NOT_EQUAL, "!=") : make_token(TOKEN_NOT, "!"); case '/': - return make_token(match('=') ? TOKEN_DIV_ASSIGN : TOKEN_DIV); + return match('=') ? make_token(TOKEN_DIV_ASSIGN, "/=") : make_token(TOKEN_DIV, "/"); case '*': if (lexer.lexer_state == LEXER_STATE_DOCS_PARSE_DIRECTIVE && match('/')) { lexer.lexer_state = LEXER_STATE_NORMAL; - return make_token(TOKEN_DOCS_END); + return make_token(TOKEN_DOCS_END, "*/"); } - return make_token(match('=') ? TOKEN_MULT_ASSIGN : TOKEN_STAR); + return match('=') ? make_token(TOKEN_MULT_ASSIGN, "*=") : make_token(TOKEN_STAR, "*"); case '=': - return make_token(match('=') ? TOKEN_EQEQ : TOKEN_EQ); + return match('=') ? make_token(TOKEN_EQEQ, "==") : make_token(TOKEN_EQ, "="); case '^': - return make_token(match('=') ? TOKEN_BIT_XOR_ASSIGN : TOKEN_BIT_XOR); + return match('=') ? make_token(TOKEN_BIT_XOR_ASSIGN, "^=") : make_token(TOKEN_BIT_XOR, "^"); case '?': - return make_token(match(':') ? TOKEN_ELVIS : TOKEN_QUESTION); + return match(':') ? make_token(TOKEN_EQEQ, "?:") : make_token(TOKEN_EQ, "?"); case '<': - if (match('<')) return make_token(match('=') ? TOKEN_SHL_ASSIGN : TOKEN_SHL); - return make_token(match('=') ? TOKEN_LESS_EQ : TOKEN_LESS); + if (match('<')) return match('=') ? make_token(TOKEN_SHL_ASSIGN, "<<=") : make_token(TOKEN_SHL, "<<"); + return match('=') ? make_token(TOKEN_LESS_EQ, "<=") : make_token(TOKEN_LESS, "<"); case '>': - if (match('>')) return make_token(match('=') ? TOKEN_SHR_ASSIGN : TOKEN_SHR); - return make_token(match('=') ? TOKEN_GREATER_EQ : TOKEN_GREATER); + if (match('>')) return match('=') ? make_token(TOKEN_SHR_ASSIGN, ">>=") : make_token(TOKEN_SHR, ">>"); + return match('=') ? make_token(TOKEN_GREATER_EQ, ">=") : make_token(TOKEN_GREATER, ">"); case '%': - return make_token(match('=') ? TOKEN_MOD_ASSIGN : TOKEN_MOD); + return match('=') ? make_token(TOKEN_MOD_ASSIGN, "%=") : make_token(TOKEN_MOD, "%"); case '&': - if (match('&')) return make_token(match('=') ? TOKEN_AND_ASSIGN : TOKEN_AND); - return make_token(match('=') ? TOKEN_BIT_AND_ASSIGN : TOKEN_AMP); + if (match('&')) return match('=') ? make_token(TOKEN_AND_ASSIGN, "&&=") : make_token(TOKEN_AND, "&&"); + return match('=') ? make_token(TOKEN_BIT_AND_ASSIGN, "&=") : make_token(TOKEN_AMP, "&"); case '|': - if (match('|')) return make_token(match('=') ? TOKEN_OR_ASSIGN : TOKEN_OR); - return make_token(match('=') ? TOKEN_BIT_OR_ASSIGN : TOKEN_BIT_OR); + if (match('|')) return match('=') ? make_token(TOKEN_OR_ASSIGN, "||=") : make_token(TOKEN_OR, "||"); + return match('=') ? make_token(TOKEN_BIT_OR_ASSIGN, "|=") : make_token(TOKEN_BIT_OR, "|"); case '+': - if (match('+')) return make_token(TOKEN_PLUSPLUS); - if (match('=')) return make_token(TOKEN_PLUS_ASSIGN); - return make_token(TOKEN_PLUS); + if (match('+')) return make_token(TOKEN_PLUSPLUS, "++"); + if (match('=')) return make_token(TOKEN_PLUS_ASSIGN, "+="); + return make_token(TOKEN_PLUS, "+"); case '-': - if (match('>')) return make_token(TOKEN_ARROW); - if (match('-')) return make_token(TOKEN_MINUSMINUS); - if (match('=')) return make_token(TOKEN_MINUS_ASSIGN); - return make_token(TOKEN_MINUS); + if (match('>')) return make_token(TOKEN_ARROW, "->"); + if (match('-')) return make_token(TOKEN_MINUSMINUS, "--"); + if (match('=')) return make_token(TOKEN_MINUS_ASSIGN, "-="); + return make_token(TOKEN_MINUS, "-"); default: if (is_alphanum_(c)) { diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c new file mode 100644 index 000000000..aa8cbea76 --- /dev/null +++ b/src/compiler/llvm_codegen.c @@ -0,0 +1,136 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + + + +static void gencontext_init(GenContext *context, Context *ast_context) +{ + memset(context, 0, sizeof(GenContext)); + context->context = LLVMContextCreate(); + context->ast_context = ast_context; +} + +static void gencontext_destroy(GenContext *context) +{ + LLVMContextDispose(context->context); +} + + +static LLVMValueRef gencontext_emit_null_constant(GenContext *context, Type *type) +{ + TODO +} + +static LLVMValueRef gencontext_emit_initializer(GenContext *context, Expr *expr) +{ + TODO +} +static void gencontext_emit_global_variable_definition(GenContext *context, Decl *decl, bool is_tentative) +{ + assert(decl->var.kind == VARDECL_GLOBAL); + + LLVMValueRef init = NULL; + + if (!decl->var.init_expr) + { + // Tentative definition, initialized to zero, but only + // emitted at the end of the translation unit. + init = gencontext_emit_null_constant(context, decl->var.type); + } + else + { + init = gencontext_emit_initializer(context, decl->var.init_expr); + } + + // TODO fix name + decl->var.backend_ref = LLVMAddGlobal(context->module, gencontext_get_llvm_type(context, decl->var.type), decl->name.string); + + // If read only: LLVMSetGlobalConstant(decl->var.backend_ref, 1); + + switch (decl->visibility) + { + case VISIBLE_MODULE: + LLVMSetVisibility(decl->var.backend_ref, LLVMProtectedVisibility); + break; + case VISIBLE_PUBLIC: + LLVMSetVisibility(decl->var.backend_ref, LLVMDefaultVisibility); + break; + case VISIBLE_LOCAL: + LLVMSetVisibility(decl->var.backend_ref, LLVMHiddenVisibility); + break; + } + + int alignment = 64; // TODO + // Should we set linkage here? + if (context->debug.builder) + { + decl->var.backend_debug_ref = LLVMDIBuilderCreateGlobalVariableExpression(context->debug.builder, + NULL /*scope*/, + decl->name.string, + decl->name.span.length, + "linkagename", + 2, + context->debug.file, + 12 /* lineno */, + decl->var.type->backend_debug_type, + decl->visibility == + VISIBLE_LOCAL, /* expr */ + NULL, /** declaration **/ + NULL, + alignment); + } +} +static void gencontext_verify_ir(GenContext *context) +{ + char *error = NULL; + assert(context->module); + LLVMVerifyModule(context->module, LLVMPrintMessageAction, &error); + if (error) + { + if (*error) + { + error_exit("Could not verify IR: %s", error); + } + error_exit("Could not verify module IR."); + } +} +void gencontext_print_llvm_ir(GenContext *context) +{ + char *err = NULL; + char *filename = strformat("xx.llvmir" /*, context->module_name*/); + if (LLVMPrintModuleToFile(context->module, filename, &err) != 0) + { + error_exit("Could not emit ir to file: %s", err); + } +} + + + +LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl) +{ + LLVMBasicBlockRef current_block = LLVMGetInsertBlock(context->builder); + LLVMPositionBuilderBefore(context->builder, context->alloca_point); + LLVMValueRef alloca = LLVMBuildAlloca(context->builder, gencontext_get_llvm_type(context, decl->var.type->canonical), decl->name.string); + LLVMPositionBuilderAtEnd(context->builder, current_block); + return alloca; +} + +void llvm_codegen(Context *context) +{ + GenContext gen_context; + gencontext_init(&gen_context, context); + gencontext_begin_module(&gen_context); + // EmitDeferred() + VECEACH(context->functions, i) + { + gencontext_emit_function_decl(&gen_context, context->functions[i]); + } + LLVMDumpModule(gen_context.module); + gencontext_print_llvm_ir(&gen_context); + LLVMDumpModule(gen_context.module); + gencontext_end_module(&gen_context); + gencontext_destroy(&gen_context); +} diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c new file mode 100644 index 000000000..68360a8f8 --- /dev/null +++ b/src/compiler/llvm_codegen_debug_info.c @@ -0,0 +1,83 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + +void gencontext_set_debug_location(GenContext *context, SourceRange source_range) +{ + if (source_range.loc == INVALID_LOC) return; + + context->debug.current_range = source_range; +#ifdef TODOLATER + CurLoc = CGM.getContext().getSourceManager().getExpansionLoc(Loc); +#endif + // If we've changed files in the middle of a lexical scope go ahead + // and create a new lexical scope with file node if it's different + // from the one in the scope. + if (!vec_size(context->debug.lexical_block_stack)) return; + +#ifdef TODOLATE + if (auto *LBF = dyn_cast(Scope)) { + LexicalBlockStack.pop_back(); + LexicalBlockStack.emplace_back(DBuilder.createLexicalBlockFile( + LBF->getScope(), getOrCreateFile(CurLoc))); + } else if (isa(Scope) || + isa(Scope)) { + LexicalBlockStack.pop_back(); + LexicalBlockStack.emplace_back( + DBuilder.createLexicalBlockFile(Scope, getOrCreateFile(CurLoc))); + } +#endif +} + +void gencontext_emit_debug_location(GenContext *context, SourceRange location) +{ + gencontext_set_debug_location(context, location); + + if (context->debug.current_range.loc == INVALID_LOC || vec_size(context->debug.lexical_block_stack) == 0) return; + + LLVMMetadataRef scope = VECLAST(context->debug.lexical_block_stack); + LLVMMetadataRef debug_location = LLVMDIBuilderCreateDebugLocation(context->context, 320, 12, scope, context->debug.inlined_at); + LLVMSetCurrentDebugLocation2(context->builder, debug_location); +} + +static LLVMMetadataRef gencontext_simple_debug_type(GenContext *context, Type *type, int dwarf_code) +{ + return LLVMDIBuilderCreateBasicType(context->debug.builder, + type->name_loc.string, + type->name_loc.span.length, + type->builtin.bitsize, + dwarf_code, 0); + +} + +LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type) +{ + assert(builtin_type->canonical == builtin_type); + // Consider special handling of UTF8 arrays. + switch (builtin_type->type_kind) + { + case TYPE_BOOL: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_boolean); + case TYPE_I8: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_signed_char); // DW_ATE_UTF? + case TYPE_U8: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_unsigned_char); + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_signed); + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_unsigned); + case TYPE_F32: + case TYPE_F64: + return gencontext_simple_debug_type(context, builtin_type, DW_ATE_float); + case TYPE_VOID: + return NULL; + default: + UNREACHABLE + } +} diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c new file mode 100644 index 000000000..9c1362c3a --- /dev/null +++ b/src/compiler/llvm_codegen_expr.c @@ -0,0 +1,379 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" +#include "compiler_internal.h" + +LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_IDENTIFIER: + return expr->identifier_expr.decl->var.backend_ref; + case EXPR_CONST: + case EXPR_TYPE: + UNREACHABLE + case EXPR_UNARY: + { + UnaryOp op = unaryop_from_token(expr->unary_expr.operator); + assert(op == UNARYOP_DEREF); + return gencontext_emit_expr(context, expr->unary_expr.expr); + } + default: + TODO; + } + return NULL; +} + +static inline LLVMValueRef gencontext_emit_cast_expr(GenContext *context, Expr *expr) +{ + LLVMValueRef rhs = gencontext_emit_expr(context, expr->expr_cast.expr); + switch (expr->expr_cast.kind) + { + case CAST_ERROR: + UNREACHABLE + case CAST_PTRPTR: + return LLVMBuildPointerCast(context->builder, rhs, LLVMTYPE(expr->type), "ptrptr"); + case CAST_PTRXI: + return LLVMBuildPtrToInt(context->builder, rhs, LLVMTYPE(expr->type), "ptrxi"); + case CAST_VARRPTR: + TODO + case CAST_ARRPTR: + TODO + case CAST_STRPTR: + TODO + case CAST_PTRBOOL: + return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstPointerNull(LLVMTYPE(expr->type->canonical->base)), "ptrbool"); + case CAST_BOOLINT: + return LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "boolsi"); + case CAST_FPBOOL: + return LLVMBuildFCmp(context->builder, LLVMRealUNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "fpbool"); + case CAST_BOOLFP: + return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "boolfp"); + case CAST_INTBOOL: + return LLVMBuildICmp(context->builder, LLVMIntNE, rhs, LLVMConstNull(LLVMTypeOf(rhs)), "intbool"); + case CAST_FPFP: + return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type) + ? LLVMBuildFPTrunc(context->builder, rhs, LLVMTYPE(expr->type), "fpfptrunc") + : LLVMBuildFPExt(context->builder, rhs, LLVMTYPE(expr->type), "fpfpext"); + case CAST_FPSI: + return LLVMBuildFPToSI(context->builder, rhs, LLVMTYPE(expr->type), "fpsi"); + case CAST_FPUI: + return LLVMBuildFPToUI(context->builder, rhs, LLVMTYPE(expr->type), "fpui"); + case CAST_SISI: + return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type) + ? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "sisitrunc") + : LLVMBuildSExt(context->builder, rhs, LLVMTYPE(expr->type), "sisiext"); + case CAST_SIUI: + return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type) + ? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "siuitrunc") + : LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "siuiext"); + break; + case CAST_SIFP: + return LLVMBuildSIToFP(context->builder, rhs, LLVMTYPE(expr->type), "sifp"); + case CAST_XIPTR: + return LLVMBuildIntToPtr(context->builder, rhs, LLVMTYPE(expr->type), "xiptr"); + case CAST_UISI: + return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type) + ? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uisitrunc") + : LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uisiext"); + case CAST_UIUI: + return type_convert_will_trunc(expr->type, expr->expr_cast.expr->type) + ? LLVMBuildTrunc(context->builder, rhs, LLVMTYPE(expr->type), "uiuitrunc") + : LLVMBuildZExt(context->builder, rhs, LLVMTYPE(expr->type), "uiuiext"); + case CAST_UIFP: + return LLVMBuildUIToFP(context->builder, rhs, LLVMTYPE(expr->type), "uifp"); + case CAST_ENUMSI: + TODO + } +} + +LLVMValueRef gencontext_emit_unary_expr(GenContext *context, Expr *expr) +{ + UnaryOp unary_op = unaryop_from_token(expr->unary_expr.operator); + switch (unary_op) + { + case UNARYOP_ERROR: + FATAL_ERROR("Illegal unary op %s", expr->unary_expr.operator); + case UNARYOP_NOT: + return LLVMBuildXor(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), LLVMConstInt(type_bool->backend_type, 1, 0), "not"); + case UNARYOP_BITNEG: + return LLVMBuildNot(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "bnot"); + case UNARYOP_NEG: + if (type_is_float(expr->unary_expr.expr->type->canonical)) + { + return LLVMBuildFNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "fneg"); + } + return LLVMBuildNeg(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "neg"); + case UNARYOP_ADDR: + return gencontext_emit_address(context, expr->unary_expr.expr); + case UNARYOP_DEREF: + return LLVMBuildLoad(context->builder, gencontext_emit_expr(context, expr->unary_expr.expr), "deref"); + case UNARYOP_INC: + TODO + case UNARYOP_DEC: + TODO + } +} + + +static LLVMValueRef gencontext_emit_assign(GenContext *context, Expr *left, LLVMValueRef right) +{ + LLVMValueRef addr = gencontext_emit_address(context, left); + return LLVMBuildStore(context->builder, right, addr); +} + +static LLVMValueRef gencontext_emit_logical_and_or(GenContext *context, Expr *expr, BinaryOp op) +{ + // Value *ScalarExprEmitter::VisitBinLAnd(const BinaryOperator *E) + // For vector implementation. + + // Set up basic blocks, following Cone + LLVMBasicBlockRef start_block = LLVMGetInsertBlock(context->builder); + LLVMBasicBlockRef phi_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.phi" : "or.phi"); + LLVMBasicBlockRef rhs_block = LLVMCreateBasicBlockInContext(context->context, op == BINARYOP_AND ? "and.rhs" : "or.rhs"); + + // Generate left-hand condition and conditional branch + LLVMValueRef lhs = gencontext_emit_expr(context, expr->binary_expr.left); + + if (op == BINARYOP_AND) + { + gencontext_emit_cond_br(context, lhs, rhs_block, phi_block); + } + else + { + gencontext_emit_cond_br(context, lhs, phi_block, rhs_block); + } + + gencontext_emit_block(context, rhs_block); + LLVMValueRef rhs = gencontext_emit_expr(context, expr->binary_expr.right); + gencontext_emit_br(context, phi_block); + + // Generate phi + gencontext_emit_block(context, phi_block); + LLVMValueRef phi = LLVMBuildPhi(context->builder, type_bool->backend_type, "val"); + + // Simplify for LLVM by entering the constants we already know of. + LLVMValueRef result_on_skip = LLVMConstInt(LLVMInt1TypeInContext(context->context), op == BINARYOP_AND ? 0 : 1, false); + LLVMValueRef logicValues[2] = { result_on_skip, rhs }; + LLVMBasicBlockRef blocks[2] = { start_block, rhs_block }; + LLVMAddIncoming(phi, logicValues, blocks, 2); + + return phi; +} + +static LLVMValueRef gencontext_emit_binary(GenContext *context, Expr *expr, bool load_lhs_after_rhs, BinaryOp binary_op) +{ + + if (binary_op == BINARYOP_AND || binary_op == BINARYOP_OR) + { + return gencontext_emit_logical_and_or(context, expr, binary_op); + } + Type *type = expr->type->canonical; + Expr *lhs = expr->binary_expr.left; + Expr *rhs = expr->binary_expr.right; + + LLVMValueRef lhs_value; + LLVMValueRef rhs_value; + if (load_lhs_after_rhs) + { + rhs_value = gencontext_emit_expr(context, rhs); + lhs_value = gencontext_emit_expr(context, lhs); + } + else + { + lhs_value = gencontext_emit_expr(context, lhs); + rhs_value = gencontext_emit_expr(context, rhs); + } + + bool is_float = type_is_float(type); + + switch (binary_op) + { + case BINARYOP_ERROR: + UNREACHABLE + case BINARYOP_MULT: + return is_float ? LLVMBuildFMul(context->builder, lhs_value, rhs_value, "fmul") : LLVMBuildMul(context->builder, lhs_value, rhs_value, "mul"); + case BINARYOP_SUB: + if (lhs->type->canonical->type_kind == TYPE_POINTER) + { + if (lhs->type->canonical == rhs->type->canonical) return LLVMBuildPtrDiff(context->builder, lhs_value, rhs_value, "ptrdiff"); + rhs_value = LLVMBuildNeg(context->builder, rhs_value, ""); + return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptrsub"); + } + if (is_float) return LLVMBuildFSub(context->builder, lhs_value, rhs_value, "fsub"); + // Consider UB version instead. + return LLVMBuildSub(context->builder, lhs_value, rhs_value, "sub"); + case BINARYOP_ADD: + if (lhs->type->canonical->type_kind == TYPE_POINTER) + { + assert(type_is_integer(lhs->type->canonical)); + return LLVMBuildGEP2(context->builder, gencontext_get_llvm_type(context, lhs->type->canonical), lhs_value, &rhs_value, 1, "ptradd"); + } + if (is_float) return LLVMBuildFAdd(context->builder, lhs_value, rhs_value, "fadd"); + // Consider UB version instead. + return LLVMBuildAdd(context->builder, lhs_value, rhs_value, "add"); + case BINARYOP_DIV: + if (is_float) return LLVMBuildFDiv(context->builder, lhs_value, rhs_value, "fdiv"); + return type_is_unsigned(type) + ? LLVMBuildUDiv(context->builder, lhs_value, rhs_value, "udiv") + : LLVMBuildSDiv(context->builder, lhs_value, rhs_value, "sdiv"); + case BINARYOP_MOD: + return type_is_unsigned(type) + ? LLVMBuildURem(context->builder, lhs_value, rhs_value, "umod") + : LLVMBuildSRem(context->builder, lhs_value, rhs_value, "smod"); + case BINARYOP_SHR: + return type_is_unsigned(type) + ? LLVMBuildLShr(context->builder, lhs_value, rhs_value, "lshr") + : LLVMBuildAShr(context->builder, lhs_value, rhs_value, "ashr"); + case BINARYOP_SHL: + return LLVMBuildShl(context->builder, lhs_value, rhs_value, "shr"); + case BINARYOP_BIT_AND: + return LLVMBuildAnd(context->builder, lhs_value, rhs_value, "and"); + case BINARYOP_BIT_OR: + return LLVMBuildOr(context->builder, lhs_value, rhs_value, "or"); + case BINARYOP_BIT_XOR: + return LLVMBuildXor(context->builder, lhs_value, rhs_value, "xor"); + case BINARYOP_EQ: + // Unordered? + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUEQ, lhs_value, rhs_value, "eq"); + return LLVMBuildICmp(context->builder, LLVMIntEQ, lhs_value, rhs_value, "eq"); + case BINARYOP_NE: + // Unordered? + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUNE, lhs_value, rhs_value, "neq"); + return LLVMBuildICmp(context->builder, LLVMIntNE, lhs_value, rhs_value, "neq"); + case BINARYOP_GE: + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGE, lhs_value, rhs_value, "ge"); + return type_is_unsigned(type) + ? LLVMBuildICmp(context->builder, LLVMIntUGE, lhs_value, rhs_value, "ge") + : LLVMBuildICmp(context->builder, LLVMIntSGE, lhs_value, rhs_value, "ge"); + case BINARYOP_GT: + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealUGT, lhs_value, rhs_value, "gt"); + return type_is_unsigned(type) + ? LLVMBuildICmp(context->builder, LLVMIntUGT, lhs_value, rhs_value, "gt") + : LLVMBuildICmp(context->builder, LLVMIntSGT, lhs_value, rhs_value, "gt"); + case BINARYOP_LE: + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "le"); + return type_is_unsigned(type) + ? LLVMBuildICmp(context->builder, LLVMIntULE, lhs_value, rhs_value, "le") + : LLVMBuildICmp(context->builder, LLVMIntSLE, lhs_value, rhs_value, "le"); + case BINARYOP_LT: + if (type_is_float(type)) LLVMBuildFCmp(context->builder, LLVMRealULE, lhs_value, rhs_value, "lt"); + return type_is_unsigned(type) + ? LLVMBuildICmp(context->builder, LLVMIntULT, lhs_value, rhs_value, "lt") + : LLVMBuildICmp(context->builder, LLVMIntSLT, lhs_value, rhs_value, "lt"); + case BINARYOP_AND: + case BINARYOP_OR: + UNREACHABLE + case BINARYOP_ASSIGN: + case BINARYOP_MULT_ASSIGN: + case BINARYOP_ADD_ASSIGN: + case BINARYOP_SUB_ASSIGN: + case BINARYOP_DIV_ASSIGN: + case BINARYOP_MOD_ASSIGN: + case BINARYOP_AND_ASSIGN: + case BINARYOP_OR_ASSIGN: + case BINARYOP_BIT_AND_ASSIGN: + case BINARYOP_BIT_OR_ASSIGN: + case BINARYOP_BIT_XOR_ASSIGN: + case BINARYOP_SHR_ASSIGN: + case BINARYOP_SHL_ASSIGN: + UNREACHABLE + } +} + + +static LLVMValueRef gencontext_emit_binary_expr(GenContext *context, Expr *expr) +{ + BinaryOp binary_op = binaryop_from_token(expr->binary_expr.operator); + if (binary_op > BINARYOP_ASSIGN) + { + BinaryOp base_op = binaryop_assign_base_op(binary_op); + assert(base_op != BINARYOP_ERROR); + LLVMValueRef value = gencontext_emit_binary(context, expr, true, base_op); + gencontext_emit_assign(context, expr->binary_expr.left, value); + return value; + } + if (binary_op == BINARYOP_ASSIGN) + { + LLVMValueRef value = gencontext_emit_expr(context, expr->binary_expr.right); + gencontext_emit_assign(context, expr->binary_expr.left, value); + return value; + } + return gencontext_emit_binary(context, expr, false, binary_op); +} + +static LLVMValueRef gencontext_emit_identifier_expr(GenContext *context, Expr *expr) +{ + return LLVMBuildLoad2(context->builder, expr->identifier_expr.decl->var.type->canonical->backend_type, + expr->identifier_expr.decl->var.backend_ref, expr->identifier_expr.decl->name.string); +} + +LLVMValueRef gencontext_emit_const_expr(GenContext *context, Expr *expr) +{ + LLVMTypeRef type = gencontext_get_llvm_type(context, expr->type->canonical); + switch (expr->const_expr.type) + { + case CONST_INT: + return LLVMConstInt(type, expr->const_expr.i, type_is_unsigned(expr->type->canonical) ? false : true); + case CONST_FLOAT: + return LLVMConstReal(type, (double) expr->const_expr.f); + case CONST_NIL: + return LLVMConstNull(type); + case CONST_BOOL: + return LLVMConstInt(type, expr->const_expr.b ? 1 : 0, false); + case CONST_STRING: + return LLVMConstStringInContext(context->context, + expr->const_expr.string.chars, + expr->const_expr.string.len, + false); + } + UNREACHABLE +} + +LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr) +{ + switch (expr->expr_kind) + { + case EXPR_POISONED: + UNREACHABLE + case EXPR_UNARY: + return gencontext_emit_unary_expr(context, expr); + case EXPR_TRY: + break; + case EXPR_CONST: + return gencontext_emit_const_expr(context, expr); + case EXPR_BINARY: + return gencontext_emit_binary_expr(context, expr); + case EXPR_CONDITIONAL: + break; + case EXPR_POST_UNARY: + break; + case EXPR_TYPE: + break; + case EXPR_IDENTIFIER: + return gencontext_emit_identifier_expr(context, expr); + case EXPR_TYPE_ACCESS: + break; + case EXPR_CALL: + break; + case EXPR_SIZEOF: + break; + case EXPR_SUBSCRIPT: + break; + case EXPR_ACCESS: + break; + case EXPR_STRUCT_VALUE: + break; + case EXPR_STRUCT_INIT_VALUES: + break; + case EXPR_INITIALIZER_LIST: + break; + case EXPR_EXPRESSION_LIST: + break; + case EXPR_CAST: + return gencontext_emit_cast_expr(context, expr); + } + TODO +} \ No newline at end of file diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c new file mode 100644 index 000000000..152bdcb20 --- /dev/null +++ b/src/compiler/llvm_codegen_function.c @@ -0,0 +1,187 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + + + +static char *mangle_name(char *buffer, Decl *decl) +{ + sprintf(buffer, "mangled_name_%*s", decl->name.span.length, decl->name.start); + return buffer; +} + +void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block) +{ + assert(context->current_block); + // If it's not used, we can delete the previous block and skip the branch. + // Unless it is the entry block or a label target for jumps + // These empty blocks will occur when doing branches. + // Consider: + // while (1) + // { + // break; + // break; + // } + // Naively we'd output + // br label %for.cond - 1st break + // br label %for.cond - 2nd break + // br label %for.cond - end of scope + // + // The fix is to introduce a new block after a break: + // br label %for.cond + // jmp: + // br label %for.cond + // jmp.1: + // br label %for.cond + // + // But this leaves us with blocks that have no parent. + // Consequently we will delete those and realize that + // we then have no need for emitting a br. + if (!context->current_block_is_target + && !LLVMGetFirstUse(LLVMBasicBlockAsValue(context->current_block))) + { + LLVMDeleteBasicBlock(context->current_block); + context->current_block = NULL; + return; + } + context->current_block = NULL; + LLVMBuildBr(context->builder, next_block); +} + +void gencontext_emit_cond_br(GenContext *context, LLVMValueRef value, LLVMBasicBlockRef thenBlock, LLVMBasicBlockRef elseBlock) +{ + assert(context->current_block); + LLVMBuildCondBr(context->builder, value, thenBlock, elseBlock); + LLVMClearInsertionPosition(context->builder); + context->current_block = NULL; + context->current_block_is_target = false; +} + + +void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block) +{ + assert(context->current_block == NULL); + LLVMAppendExistingBasicBlock(context->function, next_block); + LLVMPositionBuilderAtEnd(context->builder, next_block); + context->current_block = next_block; + context->current_block_is_target = false; +} + + +static inline void gencontext_emit_parameter(GenContext *context, Decl *decl, unsigned index) +{ + assert(decl->decl_kind == DECL_VAR && decl->var.kind == VARDECL_PARAM); + + // Allocate room on stack and copy. + decl->var.backend_ref = gencontext_emit_alloca(context, decl); + LLVMBuildStore(context->builder, LLVMGetParam(context->function, index), decl->var.backend_ref); +} + +void gencontext_emit_function_body(GenContext *context, Decl *decl) +{ + assert(decl->func.backend_value); + + LLVMValueRef prev_function = context->function; + LLVMBuilderRef prev_builder = context->builder; + + context->function = decl->func.backend_value; + + LLVMBasicBlockRef entry = LLVMAppendBasicBlockInContext(context->context, context->function, "entry"); + context->current_block = entry; + context->current_block_is_target = true; + context->builder = LLVMCreateBuilder(); + LLVMPositionBuilderAtEnd(context->builder, entry); + + LLVMValueRef alloca_point = LLVMBuildAlloca(context->builder, LLVMInt32TypeInContext(context->context), "alloca_point"); + context->alloca_point = alloca_point; + + // Generate LLVMValueRef's for all parameters, so we can use them as local vars in code + VECEACH(decl->func.function_signature.params, i) + { + gencontext_emit_parameter(context, decl->func.function_signature.params[i], i); + } + + VECEACH(decl->func.labels, i) + { + Ast *label = decl->func.labels[i]; + label->label_stmt.backend_value = gencontext_create_free_block(context, label->token.string); + } + + gencontext_emit_compound_stmt(context, decl->func.body); + + // Insert a return if needed. + if (!LLVMGetBasicBlockTerminator(LLVMGetInsertBlock(context->builder))) + { + assert(decl->func.function_signature.rtype->type_kind == TYPE_VOID); + LLVMBuildRetVoid(context->builder); + } + + // erase alloca point + if (LLVMGetInstructionParent(alloca_point)) + { + context->alloca_point = NULL; + LLVMInstructionEraseFromParent(alloca_point); + } + + LLVMDisposeBuilder(context->builder); + + context->builder = prev_builder; + context->function = prev_function; +} + +void gencontext_emit_function_decl(GenContext *context, Decl *decl) +{ + assert(decl->decl_kind == DECL_FUNC); + char workbuf[2048] = { '\0' }; + char *external_name = mangle_name(workbuf, decl); + // Resolve function backend type for function. + decl->func.backend_value = LLVMAddFunction(context->module, external_name, + gencontext_get_llvm_type(context, decl->self_type)); + + // Specify appropriate storage class, visibility and call convention + // extern functions (linkedited in separately): + /* + if (glofn->flags & FlagSystem) { + LLVMSetFunctionCallConv(glofn->llvmvar, LLVMX86StdcallCallConv); + LLVMSetDLLStorageClass(glofn->llvmvar, LLVMDLLImportStorageClass); + }*/ + if (decl->visibility == VISIBLE_LOCAL) + { + LLVMSetVisibility(decl->func.backend_value, LLVMHiddenVisibility); + } + + if (context->debug.builder) + { + LLVMDIFlags flags = LLVMDIFlagZero; + if (!decl->func.body) flags |= LLVMDIFlagPrototyped; + switch (decl->visibility) + { + case VISIBLE_LOCAL: + flags |= LLVMDIFlagPrivate; + break; + case VISIBLE_MODULE: + flags |= LLVMDIFlagProtected; + break; + case VISIBLE_PUBLIC: + flags |= LLVMDIFlagPublic; + break; + } + SourcePosition decl_position = source_file_find_position(decl->name.span.loc); + context->debug.function = LLVMDIBuilderCreateFunction(context->debug.builder, + context->debug.compile_unit, + decl->name.string, decl->name.span.length, + decl->name.string, decl->name.span.length, + context->debug.file, + decl_position.line, + decl->self_type->backend_debug_type, + decl->visibility == VISIBLE_LOCAL, + 1, + decl_position.line, + flags, + 0); + LLVMSetSubprogram(decl->func.backend_value, context->debug.function); + } + gencontext_emit_function_body(context, decl); +} diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h new file mode 100644 index 000000000..400227b20 --- /dev/null +++ b/src/compiler/llvm_codegen_internal.h @@ -0,0 +1,96 @@ +#pragma once + +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "compiler_internal.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "dwarf.h" + +typedef struct +{ + LLVMBasicBlockRef continue_block; + LLVMBasicBlockRef break_block; + LLVMBasicBlockRef next_block; +} BreakContinue; + +typedef struct +{ + LLVMDIBuilderRef builder; + LLVMMetadataRef file; + LLVMMetadataRef compile_unit; + LLVMMetadataRef function; + SourceRange current_range; + LLVMMetadataRef *lexical_block_stack; + LLVMMetadataRef inlined_at; +} DebugContext; + +#define BREAK_STACK_MAX 256 + +typedef struct +{ + const char *filename; + const char *dirname; + LLVMModuleRef module; + LLVMContextRef context; + LLVMValueRef function; + LLVMValueRef alloca_point; + LLVMBuilderRef builder; + LLVMBasicBlockRef current_block; + bool current_block_is_target; + Decl *cur_code_decl; + Decl *cur_func_decl; + bool did_call_stack_save; + Type *current_return_type; + int block_global_unique_count; + int ast_alloca_addr_space; + BreakContinue return_block; + int simple_return_expressions; + unsigned pointer_alignment; + int return_expressions; + Ast **defer_stack; + DebugContext debug; + Context *ast_context; + BreakContinue break_continue_stack[BREAK_STACK_MAX]; + size_t break_continue_stack_index; + +} GenContext; + + +void gencontext_begin_module(GenContext *context); +void gencontext_end_module(GenContext *context); +void gencontext_emit_stmt(GenContext *context, Ast *ast); +LLVMValueRef gencontext_emit_expr(GenContext *context, Expr *expr); +void gencontext_emit_debug_location(GenContext *context, SourceRange location); +LLVMMetadataRef gencontext_create_builtin_debug_type(GenContext *context, Type *builtin_type); +LLVMValueRef gencontext_emit_alloca(GenContext *context, Decl *decl); +void gencontext_emit_compound_stmt(GenContext *context, Ast *ast); +void gencontext_emit_block(GenContext *context, LLVMBasicBlockRef next_block); +void gencontext_emit_br(GenContext *context, LLVMBasicBlockRef next_block); +void gencontext_emit_cond_br(GenContext *context, LLVMValueRef value, LLVMBasicBlockRef thenBlock, LLVMBasicBlockRef elseBlock); +static inline LLVMBasicBlockRef gencontext_create_free_block(GenContext *context, const char *name) +{ + return LLVMCreateBasicBlockInContext(context->context, name); +} + +void gencontext_emit_function_decl(GenContext *context, Decl *decl); +LLVMValueRef gencontext_emit_address(GenContext *context, Expr *expr); +#define LLVMTYPE(type) gencontext_get_llvm_type(context, type->canonical) + +LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type); + +static inline bool gencontext_use_debug(GenContext *context) +{ + return context->debug.builder != NULL; +} + diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c new file mode 100644 index 000000000..f24a7de9e --- /dev/null +++ b/src/compiler/llvm_codegen_module.c @@ -0,0 +1,121 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + + + +static inline LLVMTypeRef gencontext_create_basic_llvm_type(GenContext *context, Type *type) +{ + switch (type->type_kind) + { + case TYPE_BOOL: + return LLVMInt1TypeInContext(context->context); + case TYPE_I8: + case TYPE_U8: + return LLVMInt8TypeInContext(context->context); + case TYPE_I16: + case TYPE_U16: + return LLVMInt16TypeInContext(context->context); + case TYPE_I32: + case TYPE_U32: + return LLVMInt32TypeInContext(context->context); + case TYPE_I64: + case TYPE_U64: + return LLVMInt64TypeInContext(context->context); + case TYPE_F32: + return LLVMFloatTypeInContext(context->context); + case TYPE_F64: + return LLVMDoubleTypeInContext(context->context); + case TYPE_VOID: + return LLVMVoidTypeInContext(context->context); + default: + UNREACHABLE + } +} + +static inline void gencontext_init_basic_llvm_type(GenContext *context, Type *type) +{ + type->backend_type = gencontext_create_basic_llvm_type(context, type); + if (context->debug.builder) + { + type->backend_debug_type = gencontext_create_builtin_debug_type(context, type); + } +} +void gencontext_begin_module(GenContext *context) +{ + assert(!context->module && "Expected no module"); + const char *full_path = context->ast_context->file->full_path; + char *mangled_module_name = strformat("module:%s", context->ast_context->module->name); + context->module = LLVMModuleCreateWithNameInContext(mangled_module_name, context->context); + LLVMSetModuleDataLayout(context->module, target_data_layout()); + LLVMSetSourceFileName(context->module, full_path, strlen(context->ast_context->file->full_path)); + + LLVMSetTarget(context->module, build_options.target); + if (build_options.debug_info) + { + const char *filename = context->ast_context->file->name; + const char *dir_path = context->ast_context->file->dir_path; + context->debug.builder = LLVMCreateDIBuilder(context->module); + context->debug.file = LLVMDIBuilderCreateFile(context->debug.builder, filename, strlen(filename), dir_path, strlen(dir_path)); + + bool is_optimized = false; + const char *dwarf_flags = ""; + unsigned runtime_version = 1; + LLVMDWARFEmissionKind emission_kind = LLVMDWARFEmissionFull; + context->debug.compile_unit = LLVMDIBuilderCreateCompileUnit(context->debug.builder, LLVMDWARFSourceLanguageC, + context->debug.file, DWARF_PRODUCER_NAME, + strlen(DWARF_PRODUCER_NAME), is_optimized, + dwarf_flags, strlen(dwarf_flags), + runtime_version, "" /* split name */, 0 /* len */, + emission_kind, /* dwo */0, /* inlining */0, + /* debug for profiling */0); + } + // Setup all types. Not thread-safe, but at this point in time we can assume a single context. + // We need to remove the context from the cache after this. + // This would seem to indicate that we should change Type / actual type. + gencontext_init_basic_llvm_type(context, type_char); + gencontext_init_basic_llvm_type(context, type_byte); + gencontext_init_basic_llvm_type(context, type_short); + gencontext_init_basic_llvm_type(context, type_ushort); + gencontext_init_basic_llvm_type(context, type_int); + gencontext_init_basic_llvm_type(context, type_uint); + gencontext_init_basic_llvm_type(context, type_long); + gencontext_init_basic_llvm_type(context, type_ulong); + gencontext_init_basic_llvm_type(context, type_float); + gencontext_init_basic_llvm_type(context, type_double); + gencontext_init_basic_llvm_type(context, type_void); + gencontext_init_basic_llvm_type(context, type_bool); + + context->pointer_alignment = LLVMPointerSizeForAS(target_data_layout(), 0); + + context->block_global_unique_count = 0; + context->ast_alloca_addr_space = target_alloca_addr_space(); + +/* + SizeSizeInBytes = + C.toCharUnitsFromBits(C.getTargetInfo().getMaxPointerWidth()).getQuantity(); + IntAlignInBytes = + C.toCharUnitsFromBits(C.getTargetInfo().getIntAlign()).getQuantity(); + IntTy = llvm::IntegerType::get(LLVMContext, C.getTargetInfo().getIntWidth()); + IntPtrTy = llvm::IntegerType::get(LLVMContext, + C.getTargetInfo().getMaxPointerWidth()); + Int8PtrTy = Int8Ty->getPointerTo(0); + Int8PtrPtrTy = Int8PtrTy->getPointerTo(0); + AllocaInt8PtrTy = Int8Ty->getPointerTo( + M.getDataLayout().getAllocaAddrSpace()); + ASTAllocaAddressSpace = getTargetCodeGenInfo().getASTAllocaAddressSpace(); +*/ + +} + + +void gencontext_end_module(GenContext *context) +{ + if (context->debug.builder) + { + LLVMDIBuilderFinalize(context->debug.builder); + } + LLVMDisposeModule(context->module); +} diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c new file mode 100644 index 000000000..e117f4150 --- /dev/null +++ b/src/compiler/llvm_codegen_stmt.c @@ -0,0 +1,471 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + +static void gencontext_pop_break_continue(GenContext *context); +static void +gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, + LLVMBasicBlockRef next_block); + +void gencontext_emit_compound_stmt(GenContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_COMPOUND_STMT); + VECEACH(ast->compound_stmt.stmts, i) + { + gencontext_emit_stmt(context, ast->compound_stmt.stmts[i]); + } +} + +static inline void gencontext_emit_stmt_list(GenContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_STMT_LIST); + VECEACH(ast->stmt_list, i) + { + gencontext_emit_stmt(context, ast->stmt_list[i]); + } +} + +static inline void gencontext_emit_return(GenContext *context, Ast *ast) +{ + if (!ast->return_stmt.expr) + { + LLVMBuildRetVoid(context->builder); + return; + } + LLVMValueRef returnValue = gencontext_emit_expr(context, ast->return_stmt.expr); + LLVMBuildRet(context->builder, returnValue); +} + +static inline LLVMValueRef gencontext_emit_cond(GenContext *context, Ast *ast) +{ + assert(ast->ast_kind == AST_COND_STMT); + VECEACH(ast->cond_stmt.stmts, i) + { + gencontext_emit_stmt(context, ast->cond_stmt.stmts[i]); + } + return gencontext_emit_expr(context, ast->cond_stmt.expr); +} + +static LLVMValueRef gencontext_emit_decl(GenContext *context, Ast *ast) +{ + Decl *decl = ast->declare_stmt; + + decl->var.backend_ref = gencontext_emit_alloca(context, decl); + // TODO NRVO + // TODO debug info + /* + if (EmitDebugInfo && HaveInsertPoint()) { + Address DebugAddr = address; + bool UsePointerValue = NRVO && ReturnValuePointer.isValid(); + DI->setLocation(D.getLocation()); + + // If NRVO, use a pointer to the return address. + if (UsePointerValue) + DebugAddr = ReturnValuePointer; + + (void)DI->EmitDeclareOfAutoVariable(&D, DebugAddr.getPointer(), Builder, + UsePointerValue); + } + */ + if (decl->var.init_expr) + { + LLVMValueRef value = gencontext_emit_expr(context, decl->var.init_expr); + LLVMBuildStore(context->builder, value, decl->var.backend_ref); + return value; + } + return decl->var.backend_ref; +} + + +void gencontext_emit_if(GenContext *context, Ast *ast) +{ + // In the case of something like if (foo = 1, int bar = 2; b > 0) { ... } + // Let's first emit foo = 1, int bar = 2 + // IMPROVE Consider whether these should be lowered or not. + Ast **stmts = ast->if_stmt.cond->cond_stmt.stmts; + VECEACH(stmts, i) + { + gencontext_emit_stmt(context, stmts[i]); + } + + // We need at least the exit block and the "then" block. + LLVMBasicBlockRef exit_block = LLVMCreateBasicBlockInContext(context->context, "if.exit"); + LLVMBasicBlockRef then_block = LLVMCreateBasicBlockInContext(context->context, "if.then"); + LLVMBasicBlockRef else_block = NULL; + + // We have an optional else block. + if (ast->if_stmt.else_body) + { + else_block = LLVMCreateBasicBlockInContext(context->context, "if.else"); + } + + assert(ast->if_stmt.cond->cond_stmt.expr->type->type_kind == TYPE_BOOL); + + // Output boolean value and switch. + LLVMValueRef value = gencontext_emit_expr(context, ast->if_stmt.cond->cond_stmt.expr); + gencontext_emit_cond_br(context, value, then_block, else_block ? else_block : exit_block); + + // Emit the 'then' code. + gencontext_emit_block(context, then_block); + gencontext_emit_stmt(context, ast->if_stmt.then_body); + + // Jump to exit. + gencontext_emit_br(context, exit_block); + + // Emit the 'else' branch if present. + if (else_block) + { + gencontext_emit_block(context, else_block); + gencontext_emit_stmt(context, ast->if_stmt.else_body); + gencontext_emit_br(context, exit_block); + } + + // And now we just emit the exit block. + gencontext_emit_block(context, exit_block); +} + + +static void gencontext_push_next(GenContext *context, LLVMBasicBlockRef nextBlock) +{ + // TODO +} +static void +gencontext_push_break_continue(GenContext *context, LLVMBasicBlockRef break_block, LLVMBasicBlockRef continue_block, + LLVMBasicBlockRef next_block) +{ + size_t index = context->break_continue_stack_index++; + if (index == BREAK_STACK_MAX - 1) + { + error_exit("Exhausted break/continue stack - exceeded %d entries.", BREAK_STACK_MAX); + } + if (!index) + { + context->break_continue_stack[index].continue_block = continue_block; + context->break_continue_stack[index].break_block = break_block; + context->break_continue_stack[index].next_block = next_block; + return; + } + + context->break_continue_stack[index].continue_block = continue_block ? continue_block : context->break_continue_stack[index - 1].continue_block; + context->break_continue_stack[index].next_block = next_block ? next_block : context->break_continue_stack[index - 1].next_block; + context->break_continue_stack[index].break_block = break_block ? break_block : context->break_continue_stack[index - 1].break_block; +} + +static void gencontext_pop_break_continue(GenContext *context) +{ + assert(context->break_continue_stack_index); + context->break_continue_stack_index--; +} + +void gencontext_emit_for_stmt(GenContext *context, Ast *ast) +{ + // First, emit all inits. + + Ast **init_stmts = ast->for_stmt.init; + + VECEACH(init_stmts, i) + { + gencontext_emit_stmt(context, init_stmts[i]); + } + + // We have 3 optional parts, which makes this code bit complicated. + LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "for.exit"); + LLVMBasicBlockRef inc_block = ast->for_stmt.incr ? gencontext_create_free_block(context, "for.inc") : NULL; + LLVMBasicBlockRef body_block = ast->for_stmt.body->compound_stmt.stmts ? gencontext_create_free_block(context, "for.body") : NULL; + LLVMBasicBlockRef cond_block = ast->for_stmt.cond ? gencontext_create_free_block(context, "for.cond") : NULL; + + // A loop must either have a body or an inc. + // This type of for loop is forbidden: + // for (;;); + assert(cond_block || inc_block || body_block && "For has no body, no inc and no cond."); + + // Break is simple it always jumps out. + // For continue: + // 1. If there is inc, jump to the condition + // 2. If there is no condition, jump to the body. + gencontext_push_break_continue(context, + exit_block, + inc_block ? inc_block : (cond_block ? cond_block : body_block), + NULL); + + LLVMValueRef value = NULL; + + if (cond_block) + { + // Emit cond + gencontext_emit_br(context, cond_block); + gencontext_emit_block(context, cond_block); + value = gencontext_emit_expr(context, ast->for_stmt.cond->cond_stmt.expr); + // If we have a body, conditionally jump to it. + if (body_block) + { + gencontext_emit_cond_br(context, value, body_block, exit_block); + } + else + { + // Otherwise jump to inc or cond depending on what's available. + gencontext_emit_cond_br(context, value, inc_block ? inc_block : cond_block, exit_block); + } + } + + if (body_block) + { + if (!cond_block) + { + // We don't have a cond, so we need to unconditionally jump here. + gencontext_emit_br(context, body_block); + } + gencontext_emit_block(context, body_block); + gencontext_emit_stmt(context, ast->for_stmt.body); + // IMPROVE handle continue/break. + if (inc_block) + { + gencontext_emit_br(context, inc_block); + } + } + + if (inc_block) + { + // Emit the block + gencontext_emit_block(context, inc_block); + gencontext_emit_stmt(context, ast->for_stmt.incr); + } + + // Loop back. + gencontext_emit_br(context, cond_block ? cond_block : (body_block ? body_block : inc_block)); + + // And insert exit block + gencontext_emit_block(context, exit_block); + gencontext_pop_break_continue(context); +} + +void gencontext_emit_do_stmt(GenContext *context, Ast *ast) +{ + LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "do.exit"); + LLVMBasicBlockRef cond_block = ast->do_stmt.expr ? gencontext_create_free_block(context, "do.cond") : NULL; + LLVMBasicBlockRef body_block = ast->do_stmt.body ? gencontext_create_free_block(context, "do.body") : NULL; + + // A loop must either have a body or an inc. + // This type do-while for loop is forbidden: + // do { } while (1); + assert(cond_block || body_block && "Do has no body and no cond."); + + // Break is simple it always jumps out. + // For continue: if there is no condition, jump to the body. + gencontext_push_break_continue(context, exit_block, cond_block ? cond_block : body_block, NULL); + + if (body_block) + { + // Emit the body + gencontext_emit_br(context, body_block); + gencontext_emit_block(context, body_block); + gencontext_emit_stmt(context, ast->do_stmt.body); + } + + if (cond_block) + { + gencontext_emit_br(context, cond_block); + gencontext_emit_block(context, cond_block); + LLVMValueRef value = gencontext_emit_expr(context, ast->do_stmt.expr); + gencontext_emit_cond_br(context, value, body_block ? body_block : cond_block, exit_block); + } + else + { + // Branch to the beginning of the block + gencontext_emit_br(context, body_block); + } + + // Emit the exit block. + gencontext_emit_block(context, exit_block); + + gencontext_pop_break_continue(context); +} + +void gencontext_emit_jmp(GenContext *context, LLVMBasicBlockRef block) +{ + gencontext_emit_br(context, block); + LLVMBasicBlockRef post_jump_block = gencontext_create_free_block(context, "jmp"); + gencontext_emit_block(context, post_jump_block); +} + +void gencontext_emit_label(GenContext *context, Ast *ast) +{ + gencontext_emit_br(context, ast->label_stmt.backend_value); + gencontext_emit_block(context, ast->label_stmt.backend_value); + context->current_block_is_target = true; +} + +void gencontext_emit_switch(GenContext *context, Ast *ast) +{ + LLVMValueRef switch_value = gencontext_emit_cond(context, ast->switch_stmt.cond); + + size_t cases = vec_size(ast->switch_stmt.cases); + if (!cases) + { + // No body or default is empty, just exit after the value. + return; + } + + Ast *default_case = NULL; + VECEACH(ast->switch_stmt.cases, i) + { + Ast *case_stmt = ast->switch_stmt.cases[i]; + if (case_stmt->case_stmt.value_type == CASE_VALUE_DEFAULT) + { + if (case_stmt->case_stmt.block) + { + case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.default"); + } + default_case = case_stmt; + } + else if (case_stmt->case_stmt.block) + { + case_stmt->case_stmt.backend_value = gencontext_create_free_block(context, "switch.case"); + } + } + + LLVMBasicBlockRef exit_block = gencontext_create_free_block(context, "switch.exit"); + + // We will now treat the fallthrough cases: + // switch (i) + // { + // case 1: + // case 2: + // do_something(); + // default: + // } + VECEACH(ast->switch_stmt.cases, i) + { + Ast *case_stmt = ast->switch_stmt.cases[i]; + if (case_stmt->case_stmt.backend_value != NULL) continue; + + // Look forward for a block + for (size_t j = i + 1; j < cases; j++) + { + Ast *other_case = ast->switch_stmt.cases[j]; + if (other_case->case_stmt.backend_value != NULL) + { + case_stmt->case_stmt.backend_value = other_case->case_stmt.backend_value; + break; + } + } + // No block found? Then the block is the exit block. + if (!case_stmt->case_stmt.backend_value) + { + case_stmt->case_stmt.backend_value = exit_block; + } + } + + gencontext_push_break_continue(context, exit_block, NULL, NULL); + + LLVMValueRef switch_stmt = LLVMBuildSwitch(context->builder, switch_value, default_case ? default_case->case_stmt.backend_value : exit_block, cases); + context->current_block = NULL; + VECEACH(ast->switch_stmt.cases, i) + { + Ast *case_stmt = ast->switch_stmt.cases[i]; + LLVMBasicBlockRef block = case_stmt->case_stmt.backend_value; + if (case_stmt != default_case) + { + LLVMValueRef case_value = LLVMConstInt(LLVMTypeOf(switch_value), case_stmt->case_stmt.val, case_stmt->case_stmt.value_type == CASE_VALUE_INT); + LLVMAddCase(switch_stmt, case_value, block); + } + + // Skip fallthroughs. + if (!case_stmt->case_stmt.block) continue; + + gencontext_emit_block(context, block); + // IMPORTANT! + context->current_block_is_target = true; + gencontext_push_break_continue(context, NULL, NULL, i < cases - 1 ? ast->switch_stmt.cases[i + 1]->case_stmt.backend_value : exit_block); + gencontext_emit_stmt(context, case_stmt->case_stmt.block); + gencontext_pop_break_continue(context); + gencontext_emit_br(context, exit_block); + } + gencontext_pop_break_continue(context); + gencontext_emit_block(context, exit_block); +} + + +void gencontext_emit_stmt(GenContext *context, Ast *ast) +{ + switch (ast->ast_kind) + { + case AST_POISONED: + UNREACHABLE + case AST_EXPR_STMT: + gencontext_emit_expr(context, ast->expr_stmt); + break; + case AST_DECLARE_STMT: + gencontext_emit_decl(context, ast); + break; + case AST_BREAK_STMT: + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].break_block); + break; + case AST_CONTINUE_STMT: + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].continue_block); + break; + case AST_IF_STMT: + gencontext_emit_if(context, ast); + break; + case AST_RETURN_STMT: + gencontext_emit_return(context, ast); + break; + case AST_COMPOUND_STMT: + gencontext_emit_compound_stmt(context, ast); + break; + case AST_FOR_STMT: + gencontext_emit_for_stmt(context, ast); + break; + case AST_DO_STMT: + gencontext_emit_do_stmt(context, ast); + break; + case AST_NEXT_STMT: + assert(context->break_continue_stack_index); + gencontext_emit_jmp(context, context->break_continue_stack[context->break_continue_stack_index - 1].next_block); + break; + case AST_NOP_STMT: + break; + case AST_WHILE_STMT: + case AST_CATCH_STMT: + case AST_DEFER_STMT: + case AST_TRY_STMT: + case AST_THROW_STMT: + // Should have been lowered. + UNREACHABLE + case AST_ASM_STMT: + TODO + case AST_ATTRIBUTE: + case AST_COND_STMT: + case AST_CT_IF_STMT: + case AST_CT_ELIF_STMT: + case AST_CT_ELSE_STMT: + case AST_CT_FOR_STMT: + case AST_CT_SWITCH_STMT: + case AST_CT_DEFAULT_STMT: + case AST_CT_CASE_STMT: + case AST_GENERIC_CASE_STMT: + case AST_GENERIC_DEFAULT_STMT: + UNREACHABLE + case AST_LABEL: + gencontext_emit_label(context, ast); + break; + case AST_GOTO_STMT: + gencontext_emit_jmp(context, ast->goto_stmt.label->label_stmt.backend_value); + break; + case AST_STMT_LIST: + gencontext_emit_stmt_list(context, ast); + break; + case AST_SWITCH_STMT: + gencontext_emit_switch(context, ast); + break; + case AST_CASE_STMT: + case AST_DEFAULT_STMT: + TODO + case AST_VOLATILE_STMT: + TODO + } +} diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c new file mode 100644 index 000000000..ee03417ff --- /dev/null +++ b/src/compiler/llvm_codegen_type.c @@ -0,0 +1,188 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include "llvm_codegen_internal.h" + + + +static inline LLVMTypeRef gencontext_create_llvm_type_from_decl(GenContext *context, Decl *decl) +{ + static LLVMTypeRef params[512]; + static LLVMMetadataRef debug_params[512]; + switch (decl->decl_kind) + { + case DECL_ATTRIBUTE: + case DECL_POISONED: + case DECL_GENERIC: + case DECL_MULTI_DECL: + case DECL_MACRO: + case DECL_CT_IF: + case DECL_CT_ELSE: + case DECL_CT_ELIF: + UNREACHABLE; + case DECL_FUNC: + { + VECEACH(decl->func.function_signature.params, i) + { + Type *param_type = decl->func.function_signature.params[i]->var.type; + params[i] = gencontext_get_llvm_type(context, param_type); + if (build_options.debug_info) + { + assert(param_type->backend_debug_type); + debug_params[i + 1] = param_type->backend_debug_type; + } + } + unsigned param_size = vec_size(decl->func.function_signature.params); + if (gencontext_use_debug(context)) + { + gencontext_get_llvm_type(context, decl->func.function_signature.rtype); + debug_params[0] = decl->func.function_signature.rtype->backend_debug_type; + LLVMMetadataRef function_type = LLVMDIBuilderCreateSubroutineType(context->debug.builder, + context->debug.file, + debug_params, param_size + 1, + /** TODO **/ 0); + decl->self_type->backend_debug_type = function_type; + } + return LLVMFunctionType(gencontext_get_llvm_type(context, decl->func.function_signature.rtype), + params, + param_size, + decl->func.function_signature.variadic); + + } + + case DECL_VAR: + break; + case DECL_ENUM_CONSTANT: + break; + case DECL_TYPEDEF: + break; + case DECL_STRUCT: + { + LLVMTypeRef *types = NULL; + VECEACH(decl->strukt.members, i) + { + VECADD(types, gencontext_get_llvm_type(context, decl->strukt.members[i]->var.type)); + } + LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->name.string); + LLVMStructSetBody(type, types, vec_size(types), decl->is_packed); + return type; + } + case DECL_UNION: + break; + case DECL_ENUM: + break; + case DECL_ERROR: + break; + case DECL_ERROR_CONSTANT: + break; + case DECL_ARRAY_VALUE: + break; + case DECL_IMPORT: + UNREACHABLE + } + UNREACHABLE +} +static inline LLVMTypeRef gencontext_create_llvm_type_from_ptr(GenContext *context, Type *type) +{ + LLVMTypeRef base_llvm_type = gencontext_get_llvm_type(context, type->base); + + if (gencontext_use_debug(context)) + { + type->backend_debug_type = LLVMDIBuilderCreatePointerType(context->debug.builder, type->base->backend_debug_type, type_size(type->canonical->base), 0, 0, /* TODO */ "TODO", 4); + } + + if (type->canonical != type) + { + return type->backend_type = gencontext_get_llvm_type(context, type->canonical); + } + + return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0); +} + +static inline LLVMTypeRef gencontext_create_llvm_type_from_array(GenContext *context, Type *type) +{ + LLVMTypeRef base_llvm_type = gencontext_get_llvm_type(context, type->base); + + if (gencontext_use_debug(context)) + { + LLVMMetadataRef *ranges = NULL; + Type *current_type = type; + while (current_type->canonical->type_kind == TYPE_ARRAY) + { + VECADD(ranges, LLVMDIBuilderGetOrCreateSubrange(context->debug.builder, 0, current_type->canonical->len)); + current_type = current_type->canonical->base; + } + type->backend_debug_type = LLVMDIBuilderCreateArrayType( + context->debug.builder, + type->len, + 0 /* ALIGN */, + type->base->backend_debug_type, + ranges, vec_size(ranges)); + } + + if (type->canonical != type) + { + return type->backend_type = gencontext_get_llvm_type(context, type->canonical); + } + + return type->backend_type = LLVMPointerType(base_llvm_type, /** TODO **/0); +} + +LLVMTypeRef gencontext_create_llvm_func_type(GenContext *context, Type *type) +{ + LLVMTypeRef *params = NULL; + FunctionSignature *signature = type->func.signature; + // TODO throws + if (vec_size(signature->params)) + { + params = malloc_arena(sizeof(LLVMTypeRef) * vec_size(signature->params)); + VECEACH(signature->params, i) + { + params[i] = gencontext_get_llvm_type(context, signature->params[i]->var.type->canonical); + } + } + return LLVMFunctionType( + gencontext_get_llvm_type(context, type->func.signature->rtype), + params, vec_size(signature->params), signature->variadic); +} + +LLVMTypeRef gencontext_get_llvm_type(GenContext *context, Type *type) +{ + if (type->backend_type) return type->backend_type; + switch (type->type_kind) + { + case TYPE_USER_DEFINED: + return type->backend_type = gencontext_create_llvm_type_from_decl(context, type->decl); + case TYPE_FUNC: + return type->backend_type = gencontext_create_llvm_func_type(context, type); + case TYPE_VOID: + case TYPE_F64: + case TYPE_F32: + case TYPE_U64: + case TYPE_POISONED: + case TYPE_BOOL: + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_IXX: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_UXX: + case TYPE_FXX: + case TYPE_EXPRESSION: + case TYPE_INC_ARRAY: + UNREACHABLE; + case TYPE_POINTER: + return type->backend_type = gencontext_create_llvm_type_from_ptr(context, type); + case TYPE_STRING: + TODO; + case TYPE_ARRAY: + return type->backend_type = gencontext_create_llvm_type_from_array(context, type); + case TYPE_VARARRAY: + TODO + } + UNREACHABLE; +} diff --git a/src/compiler/parser.c b/src/compiler/parser.c index af6f43359..1e0eaaa32 100644 --- a/src/compiler/parser.c +++ b/src/compiler/parser.c @@ -2229,7 +2229,7 @@ static inline bool parse_func_typedef(Decl *decl, Visibility visibility) static inline Decl *parse_typedef_declaration(Visibility visibility) { - Decl *decl = decl_new(DECL_TYPEDEF, tok, visibility); + Decl *decl = decl_new_user_defined_type(tok, DECL_TYPEDEF, visibility); advance_and_verify(TOKEN_TYPEDEF); if (tok.type == TOKEN_FUNC) { @@ -2329,29 +2329,29 @@ static inline Decl *parse_func_definition(Visibility visibility, bool is_interfa if (path || tok.type == TOKEN_TYPE_IDENT) { // Special case, actually an extension - TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", false); + TRY_EXPECT_OR(TOKEN_TYPE_IDENT, "A type was expected after '::'.", &poisoned_decl); Type *type = type_new(TYPE_USER_DEFINED); type->unresolved.path = path; type->name_loc = tok; func->func.type_parent = type; advance_and_verify(TOKEN_TYPE_IDENT); - TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", false); + TRY_CONSUME_OR(TOKEN_DOT, "Expected '.' after the type in a method function.", &poisoned_decl); } - EXPECT_IDENT_FOR_OR("function name", false); + EXPECT_IDENT_FOR_OR("function name", &poisoned_decl); func->name = tok; advance_and_verify(TOKEN_IDENT); - if (!parse_opt_parameter_type_list(visibility, &(func->func.function_signature), is_interface)) return false; + if (!parse_opt_parameter_type_list(visibility, &(func->func.function_signature), is_interface)) return &poisoned_decl; - if (!parse_opt_throw_declaration(&(func->func.function_signature))) return false; + if (!parse_opt_throw_declaration(&(func->func.function_signature))) return &poisoned_decl; if (is_interface) { if (tok.type == TOKEN_LBRACE) { SEMA_ERROR(next_tok, "Functions bodies are not allowed in interface files."); - return false; + return &poisoned_decl; } TRY_CONSUME_OR(TOKEN_EOS, "Expected ';' after function declaration.", &poisoned_decl); return func; diff --git a/src/compiler/semantic_analyser.c b/src/compiler/semantic_analyser.c index 8776e1f26..14aeed93e 100644 --- a/src/compiler/semantic_analyser.c +++ b/src/compiler/semantic_analyser.c @@ -36,26 +36,27 @@ Decl *context_find_ident(Context *context, const char *symbol) } -static inline void context_push_scope(Context *context) + +static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags) { if (context->current_scope == &context->scopes[MAX_SCOPE_DEPTH - 1]) { FATAL_ERROR("Too deeply nested scopes."); } + ScopeFlags previous_flags = context->current_scope->flags; context->current_scope++; context->current_scope->exit = EXIT_NONE; context->current_scope->local_decl_start = context->last_local; context->current_scope->defer_start = vec_size(context->defers); -} - -static inline void context_push_scope_with_flags(Context *context, ScopeFlags flags) -{ - ScopeFlags previous_flags = context->current_scope->flags; - context_push_scope(context); context->current_scope->flags = previous_flags | flags; context->current_scope->flags_created = flags; } +static inline void context_push_scope(Context *context) +{ + context_push_scope_with_flags(context, SCOPE_NONE); +} + static inline void context_pop_scope(Context *context) { assert(context->current_scope != &context->scopes[0]); @@ -211,10 +212,17 @@ static inline bool sema_analyse_function_param(Context *context, Decl *param, bo return true; } -static inline bool sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) +static inline Type *sema_analyse_function_signature(Context *context, FunctionSignature *signature, bool is_function) { + char buffer[2048]; + size_t buffer_write_offset = 0; bool all_ok = true; all_ok = sema_resolve_type(context, signature->rtype) && all_ok; + if (all_ok) + { + type_append_signature_name(signature->rtype, buffer, &buffer_write_offset); + buffer[buffer_write_offset++] = '('; + } // TODO check parameter name appearing more than once. VECEACH(signature->params, i) { @@ -228,12 +236,42 @@ static inline bool sema_analyse_function_signature(Context *context, FunctionSig continue; } param->resolve_status = RESOLVE_DONE; + if (i > 0 && all_ok) + { + buffer[buffer_write_offset++] = ','; + } + type_append_signature_name(param->var.type, buffer, &buffer_write_offset); } - VECEACH(signature->throws, i) + // TODO variadic + buffer[buffer_write_offset++] = ')'; + if (vec_size(signature->throws)) { - TODO + VECEACH(signature->throws, i) + { + TODO + if (i > 0 && all_ok) + { + buffer[buffer_write_offset++] = ','; + } +// type_append_signature_name(signature->tparam->var.type, buffer, &buffer_write_offset); + } } - return all_ok; + + if (!all_ok) return NULL; + TokenType type = TOKEN_INVALID_TOKEN; + signature->mangled_signature = symtab_add(buffer, buffer_write_offset, fnv1a(buffer, buffer_write_offset), &type); + Type *func_type = stable_get(&context->local_symbols, signature->mangled_signature); + if (!func_type) + { + func_type = type_new(TYPE_FUNC); + func_type->name_loc = (Token) { .string = signature->mangled_signature, .span.length = buffer_write_offset }; + func_type->resolve_status = RESOLVE_DONE; + func_type->canonical = func_type; + func_type->func.signature = signature; + stable_set(&context->local_symbols, signature->mangled_signature, func_type); + } + return func_type; + } @@ -602,6 +640,11 @@ static inline bool sema_analyse_goto_stmt(Context *context, Ast *statement) static inline bool sema_analyse_if_stmt(Context *context, Ast *statement) { + // IMPROVE + // convert + // if (!x) A(); else B(); + // into + // if (x) B(); else A(); context_push_scope(context); Ast *cond = statement->if_stmt.cond; context_push_scope_with_flags(context, SCOPE_CONTROL); @@ -635,7 +678,7 @@ static inline bool sema_analyse_label(Context *context, Ast *statement) return false; } } - vec_add(context->labels, statement); + VECADD(context->labels, statement); VECEACH(context->gotos, i) { Ast *the_goto = context->gotos[i]; @@ -941,6 +984,7 @@ static inline bool sema_analyse_function_body(Context *context, Decl *func) } sema_release_defer_chain(context, &func->func.body->compound_stmt.stmts); } + func->func.labels = context->labels; context_pop_scope(context); return true; } @@ -974,15 +1018,16 @@ static inline bool sema_analyse_method_function(Context *context, Decl *decl) static inline bool sema_analyse_func(Context *context, Decl *decl) { DEBUG_LOG("Analysing function %s", decl->name.string); - bool all_ok = sema_analyse_function_signature(context, &decl->func.function_signature, true); + Type *func_type = sema_analyse_function_signature(context, &decl->func.function_signature, true); + decl->self_type = func_type; + if (!func_type) return decl_poison(decl); if (decl->func.type_parent) { - all_ok = all_ok && sema_analyse_method_function(context, decl); + if (!sema_analyse_method_function(context, decl)) return decl_poison(decl); } - all_ok = all_ok && sema_analyse_function_body(context, decl); - if (!all_ok) decl_poison(decl); + if (!sema_analyse_function_body(context, decl)) return decl_poison(decl); DEBUG_LOG("Function analysis done.") - return all_ok; + return true; } static inline bool sema_analyse_macro(Context *context, Decl *decl) @@ -1027,7 +1072,15 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) static inline bool sema_analyse_typedef(Context *context, Decl *decl) { + if (decl->typedef_decl.is_func) + { + Type *func_type = sema_analyse_function_signature(context, &decl->typedef_decl.function_signature, false); + if (!func_type) return false; + decl->self_type->canonical = func_type; + return true; + } if (!sema_resolve_type(context, decl->typedef_decl.type)) return false; + decl->self_type->canonical = decl->typedef_decl.type; // Do we need anything else? return true; } @@ -1164,7 +1217,7 @@ static inline void sema_process_imports(Context *context) void sema_analysis_pass_conditional_compilation(Context *context) { DEBUG_LOG("Pass 1 - analyse: %s", context->file->name); - VECEACH(context->ct_ifs, i) + for (unsigned i = 0; i < vec_size(context->ct_ifs); i++) { sema_analyse_top_level_if(context, context->ct_ifs[i]); } diff --git a/src/compiler/source_file.c b/src/compiler/source_file.c index 145815c99..16df81714 100644 --- a/src/compiler/source_file.c +++ b/src/compiler/source_file.c @@ -16,7 +16,7 @@ typedef struct File **files; } SourceFiles; -SourceFiles source_files; +SourceFiles source_files = {}; File *source_file_load(const char *filename, bool *already_loaded) { @@ -45,16 +45,70 @@ File *source_file_load(const char *filename, bool *already_loaded) size_t size; const char* source_text = read_file(filename, &size); File *file = malloc(sizeof(File)); + file->full_path = full_path; file->start_id = vec_size(source_files.files) ? VECLAST(source_files.files)->end_id : 0; + file->current_line_start = file->start_id; file->contents = source_text; ASSERT(file->start_id + size < UINT32_MAX, "Total files loaded exceeded %d bytes", UINT32_MAX); file->end_id = (SourceLoc) (file->start_id + size); - file->name = filename; - source_files.files = VECADD(source_files.files, file); + size_t pre_allocated_lines = size / 40; + file->line_start = VECNEW(SourceLoc, pre_allocated_lines < 16 ? 16 : pre_allocated_lines); + VECADD(file->line_start, file->start_id); + path_get_dir_and_filename_from_full(file->full_path, &file->name, &file->dir_path); + VECADD(source_files.files, file); return file; } +void source_file_append_line_end(File *file, SourceLoc loc) +{ + if (file->current_line_start > loc) return; + file->current_line_start = loc + 1; + VECADD(file->line_start, file->current_line_start); +} + +SourcePosition source_file_find_position(SourceLoc loc) +{ + File *file = source_file_from_position(loc); + return source_file_find_position_in_file(file, loc); +} + +SourcePosition source_file_find_position_in_file(File *file, SourceLoc loc) +{ + assert(file->start_id <= loc); + + size_t lines = vec_size(file->line_start); + unsigned low = 0; + unsigned high = lines; + while (1) + { + // Line found iff line_start[mid] <= loc && line_start[mid + 1] < loc + // Binary search + uint32_t mid = (high + low) / 2; + + // Mid is before the location. + SourceLoc line_start = file->line_start[mid]; + if (line_start > loc) + { + high = mid; + continue; + } + if (mid + 1 != lines && file->line_start[mid + 1] < loc) + { + low = mid; + continue; + } + return (SourcePosition) + { + .file = file, + .line = mid + 1, + .col = loc - line_start + 1, + .loc = loc, + .start = file->contents + loc - file->start_id, + }; + } +} + File *source_file_from_position(SourceLoc loc) { if (loc == INVALID_LOC) @@ -62,10 +116,12 @@ File *source_file_from_position(SourceLoc loc) pseudo_file.contents = "---"; return &pseudo_file; } - if (lexer_current_file()->start_id <= loc) return lexer_current_file(); + static File *last_file = NULL; + //if (!last_file) last_file = lexer_current_file(); + //if (last_file->start_id <= loc && loc < last_file->end_id) return last_file; unsigned low = 0; - unsigned high = vec_size(source_files.files) - 2; - assert(vec_size(source_files.files) > 1); + unsigned high = vec_size(source_files.files) - 1; + //assert(vec_size(source_files.files) > 1); while (1) { // Binary search @@ -81,7 +137,7 @@ File *source_file_from_position(SourceLoc loc) low = mid + 1; continue; } - return file; + return last_file = file; } } diff --git a/src/compiler/target.c b/src/compiler/target.c new file mode 100644 index 000000000..e98e277c2 --- /dev/null +++ b/src/compiler/target.c @@ -0,0 +1,100 @@ +#include +#include +#include "compiler_internal.h" + +typedef struct +{ + LLVMTargetRef target; + LLVMTargetMachineRef machine; + LLVMTargetDataRef data_layout; + int alloca_address_space; +} Target; + +static Target target = {}; + +int target_alloca_addr_space() +{ + return target.alloca_address_space; +} + +void target_setup() +{ + assert(!target.target); + + LLVMInitializeAllTargetInfos(); + LLVMInitializeAllTargetMCs(); + LLVMInitializeAllTargets(); + LLVMInitializeAllAsmPrinters(); + LLVMInitializeAllAsmParsers(); + + target.target = NULL; + if (!build_options.target) + { + build_options.target = LLVMGetDefaultTargetTriple(); + } + char *err = NULL; + + if (LLVMGetTargetFromTriple(build_options.target, &target.target, &err) != 0) + { + error_exit("Could not create target: %s", err); + // Usually we would dispose of err, but no need to do it due to exit. + } + + target.alloca_address_space = 0; + + DEBUG_LOG("Target set to %s.", build_options.target); + // Create a specific target machine + LLVMCodeGenOptLevel level; + LLVMRelocMode reloc_mode = LLVMRelocDefault; + + switch (build_options.optimization_level) + { + case OPTIMIZATION_NOT_SET: + UNREACHABLE; + case OPTIMIZATION_AGGRESSIVE: + level = LLVMCodeGenLevelAggressive; + break; + case OPTIMIZATION_DEFAULT: + level = LLVMCodeGenLevelDefault; + break; + case OPTIMIZATION_LESS: + level = LLVMCodeGenLevelLess; + break; + case OPTIMIZATION_NONE: + level = LLVMCodeGenLevelNone; + break; + default: + UNREACHABLE; + } +// reloc = (opt->pic || opt->library)? LLVMRelocPIC : LLVMRelocDefault; + if (!build_options.cpu) + { + build_options.cpu = "generic"; + } + /* + if (!opt->features) + { + opt->features = ""; + }*/ + if (!(target.machine = LLVMCreateTargetMachine(target.target, build_options.target, build_options.cpu, "", level, reloc_mode, + LLVMCodeModelDefault))) { + error_exit("Failed to create target machine."); + } + + // The below is broken for the AMDGPU target. + target.alloca_address_space = 0; + target.data_layout = LLVMCreateTargetDataLayout(target.machine); + build_options.pointer_size = (int)LLVMPointerSize(target.data_layout); + DEBUG_LOG("Deduced pointer size to be %d bits", build_options.pointer_size * 8); +} + +void target_destroy() +{ + assert(target.machine); + LLVMDisposeTargetMachine(target.machine); +} + +void *target_data_layout() +{ + return target.data_layout; +} \ No newline at end of file diff --git a/src/compiler/types.c b/src/compiler/types.c index 3bb912d70..75aad68f8 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -68,6 +68,7 @@ const char *type_to_error_string(Type *type) case TYPE_F32: case TYPE_F64: case TYPE_FXX: + case TYPE_FUNC: return type->name_loc.string; case TYPE_POINTER: asprintf(&buffer, "%s*", type_to_error_string(type->base)); @@ -95,6 +96,101 @@ const char *type_to_error_string(Type *type) } } +static void type_append_signature_name_user_defined(Decl *decl, char *dst, size_t *offset) +{ + switch (decl->decl_kind) + { + case DECL_FUNC: + { + assert(decl->func.function_signature.mangled_signature); + int len = sprintf(dst + *offset, "func %s", decl->func.function_signature.mangled_signature); + *offset += len; + return; + } + case DECL_POISONED: + case DECL_VAR: + case DECL_ENUM_CONSTANT: + case DECL_TYPEDEF: + case DECL_ERROR_CONSTANT: + case DECL_ARRAY_VALUE: + case DECL_IMPORT: + case DECL_MACRO: + case DECL_MULTI_DECL: + case DECL_GENERIC: + case DECL_CT_IF: + case DECL_CT_ELSE: + case DECL_CT_ELIF: + case DECL_ATTRIBUTE: + UNREACHABLE + case DECL_STRUCT: + case DECL_UNION: + case DECL_ENUM: + case DECL_ERROR: + memcpy(dst + *offset, decl->name.string, decl->name.span.length); + *offset += decl->name.span.length; + return; + } + UNREACHABLE +} +void type_append_signature_name(Type *type, char *dst, size_t *offset) +{ + assert(*offset < 2000); + assert(type->resolve_status == RESOLVE_DONE && type->canonical && type_ok(type)); + Type *canonical_type = type->canonical; + switch (type->type_kind) + { + case TYPE_POISONED: + UNREACHABLE + case TYPE_USER_DEFINED: + type_append_signature_name_user_defined(canonical_type->decl, dst, offset); + return; + case TYPE_VOID: + case TYPE_BOOL: + case TYPE_I8: + case TYPE_I16: + case TYPE_I32: + case TYPE_I64: + case TYPE_IXX: + case TYPE_U8: + case TYPE_U16: + case TYPE_U32: + case TYPE_U64: + case TYPE_UXX: + case TYPE_F32: + case TYPE_F64: + case TYPE_FXX: + case TYPE_FUNC: + memcpy(dst + *offset, type->name_loc.string, type->name_loc.span.length); + *offset += type->name_loc.span.length; + return; + case TYPE_POINTER: + type_append_signature_name(type->base, dst, offset); + return; + case TYPE_STRING: + TODO + return; + case TYPE_ARRAY: + type_append_signature_name(type->base, dst, offset); + { + int len = sprintf(dst + *offset, "[%zu]", type->len); + *offset += len; + } + return; + case TYPE_VARARRAY: + type_append_signature_name(type->base, dst, offset); + dst[*offset++] = '['; + dst[*offset] = ']'; + *offset += 1; + return; + case TYPE_INC_ARRAY: + TODO + return; + case TYPE_EXPRESSION: + UNREACHABLE + } +} + + Type *type_new(TypeKind type_kind) { Type *type = malloc_arena(sizeof(Type)); @@ -103,6 +199,7 @@ Type *type_new(TypeKind type_kind) return type; } + size_t type_size(Type *canonical) { assert(canonical && canonical->canonical == canonical); @@ -132,6 +229,7 @@ size_t type_size(Type *canonical) case TYPE_UXX: case TYPE_FXX: return 8; + case TYPE_FUNC: case TYPE_POINTER: case TYPE_VARARRAY: case TYPE_STRING: @@ -215,7 +313,7 @@ Type *type_get_canonical_array(Type *arr_type) return canonical; } -static void create_type(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bytesize, unsigned bitsize) +static void type_create(const char *name, Type *location, Type **ptr, TypeKind kind, unsigned bytesize, unsigned bitsize) { *location = (Type) { .type_kind = kind, .resolve_status = RESOLVE_DONE, .builtin.bytesize = bytesize, .builtin.bitsize = bitsize }; location->name_loc.string = name; @@ -236,11 +334,11 @@ static void type_create_alias(const char *name, Type *location, Type **ptr, Type void builtin_setup() { - create_type("void", &t_u0, &type_void, TYPE_VOID, 1, 8); - create_type("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8); + type_create("void", &t_u0, &type_void, TYPE_VOID, 1, 8); + type_create("string", &t_str, &type_string, TYPE_STRING, build_options.pointer_size, build_options.pointer_size * 8); create_ptr_live_canonical(type_void); type_void->ptr_like_canonical[0] = &t_voidstar; - create_type("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0); + type_create("void*", &t_voidstar, &type_voidptr, TYPE_POINTER, 0, 0); t_voidstar.base = type_void; /*TODO @@ -249,7 +347,7 @@ void builtin_setup() type_string.type_kind = TYPE_STRING; */ #define DEF_TYPE(_name, _shortname, _type, _bits) \ -create_type(#_name, &_shortname, &type_ ## _name, _type, (_bits + 7) / 8, _bits); +type_create(#_name, &_shortname, &type_ ## _name, _type, (_bits + 7) / 8, _bits); DEF_TYPE(compint, t_ixx, TYPE_IXX, 64); DEF_TYPE(compuint, t_uxx, TYPE_UXX, 64); diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 94e843428..6d8aa6e17 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -98,8 +98,36 @@ void test_compiler(void) compiler_init(); } +void test_file(void) +{ + File file; + memset(&file, 0, sizeof(file)); + file.start_id = 3; + VECADD(file.line_start, file.start_id); + TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 1, "Expected first line"); + source_file_append_line_end(&file, 9); + TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line"); + source_file_append_line_end(&file, 19); + TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 15).line == 2, "Expected second line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 21).line == 3, "Expected third line"); + source_file_append_line_end(&file, 29); + TEST_ASSERT(source_file_find_position_in_file(&file, 3).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 5).line == 1, "Expected first line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 10).line == 2, "Expected second line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 15).line == 2, "Expected second line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 21).line == 3, "Expected third line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 25).line == 3, "Expected third line"); + TEST_ASSERT(source_file_find_position_in_file(&file, 31).line == 4, "Expected fourth line"); +} void compiler_tests(void) { + test_file(); test_lexer(); test_compiler(); diff --git a/src/utils/file_utils.c b/src/utils/file_utils.c index 357d1008c..5e021ceb6 100644 --- a/src/utils/file_utils.c +++ b/src/utils/file_utils.c @@ -7,6 +7,7 @@ #include "lib.h" #include #include +#include const char* expand_path(const char* path) { @@ -83,3 +84,22 @@ char *read_file(const char *path, size_t *return_size) fclose(file); return buffer; } + +void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path) +{ + char path[256]; + size_t path_len = strlen(full_path); + if (path_len > 255) error_exit("Path %s too long.", full_path); + + strncpy(path, full_path, path_len + 1); + const char *base_name = basename(path); + size_t filename_len = strlen(base_name); + *filename = malloc(filename_len + 1); + strncpy(*filename, base_name, filename_len + 1); + + strncpy(path, full_path, path_len + 1); + const char *dir = dirname(path); + size_t dir_len = strlen(dir); + *dir_path = malloc(dir_len + 1); + strncpy(*dir_path, dir, dir_len + 1); +} diff --git a/src/utils/lib.h b/src/utils/lib.h index 6b4a850d7..f83c88ec2 100644 --- a/src/utils/lib.h +++ b/src/utils/lib.h @@ -4,12 +4,12 @@ // Use of this source code is governed by a BSD-style // license that can be found in the LICENSE file. -#include -#include +#include "common.h" const char* expand_path(const char* path); char *read_file(const char *path, size_t *return_size); int filename_to_module(const char *path, char buffer[MAX_IDENTIFIER_LENGTH + 1]); +void path_get_dir_and_filename_from_full(const char *full_path, char **filename, char **dir_path); void init_arena(void); void *malloc_arena(unsigned long mem); void free_arena(void); @@ -279,3 +279,6 @@ static inline bool is_all_lower(const char* string) } return true; } + +char *strformat(const char *var, ...) __printflike(1, 2); + diff --git a/src/utils/stringutils.c b/src/utils/stringutils.c new file mode 100644 index 000000000..e60b6fe75 --- /dev/null +++ b/src/utils/stringutils.c @@ -0,0 +1,24 @@ +// Copyright (c) 2019 Christoffer Lerno. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +#include +#include +#include +#include "lib.h" +#include "stdio.h" + +char *strformat(const char *var, ...) +{ + va_list list; + va_start(list, var); + int len = vsnprintf(NULL, 0, var, list); + va_end(list); + if (len == 0) return ""; + va_start(list, var); + char *buffer = malloc_arena(len + 1); + int new_len = vsnprintf(buffer, len + 1, var, list); + va_end(list); + assert(len == new_len); + return buffer; +} \ No newline at end of file