From 4210f1ccb224d411226c686f2d301b614fd189fc Mon Sep 17 00:00:00 2001 From: Christoffer Lerno Date: Wed, 24 Mar 2021 19:41:28 +0100 Subject: [PATCH] Some work on vararray. Fix span on post unary. Updated error recovery. Updated return analysis to handle void functions. "while" now disallows error prone next line statement. Working on auto linking. Failable load optimized. Optimization and corrections for call returns + documentation. --- CMakeLists.txt | 1 + resources/examples/notworking/vector.c3 | 2 +- resources/testproject/bar.c3 | 2 +- resources/testproject/project.toml | 3 +- src/build/build_internal.h | 4 +- src/build/build_options.c | 255 ++++------- src/build/build_options.h | 84 +++- src/build/builder.c | 135 +++++- src/build/project.c | 100 ++++ src/build/project_creation.c | 30 +- src/build/project_creation.h | 4 +- src/compiler/ast.c | 3 +- src/compiler/compiler.c | 62 ++- src/compiler/compiler.h | 3 +- src/compiler/compiler_internal.h | 26 +- src/compiler/context.c | 3 + src/compiler/diagnostics.c | 5 +- src/compiler/headers.c | 12 +- src/compiler/linker.c | 31 ++ src/compiler/llvm_codegen.c | 74 +-- src/compiler/llvm_codegen_debug_info.c | 64 ++- src/compiler/llvm_codegen_expr.c | 431 ++++++++++++------ src/compiler/llvm_codegen_function.c | 2 +- src/compiler/llvm_codegen_internal.h | 8 +- src/compiler/llvm_codegen_module.c | 68 +-- src/compiler/llvm_codegen_stmt.c | 11 +- src/compiler/llvm_codegen_type.c | 16 +- src/compiler/parse_expr.c | 10 + src/compiler/parse_global.c | 105 ++++- src/compiler/parse_stmt.c | 26 +- src/compiler/parser_internal.h | 4 +- src/compiler/sema_casts.c | 42 +- src/compiler/sema_decls.c | 69 ++- src/compiler/sema_expr.c | 64 ++- src/compiler/sema_passes.c | 4 + src/compiler/sema_stmts.c | 233 +++++++--- src/compiler/target.c | 36 +- src/compiler/target.h | 1 + src/compiler/types.c | 21 +- src/compiler_tests/tests.c | 2 + src/main.c | 24 +- test/test_suite/define/common.c3 | 109 +++++ test/test_suite/errors/rethrow.c3t | 44 +- test/test_suite/examples/gameoflife.c3 | 90 ++++ test/test_suite/functions/returning_void.c3t | 20 + test/test_suite/macros/generic_common.c3 | 41 ++ .../statements/foreach_with_error.c3t | 44 +- .../statements/while_statement_placement.c3 | 11 + test/test_suite/variables/consts.c3 | 7 + 49 files changed, 1733 insertions(+), 713 deletions(-) create mode 100644 src/compiler/linker.c create mode 100644 test/test_suite/define/common.c3 create mode 100644 test/test_suite/examples/gameoflife.c3 create mode 100644 test/test_suite/functions/returning_void.c3t create mode 100644 test/test_suite/macros/generic_common.c3 create mode 100644 test/test_suite/statements/while_statement_placement.c3 create mode 100644 test/test_suite/variables/consts.c3 diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a8cff96a..ea68e1294 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -103,6 +103,7 @@ add_executable(c3c src/compiler/sema_types.c src/compiler/sema_stmts.c src/compiler/number.c + src/compiler/linker.c src/utils/vmem.c src/utils/vmem.h src/utils/whereami.c diff --git a/resources/examples/notworking/vector.c3 b/resources/examples/notworking/vector.c3 index 4e8eadc4b..56ee0c063 100644 --- a/resources/examples/notworking/vector.c3 +++ b/resources/examples/notworking/vector.c3 @@ -2,7 +2,7 @@ module vector(Type); public struct Vector { - Type[*] array; + Type[] array; } public func void Vector.init() diff --git a/resources/testproject/bar.c3 b/resources/testproject/bar.c3 index 5721cc930..3ced5c41f 100644 --- a/resources/testproject/bar.c3 +++ b/resources/testproject/bar.c3 @@ -1,6 +1,6 @@ module bar; -import baz.foo; +import baz::foo; import testbaz; public func void test() diff --git a/resources/testproject/project.toml b/resources/testproject/project.toml index 56c022685..5aa916419 100644 --- a/resources/testproject/project.toml +++ b/resources/testproject/project.toml @@ -7,4 +7,5 @@ warnings = ["no-unused"] # sources compiled sources = ["./**"] # libraries to use -libs = [] \ No newline at end of file +libs = [] +soft-float = true \ No newline at end of file diff --git a/src/build/build_internal.h b/src/build/build_internal.h index d43596fe7..a85a81e26 100644 --- a/src/build/build_internal.h +++ b/src/build/build_internal.h @@ -7,6 +7,7 @@ #include "utils/lib.h" #include "utils/toml.h" #include "build_options.h" +#define DEFAULT_SYMTAB_SIZE (64 * 1024) typedef struct { @@ -14,4 +15,5 @@ typedef struct } Project; Project *project_load(void); -BuildTarget *project_select_target(Project *project, const char *optional_target); \ No newline at end of file +BuildTarget *project_select_target(Project *project, const char *optional_target); + diff --git a/src/build/build_options.c b/src/build/build_options.c index 8651888e8..4e09a6e98 100644 --- a/src/build/build_options.c +++ b/src/build/build_options.c @@ -11,10 +11,17 @@ #include #include -static const int DEFAULT_SYMTAB_SIZE = 64 * 1024; -static const int MAX_SYMTAB_SIZE = 1024 * 1024; -static char *arch_os_target[ARCH_OS_TARGET_LAST + 1] = { + + + + +static int arg_index; +static int arg_count; +static const char** args; +static const char* current_arg; + +char *arch_os_target[ARCH_OS_TARGET_LAST + 1] = { [X86_FREEBSD] = "x86_freebsd", [X86_OPENBSD] = "x86_openbsd", [X86_MCU] = "x86_mcu", @@ -33,13 +40,6 @@ static char *arch_os_target[ARCH_OS_TARGET_LAST + 1] = { [WASM64] = "wasm64", }; -BuildOptions build_options; -static int arg_index; -static int arg_count; -static const char** args; -static const char* current_arg; - - #define EOUTPUT(string, ...) fprintf(stderr, string "\n", ##__VA_ARGS__) #define OUTPUT(string, ...) fprintf(stdout, string "\n", ##__VA_ARGS__) #define FAIL_WITH_ERR(string, ...) do { fprintf(stderr, "Error: " string "\n\n", ##__VA_ARGS__); usage(); exit(EXIT_FAILURE); } while (0) @@ -139,14 +139,14 @@ static inline bool match_shortopt(const char* name) } -void append_file() +void append_file(BuildOptions *build_options) { - if (vec_size(build_options.files) == MAX_FILES) + if (vec_size(build_options->files) == MAX_FILES) { fprintf(stderr, "Max %d files may be specified\n", MAX_FILES); exit(EXIT_FAILURE); } - build_options.files = VECADD(build_options.files, current_arg); + build_options->files = VECADD(build_options->files, current_arg); } static bool arg_match(const char *candidate) @@ -154,87 +154,86 @@ static bool arg_match(const char *candidate) return strcmp(current_arg, candidate) == 0; } -static void parse_optional_target() +static void parse_optional_target(BuildOptions *options) { if (at_end() || next_is_opt()) { - build_options.target_select = NULL; + options->target_select = NULL; } else { - build_options.target_select = next_arg(); + options->target_select = next_arg(); } } -static void parse_command(void) +static void parse_command(BuildOptions *options) { if (arg_match("init")) { - build_options.command = COMMAND_INIT; + options->command = COMMAND_INIT; if (at_end() || next_is_opt()) error_exit("Expected a project name after init"); - build_options.project_name = next_arg(); + options->project_name = next_arg(); return; } if (arg_match("utest")) { - build_options.command = COMMAND_UNIT_TEST; + options->command = COMMAND_UNIT_TEST; return; } if (arg_match("compile")) { - build_options.command = COMMAND_COMPILE; + options->command = COMMAND_COMPILE; return; } if (arg_match("headers")) { - build_options.command = COMMAND_GENERATE_HEADERS; + options->command = COMMAND_GENERATE_HEADERS; return; } if (arg_match("build")) { - build_options.command = COMMAND_BUILD; - parse_optional_target(); + options->command = COMMAND_BUILD; + parse_optional_target(options); return; } if (arg_match("run")) { - build_options.command = COMMAND_RUN; - parse_optional_target(); + options->command = COMMAND_RUN; + parse_optional_target(options); return; } if (arg_match("compile-run")) { - build_options.command = COMMAND_COMPILE_RUN; - parse_optional_target(); + options->command = COMMAND_COMPILE_RUN; return; } if (arg_match("clean-run")) { - build_options.command = COMMAND_CLEAN_RUN; - parse_optional_target(); + options->command = COMMAND_CLEAN_RUN; + parse_optional_target(options); return; } if (arg_match("clean")) { - build_options.command = COMMAND_CLEAN; + options->command = COMMAND_CLEAN; return; } if (arg_match("dist")) { - build_options.command = COMMAND_CLEAN_RUN; - parse_optional_target(); + options->command = COMMAND_CLEAN_RUN; + parse_optional_target(options); return; } if (arg_match("directives")) { - build_options.command = COMMAND_DOCS; - parse_optional_target(); + options->command = COMMAND_DOCS; + parse_optional_target(options); return; } if (arg_match("bench")) { - build_options.command = COMMAND_BENCH; - parse_optional_target(); + options->command = COMMAND_BENCH; + parse_optional_target(options); return; } FAIL_WITH_ERR("Cannot process the unknown command \"%s\".", current_arg); @@ -242,126 +241,63 @@ static void parse_command(void) static void print_all_targets(void) { OUTPUT("Available targets:"); - for (unsigned i = 0; i <= ARCH_OS_TARGET_LAST; i++) + for (unsigned i = 1; i <= ARCH_OS_TARGET_LAST; i++) { OUTPUT(" %s", arch_os_target[i]); } } -static void parse_option(void) +static void parse_option(BuildOptions *options) { switch (current_arg[1]) { case 'g': if (match_shortopt("gline-tables-only")) { - build_options.debug_info = DEBUG_INFO_LINE_TABLES; + options->debug_info_override = DEBUG_INFO_LINE_TABLES; FATAL_ERROR("Line tables only are currently not available"); } if (match_shortopt("g") || match_shortopt("g1")) { - build_options.debug_info = DEBUG_INFO_FULL; + options->debug_info_override = DEBUG_INFO_FULL; return; } if (match_shortopt("g0")) { - build_options.debug_info = DEBUG_INFO_NONE; + options->debug_info_override = DEBUG_INFO_NONE; return; } FAIL_WITH_ERR("Unknown debug argument -%s.", ¤t_arg[1]); case 'h': break; - case 'f': - if (match_shortopt("freg-struct-return")) - { - build_options.feature.reg_struct_return = true; - return; - } - if (match_shortopt("fpie")) - { - build_options.pie = PIE_SMALL; - return; - } - if (match_shortopt("fPIE")) - { - build_options.pie = PIE_BIG; - return; - } - if (match_shortopt("fno-pie")) - { - build_options.pie = PIE_NONE; - return; - } - if (match_shortopt("fpic")) - { - build_options.pic = PIC_SMALL; - return; - } - if (match_shortopt("fPIC")) - { - build_options.pic = PIC_BIG; - return; - } - if (match_shortopt("fno-pic")) - { - build_options.pic = PIC_NONE; - return; - } - if (match_shortopt("fpcc-struct-return")) - { - build_options.feature.stack_struct_return = true; - return; - } - if (match_shortopt("fno-memcpy-pass")) - { - build_options.feature.no_memcpy_pass = true; - return; - } - FAIL_WITH_ERR("Unknown argument -%s.", ¤t_arg[1]); - case 'm': - if (match_shortopt("msoft-float")) - { - build_options.feature.soft_float = true; - build_options.feature.no_soft_float = false; - return; - } - if (match_shortopt("mno-soft-float")) - { - build_options.feature.soft_float = true; - build_options.feature.no_soft_float = false; - return; - } - FAIL_WITH_ERR("Cannot process the unknown command \"%s\".", current_arg); case 'O': - if (build_options.optimization_level != OPTIMIZATION_NOT_SET) + if (options->optimization_setting_override != OPT_SETTING_NOT_SET) { FAIL_WITH_ERR("Multiple optimization levels were set."); } if (match_shortopt("O0")) { - build_options.optimization_level = OPTIMIZATION_NONE; + options->optimization_setting_override = OPT_SETTING_O0; } else if (match_shortopt("O1")) { - build_options.optimization_level = OPTIMIZATION_LESS; + options->optimization_setting_override = OPT_SETTING_O1; } else if (match_shortopt("O2")) { - build_options.optimization_level = OPTIMIZATION_DEFAULT; + options->optimization_setting_override = OPT_SETTING_O2; + } + else if (match_shortopt("O2")) + { + options->optimization_setting_override = OPT_SETTING_O3; } else if (match_shortopt("Os")) { - build_options.optimization_level = OPTIMIZATION_DEFAULT; - build_options.size_optimization_level = SIZE_OPTIMIZATION_SMALL; + options->optimization_setting_override = OPT_SETTING_OSMALL; } else if (match_shortopt("Oz")) { - build_options.optimization_level = OPTIMIZATION_DEFAULT; - build_options.size_optimization_level = SIZE_OPTIMIZATION_TINY; - } - else if (match_shortopt("O3")) - { - build_options.optimization_level = OPTIMIZATION_AGGRESSIVE; + options->optimization_setting_override = OPT_SETTING_OTINY; } else { @@ -369,18 +305,18 @@ static void parse_option(void) } return; case 'E': - if (build_options.compile_option != COMPILE_NORMAL) + if (options->compile_option != COMPILE_NORMAL) { FAIL_WITH_ERR("Illegal combination of compile options."); } - build_options.compile_option = COMPILE_LEX_ONLY; + options->compile_option = COMPILE_LEX_ONLY; return; case 'P': - if (build_options.compile_option != COMPILE_NORMAL) + if (options->compile_option != COMPILE_NORMAL) { FAIL_WITH_ERR("Illegal combination of compile options."); } - build_options.compile_option = COMPILE_LEX_PARSE_ONLY; + options->compile_option = COMPILE_LEX_PARSE_ONLY; return; case '-': if (match_longopt("about")) @@ -393,13 +329,11 @@ static void parse_option(void) { if (at_end() || next_is_opt()) error_exit("error: --target needs a arch+os definition."); const char *target = next_arg(); - for (unsigned i = 1; i <= ARCH_OS_TARGET_LAST; i++) + ArchOsTarget target_arch_os = arch_os_target_from_string(target); + if (target_arch_os >= 0) { - if (strcasecmp(arch_os_target[i], target) == 0) - { - build_options.arch_os_target = i; - return; - } + options->arch_os_target_override = target_arch_os; + return; } OUTPUT("Available targets:"); EOUTPUT("Invalid target %s.", target); @@ -422,35 +356,25 @@ static void parse_option(void) } if (match_longopt("emit-llvm")) { - build_options.emit_llvm = true; + options->emit_llvm = true; return; } if (match_longopt("lib")) { if (at_end() || next_is_opt()) error_exit("error: --lib needs a directory."); - if (build_options.lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS); - build_options.lib_dir[build_options.lib_count++] = check_dir(next_arg()); + if (options->lib_count == MAX_LIB_DIRS) error_exit("Max %d libraries may be specified.", MAX_LIB_DIRS); + options->lib_dir[options->lib_count++] = check_dir(next_arg()); return; } if (match_longopt("test")) { - build_options.test_mode = true; + options->test_mode = true; return; } if (match_longopt("path")) { if (at_end() || next_is_opt()) error_exit("error: --path needs a directory."); - build_options.path = check_dir(next_arg()); - return; - } - if (match_longopt("symtab")) - { - if (at_end() || next_is_opt()) error_exit("error: --symtab needs a number."); - const char *number = next_arg(); - int size = atoi(number); // NOLINT(cert-err34-c) - if (size < 1024) error_exit("error: --symtab valid size > 1024."); - if (size > MAX_SYMTAB_SIZE) error_exit("error: --symptab size cannot exceed %d", MAX_SYMTAB_SIZE); - build_options.symtab_size = size; + options->path = check_dir(next_arg()); return; } if (match_longopt("help")) @@ -466,7 +390,7 @@ static void parse_option(void) } -void parse_arguments(int argc, const char *argv[]) +BuildOptions parse_arguments(int argc, const char *argv[]) { arg_count = argc; args = argv; @@ -477,16 +401,16 @@ void parse_arguments(int argc, const char *argv[]) exit(EXIT_SUCCESS); } - build_options.path = "."; - build_options.emit_llvm = false; - build_options.emit_bitcode = true; - build_options.optimization_level = OPTIMIZATION_NOT_SET; - build_options.size_optimization_level = SIZE_OPTIMIZATION_NOT_SET; - build_options.debug_info = DEBUG_INFO_NONE; - build_options.debug_mode = false; - build_options.command = COMMAND_MISSING; - build_options.symtab_size = DEFAULT_SYMTAB_SIZE; - build_options.files = NULL; + BuildOptions build_options = { + .path = ".", + .emit_llvm = false, + .emit_bitcode = true, + .optimization_setting_override = OPT_SETTING_NOT_SET, + .debug_info_override = DEBUG_INFO_NOT_SET, + .debug_mode = false, + .command = COMMAND_MISSING, + .files = NULL + }; for (int i = DIAG_NONE; i < DIAG_WARNING_TYPE; i++) { build_options.severity[i] = DIAG_IGNORE; @@ -505,19 +429,19 @@ void parse_arguments(int argc, const char *argv[]) current_arg = args[arg_index]; if (current_arg[0] == '-') { - parse_option(); + parse_option(&build_options); continue; } if (build_options.command == COMMAND_MISSING) { - parse_command(); + parse_command(&build_options); continue; } if (build_options.command == COMMAND_COMPILE_RUN || build_options.command == COMMAND_COMPILE || build_options.command == COMMAND_GENERATE_HEADERS) { - append_file(); + append_file(&build_options); continue; } FAIL_WITH_ERR("Found the unexpected argument \"%s\".", current_arg); @@ -526,13 +450,18 @@ 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; - } - if (build_options.size_optimization_level == SIZE_OPTIMIZATION_NOT_SET) - { - build_options.size_optimization_level = SIZE_OPTIMIZATION_NONE; - } - + return build_options; } + + +ArchOsTarget arch_os_target_from_string(const char *target) +{ + for (unsigned i = 1; i <= ARCH_OS_TARGET_LAST; i++) + { + if (strcasecmp(arch_os_target[i], target) == 0) + { + return i; + } + } + return -1; +} \ No newline at end of file diff --git a/src/build/build_options.h b/src/build/build_options.h index c8ab93a72..1e8f0e940 100644 --- a/src/build/build_options.h +++ b/src/build/build_options.h @@ -71,6 +71,17 @@ typedef enum COMPILE_OUTPUT_AST, } CompileOption; +typedef enum +{ + OPT_SETTING_NOT_SET = -1, + OPT_SETTING_O0 = 0, + OPT_SETTING_O1 = 1, + OPT_SETTING_O2 = 2, + OPT_SETTING_O3 = 3, + OPT_SETTING_OSMALL = 4, + OPT_SETTING_OTINY = 5, +} OptimizationSetting; + typedef enum { OPTIMIZATION_NOT_SET = -1, @@ -100,14 +111,29 @@ typedef enum // Values correspond to LLVM values typedef enum { - PIE_NONE = -1, - PIE_DEFAULT = 0, + PIE_DEFAULT = -1, + PIE_NONE = 0, PIE_SMALL = 1, PIE_BIG = 2, } PieGeneration; typedef enum { + SOFT_FLOAT_DEFAULT = -1, + SOFT_FLOAT_NONE = 0, + SOFT_FLOAT_YES = 1 +} SoftFloat; + +typedef enum +{ + STRUCT_RETURN_DEFAULT = -1, + STRUCT_RETURN_STACK = 0, + STRUCT_RETURN_REG = 1 +} StructReturn; + +typedef enum +{ + DEBUG_INFO_NOT_SET = -1, DEBUG_INFO_NONE, DEBUG_INFO_LINE_TABLES, DEBUG_INFO_FULL @@ -135,46 +161,31 @@ typedef enum ARCH_OS_TARGET_LAST = WASM64 } ArchOsTarget; -typedef struct +typedef struct BuildOptions_ { const char* lib_dir[MAX_LIB_DIRS]; int lib_count; const char** files; const char* project_name; - ArchOsTarget arch_os_target; const char* target_select; const char* path; - const char* cpu; - const char* target_triple; - PicGeneration pic; - PieGeneration pie; - bool generate_lib; - struct - { - bool reg_struct_return : 1; - bool stack_struct_return : 1; - bool no_memcpy_pass : 1; - bool soft_float : 1; - bool no_soft_float : 1; - } feature; unsigned version; CompilerCommand command; - uint32_t symtab_size; CompileOption compile_option; DiagnosticsSeverity severity[DIAG_END_SENTINEL]; - OptimizationLevel optimization_level; - SizeOptimizationLevel size_optimization_level; - DebugInfo debug_info; + OptimizationSetting optimization_setting_override; + DebugInfo debug_info_override; + ArchOsTarget arch_os_target_override; bool debug_mode; bool emit_llvm; bool emit_bitcode; bool test_mode; - bool trap_wrapping; } BuildOptions; + typedef enum { TARGET_TYPE_EXECUTABLE, @@ -191,9 +202,32 @@ typedef struct const char *langrev; const char **sources; const char **libraries; - const char *target_triple; + const char *cpu; + bool run_after_compile : 1; + bool test_output : 1; + bool output_headers : 1; + bool output_ast : 1; + bool lex_only : 1; + bool parse_only : 1; + bool emit_llvm : 1; + bool emit_object_files : 1; + OptimizationLevel optimization_level; + SizeOptimizationLevel size_optimization_level; + DebugInfo debug_info; + PieGeneration pie; + PicGeneration pic; + ArchOsTarget arch_os_target; + uint32_t symtab_size; + struct + { + SoftFloat soft_float : 3; + StructReturn struct_return : 3; + bool no_memcpy_pass : 1; + bool trap_on_wrap : 1; + bool safe_mode : 1; + } feature; } BuildTarget; -extern BuildOptions build_options; -void parse_arguments(int argc, const char *argv[]); +BuildOptions parse_arguments(int argc, const char *argv[]); +ArchOsTarget arch_os_target_from_string(const char *target); \ No newline at end of file diff --git a/src/build/builder.c b/src/build/builder.c index 5301b1c90..1d13febaf 100644 --- a/src/build/builder.c +++ b/src/build/builder.c @@ -6,23 +6,144 @@ #include "build_internal.h" #include "build_options.h" + void load_library_files(void) {} void load_files(void) {} void compile_files(BuildTarget *target); -void build(void) +void build(const char *optional_target) { // Locate the project.toml file_find_top_dir(); // Parse it Project *project = project_load(); - BuildTarget *target = project_select_target(project, build_options.target_select); - - if (!target->target_triple && build_options.target_triple) - { - target->target_triple = build_options.target_triple; - } + BuildTarget *target = project_select_target(project, optional_target); load_library_files(); compile_files(target); +} + +static void update_build_target_from_options(BuildTarget *target, BuildOptions *options) +{ + switch (options->command) + { + case COMMAND_RUN: + case COMMAND_COMPILE_RUN: + case COMMAND_CLEAN_RUN: + target->run_after_compile = true; + break; + default: + target->run_after_compile = false; + break; + } + + switch (options->command) + { + case COMMAND_BUILD: + target->output_headers = target->type == TARGET_TYPE_DYNAMIC_LIB || target->type == TARGET_TYPE_STATIC_LIB; + break; + case COMMAND_GENERATE_HEADERS: + target->output_headers = true; + break; + default: + target->output_headers = false; + break; + } + + // Copy optimization levels. + switch (options->optimization_setting_override) + { + case OPT_SETTING_O0: + target->optimization_level = OPTIMIZATION_NONE; + target->size_optimization_level = SIZE_OPTIMIZATION_NONE; + break; + case OPT_SETTING_O1: + target->optimization_level = OPTIMIZATION_LESS; + target->size_optimization_level = SIZE_OPTIMIZATION_NONE; + break; + case OPT_SETTING_O2: + target->optimization_level = OPTIMIZATION_DEFAULT; + target->size_optimization_level = SIZE_OPTIMIZATION_NONE; + break; + case OPT_SETTING_O3: + target->optimization_level = OPTIMIZATION_AGGRESSIVE; + target->size_optimization_level = SIZE_OPTIMIZATION_NONE; + break; + case OPT_SETTING_OSMALL: + target->optimization_level = OPTIMIZATION_DEFAULT; + target->size_optimization_level = SIZE_OPTIMIZATION_SMALL; + break; + case OPT_SETTING_OTINY: + target->optimization_level = OPTIMIZATION_DEFAULT; + target->size_optimization_level = SIZE_OPTIMIZATION_TINY; + break; + case OPT_SETTING_NOT_SET: + break; + default: + UNREACHABLE + } + if (options->debug_info_override != DEBUG_INFO_NOT_SET) + { + target->debug_info = options->debug_info_override; + } + if (options->arch_os_target_override != ARCH_OS_TARGET_DEFAULT) + { + target->arch_os_target = options->arch_os_target_override; + } + target->emit_llvm = options->emit_llvm; + switch (options->compile_option) + { + case COMPILE_NORMAL: + target->emit_object_files = true; + break; + case COMPILE_LEX_ONLY: + target->lex_only = true; + break; + case COMPILE_LEX_PARSE_ONLY: + target->parse_only = true; + break; + case COMPILE_OUTPUT_HEADERS: + target->output_headers = true; + target->run_after_compile = false; + target->emit_object_files = false; + break; + case COMPILE_OUTPUT_AST: + target->parse_only = true; + target->output_ast = true; + break; + } + if (options->test_mode) + { + target->test_output = true; + target->emit_llvm = false; + target->emit_object_files = false; + } +} + +void init_default_build_target(BuildTarget *target, BuildOptions *options, const char *name) +{ + *target = (BuildTarget) { + .type = TARGET_TYPE_EXECUTABLE, + .sources = options->files, + .name = name, + .optimization_level = OPTIMIZATION_DEFAULT, + .size_optimization_level = SIZE_OPTIMIZATION_NONE, + .symtab_size = DEFAULT_SYMTAB_SIZE, + .debug_info = DEBUG_INFO_NONE, + .arch_os_target = ARCH_OS_TARGET_DEFAULT + }; + update_build_target_from_options(target, options); +} + +void init_build_target(BuildTarget *target, BuildOptions *options) +{ + // Locate the project.toml + file_find_top_dir(); + // Parse it + Project *project = project_load(); + *target = *project_select_target(project, options->target_select); + + update_build_target_from_options(target, options); + + load_library_files(); } \ No newline at end of file diff --git a/src/build/project.c b/src/build/project.c index b52335016..92ba58e14 100644 --- a/src/build/project.c +++ b/src/build/project.c @@ -4,6 +4,7 @@ #include #include "build_internal.h" +#define MAX_SYMTAB_SIZE (1024 * 1024) TomlArray *get_array(TomlTable *table, const char *key) { @@ -43,6 +44,55 @@ const char *get_valid_string(TomlTable *table, const char *key, const char *cate return copy_toml_string(value->value.string); } +int get_valid_bool(TomlTable *table, const char *key, const char *category, int default_val) +{ + TomlValue *value = toml_table_get(table, key); + if (!value) return default_val; + if (value->type != TOML_BOOLEAN) + { + error_exit("%s had an invalid mandatory '%s' field that was not a boolean, please correct it.", category, key); + } + return value->value.boolean; +} + + +static int get_valid_string_setting(TomlTable *table, const char *key, const char *category, const char** values, int first_result, int count, const char *expected) +{ + TomlValue *value = toml_table_get(table, key); + if (!value) + { + return -1; + } + if (value->type == TOML_STRING) + { + for (int i = 0; i < count; i++) + { + unsigned str_len = strlen(values[i]); + if (str_len != value->value.string->len) continue; + if (memcmp(values[i], value->value.string->str, str_len) == 0) return i + first_result; + } + } + error_exit("%s had an invalid value for '%s', expected %s", category, key, expected); +} + +long get_valid_integer(TomlTable *table, const char *key, const char *category, bool mandatory) +{ + TomlValue *value = toml_table_get(table, key); + if (!value) + { + if (mandatory) + { + error_exit("%s was missing a mandatory '%s' field, please add it.", category, key); + } + return -1; + } + if (value->type != TOML_INTEGER) + { + error_exit("%s had an invalid mandatory '%s' field that was not an integer, please correct it.", category, key); + } + return value->value.integer; +} + static const char **get_valid_array(TomlTable *table, const char *key, const char *category, bool mandatory) { @@ -79,6 +129,12 @@ void project_add_target(Project *project, TomlValue *wrapped_table, const char * error_exit("The %s had an invalid %s. Please check your [[%s]] configurations.", PROJECT_TOML, type, type_key); } BuildTarget *target = CALLOCS(BuildTarget); + target->optimization_level = OPTIMIZATION_DEFAULT; + target->size_optimization_level = SIZE_OPTIMIZATION_NONE; + target->arch_os_target = ARCH_OS_TARGET_DEFAULT; + target->debug_info = DEBUG_INFO_NONE; + target->symtab_size = DEFAULT_SYMTAB_SIZE; + vec_add(project->targets, target); TomlTable *table = wrapped_table->value.table; @@ -98,6 +154,50 @@ void project_add_target(Project *project, TomlValue *wrapped_table, const char * target->langrev = get_valid_string(table, "langrev", type, false); target->sources = get_valid_array(table, "sources", type, true); target->libraries = get_valid_array(table, "libs", type, false); + static const char *debug_infos[3] = { + [DEBUG_INFO_FULL] = "full", + [DEBUG_INFO_NONE] = "none", + [DEBUG_INFO_LINE_TABLES] = "line-tables" + }; + DebugInfo info = get_valid_string_setting(table, "debug-info", type, debug_infos, 0, 3, "one of 'full' 'line-table' or 'none'."); + if (info > -1) target->debug_info = info; + const char *arch_os_string = get_valid_string(table, "target", type, false); + if (arch_os_string) + { + ArchOsTarget arch_os = arch_os_target_from_string(arch_os_string); + if (arch_os < 0) error_exit("Error reading %s: %s target was not valid.", PROJECT_TOML, type); + target->arch_os_target = arch_os; + } + long symtab_size = get_valid_integer(table, "symtab", type, false); + if (symtab_size > 0) + { + if (symtab_size < 1024) + { + error_exit("Error reading %s: %s symtab was less than 1024.", PROJECT_TOML, type); + } + if (symtab_size > MAX_SYMTAB_SIZE) + { + error_exit("Error reading %s: %s symtab may not exceed %d.", PROJECT_TOML, type, MAX_SYMTAB_SIZE); + } + target->symtab_size = symtab_size; + } + const char *cpu = get_valid_string(table, "cpu", type, false); + target->cpu = cpu ? cpu : "generic"; + + static const char *pies[3] = { + [PIE_SMALL] = "yes-limited", + [PIE_NONE] = "no", + [PIE_BIG] = "yes-unlimited" + }; + target->pie = get_valid_string_setting(table, "pie", type, pies, 0, 3, "'yes-limited', 'yes-unlimited' or 'no'."); + target->pic = get_valid_string_setting(table, "pic", type, pies, 0, 3, "'yes-limited', 'yes-unlimited' or 'no'."); + + target->pic = PIC_DEFAULT; + target->feature.no_memcpy_pass = get_valid_bool(table, "no-memcpy-pass", type, false); + target->feature.trap_on_wrap = get_valid_bool(table, "trap-on-wrap", type, false); + // Use the fact that they correspond to 0, 1, -1 + target->feature.struct_return = get_valid_bool(table, "stack-struct-return", type, STRUCT_RETURN_DEFAULT); + target->feature.soft_float = get_valid_bool(table, "soft-float", type, SOFT_FLOAT_DEFAULT); } static void project_add_targets(Project *project, TomlTable *table, const char *type, const char *type_key, TargetType target_type) diff --git a/src/build/project_creation.c b/src/build/project_creation.c index db32d05fa..7c81f8b37 100644 --- a/src/build/project_creation.c +++ b/src/build/project_creation.c @@ -29,33 +29,33 @@ const char* TOML = "# libraries to use\n" "libs = [\"lib/**\"]\n"; -void create_project(void) +void create_project(BuildOptions *build_options) { for (int i = 0; ; i++) { - char c = build_options.project_name[i]; + char c = build_options->project_name[i]; if (c == '\0') break; if (!is_alphanum_(c)) { - fprintf(stderr, "'%s' is not a valid project name.\n", build_options.project_name); + fprintf(stderr, "'%s' is not a valid project name.\n", build_options->project_name); exit(EXIT_FAILURE); } } - if (chdir(build_options.path)) + if (chdir(build_options->path)) { - fprintf(stderr, "Can't open path %s\n", build_options.path); + fprintf(stderr, "Can't open path %s\n", build_options->path); exit(EXIT_FAILURE); } - int error = mkdir(build_options.project_name, 0755); + int error = mkdir(build_options->project_name, 0755); if (error) { - fprintf(stderr, "Could not create directory %s: %s\n", build_options.project_name, strerror(errno)); + fprintf(stderr, "Could not create directory %s: %s\n", build_options->project_name, strerror(errno)); exit(EXIT_FAILURE); } - if (chdir(build_options.project_name)) goto ERROR; + if (chdir(build_options->project_name)) goto ERROR; FILE *file = fopen("LICENCE", "a"); if (!file) goto ERROR; @@ -67,7 +67,7 @@ void create_project(void) file = fopen("project.toml", "a"); if (!file) goto ERROR; - fprintf(file, TOML, build_options.project_name); + fprintf(file, TOML, build_options->project_name); fclose(file); if (mkdir("lib", 0755)) goto ERROR; @@ -80,7 +80,7 @@ void create_project(void) chdir("test"); - if (mkdir(build_options.project_name, 0755)) goto ERROR; + if (mkdir(build_options->project_name, 0755)) goto ERROR; chdir(".."); @@ -106,9 +106,9 @@ void create_project(void) chdir("src"); - if (mkdir(build_options.project_name, 0755)) goto ERROR; + if (mkdir(build_options->project_name, 0755)) goto ERROR; - chdir(build_options.project_name); + chdir(build_options->project_name); file = fopen("main.c3", "a"); if (!file) goto ERROR; @@ -116,14 +116,14 @@ void create_project(void) chdir("../.."); - printf("Project '%s' created.\n", build_options.project_name); + printf("Project '%s' created.\n", build_options->project_name); exit(EXIT_SUCCESS); ERROR: fprintf(stderr, "Err: %s\n", strerror(errno)); printf("Something went wrong creating the project."); - chdir(build_options.path); - rmdir(build_options.project_name); + chdir(build_options->path); + rmdir(build_options->project_name); exit(EXIT_FAILURE); } diff --git a/src/build/project_creation.h b/src/build/project_creation.h index 5afcd5337..0854a319f 100644 --- a/src/build/project_creation.h +++ b/src/build/project_creation.h @@ -4,5 +4,5 @@ // Use of this source code is governed by the GNU LGPLv3.0 license // a copy of which can be found in the LICENSE file. - -void create_project(void); \ No newline at end of file +struct BuildOptions_; +void create_project(struct BuildOptions_ *build_options); \ No newline at end of file diff --git a/src/compiler/ast.c b/src/compiler/ast.c index 1391d9122..7fe0cfb40 100644 --- a/src/compiler/ast.c +++ b/src/compiler/ast.c @@ -518,7 +518,8 @@ void fprint_expr_recursive(Context *context, FILE *file, Expr *expr, int indent) DUMP("(enumconstant)"); return; case EXPR_TYPEINFO: - TODO; + DUMP("(typeinfo)"); + return; case EXPR_SLICE_ASSIGN: DUMP("(sliceassign"); DUMPEXPC(expr); diff --git a/src/compiler/compiler.c b/src/compiler/compiler.c index e22a1e389..8249fbcba 100644 --- a/src/compiler/compiler.c +++ b/src/compiler/compiler.c @@ -4,6 +4,7 @@ #include "compiler_internal.h" #include "../build/build_options.h" +#include Compiler compiler; @@ -64,7 +65,7 @@ void compiler_parse(BuildTarget *target) bool loaded = false; File *file = source_file_load(target->sources[i], &loaded); if (loaded) continue; - diag_reset(); + diag_setup(target->test_output); Context *context = context_create(file, target); parse_file(context); context_print_ast(context, stdout); @@ -75,7 +76,7 @@ void compiler_parse(BuildTarget *target) void compiler_compile(BuildTarget *target) { Context **contexts = NULL; - diag_reset(); + diag_setup(target->test_output); if (compiler.lib_dir) { vec_add(target->sources, strformat("%s/std/builtin.c3", compiler.lib_dir)); @@ -131,7 +132,7 @@ void compiler_compile(BuildTarget *target) } if (diagnostics.errors > 0) exit(EXIT_FAILURE); - if (build_options.command == COMMAND_GENERATE_HEADERS) + if (target->output_headers) { for (unsigned i = 0; i < source_count; i++) { @@ -144,7 +145,7 @@ void compiler_compile(BuildTarget *target) llvm_codegen_setup(); - void **gen_contexts = malloc(source_count * sizeof(void*)); + void **gen_contexts = malloc(source_count * sizeof(void *)); for (unsigned i = 0; i < source_count; i++) { Context *context = contexts[i]; @@ -169,13 +170,30 @@ void compiler_compile(BuildTarget *target) print_arena_status(); - free_arena(); + + bool create_exe = !target->test_output && (target->type == TARGET_TYPE_EXECUTABLE || target->type == TARGET_TYPE_TEST); +#if PLATFORM_WINDOWS + create_exe = false; +#endif + const char **obj_files = NULL; for (unsigned i = 0; i < source_count; i++) { - llvm_codegen(gen_contexts[i]); + const char *file_name = llvm_codegen(gen_contexts[i]); + assert(file_name || !create_exe); + vec_add(obj_files, file_name); } + if (create_exe) + { + linker(target->name, obj_files, source_count); + if (target->run_after_compile) + { + system(strformat("./%s", target->name)); + } + } + + free_arena(); exit(EXIT_SUCCESS); } @@ -220,30 +238,23 @@ static void target_expand_source_names(BuildTarget *target) void compile_files(BuildTarget *target) { - if (!target) - { - target = CALLOCS(BuildTarget); - target->type = TARGET_TYPE_EXECUTABLE; - target->sources = build_options.files; - target->name = "a.out"; - } + symtab_init(target->symtab_size ? target->symtab_size : 64 * 1024); + assert(target); target_expand_source_names(target); - target_setup(); + target_setup(target); if (!vec_size(target->sources)) error_exit("No files to compile."); - switch (build_options.compile_option) + if (target->lex_only) { - case COMPILE_LEX_ONLY: - compiler_lex(target); - break; - case COMPILE_LEX_PARSE_ONLY: - compiler_parse(target); - break; - case COMPILE_OUTPUT_HEADERS: - default: - compiler_compile(target); - break; + compiler_lex(target); + return; } + if (target->parse_only) + { + compiler_parse(target); + return; + } + compiler_compile(target); } @@ -258,6 +269,7 @@ void compiler_add_type(Type *type) assert(type_ok(type)); VECADD(compiler.type, type); } + Module *compiler_find_or_create_module(Path *module_name) { Module *module = stable_get(&compiler.modules, module_name->module); diff --git a/src/compiler/compiler.h b/src/compiler/compiler.h index 251347f84..11b22b07a 100644 --- a/src/compiler/compiler.h +++ b/src/compiler/compiler.h @@ -8,6 +8,7 @@ void compiler_init(); void compile_files(BuildTarget *target); -void build(); +void init_build_target(BuildTarget *build_target, BuildOptions *build_options); +void init_default_build_target(BuildTarget *target, BuildOptions *options, const char *name); void symtab_init(uint32_t max_size); diff --git a/src/compiler/compiler_internal.h b/src/compiler/compiler_internal.h index 415c92f36..e5a09ca55 100644 --- a/src/compiler/compiler_internal.h +++ b/src/compiler/compiler_internal.h @@ -24,6 +24,11 @@ typedef int32_t AlignSize; typedef int32_t ScopeId; +#if PLATFORM_WINDOWS +#define DEFAULT_EXE "a.exe" +#else +#define DEFAULT_EXE "a.out" +#endif typedef uint32_t SourceLoc; typedef struct @@ -136,6 +141,7 @@ typedef struct _Diagnostics bool panic_mode; unsigned errors; unsigned warnings; + bool test_mode; } Diagnostics; typedef struct @@ -209,6 +215,7 @@ struct _Type const char *name; Type **type_cache; void *backend_type; + void *backend_aux_type; void *backend_typeid; void *backend_debug_type; union @@ -561,7 +568,7 @@ typedef struct typedef struct { - bool is_struct_function : 1; + bool is_type_method : 1; bool is_pointer_call : 1; Expr *function; Expr **arguments; @@ -762,7 +769,6 @@ struct _Expr bool failable : 1; bool pure : 1; bool constant : 1; - bool reeval : 1; SourceSpan span; Type *type; Type *original_type; @@ -869,9 +875,11 @@ typedef struct typedef struct { bool is_type; + bool is_type_list; union { TypeInfo *type_info; + TypeInfo **type_infos; Expr *expr; }; Ast *body; @@ -1206,6 +1214,7 @@ typedef struct _Context Decl **generic_defines; Decl **functions; Decl **macros; + Decl **generics; Decl **methods; Decl **vars; Decl **incr_array; @@ -1233,7 +1242,6 @@ typedef struct _Context // Reusable returns cache. Ast **returns_cache; }; - Decl *evaluating_macro; Type *rtype; bool failable_return; int in_volatile_section; @@ -1374,7 +1382,7 @@ extern Type *type_u128, *type_i128; extern Type *type_compint, *type_compfloat; extern Type *type_c_short, *type_c_int, *type_c_long, *type_c_longlong; extern Type *type_c_ushort, *type_c_uint, *type_c_ulong, *type_c_ulonglong; -extern Type *type_typeid, *type_error, *type_typeinfo; +extern Type *type_typeid, *type_error, *type_typeinfo, *type_varheader; extern const char *attribute_list[NUMBER_OF_ATTRIBUTES]; @@ -1452,7 +1460,7 @@ static inline bool builtin_may_negate(Type *canonical) bool cast_implicit(Expr *expr, Type *to_type); bool cast(Expr *expr, Type *to_type); -bool cast_to_bool_implicit(Expr *expr); + bool cast_may_implicit(Type *from_type, Type *to_type); bool cast_may_explicit(Type *from_type, Type *to_type); bool cast_implicit_bit_width(Expr *expr, Type *to_type); @@ -1461,7 +1469,7 @@ CastKind cast_to_bool_kind(Type *type); bool cast_implicitly_to_runtime(Expr *expr); -void llvm_codegen(void *module); +const char *llvm_codegen(void *context); void *llvm_gen(Context *context); void llvm_codegen_setup(); @@ -1498,7 +1506,7 @@ static inline DeclKind decl_from_token(TokenType type); #pragma mark --- Diag functions -void diag_reset(void); +void diag_setup(bool test_output); void diag_verror_range(SourceLocation *location, const char *message, va_list args); @@ -1638,7 +1646,7 @@ void stable_clear(STable *table); const char *symtab_add(const char *symbol, uint32_t len, uint32_t fnv1hash, TokenType *type); -void target_setup(void); +void target_setup(BuildTarget *build_target); int target_alloca_addr_space(); void *target_data_layout(); void *target_machine(); @@ -1992,6 +2000,8 @@ static inline size_t type_min_alignment(size_t a, size_t b) return (a | b) & (1 + ~(a | b)); } +void linker(const char *output_file, const char **files, unsigned file_count); + #define TRY_AST_OR(_ast_stmt, _res) ({ Ast* _ast = (_ast_stmt); if (!ast_ok(_ast)) return _res; _ast; }) #define TRY_EXPR_OR(_expr_stmt, _res) ({ Expr* _expr = (_expr_stmt); if (!expr_ok(_expr)) return _res; _expr; }) #define TRY_TYPE_OR(_type_stmt, _res) ({ TypeInfo* _type = (_type_stmt); if (!type_info_ok(_type)) return _res; _type; }) diff --git a/src/compiler/context.c b/src/compiler/context.c index f56d373a3..65d16db4f 100644 --- a/src/compiler/context.c +++ b/src/compiler/context.c @@ -94,7 +94,10 @@ void context_register_global_decl(Context *context, Decl *decl) switch (decl->decl_kind) { case DECL_POISONED: + break; case DECL_GENERIC: + vec_add(context->generics, decl); + decl_set_external_name(decl); break; case DECL_MACRO: vec_add(context->macros, decl); diff --git a/src/compiler/diagnostics.c b/src/compiler/diagnostics.c index 3919950da..d516b8710 100644 --- a/src/compiler/diagnostics.c +++ b/src/compiler/diagnostics.c @@ -7,11 +7,12 @@ Diagnostics diagnostics; -void diag_reset(void) +void diag_setup(bool test_output) { diagnostics.panic_mode = false; diagnostics.errors = 0; diagnostics.warnings = 0; + diagnostics.test_mode = test_output; } typedef enum @@ -23,7 +24,7 @@ typedef enum static void print_error2(SourceLocation *location, const char *message, PrintType print_type) { - if (build_options.test_mode) + if (diagnostics.test_mode) { switch (print_type) { diff --git a/src/compiler/headers.c b/src/compiler/headers.c index 79646660e..6dcebb3b4 100644 --- a/src/compiler/headers.c +++ b/src/compiler/headers.c @@ -52,7 +52,8 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("int64_t"); return; case TYPE_I128: - TODO + OUTPUT("__int128"); + return; case TYPE_U8: OUTPUT("uint8_t"); return; @@ -66,9 +67,11 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("uint64_t"); return; case TYPE_U128: - TODO + OUTPUT("unsigned __int128"); + return; case TYPE_F16: - TODO + OUTPUT("__fp16"); + return; case TYPE_F32: OUTPUT("float"); return; @@ -77,7 +80,8 @@ static void header_print_type(FILE *file, Type *type) OUTPUT("double"); return; case TYPE_F128: - TODO + OUTPUT("__float128"); + return; case TYPE_TYPEID: OUTPUT("c3typeid_t"); return; diff --git a/src/compiler/linker.c b/src/compiler/linker.c new file mode 100644 index 000000000..46b1afc22 --- /dev/null +++ b/src/compiler/linker.c @@ -0,0 +1,31 @@ +#include "compiler_internal.h" + +#if PLATFORM_WINDOWS +static void link_exe(const char *output_file, const char **files_to_link, unsigned file_count) +{ + TODO +} +#else +static void link_exe(const char *output_file, const char **files_to_link, unsigned file_count) +{ + char *result = NULL; + asprintf(&result, "cc -o %s ", output_file); + + for (unsigned i = 0; i < file_count; i++) + { + char *new_res = NULL; + asprintf(&new_res, "%s %s", result, files_to_link[i]); + free(result); + result = new_res; + } + if (system(result) != EXIT_SUCCESS) + { + error_exit("Failed to create an executable."); + } +} +#endif + +void linker(const char *output_file, const char **files, unsigned file_count) +{ + link_exe(output_file, files, file_count); +} diff --git a/src/compiler/llvm_codegen.c b/src/compiler/llvm_codegen.c index c899c6701..f6744d216 100644 --- a/src/compiler/llvm_codegen.c +++ b/src/compiler/llvm_codegen.c @@ -6,7 +6,6 @@ -static int get_inlining_threshold(void); static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) { char *message = LLVMGetDiagInfoDescription(ref); @@ -16,7 +15,6 @@ static void diagnostics_handler(LLVMDiagnosticInfoRef ref, void *context) { case LLVMDSError: error_exit("LLVM error generating code for %s: %s", ((GenContext *)context)->ast_context->module->name, message); - break; case LLVMDSWarning: severerity_name = "warning"; break; @@ -35,6 +33,7 @@ static void gencontext_init(GenContext *context, Context *ast_context) { memset(context, 0, sizeof(GenContext)); context->context = LLVMContextCreate(); + context->build_target = ast_context->target; context->bool_type = LLVMInt1TypeInContext(context->context); context->byte_type = LLVMInt8TypeInContext(context->context); LLVMContextSetDiagnosticHandler(context->context, &diagnostics_handler, context); @@ -43,8 +42,6 @@ static void gencontext_init(GenContext *context, Context *ast_context) static void gencontext_destroy(GenContext *context) { - free(context->ir_filename); - free(context->object_filename); LLVMContextDispose(context->context); free(context); } @@ -306,18 +303,30 @@ void llvm_emit_ptr_from_array(GenContext *c, BEValue *value) // TODO insert trap on overflow. LLVMTypeRef subarray_type = llvm_get_type(c, value->type); assert(value->kind == BE_ADDRESS); - LLVMValueRef pointer_addr = LLVMBuildStructGEP2(c->builder, subarray_type, value->value, 0, "subarray_ptr"); + LLVMValueRef pointer_addr = LLVMBuildStructGEP2(c->builder, subarray_type, value->value, 0, "subarrayptr"); LLVMTypeRef pointer_type = llvm_get_type(c, type_get_ptr(value->type->array.base)); AlignSize alignment = type_abi_alignment(type_voidptr); // We need to pick the worst alignment in case this is packed in an array. if (value->alignment < alignment) alignment = value->alignment; llvm_value_set_address_align(value, - llvm_emit_load_aligned(c, pointer_type, pointer_addr, 0, "subarrptr"), value->type, alignment); + llvm_emit_load_aligned(c, pointer_type, pointer_addr, 0, "saptr"), value->type, alignment); return; } case TYPE_STRLIT: TODO case TYPE_VARARRAY: + { + llvm_value_rvalue(c, value); + LLVMTypeRef struct_type = value->type->backend_aux_type; + LLVMValueRef pointer_addr = LLVMBuildStructGEP2(c->builder, struct_type, value->value, 3, "vararrptr"); + LLVMTypeRef pointer_type = llvm_get_type(c, type_get_ptr(value->type->array.base)); + AlignSize alignment = type_abi_alignment(type_voidptr); + // We need to pick the worst alignment in case this is packed in an array. + if (value->alignment < alignment) alignment = value->alignment; + llvm_value_set_address_align(value, + llvm_emit_load_aligned(c, pointer_type, pointer_addr, 0, "vaptr"), value->type, alignment); + return; + } default: UNREACHABLE } @@ -410,7 +419,7 @@ static void gencontext_verify_ir(GenContext *context) void gencontext_emit_object_file(GenContext *context) { char *err = ""; - LLVMSetTarget(context->module, build_options.target_triple); + LLVMSetTarget(context->module, build_target.target_triple); char *layout = LLVMCopyStringRepOfTargetData(target_data_layout()); LLVMSetDataLayout(context->module, layout); LLVMDisposeMessage(layout); @@ -468,13 +477,13 @@ void llvm_emit_local_var_alloca(GenContext *c, Decl *decl) * Values here taken from LLVM. * @return return the inlining threshold given the build options. */ -static int get_inlining_threshold(void) +static int get_inlining_threshold(BuildTarget *target) { - if (build_options.optimization_level == OPTIMIZATION_AGGRESSIVE) + if (target->optimization_level == OPTIMIZATION_AGGRESSIVE) { return 250; } - switch (build_options.size_optimization_level) + switch (target->size_optimization_level) { case SIZE_OPTIMIZATION_TINY: return 5; @@ -751,19 +760,20 @@ void llvm_value_set_address(BEValue *value, LLVMValueRef llvm_value, Type *type) void llvm_value_fold_failable(GenContext *c, BEValue *value) { + if (value->kind == BE_ADDRESS_FAILABLE) { LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); - // TODO optimize load. - LLVMValueRef error_value = gencontext_emit_load(c, type_error, value->failable); + BEValue error_value; + llvm_value_set_address(&error_value, value->failable, type_error); BEValue comp; - llvm_value_set_bool(&comp, llvm_emit_is_no_error(c, error_value)); + llvm_value_set_bool(&comp, llvm_emit_is_no_error_value(c, &error_value)); if (c->error_var) { LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); llvm_emit_cond_br(c, &comp, after_block, error_block); llvm_emit_block(c, error_block); - llvm_store_aligned(c, c->error_var, error_value, type_abi_alignment(type_usize)); + llvm_store_bevalue_dest_aligned(c, c->error_var, &error_value); llvm_emit_br(c, c->catch_block); } else @@ -863,16 +873,16 @@ static void gencontext_emit_decl(GenContext *context, Decl *decl) } } -void llvm_codegen(void *context) +const char *llvm_codegen(void *context) { - GenContext *gen_context = context; - LLVMModuleRef module = gen_context->module; + GenContext *c = context; + LLVMModuleRef module = c->module; // Starting from here we could potentially thread this: LLVMPassManagerBuilderRef pass_manager_builder = LLVMPassManagerBuilderCreate(); - LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, build_options.optimization_level); - LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, build_options.size_optimization_level); - LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, build_options.optimization_level == OPTIMIZATION_NONE); - LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, get_inlining_threshold()); + LLVMPassManagerBuilderSetOptLevel(pass_manager_builder, c->build_target->optimization_level); + LLVMPassManagerBuilderSetSizeLevel(pass_manager_builder, c->build_target->size_optimization_level); + LLVMPassManagerBuilderSetDisableUnrollLoops(pass_manager_builder, c->build_target->optimization_level == OPTIMIZATION_NONE); + LLVMPassManagerBuilderUseInlinerWithThreshold(pass_manager_builder, get_inlining_threshold(c->build_target)); LLVMPassManagerRef pass_manager = LLVMCreatePassManager(); LLVMPassManagerRef function_pass_manager = LLVMCreateFunctionPassManagerForModule(module); LLVMAddAnalysisPasses(target_machine(), function_pass_manager); @@ -902,19 +912,25 @@ void llvm_codegen(void *context) LLVMDisposePassManager(pass_manager); // Serialize the LLVM IR, if requested, also verify the IR in this case - if (build_options.emit_llvm) + if (c->build_target->emit_llvm) { - gencontext_print_llvm_ir(gen_context); - gencontext_verify_ir(gen_context); + gencontext_print_llvm_ir(c); + gencontext_verify_ir(c); } - if (build_options.emit_bitcode) gencontext_emit_object_file(gen_context); - - gencontext_end_module(gen_context); - gencontext_destroy(gen_context); + const char *object_name = NULL; + if (c->build_target->emit_object_files) + { + gencontext_emit_object_file(c); + object_name = c->object_filename; + } + gencontext_end_module(c); + gencontext_destroy(c); + return object_name; } + void *llvm_gen(Context *context) { assert(intrinsics_setup); @@ -960,7 +976,7 @@ void *llvm_gen(Context *context) if (llvm_use_debug(gen_context)) LLVMDIBuilderFinalize(gen_context->debug.builder); // If it's in test, then we want to serialize the IR before it is optimized. - if (build_options.test_mode) + if (diagnostics.test_mode) { gencontext_print_llvm_ir(gen_context); gencontext_verify_ir(gen_context); diff --git a/src/compiler/llvm_codegen_debug_info.c b/src/compiler/llvm_codegen_debug_info.c index 4b44ff244..83692d5b8 100644 --- a/src/compiler/llvm_codegen_debug_info.c +++ b/src/compiler/llvm_codegen_debug_info.c @@ -123,7 +123,7 @@ void llvm_emit_debug_function(GenContext *c, Decl *decl) true, loc->line, flags, - build_options.optimization_level != OPTIMIZATION_NONE); + c->build_target->optimization_level != OPTIMIZATION_NONE); LLVMSetSubprogram(decl->backend_ref, c->debug.function); } @@ -139,7 +139,7 @@ void llvm_emit_debug_local_var(GenContext *c, Decl *decl) c->debug.file, location->line, llvm_get_debug_type(c, decl->type), - build_options.optimization_level != OPTIMIZATION_NONE, + c->build_target->optimization_level != OPTIMIZATION_NONE, LLVMDIFlagZero, decl->alignment); decl->var.backend_debug_ref = var; @@ -214,7 +214,7 @@ static LLVMMetadataRef llvm_debug_forward_comp(GenContext *c, Type *type, const type->name, strlen(type->name), scope, c->debug.file, loc ? loc->line : 0, - build_options.version, + 1 /* version TODO */, type_size(type) * 8, type_abi_alignment(type) * 8, flags, @@ -371,6 +371,54 @@ static LLVMMetadataRef llvm_debug_subarray_type(GenContext *c, Type *type) return llvm_get_debug_struct(c, type, type->name, elements, 2, NULL, NULL, LLVMDIFlagZero); } +static LLVMMetadataRef llvm_debug_vararray_type(GenContext *c, Type *type) +{ + LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, type->name, NULL, NULL, LLVMDIFlagZero); + type->backend_debug_type = forward; + Type *element = type->array.base; + + LLVMMetadataRef array = LLVMDIBuilderCreateArrayType( + c->debug.builder, + type_size(type), + type_abi_alignment(element), + llvm_get_debug_type(c, element), + NULL, 0); + + LLVMMetadataRef array_member = LLVMDIBuilderCreateMemberType( + c->debug.builder, + forward, + "array", strlen("array"), + NULL, + 0, 0, + type_abi_alignment(element) * 8, + type_size(type_usize) * 2 * 8, LLVMDIFlagZero, array); + + LLVMMetadataRef elements[3] = { + llvm_get_debug_member(c, type_get_ptr(type->array.base), "len", 0, NULL, forward, LLVMDIFlagZero), + llvm_get_debug_member(c, type_usize, "capacity", type_size(type_usize), NULL, forward, LLVMDIFlagZero), + array_member + }; + unsigned strukt_size = type_size(type_usize) * 2 * 8; + unsigned alignment = type_abi_alignment(type_usize) * 8; + LLVMMetadataRef strukt = LLVMDIBuilderCreateStructType(c->debug.builder, + NULL, + "", 0, NULL, 0, strukt_size, + alignment, + LLVMDIFlagZero, NULL, + elements, 3, + c->debug.runtime_version, + NULL, // VTable + "", 0); + LLVMMetadataRef real = LLVMDIBuilderCreatePointerType(c->debug.builder, + strukt, + strukt_size, + alignment, 0, + type->name, strlen(type->name)); + + LLVMMetadataReplaceAllUsesWith(forward, real); + return real; +} + static LLVMMetadataRef llvm_debug_errunion_type(GenContext *c, Type *type) { LLVMMetadataRef forward = llvm_debug_forward_comp(c, type, type->name, NULL, NULL, LLVMDIFlagZero); @@ -422,6 +470,8 @@ static LLVMMetadataRef llvm_debug_typedef_type(GenContext *c, Type *type) NULL, 0, NULL, 0); } + Type *original_type = type->type_kind == TYPE_TYPEDEF ? decl->typedef_decl.type_info->type : decl->distinct_decl.base_type; + SourceLocation *location = TOKLOC(decl->span.loc); // Use forward references in case we haven't resolved the original type, since we could have this: if (!type->canonical->backend_debug_type) @@ -429,7 +479,7 @@ static LLVMMetadataRef llvm_debug_typedef_type(GenContext *c, Type *type) type->backend_debug_type = llvm_debug_forward_comp(c, type, type->name, location, NULL, LLVMDIFlagZero); } LLVMMetadataRef real = LLVMDIBuilderCreateTypedef(c->debug.builder, - llvm_get_debug_type(c, decl->typedef_decl.type_info->type), + llvm_get_debug_type(c, original_type), decl->name, TOKLEN(decl->name_token), c->debug.file, location->line, c->debug.file, type_abi_alignment(type)); @@ -488,6 +538,7 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * case TYPE_TYPEINFO: case TYPE_MEMBER: case TYPE_INFERRED_ARRAY: + case TYPE_STRLIT: UNREACHABLE case TYPE_BOOL: return llvm_debug_simple_type(c, type, DW_ATE_boolean); @@ -527,15 +578,12 @@ static inline LLVMMetadataRef llvm_get_debug_type_internal(GenContext *c, Type * case TYPE_UNION: return type->backend_debug_type = llvm_debug_structlike_type(c, type, scope); case TYPE_DISTINCT: - TODO case TYPE_TYPEDEF: return type->backend_debug_type = llvm_debug_typedef_type(c, type); - case TYPE_STRLIT: - TODO case TYPE_ARRAY: return type->backend_debug_type = llvm_debug_array_type(c, type); case TYPE_VARARRAY: - TODO + return type->backend_debug_type = llvm_debug_vararray_type(c, type); case TYPE_SUBARRAY: return type->backend_debug_type = llvm_debug_subarray_type(c, type); case TYPE_ERR_UNION: diff --git a/src/compiler/llvm_codegen_expr.c b/src/compiler/llvm_codegen_expr.c index b56d832bc..256426f27 100644 --- a/src/compiler/llvm_codegen_expr.c +++ b/src/compiler/llvm_codegen_expr.c @@ -14,10 +14,12 @@ static void llvm_emit_post_unary_expr(GenContext *context, BEValue *be_value, Ex static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, BEValue *parent, BEValue *index); static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_t offset, DesignatorElement** current, DesignatorElement **last, Expr *expr, BEValue *emitted_value); -LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error) +LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value) { - LLVMValueRef domain = LLVMBuildExtractValue(c->builder, error, 0, ""); - return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr"); + assert(value->kind == BE_ADDRESS); + LLVMValueRef val = LLVMBuildStructGEP2(c->builder, llvm_get_type(c, value->type), value->value, 0, "err_domain"); + LLVMValueRef domain = llvm_emit_load_aligned(c, llvm_get_type(c, type_uptr), val, value->alignment, ""); + return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_uptr), "not_err"); } LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size) @@ -32,29 +34,29 @@ LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size) return LLVMGetUndef(llvm_const_padding_type(c, size)); } -static inline LLVMValueRef llvm_emit_add_int(GenContext *context, Type *type, LLVMValueRef left, LLVMValueRef right) +static inline LLVMValueRef llvm_emit_add_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right) { - if (build_options.trap_wrapping) + if (c->build_target->feature.trap_on_wrap) { - LLVMTypeRef type_to_use = llvm_get_type(context, type->canonical); + LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); LLVMValueRef args[2] = { left, right }; assert(type->canonical == type); LLVMValueRef add_res; if (type_is_unsigned(type)) { - add_res = llvm_emit_call_intrinsic(context, intrinsic_id_uadd_overflow, &type_to_use, 1, args, 2); + add_res = llvm_emit_call_intrinsic(c, intrinsic_id_uadd_overflow, &type_to_use, 1, args, 2); } else { - add_res = llvm_emit_call_intrinsic(context, intrinsic_id_sadd_overflow, &type_to_use, 1, args, 2); + add_res = llvm_emit_call_intrinsic(c, intrinsic_id_sadd_overflow, &type_to_use, 1, args, 2); } - LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); - LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); - llvm_emit_panic_on_true(context, ok, "Addition overflow"); + LLVMValueRef result = LLVMBuildExtractValue(c->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(c->builder, add_res, 1, ""); + llvm_emit_panic_on_true(c, ok, "Addition overflow"); return result; } - return LLVMBuildAdd(context->builder, left, right, "add"); + return LLVMBuildAdd(c->builder, left, right, "add"); } LLVMValueRef llvm_emit_coerce(GenContext *context, LLVMTypeRef coerced, BEValue *value, Type *original_type) @@ -84,39 +86,39 @@ LLVMValueRef llvm_emit_coerce(GenContext *context, LLVMTypeRef coerced, BEValue return llvm_emit_load_aligned(context, coerced, cast, max_align, "coerced"); } -LLVMValueRef llvm_emit_convert_value_from_coerced(GenContext *context, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type) +void llvm_emit_convert_value_from_coerced(GenContext *context, BEValue *result, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type) { unsigned max_align = MAX(llvm_abi_alignment(coerced), type_abi_alignment(original_type)); LLVMValueRef temp = llvm_emit_alloca(context, coerced, max_align, "coerce_temp"); llvm_store_aligned(context, temp, value, max_align); temp = LLVMBuildBitCast(context->builder, temp, llvm_get_type(context, type_get_ptr(original_type)), ""); - return llvm_emit_load_aligned(context, llvm_get_type(context, original_type), temp, max_align, "coerced"); + llvm_value_set_address_align(result, temp, original_type, max_align); } static inline LLVMValueRef -llvm_emit_sub_int(GenContext *context, Type *type, LLVMValueRef left, LLVMValueRef right) +llvm_emit_sub_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right) { - if (build_options.trap_wrapping) + if (c->build_target->feature.trap_on_wrap) { - LLVMTypeRef type_to_use = llvm_get_type(context, type); + LLVMTypeRef type_to_use = llvm_get_type(c, type); LLVMValueRef args[2] = { left, right }; assert(type->canonical == type); LLVMValueRef add_res; if (type_is_unsigned(type)) { - add_res = llvm_emit_call_intrinsic(context, intrinsic_id_usub_overflow, &type_to_use, 1, args, 2); + add_res = llvm_emit_call_intrinsic(c, intrinsic_id_usub_overflow, &type_to_use, 1, args, 2); } else { - add_res = llvm_emit_call_intrinsic(context, intrinsic_id_ssub_overflow, &type_to_use, 1, args, 2); + add_res = llvm_emit_call_intrinsic(c, intrinsic_id_ssub_overflow, &type_to_use, 1, args, 2); } - LLVMValueRef result = LLVMBuildExtractValue(context->builder, add_res, 0, ""); - LLVMValueRef ok = LLVMBuildExtractValue(context->builder, add_res, 1, ""); - llvm_emit_panic_on_true(context, ok, "Subtraction overflow"); + LLVMValueRef result = LLVMBuildExtractValue(c->builder, add_res, 0, ""); + LLVMValueRef ok = LLVMBuildExtractValue(c->builder, add_res, 1, ""); + llvm_emit_panic_on_true(c, ok, "Subtraction overflow"); return result; } - return LLVMBuildSub(context->builder, left, right, "sub"); + return LLVMBuildSub(c->builder, left, right, "sub"); } static inline void llvm_emit_subscript_addr_base(GenContext *context, BEValue *value, Expr *parent) @@ -188,7 +190,7 @@ static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, return LLVMBuildInBoundsGEP(c->builder, parent->value, &index->value, 1, "ptridx"); case TYPE_ARRAY: { - if (build_options.debug_mode) + if (c->build_target->feature.safe_mode) { llvm_emit_array_bounds_check(c, index, llvm_const_int(c, index->type, type->array.len)); } @@ -203,7 +205,7 @@ static inline LLVMValueRef llvm_emit_subscript_addr_with_base_new(GenContext *c, } case TYPE_SUBARRAY: { - if (build_options.debug_mode) + if (c->build_target->feature.safe_mode) { // TODO insert trap on overflow. } @@ -1076,7 +1078,7 @@ static void gencontext_emit_unary_expr(GenContext *c, BEValue *value, Expr *expr assert(!type_is_unsigned(type)); { LLVMValueRef zero = llvm_get_zero(c, expr->unary_expr.expr->type); - if (build_options.trap_wrapping) + if (c->build_target->feature.trap_on_wrap) { // TODO LLVMTypeRef type_to_use = llvm_get_type(c, type->canonical); @@ -1137,9 +1139,33 @@ void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_l case TYPE_STRLIT: TODO break; - case TYPE_VARARRAY: - TODO + { + llvm_value_rvalue(c, be_value); + LLVMValueRef check = LLVMBuildIsNull(c->builder, be_value->value, "checknull"); + BEValue bool_value; + llvm_value_set_bool(&bool_value, check); + LLVMBasicBlockRef null_block = llvm_basic_block_new(c, "lennull"); + LLVMBasicBlockRef non_null_block = llvm_basic_block_new(c, "lennormal"); + LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "lenend"); + llvm_emit_cond_br(c, &bool_value, null_block, non_null_block); + llvm_emit_block(c, null_block); + LLVMValueRef result_null = llvm_get_zero(c, type_usize); + llvm_emit_br(c, exit_block); + llvm_emit_block(c, non_null_block); + LLVMTypeRef struct_type = be_value->type->backend_aux_type; + LLVMValueRef len_addr = LLVMBuildStructGEP2(c->builder, struct_type, be_value->value, 0, ""); + llvm_value_set_address(be_value, len_addr, type_usize); + LLVMValueRef result = llvm_value_rvalue_store(c, be_value); + llvm_emit_br(c, exit_block); + llvm_emit_block(c, exit_block); + LLVMValueRef total = LLVMBuildPhi(c->builder, llvm_get_type(c, type_usize), ""); + LLVMValueRef logic_values[2] = { result_null, result }; + LLVMBasicBlockRef blocks[2] = { null_block, non_null_block }; + LLVMAddIncoming(total, logic_values, blocks, 2); + llvm_value_set(be_value, total, type_usize); + return; + } default: UNREACHABLE } @@ -1150,39 +1176,39 @@ static void llvm_emit_len(GenContext *c, BEValue *be_value, Expr *expr) llvm_emit_len_for_expr(c, be_value, be_value); } -static void gencontext_emit_trap_negative(GenContext *context, Expr *expr, LLVMValueRef value, const char *error) +static void llvm_emit_trap_negative(GenContext *c, Expr *expr, LLVMValueRef value, const char *error) { - if (!build_options.debug_mode) return; + if (!c->build_target->feature.safe_mode) return; if (type_is_integer_unsigned(expr->type->canonical)) return; - LLVMValueRef zero = llvm_const_int(context, expr->type, 0); - LLVMValueRef ok = LLVMBuildICmp(context->builder, LLVMIntSLT, value, zero, "underflow"); - llvm_emit_panic_on_true(context, ok, error); + LLVMValueRef zero = llvm_const_int(c, expr->type, 0); + LLVMValueRef ok = LLVMBuildICmp(c->builder, LLVMIntSLT, value, zero, "underflow"); + llvm_emit_panic_on_true(c, ok, error); } static void -gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_type_ref, LLVMValueRef *parent_base_ref, - Type **start_type_ref, LLVMValueRef *start_index_ref, Type **end_type_ref, - LLVMValueRef *end_index_ref) +llvm_emit_slice_values(GenContext *c, Expr *slice, Type **parent_type_ref, LLVMValueRef *parent_base_ref, + Type **start_type_ref, LLVMValueRef *start_index_ref, Type **end_type_ref, + LLVMValueRef *end_index_ref) { assert(slice->expr_kind == EXPR_SLICE); Expr *parent_expr = slice->slice_expr.expr; Type *parent_type = parent_expr->type->canonical; BEValue parent_addr_x; - llvm_emit_expr(context, &parent_addr_x, parent_expr); - llvm_value_addr(context, &parent_addr_x); + llvm_emit_expr(c, &parent_addr_x, parent_expr); + llvm_value_addr(c, &parent_addr_x); LLVMValueRef parent_addr = parent_addr_x.value; LLVMValueRef parent_load_value; LLVMValueRef parent_base; switch (parent_type->type_kind) { case TYPE_POINTER: - parent_load_value = parent_base = gencontext_emit_load(context, parent_type, parent_addr); + parent_load_value = parent_base = gencontext_emit_load(c, parent_type, parent_addr); break; case TYPE_SUBARRAY: - parent_load_value = gencontext_emit_load(context, parent_type, parent_addr); - parent_base = LLVMBuildExtractValue(context->builder, parent_load_value, 0, ""); + parent_load_value = gencontext_emit_load(c, parent_type, parent_addr); + parent_base = LLVMBuildExtractValue(c->builder, parent_load_value, 0, ""); break; case TYPE_ARRAY: parent_base = parent_addr; @@ -1200,11 +1226,11 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ // Emit the start and end Type *start_type = start->type->canonical; BEValue start_index; - llvm_emit_expr(context, &start_index, start); - llvm_value_rvalue(context, &start_index); + llvm_emit_expr(c, &start_index, start); + llvm_value_rvalue(c, &start_index); LLVMValueRef len; - if (!end || slice->slice_expr.start_from_back || slice->slice_expr.end_from_back || build_options.debug_mode) + if (!end || slice->slice_expr.start_from_back || slice->slice_expr.end_from_back || c->build_target->feature.safe_mode) { switch (parent_type->type_kind) { @@ -1212,10 +1238,10 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ len = NULL; break; case TYPE_SUBARRAY: - len = LLVMBuildExtractValue(context->builder, parent_load_value, 1, ""); + len = LLVMBuildExtractValue(c->builder, parent_load_value, 1, ""); break; case TYPE_ARRAY: - len = llvm_const_int(context, type_usize, parent_type->array.len); + len = llvm_const_int(c, type_usize, parent_type->array.len); break; case TYPE_VARARRAY: case TYPE_STRLIT: @@ -1228,26 +1254,26 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ // Walk from end if it is slice from the back. if (slice->slice_expr.start_from_back) { - start_index.value = llvm_emit_sub_int(context, start_type, len, start_index.value); + start_index.value = llvm_emit_sub_int(c, start_type, len, start_index.value); } // Check that index does not extend beyond the length. - if (parent_type->type_kind != TYPE_POINTER && build_options.debug_mode) + if (parent_type->type_kind != TYPE_POINTER && c->build_target->feature.safe_mode) { - LLVMValueRef exceeds_size = llvm_emit_int_comparison(context, + LLVMValueRef exceeds_size = llvm_emit_int_comparison(c, type_usize, start_type, start_index.value, len, BINARYOP_GE); - llvm_emit_panic_on_true(context, exceeds_size, "Index exceeds array length."); + llvm_emit_panic_on_true(c, exceeds_size, "Index exceeds array length."); } // Insert trap for negative start offset for non pointers. if (parent_type->type_kind != TYPE_POINTER) { - gencontext_emit_trap_negative(context, start, start_index.value, "Negative index"); + llvm_emit_trap_negative(c, start, start_index.value, "Negative index"); } Type *end_type; @@ -1256,37 +1282,37 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ if (end) { // Get the index. - llvm_emit_expr(context, &end_index, end); - llvm_value_rvalue(context, &end_index); + llvm_emit_expr(c, &end_index, end); + llvm_value_rvalue(c, &end_index); end_type = end->type->canonical; // Reverse if it is "from back" if (slice->slice_expr.end_from_back) { - end_index.value = llvm_emit_sub_int(context, end_type, len, end_index.value); - llvm_value_rvalue(context, &end_index); + end_index.value = llvm_emit_sub_int(c, end_type, len, end_index.value); + llvm_value_rvalue(c, &end_index); } // This will trap any bad negative index, so we're fine. - if (build_options.debug_mode) + if (c->build_target->feature.safe_mode) { - LLVMValueRef excess = llvm_emit_int_comparison(context, + LLVMValueRef excess = llvm_emit_int_comparison(c, start_type, end_type, start_index.value, end_index.value, BINARYOP_GT); - llvm_emit_panic_on_true(context, excess, "Negative size"); + llvm_emit_panic_on_true(c, excess, "Negative size"); if (len) { - LLVMValueRef exceeds_size = llvm_emit_int_comparison(context, + LLVMValueRef exceeds_size = llvm_emit_int_comparison(c, type_usize, end_type, len, end_index.value, BINARYOP_LT); - llvm_emit_panic_on_true(context, exceeds_size, "Size exceeds index"); + llvm_emit_panic_on_true(c, exceeds_size, "Size exceeds index"); } } } @@ -1294,7 +1320,7 @@ gencontext_emit_slice_values(GenContext *context, Expr *slice, Type **parent_typ { assert(len && "Pointer should never end up here."); // Otherwise everything is fine and dandy. Our len - 1 is our end index. - end_index.value = LLVMBuildSub(context->builder, len, LLVMConstInt(LLVMTypeOf(len), 1, false), ""); + end_index.value = LLVMBuildSub(c->builder, len, LLVMConstInt(LLVMTypeOf(len), 1, false), ""); end_type = type_usize; } @@ -1315,9 +1341,9 @@ static void gencontext_emit_slice(GenContext *context, BEValue *be_value, Expr * Type *start_type; LLVMValueRef start_index; // Use general function to get all the values we need (a lot!) - gencontext_emit_slice_values(context, expr, &parent_type, - &parent_base, - &start_type, &start_index, &end_type, &end_index); + llvm_emit_slice_values(context, expr, &parent_type, + &parent_base, + &start_type, &start_index, &end_type, &end_index); // Calculate the size @@ -1373,9 +1399,9 @@ static void gencontext_emit_slice_assign(GenContext *c, BEValue *be_value, Expr Type *start_type; LLVMValueRef start_index; // Use general function to get all the values we need (a lot!) - gencontext_emit_slice_values(c, expr->slice_assign_expr.left, &parent_type, - &parent_base, - &start_type, &start_index, &end_type, &end_index); + llvm_emit_slice_values(c, expr->slice_assign_expr.left, &parent_type, + &parent_base, + &start_type, &start_index, &end_type, &end_index); // We will need to iterate for the general case. LLVMBasicBlockRef start_block = c->current_block; @@ -1618,7 +1644,7 @@ static inline LLVMValueRef llvm_fixup_shift_rhs(GenContext *c, LLVMValueRef left static inline LLVMValueRef llvm_emit_mult_int(GenContext *c, Type *type, LLVMValueRef left, LLVMValueRef right) { - if (build_options.trap_wrapping) + if (c->build_target->feature.trap_on_wrap) { LLVMTypeRef type_to_use = llvm_get_type(c, type); LLVMValueRef args[2] = { left, right }; @@ -2450,65 +2476,95 @@ void llvm_emit_parameter(GenContext *context, LLVMValueRef **args, ABIArgInfo *i } } -void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) +void llvm_emit_call_expr(GenContext *c, BEValue *be_value, Expr *expr) { - printf("Optimize call return\n"); + Expr *function = expr->call_expr.function; FunctionSignature *signature; LLVMTypeRef func_type; LLVMValueRef func; + + + // 1. Call through a pointer. if (expr->call_expr.is_pointer_call) { - signature = expr->call_expr.function->type->canonical->pointer->func.signature; + // 1a. Find the pointee type for the function pointer: + Type *type = function->type->canonical->pointer; + + // 1b. Find the type signature using the underlying pointer. + signature = type->func.signature; + + // 1c. Evaluate the pointer expression. BEValue func_value; - llvm_emit_expr(context, &func_value, expr->call_expr.function); - func = llvm_value_rvalue_store(context, &func_value); - func_type = llvm_get_type(context, expr->call_expr.function->type->canonical->pointer); + llvm_emit_expr(c, &func_value, expr->call_expr.function); + + // 1d. Load it as a value + func = llvm_value_rvalue_store(c, &func_value); + + // 1e. Calculate the function type + func_type = llvm_get_type(c, type); } - else if (expr->call_expr.is_struct_function) + else if (expr->call_expr.is_type_method) { + // 2. Call through a type method + + // 2a. Get the function declaration Decl *function_decl = expr->call_expr.function->access_expr.ref; + + // 2b. Set signature, function and function type signature = &function_decl->func.function_signature; func = function_decl->backend_ref; - func_type = llvm_get_type(context, function_decl->type); + func_type = llvm_get_type(c, function_decl->type); } else { + // 3. Call a regular function. Decl *function_decl = expr->call_expr.function->identifier_expr.decl; + + // 3a. This may be an intrinsic, if so generate an intrinsic call instead. if (function_decl->func.is_builtin) { - gencontext_emit_call_intrinsic_expr(context, be_value, expr); + gencontext_emit_call_intrinsic_expr(c, be_value, expr); return; } + + // 3b. Set signature, function and function type signature = &function_decl->func.function_signature; func = function_decl->backend_ref; - func_type = llvm_get_type(context, function_decl->type); + func_type = llvm_get_type(c, function_decl->type); } - LLVMValueRef return_param = NULL; LLVMValueRef *values = NULL; + // 4. Prepare the return abi data. ABIArgInfo *ret_info = signature->ret_abi_info; Type *return_type = signature->rtype->type->canonical; + // 5. In the case of a failable, the error is replacing the regular return abi. + LLVMValueRef error_var = NULL; if (signature->failable) { ret_info = signature->failable_abi_info; return_type = type_error; } + // 6. Generate data for the return value. switch (ret_info->kind) { case ABI_ARG_INDIRECT: - // Create the return parameter - return_param = llvm_emit_alloca(context, llvm_get_type(context, return_type), - ret_info->indirect.realignment, "sretparam"); - // Add the pointer to the list of arguments. - vec_add(values, return_param); - if (ret_info->indirect.realignment) + // 6a. We can use the stored error var if there is no redirect. + if (signature->failable && c->error_var && type_abi_alignment(return_type) < ret_info->indirect.realignment) { - llvm_set_alignment(return_param, ret_info->indirect.realignment); + error_var = c->error_var; + vec_add(values, error_var); + break; } + // 6b. Return true is indirect, in this case we allocate a local, using the desired alignment on the caller side. + AlignSize alignment = ret_info->indirect.realignment ? ret_info->indirect.realignment : type_abi_alignment(return_type); + llvm_value_set_address_align(be_value, llvm_emit_alloca(c, llvm_get_type(c, return_type), alignment, "sretparam"), return_type, alignment); + + // 6c. Add the pointer to the list of arguments. + vec_add(values, be_value->value); break; case ABI_ARG_EXPAND: UNREACHABLE @@ -2519,138 +2575,215 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr) break; } + + // 7. We might have a failable indirect return and a normal return. + // In this case we need to add it by hand. + BEValue synthetic_return_param = {}; if (signature->failable && signature->ret_abi_info) { + // 7b. Create the address to hold the return. Type *actual_return_type = type_lowering(signature->rtype->type); - return_param = llvm_emit_alloca_aligned(context, actual_return_type, "retparam"); - llvm_value_set(be_value, return_param, type_get_ptr(actual_return_type)); - llvm_emit_parameter(context, &values, signature->ret_abi_info, be_value, be_value->type); + llvm_value_set(&synthetic_return_param, llvm_emit_alloca_aligned(c, actual_return_type, "retparam"), type_get_ptr(actual_return_type)); + // 7c. Emit it as a parameter as a pointer (will implicitly add it to the value list) + llvm_emit_parameter(c, &values, signature->ret_abi_info, &synthetic_return_param, synthetic_return_param.type); + // 7d. Update the be_value to actually be an address. + llvm_value_set_address(&synthetic_return_param, synthetic_return_param.value, actual_return_type); } + + + // 8. Add all other arguments. unsigned arguments = vec_size(expr->call_expr.arguments); assert(arguments >= vec_size(signature->params)); VECEACH(signature->params, i) { + // 8a. Evaluate the expression. Expr *arg_expr = expr->call_expr.arguments[i]; - llvm_emit_expr(context, be_value, arg_expr); + llvm_emit_expr(c, be_value, arg_expr); + + // 8b. Emit the parameter according to ABI rules. Decl *param = signature->params[i]; ABIArgInfo *info = param->var.abi_info; - llvm_emit_parameter(context, &values, info, be_value, param->type); + llvm_emit_parameter(c, &values, info, be_value, param->type); } + + // 9. Emit varargs. for (unsigned i = vec_size(signature->params); i < arguments; i++) { Expr *arg_expr = expr->call_expr.arguments[i]; - llvm_emit_expr(context, be_value, arg_expr); + llvm_emit_expr(c, be_value, arg_expr); printf("TODO: varargs should be expanded correctly\n"); - vec_add(values, llvm_value_rvalue_store(context, be_value)); + vec_add(values, llvm_value_rvalue_store(c, be_value)); } - LLVMValueRef call = LLVMBuildCall2(context->builder, func_type, func, values, vec_size(values), ""); + // 10. Create the actual call + LLVMValueRef call_value = LLVMBuildCall2(c->builder, func_type, func, values, vec_size(values), ""); + // 11. Process the return value. switch (ret_info->kind) { case ABI_ARG_EXPAND: UNREACHABLE case ABI_ARG_IGNORE: - // Default behaviour is fine. - break; + // 12. Basically void returns or empty structs. + // Here we know we don't have a failable or any return value that can be used. + assert(!signature->failable && "Failable should have produced a return value."); + *be_value = (BEValue) {}; + return; case ABI_ARG_INDIRECT: - // TODO look at failable - call = llvm_emit_load_aligned(context, - llvm_get_type(context, return_type), - return_param, - ret_info->indirect.realignment, - ""); + + // 13. Indirect, that is passing the result through an out parameter. + + // 13a. In the case of an already present error_var, we don't need to do a load here. + if (error_var) break; + + // 13b. Otherwise it will be contained in a be_value that is an address + // so we don't need to do anything more. + assert(be_value->kind == BE_ADDRESS); + break; case ABI_ARG_DIRECT_PAIR: { - // TODO look at failable - LLVMTypeRef lo = llvm_abi_type(context, ret_info->direct_pair.lo); - LLVMTypeRef hi = llvm_abi_type(context, ret_info->direct_pair.hi); - LLVMTypeRef struct_type = llvm_get_twostruct(context, lo, hi); - call = llvm_emit_convert_value_from_coerced(context, struct_type, call, return_type); + // 14. A direct pair, in this case the data is stored like { lo, hi } + // For example we might have { int, int, short, short, int }, + // this then gets bitcast to { long, long }, so we recover it by loading + // { long, long } into memory, then performing a bitcast to { int, int, short, short, int } + + // 14a. Generate the type. + LLVMTypeRef lo = llvm_abi_type(c, ret_info->direct_pair.lo); + LLVMTypeRef hi = llvm_abi_type(c, ret_info->direct_pair.hi); + LLVMTypeRef struct_type = llvm_get_twostruct(c, lo, hi); + + // 14b. Use the coerce method to go from the struct to the actual type + // by storing the { lo, hi } struct to memory, then loading it + // again using a bitcast. + llvm_emit_convert_value_from_coerced(c, be_value, struct_type, call_value, return_type); break; } case ABI_ARG_EXPAND_COERCE: { - LLVMValueRef ret = llvm_emit_alloca_aligned(context, return_type, ""); - LLVMTypeRef coerce_type = llvm_get_coerce_type(context, ret_info); - LLVMValueRef coerce = LLVMBuildBitCast(context->builder, ret, coerce_type, ""); + // 15. Expand-coerce, this is similar to "direct pair", but looks like this: + // { lo, hi } set into { pad, lo, pad, hi } -> original type. - LLVMTypeRef lo_type = llvm_abi_type(context, ret_info->coerce_expand.lo); + // 15a. Create memory to hold the return type. + LLVMValueRef ret = llvm_emit_alloca_aligned(c, return_type, ""); + llvm_value_set_address(be_value, ret, return_type); - // Find the address to the low value + // 15b. "Convert" this return type pointer in memory to our coerce type which is { pad, lo, pad, hi } + LLVMTypeRef coerce_type = llvm_get_coerce_type(c, ret_info); + LLVMValueRef coerce = LLVMBuildBitCast(c->builder, ret, coerce_type, ""); + + // 15c. Find the type of the "lo" element. + LLVMTypeRef lo_type = llvm_abi_type(c, ret_info->coerce_expand.lo); + + // 15d. Find the address to the low value unsigned alignment; - LLVMValueRef lo = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.lo_index, + LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.lo_index, type_abi_alignment(return_type), ret_info->coerce_expand.offset_lo, &alignment); - // If there is only a single field, we simply store the value. + // 15e. If there is only a single field, we simply store the value, + // so { lo } set into { pad, lo, pad } -> original type. if (!ret_info->coerce_expand.hi) { - llvm_store_aligned(context, lo, call, alignment); - call = llvm_emit_load_aligned(context, llvm_get_type(context, return_type), ret, 0, ""); + // Here we do a store to call -> lo (leaving the rest undefined) + llvm_store_aligned(c, lo, call_value, alignment); + break; } - LLVMTypeRef hi_type = llvm_abi_type(context, ret_info->coerce_expand.hi); + // 15f. Calculate the hi type. + LLVMTypeRef hi_type = llvm_abi_type(c, ret_info->coerce_expand.hi); - LLVMValueRef lo_value = LLVMBuildExtractValue(context->builder, call, 0, ""); - LLVMValueRef hi_value = LLVMBuildExtractValue(context->builder, call, 1, ""); + // 15g. We can now extract { lo, hi } to lo_value and hi_value. + LLVMValueRef lo_value = LLVMBuildExtractValue(c->builder, call_value, 0, ""); + LLVMValueRef hi_value = LLVMBuildExtractValue(c->builder, call_value, 1, ""); - // Store the low value. - llvm_store_aligned(context, lo, lo_value, alignment); + // 15h. Store lo_value into the { pad, lo, pad, hi } struct. + llvm_store_aligned(c, lo, lo_value, alignment); - LLVMValueRef hi = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.hi_index, + // 15i. Calculate the address to the high value (like for the low in 15d. + LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, ret_info->coerce_expand.hi_index, type_abi_alignment(return_type), ret_info->coerce_expand.offset_hi, &alignment); - // Store the high value. - llvm_store_aligned(context, lo, lo_value, alignment); + // 15h. Store the high value. + llvm_store_aligned(c, hi, hi_value, alignment); - // Now we can get the actual return value. - call = llvm_emit_load_aligned(context, llvm_get_type(context, return_type), ret, 0, ""); break; } case ABI_ARG_DIRECT_COERCE: { - // TODO look at failable - LLVMTypeRef coerce = llvm_get_coerce_type(context, ret_info); - // Default behaviour. - if (!coerce || coerce == llvm_get_type(context, return_type)) break; - assert(!abi_info_should_flatten(ret_info)); - call = llvm_emit_convert_value_from_coerced(context, coerce, call, return_type); + // 16. A direct coerce, this is basically "call result" bitcast return type. + + // 16a. Get the type of the return. + LLVMTypeRef coerce = llvm_get_coerce_type(c, ret_info); + + // 16b. If we don't have any coerce type, or the actual LLVM types are the same, we're done. + if (!coerce || coerce == llvm_get_type(c, return_type)) + { + // 16c. We just set as a value in be_value. + llvm_value_set(be_value, call_value, return_type); + break; + } + // 16c. We use a normal bitcast coerce. + assert(!abi_info_should_flatten(ret_info) && "Did not expect flattening on return types."); + llvm_emit_convert_value_from_coerced(c, be_value, coerce, call_value, return_type); break; } } + // 17. Handle failables. if (signature->failable) { - LLVMBasicBlockRef after_block = llvm_basic_block_new(context, "after_check"); BEValue no_err; - llvm_value_set_bool(&no_err, llvm_emit_is_no_error(context, call)); - if (context->error_var) + + // 17a. If we used the error var as the indirect recipient, then that will hold the error. + // otherwise it's whatever value in be_value. + BEValue error_holder = *be_value; + if (error_var) { - LLVMBasicBlockRef error_block = llvm_basic_block_new(context, "error"); - llvm_emit_cond_br(context, &no_err, after_block, error_block); - llvm_emit_block(context, error_block); - LLVMBuildStore(context->builder, - call, - llvm_emit_bitcast(context, context->error_var, type_get_ptr(type_error))); - llvm_emit_br(context, context->catch_block); + llvm_value_set_address(&error_holder, c->error_var, type_error); + } + // 17b. Generate a boolean switch. + llvm_value_set_bool(&no_err, llvm_emit_is_no_error_value(c, &error_holder)); + + // 17c. If we have an error var, or we aren't interested in the error variable + // - then it's straightforward. We just jump to the catch block. + LLVMBasicBlockRef after_block = llvm_basic_block_new(c, "after_check"); + if (error_var || !c->error_var) + { + llvm_emit_cond_br(c, &no_err, after_block, c->catch_block); } else { - llvm_emit_cond_br(context, &no_err, after_block, context->catch_block); + // 17d. If we have an error var we need to assign, then we need to + // first jump to an error block, where we do the copy. + LLVMBasicBlockRef error_block = llvm_basic_block_new(c, "error"); + llvm_emit_cond_br(c, &no_err, after_block, error_block); + llvm_emit_block(c, error_block); + llvm_store_bevalue_aligned(c, c->error_var, be_value, 0); + // 17e. Then jump to the catch. + llvm_emit_br(c, c->catch_block); } - llvm_emit_block(context, after_block); - // If void, be_value contents should be skipped. - if (!signature->ret_abi_info) return; - llvm_value_set_address(be_value, return_param, expr->type); + // 17f. Emit the "after" block. + llvm_emit_block(c, after_block); + + // 17g. If void, be_value contents should be skipped. + if (!signature->ret_abi_info) + { + *be_value = (BEValue) {}; + return; + } + + // 17h. Assign the return param to be_value. + *be_value = synthetic_return_param; return; } - llvm_value_set(be_value, call, expr->type); + // 17i. The simple case here is where there is a normal return. + // In this case be_value already holds the result + return; } @@ -2786,7 +2919,7 @@ LLVMValueRef llvm_emit_call_intrinsic(GenContext *context, unsigned intrinsic_id BEValue llvm_emit_assign_expr(GenContext *c, BEValue *ref, Expr *expr, LLVMValueRef failable) { - llvm_value_addr(c, ref); + assert(ref->kind == BE_ADDRESS || ref->kind == BE_ADDRESS_FAILABLE); LLVMBasicBlockRef assign_block = NULL; PUSH_ERROR(); diff --git a/src/compiler/llvm_codegen_function.c b/src/compiler/llvm_codegen_function.c index ef1e3bc61..1d26ec404 100644 --- a/src/compiler/llvm_codegen_function.c +++ b/src/compiler/llvm_codegen_function.c @@ -631,7 +631,7 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl) case DECL_UNION: case DECL_ERR: llvm_get_type(context, decl->type); - TODO // Fix typeid + // TODO // Fix typeid break; case DECL_ENUM: TODO diff --git a/src/compiler/llvm_codegen_internal.h b/src/compiler/llvm_codegen_internal.h index 0f758c69b..236e6dc7e 100644 --- a/src/compiler/llvm_codegen_internal.h +++ b/src/compiler/llvm_codegen_internal.h @@ -97,6 +97,7 @@ typedef struct bool current_block_is_target : 1; bool did_call_stack_save : 1; LLVMTypeRef type_data_definitions[TYPE_KINDS]; + BuildTarget *build_target; } GenContext; // LLVM Intrinsics @@ -174,7 +175,7 @@ extern unsigned attribute_sext; // sign extend extern unsigned attribute_byval; // ByVal (param) extern unsigned attribute_inreg; // inreg (param) -void gencontext_begin_module(GenContext *context); +void gencontext_begin_module(GenContext *c); void gencontext_end_module(GenContext *context); // BE value @@ -212,7 +213,7 @@ void llvm_emit_block(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_br(GenContext *c, LLVMBasicBlockRef next_block); void llvm_emit_compound_stmt(GenContext *context, Ast *ast); void llvm_emit_and_set_decl_alloca(GenContext *c, Decl *decl); -LLVMValueRef llvm_emit_convert_value_from_coerced(GenContext *context, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type); +void llvm_emit_convert_value_from_coerced(GenContext *context, BEValue *result, LLVMTypeRef coerced, LLVMValueRef value, Type *original_type); void llvm_emit_function_body(GenContext *context, Decl *decl); void llvm_emit_function_decl(GenContext *c, Decl *decl); LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count); @@ -228,7 +229,8 @@ void llvm_emit_extern_decl(GenContext *context, Decl *decl); LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified); LLVMValueRef llvm_emit_int_comparison(GenContext *c, Type *lhs_type, Type *rhs_type, LLVMValueRef lhs_value, LLVMValueRef rhs_value, BinaryOp binary_op); -LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error); + +LLVMValueRef llvm_emit_is_no_error_value(GenContext *c, BEValue *value); void llvm_emit_len_for_expr(GenContext *c, BEValue *be_value, BEValue *expr_to_len); LLVMValueRef llvm_emit_load_aligned(GenContext *context, LLVMTypeRef type, LLVMValueRef pointer, AlignSize alignment, const char *name); void llvm_emit_local_var_alloca(GenContext *c, Decl *decl); diff --git a/src/compiler/llvm_codegen_module.c b/src/compiler/llvm_codegen_module.c index 793695395..755aad075 100644 --- a/src/compiler/llvm_codegen_module.c +++ b/src/compiler/llvm_codegen_module.c @@ -5,65 +5,65 @@ #include "llvm_codegen_internal.h" -void gencontext_begin_module(GenContext *context) +void gencontext_begin_module(GenContext *c) { - assert(!context->module && "Expected no module"); + assert(!c->module && "Expected no module"); - asprintf(&context->ir_filename, "%.*s.ll", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); - asprintf(&context->object_filename, "%.*s.o", (int)strlen(context->ast_context->file->name) - 3, context->ast_context->file->name); + c->ir_filename = strformat("%.*s.ll", (int)strlen(c->ast_context->file->name) - 3, c->ast_context->file->name); + c->object_filename = strformat("%.*s.o", (int)strlen(c->ast_context->file->name) - 3, c->ast_context->file->name); - const char *full_path = context->ast_context->file->full_path; - char *mangled_module_name = strformat("%s-%s", context->ast_context->module->name->module, context->ast_context->file->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)); - LLVMTypeRef options_type = LLVMInt8TypeInContext(context->context); + const char *full_path = c->ast_context->file->full_path; + char *mangled_module_name = strformat("%s-%s", c->ast_context->module->name->module, c->ast_context->file->name); + c->module = LLVMModuleCreateWithNameInContext(mangled_module_name, c->context); + LLVMSetModuleDataLayout(c->module, target_data_layout()); + LLVMSetSourceFileName(c->module, full_path, strlen(c->ast_context->file->full_path)); + LLVMTypeRef options_type = LLVMInt8TypeInContext(c->context); - if (build_options.pic == PIC_BIG || build_options.pic == PIC_SMALL) + if (c->build_target->pic == PIC_BIG || c->build_target->pic == PIC_SMALL) { static const char *pic_level = "PIC Level"; - LLVMMetadataRef setting = LLVMValueAsMetadata(LLVMConstInt(options_type, build_options.pic, false)); - LLVMAddModuleFlag(context->module, LLVMModuleFlagBehaviorOverride, pic_level, strlen(pic_level), setting); + LLVMMetadataRef setting = LLVMValueAsMetadata(LLVMConstInt(options_type, c->build_target->pic, false)); + LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pic_level, strlen(pic_level), setting); } - if (build_options.pie == PIE_BIG || build_options.pie == PIE_SMALL) + if (c->build_target->pie == PIE_BIG || c->build_target->pie == PIE_SMALL) { static const char *pie_level = "PIE Level"; - LLVMMetadataRef setting = LLVMValueAsMetadata(LLVMConstInt(options_type, build_options.pie, false)); - LLVMAddModuleFlag(context->module, LLVMModuleFlagBehaviorOverride, pie_level, strlen(pie_level), setting); + LLVMMetadataRef setting = LLVMValueAsMetadata(LLVMConstInt(options_type, c->build_target->pie, false)); + LLVMAddModuleFlag(c->module, LLVMModuleFlagBehaviorOverride, pie_level, strlen(pie_level), setting); } - LLVMSetTarget(context->module, build_target.target_triple); - if (build_options.debug_info != DEBUG_INFO_NONE) + LLVMSetTarget(c->module, build_target.target_triple); + if (c->build_target->debug_info != DEBUG_INFO_NONE) { - const char *filename = context->ast_context->file->name; - const char *dir_path = context->ast_context->file->dir_path; + const char *filename = c->ast_context->file->name; + const char *dir_path = c->ast_context->file->dir_path; // Set runtime version here. - context->debug.runtime_version = 1; - context->debug.builder = LLVMCreateDIBuilder(context->module); - context->debug.file = LLVMDIBuilderCreateFile(context->debug.builder, filename, strlen(filename), dir_path, strlen(dir_path)); + c->debug.runtime_version = 1; + c->debug.builder = LLVMCreateDIBuilder(c->module); + c->debug.file = LLVMDIBuilderCreateFile(c->debug.builder, filename, strlen(filename), dir_path, strlen(dir_path)); - bool is_optimized = build_options.optimization_level != OPTIMIZATION_NONE; + bool is_optimized = c->build_target->optimization_level != OPTIMIZATION_NONE; const char *dwarf_flags = ""; unsigned runtime_version = 1; - LLVMDWARFEmissionKind emission_kind = build_options.debug_info == DEBUG_INFO_FULL ? LLVMDWARFEmissionFull : LLVMDWARFEmissionLineTablesOnly; - 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, + LLVMDWARFEmissionKind emission_kind = c->build_target->debug_info == DEBUG_INFO_FULL ? LLVMDWARFEmissionFull : LLVMDWARFEmissionLineTablesOnly; + c->debug.compile_unit = LLVMDIBuilderCreateCompileUnit(c->debug.builder, LLVMDWARFSourceLanguageC, + c->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 #if LLVM_VERSION_MAJOR > 10 , "", 0, "", 0 #endif - ); + ); } // 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. - context->block_global_unique_count = 0; - context->ast_alloca_addr_space = target_alloca_addr_space(); + c->block_global_unique_count = 0; + c->ast_alloca_addr_space = target_alloca_addr_space(); VECEACH(compiler.type, i) { diff --git a/src/compiler/llvm_codegen_stmt.c b/src/compiler/llvm_codegen_stmt.c index 0279d718c..b38da0194 100644 --- a/src/compiler/llvm_codegen_stmt.c +++ b/src/compiler/llvm_codegen_stmt.c @@ -64,9 +64,9 @@ static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast) } else { - Type *type = decl->type->canonical; + Type *type = type_flatten(decl->type); // Normal case, zero init. - if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER) + if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER || type->type_kind == TYPE_VARARRAY) { llvm_emit_store(c, decl, LLVMConstNull(alloc_type)); } @@ -1029,7 +1029,7 @@ static inline void gencontext_emit_assume(GenContext *c, Expr *expr) static inline void gencontext_emit_assert_stmt(GenContext *c, Ast *ast) { - if (build_options.debug_mode) + if (c->build_target->feature.safe_mode) { BEValue value; llvm_emit_expr(c, &value, ast->assert_stmt.expr); @@ -1174,10 +1174,7 @@ void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *pani void llvm_emit_stmt(GenContext *c, Ast *ast) { EMIT_LOC(c, ast); - if (c->catch_block == NULL) - { - c->catch_block = llvm_basic_block_new(c, "stmt_catch"); - } + assert(!c->catch_block && "Did not expect a catch block here."); switch (ast->ast_kind) { case AST_DOCS: diff --git a/src/compiler/llvm_codegen_type.c b/src/compiler/llvm_codegen_type.c index de917c127..95c29b780 100644 --- a/src/compiler/llvm_codegen_type.c +++ b/src/compiler/llvm_codegen_type.c @@ -338,7 +338,9 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) case TYPE_ERR_UNION: { LLVMTypeRef elements[2] = { llvm_get_type(c, type_usize->canonical), llvm_get_type(c, type_usize->canonical) }; - return any_type->backend_type = LLVMStructTypeInContext(c->context, elements, 2, false); + LLVMTypeRef strukt = LLVMStructCreateNamed(c->context, "error_union"); + LLVMStructSetBody(strukt, elements, 2, false); + return any_type->backend_type = strukt; } case TYPE_STRUCT: case TYPE_UNION: @@ -380,7 +382,17 @@ LLVMTypeRef llvm_get_type(GenContext *c, Type *any_type) return any_type->backend_type = array_type; } case TYPE_VARARRAY: - return any_type->backend_type = llvm_get_type(c, type_get_ptr(any_type->array.base)); + { + LLVMTypeRef size_type = llvm_get_type(c, type_usize); + LLVMTypeRef capacity_type = llvm_get_type(c, type_usize); + LLVMTypeRef func_type = llvm_get_type(c, type_voidptr); + LLVMTypeRef ptr_type = LLVMPointerType(llvm_get_type(c, any_type->array.base), 0); + LLVMTypeRef types[4] = { size_type, capacity_type, func_type, ptr_type }; + LLVMTypeRef array_type = LLVMStructCreateNamed(c->context, any_type->name); + LLVMStructSetBody(array_type, types, 4, false); + any_type->backend_aux_type = array_type; + return any_type->backend_type = LLVMPointerType(array_type, 0); + } case TYPE_VECTOR: return any_type->backend_type = LLVMVectorType(llvm_get_type(c, any_type->vector.base), any_type->vector.len); case TYPE_COMPLEX: diff --git a/src/compiler/parse_expr.c b/src/compiler/parse_expr.c index 1e1898e41..fda8baf43 100644 --- a/src/compiler/parse_expr.c +++ b/src/compiler/parse_expr.c @@ -277,6 +277,7 @@ static Expr *parse_post_unary(Context *context, Expr *left) Expr *unary = EXPR_NEW_TOKEN(EXPR_POST_UNARY, context->tok); unary->post_expr.expr = left; unary->post_expr.operator = post_unaryop_from_token(context->tok.type); + unary->span.loc = left->span.loc; advance(context); return unary; } @@ -331,6 +332,7 @@ static Expr *parse_grouping_expr(Context *context, Expr *left) * initializer * : initializer_list * | expr + * | void * ; * * @param context @@ -338,6 +340,14 @@ static Expr *parse_grouping_expr(Context *context, Expr *left) */ Expr *parse_initializer(Context *context) { + if (TOKEN_IS(TOKEN_VOID)) + { + Expr *expr = EXPR_NEW_TOKEN(EXPR_UNDEF, context->tok); + expr->type = type_void; + expr->resolve_status = RESOLVE_DONE; + advance(context); + return expr; + } if (TOKEN_IS(TOKEN_LBRACE)) { return parse_initializer_list(context); diff --git a/src/compiler/parse_global.c b/src/compiler/parse_global.c index 7e01ea41d..191ef6b74 100644 --- a/src/compiler/parse_global.c +++ b/src/compiler/parse_global.c @@ -3,37 +3,60 @@ static Decl *parse_const_declaration(Context *context, Visibility visibility); -static bool context_next_up_is_type_with_path(Context *context) +/** + * Walk forward through the token stream to identify a type on the format: foo::bar::Type + * + * @return true if there is a type at the end. + */ +static bool context_next_is_type_with_path_prefix(Context *context) { + // We assume it's called after "foo::" parsing. + assert(context->tok.type == TOKEN_IDENT && context->next_tok.type == TOKEN_SCOPE); + TokenId current = context->next_tok.id; - TokenType tok = context->next_tok.type; while (1) { + TokenType tok; + + // 1. Step past the '::' and any following comment (doc comments are not allowed here!) while (1) { current.index += 1; tok = TOKITYPE(current); - if (tok == TOKEN_COMMENT || tok == TOKEN_DOC_COMMENT) continue; - if (tok != TOKEN_IDENT) goto DONE_CHECK; - break; + if (tok != TOKEN_COMMENT) break; } + + // 2. Check that we have an ident, otherwise if + // we see a type token, we're done and return true + // on any other + if (tok != TOKEN_IDENT) return tok == TOKEN_TYPE_IDENT; + + // 3. Now we've confirmed that there is an ident, step past it + // and any following comments. while (1) { current.index += 1; tok = TOKITYPE(current); - if (tok == TOKEN_COMMENT || tok == TOKEN_DOC_COMMENT) continue; - if (tok != TOKEN_SCOPE) goto DONE_CHECK; - break; + if (tok != TOKEN_COMMENT) break; } + + // 4. If we don't see '::' after an ident we're done. + // And we know it's not a type. + if (tok != TOKEN_SCOPE) return false; + + // 5. Do another pass } - DONE_CHECK: - return tok == TOKEN_TYPE_IDENT; } /** - * Walk until we find the first top level construct. - * (Note that this is the slow path, so no need to inline) + * Walk until we find the first top level construct, the current heuristic is this: + * public, typedef, struct, import, union, extern, enum, generic, attribute, define + * are *always* sync points. + * + * func, any type, CT_IDENT, CT_TYPE_IDENT, $if, $for, $switch, generic, + * doc comment start, asm, typeof, TYPE_IDENT, local, const, IDENT + * - are sync points only if they appear in the first column. */ void recover_top_level(Context *context) { @@ -43,17 +66,55 @@ void recover_top_level(Context *context) switch (context->tok.type) { case TOKEN_PUBLIC: - case TOKEN_FUNC: - case TOKEN_CONST: case TOKEN_TYPEDEF: case TOKEN_STRUCT: case TOKEN_IMPORT: case TOKEN_UNION: - case TOKEN_MACRO: case TOKEN_EXTERN: case TOKEN_ENUM: - case TOKEN_ERR: + case TOKEN_GENERIC: + case TOKEN_ATTRIBUTE: + case TOKEN_DEFINE: return; + case TOKEN_IDENT: // Incr arrays only + case TOKEN_LOCAL: + case TOKEN_CONST: + case TOKEN_ASM: + case TOKEN_TYPEOF: + case TOKEN_CT_ASSERT: + case TOKEN_CT_TYPE_IDENT: + case TOKEN_DOCS_START: + case TOKEN_TYPE_IDENT: + case TOKEN_CT_IDENT: + case TOKEN_CT_IF: + case TOKEN_CT_FOR: + case TOKEN_CT_SWITCH: + case TOKEN_FUNC: + case TOKEN_VOID: + case TOKEN_BOOL: + case TOKEN_CHAR: + case TOKEN_DOUBLE: + case TOKEN_FLOAT: + case TOKEN_HALF: + case TOKEN_ICHAR: + case TOKEN_INT: + case TOKEN_IPTR: + case TOKEN_IPTRDIFF: + case TOKEN_ISIZE: + case TOKEN_LONG: + case TOKEN_SHORT: + case TOKEN_UINT: + case TOKEN_ULONG: + case TOKEN_UPTR: + case TOKEN_UPTRDIFF: + case TOKEN_USHORT: + case TOKEN_USIZE: + case TOKEN_QUAD: + case TOKEN_TYPEID: + // Only recover if this is in the first col. + if (TOKLOC(context->tok)->col == 1) return; + advance(context); + break; default: advance(context); break; @@ -886,7 +947,7 @@ bool parse_next_is_decl(Context *context) | (next_tok == TOKEN_CONST_IDENT); case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; - return context_next_up_is_type_with_path(context); + return context_next_is_type_with_path_prefix(context); default: return false; } @@ -918,10 +979,10 @@ bool parse_next_is_case_type(Context *context) case TOKEN_CT_TYPE_IDENT: case TOKEN_ERR: case TOKEN_TYPEID: - return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_COLON) | (next_tok == TOKEN_EOS); + return (next_tok == TOKEN_STAR) | (next_tok == TOKEN_LBRACKET) | (next_tok == TOKEN_COMMA) | (next_tok == TOKEN_COLON) | (next_tok == TOKEN_EOS); case TOKEN_IDENT: if (next_tok != TOKEN_SCOPE) return false; - return context_next_up_is_type_with_path(context); + return context_next_is_type_with_path_prefix(context); default: return false; } @@ -1281,14 +1342,16 @@ static inline Decl *parse_generics_declaration(Context *context, Visibility visi advance(context); COMMA_RPAREN_OR(poisoned_decl); } - CONSUME_OR(TOKEN_LBRACE, poisoned_decl); Ast **cases = NULL; - if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_decl; + if (!parse_switch_body(context, &cases, TOKEN_CASE, TOKEN_DEFAULT, true)) return poisoned_decl; decl->generic_decl.cases = cases; decl->generic_decl.parameters = parameters; return decl; } +/** + * define_decl ::= DEFINE path? (IDENT | TYPE_IDENT | CONST_IDENT) '(' expr (',' expr)* ')' as (IDENT | TYPE_IDENT | CONST_IDENT) + */ static inline Decl *parse_define(Context *context, Visibility visibility) { if (context->next_tok.type == TOKEN_CT_TYPE_IDENT || context->next_tok.type == TOKEN_CT_IDENT) diff --git a/src/compiler/parse_stmt.c b/src/compiler/parse_stmt.c index a29f6f1b8..c82e1e426 100644 --- a/src/compiler/parse_stmt.c +++ b/src/compiler/parse_stmt.c @@ -244,7 +244,7 @@ static inline Ast* parse_catch_stmt(Context *context) if (TOKEN_IS(TOKEN_LBRACE) && (context->next_tok.type == TOKEN_CASE || context->next_tok.type == TOKEN_DEFAULT)) { catch_stmt->catch_stmt.is_switch = true; - if (!parse_switch_body(context, &catch_stmt->catch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast; + if (!parse_switch_body(context, &catch_stmt->catch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT, false)) return poisoned_ast; return catch_stmt; } @@ -341,7 +341,7 @@ static bool parse_type_or_expr(Context *context, TypeInfo **type_info, Expr **ex * | CAST type ':' cast_stmts * ; */ -static inline Ast* parse_case_stmt(Context *context, TokenType case_type, TokenType default_type) +static inline Ast* parse_case_stmt(Context *context, TokenType case_type, TokenType default_type, bool allow_multiple_values) { Ast *ast = AST_NEW_TOKEN(AST_CASE_STMT, context->tok); advance(context); @@ -357,6 +357,19 @@ static inline Ast* parse_case_stmt(Context *context, TokenType case_type, TokenT { ast->case_stmt.expr = expr; } + if (type && allow_multiple_values && try_consume(context, TOKEN_COMMA)) + { + ast->case_stmt.is_type_list = true; + TypeInfo **type_infos = NULL; + vec_add(type_infos, type); + while (1) + { + type = TRY_TYPE_OR(parse_type(context), false); + vec_add(type_infos, type); + if (!try_consume(context, TOKEN_COMMA)) break; + } + ast->case_stmt.type_infos = type_infos; + } TRY_CONSUME(TOKEN_COLON, "Missing ':' after case"); extend_ast_with_prev_token(context, ast); ast->case_stmt.body = TRY_AST(parse_case_stmts(context, case_type, default_type)); @@ -386,7 +399,8 @@ static inline Ast *parse_default_stmt(Context *context, TokenType case_type, Tok * | default_stmt switch body * ; */ -bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type) +bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type, + bool allow_multiple_values) { CONSUME_OR(TOKEN_LBRACE, false); while (!try_consume(context, TOKEN_RBRACE)) @@ -395,7 +409,7 @@ bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, Toke TokenType next = context->tok.type; if (next == case_type) { - result = TRY_AST_OR(parse_case_stmt(context, case_type, default_type), false); + result = TRY_AST_OR(parse_case_stmt(context, case_type, default_type, allow_multiple_values), false); } else if (next == default_type) { @@ -425,7 +439,7 @@ static inline Ast* parse_switch_stmt(Context *context) switch_ast->switch_stmt.cond = TRY_EXPR_OR(parse_decl_expr_list(context), poisoned_ast); CONSUME_OR(TOKEN_RPAREN, poisoned_ast); - if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT)) return poisoned_ast; + if (!parse_switch_body(context, &switch_ast->switch_stmt.cases, TOKEN_CASE, TOKEN_DEFAULT, false)) return poisoned_ast; return switch_ast; } @@ -892,7 +906,7 @@ static inline Ast* parse_ct_switch_stmt(Context *context) TokenType next = context->tok.type; if (next == TOKEN_CT_CASE) { - result = TRY_AST_OR(parse_case_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT), poisoned_ast); + result = TRY_AST_OR(parse_case_stmt(context, TOKEN_CT_CASE, TOKEN_CT_DEFAULT, true), poisoned_ast); } else if (next == TOKEN_CT_DEFAULT) { diff --git a/src/compiler/parser_internal.h b/src/compiler/parser_internal.h index fb545dea7..8a2a6330c 100644 --- a/src/compiler/parser_internal.h +++ b/src/compiler/parser_internal.h @@ -47,7 +47,9 @@ void recover_top_level(Context *context); Expr *parse_decl_expr_list(Context *context); Ast* parse_compound_stmt(Context *context); Ast *parse_jump_stmt_no_eos(Context *context); -bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type); + +bool parse_switch_body(Context *context, Ast ***cases, TokenType case_type, TokenType default_type, + bool allow_multiple_values); Expr *parse_expression_list(Context *context); Decl *parse_decl_after_type(Context *context, bool local, TypeInfo *type); bool parse_param_list(Context *context, Expr ***result, bool allow_type, TokenType end_type); diff --git a/src/compiler/sema_casts.c b/src/compiler/sema_casts.c index eaf67bc6c..f099eeaf3 100644 --- a/src/compiler/sema_casts.c +++ b/src/compiler/sema_casts.c @@ -5,8 +5,6 @@ #include "compiler_internal.h" #include "bigint.h" -#define EXIT_T_MISMATCH() return sema_type_mismatch(context, left, canonical, cast_type) - #define FLOAT32_LIMIT 340282346638528859811704183484516925440.0000000000000000 #define FLOAT64_LIMIT 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.0000000000000000 #define FLOAT16_LIMIT 65504 @@ -30,7 +28,7 @@ static inline bool insert_runtime_cast_unless_const(Expr *expr, CastKind kind, T return insert_cast(expr, kind, type); } -static bool sema_type_mismatch(Context *context, Expr *expr, Type *type, CastType cast_type) +static bool sema_type_mismatch(Expr *expr, Type *type, CastType cast_type) { Type *expr_type = expr->type; if (expr_type == type_typeinfo) @@ -453,13 +451,6 @@ CastKind cast_to_bool_kind(Type *type) UNREACHABLE } -bool cast_to_bool_implicit(Expr *expr) -{ - assert(expr->resolve_status == RESOLVE_DONE); - if (cast_to_bool_kind(expr->type) == CAST_ERROR) return false; - return cast(expr, type_bool); -} - bool cast_may_explicit(Type *from_type, Type *to_type) { // 1. We flatten the distinct types, since they should be freely convertible @@ -546,6 +537,9 @@ bool cast_may_explicit(Type *from_type, Type *to_type) UNREACHABLE } +/** + * Can the conversion occur implicitly? + */ bool cast_may_implicit(Type *from_type, Type *to_type) { Type *from = from_type->canonical; @@ -564,8 +558,6 @@ bool cast_may_implicit(Type *from_type, Type *to_type) if (type_is_float(from)) { // This works because the type_size of FXX = 0 - unsigned a = type_size(to); - unsigned b = type_size(from); return type_size(to) >= type_size(from); } return false; @@ -654,12 +646,17 @@ bool cast_may_implicit(Type *from_type, Type *to_type) } + // 8. Check if we may cast this to bool. It is safe for many types. if (to->type_kind == TYPE_BOOL) { return cast_to_bool_kind(from) != CAST_ERROR; } - // TODO struct embedding + // 9. Substruct cast, if the first member is inline, see if we can cast to this member. + if (type_is_substruct(from)) + { + return cast_may_implicit(from->decl->strukt.members[0]->type, to); + } return false; } @@ -796,14 +793,6 @@ bool cast(Expr *expr, Type *to_type) if (canonical->type_kind == TYPE_ERRTYPE) return insert_cast(expr, CAST_EUER, to_type); break; case TYPE_IXX: - // Compile time integers may convert into ints, floats, bools - if (expr->expr_kind != EXPR_CONST && !expr->reeval) - { - expr->resolve_status = RESOLVE_NOT_DONE; - expr->reeval = true; - TODO - // return sema_analyse_expr(context, to_type, expr); - } if (type_is_integer(canonical)) return int_literal_to_int(expr, canonical, to_type); if (type_is_float(canonical)) return int_literal_to_float(expr, canonical, to_type); if (canonical == type_bool) return int_literal_to_bool(expr, to_type); @@ -826,14 +815,6 @@ bool cast(Expr *expr, Type *to_type) if (canonical->type_kind == TYPE_POINTER) return int_to_pointer(expr, to_type); break; case ALL_FLOATS: - // Compile time integers may convert into ints, floats, bools - if (from_type->type_kind == TYPE_FXX && expr->expr_kind != EXPR_CONST && !expr->reeval) - { - expr->resolve_status = RESOLVE_NOT_DONE; - expr->reeval = true; - TODO -// return sema_analyse_expr(context, to_type, expr); - } if (type_is_integer(canonical)) return float_to_integer(expr, canonical, to_type); if (canonical == type_bool) return float_to_bool(expr, to_type); if (type_is_float(canonical)) return float_to_float(expr, canonical, to_type); @@ -860,9 +841,8 @@ bool cast(Expr *expr, Type *to_type) if (canonical->type_kind == TYPE_ARRAY || canonical->type_kind == TYPE_STRUCT || canonical->type_kind == TYPE_UNION) { return insert_cast(expr, CAST_STST, to_type); - } + } // Starting in a little while... break; - UNREACHABLE case TYPE_STRLIT: canonical = type_flatten(canonical); if (canonical->type_kind == TYPE_POINTER) return insert_cast(expr, CAST_STRPTR, to_type); diff --git a/src/compiler/sema_decls.c b/src/compiler/sema_decls.c index 5dd22f2cd..038adf181 100644 --- a/src/compiler/sema_decls.c +++ b/src/compiler/sema_decls.c @@ -951,10 +951,64 @@ static inline bool sema_analyse_global(Context *context, Decl *decl) static inline bool sema_analyse_generic(Context *context, Decl *decl) { - TODO + // 1. If it has a return type, make sure it resolves. + if (decl->generic_decl.rtype && !sema_resolve_type_info(context, decl->generic_decl.rtype)) return false; + + unsigned param_count = vec_size(decl->generic_decl.parameters); + if (param_count < 1) + { + SEMA_ERROR(decl, "A generic function needs at least 1 parameter."); + return false; + } + Ast **cases = decl->generic_decl.cases; + + bool default_has_been_found = false; + VECEACH(cases, i) + { + Ast *generic_case = cases[i]; + if (generic_case->ast_kind == AST_CASE_STMT) + { + if (!generic_case->case_stmt.is_type) + { + SEMA_ERROR(generic_case->case_stmt.expr, "Expected a type as the argument."); + return false; + } + if (!generic_case->case_stmt.is_type_list) + { + TypeInfo **type_infos = VECNEW(TypeInfo *, 2); + vec_add(type_infos, generic_case->case_stmt.type_info); + generic_case->case_stmt.type_infos = type_infos; + generic_case->case_stmt.is_type_list = true; + } + TypeInfo **type_infos = generic_case->case_stmt.type_infos; + unsigned args = vec_size(type_infos); + for (unsigned j = 0; j < args; j++) + { + if (!sema_resolve_type_info(context, type_infos[j])) return false; + } + if (args != param_count) + { + if (param_count == 1) + { + SEMA_ERROR(type_infos[1], "Expected a single type as the argument."); + return false; + } + SEMA_ERROR(type_infos[args - 1], "Expected %d types in the case statement.", param_count); + return false; + } + continue; + } + assert(generic_case->ast_kind == AST_DEFAULT_STMT); + if (default_has_been_found) + { + SEMA_ERROR(generic_case, "More than one default statement found."); + return false; + } + } return true; } + static inline bool sema_analyse_define(Context *context, Decl *decl) { Path *path = decl->generic_decl.path; @@ -963,17 +1017,24 @@ static inline bool sema_analyse_define(Context *context, Decl *decl) } - -static inline bool sema_analyse_error(Context *context __unused, Decl *decl) +/** + * Semantic analysis on an error first checks the internals as if it was + * a struct, then checks that the size is not exceeded and adds padding. + */ +static inline bool sema_analyse_error(Context *context, Decl *decl) { + // 1. Step one is to analyze the error as it it was a regular struct. if (!sema_analyse_struct_union(context, decl)) return false; - ByteSize error_full_size = type_size(type_voidptr); + + // 2. Because an error is always pointer sized, we check so that it isn't exceeded. + ByteSize error_full_size = type_size(type_uptr); if (decl->strukt.size > error_full_size) { SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", error_full_size, decl->strukt.size); return false; } + // 3. If the size is smaller than than pointer sized, we add padding. if (decl->strukt.size < error_full_size) { decl->strukt.padding = error_full_size - decl->strukt.size; diff --git a/src/compiler/sema_expr.c b/src/compiler/sema_expr.c index e765e0a8d..df8f9efd0 100644 --- a/src/compiler/sema_expr.c +++ b/src/compiler/sema_expr.c @@ -191,7 +191,6 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr case DECL_VAR: break; case DECL_DISTINCT: - TODO case DECL_TYPEDEF: UNREACHABLE case DECL_POISONED: @@ -273,7 +272,7 @@ static ExprFailableStatus expr_is_failable(Expr *expr) } -static inline bool sema_type_error_on_binop(Context *context, Expr *expr) +static inline bool sema_type_error_on_binop(Expr *expr) { const char *c = token_type_to_string(binaryop_to_token(expr->binary_expr.operator)); SEMA_ERROR(expr, "%s is not defined in the expression '%s' %s '%s'.", @@ -932,7 +931,22 @@ static inline bool sema_expr_analyse_var_call(Context *context, Type *to, Expr * } -static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Expr *expr) { TODO }; +static inline bool sema_expr_analyse_generic_call(Context *context, Type *to, Decl *decl, Expr *expr) +{ + Expr **arguments = expr->call_expr.arguments; + TokenId *parameter_list = decl->generic_decl.parameters; + if (vec_size(parameter_list) != vec_size(arguments)) + { + SEMA_ERROR(expr, "Expected %d parameter(s) to the generic function.", vec_size(parameter_list)); + return false; + } + VECEACH(arguments, i) + { + if (!sema_analyse_expr(context, NULL, arguments[i])) return false; + } + + TODO +}; @@ -1244,7 +1258,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr decl = func_expr->access_expr.ref; if (decl->decl_kind == DECL_FUNC) { - expr->call_expr.is_struct_function = true; + expr->call_expr.is_type_method = true; struct_var = expr_new(EXPR_UNARY, func_expr->access_expr.parent->span); struct_var->unary_expr.expr = func_expr->access_expr.parent; struct_var->unary_expr.operator = UNARYOP_ADDR; @@ -1279,7 +1293,7 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr case DECL_MACRO: UNREACHABLE case DECL_GENERIC: - return sema_expr_analyse_generic_call(context, to, expr); + return sema_expr_analyse_generic_call(context, to, decl, expr); case DECL_POISONED: return false; default: @@ -1855,7 +1869,8 @@ static inline bool sema_expr_analyse_member_access(Context *context, Expr *expr) } if (name == kw_nameof) { - TODO + expr_rewrite_to_string(expr, ref->name); + return true; } if (name == kw_qnameof) { @@ -1942,6 +1957,7 @@ CHECK_DEEPER: switch (type->type_kind) { case TYPE_SUBARRAY: + case TYPE_VARARRAY: if (kw == kw_sizeof) { expr_rewrite_to_int_const(expr, type_usize, type_size(type)); @@ -1975,7 +1991,7 @@ CHECK_DEEPER: break; default: NO_MATCH: - SEMA_ERROR(expr, "Cannot access '%s' on '%s'", TOKSTR(expr->access_expr.sub_element), type_to_error_string(parent_type)); + SEMA_ERROR(expr, "There is no member or method '%s' on '%s'", TOKSTR(expr->access_expr.sub_element), type_to_error_string(parent_type)); return false; } @@ -3183,7 +3199,7 @@ static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *righ { if (!error_message) { - return sema_type_error_on_binop(context, parent); + return sema_type_error_on_binop(parent); } SEMA_ERROR(parent, error_message, type_to_error_string(left_type), type_to_error_string(right_type)); return false; @@ -3191,7 +3207,7 @@ static bool binary_arithmetic_promotion(Context *context, Expr *left, Expr *righ return cast_implicit(left, max) & cast_implicit(right, max); } -static bool sema_check_int_type_fit(Context *context, Expr *expr, Type *target_type) +static bool sema_check_int_type_fit(Expr *expr, Type *target_type) { if (!target_type) return true; Type *type = expr->type->canonical; @@ -3236,7 +3252,7 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr * expr_unify_binary_properties(expr, left, right); // 3d. Otherwise check against assigned type so for example short x = &p - &q is an error. - return sema_check_int_type_fit(context, expr, to); + return sema_check_int_type_fit(expr, to); } right_type = right->type->canonical; @@ -3543,7 +3559,7 @@ static bool sema_expr_analyse_bit(Context *context, Type *to, Expr *expr, Expr * // 2. Check that both are integers. if (!both_any_integer(left, right)) { - return sema_type_error_on_binop(context, expr); + return sema_type_error_on_binop(expr); } // 3. Promote to the same type. @@ -3596,7 +3612,7 @@ static bool sema_expr_analyse_shift(Context *context, Type *to, Expr *expr, Expr // 3. Only integers may be shifted. if (!both_any_integer(left, right)) { - return sema_type_error_on_binop(context, expr); + return sema_type_error_on_binop(expr); } // 4. Promote lhs using the usual numeric promotion. @@ -3697,7 +3713,7 @@ static bool sema_expr_analyse_shift_assign(Context *context, Expr *expr, Expr *l } // 3. Only integers may be shifted. - if (!both_any_integer(left, right)) return sema_type_error_on_binop(context, expr); + if (!both_any_integer(left, right)) return sema_type_error_on_binop(expr); // 4. For a constant right hand side we will make a series of checks. if (is_const(right)) @@ -4673,7 +4689,6 @@ static Expr *expr_copy_from_macro(Context *context, Expr *source_expr) MACRO_COPY_TYPE(expr->typeid_expr); return expr; case EXPR_IDENTIFIER: - // TODO return expr; case EXPR_CALL: MACRO_COPY_EXPR(expr->call_expr.function); @@ -4909,25 +4924,8 @@ static Ast *ast_copy_from_macro(Context *context, Ast *source) -static inline bool sema_expr_analyse_macro_ident(Context *context, Type *to, Expr *expr) -{ - return false; - /* - Expr *inner = expr->macro_ident; - switch (inner->expr_kind) - { - case EXPR_CALL: - return sema_expr_analyse_macro_call(context, to, expr, inner); - case EXPR_ACCESS: - case EXPR_IDENTIFIER: - // Allow @f unrolling? - default: - SEMA_ERROR(expr, "Expected a macro name after '@'"); - return false; - }*/ -} -static inline bool sema_expr_analyse_type(Context *context, Type *to, Expr *expr) +static inline bool sema_expr_analyse_type(Context *context, Expr *expr) { if (!sema_resolve_type_info(context, expr->typeid_expr)) { @@ -5121,7 +5119,7 @@ static inline bool sema_analyse_expr_dispatch(Context *context, Type *to, Expr * if (expr->expr_kind == EXPR_UNARY) expr->failable = expr->unary_expr.expr->failable; return true; case EXPR_TYPEID: - return sema_expr_analyse_type(context, to, expr); + return sema_expr_analyse_type(context, expr); case EXPR_CONST_IDENTIFIER: case EXPR_IDENTIFIER: case EXPR_MACRO_IDENTIFIER: diff --git a/src/compiler/sema_passes.c b/src/compiler/sema_passes.c index 242372f94..74828b545 100644 --- a/src/compiler/sema_passes.c +++ b/src/compiler/sema_passes.c @@ -154,6 +154,10 @@ void sema_analysis_pass_decls(Context *context) { sema_analyse_decl(context, context->macros[i]); } + VECEACH(context->generics, i) + { + sema_analyse_decl(context, context->generics[i]); + } VECEACH(context->methods, i) { sema_analyse_decl(context, context->methods[i]); diff --git a/src/compiler/sema_stmts.c b/src/compiler/sema_stmts.c index b68450e80..100ec9bbb 100644 --- a/src/compiler/sema_stmts.c +++ b/src/compiler/sema_stmts.c @@ -141,6 +141,18 @@ static inline bool sema_analyse_block_return_stmt(Context *context, Ast *stateme return true; } +/** + * We have the following possibilities: + * 1. return + * 2. return + * 3. return + * + * If we are in a block or a macro expansion we need to handle it differently. + * + * @param context + * @param statement + * @return + */ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) { // This might be a return in a function block or a macro which must be treated differently. @@ -148,18 +160,18 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) { return sema_analyse_block_return_stmt(context, statement); } + // 1. We mark that the current scope ends with a jump. context->current_scope->jump_end = true; + Type *expected_rtype = context->rtype; + assert(expected_rtype && "We should always have known type from a function return."); + Expr *return_expr = statement->return_stmt.expr; statement->return_stmt.defer = context->current_scope->defer_last; + + // 2. First handle the plain return. if (return_expr == NULL) { - if (!expected_rtype) - { - assert(context->evaluating_macro); - context->rtype = type_void; - return true; - } if (expected_rtype->canonical != type_void) { SEMA_ERROR(statement, "Expected to return a result of type %s.", type_to_error_string(expected_rtype)); @@ -167,55 +179,62 @@ static inline bool sema_analyse_return_stmt(Context *context, Ast *statement) } return true; } - if (expected_rtype == type_void && !context->failable_return) - { - SEMA_ERROR(statement, "You can't return a value from a void function, you need to add a return type."); - return false; - } + + // 3. Evaluate the return value to be the expected return type. if (!sema_analyse_expr_of_required_type(context, expected_rtype, return_expr, context->failable_return)) return false; - if (!expected_rtype) - { - assert(context->evaluating_macro); - context->rtype = type_void; - context->active_function_for_analysis->func.function_signature.rtype->type->canonical = statement->return_stmt.expr->type->canonical; - return true; - } + assert(statement->return_stmt.expr->type->canonical == expected_rtype->canonical); + return true; } -static inline bool sema_analyse_unreachable_stmt(Context *context, Ast *statement) +static inline bool sema_analyse_unreachable_stmt(Context *context) { context->current_scope->jump_end = true; return true; } +/** + * An decl-expr-list is a list of a mixture of declarations and expressions. + * The last declaration or expression is propagated. So for example: + * + * int a = 3, b = 4, float c = 4.0 + * + * In this case the final value is 4.0 and the type is float. + */ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) { assert(expr->expr_kind == EXPR_DECL_LIST); Ast **dexprs = expr->dexpr_list_expr; unsigned entries = vec_size(dexprs); - for (unsigned i = 0; i < entries; i++) - { - Ast *ast = dexprs[i]; - if (!sema_analyse_statement(context, ast)) - { - return false; - } - } + + // 1. Special case, there are no entries, so the type is void if (entries == 0) { expr_set_type(expr, type_void); return true; } + + // 2. Walk through each of our declarations / expressions as if they were regular statements. + for (unsigned i = 0; i < entries; i++) + { + if (!sema_analyse_statement(context, dexprs[i])) return false; + } + + // 3. We now look at the final expression and copy its type as the type of the entire list. + // There is a subtle difference here. The expression might have a different original type + // than the expression's type. In most (all?) cases this is isn't something one uses, + // but this makes sure that analysis is correct. Ast *last = dexprs[entries - 1]; switch (last->ast_kind) { case AST_DECLARE_STMT: + // In the declaration case, the value is the type of the declaration. expr_set_type(expr, last->declare_stmt->type); break; case AST_EXPR_STMT: + // In the expression case, *copy* the expression's types. expr_copy_types(expr, last->expr_stmt); break; default: @@ -224,29 +243,50 @@ static inline bool sema_analyse_decl_expr_list(Context *context, Expr *expr) return true; } - +/** + * Analyse a conditional expression: + * + * 1. The middle statement in a for loop + * 2. The expression in an if, while + * 3. The expression in a switch + * + * @param context the current context + * @param expr the conditional to evaluate + * @param cast_to_bool if the result is to be cast to bool after + * @return true if it passes analysis. + */ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_bool) { - assert(expr->expr_kind == EXPR_DECL_LIST); + assert(expr->expr_kind == EXPR_DECL_LIST && "Conditional expressions should always be of type EXPR_DECL_LIST"); + size_t size = vec_size(expr->dexpr_list_expr); - if (!size) + + // 1. Analyse the declaration list. + if (!sema_analyse_decl_expr_list(context, expr)) return false; + + // 2. If we get "void", either through a void call or an empty list, + // signal that. + if (expr->type->type_kind == TYPE_VOID) { - SEMA_ERROR(expr, "Expected a boolean expression"); + SEMA_ERROR(expr, cast_to_bool ? "Expected a boolean expression." : "Expected an expression resulting in a value."); return false; } - if (!sema_analyse_decl_expr_list(context, expr)) return false; + // 3. We look at the last element (which is guaranteed to exist because + // the type was not void. + Ast *last = VECLAST(expr->dexpr_list_expr); - Ast *last = expr->dexpr_list_expr[size - 1]; switch (last->ast_kind) { case AST_EXPR_STMT: + // 3a. Check for failables in case of an expression. if (last->expr_stmt->failable) { SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", type_to_error_string(last->expr_stmt->type), - cast_to_bool ? "bool" : type_to_error_string(last->expr_stmt->type)); + cast_to_bool ? "bool" : type_to_error_string(last->expr_stmt->type)); } + // 3b. Cast to bool if that is needed if (cast_to_bool) { if (!cast_implicit(last->expr_stmt, type_bool)) return false; @@ -254,19 +294,23 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_ return true; case AST_DECLARE_STMT: { + // 3c. The declaration case Decl *decl = last->declare_stmt; Expr *init = decl->var.init_expr; + // 3d. We expect an initialization for the last declaration. if (!init) { SEMA_ERROR(last, "Expected a declaration with initializer."); return false; } + // 3e. Expect that it isn't a failable if (init->failable && !decl->var.unwrap) { SEMA_ERROR(last, "'%s!' cannot be converted into '%s'.", type_to_error_string(last->expr_stmt->type), cast_to_bool ? "bool" : type_to_error_string(init->type)); } + // TODO document if (!decl->var.unwrap && cast_to_bool && cast_to_bool_kind(decl->var.type_info->type) == CAST_ERROR) { SEMA_ERROR(last->declare_stmt->var.init_expr, "The expression needs to be convertible to a boolean."); @@ -279,72 +323,146 @@ static inline bool sema_analyse_cond(Context *context, Expr *expr, bool cast_to_ } } +static inline bool sema_analyse_stmt_placement(Expr *cond, Ast *stmt) +{ + if (stmt->ast_kind == AST_COMPOUND_STMT) return true; + SourceLocation *end_of_cond = TOKILOC(cond->span.end_loc); + SourceLocation *start_of_then = TOKILOC(stmt->span.loc); + return end_of_cond->line == start_of_then->line; +} + +/** + * Check "while" statement, including end of line placement of a single statement. + */ static inline bool sema_analyse_while_stmt(Context *context, Ast *statement) { Expr *cond = statement->while_stmt.cond; Ast *body = statement->while_stmt.body; - context_push_scope(context); - bool success = sema_analyse_cond(context, cond, true); + + // 1. Begin our scope, this is relevant in case we have something like + // while (File *f = @getFileAndClose!()) where the macro pushes a defer into the scope. context_push_scope_with_label(context, statement->while_stmt.flow.label); + // 2. Analyze the condition + if (!sema_analyse_cond(context, cond, true)) + { + // 2a. In case of error, pop context and exit. + context_pop_scope_error(context); + return false; + } + + // 4. Push break / continue - which is independent of the scope. PUSH_BREAKCONT(statement); - success = success && sema_analyse_statement(context, body); - context_pop_defers_and_replace_ast(context, body); + + // 5. Analyse the statement + bool success = sema_analyse_statement(context, body); + + // 6. Pop break / continue POP_BREAKCONT(); + // 7. Check placement, in case of a single statement, it must be placed on the same line. + if (success && !sema_analyse_stmt_placement(cond, body)) + { + SEMA_ERROR(body, "A single statement after 'while' must be placed on the same line, or be enclosed in {}."); + context_pop_scope_error(context); + return false; + } + + // 8. Pop defers attach them to the statement if needed + context_pop_defers_and_replace_ast(context, body); + + // 9. Pop the while scope. context_pop_scope(context); - context_pop_defers_and_replace_ast(context, statement); - context_pop_scope(context); - if (!success) return false; + return success; } +/** + * Check the do ... while (...) statement. + */ static inline bool sema_analyse_do_stmt(Context *context, Ast *statement) { Expr *expr = statement->do_stmt.expr; Ast *body = statement->do_stmt.body; bool success; + + // 1. Begin pushing the scope and break / continue. context_push_scope_with_label(context, statement->do_stmt.flow.label); PUSH_BREAKCONT(statement); + + // 2. We analyze the statement. success = sema_analyse_statement(context, body); - context_pop_defers_and_replace_ast(context, body); + + // 3. Pop break / continue POP_BREAKCONT(); + + // 4. Pop any defers + context_pop_defers_and_replace_ast(context, body); + + // 5. If the current scope ended in a jump, then + // the do statement has no exit. statement->do_stmt.flow.no_exit = context->current_scope->jump_end; + + // 6. Pop the scope context_pop_scope(context); + + // 7. We can now exit if there was an error further up. if (!success) return false; - if (!statement->do_stmt.expr) return success; + // 8. Handle the do { } expression + if (!statement->do_stmt.expr) return true; + + // 9. We next handle the while test. This is its own scope. context_push_scope(context); - success = sema_analyse_expr_of_required_type(context, type_bool, expr, false); + + // 10. Try to evaluate and implicitly cast to boolean. + if (!sema_analyse_expr_of_required_type(context, type_bool, expr, false)) + { + // 10a. On failure, pop and return false. + context_pop_scope_error(context); + return false; + } + + // 11. Pop any defers in the expression. statement->do_stmt.expr = context_pop_defers_and_wrap_expr(context, expr); + + // 12. Pop the scope itself. context_pop_scope(context); - // while (1) with no break means that we've reached a statement which ends with a jump. + // 13. Check for infinite loops using do ... while (1). + // If it has no break then that means we've reached a statement which ends with a jump. if (statement->do_stmt.expr->expr_kind == EXPR_CONST && statement->do_stmt.expr->const_expr.b) { - if (statement->do_stmt.flow.no_exit && !statement->do_stmt.flow.has_break) - { - context->current_scope->jump_end = true; - } + // Unless there is a break, this won't ever exit. + context->current_scope->jump_end = !statement->do_stmt.flow.has_break; } - return success; + return true; } +/** + * Analyse a regular local declaration, e.g. int x = 123 + */ static inline bool sema_analyse_local_decl(Context *context, Decl *decl) { - assert(decl->decl_kind == DECL_VAR); + assert(decl->decl_kind == DECL_VAR && "Unexpected declaration type"); + + // TODO unify with global decl analysis + // Add a local to the current context, will throw error on shadowing. if (!sema_add_local(context, decl)) return decl_poison(decl); + + // 1. Local constants: const int FOO = 123. if (decl->var.kind == VARDECL_CONST) { Expr *init_expr = decl->var.init_expr; + // 1a. We require an init expression. if (!init_expr) { SEMA_ERROR(decl, "Constants need to have an initial value."); return false; } - if (init_expr->expr_kind == EXPR_TYPEINFO && init_expr->type_expr->resolve_status == RESOLVE_DONE - && init_expr->type_expr->type->type_kind == TYPE_VOID) + // 1b. We require defined constants + if (init_expr->expr_kind == EXPR_UNDEF) { SEMA_ERROR(decl, "Constants cannot be undefined."); return false; @@ -364,18 +482,16 @@ static inline bool sema_analyse_local_decl(Context *context, Decl *decl) bool type_is_inferred = decl->type->type_kind == TYPE_INFERRED_ARRAY; Expr *init = decl->var.init_expr; // Handle explicit undef - if (init->expr_kind == EXPR_TYPEINFO && init->type_expr->resolve_status == RESOLVE_DONE - && init->type_expr->type->type_kind == TYPE_VOID) + if (init->expr_kind == EXPR_UNDEF) { if (type_is_inferred) { SEMA_ERROR(decl->var.type_info, "Size of the array cannot be inferred with explicit undef."); return false; } - init->expr_kind = EXPR_UNDEF; - init->resolve_status = RESOLVE_DONE; goto EXIT_OK; } + if (!sema_expr_analyse_assign_right_side(context, NULL, decl->type, init, decl->var.failable || decl->var.unwrap ? FAILABLE_YES : FAILABLE_NO)) return decl_poison(decl); if (decl->type) @@ -389,6 +505,7 @@ static inline bool sema_analyse_local_decl(Context *context, Decl *decl) assert(right_side_type->type_kind == TYPE_ARRAY); decl->type = type_get_array(decl->type->array.base, right_side_type->array.len); } + if (decl->var.unwrap && !init->failable) { SEMA_ERROR(decl->var.init_expr, "A failable expression was expected here."); @@ -1770,7 +1887,7 @@ static inline bool sema_analyse_statement_inner(Context *context, Ast *statement case AST_NEXT_STMT: return sema_analyse_next_stmt(context, statement); case AST_UNREACHABLE_STMT: - return sema_analyse_unreachable_stmt(context, statement); + return sema_analyse_unreachable_stmt(context); case AST_VOLATILE_STMT: return sema_analyse_volatile_stmt(context, statement); case AST_WHILE_STMT: diff --git a/src/compiler/target.c b/src/compiler/target.c index 3909e3310..34f66d669 100644 --- a/src/compiler/target.c +++ b/src/compiler/target.c @@ -18,6 +18,8 @@ unsigned os_target_supports_float16(OsType os, ArchType arch); unsigned os_target_supports_float128(OsType os, ArchType arch); unsigned os_target_supports_vec(OsType os, ArchType arch, int bits, bool is_int); + + Target build_target = {}; int target_alloca_addr_space() @@ -34,6 +36,8 @@ static void type_dump(LLVMTargetDataRef llvm_target_data, LLVMTypeRef type) printf(" | %-3d %-3d %-3d", size, abialign, prefalign); } + + void llvm_dump(void) { static char* archs[] = { @@ -287,7 +291,7 @@ static inline void target_setup_arm_abi(void) UNREACHABLE } -static inline void target_setup_x86_abi(void) +static inline void target_setup_x86_abi(BuildTarget *target) { build_target.abi = ABI_X86; build_target.x86.is_win_api = build_target.os == OS_TYPE_WIN32; @@ -297,8 +301,10 @@ static inline void target_setup_x86_abi(void) } build_target.x86.use_soft_float = build_target.float_abi == FLOAT_ABI_SOFT; // Build target override. - if (build_options.feature.soft_float) build_target.x86.use_soft_float = true; - if (build_options.feature.no_soft_float) build_target.x86.use_soft_float = false; + if (target->feature.soft_float != SOFT_FLOAT_DEFAULT) + { + build_target.x86.use_soft_float = target->feature.soft_float == SOFT_FLOAT_YES; + } build_target.x86.is_win32_float_struct_abi = build_target.os == OS_TYPE_WIN32; build_target.x86.is_mcu_api = build_target.os == OS_TYPE_ELFIAMCU; @@ -324,8 +330,10 @@ static inline void target_setup_x86_abi(void) default: break; } - if (build_options.feature.reg_struct_return) build_target.x86.return_small_struct_in_reg_abi = true; - if (build_options.feature.stack_struct_return) build_target.x86.return_small_struct_in_reg_abi = false; + if (target->feature.struct_return != STRUCT_RETURN_DEFAULT) + { + build_target.x86.return_small_struct_in_reg_abi = target->feature.struct_return == STRUCT_RETURN_REG; + } } @@ -364,7 +372,7 @@ static char *arch_to_target_triple[ARCH_OS_TARGET_LAST + 1] = { }; -void target_setup(void) +void target_setup(BuildTarget *target) { assert(!build_target.target); @@ -377,13 +385,13 @@ void target_setup(void) build_target.target = NULL; const char *triple; - if (build_options.arch_os_target == ARCH_OS_TARGET_DEFAULT) + if (target->arch_os_target == ARCH_OS_TARGET_DEFAULT) { triple = LLVMGetDefaultTargetTriple(); } else { - triple = arch_to_target_triple[build_options.arch_os_target]; + triple = arch_to_target_triple[target->arch_os_target]; } char *err = NULL; @@ -400,7 +408,7 @@ void target_setup(void) LLVMCodeGenOptLevel level; LLVMRelocMode reloc_mode = LLVMRelocDefault; - switch (build_options.optimization_level) + switch (target->optimization_level) { case OPTIMIZATION_NOT_SET: UNREACHABLE; @@ -419,19 +427,15 @@ void target_setup(void) default: UNREACHABLE; } - if (build_options.pic == PIC_BIG || build_options.pic == PIC_SMALL || build_options.generate_lib) + if (target->pic == PIC_BIG || target->pic == PIC_SMALL || (target->type != TARGET_TYPE_EXECUTABLE && target->type != TARGET_TYPE_TEST)) { reloc_mode = LLVMRelocPIC; } - if (build_options.pic == PIC_NONE) + if (target->pic == PIC_NONE) { reloc_mode = LLVMRelocStatic; } - if (!build_options.cpu) - { - build_options.cpu = "generic"; - } /* if (!opt->features) { @@ -594,7 +598,7 @@ void target_setup(void) build_target.abi = ABI_RISCV; TODO case ARCH_TYPE_X86: - target_setup_x86_abi(); + target_setup_x86_abi(target); break; case ARCH_TYPE_X86_64: target_setup_x64_abi(); diff --git a/src/compiler/target.h b/src/compiler/target.h index 826a187b3..b66111def 100644 --- a/src/compiler/target.h +++ b/src/compiler/target.h @@ -345,4 +345,5 @@ typedef struct } Target; + extern Target build_target; \ No newline at end of file diff --git a/src/compiler/types.c b/src/compiler/types.c index cb5652632..8db82640e 100644 --- a/src/compiler/types.c +++ b/src/compiler/types.c @@ -11,7 +11,7 @@ static Type t_usz, t_isz, t_uptr, t_iptr, t_uptrdiff, t_iptrdiff; static Type t_cus, t_cui, t_cul, t_cull; static Type t_cs, t_ci, t_cl, t_cll; static Type t_voidstar, t_typeid, t_error, t_typeinfo; -static Type t_str; +static Type t_str, t_varheader; Type *type_bool = &t_u1; Type *type_void = &t_u0; @@ -50,6 +50,7 @@ Type *type_c_uint = &t_cui; Type *type_c_ulong = &t_cul; Type *type_c_ulonglong = &t_cull; Type *type_error = &t_error; +Type *type_varheader = &t_varheader; static unsigned size_subarray; static unsigned alignment_subarray; @@ -129,8 +130,22 @@ const char *type_to_error_string(Type *type) return strcat_arena(buffer, ")"); } case TYPE_COMPLEX: + switch (type->complex->type_kind) + { + case TYPE_F16: + return "complex16"; + case TYPE_F32: + return "complex32"; + case TYPE_F64: + return "complex64"; + case TYPE_F128: + return "complex128"; + default: + UNREACHABLE + } case TYPE_VECTOR: - TODO + asprintf(&buffer, "%s[<%llu>]", type_to_error_string(type->array.base), (unsigned long long)type->array.len); + return buffer; case TYPE_MEMBER: return "member"; case TYPE_TYPEINFO: @@ -800,7 +815,7 @@ Type *type_get_inferred_array(Type *arr_type) Type *type_get_vararray(Type *arr_type) { - return type_generate_subarray(arr_type, false); + return type_generate_vararray(arr_type, false); } static inline bool array_structurally_equivalent_to_struct(Type *array, Type *type) diff --git a/src/compiler_tests/tests.c b/src/compiler_tests/tests.c index 6864ec05b..5c1e0b57a 100644 --- a/src/compiler_tests/tests.c +++ b/src/compiler_tests/tests.c @@ -155,6 +155,8 @@ void test_file(void) } void compiler_tests(void) { + symtab_init(0x100000); + test_file(); test_lexer(); test_compiler(); diff --git a/src/main.c b/src/main.c index 0424091a3..a1469509a 100644 --- a/src/main.c +++ b/src/main.c @@ -18,39 +18,39 @@ int main(int argc, const char *argv[]) compiler_init(); // Parse arguments. - parse_arguments(argc, argv); + BuildOptions build_options = parse_arguments(argc, argv); - // Now we set up the symtab. - symtab_init(build_options.symtab_size); + BuildTarget build_target = {}; switch (build_options.command) { case COMMAND_INIT: - create_project(); + create_project(&build_options); break; case COMMAND_UNIT_TEST: compiler_tests(); break; case COMMAND_GENERATE_HEADERS: - build_options.compile_option = COMPILE_OUTPUT_HEADERS; - compile_files(NULL); - break; case COMMAND_COMPILE: - compile_files(NULL); + case COMMAND_COMPILE_RUN: + init_default_build_target(&build_target, &build_options, "foo.out"); + compile_files(&build_target); break; case COMMAND_BUILD: - build(); - break; - case COMMAND_COMPILE_RUN: - case COMMAND_MISSING: case COMMAND_RUN: case COMMAND_CLEAN_RUN: case COMMAND_CLEAN: case COMMAND_DIST: case COMMAND_DOCS: case COMMAND_BENCH: + init_build_target(&build_target, &build_options); + compile_files(&build_target); + break; + case COMMAND_MISSING: printf("TODO\n"); } + + print_arena_status(); free_arena(); return 0; diff --git a/test/test_suite/define/common.c3 b/test/test_suite/define/common.c3 new file mode 100644 index 000000000..5147e7197 --- /dev/null +++ b/test/test_suite/define/common.c3 @@ -0,0 +1,109 @@ +// #skip + + + +struct FooInt = Foo(int); +func void someFunctionIntBool = someFunction(int, bool); +// const int A_CONST_INT = A_CONST(int); DOESN'T WORK +// void* standard_foo = __stdin; DOESN'T WORK + + +// Define as +define __stdin as standard_foo; +define Foo(int) as FooInt; +define void someFunction(int, bool) as someFunctionIntBool; +define A_CONST(int) as A_CONST_INT; + + +// aliasof +void* standard_foo aliasof __stdin; +func void someFunctionIntBool aliasof someFunction(int, bool); +struct FooInt aliasof Foo(int); +A_CONST_INT aliasof A_CONST(int); + + +// define = +define standard_foo = __stdin; +define someFunctionIntBool = someFunction(int, bool); +define FooInt = Foo(int); +define A_CONST_INT = A_CONST(int); + +// :: +void* standard_foo :: __stdin; +func void someFunctionIntBool :: someFunction(int, bool); +struct FooInt :: Foo(int); +const int A_CONST_INT :: A_CONST(int); + + + +struct IntArray :: VarArray(int); +func IntArray* newIntArray :: var_array::newArray(int) +const int MAX_INT_CAPACITY ::MAX_INT_CAPACITY(int); + +func void foo() +{ + IntArray* array = newIntArray(); + array.append(10); +} + + +struct IntArray :: VarArray(int); +func IntArray* newIntArray :: var_array::newArray(int) +const int MAX_INT_CAPACITY ::MAX_INT_CAPACITY(int); + +func void foo() +{ + IntArray* array = newIntArray(); + array.append(10); +} + + + +struct IntArray :: VarArray(int); +func IntArray* newIntArray(int capacity) :: var_array::newArray(int) +const int MAX_INT_CAPACITY :: var_array::MAX_CAPACITY(int); + +func void foo(int max_elements) +{ + assert(max_elements <= MAX_INT_CAPACITY) + IntArray* array = newIntArray(max_elements); + array.append(10); +} + + + +define IntArray = VarArray(int); +define newIntArray = var_array::newArray; +define MAX_INT_CAPACITY = var_array::MAX_INT_CAPACITY(int); +define FOOBAR = 123; + +func void foo(int max_elements) +{ + assert(max_elements <= MAX_INT_CAPACITY) + IntArray* array = newIntArray(max_elements); + array.append(10); +} + + +void* standard_foo aliasof __stdin; +func void someFunctionIntBool aliasof someFunction(int, bool); +struct FooInt aliasof Foo(int); +A_CONST_INT aliasof A_CONST(int); + +func void foo(int max_elements) +{ + assert(max_elements <= MAX_INT_CAPACITY) + IntArray* array = newIntArray(max_elements); + array.append(10); +} + +struct IntArray = VarArray(int); +func newIntArray = var_array::newArray; +const MAX_INT_CAPACITY = var_array::MAX_INT_CAPACITY(int); + +func void foo(int max_elements) +{ + assert(max_elements <= MAX_INT_CAPACITY) + IntArray* array = newIntArray(max_elements); + array.append(10); +} diff --git a/test/test_suite/errors/rethrow.c3t b/test/test_suite/errors/rethrow.c3t index 3cf17c852..d29d1bf2e 100644 --- a/test/test_suite/errors/rethrow.c3t +++ b/test/test_suite/errors/rethrow.c3t @@ -7,23 +7,35 @@ func void! test() // #expect: rethrow.ll - %i = alloca i32 - %i1 = alloca { i64, i64 }, align 8 - %error_var = alloca { i64, i64 }, align 8 - store { i64, i64 } zeroinitializer, { i64, i64 }* %i1 - store i32 0, i32* %i - %0 = load { i64, i64 }, { i64, i64 }* %i1 - %1 = extractvalue { i64, i64 } %0, 0 - %noerr = icmp eq i64 %1, 0 - br i1 %noerr, label %after_check, label %error +entry: + %i = alloca i32, align 4 + %i1 = alloca %error_union, align 8 + %error_var = alloca %error_union, align 8 + %coerce = alloca { i64, i64 }, align 8 + store %error_union zeroinitializer, %error_union* %i1, align 8 + store i32 0, i32* %i, align 4 + %err_domain = getelementptr inbounds %error_union, %error_union* %i1, i32 0, i32 0 + %0 = load i64, i64* %err_domain, align 8 + %not_err = icmp eq i64 %0, 0 + br i1 %not_err, label %after_check, label %error + error: - store { i64, i64 } %0, { i64, i64 }* %error_var - br label %guard_block + %1 = bitcast %error_union* %error_var to i8* + %2 = bitcast %error_union* %i1 to i8* + call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 %2, i32 16, i1 false) + br label %guard_block + after_check: - %2 = load i32, i32* %i, align 4 - br label %noerr_block + %3 = load i32, i32* %i, align 4 + br label %noerr_block + guard_block: - %3 = load { i64, i64 }, { i64, i64 }* %error_var - ret { i64, i64 } %3 + %4 = bitcast %error_union* %error_var to { i64, i64 }* + %coerced = load { i64, i64 }, { i64, i64 }* %4, align 8 + ret { i64, i64 } %coerced + noerr_block: - ret { i64, i64 } zeroinitializer + %5 = bitcast { i64, i64 }* %coerce to %error_union* + store %error_union zeroinitializer, %error_union* %5, align 8 + %coerced2 = load { i64, i64 }, { i64, i64 }* %coerce, align 8 + ret { i64, i64 } %coerced2 diff --git a/test/test_suite/examples/gameoflife.c3 b/test/test_suite/examples/gameoflife.c3 new file mode 100644 index 000000000..913ba4373 --- /dev/null +++ b/test/test_suite/examples/gameoflife.c3 @@ -0,0 +1,90 @@ +module game_of_life; + +extern func void printf(char *fmt, ...); +extern func int atoi(char *val); +extern void *__stdoutp; +extern func void fflush(void *std); +extern func int rand(); +extern func void* malloc(usize size); +extern func void usleep(int time); + + +struct GameBoard +{ + int h; + int w; + char* world; + char* temp; +} + +func void GameBoard.show(GameBoard *board) +{ + + printf("\e[H"); + char* current = board.world; + for (int y = 0; y < board.h; y++) + { + for (int x = 0; x < board.w; x++) + { + printf(*current ? "\e[07m \e[m" : " "); + current++; + } + printf("\e[E"); + } + fflush(__stdoutp); +} + +func void GameBoard.evolve(GameBoard *board) +{ + for (int y = 0; y < board.h; y++) + { + for (int x = 0; x < board.w; x++) + { + int n = 0; + for (int y1 = y - 1; y1 <= y + 1; y1++) + { + for (int x1 = x - 1; x1 <= x + 1; x1++) + { + int actualX = (x1 + board.w) % board.w; + int actualY = (y1 + board.h) % board.h; + if (board.world[actualX + actualY * board.w]) n++; + } + } + if (board.world[x + y * board.w]) n--; + board.temp[x + y * board.w] = cast(n == 3 || (n == 2 && board.world[x + y * board.w]) as char); + } + } + for (int i = 0; i < board.w * board.h; i++) + { + board.world[i] = board.temp[i]; + } +} + + +func int main(int c, char** v) +{ + int w = 0; + int h = 0; + if (c > 1) w = atoi(v[1]); + if (c > 2) h = atoi(v[2]); + if (w <= 0) w = 30; + if (h <= 0) h = 30; + + GameBoard board; + board.w = w; + board.h = h; + board.world = malloc(h * w); + board.temp = malloc(h * w); + + for (int i = h * w - 1; i >= 0; i--) + { + board.world[i] = rand() % 10 == 0 ? 1 : 0; + } + for (int j = 0; j < 1000; j++) + { + board.show(); + board.evolve(); + usleep(200000); + } + return 1; +} \ No newline at end of file diff --git a/test/test_suite/functions/returning_void.c3t b/test/test_suite/functions/returning_void.c3t new file mode 100644 index 000000000..34e2911d0 --- /dev/null +++ b/test/test_suite/functions/returning_void.c3t @@ -0,0 +1,20 @@ +func void test1() +{ + int x; + if (x == 0) return; + return test1(); +} +// #expect: returning_void.ll + + %x = alloca i32, align 4 + store i32 0, i32* %x, align 4 + %0 = load i32, i32* %x, align 4 + %eq = icmp eq i32 %0, 0 + br i1 %eq, label %if.then, label %if.exit + +if.then: + ret void + +if.exit: + call void @returning_void.test1() + ret void diff --git a/test/test_suite/macros/generic_common.c3 b/test/test_suite/macros/generic_common.c3 new file mode 100644 index 000000000..f776b790b --- /dev/null +++ b/test/test_suite/macros/generic_common.c3 @@ -0,0 +1,41 @@ + + +generic abs(x) +{ + case double: + return fabs(x); + case int: + return abs(x); +} + +generic add(x, y) +{ + case double, double: + return x + y; + case int, double: + return cast(x as double) + y; +} + +generic addBad(x, y) +{ + case double: // #error: Expected 2 types in the case statement + return x + y; + case int, double: + return cast(x as double) + y; +} + +generic addBad2() // #error: generic function needs at least 1 parameter +{ + case double: + return x + y; + case int, double: + return cast(x as double) + y; +} + +generic addBad3(x) +{ + case 123: // #error: Expected a type as the argument + return x + y; + case double: + return cast(x as double) + y; +} diff --git a/test/test_suite/statements/foreach_with_error.c3t b/test/test_suite/statements/foreach_with_error.c3t index 58334a31c..bdbb80936 100644 --- a/test/test_suite/statements/foreach_with_error.c3t +++ b/test/test_suite/statements/foreach_with_error.c3t @@ -15,42 +15,42 @@ func void test() entry: %x = alloca [3 x i32], align 4 - %x1 = alloca { i64, i64 }, align 8 + %x1 = alloca %error_union, align 8 %g = alloca i32, align 4 %idx = alloca i64, align 8 %z = alloca i32, align 4 - store { i64, i64 } zeroinitializer, { i64, i64 }* %x1, align 8 + store %error_union zeroinitializer, %error_union* %x1, align 8 %0 = bitcast [3 x i32]* %x to i8* call void @llvm.memset.p0i8.i64(i8* align 4 %0, i8 0, i64 12, i1 false) store i32 0, i32* %g, align 4 - %1 = load { i64, i64 }, { i64, i64 }* %x1, align 8 - %2 = extractvalue { i64, i64 } %1, 0 - %noerr = icmp eq i64 %2, 0 - br i1 %noerr, label %after_check, label %foreach.exit + %err_domain = getelementptr inbounds %error_union, %error_union* %x1, i32 0, i32 0 + %1 = load i64, i64* %err_domain, align 8 + %not_err = icmp eq i64 %1, 0 + br i1 %not_err, label %after_check, label %foreach.exit after_check: store i64 0, i64* %idx, align 8 br label %foreach.cond foreach.cond: - %3 = load i64, i64* %idx, align 8 - %lt = icmp ult i64 %3, 3 + %2 = load i64, i64* %idx, align 8 + %lt = icmp ult i64 %2, 3 br i1 %lt, label %foreach.body, label %foreach.exit foreach.body: - %4 = getelementptr inbounds i32, [3 x i32]* %x, i64 %3 - %5 = load i32, i32* %4, align 4 - store i32 %5, i32* %z, align 4 - %6 = load i32, i32* %g, align 4 - %7 = load i32, i32* %z, align 4 - %add = add i32 %6, %7 + %3 = getelementptr inbounds i32, [3 x i32]* %x, i64 %2 + %4 = load i32, i32* %3, align 4 + store i32 %4, i32* %z, align 4 + %5 = load i32, i32* %g, align 4 + %6 = load i32, i32* %z, align 4 + %add = add i32 %5, %6 store i32 %add, i32* %g, align 4 - %8 = load { i64, i64 }, { i64, i64 }* %x1, align 8 - %9 = extractvalue { i64, i64 } %8, 0 - %noerr2 = icmp eq i64 %9, 0 - br i1 %noerr2, label %after_check3, label %voiderr + %err_domain2 = getelementptr inbounds %error_union, %error_union* %x1, i32 0, i32 0 + %7 = load i64, i64* %err_domain2, align 8 + %not_err3 = icmp eq i64 %7, 0 + br i1 %not_err3, label %after_check4, label %voiderr -after_check3: +after_check4: ; preds = %foreach.body %arridx = getelementptr inbounds [3 x i32], [3 x i32]* %x, i64 0, i64 0 store i32 1, i32* %arridx, align 4 br label %voiderr @@ -59,9 +59,9 @@ voiderr: br label %foreach.inc foreach.inc: - %10 = load i64, i64* %idx, align 8 - %11 = add i64 %10, 1 - store i64 %11, i64* %idx, align 8 + %8 = load i64, i64* %idx, align 8 + %9 = add i64 %8, 1 + store i64 %9, i64* %idx, align 8 br label %foreach.cond foreach.exit: diff --git a/test/test_suite/statements/while_statement_placement.c3 b/test/test_suite/statements/while_statement_placement.c3 new file mode 100644 index 000000000..db5433899 --- /dev/null +++ b/test/test_suite/statements/while_statement_placement.c3 @@ -0,0 +1,11 @@ +func void test1() +{ + int a; + while (1) a++; + while (1) + { + a++; + } + while (1) + a++; // #error: must be placed on the same line +} \ No newline at end of file diff --git a/test/test_suite/variables/consts.c3 b/test/test_suite/variables/consts.c3 new file mode 100644 index 000000000..c772696e3 --- /dev/null +++ b/test/test_suite/variables/consts.c3 @@ -0,0 +1,7 @@ +const int foo = 3; // #error: must be all upper case + +func void foo(int i) +{ + const int x = i; // #error: must be all upper case + const int Y = i; +} \ No newline at end of file